PyTorchのニューラルネットワークチュートリアルの続きです。日々発見ばかりです。
新しい発見があることは素晴らしいよね。
PyTorchの畳み込みニューラルネットワークの作成チュートリアルの続きを行っていきます。前回は、Pythonのクラスによるニューラルネットワークの定義と、インスタンス化でニューラルネットワークを作成しました。今回は作成した畳み込みニューラルネットワークのパラメータの確認、試しに入力、試しにネットワークの勾配を求めるという内容になります。
今やっているチュートリアルはこちらです。
こんな人の役に立つかも
・機械学習プログラミングを勉強している人
・PyTorchで畳み込みニューラルネットワークをプログラミングしたい人
・PyTorchのチュートリアルをやっている人
パラメータ確認
前回の記事で定義した畳み込みニューラルネットワークのパラメータを確認できるようです。
前回の記事はこちらをご参考ください。
「パラメータ」は、ニューラルネットワークが訓練で調整する「重み」のことです。
次のようなプログラムで確認できます。
試しに全てのパラメータをfor文で確認です。
params = list(net.parameters())
print(len(params))
#print(params[0].size()) # conv1's .weight
for i in range(0,10):
print(params[i].size())
パラメータとなるものは全部で10要素あるようです。「param」をlenした値です。
また、param[0]の形は、「6,1,3,3」となっています。これはちょうど畳み込みのフィルターの形ですね。畳み込み層においては、パラメータとしてフィルタの値を調整するのでこのようになっているのですね。
それぞれの層の後にある出力と同じ数のサイズのパラメータは、おそらくそれぞれの層に対する「バイアス」だと思います。きっとバイアス。
10
torch.Size([6, 1, 3, 3])
torch.Size([6])
torch.Size([16, 6, 3, 3])
torch.Size([16])
torch.Size([120, 576])
torch.Size([120])
torch.Size([84, 120])
torch.Size([84])
torch.Size([10, 84])
torch.Size([10])
このように、各層でのパラメータが一覧となって出てきます。
実際には、param[0]〜param[9]のようにパラメータが格納されています。試しにparam[0]の中身をのぞいてみます。
print(params[0])
6画像分の3×3のフィルターがイメージできる形になっています。
Parameter containing:
tensor([[[[ 0.0695, 0.0303, 0.0173],
[-0.2240, 0.0639, -0.2337],
[-0.2131, 0.2986, -0.2696]]],
[[[ 0.1750, 0.0514, 0.0301],
[ 0.1078, 0.2482, 0.1064],
[ 0.1301, -0.0850, -0.2420]]],
[[[ 0.1912, 0.1707, 0.3020],
[ 0.0112, -0.3174, 0.2948],
[-0.0987, 0.1531, 0.3158]]],
[[[ 0.1970, -0.0322, -0.0359],
[ 0.1169, -0.0205, 0.0318],
[ 0.2110, 0.1198, 0.1671]]],
[[[-0.0275, -0.2379, 0.2570],
[ 0.2165, -0.2671, -0.0809],
[ 0.1483, 0.1285, -0.2680]]],
[[[ 0.3011, -0.1690, 0.3042],
[ 0.3217, -0.2489, -0.1040],
[ 0.2947, -0.0370, -0.2040]]]], requires_grad=True)
ここで、少し疑問点が生まれました。
チュートリアルのトップの絵が、入力画像32×32から畳み込み層1へ畳み込み処理が行われる際に、28×28の画像サイズになっていることが腑に落ちませんでした。というのも32×32を3×3のフィルターで畳み込みを行うと、画像サイズは30×30になるからです。
このあと色々調べた結果、トップの絵の画像サイズは少し古いものであることがわかりました。昔のチュートリアルはフィルターサイズが5×5だったのでしょうか。この絵に描かれている畳み込み層1のフィルターは5×5で畳み込みがされているので、トップの絵は本当に畳み込みのイメージとして利用されているだけなんですね^^;
ネットワークに画像を入力してみる
チュートリアルでは、画像とみなしたランダムに生成したテンソルを入力します。なので、お試し順伝播をやっているイメージです。
input = torch.randn(1, 1, 32, 32)
#(バッチサイズ,チャンネル,画像縦,画像横)
print(input)
tensor([[[[-1.2612, 0.7079, 2.0341, ..., -0.8087, 0.7575, -0.9846],
[-0.4048, -0.3351, -0.3069, ..., -0.7463, -0.3623, -0.3151],
[-0.5288, -0.7322, -1.5237, ..., 1.0893, -1.1746, 0.6117],
...,
[-0.9273, 0.3408, -0.5957, ..., 1.6906, 0.1216, 0.1938],
[-0.1306, 0.2935, 1.0064, ..., 0.1979, 0.0738, 0.5362],
[ 0.3193, -1.3578, -1.4728, ..., -0.3659, -1.1556, 0.9079]]]])
テンソルをこのように4階のテンソルとして生成しているのは、畳み込み層の入力が、(バッチサイズ、チャンネル数、画像縦、画像横)というデータ形式を取るためです。一枚だけの画像でもこのようにバッチサイズとチャンネル数の情報を付加してあげないとネットワークに入力できないので注意です。
次のようにして、画像(ランダム生成したテンソル)をネットワークに入力できます。outには現状のネットワークの予測結果が格納されます。
10分類問題なので、10個値として出力されます。
out = net(input)
print(out)
tensor([[-0.1081, -0.1610, 0.0489, 0.0415, 0.1231, 0.1060, 0.0352, -0.0925, 0.1267, 0.0336]], grad_fn=<AddmmBackward>)
これで、ネットワークを定義して画像を入力して利用するまでの流れが理解できたような気がします。
試しに勾配を求めてみる
勾配を求めるbackwordを使ってみるチュートリアルです。
勾配を求める際に、まず勾配を初期化する必要があるようです。
また、以前autogradのところで勉強したbackwordメソッドで勾配を求めてみます。出力「out」が1×10の形なので、backwordには次のようなテンソル(ベクトル)を指定します。今回は、randnで生成したランダムな勾配(10個の値が入ったベクトル)で勾配を求めています。
#勾配の初期化
net.zero_grad()
#backwordメソッドで勾配を求める
out.backward(torch.randn(1, 10))
ちょっとまだこのランダムな勾配でbackwordする意味がいまいち理解できていないのでなんとも言えないのですが、チュートリアルを進めていきたいと思います。
ランダムな値で試しすぎていてなんだかチュートリアルの具体性にかけている気もします。
一つ今回チュートリアルをやってみてしっかり理解できたことがあります。
・順伝播は用途によって色々カスタマイズする必要があるのでメソッドとして実装
・逆伝播は勾配を計算するという決まった動作のため、autogradに組み込まれている
ということを身をもって理解することができました。
次の記事はこちらです。