ゼロから作るDeep LearningをJuliaで実装してみる (3)3層ニューラルネットワークの実装

## :red_car: この記事の目的

本記事は、Deep Learningの入門書「ゼロから作るDeep Learning」を参考に、Juliaを用いて同書の内容を実装してみる、という試みです。

※これはあくまで筆者の一つの試みと提案で、**必ずしも内容が正解ではありません**のでご了承お願い致します。
また、より良いアドバイスや協力のご提案などありましたら、お気軽にお声がけ頂けますと幸いです。

## :red_car: 今回取り組むこと

本記事は、参考書の3.4章「3層ニューラルネットワークの実装」にあたります。
特に、3.4.3の実装を確認したいと思います。

参考書のソースでは、init_network()関数の冒頭でnetwork変数を辞書型で宣言しています。Juliaでもこれと同じように、network変数を辞書型で宣言します。
さらに、networkの内容も併せて多次元配列で定義します。

```ruby:3_multidimensional_array
network = Dict()
network["W1"] = [0.1 0.3 0.5; 0.2 0.4 0.6]
network["b1"] = [0.1 0.2 0.3]
network["W2"] = [0.1 0.4; 0.2 0.5; 0.3 0.6]
network["b2"] = [0.1 0.2]
network["W3"] = [0.1 0.3; 0.2 0.4]
network["b3"] = [0.1 0.2]
```

networkは以下のような行と列を持つ多次元配列となります。

|行列|行と列の数|
|:--:|:--------|
|network["W1"]|2行3列|
|network["b1"]|1行3列|
|network["W2"]|3行2列|
|network["b2"]|1行2列|
|network["W3"]|2行2列|
|network["b3"]|1行2列|

Juliaでは、行列同士の掛け算や足し算、引き算はそのまま記述すれば良いです。
ただし、行数と列数が次の条件を満たしていることが必要です。

**掛け算**
　条件：行列Aと行列Bをかけるとき、Aの列数とBの行数が同じであること。
　例えば、l,m,nを整数とするとき、AとBのそれぞれの行数と列数が以下を満たすときです。
　　・Aはl行m列の行列
　　・Bはm行n列の行列

**足し算、引き算**
　条件：二つの行列のそれぞれの行数と列数が等しいこと。

以下の計算を行う際のソースを作成します。
xを1行2列の行列、W1を2行3列の行列、b1を1行3列の行列とするとき、
　a1 = x * W1 + b1
を求めるソースは、

```ruby:3_multidimensional_array
x = [1.0 0.5]
W1 = [0.1 0.3 0.5; 0.2 0.4 0.6]
b1 = [0.1 0.2 0.3]

a1 = x * W1 + b1
```

上記の記載で行列の計算が行われます。

以降、参考書のソースに従うと、
関数init_network()を宣言し、辞書型の変数networkを取得する処理を作成します。

```ruby:3_multidimensional_array
function init_network()
    network = Dict()
    network["W1"] = [0.1 0.3 0.5; 0.2 0.4 0.6]
    network["b1"] = [0.1 0.2 0.3]
    network["W2"] = [0.1 0.4; 0.2 0.5; 0.3 0.6]
    network["b2"] = [0.1 0.2]
    network["W3"] = [0.1 0.3; 0.2 0.4]
    network["b3"] = [0.1 0.2]
    
    return network
end
```

前回の記事で作成した[[前回の記事](https://qiita.com/ttabata/items/7d4b8d92e38df47cffbb)]、シグモイド関数を引用します。
ソースは以下となります。

```ruby:3_multidimensional_array
function sigmoid_function(x)
    return map(i -> 1 / (1 + exp(-i)), x)
end
```

また、恒等関数を定義します。
参考書の通り、恒等関数は以下となります。

```ruby:3_multidimensional_array
function identify_function(x)
    return x
end
```


さらに、シグモイド関数及び恒等関数を用いて、ニューラルネットワークの各層の計算を行う以下のようなforward()関数を定義します。

```ruby:3_multidimensional_array
function forward(network, x)
    W1, W2, W3 = network["W1"], network["W2"], network["W3"]
    b1, b2, b3 = network["b1"], network["b2"], network["b3"]
    
    a1 = x * W1 + b1
    z1 = sigmoid_function(a1)
    a2 = z1 * W2 + b2  
    z2 = sigmoid_function(a2)
    a3 = z2 * W3 + b3
    y = identify_function(a3)
    
    return y
end
```

これらの関数を用いて、以下の手順で行列の計算を行います。

手順(1) init_network()を実行し、行列network変数を取得する。
手順(2) 行列xを宣言する。
手順(3) 引数にnetworkとxを設定してforwar()関数を呼び出す。
　　forwar()関数の中でシグモイド関数や恒等関数を用いた行列の計算を行う。
手順(4) 解として得られる行列yを表示する。

全ソースは以下となります。

```ruby:3_multidimensional_array
function init_network()
    network = Dict()
    network["W1"] = [0.1 0.3 0.5; 0.2 0.4 0.6]
    network["b1"] = [0.1 0.2 0.3]
    network["W2"] = [0.1 0.4; 0.2 0.5; 0.3 0.6]
    network["b2"] = [0.1 0.2]
    network["W3"] = [0.1 0.3; 0.2 0.4]
    network["b3"] = [0.1 0.2]
    
    return network
end


# シグモイド関数の定義
function sigmoid_function(x)
    return map(i -> 1 / (1 + exp(-i)), x)
end


# 恒等関数の定義
function identify_function(x)
    return x
end


function forward(network, x)
    W1, W2, W3 = network["W1"], network["W2"], network["W3"]
    b1, b2, b3 = network["b1"], network["b2"], network["b3"]
    
    a1 = x * W1 + b1
    z1 = sigmoid_function(a1)
    a2 = z1 * W2 + b2  
    z2 = sigmoid_function(a2)
    a3 = z2 * W3 + b3
    y = identify_function(a3)
    
    return y
end

network = init_network()  # 手順(1)
x = [1.0 0.5]             # 手順(2)
y = forward(network, x)   # 手順(3)
println(y)                # 手順(4)
```

実行結果です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/319cbaf0-7654-8c4d-ed9a-c18308b183cd.png)

参考書と同じ結果を得ました。
次は、ソフトマックス関数の実装を行ってみたいと思います。


## :red_car: もくじ

[(1)論理回路](https://qiita.com/ttabata/items/f9253d5ef94ca3807f42)
[(2)活性化関数](https://qiita.com/ttabata/items/7d4b8d92e38df47cffbb)
[(3)3層ニューラルネットワークの実装](https://qiita.com/ttabata/items/4f50ec32c0276d21d966)
[(4)ソフトマックス関数](https://qiita.com/ttabata/items/21329897119f81a141e9)
[(5)MNIST手書き数字認識 その①画像表示](https://qiita.com/ttabata/items/6f7025f48fa52d465c81)
[(5)MNIST手書き数字認識 その②推論](https://qiita.com/ttabata/items/0781e1aacb11d9148053)

## :red_car: 関連情報

:paperclip: Julia早引きノート［01］変数・定数の使い方
https://qiita.com/ttabata/items/a1ada2c0cba03672e105

:paperclip: Julia - 公式ページ
https://julialang.org/

:paperclip: Julia - 日本語公式ドキュメント
https://julia-doc-ja.readthedocs.io/ja/latest/index.html

:paperclip: 初めてのJuliaとインストール (Windows & Linux)
https://qiita.com/ttlabo/items/b05bb43d06239f968035

## :red_car: 出典

■参考書
「ゼロから作るDeep Learning ～ Pythonで学ぶディープラーニングの理論と実装」
　斎藤康毅 著／オライリー・ジャパン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/4a558c91-5e9c-9d6f-3673-d44fbb583103.png)

## :red_car: ご意見など

ご意見、間違い訂正などございましたらお寄せ下さい。