Edited at
KerasDay 23

Kerasで分岐を書いてみる その2

More than 1 year has passed since last update.


Kerasで分岐2

前回(※)Keras.backendのswitchを紹介しましたが、もうちょい触ってみました。

以下記事に書いたコードにちょっとバリエーションを追加したものになりますので、まずは以下をご参照ください。

https://qiita.com/cvusk/items/6955ae3bc802c28c3b66

※ アドベントカレンダー的には日程が前後してしまいましたが・・・。

なお、コード全文は以下にあります。

https://github.com/shibuiwilliam/kerasswitch


switchの中に直接レイヤ定義を書く

switch文の中にレイヤ定義を直接書くことも可能です。

ここで書いているモデルは、AndゲートとOrゲートを同居させたモデルで、switchでAndとOrを判定しています。

入力の形式は(8,3)です。

入力値の第3項は、Andの場合は0、Orの場合は1に設定し、switchはこの第3項を見て、第1,2項のみを次のレイヤの入力にします。


# and data and or data
X = np.array([[0,0,0],[0,1,0],[1,0,0],[1,1,0], #and
[0,0,1],[0,1,1],[1,0,1],[1,1,1]]) #or
Y = np.array([0,0,0,1, #and
0,1,1,1]) #or
Y = keras.utils.to_categorical(Y, 2)

# input layer
inputs = Input(shape=X.shape[1:],name="input")

# switch cases for and and or
x_switch = Lambda(lambda x: K.switch(x[:,2]<1,
Dense(32, activation="relu", name="and1")(x[:,:2]), # !
Dense(32, activation="relu", name="or2") # !!
(Dense(16, activation="relu", name="or1")(x[:,:2]))), # !!
output_shape=(32,))(inputs)

# output layer
outputs = Dense(2, activation="softmax", name="softmax")(x_switch)

model = Model(inputs, outputs)

model.compile(loss='categorical_crossentropy',
optimizer=Adam(),
metrics=['accuracy'])
model.fit(X, Y,
batch_size=4,
epochs=20000,
shuffle=True)

この場合の注意点は、expression部分に複数レイヤを定義する場合、冗長かつ読みづらくなることです。

1レイヤ(# !部分)だけであれば良いのですが、2レイヤ以上(# !!部分)だと、レイヤを変数で取ることができないので、Lambdaの出力から先に書いていくことになります。

つまり、Lambdaの出力に対して入力されるレイヤを()に入れていく方式になり、通常のレイヤ記述の順番と逆になります。


# !!部分の通常の記述
a = Dense(16, activation="relu", name="or1")(x[:,:2])
a = Dense(32, activation="relu", name="or2")(a)


複数の入力を取る

上記では入力の第3項にAndかOrの判定フラグを入れていましたが、このフラブを別の入力テンソルにして、複数入力をとるモデルにすることも可能です。


# and data and or data
X2 = np.array([[0,0],[0,1],[1,0],[1,1], # and
[0,0],[0,1],[1,0],[1,1]]) # or
X2_flag = np.array([[0],[0],[0],[0], # and
[1],[1],[1],[1]]) # or
Y2 = np.array([0,0,0,1, #and
0,1,1,1]) #or
Y2 = keras.utils.to_categorical(Y2, 2)

# input layer
inputs = Input(shape=X2.shape[1:], name="input")
flags = Input(shape=X2_flag.shape[1:], name="flags")

indense = Dense(4, activation="relu", name="dense1")(inputs)

# and layers and or layers
def andGate(inputs):
andDense = Dense(16, activation="relu", name="and1")(inputs)
andDense = Dense(32, activation="relu", name="and2")(andDense)
return andDense
def orGate(inputs):
orDense = Dense(32, activation="sigmoid", name="or1")(inputs)
return orDense

# switch cases for and and or
x_switch = Lambda(lambda x: K.switch(x[1][:,0]<1,
andGate(x[0]),
orGate(x[0])),
output_shape=(32,))([indense, flags])

# output layer
outputs = Dense(2, activation="softmax", name="softmax")(x_switch)

model = Model([inputs, flags], outputs)

model.compile(loss='categorical_crossentropy',
optimizer=Adam(),
metrics=['accuracy'])

model.fit([X2, X2_flag], Y2,
batch_size=4,
epochs=20000,
shuffle=True)

入力はinputsとflagsにして、inputsをdense1を介したあとにswitchするという方式です。

switchではflagsをconditionとしつつ、andGateとorGateにはdense1のみが入力されていく、というモデルになっています。