「PyTorch入門 4. モデル構築」¶

菅間修正済み 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でニューラルネットワークのモデルを構築する方法について解説します


ニューラルネットワークモデルの作り方¶

ニューラルネットワークは、レイヤー(もしくはモジュール)と呼ばれるデータ操作の固まりで構成されています。

torch.nnで用意されているクラス、関数は、独自のニューラルネットワークを構築するために必要な要素を網羅しています。

PyTorchの全てのモジュールは、nn.Moduleを継承しています。

そしてニューラルネットワークは、モジュール自体が他のモジュール(レイヤー)から構成されています。


この入れ子構造により、複雑なアーキテクチャを容易に構築・管理することができます。

以下で、FashionMNIST datasetの画像データをクラス分類するネットワークモデルを構築します。

In [ ]:
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で訓練を行います。

In [ ]:
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('Using {} device'.format(device))
Using cuda device

クラスの定義¶

nn.Moduleを継承し、独自のネットワークモデルを定義し、その後ネットワークのレイヤーを __init__で初期化します。

nn.Module を継承した全モジュールは、入力データの順伝搬関数であるforward関数を持ちます。

In [ ]:
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上に移動させます。

以下でネットワークの構造を出力し確認します。

In [ ]:
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モジュールにこの出力結果を与えることで、入力データが各クラスに属する確率の予測値を求めることができます。

In [ ]:
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枚の画像からなるミニバッチのサンプルを用意し、このミニバッチをネットワークに入力し、各処理による変化を確認していきます。

In [ ]:
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されます)。

In [ ]:
flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
torch.Size([3, 784])

nn.Linear

linear layerは、線形変換を施します。

linear layerは重みとバイアスのパラメータを保持しています。

In [ ]:
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の間に用意しました。

なお、非線形活性化関数には他にも様々な種類があります。

In [ ]:
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と名付けた、複数のモジュールを束ねたモジュールを簡単に構築できます。

In [ ]:
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になります。

In [ ]:
softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)

モデルパラメータ¶

ニューラルネットワークを構成する多くのモジュールは、おのおのパラメータを保持しています。

例えば、重みやバイアスです。これらの値が訓練時に最適化されます。

nn.Module を継承することで、モデルオブジェクト内で定義されたすべてのフィールドが自動的に追跡でき、parameters() や named_parameters() メソッドを使って、モデルの各レイヤーのすべてのパラメータにアクセスできるようになります。

以下にfor文を用いて、各パラメータを処理し、そのサイズと値を表示します。

In [ ]:
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>) 


さらなる詳細¶

以下のページも参考ください。

  • torch.nn API

以上。