TensorFlow入門之一:第一個機器學習Demo
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/geyunfei_/article/details/78782804
本文主要通過一個簡單的 Demo 介紹 TensorFlow 初級 API 的使用方法,因為自己也是初學者,因此本文的目的主要是引導剛接觸 TensorFlow 或者 機器學習的同學,能夠從第一步開始學習 TensorFlow。閱讀本文先確認具備以下基礎技能:
- 會使用 Python 程式設計(初級就OK,其實 TensorFlow 也支援 Java、C++、Go)
- 一些陣列相關的知識(線性代數沒忘乾淨就行)
- 最好再懂點機器學習相關的知識(臨時百度、Google也來得及)
基礎知識
張量(Tensor)
TensorFlow 內部的計算都是基於張量的,因此我們有必要先對張量有個認識。張量是在我們熟悉的標量、向量之上定義的,詳細的定義比較複雜,我們可以先簡單的將它理解為一個多維陣列:
3 # 這個 0 階張量就是標量,shape=[]
[1., 2., 3.] # 這個 1 階張量就是向量,shape=[3]
[[1., 2., 3.], [4., 5., 6.]] # 這個 2 階張量就是二維陣列,shape=[2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # 這個 3 階張量就是三維陣列,shape=[2, 1, 3]
TensorFlow 內部使用tf.Tensor
類的例項來表示張量,每個 tf.Tensor
有兩個屬性:
- dtype Tensor 儲存的資料的型別,可以為
tf.float32
、tf.int32
、tf.string
… - shape Tensor 儲存的多維陣列中每個維度的陣列中元素的個數,如上面例子中的shape
我們現在可以敲幾行程式碼看一下 Tensor 。在命令終端輸入 python 或者 python3 啟動一個 Python 會話,然後輸入下面的程式碼:
# 引入 tensorflow 模組
import tensorflow as tf
# 建立一個整型常量,即 0 階 Tensor
t0 = tf.constant(3, dtype=tf.int32)
# 建立一個浮點數的一維陣列,即 1 階 Tensor
t1 = tf.constant([3., 4.1, 5.2], dtype=tf.float32)
# 建立一個字串的2x2陣列,即 2 階 Tensor
t2 = tf.constant([['Apple', 'Orange'], ['Potato', 'Tomato']], dtype=tf.string)
# 建立一個 2x3x1 陣列,即 3 階張量,資料型別預設為整型
t3 = tf.constant([[[5], [6], [7]], [[4], [3], [2]]])
# 列印上面建立的幾個 Tensor
print(t0)
print(t1)
print(t2)
print(t3)
上面程式碼的輸出為,注意shape
的型別:
>>> print(t0)
Tensor("Const:0", shape=(), dtype=int32)
>>> print(t1)
Tensor("Const_1:0", shape=(3,), dtype=float32)
>>> print(t2)
Tensor("Const_2:0", shape=(2, 2), dtype=string)
>>> print(t3)
Tensor("Const_3:0", shape=(2, 3, 1), dtype=int32)
print 一個 Tensor 只能打印出它的屬性定義,並不能打印出它的值,要想檢視一個 Tensor 中的值還需要經過Session 執行一下:
>>> sess = tf.Session()
>>> print(sess.run(t0))
3
>>> print(sess.run(t1))
[ 3. 4.0999999 5.19999981]
>>> print(sess.run(t2))
[[b'Apple' b'Orange']
[b'Potato' b'Tomato']]
>>> print(sess.run(t3))
[[[5]
[6]
[7]]
[[4]
[3]
[2]]]
>>>
資料流圖(Dataflow Graph)
資料流是一種常用的平行計算程式設計模型,資料流圖是由**節點(nodes)和線(edges)**構成的有向圖:
- 節點(nodes) 表示計算單元,也可以是輸入的起點或者輸出的終點
- 線(edges) 表示節點之間的輸入/輸出關係
在 TensorFlow 中,每個節點都是用 tf.Tensor
的例項來表示的,即每個節點的輸入、輸出都是Tensor,如下圖中 Tensor 在 Graph 中的流動,形象的展示 TensorFlow 名字的由來
TensorFlow 中的資料流圖有以下幾個優點:
- 可並行 計算節點之間有明確的線進行連線,系統可以很容易的判斷出哪些計算操作可以並行執行
- 可分發 圖中的各個節點可以分佈在不同的計算單元(CPU、 GPU、 TPU等)或者不同的機器中,每個節點產生的資料可以通過明確的線傳送的下一個節點中
- 可優化 TensorFlow 中的 XLA 編譯器可以根據資料流圖進行程式碼優化,加快執行速度
- 可移植 資料流圖的資訊可以不依賴程式碼進行儲存,如使用Python建立的圖,經過儲存後可以在C++或Java中使用
Sesssion
我們在Python中需要做一些計算操作時一般會使用NumPy,NumPy在做矩陣操作等複雜的計算的時候會使用其他語言(C/C++)來實現這些計算邏輯,來保證計算的高效性。但是頻繁的在多個程式語言間切換也會有一定的耗時,如果只是單機操作這些耗時可能會忽略不計,但是如果在分散式平行計算中,計算操作可能分佈在不同的CPU、GPU甚至不同的機器中,這些耗時可能會比較嚴重。
TensorFlow 底層是使用C++實現,這樣可以保證計算效率,並使用 tf.Session
類來連線客戶端程式與C++執行時。上層的Python、Java等程式碼用來設計、定義模型,構建的Graph,最後通過tf.Session.run()
方法傳遞給底層執行。
構建計算圖
上面介紹的是 TensorFlow 和 Graph 的概念,下面介紹怎麼用 Tensor 構建 Graph。
Tensor 即可以表示輸入、輸出的端點,還可以表示計算單元,如下的程式碼建立了對兩個 Tensor 執行 + 操作的 Tensor:
import tensorflow as tf
# 建立兩個常量節點
node1 = tf.constant(3.2)
node2 = tf.constant(4.8)
# 建立一個 adder 節點,對上面兩個節點執行 + 操作
adder = node1 + node2
# 列印一下 adder 節點
print(adder)
# 列印 adder 執行後的結果
sess = tf.Session()
print(sess.run(adder))
上面print
的輸出為:
Tensor("add:0", shape=(), dtype=float32)
8.0
上面使用tf.constant()
建立的 Tensor 都是常量,一旦建立後其中的值就不能改變了。有時我們還會需要從外部輸入資料,這時可以用tf.placeholder
建立佔位 Tensor,佔位 Tensor 的值可以在執行的時候輸入。如下就是建立佔位 Tensor 的例子:
import tensorflow as tf
# 建立兩個佔位 Tensor 節點
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
# 建立一個 adder 節點,對上面兩個節點執行 + 操作
adder_node = a + b
# 列印三個節點
print(a)
print(b)
print(adder)
# 執行一下,後面的 dict 引數是為佔位 Tensor 提供輸入資料
sess = tf.Session()
print(sess.run(adder, {a: 3, b: 4.5}))
print(sess.run(adder, {a: [1, 3], b: [2, 4]}))
上面程式碼的輸出為:
Tensor("Placeholder:0", dtype=float32)
Tensor("Placeholder_1:0", dtype=float32)
Tensor("add:0", dtype=float32)
7.5
[ 3. 7.]
我們還可以新增其他操作構建複雜的 Graph
# 新增×操作
add_and_triple = adder * 3.
print(sess.run(add_and_triple, {a: 3, b: 4.5}))
上面的輸出為
22.5
TensorFlow 應用例項
上面介紹了 TensorFlow 中的一些基本概念,下面我們通過一個小例子來了解一下怎麼使用 TensorFlow 進行機器學習。
建立模型(Model)
如下為我們進行某項實驗獲得的一些實驗資料:
輸入 | 輸出 |
---|---|
1 | 4.8 |
2 | 8.5 |
3 | 10.4 |
6 | 21 |
8 | 25.3 |
我們將這些資料放到一個二維圖上可以看的更直觀一些,如下,這些資料在圖中表現為一些離散的點:
我們需要根據現有的這些資料歸納出一個通用模型,通過這個模型我們可以預測其他的輸入值產生的輸出值。
如下圖,我們選擇的模型既可以是紅線表示的鬼都看不懂的曲線模型,也可以是藍線表示的線性模型,在概率統計理論的分析中,這兩種模型符合真實模型的概率是一樣的。
根據 “奧卡姆剃刀原則-若有多個假設與觀察一致,則選最簡單的那個,藍線表示的線性模型更符合我們的直觀預期。
如果用 xxx 表示輸入, yyy 表示輸出,線性模型可以用下面的方程表示:
即使我們選擇了直線模型,可以選擇的模型也會有很多,如下圖的三條直線都像是一種比較合理的模型,只是WWW和bbb引數不同。這時我們需要設計一個損失模型(loss model),來評估一下哪個模型更合理一些,並找到一個最準確的模型。
如下圖每條黃線代表線性模型計算出來的值與實際輸出值之間的差值:
我們用y′y′y’表示實驗得到的實際輸出,用下面的方程表示我們的損失模型:
顯然,損失模型裡得到的losslossloss越小,說明我們的線性模型越準確。
使用 TensorFlow 實現模型
上面我們根據實驗資料建立了一個線性模型,併為這個線性模型設計了一個損失模型,下面介紹的是怎麼在 TensorFlow 中實現我們設計的模型。
在我們的線性模型
中,輸入
可以用佔位 Tensor 表示,輸出
可以用線性模型的輸出表示,我們需要不斷的改變
和
的值,來找到一個使
最小的值。這裡
和
可以用變數 Tensor 表示。使用tf.Variable()
可以建立一個變數 Tensor,如下就是我們模型的實現程式碼:
import tensorflow as tf
# 建立變數 W 和 b 節點,並設定初始值
W = tf.Variable([.1], dtype=tf.float32)
b = tf.Variable([-.1], dtype=tf.float32)
# 建立 x 節點,用來輸入實驗中的輸入資料
x = tf.placeholder(tf.float32)
# 建立線性模型
linear_model = W*x + b
# 建立 y 節點,用來輸入實驗中得到的輸出資料,用於損失模型計算
y = tf.placeholder(tf.float32)
# 建立損失模型
loss = tf.reduce_sum(tf.square(linear_model - y))
# 建立 Session 用來計算模型
sess = tf.Session()
通過tf.Variable()
建立變數 Tensor 時需要設定一個初始值,但這個初始值並不能立即使用,例如上面的程式碼中,我們使用print(sess.run(W))
嘗試列印W
的值會得到下面提示未初始化的異常
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value Variable
變數 Tensor 需要經過下面的 init
過程後才能使用:
# 初始化變數
init = tf.global_variables_initializer()
sess.run(init)
這之後再使用print(sess.run(W))
列印就可以看到我們之前賦的初始值:
[ 0.1]
變數初始化完之後,我們可以先用上面對W
和b
設定的初始值0.1
和-0.1
執行一下我們的線性模型看看結果:
print(sess.run(linear_model, {x: [1, 2, 3, 6, 8]}))
輸出結果為:
[ 0. 0.1 0.20000002 0.5 0.69999999]
貌似與我們實驗的實際輸出差距很大,我們再執行一下損失模型:
print(sess.run(loss, {x: [1, 2, 3, 6, 8], y: [4.8, 8.5, 10.4, 21.0, 25.3]}))
得到的損失值也很大
1223.05
我們可以用tf.assign()
對W
和b
變數重新賦值再檢驗一下:
# 給 W 和 b 賦新值
fixW = tf.assign(W, [2.])
fixb = tf.assign(b, [1.])
# run 之後新值才會生效
sess.run([fixW, fixb])
# 重新驗證損失值
print(sess.run(loss, {x: [1, 2, 3, 6, 8], y: [4.8, 8.5, 10.4, 21.0, 25.3]}))
輸出的損失值比之前的小了很多:
159.94
我們需要不斷調整變數W
和b
的值,找到使損失值最小的W
和b
。這肯定是一個very boring的過程,因此 TensorFlow 提供了訓練模型的方法,自動幫我們進行這些繁瑣的訓練工作。
使用 TensorFlow 訓練模型
TensorFlow 提供了很多優化演算法來幫助我們訓練模型。最簡單的優化演算法是**梯度下降(Gradient Descent)**演算法,它通過不斷的改變模型中變數的值,來找到最小損失值。
如下的程式碼就是使用梯度下降優化演算法幫助我們訓練模型:
# 建立一個梯度下降優化器,學習率為0.001
optimizer = tf.train.GradientDescentOptimizer(0.001)
train = optimizer.minimize(loss)
# 用兩個陣列儲存訓練資料
x_train = [1, 2, 3, 6, 8]
y_train = [4.8, 8.5, 10.4, 21.0, 25.3]
# 訓練10000次
for i in range(10000):
sess.run(train, {x: x_train, y: y_train})
# 列印一下訓練後的結果
print('W: %s b: %s loss: %s' % (sess.run(W), sess.run(b), sess.run(loss, {x: x_train , y: y_train})))
打印出來的訓練結果如下,可以看到損失值已經很小了:
W: [ 2.98236108] b: [ 2.07054377] loss: 2.12941
我們整理一下前面的程式碼,完整的demo程式碼如下,將下面的程式碼儲存在一個demo.py檔案裡,通過python3 demo.py
執行一下就可以看到訓練結果了:
import tensorflow as tf
# 建立變數 W 和 b 節點,並設定初始值
W = tf.Variable([.1], dtype=tf.float32)
b = tf.Variable([-.1], dtype=tf.float32)
# 建立 x 節點,用來輸入實驗中的輸入資料
x = tf.placeholder(tf.float32)
# 建立線性模型
linear_model = W * x + b
# 建立 y 節點,用來輸入實驗中得到的輸出資料,用於損失模型計算
y = tf.placeholder(tf.float32)
# 建立損失模型
loss = tf.reduce_sum(tf.square(linear_model - y))
# 建立 Session 用來計算模型
sess = tf.Session()
# 初始化變數
init = tf.global_variables_initializer()
sess.run(init)
# 建立一個梯度下降優化器,學習率為0.001
optimizer = tf.train.GradientDescentOptimizer(0.001)
train = optimizer.minimize(loss)
# 用兩個陣列儲存訓練資料
x_train = [1, 2, 3, 6, 8]
y_train = [4.8, 8.5, 10.4, 21.0, 25.3]
# 訓練10000次
for i in range(10000):
sess.run(train, {x: x_train, y: y_train})
# 列印一下訓練後的結果
print('W: %s b: %s loss: %s' % (sess.run(W), sess.run(
b), sess.run(loss, {x: x_train, y: y_train})))
TensorFlow 高階訓練模型
tf.estimator
是TensorFlow提供的高階庫,提供了很多常用的訓練模型,可以簡化機器學習中的很多訓練過程,如:
- 執行訓練迴圈
- 執行評估迴圈
- 管理訓練資料集
評估模型
前面的demo中我們構建了一個線性模型,通過使用一組實驗資料訓練我們的線性模型,我們得到了一個自認為損失最小的最優模型,根據訓練結果我們的最優模型可以表示為下面的方程: