TensorFlow低階API(一)—— 簡介


簡介

本文旨在知道您使用低級別TensorFlow API(TensorFlow Core)開始編程。您可以學習執行以下操作:

  • 管理自己的TensorFlow程序(tf.Graph)和TensorFlow運行時(tf.Session),而不是依靠Estimator來管理它們
  • 使用tf.Session運行TensorFlow操作
  • 在此低級別環境中使用高級別組件(數據集、層和feature_columns)
  • 構建自己的訓練循環,而不是使用Estimator提供的訓練循環

我們建議盡可能使用高階的API構建模型。以下是TensorFlow Core為何很重要的原因:

  • 如果您能夠使用低階TensorFlow操作,實驗和調試都會更直接
  • 在使用高階的API時,能夠理解其內部的工作原理

 

設置

在使用本教程之前,請先安裝TensorFlow

要充分理解本指南中的內容,您應當具備以下方面的知識:

  • 如何使用Python編程
  • 對陣列有所了解
  • 理想情況下,最好對機器學習有一定的了解

您隨時啟動Python,並按照以下演示進行操作。運行以下行來設置你的python環境:

1 from __future__ import absolute_import
2 from __future__ import division
3 from __future__ import print_function
4 
5 import numpy as np
6 import tensorflow as tf

 

張量值

TensorFlow的核心數據單位是張量。一個張量由一組形成陣列(任意維數)的原始值組成。張量的階是它的維數,而它的形狀是一個整數元組,指定了陣列每個維度的長度。以下是張量值的一些示例:

1 3. # a rank 0 tensor; a scalar with shape [],
2 [1., 2., 3.] # a rank 1 tensor; a vector with shape [3]
3 [[1., 2., 3.], [4., 5., 6.]] # a rank 2 tensor; a matrix with shape [2, 3]
4 [[[1., 2., 3.]], [[7., 8., 9.]]] # a rank 3 tensor with shape [2, 1, 3]

 TensorFlow使用numpy陣列來表示張量值。

 

TensorFlow Core演示

您可以將TensorFlow Core程序看作由兩個相互獨立的部分組成:

  1. 構造計算圖(tf.Graph)
  2. 運行計算圖(tf.Session)

計算圖是排列成一個圖的一系列TensorFlow指令。圖由兩種類型的對象組成:

  • 操作(簡稱“op”):圖的節點。操作描述了消耗和生成張量的計算。
  • 張量:圖的邊。它們代表將流經圖的值。大多數TensorFlow函數會返回tf.Tensors。

重要提示tf.Tensors 不具有值,它們只是計算圖中元素的手柄。

我們來構建一個簡單的計算圖。最基本的指令是一個常量。構建指令的Python函數將一個張量值作為輸入值。生成的指令不需要輸入值。它在運行時輸出的是被傳遞給構造函數的值。我們可以創建兩個如下所示的兩個浮點數常量 a 和 b :

1 a = tf.constant(3.0, dtype=tf.float32)
2 b = tf.constant(4.0) # also tf.float32 implicitly
3 total = a + b
4 print(a)
5 print(b)
6 print(total)

打印語句會生成:

1 Tensor("Const:0", shape=(), dtype=float32)
2 Tensor("Const_1:0", shape=(), dtype=float32)
3 Tensor("add:0", shape=(), dtype=float32)

請注意,打印張量並不會如您可能預期的那樣輸出值3.0、4.0和7.0。上述語句只會構建計算圖。這些tf.Tensor對象僅代表將要運行的操作的結果。

圖中的每個指令都擁有唯一的名稱。這個名稱不同於使用Python分配給相應對象的名稱。張量是根據生成它們的指令命名的,后面跟着輸出索引,如上文的 “add:0” 所示,表示該add指令的第0個輸出。

TensorBoard

TensorFlow提供了一個名為TensorBoard的使用程序。TensorBoard的諸多功能之一是將計算圖可視化。您只需要使用幾個簡單的命令就能輕松完成此操作。

首先將計算圖保存為TensorBoard摘要文件具體操作如下所示:

1 writer = tf.summary.FileWriter('.')
2 writer.add_graph(tf.get_default_graph())

這將在當前目錄中生成一個 event 文件,其命名格式如下:

1 events.out.tfevents.{timestamp}.{hostname}

現在,在新的終端使用以下shell命令啟動TensorBoard:

1 tensorboard --logdir .

接下來,在您的瀏覽器中打開TensorBoard的圖頁面,您應該會看到與以下圖形類似的圖:

要想詳細了解TensorBoard的計算圖可視化工具,請參閱TensorBoard:圖的直觀展示。

會話(Session)

要評估張量,需要實例化一個tf.Session對象(非正式名稱為會話)。會話會封裝TensorFlow運行時的狀態,並運行TensorFlow操作。如果說tf.Graph像一個 .py 文件,那么tf.Session就像一個python可執行對象。

下面的代碼會創建一個tf.Session對象,然后調用其 run 方法來評估我們在上文中所創建的 total 張量:

1 sess = tf.Session()
2 print(sess.run(total))

當您用 Session.run 請求輸出節點時,TensorFlow會回溯整個圖,並流經提供了所請求的輸出節點對應的輸入值的所有節點。因此此指令會打印預期的值7.0:

1 7.0

您可以將多個張量值傳遞給 tf.Session.run。run方法以透明方式處理元組或字典的任何組合,如下例所示:

1 print(sess.run({'ab':(a, b), 'total':total}))

它返回的結果擁有相同的布局結構:

1 {'total': 7.0, 'ab': (3.0, 4.0)}

在調用tf.Session.run期間,任何tf.Tensor都只有單個值。例如,以下代碼調用 tf.random_uniform 來生成一個tf.Tensor,后者都會生成隨機的三元素矢量(值位於[0,1)區間內):

1 vec = tf.random_uniform(shape=(3,))
2 out1 = vec + 1
3 out2 = vec + 2
4 print(sess.run(vec))
5 print(sess.run(vec))
6 print(sess.run((out1, out2)))

每次調用run時,結果都會顯示不同的隨機值,但在單個run期間(out1 和 out2接收到相同的隨即輸入值),結果顯示的值是一致的:

1 [ 0.52917576  0.64076328  0.68353939]
2 [ 0.66192627  0.89126778  0.06254101]
3 (
4   array([ 1.88408756,  1.87149239,  1.84057522], dtype=float32),
5   array([ 2.88408756,  2.87149239,  2.84057522], dtype=float32)
6 )

部分TensorFlow會返回 tf.Operations,而不是tf.Tensors。對指令調用run的結果是None。您運行指令是為了產生副作用,而不是為了檢索一個值。這方面的例子包括稍后將演示初始化和訓練操作。

供給

目前來講,這個圖不是特別有趣,因為它總是生成一個常量結果。圖可以參數化以便接收外部輸入,也稱為占位符。占位符表示承諾在稍后提供值,它就像函數參數。

1 x = tf.placeholder(tf.float32)
2 y = tf.placeholder(tf.float32)
3 z = x + y

前面三行有點像函數。我們定義了這個函數的兩個輸入參數(x 和 y),然后對它們運行指令。我們可以使用run方法的 feed_dict 參數為占位符提供具體的值,從而評估這個具有多個輸入的圖:

1 print(sess.run(z, feed_dict={x: 3, y: 4.5}))
2 print(sess.run(z, feed_dict={x: [1, 3], y: [2, 4]}))

上述操作的結果是輸出以下內容:

1 7.5
2 [ 3.  7.]

另請注意feed_dict 參數可以覆蓋圖中的任何張量。占位符和其它任何 tf.Tensors 的唯一不同之處在於如果沒有為占位符提供值,那么占位符會拋出錯誤。

 

數據集

 占位符適應於簡單的實驗,而數據集是將數據流式傳輸到模型的首要方法。

要從數據集中獲取可運行的tf.Tensor,您必須先將其轉換成 tf.data.Iterator,然后調用迭代器的 get_next 方法。

創建迭代器的最簡單的方式是采用 make_one_shot_iterator 方法。例如,在下面的代碼中,next_item 張量將在每次 run 調用時從 my_data陣列中返回一行:

1 my_data = [
2     [0, 1,],
3     [2, 3,],
4     [4, 5,],
5     [6, 7,],
6 ]
7 slices = tf.data.Dataset.from_tensor_slices(my_data)
8 next_item = slices.make_one_shot_iterator().get_next()

到達數據流末端時,Dataset會拋出 OutOfRangeError 。例如,下面的額代碼會一直讀取next_item,直到沒有數據可讀。

1 while True:
2   try:
3     print(sess.run(next_item))
4   except tf.errors.OutOfRangeError:
5     break

如果Dataset依賴於有狀態操作,則可能需要在迭代器之前先初始化它,如下所示:

 1 r = tf.random_normal([10,3])
 2 dataset = tf.data.Dataset.from_tensor_slices(r)
 3 iterator = dataset.make_initializable_iterator()
 4 next_row = iterator.get_next()
 5 
 6 sess.run(iterator.initializer)
 7 while True:
 8   try:
 9     print(sess.run(next_row))
10   except tf.errors.OutOfRangeError:
11     break

要詳細的了解數據集和迭代器,請參閱導入數據

 

可訓練的模型必須修改圖中的值,以便在輸入相同值的情況下獲得新的輸出值。將可訓練參數添加到圖中的首選方法是層。

層將變量和作用到它們的操作打包在一起。例如,密集連接層會對每個輸出對應的所有輸入執行加權和,並應用激活函數(可選)。連接權重偏差層對象管理。

創建層

下面的代碼會創建一個 Dense 層,該層會接收一批輸入矢量,並為每個矢量生成一個輸出值。要將層應用於輸入值,請將該層作為函數來調用。例如:

1 x = tf.placeholder(tf.float32, shape=[None, 3])
2 linear_model = tf.layers.Dense(units=1)
3 y = linear_model(x)

層會檢查其輸入數據,以確定其內部變量的大小。因此,我們必須在這里設置x占位符的形狀,以便層構建正確大小的權重矩陣。

我們現在定義了輸出值y的計算,在我們運行計算前,還需要處理一個細節。

初始化層

層包含的變量必須先初始化,然后才能使用。盡管可以單獨初始化各個變量,但也可以輕松的初始化一個TensorFlow圖中的所有變量(如下所示):

1 init = tf.global_variables_initializer()
2 sess.run(init)

重要提示:調用 tf.global_variables_initializer 僅會創建並返回 TensorFlow 操作的句柄。當我們使用 tf.Session.run 運行該操作時,該操作將初始化所有全局變量。

另請注意,此 global_variables_initializer 僅會初始化創建初始化程序時圖中就存在的變量。因此您應該在構建圖表的最后一步添加初始化程序。

 執行層

我們現在已經完成了層的初始化,可以像處理任何其它張量一樣評估 linear_model 的輸出張量了。

例如,下面的代碼:

1 print(sess.run(y, {x: [[1, 2, 3],[4, 5, 6]]}))

會生成一個兩元素輸出向量,如下所示:

1 [[ 4.5423756]
2  [11.656053 ]]

層函數的快捷方式

對於每個層類(如 tf.layers.Dense),TensorFlow提供了一個快捷函數(如 tf.layers.dense)。兩者唯一的區別是快捷函數版本是在單次調用中創建和使用層。例如,以下代碼等同於較早版本:

1 x = tf.placeholder(tf.float32, shape=[None, 3])
2 y = tf.layers.dense(x, units=1)
3 
4 init = tf.global_variables_initializer()
5 sess.run(init)
6 
7 print(sess.run(y, {x: [[1, 2, 3], [4, 5, 6]]}))

盡管這種方法很方便,但無法訪問 tf.layers.Layer 對象。這會讓自省和調試變得更加困難,並且無法重復使用相應的層。

 

特征列

使用特征列進行實驗的最簡單方法是使用 tf.feature_column.input_layer 函數。此函數只接受密集列作為輸入,因此要查看類別列的結果,您必須將其封裝在 tf.feature_column.indicator_column 中,例如:

 1 features = {
 2     'sales' : [[5], [10], [8], [9]],
 3     'department': ['sports', 'sports', 'gardening', 'gardening']}
 4 
 5 department_column = tf.feature_column.categorical_column_with_vocabulary_list(
 6         'department', ['sports', 'gardening'])
 7 department_column = tf.feature_column.indicator_column(department_column)
 8 
 9 columns = [
10     tf.feature_column.numeric_column('sales'),
11     department_column
12 ]
13 
14 inputs = tf.feature_column.input_layer(features, columns)

運行input張量會將feature解析為一批向量。

特征列和層一樣具有內部狀態,因此通常需要將它們初始化。類別列表會在內部使用對照表,而這些表需要單獨的初始化操作 tf.tables_initializer

1 var_init = tf.global_variables_initializer()
2 table_init = tf.tables_initializer()
3 sess = tf.Session()
4 sess.run((var_init, table_init))

初始化內部狀態后,您可以運行input(像運行其它tf.Tensor一樣):

1 print(sess.run(inputs))

這顯示了特征列如何打包輸入矢量,並將“department”作為第一和第二索引,將"sales"作為第三個索引。

1 [[  1.   0.   5.]
2  [  1.   0.  10.]
3  [  0.   1.   8.]
4  [  0.   1.   9.]]

 

訓練

您現在已經了解TensorFlow核心部分的基礎知識了,我們來手動訓練一個小型回歸模型吧。

定義數據

我們首先來定義一些輸入值x,以及每個輸入值得預期輸出值y_ture:

1 x = tf.constant([[1], [2], [3], [4]], dtype=tf.float32)
2 y_true = tf.constant([[0], [-1], [-2], [-3]], dtype=tf.float32)

定義模型

接下來,建立一個簡單得線性模型,其輸出值只有一個:

1 linear_model = tf.layers.Dense(units=1)
2 
3 y_pred = linear_model(x)

您可以如下評估預測值:

1 sess = tf.Session()
2 init = tf.global_variables_initializer()
3 sess.run(init)
4 
5 print(sess.run(y_pred))

該模型尚未訓練,因此四個“預測值”並不理想。以下是我們得到得結果,您自己的輸出應該有所不同:

1 [[-1.460115]
2  [-2.92023 ]
3  [-4.380345]
4  [-5.84046 ]]

損失

要優化模型,你首先需要定義損失。我們將使用均方誤差,這是回歸問題的標准損失。

雖然您可以使用較低級別的數學運算手動定義,但 tf.losses 模塊提供了一系列常用的損失函數。您可以用它來計算均方誤差,具體操作如下所示:

1 loss = tf.losses.mean_squared_error(labels=y_true, predictions=y_pred)
2 
3 print(sess.run(loss))

會得到如下所示的損失值(你的應該有所不同):

1 3.6203036

訓練

TensorFlow提供了執行標准優化算法的優化器。這些優化器被實現為 tf.train.Optimizer 的子類。它們會逐漸改變每個變量,以便將損失最小化。最簡單的優化算法是梯度下降法,由 tf.train.GrandientDescentOptimizer 實現。它會根據損失相對於變量的導數大小來修改各個變量。例如:

1 optimizer = tf.train.GradientDescentOptimizer(0.01)
2 train = optimizer.minimize(loss)

該代碼構建了優化所需的所有圖組件,並返回一個訓練指令。該訓練指令在運行時會更新圖中的變量。您可以按照以下方式運行該指令:

1 for i in range(100):
2   _, loss_value = sess.run((train, loss))
3   print(loss_value)

由於train是一個指令而不是張量,因此它在運行時不會返回一個值。為了查看訓練期間損失的進展,我們會同時運行損失張量,生成如下所示的輸出值:

 1 5.4017677
 2 3.8268266
 3 2.7335377
 4 1.9744602
 5 1.4472877
 6 1.081032
 7 0.8264358
 8 0.6493206
 9 0.5259704
10 0.4399293
11 0.37977904
12 ...

完整程序

 1 x = tf.constant([[1], [2], [3], [4]], dtype=tf.float32)
 2 y_true = tf.constant([[0], [-1], [-2], [-3]], dtype=tf.float32)
 3 
 4 linear_model = tf.layers.Dense(units=1)
 5 
 6 y_pred = linear_model(x)
 7 loss = tf.losses.mean_squared_error(labels=y_true, predictions=y_pred)
 8 
 9 optimizer = tf.train.GradientDescentOptimizer(0.01)
10 train = optimizer.minimize(loss)
11 
12 init = tf.global_variables_initializer()
13 
14 sess = tf.Session()
15 sess.run(init)
16 for i in range(100):
17   _, loss_value = sess.run((train, loss))
18   print(loss_value)
19 
20 print(sess.run(y_pred))

 

后續步驟

要詳細了解如何使用TensorFlow構建模型,請參閱以下內容:

  • 自定義Estimator,了解如何使用TensorFlow構建自定義模型。掌握TensorFlow  Core知識有助於理解和調試您的模型。

如果您像詳細了解TensorFlow的內部工作原理,請參閱以下文檔。這些文檔深入探討了這篇文章中提到的許多主題:

  • 圖與會話
  • 張量
  • 變量

 

 

參考鏈接:https://tensorflow.google.cn/guide/low_level_intro#training_2


免責聲明!

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



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