ディープラーニング究極の難問（笑）

# 与えられた数値が整数かどうかの判別は難しそう
ニューラルネットワークの中には浮動小数点の数が流れていますが、入力された浮動小数点が整数なのかそうでないのか判定する**「ネットワーク内のパラメータおよび入力に対して微分可能という意味でフツーの」**ニューラルネットワークを作るのはほぼ不可能に近いんじゃないかと思います。例えば、以下の浮動小数点を１つ入力して整数なら１を出しそうでなければ０を出すニューラルネットワークを学習しても正答率が6割からほとんど改善しません（フレームワークはkeras）。ニューラルネットワークへの入力が整数になる確率はほぼ50%になっています。

```python
import array
import math
import random
import numpy as np
import keras

class is_integer(keras.utils.Sequence):
    def __init__(self, batch_size, mode):
        self.batch_size = batch_size
        self.mode = mode

    def __len__(self):
        return 1000000 # 1 epochあたりのbatchの個数。本当は無限大

    def __getitem__(self, idx):
        train_batch = np.empty((self.batch_size, self.mode), dtype=keras.backend.floatx())
        label_batch = np.empty(self.batch_size, dtype=keras.backend.floatx())
        for i in range(self.batch_size):
            while True:
                four_bytes = array.array('B',
                               np.random.randint(0,256,4,dtype=np.uint8))
                memoryview(four_bytes).cast('f')[0] += random.randint(-100,100)
                float_number = memoryview(four_bytes).cast('f')[0]
                if math.isfinite(float_number):
                    break
                
            if float_number.is_integer():
                label_batch[i]=1
            else:
                label_batch[i]=0

            if self.mode == 32:
                for j in range(4):
                    for k in range(8):
                        if (four_bytes[j] & (1 << k)) == 0:
                            train_batch[i][8*j+k] = 0
                        else:
                            train_batch[i][8*j+k] = 1
            elif self.mode == 1:
                train_batch[i][0] = float_number
        
        return (train_batch, label_batch)

if __name__ == "__main__":
    # 次の数字を32にすると数値の各ビットを32個の数値としてニューラルネットワークに与える
    mode = 1

    keras.backend.set_floatx("float64")
    model = keras.models.Sequential()
    model.add(keras.layers.Dense(units=32, activation='relu', input_dim=mode))
    for i in range(10):
        model.add(keras.layers.Dense(units=32, activation='relu'))
    model.add(keras.layers.Dense(units=1, activation='sigmoid'))
    model.compile(loss="binary_crossentropy",
                  optimizer="sgd", metrics=["binary_accuracy"])
    int_generator1 = is_integer(100,mode)
    int_generator2 = is_integer(100,mode)
    history=model.fit_generator(int_generator1, steps_per_epoch=100, epochs=100,
                        validation_data=int_generator2, validation_steps=100)
```

なお、上記プログラムで`mode=1`を`mode=32`にすると、32ビットで表される浮動小数点数の各ビットをニューラルネットワークに入れるようになり、そのときは楽勝で正答率99%超えしますが、そういうのは無しで、浮動小数点数をそのまま入力して整数かどうか判定するニューラルネットワークがあり得るのかという話です。解き方見つけちゃった方はコメント欄とかでご教授下さい。

# 与えられた数値を四捨五入して得られた整数の偶奇の判定も難しかった
最初に述べた判別問題では、実数の中で正解が1になる入力が離散的でいわゆるルベーグ測度ゼロですが、クラスのルベーグ測度を揃えた問題として、与えられた数値に一番近い整数(pythonの関数roundで得られる整数)偶数か奇数か判定する問題は下記のプログラムで正解率が~~95%~~50%くらいまで~~上がり~~しか上がらず、入力をビットごとに分け~~ても正解率は上がりませんでした~~ると正解率は95%くらいに上がりました。偶数と奇数がかなり偏って現れるようにプログラムを組んだため正解率を過大評価したが、偶奇の出現を半々に揃えると正解率は50%程度にしかなりませんでした。

```python
import array
import math
import random
import numpy as np
import keras

class is_integer(keras.utils.Sequence):
    def __init__(self, batch_size, mode):
        self.batch_size = batch_size
        self.mode = mode

    def __len__(self):
        return 1000000 # 1 epochあたりのbatchの個数。本当は無限大

    def __getitem__(self, idx):
        train_batch = np.empty((self.batch_size, self.mode), dtype=keras.backend.floatx())
        label_batch = np.empty(self.batch_size, dtype=keras.backend.floatx())
        for i in range(self.batch_size):
            while True:
                four_bytes = array.array('B',
                               np.random.randint(0,256,4,dtype=np.uint8))
                # 次の行で偶奇が半々ずつ現れるようにしている
                memoryview(four_bytes).cast('f')[0] = random.uniform(-100000,100000)
                float_number = memoryview(four_bytes).cast('f')[0]
                if math.isfinite(float_number):
                    break
                
            if round(float_number) % 2 == 0:
                label_batch[i]=0
            else:
                label_batch[i]=1

            if self.mode == 32:
                for j in range(4):
                    for k in range(8):
                        if (four_bytes[j] & (1 << k)) == 0:
                            train_batch[i][8*j+k] = 0
                        else:
                            train_batch[i][8*j+k] = 1
            elif self.mode == 1:
                train_batch[i][0] = float_number
        
        return (train_batch, label_batch)


if __name__ == "__main__":
    keras.backend.set_floatx("float64")
    # 次の数字を32にすると数値の各ビットを32個の数値としてニューラルネットワークに与える
    mode = 1
    model = keras.models.Sequential()
    model.add(keras.layers.Dense(units=32, activation='relu', input_dim=mode))
    for i in range(10):
        model.add(keras.layers.Dense(units=32, activation='relu'))
    model.add(keras.layers.Dense(units=1, activation='sigmoid'))
    model.compile(loss="binary_crossentropy",
                  optimizer="sgd", metrics=["binary_accuracy"])
    int_generator1 = is_integer(100, mode) 
    int_generator2 = is_integer(100, mode) 
    history=model.fit_generator(int_generator1, steps_per_epoch=100, epochs=300,
                        validation_data=int_generator2, validation_steps=100)
```

# ビット列として符号なし整数を与えても10で割った余りをニューラルネットは学習できないらしい
上記２つの例では、数値のビット列表現を与えればわりとうまく分類できましたが、符号なし32ビット整数を10で割った余りを求める問題については、ビット列を与えてもニューラルネットはせいぜい正解率20%くらいしか達成できないようです。以下のkerasコードは10で割った余りを学習しようとします。なお、以下のプログラムで`mode = 1`として、32ビット符号なし整数を1つの64ビット浮動小数点数として与えると正解率は10%(当てずっぽうと同じ)しか出ないため、その２倍うまくやっているとは言えます。

```python
import array
import math
import random
import numpy as np
import keras

class is_integer(keras.utils.Sequence):
    def __init__(self, batch_size, mode, num_category):
        self.batch_size = batch_size
        self.mode = mode
        self.num_category = num_category

    def __len__(self):
        return 1000000 # 1 epochあたりのbatchの個数。本当は無限大

    def __getitem__(self, idx):
        train_batch = np.empty((self.batch_size, self.mode), dtype=keras.backend.floatx())
        label_batch = np.zeros((self.batch_size,self.num_category),
                               dtype=keras.backend.floatx())
        for i in range(self.batch_size):
            four_bytes = array.array('B',
                               np.random.randint(0,256,4,dtype=np.uint8))
            int_number = memoryview(four_bytes).cast("I")[0]
            label_batch[i][int_number % self.num_category]=1

            if self.mode == 32:
                for j in range(4):
                    for k in range(8):
                        if (four_bytes[j] & (1 << k)) == 0:
                            train_batch[i][8*j+k] = 0
                        else:
                            train_batch[i][8*j+k] = 1
            elif self.mode == 1:
                train_batch[i][0] = int_number
        
        return (train_batch, label_batch)

if __name__ == "__main__":
    # 次の数字を32にすると数値の各ビットを32個の数値としてニューラルネットワークに与える
    mode = 32
    num_category=10

    keras.backend.set_floatx("float64")
    model = keras.models.Sequential()
    model.add(keras.layers.Dense(units=32, activation='relu', input_dim=mode))
    for i in range(10):
        model.add(keras.layers.Dense(units=32, activation='relu'))
    model.add(keras.layers.Dense(units=num_category, activation='softmax'))
    model.compile(loss="categorical_crossentropy",
                  optimizer="sgd", metrics=["categorical_accuracy"])
    int_generator1 = is_integer(100,mode,num_category)
    int_generator2 = is_integer(100,mode,num_category)
    history=model.fit_generator(int_generator1, steps_per_epoch=100, epochs=300,
                        validation_data=int_generator2, validation_steps=100)
```

# 参考文献
* [「整数が奇数か偶数かをディ～プラ～ニングで判定してみた」の記事は、まだですか？🤔 #えっ](https://twitter.com/zr_tex8r/status/1144461210030501891?s=19)