LoginSignup
1
3

More than 3 years have passed since last update.

ディープラーニング究極の難問(笑)

Last updated at Posted at 2019-08-03

与えられた数値が整数かどうかの判別は難しそう

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

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

与えられた数値を四捨五入して得られた整数の偶奇の判定も難しかった

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

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で割った余りをニューラルネットは学習できないらしい

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

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)

参考文献

1
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3