中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

令人困惑的TensorFlow!

2018-07-04    來(lái)源:raincent

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用
雖然對(duì)于大多數(shù)人來(lái)說(shuō) TensorFlow 的開(kāi)發(fā)語(yǔ)言是 Python,但它并不是一個(gè)標(biāo)準(zhǔn)的 Python 庫(kù)。這個(gè)神經(jīng)網(wǎng)絡(luò)框架通過(guò)構(gòu)建「計(jì)算圖」來(lái)運(yùn)行,對(duì)于很多新手來(lái)說(shuō),在理解其邏輯時(shí)會(huì)遇到很多困難。本文中,來(lái)自谷歌大腦的工程師 Jacob Buckman 將試圖幫你解決初遇 TensorFlow 時(shí)你會(huì)遇到的麻煩。

導(dǎo)論

這是什么?我是誰(shuí)?

我叫 Jacob,是 Google AI Resident 項(xiàng)目的研究學(xué)者。我是在 2017 年夏天加入該項(xiàng)目的,盡管已經(jīng)擁有了豐富的編程經(jīng)驗(yàn),并且對(duì)機(jī)器學(xué)習(xí)的理解也很深刻,但此前我從未使用過(guò) TensorFlow。當(dāng)時(shí)我覺(jué)得憑我的能力應(yīng)該很快就能上手。但讓我沒(méi)想到的是,學(xué)習(xí)曲線相當(dāng)?shù)亩盖,甚至在加入該?xiàng)目幾個(gè)月后,我還偶爾對(duì)如何使用 TensorFlow 代碼來(lái)實(shí)現(xiàn)想法感到困惑。我把這篇博文當(dāng)作瓶中信寫給過(guò)去的自己:一篇我希望在學(xué)習(xí)之初能被給予的入門介紹。我希望這篇博文也能幫助到其他人。

以往的教程缺少了哪些內(nèi)容?

自 TensorFlow 發(fā)布的三年以來(lái),其已然成為深度學(xué)習(xí)生態(tài)系統(tǒng)中的一塊基石。然而對(duì)于初學(xué)者來(lái)說(shuō),它可能并不直觀,特別是與 PyTorch 或 DyNet 這樣運(yùn)行即定義的神經(jīng)網(wǎng)絡(luò)庫(kù)相比。

市面上有許多 TensorFlow 的入門教程,包含從線性回歸到 MNIST 分類和機(jī)器翻譯的內(nèi)容。這些具體實(shí)用的指南是使 TensorFlow 項(xiàng)目啟動(dòng)并運(yùn)行的良好資源,同時(shí)可以作為類似項(xiàng)目的切入點(diǎn)。但對(duì)于有些應(yīng)用開(kāi)發(fā)人員而言,他們開(kāi)發(fā)的應(yīng)用并沒(méi)有好的教程,或?qū)τ谀切┫氪蚱瞥R?guī)的人(在研究中很常見(jiàn))而言,剛接觸 TensorFlow 肯定是讓人沮喪的。

我試圖通過(guò)這篇文章去填補(bǔ)這個(gè)空白。我沒(méi)有專注于某個(gè)特定的任務(wù),而是提出更一般的方法,并解析 TensorFlow 背后基礎(chǔ)的抽象概念。掌握好這些概念之后,用 TensorFlow 進(jìn)行深度學(xué)習(xí)就會(huì)變得直觀易懂。

目標(biāo)受眾

本教程適用于那些在編程和機(jī)器學(xué)習(xí)方面有一定經(jīng)驗(yàn),并想要學(xué)習(xí) TensorFlow 的人。例如:一位想在機(jī)器學(xué)習(xí)課程的最后一個(gè)項(xiàng)目中使用 TensorFlow 的計(jì)算機(jī)科學(xué)專業(yè)的學(xué)生;一位剛被分配到涉及深度學(xué)習(xí)項(xiàng)目的軟件工程師;或是一位處于困惑中的新的 Google AI Resident 新手(向過(guò)去的 Jacob 大聲打招呼)。如果你想進(jìn)一步了解基礎(chǔ)知識(shí),請(qǐng)參閱以下資源:

https://ml.berkeley.edu/blog/2016/11/06/tutorial-1/

http://colah.github.io/

https://www.udacity.com/course/intro-to-machine-learning—ud120

https://www.coursera.org/learn/machine-learning

我們就開(kāi)始吧!

理解 TensorFlow

TensorFlow 不是一個(gè)標(biāo)準(zhǔn)的 Python 庫(kù)

大多數(shù) Python 庫(kù)被編寫為 Python 的自然擴(kuò)展形式。當(dāng)你導(dǎo)入一個(gè)庫(kù)時(shí),你得到的是一組變量、函數(shù)和類,他們擴(kuò)展并補(bǔ)充了你的代碼「工具箱」。當(dāng)你使用它們時(shí),你能預(yù)期到返回的結(jié)果是怎樣的。在我看來(lái),當(dāng)談及 TensorfFlow 時(shí),應(yīng)該把這種認(rèn)知完全拋棄。思考什么是 TensorFlow 及其如何與其他代碼進(jìn)行交互從根本上來(lái)說(shuō)就是錯(cuò)誤的。

Python 和 TensorFlow 之間的關(guān)系可以類比 Javascript 和 HTML 之間的關(guān)系。Javascript 是一種全功能的編程語(yǔ)言,可以做各種美妙的事情。HTML 是用于表示某種類型的實(shí)用計(jì)算抽象(此處指可由 Web 瀏覽器呈現(xiàn)的內(nèi)容)的框架。Javascript 在交互式網(wǎng)頁(yè)中的作用是組裝瀏覽器看到的 HTML 對(duì)象,然后在需要時(shí)通過(guò)將其更新為新的 HTML 來(lái)與其交互。

與 HTML 類似,TensorFlow 是用于表示某種類型的計(jì)算抽象(稱為「計(jì)算圖」)的框架。但我們用 Python 操作 TensorFlow 時(shí),我們用 Pyhton 代碼做的第一件事就是構(gòu)建計(jì)算圖。一旦完成,我們做的第二件事就是與它進(jìn)行交互(啟動(dòng) TensorFlow 的「會(huì)話」)。但重要的是,要記住計(jì)算圖不在變量?jī)?nèi)部;而是處在全局命名空間中。正如莎士比亞所說(shuō):「所有的 RAM 都是一個(gè)階段,所有的變量都僅僅是指針」

第一個(gè)關(guān)鍵抽象:計(jì)算圖

當(dāng)你在瀏覽 TensorFlow 文檔時(shí),可能會(huì)發(fā)現(xiàn)對(duì)「圖形」和「節(jié)點(diǎn)」的間接引用。如果你仔細(xì)閱讀,你甚至可能已經(jīng)發(fā)現(xiàn)了這個(gè)頁(yè)面(https://www.tensorflow.org/programmers_guide/graphs),該頁(yè)面涵蓋了我將以更準(zhǔn)確和技術(shù)化的方式去解釋的內(nèi)容。本節(jié)是一篇高級(jí)攻略,把握重要的直覺(jué)概念,同時(shí)忽略一些技術(shù)細(xì)節(jié)。

那么:什么是計(jì)算圖?它本質(zhì)上是一個(gè)全局?jǐn)?shù)據(jù)結(jié)構(gòu):是一個(gè)有向圖,用于捕獲有關(guān)如何計(jì)算的指令。

讓我們來(lái)看看構(gòu)建計(jì)算圖的一個(gè)示例。在下圖中,上半部分是我們運(yùn)行的代碼及其輸出,下半部分是生成的計(jì)算圖。

import tensorflow as tf

計(jì)算圖:

 

 

可見(jiàn),僅僅導(dǎo)入 TensorFlow 并不會(huì)給我們生成一個(gè)有趣的計(jì)算圖。而只是一個(gè)單獨(dú)的,空白的全局變量。但當(dāng)我們調(diào)用一個(gè) TensorFlow 操作時(shí),會(huì)發(fā)生什么?

代碼:

import tensorflow as tf
two_node = tf.constant(2)
print two_node

輸出:

Tensor("Const:0", shape=(), dtype=int32)

計(jì)算圖:

 

 

快看!我們得到了一個(gè)節(jié)點(diǎn)。它包含常量 2。很驚訝吧,這來(lái)自于一個(gè)名為 tf.constant 的函數(shù)。當(dāng)我們打印這個(gè)變量時(shí),我們看到它返回一個(gè) tf.Tensor 對(duì)象,它是一個(gè)指向我們剛剛創(chuàng)建的節(jié)點(diǎn)的指針。為了強(qiáng)調(diào)這一點(diǎn),以下是另外一個(gè)示例:

代碼:

import tensorflow as tf
two_node = tf.constant(2)
another_two_node = tf.constant(2)
two_node = tf.constant(2)
tf.constant(3)

 

計(jì)算圖:

 

 

每次我們調(diào)用 tf.constant 時(shí),我們都會(huì)在圖中創(chuàng)建一個(gè)新的節(jié)點(diǎn)。即使該節(jié)點(diǎn)的功能與現(xiàn)有節(jié)點(diǎn)相同,即使我們將節(jié)點(diǎn)重新分配給同一個(gè)變量,或者即使我們根本沒(méi)有將其分配給一個(gè)變量,結(jié)果都是一樣的。

代碼:

import tensorflow as tf
two_node = tf.constant(2)
another_pointer_at_two_node = two_node
two_node = None
print two_node
print another_pointer_at_two_node

輸出:

None
Tensor("Const:0", shape=(), dtype=int32)

計(jì)算圖:

 

 

好啦,讓我們更進(jìn)一步:

代碼:

import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node ## equivalent to tf.add(two_node, three_node)

計(jì)算圖:

 

 

現(xiàn)在我們正談?wù)?mdash;這才是我們真正想要的計(jì)算圖!請(qǐng)注意,+ 操作在 TensorFlow 中過(guò)載,因此同時(shí)添加兩個(gè)張量會(huì)在圖中增加一個(gè)節(jié)點(diǎn),盡管它表面上看起來(lái)不像是 TensorFlow 操作。

那好,所以 two_node 指向包含 2 的節(jié)點(diǎn),three_node 指向包含 3 的節(jié)點(diǎn),同時(shí) sum_node 指向包含 ...+ 的節(jié)點(diǎn)?怎么回事?它不是應(yīng)該包含 5 嗎?

事實(shí)證明,并沒(méi)有。計(jì)算圖只包含計(jì)算步驟;不包含結(jié)果。至少……現(xiàn)在還沒(méi)有!

第二個(gè)關(guān)鍵抽象: 會(huì)話

如果錯(cuò)誤地理解 TensorFlow 抽象概念也有個(gè)「瘋狂三月」(NCAA 籃球錦標(biāo)賽,大部分在三月進(jìn)行),那么會(huì)話將成為每年的一號(hào)種子選手。會(huì)話有著那樣令人困惑的殊榮是因?yàn)槠浞粗庇X(jué)的命名卻又普遍存在—幾乎每個(gè) TensorFlow 呈現(xiàn)都至少一次明確地調(diào)用 tf.Session()。

會(huì)話的作用是處理內(nèi)存分配和優(yōu)化,使我們能夠?qū)嶋H執(zhí)行由計(jì)算圖指定的計(jì)算。你可以將計(jì)算圖想象為我們想要執(zhí)行的計(jì)算的「模版」:它列出了所有步驟。為了使用計(jì)算圖,我們需要啟動(dòng)一個(gè)會(huì)話,它使我們能夠?qū)嶋H地完成任務(wù);例如,遍歷模版的所有節(jié)點(diǎn)來(lái)分配一堆用于存儲(chǔ)計(jì)算輸出的存儲(chǔ)器。為了使用 TensorFlow 進(jìn)行各種計(jì)算,你既需要計(jì)算圖也需要會(huì)話。

會(huì)話包含一個(gè)指向全局圖的指針,該指針通過(guò)指向所有節(jié)點(diǎn)的指針不斷更新。這意味著在創(chuàng)建節(jié)點(diǎn)之前還是之后創(chuàng)建會(huì)話都無(wú)所謂。

創(chuàng)建會(huì)話對(duì)象后,可以使用 sess.run(node) 返回節(jié)點(diǎn)的值,并且 TensorFlow 將執(zhí)行確定該值所需的所有計(jì)算。

代碼:

import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
print sess.run(sum_node)

 

輸出:

5

計(jì)算圖:

 

 

太好了!我們也可以傳遞一個(gè)列表,sess.run([node1, node2, ...]),并讓它返回多個(gè)輸出:

代碼:

import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
print sess.run([two_node, sum_node])

 

輸出:

[2, 5]

計(jì)算圖:

 

 

一般來(lái)說(shuō),sess.run() 的調(diào)用往往是 TensorFlow 最大的瓶頸之一,因此調(diào)用它的次數(shù)越少越好。如果可以的話,在一個(gè) sess.run() 的調(diào)用中返回多個(gè)項(xiàng)目,而不是進(jìn)行多個(gè)調(diào)用。

占位符和 feed_dict

迄今為止,我們所做的計(jì)算一直很乏味:沒(méi)有機(jī)會(huì)獲得輸入,所以它們總是輸出相同的東西。一個(gè)更有價(jià)值的應(yīng)用可能涉及構(gòu)建一個(gè)計(jì)算圖,它接受輸入,以某種(一致)方式處理它,并返回一個(gè)輸出。

最直接的方法是使用占位符。占位符是一種用于接受外部輸入的節(jié)點(diǎn)。

代碼:

import tensorflow as tf
input_placeholder = tf.placeholder(tf.int32)
sess = tf.Session()
print sess.run(input_placeholder)

輸出:

Traceback (most recent call last):
...
InvalidArgumentError (see above *for* traceback): You must feed a value *for* placeholder tensor 'Placeholder' *with* dtype int32
[[Node: Placeholder = Placeholder[dtype=DT_INT32, shape=<unknown>, _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]

 

計(jì)算圖:

 

 

... 這是一個(gè)糟糕的例子,因?yàn)樗l(fā)了一個(gè)異常。占位符預(yù)計(jì)會(huì)被賦予一個(gè)值。但我們沒(méi)有提供一個(gè)值,所以 TensorFlow 崩潰了。

為了提供一個(gè)值,我們使用 sess.run() 的 feed_dixt 屬性。

代碼:

import tensorflow as tf
input_placeholder = tf.placeholder(tf.int32)
sess = tf.Session()
print sess.run(input_placeholder, feed_dict={input_placeholder: 2})

 

輸出:

2

計(jì)算圖:

 

 

這就好多了。注意傳遞給 feed_dict 的 dict 格式,其關(guān)鍵應(yīng)該是與圖中的占位符節(jié)點(diǎn)相對(duì)應(yīng)的變量(如前所述,它實(shí)際上意味著指向圖中占位符節(jié)點(diǎn)的指針)。相應(yīng)的值是要分配給每個(gè)占位符的數(shù)據(jù)元素——通常是標(biāo)量或 Numpy 數(shù)組。

第三個(gè)關(guān)鍵抽象:計(jì)算路徑

讓我們看看另一個(gè)使用占位符的示例:

代碼:

import tensorflow as tf
input_placeholder = tf.placeholder(tf.int32)
three_node = tf.constant(3)
sum_node = input_placeholder + three_node
sess = tf.Session()
print sess.run(three_node)
print sess.run(sum_node)

輸出:

3
Traceback (most recent call last):
...
InvalidArgumentError (see above for traceback): You must feed a value *for* placeholder tensor 'Placeholder_2' with dtype int32
[[Node: Placeholder_2 = Placeholder[dtype=DT_INT32, shape=<unknown>, _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]

計(jì)算圖:

 

 

為什么第二次調(diào)用 sess.run() 會(huì)失敗?即使我們沒(méi)有評(píng)估 input_placeholder,為什么仍會(huì)引發(fā)與 input_placeholder 相關(guān)的錯(cuò)誤?答案在于最終的關(guān)鍵 TensorFlow 抽象:計(jì)算路徑。幸運(yùn)的是,這個(gè)抽象非常直觀。

當(dāng)我們?cè)谝蕾囉趫D中其他節(jié)點(diǎn)的節(jié)點(diǎn)上調(diào)用 sess.run() 時(shí),我們也需要計(jì)算那些節(jié)點(diǎn)的值。如果這些節(jié)點(diǎn)具有依賴關(guān)系,那么我們需要計(jì)算這些值(依此類推……),直到達(dá)到計(jì)算圖的「頂端」,即節(jié)點(diǎn)沒(méi)有父節(jié)點(diǎn)時(shí)。

sum_node 的計(jì)算路徑:

 

 

所有三個(gè)節(jié)點(diǎn)都需要進(jìn)行求值以計(jì)算 sum_node 的值。最重要的是,這包含了我們未填充的占位符,并解釋了異常!

現(xiàn)在來(lái)看 three_node 的計(jì)算路徑:

 

 

根據(jù)圖結(jié)構(gòu),我們不需要計(jì)算所有節(jié)點(diǎn)才能評(píng)估我們想要的節(jié)點(diǎn)!因?yàn)槲覀冊(cè)谠u(píng)估 three_node 時(shí)不需要評(píng)估 placehoolder_node,所以運(yùn)行 sess.run(three_node) 不會(huì)引發(fā)異常。

TensorFlow 僅通過(guò)必需的節(jié)點(diǎn)自動(dòng)進(jìn)行計(jì)算這一事實(shí)是該框架的一個(gè)巨大優(yōu)勢(shì)。如果計(jì)算圖非常大并且有許多不必要的節(jié)點(diǎn),那么它可以節(jié)省大量調(diào)用的運(yùn)行時(shí)間。它允許我們構(gòu)建大型的「多用途」計(jì)算圖,這些計(jì)算圖使用單個(gè)共享的核心節(jié)點(diǎn)集合,并根據(jù)所采取的不同計(jì)算路徑去做不同的事情。對(duì)于幾乎所有應(yīng)用而言,根據(jù)所采取的計(jì)算路徑考慮 sess.run() 的調(diào)用是很重要的。

變量 & 副作用

至此,我們已經(jīng)看到兩種類型的「無(wú)祖先」節(jié)點(diǎn)(no-ancestor node):每次運(yùn)行都一樣的 tf.constant 和每次運(yùn)行都不一樣的 tf.placeholder。我們常常要考慮第三種情況:一個(gè)通常在運(yùn)行時(shí)保持值不變的節(jié)點(diǎn)也可以被更新為新值。

這時(shí)就需要引入變量。

變量對(duì)于使用 TensorFlow 進(jìn)行深度學(xué)習(xí)是至關(guān)重要的,因?yàn)槟P偷膮?shù)就是變量。在訓(xùn)練期間,你希望通過(guò)梯度下降在每個(gè)步驟更新參數(shù);但在評(píng)估時(shí),你希望保持參數(shù)不變,并將大量不同的測(cè)試集輸入模型。通常,模型所有可訓(xùn)練參數(shù)都是變量。

要?jiǎng)?chuàng)建變量,就需要使用 tf.get_variable()。tf.get_variable() 的前兩個(gè)參數(shù)是必需的,其余參數(shù)是可選的。它們是 tf.get_variable(name,shape)。name 是一個(gè)唯一標(biāo)識(shí)這個(gè)變量對(duì)象的字符串。它必須相對(duì)于全局圖是唯一的,所以要明了你使用過(guò)的所有命名,確保沒(méi)有重復(fù)。shape 是與張量形狀對(duì)應(yīng)的整數(shù)數(shù)組,它的語(yǔ)法非常直觀:按順序,每個(gè)維度只有一個(gè)整數(shù)。例如,一個(gè) 3x8 矩陣形狀是 [3, 8]。要?jiǎng)?chuàng)建一個(gè)標(biāo)量,就需要使用形狀為 [] 的空列表。

代碼:

import tensorflow as tf
count_variable = tf.get_variable("count", [])
sess = tf.Session()
print sess.run(count_variable)

輸出:

Traceback (most recent call last):
...
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value count
[[Node: _retval_count_0_0 = _Retval[T=DT_FLOAT, index=0, _device="/job:localhost/replica:0/task:0/device:CPU:0"](count)]]

計(jì)算圖:

 

 

噫,另一個(gè)異常。當(dāng)首次創(chuàng)建變量節(jié)點(diǎn)時(shí),它的值基本上為「null」,并且任何試圖對(duì)它求值的操作都會(huì)引發(fā)這個(gè)異常。我們只能在將值放入變量之后才能對(duì)其求值。主要有兩種將值放入變量的方法:初始化器和 tf.assign()。我們先看看 tf.assign():

代碼:

import tensorflow as tf
count_variable = tf.get_variable("count", [])
zero_node = tf.constant(0.)
assign_node = tf.assign(count_variable, zero_node)
sess = tf.Session()
sess.run(assign_node)
print sess.run(count_variable)

輸出:

0

計(jì)算圖:

 

 

與我們迄今為止見(jiàn)過(guò)的節(jié)點(diǎn)相比,tf.assign(target, value) 是具備一些獨(dú)特屬性:

恒等運(yùn)算。tf.assign(target, value) 不做任何有趣的運(yùn)算,通常與 value 相等。

副作用。當(dāng)計(jì)算「流經(jīng)」assign_node 時(shí),副作用發(fā)生在圖中的其他節(jié)點(diǎn)上。此時(shí),副作用是用存儲(chǔ)在 zero_node 中的值替換 count_variable 的值。

非依賴邊。即使 count_variable 節(jié)點(diǎn)和 assign_node 在圖中是相連的,但它們彼此獨(dú)立。這意味著計(jì)算任一節(jié)點(diǎn)時(shí),計(jì)算不會(huì)通過(guò)邊回流。然而,assign_node 依賴于 zero_node,它需要知道分配了什么。

「副作用」節(jié)點(diǎn)支撐著大部分 Tensorflow 深度學(xué)習(xí)工作流程,所以請(qǐng)確保自己真正理解了在該節(jié)點(diǎn)發(fā)生的事情。當(dāng)我們調(diào)用 sess.run(assign_node) 時(shí),計(jì)算路徑會(huì)通過(guò) assign_node 和 zero_node。

計(jì)算圖:

 

 

當(dāng)計(jì)算流經(jīng)圖中的任何節(jié)點(diǎn)時(shí),它還會(huì)執(zhí)行由該節(jié)點(diǎn)控制的任何副作用,如圖中綠色所示。由于 tf.assign 的特殊副作用,與 count_variable(之前為「null」)關(guān)聯(lián)的內(nèi)存現(xiàn)在被永久設(shè)置為 0。這意味著當(dāng)我們下一次調(diào)用 sess.run(count_variable) 時(shí),不會(huì)引發(fā)任何異常。相反,我們會(huì)得到 0 值。成功!

接下來(lái),讓我們看看初始化器:

代碼:

import tensorflow as tf
const_init_node = tf.constant_initializer(0.)
count_variable = tf.get_variable("count", [], initializer=const_init_node)
sess = tf.Session()
print sess.run([count_variable])

輸出:

Traceback (most recent call last):
...
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value count
[[Node: _retval_count_0_0 = _Retval[T=DT_FLOAT, index=0, _device="/job:localhost/replica:0/task:0/device:CPU:0"](count)]]

計(jì)算圖:

 

 

那好,這里發(fā)生了什么?為什么初始化器不工作?

問(wèn)題出現(xiàn)在會(huì)話和圖之間的分離。我們已將 get_variable 的 initializer 屬性設(shè)置為指向 const_init_node,但它只是在圖中的節(jié)點(diǎn)之間添加了一個(gè)新的連接。我們還沒(méi)有做任何解決異常根源的事:與變量節(jié)點(diǎn)(存儲(chǔ)在會(huì)話中,而不是計(jì)算圖中)相關(guān)聯(lián)的內(nèi)存仍然設(shè)置為「null」。我們需要通過(guò)會(huì)話使 const_init_node 去更新變量。

代碼:

import tensorflow as tf
const_init_node = tf.constant_initializer(0.)
count_variable = tf.get_variable("count", [], initializer=const_init_node)
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
print sess.run(count_variable)

輸出:

0

計(jì)算圖:

 

 

為此,我們添加另一個(gè)特殊的節(jié)點(diǎn):init = tf.global_variables_initializer()。與 tf.assign() 類似,這是一個(gè)帶有副作用的節(jié)點(diǎn)。與 tf.assign() 相反,實(shí)際上我們不需要指定它的輸入是什么!tf.global_variables_initializer() 將在其創(chuàng)建時(shí)查看全局圖并自動(dòng)將依賴關(guān)系添加到圖中的每個(gè) tf.initializer。當(dāng)我們?cè)谥笫褂?sess.run(init) 對(duì)它求值時(shí),它會(huì)告訴每個(gè)初始化程序執(zhí)行變量初始化,并允許我們運(yùn)行 sess.run(count_variable) 而不出錯(cuò)。

變量共享

你可能會(huì)遇到帶有變量共享的 Tensorflow 代碼,其涉及創(chuàng)建作用域并設(shè)置「reuse = True」。我強(qiáng)烈建議不要在自己的代碼中使用變量共享。如果你想在多個(gè)地方使用單個(gè)變量,只需以編程方式記錄指向該變量節(jié)點(diǎn)的指針,并在需要時(shí)重新使用它。換言之,對(duì)于想要保存在內(nèi)存中的每個(gè)變量,你只需要調(diào)用一次 tf.get_variable()。

優(yōu)化器

最后:進(jìn)行真正的深度學(xué)習(xí)!如果你跟上我的節(jié)奏,那么其余概念對(duì)你來(lái)說(shuō)應(yīng)該非常簡(jiǎn)單。

在深度學(xué)習(xí)中,典型的「內(nèi)循環(huán)」訓(xùn)練如下:

1. 獲取輸入和 true_output
2. 根據(jù)輸入和參數(shù)計(jì)算「推測(cè)」值
3. 根據(jù)推測(cè)與 true_output 之間的差異計(jì)算「損失」
4. 根據(jù)損失的梯度更新參數(shù)

讓我們把所有東西放在一個(gè)快速腳本里,解決簡(jiǎn)單的線性回歸問(wèn)題:

代碼:

 

 

輸出:

 

 

就像你看到的一樣,損失基本上變?yōu)榱,并且我們?duì)真實(shí)參數(shù)進(jìn)行了很好的估計(jì)。我希望你只對(duì)代碼中的以下部分感到陌生:

## finally, set up the optimizer and minimization node
optimizer = tf.train.GradientDescentOptimizer(1e-3)
train_op = optimizer.minimize(loss)

但是,既然你對(duì) Tensorflow 的基本概念有了很好的理解,這段代碼就很容易解釋!第一行,optimizer = tf.train.GradientDescentOptimizer(1e-3) 不會(huì)向計(jì)算圖中添加節(jié)點(diǎn)。它只是創(chuàng)建一個(gè)包含有用的幫助函數(shù)的 Python 對(duì)象。第二行,train_op = optimizer.minimize(loss) 將一個(gè)節(jié)點(diǎn)添加到圖中,并將一個(gè)指針存儲(chǔ)在變量 train_op 中。train_op 節(jié)點(diǎn)沒(méi)有輸出,但是有一個(gè)十分復(fù)雜的副作用:

train_op 回溯輸入和損失的計(jì)算路徑,尋找變量節(jié)點(diǎn)。對(duì)于它找到的每個(gè)變量節(jié)點(diǎn),計(jì)算該變量對(duì)于損失的梯度。然后計(jì)算該變量的新值:當(dāng)前值減去梯度乘以學(xué)習(xí)率的積。最后,它執(zhí)行賦值操作更新變量的值。

因此基本上,當(dāng)我們調(diào)用 sess.run(train_op) 時(shí),它對(duì)我們的所有變量做了一個(gè)梯度下降的步驟。當(dāng)然,我們也需要使用 feed_dict 填充輸入和輸出占位符,并且我們還希望打印損失的值,因?yàn)檫@樣方便調(diào)試。

用 tf.Print 調(diào)試

當(dāng)你用 Tensorflow 開(kāi)始做更復(fù)雜的事情時(shí),你需要進(jìn)行調(diào)試。一般來(lái)說(shuō),檢查計(jì)算圖中發(fā)生了什么是相當(dāng)困難的。因?yàn)槟阌肋h(yuǎn)無(wú)法訪問(wèn)你想打印的值—它們被鎖定在 sess.run() 的調(diào)用中,所以你不能使用常規(guī)的 Python 打印語(yǔ)句。具體來(lái)說(shuō),假設(shè)你是想檢查一個(gè)計(jì)算的中間值。在調(diào)用 sess.run() 之前,中間值還不存在。但是,當(dāng)你調(diào)用的 sess.run() 返回時(shí),中間值又不見(jiàn)了!

讓我們看一個(gè)簡(jiǎn)單的示例。

代碼:

import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
print sess.run(sum_node)

輸出:

5

這讓我們看到了答案是 5。但是,如果我們想要檢查中間值,two_node 和 three_node,怎么辦?檢查中間值的一個(gè)方法是向 sess.run() 中添加一個(gè)返回參數(shù),該參數(shù)指向要檢查的每個(gè)中間節(jié)點(diǎn),然后在返回后,打印它的值。

代碼

import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
answer, inspection = sess.run([sum_node, [two_node, three_node]])
print inspection
print answer

輸出:

[2, 3]5

這通常是有用的,但當(dāng)代碼變得越來(lái)越復(fù)雜時(shí),這可能有點(diǎn)棘手。一個(gè)更方便的方法是使用 tf.Print 語(yǔ)句。令人困惑的是,tf.Print 實(shí)際上是一種具有輸出和副作用的 Tensorflow 節(jié)點(diǎn)!它有兩個(gè)必需參數(shù):要復(fù)制的節(jié)點(diǎn)和要打印的內(nèi)容列表!敢獜(fù)制的節(jié)點(diǎn)」可以是圖中的任何節(jié)點(diǎn);tf.Print 是一個(gè)與「要復(fù)制的節(jié)點(diǎn)」相關(guān)的恒等操作,意味著輸出的是輸入的副本。但是,它的副作用是打印出「打印列表」里的所有當(dāng)前值。

代碼:

import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
print_sum_node = tf.Print(sum_node, [two_node, three_node])
sess = tf.Session()
print sess.run(print_sum_node)

 

輸出:

[2][3]5

計(jì)算圖:

 

 

有關(guān) tf.Print 一個(gè)重要且有點(diǎn)微妙的點(diǎn):打印是一個(gè)副作用。像所有其他副作用一樣,只要在計(jì)算流經(jīng) tf.Print 節(jié)點(diǎn)時(shí)才會(huì)進(jìn)行打印。如果 tf.Print 節(jié)點(diǎn)不在計(jì)算路徑上,則不會(huì)打印任何內(nèi)容。特別的是,即使 tf.Print 節(jié)點(diǎn)正在復(fù)制的原始節(jié)點(diǎn)位于計(jì)算路徑上,但 tf.Print 節(jié)點(diǎn)本身可能不在。請(qǐng)注意這個(gè)問(wèn)題!當(dāng)這種情況發(fā)生時(shí)(總會(huì)發(fā)生的),如果你沒(méi)有明確地找到問(wèn)題所在,它會(huì)讓你感到十分沮喪。一般來(lái)說(shuō),最好在創(chuàng)建要復(fù)制的節(jié)點(diǎn)后,立即創(chuàng)建你的 tf.Print 節(jié)點(diǎn)。

代碼:

import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node### this new copy of two_node is not on the computation path, so nothing prints!
print_two_node = tf.Print(two_node, [two_node, three_node, sum_node])
sess = tf.Session()
print sess.run(sum_node)

輸出:

5

計(jì)算圖:

 

 

這里有一個(gè)很好的資源(https://wookayin.github.io/tensorflow-talk-debugging/#1),它提供了其他一些實(shí)用的調(diào)試建議。

結(jié)論

希望這篇博文可以幫助你更好地理解什么是 Tensorflow,它是如何工作的以及怎么使用它。總而言之,本文介紹的概念對(duì)所有 Tensorflow 項(xiàng)目都很重要,但只是停留在表面。在你探索 Tensorflow 的旅程中,你可能會(huì)遇到其他各種你需要的有趣概念:條件、迭代、分布式 Tensorflow、變量作用域、保存和加載模型、多圖、多會(huì)話和多核、數(shù)據(jù)加載器隊(duì)列等等。我將在未來(lái)的博文中討論這些主題。但如果你使用官方文檔、一些代碼示例和一點(diǎn)深度學(xué)習(xí)的魔力來(lái)鞏固你在本文學(xué)到的思想,我相信你一定可以弄明白 Tensorflow!

原文鏈接:https://jacobbuckman.com/post/tensorflow-the-confusing-parts-1/

標(biāo)簽: Google 代碼 谷歌 腳本 網(wǎng)絡(luò)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:GDPR阻礙安全研究的五個(gè)方面

下一篇:發(fā)布AI芯片昆侖和百度大腦3.0、L4自動(dòng)駕駛巴士量產(chǎn)下線,這是百度All in AI一年后的最新答卷