LoginSignup
7
2

More than 5 years have passed since last update.

Chainer v2のenable_backpropの挙動について

Last updated at Posted at 2017-09-11

TL;DR

  • enable_backpropbackward() 実行時ではなくforwardグラフ構築時に適用される。
    • ただし、動きがあまり直感的でないため、グラフ中に何度もenable_backpropを変更するような操作はしないほうが良い。
  • chainer.configurationにおいてtrainenable_backpropを内包しないので気をつける。
    • 標準ライブラリの chainer.extensions.Evaluatorはこれらを適切に設定してくれるので安心して大丈夫。
    • 自前のchainer.extensions.Evaluatorみたいなものを書くときは両方のconfigurationsを変更すること

目的

とうとう重い腰をあげてChainerとオレオレヘルパーライブラリをv2にあげようとしたところ、enable_backpropの挙動がきになったのでもメモ。
特に、下記の質問について考えてみる。

  • グラフの途中でenable_backpropが変わったらどうなるのだろうか。 .unchain_backward()みたいなことはできるのだろうか?
  • trainをFalseにしたら、enable_backpropも自動的にFalseになるのだろうか?
  • 標準ライブラリの chainer.extensions.Evaluatorは信用して使って大丈夫なのだろうか?

ちなみにこの投稿はv2.0.0時点のものです。

調査

グラフの途中でenable_backpropが変わったらどうなる?

contextmanagerを使った実装だと、グラフの途中でenable_backpropが変わったらどうなるのかがとても気になります。
もし、no_backprop_modebackward()実行時に効果を持ち、forward計算時にはno_backprop_modeを使っていたのにbackward計算をno_backprop_modeで囲い忘れたがために異常に良い精度がでてしまった...では困ります1

この答えは、function_node.pyにあります。ここでの記載のとおり、no_backprop_modeの効果は計算グラフ構築時に親を登録しないところにあるので、forward計算時に指定した範囲で効果を持ちます。

通常の実行

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('enable_backprop', True):
    chainer.config.show()
    b = a * 2.0
b.backward()
print a.grad
出力
cudnn_deterministic  False
debug                False
enable_backprop      True
keep_graph_on_report False
train                True
type_check           True
use_cudnn            auto
[ 2.]

backwardが範囲外にあってもちゃんと効果があることがわかります。

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('enable_backprop', False):
    chainer.config.show()
    b = a * 2.0
b.backward()
print a.grad
出力
cudnn_deterministic  False
debug                False
enable_backprop      False
keep_graph_on_report False
train                True
type_check           True
use_cudnn            auto
None

また、上記で述べたように、enable_backpropとの接続を断ち切ります。したがってcontextmanager内で新しく作られた変数ではなく、その親の勾配が0になります。

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('enable_backprop', False):
    b = a * 2.0
c = b + 0.5
c.backward()
print a.grad  # None
print b.grad  # [ 1.]

しかも、backwardが呼ばれた時のconfigurationに完全に依存しないかというと、そうではありません。なので、1つの計算グラフの中で何度もenable_backpropを使うような実装はしないで、素直にunchain_backward()を使ったほうがよさそうです。

trainをFalseにしたら、enable_backpropも自動的にFalseになる?

なりません。

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('train', False):
    chainer.config.show()
    b = a * 2.0
b.backward()
print a.grad
出力
cudnn_deterministic  False
debug                False
enable_backprop      True
keep_graph_on_report False
train                False
type_check           True
use_cudnn            auto
[ 2.]

なので、自分でextensions.Evaluatorみたいなコードを書く場合はenable_backproptrainの両方をFalseにする必要があります。

標準ライブラリの chainer.extensions.Evaluatorは信用して使って大丈夫?

大丈夫そうです。 enable_backproptrainの両方をFalseにしてくれています。

import chainer
import chainer.functions as F
import chainer.links as L
from chainer import training
from chainer.training import extensions

# Network definition
class MLP(chainer.Chain):
    def __init__(self, n_out):
        super(MLP, self).__init__()
        with self.init_scope():
            self.l1 = L.Linear(None, n_out)

    def __call__(self, x):
        chainer.config.show()
        print ""
        return self.l1(x)

model = L.Classifier(MLP(10))

optimizer = chainer.optimizers.Adam()
optimizer.setup(model)

# Load the MNIST dataset
train, test = chainer.datasets.get_mnist()
test = chainer.datasets.split_dataset(test, 1)[0]

train_iter = chainer.iterators.SerialIterator(train, 32)
test_iter = chainer.iterators.SerialIterator(test, 1,
                                             repeat=False, shuffle=False)
# Set up a trainer
updater = training.StandardUpdater(train_iter, optimizer)
trainer = training.Trainer(updater, (1, 'iteration'))
trainer.extend(extensions.Evaluator(test_iter, model), trigger=(1, 'iteration'))

# Run the training
trainer.run()
出力
cudnn_deterministic  False
debug                False
enable_backprop      True
keep_graph_on_report False
train                True
type_check           True
use_cudnn            auto

cudnn_deterministic  False
debug                False
enable_backprop      False
keep_graph_on_report False
train                False
type_check           True
use_cudnn            auto

ご覧のとおりenable_backproptrainの両方がFalseになっています。コードでいうとこのあたりが該当しています。


  1. もちろん、optimizerを呼ばないと実際にはデータが変わらないので、実用上このミスをおかしても問題ありませんが。 

7
2
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
7
2