多層パーセプトロンはどう凄いのか？機械学習勉強会 #3

## 前回まで

第1回
http://qiita.com/mochizukikotaro/items/5258b4857fa6a78f9ed5

第2回
http://qiita.com/mochizukikotaro/items/1632d3417d6d662beb81

----
## 今回

多層パーセプトロンを用いると何が嬉しいのかを説明したいと思います。

※ 今回も。初心者による、初心者のための勉強会資料となっています。

----
## 簡単な問題をかんがえる

```py:train_data
train_x = np.array([
    [1, 0, 1, 0],
    [0, 1, 0, 1],
    [1, 0, 0, 1],
    [0, 1, 1, 0],
    [0, 1, 1, 0],
    ])

train_y = np.array([
    [1,0],
    [1,0],
    [0,1],
    [0,1],
    [0,1],
    ])
```

----
## 前回使ったモデルで考える

```py:model
x = tf.placeholder("float", [None, 4])
y_ = tf.placeholder("float", [None, 2])
W = tf.Variable(tf.zeros([4, 2]))
b = tf.Variable(tf.zeros([2]))

y = tf.nn.softmax(tf.matmul(x, W) + b)
```

----
## うごかしてみると

```py:結果
% python mnist_beginner_sample.py                                                                                                        (git)-[test]
Step: 0
  誤差: 2.48534

Step: 800
  誤差: 2.30793

Step: 3200
  誤差: 2.30793

# 全然収束しない

[[ 0.43708152  0.56291848]
 [ 0.43708152  0.56291848]
 [ 0.56291795  0.43708208]
 [ 0.31885403  0.68114597]
 [ 0.31885403  0.68114597]]
```

----
## ここで層を増やしてみる

```py:multilayer
i_dim = 4
h_dim = 7

x = tf.placeholder("float", [None, i_dim])
y_ = tf.placeholder("float", [None, 2])

w_h = tf.Variable(tf.random_normal([i_dim, h_dim], mean=0.0, stddev=0.05))
w_o = tf.Variable(tf.random_normal([h_dim, 2], mean=0.0, stddev=0.05))
b_h = tf.Variable(tf.zeros([h_dim]))
b_o = tf.Variable(tf.zeros([2]))

def model(X, w_h, b_h, w_o, b_o):
    h = tf.sigmoid(tf.matmul(X, w_h) + b_h)
    y = tf.nn.softmax(tf.matmul(h, w_o) + b_o)
    return y

y = model(x, w_h, b_h, w_o, b_o)
```

----
## ポイントはここです

```py:point
h = tf.sigmoid(tf.matmul(X, w_h) + b_h)
pyx = tf.nn.softmax(tf.matmul(h, w_o) + b_o)
```

前回 $softmax$ を無視したように、今回も $sigmoid$ については無視しましょう。 :grinning: :neckbeard: 

----
## するとこんな感じになります

```py
% python mlp.py                                                                                                                          (git)-[test]

Step: 1000
誤差3.33253

Step: 4000
誤差1.20457

# だいたいOKでしょう

[[ 0.83402312  0.16597687]
 [ 0.83378381  0.16621622]
 [ 0.26640555  0.73359448]
 [ 0.06264886  0.93735111]
 [ 0.06264886  0.93735111]]
```

----
## ちょっと注意

じつは、上のモデルでも上手くいくようにデータの個数を5個にして、ちょっと歪にしています。最後の要素を取り除いてキレイな左右対称だと上手くいきません。が、例えば層を2層にして、`relu`メソッドや、`adam`オプティマイザーを使えばその場合も上手くいます。


----
## :earth_africa: 

----
## なぜ層を増やすとうまくいくのでしょうか？

```math
{\tiny
\begin{pmatrix}
1&0&1&0 \\
0&1&0&1 \\
1&0&0&1 \\
0&1&1&0
\end{pmatrix}
}
\cdot
W
\to
{\tiny
\begin{pmatrix}
1&0 \\
1&0 \\
0&1 \\
0&1
\end{pmatrix}
}
```

まず、上の変換となるような $W$ を考えてみてください。
※前回の例と似たようで違う行列です。

----
## じつは上手くいきません

※文字だけで上手くいかない理由を説明することは僕にはできません。

では、今度は次の変換を与える $W$ を考えてみてください。

```math
{\tiny
\begin{pmatrix}
1&0&1&0 \\
0&1&0&1 \\
1&0&0&1 \\
0&1&1&0
\end{pmatrix}
}
\cdot
W
\to
{\tiny
\begin{pmatrix}
1&0&1&0&1 \\
0&1&0&1&0 \\
1&0&0&1&0 \\
0&1&1&0&0
\end{pmatrix}
}
```

----
## 正確には作れないとおもいますが

例えば、こんな感じが近いかと思います。

```math
{\tiny
\begin{pmatrix}
1&0&1&0 \\
0&1&0&1 \\
1&0&0&1 \\
0&1&1&0
\end{pmatrix}
}
\cdot

{\tiny
\begin{pmatrix}
1&0&0&0&0.4 \\
0&1&0&0&0 \\
0&0&1&0&0.4 \\
0&0&0&1&0
\end{pmatrix}
}
\to

{\tiny
\begin{pmatrix}
1&0&1&0&0.8 \\
0&1&0&1&0 \\
1&0&0&1&0.4 \\
0&1&1&0&0.4
\end{pmatrix}
}
```

おしいです。この惜しい状態から、0.8を1に近づけ、0.4を0に近づける役割を担うのが、`sigmoid` や `relu` などの関数なんだとおもいます。（要勉強）

----
## 同じことをもう一回繰り返します。

繰り返すと以下のイメージ。

```math
{\tiny
\begin{pmatrix}
1&0&1&0 \\
0&1&0&1 \\
1&0&0&1 \\
0&1&1&0
\end{pmatrix}
}
\to

{\tiny
\begin{pmatrix}
1&0&1&0&1 \\
0&1&0&1&0 \\
1&0&0&1&0 \\
0&1&1&0&0
\end{pmatrix}
}
\to

{\tiny
\begin{pmatrix}
1&0&1&0&1&0 \\
0&1&0&1&0&1 \\
1&0&0&1&0&0 \\
0&1&1&0&0&0
\end{pmatrix}
}
\to

{\tiny
\begin{pmatrix}
1&0 \\
0&1 \\
0&0 \\
0&0
\end{pmatrix}
}
```

※ まあ。あくまでイメージです。 :stuck_out_tongue: 

----
## つまり

層を増やすことで、どんなトレーニングデータに対しても、一応正解に近づけることができます。

サンプル
https://gist.github.com/mochizukikotaro/e368328b10bf6b5bff4454264367be59

----
## 以上、ありがとうございました。 :massage: