學習目標
- 目標
- 知道softmax迴歸的原理
- 應用softmax_cross_entropy_with_logits實現softamx以及交叉熵損失計算
- 應用matmul實現多隱層神經網絡的計算
- 應用
- 應用TensorFlow完成Mnist手寫數字勢識別
到目前爲止,我們所接觸的都是二分類問題,神經網絡輸出層只有一個神經元,表示預測輸出\hat{y}y^是正類的概率{P}(y=1|x), \hat{y} > {0.5}P(y=1∣x),y^>0.5則判斷爲正類,反之判斷爲負類。那麼對於多分類問題怎麼辦?
2.1.1 Softmax 迴歸
對於多分類問題,用 N表示種類個數,那麼神經網絡的輸出層的神經元個數必須爲L[output]=N, 每個神經元的輸出依次對應屬於N個類別當中某個具體類別的概率,即 P(y=N_1|x),..,P(y=N_n|x)P(y=N1∣x),..,P(y=Nn∣x)。
輸出層即:
Z^{[L]} = W^{[L]}a^{[L-1]} + b^{[L]}Z[L]=W[L]a[L−1]+b[L],Z的輸出值個數爲類別個數
需要對所有的輸出結果進行一下softmax公式計算:
a^{[L]}_i = \frac{e^{Z^{[L]}_i}}{\sum^C_{i=1}e^{Z^{[L]}_i}}ai[L]=∑i=1CeZi[L]eZi[L],並且滿足\sum^C_{i=1}a^{[L]}_i = 1∑i=1Cai[L]=1,我們來看一下計算案例:
2.1.2 交叉熵損失
對於softmax迴歸(邏輯迴歸代價函數的推廣,都可稱之爲交叉熵損失),它的代價函數公式爲:
L(\hat y, y) = -\sum^C_{j=1}y_jlog\hat y_jL(y^,y)=−∑j=1Cyjlogy^j
總損失函數可以記爲J = \frac{1}{m}\sum^m_{i=1}L(\hat y, y)J=m1∑i=1mL(y^,y)
邏輯迴歸的損失也可以這樣表示,:
所以與softmax是一樣的,一個二分類一個多分類衡量。
對於真實值會進行一個one-hot編碼,每一個樣本的所屬類別都會在某個類別位置上標記。
上圖改樣本的損失值爲:
0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)+0log(0.05)+0log(0.10)+0log(0.10)0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)+0log(0.05)+0log(0.10)+0log(0.10)
注:關於one_hot編碼
框架使用
-
便於編程:包括神經網絡的開發和迭代、配置產品;
-
運行速度:特別是訓練大型數據集時;
目前最火的深度學習框架大概是 Tensorflow 了。Tensorflow 框架內可以直接調用梯度下降算法,極大地降低了編程人員的工作量。例如以下代碼:
2.1.3 案例:Mnist手寫數字識別神經網絡實現
2.1.3.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)
2.1.3.2 特徵值
2.1.3.3 目標值
2.1.3.4 Mnist數據獲取API
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.1.3.5 網絡設計
我們採取兩個層,除了輸入層之外。第一個隱層中64個神經元,最後一個輸出層(全連接層)我們必須設置10個神經元的神經網絡。
2.1.3.6 全連接層計算
- tf.matmul(a, b,name=None)+bias
- return:全連接結果,供交叉損失運算
- tf.train.GradientDescentOptimizer(learning_rate)
- 梯度下降
- learning_rate:學習率
- method:
- minimize(loss):最小優化損失
2.1.3.7 前期確定事情與流程
- 確定網絡結構以及形狀
- 第一層參數:輸入:x [None, 784] 權重:[784, 64] 偏置[64],輸出[None, 64]
- 第二層參數:輸入:[None, 64] 權重:[64, 10] 偏置[10],輸出[None, 10]
- 流程:
- 獲取數據
- 前向傳播:網絡結構定義
- 損失計算
- 反向傳播:梯度下降優化
- 功能完善
- 準確率計算
- 添加Tensorboard觀察變量、損失變化
- 訓練模型保存、模型存在加載模型進行預測
2.1.3.8 主網絡搭建流程
- 獲取數據
mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)
- 定義數據佔位符,Mnist數據實時提供給placeholder
# 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去定義
# 要進行全連接層的矩陣運算 [None, 784]*[784, 64] + [64] = [None,64]
# [None, 64]*[64, 10] + [10] = [None,10]
with tf.variable_scope("fc_model"):
# 第一層:隨機初始化權重和偏置參數,要使用變量OP 定義
weight_1 = tf.Variable(tf.random_normal([784, 64], mean=0.0, stddev=1.0),
name="weightes_1")
bias_1 = tf.Variable(tf.random_normal([64], mean=0.0, stddev=1.0),
name='biases_1')
# 第二層:隨機初始化權重和偏置參數,要使用變量OP 定義
weight_2 = tf.Variable(tf.random_normal([64, 10], mean=0.0, stddev=1.0),
name="weightes_2")
bias_2 = tf.Variable(tf.random_normal([10], mean=0.0, stddev=1.0),
name='biases_2')
# 全連接層運算
# 10個神經元
# y_predict = [None,10]
y1 = tf.matmul(x, weight_1) + bias_1
y_predict = tf.matmul(y1, weight_2) + bias_2
- 損失計算與優化
# 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.1.3.9 完善模型功能
- 1、增加準確率計算
- 2、增加變量tensorboard顯示
- 3、增加模型保存加載
- 4、增加模型預測結果輸出
如何計算準確率
- equal_list = tf.equal(tf.argmax(y, 1), tf.argmax(y_label, 1))
-
accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
-
模型評估(計算準確性)
# 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))
增加變量tensorboard顯示
- 在會話外當中增加以下代碼
# (1)、收集要顯示的變量
# 先收集損失和準確率
tf.summary.scalar("losses", loss)
tf.summary.scalar("acc", accuracy)
# 維度高的張量值
tf.summary.histogram("w1", weight_1)
tf.summary.histogram("b1", bias_1)
# 維度高的張量值
tf.summary.histogram("w2", weight_2)
tf.summary.histogram("b2", bias_2)
# 初始化變量op
init_op = tf.global_variables_initializer()
# (2)、合併所有變量op
merged = tf.summary.merge_all()
- 在會話當中去創建文件寫入每次的變量值
# (1)創建一個events文件實例
file_writer = tf.summary.FileWriter("./tmp/summary/", graph=sess.graph)
# 運行合變量op,寫入事件文件當中
summary = sess.run(merged, feed_dict={x: mnist_x, y_true: mnist_y})
file_writer.add_summary(summary, i)
增加模型保存加載
創建Saver,然後保存
# 創建模型保存和加載
saver = tf.train.Saver()
# 每隔100步保存一次模型
if i % 100 == 0:
saver.save(sess, "./tmp/modelckpt/fc_nn_model")
在訓練之前加載模型
# 加載模型
if os.path.exists("./tmp/modelckpt/checkpoint"):
saver.restore(sess, "./tmp/modelckpt/fc_nn_model")
增加模型預測結果輸出
增加標誌位
tf.app.flags.DEFINE_integer("is_train", 1, "指定是否是訓練模型,還是拿數據去預測")
FLAGS = tf.app.flags.FLAGS
然後判斷是否訓練,如果不是訓練就直接預測,利用tf.argmax對樣本的真實目標值y_true,和預測的目標值y_predict求出最大值的位置
# 如果不是訓練,我們就去進行預測測試集數據
for i in range(100):
# 每次拿一個樣本預測
mnist_x, mnist_y = mnist.test.next_batch(1)
print("第%d個樣本的真實值爲:%d, 模型預測結果爲:%d" % (
i+1,
tf.argmax(sess.run(y_true, feed_dict={x: mnist_x, y_true: mnist_y}), 1).eval(),
tf.argmax(sess.run(y_predict, feed_dict={x: mnist_x, y_true: mnist_y}), 1).eval()
)
)
4.7.3.6 完整代碼
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 定義一個是否訓練、預測的標誌
tf.app.flags.DEFINE_integer("is_train", 1, "訓練or預測")
FLAGS = tf.app.flags.FLAGS
def full_connected_nn():
"""
全連接層神經網絡進行Mnist手寫數字識別訓練
:return:
"""
mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)
# 1、獲取數據,定義特徵之和目標值張量
# x
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, 64] + [64] = [None,64]
# [None, 64]*[64, 10] + [10] = [None,10]
with tf.variable_scope("fc_model"):
# 第一層:隨機初始化權重和偏置參數,要使用變量OP 定義
weight_1 = tf.Variable(tf.random_normal([784, 64], mean=0.0, stddev=1.0),
name="weightes_1")
bias_1 = tf.Variable(tf.random_normal([64], mean=0.0, stddev=1.0),
name='biases_1')
# 第二層:隨機初始化權重和偏置參數,要使用變量OP 定義
weight_2 = tf.Variable(tf.random_normal([64, 10], mean=0.0, stddev=1.0),
name="weightes_2")
bias_2 = tf.Variable(tf.random_normal([10], mean=0.0, stddev=1.0),
name='biases_2')
# 全連接層運算
# 10個神經元
# y_predict = [None,10]
y1 = tf.matmul(x, weight_1) + bias_1
y_predict = tf.matmul(y1, weight_2) + bias_2
# 3、根據輸出結果與真是結果建立softmax、交叉熵損失計算
with tf.variable_scope("softmax_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))
# 1、收集要在tensorboard觀察的張量值
# 數值型-->scalar 準確率,損失
tf.summary.scalar("loss", loss)
tf.summary.scalar("acc", accuracy)
# 維度高的張量值
tf.summary.histogram("w1", weight_1)
tf.summary.histogram("b1", bias_1)
# 維度高的張量值
tf.summary.histogram("w2", weight_2)
tf.summary.histogram("b2", bias_2)
# 2、合併變量
merged = tf.summary.merge_all()
# 1、創建保存模型的OP
saver = tf.train.Saver()
# 開啓會話進行訓練
with tf.Session() as sess:
# 初始化變量OP
sess.run(tf.global_variables_initializer())
# 創建events文件
file_writer = tf.summary.FileWriter("./tmp/summary/", graph=sess.graph)
# 加載本地模型繼續訓練或者拿來進行預測測試集
# 加載模型,從模型當中找出與當前訓練的模型代碼當中(名字一樣的OP操作),覆蓋原來的值
ckpt = tf.train.latest_checkpoint("./tmp/model/")
# 判斷模型是否存在
if ckpt:
saver.restore(sess, ckpt)
if FLAGS.is_train == 1:
# 循環訓練
for i in range(2000):
# 每批次給50個樣本
mnist_x, mnist_y = mnist.train.next_batch(50)
_, loss_run, acc_run, summary = sess.run([train_op, loss, accuracy, merged],
feed_dict={x: mnist_x, y_true: mnist_y})
print("第 %d 步的50個樣本損失爲:%f , 準確率爲:%f" % (i, loss_run, acc_run))
# 3、寫入運行的結果到文件當中
file_writer.add_summary(summary, i)
# 每隔100步保存一次模型的參數
if i % 100 == 0:
saver.save(sess, "./tmp/model/fc_nn_model")
else:
# 進行預測
# 預測100個樣本
for i in range(100):
# label [1, 10]
image, label = mnist.test.next_batch(1)
# 直接運行網絡的輸出預測結果
print("第 %d 樣本,真實的圖片數字爲:%d, 神經網絡預測的數字爲:%d " % (
i,
tf.argmax(label, 1).eval(),
tf.argmax(sess.run(y_predict, feed_dict={x: image, y_true: label}), 1).eval()
))
return None
if __name__ == '__main__':
full_connected_nn()
4.7.4 調整學習率調整網絡參數帶來的問題
如果我們對網絡當中的學習率進行修改,也就是一開始我們並不會知道學習率填哪些值,也並不知道調整網絡的參數大小帶來的影響(第一部分第四節)。
- 假設學習率調整到1,2
- 假設參數調整到比較大的值,幾十、幾百
總結:參數調整了之後可能沒影響,是因爲網絡較小,可能並不會造成後面所介紹的梯度消失或者梯度爆炸
4.7.5總結
- 掌握softmax公式以及特點
- tensorflow.examples.tutorials.mnist.input_data 獲取Mnist數據
- tf.matmul(a, b,name=None)實現全連接層計算
- tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)實現梯度下降優化