無限不可能性ドライブ

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

【VBA編】(準備)クラスについて(2)

クラスについて その2

前回に引き続きクラスについてもう少し見ていきましょう。

インスタンス(オブジェクト)

クラスを使うにあたっての利点の一つは1つのクラスから複数のインスタンス(実体)を作れることではないでしょうか。
特に同じようで若干異なる処理をさせたいときなどに役に立つと思います。

次のような処理をしたい場合、クラスを使わないで書くとしたらどうなるでしょうか。
f:id:celaeno42:20181105230935p:plain

[処理結果]
f:id:celaeno42:20181105231040p:plain

ひとまずこのようにしてみました。
各種類の次の書き込み行をそれぞれの変数で制御しているのが煩わしい感じがします。

'[標準モジュール]
Option Explicit

Public Enum CL
    Shokuhin = 5
    Bunbougu = 7
    Kaden = 9
End Enum

Public Sub Main()
    Dim r As Long
    Dim eRow As Long
    Dim shurui As String
    Dim rwShokuhin As Long
    Dim rwBunbougu As Long
    Dim rwKaden As Long
    
    Cells(1, CL.Shokuhin).Value = "食品"
    Cells(1, CL.Bunbougu).Value = "文房具"
    Cells(1, CL.Kaden).Value = "家電"
    
    rwShokuhin = 2
    rwBunbougu = 2
    rwKaden = 2
    
    eRow = Cells(Rows.Count, 1).End(xlUp).Row

    For r = 2 To eRow
        shurui = Cells(r, 1).Value

        Select Case shurui
            Case "食品"
                Cells(rwShokuhin, CL.Shokuhin).Value = Cells(r, 2).Value
                rwShokuhin = rwShokuhin + 1
            Case "文房具"
                Cells(rwBunbougu, CL.Bunbougu).Value = Cells(r, 2).Value
                rwBunbougu = rwBunbougu + 1
            Case "家電"
                Cells(rwKaden, CL.Kaden).Value = Cells(r, 2).Value
                rwKaden = rwKaden + 1
        End Select

    Next

    MsgBox "終了しました。"    

End Sub

クラスを使って書いてみます。

'[クラスモジュール - classPlot]
Option Explicit

Dim mShurui As String
Dim mRow As Long
Dim mCol As Long

'初期化処理
'自分の担当する種類を覚えて、自分の列の1行目に表示する
'[引数] <- aShurui / String:自分の担当する種類, aCol / Long:表示先列
Public Sub Initialize(ByRef aShurui As String, ByRef aCol As Long)
    mShurui = aShurui
    mRow = 2
    mCol = aCol
    
    Cells(1, mCol).Value = mShurui
End Sub

'品目追加処理
'表示先セルに品目を表示して表示先行をインクリメントする
'[引数] <- aItem / String:品目
Public Sub AddItem(ByRef aItem As String)
    Cells(mRow, mCol).Value = aItem
    mRow = mRow + 1
End Sub

'自分の種類を返す
Public Property Get Shurui() As String
    Shurui = mShurui
End Property

「初期化処理」では、自分の担当する「種類」と「表示先の列」を引数として取っています。
「種類」と「列」を変数に格納したら、1行目に種類を表示します。
「品目」の表示は2行目からなので、mRow に 2 をセットしています。
なお、今回は 「Class_Initialize()」での処理は不要と判断して定義していません。

「品目追加処理」では、「品目」を引数として取り、表示先のセルに表示しています。
その後、行を更新し次の表示に備えています。

あと、呼び出し元で利用するために、「種類」を返す Property Get を定義しています。
今回は、「種類」の設定は「初期化処理」のみで行うので、Property Let は定義しませんでした。

では、呼び出し元を見てみましょう。

'[標準モジュール]
Option Explicit

Public Sub Main_class()
    Dim r As Long
    Dim eRow As Long
    Dim item As String
    
    Dim cShokuhin As classPlot
    Dim cBunbougu As classPlot
    Dim cKaden As classPlot
    
    Set cShokuhin = New classPlot
    Set cBunbougu = New classPlot
    Set cKaden = New classPlot
    
    '種類と表示先列を引数として振り分け担当(オブジェクト)を作る
    Call cShokuhin.Initialize("食品", 5)
    Call cBunbougu.Initialize("文房具", 7)
    Call cKaden.Initialize("家電", 9)
    
    eRow = Cells(Rows.Count, 1).End(xlUp).Row
    
    For r = 2 To eRow
        item = Cells(r, 2).Value
        Select Case Cells(r, 1).Value
            Case cShokuhin.Shurui
                Call cShokuhin.AddItem(item)
            Case cBunbougu.Shurui
                Call cBunbougu.AddItem(item)
            Case cKaden.Shurui
                Call cKaden.AddItem(item)
        End Select
    Next
    
    MsgBox "終了しました。"
    
End Sub

「食品」「文房具」「家電」を担当する「cShokuhin」「cBunbougu」「cKaden」を宣言し、
それぞれのインスタンスを生成しています(New しています)。
それぞれに該当の引数を与えて「初期化処理」をした後、A列を順番に見ていって
該当するインスタンスの「AddItem()」を呼び出して、「品目」を表示しています。

先ほどのコードと異なる点は、「品目」を表示する際に、呼び出し元では「何行目に表示するか」を一切気にしていないというところです。
また、列についても、「初期化処理」で設定してしまえば、以降は気にする必要がありません。

クラスを使うとコードが長くなる場合がありますが、必要な設定や処理はクラスに任せて、
呼び出し側では「あとはお願い」的にコードが書けるので考慮すべきことが少なくなります。

ということは、バグが混入する余地を減らすことにもつながります。

配列への格納

複数のインスタンスを作る際、上記の例のようにそれぞれで変数を宣言してもいいのですが、
宣言すべき変数が多くなる場合や、そもそもいくつ変数が必要になるかがわからない場合があるかと思います。
そういった場合はインスタンスを配列に格納すると便利です。

'[クラスモジュール - classPlayer]
Option Explicit

Dim mIndex As Long
Dim mColor As Long
Dim mBaseColor As Long
Dim mRow As Long
Dim mCol As Long

Private Sub Class_Initialize()
    mRow = 1
    mBaseColor = Cells(1, 1).Interior.Color
End Sub

Public Sub Initialize(ByRef aIndex As Long, ByRef aCol As Long)
    mIndex = aIndex
    mColor = Cells(1, aCol).Interior.Color
    mCol = aCol
End Sub

Public Property Get MyIndex() As Long
    MyIndex = mIndex
End Property

Public Property Get MyRow() As Long
    MyRow = mRow
End Property

Public Property Get MyColumn() As Long
    MyColumn = mCol
End Property

Public Function GoNext() As Boolean
    Dim res As Boolean
    Dim offsetR As Long
    Dim offsetC As Long
    
    res = True
    offsetR = 0
    offsetC = 0
    
    If Cells(mRow, mCol + 1).Interior.Color <> mBaseColor And Cells(mRow, mCol + 1).Interior.Color <> mColor Then
        offsetC = 1
    ElseIf Cells(mRow, mCol - 1).Interior.Color <> mBaseColor And Cells(mRow, mCol - 1).Interior.Color <> mColor Then
        offsetC = -1
    ElseIf Cells(mRow + 1, mCol).Interior.Color <> mBaseColor And Cells(mRow + 1, mCol).Interior.Color <> mColor Then
        offsetR = 1
    Else
        res = False
    End If
    
    If res Then
        mRow = mRow + offsetR
        mCol = mCol + offsetC
        
        Application.Wait [Now()+"00:00:00.1"]
        Cells(mRow, mCol).Interior.Color = mColor
    End If
    
    GoNext = res
End Function

今回はクラスモジュールのコード説明は省略します。

呼び出し側はこのようにしています。

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

Public Sub Main()
    Dim cPlayers() As classPlayer
    Dim i As Long
    Dim flagNext As Boolean
    
    ReDim cPlayers(4)
    
    For i = 1 To UBound(cPlayers)
        Set cPlayers(i) = New classPlayer
        Call cPlayers(i).Initialize(i, getColumn(i))
    Next
    
    For i = 1 To UBound(cPlayers)
        Do
            flagNext = cPlayers(i).GoNext
        Loop Until flagNext = False
        Cells(cPlayers(i).MyRow, cPlayers(i).MyColumn).Value = cPlayers(i).MyIndex
    Next
    
End Sub

Private Function getColumn(ByRef aIndex As Long) As Long
    Dim res As Long
    
    Select Case aIndex
        Case 1
            res = 2
        Case 2
            res = 6
        Case 3
            res = 10
        Case 4
            res = 14
    End Select
    
    getColumn = res
    
End Function

まずは、classPlayer 型の配列 cPlayers() を宣言しています。
素数はここで設定してもいいのですが、今回は ReDim で設定しています。

ひとつめの For - Next ですが、
[ Set cPlayers(i) = New classPlayer ] とすることによって配列の要素ごとにインスタンスを格納しています。
そのあとで [ Call cPlayers(i).Initialize() ] を実行して、インスタンスの初期化を行っています。

次の For - Next では、配列から取り出したインスタンスを利用して処理を行っています。

このように配列に格納することで、取り回しを楽にすることができます。
(実際、この後で書いていくニューラルネットワークのコードでは、この手法を利用しています。)

ちなみに [ Option Base 1 ] で配列のインデックスを 1 始まりにしているのは、
今回のコードに都合がいいようにそうしているだけです。


上記コードはExcelシートにこのようなあみだくじを作成して実行してみてください。
色は同じでなくても構いません。
(実行前に復帰用にシートをコピーしておくことをお勧めします。)
f:id:celaeno42:20181108230439p:plain


これでひとまずクラスの説明は終わります。

クラスを使えるようにしましょう

VBAはクラスを使わなくてもコードを書くことは可能です。
しかし、クラスを使うことで、より簡単に、便利に、安全にコードを書くことができるようになります。
もちろん、適材適所がありますので、なんでもかんでもクラスにすればいいというわけではありません。
ただ、必要な時にクラスが使えるようにしておくことで、手持ちの武器が増え、表現の幅が広がります。
クラスを使うコードは使わないコードとちょっと発想の転換が必要というか頭の使い方が異なると思うので、はじめはなかなか慣れないかもしれません。
そういった場合はちょっとVBAから離れて「オブジェクト指向」の考え方のほうに目を向けてみるのもいいかと思います。

【VBA編】(準備)クラスについて(1)

準備

VBAニューラルネットワークを実装するにあたり、(ここでの実装では)クラスの概念が必要になります。
とはいえ、VBAでクラスを使うのはあまりなじみがないと思いますので、ここでは準備段階として簡単に説明しようと思います。

オブジェクト指向

オブジェクトとかクラスとかいう言葉を聞くと拒否反応を起こしてしまう方もいるのではないでしょうか。
でも、大丈夫です。
ExcelVBAはオブジェクト指向を完全にはサポートしていないので、覚えることはそれほど多くありません。
なので、ひとまずはあまりオブジェクト指向を意識しなくてもよいと思います。
とはいえ、概念だけでも知っておく必要はありますので、該当する部分だけを簡単に説明します。

クラス

クラスとは一般的には設計図やひな型のようなものという説明がされることが多いかと思います。
オブジェクト指向では、その設計図・ひな型(クラス)をもとに実体(オブジェクト、インスタンス)を作って利用していきます。
同じクラスから作られたオブジェクトは同じ属性、機能を持ちますが、(基本的には)それぞれが独立しています。
ユーザフォームのコントロールのようなものと考えればよいでしょうか。
たとえば、コマンドボタンを配置すると、いろいろと初期値が設定されていますが、
キャプションや色などはそれぞれのコマンドボタンごとに自由に設定できます(独立しています)。
クラスを定義し、そこからオブジェクトを作ることで、同じ機能を持つただし若干異なるオブジェクトを(比較的簡単に)大量に作ることができます。
オブジェクト指向では、継承や多様性などいろいろな用語が出てきますが、VBAではこの程度の理解で大丈夫だと思います。
つまり、
  「設計図・ひな型(クラス)となるものを用意し、それをもとに実体(オブジェクト)を作る」
まずは、これを抑えておきましょう。

[ひな型をもとに実体を作る]
f:id:celaeno42:20181101233203p:plain:w400

3冊の本を作ってみる

例として3冊の本を作ってみましょう。
本はそれぞれ「コード」「タイトル」「著者」「(税抜き)価格」の情報を持っていて、
「コード」「タイトル」「著者」「税抜き価格」「税込み価格」を表示します。

クラスを使わないで書いてみる

まずはクラスを使わないで書いてみましょう。
本ごとに必要な変数を用意して、それぞれの変数に必要な情報を格納するようなコードにしています。
(ユーザ定義型を使えばもう少しスマートに書けるとは思いますが…)

'[標準モジュール]
Option Explicit

Public Sub クラス不使用()
    Dim book01Code As String
    Dim book01Title As String
    Dim book01Author As String
    Dim book01Price As Long

    Dim book02Code As String
    Dim book02Title As String
    Dim book02Author As String
    Dim book02Price As Long
    
    Dim book03Code As String
    Dim book03Title As String
    Dim book03Author As String
    Dim book03Price As Long
    
    book01Code = "bk-01"
    book01Title = "長時間の影"
    book01Author = "H.P.ラヴクラフト"
    book01Price = 800
    
    book02Code = "bk-02"
    book02Title = "異次元を覗く家"
    book02Author = "W.H.ホジスン"
    book02Price = 1000
    
    book03Code = ""
    book03Title = ""
    book03Author = ""
    book03Price = -100
    
    Call printBook(book01Code, book01Title, book01Author, book01Price)
    Call printBook(book02Code, book02Title, book02Author, book02Price)
    Call printBook(book03Code, book03Title, book03Author, book03Price)
    
End Sub

Private Sub printBook(ByRef aCode As String, ByRef aTitle As String, ByRef aAuthor As String, ByRef aPrice As Long)
    Const TAX As Double = 1.08
    
    Debug.Print "----" & aCode & "----"
    Debug.Print "タイトル = " & aTitle
    Debug.Print "著者 = " & aAuthor
    Debug.Print "税抜き価格 = " & aPrice
    Debug.Print "税込み価格 = " & aPrice * TAX
    Debug.Print "------------"
End Sub


[イミディエイトウィンドウ](出力結果)

----bk-01----
タイトル = 長時間の影
著者 = H.P.ラヴクラフト
税抜き価格 = 800
税込み価格 = 864
------------
----bk-02----
タイトル = 異次元を覗く家
著者 = W.H.ホジスン
税抜き価格 = 1000
税込み価格 = 1080
------------
--------
タイトル = 
著者 = 
税抜き価格 = -100
税込み価格 = -108
------------


クラスを使って書いてみる - 1(クラスモジュール)

では、これをクラスを使って書いてみましょう。
クラスはクラスモジュールに書きますので、VBEの[挿入]から「クラスモジュール」を選んでクラスモジュールを追加しましょう。
クラスモジュールのオブジェクト名は「classBook」としておきます。

VBEのプロジェクトはこのようになります。
f:id:celaeno42:20181103231252p:plain

以下のコードは classBook に書きます。

'[クラスモジュール - classBook]
Option Explicit

Public pCode As String
Public pTitle As String
Public pAuthor As String
Public pPrice As Long
Const TAX As Double = 1.08

Public Function exTaxPrice() As Long
    exTaxPrice = pPrice
End Function

Public Function inTaxPrice() As Long
    inTaxPrice = pPrice * TAX
End Function

「コード」「タイトル」「著者」「(税抜き)価格」はこのモジュールの外から呼ばれることを想定して、それぞれ Public変数として宣言しています。
これらの「クラスの持つ変数」をここでは他の言語のマネをして「メンバ変数」と呼ぶことにします。
また、定数として「税率」を持っていて、「税抜き価格」「税込み価格」は Function プロシージャで計算結果を戻すようにしています。
(もっとも、「税抜き価格」はそのまま「価格」を返しているだけですが、Functionプロシージャとすることで、呼び出し側で「税込み価格」と同じような書式で呼び出すことができます。)

では、呼び出し側の処理と、結果を出力する処理を見てみましょう。
これらは標準モジュールに書いています。

'[標準モジュール]
Option Explicit

Public Sub クラス使用()
    Dim cBook11 As classBook
    Dim cBook12 As classBook
    Dim cBook13 As classBook
    
    Set cBook11 = New classBook
    cBook11.pCode = "bk-11"
    cBook11.pTitle = "銀河ヒッチハイクガイド"
    cBook11.pAuthor = "ダグラス・アダムス"
    cBook11.pPrice = 420
    
    Set cBook12 = New classBook
    cBook12.pCode = "bk-12"
    cBook12.pTitle = "プロジェクトぴあの"
    cBook12.pAuthor = "山本弘"
    cBook12.pPrice = 1500
    
    Set cBook13 = New classBook
    cBook13.pTitle = ""
    cBook13.pPrice = -100
    
    Call printBookInfo(cBook11)
    Call printBookInfo(cBook12)
    Call printBookInfo(cBook13)

    Set cBook11 = Nothing
    Set cBook12 = Nothing
    Set cBook13 = Nothing

End Sub

Private Sub printBookInfo(ByRef aBook As classBook)
    Debug.Print "----" & aBook.pCode & "----"
    Debug.Print "タイトル = " & aBook.pTitle
    Debug.Print "著者 = " & aBook.pAuthor
    Debug.Print "税抜き価格 = " & aBook.exTaxPrice
    Debug.Print "税込み価格 = " & aBook.inTaxPrice
    Debug.Print "------------"
End Sub

クラスは変数として宣言できます。
通常の変数と同様に

 Dim 変数名 as クラス名

とすればOKです。

そのあとで

 Set 変数名 = New クラス名

とすることで、クラス(設計図)からインスタンス(オブジェクト / 実体)が作られます。
(一般的に 「Newする」といいます。)

クラスの持つ Public な変数やプロシージャなどにアクセスするには、「 . 」(ドット演算子)を用います。
上記コードでは、[ cBook11.pCode = "bk-01" ] などとしているところで、
これは、「cBook11 オブジェクト のメンバ変数 pCode に 文字列 "bk-01" を代入する」という意味です。
フォームコントロールの [ Text1.Text = "bk-01" ] などの書き方と同じですね。

また、クラスはプロシージャの引数として渡すこともできます。
上記コードでは、printBookInfoの引数として classBook型の変数 を渡しています。

最後の [ Set cBook11 = Nothing ] でオブジェクトを解放(破棄)しています。
ただ、VBAの場合、変数は所属するプロシージャが終了すると破棄されるので、あまり神経質にならなくてもいいのでは?との意見もあります。

では、実行して出力を見てみましょう。
設定した値が表示されているのがわかりますね。

[イミディエイトウィンドウ]

----bk-11----
タイトル = 銀河ヒッチハイクガイド
著者 = ダグラス・アダムス
税抜き価格 = 420
税込み価格 = 454
------------
----bk-12----
タイトル = プロジェクトぴあの
著者 = 山本弘
税抜き価格 = 1500
税込み価格 = 1620
------------
--------
タイトル = 
著者 = 
税抜き価格 = -100
税込み価格 = -108
------------


クラスを使って書いてみる - 2(プロパティ)

さて、設定した値が表示されるのはいいのですが、例えばタイトルにブランクを設定したら「未定」と表示させたり、価格にマイナスの値が設定されたら 0 にしたりというようにしたい場合、上記のコードでは実現できません。
そこで、classBook をもう少し使い勝手の良いクラスに修正してみましょう。

'[クラスモジュール - classBook]
Option Explicit

Dim mCode As String
Dim mTitle As String
Dim mAuthor As String
Dim mPrice As Long
Const TAX As Double = 1.08

Public Property Let Code(ByRef aCode As String)
    If aCode = "" Then
        aCode = "bk-xx"
    Else
        mCode = aCode
    End If
End Property

Public Property Get Code() As String
    Code = mCode
End Property

Public Property Let Title(ByRef aTitle As String)
    If aTitle = "" Then
        mTitle = "未定"
    Else
        mTitle = aTitle
    End If
End Property

Public Property Get Title() As String
    Title = mTitle
End Property

Public Property Let Author(ByRef aAuthor As String)
    If aAuthor = "" Then
        mAuthor = "未定"
    Else
        mAuthor = aAuthor
    End If
End Property

Public Property Get Author() As String
    Author = mAuthor
End Property

Public Property Let Price(ByRef aPrice As Long)
    If aPrice < 0 Then
        mPrice = 0
    Else
        mPrice = aPrice
    End If
End Property

Public Function exTaxPrice() As Long
    exTaxPrice = mPrice
End Function

Public Function inTaxPrice() As Long
    inTaxPrice = mPrice * TAX
End Function


少し長くなりましたが解説していきましょう。
まず、前回 Public で宣言していたメンバ変数を Dim の宣言に変更しています。
そのため、前回のように呼び出し側(classBookの外)からは直接メンバ変数を操作できなくなっています。
そこで、メンバ変数を操作するために、Propertyプロシージャを利用します。
コードを見ると Property Let、Property Get でなんらかの同じ名前の定義をしていることがわかると思います。
これは、他の言語では セッター/ゲッター(Setter/Getter) と呼ばれるもので、クラスのメンバ変数の値を設定したり取得したりするプロシージャです。
VBAでは Property プロシージャと呼ばれ、通常は、Let(オブジェクトの場合はSet)と Get で同じ名前のプロシージャを定義して、値を設定したり取得したりする際に利用します。
Let(Set)は1つの引数をとり、Get は引数をとることができないという点も Property プロシージャ独特の仕様です。
これも次のようなフォームコントロールの使い方と同様です。

 Text1.Text = "Hello World"
 result = Text1.Text
 Debug.Print(result)

 [出力]
 Hello World

一部を抜粋しましょう。

Dim mTitle As String

Public Property Let Title(ByRef aTitle As String)
    If aTitle = "" Then
        mTitle = "未定"
    Else
        mTitle = aTitle
    End If
End Property

Public Property Get Title() As String
    Title = mTitle
End Property

これは Titleプロパティに Let で値(引数の aTitle) を設定、Get で値を取得するコードです。
引数 aTitle がブランクの場合は、Titleのデフォルト値として "未定" を設定するようにしています。
設定値はメンバ変数 mTitle に格納し、Get の戻り値としています。

これは例えば、

 cBook.Title = "アイの物語"  '値の設定(Let が呼ばれる)
 result = cBook.Title     '値の取得(Get が呼ばれる)
 Debug.Print(result)

 [出力]
 アイの物語

のように使います。

Dim mPrice As Long
Const TAX As Double = 1.08

Public Property Let Price(ByRef aPrice As Long)
    If aPrice < 0 Then
        mPrice = 0
    Else
        mPrice = aPrice
    End If
End Property

Public Function exTaxPrice() As Long
    exTaxPrice = mPrice
End Function

Public Function inTaxPrice() As Long
    inTaxPrice = mPrice * TAX
End Function

上記コードは、Let で価格(Price)を設定している個所です。
価格がマイナス(aPrice が 0 未満)なら、価格に 0 を設定するようにしています。
ただ、Price プロパティ については、Get がありません。
(代わりに exTaxPrice()、inTaxPrice() で値を返しています。)
このように、必ずしも Let(Set) と Get を対で利用しなくてもかまいません。

では、これを標準モジュールから呼び出してみましょう。

'[標準モジュール]
Option Explicit

Public Sub クラス使用()
    Dim cBook11 As classBook
    Dim cBook12 As classBook
    Dim cBook13 As classBook
    
    Set cBook11 = New classBook
    cBook11.Code = "bk-11"
    cBook11.Title = "銀河ヒッチハイクガイド"
    cBook11.Author = "ダグラス・アダムス"
    cBook11.Price = 420
    
    Set cBook12 = New classBook
    cBook12.Code = "bk-12"
    cBook12.Title = "プロジェクトぴあの"
    cBook12.Author = "山本弘"
    cBook12.Price = 1500
    
    Set cBook13 = New classBook
    cBook13.Title = ""
    cBook13.Price = -100
    
    Call printBookInfo(cBook11)
    Call printBookInfo(cBook12)
    Call printBookInfo(cBook13)

    Set cBook11 = Nothing
    Set cBook12 = Nothing
    Set cBook13 = Nothing

End Sub

Private Sub printBookInfo(ByRef aBook As classBook)
    Debug.Print "----" & aBook.Code & "----"
    Debug.Print "タイトル = " & aBook.Title
    Debug.Print "著者 = " & aBook.Author
    Debug.Print "税抜き価格 = " & aBook.exTaxPrice
    Debug.Print "税込み価格 = " & aBook.inTaxPrice
    Debug.Print "------------"
End Sub

[イミディエイトウィンドウ]

----bk-11----
タイトル = 銀河ヒッチハイクガイド
著者 = ダグラス・アダムス
税抜き価格 = 420
税込み価格 = 454
------------
----bk-12----
タイトル = プロジェクトぴあの
著者 = 山本弘
税抜き価格 = 1500
税込み価格 = 1620
------------
--------
タイトル = 未定
著者 = 
税抜き価格 = 0
税込み価格 = 0
------------

呼び出し方は先ほどとそれほど変わっていません。
ただ、3冊めの出力結果が若干変わっていて、タイトル = 未定 となっていたり、価格が(マイナスでなく)0 になっています。
これは、プロパティの処理で引数がブランクだったりマイナスだったりしたときにデフォルト値を設定するようにしているためです。

このように、メンバ変数へのアクセスは必ず Property や Function から行うようにすることで、(価格にマイナスが設定されるような)不用意な操作を防ぐことができ、より安全性の高いコードになります(なるといわれてます)。
オブジェクト指向では「情報隠蔽」とか「カプセル化」といったりします。)

クラスを使って書いてみる - 3(初期化1)

もう一度3つめの出力結果をよく見てみると、「コード」と「著者名」がブランクになっています。
それぞれの Property プロシージャでは、「引数としてブランクが渡されたらデフォルト値に設定する」としていましたが、そもそも呼び出し側でこれらの項目については何の設定もしていないため、Code と Author については、Let プロシージャが呼ばれていないのです。そのため、Get プロシージャでは何の設定もされていない mCode, mAuthor が返されています。

そこで、classBook が New されたときに初期値を設定することで「何も設定しなくても初期値が設定されている」ように修正してみましょう。

'[クラスモジュール - classBook]
Option Explicit

Dim mCode As String
Dim mTitle As String
Dim mAuthor As String
Dim mPrice As Long
Const TAX As Double = 1.08

Private Sub Class_Initialize()
    mCode = "bk-xx"
    mTitle = "未定"
    mAuthor = "未定"
    mPrice = 0
End Sub

Public Property Let Code(ByRef aCode As String)
    mCode = aCode
End Property

Public Property Get Code() As String
    Code = mCode
End Property

Public Property Let Title(ByRef aTitle As String)
    If aTitle <> "" Then
        mTitle = aTitle
    End If
End Property

Public Property Get Title() As String
    Title = mTitle
End Property

Public Property Let Author(ByRef aAuthor As String)
    If aAuthor <> "" Then
        mAuthor = aAuthor
    End If
End Property

Public Property Get Author() As String
    Author = mAuthor
End Property

Public Property Let Price(ByRef aPrice As Long)
    If aPrice > 0 Then
        mPrice = aPrice
    End If
End Property

Public Function exTaxPrice() As Long
    exTaxPrice = mPrice
End Function

Public Function inTaxPrice() As Long
    inTaxPrice = mPrice * TAX
End Function

f:id:celaeno42:20181103233013p:plain:w600


VBEのオブジェクトボックス(①の箇所)で「Class」、プロシージャボックス(②の箇所)で「Initialize」を選択すると、「Class_Initialize()」プロシージャが挿入されます。
このプロシージャはクラスが New されたときに自動的に呼び出される(実行される)プロシージャで、オブジェクトの初期設定に利用することができます。
他の言語では「コンストラクタ」と呼ばれる機能に近いと思います。
ただし、VBAの「Class_Initialize()」プロシージャは(残念ながら)引数をとることができないので、New する際には同じ初期値(デフォルト値)でしか初期化できません。
今回は、「コード」は「bk-xx」、「タイトル」は「未定」、「著者」は「未定」、「価格」は「0」で初期化しています。
また、前のコードでは、Let プロシージャで引数にブランクや0が渡されたときは、それぞれのデフォルト値を設定するようにしていましたが、今回のコードでは無視するようにしています。
New したときに自動的に実行される「Class_Initialize()」で初期値を設定しているため、それぞれの Let プロシージャであらためてデフォルト値を設定する必要がないためです。

では、実際に呼び出して結果を見てみましょう。
呼び出し元のコードは前回と同じです。

[イミディエイトウィンドウ]

----bk-11----
タイトル = 銀河ヒッチハイクガイド
著者 = ダグラス・アダムス
税抜き価格 = 420
税込み価格 = 454
------------
----bk-12----
タイトル = プロジェクトぴあの
著者 = 山本弘
税抜き価格 = 1500
税込み価格 = 1620
------------
----bk-xx----
タイトル = 未定
著者 = 未定
税抜き価格 = 0
税込み価格 = 0
------------

3冊目の「著者」がきちんと「未定」になっていますね。

クラスを使って書いてみる - 4(初期化2)

さて、いまの classBook では、New した後に「タイトル」プロパティには「タイトル」、「著者」プロパティには「著者名」など、それぞれのプロパティを代入する必要があります。
これはちょっと面倒なので、一気に設定できると便利ですね。
そこでプロパティを一気に設定できるプロシージャを作ることにしましょう。
先ほども書いたように、他の言語では New する際に初期値を引数で渡せますが、VBAではそれができないので、New した後にこの「初期値設定用のプロシージャ」を呼び出すことで、プロパティをひとつひとつ設定する手間を省くことができます。
今回、この「初期値設定用のプロシージャ」は「Initialize()」という名前にしましたが、「Class_Initialize()」と紛らわしいので別の名前にしても構いません。
(私は「Constructor()」とすることもあります。)

'[クラスモジュール - classBook]
Option Explicit

Dim mCode As String
Dim mTitle As String
Dim mAuthor As String
Dim mPrice As Long
Const TAX As Double = 1.08

Private Sub Class_Initialize()
    mCode = "bk-xx"
    mTitle = "未定"
    mAuthor = "未定"
    mPrice = 0
End Sub

'プロパティを一度に設定する
Public Sub Initialize(ByRef aCode As String, ByRef aTitle As String, ByRef aAuthor As String, ByRef aPrice As Long)
    Me.Code = aCode
    Me.Title = aTitle
    Me.Author = aAuthor
    Me.Price = aPrice
End Sub

Public Property Let Code(ByRef aCode As String)
    If aCode <> "" Then
        mCode = aCode
    End If
End Property

Public Property Get Code() As String
    Code = mCode
End Property

Public Property Let Title(ByRef aTitle As String)
    If aTitle <> "" Then
        mTitle = aTitle
    End If
End Property

Public Property Get Title() As String
    Title = mTitle
End Property

Public Property Let Author(ByRef aAuthor As String)
    If aAuthor <> "" Then
        mAuthor = aAuthor
    End If
End Property

Public Property Get Author() As String
    Author = mAuthor
End Property

Public Property Let Price(ByRef aPrice As Long)
    If aPrice > 0 Then
        mPrice = aPrice
    End If
End Property

Public Function exTaxPrice() As Long
    exTaxPrice = mPrice
End Function

Public Function inTaxPrice() As Long
    inTaxPrice = mPrice * TAX
End Function

「Initialize()」プロシージャでは、引数をそれぞれの Property プロシージャに渡しています。
これは、「Initialize()」 の引数として渡されてきた値が不用意にプロパティに格納されないようにするためです。
いったん Property プロシージャに渡すことで Property Let でのチェックが働くので、コードの安全性を高めることができます。

では、呼び出し側を見てみましょう。

'[標準モジュール]
Option Explicit

Public Sub クラス使用()
    Dim cBook11 As classBook
    Dim cBook12 As classBook
    Dim cBook13 As classBook
    
    Set cBook11 = New classBook
    cBook11.Code = "bk-11"
    cBook11.Title = "銀河ヒッチハイクガイド"
    cBook11.Author = "ダグラス・アダムス"
    cBook11.Price = 420
    
    Set cBook12 = New classBook
    Call cBook12.Initialize("bk-12", "プロジェクトぴあの", "山本弘", 1500)
    
    Set cBook13 = New classBook
    Call cBook13.Initialize("", "", "", -100)
    
    Call printBookInfo(cBook11)
    Call printBookInfo(cBook12)
    Call printBookInfo(cBook13)
    
    '価格改定!
    Debug.Print
    Debug.Print "--! 価格改定 !--"
    cBook12.Price = 1200
    Call printBookInfo(cBook12)

    Set cBook11 = Nothing
    Set cBook12 = Nothing
    Set cBook13 = Nothing

End Sub

Private Sub printBookInfo(ByRef aBook As classBook)
    Debug.Print "----" & aBook.Code & "----"
    Debug.Print "タイトル = " & aBook.Title
    Debug.Print "著者 = " & aBook.Author
    Debug.Print "税抜き価格 = " & aBook.exTaxPrice
    Debug.Print "税込み価格 = " & aBook.inTaxPrice
    Debug.Print "------------"
End Sub

「cBook12」と「cBook13」のプロパティ設定を今回作成した「Initialize()」プロシージャで行っています。
「cBook11」と比べるとスッキリしていますね。
なお、必要に応じて「Initialize()」プロシージャの引数に Optional キーワードを使うことで、呼び出し側で必要な引数のみを設定すればよくなるため、より使い勝手の良いクラスになると思います。

今回は、一度書籍情報を表示した後、「cBook12」の価格を変更してみました。
このようにいったんオブジェクトの初期値を一度に設定した後、一部のプロパティを変更することも可能です。

出力はこのようになります。

[イミディエイトウィンドウ]

----bk-11----
タイトル = 銀河ヒッチハイクガイド
著者 = ダグラス・アダムス
税抜き価格 = 420
税込み価格 = 454
------------
----bk-12----
タイトル = プロジェクトぴあの
著者 = 山本弘
税抜き価格 = 1500
税込み価格 = 1620
------------
----bk-xx----
タイトル = 未定
著者 = 未定
税抜き価格 = 0
税込み価格 = 0
------------

--! 価格改定 !--
----bk-12----
タイトル = プロジェクトぴあの
著者 = 山本弘
税抜き価格 = 1200
税込み価格 = 1296
------------

2冊目と3冊目が正しく初期値設定されていることと、きちんと2冊目の価格が変更されたことがわかりますね。

次回はクラスについてもうちょっと見ていきます。

【VBA編】どんなものを作っていくか

今回作るニューラルネットワーク


さんざん既出ですが、これを実装していきます。
f:id:celaeno42:20180922233518p:plain


完成のイメージはこんな感じです。(動画が再生されます)
https://1drv.ms/v/s!Akn_IZSOKLJ-1WwjfhdtoXOFKQv1

どう実装するか

ユニットクラスを作りそれで各層のユニットを作っていきます。
また、レイヤークラスを作り、各層のユニットを管理します。
基本的には、これまでで求めた計算式を実装していく形になります。
ニューラルネットワークの実装では、通常は行列計算を使った実装が行われると思いますが、
今回は行列計算は利用しません。
そのかわり、処理速度はあまり速くはありません。

アイリスデータセット

データセットはアイリスデータセットを使用し、アイリス(あやめ)の分類を行います。
f:id:celaeno42:20181102222129p:plain

実際のデータはこんな感じです。
f:id:celaeno42:20181201104704p:plain

がくの長さ、がくの幅、花弁の長さ、花弁の幅のデータから、それがどの種類のあやめかを推測します。
今回は、訓練用データとして129件、テストデータとして21件に分けたものを使用します。

訓練用データ:https://1drv.ms/u/s!Akn_IZSOKLJ-1lIFNhzFRmzawQF1
テストデータ:https://1drv.ms/u/s!Akn_IZSOKLJ-1lEDYSXiM_n2tniI
(webで表示すると1行目が文字化けしてますが、ダウンロードすると正常に表示されます(Shift-JIS))

【数式編】(逆伝播)のまとめ 1

f:id:celaeno42:20181027233159p:plain


逆伝播についてはそれぞれの重みとバイアスの  \nabla E (勾配)の部分を載せます。
重みやバイアスは、 \nabla E (勾配)に学習率を掛け、現在の重みやバイアスの値から引くことで更新していきます。

出力層

出力層についても共通部分を  \delta で表すことにしましょう。

ユニットo11

 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{11}^4} = \frac{\partial E}{\partial u_1^4} \frac{\partial u_1^4}{\partial w_{11}^4} = (z_1^4 - t_1) \times z_1^3 = \delta_1^4 \times z_1^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{12}^4} = \frac{\partial E}{\partial u_1^4} \frac{\partial u_1^4}{\partial w_{12}^4} = (z_1^4 - t_1) \times z_2^3 = \delta_1^4 \times z_2^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{13}^4} = \frac{\partial E}{\partial u_1^4} \frac{\partial u_1^4}{\partial w_{13}^4} = (z_1^4 - t_1) \times z_3^3 = \delta_1^4 \times z_3^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{14}^4} = \frac{\partial E}{\partial u_1^4} \frac{\partial u_1^4}{\partial w_{14}^4} = (z_1^4 - t_1) \times z_4^3 = \delta_1^4 \times z_4^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial b_1^4} = \frac{\partial E}{\partial u_1^4} \frac{\partial u_1^4}{\partial b_1^4} = (z_1^4 - t_1) \times 1 = \delta_1^4 \times 1
\end{align}


ユニットo12

 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{21}^4} = \frac{\partial E}{\partial u_2^4} \frac{\partial u_2^4}{\partial w_{21}^4} = (z_2^4 - t_2) \times z_1^3 = \delta_2^4 \times z_1^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{22}^4} = \frac{\partial E}{\partial u_2^4} \frac{\partial u_2^4}{\partial w_{22}^4} = (z_2^4 - t_2) \times z_2^3 = \delta_2^4 \times z_2^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{23}^4} = \frac{\partial E}{\partial u_2^4} \frac{\partial u_2^4}{\partial w_{23}^4} = (z_2^4 - t_2) \times z_3^3 = \delta_2^4 \times z_3^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{24}^4} = \frac{\partial E}{\partial u_2^4} \frac{\partial u_2^4}{\partial w_{24}^4} = (z_2^4 - t_2) \times z_4^3 = \delta_2^4 \times z_4^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial b_2^4} = \frac{\partial E}{\partial u_2^4} \frac{\partial u_2^4}{\partial b_2^4} = (z_2^4 - t_2) \times 1 = \delta_2^4 \times 1
\end{align}


ユニットo13

 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{31}^4} = \frac{\partial E}{\partial u_3^4} \frac{\partial u_3^4}{\partial w_{31}^4} = (z_3^4 - t_3) \times z_1^3 = \delta_3^4 \times z_1^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{32}^4} = \frac{\partial E}{\partial u_3^4} \frac{\partial u_3^4}{\partial w_{32}^4} = (z_3^4 - t_3) \times z_2^3 = \delta_3^4 \times z_2^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{33}^4} = \frac{\partial E}{\partial u_3^4} \frac{\partial u_3^4}{\partial w_{33}^4} = (z_3^4 - t_3) \times z_3^3 = \delta_3^4 \times z_3^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial w_{34}^4} = \frac{\partial E}{\partial u_3^4} \frac{\partial u_3^4}{\partial w_{34}^4} = (z_3^4 - t_3) \times z_4^3 = \delta_3^4 \times z_4^3
\end{align}


 \displaystyle \begin{align}
 \frac{\partial E}{\partial b_3^4} = \frac{\partial E}{\partial u_3^4} \frac{\partial u_3^4}{\partial b_3^4} = (z_3^4 - t_3) \times 1 = \delta_3^4 \times 1
\end{align}


隠れ層2層め

ユニットh21

 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{11}^3} = \frac{\partial E}{\partial u_1^3} \frac{\partial u_1^3}{\partial w_{11}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{11}^4 \times ReLU'(u_1^3) \\
\\+ \delta_2^4 \times w_{21}^4 \times ReLU'(u_1^3) \\
\\+ \delta_3^4 \times w_{31}^4 \times ReLU'(u_1^3)
\end{pmatrix}
\times z_1^2 = \delta_1^3 \times z_1^2 \\
\\
\\
\frac{\partial E}{\partial w_{12}^3} = \frac{\partial E}{\partial u_1^3} \frac{\partial u_1^3}{\partial w_{12}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{11}^4 \times ReLU'(u_1^3) \\
\\+ \delta_2^4 \times w_{21}^4 \times ReLU'(u_1^3) \\
\\+ \delta_3^4 \times w_{31}^4 \times ReLU'(u_1^3)
\end{pmatrix}
\times z_2^2 = \delta_1^3 \times z_2^2 \\
\\
\\
\frac{\partial E}{\partial w_{13}^3} = \frac{\partial E}{\partial u_1^3} \frac{\partial u_1^3}{\partial w_{13}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{11}^4 \times ReLU'(u_1^3) \\
\\+ \delta_2^4 \times w_{21}^4 \times ReLU'(u_1^3) \\
\\+ \delta_3^4 \times w_{31}^4 \times ReLU'(u_1^3)
\end{pmatrix}
\times z_3^2 = \delta_1^3 \times z_3^2 \\
\\
\\
\frac{\partial E}{\partial b_1^3} = \frac{\partial E}{\partial u_1^3} \frac{\partial u_1^3}{\partial b_1^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{11}^4 \times ReLU'(u_1^3) \\
\\+ \delta_2^4 \times w_{21}^4 \times ReLU'(u_1^3) \\
\\+ \delta_3^4 \times w_{31}^4 \times ReLU'(u_1^3)
\end{pmatrix}
\times 1 = \delta_1^3 \times 1
\end{align}


ユニットh22

 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{21}^3} = \frac{\partial E}{\partial u_2^3} \frac{\partial u_2^3}{\partial w_{21}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{12}^4 \times ReLU'(u_2^3) \\
\\+ \delta_2^4 \times w_{22}^4 \times ReLU'(u_2^3) \\
\\+ \delta_3^4 \times w_{32}^4 \times ReLU'(u_2^3)
\end{pmatrix}
\times z_1^2 = \delta_2^3 \times z_1^2 \\
\\
\\
\frac{\partial E}{\partial w_{22}^3} = \frac{\partial E}{\partial u_2^3} \frac{\partial u_2^3}{\partial w_{22}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{12}^4 \times ReLU'(u_2^3) \\
\\+ \delta_2^4 \times w_{22}^4 \times ReLU'(u_2^3) \\
\\+ \delta_3^4 \times w_{32}^4 \times ReLU'(u_2^3)
\end{pmatrix}
\times z_2^2 = \delta_2^3 \times z_2^2 \\
\\
\\
\frac{\partial E}{\partial w_{23}^3} = \frac{\partial E}{\partial u_2^3} \frac{\partial u_2^3}{\partial w_{23}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{12}^4 \times ReLU'(u_2^3) \\
\\+ \delta_2^4 \times w_{22}^4 \times ReLU'(u_2^3) \\
\\+ \delta_3^4 \times w_{32}^4 \times ReLU'(u_2^3)
\end{pmatrix}
\times z_3^2 = \delta_2^3 \times z_3^2 \\
\\
\\
\frac{\partial E}{\partial b_2^3} = \frac{\partial E}{\partial u_2^3} \frac{\partial u_2^3}{\partial b_2^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{12}^4 \times ReLU'(u_2^3) \\
\\+ \delta_2^4 \times w_{22}^4 \times ReLU'(u_2^3) \\
\\+ \delta_3^4 \times w_{32}^4 \times ReLU'(u_2^3)
\end{pmatrix}
\times 1 = \delta_2^3 \times 1
\end{align}


ユニットh23

 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{31}^3} = \frac{\partial E}{\partial u_3^3} \frac{\partial u_3^3}{\partial w_{31}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{13}^4 \times ReLU'(u_3^3) \\
\\+ \delta_2^4 \times w_{23}^4 \times ReLU'(u_3^3) \\
\\+ \delta_3^4 \times w_{33}^4 \times ReLU'(u_3^3)
\end{pmatrix}
\times z_1^2 = \delta_3^3 \times z_1^2 \\
\\
\\
\frac{\partial E}{\partial w_{32}^3} = \frac{\partial E}{\partial u_3^3} \frac{\partial u_3^3}{\partial w_{32}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{13}^4 \times ReLU'(u_3^3) \\
\\+ \delta_2^4 \times w_{23}^4 \times ReLU'(u_3^3) \\
\\+ \delta_3^4 \times w_{33}^4 \times ReLU'(u_3^3)
\end{pmatrix}
\times z_2^2 = \delta_3^3 \times z_2^2 \\
\\
\\
\frac{\partial E}{\partial w_{33}^3} = \frac{\partial E}{\partial u_3^3} \frac{\partial u_3^3}{\partial w_{33}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{13}^4 \times ReLU'(u_3^3) \\
\\+ \delta_2^4 \times w_{23}^4 \times ReLU'(u_3^3) \\
\\+ \delta_3^4 \times w_{33}^4 \times ReLU'(u_3^3)
\end{pmatrix}
\times z_3^2 = \delta_3^3 \times z_3^2 \\
\\
\\
\frac{\partial E}{\partial b_3^3} = \frac{\partial E}{\partial u_3^3} \frac{\partial u_3^3}{\partial b_3^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{13}^4 \times ReLU'(u_3^3) \\
\\+ \delta_2^4 \times w_{23}^4 \times ReLU'(u_3^3) \\
\\+ \delta_3^4 \times w_{33}^4 \times ReLU'(u_3^3)
\end{pmatrix}
\times 1 = \delta_3^3 \times 1
\end{align}


ユニットh24

 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{41}^3} = \frac{\partial E}{\partial u_4^3} \frac{\partial u_4^3}{\partial w_{41}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{14}^4 \times ReLU'(u_4^3) \\
\\+ \delta_2^4 \times w_{24}^4 \times ReLU'(u_4^3) \\
\\+ \delta_3^4 \times w_{34}^4 \times ReLU'(u_4^3)
\end{pmatrix}
\times z_1^2 = \delta_4^3 \times z_1^2 \\
\\
\\
\frac{\partial E}{\partial w_{42}^3} = \frac{\partial E}{\partial u_4^3} \frac{\partial u_4^3}{\partial w_{42}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{14}^4 \times ReLU'(u_4^3) \\
\\+ \delta_2^4 \times w_{24}^4 \times ReLU'(u_4^3) \\
\\+ \delta_3^4 \times w_{34}^4 \times ReLU'(u_4^3)
\end{pmatrix}
\times z_2^2 = \delta_4^3 \times z_2^2 \\
\\
\\
\frac{\partial E}{\partial w_{43}^3} = \frac{\partial E}{\partial u_4^3} \frac{\partial u_4^3}{\partial w_{43}^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{14}^4 \times ReLU'(u_4^3) \\
\\+ \delta_2^4 \times w_{24}^4 \times ReLU'(u_4^3) \\
\\+ \delta_3^4 \times w_{34}^4 \times ReLU'(u_4^3)
\end{pmatrix}
\times z_3^2 = \delta_4^3 \times z_3^2 \\
\\
\\
\frac{\partial E}{\partial b_4^3} = \frac{\partial E}{\partial u_4^3} \frac{\partial u_4^3}{\partial b_4^3}
&=
\begin{pmatrix}
 \delta_1^4 \times w_{14}^4 \times ReLU'(u_4^3) \\
\\+ \delta_2^4 \times w_{24}^4 \times ReLU'(u_4^3) \\
\\+ \delta_3^4 \times w_{34}^4 \times ReLU'(u_4^3)
\end{pmatrix}
\times 1 = \delta_4^3 \times 1
\end{align}

隠れ層1層め

ユニットh11

 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{11}^2} &= \frac{\partial E}{\partial u_1^2} \frac{\partial u_1^2}{\partial w_{11}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{11}^3 \times ReLU'(u_1^2) \\
\\ + \delta_2^3 \times w_{21}^3 \times ReLU'(u_1^2) \\
\\ + \delta_3^3 \times w_{31}^3 \times ReLU'(u_1^2) \\
\\ + \delta_4^3 \times w_{41}^3 \times ReLU'(u_1^2)
\end{pmatrix}
\times x_1
= \delta_1^2 \times x_1
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{12}^2} &= \frac{\partial E}{\partial u_1^2} \frac{\partial u_1^2}{\partial w_{12}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{11}^3 \times ReLU'(u_1^2) \\
\\ + \delta_2^3 \times w_{21}^3 \times ReLU'(u_1^2) \\
\\ + \delta_3^3 \times w_{31}^3 \times ReLU'(u_1^2) \\
\\ + \delta_4^3 \times w_{41}^3 \times ReLU'(u_1^2)
\end{pmatrix}
\times x_2
= \delta_1^2 \times x_2
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{13}^2} &= \frac{\partial E}{\partial u_1^2} \frac{\partial u_1^2}{\partial w_{13}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{11}^3 \times ReLU'(u_1^2) \\
\\ + \delta_2^3 \times w_{21}^3 \times ReLU'(u_1^2) \\
\\ + \delta_3^3 \times w_{31}^3 \times ReLU'(u_1^2) \\
\\ + \delta_4^3 \times w_{41}^3 \times ReLU'(u_1^2)
\end{pmatrix}
\times x_3
= \delta_1^2 \times x_3
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{14}^2} &= \frac{\partial E}{\partial u_1^2} \frac{\partial u_1^2}{\partial w_{14}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{11}^3 \times ReLU'(u_1^2) \\
\\ + \delta_2^3 \times w_{21}^3 \times ReLU'(u_1^2) \\
\\ + \delta_3^3 \times w_{31}^3 \times ReLU'(u_1^2) \\
\\ + \delta_4^3 \times w_{41}^3 \times ReLU'(u_1^2)
\end{pmatrix}
\times x_4
= \delta_1^2 \times x_4
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial b_1^2} &= \frac{\partial E}{\partial u_1^2} \frac{\partial u_1^2}{\partial b_1^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{11}^3 \times ReLU'(u_1^2) \\
\\ + \delta_2^3 \times w_{21}^3 \times ReLU'(u_1^2) \\
\\ + \delta_3^3 \times w_{31}^3 \times ReLU'(u_1^2) \\
\\ + \delta_4^3 \times w_{41}^3 \times ReLU'(u_1^2)
\end{pmatrix}
\times 1
= \delta_1^2 \times 1
\end{align}


ユニットh12

 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{21}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{21}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_1
= \delta_2^2 \times x_1
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{22}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{22}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_2
= \delta_2^2 \times x_2
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{23}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{23}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_3
= \delta_2^2 \times x_3
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{24}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{24}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_4
= \delta_2^2 \times x_4
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial b_2^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial b_2^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times 1
= \delta_2^2 \times 1
\end{align}


ユニットh13

 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{31}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{31}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_1
= \delta_3^2 \times x_1
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{32}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{32}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_2
= \delta_3^2 \times x_2
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{33}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{33}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_3
= \delta_3^2 \times x_3
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{34}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{34}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_4
= \delta_3^2 \times x_4
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial b_3^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial b_3^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times 1
= \delta_3^2 \times 1
\end{align}


これらの計算式を実装することで、ニューラルネットワークを構築することができます。

【数式編】(順伝播)のまとめ、あと(損失関数)

f:id:celaeno42:20181027233159p:plain


いままでに求めた順伝播の式をまとめておきます。

隠れ層1層め

ユニットh11

\displaystyle u_1^2 = w_{11}^2 x_1 + w_{12}^2 x_2 + w_{13}^2 x_3 + w_{14}^2 x_4 + b_1^2

\displaystyle z_1^2 = ReLU(u_1^2)


ユニットh12

\displaystyle u_2^2 = w_{21}^2 x_1 + w_{22}^2 x_2 + w_{23}^2 x_3 + w_{24}^2 x_4 + b_2^2

\displaystyle z_2^2 = ReLU(u_2^2)


ユニットh13

\displaystyle u_3^2 = w_{31}^2 x_1 + w_{32}^2 x_2 + w_{33}^2 x_3 + w_{34}^2 x_4 + b_3^2

\displaystyle z_3^2 = ReLU(u_3^2)


隠れ層2層め

ユニットh21

\displaystyle u_1^3 = w_{11}^3 z_1^2 + w_{12}^3 z_2^2 + w_{13}^3 z_3^2 + b_1^3

\displaystyle z_1^3 = ReLU(u_1^3)


ユニットh22

\displaystyle u_2^3 = w_{21}^3 z_1^2 + w_{22}^3 z_2^2 + w_{23}^3 z_3^2 + b_2^3

\displaystyle z_2^3 = ReLU(u_2^3)


ユニットh23

\displaystyle u_3^3 = w_{31}^3 z_1^2 + w_{32}^3 z_2^2 + w_{33}^3 z_3^2 + b_3^3

\displaystyle z_3^3 = ReLU(u_3^3)


ユニットh24

\displaystyle u_4^3 = w_{41}^3 z_1^2 + w_{42}^3 z_2^2 + w_{43}^3 z_3^2 + b_4^3

\displaystyle z_4^3 = ReLU(u_4^3)


出力層

ユニットo11

\displaystyle u_1^4 = w_{11}^4 z_1^3 + w_{12}^4 z_2^3 + w_{13}^4 z_3^3 + w_{14}^4 z_4^3 + b_1^4


\displaystyle z_1^4 = Softmax(u_1^4) \left( = \frac{\exp(u_1^4)}{\sum_{k=1}^3 \exp(u_k^4)} = \frac{\exp(u_1^4)}{\exp(u_1^4) + \exp(u_2^4) + \exp(u_3^4)} \right)


ユニットo12

\displaystyle u_2^4 = w_{21}^4 z_1^3 + w_{22}^4 z_2^3 + w_{23}^4 z_3^3 + w_{24}^4 z_4^3 + b_2^4


\displaystyle z_2^4 = Softmax(u_2^4) \left( = \frac{\exp(u_2^4)}{\sum_{k=1}^3 \exp(u_k^4)} = \frac{\exp(u_2^4)}{\exp(u_1^4) + \exp(u_2^4) + \exp(u_3^4)} \right)


ユニットo13

\displaystyle u_3^4 = w_{31}^4 z_1^3 + w_{32}^4 z_2^3 + w_{33}^4 z_3^3 + w_{34}^4 z_4^3 + b_3^4


\displaystyle z_3^4 = Softmax(u_3^4) \left( = \frac{\exp(u_3^4)}{\sum_{k=1}^3 \exp(u_k^4)} = \frac{\exp(u_3^4)}{\exp(u_1^4) + \exp(u_2^4) + \exp(u_3^4)} \right)


損失関数(クロスエントロピーエラー)

\displaystyle E =  -\sum_{k=1}^3 t_k \log z_k^4 = -(t_1 \log z_1^4 + t_2 \log z_2^4 + t_3 \log z_3^4 )

【数式編】(逆伝播)1つめの隠れ層の重みとバイアスを更新する 3-(3)

f:id:celaeno42:20181027205933p:plain

ユニットh12 の重みとバイアスの更新式

もう一度、前回までで求めた更新式を見てみましょう。

(再掲)
 \displaystyle \begin{align}
\nabla E = \frac{\partial E}{\partial w_{11}^2} &= \frac{\partial E}{\partial u_1^2} \frac{\partial u_1^2}{\partial w_{11}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{11}^3 \times ReLU'(u_1^2) \\
\\ + \delta_2^3 \times w_{21}^3 \times ReLU'(u_1^2) \\
\\ + \delta_3^3 \times w_{31}^3 \times ReLU'(u_1^2) \\
\\ + \delta_4^3 \times w_{41}^3 \times ReLU'(u_1^2)
\end{pmatrix}
\times x_1
\end{align}

よく見ると、


 \displaystyle \left(\delta \times 次の層のユニットの関係する重み \times 自ユニットの u の微分 \right) を足し合わせたもの \times 更新する重みへの入力


という構造になっていることがわかります。
それを踏まえると以下のようになると思います。
今回の共通部は  \displaystyle \delta_2^2 としましょう。


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{21}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{21}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_1
= \delta_2^2 \times x_1
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{22}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{22}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_2
= \delta_2^2 \times x_2
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{23}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{23}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_3
= \delta_2^2 \times x_3
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{24}^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial w_{24}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times x_4
= \delta_2^2 \times x_4
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial b_2^2} &= \frac{\partial E}{\partial u_2^2} \frac{\partial u_2^2}{\partial b_2^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{12}^3 \times ReLU'(u_2^2) \\
\\ + \delta_2^3 \times w_{22}^3 \times ReLU'(u_2^2) \\
\\ + \delta_3^3 \times w_{32}^3 \times ReLU'(u_2^2) \\
\\ + \delta_4^3 \times w_{42}^3 \times ReLU'(u_2^2)
\end{pmatrix}
\times 1
= \delta_2^2 \times 1
\end{align}


ユニットh13 の重みとバイアスの更新式

同じパターンなので以下のようになります。
共通部は  \displaystyle \delta_3^2 とします。


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{31}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{31}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_1
= \delta_3^2 \times x_1
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{32}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{32}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_2
= \delta_3^2 \times x_2
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{33}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{33}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_3
= \delta_3^2 \times x_3
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial w_{34}^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial w_{34}^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times x_4
= \delta_3^2 \times x_4
\end{align}


 \displaystyle \begin{align}
\frac{\partial E}{\partial b_3^2} &= \frac{\partial E}{\partial u_3^2} \frac{\partial u_3^2}{\partial b_3^2}
= 
\begin{pmatrix}
 \delta_1^3 \times w_{13}^3 \times ReLU'(u_3^2) \\
\\ + \delta_2^3 \times w_{23}^3 \times ReLU'(u_3^2) \\
\\ + \delta_3^3 \times w_{33}^3 \times ReLU'(u_3^2) \\
\\ + \delta_4^3 \times w_{43}^3 \times ReLU'(u_3^2)
\end{pmatrix}
\times 1
= \delta_3^2 \times 1
\end{align}


これで必要な計算式がすべて求まりました。