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