【深度学习】梯度消失和梯度爆炸问题的最完整解析
? 作者丨奧雷利安 · 杰龍
來源丨機械工業出版社《機器學習實戰:基于Scikit-Learn、Keras和TensorFlow》
編輯丨極市平臺
1?梯度消失與梯度爆炸
正如我們在第10章中討論的那樣,反向傳播算法的工作原理是從輸出層到輸入層,并在此過程中傳播誤差梯度。一旦算法計算出代價函數相對于網絡中每個參數的梯度,就可以使用這些梯度以梯度下降步驟來更新每個參數。
不幸的是,隨著算法向下傳播到較低層,梯度通常會越來越小。結果梯度下降更新使較低層的連接權重保持不變,訓練不能收斂到一個好的最優解。我們稱其為梯度消失問題。在某些情況下,可能會出現相反的情況:梯度可能會越來越大,各層需要更新很大的權重直到算法發散為止。這是梯度爆炸問題,它出現在遞歸神經網絡中(請參閱第15章)。更籠統地說,深度神經網絡很受梯度不穩定的影響。不同的層可能以不同的速度學習。
這種不幸的行為是很久以前就憑經驗觀察到了,這也是深度神經網絡在2000年代初期被大量拋棄的原因之一。目前尚不清楚是什么原因導致在訓練DNN時使梯度如此不穩定,但是Xavier Glorot和Yoshua Bengio在2010年的一篇論文中闡明了一些觀點1。作者發現了一些疑點,包括流行的邏輯sigmoid激活函數和當時最流行的權重初始化技術(即平均值為0且標準差為1的正態分布)。簡而言之,它們表明使用此激活函數和此初始化方案,每層輸出的方差遠大于其輸入的方差。隨著網絡的延伸,方差在每一層之后都會增加,直到激活函數在頂層達到飽和為止。實際上,由于邏輯函數的平均值為0.5,而不是0(雙曲線正切函數的平均值為0,在深度網絡中的行為比邏輯函數稍微好一些),因此飽和度實際上變得更差。
查看邏輯激活函數(參見圖11-1),你可以看到,當輸入變大(負數或正數)時,該函數會以0或1飽和,并且導數非常接近0。因此反向傳播開始時它幾乎沒有梯度可以通過網絡傳播回去。當反向傳播通過頂層向下傳播時,幾乎沒有什么梯度不斷被稀釋,因此對于底層來說,實際上什么也沒有留下。
圖11-1 邏輯激活函數的飽和
1.1 Glorot和He初始化
Glorot和Bengio在它們的論文中提出了一種能顯著緩解不穩定梯度問題的方法。它們指出,我們需要信號在兩個方向上正確流動:進行預測時,信號為正向;在反向傳播梯度時,信號為反向。我們既不希望信號消失,也不希望它爆炸并飽和。為了使信號正確流動,作者認為,我們需要每層輸出的方差等于其輸入的方差2,并且我們需要在反方向時流過某層之前和之后的梯度具有相同的方差(如果你對數學細節感興趣,請查看本論文)。除非該層具有相等數量的輸入和神經元(這些數字稱為該層的扇入和扇出),否則實際上不可能同時保證兩者,但是Glorot和Bengio提出了一個很好的折中方案,在實踐中證明很好地發揮作用:必須按照公式11-1中所述的隨機初始化每層的連接權重,其中fanavg = (fanin + fanout ) / 2。這種初始化策略稱為Xavier初始化或者Glorot初始化,以論文的第一作者命名。
公式11-1. Glorot初始化(使用邏輯激活函數時)
正態分布,均值為 0,方差為 或 和+ 之間的均勻分布,其中?
如果在公式11-1中你用fanin替換fanavg,則會得到Yann LeCun在1990年代提出的初始化策略。他稱其為LeCun初始化。Genevieve Orr和Klaus-Robert Müller甚至在其1998年出版的《Neural Networks: Tricks of theTrade》(Springer)一書中進行了推薦。當fanin= fanout時,LeCun初始化等效于Glorot初始化。研究人員花了十多年的時間才意識到這一技巧的重要性。使用Glorot初始化可以大大加快訓練速度,這是導致深度學習成功的訣竅之一。
一些論文為不同的激活函數提供了類似的策略3。這些策略的差異僅在于方差的大小以及它們使用的是fanavg還是fanin,如表11-1所示(對于均勻分布,只需計算r = )。ReLU激活函數的初始化策略(及其變體,包括ELU激活函數)有時簡稱為He初始化。本章稍后將解釋SELU激活函數。它應該與LeCun初始化一起使用(最好與正態分布一起使用,如我們所見)。
初始化 | 激活函數 | σ2 |
Glorot | None, tanh, logistic, ?softmax | 1 / fanavg |
He | ReLU 和變體 | 2 / fanin |
LeCun | SELU | 1 / fanin |
表11-1. 每種激活函數的初始化參數
默認情況下,Keras使用具有均勻分布的Glorot初始化。創建層時,可以通過設置
kernel_initializer="he_uniform"或
kernel_initializer="he_normal"來將其更改為He初始化:
如果你要使用均勻分布但基于fanavg而不是fanin進行He初始化,則可以使用VarianceScaling初始化,如下所示:
he_avg_init = keras.initializers.VarianceScaling(scale=2.,mode='fan_avg',distribution='uniform')keras.layers.Dense(10, activation="sigmoid",kernel_initializer=he_avg_init)1.2 非飽和激活函數
Glorot和Bengio在2010年的論文中提出的一項見解是,梯度不穩定的問題部分是由于激活函數選擇不當所致。在此之前,大多數人都認為,如果大自然母親選擇在生物神經元中使用類似sigmoid的激活函數,那么它們必定是一個好選擇。但是事實證明,其他激活函數在深度神經網絡中的表現要好得多,尤其是ReLU激活函數,這主要是因為它對正值不飽和(并且計算速度很快)。
不幸的是,ReLU激活函數并不完美。它有一個被稱為“瀕死的ReLUs”的問題:在訓練過程中,某些神經元實際上“死亡”了,這意味著它們停止輸出除0以外的任何值。在某些情況下,你可能會發現網絡中一半的神經元都死了,特別是如果你使用較大的學習率。當神經元的權重進行調整時,其輸入的加權和對于訓練集中的所有實例均為負數,神經元會死亡。發生這種情況時,它只會繼續輸出零,梯度下降不會再影響它,因為ReLU函數的輸入為負時其梯度為零4。
要解決此問題,你可能需要使用ReLU函數的變體,例如leaky ReLU。該函數定義為LeakyReLUα(z) =max(αz, z)(見圖11-2)。超參數α定義函數“泄漏”的程度:它是z < 0時函數的斜率,通常設置為0.01。這個小的斜率確保了leaky ReLUs永遠不會死亡。它們可能會陷入長時間的昏迷,但是它們有機會最后醒來。2015年的一篇論文比較了ReLU激活函數的幾種變體5,其結論之一是泄漏的變體要好于嚴格的ReLU激活函數。實際上,設置α = 0.2(大泄漏)似乎比α = 0.01(小泄漏)會產生更好的性能。本論文還對隨機的leaky ReLU(RReLU)進行了評估,在訓練過程中在給定范圍內隨機選擇α,在測試過程中將其固定為平均值。RReLU的表現也相當不錯,似乎可以充當正則化函數(減少了過度擬合訓練集的風險)。最后,本文評估了參數化leaky ReLU(PReLU),其中α可以在訓練期間學習(不是超參數,它像其他任何參數一樣,可以通過反向傳播進行修改)。據報道,PReLU在大型圖像數據集上的性能明顯優于ReLU,但是在較小的數據集上,它存在過度擬合訓練集的風險。
圖11-2. Leaky ReLU: 與ReLU類似,對負值有一個小斜率
最后但并非最不重要的一點是,Djork-Arné Clevert等人在2015年發表的論文提出了一種新的激活函數6,稱為指數線性單位(exponential linear unit,ELU),該函數在作者的實驗中勝過所有ReLU變體:減少訓練時間,神經網絡在測試集上表現更好。圖11-3繪制了函數圖,公式11-2給出了其定義。
公式11-2. ?ELU激活函數
圖11-3. ELU激活函數
ELU激活函數與ReLU函數非常相似,但有一些主要區別:
當z < 0時,它取負值,這使該單元的平均輸出接近于0,有助于緩解梯度消失的問題。超參數α定義當z為較大負數時,ELU函數逼近的值。通常將其設置為1,但是你可以像其他任何超參數一樣對其進行調整。
對于z < 0,它具有非零梯度,從而避免了神經元死亡的問題。
如果α等于1,則該函數在所有位置(包括z = 0左右)都是平滑的,這有助于加速梯度下降,因為它在z = 0的左右兩側彈跳不大。
ELU激活函數的主要缺點是它的計算比ReLU函數及其變體要慢(由于使用了指數函數)。它在訓練過程中更快的收斂速度彌補了這種緩慢的計算,但是在測試時,ELU網絡將比ReLU網絡慢。
然后,Günter Klambauer等人在2017年發表的論文提出了可擴展的ELU(Scaled ELU)激活函數7:顧名思義,它是ELU激活函數的可擴展變體。作者表明,如果你構建一個僅由密集層堆疊組成的神經網絡,并且如果所有隱藏層都使用SELU激活函數,則該網絡是自歸一化的:每層的輸出傾向于在訓練過程中保留平均值0和標準偏差為1,從而解決了梯度消失/爆炸的問題。結果,SELU激活函數通常大大優于這些神經網絡(尤其是深層神經網絡)的其他激活函數。但是,有一些產生自歸一化的條件(有關數學證明,請參見論文):
輸入特征必須是標準化的(平均為0和標準偏差為1)。
每個隱藏層的權重必須使用LeCun正態初始化。在Keras中,這意味著設置
kernel_initializer="lecun_normal"網絡的架構必須是順序的。不幸的是,如果你嘗試在非順序架構(例如循環網絡)中使用SELU(請參見第15章15)或具有跳過連接的網絡(即在Wide & Deep網絡中跳過層的連接),將無法保證自歸一化,因此SELU不一定會勝過其他激活函數。
本論文僅在所有層都是密集層的情況下保證自歸一化,但一些研究人員指出SELU激活函數也可以改善卷積神經網絡的性能(請參閱第14章)。
那么,你應該對深度神經網絡的隱藏層使用哪個激活函數呢?盡管你的目標會有所不同,但通常SELU > ELU > leaky ReLU(及其變體)> ReLU> tanh > logistic。如果網絡的架構不能自歸一化,那么ELU的性能可能會優于SELU(因為SELU在z = 0時不平滑)。如果你非常關心運行時延遲,那么你可能更喜歡leaky ReLU。如果你不想調整其他超參數,則可以使用Keras使用的默認α值(例如,leaky ReLU為0.3)。如果你有空閑時間和計算能力,則可以使用交叉驗證來評估其他激活函數,例如,如果網絡過度擬合,則為RReLU;如果你的訓練集很大,則為PReLU。就是說,由于ReLU是迄今為止最常用的激活函數,因此許多庫和硬件加速器都提供了ReLU特定的優化。因此,如果你將速度放在首位,那么ReLU可能仍然是最佳選擇。
要使用leaky ReLU激活函數,創建一個LeakyReLU層,并將其添加到你想要應用它的層之后的模型中:
model = keras.models.Sequential([[...]keras.layers.Dense(10,kernel_initializer="he_normal"),keras.layers.LeakyReLU(alpha=0.2),[...]])對于PReLU,將LeakyRelu(alpha=0.2)替換為PReLU ()。Keras當前沒有RReLU的官方實現,但是你可以輕松地實現自己的(要了解如何實現,請參閱第12章末尾的練習)。
對于SELU激活,在創建層時設置
activation="selu"和
kernel_initializer="lecun_normal":layer = keras.layers.Dense(10, activation="selu",kernel_initializer="lecun_normal")1.3 批量歸一化
盡管將He初始化與ELU(或ReLU的任何變體)一起使用可以顯著減少在訓練開始時的梯度消失/爆炸問題的危險,但這并不能保證它們在訓練期間不會再出現。
在2015年的一篇論文中8,Sergey Ioffe和Christian Szegedy提出了一種稱為批量歸一化(BN)的技術來解決這些問題。該技術包括在模型中的每個隱藏層的激活函數之前或之后添加一個操作。該操作對每個輸入零中心并歸一化,然后每層使用兩個新的參數向量縮放和偏移其結果:一個用于縮放,另一個用于偏移。換句話說,該操作可以使模型學習各層輸入的最佳縮放和均值。在許多情況下,如果你將BN層添加為神經網絡的第一層,則無須歸一化訓練集(例如,使用StandardScaler);BN層會為你完成此操作(因為它一次只能查看一個批次,它還可以重新縮放和偏移每個輸入特征)。
為了使輸入零中心并歸一化,該算法需要估計每個輸入的均值和標準差。通過評估當前小批次上的輸入的平均值和標準偏差(因此稱為“批量歸一化”)來做到這一點。公式11-3逐步總結了整個操作。
公式11-3. 批量歸一化算法
在此算法中:
μB是輸入均值的向量,在整個小批量B上評估(每個輸入包含一個均值)。
σB是輸入標準偏差的向量,也在整個小批量中進行評估(每個輸入包含一個標準偏差)。
mB是小批量中的實例數量。
(i) 是實例i的零中心和歸一化輸入的向量。
γ是該層的輸出縮放參數向量(每個輸入包含一個縮放參數)。
?表示逐元素乘法(每個輸入乘以其相應的輸出縮放參數)。
β是層的輸出移動(偏移)參數矢量(每個輸入包含一個偏移參數)。每個輸入都通過其相應的移動參數進行偏移。
ε是一個很小的數字以避免被零除(通常為10-5)。這稱為平滑項。
z(i)是BN操作的輸出。它是輸入的縮放和偏移的輸出。
因此在訓練期間,BN會歸一化其輸入,然后重新縮放并偏移它們。好!那在測試期間呢?這不那么簡單。確實,我們可能需要對單個實例而不是成批次的實例做出預測:在這種情況下,我們無法計算每個輸入的均值和標準差。而且,即使我們確實有一批次實例,它也可能太小,或者這些實例可能不是獨立的和相同分布的,因此在這批實例上計算統計信息將是不可靠的。一種解決方法是等到訓練結束,然后通過神經網絡運行整個訓練集,計算BN層每個輸入的均值和標準差。然后,在進行預測時,可以使用這些“最終”的輸入平均值和標準偏差,而不是一個批次的輸入平均值和標準偏差。然而,大多數批量歸一化的實現都是通過使用該層輸入的平均值和標準偏差的移動平均值來估計訓練期間的最終統計信息。這是Keras在使用BatchNormalization層時自動執行的操作。綜上所述,在每個批歸一化層中學習了四個參數向量:通過常規反向傳播學習γ(輸出縮放向量)和β(輸出偏移向量),和使用指數移動平均值估計的μ(最終的輸入均值向量)和σ(最終輸入標準差向量)。請注意,μ和σ是在訓練期間估算的,但僅在訓練后使用(以替換方程式11-3中的批量輸入平均值和標準偏差)。
Ioffe和Szegedy證明,批量歸一化極大地改善了它們試驗過的所有深度神經網絡,從而極大地提高了ImageNet分類任務的性能(ImageNet是將圖像分類為許多類的大型圖像數據庫,通常用于評估計算機視覺系統)。消失梯度的問題已大大減少,以至于它們可以使用飽和的激活函數,例如tanh甚至邏輯激活函數。網絡對權重初始化也不太敏感。作者可以使用更大的學習率,大大加快了學習過程。它們特別指出:
批量歸一化應用于最先進的圖像分類模型,以少14倍的訓練步驟即可達到相同的精度,在很大程度上擊敗了原始模型。[…] 使用批量歸一化網絡的集成,我們在ImageNet分類中改進了已發布的最好結果:前5位的驗證錯誤達到了4.9%(和4.8%的測試錯誤),超過了人工評分者的準確性。
最后,就像不斷贈送的禮物一樣,批量歸一化的作用就像正則化一樣,減少了對其他正則化技術(如dropout,本章稍后將介紹)的需求。
但是,批量歸一化確實增加了模型的復雜性(盡管它可以消除對輸入數據進行歸一化的需求,正如我們前面所討論的)。此外,還有運行時間的損失:由于每一層都需要額外的計算,因此神經網絡的預測速度較慢。幸運的是,經常可以在訓練后將BN層與上一層融合,從而避免了運行時的損失。這是通過更新前一層的權重和偏置來完成的,以便它直接產生適當的縮放和偏移的輸出。例如,如果前一層計算X W + b,則BN層將計算γ?(XW + b – μ)/σ + β(忽略分母中的平滑項ε)。如果我們定義W′ = γ?W/σ和b′ = γ?(b –μ)/σ + β,,則方程式可簡化為XW′ + b′。因此,如果我們用更新后的權重和偏置(W′ 和b′)替換前一層的權重和偏置(W和b),就可以去掉BN層(TFLite的優化器會自動執行此操作;請參閱第19章)。
你可能會發現訓練相當慢,因為當你使用批量歸一化時每個輪次要花費更多時間。通常情況下,這被BN的收斂速度要快得多的事實而抵消,因此達到相同性能所需的輪次更少,總而言之,墻上的時間通常會更短(這是墻上的時鐘所測量的時間)。
用Keras實現批量歸一化
與使用Keras進行的大多數操作一樣,實現批量歸一化既簡單又直觀。只需在每個隱藏層的激活函數之前或之后添加一個BatchNormalization層,然后可選地在模型的第一層后添加一個BN層。例如,此模型在每個隱藏層之后和在模型的第一層(展平輸入圖像之后)應用BN:
model = keras.models.Sequential([keras.layers.Flatten(input_shape=[28, 28]),keras.layers.BatchNormalization(),keras.layers.Dense(300, activation="elu", kernel_initializer="he_normal"),keras.layers.BatchNormalization(),keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal"),keras.layers.BatchNormalization(),keras.layers.Dense(10, activation="softmax")])就這樣!在這個只有兩個隱藏層的小例子中,批量歸一化不可能產生非常積極的影響。但是對于更深層的網絡,它可以帶來巨大的改變。
讓我們顯示一下模型摘要:
如你所見,每個BN層的每個輸入添加了四個參數:γ,β,μ 和 σ(例如,第一個BN層添加了3136個參數,即4×784)。最后兩個參數μ和σ是移動平均值。它們不受反向傳播的影響,因此Keras稱其為“不可訓練”9(如果你計算BN參數的總數3,136+ 1,200 + 400,然后除以2,則得到2,368,即此模型中不可訓練參數的總數)。
讓我們看一下第一個BN層的參數。兩個是可訓練的(通過反向傳播),兩個不是:
>>> [(var.name, var.trainable) for var inmodel.layers[1].variables][('batch_normalization_v2/gamma:0', True),('batch_normalization_v2/beta:0', True),('batch_normalization_v2/moving_mean:0', False),('batch_normalization_v2/moving_variance:0', False)]現在,當你在Keras中創建BN層時,它還會創建兩個操作,在訓練期間的每次迭代中,Keras都會調用這兩個操作。這些操作會更新移動平均值。由于我們使用的是TensorFlow后端,因此這些操作是TensorFlow操作(我們將在第12章中討論TF操作):
>>> model.layers[1].updates[<tf.Operation 'cond_2/Identity' type=Identity>,<tf.Operation 'cond_3/Identity' type=Identity>]BN論文的作者主張在激活函數之前而不是之后添加BN層(就像我們剛才所做的那樣)。關于此問題,存在一些爭論,哪個更好取決于你的任務—你也可以對此進行試驗,看看哪個選擇最適合你的數據集。要在激活函數之前添加BN層,你必須從隱藏層中刪除激活函數,并將其作為單獨的層添加到BN層之后。此外,由于批量歸一化層的每個輸入都包含一個偏移參數,因此你可以從上一層中刪除偏置項(創建時只需傳遞use_bias = False即可):
model = keras.models.Sequential([keras.layers.Flatten(input_shape=[28,28]),keras.layers.BatchNormalization(),keras.layers.Dense(300,kernel_initializer="he_normal", use_bias=False),keras.layers.BatchNormalization(),keras.layers.Activation("elu"),keras.layers.Dense(100,kernel_initializer="he_normal", use_bias=False),keras.layers.BatchNormalization(),keras.layers.Activation("elu"),keras.layers.Dense(10,activation="softmax")])BatchNormalization類具有許多可以調整的超參數。默認值通??梢?#xff0c;但是你偶爾可能需要調整動量(momentum)。BatchNormalization層在更新指數移動平均值時使用此超參數。給定一個新值V(即在當前批次中計算的輸入均值或標準差的新向量),該層使用以下公式來更新運行時平均:
momentum? momentum?
一個良好的動量值通常接近1;例如0.9、0.99或0.999(對于較大的數據集和較小的批處理,你需要更多的9)。
另一個重要的超參數是軸:它確定哪個軸應該被歸一化。默認為 -1,這意味著默認情況下它將對最后一個軸進行歸一化(使用跨其他軸計算得到的均值和標準差)。當輸入批次為2D(即批次形狀為[批次大小,特征])時,這意味著將基于在批次中所有實例上計算得到的平均值和標準偏差對每個輸入特征進行歸一化。例如,先前代碼示例中的第一個BN層將獨立地歸一化(重新縮放和偏移)784個輸入特征中的每一個。如果將第一個BN層移動到Flatten層之前,則輸入批次將為3D,形狀為 [批次大小,高度,寬度];因此,BN層將計算28個均值和28個標準差(每列像素1個,在批次中的所有實例以及在列中所有行之間計算),它將使用相同的平均值和標準偏差對給定列中的所有像素進行歸一化。也是只有28個縮放參數和28個偏移參數。相反,你如果仍然要獨立的處理784個像素中的每一個,則你應設置axis = [1、2]。
請注意,BN層在訓練期間和訓練后不會執行相同的計算:它在訓練期間使用批處理統計信息,在訓練后使用``最終'的'統計信息(即移動平均的最終值)。讓我們看一下這個類的源代碼,看看如何處理:
class BatchNormalization(keras.layers.Layer):[...]def call(self, inputs,training=None):[...]如你所見,call ()方法是執行計算的方法,它有一個額外的訓練參數,默認情況下將其設置為None,但是在訓練過程中fit()方法將其設置為1。如果你需要編寫自定義層,它的行為在訓練和測試期間必須有所不同,把一個訓練參數添加到call()方法中,并在該方法中使用這個參數來決定要計算的內容10(我們將在第12章中討論自定義層)。
BatchNormalization已成為深度神經網絡中最常用的層之一,以至于在圖表中通常將其省略,因為假定在每層之后都添加了BN。但是Hongyi Zhang等人最近的論文可能會改變這一假設11:通過使用一種新穎的fixed-update(fixup)權重初始化技術,作者設法訓練了一個非常深的神經網絡(10,000層!),沒有使用BN,在復雜的圖像分類任務上實現了最先進的性能。但是,由于這是一項前沿研究,因此你在放棄批量歸一化之前,可能需要等待其他研究來確認此發現。
1.4 梯度裁剪
緩解梯度爆炸問題的另一種流行技術是在反向傳播期間裁剪梯度,使它們永遠不會超過某個閾值。這稱為“梯度裁剪”(Gradient Clipping)12。這種技術最常用于循環神經網絡,因為在RNN中難以使用“批量歸一化”,正如我們將在第15章中看到的那樣。對于其他類型的網絡,BN通常就足夠了。
在Keras中,實現梯度裁剪僅僅是一個在創建優化器時設置clipvalue或clipnorm參數的問題,例如:
optimizer = keras.optimizers.SGD(clipvalue=1.0)model.compile(loss="mse", optimizer=optimizer)該優化器會將梯度向量的每個分量都裁剪為 -1.0和1.0之間的值。這意味著所有損失的偏導數(相對與每個可訓練的參數)將限制在 -1.0和1.0之間。閾值是你可以調整的超參數。注意,它可能會改變梯度向量的方向。例如,如果原始梯度向量為[0.9,100.0],則其大部分指向第二個軸的方向;但是按值裁剪后,將得到[0.9,1.0],該點大致指向兩個軸之間的對角線。實際上,這種方法行之有效。如果要確?!疤荻炔眉簟辈桓奶荻认蛄康姆较?#xff0c;你應該通過設置clipnorm而不是clipvalue按照范數來裁剪。如果?2范數大于你選擇的閾值,則會裁剪整個梯度。例如,如果你設置clipnorm = 1.0,則向量[0.9,100.0]將被裁剪為[0.00899964,0.9999595],保留其方向,但幾乎消除了第一個分量。如果你觀察到了梯度在訓練過程中爆炸(你可以使用TensorBoard跟蹤梯度的大小),你可能要嘗試使用兩種方法,按值裁剪和按范數裁剪,看看哪個選擇在驗證集上表現更好。?
福利時間
以上內容摘自《機器學習實戰:基于Scikit-Learn、Keras和TensorFlow(原書第2版)》一書,經出版方授權發布。
《機器學習實戰:基于Scikit-Learn、Keras和TensorFlow(原書第2版)》
讀者可以進群抽獎,10月24日23:00開獎,中獎的三位讀者各贈書一本。
當然也可以自行購買:
總結
以上是生活随笔為你收集整理的【深度学习】梯度消失和梯度爆炸问题的最完整解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【数据集下载神器】体验1000+优质数据
- 下一篇: 专家:电脑看多了掉头发怎么办?