TENSORFLOW:底層概念介紹

介紹


這個幫助文檔讓你可以使用TensorFlow的底層API開始編程,讓你知道:
– 如何管理你自己的TensorFlow程序(用tf.Graph)和一個TensorFlow的runtime(用tf.Session)。這樣你就不需要依賴Estimators去幫你管理了。
– 利用tf.Session去運行一個TensorFlow的Operations。
– 在底層API裏使用高層的component,比如datasets,layers和feature_columns.
– 定義你自己的training loop,而不依賴於Estimators提供的。

我們推薦儘量使用高層的API去建模。但是知道TensorFlow的底層API也是有價值的。
– 用底層API實驗和調試讓你更加知道細節和明白。
– 它讓你在使用高層API建模時知道內部是怎麼運行的。

配置


首先你需要安裝一個TensorFlow。
要看明白這個文檔,你需要有以下知識:
– 如何用Python編程
– 知道一些線性代數
– 最好有一些機器學習背景
啓動你的python,運行下邊幾行來配置你的運行環境:

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import tensorflow as tf

Tensor Values


在TensorFlow裏最核心的數據單元是tensor。一個tensor是由任意維度的數組構成,裏邊的元素是基本數據類型。一個tensor的rank指的就是它的維度。它的shape是一個由整數構成的tuple,裏邊每個int表示tensor一個維度的長度。

3. # 一個 rank 0 的tensor; 一個標量的shape是[],
[1., 2., 3.] # 一個rank爲1的tensor; 這個向量的shape是[3]
[[1., 2., 3.], [4., 5., 6.]] # 一個rank爲2的tensor; 這個矩陣的shape是[2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # 一個rank爲3的 tensor,它的shape是[2, 1, 3]

TensorFlow使用numpy array來表示tensor的值。

TensorFlow 核心概念


你可以認爲TensorFlow的核心程序包含兩個獨立的塊:
– 構建一個計算流圖(一個tf.Graph)
– 運行一個計算流圖(用一個tf.Session)

Graph

一個計算流圖是一系列TensorFlow的operations被編排到一個graph裏。這個graph由兩種類型的Object構成。
– Operations(ops):是Graph的節點。Ops用tensor作爲輸入來計算,併產生新的tensor。
– Tensors:它構成graph的邊。tensors是在graph上流動的數據。大多數的TensorFlow function返回 tf.Tensors.

注意:tf.Tensors 並不擁有值,他們只是持有計算流圖中元素的句柄。

讓我們建一個簡單的計算流圖。最簡單的operation是一個constant。這個Python function建了一個operation,它用一個tensor value作爲input,最終產生結果的operation不需要input。運行時,它輸出傳入構造函數的值。我們可以創建兩個浮點數常數a和b:

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

print 語句生成的輸出:

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

注意print這些tensors並沒有像你可能期望的輸出3.0,4.0,和7.0。上邊的這些語句只是在構建計算流圖。tf.Tensor 只是表示這些opterations運行後會產生的result。

每一個Graph中的operation都有一個唯一的名字。它和你在Python中起的變量名是沒有關係的。Tensors是以產生他們的operation的名字後邊加上index構成的。比如”add:0″。

TensorBoard

TensorFlow提供了一個工具叫做TensorBoard. TensorBoard衆多功能之一是可視化一個計算流圖。你可以通過幾行代碼就可以容易的看到TensorBoard。

首先你需要把計算流圖存入一個TensorBoard summary文件:

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

上邊的代碼會生在當前文件夾成一個event文件。文件名是下邊的格式:

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

現在,在一個新的termianl裏,用下邊的命令:

tensorboard --logdir .

這時你打開下邊網址就可以看到計算流圖了:
http://localhost:6006/#graphs

關於TensorBoard的更多細節,可以看 TensorBoard:Graph Visualizaiton

Session

爲了給tensors求值,實例化一個tf.Session對象。通常叫做session。一個session包含了TensorFlow的runtime的狀態信息。並且運行tensorflow的operations。如果你把tf.Graph比作一個.py文件。那麼tf.Session就是python的可執行程序。
下邊的代碼創建一個tf.session的實例,用它的run方法來給total這個我們之前定義的tensor求值。

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

當你用Session.run去請求一個node的output,TensorFlow就會在圖裏向前一直求值,運行所有給output節點提供input的節點。所以我們得到輸出7.0:

7.0

你可以一次給session傳多個tensors去運行。run方法會自動處理任意類型的tuple或者dictionaries的組合。比如:

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

運行後可以得同樣的結構:

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

在對tf.Session.run的一次調用中,任何tf.Tensor只會有一個值。比如,下邊的代碼調用tf.random_uniform去產生一個tf.Tensor,它生成一個3個元素的隨機值構成的向量。每個值的區間在0和1之間。

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

結果顯示前兩次調用,每次調用生成一個新的隨機值。但是最後一個sess.run因爲是一次調用,所以vec的值不變。out1和out2應用同樣的vec。

[0.4134481  0.14323366 0.9671421 ]
[0.05151916 0.4826808  0.8643962 ]
(array([0.01238942, 0.10127807, 0.33896255], dtype=float32), array([1.0123894, 1.1012781, 1.3389626], dtype=float32), array([2.0123894, 2.101278 , 2.3389626], dtype=float32))

一些TensorFlow方法返回tf.Operations而不是tf.Tensors。在Operation上調用run方法返回的結果是None。你運行一個operation,是爲了使用它的副作用,而不是爲了它的返回值。後邊我們演示的初始化和training的ops都是這樣。

Feeding

如果一個計算流圖裏只有常數,那也沒啥意思。一個graph可以通過參數來接收外部的input。這被叫做 placeholders, 一個placeholder是一個保證稍後運行時會提供一個實際值。就像函數的參數一樣。

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

上邊這三行代碼就像一個函數定義了2個float32的參數x,y。然後執行對x和y的加法。我們可以對這個graph求值,前提是定義x,y的值通過tf.Session.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可以用來覆蓋任何圖上的tensor。即使之前定義的constant的tensor,你也可以在後用的feed_dict裏覆蓋它的值。palceholder和tensor唯一的不同就是如果沒有給placeholder賦值,會出異常。

Datasets


Placeholders是用來單次執行的。而Dataset是用來流式讀入數據到模型的。
爲了從Dataset得到一個可運行的tf.Tensor,你首先必須把它轉成一個tf.data.Iterator。然後調用iterator的get_next方法。

最簡單的創建一個Iterator是用make_one_shot_iterator 方法。比如,下邊的代碼,next_item 這個tensor每次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

如需瞭解更多細節關於dataset和Iterators,可以看:Importing Data。

Layers


一個可以訓練的模型必須修改graph裏的值,這樣圖纔可以不斷改進,同樣的輸入可以輸出不同的output。Layers是推薦的給graph裏添加可以訓練的parameter的方法。
Layers 把變量和變量上操作封裝到一起。比如一個 densely-connected layer就是對所有的input執行一個帶權重的求和,然後應用一個可選的activation function。得到輸出。這個鏈接裏的weight值和bias的值是由layer這個對象管理的。

創建一個Layer

下邊的代碼創建一個Dense層,它的輸入是一組input向量,輸出是對每一個向量生成一個單一的值。給layer傳入一個input,那麼就像調用一個函數一樣的調用layer,比如:

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

Layer需要知道向量的大小來初始化內部的變量,所以這裏我們必須傳入x這個placeholder的shape來讓Layer內部可以創建一個有着正確大小的weight矩陣。

現在我們已經定義了計算output的計算,也就是y,在執行前,我們還有一件事需要注意。

初始化Layers

Layers裏的變量是必須先初始化才能使用的。當然你可以一個個初始化,TensorFlow提供了一種簡單的方法來統一初始化graph裏所有的變量:

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

注意:調用tf.global_variables_initializer只創建和返回了一個對TensorFlow operation的句柄。只有用tf.Session.run去調用這個operation,它纔會對所有全局的變量進行初始化。

同樣,global_variables_initializer只會初始化比它先存在graph裏的那些變量,所以它應該最後創建。

執行Layers

現在Layer已經已經被初始化了,我們可以對linear_model的output進行求值,就像對其他tensor一樣。

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

會生成一個2個元素的輸出向量:

[[-3.41378999]
 [-9.14999008]]

Layer Function的快捷方式

對於每一個layer class(比如 tf.layers.Dense) TensorFlow 也提供一個快捷方法(比如tf.layers.dense)。他們的區別是快捷方法的創建和調用時一起的。比如,之前例子的等價方式:

x = tf.placehodler(tf.float32,shape=[None,3])
y = tf.layers.dense(x,units=1)

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

這樣調用比較省事,x,y都是tensor,沒有了對tf.laysers.Layer的對象。但是它因爲綁定了變量x,就不能重用了。調試也麻煩了。

Feature Columns


最簡單的體驗feature columns的方法是使用tf.feature_column.input_layer方法。這個方法只接受dense columns作爲輸入。所以爲了看一個離散類型的列,你必須把它封裝到一個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 tensor 會自動從features解析出一組vectors。
Feature columns可以有內部狀態,比如layers。所以他們經常需要被初始化。離散型的column需要用lookup tables,它需要一個獨立的初始化操作,tf.tables_initializer

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

一旦你初始化完成,你就可以像運行其他tensor一樣的運行inputs

print(sess.run(inputs))

輸出顯示了feature columns是如何對input向量用one-hot encoding對department列編碼的,前兩列是對department進行編碼,第三列是sales。

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

Training


現在你已經對TensorFlow一些基本的概念有所瞭解,我們開始自己動手訓練一個小的regession模型吧。

定義數據

首先讓我們定義一些inputs,x,和每個input的期望輸出,y_true:

x = tf.contant([[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)

我們可以這樣求得預測值:

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

因爲模型還沒有被訓練,所以預測的值並不好,它和你期望的output肯定有差距:

[[ 0.02631879]
 [ 0.05263758]
 [ 0.07895637]
 [ 0.10527515]]

Loss

爲了優化一個模型,你首先需要定義它的損失loss。我們使用均方差,它是迴歸問題的一個標準的loss。

你可以自己寫均方差的數學公式來計算,同時tf.losses模塊提供了一組常用的loss function。你可以像下面這樣調用它來求得均方差:

loss = tf.losses.mean_squard_error(lables=y_true,predictions = y_pred)
print(sess.run(loss))

通過上邊的調用,你就可以產生一個loss值。比如下邊這樣:

2.23962

Training

TensorFlow 提供了optimizers,它實現了標準的優化算法。他們都是tf.train.Optimizer的子類。他們漸進的修改每個參數來達到最小化loss的目的。最簡單的優化算法時梯度下降(gradient descent),由tf.train.GradientDescentOptimizer實現。它是根據loss對每個變量導數的大小來改變參數值的。比如:

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

這一段代碼會構建圖裏和這個優化相關的所有的component。並返回一個traning的operation。運行這個operation的時候。它會更新圖裏的參數。你可以這樣調用它:

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

因爲 train 是一個operation,不是一個tensor。所以它運行時沒有返回值。爲了看到loss function值的變化,每次迭代,我們也運行一個loss tensor。這樣我們就看到如下的輸出:

1.35659
1.00412
0.759167
0.588829
0.470264
0.387626
0.329918
0.289511
0.261112
0.241046
...

完整的程序

x = tf.contant([[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.GradentDescentOptimizer(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))

------------------------------------------------------------------------------------

原文:http://www.rethink.fun/index.php/2018/03/09/tensorflow2/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章