督學習實戰(zhàn):從標注匱乏到準確率89%的完整方案)
做圖像分類項目時最扎心的不是模型選得不夠花而是標注數據根本不夠用。我去年接手一個真實業(yè)務場景總共只有不到一千張人工標注圖手里卻壓著幾十萬張完全沒標注的原始圖片。外包標一輪問了下報價直接放棄了。后來靠半監(jiān)督學習硬生生把分類準確率從78%拉到89%整個過程沒額外標過一張圖。今天就把這套圖像分類半監(jiān)督學習的完整思路和實操經驗整理出來核心方法怎么拆解、損失函數怎么設、訓練時踩過哪些坑、什么情況下別硬上一條條說清楚你們可以直接拿著調。1. 半監(jiān)督學習的核心思路與方案選型1.1 為什么要用半監(jiān)督學習標注瓶頸才是真問題很多人一提到圖像分類第一反應就是堆數據、上大模型。但真實項目里干凈、準確、覆蓋所有類別分布的標注數據永遠是稀缺資源。標注成本高不只是錢的問題還有時間成本醫(yī)學影像需要專業(yè)醫(yī)生逐張看工業(yè)質檢缺陷圖需要老師傅確認電商圖片需要熟悉商品體系的人打標。這個過程又慢又貴而且標注質量不穩(wěn)定返工率高。反過來看無標注數據幾乎遍地都是。監(jiān)控視頻抽幀、服務器上積壓的歷史圖片、爬蟲采集的公開圖像、生產線自動拍攝的樣張這些數據沒有標簽但數量可能是標注數據的幾百倍。半監(jiān)督學習解決的就是這個問題利用大量無標注數據來提升模型的泛化能力讓模型在沒有額外標注的情況下把分類邊界學得更準。它的適用人群也很明確手里有少量干凈標注數據、同時有大量無標注數據、但預算不足以繼續(xù)擴標注集的工程師和研究者。如果你已經有十萬張完美標注的數據那直接走監(jiān)督學習就行半監(jiān)督的收益邊際不大。反過來如果標注只有幾百張、每類幾十張半監(jiān)督幾乎是你成本最低的提點手段。1.2 半監(jiān)督到底在學什么三個基礎假設要理解半監(jiān)督學習必須弄明白它依賴的三個前提假設。這些假設既是方法有效性的來源也是翻車的原因。第一個是聚類假設。同類樣本在特征空間中應當彼此靠近。如果兩個樣本的特征表示很接近那它們的類別大概率相同。模型可以利用無標注數據來感知這種聚集結構讓決策邊界更貼合數據分布。第二個是平滑假設。在數據分布的高密度區(qū)域決策邊界應當是平滑的。也就是說對特征空間中的某個樣本做小擾動其預測結果不應發(fā)生劇烈變化。這給了半監(jiān)督方法一個操作抓手讓模型對“變形后的同一張圖”也保持穩(wěn)定輸出。第三個是低密度分離假設。不同類別之間的決策邊界應該穿過數據密度較低的區(qū)域。如果模型能把大量無標注樣本分到明確的類別簇中而類別之間的空白地帶沒有樣本那說明分類邊界大概率是對的。這三個假設用大白話講就是長得像的應該歸一類、換了個說法意思不變、類別中間最好留出空地。所有半監(jiān)督方法本質上都是在用不同方式讓模型滿足這些假設。1.3 三條技術路線和我的選型建議目前圖像分類半監(jiān)督主流有三條技術路線。第一條是一致性正則代表思路有 Π-Model、Temporal Ensembling、Mean Teacher。核心做法是對同一個輸入做兩次不同的隨機增強要求模型輸出一致。這種方式不依賴偽標簽但收斂慢對增強策略敏感。第二條是偽標簽法也就是自訓練。先用標注數據訓練一個模型對無標注數據預測出偽標簽挑高置信度的樣本加入訓練集迭代循環(huán)。實現簡單但存在確認偏差問題模型一旦預測錯了錯誤會反復自我強化。第三條是混合方案把兩者結合起來比如 FixMatch 這類思路。弱增強生成偽標簽強增強輸入模型訓練兩者用同一個模型?,F在學術界和工業(yè)界的基線基本都錨定這一方向。我的實際選型建議是如果無標注數據量很大、但標注數據每類只有幾十張優(yōu)先嘗試混合方案性價比最高。如果標注數據稍微充足一些但計算資源有限一致性正則也夠用。偽標簽法最容易實現但必須要配高置信度閾值和較強的數據增強否則很容易訓練崩。2. 關鍵方法拆解一致性正則與偽標簽是怎么干活的2.1 一致性正則的直觀理解與損失設計一致性正則的思想非常樸素。你給模型看一張貓的照片模型說這是貓你把照片水平翻轉、輕微裁剪、調一下亮度再讓模型看模型還是應該說這是貓。對無標注數據來說我們不知道它到底是什么類別但我們知道同一張圖的不同增強版本類別一定相同。這個約束落到損失函數上就是讓模型對兩個增強版本的預測分布盡量一致。常見的做法是算兩個預測分布之間的 KL 散度或交叉熵。以 Mean Teacher 思路為例一個學生模型做梯度更新一個教師模型用學生權重的指數移動平均來維護訓練時讓學生模型的預測去逼近教師模型的預測同時學生模型也接受有標注數據的監(jiān)督損失。教師模型的輸出比單次預測更穩(wěn)定相當于給學生提供了更可靠的學習目標。實操中有一個需要注意的點一致性正則的損失權重不能從一開始就拉滿否則模型還沒學好特征就先被“一致性”給僵住了。我常用的做法是前若干個 epoch 讓監(jiān)督損失主導無監(jiān)督損失權重從一個很小的值逐漸上升到目標值類似一個升溫過程。2.2 偽標簽的流程、閾值與確認偏差偽標簽法就更好理解了。模型先在標注數據上學出初始能力然后對無標注數據做預測把置信度高的預測結果當作“偽標簽”加入訓練。聽上去很簡單但這里有個致命的坑確認偏差。模型對某個樣本如果第一次就預測錯了而且置信度還很高那么把它當偽標簽訓練等于把錯誤再教一遍。等到下一輪迭代模型對這類樣本的錯誤傾向會更強。幾輪下來模型就徹底被自己帶偏了。應對確認偏差的手段主要是兩個方向。一是設置高置信度閾值比如只有 max 概率超過 0.95 的偽標簽才參與訓練。閾值高雖然會丟掉很多候選樣本但留下的偽標簽整體可靠性更高。二是增加偽標簽的噪聲容忍度比如把硬標簽改成軟標簽用模型預測的概率分布直接做監(jiān)督信號這樣即使預測不完全正確梯度也不至于太“狠”。我在實際操作中通常先用高閾值跑通一個版本再考慮軟標簽變體。2.3 為什么最終選 FixMatch 思路而不是 Mean Teacher 或 MixMatch我在項目里最終采用的是 FixMatch 這一類混合思路。它的關鍵設計是對無標注圖片打兩次增強一個弱增強、一個強增強。弱增強版本送入模型得到預測分布取 argmax 和對應的置信度如果置信度超過閾值就把這個 argmax 當作偽標簽然后用強增強版本作為輸入讓模型預測去擬合這個偽標簽。整個過程只有一個模型、一份無監(jiān)督損失實現起來非常干凈。這里弱增強和強增強的分工特別關鍵。弱增強的目的是生成相對可靠的偽標簽所以擾動幅度要小不能讓模型“看花了眼”。強增強的目的是讓模型在更大的數據擾動范圍內保持穩(wěn)定所以要足夠“狠”逼模型去學習真正的語義特征而不是淺層的像素模式。我測試下來弱增強通常用隨機水平翻轉和裁剪強增強用 RandAugment 或 AutoAugment 加 CutOut效果比較穩(wěn)。為什么不選 Mean Teacher因為雙模型結構訓練成本高且 EMA 更新需要額外調參收斂速度慢。為什么不選 MixMatch它用了多個增強分支和標簽銳化性能不錯但復雜度高工程上維護成本偏高。FixMatch 思路在效果和實現復雜度之間最平衡特別適合這類“標注少、無標注多”的真實場景。方法思路核心機制優(yōu)點代價一致性正則Mean Teacher學生模型逼近 EMA 教師輸出目標穩(wěn)定無需偽標簽訓練慢雙模型成本高偽標簽Self-training高置信度偽標簽自舉實現簡單直接確認偏差風險高混合方案FixMatch弱增強生成標簽強增強訓練效果好實現干凈對增強策略和閾值敏感3. 實操過程從數據準備到訓練收斂3.1 數據劃分與增強策略我拿一個公開的十類圖像數據集舉例類別數不多比較典型。假設每類只有 40 張標注圖總共 400 張標注數據另有 4 萬張無標注數據。先把標注集按 8:2 切出訓練子集和驗證子集無標注數據不參與驗證和測試集劃分因為它的分布和標注集可能有偏移拿它做評估會虛高。數據加載時我把一個 batch 設計成兩部分一部分是標注樣本另一部分是無標注樣本。比如每個 batch 包含 64 張標注圖和 64 張無標注圖無標注圖又同時生成兩個版本弱增強版本和強增強版本。這樣一次前向計算就能同時拿到監(jiān)督損失和無監(jiān)督損失所需的全部輸入。弱增強我用隨機水平翻轉加隨機裁剪后縮放回原尺寸這是圖像分類里最常見的組合。強增強我用 RandAugment配合 CutOut。RandAugment 的幅度和操作數可以調整我常用幅度 10、操作數 2 起步如果發(fā)現無監(jiān)督損失下降困難再把幅度調小。CutOut 遮蔽的像素塊大小在 16 到 32 之間具體看圖像分辨率。3.2 損失函數、采樣配置與訓練超參整體損失函數就是三部分相加監(jiān)督交叉熵損失、無監(jiān)督偽標簽交叉熵損失和一個權重系數 λ。無監(jiān)督損失只在高置信度 mask 為真的樣本上計算這樣做的本質是對偽標簽做了一次篩選。參數設置方面我給出一套經過多次實驗驗證的初始配置優(yōu)化器SGDmomentum 0.9權重衰減 5e-4學習率0.03帶 5 個 epoch 的線性 warmup之后 cosine 衰減總訓練輪數200 epoch無監(jiān)督損失權重 λ前 20 個 epoch 從 0 線性升到 1之后保持 1高置信度閾值0.95batch 構成64 張標注圖 64 張無標注圖含強弱兩個版本EMA對模型參數做指數移動平均decay 0.999用于最終評估這里有個容易忽略的點無標注樣本的批次大小不要太少。半監(jiān)督的效果依賴無標注數據的多樣性batch 太小會導致偽標簽分布不穩(wěn)定訓練震蕩。如果有顯存余量我建議無標注部分至少和標注部分一樣大。3.3 核心訓練偽代碼我按 PyTorch 風格整理一份核心訓練邏輯把關鍵部分做了精簡方便理解整體流程for step, batch in enumerate(loader): # batch 包含標注圖像、標注標簽、無標注弱增強、無標注強增強 x_l, y_l, x_u_w, x_u_s batch # 1. 監(jiān)督損失 logits_l model(x_l) loss_s ce_loss(logits_l, y_l) # 2. 無監(jiān)督損失先獲取弱增強的預測結果 logits_u_w model(x_u_w) probs_u_w softmax(logits_u_w, dim1) conf, pseudo_label torch.max(probs_u_w, dim1) # 3. 高置信度樣本的 mask mask (conf threshold).float() # 4. 強增強版本的預測與偽標簽做交叉熵 logits_u_s model(x_u_s) loss_u ce_loss(logits_u_s, pseudo_label, reductionnone) loss_u (loss_u * mask).sum() / mask.sum().clamp(min1.0) # 5. 總損失 loss loss_s lambda_u * loss_u optimizer.zero_grad() loss.backward() optimizer.step()這條邏輯里最關鍵的就是 mask 的操作它直接決定了無監(jiān)督哪些樣本的反向傳播是激活的。我見過有人直接把所有樣本的無監(jiān)督損失都算進去結果低置信度樣本的噪聲把模型帶崩了。mask 的作用等價于只讓模型在“自己比較有把握”的樣本上吸收信息其余樣本干脆忽略。3.4 評估、模型保存與監(jiān)控指標訓練過程中我保留一份單獨的標注驗證集每個 epoch 結束后用 EMA 模型跑一次驗證集準確率。EMA 模型的作用是平滑震蕩它的準確率通常比原始模型穩(wěn)定不少也更適合作為最終的預測版本。監(jiān)控指標我會同時記錄四類信息監(jiān)督損失值、無監(jiān)督損失值、偽標簽啟用比例即 mask 的平均值、驗證集準確率。偽標簽啟用比例是一個特別有用的早期預警信號。如果閾值設得太高可能大部分無標注樣本都被過濾掉了導致無監(jiān)督損失幾乎不更新整個半監(jiān)督分支形同虛設。如果這個比例長期在 90% 以上說明閾值設得太低偽標簽噪聲已經被大量注入。模型保存方面我一般不只存最后一輪權重而是存驗證集準確率最高的 EMA 權重快照。這比“跑到最后一個 epoch 就收工”要穩(wěn)得多因為訓練后期往往有些震蕩峰值往往在最后幾輪之前。4. 常見問題與排查技巧實錄4.1 無監(jiān)督損失不下降模型根本不收斂這個現象通常有兩種表現。一種是無監(jiān)督損失持續(xù)停在某個固定值附近波動另一種是驗證集準確率反而比純監(jiān)督基線還低。我排查的順序如下。先看偽標簽啟用比例。如果低于 10%說明高置信度閾值擋住了絕大多數樣本。原因可能是模型本身還沒學到足夠強的特征或者強增強太猛導致預測分布普遍低置信度。解決方法是把閾值從 0.95 降到 0.9 甚至 0.8 試一下同時把 RandAugment 的幅度調低。再看學習率。半監(jiān)督訓練對學習率比純監(jiān)督更敏感學習率過大時模型很快震蕩過小時無監(jiān)督信息根本推不動模型。我在實際項目中碰到的多數情況是學習率偏大SGD 配 0.03 只是一個起點如果你用的是 Adam建議從 1e-3 往下調。最后確認無監(jiān)督數據分布是否和標注數據差距太大。如果無標注數據里混入了大量不屬于任何目標類別的圖片偽標簽無論怎么篩選都學不到有效信息。這時候需要先對無標注數據做一輪清洗或聚類把離群部分剔除。4.2 偽標簽噪聲太大模型被自己帶偏這里說的噪聲大不是指置信度閾值低而是即使置信度很高預測結果依然是錯的。高頻類別尤其容易出現這種現象模型對“狗”這類樣本見得多了把一些背景相似但不屬于狗的圖片也高置信度地標成狗。偽標簽和真實標簽不一致又互相強化就是典型的確認偏差。我的處理策略有三層。第一層是提升數據增強強度讓強增強真正制造出足夠的擾動逼模型必須依賴語義特征而非紋理背景。第二層是臨時凍結監(jiān)督分支先用純監(jiān)督損失訓練 10 到 20 個 epoch讓模型初步收斂再打開無監(jiān)督分支。不要一開始就讓兩個分支同時優(yōu)化否則噪聲會污染早期特征學習。第三層是把硬偽標簽換成軟標簽即用模型的預測概率分布作為監(jiān)督目標而不是只取 argmax這樣可以在一定程度上緩解錯誤標簽的“過度自信”。如果上述手段都不理想還可以試試改變偽標簽的產生方式用一個 EMA 模型的輸出來生成偽標簽而不是用當前快速更新的模型。EMA 模型變化慢輸出目標更穩(wěn)定能明顯降低噪聲波動。4.3 顯存翻倍與訓練時間變長的問題半監(jiān)督訓練相比純監(jiān)督訓練最直接的工程代價就是多了一路強增強輸入。每個 batch 里無標注數據需要同時保存弱增強和強增強的中間特征顯存占用幾乎翻倍。訓練時間也會因此變長。我的處理經驗是分三步優(yōu)化。第一步是混精度訓練模型前向和反向用 FP16 計算損失計算保留 FP32顯存和速度都能明顯改善。第二步是調整 batch 構成不一定非得標注和無標注各 64可以把標注壓到 32、無標注保持 64這樣顯存壓力小一些整體效果損失不大。第三步是如果實在顯存不夠就把無監(jiān)督損失改成每隔一個 step 計算一次相當于把無監(jiān)督更新的頻率減半雖然訓練速度會變慢但至少能跑起來。這里還要提醒一句不要把無標注數據全部一股腦塞進內存再預處理。最好用在線增強讓 GPU 邊訓練邊處理否則數據 IO 會成為新的瓶頸。我頭幾次實驗時直接在磁盤上預生成全部強增強版本結果磁盤讀寫把訓練速度拖垮了一半。4.4 類別不平衡時半監(jiān)督反而拖后腿如果你標注數據里的類別分布不是均勻的比如 A 類有 200 張、B 類只有 20 張半監(jiān)督的收益會明顯下降甚至比純監(jiān)督還差。原因是模型會在高頻類別上先形成高置信度的偽標簽這些偽標簽數量多在無監(jiān)督損失里占比大導致模型向高頻類偏移低頻類幾乎學不到。針對這種情況我采取兩個辦法。一是對每個類別單獨計算置信度閾值而不是用全局同一個閾值。高頻類可以設 0.95低頻類降到 0.8讓低頻類也有機會產生偽標簽參與訓練。二是無監(jiān)督損失按類別做重加權計算每一類的樣本數用類頻率的倒數對損失加權避免高頻類主導梯度更新。還有一個比較取巧的做法先統計無標注數據在所有類別上的預測分布如果發(fā)現某些類幾乎沒有無標注樣本被選中直接手動對那些類別的偽標簽閾值做退火從 0.95 階梯降到 0.7。這樣雖然會引入一部分噪聲但至少讓低頻類別在訓練中“有存在感”。我試過之后低頻類的單類準確率提升明顯整體平均準確率也漲了。5. 幾個工程上的心得判斷5.1 半監(jiān)督不是萬能藥什么時候用它收益最大經過這個項目我對半監(jiān)督學習的適用邊界有了更清晰的認識。它最適用的場景是標注數據極度稀缺每類幾十張甚至更少同時無標注數據量大且分布與標注集基本一致。在這種情況下半監(jiān)督能帶來從 70% 到 85% 量級的提升幾乎等于免費標注。但如果標注數據已經比較充足比如每類超過幾百張半監(jiān)督的提升就明顯減弱。這時候與其花時間調半監(jiān)督參數不如把算力留給數據清洗或更強的網絡結構。另外如果無標注數據本身噪聲極大包含大量無關類別或重復圖片收益也會縮水需要先做一輪去重和過濾。從資源分配角度看我現在的習慣是先跑一個純監(jiān)督基線如果準確率已經超過 90%半監(jiān)督基本不用碰。如果準確率只有 75% 以下半監(jiān)督就值得全力投入。在標注和無標注的比例上我一般要求無標注量至少達到標注量的 50 倍以上不然收益不明顯。5.2 如果還想再進一步值得嘗試的方向這套流程穩(wěn)定跑通之后有幾條擴展路徑可以明顯再拔高效果。一條是把半監(jiān)督和預訓練特征結合。現在很多圖像模型都用大規(guī)模無監(jiān)督預訓練初始化把初始化權重換成預訓練模型再疊加偽標簽訓練收斂速度更快最終準確率也更高。我測試過用預訓練初始化的半監(jiān)督訓練相比隨機初始化最終準確率能再漲兩到三個點。另一條是引入更強的數據增強策略。RandAugment 的幅度調參范圍很寬如果進一步搜索幅度和操作數的組合或者在強增強里加入更激進的裁剪策略無監(jiān)督損失能提供更強的正則化信號。還有一條是和主動學習結合。先用半監(jiān)督模型對無標注數據打分挑出置信度最低、模型最“不確定”的樣本去做人工標注再把新標注的樣本加入標注集繼續(xù)訓練。這樣標注人力極少但標注的目標樣本價值非常高形成了一個小成本高收益的閉環(huán)。結尾我個人在實際操作中的體會是半監(jiān)督不應該被當成一個花哨的學術技巧而是當作一種免費的“數據擴充”手段。先用小規(guī)模數據快速跑通純監(jiān)督基線確認模型架構和增強策略沒有大問題再啟動半監(jiān)督分支按照三件事來檢查監(jiān)督損失是否正常下降、無監(jiān)督損失是否有效更新、偽標簽啟用比例是否維持在一個健康的區(qū)間。最后再分享一個小技巧訓練過程中每 100 步打印一次無標注樣本的置信度均值也就是所有無標注樣本 max 概率的平均值。它比 loss 值更能反映訓練動態(tài)。如果這個均值一直低于 0.7說明增強太強或閾值太高模型根本沒有從無標注數據里學到任何東西趁早調整別等訓完一輪再回頭看。