注意!
PyTorch 0.4 以降、Variableは非推奨となり、Tensorに統合されました。
Welcome to the migration guide for PyTorch 0.4.0. In this release we introduced many exciting new features and critical bug fixes, with the goal of providing users a better and cleaner interface. In this guide, we will cover the most important changes in migrating existing code from previous versions:
・ Tensors and Variables have merged
よって、本記事の内容は一部サポートされていませんので、0.4向けに改変された元のTutorialを見てください!
Pytorch Tutorial
References:
公式ドキュメント:PyTorch Tutorials
このノートのソース:[Github]
(https://github.com/kenmikanmi/PyTorch_Tutorials_in_hand/blob/master/Pytorch_Tutorials_.ipynb)
本記事について
基本的に,PyTorch初心者向けの公式ドキュメントに記載してあるコードを動かしたものです。公式ドキュメントには,以下の5つのチュートリアルがあり,一応60分で全体をさらうことができるみたいです。
このノートは,公式ドキュメントに公開されているチュートリアルのうち,最初の2つ:
- What is PyTorch?
- Autograd: automatic differentiation
の日本語訳と,適宜説明を追加しています。
余談ですが,PyTorchがなんたるかを知るにはこちらの記事が良いと思います:
https://qiita.com/miyamotok0105/items/1fd1d5c3532b174720cd
いきさつ(読まなくても良い)
研究でこれまで色々論文を読んできたけれど,学習済みモデルをベースに,自分のタスクに対応した学習を行う必要がでてきました。論文著者の公開している学習済みモデルがTorch7の形式(.t7)で保存されていたのですが,Torch7ライブラリを使うにはプログラミング言語luaを新しく学ばなくてはならない…
そんななか,Torch7のモデルを簡単に利用できる別のPythonベースのライブラリPyTorchがあることを知り,軽く使ってみた所PyTorchいけそう・・・と思ったので,一から勉強することにしました。
PyTorchの公式ドキュメントのチュートリアルがわかりやすいという情報を得たので,jupyter notebookでちまちま動かしてみたものを,後で参照しやすいようにまとめました。このノートのソースの方が見やすいと思います。ツッコミ歓迎ですm(._.)m
PyTorchとは
- 深層学習研究のプラットフォーム
- numpyよりも優れた,GPU上での計算の仕組みを提供する
なるほど。
はじめに
Tensors
PyTorchの基本はtorch.Tensorに含まれるTensor型です。
Tensorはnumpy.ndarrayに似ているが,TensorはGPU上での計算において優れた型です。
これからTensorの使い方を軽く見てみましょう:
from __future__ import print_function
import torch
初期化されていない(5, 3)行列をTensor型で宣言すると:
x = torch.Tensor(5, 3)
x
-1.9742e-22 4.5800e-41 4.0313e+05
3.0777e-41 1.6255e-43 1.5554e-43
1.5975e-43 1.3873e-43 1.4574e-43
6.4460e-44 1.5975e-43 1.3593e-43
1.5414e-43 1.4013e-43 1.5414e-43
[torch.FloatTensor of size 5x3]
乱数で初期化した(5, 3)行列をTensor型で宣言すると
x = torch.rand(5, 3)
x
0.3983 0.2964 0.2377
0.9519 0.3093 0.3955
0.4799 0.2055 0.6707
0.8251 0.8141 0.7262
0.9655 0.8692 0.6213
[torch.FloatTensor of size 5x3]
サイズの確認は:
x.size()
torch.Size([5, 3])
Operations:演算
Tensorは様々な方法で四則演算できる。
例えば,加算1つとっても4つくらいの方法がある。
加算方法1:
y = torch.rand(5, 3)
x + y
0.6591 0.3766 1.1684
1.8462 0.7029 0.9779
1.4392 0.5668 0.9335
1.1356 1.1658 0.9240
1.8273 1.0639 1.0738
[torch.FloatTensor of size 5x3]
加算方法2:
print(x + y)
0.6591 0.3766 1.1684
1.8462 0.7029 0.9779
1.4392 0.5668 0.9335
1.1356 1.1658 0.9240
1.8273 1.0639 1.0738
[torch.FloatTensor of size 5x3]
加算方法3:
result = torch.Tensor(5, 3) # make empty (5, 3) Tensor
torch.add(x, y, out = result) # input x + y to Tensor
result
0.6591 0.3766 1.1684
1.8462 0.7029 0.9779
1.4392 0.5668 0.9335
1.1356 1.1658 0.9240
1.8273 1.0639 1.0738
[torch.FloatTensor of size 5x3]
加算方法4:
y.add_(x) # "add_(x)" -> y = y + x
y
0.6591 0.3766 1.1684
1.8462 0.7029 0.9779
1.4392 0.5668 0.9335
1.1356 1.1658 0.9240
1.8273 1.0639 1.0738
[torch.FloatTensor of size 5x3]
Note
基本的に,演算の関数の後に _ がついているものは代入の操作を表す。
上記の例でy.add_(x)は,y = y+xを行うことと同じ。
numpyと同じように,次のようにインデックスを指定できる。
x[:, 1]
0.2964
0.3093
0.2055
0.8141
0.8692
[torch.FloatTensor of size 5]
あとで読みたい
Tensorの演算は他にも,インデックス,スライス,線形代数の演算などが100以上ある。詳細はこちら。
Torch.Tensorとnumpyを行ったり来たり
Torch.Tensorとnumpyの要素は,同じメモリの場所に格納されており,相互の変換は簡単にできる。
numpyへの変換
a = torch.ones(5)
a
1
1
1
1
1
[torch.FloatTensor of size 5]
b = a.numpy()
b
array([ 1., 1., 1., 1., 1.], dtype=float32)
ここで,面白いことに…
a.add_(1)
print(a)
print(b)
2
2
2
2
2
[torch.FloatTensor of size 5]
[ 2. 2. 2. 2. 2.]
torch.Tensorに変換
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
[ 2. 2. 2. 2. 2.]
2
2
2
2
2
[torch.DoubleTensor of size 5]
CPU上のTensorは,全てnumpyに変換できるらしい(GPU上のTensorは変換できないということだろうか?)。
autogradとVariable
Autogradは”automatic differentiation”のことで,そのパッケージであるautogradはPyTorchの中核を担っている。これから,autogradに少し触れてみて,それをもとにニューラルネットワークを学習させてみよう。
autogradパッケージは,Tensorの全ての演算に対して自動微分を行うためのもの。これは,PyTorchの特徴である"define-by-run"を実現している。つまり,順伝播のコードを書くだけで逆伝播が定義できる。
Variable
-
autograd.Variableが,autogradの中心的なパッケージである。VariableはTensorのラッパーであり,Tensorのほぼ全ての演算が含まれている。 - ネットワークを定義してしまえば,
.backward()を呼び出すだけで勾配計算を自動的に行うことができる。
Tensorの生データには.dataでアクセスできる。そして,Variableに関する勾配は.gradに蓄積されている。Variableの概念図を以下に示す。
Function
autogradに関して,もうひとつ重要なクラスがあります。それはFunctionと呼ばれるパッケージです。
VariableとFunctionは内部でつながっていて,この2つによってニューラルネットワークのグラフが構築されます。そしてこのグラフに,ニューラルネットの計算の全ての履歴が残ります。
生成されたvariableのそれぞれに.grad_fnという属性があり,この属性によってどのFunctionによってvariableが生成されたのかを参照できる。ただし,ユーザによって作られたvariableの場合grad_fnはNoneとなる。
variableの導関数を計算したいのであれば,variableがもっている.backward()を呼び出すと良い。もしvariableが単一の要素だけのとき,backward()には特に引数を指定する必要はないが,複数の要素をもつときは引数grad_outputを指定してやる必要がある(←どういうこと?)。
と言っても分かりづらいので,具体例を交えながら見ていきましょう。
import torch
from torch.autograd import Variable
variableを作ってみます:
x = Variable(torch.ones(2, 2), requires_grad = True)
x
Variable containing:
1 1
1 1
[torch.FloatTensor of size 2x2]
次に,variableの加算をしてみましょう:
y = x + 2
y
Variable containing:
3 3
3 3
[torch.FloatTensor of size 2x2]
さっき言ったように,何らかの演算の後に生成されたvariableには属性grad_fnが付与されます。yのgrad_fnを参照してみると:
y.grad_fn
<AddBackward0 at 0x7fac69a8d320>
yにもっと色んな演算をしていきます:
z = y * y * 3
out = z.mean()
print(z, out)
Variable containing:
27 27
27 27
[torch.FloatTensor of size 2x2]
Variable containing:
27
[torch.FloatTensor of size 1]
勾配計算
いよいよ逆伝播の計算です。
次に行うout.backward()は,out.backward(torch.tensor([1.0]))と等価です。
out.backward()
勾配を出力してみましょう。勾配とはすなわち $\frac{d({\rm out})}{dx}$ のことです。
x.grad
Variable containing:
4.5000 4.5000
4.5000 4.5000
[torch.FloatTensor of size 2x2]
4.5の要素をもつ行列(テンソル)が得られました。これはどういうことなのでしょうか。
out Variable を"$o$"とします。$o$は,out = z.mean()で定義したので,次式で得られます。
$o = \frac{1}{4}\sum_i z_i$
ここで,$z_i = 3(x_i + 2)^2$ですから,
$\frac{\partial o}{\partial x_i}=\frac{3}{2}(x_i + 2)$
$x_i$の各要素は$1$なので,
$\frac{\partial o}{\partial x_i}|_{x_i = 1} = 4.5$
確かに,Variableには過去の計算の履歴が残っていましたね!
autogradを使えば,もっとクレイジーなことができる!
import torch
x = torch.randn(3)
x = Variable(x, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
Variable containing:
-1453.1710
229.0913
-37.1547
[torch.FloatTensor of size 3]
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
Variable containing:
51.2000
512.0000
0.0512
[torch.FloatTensor of size 3]
この上記の操作は何を意味しているのでしょうか…?(疑問)
Python歴1年半で,プログラミング言語やライブラリのリテラシーがあまりない僕にもなんとなくわかったような気がします。

