階與Softmax實戰(zhàn))
1. 從二分類到多分類Logistic回歸的進(jìn)階玩法很多人第一次接觸Logistic回歸都是從二分類任務(wù)開始的——判斷一封郵件是不是垃圾郵件、一個用戶會不會點擊廣告、一筆交易是否存在欺詐風(fēng)險。二分類的邏輯很直觀用Sigmoid函數(shù)把線性輸出壓縮到0到1之間設(shè)定一個閾值大于它就歸為正類小于它就歸為負(fù)類。這套流程跑通了很多人就覺得Logistic回歸“不過如此”。但實際工作中你很快會遇到需要分三個、五個甚至十幾個類別的情況。比如手寫數(shù)字識別要區(qū)分0到9共十個類別鳶尾花分類要區(qū)分三個品種用戶意圖識別可能要區(qū)分咨詢、投訴、下單、退訂等七八種意圖。這時候如果還硬套二分類的思路就會陷入“一對多拆解”的泥潭——訓(xùn)練十個二分類器每個負(fù)責(zé)判斷“是不是這一類”最后投票決定。這種做法雖然能跑通但計算開銷大、類別不平衡時容易出問題而且概率輸出沒有歸一化解釋起來很別扭。Logistic回歸的多分類擴(kuò)展核心要解決的就是這個問題如何讓模型直接輸出樣本屬于每個類別的概率并且這些概率加起來等于1。這背后涉及到Softmax函數(shù)的引入、交叉熵?fù)p失函數(shù)的設(shè)計、以及從二分類到多分類的數(shù)學(xué)推導(dǎo)。我見過不少同行在面試時被問到“Softmax和Sigmoid什么關(guān)系”就卡殼其實只要理解了多分類的本質(zhì)是二分類的推廣這個問題就迎刃而解。這篇文章適合已經(jīng)了解Logistic回歸基本原理、想進(jìn)一步掌握多分類實現(xiàn)的讀者。無論你是用Python手寫實現(xiàn)還是調(diào)用scikit-learn、PyTorch等框架我都會把背后的邏輯拆開講清楚。更重要的是我會分享在實際項目中踩過的坑——比如類別不平衡怎么處理、Softmax數(shù)值溢出怎么避免、多分類的評價指標(biāo)怎么選——這些是文檔里不會寫的經(jīng)驗。2. 核心原理拆解Softmax到底在做什么2.1 從Sigmoid到Softmax的數(shù)學(xué)推導(dǎo)二分類的Logistic回歸輸出的是一個標(biāo)量概率值表示樣本屬于正類的概率。數(shù)學(xué)形式是P(y1|x) σ(w·x b) 1 / (1 exp(-(w·x b)))其中σ是Sigmoid函數(shù)w是權(quán)重向量b是偏置項。負(fù)類的概率自然就是1減去正類概率。到了多分類假設(shè)有K個類別我們需要輸出K個概率值分別表示樣本屬于每個類別的概率。最直接的想法是對每個類別都算一個線性得分然后用某種方式把這些得分轉(zhuǎn)化成概率。Softmax函數(shù)就是干這個的P(yk|x) exp(z_k) / Σ_j exp(z_j)其中z_k w_k·x b_k是第k個類別的線性得分。Softmax的作用可以這樣理解先通過指數(shù)函數(shù)把每個得分變成正數(shù)然后歸一化使得所有類別的概率之和為1。指數(shù)函數(shù)還有一個好處——它會放大得分之間的差距讓高分更高、低分更低這樣模型的決策邊界更清晰。這里有一個關(guān)鍵點Softmax其實和Sigmoid有內(nèi)在聯(lián)系。當(dāng)K2時Softmax退化成Sigmoid。你可以自己推導(dǎo)一下設(shè)z_1和z_2是兩個類別的得分Softmax輸出P(y1) exp(z_1) / (exp(z_1) exp(z_2))分子分母同除以exp(z_1)得到1 / (1 exp(z_2 - z_1))令z z_1 - z_2這就是Sigmoid的形式。所以Softmax是Sigmoid在多分類上的自然推廣不是兩個獨立的東西。2.2 交叉熵?fù)p失函數(shù)的來龍去脈多分類的損失函數(shù)通常用交叉熵形式是L -Σ_k y_k * log(P(yk|x))其中y_k是真實標(biāo)簽的one-hot編碼如果樣本屬于第k類y_k1否則為0。所以實際上只有真實類別那一項的log概率被計入損失。為什么用交叉熵而不是均方誤差從信息論的角度看交叉熵衡量的是模型預(yù)測分布和真實分布之間的差異。從優(yōu)化的角度看交叉熵配合Softmax求導(dǎo)特別簡潔——梯度就是預(yù)測概率減去真實標(biāo)簽即?L/?z_k P(yk|x) - y_k。這個梯度形式非常優(yōu)雅計算量小而且不會像均方誤差那樣在輸出接近0或1時梯度消失。我見過一些初學(xué)者試圖用均方誤差來訓(xùn)練多分類Logistic回歸結(jié)果收斂很慢甚至不收斂。原因就在于均方誤差對Softmax輸出的梯度包含一個Sigmoid導(dǎo)數(shù)項當(dāng)預(yù)測概率接近0或1時這個導(dǎo)數(shù)趨近于0導(dǎo)致梯度消失。交叉熵則沒有這個問題所以它是分類任務(wù)的標(biāo)準(zhǔn)選擇。2.3 參數(shù)估計從最大似然到梯度下降多分類Logistic回歸的參數(shù)估計本質(zhì)上還是最大似然估計。給定N個訓(xùn)練樣本似然函數(shù)是L(w) Π_i Π_k P(yk|x_i)^{y_ik}取對數(shù)后變成log L(w) Σ_i Σ_k y_ik * log(P(yk|x_i))最大化這個對數(shù)似然等價于最小化負(fù)對數(shù)似然也就是交叉熵?fù)p失。由于沒有閉式解通常用梯度下降或其變體來優(yōu)化。梯度下降的更新公式是w_k : w_k - α * (1/N) * Σ_i (P(yk|x_i) - y_ik) * x_i其中α是學(xué)習(xí)率。這個更新公式的直觀含義是對于每個樣本如果模型預(yù)測第k類的概率高于真實標(biāo)簽就把第k類的權(quán)重往遠(yuǎn)離樣本特征的方向推如果低于真實標(biāo)簽就往靠近的方向拉。所有樣本的梯度累加后取平均再乘以學(xué)習(xí)率就是一次更新。在實際實現(xiàn)中通常會加上L2正則化項來防止過擬合L -Σ_i Σ_k y_ik * log(P(yk|x_i)) λ * Σ_k ||w_k||^2正則化系數(shù)λ控制懲罰力度需要通過交叉驗證來選。我一般會從0.01開始試如果模型過擬合嚴(yán)重就調(diào)大如果欠擬合就調(diào)小。3. 實操過程從零實現(xiàn)多分類Logistic回歸3.1 數(shù)據(jù)準(zhǔn)備與預(yù)處理假設(shè)我們有一個手寫數(shù)字識別的數(shù)據(jù)集每個樣本是28x28的灰度圖像展平后是784維特征標(biāo)簽是0到9共10個類別。數(shù)據(jù)預(yù)處理包括幾個關(guān)鍵步驟第一步是歸一化。把像素值從0到255縮放到0到1之間或者標(biāo)準(zhǔn)化到均值為0、方差為1。歸一化的目的是讓不同特征的尺度一致避免某些特征因為數(shù)值大而主導(dǎo)梯度。對于圖像數(shù)據(jù)我通常直接除以255簡單有效。第二步是添加偏置項。如果手動實現(xiàn)需要在特征矩陣前面加一列全1這樣權(quán)重向量就不用單獨處理偏置了。如果用scikit-learn或PyTorch它們會自動處理偏置不需要手動加。第三步是劃分訓(xùn)練集和測試集。通常按8:2或7:3劃分如果數(shù)據(jù)量小還要做交叉驗證。對于類別不平衡的數(shù)據(jù)劃分時要保證每個類別的比例在訓(xùn)練集和測試集中大致一致可以用分層抽樣。第四步是標(biāo)簽編碼。多分類的標(biāo)簽需要轉(zhuǎn)成one-hot編碼或者用整數(shù)編碼配合合適的損失函數(shù)。scikit-learn的LogisticRegression支持整數(shù)標(biāo)簽PyTorch的CrossEntropyLoss也支持整數(shù)標(biāo)簽但手動實現(xiàn)時通常用one-hot更方便。import numpy as np from sklearn.datasets import load_digits from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 加載數(shù)據(jù) digits load_digits() X, y digits.data, digits.target # 歸一化 scaler StandardScaler() X scaler.fit_transform(X) # 劃分?jǐn)?shù)據(jù)集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 轉(zhuǎn)one-hot編碼 def one_hot(y, num_classes): return np.eye(num_classes)[y] y_train_onehot one_hot(y_train, 10) y_test_onehot one_hot(y_test, 10)3.2 手動實現(xiàn)Softmax與交叉熵手動實現(xiàn)的好處是能徹底理解每一步在做什么。先定義Softmax函數(shù)def softmax(z): # z的形狀是(batch_size, num_classes) # 減去最大值防止數(shù)值溢出 z_shifted z - np.max(z, axis1, keepdimsTrue) exp_z np.exp(z_shifted) return exp_z / np.sum(exp_z, axis1, keepdimsTrue)這里有一個非常重要的技巧減去最大值。因為exp函數(shù)在輸入很大時會溢出比如exp(1000)會變成inf。減去每行的最大值后最大的輸入變成0exp(0)1其他輸入變成負(fù)數(shù)exp的結(jié)果在0到1之間不會溢出。這個操作不改變Softmax的輸出因為分子分母同時除以了exp(max)數(shù)學(xué)上是等價的。然后定義交叉熵?fù)p失def cross_entropy_loss(y_pred, y_true): # y_pred是Softmax輸出的概率y_true是one-hot標(biāo)簽 # 加上一個極小值防止log(0) epsilon 1e-15 y_pred np.clip(y_pred, epsilon, 1 - epsilon) return -np.sum(y_true * np.log(y_pred)) / y_true.shape[0]計算梯度def compute_gradient(X, y_pred, y_true): # 梯度 (1/N) * X^T * (y_pred - y_true) N X.shape[0] return X.T (y_pred - y_true) / N完整的訓(xùn)練循環(huán)def train_logistic_regression(X, y_onehot, learning_rate0.1, epochs1000, reg_lambda0.01): N, D X.shape K y_onehot.shape[1] W np.zeros((D, K)) # 權(quán)重矩陣 losses [] for epoch in range(epochs): # 前向傳播 z X W y_pred softmax(z) # 計算損失 loss cross_entropy_loss(y_pred, y_onehot) # 加L2正則化 loss reg_lambda * np.sum(W ** 2) / (2 * N) losses.append(loss) # 計算梯度 gradient compute_gradient(X, y_pred, y_onehot) # 加正則化梯度 gradient reg_lambda * W / N # 更新權(quán)重 W - learning_rate * gradient if epoch % 100 0: print(fEpoch {epoch}, Loss: {loss:.4f}) return W, losses3.3 用scikit-learn快速落地如果不想手動實現(xiàn)scikit-learn提供了開箱即用的多分類Logistic回歸。關(guān)鍵參數(shù)有幾個multi_class可選ovr一對多或multinomialSoftmax。multinomial就是真正的多分類ovr是拆成多個二分類。默認(rèn)是auto會根據(jù)數(shù)據(jù)自動選擇但我建議顯式指定multinomial。solver優(yōu)化算法。lbfgs適合小數(shù)據(jù)集saga適合大數(shù)據(jù)集且支持L1正則化。C正則化系數(shù)的倒數(shù)C越小正則化越強(qiáng)。默認(rèn)是1.0。max_iter最大迭代次數(shù)。如果模型沒收斂會報警告需要調(diào)大。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, classification_report # 訓(xùn)練模型 clf LogisticRegression( multi_classmultinomial, solverlbfgs, C1.0, max_iter1000, random_state42 ) clf.fit(X_train, y_train) # 預(yù)測 y_pred clf.predict(X_test) print(fAccuracy: {accuracy_score(y_test, y_pred):.4f}) print(classification_report(y_test, y_pred))3.4 用PyTorch實現(xiàn)更靈活的訓(xùn)練PyTorch適合需要自定義損失函數(shù)、優(yōu)化器或網(wǎng)絡(luò)結(jié)構(gòu)的場景。多分類Logistic回歸在PyTorch中就是一個單層全連接網(wǎng)絡(luò)加CrossEntropyLossimport torch import torch.nn as nn import torch.optim as optim # 轉(zhuǎn)成Tensor X_train_tensor torch.FloatTensor(X_train) y_train_tensor torch.LongTensor(y_train) X_test_tensor torch.FloatTensor(X_test) y_test_tensor torch.LongTensor(y_test) # 定義模型 model nn.Linear(X_train.shape[1], 10) # 定義損失函數(shù)和優(yōu)化器 criterion nn.CrossEntropyLoss() optimizer optim.SGD(model.parameters(), lr0.01, weight_decay1e-4) # 訓(xùn)練 for epoch in range(1000): model.train() optimizer.zero_grad() outputs model(X_train_tensor) loss criterion(outputs, y_train_tensor) loss.backward() optimizer.step() if epoch % 100 0: print(fEpoch {epoch}, Loss: {loss.item():.4f}) # 評估 model.eval() with torch.no_grad(): outputs model(X_test_tensor) _, predicted torch.max(outputs, 1) accuracy (predicted y_test_tensor).float().mean() print(fAccuracy: {accuracy.item():.4f})注意PyTorch的CrossEntropyLoss內(nèi)部已經(jīng)包含了Softmax所以模型輸出的是線性得分不需要手動加Softmax。這是初學(xué)者容易搞錯的地方——如果手動加了Softmax再傳給CrossEntropyLoss相當(dāng)于做了兩次Softmax結(jié)果會不對。4. 常見問題與排查技巧實錄4.1 損失不下降或震蕩怎么辦這是訓(xùn)練中最常見的問題??赡艿脑蚝团挪樗悸返谝粚W(xué)習(xí)率太大。如果損失震蕩劇烈甚至變成NaN先把學(xué)習(xí)率調(diào)小一個數(shù)量級試試。我通常從0.1開始如果震蕩就降到0.01、0.001。用Adam優(yōu)化器的話學(xué)習(xí)率可以設(shè)0.001左右。第二數(shù)據(jù)沒有歸一化。不同特征的尺度差異大會導(dǎo)致梯度方向偏離最優(yōu)方向收斂慢甚至不收斂。檢查一下特征的最大值和最小值如果差異超過一個數(shù)量級就要做標(biāo)準(zhǔn)化。第三標(biāo)簽編碼錯誤。如果用整數(shù)標(biāo)簽配合CrossEntropyLoss標(biāo)簽必須從0開始連續(xù)編號。如果標(biāo)簽是1到10而不是0到9會導(dǎo)致索引越界或計算錯誤。用np.unique檢查一下標(biāo)簽的取值。第四Softmax數(shù)值溢出。如果手動實現(xiàn)Softmax時沒有減去最大值當(dāng)線性得分很大時exp會溢出導(dǎo)致NaN。檢查一下z的最大值如果超過700exp(700)接近浮點數(shù)上限就必須做平移。4.2 類別不平衡怎么處理實際數(shù)據(jù)中類別不平衡是常態(tài)。比如用戶意圖識別中咨詢類可能占80%投訴類只占5%。這時候模型會傾向于預(yù)測多數(shù)類少數(shù)類的召回率很低。處理方法有幾種重采樣對少數(shù)類過采樣復(fù)制樣本或SMOTE生成新樣本對多數(shù)類欠采樣。過采樣容易過擬合欠采樣會丟失信息通常結(jié)合使用。類別權(quán)重在損失函數(shù)中給少數(shù)類更高的權(quán)重。scikit-learn的LogisticRegression有class_weightbalanced參數(shù)會自動按類別頻率的倒數(shù)設(shè)置權(quán)重。PyTorch的CrossEntropyLoss有weight參數(shù)可以傳入每個類別的權(quán)重。調(diào)整閾值如果模型輸出概率可以對少數(shù)類降低判定閾值。但多分類中閾值調(diào)整比較復(fù)雜通常還是用類別權(quán)重。我個人的經(jīng)驗是先試class_weightbalanced如果效果不夠好再考慮重采樣。重采樣時要注意只在訓(xùn)練集上做測試集保持原始分布否則評估結(jié)果會偏樂觀。4.3 多分類的評價指標(biāo)怎么選準(zhǔn)確率是最直觀的指標(biāo)但在類別不平衡時會有誤導(dǎo)性。比如90%的樣本是A類模型全預(yù)測A類也能達(dá)到90%準(zhǔn)確率但B類和C類的召回率是0。更全面的指標(biāo)包括宏平均F1先算每個類別的F1再取平均。每個類別權(quán)重相同適合關(guān)心少數(shù)類的場景。微平均F1把所有類別的TP、FP、FN累加后算F1。多數(shù)類主導(dǎo)適合關(guān)心整體性能的場景。加權(quán)平均F1按類別樣本數(shù)加權(quán)平均F1。介于宏平均和微平均之間?;煜仃囍庇^展示每個類別的誤判情況能發(fā)現(xiàn)模型在哪些類別上容易混淆。from sklearn.metrics import f1_score, confusion_matrix print(fMacro F1: {f1_score(y_test, y_pred, averagemacro):.4f}) print(fMicro F1: {f1_score(y_test, y_pred, averagemicro):.4f}) print(fWeighted F1: {f1_score(y_test, y_pred, averageweighted):.4f}) print(Confusion Matrix:) print(confusion_matrix(y_test, y_pred))4.4 正則化系數(shù)怎么調(diào)正則化系數(shù)λ或scikit-learn中的C控制模型復(fù)雜度。λ太大模型欠擬合訓(xùn)練集和測試集準(zhǔn)確率都低λ太小模型過擬合訓(xùn)練集準(zhǔn)確率高但測試集低。調(diào)參方法是交叉驗證。把訓(xùn)練集分成K份每次用K-1份訓(xùn)練、1份驗證取平均驗證分?jǐn)?shù)最高的λ。scikit-learn的GridSearchCV可以自動做這件事from sklearn.model_selection import GridSearchCV param_grid {C: [0.001, 0.01, 0.1, 1, 10, 100]} grid GridSearchCV( LogisticRegression(multi_classmultinomial, solverlbfgs, max_iter1000), param_grid, cv5, scoringf1_macro ) grid.fit(X_train, y_train) print(fBest C: {grid.best_params_[C]}) print(fBest CV F1: {grid.best_score_:.4f})我一般會先粗調(diào)比如試0.001、0.01、0.1、1、10、100找到最優(yōu)區(qū)間后再細(xì)調(diào)。如果數(shù)據(jù)量很大交叉驗證會很慢可以先用小樣本調(diào)參再在全量數(shù)據(jù)上訓(xùn)練。4.5 常見問題速查表問題現(xiàn)象可能原因排查方法解決方案損失變成NaN學(xué)習(xí)率太大或Softmax溢出檢查學(xué)習(xí)率和z的最大值調(diào)小學(xué)習(xí)率Softmax減最大值損失不下降數(shù)據(jù)未歸一化或標(biāo)簽錯誤檢查特征尺度和標(biāo)簽范圍標(biāo)準(zhǔn)化特征修正標(biāo)簽編碼測試集準(zhǔn)確率遠(yuǎn)低于訓(xùn)練集過擬合對比訓(xùn)練集和測試集指標(biāo)增大正則化增加數(shù)據(jù)量少數(shù)類召回率為0類別不平衡查看混淆矩陣設(shè)置類別權(quán)重重采樣訓(xùn)練速度慢特征維度高或數(shù)據(jù)量大檢查特征數(shù)和樣本數(shù)降維用SAGA優(yōu)化器Mini-batch模型不收斂迭代次數(shù)不夠查看收斂警告增大max_iter調(diào)整學(xué)習(xí)率5. 進(jìn)階技巧與實戰(zhàn)經(jīng)驗5.1 特征工程比模型選擇更重要Logistic回歸是線性模型它的表達(dá)能力有限。如果特征和標(biāo)簽之間是非線性關(guān)系再怎么調(diào)參也提升有限。這時候特征工程就特別關(guān)鍵。對于多分類任務(wù)常用的特征工程手段包括多項式特征把原始特征做二階或三階組合讓模型能擬合非線性邊界。scikit-learn的PolynomialFeatures可以自動生成。分箱把連續(xù)特征離散化成多個區(qū)間每個區(qū)間作為一個類別特征。適合特征和標(biāo)簽關(guān)系非單調(diào)的場景。交互特征把兩個特征的乘積作為新特征捕捉特征之間的協(xié)同效應(yīng)。領(lǐng)域特征根據(jù)業(yè)務(wù)知識構(gòu)造的特征往往比自動生成的特征更有效。我做過一個用戶意圖分類的項目原始特征只有用戶的歷史行為統(tǒng)計準(zhǔn)確率卡在75%上不去。后來加了幾個領(lǐng)域特征——比如最近一次操作距今天數(shù)、操作頻率的滑動平均——準(zhǔn)確率直接提到82%。所以不要只盯著模型調(diào)參多花時間在特征上。5.2 用Softmax的溫度參數(shù)調(diào)節(jié)置信度Softmax有一個溫度參數(shù)T形式是P(yk|x) exp(z_k / T) / Σ_j exp(z_j / T)T1時就是標(biāo)準(zhǔn)Softmax。T1時概率分布更平滑模型輸出的置信度降低T1時概率分布更尖銳置信度升高。這個技巧在模型蒸餾和不確定性估計中很常用。比如你希望模型對不確定的樣本輸出更保守的概率可以把T調(diào)大。在實際部署中如果發(fā)現(xiàn)模型對某些樣本過度自信但預(yù)測錯誤可以適當(dāng)調(diào)大T來校準(zhǔn)置信度。5.3 多分類中的類別合并策略有時候類別太多每個類別的樣本很少模型很難學(xué)好。這時候可以考慮把相似的類別合并。比如用戶意圖識別中“咨詢價格”和“咨詢庫存”可以合并成“咨詢產(chǎn)品信息”。合并后類別數(shù)減少每個類別的樣本增多模型更容易收斂。合并的策略有兩種一是根據(jù)業(yè)務(wù)知識手動合并二是根據(jù)混淆矩陣自動合并——如果兩個類別經(jīng)常互相誤判說明它們的特征很相似可以考慮合并。但合并會損失細(xì)粒度信息需要權(quán)衡。5.4 在線學(xué)習(xí)與增量更新實際業(yè)務(wù)中數(shù)據(jù)是不斷到來的模型需要定期更新。如果每次都用全量數(shù)據(jù)重新訓(xùn)練計算成本很高。這時候可以用增量學(xué)習(xí)——用新數(shù)據(jù)更新已有模型的參數(shù)而不是從頭訓(xùn)練。scikit-learn的LogisticRegression支持partial_fit方法可以用小批量數(shù)據(jù)逐步更新模型。但要注意partial_fit需要先指定所有可能的類別而且學(xué)習(xí)率需要手動調(diào)整。PyTorch則更靈活可以加載已有模型用新數(shù)據(jù)繼續(xù)訓(xùn)練幾個epoch。# scikit-learn增量學(xué)習(xí) clf LogisticRegression(multi_classmultinomial, solversaga) clf.partial_fit(X_train[:100], y_train[:100], classesnp.arange(10)) for i in range(100, len(X_train), 100): clf.partial_fit(X_train[i:i100], y_train[i:i100])增量學(xué)習(xí)的關(guān)鍵是控制學(xué)習(xí)率——新數(shù)據(jù)的影響不能太大否則會覆蓋舊知識也不能太小否則學(xué)不到新模式。通常會用衰減的學(xué)習(xí)率隨著更新次數(shù)增加而減小。5.5 模型可解釋性系數(shù)分析Logistic回歸的一大優(yōu)勢是可解釋性強(qiáng)。每個類別的權(quán)重向量w_k表示該類別對每個特征的偏好。權(quán)重為正說明該特征值越大樣本越可能屬于該類權(quán)重為負(fù)則相反。通過分析權(quán)重可以發(fā)現(xiàn)哪些特征對分類最重要。比如在手寫數(shù)字識別中數(shù)字8的權(quán)重在中間區(qū)域的像素上可能很大因為8的中間有交叉。這種分析可以幫助理解模型的決策邏輯也能指導(dǎo)特征工程。# 查看每個類別的權(quán)重 for k in range(10): top_features np.argsort(np.abs(clf.coef_[k]))[-10:] print(fClass {k} top features: {top_features})如果特征有實際含義比如用戶年齡、收入還可以直接解釋“年齡越大越可能屬于某類”。這種可解釋性是深度學(xué)習(xí)模型不具備的也是Logistic回歸在金融、醫(yī)療等需要解釋的領(lǐng)域仍然流行的原因。6. 我踩過的坑與實操心得第一個坑是Softmax的數(shù)值穩(wěn)定性。剛開始手動實現(xiàn)時沒有減去最大值結(jié)果在訓(xùn)練后期線性得分變大exp溢出導(dǎo)致?lián)p失變成NaN。排查了半天才找到原因。后來養(yǎng)成習(xí)慣只要寫Softmax就先減最大值這個操作沒有任何副作用但能避免大麻煩。第二個坑是標(biāo)簽編碼。有一次用PyTorch的CrossEntropyLoss標(biāo)簽是1到10而不是0到9結(jié)果訓(xùn)練時損失正常下降但預(yù)測結(jié)果全錯。后來發(fā)現(xiàn)CrossEntropyLoss內(nèi)部用標(biāo)簽做索引標(biāo)簽必須從0開始。這個錯誤很隱蔽因為不會報錯只是結(jié)果不對?,F(xiàn)在我會在訓(xùn)練前用np.unique(y)檢查標(biāo)簽范圍。第三個坑是類別不平衡。做一個欺詐檢測項目時欺詐樣本只占0.5%模型全預(yù)測非欺詐也能達(dá)到99.5%準(zhǔn)確率但完全沒用。后來用了class_weightbalanced欺詐類的召回率從0提升到70%多。但代價是誤報率上升需要根據(jù)業(yè)務(wù)場景權(quán)衡。如果誤報成本高就不能一味追求召回率。第四個坑是正則化系數(shù)。有一次調(diào)參時C設(shè)得太大100模型在訓(xùn)練集上準(zhǔn)確率99%測試集只有85%明顯過擬合。后來用GridSearchCV找到最優(yōu)C是0.1測試集準(zhǔn)確率提到92%。所以不要憑感覺設(shè)參數(shù)交叉驗證是必須的。第五個坑是特征尺度。有一次做文本分類特征包括詞頻和文檔長度詞頻范圍是0到100文檔長度是0到10000。沒有歸一化直接訓(xùn)練結(jié)果模型完全被文檔長度主導(dǎo)詞頻的影響被淹沒。標(biāo)準(zhǔn)化之后兩個特征都發(fā)揮作用準(zhǔn)確率提升明顯。最后分享一個小技巧如果數(shù)據(jù)量不大可以用全批量梯度下降收斂更穩(wěn)定如果數(shù)據(jù)量大用Mini-batch梯度下降訓(xùn)練更快。Batch size通常設(shè)32、64、128太小梯度噪聲大太大內(nèi)存吃不消。我一般從64開始試根據(jù)訓(xùn)練速度和收斂情況調(diào)整。多分類Logistic回歸雖然簡單但要把每個細(xì)節(jié)都做對還是需要不少經(jīng)驗。希望這些踩坑經(jīng)歷能幫你少走彎路。