
- 作者:斎藤勇哉
- 発売日: 2020/11/30
- メディア: Kindle版
目次
- 目次
- 目的
- 前準備
- 関連記事
- NEURAL NETWORKS(ニューラルネットワーク)
- ニューラルネットワークの定義
- Loss Function (損失関数)
- Backprop (誤差逆伝播)
- 重み(weights)の更新
目的
PyTorchのチュートリアルNeural Networksを参考にPyTorchでニューラルネットワークの構築と学習について学ぶ。
前準備
PyTorchのインストールはこちらから。
初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。
コマンドラインの「>>>」の行がPythonで実行するコマンドです。 それ以外の行は、コマンドの実行結果です。
関連記事
チュートリアル
- 【PyTorch】チュートリアル(日本語版 )① 〜テンソル〜
- 【PyTorch】チュートリアル(日本語版 )② 〜AUTOGRAD〜
- 【PyTorch】チュートリアル(日本語版 )③ 〜NEURAL NETWORKS(ニューラルネットワーク)〜
- 【PyTorch】チュートリアル(日本語版 )④ 〜TRAINING A CLASSIFIER(画像分類)〜
サンプル
- 【PyTorch】サンプル① 〜NUMPY〜
- 【PyTorch】サンプル② 〜TENSOR (テンソル) 〜
- 【PyTorch】サンプル③ 〜TENSORS AND AUTOGRAD(テンソルと自動微分)〜
- 【PyTorch】サンプル④ 〜Defining New autograd Functions(自動微分関数の定義)〜
- 【PyTorch】サンプル⑤ 〜Static Graphs(静的グラフ)〜
- 【PyTorch】サンプル⑥ 〜 nn パッケージ 〜
- 【PyTorch】サンプル⑦ 〜 optim パッケージ 〜
- 【PyTorch】サンプル⑧ 〜 複雑なモデルの構築方法 〜
- 【PyTorch】サンプル⑨ 〜 動的グラフ 〜
NEURAL NETWORKS(ニューラルネットワーク)
PyTorchのニューラルネットワークは、torch.nn
パッケージで作ることができます。
チュートリアル②では、autograd
を紹介しました。
これからご紹介するnn
でニューラルネットのモデルを定義をすることができます。nn.Module
には、様々なレイヤー(convolution, activation functionなど)やoutput
を返すforward(input)
メソッドがあります。
ニューラルネットワークの基本的なトレーニング手順は以下の通りです。
- ニューラルネットワーク(モデル)の適宜
- データをニューラルネットワークに入力
- loss (実際の正解とどれだけ異なっていたか)を計算
- lossが小さくなるようにニューラルネットワークのパラメータを更新
ニューラルネットワークの定義
実際に、ニューラルネットワークモデルを作って見ましょう。 以下のコードを実行してみます。
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)
最初に、必要なパッケージをimportします。
import torch import torch.nn as nn import torch.nn.functional as F
次に、class Net(nn.Module):
でニューラルネットワークモデル用のクラスを定義します。
使用するレイヤーの定義はここでされます。
この例では、2DのConvolutionレイヤーとAffine(fc)レイヤーが使われています。
nn.Conv2d(1, 6, 3)
は入力画像のチャネルが1、畳み込み後の出力が8チャネル、kernelが3x3になります。
nn.Linear(16 * 6 * 6, 120)
は、入力が1666、出力が120であることを示しています。
nn.Conv2d(6, 16, 3)
から1枚の画像サイズ6x6が16枚、出力されるのでそれらをflattenにするため、16 * 6 * 6という計算になっています。
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 __init__(self):
で定義したレイヤーを使って実際にモデルを構築しています。
この場合、Conv1 > ReLU > Max pooling > Conv2 > ReLU > Max pooling > fc1 > ReLU > fc2 > ReLU > fc3の順にデータが流れていきます。
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
作ったモデルを確認します。
net = Net()
print(net)
Out:
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) )
ニューラルネットワークの定義ができたので、このモデルに入力するためのデータを作成します。
乱数をつかって1チャネル32x32のデータを作って、先程のモデルに入力します。
このとき出力結果をout
に格納します。
input = torch.randn(1, 1, 32, 32) out = net(input) print(out)
最後のレイヤーは(fc3): Linear(in_features=84, out_features=10, bias=True)
なので10個のテンソルが出力されます。
Out:
tensor([[ 0.0658, 0.1263, -0.0645, -0.0134, 0.0501, -0.0124, 0.0317, -0.0344, 0.1009, -0.0080]], grad_fn=<AddmmBackward>)
一度、勾配(gradient)をゼロに初期化後、ニューラルネットワークからの出力out
の勾配を計算。
net.zero_grad() out.backward(torch.randn(1, 10))
Loss Function (損失関数)
損失関数は、目的となる答えとニューラルネットワークが出力した結果がどれだけ離れているかを計算します。
nn
パッケージには、様々な損失関数があります。
中でも最もシンプルなのがnn.MSELoss
です。
この損失関数では、実際の答えとニューラルネットワークの出力との平均二乗誤差(mean-squared error)を計算します。
output = net(input) target = torch.randn(10) # 適当にダミーの答えを乱数で作成 target = target.view(1, -1) # 出力と答えのサイズをそろえる。 criterion = nn.MSELoss() loss = criterion(output, target) print(loss)
Out:
tensor(0.4848, grad_fn=<MseLossBackward>)
OutputからInputにかけてのloss
を追う場合には、.grad_fn
属性を使います。これによって、このような計算グラフをみることができます。
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
ここで、loss.backward()
を呼ぶと全体の計算グラフを微分することができます。さらに、requires_grad=True
となっているテンソルは、.grad
属性を持ち、勾配が蓄積されていきます。
どのようにloss
を追っていくかは、以下のコードで確認します。
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
Out:
<MseLossBackward object at 0x7f60ae3a2f60> <AddmmBackward object at 0x7f60ae3a2eb8> <AccumulateGrad object at 0x7f60ae3a2f28>
Backprop (誤差逆伝播)
誤差逆伝播法は、loss.backward()
で実行することができます。
誤差逆伝播をする前に、net.zero_grad()
で勾配を一度リセットする必要があります。
conv1
における逆伝播前後の勾配をloss.backward()
を使って、見てみます。
net.zero_grad() # 勾配をゼロに print('conv1.bias.grad before backward') print(net.conv1.bias.grad) # 逆伝播前のconv1の勾配 loss.backward() # 逆伝播 print('conv1.bias.grad after backward') print(net.conv1.bias.grad) # 逆伝播後のconv1の勾配
Out:
conv1.bias.grad before backward tensor([0., 0., 0., 0., 0., 0.]) conv1.bias.grad after backward tensor([ 0.0111, -0.0162, 0.0002, -0.0016, -0.0076, -0.0027])
重み(weights)の更新
確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。
weight = weight - learning_rate * gradient
SGDは、Pythonコードで以下のようにして実装することができます。 この例では、学習率を0.01として、ニューラルネットワークにあるすべての重み(net.parameters())を更新しています。
learning_rate = 0.01 for f in net.parameters(): f.data.sub_(f.grad.data * learning_rate)
ただし、この最適化アルゴリズムはSGD, Nesterov-SGD, Adam, RMSPropなど様々であり、使い分けた場合があります。
その場合には、torch.optim
を使うとよいです。
SGDを使う場合は以下のようなコードになります。
import torch.optim as optim # 最適化アルゴリズムの作成 optimizer = optim.SGD(net.parameters(), lr=0.01) # in your training loop: optimizer.zero_grad() # 勾配のリセット output = net(input) loss = criterion(output, target) loss.backward() optimizer.step() # 重みの更新
頑張れ!、喝!!の代わりにB!ブックマークを押していただけるとただただうれしいです(^^)! ↓

- 作者:斎藤勇哉
- 発売日: 2020/11/30
- メディア: Kindle版