1. 程式人生 > >TensorFlow指南(一)——上手TensorFlow

TensorFlow指南(一)——上手TensorFlow

http://blog.csdn.net/u011239443/article/details/79066094
TensorFlow是谷歌開源的深度學習庫。不多介紹,相信準備學習TensorFlow的同學也會自己去更多的瞭解。本系列博文講儘量不涉及深度學習理論,但是會給出相關理論對應的博文等資料供大家參閱。

這裡,首先要跟大家介紹一個計算圖的概念:

TensorFlow會根據程式碼先建立好計算圖,然後資料會再流入這樣的計算圖中:

這個概念能幫助我們在編碼的時候更好的去理解。我們再來理解一下TensorFlow字面上的意思:

Tensor,張量,其實我們可以簡單的理解為是多維陣列,這也是TensorFlow中的基本資料結構。
Flow,流,很直觀的表達了,Tensor之間的轉化是一種類似於資料流的方式。

1. 安裝

2. 初體驗

先建立計算圖:

import tensorflow as tf
x = tf.Variable(3, name="x") 
y = tf.Variable(4, name="y") 
f=x*x*y+y+2

計算:

init = tf.global_variables_initializer() # 準備初始化全域性變數,及 x 和 y
with tf.Session() as sess:
    init.run() # 初始化
    result = f.eval() # 計算 f
print(result)

可以看到,TensorFlow編碼具有惰性,with tf.Session()

之前的程式碼不會直接執行,而是最後在Sessionrun的。

3. 節點值的生命週期

這裡需要注意的概念是:

多個run不會重用已計算過的相同節點的值。
什麼意思呢?我們來看下面這段程式碼:

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

這段程式碼Session中會有兩個run,第一個run先計算出w,再計算出x,最後計算出y。第二個run不會直接去複用x,而是從頭開始,先計算出w,再計算出x,最後計算出z。如果想避免重複計算,必須只使用一個run

,如下:

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

4. 實現梯度下降

4.1 手動計算梯度下降

# -*- coding:utf-8 -*- 
import numpy as np
import tensorflow as tf
from sklearn.datasets import fetch_california_housing
from sklearn.preprocessing import StandardScaler


n_epochs = 1000 # 迭代次數
learning_rate = 0.01

housing = fetch_california_housing()
m,n = housing.data.shape
scaler = StandardScaler()
scaled_housing_data = scaler.fit_transform(housing.data)
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]


X = tf.constant(scaled_housing_data_plus_bias,dtype=tf.float32)
y = tf.constant(housing.target.reshape(-1,1),dtype=tf.float32)
theta = tf.Variable(tf.random_uniform([n+1,1],-1.0,1.0)) # 用隨機數初始化
y_pred = tf.matmul(X,theta)
error = y_pred - y
mse = tf.reduce_mean(tf.square(error))
gradients = 2/m * tf.matmul(tf.transpose(X),error)
training_op = tf.assign(theta,theta-learning_rate * gradients)

init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)
    #for x in theta:
    #    print(x)
    for epoch in range(n_epochs):
        if epoch % 100 == 0:
            print("Epoch", epoch, "MSE =", mse.eval())
        sess.run(training_op)
Epoch 0 MSE = 6.46781
Epoch 100 MSE = 0.664388
Epoch 200 MSE = 0.543974
Epoch 300 MSE = 0.536818
Epoch 400 MSE = 0.534512
Epoch 500 MSE = 0.532798
Epoch 600 MSE = 0.531383
Epoch 700 MSE = 0.530206
Epoch 800 MSE = 0.529225
Epoch 900 MSE = 0.528407

4.2 梯度下降優化器

TensorFlow也有直接自己封裝好的梯度下降優化器 GradientDescentOptimizer ,我們可以直接使用它來優化
mse。我們只需要把上述程式碼中的:

gradients = 2/m * tf.matmul(tf.transpose(X),error)
training_op = tf.assign(theta,theta-learning_rate * gradients)

替換成

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
Epoch 0 MSE = 7.25867
Epoch 100 MSE = 0.694411
Epoch 200 MSE = 0.577526
Epoch 300 MSE = 0.56535
Epoch 400 MSE = 0.55735
Epoch 500 MSE = 0.551041
Epoch 600 MSE = 0.54601
Epoch 700 MSE = 0.541978
Epoch 800 MSE = 0.538734
Epoch 900 MSE = 0.536115

5. Mini-batch 梯度下降

X = tf.placeholder(tf.float32,(None,n+1),'X')
y = tf.placeholder(tf.float32,(None,1),'y')
batch_size = 100
n_batches = int(np.ceil(m/batch_size))

def fetch_batch(epoch,batch_index,batch_size):
    np.random.seed(epoch*n_batches+batch_index) # 每次呼叫 都有不同的 隨機種子
    indices = np.random.randint(m,size=batch_size) # 去 0 ~ m-1 之間去 batch_size 整數
    X_batch = scaled_housing_data_plus_bias[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):
        if epoch%100==0:
            print("Epoch", epoch, "MSE =", mse.eval())

        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})
Epoch 0 MSE = 12.6052
Epoch 100 MSE = 0.524321
Epoch 200 MSE = 0.524321
Epoch 300 MSE = 0.524321
Epoch 400 MSE = 0.524321
Epoch 500 MSE = 0.524321
Epoch 600 MSE = 0.524321
Epoch 700 MSE = 0.524321
Epoch 800 MSE = 0.524321
Epoch 900 MSE = 0.524321

6. 儲存 & 載入 模型

我們可以使用 Saver 來儲存或者載入已經訓練好的模型:

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())

        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})
    save_path = saver.save(sess,'./my_model_final.ckpt')   

可以看到路徑下的檔案:

我們可以使用該路徑載入模型:

with tf.Session() as sess:
    saver.restore(sess,'./my_model_final.ckpt')
    print(mse.eval())
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0.524321

7. 使用 TensorBoard 視覺化訓練

TensorFlow在訓練模型的時候可以將訓練過程通過日誌儲存下來。TensorBoard可以根據這些日誌來視覺化訓練過程。
首先,我們利用當前時間給日誌檔案起一個唯一名字:

from datetime import datetime
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logs = 'tf_logs'
logdir = "{}/run-{}".format(root_logs,now)
  • scalar 用於獲取摘要資訊,

  • FileWriter類提供了一種機制,在給定的目錄中建立事件檔案,並向其新增摘要和事件。該類將非同步更新檔案內容。這允許訓練程式呼叫方法,直接從訓練迴圈中直接向檔案新增資料,而不需要減慢訓練的速度。

mse_summary = tf.summary.scalar('MSE',mse)
file_writer = tf.summary.FileWriter(logdir,tf.get_default_graph())

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)
            if batch_index % 10 == 0:
                summary_str = mse_summary.eval(feed_dict={X:X_batch,y:y_batch})
                step = epoch * n_batches + batch_index
                file_writer.add_summary(summary_str,step)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})

目錄下生成了檔案:

命令執行TensorBoard:

tensorboard --logdir tf_logs/


瀏覽器中開啟該連結,可以看到訓練過程中mse視覺化的展示:

按GRAPHS,可以看到計算圖

8.名字空間

當處理更復雜的模型,比如神經網路時,計算圖很容易就會變成成千上萬個節點。為了避免這種情況,我們可以建立名稱範圍來分組相關的節點。比如我們把上述程式碼中的 error 和 mse 發到 “loss”空間裡面:

with tf.name_scope("loss") as scope:
    error = y_pred - y
    mse = tf.reduce_mean(tf.square(error), name="mse")

使用tensorboard,我們可以看到新的計算圖:

print(error.op.name)
print(mse.op.name)
loss/sub
loss/mse

9. 模組化

我們先試著實現這樣功能的程式碼:兩個迴歸模型,分別餵給ReLU,再將得到的結果相加:

reset_graph()
n_features = 3
X = tf.placeholder(tf.float32,(None,n_features),name='X')
w1 = tf.Variable(tf.random_normal((n_features,1)),name='w1')
w2 = tf.Variable(tf.random_normal((n_features,1)),name='w2')
b1 = tf.Variable(0.0,name='b1')
b2 = tf.Variable(0.0,name='b2')

z1 = tf.add(tf.matmul(X,w1),b1,name='z1')
z2 = tf.add(tf.matmul(X,w2),b2,name='z1')

relu1 = tf.maximum(0.0,z1,name='relu1')
relu2 = tf.maximum(0.0,z2,name='relu1')


output = tf.add(relu1,relu2)

file_writer = tf.summary.FileWriter("logs/relu0", tf.get_default_graph())

得到的計算圖:

看程式碼,就感覺有好多重複的地方。如果需要10、20個ReLU相加的話,可想而知就會非常冗餘。

我們可以使用模組化的方法將其簡化:


reset_graph()

def relu(X):
    with tf.name_scope('relu'):
        w_shape = (int(X.get_shape()[1]),1)
        w = tf.Variable(tf.random_normal(w_shape),name='w')
        b = tf.Variable(0.0,name='b')
        z = tf.add(tf.matmul(X,w),b)
        return tf.maximum(0.0,z,name='max')


n_features = 3
X = tf.placeholder(tf.float32,(None,n_features),name='X')
relus = [relu(X) for i in range(10)]
output = tf.add_n(relus)

file_writer = tf.summary.FileWriter("logs/relu2", tf.get_default_graph())
file_writer.close()

這裡除了使用模組化,還將節點塞入了名字空間’relu’中。模組化會自動生成10個 relu 節點,第一個 relu 節點名字為‘relu’,第二個為‘relu_1’,以此類推。add_n 會將 tensor list 中的元素累加。我們可以看到計算圖為:

10.共享變數

繼續上面的程式碼,如果我們想使用 想給各個 ReLu 設定一個閾值,該怎麼實現呢:


reset_graph()

def relu(X):
    with tf.variable_scope('relu',reuse=True):
        threshold = tf.get_variable('threshold')
        w_shape = (int(X.get_shape()[1]),1)
        w = tf.Variable(tf.random_normal(w_shape),name='w')
        b = tf.Variable(0.0,name='b')
        z = tf.add(tf.matmul(X,w),b)
        return tf.maximum(threshold,z,name='max')


n_features = 3
X = tf.placeholder(tf.float32,(None,n_features),name='X')
with tf.variable_scope('relu'):
    threshold = tf.get_variable('threshold',shape=(),initializer=tf.constant_initializer(0.0))
relus = [relu(X) for i in range(10)]
output = tf.add_n(relus)

file_writer = tf.summary.FileWriter("logs/relu3", tf.get_default_graph())
file_writer.close()

在 relu 函式外建立一個變數空間‘relu’,在該變數空間裡面建立threshold,並初始化。在‘relu’變數空間下,是共享一個threshold的。所以,將relu函式中的變數塞到 在‘relu’變數空間下,設定
reuse=True。在‘relu’變數空間下,呼叫get_variable(‘threshold’),就能得到共享變數 threshold。計算圖如下: