《Scikit-Learn與TensorFlow機器學習實用指南》 第11章 訓練深度神經(jīng)網(wǎng)絡(luò)(上)


(第一部分 機器學習基礎(chǔ))
第01章 機器學習概覽
第02章 一個完整的機器學習項目(上)
第02章 一個完整的機器學習項目(下)
第03章 分類
第04章 訓練模型
第05章 支持向量機
第06章 決策樹
第07章 集成學習和隨機森林
第08章 降維
(第二部分 神經(jīng)網(wǎng)絡(luò)和深度學習)
第9章 啟動和運行TensorFlow
第10章 人工神經(jīng)網(wǎng)絡(luò)
第11章 訓練深度神經(jīng)網(wǎng)絡(luò)(上)
第11章 訓練深度神經(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章 強化學習(上)
第16章 強化學習(下)


第 10 章介紹了人工神經(jīng)網(wǎng)絡(luò)檩帐,并訓練了我們的第一個深度神經(jīng)網(wǎng)絡(luò)佑惠。 但它是一個非常淺的 DNN磨确,只有兩個隱藏層识藤。 如果你需要解決非常復雜的問題窖杀,例如檢測高分辨率圖像中的數(shù)百種類型的對象言津,該怎么辦偏竟? 你可能需要訓練更深的 DNN颈走,也許有 10 層硬纤,每層包含數(shù)百個神經(jīng)元解滓,通過數(shù)十萬個連接相連。 這可不像公園散步那么簡單:

  • 首先,你將面臨棘手的梯度消失問題(或相關(guān)的梯度爆炸問題)舀武,這會影響深度神經(jīng)網(wǎng)絡(luò)冒滩,并使較低層難以訓練毕箍。
  • 其次腮鞍,對于如此龐大的網(wǎng)絡(luò)值骇,訓練將非常緩慢。
  • 第三移国,具有數(shù)百萬參數(shù)的模型將會有嚴重的過擬合訓練集的風險吱瘩。

在本章中,我們將依次討論這些問題迹缀,并提出解決問題的技巧使碾。 我們將從解釋梯度消失問題開始,并探討解決這個問題的一些最流行的解決方案祝懂。 接下來我們將看看各種優(yōu)化器票摇,與普通梯度下降相比,它們可以加速大型模型的訓練砚蓬。 最后兄朋,我們將瀏覽一些流行的大型神經(jīng)網(wǎng)絡(luò)正則化技術(shù)。

使用這些工具怜械,你將能夠訓練非常深的網(wǎng)絡(luò):歡迎來到深度學習的世界颅和!

梯度消失/爆炸問題

正如我們在第 10 章中所討論的那樣,反向傳播算法的工作原理是從輸出層到輸入層缕允,傳播誤差的梯度峡扩。 一旦該算法已經(jīng)計算了網(wǎng)絡(luò)中每個參數(shù)的損失函數(shù)的梯度,它就通過梯度下降使用這些梯度來更新每個參數(shù)障本。

不幸的是教届,隨著算法進展到較低層,梯度往往變得越來越小驾霜。 結(jié)果案训,梯度下降更新使得低層連接權(quán)重實際上保持不變,并且訓練永遠不會收斂到良好的解決方案粪糙。 這被稱為梯度消失問題强霎。 在某些情況下,可能會發(fā)生相反的情況:梯度可能變得越來越大蓉冈,許多層得到了非常大的權(quán)重更新城舞,算法發(fā)散。這是梯度爆炸的問題寞酿,在循環(huán)神經(jīng)網(wǎng)絡(luò)中最為常見(見第 14 章)家夺。 更一般地說,深度神經(jīng)網(wǎng)絡(luò)受梯度不穩(wěn)定之苦; 不同的層次可能以非常不同的學習速率伐弹。

雖然這種現(xiàn)象已經(jīng)經(jīng)過了相當長的一段時間的實驗觀察(這是造成深度神經(jīng)網(wǎng)絡(luò)大部分時間都被拋棄的原因之一)拉馋,但直到 2010 年左右,人們才有了明顯的進步。 Xavier Glorot 和 Yoshua Bengio 發(fā)表的題為《Understanding the Difficulty of Training Deep Feedforward Neural Networks》的論文發(fā)現(xiàn)了一些疑問煌茴,包括流行的 sigmoid 激活函數(shù)和當時最受歡迎的權(quán)重初始化技術(shù)的組合柠逞,即隨機初始化時使用平均值為 0,標準差為 1 的正態(tài)分布景馁。簡而言之,他們表明逗鸣,用這個激活函數(shù)和這個初始化方案合住,每層輸出的方差遠大于其輸入的方差。網(wǎng)絡(luò)正向撒璧,每層的方差持續(xù)增加透葛,直到激活函數(shù)在頂層飽和。這實際上是因為logistic函數(shù)的平均值為 0.5 而不是 0(雙曲正切函數(shù)的平均值為 0僚害,表現(xiàn)略好于深層網(wǎng)絡(luò)中的logistic函數(shù))萨蚕。

看一下logistic 激活函數(shù)(參見圖 11-1)蹄胰,可以看到當輸入變大(負或正)時,函數(shù)飽和在 0 或 1裕寨,導數(shù)非常接近 0浩蓉。因此,當反向傳播開始時宾袜, 它幾乎沒有梯度通過網(wǎng)絡(luò)傳播回來捻艳,而且由于反向傳播通過頂層向下傳遞,所以存在的小梯度不斷地被稀釋庆猫,因此較低層確實沒有任何東西可用认轨。

圖11-1 邏輯激活函數(shù)飽和

Glorot 和 Bengio 在他們的論文中提出了一種顯著緩解這個問題的方法。 我們需要信號在兩個方向上正確地流動:在進行預測時是正向的月培,在反向傳播梯度時是反向的好渠。 我們不希望信號消失,也不希望它爆炸并飽和节视。 為了使信號正確流動拳锚,作者認為,我們需要每層輸出的方差等于其輸入的方差寻行。(這里有一個比喻:如果將麥克風放大器的旋鈕設(shè)置得太接近于零霍掺,人們聽不到聲音,但是如果將麥克風放大器設(shè)置得太大,聲音就會飽和杆烁,人們就會聽不懂你在說什么牙丽。 現(xiàn)在想象一下這樣一個放大器的鏈條:它們都需要正確設(shè)置,以便在鏈條的末端響亮而清晰地發(fā)出聲音烤芦。 你的聲音必須以每個放大器的振幅相同的幅度出來。)而且我們也需要梯度在相反方向上流過一層之前和之后有相同的方差(如果您對數(shù)學細節(jié)感興趣遂唧,請查閱論文)。實際上不可能保證兩者都是一樣的召边,除非這個層具有相同數(shù)量的輸入和輸出連接,但是他們提出了一個很好的折衷辦法贱鼻,在實踐中證明這個折中辦法非常好:隨機初始化連接權(quán)重必須如公式 11-1 所描述的那樣。其中n_inputsn_outputs是權(quán)重正在被初始化的層(也稱為扇入和扇出)的輸入和輸出連接的數(shù)量。 這種初始化策略通常被稱為Xavier初始化(在作者的名字之后)蛾扇,或者有時是 Glorot 初始化。

公式11-1 Xavier初始化(使用邏輯激活函數(shù))

當輸入連接的數(shù)量大致等于輸出連接的數(shù)量時更哄,可以得到更簡單的等式

我們在第 10 章中使用了這個簡化的策略。

使用 Xavier 初始化策略可以大大加快訓練速度栅炒,這是導致深度學習目前取得成功的技巧之一。 最近的一些論文針對不同的激活函數(shù)提供了類似的策略,如表 11-1 所示。 ReLU 激活函數(shù)(及其變體扛禽,包括簡稱 ELU 激活)的初始化策略有時稱為 He 初始化(根據(jù)作者姓氏)。

表11-1 每種類型激活函數(shù)的初始化參數(shù)

默認情況下掐场,fully_connected()函數(shù)(在第 10 章中介紹)使用 Xavier 初始化(具有均勻分布)。你可以通過使用如下所示的variance_scaling_initializer()函數(shù)來將其更改為 He 初始化:

he_init = tf.contrib.layers.variance_scaling_initializer()
hidden1 = fully_connected(X, n_hidden1, weights_initializer=he_init, scope="h1")

注意:本書使用tensorflow.contrib.layers.fully_connected()而不是tf.layers.dense()(本章編寫時不存在)。 現(xiàn)在最好使用tf.layers.dense()蝌戒,因為contrib模塊中的任何內(nèi)容可能會更改或刪除,恕不另行通知粹淋。 dense()函數(shù)幾乎與fully_connected()函數(shù)完全相同屋匕。 與本章有關(guān)的主要差異是:

幾個參數(shù)被重新命名:范圍變成名字过吻,activation_fn變成激活(類似地,_fn后綴從諸如normalizer_fn之類的其他參數(shù)中移除)逼纸,weights_initializer變成kernel_initializer等等。默認激活現(xiàn)在是None贺嫂,而不是tf.nn.relu。 它不支持tensorflow.contrib.framework.arg_scope()(稍后在第 11 章中介紹)曲饱。 它不支持正則化的參數(shù)(稍后在第 11 章介紹)渔工。

he_init = tf.contrib.layers.variance_scaling_initializer()
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
                          kernel_initializer=he_init, name="hidden1")

He 初始化只考慮了扇入侵浸,而不是像 Xavier 初始化那樣扇入和扇出之間的平均值掏觉。 這也是variance_scaling_initializer()函數(shù)的默認值织盼,但您可以通過設(shè)置參數(shù)mode ="FAN_AVG"來更改它沥邻。

非飽和激活函數(shù)

Glorot 和 Bengio 在 2010 年的論文中的一個見解是埃跷,消失/爆炸的梯度問題部分是由于激活函數(shù)的選擇不好造成的。 在那之前剪勿,大多數(shù)人都認為,如果大自然選擇在生物神經(jīng)元中使用 sigmoid 激活函數(shù)赴涵,它們必定是一個很好的選擇。 但事實證明欺殿,其他激活函數(shù)在深度神經(jīng)網(wǎng)絡(luò)中表現(xiàn)得更好,特別是 ReLU 激活函數(shù)棍潘,主要是因為它對正值不會飽和(也因為它的計算速度很快)。

不幸的是肴楷,ReLU激活功能并不完美。 它有一個被稱為 “ReLU 死區(qū)” 的問題:在訓練過程中鞠值,一些神經(jīng)元有效地死亡,意味著它們停止輸出 0 以外的任何東西粤剧。在某些情況下,你可能會發(fā)現(xiàn)你網(wǎng)絡(luò)的一半神經(jīng)元已經(jīng)死亡,特別是如果你使用大學習率世囊。 在訓練期間,如果神經(jīng)元的權(quán)重得到更新,使得神經(jīng)元輸入的加權(quán)和為負贝奇,則它將開始輸出 0 。當這種情況發(fā)生時陕习,由于當輸入為負時,ReLU函數(shù)的梯度為0拌牲,神經(jīng)元不可能恢復生機。

為了解決這個問題枣购,你可能需要使用 ReLU 函數(shù)的一個變體,比如 leaky ReLU分瘾。這個函數(shù)定義為LeakyReLUα(z)= max(αz,z)(見圖 11-2)上岗。超參數(shù)α定義了函數(shù)“l(fā)eaks”的程度:它是z < 0時函數(shù)的斜率蕴坪,通常設(shè)置為 0.01肴掷。這個小斜坡確保 leaky ReLU 永不死亡;他們可能會長期昏迷辞嗡,但他們有機會最終醒來捆等。最近的一篇論文比較了幾種 ReLU 激活功能的變體滞造,其中一個結(jié)論是 leaky Relu 總是優(yōu)于嚴格的 ReLU 激活函數(shù)续室。事實上丰泊,設(shè)定α= 0.2(巨大 leak)似乎導致比α= 0.01(小 leak)更好的性能盏浇。他們還評估了隨機化 leaky ReLU(RReLU),其中α在訓練期間在給定范圍內(nèi)隨機挑選,并在測試期間固定為平均值盟榴。它表現(xiàn)相當好姨夹,似乎是一個正則項(減少訓練集的過擬合風險)绰咽。最后,他們還評估了參數(shù) leaky ReLU(PReLU)陈哑,其中α被授權(quán)在訓練期間被學習(而不是超參數(shù),它變成可以像任何其他參數(shù)一樣被反向傳播修改的參數(shù))。據(jù)報道這在大型圖像數(shù)據(jù)集上的表現(xiàn)強于 ReLU,但是對于較小的數(shù)據(jù)集规脸,其具有過度擬合訓練集的風險堕花。

圖11-2 Leaky ReLU 帶泄露整流函數(shù)

最后伴榔,Djork-Arné Clevert 等人在 2015 年的一篇論文中提出了一種稱為指數(shù)線性單元(exponential linear unit,ELU)的新的激活函數(shù)秤掌,在他們的實驗中表現(xiàn)優(yōu)于所有的 ReLU 變體:訓練時間減少,神經(jīng)網(wǎng)絡(luò)在測試集上表現(xiàn)的更好虹蓄。 如圖 11-3 所示体箕,公式 11-2 給出了它的定義尽楔。

公式11-2 ELU激活函數(shù)

它看起來很像 ReLU 函數(shù)涎跨,但有一些區(qū)別叔营,主要區(qū)別在于:

  • 首先它在z < 0時取負值,這使得該單元的平均輸出接近于 0所宰。這有助于減輕梯度消失問題绒尊,如前所述。 超參數(shù)α定義為當z是一個大的負數(shù)時仔粥,ELU 函數(shù)接近的值婴谱。它通常設(shè)置為 1蟹但,但是如果你愿意,你可以像調(diào)整其他超參數(shù)一樣調(diào)整它谭羔。
  • 其次华糖,它對z < 0有一個非零的梯度,避免了神經(jīng)元死亡的問題瘟裸。
  • 第三客叉,函數(shù)在任何地方都是平滑的,包括z = 0左右话告,這有助于加速梯度下降兼搏,因為它不會彈回z = 0的左側(cè)和右側(cè)。

ELU 激活函數(shù)的主要缺點是計算速度慢于 ReLU 及其變體(由于使用指數(shù)函數(shù))沙郭,但是在訓練過程中佛呻,這是通過更快的收斂速度來補償?shù)摹?然而,在測試時間病线,ELU 網(wǎng)絡(luò)將比 ReLU 網(wǎng)絡(luò)慢吓著。

提示:
那么你應(yīng)該使用哪個激活函數(shù)來處理深層神經(jīng)網(wǎng)絡(luò)的隱藏層? 雖然你的里程會有所不同氧苍,一般 ELU > leaky ReLU(及其變體)> ReLU > tanh > sigmoid夜矗。 如果您關(guān)心運行時性能,那么您可能喜歡 leaky ReLU超過ELU让虐。 如果你不想調(diào)整另一個超參數(shù)紊撕,你可以使用前面提到的默認的α值(leaky ReLU 為 0.01,ELU 為 1)赡突。 如果您有充足的時間和計算能力对扶,您可以使用交叉驗證來評估其他激活函數(shù),特別是如果您的神經(jīng)網(wǎng)絡(luò)過擬合惭缰,則為RReLU; 如果您擁有龐大的訓練數(shù)據(jù)集浪南,則為 PReLU。

TensorFlow 提供了一個可以用來建立神經(jīng)網(wǎng)絡(luò)的elu()函數(shù)漱受。 調(diào)用fully_connected()函數(shù)時络凿,只需設(shè)置activation_fn參數(shù)即可:

hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.elu, name="hidden1")

TensorFlow 沒有針對 leaky ReLU 的預定義函數(shù),但是很容易定義:

def leaky_relu(z, name=None):
    return tf.maximum(0.01 * z, z, name=name)

hidden1 = tf.layers.dense(X, n_hidden1, activation=leaky_relu, name="hidden1")

批量標準化

盡管使用 He初始化和 ELU(或任何 ReLU 變體)可以顯著減少訓練開始階段的梯度消失/爆炸問題昂羡,但不保證在訓練期間問題不會回來絮记。

在 2015 年的一篇論文中,Sergey Ioffe 和 Christian Szegedy 提出了一種稱為批量標準化(Batch Normalization虐先,BN)的技術(shù)來解決梯度消失/爆炸問題怨愤,每層輸入的分布在訓練期間改變的問題,更普遍的問題是當前一層的參數(shù)改變蛹批,每層輸入的分布會在訓練過程中發(fā)生變化(他們稱之為內(nèi)部協(xié)變量偏移問題)撰洗。

該技術(shù)包括在每層的激活函數(shù)之前在模型中添加操作篮愉,簡單地對輸入進行zero-centering和規(guī)范化,然后每層使用兩個新參數(shù)(一個用于尺度變換差导,另一個用于偏移)對結(jié)果進行尺度變換和偏移试躏。 換句話說,這個操作可以讓模型學習到每層輸入值的最佳尺度和平均值柿汛。

為了對輸入進行歸零和歸一化冗酿,算法需要估計輸入的均值和標準差埠对。 它通過評估當前小批量輸入的均值和標準差(因此命名為“批量標準化”)來實現(xiàn)络断。 整個操作在公式 11-3 中。

公式11-3 批量標準化算法
  • μB是整個小批量B的經(jīng)驗均值

  • σB是經(jīng)驗標準差项玛,也是來評估整個小批量的貌笨。

  • mB是小批量中的實例數(shù)量。

  • Xi是以為零中心和標準化的輸入襟沮。

  • γ是層的縮放參數(shù)锥惋。

  • β是層的便宜參數(shù)(偏移量)

  • ?是一個很小的數(shù)字,以避免被零除(通常為10 ^ -3)开伏。 這被稱為平滑項(拉布拉斯平滑膀跌,Laplace Smoothing)。

  • z(i) 是BN操作的輸出:它是輸入的縮放和移位版本固灵。

在測試時捅伤,沒有小批量計算經(jīng)驗均值和標準差,所以您只需使用整個訓練集的均值和標準差巫玻。 這些通常在訓練期間使用移動平均值進行有效計算丛忆。 因此,總的來說仍秤,每個批次標準化的層次都學習了四個參數(shù):γ(縮放度)熄诡,β(偏移),μ(平均值)和σ(標準差)诗力。

作者證明凰浮,這項技術(shù)大大改善了他們試驗的所有深度神經(jīng)網(wǎng)絡(luò)。梯度消失問題大大減少了苇本,他們可以使用飽和激活函數(shù)袜茧,如 tanh 甚至邏輯激活函數(shù)。網(wǎng)絡(luò)對權(quán)重初始化也不那么敏感圈澈。他們能夠使用更大的學習率惫周,顯著加快了學習過程。具體地康栈,他們指出递递,“應(yīng)用于最先進的圖像分類模型喷橙,批標準減少了 14 倍的訓練步驟實現(xiàn)了相同的精度,以顯著的優(yōu)勢擊敗了原始模型登舞。[...] 使用批量標準化的網(wǎng)絡(luò)集合贰逾,我們改進了 ImageNet 分類上的最佳公布結(jié)果:達到4.9% 的前5個驗證錯誤(和 4.8% 的測試錯誤),超出了人類評估者的準確性菠秒。批量標準化也像一個正則化項一樣疙剑,減少了對其他正則化技術(shù)的需求(如本章稍后描述的 dropout).

然而,批量標準化的確會增加模型的復雜性(盡管它不需要對輸入數(shù)據(jù)進行標準化践叠,因為第一個隱藏層會照顧到這一點言缤,只要它是批量標準化的)。 此外禁灼,還存在運行時間的損失:由于每層所需的額外計算管挟,神經(jīng)網(wǎng)絡(luò)的預測速度較慢。 所以弄捕,如果你需要預測閃電般快速僻孝,你可能想要檢查普通ELU + He初始化執(zhí)行之前如何執(zhí)行批量標準化。

注意:
您可能會發(fā)現(xiàn)守谓,訓練起初相當緩慢穿铆,而梯度下降正在尋找每層的最佳縮放和偏移量,一旦找到恰當值斋荞,就會加速荞雏。

使用 TensorFlow 實現(xiàn)批量標準化

TensorFlow 提供了一個batch_normalization()函數(shù),它簡單地對輸入進行居中和標準化譬猫,但是您必須自己計算平均值和標準差(基于訓練期間的小批量數(shù)據(jù)或測試過程中的完整數(shù)據(jù)集) 作為這個函數(shù)的參數(shù)讯檐,并且還必須處理縮放和偏移量參數(shù)的創(chuàng)建(并將它們傳遞給此函數(shù))。 這是可行的染服,但不是最方便的方法别洪。 相反,你應(yīng)該使用batch_norm()函數(shù)柳刮,它為你處理所有這些挖垛。 您可以直接調(diào)用它,或者告訴fully_connected()函數(shù)使用它秉颗,如下面的代碼所示:

注意:本書使用tensorflow.contrib.layers.batch_norm()而不是tf.layers.batch_normalization()(本章寫作時不存在)痢毒。 現(xiàn)在最好使用tf.layers.batch_normalization(),因為contrib模塊中的任何內(nèi)容都可能會改變或被刪除蚕甥,恕不另行通知哪替。 我們現(xiàn)在不使用batch_norm()函數(shù)作為fully_connected()函數(shù)的正則化參數(shù),而是使用batch_normalization()菇怀,并明確地創(chuàng)建一個不同的層凭舶。 參數(shù)有些不同晌块,特別是:

  • decay更名為momentum
  • is_training被重命名為training
  • updates_collections被刪除:批量標準化所需的更新操作被添加到UPDATE_OPS集合中,并且您需要在訓練期間明確地運行這些操作(請參閱下面的執(zhí)行階段)
  • 我們不需要指定scale = True帅霜,因為這是默認值匆背。

還要注意,為了在每個隱藏層激活函數(shù)之前運行批量標準化身冀,我們手動應(yīng)用 RELU 激活函數(shù)钝尸,在批量規(guī)范層之后。注意:由于tf.layers.dense()函數(shù)與本書中使用的tf.contrib.layers.arg_scope()不兼容搂根,我們現(xiàn)在使用 python 的functools.partial()函數(shù)珍促。 它可以很容易地創(chuàng)建一個my_dense_layer()函數(shù),只需調(diào)用tf.layers.dense()兄墅,并自動設(shè)置所需的參數(shù)(除非在調(diào)用my_dense_layer()時覆蓋它們)踢星。 如您所見澳叉,代碼保持非常相似隙咸。

import tensorflow as tf

n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")

training = tf.placeholder_with_default(False, shape=(), name='training')

hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1")
bn1 = tf.layers.batch_normalization(hidden1, training=training, momentum=0.9)
bn1_act = tf.nn.elu(bn1)

hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2")
bn2 = tf.layers.batch_normalization(hidden2, training=training, momentum=0.9)
bn2_act = tf.nn.elu(bn2)

logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs")
logits = tf.layers.batch_normalization(logits_before_bn, training=training,
                                       momentum=0.9)
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
training = tf.placeholder_with_default(False, shape=(), name='training')

為了避免一遍又一遍重復相同的參數(shù),我們可以使用 Python 的partial()函數(shù):

from functools import partial

my_batch_norm_layer = partial(tf.layers.batch_normalization,
                              training=training, momentum=0.9)

hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1")
bn1 = my_batch_norm_layer(hidden1)
bn1_act = tf.nn.elu(bn1)
hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2")
bn2 = my_batch_norm_layer(hidden2)
bn2_act = tf.nn.elu(bn2)
logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs")
logits = my_batch_norm_layer(logits_before_bn)

完整代碼

from functools import partial
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf

if __name__ == '__main__':
    n_inputs = 28 * 28
    n_hidden1 = 300
    n_hidden2 = 100
    n_outputs = 10

    mnist = input_data.read_data_sets("/tmp/data/")

    batch_norm_momentum = 0.9
    learning_rate = 0.01

    X = tf.placeholder(tf.float32, shape=(None, n_inputs), name = 'X')
    y = tf.placeholder(tf.int64, shape=None, name = 'y')
    training = tf.placeholder_with_default(False, shape=(), name = 'training')#給Batch norm加一個placeholder

    with tf.name_scope("dnn"):
        he_init = tf.contrib.layers.variance_scaling_initializer()
        #對權(quán)重的初始化

        my_batch_norm_layer = partial(
            tf.layers.batch_normalization,
            training = training,
            momentum = batch_norm_momentum
        )

        my_dense_layer = partial(
            tf.layers.dense,
            kernel_initializer = he_init
        )

        hidden1 = my_dense_layer(X ,n_hidden1 ,name = 'hidden1')
        bn1 = tf.nn.elu(my_batch_norm_layer(hidden1))
        hidden2 = my_dense_layer(bn1, n_hidden2, name = 'hidden2')
        bn2 = tf.nn.elu(my_batch_norm_layer(hidden2))
        logists_before_bn = my_dense_layer(bn2, n_outputs, name = 'outputs')
        logists = my_batch_norm_layer(logists_before_bn)

    with tf.name_scope('loss'):
        xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels = y, logits= logists)
        loss = tf.reduce_mean(xentropy, name = 'loss')

    with tf.name_scope('train'):
        optimizer = tf.train.GradientDescentOptimizer(learning_rate)
        training_op = optimizer.minimize(loss)

    with tf.name_scope("eval"):
        correct = tf.nn.in_top_k(logists, y, 1)
        accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

    init = tf.global_variables_initializer()
    saver = tf.train.Saver()

    n_epoches = 20
    batch_size = 200
# 注意:由于我們使用的是 tf.layers.batch_normalization() 而不是 tf.contrib.layers.batch_norm()(如本書所述)成洗,
# 所以我們需要明確運行批量規(guī)范化所需的額外更新操作(sess.run([ training_op五督,extra_update_ops], ...)。
    extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

    with tf.Session() as sess:
        init.run()
        for epoch in range(n_epoches):
            for iteraton in range(mnist.train.num_examples//batch_size):
                X_batch, y_batch = mnist.train.next_batch(batch_size)
                sess.run([training_op,extra_update_ops],
                         feed_dict={training:True, X:X_batch, y:y_batch})
            accuracy_val = accuracy.eval(feed_dict= {X:mnist.test.images,
                                                    y:mnist.test.labels})
            print(epoch, 'Test accuracy:', accuracy_val)

什么F垦辍充包? 這對 MNIST 來說不是一個很好的準確性。 當然遥椿,如果你訓練的時間越長基矮,準確性就越好,但是由于這樣一個淺的網(wǎng)絡(luò)冠场,批量范數(shù)和 ELU 不太可能產(chǎn)生非常積極的影響:它們大部分都是為了更深的網(wǎng)絡(luò)而發(fā)光家浇。請注意,您還可以訓練操作取決于更新操作:

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    with tf.control_dependencies(extra_update_ops):
        training_op = optimizer.minimize(loss)

這樣碴裙,你只需要在訓練過程中評估training_op钢悲,TensorFlow也會自動運行更新操作:

sess.run(training_op, feed_dict={training: True, X: X_batch, y: y_batch})

梯度裁剪

減少梯度爆炸問題的一種常用技術(shù)是在反向傳播過程中簡單地剪切梯度,使它們不超過某個閾值(這對于遞歸神經(jīng)網(wǎng)絡(luò)是非常有用的舔株;參見第 14 章)莺琳。 這就是所謂的梯度裁剪。一般來說载慈,人們更喜歡批量標準化惭等,但了解梯度裁剪以及如何實現(xiàn)它仍然是有用的。

在 TensorFlow 中办铡,優(yōu)化器的minimize()函數(shù)負責計算梯度并應(yīng)用它們辞做,所以您必須首先調(diào)用優(yōu)化器的compute_gradients()方法珠十,然后使用clip_by_value()函數(shù)創(chuàng)建一個裁剪梯度的操作,最后 創(chuàng)建一個操作來使用優(yōu)化器的apply_gradients()方法應(yīng)用裁剪梯度:

threshold = 1.0

optimizer = tf.train.GradientDescentOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(loss)
capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var)
              for grad, var in grads_and_vars]
training_op = optimizer.apply_gradients(capped_gvs)

像往常一樣凭豪,您將在每個訓練階段運行這個training_op焙蹭。 它將計算梯度,將它們裁剪到 -1.0 和 1.0 之間嫂伞,并應(yīng)用它們孔厉。 threhold是您可以調(diào)整的超參數(shù)。

復用預訓練層

從零開始訓練一個非常大的 DNN 通常不是一個好主意帖努,相反撰豺,您應(yīng)該總是嘗試找到一個現(xiàn)有的神經(jīng)網(wǎng)絡(luò)來完成與您正在嘗試解決的任務(wù)類似的任務(wù),然后復用這個網(wǎng)絡(luò)的較低層:這就是所謂的遷移學習拼余。這不僅會大大加快訓練速度污桦,還將需要更少的訓練數(shù)據(jù)。

例如匙监,假設(shè)您可以訪問經(jīng)過訓練的 DNN凡橱,將圖片分為 100 個不同的類別,包括動物亭姥,植物稼钩,車輛和日常物品。 您現(xiàn)在想要訓練一個 DNN 來對特定類型的車輛進行分類达罗。 這些任務(wù)非常相似坝撑,因此您應(yīng)該嘗試重新使用第一個網(wǎng)絡(luò)的一部分(請參見圖 11-4)。

圖11-4 復用預訓練層

如果新任務(wù)的輸入圖像與原始任務(wù)中使用的輸入圖像的大小不一致粮揉,則必須添加預處理步驟以將其大小調(diào)整為原始模型的預期大小巡李。 更一般地說,如果輸入具有類似的低級層次的特征扶认,則遷移學習將很好地工作侨拦。

復用 TensorFlow 模型

如果原始模型使用 TensorFlow 進行訓練,則可以簡單地將其恢復并在新任務(wù)上進行訓練:

[...] # construct the original model 

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")
    # continue training the model...

完整代碼:

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_hidden3 = 50
n_hidden4 = 50
n_outputs = 10

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2")
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3")
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4")
    hidden5 = tf.layers.dense(hidden4, n_hidden5, activation=tf.nn.relu, name="hidden5")
    logits = tf.layers.dense(hidden5, n_outputs, name="outputs")

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

learning_rate = 0.01
threshold = 1.0

optimizer = tf.train.GradientDescentOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(loss)
capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var)
              for grad, var in grads_and_vars]
training_op = optimizer.apply_gradients(capped_gvs)

init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")

但是蝠引,一般情況下阳谍,您只需要重新使用原始模型的一部分(就像我們將要討論的那樣)。 一個簡單的解決方案是將Saver配置為僅恢復原始模型中的一部分變量螃概。 例如析蝴,下面的代碼只恢復隱藏層1,2和3:

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")       # reused
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # reused
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # reused
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs")                         # new!

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)
[...] # build new model with the same definition as before for hidden layers 1-3 

reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):                                      # not shown in the book
        for iteration in range(mnist.train.num_examples // batch_size): # not shown
            X_batch, y_batch = mnist.train.next_batch(batch_size)      # not shown
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})  # not shown
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,  # not shown
                                                y: mnist.test.labels}) # not shown
        print(epoch, "Test accuracy:", accuracy_val)                   # not shown

    save_path = saver.save(sess, "./my_new_model_final.ckpt")

首先我們建立新的模型徒溪,確保復制原始模型的隱藏層 1 到 3。我們還創(chuàng)建一個節(jié)點來初始化所有變量。 然后我們得到剛剛用trainable = True(這是默認值)創(chuàng)建的所有變量的列表擅这,我們只保留那些范圍與正則表達式hidden [123]相匹配的變量(即,我們得到所有可訓練的隱藏層 1 到 3 中的變量)。 接下來,我們創(chuàng)建一個字典豺鼻,將原始模型中每個變量的名稱映射到新模型中的名稱(通常需要保持完全相同的名稱)。 然后款慨,我們創(chuàng)建一個Saver儒飒,它將只恢復這些變量,并且創(chuàng)建另一個Saver來保存整個新模型檩奠,而不僅僅是第 1 層到第 3 層桩了。然后,我們開始一個會話并初始化模型中的所有變量埠戳,然后從原始模型的層 1 到 3中恢復變量值井誉。最后,我們在新任務(wù)上訓練模型并保存整胃。

任務(wù)越相似颗圣,您可以重復使用的層越多(從較低層開始)。 對于非常相似的任務(wù)屁使,您可以嘗試保留所有隱藏的層在岂,只替換輸出層。

復用來自其它框架的模型

如果模型是使用其他框架進行訓練的屋灌,則需要手動加載權(quán)重(例如洁段,如果使用 Theano 訓練,則使用 Theano 代碼)共郭,然后將它們分配給相應(yīng)的變量。 這可能是相當乏味的疾呻。 例如除嘹,下面的代碼顯示了如何復制使用另一個框架訓練的模型的第一個隱藏層的權(quán)重和偏置:

original_w = [...] # Load the weights from the other framework
original_b = [...] # Load the biases from the other framework

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
hidden1 = fully_connected(X, n_hidden1, scope="hidden1")
[...] # # Build the rest of the model

# Get a handle on the variables created by fully_connected()
with tf.variable_scope("", default_name="", reuse=True):  # root scope
    hidden1_weights = tf.get_variable("hidden1/weights")
    hidden1_biases = tf.get_variable("hidden1/biases")

# Create nodes to assign arbitrary values to the weights and biases
original_weights = tf.placeholder(tf.float32, shape=(n_inputs, n_hidden1))
original_biases = tf.placeholder(tf.float32, shape=(n_hidden1))
assign_hidden1_weights = tf.assign(hidden1_weights, original_weights)
assign_hidden1_biases = tf.assign(hidden1_biases, original_biases)

init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)
    sess.run(assign_hidden1_weights, feed_dict={original_weights: original_w})
    sess.run(assign_hidden1_biases, feed_dict={original_biases: original_b})
    [...] # Train the model on your new task

凍結(jié)較低層

第一個 DNN 的較低層可能已經(jīng)學會了檢測圖片中的低級特征,這將在兩個圖像分類任務(wù)中有用岸蜗,因此您可以按照原樣重新使用這些層尉咕。 在訓練新的 DNN 時,“凍結(jié)”權(quán)重通常是一個好主意:如果較低層權(quán)重是固定的璃岳,那么較高層權(quán)重將更容易訓練(因為他們不需要學習一個移動的目標)年缎。 要在訓練期間凍結(jié)較低層,最簡單的解決方案是給優(yōu)化器列出要訓練的變量铃慷,不包括來自較低層的變量:

train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
                               scope="hidden[34]|outputs")
training_op = optimizer.minimize(loss, var_list=train_vars)

第一行獲得隱藏層 3 和 4 以及輸出層中所有可訓練變量的列表单芜。 這留下了隱藏層 1 和 2 中的變量。接下來犁柜,我們將這個受限制的可列表變量列表提供給optimizerminimize()函數(shù)≈摒現(xiàn)在,層 1 和層 2 被凍結(jié):在訓練過程中不會發(fā)生變化(通常稱為凍結(jié)層)。

緩存凍結(jié)層

由于凍結(jié)層不會改變扒腕,因此可以為每個訓練實例緩存最上面的凍結(jié)層的輸出绢淀。 由于訓練貫穿整個數(shù)據(jù)集很多次,這將給你一個巨大的速度提升瘾腰,因為每個訓練實例只需要經(jīng)過一次凍結(jié)層(而不是每個迭代一次)皆的。 例如,你可以先運行整個訓練集(假設(shè)你有足夠的內(nèi)存):

hidden2_outputs = sess.run(hidden2, feed_dict={X: X_train})

然后在訓練過程中蹋盆,不再對訓練實例建立批次祭务,而是從隱藏層2的輸出建立批次,并將它們提供給訓練操作:

import numpy as np

n_epochs = 100
n_batches = 500

for epoch in range(n_epochs):
    shuffled_idx = rnd.permutation(len(hidden2_outputs))
    hidden2_batches = np.array_split(hidden2_outputs[shuffled_idx], n_batches)
    y_batches = np.array_split(y_train[shuffled_idx], n_batches)
    for hidden2_batch, y_batch in
        zip(hidden2_batches, y_batches):
        sess.run(training_op, feed_dict={hidden2: hidden2_batch, y: y_batch})

最后一行運行先前定義的訓練操作(凍結(jié)層 1 和 2)怪嫌,并從第二個隱藏層(以及該批次的目標)為其輸出一批輸出义锥。 因為我們給 TensorFlow 隱藏層 2 的輸出,所以它不會去評估它(或者它所依賴的任何節(jié)點)岩灭。

調(diào)整拌倍,刪除或替換較高層

原始模型的輸出層通常應(yīng)該被替換,因為對于新的任務(wù)來說噪径,最有可能沒有用處柱恤,甚至可能沒有適合新任務(wù)的輸出數(shù)量。

類似地找爱,原始模型的較高隱藏層不太可能像較低層一樣有用梗顺,因為對于新任務(wù)來說最有用的高層特征可能與對原始任務(wù)最有用的高層特征明顯不同。 你需要找到正確的層數(shù)來復用车摄。

嘗試先凍結(jié)所有復制的層寺谤,然后訓練模型并查看它是如何執(zhí)行的。 然后嘗試解凍一個或兩個較高隱藏層吮播,讓反向傳播調(diào)整它們变屁,看看性能是否提高。 您擁有的訓練數(shù)據(jù)越多意狠,您可以解凍的層數(shù)就越多粟关。

如果仍然無法獲得良好的性能,并且您的訓練數(shù)據(jù)很少环戈,請嘗試刪除頂部的隱藏層闷板,并再次凍結(jié)所有剩余的隱藏層。 您可以迭代院塞,直到找到正確的層數(shù)重復使用遮晚。 如果您有足夠的訓練數(shù)據(jù),您可以嘗試替換頂部的隱藏層迫悠,而不是丟掉它們鹏漆,甚至可以添加更多的隱藏層。

Model Zoos

你在哪里可以找到一個類似于你想要解決的任務(wù)訓練的神經(jīng)網(wǎng)絡(luò)? 首先看看顯然是在你自己的模型目錄艺玲。 這是保存所有模型并組織它們的一個很好的理由括蝠,以便您以后可以輕松地檢索它們。 另一個選擇是在模型動物園中搜索饭聚。 許多人為了各種不同的任務(wù)而訓練機器學習模型忌警,并且善意地向公眾發(fā)布預訓練模型。

TensorFlow 在 https://github.com/tensorflow/models 中有自己的模型動物園秒梳。 特別是法绵,它包含了大多數(shù)最先進的圖像分類網(wǎng)絡(luò),如 VGG酪碘,Inception 和 ResNet(參見第 13 章朋譬,檢查model/slim目錄),包括代碼兴垦,預訓練模型和 工具來下載流行的圖像數(shù)據(jù)集徙赢。

另一個流行的模型動物園是 Caffe 模型動物園。 它還包含許多在各種數(shù)據(jù)集(例如探越,ImageNet狡赐,Places 數(shù)據(jù)庫,CIFAR10 等)上訓練的計算機視覺模型(例如钦幔,LeNet枕屉,AlexNet,ZFNet鲤氢,GoogLeNet搀擂,VGGNet,開始)铜异。 Saumitro Dasgupta 寫了一個轉(zhuǎn)換器哥倔,可以在 https://github.com/ethereon/caffetensorflow

無監(jiān)督的預訓練

假設(shè)你想要解決一個復雜的任務(wù)揍庄,你沒有太多的標記的訓練數(shù)據(jù),但不幸的是东抹,你不能找到一個類似的任務(wù)訓練模型蚂子。 不要失去希望! 首先缭黔,你當然應(yīng)該嘗試收集更多的有標簽的訓練數(shù)據(jù)食茎,但是如果這太難或太昂貴,你仍然可以進行無監(jiān)督的訓練(見圖 11-5)馏谨。 也就是說别渔,如果你有很多未標記的訓練數(shù)據(jù),你可以嘗試逐層訓練層,從最低層開始哎媚,然后上升喇伯,使用無監(jiān)督的特征檢測算法,如限制玻爾茲曼機(RBM拨与;見附錄 E)或自動編碼器(見第 15 章)稻据。 每個層都被訓練成先前訓練過的層的輸出(除了被訓練的層之外的所有層都被凍結(jié))。 一旦所有層都以這種方式進行了訓練买喧,就可以使用監(jiān)督式學習(即反向傳播)對網(wǎng)絡(luò)進行微調(diào)捻悯。

圖11-5 無監(jiān)督的預訓練

這是一個相當漫長而乏味的過程,但通常運作良好淤毛。 實際上今缚,這是 Geoffrey Hinton 和他的團隊在 2006 年使用的技術(shù),導致了神經(jīng)網(wǎng)絡(luò)的復興和深度學習的成功低淡。 直到 2010 年姓言,無監(jiān)督預訓練(通常使用 RBM)是深度網(wǎng)絡(luò)的標準,只有在梯度消失問題得到緩解之后查牌,純訓練 DNN 才更為普遍事期。 然而,當您有一個復雜的任務(wù)需要解決時纸颜,無監(jiān)督訓練(現(xiàn)在通常使用自動編碼器而不是 RBM)仍然是一個很好的選擇兽泣,沒有類似的模型可以重復使用,而且標記的訓練數(shù)據(jù)很少胁孙,但是大量的未標記的訓練數(shù)據(jù)唠倦。(另一個選擇是提出一個監(jiān)督的任務(wù),您可以輕松地收集大量標記的訓練數(shù)據(jù)涮较,然后使用遷移學習稠鼻,如前所述。 例如狂票,如果要訓練一個模型來識別圖片中的朋友候齿,你可以在互聯(lián)網(wǎng)上下載數(shù)百萬張臉并訓練一個分類器來檢測兩張臉是否相同,然后使用此分類器將新圖片與你朋友的每張照片做比較闺属。)

在輔助任務(wù)上預訓練

最后一種選擇是在輔助任務(wù)上訓練第一個神經(jīng)網(wǎng)絡(luò)慌盯,您可以輕松獲取或生成標記的訓練數(shù)據(jù),然后重新使用該網(wǎng)絡(luò)的較低層來完成您的實際任務(wù)掂器。 第一個神經(jīng)網(wǎng)絡(luò)的較低層將學習可能被第二個神經(jīng)網(wǎng)絡(luò)重復使用的特征檢測器亚皂。

例如,如果你想建立一個識別面孔的系統(tǒng)国瓮,你可能只有幾個人的照片 - 顯然不足以訓練一個好的分類器灭必。 收集每個人的數(shù)百張照片將是不實際的狞谱。 但是,您可以在互聯(lián)網(wǎng)上收集大量隨機人員的照片禁漓,并訓練第一個神經(jīng)網(wǎng)絡(luò)來檢測兩張不同的照片是否屬于同一個人跟衅。 這樣的網(wǎng)絡(luò)將學習面部優(yōu)秀的特征檢測器,所以重復使用它的較低層將允許你使用很少的訓練數(shù)據(jù)來訓練一個好的面部分類器璃饱。

收集沒有標簽的訓練樣本通常是相當便宜的与斤,但標注它們卻相當昂貴。 在這種情況下荚恶,一種常見的技術(shù)是將所有訓練樣例標記為“好”撩穿,然后通過破壞好的訓練樣例產(chǎn)生許多新的訓練樣例,并將這些樣例標記為“壞”谒撼。然后食寡,您可以訓練第一個神經(jīng)網(wǎng)絡(luò) 將實例分類為好或不好。 例如廓潜,您可以下載數(shù)百萬個句子抵皱,將其標記為“好”,然后在每個句子中隨機更改一個單詞辩蛋,并將結(jié)果語句標記為“不好”呻畸。如果神經(jīng)網(wǎng)絡(luò)可以告訴“The dog sleeps”是好的句子,但“The dog they”是壞的悼院,它可能知道相當多的語言伤为。 重用其較低層可能有助于許多語言處理任務(wù)。

另一種方法是訓練第一個網(wǎng)絡(luò)為每個訓練實例輸出一個分數(shù)据途,并使用一個損失函數(shù)確保一個好的實例的分數(shù)大于一個壞實例的分數(shù)至少一定的邊際绞愚。 這被稱為最大邊際學習.


(第一部分 機器學習基礎(chǔ))
第01章 機器學習概覽
第02章 一個完整的機器學習項目(上)
第02章 一個完整的機器學習項目(下)
第03章 分類
第04章 訓練模型
第05章 支持向量機
第06章 決策樹
第07章 集成學習和隨機森林
第08章 降維
(第二部分 神經(jīng)網(wǎng)絡(luò)和深度學習)
第9章 啟動和運行TensorFlow
第10章 人工神經(jīng)網(wǎng)絡(luò)
第11章 訓練深度神經(jīng)網(wǎng)絡(luò)(上)
第11章 訓練深度神經(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章 強化學習(上)
第16章 強化學習(下)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市颖医,隨后出現(xiàn)的幾起案子位衩,更是在濱河造成了極大的恐慌,老刑警劉巖熔萧,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糖驴,死亡現(xiàn)場離奇詭異,居然都是意外死亡佛致,警方通過查閱死者的電腦和手機遂赠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晌杰,“玉大人,你說我怎么就攤上這事筷弦±哐荩” “怎么了抑诸?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長爹殊。 經(jīng)常有香客問我蜕乡,道長,這世上最難降的妖魔是什么梗夸? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任层玲,我火速辦了婚禮,結(jié)果婚禮上反症,老公的妹妹穿的比我還像新娘辛块。我一直安慰自己,他們只是感情好铅碍,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布润绵。 她就那樣靜靜地躺著,像睡著了一般胞谈。 火紅的嫁衣襯著肌膚如雪尘盼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天烦绳,我揣著相機與錄音卿捎,去河邊找鬼。 笑死径密,一個胖子當著我的面吹牛午阵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播睹晒,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼趟庄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伪很?” 一聲冷哼從身側(cè)響起戚啥,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锉试,沒想到半個月后猫十,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡呆盖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年拖云,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片应又。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡宙项,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出株扛,到底是詐尸還是另有隱情尤筐,我是刑警寧澤汇荐,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站盆繁,受9級特大地震影響掀淘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜油昂,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一革娄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冕碟,春花似錦拦惋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至我衬,卻和暖如春叹放,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挠羔。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工井仰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人破加。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓俱恶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親范舀。 傳聞我的和親對象是個殘疾皇子合是,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355