)
做畢設(shè)選題的時候很多同學一聽到“動物疲勞識別”第一反應是“這東西怎么做”第二反應是“是不是太偏了”。實際上這個題目放到計算機視覺的框架里就是一個典型的圖像二分類問題給一張動物圖像判斷它處于疲勞狀態(tài)還是正常狀態(tài)。沒有目標檢測沒有語義分割沒有姿態(tài)估計門檻非常友好但它又足夠完整能讓你把數(shù)據(jù)采集、標注、模型訓練、評估、部署這一整套深度學習流程全部走一遍。這篇文章就以我實際帶過的一個模擬項目X為例把基于CNN卷積網(wǎng)絡(luò)的動物疲勞識別從零到落地講清楚適合正在準備畢業(yè)設(shè)計、打算入門圖像分類的讀者直接參考。我先把話放在前面這個項目的核心價值不在于“識別準確率能有多高”而在于你能不能把每一個環(huán)節(jié)都處理好。數(shù)據(jù)定義是否合理、標注是否一致、訓練過程是否可復現(xiàn)、評估指標是否選對了這些才是答辯和高分的關(guān)鍵。下面我按整個項目推進的時間線來拆解每一部分都會給出我實際操作中的具體參數(shù)、踩過的坑和修正經(jīng)驗。1. 選題分析與整體技術(shù)路線1.1 這個題目為什么會成為一個好的畢設(shè)選題很多同學選畢設(shè)題目時會陷入兩個極端要么選一個非常宏大但根本做不完的方向比如“基于深度學習的智能養(yǎng)殖系統(tǒng)”聽起來很唬人但涉及檢測、跟蹤、識別、告警好幾塊一個人短期根本搞不定要么選一個過于簡單的東西比如用現(xiàn)成的模型跑一下公開數(shù)據(jù)集寫個報告就完事答辯時很容易被問住?!皠游锲谧R別”恰好卡在中間位置。從技術(shù)難度看它不需要你做復雜的目標定位只要圖像里動物主體相對明確CNN就能直接端到端學習疲勞特征。從算法深度看它涉及到圖像分類任務的典型難點——類間差異小、類內(nèi)差異大、樣本不均衡這些都是可以寫進論文的實質(zhì)內(nèi)容。從應用價值看疲勞狀態(tài)監(jiān)測在畜牧業(yè)、寵物健康監(jiān)護、實驗動物狀態(tài)評估等領(lǐng)域都有真實需求不是憑空捏造的場景。我當時給A同學建議這個題目的時候特別強調(diào)了一點不要試圖一步到位做一個“全自動畜牧疲勞監(jiān)測系統(tǒng)”那是產(chǎn)品級目標。畢設(shè)的核心是把“基于CNN的動物疲勞識別”這個算法鏈路做扎實最后能給出一個可復現(xiàn)的模型、一組可靠的評估數(shù)據(jù)、一個簡單的推理調(diào)用方式就非常完整了。這個定位很重要因為很多人的畢設(shè)最后爛尾不是技術(shù)能力不夠而是目標定得太大時間被各種邊角料吞掉了。1.2 疲勞狀態(tài)怎么定義數(shù)據(jù)從哪來這個題目的第一個坑就是對“疲勞”的定義。疲勞不是一只動物站在那里寫著“我很累”它是一種需要人為約定的視覺狀態(tài)。如果不把定義先想清楚后面所有的標注都會亂套。在實際操作里我們采用的是“行為學特征 影像特征”結(jié)合的定義方式。針對常見的哺乳動物以某實驗農(nóng)場的一批動物為樣本我們約定以下可辨識特征作為疲勞狀態(tài)的判斷依據(jù)眼睛閉合程度疲勞狀態(tài)下眼睛閉合面積明顯超過正常狀態(tài)或者出現(xiàn)持續(xù)性半閉耳朵姿態(tài)健康狀態(tài)下耳朵經(jīng)常轉(zhuǎn)動、豎起疲勞狀態(tài)下耳朵低垂活動頻率降低頭部角度疲勞的動物頭部經(jīng)常低垂少見主動環(huán)顧四周的動作反應速度這個特征在單張靜態(tài)圖像里無法直接體現(xiàn)所以我們只作為輔助條件不作為主要標注依據(jù)。你可以看到這個定義里面“眼睛閉合程度”“耳朵姿態(tài)”“頭部角度”都是可以在單張圖像上觀察到的這就保證了人工標注的可操作性。疲勞狀態(tài)是二分類標簽如果后續(xù)你想做成三分類正常、輕度疲勞、重度疲勞可以在此基礎(chǔ)之上擴展但畢設(shè)階段建議先做二分類。數(shù)據(jù)來源方面我們用了兩條腿走路一部分來自公開的動物行為圖像集另一部分是我們自己在模擬場景下拍攝的。公開數(shù)據(jù)的好處是量大、覆蓋場景廣壞處是標注標準不一定和我們的定義一致自采數(shù)據(jù)的好處是標注可控壞處是需要花時間和精力。對于畢設(shè)來說我建議公開數(shù)據(jù)占比大概七成、自采數(shù)據(jù)三成這樣既能保證數(shù)據(jù)量又能保證評價時有自己可控的驗證集。需要注意的是公開數(shù)據(jù)里如果有非常不清晰的圖像寧可不要也不要讓它污染訓練集。1.3 整體技術(shù)路線整個項目的技術(shù)路線可以歸納為五步數(shù)據(jù)采集與清洗、疲勞標簽標注、圖像預處理與增強、CNN模型訓練與調(diào)優(yōu)、模型評估與推理封裝。不要跳過任何一步尤其是數(shù)據(jù)清洗和標注一致性檢查很多人為了趕進度在這一塊偷工減料后期訓練時各種奇怪問題都會冒出來。模型方面我們采用的是經(jīng)典的卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。從零搭建了一個小規(guī)模的CNN同時也做了基于預訓練殘差網(wǎng)絡(luò)的遷移學習實驗最后對比發(fā)現(xiàn)兩種方案在不同數(shù)據(jù)規(guī)模下的表現(xiàn)差異。對于有GPU環(huán)境的同學我建議兩條路線都跑一下答辯時這是一個很好的工作量體現(xiàn)如果設(shè)備限制比較大可以從零搭建的小網(wǎng)絡(luò)開始效果也完全夠用。我把整個流程的時間分配建議寫在下面表格里你可以根據(jù)自己的周期調(diào)整階段時間占比說明數(shù)據(jù)采集與標注30%決定模型上限最容易被低估預處理與增強15%清洗、統(tǒng)一尺寸、增強策略模型搭建與訓練30%結(jié)構(gòu)設(shè)計和調(diào)參迭代評估與分析15%指標、混淆矩陣、誤判案例模塊封裝與文檔10%推理腳本、README、答辯材料這個時間分配不是我隨便寫的。我見過太多同學在模型訓練上死磕卻忽略了數(shù)據(jù)標注的質(zhì)量最后訓練出來的模型在測試集上準確率很高一拿到自己拍的照片上就崩。原因往往不是模型不行而是訓練數(shù)據(jù)和真實場景差異太大或者標注標準不統(tǒng)一導致模型學到的特征混亂。2. 數(shù)據(jù)集構(gòu)建與預處理2.1 數(shù)據(jù)采集的實操細節(jié)數(shù)據(jù)采集聽起來簡單不就是拍照片嗎但在實際操作中有幾個細節(jié)直接決定了數(shù)據(jù)的可用性。首先是拍攝角度的一致性。在自采數(shù)據(jù)時我強烈建議固定攝像頭的安裝高度和角度盡量避免出現(xiàn)俯拍、仰拍混雜的情況。CNN雖然有一定的尺度和平移魯棒性但角度差異過大時模型很容易學到“角度特征”而不是“疲勞特征”。我們在模擬場景中把攝像頭固定在側(cè)上方約45度角的位置保證動物主體在畫面中央且完整。其次是光線條件。疲勞狀態(tài)判斷非常依賴眼睛和耳朵的細節(jié)光線過暗時這些特征會被噪聲淹沒。自采數(shù)據(jù)的時候盡量在自然光充足的條件下拍攝不要使用會導致偏色的彩色光源。如果你之后要對公開數(shù)據(jù)進行清洗優(yōu)先選擇那些主體清晰、光照均勻的圖像模糊的、嚴重過曝的、主體被遮擋的圖像直接舍棄。第三是樣本的去重問題。很多人從視頻里抽幀如果直接連續(xù)抽幀相鄰幀之間高度相似這些近重復樣本會被訓練集和驗證集同時包含導致驗證準確率虛高。我們當時抽幀策略是每隔若干幀抽一幀并且抽完以后計算相鄰幀的像素相似度相似度超過閾值的保留一張其余的丟棄。這個操作雖然簡單但能有效避免數(shù)據(jù)泄漏在答辯時也是可以講的細節(jié)。最后要檢查的是類別平衡性。疲勞狀態(tài)在自然場景里本來就是少數(shù)情況所以采集到的正常樣本往往比疲勞樣本多很多。我們的初始數(shù)據(jù)集里正常和疲勞的比例大概是3.5比1這個比例如果不處理模型會傾向于把所有樣本都預測為正常因為這樣準確率也能到70%以上。后面我會講具體的處理辦法但至少你要先心里有數(shù)你的數(shù)據(jù)不平衡程度有多嚴重各是多少張圖。2.2 標注標準的制定與一致性檢查標注是項目中最容易被輕視的環(huán)節(jié)也是影響最終結(jié)果最大的環(huán)節(jié)。一張圖像到底算疲勞還是正常不同人看可能結(jié)果不同所以必須要把標準提前固化下來。我們當時做了一套標注規(guī)范文檔里面寫明了各種邊界情況的判定規(guī)則。比如眼睛半閉但耳朵豎直怎么算答案是只要眼睛閉合面積超過整個眼球面積的一半就傾向判定為疲勞耳朵姿態(tài)作為輔助修正。頭部低垂但眼睛完全睜開怎么算答案是以眼睛狀態(tài)為主判定為正常。這套規(guī)則看起來非常死板但它保證了不同標注者之間的一致性。實際操作中我們讓三個人獨立對同一批圖像進行標注最后取多數(shù)票作為最終標簽。三個人標簽不一致的圖像單獨挑出來討論討論后仍無法達成一致的直接棄用。你可能會覺得三個人標注太奢侈但對于畢設(shè)來說哪怕只有你自己一個人標注也建議隔一段時間后重標一次然后對比兩次標注的一致性。我們當時第二遍重標的時候發(fā)現(xiàn)有接近5%的圖像兩次標注結(jié)果不一致這些圖像基本集中在模糊和遮擋比較嚴重的區(qū)域。把它們剔除之后模型的訓練過程明顯穩(wěn)定了很多。另外一個我強烈建議的操作是在標注完成之后按照類別分別隨機抽取一部分圖像人工快速瀏覽一遍確認是否有標錯的異常值。這個檢查看起來沒有技術(shù)含量但能阻止臟數(shù)據(jù)進入訓練流程。很多同學把時間花在調(diào)學習率上卻不愿意花半小時做數(shù)據(jù)檢查這完全搞反了優(yōu)先級。2.3 數(shù)據(jù)增強怎么做才不會引入問題數(shù)據(jù)增強是圖像分類任務里繞不開的環(huán)節(jié)。它的本質(zhì)是對原始圖像做一系列隨機變換生成更多樣的訓練樣本從而提升模型的泛化能力。對于動物疲勞識別這個場景我們用到的增強操作包括水平翻轉(zhuǎn)這個操作對于動物圖像來說是安全的因為左右翻轉(zhuǎn)不會改變疲勞語義隨機裁剪在原始圖像上隨機裁剪一部分后縮放到固定尺寸可以模擬不同的取景范圍亮度與對比度擾動適當模擬光線條件變化輕微旋轉(zhuǎn)在正負15度范圍內(nèi)隨機旋轉(zhuǎn)模擬拍攝角度的小幅波動。這里有一個很重要的原則增強操作不能改變疲勞語義。比如垂直翻轉(zhuǎn)就不能用因為動物如果倒過來特征分布完全沒有意義了。還比如過度的隨機裁剪如果裁掉了動物主體模型學到的就是背景特征這也會出問題。所以我們裁剪的范圍通??刂圃谠紙D的0.7到1.0之間并且要保證裁剪后圖像里動物主體仍然可見。增強策略還要注意訓練集和驗證集的區(qū)別。驗證集和測試集一定不要做任何數(shù)據(jù)增強最多做一個尺寸歸一化和歸一化處理否則你評估的就是增強后的數(shù)據(jù)性能而不是模型本身的能力。這是一個很基礎(chǔ)但容易被忽略的細節(jié)。預處理方面統(tǒng)一的圖像尺寸我們定為224乘224像素這也是主流圖像分類模型的標準輸入大小。像素值歸一化到0到1區(qū)間方便模型訓練時梯度穩(wěn)定。如果你用的是預訓練模型建議直接用它在ImageNet上統(tǒng)計的均值和標準差做標準化而不是自己重新統(tǒng)計這樣遷移效果更穩(wěn)。3. CNN模型設(shè)計與核心參數(shù)選擇3.1 從零搭建還是用預訓練模型這是每個做圖像分類的人都會面臨的選擇題。我給出的建議是如果你手里只有幾千張圖像優(yōu)先使用預訓練模型做遷移學習如果數(shù)據(jù)集到了幾萬張的規(guī)模從零搭建的CNN也能有不錯的表現(xiàn)。為什么這么說因為預訓練模型在大型通用圖像數(shù)據(jù)集上學到過豐富的底層特征比如邊緣、紋理、形狀。這些特征對動物圖像同樣有效你只需要讓模型在動物疲勞數(shù)據(jù)上微調(diào)高層特征即可。從零搭建的CNN需要從隨機初始化開始學習所有特征在數(shù)據(jù)量不足時容易欠擬合或過擬合。不過從畢設(shè)角度出發(fā)我還是建議把兩種方案都跑一遍做一個對比實驗。這個對比本身就是工作量也是論文里可以寫的一章內(nèi)容。我們的實驗中預訓練殘差網(wǎng)絡(luò)在驗證集上的準確率比從零搭建的小CNN高了大約4到5個百分點尤其是在疲勞類別上的召回率提升更明顯。如果你設(shè)備資源有限也不需要擔心。預訓練模型可以用CPU訓練只是慢一些。很多同學以為深度學習必須要有好顯卡其實對于二分類任務、小數(shù)據(jù)集來說CPU也完全可以跑只不過一次訓練可能要幾個小時。我后面給出的訓練策略里會提到如果速度太慢可以減少訓練輪次、降低輸入分辨率這些都是可接受的折中方案。3.2 具體網(wǎng)絡(luò)結(jié)構(gòu)示例如果你想從零搭建一個輕量的CNN我給你一個我實測過非常穩(wěn)的結(jié)構(gòu)方案。輸入是224乘224的RGB圖像經(jīng)過下面這些層卷積層1輸出通道32卷積核大小3乘3填充1激活函數(shù)ReLU后面接2乘2最大池化 卷積層2輸出通道64卷積核大小3乘3填充1激活函數(shù)ReLU后面接2乘2最大池化 卷積層3輸出通道128卷積核大小3乘3填充1激活函數(shù)ReLU后面接2乘2最大池化 展平后接全連接層256維加ReLU和Dropout最后接一個2節(jié)點的全連接分類層輸出對應正常和疲勞兩個類別的邏輯回歸值。這個結(jié)構(gòu)的整體參數(shù)量在百萬量級對畢設(shè)來說完全夠用。如果你用PyTorch實現(xiàn)核心代碼大概長這樣import torch.nn as nn class AnimalFatigueCNN(nn.Module): def __init__(self, num_classes2): super(AnimalFatigueCNN, self).__init__() self.features nn.Sequential( nn.Conv2d(3, 32, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(64, 128, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2) ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(128 * 28 * 28, 256), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(256, num_classes) ) def forward(self, x): x self.features(x) x self.classifier(x) return x注意這里全連接層的輸入維度是根據(jù)前面特征圖尺寸算出來的。224輸入經(jīng)過三次2倍池化之后變成28乘28再乘上卷積通道數(shù)128就是128乘28乘28。很多同學在復現(xiàn)時最容易在這里報錯所以一開始就把計算過程理清楚。如果你選擇預訓練模型路線可以把模型最后一層全連接替換成輸出維度為2的新全連接層其他部分沿用預訓練權(quán)重。這些模型在深度學習框架里都有直接加載的方式不需要自己重新實現(xiàn)。訓練時凍結(jié)前幾層參數(shù)、只微調(diào)后面幾層的方式在數(shù)據(jù)量比較小的時候效果更穩(wěn)。3.3 損失函數(shù)、優(yōu)化器與超參數(shù)設(shè)定二分類任務的損失函數(shù)首選交叉熵損失。PyTorch里的CrossEntropyLoss已經(jīng)結(jié)合了Softmax不需要在網(wǎng)絡(luò)最后一層再手動加激活函數(shù)。如果類別不平衡可以給損失函數(shù)傳入類別權(quán)重讓少數(shù)類樣本的錯誤產(chǎn)生更大的梯度。我們當時的正常樣本數(shù)量大約是疲勞樣本的3.5倍所以在CrossEntropyLoss里給疲勞類設(shè)置的權(quán)重是3.5給正常類的權(quán)重是1.0。這個簡單的操作讓疲勞類別的召回率明顯提升。優(yōu)化器方面我推薦使用Adam初始學習率設(shè)置為0.001。Adam自帶自適應學習率調(diào)整對于第一次做深度學習項目的同學來說使用門檻比SGD低很多不需要手動調(diào)整動量等參數(shù)。如果用的是預訓練模型遷移學習學習率可以降到0.0001因為模型已經(jīng)在一個比較好的初始點上學習率太大會破壞已經(jīng)學好的特征。批次大小我設(shè)置為32。這個數(shù)值在大部分顯卡甚至CPU上都能跑得動同時梯度更新也比較穩(wěn)定。不要為了追求速度把批次設(shè)得過大對于這種小數(shù)據(jù)集批次64以下效果差異不大但一旦內(nèi)存溢出就很麻煩。訓練輪次我們先設(shè)定為30到50輪配合早停機制使用。所謂早停就是每一輪訓練后在驗證集上計算準確率如果連續(xù)若干輪驗證準確率不再提升就提前終止訓練防止過擬合。這個機制在實際訓練中非常有用我們在第35輪左右觀察到了明顯的驗證損失上升趨勢如果不早停繼續(xù)訓練到50輪模型在測試集上的表現(xiàn)反而會下降。如果你想完全復現(xiàn)我的實驗條件還需要設(shè)置隨機種子。PyTorch和Python的隨機數(shù)生成器、NumPy的隨機數(shù)生成器都要設(shè)置同一個種子。這樣不同人運行同一個腳本能得到一模一樣的結(jié)果這在畢業(yè)論文里是一個可復現(xiàn)性加分項。代碼很簡單就是固定seed為某個整數(shù)然后設(shè)置環(huán)境變量和各個庫的種子這里不展開。4. 訓練實操與避坑記錄4.1 數(shù)據(jù)劃分與訓練策略數(shù)據(jù)劃分方式會直接影響你對模型效果的判斷。我們當時把數(shù)據(jù)集按7比2比1劃分為訓練集、驗證集、測試集。很多同學只劃分訓練集和測試集沒有單獨的驗證集然后在測試集上調(diào)參數(shù)這是非常大的錯誤。調(diào)參過程本身已經(jīng)利用了測試集的信息最后得到的準確率是偏樂觀的不能真實反映模型泛化能力。分出一個驗證集專門用來調(diào)參和早停測試集只在最終評估時使用一次結(jié)果才可信。劃分的時候還要注意按個體劃分而不是簡單隨機劃分。什么意思呢如果同一個動物的多張圖像同時出現(xiàn)在訓練集和測試集里模型可能記住了這個動物的特征而不是學到通用的疲勞特征測試集準確率會虛高。我們當時按動物個體先分好組確保同一個體的圖像只出現(xiàn)在一個集合里。這個細節(jié)乍一看不重要但答辯時如果評委問“你的模型是不是真的泛化到了新動物”你就能給出一個站得住的答案。訓練過程中我習慣每一輪都打印訓練損失、訓練準確率、驗證損失、驗證準確率四組數(shù)據(jù)。不要只看準確率損失下降曲線更能反映模型是否收斂。很多情況下準確率看起來沒變化但損失還在緩慢下降說明模型仍在學習反過來準確率很高但驗證損失開始上升說明已經(jīng)在過擬合了。如果資源允許每次訓練結(jié)束以后保存最優(yōu)模型權(quán)重而不是最后一輪的權(quán)重。因為最后一輪可能已經(jīng)過擬合或者開始波動最優(yōu)權(quán)重通常是驗證準確率最高的那個時刻的權(quán)重。我在訓練腳本里加入了一個簡單的判斷當驗證準確率超過歷史最佳值時保存當前模型參數(shù)。這個策略簡單但非常有效。4.2 訓練曲線怎么看訓練曲線是診斷模型狀態(tài)最重要的工具。我一般會在訓練結(jié)束后把損失曲線和準確率曲線畫在一張圖上直觀地觀察模型行為。常見的情況有幾種。第一種是訓練損失下降驗證損失也在下降這是最健康的狀態(tài)說明模型在學習且沒有過擬合。第二種是訓練損失下降驗證損失先降后升說明模型開始過擬合了。這時候可以回調(diào)早停、增加Dropout、增加數(shù)據(jù)增強或者減少模型參數(shù)。第三種是訓練損失和驗證損失都幾乎不降這說明模型容量不足或者學習率設(shè)置不合理需要增加網(wǎng)絡(luò)深度、寬度或者調(diào)大學習率。第四種情況比較隱蔽訓練準確率很高但驗證準確率一直在某個較低水平徘徊。這通常是數(shù)據(jù)劃分出了問題比如訓練集和驗證集分布不一致或者驗證集數(shù)據(jù)量太小、噪聲太大。我遇到過類似的情況后來檢查發(fā)現(xiàn)驗證集里有幾張圖像存在標注錯誤把它們剔除之后驗證準確率正常了。所以看到曲線異常先懷疑數(shù)據(jù)和標注再去動模型。我強烈建議你養(yǎng)成記錄每次實驗的參數(shù)和結(jié)果的習慣。我當時用了一個簡單的表格記錄學習率、模型結(jié)構(gòu)、是否用預訓練、數(shù)據(jù)增強方式、驗證準確率等關(guān)鍵信息。這樣做的好處是調(diào)參不再靠感覺每次修改都有據(jù)可查。很多同學調(diào)參沒過一會兒就忘了自己試過什么后期總結(jié)實驗部分的時候才知道有多痛苦。4.3 常見問題與排查方法訓練過程中我遇到的典型問題整理成表格給你參考癥狀可能原因排查方向損失一直不下降學習率過大或過小嘗試0.01、0.001、0.0001梯度訓練準確率正常驗證準確率低過擬合檢查是否缺驗證集、增強策略、Dropout正常類別準確率高疲勞類別很低類別不平衡給損失函數(shù)加類別權(quán)重或?qū)ι贁?shù)類過采樣換一批圖像后效果崩掉訓練數(shù)據(jù)和實際場景差異大增加自采數(shù)據(jù)統(tǒng)一拍攝條件預測結(jié)果總偏向某一個類別閾值或類別權(quán)重問題調(diào)整決策閾值或重新設(shè)計損失權(quán)重還有一個容易被忽視的問題圖像的EXIF信息里的方向標記。手機拍攝的照片有時會帶有旋轉(zhuǎn)標記直接讀圖時一些庫不會自動處理方向?qū)е聢D像被旋轉(zhuǎn)了90度。訓練和測試時旋轉(zhuǎn)方向不一致模型自然表現(xiàn)不穩(wěn)定。解決辦法是在數(shù)據(jù)預處理階段統(tǒng)一將所有圖像轉(zhuǎn)換成RGB并校正方向輸出為標準的三通道圖像。推理階段也常遇到一個問題輸入圖像的尺寸和訓練時不一致。如果你訓練時統(tǒng)一縮放到224乘224那么推理時也必須走同樣的預處理流程不能直接拿一張1920乘1080的原圖丟給模型。我的做法是把預處理邏輯寫成一個獨立的函數(shù)訓練和推理共用同一個函數(shù)避免兩邊出現(xiàn)差異。這些都是很小的細節(jié)但往往就是這些細節(jié)決定了最終成績。5. 模型評估與結(jié)果分析5.1 評價指標怎么選二分類任務的評價指標不只有準確率。在動物疲勞識別場景下準確率甚至不是最重要的指標。設(shè)想一下如果目的是篩選出疲勞狀態(tài)的動物我們更關(guān)心的是“把疲勞動物漏掉”的概率有多低也就是疲勞類別的召回率。反過來如果資源有限、不可能對所有標記疲勞的個體都查驗那么精確率也很重要。所以我建議你至少報告四個指標準確率、精確率、召回率、F1值并且分別計算正常類別和疲勞類別各自的數(shù)值而不是只給一個總的準確率?;煜仃囈惨嫵鰜硭苤庇^地展示模型在哪一類上犯錯更多。我們在測試集上的最終結(jié)果顯示預訓練模型的整體準確率約為93%但把混淆矩陣拆開看正常類的召回率是96%疲勞類的召回率只有88%。這說明疲勞類被漏檢的概率仍然偏高。針對這個情況我們通過調(diào)整分類閾值將決策邊界向疲勞類別偏移在疲勞類召回率提升到92%的同時正常類召回率小幅下降到93%整體F1值更均衡了。這個調(diào)整操作在論文里可以寫得很有說服力因為你已經(jīng)從“模型訓練”進入了“模型決策”層面的優(yōu)化。5.2 測試結(jié)果實測記錄這里給出我們一組實際的測試記錄數(shù)據(jù)來自一個模擬場景下的動物圖像集類別是二分類。需要注意這組數(shù)據(jù)僅供你理解評估流程不同數(shù)據(jù)分布下結(jié)果一定會有差異你的任務是把評估方法做對而不是套用某一個準確率數(shù)值。在測試集共400張圖像上正常類200張疲勞類200張這里為了展示效果我們做了類別均衡處理實際中不需要強行均衡測試集保持原始分布更真實。指標數(shù)值整體準確率92.5%正常類精確率94.1%正常類召回率96.0%疲勞類精確率89.2%疲勞類召回率88.0%宏平均F10.915可以看到疲勞類精確率明顯低于正常類。這說明模型把一部分正常圖像誤判成了疲勞。我逐個查看誤判圖像后發(fā)現(xiàn)最常見的情況是圖像里動物正在閉眼休息或者低頭吃食單從視覺特征上看確實和疲勞比較接近。這種情況下模型犯錯其實情有可原因為只憑單張靜態(tài)圖像人類也不能做到百分之百準確。這個觀察讓我意識到如果后續(xù)想要進一步提升效果引入視頻時序信息會是一個合理方向。當然畢設(shè)階段做到這一步已經(jīng)很完整了時序模型可以作為展望章節(jié)的內(nèi)容來寫。5.3 誤判案例拆解誤判案例分析是論文里很出彩的部分因為它能體現(xiàn)你對問題本質(zhì)的理解。我舉一個我們遇到的典型例子一張動物的側(cè)面照眼睛睜得很大按標注規(guī)則應該判為正常但模型輸出了疲勞類別。我仔細對比了同類圖像發(fā)現(xiàn)模型學到的疲勞特征里可能包含“頭部低垂耳朵下垂”的組合而這張圖像里動物的耳朵恰好是向后壓的物理位置上和疲勞樣本比較像因此模型發(fā)生了混淆。這類問題很難單純靠調(diào)參解決更可行的路徑是增加類似硬樣本的訓練數(shù)據(jù)或者在預處理階段把耳朵區(qū)域單獨裁剪出來作為輔助輸入。當然這些都是畢設(shè)之后可以繼續(xù)做的方向不需要在一開始的版本里實現(xiàn)。與之相反的另一類誤判是圖像里動物眼睛明顯半閉但模型判成了正常。這類誤判更危險因為它是漏檢。我分析后發(fā)現(xiàn)這些圖像里動物處于側(cè)后方角度眼睛區(qū)域被毛發(fā)的陰影覆蓋視覺特征非常弱。說明模型對眼睛局部區(qū)域的敏感性還不夠。解決思路有兩個一是用注意力機制讓模型更關(guān)注眼睛和耳朵區(qū)域二是使用目標檢測先定位頭部區(qū)域再做分類。這兩種方法都會顯著增加項目的復雜度所以我當時沒有在畢設(shè)主流程中加入而是留在了改進方案里。6. 模型部署與后續(xù)擴展6.1 模型導出與推理封裝畢設(shè)如果只停留在訓練腳本和準確率報告里總覺得少了點東西。我建議至少做一個簡單的推理腳本讓老師或者同學能直接拿一張新圖像傳入模型輸出分類結(jié)果和置信度。這個動作不復雜但會讓整個項目的完整度提升一個檔次。我用PyTorch為例說一下導出流程。訓練結(jié)束后把模型狀態(tài)字典加載回模型結(jié)構(gòu)然后調(diào)用eval模式。需要特別注意的是一定要在eval模式下推理否則BN層和Dropout層的行為會和訓練時不一致。我在沒加eval模式的時候推理結(jié)果忽好忽壞查了半天才發(fā)現(xiàn)是這地方的小問題。如果想讓推理速度更快、脫離深度學習框架環(huán)境運行可以把模型導出為ONNX格式然后使用ONNX Runtime加載。ONNX是一個通用的模型交換格式很多平臺都能直接運行。部署到邊緣設(shè)備的時候也可以轉(zhuǎn)成更輕量的格式但畢設(shè)階段做到ONNX這一步已經(jīng)完全夠用。一個最簡單的推理流程大概是讀取圖像統(tǒng)一縮放和歸一化輸入模型得到兩個類別的得分再經(jīng)過Softmax轉(zhuǎn)成概率取概率較大的類別作為輸出同時打印出置信度。我還建議在推理腳本里加入一個邊界情況判斷如果輸入圖像是灰度圖先轉(zhuǎn)換成三通道RGB如果圖像里沒有動物主體直接提示用戶重新輸入不要硬跑模型輸出一個沒有意義的結(jié)果。這些交互細節(jié)看似不起眼但對于一個需要演示的項目來說非常重要因為現(xiàn)場演示出問題的概率其實很高提前加好防御邏輯能幫你避免尷尬。6.2 后續(xù)還能怎么擴展畢設(shè)答完以后如果你對這個方向還有興趣可以沿著幾個方向繼續(xù)深入。最直接的方向是增加類別把二分類擴展為“正常、輕度疲勞、重度疲勞”三分類配合更細的數(shù)據(jù)標注規(guī)范。第二個方向是引入時序信息用視頻片段而不是單張圖像來判斷疲勞狀態(tài)通過幀序列捕捉動物的動態(tài)行為特征比如反應速度、活動頻率這些在靜態(tài)圖像里完全無法體現(xiàn)。第三個方向是把區(qū)域感知和分類結(jié)合先檢測動物頭部、眼睛區(qū)域然后對這個局部區(qū)域做疲勞判斷理論上能降低背景干擾。還有一個我比較看好的方向是遷移到不同動物種類上。你可以在一個動物種類上訓練好模型然后通過少量數(shù)據(jù)和微調(diào)把它適配到另一個種類這樣可以大幅降低人工標注成本。這個方向在真實應用場景中價值很高也適合作為后續(xù)研究內(nèi)容寫進論文展望。做這個項目的整個過程里我個人最大的體會是模型的代碼實現(xiàn)反而是整個流程里最簡單的一環(huán)真正花時間的、決定成敗的永遠是數(shù)據(jù)定義、數(shù)據(jù)處理和評估設(shè)計。很多同學一上來就想用最高級的網(wǎng)絡(luò)結(jié)構(gòu)、最流行的訓練技巧結(jié)果忽略了最基本的數(shù)據(jù)質(zhì)量檢查最后模型泛化能力一塌糊涂。如果你準備做這個題目我真心建議你把一半以上的時間花在數(shù)據(jù)上先把數(shù)據(jù)和評估體系做扎實再談模型結(jié)構(gòu)你會發(fā)現(xiàn)問題少很多。最后分享一個小技巧把所有實驗的超參數(shù)、結(jié)果、踩坑記錄都寫在一個單獨的文檔里包括失敗了的東西也記下來。答辯的時候評委問“你有沒有嘗試過其他方案”你就能自然地講出來“試過效果不好原因是什么”。這種回答展示的是真實的研究過程遠遠比只講成功的部分更有說服力。希望這篇內(nèi)容能幫你在畢設(shè)路上少走幾個彎路也祝你最后能交出一份自己滿意的項目。