TensorFlow的helloworld(MNIST數字識別問題)程式碼手把手解讀
程式碼來源是《TensorFlow實戰Google深度學習框架第2版》
因為是“helloworld”級別,所以是針對每一行程式碼進行解析。
訓練神經網路的全部過程,總結為三個步驟:
步驟1,定義神經網路結構和前向傳播的輸出結果。
步驟2,定義損失函式以及選擇反向傳播優化的演算法。
步驟3,生成會話(tf.Session)並且在訓練資料上反覆執行反向傳播優化演算法。
一、引入官方寫的類,這個類主要是呼叫官方的函式,用來解析檔案中的4個檔案生成為程式碼可執行格式。網站為 http://yann.lecun.com/exdb/mnist
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data
二、定義一些變數, 分別為輸入層節點數、輸出層節點數、隱藏層節點數等
INPUT_NODE = 784 OUTPUT_NODE = 10 LAYER1_NODE = 500 BATCH_SIZE = 100 #一個訓練batch中的訓練資料個數。資料越小時,訓練過程越接近隨機梯度下降;數字越大時,訓練越接近梯度下降 LEARNING_RATE_BASE=0.8#基礎的學習率 LEARNING_RATE_DECAY=0.99#學習率的衰減率 REGULARIZATION_RATE = 0.0001#描述模型複雜度的正則化在損失函式中的係數 TRAINING_STEPS = 30000#訓練論數 MOVING_AVERAGE_DECAY = 0.99#滑動平均衰減率
三、輔助函式,這個可自己更改,通過改第二個引數avg_class,選擇是否用滑動平均模型,比較簡單。
def inference(input_tensor, avg_class, weights1, biases1,weights2, biases2): if avg_class == None: layer1 = tf.nn.relu(tf.matmul(input_tensor,weights1)+biases1) return tf.matmul(layer1,weights2)+biases2 else: layer1 = tf.nn.relu( tf.matmul(input_tensor, avg_class.average(weights1))+avg_class.average(biases1) ) return tf.matmul(layer1, avg_class.average(weights2))+avg_class.average(biases2)
四、訓練模型,
(1)首先是利用placeholder機制
tf中有一個常量定義:tf.constant
兩個變數定義:
tf.Variable:主要在於一些可訓練變數(trainable variables),比如模型的權重(weights,W)或者偏執值(bias);《TensorFlow實戰Google深度學習框架第2版》:“在Tensorflow中,變數(tf.Variable)的作用就是儲存和更新神經網路中的引數。TensorFlow還提出了一些其他的隨機數生成器,
| 函式名稱 | 隨機數分佈 |
|tf.random |正態分佈|
|tf.truncated_normal |正態分佈,但如果隨機出來的值偏離平均值超過兩個平均差,那麼這個數將會被重新隨機 |
|tf.random_uniform |均勻分佈|
|tf.random_gamma |Gamma分佈|
下面程式碼用的就是tf.truncated_normal
tf.placeholder:用於得到傳遞進來的真實的訓練樣本,tf提供了placeholder用於輸入資料,而且也可以不定義維度,但是tf.Variable必須初始化,placeholder中資料的維度資訊可以根據提供的資料推導得出,中文註釋為“佔位符”,意思是定義了一個位置,這個位置中的資料在程式執行時再指定
def train(mnist):
x=tf.placeholder(tf.float32,[None,INPUT_NODE],name='x-input')
y_=tf.placeholder(tf.float32,[None,OUTPUT_NODE],name='y-input')
weights1 = tf.Variable(
tf.truncated_normal([INPUT_NODE,LAYER1_NODE],stddev=0.1)
)
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
weights2 = tf.Variable(
tf.truncated_normal([LAYER1_NODE,OUTPUT_NODE], stddev=0.1)
)
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))
(2)
步驟2,定義損失函式以及選擇反向傳播優化的演算法。
首先是求loss,
首先:用前行傳播演算法求出y,根據交叉熵平均值和正則化算出loss
其次給定滑動平均衰減率和訓練輪數的變數,初始化滑動平均類,
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
其中global_step的作用就是加快訓練早期變數的更新速度
在所有代表神經網路引數的變數上使用滑動平均variables_averages_op = variable_averages.apply(tf.trainable_variables()),
因為global_step不需要訓練,所以之前定義時候是 global_step = tf.Variable(0, trainable=False),第二個引數trainable=False則代表不可訓練的引數。
最後求反向傳播演算法 train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
with tf.control_dependencies([train_step, variables_averages_op]):
train_op =tf.no_op(name=‘train’)
求出train_op,用於步驟3
主要是用train_step和variables_averages_op求得train_op,
y= inference(x, None, weights1, biases1, weights2,biases2)
global_step = tf.Variable(0, trainable=False)
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
variables_averages_op = variable_averages.apply(tf.trainable_variables())
average_y = inference(x, variable_averages, weights1, biases1, weights2,biases2)
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cross_entropy_mean = tf.reduce_mean(cross_entropy)
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
regularization = regularizer(weights1)+regularizer(weights2)
loss = cross_entropy_mean+regularization
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples/BATCH_SIZE,
LEARNING_RATE_DECAY
)
train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
with tf.control_dependencies([train_step, variables_averages_op]):
train_op =tf.no_op(name='train')
correct_prediction = tf.equal(tf.argmax(average_y,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
(3)建立一個會話
這個是最開始所說的步驟3
基本的常量與變數設定完,我習慣先從建立會話處入手,這樣更容易從整體看到區域性(因為之前在做嵌入式就是這麼幹的,從main函式入手,習慣沒改過來)
如下程式碼、
第一行:建立一個會話,並通過Python中的上下文管理器來管理這個會話。
第二行:通過tf.global_variables_initializer().run()函式實現初始化所有變數的過程。這個函式不需要將變數一個一個初始化,並且會自動處理變數之間的依賴關係。
第三行:建立驗證字典
第四行:建立訓練字典
第五行:輪詢,就是迭代地訓練神經網路
第六行~第十行:判斷是否為第1000個訓練資料,如果是則,用驗證集來看準確率,一般的神經網路訓練過程都需要一個驗證集來觀察訓練過程中準確率是否增加,這麼做的目的是防止準確度越來越小,那麼訓練的模型就是錯誤的了,直接終止即可,畢竟一些大的模型需要花很長時間來訓練。
第十一行:這裡採用了mnist.train.next_batch函式,這個我猜測就是官方給的一個查詢下一個BATCH的函式,找出下一個XS和YS即可
第十二行:sess.run()函式作用是通過選取的樣本訓練神經網路並更新引數。這裡主要是更新train_op,這個引數上一步求出的。這麼做的目的就是步驟三所說的生成會話(tf.Session)並且在訓練資料上反覆執行反向傳播優化演算法。
第十三行:在訓練結束之後,在測試資料上檢測神經網路模型的最終正確率。
with tf.Session() as sess:
tf.global_variables_initializer().run()
validate_feed = {x:mnist.validation.images, y_:mnist.validation.labels}
test_feed = {x:mnist.test.images, y_:mnist.test.labels}
for i in range(TRAINING_STEPS):
if i % 1000==0:
validate_acc = sess.run(accuracy, feed_dict=validate_feed)
print("After %d training step(s), validation accuracy"
"using average model is %g" %(i, validate_acc)
)
xs, ys=mnist.train.next_batch(BATCH_SIZE)
sess.run(train_op,feed_dict={x:xs,y_:ys})
test_acc = sess.run(accuracy, feed_dict=test_feed)
print("After %d training step(s), test accuracy using average" "model is %g"%(TRAINING_STEPS,test_acc) )