読者です 読者をやめる 読者になる 読者になる

技術メモ

役に立てる技術的な何か、時々自分用の覚書。統計学、機械学習、神経科学、暗号理論...と幅広く興味があります。

SOM(自己組織化写像)のプログラム (python)

むかし作ったやつを手直しして載せる。
(昔のを見てると、行列で書けばいいものを全部for文で書いてたりして可愛い。)

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)

実行結果

f:id:swdrsker:20161208070819g:plain

見た目に綺麗なものが出来上がると気持ちいいね。
ただ気持ち悪いのは出来上がったMapの各要素が元々用意されていた色とは違うというところ。
元々のベクトルを並び替えられてこそ意味があるよねって思う。機会があればクラスタリングまでするようなものも書きたい。

学習率はそんなシビアなパラメータじゃなかった、ランダムなベクトルを突っ込んで学習しているだけなので過学習とか発散とかないからだと思う。
最初の方は0.2とかにしてだんだん学習率を低くしていくとかしたら効率が良くなるのかもしれない。


※ こちらにガウシアンカーネルも時間変化の学習率も実装されている方のプログラムがありました。
Pythonで自己組織化マップ NumPy版 - Qiita