深度學習之TensorFlow 第三章基本開發步驟--以邏輯迴歸擬合二維資料為例
深度學習有四個步驟:
準備資料 搭建模型 迭代訓練 使用模型
import tensorflow as tf import numpy as np #陣列 import matplotlib.pyplot as plt # matplotlib.pyplot 2D繪相簿 類似MATLAB的一種繪圖框架 plotdata = { "batchsize":[], "loss":[] } #存放批次值和損失值 def moving_average(a, w=10): if len(a) < w: return a[:] return [val if idx < w else sum(a[(idx-w):idx])/w for idx, val in enumerate(a)] #生成模擬資料 train_X = np.linspace(-1, 1, 100) #train_X = np.linspace(-1, 1, 100) #生成-1~1之間的100個數作為x。 train_Y = 2 * train_X + np.random.randn(*train_X.shape) * 0.3 #(a屬於[-1,1]之間的隨機數) train_Y = 2 * train_X + np.random.randn(*train_X.shape) * 0.3 # y=2x,但是加入了噪聲 #顯示模擬資料點 plt.plot(train_X, train_Y, 'ro', label='Original data') #‘ro’代表紅色實心圈標記 plt.legend() #顯示圖例label='Original data' plt.show() # 建立模型 # 佔位符 X = tf.placeholder("float") Y = tf.placeholder("float") #X代表輸入,Y代表對應的真實值,都是佔位符(佔住一個固定的位置等著再往裡面新增內容的符號) # 模型引數 W = tf.Variable(tf.random_normal([1]), name="weight") b = tf.Variable(tf.zeros([1]), name="bias") #W和b是引數,W被初始化為[-1,1]的隨機數,形狀為一維的數字,b的初始化為0,形狀也是一維。 # 前向結構 z = tf.multiply(X, W)+ b #反向優化 cost =tf.reduce_mean( tf.square(Y - z)) learning_rate = 0.01 #學習率代表調整引數的速度。這個值一般小於1。值越大,代表調整的速度越大,但不精確。 optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost) #GradientDescentOptimizer是封裝好的梯度下降演算法。 # 初始化變數 init = tf.global_variables_initializer() # 訓練引數 training_epochs = 20 #迭代次數 display_step = 2 # 啟動session with tf.Session() as sess: sess.run(init) # Fit all training data for epoch in range(training_epochs): for (x, y) in zip(train_X, train_Y): sess.run(optimizer, feed_dict={X: x, Y: y}) #外層放迴圈次數 通過sess.run來進行網路節點的運算,通過feed機制將真實資料灌到佔位符對應的位置(feed_dict={X: train_X, Y:train_Y})同時沒執行一次都會將網路結構中的節點打印出來 #顯示訓練中的詳細資訊 if epoch % display_step == 0: loss = sess.run(cost, feed_dict={X: train_X, Y:train_Y}) print ("Epoch:", epoch+1, "cost=", loss,"W=", sess.run(W), "b=", sess.run(b)) if not (loss == "NA" ): #是個安全檢查,保證loss值有效才會將其用圖示顯示出來 plotdata["batchsize"].append(epoch) plotdata["loss"].append(loss) print (" Finished!") print ("cost=", sess.run(cost, feed_dict={X: train_X, Y: train_Y}), "W=", sess.run(W), "b=", sess.run(b)) #print ("cost:",cost.eval({X: train_X, Y: train_Y})) #圖形顯示 plt.plot(train_X, train_Y, 'ro', label='Original data') plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line') plt.legend() plt.show() plotdata["avgloss"] = moving_average(plotdata["loss"]) plt.figure(1) #plt.figure(1)是新建一個名叫 Figure1的畫圖視窗 plt.subplot(211) #第一個畫板的第一個子圖 plt.plot(plotdata["batchsize"], plotdata["avgloss"], 'b--') #plt.plot(x,c)是在畫圖窗口裡具體繪製橫軸為x 縱軸為c的曲線 ‘b--’為藍色破折線 plt.xlabel('Minibatch number') plt.ylabel('Loss') plt.title('Minibatch run vs. Training loss') plt.show() print ("x=0.2,z=", sess.run(z, feed_dict={X: 0.2}))
學習速率對於梯度下降演算法,這應該是一個最重要的超引數。如圖所示,如果學習速率設定得非常大,那麼訓練可能不會收斂,就直接發散了;如果設定的比較小,雖然可以收斂,但是訓練時間可能無法接受;如果設定的稍微高一些,訓練速度會很快,但是當接近最優點會發生震盪,甚至無法穩定。不同學習速率的選擇影響可能非常大。 理想的學習速率是:剛開始設定較大,有很快的收斂速度,然後慢慢衰減,保證穩定到達最優點。
下面記錄一下各種函式的作用,有助於學習記憶python庫和函式:
1.首先是linspace函式:numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
返回(start,stop)間的num個數,endpoint為真sample必然有終點,為否必然沒有終點,retstep表示一定步長,最後返回(sample,step)
2.np.random.randn : numpy.random.rand(d0, d1, ..., dn)
隨機產生維度為(d0,d1...,dn)的隨機數,從標準正態分佈中產生。
但random.rand(d0,d1...,dn)產生數值取值範圍是[0,1)
3.tf.placeholder : tf.placeholder(dtype,shape = None , name = None)
佔位數,可以理解為變數,在需要的時候才去賦值
dtype 表示資料型別,例如tf.float32,tf.float64,shape表示陣列形狀,shape = None 表示一維數,還可以shape =[3,4],shape = [None,4]表示行未定。返回Tensor型別。
4.tf.Variable.int()
tf.Variable.init(initial_value, trainable=True, collections=None, validate_shape=True, name=None)
引數表引用相關資料:
initial_value |
所有可以轉換為Tensor的型別 |
變數的初始值 |
trainable |
bool |
如果為True,會把它加入到GraphKeys.TRAINABLE_VARIABLES,才能對它使用Optimizer |
collections |
list |
指定該圖變數的型別、預設為[GraphKeys.GLOBAL_VARIABLES] |
validate_shape |
bool |
如果為False,則不進行型別和維度檢查 |
name |
string |
變數的名稱,如果沒有指定則系統會自動分配一個唯一的值 |
5.tf.random_normal() : tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
程式碼中僅用到shape=[1],表示一維數
6.tf.reduce_mean(input_tensor, reduction_indices=None, keep_dims=False, name=None) 求平均值
input_tensor:待求值的tensor。
reduction_indices:在哪一維上求解。
7.tf.train.GrandientDescentOptimizer(learning_rate)
產生一個規定學習率的優化器:optmizier = tf.train.GrandientDescentOptimizer(learning_rate = 0.01)
計算梯度並且直接作用於變數上:optimizer.minimize(cost , var_list = <list of variables>)
optimizer.run()
計算出梯度:gradients = optimizer.compute_gradients(loss , <list of variables>)
然後就可以根據自己的需要處理梯度了。
8.tf.global_variables_initializer()初始化所有Tensor變數的狀態
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
9.def moving_average(a, w=10):
if len(a) < w:
return a[:]
return [val if idx < w else sum(a[(idx-w):idx])/w for idx, val in enumerate(a)]
滑動平均模型 用途:用於控制變數的更新幅度,使得模型在訓練初期引數更新較快,在接近最優值處引數更新較慢,幅度較小
方式:主要通過不斷更新衰減率來控制變數的更新幅度.
在訓練神經網路時,不斷保持和更新每個引數的滑動平均值,在驗證和測試時,引數的值使用其滑動平均值能有效提高神經網路的準確率。
第一個返回值是如果a向量的長度小於w的值,就輸出向量a中的所有值。
第二個返回值是如果向量a的長度大於w值,將索引小於長度w的值直接輸出,將索引大於w值的按照sum(a[(idx-w):idx])/w的方式進行計算輸出。
在python中enumerate的用法多用於在for迴圈中得到計數,
enumerate引數為可遍歷的變數,如 字串,列表等; 返回值為enumerate類。
舉個例子a=[a,b,c,d]
For idx,val in enumerate(a) #idx為向量a的索引即下標
[0 a] [1 b] [2 c] [3 d]
在python中enumerate的用法多用於在for迴圈中得到計數,
enumerate引數為可遍歷的變數,如 字串,列表等; 返回值為enumerate類。
tensorflow開發的基本步驟
1.定義輸入節點的方法 2.定義學習引數的變數 3.定義運算 4.優化函式,優化目標 5.初始化所有變數 6.迭代更新引數到最優解 7.測試模型8. 使用模型
模型內部的資料流向
1 正向 :資料從輸入開始,依次進行各節點定義的運算,一直運算到輸出,是模型最基本的資料流向。它直觀地表現了網路模型的結構,在模型的訓練、測試、使用場景中都會用到。這部分是必須要掌握的。
2 反向 :只有在訓練場景下會用到。這裡使用了一個叫做反向鏈式求導的方法,即先從正向的最後一個節點開始,計算此時結果值與真實值的誤差,這樣會形成一個用學習引數表示誤差的方程,然後對方程中的每個引數求導,得到其梯度修正值,同時反推出上一層的誤差,這樣就將該層節點的誤差按照正向的相反方向傳到上一層,並接著計算上一層的修正值,如此反覆下去一步一步地進行轉播,直到傳到正向的第一個節點。 這部分原理TensorFlow已經實現好了,簡單理解即可,應該把重點放在使用什麼方法來計算誤差,使用哪些梯度下降的優化方法,如何調節梯度下降中的引數(如學習率)問題上。
一個標準的模型結構分為輸入、中間節點、輸出三大部分,而如何讓這三部分連通起來學習規則並可進行計算,則是框架TensorFlow所做的事情。
TensorFlow將中間節點及節點間的運算關係(OPS)定義在自己內部的一個“圖”上,全通過一個“會話(session)”進行圖中OPS的具體運算。
可以這樣理解: “圖”是靜態的,無論做任何加、減、乘、除,它們只是將關係搭建在一起,不會有任何計算。 “會話”是動態的,只有啟動會話後才會將資料流向圖中,並按照圖中的關係運算。並將最終的結果從圖中流出。
TensorFlow用這種方式分離了計算的定義和執行,“圖”類似施工圖(blueprint),而會話更像施工地點。
構建一個完整的圖一般需要定義3種變數。 輸入節點:即網路的入口。 用於訓練的模型引數(也叫學習引數):是連線各個節點的路徑。 模型中的節點(OP):最複雜的就是OP。OP可以用來代表模型中的中間節點,也可以代表最終的輸出節點,是網路中的真正結構。