LoginSignup
11
9

More than 5 years have passed since last update.

tf.layers で重み減衰

Last updated at Posted at 2018-03-21

TensorFlowでtf.layersモジュールを使った場合の正則化の仕方の個人的なメモです.

環境

  • Python 3.6.4
  • TensorFlow 1.6.1 (cpu)

重み減衰

tf.layersモジュールを使うと,TensorFlowが楽に使えることを最近やっと知りました.ほぼtf.kerasモジュールと同じ様に使えてハッピーです.しかし,ある時過学習におちいり,ふと正則化にシンプルな重み減衰をしてみたいなぁと思いました.

tf.layers.denceなどのVariableをともなうtf.layersモジュールには引数にkernel_regularizerbias_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_regularizerbias_regularizerに正則化を行う関数tf.contrib.layers.l2_regularizerを渡せば,その関数がtf.get_variableの引数のregularizerに渡り,Variablesの重みの二乗和がtf.GraphKeys.REGULARIZATION_LOSSESでアクセスできる様になるということだと思います.

検証

実験コード

hoge
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エポックまで学習を進めたのにも関わらず,過学習を抑制していることがわかります.
重み減衰を課さなかった場合は,最後らへんに過学習を引き起こしているのが見て取れます.

without_weight_decay.png

weight_decay.png

Seed値について

どうでもいいことなのですがSeedについて.実験を行うときにSeed値を固定しないと実験の意味がないのですが,かなりハマったのでメモしときます.
TensorFlowでSeed値を決めるときはtf.set_random_seedを使うのですが,実はtf.set_random_seedは現在のdefault graphにのみに影響するらしいです.なので,default graphを決定した後にtf.set_random_seedを使うのが良いです.

以下の記事に丁寧に書いてありました.

ここからが自分的にかなりハマりました(笑)
今回の実験の題材に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を記述しても大丈夫だと思います.たぶん…


  1. 以下にtf.layersのコードがあるのですが,めんどくさくて追うのをやめました. https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/layers/core.py。 

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9