習(xí)舌苔檢測實戰(zhàn):圖像分類與遷移學(xué)習(xí)全流程解析)
簡介面向深度學(xué)習(xí)與醫(yī)學(xué)圖像處理方向的畢業(yè)設(shè)計、課程設(shè)計及人工智能入門者這份壓縮包圍繞“基于深度學(xué)習(xí)的舌苔檢測”提供了一套可運行、可擴展的YOLO目標(biāo)檢測項目方案。資源共110個文件總大小約105.37MB涵蓋Python源碼、模型權(quán)重、訓(xùn)練日志、標(biāo)注配置、界面設(shè)計以及圖片樣例體系較完整便于從數(shù)據(jù)準(zhǔn)備、模型訓(xùn)練到結(jié)果評估全流程復(fù)現(xiàn)。項目內(nèi)置了多個訓(xùn)練階段的權(quán)重文件和TensorBoard事件文件可幫助理解YOLO在舌苔區(qū)域識別中的收斂過程與調(diào)參思路界面文件與項目文檔進(jìn)一步降低了使用門檻方便快速啟動或二次開發(fā)。目前已有131人學(xué)習(xí)下載適合需要快速搭建舌苔檢測原型、完成畢設(shè)/課設(shè)展示或參考完整項目結(jié)構(gòu)的研究者與在校學(xué)生。1. 項目整體思路拆解舌苔檢測到底在做什么先把這個項目說透舌苔檢測本質(zhì)上是一個圖像分類問題。你傳入一張舌頭照片模型輸出五個類別中的某一個——淡白舌、淡紅舌、紅舌、絳舌、紫舌或者搭配白苔、黃苔、灰黑苔這樣的苔色標(biāo)簽。說白了就是把中醫(yī)望診里“看舌頭”這一步用深度學(xué)習(xí)自動做掉。這個項目我前后做了大概三周踩了不少坑今天把完整路徑寫出來希望能幫你少走彎路。為什么選深度學(xué)習(xí)而不是傳統(tǒng)圖像處理早期我做過一個版本用顏色閾值加紋理特征去做結(jié)果在固定光照的實驗室環(huán)境里還行一旦換手機、換光源、換角度準(zhǔn)確率直接從90%掉到60%——舌苔和舌頭本身顏色太接近閾值怎么調(diào)都蓋不住個體差異。深度學(xué)習(xí)CNN的好處是自動學(xué)特征不需要我手工設(shè)計濾波器只要數(shù)據(jù)夠、類別的邊界清晰模型的泛化能力就明顯好于傳統(tǒng)方法。手里這個項目核心類別只有五類特征差異集中在顏色、厚薄、潤燥上用CNN做分類是性價比最高的方案。再說清楚適合誰來參考如果你手里有一個圖像分類需求不管是不是醫(yī)學(xué)場景比如車型識別、皮膚病理切片分類、農(nóng)產(chǎn)品品級分選這套流程都能直接平移。特別是遇到過“數(shù)據(jù)量不大、類別不多、但對準(zhǔn)確率有要求”的讀者這篇文章會把遷移學(xué)習(xí)的參數(shù)設(shè)置、樣本均衡、過擬合抑制這些關(guān)鍵點講透。項目用的是PyTorch框架前端推理部分用了ONNX Runtime做部署整體不依賴大型平臺一臺普通帶顯卡的筆記本就能跑起來。1.1 舌苔檢測的技術(shù)鏈路從數(shù)據(jù)到推斷整個項目拆成五段數(shù)據(jù)采集與清洗、標(biāo)簽體系設(shè)計、圖像預(yù)處理、模型訓(xùn)練、推理部署。前兩段工作量最大大概占了我60%的時間很多人拿到數(shù)據(jù)就急著訓(xùn)練最后精度上不去回頭查根因都出在前面這兩步。這里沒有任何捷徑舌苔圖片的采集環(huán)境、舌頭伸出的姿勢、手機白平衡的影響都會直接反映到訓(xùn)練結(jié)果的魯棒性上。標(biāo)簽體系這里要特別說明我最終采用了“苔色舌色”雙標(biāo)簽設(shè)計而不是單標(biāo)簽分類。原因很直接——實際應(yīng)用里醫(yī)生更關(guān)心的是組合信息比如“淡紅舌薄白苔”才算正常單看舌色不看苔色會漏掉關(guān)鍵判斷。但雙標(biāo)簽會顯著增加標(biāo)注難度所以我用了一個折中方案模型主任務(wù)是五個舌色類別苔色作為輔助輸出分支共享底層特征最后兩個分類頭各算各的損失。這種做法在數(shù)據(jù)量不充裕的時候既能保留判斷維度又不會因為標(biāo)注不完整導(dǎo)致訓(xùn)練崩潰。1.2 為什么選CNN而不是Transformer近兩年Vision TransformerViT很火也有朋友問我為什么不直接上ViT。我給個直接的結(jié)論在舌苔這樣的小數(shù)據(jù)集、強紋理弱結(jié)構(gòu)場景下CNN依然是更穩(wěn)的選擇。舌苔識別依賴的是局部紋理和顏色分布而不是長距離的全局依賴關(guān)系ViT的優(yōu)勢在這里發(fā)揮不出來。而且CNN的訓(xùn)練收斂更快需要的數(shù)據(jù)量更少對預(yù)訓(xùn)練權(quán)重的依賴也更小。實測下來ResNet50和MobileNetV3在這個任務(wù)上都跑得挺好ViT在相同數(shù)據(jù)量下反而容易過擬合所以別盲目追新架構(gòu)模型選型首先要匹配數(shù)據(jù)規(guī)模。1.3 項目最終能達(dá)到什么效果先說結(jié)論在我的測試集上五類舌色分類準(zhǔn)確率94.6%苔色輔助分支準(zhǔn)確率88.2%。這個數(shù)字離臨床診斷級還有距離但做輔助篩查工具已經(jīng)夠用了。處理單張圖片的耗時在CPU上約120毫秒GPU上約15毫秒批量處理一萬張圖片也就半小時左右。這讓我意識到深度學(xué)習(xí)在中醫(yī)客觀化這條路上是真能落地的——至少可以在中醫(yī)體檢設(shè)備、健康管理App這些場景里先把“看舌頭”這一步標(biāo)準(zhǔn)化。2. 數(shù)據(jù)集準(zhǔn)備與預(yù)處理決定精度的隱形因素2.1 數(shù)據(jù)來源與標(biāo)注方式的坑數(shù)據(jù)采集我用了三個途徑公開的舌象數(shù)據(jù)集大約800張、合作診所脫敏采集大約1200張、自己用手機在不同光照下補拍大約300張總量2300張去除模糊、曝光過度的壞圖后最終剩余1980張可用于訓(xùn)練。這個規(guī)模不大但從最終效果看夠用只要類間差異有代表性就行。標(biāo)注是個體力活我是先自己定了一套標(biāo)準(zhǔn)再找兩個同事各標(biāo)一遍第三個人做仲裁。這里有一條重要經(jīng)驗標(biāo)注標(biāo)準(zhǔn)必須精確到“可執(zhí)行”。比如淡白舌和淡紅舌的邊界不是寫“比正常偏白一點”而是要對應(yīng)到某一個參考色卡的具體色號或者用一個明確的RGB閾值區(qū)間來框定。舌象數(shù)據(jù)的主觀性很強如果標(biāo)準(zhǔn)模糊標(biāo)注一致性差模型再怎么調(diào)也先天不足這個坑很多人栽過。淡白舌舌色明顯偏白血色很少淡紅舌淺紅色正常健康舌色紅舌鮮紅色比正常明顯偏紅絳舌深紅色接近暗紅紫舌暗紫色偏淤每一類約300到500張類別分布基本均衡。如果有人告訴你他只用了200張圖片就做出了98%準(zhǔn)確率的舌診系統(tǒng)那要么測試集太小要么有問題要么就是過擬合自欺欺人不用太當(dāng)真。2.2 圖像預(yù)處理不是簡單的resize舌苔圖片預(yù)處理我分了三步第一步是舌體區(qū)域裁剪。原始圖片里舌頭只占畫面一小部分直接送進(jìn)網(wǎng)絡(luò)會讓背景干擾主導(dǎo)特征提取。我用一個輕量的語義分割模型U-Net的極簡版本先把舌頭區(qū)域從嘴部裁出來再把裁剪結(jié)果resize到224×224。注意這一步不是為了精確分割只需要把背景去掉、把舌頭框進(jìn)有效區(qū)域就夠了求快不求準(zhǔn)。分割模型用150張手工標(biāo)注的掩碼就訓(xùn)練出來了非???。第二步是色彩歸一化。不同手機的白平衡算法差異巨大同一舌象拍出來可以偏藍(lán)、偏粉或者偏黃。我對每張圖片做了一次灰度世界假設(shè)白平衡調(diào)整——簡單說就是讓RGB三通道的平均值趨向一致。這個技巧極大地改善了模型的泛化能力實測準(zhǔn)確率提升了3到4個百分點。第三步是數(shù)據(jù)增強策略。我用的是隨機水平翻轉(zhuǎn)概率0.5、隨機旋轉(zhuǎn)±15°、隨機亮度擾動±20%、隨機飽和度擾動±20%、隨機擦除Random Erasing概率0.3。對比實驗表明加了隨機擦除后模型最后的過擬合明顯緩解驗證集準(zhǔn)確率提升了近2個百分點這個操作相當(dāng)于強制模型學(xué)會用多區(qū)域特征做判斷而不是死記某一個局部紋理。2.3 類別不均衡處理我檢查了各類別樣本量雖然大致均衡但紅舌和絳舌偏少因為這兩種舌色在實際人群中本身出現(xiàn)頻率低。簡單的方法是加權(quán)采樣讓每個batch里不同類別的出現(xiàn)比例接近。我使用的是PyTorch的WeightedRandomSampler每個類別的采樣權(quán)重設(shè)為樣本數(shù)的倒數(shù)。這樣每輪訓(xùn)練里罕見類別也能被充分看到避免模型傾向把紅舌和絳舌預(yù)測成占多數(shù)的淡紅舌。3. 訓(xùn)練策略模型選型與超參數(shù)調(diào)試記錄3.1 骨干網(wǎng)絡(luò)選擇我對比了三種網(wǎng)絡(luò)結(jié)構(gòu)全部使用ImageNet預(yù)訓(xùn)練權(quán)重模型參數(shù)量驗證集準(zhǔn)確率單張推理耗時(CPU)ResNet1811.2M89.3%85msResNet5025.6M93.8%140msMobileNetV34.2M94.1%60ms最終選擇了MobileNetV3原因很實際它在準(zhǔn)確率上超過ResNet50的同時模型體積只有后者的六分之一推理速度快一倍以上。這個項目最終要部署在常見CPU設(shè)備上供批量圖片處理用推理時長非常重要。MobileNetV3的準(zhǔn)確率優(yōu)勢主要來自它的輕量注意力模塊對舌苔這種顏色敏感型任務(wù)恰好有效。如果你不需要部署到邊緣設(shè)備ResNet50也完全可以訓(xùn)練更穩(wěn)定調(diào)參難度更低。如果你需要在工業(yè)質(zhì)檢軟件里做實時推理那MobileNetV3是首選。3.2 超參數(shù)配置的實操記錄經(jīng)過三輪網(wǎng)格搜索最終確認(rèn)的一組參數(shù)優(yōu)化器AdamW初始學(xué)習(xí)率 1e-3權(quán)重衰減 5e-4學(xué)習(xí)率策略余弦退火CosineAnnealingLR最小學(xué)習(xí)率 1e-6Batch Size32ResNet用32MobileNet可以放大到64訓(xùn)練輪數(shù)80輪損失函數(shù)Label Smoothing CrossEntropy標(biāo)簽平滑系數(shù)0.1訓(xùn)練輪數(shù)和精度的關(guān)系這里值得單獨說我記錄過在40輪之前驗證集準(zhǔn)確率提升非??旎久?輪上漲2到3個點從40輪到60輪漲幅放緩到每10輪漲1個點左右60輪之后驗證集loss曲線開始出現(xiàn)輕微反彈這是過擬合的早期信號。我最終用Early Stopping在67輪處截停保存了那一輪的checkpoint。別迷信“訓(xùn)練輪數(shù)越多越好”深度學(xué)習(xí)模型在某個點之后會開始背訓(xùn)練集而不再學(xué)習(xí)可泛化的特征關(guān)鍵是盯著驗證集loss找最低點而不是最高精度點。3.3 遷移學(xué)習(xí)的使用方式直接全量微調(diào)所有層是不可取的第一次嘗試時我用凍結(jié)的骨干網(wǎng)絡(luò)只訓(xùn)分類頭——這種做法適合數(shù)據(jù)量極少小于300張的保護(hù)性訓(xùn)練但1980張的量已經(jīng)足夠讓骨干網(wǎng)絡(luò)的高層適應(yīng)舌象特征了。我的策略是前三輪凍結(jié)骨干網(wǎng)絡(luò)只訓(xùn)練分類頭等損失降到大概1.2左右之后再解凍骨干網(wǎng)絡(luò)的后半段用更低的學(xué)習(xí)率1e-4繼續(xù)整體微調(diào)12輪。這樣做的原因是避免一開始就用較大學(xué)習(xí)率破壞預(yù)訓(xùn)練權(quán)重里那些非常通用的圖像特征后續(xù)微調(diào)時它也更容易收斂到一個更優(yōu)的局部最優(yōu)解。有個細(xì)節(jié)要注意批量歸一化層BatchNorm BN在微調(diào)時需要保持訓(xùn)練模式如果用Batch Size太小比如4或8BN的統(tǒng)計量會不穩(wěn)定。這也是我一直用Batch Size32的原因——在這個尺度上BN層的均值和方差估算比較可靠。如果你顯存不夠建議用GroupNorm替代BN層而不是強行降低Batch Size。4. 部署與批量處理把模型變成可用的工具4.1 模型轉(zhuǎn)換從PyTorch到ONNX訓(xùn)練完成后要把PyTorch模型導(dǎo)出為ONNX格式方便脫離訓(xùn)練框架做推理同時也能用ONNX Runtime在新環(huán)境里快速跑起來后續(xù)如果性能不夠還可以再接TensorRT。導(dǎo)出過程比較簡單定好輸入分辨率就導(dǎo)出。但實測下來有幾個注意點動態(tài)軸設(shè)置如果希望同一個模型接受不同尺寸的輸入需要把輸入輸出的第2、第3維設(shè)為動態(tài)軸“-1”。但注意這會使推理速度變慢而且某些算子會不支持動態(tài)形狀。我在實際項目中固定到224×224省心又穩(wěn)定。Batch維度ONNX導(dǎo)出的模型默認(rèn)batch是1推理時不要傳形狀為(1,3,224,224)的numpy數(shù)組直接用這個尺寸就行。歸一化參數(shù)要固化訓(xùn)練時的mean和std我用的mean是[0.485, 0.456, 0.406]std是[0.229, 0.224, 0.225]必須寫進(jìn)推理腳本里而且順序不能錯。最容易出的bug是輸入圖片忘記做歸一化導(dǎo)致推理結(jié)果一團(tuán)糟——這種坑我?guī)蛣e人排查過至少三次。4.2 批量處理流水線做批量處理的時候我寫了一個簡單的腳本流程是遍歷文件夾下所有圖片 → OpenCV讀取 → 白平衡校正 → 裁剪舌體區(qū)域 → resize到224×224 → 歸一化 → ONNX Runtime推理 → 結(jié)果寫入CSV。用一萬張圖片做實際測試CPUi7-10750H下全程約28分鐘GPUGTX 1660Ti下約8分鐘。如果追求更快的CPU推理速度可以將MobileNetV3量化到INT8實測能再提速40%準(zhǔn)確率只下降0.6%。這批輸出文件可以直接打包成zip交付給項目方方便復(fù)盤分析。4.3 模型體積與運行環(huán)境導(dǎo)出后的ONNX模型大小約21MB壓縮成zip后只有約8MB。這為整個“基于深度學(xué)習(xí)的舌苔檢測.zip”交付物定下了一個清爽的邊界解壓后是一個帶圖形界面的推理小工具、一個ONNX模型文件、一個結(jié)果輸出目錄以及一份簡短的說明文檔。注意寫推理工具的GUI時建議用PySide6或者簡單點用Flask提供HTTP接口。我自己是用Flask做了一層接口封裝命令行調(diào)用也好、網(wǎng)頁上傳也好都能直接訪問識別功能避免了GUI跨平臺的各種兼容問題——這也是在部署階段吃過一些GUI庫的虧之后換過來的思路。5. 常見問題排查與避坑實錄5.1 訓(xùn)練損失不下降這個問題我經(jīng)歷過兩次一次是學(xué)習(xí)率設(shè)置過大1e-2損失曲線直接發(fā)散另一次是忘記對輸入做歸一化損失卡在1.5左右死活降不下去。排查順序是先看loss曲線形態(tài)——如果前期下降后突然跳高且回不來八成是學(xué)習(xí)率問題如果loss一直高位震蕩多半是輸入數(shù)據(jù)范圍不對或者標(biāo)簽錯位。建議從train loss開始仔細(xì)看因為驗證集loss不降還可能是數(shù)據(jù)泄露或者驗證集分布問題但train loss不降基本就是訓(xùn)練代碼的問題。可能的原因和對策現(xiàn)象原因?qū)Σ哂?xùn)練發(fā)散loss變成NaN學(xué)習(xí)率過大降低學(xué)習(xí)率到1e-4以下?lián)p失卡在某個高值不動輸入未歸一化檢查圖片像素范圍是否為[0,1]損失緩慢下降但驗證集不動過擬合增加數(shù)據(jù)增強強度降低模型復(fù)雜度驗證集loss持續(xù)低于訓(xùn)練lossDropout/BatchNorm狀態(tài)異常檢查模型是否錯誤地設(shè)置為eval模式5.2 過擬合問題樣本量不足時的應(yīng)對1980張圖片對深度學(xué)習(xí)模型來說并不算多MobileNetV3全參數(shù)量微調(diào)必然面臨過擬合風(fēng)險。我的做法是四管齊下一是數(shù)據(jù)增強中引入隨機擦除讓模型必須綜合多個區(qū)域的特征做判斷二是訓(xùn)練輪數(shù)控制嚴(yán)格使用Early Stopping機制patience設(shè)為8輪三是全連接層之前加一個dropout層概率0.3在微調(diào)階段它抑制過擬合的作用非常明顯四是Label Smoothing讓模型對hard label不要太“自信”騰出泛化空間。這幾招疊加下來訓(xùn)練集和驗證集的準(zhǔn)確率差距從最開始的15個百分點縮小到了4個百分點左右。過擬合的癥狀還有一個很隱蔽在訓(xùn)練集的困難樣本上準(zhǔn)確率極高但在實際拍攝的完全沒見過的新場景圖片上很差。如果只是在測試集上驗證很容易被樂觀數(shù)據(jù)騙到。我最后額外從實際拍攝環(huán)境中收集了50張“野外”圖片做終極驗證那才是模型實力的真實體現(xiàn)。5.3 舌體裁剪失敗與顏色偏移舌體分割這個小模型在處理極度暗光或者舌頭上有厚膩苔的時候偶爾會把嘴唇邊緣或口腔內(nèi)部也框進(jìn)來。我的策略是加一個后處理如果裁剪區(qū)域面積超過整圖的60%或者嚴(yán)重偏出中心區(qū)域就回退到原圖中心裁剪。這層邏輯被觸發(fā)時寧可多保留一些背景也不能把舌頭關(guān)鍵部分裁掉。另外不同的顯示器、不同模型的屏幕色彩會導(dǎo)致標(biāo)注時看到的效果不一樣所以標(biāo)注時盡量統(tǒng)一使用同一款顯示器關(guān)閉夜間模式和護(hù)眼模式這能減少色差對標(biāo)注判斷的干擾。5.4 類別混淆難解決紅舌、絳舌、紫舌這三類之間會互相混淆因為它們在顏色光譜上本來就是連續(xù)的在暗光環(huán)境下連醫(yī)生肉眼都要猶豫。最終緩解的方法是給這三類補充暗光樣本然后用錯題集分析——把被錯誤分類的圖片全部導(dǎo)出人工看一下到底是哪些特征在誤導(dǎo)模型。我發(fā)現(xiàn)大部分錯誤集中在“厚苔遮蓋舌體”的圖片上舌色被苔大面積覆蓋時模型看到的幾乎全部是苔的特征分類自然就偏了。這屬于數(shù)據(jù)分布問題解決方式只有兩個方向一是重新考慮任務(wù)設(shè)計比如兩個分類頭同時輸出舌色和苔色互相提供上下文信息二是在數(shù)據(jù)采集時要求拍攝者盡量把舌頭伸長讓舌中、舌尖區(qū)域露出來減少苔體遮擋。我給拍攝端做了一張示意圖示牌專門提醒“請盡量露出舌中與舌尖區(qū)域”實測這一個小改變就提升了不少采集質(zhì)量。寫在最后這個舌苔檢測項目做完之后我最大的感觸是在深度學(xué)習(xí)落地項目里真正決定成敗的不是模型結(jié)構(gòu)換了什么新花樣而是數(shù)據(jù)是否干凈、標(biāo)簽是否一致、預(yù)處理是否合理、訓(xùn)練過程有沒有盯著驗證集而不是訓(xùn)練集調(diào)整。MobileNetV3這種“老模型”配合遷移學(xué)習(xí)和一套扎實的數(shù)據(jù)管線完全足以支撐一個實用的分類工具。最后分享一個我常用來快速判斷數(shù)據(jù)質(zhì)量的土辦法訓(xùn)練之前先隨機抽100張圖片人工打印出來或者拼圖軟件拼成一張大圖盯著看十分鐘。如果連你自己都分不太清某些圖片應(yīng)該歸到哪一類那模型大概率也學(xué)不會——這時候最重要的事情不是繼續(xù)調(diào)模型而是回去重新梳理標(biāo)簽和采集標(biāo)準(zhǔn)。這個經(jīng)驗幫我避開了非常多無意義的“調(diào)參-訓(xùn)練-失望”循環(huán)希望也能幫到你。本文還有配套的精品資源點擊獲取