TensorFlowの基本的な関数をXORゲートを学習するニューラルネットワーク作成してまとめてみる


TensorFlowの関数についてよくわからなかったので[この本](https://www.amazon.co.jp/gp/product/4839962510/ref=s9u_simh_gw_i1?ie=UTF8&pd_rd_i=4839962510&pd_rd_r=43PPPAM7DTS08SPP1Y40&pd_rd_w=E8f5E&pd_rd_wg=uI3lL&pf_rd_m=AN1VRQENFRJN5&pf_rd_s=&pf_rd_r=4J4CEBWSM45D5K2A5SGW&pf_rd_t=36701&pf_rd_p=d4802771-73ad-49b1-a154-90aaec384d3e&pf_rd_i=desktop)を見てまとめてみた。

xorゲートをTensorFlowを使って実現する。
真理値表は以下、入力層の次元が2 出力層の次元が1となる

| x1 | x2 | y |
|:--:|:--:|:--:|
| 0| 0| 0|
| 0| 1| 1|
| 1| 0| 1|
| 1| 1| 0|


## プログラムの流れ

### ①ライブラリのインポート

```python
import numpy as np
import tensorflow as tf
```

### ②XORのデータ用意
```python
X = np.array([[0,0],[0,1],[1,0],[1,1]])
Y = np.array([[0],[1],[1],[0]])
```

### ③入力と正解ラベルの入れ物用意

```python
x = tf.placeholder(tf.float32, shape=[None,2])
t = tf.placeholder(tf.float32, shape=[None,1])
```

`tf.placeholder()`
データを格納する入れ物のような存在.
モデル定義の際には次元だけを決めておき、モデルの学習など実際にデータが必要になったタイミングで値を入れて実際の式を評価することを可能にする
`shape=[None,2]`は入力ベクトルの次元が2であることを表し、`None`としているのはデータ数が可変でも対応できる入れ物となる
`None`部分 ← 要するにxorゲートの際は`00`,`01`,`10`,`11`の4つのデータであるが、実際にはデータ数がわからないことがあるので`None`となる

### ④モデルの定義（入力層 - 隠れ層）
x:入力　　h:隠れ層の出力　　W:重み　　b:バイアス

```math
h = Wx + b
```

```python
W = tf.Variable(tf.truncated_normal([2,2]))
b = tf.Variable(tf.zeros([2]))
h = tf.nn.sigmoid(tf.matmul(x,W) + b)
```

`tf.Variable()`
変数を生成するのに必要。TensorFlowが持つ独自の型でデータを扱っていく
`中身のtf.zeros()`はNumpyにおける`np.zeros()`に相当する
`tf.truncated_normal()`は切断正規分布に従うデータを生成するメソッド。`0`で初期化すると正しく誤差が反映されなくなる恐れがあるため


### ⑤モデルの定義（隠れ層 - 出力層）
h:出力層への入力(隠れ層出力)　　y:出力　　V:重み　　c:バイアス

```math
y = Vh + c
```

```python
V = tf.Variable(tf.truncated_normal([2,1]))
c = tf.Variable(tf.zeros([1]))
y = tf.nn.sigmoid(tf.matmul(h,V) + c)
```

説明は④と同じ

### ⑥誤差関数

```python
cross_entropy = -tf.reduce_sum(t * tf.log(y) + (1-t) * tf.log(1-y))
```
今回は2値分類であるため、交差エントロピー関数を使用する

`-tf.reduce_sum(t * tf.log(y) + (1-t) * tf.log(1-y))`
交差エントロピー誤差関数の計算を数式通りに書くことができる
`tf.reduce_sum()`は`np.sum()`に対応する

### ⑦確率的勾配降下法

```python
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy)
```
確率的勾配降下法を適用している
`GradientDescentOptimizer()`の引数`0.1`は学習率

### ⑧ 学習後の結果の確認

```python
correct_prediction = tf.equal(tf.to_float(tf.greater(y, 0.5)), t)
```

学習後の結果が正しいかどうか確認するための実装
`y >= 0.5` でニューロンが発火する。それを正解ラベルの`t`と比較して`True` or `False`を返す

### ⑨セッション用意

```python
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
```

TensorFlowでは必ずセッションというデータのやり取りの流れの中で計算が行われます。
ここではじめてモデルの定義で宣言した変数の・式の初期化が行われます。

### ⑩学習

```python
for epoch in range(4000):
    sess.run(train_step, feed_dict={
        x:X,
        t:Y
    })

    if epoch % 1000 == 0:
        print('epoch:', epoch)
```

`sess.run(train_step)`これは勾配降下法による学習をすること
`feed_dict`で`placeholder`である`x`,`t`に値を代入している
まさにplaceholderに値をfeed

### ⑪学習結果の確認(正解ラベルとの比較)

```python
classified = correct_prediction.eval(session=sess, feed_dict={
    x:X,
    t:Y
})
```

`eval()`
ニューロンが発火する・しないを適切に分類できるようになっているかを確認するのに使う
要するにここでは、`correct_prediction`の値の確認に使用する

### ⑫学習結果の確認(出力確率)

```python
prob = y.eval(session=sess, feed_dict={
    x:X,
    t:Y
})
```
各入力に対する出力確率を得ることができる
要するにyの値を確認できる

### ⑫表示

```python
print('classified:')
print(classified)
print()
print('output probability:')
print(prob)
```

## 結果

```txt:出力
epoch: 0
epoch: 1000
epoch: 2000
epoch: 3000
classified:
[[ True]
 [ True]
 [ True]
 [ True]]

output probability:
[[ 0.00661706]
 [ 0.99109781]
 [ 0.99389231]
 [ 0.00563505]]
```

## 参考
[詳解 ディープラーニング ~TensorFlow・Kerasによる時系列データ処理](https://www.amazon.co.jp/gp/product/4839962510/ref=s9u_simh_gw_i1?ie=UTF8&pd_rd_i=4839962510&pd_rd_r=43PPPAM7DTS08SPP1Y40&pd_rd_w=E8f5E&pd_rd_wg=uI3lL&pf_rd_m=AN1VRQENFRJN5&pf_rd_s=&pf_rd_r=4J4CEBWSM45D5K2A5SGW&pf_rd_t=36701&pf_rd_p=d4802771-73ad-49b1-a154-90aaec384d3e&pf_rd_i=desktop)