無限不可能性ドライブ

『ニューラルネットワーク自作入門』に刺激されてExcelVBAでニューラルネットワークを作ってみたものの、やっぱり数学やらなきゃと思い少しずつやってきたのもあって、自分の知識の整理とかそういった感じです。

【VBA編】(順伝播)動作確認(2)

f:id:celaeno42:20181201115558p:plain

順伝播の実行

前回は順伝播の動作確認で必要な部分のコードを追加しました。
今回は、実際にデータを読み込んで順伝播の処理を行い、結果を表示させてみます。
celaeno42.hatenablog.com

データの準備

使用するデータは以前の記事で準備しています。

celaeno42.hatenablog.com
celaeno42.hatenablog.com

順伝播テストのコード

では、順伝播のテスト用コードを書いていきましょう。
流れとしては、読み込んだデータを入力層に渡し、入力層の出力結果を1つめの隠れ層に渡し、1つめの隠れ層の出力結果を2つめの隠れ層に渡し、2つめの隠れ層の出力結果を出力層に渡して、最終出力を得る。というものです。
各層はデータを受け取ると順伝播の計算を行って結果を出力するという設計にしているので、上記のように順番に層をつなげていくことでデータ入力から最終出力を得ることができるようになっています。

標準モジュール【mdlForwardTest】

'[mdlForwardTest - 標準モジュール]
Option Explicit
Option Base 1

Public Sub 順伝播テスト()
    Dim datas() As Double
    Dim r As Long
    Dim c As Long
    Dim eRow As Long
    Dim eCol As Long
    Dim label As String                 '正解
    Dim answer As String              'ニューラルネットワークが出した答え
    Dim count As Long
    Dim correctCount As Long        '正解数
    
    '[ 1 ] 入力層、隠れ層1、隠れ層2、出力層を宣言する
    '(入力層を省略した場合は cInputLayer の宣言等は不要)
    Dim cInputLayer As classInputLayer:             Set cInputLayer = New classInputLayer
    Dim cHiddenLayer1 As classHiddenLayer:      Set cHiddenLayer1 = New classHiddenLayer
    Dim cHiddenLayer2 As classHiddenLayer:      Set cHiddenLayer2 = New classHiddenLayer
    Dim cOutputLayer As classOutputLayer:        Set cOutputLayer = New classOutputLayer

    ws_Train_Result.Cells.Clear
    
    '[ 2 ] 入力データ用の配列を準備する
    With ws_Train_Data_Input
        eRow = .Cells(Rows.count, CL.DATA_LABEL).End(xlUp).Row
        eCol = .Cells(RW.DATA_START, Columns.count).End(xlToLeft).Column
        ReDim datas(eCol - 1)
    End With
    
    '[ 3 ] 隠れ層と出力層を初期化:ユニット数, 入力データの項目数, 活性化関数の種類
    Call cHiddenLayer1.Initialize(3, UBound(datas), ACT.ReLU)
    Call cHiddenLayer2.Initialize(4, 3, ACT.ReLU)
    Call cOutputLayer.Initialize(3, 4, ACT.Softmax)

    '[ 4 ] 入力データを順番に処理していく
    count = 0
    correctCount = 0
    For r = RW.DATA_START To eRow

        '[ 5 ] 入力データの配列に1つ分のデータをセットする
        For c = CL.DATA_START To eCol
            datas(c - 1) = ws_Train_Data_Input.Cells(r, c).Value
        Next
        '[ 6 ] ラベル(正解)をセットする
        label = ws_Train_Data_Input.Cells(r, 1).Value

        count = count + 1
        
        '[ 7 - 1 ] 順伝播を行う
        '----入力層を省略しない場合----
        '入力層にデータを渡す
        Call cInputLayer.DataInput(datas)
        '隠れ層1に入力層の出力値を渡して順伝播を行う
        Call cHiddenLayer1.Forward(cInputLayer.OutputDataList)
        '隠れ層2に隠れ層1の出力値を渡して順伝播を行う
        Call cHiddenLayer2.Forward(cHiddenLayer1.OutputDataList)
        '出力層に隠れ層2の出力値を渡して順伝播を行う
        Call cOutputLayer.Forward(cHiddenLayer2.OutputDataList)
        '----------------------------

        '[ 7 - 2 ] 順伝播を行う
        '----入力層を省略した場合----
'        Call cHiddenLayer1.Forward(datas)
'        Call cHiddenLayer2.Forward(cHiddenLayer1.OutputDataList)
'        Call cOutputLayer.Forward(cHiddenLayer2.OutputDataList)
        '----------------------------
        
        '[ 8 ] 出力層の出した答えのインデックスを元に Iris の種類を answer に格納する
        Select Case cOutputLayer.GetAnswerIndex
            Case IRIS.SETOSA
                answer = G.VAL_SETOSA
            Case IRIS.VERSICOLOR
                answer = G.VAL_VERSICOLOR
            Case IRIS.VIRGINICA
                answer = G.VAL_VIRGINICA
        End Select

        '[ 9 ] 結果をシートに出力する
        With ws_Train_Result
            .Cells(r, 1).Value = label
            .Cells(r, 2).Value = answer
            .Cells(r, 3).Value = (.Cells(r, 1).Value = .Cells(r, 2).Value)
            If label = answer Then
                correctCount = correctCount + 1
            End If
        End With

    Next
    
    '[ 10 ] 正解数を出力する
    With ws_Train_Result
        .Cells(1, 1).Value = count
        .Cells(1, 2).Value = correctCount
        .Cells(1, 3).Value = correctCount / count
    End With
    
End Sub

コードの解説

[ Option Base 1 ] を宣言しているので注意してください。

[ 1 ] 入力層、隠れ層1、隠れ層2、出力層を宣言する

今回のニューラルネットワークは隠れ層を2層にしているので、隠れ層として2つのクラスを宣言しています。「入力層の作成」で入力層の作成を省略した場合は「cInputLayer」の宣言は不要です。

[ 2 ] 入力データ用の配列を準備する

「訓練データ(入力)」シートのデータをもとに、データの最終行(データの件数)を調べ、また、1件分のデータの項目数からデータ格納用の配列を準備しています。

[ 3 ] 隠れ層と出力層を初期化:ユニット数, 入力データ数, 活性化関数の種類

隠れ層と出力層のユニットを生成し、それぞれの層を構築します。引数は その層のユニット数、その層への入力データの項目数、活性化関数の種類です。例えば、2層めの隠れ層は上の図を見ると、ユニット数は4つで前の層からの各ユニットへの入力数は3つになっています。
なお、入力層については特に初期化は不要です。

[ 4 ] 入力データを順番に処理していく

「訓練データ(入力)」シートのデータを順番に処理していきます。

[ 5 ] 入力データの配列に1つ分のデータをセットする

データ格納用の配列 datas() に1つ分(1行分)のデータを格納していきます。
1列目はラベルなので、入力用のデータとしては2列目からです。
具体的には「がくの長さ」,「がくの幅」,「花弁の長さ」,「花弁の幅」の4つです。
f:id:celaeno42:20190303225656p:plain

[ 6 ] ラベル(正解)をセットする

ラベルを変数に格納します。

[ 7 ] 順伝播を行う

各層の Forward() を呼び出して順伝播を実行します。引数として前の層の出力のリストを渡します。
入力層を省略しなかった場合と省略した場合で若干異なります。

[ 8 ] 出力層の出した答えのインデックスを元に Iris の種類を answer に格納する

[ 7 ] で実行した順伝播によって計算された答えのインデックスを元に Iris の種類「Iris-setosa」,「Iris-versicolor」,「Iris-virginica」を answer 変数に格納します。

[ 9 ] 結果をシートに出力する

ラベル、順伝播で出した答え、ラベルと答えがあっているかどうか(= True or False)をデータごとに「訓練結果」シートに出力します。
あわせて、正解数をカウントしています。

[ 10 ] 正解数を出力する

最後にデータ数、正解数、正解率を出力します。

テスト実行

では、「順伝播テスト()」プロシージャを実行してみましょう。
実行したら「訓練結果」シートを確認してみます。

f:id:celaeno42:20190208235105p:plain

1行目は左から、データ数、正解数、正解率です。32.6% くらいしか正解してないですね。というのも、このニューラルネットワークはまだぜんぜん学習していない(パラメータをランダムに決めただけ)なので、あてずっぽうに解答しているからです。本来はここから学習してだんだんと精度を上げていくのですが、まだ学習の機能(逆伝播)は実装していないので、今回は学習済みのパラメータをもちいて学習済みの状態でもテストをしてみようと思います。なお、パラメータはランダムで設定されるので、実行のたびに結果は変わります。当然、必ずしも上記結果と同じになるわけではありません。

コードの追加

シートから学習済みのパラメータを読み込むために「順伝播テスト()」プロシージャに以下のようにコードを追加します。

    '[ 3 ] 隠れ層と出力層を初期化:ユニット数, 入力データ数, 活性化関数の種類
    Call cHiddenLayer1.Initialize(3, UBound(datas), ACT.ReLU)
    Call cHiddenLayer2.Initialize(4, 3, ACT.ReLU)
    Call cOutputLayer.Initialize(3, 4, ACT.Softmax)
    
'----この部分を追加----
    'Initializeでいったんランダムな重みが設定されるがLoadWeightすることで、上書きされる
    Call cHiddenLayer1.LoadWeight(ws_WI_H1)
    Call cHiddenLayer2.LoadWeight(ws_WI_H2)
    Call cOutputLayer.LoadWeight(ws_WI_Out)
'--------ここまで--------

    '[ 4 ] 入力データを順番に処理していく
    count = 0
    correctCount = 0
    For r = RW.DATA_START To eRow


読み込み用パラメータの設定

学習済みのパラメータをそれぞれの層のパラメータ初期値格納用シートに設定します。1行が1ユニット分のパラメータで、1列目がバイアス、2列目以降が重みです。
今回設定する具体的な値は以下の通りです。


隠れ層1層め用初期パラメータ格納シート【wi_h1】
f:id:celaeno42:20190209004626p:plain


隠れ層2層め用初期パラメータ格納シート【wi_h2】
f:id:celaeno42:20190209005538p:plain


出力層用初期パラメータ格納シート【wi_out】
f:id:celaeno42:20190209005639p:plain

学習済みパラメータで再度テストを実行

では再度「順伝播テスト()」プロシージャを実行してみましょう。
実行したら「訓練結果」シートを確認してみます。

f:id:celaeno42:20190209010031p:plain

129 個中 126 個が正解で、正解率は 97.7% までになっていますね!先ほどの 32.6% とは大違いです。

このように、最適と思われるパラメータを調整で求めていくことを「学習する」といいます。学習するには逆伝播をおこなってパラメータを調整していく必要があります。

次回以降はいよいよ逆伝播を実装してニューラルネットワークが学習できるようにしていきます。


f:id:celaeno42:20181212233850p:plain