ニューラルネットワークの基礎知識 - 2

# 概要
今回は１つの隠れ層を入れてネットワークを構築します。[前回のベース](https://qiita.com/_dakc_/items/049ce4b43dd710dba945)にプログラムを書いているので、もし読んでいない場合は、まず[ニューラルネットワークの基礎知識 - 1](https://qiita.com/_dakc_/items/049ce4b43dd710dba945)を読むことをお勧めします。

# ネットワークを構成
今回作成するネットワークは、**入力層で３つのニューロン（ノード）**を持ち、**隠れ層で１つのニューロン**、そして**出力層でも１つのニューロン**を持ちます。

![](https://github.com/dakc/ai/blob/master/neural_network/with_single_hidden_layer.png?raw=true)

# Let's Code
```python
# numpyのインポート
import numpy as np
```
### 学習データの用意
```python
# ０：不合格、１：合格
# 合格状態のデータ
inputs = np.array([
    [0,1,1],
    [1,0,0],
    [0,1,0],
    [1,0,1]])

# ０：仕事見つからなかった、１：見つかった
# 仕事見つかった実績
outputs = np.array([
    [1],
    [1],
    [0],
    [1]])
```

### 各パラメータの初期化
weightとbias共に２セットが必要です。一つは入力層と隠れ層をつなぐため、もう一つは隠れ層と出力層をつなぐため。エポック数とlearning rateは前回と同じです。

```python
# 入力層と隠れ層をつなぐ weightの初期化
weights_ih = np.array([
    [0.5],
    [0.5],
    [0.5]])

# 入力層と隠れ層をつなぐbiasの初期化
bias_h = np.array([[0.2]])

# 隠れ層と出力層をつなぐweightの初期化
weights_ho = np.array([[0.5]])

# 隠れ層と出力層をつなぐbiasの初期化
bias_o = np.array([[0.2]])

# learning rateの初期化
lr = 0.1

# epochの初期化
epoch = 5000
```

### 活性化関数の定義
```python
def activate(x):
    return 1/(1+np.exp(-x))
```

### 学習実行
```python

# start training
for _ in range(epochs):
    # feed forward
    # 隠れ層での予測
    prediction_h = activate(np.dot(inputs, weights_ih) + bias_h)

    # 出力層での予測
    prediction_o = activate(np.dot(prediction_h, weights_ho) + bias_o)

    # backpropagation 
    # phase 1：出力層と隠れ層をつなぐweightとbiasの値の調整
    
    # △wの計算
    error = prediction_o - outputs
    sigmoid_der_prediction_o = prediction_o * (1 - prediction_o)
    der_cost_w_o = np.dot(prediction_h.T,  sigmoid_der_prediction_o * error)
    
    # △bの計算
    der_cost_b_o = sigmoid_der_prediction_o * error
    
    # Gradient Descentを使ってweightの調整
    weights_ho -= learning_rate * der_cost_w_o
    
    # Gradient Descentを使ってbiasの調整
    bias_o -=  learning_rate * np.sum(der_cost_b_o,axis=0,keepdims=True)
    
    # phase 2
    # 隠れ層と入力層をつなぐweightとbiasの値の調整
    
    # △wの計算
    sigmoid_der_prediction_h = prediction_h * (1 - prediction_h)
    tmp = sigmoid_der_prediction_h * sigmoid_der_prediction_o * weights_ho * error
    der_cost_w_h = np.dot(inputs.T, tmp)
    
    # △bの計算
    der_cost_b_h = np.sum(tmp,axis=0,keepdims=True)
    
    # Gradient Descentを使ってweightの調整
    weights_ih -= learning_rate * der_cost_w_h
    
    # Gradient Descentを使ってbiasの調整
    bias_h -=  learning_rate * der_cost_b_h
    
# 学習終了
print("Loss  : {:.8f}".format(error))
print("weight: ", weights_ih.T[0])
print("bias  : ", np.asscalar(bias_h))
```


### 評価
どのように学習したか評価します。

```python
# 結果を表示するための関数
def get_prediction(data):
    pred_h = activate(np.dot(data, weights_ih) + bias_h)
    pred_o = activate(np.dot(pred_h , weights_ho) + bias_o)
    msg = "Hired" if round(np.asscalar(pred_o)) else "Not Hired"
    print(data, msg, np.asscalar(pred_o))

# 全科目合格の場合は、仕事見つかるか
get_prediction([1,1,1])

# 物理のみ不合格の場合は、仕事見つかるか
get_prediction([1,1,0])

# 全科目不合格の場合は、仕事見つかるか
get_prediction([0,0,0])
```

今回も、サンプルデータが少ない事とネットワークが単純すぎるから間違った答えがおおいかもしれませんが、基礎の理解という事でご許しください。
また、前回の隠れ層なしの結果と比べて、ネットワークが複雑になっていますが、その反面若干エラー（ロス）の値が少なくなっています。

# Google Colab
直接Colabから実行することもできますので一度試してみてください。ちなみに、Colabのコメントはすべて英語です。
[with_single_hidden_layer.ipynb](https://colab.research.google.com/github/dakc/ai/blob/master/neural_network/with_single_hidden_layer.ipynb)

# 最後に
今回はニューラルネットワークの基礎を理解する目的で隠れ層を１つのみ実装しました。次回はこれらの知識を応用してかつほかのフレームワークを使ってより複雑なネットワークを作りましょう。