專案簡介
在之前的文章
keras入門(三)搭建CNN模型破解網站驗證碼
中,筆者介紹介紹瞭如何用Keras來搭建CNN模型來破解網站的驗證碼,其中驗證碼含有字母和數字。
讓我們一起回顧一下
keras入門(三)搭建CNN模型破解網站驗證碼
的處理思路:
利用OpenCV對影象進行單個字元的切割,大概400多張圖片;
對切割好的單個字元進行人工手動標記;
搭建合適的CNN模型,對標記好的資料集進行訓練;
對於新的驗證碼,先切割單個字元,再對單個字元進行預測,組成總的預測結果。
這一次,筆者將會換種思路,使用CNN模型來破解網站的驗證碼。我們的資料集如下:
驗證碼資料集
一共是946張圖片,這裡只展示了一部分,可以看到,這些驗證碼全部由數字組成。那麼,新的破解驗證碼的思路是什麼呢?如下:
直接對驗證碼進行標記,標記的結果見上圖;
搭建合適的CNN模型對標記好的資料集進行訓練;
對新驗證碼進行預測。
這種思路的好處是,不需要對驗證碼進行繁瑣的預處理,只需要簡單的資料標記即可。
下面,筆者將會具體展示這個過程。
資料標記
資料標記絕對是個累活,當我想到要對946張圖片進行標記並重命名,而且還要保證標記的準確性的時候,我開始是有點拒絕的心態,畢竟這項工作費時費力,而且能不能保證識別的效果還是個未知數。
就這麼糾結了一段時間,原本年前就想做的專案一直拖到了現在,後來我想,能不能寫個指令碼,能夠幫助我快速地進行資料標註,並自動儲存呢?這麼想著,我就動手自己做了一個由Tornado實現的前端頁面,可以幫助我快速地標記資料並儲存圖片,頁面如下:
資料標註介面
介面雖然簡陋,卻能幫助我很好地提升資料標記的速度,只需要在value文字框中輸入自己識別的結果,程式就能自動儲存標記好的圖片,並切換至下一張未標記的驗證碼。有了如此好的工具,結果我用了不到一小時就標記完了這946張驗證碼(其實是1000張,因為標記好的結果會有重複)。有機會筆者會介紹這個驗證碼標記的專案~
模型訓練
標記完驗證碼後,我們就利用這946張驗證碼作為訓練資料,訓練CNN模型。我們使用Keras框架,CNN模型的結構圖如下:
模型結構圖
模型訓練的Python程式碼如下:
# CNN模型訓練# -*- coding: utf-8 -*-import cv2import osimport randomimport numpy as npfrom keras。models import *from keras。layers import *from keras import callbackscharacters = ‘0123456789’width, height, n_len, n_class = 50, 22, 4, 10# 產生訓練的一批圖片,預設是32張圖片def gen(dir, batch_size=32): X = np。zeros((batch_size, height, width, 3), dtype=np。uint8) y = [np。zeros((batch_size, n_class), dtype=np。uint8) for _ in range(n_len)] files = os。listdir(dir) while True: for i in range(batch_size): path = random。choice(files) imagePixel = cv2。imread(dir+‘/’+path, 1) filename = path[:4] X[i] = imagePixel for j, ch in enumerate(filename): y[j][i, :] = 0 y[j][i, characters。find(ch)] = 1 yield X, yinput_tensor = Input((height, width, 3))x = input_tensor# 產生有四個block的卷積神經網路for i in range(4): # 卷積層 x = Conv2D(32 * 2 ** i, (3, 3), activation=‘relu’, padding=‘same’)(x) x = Conv2D(32 * 2 ** i, (3, 3), activation=‘relu’, padding=‘same’)(x) # 池化層 x = MaxPooling2D((2, 2))(x)x = Flatten()(x)x = Dropout(0。25)(x)# 多輸出模型,使用了4個‘softmax’來分別預測4個字母的輸出x = [Dense(n_class, activation=‘softmax’, name=‘c%d’ % (i + 1))(x) for i in range(4)]model = Model(inputs=input_tensor, outputs=x)model。summary()# 儲存模型結構圖from keras。utils。vis_utils import plot_modelplot_model(model, to_file=“。/model。png”, show_shapes=True)model。compile(loss=‘categorical_crossentropy’, optimizer=‘adadelta’, metrics=[‘accuracy’])# 儲存效果最好的模型cbks = [callbacks。ModelCheckpoint(“best_model。h5”, save_best_only=True)]dir = ‘。/result’history = model。fit_generator(gen(dir, batch_size=8), # 每次生成器會產生8張小批次的圖片 steps_per_epoch=120, # 每次的epoch要訓練120批圖片 epochs=50, # 總共訓練50次 callbacks=cbks, # 儲存最好的模型 validation_data=gen(dir), # 驗證資料也是用生成器來產生 validation_steps=10 # 用10組圖片來進行驗證 )# 繪製損失值影象import matplotlib。pyplot as pltdef plot_train_history(history, train_metrics, val_metrics): plt。plot(history。history。get(train_metrics), ‘-o’) plt。plot(history。history。get(val_metrics), ‘-o’) plt。ylabel(train_metrics) plt。xlabel(‘Epochs’) plt。legend([‘train’, ‘validation’])# 列印整體的loss與val_loss,並儲存圖片plot_train_history(history, ‘loss’, ‘val_loss’)plt。savefig(‘。/all_loss。png’)plt。figure(figsize=(12, 4))# 第一個數字的正確率plt。subplot(2, 2, 1)plot_train_history(history, ‘c1_acc’, ‘val_c1_acc’)# 第二個數字的正確率plt。subplot(2, 2, 2)plot_train_history(history, ‘c2_acc’, ‘val_c2_acc’)# 第三個數字的正確率plt。subplot(2, 2, 3)plot_train_history(history, ‘c3_acc’, ‘val_c3_acc’)# 第四個數字的正確率plt。subplot(2, 2, 4)plot_train_history(history, ‘c4_acc’, ‘val_c4_acc’)# 儲存圖片plt。savefig(‘。/train。png’)
在這個程式碼中,我們總共訓練了50個epoch,每個epoch共120次批次,每個批次是8張驗證碼,每張驗證碼的大小為50*22。
執行該訓練模型,後幾個epoch的輸出結果如下:
總的損失值影象如下:
總的損失值影象
四個數字每個數字的損失值影象如下:
四個數字每個數字的損失值影象
訓練完後,程式會將訓練效果最好的epoch儲存為best_model。h5檔案,便於後續的模型預測。由輸出的結果及影象來看,該CNN模型的訓練效果應該是相當好的,下面,我們來看看對新驗證碼的預測效果。
模型預測
新的驗證碼共有20張,如下:
新的驗證碼
模型預測的Python程式碼如下:
# 使用訓練好的CNN模型對新圖片進行預測# -*- coding: utf-8 -*-from keras。models import load_modelimport cv2import numpy as np# 匯入訓練好的模型model = load_model(‘best_model。h5’)batch_size = 20width, height, n_len, n_class = 50, 22, 4, 10# 匯入驗證碼資料並進行預測X = np。zeros((batch_size, height, width, 3), dtype=np。uint8)for i in range(batch_size): X_test = cv2。imread(‘。/new_image/code%d。png’ %(i+1), 1) X[i] = X_testy_pred = model。predict(X)y_pred = np。argmax(y_pred, axis=2)# 輸出每張驗證碼的預測結果for i in range(batch_size): print(‘第%d張驗證碼的識別結果為:’ %(i+1), end=‘’) print(‘’。join(map(str, y_pred[:, i]。tolist())))
執行該模型,得到的輸出結果如下:
第1張驗證碼的識別結果為:3568第2張驗證碼的識別結果為:5402第3張驗證碼的識別結果為:6051第4張驗證碼的識別結果為:6769第5張驗證碼的識別結果為:2675第6張驗證碼的識別結果為:2450第7張驗證碼的識別結果為:2364第8張驗證碼的識別結果為:6879第9張驗證碼的識別結果為:3702第10張驗證碼的識別結果為:3459第11張驗證碼的識別結果為:5895第12張驗證碼的識別結果為:8042第13張驗證碼的識別結果為:6897第14張驗證碼的識別結果為:6558第15張驗證碼的識別結果為:9428第16張驗證碼的識別結果為:5662第17張驗證碼的識別結果為:5431第18張驗證碼的識別結果為:4981第19張驗證碼的識別結果為:0567第20張驗證碼的識別結果為:5239
對這20張新的驗證碼,預測完全正確!不得不說,CNN模型的識別效果非常好!
總結
本文采用了一種新的思路,搭建CNN模型來實現驗證碼的識別,取得了不錯的識別效果,而且識別的驗證碼是從網頁中下載下來的,具有實際背景,增強了該專案的應用性。
本專案已放至Github,地址為:https://github。com/percent4/CAPTCHA-Recognizition 。
注意:本人現已開通微信公眾號: NLP奇幻之旅(微訊號為:easy_web_scrape), 歡迎大家關注哦~~