:從數(shù)據(jù)預(yù)處理到深度學(xué)習(xí)模型構(gòu)建與調(diào)優(yōu))
1. 項目概述從“看見”到“看懂”的飛躍高光譜分類聽起來是個挺學(xué)術(shù)的詞但說白了它就是讓機器像經(jīng)驗豐富的專家一樣不僅能“看見”物體更能“看懂”物體到底是什么。我們?nèi)搜劭吹降氖澜缡腔诩t、綠、藍三原色混合而成的“真彩色”圖像。但自然界中每種物質(zhì)都有其獨特的光譜“指紋”——它們對不同波長的光有著不同的吸收和反射特性。高光譜成像技術(shù)就是用一個能同時捕捉數(shù)百個連續(xù)、狹窄光譜波段的“超級相機”為每個像素點記錄下一條完整的光譜曲線。這個項目要做的“高光譜分類”核心任務(wù)就是基于這些海量的光譜數(shù)據(jù)自動、準(zhǔn)確地將圖像中的每個像素點劃分到預(yù)設(shè)的類別中去比如區(qū)分農(nóng)田里的玉米、小麥和水稻識別城市地物中的建筑、道路和植被或者在遙感監(jiān)測中圈定污染區(qū)域、評估作物健康狀況。它解決的痛點非常明確傳統(tǒng)RGB圖像或?qū)挷ǘ芜b感信息量有限在“同物異譜”同一種東西在不同條件下光譜不同和“異物同譜”不同東西光譜看起來相似面前常常力不從心。而高光譜數(shù)據(jù)憑借其豐富的光譜維度為精準(zhǔn)識別提供了可能。無論你是遙感、農(nóng)業(yè)、環(huán)境監(jiān)測領(lǐng)域的研究者還是對計算機視覺和機器學(xué)習(xí)感興趣的開發(fā)者這個項目都是一個絕佳的切入點。它融合了數(shù)據(jù)處理、特征工程和模型算法能讓你深刻理解如何從高維數(shù)據(jù)中挖掘價值。接下來我會結(jié)合我處理過的多個實際數(shù)據(jù)集的經(jīng)驗拆解從數(shù)據(jù)準(zhǔn)備到模型上線的完整流程分享那些在論文和教科書里不會寫的實操細節(jié)和避坑指南。2. 核心思路與方案選型為什么是“光譜特征深度學(xué)習(xí)”面對高光譜數(shù)據(jù)最直接的挑戰(zhàn)就是其“維度災(zāi)難”。一個典型的高光譜影像可能包含200-300個波段但空間分辨率下的像素數(shù)量動輒數(shù)十萬。這意味著數(shù)據(jù)維度極高但標(biāo)記樣本已知道類別的像素往往非常稀缺且獲取成本高。因此整個項目的設(shè)計思路必須圍繞“如何從有限樣本中高效提取最具判別性的特征”展開。早期的傳統(tǒng)方法如支持向量機SVM和隨機森林RF直接對光譜向量進行分類雖然簡單但難以捕捉光譜間的復(fù)雜非線性關(guān)系及空間上下文信息。而深度學(xué)習(xí)尤其是卷積神經(jīng)網(wǎng)絡(luò)CNN因其強大的特征學(xué)習(xí)能力已成為當(dāng)前的主流方案。但具體用哪種網(wǎng)絡(luò)結(jié)構(gòu)需要權(quán)衡。方案一基于純光譜的1D-CNN。這是最直接的思路把每個像素的光譜曲線視為一個一維信號用一維卷積核在光譜維度上進行特征提取。它的優(yōu)點是模型相對輕量訓(xùn)練快特別適合樣本量極少的情況。但缺點是完全忽略了像素之間的空間關(guān)系分類結(jié)果圖可能會顯得“椒鹽噪聲”很重即同一個均質(zhì)區(qū)域內(nèi)分類結(jié)果跳變嚴重。方案二基于空間-光譜聯(lián)合的2D/3D-CNN。這是目前效果最好的主流方案。2D-CNN如簡單的VGG或ResNet變體將高光譜數(shù)據(jù)的每個波段當(dāng)作一個通道輸入一個空間塊例如以目標(biāo)像素為中心的7x7或9x9窗口同時學(xué)習(xí)空間紋理和光譜特征。3D-CNN則更進一步使用三維卷積核同時在空間和光譜維度上滑動能更自然地耦合空譜信息但計算量和參數(shù)量會急劇增加。方案三混合模型與注意力機制。這是前沿探索方向。例如用2D-CNN提取空間特征同時用一個并行分支如1D-CNN或Transformer提取光譜特征最后融合。或者引入注意力機制讓模型自動關(guān)注那些判別性強的波段和空間區(qū)域。這類模型性能潛力大但對數(shù)據(jù)量、調(diào)參技巧和計算資源要求更高。我的選型心得對于大多數(shù)入門和實際應(yīng)用項目我強烈建議從“基于空間塊的2D-CNN”開始。它是一個絕佳的平衡點效果顯著優(yōu)于純光譜方法實現(xiàn)和理解難度低于3D-CNN且有很多成熟的代碼框架可以借鑒。在數(shù)據(jù)預(yù)處理階段通過提取空間塊我們實際上已經(jīng)將空間信息引入了模型。后續(xù)的優(yōu)化可以在此基礎(chǔ)上進行比如引入殘差連接、注意力模塊等。確定了主干網(wǎng)絡(luò)還需要配套一系列關(guān)鍵設(shè)計如何劃分訓(xùn)練集、驗證集和測試集如何解決樣本不平衡問題如何設(shè)計數(shù)據(jù)增強策略來彌補樣本不足這些決策共同構(gòu)成了項目的技術(shù)骨架。3. 數(shù)據(jù)預(yù)處理比模型本身更重要的基石拿到高光譜數(shù)據(jù)通常是.mat或.tif格式直接丟給模型訓(xùn)練十有八九會失敗。預(yù)處理環(huán)節(jié)決定了數(shù)據(jù)質(zhì)量的上限也直接影響模型的性能下限。這個過程可以細化為幾個關(guān)鍵步驟。3.1 數(shù)據(jù)讀取與初步探查首先你需要了解你的數(shù)據(jù)。使用Python的scipy.io讀取.mat文件或用rasterio、GDAL讀取.tif文件。關(guān)鍵要弄清楚三個維度(height, width, bands)即圖像的高、寬和波段數(shù)。同時你需要對應(yīng)的標(biāo)簽圖(height, width)其中每個像素值代表類別索引如0代表背景1代表類別A等。import scipy.io as sio import numpy as np # 示例讀取Indian Pines數(shù)據(jù)集 data sio.loadmat(Indian_pines_corrected.mat) img data[indian_pines_corrected] # 形狀例如 (145, 145, 200) gt sio.loadmat(Indian_pines_gt.mat)[indian_pines_gt] # 形狀 (145, 145) print(f圖像形狀: {img.shape}, 標(biāo)簽形狀: {gt.shape}) print(f唯一標(biāo)簽值: {np.unique(gt)}) # 查看有哪些類別緊接著繪制幾個典型波段或假彩色合成圖例如用第30、20、10波段作為R、G、B直觀感受數(shù)據(jù)并隨機選取幾個像素點繪制其光譜曲線觀察不同類別的曲線差異。這一步能幫你建立對數(shù)據(jù)的直覺。3.2 光譜預(yù)處理與降維高光譜數(shù)據(jù)通常包含噪聲和冗余。相鄰波段間高度相關(guān)直接使用所有波段不僅計算負擔(dān)重還可能引入噪聲導(dǎo)致模型過擬合。壞波段剔除檢查并剔除信噪比極低或充滿噪聲的波段通常是邊緣波段。標(biāo)準(zhǔn)化/歸一化這是必須的一步。由于傳感器響應(yīng)和光照條件差異不同波段甚至不同像素的數(shù)值范圍可能差異巨大。我常用標(biāo)準(zhǔn)差標(biāo)準(zhǔn)化即對每個波段單獨處理減去均值除以標(biāo)準(zhǔn)差。這能加速模型收斂并提高性能。from sklearn.preprocessing import StandardScaler # 將3D數(shù)據(jù)重塑為2D (像素數(shù), 波段數(shù)) 以便處理 h, w, b img.shape img_2d img.reshape(-1, b) scaler StandardScaler() img_2d_scaled scaler.fit_transform(img_2d) img_scaled img_2d_scaled.reshape(h, w, b)降維可選但推薦對于波段數(shù)非常多100的數(shù)據(jù)可以考慮使用主成分分析PCA將數(shù)據(jù)降到較低維度如30-50個主成分。這能大幅減少計算量且前幾個主成分往往包含了絕大部分信息。但要注意降維可能會損失一些細微的光譜特征對于依賴特定窄波段診斷的應(yīng)用需謹慎。3.3 樣本提取與空間塊構(gòu)建這是2D-CNN輸入準(zhǔn)備的核心。我們不是處理單個像素而是以每個有標(biāo)簽的像素為中心裁剪出一個固定大小的空間鄰域塊如7x7, 9x9, 11x11。這個塊的大小是關(guān)鍵超參數(shù)太小則空間信息不足太大則可能包含過多異質(zhì)信息且增加計算量。def extract_patches(img, gt, patch_size9, ignore_label0): 從圖像和標(biāo)簽圖中提取以有標(biāo)簽像素為中心的空間塊。 img: 預(yù)處理后的高光譜圖像 (H, W, C) gt: 地面真值標(biāo)簽圖 (H, W) patch_size: 空間塊大小奇數(shù) ignore_label: 需要忽略的標(biāo)簽如背景 half_size patch_size // 2 # 填充圖像邊緣使得邊緣像素也能提取完整塊 img_padded np.pad(img, ((half_size, half_size), (half_size, half_size), (0, 0)), modereflect) patches, labels [], [] # 獲取所有非忽略標(biāo)簽的像素坐標(biāo) valid_positions np.argwhere(gt ! ignore_label) for (y, x) in valid_positions: # 在填充后的圖像中計算中心位置 center_y, center_x y half_size, x half_size # 提取塊 patch img_padded[center_y-half_size:center_yhalf_size1, center_x-half_size:center_xhalf_size1, :] patches.append(patch) labels.append(gt[y, x]) return np.array(patches), np.array(labels) - 1 # 標(biāo)簽通常從0開始編號關(guān)鍵注意事項填充方式我推薦使用reflect反射填充而非constant常數(shù)填充。反射填充能更好地保持圖像邊緣的連續(xù)性避免引入突兀的邊界值這對卷積操作更友好。提取出的patches形狀將是(N, patch_size, patch_size, bands)labels形狀是(N,)。這里的N是所有有效標(biāo)簽像素的數(shù)量。3.4 數(shù)據(jù)集劃分與樣本平衡這是最容易出問題的一步。絕對不要用sklearn的train_test_split對像素索引進行隨機劃分因為相鄰像素在空間上是高度相關(guān)的隨機劃分會導(dǎo)致訓(xùn)練集和測試集的數(shù)據(jù)分布高度相似即“數(shù)據(jù)泄露”你會得到一個虛高的、不可信的準(zhǔn)確率。正確的做法是按空間區(qū)域或按類別比例分層采樣。一種穩(wěn)健的方法是將整個圖像劃分為若干不重疊的大網(wǎng)格如4x4。隨機選擇其中幾個網(wǎng)格的像素作為測試集其余網(wǎng)格的像素作為訓(xùn)練集和驗證集。這樣可以確保測試集來自空間上獨立的區(qū)域評估結(jié)果更可靠。此外高光譜數(shù)據(jù)中各類別的像素數(shù)往往極不平衡。例如背景或某一主導(dǎo)地物可能占絕大多數(shù)。直接訓(xùn)練會導(dǎo)致模型偏向大類別。解決方法包括過采樣復(fù)制少數(shù)類別的樣本。欠采樣隨機丟棄多數(shù)類別的部分樣本。類別權(quán)重在損失函數(shù)中為少數(shù)類別賦予更高的權(quán)重。這是我最常用的方法在PyTorch或TensorFlow中很容易實現(xiàn)。4. 模型構(gòu)建與訓(xùn)練實戰(zhàn)以HybridSN網(wǎng)絡(luò)為例理論說再多不如一行代碼。這里我以一個效果和復(fù)雜度都比較均衡的混合光譜卷積網(wǎng)絡(luò)HybridSN的簡化版為例展示如何用PyTorch搭建和訓(xùn)練一個高光譜分類模型。這個網(wǎng)絡(luò)結(jié)合了3D卷積、2D卷積和全連接層能有效提取空譜特征。4.1 模型定義import torch import torch.nn as nn import torch.nn.functional as F class HybridSN(nn.Module): def __init__(self, num_bands, num_classes, patch_size9): super(HybridSN, self).__init__() # 第一層3D卷積在光譜維度上進行壓縮 self.conv3d_1 nn.Conv3d(1, 8, kernel_size(7, 3, 3), padding(0, 1, 1)) # 計算經(jīng)過第一層3D卷積后的光譜維度 self.out_bands_3d num_bands - 7 1 # kernel_size[0]7, padding[0]0, stride默認為1 self.conv3d_2 nn.Conv3d(8, 16, kernel_size(5, 3, 3), padding(0, 1, 1)) self.out_bands_3d self.out_bands_3d - 5 1 # 第二層3D卷積后將光譜維度壓扁轉(zhuǎn)為2D卷積 self.conv2d_1 nn.Conv2d(16 * self.out_bands_3d, 32, kernel_size3, padding1) # 計算經(jīng)過3D卷積后的空間尺寸 self.spatial_size_after_3d patch_size - 2*2 # 兩次3D卷積每次空間上padding1kernel3尺寸不變 self.conv2d_2 nn.Conv2d(32, 64, kernel_size3, padding1) self.conv2d_3 nn.Conv2d(64, 128, kernel_size3, padding1) # 全局平均池化層 self.gap nn.AdaptiveAvgPool2d(1) # 全連接層 self.fc1 nn.Linear(128, 256) self.dropout nn.Dropout(p0.4) # 加入Dropout防止過擬合 self.fc2 nn.Linear(256, num_classes) def forward(self, x): # 輸入x形狀: (batch_size, 1, bands, patch_size, patch_size) x F.relu(self.conv3d_1(x)) x F.relu(self.conv3d_2(x)) # 重塑: (batch, channels_3d * remaining_bands, height, width) batch, c, b, h, w x.shape x x.view(batch, c * b, h, w) x F.relu(self.conv2d_1(x)) x F.relu(self.conv2d_2(x)) x F.relu(self.conv2d_3(x)) x self.gap(x) # (batch, 128, 1, 1) x x.view(batch, -1) # (batch, 128) x F.relu(self.fc1(x)) x self.dropout(x) x self.fc2(x) return x4.2 訓(xùn)練流程與關(guān)鍵技巧定義了模型結(jié)構(gòu)接下來是訓(xùn)練循環(huán)。這里有幾個超參數(shù)和技巧需要特別注意。import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset from sklearn.model_selection import train_test_split # 假設(shè)我們已經(jīng)有了 patches 和 labels并已劃分為 X_train, X_val, y_train, y_val # 1. 轉(zhuǎn)換為Tensor并創(chuàng)建DataLoader train_dataset TensorDataset(torch.FloatTensor(X_train).unsqueeze(1), torch.LongTensor(y_train)) val_dataset TensorDataset(torch.FloatTensor(X_val).unsqueeze(1), torch.LongTensor(y_train)) # 注意輸入需要增加一個通道維因為Conv3d期望輸入為 (N, C, D, H, W)我們C1 train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) val_loader DataLoader(val_dataset, batch_size128, shuffleFalse) # 2. 初始化模型、損失函數(shù)、優(yōu)化器 device torch.device(cuda if torch.cuda.is_available() else cpu) model HybridSN(num_bandsX_train.shape[-1], num_classeslen(np.unique(y_train)), patch_size9).to(device) # 使用帶權(quán)重的交叉熵損失解決類別不平衡 class_counts np.bincount(y_train) class_weights 1. / class_counts class_weights torch.FloatTensor(class_weights).to(device) criterion nn.CrossEntropyLoss(weightclass_weights) # 優(yōu)化器選擇Adam并配合學(xué)習(xí)率衰減 optimizer optim.Adam(model.parameters(), lr0.001, weight_decay1e-4) scheduler optim.lr_scheduler.StepLR(optimizer, step_size20, gamma0.5) # 每20個epoch學(xué)習(xí)率減半 # 3. 訓(xùn)練循環(huán) num_epochs 100 best_val_acc 0.0 for epoch in range(num_epochs): model.train() running_loss 0.0 for inputs, targets in train_loader: inputs, targets inputs.to(device), targets.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, targets) loss.backward() optimizer.step() running_loss loss.item() * inputs.size(0) epoch_loss running_loss / len(train_loader.dataset) scheduler.step() # 驗證階段 model.eval() correct, total 0, 0 with torch.no_grad(): for inputs, targets in val_loader: inputs, targets inputs.to(device), targets.to(device) outputs model(inputs) _, predicted torch.max(outputs.data, 1) total targets.size(0) correct (predicted targets).sum().item() val_acc 100 * correct / total print(fEpoch [{epoch1}/{num_epochs}], Loss: {epoch_loss:.4f}, Val Acc: {val_acc:.2f}%) # 保存最佳模型 if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_hybridsn_model.pth)訓(xùn)練心得學(xué)習(xí)率與衰減高光譜數(shù)據(jù)訓(xùn)練容易過擬合。初始學(xué)習(xí)率不宜過大如0.001并配合StepLR或ReduceLROnPlateau當(dāng)驗證集指標(biāo)停滯時自動降低學(xué)習(xí)率使用這是穩(wěn)定訓(xùn)練、找到更好局部最優(yōu)解的關(guān)鍵。權(quán)重衰減Adam優(yōu)化器中的weight_decay參數(shù)即L2正則化非常重要能有效抑制模型復(fù)雜度防止過擬合。通常設(shè)置在1e-4到1e-5之間。早停上述代碼只保存了最佳模型在實際中最好加入“早停”機制。如果連續(xù)多個epoch驗證集準(zhǔn)確率不再提升則停止訓(xùn)練避免無效計算和過擬合。Batch Size在GPU內(nèi)存允許的情況下可以適當(dāng)增大batch_size如64128這有助于訓(xùn)練更穩(wěn)定。但對于小樣本數(shù)據(jù)集過大的batch size可能會降低模型泛化能力。5. 結(jié)果評估與可視化超越整體準(zhǔn)確率模型訓(xùn)練完成后在獨立的測試集上評估性能。千萬不要只用“整體準(zhǔn)確率”這一個指標(biāo)因為如果數(shù)據(jù)極度不平衡即使模型把所有樣本都預(yù)測為最大類也能獲得很高的整體準(zhǔn)確率但這毫無意義。必須使用更細致的評估指標(biāo)混淆矩陣直觀展示每個類別被分對和分錯的情況。每類準(zhǔn)確率每個類別自身的分類精度。平均準(zhǔn)確率所有類別準(zhǔn)確率的平均值更關(guān)注小類別。Kappa系數(shù)考慮了隨機分類的準(zhǔn)確率比單純準(zhǔn)確率更可靠。from sklearn.metrics import confusion_matrix, classification_report, cohen_kappa_score import seaborn as sns import matplotlib.pyplot as plt # 在測試集上預(yù)測 model.eval() all_preds, all_labels [], [] with torch.no_grad(): for inputs, targets in test_loader: # 需要事先創(chuàng)建test_loader inputs, targets inputs.to(device), targets.to(device) outputs model(inputs) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(targets.cpu().numpy()) all_preds np.array(all_preds) all_labels np.array(all_labels) # 1. 計算整體指標(biāo) overall_acc np.mean(all_preds all_labels) kappa cohen_kappa_score(all_labels, all_preds) print(f整體準(zhǔn)確率: {overall_acc:.4f}) print(fKappa系數(shù): {kappa:.4f}) # 2. 分類報告包含每類精度、召回率、F1-score print(classification_report(all_labels, all_preds, target_namesclass_names)) # 3. 繪制混淆矩陣 cm confusion_matrix(all_labels, all_preds) plt.figure(figsize(10,8)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclass_names, yticklabelsclass_names) plt.xlabel(預(yù)測標(biāo)簽) plt.ylabel(真實標(biāo)簽) plt.title(混淆矩陣) plt.tight_layout() plt.show()可視化分類圖是最終成果的直觀體現(xiàn)。你需要將整幅高光譜圖像的每個像素都用訓(xùn)練好的模型預(yù)測一遍生成一張彩色分類圖并與真實地面真值圖進行對比。def predict_entire_image(model, img_scaled, patch_size, device): 預(yù)測整幅圖像 half_size patch_size // 2 h, w, b img_scaled.shape img_padded np.pad(img_scaled, ((half_size, half_size), (half_size, half_size), (0, 0)), modereflect) # 為每個像素提取塊并預(yù)測效率較低可優(yōu)化為批量預(yù)測 output_map np.zeros((h, w), dtypenp.uint8) model.eval() with torch.no_grad(): # 更高效的方式將整個滑動窗口過程向量化或分塊批量預(yù)測這里為清晰起見用循環(huán) # 實際建議將圖像分割成網(wǎng)格對每個網(wǎng)格內(nèi)的所有像素塊一起預(yù)測 for i in range(h): for j in range(w): patch img_padded[i:ipatch_size, j:jpatch_size, :] patch_tensor torch.FloatTensor(patch).unsqueeze(0).unsqueeze(0).to(device) # (1,1,b,p,p) output model(patch_tensor) pred torch.argmax(output, dim1).item() output_map[i, j] pred return output_map # 生成并顯示分類圖 prediction_map predict_entire_image(model, img_scaled, patch_size9, devicedevice) plt.figure(figsize(15,5)) plt.subplot(1,3,1) plt.imshow(img_scaled[:,:,[30,20,10]]) # 假彩色顯示 plt.title(原始圖像假彩色) plt.subplot(1,3,2) plt.imshow(gt, cmapjet) plt.title(真實地物標(biāo)簽) plt.subplot(1,3,3) plt.imshow(prediction_map, cmapjet) plt.title(模型預(yù)測結(jié)果) plt.show()6. 常見問題排查與性能調(diào)優(yōu)實錄在實際操作中你幾乎一定會遇到下面這些問題。我把我的排查經(jīng)驗和解決方案記錄下來希望能幫你節(jié)省大量時間。6.1 模型過擬合嚴重訓(xùn)練集準(zhǔn)確率高驗證集上不去這是高光譜小樣本分類中最常見的問題。現(xiàn)象訓(xùn)練loss持續(xù)下降訓(xùn)練acc很快接近100%但驗證集loss在下降后很快開始上升驗證acc停滯不前。原因模型復(fù)雜度過高記住了訓(xùn)練樣本的噪聲而非一般規(guī)律。解決方案增強正則化增大weight_decay在卷積層后增加Dropout層如nn.Dropout2d使用更激進的Dropout率0.5甚至更高。數(shù)據(jù)增強這是對付過擬合的利器。對空間塊進行隨機水平/垂直翻轉(zhuǎn)、旋轉(zhuǎn)、添加微小的高斯噪聲等。注意光譜維度一般不做增強以免破壞其物理意義。簡化模型減少網(wǎng)絡(luò)層數(shù)或卷積核數(shù)量。對于很小的數(shù)據(jù)集如Indian Pines一個簡單的3層2D-CNN可能比復(fù)雜的HybridSN更有效。早停嚴格監(jiān)控驗證集損失一旦連續(xù)5-10個epoch不降反升立即停止訓(xùn)練。獲取更多數(shù)據(jù)如果可能這是根本解決方法。或者利用半監(jiān)督、自監(jiān)督學(xué)習(xí)利用無標(biāo)簽數(shù)據(jù)。6.2 某些類別識別精度始終極低現(xiàn)象從混淆矩陣看某幾個類別的生產(chǎn)者精度漏分誤差高或用戶精度錯分誤差高非常低。原因樣本量太少或該類光譜特征與其他類太相似。解決方案檢查樣本數(shù)量如果該類別訓(xùn)練樣本少于100個考慮過采樣或使用更強大的數(shù)據(jù)增強專門針對該類。分析光譜曲線繪制該類與易混淆類別的平均光譜曲線看是否在關(guān)鍵波段有重疊。如果光譜本身難以區(qū)分可能需要引入空間上下文特征增大patch_size或外部知識。調(diào)整損失函數(shù)權(quán)重進一步增大該類在CrossEntropyLoss中的權(quán)重。集成學(xué)習(xí)訓(xùn)練多個模型如不同初始化、不同patch_size對難以區(qū)分的類別進行投票可能提升魯棒性。6.3 訓(xùn)練過程不穩(wěn)定Loss出現(xiàn)NaN或劇烈震蕩現(xiàn)象訓(xùn)練幾個batch后loss變成NaN或者loss值上下跳動很大。原因?qū)W習(xí)率過高數(shù)據(jù)未標(biāo)準(zhǔn)化梯度爆炸。解決方案檢查數(shù)據(jù)確認數(shù)據(jù)標(biāo)準(zhǔn)化是否已做。輸入數(shù)據(jù)中是否存在異常值NaN或inf。降低學(xué)習(xí)率將初始學(xué)習(xí)率降低一個數(shù)量級如從0.001降到0.0001。梯度裁剪在optimizer.step()之前添加torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)防止梯度爆炸。使用更穩(wěn)定的優(yōu)化器可以嘗試從Adam換為SGD with momentum雖然收斂可能慢但有時更穩(wěn)定。6.4 預(yù)測整圖速度太慢現(xiàn)象使用predict_entire_image中的雙重循環(huán)預(yù)測一張圖需要數(shù)小時。原因每個像素單獨提取塊、單獨預(yù)測效率極低。優(yōu)化方案批量預(yù)測將整張圖劃分成若干個網(wǎng)格每個網(wǎng)格內(nèi)所有像素的塊組成一個大batch一次性送入模型預(yù)測。這需要仔細處理邊緣填充和索引映射。使用滑動窗口的卷積實現(xiàn)這需要將模型改造為全卷積網(wǎng)絡(luò)FCN一次前向傳播即可得到整張圖的分類結(jié)果。這是最高效的方式但需要對網(wǎng)絡(luò)結(jié)構(gòu)進行修改將最后的全連接層替換為1x1卷積。工程優(yōu)化使用torch.no_grad()和model.eval()確保數(shù)據(jù)在GPU上使用更大的batch_size進行預(yù)測。6.5 不同數(shù)據(jù)集上泛化能力差現(xiàn)象在一個數(shù)據(jù)集上訓(xùn)練好的模型直接用到另一個數(shù)據(jù)集上效果暴跌。原因不同傳感器、不同時間、不同地點獲取的高光譜數(shù)據(jù)存在分布差異。解決方案域適應(yīng)這是前沿研究方向使用特定算法減小源域訓(xùn)練數(shù)據(jù)和目標(biāo)域新數(shù)據(jù)之間的分布差異。標(biāo)準(zhǔn)化確保對新數(shù)據(jù)采用與訓(xùn)練數(shù)據(jù)完全相同的預(yù)處理流程使用訓(xùn)練數(shù)據(jù)計算得到的均值和標(biāo)準(zhǔn)差進行標(biāo)準(zhǔn)化。微調(diào)如果新數(shù)據(jù)有少量標(biāo)注可以在預(yù)訓(xùn)練模型的基礎(chǔ)上進行微調(diào)。使用更具泛化性的特征在設(shè)計模型時考慮使用光譜導(dǎo)數(shù)、光譜指數(shù)等物理意義更明確的特征而非完全依賴數(shù)據(jù)驅(qū)動的深度特征。高光譜分類是一個從數(shù)據(jù)理解、預(yù)處理、模型設(shè)計到調(diào)優(yōu)的完整閉環(huán)。每個環(huán)節(jié)都需要耐心和細致的調(diào)試。我個人的體會是成功的關(guān)鍵往往不在于使用最炫酷的模型而在于對數(shù)據(jù)的深刻理解和扎實的工程實現(xiàn)。先從經(jīng)典的2D-CNN加精心預(yù)處理的基礎(chǔ)流程跑通確保評估方式正確再逐步嘗試更復(fù)雜的模型和技巧這樣建立的認知才是最牢固的。最后別忘了保存好你的預(yù)處理參數(shù)、模型配置和訓(xùn)練日志這些都是寶貴的實驗資產(chǎn)。