PyTorch PR

PyTorchのネットワークの書き方を徹底解説|カスタムレイヤー作成も説明!

記事内に商品プロモーションを含む場合があります

PyTorchのネットワークの書き方を徹底解説|カスタムレイヤー作成も説明!

 

 

  • Pytorchのネットワークの定義方法を詳しく知りたい
  • 人によってPytorchのネットワークの定義方法が異なってよくわからない…

その悩みを解決していきます!

本記事では、Pytorchのネットワークの定義方法を説明し、自由自在にネットワークを組むことを目標としています。

また、基本的な方法のみならず、より高度なカスタマイズである自作モジュールや初期値設定方法も紹介します。

 

Pytorchのモデルを設計するための準備

 

ここでは、PyTorchのモデル設計を行うための準備を行います。

 

モデル設計のためのライブラリをインポート

 

まずは、モデル設計のためのライブラリをインポートしていきましょう。

具体的には、下記のコードを実行してください。

import torch
import torch.nn as nn
import torch.nn.functional as F

 

以降、基本的には、PyTorchの『Tensor』に関しては、既知であることを想定して作成しています。『Tensor?』という方は、下記の記事を参考にしてから本記事に戻ってくることが望ましいです。

 

【Pytorch】tensor型とは|知らないとまずいものをまとめてみたPyTorchのTensorに関してまとめてみました。この中で紹介しているTensorの関数やメソッドは、どれもPytorchで深層学習を実装する際に必要不可欠なものばかりです。15分程度で読み終わるので一読して頭を整理させましょう。...

 

具体的なサンプルデータを生成

 

本記事では、具体的にネットワークを設計し、サンプルデータを実際にネットワークに入力し順伝搬の結果を出力するまでを実装します。

そのため、このタイミングでサンプルデータを生成しておきます。

<Input>

sample = torch.randn(1, 10)
sample

 

<Output>

tensor([[ 0.4883,  0.9117, -0.3643,  0.9628,  1.9357, -0.6975, -0.5308, -0.0492,
          0.6824, -0.3260]])

 

今回は、特徴量の数が10個ある一つのデータをサンプルデータとして使用します。

 

ネットワークの設計方法の種類

 

PyTorchでネットワークを設計する方法は大きく分けて2種類あります。

  1. nn.Sequentialを使用する書き方
  2. ネットワークをモジュール化する書き方

以降、この二つの書き方を詳しく説明していきます。

 

nn.Sequentialを使用する書き方

 

まずは、nn.Sequentialを使用してモデルを設計するメリット・デメリットを説明し、その後具体的な書き方を説明します。

 

nn.Sequentialを使用するメリット・デメリット

 

nn.Sequentialを使用するメリット・デメリットは以下のようになります。

メリット

  1. 簡単にネットワークを定義できる
  2. 可読性が高い

デメリット

  1. カスタマイズ性が低い

 

わかりやすくコードが書けますが、複雑なネットワークを定義することができません。

 

nn.Sequentialの実装

 

今回は、活性化関数をReLU関数とした3層のネットワークを具体例として定義していきます。

下記のように、ネットワークの処理をまとめて書くだけで、複雑なネットワークを定義することができます。

<Input>

# Sequentialの書き方
model = nn.Sequential(
            nn.Linear(10, 32),
            nn.ReLU(),
            nn.Linear(32, 16),
            nn.ReLU(),
            nn.Linear(16, 10)
            )

print(model)

 

<Output>

Sequential(
  (0): Linear(in_features=10, out_features=32, bias=True)
  (1): ReLU()
  (2): Linear(in_features=32, out_features=16, bias=True)
  (3): ReLU()
  (4): Linear(in_features=16, out_features=10, bias=True)
)

 

 

nn.Sequentialの順伝搬

 

ネットワークが定義できたので、先ほどのサンプルデータを順伝搬させ、動作を確認しましょう。

<Input>

y = model(sample)
y

 

<Output>

tensor([[-3.0313e-01, -1.1111e-04,  7.7502e-02, -2.0157e-01,  3.7895e-03,
          5.1864e-02, -7.6282e-02, -5.4766e-03,  6.1397e-02,  1.0263e-01]],
       grad_fn=<AddmmBackward>)

 

このように、出力を計算することができましたね。

 

ネットワークをモジュール化する書き方(nn.Moduleを継承)

 

次に、ネットワークをモジュール化する書き方を説明していきます。

同様に、モジュール化する書き方のメリット・デメリットを説明した後に、具体的な書き方を説明します。

 

ネットワークをモジュール化する書き方のメリット・デメリット

 

ネットワークをモジュール化する書き方のメリット・デメリットを説明します。

メリット

  1. カスタマイズ性が高い
  2. 再利用しやすい

デメリット

  1. 定義するのに手間がかかる

 

Pytorchの良さはカスタマイズ性なので、多くの方がこれから紹介するモジュール化する書き方を使用します。

 

ネットワークのモジュール化を実装

 

基本的な書き方は、nn.Moduleクラスを継承し、クラスに対して以下を定義します。

  1. __init__ : コンストラクタでネットワークを定義する
  2. forwardメソッド : forwardメソッドで順伝搬を定義する

*コンストラクタ : 簡単に説明するとclassを呼び出したときに実行される部分です。

 

具体例を以下に示します。

# ネットワークのモジュール化
class Model(nn.Module):
    def __init__(self, input):
        super(Model, self).__init__()
        
        # ネットワークを定義
        self.linear1 = nn.Linear(input, 32)
        self.linear2 = nn.Linear(32, 16)
        self.linear3 = nn.Linear(16, 10)
        self.relu = nn.ReLU()

    # 順伝搬を定義
    def forward(self, x):
        x = self.linear1(x)
        x = self.relu(x)
        x = self.linear2(x)
        x = self.relu(x)
        x = self.linear3(x)
        return x

 

これで、Sequentialで定義したネットワークと同じネットワークを定義できます。

 

モジュール化したネットワークの順伝搬

 

先ほとモジュール化したネットワークに対して、サンプルデータを入力し、順伝搬の出力を行いましょう。

下記のコードを実行してください。

<Input>

# instance化
model = Model(input=10)

# 順伝搬
y = model(sample)
y

 

<Output>

tensor([[-0.1017, -0.2631, -0.1403,  0.1053,  0.0721, -0.0560,  0.2038, -0.1486,
          0.3030,  0.1571]], grad_fn=<AddmmBackward>)

 

この自作モジュールを複数使用してネットワークを作成することもできます。

 

モジュールを複数使用して新たなモジュールを作成可能!

 

モジュールを作成する方法は、複数のモジュールを簡単に組み合わせることが可能です。

簡単な例ですが、上記のネットワークを二つのモジュール使用して作成してみます。

まずは、活性化関数も含めた各層の処理を以下のコードでモジュール化します。

# Custum Layer
class CustomLayer(nn.Module):
    def __init__(self, input, output):
        super(CustomLayer, self).__init__()
        self.linear=nn.Linear(input, output)
        self.relu=nn.ReLU()
    def forward(self, x):
        x=self.linear(x)
        x=self.relu(x)
        return x

 

さらに、この自作モジュールを使用して、これまで定義したネットワークと同じネットワークを作成します。

# ネットワークをモジュール化
class Model(nn.Module):
    def __init__(self, input):
        super(Model, self).__init__()
        self.custom1 = CustomLayer(input, 32)
        self.custom2 = CustomLayer(32, 16)
        self.linear3 = nn.Linear(16, 10)

    def forward(self, x):
        x = self.custom1(x)
        x = self.custom2(x)
        x = self.linear3(x)
        return x

 

順伝搬を実装してみます。

<Input>

model = Model(input=10)
y = model(sample)
y

 

<Output>

tensor([[0.0000, 0.1822, 0.2371, 0.1343, 0.0995, 0.1915, 0.2539, 0.0000, 0.0348,
         0.0000]], grad_fn=<ReluBackward0>)

 

より高度なモデルを実装する際は、一つのモジュールに全てのネットワークを定義すると可読性が下がってしまいます。

今回は、簡単な例だったのでありがたみが上手く伝らなったかもしれませんが、このような再利用性とカスタマイズ性がPyTorchの強みの一つです。

 

 

応用 : パラメータの閲覧方法

 

パラメータを閲覧したい場合は、nn.parameters()を使用します。

しかし、nn.parameters()は、iteratorであり、for文を用いて以下のように取り出す必要があります。

class Model(nn.Module):
    def __init__(self, input):
        super(Model, self).__init__()
        self.linear = nn.Linear(input, 2)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.linear(x)
        x = self.relu(x)
        return x

model = Model(input=2)
for param in model.parameters():
    print(param)

 

<Output>

Parameter containing:
tensor([[ 0.3023, -0.3188],
        [ 0.3533,  0.1782]], requires_grad=True)
Parameter containing:
tensor([-0.2661,  0.6087], requires_grad=True)

 

net.state_dict()を使用する方法もあります。

print(model.state_dict())

<Output>

OrderedDict([('linear.weight', tensor([[-0.6966, -0.5847],
        [ 0.2982,  0.3681]])), ('linear.bias', tensor([ 0.5681, -0.2452]))])

 

 

 

応用 : モデルの初期値設定方法(重みの初期値設定方法)

 

PyTorchでモデルを作成すると適当な初期値が割り当てられます。

実際にどのような初期値が与えられているかを確認してみます。

# default
class Model(nn.Module):
    def __init__(self, input):
        super(Model, self).__init__()
        self.linear = nn.Linear(input, 2)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.linear(x)
        x = self.relu(x)
        return x

model = Model(input=2)
for param in model.parameters():
    print(param)

 

<Output>

Parameter containing:
tensor([[ 0.3023, -0.3188],
        [ 0.3533,  0.1782]], requires_grad=True)
Parameter containing:
tensor([-0.2661,  0.6087], requires_grad=True)

 

torch.nn.initを使用する方法

 

例えば、全ての初期値を全て0にする場合は、torch.nn.init.zeros_()を使用します。

class Model(nn.Module):
    def __init__(self, input):
        super(Model, self).__init__()
        self.linear = nn.Linear(input, 2)
        # parameterを全て0に初期化
        nn.init.zeros_(self.linear.weight)
        nn.init.zeros_(self.linear.bias)

        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.linear(x)
        x = self.relu(x)
        return x

model = Model(input=2)
for param in model.parameters():
    print(param)

 

<Output>

Parameter containing:
tensor([[0., 0.],
        [0., 0.]], requires_grad=True)
Parameter containing:
tensor([0., 0.], requires_grad=True)

 

他にも以下のようなバリエーションがあります。

  • torch.nn.init.ones_(param) : 重みを全て1に初期化
  • torch.nn.init.normal_(param, mean=0.0, std=1.0) : 重みを平均mean, 標準偏差stdの正規分布により初期化
  • torch.nn.init.uniform_(param, a=0.0, b=1.0) : 重みをaからbの範囲の一様乱数により初期化 

 

nn.Parameterを使用する方法

 

nn.Parameterを用いることを任意の値に初期化することができます。

class Model(nn.Module):
    def __init__(self, input):
        super(Model, self).__init__()
        self.linear = nn.Linear(input, 2)
        # parameterを任意の値に初期化
        self.linear.weight = nn.Parameter(torch.tensor([[1., 2.,], 
                                                        [3., 4.]]))
        self.linear.bias = nn.Parameter(torch.tensor([1., 2.]))

        self.relu = nn.ReLU()
    def forward(self, x):
        x = self.linear(x)
        x = self.relu(x)
        return x

model = Model(input=2)


for param in model.parameters():
    print(param)

 

<Output>

arameter containing:
tensor([[1., 2.],
        [3., 4.]], requires_grad=True)
Parameter containing:
tensor([1., 2.], requires_grad=True)

 

応用:カスタムレイヤーの作成方法

 

PyTorchに準備されていないカスタムレイヤーを作ることも可能です。

具体例として、重みの二乗ノルムを加算した線形層を作成してみます。

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        
        self.W = nn.Parameter(torch.tensor([[1., 1.],
                                            [1., 1]]))
        self.b = nn.Parameter(torch.tensor([1., 1.]))
        self.relu = nn.ReLU()

    def forward(self, x):
        x = torch.matmul(x, self.W) + self.b + (self.W**2).sum(dim=1)
        x = self.relu(x)
        return x

sample = torch.zeros(5, 2)
model = Model()
print("Output: ", model(sample).detach().numpy())

for param in model.parameters():
    print(param)

 

<Output>

Output:  [[3. 3.]
 [3. 3.]
 [3. 3.]
 [3. 3.]
 [3. 3.]]
Parameter containing:
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
Parameter containing:
tensor([1., 1.], requires_grad=True)

 

カスタムレイヤーを作成するときも、モデルの初期化と同様にnn.Parameterを用いて定義します。

また、nn.Parameterの引数は、PyTorchのTensorであり、重みの初期値となります。

 

まとめ

 

今回は、Pytorchのネットワークの書き方を二つ紹介しました。

特に後者のモジュールを作成する方法は、理解するのは少し難しいですが、複雑なネットワークを簡単に作成できる方法です。

あとは、DatasetとDataLoaderで作成したデータを入力し、パラメータを更新することでネットワークを学習することができます。

DatasetとDataLoaderに関しては下記を参考にしてください

 

本記事が皆様の役に立てたら光栄です…

『とりあえずPytorchで深層学習を実装してみたい!』という方は下記を参考にしてください。

 

【入門】PyTorchの使い方をMNIST画像分類で学ぶ|初心者でも理解可能!本記事では、MNISTデータセットを利用して、PyTorchの実装の流れを紹介しました。本記事を通して読めば、PyTorchを実務で使用するイメージが確実に身につくと思います。...

 

ABOUT ME
努力のガリレオ
【運営者】 : 東大で理論物理を研究中(経歴)東京大学, TOEIC950点, NASA留学, カナダ滞在経験有り, 最優秀塾講師賞, オンライン英会話講師試験合格, ブログと独自コンテンツで収益6桁達成 【編集者】: イングリッシュアドバイザーとして勤務中(経歴)中学校教諭一種免許取得[英語],カナダ留学経験あり, TOEIC650点
Python学習を効率化させるサービス

 

Pythonを学習するのに効率的なサービスを紹介していきます。

まず最初におすすめするのは、Udemyです。

Udemyは、Pythonに特化した授業がたくさんあり、どの授業も良質です。

また、セール中は1500円定義で利用することができ、コスパも最強です。

下記の記事では、実際に私が15個以上の講義を受講して特におすすめだった講義を紹介しています。

 

【最新】UdemyでおすすめのPythonコース|東大生が厳選!10万を超える講座があるUdemyの中で、Pythonに関係する講座を厳選しました。また、本記事では、Udemyを使用しながらPythonをどのような順番で勉強するべきかを紹介しました。ぜひ参考にしてください。...

 

他のPythonに特化したオンライン・オフラインスクールも下記の記事でまとめています。

 

【最新】Pythonに強いプログラミングスクール7選|東大生が厳選Pythonの流行と共に、Pythonに強いプログラミングスクールが増えてきました。本記事では、特にPythonを効率的に学ぶことができるプログラミングスクールを経験をもとに厳選して、内容を詳しく解説しています。...

 

自分の学習スタイルに合わせて最適なものを選びましょう。

また、私がPythonを学ぶ際に使用した本を全て暴露しているので参考にしてください。