TensorFlowでtf.layers
モジュールを使った場合の正則化の仕方の個人的なメモです.
環境
- Python 3.6.4
- TensorFlow 1.6.1 (cpu)
重み減衰
tf.layers
モジュールを使うと,TensorFlowが楽に使えることを最近やっと知りました.ほぼtf.keras
モジュールと同じ様に使えてハッピーです.しかし,ある時過学習におちいり,ふと正則化にシンプルな重み減衰をしてみたいなぁと思いました.
tf.layers.dence
などのVariable
をともなうtf.layers
モジュールには引数にkernel_regularizer
やbias_regularizer
があることは知っていたのですが,ここに正則化を行う関数(例えばtf.contrib.layers.l2_regularizer
)を渡せば損失関数に勝手に正則化項が加わるのか? なわけないよな,と思い調べてみました.
おそらくですがtf.layers
モジュールを使うときのVariable
はモジュール内でtf.get_variable
を用いて定義されているのだと思います1.この関数のtf.get_variable
の引数にregularizer
があり,以下の様に書かれていました.
regularizer: A (Tensor -> Tensor or None) function; the result of applying it on a newly created variable will be added to the collection
tf.GraphKeys.REGULARIZATION_LOSSES
and can be used for regularization.
つまり,tf.layers.dence
などのモジュールの引数kernel_regularizer
,bias_regularizer
に正則化を行う関数tf.contrib.layers.l2_regularizer
を渡せば,その関数がtf.get_variable
の引数のregularizer
に渡り,Variables
の重みの二乗和がtf.GraphKeys.REGULARIZATION_LOSSES
でアクセスできる様になるということだと思います.
検証
実験コード
import tensorflow as tf
sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, shape=[None, 784])
init = tf.constant_initializer(value=1)
regularizer = tf.contrib.layers.l2_regularizer(scale=0.01)
dence = tf.layers.dense(
inputs=x,
units=10,
kernel_initializer=init,
kernel_regularizer=regularizer)
weight_decay = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
print(weight_decay) # 何が入っているのか見てみる
sess.run(tf.global_variables_initializer())
print(weight_decay[0].eval()) # 値を見てみる
実行結果
[<tf.Tensor 'dense/kernel/Regularizer/l2_regularizer:0' shape=() dtype=float32>]
39.2
ここで,この実行結果が先ほどの推測とあっているのか確かめます.損失関数に加わる重み減衰の式は次の様になります.
$$
\frac{1}{2}\times\lambda \times\boldsymbol{w}^T \boldsymbol{w}
$$
$\lambda$は正則化係数です.
まず,重み行列$W$は$784 \times 10$の行列です.また全て$1$で初期化しました.
$$
W = \begin{pmatrix}
w_{1, 1} = 1 & \cdots & w_{1, j}=1 & \cdots & w_{1, 10}=1 \
\vdots & & \vdots & & \vdots \
w_{i, 1} = 1 & & w_{i, j} = 1 & & w_{i, 10}=1 \
\vdots & & \vdots & & \vdots \
w_{784, 1} =1 & \cdots & w_{784, j}=1 & \cdots & w_{784, 10}=1
\end{pmatrix}
$$
tf.contrib.layers.l2__regularizer
は重みの二乗和の半分に正則化係数(今だと0.01)をかける関数でした.
なので,重み行列$W$にtf.contrib.layers.l2__regularizer
を適用した後の値は
$$
(784 \times 10) \times 1^2 \times 0.01 \times \frac{1}{2} = 39.2
$$
です.実行結果とあってます.よかった.
ちなみにkernel_regularizer
引数にtf.keras.regularizers.l2
を渡すことができますが,こちらは$1/2$倍をしないので注意です.
重み減衰の力を知る
重み減衰の威力を説明するいい実験結果ではないのですが,mnistを使った実験をしたので載せときます.
モデルはMultiPerceptronで層数は3つで,隠れ層の隠れ素子を256としました.コードはgistにのせときます.
結果は下の図の様になりました.見にくくてごめんなさい.
重み減衰を課した場合の方が早く収束していることがわかります.また,200エポックまで学習を進めたのにも関わらず,過学習を抑制していることがわかります.
重み減衰を課さなかった場合は,最後らへんに過学習を引き起こしているのが見て取れます.
Seed値について
どうでもいいことなのですがSeedについて.実験を行うときにSeed値を固定しないと実験の意味がないのですが,かなりハマったのでメモしときます.
TensorFlowでSeed値を決めるときはtf.set_random_seed
を使うのですが,実はtf.set_random_seed
は現在のdefault graph
にのみに影響するらしいです.なので,default graph
を決定した後にtf.set_random_seed
を使うのが良いです.
以下の記事に丁寧に書いてありました.
- https://stackoverflow.com/questions/36288235/how-to-get-stable-results-with-tensorflow-setting-random-seed
- https://qiita.com/yuyakato/items/9a5d80e6c7c41e9a9d22
ここからが自分的にかなりハマりました(笑)
今回の実験の題材にTensorFlowが提供するmnistを扱いましたが,コードでmnist.train.next_batch
を使っています.tf.set_random_seed
を適切に設定したはずなのになぜか結果が違ってイライラしましたがmnist.train.next_batch()のコードをみて思いました.
numpy.random.shuffleつこうてるやん!!!
numpy.random.seed
を設定してめでたく再現性を保てました.疲れた.
numpyはmnist.train.next_batch
を使う前であれば多分どこにnp.random.seed
を記述しても大丈夫だと思います.たぶん…
-
以下に
tf.layers
のコードがあるのですが,めんどくさくて追うのをやめました. https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/layers/core.py。 ↩