25
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[TensorFlow 2.x (tf.keras)] 乱数シードを固定して再現性を向上

Last updated at Posted at 2020-09-11

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_seedtf.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_modeltoy_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

25
19
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
25
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?