むかし作ったやつを手直しして載せる
SOMとは
SOMはイメージとしては、"似たような"ベクトル同士が(1次元や2次元の)マップ上において"近傍に"配置されるように仕分けしていく機械学習。「多次元上で類似するものを低次元上で近傍に配置する」という意味では多次元尺度構成法(MDS)と似ている。
MDSはクラスタリングとかデータの可視化によく使われたりするんだけど、SOMは身の回りで実際に使われているのかはよく知らない。
詳しい説明は他にいくらでもあるので、そちらに譲ることにして、良さそうなサイトを挙げておく。
ただ、SOMの面白いところは、視覚情報処理において知られていた側抑制(近くのニューロンの発火を抑制する)をヒントに作られたというところで、生理学で知られていた事が工学的に応用できるというのはかなり面白いよね!(側抑制についてはこれとか)
さらに、SOMのアルゴリズムの中で使われるWinner-take-all(一番類似度が高いニューロンだけが重みを更新する)は非現実的だと思われていたんだけど、最近では神経発火のシミュレーションで同じような現象を再現するというような研究もあって、脳内でも実際にSOMみたいな学習機構があるんじゃないかって話があるんだよね!
マウスの海馬では部屋の場所ごとに発火するニューロン(Place-cell)というものが確認されているんだけど、これって凄いSOMっぽくてなんかわくわくするよね!(Place cellについてはこれとか)
うんちくはさておき…
実際に書いたものは、最初はランダムな色だったマップが似た色同士固まるように変化していくというもの。
サンプルプログラム
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation row = 40 col = 40 learntime = 100000 alpha = 0.05 weight = np.random.random([row,col,3]) def som(colorvec): min_index = np.argmin(((weight-colorvec)**2).sum(axis=2)) mini = int(min_index / col) minj = int(min_index % col) for i in range(-2,3): for j in range(-2,3): try: weight[mini+i,minj+j] += alpha * (colorvec - weight[mini+i,minj+j])/(abs(i)+abs(j)+1) except: pass def show_rslt(): im = plt.imshow(weight,interpolation='none') return im fig = plt.figure() plt.axis('off') ims = [] for time in xrange(learntime): color = np.random.rand(3) som(color) if time % 500 == 0: ims.append([show_rslt()]) ani = animation.ArtistAnimation(fig, ims) # ani.save('som.mp4') # <- NOT able to use on Windows plt.show()
類似度には何のひねりもなくL2ノルムを使う。
学習の肝となっているこの行は、右辺の分母に本当はガウシアンカーネルとかを使うんだけど、手抜きでガウシアンカーネル風になるようにしてみた。
weight[mini+i,minj+j] += alpha * (colorvec - weight[mini+i,minj+j])/(abs(i)+abs(j)+1)
実行結果
見た目に綺麗なものが出来上がると気持ちいい。
ただ気持ち悪いのは出来上がったMapの各要素が元々用意されていた色とは違うというところ。
元々のベクトルを並び替えられてこそ意味があるよねって思う。機会があればクラスタリングまでするようなものも書きたい。
学習率はそんなシビアなパラメータじゃなかった、ランダムなベクトルを突っ込んで学習しているだけなので過学習とか発散とかないからだと思う。
最初の方は0.2とかにしてだんだん学習率を低くしていくとかしたら効率が良くなるのかもしれない。
※ こちらにガウシアンカーネルも時間変化の学習率も実装されている方のプログラムがありました。
Pythonで自己組織化マップ NumPy版 - Qiita