證避坑)
簡(jiǎn)介Matlab數(shù)據(jù)降維工具箱面向科研人員、高校師生和算法開(kāi)發(fā)者適合高維數(shù)據(jù)特征壓縮、可視化探索、分類聚類前處理以及多種降維方法橫向?qū)Ρ?。?nèi)容覆蓋PCA、LDA、ICA、MDS等經(jīng)典線性方法也包含Isomap、LLE、Laplacian、LTSA、DiffusionMaps等流形學(xué)習(xí)方案并延伸至核PCA、核LDA、SPCA、CCA、MVU與自編碼器等方法譜系較完整。壓縮包共84個(gè)文件約1011KB以57個(gè).m源碼為主體配合多平臺(tái)mex二進(jìn)制、C/C及頭文件、少量可執(zhí)行程序和說(shuō)明文本兼顧直接調(diào)用與跨平臺(tái)編譯擴(kuò)展。已有4285人學(xué)習(xí)適合快速搭建降維實(shí)驗(yàn)基線、比較算法表現(xiàn)或按需改造源碼的研究與工程場(chǎng)景。1. 一張 200 多列的特征表這套 Matlab 降維工具箱解決哪個(gè)環(huán)節(jié)一張 200 多列的特征表丟進(jìn) MatlabPCA 跑完前兩個(gè)主成分只解釋了三成多的方差散點(diǎn)圖糊成一團(tuán)——這時(shí)候缺的不是再換一個(gè)算法而是一套能把預(yù)處理、降維、驗(yàn)證串起來(lái)的 Matlab 降維工具。這套 Dimensionality Reduction 工具箱把線性方法PCA、LDA、因子分析與非線性嵌入Isomap、LLE、t-SNE、UMAP收在相近的調(diào)用習(xí)慣下附示例腳本和小尺寸演示數(shù)據(jù)裝好就能跑通從特征表到二維圖、再到下游分類器的完整鏈路。適合做信號(hào)與圖像特征分析的研究生、要把高維指標(biāo)壓成可視化報(bào)表的算法工程師也適合給課程設(shè)計(jì)找一份能改能調(diào)的實(shí)現(xiàn)。2. 算法選型PCA、LDA、Isomap、t-SNE 各自的適用邊界降維翻車八成不是因?yàn)榇a寫(xiě)錯(cuò)而是因?yàn)橐婚_(kāi)始就把方法用錯(cuò)了場(chǎng)景。把 t-SNE 的二維圖當(dāng)成“可分性證據(jù)”去做特征篩選把 PCA 用在一條卷起來(lái)的螺旋流形上最后得出的結(jié)論必然站不住。這一章先把選型的判斷依據(jù)講清楚再落到工具箱里各函數(shù)的調(diào)用約定上后面動(dòng)手才有方向。2.1 先判斷數(shù)據(jù)是不是躺在一張彎曲的薄片上選型的第一步不是看算法列表而是看數(shù)據(jù)的局部結(jié)構(gòu)。我一般會(huì)抽 300500 個(gè)樣本用原始特征的前三個(gè)主成分畫(huà)一次三維散點(diǎn)再對(duì)同一批樣本算一下每列特征的相關(guān)系數(shù)矩陣。如果前幾個(gè)主成分就能吃掉大部分方差、相關(guān)系數(shù)矩陣?yán)镉忻黠@的塊狀結(jié)構(gòu)說(shuō)明數(shù)據(jù)近似線性PCA 或因子分析夠用如果三維圖里出現(xiàn)明顯的彎折、環(huán)狀、卷曲主成分解釋率卻上不去那就說(shuō)明數(shù)據(jù)落在一張非線性流形上得換 Isomap、LLE 或 t-SNE 這類基于近鄰圖的方法。第二件事是確認(rèn)有沒(méi)有標(biāo)簽。有標(biāo)簽且要做分類LDA 是有監(jiān)督投影里最省事的選擇它顯式地最大化類間散度與類內(nèi)散度的比值投影后的維度上限是“類別數(shù)減一”這一點(diǎn)必須提前算清楚別拿 10 類數(shù)據(jù)去要 20 維 LDA 結(jié)果。沒(méi)有標(biāo)簽就只用無(wú)監(jiān)督方法不要偷偷把標(biāo)簽喂進(jìn)去調(diào)參數(shù)那是另一種形式的泄漏。第三件事是數(shù)據(jù)的量級(jí)。t-SNE 的復(fù)雜度大致隨樣本數(shù)呈平方增長(zhǎng)樣本過(guò)萬(wàn)時(shí)在普通工作站上會(huì)很吃力工具箱里那套 Barnes-Hut 近似實(shí)現(xiàn)能緩解一部分壓力但內(nèi)存峰值依然要看近鄰圖。Isomap 和 LLE 要先構(gòu) kNN 圖再求特征分解樣本數(shù)上萬(wàn)時(shí)稀疏矩陣的構(gòu)造本身就會(huì)成為瓶頸。樣本量大、只想要一個(gè)穩(wěn)定的低維表示PCA 依舊是性價(jià)比最高的起點(diǎn)。2.2 工具箱統(tǒng)一的調(diào)用約定輸入矩陣、輸出得分、附加結(jié)構(gòu)體這套工具箱在接口上做了一件很討巧的事所有方法都遵循“輸入樣本×特征矩陣輸出低維得分矩陣”的主約定附加信息放在第二個(gè)返回值里。PCA 的第二個(gè)返回值是主成分系數(shù)與解釋方差I(lǐng)somap 和 LLE 返回的是近鄰圖與殘差t-SNE 返回的是收斂信息。這樣一來(lái)第 3 章那條鏈路里替換算法只需要改一行調(diào)用后面的出圖和驗(yàn)證代碼完全不用動(dòng)。調(diào)用前有兩個(gè)約定必須記住。第一輸入必須是數(shù)值矩陣table要先轉(zhuǎn)成double類別型、時(shí)間型列一律提前處理掉工具箱不會(huì)替你猜。第二絕大多數(shù)方法默認(rèn)把每一列當(dāng)成一個(gè)特征、每一行當(dāng)成一個(gè)樣本如果你的數(shù)據(jù)是轉(zhuǎn)置的很多傳感器數(shù)據(jù)導(dǎo)出就是這樣必須顯式轉(zhuǎn)置否則降維結(jié)果看上去“能跑”但每一個(gè)維度都在混樣本屬于最難排查的一類錯(cuò)誤。注意工具箱里的示例腳本用的是自帶的小尺寸演示數(shù)據(jù)直接換成自己的表之前先確認(rèn)列方向與樣本數(shù)別急著調(diào)參數(shù)。2.3 四類方法的對(duì)照與選擇順序把方法按“是否需要標(biāo)簽、是否線性、能否增量更新、可解釋性”四個(gè)維度擺開(kāi)選型就變成了查表。下面這張表是我自己常用的決策順序從左往右逐項(xiàng)排除剩下的就是候選。方法需要標(biāo)簽線性可解釋性典型用途主要代價(jià)PCA否是高載荷可回溯原始特征去冗余、壓縮、可視化對(duì)非線性流形無(wú)能為力因子分析否是高有潛變量含義問(wèn)卷、指標(biāo)建模需要指定因子數(shù)并做旋轉(zhuǎn)LDA是是中判別方向可解釋有監(jiān)督分類前的投影維度上限為類別數(shù)減一Isomap否否中測(cè)地距離可解釋卷曲流形的展開(kāi)對(duì)近鄰數(shù)敏感圖不連通會(huì)報(bào)錯(cuò)LLE否否低局部結(jié)構(gòu)保持對(duì)新樣本無(wú)自然映射t-SNE否否低簇間距離無(wú)意義高維特征的可視化檢查慢參數(shù)敏感不可當(dāng)特征用UMAP否否低可視化 粗略的降維特征依賴近鄰數(shù)與最小距離參數(shù)一個(gè)常見(jiàn)的誤用是把 t-SNE 的輸出直接當(dāng)特征送進(jìn)分類器。t-SNE 只保留局部鄰域概率結(jié)構(gòu)簇與簇之間的距離、簇內(nèi)方差都沒(méi)有意義用它做特征分類器學(xué)到的是一張隨機(jī)布局圖。要降維后接下游模型優(yōu)先 PCA、LDA 或 UMAP要給人看才用 t-SNE。3. 跑通第一條鏈路從原始特征表到可解釋的二維圖這一章把整條鏈路走一遍讀表、清洗、標(biāo)準(zhǔn)化、PCA 定維、非線性嵌入出圖、結(jié)果寫(xiě)回。每一步都給可抄的代碼代碼后面說(shuō)明為什么這么寫(xiě)、參數(shù)改了會(huì)怎樣。跑完這一遍你手上就有一份能替換成自己數(shù)據(jù)的骨架。3.1 裝載與清洗把 ID 列摘出去很多特征表的第一列是樣本編號(hào)第二列可能是采集時(shí)間直接table2array會(huì)把字符串列變成NaN或者干脆報(bào)錯(cuò)。穩(wěn)妥的寫(xiě)法是先聲明導(dǎo)入選項(xiàng)再把非數(shù)值列單獨(dú)摘出來(lái)保存后面出圖時(shí)還要用它們標(biāo)注點(diǎn)。% 讀入特征表保留原始列名避免中文列名被自動(dòng)改寫(xiě) opts detectImportOptions(features_demo.csv); opts.VariableNamingRule preserve; T readtable(features_demo.csv, opts); % 第一列當(dāng)樣本 ID其余列當(dāng)特征非數(shù)值列在這里就被擋掉了 sampleId string(T{:, 1}); X table2array(T(:, 2:end)); if ~isa(X, double) X double(X); end % 缺失值處理先統(tǒng)計(jì)再?zèng)Q定別上來(lái)就填 0 missRate mean(isnan(X), 1); fprintf(含缺失的列數(shù)%d最高缺失率 %.2f%%\n, sum(missRate 0), 100*max(missRate)); X fillmissing(X, constant, 0);detectImportOptions比csvread穩(wěn)它會(huì)推斷每列類型VariableNamingRule設(shè)成preserve是為了保住原始列名后面看 PCA 載荷時(shí)要靠它對(duì)應(yīng)回業(yè)務(wù)含義。缺失值那段先打印統(tǒng)計(jì)再填是因?yàn)椤罢邪顺啥际强铡边@種列直接補(bǔ) 0 等于往數(shù)據(jù)里灌噪聲正確做法是整列刪掉。填 0 只是為了讓后面zscore不返回NaN如果缺失率超過(guò) 5%建議換成按列中位數(shù)填充或者干脆用工具箱里帶缺失處理的變體。3.2 標(biāo)準(zhǔn)化與 PCA用累計(jì)方差定維數(shù)特征量綱不統(tǒng)一是 PCA 最常見(jiàn)的翻車點(diǎn)。一個(gè)以毫伏為單位的通道和一個(gè)以百分比為單位的通道放在一起前者方差大幾十倍第一主成分幾乎就是它自己。標(biāo)準(zhǔn)化之后再分解各列權(quán)重才可比。% 按列 z-score 標(biāo)準(zhǔn)化同時(shí)保留 mu/sigma 供新樣本復(fù)用 [Xn, mu, sigma] zscore(X); % 已經(jīng)中心化過(guò)這里關(guān)掉 pca 的內(nèi)部中心化避免二次處理 [coeff, score, latent, tsq, explained] pca(Xn, ... Algorithm, svd, Centered, false); cumVar cumsum(explained); k find(cumVar 95, 1, first); fprintf(累計(jì)解釋 95%% 方差需要 %d 個(gè)主成分共 %d 維\n, k, size(X, 2)); fprintf(前三個(gè)主成分解釋率%.2f%% / %.2f%% / %.2f%%\n, explained(1:3));zscore的三個(gè)返回值要一起留著以后來(lái)了新樣本必須用訓(xùn)練集的mu和sigma變換不能用新樣本自己的統(tǒng)計(jì)量否則訓(xùn)練和推理的口徑不一致這是第 5 章要展開(kāi)的坑之一。Algorithm選svd在特征數(shù)遠(yuǎn)大于樣本數(shù)時(shí)更穩(wěn)eig在維度低時(shí)更快兩者結(jié)果在數(shù)值上等價(jià)但符號(hào)可能相反PCA 主成分的正負(fù)號(hào)本來(lái)就沒(méi)有絕對(duì)意義別因?yàn)閮纱闻艹鰜?lái)圖翻轉(zhuǎn)了就以為代碼有問(wèn)題。k的取法這里用 95% 閾值是最省事的一種。實(shí)際項(xiàng)目里我一般會(huì)同時(shí)看碎石圖的拐點(diǎn)兩者差得多的時(shí)候以拐點(diǎn)為準(zhǔn)因?yàn)榉讲铋撝禃?huì)被少數(shù)高方差噪聲列帶偏。如果后面要接分類器更靠譜的做法是直接把k當(dāng)超參數(shù)用交叉驗(yàn)證選這一點(diǎn)放到 4.1 講。3.3 非線性嵌入困惑度與近鄰數(shù)怎么給初值PCA 出來(lái)的圖如果仍然糊成一團(tuán)就該上非線性方法了。t-SNE 的困惑度可以理解為“每個(gè)點(diǎn)預(yù)期的有效鄰居數(shù)”經(jīng)驗(yàn)初值取樣本數(shù)的平方根再夾到 550 之間近鄰圖方法Isomap、LLE、UMAP的 k 一般從 1015 起再按圖的連通性調(diào)整。rng(20240501); % 固定隨機(jī)種子保證同一份數(shù)據(jù)每次出圖一致 % 先用 PCA 降到 3050 維再喂 t-SNE既降噪也提速 Y tsne(score(:, 1:min(50, k)), ... NumDimensions, 2, ... Perplexity, 30, ... Exaggeration, 4, ... LearnRate, 500, ... Distance, euclidean, ... Standardize, false, ... Verbose, 1); % 近鄰圖方法k 從 12 起返回值里帶殘差可以判斷是否退化 [Yiso, ~, info] isomap(Xn, NumNeighbors, 12, NumDimensions, 2); fprintf(Isomap 殘差%.4f\n, info.reconstructionError(end));先 PCA 再 t-SNE 是常規(guī)做法把幾百維降到 50 維既能壓掉大部分噪聲維度也能把 t-SNE 的耗時(shí)砍掉一大截。Perplexity小于 5 時(shí)結(jié)果會(huì)退化成局部小團(tuán)大于 50 時(shí)計(jì)算量陡增且結(jié)構(gòu)被抹平30 是個(gè)安全的起點(diǎn)。Exaggeration控制早期階段的簇間斥力值大一些簇分得更開(kāi)但太大會(huì)造出并不存在的空隙4 到 12 之間比較常見(jiàn)。Distance這一項(xiàng)容易被忽略特征都是正值的計(jì)數(shù)型數(shù)據(jù)比如詞頻、像素強(qiáng)度累計(jì)用cosine往往比euclidean更能反映相似性標(biāo)準(zhǔn)化后的連續(xù)特征就老實(shí)待著用歐氏距離。Standardize在已經(jīng)做過(guò) z-score 的情況下必須關(guān)掉否則相當(dāng)于標(biāo)準(zhǔn)化了兩遍各列權(quán)重會(huì)被再壓一次。Isomap 返回的info.reconstructionError是個(gè)有用的診斷量它隨維度增加應(yīng)當(dāng)單調(diào)下降如果在某個(gè)維度之后基本不動(dòng)說(shuō)明再加維度也榨不出結(jié)構(gòu)了。另外isomap對(duì)圖連通性很敏感k 太小時(shí)會(huì)出現(xiàn)孤立點(diǎn)函數(shù)會(huì)給出警告或者返回NaN這時(shí)候把 k 往上加兩三個(gè)再試。3.4 出圖與導(dǎo)出把坐標(biāo)寫(xiě)回表里降維結(jié)果最終要么給人看要么給下游用兩條路都得把坐標(biāo)存成結(jié)構(gòu)化數(shù)據(jù)別只留一個(gè)scatter。我通常把低維坐標(biāo)和原始 ID、標(biāo)簽拼成一張表導(dǎo)出方便后面用別的工具復(fù)核。% 拼成交付用的表ID 標(biāo)簽 二維坐標(biāo) 前若干個(gè)主成分 outTbl table(sampleId, Y(:,1), Y(:,2), ... VariableNames, {SampleId, Dim1, Dim2}); writetable(outTbl, embedding_tsne.csv); % 出圖按標(biāo)簽著色點(diǎn)大小隨局部密度微調(diào) figure(Color, w); gscatter(Y(:,1), Y(:,2), label, [], ., 12); xlabel(t-SNE 1); ylabel(t-SNE 2); title(sprintf(perplexity30, k%d, k)); exportgraphics(gcf, embedding_tsne.png, Resolution, 300);gscatter比手動(dòng)循環(huán)scatter省事它會(huì)自動(dòng)分配顏色。exportgraphics比saveas清晰度高投稿或者寫(xiě)報(bào)告都能直接用。導(dǎo)出表里額外保留前若干個(gè)主成分是有意義的可視化靠 t-SNE下游建???PCA兩條線并行不沖突也方便后面對(duì)照“圖上看著分開(kāi)”和“數(shù)值上確實(shí)可分”是不是一回事。提示圖上的簇間距不要當(dāng)相似度讀。t-SNE 只保證鄰域概率結(jié)構(gòu)兩個(gè)簇在圖上一左一右不代表它們?cè)谠伎臻g里離得遠(yuǎn)。4. 參數(shù)標(biāo)定維數(shù)、近鄰數(shù)、困惑度和隨機(jī)種子參數(shù)亂給是降維結(jié)果的第二大殺手。同一份數(shù)據(jù)k 取 5 和取 50 出來(lái)的圖能講出兩個(gè)完全相反的結(jié)論。這一章把四個(gè)關(guān)鍵參數(shù)拆開(kāi)講每個(gè)參數(shù)控制什么、初值怎么給、怎么用下游任務(wù)反過(guò)來(lái)驗(yàn)證。4.1 目標(biāo)維數(shù)的三種定法方差閾值、平行分析、下游精度方差閾值最直觀也最容易被噪聲帶偏。當(dāng)數(shù)據(jù)里混著幾個(gè)高方差的儀器漂移通道時(shí)前兩個(gè)主成分可能就是這倆通道累計(jì)方差很快到 90%但結(jié)構(gòu)一點(diǎn)沒(méi)提出來(lái)。這種情況要先做異常通道篩查或者改用穩(wěn)健的協(xié)方差估計(jì)。平行分析是更嚴(yán)謹(jǐn)?shù)囊环N把原始矩陣的每一列各自獨(dú)立打亂做一次 PCA得到一條“純?cè)肼曄碌慕忉尫讲钋€”真實(shí)曲線的拐點(diǎn)要高于這條噪聲曲線才保留。實(shí)現(xiàn)只需要十幾行跑一次幾十秒比拍腦袋定閾值強(qiáng)。% 平行分析打亂各列內(nèi)部順序破壞列間相關(guān)性保留邊緣分布 nPerm 20; noiseVar zeros(size(Xn, 2), nPerm); for p 1:nPerm Xp zeros(size(Xn)); for j 1:size(Xn, 2) Xp(:, j) Xn(randperm(size(Xn,1)), j); end [~, ~, ~, ~, expP] pca(Xp, Algorithm, svd, Centered, false); noiseVar(:, p) expP; end noise95 prctile(noiseVar, 95, 2); % 每個(gè)維度的噪聲解釋率上界 kParallel find(explained(:) noise95, 1, first) - 1; fprintf(平行分析建議保留 %d 維\n, kParallel);這段代碼的關(guān)鍵在“按列獨(dú)立打亂”列與列之間的相關(guān)性被破壞了但每列自己的分布沒(méi)變所以噪聲曲線反映的是“如果這些特征彼此無(wú)關(guān)PCA 能提出來(lái)多少”。真實(shí)解釋率低于噪聲上界的那一刻就說(shuō)明再往后提的都是噪聲。第三種也是最貼合業(yè)務(wù)的把維數(shù)當(dāng)超參數(shù)用交叉驗(yàn)證后的分類或回歸精度來(lái)選。做法是讓 k 在 5 到 100 之間按步長(zhǎng)掃一遍每個(gè) k 都跑一次完整的“降維 訓(xùn)練 驗(yàn)證”取精度最高且方差最小的那個(gè)。這種定法計(jì)算量最大但它優(yōu)化的是你真正關(guān)心的指標(biāo)尤其是后面要接模型的時(shí)候前兩種方法只當(dāng)粗篩。4.2 近鄰圖參數(shù)k 太小碎片化太大抹平結(jié)構(gòu)Isomap、LLE、UMAP 都依賴 kNN 圖k 決定了“局部”有多局部。k 太小圖變成一堆互不相連的小碎片測(cè)地距離算不出來(lái)LLE 的重構(gòu)權(quán)重矩陣會(huì)病態(tài)k 太大鄰域跨越了流形的彎折處測(cè)地距離把不同分支上的點(diǎn)串成一條直線展開(kāi)后的結(jié)構(gòu)全錯(cuò)。實(shí)操里我一般這樣定先取 k 10畫(huà)出圖看有沒(méi)有明顯的孤立點(diǎn)或者整片區(qū)域被壓成一條線。有孤立團(tuán)就往上加每次加 5直到圖連通如果加上去之后原本分開(kāi)的兩簇黏在一起說(shuō)明已經(jīng)過(guò)頭了退回去取中間值。工具箱里的近鄰圖診斷函數(shù)能直接輸出連通分量個(gè)數(shù)比肉眼判斷準(zhǔn)。注意UMAP 的n_neighbors和 t-SNE 的Perplexity不是一回事。前者直接控制近鄰圖規(guī)模后者是概率分布的熵目標(biāo)兩者雖然都在調(diào)“局部性”但敏感區(qū)間和調(diào)法完全不同別套用經(jīng)驗(yàn)值。4.3 用下游分類器給降維結(jié)果打分降維好不好最終看下游任務(wù)吃不吃得下。最省事的評(píng)估是固定分類器我一般用線性 SVM 或 KNN 做基線換不同的降維方法和維數(shù)比 5 折交叉驗(yàn)證的精度。之所以用簡(jiǎn)單分類器是因?yàn)閺?fù)雜模型會(huì)把降維的差異吃掉你調(diào)了半天以為降維有用其實(shí)模型自己在高維空間里也學(xué)到了。參數(shù)控制什么初值敏感區(qū)間失控表現(xiàn)目標(biāo)維數(shù) k保留信息量累計(jì)方差 95%5100太大引入噪聲太小丟失判別信息近鄰數(shù) k圖方法局部鄰域尺度1015550太小圖碎片化太大抹平流形Perplexity鄰域概率熵30550太小出小團(tuán)太大糊成一片Exaggeration早期簇間斥力4412太大造出假空隙隨機(jī)種子初始化固定整數(shù)—不固定則圖不可復(fù)現(xiàn)評(píng)估時(shí)有一個(gè)細(xì)節(jié)要注意降維和分類器必須在小折內(nèi)重新擬合不能在全量數(shù)據(jù)上先降維再切折。這一點(diǎn)在 6 章用一整段代碼展開(kāi)這里先記住結(jié)論只要降維看到了驗(yàn)證折的數(shù)據(jù)精度就是虛高的。4.4 復(fù)現(xiàn)性種子、并行與浮點(diǎn)差異t-SNE 和 UMAP 都是隨機(jī)初始化不固定種子你每次截圖都不一樣匯報(bào)的時(shí)候說(shuō)不清是哪一版。固定rng是基本操作但還有兩個(gè)細(xì)節(jié)一是并行循環(huán)里每個(gè) worker 的隨機(jī)流是獨(dú)立的parfor里調(diào) t-SNE 需要給每次迭代單獨(dú)設(shè)種子二是不同 Matlab 版本、不同 BLAS 庫(kù)的浮點(diǎn)累加順序不同同一顆種子在 A 機(jī)器和 B 機(jī)器上仍可能有微小差異圖會(huì)差一點(diǎn)點(diǎn)。% parfor 里保證每個(gè)任務(wù)獨(dú)立可復(fù)現(xiàn) seeds 20240501 (0:nRuns-1); results cell(nRuns, 1); parfor r 1:nRuns rs RandStream(Threefry, Seed, seeds(r)); RandStream.setGlobalStream(rs); results{r} tsne(score(:, 1:k), NumDimensions, 2, ... Perplexity, 30, Verbose, 0); endThreefry是支持并行獨(dú)立流的生成器用它比默認(rèn)的Mersenne Twister在parfor里更安全。多次運(yùn)行不同種子還能干一件有用的事把多次結(jié)果的鄰域保持度比如 KL 散度終值統(tǒng)計(jì)一下波動(dòng)特別大的話說(shuō)明參數(shù)落在了不穩(wěn)定區(qū)間這本身就是個(gè)信號(hào)比單次跑出來(lái)的漂亮圖可信得多。5. 避坑與排查五個(gè)把降維結(jié)果做廢的細(xì)節(jié)下面五條都是我在實(shí)際項(xiàng)目里真踩過(guò)或者幫人排查過(guò)的每一條按“現(xiàn)象、原因、解決”寫(xiě)??粗嵥榈恳粭l都能讓一整天的結(jié)果白跑。5.1 預(yù)處理順序錯(cuò)位兩個(gè)最隱蔽的坑現(xiàn)象一模型在訓(xùn)練集上精度 0.95換一批新數(shù)據(jù)掉到 0.6。原因是在全量數(shù)據(jù)上做了zscore和 PCA然后才切分訓(xùn)練測(cè)試集驗(yàn)證折的信息通過(guò)均值和主成分方向漏進(jìn)了訓(xùn)練過(guò)程。這種做法在離線評(píng)估時(shí)看不出問(wèn)題因?yàn)樵u(píng)估本身也被污染了。解決辦法是把mu、sigma、coeff都當(dāng)成模型的一部分只在訓(xùn)練折上擬合驗(yàn)證折用訓(xùn)練折的參數(shù)做變換具體寫(xiě)法見(jiàn) 6 章。任何時(shí)候你發(fā)現(xiàn)“離線指標(biāo)好得不像話”先查這條?,F(xiàn)象二PCA 載荷圖里第一主成分幾乎等于某一個(gè)通道其他通道權(quán)重接近 0。原因是沒(méi)做標(biāo)準(zhǔn)化那列的原始量綱遠(yuǎn)大于其他列。解決辦法是分解前先zscore同時(shí)把pca的Centered關(guān)掉避免二次中心化。如果那列本身就該占主導(dǎo)比如它確實(shí)是主信號(hào)那另說(shuō)但大多數(shù)情況是量綱問(wèn)題。5.2 參數(shù)與評(píng)估口徑圖好看不等于結(jié)論對(duì)現(xiàn)象三t-SNE 圖上兩類分得很開(kāi)換成線性 SVM 一測(cè)精度只有 0.55。原因是把可視化當(dāng)成了可分性證據(jù)。t-SNE 會(huì)放大局部密度差異隨機(jī)標(biāo)簽跑出來(lái)的圖照樣有漂亮的簇。解決辦法是任何“看起來(lái)分開(kāi)了”的結(jié)論都必須用下游分類器的交叉驗(yàn)證兜底圖只用來(lái)發(fā)現(xiàn)異常樣本和檢查數(shù)據(jù)質(zhì)量?,F(xiàn)象四換個(gè)隨機(jī)種子重跑原本清晰的三簇變成了兩簇。原因是Perplexity落在了不穩(wěn)定區(qū)間或者樣本里有一批近重復(fù)點(diǎn)。解決辦法是把 perplexity 在 1050 之間掃一遍看簇結(jié)構(gòu)在多大范圍內(nèi)保持穩(wěn)定只在穩(wěn)定區(qū)間里取參數(shù)近重復(fù)點(diǎn)先做去重或加微小擾動(dòng)。一個(gè)只在特定種子下成立的結(jié)論不要寫(xiě)進(jìn)報(bào)告?,F(xiàn)象五Isomap 跑出來(lái)一片NaN或者圖縮成一個(gè)點(diǎn)。原因是 k 太小導(dǎo)致近鄰圖不連通測(cè)地距離矩陣?yán)锍霈F(xiàn)無(wú)窮大特征分解自然崩。解決辦法是逐步加大NumNeighbors直到連通分量為 1工具箱的近鄰圖診斷能直接給出這個(gè)數(shù)字。如果加到很大才連通說(shuō)明樣本本身在空間里分成了幾個(gè)相距很遠(yuǎn)的團(tuán)這時(shí)候該先懷疑是不是混進(jìn)了不同來(lái)源的數(shù)據(jù)。5.3 內(nèi)存與性能樣本量上來(lái)之后的現(xiàn)實(shí)問(wèn)題現(xiàn)象六五萬(wàn)條樣本跑 t-SNE內(nèi)存直接爆掉。原因是完整的相似度矩陣是 n×n 的五萬(wàn)樣本就是 2.5e9 個(gè)元素雙精度下 20GB 起步。解決辦法有三條先用 PCA 降到 50 維以內(nèi)減少距離計(jì)算開(kāi)銷打開(kāi)近似算法選項(xiàng)把精確的近鄰搜索換成近似搜索再不行就分層抽樣先在一萬(wàn)條上把參數(shù)定下來(lái)再對(duì)全量做一次固定參數(shù)的嵌入。別指望在小樣本上定好的參數(shù)直接搬到全量上還完全一致樣本規(guī)模變了合適的 perplexity 也會(huì)變。一言以蔽之降維這條鏈路上報(bào)錯(cuò)信息從來(lái)都指向代碼真正的問(wèn)題幾乎都藏在數(shù)據(jù)形態(tài)和評(píng)估流程里??吹疆惓O然仡^看數(shù)據(jù)比盯著函數(shù)簽名強(qiáng)。6. 進(jìn)階把降維塞進(jìn)交叉驗(yàn)證堵住標(biāo)簽泄漏前面反復(fù)提到“降維必須在折內(nèi)做”這一段給一個(gè)能直接抄的完整寫(xiě)法。核心思路是把標(biāo)準(zhǔn)化、PCA、分類器打包成一條“在訓(xùn)練折上擬合、在驗(yàn)證折上只用 transform”的管道每一折都從頭來(lái)一遍。cvp cvpartition(label, KFold, 5, Stratify, true); acc zeros(cvp.NumTestSets, 1); for i 1:cvp.NumTestSets tr training(cvp, i); te test(cvp, i); % 只用訓(xùn)練折估計(jì) mu / sigma / 主成分方向 [Xtr, mu, sigma] zscore(X(tr, :)); [coeff, scoreTr, ~, ~, explained] pca(Xtr, ... Algorithm, svd, Centered, false); k find(cumsum(explained) 95, 1, first); Ztr scoreTr(:, 1:k); % 驗(yàn)證折用訓(xùn)練折的參數(shù)變換絕不重新擬合 Zte ((X(te, :) - mu) ./ sigma) * coeff(:, 1:k); mdl fitcecoc(Ztr, label(tr), Learners, linear); pred predict(mdl, Zte); acc(i) mean(pred label(te)); end fprintf(5 折精度 %.3f ± %.3f\n, mean(acc), std(acc));這段代碼里有兩處必須照抄而不能“優(yōu)化”。第一Zte的計(jì)算用的是訓(xùn)練折的mu、sigma和coeff如果偷懶寫(xiě)成對(duì)X(te,:)再跑一次zscore和pca驗(yàn)證折的統(tǒng)計(jì)量就參與了變換精度會(huì)虛高幾個(gè)百分點(diǎn)而且這個(gè)偏差在小數(shù)據(jù)集上特別明顯。第二fitcecoc在每個(gè)折里重新訓(xùn)練不能在外面訓(xùn)好一個(gè)模型循環(huán)里只換數(shù)據(jù)。這兩條加起來(lái)才叫真正的“折內(nèi)降維”。如果要把降維方法當(dāng)超參數(shù)一起搜用fitcecoc外面套一層bayesopt或者簡(jiǎn)單的網(wǎng)格掃把k和近鄰數(shù)放進(jìn)搜索空間。注意parfor和cvpartition一起用時(shí)隨機(jī)流要單獨(dú)設(shè)否則各折的劃分雖然固定降維內(nèi)部的隨機(jī)初始化還是會(huì)飄。還有一個(gè)容易被忽略的點(diǎn)LDA 這類有監(jiān)督降維標(biāo)簽必須只來(lái)自訓(xùn)練折。有人圖省事在全量數(shù)據(jù)上跑一次 LDA 再切折這比 PCA 泄漏更嚴(yán)重因?yàn)榕袆e方向直接把測(cè)試折的標(biāo)簽信息編碼進(jìn)去了精度能虛高十幾個(gè)點(diǎn)而且離線怎么調(diào)都“很好”一上線就原形畢露。我現(xiàn)在的習(xí)慣是只要流程里出現(xiàn)zscore、pca、tsne這些詞就先寫(xiě)一個(gè)空的折循環(huán)確認(rèn)所有擬合動(dòng)作都在tr的索引里再往里填模型。有次趕進(jìn)度跳過(guò)了這一步回頭查一個(gè)“訓(xùn)練集 0.98、線上 0.61”的模型翻了半天代碼才發(fā)現(xiàn)標(biāo)準(zhǔn)化寫(xiě)在了切折之前血淚經(jīng)驗(yàn)從此再?zèng)]省過(guò)這五行循環(huán)。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取