Chainerの使い⽅方と
⾃自然⾔言語処理理への応⽤用
2015/11/25  WebDB  forum@芝浦⼯工業⼤大学
(株)Preferred  Infrastructure
海野  裕也
v1.5向け
⾃自⼰己紹介
海野  裕也
l  -2008 東⼤大情報理理⼯工修⼠士
l  ⾃自然⾔言語処理理
l  2008-2011 ⽇日本アイ・ビー・エム(株)東京基礎研
l  テキストマイニング、⾃自然⾔言語処理理の研究開発
l  2011- (株)プリファードインフラストラクチャー
l  ⾃自然⾔言語処理理、情報検索索、機械学習、テキストマイニングなど
の研究開発
l  研究開発系案件、コンサルティング
l  JubatusやChainerの開発
NLP若若⼿手の会共同委員⻑⾧長(2014-)
「オンライン機械学習」(2015, 講談社)
2
Chainer
http://chainer.org/
3	
v1.5が本⽇日リリース
今⽇日のおはなし
l  Chainer の使い⽅方を紹介します
l  Deep Learning のおさらいも簡単にしますが、学術
的な詳細は既存の⽂文献を参考にしてください
l  CUDA サポートについても解説しますが、CUDA  の
詳細は NVIDIA のドキュメントを参照してください
l  ⾃自然⾔言語処理理への応⽤用⽅方法を紹介します
l  Recurrent Net などのよく知られた結果を  Chainer
で実装する⽅方法を紹介します
4
ニューラルネットの基礎
ニューラルネット
l  値が伝播していく有向グラフ
l  エッジで重みをかけて、ノードに⼊入るところで⾜足し
込み、ノードの中で⾮非線形変換する
l  全体としては巨⼤大で複雑な関数を表す
6
ニューラルネット=合成関数
l  ベクトルに対して線形・⾮非線形な関数をたくさん適
⽤用する合成関数と捉えるとよい
l  各ノードはベクトルを保持する変数
7
⼀一般のニューラルネットは  DAG = 計算グラフ
⼀一般にはグラフが分岐したり合流流したりする
l  分岐:同じ変数を複数の場所でつかう
l  合流流:⼆二つ以上の変数を受け取る関数を適⽤用する
8
計算グラフの例例
z = x ** 2 + 2 * x * y + y
9	
x
y
_ **
2
2 * _ _ * _ _ + _ z
_ + _
機械学習のおさらい
多くの機械学習⼿手法は、
1.  ⽬目的関数の設計
2.  勾配の計算
3.  最⼩小化のための反復復計算
からなる
10	
先ほどの計算は
ここに使う
機械学習の例例:分類学習の⽬目的関数
11	
argminw ∑(x, y) l(x, y; w)
l  xは⼊入⼒力力ベクトル、yは予測ラベル
l  l(x, y)は予測が正しければ⼩小さく、間違えれば⼤大
きくなる値(損失関数)
l  上記関数を最⼩小化するパラメータwを求めたい
機械学習の例例:分類学習のアルゴリズム
l  ⽬目的関数をパラメータwで微分した値(勾配)
を計算する⽅方法を⽤用意する
l  wを勾配の⽅方向に少しだけ動かす、を繰り返す
l  実際は更更新⽅方向の取り⽅方に⼯工夫が他数ある
12	
initialize w
until converge:
w := w - η d/dw L(x, y; w)
最急降降下法
誤差逆伝播(後退型⾃自動微分)
合成関数の微分を計算するには連鎖律律 (chain
rule) をつかう
13	
z = h(y), y = g(x), x = f(w)
z
w
=
z
y
y
x
x
w
= Dh(y)Dg(x)Df (w)
各関数の微分がわかれば機械的に計算できる
誤差逆伝播は、計算グラフを逆向きにたどる
計算グラフと順伝播時の各変数の値があれば計算可能
14
ニューラルネットの学習⽅方法
1.  ⽬目的関数の設計
l  計算グラフを⾃自分で設計する
2.  勾配の計算
l  誤差逆伝播で機械的に計算できる
3.  最⼩小化のための反復復計算
l  勾配を使って反復復更更新する
15	
1さえ設計すれば残りは
ほぼ⾃自動化されている
Recurrent Net
l  ループがあるニューラルネット
l  時刻の概念念があり、t=T の状態は t=T-1 の状態と t=T の⼊入
⼒力力を使って求める
16	
T
T-1
T
Recurrent Net は時間展開して考える
l  時間展開すれば、DAG の計算グラフになる
l  DAG の計算グラフは誤差逆伝播できる(Backprop
Through Time) 17	
t=1
t=2
t=3
t=4
Truncated BPTT
l  ⻑⾧長い系列列を学習するとき、計算グラフが⻑⾧長⼤大になり計算コ
ストがかかる
l  古い情報を忘れて(グラフを切切り落落として)計算コストを
削減するのが Truncated BPTT(勾配は不不正確になる)
18	
t=1
t=2
t=3
t=4
Truncated
Chainer の使い⽅方
Chainer はニューラルネットのフレームワーク
l  機能
l  ニューラルネットを記述する
l  ニューラルネットの順伝播・逆伝播を実⾏行行する
l  勾配法を実⾏行行してパラメータを最適化する
l  Chainer の特徴
l  順伝播は単純に Python のスクリプトとして書ける
l  そのスクリプトの実⾏行行結果は計算⼿手順を記憶してい
て、逆伝播を⼿手で書く必要はない
20
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 もインストールできます
21
順伝播
l  今まで「変数」と呼んでいたものは、Chainer
では  Variable オブジェクト
l  Variable を  Function に⼊入れると、順伝搬後の
Variable が返ってくる
l  Variable が計算グラフを保持している
l  Function は、四則演算以外に
chainer.functions に⽤用意されている
22
順伝搬とコード例例
23	
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 配列列を要求する
ので注意
24	
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として分離離された(後述)
25	
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オブジェクトになった
26	
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)
27	
v1.5~
ロス関数、勾配計算
l  ロス関数もFunctionの⼀一種
l  ロス関数の出⼒力力に、Variable.backward()  を呼ぶと
勾配が計算できる
loss = F.softmax_cross_entropy(y, t)
loss.backward()
28
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())
29
Optimizer による最適化
l  まず勾配をゼロ初期化:zerograds()
l  順伝播・逆伝播を実⾏行行
l  最適化ルーチンを実⾏行行:update()
l  以上を何回も繰り返す
model.zerograds()
loss = ...
loss.backward()
optimizer.update()
30
Chainer を使う場合の全体の流流れ
1.  Linkを使ってChainを定義する
2.  Optimizer  に、Chain を設定する
3.  forward 関数を定義する
4.  データセットを読み込み、訓練⽤用と評価⽤用にわける
5.  訓練ループを回す
a.  勾配をゼロ初期化
b.  順伝搬して、得られたロス値の backward メソッドを呼ぶ
c.  Optimizerを、update
6.  適当な頻度度で評価ループを回す
a.  テストデータで順伝搬関数を呼んで結果を記録
31
新しい Function を⾃自分で定義する
l  Function は Python で新しく作ることができる
l  forward(_cpu/_gpu) と backward(_cpu/_gpu) を実装
する必要がある
l  配列列のタプルを受け取って、配列列のタプルを返す
l  Linkは内部でFunctionを呼んで作る
32
⾃自作Functionの例例
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
33	
tupleを返す
勾配をtupleで返す
新しい Function を⾃自分で定義する
l  Function を書いたらテストしましょう
l  とくに勾配チェック (gradient check) は必須
l  有限差分法で forward のみから計算した勾配が、backward で
計算した勾配と⼀一致するかを確かめる
l  chainer.gradient_check.numerical_grad を使うと簡単
に書ける
l  公式リポジトリの tests/chainer_tests/
function_tests にたくさん例例が書いてあります
34
CUDA サポート
l  CuPy: 新しい CUDA 配列列実装
l  NumPy と同じようなインターフェイスで使える
l  関数・メソッドのサブセットを実装
l  配列列のスライス、転置、reshape 等も⾃自由にできます
l  カスタムカーネルも記述できる(elementwise,
reduction)
35
CuPy を使う準備
l  まず CUDA が使える GPU を⽤用意する
l  CUDA 6.5 以上をインストール
l  Ubuntu なら公式に deb パッケージがお薦め
l  パスを通す
l  PATH と LD_LIBRARY_PATH を通す必要があります
l  公式インストーラからはデフォルトで /usr/local/cuda にインストー
ルされるので、以下のように設定
l  PATH=/usr/local/cuda/bin:$PATH
l  LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
l  Chainer をインストールしたら  cupy モジュールを
import してみて動くか確認
36
CuPy の使い⽅方
l  基本的に  numpy の代わりに  cupy を使う以外は  
NumPy と⼀一緒
l  CPU/GPU の両⽅方で動く関数の書き⽅方
l  chainer.cuda.get_array_module() を使うと、引数に  
cupy.ndarray があるかないかで  numpy / cupy のどちらかを返してく
れます
l  例例えば下は  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))
37
Chainerをつかった⾃自然⾔言語処理理の例例
⾃自然⾔言語処理理のサンプルは他数同梱されている
l  word2vec
l  Skip-gram/Continuous BoW
l  Negative Sampling/Hierarchical Softmax
l  Recurrent Neural Network
l  Long-short term memory
l  Gated recurrent unit
l  Recursive Neural Network
l  Neural Tensor Network
39
Recurrent Neural Network
l  次の単語を次々に予測するモデル
l  隠れ変数を更更新しながら予測に使う
40	
x1 x2 x3 x4
h1 h2 h3 h4
y1 y2 y3 y4
h0
yi は xi+1 の予測
LSTM-RNN⾔言語モデル
class RNNLM(chainer.Chain):
def __init__(self, n_vocab, n_units, train=True):
super(RNNLM, self).__init__(
embed=L.EmbedID(n_vocab, n_units),
l1=L.LSTM(n_units, n_units),
l2=L.LSTM(n_units, n_units),
l3=L.Linear(n_units, n_vocab))
self.train = train
41	
埋め込みベクトル
2段のLSTM
出⼒力力
LSTM-RNN⾔言語モデル
def reset_state(self):
self.l1.reset_state()
self.l2.reset_state()
def __call__(self, x):
h0 = self.embed(x)
h1 = self.l1(F.dropout(h0, train=self.train))
h2 = self.l2(F.dropout(h1, train=self.train))
y = self.l3(F.dropout(h2, train=self.train))
return y
42	
LSTMは前の時刻の隠れ状態を
保持している
LSTM-RNN⾔言語モデルを学習する
for i in range(jump * n_epoch):
x = chainer.Variable(...)
t = chainer.Variable(...)
loss_i = model(x, t)
accum_loss += loss_i
...
model.zerograds()
accum_loss.backward()
accum_loss.unchain_backward() # truncate
accum_loss = 0
optimizer.update()
43	
truncated BPTT
word2vec
l  周囲の単語から中⼼心の単語を予測するモデル(CBoW)
l  中⼼心の単語から周囲の単語を予測するモデル(Skip-
gram)
44
word2vec
class ContinuousBoW(chainer.Chain):
def __init__(self, n_vocab, n_units, loss_func):
super(ContinuousBoW, self).__init__(
embed=F.EmbedID(n_vocab, args.unit),
loss_func=loss_func)
def __call__(self, x, context):
h = None
for c in context:
e = self.embed(c)
h = h + e if h is not None else e
return self.loss_func(h, x)
45	
コンテキストの平均を取って出
⼒力力に投げるだけ
埋め込みベクトル
Recursive Neural Network
l  事前に与えられた構造(普通は構⽂文⽊木)に沿って、再帰
的にベクトルを結合する
l  Recurrent Netは直鎖に対するRecursive Netともとれる
46	
x1 x2
p1
x3
p2
p1 = f(x1, x2)
p2 = f(p1, x3)
Recursive Neural Network
class RecursiveNet(chainer.Chain):
def __init__(self, n_vocab, n_units):
super(RecursiveNet, self).__init__(
embed=L.EmbedID(n_vocab, n_units),
l=L.Linear(n_units * 2, n_units),
w=L.Linear(n_units, n_label))
def leaf(self, x):
return self.embed(x)
def node(self, left, right):
return F.tanh(self.l(F.concat((left, right))))
47	
leafで埋め込み
nodeで合成
Recursive Netで構⽂文⽊木をたどる
def traverse(model, node):
if isinstance(node['node'], int): # leaf node
word = xp.array([node['node']], np.int32)
loss = 0
x = chainer.Variable(word)
v = model.leaf(x)
else: # internal node
left_node, right_node = node['node']
left_loss, left = traverse(model, left_node)
right_loss, right = traverse(model, right_node)
v = model.node(left, right)
loss = left_loss + right_loss
48
公式の Examples
公式リポジトリの examples ディレクトリにいくつか例例が
あります
l  mnist: MNIST を多層パーセプトロンで学習するもっと
も基本のサンプル
l  imagenet: ImageNet からの⼤大規模ConvNet学習
l  modelzoo: Caffe 公式モデルを読み込んでつかう
l  ptb: Penn-Tree Bank から LSTM ⾔言語モデルを学習する
l  無限⻑⾧長の⼊入⼒力力に対する Truncated BPTT の例例にもなっています
l  word2vec: word2vec の実装と PTB からの学習
l  sentiment: Recursive Net を使った極性判定
49
まとめ
l  ニューラルネットを(おもに実装⾯面から)簡単におさら
いしました
l  Chainer の使い⽅方をざっくりお伝えしました
l  このチュートリアルをもとに、Chainer を使って⾃自然⾔言
語処理理でなにか作って公開、または論論⽂文発表していただ
けると⼤大変うれしいです
l  Chainer ⾃自体へのフィードバックもお待ちしております
50

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