Chainer, Cupy⼊⾨
2016/07/02 Chainer Meetup #03
(株)Preferred Networks
海野 裕也
v1.10向け
今⽇のおはなし
l  Deep Learningのおさらい
l  Chainer の使い⽅の紹介
l  CuPyの使い⽅の紹介
2
ニューラルネットの基礎
ニューラルネット
l  値が伝播していく有向グラフ
l  エッジで重みをかけて、ノードに⼊るところで⾜し
込み、ノードの中で⾮線形変換する
l  全体としては巨⼤で複雑な関数を表す
4
ニューラルネット=合成関数
l  ベクトルに対して線形・⾮線形な関数をたくさん適
⽤する合成関数と捉えるとよい
l  各ノードはベクトルを保持する変数
5
⼀般のニューラルネットは DAG = 計算グラフ
⼀般にはグラフが分岐したり合流したりする
l  分岐:同じ変数を複数の場所でつかう
l  合流:⼆つ以上の変数を受け取る関数を適⽤する
6
計算グラフの例
z = x ** 2 + 2 * x * y + y
7	
x
y
_ **
2
2 * _ _ * _ _ + _ z
_ + _
誤差逆伝播は、計算グラフを逆向きにたどる
計算グラフと順伝播時の各変数の値があれば計算可能
8
機械学習(教師あり学習)のおさらい
⽬的
l  ⼊⼒Xに対して出⼒Yを予測する関数fを求めたい
l  例:Xがメール、Yはスパムか否か
⽅法
l  正解のわかっているデータx1, y1 … xn, ynに対し
て、f(xi)とyiがなるべく⼀致するfを求める
l  |f(x1) – y1| + … + |f(xn) – yn| を最⼩にしたい
9	
⽬的関数
機械学習のおさらい
多くの機械学習⼿法は、
1.  ⽬的関数の設計
2.  勾配の計算
3.  最⼩化のための反復計算
からなる
10	
先ほどの計算は
ここに使う
ニューラルネットの学習⽅法
1.  ⽬的関数の設計
l  計算グラフを⾃分で設計する
2.  勾配の計算
l  誤差逆伝播で機械的に計算できる
3.  最⼩化のための反復計算
l  勾配を使って反復更新する
11	
1さえ設計すれば残りは
ほぼ⾃動化されている
Chainer の使い⽅
Chainer はニューラルネットのフレームワーク
l  機能
l  ニューラルネットを記述する
l  ニューラルネットの順伝播・逆伝播を実⾏する
l  勾配法を実⾏してパラメータを最適化する
l  Chainer の特徴
l  順伝播は単純に Python のスクリプトとして書ける
l  そのスクリプトの実⾏結果は計算⼿順を記憶してい
て、逆伝播を⼿で書く必要はない
13
Chainer のインストール
l  環境は Linux(特に Ubuntu)がおすすめ
l  インストール⽅法
l  新しめの Python 環境を⽤意(CPython 2.7+, 3.4+, 3.5+)
l  pip も⽤意
l  コマンドを実⾏: pip install chainer
l  chainer パッケージが import できれば完了です
l  Python スタックの環境構築は、Anaconda がお
すすめ
l  Python のバージョン管理は pyenv がおすすめ
l  pyenv からコマンド⼀つで Anaconda もインストールできます
14
順伝播
l  今まで「変数」と呼んでいたものは、Chainer
では Variable オブジェクト
l  Variable を Function に⼊れると、順伝搬後の
Variable が返ってくる
l  Variable が計算グラフを保持している
l  Function は、四則演算以外に
chainer.functions に⽤意されている
15
順伝搬とコード例
16	
x
y
_**2
2*_ _*_ _+_ z
_+_
x = Varaible(...)
y = Variable(...)
z = x ** 2 + 2 * x * y + y
Variable オブジェクト
l  計算グラフの(データ)ノード
l  NumPy または CuPy(後述)の配列を保持する
l  初期化時に配列を渡す
l  data 属性に保存される
l  多くの Function は配列の最初の軸をミニバッチとして
使うので注意
l  下の x は、20 次元ベクトルが 10 個⼊ったミニバッチとみなす
l  現状、Chainer は多くの場所で float32 配列を要求する
ので注意
17	
x = Variable(np.zeros((10, 20),
dtype=np.float32))
x.data
Function オブジェクト
l  計算グラフの「演算」ノード
l  chainer.functions (以降 F) にいろいろ定義され
ている
l  F.relu, F.max_pooling_2d, F.lstm, ...
l  Functionの呼び出し結果が、再びVariableになる
l  v1.5からパラメータはLinkとして分離された
18	
x = Variable(...)
y = F.relu(x) # yもVariable
Link オブジェクト
l  パラメータ付きの関数
l  最適化の対象となる
l  save/loadができる(v1.5からsave/loadをサポート)
l  chainer.links(以降L)に⾊々⽤意されている
l  L.Linear, L.Convolution2D, L.EmbedID, ...
l  Linkの呼び出し結果が、再びVariableになる
l  v1.5からFunctionとパラメータは分離され、パラメータ
付きの関数はLinkオブジェクトになった
19	
v1.5~
ChainでLinkをまとめる
l  ⼀般的にパラメータ付きの関数(Link)は複数あるので、
Chainでまとめて管理できる
l  Chainを継承すると再利⽤しやすくなる
model = Chain(embed=L.EmbedID(10000, 100),
layer1=L.Linear(100, 100),
layer2=L.Linear(100, 10000))
x = Variable(...)
h = F.relu(model.layer1(model.embed(x)))
y = model.layer2(h)
20	
v1.5~
ロス関数、勾配計算
l  ロス関数もFunctionの⼀種
l  ロス関数の出⼒に、Variable.backward() を呼ぶと
勾配が計算できる
loss = F.softmax_cross_entropy(y, t)
loss.backward()
21
Optimizer の設定
l  勾配が計算できたら、あとは勾配法をまわす
l  勾配法のアルゴリズムは Optimizer クラスの⼦クラス
l  chainer.optimizers に定義されている
l  実装されている最適化⼿法:SGD, MomentumSGD, AdaGrad,
RMSprop, RMSpropGraves, AdaDelta, Adam
l  最適化対象をsetup メソッドに渡す
l  正則化はhook関数として登録する
optimizer = optimizers.SGD()
optimizer.setup(model)
optimizer.add_hook(optimizer.WeightDecay())
22
Optimizer による最適化
l  まず勾配をゼロ初期化:zerograds()
l  順伝播・逆伝播を実⾏
l  最適化ルーチンを実⾏:update()
l  以上を何回も繰り返す
model.zerograds()
loss = ...
loss.backward()
optimizer.update()
23
Chainer を使う場合の全体の流れ
1.  Linkを使ってChainを定義する
2.  Optimizer に、Chain を設定する
3.  forward 関数を定義する
4.  データセットを読み込み、訓練⽤と評価⽤にわける
5.  訓練ループを回す
a.  勾配をゼロ初期化
b.  順伝搬して、得られたロス値の backward メソッドを呼ぶ
c.  Optimizerを、update
6.  適当な頻度で評価ループを回す
a.  テストデータで順伝搬関数を呼んで結果を記録
24	
次のバージョンで訓練ループは抽象化されます
CUDAによる⾏列ライブラリCuPy
25
CuPyとは何か?
NumPy互換インターフェースの
CUDA実装の⾏列ライブラリ
26	
Pythonの⾏列ライブラリ
NVIDIA GPUの開発環境とライブラリ
既存のライブラリと
同じインターフェースで
GPUの⾼速性を⼿に⼊れられる
27
CuPyとNumPyの⽐較
import numpy
x = numpy.array([1,2,3], numpy.float32)
y = x * x
s = numpy.sum(y)
print(s)
import cupy
x = cupy.array([1,2,3], cupy.float32)
y = x * x
s = cupy.sum(y)
print(s)
28
CuPyはどのくらい早いの?
l  状況しだいですが、最⼤数⼗倍程度速くなります
def test(xp):
a = xp.arange(1000000).reshape(1000, -1)
return a.T * 2
test(numpy)
t1 = datetime.datetime.now()
for i in range(1000):
test(numpy)
t2 = datetime.datetime.now()
print(t2 -t1)
test(cupy)
t1 = datetime.datetime.now()
for i in range(1000):
test(cupy)
t2 = datetime.datetime.now()
print(t2 -t1)
29	
時間
[ms]
倍率
NumPy 2929 1.0
CuPy 585 5.0
CuPy +
Memory Pool
123 23.8
Intel Core i7-4790 @3.60GHz,
32GB, GeForce GTX 970
なぜCuPyが求められるのか?
l  GPUを使った応⽤研究では、必
要な知識が以前より増えた
l  GPU⾃体が複雑
l  GPUを効率的に扱うアルゴリズム
も複雑
l  使わないと効率で勝てない
l  GPUを効率的に⼿軽に使える仕
組みが必要になっている
30	
GPU
CUDA
⾏列ライブラリ
深層学習エンジン
応⽤研究
裏の仕組み
l  CUDA⽤ソースを⾃動⽣成してコンパイラが⾛る
l  ⽣成されたバイナリをGPUに⾃動的に転送・実⾏する
l  ビルド結果はキャッシュされるので2回⽬移⾏⾼速
31	
スタブ
スタブ
実処理
nvcc
コンパイラ
.cubin
GPU
実行	
キャッシュ
する
⾃分でコードを書きたい時
例:z[i] = x[i] + 2 * y[i] を書きたい
32	
引数の型: “float32 x, float32 y”
戻り値の型: “float32 z”
処理: “z = x + 2 * y;”
ループやインデックスの処理は
⾃動で埋めてくれる
これだけ書け
ば良い
チューニングの⽅法
l  CUDAのツールがそのまま使える
l  NVIDIA Visual Profiler (nvvp)やnvprofコマンド
l  CPU⽤のプロファイラではGPUのボトルネックがわ
からないので注意
l  詳細はCUDAのサイトへ
33
深層学習以外にも利⽤できる
l  既存のNumPyコードがほぼそのまま動く
l  既存の解析⼿法がそのままCUDA上で動く
l  NumPyのベクトルデータとの変換は1⾏
34
まとめ
l  ニューラルネットを(おもに実装⾯から)簡単におさら
いしました
l  Chainerは直感的なインターフェスで深層学習できるラ
イブラリ
l  CuPyはNumPyインターフェースでCUDAを使えるライ
ブラリ
35

Chainer, Cupy入門