1. 程式人生 > >Tensorflow之底層操作

Tensorflow之底層操作

1、張量值

TF的核心資料單位是張量,張量由一組形成陣列的原始資料組成,張量的階是它的維數,而它的維數是一哥整數元組,指定了陣列的每個維度的長度:

2、TF的低階API可以由兩部分獨立組成

    (1) 構建計算圖(tf.Graph)

    (2) 運算計算圖(tf.Session)

    (1) Graph

        計算圖是排列成一個圖的一系列TF指令,圖由兩種型別的物件組成

    •      指令:圖的節點。指令說明的是消耗和生成張量的計算。
    •      張量:圖的邊。它們代表將流經圖的值。大多數會返回tf.Tensor

最基本的指令是一個常量。構建指令的Python函式將一個張量值作為輸入值。生成的指令不需要輸入值。它在執行時輸出的是被傳遞給建構函式的值。我們建立兩個浮點數常量a和b:

a = tf.constant(3.0,dtype = tf.float32)
b = tf.constant(4.0)
total = a+b
print (a)
print (b)
print (total)

列印語句會生成:

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

圖中每個張量都擁有唯一的名稱。這個名稱不同於使用的Python分配給對應的物件的名稱。張量是根據生成它們的指令命名的,後面跟著輸出索引。

Tensorboard

Tensorboard能夠使計算圖視覺化。其過程如下:

(1)將計算圖儲存為Tnesorboard摘要檔案,具體操作如下:

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

這將在當前目錄中生成一個event檔案,其名稱格式如下

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

在終端使用shell命令,啟動TensorBoard

tensorboard --logdir

接下來在瀏覽器開啟TnesorBoard的圖頁面,你應該看到計算圖如下所示:


會話

要評估張量,需要例項化一個tf.Session物件(會話)。會話會封裝TensorFlow執行時的狀態,並執行TF指令。如果tf.Graph像一個.py檔案,那麼tf.Session就像一個可執行的python

以下建立一個tf.Session物件,然後呼叫其run方法來評估我們在上文中建立的total張量:

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

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

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

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

它返回的結果有相同的佈局:

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

供給

上面的圖只能產生一個常量的結果,圖可以引數化以便接收外部輸入,也稱為佔位符。佔位符表示承諾在稍後提供值,它就行函式引數:

x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = x + y

前三行有點像函式。我們定義了這個函式的兩個輸入引數(x和y),然後對它們執行指令。我們可以使用run方法的feed_dict引數來為佔位符提供真正的值,從而通過多個輸入值來評估這個圖:

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

輸出結果為:

7.5
[3.,7.]

注意,feed_dict引數可以用於覆蓋圖中的任何張量。佔位符和其他tf.Tensors的唯一不同之處在於如果沒有提供值給他們,那麼佔位符會顯示錯誤。

資料集

佔位符適用於簡單實驗,但資料集是將資料流形式傳遞到模型的首選方法。

要從資料集中獲取可執行的tf.Tensor,必須現將其轉換成tf.data.Iterater,然後呼叫迭代器的get_next方法。

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

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

到達資料流末端是,Dataset會丟擲OutOfRangeError。例如,下面代買會一直讀取next_item,直到沒有資料可讀:

while True:
  try:
    print(sess.run(next_item))
  except tf.errors.OutOfRangeError:
   break

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

層將變數和作用於他們的指令打包在一起。例如,密集連線層會對每個輸出值對應的所有輸入值執行加權和,並應用可選擇的啟用函式。連線權重和偏差由層物件管理。

建立層

下面會建立一個Dense層,該層會接收一批輸入向量,併為每個向量生成單一的輸出值。要將層應用於輸入值,請將該層當做函式來呼叫。例如

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

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

在定義完圖後,還有一個細節需要處理(初始化)

初始化層

層包含的變數必須先初始化,然後才能使用。如下所示:

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

此global_variables_initializer僅會初始化建立初始化程式是圖中就存在的變數。因此應該在構建圖示的最後一步新增初始化程式。

執行層

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

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

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

[[-3.413478999]
 [-9.149999908]]

層函式的快捷方式

對於每個層類別(如tf.layers.Dense),TF還提供了一個快捷函式(如tf.layers.dense)。兩者唯一的區別是快捷函式版本是在單次呼叫中建立和執行層。例如:

x = tf.placeholder(tf.float32,shape=[None,3])
y = tf.layers.dense(x,units=1)
init = tf.global_variables_initializer()
sess.run()


print (sess.run(y,{x:[[1,2,3],[4,5,6]]}))
儘管這種方法很方便,但無法訪問 tf.layers.Layer 物件。這會讓自省和除錯變得更加困難,並且無法重複使用相應的層。

特徵列

使用特徵列進行試驗的最簡單方式是使用tf.feature_column.input_layer函式。此函式只接受密集列作為輸入資料,因此要檢視分類列的結果,你必須將其包含在tf.feature_column.indicator_column。例如:

features = {
 'sales': [[5],[10],[8],[9]]
  'department':['sports','sports','gardening','gardening]}
department_column = tf.feature_column.categorical_column_with_vocabulary_list('department',['sports','gardening'])
department_column = tf.feature_column.indicator_column(department_column)
columns = [tf.feature_column.numeric_column('sales'),department_column]
inputs = tf.feature_column.input_layer(features,columns)

執行inputs張量會將features解析為一批向量

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

var_init = tf.global_variables_initializer()
table_init = tf.tables_initalizer()
sess = tf.Session()
sess.run(var_init,table_init)

內部狀態初始化完成後,可以像執行任何其他tf.Tensor一樣執行inputs:

print (sess.run(inputs))

這顯示了特徵列如何打包輸入向量,在這個過程中,獨熱“部門”被用作第一和第二索引,“銷售”用作第三個。

[[  1.   0.   5.]
 [  1.   0.  10.]
 [  0.   1.   8.]
 [  0.   1.   9.]]

訓練

1、定義資料

首先定義一些輸入值x,以及每個輸入值的預期輸出值y_true

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

2、定義模型

接下來,定義一個簡單的線性模型,其輸出值只1個

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

可以如下評估預測值:

sess=  tf.Session()
init = tf.global_variables_initializers()
sess.run()
print (sess.run(y_pred))

模型尚未接受訓練,預測值並不理想

3、損失

要優化模型,首先要定義損失,我們將適應均方誤差,這是迴歸問題的標準損失

tf.losses提供了一系列常用損失函式,可以使用它來計算均方誤差

loss = tf.losses.mean_squared_error(labels=y_true,predictions =y_pred)

print (sess.run(loss))

將會生成一個損失

4、訓練

TF也提供了優化器啦執行標準的優化演算法,這些優化器被用作tf.train.Optimizer的子類別。他們會逐漸改變每個變數,使損失最小化。最簡單的優化演算法是梯度下降法。可以通過tf.train.GradientDescentOptimizer執行。他會根據損失相對於變數的導數大小來修改各個變數。

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

該程式碼構建了優化所需要的所有圖元件,並返回一個訓練指令。該指令在執行時會更新圖中的變數。可如下執行該指令:

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

由於train是一個指令而不是張量,因此在她執行時不會返回一個值。為了檢視訓練期間損失的進展,我們會同時執行損失張量,生如下損失值:

1.356591.004120.7591670.5888290.4702640.3876260.3299180.2895110.2611120.241046

完整程式

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

linear_model
= tf.layers.Dense(units=1)

y_pred
= linear_model(x)
loss
= tf.losses.mean_squared_error(labels=y_true, predictions=y_pred)

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

init
= tf.global_variables_initializer()

sess
= tf.Session()
sess
.run(init)for i in range(100):
  _
, loss_value = sess.run((train, loss))print(loss_value)print(sess.run(y_pred))