KerasのContainer内部のtrainable設定について検証してみた

Kerasには、複数レイヤをまとめてインスタンス化するContainerが用意されている。例えば以下のように書くと、２層の全結合層をインスタンス化したshared_layerが作成される。これによってネットワーク内に共通のパラメータを持った層を組み込むことができる。


```Python
inputs = Input((3,))
x = Dense(3, activation="sigmoid", use_bias=False)(inputs)
x = Dense(3, activation="sigmoid", use_bias=False)(x)
shared_layer = Container(inputs=inputs, outputs=x, name="shared_layer")
```

#検証すること
kerasではモデルを構成するそれぞれの層のtrainableを変更することでその層の重みを更新するかどうか決定するが、どうやらContainerで作ったインスタンスとContainerを構成する各層でこの設定が独立してるらしい。
ということで以下の２つの設定をそれぞれ変更した4通りについて層の重みが更新されるか検証する。
**①Containerでインスタンス化した層のtrainable設定**
**②Container内部の層のtrainable設定**

#実行環境
Keras：2.1.6
Python：3.6.4

**_※keras2.2以降ではContainerの代わりにNetworkを使うらしいので注意_**

#検証モデル
使用するライブラリは以下の通り。

```Python
from keras.layers import Input, Dense
from keras.models import Model
from keras.engine.topology import Container
from keras.optimizers import sgd

import numpy as np
```

3層のニューラルネットワークを構築し、shared_layer内のtrainable設定をいじっておく。

```Python:model.py
#共有レイヤ作成
inputs = Input((3,))
x = Dense(3, activation="sigmoid", use_bias=False)(inputs)
x = Dense(3, activation="sigmoid", use_bias=False)(x)
shared_layer = Container(inputs=inputs, outputs=x, name="shared_layer")

#テストモデル構築
y = shared_layer(inputs)

model = Model(inputs=inputs, outputs=y)

#shared_layerのうち片方の重みを固定
model.get_layer("shared_layer").layers[1].trainable=False
```

#①shared_layer.trainable=True
##trainableの状況確認
```Python
print("shared_layer.trainable = " + str(model.get_layer("shared_layer").trainable) + "\n")
print("--each layer.trainable in shared_layer--")
for i in model.get_layer("shared_layer").layers:
    print(i.name, i.trainable)
```
>shared_layer.trainable = True
>
--each layer.trainable in shared_layer--
input_1 False
dense_1 False
dense_2 True

##パラメータの確認
```Python
for i in model.layers:
    print(i.name, i.get_weights())
```
>
input_1 []
shared_layer [array([[-0.43590093, -0.49712682, -0.5146408 ],
       [ 0.51134324,  0.07132649,  0.2040441 ],
       [-0.62061524,  0.42325187, -0.5272429 ]], dtype=float32), array([[-0.09528399,  0.9781587 ,  0.22182012],
       [ 0.90270543,  0.23723674, -0.18667579],
       [ 0.929554  , -0.57312965,  0.7156825 ]], dtype=float32)]

##学習
```Python
#optimizer作成
SGD = sgd(lr = 10)

#ダミーデータ作成（バッチサイズ5）
train_x = np.random.rand(5,3)
train_y = np.random.rand(5,3)

#重み更新
model.compile(optimizer=SGD, loss="mean_squared_error")
model.fit(x=train_x, y=train_y)

#重みの確認
for i in model.layers:
    print(i.name, i.get_weights())
```
>input_1 []
shared_layer [array([[-0.43590093, -0.49712682, -0.5146408 ],
       [ 0.51134324,  0.07132649,  0.2040441 ],
       [-0.62061524,  0.42325187, -0.5272429 ]], dtype=float32), array([[-0.18501434,  0.9731035 ,  0.05751626],
       [ 0.8193617 ,  0.24262278, -0.386313  ],
       [ 0.8491909 , -0.57754445,  0.57252395]], dtype=float32)]

2番めのDense層のみパラメータが更新されている。。。
#②shared_layer.trainable=False
##設定変更、trainableの状況確認
```Python
#設定の変更
model.get_layer("shared_layer").trainable=False

print("shared_layer.trainable = " + str(model.get_layer("shared_layer").trainable) + "\n")
print("--each layer.trainable in shared_layer--")
for i in model.get_layer("shared_layer").layers:
    print(i.name, i.trainable)
```
>shared_layer.trainable = False
>
--each layer.trainable in shared_layer--
input_1 False
dense_1 False
dense_2 True

##パラメータの確認
```Python
for i in model.layers:
    print(i.name, i.get_weights())
```
>
input_1 []
shared_layer [array([[-0.43590093, -0.49712682, -0.5146408 ],
       [ 0.51134324,  0.07132649,  0.2040441 ],
       [-0.62061524,  0.42325187, -0.5272429 ]], dtype=float32), array([[-0.18501434,  0.9731035 ,  0.05751626],
       [ 0.8193617 ,  0.24262278, -0.386313  ],
       [ 0.8491909 , -0.57754445,  0.57252395]], dtype=float32)]

##学習
```Python
#ダミーデータ作成（バッチサイズ5）
train_x = np.random.rand(5,3)
train_y = np.random.rand(5,3)

#重み更新
model.compile(optimizer=SGD, loss="mean_squared_error")
model.fit(x=train_x, y=train_y)

#重みの確認
for i in model.layers:
    print(i.name, i.get_weights())
```
>input_1 []
shared_layer [array([[-0.43590093, -0.49712682, -0.5146408 ],
       [ 0.51134324,  0.07132649,  0.2040441 ],
       [-0.62061524,  0.42325187, -0.5272429 ]], dtype=float32), array([[-0.18501434,  0.9731035 ,  0.05751626],
       [ 0.8193617 ,  0.24262278, -0.386313  ],
       [ 0.8491909 , -0.57754445,  0.57252395]], dtype=float32)]

どの層も重みの変化なし。。。
#結論
まとめるとこんな感じ。早い話、**_コンテナのtrainableとコンテナ内の層のtrainableの論理積だった。_**

| コンテナのtrainable＼コンテナ内のlayer.trainable  |True  |False  |
|---|---|---|
|True |○  |×  |
|False  |×  |×  |