據(jù)集實戰(zhàn)線性判別分析:原理推導(dǎo)與Python代碼實現(xiàn))
簡介本資源是一份面向機器學(xué)習初學(xué)者與實踐者的線性判別分析LDA教學(xué)實踐包聚焦特征降維與多類分類任務(wù)以經(jīng)典西瓜數(shù)據(jù)集3.0為載體完整呈現(xiàn)LDA在Python中的建模、訓(xùn)練與評估全流程。壓縮包共10個文件含2個核心Python腳本LDA.py與self_def.py、1個CSV格式的西瓜數(shù)據(jù)集watermelon_3a.csv、3個XML配置文件及輔助開發(fā)文件如.iml、.pyc、.DS_Store總大小僅12KB輕量易部署適合嵌入課程實驗或自學(xué)項目。已有584人下載學(xué)習資源結(jié)構(gòu)簡潔清晰src目錄封裝主程序邏輯data目錄存放原始數(shù)據(jù).idea配置支持PyCharm快速調(diào)試。讀者可直接運行代碼完成數(shù)據(jù)加載、訓(xùn)練集劃分、LDA模型擬合、預(yù)測及classification_report性能評估同時獲得對高斯假設(shè)、類內(nèi)/類間散度理解的實踐入口是掌握監(jiān)督式降維方法的實用入門材料。1. 項目概述從“西瓜數(shù)據(jù)集”到線性判別分析LDA的實戰(zhàn)之旅如果你剛接觸機器學(xué)習面對一堆算法名詞感到頭大那“線性判別分析”Linear Discriminant Analysis, LDA絕對是一個值得你花時間吃透的經(jīng)典算法。它不像深度學(xué)習那樣需要海量數(shù)據(jù)和算力也不像某些黑盒模型那樣難以解釋。LDA的核心思想非常直觀找到一個投影方向讓不同類別的數(shù)據(jù)在這個方向上投影后類間的距離盡可能大類內(nèi)的距離盡可能小。聽起來是不是有點像在人群中找一條路讓不同隊伍的人站得盡量分開而同一隊伍的人站得盡量緊湊這個經(jīng)典的“降維”兼“分類”算法在文本主題建模另一個LDALatent Dirichlet Allocation、人臉識別、生物信息學(xué)等領(lǐng)域至今仍有廣泛應(yīng)用。今天我們就用一個非常接地氣的“西瓜數(shù)據(jù)集”來手把手實現(xiàn)LDA。這個數(shù)據(jù)集在周志華老師的《機器學(xué)習》俗稱“西瓜書”里被反復(fù)使用數(shù)據(jù)量小、特征明確是理解算法原理的絕佳“教具”。我們將用Python從零開始不依賴高級的scikit-learn封裝當然最后也會對比一下一步步推導(dǎo)公式、編寫代碼、可視化結(jié)果讓你不僅知道怎么調(diào)包更明白包里的每一行代碼在算什么。你會發(fā)現(xiàn)LDA.zip里裝的不僅是代碼更是一套理解監(jiān)督式降維與分類的完整思維模型。2. LDA的核心原理與數(shù)學(xué)推導(dǎo)拆解2.1 問題定義我們要解決什么假設(shè)我們手頭有一個數(shù)據(jù)集比如我們的“西瓜數(shù)據(jù)集”每個西瓜樣本有多個特征如密度、含糖率并且每個樣本都有一個已知的類別標簽如好瓜、壞瓜。LDA要解決的核心問題是如何利用這些已知的類別信息找到原始特征空間的一個低維投影通常是直線或平面使得在這個新的低維空間里不同類別的樣本能最大程度地被區(qū)分開。這帶來了兩個直接的好處1.降維將高維數(shù)據(jù)可視化到二維或三維便于我們觀察和理解。2.分類可以直接在這個投影后的低維空間里構(gòu)建一個簡單的分類器比如找一個分類閾值。2.2 核心思想類內(nèi)小類間大LDA的目標可以用一個優(yōu)化問題來精確描述。我們目標是找到一個投影向量w將樣本x投影到一維空間y w^T x。LDA希望投影后滿足類內(nèi)散度Within-class scatter最小化同一類別樣本的投影點盡可能聚集在一起方差小。類間散度Between-class scatter最大化不同類別樣本的投影點的中心盡可能遠離。如何量化“散度”呢這里引入了散度矩陣的概念。類內(nèi)散度矩陣Sw衡量每個類別內(nèi)部樣本的分散程度。計算方法是每個類別樣本與其類別均值的協(xié)方差矩陣之和。Sw Σ_i Σ_{x∈X_i} (x - m_i)(x - m_i)^T其中m_i是第i類的均值向量。類間散度矩陣Sb衡量不同類別中心之間的分散程度。計算方法是每個類別的樣本數(shù)乘以該類均值與總體均值的差的外積之和。Sb Σ_i n_i (m_i - m)(m_i - m)^T其中m是全體樣本的總體均值向量n_i是第i類的樣本數(shù)。投影后類內(nèi)散度變?yōu)閣^T Sw w類間散度變?yōu)閣^T Sb w。我們的優(yōu)化目標就是最大化它們的比值即尋找w使得下式最大J(w) (w^T Sb w) / (w^T Sw w)這個J(w)就是著名的“廣義瑞利商”Generalized Rayleigh Quotient。2.3 求解特征值分解的登場如何求解使J(w)最大的w呢在數(shù)學(xué)上可以證明最優(yōu)的投影方向w滿足方程Sb w λ Sw w這被稱為廣義特征值問題。通常Sw是可逆的上式可以轉(zhuǎn)化為標準特征值問題Sw^{-1} Sb w λ w也就是說我們要求解矩陣Sw^{-1} Sb的特征值和特征向量。其中最大特征值對應(yīng)的特征向量就是我們要找的、使得類間區(qū)分度最大的投影方向。如果我們想投影到多維比如二維就取前k個最大特征值對應(yīng)的特征向量組成投影矩陣W。注意這里有一個非常重要的前提就是Sw必須是可逆的。這就要求樣本數(shù)量必須大于特征維度且特征之間不能存在完全的線性相關(guān)性。在實際應(yīng)用中如果Sw奇異不可逆需要進行正則化處理如加上一個很小的單位矩陣Sw εI或者先使用PCA進行降維。2.4 LDA與PCA的本質(zhì)區(qū)別很多人容易混淆LDA和主成分分析PCA。這里務(wù)必厘清PCA無監(jiān)督尋找數(shù)據(jù)方差最大的投影方向目標是保留最多的數(shù)據(jù)信息方差完全無視樣本的類別標簽。LDA有監(jiān)督尋找能最好區(qū)分已知類別的投影方向目標是最大化分類潛力強烈依賴類別標簽。用一個比喻PCA是給你一堆混在一起的彩色珠子它幫你找到一條線讓所有珠子在這條線上鋪得最開方差最大不管顏色。LDA是已知哪些珠子是紅色、哪些是藍色它幫你找到一條線讓紅色珠子和藍色珠子在這條線上分別聚成兩團且兩團離得盡量遠。3. 西瓜數(shù)據(jù)集解析與Python環(huán)境準備3.1 西瓜數(shù)據(jù)集3.0α 深度解析我們采用“西瓜書”第3章最經(jīng)典的“西瓜數(shù)據(jù)集3.0α”。這個數(shù)據(jù)集雖然小但涵蓋了連續(xù)值和離散值為了聚焦LDA核心我們通常只選取兩個連續(xù)特征密度和含糖率以及一個二分類標簽好瓜是和壞瓜否。原始數(shù)據(jù)可能如下表所示編號密度含糖率好瓜10.6970.460是20.7740.376是............170.5930.042否這個數(shù)據(jù)集非常適合教學(xué)維度低只有2個特征投影結(jié)果可以輕松在二維平面上可視化直觀理解LDA的投影直線。樣本少總共17個樣本手動計算或調(diào)試代碼都非常方便。線性可分性數(shù)據(jù)在二維平面上大致是線性可分的LDA能發(fā)揮出很好的效果。在實際代碼中我們會將“是”標記為類別1“否”標記為類別0并將特征數(shù)據(jù)整理成一個(17, 2)的矩陣X標簽整理成一個(17,)的向量y。3.2 Python環(huán)境與工具庫清單我們將使用最基礎(chǔ)的科學(xué)計算庫來實現(xiàn)LDA確保每一步都清晰可見。# 環(huán)境準備所需庫 import numpy as np import matplotlib.pyplot as plt from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as SKLDA # 用于對比 # 確保可復(fù)現(xiàn)性 np.random.seed(42)核心庫說明NumPy整個項目的基石。所有矩陣運算均值、協(xié)方差、特征值分解都依賴它。務(wù)必理解其數(shù)組廣播和矩陣乘法。Matplotlib用于可視化。我們將繪制原始數(shù)據(jù)散點圖、LDA投影方向、以及投影后的數(shù)據(jù)分布直方圖。scikit-learn僅用于最后的結(jié)果驗證和對比。我們會用它的LDA實現(xiàn)來檢驗我們自己編寫的算法是否正確。一個關(guān)鍵的實操心得在開始編碼前我強烈建議你在Jupyter Notebook或支持交互的Python環(huán)境中進行。這樣你可以隨時打印中間變量如Sw,Sb, 特征值等的形狀和值與手算或邏輯推導(dǎo)進行比對這是理解算法、調(diào)試代碼最有效的方式。4. 從零手撕LDA代碼實現(xiàn)4.1 數(shù)據(jù)加載與預(yù)處理首先我們手動輸入西瓜數(shù)據(jù)集并進行標準化處理。雖然LDA本身不受量綱影響因為其優(yōu)化目標是一個比值但良好的數(shù)據(jù)習慣是從標準化開始。def load_watermelon_data(): 加載西瓜數(shù)據(jù)集3.0α (密度 含糖率) # 特征X: 密度, 含糖率 X np.array([ [0.697, 0.460], [0.774, 0.376], [0.634, 0.264], [0.608, 0.318], [0.556, 0.215], [0.403, 0.237], [0.481, 0.149], [0.437, 0.211], [0.666, 0.091], [0.243, 0.267], [0.245, 0.057], [0.343, 0.099], [0.639, 0.161], [0.657, 0.198], [0.360, 0.370], [0.593, 0.042], [0.719, 0.103] ]) # 標簽y: 1為好瓜(是), 0為壞瓜(否) y np.array([1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]) return X, y X, y load_watermelon_data() print(f數(shù)據(jù)形狀: X{X.shape}, y{y.shape}) print(f類別分布: 好瓜{sum(y1)}個, 壞瓜{sum(y0)}個) # 數(shù)據(jù)標準化 (Z-score標準化) def standardize(X): mean np.mean(X, axis0) std np.std(X, axis0) return (X - mean) / std, mean, std X_std, X_mean, X_std_dev standardize(X) print(標準化后的前5個樣本:\n, X_std[:5])注意這里標準化存儲了均值和標準差這是一個好習慣。在后續(xù)將新樣本投影到LDA空間時你需要用同樣的均值和標準差先對新樣本進行標準化然后再乘以我們求得的投影矩陣W。很多初學(xué)者會忘記這一步導(dǎo)致預(yù)測錯誤。4.2 核心計算類內(nèi)與類間散度矩陣這是LDA算法的核心步驟。我們需要分別計算每個類別的均值、總體均值然后套用公式計算Sw和Sb。def compute_lda_components(X, y): 計算LDA所需的散度矩陣和投影向量 參數(shù): X: 標準化后的特征矩陣 (n_samples, n_features) y: 標簽向量 (n_samples,) 返回: W: 投影矩陣 (n_features, n_components) eigenvalues: 特征值 n_features X.shape[1] labels np.unique(y) # 計算總體均值 overall_mean np.mean(X, axis0) # 初始化 Sw 和 Sb 為零矩陣 Sw np.zeros((n_features, n_features)) Sb np.zeros((n_features, n_features)) for label in labels: # 獲取當前類別的樣本 X_i X[y label] # 計算當前類別的均值 mean_i np.mean(X_i, axis0) # 計算當前類別的類內(nèi)散度矩陣并累加到 Sw # X_i - mean_i 是中心化后的矩陣 Sw (X_i - mean_i).T (X_i - mean_i) # 等價于 np.cov(X_i.T, biasTrue) * (len(X_i)-1) # 計算當前類別的類間散度矩陣貢獻并累加到 Sb n_i len(X_i) mean_diff (mean_i - overall_mean).reshape(-1, 1) # 轉(zhuǎn)換為列向量 Sb n_i * (mean_diff mean_diff.T) # 求解廣義特征值問題 Sw^{-1} Sb w λ w # 使用 np.linalg.eig 求解但更穩(wěn)定的方式是使用 np.linalg.pinv(Sw) Sb # 因為Sw可能接近奇異使用偽逆更穩(wěn)健 Sw_inv np.linalg.pinv(Sw) # 使用偽逆代替逆提高數(shù)值穩(wěn)定性 M Sw_inv Sb # 計算特征值和特征向量 eigenvalues, eigenvectors np.linalg.eig(M) # 特征值和特征向量是復(fù)數(shù)由于數(shù)值計算我們?nèi)嵅?eigenvalues np.real(eigenvalues) eigenvectors np.real(eigenvectors) # 按特征值降序排序 idx eigenvalues.argsort()[::-1] eigenvalues eigenvalues[idx] eigenvectors eigenvectors[:, idx] # 我們通常選擇非零特征值對應(yīng)的特征向量。 # 對于二分類問題Sb的秩最大為1因此最多只有一個非零特征值對應(yīng)一個投影方向。 # 我們選取前 min(n_features, len(labels)-1) 個特征向量對于二分類就是1個。 n_components min(n_features, len(labels) - 1) W eigenvectors[:, :n_components] return W, eigenvalues W, eig_vals compute_lda_components(X_std, y) print(投影向量 W (列向量):\n, W) print(對應(yīng)的特征值:\n, eig_vals) print(投影向量形狀:, W.shape)關(guān)鍵點解析Sw的計算(X_i - mean_i).T (X_i - mean_i)是協(xié)方差矩陣的(n-1)倍。這是計算類內(nèi)散度最直接的方式。Sb的計算mean_diff被重塑為列向量mean_diff mean_diff.T得到一個矩陣乘以樣本數(shù)n_i后累加。使用偽逆np.linalg.pinv這是實現(xiàn)中的關(guān)鍵技巧。直接求逆np.linalg.inv(Sw)在Sw接近奇異即特征間相關(guān)性很強或樣本數(shù)少于特征數(shù)時會報錯。偽逆提供了數(shù)值上更穩(wěn)定的解。特征值分解結(jié)果np.linalg.eig返回的特征值和特征向量可能是復(fù)數(shù)這是浮點數(shù)計算中的微小誤差導(dǎo)致的我們?nèi)嵅考纯伞M队熬S度選擇LDA能降維到的最大維度是min(n_features, n_classes-1)。對于二分類問題無論原始特征有多少維投影后最多只有一維一條直線。我們的西瓜數(shù)據(jù)是二維特征二分類所以W是一個(2, 1)的矩陣即一個投影方向。4.3 數(shù)據(jù)投影與結(jié)果可視化得到投影方向W后我們就可以將原始數(shù)據(jù)投影到這條直線上并直觀地看到分類效果。def project_and_visualize(X, y, W): 將數(shù)據(jù)投影到LDA方向并可視化 # 投影: Y X * W X_projected X W # 創(chuàng)建畫布 fig, axes plt.subplots(1, 2, figsize(14, 5)) # 子圖1: 原始數(shù)據(jù)及LDA投影方向 ax1 axes[0] # 繪制原始數(shù)據(jù)點 ax1.scatter(X[y1, 0], X[y1, 1], cred, markero, label好瓜, alpha0.7, s80) ax1.scatter(X[y0, 0], X[y0, 1], cblue, markers, label壞瓜, alpha0.7, s80) # 繪制LDA投影方向直線 # 直線的方向由W決定我們過原點畫一條線因為數(shù)據(jù)已標準化中心在原點 # 生成直線上的點 line_x np.linspace(X[:,0].min()-0.5, X[:,0].max()0.5, 100) # W是(2,1)矩陣W[1]/W[0]是斜率 if abs(W[0]) 1e-10: # 避免除零 line_y (W[1]/W[0]) * line_x ax1.plot(line_x, line_y, k--, linewidth2, labelfLDA投影方向 (斜率{W[1]/W[0]:.3f})) else: # 如果W[0]接近0說明投影方向接近y軸 ax1.axvline(x0, colork, linestyle--, linewidth2, labelLDA投影方向 (垂直)) # 繪制樣本點到投影方向的垂線可選更直觀 for i in range(len(X)): x_point X[i] # 計算投影點坐標 # 投影到方向向量W上 proj (x·w)/(w·w) * w w_vec W.flatten() proj_coeff np.dot(x_point, w_vec) / np.dot(w_vec, w_vec) proj_point proj_coeff * w_vec ax1.plot([x_point[0], proj_point[0]], [x_point[1], proj_point[1]], gray, alpha0.3, linewidth0.5) ax1.set_xlabel(密度 (標準化后)) ax1.set_ylabel(含糖率 (標準化后)) ax1.set_title(原始數(shù)據(jù)與LDA投影方向) ax1.legend() ax1.grid(True, linestyle--, alpha0.5) ax1.axis(equal) # 子圖2: 投影后的數(shù)據(jù)分布直方圖 ax2 axes[1] # 獲取兩類投影后的值 proj_good X_projected[y1].flatten() proj_bad X_projected[y0].flatten() # 繪制直方圖 ax2.hist(proj_good, bins8, alpha0.7, colorred, label好瓜, edgecolorblack) ax2.hist(proj_bad, bins8, alpha0.7, colorblue, label壞瓜, edgecolorblack) # 計算并繪制兩類投影中心的垂線 mean_good np.mean(proj_good) mean_bad np.mean(proj_bad) ax2.axvline(xmean_good, colordarkred, linestyle-, linewidth2, labelf好瓜中心 ({mean_good:.3f})) ax2.axvline(xmean_bad, colordarkblue, linestyle-, linewidth2, labelf壞瓜中心 ({mean_bad:.3f})) # 計算一個簡單的分類閾值兩類中心的平均值 simple_threshold (mean_good mean_bad) / 2 ax2.axvline(xsimple_threshold, colorgreen, linestyle--, linewidth3, labelf分類閾值 ({simple_threshold:.3f})) ax2.set_xlabel(投影值) ax2.set_ylabel(頻數(shù)) ax2.set_title(投影后數(shù)據(jù)分布直方圖) ax2.legend() ax2.grid(True, linestyle--, alpha0.5) plt.tight_layout() plt.show() return X_projected, simple_threshold X_proj, threshold project_and_visualize(X_std, y, W) print(f投影后數(shù)據(jù)形狀: {X_proj.shape}) print(f建議的分類閾值 (兩類中心均值): {threshold:.4f})可視化解讀左圖你可以清晰地看到一條黑色虛線那就是LDA找到的最佳投影方向。所有樣本點都沿著灰色細線“垂落”到這條黑線上。我們的目標就是讓紅色圓圈好瓜和藍色方塊壞瓜在這條黑線上的投影點盡可能地各自聚集且兩團離得遠。右圖直方圖展示了投影后的結(jié)果。紅色和藍色的柱子分別代表好瓜和壞瓜投影值的分布。兩條深色豎線是它們的中心綠色虛線是取兩個中心點中點得到的簡單分類閾值??梢钥吹皆谶@個一維投影空間里兩類數(shù)據(jù)基本被分開了只有極少部分重疊。這證明了LDA投影的有效性。4.4 分類器構(gòu)建與性能評估投影之后我們實際上得到了一個一維特征。在這個一維空間里構(gòu)建分類器就非常簡單了最常見的就是用一個閾值來劃分。def lda_classifier(X_projected, threshold): 簡單的閾值分類器 # 假設(shè)投影后好瓜的投影值通常更大取決于W的方向 # 如果投影值大于閾值預(yù)測為好瓜(1)否則為壞瓜(0) return (X_projected threshold).astype(int).flatten() # 在訓(xùn)練集上應(yīng)用分類器 y_pred lda_classifier(X_proj, threshold) # 計算準確率 accuracy np.mean(y_pred y) print(f基于LDA投影和閾值分類的訓(xùn)練集準確率: {accuracy:.2%}) # 更嚴謹?shù)淖龇ú榭捶诸悰Q策 print(\n樣本預(yù)測詳情 (編號 真實標簽 投影值 預(yù)測標簽):) for i in range(len(X)): print(f{i1:2d}: {y[i]} - {X_proj[i,0]:7.4f} - {y_pred[i]} {? if y[i]y_pred[i] else ?})注意事項這里我們直接在訓(xùn)練集上評估準確率會偏高過擬合。在實際項目中必須使用未見過的測試集或交叉驗證來評估模型性能。對于這個微型教學(xué)數(shù)據(jù)集我們主要是為了理解流程。5. 與scikit-learn實現(xiàn)對比及高級話題5.1 使用scikit-learn驗證為了驗證我們手寫的LDA是否正確最好的辦法是調(diào)用業(yè)界標準的scikit-learn實現(xiàn)進行對比。def compare_with_sklearn(X_std, y): 與scikit-learn的LDA進行對比 # 使用sklearn的LDA指定降維到1維 sk_lda SKLDA(n_components1) X_proj_sk sk_lda.fit_transform(X_std, y) # 注意sklearn的fit_transform直接返回降維后的數(shù)據(jù) # 獲取sklearn計算出的投影向量 # sklearn的coef_是判別函數(shù)的系數(shù)方向與我們的W有關(guān)但不直接相等。 # 更直接的是獲取標量化后的特征向量。 # sklearn將投影向量存儲在scalings_屬性中 W_sk sk_lda.scalings_[:, :1] # 取第一列 print(scikit-learn 計算出的投影向量 (scalings_):\n, W_sk) print(我們手寫代碼計算出的投影向量 W:\n, W) # 比較方向是否一致可能差一個符號因為方向相反不影響分類 # 計算兩個向量的余弦相似度絕對值 cos_sim np.abs(np.dot(W.flatten(), W_sk.flatten()) / (np.linalg.norm(W) * np.linalg.norm(W_sk))) print(f\n投影向量方向余弦相似度 (絕對值): {cos_sim:.6f}) print((非常接近1說明方向一致)) # 比較投影后的數(shù)據(jù)可能差一個縮放因子和符號 # 將我們的投影數(shù)據(jù)與sklearn的進行相關(guān)性比較 corr np.corrcoef(X_proj.flatten(), X_proj_sk.flatten())[0,1] print(f投影后數(shù)據(jù)的相關(guān)系數(shù): {corr:.6f}) print((非常接近1或-1說明線性相關(guān)即投影本質(zhì)相同)) # 使用sklearn的模型進行預(yù)測 y_pred_sk sk_lda.predict(X_std) accuracy_sk np.mean(y_pred_sk y) print(f\nscikit-learn LDA 訓(xùn)練集準確率: {accuracy_sk:.2%}) print(f我們手寫LDA的準確率: {np.mean(lda_classifier(X_proj, threshold)y):.2%}) compare_with_sklearn(X_std, y)運行這段代碼你會發(fā)現(xiàn)我們的投影方向W與sklearn的scalings_可能方向相反余弦相似度接近-1但投影后數(shù)據(jù)的相關(guān)系數(shù)絕對值接近1。這是完全正常的因為投影直線沒有正反方向之分w和-w是同一個子空間。分類準確率也應(yīng)該是一致的。這個對比驗證了我們手寫LDA核心邏輯的正確性。5.2 多分類LDA與維度選擇我們的例子是二分類。對于多分類問題C2個類別LDA的原理完全一樣。此時Sb矩陣的秩最大為C-1。這意味著我們最多可以找到C-1個有意義的投影方向?qū)?yīng)C-1個非零特征值。這些投影方向張成了一個最多C-1維的子空間。在代碼實現(xiàn)上你只需要修改n_components參數(shù)例如設(shè)置為min(n_features, n_classes-1)然后取前n_components個特征向量組成投影矩陣W。投影后的數(shù)據(jù)X_projected X W將是一個(n_samples, n_components)的矩陣。如何選擇降維后的維度k一個常用的方法是觀察特征值。特征值λ_i的大小代表了對應(yīng)投影方向的重要性區(qū)分能力。你可以計算累計貢獻率累計貢獻率 前k個特征值之和 / 所有特征值之和然后選擇一個k使得累計貢獻率達到一個較高的閾值如95%。對于分類任務(wù)有時直接選擇k n_classes - 1也是一個合理的默認值。5.3 LDA的假設(shè)與局限性沒有哪個算法是萬能的LDA也不例外它建立在幾個關(guān)鍵假設(shè)之上正態(tài)性假設(shè)每個類別的數(shù)據(jù)特征服從多元正態(tài)分布。在實際中雖然輕微偏離此假設(shè)LDA仍可工作但嚴重偏離會影響性能。同方差性假設(shè)所有類別的協(xié)方差矩陣相同即Sw矩陣。這是我們推導(dǎo)中Sw是各類別協(xié)方差矩陣之和的前提。如果不同類別數(shù)據(jù)形狀差異很大比如一個類是圓形散開另一個類是扁橢圓形LDA的效果會打折扣。線性可分性LDA尋找的是線性投影。如果原始數(shù)據(jù)本身是非線性可分的比如環(huán)形分布線性投影無法很好地區(qū)分此時需要考慮核判別分析KDA或其他非線性方法。一個重要的實操心得在應(yīng)用LDA前快速檢查數(shù)據(jù)是否大致符合這些假設(shè)是個好習慣。例如可以分別畫出每個類別特征的分布直方圖或Q-Q圖來檢查正態(tài)性可以計算每個類別的協(xié)方差矩陣并觀察它們是否相似。當假設(shè)被嚴重違反時樸素貝葉斯分類器對特征獨立性假設(shè)敏感或二次判別分析QDA放松同方差假設(shè)可能是更好的選擇。6. 常見問題、調(diào)試技巧與擴展應(yīng)用6.1 報錯與問題排查速查表問題現(xiàn)象可能原因解決方案Sw矩陣奇異無法求逆1. 樣本數(shù)少于特征數(shù)。2. 特征之間存在完全線性相關(guān)。1. 增加樣本或使用正則化Sw λI。2. 進行特征選擇或先用PCA降維。特征值/特征向量出現(xiàn)復(fù)數(shù)數(shù)值計算誤差Sw^{-1}Sb不是嚴格的對稱矩陣。取np.real()獲取實部通常不影響結(jié)果?;蚴褂胹cipy.linalg.eigh求解廣義特征值問題。投影后分類效果很差1. LDA的假設(shè)正態(tài)、同方差嚴重不滿足。2. 數(shù)據(jù)本質(zhì)是非線性可分的。1. 嘗試對數(shù)據(jù)進行變換如對數(shù)變換使其更接近正態(tài)。2. 使用QDA、核方法或非線性分類器。與sklearn結(jié)果符號相反投影方向w和-w是等價的。檢查投影后數(shù)據(jù)的相關(guān)系數(shù)如果絕對值接近1則結(jié)果一致??赏ㄟ^比較分類準確率驗證。多分類時維度超過C-1試圖獲取多于C-1個投影方向。將n_components設(shè)置為min(n_features, n_classes-1)。6.2 性能優(yōu)化與數(shù)值穩(wěn)定技巧正則化Regularization當Sw接近奇異時在Sw上加一個小的正則化項λ * I單位矩陣是標準做法。這相當于在優(yōu)化目標中加入了對投影向量w的L2范數(shù)懲罰防止過擬合并提高數(shù)值穩(wěn)定性。lambda_reg 1e-4 # 一個很小的正數(shù) Sw_reg Sw lambda_reg * np.eye(Sw.shape[0]) Sw_inv np.linalg.inv(Sw_reg) # 此時可以直接求逆使用更穩(wěn)定的求解器對于廣義特征值問題Sb w λ Sw w可以使用scipy.linalg.eigh函數(shù)它專門用于求解厄米特實對稱矩陣的特征值問題并支持廣義形式數(shù)值上更穩(wěn)定。from scipy.linalg import eigh eigenvalues, eigenvectors eigh(Sb, Sw) # 直接求解廣義特征值問題先進行PCA預(yù)降維白化如果特征維數(shù)很高可以先使用PCA將數(shù)據(jù)降到較低維度比如保留95%方差然后再應(yīng)用LDA。這能有效緩解小樣本問題并去除噪聲。6.3 超越西瓜數(shù)據(jù)集LDA的實際應(yīng)用場景理解了LDA在西瓜數(shù)據(jù)集上的運作后你可以將其應(yīng)用到更廣泛的領(lǐng)域圖像識別與計算機視覺在人臉識別中LDA常被稱為Fisherfaces被用來提取最能區(qū)分不同人臉的線性特征。將每張人臉圖像拉成一個高維向量LDA可以找到區(qū)分不同人的低維子空間。生物信息學(xué)與醫(yī)學(xué)診斷在基因表達數(shù)據(jù)分析中樣本患者有成百上千個基因特征的表達量標簽是疾病類型如癌癥亞型。LDA可以用于尋找對區(qū)分疾病亞型最重要的基因組合并實現(xiàn)降維可視化。文本分類與主題模型注意這里的LDA通常是潛在狄利克雷分布與線性判別分析同名但完全不同。不過線性判別分析本身也可以用于文本分類例如將文檔的詞袋模型向量投影到低維空間后再分類。故障診斷與質(zhì)量控制在工業(yè)生產(chǎn)中傳感器會采集設(shè)備的多維運行數(shù)據(jù)。LDA可以學(xué)習正常狀態(tài)和各類故障狀態(tài)下的數(shù)據(jù)投影當新數(shù)據(jù)投影點偏離正常區(qū)域時即可預(yù)警。最后再分享一個小技巧當你使用LDA作為分類前的降維工具時不妨將它與最簡單的分類器如最近鄰、線性SVM在投影后的低維空間上結(jié)合。這樣構(gòu)建的管道Pipeline往往比直接在原始高維空間使用復(fù)雜分類器更高效、更不容易過擬合且模型可解釋性更強。你可以用sklearn.pipeline.Pipeline輕松實現(xiàn)這一流程并利用網(wǎng)格搜索來優(yōu)化LDA的維度和分類器的參數(shù)。本文還有配套的精品資源點擊獲取