(第一部分 機(jī)器學(xué)習(xí)基礎(chǔ))
第01章 機(jī)器學(xué)習(xí)概覽
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(上)
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(下)
第03章 分類
第04章 訓(xùn)練模型
第05章 支持向量機(jī)
第06章 決策樹
第07章 集成學(xué)習(xí)和隨機(jī)森林
第08章 降維
(第二部分 神經(jīng)網(wǎng)絡(luò)和深度學(xué)習(xí))
第9章 啟動(dòng)和運(yùn)行TensorFlow
第10章 人工神經(jīng)網(wǎng)絡(luò)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(上)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(下)
第12章 設(shè)備和服務(wù)器上的分布式 TensorFlow
第13章 卷積神經(jīng)網(wǎng)絡(luò)
第14章 循環(huán)神經(jīng)網(wǎng)絡(luò)
第15章 自編碼器
第16章 強(qiáng)化學(xué)習(xí)(上)
第16章 強(qiáng)化學(xué)習(xí)(下)
TensorFlow 是一款用于數(shù)值計(jì)算的強(qiáng)大的開源軟件庫(kù)粹懒,特別適用于大規(guī)模機(jī)器學(xué)習(xí)的微調(diào)。 它的基本原理很簡(jiǎn)單:首先在 Python 中定義要執(zhí)行的計(jì)算圖(例如圖 9-1),然后 TensorFlow 使用該圖枫夺,并使用優(yōu)化的 C++ 代碼高效運(yùn)行該圖笨忌。
最重要的是稽物,Tensorflow 可以將圖分解為多個(gè)部分捷兰,并在多個(gè) CPU 或 GPU 上并行運(yùn)行(如圖 9-2 所示)农渊。 TensorFlow 還支持分布式計(jì)算泻红,因此您可以在數(shù)百臺(tái)服務(wù)器上分割計(jì)算夭禽,從而在合理的時(shí)間內(nèi)在龐大的訓(xùn)練集上訓(xùn)練龐大的神經(jīng)網(wǎng)絡(luò)(請(qǐng)參閱第 12 章)。 TensorFlow 可以訓(xùn)練一個(gè)擁有數(shù)百萬個(gè)參數(shù)的網(wǎng)絡(luò)谊路,訓(xùn)練集由數(shù)十億個(gè)具有數(shù)百萬個(gè)特征的實(shí)例組成讹躯。 這應(yīng)該不會(huì)讓人吃驚,因?yàn)?TensorFlow 是 由Google 大腦團(tuán)隊(duì)開發(fā)的缠劝,它支持谷歌的大量服務(wù)潮梯,例如 Google Cloud Speech,Google Photos 和 Google Search惨恭。
當(dāng) TensorFlow 于 2015 年 11 月開放源代碼時(shí)脱羡,已有許多深度學(xué)習(xí)的流行開源庫(kù)(表 9-1 列出了一些)萝究,公平地說,大部分 TensorFlow 的功能已經(jīng)存在于一個(gè)庫(kù)或另一個(gè)庫(kù)中锉罐。 盡管如此帆竹,TensorFlow 的整潔設(shè)計(jì),可擴(kuò)展性脓规,靈活性和出色的文檔(更不用說谷歌的名字)迅速將其推向了榜首栽连。 簡(jiǎn)而言之,TensorFlow 的設(shè)計(jì)靈活性侨舆,可擴(kuò)展性和生產(chǎn)就緒性秒紧,現(xiàn)有框架可以說只有其中三種可用。 這里有一些 TensorFlow 的亮點(diǎn):
-
它不僅在 Windows态罪,Linux 和 MacOS 上運(yùn)行噩茄,而且在移動(dòng)設(shè)備上運(yùn)行,包括 iOS 和 Android复颈。
它提供了一個(gè)非常簡(jiǎn)單的 Python API绩聘,名為 TF.Learn2(
tensorflow.con trib.learn
)沥割,與 Scikit-Learn 兼容。正如你將會(huì)看到的凿菩,你可以用幾行代碼來訓(xùn)練不同類型的神經(jīng)網(wǎng)絡(luò)机杜。之前是一個(gè)名為 Scikit Flow(或 Skow)的獨(dú)立項(xiàng)目。 它還提供了另一個(gè)簡(jiǎn)單的稱為 TF-slim(
tensorflow.contrib.slim
)的 API 來簡(jiǎn)化構(gòu)建衅谷,訓(xùn)練和求出神經(jīng)網(wǎng)絡(luò)椒拗。其他幾個(gè)高級(jí) API 已經(jīng)在 TensorFlow 之上獨(dú)立構(gòu)建,如 Keras 或 Pretty Tensor获黔。
它的主要 Python API 提供了更多的靈活性(以更高復(fù)雜度為代價(jià))來創(chuàng)建各種計(jì)算蚀苛,包括任何你能想到的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。
它包括許多 ML 操作的高效 C ++ 實(shí)現(xiàn)玷氏,特別是構(gòu)建神經(jīng)網(wǎng)絡(luò)所需的 C++ 實(shí)現(xiàn)堵未。還有一個(gè) C++ API 來定義您自己的高性能操作。
它提供了幾個(gè)高級(jí)優(yōu)化節(jié)點(diǎn)來搜索最小化損失函數(shù)的參數(shù)盏触。由于 TensorFlow 自動(dòng)處理計(jì)算您定義的函數(shù)的梯度渗蟹,因此這些非常易于使用。這稱為自動(dòng)分解(或
autodiff
)赞辩。它還附帶一個(gè)名為 TensorBoard 的強(qiáng)大可視化工具雌芽,可讓您瀏覽計(jì)算圖表,查看學(xué)習(xí)曲線等辨嗽。
Google 還推出了云服務(wù)來運(yùn)行 TensorFlow 圖(https://cloud.google.com/ml-engine/)世落。
最后,它擁有一支充滿熱情和樂于助人的開發(fā)團(tuán)隊(duì)召庞,以及一個(gè)不斷成長(zhǎng)的社區(qū)岛心,致力于改善它。它是 GitHub 上最受歡迎的開源項(xiàng)目之一篮灼,并且越來越多的優(yōu)秀項(xiàng)目正在基于TF構(gòu)建(例如忘古,查看 https://www.tensorflow.org/ 或 https://github.com/jtoy/awesome-tensorflow)。 要問技術(shù)問題诅诱,您應(yīng)該使用 http://stackoverflow.com/ 并用
tensorflow
標(biāo)記您的問題髓堪。您可以通過 GitHub 提交錯(cuò)誤和功能請(qǐng)求。有關(guān)一般討論娘荡,請(qǐng)加入 Google 小組(https://groups.google.com/a/tensorflow.org/forum/#!forum/discuss)干旁。
在本章中,我們將介紹 TensorFlow 的基礎(chǔ)知識(shí)炮沐,從安裝到創(chuàng)建争群,運(yùn)行,保存和可視化簡(jiǎn)單的計(jì)算圖大年。 在構(gòu)建第一個(gè)神經(jīng)網(wǎng)絡(luò)之前掌握這些基礎(chǔ)知識(shí)很重要(我們將在下一章中介紹)换薄。
安裝
讓我們開始吧玉雾!假設(shè)您按照第 2 章中的安裝說明安裝了 Jupyter 和 Scikit-Learn,您可以簡(jiǎn)單地使用pip
來安裝 TensorFlow轻要。 如果你使用virtualenv
創(chuàng)建了一個(gè)獨(dú)立的環(huán)境复旬,你首先需要激活它:
<pre>
$ cd $ML_PATH # Your ML working directory (e.g., $HOME/ml)
$ source env/bin/activate
</pre>
下一步,安裝 Tensorflow冲泥。
$ pip3 install --upgrade tensorflow
對(duì)于 GPU 支持驹碍,你需要安裝tensorflow-gpu
而不是tensorflow
。具體請(qǐng)參見 12 章內(nèi)容凡恍。
為了測(cè)試您的安裝志秃,請(qǐng)輸入一下命令。其輸出應(yīng)該是您安裝的 Tensorflow 的版本號(hào)咳焚。
$ python -c 'import tensorflow; print(tensorflow.__version__)'
1.0.0
創(chuàng)造第一個(gè)圖譜洽损,然后運(yùn)行它
import tensorflow as tf
x = tf.Variable(3, name="x")
y = tf.Variable(4, name="y")
f = x*x*y + y + 2
這就是它的一切庞溜! 最重要的是要知道這個(gè)代碼實(shí)際上并不執(zhí)行任何計(jì)算革半,即使它看起來像(尤其是最后一行)。 它只是創(chuàng)建一個(gè)計(jì)算圖譜流码。 事實(shí)上又官,變量都沒有初始化。要畫出此圖漫试,你需要打開一個(gè) TensorFlow 會(huì)話并使用它初始化變量并求出f
六敬。TensorFlow 會(huì)話負(fù)責(zé)處理在諸如 CPU 和 GPU 之類的設(shè)備上的操作并運(yùn)行它們,并且它保留所有變量值驾荣。以下代碼創(chuàng)建一個(gè)會(huì)話外构,初始化變量,并求出f
播掷,然后關(guān)閉會(huì)話(釋放資源):
# way1
sess = tf.Session()
sess.run(x.initializer)
sess.run(y.initializer)
result = sess.run(f)
print(result)
sess.close()
不得不每次重復(fù)sess.run() 有點(diǎn)麻煩审编,但幸運(yùn)的是有一個(gè)更好的方法:
# way2
with tf.Session() as sess:
x.initializer.run()
y.initializer.run()
result = f.eval()
print(result)
在with
塊中,會(huì)話被設(shè)置為默認(rèn)會(huì)話歧匈。 調(diào)用x.initializer.run()
等效于調(diào)用tf.get_default_session().run(x.initial)
垒酬,f.eval()
等效于調(diào)用tf.get_default_session().run(f)
。 這使得代碼更容易閱讀件炉。 此外勘究,會(huì)話在塊的末尾自動(dòng)關(guān)閉。
你可以使用global_variables_initializer()
函數(shù)斟冕,而不是手動(dòng)初始化每個(gè)變量口糕。 請(qǐng)注意,它實(shí)際上沒有立即執(zhí)行初始化磕蛇,而是在圖譜中創(chuàng)建一個(gè)當(dāng)程序運(yùn)行時(shí)所有變量都會(huì)初始化的節(jié)點(diǎn):
# way3
init = tf.global_variables_initializer() # prepare an init node
with tf.Session() as sess:
init.run() # actually initialize all the variables
result = f.eval()
在 Jupyter 內(nèi)部或在 Python shell 中景描,您可能更喜歡創(chuàng)建一個(gè)InteractiveSession
券时。 與常規(guī)會(huì)話的唯一區(qū)別是,當(dāng)創(chuàng)建InteractiveSession
時(shí)伏伯,它將自動(dòng)將其自身設(shè)置為默認(rèn)會(huì)話橘洞,因此您不需要使用模塊(但是您需要在完成后手動(dòng)關(guān)閉會(huì)話):
# way4
init = tf.global_variables_initializer()
sess = tf.InteractiveSession()
init.run()
result = f.eval()
print(result)
sess.close()
TensorFlow 程序通常分為兩部分:第一部分構(gòu)建計(jì)算圖譜(這稱為構(gòu)造階段),第二部分運(yùn)行它(這是執(zhí)行階段)说搅。 構(gòu)建階段通常構(gòu)建一個(gè)表示 ML 模型的計(jì)算圖譜,然后對(duì)其進(jìn)行訓(xùn)練,計(jì)算炸枣。 執(zhí)行階段通常運(yùn)行循環(huán),重復(fù)地求出訓(xùn)練步驟(例如弄唧,每個(gè)小批次)适肠,逐漸改進(jìn)模型參數(shù)。
管理圖譜
您創(chuàng)建的任何節(jié)點(diǎn)都會(huì)自動(dòng)添加到默認(rèn)圖形中:
>>> x1 = tf.Variable(1)
>>> x1.graph is tf.get_default_graph()
True
在大多數(shù)情況下候引,這是很好的侯养,但有時(shí)您可能需要管理多個(gè)獨(dú)立圖形。 您可以通過創(chuàng)建一個(gè)新的圖形并暫時(shí)將其設(shè)置為一個(gè)塊中的默認(rèn)圖形澄干,如下所示:
>>> graph = tf.Graph()
>>> with graph.as_default():
... x2 = tf.Variable(2)
...
>>> x2.graph is graph
True
>>> x2.graph is tf.get_default_graph()
False
提示
在 Jupyter(或 Python shell)中逛揩,通常在實(shí)驗(yàn)時(shí)多次運(yùn)行相同的命令。 因此麸俘,您可能會(huì)收到包含許多重復(fù)節(jié)點(diǎn)的默認(rèn)圖形辩稽。 一個(gè)解決方案是重新啟動(dòng) Jupyter 內(nèi)核(或 Python shell),但是一個(gè)更方便的解決方案是通過運(yùn)行tf.reset_default_graph()
來重置默認(rèn)圖从媚。
節(jié)點(diǎn)值的生命周期
求出節(jié)點(diǎn)時(shí)逞泄,TensorFlow 會(huì)自動(dòng)確定所依賴的節(jié)點(diǎn)集,并首先求出這些節(jié)點(diǎn)拜效。 例如喷众,考慮以下代碼:
w = tf.constant(3)
x = w + 2
y = x + 5
z = x * 3
with tf.Session() as sess:
print(y.eval()) # 10
print(z.eval()) # 15
首先,這個(gè)代碼定義了一個(gè)非常簡(jiǎn)單的圖紧憾。然后到千,它啟動(dòng)一個(gè)會(huì)話并運(yùn)行圖來求出y
:TensorFlow 自動(dòng)檢測(cè)到y
取決于x
,它取決于w
稻励,所以它首先求出w
父阻,然后x
,然后y
望抽,并返回y
的值加矛。最后,代碼運(yùn)行圖來求出z
煤篙。同樣斟览,TensorFlow 檢測(cè)到它必須首先求出w
和x
。重要的是要注意辑奈,它不會(huì)復(fù)用以前的w和x的求出結(jié)果苛茂。簡(jiǎn)而言之已烤,前面的代碼求出w
和x
兩次。
所有節(jié)點(diǎn)值都在圖運(yùn)行之間刪除妓羊,除了變量值胯究,由會(huì)話跨圖形運(yùn)行維護(hù)(隊(duì)列和讀者也保持一些狀態(tài))。變量在其初始化程序運(yùn)行時(shí)啟動(dòng)其生命周期躁绸,并且在會(huì)話關(guān)閉時(shí)結(jié)束裕循。
如果要有效地求出y
和z
,而不像之前的代碼那樣求出w
和x
兩次净刮,那么您必須要求 TensorFlow 在一個(gè)圖形運(yùn)行中求出y
和z
剥哑,如下面的代碼所示:
with tf.Session() as sess:
y_val, z_val = sess.run([y, z])
print(y_val) # 10
print(z_val) # 15
在單進(jìn)程 TensorFlow 中,多個(gè)會(huì)話不共享任何狀態(tài)淹父,即使它們復(fù)用同一個(gè)圖(每個(gè)會(huì)話都有自己的每個(gè)變量的副本)株婴。 在分布式 TensorFlow 中,變量狀態(tài)存儲(chǔ)在服務(wù)器上暑认,而不是在會(huì)話中困介,因此多個(gè)會(huì)話可以共享相同的變量。
用TensorFlow做線性回歸
TensorFlow 操作(也簡(jiǎn)稱為 ops)可以采用任意數(shù)量的輸入并產(chǎn)生任意數(shù)量的輸出穷吮。 例如逻翁,加法運(yùn)算和乘法運(yùn)算都需要兩個(gè)輸入并產(chǎn)生一個(gè)輸出。 常量和變量不輸入(它們被稱為源操作)捡鱼。 輸入和輸出是稱為張量的多維數(shù)組(因此稱為“tensor flow”)。 就像 NumPy 數(shù)組一樣酷愧,張量具有類型和形狀驾诈。 實(shí)際上,在 Python API 中溶浴,張量簡(jiǎn)單地由 NumPyndarray
表示乍迄。 它們通常包含浮點(diǎn)數(shù)士败,但您也可以使用它們來傳送字符串(任意字節(jié)數(shù)組)闯两。
迄今為止的示例,張量只包含單個(gè)標(biāo)量值谅将,但是當(dāng)然可以對(duì)任何形狀的數(shù)組執(zhí)行計(jì)算漾狼。例如,以下代碼操作二維數(shù)組來對(duì)加利福尼亞房屋數(shù)據(jù)集進(jìn)行線性回歸(在第 2 章中介紹)饥臂。它從獲取數(shù)據(jù)集開始逊躁;之后它會(huì)向所有訓(xùn)練實(shí)例添加一個(gè)額外的偏置輸入特征(x0 = 1
)(它使用 NumPy 進(jìn)行,因此立即運(yùn)行)隅熙;之后它創(chuàng)建兩個(gè) TensorFlow 常量節(jié)點(diǎn)X
和y
來保存該數(shù)據(jù)和目標(biāo)稽煤,并且它使用 TensorFlow 提供的一些矩陣運(yùn)算來定義theta
核芽。這些矩陣函數(shù)transpose()
,matmul()
和matrix_inverse()
是不言自明的酵熙,但是像往常一樣轧简,它們不會(huì)立即執(zhí)行任何計(jì)算;相反匾二,它們會(huì)在圖形中創(chuàng)建在運(yùn)行圖形時(shí)執(zhí)行它們的節(jié)點(diǎn)吉懊。您可以認(rèn)識(shí)到θ
的定義對(duì)應(yīng)于方程 。
最后假勿,代碼創(chuàng)建一個(gè)session
并使用它來求出theta
借嗽。
import numpy as np
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
m, n = housing.data.shape
#np.c_按colunm來組合array
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
X = tf.constant(housing_data_plus_bias, dtype=tf.float32, name="X")
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")
XT = tf.transpose(X)
theta = tf.matmul(tf.matmul(tf.matrix_inverse(tf.matmul(XT, X)), XT), y)
with tf.Session() as sess:
theta_value = theta.eval()
print(theta_value)
如果你有一個(gè) GPU 的話,上述代碼相較于直接使用 NumPy 計(jì)算正態(tài)方程式的主要優(yōu)點(diǎn)是 TensorFlow 會(huì)自動(dòng)運(yùn)行在你的 GPU 上(如果安裝了支持 GPU 的 TensorFlow转培,則 TensorFlow 將自動(dòng)運(yùn)行在 GPU 上恶导,請(qǐng)參閱第 12 章了解更多詳細(xì)信息)。
實(shí)現(xiàn)梯度下降
讓我們嘗試使用批量梯度下降(在第 4 章中介紹)浸须,而不是正態(tài)方程惨寿。 首先,我們將通過手動(dòng)計(jì)算梯度來實(shí)現(xiàn)删窒,然后我們將使用 TensorFlow 的自動(dòng)擴(kuò)展功能來使 TensorFlow 自動(dòng)計(jì)算梯度裂垦,最后我們將使用幾個(gè) TensorFlow 的優(yōu)化器。
警告
當(dāng)使用梯度下降時(shí)肌索,請(qǐng)記住蕉拢,首先要對(duì)輸入特征向量進(jìn)行歸一化,否則訓(xùn)練可能要慢得多诚亚。 您可以使用 TensorFlow晕换,NumPy,Scikit-Learn 的StandardScaler
或您喜歡的任何其他解決方案站宗。 以下代碼假定此規(guī)范化已經(jīng)完成闸准。
手動(dòng)計(jì)算梯度
以下代碼清晰易懂,除了幾個(gè)新元素:
-
random_uniform()
函數(shù)在圖形中創(chuàng)建一個(gè)節(jié)點(diǎn)梢灭,它將生成包含隨機(jī)值的張量夷家,給定其形狀和值作用域,就像 NumPy 的rand()
函數(shù)一樣敏释。 -
assign()
函數(shù)創(chuàng)建一個(gè)為變量分配新值的節(jié)點(diǎn)库快。 在這種情況下,它實(shí)現(xiàn)了批次梯度下降步驟 颂暇。 - 主循環(huán)一次又一次(共
n_epochs
次)執(zhí)行訓(xùn)練步驟缺谴,每 100 次迭代都打印出當(dāng)前均方誤差(MSE)。 你應(yīng)該看到 MSE 在每次迭代中都會(huì)下降。
housing = fetch_california_housing()
m, n = housing.data.shape
m, n = housing.data.shape
#np.c_按colunm來組合array
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
scaled_housing_data_plus_bias = scale(housing_data_plus_bias)
n_epochs = 1000
learning_rate = 0.01
X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name="X")
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
gradients = 2/m * tf.matmul(tf.transpose(X), error)
training_op = tf.assign(theta, theta - learning_rate * gradients)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
if epoch % 100 == 0:
print("Epoch", epoch, "MSE =", mse.eval())
sess.run(training_op)
best_theta = theta.eval()
Using autodi?
前面的代碼工作正常湿蛔,但它需要從代價(jià)函數(shù)(MSE)中利用數(shù)學(xué)公式推導(dǎo)梯度膀曾。 在線性回歸的情況下,這是相當(dāng)容易的阳啥,但是如果你必須用深層神經(jīng)網(wǎng)絡(luò)來做這個(gè)事情添谊,你會(huì)感到頭痛:這將是乏味和容易出錯(cuò)的。 您可以使用符號(hào)求導(dǎo)來為您自動(dòng)找到偏導(dǎo)數(shù)的方程式察迟,但結(jié)果代碼不一定非常有效斩狱。
為了理解為什么,考慮函數(shù)f(x) = exp(exp(exp(x)))
扎瓶。如果你知道微積分所踊,你可以計(jì)算出它的導(dǎo)數(shù)f'(x) = exp(x) * exp(exp(x)) * exp(exp(exp(x)))
。如果您按照普通的計(jì)算方式分別去寫f(x)
和f'(x)
概荷,您的代碼將不會(huì)如此有效秕岛。 一個(gè)更有效的解決方案是寫一個(gè)首先計(jì)算exp(x)
,然后exp(exp(x))
误证,然后exp(exp(exp(x)))
的函數(shù)继薛,并返回所有三個(gè)。這直接給你(第三項(xiàng))f(x)
愈捅,如果你需要求導(dǎo)遏考,你可以把這三個(gè)子式相乘理盆,你就完成了包个。 通過傳統(tǒng)的方法,您不得不將exp
函數(shù)調(diào)用 9 次來計(jì)算f(x)
和f'(x)
何什。 使用這種方法像棘,你只需要調(diào)用它三次稽亏。
當(dāng)您的功能由某些任意代碼定義時(shí)缕题,它會(huì)變得更糟。 你能找到方程(或代碼)來計(jì)算以下函數(shù)的偏導(dǎo)數(shù)嗎胖腾?提示:不要嘗試烟零。
def my_func(a, b):
z = 0
for i in range(100):
z = a * np.cos(z + i) + z * np.sin(b - i)
return z
幸運(yùn)的是,TensorFlow 的自動(dòng)計(jì)算梯度功能可以計(jì)算這個(gè)公式:它可以自動(dòng)高效地為您計(jì)算梯度咸作。 只需用以下面這行代碼替換上一節(jié)中代碼的gradients = ...
行锨阿,代碼將繼續(xù)正常工作:
gradients = tf.gradients(mse, [theta])[0]
gradients()
函數(shù)使用一個(gè)op
(在這種情況下是MSE)和一個(gè)變量列表(在這種情況下只是theta
),它創(chuàng)建一個(gè)ops
列表(每個(gè)變量一個(gè))來計(jì)算op
的梯度變量记罚。 因此墅诡,梯度節(jié)點(diǎn)將計(jì)算 MSE 相對(duì)于theta
的梯度向量。
自動(dòng)計(jì)算梯度有四種主要方法桐智。 它們總結(jié)在表 9-2 中末早。 TensorFlow 使用反向模式烟馅,這是完美的(高效和準(zhǔn)確),當(dāng)有很多輸入和少量的輸出然磷,如通常在神經(jīng)網(wǎng)絡(luò)的情況郑趁。 它只需要通過 次圖遍歷即可計(jì)算所有輸出的偏導(dǎo)數(shù)。
使用優(yōu)化器
所以還是要用 TensorFlow 自動(dòng)計(jì)算梯度姿搜。 但它還有更好的方法:它還提供了一些可以直接使用的優(yōu)化器寡润,包括梯度下降優(yōu)化器。您可以使用以下代碼簡(jiǎn)單地替換以前的gradients = ...
和training_op = ...
行舅柜,并且一切都將正常工作:
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
如果要使用其他類型的優(yōu)化器梭纹,則只需要更改一行。 例如致份,您可以通過定義優(yōu)化器來使用動(dòng)量?jī)?yōu)化器(通常會(huì)比漸變收斂的收斂速度快得多变抽;參見第 11 章)
optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9)
將數(shù)據(jù)提供給訓(xùn)練算法
我們嘗試修改以前的代碼來實(shí)現(xiàn)小批量梯度下降(Mini-batch Gradient Descent)。 為此知举,我們需要一種在每次迭代時(shí)用下一個(gè)小批量替換X
和Y
的方法瞬沦。 最簡(jiǎn)單的方法是使用占位符(placeholder)節(jié)點(diǎn)。 這些節(jié)點(diǎn)是特別的雇锡,因?yàn)樗鼈儗?shí)際上并不執(zhí)行任何計(jì)算逛钻,只是輸出運(yùn)行時(shí)的數(shù)據(jù)。 它們通常用于在訓(xùn)練期間將訓(xùn)練數(shù)據(jù)傳遞給 TensorFlow锰提。 如果在運(yùn)行時(shí)沒有為占位符指定值曙痘,則會(huì)收到異常。
要?jiǎng)?chuàng)建占位符節(jié)點(diǎn)立肘,您必須調(diào)用placeholder()
函數(shù)并指定輸出張量的數(shù)據(jù)類型边坤。 或者,您還可以指定其形狀谅年,如果要強(qiáng)制執(zhí)行茧痒。 如果指定維度為None
,則表示“任何大小”融蹂。例如旺订,以下代碼創(chuàng)建一個(gè)占位符節(jié)點(diǎn)A
,還有一個(gè)節(jié)點(diǎn)B = A + 5
超燃。當(dāng)我們求出B
時(shí)区拳,我們將一個(gè)feed_dict
傳遞給eval()
方法并指定A
的值。注意意乓,A
必須具有 2 級(jí)(即它必須是二維的)樱调,并且必須有三列(否則引發(fā)異常),但它可以有任意數(shù)量的行。
>>> A = tf.placeholder(tf.float32, shape=(None, 3))
>>> B = A + 5
>>> with tf.Session() as sess:
... B_val_1 = B.eval(feed_dict={A: [[1, 2, 3]]})
... B_val_2 = B.eval(feed_dict={A: [[4, 5, 6], [7, 8, 9]]})
...
>>> print(B_val_1)
[[ 6. 7. 8.]]
>>> print(B_val_2)
[[ 9. 10. 11.]
[ 12. 13. 14.]]
您實(shí)際上可以提供任何操作的輸出笆凌,而不僅僅是占位符圣猎。 在這種情況下,TensorFlow 不會(huì)嘗試求出這些操作菩颖;它使用你提供的值样漆。
要實(shí)現(xiàn)小批量漸變下降,我們只需稍微調(diào)整現(xiàn)有的代碼晦闰。 首先更改X
和Y
的定義放祟,使其定義為占位符節(jié)點(diǎn):
X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
然后定義批量大小并計(jì)算總批次數(shù):
batch_size = 100
n_batches = int(np.ceil(m / batch_size))
最后,在執(zhí)行階段呻右,逐個(gè)獲取小批量跪妥,然后在求出依賴于X
和y
的值的任何一個(gè)節(jié)點(diǎn)時(shí),通過feed_dict
提供X
和y
的值声滥。
def fetch_batch(epoch, batch_index, batch_size):
[...] # load the data from disk
return X_batch, y_batch
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
best_theta = theta.eval()
在求出theta時(shí)眉撵,我們不需要傳遞X和y的值,因?yàn)樗灰蕾囉谒鼈儭?/p>
MINI-BATCH 完整代碼
import numpy as np
from sklearn.datasets import fetch_california_housing
import tensorflow as tf
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()
m, n = housing.data.shape
print("數(shù)據(jù)集:{}行,{}列".format(m,n))
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
scaler = StandardScaler()
scaled_housing_data = scaler.fit_transform(housing.data)
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]
n_epochs = 1000
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
init = tf.global_variables_initializer()
n_epochs = 10
batch_size = 100
n_batches = int(np.ceil(m / batch_size)) # ceil() 方法返回 x 的值上限 - 不小于 x 的最小整數(shù)落塑。
def fetch_batch(epoch, batch_index, batch_size):
know = np.random.seed(epoch * n_batches + batch_index) # not shown in the book
print("我是know:",know)
indices = np.random.randint(m, size=batch_size) # not shown
X_batch = scaled_housing_data_plus_bias[indices] # not shown
y_batch = housing.target.reshape(-1, 1)[indices] # not shown
return X_batch, y_batch
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
best_theta = theta.eval()
print(best_theta)
保存和恢復(fù)模型
一旦你訓(xùn)練了你的模型纽疟,你應(yīng)該把它的參數(shù)保存到磁盤,所以你可以隨時(shí)隨地回到它憾赁,在另一個(gè)程序中使用它污朽,與其他模型比較,等等龙考。 此外蟆肆,你可能希望在訓(xùn)練期間定期保存檢查點(diǎn),以便如果你的計(jì)算機(jī)在訓(xùn)練過程中崩潰晦款,你可以從上次檢查點(diǎn)繼續(xù)進(jìn)行炎功,而不是從頭開始。
TensorFlow 可以輕松保存和恢復(fù)模型缓溅。 只需在構(gòu)造階段結(jié)束(創(chuàng)建所有變量節(jié)點(diǎn)之后)創(chuàng)建一個(gè)保存節(jié)點(diǎn); 那么在執(zhí)行階段蛇损,只要你想保存模型,只要調(diào)用它的save()
方法:
[...]
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name="theta")
[...]
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
if epoch % 100 == 0: # checkpoint every 100 epochs
save_path = saver.save(sess, "/tmp/my_model.ckpt")
sess.run(training_op)
best_theta = theta.eval()
save_path = saver.save(sess, "/tmp/my_model_final.ckpt")
恢復(fù)模型同樣容易:在構(gòu)建階段結(jié)束時(shí)創(chuàng)建一個(gè)保存器坛怪,就像之前一樣州藕,但是在執(zhí)行階段的開始,不是使用init
節(jié)點(diǎn)初始化變量酝陈,你可以調(diào)用restore()
方法 的保存器對(duì)象:
with tf.Session() as sess:
saver.restore(sess, "/tmp/my_model_final.ckpt")
[...]
默認(rèn)情況下,保存器將以自己的名稱保存并還原所有變量毁涉,但如果需要更多控制沉帮,則可以指定要保存或還原的變量以及要使用的名稱。 例如,以下保存器將僅保存或恢復(fù)theta
變量穆壕,它的鍵名稱是weights
:
saver = tf.train.Saver({"weights": theta})
完整代碼
numpy as np
from sklearn.datasets import fetch_california_housing
import tensorflow as tf
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()
m, n = housing.data.shape
print("數(shù)據(jù)集:{}行,{}列".format(m,n))
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
scaler = StandardScaler()
scaled_housing_data = scaler.fit_transform(housing.data)
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]
n_epochs = 1000 # not shown in the book
learning_rate = 0.01 # not shown
X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name="X") # not shown
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y") # not shown
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions") # not shown
error = y_pred - y # not shown
mse = tf.reduce_mean(tf.square(error), name="mse") # not shown
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate) # not shown
training_op = optimizer.minimize(mse) # not shown
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
if epoch % 100 == 0:
print("Epoch", epoch, "MSE =", mse.eval()) # not shown
save_path = saver.save(sess, "/tmp/my_model.ckpt")
sess.run(training_op)
best_theta = theta.eval()
save_path = saver.save(sess, "/tmp/my_model_final.ckpt") #找到tmp文件夾就找到文件了
使用 TensorBoard 展現(xiàn)圖形和訓(xùn)練曲線
所以現(xiàn)在我們有一個(gè)使用小批量梯度下降訓(xùn)練線性回歸模型的計(jì)算圖譜待牵,我們正在定期保存檢查點(diǎn)。 聽起來很復(fù)雜喇勋,不是嗎缨该? 然而,我們?nèi)匀灰揽?code>print()函數(shù)可視化訓(xùn)練過程中的進(jìn)度川背。 有一個(gè)更好的方法:進(jìn)入 TensorBoard贰拿。如果您提供一些訓(xùn)練統(tǒng)計(jì)信息,它將在您的網(wǎng)絡(luò)瀏覽器中顯示這些統(tǒng)計(jì)信息的良好交互式可視化(例如學(xué)習(xí)曲線)熄云。 您還可以提供圖形的定義膨更,它將為您提供一個(gè)很好的界面來瀏覽它。 這對(duì)于識(shí)別圖中的錯(cuò)誤缴允,找到瓶頸等是非常有用的荚守。
第一步是調(diào)整程序,以便將圖形定義和一些訓(xùn)練統(tǒng)計(jì)信息(例如练般,training_error
(MSE))寫入 TensorBoard 將讀取的日志目錄矗漾。 您每次運(yùn)行程序時(shí)都需要使用不同的日志目錄,否則 TensorBoard 將會(huì)合并來自不同運(yùn)行的統(tǒng)計(jì)信息薄料,這將會(huì)使可視化混亂敞贡。 最簡(jiǎn)單的解決方案是在日志目錄名稱中包含時(shí)間戳。 在程序開頭添加以下代碼:
from datetime import datetime
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = "tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)
接下來都办,在構(gòu)建階段結(jié)束時(shí)添加以下代碼:
mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
第一行創(chuàng)建一個(gè)節(jié)點(diǎn)嫡锌,這個(gè)節(jié)點(diǎn)將求出 MSE 值并將其寫入 TensorBoard 兼容的二進(jìn)制日志字符串(稱為摘要)中。 第二行創(chuàng)建一個(gè)FileWriter
琳钉,你將用它來將摘要寫入日志目錄中的日志文件中势木。 第一個(gè)參數(shù)指示日志目錄的路徑(在本例中為tf_logs/run-20160906091959/
,相對(duì)于當(dāng)前目錄)歌懒。 第二個(gè)(可選)參數(shù)是你想要可視化的圖形啦桌。 創(chuàng)建時(shí),文件寫入器創(chuàng)建日志目錄(如果需要)及皂,并將其定義在二進(jìn)制日志文件(稱為事件文件)中甫男。
接下來,您需要更新執(zhí)行階段验烧,以便在訓(xùn)練期間定期求出mse_summary
節(jié)點(diǎn)(例如板驳,每 10 個(gè)小批量)。 這將輸出一個(gè)摘要碍拆,然后可以使用file_writer
寫入事件文件若治。 以下是更新的代碼:
[...]
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
if batch_index % 10 == 0:
summary_str = mse_summary.eval(feed_dict={X: X_batch, y: y_batch})
step = epoch * n_batches + batch_index
file_writer.add_summary(summary_str, step)
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
[...]
警告
避免在每一個(gè)訓(xùn)練階段記錄訓(xùn)練數(shù)據(jù)慨蓝,因?yàn)檫@會(huì)大大減慢訓(xùn)練速度(以上代碼每 10 個(gè)小批量記錄一次).
最后,要在程序結(jié)束時(shí)關(guān)閉FileWriter
:
file_writer.close()
完整代碼
import numpy as np
from sklearn.datasets import fetch_california_housing
import tensorflow as tf
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()
m, n = housing.data.shape
print("數(shù)據(jù)集:{}行,{}列".format(m,n))
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
scaler = StandardScaler()
scaled_housing_data = scaler.fit_transform(housing.data)
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]
from datetime import datetime
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = r"D://tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)
n_epochs = 1000
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
init = tf.global_variables_initializer()
mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
n_epochs = 10
batch_size = 100
n_batches = int(np.ceil(m / batch_size))
def fetch_batch(epoch, batch_index, batch_size):
np.random.seed(epoch * n_batches + batch_index) # not shown in the book
indices = np.random.randint(m, size=batch_size) # not shown
X_batch = scaled_housing_data_plus_bias[indices] # not shown
y_batch = housing.target.reshape(-1, 1)[indices] # not shown
return X_batch, y_batch
with tf.Session() as sess: # not shown in the book
sess.run(init) # not shown
for epoch in range(n_epochs): # not shown
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
if batch_index % 10 == 0:
summary_str = mse_summary.eval(feed_dict={X: X_batch, y: y_batch})
step = epoch * n_batches + batch_index
file_writer.add_summary(summary_str, step)
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
best_theta = theta.eval()
file_writer.close()
print(best_theta)
現(xiàn)在運(yùn)行程序:它會(huì)創(chuàng)建日志目錄端幼,將事件文件寫入其中礼烈,包括圖的定義和MSE值。打開shell婆跑,進(jìn)入工作目錄此熬,輸入ls -l tf_logs/run*
,列出日志目錄的內(nèi)容:
$ cd $ML_PATH # Your ML working directory (e.g., $HOME/ml)
$ ls -l tf_logs/run*
total 40
-rw-r--r-- 1 ageron staff 18620 Sep 6 11:10 events.out.tfevents.1472553182.mymac
如果再次運(yùn)行程序滑进,會(huì)在目錄tf_logs/
看到第二個(gè)文件夾:
$ ls -l tf_logs/
total 0
drwxr-xr-x 3 ageron staff 102 Sep 6 10:07 run-20160906091959
drwxr-xr-x 3 ageron staff 102 Sep 6 10:22 run-20160906092202
很好犀忱!現(xiàn)在啟動(dòng)TensorBoard服務(wù)。你需要激活虛擬環(huán)境郊供,然后輸入tensorboard
命令峡碉,將其只想根日志目錄,啟動(dòng)服務(wù)驮审。這樣就能啟動(dòng)TensorBoard鲫寄,監(jiān)聽端口是6006(“goog”字母的倒立):
$ source env/bin/activate
$ tensorboard --logdir tf_logs/
Starting TensorBoard on port 6006
(You can navigate to http://0.0.0.0:6006)
然后打開瀏覽器訪問http://0.0.0.0:6006/(或http://localhost:6006/),就進(jìn)入了TensorBoard疯淫。在Events欄地来,可以在右邊看到MSE。如果點(diǎn)擊MSE熙掺,就可以看到兩次訓(xùn)練過程中MSE的圖形(見圖9-3)未斑。可以勾選曲線币绩、放大縮小蜡秽、懸停獲得更多信息。
點(diǎn)擊Graphs欄缆镣,可以看到圖9-4芽突。
為了避免圖聚集在一起,有多個(gè)邊(edge董瞻,即與其它節(jié)點(diǎn)相連)被獨(dú)立放到了右邊的輔助區(qū)域(右鍵點(diǎn)擊可以在主圖和輔助區(qū)域變換)寞蚌。圖的一些部分也折疊了起來。例如钠糊,鼠標(biāo)在gradients節(jié)點(diǎn)懸停挟秤,然后點(diǎn)擊⊕就可以展開子圖。再在子圖中抄伍,展開mse_grad subgraph
艘刚。
提示
如果你想在Jupyter中直接查看圖,可以在本章的notebook中使用show_graph()
函數(shù)截珍。這是A. Mordvintsev在他的deepdream tutorial notebook開發(fā)的昔脯。另一種方法是安裝E. Jang的TensorFlow debugger tool啄糙,它包括一個(gè)圖像可視化的Jupyter插件。
命名作用域
當(dāng)處理更復(fù)雜的模型(如神經(jīng)網(wǎng)絡(luò))時(shí)云稚,計(jì)算圖極易與數(shù)千個(gè)節(jié)點(diǎn)混淆。 為了避免這種情況沈堡,您可以創(chuàng)建命名作用域來對(duì)相關(guān)節(jié)點(diǎn)進(jìn)行分組静陈。 例如,我們修改以前的代碼來定義名為loss
的命名作用域內(nèi)的錯(cuò)誤和mse
操作:
with tf.name_scope("loss") as scope:
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
在作用域內(nèi)定義的每個(gè)op
的名稱現(xiàn)在以loss/
為前綴:
>>> print(error.op.name)
loss/sub
>>> print(mse.op.name)
loss/mse
在 TensorBoard 中诞丽,mse
和error
節(jié)點(diǎn)現(xiàn)在出現(xiàn)在loss
命名空間中鲸拥,默認(rèn)情況下會(huì)出現(xiàn)崩潰(圖 9-5)。
完整代碼
import numpy as np
from sklearn.datasets import fetch_california_housing
import tensorflow as tf
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()
m, n = housing.data.shape
print("數(shù)據(jù)集:{}行,{}列".format(m,n))
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
scaler = StandardScaler()
scaled_housing_data = scaler.fit_transform(housing.data)
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]
from datetime import datetime
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = r"D://tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)
n_epochs = 1000
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
def fetch_batch(epoch, batch_index, batch_size):
np.random.seed(epoch * n_batches + batch_index) # not shown in the book
indices = np.random.randint(m, size=batch_size) # not shown
X_batch = scaled_housing_data_plus_bias[indices] # not shown
y_batch = housing.target.reshape(-1, 1)[indices] # not shown
return X_batch, y_batch
with tf.name_scope("loss") as scope:
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
init = tf.global_variables_initializer()
mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
n_epochs = 10
batch_size = 100
n_batches = int(np.ceil(m / batch_size))
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
if batch_index % 10 == 0:
summary_str = mse_summary.eval(feed_dict={X: X_batch, y: y_batch})
step = epoch * n_batches + batch_index
file_writer.add_summary(summary_str, step)
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
best_theta = theta.eval()
file_writer.flush()
file_writer.close()
print("Best theta:")
print(best_theta)
模塊性
假設(shè)您要?jiǎng)?chuàng)建一個(gè)圖僧免,它的作用是將兩個(gè)整流線性單元(ReLU)的輸出值相加刑赶。 ReLU 計(jì)算一個(gè)輸入值的對(duì)應(yīng)線性函數(shù)輸出值,如果為正懂衩,則輸出該結(jié)值撞叨,否則為 0,如等式 9-1 所示浊洞。
下面的代碼做這個(gè)工作牵敷,但是它是相當(dāng)重復(fù)的:
n_features = 3
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
w1 = tf.Variable(tf.random_normal((n_features, 1)), name="weights1")
w2 = tf.Variable(tf.random_normal((n_features, 1)), name="weights2")
b1 = tf.Variable(0.0, name="bias1")
b2 = tf.Variable(0.0, name="bias2")
z1 = tf.add(tf.matmul(X, w1), b1, name="z1")
z2 = tf.add(tf.matmul(X, w2), b2, name="z2")
relu1 = tf.maximum(z1, 0., name="relu1")
relu2 = tf.maximum(z1, 0., name="relu2")
output = tf.add(relu1, relu2, name="output")
這樣的重復(fù)代碼很難維護(hù),容易出錯(cuò)(實(shí)際上法希,這個(gè)代碼包含了一個(gè)剪貼錯(cuò)誤枷餐,你發(fā)現(xiàn)了嗎?) 如果你想添加更多的 ReLU苫亦,會(huì)變得更糟毛肋。 幸運(yùn)的是,TensorFlow 可以讓您保持 DRY(不要重復(fù)自己):只需創(chuàng)建一個(gè)功能來構(gòu)建 ReLU屋剑。 以下代碼創(chuàng)建五個(gè) ReLU 并輸出其總和(注意润匙,add_n()
創(chuàng)建一個(gè)計(jì)算張量列表之和的操作):
def relu(X):
w_shape = (int(X.get_shape()[1]), 1)
w = tf.Variable(tf.random_normal(w_shape), name="weights")
b = tf.Variable(0.0, name="bias")
z = tf.add(tf.matmul(X, w), b, name="z")
return tf.maximum(z, 0., name="relu")
n_features = 3
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = [relu(X) for i in range(5)]
output = tf.add_n(relus, name="output")
請(qǐng)注意,創(chuàng)建節(jié)點(diǎn)時(shí)饼丘,TensorFlow 將檢查其名稱是否已存在趁桃,如果它已經(jīng)存在,則會(huì)附加一個(gè)下劃線肄鸽,后跟一個(gè)索引卫病,以使該名稱是唯一的。 因此典徘,第一個(gè) ReLU 包含名為weights
蟀苛,bias
,z
和relu
的節(jié)點(diǎn)(加上其他默認(rèn)名稱的更多節(jié)點(diǎn)逮诲,如MatMul
); 第二個(gè) ReLU 包含名為weights_1
帜平,bias_1
等節(jié)點(diǎn)的節(jié)點(diǎn); 第三個(gè) ReLU 包含名為 weights_2
幽告,bias_2
的節(jié)點(diǎn),依此類推裆甩。 TensorBoard 識(shí)別這樣的組并將它們折疊在一起以減少混亂(如圖 9-6 所示)
使用名稱作用域冗锁,您可以使圖形更清晰。 簡(jiǎn)單地將relu()
函數(shù)的所有內(nèi)容移動(dòng)到名稱作用域內(nèi)嗤栓。 圖 9-7 顯示了結(jié)果圖冻河。 請(qǐng)注意,TensorFlow 還通過附加_1
茉帅,_2
等來提供名稱作用域的唯一名稱叨叙。
def relu(X):
with tf.name_scope("relu"):
w_shape = (int(X.get_shape()[1]), 1) # not shown in the book
w = tf.Variable(tf.random_normal(w_shape), name="weights") # not shown
b = tf.Variable(0.0, name="bias") # not shown
z = tf.add(tf.matmul(X, w), b, name="z") # not shown
return tf.maximum(z, 0., name="max") # not shown
共享變量
如果要在圖形的各個(gè)組件之間共享一個(gè)變量,一個(gè)簡(jiǎn)單的選項(xiàng)是首先創(chuàng)建它堪澎,然后將其作為參數(shù)傳遞給需要它的函數(shù)擂错。 例如,假設(shè)要使用所有 ReLU 的共享閾值變量來控制 ReLU 閾值(當(dāng)前硬編碼為 0)樱蛤。 您可以先創(chuàng)建該變量钮呀,然后將其傳遞給relu()
函數(shù):
reset_graph()
def relu(X, threshold):
with tf.name_scope("relu"):
w_shape = (int(X.get_shape()[1]), 1) # not shown in the book
w = tf.Variable(tf.random_normal(w_shape), name="weights") # not shown
b = tf.Variable(0.0, name="bias") # not shown
z = tf.add(tf.matmul(X, w), b, name="z") # not shown
return tf.maximum(z, threshold, name="max")
threshold = tf.Variable(0.0, name="threshold")
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = [relu(X, threshold) for i in range(5)]
output = tf.add_n(relus, name="output")
這很好:現(xiàn)在您可以使用閾值變量threshold
來控制所有 ReLU 的閾值。但是刹悴,如果有許多共享參數(shù)行楞,比如這一項(xiàng),那么必須一直將它們作為參數(shù)傳遞土匀,這將是非常痛苦的子房。許多人創(chuàng)建了一個(gè)包含模型中所有變量的 Python 字典,并將其傳遞給每個(gè)函數(shù)就轧。另一些則為每個(gè)模塊創(chuàng)建一個(gè)類(例如:一個(gè)使用類變量來處理共享參數(shù)的 ReLU 類)证杭。另一種選擇是在第一次調(diào)用時(shí)將共享變量設(shè)置為relu()
函數(shù)的屬性,如下所示:
def relu(X):
with tf.name_scope("relu"):
if not hasattr(relu, "threshold"):
relu.threshold = tf.Variable(0.0, name="threshold")
w_shape = int(X.get_shape()[1]), 1 # not shown in the book
w = tf.Variable(tf.random_normal(w_shape), name="weights") # not shown
b = tf.Variable(0.0, name="bias") # not shown
z = tf.add(tf.matmul(X, w), b, name="z") # not shown
return tf.maximum(z, relu.threshold, name="max")
TensorFlow 提供了另一個(gè)選項(xiàng)妒御,這將提供比以前的解決方案稍微更清潔和更模塊化的代碼解愤。首先要明白一點(diǎn),這個(gè)解決方案很刁鉆難懂乎莉,但是由于它在 TensorFlow 中使用了很多送讲,所以值得我們?nèi)ド钊爰?xì)節(jié)。 這個(gè)想法是使用get_variable()
函數(shù)來創(chuàng)建共享變量惋啃,如果它還不存在哼鬓,或者如果已經(jīng)存在,則復(fù)用它边灭。 所需的行為(創(chuàng)建或復(fù)用)由當(dāng)前variable_scope()
的屬性控制异希。 例如,以下代碼將創(chuàng)建一個(gè)名為relu/threshold
的變量(作為標(biāo)量绒瘦,因?yàn)?code>shape = ()称簿,并使用 0.0 作為初始值):
with tf.variable_scope("relu"):
threshold = tf.get_variable("threshold", shape=(),
initializer=tf.constant_initializer(0.0))
請(qǐng)注意扣癣,如果變量已經(jīng)通過較早的get_variable()
調(diào)用創(chuàng)建,則此代碼將引發(fā)異常憨降。 這種行為可以防止錯(cuò)誤地復(fù)用變量父虑。如果要復(fù)用變量,則需要通過將變量scope
的復(fù)用屬性設(shè)置為True
來明確說明(在這種情況下券册,您不必指定形狀或初始值):
with tf.variable_scope("relu", reuse=True):
threshold = tf.get_variable("threshold")
該代碼將獲取現(xiàn)有的relu/threshold
變量频轿,如果不存在會(huì)引發(fā)異常(如果沒有使用get_variable()
創(chuàng)建)。 或者烁焙,您可以通過調(diào)用scope
的reuse_variables()
方法將復(fù)用屬性設(shè)置為true
:
with tf.variable_scope("relu") as scope:
scope.reuse_variables()
threshold = tf.get_variable("threshold")
警告
一旦reuse
設(shè)置為True
,它將不能在塊內(nèi)設(shè)置為False
耕赘。 而且骄蝇,如果在其中定義其他變量作用域,它們將自動(dòng)繼承reuse = True
操骡。 最后九火,只有通過get_variable()
創(chuàng)建的變量才可以這樣復(fù)用.
現(xiàn)在,您擁有所有需要的部分册招,使relu()
函數(shù)訪問閾值變量岔激,而不必將其作為參數(shù)傳遞:
def relu(X):
with tf.variable_scope("relu", reuse=True):
threshold = tf.get_variable("threshold")
w_shape = int(X.get_shape()[1]), 1 # not shown
w = tf.Variable(tf.random_normal(w_shape), name="weights") # not shown
b = tf.Variable(0.0, name="bias") # not shown
z = tf.add(tf.matmul(X, w), b, name="z") # not shown
return tf.maximum(z, threshold, name="max")
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
with tf.variable_scope("relu"):
threshold = tf.get_variable("threshold", shape=(),
initializer=tf.constant_initializer(0.0))
relus = [relu(X) for relu_index in range(5)]
output = tf.add_n(relus, name="output")
該代碼首先定義relu()
函數(shù),然后創(chuàng)建relu/threshold
變量(作為標(biāo)量是掰,稍后將被初始化為 0.0)虑鼎,并通過調(diào)用relu()
函數(shù)構(gòu)建五個(gè)ReLU。relu()
函數(shù)復(fù)用relu/threshold
變量键痛,并創(chuàng)建其他 ReLU 節(jié)點(diǎn)炫彩。
提示
使用get_variable()
創(chuàng)建的變量始終以其variable_scope
的名稱作為前綴命名(例如,relu/threshold
)絮短,但對(duì)于所有其他節(jié)點(diǎn)(包括使用tf.Variable()
創(chuàng)建的變量)江兢,變量作用域的行為就像一個(gè)新名稱的作用域。 特別是丁频,如果已經(jīng)創(chuàng)建了具有相同名稱的名稱作用域杉允,則添加后綴以使該名稱是唯一的。 例如席里,在前面的代碼中創(chuàng)建的所有節(jié)點(diǎn)(閾值變量除外)的名稱前綴為relu_1/
到relu_5/
叔磷,如圖 9-8 所示。
不幸的是胁勺,必須在relu()
函數(shù)之外定義閾值變量世澜,其中 ReLU 代碼的其余部分都駐留在其中。 要解決此問題署穗,以下代碼在第一次調(diào)用時(shí)在relu()
函數(shù)中創(chuàng)建閾值變量寥裂,然后在后續(xù)調(diào)用中重新使用嵌洼。 現(xiàn)在,relu()
函數(shù)不必?fù)?dān)心名稱作用域或變量共享:它只是調(diào)用get_variable()
封恰,它將創(chuàng)建或復(fù)用閾值變量(它不需要知道是哪種情況)麻养。 其余的代碼調(diào)用relu()
五次,確保在第一次調(diào)用時(shí)設(shè)置reuse = False
诺舔,而對(duì)于其他調(diào)用來說鳖昌,reuse = True
。
def relu(X):
threshold = tf.get_variable("threshold", shape=(),
initializer=tf.constant_initializer(0.0))
w_shape = (int(X.get_shape()[1]), 1) # not shown in the book
w = tf.Variable(tf.random_normal(w_shape), name="weights") # not shown
b = tf.Variable(0.0, name="bias") # not shown
z = tf.add(tf.matmul(X, w), b, name="z") # not shown
return tf.maximum(z, threshold, name="max")
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = []
for relu_index in range(5):
with tf.variable_scope("relu", reuse=(relu_index >= 1)) as scope:
relus.append(relu(X))
output = tf.add_n(relus, name="output")
生成的圖形與之前略有不同低飒,因?yàn)楣蚕碜兞看嬖谟诘谝粋€(gè) ReLU 中(見圖 9-9)许昨。
TensorFlow 的介紹到此結(jié)束。 我們將在以下章節(jié)中討論更多高級(jí)專題褥赊,特別是與深層神經(jīng)網(wǎng)絡(luò)糕档,卷積神經(jīng)網(wǎng)絡(luò)和遞歸神經(jīng)網(wǎng)絡(luò)相關(guān)的許多操作,以及如何使用多線程拌喉,隊(duì)列速那,多個(gè) GPU 以及如何將 TensorFlow 擴(kuò)展到多臺(tái)服務(wù)器。
(第一部分 機(jī)器學(xué)習(xí)基礎(chǔ))
第01章 機(jī)器學(xué)習(xí)概覽
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(上)
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(下)
第03章 分類
第04章 訓(xùn)練模型
第05章 支持向量機(jī)
第06章 決策樹
第07章 集成學(xué)習(xí)和隨機(jī)森林
第08章 降維
(第二部分 神經(jīng)網(wǎng)絡(luò)和深度學(xué)習(xí))
第9章 啟動(dòng)和運(yùn)行TensorFlow
第10章 人工神經(jīng)網(wǎng)絡(luò)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(上)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(下)
第12章 設(shè)備和服務(wù)器上的分布式 TensorFlow
第13章 卷積神經(jīng)網(wǎng)絡(luò)
第14章 循環(huán)神經(jīng)網(wǎng)絡(luò)
第15章 自編碼器
第16章 強(qiáng)化學(xué)習(xí)(上)
第16章 強(qiáng)化學(xué)習(xí)(下)