Tensorflow案例4:Mnist手寫數字識別(線性神經網路)及其侷限性
學習目標
- 目標
- 應用matmul實現全連線層的計算
- 說明準確率的計算
- 應用softmax_cross_entropy_with_logits實現softamx以及交叉熵損失計算
- 說明全連線層在神經網路的作用
- 應用全連線神經網路實現影象識別
- 應用
- Mnist手寫數字勢識別
1、 資料集介紹
檔案說明:
- train-images-idx3-ubyte.gz: training set images (9912422 bytes)
- train-labels-idx1-ubyte.gz: training set labels (28881 bytes)
- t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)
- t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)
網址:http://yann.lecun.com/exdb/mnist/
1.1 特徵值
1.2 目標值
1.3 獲取介面
TensorFlow框架自帶了獲取這個資料集的介面,所以不需要自行讀取。
- from tensorflow.examples.tutorials.mnist import input_data
- mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)
- mnist.train.next_batch(100)(提供批量獲取功能)
- mnist.train.images、labels
- mnist.test.images、labels
- mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)
2、 實戰:Mnist手寫數字識別
2.1 網路設計
我們採取只有一層,最後一個輸出層的神經網路。也稱之為全連線(full connected)層神經網路。
2.1.1 全連線層計算
- tf.matmul(a, b,name=None)+bias
- return:全連線結果,供交叉損失運算
2.2 流程
1、準備資料
2、全連線結果計算
3、損失優化
4、模型評估(計算準確性)
mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)
# 1、準備資料
# x [None, 784] y_true [None. 10]
with tf.variable_scope("mnist_data"):
x = tf.placeholder(tf.float32, [None, 784])
y_true = tf.placeholder(tf.int32, [None, 10])
# 2、全連線層神經網路計算
# 類別:10個類別 全連線層:10個神經元
# 引數w: [784, 10] b:[10]
# 全連線層神經網路的計算公式:[None, 784] * [784, 10] + [10] = [None, 10]
# 隨機初始化權重偏置引數,這些是優化的引數,必須使用變數op去定義
with tf.variable_scope("fc_model"):
weight = tf.Variable(tf.random_normal([784, 10], mean=0.0, stddev=1.0), name="w")
bias = tf.Variable(tf.random_normal([10], mean=0.0, stddev=1.0), name="b")
# fc層的計算
# y_predict [None, 10]輸出結果,提供給softmax使用
y_predict = tf.matmul(x, weight) + bias
# 3、softmax迴歸以及交叉熵損失計算
with tf.variable_scope("softmax_crossentropy"):
# labels:真實值 [None, 10] one_hot
# logits:全臉層的輸出[None,10]
# 返回每個樣本的損失組成的列表
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true,
logits=y_predict))
# 4、梯度下降損失優化
with tf.variable_scope("optimizer"):
# 學習率
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
2.3 完善模型功能
- 1、增加準確率計算
- 2、增加變數tensorboard顯示
- 3、增加模型儲存載入
- 4、增加模型預測結果輸出
2.3.1 如何計算準確率
- equal_list = tf.equal(tf.argmax(y, 1), tf.argmax(y_label, 1))
- accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
完整程式碼
# -*- coding=utf-8 -*-
import os
# os.environ["TF_CPP_MIN_LOG_LEVEL"]='1' # 這是預設的顯示等級,顯示所有資訊
# os.environ["TF_CPP_MIN_LOG_LEVEL"]='2' # 只顯示 warning 和 Error
# os.environ["TF_CPP_MIN_LOG_LEVEL"]='3' # 只顯示 Error
import time
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 定義一個是否訓練/預測的標誌
tf.app.flags.DEFINE_integer("is_train", 1, "訓練or預測")
# 訓練步數
tf.app.flags.DEFINE_integer("train_step", 0, "訓練模型的步數")
# 定義模型的路徑
tf.app.flags.DEFINE_string("model_dir", " ", "模型儲存的路徑+模型名字")
FLAGS = tf.app.flags.FLAGS
# 此模型終端的執行方法
# python3 day02_05nn_mnist_FullDemo.py --is_train=1 --train_step=2000 # 訓練
# python3 day02_05nn_mnist_FullDemo.py --is_train=0 # 預測
# tensorboard 終端檢視
# tensorboard --logdir="./temp/summary/"
def full_connected_nn():
"""
全連線層神經網路進行Mnist手寫數字識別訓練
:return:
"""
mnist = input_data.read_data_sets("../data/mnist/input_data", one_hot=True)
# 1、獲得資料,定義特徵值和目標值張量
with tf.variable_scope("data"):
# 定義特徵值佔位符
x = tf.placeholder(tf.float32, [None, 784], name="feature")
# 定義目標值佔位符
y_true = tf.placeholder(tf.int32, [None, 10], name="label")
# 2、根據識別的類別數、建立全連線層網路
# 手寫數字10個類別
# 設計了一層的神經網路,最後一層,10個神經元
# 確定網路的引數weight [784, 10], bias [10]
# 要進行全連線層的矩陣運算 [None, 784] * [784, 10] + [10] = [None, 10]
with tf.variable_scope("fc_model"):
# 隨機初始化權重和偏置引數,要使用變數OP 定義
weights = tf.Variable(tf.random_normal([784, 10], mean=0, stddev=0.1), name="weights")
bias = tf.Variable(tf.random_normal([10], mean=0, stddev=1.0), name="bias")
# 全連線層運算 10個神經元 y_predict=[None, 10]
y_predict = tf.matmul(x, weights) + bias
# 3、根據輸出結果與真實結果建立softmax、交叉熵損失計算
with tf.variable_scope("soft_cross"):
# 先進行網路輸出的值的概率計算softmax,再進行交叉熵損失計算
all_loss = tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict, name="compute_loss")
# 求出平均損失
loss = tf.reduce_mean(all_loss)
# 4、定義梯度下降優化器進行優化
with tf.variable_scope("GD"):
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
# 5、求出每次訓練的準確率為
with tf.variable_scope("accuracy"):
# 求出每個樣本是否相等的一個列表
equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
# 計算相等的樣本的比例
accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
# 6、tensorflowboard展示的資料
# 1)收集要在tensorflowboard觀察的張量值
# 數值型 --> scalar 準確率, 損失值
tf.summary.scalar("loss", loss)
tf.summary.scalar("accuracy", accuracy)
# 維度高的張量值
tf.summary.histogram("w", weights)
tf.summary.histogram("b", bias)
# 2)合併變數
merged = tf.summary.merge_all()
# 7、建立儲存模型的OP
saver = tf.train.Saver()
# 8、開啟會話進行訓練
with tf.Session() as sess:
# 初始化變數OP
sess.run(tf.global_variables_initializer())
# 建立tensorboard的events檔案
filte_writer = tf.summary.FileWriter("./temp/summary/", graph=sess.graph)
# 9、載入本地模型繼續訓練或者拿來進行預測測試集
checkoutpoint = tf.train.latest_checkpoint("./temp/model/fc_nn_model")
# 判斷模型是否存在
if checkoutpoint:
saver.restore(sess, checkoutpoint)
# 判斷是訓練還是預測
if FLAGS.is_train == 1:
# 迴圈訓練
# for i in range(FLAGS.train_step):
for i in range(2000):
# 每批次給50個樣本
mnist_x, mnist_y = mnist.train.next_batch(50)
# 檢視資料的尺寸
# print(mnist_x.shape)
_, loss_run, accuracy_run, summary = sess.run([train_op, loss, accuracy, merged],
feed_dict={x: mnist_x, y_true: mnist_y})
# 列印每步訓練的效果
print("第{0}步的50個樣本損失為:{1}, 準確率為:{2}".format(i, loss_run, accuracy_run))
# 3) 寫入執行的結果到檔案當中
filte_writer.add_summary(summary, i)
# 每隔100步儲存一次模型的引數
if i % 100 == 0:
# saver.save(sess, FLAGS.model_dir)
saver.save(sess, save_path="./temp/fc_nn_model/fc_nn_model")
else:
# 進行預測
# 匯入模型
# 載入模型,從模型當中找出與當前訓練的模型程式碼當中(名字一樣的OP操作),覆蓋原來的值
checkoutpoint = tf.train.latest_checkpoint("./temp/fc_nn_model/")
# 判斷模型是否存在
if checkoutpoint:
saver.restore(sess, checkoutpoint)
# 預測100個樣本
N = 200
a = 0
for i in range(N):
image, label = mnist.test.next_batch(1)
# 真實的圖片數字
result_true = tf.argmax(label, 1).eval()
# 神經網路預測的數字
result_predict = tf.argmax(sess.run(y_predict, feed_dict={x: image, y_true: label}), 1).eval()
# 直接執行網路的輸出預測結果
print("第{0}樣本,真實的圖片數字為:{1}, 神經網路預測的數字為:{2}".format(
i,
result_true,
result_predict)
)
if result_true == result_predict:
a += 1
test_accuracy = (a / N) * 100
print("測試正確率:", test_accuracy, "%")
else:
print("模型不存在,checkoutpoint 請輸出入正確的模型路徑")
return None
if __name__ == '__main__':
start_time = time.time()
full_connected_nn()
end_time = time.time()
all_time = end_time - start_time
print("time:{:.2f} s" .format (all_time))
3、線性神經網路侷限性
任意多個隱層的神經網路和單層的神經網路都沒有區別,而且都是線性的,而且線性模型的能夠解決的問題也是有限的
1、 更復雜抽象的資料
一個單隱含層有更多的神經元,你就能捕捉更多的特徵。而且有更多隱層,意味著你能從資料集中提取更多複雜的結構。
1.1 增加網路深度
1.2 使用非線性啟用函式
2、神經網路更多特性
2.1黑盒子特點
- 增加網路的深度的確能夠達到效果,但是增加多少?這是一個不確定的問題,或者可以改變神經元的一些特點,改變結構同時增加網路深度,這些都有很多結構進行了嘗試。
- 不清楚網路內部每個神經元到底在什麼事情
2.2 更多發展
更多神經元 + 更深的網路 = 更復雜的抽象。這也是簡單的神經元如何變得更聰明,並在影象識別、圍棋這些特定問題上表現如此之好的原因。
2.2.1 神經網路拓展介紹
- 神經網路的種類
- 基礎神經網路:線性神經網路,BP神經網路,Hopfield神經網路等
- 進階神經網路:玻爾茲曼機,受限玻爾茲曼機,遞迴神經網路等
- 深度神經網路:深度置信網路,卷積神經網路,迴圈神經網路,LSTM網路等
Inception:谷歌公開的一個影象識別模型
2.3 兩大挑戰:計算能力和訓練資料
在此文章中,我們看到了一些 TensorFolw Playground 演示解釋了神經網路的機制和能力。就像你看到的那樣,這一技術的基礎非常簡單。每一個神經元只將一個數據點分類成兩個類別中的一個。然而,通過有更多的神經元和深度層,一個神經網路能提取出訓練資料集中隱藏的見解和複雜模式,並且建立抽象的層級結構。
接下來的問題是,為什麼如今還不是每個人都在使用這一偉大的技術?這是因為神經網路還有兩大挑戰。
- 第一個是訓練深度神經網路需要大量的算力。
- 第二,它們需要大量的訓練資料集。一個強力的 GPU 伺服器可能要花費數天、甚至數週的時間,才能使用數百萬張影象的資料集訓練出一個深度網路。
而且,為了得到最好的訓練結果,需要結合不同的網路設計與演算法,並進行大量的試錯。如今,一些研究者會使用幾十個 GPU 伺服器,甚至超級計算機來進行大規模分散式的訓練。但在不久的將來,全面管理的分散式訓練與預測服務——比如谷歌 TensorFlow 雲機器學習平臺——可能會解決這些問題,為大家提供成本合理的基於雲的 CPU 和 GPU,而且也可能會把大型的或深度的神經網路的能力開放給每一個人。