當(dāng)你花了幾個星期構(gòu)建一個數(shù)據(jù)集、編碼一個神經(jīng)網(wǎng)絡(luò)并訓(xùn)練好了模型,然后發(fā)現(xiàn)結(jié)果并不理想,接下來你會怎么做?
深度學(xué)習(xí)通常被視為一個黑盒子,我并不反對這種觀點(diǎn)——但是你能講清楚學(xué)到的上萬參數(shù)的意義嗎?
但是黑盒子的觀點(diǎn)為機(jī)器學(xué)習(xí)從業(yè)者指出了一個明顯的問題:你如何調(diào)試模型?
在這篇文章中,我將會介紹一些我們在 Cardiogram 中調(diào)試 DeepHeart 時用到的技術(shù),DeepHeart 是使用來自 Apple Watch、 Garmin、和 WearOS 的數(shù)據(jù)預(yù)測疾病的。
在 Cardiogram 中,我們認(rèn)為構(gòu)建 DNN 并不是煉金術(shù),而是工程學(xué)。
你的心臟暴露了很多你的信息。DeepHeart 使用來自 Apple Watch、 Garmin、和 WearOS 的心率數(shù)據(jù)來預(yù)測你患糖尿病、高血壓以及睡眠窒息癥(sleep apnea)的風(fēng)險。
一、預(yù)測合成輸出
通過預(yù)測根據(jù)輸入數(shù)據(jù)構(gòu)建的合成輸出任務(wù)來測試模型能力。
我們在構(gòu)建檢測睡眠窒息癥的模型時使用了這個技術(shù)?,F(xiàn)有關(guān)于睡眠窒息癥篩查的文獻(xiàn)使用日間和夜間心率標(biāo)準(zhǔn)差的差異作為篩查機(jī)制。因此我們?yōu)槊恐艿妮斎霐?shù)據(jù)創(chuàng)建了合成輸出任務(wù):
標(biāo)準(zhǔn)差 (日間心率)—標(biāo)準(zhǔn)差 (夜間心率)
為了學(xué)習(xí)這個函數(shù),模型要能夠:
1. 區(qū)分白天和黑夜
2. 記住過去幾天的數(shù)據(jù)
這兩個都是預(yù)測睡眠窒息癥的先決條件,所以我們使用新架構(gòu)進(jìn)行實驗的第一步就是檢查它是否能學(xué)習(xí)這個合成任務(wù)。
你也可以通過在合成任務(wù)上預(yù)訓(xùn)練網(wǎng)絡(luò),以半監(jiān)督的形式來使用類似這樣的合成任務(wù)。當(dāng)標(biāo)記數(shù)據(jù)很稀缺,而你手頭有大量未標(biāo)記數(shù)據(jù)時,這種方法很有用。
二、可視化激活值
理解一個訓(xùn)練好的模型的內(nèi)部機(jī)制是很難的。你如何理解成千上萬的矩陣乘法呢?
在這篇優(yōu)秀的 Distill 文章《Four Experiments in Handwriting with a Neural Network》中,作者通過在熱圖中繪制單元激活值,分析了手寫模型。我們發(fā)現(xiàn)這是一個「打開 DNN 引擎蓋」的好方法。
我們檢查了網(wǎng)絡(luò)中幾個層的激活值,希望能夠發(fā)現(xiàn)一些語義屬性,例如,當(dāng)用戶在睡覺、工作或者焦慮時,激活的單元是怎樣的?
用 Keras 寫的從模型中提取激活值的代碼很簡單。下面的代碼片段創(chuàng)建了一個 Keras 函數(shù) last_output_fn,該函數(shù)在給定一些輸入數(shù)據(jù)的情況下,能夠獲得一層的輸出(即它的激活值)。
fromkerasimportbackendasKdefextract_layer_output(model,layer_name,input_data):layer_output_fn=K.function([model.layers[0].input],[model.get_layer(layer_name).output])layer_output=layer_output_fn([input_data])#layer_output.shapeis(num_units,num_timesteps)returnlayer_output[0]
我們可視化了網(wǎng)絡(luò)好幾層的激活值。在檢查第二個卷積層(一個寬為 128 的時間卷積層)的激活值時,我們注意到了一些奇怪的事:
卷積層的每個單元在每個時間步長上的激活值。藍(lán)色的陰影代表的是激活值。
激活值竟然不是隨著時間變化的!它們不受輸入值影響,被稱為「死神經(jīng)元」。
ReLU 激活函數(shù),f(x) = max(0, x)
這個架構(gòu)使用了 激活函數(shù),當(dāng)輸入是負(fù)數(shù)的時候它輸出的是 0。盡管它是這個神經(jīng)網(wǎng)絡(luò)中比較淺的層,但是這確實是實際發(fā)生的事情。
在訓(xùn)練的某些時候,較大的梯度會把某一層的所有偏置項都變成負(fù)數(shù),使得 ReLU 函數(shù)的輸入是很小的負(fù)數(shù)。因此這層的輸出就會全部為 0,因為對小于 0 的輸入來說,ReLU 的梯度為零,這個問題無法通過來解決。
當(dāng)一個卷積層的輸出全部為零時,后續(xù)層的單元就會輸出其偏置項的值。這就是這個層每個單元輸出一個不同值的原因——因為它們的偏置項不同。
我們通過用 Leaky ReLU 替換 ReLU 解決了這個問題,前者允許梯度傳播,即使輸入為負(fù)時。
我們沒想到會在此次分析中發(fā)現(xiàn)「死神經(jīng)元」,但最難找到的錯誤是你沒打算找的。
三、梯度分析
梯度的作用當(dāng)然不止是優(yōu)化損失函數(shù)。在梯度下降中,我們計算與Δparameter 對應(yīng)的Δloss。盡管通常意義上梯度計算的是改變一個變量對另一個變量的影響。由于梯度計算在梯度下降方法中是必需的,所以像 TensorFlow 這樣的框架都提供了計算梯度的函數(shù)。
我們使用梯度分析來確定我們的深度神經(jīng)網(wǎng)絡(luò)能否捕捉數(shù)據(jù)中的長期依賴。DNN 的輸入數(shù)據(jù)特別長:4096 個時間步長的心率或者計步數(shù)據(jù)。我們的模型架構(gòu)能否捕捉數(shù)據(jù)中的長期依賴非常重要。例如,心率的恢復(fù)時間可以預(yù)測糖尿病。這就是鍛煉后恢復(fù)至休息時的心率所耗的時間。為了計算它,深度神經(jīng)網(wǎng)絡(luò)必須能夠計算出你休息時的心率,并記住你結(jié)束鍛煉的時間。
衡量模型能否追蹤長期依賴的一種簡單方法是去檢查輸入數(shù)據(jù)的每個時間步長對輸出預(yù)測的影響。如果后面的時間步長具有特別大的影響,則說明模型沒有有效地利用早期數(shù)據(jù)。
對于所有時間步長 t,我們想要計算的梯度是與Δinput_t 對應(yīng)的Δoutput。下面是用 Keras 和 TensorFlow 計算這個梯度的代碼示例:
defgradient_output_wrt_input(model,data):#[:,2048,0]meansallusersinbatch,midpointtimestep,0thtask(diabetes)output_tensor=model.model.get_layer('raw_output').output[:,2048,0]#output_tensor.shape==(num_users)#Averageoutputoverallusers.Resultisascalar.output_tensor_sum=tf.reduce_mean(output_tensor)inputs=model.model.inputs#(num_usersxnum_timestepsxnum_input_channels)gradient_tensors=tf.gradients(output_tensor_sum,inputs)#gradient_tensors.shape==(num_usersxnum_timestepsxnum_input_channels)#Averageoverusersgradient_tensors=tf.reduce_mean(gradient_tensors,axis=0)#gradient_tensors.shape==(num_timestepsxnum_input_channels)#eggradient_tensor[10,0]isderivoflastoutputwrt10thinputheartrate#ConverttoKerasfunctionk_gradients=K.function(inputs=inputs,outputs=gradient_tensors)#Applyfunctiontodatasetreturnk_gradients([data.X])
在上面的代碼中,我們在平均池化之前,在中點(diǎn)時間步長 2048 處計算了輸出。我們之所以使用中點(diǎn)而不是最后的時間步長的原因是,我們的 LSTM 單元是雙向的,這意味著對一半的單元來說,4095 實際上是第一個時間步長。我們將得到的梯度進(jìn)行了可視化:
Δoutput_2048 / Δinput_t
請注意我們的 y 軸是 log 尺度的。在時間步長 2048 處,與輸入對應(yīng)的輸出梯度是 0.001。但是在時間步長 2500 處,對應(yīng)的梯度小了一百萬倍!通過梯度分析,我們發(fā)現(xiàn)這個架構(gòu)無法捕捉長期依賴。
四、分析模型預(yù)測
你可能已經(jīng)通過觀察像 AUROC 和平均絕對誤差這樣的指標(biāo)分析了模型預(yù)測。你還可以用更多的分析來理解模型的行為。
例如,我們好奇 DNN 是否真的用心率輸入來生成預(yù)測,或者說它的學(xué)習(xí)是不是嚴(yán)重依賴于所提供的元數(shù)據(jù)——我們用性別、年齡這樣的用戶元數(shù)據(jù)來初始化 LSTM 的狀態(tài)。為了理解這個,我們將模型與在元數(shù)據(jù)上訓(xùn)練的 logistic 回歸模型做了對比。
DNN 模型接收了一周的用戶數(shù)據(jù),所以在下面的散點(diǎn)圖中,每個點(diǎn)代表的是一個用戶周。
這幅圖驗證了我們的猜想,因為預(yù)測結(jié)果并不是高度相關(guān)的。
除了進(jìn)行匯總分析,查看最好和最壞的樣本也是很有啟發(fā)性的。對一個二分類任務(wù)而言,你需要查看最令人震驚的假陽性和假陰性(也就是預(yù)測距離標(biāo)簽最遠(yuǎn)的情況)。嘗試鑒別損失模式,然后過濾掉在你的真陽性和真陰性中出現(xiàn)的這種模式。
一旦你對損失模式有了假設(shè),就通過分層分析進(jìn)行測試。例如,如果最高損失全部來自第一代 Apple Watch,我們可以用第一代 Apple Watch 計算我們的調(diào)優(yōu)集中用戶集的準(zhǔn)確率指標(biāo),并將這些指標(biāo)與在剩余調(diào)優(yōu)集上計算的指標(biāo)進(jìn)行比較。
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4771瀏覽量
100712 -
機(jī)器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8406瀏覽量
132558 -
數(shù)據(jù)集
+關(guān)注
關(guān)注
4文章
1208瀏覽量
24689
原文標(biāo)題:你用什么方法調(diào)試深度神經(jīng)網(wǎng)絡(luò)?這里有四種簡單的方式哦
文章出處:【微信號:tyutcsplab,微信公眾號:智能感知與物聯(lián)網(wǎng)技術(shù)研究所】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論