首頁 > 軟體

TensorFlow入門初探

2020-06-16 16:58:55

TensorFlow是一個採用資料流圖,用於數值計算的開源軟體庫。自己接觸tensorflow比較的早,可是並沒有系統深入的學習過,現在TF在深度學習已經成了“標配”,所以打算系統的學習一遍。在本篇文章中主要介紹TF的基礎知識。。。

建立並執行圖

首先建立 兩個變數

import tensorflow as tf
reset_graph()
x = tf.Variable(3, name="x")
y = tf.Variable(4, name="y")
f = x*x*y + y + 2
>>f
<tf.Tensor 'add_3:0' shape=() dtype=int32>

然而,f中並沒有得到想要的結果,實際上上面的程式碼並沒有真正的執行,它只是建立了一個計算圖(compution graph),並且定義的變數也並沒有被初始化,為了計算剛才定義的圖,我們需要開啟一個session,然後初始化上面的變數。

sess = tf.Session()
sess.run(x.initializer)
sess.run(y.initializer)
result = sess.run(f)
sess.close()  # 最終關閉這個session
>>print(result)
42

類似Pythonwith open()語法,我們還可以這樣寫

with tf.Session() as sess:
    x.initializer.run() #sess.run(x.initializer)
    y.initializer.run()
    result = f.eval()  #sess.run(f)
    print(result)

注意上面程式碼註釋的部分,這兩鐘方法是等價的,即

 x.initializer.run()  = tf.get_default_session().run(x.initializer)

選擇哪一種寫法主要取決於哪種方法簡單。那麼如果我們由很多的變數,都需要進行初始化,再逐一初始化就顯得繁瑣了,這時候我們可以使用global_variables_initializer()方法進行初始化。

init = tf.global_variables_initializer() # 準備init節點
with tf.Session() as sess:
    init.run()              #執行初始化動作
    print(f.eval)

從上面的程式碼可以看出,TF程式的執行過程分為兩個階段,

  • 1.構建計算圖,構建能夠表示 機器學習模型的圖。
  • 2.執行部分,通常是一個迴圈,重複地對訓練步驟進行評估,改善模型的引數。

管理計算圖

當我們建立一個節點的時候, 被建立的節點自動的被新增到預設的計算圖中:

>>x.graph is tf.get_default_graph() 
True

但是大多的時候,我們想分別管理相互獨立的graphs,這時候就要建立新的graph

graph = tf.Graph()
with graph.as_default():
    x1 = tf.Variable(2)
    
print(x1.graph is graph) #True
print(x1.graph is tf.get_default_graph) #False

note:我們在使用Python shell試驗階段的時候,可能會出現輸出和我們的預期不一樣,這是因為多次執行導致預設的graph包含重複的nodes,一個解決方案是重新啟動shell,另外一個是使用tf.reset_default_graph()

節點的生命週期

節點的生命週期也成為變數的生命週期,因為在TF中每一個變數在graph中都對應一個node,當我們建立一個node,TF會自動判斷該節點的依賴關係,例如下面這段程式碼:

w = tf.constant(3)
x = w + 2
y = x + 5
z = x * 3
with tf.Session() as sess:
    print(y.eval()) #10
    print(z.eval()) #15

上面這段程式碼定義了一個簡單的graph,並計算y和z的值,TF發現y依賴x、x依賴w。所以它依次計算w、x和y。再計算z的時候,發現需要計算x和w。最終這段程式碼執行了兩次w和x。當執行完畢後所有的節點都被刪除,除了Variable值,variable的生命週期為整個session。也就是說variable的生命週期從initializer開始,到sessionclose結束。
上面這段程式碼在正式的生產環境下效率是很低的,為了避免被重複計算,我們就需要告訴TF計算y和z在同一個graph中。下面是程式碼:

with tf.Session() as sess: 
    y_val,z_val = sess.run([y,z]) ##
    print(y_val) 
    print(z_val) 

note:在單進程的TF程式中,多個session是不共用變數(資料)的,每一個session有著獨自的變數copy。在分散式TF程式中,變數是儲存在server,而不是在session中,所以多個session可以共用變數。

使用TF求解線性回歸

1 正規方程求解

在之前的文章使用sklearn進行資料探勘介紹了使用sklean進行資料探勘,這裡我們使用TF來進行計算,不過為了方便我們直接使用sklean提供的資料集,跳過資料處理過程,直接使用正規方程(Normal Equation)方法求解θ=(X T X) 1 X T y  θ=(XT⋅X)−1⋅XT⋅y 。類似Numpy,TF也提供了許多資料轉換的方法,在numpy陣列被成為ndarray,詳見掌握numpy,在TF中的多維陣列被成為張量(tensors)。

import numpy as np
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
m,n = housing.data.shape
data = np.c_[np.ones((m,1)),housing.data] #新增X0=1
X = tf.constant(data,dtype=tf.float32,name='X')
y = tf.constant(housing.target.reshape(-1,1),dtype=tf.float32,name='y')#轉為列向量(D -> D)
X_T = tf.transpose(X)
theat = tf.matmul(tf.matmul(tf.matrix_inverse( tf.matmul(X_T,X)),X_T),y)    
with tf.Session() as sess:
    theat_hat = theat.eval()
    print(theat_hat)

上面這段程式碼可以完全使用Numpy替代,當然也可以使用sklearn的回歸方法,也是分分鐘搞定的事情,

from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(housing.data, housing.target.reshape(-1, 1))
print(np.vstack((lin_reg.intercept_.reshape(-1, 1), lin_reg.coef_.T)))

使用TF的優勢是可以使用GPU進行運算。
note:reshape(-1,1)的作用是將一維陣列轉化為二維陣列,引數-1表示unspecified,表示會根據陣列的長度作為這一維度的值。

2 使用批梯度下降求解

上面使用的是正規方程求解,現在我們使用梯度下降方法求解,在求解之前我們需要現對資料做normalize,否則會導致收斂速度慢

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaled_data = scaler.fit_transform(housing.data)
data = np.c_[np.ones((m,1)),scaled_data] #新增X0=1

下面就是使用TF計算梯度下降了,最終的疊代公式為θ    =2m X T (Xθy) θ′=2mXT⋅(X⋅θ−y) ,這裡就不再贅述。

n_epoch = 100
learning_rate = 0.1
X = tf.constant(data,dtype=tf.float32,name='X')
y = tf.constant(housing.target.reshape(-1,1),dtype=tf.float32,name='y')
theta = tf.Variable(tf.random_uniform([n+1,1],-1,1),name='theta')
y_pred = tf.matmul(X,theta,name='prediction')
error = y_pred - y
mse = tf.reduce_mean(tf.square(error),name='mse') #Mean Squared Error 
gradient = 2/m * tf.matmul(tf.transpose(X),error)
training_op = tf.assign(theta,theta - learning_rate * gradient)

init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    for epoch in range(n_epoch):
        if epoch % 100 == 0:
            print("Epoch", epoch, "MSE =", mse.eval())
        sess.run(training_op)
    >>print('best theta:',theta.eval())
Epoch 0 MSE = 9.16154
Epoch 100 MSE = 0.714501
Epoch 200 MSE = 0.566705
Epoch 300 MSE = 0.555572
Epoch 400 MSE = 0.548812
Epoch 500 MSE = 0.543636
Epoch 600 MSE = 0.539629
Epoch 700 MSE = 0.536509
Epoch 800 MSE = 0.534068
Epoch 900 MSE = 0.532147
'best theta:'
[[ 2.06855249],
 [ 0.88740271],
 [ 0.14401658],
 [-0.34770882],
 [ 0.36178368],
 [ 0.00393812],
 [-0.04269557],
 [-0.66145277],
 [-0.63752776]]

上面的程式碼比較簡單,tf.random_uniform()生成一個均勻分布,大小為(n+1,1),取值範圍(-1,1)。至於為什麼n+1,是因為考慮到x 0 =1 x0=1 tf.assign()是建立一個新的節點,為variable更新值

2.1使用TF自動求導

上面程式碼通過手動計算損失函數導數的迭代公式計算出θ θ 的值,一個線性回歸手動算起來固然容易,但當模型為一個神經網路再進行手動求導就會很吃力了。TF提供了自動求導功能,只需要將上面那段程式碼的梯度部分替換成下面

gradient = tf.gradients(mse,[theta])[0]

上面的gradients()方法能夠自動的將損失函數針對引數進行求導(本例分別為mse mse θ θ ),

2.2使用優化器

TF提供了計算梯度的方法,非常方便,不過還可以變得更加的方便。TF提供了許多優化方法,例如梯度下降優化器(Gradient Descent optimizer)。僅僅需要將gradient = 和training_op替換為以下程式碼:

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)

有許多的優化方法,例如MomentumOptimizer

3 向演算法中輸入資料和mini-batch求導

前面使用的是批梯度下降方法求解θ θ ,這種方法比較適用與較小的資料集,如果資料集很大最好使用mini-batch梯度下降方法。我們需要將上面的程式碼疊代部分的Xy替換為mini-batch,可以使用placeholder來實現mini-batch,顧名思義這是使用預留位置的方法,它們並不參與運算,只有在你指定訓練的時候才輸出資料,舉一個例子:

A = tf.placeholder(tf.float32,shape=(None,3))
B = A + 5
with tf.Session() as sess:
    test_b_1 = B.eval(feed_dict={A:[[1,2,3]]})
    test_b_2 = B.eval(feed_dict={A:[[4,5,6],[7,8,9]]})
print(test_b_1)  #[[ 6.  7.  8.]]
print(test_b_2) #[[  9.  10.  11.]  [ 12.  13.  14.]]

上面這段程式碼使用placeholder()建立一個預留位置節點,並且指定其數值型別和輸入形狀,None表示任意長度。接著又建立一個節點為B=A+5。當計算B的值時候,使用feed_dict以字典的型別傳入到eval()中。
實現mini-batch我們只需要修改少量程式碼,首先我們需要先定義好參與疊代的X和y

X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")

然後定義需要疊代的次數、學習率、batch的大小以及batch的個數還有目標函數

learning_rate = 0.01
batch_size = 100
n_batches = int(np.ceil(m / batch_size))
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta") #X0
y_pred = tf.matmul(X, theta, name="predictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
init = tf.global_variables_initializer()

最後就是計算過程,mini-batch逐個被送到訓練演算法中

def fetch_batch(epoch, batch_index, batch_size):
    np.random.seed(epoch * n_batches + batch_index) 
    indices = np.random.randint(m, size=batch_size)  
    X_batch = data[indices] 
    y_batch = housing.target.reshape(-1, 1)[indices] 
    return X_batch, y_batch

with tf.Session() as sess:
    sess.run(init)
    for epoch in range(n_epochs):#疊代的次數
        for batch_index in range(n_batches):
            X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})

    best_theta = theta.eval()

模型的持久化

類似sklearn,模型訓練好之後我們可以將model持久化,以備以後的使用TF提供了Saver()方法,

init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
    sess.run(init)
    for epoch in range(n_epochs):
        if epoch % 100 == 0:
            print("Epoch", epoch, "MSE =", mse.eval())                                # 儲存執行過程
            save_path = saver.save(sess, "/tmp/my_model.ckpt")
        sess.run(training_op)
    
    best_theta = theta.eval()
    save_path = saver.save(sess, "/tmp/my_model_final.ckpt")#儲存最後的結果

模型的載入也是很簡單的:

with tf.Session() as sess:
    saver.restore(sess, "/tmp/my_model_final.ckpt")
    best_theta_restored = theta.eval() 

本文永久更新連結地址http://www.linuxidc.com/Linux/2017-12/149554.htm


IT145.com E-mail:sddin#qq.com