【AIプログラミング】PyTorchで畳み込みニューラルネットワーク、チュートリアル Part3-4

PyTorchの畳み込みニューラルネットワークの続きです。損失関数、バックプロパゲーションなどを勉強しました。

もう少しでニューラルネットワークができそうだね。

PyTorchの畳み込みニューラルネットワークのチュートリアルがやっと完了しそうです。今回は、損失関数の定義、バックプロパゲーション、訓練という流れでチュートリアルを進めていきます。

今進めているチュートリアルはこちらです。

ニューラルネットワークの構成要素については、こちらの記事もご参考ください。

ぱんだクリップ
【AIプログラミング】ニューラルネットワークの要素技術について勉強① | ぱんだクリップ 何となくライブラリで使ってきましたが、中身について詳しく勉強しないとハイパーパラメータが理解できないと思いました。 なんとなく使えてしまうライブラリってすごいね...
ぱんだクリップ
【AIプログラミング】ニューラルネットワークの要素技術について勉強② | ぱんだクリップ 前回のニューラルネットワークの要素技術について勉強の続きです。 今回はバックプロパゲーションとかからかな。 ニューラルネットワークは、出力を行うときに値を変換する...
目次

損失関数を実装する

損失関数は、(出力,正解)という引数を与えて、モデルから出力された回答がどれだけ正解から遠いかを表現してくれるものです。
PyTorchのnnパッケージには、いくつかの損失関数が実装されていますが、チュートリアルでは、一番シンプルな「nn.MSELoss」を利用します。
MSELossは、平均二乗誤差などと呼ばれる誤差関数になります。
output = net(input)
target = torch.randn(10)  # a dummy target, for example
target = target.view(1, -1)  # make it the same shape as output
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)
tensor(0.6772, grad_fn=<MseLossBackward>)

outputには、randnで作成したランダムな出力を、target変数には、ランダムに作り出した仮の正解を与えます。

output、targetの値を作成できたら、criterion変数に誤差関数をインスタンス化します。そして、criterion(output,target)とすることで、モデルの出力した値と正解を誤差関数で比較します。

今回は、二乗誤差を計算します。

バックプロパゲーション

先の勾配を求めるというところで行なった操作と同じになります。勾配の初期化を行い、勾配を求める、という流れになります。最終的な誤差「loss」からbackwordを行うことで、ニューラルネットワークの各パラメータの勾配を求めることができます。

net.zero_grad()     # zeroes the gradient buffers of all parameters

print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([-0.0014,  0.0065,  0.0212, -0.0037, -0.0007, -0.0094])

backwordでなぜ勾配が求められるか、は以下の計算グラフやautogradのチュートリアルで勉強しましたので、ご参考ください。

ぱんだクリップ
【AIプログラミング】PyTorchのチュートリアルの前提、計算グラフについて勉強Part2-0 | ぱんだクリップ 自動微分という機能のチュートリアルに入りました。 いきなり自動微分というものが出てきたね。 PyTorchのチュートリアルの2番目に入ったところ、「自動微分(Auto Grad)」...

バックプロパゲーションを行う前に、まずは勾配を初期化しましょう。

このチュートリアルでは、「畳み込み層1」の6個の重みに対する勾配を確認をしています。

重みの更新を行う

ニューラルネットワークのもっともシンプルな重み(パラメータ)の更新方法は「確率的勾配降下法(SGD)」という方法で行われます。

・確率的勾配降下法の計算

更新後の重み」 = 「現在の重み」 – (学習率 × 勾配)

よくテストにでるって言われそうなやつですね。

このような式で一回の訓練(1エポック終了後)に重みを更新することになります。

先ほどのbackwordの流れですでに勾配は求められていますので、あとは学習率と掛け合わせて現在の重みから引き算し、その結果を新しい重みとして反映するだけです。

SGDの実装例としては次のようなプログラムになります。

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

学習率は、手動で設定する「ハイパーパラメータ」です。この値が大きいほど、早く重みが最適な値に収束するのですが、値が大きいと最適な重みを飛び越してしまう可能性もあります。逆に値が小さすぎると計算量が増えてしまうという点があります。

学習率(learning_rate)は、大抵0.001〜0.1などの値をとるらしいです。

そして、全てのパラメータに対してfor文でシンプルに計算をしています。

今回は、一番シンプルな、SGDこの重みの更新アルゴリズムは現状では「Adam」というより改良されたものが一般的に利用されるようです。学習率を訓練が進むに連れて小さくしたりして、より最適な重みを効率よく見つけられるようになっているとのことでした。scikit-learnのMLPでもデフォルトでAdamが選択されます。

また、このようにシンプルな確率的勾配降下法出会っても、PyTorchにすでに実装されているものを利用した方がみやすいプログラムとすることができます。

import torch.optim as optim

# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)

torch.optimを呼び出すことで、様々な最適化アルゴリズムを利用することができます。今回は、「optim.SGD」のようにして確率的勾配降下法を呼び出しています。ニューラルネットワークの全パラメータと、学習率を渡すことで、最適化アルゴリズムを全てやってくれますので、とても便利です。

訓練

今まで個別にみてきた

・データをネットワークに入力

・勾配を求める

・バックプロパゲーションをする

という個別の動作を組み合わせて訓練の処理を作ることができます。

#訓練ループ
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()    # Does the update

初期化→データの入力→損失を求める→損失から勾配を求める→パラメータを更新する

という流れになっています。最後の「optimizer.step()」は、ニューラルネットワークのパラメータを更新する、という処理になります。

この訓練の流れをエポック数分繰り返すことで重みを更新し、徐々に最適なネットワークへと変化していきます。

GitHubにてチュートリアルと、自分なりの解釈を入れたColaboファイルを配置します。

あわせて読みたい
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次