TF2.1でXOR回路

2020.04.26

TF2.1でモデル化

前回のAND回路と同じ単純な、y=x1*w1 + x2*w2 + bで判定させるモデルを作ります。

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]
-----------------

つまり、y=0.0*x1 + 0.0*x2 + 0.5が最適解となり、このモデルでの限界となりました。グラフで見ると以下のようになります。
f:id:hmxnet:20200514165733j:plain

このままだと使い物にならないので、ここでディープニューラルネットの登場です。
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()

f:id:hmxnet:20200514171810j:plain

意表を突いたフィッティングの仕方です。折り紙を対角上に折った形状です。確かに0,1のとき1、1,0のとき1が出力されています。これがディープニューラルネット威力です。
場合によっては、以下のように山折りではなく谷折りにした形も出力されることもあります。これは係数の初期値によるもので初期値が乱数で初期化されているからです。どちらも期待した出力は得ています。
f:id:hmxnet:20200514173752j:plain