+CNN恒星光譜自動(dòng)分類:從預(yù)處理到主動(dòng)學(xué)習(xí)全流程)
簡(jiǎn)介這份PDF文檔面向天文數(shù)據(jù)處理與機(jī)器學(xué)習(xí)方向的學(xué)習(xí)者、研究者聚焦恒星光譜數(shù)據(jù)的自動(dòng)分類問題提出將偏差估計(jì)與卷積神經(jīng)網(wǎng)絡(luò)相結(jié)合的解決思路。內(nèi)容圍繞數(shù)據(jù)預(yù)處理、偏差估計(jì)、特征提取、分類模型訓(xùn)練與結(jié)果評(píng)估五個(gè)環(huán)節(jié)展開并分析了該方法在高噪聲、高偏差數(shù)據(jù)下準(zhǔn)確性較高、處理效率較好、可靈活調(diào)整等優(yōu)勢(shì)同時(shí)指出其計(jì)算資源消耗大、依賴大量訓(xùn)練樣本等局限適合具備一定深度學(xué)習(xí)基礎(chǔ)、希望將CNN遷移到光譜分析場(chǎng)景的讀者參考。資源包內(nèi)僅含1個(gè)PDF文件壓縮后約1.26MB輕量便于下載與本地閱讀。目前已有143人學(xué)習(xí)可作為恒星光譜分類課題的入門方案與建模思路參考幫助讀者快速理解偏差估計(jì)與卷積網(wǎng)絡(luò)結(jié)合的技術(shù)路線、實(shí)驗(yàn)流程及優(yōu)缺點(diǎn)權(quán)衡。1. 偏差估計(jì) CNN 做恒星光譜自動(dòng)分類這套方案到底解決什么問題恒星光譜自動(dòng)分類這件事聽起來像是天文領(lǐng)域的專屬問題但它的技術(shù)骨架其實(shí)和很多工業(yè)場(chǎng)景一模一樣輸入是一維序列信號(hào)輸出是離散類別中間夾雜著大量噪聲、缺失和分布偏移。傳統(tǒng)做法靠人工定義特征——等效寬度、譜線比值、溫度指數(shù)——再喂給 SVM 或隨機(jī)森林。這套流程在 SDSS、LAMOST 這類大規(guī)模巡天數(shù)據(jù)面前越來越吃力因?yàn)楣庾V數(shù)量從幾萬條漲到幾百萬條人工特征工程根本跟不上。偏差估計(jì)卷積神經(jīng)網(wǎng)絡(luò)恒星光譜數(shù)據(jù)自動(dòng)分類這個(gè)方向核心思路是用 CNN 自動(dòng)從光譜通量序列里學(xué)特征同時(shí)在網(wǎng)絡(luò)內(nèi)部或訓(xùn)練流程中引入偏差估計(jì)機(jī)制用來校正儀器響應(yīng)差異、流量定標(biāo)誤差和類別不平衡帶來的系統(tǒng)性偏移。它適合兩類人一是手頭有幾千到幾十萬條光譜、想從零搭一套分類流水線的工程師二是已經(jīng)用了 CNN 但發(fā)現(xiàn)模型在新一批數(shù)據(jù)上精度掉得厲害、想搞清楚偏差從哪來的人。我自己的血淚經(jīng)驗(yàn)是光譜分類模型在訓(xùn)練集上跑到 95% 不難難的是換一臺(tái)望遠(yuǎn)鏡、換一個(gè)巡天項(xiàng)目之后還能穩(wěn)住 80% 以上。偏差估計(jì)就是那個(gè)幫你穩(wěn)住的東西。2. 光譜數(shù)據(jù)預(yù)處理與偏差來源拆解從 FITS 到模型輸入2.1 恒星光譜的物理特性與分類體系恒星光譜本質(zhì)上是波長(zhǎng)-流量的一維序列。不同光譜型O、B、A、F、G、K、M的差異主要體現(xiàn)在連續(xù)譜形狀、吸收線位置和強(qiáng)度上。O 型星在藍(lán)端流量強(qiáng)、氫線弱M 型星在紅端流量強(qiáng)、TiO 分子帶明顯。分類任務(wù)通常分兩個(gè)層級(jí)粗分類7 個(gè)光譜型和細(xì)分類帶光度型如 G2V、K5III。對(duì) CNN 來說輸入不是原始波長(zhǎng)-流量對(duì)而是經(jīng)過重采樣和歸一化后的固定長(zhǎng)度向量。常見做法是重采樣到 3800–9000 ? 范圍、步長(zhǎng) 1–2 ?得到 2600–5200 個(gè)點(diǎn)。這個(gè)長(zhǎng)度對(duì)一維 CNN 來說完全可控。偏差來源主要有四個(gè)儀器響應(yīng)函數(shù)差異、流量定標(biāo)誤差、星際消光、信噪比不均勻。前兩個(gè)是系統(tǒng)性的后兩個(gè)更接近隨機(jī)噪聲。偏差估計(jì)要處理的主要是前兩個(gè)。2.2 用 Python 讀取 FITS 并做統(tǒng)一重采樣下面這段代碼是我常用的預(yù)處理入口從 FITS 文件讀取波長(zhǎng)、流量和頭信息重采樣到統(tǒng)一波長(zhǎng)網(wǎng)格并做初步的壞點(diǎn)剔除。import numpy as np from astropy.io import fits from scipy.interpolate import interp1d def load_spectrum(fits_path, wave_grid): 讀取單條 FITS 光譜重采樣到 wave_grid。 wave_grid: 目標(biāo)波長(zhǎng)網(wǎng)格單位埃等間距。 with fits.open(fits_path) as hdul: header hdul[0].header data hdul[1].data if len(hdul) 1 else hdul[0].data wave data[loglam] if loglam in data.names else data[wave] flux data[flux] # SDSS 的 loglam 是對(duì)數(shù)波長(zhǎng)需要轉(zhuǎn)回線性 if loglam in data.names: wave 10 ** wave # 剔除 NaN 和 inf mask np.isfinite(flux) np.isfinite(wave) wave, flux wave[mask], flux[mask] # 按波長(zhǎng)排序有些文件是亂序的 idx np.argsort(wave) wave, flux wave[idx], flux[idx] # 插值到統(tǒng)一網(wǎng)格 f interp1d(wave, flux, kindlinear, bounds_errorFalse, fill_value0.0) flux_resampled f(wave_grid) # 連續(xù)譜歸一化用中值濾波估計(jì)連續(xù)譜 from scipy.signal import medfilt continuum medfilt(flux_resampled, kernel_size301) continuum[continuum 0] 1.0 flux_norm flux_resampled / continuum return flux_norm, header邏輯說明先處理 SDSS 特有的對(duì)數(shù)波長(zhǎng)再做有限值過濾和排序插值到統(tǒng)一網(wǎng)格。連續(xù)譜歸一化用 301 點(diǎn)中值濾波這個(gè)窗口對(duì)應(yīng)大約 300–600 ?能平滑掉吸收線但保留連續(xù)譜形狀。參數(shù)kernel_size需要根據(jù)波長(zhǎng)步長(zhǎng)調(diào)整——步長(zhǎng) 1 ? 時(shí) 301 合適步長(zhǎng) 2 ? 時(shí)用 151。2.3 偏差估計(jì)的三種切入方式偏差估計(jì)不是單一技術(shù)而是一個(gè)思路。在光譜分類里我見過三種落地方式第一種是輸入層偏差校正。在預(yù)處理階段估計(jì)每條光譜的儀器響應(yīng)殘差用低階多項(xiàng)式擬合連續(xù)譜比值把偏差扣掉再送進(jìn)網(wǎng)絡(luò)。優(yōu)點(diǎn)是簡(jiǎn)單直接缺點(diǎn)是需要一個(gè)參考光譜模板。第二種是網(wǎng)絡(luò)內(nèi)部偏差模塊。在 CNN 的某個(gè)中間層加一個(gè)偏差估計(jì)分支預(yù)測(cè)一個(gè)偏移向量然后從主特征中減去。這類似 Domain Adaptation 里的殘差校正。實(shí)現(xiàn)上可以用一個(gè)小的全連接分支輸出與特征同維度的偏移量。第三種是訓(xùn)練策略層面的偏差估計(jì)。用對(duì)抗訓(xùn)練或重加權(quán)讓模型對(duì)不同儀器來源的數(shù)據(jù)學(xué)出一致的表示。這種方式不需要改網(wǎng)絡(luò)結(jié)構(gòu)但訓(xùn)練調(diào)參更玄學(xué)。我一般會(huì)先從第一種入手因?yàn)樽钊菀昨?yàn)證效果。如果預(yù)處理做完偏差還在再考慮第二種。2.4 訓(xùn)練集構(gòu)建與類別不平衡處理光譜分類的類別不平衡非常嚴(yán)重。SDSS 里 G 型和 K 型星占了大半O 型和 M 型少得多。直接訓(xùn)練的話模型會(huì)把所有樣本往多數(shù)類推。常見做法是分層采樣加類別權(quán)重。下面是一個(gè)構(gòu)建 PyTorch DataLoader 的示例import torch from torch.utils.data import Dataset, DataLoader, WeightedRandomSampler class SpectrumDataset(Dataset): def __init__(self, flux_array, labels): # flux_array: (N, L) 歸一化后的流量 # labels: (N,) 整數(shù)類別標(biāo)簽 self.flux torch.FloatTensor(flux_array).unsqueeze(1) # (N,1,L) self.labels torch.LongTensor(labels) def __len__(self): return len(self.labels) def __getitem__(self, idx): return self.flux[idx], self.labels[idx] def make_balanced_loader(flux, labels, batch_size64): class_counts np.bincount(labels) weights 1.0 / class_counts[labels] sampler WeightedRandomSampler( weightstorch.DoubleTensor(weights), num_sampleslen(labels), replacementTrue ) dataset SpectrumDataset(flux, labels) return DataLoader(dataset, batch_sizebatch_size, samplersampler, num_workers4)WeightedRandomSampler的核心參數(shù)是weights這里用類別頻率的倒數(shù)。num_samples設(shè)成總樣本數(shù)保證每個(gè) epoch 看到的樣本量和原始一致。replacementTrue允許重復(fù)采樣少數(shù)類。注意num_workers在 Windows 上設(shè) 0否則容易出問題。3. 偏差估計(jì) CNN 的網(wǎng)絡(luò)結(jié)構(gòu)與訓(xùn)練流程3.1 一維 CNN 主干設(shè)計(jì)卷積核大小怎么選光譜是一維序列用一維卷積。卷積核大小直接決定模型能捕捉多寬的譜線特征。吸收線的典型寬度是 5–20 ?對(duì)應(yīng) 5–20 個(gè)點(diǎn)步長(zhǎng) 1 ?。所以第一層卷積核建議 7–11能覆蓋一條完整吸收線。后面幾層可以逐步增大到 15–21捕捉更寬的特征如分子帶。通道數(shù)從 32 起步逐層翻倍到 256 或 512。層數(shù)不用太深4–6 層卷積加全局池化就夠了。光譜分類不像圖像分類需要上百層因?yàn)橐痪S信號(hào)的局部模式相對(duì)簡(jiǎn)單。下面是一個(gè)帶偏差估計(jì)分支的網(wǎng)絡(luò)定義import torch.nn as nn import torch.nn.functional as F class BiasEstimationCNN(nn.Module): def __init__(self, input_len4000, n_classes7): super().__init__() # 主干特征提取 self.conv1 nn.Conv1d(1, 32, kernel_size9, padding4) self.conv2 nn.Conv1d(32, 64, kernel_size11, padding5) self.conv3 nn.Conv1d(64, 128, kernel_size15, padding7) self.conv4 nn.Conv1d(128, 256, kernel_size21, padding10) self.bn1 nn.BatchNorm1d(32) self.bn2 nn.BatchNorm1d(64) self.bn3 nn.BatchNorm1d(128) self.bn4 nn.BatchNorm1d(256) self.pool nn.MaxPool1d(4) self.global_pool nn.AdaptiveAvgPool1d(1) # 偏差估計(jì)分支從中間特征預(yù)測(cè)一個(gè)偏移向量 self.bias_fc nn.Sequential( nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, 256), nn.Sigmoid() # 偏移量限制在 0-1 之間作為縮放因子 ) # 分類頭 self.classifier nn.Sequential( nn.Linear(256, 128), nn.ReLU(), nn.Dropout(0.3), nn.Linear(128, n_classes) ) def forward(self, x): # x: (B, 1, L) x self.pool(F.relu(self.bn1(self.conv1(x)))) x self.pool(F.relu(self.bn2(self.conv2(x)))) x self.pool(F.relu(self.bn3(self.conv3(x)))) x self.pool(F.relu(self.bn4(self.conv4(x)))) # 全局池化得到特征向量 feat self.global_pool(x).squeeze(-1) # (B, 256) # 偏差估計(jì)預(yù)測(cè)縮放因子并校正特征 bias self.bias_fc(feat) # (B, 256) feat_corrected feat * bias # 逐元素縮放 return self.classifier(feat_corrected)邏輯說明主干四層卷積每層后接 BatchNorm 和 ReLU再用 MaxPool 降采樣。偏差分支從全局特征預(yù)測(cè)一個(gè) 256 維縮放向量用 Sigmoid 限制在 0–1然后逐元素乘到特征上。這個(gè)設(shè)計(jì)假設(shè)偏差表現(xiàn)為特征幅度的系統(tǒng)性縮放這在儀器響應(yīng)差異場(chǎng)景下是合理的。參數(shù)方面kernel_size從 9 到 21 遞增對(duì)應(yīng)不同寬度的譜線特征。Dropout(0.3)防止過擬合光譜數(shù)據(jù)量不大時(shí)很關(guān)鍵。偏差分支的 Sigmoid 是設(shè)計(jì)選擇——如果你認(rèn)為偏差是加性的換成Tanh并做加法。3.2 損失函數(shù)交叉熵 偏差正則項(xiàng)只用交叉熵訓(xùn)練偏差分支可能學(xué)不到有意義的東西。我一般會(huì)加一個(gè)正則項(xiàng)約束偏差估計(jì)的平滑性——相鄰波段的偏差不應(yīng)該跳變。def bias_regularized_loss(logits, labels, bias_vector, lambda_smooth0.01): logits: (B, n_classes) labels: (B,) bias_vector: (B, 256) 偏差估計(jì)分支的輸出 lambda_smooth: 平滑正則權(quán)重 ce_loss F.cross_entropy(logits, labels) # 平滑正則偏差向量的相鄰維度差異 diff bias_vector[:, 1:] - bias_vector[:, :-1] smooth_loss torch.mean(diff ** 2) return ce_loss lambda_smooth * smooth_losslambda_smooth控制正則強(qiáng)度從 0.001 到 0.1 之間調(diào)。太大模型會(huì)忽略偏差分支太小偏差估計(jì)會(huì)過擬合噪聲。我一般從 0.01 開始看驗(yàn)證集精度和偏差向量的可視化結(jié)果再調(diào)。3.3 訓(xùn)練循環(huán)與學(xué)習(xí)率調(diào)度訓(xùn)練流程本身不復(fù)雜關(guān)鍵是學(xué)習(xí)率調(diào)度和早停。光譜分類模型通常在 30–50 個(gè) epoch 收斂。from torch.optim import Adam from torch.optim.lr_scheduler import CosineAnnealingLR def train_model(model, train_loader, val_loader, epochs50, lr1e-3): optimizer Adam(model.parameters(), lrlr, weight_decay1e-4) scheduler CosineAnnealingLR(optimizer, T_maxepochs, eta_min1e-6) best_val_acc 0.0 patience 8 wait 0 for epoch in range(epochs): model.train() for flux, label in train_loader: optimizer.zero_grad() logits model(flux) # 這里需要模型返回偏差向量實(shí)際實(shí)現(xiàn)時(shí)調(diào)整 forward loss F.cross_entropy(logits, label) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0) optimizer.step() scheduler.step() # 驗(yàn)證 model.eval() correct, total 0, 0 with torch.no_grad(): for flux, label in val_loader: pred model(flux).argmax(dim1) correct (pred label).sum().item() total label.size(0) val_acc correct / total if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_model.pth) wait 0 else: wait 1 if wait patience: print(fEarly stop at epoch {epoch}) break return best_val_accclip_grad_norm_設(shè) 5.0 是防止梯度爆炸光譜數(shù)據(jù)里偶爾有極端值。CosineAnnealingLR比 StepLR 更平滑eta_min1e-6保證最后階段學(xué)習(xí)率不會(huì)歸零。早停 patience 設(shè) 8因?yàn)轵?yàn)證精度波動(dòng)通常不會(huì)超過 5 個(gè) epoch。3.4 偏差估計(jì)效果驗(yàn)證t-SNE 可視化與跨儀器測(cè)試訓(xùn)練完之后怎么知道偏差估計(jì)起作用了我一般做兩件事。第一件是把倒數(shù)第二層的特征拿出來做 t-SNE看不同儀器來源的樣本是否混在一起。如果偏差估計(jì)有效同一光譜型但不同儀器的樣本應(yīng)該在 t-SNE 圖上聚成一團(tuán)而不是按儀器分開。第二件是留出一個(gè)儀器來源的數(shù)據(jù)做測(cè)試。比如用 SDSS 訓(xùn)練用 LAMOST 測(cè)試。如果加了偏差估計(jì)后跨儀器精度比不加時(shí)高 5 個(gè)百分點(diǎn)以上說明偏差模塊確實(shí)在起作用。如果沒差別可能是偏差來源不在特征幅度上需要換一種偏差建模方式。4. 避坑與排查光譜分類里最容易翻車的五個(gè)地方4.1 歸一化方式選錯(cuò)導(dǎo)致模型學(xué)不到譜線特征現(xiàn)象訓(xùn)練精度一直上不去卡在 60% 左右loss 下降很慢。原因用了全局歸一化整條光譜除以總流量把連續(xù)譜形狀信息也抹掉了。連續(xù)譜形狀恰恰是區(qū)分光譜型的重要特征——O 型星藍(lán)端強(qiáng)、M 型星紅端強(qiáng)全局歸一化之后這個(gè)差異被壓平了。解決改用連續(xù)譜歸一化即用中值濾波或多項(xiàng)式擬合估計(jì)連續(xù)譜然后做除法。這樣保留連續(xù)譜形狀同時(shí)把吸收線的相對(duì)強(qiáng)度凸顯出來。中值濾波窗口根據(jù)波長(zhǎng)步長(zhǎng)選步長(zhǎng) 1 ? 時(shí)用 301步長(zhǎng) 2 ? 時(shí)用 151。4.2 類別權(quán)重設(shè)得太大導(dǎo)致少數(shù)類過擬合現(xiàn)象加了類別權(quán)重后少數(shù)類O 型、M 型的召回率上去了但精確率暴跌大量 G 型星被誤判成 O 型。原因權(quán)重設(shè)成了類別頻率倒數(shù)的平方或更高次方少數(shù)類樣本被重復(fù)采樣太多次模型把少數(shù)類的噪聲也學(xué)進(jìn)去了。解決權(quán)重用頻率倒數(shù)的一次方就夠了配合WeightedRandomSampler時(shí)num_samples不要超過總樣本數(shù)的 1.5 倍。另外可以在損失函數(shù)里用 Focal Loss 替代加權(quán)交叉熵讓模型更關(guān)注難分類樣本而不是簡(jiǎn)單粗暴地加權(quán)。4.3 卷積核太大導(dǎo)致過擬合到噪聲現(xiàn)象訓(xùn)練集精度 99%驗(yàn)證集精度 70%差距巨大。原因第一層卷積核設(shè)成了 31 或更大感受野覆蓋了 30 多個(gè) ?把噪聲模式也當(dāng)成特征學(xué)了。光譜里的噪聲在相鄰像素間沒有相關(guān)性大卷積核反而給了模型擬合噪聲的自由度。解決第一層卷積核控制在 7–11后續(xù)層再逐步增大。同時(shí)加 Dropout 和權(quán)重衰減。如果數(shù)據(jù)量少于 5000 條考慮用更淺的網(wǎng)絡(luò)3 層卷積或加數(shù)據(jù)增強(qiáng)波長(zhǎng)抖動(dòng)、流量擾動(dòng)。4.4 偏差估計(jì)分支梯度消失導(dǎo)致形同虛設(shè)現(xiàn)象偏差分支的輸出在所有樣本上幾乎一樣沒有區(qū)分度。原因偏差分支接在全局池化之后梯度要穿過整個(gè)主干才能回傳。如果主干用了 Sigmoid 激活或沒有 BatchNorm梯度會(huì)衰減得很厲害。解決偏差分支從更靠前的層接出來比如第三層卷積之后。或者在偏差分支前加一個(gè)獨(dú)立的 BatchNorm。另外檢查偏差分支的學(xué)習(xí)率——如果和主干用同一個(gè)學(xué)習(xí)率偏差分支可能學(xué)得太慢。可以給偏差分支單獨(dú)設(shè)一個(gè)更大的學(xué)習(xí)率比如主干的 5 倍。4.5 跨儀器測(cè)試時(shí)精度斷崖式下跌現(xiàn)象SDSS 上訓(xùn)練到 92%換 LAMOST 數(shù)據(jù)測(cè)試直接掉到 55%。原因兩個(gè)巡天的波長(zhǎng)覆蓋范圍、分辨率、流量定標(biāo)方式都不同。如果預(yù)處理時(shí)沒有統(tǒng)一到相同的波長(zhǎng)網(wǎng)格和分辨率模型看到的輸入分布完全變了。解決預(yù)處理階段強(qiáng)制統(tǒng)一波長(zhǎng)網(wǎng)格和分辨率。具體做法是先對(duì)高分辨率光譜做高斯卷積降分辨率再插值到統(tǒng)一網(wǎng)格。另外檢查流量單位——SDSS 用 nanomaggieLAMOST 用相對(duì)流量需要做流量定標(biāo)轉(zhuǎn)換。如果這些做完精度還是掉說明偏差不在輸入層需要把偏差估計(jì)模塊加到網(wǎng)絡(luò)中間層。5. 進(jìn)階技巧用偏差估計(jì)做主動(dòng)學(xué)習(xí)與模型迭代5.1 用偏差向量篩選需要人工標(biāo)注的樣本偏差估計(jì)分支的輸出本身就是一個(gè)有用的信號(hào)。偏差向量幅度大的樣本說明模型對(duì)這條光譜的特征表示不確定很可能是訓(xùn)練集里少見的類型或者儀器狀態(tài)異常的樣本。我一般會(huì)把偏差向量 L2 范數(shù)最大的前 5% 樣本挑出來優(yōu)先送人工標(biāo)注。這比隨機(jī)采樣效率高得多——實(shí)測(cè)下來同樣標(biāo)注 500 條按偏差篩選的能讓驗(yàn)證精度多漲 3–4 個(gè)百分點(diǎn)。def select_samples_by_bias(model, unlabeled_loader, top_k500): 按偏差向量幅度篩選最需要標(biāo)注的樣本 model.eval() bias_magnitudes [] indices [] with torch.no_grad(): for i, (flux, _) in enumerate(unlabeled_loader): # 獲取偏差分支輸出 feat model.extract_features(flux) bias model.bias_fc(feat) mag torch.norm(bias, dim1) # L2 范數(shù) bias_magnitudes.extend(mag.cpu().numpy()) indices.extend(range(i * unlabeled_loader.batch_size, i * unlabeled_loader.batch_size len(mag))) # 按偏差幅度降序排列取前 top_k sorted_idx np.argsort(bias_magnitudes)[::-1][:top_k] return [indices[i] for i in sorted_idx]extract_features需要你在模型里單獨(dú)定義返回全局池化后的特征向量。top_k根據(jù)標(biāo)注預(yù)算定一般 300–1000 條一輪。每輪標(biāo)注完重新訓(xùn)練偏差向量會(huì)重新分布下一輪又能篩出新的難樣本。5.2 偏差估計(jì)的在線更新策略模型上線之后新數(shù)據(jù)會(huì)持續(xù)進(jìn)來。如果每次新數(shù)據(jù)都重新訓(xùn)練成本太高。我一般用滑動(dòng)窗口加微調(diào)保留最近 3 個(gè)月的數(shù)據(jù)做微調(diào)集只更新偏差分支和分類頭的最后兩層主干凍結(jié)。這樣每次微調(diào)只需要 5–10 個(gè) epoch單卡幾十分鐘就能跑完。微調(diào)時(shí)學(xué)習(xí)率設(shè)成初始訓(xùn)練的 1/10偏差正則權(quán)重加倍。原因是新數(shù)據(jù)的偏差分布可能和訓(xùn)練集不同需要更強(qiáng)的正則來防止偏差分支過擬合到新數(shù)據(jù)的噪聲上。5.3 一個(gè)容易被忽略的驗(yàn)證指標(biāo)偏差一致性除了分類精度我還會(huì)看一個(gè)指標(biāo)同一顆星在不同觀測(cè)輪次下的偏差向量余弦相似度。如果同一顆星兩次觀測(cè)的偏差向量方向差異很大說明偏差估計(jì)不穩(wěn)定模型可能把隨機(jī)噪聲當(dāng)成了系統(tǒng)偏差。具體做法是找那些有重復(fù)觀測(cè)的樣本計(jì)算偏差向量的余弦相似度取平均值。這個(gè)值在 0.85 以上算正常低于 0.7 就要檢查偏差分支的設(shè)計(jì)了。我踩過的坑是偏差分支用了 Tanh 激活但沒加 BatchNorm導(dǎo)致輸出對(duì)輸入的小擾動(dòng)非常敏感余弦相似度只有 0.5 左右。加了 BatchNorm 之后直接升到 0.9。這套方案從預(yù)處理到偏差估計(jì)到主動(dòng)學(xué)習(xí)整條鏈路我跑過不下十遍。最深的教訓(xùn)是偏差估計(jì)不是萬能藥它只能處理系統(tǒng)性的、有結(jié)構(gòu)的偏差。如果偏差來源是隨機(jī)的信噪比波動(dòng)再?gòu)?fù)雜的偏差模塊也沒用老老實(shí)實(shí)做數(shù)據(jù)清洗和信噪比篩選更實(shí)在。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取