自動微分 (Autograd)
学習目標: PyTorchの自動微分システムと勾配計算の仕組みを理解する
なぜ自動微分が必要か
ニューラルネットワークの学習には勾配(微分)の計算が不可欠です。損失関数を各パラメータで微分し、勾配降下法でパラメータを更新します。
パラメータ更新式:
θ = θ - α × ∂L/∂θ
基本的な使い方
import torch
# 勾配追跡を有効にしてテンソルを作成
x = torch.tensor([2.0, 3.0], requires_grad=True)
print(x.requires_grad) # True
# 計算を実行
y = x ** 2 + 3 * x + 1
z = y.sum() # スカラー化
# 逆伝播(勾配計算)
z.backward()
# 勾配を確認
print(x.grad) # tensor([7., 9.])
# dy/dx = 2x + 3 → x=2のとき7、x=3のとき9
計算グラフ
PyTorchは計算を自動的に記録し、計算グラフを構築します。backward()を呼ぶと、このグラフを逆向きにたどって勾配を計算します。
勾配追跡の制御
# 勾配追跡を一時的に無効化(推論時など)
x = torch.tensor([1.0], requires_grad=True)
with torch.no_grad():
y = x * 2
print(y.requires_grad) # False
# detach(): 勾配追跡を切り離した新しいテンソルを作成
z = x.detach()
print(z.requires_grad) # False
# 勾配のリセット(学習ループで必要)
x = torch.tensor([1.0], requires_grad=True)
y = x * 2
y.backward()
print(x.grad) # tensor([2.])
# 勾配は累積されるので、明示的にリセット
x.grad.zero_() # またはoptimizer.zero_grad()
重要: 勾配は
backward()のたびに累積されます。学習ループでは毎回zero_grad()を呼んでリセットしてください。
実践例:線形回帰
import torch
# データ
X = torch.tensor([[1.0], [2.0], [3.0], [4.0]])
y = torch.tensor([[2.0], [4.0], [6.0], [8.0]])
# パラメータ(勾配追跡あり)
w = torch.tensor([[1.0]], requires_grad=True)
b = torch.tensor([[0.0]], requires_grad=True)
learning_rate = 0.1
for epoch in range(100):
# 順伝播
y_pred = X @ w + b
# 損失計算
loss = ((y_pred - y) ** 2).mean()
# 逆伝播
loss.backward()
# パラメータ更新(勾配追跡なしで)
with torch.no_grad():
w -= learning_rate * w.grad
b -= learning_rate * b.grad
# 勾配リセット
w.grad.zero_()
b.grad.zero_()
print(f"w: {w.item():.4f}, b: {b.item():.4f}")
# 結果: w ≈ 2.0, b ≈ 0.0
理解度チェック
Q. 学習ループで毎回zero_grad()を呼ぶ理由は?