Learning Pytorch with examples
TL;DR
PytorchのLearning Pytorch with examplesのまとめ
Pytorchで二層のネットワークを愚直に書いてみる
GPUを扱えるようにPyTorchを使いましたが、numpyでも簡単に再現できる二層のニューラルネットワークを作成しました。これをうまくPytorchっぽく書き直していこうと思います。
import torch
import numpy as np
import matplotlib.pyplot as plt
# device, dtypeの定義
dtype = torch.float
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 入力と出力、レイヤーごとの重みの定義
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)
# 学習率などの定義
learning_rate = 1e-6
# plotのための準備
t_list=np.arange(0,100)
loss_list=[]
for t in range(100):
#forward
h = x.mm(w1)
h_relu = h.clamp(min=0)
y_pred = h_relu.mm(w2)
# lossの計算
loss = (y_pred - y).pow(2).sum().item()
loss_list.append(loss)
#backward
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.t().mm(grad_y_pred)
grad_h_relu = grad_y_pred.mm(w2.t())
grad_h = grad_h_relu.clone()
grad_h[h < 0] = 0
grad_w1 = x.t().mm(grad_h)
# 重み行列の更新
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
plt.scatter(t_list, loss_list)
classやnnモジュールを使ってより抽象的に書く
1. Classとnnモジュールを使って二層のニューラルネットワークの構造と順伝播を定義する
愚直に書いたやつ
基本的に非常に単純で、クラスで表すと実は長くなるけど、ネットワークを重ねるごとにクラスを使うメリットが出てくる。
# forward
h = x.mm(w1)
h_relu = h.clamp(min=0)
y_pred = h_relu.mm(w2)
Classとnnモジュールを使って書いたやつ
-
nn.Liner
:FC層を作る -
F.relu
:relu関数
import torch.nn as nn
import torch.nn.functional as F
# torch.nn.Moduleを継承してクラスを作る
class TwoLayerNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
#TwoLayerNetの継承
super(TwoLayerNet, self).__init__()
self.fc1=nn.Linear(D_in, H)
self.fc2=nn.Linear(H, D_out)
def forward(self, x):
#順伝播の時に起こる処理を書く
h=F.relu(self.fc1(x))
y=self.fc2(h)
return y
2. 損失関数を定義
# 愚直に書いたやつ
loss = (y_pred - y).pow(2).sum().item()
# nnモジュールを使ったやつ
criterion=nn.MSELoss(reduction='sum')
loss=criterion(y_pred, y)
3. 自動微分
これは非常に短くなる。loss関数を微分していくのはこのくらいならかけるけど大きくなると大変になるが、nnモジュールを使えば一行でかける。
愚直に書いたやつ
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.t().mm(grad_y_pred)
grad_h_relu = grad_y_pred.mm(w2.t())
grad_h = grad_h_relu.clone()
grad_h[h < 0] = 0
grad_w1 = x.t().mm(grad_h)
nnモジュールを使った場合
loss.backward()
4. optimizerの定義
MomentumSGDやAdamなんかが気軽に使える。複雑なモデルになってもこれで一瞬。
愚直に書いたやつ
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
optimizerを作成する場合
ここでモデルを作ったのが効いてくる。愚直に書くと重み行列の量だけ更新する必要があるが、optimizerをすれば複雑なモデルでもやってくれる。lr=learning rate
はSGDなので低め。momentum=0.9
などとすることでMomentumSGDになるし、optimizer=torch.optim.Adam(model.parameters()
とかすればAdamが使える。
optimizer=torch.optim.SGD(model.parameters(), lr=1e-4)
5. model verで学習させる。
最後にこれまでのをまとめると以下のようなコードになる。構造がわかりやすくなっていると思う。
import torch.nn as nn
import torch.nn.functional as F
# modelの作成
class TwoLayerNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
super(TwoLayerNet, self).__init__()
self.fc1=nn.Linear(D_in, H)
self.fc2=nn.Linear(H, D_out)
def forward(self, x):
h=F.relu(self.fc1(x))
y=self.fc2(h)
return y
model=TwoLayerNet(D_in, H, D_out)
model=model.to(device) #modelをGPUへ送る
# 損失関数とoptimizerの定義
criterion=nn.MSELoss(reduction='sum')
optimizer=torch.optim.SGD(model.parameters(), lr=1e-4)
# データ作成
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
t_list=np.arange(0,100)
loss_list=[]
# 学習
for t in range(100):
#順伝播
y_pred=model(x)
#損失関数の計算
loss=criterion(y_pred, y)
loss_list.append(loss.cpu().detach().numpy()) # variableかつGPUなのでCPUに移してnumpyに変換できるようにする
#optimzeされてTensorを初期化する
optimizer.zero_grad()
#自動微分
loss.backward()
#optimizerによる重みの更新
optimizer.step()
plt.scatter(t_list,loss_list)
結論
とりあえずどの部分がどの部分に当たるのかはなんとなくわかった。次は転移学習しましょう。