元理系院生の新入社員がPythonとJavaで色々頑張るブログ

プログラミングや機械学習について調べた事を書いていきます

Pythonで逐次型自己組織化マップ

この記事に書かれていること

逐次型自己組織化マップの説明
Pythonによる逐次型自己組織化マップの実装



自己組織化マップとはなにか

自己組織化マップ(Self Organizing Map)は、多次元の特徴ベクトルをベクトルの関係性を維持しながら任意の次元に写像する様に学習するニューラルネットワークの1つです。

難しい言い回しですね。

色の例を用いてざっくりと説明します。
まず、マス毎にランダムに色が塗られている方眼紙があるとします。
f:id:emoson:20150216031931p:plain

そして、スイッチを押すとランダムに色が出てくる変な機械があるとします。
誰が何の為にそんな機械があるかどうかは考えないでおきます。

SOMの学習は次のように行われます。

  1. スイッチを押して色を取得する
  2. 方眼紙の中から最も取得した色に近いマスを選択する
  3. そのマスの色と取得した色を混ぜあわせる
  4. ついでにそのマスの周りのマスにも少し色を混ぜあわせる
  5. 上記を繰り返す

学習終了後に方眼紙を眺めてみると、似た色同士が近い所に集まるように方眼紙が塗り分けられています。
(例)
f:id:emoson:20150216032617p:plain


色の説明だけだとSOMの用途が分かりにくいですよね。
SOMの主な用途はベクトル間の関係を壊さずに少ない次元に写像する事で、人間には分かりにくい多次元のデータの関係の可視化を行ったり、近傍関係を用いてクラスタリングに利用したりします。

Pythonによる逐次型自己組織化マップの実装

__author__ = 'emoson'
#ユークリッド距離
dist = lambda vec1, vec2: (sum([(vec[0]-vec[1])**2 for vec in list(zip(vec1, vec2))]))**0.5

def near(map_vector, input_vector):
    """
    input_vectorに対し、最近傍ユニットのラベルを返す
    :param map_vector:
    :param input_vector:
    :return:
    """
    dist_vector = [dist(input_vector, m) for m in map_vector]
    return dist_vector.index(min(dist_vector))


def learning(input_vectors, map_width, map_height, vec_size, learning_count, area_size):
    """
    自己組織化マップの学習
    :param input_vectors:
    :param map_width:
    :param map_height:
    :param vec_size 入力ベクトルの次元数(色の場合はrgbの3次元):
    :param learning_count:
    :param area_size:
    :return map_vector:
    """
    import random
    map_vector = [[random.random() for v in range(vec_size)] for h in range(map_height) for w in range(map_width)]
    a = 1.0
    a_size = area_size
    for t in range(learning_count):
        for input_vector in input_vectors:
            #近傍ユニットの探索
            bmu = near(map_vector=map_vector, input_vector=input_vector)
            #重みの更新
            for i in range(map_width * map_height):
                c = (a_size - dist([i // map_width, i % map_width], [bmu // map_width, bmu % map_width]))
                if c > 0:
                    map_vector[i] = [mv+c*a*(iv-mv) for iv, mv in zip(input_vector, map_vector[i])]
        #学習係数、学習範囲の更新
        a = (learning_count - t) / learning_count
        a_size = area_size * (learning_count - t) / learning_count
    return map_vector

次のように呼び出すと、学習されたSOMのmapが返って来ます。

width = 20
height = 20
import random
input_vectors = [[random.random() for j in range(3)] for i in range(30)]
som_map = learning(input_vectors=input_vectors, map_width=width, map_height=height, vec_size=3, learning_count=0, area_size=3)

学習回数毎にSOMのマップを可視化すると、次のようになります。
f:id:emoson:20150216033606g:plain

徐々にマップが学習されている様子が分かりますね。

動画

ブログ用 - YouTube