Python盤記念幣系列之二:識別驗證碼 04

上一篇咱們對圖片作了預處理,構建了數據集,今天咱們就要用這個數據集來訓練神經網絡了。html

學習數據集

咱們拿到任何一個數據集都要先進行觀察。一是咱們本身要學會分辨,這樣才能更有針對性的指導神經網絡來分類;二是要看咱們要處理的問題的複雜度,這樣也是便於瞭解咱們的神經網絡要有多複雜(或者多「深」)。python

上圖是咱們的數據集的截圖。觀察發現「0」、「1」、「9」,「I」,「O」這五個字符是沒有圖片的,那是咱們的數據集錯了嗎?檢查原始的驗證碼圖片發現,確實沒有這幾個字符。其實認真想一下就能知道,這幾個都是容易與別的字符產生混淆的字符,因此大機率是在生成驗證碼的時候就能夠把它們剔除了,在這裏也要爲這個程序員的細心點個贊~另外,觀察還發現每一個字符文件夾下面的圖片數量是差很少的,這樣也是爲了讓神經網絡能不偏不倚的爲每個字符尋找最優的參數。git

設計神經網絡

說了這麼多,終於要開始設計神經網絡了。用Python編寫神經網絡的庫有不少,好比TensorFlowPyTorchKeras等等,這裏咱們不討論各自的優劣勢,個人工做中用的是Keras,因此這裏咱們採用Keras程序員

由於是圖像分類,因此咱們使用在圖像類任務中最經常使用到的神技——卷積神經網絡(CNN)。github

from keras.layers import Flatten, Input, Dropout, Conv2D, MaxPooling2D, Dense
from keras.models import Model
from keras.optimizers import Adam

def model(input_size, class_num):
    input = Input(shape=input_size)
    x = Conv2D(16, (3,3), activation='relu', padding='same')(input)
    x = MaxPooling2D((2,2), strides=(2,2))(x)
    x = Conv2D(64, (3,3), activation='relu', padding='same')(input)
    x = MaxPooling2D((2,2), strides=(2,2))(x)
    x = Conv2D(256, (3,3), activation='relu', padding='same')(input)
    x = MaxPooling2D((2,2), strides=(2,2))(x)
    x = Flatten()(x)
    x = Dense(1024, activation='relu')(x)
    x = Dropout(0.5)(x)
    x = Dense(2048, activation='relu')(x)
    x = Dropout(0.5)(x)
    x = Dense(class_num, activation='softmax')(x)
    model = Model(input=input, output = x)
    model.compile(optimizer=Adam(lr=1e-4), loss='categorical_crossentropy', metrics=['accuracy'])
    return model
複製代碼

這基本上就是一個最簡單的CNN了,模型結構大體以下圖:markdown

就是簡單的卷積-池化-卷積-池化-卷積-池化-全鏈接-全鏈接-dropout結構,由於問題很簡單,因此模型結構不須要多複雜。網絡

訓練神經網絡

網絡設計好了,就能夠準備開始訓練了,也就是想辦法把訓練圖片喂到模型裏面讓它自動更新各項參數。由於咱們前期其實已經作好了部分工做,因此只須要按照類別讀取圖片,而後輸入到模型中區便可,讀取圖片並生成標籤的代碼以下:app

image_path = './chars'
data = []
labels = []
imagePaths = []

for label in os.listdir(image_path):
    for image in os.listdir(os.path.join(image_path, label)):
        imagePaths.append(os.path.join(image_path, label, image))

# 拿到圖像數據路徑,方便後續讀取
imagePaths = sorted(imagePaths)
random.seed(42)
random.shuffle(imagePaths)

# 遍歷讀取數據
for imagePath in imagePaths:
    # 讀取圖像數據
    image = cv2.imread(imagePath, 0)
    image = cv2.resize(image, (16, 16))
    image = np.expand_dims(image, axis=-1)
    data.append(image)
    # 讀取標籤
    label = imagePath.split(os.path.sep)[-2]
    labels.append(label)

# 對圖像數據作scale操做
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)

# 數據集切分
(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42)

# 轉換標籤爲one-hot encoding格式
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)
複製代碼

訓練模型的代碼以下:dom

print("------準備訓練網絡------")
# 設置初始化超參數
EPOCHS = 50
BS = 16
# 創建卷積神經網絡
model = model(input_size=(16,16,1), class_num=31)
H = model.fit(trainX, trainY, validation_data=(testX, testY), epochs=EPOCHS, batch_size=BS)
複製代碼

訓練模型的代碼反而最少,是否是發現訓練一個神經網絡其實根本就不難。ide

來看一下訓練神經網絡時的輸出:

Train on 332 samples, validate on 111 samples
Epoch 1/50

 16/332 [>.............................] - ETA: 7s - loss: 3.4399 - accuracy: 0.0625
 32/332 [=>............................] - ETA: 4s - loss: 3.4547 - accuracy: 0.0312
 48/332 [===>..........................] - ETA: 3s - loss: 3.4442 - accuracy: 0.0208
 64/332 [====>.........................] - ETA: 2s - loss: 3.4401 - accuracy: 0.0312
 80/332 [======>.......................] - ETA: 2s - loss: 3.4368 - accuracy: 0.0250
 96/332 [=======>......................] - ETA: 2s - loss: 3.4366 - accuracy: 0.0208
112/332 [=========>....................] - ETA: 1s - loss: 3.4371 - accuracy: 0.0179
128/332 [==========>...................] - ETA: 1s - loss: 3.4373 - accuracy: 0.0156
144/332 [============>.................] - ETA: 1s - loss: 3.4358 - accuracy: 0.0139
160/332 [=============>................] - ETA: 1s - loss: 3.4337 - accuracy: 0.0188
176/332 [==============>...............] - ETA: 1s - loss: 3.4330 - accuracy: 0.0170
192/332 [================>.............] - ETA: 1s - loss: 3.4310 - accuracy: 0.0156
208/332 [=================>............] - ETA: 0s - loss: 3.4313 - accuracy: 0.0192
224/332 [===================>..........] - ETA: 0s - loss: 3.4325 - accuracy: 0.0179
240/332 [====================>.........] - ETA: 0s - loss: 3.4300 - accuracy: 0.0208
256/332 [======================>.......] - ETA: 0s - loss: 3.4315 - accuracy: 0.0195
272/332 [=======================>......] - ETA: 0s - loss: 3.4334 - accuracy: 0.0184
288/332 [=========================>....] - ETA: 0s - loss: 3.4341 - accuracy: 0.0208
304/332 [==========================>...] - ETA: 0s - loss: 3.4349 - accuracy: 0.0197
320/332 [===========================>..] - ETA: 0s - loss: 3.4315 - accuracy: 0.0281
332/332 [==============================] - 2s 7ms/step - loss: 3.4340 - accuracy: 0.0271 - val_loss: 3.4193 - val_accuracy: 0.0270
複製代碼

神經網絡會在運行每個Epoch時更新參數,這樣不停更新,最後達到最優:

Epoch 50/50

 16/332 [>.............................] - ETA: 1s - loss: 0.0155 - accuracy: 1.0000
 32/332 [=>............................] - ETA: 1s - loss: 0.0132 - accuracy: 1.0000
 48/332 [===>..........................] - ETA: 1s - loss: 0.0259 - accuracy: 1.0000
 64/332 [====>.........................] - ETA: 1s - loss: 0.0289 - accuracy: 1.0000
 80/332 [======>.......................] - ETA: 1s - loss: 0.0247 - accuracy: 1.0000
 96/332 [=======>......................] - ETA: 1s - loss: 0.0271 - accuracy: 1.0000
112/332 [=========>....................] - ETA: 1s - loss: 0.0251 - accuracy: 1.0000
128/332 [==========>...................] - ETA: 1s - loss: 0.0243 - accuracy: 1.0000
144/332 [============>.................] - ETA: 1s - loss: 0.0230 - accuracy: 1.0000
160/332 [=============>................] - ETA: 1s - loss: 0.0234 - accuracy: 1.0000
176/332 [==============>...............] - ETA: 0s - loss: 0.0318 - accuracy: 0.9943
192/332 [================>.............] - ETA: 0s - loss: 0.0372 - accuracy: 0.9896
208/332 [=================>............] - ETA: 0s - loss: 0.0354 - accuracy: 0.9904
224/332 [===================>..........] - ETA: 0s - loss: 0.0395 - accuracy: 0.9866
240/332 [====================>.........] - ETA: 0s - loss: 0.0521 - accuracy: 0.9833
256/332 [======================>.......] - ETA: 0s - loss: 0.0491 - accuracy: 0.9844
272/332 [=======================>......] - ETA: 0s - loss: 0.0531 - accuracy: 0.9816
288/332 [=========================>....] - ETA: 0s - loss: 0.0510 - accuracy: 0.9826
304/332 [==========================>...] - ETA: 0s - loss: 0.0488 - accuracy: 0.9836
320/332 [===========================>..] - ETA: 0s - loss: 0.0488 - accuracy: 0.9844
332/332 [==============================] - 2s 6ms/step - loss: 0.0478 - accuracy: 0.9849 - val_loss: 0.0197 - val_accuracy: 0.9910
複製代碼

下面是整個訓練過程當中,各項參數值的曲線:

簡單的,就是在訓練過程當中,不管是訓練集仍是驗證集,它們的損失值不斷降低到無限接近於0,而模型的準確率則無限接近於1.

測試神經網絡

咱們隨便拿兩個字符來進行測試:

測試代碼以下:

# 加載測試數據並進行相同預處理操做
image = cv2.imread('./test_chars/3/1.jpg', 0)
output = image.copy()
image = cv2.resize(image, (16, 16))
# scale圖像數據
image = image.astype("float") / 255.0
image = np.expand_dims(image, axis=-1)
# 對圖像進行拉平操做
image = image.reshape((1, image.shape[0], image.shape[1],image.shape[2]))
# 讀取模型和標籤
print("------讀取模型和標籤------")
model = load_model('./output/cnn.model')
lb = pickle.loads(open('./output/cnn_lb.pickle', "rb").read())
# 預測
preds = model.predict(image)
# 獲得預測結果以及其對應的標籤
i = preds.argmax(axis=1)[0]
label = lb.classes_[i]
# 在圖像中把結果畫出來
text = "{}: {:.2f}%".format(label, preds[0][i] * 100)
print(text)
複製代碼

輸出結果爲:

再試一張:

輸出結果爲:

兩次實驗的結果都代表,咱們的神經網絡模型的性能是能夠的。

後記

至此,驗證碼的識別就講完了。

本系列的全部源代碼都會放在下面的github倉庫裏面,有須要能夠參考,有問題歡迎指正,謝謝!

https://github.com/TitusWongCN/AutoTokenAppointment
複製代碼

獲得最新消息,最新的記念幣將於本月19號開始預定,因此本系列也立刻會在這個時間左右完結。敬請期待最後的自動預定部分~


第一期:Python盤記念幣系列之一:簡介

第二期:Python盤記念幣系列之二:識別驗證碼 01

第三期:Python盤記念幣系列之二:識別驗證碼 02

第四期:Python盤記念幣系列之二:識別驗證碼 03

相關文章
相關標籤/搜索