import torch
PyTorchによる深層学習
簡単な計算
PyTorchパッケージを用いて \(N\) 行 \(D\) 列のランダムな行列 \(x,y,z\) を作り, \(c = \sum x*y + z\) を計算をする. 最後に, 計算された \(c\) の \(x\) に対する勾配(微分値)をbackwardで計算する.
randでランダムな行列をを生成する際に, 引数 requires_grad を True に設定しておくことによって, 勾配が計算される.
変数に対しては, dataで値が, 勾配を計算するように指定した変数に対しては gradで勾配が得られる.
= 3,4 #3行4列のランダムなテンソルを生成し, 勾配を計算する
N, D = torch.rand( (N, D), requires_grad=True)
x = torch.rand( (N, D), requires_grad=True)
y = torch.rand( (N, D), requires_grad=True)
z
= x*y
a = a+z
b = torch.sum(b)
c
c.backward()
c.data
tensor(7.9498)
print(x.data)
tensor([[0.1379, 0.2976, 0.8908, 0.8022],
[0.2833, 0.0333, 0.8762, 0.2876],
[0.3688, 0.8043, 0.9437, 0.0616]])
print(x.grad)
tensor([[0.7403, 0.0179, 0.0335, 0.0769],
[0.5766, 0.5749, 0.5347, 0.4289],
[0.7062, 0.0719, 0.6709, 0.1248]])
最小2乗法
\(y = a x\) のパラメータ \(a\) の最適化を最小2乗法によって行う.
from_numpyでNumPyの配列をPyTorchのテンソルに変換できる.
パラメータ \(a\) をランダムに設定し,勾配を計算するように指示する.
予測値 yhat を計算した後で, 損出関数 loss を誤差の2乗平均として計算し, backward で勾配を計算する.
勾配の逆方向に学習率 lr (learning rate) だけ移動させたものを新しい \(a\) とし, それをn_epochs回繰り返す.
ここで,反復ごとに勾配を \(0\) に初期化する a.grad.zero_ を呼び出すことに注意されたい.
import numpy as np
import torch
import torch.optim as optim
import torch.nn as nn
= np.array([1.0, 2.0, 3.0, 4.0, 5.0], dtype=np.float32)
x_numpy = np.array([15.0, 20.0, 34.0, 42.0, 58.0], dtype=np.float32)
y_numpy
= torch.from_numpy(x_numpy)
x = torch.from_numpy(y_numpy)
y
= torch.rand(1, requires_grad=True) #スカラーを定義
a
= 10 #反復回数(エポック数)
n_epochs = 0.01 #学習率
lr for epoch in range(n_epochs):
= a*x
yhat = y - yhat
error =(error**2).mean()
loss
loss.backward()
with torch.no_grad():
-= lr*a.grad
a
print(loss.data, a.data)
a.grad.zero_()
tensor(1317.2814) tensor([2.6670])
tensor(803.9544) tensor([4.5403])
tensor(491.6464) tensor([6.0014])
tensor(301.6381) tensor([7.1411])
tensor(186.0371) tensor([8.0301])
tensor(115.7055) tensor([8.7234])
tensor(72.9157) tensor([9.2643])
tensor(46.8824) tensor([9.6861])
tensor(31.0437) tensor([10.0152])
tensor(21.4075) tensor([10.2719])
線形回帰
クラス
nn.Moduleクラスから派生させて線形回帰を行うクラス LinearRegression を作る.
コンストラクタ init で親クラスを呼び出した後でパラメータ \(a\) を定義する.
与えられた \(x\) に対して予測値 \(y=ax\) を計算するための関数 forward を定義する.
モデルのインスタンス model のstate_dicメソッドでパラメータ \(a\) の現在の値の辞書を得ることができる.
class LinearRegression(nn.Module):
#コンストラクタ
def __init__(self):
super().__init__() #親クラスのコンストラクタを呼ぶ
self.a = nn.Parameter(torch.rand(1, requires_grad=True)) #モデルのパラメータを準備
#予測値の計算
def forward(self, x):
return self.a*x
= LinearRegression()
model
#パラメータと現在の値の辞書 model.state_dict()
OrderedDict([('a', tensor([0.5324]))])
訓練
損出関数を計算する関数 loss_fn を最小2乗誤差 nn.MSELoss とし, 最適化は率的勾配降下法 (SGD: Stochastic Fradient Descent) optim.SDG とする. 引数はモデルのパラメータ model.parameters() と学習率 lr である.
optimizerのstepで勾配降下法の1反復を行い, 反復ごとに zero_grad で勾配を \(0\) に初期化する.
= nn.MSELoss() #損出関数(最小2乗誤差)
loss_fn = optim.SGD(model.parameters(), lr) # 最適化(確率的勾配降下法)を準備; model.parameters()はパラメータを返すジェネレータ
optimizer
for epoch in range(n_epochs):
#モデルを訓練モードにする
model.train() = model(x) #forwardメソッドで予測値を計算する
yhat = loss_fn(y, yhat) #損出関数
loss #誤差逆伝播で勾配を計算
loss.backward()
#最適化の1反復
optimizer.step() #勾配を0にリセット
optimizer.zero_grad()
print(loss.data, model.state_dict())
tensor(1253.9412) OrderedDict([('a', tensor([2.8753]))])
tensor(765.4183) OrderedDict([('a', tensor([4.7027]))])
tensor(468.2010) OrderedDict([('a', tensor([6.1281]))])
tensor(287.3740) OrderedDict([('a', tensor([7.2399]))])
tensor(177.3588) OrderedDict([('a', tensor([8.1072]))])
tensor(110.4256) OrderedDict([('a', tensor([8.7836]))])
tensor(69.7034) OrderedDict([('a', tensor([9.3112]))])
tensor(44.9280) OrderedDict([('a', tensor([9.7227]))])
tensor(29.8547) OrderedDict([('a', tensor([10.0437]))])
tensor(20.6841) OrderedDict([('a', tensor([10.2941]))])
線形層の追加
nn.Linear(入力数, 出力数)で線形層をモデルに追加する. モデルは \(y = w_0 + w_1 x\) となる.
データの \(x,y\) は縦ベクトル(shapeは (5,1) )になおしておく.
class LayerLinearRegression(nn.Module):
#コンストラクタ
def __init__(self):
super().__init__() #親クラスのコンストラクタを呼ぶ
self.linear = nn.Linear(1,1, dtype=torch.float32) #1入力・1出力の線形層
#self.linear =nn.Sequential(nn.Linear(1,2), nn.ReLU(), nn.Linear(2,1)) #多層のモデルもSequentialを用いて作れる
#予測値の計算
def forward(self, x):
return self.linear(x)
= LayerLinearRegression()
model
#パラメータと現在の値の辞書 model.state_dict()
OrderedDict([('linear.weight', tensor([[-0.9852]])),
('linear.bias', tensor([0.3170]))])
= x.reshape(-1,1)
x = y.reshape(-1,1) y
= nn.MSELoss() #損出関数(最小2乗誤差)
loss_fn = optim.SGD(model.parameters(), lr) # 最適化(確率的勾配降下法)を準備; model.parameters()はパラメータを返すジェネレータ
optimizer
=10
n_epochsfor epoch in range(n_epochs):
#モデルを訓練モードにする
model.train() = model(x) #forwardメソッドで予測値を計算する
yhat = loss_fn(y, yhat) #損出関数
loss #誤差逆伝播で勾配を計算
loss.backward()
#最適化の1反復
optimizer.step() #勾配を0にリセット
optimizer.zero_grad()
print(loss.data, model.state_dict())
tensor(1611.6226) OrderedDict([('linear.weight', tensor([[1.6726]])), ('linear.bias', tensor([1.0458]))])
tensor(942.0174) OrderedDict([('linear.weight', tensor([[3.7018]])), ('linear.bias', tensor([1.6005]))])
tensor(551.8026) OrderedDict([('linear.weight', tensor([[5.2514]])), ('linear.bias', tensor([2.0224]))])
tensor(324.4026) OrderedDict([('linear.weight', tensor([[6.4348]])), ('linear.bias', tensor([2.3428]))])
tensor(191.8832) OrderedDict([('linear.weight', tensor([[7.3385]])), ('linear.bias', tensor([2.5859]))])
tensor(114.6554) OrderedDict([('linear.weight', tensor([[8.0289]])), ('linear.bias', tensor([2.7699]))])
tensor(69.6488) OrderedDict([('linear.weight', tensor([[8.5564]])), ('linear.bias', tensor([2.9087]))])
tensor(43.4192) OrderedDict([('linear.weight', tensor([[8.9594]])), ('linear.bias', tensor([3.0132]))])
tensor(28.1319) OrderedDict([('linear.weight', tensor([[9.2676]])), ('linear.bias', tensor([3.0914]))])
tensor(19.2212) OrderedDict([('linear.weight', tensor([[9.5032]])), ('linear.bias', tensor([3.1495]))])
データセットとデータローダー
データセットはTensorDatasetで生成されるサプライチェーン.
1バッチずつデータを出力するデータローダーは DataLoaderクラスに,データセットを入れることによって生成される.
from torch.utils.data import TensorDataset, DataLoader
= TensorDataset(x,y)
train_data 0] train_data[
(tensor([1.]), tensor([15.]))
= DataLoader(dataset = train_data, batch_size=2, shuffle=True)
train_loader for x_batch, y_batch in train_loader:
print(x_batch, y_batch)
tensor([[4.],
[2.]]) tensor([[42.],
[20.]])
tensor([[3.],
[5.]]) tensor([[34.],
[58.]])
tensor([[1.]]) tensor([[15.]])
= nn.MSELoss() #損出関数(最小2乗誤差)
loss_fn = optim.SGD(model.parameters(), lr) # 最適化(確率的勾配降下法)を準備; model.parameters()はパラメータを返すジェネレータ
optimizer
=10
n_epochsfor epoch in range(n_epochs):
for x_batch, y_batch in train_loader:
#モデルを訓練モードにする
model.train() = model(x_batch) #forwardメソッドで予測値を計算する
yhat = loss_fn(y_batch, yhat) #損出関数
loss #誤差逆伝播で勾配を計算
loss.backward()
#最適化の1反復
optimizer.step() #勾配を0にリセット
optimizer.zero_grad()
print(loss.data, model.state_dict())
tensor(5.4948) OrderedDict([('linear.weight', tensor([[9.5969]])), ('linear.bias', tensor([3.1964]))])
tensor(26.1062) OrderedDict([('linear.weight', tensor([[9.8901]])), ('linear.bias', tensor([3.2406]))])
tensor(0.6415) OrderedDict([('linear.weight', tensor([[9.8260]])), ('linear.bias', tensor([3.2246]))])
tensor(2.0398) OrderedDict([('linear.weight', tensor([[9.8243]])), ('linear.bias', tensor([3.2388]))])
tensor(4.9985) OrderedDict([('linear.weight', tensor([[9.8052]])), ('linear.bias', tensor([3.2228]))])
tensor(33.0736) OrderedDict([('linear.weight', tensor([[10.3803]])), ('linear.bias', tensor([3.3379]))])
tensor(12.2090) OrderedDict([('linear.weight', tensor([[10.4364]])), ('linear.bias', tensor([3.3245]))])
tensor(0.9685) OrderedDict([('linear.weight', tensor([[10.4298]])), ('linear.bias', tensor([3.3305]))])
tensor(9.3001) OrderedDict([('linear.weight', tensor([[10.1858]])), ('linear.bias', tensor([3.2695]))])
tensor(8.4185) OrderedDict([('linear.weight', tensor([[10.3913]])), ('linear.bias', tensor([3.3230]))])
tensor(8.5516) OrderedDict([('linear.weight', tensor([[10.2943]])), ('linear.bias', tensor([3.2770]))])
tensor(6.0230) OrderedDict([('linear.weight', tensor([[10.0980]])), ('linear.bias', tensor([3.2279]))])
tensor(5.9756) OrderedDict([('linear.weight', tensor([[10.0438]])), ('linear.bias', tensor([3.1984]))])
tensor(2.4884) OrderedDict([('linear.weight', tensor([[10.0065]])), ('linear.bias', tensor([3.2023]))])
tensor(22.7092) OrderedDict([('linear.weight', tensor([[10.4830]])), ('linear.bias', tensor([3.2976]))])
tensor(2.8948) OrderedDict([('linear.weight', tensor([[10.5750]])), ('linear.bias', tensor([3.3130]))])
tensor(16.4854) OrderedDict([('linear.weight', tensor([[10.3412]])), ('linear.bias', tensor([3.2322]))])
tensor(2.0351) OrderedDict([('linear.weight', tensor([[10.3697]])), ('linear.bias', tensor([3.2608]))])
tensor(8.0694) OrderedDict([('linear.weight', tensor([[10.2786]])), ('linear.bias', tensor([3.2171]))])
tensor(6.8768) OrderedDict([('linear.weight', tensor([[10.4632]])), ('linear.bias', tensor([3.2660]))])
tensor(9.7260) OrderedDict([('linear.weight', tensor([[10.2137]])), ('linear.bias', tensor([3.2036]))])
tensor(1.2645) OrderedDict([('linear.weight', tensor([[10.2342]])), ('linear.bias', tensor([3.2210]))])
tensor(8.8373) OrderedDict([('linear.weight', tensor([[10.3283]])), ('linear.bias', tensor([3.2355]))])
tensor(15.1480) OrderedDict([('linear.weight', tensor([[10.1726]])), ('linear.bias', tensor([3.1577]))])
tensor(14.0528) OrderedDict([('linear.weight', tensor([[10.3015]])), ('linear.bias', tensor([3.1624]))])
tensor(1.1820) OrderedDict([('linear.weight', tensor([[10.3148]])), ('linear.bias', tensor([3.1771]))])
tensor(5.9366) OrderedDict([('linear.weight', tensor([[10.1199]])), ('linear.bias', tensor([3.1284]))])
tensor(7.2067) OrderedDict([('linear.weight', tensor([[10.0701]])), ('linear.bias', tensor([3.1122]))])
tensor(10.5234) OrderedDict([('linear.weight', tensor([[10.3173]])), ('linear.bias', tensor([3.1644]))])
tensor(5.9218) OrderedDict([('linear.weight', tensor([[10.1226]])), ('linear.bias', tensor([3.1157]))])