SEワンタンの独学備忘録

IT関連の独学した内容や資格試験に対する取り組みの備忘録

【機械学習】入門⑬ 2層ニューラルネットワークモデルをPythonで実装①

前回考えたニューラルネットワークモデルをPythonで実装することによりイメージをつかみます。

イメージ概要

前回考えたニューラルネットワークモデルに少し修正をしたイメージです。

f:id:wantanBlog:20200518214645p:plain


データの準備の実装

データの説明

まずはニューラルネットワークにかけるデータの準備を行います。
基本データは以前の分類に用いた「アヤメの品種データ」を使います。
※データ自体はなんでもよいです。

■説明変数

0 sepal length (cm) がく片の長さ
2 petal length (cm) 花弁の長さ

今回はがく片の長さと花弁の長さを使ってみます。

■目的変数
3クラスに分類するという点だけ抑えておけばよいです。

0 setosa(ヒオウギアヤメ)
1 versicolor(ブルーフラッグ)
2 virginica(バージニカ)
データの保存

scikit-learnからデータを抜いてきてファイルに保存するところまで。

# データ準備
import numpy as npy
import matplotlib.pyplot as plt
%matplotlib inline
# アヤメ品種データの読み込み
from sklearn.datasets import load_iris

iris_data = load_iris()

# 説明変数
X_array = iris_data.data
# カラムデータを取得
X0=X_array[:,0]
X1=X_array[:,3]

# 目的変数
t_array = iris_data.target
T=t_array

N=len(X0)
petal_data=npy.zeros((N,2))
T3 = npy.zeros((N,3), dtype=npy.uint8)

#データの設定
for i in range(N):
    petal_data[i]=[X0[i], X1[i]]
    
    # 0[1,0,0] 1[0,1,0] 2[0,0,1]
    if T[i] == 0:
        T3[i]=[1,0,0]
    elif T[i] == 1:
        T3[i]=[0,1,0]
    else:
        T3[i]=[0,0,1]


X_range0=[min(X0)*0.9,max(X0)*1.1]
X_range1=[min(X1)*0.9,max(X1)*1.1]

# データをclassdata3.npzファイルに保存する
npy.savez('neural_rawdata.npz',X=petal_data,T3=T3,X_range0=X_range0,X_range1=X_range1,X_n=N)
訓練データとテストデータに分割

今回はデータを訓練データと検証のためのテストデータに分けてみます。
データセットは150個のデータが50個ごとにクラス1、2、3の順にならんでいるので各クラスから25個をそれぞれ訓練データとテストデータに分割しました。

・実装

# 生データファイルから取り出す
sample_data = npy.load('neural_rawdata.npz')
# 入力値の設定
X=sample_data['X']
# がく片の長さの表示範囲設定
X_range0=sample_data['X_range0']
# 花弁の長さの表示範囲設定
X_range1=sample_data['X_range1']
# クラス(答え)の設定
T3=sample_data['T3']

# データの並び替え
X1 = X[:50,:]
X2 = X[50:100,:]
X3 = X[100:150,:]

npy.random.shuffle(X1)
npy.random.shuffle(X2)
npy.random.shuffle(X3)

X_test=npy.r_[X1[:25,:], X2[:25,:], X3[:25,:]]
X_train=npy.r_[X1[25:50,:],X2[25:50,:],X3[25:50,:]]
T_test=npy.r_[T3[:25,:], T3[50:75,:], T3[100:125,:]]
T_train=npy.r_[T3[25:50,:],T3[75:100,:],T3[125:150,:]]

npy.savez('neural_data.npz',X_train=X_train, T_train=T_train,X_test=X_test,T_test=T_test,X_range0=X_range0,X_range1=X_range1)
データのプロット

分割した訓練データとテストデータをグラフ上にプロットします。

・実装例

import matplotlib.pyplot as plt
%matplotlib inline

# データの表示
def Show_data(x,t):
    wk,n=t.shape
    c=[[0,0,0],[.5,.5,.5],[1,1,1]]
    for i in range(n):
        plt.plot(x[t[:,i]==1,0],x[t[:,i]==1,1],linestyle='none',marker='o',markeredgecolor='black',color=c[i],alpha=0.8)
    plt.grid(True)

plt.figure(1,figsize=(8,3.7))
plt.subplot(1,2,1)
Show_data(X_train,T_train)
plt.xlim(X_range0)
plt.ylim(X_range1)
plt.title('Training Data')
plt.subplot(1,2,2)
Show_data(X_test,T_test)
plt.xlim(X_range0)
plt.ylim(X_range1)
plt.title('Test Data')

plt.show()

・出力結果

f:id:wantanBlog:20200517213423p:plain

訓練データとテストデータがそれぞれ違うデータで表示されていればとりあえずOK。

ニューラルネットワークの実装

シグモイド関数の実装

ニューラルネットワーク内で使用されるシグモイド関数を先に実装しておきます。

・シグモイド関数
f:id:wantanBlog:20200518003557p:plain

・実装例

# シグモイド関数
def Sigmoid(x):
    y=1/(1+npy.exp(-x))
    return y
ニューラルネットワークの実装
# ニューラルネットワーク(wv重み、M中間層ノード数、K出力層ノード数、x入力層の入力値)
def FNN(wv, M, K, x):
    # Nデータ数 Dデータの入力次元
    N, D = x.shape
    # w 入力層⇒中間層の重み
    w = wv[:M * (D+1)]
    w = w.reshape(M,(D+1))
    # v 中間層⇒出力層の重み
    v = wv[M*(D+1):]
    v = v.reshape((K,M+1))
    # b 中間層の入力総和
    b = npy.zeros((N,M+1))
    # z 中間層の出力値
    z = npy.zeros((N,M+1))
    # a 出力層の入力総和
    a = npy.zeros((N,K))
    # y 出力層の出力値
    y = npy.zeros((N,K))
    
    # データごとに中間層、出力層の計算を行う
    for n in range(N):
        # 中間層の計算(シグモイド関数)
        for m in range(M):
            b[n,m] = npy.dot(w[m,:],npy.r_[x[n,:],1])
            z[n,m] = Sigmoid(b[n,m])
        # ダミーニューロン
        z[n,M]=1
        u=0
        # 出力層の計算(ソフトマックス関数)
        for k in range(K):
            a[n,k] = npy.dot(v[k,:],z[n,:])
            u = u + npy.exp(a[n,k])
        for k in range(K):
            y[n,k] = npy.exp(a[n,k])/u
    return y,a,z,b

# テスト実行
WV = npy.ones(15)
M = 2
K = 3
FNN(WV,M,K,X_train[:2,:])

WV(中間層と出力層の重み)と中間層のノード数(M)、出力層のノード数(K)、入力値(X)をニューラルネットワークに渡して戻り値として各層の出力値と入力総和を返します。

中間層での計算は先ほど実装したシグモイド関数を適用します。

出力層の計算にはソフトマックス関数を適用するので、ニューラルネットワーク内で合計値(u)を求めて各値を割ることで出力値とします。
f:id:wantanBlog:20200518005737p:plain

・テスト実行

テスト実行なので、重みは全て1としています。
入力値については訓練データの頭2つのデータを適用しています。

・出力結果

(array([[0.33333333, 0.33333333, 0.33333333],
[0.33333333, 0.33333333, 0.33333333]]),
array([[2.9955243 , 2.9955243 , 2.9955243 ],
[2.99396317, 2.99396317, 2.99396317]]),
array([[0.99776215, 0.99776215, 1. ],
[0.99698158, 0.99698158, 1. ]]),
array([[6.1, 6.1, 0. ],
[5.8, 5.8, 0. ]]))

注目すべきは最終的な出力結果となる「y」ですが、テスト実行で重みパラメータが全て1なので確率が全ての「0.33」となっています。
なので、現時点では有効に活用できておらず、重みの設定の重要性が分かります。

重みの設定ができていないとニューラルネットワークがあっても活用することはできないので、次回は最適な重みを設定することを検討します。

・今回のソース
python_dev/Neural_net1_prod.ipynb at master · wantanblog/python_dev · GitHub