省錢利器!基於TensorFlow的端到端驗證碼識別
最近機器學習很熱,作為機器學習在影像識別方面最火的研究領域,神經網路將影像識別帶入了新的高度,很多之前還只能在實驗室中的理想情況下的成果,目前已經大量的運用在了實際生產環境中了,瞭解一些神經網路的知識,不僅能拓寬自己的視野,關鍵時刻這還是一種很好的省錢省力的解決方案。最近在做端到端的識別的研究,剛剛將模型搭建完畢,想著用什麼方式測試一下呢?目前網上最多的就是驗證碼圖片了,隨便一個網站都能拔下來一堆,藉助一些打碼平臺,能很快的完成資料的標註工作,正好把剛剛搭建完成的end-to-end的識別模型在這些驗證碼上一試。
好了背景介紹完了,為了接下來介紹一些原理性的東西,一些基本術語的概念需要先明晰一下:
機器學習解決問題主要是分類問題和迴歸問題
迴歸問題:概括的講就是用於分析兩個變數X和Y之前的關係。希望能找到一個模型來儘量的表示Y=f(X),其中的f就是需要學習的模型。例如X:表示房屋大小,Y:表示房屋價格,當你獲取多組資料以後,就要儘量建模來擬合X和Y之間的關係。
分類問題:根據物體一系列的特徵,通過模型對問題進行分類。例如通過影像的畫素,判斷影像中的物體,這就是典型的影像識別問題。例如根據組人口袋中錢的數量將其分類為高富帥或是屌絲。
分類和迴歸的根本區別就在於分類是已經劃定了最終結果的數量(離散值),而回歸的結果是一串可能的值(連續值)。具體到下面介紹的神經網路,主要是用來解決分類問題。
卷積:卷積就是一種數學運算,主要是用來提取特徵。左側綠色的部分的5×5矩陣就象徵著我們輸入圖片的畫素值,然後上面的3×3黃色部分矩陣就是我們的卷積核,讓卷積核在輸入矩陣上進行從左到右,從上到下滑動,然後每一次滑動,兩個矩陣對應位置就進行內積,對應的結果就轉化為右邊矩陣的一個元素值。
當然進行一次卷積運算就能得到右邊的特徵,要是進行多次呢,當然就提取到多個特徵了,多個特徵以多層(又叫通道或者feature map)的方式表示,提取的特徵越多表達能力就越強,當然也可能越來越陷入過擬合。例如下圖通過三個5×5大小的卷積核,對一張28 × 28畫素的圖片進行卷積操作,最終會得到三個特徵層。
池化:也叫下采樣,通常有Mean pooling(均值取樣)、Max pooling(最大值取樣)、Overlapping (重疊取樣)、L2 pooling(均方取樣)、Local Contrast Normalization(歸一化取樣)、Stochasticpooling(隨即取樣)、Def-pooling(形變約束取樣),主要是用來減少計算量(減少引數個數)和旋轉不變性(抗噪)。其中最經典的Max pooling(最大值取樣)的過程是,對於左邊的4×4大小的畫素塊,通過進行2×2大小的MAX POOLING區域塊,每次取值都取相隔一個POOLING的區域塊中的最大值,最終變為右側2×2大小的畫素塊。這樣引數個數減少了一倍,而且由於是區域的最值,也保留這個區域的特徵,微小的改變不會影響它的結果。
全連線:顧名思義就是輸出的神經元和輸入的每個神經元都連線,如圖左所示,用於將提取到的每一部分的特徵進行彙總。當然這樣的問題就是計算量非常的大,如果我們有1000×1000畫素的影像,有1百萬個隱層神經元,每個隱層神經元都連線影像的每一個畫素點,就有1000x1000x1000000=10^12個連線,也就是10^12個權值引數,可以看到全連線的方式會產生大量的引數,因此一般都是在網路的最後,此時經過卷積和池化的特徵已經比最開始的時候少了很多,此時才進行全連線。減少這些引數的方式是使用區域性連線,每一個節點僅與上層節點同位置附件10×10的視窗相連線,則1百萬個隱層神經元就只有100w乘以100,即10^8個引數。其權值連線個數比原來減少了四個數量級,這就是“稀疏連線”的思想。更進一步的,假如這些連結中的引數都是一樣的呢,那麼就只需要100個引數就好了,比之前的天文數字降低了好多,這就是“權值共享”的思想。這就是上邊卷積操作的由來,因此卷積也能看做是特殊的全連結。這些思想有效的降低了問題規模,使得訓練深度神經網路成為可能。
說到神經網路,尤其是影像識別領域,一個很基礎也是很重要的例子就是MNIST手寫數字識別,他是由60000張訓練資料和10000張測試資料組成的資料集,每張圖片大小為32*32的灰度圖(也就是畫素點用0-255個色階表示)。他就相當於我們學習每種語言的時候,最開始的例子都是Hello Word一樣。學習他的原理可以最快的瞭解神經網路中所用方法的基本知識。
LeNet-5是對MNIST集合學習的重要模型(當年美國大多數銀行就是用它來識別支票上面的手寫數字的。能夠達到這種商用的地步,它的準確性可想而知),這個模型雖然只有七層,但它包含了卷積神經網路中主要的步驟和流程。
C1層:首先對於輸入的32×32大小的圖片,用6個5×5大小的卷積核進行卷積運算,獲取C1層6個28×28大小的特徵圖。
S2層:之後也就是下采樣層,通過MAX POOLING的方式,選取2×2大小的POOLING大小,相當於進行分塊,這樣就得到了14×14個塊的S2了。
C3層:也是類似C1層的卷積操作,不過這16層可不是用16個卷積核,而使用上面6層的組合得來的,這裡就不詳細解釋了,有興趣的朋友可以查一下。
S4層:就是類似S2層的下采樣,這時候我們發現之前的32×32的圖片,經過處理已經變成了16張大小為5×5的特徵圖了。
C5 C6層:之後的就是全連線層還有輸出了,網路會根據提取到的特徵判斷輸入的圖片最可能分成什麼類。
那麼到底他是怎麼利用這些特徵判斷的呢,有一個不太恰當但是利於理解的例子是假如對下圖“0”,從提取到的以下特徵就可以發現,他們組合起來就是一個0。說他不太恰當是因為最終的特徵都是機器自己總結歸納的,到底是不是這樣影像的我們就不得而知了,這裡僅僅是為了易於理解的展示。實際上神經網路進行的就是“編解碼”工作,將訓練樣本的特徵進行抽象的編碼,之後根據提取到的特徵再次嘗試進行解碼還原,學習每一層的引數。這也恰恰是神經網路強大的地方,傳統的做法會去人為的提取影像特徵,像是GIST、HOG、SIFT特徵,這些精心設計的特徵很難去泛化,往往在新的資料集上的效果急劇下降,而卷積神經網路是在大量的資料集上自學習的特徵提取,泛化效能強大。
說了這麼多MNIST的手寫字元的識別,和今天的主題是不是有點跑題啊,其實一點都不跑題,既然已經能準確的識別一個字元了,那麼其實驗證碼就是多個字元的識別,本質是一樣的道理,識別一個字元的時候每次的輸出結果都是一個0-9或者a/A-z/Z的字元,輸出空間為10 + 26 × 2,那麼識別多個字元比如4位的驗證碼呢?實際上就是輸出4個字元對應的樣本空間4 × (10 + 26 × 2)。我們使用Tensorflow這個機器學習的開發框架,Tensorflow是谷歌於2015年開源的一個機器學習的框架,自己開發了這麼好的框架就直接開源了,很佩服google的戰略眼光和技術能力,經過一年時間它也確實不負眾望的成為了git上最火的開源專案。需要識別的驗證碼就如下圖一樣,由於需要預先的訓練,驗證碼的命名方式是“label_uuid”,前面的label用來訓練,後面的uuid避免相同的label不同樣子的圖片命名衝突。
tensorflow中構造的模型程式碼如下(mode.py):
def convolutional_layers():
"""
Get the convolutional layers of the model.
"""
x = tf.placeholder(tf.float32, [None, None, None])
# First layer
W_conv1 = weight_variable([5, 5, 1, 16])
b_conv1 = tf.Variable(tf.zeros([16]))
x_expanded = tf.expand_dims(x, 3)
h_conv1 = tf.nn.relu(conv2d(x_expanded, W_conv1) + b_conv1)
h_pool1 = max_pool(h_conv1, ksize=(2, 2), stride=(2, 2))
# Second layer
W_conv2 = weight_variable([5, 5, 16, 32])
b_conv2 = bias_variable([32])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool(h_conv2, ksize=(2, 2), stride=(2, 2))
# Third layer
W_conv3 = weight_variable([5, 5, 32, 48])
b_conv3 = bias_variable([48])
h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)
h_pool3 = max_pool(h_conv3, ksize=(2, 2), stride=(2, 2))
return x, h_pool3, [W_conv1, b_conv1,
W_conv2, b_conv2,
W_conv3, b_conv3]
def get_model(train=False, ):
x, conv_layer, conv_vars = convolutional_layers()
# Densely connected layer
W_fc1 = weight_variable([48 * 7 * 5, 256])
b_fc1 = bias_variable([256])
conv_layer_flat = tf.reshape(conv_layer, [-1, 48 * 7 * 5])
h_fc1 = tf.nn.relu(tf.matmul(conv_layer_flat, W_fc1) + b_fc1)
if train:
h_fc1 = tf.nn.dropout(h_fc1, 0.9, seed=SEED)
# Output layer
W_fc2 = weight_variable([256, 1 + 4 * len(common.CHARS)])
b_fc2 = bias_variable([1 + 4 * len(common.CHARS)])
y = tf.matmul(h_fc1, W_fc2) + b_fc2
return (x, y, conv_vars + [W_fc1, b_fc1, W_fc2, b_fc2])
前三層(layer)的結構類似於上面LeNet-5前幾層的結構,都是卷積層接著MAX POOLING,主要是用於提取影像的特徵,後兩層也是類似的全連線層,用於組合這些特徵以便獲取結果,最後一層的輸出是1+4 × len(common.CHARS),1表示輸出的“是否”驗證碼圖片,4 × len(common.CHARS)表示的就是上面所說的樣本空間了。返回的資訊中,x指代輸入的內容tensor(Tensorflow中的基本資料結構),y表明最終的預測結果,之後的列表中儲存的是各層中用到的引數,訓練的目的就是學習這些引數的值,一旦完成訓練,就可以通過這些引數直接預測圖片。
用8000多張驗證碼的圖片進行訓練,由於不可避免的少量的標註有錯誤會影響一些最後的效果,最終在500張測試集圖片上驗證的準確率達到了87%,當然隨著訓練資料的增多和網路的進一步優化,這個準確率是可以進一步提升的,雖然沒有達到99%的準確率,但是我們工業上和實驗上一個很重要的區別就是我們能用策略彌補不足之處,加入反覆請求3次的策略,這樣最終的準確率就是(1-0.13×0.13×0.13)×100% =99.7803%,完全達到使用標準,通過圖表可以直觀的對比一下訓練的網路和打碼平臺的效果,可以看到正確率直接上了一個等級。
當然這種型別的驗證碼屬於很好識別的,更復雜的例如google的驗證碼,其中包含了更多的字元還有很多字元的形變和粘連,就需要更大量的訓練樣本和更復雜的網路,目前來看雙向RNN的網路可以通過更少的樣本取得更好的效果,那又是一個CTC的故事,不過鑑於篇幅有限這個留著下次再來分享吧。
總結一下,其實這次的文章僅僅很粗略的介紹了一下神經網路的相關知識,很多關鍵的點如損失函式,啟用函式和權重更新等雖然程式碼中也有所涉及,但是由於篇幅有限都沒有說,還有一些數學公式也沒有列出,希望這次全作為一個拋磚引玉,喜歡的同學能在網上找到很多的類似資料學習,讓我們共同學習,讓機器更加聰明,讓人們更加輕鬆。
更多的公式以及公式的影像表示的學習建議檢視:https://www.zhihu.com/question/31497611?location=35
相關文章
- 2.CNN圖片多標籤分類(基於TensorFlow實現驗證碼識別OCR)CNN
- 驗證碼的前世今生:從圖文識別到無感驗證
- 驗證碼識別
- 關於移動端OCR證件識別的應用
- 手機端身份證識別
- 初探驗證碼識別
- python 驗證碼識別示例(一) 某個網站驗證碼識別Python網站
- tensorflow 訓練 think-captcha 圖片驗證碼自動識別APT
- 使用tensorflow2識別4位驗證碼及思考總結
- 常見驗證碼的弱點與驗證碼識別
- 使用TensorFlow 來實現一個簡單的驗證碼識別過程
- 端到端文字識別CRNN論文解讀RNN
- 驗證碼的識別和運用
- 無密碼驗證:客戶端密碼客戶端
- APP移動端識別行駛證、駕駛證識別技術APP
- 影片直播app原始碼,傳送驗證碼 驗證碼識別APP原始碼
- SMSSDK驗證碼服務端校驗介面服務端
- Python識別網站驗證碼Python網站
- python利用Tesseract識別驗證碼Python
- 帶你讀論文 | 端到端語音識別模型模型
- Java 基於ArcFace人臉識別2.0 服務端DemoJava服務端
- 專案部署到centos7雲端驗證碼出現亂碼CentOS
- 基於CFSSL工具建立CA證書,服務端證書,客戶端證書服務端客戶端
- 【技術向】基於jarm的Tor中繼節點遠端識別JAR中繼
- 使用tensorflow和cnn(卷積神經網路)識別驗證碼並構建APICNN卷積神經網路API
- 語音識別新正規化:完全的“端到端”模型,優勢在哪裡?模型
- 【全網最高識別率】國稅局驗證碼識別
- 簡單幾步實現滑動驗證碼(後端驗證)後端
- Kubernetes客戶端認證(二)—— 基於ServiceAccount的JWTToken認證客戶端JWT
- Web 端 實現 app “輸入驗證碼 ”的效果WebAPP
- 基於c語言的TCP客戶端、服務端基礎程式碼C語言TCP客戶端服務端
- 使用 Ruby 識別英文數字驗證碼
- 使用 Swift 識別英文數字驗證碼Swift
- 使用 OCaml 識別英文數字驗證碼
- 神器!使用Python 輕鬆識別驗證碼Python
- 機器視覺以及驗證碼識別視覺
- 識別英文數字驗證碼的程式(Python示例)Python
- 爬蟲遇到頭疼的驗證碼?教你彈窗處理和驗證碼識別爬蟲