blogこの広告は、90日以上更新していないブログに表示しています。Lispも高速なネイティブコンパイラを持ち、C/C++のライブラリも呼べるので、機械学習にも問題なく使える。Lispで実装された深層学習を可能とするライブラリにMGLがあり、GithubでMITライセンスで公開されている。#39でMGLを紹介してきた。これがそのときのスライド。Challengeの優勝者で、今はDeepMindにいるらしい。このコンペのコードが公開されているが、MGLが使われていることが分かる。MGLには深層学習以外にも進化計算や自然言語処理などのパッケージもあるらしい。ドキュメントもちゃんと書かれており、利用するのに必要な基本的材料は揃っているように見える。MGLは行列演算に以前紹介したMGL-MATを使う。よってCPUであればOpenBLASやMKLなどを、GPUであればCUDAを利用して高速に学習できる。以前、自分でも多層ニューラルネットを実装してみたが、ごくごくナイーブな実装なので実用とは言えなかった。それに対して、MGLはPython/C++のライブラリと比べても遜色ない性能を示す(後述のベンチマーク参照)。MGLは少なくともLinuxとMacで動くはずだが、自分はLinuxでしか試してないので、Macでも動いたという人は教えてほしい。*1GPUは別に必須というわけではなく、CPUのみで使う場合はCUDAのインストールも不要である。現状MGLでできること多層ニューラルネットワーク(ReLU、Maxoutなど各種活性化関数、Dropout完備)リカレントニューラルネットワーク、LSTM制約ボルツマンマシン(RBM)ベースの教師なし事前学習(DBN、DBM)cl-cudaによるGPU計算、とてもはやいLLA経由でOpenBLAS等を使ってCPU計算、かなりはやいできないことマルチGPUはできないっぽい畳み込みニューラルネットワーク*2MGLのインストールMGL-MATはLLAとcl-cudaに依存する。LLAとMGL-MATのインストールについては以前の記事を参照。MGL-MATを使うMGL-MATのインストールさえできていればMGLのインストールは簡単で、GithubからMGLのソースをダウンロードしてきてquickloadするだけである。その他の依存ライブラリはQuicklispから入る。:mgl)MGLに付属のMNISTのサンプルが長いので整理したものを作った。以下ではこのサンプルコードに従って話を進めていく。読むと大体の流れが分かるかと思う。深層学習では数値の精度はそれほど必要ではなく、単精度で十分らしい。そこでMGL-MATで扱う数値のデフォルトの型を指定する変数*default-mat-ctype*の値を:doubleから:floatにしておく。GPU計算ではこの設定の有無で速度に大きな違いが出る。CUDAを使うかどうかを変数*cuda-enabled*によって制御できる。t)MNISTデータの読み込みMNISTデータの配布ページから4つのファイルをダウンロードしてきて、適当なディレクトリにgunzipなどで展開する。次にそのディレクトリへのパスを設定する。"/path/to/mnist-data/")この状態で以下を実行すると、変数*training-data*と*test-data*にデータが格納される。'done)これらのデータ読み込み関数はload-mnist.lispで定義されている。当然ながら他のフォーマットのデータを扱うときはこの部分は自分で書く必要がある。読み込んだデータはデータ点を表す構造体datumのベクタになっている。0.0)>)datumのスロットは通し番号idと、正解ラベルlabel、そして実際のデータarrayの3つ。Lispの配列ではなくて、MGL-MATのMAT構造体になっていることに注意する。モデル定義フィードフォワードニューラルネットワークはfnnクラスのオブジェクトとして作られる。fnnのmake-instanceをラップするマクロとしてbuild-fnnがあるので、それを使って隠れ層2層のネットワークを作ってみるとこうなる。出力は10通りの値、活性化関数はソフトマックス関数ここで定義されたネットワークの中身を見てみると、以下のようになっている。100ネットワークの実体はlumpという、レイヤーのような役割を持つクラスのオブジェクトの集合で、lumpには->が接頭辞として付いている。lumpは同名のlumpを生成する関数を持っている。サイズ784の入力層を表す->inputクラスのインスタンスを作る関数を呼ぶ。build-fnnのキーワードオプション:classは生成するネットワークのオブジェクトのクラスで、:max-n-stripesがミニバッチのバッチサイズを表す。build-fnnの本体部分はlet*みたいなもので(実際let*に展開される)、lumpにつける名前とそのlumpを生成する関数呼び出しの対が並んでいる構造になっており、let*と同様に以前に束縛された変数を後の定義中で使うことができる。lumpには->inputや->reluなどのユニットの値を持つものと、層間の線型写像を担当する->activationがある。この例では->input、->relu、->softmax-xe-lossがユニットの値を持っている。具体的には、順伝搬の値が入るnodesというスロットと、逆伝搬の値が入るderivativesというスロットを持っており、どちらもバッチサイズ×ユニット数の行列になっている。入力層のlumpを表示してみると以下のようになっており、nodesとderivativesに784次元の入力データをバッチサイズ分束ねて行列としたものが入っていることが分かる。NILさて、次は隠れ層についてだが、隠れ層のlumpはその層における活性化関数の種類に応じて->relu、->sigmoid、->tanh、->maxoutなどの中から選ぶ。最近は深層学習にはReLUやMaxoutを使うことが多い。->softmax-xe-lossにはtargetというスロットがあり、正解ラベルをバッチサイズ分並べたリストが入る。回帰については別の記事で紹介できればと思う。>activationは前の層の出力から次の層の入力であるユニット活性を計算するためのlumpで、これ自体がlumpの集合を持つサブネットワークになっている。ここに学習対象である層間の重みやバイアスが入っている。MaxoutとDropout活性化関数にMaxoutを使うとネットワークを多層にしたときでも学習がうまくいくことが知られている。Maxoutは複数の線形ユニットからなる関数で、複数の直線からその入力における最大値を選ぶことによって下に凸な関数になる。そのため何個の直線を使うかをパラメータ:group-sizeで指定してやる必要がある。5本の直線を使うなら以下のように書ける。5)Dropoutは学習のたびにユニットの一部を一定確率で無効化する。これは沢山のサブネットワークでアンサンブル学習しているようなもので、強い正則化の効果がある。上の活性化関数でユニットの半分をランダムに無効化するときは、このように書ける。0.5)これらをまとめると、ネットワーク全体はこのように書ける。'prediction)))))ネットワークへの入出力の設定こうしてネットワークのオブジェクトを生成したわけだが、学習や予測を行なうためには、まずこのネットワークに入出力を設定してやる必要がある。入力は->inputのnodesスロットに、出力は->softmax-xe-lossのtargetスロットにそれぞれ設定する。これを行うメソッドがset-inputであり、例えば以下のように定義される。よってネットワークに正しくデータの値が設定されるのであれば、データの表現は別に何でもいいということが分かる。次に、学習を行なう関数を定義する。ここでどんなアルゴリズムを使うか、学習の途中経過をモニタリングするか等を決める。モーメンタムSGDを使って最適化するときは、以下のように書ける。fnn))最適化の本体はminimizeで、最適化アルゴリズムを表すoptimizerと学習主体を表すlearnerを引数に取る。ここではoptimizerのところはモーメンタムSGDを指定しているが、他にもADAMなども使える。最適化アルゴリズムは学習率やモーメンタムなど独自のパラメータを持つ。optimizerやlearnerには学習の途中経過を出力するモニター関数を噛ませることができて、例えば訓練データとテストデータに対する正答率を表示する関数log-bpn-test-errorを最適化の途中で一定間隔で実行する場合にはこのようにする。fnn))これらを使って、学習の進行過程全体はこのように書ける。fnn)ここでwith-cuda*はMGL-MATの記事でも出てきたCUDAによる計算を有効化するためのマクロで、init-bpn-weightsは正規分布に従って重みの初期値を設定する関数。n-ephochは訓練データを何周するかを表す。先に定義したmy-fnnを実際に学習させるとmy-fnn中の重みパラメータが破壊的に更新される。(10000)...となって最終的にテストデータに対して98.10%の正答率であることが分かる。Dropoutを入れた以下のネットワークで100エポックほど回すと98.96%までいった。'prediction))))サンプルファイルのコメントによると、DBMで事前学習してからDropoutありでファインチューニングすることによって99.22%までいくらしい。予測データセットの検証にはmonitor-bpn-resultsという関数が用意されており、例えば以下のように使う。(10000)>)個々のデータを予測するには、ネットワークの入力層のnodesに値を設定し、forward関数をネットワークを引数として呼んだ後、出力層のnodesを見れば予測結果が入っている。1))))例えば、訓練データの最初のデータを学習済みのmy-fnnによって予測してみると、以下のような合計が1になるような確率リストが表示される。2.232391e-16)>このうち最も確率が高いクラス5が予測結果となり、データの正解ラベルも5なので正解していることが分かる。ベンチマークMNISTを多層ニューラルネットで学習する例をTensorflow、Chainerと比較してみる。LLAでOpenBLASを使っているので、NumpyでもOpenBLASを使うようにする。するとOpenBLASを使うようになる。Tensorflowの多層ニューラルネットの例はこれを元にした。ChainerにはMNISTのサンプルが付属するのでそれを元にした。どちらもユニット数は256、活性化関数をReLU、最適化アルゴリズムをモーメンタムSGDとし、バッチサイズ100で15エポック学習したときの、学習部分のみに要した時間で比較する。結果はこのようになる。実行速度を出すために副作用を使いまくっているので関数型的とはいえない個々のケースにおいて実装しなければならないこと。SGDなら学習率とモーメンタムを決めるモニタリング関数、ロギング関数などTODOMNISTのサンプルではさらにL2正則化をやっているのでそこを詳しく見ていく。1.3.1となっている。前回はMNISTの分類問題を解いたが、今回は回帰問題の場合。月別アーカイブはてなブログをはじめよう!masatoiさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。