lispmeetup #39 mglの紹介: common lispによるディープラーニング
TRANSCRIPT
MGLの紹介
Satoshi Imai / 今井 悟士
Twitter: @masatoi0 Github: masatoi
Common Lispによるディープラーニング
MGLとは
● Common Lisp用の機械学習ライブラリ
● ディープラーニングの割と最近の手法までカバー
● MGL-MATという行列演算ライブラリを使う
– cl-cuda、LLA(Lisp Linear Algebra)によって高速化
● cl-cuda非対応の旧版はQuicklispにある(最新版はGithubから)
● サンプルが長かったので整理した
– https://github.com/masatoi/mgl-user
MGLに実装されているモデル
● 教師あり学習
– FFNN (Feed Forward Neural Networks)
● 教師なし事前学習
– DBN (Deep Brief Networks)
– DBM (Deep Boltzmann Machine)
● 時系列向けモデル
– RNN (Reccurent Neural Networks)
– LSTM (Long Short Term Memory)
ニューラルネットワーク
入力層 隠れ層 出力層
● フィードフォワードニューラルネットワーク(FFNN)
ニューラルネットワークによる予測
入力層 隠れ層 出力層
入力
重み
活性化関数
入力層 隠れ層 出力層
入力
重み
活性化関数
出力層の活性化関数は問題に応じて変える
入力層 隠れ層 出力層
● 回帰問題なら恒等写像● 分類問題ならソフトマックス関数
0.2
0.5
0.3
例えば3クラス分類問題なら...
ニューラルネットワークの学習
● 予測値と実測値の違いを表す損失関数を最小化する → 勾配法
● 勾配は出力層から入力層に向かって伝搬するように計算(逆伝搬)
● 勾配法の色々なバリエーション
– 確率的勾配降下法(SGD)
– Momentum SGD
– ADAM
● 詳細は本を読もう!
ディープラーニング
● 隠れ層を多層にしたニューラルネットワーク
– 勾配消失問題:多層にすると逆伝搬のときに勾配が消失/発散する
● → オートエンコーダ/RBMによる教師なし事前学習 (後述)
● → 活性化関数にReLUやMaxoutを使う (後述)
● 特徴量を作りこまなくても生のデータを与えればいい。手軽。性能良し
● 一方で計算時間はかかる
– → データを小分けしてまとめて並列に処理する → ミニバッチ
● ネットワークを複雑にすると過学習する
– → Dropout (後述)を使って汎化性能を上げられる
ディープラーニングで使われる活性化関数: ReLU、Maxout
● 正規化線形関数(Rectified Linear Unit; ReLU)
– 勾配が消失しにくい
– 計算が単純で速い
– 多分一番メジャー
● Maxout
– 複数の線形ユニットから成る
– 活性化関数自体が学習
– ReLUよりも良い場合も多い
– 計算時間は増える
例題:MNIST
● 手書き数字認識のデータセット。よく例題に使われる
● 28×28ピクセルの画像(784次元)に正解ラベルがついている
● 訓練データ60000個、テストデータ10000個
● http://yann.lecun.com/exdb/mnist
データセットの読み込み
● MNISTのデータを読み込む関数training-data、test-data
● データセットはデータ点の構造体の配列
● データ点の中身
MGLUSER> (aref *trainingdata* 0)#S(DATUM :ID 1 :LABEL 5 :ARRAY #<MAT 784 AB #(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.011764705882352941d0 0.07058823529411765d0... 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0)>)
MGL-MATの構造体
ネットワークの構造を定義する
● build-fnnマクロでネットワークの構造を指定してmake-instanceする
– 入力層784次元、1200次元の隠れ層が3層、出力層10次元
– 隠れ層の活性化関数はReLU、出力層の活性化関数はソフトマックス関数
– バッチサイズ100
(defparameter myfnn (buildfnn (:class 'fnn :maxnstripes 100) ;; Input Layer 784 dim (inputs (>input :size 784)) ;; Hidden Layer 1 1200 units, ReLU (f1activations (>activation inputs :name 'f1 :size 1200)) (f1 (>relu f1activations)) ;; Hidden Layer 2 1200 units, ReLU (f2activations (>activation f1 :name 'f2 :size 1200)) (f2 (>relu f2activations)) ;; Hidden Layer 3 1200 units, ReLU (f3activations (>activation f2 :name 'f3 :size 1200)) (f3 (>relu f3activations)) ;; Output Layer: Softmax layer 10 dim (prediction (>softmaxxeloss (>activation f3 :name 'prediction :size 10) :name 'prediction))))
ネットワークの内容
● ネットワークはlumpというオブジェクトの集合から構成されている
– ->input、 ->relu、 ->softmax-xe-lossなど
– ->activationは層間の重みを持っている
MGLUSER> (describe myfnn)#<FNN {10205FDD53}>BPN description: CLUMPS = #(#<>INPUT INPUTS :SIZE 784 1/100 :NORM 0.00000> #<>ACTIVATION (F1 :ACTIVATION) :STRIPES 1/100 :CLUMPS 4> #<>RELU F1 :SIZE 1200 1/100 :NORM 0.00000> #<>ACTIVATION (F2 :ACTIVATION) :STRIPES 1/100 :CLUMPS 4> #<>RELU F2 :SIZE 1200 1/100 :NORM 0.00000> #<>ACTIVATION (F3 :ACTIVATION) :STRIPES 1/100 :CLUMPS 4> #<>RELU F3 :SIZE 1200 1/100 :NORM 0.00000> #<>ACTIVATION (PREDICTION :ACTIVATION) :STRIPES 1/100 :CLUMPS 4> #<>SOFTMAXXELOSS PREDICTION :SIZE 10 1/100 :NORM 0.00000>) NSTRIPES = 1 MAXNSTRIPES = 100
ネットワークの内容
● 各層のオブジェクトはnodesとderivativesというスロットを持っていて、それ
ぞれ順伝搬、逆伝搬の値が入る
MGLUSER> (describe (aref (clumps myfnn) 0))#<>INPUT INPUTS :SIZE 784 100/100 :NORM 94.94252> [standardobject]
Slots with :INSTANCE allocation: NAME = INPUTS SIZE = 784 NODES = #<MAT 100x784 AF #2A((0.0d0 0.0d0 0.0d0 0.0d0 0.0d0.. DERIVATIVES = #<MAT 100x784 A #2A((3.2746879326098654d13 2.3442.. DEFAULTVALUE = 0 SHAREDWITHCLUMP = NIL X = #<>INPUT INPUTS :SIZE 784 100/100 :NORM 94.94252> DROPOUT = NIL MASK = NIL
ネットワークの内容
● 出力層は教師信号を表すtargetスロットを持っている
MGLUSER> (describe (aref (clumps myfnn) 8))#<>SOFTMAXXELOSS PREDICTION :SIZE 10 100/100 :NORM 9.98895> [standardobject]
Slots with :INSTANCE allocation: NAME = PREDICTION SIZE = 10 NODES = #<MAT 100x10 ABF #2A((2.017507522628972d9.. DERIVATIVES = NIL DEFAULTVALUE = 0 SHAREDWITHCLUMP = NIL X = #<>ACTIVATION (PREDICTION :ACTIVATION) :STRIPES 100/10 TARGET = (3 8 6 1 0 0 9 4 8 7 0 4 2 5 6 0 6 3 9 3 2 0 9 3 1 ..
ネットワークに入出力を設定
● 入力層のnodesと出力層のtargetにデータセットの値を設定する
(defmethod setinput (samples (bpn fnn)) (let* ((inputs (or (findclump (chunklumpname 'inputs nil) bpn :errorp nil) (findclump 'inputs bpn))) (prediction (findclump 'prediction bpn))) (clampdata samples (nodes inputs)) (setf (target prediction) (labeltargetlist samples))))
学習部分
● 最適化の本体はminimize
● 最適化アルゴリズムを表すoptimizerと学習対象を表すlearnerを渡
す
(defun trainfnn (fnn training &key (nepochs 3000) (learningrate 0.1) (momentum 0.9)) (let ((optimizer (makeinstance 'segmentedgdoptimizer :segmenter (constantly (makeinstance 'sgdoptimizer :learningrate learningrate :momentum momentum :batchsize (maxnstripes fnn))))) (learner (makeinstance 'bplearner :bpn fnn)) (dateset (makesampler training :nepochs nepochs))) (minimize optimizer learner :dataset dateset) fnn))
モニタリング関数
● optimizerに学習の途中経過を表示するモニタリング関数を付ける
(defun trainfnnwithmonitor (fnn training test &key (nepochs 3000) (learningrate 0.1) (momentum 0.9)) (let ((optimizer (monitoroptimizationperiodically (makeinstance 'segmentedgdoptimizerwithdata :training training :test test :segmenter (constantly (makeinstance 'sgdoptimizer :learningrate learningrate :momentum momentum :batchsize (maxnstripes fnn)))) '((:fn logbpntesterror :period logtestperiod) (:fn resetoptimizationmonitors :period logtrainingperiod :lasteval 0)))) (learner (makeinstance 'bplearner :bpn fnn)) (dateset (makesampler training :nepochs nepochs))) (minimize optimizer learner :dataset dateset) fnn))
学習の進行部分
● fnnの重みをランダムに初期化して訓練を実行
● with-cuda*マクロを被せることでGPUを使った計算ができる
– CPUを使う場合は変数 *cuda-enable* を nil に設定する
(defun trainfnnprocesswithmonitor (fnn training test &key (nepochs 30) (learningrate 0.1) (momentum 0.9)) (withcuda* () (repeatably () (initbpnweights fnn :stddev 0.01) (trainfnnwithmonitor fnn training test :nepochs nepochs :learningrate learningrate :momentum momentum))) fnn)
実験
● 784-1200-1200-1200-10のネットワーク
● 隠れ層の活性化関数はReLU
(time (trainfnnprocesswithmonitor myfnn *trainingdata* *testdata* :nepochs 30))
Evaluation took: 1638.934 seconds of real time 1638.103618 seconds of total run time (1364.208722 user, 273.894896 system) [ Run times consist of 0.873 seconds GC time, and 1637.231 seconds nonGC time. ] 99.95% CPU 5,559,500,797,670 processor cycles 1 page fault 6,657,802,576 bytes consed
Dropout
● 一定確率でユニットを無効化して学習する
– 小さなネットワークの集団学習と見なせる → 汎化性能が向上
● 一方で収束は遅くなる
入力層 隠れ層 出力層
Dropoutを入れたネットワークの定義
● Dropoutのためのlumpが増えている
● 入力層の20%、隠れ層の50%を無効化する
(defparameter fnnreludropout (buildfnn (:class 'fnn :maxnstripes 100) (inputs (>input :size 784 :dropout 0.2)) (f1activations (>activation inputs :name 'f1 :size 1200)) (f1* (>relu f1activations)) (f1 (>dropout f1* :dropout 0.5)) (f2activations (>activation f1 :name 'f2 :size 1200)) (f2* (>relu f2activations)) (f2 (>dropout f2* :dropout 0.5)) (f3activations (>activation f2 :name 'f3 :size 1200)) (f3* (>relu f3activations)) (f3 (>dropout f3* :dropout 0.5)) (prediction (>softmaxxeloss (>activation f3 :name 'prediction :size 10) :name 'prediction))))
Dropout+Maxout
● 隠れ層の活性化関数をReLUからMaxoutに
● Maxoutのユニット数(group-size)を指定する
(defparameter fnnmaxoutdropout (let ((groupsize 5)) (buildfnn (:class 'fnn :maxnstripes 100) (inputs (>input :size 784 :dropout 0.2)) (f1activations (>activation inputs :name 'f1 :size 1200)) (f1* (>max f1activations :groupsize groupsize)) (f1 (>dropout f1*)) (f2activations (>activation f1 :name 'f2 :size 1200)) (f2* (>max f2activations :groupsize groupsize)) (f2 (>dropout f2*)) (f3activations (>activation f2 :name 'f3 :size 1200)) (f3* (>max f3activations :groupsize groupsize)) (f3 (>dropout f3*)) (prediction (>softmaxxeloss (>activation f3 :name 'prediction :size 10) :name 'prediction)))))
テストデータに対する予測性能
● 大差ない (ReLU: 98.51%、 ReLU+Dropout: 98.65%、Maxout+Dropout:98.51%)
計算時間
● 同じ784-1200-1200-1200-10のネットワーク
● CPUとGPUでReLU、ReLU+Dropout、Maxout+Dropoutを比較
● CPU: Core i5 4670 (4コア)、GPU: GeForce GTX750Ti (640 CUDAコア)
● MGL-MATのデータ型がdouble-floatになっていると遅い
– → MGL-MAT:*default-mat-ctype* を :float に設定
ReLU ReLU+Dropout Maxout+Dropout
CPU 884s 1030s NIL
GPU 243s 248s 130s
Tensorflowとの比較
● Tensorflow: Googleが出しているフレームワーク (C++/Python)
– Tensorflow 0.8.0
● MNIST、隠れ層2層(256ユニット)、ReLU、Momentum SGD
● CPU(Core i5 4670)による実行時間の比較
– TensorflowはCPU使用率が低く、220%くらいしか出ていない
– MGLのCPU使用率はほぼ400% (4コアCPU)
real total
MGL 36.6s 146.04s
Tensorflow 95.52s 175.55s
Tensorflowとの比較
● MNIST、隠れ層2層(256ユニット)、ReLU、Momentum SGD
● GPU(GeForce GTX750Ti)による実行時間の比較
● MNIST、隠れ層2層(2560ユニット)、ReLU、Momentum SGD
– 隠れ層の次元を10倍にしてみる
real
MGL 22.609s
Tensorflow 28.209s
real
MGL 206.371s
Tensorflow 216.011s
まとめ
● Lispで機械学習できない理由はない
– 同じ言語の中で高い記述力と計算速度のトレードオフを調節できる
– モデル記述はPython、学習アルゴリズムはC++で書くといった面倒がない
● ReLU、Maxout、Dropout、RNN、LSTMなど、最近よく使われる手法や
モデルが揃っている
– 畳み込みニューラルネットは実装されていない
● 計算速度も他のライブラリに比べて遜色ない。むしろ速い