使用深度學習的CNN神經網路破解Captcha驗證碼
樣本資料的生成與處理:
我們先來看看mnist(一個手寫體數字圖片的資料集,有55000張0-9的手寫體數字的圖片)中圖片和圖片標籤的資料形式:
已知mnist資料集中的每張圖片是28x28畫素的灰度影象。每張圖片上是一個單獨的手寫體數字。
程式碼如下:
from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) print(mnist.train.images[0]) print(mnist.train.images[0].shape) print(mnist.train.labels[0])
執行結果如下:
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.3803922 0.37647063 0.3019608 0.46274513 0.2392157 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.3529412 0.5411765 0.9215687 0.9215687 0.9215687 0.9215687 0.9215687 0.9215687 0.9843138 0.9843138 0.9725491 0.9960785 0.9607844 0.9215687 0.74509805 0.08235294 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.54901963 0.9843138 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.7411765 0.09019608 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.8862746 0.9960785 0.81568635 0.7803922 0.7803922 0.7803922 0.7803922 0.54509807 0.2392157 0.2392157 0.2392157 0.2392157 0.2392157 0.5019608 0.8705883 0.9960785 0.9960785 0.7411765 0.08235294 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.14901961 0.32156864 0.0509804 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.13333334 0.8352942 0.9960785 0.9960785 0.45098042 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.32941177 0.9960785 0.9960785 0.9176471 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.32941177 0.9960785 0.9960785 0.9176471 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.4156863 0.6156863 0.9960785 0.9960785 0.95294124 0.20000002 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.09803922 0.45882356 0.8941177 0.8941177 0.8941177 0.9921569 0.9960785 0.9960785 0.9960785 0.9960785 0.94117653 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.26666668 0.4666667 0.86274517 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.9960785 0.5568628 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.14509805 0.73333335 0.9921569 0.9960785 0.9960785 0.9960785 0.8745099 0.8078432 0.8078432 0.29411766 0.26666668 0.8431373 0.9960785 0.9960785 0.45882356 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.4431373 0.8588236 0.9960785 0.9490197 0.89019614 0.45098042 0.34901962 0.12156864 0. 0. 0. 0. 0.7843138 0.9960785 0.9450981 0.16078432 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.6627451 0.9960785 0.6901961 0.24313727 0. 0. 0. 0. 0. 0. 0. 0.18823531 0.9058824 0.9960785 0.9176471 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.07058824 0.48627454 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.32941177 0.9960785 0.9960785 0.6509804 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.54509807 0.9960785 0.9333334 0.22352943 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.8235295 0.9803922 0.9960785 0.65882355 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.9490197 0.9960785 0.93725497 0.22352943 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.34901962 0.9843138 0.9450981 0.3372549 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.01960784 0.8078432 0.96470594 0.6156863 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.01568628 0.45882356 0.27058825 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. ] (784,) [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.] Process finished with exit code 0
如上所示,我們可以發現在mnist資料集中儲存的並不是通常意義下的.jpg的圖片格式,而是一張圖片對應一個784個元素的一維陣列,元素的個數恰好等於畫素點的個數,即一個元素的值就是一個畫素點的灰度值(.jpg格式的灰度圖片上的R、G、B三個通道上的值是相等的)。
再看下面的標籤,標籤是一個有10個元素的陣列,元素的數量是10對應著資料集中只有0-9十種數字。mnist資料集的標籤採用獨熱編碼(one-hot),即一張圖片代表數字幾,則標籤中對應下標的元素值是1,而其他元素的值是0。從上面的結果中我們可以看出mnist.train.images[0]這張圖片代表數字7。
我們先利用Captcha庫的自帶函式生成驗證碼圖片,仿照mnist的圖片和標籤的資料形式來處理該圖片的資料,生成可以直接輸入CNN網路模型訓練的圖片和標籤資料。
由於彩色圖片轉換成灰度圖片再輸入CNN網路進行識別時幾乎不影響識別準確率,但灰度圖片的資料處理量只有彩色圖片的三分之一,因此我們需要將Captcha庫生成的彩色圖片轉換成灰度圖片。
由於十個數字+大小寫字母的候選字符集太大了,在訓練模型時會需要很長的時間才能得到比較滿意的結果,因此我實際生成驗證碼時只從0-9數字中隨機挑選。
程式碼如下:
CaptchaGenerator.py
# CaptchaGenerator.py
from captcha.image import ImageCaptcha
import random
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
number = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z']
ALPHABET = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z']
# 以0-9中的數字生成隨機驗證碼
char_set = number
# 生成驗證碼的函式,生成的驗證碼序列長度為4
def random_captcha_text(ch_len, ch_set):
# 定義驗證碼包含的字元在這個列表
ch_text = []
# 進行captcha_size個迴圈,每次迴圈將1個隨機抽到的字元放入captcha_text
for i in range(ch_len):
ch_text.append(random.choice(ch_set))
return ch_text
# 獲取和生成的驗證碼對應的字元圖片
def gen_captcha_text_and_image(wid, hei, ch_len, ch_set):
# 生成指定大小的圖片
img = ImageCaptcha(width=wid, height=hei)
# 生成一個隨機的驗證碼序列
ch_text = random_captcha_text(ch_len, ch_set)
# 將字串序列中每個字元連線起來
ch_text = "".join(ch_text)
# 根據驗證碼序列生成對應的字元圖片
ch_text_img = img.generate(ch_text)
ch_img = Image.open(ch_text_img)
# 將圖片轉換成一個數組,這個陣列有3個維度
# 因為圖片是用RGB模式表示的,將其轉換成陣列即圖片的解析度160X60的矩陣,矩陣每個元素是一個畫素點上的RGB三個通道的值
ch_img = np.array(ch_img)
return ch_text, ch_img
# 把彩色影象轉為灰度影象,因為色彩對識別驗證碼沒有什麼用處,反而會增大資料處理量,RGB影象的資料處理量是灰度影象資料處理量的三倍
def convert_image_to_gray(img):
# 這是彩色影象轉換為灰度影象的公式
r, g, b = img[:, :, 0], img[:, :, 1], img[:, :, 2]
gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
# 也可以求r、g、b三個值的平均值作為灰度值
img[:, :, 0], img[:, :, 1], img[:, :, 2] = gray, gray, gray
gray_img = img
# 建立一個新矩陣,這個矩陣中的元素個數與img矩陣一樣。
# 但是每個元素只是原來RGB三個通道中的值的一個(轉換成灰度圖片後,RGB三個通道的數值相等)
gray_img_matrix = np.array(img[:, :, 0])
# 把矩陣變成一維陣列,陣列元素按矩陣行順序排列
gray_img_array = gray_img_matrix.flatten()
return gray_img, gray_img_array
# 生成影象對應的標籤
def generate_captcha_text_label(ch_text, ch_set):
ch_text_label = np.zeros(len(ch_text) * len(ch_set))
for i in range(len(ch_text)):
char = ch_text[i]
temp = ord(char)
if 48 <= temp <= 57:
temp = temp - 48
elif 97 <= temp <= 122:
temp = temp - 97 + 10
elif 65 <= temp <= 90:
temp = temp - 65 + 10 + 26
ch_text_label[i * len(ch_set) + temp] = 1
return ch_text_label
# 把生成的標籤轉換回字元序列
def text_label_turn_to_char_list(ch_text_label, ch_len, ch_set):
ch_list = []
for i in range(ch_len):
for j in range(len(ch_set)):
if ch_text_label[i * len(ch_set) + j] == 1.0:
ch_list.append(ch_set[j])
ch_list = "".join(ch_list)
return ch_list
# 把預測得到的標籤(經過tf.argmax函式處理後的陣列)轉換回字元序列
def pred_label_turn_to_char_list(pred_label, ch_len, ch_set):
ch_list = []
for i in range(ch_len):
ch_list.append(ch_set[pred_label[i]])
ch_list = "".join(ch_list)
return ch_list
# 測試
if __name__ == '__main__':
width = 160
height = 60
char_size = 4
text, image = gen_captcha_text_and_image(width, height, char_size, char_set)
print("生成的驗證碼字元序列為:", text)
print("生成的驗證碼圖片的資料形式:\n", image)
print("生成的驗證碼圖片資料的維度:", image.shape)
plt.figure()
plt.title(text)
plt.imshow(image)
plt.show()
gray_image, gray_image_array = convert_image_to_gray(image)
print("生成的驗證碼灰度圖片的資料形式:\n", gray_image)
print("生成的驗證碼灰度圖片資料處理成陣列形式後的資料:\n", gray_image_array)
print("生成的灰度圖片對應的一位陣列的維度大小:", gray_image_array.shape)
plt.figure()
plt.title(text)
plt.imshow(image)
plt.show()
label = generate_captcha_text_label(text, char_set)
print("圖片的標籤陣列為:\n", label)
print("標籤陣列的維度大小為:", label.shape)
char_list = text_label_turn_to_char_list(label, char_size, char_set)
print("由標籤陣列生成對應的字元序列:", char_list)
執行結果如下:
生成的驗證碼字元序列為: 0733
生成的驗證碼圖片的資料形式:
[[[247 244 244]
[247 244 244]
[247 244 244]
...
[247 244 244]
[247 244 244]
[247 244 244]]
[[247 244 244]
[247 244 244]
[247 244 244]
...
[247 244 244]
[247 244 244]
[247 244 244]]
[[247 244 244]
[247 244 244]
[247 244 244]
...
[247 244 244]
[247 244 244]
[247 244 244]]
...
[[247 244 244]
[247 244 244]
[247 244 244]
...
[247 244 244]
[247 244 244]
[247 244 244]]
[[247 244 244]
[247 244 244]
[247 244 244]
...
[247 244 244]
[247 244 244]
[247 244 244]]
[[247 244 244]
[247 244 244]
[247 244 244]
...
[247 244 244]
[247 244 244]
[247 244 244]]]
生成的驗證碼圖片資料的維度: (60, 160, 3)
生成的驗證碼灰度圖片的資料形式:
[[[244 244 244]
[244 244 244]
[244 244 244]
...
[244 244 244]
[244 244 244]
[244 244 244]]
[[244 244 244]
[244 244 244]
[244 244 244]
...
[244 244 244]
[244 244 244]
[244 244 244]]
[[244 244 244]
[244 244 244]
[244 244 244]
...
[244 244 244]
[244 244 244]
[244 244 244]]
...
[[244 244 244]
[244 244 244]
[244 244 244]
...
[244 244 244]
[244 244 244]
[244 244 244]]
[[244 244 244]
[244 244 244]
[244 244 244]
...
[244 244 244]
[244 244 244]
[244 244 244]]
[[244 244 244]
[244 244 244]
[244 244 244]
...
[244 244 244]
[244 244 244]
[244 244 244]]]
生成的驗證碼灰度圖片資料處理成陣列形式後的資料:
[244 244 244 ... 244 244 244]
生成的灰度圖片對應的一位陣列的維度大小: (9600,)
圖片的標籤陣列為:
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
標籤陣列的維度大小為: (40,)
由標籤陣列生成對應的字元序列: 0733
Process finished with exit code 0
我們可以發現,Captcha庫生成的.jpg圖片,其資料形式是160X60的矩陣(160和60是我們設定的長度和寬度上的畫素點個數),每個矩陣元素是一個有3個元素的一維陣列,代表了這個畫素點的R、G、B三個通道的值(範圍在0-255之間)。然後我們將其轉換成.jpg格式的灰度圖片,這時矩陣的形式不變,但一個畫素點上的R、G、B三個通道的值都變成相同值了。
由於灰度圖片上一個畫素點的三個通道上值相同,那麼我們顯然一個畫素點上只要提取一個值即可。因此我們繼續將其處理成160X60=9600個元素的一維陣列,這就是我們輸入CNN網路時圖片的資料形式。
注意:
mnist中圖片的一維陣列之所以都是0-1之間的小數值是因為資料形式在變為一維陣列後都除以了255(RGB值的範圍是0-255),這樣可以讓數字變得更小,對計算有些好處。在我的程式碼中沒有對圖片資料做除以255的處理。
由於我上面設定的驗證碼字元序列長度為4,這些字元都是從0-9個數字中隨機挑選的,因此圖片的標籤陣列為4X10=40個元素的一維陣列。這就是我們輸入CNN網路時標籤的資料形式。
建立模型、模型的訓練、模型的測試:
有了上面的CaptchaGenerator.py檔案,我們現在可以利用其中定義的函式和方法來生成我們的訓練樣本和測試樣本了。
我們先建立一個get_next_batch函式用來一次性生成一批樣本,後面訓練和測試模型時我們直接用這個函式生成訓練或測試用的一批樣本的圖片資料和標籤資料。
然後定義X、Y、keep_prob佔位符,用來進行資料X(圖片資料)、Y(圖片的標籤資料)和dropout函式的引數keep_prob的輸入。
然後定義生成w、b的函式,定義conv2d卷積函式和max_pool2x2池化函式。
現在我們可以開始定義CNN神經網路模型了。定義的神經網路模型結構如下:
隱藏層1:
過濾器大小3x3,卷積步長1,SAME模式,輸出32層feature(輸入的資料是1層);
池化視窗2x2,池化步長2,SAME模式;
設定一個dropout層,比率keep_prob為0.75;
隱藏層2:
過濾器大小3x3,卷積步長1,SAME模式,輸出64層feature(輸入的資料是32層feature);
池化視窗2x2,池化步長2,SAME模式;
設定一個dropout層,比率keep_prob為0.75;
隱藏層3:
過濾器大小3x3,卷積步長1,SAME模式,輸出64層feature(輸入的資料是64層feature);
池化視窗2x2,池化步長2,SAME模式;
設定一個dropout層,比率keep_prob為0.75;
將輸出的64層feature資料矩陣reshape成一個一位陣列;
全連線層1:
設定全連線層神經元數量為1024;
設定一個dropout層,比率keep_prob為0.75;
輸出層:
設定最後分類向量的長度為text_len(字元序列的長度) * len_char_set(用於抽取隨機字元的char序列)。然後用sigmod啟用函式處理輸出資料。
輸入影象資料的尺寸變化:
x:一個shape為(BATCH_SIZE,160,60,1)的張量
conv_1c:一個shape為(BATCH_SIZE,160,60,32)的張量
conv_1p:一個shape為(BATCH_SIZE,80,30,32)的張量
conv_2c:一個shape為(BATCH_SIZE,80,30,64)的張量
conv_2p:一個shape為(BATCH_SIZE,40,15,64)的張量
conv_3c:一個shape為(BATCH_SIZE,40,15,64)的張量
conv_3p:一個shape為(BATCH_SIZE,20,8,64)的張量
conv_3r:一個shape為(BATCH_SIZE,40X15X64)的張量
fc_1:一個shape為(BATCH_SIZE,1024)的張量
out:一個shape為(BATCH_SIZE,40)的張量
out張量會再被sigmoid函式處理,但其shape不變
predict:一個shape為(BATCH_SIZE,4,10)的張量
real_value:一個shape為(BATCH_SIZE,4,10)的張量
設定loss函式、optimizer優化器、設定準確率accuracy、設定saver:
loss函式採用cross_entropy交叉熵函式。
optimizer優化器選擇Adam演算法,學習率設為0.001。
我們將預測值和真實值reshape成TEXT_LEN行len(CHAR_SET)列的矩陣,然後用tf.argmax函式返回矩陣每一行的最大值的下標,再使用tf.equal函式進行比較,最後求其平均值即可得準確率accuracy。
設定一個用於儲存模型的物件saver。
建立一個用於訓練的Session會話:
首先我們要設定一個if判斷,如果已有儲存的模型則從最近的檢查點恢復模型,這樣在上一次訓練中斷後重新訓練時就不需要再重頭開始訓練;
然後我們設定一個while True無限迴圈,在迴圈中設定比如準確率大於0.5時break跳出迴圈;
sess.run我們的optimizer和loss函式,每訓練5次列印一次loss值,每訓練100次我們生成一個驗證集(validation set)來看一下當前訓練的模型的識別準確率,如果準確率大於我們的設定值比如0.5,則跳出迴圈,訓練結束。
建立一個用於測試的Session會話:
從最近的檢查點恢復模型引數(即我們訓練好的最近一次模型的引數);
建立一個for迴圈,迴圈次數iteration,然後在迴圈中建立一個測試的批樣本,樣本大小TEST_SIZE,sess.run計算預測值pred、真實值real、準確率acc;
注意我們計算得到的pred和real此時是一個一維的tensor張量,張量中儲存了4個下標,代表對應的字元在char_set候選字符集中的位置。然後通過pred_label_turn_to_char_list函式(這個函式在CaptchaGenerator.py中定義)就可以得到對應的字元序列;
最後我們列印每次的預測字元序列和真實字元序列,每過一輪iteration列印本輪的識別準確率acc。
建立模型、模型的訓練、模型的測試的所有完整程式碼如下:
CaptchaTrain.py
from CaptchaGenerator import char_set
from CaptchaGenerator import gen_captcha_text_and_image, convert_image_to_gray, generate_captcha_text_label, \
text_label_turn_to_char_list, pred_label_turn_to_char_list
import matplotlib.pyplot as plt
import tensorflow as tf
from PIL import Image
import numpy as np
import os
import math
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
# 基本引數設定:驗證碼影象的大小、驗證碼字串長度、訓練批樣本大小
IMAGE_WIDTH = 160
IMAGE_HEIGHT = 60
TEXT_LEN = 4
CHAR_SET = char_set
BATCH_SIZE = 64
VALIDATION_SIZE = 100
TEST_SIZE = 100
ITERATION = 5
# 測試
# text, image = gen_captcha_text_and_image(IMAGE_WIDTH, IMAGE_HEIGHT, TEXT_LEN, CHAR_SET)
# print("隨機生成的字串序列為:", text)
# print("影象的矩陣為:\n")
# print(image)
# print("驗證碼影象的尺寸為:", image.shape)
# plt.figure()
# plt.title(text)
# plt.imshow(image)
# plt.show()
# gray_image, gray_image_array = convert_image_to_gray(image)
# print("轉換的灰度影象的矩陣為:\n")
# print(gray_image)
# print("驗證碼灰度影象的尺寸為:", gray_image.shape)
# print("灰度影象轉換成的一維陣列為:\n")
# print(gray_image_array)
# print("灰度影象轉換成的一維陣列尺寸為:", gray_image_array.shape)
# plt.figure()
# plt.title(text)
# plt.imshow(gray_image)
# plt.show()
# label = generate_captcha_text_label(text, CHAR_SET)
# print("影象的標籤為:\n", label)
# char_list = text_label_turn_to_char_list(label, TEXT_LEN, CHAR_SET)
# print("標籤對應的驗證碼為:", char_list)
# 生成一個訓練batch
def get_next_batch(batch_size, mode="train"):
batch_x = np.zeros(shape=[batch_size, IMAGE_WIDTH * IMAGE_HEIGHT])
batch_y = np.zeros(shape=[batch_size, TEXT_LEN * len(CHAR_SET)])
if mode == "train":
if not os.path.exists("./train_image/"):
os.mkdir("./train_image/")
elif mode == "validation":
if not os.path.exists("./validation_image/"):
os.mkdir("./validation_image/")
elif mode == "test":
if not os.path.exists("./test_image/"):
os.mkdir("./test_image/")
for i in range(batch_size):
tex, img = gen_captcha_text_and_image(IMAGE_WIDTH, IMAGE_HEIGHT, TEXT_LEN, CHAR_SET)
im = Image.fromarray(img)
if mode == "train":
im_save_path = "./train_image/" + str(i) + "_train.jpg"
im.save(im_save_path)
elif mode == "validation":
im_save_path = "./validation_image/" + str(i) + "_validation.jpg"
im.save(im_save_path)
elif mode == "test":
im_save_path = "./test_image/" + str(i) + "_test.jpg"
im.save(im_save_path)
gray_image, gray_image_matrix = convert_image_to_gray(img)
batch_x[i, :] = gray_image_matrix
batch_y[i, :] = generate_captcha_text_label(tex, CHAR_SET)
return batch_x, batch_y
# 測試
# batch_x, batch_y = get_next_batch(BATCH_SIZE)
# print(batch_x[0], batch_y[0])
X = tf.placeholder(tf.float32, [None, IMAGE_HEIGHT * IMAGE_WIDTH])
Y = tf.placeholder(tf.float32, [None, TEXT_LEN * len(CHAR_SET)])
keep_prob = tf.placeholder(tf.float32)
# 定義生成w變數的函式
def weight_variable(shape):
initial = 0.01 * tf.random_normal(shape)
return tf.Variable(initial)
# 定義生成b變數的函式
def bias_variable(shape):
initial = 0.1 * tf.random_normal(shape)
return tf.Variable(initial)
# 定義卷積函式,x是輸入的影象,W是此卷積層的權重矩陣
def conv2d(x, w):
return tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')
# 定義池化函式,x是輸入的矩陣
def max_pool2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# 定義神經網路函式
def convolutional_neural_networks(x_input, hei, wid, text_len, len_char_set):
x = tf.reshape(x_input, shape=[-1, hei, wid, 1])
# 影象的尺寸變化:
# x:一個shape為(BATCH_SIZE,160,60,1)的張量
# conv_1c:一個shape為(BATCH_SIZE,160,60,32)的張量
# conv_1p:一個shape為(BATCH_SIZE,80,30,32)的張量
# conv_2c:一個shape為(BATCH_SIZE,80,30,64)的張量
# conv_2p:一個shape為(BATCH_SIZE,40,15,64)的張量
# conv_3c:一個shape為(BATCH_SIZE,40,15,64)的張量
# conv_3p:一個shape為(BATCH_SIZE,20,8,64)的張量
# conv_3r:一個shape為(BATCH_SIZE,40X15X64)的張量
# fc_1:一個shape為(BATCH_SIZE,1024)的張量
# out:一個shape為(BATCH_SIZE,40)的張量
# out張量會再被sigmoid函式處理,但其shape不變
# predict:一個shape為(BATCH_SIZE,4,10)的張量
# real_value:一個shape為(BATCH_SIZE,4,10)的張量
# 現在知道為什麼tf.argmax()函式中維度值是2了嗎?因為這是個3維向量,只有取維度2時比較的才是代表一個字元的10個元素中的最大值的下標
conv_width_input = wid
conv_height_input = hei
w_c1 = weight_variable([3, 3, 1, 32])
b_c1 = bias_variable([32])
conv_1c = tf.nn.relu(conv2d(x, w_c1) + b_c1)
conv_1p = max_pool2x2(conv_1c)
conv_1d = tf.nn.dropout(conv_1p, keep_prob)
conv_width_1 = math.ceil(conv_width_input / 2)
conv_height_1 = math.ceil(conv_height_input / 2)
w_c2 = weight_variable([3, 3, 32, 64])
b_c2 = bias_variable([64])
conv_2c = tf.nn.relu(conv2d(conv_1d, w_c2) + b_c2)
conv_2p = max_pool2x2(conv_2c)
conv_2d = tf.nn.dropout(conv_2p, keep_prob)
conv_width_2 = math.ceil(conv_width_1 / 2)
conv_height_2 = math.ceil(conv_height_1 / 2)
w_c3 = weight_variable([3, 3, 64, 64])
b_c3 = bias_variable([64])
conv_3c = tf.nn.relu(conv2d(conv_2d, w_c3) + b_c3)
conv_3p = max_pool2x2(conv_3c)
conv_3d = tf.nn.dropout(conv_3p, keep_prob)
conv_width_3 = int(math.ceil(conv_width_2 / 2))
conv_height_3 = int(math.ceil(conv_height_2 / 2))
# Fully connected layer
w_d1 = weight_variable([conv_width_3 * conv_height_3 * 64, 1024])
b_d1 = bias_variable([1024])
conv_3r = tf.reshape(conv_3d, [-1, conv_width_3 * conv_height_3 * 64])
fc_1 = tf.nn.relu(tf.matmul(conv_3r, w_d1) + b_d1)
fc_1d = tf.nn.dropout(fc_1, keep_prob)
w_out = weight_variable([1024, text_len * len_char_set])
b_out = bias_variable([text_len * len_char_set])
out = tf.matmul(fc_1d, w_out) + b_out
return out
output = convolutional_neural_networks(X, IMAGE_WIDTH, IMAGE_HEIGHT, TEXT_LEN, len(CHAR_SET))
# 將預測值規整成BATCH_SIZE個TEXT_LEN行len(CHAR_SET)列的矩陣,這樣每行就對應字元序列中的一個字元的標籤
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=output, labels=Y))
# loss函式
optimizer = tf.train.AdamOptimizer(0.001).minimize(loss)
# 優化器
predict = tf.argmax(tf.reshape(output, [-1, TEXT_LEN, len(CHAR_SET)]), 2)
# 將真實值規整成BATCH_SIZE個TEXT_LEN行len(CHAR_SET)列的矩陣,這樣每行就對應字元序列中的一個字元的標籤
real_value = tf.argmax(tf.reshape(Y, [-1, TEXT_LEN, len(CHAR_SET)]), 2)
# 比較預測值是否正確
correct_pre = tf.equal(predict, real_value)
# 計算正確率
accuracy = tf.reduce_mean(tf.cast(correct_pre, tf.float32))
# 儲存模型的物件saver
saver = tf.train.Saver()
# 建立儲存模型的路徑
if not os.path.exists("./tmp/"):
os.mkdir("./tmp")
# 建立用於訓練模型的Session會話
with tf.Session() as sess_train:
sess_train.run(tf.global_variables_initializer())
if os.path.exists("./tmp/checkpoint"):
# 判斷模型是否存在,如果存在則從模型中恢復變數
saver.restore(sess_train, tf.train.latest_checkpoint('./tmp/'))
step = 0
while True:
batch_x_train, batch_y_train = get_next_batch(BATCH_SIZE)
_, batch_loss = sess_train.run([optimizer, loss],
feed_dict={X: batch_x_train, Y: batch_y_train, keep_prob: 0.75})
if step % 5 == 0:
# 每訓練5次列印一次loss值
print("iteration:%d , batch_loss:%s" % (step, batch_loss))
if step % 100 == 0:
# 每訓練100次儲存一次模型
saver.save(sess_train, "./tmp/train_model", global_step=step)
# 每訓練100次計算並列印一次準確率
batch_x_validation, batch_y_validation = get_next_batch(VALIDATION_SIZE, mode="validation")
acc = sess_train.run(accuracy,
feed_dict={X: batch_x_validation, Y: batch_y_validation, keep_prob: 1})
print("iteration:%d , acc:%s" % (step, acc))
# 如果準確率大於設定值,儲存模型,完成訓練
if acc > 0.9:
saver.save(sess_train, "./tmp/train_model", global_step=step)
break
step = step + 1
# 建立用於測試模型的Session會話
with tf.Session() as sess_test:
sess_test.run(tf.global_variables_initializer())
saver.restore(sess_test, tf.train.latest_checkpoint('./tmp/'))
for i in range(ITERATION):
batch_x_test, batch_y_test = get_next_batch(TEST_SIZE, mode="test")
# 注意,在Session會話前定義的各種變數都是tensorflow的資料流圖中的節點,所謂的節點就是定義了一組計算公式和取得的結果,用變量表示結果
# 但是在Session中如果沒有run相應的變數節點的話,這個取得的結果是沒有計算出來的,所以如果我們想要使用這個變數的結果
# 就必須對相應的變數進行sess.run
# 如下面sess.run計算了predict, real_value, accuracy三個變數,因為我們後面需要用到這三個變數的結果
# 注意計算出來的返回值pred, real, acc都是tensor張量,具體的張量的shape由我們輸入的資料格式決定
pred, real, acc = sess_test.run([predict, real_value, accuracy],
feed_dict={X: batch_x_test, Y: batch_y_test, keep_prob: 1})
# 注意pred和real都是tensorflow的張量tensor,雖然它們和多維陣列形式非常類似,但不是陣列,不是可迭代物件
# 這裡有個小坑,就是tensorflow的張量tensor不能直接用在for迴圈裡迭代,另外陣列中的元素如果是float型不能直接用作陣列的下標
pred_int = tf.cast(pred, tf.int32)
real_int = tf.cast(real, tf.int32)
pred_array = pred_int.eval(session=sess_test)
real_array = real_int.eval(session=sess_test)
# pred, real的張量形式是2維矩陣,TEXT_SIZE行X4列矩陣,之所以是4列是因為前面用tf.argmax逐行提取了每行最大值的下標,一共四行,所以是4個下標
# 上面兩步先將張量的值全部轉為int型,然後將張量轉換成多維陣列
# 把張量轉換成多維陣列後,我們就可以進行迭代,然後使用pred_label_turn_to_char_list函式將標籤轉換成對應的字元序列
for j in range(TEST_SIZE):
# 這裡預測值的標籤經過tf.argmax只提取出了四個字元的下標,真實值的標籤也這麼處理了
# 因此由標籤得到字元序列都使用函式pred_label_turn_to_char_list
pred_char_list = pred_label_turn_to_char_list(pred_array[j], TEXT_LEN, CHAR_SET)
real_char_list = pred_label_turn_to_char_list(real_array[j], TEXT_LEN, CHAR_SET)
print("第{}輪ITERATION中第{}個驗證碼預測:預測驗證碼為:{} 真實驗證碼為:{}".format(i + 1, j + 1, pred_char_list, real_char_list))
print("第{}輪ITERATION識別測試正確率:{}".format(i + 1, acc))
執行結果如下:
由於我已經訓練了該模型,且準確率已經超過0.9,因此我執行這段程式碼時會執行一次sess_train會話中的while True迴圈,然後就會因此判斷acc>0.9跳出迴圈,sess_train會話結束。隨後就開始執行sess_test會話,開始測試模型。我設定的TEST_SIZE為100,iteration為5,即進行5輪測試,每輪測試100個樣本。
iteration:0 , batch_loss:0.09243201
iteration:0 , acc:0.915
第1輪ITERATION中第1個驗證碼預測:預測驗證碼為:3600 真實驗證碼為:3600
第1輪ITERATION中第2個驗證碼預測:預測驗證碼為:8782 真實驗證碼為:8782
第1輪ITERATION中第3個驗證碼預測:預測驗證碼為:2812 真實驗證碼為:2817
第1輪ITERATION中第4個驗證碼預測:預測驗證碼為:6472 真實驗證碼為:5472
第1輪ITERATION中第5個驗證碼預測:預測驗證碼為:4578 真實驗證碼為:4578
第1輪ITERATION中第6個驗證碼預測:預測驗證碼為:3215 真實驗證碼為:3215
第1輪ITERATION中第7個驗證碼預測:預測驗證碼為:7090 真實驗證碼為:7090
第1輪ITERATION中第8個驗證碼預測:預測驗證碼為:5931 真實驗證碼為:5931
第1輪ITERATION中第9個驗證碼預測:預測驗證碼為:4491 真實驗證碼為:4191
第1輪ITERATION中第10個驗證碼預測:預測驗證碼為:9578 真實驗證碼為:9573
第1輪ITERATION中第11個驗證碼預測:預測驗證碼為:0066 真實驗證碼為:0056
第1輪ITERATION中第12個驗證碼預測:預測驗證碼為:9398 真實驗證碼為:9398
第1輪ITERATION中第13個驗證碼預測:預測驗證碼為:8191 真實驗證碼為:8191
第1輪ITERATION中第14個驗證碼預測:預測驗證碼為:2696 真實驗證碼為:2696
第1輪ITERATION中第15個驗證碼預測:預測驗證碼為:5633 真實驗證碼為:5632
第1輪ITERATION中第16個驗證碼預測:預測驗證碼為:1790 真實驗證碼為:1190
第1輪ITERATION中第17個驗證碼預測:預測驗證碼為:9250 真實驗證碼為:9250
第1輪ITERATION中第18個驗證碼預測:預測驗證碼為:3232 真實驗證碼為:3232
第1輪ITERATION中第19個驗證碼預測:預測驗證碼為:0090 真實驗證碼為:0198
第1輪ITERATION中第20個驗證碼預測:預測驗證碼為:6187 真實驗證碼為:6187
第1輪ITERATION中第21個驗證碼預測:預測驗證碼為:7596 真實驗證碼為:7596
第1輪ITERATION中第22個驗證碼預測:預測驗證碼為:1270 真實驗證碼為:1276
第1輪ITERATION中第23個驗證碼預測:預測驗證碼為:8327 真實驗證碼為:8327
第1輪ITERATION中第24個驗證碼預測:預測驗證碼為:9285 真實驗證碼為:9285
第1輪ITERATION中第25個驗證碼預測:預測驗證碼為:9833 真實驗證碼為:9833
第1輪ITERATION中第26個驗證碼預測:預測驗證碼為:2394 真實驗證碼為:2394
第1輪ITERATION中第27個驗證碼預測:預測驗證碼為:5769 真實驗證碼為:5769
第1輪ITERATION中第28個驗證碼預測:預測驗證碼為:2878 真實驗證碼為:2878
第1輪ITERATION中第29個驗證碼預測:預測驗證碼為:7086 真實驗證碼為:7086
第1輪ITERATION中第30個驗證碼預測:預測驗證碼為:4052 真實驗證碼為:4052
第1輪ITERATION中第31個驗證碼預測:預測驗證碼為:4686 真實驗證碼為:4686
第1輪ITERATION中第32個驗證碼預測:預測驗證碼為:1811 真實驗證碼為:1612
第1輪ITERATION中第33個驗證碼預測:預測驗證碼為:8283 真實驗證碼為:8283
第1輪ITERATION中第34個驗證碼預測:預測驗證碼為:8984 真實驗證碼為:8984
第1輪ITERATION中第35個驗證碼預測:預測驗證碼為:9039 真實驗證碼為:9039
第1輪ITERATION中第36個驗證碼預測:預測驗證碼為:3775 真實驗證碼為:3775
第1輪ITERATION中第37個驗證碼預測:預測驗證碼為:8107 真實驗證碼為:8102
第1輪ITERATION中第38個驗證碼預測:預測驗證碼為:8224 真實驗證碼為:8224
第1輪ITERATION中第39個驗證碼預測:預測驗證碼為:6466 真實驗證碼為:6472
第1輪ITERATION中第40個驗證碼預測:預測驗證碼為:0861 真實驗證碼為:0861
第1輪ITERATION中第41個驗證碼預測:預測驗證碼為:7898 真實驗證碼為:7898
第1輪ITERATION中第42個驗證碼預測:預測驗證碼為:5288 真實驗證碼為:5233
第1輪ITERATION中第43個驗證碼預測:預測驗證碼為:6557 真實驗證碼為:6507
第1輪ITERATION中第44個驗證碼預測:預測驗證碼為:5279 真實驗證碼為:5279
第1輪ITERATION中第45個驗證碼預測:預測驗證碼為:3732 真實驗證碼為:3732
第1輪ITERATION中第46個驗證碼預測:預測驗證碼為:8828 真實驗證碼為:8828
第1輪ITERATION中第47個驗證碼預測:預測驗證碼為:4037 真實驗證碼為:4037
第1輪ITERATION中第48個驗證碼預測:預測驗證碼為:8967 真實驗證碼為:8967
第1輪ITERATION中第49個驗證碼預測:預測驗證碼為:1988 真實驗證碼為:1988
第1輪ITERATION中第50個驗證碼預測:預測驗證碼為:2251 真實驗證碼為:2251
第1輪ITERATION中第51個驗證碼預測:預測驗證碼為:7981 真實驗證碼為:7981
第1輪ITERATION中第52個驗證碼預測:預測驗證碼為:2836 真實驗證碼為:2836
第1輪ITERATION中第53個驗證碼預測:預測驗證碼為:0784 真實驗證碼為:0784
第1輪ITERATION中第54個驗證碼預測:預測驗證碼為:8565 真實驗證碼為:8565
第1輪ITERATION中第55個驗證碼預測:預測驗證碼為:5576 真實驗證碼為:5576
第1輪ITERATION中第56個驗證碼預測:預測驗證碼為:7371 真實驗證碼為:7371
第1輪ITERATION中第57個驗證碼預測:預測驗證碼為:5921 真實驗證碼為:5921
第1輪ITERATION中第58個驗證碼預測:預測驗證碼為:9828 真實驗證碼為:9828
第1輪ITERATION中第59個驗證碼預測:預測驗證碼為:4898 真實驗證碼為:4898
第1輪ITERATION中第60個驗證碼預測:預測驗證碼為:1941 真實驗證碼為:1941
第1輪ITERATION中第61個驗證碼預測:預測驗證碼為:4876 真實驗證碼為:4876
第1輪ITERATION中第62個驗證碼預測:預測驗證碼為:8918 真實驗證碼為:8918
第1輪ITERATION中第63個驗證碼預測:預測驗證碼為:0741 真實驗證碼為:0742
第1輪ITERATION中第64個驗證碼預測:預測驗證碼為:9302 真實驗證碼為:9302
第1輪ITERATION中第65個驗證碼預測:預測驗證碼為:3938 真實驗證碼為:3938
第1輪ITERATION中第66個驗證碼預測:預測驗證碼為:3346 真實驗證碼為:3346
第1輪ITERATION中第67個驗證碼預測:預測驗證碼為:7246 真實驗證碼為:7296
第1輪ITERATION中第68個驗證碼預測:預測驗證碼為:9503 真實驗證碼為:9503
第1輪ITERATION中第69個驗證碼預測:預測驗證碼為:9907 真實驗證碼為:9407
第1輪ITERATION中第70個驗證碼預測:預測驗證碼為:2210 真實驗證碼為:2210
第1輪ITERATION中第71個驗證碼預測:預測驗證碼為:0089 真實驗證碼為:0089
第1輪ITERATION中第72個驗證碼預測:預測驗證碼為:0575 真實驗證碼為:0576
第1輪ITERATION中第73個驗證碼預測:預測驗證碼為:5380 真實驗證碼為:5380
第1輪ITERATION中第74個驗證碼預測:預測驗證碼為:3541 真實驗證碼為:3541
第1輪ITERATION中第75個驗證碼預測:預測驗證碼為:3158 真實驗證碼為:3158
第1輪ITERATION中第76個驗證碼預測:預測驗證碼為:6754 真實驗證碼為:6754
第1輪ITERATION中第77個驗證碼預測:預測驗證碼為:2402 真實驗證碼為:2402
第1輪ITERATION中第78個驗證碼預測:預測驗證碼為:7359 真實驗證碼為:7359
第1輪ITERATION中第79個驗證碼預測:預測驗證碼為:0043 真實驗證碼為:0743
第1輪ITERATION中第80個驗證碼預測:預測驗證碼為:3009 真實驗證碼為:3009
第1輪ITERATION中第81個驗證碼預測:預測驗證碼為:7051 真實驗證碼為:7051
第1輪ITERATION中第82個驗證碼預測:預測驗證碼為:5295 真實驗證碼為:5295
第1輪ITERATION中第83個驗證碼預測:預測驗證碼為:3419 真實驗證碼為:3719
第1輪ITERATION中第84個驗證碼預測:預測驗證碼為:2537 真實驗證碼為:2537
第1輪ITERATION中第85個驗證碼預測:預測驗證碼為:5331 真實驗證碼為:5331
第1輪ITERATION中第86個驗證碼預測:預測驗證碼為:5498 真實驗證碼為:5498
第1輪ITERATION中第87個驗證碼預測:預測驗證碼為:4284 真實驗證碼為:4284
第1輪ITERATION中第88個驗證碼預測:預測驗證碼為:1095 真實驗證碼為:1095
第1輪ITERATION中第89個驗證碼預測:預測驗證碼為:5982 真實驗證碼為:5982
第1輪ITERATION中第90個驗證碼預測:預測驗證碼為:9546 真實驗證碼為:9546
第1輪ITERATION中第91個驗證碼預測:預測驗證碼為:5214 真實驗證碼為:5214
第1輪ITERATION中第92個驗證碼預測:預測驗證碼為:3880 真實驗證碼為:3882
第1輪ITERATION中第93個驗證碼預測:預測驗證碼為:3095 真實驗證碼為:3035
第1輪ITERATION中第94個驗證碼預測:預測驗證碼為:0175 真實驗證碼為:0175
第1輪ITERATION中第95個驗證碼預測:預測驗證碼為:2584 真實驗證碼為:2584
第1輪ITERATION中第96個驗證碼預測:預測驗證碼為:0495 真實驗證碼為:0495
第1輪ITERATION中第97個驗證碼預測:預測驗證碼為:2612 真實驗證碼為:3612
第1輪ITERATION中第98個驗證碼預測:預測驗證碼為:9826 真實驗證碼為:9826
第1輪ITERATION中第99個驗證碼預測:預測驗證碼為:2211 真實驗證碼為:2271
第1輪ITERATION中第100個驗證碼預測:預測驗證碼為:6740 真實驗證碼為:6145
第1輪ITERATION識別測試正確率:0.925000011920929
第2輪ITERATION中第1個驗證碼預測:預測驗證碼為:3277 真實驗證碼為:3277
第2輪ITERATION中第2個驗證碼預測:預測驗證碼為:3392 真實驗證碼為:3392
第2輪ITERATION中第3個驗證碼預測:預測驗證碼為:8422 真實驗證碼為:8422
第2輪ITERATION中第4個驗證碼預測:預測驗證碼為:7521 真實驗證碼為:7521
第2輪ITERATION中第5個驗證碼預測:預測驗證碼為:1025 真實驗證碼為:1025
第2輪ITERATION中第6個驗證碼預測:預測驗證碼為:0206 真實驗證碼為:0206
第2輪ITERATION中第7個驗證碼預測:預測驗證碼為:4957 真實驗證碼為:4927
第2輪ITERATION中第8個驗證碼預測:預測驗證碼為:2148 真實驗證碼為:2148
第2輪ITERATION中第9個驗證碼預測:預測驗證碼為:3627 真實驗證碼為:3621
第2輪ITERATION中第10個驗證碼預測:預測驗證碼為:6057 真實驗證碼為:6057
第2輪ITERATION中第11個驗證碼預測:預測驗證碼為:0408 真實驗證碼為:0478
第2輪ITERATION中第12個驗證碼預測:預測驗證碼為:4912 真實驗證碼為:4912
第2輪ITERATION中第13個驗證碼預測:預測驗證碼為:4438 真實驗證碼為:4438
第2輪ITERATION中第14個驗證碼預測:預測驗證碼為:2900 真實驗證碼為:2900
第2輪ITERATION中第15個驗證碼預測:預測驗證碼為:6702 真實驗證碼為:6702
第2輪ITERATION中第16個驗證碼預測:預測驗證碼為:6697 真實驗證碼為:6797
第2輪ITERATION中第17個驗證碼預測:預測驗證碼為:1840 真實驗證碼為:1840
第2輪ITERATION中第18個驗證碼預測:預測驗證碼為:0155 真實驗證碼為:0155
第2輪ITERATION中第19個驗證碼預測:預測驗證碼為:4072 真實驗證碼為:4072
第2輪ITERATION中第20個驗證碼預測:預測驗證碼為:4810 真實驗證碼為:4810
第2輪ITERATION中第21個驗證碼預測:預測驗證碼為:2632 真實驗證碼為:2632
第2輪ITERATION中第22個驗證碼預測:預測驗證碼為:7469 真實驗證碼為:7469
第2輪ITERATION中第23個驗證碼預測:預測驗證碼為:8867 真實驗證碼為:8867
第2輪ITERATION中第24個驗證碼預測:預測驗證碼為:8322 真實驗證碼為:8322
第2輪ITERATION中第25個驗證碼預測:預測驗證碼為:2552 真實驗證碼為:2552
第2輪ITERATION中第26個驗證碼預測:預測驗證碼為:1436 真實驗證碼為:1436
第2輪ITERATION中第27個驗證碼預測:預測驗證碼為:8776 真實驗證碼為:8776
第2輪ITERATION中第28個驗證碼預測:預測驗證碼為:9219 真實驗證碼為:9279
第2輪ITERATION中第29個驗證碼預測:預測驗證碼為:9081 真實驗證碼為:9085
第2輪ITERATION中第30個驗證碼預測:預測驗證碼為:9667 真實驗證碼為:9661
第2輪ITERATION中第31個驗證碼預測:預測驗證碼為:8040 真實驗證碼為:8040
第2輪ITERATION中第32個驗證碼預測:預測驗證碼為:1324 真實驗證碼為:1324
第2輪ITERATION中第33個驗證碼預測:預測驗證碼為:2723 真實驗證碼為:2723
第2輪ITERATION中第34個驗證碼預測:預測驗證碼為:2202 真實驗證碼為:2202
第2輪ITERATION中第35個驗證碼預測:預測驗證碼為:3786 真實驗證碼為:3786
第2輪ITERATION中第36個驗證碼預測:預測驗證碼為:2413 真實驗證碼為:2413
第2輪ITERATION中第37個驗證碼預測:預測驗證碼為:2813 真實驗證碼為:2813
第2輪ITERATION中第38個驗證碼預測:預測驗證碼為:0843 真實驗證碼為:0843
第2輪ITERATION中第39個驗證碼預測:預測驗證碼為:7766 真實驗證碼為:7765
第2輪ITERATION中第40個驗證碼預測:預測驗證碼為:0885 真實驗證碼為:0885
第2輪ITERATION中第41個驗證碼預測:預測驗證碼為:1920 真實驗證碼為:1920
第2輪ITERATION中第42個驗證碼預測:預測驗證碼為:1725 真實驗證碼為:1725
第2輪ITERATION中第43個驗證碼預測:預測驗證碼為:2444 真實驗證碼為:2444
第2輪ITERATION中第44個驗證碼預測:預測驗證碼為:0779 真實驗證碼為:0779
第2輪ITERATION中第45個驗證碼預測:預測驗證碼為:2728 真實驗證碼為:2728
第2輪ITERATION中第46個驗證碼預測:預測驗證碼為:6073 真實驗證碼為:6073
第2輪ITERATION中第47個驗證碼預測:預測驗證碼為:6409 真實驗證碼為:6409
第2輪ITERATION中第48個驗證碼預測:預測驗證碼為:8409 真實驗證碼為:8409
第2輪ITERATION中第49個驗證碼預測:預測驗證碼為:3818 真實驗證碼為:3818
第2輪ITERATION中第50個驗證碼預測:預測驗證碼為:7021 真實驗證碼為:7021
第2輪ITERATION中第51個驗證碼預測:預測驗證碼為:9602 真實驗證碼為:9677
第2輪ITERATION中第52個驗證碼預測:預測驗證碼為:0741 真實驗證碼為:0141
第2輪ITERATION中第53個驗證碼預測:預測驗證碼為:0736 真實驗證碼為:7736
第2輪ITERATION中第54個驗證碼預測:預測驗證碼為:8661 真實驗證碼為:8661
第2輪ITERATION中第55個驗證碼預測:預測驗證碼為:9896 真實驗證碼為:9896
第2輪ITERATION中第56個驗證碼預測:預測驗證碼為:5860 真實驗證碼為:5860
第2輪ITERATION中第57個驗證碼預測:預測驗證碼為:9313 真實驗證碼為:9313
第2輪ITERATION中第58個驗證碼預測:預測驗證碼為:1258 真實驗證碼為:1258
第2輪ITERATION中第59個驗證碼預測:預測驗證碼為:3480 真實驗證碼為:3480
第2輪ITERATION中第60個驗證碼預測:預測驗證碼為:4219 真實驗證碼為:4219
第2輪ITERATION中第61個驗證碼預測:預測驗證碼為:9210 真實驗證碼為:9270
第2輪ITERATION中第62個驗證碼預測:預測驗證碼為:9016 真實驗證碼為:9013
第2輪ITERATION中第63個驗證碼預測:預測驗證碼為:7663 真實驗證碼為:7663
第2輪ITERATION中第64個驗證碼預測:預測驗證碼為:7496 真實驗證碼為:7496
第2輪ITERATION中第65個驗證碼預測:預測驗證碼為:0454 真實驗證碼為:0417
第2輪ITERATION中第66個驗證碼預測:預測驗證碼為:5770 真實驗證碼為:5770
第2輪ITERATION中第67個驗證碼預測:預測驗證碼為:0090 真實驗證碼為:0030
第2輪ITERATION中第68個驗證碼預測:預測驗證碼為:0893 真實驗證碼為:0893
第2輪ITERATION中第69個驗證碼預測:預測驗證碼為:5411 真實驗證碼為:5411
第2輪ITERATION中第70個驗證碼預測:預測驗證碼為:2236 真實驗證碼為:2236
第2輪ITERATION中第71個驗證碼預測:預測驗證碼為:8665 真實驗證碼為:8665
第2輪ITERATION中第72個驗證碼預測:預測驗證碼為:2360 真實驗證碼為:2360
第2輪ITERATION中第73個驗證碼預測:預測驗證碼為:2382 真實驗證碼為:1382
第2輪ITERATION中第74個驗證碼預測:預測驗證碼為:9244 真實驗證碼為:9744
第2輪ITERATION中第75個驗證碼預測:預測驗證碼為:5228 真實驗證碼為:5226
第2輪ITERATION中第76個驗證碼預測:預測驗證碼為:8296 真實驗證碼為:8296
第2輪ITERATION中第77個驗證碼預測:預測驗證碼為:5251 真實驗證碼為:5251
第2輪ITERATION中第78個驗證碼預測:預測驗證碼為:2971 真實驗證碼為:2972
第2輪ITERATION中第79個驗證碼預測:預測驗證碼為:5039 真實驗證碼為:5039
第2輪ITERATION中第80個驗證碼預測:預測驗證碼為:6783 真實驗證碼為:6783
第2輪ITERATION中第81個驗證碼預測:預測驗證碼為:9014 真實驗證碼為:9014
第2輪ITERATION中第82個驗證碼預測:預測驗證碼為:3410 真實驗證碼為:3410
第2輪ITERATION中第83個驗證碼預測:預測驗證碼為:9035 真實驗證碼為:9035
第2輪ITERATION中第84個驗證碼預測:預測驗證碼為:8323 真實驗證碼為:8371
第2輪ITERATION中第85個驗證碼預測:預測驗證碼為:7635 真實驗證碼為:7635
第2輪ITERATION中第86個驗證碼預測:預測驗證碼為:3609 真實驗證碼為:3609
第2輪ITERATION中第87個驗證碼預測:預測驗證碼為:0195 真實驗證碼為:0195
第2輪ITERATION中第88個驗證碼預測:預測驗證碼為:8957 真實驗證碼為:8957
第2輪ITERATION中第89個驗證碼預測:預測驗證碼為:9583 真實驗證碼為:9583
第2輪ITERATION中第90個驗證碼預測:預測驗證碼為:5261 真實驗證碼為:5261
第2輪ITERATION中第91個驗證碼預測:預測驗證碼為:7329 真實驗證碼為:7329
第2輪ITERATION中第92個驗證碼預測:預測驗證碼為:1226 真實驗證碼為:1226
第2輪ITERATION中第93個驗證碼預測:預測驗證碼為:5388 真實驗證碼為:5388
第2輪ITERATION中第94個驗證碼預測:預測驗證碼為:0773 真實驗證碼為:0773
第2輪ITERATION中第95個驗證碼預測:預測驗證碼為:9700 真實驗證碼為:9700
第2輪ITERATION中第96個驗證碼預測:預測驗證碼為:9207 真實驗證碼為:9207
第2輪ITERATION中第97個驗證碼預測:預測驗證碼為:6443 真實驗證碼為:6443
第2輪ITERATION中第98個驗證碼預測:預測驗證碼為:0586 真實驗證碼為:0585
第2輪ITERATION中第99個驗證碼預測:預測驗證碼為:0166 真實驗證碼為:0166
第2輪ITERATION中第100個驗證碼預測:預測驗證碼為:2850 真實驗證碼為:2850
第2輪ITERATION識別測試正確率:0.9399999976158142
第3輪ITERATION中第1個驗證碼預測:預測驗證碼為:7838 真實驗證碼為:7838
第3輪ITERATION中第2個驗證碼預測:預測驗證碼為:1919 真實驗證碼為:1919
第3輪ITERATION中第3個驗證碼預測:預測驗證碼為:7428 真實驗證碼為:7428
第3輪ITERATION中第4個驗證碼預測:預測驗證碼為:7943 真實驗證碼為:7943
第3輪ITERATION中第5個驗證碼預測:預測驗證碼為:0699 真實驗證碼為:0694
第3輪ITERATION中第6個驗證碼預測:預測驗證碼為:3866 真實驗證碼為:8365
第3輪ITERATION中第7個驗證碼預測:預測驗證碼為:4359 真實驗證碼為:4359
第3輪ITERATION中第8個驗證碼預測:預測驗證碼為:5214 真實驗證碼為:5214
第3輪ITERATION中第9個驗證碼預測:預測驗證碼為:7115 真實驗證碼為:7115
第3輪ITERATION中第10個驗證碼預測:預測驗證碼為:0455 真實驗證碼為:0455
第3輪ITERATION中第11個驗證碼預測:預測驗證碼為:2250 真實驗證碼為:2252
第3輪ITERATION中第12個驗證碼預測:預測驗證碼為:3934 真實驗證碼為:3934
第3輪ITERATION中第13個驗證碼預測:預測驗證碼為:4421 真實驗證碼為:4421
第3輪ITERATION中第14個驗證碼預測:預測驗證碼為:2810 真實驗證碼為:2810
第3輪ITERATION中第15個驗證碼預測:預測驗證碼為:7105 真實驗證碼為:7100
第3輪ITERATION中第16個驗證碼預測:預測驗證碼為:8633 真實驗證碼為:8633
第3輪ITERATION中第17個驗證碼預測:預測驗證碼為:0876 真實驗證碼為:0876
第3輪ITERATION中第18個驗證碼預測:預測驗證碼為:3071 真實驗證碼為:3071
第3輪ITERATION中第19個驗證碼預測:預測驗證碼為:3107 真實驗證碼為:3107
第3輪ITERATION中第20個驗證碼預測:預測驗證碼為:2545 真實驗證碼為:2545
第3輪ITERATION中第21個驗證碼預測:預測驗證碼為:8785 真實驗證碼為:8785
第3輪ITERATION中第22個驗證碼預測:預測驗證碼為:4462 真實驗證碼為:4462
第3輪ITERATION中第23個驗證碼預測:預測驗證碼為:8248 真實驗證碼為:8248
第3輪ITERATION中第24個驗證碼預測:預測驗證碼為:4248 真實驗證碼為:4248
第3輪ITERATION中第25個驗證碼預測:預測驗證碼為:9226 真實驗證碼為:9226
第3輪ITERATION中第26個驗證碼預測:預測驗證碼為:4758 真實驗證碼為:4758
第3輪ITERATION中第27個驗證碼預測:預測驗證碼為:2126 真實驗證碼為:2126
第3輪ITERATION中第28個驗證碼預測:預測驗證碼為:8614 真實驗證碼為:8614
第3輪ITERATION中第29個驗證碼預測:預測驗證碼為:6364 真實驗證碼為:6364
第3輪ITERATION中第30個驗證碼預測:預測驗證碼為:2610 真實驗證碼為:2610
第3輪ITERATION中第31個驗證碼預測:預測驗證碼為:5483 真實驗證碼為:5483
第3輪ITERATION中第32個驗證碼預測:預測驗證碼為:6674 真實驗證碼為:6674
第3輪ITERATION中第33個驗證碼預測:預測驗證碼為:8682 真實驗證碼為:8682
第3輪ITERATION中第34個驗證碼預測:預測驗證碼為:8175 真實驗證碼為:8175
第3輪ITERATION中第35個驗證碼預測:預測驗證碼為:3285 真實驗證碼為:3286
第3輪ITERATION中第36個驗證碼預測:預測驗證碼為:7972 真實驗證碼為:7972
第3輪ITERATION中第37個驗證碼預測:預測驗證碼為:6277 真實驗證碼為:6277
第3輪ITERATION中第38個驗證碼預測:預測驗證碼為:9432 真實驗證碼為:9432
第3輪ITERATION中第39個驗證碼預測:預測驗證碼為:4542 真實驗證碼為:4542
第3輪ITERATION中第40個驗證碼預測:預測驗證碼為:1555 真實驗證碼為:1555
第3輪ITERATION中第41個驗證碼預測:預測驗證碼為:8122 真實驗證碼為:8127
第3輪ITERATION中第42個驗證碼預測:預測驗證碼為:8201 真實驗證碼為:8201
第3輪ITERATION中第43個驗證碼預測:預測驗證碼為:9992 真實驗證碼為:9992
第3輪ITERATION中第44個驗證碼預測:預測驗證碼為:3473 真實驗證碼為:3473
第3輪ITERATION中第45個驗證碼預測:預測驗證碼為:4410 真實驗證碼為:4410
第3輪ITERATION中第46個驗證碼預測:預測驗證碼為:9092 真實驗證碼為:9097
第3輪ITERATION中第47個驗證碼預測:預測驗證碼為:0466 真實驗證碼為:0466
第3輪ITERATION中第48個驗證碼預測:預測驗證碼為:8261 真實驗證碼為:8261
第3輪ITERATION中第49個驗證碼預測:預測驗證碼為:2607 真實驗證碼為:2607
第3輪ITERATION中第50個驗證碼預測:預測驗證碼為:6106 真實驗證碼為:6106
第3輪ITERATION中第51個驗證碼預測:預測驗證碼為:5815 真實驗證碼為:5815
第3輪ITERATION中第52個驗證碼預測:預測驗證碼為:6280 真實驗證碼為:6280
第3輪ITERATION中第53個驗證碼預測:預測驗證碼為:1644 真實驗證碼為:1044
第3輪ITERATION中第54個驗證碼預測:預測驗證碼為:8612 真實驗證碼為:8612
第3輪ITERATION中第55個驗證碼預測:預測驗證碼為:9279 真實驗證碼為:9279
第3輪ITERATION中第56個驗證碼預測:預測驗證碼為:1343 真實驗證碼為:1343
第3輪ITERATION中第57個驗證碼預測:預測驗證碼為:4622 真實驗證碼為:4622
第3輪ITERATION中第58個驗證碼預測:預測驗證碼為:8134 真實驗證碼為:8134
第3輪ITERATION中第59個驗證碼預測:預測驗證碼為:9973 真實驗證碼為:9973
第3輪ITERATION中第60個驗證碼預測:預測驗證碼為:3271 真實驗證碼為:3271
第3輪ITERATION中第61個驗證碼預測:預測驗證碼為:2885 真實驗證碼為:2885
第3輪ITERATION中第62個驗證碼預測:預測驗證碼為:4650 真實驗證碼為:4650
第3輪ITERATION中第63個驗證碼預測:預測驗證碼為:5360 真實驗證碼為:5360
第3輪ITERATION中第64個驗證碼預測:預測驗證碼為:3173 真實驗證碼為:3173
第3輪ITERATION中第65個驗證碼預測:預測驗證碼為:4507 真實驗證碼為:4507
第3輪ITERATION中第66個驗證碼預測:預測驗證碼為:6417 真實驗證碼為:6417
第3輪ITERATION中第67個驗證碼預測:預測驗證碼為:5661 真實驗證碼為:5561
第3輪ITERATION中第68個驗證碼預測:預測驗證碼為:3953 真實驗證碼為:3923
第3輪ITERATION中第69個驗證碼預測:預測驗證碼為:1708 真實驗證碼為:1308
第3輪ITERATION中第70個驗證碼預測:預測驗證碼為:0859 真實驗證碼為:0269
第3輪ITERATION中第71個驗證碼預測:預測驗證碼為:4174 真實驗證碼為:4174
第3輪ITERATION中第72個驗證碼預測:預測驗證碼為:3115 真實驗證碼為:3115
第3輪ITERATION中第73個驗證碼預測:預測驗證碼為:6205 真實驗證碼為:6205
第3輪ITERATION中第74個驗證碼預測:預測驗證碼為:0214 真實驗證碼為:0214
第3輪ITERATION中第75個驗證碼預測:預測驗證碼為:7480 真實驗證碼為:7420
第3輪ITERATION中第76個驗證碼預測:預測驗證碼為:7284 真實驗證碼為:7234
第3輪ITERATION中第77個驗證碼預測:預測驗證碼為:0438 真實驗證碼為:0438
第3輪ITERATION中第78個驗證碼預測:預測驗證碼為:1800 真實驗證碼為:1802
第3輪ITERATION中第79個驗證碼預測:預測驗證碼為:7715 真實驗證碼為:7715
第3輪ITERATION中第80個驗證碼預測:預測驗證碼為:8345 真實驗證碼為:8345
第3輪ITERATION中第81個驗證碼預測:預測驗證碼為:3971 真實驗證碼為:3901
第3輪ITERATION中第82個驗證碼預測:預測驗證碼為:9980 真實驗證碼為:9980
第3輪ITERATION中第83個驗證碼預測:預測驗證碼為:2626 真實驗證碼為:7638
第3輪ITERATION中第84個驗證碼預測:預測驗證碼為:5136 真實驗證碼為:5136
第3輪ITERATION中第85個驗證碼預測:預測驗證碼為:1044 真實驗證碼為:1044
第3輪ITERATION中第86個驗證碼預測:預測驗證碼為:0115 真實驗證碼為:0115
第3輪ITERATION中第87個驗證碼預測:預測驗證碼為:4180 真實驗證碼為:4180
第3輪ITERATION中第88個驗證碼預測:預測驗證碼為:6279 真實驗證碼為:6279
第3輪ITERATION中第89個驗證碼預測:預測驗證碼為:2138 真實驗證碼為:2138
第3輪ITERATION中第90個驗證碼預測:預測驗證碼為:9046 真實驗證碼為:9040
第3輪ITERATION中第91個驗證碼預測:預測驗證碼為:4094 真實驗證碼為:4095
第3輪ITERATION中第92個驗證碼預測:預測驗證碼為:0784 真實驗證碼為:0784
第3輪ITERATION中第93個驗證碼預測:預測驗證碼為:6213 真實驗證碼為:6293
第3輪ITERATION中第94個驗證碼預測:預測驗證碼為:2110 真實驗證碼為:2110
第3輪ITERATION中第95個驗證碼預測:預測驗證碼為:2502 真實驗證碼為:2502
第3輪ITERATION中第96個驗證碼預測:預測驗證碼為:0681 真實驗證碼為:0681
第3輪ITERATION中第97個驗證碼預測:預測驗證碼為:8365 真實驗證碼為:8365
第3輪ITERATION中第98個驗證碼預測:預測驗證碼為:3618 真實驗證碼為:3578
第3輪ITERATION中第99個驗證碼預測:預測驗證碼為:3081 真實驗證碼為:3087
第3輪ITERATION中第100個驗證碼預測:預測驗證碼為:0940 真實驗證碼為:0940
第3輪ITERATION識別測試正確率:0.9300000071525574
第4輪ITERATION中第1個驗證碼預測:預測驗證碼為:2738 真實驗證碼為:2738
第4輪ITERATION中第2個驗證碼預測:預測驗證碼為:9349 真實驗證碼為:9379
第4輪ITERATION中第3個驗證碼預測:預測驗證碼為:0977 真實驗證碼為:0977
第4輪ITERATION中第4個驗證碼預測:預測驗證碼為:1922 真實驗證碼為:1922
第4輪ITERATION中第5個驗證碼預測:預測驗證碼為:8195 真實驗證碼為:8195
第4輪ITERATION中第6個驗證碼預測:預測驗證碼為:9096 真實驗證碼為:9096
第4輪ITERATION中第7個驗證碼預測:預測驗證碼為:3107 真實驗證碼為:3107
第4輪ITERATION中第8個驗