PyTorch 1.8 チュートリアル : PyTorch の学習 : ニューラルネットワーク (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/21/2021 (1.8.0)
* 本ページは、PyTorch 1.8 Tutorials の以下のページを翻訳した上で適宜、補足説明したものです:
- Learning PyTorch : Neural Networks
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
人工知能研究開発支援 | 人工知能研修サービス | テレワーク & オンライン授業を支援 |
PoC(概念実証)を失敗させないための支援 (本支援はセミナーに参加しアンケートに回答した方を対象としています。) |
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ ; Facebook |
PyTorch の学習 : ニューラルネットワーク
ニューラルネットワークは torch.nn パッケージを使用して構築できます。
autograd を簡単に見た今、nn はモデルを定義してそれらを微分するために autograd に依拠します。nn.Module は層、そして出力を返すメソッド forward(input) を含みます。
例えば、数字画像を分類するこのネットワークを見てください :
convnet
それは単純な feed-forward ネットワークです。それは入力を取り、それを幾つかの層を次々に通して供給し、そして最後に出力を与えます。
ニューラルネットワークのための典型的な訓練手続きは以下のようなものです :
- 幾つかの学習可能なパラメータ (or 重み) を持つニューラルネットワークを定義する
- 入力のデータセットに渡り反復する
- 入力をネットワークを通して処理する
- 損失を計算する (出力が正解からどのくらい遠いか)
- 勾配をネットワークのパラメータに逆伝播する
- ネットワークの重みを更新する、典型的には単純な更新ルールを使用します :
weight = weight – learning_rate * gradient
ネットワークを定義する
このネットワークを定義しましょう :
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 3x3 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 3)
self.conv2 = nn.Conv2d(6, 16, 3)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
Net( (conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1)) (conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1)) (fc1): Linear(in_features=576, out_features=120, bias=True) (fc2): Linear(in_features=120, out_features=84, bias=True) (fc3): Linear(in_features=84, out_features=10, bias=True) )
貴方は forward 関数を定義しなければならないだけです、そして backward 関数 (そこでは勾配が計算されます) は autograd を使用して貴方のために自動的に定義されます。forward 関数では任意の Tensor 演算が使用できます。
モデルの学習可能なパラメータは net.parameters() で返されます。
params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
10 torch.Size([6, 1, 3, 3])
ランダムな 32×32 入力を試してみましょう。
Note: この net (LeNet) の想定される入力サイズは 32×32 です。MNIST データセット上でこのネットを使用するためには、データセットからの画像を 32×32 にリサイズしてください。
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[ 0.1464, 0.0673, -0.0185, 0.0048, 0.1427, -0.0866, -0.0237, -0.0991, -0.0903, 0.0886]], grad_fn=<AddmmBackward>)
総てのパラメータの勾配バッファをゼロにしてランダムな勾配で backprop します :
net.zero_grad()
out.backward(torch.randn(1, 10))
[Note]
torch.nn はミニバッチをサポートするだけです。torch.nn パッケージ全体は、単一のサンプルではない、サンプルのミニバッチである入力をサポートするだけです。
例えば、nn.Conv2d は サンプル数 x チャネル数 x 高さ x 幅 – nSamples x nChannels x Height x Width の 4D Tensor を取ります。
もし貴方が単一のサンプルを持つ場合には、fake バッチ次元を付加するために単に input.unsqueeze(0) を使用してください。
更に進める前に、ここまでに見た総てのクラスをまとめましょう。
まとめ:
- torch.Tensor – backward() のような autograd 演算のためのサポートを持つ多次元配列です。tensor に関する勾配もまた保持します。
- nn.Module – ニューラルネットワーク・モジュール。パラメータをカプセル化する便利な方法で、それらを GPU に移し、エクスポートし、ロードする等のためのヘルパーを持ちます。
- nn.Parameter – Tensor の一種で、それは Module への属性として割り当てられる時に自動的にパラメータとして登録されます。
- autograd.Function – autograd 演算の forward と backward 定義を実装します。総ての Tensor 演算は少なくとも単一の Function ノードを作成します、これは Tensor を作成した関数に接続してその履歴をエンコードします。
この時点で、以下をカバーしました :
- ニューラルネットワークを定義する
- 入力を処理して backward を呼び出す。
Still Left:
- 損失を計算する
- ネットワークの重みを更新する
損失関数
損失関数は入力の (output, target) ペアを取り、そして output が target からどのくらい遠く離れているかを推定する値を計算します。
nn パッケージの下には幾つかの様々な 損失関数 があります。単純な損失は : nn.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(1.0942, grad_fn=<MseLossBackward>)
さて、.grad_fn 属性を利用して、backward 方向に損失を追う場合、このように見える計算グラフを見るでしょう :
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d -> view -> linear -> relu -> linear -> relu -> linear -> MSELoss -> loss
そして、loss.backward() を呼び出す時、グラフ全体は損失に関して微分され、そして requires_grad=True を持つグラフの総ての Tensor は勾配で累積されたそれらの .grad Tensor を持ちます。
例として、2,3 のステップを後方に追いましょう :
print(loss.grad_fn) # MSELoss
print(loss.grad_fn.next_functions[0][0]) # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU
<MseLossBackward object at 0x7f0c0e10acf8> <AddmmBackward object at 0x7f0c0e10a2e8> <AccumulateGrad object at 0x7f0c0e10a2e8>
Backprop
誤差を逆伝播するために行なわなければならないことの総ては loss.backward() です。けれども既存の勾配をクリアする必要があります、そうでないなら勾配は既存の勾配に累積されます。
今は loss.backward() を呼び出します、そして backward 前後の conv1 の bias 勾配を見てみます。
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.0068, -0.0244, 0.0278, -0.0142, 0.0111, -0.0078])
今は、損失関数をどのように使用するかを見ました。
Read Later:
ニューラルネットワーク・パッケージは深層ニューラルネットワークのビルディングブロックを形成する様々なモジュールと損失関数を含みます。文書による完全なリストは こちら です。
The only thing left to learn is:
- ネットワークの重みを更新する。
重みを更新する
実際に使用される最も単純な更新ルールは確率的勾配降下 (SGD, Stochastic Gradient Descent) です :
weight = weight – learning_rate * gradient
これは単純な Python コードを使用して実装できます :
learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
けれども、ニューラルネットワークを使用する時、SGD, Nesterov-SGD, Adam, RMSProp 等のような幾つかの様々な更新ルールを使用することを望むでしょう。これを可能にするために、小さいパッケージ : torch.optim を構築しました、これはこれら総てのメソッドを実装しています。それを使用するのは非常に単純です :
import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update
Note:
どのように勾配バッファが optimizer.zero_grad() を使用して手動でゼロに設定されなければならないかを注視してください。これは Backprop セクションで説明されたように勾配が累積されるからです。
以上