在Tensorflow學習(7)-通過MNIST數字識別問題學習(1)中,將計算神經網絡前向傳播結果的過程抽象成了一個函數。
即
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
在定義中可看到,這個函數的參數包括了神經網絡中的所有參數。然而,當網絡結構更復雜、參數更多時。就需要一個更好的方式來傳遞和管理神經網絡中的參數了。
TensorFlow提供了通過變量名稱來創建或者獲取一個變量的機制,通過這個機制,可以在不同的函數中直接通過變量的名字來使用變量,而不需要將變量通過參數的形式來傳遞。
主要是通過tf.get_variable和tf.variable_scope來實現的。下面介紹如何使用這兩個函數。
之前我們通過tf.Variable函數來創建一個變量。除了tf.Variable函數,TensorFlow還提供了tf.get_variable來創建或者獲取變量,當tf.get_variable用來創建變量時,和tf.Variable是基本等價的
# 下面兩個定義是等價的
v = tf.get_variable("v", shape=[1], initializer=tf.constant_initializer(1.0))
v = tf.Variable(tf.constant(1.0, shape=[1]), name="v")
它們兩個最大的區別就是tf.Variable函數中,變量名稱(name)是一個可選的參數,但是tf.get_variable函數,變量命名成是一個必填的參數。tf.get_variable會根據這個名字創建或者獲取變量。
獲取
如果需要通過tf.get_variable獲取一個已經創建的變量,需要通過tf.variable_scope函數來生成一個上下文管理器,並明確指明在這個上下文管理器中,tf.get_variable將直接獲取以生成的變量
import tensorflow as tf
# 在名字爲one的命名空間創建名字爲v的變量
with tf.variable_scope("one"):
v = tf.get_variable("v", [1], initializer=tf.constant_initializer(1.0))
# 如果在命名空間中已經存在名字爲v的變量,代碼就會報錯
with tf.variable_scope("one"):
v = tf.get_variable("v", [1], initializer=tf.constant_initializer(1.0))
# 在生成上下文管理器時,將參數reuse設爲True。這樣tf.get_variable函數將直接獲取已經聲明的變量
with tf.variable_scope("one",reuse=True):
v1 = tf.get_variable("v")
print(v==v1)
print(v==v1)
實現方式
tf.variable_scope函數生成的上下文管理器也會創建一個TensorFlow中的命名空間,在命名空間內創建的變量名稱都會帶上命名空間名作爲前綴。
import tensorflow as tf
v1 = tf.get_variable("v", [1])
print(v1.name) # v:0
with tf.variable_scope("one"):
v2 = tf.get_variable("v", [1])
print(v2.name) # one/v:0
with tf.variable_scope("two"):
v3 = tf.get_variable("v", [1])
print(v3.name) # one/two/v:0
with tf.variable_scope("", reuse=True):
v4 = tf.get_variable("one/v", [1])
print(v4.name) # one/v:0
print(v4 == v2) # True
with tf.variable_scope("one", reuse=True):
v5 = tf.get_variable("two/v", [1])
print(v5.name) # one/two/v:0
print(v5 == v3) # True
通過所學對def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):進行改進
不必以參數的形式傳入每層的變量
def inference(input_tensor, avg_class=None, reuse=False):
if(avg_class==None):
with tf.variable_scope('layer1', reuse=reuse):
weights = tf.get_variable("weights", [INPUT_NODE, LAYER1_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [1, LAYER1_NODE], initializer=tf.constant_initializer(0.0))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
with tf.variable_scope('layer2', reuse=reuse):
weights = tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [1, OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
layer2 = tf.matmul(layer1, weights) + biases
return layer2
else:
with tf.variable_scope('layer1', reuse=reuse):
weights = avg_class.average(tf.get_variable("weights", [INPUT_NODE, LAYER1_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1)))
biases = avg_class.average(
tf.get_variable("biases", [1, LAYER1_NODE], initializer=tf.constant_initializer(0.0)))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
with tf.variable_scope('layer2', reuse=reuse):
weights = avg_class.average(tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1)))
biases = avg_class.average(
tf.get_variable("biases", [1, OUTPUT_NODE], initializer=tf.constant_initializer(0.0)))
layer2 = tf.matmul(layer1, weights) + biases
return layer2
使用所學對Tensorflow學習(7)-通過MNIST數字識別問題學習(1)的代碼進行了重寫
其中inference_()和train_()是以前的版本。
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# MNIST數據集相關的參數
INPUT_NODE = 784 # 輸出層的節點數。也就是圖片的像素
OUTPUT_NODE = 10 # 輸出節點數,相當於類別
# 配置神經網絡的參數
LAYER1_NODE = 500 # 隱藏層節點數。這裏使用一個500個節點的隱藏層
LEARNING_RATE_BASE = 0.8 # 基礎的學習率
LEARNING_RATE_DECAY = 0.99 # 學習率的衰減率
REGULARIZATION_RATE = 0.0001 # 描述模型複雜度的正則化項在損失函數中的佔比係數
MOVING_AVERAGE_DECAY = 0.999 # 滑動平均衰減率
BATCH_SIZE = 100 # 數字越小越接近隨機梯度下降,越大越接近梯度下降
TRAINING_STEPS = 30000 # 訓練輪數
def new(reuse=False):
with tf.variable_scope('layer1', reuse=reuse):
tf.get_variable("weights", [INPUT_NODE, LAYER1_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
tf.get_variable("biases", [1, LAYER1_NODE], initializer=tf.constant_initializer(0.0))
with tf.variable_scope('layer2', reuse=reuse):
tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
tf.get_variable("biases", [1, OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
def inference(input_tensor, avg_class=None, reuse=False):
if(avg_class==None):
with tf.variable_scope('layer1', reuse=reuse):
weights = tf.get_variable("weights", [INPUT_NODE, LAYER1_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [1, LAYER1_NODE], initializer=tf.constant_initializer(0.0))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
with tf.variable_scope('layer2', reuse=reuse):
weights = tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [1, OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
layer2 = tf.matmul(layer1, weights) + biases
return layer2
else:
with tf.variable_scope('layer1', reuse=reuse):
weights = avg_class.average(tf.get_variable("weights", [INPUT_NODE, LAYER1_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1)))
biases = avg_class.average(
tf.get_variable("biases", [1, LAYER1_NODE], initializer=tf.constant_initializer(0.0)))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
with tf.variable_scope('layer2', reuse=reuse):
weights = avg_class.average(tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1)))
biases = avg_class.average(
tf.get_variable("biases", [1, OUTPUT_NODE], initializer=tf.constant_initializer(0.0)))
layer2 = tf.matmul(layer1, weights) + biases
return layer2
def train(mnist):
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
new()
y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-input')
y = inference(x,None,True)
# 定義存儲訓練輪數的變量。這個變量不需要計算滑動平均值,
# 所以這裏指定這個變量爲不可訓練的變量(trainable = Fasle)。
global_step = tf.Variable(0, trainable=False)
# 給定滑動平均衰減率和訓練輪數的變量
# 這裏給定訓練輪數的變量就是我們在<Tensorflow學習(6)-神經網絡進一步優化(滑動平均模型)>中提到的:
# ExponentialMovingAverage還提供了num_updates參數來動態設置decay大小,避免模型前期訓練的太慢
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, num_updates=global_step)
# 在所有代表神經網絡參數的變量上使用滑動平均。tf.trainable_variables返回的就是
# 圖上集合GraphKeys.TRAINABLE_VARIABLESZ中的元素。這個集合的元素就是所有沒有指定trainable=False的參數。
variable_averages_op = variable_averages.apply(tf.trainable_variables())
print(tf.trainable_variables())
a_y = inference(x, variable_averages, True)
# 分類問題,計算交叉熵作爲刻畫預測和真是指之間差距的損失函數。這裏使用TensorFlow中提供的
# sparse_softmax_cross_entropy_with_logits函數來計算交叉熵。當分類只有一個正確答案時
# 可以使用這個函數來計算交叉熵損失。
# 這個函數的第一個參數是神經網絡不包括softmax層的前向傳播結果,第二個是訓練數據的正確答案。
# 因爲標準答案是一個長度爲10的一位數組,而該函數需要的是提供一個正確答案的數字,所以要使用tf.argmax
# 函數來得到正確答案對應的編號
# (2)tf.argmax()會在下面給出解釋,編號爲2
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
# 計算在當前batch中所有樣例的交叉熵平均值
# (3)tf.redunce_mean
cross_entropy_mean = tf.reduce_mean(cross_entropy)
# 計算L2正則化損失函數。
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
# 計算模型的正則化損失。一般只計算權重的正則化損失,不計算偏置項
with tf.variable_scope("layer1", reuse=True):
weights1 = tf.get_variable("weights")
with tf.variable_scope("layer2", reuse=True):
weights2 = tf.get_variable("weights")
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, # 過完所有訓練集需要的輪數
# 如在加上參數staircase=True,上面兩個數就保證了
# 學習率沒經過一次所有的訓練數據就就乘以一次衰減率
LEARNING_RATE_DECAY)
# 使用tf.train.GradientDescentOptimizer優化算法來優化損失函數
train_step = tf.train.GradientDescentOptimizer(learning_rate). \
minimize(loss, global_step=global_step)
# 在訓練神經網絡模型時,每過一遍數據需要通過反向傳播來更新網絡中的參數
# 又要更新每一個參數的滑動平均值。爲了一次完成多個操作,TensorFlow提供了
# tf.group和tf.control_dependencies兩種機制。
# 這樣寫與下面一行代碼是等價的
# with tf.control_dependencies([train_step,variable_averages_op]):
# train_op = tf.no_op(name='train')
# (感覺tf.group這種挺好記的,記錄上面這種就爲了以後看代碼的時候防止看不懂吧)
train_op = tf.group(train_step, variable_averages_op)
# 檢驗正確率。現在這些代碼看的明白,就不解釋了。但是可能下次看就不明白了。。
# 不明白就看書吧,書上有
correct_prediction_ = tf.equal(tf.argmax(a_y, 1), tf.argmax(y_, 1))
accuracy_ = tf.reduce_mean(tf.cast(correct_prediction_, tf.float32))
# 終於可以開始訓練了
# 由於數據集比較小,所以沒有劃分爲更小的batch。
# 關於如何使用batch可以參考之前的博客
with tf.Session() as sess:
# ??? 咋直接run()了
tf.global_variables_initializer().run()
# 準備驗證數據。這個一般都是用來大致判斷停止的條件。
validata_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):
# 每1000輪輸出一次在驗證集上的測試效果
if i % 100 == 0:
validata_acc_ = sess.run(accuracy_, feed_dict=validata_feed)
print("%d training steps , validata_acc ,using average model is %g" % (i, validata_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("last acc not use %d", test_acc_)
# 定義一個輔助函數,給定神經網絡的輸入和所有參數,計算神經網絡的前向傳播結果。
# 這個函數也支持傳入用於計算滑動平均值的類,方便在測試時使用滑動平均值
def inference_(input_tensor, avg_class, weights1, biases1, weights2, biases2): # inference有推斷,推理的意思
# 當沒有提供滑動平均類時,直接使用參數當前的取值
if avg_class is None:
# 計算隱藏層的前向傳播結果,使用ReLU激活函數
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
# 因爲在計算損失函數時會一併計算softmax函數,所以這裏不需要加入激活函數
return tf.matmul(layer1, weights2) + biases2
# 當提供滑動平均類時
else:
# 使用avg_class.average來取變量的滑動平均值,用它來計算相應神經網絡前向傳播的結果
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)
# 訓練模型的過程
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')
# 生成隱藏層的參數
# (1)tf.truncated_normal() 會在博客下方列出解釋
weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[1, LAYER1_NODE]))
# 生成輸出層的參數
weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[1, OUTPUT_NODE]))
# 使用自定義的函數inference()計算當前參數下神經網絡前向傳播的結果
# 這裏不計算滑動平均值
y = inference(x, None, weights1, biases1, weights2, biases2)
# 定義存儲訓練輪數的變量。這個變量不需要計算滑動平均值,
# 所以這裏指定這個變量爲不可訓練的變量(trainable = Fasle)。
global_step = tf.Variable(0, trainable=False)
# 給定滑動平均衰減率和訓練輪數的變量
# 這裏給定訓練輪數的變量就是我們在<Tensorflow學習(6)-神經網絡進一步優化(滑動平均模型)>中提到的:
# ExponentialMovingAverage還提供了num_updates參數來動態設置decay大小,避免模型前期訓練的太慢
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, num_updates=global_step)
# 在所有代表神經網絡參數的變量上使用滑動平均。tf.trainable_variables返回的就是
# 圖上集合GraphKeys.TRAINABLE_VARIABLESZ中的元素。這個集合的元素就是所有沒有指定trainable=False的參數。
variable_averages_op = variable_averages.apply(tf.trainable_variables())
# 計算使用了滑動平均之後的前向傳播結果
# 可以在inference函數中注意到,需要使用滑動平均值時,需要明確調用average函數。
# 因爲滑動平均不會改變變量本身的取值,而是會維護一個影子變量來記錄其滑動平均值,
# 當需要使用這個滑動平均值時,需要明確指明調用average函數
average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)
# 分類問題,計算交叉熵作爲刻畫預測和真是指之間差距的損失函數。這裏使用TensorFlow中提供的
# sparse_softmax_cross_entropy_with_logits函數來計算交叉熵。當分類只有一個正確答案時
# 可以使用這個函數來計算交叉熵損失。
# 這個函數的第一個參數是神經網絡不包括softmax層的前向傳播結果,第二個是訓練數據的正確答案。
# 因爲標準答案是一個長度爲10的一位數組,而該函數需要的是提供一個正確答案的數字,所以要使用tf.argmax
# 函數來得到正確答案對應的編號
# (2)tf.argmax()會在下面給出解釋,編號爲2
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
# 計算在當前batch中所有樣例的交叉熵平均值
# (3)tf.redunce_mean
cross_entropy_mean = tf.reduce_mean(cross_entropy)
# 計算L2正則化損失函數。
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, # 過完所有訓練集需要的輪數
# 如在加上參數staircase=True,上面兩個數就保證了
# 學習率沒經過一次所有的訓練數據就就乘以一次衰減率
LEARNING_RATE_DECAY)
# 使用tf.train.GradientDescentOptimizer優化算法來優化損失函數
train_step = tf.train.GradientDescentOptimizer(learning_rate). \
minimize(loss, global_step=global_step)
# 在訓練神經網絡模型時,每過一遍數據需要通過反向傳播來更新網絡中的參數
# 又要更新每一個參數的滑動平均值。爲了一次完成多個操作,TensorFlow提供了
# tf.group和tf.control_dependencies兩種機制。
# 這樣寫與下面一行代碼是等價的
# with tf.control_dependencies([train_step,variable_averages_op]):
# train_op = tf.no_op(name='train')
# (感覺tf.group這種挺好記的,記錄上面這種就爲了以後看代碼的時候防止看不懂吧)
train_op = tf.group(train_step, variable_averages_op)
# 檢驗正確率。現在這些代碼看的明白,就不解釋了。但是可能下次看就不明白了。。
# 不明白就看書吧,書上有
correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
correct_prediction_ = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy_ = tf.reduce_mean(tf.cast(correct_prediction_, tf.float32))
# 終於可以開始訓練了
# 由於數據集比較小,所以沒有劃分爲更小的batch。
# 關於如何使用batch可以參考之前的博客
with tf.Session() as sess:
# ??? 咋直接run()了
tf.global_variables_initializer().run()
# 準備驗證數據。這個一般都是用來大致判斷停止的條件。
validata_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):
# 每1000輪輸出一次在驗證集上的測試效果
if i % 100 == 0:
validata_acc = sess.run(accuracy, feed_dict=validata_feed)
validata_acc_ = sess.run(accuracy_, feed_dict=validata_feed)
print("%d training steps , validata_acc ,using average model is %g" % (i, validata_acc))
print("%d training steps , validata_acc ,using average model is %g" % (i, validata_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)
test_acc_ = sess.run(accuracy_, feed_dict=test_feed)
print("last acc %d", test_acc)
print("last acc not use %d", test_acc_)
def main(argv=None):
mnist = input_data.read_data_sets("path/", one_hot=True)
train(mnist)
if __name__ == '__main__':
tf.app.run()
其他
tf.variable是可以嵌套的,tf.get_variable_scope().reuse可以獲取當前上下文管理器中的reuse參數。
import tensorflow as tf
# 在名字爲one的命名空間創建名字爲v的變量
with tf.variable_scope("one"):
print(tf.get_variable_scope().reuse) #輸出False
with tf.variable_scope("two",reuse=True):
print(tf.get_variable_scope().reuse) #輸出True