TensorFlow の使い方練習2:Functional API
はじめに
本記事は以下の記事の続きとなる。
前回は Keras で Sequential モデルを使って単純なフィードフォワード NN を作るとこまでやったので、今度はもっと複雑なコネクションを持つ NN を構築する。
ネットワークが複雑になるとデータセットを用意するのも学習するのも面倒くさくなっていくのでどう記事にしたものか悩んだのだが、とりあえず出力の shape
が期待したものになっていたり、shape
だけ一致している擬似データに対して学習が実行できればネットワークは構築できたことにしよう。
今回作成した Google Colab の notebook はこちら。
Functional API
Keras で複雑なネットワークを作るには Functional API を使う。私が説明するまでもなくとても丁寧なガイドが書かれているのでとりあえずはもうそちらを読んでもろて。
Functional API では有向非巡回グラフが書け、モデルの保存なども簡単である。一方で再帰的な結合を持つネットワーク(RNN)や状態を持つモデルなどは Imperative API(Model
のサブクラス化)を使わないと定義できないし、保存も若干面倒臭いらしい。
準備
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
擬似データ
入力を入れて出力が出てくればいいので、適当に 64 次元のデータ 100 個分を用意する。
X = np.random.normal(0, 1, (100, 64))
入力レイヤの定義
入ってくるデータの shape
を指定する。1つのデータが 64 次元であれば以下のようになる。
inputs = keras.Input(shape=64)
print(inputs.dtype)
print(inputs.shape)
<dtype: 'float32'>
(None, 64)
画像であれば以下のようになる。
img_inputs = keras.Input(shape=(32, 32, 3), dtype=tf.uint8)
print(img_inputs.dtype)
print(img_inputs.shape)
<dtype: 'uint8'>
(None, 32, 32, 3)
モデル
モデルには inputs
と outputs
を指定できる。基本的に inputs
には入力レイヤを与えればよく、outputs
を構成するときにいろいろ細工すると複雑なモデルができるということになる。モデルには名前も指定できる(モデルはネストできるので名前をつけておくとあとで参照するときに便利)。
model = keras.Model(inputs=inputs, outputs=..., name='my_model')
何もしないモデル
入力をそのまま出力すれば何もしないモデル(恒等変換)になる。
inputs = keras.Input(shape=64)
outputs = inputs
model = keras.Model(inputs=inputs, outputs=outputs)
y = model(X)
print(y.shape)
(100, 64)
線形回帰
Dense
レイヤを噛ますだけ。
inputs = keras.Input(shape=64)
outputs = layers.Dense(2)(inputs)
model = keras.Model(inputs=inputs, outputs=outputs)
y = model(X)
print(y.shape)
(100, 2)
keras.utils.plot_model(model, to_file="linear.png", show_shapes=True)
層を積み重ねてモデルを作る
何段階も噛ますだけ。
inputs = keras.Input(shape=64)
x = layers.Dense(32, activation='relu')(inputs)
x = layers.Dense(16, activation='relu')(x)
x = layers.Dense(8, activation='relu')(x)
x = layers.Dense(4, activation='relu')(x)
outputs = layers.Dense(2)(x)
model = keras.Model(inputs=inputs, outputs=outputs)
y = model(X)
print(y.shape)
(100, 2)
# モデルに関する情報
model.summary()
Model: "model_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_5 (InputLayer) [(None, 64)] 0
dense_1 (Dense) (None, 32) 2080
dense_2 (Dense) (None, 16) 528
dense_3 (Dense) (None, 8) 136
dense_4 (Dense) (None, 4) 36
dense_5 (Dense) (None, 2) 10
=================================================================
Total params: 2,790
Trainable params: 2,790
Non-trainable params: 0
_________________________________________________________________
keras.utils.plot_model(model, to_file="ffn.png", show_shapes=True)
スキップコネクション
ResNet とか U-Net で使われてる、層をまたいで別の層に出力を伝搬させるやつ。
以前のレイヤの出力を layers.add
で足しこむ。
inputs = keras.Input(shape=64)
x = layers.Dense(32, activation='relu')(inputs)
y = layers.Dense(32, activation='relu')(x)
x = layers.add([x, y])
x = layers.Dense(16, activation='relu')(x)
outputs = layers.Dense(2)(x)
model = keras.Model(inputs=inputs, outputs=outputs)
y = model(X)
print(y.shape)
(100, 2)
keras.utils.plot_model(model, to_file="skip.png", show_shapes=True)
複数入力/複数出力
マルチモーダル AI とかに使えるだろう。
# 擬似データ
X1 = np.random.normal(0, 1, (100, 64))
X2 = np.random.normal(0, 1, (100, 64))
X3 = np.random.normal(0, 1, (100, 64))
Y1 = np.random.normal(0, 1, (100, 2))
Y2 = np.random.normal(0, 1, (100, 2))
inputs_1st = keras.Input(shape=64, name='inputs_1st')
inputs_2nd = keras.Input(shape=64, name='inputs_2nd')
inputs_3rd = keras.Input(shape=64, name='inputs_3rd')
x = layers.Dense(32, activation='relu')(inputs_1st)
x = layers.Dense(32, activation='relu')(x)
y = layers.Dense(32, activation='relu')(inputs_2nd)
y = layers.Dense(32, activation='relu')(y)
z = layers.Dense(32, activation='relu')(inputs_3rd)
z = layers.Dense(32, activation='relu')(z)
x = layers.concatenate([x, y, z])
x = layers.Dense(32, activation='relu')(x)
x = layers.Dense(32, activation='relu')(x)
v = layers.Dense(32, activation='relu')(x)
outputs_1st = layers.Dense(2, activation='relu', name='outputs_1st')(v)
w = layers.Dense(32, activation='relu')(x)
outputs_2nd = layers.Dense(2, activation='relu', name='outputs_2nd')(w)
model = keras.Model(
inputs=[inputs_1st, inputs_2nd, inputs_3rd],
outputs=[outputs_1st, outputs_2nd]
)
# オプティマイザと損失関数の設定
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss={
"outputs_1st": keras.losses.MeanAbsoluteError(),
"outputs_2nd": keras.losses.MeanSquaredError(),
},
loss_weights={
"outputs_1st": 1.0,
"outputs_2nd": 0.2,
},
)
# 学習
model.fit(
{"inputs_1st": X1, "inputs_2nd": X2, "inputs_3rd": X3},
{"outputs_1st": Y1, "outputs_2nd": Y2},
epochs=2,
batch_size=32,
)
Y1_pred, Y2_pred = model({
'inputs_1st': X1,
'inputs_2nd': X2,
'inputs_3rd': X3,
})
keras.utils.plot_model(model, to_file="multi.png", show_shapes=True)
Discussion