Pythonによるスパイキングニューラルネットワークの実装
スパイキングニューラルネットワーク(Spiking Neural Network、以下SNN)について、最近注目度が上がっているらしい。
Pythonで組んでみている人は少なそうなので、備忘録も兼ねてとりあえず組んでみた。
理論は下記の記事が大変細かく説明しているので紹介。
今回はIzhkevichモデルを使ってPythonで組んでみる。
使うのはnumpyだけで、ただ微分方程式を解いているだけだが、なかなか面白い結果が得られた。
まずは1ニューロンの場合。
import numpy as np import matplotlib.pyplot as plt ## izhikevich法による計算 def dv(v,u,inp): v= 0.04*v*v +5*v +140 -u+inp return v/20 # 20は時定数 def du(v,u): a=0.02 b=0.2 u=a+(b*v-u) return u def runge(v,u,inp): kv1 = dv(v,u, inp); ku1 = du(v,u); kv2 = dv(v+kv1*0.5,u+ku1*0.5, inp); ku2 = du(v+kv1*0.5,u+ku1*0.5); kv3 = dv(v+kv2*0.5,u+ku2*0.5, inp); ku3 = du(v+kv2*0.5,u+ku2*0.5); kv4 = dv(v+kv3, u+ku3, inp); ku4 = du(v+kv3, u+ku3); v = v + (kv1 + 2.0*kv2 + 2.0*kv3 + kv4)/6.0; u = u + (ku1 + 2.0*ku2 + 2.0*ku3 + ku4)/6.0; return v,u inp = 10 #入力電流 v = -65.0 #初期膜電位 u = 5.0 #初期値 sv = [] su = [] #t=1000まで回してみる for t in range (0,1000): v,u=runge(v,u,inp) if v > 30: v = -65 u = u+2 sv.append(v) su.append(u) plt.plot(sv) plt.show() plt.plot(su) plt.show()
Vが発火してるタイミングから少し遅れてUも上昇しており、Uの影響によってVの発火が抑えられている。
ふむ。問題なさそうだ。
では次にネットワークにしてみる。
ネットワークのニューロンはとりあえず3個。何か一つが発火したとき、隣のニューロンへ興奮信号を送るケースを考える。
import numpy as np import matplotlib.pyplot as plt ## izhikevich法による計算 def dv(v,u,inp): v= 0.04*v*v +5*v +140 -u+inp return v/20 # 20は時定数 def du(v,u): a=0.02 b=0.2 u=a+(b*v-u) return u #結合入力計算 def ds(s,a): return (-s+a)/10 # 10は時定数 def runge(v,u,inp): kv1 = dv(v,u, inp); ku1 = du(v,u); kv2 = dv(v+kv1*0.5,u+ku1*0.5, inp); ku2 = du(v+kv1*0.5,u+ku1*0.5); kv3 = dv(v+kv2*0.5,u+ku2*0.5, inp); ku3 = du(v+kv2*0.5,u+ku2*0.5); kv4 = dv(v+kv3, u+ku3, inp); ku4 = du(v+kv3, u+ku3); v = v + (kv1 + 2.0*kv2 + 2.0*kv3 + kv4)/6.0; u = u + (ku1 + 2.0*ku2 + 2.0*ku3 + ku4)/6.0; return v,u inp = [10,10,10] #入力電流 v = [-65.0,-65.0,-65.0] #初期膜電位 u = [5.0,5.0,5.0] #初期値 s = np.zeros(3) sv = np.zeros((1000,3)) su = np.zeros((1000,3)) ss = np.zeros((1000,3)) print(v) #t=1000まで回してみる for t in range (0,1000): for n in range (0,3): v[n],u[n]=runge(v[n],u[n],inp[n]) if v[n]> 30: v[n] = -65 u[n] = u[n]+2 s[n] = ds(s[n],5) #発火したときに強度5のシナプス信号を送信 else: s = ds(s,0) inp[n] = inp[n] + s[n-1] #隣のニューロンにシナプス入力を反映 sv[t]=v su[t]=u plt.title("V") plt.xlabel("time") plt.ylabel("mV") plt.plot(sv) plt.show() plt.title("U") plt.xlabel("time") plt.ylabel("Activity") plt.plot(su) plt.show()
結合の効果によって、3つのニューロンの発火タイミングがずれ込む様子を確認できる。
結合がなければすべてのニューロンが冒頭で出したようなスパイクを示すが、今回の結果は全体的に冒頭のケースよりも発火間隔が短くなっているのがわかるだろうか。
3つのニューロンがお互いに発火したとき、その興奮信号を受け取っているので当然ではあるが、こうしてグラフにしてみるとなかなか単純な話ではなさそう。
逆に、興奮ではなく抑制信号を送るように書いてみよう。
s[n] = ds(s[n],5) #発火したときに強度5のシナプス信号を送信
この部分を5から-5に書き換えてやればよい。
結果は次のようになる。
興奮のときと比較すると、発火間隔は長くなっていることがわかる。
前は11回ほど発火していたが、抑制のときは9回くらいになっている。
入力はすべてのニューロンに定常的にただ10(mV)を入れているだけだが、ネットワークの作りだけでここまで差が出る。
各ニューロンに入る入力が全て異なる場合や、ニューロンの数が増えた場合にどうなるかは、実際に試してみていただければ。
また、今回は膜電位Vの動きをそのまますべてプロットしたが、ニューロンの数が増えると線が増えすぎて非常に見にくくなる。
こういったものを解決するために、神経科学の領域ではラスタープロット表記やインタースパイク・インターバル表記などが用いられている。
さて、SNNを遊び程度にとりあえず組んでみたが、これがどのように社会の中で活用していくかは正直研究者でも手探りな部分が多いらしい。
ディープラーニングなどで使われているCNNなどに比べて計算量が多すぎるという欠点があり、まだまだこれからの技術といえる。
しかし、SNNはCNNと比べて表現できる情報量が桁違いに多いというのはグラフを見ればよく分かると思う。
発火間隔の長さ・短さはもとより、基底状態の長さや発火にかかる時間など、0と1だけでは表現できないことが多く含まれている。
「十分な入力はあったが、直前まで基底状態だったため発火しなかった」
「入力は不足しているが、それまで頻繁に入力を受けていたため発火できた」
というケースもある。
ディープラーニング等CNNでは、確率が0.4から0.6程度のデータの取扱いに苦心していると聞くが、SNNであればそういった部分にも解を出すことができるのではないかと考えている。
SNNの研究は昔から行われており、理論上の体系はもう既に組み上がっている。だが、これまではその計算を行えるマシンがなく、まさに「机上の空論」だった。
しかしながら、近年コンピュータのスペック向上が目覚ましく、机上の空論ではなくなりつつある。その一例として、今まさにスーパーコンピュータを用いてSNNでコンピュータ上に脳の全てを再現しようという試みが行われている。
www.hpcwire.jp
大変夢がある。量子コンピュータが完成したならば、SF世界にあるような人型AIというのも実現可能性が出てくるのではないかと考えている。
夢は大きいが、結局のところ、こうしてSNNから得られた数値にどういった意味があり、どのように活用できるかがわからなければ何も始まらない。
これを打ち立てられたらブレークスルーが起きるだろう。いつどこからこういったニュースが現れるか楽しみにしたい。