人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

Theanoの使い方 (1) シンボルと共有変数

今回からしばらくの間、Deep Learningの各種アルゴリズムをスクラッチから実装していきたい。Pylearn2などDeep Learningのアルゴリズムを実装したPythonライブラリもあるのでスクラッチから実装する意味はほとんどないのだけれど、今回はアルゴリズムの詳細を勉強するというのが趣旨。

参考にした資料はDeep Learning TutorialTheanoというPythonの数値計算ライブラリを用いたDeep Learningの各種アルゴリズムの実装方法がソースコード付きで解説されている。ぶっちゃけたところこの資料を読み込めばこんな記事はいらないのだけれど、実装する過程で試行錯誤しないと理解できないところが多々あったのでそういうところをまとめておきたい。

今回はTheanoの基本的なところから。いろいろTIPSがあるので断片的に記事を書くかも・・・

Theanoについて

TheanoはPythonの数値計算ライブラリ。Deep Learningの説明で頻繁に出てくるので、Deep Learningを実装したライブラリだと勘違いしていたのだけれど違ってた。主な機能は、

  • 実行時コンパイルによる高速化
  • GPUのサポートによる高速化
  • 自動微分のサポート

ニューラルネットは微分を多用するので、微分の結果を自分で解かなくて済むのは大きなメリットになる。また、ニューラルネットで頻出する関数(softmaxなど)が実装されていたり、勾配降下法が非常に簡単に書けたりするなどDeep Learningの実装で便利なように作られたのだなという印象を受けた。

TheanoをGPUで使えるようにインストールする方法は下の記事を参照のこと。

Theanoの基本は下の資料が参考になった。

シンボルによる数式の定義

Theanoでは実行時コンパイルをサポートするためシンボル(TensorVariable)という概念が出てくる。たとえば、x^2という簡単な式の計算でも以下のような手順を踏む必要がある。

Using gpu device 0: GeForce GTX 760 Ti OEM
<class 'theano.tensor.var.TensorVariable'>
<class 'theano.tensor.var.TensorVariable'>
<class 'theano.compile.function_module.Function'>
1.0
4.0
9.0
16.0
25.0

こんな面倒なことやってられっか!と思ったのだけれど、先に挙げたTheanoのメリットを享受するには耐えなければならないということがわかってきた。dscalarの部分をdvectorやdmatrixに変えるとベクトル演算や行列演算もできる。スカラー、ベクトル、行列などを抽象化した概念をテンソル(Tensor)といい、TensorVariableという名前の由来になっている。物理で頻繁に使うみたいだけどメリットがよくわからない。コンパイルするためにPythonではあまり意識しなくてよかったデータ型が重要になっている。

x = T.dvector('x')
(中略)
print f([1,2,3])

x = T.dmatrix('x')
(中略)
print f([[1,2,3], [4,5,6]])

シンボルを組み立てた数式はそのままでは使えず、theano.function()で関数化する必要があるというのがポイント。いったんtheano.function()で関数化すれば自動的にコンパイルされるためシンボルで定義した数式が非常に高速に計算できる。

シグモイド関数の例

もう少しシンボルによる数式の例を。ニューラルネットのユニットでよく使うシグモイド関数は、

{ \displaystyle
s(x) = \frac{1}{1+e^{-x}}
}

で定義される。これをTheanoで実装すると

入力は行列で与えているが、要素ごとに計算(Elementwise)されて結果が行列で返る。

[[ 0.5         0.73105858]
 [ 0.26894142  0.11920292]]

共有変数

プログラムの実行中に値を保持し続けないといけないようなデータは共有変数(SharedVariable)という仕組みが使える。たとえば、機械学習の文脈では、訓練データやモデルパラメータが該当する。共有変数を作るときは、具体的なデータを初期値として与える必要がある。numpyのndarrayなどそのまま使えるので便利。

<class 'theano.sandbox.cuda.var.CudaNdarraySharedVariable'>
[[ 1.  2.  3.]
 [ 4.  5.  6.]]

共有変数に格納したデータは自動的にGPUのメモリに格納されるためデータを高速に読み書きできるようになる。GPUのメモリに乗せるデータはfloat型(theano.config.floatX)にする必要があるborrowはデフォルトではFalseだけれど、Trueにしておくとdataに対する変更がXにも反映されるようになる。チュートリアルでは学習データはFalseでパラメータはTrueのように使い分けているようだけど基本的にTrueにしておいて問題なさそう?共有変数の値はシンボルと違ってget_value()で取得できるのがポイントかな。

線形回帰モデルの例

線形回帰モデルの式は、

{ \displaystyle
y = Wx + b
}

で表される。学習で求まったWbを共有変数とみなして上の式を実装してみる。

こんな感じで数式を組み立てるときにはシンボルと同じような感じで共有変数も使える。シンボルは関数化してから関数の引数として値を与える必要があるが、共有変数には最初から何らかの値が入っているのがポイントかな。共有変数の値を更新する方法はまた今度。非常に便利な仕組みがある。

通常のPython変数とシンボル(TensorVariable)と共有変数(SharedVariable)の違いをしっかり意識してごちゃまぜにしないのがtheanoのソースコードを読む上でのコツだと感じた。

次回はTheanoの自動微分について書きたい。