1. 程式人生 > >【TensorFlow學習筆記】2:基本使用流程和使用檢查點,按照時間自動管理檢查點

【TensorFlow學習筆記】2:基本使用流程和使用檢查點,按照時間自動管理檢查點

學習《深度學習之TensorFlow》時的一些實踐。


TF的基本使用

對於分類問題的特徵X和標籤Y,分別定義tf.placeholder,這是計算圖輸入資料的入口。

對於模型中的引數(注意不是超引數),如往往是權向量w和偏置b,定義tf.Variable,並傳入初始的值,模型訓練就是在改變這些引數的值。

定義前向結構,即計算圖中,特徵X以及前面的引數經過怎樣的運算結合得到標籤Y的預測值Z。

定義損失函式,它是基於Y和Z的一個具體的計算過程。

定義優化器,這是一個反向過程。因為是針對損失來做優化的,所以上一步定義的損失要傳入這個優化器裡。

定義tf.Variable

的初始化過程,並定義好超引數,準備訓練。

啟動Session。

  • 通過Session的run()方法啟動初始化過程。

  • 對於每個epoch,通過Session的run()方法啟動優化器來訓練模型引數。

  • 上一步迴圈結束後,訓練過程也就結束了,可以通過Session的run()方法來檢視損失、預測值Z等。

關閉Session。

要注意,除了啟動初始化過程,其餘的run過程都需要通過引數feed_dict將具體的X傳入,對於訓練過程和計算損失的過程,還需要將Y傳入。

在訓練完成後,tf.Variable如w和b是可以直接訪問的,因為這決定了訓練好的模型,而損失loss、預測值Z等都是依賴於輸入的X和Y的,需要feed_dict

將其傳入。

一些小測試

定義常量

import tensorflow as tf

a = tf.constant(3)
b = tf.constant(4)
with tf.Session() as sess:
    print("相加: %i" % sess.run(a + b))
    print("相乘: %i" % sess.run(a * b))

相加: 7
相乘: 12

計算圖中的運算結點

import tensorflow as tf

a = tf.placeholder(tf.int16)
b = tf.placeholder(tf.int16)
add = tf.add(a, b) mul = tf.multiply(a, b) with tf.Session() as sess: print("相加: %i" % sess.run(add, feed_dict={a: 3, b: 4})) print("相乘: %i" % sess.run(mul, feed_dict={a: 3, b: 4})) print("一起取出多個結點:", sess.run([add, mul], feed_dict={a: 4, b: 5}))

取出檢查點檔案中的模型引數

這個是在下面的例子生成的檢查點檔案的基礎上的。

from tensorflow.python.tools.inspect_checkpoint import print_tensors_in_checkpoint_file

savedir = "../z3/"
# 預設情況下,Saver使用tf.Variable.name屬性來儲存變數
# 這裡用print_tensors_in_checkpoint_file()輸出其中所有變數和它的值
print_tensors_in_checkpoint_file(savedir + "linermodel.ckpt-18", tensor_name=None, all_tensors=True)

tensor_name: bias
[-0.05547838]
tensor_name: weight
[2.0291195]

擬合線性迴歸模型並使用檢查點

除了上面的流程外,這裡選擇了在epoch為偶數時將模型儲存在檢查點檔案中。之所以這樣做(而不是在訓練完成再儲存),是因為訓練模型時可能因為各種原因中斷訓練,用這種方式至少可以確保記錄了中間的結果,即使訓練中途崩了,下次也不必重新訓練,只要拿出模型繼續訓練就好了。

注意38行的saver = tf.train.Saver(max_to_keep=1),這可以保證只儲存一個檢查點檔案,這樣每次就會覆蓋掉之前儲存的,而只保留最新的。

在程式的最後從檢查點中讀取了模型,並做了預測。這裡發現和模型訓練完後的預測值不一樣,因為這個程式裡最後一次儲存檢查點並不是模型訓練完的那一次(少了一次,epoch實際是0~19而在18時做了最後一次儲存)。

import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt
import math

# 生成樣本,y=2x噪聲上下0.1
X_train = np.linspace(-1, 1, 100)
Y_train = 2 * X_train + np.random.randn(*X_train.shape) * 0.2 - 0.1
# plt.plot(X_train, y_train, 'ro', label="原始資料")
# plt.legend()
# plt.show()

"""建立模型"""
# 佔位符
X = tf.placeholder("float")
Y = tf.placeholder("float")
# 模型引數
W = tf.Variable(tf.random_normal([1]), name="weight")  # 一維的權重w_1,初始化為-1到1的隨機數
b = tf.Variable(tf.zeros([1]), name="bias")  # 一維的偏置b,初始化為0
# (1)前向結構:通過正向生成一個結果
Z = tf.multiply(X, W) + b
# (2)定義損失的計算:這裡就是所有的y和z的差的平方的平均值
# reduce_mean用於計算指定axis的平均值,未指定時則對tensor中每個數加起來求平均
cost = tf.reduce_mean(tf.square(Y - Z))  # <class 'tensorflow.python.framework.ops.Tensor'>
# (3)反向優化:通過反向過程調整模型引數.這裡使用學習率為0.01的梯度下降最小化損失cost
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(cost)

"""迭代訓練模型"""
# 初始化過程:初始化所有的變數
init = tf.global_variables_initializer()
# 模型的超引數,這裡epoch是模型會完整學習多少次樣本
train_epochs = 20
# 這個僅僅是控制每多少個epoch顯示下模型的詳細資訊
display_step = 2
# tf.train.Saver用於儲存模型到檔案/從檔案中取用模型
# 使用了檢查點,這裡指定max_to_keep=1即在迭代過程中只儲存一個檔案
# 那麼在迴圈訓練中,新生成的模型會覆蓋之前的模型
saver = tf.train.Saver(max_to_keep=1)

# 啟動Session,這種方式不用手動關閉Session
with tf.Session() as sess:
    # 執行初始化過程
    sess.run(init)
    # 用於記錄批次和損失
    plotdata = {"ephch": [], "loss": []}
    # 向模型輸入資料,對於每個epoch
    for epoch in range(train_epochs):
        # 都要遍歷模型中所有的樣本(x,y)
        for (x, y) in zip(X_train, Y_train):
            # 執行優化器,填充模型中X,Y佔位符的內容為這個具體的樣本(x,y)
            sess.run(optimizer, feed_dict={X: x, Y: y})
        # 每display_step個epoch顯示一下訓練中的詳細資訊
        if epoch % display_step == 0:
            # 計算下損失.每次要計算中間變數的當前值時候都要sess.run得到
            loss = sess.run(cost, feed_dict={X: X_train, Y: Y_train})
            print("Epoch:", epoch, "cost=", loss, "W=", sess.run(W), "b=", sess.run(b))
            # 如果損失存在,將該批次和對應損失記錄下來
            if loss != "NA":
                plotdata["ephch"].append(epoch)
                plotdata["loss"].append(loss)
            """儲存檢查點"""
            # 這裡選擇在每次輸出資訊後儲存一下檢查點,同時使用global_step記錄epoch次數
            saver.save(sess, "./linermodel.ckpt", global_step=epoch)
    print("完成,cost=", sess.run(cost, feed_dict={X: X_train, Y: Y_train}), "W=", sess.run(W), "b=", sess.run(b))
    # 提前計算出結果以在Session外也能使用,下面這種方式都可以
    result = sess.run(W) * X_train + sess.run(b)
    result2 = sess.run(Z, feed_dict={X: X_train})
    # 這裡驗證一下它們中對應項的值是相等的
    for k1, k2 in zip(result, result2):
        assert math.isclose(k1, k2, rel_tol=1e-5)
    """使用模型"""
    # 如果要使用模型,直接將樣本的值傳入並計算輸出Z即可
    print("對x=5的預測值:", sess.run(Z, feed_dict={X: 5}))
    """儲存模型到檔案"""
    # saver.save(sess, "./linermodel.ckpt")

"""訓練模型視覺化"""
plt.scatter(X_train, Y_train, c='r', marker='o', label="原始資料")
plt.plot(X_train, result, label="擬合直線")
plt.legend()
plt.show()


def moving_average(a, w=10):
    """
    對損失序列a,生成w-平均損失序列
    即每個位置的損失由其和其前的共w個損失的平均來代替
    """
    if len(a) < w:  # 當w太小不足以計算任何元素的平均時
        return a[:]  # 直接返回a的複製
    return [val if idx < w else sum(a[idx - w:idx]) / w for idx, val in enumerate(a)]


"""繪製平均loss變化曲線"""
plotdata["avgloss"] = moving_average(plotdata["loss"])
plt.plot(plotdata["ephch"], plotdata["avgloss"], 'b--')
plt.xlabel("ephch")
plt.ylabel("avg loss")
plt.title("平均損失變化")
plt.show()

"""從檔案載入模型"""
with tf.Session() as sess2:
    sess2.run(init)  # 還是需要執行一下初始化過程
    # saver.restore(sess2, "./linermodel.ckpt")
    # 在當前目錄下尋找最近的檢查點並載入
    ckpt = tf.train.latest_checkpoint("./")
    if ckpt is not None:
        saver.restore(sess2, ckpt)
    print("對x=5的預測值:", sess2.run(Z, feed_dict={X: 5}))

Epoch: 0 cost= 0.1917328 W= [1.4377091] b= [0.12655073]
Epoch: 2 cost= 0.055657808 W= [1.871314] b= [0.00432279]
Epoch: 4 cost= 0.04542881 W= [1.9882357] b= [-0.03980212]
Epoch: 6 cost= 0.044927694 W= [2.0185487] b= [-0.05142205]
Epoch: 8 cost= 0.044943742 W= [2.0263882] b= [-0.05443016]
Epoch: 10 cost= 0.044957623 W= [2.028414] b= [-0.05520765]
Epoch: 12 cost= 0.044961873 W= [2.0289385] b= [-0.05540875]
Epoch: 14 cost= 0.044963013 W= [2.029074] b= [-0.05546085]
Epoch: 16 cost= 0.044963315 W= [2.0291097] b= [-0.05547457]
Epoch: 18 cost= 0.044963405 W= [2.0291195] b= [-0.05547838]
完成,cost= 0.04496341 W= [2.0291212] b= [-0.05547896]
對x=5的預測值: [10.090127]
在這裡插入圖片描述
在這裡插入圖片描述對x=5的預測值: [10.090119]

按照時間自動管理檢查點

書上用一個全域性張量每次自增1為例。

import tensorflow as tf

'''
按照時間儲存檢查點
'''

# 清除預設圖堆疊,重置全域性的預設圖
tf.reset_default_graph()
# 建立(如果需要的話)並返回global step tensor,預設引數graph=None即使用預設圖
# 當用這種方法按照時間儲存檢查點時,必須要定義這個global step張量
global_step = tf.train.get_or_create_global_step()
# 這裡是為global_step張量每次增加1
step = tf.assign_add(global_step, 1)
# 通過MonitoredTrainingSession實現按時間自動儲存,設定檢查點儲存目錄,設定儲存間隔為2秒
with tf.train.MonitoredTrainingSession(checkpoint_dir='log/checkpoints', save_checkpoint_secs=2) as sess:
    # 輸出一下當前global_step這個張量的值
    print(sess.run([global_step]))
    # 這裡是一個死迴圈,只要sess不結束就一直迴圈
    while not sess.should_stop():
        # 執行step,即為global_step這個張量增加1
        i = sess.run(step)
        # 每次執行都輸出一下
        print(i)

多次執行可以發現數字(global_step這個張量的值)並不會從頭開始,而是因為每2秒儲存一次,從上次儲存的檢查點開始。

如某次的執行結果如下:
在這裡插入圖片描述