2021-05-12 14:32:11
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)方法求解θ=(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梯度下降方法。我們需要將上面的程式碼疊代部分的X
和y
替換為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
相關文章