#!/usr/bin/env python
# coding: utf-8
# # Softmax Regressionの実験
# ## 1. MNISTデータセットの用意
# In[1]:
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
# In[2]:
# 表示してみる
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
fig = plt.figure(figsize=(12,6))
for i, img in enumerate(mnist.train.images[:25]):
ax = fig.add_subplot(5,5,i+1)
ax.imshow(img.reshape(28, 28), interpolation="none")
# ## 2. Softmax Regressionの実装
# - TensorFlowのプログラムは以下の2つの段階からなる
# - Computational Graphを構築(計算の依存関係の定義)
# - Computational Graphを実行(ここで初めて具体的な計算が行われる)
# ### 2.1 準備
# In[3]:
# import
import tensorflow as tf
# ログ保存用のディレクトリがあれば一度削除し、新たに作りなおす
LOG_DIR = "/tmp/softmax"
if tf.gfile.Exists(LOG_DIR):
tf.gfile.DeleteRecursively(LOG_DIR)
tf.gfile.MakeDirs(LOG_DIR)
# In[4]:
# TensorBoardのための準備
def variable_summaries(var):
""" テンソルに様々なsummaryを付加する(TensorBoardでの可視化に利用)
Args:
var: summaryを付加したいテンソル
Returns:
なし(summary opの付加を行うだけの関数)
"""
with tf.name_scope('summaries'):
mean = tf.reduce_mean(var)
tf.summary.scalar('mean', mean)
with tf.name_scope('stddev'):
stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
tf.summary.scalar('stddev', stddev)
tf.summary.scalar('max', tf.reduce_max(var))
tf.summary.scalar('min', tf.reduce_min(var))
tf.summary.histogram('histogram', var)
# ### 2.2 Computational Graphの構築
# In[5]:
with tf.name_scope("input"):
# データを与えるためのplaceholder
x = tf.placeholder(tf.float32, [None, 784], name = "image") # input image
y_ = tf.placeholder(tf.float32, [None, 10], name = "label") # label
with tf.name_scope("variables"):
# 学習可能なパラメータ
with tf.name_scope("Weight"):
W = tf.Variable(tf.zeros([784, 10]))
variable_summaries(W)
with tf.name_scope("Bias"):
b = tf.Variable(tf.zeros([10]))
variable_summaries(b)
# In[6]:
def inference(images, weights, biases):
""" 画像からそのクラスを予想する
Args:
images: 画像のplaceholder
weights: 重みのVariable
biases: バイアスのVariable
Returns:
logits: 計算されたlogit
"""
with tf.name_scope("logits"):
logits = tf.matmul(images, weights) + biases
tf.summary.histogram("logits_histogram", logits)
return logits
# In[7]:
def loss(logits, labels):
""" logitとlabelから誤差関数(交差エントロピー)を計算する
Args:
logits:識別器の予測
labels:正しいラベル
Returns:
cross_entropy: 交差エントロピー
"""
with tf.name_scope("loss"):
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits), name="cross_entropy_mean")
tf.summary.scalar('cross_entropy', cross_entropy) # ログをとる
return cross_entropy
# In[8]:
def train(loss, learning_rate):
""" 訓練用のopを生成する
Args:
loss
learning_rate
Returns:
The op for training
"""
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_op = optimizer.minimize(loss)
return train_op
# In[9]:
def evaluation(logits, labels):
""" logitsがlabelsを予測する精度を評価する
Args:
logits
labels
Returns:
正解率
"""
with tf.name_scope("evaluation"):
correct = tf.equal(tf.argmax(logits, 1), tf.argmax(labels, 1))
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
tf.summary.scalar("accuracy", accuracy) # ログをとる
return accuracy
# In[10]:
# グラフを構築
logits = inference(x, W, b)
loss = loss(logits, y_)
train_op = train(loss, 0.1)
accuracy = evaluation(logits, y_)
summary = tf.summary.merge_all() # すべてのsummaryをひとまとめにするop
# ### 2.3 Computational Graphの実行
# In[11]:
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
# In[12]:
# summaryをファイルに出力するためのwriter
train_writer = tf.summary.FileWriter(LOG_DIR+"/train", sess.graph) # sess.graphを渡すことでComputational GraphをTensorBoardに可視化
test_writer = tf.summary.FileWriter(LOG_DIR+"/test")
# In[13]:
MAX_STEP = 2000
BATCH_SIZE = 100
for step in range(MAX_STEP):
batch_xs, batch_ys = mnist.train.next_batch(BATCH_SIZE)
sess.run(train_op, feed_dict={x: batch_xs, y_: batch_ys}) # trainを実行
# 100stepごとにテスト結果を表示
if step % 100 == 0:
test_loss, test_accuracy = sess.run([loss, accuracy], feed_dict={x: mnist.test.images, y_: mnist.test.labels})
print("step: {}, test_accuracy: {}, test_loss: {}".format(step, test_accuracy, test_loss))
# ログの実行
train_summary_str = sess.run(summary, feed_dict={x: batch_xs, y_: batch_ys})
test_summary_str = sess.run(summary, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
train_writer.add_summary(train_summary_str, step)
test_writer.add_summary(test_summary_str, step)
train_writer.close()
test_writer.close()
# #### テストデータに対する正答率
# In[14]:
print("Accuracy: {}".format(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})))
# ## 3. TensorBoardによる可視化
# ### 3.1 TensorBoardの起動
# LOG_DIRを/tmp/softmaxにしていた場合、
# ~~~bash
# $ tensorboard --logdir=/tmp/softmax
# ~~~
# によってTensorBoardを起動する。
# > Starting TensorBoard 41 on port 6006
#
# のように表示されるので、ブラウザで
#
# http://localhost:6006/
#
# を開く。
#
# ### 3.2 スクリーンショット
# - SCALARS
#
#
# - DISTRIBUTIONS
#
#
# - HISTOGRAMS
#
#
# - GRAPHS
#
#
# ## 4. 重みの可視化
# Softmax Regressionは、
# + 重み行列Wの各ベクトル(今回は10クラスなので10個)と入力画像との内積をとる
# + バイアスを足してSoftmaxする
#
# という計算によって事後確率を求めている。したがって、「最もiと認識されやすい画像」は重みのi番目のベクトル(i=0, 1, ..., 9)によって得ることができる。
# In[15]:
fig = plt.figure(figsize=(12,6))
for i in range(W.eval().shape[1]):
ax = fig.add_subplot(2,5,i+1)
ax.imshow(W.eval()[:, i].reshape(28, 28), interpolation="none")
# ## 5. 識別器をだましてみる
# 様々な文字画像に先述の重み画像を混ぜあわせることを考える。
#
# 人間の目には依然として容易に識別できるにもかかわらず、識別器は誤って識別するようになる。例えば"7"に対応する重みを混ぜあわせると、多くの画像を"7"と予想するようになる。
# In[16]:
# "7"に対応する重みをテスト用画像に足し合わせて表示する
import numpy as np
fake_x = np.array([img + W.eval()[:, 7] for img in mnist.test.images[:25]])
fig = plt.figure(figsize=(12,6))
for i, img in enumerate(fake_x):
ax = fig.add_subplot(5,5,i+1)
ax.imshow(img.reshape(28, 28), interpolation="none")
# In[17]:
# 上の画像の多くを"7"と予想するようになる
print(sess.run(tf.argmax(logits, 1), feed_dict={x: fake_x}))
# ## 6. 自信があるほどよく当たるのか
# Softmax Regressionでは各クラスの事後確率を推定し、事後確率が最大となるクラスをモデルの予想としている。事後確率の最大値が90%となる入力画像と50%となる入力画像では、前者の方が正答率が高いはず。それを確かめてみる。
# In[18]:
posteriors = sess.run(tf.nn.softmax(logits), feed_dict={x: mnist.test.images})# テストデータに対してモデルが出力した事後確率
print(posteriors.shape)
is_true = np.argmax(posteriors, 1) == np.argmax(mnist.test.labels, 1) # 正解したデータにのみTrueが入ったarray
print(is_true.shape)
# - 正しく識別できたデータについて、正しいクラスに対する事後確率がどのような値をとっていたかを表すヒストグラムを描画
# In[19]:
plt.hist(np.max(posteriors[is_true], 1))
# - 識別に失敗したデータについて、間違えて予測したクラスに対する事後確率がどのような値をとっていたかを表すヒストグラムを描画
# In[20]:
plt.hist(np.max(posteriors[~is_true], 1))
# In[21]:
# 事後確率のクラス間の最大値を0.1刻みにしたもの
floor_posteriors = np.array(map(lambda x: int(x*10)/10., np.max(posteriors, 1)))
# In[22]:
# モデルの予想した事後確率と、正解(1)不正解(0)をたばねたもの
count = np.concatenate((np.vstack(floor_posteriors), np.vstack(is_true.astype(int))), axis=1)
print(count)
# In[23]:
# 事後確率が最大となるクラスにおける事後確率ごとに、正答率を記録する
h = []
for i in range(1, 10):
a = np.array(filter(lambda x: x[0]==i/10., count))
if a.any():
h.append([i/10., np.sum(a[:, 1])/a.shape[0] ])
h = np.array(h)
print(h)
# In[24]:
plt.xlabel("Max posterior")
plt.ylabel("Accuracy")
plt.plot(h[:, 0], h[:, 1])
# In[ ]: