TensorFlow Playground
http://playground.tensorflow.org
幫助更好的理解,游樂場Playground可以實現可視化訓練過程的工具
TensorFlow Playground的左側提供了不同的數據集來測試神經網絡。默認的數據為左上角被框出來的那個。被選中的數據也會顯示在最右邊的 “OUTPUT”欄下。在這個數據中,可以看到一個二維平面上有紅色或者藍色的點,每一個小點代表了一個樣例,而點的顏色代表了樣例的標簽。因為點的顏色只有兩種,所以這是 一個二分類的問題。在這里舉一個例子來說明這個數據可以代表的實際問題。假設需要判斷某工廠生產的零件是否合格,那么藍色的點可以表示所有合格的零件而紅色的表示不合格的零件。這樣判斷一個零件是否合格就變成了區分點的顏色。
為了將一個實際問題對應到平面上不同顏色點的划分,還需要將實際問題中的實體, 比如上述例子中的零件,變成平面上的一個點。 這就是特征提取解決的問題。還是以零件為例, 可以用零件的長度和質量來大致描述一個零件。這樣一個物理意義上的零件就可以被轉化成長度和質量這兩個數字。在機器學習中,所有用於描述實體的數字的組合就是一 個實體的特征向量(feature vector)。特征向量的提取對機器學習的效果至關重要,通過特征提取,就可以將實際問題中的實體轉化為空間中的點。假設使用長度和質量作為一個零件的特征向量,那么每個零件就是二維平面上的一個點。 TensorFlow Playground中 FEATURES 一欄對應了特征向量。 可以認為 x1代表零件的長度,而 x2代表零件的質量。
在二分類問題中,比如 判斷零件是否合格,神經網絡的輸出層往往只包含一個節點,而這個節點會輸出一個實數值。通過這個輸出值和一個事先設定的閥值,就可以得到最后的分類結果。以判斷零件合格為例,可以認為當輸出的數值大於 0 時,給出的判斷結果是零件合格,反之則零件不合格。一般可以認為當輸出值距離閾值越遠時得到的判斷越可靠。
主流的神經網絡主體結構都是分層的結構:
輸入層 - 隱藏層 - 輸出層
綜上,使用神經網絡解決分類或回歸問題主要可以分為以下 4 個步驟:
1. 提取問題中實體的特征向量作為神經網絡的輸入。不同的實體可以提取不同的特征向量。
2. 定義神經網絡的結構,並定義如何從神經網絡的輸入得到輸出。這個過程就是神經網絡的前向傳播算法
3. 通過訓練數據來調整神經網絡中參數的取值,這就是訓練神經網絡的過程。
4. 使用訓練好的神經網絡來預測未知的數據。
前向傳播算法 forward-propagation
神經元(op)也稱為節點是構成一個神經網絡的最小單元,每個神經元的輸入既可以是其他神經元的輸出,也可以是整個神經網絡的輸入。所謂神經網絡的結構指的就是不同神經元之間的連接結構。一個最簡單的神經元結構的輸出就是所有輸入的加權和,而不同輸入的權重就是神經元的參數。神經網絡的訓練過程就是優化神經元中參數取值的過程。
判斷零件是否合格的三層前向傳播全連接神經網絡
其中全連接神經網絡是指: 相臨兩層之間任意兩個節點都有連接. 區別與卷積層和LSTM結構
計算神經網絡的前向傳播結果需要三部分信息:
1.神經網絡輸入特征向量 比如兩個特征,一個是零件的長度x1,一個是零件的質量x2,組成特征向量
2.神經網絡的連接結構 (連接前后關系)
3.每個神經元中的參數 W(1)表示第一層節點的參數,W(1)1,2表示連接x1和a12節點的邊上的權重.
因為這個輸出值大於閾值 0,所以在這個樣例中最后給出的答案是:這個產品是合格的。這就是整個前向傳播的算法。
前向傳播算法可以表示為矩陣乘法,其中tf.matmul實現了矩陣乘法的功能
a = tf.matmul(x,w1) y = tf.matmul(a,w2)
神經網絡參數與TensorFlow變量
神經網絡中的參數是神經網絡實現分類或者回歸問題中重要的部分
變量 (tf.Variable)的作用就是保存和更新神經網絡中的參數。TensorFlow 中的變量需要指定初始值。 一般使用隨機數給 TensorFlow 中的變量初始化
調用 TensorFlow 變量的聲明函數 tf.Variable 聲明一個 2 * 3的矩陣變量
weights = tf.Variable(tf.random_normal([2,3],mean=0,stddev=2)
隨機數生成器
tf.random normal 正態分布 平均值、 標准差、馭伯類型
tf.truncated normal 正態分布,但如果隨機出來的值偏離平均值超過 2個標准差,那么這個數將會被重新隨機
tf.random uniform 均勻分布
常數生成器
tf.zeros 產生全 0 的數組
tf.ones 產生全 1的數組
tf.ones([2, 3], int32) -> [[1 , 1, 1), [1 , 1, 1))
tf.fill 產生一個全部為給定數字的數組
tf.fill([2, 3), 9) -> ((9, 9, 9), (9, 9, 9))
tf.constant 產生一個給定值的常量
biases= tf.Variable(tf. zeros([3]))
以上代碼將會生成一個初始值全部為 0 且長度為 3 的變量
TensorFlow 也支持通過其他變量的初始值 來初始化新的變量(另一個)
w2 = tf.Variable(weights.initialized_value()*2.0)
w2輸入的是weights的初始值的兩倍
TensorFlow 中, 一個變量的值在被使用之前,這個變量的初始化過程需要被顯式地調用。
下面通過變量實現神經網絡的參數初始化並實現前向傳播的過程:
import tensorflow as tf # 聲明 w1, w2 兩個變量, 這里還通過seed參數設定了隨機種子 seed=1 # 這樣可以保證每次運行得到的結果是一樣的
w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1)) # 暫時將輸入的特征向盤定義為一個常量。注意這里 x 是一個 1x2 的矩陣
x = tf.constant([[0.7, 0.9]]) # 通過 3.4.2 節描述的前向傳播算法獲得神經網絡的輸出
a = tf.matmul(x, w1) y = tf.matmul(a, w2) with tf.Session() as sess: # 因為wl和w2都還沒有運行初始化,不能直接sess.run(y)來獲取y的取值
sess.run(w1.initializer) # 初始化 w1
sess.run(w2.initializer) # 初始化 w2
print(sess.run(y))
當聲明了變量 w1, w2 之后,可以通過 w1 和 w2 來定義神經網絡的前向傳播過程並得到中間結果 a 和最后答 案,此過程對應 [1] 從零開始 TensorFlow 學習 中A階段: 定義計算圖中的所有計算() 但沒有真正運行計算
with tf.Session() as sess 聲明一個會話(session)通過會話計算結果,但在計算y之前,需要將所有用到的變量初始化,也就是說雖然在變量定義時給出的變量初始化的方法,但這個方法並沒有被真正運行(因為初始化也是一種運算),所以在運行y之前需要通過運行w1.initializer和w2.initializer來初始化權重進行賦值, 雖然直接指定每個變量的初始化是一個可行的方案,但是隨着節點增多或節點之間的依賴關系增多,單個調用的方法會變得復雜,這里TensorFlow提供了一種便利的方式tf.global_variables_initializer()來初始化所有變量.
import tensorflow as tf # 聲明 w1, w2 兩個變量, 這里還通過seed參數設定了隨機種子 seed=1 # 這樣可以保證每次運行得到的結果是一樣的
w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1)) # 暫時將輸入的特征向盤定義為一個常量。注意這里 x 是一個 1x2 的矩陣
x = tf.constant([[0.7, 0.9]]) # 通過 3.4.2 節描述的前向傳播算法獲得神經網絡的輸出
a = tf.matmul(x, w1) y = tf.matmul(a, w2) with tf.Session() as sess: # 因為wl和w2都還沒有運行初始化,不能直接sess.run(y)來獲取y的取值
init_op = tf.global_variables_initializer() sess.run(init_op) # 初始化全部節點op
print(sess.run(y))
TensorFlow 的核心概念是張量(tensor),所有的數據都是通過張量的形式來組織的,那么變量和張量是什么關系呢? 變量的聲明函數 tf.Variable 是一個運算。這個運算的輸出結果就是一個張量, 而這個張量即是variable,所以變量可以看作是一種特殊的張量
那么tf.Variable底層是如何實現的呢, 下面是神經網絡前向傳播樣例中變量 w1 相關部分的計算圖可視化結果
w1是一個 Variable 運算
在這張圖 的下方可以看到 w1 通過一個 read 操作將值提供給了一個乘法運算,這個乘法操作就是 tf.matmul(x, w1), 初始化變量 w1 的操作是通過 Assign 操作 輸入random_normal隨機值完成的.整個過程就是變量初始化過程.
TensorFlow 中集合(collection)的概念,所有的變量都會被自動地加入 到 GraphKeys.VARIABLES 這個集合中。 通過 tf.global_variables()函數可以拿到當前計算圖 上所有的變量。拿到計算圖上所有的變量有助於持久化整個計算圖的運行狀態.
通過變量聲明函數 中的 trainable 參數來區分需要優化的參數(比如神經網絡中的參數) 和其他參數(比如選代的輪數) .如果聲明變量時參數 trainable 為 True ,那么這個變量將會被加入到GraphKeys.TRAINABLE_VARIABLES集合. 在TensorFlow 中可以通過tf.trainable_variables函數得到所有需要優化的參數. TensorFlow 中提供的神 經網絡優化算法會將GraphKeys.TRAINABLE_VARIABLES 集合中的變量作為默認的優化對象
維度(shape)和類型 (type) 也是變量最重要的兩個屬性。和大部分程序 語言類似,變量的類型是不可改變的。一個變量在構建之后,它的類型就不能再改變了 。比如在上面給出的前向傳播樣例中, w1的類型為 random_normal結果的默認類型 tf.float32, 那么它將不能被賦予其他類型的值
w1.assign(2)
報錯TypeError : type float64 that does not match type float32 of argument '' ref ''
反向傳播算法(back-propagation)
只有經過有效訓練的神經網絡模型才可以真正地解決分類或者回歸問題. 使用監督學習的方式設置神經網絡參數需要有一個標注好的訓練數據集,通過調整神經網絡中的參數對訓練數據進行擬合,可以使得模型對未知的樣本提供預測的能力.
神經網絡優化算法中,最常用的是反向傳播算法(back-propagation)

反向傳播算法實現了一個法代的過程。在每次迭代的開始,首先需要選取一小部分訓練數據, 這一小部分數據叫做一個 batch
這個 batch 的樣例會通過前向傳播算法得到神經網絡模型的預測結果。因為訓練數據都是有正確答案標注的, 所以可以計算出當前神經網絡模型的預測答案與正確答案之間的差距。最后,基於預測值 和真實值之間的差距,反向傳播算法會相應更新神經網絡參數的取值,使得在這個 batch 上神經網絡模型的預測結果和真實答案更加接近
通過TensorFlow實現反向傳播算法的第一步是使用 TensorFlow表達一個batch 的數據。
x = tf.constant([[0.7, 0.9]])
使用tf.placeholder代替tf.constant
但如果每輪迭代中選取的數據都要通過常量來表示,那么TensorFlow 的計算圖將會太大。因為每生成一個常量,TensorFlow都會在計算圖中增加一個節點。 一個神經網絡的訓練過程會需要經過幾百萬輪甚至幾億輪的迭代,這樣計算圖就會非常大,而且利用率很低. 為了避免這個問題, TensorFlow 提供了 placeholder 機制用於提供輸入數據。placeholder 相當於定義了一個位置,這個位置中的數據在程序運行時再指定. 這樣在程序 中就不需要生成大量常量來提供輸入數據,而只需要將數據通過 placeholder 傳入 TensorFlow 計算圖。在 placeholder 定義時,這個位置上的數據類型是需要指定的。和其他張量一樣,placeholder 的類型也是不可以改變的。 placeholder 中數據的維度信息可以根據 提供的數據推導得出,所以不一定要給出。下面給出了通過 placeholder 實現前向傳播算法的代碼。
import tensorflow as tf w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1)) x = tf.placeholder(tf.float32,shape=(1,2),name="input") a = tf.matmul(x, w1) y = tf.matmul(a, w2) with tf.Session() as sess: # 因為wl和w2都還沒有運行初始化,不能直接sess.run(y)來獲取y的取值
init_op = tf.global_variables_initializer() sess.run(init_op) # 初始化全部節點op
print(sess.run(y)) """ 報錯 InvalidArgumentError (see above for traceback): You must feed a value for placeholder tensor 'input' with dtype float and shape [1,2] """
tf.placeholder替換了原來通過常量tf.constant定義,但額外的需要提供一個feed_dict字典(map)來assign x的取值,在feed_dict中需要給出每個用到的placeholder的取值
正確寫法
import tensorflow as tf w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1)) x = tf.placeholder(tf.float32,shape=(1,2),name="input") a = tf.matmul(x, w1) y = tf.matmul(a, w2) with tf.Session() as sess: # 因為wl和w2都還沒有運行初始化,不能直接sess.run(y)來獲取y的取值
init_op = tf.global_variables_initializer() sess.run(init_op) # 初始化全部節點op
feed_dict ={x:[[0.7,0.9]]} print(sess.run(y,feed_dict))
以上程序只計算了 x = [[0.7, 0.9]] 這1個batch的訓練樣例,如果要增加更多的訓練樣例,首先要調整tf.placeholder的shape參數改為n × 2, 將輸入的 1 x 2 矩陣改為 n x 2 的矩陣,其中n × 2的矩陣的每1行為1個樣例數據,可以得到 n x 1 矩陣形式的前向傳播結果 ( n個樣例 )
import tensorflow as tf w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1)) x = tf.placeholder(tf.float32,shape=(4,2),name="input") a = tf.matmul(x, w1) y = tf.matmul(a, w2) with tf.Session() as sess: # 因為wl和w2都還沒有運行初始化,不能直接sess.run(y)來獲取y的取值
init_op = tf.global_variables_initializer() sess.run(init_op) # 初始化全部節點op
feed_dict ={x:[[0.7,0.9],[0.7,0.9],[0.7,0.9],[0.7,0.9]]} print(sess.run(y,feed_dict)) """ 結果: [[3.957578] [3.957578] [3.957578] [3.957578]] """
損失函數
得到前向傳播結果之后,需要定義一個損失函數來刻畫當前的預測值與真實值(標記好的數據集)之間的差距, 然后通過反向傳播算法(back-propagation)來調整神經網絡參數的取值,使得差距可以被縮小.
簡單的損失函數
# 使用 sigmoid 函數將 y 轉換為 0~1 之間的數值。轉換后 y 代表預測是正樣本的概率 # 1-y 代表 預測是負樣本的概率
y=tf.sigmoid(y) # 定義損失函數(交叉熵)來刻畫預測值與真實值的差距
cross_entropy = -tf.reduce_mean(y * tf.log(tf.clip_by_value(y,1e-10,1.0))+ (1-y)*tf.log(tf.clip_by_value(1-y,1e-10,1.0))) # 學習率
learning_rate = 0.001
# 定義反向傳播算法來優化神經網絡中的參數
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
其中 cross_entropy定義了真實值和預測值之間的交叉熵 (cross entropy), 這是分類問題中一個常用的損失函數
train_step定義了反向傳播的優化方法 目前TensorFlow支持10種不同的優化器, 常用的有tf.train.GradientDescentOptimizer、tf.train.AdamOptimizer和tf.train.MomentumOptimizer
在定義了反向傳播算法之后,通過sess.run(train_step)就可以對所有在GraphKeys.TRAINABLE_VARIABLES集合中的變量進行優化,使得當前batch下損失函數值更小
完整的神經網絡樣例程序
import tensorflow as tf # 通過RandomState生成模擬數據集
from numpy.random import RandomState # 定義訓練數據 batch 的大小
batch_size = 8
# 定義神經網絡的參數
w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1)) # 在 shape 的一個維度上使用 None 可以方便使用不同的 batch 大小. 在訓練時需要把數據分成 # 成比較小的 batch, 但是在測試時,可以一次性使用全部的數據. 當數據集比較小時這樣比較方便 # 測試, 當數據集比較大時,將大量數據放入一個 batch 可能會導致內存溢出
x = tf.placeholder(tf.float32,shape=(None,2),name="x-input") _y = tf.placeholder(tf.float32,shape=(None,1),name="y-input") # 定義神經網絡的前向傳播過程
a = tf.matmul(x, w1) y = tf.matmul(a, w2) # 定義損失函數和反向傳播的算法
y=tf.sigmoid(y) # 定義損失函數(交叉熵)來刻畫預測值與真實值的差距
cross_entropy = -tf.reduce_mean( _y * tf.log(tf.clip_by_value(y,1e-10,1.0))+ (1-y)*tf.log(tf.clip_by_value(1-y,1e-10,1.0))) # 學習率
learning_rate = 0.001
# 定義反向傳播算法來優化神經網絡中的參數
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy) # 通過隨機數生成一個模擬的數據集
rdm = RandomState(1) dataset_size = 128 # 樣本數量
X = rdm.rand(dataset_size,2) # 定義規則來給出樣本的標簽. x1+x2<1的樣例都認為是正樣本(比如零件合格),其他為情況為負樣本(零件不合格) # 大部分解決分類問題的神經網絡都采用0來表示負樣本,1來表示正樣本
Y = [[int(x1+x2<1)] for (x1,x2) in X] # 創建一個會話來運行TensorFlow程序
with tf.Session() as sess: # 因為wl和w2都還沒有運行初始化,不能直接sess.run(y)來獲取y的取值
init_op = tf.global_variables_initializer() sess.run(init_op) # 初始化全部節點op
# 打印最初的神經網絡節點參數值
print("w1: ",sess.run(w1)) print("w2: ",sess.run(w2)) # 設定訓練的輪數
STEPS = 50000
for i in range(STEPS): # 每次選取batch_size個樣本進行訓練
start = (i*batch_size) % dataset_size end = min(start+batch_size,dataset_size) # 通過選取的樣本訓練神經網絡並更新參數
sess.run(train_step,feed_dict={x:X[start:end],_y:Y[start:end]}) # 每隔1000輪 就計算在所有的數據上的交叉熵並輸出
if i % 1000 == 0: total_cross_entropy = sess.run(cross_entropy,feed_dict={x:X,_y:Y}) print("After {0} training step(s),cross entropy on all data is {1}".format(i,total_cross_entropy)) """ 輸出: After 43000 training step(s),cross entropy on all data is 1.3634375761739648e-07 After 44000 training step(s),cross entropy on all data is 9.776589138255076e-08 After 45000 training step(s),cross entropy on all data is 7.139959734558943e-08 After 46000 training step(s),cross entropy on all data is 4.426699007353818e-08 After 47000 training step(s),cross entropy on all data is 3.026656614224521e-08 After 48000 training step(s),cross entropy on all data is 1.577882535741537e-08 After 49000 training step(s),cross entropy on all data is 1.577882535741537e-08 """
按照這段程序可以總結訓練神經網絡的三個步驟:
1. 定義神經網絡的結構和前向傳播的輸出結果
2. 定義損失函數,選擇反向傳播優化的算法
3. 設定會話(tf.Session)並且在訓練數據上反復迭代運行反向傳播優化算法