はじめに
自己符号化器(AutoEncoder)にスパース項を導入して、スパース正則化を行ってみます。
おおまかな内容
スパース正則化とは、訓練サンプルをより少ないユニット(:ニューロン:当ブログではノード)で再現できるようにパラメータを決定していくことです。スパースであるとは「まばら」であるということです。(すかすかともいいます。)
昔から人間の脳は10%くらいしか使われていない、などといわれていたようですが、ある意味間違いで、ある意味正しいといえると思います。脳の残り90%は未使用領域であるような言い方ですが、そうではありません。(こう解釈すると間違いです)ある思考や処理をする場合、その瞬間では高々10%くらいしか使われないという説明であればおおむね正しいです。脳はとてもエネルギーを消費する機関で、もし大部分が一斉に活動すると酸欠や血糖不足になり気を失ってしまうことになります。少ししか使わないというのはとっても「エコ」です。「やらなくてもいいことなら、やらない。やらなければいけないことは手短に。」ということです。
このスパース正則化により、学習がどのようななるのかを実験します。
なぜやるのか・どうなると思うか
各種の学習最適化手法の中でも「それぞれのノードの使用率(活性化される率)」に注目した手法です。Adamのアルゴリズムは比較的最近のもので、深層学習(青イルカ本)に載っていませんが、「スパース正則化」についてはに青イルカ本の5.4 スパース正則化(P62)として詳しく載っています。
今回はAdamでのしくじりにめげず自力で実装をしてみます。
果たしてうボクにうまく実装することができるのでしょうか。
「ボク、とっても気になります!」
準備と実験のやり方
アルゴリズム実装はどちらかといえば準備に相当するので、本当はこの「本実験」の方ではなく、「自由研究の準備」のカテゴリの方に書こうかと思ったのですが、準備のほうが本編よりも多くなってしまっているので、こちらに記載します。なお、プログラム自体はいろいろ調整中のため、ソースは改めてアップします。
実装で変更したのは以下です。設定まわりを除けば実質4行です。さすがpython!
変数の準備
β:sp_beta:スパース化のための調整係数
ρ:sp_rho:目標とするユニット(ノード)の活性化率
ρ_hat:sp_rhowork:実際のユニットの活性化率(近似値でもよい)
λ:sp_lambda:活性化率近似のための係数
上記を使った式の変更
コアな部分だけ書きます。下記 p は活性化関数f(Wx+b)の値を格納した行列です。
mask[ii]はドロップアウト計算用のマスク行列(ドロップアウトされた該当ノードがゼロ、他は1.0)です。
# 最近の活性化率の(移動)平均を計算します。青イルカ本P.66の式です。epsは発散防止用の微小値 10e-4 です。
self.sp_rhowork = self.sp_lambda * self.sp_rhowork + (1.0 - self.sp_lambda) * p + self.eps4
# スパース項を計算します。青イルカ本(5.4式)(P.65)の「スパース項」そのものです。同様に微小値 10e-4を加味しています。
spItem = self.sp_beta * (-(self.sp_rho * np.ones(self.n_hidden) / self.sp_rhowork)
+ ((1.0 - self.sp_rho) * np.ones(self.n_hidden)) / ((1.0 - self.sp_rhowork) + self.eps4))
#Limiter 以下の行は発散防止のためのもので、本来は不要です。±3.0をリミットとしています。3.0でなくてもよいです。
spItem = spItem * ((spItem >= -3.0) & (spItem <= 3.0)) + 3.0 * (spItem > 3.0) - 3.0 * (spItem < -3.0)
#元の式にspItem項を加えています。こちらが(5.4式)の本体です。
delta2 = (np.dot(self.W2.T, delta1) + spItem) * self.func.differential_func(p) * mask[ii]
(追記)自分への突っ込み:「平均活性度」とは活性化関数の値の「平均値」だと思ってたけど、説明にあるρ(ロウ)のグラフの値域がゼロから1までなので、活性化「率」をいっているのかもしれないぞ~。だとすると式がちがうぞ~。
(追記2)自分への突っ込み:式の前の節にρは確率って書いてあるじゃん!よく読もうね。なので「平均値」として計算している最初の行の活性化値の配列p(ピー)は計算して「1:活性化した」か「0:活性化しない」にしてやらないと。。。マイナスの活性化をどう扱うかがポイントかなぁ。活性化がマイナスに振れていても「抑制」なのでとっても意味はあるけど、マイナスを足し算したり、何回も掛け算するので、傾きを望まない方向にもっていくことがあるのをコントロールできないなぁ。マイナスは「0:活性化しない」の方向で。。。活性化したかどうかの「閾値」がいるゾ~。またハイパーなパラメター増えそう。
(追記3)決定ではありませんが、上の式の最初の2行を以下のようにしてみました(3行)。
#とりあえずは閾値を0.5にして1とゼロにして活性化の確率計算することにしました。
activateRho = 1.0 * (p > 0.5)
self.sp_rhowork = self.sp_lambda * self.sp_rhowork + (1.0 - self.sp_lambda) * activateRho
spItem = self.sp_beta * (-(self.sp_rho * np.ones(self.n_hidden) / (self.sp_rhowork + self.eps4))
+ ((1.0 - self.sp_rho) * np.ones(self.n_hidden)) / ((1.0 - self.sp_rhowork) + self.eps4))
#以降変化なし
(追記ここまで)
実験の結果
目標のスパース度合(活性化率がρ(ロウ))に近づいていくよう、バックプロパゲーションでフィードバックする「傾き」をある意味「無理やり」調整するので、全般的に学習を遅らせる方向にはたらくことが多いです。でも、活性化関数との組み合わせによっては(実験9)で行っている学習係数調整以上の効果がありました。(中盤は学習進度は遅めだが、終盤の最終精度がよい。)調整係数:βや目標活性率ρの値を変化させると、おおむね期待する動作をしています。ボクの自己符号化器ではβ=3.0とかにすると発散しちゃうのですが、係数値の変化に対する傾向は同じなので、ほぼほぼ正しいのではないかと考えています。発散防止用にいれた値やしくみがすこし怪しいかもです。(なお、このブログを書いている時点では実験9は継続中です。(追記)この実験10もスパース項の計算に問題があることがわかったので実験継続となりました。迷宮入りとも言います。(追記ここまで))
比較用に同じ条件でスパース正規化をしないケースの実験を貼ります。
わかったこと・わからなかったこと
調整係数:βを大きくすると「そのもの」の学習をする傾向や、目標活性率ρに近づこうとする傾向は確認できました。
ただ、青イルカ本「図5.6 スパース正則化工の重みβと学習される特徴の違い.」(P.67)にあるようなβ=0.1での「ストローク」状の重み図形のような「きれいな」一部ストロークになるような組み合わせはみつけられていません。(それらしくはなるのですが。。。)
(追記)「きれいな」一部ストローク取得こそが、スパース正則化の真骨頂(あ、この言葉かっこいい)であると感じており、この自由研究(その10)は継続実験になっています。どうしても
のようなきれいな形にならないのです。
長年*1実験をしてきていますが、重みのイメージパターンとして「きれいなストローク」様式と「幾何学的モアレ」様式を獲得できていません。
(追記)ちなみにボクが勝手に「幾何学的モアレ」様式と呼んでいるのは下のような重みイメージです。(上のストロークを含め、イメージは他の方の実験結果から拝借しました。m(_ _)m)
市松模様的です。MNIST数字イメージのまんま「多重重ね合わせ」というより「干渉縞」っぽいので、モアレ様式と勝手に呼んでいます。干渉縞は「干渉縞同士の重ね合わせ+ちょっとした演算」で元の画像を再現できるものがあります。(ホログラムなどは立体を再現できますし。。。)
「数字の複雑怪奇な重ね合わせ」重みなら何十万件も作ってきているのに、このパターンを見たことないのは、出現条件が全く違う領域でテストしているのかもしれません。ちょっと残念ですが、いつかはボクのプログラムで作れる(学習できる)と信じています。(追記ここまで)
ボクの自己符号化器プログラムはちょっとした工夫で効率化的になるようアレンジをしている部分がありますが、「決定的に何かを欠落させてしまっている」可能性を感じています。いや、「もともと何かを間違えている」可能性すらある、とも感じています。そう、すこしぐらいかなりバグがあっても動くくらいに、自己符号化器の特徴抽出能力はきわめて柔軟にして堅牢(あ、この言葉もかっこいい)なしくみといえるのです。(あ、言い切ったぞ)
感想・思ったこと・考えたこと
「スパースであること」に注目して学習調整がうまくいくのであれば、「各ノードの情報量」や他のノードに対する「独自性・類似性・関連性」に関する補正項を入れて収束させていけばいろいろ学習をコントロールできる感じがしました。(気のせいかもですが。。。)
飲んだら♪こういっちゃうよ~♪♪「キュー!!」
あまり他の方に「おすすめ」をするタイプではないのですが、ひとつだけおすすめしたいことがあります。この夏ボク的に大人気だった清涼飲料水。それは。
フツーの炭酸水に「ポッカレモンプレミアム」(シチリア産ストレート果汁)を入れるだけのものです。(5ccくらい?)砂糖やシロップはいれません。炭酸水はボク的にはこれです。
甘い飲料が苦手のボクにぴったり。(甘いケーキは大好きです。)飲み物は甘いとボク的に後味がすっきりぜず苦手です。
最近はパッケージがかわって近くのスーパーにはこちらがおいてあります。味は同じだからいいのですが、栽培とかではなく「有機」そのものを商品名にするなんてすごい挑戦だとおもいました。オーガニックはわかるのですが。。。「有機」と名付ける勇気!
商品名はともかく、来年の夏ぜひチャレンジしてみてください。
「フツーのポッカレモンではでない味なので代用不可です。」
ちなみにボクはまとめ買いして、今も切らすことのない「ありよりのあり!」です。
さて、またもや予告と違った実験レポートとなってしまいすみません。次こそは学習カリキュラム実験やるつもりでいます。
*1:人間の時間では3ヶ月ですが、カエルにとっては人間の3年に相当します。いわゆるFlog Year(人間:カエル≒1年:1ヶ月)です。人間界の「石の上にも3年」はカエル界では「葉っぱの上にも3ヶ月」といいます。「桃栗3年柿8年」はカエル界では「柿は子孫のために植えろ」(ジブンで到達できなくても継承者が大成できればよいの意)になります。