TF2.1でXOR回路
2020.04.26
TF2.1でモデル化
前回のAND回路と同じ単純な、で判定させるモデルを作ります。
import numpy as np import tensorflow as tf from tensorflow.keras import Input from tensorflow.keras.layers import Dense from tensorflow.keras import Model from tensorflow.keras.optimizers import SGD, Adam trainX = np.array([ [0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) trainY = np.array([ 0.0, 1.0, 1.0, 0.0 ]) x_in = Input(shape=(2,)) x = x_in x = Dense(1, use_bias=True, activation=None)(x) model = Model(x_in, x) learning_rate = 0.1 sgd = SGD(lr=learning_rate) model.compile(optimizer=sgd, loss='mean_squared_error') model.summary() batch_size = 4 history = model.fit(x=trainX, y=trainY, epochs=1000, batch_size=batch_size, shuffle=False, validation_split=0.0) evaluate = model.evaluate(trainX, trainY) print('Evaluate:', evaluate) testX = trainX output = model.predict(testX[0:batch_size]) print('Predict:', output)
結果、すべて出力は0.5となってしまいました。
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 2)] 0 _________________________________________________________________ dense (Dense) (None, 1) 3 ================================================================= Total params: 3 Trainable params: 3 Non-trainable params: 0 _________________________________________________________________ Epoch 1/1000 1/1 [==============================] - 0s 999us/step - loss: 1.1281 Epoch 2/1000 1/1 [==============================] - 0s 999us/step - loss: 0.8644 (略) Evaluate: 0.25 Predict: [[0.49999982] [0.49999994] [0.49999994] [0.50000006]]
係数を表示させてみます。
def PrintWeights(model): print('-----Weights-----') for i, l in enumerate(model.layers): weights = l.get_weights() if len(weights) == 0: continue name = ['W', 'b'] for j, c in enumerate(weights): print('{}:{}'.format(name[j], c)) print('-----------------') PrintWeights(model)
-----Weights----- W:[[1.2831156e-07] [1.2877769e-07]] b:[0.49999982] -----------------
つまり、が最適解となり、このモデルでの限界となりました。グラフで見ると以下のようになります。
このままだと使い物にならないので、ここでディープニューラルネットの登場です。
2層のネットワークに加えて非線形関数(ReLU)を入れてみます。前と違う点はDenseの部分です。
import numpy as np import tensorflow as tf from tensorflow.keras import Input from tensorflow.keras.layers import Dense from tensorflow.keras import Model from tensorflow.keras.optimizers import SGD, Adam trainX = np.array([ [0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) trainY = np.array([ 0.0, 1.0, 1.0, 0.0 ]) x_in = Input(shape=(2,)) x = x_in x = Dense(2, use_bias=True, activation='relu')(x) x = Dense(1, use_bias=True, activation=None)(x) model = Model(x_in, x) learning_rate = 0.1 sgd = SGD(lr=learning_rate) model.compile(optimizer=sgd, loss='mean_squared_error') model.summary() batch_size = 4 history = model.fit(x=trainX, y=trainY, epochs=1000, batch_size=batch_size, shuffle=False, validation_split=0.0) evaluate = model.evaluate(trainX, trainY) print('Evaluate:', evaluate) testX = trainX output = model.predict(testX[0:batch_size]) print('Predict:', output)
Predictを見るとかなりいい感じにフィッティング(学習)してくれました。
Model: "model_4" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_6 (InputLayer) [(None, 2)] 0 _________________________________________________________________ dense_6 (Dense) (None, 2) 6 _________________________________________________________________ dense_7 (Dense) (None, 1) 3 ================================================================= Total params: 9 Trainable params: 9 Non-trainable params: 0 _________________________________________________________________ Epoch 1/1000 1/1 [==============================] - 0s 2ms/step - loss: 0.4063 Epoch 2/1000 1/1 [==============================] - 0s 2ms/step - loss: 0.3359 (略) Evaluate: 6.362464266378154e-13 Predict: [[1.0325746e-06] [9.9999923e-01] [9.9999923e-01] [5.2721737e-07]]
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import axes3d %matplotlib notebook def ReLU(x): return np.maximum(0, x) x1 = np.linspace(0, 1, 100) x2 = np.linspace(0, 1, 100) X1, X2 = np.meshgrid(x1, x2) W1 = model.layers[1].get_weights()[0] b1 = model.layers[1].get_weights()[1] W2 = model.layers[2].get_weights()[0] b2 = model.layers[2].get_weights()[1] Y11 = ReLU(X1 * W1[0, 0] + X2 * W1[1, 0] + b1[0]) Y12 = ReLU(X1 * W1[0, 1] + X2 * W1[1, 1] + b1[1]) Y = Y11 * W2[0] + Y12 * W2[1] + b2 fig = plt.figure() ax = fig.gca(projection='3d') ax.plot_surface(X1, X2, Y, cmap=plt.cm.viridis) ax.set_xlabel('X1') ax.set_ylabel('X2') ax.set_zlabel('Y') ax.set_zlim([0,1]) plt.show()
意表を突いたフィッティングの仕方です。折り紙を対角上に折った形状です。確かに0,1のとき1、1,0のとき1が出力されています。これがディープニューラルネット威力です。
場合によっては、以下のように山折りではなく谷折りにした形も出力されることもあります。これは係数の初期値によるもので初期値が乱数で初期化されているからです。どちらも期待した出力は得ています。