chainer, cupy入門
TRANSCRIPT
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 numpyx = numpy.array([1,2,3], numpy.float32)y = x * xs = numpy.sum(y)print(s)
import cupyx = cupy.array([1,2,3], cupy.float32)y = x * xs = 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