人工知能に関する断創録

人工知能、認知科学、心理学、ロボティクス、生物学などに興味を持っています。このブログでは人工知能のさまざまな分野について調査したことをまとめています。最近は、機械学習、Deep Learning、Keras、PyTorchに関する記事が多いです。



PyTorch (2) 自動微分

PyTorchの自動微分を試してみた。

180126-autograd.ipynb - Google ドライブ

import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable

まずは必要なライブラリをインポート。

# テンソルを作成
x = Variable(torch.Tensor([1]), requires_grad=True)
w = Variable(torch.Tensor([2]), requires_grad=True)
b = Variable(torch.Tensor([3]), requires_grad=True)

# 計算グラフを構築
# y = 2 * x + 3
y = w * x + b

# 勾配を計算
y.backward()

# 勾配を表示
print(x.grad)  # dy/dx = w = 2
print(w.grad)  # dy/dw = x = 1
print(b.grad)  # dy/db = 1
Variable containing:
 2
[torch.FloatTensor of size 1]

Variable containing:
 1
[torch.FloatTensor of size 1]

Variable containing:
 1
[torch.FloatTensor of size 1]
  • テンソルをVariableで囲むと勾配が格納されるgradプロパティを持つようになる
  • requires_grad=Falseだと微分の対象にならず勾配はNoneが返る

Backward computation is never performed in the subgraphs, where all Variables didn’t require gradients. http://pytorch.org/docs/0.3.0/notes/autograd.html

  • requires_grad=Fase はFine-tuningで層のパラメータを固定したいときに便利

This is especially useful when you want to freeze part of your model, or you know in advance that you’re not going to use gradients w.r.t. some parameters. For example if you want to finetune a pretrained CNN, it’s enough to switch the requires_grad flags in the frozen base http://pytorch.org/docs/0.3.0/notes/autograd.html

  • 計算グラフを構築してbackward()を実行するとグラフを構築する各変数のgradに勾配が入る

Once you finish your computation you can call .backward() and have all the gradients computed automatically. You can access the raw tensor through the .data attribute, while the gradient w.r.t. this variable is accumulated into .grad. http://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html

Theanoの使い方 (2) 自動微分(2015/5/18)をTheanoではなくPyTorchでやってみる

例1

 y = x^2

 \displaystyle \frac{dy}{dx} = 2x

x = Variable(torch.Tensor([2]), requires_grad=True)
y = x ** 2
y.backward()
print(x.grad)
Variable containing:
 4
[torch.FloatTensor of size 1]
  • yは変数(Variable)xの式で成り立っていて、yのbackward()を呼び出すとそれぞれの変数のgradプロパティに勾配が入る。

例2

 y = e^x

 \displaystyle \frac{dy}{dx} = e^x

x = Variable(torch.Tensor([2]), requires_grad=True)
y = torch.exp(x)
y.backward()
print(x.grad)
Variable containing:
 7.3891
[torch.FloatTensor of size 1]
  • 計算グラフを構築するときは numpy の関数 numpy.exp() を使ってはダメ
  • テンソル計算を行う専用の関数を使う torch.exp()
  • これらの関数は微分可能なので計算グラフ上で誤差逆伝搬が可能

例3

 y = \sin(x)

 \displaystyle \frac{dy}{dx} = \cos(x)

x = Variable(torch.Tensor([np.pi]), requires_grad=True)
y = torch.sin(x)
y.backward()
print(x.grad)
Variable containing:
-1
[torch.FloatTensor of size 1]

例4

 y = (x - 4)(x^2 + 6)

 \displaystyle \frac{dy}{dx} = 3 x^2 - 8 x + 6

x = Variable(torch.Tensor([0]), requires_grad=True)
y = (x - 4) * (x ** 2 + 6)
y.backward()
print(x.grad)
Variable containing:
 6
[torch.FloatTensor of size 1]

例5

 y = (\sqrt{x} + 1)^3

 \displaystyle \frac{dy}{dx} = \frac{3 (\sqrt{x} + 1)^2}{2 \sqrt{x}}

x = Variable(torch.Tensor([2]), requires_grad=True)
y = (torch.sqrt(x) + 1) ** 3
y.backward()
print(x.grad)
Variable containing:
 6.1820
[torch.FloatTensor of size 1]

例6

最後は偏微分の例。

 z = (x + 2 y)^2

 \displaystyle \frac{\partial z}{\partial x} = 2 (x + 2y)

 \displaystyle \frac{\partial z}{\partial y} = 4 (x + 2y)

x = Variable(torch.Tensor([1]), requires_grad=True)
y = Variable(torch.Tensor([2]), requires_grad=True)
z = (x + 2 * y) ** 2
z.backward()
print(x.grad)  # dz/dx
print(y.grad)  # dz/dy
Variable containing:
 10
[torch.FloatTensor of size 1]

Variable containing:
 20
[torch.FloatTensor of size 1]

lossを微分する

ニューラルネットの場合は、lossをパラメータ(重みやバイアス)で偏微分した値を使って勾配降下法でパラメータを更新するのが一般的。

# バッチサンプル数=5、入力特徴量の次元数=3
x = Variable(torch.randn(5, 3))
# バッチサンプル数=5、出力特徴量の次元数=2
y = Variable(torch.randn(5, 2))

# Linear層を作成
# 3ユニット => 2ユニット
linear = nn.Linear(3, 2)

# Linear層のパラメータ
print('w:', linear.weight)
print('b:', linear.bias)

# lossとoptimizer
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(linear.parameters(), lr=0.01)

# forward
pred = linear(x)

# loss = L
loss = criterion(pred, y)
print('loss:', loss)

# backpropagation
loss.backward()

# 勾配を表示
print('dL/dw:', linear.weight.grad)
print('dL/db:', linear.bias.grad)

# 勾配を用いてパラメータを更新
print('*** by hand')
print(linear.weight.sub(0.01 * linear.weight.grad))
print(linear.bias.sub(0.01 * linear.bias.grad))

# 勾配降下法
optimizer.step()

# 1ステップ更新後のパラメータを表示
# 上の式と結果が一致することがわかる
print('*** by optimizer.step()')
print(linear.weight)
print(linear.bias)

参考