PyTorchのネットワークの書き方を徹底解説|カスタムレイヤー作成も説明!
- Pytorchのネットワークの定義方法を詳しく知りたい
- 人によってPytorchのネットワークの定義方法が異なってよくわからない…
その悩みを解決していきます!
本記事では、Pytorchのネットワークの定義方法を説明し、自由自在にネットワークを組むことを目標としています。
また、基本的な方法のみならず、より高度なカスタマイズである自作モジュールや初期値設定方法も紹介します。
Pytorchのモデルを設計するための準備
ここでは、PyTorchのモデル設計を行うための準備を行います。
モデル設計のためのライブラリをインポート
まずは、モデル設計のためのライブラリをインポートしていきましょう。
具体的には、下記のコードを実行してください。
import torch
import torch.nn as nn
import torch.nn.functional as F
以降、基本的には、PyTorchの『Tensor』に関しては、既知であることを想定して作成しています。『Tensor?』という方は、下記の記事を参考にしてから本記事に戻ってくることが望ましいです。
具体的なサンプルデータを生成
本記事では、具体的にネットワークを設計し、サンプルデータを実際にネットワークに入力し順伝搬の結果を出力するまでを実装します。
そのため、このタイミングでサンプルデータを生成しておきます。
<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種類あります。
- nn.Sequentialを使用する書き方
- ネットワークをモジュール化する書き方
以降、この二つの書き方を詳しく説明していきます。
nn.Sequentialを使用する書き方
まずは、nn.Sequential
を使用してモデルを設計するメリット・デメリットを説明し、その後具体的な書き方を説明します。
nn.Sequentialを使用するメリット・デメリット
nn.Sequentialを使用するメリット・デメリットは以下のようになります。
メリット
- 簡単にネットワークを定義できる
- 可読性が高い
デメリット
- カスタマイズ性が低い
わかりやすくコードが書けますが、複雑なネットワークを定義することができません。
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を継承)
次に、ネットワークをモジュール化する書き方を説明していきます。
同様に、モジュール化する書き方のメリット・デメリットを説明した後に、具体的な書き方を説明します。
ネットワークをモジュール化する書き方のメリット・デメリット
ネットワークをモジュール化する書き方のメリット・デメリットを説明します。
メリット
- カスタマイズ性が高い
- 再利用しやすい
デメリット
- 定義するのに手間がかかる
Pytorchの良さはカスタマイズ性なので、多くの方がこれから紹介するモジュール化する書き方を使用します。
ネットワークのモジュール化を実装
基本的な書き方は、nn.Moduleクラスを継承し、クラスに対して以下を定義します。
- __init__ : コンストラクタでネットワークを定義する
- 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で深層学習を実装してみたい!』という方は下記を参考にしてください。
Pythonを学習するのに効率的なサービスを紹介していきます。
まず最初におすすめするのは、Udemyです。
Udemyは、Pythonに特化した授業がたくさんあり、どの授業も良質です。
また、セール中は1500円定義で利用することができ、コスパも最強です。
下記の記事では、実際に私が15個以上の講義を受講して特におすすめだった講義を紹介しています。
他のPythonに特化したオンライン・オフラインスクールも下記の記事でまとめています。
自分の学習スタイルに合わせて最適なものを選びましょう。
また、私がPythonを学ぶ際に使用した本を全て暴露しているので参考にしてください。