CNNの勉強(Ⅱ)-自己組織化マップの理解とPythonによるプログラム例

前回は、CNNの勉強(Ⅰ)として、ネオコグニトロンまで説明をしました。

今回は、視覚野の方位選択性コラムが自己組織的に、つまり教師な学習により生成される過程を再現してみたいと思います。

自己組織化とは

視覚野における自己組織化

視覚野
自己組織化
眼優位性
方位選択性
ブロッブ
図1 視覚野における自己組織化とコラム構造

まず、図1の(a)、(b)、(c)について詳しく説明します。

(a)図は視覚野の眼優位性コラムをイメージしたものです。黒色で塗られた部分が右目から情報を受ける眼優位性コラムで、白抜きの部分が左目から情報を受ける眼優位性コラムを表しています。外部の像は網膜で受け取り、視交叉で左右の目からの情報が交差し、外側膝状体を中継して視覚野へ投射されています。

(b)図は(a)図の一部分を縦に切り抜き詳しく表したものです。大脳新皮質は6つの層からできています(図(b)では6層示せていませんが)。図(b)において、右斜め上方向への流れを持つのが眼優位性コラムで、それに垂直に交わる形で方向選択性コラムが並んでいます。方位選択性コラムは特定の傾きにたいして選択的に反応します。少しずつ異なる傾きに反応する方位選択性コラムが並んでいます。

(c)図は、(a)図の一部を拡大したもので、眼優位性コラムの境界面で方位選択性コラムが垂直に交わっていることを示したものです。(b)図は、あくまでモデルであり、方位選択性コラムの並行状態がどこまででも続いていることを仮定しています。実際に、個々の方位選択性コラムが並行な状態は眼優位性コラムの境界面付近であり、境界面以外では一部に集まったり広がったりと、必ずしも並行な状態が続くわけではないことを示しています。(c)は自己組織化の話題で関連性が薄いため、忘れてもらって大丈夫です。

図1では、色分けに一貫性を持たせたので、参考にしてください。

ここで重要な点は、この方位選択性コラムが、教師なしに勝手に(自己組織的に)似た角度のコラムが隣り合う( (c)ではこれを否定しているが…)ように学習が行われることです。これを自己組織化といいます。

自己組織化マップは視覚野のコラム構造が自己組織化されることをモデル化したものです。

感覚系で全体的にいえること

自己組織化は、視覚野だけでなく視覚野でも行われていることが有名で、視覚野や聴覚野に限らず、その他多くの感覚系でも行われています。これは、トポグラフィックマッピングと関連性が強く、自己組織化が関与していると考えられています。この仕組みを実現しようとするアプローチの一つが自己組織化マップといえます。

ちなみに、この話題になると「特定の働きをするニューロンの配置が遺伝子に埋め込まれているにちがいなく、それを使用して再現しているだけだ」と思う方が多いと思いますので、以下に面白い実験結果を紹介します。

聴覚野になるべきところに視覚野が!?

2000年にNature誌に公開された論文で、生まれたばかりで脳が完全に発達していないフェレットを使用して、視覚情報を聴覚野の領域に繋ぎ変えたというものがあります。結果、聴覚野になるはずの領域に、視覚野の方位選択性コラムと方向マップが構築されていました。

気になる方は「Induction of visual orientation modules in auditory cortex.」と検索してみてください。

ここから、 特定の働きをするニューロンの配置は遺伝子により決められているわけではないといえます。

まとめると

ここまでをまとめてみます。

  1. 大脳において、自己組織化される前は、特定の領野に特定の情報が入力されなければならないという決まりはなく、入力された情報に従って、それぞれの領野を自己組織化すると仮定できる。
  2. 大脳全領域において一貫した学習手法に従って学習が行われていると仮定できる。(聴覚野の位置で視覚野の学習ができたことから)

特に2が重要です。現在では画像認識や音声認識など認識対象ごとに人間が特徴量やネットワークの型を決めますが、自己組織化なる万能な学習手段が見つかれば、 入力データの性質によらずに特徴抽出から認識まで、同一のネットワークで学習できることを示唆しているのです。

自己組織化マップ

それでは、自己組織化マップについて説明していきます。

自己組織化マップとは

自己組織化マップ(Self-Organizing maps : SOM)はコホネンにより提案されたことにちなみ、コホネンマップやコホネンネットワークともいわれ、入力層と出力層の2層から成り立ちます。出力層は他に競合層ともいわれます。競合層といえば競合学習が思い出されますよね。

以下には、出力層が2次元の例を示します。

図2(a)は出力層のニューロンが2次元上に配置された状態を示しています。図2(b)は入力層のニューロンを1つ追加した状態を示しています。入力層のニューロンは出力層に存在する全てのニューロンに結合しています。

図2 自己組織化マップの説明1

入力層のニューロン数を増やしてみます。増やした状態が図3です。

図3 自己組織化マップの説明2

逆の見方もできて、単純パーセプトロンというと語弊がありますが、イメージとしては単純パーセプトロンが並列に沢山並んでいるようなものです。

図4 自己組織化マップの説明3

単純パーセプトロンを含め、ニューロンは入力次元数と等しい数の成分を重みベクトルに持ちます。ニューロンは、線形分離可能な分類問題で、正しく分類できることを目標に習を行います。一方で自己組織化マップは入力ベクトルと重みベクトルが近くなるように学習が行われます。

他のニューラルネットワークと異なるのは、個々のニューロンが個別に学習されるのではなく、マップ上で入力ベクトルに最も近い重みベクトルを持つ勝者ニューロンを中心に学習が行われることです。勝者ニューロンになれなかったニューロンは、勝者ニューロンに倣えという感じに学習がされます。これは、朱に交われば赤くなるというのが最も分かりやすいでしょう。運よく入力ベクトルと近い重みベクトルを持つ勝者ニューロンが影響力をもち、周囲のニューロンに影響をあたえます。つまり、入力データ数がm個のとき、n個の出力層ニューロンの内、m個のみ勝者ニューロンになることが許され、入力データに限りなく近い重みをもてます。周囲のニューロンは学習データとして入力されなかったそれ以外のデータに対応できるように、勝者ニューロンとは少しだけことなる特徴をもちます。自己組織化マップではこのようにして教師なしの自己組織化を実現します。

自己組織化マップの特徴

以下の特徴を頭の片隅に置きつつ、以降の説明を読んでいただければと思います。

・1つの入力に対して発火するニューロンはただ1つ
・教師なし学習である
・大脳における初期の学習を再現している。
・マップ上のニューロンの出力値は内積で決められることが、他のニューラルネットワークのニューロンと同じだが、閾値がニューロンの入力に含まれないことが異なる。
※考え方によってはマップ上のニューロンに閾値が無いわけではない。マップ内のニューロン全ては共通の閾値、つまり、2番目に大きかったニューロンの出力値を閾値として持つと解釈できる。二番目に大きいニューロンの出力値をマップ上の全てのニューロンから引き、正か負かを単位ステップ関数で量子化し、その行列を実際のマップの出力値とアダマール積する。これにより勝者ニューロン以外は0、勝者ニューロンはその出力値を出力できる。

出力層の出力

自己組織化マップの出力層において、発火するニューロンは入力データ1つにつき1つだけです。入力ベクトルに最も近い重みベクトルを持つ、つまり、マップ(出力層)上のニューロンの中で内積の値が最も大きかったもの以外は、0に丸められます。これを数式に表すと、以下のようになります。

入力ベクトルを\(\boldsymbol{x} = (x_0\ x_1\ \cdots\ x_m)^T\)
マップ上\(i\)番目のニューロンの重みベクトルを \(\boldsymbol{w_i} = (w_{0i}\ w_{1i}\ \cdots\ w_{mi})^T\)
全てのニューロンの重みを横に並べ行列にしたものを\(\boldsymbol{W}\)

\( y_i =
\begin{cases}
\boldsymbol{w_i}^T\boldsymbol{x} & (i == argmax (\boldsymbol{W}^T\boldsymbol{x}) )\\
0 & (i != argmax (\boldsymbol{W}^T\boldsymbol{x}) )
\end{cases}
\)

これは、argmaxで出力が最大となったニューロンのインデックスを取得し、それが\(i\)であれば、その値を、\(i\)でなければ0を出力することを表しています。

重みの更新方法

自己組織化マップの入力層および出力層の次元数に制限はありません。、また、入力次元数が\(m\)、出力次元数が\(n\)ならば、出力層の各ニューロンは\(m\)個の成分を持つベクトルになります。それぞれのニューロンの持つ重みベクトルが、入力ベクトルにどの程度似ているかを出力層のニューロン同士で競争させます。勝ち抜いたニューロン、つまり最も入力ベクトルに近い重みベクトルを持っていたニューロンの重みが、更に入力ベクトルに近づくように調節し、周囲も勝者ニューロンからの距離に応じた影響をうけ、重みが入力ベクトルに近づけられます。

\(i\)番目のニューロンの重み更新の手順を以下に示します。

①勝者ニューロンから\(i\)番目のニューロンまでの距離を求める。
➁①で求めた距離を正規分布に代入し、係数\(h_i\)を取得する。
➂学習率\(c\)を定め、\(\boldsymbol{w}_i^{(t+1)} = \boldsymbol{w_i}^{(t)} + ch_i(\boldsymbol{x} – \boldsymbol{w_i}^{(t)})\)
で重みを更新する。
※実際の学習ではブロードキャストルールを使用して、一気に全てのニューロンの重みを更新します。この場合、勝者ニューロンからどのくらい影響を受けるかを調節する係数\(h_i\)を、並べたベクトルを\(H\)として、

\(\boldsymbol{W}^{(t+1)} = \boldsymbol{W}^{(t)} + cH(\boldsymbol{x} – \boldsymbol{W}^{(t)})\)

で重み更新を行います。

Pythonによるプログラム例

使用する入力データ

今回自己組織化マップで教師なし学習をさせるのは、二次元の4つのベクトル

\(
\begin{pmatrix}
1\\0
\end{pmatrix},
\begin{pmatrix}
0\\1
\end{pmatrix},
\begin{pmatrix}
-1\\0
\end{pmatrix},
\begin{pmatrix}
0\\-1
\end{pmatrix} \)

です。用意するのは、これだけです。教師信号はありません。これを入力するだけで、出力層のマップには、それ以外の二次元入力

\(
\begin{pmatrix}
\frac{1}{\sqrt{2}}\\ \frac{1}{\sqrt{2}}
\end{pmatrix}
\)

などに反応するニューロンが”自己組織的”に生成されます。

今回学習に使用する自己組織化マップの形状

図5 今回使用する自己組織化マップ

入力データは二次元ベクトルなので、入力層のノード数は2つです。マップは5×5に設定しました。これにより、学習後は入力される全ての二次元ベクトルパターンは25個の特徴に分けられることになります。

Pythonでプログラムを書いてみる

最初に必要なライブラリ及び関数をインポートします。

import numpy as np
import matplotlib.pyplot as plt
#ガウス分布の確率密度関数をインポート
from scipy.stats import norm

つぎに、入力データを定義します。

X = np.array([[1, 0, -1, 0],
              [0, 1, 0, -1]])

自己組織化では、出力層の全てのニューロンが自分の位置を把握*1私の憶測ですが、実際の脳ではグリア細胞が学習に関与することで、このような位置関係を考慮した学習が可能になっていると考えます。していなければならないので、図5のようにマップの座標を定めます。

図6 マップの形状

位置を列ベクトルで表し、それを25個並べて、行列にします。

Vec = np.array([[0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4],
                [0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4]])

次に個々のニューロンの重みを正規分布に従ってランダムに生成します。以下の重み行列は、列ベクトルを25個並べたものです。

W = np.random.rand(2, 25)

自己組織化マップでは入力ベクトルと重みベクトルがどれだけ似ているかを、競争して学習するため内積を使用しますが、それぞれの重みベクトルの大きさが異なると、互いに正しい比較ができないため、それぞれが単位ベクトルになるように正規化します。

W = W / np.linalg.norm(W, axis=0)

この初期状態で、マップ上のニューロンがどの向きのベクトルを持っているのか、プロットしてみます。

plt.figure()
plt.quiver(Vec[0, :], Vec[1, :], W[0, :], W[1, :], angles='xy', scale_units='xy', scale=1)
plt.xlim([-1,5])
plt.ylim([-1,5])
plt.grid()
plt.axes().set_aspect('equal')
plt.show()
図7 初期状態におけるそれぞれの重みベクトルの例

初期状態なので、それぞれのニューロンが持つ重みベクトルの向きはバラバラです。それでは、まず一つずつ学習手順を追っていきます。

1.学習データを一つ選びます。ここではX[:, 0]を選びました。そして、入力ベクトルX[:, 0]と重みベクトルの近さ、つまり、内積を計算します。

図8 入力ベクトルと重みベクトル

そして、出力結果が最大のニューロンのインデックスをjに代入します。

j = np.argmax(np.dot(W.T, X[:, 0]))

2.j番目のニューロン(勝者ニューロン)からみた他のニューロンの位置ベクトルを計算しユークリッド距離に変換します。

D_Vec = Vec - Vec[:, j][:, np.newaxis]
D = np.linalg.norm(D_Vec, axis=0)

3.勝者ニューロンから近いところほど影響を受け、遠いニューロンほど影響を受けにくくするために、ガウス関数を使用して、距離に応じた係数を決定します。

H = norm.pdf(D)

4.重みベクトルを更新し、単位ベクトルに変換します。

W = W + c * H * (X[:, 0][:, np.newaxis] - W)
W = W / np.linalg.norm(W, axis=0)

ここまでが、1回分の学習です。これを4つの学習データを順番に変更しながら、10000回反復させるようにプログラムしたものが、以下になります。

#学習率cを0.01にセット
c = 0.01
#4つの入力データを10000回学習させる
for _ in range(10000):
    #iは4つの学習データから1つを選ぶ
    for i in range(4):
        #内積(入力ベクトルと重みベクトルの向きの近さ)を計算した結果、
        #最も値の大きかったニューロン(勝者ニューロン)のインデックスを取得する
        j = np.argmax(np.dot(W.T, X[:, i]),axis=0)
        #勝者ニューロンから見た時の周囲のニューロンの位置ベクトルを計算する
        D_Vec = Vec - Vec[:, j][:, np.newaxis]
        #D_Vecの列方向において、ユークリッド距離を計算する
        D = np.linalg.norm(D_Vec, axis=0)
        #勝者ニューロンを中心にガウス分布に従う割合で、入力データに
        #重みベクトルを近づけるための係数ベクトルHを取得する
        H = norm.pdf(D, scale=2)
        #全てのニューロンの重みベクトルを更新する
        W = W + c * H * (X[:, i][:, np.newaxis] - W)
        #それぞれの重みベクトルを単位ベクトルに正規化する
        W = W / np.linalg.norm(W, axis=0)

どのように重みベクトルが変化したか可視化してみましょう。

重み行列を初期化するときに、どの様なランダム値が生起したかにより、自己組織化マップの結果が変わるので、4パターンを実験してみました。

図9 それぞれのニューロンが発火する入力ベクトル

自己組織化マップにより視覚野の方位選択性コラムが、近い傾きが隣り合うように配置されるという現象を簡単な例を用いてシミュレーションできました。

プログラムをすべて示します

import matplotlib.pyplot as plt
#ガウス分布の確率密度関数をインポート
from scipy.stats import norm
X = np.array([[1, 0, -1, 0],
              [0, 1, 0, -1]])
Vec = np.array([[0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4],
                [0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4]])
W = np.random.rand(2, 25)
W = W / np.linalg.norm(W, axis=0)
#学習率cを0.01にセット
c = 0.01
#4つの入力データを10000回学習させる
for _ in range(10000):
    #iは4つの学習データから1つを選ぶ
    for i in range(4):
        #内積(入力ベクトルと重みベクトルの向きの近さ)を計算した結果、
        #最も値の大きかったニューロン(勝者ニューロン)のインデックスを取得する
        j = np.argmax(np.dot(W.T, X[:, i]),axis=0)
        #勝者ニューロンから見た時の周囲のニューロンの位置ベクトルを計算する
        D_Vec = Vec - Vec[:, j][:, np.newaxis]
        #D_Vecの列方向において、ユークリッド距離を計算する
        D = np.linalg.norm(D_Vec, axis=0)
        #勝者ニューロンを中心にガウス分布に従う割合で、入力データに
        #重みベクトルを近づけるための係数ベクトルHを取得する
        H = norm.pdf(D, scale=2)
        #全てのニューロンの重みベクトルを更新する
        W = W + c * H * (X[:, i][:, np.newaxis] - W)
        #それぞれの重みベクトルを単位ベクトルに正規化する
        W = W / np.linalg.norm(W, axis=0)

plt.figure()
plt.quiver(Vec[0, :], Vec[1, :], W[0, :], W[1, :], angles='xy', scale_units='xy', scale=1)
plt.xlim([-1,5])
plt.ylim([-1,5])
plt.grid()
plt.axes().set_aspect('equal')
plt.show()

次の記事は

です。

Follow me!

References   [ + ]

1. 私の憶測ですが、実際の脳ではグリア細胞が学習に関与することで、このような位置関係を考慮した学習が可能になっていると考えます。

CNNの勉強(Ⅱ)-自己組織化マップの理解とPythonによるプログラム例” に対して1件のコメントがあります。

この投稿はコメントできません。