SlideShare a Scribd company logo
Chainerの使い⽅方と
⾃自然⾔言語処理理への応⽤用
NLP若若⼿手の会  2015/09/03  @和倉温泉
得居  誠也,  Preferred  Networks
⾃自⼰己紹介
l  得居  誠也  (Seiya  Tokui)    @beam2d  (Twitter,  GitHub)
l  PFI  (2012-‐‑‒2014)  -‐‑‒>  PFN  (2014-‐‑‒)
l  専⾨門:機械学習
–  ⾃自然⾔言語処理理(品詞タグ付け)、近傍探索索のためのハッシング(修⼠士)
–  Deep  Learning(2012年年秋ごろ〜~)、画像・映像認識識
l  4  ⽉月に  Chainer  を作り始めて今はこれがメイン
2
Chainer
サイト:  http://chainer.org
ドキュメント:  http://docs.chainer.org
GitHub:  https://github.com/pfnet/chainer
l  Deep  Learning  のフレームワーク
l  Python  ライブラリとして提供
l  最新版は  v1.3.0  (2015/9/3現在)
3
今⽇日のおはなし
l  Chainer  の使い⽅方を紹介します
–  Deep  Learning  のおさらいも簡単にしますが、学術的な詳細は既存の⽂文献
を参考にしてください
–  CUDA  サポートについても解説しますが、CUDA  の詳細は  NVIDIA  のド
キュメントを参照してください
l  ⾃自然⾔言語処理理への応⽤用⽅方法を紹介します
–  Recurrent  Net  などのよく知られた結果を  Chainer  で実装する⽅方法を紹介
します
4
ニューラルネットの基礎
ニューラルネット
l  値が伝播していく有向グラフ
l  エッジで重みをかけて、ノードに⼊入るところで⾜足し込み、ノードの中で⾮非線形
変換する
l  全体としては巨⼤大で複雑な関数を表す
6
ニューラルネット=合成関数
l  ニューラルネットは、ベクトルに対して線形・⾮非線形な関数をたくさん適⽤用す
る合成関数と捉えるとよい
–  ベクトルをまとめると上図のように
–  まとめる理理由は⼆二つ
u  わかりやすい/扱いやすいから
u  ベクトル演算によって⾼高速化しやすいから
l  各ノードはベクトルを保持する変数
–  紫のノードは  “中間変数”
7
⼀一般のニューラルネットは  DAG  =  計算グラフ
l  ⼀一般にはグラフが分岐したり合流流したりする
l  分岐:同じ変数を複数の場所でつかう
l  合流流:⼆二つ以上の変数を受け取る関数を適⽤用する
8
計算グラフの例例
l  このグラフは  z = x ** 2 + 2 * x * y + y  という式を表す
9
x
y
_ ** 2
2 * _ _ * _ _ + _ z
_ + _
誤差逆伝播(後退型⾃自動微分)
l  合成関数の微分を計算するには連鎖律律  (chain  rule)  をつかう
l  たとえば                                                                                                                          を微分すると
–  ヤコビ⾏行行列列の掛け算になっている
l  ニューラルネットの最適化では、⽬目的関数はスカラーで、微分したいパラメー
タや中間変数は⾼高次元のベクトル
l  上の式でいえば          がスカラーで、                            はベクトル
l  この場合(上の式で)左からかけていったほうが効率率率が良良い
–  つまり        (出⼒力力)から        (⼊入⼒力力)に向けてヤコビ⾏行行列列を順にかけていく
–  もとの関数と逆順なので誤差逆伝播法という
10
z
z = h(y), y = g(x), x = f(w)
z
w
=
z
y
y
x
x
w
= Dh(y)Dg(x)Df (w)
w, x, y
z w
誤差逆伝播は、計算グラフを逆向きにたどる
l  計算グラフと順伝播時の各変数の値があれば計算可能
11
Recurrent  Net
l  ループがあるニューラルネット
l  時刻の概念念があり、t=T  の状態は  t=T-‐‑‒1  の状態と  t=T  の⼊入⼒力力を使って求める
12
T
T-1
T
Recurrent  Net  は時間展開して考える
l  時間展開すれば、DAG  の計算グラフになる
l  DAG  の計算グラフは誤差逆伝播できる(Backprop  Through  Time)
13
t=1
t=2
t=3
t=4
Truncated  BPTT
l  ⻑⾧長い系列列を学習するとき、計算グラフが⻑⾧長⼤大になり計算コストがかかる
l  古い情報を忘れて(グラフを切切り落落として)計算コストを削減するのが  
Truncated  BPTT(勾配は不不正確になる)
14
t=1
t=2
t=3
t=4
Truncated
Chainer の使い⽅方
Chainer  はニューラルネットのフレームワーク
l  ニューラルネットをつかうためには最低限以下の機能が必要
–  ニューラルネットを記述する
–  ニューラルネットの順伝播・逆伝播を実⾏行行する
–  勾配法を実⾏行行してパラメータを最適化する
l  Chainer  はこれらを  Python  上で実装したもの
l  Chainer  の特徴
–  順伝播は単純に  Python  のスクリプトとして書ける
–  そのスクリプトの実⾏行行結果は計算⼿手順を記憶していて、逆伝播を⼿手で書く必
要はない
16
Chainer  のインストール
l  環境は  Linux(特に  Ubuntu)がおすすめです
l  インストール⽅方法
–  新しめの  Python  環境を⽤用意(CPython  2.7+  または  3.4+)
–  pip  も⽤用意
–  コマンドを実⾏行行:  pip install chainer
–  chainer  パッケージが  import  できれば完了了です
l  Python  スタックの環境構築は、Anaconda  がおすすめです
l  Python  のバージョン管理理は  pyenv  がおすすめです(慣れてるものがある⽅方は
そちらで  OK  です)
–  pyenv  からコマンド⼀一つで  Anaconda  もインストールできます
17
順伝播
l  今まで「変数」と呼んでいたものは、Chainer  では  Variable  オブジェクト
l  Variable  オブジェクトを特殊な「関数」に⼊入れると、順伝播を実⾏行行しながら計
算グラフが構築される
–  「関数」は  Function  オブジェクト
–  簡単な四則演算などは  Variable  オブジェクト⾃自体に定義されている
–  ほかの  Function  は  chainer.functions(F と略略記)にたくさん⽤用意さ
れている
x = Varaible(...)
y = Variable(...)
z = x ** 2 + 2 * x * y + y
18
x
y
_**2
2*_ _*_ _+_ z
_+_
Variable  オブジェクト
l  計算グラフの(データ)ノード
l  NumPy  または  CuPy(後述)の配列列を保持する
–  初期化時に配列列を渡す
–  data  属性に保存される
x = Variable(np.zeros((10, 20), dtype=np.float32))
x.data #=> セットした配列列が取り出せる
l  多くの  Function  は配列列の最初の軸をミニバッチとして使うので注意
–  例例えば上の  x  は、20  次元ベクトルが  10  個⼊入ったミニバッチとみなす
l  現状、Chainer  は多くの場所で  float32  配列列を要求するので注意(NumPy/
CuPy  はデフォルトの⼩小数型が  float64)
19
Function  オブジェクト
l  計算グラフの「演算」ノード
l  chainer.functions (=: F)  にいろいろ定義されている
l  パラメータつきの  Function
–  F.Linear, F.Convolution2D, F.EmbedID, ...
–  あらかじめ定義しておいて、順伝播の計算時につかう
l  パラメータなしの関数
–  F.relu, F.max_pooling_2d, F.lstm, ...
–  Variable  オブジェクトを返すただの  Python  関数が提供されているので、
順伝播のときに直接つかう
20
FunctionSet  でパラメータつき関数をまとめる
l  パラメータ付きの  Function  オブジェクトは複数のイテレーションで使いまわす
ので、あらかじめ作っておく
l  FunctionSet  を使うと、これらをまとめて管理理できる
model = FunctionSet(embed=F.EmbedID(10000, 100),
layer1=F.Linear(100, 100),
layer2=F.Linear(100, 10000))
def forward(x):
h = F.relu(model.layer1(model.embed(x)))
return model.layer2(h)
21
ロス関数、勾配計算
l  様々なロス関数が定義されている
–  F.softmax_cross_entropy, F.mean_squared_error
–  パラメータ付きのロス関数もある:  F.NegativeSampling,
F.BinaryHierarchicalSoftmax
l  ロス関数を計算したら、Variable.backward()  でそのロス値の勾配を求める
def forward(x, t):
h = F.relu(model.layer1(model.embed(x)))
return F.softmax_cross_entropy(model.layer2(h), t)
loss = forward(x)
loss.backward()
22
Optimizer  の設定
l  勾配が計算できたら、あとは勾配法をまわす
l  勾配法のアルゴリズムはみな  Optimizer  クラスの⼦子クラス
–  chainer.optimizers  に定義されている
–  実装されている最適化⼿手法:SGD, MomentumSGD, AdaGrad, RMSprop,
RMSpropGraves, AdaDelta, Adam
l  パラメータと勾配の配列列をあらかじめ  setup  メソッドに渡しておく必要がある
l  FunctionSet  をつかう場合、簡単にわたせる
optimizer = optimizers.SGD()
optimizer.setup(model)
23
Optimizer  による最適化
l  Optimizer  を使う⼿手順
–  まず勾配をゼロ初期化:zero_̲grads()
–  順伝播・逆伝播を実⾏行行
–  必要なら正則化メソッドを実⾏行行:weight_̲decay(),  clip_̲grads()
–  最適化ルーチンを実⾏行行:update()
optimizer.zero_grads()
loss = forward(x, t)
loss.backward()
optimizer.weight_decay(0.005)
optimizer.update()
24
Chainer  を使う場合の全体の流流れ
1.  FunctionSet  にパラメータ付き関数を定義する
2.  Optimizer  を定義して、FunctionSet  を設定する
3.  forward  関数を定義する
4.  データセットを読み込み、訓練⽤用と評価⽤用にわける
5.  訓練ループを回す
a.  勾配をゼロ初期化
b.  forward  関数を呼び、得られたロス値の  backward  メソッドを呼ぶ
c.  正則化処理理をして、update
6.  適当な頻度度で評価ループを回す
a.  forward  関数を呼んで結果を記録
25
例例:MNIST
# Model definition
model = FunctionSet(
l1=F.Linear(784, 100),
l2=F.Linear(100, 100),
l3=F.Linear(100, 10))
opt = optimizers.SGD()
opt.setup(model)
# Forward computation
def forward(x, t):
h1 = F.relu(model.l1(x))
h2 = F.relu(model.l2(h1))
y = model.l3(h2)
return F.softmax_cross_entropy(
y, t)
# Training loop
for epoch in xrange(n_epoch):
for i in xrange(0, N, batchsize):
x = Variable(...)
t = Variable(...)
opt.zero_grads()
loss = forward(x, t)
loss.backward()
opt.update()
新しい  Function  を⾃自分で定義する
l  Function  は  Python  で新しく作ることができる
l  forward(_̲cpu/_̲gpu)  と  backward(_̲cpu/_̲gpu)  を実装する必要がある
l  これらは配列列のタプルを受け取って、配列列のタプルを返す
class SquaredDiff(Function):
def forward_cpu(self, inputs):
x, y = inputs
z = x – y
return z * z,
def backward_cpu(self, inputs, grad_outputs):
x, y = inputs
gz = grad_outputs
gx = 2 * (x – y) * gz
return gx, -gx
27
新しい  Function  を⾃自分で定義する
l  Function  を書いたらテストしましょう
l  とくに勾配チェック  (gradient  check)  は必須
–  有限差分法で  forward  のみから計算した勾配が、backward  で計算した勾
配と⼀一致するかを確かめる
–  chainer.gradient_check.numerical_grad  を使うと簡単に書ける
l  公式リポジトリの  tests/chainer_̲tests/function_̲tests  にたくさん例例が書いて
あります
28
CUDA  サポート
l  Chainer  v1.3.0  が昨⽇日リリース
l  CuPy:  新しい  CUDA  配列列実装
–  NumPy  と同じようなインターフェイスで使える
u  関数・メソッドのサブセットを実装
u  配列列のスライス、転置、reshape  等も⾃自由にできます
–  カスタムカーネルも記述できる(elementwise,  reduction)
29
CuPy  を使う準備
l  まず  CUDA  が使える  GPU  を⽤用意する
l  CUDA  6.5  以上をインストール
–  Ubuntu  なら公式に  deb  パッケージが配布されているのでそれをつかうの
がお⼿手軽です
l  パスを通す
–  PATH  と  LD_̲LIBRARY_̲PATH  を通す必要があります
–  公式インストーラからはデフォルトで  /usr/local/cuda  にインストールさ
れるので、以下のように設定
u  PATH=/usr/local/cuda/bin:$PATH
u  LD_̲LIBRARY_̲PATH=/usr/local/cuda/lib64:$LD_̲LIBRARY_̲PATH
l  Chainer  をインストールしたら  cupy  モジュールを  import  してみて動くか確
認
30
CuPy  の使い⽅方
l  基本的に  numpy  の代わりに  cupy  を使う以外は  NumPy  と⼀一緒
l  CPU/GPU  の両⽅方で動く関数の書き⽅方
–  chainer.cuda.get_array_module()  を使うと、引数に  cupy.ndarray  
があるかないかで  numpy  /  cupy  のどちらかを返してくれます
–  例例えば下は  NumPy  と  CuPy  の両⽅方で動く  logsumexp  の実装例例(より省省
メモリな実装を考えてみてください)
def logsumexp(x, axis=None):
xp = cuda.get_array_module(x)
x_max = x.max(axis=axis)
return x_max + xp.log(xp.exp(x – x_max).sum(axis=axis))
31
⾃自然⾔言語処理理への応⽤用
32
単語埋め込み
l  単語を  ID  で表したものを対応する密ベクトルに変換する
–  この密ベクトルも学習の対象
l  F.EmbedID  がこれを実装している
–  パラメータ付きの  Function
–  つくるときに  ID  の範囲と密ベクトルの次元を指定する
–  int32  配列列が⼊入った  Variable  を受け取って、float32  配列列が⼊入った  
Variable  を返す
l  例例えば  1  万語彙を  200  次元に埋め込みたい場合は以下のようにする
embedder = F.Embed(10000, 200)
word_arr = np.ndarray((10,), dtype=np.int32)
word = Variable(word_arr)
x = embedder(word) #=> サイズ  10 x 200  の配列列が⼊入った変数
33
Recurrent  Net
l  時刻を  1  ステップ分だけ実⾏行行する関数を書いて、それを  for  ⽂文の中から呼び出
す
l  リカレントな部分は、状態を保持する変数を持っておいてループの中でつかう
l  内部的には展開された  DAG  が作られる
def forward_one_step(x, h, t):
h = F.tanh(model.input(x) + model.lateral(h))
y = model.output(h)
return F.softmax_cross_entropy(y, t), h
h = Variable(np.zeros((1, 200), dtype=np.float32))
accum_loss = 0
for x, t in input_seq:
loss, h = forward_one_step(x, h, t)
accum_loss += loss
34
例例:RNN  ⾔言語モデル
# Model definition
model = FunctionSet(
emb=F.EmbedID(1000, 50),
h2h=F.Linear( 50, 50),
h2y=F.Linear( 50, 1000))
opt = optimizers.SGD()
opt.setup(model)
# Forward computation of one step
def fwd1step(h, w, t):
x = model.emb(w)
h = F.tanh(x + model.h2h(h))
y = model.h2y(h)
return F.softmax_cross_entropy(
y, t), h
# Full RNN forward computation
def forward(seq):
h = Variable(...) # init state
accum_loss = 0
for curw, nextw in 
zip(seq, seq[1:]):
x = Variable(curw)
t = Variable(nextw)
loss, h = fwd1step(h, x, t)
accum_loss += loss
return accum_loss
Long  Short-‐‑‒Term  Memory
l  ⻑⾧長期の依存関係をとらえるのに  LSTM  はシグモイド関数よりもよい挙動を⽰示す
l  F.lstm  に実装されている
l  これは  2  引数  2  出⼒力力の関数
–  「セル」と「⼊入⼒力力」を受け取り、「新しいセル」と「次の状態」を出⼒力力
–  ⼊入⼒力力は、各ユニットごとに  4  種類の値からなる:⼊入⼒力力、⼊入⼒力力ゲート、忘却
ゲート、出⼒力力ゲート
–  この  4  つの値を  2  番⽬目の次元につめた⾏行行列列を受け取る
u  単純に、LSTM  ユニット数の  4  倍のサイズの⼊入⼒力力ベクトルをとると思え
ばOK
36
例例:LSTM  RNN  ⾔言語モデル
# Model definition
model = FunctionSet(
emb=F.EmbedID(1000, 50*4),
h2h=F.Linear( 50, 50*4),
h2y=F.Linear( 50, 1000))
opt = optimizers.SGD()
opt.setup(model)
# Forward computation of one step
def fwd1step(c, h, w, t):
x = model.emb(w)
c, h = F.lstm(
c, x + model.h2h(h))
y = model.h2y(h)
return F.softmax_cross_entropy(
y, t), c, h
# Full RNN forward computation
def forward(seq):
c = Variable(...) # init cell
h = Variable(...) # init state
accum_loss = 0
for curw, nextw in 
zip(seq, seq[1:]):
x = Variable(curw)
t = Variable(nextw)
loss, c, h = fwd1step(
c, h, x, t)
accum_loss += loss
return accum_loss
unchain_̲backward  による  Truncated  BPTT
l  Truncated  BPTT  は  unchain_backward()  を使うことで実装できる
–  Variable  から逆向きに計算グラフをたどったときに通る辺をすべて計算グ
ラフから取り除く
–  Python  変数に保持している  Variable  オブジェクトはそのまま残る
accum_loss = 0
for i, x in enumerate(batches):
loss, h = forward_on_step(*x) # forward
accum_loss += loss
if i % 30 == 0:
optimizer.zero_grads()
accum_loss.backward() # backward
accum_loss.unchain_backward() # truncate graph
optimizer.update()
accum_loss = 0
38
公式の  Examples
公式リポジトリの  examples  ディレクトリにいくつか例例があります
l  mnist:  MNIST  を多層パーセプトロンで学習するもっとも基本のサンプル
l  imagenet:  ImageNet  からの⼤大規模ConvNet学習
l  modelzoo:  Caffe  公式モデルを読み込んでつかう
l  ptb:  Penn-‐‑‒Tree  Bank  から  LSTM  ⾔言語モデルを学習する
–  無限⻑⾧長の⼊入⼒力力に対する  Truncated  BPTT  の例例にもなっています
l  word2vec:  word2vec  の実装と  PTB  からの学習
l  sentiment:  Recursive  Net  を使った極性判定
39
まとめ
l  ニューラルネットを(おもに実装⾯面から)簡単におさらいしました
l  Chainer  の使い⽅方をざっくりお伝えしました
l  このチュートリアルをもとに、Chainer  を使って⾃自然⾔言語処理理でなにか作って
公開、または論論⽂文発表していただけると⼤大変うれしいです
l  Chainer  ⾃自体へのフィードバックもお待ちしております!!!
40

More Related Content

Chainerの使い方と自然言語処理への応用

  • 2. ⾃自⼰己紹介 l  得居  誠也  (Seiya  Tokui)    @beam2d  (Twitter,  GitHub) l  PFI  (2012-‐‑‒2014)  -‐‑‒>  PFN  (2014-‐‑‒) l  専⾨門:機械学習 –  ⾃自然⾔言語処理理(品詞タグ付け)、近傍探索索のためのハッシング(修⼠士) –  Deep  Learning(2012年年秋ごろ〜~)、画像・映像認識識 l  4  ⽉月に  Chainer  を作り始めて今はこれがメイン 2
  • 3. Chainer サイト:  http://chainer.org ドキュメント:  http://docs.chainer.org GitHub:  https://github.com/pfnet/chainer l  Deep  Learning  のフレームワーク l  Python  ライブラリとして提供 l  最新版は  v1.3.0  (2015/9/3現在) 3
  • 4. 今⽇日のおはなし l  Chainer  の使い⽅方を紹介します –  Deep  Learning  のおさらいも簡単にしますが、学術的な詳細は既存の⽂文献 を参考にしてください –  CUDA  サポートについても解説しますが、CUDA  の詳細は  NVIDIA  のド キュメントを参照してください l  ⾃自然⾔言語処理理への応⽤用⽅方法を紹介します –  Recurrent  Net  などのよく知られた結果を  Chainer  で実装する⽅方法を紹介 します 4
  • 7. ニューラルネット=合成関数 l  ニューラルネットは、ベクトルに対して線形・⾮非線形な関数をたくさん適⽤用す る合成関数と捉えるとよい –  ベクトルをまとめると上図のように –  まとめる理理由は⼆二つ u  わかりやすい/扱いやすいから u  ベクトル演算によって⾼高速化しやすいから l  各ノードはベクトルを保持する変数 –  紫のノードは  “中間変数” 7
  • 8. ⼀一般のニューラルネットは  DAG  =  計算グラフ l  ⼀一般にはグラフが分岐したり合流流したりする l  分岐:同じ変数を複数の場所でつかう l  合流流:⼆二つ以上の変数を受け取る関数を適⽤用する 8
  • 9. 計算グラフの例例 l  このグラフは  z = x ** 2 + 2 * x * y + y  という式を表す 9 x y _ ** 2 2 * _ _ * _ _ + _ z _ + _
  • 10. 誤差逆伝播(後退型⾃自動微分) l  合成関数の微分を計算するには連鎖律律  (chain  rule)  をつかう l  たとえば                                                                                                                          を微分すると –  ヤコビ⾏行行列列の掛け算になっている l  ニューラルネットの最適化では、⽬目的関数はスカラーで、微分したいパラメー タや中間変数は⾼高次元のベクトル l  上の式でいえば          がスカラーで、                            はベクトル l  この場合(上の式で)左からかけていったほうが効率率率が良良い –  つまり        (出⼒力力)から        (⼊入⼒力力)に向けてヤコビ⾏行行列列を順にかけていく –  もとの関数と逆順なので誤差逆伝播法という 10 z z = h(y), y = g(x), x = f(w) z w = z y y x x w = Dh(y)Dg(x)Df (w) w, x, y z w
  • 12. Recurrent  Net l  ループがあるニューラルネット l  時刻の概念念があり、t=T  の状態は  t=T-‐‑‒1  の状態と  t=T  の⼊入⼒力力を使って求める 12 T T-1 T
  • 13. Recurrent  Net  は時間展開して考える l  時間展開すれば、DAG  の計算グラフになる l  DAG  の計算グラフは誤差逆伝播できる(Backprop  Through  Time) 13 t=1 t=2 t=3 t=4
  • 14. Truncated  BPTT l  ⻑⾧長い系列列を学習するとき、計算グラフが⻑⾧長⼤大になり計算コストがかかる l  古い情報を忘れて(グラフを切切り落落として)計算コストを削減するのが   Truncated  BPTT(勾配は不不正確になる) 14 t=1 t=2 t=3 t=4 Truncated
  • 16. Chainer  はニューラルネットのフレームワーク l  ニューラルネットをつかうためには最低限以下の機能が必要 –  ニューラルネットを記述する –  ニューラルネットの順伝播・逆伝播を実⾏行行する –  勾配法を実⾏行行してパラメータを最適化する l  Chainer  はこれらを  Python  上で実装したもの l  Chainer  の特徴 –  順伝播は単純に  Python  のスクリプトとして書ける –  そのスクリプトの実⾏行行結果は計算⼿手順を記憶していて、逆伝播を⼿手で書く必 要はない 16
  • 17. Chainer  のインストール l  環境は  Linux(特に  Ubuntu)がおすすめです l  インストール⽅方法 –  新しめの  Python  環境を⽤用意(CPython  2.7+  または  3.4+) –  pip  も⽤用意 –  コマンドを実⾏行行:  pip install chainer –  chainer  パッケージが  import  できれば完了了です l  Python  スタックの環境構築は、Anaconda  がおすすめです l  Python  のバージョン管理理は  pyenv  がおすすめです(慣れてるものがある⽅方は そちらで  OK  です) –  pyenv  からコマンド⼀一つで  Anaconda  もインストールできます 17
  • 18. 順伝播 l  今まで「変数」と呼んでいたものは、Chainer  では  Variable  オブジェクト l  Variable  オブジェクトを特殊な「関数」に⼊入れると、順伝播を実⾏行行しながら計 算グラフが構築される –  「関数」は  Function  オブジェクト –  簡単な四則演算などは  Variable  オブジェクト⾃自体に定義されている –  ほかの  Function  は  chainer.functions(F と略略記)にたくさん⽤用意さ れている x = Varaible(...) y = Variable(...) z = x ** 2 + 2 * x * y + y 18 x y _**2 2*_ _*_ _+_ z _+_
  • 19. Variable  オブジェクト l  計算グラフの(データ)ノード l  NumPy  または  CuPy(後述)の配列列を保持する –  初期化時に配列列を渡す –  data  属性に保存される x = Variable(np.zeros((10, 20), dtype=np.float32)) x.data #=> セットした配列列が取り出せる l  多くの  Function  は配列列の最初の軸をミニバッチとして使うので注意 –  例例えば上の  x  は、20  次元ベクトルが  10  個⼊入ったミニバッチとみなす l  現状、Chainer  は多くの場所で  float32  配列列を要求するので注意(NumPy/ CuPy  はデフォルトの⼩小数型が  float64) 19
  • 20. Function  オブジェクト l  計算グラフの「演算」ノード l  chainer.functions (=: F)  にいろいろ定義されている l  パラメータつきの  Function –  F.Linear, F.Convolution2D, F.EmbedID, ... –  あらかじめ定義しておいて、順伝播の計算時につかう l  パラメータなしの関数 –  F.relu, F.max_pooling_2d, F.lstm, ... –  Variable  オブジェクトを返すただの  Python  関数が提供されているので、 順伝播のときに直接つかう 20
  • 21. FunctionSet  でパラメータつき関数をまとめる l  パラメータ付きの  Function  オブジェクトは複数のイテレーションで使いまわす ので、あらかじめ作っておく l  FunctionSet  を使うと、これらをまとめて管理理できる model = FunctionSet(embed=F.EmbedID(10000, 100), layer1=F.Linear(100, 100), layer2=F.Linear(100, 10000)) def forward(x): h = F.relu(model.layer1(model.embed(x))) return model.layer2(h) 21
  • 22. ロス関数、勾配計算 l  様々なロス関数が定義されている –  F.softmax_cross_entropy, F.mean_squared_error –  パラメータ付きのロス関数もある:  F.NegativeSampling, F.BinaryHierarchicalSoftmax l  ロス関数を計算したら、Variable.backward()  でそのロス値の勾配を求める def forward(x, t): h = F.relu(model.layer1(model.embed(x))) return F.softmax_cross_entropy(model.layer2(h), t) loss = forward(x) loss.backward() 22
  • 23. Optimizer  の設定 l  勾配が計算できたら、あとは勾配法をまわす l  勾配法のアルゴリズムはみな  Optimizer  クラスの⼦子クラス –  chainer.optimizers  に定義されている –  実装されている最適化⼿手法:SGD, MomentumSGD, AdaGrad, RMSprop, RMSpropGraves, AdaDelta, Adam l  パラメータと勾配の配列列をあらかじめ  setup  メソッドに渡しておく必要がある l  FunctionSet  をつかう場合、簡単にわたせる optimizer = optimizers.SGD() optimizer.setup(model) 23
  • 24. Optimizer  による最適化 l  Optimizer  を使う⼿手順 –  まず勾配をゼロ初期化:zero_̲grads() –  順伝播・逆伝播を実⾏行行 –  必要なら正則化メソッドを実⾏行行:weight_̲decay(),  clip_̲grads() –  最適化ルーチンを実⾏行行:update() optimizer.zero_grads() loss = forward(x, t) loss.backward() optimizer.weight_decay(0.005) optimizer.update() 24
  • 25. Chainer  を使う場合の全体の流流れ 1.  FunctionSet  にパラメータ付き関数を定義する 2.  Optimizer  を定義して、FunctionSet  を設定する 3.  forward  関数を定義する 4.  データセットを読み込み、訓練⽤用と評価⽤用にわける 5.  訓練ループを回す a.  勾配をゼロ初期化 b.  forward  関数を呼び、得られたロス値の  backward  メソッドを呼ぶ c.  正則化処理理をして、update 6.  適当な頻度度で評価ループを回す a.  forward  関数を呼んで結果を記録 25
  • 26. 例例:MNIST # Model definition model = FunctionSet( l1=F.Linear(784, 100), l2=F.Linear(100, 100), l3=F.Linear(100, 10)) opt = optimizers.SGD() opt.setup(model) # Forward computation def forward(x, t): h1 = F.relu(model.l1(x)) h2 = F.relu(model.l2(h1)) y = model.l3(h2) return F.softmax_cross_entropy( y, t) # Training loop for epoch in xrange(n_epoch): for i in xrange(0, N, batchsize): x = Variable(...) t = Variable(...) opt.zero_grads() loss = forward(x, t) loss.backward() opt.update()
  • 27. 新しい  Function  を⾃自分で定義する l  Function  は  Python  で新しく作ることができる l  forward(_̲cpu/_̲gpu)  と  backward(_̲cpu/_̲gpu)  を実装する必要がある l  これらは配列列のタプルを受け取って、配列列のタプルを返す class SquaredDiff(Function): def forward_cpu(self, inputs): x, y = inputs z = x – y return z * z, def backward_cpu(self, inputs, grad_outputs): x, y = inputs gz = grad_outputs gx = 2 * (x – y) * gz return gx, -gx 27
  • 28. 新しい  Function  を⾃自分で定義する l  Function  を書いたらテストしましょう l  とくに勾配チェック  (gradient  check)  は必須 –  有限差分法で  forward  のみから計算した勾配が、backward  で計算した勾 配と⼀一致するかを確かめる –  chainer.gradient_check.numerical_grad  を使うと簡単に書ける l  公式リポジトリの  tests/chainer_̲tests/function_̲tests  にたくさん例例が書いて あります 28
  • 29. CUDA  サポート l  Chainer  v1.3.0  が昨⽇日リリース l  CuPy:  新しい  CUDA  配列列実装 –  NumPy  と同じようなインターフェイスで使える u  関数・メソッドのサブセットを実装 u  配列列のスライス、転置、reshape  等も⾃自由にできます –  カスタムカーネルも記述できる(elementwise,  reduction) 29
  • 30. CuPy  を使う準備 l  まず  CUDA  が使える  GPU  を⽤用意する l  CUDA  6.5  以上をインストール –  Ubuntu  なら公式に  deb  パッケージが配布されているのでそれをつかうの がお⼿手軽です l  パスを通す –  PATH  と  LD_̲LIBRARY_̲PATH  を通す必要があります –  公式インストーラからはデフォルトで  /usr/local/cuda  にインストールさ れるので、以下のように設定 u  PATH=/usr/local/cuda/bin:$PATH u  LD_̲LIBRARY_̲PATH=/usr/local/cuda/lib64:$LD_̲LIBRARY_̲PATH l  Chainer  をインストールしたら  cupy  モジュールを  import  してみて動くか確 認 30
  • 31. CuPy  の使い⽅方 l  基本的に  numpy  の代わりに  cupy  を使う以外は  NumPy  と⼀一緒 l  CPU/GPU  の両⽅方で動く関数の書き⽅方 –  chainer.cuda.get_array_module()  を使うと、引数に  cupy.ndarray   があるかないかで  numpy  /  cupy  のどちらかを返してくれます –  例例えば下は  NumPy  と  CuPy  の両⽅方で動く  logsumexp  の実装例例(より省省 メモリな実装を考えてみてください) def logsumexp(x, axis=None): xp = cuda.get_array_module(x) x_max = x.max(axis=axis) return x_max + xp.log(xp.exp(x – x_max).sum(axis=axis)) 31
  • 33. 単語埋め込み l  単語を  ID  で表したものを対応する密ベクトルに変換する –  この密ベクトルも学習の対象 l  F.EmbedID  がこれを実装している –  パラメータ付きの  Function –  つくるときに  ID  の範囲と密ベクトルの次元を指定する –  int32  配列列が⼊入った  Variable  を受け取って、float32  配列列が⼊入った   Variable  を返す l  例例えば  1  万語彙を  200  次元に埋め込みたい場合は以下のようにする embedder = F.Embed(10000, 200) word_arr = np.ndarray((10,), dtype=np.int32) word = Variable(word_arr) x = embedder(word) #=> サイズ  10 x 200  の配列列が⼊入った変数 33
  • 34. Recurrent  Net l  時刻を  1  ステップ分だけ実⾏行行する関数を書いて、それを  for  ⽂文の中から呼び出 す l  リカレントな部分は、状態を保持する変数を持っておいてループの中でつかう l  内部的には展開された  DAG  が作られる def forward_one_step(x, h, t): h = F.tanh(model.input(x) + model.lateral(h)) y = model.output(h) return F.softmax_cross_entropy(y, t), h h = Variable(np.zeros((1, 200), dtype=np.float32)) accum_loss = 0 for x, t in input_seq: loss, h = forward_one_step(x, h, t) accum_loss += loss 34
  • 35. 例例:RNN  ⾔言語モデル # Model definition model = FunctionSet( emb=F.EmbedID(1000, 50), h2h=F.Linear( 50, 50), h2y=F.Linear( 50, 1000)) opt = optimizers.SGD() opt.setup(model) # Forward computation of one step def fwd1step(h, w, t): x = model.emb(w) h = F.tanh(x + model.h2h(h)) y = model.h2y(h) return F.softmax_cross_entropy( y, t), h # Full RNN forward computation def forward(seq): h = Variable(...) # init state accum_loss = 0 for curw, nextw in zip(seq, seq[1:]): x = Variable(curw) t = Variable(nextw) loss, h = fwd1step(h, x, t) accum_loss += loss return accum_loss
  • 36. Long  Short-‐‑‒Term  Memory l  ⻑⾧長期の依存関係をとらえるのに  LSTM  はシグモイド関数よりもよい挙動を⽰示す l  F.lstm  に実装されている l  これは  2  引数  2  出⼒力力の関数 –  「セル」と「⼊入⼒力力」を受け取り、「新しいセル」と「次の状態」を出⼒力力 –  ⼊入⼒力力は、各ユニットごとに  4  種類の値からなる:⼊入⼒力力、⼊入⼒力力ゲート、忘却 ゲート、出⼒力力ゲート –  この  4  つの値を  2  番⽬目の次元につめた⾏行行列列を受け取る u  単純に、LSTM  ユニット数の  4  倍のサイズの⼊入⼒力力ベクトルをとると思え ばOK 36
  • 37. 例例:LSTM  RNN  ⾔言語モデル # Model definition model = FunctionSet( emb=F.EmbedID(1000, 50*4), h2h=F.Linear( 50, 50*4), h2y=F.Linear( 50, 1000)) opt = optimizers.SGD() opt.setup(model) # Forward computation of one step def fwd1step(c, h, w, t): x = model.emb(w) c, h = F.lstm( c, x + model.h2h(h)) y = model.h2y(h) return F.softmax_cross_entropy( y, t), c, h # Full RNN forward computation def forward(seq): c = Variable(...) # init cell h = Variable(...) # init state accum_loss = 0 for curw, nextw in zip(seq, seq[1:]): x = Variable(curw) t = Variable(nextw) loss, c, h = fwd1step( c, h, x, t) accum_loss += loss return accum_loss
  • 38. unchain_̲backward  による  Truncated  BPTT l  Truncated  BPTT  は  unchain_backward()  を使うことで実装できる –  Variable  から逆向きに計算グラフをたどったときに通る辺をすべて計算グ ラフから取り除く –  Python  変数に保持している  Variable  オブジェクトはそのまま残る accum_loss = 0 for i, x in enumerate(batches): loss, h = forward_on_step(*x) # forward accum_loss += loss if i % 30 == 0: optimizer.zero_grads() accum_loss.backward() # backward accum_loss.unchain_backward() # truncate graph optimizer.update() accum_loss = 0 38
  • 39. 公式の  Examples 公式リポジトリの  examples  ディレクトリにいくつか例例があります l  mnist:  MNIST  を多層パーセプトロンで学習するもっとも基本のサンプル l  imagenet:  ImageNet  からの⼤大規模ConvNet学習 l  modelzoo:  Caffe  公式モデルを読み込んでつかう l  ptb:  Penn-‐‑‒Tree  Bank  から  LSTM  ⾔言語モデルを学習する –  無限⻑⾧長の⼊入⼒力力に対する  Truncated  BPTT  の例例にもなっています l  word2vec:  word2vec  の実装と  PTB  からの学習 l  sentiment:  Recursive  Net  を使った極性判定 39
  • 40. まとめ l  ニューラルネットを(おもに実装⾯面から)簡単におさらいしました l  Chainer  の使い⽅方をざっくりお伝えしました l  このチュートリアルをもとに、Chainer  を使って⾃自然⾔言語処理理でなにか作って 公開、または論論⽂文発表していただけると⼤大変うれしいです l  Chainer  ⾃自体へのフィードバックもお待ちしております!!! 40