TensorFlowによるMNIST文字認識

※以下、個人的な勉強のためのレポートです。
※間違い多々あると存じますが、現在の理解レベルのスナップショットのようなものです。
※勉強のためWebサイトや書籍からとても参考になったものを引用させていただいております。
http://ai999.careers/rabbit/

#MNIST
https://weblabo.oscasierra.net/python/ai-mnist-data-detail.html
・手書きで書かれた数字を画像にした画像データ(image)と、その画像に書かれた数字(0～9)を表すラベルデータ(label)から構成される
・ペアは、学習用に60,000個、検証用に10,000個の数提供されている
　　train-images-idx3-ubyte: 学習用の画像セット
　　train-labels-idx1-ubyte: 学習用のラベルセット
　　t10k-images-idx3-ubyte: 検証用の画像セット
　　t10k-labels-idx1-ubyte: 検証用のラベルセット
・28×28の升目(ピクセル)と、その値(チャネル、ピクセル値：0～255までの値、0が白,255が黒)

#実装

```
#入力xのプレースホルダ、28*28=784
x = tf.placeholder(tf.float32, [None, 784])
#出力dのプレースホルダ、0-9までの10分類
d = tf.placeholder(tf.float32, [None, 10])
#重み。784個の数値から出力層10個へのそれぞれの重み。標準偏差0.01で初期化。
W = tf.Variable(tf.random_normal([784, 10], stddev=0.01))
#バイアス。出力層10個それぞれについて一つずつ。0で初期化。
b = tf.Variable(tf.zeros([10]))
#計算ノード。分類問題なのでソフトマックス関数を使用。
y = tf.nn.softmax(tf.matmul(x, W) + b)

#誤差関数には交差エントロピーの値を使用。
cross_entropy = -tf.reduce_sum(d * tf.log(y), reduction_indices=[1])
loss = tf.reduce_mean(cross_entropy)
train = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

```

#結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/dd2e94d5-380f-539a-f8cb-11b6de103553.png)
100の学習で、正解率 約87%

#実際のデータの確認

```
plt.imshow(x_batch[0].reshape(28,28))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/c65979f3-d1b0-589a-63db-5f50469b124b.png)

#3層化
中間層を二つ作成するため、600と300で用意する。
入力層784⇒中間層600⇒中間層300⇒出力層10

```
hidden_layer_size_1 = 600
hidden_layer_size_2 = 300

#3層化されたので、重み、バイアスともに3つ用意する
W1 = tf.Variable(tf.random_normal([784, hidden_layer_size_1], stddev=0.01))
W2 = tf.Variable(tf.random_normal([hidden_layer_size_1, hidden_layer_size_2], stddev=0.01))
W3 = tf.Variable(tf.random_normal([hidden_layer_size_2, 10], stddev=0.01))

b1 = tf.Variable(tf.zeros([hidden_layer_size_1]))
b2 = tf.Variable(tf.zeros([hidden_layer_size_2]))
b3 = tf.Variable(tf.zeros([10]))
```

#dropoutにより、過学習を抑制する。

```
dropout_rate = 0.5
keep_prob = tf.placeholder(tf.float32)
drop = tf.nn.dropout(z2, keep_prob)
```

#結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/659a2b67-64a7-c820-ff0a-3adf01b100c3.png)
正解率が約90%に改善

##ハイパーパラメータの変更
**中間層のノード数を、600/300から、100/50に変更**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/377f3c41-d2af-905c-30cc-9f4e7c1dd1d6.png)
⇒パラメータ数が減少することでマシンの処理は軽くなるが、学習精度は落ちる
**中間層のノード数を、1000/500に変更**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/ab8f2bf7-b29d-b7a9-c86c-da1b5662b1d5.png)
⇒パラメータ数が増大することでマシンの処理負荷は重たくなる。しかし学習精度は約91%まで改善。

**NNにおいては、中間層のノード数は、速度と精度のトレードオフを考慮する必要がある**

##最適化関数の変更
**momentum**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/e7e44bc0-84e4-9e5c-11fd-28988e65a3be.png)
⇒精度が94％まで改善
**Adagrad**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/ceaa42a3-d1df-f4fc-3e3d-721b240005c8.png)
⇒精度　約90％
**RMSProp**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/0a11500a-0600-be6e-7189-aea7c7d41955.png)
⇒96.75％を達成！

実際には、各最適化関数が頭打ちになるまで学習回数を上げて比較すると良い。

#CNN(Convolutional Neural Network:畳み込みニューラルネットワーク)
##構造
conv - relu - pool - conv - relu - pool - affin - relu - dropout - affin - softmax

##実装
```
# 第一層のweightsとbiasのvariable
#最初の5*5がCNNのフィルターサイズ。1チャネルのものを32チャネルに拡張。
W_conv1 = tf.Variable(tf.truncated_normal([5, 5, 1, 32], stddev=0.1))
b_conv1 = tf.Variable(tf.constant(0.1, shape=[32]))

# 第一層のconvolutionalとpool
# strides[0] = strides[3] = 1固定
#ストライドは1、パディングはSHAPEが変わらないような値を設定。
h_conv1 = tf.nn.relu(tf.nn.conv2d(x_image, W_conv1, strides=[1, 1, 1, 1], padding='SAME') + b_conv1)
# プーリングサイズ n*n にしたい場合 ksize=[1, n, n, 1]
#max_poolが2,2、ストライドが2,2
h_pool1 = tf.nn.max_pool(h_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

# 第二層
W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 32, 64], stddev=0.1))
b_conv2 = tf.Variable(tf.constant(0.1, shape=[64]))
h_conv2 = tf.nn.relu(tf.nn.conv2d(h_pool1, W_conv2, strides=[1, 1, 1, 1], padding='SAME') + b_conv2)
h_pool2 = tf.nn.max_pool(h_conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

# 第一層と第二層でreduceされてできた特徴に対してrelu
#affinの実装部分
#max_poolの出力が7*7*64になっているので、reshapeで1次元に直す。
W_fc1 = tf.Variable(tf.truncated_normal([7 * 7 * 64, 1024], stddev=0.1))
b_fc1 = tf.Variable(tf.constant(0.1, shape=[1024]))
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

# Dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 出来上がったものに対してSoftmax
W_fc2 = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1))
b_fc2 = tf.Variable(tf.constant(0.1, shape=[10]))
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

# 交差エントロピー
loss = -tf.reduce_sum(d * tf.log(y_conv))

train = tf.train.AdamOptimizer(1e-4).minimize(loss)

correct = tf.equal(tf.argmax(y_conv,1), tf.argmax(d,1))
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
```

#結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/17196058-5905-fdf0-367e-3c867a8399d7.png)
⇒計算負荷は高い。しかし精度は最高で96％を記録。計算回数を増加させることで、99％まで高まる可能性もある。

##ハイパーパラメータの変更
**dropout_ratioを0.0に変更(dropoutさせない)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/dafa99a2-4ab2-a575-b9a6-67344707f08e.png)
⇒あまり変わらない。やや下がる。

#論文からの実装
State of the Art：現時点で一番精度が高い技術
ex)「VGG16 arxiv」で検索。
※arXiv（アーカイヴ、archiveと同じ発音）は、物理学、数学、計算機科学、量的生物学、計量ファイナンス、統計学の、プレプリント（英語版）を含む様々な論文が保存・公開されているウェブサイト
⇒PDF版の論文が閲覧可能
ex)ブログなどでの考察
ex)githubの実装コード。実装が入手できる。

##JDLAの例題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/1aeaa934-3fe6-cf9a-e937-e2890f4ce430.png)

(答え)a
必要なパラメータ数は増加するが、その分スパース(Sparse:「すかすか」、「少ない」の意)な演算になる。よってdense(密)ではない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/874d28c1-e8ce-03a1-df69-499f8c770ded.png)

(答え)a
Auxiliary calssifiresを導入して、calssifiresを増加させて計算量を減少させているわけではない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/ccfbc3b4-2915-ac11-2ed1-3d846071556d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/4d6c4bbf-068f-f84d-2f2f-718762691968.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/18dd7cf0-3030-286c-e500-4d966e6d59fa.png)


(答え)a、a、a
いわゆるskipコネクション。これにより層を深くしても学習が進むことになった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/b1990757-c52f-9a71-60d2-11d03ac6a538.png)


(答え)あ
転移学習の問題。(あ)は、転移学習の例としては不適切。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405299/e04aca02-f48b-a4ad-7d10-75bce5e5094c.png)

(答え)a
b
最大ではなく、必ず定義された2つの候補領域は生成されるように実装されている
c
2つの誤差の単純和ではなく、位置特定誤差と各クラスの確信度に対する二つの誤差の重みづけまで表現されている。
d
出力についてはカテゴリの＋の確率と、二つの候補領域が出力されるというところが間違い。

##確認テスト
VGG/GoogleNet/ResNetの特徴をそれぞれ述べよ
http://thunders1028.hatenablog.com/entry/2017/11/01/035609
〇VGG
AlexNetをより深くした、畳み込み層とプーリング層から成るどノーマルなCNNで、重みがある層(畳み込み層や全結合層)を16層、もしくは19層重ねたもの。それぞれVGG16やVGG19と呼ばれる。小さいフィルターを持つ畳み込み層を2〜4つ連続して重ね、それをプーリング層でサイズを半分にするというのを繰り返し行う構造が特徴。パラメータも多い。大きいフィルターで画像を一気に畳み込むよりも小さいフィルターを何個も畳み込む(=層を深くする)方が特徴をより良く抽出できる。
〇GoogleNet
通常の入力層から出力層まで縦一直線な構造ではなく、インセプション構造と呼ばれる横にも層が広がる構造をしている。このため、Inceptionモデルとも呼ばれる。横への層の広がりは、異なるサイズのフィルターの畳み込み層を複数横に並べて、それを結合するという形になっている。
〇ResNet
これまでのネットワークでは層を深くしすぎると性能が落ちるという問題があったが、それを「スキップ構造」によって解決し、152層もの深さを実現した。スキップ構造は、ある層への入力をバイパスし層をまたいで奥の層へ入力してしまうというもので、これにより勾配の消失や発散を防止し、超多層のネットワークを実現している。