人工智能實踐:全連接網絡基礎
MNIST數據集
MNIST數據集:包含7萬張黑底白字手寫數字圖片,其中55000張為訓練集, 5000張為驗證集,10000張為測試集。每張圖片大小為28*28像素,圖片中純黑色像素值為0,純白色像素值為1。數據集的標簽是長度為10的一維數組,數組中每個元素索引號表示對應數字出現的概率。 在將mnist數據集作為輸入餵入神經網絡時,需先將數據集中每張圖片變為長度 784一維數組,將該數組作為神經網絡輸入特征餵入神經網絡。
例如: 一張數字手寫體圖片變成長度為784的一維數組[0.0.0.0.0.231 0.235 0.459 ……0.219 0.0.0.0.]輸入神經網絡。該圖片對應的標簽為[0.0.0.0.0.0.1.0.0.0],標簽中索引號為6的元素為1,表示是數字6出現的概率為100%,則該圖片對應的識別結果是6。
使用input_data模塊中的read_data_sets()函數加載mnist數據集:
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(’./data/’,one_hot=True)
在 read_data_sets()函數中有兩個參數,第一個參數表示數據集存放路徑,第二個參數表示數據集的存取形式。當第二個參數為Ture時,表示以獨熱碼形式存取數據集。read_data_sets()函數運行時,會檢查指定路徑內是否已經有數據集,若指定路徑中沒有數據集,則自動下載,並將mnist數據集分為訓練集train、驗證集validation和測試集test存放。
返回mnist數據集中訓練集train、驗證集validation和測試集test樣本數
在Tensorflow中用以下函數返回子集樣本數:
①返回訓練集train樣本數
print “train data size:”,mnist.train.num_examples
輸出結果:train data size:55000
②返回驗證集validation樣本數
print “validationdata size:”,mnist.validation.num_examples
輸出結果:validationdata size:5000
③返回測試集test樣本數
print “test data size:”,mnist.test.num_examples
輸出結果:test data size:10000
使用train.labels函數返回mnist數據集標簽
例如: 在mnist數據集中,若想要查看訓練集中第0張圖片的標簽,則使用如下函數
mnist.train.labels[0] 輸出結果:
array([0.,0.,0.,0.,0.,0.,1.,0.,0.,0])
使用train.images函數返回mnist數據集圖片像素值
例如: 在 mnist 數據集中,若想要查看訓練集中第 0 張圖片像素值,則使用如下函數
mnist.train.images[0]
輸出結果:
array([0. ,0. ,0. ,
0. ,0. ,0. ,
0. ,0. ,0. ,
… … …])
使用mnist.train.next_batch()函數將數據輸入神經網絡
例如:
BATCH_SIZE = 200
xs,ys = mnist.train.next_batch(BATCH_SIZE)
print “xsshape:”,xs.shape
print “ys shape:”,ys.shape
輸出結果:xs.shape(200,784)
輸出結果:ys.shape(200,10)
其中,mnist.train.next_batch()函數包含一個參數BATCH_SIZE,表示隨機從訓練集中抽取BATCH_SIZE個樣本輸入神經網絡,並將樣本的像素值和標簽分別賦給xs和ys。在本例中,BATCH_SIZE設置為200,表示一次將200個樣本的像素值和標簽分別賦值給xs和ys,故xs的形狀為(200,784),對應的ys的形狀為(200,10)。
實現“Mnist數據集手寫數字識別”的常用函數:
①tf.get_collection(“”)函數表示從 collection 集合中取出全部變量生成一個列表。
②tf.add( )函數表示將參數列表中對應元素相加。
例如:
x=tf.constant([[1,2],[1,2]]) y=tf.constant([[1,1],[1,2]]) z=tf.add(x,y) print z
輸出結果:
[[2,3],[2,4]]
③tf.cast(x,dtype)函數表示將參數x轉換為指定數據類型。
例如:
A = tf.convert_to_tensor(np.array([[1,1,2,4], [3,4,8,5]]))
print A.dtype b = tf.cast(A, tf.float32) print b.dtype
結果輸出:
<dtype: ‘int64‘>
<dtype: ‘float32‘>
從輸出結果看出,將矩陣A由整數型變為32位浮點型。
④tf.equal( )函數表示對比兩個矩陣或者向量的元素。若對應元素相等,則返回True;若對應元素不相等,則返回False。
例如:
A = [[1,3,4,5,6]] B =[[1,3,4,3,2]]
with tf.Session( ) as sess:
print(sess.run(tf.equal(A, B)))
輸出結果:
[[True True True False False]]
在矩陣A和B中,第1、2、3個元素相等,第4、5個元素不等,故輸出結果中,第1、2、3個元素取值為True,第4、5個元素取值為False。
⑤tf.reduce_mean(x,axis)函數表示求取矩陣或張量指定維度的平均值。若不指定第二個參數,則在所有元素中取平均值;若指定第二個參數為0,則在第一維元素上取平均值,即每一列求平均值;若指定第二個參數為1,則在第二維元素上取平均值,即每一行求平均值。
例如:
x = [[1., 1.][2., 2.]]
print(tf.reduce_mean(x))
輸出結果:1.5
print(tf.reduce_mean(x, 0))
輸出結果:[1.5, 1.5]
print(tf.reduce_mean(x, 1))
輸出結果:[1., 1.]
⑥tf.argmax(x,axis)函數表示返回指定維度axis下,參數x中最大值索引號。
例如:
在tf.argmax([1,0,0],1)函數中,axis為1,參數x為[1,0,0],表示在參數x
的第一個維度取最大值對應的索引號,故返回0。
⑦os.path.join()函數表示把參數字符串按照路徑命名規則拼接。
例如:
import os os.path.join(‘/hello/‘,‘good/boy/‘,‘doiido‘)
輸出結果:‘/hello/good/boy/doiido‘
⑧字符串.split( )函數表示按照指定“拆分符”對字符串拆分,返回拆分列表。
例如:‘./model/mnist_model-1001‘.split(‘/‘)[-1].split(‘-‘)[-1]
在該例子中,共進行兩次拆分。第一個拆分符為‘/’,返回拆分列表,並提取列表中索引為-1的元素即倒數第一個元素;第二個拆分符為‘-’,返回拆分列表,並提取列表中索引為-1的元素即倒數第一個元素,故函數返回值為1001。
⑨tf.Graph( ).as_default( )函數表示將當前圖設置成為默認圖,並返回一個上下文管理器。該函數一般與with關鍵字搭配使用,應用於將已經定義好的神經網絡在計算圖中復現。
例如:
with tf.Graph().as_default() as g,表示將在Graph()內定義的節點加入到計算圖g中。
神經網絡模型的保存
在反向傳播過程中,一般會間隔一定輪數保存一次神經網絡模型,並產生三個文件(保存當前圖結構的.meta文件、保存當前參數名的.index文件、保存當前參數的.data文件),在Tensorflow中如下表示:
saver = tf.train.Saver()
with tf.Session() as sess:
for i in range(STEPS):
if i % 輪數 == 0:
saver.save(sess, os.path.join(MODEL_SAVE_PATH,MODEL_NAME),global_step=global_step)
其中,tf.train.Saver()用來實例化saver對象。上述代碼表示,神經網絡每循環規定的輪數,將神經網絡模型中所有的參數等信息保存到指定的路徑中,並在存放網絡模型的文件夾名稱中註明保存模型時的訓練輪數。
神經網絡模型的加載 在測試網絡效果時,需要將訓練好的神經網絡模型加載,在Tensorflow中這樣表示:
with tf.Session() as sess:
ckpt =tf.train.get_checkpoint_state(存儲路徑)
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess,ckpt.model_checkpoint_path)
在with結構中進行加載保存的神經網絡模型,若ckpt和保存的模型在指定路徑中存在,則將保存的神經網絡模型加載到當前會話中。
加載模型中參數的滑動平均值
在保存模型時,若模型中采用滑動平均,則參數的滑動平均值會保存在相應文件中。通過實例化saver對象,實現參數滑動平均值的加載,在Tensorflow中如下表示:
ema = tf.train.ExponentialMovingAverage(滑動平均基數)
ema_restore = ema.variables_to_restore()
saver = tf.train.Saver(ema_restore)
神經網絡模型準確率評估方法在網絡評估時,一般通過計算在一組數據上的識別準確率,評估神經網絡的效果。
在Tensorflow中這樣表示:
correct_prediction= tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy =tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
在上述中,y 表示在一組數據(即batch_size 個數據)上神經網絡模型的預測結果,y 的形狀為[batch_size,10],每一行表示一張圖片的識別結果。通過tf.argmax()函數取出每張圖片對應向量中最大值元素對應的索引值,組成長度為輸入數據batch_size個的一維數組。通過tf.equal()函數判斷預測結果張量和實際標簽張量的每個維度是否相等,若相等則返回True,不相等則返回False。通過 tf.cast()函數將得到的布爾型數值轉化為實數型,再通過tf.reduce_mean()函數求平均值,最終得到神經網絡模型在本組數據上的準確率。
模塊化搭建神經網絡
神經網絡八股包括前向傳播過程、反向傳播過程、反向傳播過程中用到的正則化、指數衰減學習率、滑動平均方法的設置、以及測試模塊。
前向傳播過程(forward.py)
前向傳播過程完成神經網絡的搭建,結構如下:
def forward(x, regularizer):
def forward(x, regularizer):
w=
b=
y=
return y
def get_weight(shape, regularizer):
def get_bias(shape):
前向傳播過程中,需要定義神經網絡中的參數w和偏置b,定義由輸入到輸出的網絡結構。通過定義函數get_weight()實現對參數w的設置,包括參數w的形狀和是否正則化的標誌。同樣,通過定義函數get_bias()實現對偏置b的設置。
反向傳播過程(backword.py)
反向傳播過程完成網絡參數的訓練,結構如下:
def backward( mnist ):
x = tf.placeholder(dtype, shape )
y_ = tf.placeholder(dtype, shape )
#定義前向傳播函數
y = forward( )
global_step =
loss =
train_step =tf.train.GradientDescentOptimizer(learning_rate).
minimize(loss,global_step=global_step)
#實例化saver對象
saver = tf.train.Saver() with tf.Session() as sess:
#初始化所有模型參數
tf.initialize_all_variables().run()
#訓練模型
for i in range(STEPS):
sess.run(train_step,feed_dict={x: , y_: })
if i % 輪數 == 0:
print
saver.save( )
反向傳播過程中,用tf.placeholder(dtype, shape)函數實現訓練樣本x和樣本標簽y_占位,函數參數dtype表示數據的類型,shape表示數據的形狀;y表示定義的前向傳播函數forward;loss表示定義的損失函數,一般為預測值與樣本標簽的交叉熵(或均方誤差)與正則化損失之和;train_step表示利用優化算法對模型參數進行優化,常用優化算法GradientDescentOptimizer、AdamOptimizer、MomentumOptimizer算法,在上述代碼中使用的GradientDescentOptimizer優化算法。接著實例化saver對象,其中利用tf.initialize_all_variables().run()函數實例化所有參數模型,利用sess.run( )函數實現模型的訓練優化過程,並每間隔一定輪數保存一次模型。
正則化、指數衰減學習率、滑動平均方法的設置
①正則化項regularization
當在前向傳播過程中即forward.py文件中,設置正則化參數regularization為 1時,則表明在反向傳播過程中優化模型參數時,需要在損失函數中加入正則化項。 結構如下:
首先,需要在前向傳播過程即forward.py文件中加入
if regularizer !=None: tf.add_to_collection(‘losses‘, tf.contrib.layers.l2_regularizer(regularizer)(w))
其次,需要在反向傳播過程即backword.py文件中加入
ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=tf.argmax(y_, 1)) cem = tf.reduce_mean(ce)
loss = cem + tf.add_n(tf.get_collection(‘losses‘))
其中,tf.nn.sparse_softmax_cross_entropy_with_logits()表示softmax()函數與交叉熵一起使用。
②指數衰減學習率 在訓練模型時,使用指數衰減學習率可以使模型在訓練的前期快速收斂接近較優解,又可以保證模型在訓練後期不會有太大波動。運用指數衰減學習率,需要在反向傳播過程即backword.py文件中加入:
learning_rate = tf.train.exponential_decay( LEARNING_RATE_BASE, global_step,
LEARNING_RATE_STEP, LEARNING_RATE_DECAY, staircase=True)
③滑動平均 在模型訓練時引入滑動平均可以使模型在測試數據上表現的更加健壯。
需要在反向傳播過程即backword.py文件中加入:
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY,global_step)
ema_op = ema.apply(tf.trainable_variables()) with tf.control_dependencies([train_step, ema_op]):
train_op = tf.no_op(name=‘train‘)
測試過程(test.py)
當神經網絡模型訓練完成後,便可用於測試數據集,驗證神經網絡的性能。結構
如下: 首先,制定模型測試函數test()
def test( mnist ):
with tf.Graph( ).as_default( ) as g:
#給x y_占位
x = tf.placeholder(dtype,shape)
y_ = tf.placeholder(dtype,shape) #前向傳播得到預測結果y
y = mnist_forward.forward(x,None) #前向傳播得到y
#實例化可還原滑動平均的saver
ema =tf.train.ExponentialMovingAverage(滑動衰減率)
ema_restore =ema.variables_to_restore()
saver =tf.train.Saver(ema_restore)
#計算正確率
correct_prediction =tf.equal(tf.argmax(y,1),tf.argmax(y_,1))
accuracy =tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
while True:
with tf.Session() as sess:
#加載訓練好的模型
ckpt = tf.train.get_checkpoint_state(存儲路徑)
#如果已有ckpt模型則恢復
if ckpt andckpt.model_checkpoint_path:
#恢復會話
saver.restore(sess, ckpt.model_checkpoint_path)
#恢復輪數
global_ste = ckpt.model_checkpoint_path.split(‘/‘)[-1].split(‘-‘)[-1]
#計算準確率
accuracy_score = sess.run(accuracy,feed_dict={x:測試數據, y_:測試數據標簽 })
# 打印提示
print("After %straining step(s), test accuracy=%g" % (global_step, accuracy_score))
#如果沒有模型
else:
print(‘No checkpoint file found‘) #模型不存在提示
return
其次,制定main()函數 def main():
#加載測試數據集
mnist =input_data.read_data_sets("./data/", one_hot=True)
#調用定義好的測試函數test()
test(mnist) if __name__ == ‘__main__‘:
main()
通過對測試數據的預測得到準確率,從而判斷出訓練出的神經網絡模型的性能好壞。當準確率低時,可能原因有模型需要改進,或者是訓練數據量太少導致過擬合。
手寫數字識別準確率輸出
實現手寫體mnist數據集的識別任務,共分為三個模塊文件,分別是描述網絡結構的前向傳播過程文件(mnist_forward.py)、描述網絡參數優化方法的反向傳播過程文件(mnist_backward.py)、驗證模型準確率的測試過程文件(mnist_test.py)。
前向傳播過程文件(mnist_forward.py)
在前向傳播過程中,需要定義網絡模型輸入層個數、隱藏層節點數、輸出層個數,定義網絡參數w、偏置b,定義由輸入到輸出的神經網絡架構。
實現手寫體mnist數據集的識別任務前向傳播過程如下:
#coding:utf-8
#1前向傳播過程
import tensorflow as tf
#網絡輸入節點為784個(代表每張輸入圖片的像素個數)
INPUT_NODE = 784
#輸出節點為10個(表示輸出為數字0-9的十分類)
OUTPUT_NODE = 10
#隱藏層節點500個
LAYER1_NODE = 500
def get_weight(shape, regularizer):
#參數滿足截斷正態分布,並使用正則化,
w = tf.Variable(tf.truncated_normal(shape,stddev=0.1))
#w = tf.Variable(tf.random_normal(shape,stddev=0.1))
#將每個參數的正則化損失加到總損失中
if regularizer != None: tf.add_to_collection(‘losses‘, tf.contrib.layers.l2_regularizer(regularizer)(w))
return w
def get_bias(shape):
#初始化的一維數組,初始化值為全 0
b = tf.Variable(tf.zeros(shape))
return b
def forward(x, regularizer):
#由輸入層到隱藏層的參數w1形狀為[784,500]
w1 = get_weight([INPUT_NODE, LAYER1_NODE], regularizer)
#由輸入層到隱藏的偏置b1形狀為長度500的一維數組,
b1 = get_bias([LAYER1_NODE])
#前向傳播結構第一層為輸入 x與參數 w1矩陣相乘加上偏置 b1 ,再經過relu函數 ,得到隱藏層輸出 y1。
y1 = tf.nn.relu(tf.matmul(x, w1) + b1)
#由隱藏層到輸出層的參數w2形狀為[500,10]
w2 = get_weight([LAYER1_NODE, OUTPUT_NODE], regularizer)
#由隱藏層到輸出的偏置b2形狀為長度10的一維數組
b2 = get_bias([OUTPUT_NODE])
#前向傳播結構第二層為隱藏輸出 y1與參 數 w2 矩陣相乘加上偏置 矩陣相乘加上偏置 b2,得到輸出 y。
#由於輸出 。由於輸出 y要經過softmax oftmax 函數,使其符合概率分布,故輸出y不經過 relu函數
y = tf.matmul(y1, w2) + b2
return y
反向傳播過程文件(mnist_backward.py)
反向傳播過程實現利用訓練數據集對神經網絡模型訓練,通過降低損失函數值,實現網絡模型參數的優化,從而得到準確率高且泛化能力強的神經網絡模型。
實現手寫體mnist數據集的識別任務反向傳播過程如下:
#coding:utf-8
#2反向傳播過程
#引入tensorflow、input_data、前向傳播mnist_forward和os模塊
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_forward
import os
#每輪餵入神經網絡的圖片數
BATCH_SIZE = 200
#初始學習率
LEARNING_RATE_BASE = 0.1
#學習率衰減率
LEARNING_RATE_DECAY = 0.99
#正則化系數
REGULARIZER = 0.0001
#訓練輪數
STEPS = 50000
#滑動平均衰減率
MOVING_AVERAGE_DECAY = 0.99
#模型保存路徑
MODEL_SAVE_PATH="./model/"
#模型保存名稱
MODEL_NAME="mnist_model"
def backward(mnist):
#用placeholder給訓練數據x和標簽y_占位
x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE])
y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE])
#調用mnist_forward文件中的前向傳播過程forword()函數,並設置正則化,計算訓練數據集上的預測結果y
y = mnist_forward.forward(x, REGULARIZER)
#當前計算輪數計數器賦值,設定為不可訓練類型
global_step = tf.Variable(0, trainable=False)
#調用包含所有參數正則化損失的損失函數loss
ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cem = tf.reduce_mean(ce)
loss = cem + tf.add_n(tf.get_collection(‘losses‘))
#設定指數衰減學習率learning_rate
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples / BATCH_SIZE,
LEARNING_RATE_DECAY,
staircase=True)
#使用梯度衰減算法對模型優化,降低損失函數
#train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
train_step = tf.train.MomentumOptimizer(learning_rate,0.9).minimize(loss, global_step=global_step)
#train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss, global_step=global_step)
#定義參數的滑動平均
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
ema_op = ema.apply(tf.trainable_variables())
#實例化可還原滑動平均的saver
#在模型訓練時引入滑動平均可以使模型在測試數據上表現的更加健壯
with tf.control_dependencies([train_step,ema_op]):
train_op = tf.no_op(name=‘train‘)
saver = tf.train.Saver()
with tf.Session() as sess:
#所有參數初始化
init_op = tf.global_variables_initializer()
sess.run(init_op)
#每次餵入batch_size組(即200組)訓練數據和對應標簽,循環叠代steps輪
for i in range(STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
if i % 1000 == 0:
print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
#將當前會話加載到指定路徑
saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)
def main():
#讀入mnist
mnist = input_data.read_data_sets("./data/", one_hot=True)
#反向傳播
backward(mnist)
if __name__ == ‘__main__‘:
main()
測試過程文件(mnist_test.py)
當訓練完模型後,給神經網絡模型輸入測試集驗證網絡的準確性和泛化性。註意,所用的測試集和訓練集是相互獨立的。 實現手寫體mnist數據集的識別任務測試傳播過程如下:
#coding:utf-8
#驗證網絡的準確性和泛化性
import time
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_forward
import mnist_backward
#程序5秒的循環間隔時間
TEST_INTERVAL_SECS = 5
def test(mnist):
#利用tf.Graph()復現之前定義的計算圖
with tf.Graph().as_default() as g:
#利用placeholder給訓練數據x和標簽y_占位
x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE])
y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE])
#調用mnist_forward文件中的前向傳播過程forword()函數
y = mnist_forward.forward(x, None)
#實例化具有滑動平均的saver對象,從而在會話被加載時模型中的所有參數被賦值為各自的滑動平均值,增強模型的穩定性
ema = tf.train.ExponentialMovingAverage(mnist_backward.MOVING_AVERAGE_DECAY)
ema_restore = ema.variables_to_restore()
saver = tf.train.Saver(ema_restore)
#計算模型在測試集上的準確率
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
while True:
with tf.Session() as sess:
#加載指定路徑下的ckpt
ckpt = tf.train.get_checkpoint_state(mnist_backward.MODEL_SAVE_PATH)
#若模型存在,則加載出模型到當前對話,在測試數據集上進行準確率驗證,並打印出當前輪數下的準確率
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
global_step = ckpt.model_checkpoint_path.split(‘/‘)[-1].split(‘-‘)[-1]
accuracy_score = sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
print("After %s training step(s), test accuracy = %g" % (global_step, accuracy_score))
#若模型不存在,則打印出模型不存在的提示,從而test()函數完成
else:
print(‘No checkpoint file found‘)
return
time.sleep(TEST_INTERVAL_SECS)
def main():
#加載指定路徑下的測試數據集
mnist = input_data.read_data_sets("./data/", one_hot=True)
test(mnist)
if __name__ == ‘__main__‘:
main()
運行以上三個文件,可得到手寫體 mnist 數據集的識別任務的運行結果:
從終端顯示的運行結果可以看出,隨著訓練輪數的增加,網絡模型的損失函數值在不斷降低,並且在測試集上的準確率在不斷提升,有較好的泛化能力。修改mnist_forward.py中w和b的初始化方法、修改隱藏層節點個數和隱藏層層數,修改mnist_backward.py代碼中的超參數,找出最快提升準確度的“全連接網絡”解決方案。
人工智能實踐:全連接網絡基礎