上一篇 第一個(gè)機(jī)器學(xué)習(xí)問(wèn)題 其實(shí)是一個(gè)線性回歸問(wèn)題(Linear Regression)假消,呈現(xiàn)了用數(shù)據(jù)來(lái)訓(xùn)練模型的具體方式嚷往。本篇從平行世界返回扮超,利用TensorFlow,重新解決一遍該問(wèn)題苍凛。
TensorFlow的API有低級(jí)和高級(jí)之分壁拉。
底層的API基于TensorFlow內(nèi)核,它主要用于研究或需要對(duì)模型進(jìn)行完全控制的場(chǎng)合汤功。如果你想使用TF來(lái)輔助實(shí)現(xiàn)某個(gè)特定算法物邑、呈現(xiàn)和控制算法的每個(gè)細(xì)節(jié),那么就該使用低級(jí)的API冤竹。
高級(jí)API基于TensorFlow內(nèi)核構(gòu)建拂封,屏蔽了繁雜的細(xì)節(jié),適合大多數(shù)場(chǎng)景下使用鹦蠕。如果你有一個(gè)想法要驗(yàn)證并快速獲得結(jié)果冒签,那么TF的高級(jí)API就是高效的構(gòu)建工具。
本篇使用TF的低級(jí)API來(lái)呈現(xiàn)線性回歸的每一個(gè)步驟钟病。
第一個(gè)機(jī)器學(xué)習(xí)的TF實(shí)現(xiàn)
TensorFlow的計(jì)算分為兩個(gè)階段:
- 構(gòu)建計(jì)算圖萧恕;
- 執(zhí)行計(jì)算圖刚梭。
先給出“平行世界”版本,(a, b)初始值為(-1, 50)票唆,第二次嘗試(-1, 40)朴读。
import tensorflow as tf
# model parameters
a = tf.Variable([-1.], tf.float32)
b = tf.Variable([50.], tf.float32)
# model input and output
x = tf.placeholder(tf.float32)
linear_model = a * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) / 8
# training data
x_train = [22, 25, 28, 30]
y_train = [18, 15, 12, 10]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # 1st
print("loss: %s" % (sess.run(loss, {x: x_train, y: y_train})))
# 2nd
fixa = tf.assign(a, [-1.])
fixb = tf.assign(b, [40.])
sess.run([fixa, fixb])
print("loss: %s" % (sess.run(loss, {x: x_train, y: y_train})))
程序輸出
loss: 50.0
loss: 0.0
上面的python代碼利用了在2 TensorFlow內(nèi)核基礎(chǔ) 介紹的基本API實(shí)現(xiàn)了“第一個(gè)機(jī)器學(xué)習(xí)問(wèn)題”。代碼通過(guò)一步步構(gòu)造計(jì)算圖走趋,最后得到了loss節(jié)點(diǎn)衅金。loss即4 第一個(gè)機(jī)器學(xué)習(xí)問(wèn)題中定義過(guò)的損失函數(shù),這里再次給出其定義:
構(gòu)建好計(jì)算圖簿煌,接下來(lái)開(kāi)始執(zhí)行氮唯。執(zhí)行l(wèi)oss節(jié)點(diǎn)(同時(shí)提供基于tf.placeholder的訓(xùn)練數(shù)據(jù)),得到loss的值為50姨伟。然后開(kāi)始第二次訓(xùn)練惩琉,修改基于tf.Variable的a和b的值,再次執(zhí)行l(wèi)oss節(jié)點(diǎn)夺荒,loss的值為0瞒渠,降到了最低。此時(shí)的a和b就是最佳的模型參數(shù)了技扼。
還記得那個(gè)神秘力量嗎伍玖?到底是什么讓機(jī)器在第二次訓(xùn)練中將模型參數(shù)(a, b)的值從初始的隨機(jī)值(-1, 50)遷移到最優(yōu)的(-1, 40)?如果不靠運(yùn)氣的話淮摔,機(jī)器如何能自動(dòng)的找到最優(yōu)解呢私沮?
梯度下降算法
在此之前始赎,或許你已經(jīng)想到了隨機(jī)窮舉的辦法和橙,因?yàn)闄C(jī)器不怕累。這的確是個(gè)辦法造垛,但面臨的挑戰(zhàn)也不可接受:不可控魔招。因?yàn)榧幢闶侵挥?個(gè)參數(shù)的模型訓(xùn)練,其枚舉域也是無(wú)限大的五辽,這和靠運(yùn)氣沒(méi)有分別办斑。運(yùn)氣差的話,等個(gè)幾百年也說(shuō)不定杆逗。
不繞圈子乡翅,那個(gè)神秘力量就是:梯度下降算法(gradient descent)。雖然它也是讓機(jī)器一小步一小步的去嘗試不同的(a, b)的組合罪郊,但是它能指導(dǎo)每次前進(jìn)的方向蠕蚜,使得每嘗試一組新的值,loss就能變小一點(diǎn)點(diǎn)悔橄,直到趨于穩(wěn)定靶累。
而這一切TF已經(jīng)把它封裝好了腺毫。 本篇先把它當(dāng)個(gè)黑盒子使用。
tf.train API
import tensorflow as tf
# model parameters
a = tf.Variable([-1.], tf.float32)
b = tf.Variable([50.], tf.float32)
# model input and output
x = tf.placeholder(tf.float32)
linear_model = a * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) / 8 # sum of the squares
# training data
x_train = [22, 25, 28, 30]
y_train = [18, 15, 12, 10]
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
for i in range(1000):
sess.run(train, {x: x_train, y: y_train})
# evaluate training accuracy
curr_a, curr_b, curr_loss = sess.run([a, b, loss], {x: x_train, y: y_train})
print("a: %s b: %s loss: %s" % (curr_a, curr_b, curr_loss))
代碼幾乎和TensorFlow Get Started官方代碼一致挣柬,主要區(qū)別在于訓(xùn)練數(shù)據(jù)不同潮酒,以及初始值不同。
- TF官方的訓(xùn)練數(shù)據(jù)是x_train = [1, 2, 3, 4]邪蛔,y_train = [0, -1, -2, -3]急黎,而我們的訓(xùn)練數(shù)據(jù)是“平行世界”的觀察記錄x_train = [22, 25, 28, 30],y_train = [18, 15, 12, 10]侧到。
- TF官方的(a, b)初始值是(.3, -.3), 我們的是(-1., 50.)叁熔。
- 或許你還發(fā)現(xiàn)在官方版本的loss函數(shù)末尾沒(méi)有
/ 8
,是因?yàn)槲沂褂镁讲畹木壒剩?由4x2得到(4個(gè)訓(xùn)練數(shù)據(jù))床牧。
重點(diǎn)說(shuō)下tf.train API荣回。tf.train.GradientDescentOptimizer即封裝了梯度下降算法。梯度下降在數(shù)學(xué)上屬于最優(yōu)化領(lǐng)域戈咳,從其名字Optimizater也可體現(xiàn)出心软。其參數(shù)就是“學(xué)習(xí)率”(learning rate),先記住這個(gè)名詞著蛙,暫不展開(kāi)删铃,其基本的效用是決定待調(diào)整參數(shù)的調(diào)整幅度。學(xué)習(xí)率越大踏堡,調(diào)整幅度越大猎唁,學(xué)習(xí)的越快。反之亦然顷蟆〗胗纾可也并不是越大越好,是相對(duì)來(lái)說(shuō)的帐偎。先取0.01逐纬。
另一個(gè)需要輸入給梯度下降算法的就是loss,它是求最優(yōu)化解的主體削樊,通過(guò)optimizer.minimize(loss)傳入豁生,并返回train節(jié)點(diǎn)。接下來(lái)在循環(huán)中執(zhí)行train節(jié)點(diǎn)即可漫贞,循環(huán)的次數(shù)甸箱,即訓(xùn)練的步數(shù)。
執(zhí)行計(jì)算圖迅脐,程序輸出:
a: [ nan] b: [-inf] loss: nan
這個(gè)結(jié)果令人崩潰芍殖,僅僅換了下TF官方get started中例子中模型的訓(xùn)練數(shù)據(jù)和初始值,它就不工作了仪际。
先來(lái)看看問(wèn)題在哪围小。一個(gè)調(diào)試的小技巧就是打印每次訓(xùn)練的情況昵骤,并調(diào)整loop的次數(shù)。
for i in range(49):
sess.run(train, {x: x_train, y: y_train})
curr_a, curr_b, curr_loss = sess.run([a, b, loss], {x: x_train, y: y_train})
print("a: %s b: %s loss: %s" % (curr_a, curr_b, curr_loss))
程序輸出:
TF實(shí)際是工作的肯适,并沒(méi)有撂挑子变秦。只是它訓(xùn)練時(shí)每次調(diào)整(a, b)都幅度很大,接下來(lái)又矯枉過(guò)正且幅度越來(lái)越大框舔,導(dǎo)致最終承載a和b的tf.float32溢出而產(chǎn)生了nan蹦玫。這不是TF的一個(gè)bug,而是算法本身刘绣、訓(xùn)練數(shù)據(jù)樱溉、學(xué)習(xí)率、訓(xùn)練次數(shù)共同導(dǎo)致的(它們有個(gè)共同的名字:超參數(shù)纬凤。)福贞。可見(jiàn)停士,訓(xùn)練是一門藝術(shù)挖帘。
直覺(jué)上,初始值或許有優(yōu)劣之分恋技,或許是離最優(yōu)值越近的初始值越容易找到拇舀。可是訓(xùn)練數(shù)據(jù)則應(yīng)該是無(wú)差別的吧蜻底?實(shí)則不然骄崩。但是現(xiàn)在我還不打算把它解釋清楚,等后面分析完梯度下降算法后再回來(lái)看這個(gè)問(wèn)題薄辅。
遇到該問(wèn)題的也不再少數(shù)要拂,Stack Overflow上已經(jīng)很好的回答了。我們先通過(guò)調(diào)整學(xué)習(xí)率和訓(xùn)練次數(shù)來(lái)得到一個(gè)完美的Ending长搀。
把學(xué)習(xí)率從0.01調(diào)制0.0028宇弛,然后將訓(xùn)練次數(shù)從1000調(diào)整至70000鸡典。
程序輸出:
a: [-1.02855277] b: [ 40.75948715] loss: 0.00379487
最終代碼如下:
import tensorflow as tf
# model parameters
a = tf.Variable([-1.], tf.float32)
b = tf.Variable([50.], tf.float32)
# model input and output
x = tf.placeholder(tf.float32)
linear_model = a * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) / 8 # sum of the squares
# training data
x_train = [22, 25, 28, 30]
y_train = [18, 15, 12, 10]
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.0028)
train = optimizer.minimize(loss)
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
for i in range(70000):
sess.run(train, {x: x_train, y: y_train})
# evaluate training accuracy
curr_a, curr_b, curr_loss = sess.run([a, b, loss], {x: x_train, y: y_train})
print("a: %s b: %s loss: %s" % (curr_a, curr_b, curr_loss))
TensorBoard
TF的另一個(gè)強(qiáng)大之處就是可視化算法的TensorBoard源请,把構(gòu)造的計(jì)算圖顯示出來(lái)。圖中顯示彻况,每一個(gè)基本運(yùn)算都被獨(dú)立成了一個(gè)節(jié)點(diǎn)谁尸。除了圖中我標(biāo)注的Rank節(jié)點(diǎn)、range節(jié)點(diǎn)纽甘,start節(jié)點(diǎn)良蛮、delta節(jié)點(diǎn)外,其他節(jié)點(diǎn)都是由所寫代碼構(gòu)建出來(lái)的悍赢。
詞匯表
- derivative决瞳; 導(dǎo)數(shù)货徙;
- estimator: 估計(jì);
- gradient descent: 梯度下降皮胡;
- inference: 推理痴颊;
- linear regression:線性回歸;
- loss function: 損失函數(shù)屡贺;
- magnitude: 量蠢棱;
- optimal: 最優(yōu)的;
- optimizers: 優(yōu)化器甩栈;
共享協(xié)議:署名-非商業(yè)性使用-禁止演繹(CC BY-NC-ND 3.0 CN)
轉(zhuǎn)載請(qǐng)注明:作者黑猿大叔(簡(jiǎn)書)