忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

かんたん!制限付きボルツマンマシン コントラスティブ・ダイバージェンス(Contrastive Divergence)法[Deep Learningシリーズ]

コントラスティブ・ダイバージェンス法

制限付きボルツマンマシン(Restricted Boltzmann Machine : RBM)は覚えたパターンを後で思い出す能力を持ったニューラルネットワークです。
あるパターンを覚え、後でそれに似たパターンを入力してやると、最初に教えたパターンを思い出すことができます。
制限付きボルツマンマシンはDeep Learningするネットワークの一部に使われています。
ですからDeep Learningしようと思ったら、制限付きボルツマンマシンを学んで損はないでしょう。



制限付きボルツマンマシンの学習には、コントラスティブ・ダイバージェンス(Contrastive Divergence : CD)という方法が有名なようです。
ですので、今回はこのコントラスティブ・ダイバージェンス法について解説します。

起きている間にデータを一つだけ取り入れ、すぐ眠る

「コントラスティブ・ダイバージェンス?なんだか難しそうだ…」とお思いの方に朗報です。
これは簡単です。

コントラスティブ・ダイバージェンス法を一言で言うと、こういうことになります:
「起きている間にデータを一つだけ取り入れ、すぐ眠る」



ここで重要なのは、データをひとつ取り入れた後、別のデータを見てはいけないということです。
起きている時にデータをひとつ見て学習の準備をし、寝て学習の準備をし、最後に昼と夜に準備された情報を元に学習を行います。
(※なお、「眠る」といってもなにもしないわけではなく、反学習という学習の準備が進んでいます。)
人間でたとえると「起きている間にあんまりつめ込むと学習が上手く進まない」というようなことでしょうか?
(テスト前の一夜漬けは上手くいかないということでしょうか?
しかし制限付きボルツマンマシンと人間が同じかというと私にはよくわかりません。
話半分に聞いて下さい。)

もう少し詳しいコントラスティブ・ダイバージェンスのアルゴリズムは、次のようになります。
以前私が書いた記事から引用します:

  1. 外部からの入力パターンを学習する(目が覚めている状態:Positive Phase)
    1. 入力パターンを可視ニューロンにセットする(つまり可視ニューロンのOn/Offを環境からセットする)。
    2. 隠れニューロンの発火状態(On/Off、1か0)を更新する。
    3. 結合の重みを、ヘブ則に従って更新する準備をする。
      つまり、
      d重み += 学習係数 × 可視ニューロンの発火状態 × 隠れニューロンの発火確率;
      (「d重み」は重みの変化を表す変数です。
      最後に「重み += d重み;」します。)
    4. ニューロンのバイアスを更新する準備をする。
      ここで、可視ニューロンと隠れニューロンでなぜか学習式が異なることに注意。
      可視ニューロン : dバイアス += 学習係数 × 発火状態;
      隠れニューロン : dバイアス += 学習係数 × 発火確率;
      (「dバイアス」はバイアスの変化を表す変数です。
      最後に「バイアス += dバイアス;」します)
  2. 外部からの入力をなくし、自由連想する(夢を見ている状態 : Negative Phase)
    1. 可視ニューロンの発火状態を更新する。
    2. 隠れニューロンの発火状態を更新する。(場合によってはここから1に戻り、1、2を何度か繰り返すが、戻らなくてもそこそこのいい結果が出ることが知られている)
    3. 結合の重みを、ヘブ則の逆で更新する準備をする。
      つまり、
      d重み += -学習係数 × 可視ニューロンの発火状態 × 隠れニューロンの発火確率;
      I.3では重みを増やしていたが、ここでは重みを減らしていることに注目。
      (学習係数の前にマイナス符号あり)
    4. ニューロンのバイアスを更新する準備をする。
      ここで、可視ニューロンと隠れニューロンでなぜか学習式が異なることに注意。
      可視ニューロン : dバイアス += -学習係数 × 発火状態;
      隠れニューロン : dバイアス += -学習係数 × 発火確率;
      I.4ではバイアスを増やしていたが、ここではバイアスを減らしていることに注意。
      (学習係数の前にマイナス符号有り)
  3. 結合の重み、ニューロンのバイアスを更新する。
    重み += d重み;
    バイアス += dバイアス;
    その後、
    d重み = 0;
    dバイアス = 0;

I、II、IIIを繰り返します。
(1000回くらい?)

Iでデータをひとつ取り入れパラメーターを大きくする準備をします。
IIで眠ってパラメーターを小さくする準備をします。
IIIで実際の学習を行います。

昼と夜、起きている時と寝ている時、Positive PhaseとNegative Phase

「起きている?
眠る?
何のことだ?」
とお思いの方がいらっしゃるはずです。
今から解説します。

ここで使われている「起きている」とか「眠る」は私のオリジナル比喩ではありません。
安心して下さい。
私はよくあまり上手ではないオリジナル比喩を持ちだして相手を混乱させてしまうことがありますが、これはそうではありません。
(制限付きに限らず)ボルツマンマシンの学習はざっくり分けると2つのステップからなります。
それが「起きている時の学習」と「眠っている時の学習」なのです。
(厳密に言うとこの2つが終わった後、最終ステップ「昼と夜に蓄積された情報から実際にシナプスの重みとニューロンのバイアスを修正する」があるのですが、まあ細かいことは抜きにしましょう)
この表現はボルツマンマシンの話でよく使われます。
眠っている時の反学習と言うのはいかにもフランシス・クリックさんの、夢は忘れるために見るのだと言う説と似ているためよく関連があると言われています。


で、「起きている」とはどういうことかというと、外部から入力を受け付けて学習の準備をする、という意味です。
「眠る」とは、外部からの入力をシャットアウトし、内部の情報だけで色々やって学習の準備をする、という意味です。
そして最後のステップで、「起きている」時と「寝ている」時に得た情報を元に、実際の学習を行います。

(この記事では「起きている」時を昼と呼ぶことにします。
正式名称は"Positive phase"です。
この記事では「寝ている」時を夜と呼ぶことにします。
正式名称は"Negative phase"です。)

つまり実際の生き物は昼と夜にそれぞれ別に学習するのですが、コントラスティブ・ダイバージェンス法では学習をまとめてやるのですね。
そうしないと、私のやった感じだと、ちょっと不安定になる感じがあります。
本によっては別々に学習している方法を紹介しているものもありますが…。

(下の図では、最後の「実際に学習を行うステップ」が単純化のため描かれていません。
2つ四角が描かれていますが、本当は3つなければおかしいのです。)




データの流れを書くとこんな感じです:
(話を簡単にするために、可視ニューロンは1個、隠れニューロンは1個としています)


はじめに可視ニューロンに外部からデータがセットされます。
昼です。
可視ニューロンの状態を元に隠れニューロンの状態が更新されます。
(上の図には書いていませんが、ここでd重みが更新されます)

夜です。
隠れニューロンの状態を元に可視ニューロンの状態が更新されます。
可視ニューロンの状態を元に隠れニューロンの状態が更新されます。
(上の図には書いていませんが、ここでd重みが更新されます)

最後に、上の図には書いていませんが、重み += d重み;というふうに、ニューロンの結合の重みそのものが更新されます。

詳しい手順

上に手順を書きましたが、ここではもうちょっと具体的なケースを見てみます。
話を簡単にするために、可視ニューロン1つ、隠れニューロン1つで考えて行きましょう。

 
  
ここには全部で2つのニューロンがあります。
最初は全てのニューロンが0、0.0です。
この2つの数字は、それぞれ発火状態と発火確率です。
発火状態 = 0
発火確率 = 0.0
です。
上の整数が発火状態、下の小数が発火確率です。

発火状態は0か1の整数です。
発火状態は二通りしかありません。

発火確率は0から1までの小数です。
発火確率がたとえば0.2だと、そのニューロンが次の瞬間発火する確率は0.2、発火しない確率は0.8です。

普通はまず発火確率を求めてから発火状態を更新します。
(これには例外があって、学習時や想起時に、可視ニューロンは発火確率と無関係に発火状態が入力されます)

結合の重みは0ということにしましょう。

さて!
では学習を始めます。
制限付きボルツマンマシンは学習したデータを後で想起することができます。
それを今セットするのです。

とりあえず1をセットしましょう。
セットされるのは上の青い1つの可視ニューロンです。



可視ニューロンの状態が1になりました!
これを今から学習します。
(学習によって結合の重みが大きくなれば良い)


(なお、可視ニューロンの発火確率が0.0であることに注意して下さい。
普通発火確率が0.0なら発火状態も0になりますが、このように外部からデータを入力するときは違うのです。
ミスマッチが起きるのです。
でもまぁ気にしないでください。)

次は隠れニューロンの状態を更新します。
まずは発火確率を求めます。



隠れニューロンの発火確率は0.5となります。
つまり発火するかしないか半々です。
何故そうなるのかというと、結合の重みが0だからです。
つまり隠れニューロンには刺激が来ないわけで、これでは発火していいのかいけないのかわかりません。
別の説明の仕方をすると、活動電位がsigmoid(1 * 0.0)だからです。
sigmoid(0)は0.5ですからね。

次に結合の重みの変化を表す「d重み」を更新します。
昼のd重みの更新式は、こうです:

d重み += 学習係数 * 可視ニューロンの発火状態 * 隠れニューロンの発火確率;

これは「2つのニューロンが同時に発火した時その間の結合が強くなる」というヘブ則に似ていますね。
(夜の更新式はまた違います。プラズマイナス逆になるのです)

よって、このケースでは次のようになります:

d重み += 学習係数 * 1(可視ニューロンの発火状態) * 0.5(隠れニューロンの発火確率)

学習係数を1とすると(本当は0.1とかのほうがいいのかも知れませんが、ここでは簡単にするために1とします)、こうなります:

d重み = 0.5

同様にして、dバイアスも計算します。
しかしめんどくさくなってきたのでここではバイアスの計算は省きます!
大体は上の引用を見ていただければ理解していただけると思います。

これで昼は終わりです。

夜、そして学習

さて夜がやってきました。
おやすみなさいの時間です。
制限付きボルツマンマシンは目を閉じ眠ります。
目を閉じると外の世界からのインプットがなくなりますね。
つまり外の世界からの情報に惑わされずにニューロンは状態を更新できるのです。

可視ニューロンは、今度は外部からデータをセットされるのではなく、隠れニューロンの発火状態を元に自分自身の発火確率を求めます。

ちなみに、夜のd重みの更新式は次のようになります:

d重み += -学習係数 * 可視ニューロンの発火状態 * 隠れニューロンの発火確率;

(昼の更新式とはちょっと違います。
プラスマイナスが違うのです。
学習係数の前にマイナスが付いていますよね。
つまり、「2つのニューロンが同時に発火すると、その間の結合が弱まる」のです。
ヘブ則の逆ですね。
何でこんなのが必要なのでしょう?
不思議ですね。)

そうやってd重みを更新した後、今度は重みを更新します。
重み += d重み; //重みを更新
d重み = 0;        //リセット

  • 隠れニューロンの発火状態が0の場合(実は、1の場合も結果は変わりません)


    可視ニューロンの発火確率は0.5となります。
    つまり、可視ニューロンの次の発火確率は0と1両方とも0.5です。
    • 可視ニューロンの発火状態が0の場合
        
        
      隠れニューロンの発火確率は0.5となります。
      これから隠れニューロンの発火状態を求めてもいいのですが、どうせ学習には使われないので解説はスキップします。

      ではこのような状態で、どのように学習は行われるのでしょうか?
      d重み += -1(学習係数) * 0(可視ニューロンの発火状態) * 0.5(隠れニューロンの発火確率); 
      学習係数の前にマイナス符号がついていることに注意して下さい。
      これがいわゆる反学習です。
      ボルツマンマシンは忘れるために夢を見るのです。

      さて、昼のd重みの値は0.5だったので、現在のd重みの値はこうなります:
      d重み = 0.5 - 1 * 0 * 0.5
                = 0.5
      これは最後に接続を0.5だけ増やせという意味です。
      つまり、学習の結果、接続の重みは0.5になります。
    • 可視ニューロンの発火状態が1の場合


      隠れニューロンの発火確率は0.5となります。

      このような状態で、どのように学習は行われるのでしょうか?
      d重み += -1(学習係数) * 1(可視ニューロンの発火状態) * 0.5(隠れニューロンの発火確率); 

      さて、昼のd重みの値は0.5だったので、現在のd重みの値はこうなります:
      d重み = 0.5 - 1 * 1 * 0.5
                = 0.0
      これは最後に接続を0.0だけ増やせという意味です。
      つまり、学習の結果、接続の重みは0.0になります。

この調子で「隠れニューロンの発火状態が1」の場合も手作業でシミュレートできます。
しかし結果は同じなので気にしないでください。
このケースでは、隠れニューロンの発火状態は学習に何ら影響を与えません。

最終的に重みの期待値が0.25(= (0.5 + 0.0) / 2)になることは解っていただけると思います。
このように、重みがプラスになるとどうなるかというと、想起の際に出力が1になる確率が高まります。
つまり、1という学習したパターンを思い出せるようになったのです。

ちなみに、ここでは昼と夜を一度しか繰り返していません。
が、実際のプログラムでは1000回くらい繰り返したりします。
そして複数の学習パターンを学習することもあるので(制限付きボルツマンマシンは、複数のパターンを覚えることができます)、更に繰り返しの数は増えます。

拍手[9回]

PR