器學(xué)習(xí)實(shí)現(xiàn)缺陷水果分選:Matlab從驗(yàn)證到產(chǎn)線的完整流程)
做果蔬加工的朋友應(yīng)該都有體會(huì)分選線上的“挑壞果”這道工序看著簡(jiǎn)單其實(shí)是最難自動(dòng)化的一環(huán)。不同品類的缺陷形態(tài)差異極大——有的是磕碰造成的褐變有的是霉變白斑有的是果皮疤痕——而且同一種缺陷在不同品種、不同成熟度上的表現(xiàn)可能完全不一樣。傳統(tǒng)機(jī)器視覺靠固定閾值去分換個(gè)光照、換個(gè)批次就失靈。這幾年我陸陸續(xù)續(xù)做了幾個(gè)用機(jī)器學(xué)習(xí)做缺陷水果分選的項(xiàng)目其中一個(gè)典型做法就是在Matlab里快速完成算法驗(yàn)證再遷移到產(chǎn)線設(shè)備。這篇文章就把這套流程拆開講清楚從數(shù)據(jù)準(zhǔn)備、特征與模型選型到Matlab代碼主干、評(píng)估調(diào)優(yōu)再到真正上線會(huì)遇到的工程問題一次說透。想做缺陷水果分選、或者剛接觸農(nóng)產(chǎn)品視覺檢測(cè)開發(fā)的人都可以拿這套思路作為起點(diǎn)。1. 缺陷分選的任務(wù)本質(zhì)先定義問題再談算法很多人在做這類項(xiàng)目時(shí)容易一上來就抱著深度學(xué)習(xí)模型不放結(jié)果數(shù)據(jù)集只有幾百張圖模型訓(xùn)出來連自己都不信。我的經(jīng)驗(yàn)是先把“篩什么、分幾類、目標(biāo)是什么”這三件事徹底理清楚再談具體用哪種算法。1.1 缺陷的類型與分級(jí)標(biāo)準(zhǔn)決定了算法的輸出設(shè)計(jì)水果缺陷在視覺上并不是同一個(gè)概念至少可以分成這樣幾類缺陷類型典型表現(xiàn)視覺特征磕碰傷撞擊后局部褐變區(qū)域邊緣模糊顏色由黃褐色向深褐色過渡腐爛/霉變表面軟爛、菌絲或霉斑顏色突變明顯常伴隨凹陷或絨毛感疤痕/果銹生長(zhǎng)期表皮損傷顏色與正常果皮差異小紋理粗糙蟲眼/孔洞蟲蛀造成的凹陷或開口局部暗色邊緣銳利畸形形狀偏離正常果形輪廓不規(guī)則與其他缺陷不同屬于形狀問題這五類缺陷對(duì)應(yīng)的視覺特征差異很大意味著同一個(gè)模型的輸入設(shè)計(jì)也可能不同??呐鰝透癄€適合用顏色與紋理特征捕捉畸形更適合分析輪廓形狀而疤痕如果顏色接近正常果皮可能要靠局部紋理細(xì)節(jié)才能抓住。更重要的是分揀目標(biāo)通常不止“好果/壞果”二分類。產(chǎn)線上常見的需求是三級(jí)分選無缺陷、輕微缺陷可打折銷售、嚴(yán)重缺陷直接剔除。這個(gè)輸出設(shè)計(jì)直接決定了最后的分類層是二分類還是三分類也決定了評(píng)估指標(biāo)怎么定。1.2 為什么固定閾值和人工規(guī)則在壞果面前會(huì)被打穿早期做機(jī)器視覺分選最常見的方案是用顏色空間加閾值把RGB轉(zhuǎn)到HSV或者Lab然后對(duì)某個(gè)通道設(shè)一個(gè)范圍超出范圍就判為缺陷。這個(gè)思路在玩具數(shù)據(jù)上看著沒問題一上產(chǎn)線就崩原因是這樣幾個(gè)一是光照不均。同一個(gè)蘋果表面高光區(qū)域和陰影區(qū)域的像素值差異可能比好果和壞果的差異還大。閾值一旦固定高光處的好果會(huì)被誤殺。二是缺陷顏色與正常果皮顏色接近。比如蘋果的果銹和正常紅暈之間、梨的碰傷和自然黃褐色之間在顏色上幾乎沒有清晰的界線。三是缺陷可能是局部的。一個(gè)果子95%是好的只有一小塊爛點(diǎn)。如果只統(tǒng)計(jì)全局平均色這個(gè)小面積缺陷很容易被淹沒如果做像素級(jí)閾值又會(huì)被果柄、花萼這些本身顏色比較深的部分干擾。所以純規(guī)則的方案只能在品種單一、光照控制極好、缺陷形態(tài)非常標(biāo)準(zhǔn)的條件下工作。一旦換成自然種植的水果規(guī)則就開始失效。這個(gè)場(chǎng)景下機(jī)器學(xué)習(xí)能自動(dòng)從樣本中學(xué)習(xí)“缺陷到底長(zhǎng)什么樣”尤其是小的局部異常比人工寫規(guī)則靠譜得多。1.3 機(jī)器學(xué)習(xí)介入的三個(gè)切入點(diǎn)根據(jù)項(xiàng)目需求和數(shù)據(jù)集條件大概有三條路線可以選整圖分類輸入一張果圖輸出是“好果/輕微缺陷/嚴(yán)重缺陷”。適合傳送帶上單果逐個(gè)拍照的場(chǎng)景實(shí)現(xiàn)最簡(jiǎn)單。區(qū)域檢測(cè)/分割在圖上定位缺陷區(qū)域輸出缺陷位置的邊界框或像素掩膜。適合需要知道“壞在哪、有多大”的場(chǎng)景比如自動(dòng)化削皮或精準(zhǔn)切割。質(zhì)量評(píng)分回歸輸出一個(gè)連續(xù)的質(zhì)量分?jǐn)?shù)。適合做出口分級(jí)比如按0到100分打分再映射到不同等級(jí)。這三個(gè)切入點(diǎn)不是互斥的。我接觸過的一個(gè)分選項(xiàng)目是先做整圖分類三分類把嚴(yán)重缺陷的果直接淘汰剩下“輕微缺陷”的果再用區(qū)域檢測(cè)找到缺陷位置好讓后端工人快速確認(rèn)能不能降價(jià)售賣。這樣既控制了初期實(shí)現(xiàn)難度又滿足業(yè)務(wù)需要。2. 樣本與標(biāo)注缺陷數(shù)據(jù)比模型更值得投入說句實(shí)在話這類項(xiàng)目能不能成七成取決于數(shù)據(jù)三成才取決于模型。但現(xiàn)實(shí)中大家最不重視的就是數(shù)據(jù)采集。很多項(xiàng)目組拿了一個(gè)公開果蔬數(shù)據(jù)集就開始跑代碼模型在公開數(shù)據(jù)上準(zhǔn)確率很高到了自己產(chǎn)線上連一半都不到。原因很簡(jiǎn)單產(chǎn)線上的光照、相機(jī)角度、品種和公開數(shù)據(jù)集完全不一樣。2.1 采集環(huán)境的幾個(gè)關(guān)鍵設(shè)置采集圖像的目標(biāo)不是“拍得好看”而是“拍得穩(wěn)定”。同一個(gè)類別里光照引起的差異越少模型越好學(xué)。我常用的設(shè)置是這樣的背景選擇深色啞光板純黑或深藍(lán)。這樣后續(xù)做背景分割時(shí)HSV的飽和度通道能非常干凈地把水果和背景分開。光源用漫射光LED面板最好加遮光罩避免水果表面出現(xiàn)鏡面反射。高光的區(qū)域在圖像上是白色一片如果出現(xiàn)在訓(xùn)練集太多模型容易把這部分誤學(xué)成特征。相機(jī)高度和角度固定水果從傳送帶上滾過時(shí)盡量讓果萼/果柄朝向側(cè)面。角度一多變同一個(gè)果子的外觀差異會(huì)非常大增加了不必要的分類難度。分辨率不用太高500萬像素其實(shí)已經(jīng)夠用。更關(guān)鍵的是每顆果在畫面里的占比要基本一致不要讓小果和大果的成像尺寸差很多。采集數(shù)量上我建議至少覆蓋三個(gè)維度品種、成熟度、產(chǎn)地批次。每個(gè)維度下的缺陷形態(tài)都會(huì)有差異如果只采一批果模型上線遇到新批次大概率翻車。2.2 標(biāo)注粒度整圖、框選還是像素級(jí)這一步?jīng)Q定了標(biāo)注成本也決定了后面能做什么。標(biāo)注方式成本能支持的算法適用場(chǎng)景整圖級(jí)低圖像分類大樣本快速摸底矩形框中目標(biāo)檢測(cè)需要定位缺陷位置像素級(jí)掩膜高分割模型需要精確計(jì)算缺陷面積如果項(xiàng)目目標(biāo)是先做分類分選我強(qiáng)烈建議初期先不要做像素級(jí)標(biāo)注。用文件夾結(jié)構(gòu)組織數(shù)據(jù)一個(gè)類別一個(gè)文件夾Matlab的imageDatastore直接讀取半天就能建好一個(gè)可訓(xùn)練的數(shù)據(jù)集。后期如果需要上線區(qū)域檢測(cè)再挑一批樣本做框選或掩膜標(biāo)注。要特別提醒的是標(biāo)注標(biāo)準(zhǔn)的統(tǒng)一。比如“輕微碰傷”和“嚴(yán)重碰傷”誰說了算如果三個(gè)標(biāo)注員標(biāo)準(zhǔn)不一樣模型學(xué)到的東西就會(huì)混亂。我一般會(huì)先寫一版標(biāo)注規(guī)范配幾組典型的參考圖然后讓標(biāo)注人員先標(biāo)50張我來檢查一遍把分歧點(diǎn)全部統(tǒng)一后再擴(kuò)大標(biāo)注規(guī)模。2.3 增強(qiáng)與類別平衡把少量缺陷樣本用出十倍效果缺陷水果的數(shù)據(jù)集天然不平衡好果數(shù)量遠(yuǎn)多于爛果。這很正常因?yàn)檎鎸?shí)產(chǎn)線上合格率可能就是90%以上。問題是如果直接用原始比例訓(xùn)練分類器模型會(huì)把所有樣本都判成好果整體準(zhǔn)確率依然很高但爛果一個(gè)都沒攔住。處理思路有兩個(gè)方向。第一個(gè)是過采樣對(duì)缺陷類做圖像增強(qiáng)讓模型看到更多變換。第二個(gè)是代價(jià)敏感在訓(xùn)練時(shí)給缺陷類的誤判更高的懲罰權(quán)重。Matlab里做增強(qiáng)非常方便augmentedImageDatastore集成了旋轉(zhuǎn)、平移、縮放、反射這些操作。我自己會(huì)額外加這三樣亮度擾動(dòng)±20%模擬不同曝光條件。高斯噪聲模擬傳感器噪聲。隨機(jī)局部遮擋模擬果梗、葉片遮擋的情況。有一點(diǎn)要注意不要過度增強(qiáng)。有一個(gè)常見的失敗案例把圖像旋轉(zhuǎn)90度后蘋果看起來還是蘋果但把長(zhǎng)條形的香蕉旋轉(zhuǎn)成豎直外觀形態(tài)就失真了反而引入了錯(cuò)誤樣本。增強(qiáng)操作必須符合這個(gè)水果品類在真實(shí)分選線上可能出現(xiàn)的姿態(tài)。3. 模型選型小樣本SVM與大樣本CNN的邊界模型選型沒有絕對(duì)的最佳只有合不合適。對(duì)于這種單品種水果的缺陷分類任務(wù)我通常按數(shù)據(jù)集規(guī)模畫一條線初始樣本總數(shù)在幾千張以內(nèi)走特征工程加SVM的路線數(shù)據(jù)量達(dá)到數(shù)萬張或者缺陷形態(tài)特別復(fù)雜再用卷積神經(jīng)網(wǎng)絡(luò)。3.1 特征工程加SVM千級(jí)樣本下的穩(wěn)妥路線傳統(tǒng)機(jī)器學(xué)習(xí)的核心是把圖像變成一組有判別力的數(shù)值特征。對(duì)水果缺陷分類我常用下面這組特征組合顏色特征在HSV空間分別計(jì)算均值、標(biāo)準(zhǔn)差必要時(shí)加入顏色直方圖。HSV比RGB更適合描述光照差異能把“同一顆果在不同亮度下”的差異盡量縮小。紋理特征用灰度共生矩陣GLCM提取對(duì)比度、能量、同質(zhì)性??呐鰝透癄€區(qū)域在紋理上往往是粗糙的和光滑果皮差異明顯。形狀特征面積、周長(zhǎng)、圓形度、主軸長(zhǎng)度比。對(duì)畸形果的識(shí)別特別有效。這組特征計(jì)算量不大維度也不高十幾維到幾十維完全不需要GPU。配合RBF核的SVM在幾百到幾千樣本量下泛化能力強(qiáng)于很多復(fù)雜模型而且訓(xùn)練只需幾秒鐘。為什么這個(gè)組合在中小樣本下比CNN靠譜是因?yàn)槭謩?dòng)特征已經(jīng)先把人類經(jīng)驗(yàn)注入進(jìn)去了。比如我們明確知道顏色和紋理對(duì)缺陷判別很重要就主動(dòng)提取了這兩類信息。而CNN要從零開始學(xué)這兩個(gè)概念需要大量樣本才能學(xué)到數(shù)據(jù)一少就過擬合。3.2 遷移學(xué)習(xí)與CNN什么時(shí)候值得切換當(dāng)條件變成這樣幾個(gè)我建議切換到CNN缺陷類型多且外觀變化大手動(dòng)特征開始不夠用。樣本量已經(jīng)過萬或者能拿到一個(gè)同品類相近的預(yù)訓(xùn)練模型做遷移學(xué)習(xí)。產(chǎn)線光照環(huán)境無法完全控制要求模型對(duì)光照變化有更強(qiáng)的魯棒性。CNN方案在Matlab里通常不是從零開始訓(xùn)練而是用預(yù)訓(xùn)練網(wǎng)絡(luò)做遷移學(xué)習(xí)。把網(wǎng)絡(luò)的最后幾層換掉用果蔬數(shù)據(jù)微調(diào)前面的特征提取層。這樣做的好處是前面卷積層學(xué)到的邊緣、紋理、顏色漸變等基礎(chǔ)視覺特征可以復(fù)用大大降低了對(duì)數(shù)據(jù)量的要求。這里有一種常見的誤解以為遷移學(xué)習(xí)就不用準(zhǔn)備太多數(shù)據(jù)了。實(shí)際上遷移學(xué)習(xí)解決的是“特征提取能力”的起點(diǎn)問題但“缺陷長(zhǎng)什么樣”這件事依然要靠你的數(shù)據(jù)教給它。如果自己的缺陷樣本太少遷移學(xué)習(xí)也一樣過擬合。3.3 為什么用Matlab做這個(gè)項(xiàng)目最順手我在這類項(xiàng)目里用過Python也用過Matlab現(xiàn)在可以坦然地說Matlab不是萬能的但在算法驗(yàn)證階段確實(shí)是最順手的。第一圖像處理工具箱的函數(shù)覆蓋非常完整從讀取圖像、色彩空間轉(zhuǎn)換到形態(tài)學(xué)處理、GLCM特征提取全部是現(xiàn)成函數(shù)不需要東拼西湊裝包。第二從傳統(tǒng)機(jī)器學(xué)習(xí)到深度學(xué)習(xí)兩個(gè)工具箱能無縫銜接。你可以在同一個(gè)腳本里先用SVM做基線再用trainNetwork訓(xùn)練CNN評(píng)估部分的代碼幾乎不用改。第三調(diào)試體驗(yàn)好。矩陣和圖像的變量可以在工作區(qū)直接可視化縮放查看缺陷區(qū)域非常直觀。Python在這一點(diǎn)上差不少往往要寫額外代碼把中間結(jié)果保存成圖片才能看到。第四部署路徑比較清晰。模型訓(xùn)練完之后可以用GPU Coder生成C/CUDA代碼也可以直接調(diào)用導(dǎo)出的模型在嵌入式設(shè)備上跑推理。雖然Python生態(tài)的部署工具多但Matlab這條鏈路在工業(yè)場(chǎng)景里其實(shí)很成熟。4. 核心代碼實(shí)現(xiàn)從預(yù)處理到分類的主干拆解下面這段我盡量說細(xì)一點(diǎn)把每一步的核心邏輯和為什么這么寫講清楚。完整的工程代碼會(huì)多一些但核心骨架就是這幾段。4.1 圖像讀取與背景分割第一步是把圖像里的水果和背景干凈地分開。用深色背景的話HSV空間里背景的飽和度通常很低直接用飽和度閾值就能摳出前景。% 讀取圖像 I imread(apple_sample.jpg); % 轉(zhuǎn)換到HSV空間 hsv rgb2hsv(I); % 提取飽和度通道低飽和度的背景置為0 mask hsv(:,:,2) 0.15; % 形態(tài)學(xué)閉運(yùn)算填充小空洞 mask imclose(mask, strel(disk, 12)); % 填充內(nèi)部孔洞 mask imfill(mask, holes); % 利用掩膜摳出前景 J bsxfun(times, uint8(I), uint8(mask));這里有幾個(gè)容易踩的坑。飽和度閾值不能拍腦袋定要拿產(chǎn)線上實(shí)際拍的照片統(tǒng)計(jì)一下背景和果皮的飽和度分布再定。有的水果表面本身飽和度就不高比如部分黃桃品種閾值設(shè)高了會(huì)把果皮部分也當(dāng)背景摳掉。閉運(yùn)算的結(jié)構(gòu)元素大小也要根據(jù)圖像分辨率調(diào)整圖像大就適當(dāng)加大半徑否則填補(bǔ)不了缺陷區(qū)域造成的空洞。4.2 特征提取函數(shù)設(shè)計(jì)特征提取的核心是把一張圖像壓縮成一串有判別力的數(shù)字。下面這個(gè)函數(shù)我經(jīng)常用包含了顏色矩和GLCM紋理特征。function feat extractFruitFeatures(I) I im2double(I); hsv rgb2hsv(I); % 顏色矩每個(gè)通道的均值和標(biāo)準(zhǔn)差 feat []; for ch 1:3 C hsv(:,:,ch); feat [feat, mean(C(:)), std(C(:))]; end % 灰度共生矩陣紋理特征 gray rgb2gray(I); glcm graycomatrix(gray, ... Offset, [0 1; -1 1; -1 0; -1 -1], ... Sym, true, ... NumLevels, 64); stats graycoprops(glcm, {Contrast, Energy, Homogeneity}); feat [feat, mean(stats.Contrast), mean(stats.Energy), mean(stats.Homogeneity)]; end這段代碼的邏輯是先用HSV空間提取顏色信息因?yàn)镠SV比RGB在光照變化下更穩(wěn)定再對(duì)灰度圖計(jì)算四個(gè)方向上的GLCM把對(duì)比度、能量、同質(zhì)性取平均消除方向敏感性。如果后面發(fā)現(xiàn)特征不夠用可以繼續(xù)加入顏色直方圖、形狀因子等特征維度的擴(kuò)展不影響整體流程。4.3 SVM訓(xùn)練與評(píng)估有了特征提取函數(shù)接下來就是把整個(gè)數(shù)據(jù)集的特征矩陣算出來然后訓(xùn)練SVM。% 使用imageDatastore管理數(shù)據(jù) imds imageDatastore(dataset/, ... IncludeSubfolders, true, ... LabelSource, foldernames); % 劃分訓(xùn)練集和測(cè)試集stratify保證類別比例一致 [imdsTrain, imdsTest] splitEachLabel(imds, 0.7, randomized); % 提取訓(xùn)練集特征 numTrain numel(imdsTrain.Files); trainFeatures zeros(numTrain, 9); for i 1:numTrain I readimage(imdsTrain, i); trainFeatures(i, :) extractFruitFeatures(I); end trainLabels imdsTrain.Labels; % 訓(xùn)練多分類SVMRBF核特征標(biāo)準(zhǔn)化 model fitcecoc(trainFeatures, trainLabels, ... Learners, templateSVM(KernelFunction, rbf, KernelScale, auto), ... Standardize, true); % 提取測(cè)試集特征并預(yù)測(cè) numTest numel(imdsTest.Files); testFeatures zeros(numTest, 9); for i 1:numTest I readimage(imdsTest, i); testFeatures(i, :) extractFruitFeatures(I); end predictions predict(model, testFeatures); % 混淆矩陣 cm confusionchart(imdsTest.Labels, predictions);這段代碼里有三個(gè)細(xì)節(jié)值得展開。一是splitEachLabel默認(rèn)支持按類別比例分層劃分這比隨機(jī)切分更靠譜防止某一類全被分到測(cè)試集里。二是fitcecoc默認(rèn)采用一對(duì)一多類策略對(duì)三分類來說內(nèi)部會(huì)有三組二分類器效果穩(wěn)定。三是特征標(biāo)準(zhǔn)化一定要打開因?yàn)轭伾氐牧考?jí)和GLCM的量級(jí)差別很大不標(biāo)準(zhǔn)化的話SVM會(huì)過分依賴數(shù)值大的特征。4.4 CNN快速實(shí)現(xiàn)的一個(gè)最小方案如果你的數(shù)據(jù)量上來了想切換到CNNMatlab里的代碼也很直白。% 定義網(wǎng)絡(luò)結(jié)構(gòu) layers [ imageInputLayer([100 100 3]) convolution2dLayer(3, 16, Padding, same) batchNormalizationLayer reluLayer maxPooling2dLayer(2, Stride, 2) convolution2dLayer(3, 32, Padding, same) batchNormalizationLayer reluLayer maxPooling2dLayer(2, Stride, 2) fullyConnectedLayer(3) softmaxLayer classificationLayer]; % 數(shù)據(jù)增強(qiáng) imageAugmenter imageDataAugmenter(... RandRotation, [-15 15], ... RandXTranslation, [-10 10], ... RandYTranslation, [-10 10], ... RandXScale, [0.9 1.1], ... RandYScale, [0.9 1.1], ... RandBrightness, [-0.15 0.15]); augmentedImds augmentedImageDatastore([100 100], imdsTrain, ... DataAugmentation, imageAugmenter); % 訓(xùn)練參數(shù)與訓(xùn)練 options trainingOptions(adam, ... InitialLearnRate, 1e-3, ... MaxEpochs, 30, ... MiniBatchSize, 32, ... Plots, training-progress); net trainNetwork(augmentedImds, layers, options);這里網(wǎng)絡(luò)的卷積層參數(shù)我故意設(shè)得很小是因?yàn)槿毕莘诸惾蝿?wù)不需要像ImageNet那樣上千類的大網(wǎng)絡(luò)小網(wǎng)絡(luò)在數(shù)據(jù)有限時(shí)反而泛化更好、訓(xùn)練更快。輸入圖像統(tǒng)一縮放到100x100足以看清局部缺陷。注意這里我用的augmentedImageDatastore只對(duì)訓(xùn)練集做增強(qiáng)測(cè)試集不要增強(qiáng)否則評(píng)估結(jié)果會(huì)失真。4.5 跑通流程后的三條經(jīng)驗(yàn)第一先跑傳統(tǒng)特征加SVM做基線。哪怕你篤定最后用CNN也務(wù)必先訓(xùn)練一個(gè)SVM基線。它能幫你快速判斷數(shù)據(jù)集是否靠譜如果SVM的準(zhǔn)確率都達(dá)到90%以上說明問題本身不難CNN沒必要上如果SVM只有70%可能數(shù)據(jù)量或特征有問題先解決這些問題再上CNN才有效。第二隨時(shí)可視化中間結(jié)果。我習(xí)慣在每個(gè)處理函數(shù)里保留幾張中間圖像的輸出用于確認(rèn)背景分割有沒有把果肉切掉、特征提取是否合理。這種“看一眼中間結(jié)果”的習(xí)慣能省下大量后期調(diào)參的時(shí)間。第三把特征提取函數(shù)設(shè)計(jì)成獨(dú)立模塊。后續(xù)無論是換網(wǎng)絡(luò)還是加特征都不用改主循環(huán)代碼。我見過很多項(xiàng)目把所有邏輯寫在一個(gè)腳本里改一個(gè)參數(shù)要跑半天這是工程習(xí)慣問題。5. 評(píng)估指標(biāo)與閾值調(diào)整別只看準(zhǔn)確率很多初學(xué)者拿到測(cè)試集準(zhǔn)確率90%就歡天喜地這個(gè)心態(tài)在分選項(xiàng)目里是要吃虧的。真實(shí)產(chǎn)線上準(zhǔn)確率是最容易欺騙人的指標(biāo)。5.1 混淆矩陣在不同代價(jià)下的讀法先看一個(gè)典型的混淆矩陣場(chǎng)景。假設(shè)總共1000顆果900顆好果、100顆嚴(yán)重缺陷。分類器把所有果全部判為好果準(zhǔn)確率90%。但這個(gè)分類器在產(chǎn)線上毫無價(jià)值因?yàn)?00顆爛果全部流入市場(chǎng)。更合理的評(píng)估是分別看這三組數(shù)字指標(biāo)計(jì)算方式反映的問題精確率查準(zhǔn)率判為缺陷的果中有多少真的是缺陷誤檢率高低影響好果浪費(fèi)比例召回率查全率真實(shí)缺陷中有多少被攔住漏檢率高低影響壞果流出比例F1分?jǐn)?shù)兩者的調(diào)和平均綜合平衡在分選場(chǎng)景里漏檢和誤檢的成本是不對(duì)稱的。漏檢意味著壞果流向市場(chǎng)可能引發(fā)投訴、品牌受損單次成本可能很高。誤檢意味著好果被丟棄單次成本是這顆果的售價(jià)看起來低但量大了累計(jì)成本也可觀。所以實(shí)際項(xiàng)目里我會(huì)先和業(yè)務(wù)方確認(rèn)他們更在意哪一類錯(cuò)誤。如果更在意爛果不能流出那就優(yōu)先拉高召回率哪怕犧牲一些精確率。這在算法上的實(shí)現(xiàn)方法就是調(diào)整分類閾值。5.2 分類閾值調(diào)優(yōu)的實(shí)際操作SVM或者CNN輸出的是每個(gè)類別的分類得分或概率。默認(rèn)情況下模型會(huì)把樣本判給得分最高的那個(gè)類。我們可以不這么做。具體操作是用fitcecoc的predict輸出里包含得分矩陣或者訓(xùn)練完CNN后用predict輸出各分類的概率。然后我們不再取最大值的索引而是自己設(shè)定一個(gè)閾值只有當(dāng)“嚴(yán)重缺陷”的概率超過某個(gè)值時(shí)才判為嚴(yán)重缺陷否則寧可判成好果讓人工復(fù)檢。% 獲取分類得分而不是硬標(biāo)簽 [~, scores] predict(model, testFeatures); % 假設(shè)類別順序是好果1輕微缺陷2嚴(yán)重缺陷3 % 自定義閾值嚴(yán)重缺陷概率大于0.4才判為嚴(yán)重 severeIdx scores(:, 3) 0.4; finalLabels repmat(好果, size(severeIdx)); finalLabels(scores(:, 2) scores(:, 1) scores(:, 2) scores(:, 3)) 輕微缺陷; finalLabels(severeIdx) 嚴(yán)重缺陷;這個(gè)閾值怎么選定把測(cè)試集上所有樣本的分?jǐn)?shù)畫成直方圖或者畫出ROC曲線找到召回率與精確率的平衡點(diǎn)。通常我會(huì)畫出閾值從0.1到0.9變化時(shí)漏檢率和誤檢率的變化曲線讓業(yè)務(wù)方看著曲線來選閾值。因?yàn)殚撝当举|(zhì)上是商業(yè)決策不應(yīng)該由算法工程師一個(gè)人拍板。5.3 驗(yàn)證集劃分避免同批次數(shù)據(jù)泄露這是最容易踩的一個(gè)坑比模型本身更值得警惕。如果訓(xùn)練集和測(cè)試集里的圖像是從同一批水果、同一光照條件下連續(xù)拍的它們之間高度相似測(cè)試集準(zhǔn)確率會(huì)虛高。一旦換到新批次的水果準(zhǔn)確率會(huì)斷崖式下跌。正確的做法是按“批次”或“采集時(shí)間”來劃分?jǐn)?shù)據(jù)。比如上個(gè)月采的果圖全部用于訓(xùn)練這個(gè)月新采的果圖全部用于測(cè)試。這樣測(cè)試結(jié)果才更接近線上真實(shí)表現(xiàn)。也可以用cvpartition做分層交叉驗(yàn)證但每一折內(nèi)部的樣本仍然可能來自同一批次所以最穩(wěn)妥的還是時(shí)間維度上的前后切分。我在一個(gè)項(xiàng)目里就吃過這個(gè)虧。訓(xùn)練和測(cè)試來自同一批橘子準(zhǔn)確率96%自我感覺很好。結(jié)果換了一批不同產(chǎn)地的橘子準(zhǔn)確率直接掉到82%。后來把所有數(shù)據(jù)按采集日期重新劃分訓(xùn)練集與測(cè)試集準(zhǔn)確率就穩(wěn)定在90%左右。這個(gè)90%雖然比96%低但它才是真實(shí)水平。6. 從實(shí)驗(yàn)室到分選線讓人頭疼的是工程問題模型在電腦上跑通只是第一步真正上線之后你才會(huì)發(fā)現(xiàn)算法本身的難度可能只占30%剩下70%都是工程問題。這里挑三個(gè)最典型的展開說。6.1 推理速度與硬件部署先算一筆時(shí)間賬。假設(shè)分選線傳送帶速度是1米/秒相機(jī)視野覆蓋20厘米那么一顆果子從進(jìn)入視野到離開視野只有0.2秒。如果你的系統(tǒng)還要在判定后控制機(jī)械機(jī)構(gòu)把缺陷果推出去留給算法的時(shí)間還要再砍一半大概100毫秒。而且這100毫秒里包含圖像采集、傳輸、推理、輸出控制信號(hào)的整個(gè)鏈路。SVM方案在這種場(chǎng)景下優(yōu)勢(shì)明顯單張圖像特征提取加預(yù)測(cè)能在CPU上跑到幾十毫秒以內(nèi)。CNN方案就看硬件了在嵌入式設(shè)備上跑小型網(wǎng)絡(luò)也要幾十毫秒再加預(yù)處理時(shí)間就逼近極限。所以如果產(chǎn)線速度很快優(yōu)先選輕量網(wǎng)絡(luò)比如只保留兩三個(gè)卷積層的極小網(wǎng)絡(luò)或者用深度可分離卷積結(jié)構(gòu)推理速度能大幅提升。Matlab的部署鏈路是把訓(xùn)練好的模型導(dǎo)出然后用GPU Coder生成C代碼再交叉編譯到工控機(jī)或嵌入式板卡。我建議在項(xiàng)目一開始就確認(rèn)目標(biāo)硬件不要讓算法在開發(fā)機(jī)上跑得飛快到了產(chǎn)線硬件上才發(fā)現(xiàn)推理時(shí)間嚴(yán)重超標(biāo)。6.2 光源、背景、遮擋產(chǎn)線環(huán)境的隱形變量同一套訓(xùn)練集里的圖片如果是在實(shí)驗(yàn)室環(huán)境拍的到了工廠環(huán)境很容易出現(xiàn)三個(gè)問題。一是環(huán)境光干擾。工廠車間的頂燈、窗戶自然光都會(huì)影響成像要裝遮光罩把相機(jī)和檢測(cè)區(qū)域圍起來保證檢測(cè)區(qū)域的光照只來自可控光源。二是果體表面反光。表面光亮的蘋果和打過蠟的水果會(huì)反射光源形成高光區(qū)塊被模型誤認(rèn)成白色霉斑。解決方法是改用漫射光源或者在預(yù)處理里把高光區(qū)域的像素做校正處理。三是果與果之間的遮擋。如果相機(jī)視野里同時(shí)出現(xiàn)兩顆果邊緣交錯(cuò)背景分割就很容易出錯(cuò)。這種情況要么調(diào)整物理結(jié)構(gòu)讓果子單顆通過要么改用檢測(cè)模型而不是分類模型。這些事不少是到了現(xiàn)場(chǎng)才發(fā)現(xiàn)的。有一次項(xiàng)目調(diào)試時(shí)傳送帶表面有一層淡淡的水漬導(dǎo)致背景分割時(shí)飽和度閾值把部分背景誤當(dāng)成前景間接影響了特征提取的質(zhì)量。調(diào)整后加了除水裝置才恢復(fù)正常。小結(jié)一下任何“環(huán)境變化”都可能影響視覺系統(tǒng)的表現(xiàn)在現(xiàn)場(chǎng)跑之前留出充足的聯(lián)調(diào)時(shí)間。6.3 模型上線后的維護(hù)閉環(huán)模型上線不是結(jié)束而是新一輪數(shù)據(jù)積累的開始。真實(shí)產(chǎn)線上被誤判的樣本是寶貴的訓(xùn)練資源。我建議在分選線后端加一個(gè)復(fù)核環(huán)節(jié)定期把誤檢和漏檢的圖像回收回來由質(zhì)檢人員打標(biāo)簽累積一段時(shí)間后補(bǔ)充進(jìn)訓(xùn)練集做增量訓(xùn)練或遷移學(xué)習(xí)。具體做法是每?jī)芍芑蛘呙吭聦?dǎo)出一批誤判樣本人工復(fù)核后并入原數(shù)據(jù)集重新訓(xùn)練模型并對(duì)比新舊模型在驗(yàn)證集上的表現(xiàn)。如果新模型沒有提升就檢查是不是標(biāo)注出了分歧如果提升了再部署到產(chǎn)線。這個(gè)節(jié)奏不需要太頻繁但是必須堅(jiān)持因?yàn)樾录竟?jié)、新品種的水果外觀變化非常快一個(gè)模型在另一個(gè)產(chǎn)季往往就不適用了。還有一個(gè)容易忽略的細(xì)節(jié)是做好版本管理。模型文件、訓(xùn)練代碼、數(shù)據(jù)集的版本要記錄清楚否則幾個(gè)月后回來看自己當(dāng)初的模型完全不知道當(dāng)時(shí)用的什么參數(shù)、什么數(shù)據(jù)訓(xùn)出來的。我在項(xiàng)目里會(huì)為每個(gè)版本建一個(gè)文件夾包含訓(xùn)練腳本、網(wǎng)絡(luò)結(jié)構(gòu)定義、訓(xùn)練時(shí)的關(guān)鍵指標(biāo)截圖以及模型文件的哈希值。做這類項(xiàng)目我最大的體會(huì)是算法本身不是瓶頸數(shù)據(jù)質(zhì)量、評(píng)估方式和工程細(xì)節(jié)才是。你在Matlab里花一晚上寫出來的分類器可能比很多團(tuán)隊(duì)花一個(gè)月搭出來的深度學(xué)習(xí)框架更實(shí)用。先把數(shù)據(jù)準(zhǔn)備扎實(shí)把評(píng)估指標(biāo)和業(yè)務(wù)目標(biāo)對(duì)應(yīng)起來再用最小的模型跑通整個(gè)流程最后再考慮上復(fù)雜方案這條路是最穩(wěn)的。