多層パーセプトロンをPythonで実装してみた-1-
A Neural Network Playgroundのデモが素晴らしかったので
Pythonを使ってそれっぽく動く物を作ってみた。
開発環境
- Visual studio 2015 community
- PTVS
- Python3.5.2
- numpy 1.11.1
- scipy 0.18.0
- scikit-learn 0.18
- matplotlib 1.5.3
多層パーセプトロン
ニューラルネットワークはまったくの初心者だったので色々調べると
多層パーセプトロンを使えばできそうなのでそれについて調べた。
順方向の計算は簡単
逆誤差伝播法はかなり難しい・・・
出力層はなんとか自分で計算できたが隠れ層は無理。
↓も正解か自信がない(手書き文字認識は問題なく出来たから大丈夫だとは思う)
+出力層
+隠れ層
学習は1データ(x,yの値と期待値y)毎に更新するオンライン学習
重み更新は学習率固定
ニューロンレイヤークラス
まず基本となるレイヤークラスを作成して
それを継承して出力層クラスと隠れ層クラスを作成した。
playgroundのデモは値の範囲が-1~+1までなので活性化関数はtanh(x)を使った。
※11/1複数の出力層に対応する様修正
# -*- coding: utf-8 -*- import numpy as np class neuronLayer(object): """レイヤー基本クラス""" def __init__(self,input_num,neuron_num,output_num,e,bias=None): self._input_num = input_num self._neuron_num = neuron_num self._ouput_num = output_num self._e = e if bias is None: self._isBias = False else: self._isBias = True #biasを追加する拡張 self._input_num += 1 self._bias = np.ones([self._neuron_num,1]).reshape(self._neuron_num,1) #初期化 self._x = np.zeros(self._input_num*self._neuron_num).reshape(self._neuron_num,self._input_num) self._s = np.zeros(self._neuron_num) self._y = np.zeros(self._ouput_num*self._neuron_num).reshape(self._neuron_num,self._ouput_num) self._delWdelE = np.zeros(self._input_num*self._neuron_num).reshape(self._neuron_num,self._input_num) self._delSdelE = np.zeros(self._ouput_num*self._neuron_num).reshape(self._neuron_num,self._ouput_num) #重みランダム初期化 #self._weight = np.random.rand(self._neuron_num,self._input_num)*2-1.0 #http://deeplearning.net/tutorial/mlp.html self._weight = np.asarray( np.random.uniform( low=-np.sqrt(6. / (self._input_num + self._ouput_num)), high=np.sqrt(6. / (self._input_num + self._ouput_num)), size=(self._neuron_num, self._input_num) ) ) def activationFunction(self,x): return self.tanh(x) def dActivationFunction(self,x): return self.dtanh(x) def sigmoid(self,x): return(1.0/(1.0+np.exp(-x))) def dsigmoid(self,x): return self.sigmoid(x)*(1-self.sigmoid(x)) def tanh(self,x): return np.tanh(x) def dtanh(self,x): return 1.0-self.tanh(x)*self.tanh(x) def ReLu(self,x): return x * (x > 0) def dReLu(self,x): return 1.0 * (x > 0) def foward(self,x): #biasが有効のときはbiasを追加 if(self._isBias == True): self._x = np.hstack((x,self._bias)) else: self._x = x # s = Σ(x*w) if(self._neuron_num == 1): self._s = (self._x * self._weight).sum() else: self._s = (self._x * self._weight).sum(axis=1)+np.zeros(self._ouput_num).reshape(self._ouput_num,1) self._y = self.activationFunction(self._s) def updateWeight(self): #print(self._weight) self._weight -= self._e * self._delWdelE def getY(self): return self._y def getWeight(self): if(self._isBias == True): #前段はbiasの重みが不要なので削除して返す return np.delete(self._weight,self._input_num-1,1) else: return self._weight def getDelSdelE(self): return self._delSdelE class inputLayer(object): def __init__(self,input_num,neuron_num,output_num): self._input_num = input_num self._neuron_num = neuron_num self._ouput_num = output_num self._x = np.zeros(self._ouput_num).reshape(self._ouput_num,1) self._y = np.zeros(self._input_num*self._ouput_num).reshape(self._input_num,self._ouput_num) def foward(self,x): self._y = x + self._x def getY(self): return self._y class outputLayer(neuronLayer): def backprop(self,y): if(self._neuron_num==1): self._delSdelE = (self._y -y) * self.dActivationFunction(self._s) else: self._delSdelE = (self._y -y) * self.dActivationFunction(self._s) self._delSdelE = self._delSdelE.T self._delWdelE = self._delSdelE * self._x class hiddenLayer(neuronLayer): def backdrop(self,delSdelE,wn): if(self._ouput_num==1): self._delSdelE = (delSdelE * wn).sum() * self.dActivationFunction(self._s) self._delSdelE = self._delSdelE.reshape(self._neuron_num,1) else: self._delSdelE = (delSdelE * wn).sum(axis=0).reshape(1,self._neuron_num) self._delSdelE *= self.dActivationFunction(self._s[0,:]) self._delSdelE = self._delSdelE.T self._delWdelE = self._delSdelE * self._x
実際に分類したりMNISTで手書き文字認識したりは次の記事に書く。