菅間修正済み 2025/05/15
【原題】BUILD THE NEURAL NETWORK
【原著】 Suraj Subramanian、Seth Juarez 、Cassie Breviu 、Dmitry Soshnikov、Ari Bornstein
【元URL】https://pytorch.org/tutorials/beginner/basics/buildmodel_tutorial.html
【翻訳】電通国際情報サービスISID AIトランスフォーメーションセンター 小川 雄太郎
【日付】2021年03月18日
【チュトーリアル概要】
本チュートリアルでは、PyTorchでニューラルネットワークのモデルを構築する方法について解説します
PyTorchの全てのモジュールは、nn.Moduleを継承しています。
そしてニューラルネットワークは、モジュール自体が他のモジュール(レイヤー)から構成されています。
この入れ子構造により、複雑なアーキテクチャを容易に構築・管理することができます。
以下で、FashionMNIST datasetの画像データをクラス分類するネットワークモデルを構築します。
import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
可能であれば高速に訓練を行うためにも、GPU等を利用したいところです。
GPUが使用可能かどうかは、torch.cudaの結果から判断できます。
GPUが使用できない場合は、CPUで訓練を行います。
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('Using {} device'.format(device))
Using cuda device
nn.Moduleを継承し、独自のネットワークモデルを定義し、その後ネットワークのレイヤーを __init__で初期化します。
nn.Module を継承した全モジュールは、入力データの順伝搬関数であるforward関数を持ちます。
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
nn.ReLU()
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
NeuralNetworkクラスのインスタンスを作成し、変数device上に移動させます。
以下でネットワークの構造を出力し確認します。
model = NeuralNetwork().to(device)
print(model)
NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear_relu_stack): Sequential(
(0): Linear(in_features=784, out_features=512, bias=True)
(1): ReLU()
(2): Linear(in_features=512, out_features=512, bias=True)
(3): ReLU()
(4): Linear(in_features=512, out_features=10, bias=True)
(5): ReLU()
)
)
モデルを利用するには、入力データを与える必要があります。
入力データをモデルに投入すると、forward関数で処理されるとともに、いくつかのbackground operationsが実行されます。
そのため、 model.forward() と記載して入力データを処理しないように注意してください。
今回のモデルに入力を与えると、各クラスの生の予測値を含む10次元のテンソルが返されます。
nn.Softmaxモジュールにこの出力結果を与えることで、入力データが各クラスに属する確率の予測値を求めることができます。
X = torch.rand(1, 28, 28, device=device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred}")
Predicted class: tensor([4], device='cuda:0')
FashionMNISTモデルを各レイヤーレベルで確認しましょう。
サイズ28x28の3枚の画像からなるミニバッチのサンプルを用意し、このミニバッチをネットワークに入力し、各処理による変化を確認していきます。
input_image = torch.rand(3,28,28)
print(input_image.size())
torch.Size([3, 28, 28])
nn.Flatten
nn.Flattenレイヤーで、2次元(28x28)の画像を、1次元の784ピクセルの値へと変換します。
ミニバッチの0次元目は、サンプル番号を示す次元で、この次元はnn.Flattenを通しても変化しません(1次元目以降がFlattenされます)。
flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
torch.Size([3, 784])
layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())
torch.Size([3, 20])
nn.ReLU
非線形な活性化関数は、ニューラルネットワークの入力と出力の間にある、複雑な関係性を表現するために重要な要素です。
これらの活性化関数は線形変換のあとに、非線形性を加え、ニューラルネットワークの表現力を向上させる役割をします。
今回のモデルではnn.ReLUをlinear layerの間に用意しました。
なお、非線形活性化関数には他にも様々な種類があります。
print(f"Before ReLU: {hidden1}\n\n")
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")
Before ReLU: tensor([[ 0.1669, -0.2034, 0.0643, -0.0435, -0.0955, 0.1845, -0.3133, -0.0645,
-0.3299, 0.4222, -1.0488, 0.3029, 0.4046, -0.3916, 0.2352, -0.3289,
-0.3688, 0.6674, -0.0969, -0.3645],
[ 0.0417, 0.1629, 0.2122, 0.0110, -0.1328, 0.1351, -0.7230, 0.3554,
-0.3740, 0.3045, -0.6062, -0.0778, 0.4882, -0.1422, 0.1498, -0.2785,
-0.0507, 0.5722, 0.1246, 0.1802],
[ 0.0109, 0.1882, 0.2240, -0.0627, -0.5239, 0.5350, -0.6056, 0.2645,
-0.6044, 0.0847, -0.7515, 0.2747, 0.3523, -0.3462, 0.0146, -0.3359,
-0.0645, 0.6193, -0.2379, 0.5842]], grad_fn=<AddmmBackward>)
After ReLU: tensor([[0.1669, 0.0000, 0.0643, 0.0000, 0.0000, 0.1845, 0.0000, 0.0000, 0.0000,
0.4222, 0.0000, 0.3029, 0.4046, 0.0000, 0.2352, 0.0000, 0.0000, 0.6674,
0.0000, 0.0000],
[0.0417, 0.1629, 0.2122, 0.0110, 0.0000, 0.1351, 0.0000, 0.3554, 0.0000,
0.3045, 0.0000, 0.0000, 0.4882, 0.0000, 0.1498, 0.0000, 0.0000, 0.5722,
0.1246, 0.1802],
[0.0109, 0.1882, 0.2240, 0.0000, 0.0000, 0.5350, 0.0000, 0.2645, 0.0000,
0.0847, 0.0000, 0.2747, 0.3523, 0.0000, 0.0146, 0.0000, 0.0000, 0.6193,
0.0000, 0.5842]], grad_fn=<ReluBackward0>)
nn.Sequential
nn.Sequentialは、モジュールを順番に格納する箱のような要素です。
入力データはnn.Sequentialに定義された順番に各モジュールを伝搬します。
例えば以下の実装例のように、seq_modulesと名付けた、複数のモジュールを束ねたモジュールを簡単に構築できます。
seq_modules = nn.Sequential(
flatten,
layer1,
nn.ReLU(),
nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)
nn.Softmax
ニューラルネットワークの最後のlinear layerはlogits [- ∞, ∞] を出力します。
このlogitsはnn.Softmaxモジュールへと渡されます。
その結果、採取的な値は[0, 1]の範囲となり、これは各クラスである確率を示します。
dimパラメータは次元を示しており、dim=1の次元で和を求めると確率の総和なので1になります。
softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)
nn.Module を継承することで、モデルオブジェクト内で定義されたすべてのフィールドが自動的に追跡でき、parameters() や named_parameters() メソッドを使って、モデルの各レイヤーのすべてのパラメータにアクセスできるようになります。
以下にfor文を用いて、各パラメータを処理し、そのサイズと値を表示します。
print("Model structure: ", model, "\n\n")
for name, param in model.named_parameters():
print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
Model structure: NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear_relu_stack): Sequential(
(0): Linear(in_features=784, out_features=512, bias=True)
(1): ReLU()
(2): Linear(in_features=512, out_features=512, bias=True)
(3): ReLU()
(4): Linear(in_features=512, out_features=10, bias=True)
(5): ReLU()
)
)
Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[-0.0346, 0.0327, 0.0167, ..., 0.0209, -0.0141, 0.0356],
[ 0.0102, 0.0071, 0.0073, ..., -0.0241, 0.0041, 0.0159]],
device='cuda:0', grad_fn=<SliceBackward>)
Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([0.0353, 0.0058], device='cuda:0', grad_fn=<SliceBackward>)
Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[ 0.0287, 0.0135, 0.0429, ..., -0.0268, 0.0441, -0.0108],
[-0.0040, 0.0116, 0.0063, ..., 0.0297, 0.0293, -0.0179]],
device='cuda:0', grad_fn=<SliceBackward>)
Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([-0.0209, -0.0367], device='cuda:0', grad_fn=<SliceBackward>)
Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[ 0.0299, 0.0182, -0.0258, ..., 0.0223, -0.0016, 0.0400],
[-0.0118, 0.0364, -0.0230, ..., -0.0393, -0.0152, 0.0244]],
device='cuda:0', grad_fn=<SliceBackward>)
Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([-0.0238, -0.0124], device='cuda:0', grad_fn=<SliceBackward>)
以上。