エルマンネットワーク

というのを実装して見た。リカレントニューラルネットワーク(RNN)の一形態?

単純再帰型ネットワーク
http://www.cis.twcu.ac.jp/~asakawa/waseda2002/elman.pdf

import numpy as np

def sigmoid(x):
     return np.tanh(x)

def dsigmoid(x):
     return 1.0-x*x

class ElmanNet:
    def __init__(self , numIn , numHidden , numOut):
       self.input = np.ones( numIn )
       self.output = np.ones( numOut )
       self.hidden = np.ones( numHidden )
       self.context = np.zeros( numHidden )
       self.weight_in_hid = np.random.uniform(low=-0.5,high=0.5,size=(numIn , numHidden) )
       self.weight_hid_out = np.random.uniform(low=-0.5,high=0.5,size=(numHidden,numOut) )
       self.weight_cont_hid = np.random.uniform(low=-0.5,high=0.5,size=(numHidden,numHidden) )
    def propagate_forward(self , data):
       self.input = np.copy(data)
       self.context = np.copy(self.hidden)
       self.hidden = np.vectorize(sigmoid)(np.dot(self.input , self.weight_in_hid) + np.dot(self.context , self.weight_cont_hid))
       self.output = np.vectorize(sigmoid)(np.dot(self.hidden , self.weight_hid_out))
       return self.output
    def propagate_backward(self , target , learnRate=0.1):
       error_out = (target - self.output) * np.vectorize(dsigmoid)(self.output)
       error_hidden = np.dot(error_out , self.weight_hid_out.T) * np.vectorize(dsigmoid)(self.hidden)
       self.weight_hid_out  += np.array( learnRate * np.matrix(self.hidden).T * np.matrix(error_out) )
       self.weight_in_hid   += np.array( learnRate * np.matrix(self.input).T * np.matrix(error_hidden) )
       self.weight_cont_hid += np.array( learnRate * np.matrix(self.context).T * np.matrix(error_hidden) )
    def train(self , samples , r=1500):
       L = len(samples)
       for _ in xrange(r):
           for (inp,outp) in samples:
               self.propagate_forward(inp)
               self.propagate_backward(outp)
    def predict(self , data):
       return self.propagate_forward(data)


if __name__=="__main__":
   def tobit(n,nbits=6):
      return [int(n==i) for i in xrange(nbits)]
   net = ElmanNet(6,15,6)
   samples = [(tobit(0),tobit(1)),(tobit(1),tobit(2)),(tobit(2),tobit(3)),(tobit(3),tobit(4)),
              (tobit(4),tobit(5)),(tobit(5),tobit(4)),(tobit(4),tobit(3)),(tobit(3),tobit(2)),
              (tobit(2),tobit(1)),(tobit(1),tobit(0)),(tobit(0),tobit(1)),(tobit(1),tobit(2)),
              (tobit(2),tobit(3)),(tobit(3),tobit(4)),(tobit(4),tobit(5)),(tobit(5),tobit(4))]
   net.train(samples)
   out = net.predict(tobit(4))
   print(out)
   out = net.predict(out)
   print(out)
   print net.predict(tobit(2))

例として、0,1,2,3,4,5,4,3,2,1,0,1,2,3,4,5,...という時系列を学習させている。一応、値が上昇中か下降中か覚えておく必要があるけど、勝手に覚えておいてくれるという寸法。0,1,2,3,4,5は、[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,1,0,0,0],...にエンコードしてる。3bitで[0,0,0],[0,0,1],[0,1,],[0,1,1],...とかやった方がメモリには優しいけど、予測精度がでない


一応、上の計算結果は(乱数で初期化している部分があるので、結果は毎回異なるけど)例えば

[ 0.05445429 -0.0483922  -0.14895301  0.98319809  0.09161327 -0.09690022]
[ 0.05653943  0.13345083  0.98843438 -0.28019387 -0.08493135  0.05609208]
[-0.09952735  0.98482479  0.22479163 -0.13254797 -0.08240431  0.20785037]

とかなって、[0,0,0,1,0,0],[0,0,1,0,0,0],[0,1,0,0,0,0]に近い値が返ってくる


有限オートマトンとか、ある種の文法構造を学習することができるっぽいけど、他の文法推論アルゴリズムと比べて、大規模な文法の推論には向かないらしい