Python R データ解析 備忘録

統計データ解析研究室に所属する大学院生です。日々やったことをいろいろまとめます(目標)。

pythonで多層ニューラルネット(Deep Learning)の実装

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)

「深層学習」をchapter4まで読んだ知識で,多層のニューラルネットpythonで実装してみました.1〜3chapterまではざっと読んで,ニューラルネットの構造や式の表記の仕方を頭に入れて,chapter4で誤差逆伝播法をしっかり読んだという感じです.誤差逆伝播法を理解したところで,基本的な多層ニューラルネットワークは実装出来そうだったので試しにやってみました.pythonでコードを書いたのは初めてだったのでかなり時間がかかってしまいました....

今回使ったデータはRから書き出したirisデータです.後から知った話ですがpythonにもirisのデータセットがあるらしいです.
コードの改善点はかなりあると思います...
でもとりあえずirisデータを学習させて正答率90%以上にはなりました.

#coding:utf-8
import numpy as np
import pandas as pd
from numpy import linalg as la
import math
import matplotlib.pyplot as plt
import random

##########設定################
#層と各層のユニットの数を指定
Units=np.array([4,50,50,50,3])
print(Units)
Layer=len(Units)
n=1000 #何回ミニバッチを使うか
n2=10 #ミニバッチのデータ数
f_type=0 #活性化関数の種類 0:シグモイド関数, 1:正規化線形関数
epsilon=0.001
ave=0
sigma=1
##############################

必要なモジュールをインポートして,ニューラルネットの構造と学習の設定をする.
Units:ニューラルネットの構造
今回の場合,5層でユニット数は入力層から順番に4,50,50,50,3の構造のニューラルネット
n:学習回数
n2:ミニバッチに使用するデータの個数
f_type:使用する活性化関数(0:シグモイド関数, 1:正規化線形関数)
epsilon:パラメータの更新率(「深層学習」によるとミニバッチのデータ数を増やすと更新率を大きく出来るらしい)
ave,sigma:パラメータWの初期値に使用する正規乱数の平均,分散

#############活性化関数を定義
def f(x,i,j):#i:微分, j:種類
if j==0:
ans=1/(1+np.exp(-x))#ロジスティック曲線
ans2=ans*(1-ans)#ロジスティック微分
if j==1:
ans2=np.array(x >=0,dtype=float)
ans=ans2*x
if i==0:
return(ans)
else:
return(ans2)

活性化関数を定義.パラメータ推定時に微分したものも必要なので,それも定義した.

#############ソフトマックス関数を定義
def softmax(x):
x1=np.exp(x)
x2=np.sum(x1,axis=0)
return(x1/x2)

出力にはソフトマックス関数を用いた.これにより各ユニットからの出力値の合計が1になるので,出力値を確立と見立てることが出来る.

#############ZUを求める関数の定義
def UZ(Layer,X,W):
U=range(Layer)
Z=range(Layer)

for a in range(Layer):
if a==0:
U[0]=X
Z[0]=X
else:
U[a]=W[a].dot(Z[a-1])+B[a].dot( np.ones((X.shape)[1])[np.newaxis,:])
if a !=Layer-1:
Z[a]=f(U[a],0,f_type)
else:
Z[a]=softmax(U[a])
return([U,Z])

入力した値から出力の値を求める関数を定義

############irisデータの準備
iris=pd.read_csv("iris.csv", header=None)
#numpyの形式にする.
iris_N=np.array(iris.ix[1:,1:])
for i in range(4):
iris_N[:,i]=iris_N[:,i].astype(np.float64)

t=np.zeros((150,3))
iris_N=np.c_[iris_N,t]

#答えデータを行列で表す.
for i in range(len(iris_N)):
if iris_N[i,4]=="setosa":
iris_N[i,5]=iris_N[i,5]+1.0
if iris_N[i,4]=="versicolor":
iris_N[i,6]=iris_N[i,6]+1.0
if iris_N[i,4]=="virginica":
iris_N[i,7]=iris_N[i,7]+1.0

np.random.shuffle(iris_N)

test=np.array(iris_N[:50,:])
train=iris_N[50:,:]

Rのirisデータをwrite.csv()で書き出したデータ(iris.csv)を読み込んだ.5列のデータで,そのうちの1列が答えのデータ.なので,入力層のユニット数は4つ.
答えデータは行列で与える必要があるので,setosaだったら(1,0,0),versicolorだったら(0,1,0),virginicaだったら(0,0,1)に変換した(かなり頭の悪いコードなので後で要改善).これより出力層のユニットは3つ.


######### 計算########################
for r in range(1):
W=range(Layer)
B=range(Layer)
for a in range(Layer):
if a==0:
continue
W[a]=np.random.normal(ave,sigma,(Units[a],Units[a-1]))
B[a]=np.random.normal(ave,sigma,(Units[a],1))

rate2=np.zeros((2,n))
for i in range(n):
index = random.sample(range(100), n2)#抽出する添字を取得
X=np.array(train[index,0:4].T,dtype=float)
d=np.array(train[index,5:8].T,dtype=float)

uz=UZ(Layer,X,W)
U=uz[0]
Z=uz[1]

#デルタとWの偏微分を求める
N=(X.shape)[1]*1.0
delta=range(Layer)
Wd=range(Layer)
Bd=range(Layer)
for a in range(Layer-1,0,-1):
if a==max(range(Layer)):
delta[a]=Z[a]-d
else:
delta[a]=f(U[a],1,f_type)*((W[a+1].T).dot(delta[a+1]))
#W+Bの更新
Wd[a]=(1/N)*((delta[a]).dot(Z[a-1].T))
W[a]=W[a]-(epsilon*Wd[a])
Bd[a]=(1/N)*(delta[a].dot(np.ones((X.shape)[1])[:,np.newaxis]))
B[a]=B[a]-(epsilon*Bd[a])

説明は次回詳しくしますが,「深層学習」で定義されているデルタを求めることにより,誤差関数E(W)のWでの偏微分を求め,確率的勾配降下法によるパラメータWの更新を行う.

############結果の表示############

U=range(Layer)
Z=range(Layer)
X=np.array(test[:,0:4].T,dtype=float)
d=np.array(test[:,5:8].T,dtype=float)

uz=UZ(Layer,X,W)
U=uz[0]
Z=uz[1]

res=np.r_[Z[Layer-1],d]
a=res[0:3,:]
b=res[3:6,:]
a2=np.array(np.where(np.max(a,axis=0)==a))
b2=np.array(np.where(b==1))
a3=pd.DataFrame(a2)
b3=pd.DataFrame(b2)
aix=np.array(((a3.T).sort(1)).index)
bix=np.array(((a3.T).sort(1)).index)
correct=np.array(np.where(a2[0,aix]==b2[0,bix]))
correct_len=correct.shape[1]
rate=(correct_len*1.)/(res.shape[1])
rate2[0,i]=i
rate2[1,i]=rate

plt.plot(rate2[0],rate2[1])
plt.show()

あらかじめ分けておいてテストデータで正答率を求める.


ミニバッチ一回の更新ごとにテストデータの正答率を求めました.縦軸が正答率,横軸が学習回数です.
f:id:sakaba-kenichiro:20160405200949p:plain
学習回数が200回を超えたところから,正答率は上がったり下がったり...
何層で各層のユニット数を何個にすればいいのか分かりません...