操:從sigmoid到交叉熵的二分類深度學(xué)習(xí)入門)
說起深度學(xué)習(xí)課程里的“實(shí)驗(yàn)三”十有八九會(huì)撞上這樣一句話用 logistic 回歸預(yù)測(cè)二分類。我見過不少同學(xué)看到題目后的第一反應(yīng)是“這算哪門子深度學(xué)習(xí)不就是個(gè)線性模型套了個(gè)激活函數(shù)嗎”真等自己上手跑完才發(fā)現(xiàn)這個(gè)實(shí)驗(yàn)從頭到尾踩的坑比后面好多大型網(wǎng)絡(luò)實(shí)驗(yàn)加起來都要多。其實(shí) logistic 回歸是理解整個(gè)深度學(xué)習(xí)最理想的一顆釘子。從網(wǎng)絡(luò)結(jié)構(gòu)上看它就是一個(gè)沒有隱藏層的單神經(jīng)元模型輸入特征做加權(quán)求和過一個(gè) sigmoid 激活輸出一個(gè) 0 到 1 之間的概率。這套流程你吃透了后續(xù)學(xué)多層感知機(jī)、CNN、RNN 時(shí)就會(huì)發(fā)現(xiàn)大家都是在同一個(gè)骨架上做文章只是把單神經(jīng)元變成了大網(wǎng)絡(luò)把線性加權(quán)換成了卷積或循環(huán)結(jié)構(gòu)。當(dāng)年我?guī)б晃涣慊A(chǔ)的 A 同學(xué)做這個(gè)實(shí)驗(yàn)他花了一個(gè)下午把前向傳播、損失函數(shù)、梯度更新通了隔周再去看多層網(wǎng)絡(luò)的反向傳播明顯比別人輕松一大截。這篇文章我會(huì)把這個(gè)實(shí)驗(yàn)從原理到實(shí)操完整拆開把 sigmoid 為什么長(zhǎng)這樣、交叉熵為什么比均方誤差好用、數(shù)據(jù)預(yù)處理怎么做不泄露、訓(xùn)練曲線怎么看這些“老師不細(xì)講但考試和工作都要考”的東西一次性說清楚。適合正在做課程實(shí)驗(yàn)的同學(xué)也適合想回頭把這顆地基補(bǔ)扎實(shí)的自學(xué)者。1. 實(shí)驗(yàn)整體認(rèn)知與設(shè)計(jì)思路1.1 實(shí)驗(yàn)三為什么偏偏選 logistic 回歸很多課程的實(shí)驗(yàn)順序都是精心排過的把 logistic 回歸放在第三個(gè)實(shí)驗(yàn)剛好卡在“你已經(jīng)會(huì)線性回歸了但還沒接觸神經(jīng)網(wǎng)絡(luò)”的中間地帶。它有兩個(gè)身份對(duì)經(jīng)典機(jī)器學(xué)習(xí)來說它是一個(gè)線性分類器對(duì)深度學(xué)習(xí)來說它是神經(jīng)網(wǎng)絡(luò)最樸素的雛形。從結(jié)構(gòu)上看logistic 回歸做的事情極其簡(jiǎn)單把每個(gè)特征乘上一個(gè)權(quán)重累加再加一個(gè)偏置然后過一個(gè) sigmoid 函數(shù)把結(jié)果壓到 0 到 1 之間。這個(gè)結(jié)構(gòu)像什么就是一個(gè)最精簡(jiǎn)的神經(jīng)元。學(xué)完這個(gè)單神經(jīng)元下一步加一層隱藏層就是兩層神經(jīng)網(wǎng)絡(luò)再加幾層就變成了一個(gè)標(biāo)準(zhǔn)的多層感知機(jī)。所以實(shí)驗(yàn)安排這門課的人真實(shí)意圖是讓你先用最少的代碼把前向傳播、損失計(jì)算、梯度反傳、參數(shù)更新這條完整流水線走一遍。這個(gè)實(shí)驗(yàn)的定位決定了它的學(xué)習(xí)重點(diǎn)不在模型本身的精度而在于理解訓(xùn)練過程。說實(shí)話二分類任務(wù)用 logistic 回歸去跑很多時(shí)候精度也不會(huì)特別高尤其是數(shù)據(jù)不是線性可分的時(shí)候它的表現(xiàn)可能就是“夠用”。但正因?yàn)槟P秃?jiǎn)單所有中間過程都可以被清清楚楚地打印出來、畫出來、算出來——權(quán)重怎么變、損失怎么降、決策邊界怎么移動(dòng)全部都直觀可見。這是深網(wǎng)絡(luò)給不了你的學(xué)習(xí)體驗(yàn)。1.2 二分類預(yù)測(cè)的建模思路二分類在現(xiàn)實(shí)中隨處可見一封郵件是不是垃圾郵件一個(gè)用戶會(huì)不會(huì)下單一個(gè)病人是不是得了某種疾病本質(zhì)上都是“二選一”的判斷題。這個(gè)實(shí)驗(yàn)要做的事情就是給出一組樣本和它們的標(biāo)簽訓(xùn)練一個(gè)模型讓模型在面對(duì)沒見過的樣本時(shí)能給出一個(gè)正確的二分類判斷。這里有一個(gè)初學(xué)者很容易誤解的地方模型輸出并不是直接的 0 或 1而是一個(gè)概率值。比如模型對(duì)某條樣本輸出 0.87意思就是“我覺得它有 87% 的概率屬于類別 1”。然后我們?cè)偃藶榧s定一個(gè)閾值——通常是 0.5——概率大于 0.5 判定為類別 1否則判定為類別 0。后面我還會(huì)講到這個(gè)閾值不是固定死的不同業(yè)務(wù)場(chǎng)景下調(diào)閾值可能比調(diào)模型參數(shù)還管用。整個(gè)實(shí)驗(yàn)的建模流程可以歸納成一條清晰的流水線數(shù)據(jù)準(zhǔn)備拿到特征矩陣 X形狀是 樣本數(shù)×特征數(shù)和標(biāo)簽向量 y形狀是 樣本數(shù)×1。數(shù)據(jù)預(yù)處理標(biāo)準(zhǔn)化、劃分訓(xùn)練集和測(cè)試集。模型定義初始化權(quán)重 W 和偏置 b寫出前向傳播函數(shù)。損失計(jì)算用交叉熵衡量預(yù)測(cè)概率與真實(shí)標(biāo)簽的差距。梯度計(jì)算與參數(shù)更新讓模型在訓(xùn)練數(shù)據(jù)上一點(diǎn)點(diǎn)往正確的方向調(diào)整。評(píng)估在測(cè)試集上驗(yàn)證泛化效果。建議你在動(dòng)手寫代碼之前先在紙上把這個(gè)流程走一遍把每一步的輸入輸出形狀寫清楚。很多人寫著寫著就亂了回頭查 bug 半天最后發(fā)現(xiàn)是某個(gè)張量的維度沒對(duì)上。2. 核心原理拆解sigmoid 與交叉熵的搭檔邏輯2.1 從線性回歸到概率輸出sigmoid 到底在做什么先回憶一下線性回歸給定特征 x1、x2…… 模型算出一個(gè)分?jǐn)?shù) z w1x1 w2x2 ... b。在回歸任務(wù)里這個(gè) z 可以直接作為預(yù)測(cè)值。但二分類任務(wù)不行——分類要輸出的是概率概率有天然的取值范圍必須在 0 到 1 之間而線性回歸的 z 范圍是從負(fù)無窮到正無窮直接拿來當(dāng)概率是不合理的。sigmoid 函數(shù)就是來解決這個(gè)問題的公式是σ(z) 1 / (1 e^(-z))它的作用是把任意實(shí)數(shù) z 壓縮到 (0, 1) 區(qū)間。z 越大輸出越接近 1z 越小輸出越接近 0z 恰好為 0 時(shí)輸出為 0.5。這個(gè)函數(shù)圖像是一條平滑的 S 型曲線所以它本質(zhì)上做的是一件非常優(yōu)雅的事情把“證據(jù)的強(qiáng)度”轉(zhuǎn)化成“某種結(jié)果發(fā)生的概率”。舉個(gè)例子感受一下假設(shè)某個(gè)特征的權(quán)重是 0.8x 每增加一個(gè)單位z 就增加 0.8sigmoid 輸出就會(huì)明顯地向 1 靠攏。相當(dāng)于模型每看到一點(diǎn)新的證據(jù)就它對(duì)類別 1 的信心就更足一點(diǎn)而且這種信心的增長(zhǎng)不是線性的——靠近中間區(qū)域時(shí)變化快到了極值附近就飽和了這和人類的直覺也蠻契合的。在代碼里實(shí)現(xiàn)它也非常簡(jiǎn)單def sigmoid(z): return 1 / (1 np.exp(-z))不過這里有個(gè)小細(xì)節(jié)如果你在損失函數(shù)里面用了 log(p)p 可能被算成 0log(0) 會(huì)直接報(bào)警。后面我會(huì)講怎么用數(shù)值技巧規(guī)避。2.2 決策邊界為什么是線性的把前向傳播的公式展開來看p σ(w1x1 w2x2 ... b)當(dāng)我們把判定閾值設(shè)為 0.5也就是 p 0.5 時(shí)預(yù)測(cè)為類別 1那實(shí)際上等價(jià)于看 z w1x1 w2x2 ... b 是否大于 0。z 0 這條線就是模型的決策邊界。因?yàn)?z 是特征的線性組合所以 z 0 在二維空間里是一條直線在三維空間里是一個(gè)平面在高維空間里是一個(gè)超平面。這給 logistic 回歸帶來了兩個(gè)鮮明的特點(diǎn)一是可解釋性極強(qiáng)。每個(gè)特征的權(quán)重 W 的正負(fù)方向直接告訴我們這個(gè)特征對(duì)預(yù)測(cè)結(jié)果是正向推動(dòng)還是反向推動(dòng)絕對(duì)值大小代表影響強(qiáng)度。這是很多樹模型和深度網(wǎng)絡(luò)做不到的。二是模型容量有限。當(dāng)數(shù)據(jù)本身線性不可分時(shí)比如兩類樣本像陰陽魚一樣糾纏在一起單純一個(gè)線性決策邊界是搞不定的。這時(shí)你會(huì)看到訓(xùn)練精度卡在一個(gè)不上不下的位置怎么調(diào)學(xué)習(xí)率都沒用。這不是 bug而是模型表達(dá)能力的限制后續(xù)課程通常會(huì)通過引入隱藏層或者核技巧來解決。2.3 交叉熵為什么完勝均方誤差訓(xùn)練模型需要定義一個(gè)損失函數(shù)來指導(dǎo)“往哪個(gè)方向調(diào)整參數(shù)”。二分類最常用的損失是交叉熵L -[y·log(p) (1-y)·log(1-p)]拆開看當(dāng)真實(shí)標(biāo)簽 y 1 時(shí)損失變?yōu)?-log(p)p 越接近 1損失越小當(dāng) y 0 時(shí)損失變?yōu)?-log(1-p)p 越接近 0損失越小。換句話說模型越自信地猜錯(cuò)懲罰越大越接近正確答案損失越趨近于 0。這種“對(duì)數(shù)懲罰”的特性讓模型不敢在錯(cuò)誤的方向上過度自信。為什么不直接用均方誤差 MSE (y - p)2 呢這是實(shí)驗(yàn)報(bào)告里特別愛問的一道題。關(guān)鍵在于梯度。把 sigmoid 代入 MSE 之后梯度里會(huì)冒出一個(gè) σ(z) σ(z)·(1-σ(z)) 項(xiàng)。這個(gè)項(xiàng)有個(gè)致命弱點(diǎn)當(dāng) z 很大的時(shí)候σ 接近 1σ(1-σ) 接近 0當(dāng) z 很小的時(shí)候σ 接近 0σ(1-σ) 也接近 0。也就是說模型在預(yù)測(cè)得越極端、越自信的時(shí)候梯度反而越小訓(xùn)練變得極其緩慢這在深度學(xué)習(xí)中叫“梯度飽和”。而交叉熵配合 sigmoid 之后求導(dǎo)結(jié)果很漂亮梯度變成了 (p - y)·x。這個(gè)式子直觀又合理——預(yù)測(cè)和真實(shí)標(biāo)簽差距越大梯度的幅度越大模型學(xué)得越快預(yù)測(cè)接近正確時(shí)梯度自然變小。這也是為什么深度學(xué)習(xí)里幾乎不會(huì)用 MSE 來做分類任務(wù)的損失函數(shù)。所以你在實(shí)驗(yàn)里如果不小心把損失換成了 MSE會(huì)觀察到訓(xùn)練半天損失都降不下去不是代碼 bug而是數(shù)學(xué)選型出了問題。建議你在實(shí)驗(yàn)中分別用交叉熵和 MSE 各跑一次把損失下降曲線對(duì)比一下。這個(gè)對(duì)比實(shí)驗(yàn)做出來你對(duì)這個(gè)知識(shí)點(diǎn)的理解會(huì)比只看書深刻得多。3. 實(shí)操全流程從數(shù)據(jù)準(zhǔn)備到訓(xùn)練閉環(huán)3.1 數(shù)據(jù)準(zhǔn)備與預(yù)處理的幾個(gè)細(xì)節(jié)做這個(gè)實(shí)驗(yàn)我建議你先不要直接上復(fù)雜數(shù)據(jù)集而是自己生成一批可分的二維模擬數(shù)據(jù)。好處是直觀后面畫決策邊界時(shí)一眼就能看出效果。比如生成兩堆數(shù)據(jù)類別 0 以某個(gè)點(diǎn)為中心呈高斯分布類別 1 在另一個(gè)點(diǎn)附近分布讓它們之間有一條清晰的分界線。拿到原始數(shù)據(jù)后有幾步預(yù)處理是整個(gè)實(shí)驗(yàn)成敗的關(guān)鍵很多人第一步就跑偏了。特征標(biāo)準(zhǔn)化大概是這個(gè)實(shí)驗(yàn)里第二重要的預(yù)處理操作。如果 x1 的取值范圍是 0 到 1而 x2 的范圍是 0 到 10000那權(quán)重更新時(shí)會(huì)被大尺度的特征主導(dǎo)梯度下降會(huì)在某些方向上震蕩收斂速度極慢。標(biāo)準(zhǔn)做法是把每個(gè)特征減去均值、除以標(biāo)準(zhǔn)差讓數(shù)據(jù)分布在一個(gè)相近的尺度上。至于先劃分?jǐn)?shù)據(jù)集還是先標(biāo)準(zhǔn)化這里有一個(gè)很容易被忽略的坑。正確順序是先把數(shù)據(jù)拆成訓(xùn)練集和測(cè)試集然后用訓(xùn)練集的均值和標(biāo)準(zhǔn)差去標(biāo)準(zhǔn)化訓(xùn)練集和測(cè)試集。絕對(duì)不要在完整數(shù)據(jù)集上先算均值方差再做標(biāo)準(zhǔn)化因?yàn)檫@樣測(cè)試集的信息會(huì)偷偷混進(jìn)訓(xùn)練階段評(píng)估結(jié)果會(huì)虛高這種現(xiàn)象叫數(shù)據(jù)泄露。實(shí)驗(yàn)數(shù)據(jù)的隨機(jī)性本就是這個(gè)實(shí)驗(yàn)要模擬的真實(shí)情況一旦泄露你后面調(diào)參得到的所有結(jié)論都不可信。我見過不止一個(gè)同學(xué)在報(bào)告里寫“我的模型在測(cè)試集上準(zhǔn)確率 98%”結(jié)果代碼被細(xì)看后發(fā)現(xiàn)他是把數(shù)據(jù)整體標(biāo)準(zhǔn)化之后才劃分的測(cè)試集。做實(shí)驗(yàn)不是只看數(shù)字而是要確保數(shù)字背后的邏輯站得住腳。劃分?jǐn)?shù)據(jù)時(shí)還有一個(gè)小建議二分類數(shù)據(jù)最好用分層采樣保證訓(xùn)練集和測(cè)試集里的正負(fù)樣本比例大致一致否則隨機(jī)切分完全可能把某個(gè)類別的樣本都切到測(cè)試集里去。3.2 模型定義與前向傳播logistic 回歸的前向傳播說白了就是三行核心公式# 輸入 X: (n_samples, n_features) # 權(quán)重 W: (n_features, 1) # 偏置 b: 標(biāo)量 # 1. 線性加權(quán) z X W b # 2. 通過 sigmoid 得到概率 p sigmoid(z)這里X W是矩陣乘法的標(biāo)準(zhǔn)寫法如果你用的是帶自動(dòng)求導(dǎo)的深度學(xué)習(xí)框架這行代碼基本不用改只是封裝成網(wǎng)絡(luò)層的寫法會(huì)更簡(jiǎn)潔。但我始終建議至少手寫一遍不借助自動(dòng)求導(dǎo)的實(shí)現(xiàn)因?yàn)槟阒挥杏H手寫出梯度才能真正理解反向傳播的細(xì)節(jié)。參數(shù)初始化方面logistic 回歸里把 W 設(shè)成全 0 理論上是可行的因?yàn)槟P椭挥幸粋€(gè)神經(jīng)元不像多層網(wǎng)絡(luò)那樣存在對(duì)稱性問題。但為了和后續(xù)深度學(xué)習(xí)實(shí)驗(yàn)保持習(xí)慣一致我還是建議用小的隨機(jī)數(shù)初始化幅度控制在 0.01 左右。這樣寫的好處是你以后改造成多層網(wǎng)絡(luò)時(shí)不需要重新適應(yīng)初始化規(guī)則。構(gòu)建完模型后先用一個(gè)樣本做前向傳播手動(dòng)算一遍概率和損失確認(rèn)維度都正確之后再開始訓(xùn)練。這個(gè)“先手工算一遍”的習(xí)慣能幫你省下大量調(diào)試時(shí)間。3.3 訓(xùn)練循環(huán)的搭建與監(jiān)控訓(xùn)練過程是一個(gè)不斷重復(fù)的循環(huán)核心動(dòng)作可以歸納為三件事做一次前向傳播、算一次損失、更新一次參數(shù)。用偽代碼寫出來非常清晰epochs 500 learning_rate 0.1 for epoch in range(epochs): # 前向 p sigmoid(X_train W b) # 損失加一個(gè)小 epsilon 防止 log(0) loss -np.mean(y * np.log(p 1e-8) (1 - y) * np.log(1 - p 1e-8)) # 梯度推導(dǎo)結(jié)果就是 X^T (p-y) 除以樣本數(shù) grad_w X_train.T (p - y) / n_samples grad_b np.mean(p - y) # 參數(shù)更新 W - learning_rate * grad_w b - learning_rate * grad_b # 每 20 輪打印一次狀態(tài) if epoch % 20 0: acc ((p 0.5).reshape(-1) y).mean() print(fepoch {epoch}, loss {loss:.4f}, acc {acc:.4f})關(guān)于這個(gè)梯度公式 grad_w X_train.T (p - y) / n_samples我第一次推出來的時(shí)候覺得挺驚喜的logistic 回歸的梯度形式竟然和線性回歸的梯度形式長(zhǎng)得很像都是“(預(yù)測(cè)值 - 真實(shí)值) 再乘上特征”。這背后的邏輯是交叉熵和 sigmoid 這一對(duì)組合恰好讓非線性激活函數(shù)帶來的影響在梯度里被抵消掉了只剩下一項(xiàng)線性的誤差信號(hào)。訓(xùn)練循環(huán)里有一個(gè)地方值得單獨(dú)強(qiáng)調(diào)你應(yīng)該同時(shí)記錄訓(xùn)練集和驗(yàn)證集的損失與準(zhǔn)確率。很多人只盯著訓(xùn)練集看看到一個(gè)漂亮的收斂曲線就以為大功告成其實(shí)訓(xùn)練集曲線再漂亮也不能說明泛化能力只有驗(yàn)證集曲線才能看出模型是否過擬合。還有 batch size 的概念。上面這個(gè)循環(huán)是每次把所有數(shù)據(jù)都拿進(jìn)來更新一次梯度這叫全量梯度下降數(shù)據(jù)量小的時(shí)候沒問題。如果數(shù)據(jù)量有幾萬條每次全量計(jì)算會(huì)很慢可以每次隨機(jī)抽一小批數(shù)據(jù)比如 32 或 64 條來更新參數(shù)這就是 mini-batch 梯度下降。實(shí)驗(yàn)階段用全量就夠了但如果你將來做大模型訓(xùn)練會(huì)發(fā)現(xiàn) mini-batch 幾乎是標(biāo)配。4. 訓(xùn)練調(diào)參與效果分析4.1 學(xué)習(xí)率怎么調(diào)別在玄學(xué)里浪費(fèi)半天學(xué)習(xí)率大概是初學(xué)者在實(shí)驗(yàn)中花費(fèi)時(shí)間最多、也最容易讓人抓狂的一個(gè)超參數(shù)。我自己常用的策略是先把學(xué)習(xí)率固定在一個(gè)基準(zhǔn)值上跑一遍觀察損失曲線的形態(tài)再?zèng)Q定調(diào)大還是調(diào)小。學(xué)習(xí)率典型現(xiàn)象處理建議1.0 及以上損失劇烈震蕩甚至出現(xiàn) NaN立即調(diào)小 10 倍以上0.1正常收斂曲線順滑可以作為默認(rèn)起點(diǎn)0.01穩(wěn)定下降但速度偏慢增加迭代輪數(shù)即可0.001下降非常緩慢效率低可能需要上萬輪不建議看曲線時(shí)抓住一個(gè)核心信號(hào)損失曲線如果一路順滑下降且最終平穩(wěn)說明學(xué)習(xí)率基本合理如果先快速下降然后開始鋸齒狀抖動(dòng)往往是學(xué)習(xí)率偏大如果從頭到尾都平坦得幾乎不動(dòng)可能就是學(xué)習(xí)率太小或者特征沒做標(biāo)準(zhǔn)化導(dǎo)致梯度太弱。迭代輪數(shù)也不是越大越好。模型在訓(xùn)練集上損失會(huì)一直下降但驗(yàn)證集的損失通常會(huì)先降后升。所以更嚴(yán)謹(jǐn)?shù)淖龇ㄊ怯?xùn)練過程中每輪都記錄驗(yàn)證集損失當(dāng)驗(yàn)證集損失開始上升時(shí)說明繼續(xù)訓(xùn)練已經(jīng)沒有意義了完全可以早停。這個(gè)技巧叫 early stopping在后面的深度學(xué)習(xí)課程中同樣非常實(shí)用。4.2 過擬合與欠擬合在曲線上的表現(xiàn)很多人覺得 logistic 回歸是線性模型應(yīng)該不會(huì)過擬合這個(gè)想法不完全對(duì)。當(dāng)特征數(shù)量遠(yuǎn)多于樣本數(shù)量時(shí)logistic 回歸照樣會(huì)過擬合。欠擬合的特征很典型訓(xùn)練集損失很高訓(xùn)練集準(zhǔn)確率也很低而且無論如何延長(zhǎng)訓(xùn)練時(shí)間都上不去。這種情況多半是數(shù)據(jù)線性不可分或者特征確實(shí)和標(biāo)簽關(guān)系不大。解決方案要么是換更復(fù)雜的模型要么是構(gòu)造更有效的特征單純調(diào)參數(shù)是救不回來的。過擬合的特征正好相反訓(xùn)練集損失一路下降訓(xùn)練準(zhǔn)確率高達(dá) 95% 以上但驗(yàn)證集準(zhǔn)確率停在 75% 左右上不去驗(yàn)證集損失不降反升。這時(shí)可以在損失函數(shù)中加入正則項(xiàng)比如在交叉熵基礎(chǔ)上加 λ/2·||W||2對(duì)應(yīng)的梯度更新代碼只需要多減一項(xiàng) λ·W。這一步加進(jìn)去你會(huì)觀察到驗(yàn)證集表現(xiàn)有明顯改善。這也是后續(xù)深度學(xué)習(xí)實(shí)驗(yàn)里 weight decay 概念的起源。4.3 評(píng)估指標(biāo)別讓準(zhǔn)確率騙了你實(shí)驗(yàn)報(bào)告里如果只寫一個(gè)準(zhǔn)確率那你大概率會(huì)被追問一個(gè)問題如果測(cè)試集里 90% 是負(fù)樣本10% 是正樣本一個(gè)無腦把所有樣本都預(yù)測(cè)為負(fù)樣本的模型準(zhǔn)確率是多少答案是 90%。所以準(zhǔn)確率在類別不平衡的數(shù)據(jù)集上是非常具有欺騙性的。更完整的評(píng)估體系是看混淆矩陣以及由它引出的幾個(gè)指標(biāo)精確率 Precision TP / (TP FP)就是預(yù)測(cè)為正樣本的那些里有多少是真的正樣本。垃圾郵件過濾場(chǎng)景更看重這個(gè)誤殺一封正常郵件比漏掉一封垃圾郵件嚴(yán)重得多。召回率 Recall TP / (TP FN)就是真實(shí)正樣本里有多少被找出來了。疾病篩查場(chǎng)景更看重這個(gè)寧可多查幾次也不能漏診。F1 分?jǐn)?shù)是兩者的調(diào)和平均適合需要同時(shí)兼顧精確率和召回率的場(chǎng)景。還有一個(gè)在實(shí)驗(yàn)報(bào)告中很加分的指標(biāo)是 AUC也就是 ROC 曲線下的面積。它衡量的是模型“隨機(jī)抽一對(duì)正負(fù)樣本把正樣本排在前面的概率”所以它不受分類閾值的影響能更全面地反映模型的排序能力。AUC 在 0.5 附近說明模型跟瞎猜差不多越接近 1 說明區(qū)分能力越強(qiáng)。我建議你在實(shí)驗(yàn)里把準(zhǔn)確率、精確率、召回率、F1、AUC 都算一遍然后寫進(jìn)報(bào)告順便對(duì)比一下它們?cè)诋?dāng)前數(shù)據(jù)集下的表現(xiàn)。這樣一份實(shí)驗(yàn)報(bào)告交上去老師一眼就能看出你是真把二分類模型玩明白了而不是只會(huì)調(diào)一個(gè)包然后打印 accuracy。5. 常見問題與避坑實(shí)錄5.1 損失不下降、甚至變成 NaN這是每次實(shí)驗(yàn)都有一半人會(huì)碰到的問題排查順序很重要不要病急亂投醫(yī)。第一件事看學(xué)習(xí)率。如果 loss 從第一輪開始就是 NaN那大概率是學(xué)習(xí)率設(shè)太大參數(shù)更新一步就飛出去了。先把學(xué)習(xí)率降到 0.001 試試如果恢復(fù)正常再逐步往上加。第二件事看特征尺度。沒有標(biāo)準(zhǔn)化的數(shù)據(jù)特征范圍如果達(dá)到幾千z 的值也會(huì)變得很大sigmoid 直接飽和梯度小到幾乎為 0損失變化慢得像蝸牛。這時(shí)你看到的就是 loss 在下降趨勢(shì)上“一動(dòng)不動(dòng)”而不是上下震蕩。第三件事檢查數(shù)據(jù)本身。用代碼檢查一下 X 和 y 里有沒有 NaN這個(gè)操作很多人會(huì)跳過但真實(shí)數(shù)據(jù)里確實(shí)容易出現(xiàn)空值。第四件事確認(rèn)形狀。如果 p 和 y 的 shape 不對(duì)loss 和梯度都可能算出一個(gè)奇怪的值。打印 p.shape 和 y.shape一眼就能發(fā)現(xiàn)。這里也順便給自己留個(gè)好習(xí)慣在計(jì)算 log(p) 和 log(1-p) 時(shí)加一個(gè)極小值兜底比如 log(p 1e-8)防止 p 被算出恰好為 0 導(dǎo)致的無窮大問題。5.2 訓(xùn)練集損失在降驗(yàn)證集準(zhǔn)確率卻紋絲不動(dòng)這個(gè)現(xiàn)象背后的原因通常有三個(gè)。數(shù)據(jù)劃分有問題沒有分層抽樣導(dǎo)致驗(yàn)證集里某一類樣本特別多模型學(xué)到的模式在驗(yàn)證集上根本不適用。標(biāo)準(zhǔn)化信息泄露直接在全體數(shù)據(jù)上做了標(biāo)準(zhǔn)化再劃分?jǐn)?shù)據(jù)集這種操作會(huì)讓模型在驗(yàn)證集上的表現(xiàn)虛高然后在換一份真實(shí)數(shù)據(jù)測(cè)試時(shí)瞬間打回原形。樣本量太少驗(yàn)證集只有幾十條樣本時(shí)評(píng)估結(jié)果方差會(huì)很大今天 80%明天 60%根本沒有參考價(jià)值。最穩(wěn)妥的方案是先劃分訓(xùn)練集和測(cè)試集再只對(duì)訓(xùn)練集做標(biāo)準(zhǔn)化。劃分時(shí)使用分層采樣保證類別比例一致。如果數(shù)據(jù)量太少可以考慮用交叉驗(yàn)證來替代單次劃分。5.3 準(zhǔn)確率很高但 F1 很低怎么辦準(zhǔn)確率 85%、F1 卻只有 30%這種情況在類別不平衡的數(shù)據(jù)集里非常典型。你可以想象一下一個(gè)數(shù)據(jù)集有 900 個(gè)負(fù)樣本和 100 個(gè)正樣本模型把所有樣本都預(yù)測(cè)為負(fù)類準(zhǔn)確率是 90%但召回率是 0%F1 自然低得可憐。想讓模型真正學(xué)會(huì)抓少數(shù)類有兩條路調(diào)整分類閾值。不要把閾值死定為 0.5可以把閾值調(diào)到 0.3 或更低讓模型更容易判定為正類。你可以在驗(yàn)證集上畫一條 precision-recall 隨閾值變化的曲線看閾值放在哪個(gè)位置能讓 F1 最高。給少數(shù)類更大的權(quán)重。在損失函數(shù)中給正樣本乘以一個(gè)大于 1 的權(quán)重相當(dāng)于人為放大正樣本的犯錯(cuò)成本讓模型更愿意去擬合少數(shù)類。這兩條路在 logistic 回歸里都可以輕松實(shí)現(xiàn)建議你都試一遍然后在報(bào)告里寫清楚你對(duì)閾值的理解這會(huì)比單純說“我調(diào)了學(xué)習(xí)率”更有說服力。5.4 一個(gè)救命調(diào)試技巧先過擬合一個(gè)小批量這是我自己用得非常多的調(diào)試套路。在訓(xùn)練之前先只取 8 個(gè)樣本作為訓(xùn)練集跑一個(gè)小規(guī)模訓(xùn)練。如果代碼邏輯沒問題模型的損失應(yīng)該會(huì)在幾十個(gè) epoch 內(nèi)快速降到接近于 0訓(xùn)練準(zhǔn)確率能到 100%。如果小批量數(shù)據(jù)都無法過擬合那說明你的前向傳播、損失函數(shù)或者梯度更新里面一定有 bug先把這個(gè) bug 找出來再說。這么做最大的意義在于它把“模型學(xué)習(xí)的邏輯”和“數(shù)據(jù)規(guī)模帶來的問題”分開排查了。等小批量能過擬合再切回全量數(shù)據(jù)這時(shí)你面對(duì)的就只剩下學(xué)習(xí)率、正則化這些正經(jīng)調(diào)參問題而不是跟一個(gè)寫錯(cuò)符號(hào)的梯度公式搏斗兩個(gè)小時(shí)。寫到最后的一些實(shí)在話這個(gè)實(shí)驗(yàn)我前前后后重寫過很多遍每寫一遍都有新的理解。第一遍用深度學(xué)習(xí)框架跑通的時(shí)候我只知道調(diào)包很多細(xì)節(jié)是懵的第二遍強(qiáng)迫自己手寫前向和梯度更新才算真正搞懂 sigmoid 和交叉熵為什么是一對(duì)好搭檔第三遍加上正則化和評(píng)估指標(biāo)才發(fā)現(xiàn)自己以前對(duì)實(shí)驗(yàn)報(bào)告的理解太淺了。我建議你也找時(shí)間做一次“手寫版”的 logistic 回歸哪怕只是抽出半天時(shí)間它的價(jià)值遠(yuǎn)比你跑來跑去調(diào)學(xué)習(xí)率大得多。最后再分享一個(gè)實(shí)戰(zhàn)小技巧很多同學(xué)在這個(gè)實(shí)驗(yàn)里會(huì)把大量時(shí)間耗在調(diào)學(xué)習(xí)率上但實(shí)際操作中最有效的路徑是先固定一個(gè)學(xué)習(xí)率、把整個(gè)流程跑通確保代碼無誤再回頭調(diào)參。先保證流程完整再追求性能優(yōu)化——這個(gè)順序在后面的所有深度學(xué)習(xí)項(xiàng)目里都不會(huì)變。