這篇文章是TensorFlow的入門教程燎悍。在開(kāi)始閱讀本文之前父能,請(qǐng)確保你會(huì)Python宽菜,并且對(duì)矩陣有一定的了解谣膳,除此之外,最好能懂一點(diǎn)機(jī)器學(xué)習(xí)的知識(shí)铅乡,不過(guò)如果你對(duì)機(jī)器學(xué)習(xí)一無(wú)所知也沒(méi)關(guān)系继谚,你可以從閱讀這篇文章開(kāi)始學(xué)起。
TensorFlow提供了豐富的接口供調(diào)用阵幸。TensorFlow的內(nèi)核盡可能開(kāi)放了最完備的接口犬庇,它允許你在此基礎(chǔ)上從最底層開(kāi)始開(kāi)發(fā)。我們建議一般開(kāi)發(fā)者可以不用從這么底層開(kāi)始開(kāi)發(fā)侨嘀,這些底層接口更適合科研人員迁客。TensorFlow的上層接口都是在此基礎(chǔ)上搭建的惭墓。上層接口比底層更容易使用。像tf.contrib.learn這樣的高層接口幫助你去管理數(shù)據(jù)集梨树、估算葬荷、訓(xùn)練和推理涨共。請(qǐng)注意,有一些高層的接口的方法名包含contrib宠漩,這些是正在開(kāi)發(fā)的接口举反,它們?cè)诮酉聛?lái)的版本當(dāng)中,有可能會(huì)被修改甚至刪掉扒吁。
這篇文章會(huì)先講一下TensorFloat的基礎(chǔ)知識(shí)火鼻,然后,我們會(huì)帶著大家一起學(xué)習(xí)一下如何用tf.contrib.learn實(shí)現(xiàn)之前提到的模型雕崩。
Tensors
TensorFlow的核心就是tensor魁索,tensor是由一系列任意維度的矩陣構(gòu)成。
3 # 這是一個(gè)維度為0的tensor盼铁,這是一個(gè)標(biāo)量粗蔚,它的大小是[]。
[1. ,2., 3.] # 這是一個(gè)維度為1的tensor饶火,這是一個(gè)矢量鹏控,它的大小是[3]
[[1., 2., 3.], [4., 5., 6.]] # 這是一個(gè)維度為2的tensor致扯。這是一個(gè)矩陣,它的大小是[2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # 這是一個(gè)維度為3的tensor当辐,它的大小是[2, 1, 3]
TensorFlow的內(nèi)核
導(dǎo)入TensorFlow
TensorFlow的導(dǎo)入方式如下:
import tensorflow as tf
導(dǎo)入了TensorFlow了之后抖僵,就可以通過(guò)Python來(lái)訪問(wèn)TensorFlow的里面的類、方法和符號(hào)瀑构。后續(xù)所有的代碼執(zhí)行前都必須先導(dǎo)入TensorFlow裆针。
算法圖
TensorFlow的內(nèi)核分成兩部分:構(gòu)建算法圖和運(yùn)行算法圖。
算法圖是由一系列算法作為節(jié)點(diǎn)形成的一幅圖寺晌。讓我們一起構(gòu)建一個(gè)簡(jiǎn)單的算法圖世吨。每一個(gè)節(jié)點(diǎn)都有任意數(shù)量的tensor作為輸入,一個(gè)tensor作為輸出呻征。常量是一個(gè)特殊的節(jié)點(diǎn)耘婚。所有的常量都沒(méi)有輸入,它的輸出來(lái)自內(nèi)部存儲(chǔ)的數(shù)據(jù)陆赋。如果想要?jiǎng)?chuàng)建兩個(gè)浮點(diǎn)數(shù)類型的tensor沐祷,比如node1和node2,我們可以這樣寫:
node1 = tf.constant(3.0, dtype=tf.float32)
node2 = tf.constant(4.0) # 隱式指定了tensor內(nèi)部數(shù)據(jù)的類型是tf.float32
print(node1, node2)
最后一行打印出來(lái)的結(jié)果是:
Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)
請(qǐng)注意攒岛,這里并沒(méi)有像我們期望的那樣打印出3.0和4.0赖临。這是因?yàn)閚ode1和node2是節(jié)點(diǎn),只有運(yùn)算的時(shí)候灾锯,才會(huì)分別生成3.0和4.0兢榨。想要對(duì)這兩個(gè)節(jié)點(diǎn)進(jìn)行運(yùn)算,我們必須啟動(dòng)一個(gè)會(huì)話(Session)顺饮。
下面的代碼創(chuàng)建 了一個(gè)會(huì)話吵聪,并且調(diào)用了它的run方法來(lái)執(zhí)行這個(gè)算法圖。求出了node1和node2的值兼雄。
sess = tf.Session()
print(sess.run([node1, node2]))
這樣我們就可以看到期望的結(jié)果:
[3.0, 4.0]
我們還可以構(gòu)建更加復(fù)雜的圖吟逝。比如,我們可以把剛才的兩個(gè)節(jié)點(diǎn)加起來(lái)生成一個(gè)新的節(jié)點(diǎn):
node3 = tf.add(node1, node2)
print("node3: ", node3)
print("sess.run(node3): ",sess.run(node3))
最后兩行打印的代碼打印出來(lái)的結(jié)果如下:
node3: Tensor("Add:0", shape=(), dtype=float32)
sess.run(node3): 7.0
TensorFlow提供了一個(gè)工具赦肋,叫TensorBoard块攒,可以用來(lái)查看算法圖的結(jié)構(gòu)。這就是剛才的代碼對(duì)應(yīng)的算法圖佃乘。
這張圖非常簡(jiǎn)單局蚀,因?yàn)槲覀兊乃惴óa(chǎn)生的永遠(yuǎn)是一個(gè)定值。一張圖如果想要有外部輸入恕稠,我們就需要用到占位符(placeholder)琅绅。一個(gè)占位符表示一定會(huì)提供一個(gè)輸入。
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b # 這里面的加號(hào)和調(diào)用tf.add(a, b)等效
上面的三行像是一個(gè)函數(shù)鹅巍。定義了兩個(gè)輸入?yún)?shù)a和b千扶。然后把它們加了起來(lái)料祠。我們要執(zhí)行這個(gè)算法圖,就必須要傳參數(shù):
print(sess.run(adder_node, {a: 3, b:4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))
運(yùn)行的結(jié)果是:
7.5
[ 3. 7.]
這張算法圖是這樣的:
我們還可以把圖變得更復(fù)雜澎羞,比如我們可以再添加一個(gè)節(jié)點(diǎn):
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))
執(zhí)行的結(jié)果是:
22.5
對(duì)應(yīng)的圖是這樣的:
在機(jī)器學(xué)習(xí)中髓绽,我們希望一個(gè)模型可以接受任何參數(shù)。為了讓模型可以被訓(xùn)練妆绞,我們希望可以通過(guò)修改圖顺呕,使得同樣的輸入會(huì)得到新的輸出。變量(Variables)允許我們把一個(gè)可以訓(xùn)練的參數(shù)加入到圖中括饶。創(chuàng)建變量的時(shí)候株茶,需要指定它們的類型和初始值。
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
當(dāng)你調(diào)用tf.constant的時(shí)候图焰,常量就被初始化了启盛,它的值永遠(yuǎn)不會(huì)改變。但是當(dāng)你調(diào)用tf.Variable的時(shí)候技羔,變量并沒(méi)有被初始化僵闯,要初始化變量,你必須顯式地執(zhí)行如下操作:
init = tf.global_variables_initializer()
sess.run(init)
有一點(diǎn)很重要藤滥,init是一個(gè)引用鳖粟。它引用的是一個(gè)子圖。這個(gè)子圖初始化了所有全局變量拙绊。一直到執(zhí)行sess.run的時(shí)候向图,這些變量才真正被初始化。
因?yàn)閤是一個(gè)占位符时呀,所以我們可以一次性計(jì)算醋linear_model的四個(gè)值。
print(sess.run(linear_model, {x:[1,2,3,4]}))
運(yùn)行的結(jié)果是:
[ 0. 0.30000001 0.60000002 0.90000004]
我們創(chuàng)建了一個(gè)模型晶默,但是我們不知道這個(gè)模型好不好谨娜。想要對(duì)這個(gè)模型做一個(gè)評(píng)估,我們需要y占位符去提供想要的值磺陡,然后我們需要寫一個(gè)損失函數(shù)(loss function)趴梢。
損失函數(shù)表征了當(dāng)前模型和所提供的數(shù)據(jù)之間的差別。我們將會(huì)用一個(gè)標(biāo)準(zhǔn)的損失函數(shù)來(lái)做線性回歸币他。線性回歸就是把所求的值和所提供數(shù)據(jù)的差的平方加起來(lái)坞靶。linear_model - y是一個(gè)向量,向量的值就是所求的值和提供的數(shù)據(jù)之間的誤差蝴悉。我們調(diào)用tf.square去把這個(gè)值求平方彰阴。然后我們用tf.reduce_sum把所有的值加起來(lái),得到一個(gè)數(shù)字拍冠,通過(guò)這個(gè)方法得到一個(gè)衡量這個(gè)樣本錯(cuò)誤的數(shù)據(jù)尿这。
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
求出的損耗是:
23.66
我們可以把W和b設(shè)置為正確答案-1和1簇抵。一個(gè)變量可以用tf.Variable來(lái)初始化,如果要修改它的值射众,也可以用tf.assign碟摆。比如,W = -1 并且 b = 1 就是我們這個(gè)模型最理想的參數(shù):
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
當(dāng)我們這么設(shè)置了之后叨橱,我們發(fā)現(xiàn)損耗就變成了:
0.0
我們這里是事先知道W和b的正確值典蜕,而我們現(xiàn)在研究機(jī)器學(xué)習(xí)的目標(biāo)是要機(jī)器自己找到這個(gè)合適的參數(shù)。接下來(lái)罗洗,我們就來(lái)訓(xùn)練我們的機(jī)器找到這個(gè)參數(shù)愉舔。
tf.train
TensorFlow提供了一個(gè)優(yōu)化器,緩慢的改變每個(gè)變量栖博,來(lái)最小化損耗屑宠。最簡(jiǎn)單的優(yōu)化器就是gradient descent。這個(gè)優(yōu)化器調(diào)整參數(shù)的方式是根據(jù)變量和損耗之間的導(dǎo)數(shù)的大小仇让。簡(jiǎn)單起見(jiàn)典奉,一般都幫你代勞。
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
sess.run(init) # reset values to incorrect defaults.
for i in range(1000):
sess.run(train, {x:[1,2,3,4], y:[0,-1,-2,-3]})
print(sess.run([W, b]))
最終求得模型的參數(shù)是:
[array([-0.9999969], dtype=float32), array([ 0.99999082],
dtype=float32)]
到此為止丧叽,我們讓機(jī)器完成了一次學(xué)習(xí)的過(guò)程卫玖。盡管這僅僅是一個(gè)簡(jiǎn)單的線性回歸的問(wèn)題,根本不需要用TensorFlow大費(fèi)周章的來(lái)實(shí)現(xiàn)踊淳。TensorFlow為常見(jiàn)的設(shè)計(jì)模式假瞬,數(shù)據(jù)結(jié)構(gòu)和功能提供了很好的抽象。我們將在下節(jié)開(kāi)始學(xué)習(xí)怎么使用TensorFlow的這些特性迂尝。
完整的代碼
一個(gè)完整的訓(xùn)練線性回歸模型的代碼如下:
import numpy as np
import tensorflow as tf
# Model parameters
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x:x_train, y:y_train})
# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
運(yùn)行的結(jié)果是:
W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11
注意這里的損耗是一個(gè)非常接近于0的數(shù)字脱茉,如果你運(yùn)行同樣的代碼,得到的結(jié)果不一定和這個(gè)一模一樣垄开,因?yàn)槲覀兪怯秒S機(jī)值來(lái)訓(xùn)練這個(gè)模型的琴许。
最后給出這個(gè)算法圖的圖形: