メインコンテンツへスキップ

Chainerの自動微分で勾配を求める(補足)

·1343 文字·3 分
目次

はじめに
#

ChainerのVariableクラスを使った自動微分に関する記事である。前回記事の補足として、backwardメソッドを使用するときの注意点と、chainer.grad関数を使った自動微分の計算について述べる。

backwardメソッドを使用する度に勾配が加算されるため、2回以上使用するときは、勾配を除去する必要がある。また、chainer.grad関数を使うと、任意のVariable変数間の勾配を計算できる。

前回記事:Chainerの自動微分で勾配を求める

環境
#

ソフトウェア バージョン
Spyder 3.3.3
Python 3.7.3
NumPy 1.16.2
Chainer 6.3.0

以下では、各ライブラリを以下のようにインポートしていることを前提とする。

import numpy as np
import chainer
from chainer import Variable

backwardメソッドの注意点
#

chainer.Variablebackwardを使うと自動微分が計算される。ただし、Chainerの仕様上、backwardを実行するたびに、変数の勾配が加算されてしまう。そのため、2回以上backwardを実行すると、望ましい結果が得られなくなる。

: \(y = 2x\)の勾配を計算する。\(x=1\)に対する勾配は2であるが、2度backwardを実行すると、誤った勾配が出力される。

>>> x = Variable(np.array([1], dtype=np.float32))
>>> y = 2*x
>>> 
>>> y.backward() # 1回目の実行
>>> print(x.grad) # 正しい勾配
[2.]
>>> y.backward() # 2回目の実行
>>> print(x.grad) # 誤った勾配
[4.]

さらにy.backward()を実行するたびにxの勾配は2ずつ増えていく。すなわち、勾配が蓄積され続けている。

2回以上backwardを実行しても正しい勾配を得るためには、以下のようにbackwardを実行する度にcleargradで勾配を削除してやる必要がある。

>>> x = Variable(np.array([1], dtype=np.float32))
>>> y = 2*x
>>> 
>>> y.backward() # 1回目の実行
>>> print(x.grad) # 正しい勾配
[2.]
>>> x.cleargrad() # xの勾配を削除
>>> y.backward() # 2回目の実行
>>> print(x.grad) # 正しい勾配
[2.]

chainer.gradによる勾配計算
#

chainer.grad関数を使うと、任意のVariable変数間の勾配を計算できる。主な引数を以下に示す。

chainer.grad(outputs, inputs, set_grad=False, retain_grad=False)

引数の説明は以下の通り。

  • outputs (tuple or list of Variable): 逆誤差伝搬の起点となる、出力の変数。
  • inputs (tuple or list of Variable): 勾配を計算する入力側の変数。
  • set_grad (bool): Trueの場合、inputsの変数のgradに勾配が格納される。デフォルトはFalse
  • retain_grad (bool): Trueの場合、中間変数のgradに勾配が格納される。デフォルトはFalse

chainer.grad関数では、必要最小限の計算パスのみを対象として勾配が計算される。 なお、backwardと異なり、chainer.gradを何度実行しても勾配は蓄積されず、同じ結果が得られる。

#

以下の計算グラフを考える。 実行結果1~5では、全て以下の変数を使用している。

x0 = Variable(np.array([0,1], dtype=np.float32))
x1 = Variable(np.array([2,3], dtype=np.float32))
y = 2*x0 + 3*x1
z = 2*y

実行結果1
#

zに対するx0の勾配を計算する。

>>> chainer.grad([z], [x0])
[variable([4., 4.])]
>>> print(x0.grad) # set_grad=Falseの時、勾配は残らない
None
>>> chainer.grad([z], [x0]) # cleargradしなくても同じ結果になる
[variable([4., 4.])]

実行結果2
#

zに対するx0, x1の勾配を計算する。

>>> chainer.grad([z], [x0, x1])
[variable([4., 4.]), variable([6., 6.])]

実行結果3
#

yに対するx0の勾配を計算する。

>>> chainer.grad([y], [x0])
[variable([2., 2.])]

実行結果4
#

gradに勾配を残す

>>> chainer.grad([y], [x0], set_grad=True)
[variable([4., 4.])]
>>> print(x0.grad)
array([4., 4.], dtype=float32)

実行結果5
#

中間変数yに勾配を残す

>>> chainer.grad([y], [x0], retain_grad=True)
[variable([4., 4.])]
>>> print(y.grad) # 中間変数
array([2., 2.], dtype=float32)
>>> print(x0.grad) # 入力変数には勾配は残らない
None
Helve
著者
Helve
関西在住、電機メーカ勤務のエンジニア。X(旧Twitter)で新着記事を配信中です

関連記事

Chainerの自動微分で勾配を求める
·2221 文字·5 分
ChainerのVariableクラスを使った1階微分、2階微分の求め方について解説する。
ChainerのIteratorクラスによる学習用ミニバッチ作成
·2129 文字·5 分
データセットから学習用ミニバッチを作成してくれるIteratorクラスの動作を確認する。
Chainer入門 最小限のニューラルネットワーク実装
·1600 文字·4 分
ディープラーニング用のライブラリChainerの使い方を理解するため、ChainerのChainクラスとOptimizerを使って最小限のニューラルネットワーク (NN) を実装する。
scikit-learnのBaggingClassifierでバギングする
·2756 文字·6 分
BaggingClassifierを用いた学習(バギング、ペースティング、ランダムサブスペース、ランダムパッチ)について解説する。
Scikit-learnの主成分分析 (PCA)
·1432 文字·3 分
Scikit-learnのPCAクラスのパラメータ、属性とメソッドについて解説する。
Scikit-learnの正則化付き重回帰モデル
·2498 文字·5 分
Scikit-learnに実装されている重回帰、Ridge回帰、Lasso回帰、Elastic Netのロジックと使用方法をまとめた。