1. 目的
2. 前準備
3. 計算グラフ
4. パッケージのインポート
5. モデルの定義
6. 使用するデータ
7. ニューラルネットワークのモデルの構築
8. 損失関数の定義
9. 最適化関数の定義
10. 学習回数
11. モデルへのデータ入出力 (Forward pass)
12. 損失(loss)の計算
13. 勾配の初期化
14. 勾配の計算
15. パラメータ(Weight)の更新
16. 実行
16.1. 9_dynamic_graph.py
1. 目的
- PyTorchの特徴の一つである動的グラフに挑戦する。
2. 前準備
PyTorchのインストールはこちらから。
初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。
3. 計算グラフ
計算グラフには、静的グラフと動的グラフがあります。
TensorFlowやTheanoといったフレームワークは静的グラフを用いていますが、それに対してPyTorchは動的グラフを採用しています。
静的グラフと動的グラフの説明についてはこちらをご覧ください。
簡単に言うと、静的グラフは、事前にニューラルネットワーク構造を決めてから学習・推定を実行します。この時、一度決めたネットワーク構造は学習や推定時に変更することができません。
これに対し、動的グラフは、ネットワーク構造を定義する時点でネットワーク構造を固定する必要はなく、ネットワークに入力されるデータの様子をみて、ネットワークを構造を適宜変えることができます。故に、動的な計算グラフになります。
この動的グラフにより、柔軟な学習や推測が可能となります。
4. パッケージのインポート
import random import torch
5. モデルの定義
【PyTorch】サンプル⑧ 〜 複雑なモデルの構築方法 〜で紹介した方法でニューラルネットワークモデルを構築していきます。
これまでは、レイヤ(層)を積み重ねるようにして、ニューラルネットワークモデルを定義していましたが、今回はネットワークモデルをクラスを使って定義します。クラスについては、こちらが参考になりました。
簡単のために、線形結合のみのネットワークを構築していきます。
クラスの名前をDynamicNet
とします。引数torch.nn.Module
はお決まりです。
class DynamicNet(torch.nn.Module):
コンストラクタで2層の線形結合層をインスタンス化します。
コンストラクタとインスタンスについては、こちらが参考になりました。
def __init__(self, D_in, H, D_out): super(DynamicNet, self).__init__() self.input_linear = torch.nn.Linear(D_in, H) self.middle_linear = torch.nn.Linear(H, H) self.output_linear = torch.nn.Linear(H, D_out)
コンストラクタで定義された線形結合層をつかってモデルの予測値y_pred
を計算します。
テンソルの演算式は、forward
オブジェクトで自由に定義できます。
このチュートリアルでは、動的グラフの特徴を感じてもらうために、不思議なモデルを作っていきます。
def forward(self, x): h_relu = self.input_linear(x).clamp(min=0) for _ in range(random.randint(0, 3)): h_relu = self.middle_linear(h_relu).clamp(min=0) y_pred = self.output_linear(h_relu) return y_pred
ポイントは、この部分です。
for _ in range(random.randint(0, 3)): h_relu = self.middle_linear(h_relu).clamp(min=0)
この部分でやっていることは、forループの回数を0〜3回をランダムに決めて、中間層の線形結合層の数を随時変更しています。
6. 使用するデータ
バッチサイズN
を64、入力の次元D_in
を1000、隠れ層の次元H
を100、出力の次元D_out
を10とします。
# N is batch size; D_in is input dimension; # H is hidden dimension; D_out is output dimension. N, D_in, H, D_out = 64, 1000, 100, 10
入力x
と予測したいy
を乱数で定義します。
# Create random input and output data x = torch.randn(N, D_in) y = torch.randn(N, D_out)
7. ニューラルネットワークのモデルの構築
先程作成したニューラルネットワークモデルのクラスDynamicNet
をつかってモデルmodel
を定義します。
model = DynamicNet(D_in, H, D_out)
8. 損失関数の定義
二乗誤差をこのモデルの損失関数とします。
reduction
のデフォルトはmean
ですので、何も指定しなければtorch.nn.MSELoss
は平均二乗誤差を返します。
reduction=sum
とした場合は、累積二乗誤差を算出します。
criterion = torch.nn.MSELoss(reduction='sum')
9. 最適化関数の定義
optim
パッケージを使って最適化関数としてMomentum SGDを定義します。
lr
は学習率です。
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
10. 学習回数
学習回数は500回にします。
for t in range(500):
11. モデルへのデータ入出力 (Forward pass)
定義したニューラルネットワークモデルmodel
へデータx
を入力し、予測値y_pred
を取得します。
y_pred = model(x)
12. 損失(loss)の計算
定義した損失関数Momentum SGD
で予測値y_pred
と真値y
との間の損失を計算します。
loss = criterion(y_pred, y) if t % 100 == 99: print(t, loss.item())
13. 勾配の初期化
逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。
optimizer.zero_grad()
14. 勾配の計算
backward
メソッドでモデルパラメータ(Weight)の勾配を算出します。
loss.backward()
15. パラメータ(Weight)の更新
step
メソッドでモデルのパラメータを更新します。
optimizer.step()
16. 実行
以下のコードを9_dynamic_graph.py
として保存します。
16.1. 9_dynamic_graph.py
import random import torch class DynamicNet(torch.nn.Module): def __init__(self, D_in, H, D_out): super(DynamicNet, self).__init__() self.input_linear = torch.nn.Linear(D_in, H) self.middle_linear = torch.nn.Linear(H, H) self.output_linear = torch.nn.Linear(H, D_out) def forward(self, x): h_relu = self.input_linear(x).clamp(min=0) for _ in range(random.randint(0, 3)): h_relu = self.middle_linear(h_relu).clamp(min=0) y_pred = self.output_linear(h_relu) return y_pred # N is batch size; D_in is input dimension; # H is hidden dimension; D_out is output dimension. N, D_in, H, D_out = 64, 1000, 100, 10 # Create random Tensors to hold inputs and outputs x = torch.randn(N, D_in) y = torch.randn(N, D_out) # Construct our model by instantiating the class defined above model = DynamicNet(D_in, H, D_out) # Construct our loss function and an Optimizer. criterion = torch.nn.MSELoss(reduction='sum') optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9) for t in range(500): # Forward pass: Compute predicted y by passing x to the model y_pred = model(x) # Compute and print loss loss = criterion(y_pred, y) if t % 100 == 99: print(t, loss.item()) # Zero gradients, perform a backward pass, and update the weights. optimizer.zero_grad() loss.backward() optimizer.step()
コードを保存したら、実行してみましょう。
左の数字が学習回数、右の数値がモデルの推定値と実際の答えと二乗誤差です。
python3 9_dynamic_graph.py
99 3.707427978515625 199 1.2958085536956787 299 1.837145209312439 399 0.9760080575942993 499 0.43565574288368225
参考のために、静的グラフの場合の結果を載せておきます。
動的グラフの方が損失の収束が早いことがわかります。
In:
python3 9_static_graph.py
Out:
99 2.5070412158966064 199 0.04455046355724335 299 0.0011508199386298656 399 3.597284376155585e-05 499 1.361027443635976e-06