PyTorch 2.0 チュートリアル : 学習 : ニューラルネットワーク

PyTorch 2.0 チュートリアル : 学習 : ニューラルネットワーク (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/21/2023 (2.0.0)

* 本ページは、PyTorch 2.0 Tutorials の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

  • 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
  • sales-info@classcat.com  ;  Website: www.classcat.com  ;   ClassCatJP

 

PyTorch 2.0 チュートリアル : 学習 : ニューラルネットワーク

ニューラルネットワークは 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, 5x5 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120)  # 5*5 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 specify with a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = torch.flatten(x, 1) # flatten all dimensions except the batch dimension
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()
print(net)
Net(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=400, 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 関数では任意のテンソル演算が使用できます。

モデルの学習可能なパラメータは net.parameters() で返されます。

params = list(net.parameters())
print(len(params))
print(params[0].size())  # conv1's .weight
10
torch.Size([6, 1, 5, 5])

ランダムな 32×32 入力を試してみましょう。

Note: この net (LeNet) の想定される入力サイズは 32×32 です。MNIST データセット上でこのネットを使用するためには、データセットからの画像を 32×32 にリサイズしてください。

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[-0.0710, -0.0796, -0.1279, -0.0826,  0.1072, -0.0846, -0.0439, -0.0693,
         -0.0499,  0.0187]], grad_fn=)

総てのパラメータの勾配バッファをゼロにしてランダムな勾配で 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 テンソルを取ります。

もし貴方が単一のサンプルを持つ場合には、フェイクなバッチ次元を付加するために単に input.unsqueeze(0) を使用してください。

 
更に進める前に、ここまでに見た総てのクラスをまとめましょう。

まとめ:

  • torch.Tensor – backward() のような autograd 演算のためのサポートを持つ多次元配列です。テンソルに関する勾配もまた保持します。
  • nn.Module – ニューラルネットワーク・モジュール。パラメータをカプセル化する便利な方法で、それらを GPU に移し、エクスポートし、ロードする等のためのヘルパーを持ちます。
  • nn.Parameter – テンソルの一種で、それは Module への属性として割り当てられる時に自動的にパラメータとして登録されます。
  • autograd.Function – autograd 演算の forward と backward 定義を実装します。総てのテンソル演算は少なくとも単一の Function ノードを作成します、これはテンソルを作成した関数に接続してその履歴をエンコードします。

 
この時点で、以下をカバーしました :

  • ニューラルネットワークを定義する
  • 入力を処理して 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.0570, grad_fn=<MseLossBackward0>)

 
さて、.grad_fn 属性を利用して、backward 方向に損失を追う場合、このように見える計算グラフを見るでしょう :

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
      -> flatten -> linear -> relu -> linear -> relu -> linear
      -> MSELoss
      -> loss

そして、loss.backward() を呼び出す時、グラフ全体はニューラルネット・パラメータに関して微分され、そして requires_grad=True を持つグラフの総てのテンソルは勾配で累積されたそれらの .grad テンソルを持ちます。

例として、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
<MseLossBackward0 object at 0x7f9462c38250>
<AddmmBackward0 object at 0x7f9462c38580>
<AccumulateGrad object at 0x7f9462c3ba90>

 

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
None
conv1.bias.grad after backward
tensor([-0.0075,  0.0111, -0.0024, -0.0257,  0.0085,  0.0002])

今は、損失関数をどのように使用するかを見ました。

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 セクションで説明されたように勾配が累積されるからです。

 

以上