(原英文鏈接:http://matthewearl.github.io/2016/05/06/cnn-anpr/)
簡(jiǎn)介
過(guò)去幾周我一直在涉足深度學(xué)習(xí)領(lǐng)域炼绘,尤其是卷積神經(jīng)網(wǎng)絡(luò)模型。最近杨何,谷歌圍繞街景多位數(shù)字識(shí)別技術(shù)發(fā)布了一篇不錯(cuò)的paper。該文章描述了一個(gè)用于提取街景門(mén)牌號(hào)的單個(gè)端到端神經(jīng)網(wǎng)絡(luò)系統(tǒng)沥邻。然后危虱,作者闡述了基于同樣的網(wǎng)絡(luò)結(jié)構(gòu)如何來(lái)突破谷歌驗(yàn)證碼識(shí)別系統(tǒng)的準(zhǔn)確率。
為了親身體驗(yàn)神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)唐全,我決定嘗試設(shè)計(jì)一個(gè)可以解決類(lèi)似問(wèn)題的系統(tǒng):車(chē)牌號(hào)自動(dòng)識(shí)別系統(tǒng)埃跷。設(shè)計(jì)這樣一個(gè)系統(tǒng)的原因有3點(diǎn):
- 我應(yīng)該能夠參照谷歌那篇paper搭建一個(gè)同樣的或者類(lèi)似的網(wǎng)絡(luò)架構(gòu):谷歌提供的那個(gè)網(wǎng)絡(luò)架構(gòu)在驗(yàn)證碼識(shí)別上相當(dāng)不錯(cuò),那么講道理的話(huà)邮利,用它來(lái)識(shí)別車(chē)牌號(hào)應(yīng)該也會(huì)很給力弥雹。擁有一個(gè)知名的網(wǎng)絡(luò)架構(gòu)將會(huì)大大地簡(jiǎn)化我學(xué)習(xí)CNNs的步驟。
- 我可以很容易地生成訓(xùn)練數(shù)據(jù)近弟。訓(xùn)練神經(jīng)網(wǎng)絡(luò)存在一個(gè)很大的問(wèn)題就是需要大量的標(biāo)簽樣本缅糟。通常要訓(xùn)練好一個(gè)網(wǎng)絡(luò)就需要幾十萬(wàn)張標(biāo)記過(guò)的圖片。僥幸的是祷愉,由于UK車(chē)牌號(hào)相對(duì)一致,所以我可以合成訓(xùn)練數(shù)據(jù)赦颇。
- 好奇心二鳄。傳統(tǒng)的車(chē)牌號(hào)自動(dòng)識(shí)別系統(tǒng)依賴(lài)于自己編寫(xiě)算法來(lái)實(shí)現(xiàn)車(chē)牌定位,標(biāo)準(zhǔn)化媒怯,分割和字符識(shí)別等功能订讼。照這樣的話(huà),實(shí)現(xiàn)這些系統(tǒng)的代碼可能達(dá)到上千行扇苞。然而欺殿,我比較感興趣的是,如何使用相對(duì)較少的代碼和最少的專(zhuān)業(yè)領(lǐng)域知識(shí)來(lái)開(kāi)發(fā)一個(gè)不錯(cuò)的系統(tǒng)鳖敷。
開(kāi)發(fā)該項(xiàng)目的環(huán)境要求有Python,Tensorflow,OpenCV和NumPy等軟件脖苏。源代碼在這里。
輸入定踱,輸出和滑窗
為了簡(jiǎn)化生成的訓(xùn)練圖片棍潘,減少計(jì)算量,我決定該網(wǎng)絡(luò)可操作的輸入圖片為128*64的灰度圖崖媚。
選用128*64分辨率的圖片作為輸入亦歉,對(duì)于基于適當(dāng)?shù)馁Y源和合理的時(shí)間訓(xùn)練來(lái)說(shuō)足夠小,對(duì)于車(chē)牌號(hào)讀取來(lái)說(shuō)也足夠大畅哑。
為了在更大的圖片中檢測(cè)車(chē)牌號(hào)肴楷,采用了一個(gè)多尺度的滑窗來(lái)解決。
右邊的圖片是神經(jīng)網(wǎng)絡(luò)的輸入圖片荠呐,大小為128*64赛蔫,而左邊的圖片則展示了在原始輸入圖片的上下文中的滑窗绷杜。
對(duì)于每個(gè)滑窗,網(wǎng)絡(luò)都會(huì)輸出:
- 輸入圖片中存在車(chē)牌的概率濒募。(上邊動(dòng)畫(huà)所顯示的綠框)
- 每個(gè)位置上的字符的概率鞭盟,比如針對(duì)7個(gè)可能位置中的每一個(gè)位置,網(wǎng)絡(luò)都應(yīng)該返回一個(gè)貫穿36個(gè)可能的字符的概率分布瑰剃。(在這個(gè)項(xiàng)目中我假定車(chē)牌號(hào)恰好有7位字符齿诉,UK車(chē)牌號(hào)通常都這樣)
考慮一個(gè)車(chē)牌存在當(dāng)且僅當(dāng):
- 車(chē)牌完全包含在圖片邊界內(nèi)。
- 車(chē)牌的寬度小于圖片寬度的80%晌姚,且車(chē)牌的高度小于圖片高度的87.5%粤剧。
- 車(chē)牌的寬度大于圖片寬度的60%,或車(chē)牌的高度大于圖片高度的60%挥唠。
為了檢測(cè)這些號(hào)碼抵恋,我們可以利用一個(gè)滑窗,每次滑動(dòng)8個(gè)像素宝磨,而且在保證不丟失車(chē)牌的情況下提供一個(gè)縮放等級(jí)弧关,縮放系數(shù)為$\sqrt{2}$,同時(shí)對(duì)于任何單個(gè)的車(chē)牌不會(huì)生成過(guò)量的匹配框唤锉。在后處理過(guò)程中會(huì)做一些復(fù)本(稍后解釋?zhuān)?/p>
合成圖片
為了訓(xùn)練任何一個(gè)神經(jīng)網(wǎng)絡(luò)世囊,必須提供一套擁有正確輸出的訓(xùn)練數(shù)據(jù)。在這里表現(xiàn)為一套擁有期望輸出的128*64大小的圖片窿祥。這里給出一個(gè)本項(xiàng)目生成的訓(xùn)練數(shù)據(jù)的實(shí)例:
-
期望輸出 HH41RFP 1株憾。
-
期望輸出 FB78PFD 1。
-
期望輸出 JW01GAI 0晒衩。(車(chē)牌部分截?cái)啵?/p>
-
期望輸出 AM46KVG 0嗤瞎。(車(chē)牌太小)
-
期望輸出 XG86KIO 0听系。(車(chē)牌太大)
-
期望輸出 XH07NYO 0贝奇。(車(chē)牌不存在)
期望輸出的第一部分表示網(wǎng)絡(luò)應(yīng)該輸出的號(hào)碼,第二部分表示網(wǎng)絡(luò)應(yīng)該輸出的“存在”值跛锌。對(duì)于標(biāo)記過(guò)的數(shù)據(jù)不存在的情況我在括號(hào)里作了解釋弃秆。
生成圖片的過(guò)程如下圖所示:
文本和車(chē)牌的顏色是隨機(jī)選擇的,但是文本顏色必須比車(chē)牌顏色更深一些髓帽。這是為了模擬真實(shí)場(chǎng)景的光線(xiàn)變化菠赚。最后再加入一些噪音,這樣不僅能夠解釋真實(shí)傳感器的噪音郑藏,而且能夠避免過(guò)多依賴(lài)于銳化的輪廓邊界而看到的將會(huì)是離焦的輸入圖片衡查。
擁有背景是很重要的,這意味著網(wǎng)絡(luò)必須學(xué)習(xí)分辨沒(méi)有“欺騙”的車(chē)牌號(hào)邊界:使用一個(gè)黑色背景為例必盖,網(wǎng)絡(luò)可能會(huì)基于非黑色來(lái)學(xué)習(xí)分辨車(chē)牌的位置拌牲,這會(huì)導(dǎo)致分不清楚真實(shí)圖片里的小汽車(chē)俱饿。
背景圖片來(lái)源于SUN database,里面包含了超過(guò)10萬(wàn)張圖片塌忽。重要的是大量的圖片可以避免網(wǎng)絡(luò)“記住”背景圖片拍埠。
車(chē)牌變換采用了一種基于隨機(jī)滾轉(zhuǎn)、傾斜土居、偏轉(zhuǎn)枣购、平移以及縮放的仿射變換。每個(gè)參數(shù)允許的范圍是車(chē)牌號(hào)可能被看到的所有情況的集合擦耀。比如棉圈,偏轉(zhuǎn)比滾轉(zhuǎn)允許變化更多(你更可能看到一輛汽車(chē)在拐彎而不是翻轉(zhuǎn)到一邊)。
生成圖片的代碼相對(duì)較短(大約300行)眷蜓》竹可以從gen.py里讀取。
網(wǎng)絡(luò)結(jié)構(gòu)
使用的網(wǎng)絡(luò)結(jié)構(gòu)如下圖所示:
通過(guò)維基百科可以查看CNN模塊的介紹吁系。上面的網(wǎng)絡(luò)結(jié)構(gòu)實(shí)際上是基于Stark的這篇paper德召,關(guān)于這個(gè)結(jié)構(gòu)它比谷歌的那篇paper給出了更多的細(xì)節(jié)。
輸出層有一個(gè)節(jié)點(diǎn)(左邊)被用來(lái)作為車(chē)牌是否存在的指示器垮抗。剩下的節(jié)點(diǎn)用來(lái)編碼一個(gè)特定車(chē)牌號(hào)的概率:圖中的每一列與車(chē)牌號(hào)中的每一位號(hào)碼一致氏捞,每一個(gè)節(jié)點(diǎn)給出與存在的字符相符合的概率。例如冒版,位于第2列第3行的節(jié)點(diǎn)給出車(chē)牌號(hào)中第二個(gè)號(hào)碼是字符c的概率。
除了輸出層使用ReLU激活函數(shù)之外逞姿,所有層都采用深度神經(jīng)網(wǎng)絡(luò)的標(biāo)準(zhǔn)結(jié)構(gòu)辞嗡。指示存在的節(jié)點(diǎn)使用sigmoid激活函數(shù),典型地用于二值輸出滞造。其他輸出節(jié)點(diǎn)使用softmax貫穿字符(結(jié)果是每一列的概率之和為1)续室,是模型化離散概率分布的標(biāo)準(zhǔn)方法。
定義網(wǎng)絡(luò)結(jié)構(gòu)的代碼在model.py里谒养。
根據(jù)標(biāo)簽和網(wǎng)絡(luò)輸出的交叉熵來(lái)定義損失函數(shù)挺狰。為了數(shù)值穩(wěn)定性,利用softmax_cross_entropy_with_logits和sigmoid_cross_entropy_with_logits將最后一層的激活函數(shù)卷入交叉熵的計(jì)算买窟。關(guān)于對(duì)交叉熵詳細(xì)而直觀的介紹可以參考Michael A. Nielsen的free online book中查看這一節(jié)丰泊。
使用一塊nVidia GTX 970花費(fèi)大約6小時(shí)來(lái)訓(xùn)練(train.py),通過(guò)CPU的一個(gè)后臺(tái)進(jìn)程來(lái)運(yùn)行訓(xùn)練數(shù)據(jù)的生成始绍。
輸出處理
事實(shí)上為了從輸入圖片中檢測(cè)和識(shí)別車(chē)牌號(hào)瞳购,搭建了類(lèi)似于上面的一個(gè)檢測(cè)網(wǎng)絡(luò),并采用了多位置和多尺度的128*64滑窗亏推,這在滑窗那一節(jié)有所描述学赛。
檢測(cè)網(wǎng)絡(luò)和訓(xùn)練網(wǎng)絡(luò)的不同點(diǎn)在于最后兩層采用了卷積層而不是全連接層年堆,這樣可以使檢測(cè)網(wǎng)絡(luò)的輸入圖片大小不僅限于128*64。將一張完整的圖片以一種特定尺寸扔進(jìn)網(wǎng)絡(luò)中盏浇,然后返回一張每個(gè)“像素”擁有一個(gè)存在/字符概率值的圖片变丧。因?yàn)橄噜彽幕皶?huì)共享很多卷積特征,所以將這些特定圖片卷進(jìn)同一個(gè)網(wǎng)絡(luò)可以避免多次計(jì)算同樣的特征绢掰。
可視化輸出的“存在”部分會(huì)返回如下所示的圖片:
圖上的邊界框是網(wǎng)絡(luò)檢測(cè)存在車(chē)牌概率大于99%的區(qū)域痒蓬。設(shè)置高閾值的原因是為了解釋訓(xùn)練過(guò)程中引進(jìn)的一個(gè)偏差:幾乎過(guò)半的訓(xùn)練圖片都包含一個(gè)車(chē)牌,然而真實(shí)場(chǎng)景中有車(chē)牌的圖片很少見(jiàn)曼月,所以如果設(shè)置閾值為50%的話(huà)谊却,那么檢測(cè)網(wǎng)絡(luò)的假陽(yáng)性就會(huì)偏高。
在檢測(cè)網(wǎng)絡(luò)輸出之后哑芹,我們使用非極大值抑制(NMS)的方法來(lái)過(guò)濾掉冗余的邊界框:
首先將重疊的矩形框分組炎辨,然后針對(duì)每一組輸出:
- 所有邊界框的交集。
- 找出組中車(chē)牌存在概率最高的邊界框?qū)?yīng)的車(chē)牌號(hào)聪姿。
下圖所示文章最開(kāi)始給出的那張車(chē)牌圖片的檢測(cè)結(jié)果:
哎呦碴萧,字符R被誤檢成了P。上圖中車(chē)牌存在概率最大的滑窗如下圖所示:
第一眼似乎以為這個(gè)對(duì)于檢測(cè)器來(lái)說(shuō)是小菜一碟末购,然而事實(shí)證明這是過(guò)擬合的問(wèn)題破喻。下圖給出了生成訓(xùn)練圖片時(shí)所用的車(chē)牌號(hào)中R的字體:
注意字符R腿的角度是如何不同于輸入圖片中字符R腿的角度。由于網(wǎng)絡(luò)僅僅學(xué)習(xí)過(guò)上面的那種R字體盟榴,因此當(dāng)遇到不同字體的R字符時(shí)就迷惑了曹质。為了測(cè)試這種假設(shè),我在GIMP中改進(jìn)了圖片擎场,使得其更接近于訓(xùn)練時(shí)的字體:
改進(jìn)之后羽德,檢測(cè)得到了正確的輸出:
檢測(cè)的源碼在這里detect.py
總結(jié)
我已經(jīng)開(kāi)源了一個(gè)擁有相對(duì)較短代碼(大約800行)的系統(tǒng),它不用導(dǎo)入任何特定領(lǐng)域的庫(kù)以及不需要太多特定領(lǐng)域的知識(shí)迅办,就能夠?qū)崿F(xiàn)車(chē)牌號(hào)自動(dòng)識(shí)別宅静。此外,我還通過(guò)在線(xiàn)合成圖片的方法解決了上千張訓(xùn)練圖片的需求問(wèn)題(通常是在深度神經(jīng)網(wǎng)絡(luò)的情況下)站欺。
另一方面姨夹,我的系統(tǒng)也存在一些缺點(diǎn):
- 只適用于特定車(chē)牌號(hào)。尤其是矾策,網(wǎng)絡(luò)結(jié)構(gòu)明確假定了輸出只有7個(gè)字符磷账。
- 只適用于特定字體。
- 速度太慢蝴韭。該系統(tǒng)運(yùn)行一張適當(dāng)尺寸的圖片要花費(fèi)幾秒鐘够颠。
為了解決第1個(gè)問(wèn)題,谷歌團(tuán)隊(duì)將他們的網(wǎng)絡(luò)結(jié)構(gòu)的高層拆分成了多個(gè)子網(wǎng)絡(luò)榄鉴,每一個(gè)子網(wǎng)絡(luò)用于假定輸出號(hào)碼中的不同號(hào)碼位履磨。還有一個(gè)并行的子網(wǎng)絡(luò)來(lái)決定存在多少號(hào)碼蛉抓。我覺(jué)得這種方法可以應(yīng)用到這兒,但是我沒(méi)有在這個(gè)項(xiàng)目中實(shí)現(xiàn)剃诅。
關(guān)于第2點(diǎn)我在上面舉過(guò)例子巷送,由于字體的稍微不同而導(dǎo)致字符R的誤檢。如果嘗試著檢測(cè)US車(chē)牌號(hào)的話(huà)矛辕,誤檢將會(huì)更加嚴(yán)重笑跛,因?yàn)閁S車(chē)牌號(hào)字體類(lèi)型更多。一個(gè)可能的解決方案就是使得訓(xùn)練數(shù)據(jù)有更多不同的字體類(lèi)型可選擇聊品,盡管還不清楚需要多少字體類(lèi)型才能成功飞蹂。
第3點(diǎn)提到的速度慢的問(wèn)題是扼殺許多應(yīng)用的cancer:在一個(gè)相當(dāng)強(qiáng)大的GPU上處理一張適當(dāng)尺寸的輸入圖片就要花費(fèi)幾秒鐘。我認(rèn)為不引進(jìn)一種級(jí)聯(lián)式結(jié)構(gòu)的檢測(cè)網(wǎng)絡(luò)就想避開(kāi)這個(gè)問(wèn)題是不太可能的翻屈,比如Haar級(jí)聯(lián)陈哑,HOG檢測(cè)器,或者一個(gè)更簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)伸眶。
我很有興趣去嘗試和其他機(jī)器學(xué)習(xí)方法的比較會(huì)怎樣惊窖,特別是姿態(tài)回歸看起來(lái)有希望,最后可能會(huì)附加一個(gè)最基本的分類(lèi)階段厘贼。如果使用了像scikit-learn這樣的機(jī)器學(xué)習(xí)庫(kù)界酒,那么應(yīng)該同樣簡(jiǎn)單。
總之嘴秸,我使用單個(gè)CNN網(wǎng)絡(luò)實(shí)現(xiàn)了一個(gè)車(chē)牌號(hào)檢測(cè)器/識(shí)別器毁欣,然而從性能方面來(lái)講,它還不能與傳統(tǒng)的手工(但更繁瑣的)管道線(xiàn)一較高下岳掐。