Tensorflow 2.x
(tf.keras
)で乱数シードを固定する方法を紹介します。
実行環境
- Python: 3.6 | 3.7
- Tensorflow: 2.0 | 2.1 | 2.2 | 2.3
- GitHub Actionsでテスト
テストに使用したコードはこちらにあります。
背景
機械学習の開発において、「学習を再現可能にしたい」や「テスト用に、モデルの初期値を固定したい」などの要求があります。
重みの初期値の違いは学習結果に影響を及ぼすので、初期値を固定できるとこれらの悩みの解消に役立ちそうです。
重みの初期値生成は乱数を利用してます。乱数は乱数シードに基づいて生成されています。TensorFlowはデフォルトでは乱数シードは可変です。よって、毎回異なる初期値をもったモデルが生成されてしまいます。
そこで今回は、乱数シードを固定することで再現性の向上を目指します。
乱数シードの固定
TensorFlowに加えて、NumPy、Pythonの組み込み関数のシードも固定します。
まとめると以下の様な乱数固定関数が実装できます。
import tensorflow as tf
import numpy as np
import random
import os
def set_seed(seed=200):
tf.random.set_seed(seed)
# optional
# for numpy.random
np.random.seed(seed)
# for built-in random
random.seed(seed)
# for hash seed
os.environ["PYTHONHASHSEED"] = str(seed)
これは、以下の様に使用します。ただし、TensorFlowの乱数シード固定だけで十分な場合は、set_seed
をtf.random.set_seed
に置き換えてください。
set_seed(0)
toy_model = tf.keras.Sequential(
tf.keras.layers.Dense(2, input_shape=(10,))
)
# 何らかの処理...
# モデルを再現
set_seed(0)
reproduced_toy_model = tf.keras.Sequential(
tf.keras.layers.Dense(2, input_shape=(10,))
)
reproduced_toy_model
は先に生成したモデルtoy_model
と同じ初期値 (重みの) を持ちます。つまり、再現されています。
set_seed
を使用しなければ、reproducible_toy_model
とtoy_model
は全く異なる初期値をもってしまい、再現性が損なわれてしまいます。
tf.keras.Sequential
だけではなく、Functional APIやSubClassを利用することもできます。
乱数シードの固定方法 (set_seed
) をもう少し整理します。
tf.random.set_seed
について
tf.random.set_seed
の挙動は少し注意が必要です。
まず、tf.random.set_seed
を使用した後で、乱数を使用する関数(tf.random.uniform
: 一様分布からランダムに値をサンプリング) を何度か使用してみます。
tf.random.set_seed(0)
tf.random.uniform([1]) # => [0.29197514]
tf.random.uniform([1]) # => [0.5554141] (異なる値!)
tf.random.uniform([1]) # => [0.1952138] (異なる値!!)
tf.random.uniform([1]) # => [0.17513537] (異なる値!!!)
それぞれ異なる値が出力されました。このままでは再現性がなさそうです。
しかし、改めてtf.random.set_seed
を以下の様に使います。
tf.random.set_seed(0)
tf.random.uniform([1]) # => [0.29197514] (A)
tf.random.uniform([1]) # => [0.5554141] (B)
tf.random.set_seed(0)
tf.random.uniform([1]) # => [0.29197514] (Aの再現)
tf.random.uniform([1]) # => [0.5554141] (Bの再現)
このようにtf.random.set_seed
が呼ばれた場所を起点にして出力が再現されます(tf.random.uniform
はランダムに値を出力する関数にも関わらず)。
なので、例えば、モデルインスタンス生成 (Sequentialやfunctional APIやSubClassの利用) の直前にtf.random.set_seed
を呼ぶようにすると、生成されたモデルは毎回同じ初期値をもってくれます。
補足
TensorFlowには引数にseedを渡せるlayerや関数があります。
しかし、layerやlayerに渡すinitializerの引数を明示的に指定するのはモデルが大きくなってくるとあまり現実的な方法ではないと思います。
また、今回紹介したtf.random.set_seed
を併用しないとうまく動作しないものがあります。
なので、固定したい箇所が少ない場合でも、まずはtf.random.set_seed
を試してみて下さい。
まとめ
TensorFlow 2.x (tf.keras) では、tf.random.set_seed
を使って乱数シードを固定できます。
特に重みの初期値が同じモデルを毎回生成できるようになるので、再現性の向上が期待できます。
Ref
- TensorFlow official document: tf.random.set_seed
- keras (tf.kerasじゃない方)の乱数固定: kerasで学習が再現できない人へ
- 計算結果の再現性も含めた方法 (但し、速度低下): Tensorflow2.3のtf.kerasで再現性を確保する