tensorflow的一些基礎用法


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

類似python的with 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)方法求解\(\theta=(X^T\cdot X)^{-1}\cdot X^T\cdot 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')#轉為列向量(1D -> 2D)
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計算梯度下降了,最終的迭代公式為$ \theta^{'}=\frac{2}{m}X^T\cdot (X\cdot \theta-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\) tf.assign()是創建一個新的節點,為variable更新值

2.1使用TF自動求導####

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

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

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

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求導###

前面使用的是批梯度下降方法求解\(\theta\),這種方法比較適用與較小的數據集,如果數據集很大最好使用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() 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM