Easy-Classification-驗證碼識別

2022-11-24 12:01:11

1.背景

Easy-Classification是一個應用於分類任務的深度學習框架,它整合了眾多成熟的分類神經網路模型,可幫助使用者簡單快速的構建分類訓練任務。
 
本例基於Easy-Classification框架,快速搭建一個驗證碼識別訓練任務。專案整體目錄如下:
  • 任務輸入:4位元大小寫字母和數位混合組成的驗證碼圖片, 圖片大小為100*40。
  • 任務輸出:識別影象中的字母和數位,並輸出驗證碼編碼。

2.驗證碼識別

2.1 生成訓練資料

在專案根目錄下新建data目錄用於放置訓練集,測試集,驗證集資料。驗證碼訓練資料基於指令碼模擬生成。執行scripts/make_captcha.py檔案,或make_captcha_1.py,make_captcha_3.py可批次生成驗證碼影象資訊。(實際選擇哪一種,看實際需要驗證碼識別影象的樣式,若本身存在訓練資料,可基於實際的訓練資料訓練)。
基於make_captcha.py檔案,最終生成訓練資料10000份,驗證資料5000份,最終模擬應用資料10000份。訓練驗證碼圖片如:
說明:
  1. 基於指令碼模擬驗證碼圖片,生成的驗證碼字元做大小寫區分。
  1. 每個驗證碼圖片,對應的驗證碼字串為圖片名稱,如00FS_69570.png,00FS是驗證碼字串,後面的是亂數避免檔案重名。

2.2 編寫訓練指令碼

訓練過程需編寫組態檔,自定義DateSet資料載入類,訓練過程指令碼類。一個影象中存在多個識別物件,考慮到最終是4個字元,本例基於one-hot模式,將驗證碼的label轉換為one-hot編碼。詳情請參考對應目錄下實現原始碼。
自定義DateSe部分核心程式碼說明:
"""
@Description : 構建Dataset類,不同的任務,dataset自行編寫,如基於csv,文字等載入標籤,均可從cfg組態檔中讀取後,自行擴充套件編寫
編寫自定義Dataset類時,初始化引數需定義為source_img, cfg。否則資料載入通用模組,data_load_service.py模組會報錯。
source_img :傳入的影象地址資訊
cfg:傳入的設定類資訊,針對不同的任務,可能生成的label模式不同,可基於設定類指定label的載入模式,最終為訓練的影象初始化label (使用者自定義實現)
本例為驗證碼載入類:基於檔名稱生成標籤(如驗證碼:0AaW_54463.png,標籤值為:0AaW,返回one-hot編碼)
"""import torch
from torch.utils.data.dataset import Dataset
import torchvision.transforms as transforms
import cv2
from universe.data_load.normalize_adapter import NormalizeAdapter
from PIL import Image
from universe.utils.utils import one_hot
classTrainDataset(Dataset):
"""
    構建一個 載入原始圖片的dataSet物件
    此函數可載入 訓練集資料,基於路徑識別驗證碼真實的label,label在轉換為one-hot編碼
    若 驗證集邏輯與訓練集邏輯一樣,驗證集可使用TrainDataset,不同,則需自定義一個,參考如下EvalDataset
    """def__init__(self, source_img, cfg):
        self.source_img = source_img
        self.cfg = cfg
        self.transform = createTransform(cfg, TrainImgDeal)
def__getitem__(self, index):
        img = cv2.imread(self.source_img[index])
if self.transform isnotNone:
            img = self.transform(img)
# ../ data / train\Qigj_73075.png        label = self.source_img[index].split("_")[0][-4:]
        target = torch.Tensor(one_hot(label))
return img, target, self.source_img[index]
def__len__(self):
returnlen(self.source_img)
classEvalDataset(Dataset):
"""
    構建一個 載入原始圖片的dataSet物件
    此函數可載入 驗證集資料,基於路徑識別驗證碼真實的label,label在轉換為one-hot編碼
    """def__init__(self, source_img, cfg):
        self.source_img = source_img
        self.cfg = cfg
# 若驗證集圖片處理邏輯(增強,調整)與 訓練集不同,可自定義一個EvalImgDeal        self.transform = createTransform(cfg, TrainImgDeal)
def__getitem__(self, index):
        img = cv2.imread(self.source_img[index])
if self.transform isnotNone:
            img = self.transform(img)
# ../ data / train\Qigj_73075.png        label = self.source_img[index].split("_")[0][-4:]
        target = torch.Tensor(one_hot(label))
return img, target, self.source_img[index]
def__len__(self):
returnlen(self.source_img)
classPredictDataset(Dataset):
"""
        構建一個 載入預測圖片的dataSet物件
        此函數可載入 測試集資料,應用集資料(返回影象資訊)
    """def__init__(self, source_img,cfg):
        self.source_img = source_img
# 若預測集圖片處理邏輯(增強,調整)與 訓練集不同,可自定義一個PredictImgDeal        self.transform = createTransform(cfg, TrainImgDeal)
def__getitem__(self, index):
        img = cv2.imread(self.source_img[index])
if self.transform isnotNone:
            img = self.transform(img)
# 用於記錄實際的label值(因為應用資料也是指令碼生成的,所以可以知道正確的驗證碼)        real_label = self.source_img[index].split("_")[0][-4:]
return img, real_label, self.source_img[index]
def__len__(self):
returnlen(self.source_img)
classTrainImgDeal:
def__init__(self, cfg):
        img_size = cfg['target_img_size']
        self.h = img_size[0]
        self.w = img_size[1]
def__call__(self, img):
        img = cv2.resize(img, (self.h, self.w))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(img)
return img
defcreateTransform(cfg, img_deal):
    my_normalize = NormalizeAdapter.getNormalize(cfg['model_name'])
    transform = transforms.Compose([
        img_deal(cfg),
        transforms.ToTensor(),
        my_normalize,
    ])
return transform

2.3 訓練結果展示

訓練結果會輸出到out目錄,輸出資訊包括acc,loss的過程圖,最優訓練權重檔案。
本例採用的網路模型為moblienetv3。

2.3.1 影象訓練

本例基於make_captcha.py驗證碼生成指令碼,批次生成訓練資料10000條,驗證資料10000條,預測資料6000條。訓練結果如下:

2.3.2 混合影象訓練

由於訓練影象與驗證影象是基於不同的驗證碼指令碼生成的影象,將這些影象混合在一起訓練。由於訓練資料不算多,目前訓練結果準確率不高。
三種不同的驗證碼指令碼生成的驗證碼圖片,在影象清晰度,間隔,影象複雜情況不一樣。訓練資料10000條,驗證資料5000條,預測資料10000條,預載入權重檔案,訓練結果如下:
輸出訓練紀錄檔參考資訊:
 1/100  [9600/10000 (96%)] - ETA: 0:00:19, loss: 0.0003, acc: 0.9234  LR: 0.001000 
           [VAL] loss: 0.00012, acc: 81.060% 

    2/100  [9600/10000 (96%)] - ETA: 0:00:23, loss: 0.0000, acc: 0.9977  LR: 0.001000 
           [VAL] loss: 0.00011, acc: 82.000% 

    3/100  [9600/10000 (96%)] - ETA: 0:00:20, loss: 0.0000, acc: 0.9978  LR: 0.001000 
           [VAL] loss: 0.00012, acc: 79.520% 

    4/100  [9600/10000 (96%)] - ETA: 0:00:18, loss: 0.0000, acc: 0.9888  LR: 0.001000 
           [VAL] loss: 0.00013, acc: 78.020% 

    5/100  [9600/10000 (96%)] - ETA: 0:00:18, loss: 0.0000, acc: 0.9824  LR: 0.001000 
           [VAL] loss: 0.00012, acc: 80.260% 

    6/100  [9600/10000 (96%)] - ETA: 0:00:19, loss: 0.0000, acc: 0.9903  LR: 0.001000 
           [VAL] loss: 0.00013, acc: 80.040% 

    7/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9923  LR: 0.000100 
           [VAL] loss: 0.00010, acc: 83.900% 

    8/100  [9600/10000 (96%)] - ETA: 0:00:20, loss: 0.0000, acc: 0.9977  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.280% 

    9/100  [9600/10000 (96%)] - ETA: 0:00:18, loss: 0.0000, acc: 0.9987  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.400% 

   10/100  [9600/10000 (96%)] - ETA: 0:00:20, loss: 0.0000, acc: 0.9992  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.600% 

   11/100  [9600/10000 (96%)] - ETA: 0:00:19, loss: 0.0000, acc: 0.9993  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.460% 

   12/100  [9600/10000 (96%)] - ETA: 0:00:19, loss: 0.0000, acc: 0.9995  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.600% 

   13/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9998  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 85.100% 

   14/100  [9600/10000 (96%)] - ETA: 0:00:19, loss: 0.0000, acc: 0.9996  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.720% 

   15/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9998  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 85.140% 

   16/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9998  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.720% 

   17/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9999  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 85.220% 

   18/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9999  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.900% 

   19/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9999  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.980% 

   20/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 1.0000  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 85.280% 

   21/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9999  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 85.140% 

   22/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 1.0000  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 85.140% 

   23/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 0.9998  LR: 0.000100 
           [VAL] loss: 0.00009, acc: 84.880% 

   24/100  [9600/10000 (96%)] - ETA: 0:00:20, loss: 0.0000, acc: 1.0000  LR: 0.000100 
           [VAL] loss: 0.00010, acc: 85.120% 

   25/100  [9600/10000 (96%)] - ETA: 0:00:20, loss: 0.0000, acc: 1.0000  LR: 0.000010 
           [VAL] loss: 0.00009, acc: 85.160% 

   26/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 1.0000  LR: 0.000010 
           [VAL] loss: 0.00009, acc: 85.180% 

   27/100  [9600/10000 (96%)] - ETA: 0:00:21, loss: 0.0000, acc: 1.0000  LR: 0.000010 
           [VAL] loss: 0.00009, acc: 85.220% 

[INFO] Early Stop with patient 7 , best is Epoch - 20 :0.852800
--------------------------------------------------
{'model_name': 'mobilenetv3', 'GPU_ID': '', 'class_number': 248, 'random_seed': 42, 'cfg_verbose': True, 'num_workers': 8, 'train_path': 'data/train', 'val_path': 'data/val', 'test_path': 'data/test', 'label_type': 'DIR', 'label_path': '', 'pretrained': 'output/mobilenetv3_e21_0.84700.pth', 'try_to_train_items': 10000, 'save_best_only': True, 'save_one_only': True, 'save_dir': 'output/', 'metrics': ['acc'], 'loss': 'CE', 'show_heatmap': False, 'show_data': False, 'target_img_size': [224, 224], 'learning_rate': 0.001, 'batch_size': 64, 'epochs': 100, 'optimizer': 'Adam', 'scheduler': 'default-0.1-3', 'warmup_epoch': 0, 'weight_decay': 0, 'k_flod': 5, 'start_fold': 0, 'early_stop_patient': 7, 'use_distill': 0, 'label_smooth': 0, 'class_weight': None, 'clip_gradient': 0, 'freeze_nonlinear_epoch': 0, 'dropout': 0.5, 'mixup': False, 'cutmix': False, 'sample_weights': None, 'model_path': '../../config/weight/mobilenet/mobilenetv3_e22_1.00000.pth', 'TTA': False, 'merge': False, 'test_batch_size': 1}
--------------------------------------------------

Process finished with exit code 0

 

2.4 預測應用

編寫預測類指令碼,在組態檔中,設定model_path(為訓練好的權重檔案路徑如:),載入預測資料,模型預測後將結果輸出到csv檔案中。預測程式碼參考如下:
def predict(cfg):
    initConfig(cfg)
    model = ModelService(cfg)
    data = DataLoadService(cfg)

    test_loader = data.getPredictDataloader(PredictDataset)

    runner = RunnerCaptchaService(cfg, model)
    modelLoad(cfg['model_path'])
    res_dict = runner.predict(test_loader)
    print(len(res_dict))

    # to csv
    res_df = pd.DataFrame.from_dict(res_dict, orient='index', columns=['label'])
    res_df = res_df.reset_index().rename(columns={'index': 'image_id'})
    res_df.to_csv(os.path.join(cfg['save_dir'], 'pre.csv'),
                  index=False, header=True)


if __name__ == '__main__':
    predict(cfg)

2.4.1 影象訓練-預測結果

訓練影象,驗證影象,預測影象均由make_captcha.py指令碼生成,實際預測6000張影象,本次識別準確度100%。

2.4.2 混合影象模式-預測結果

本例的訓練影象,驗證影象採用了不同的驗證碼指令碼生成,準確率較低,10000張影象,有3373張識別失敗。
  1. 驗證碼識別中,大小寫字母(如x,X),這種容易識別失敗。
  1. 當訓練資料偏少時,對於o,O,0,這種看起來相似的字母也容易識別錯誤。若訓練的資料中某一個字母的影象較少,識別該字母的準確度也偏低。所以,本質還是儘可能的豐富訓練資料。
  1. 驗證碼識別,基於特定場景,基於指令碼構建的訓練資料,測試資料一定要儘可能的與實際的影象相似(清晰度,字母間的間隔),這樣準確度才高。參考上面的兩種測試,若訓練的驗證碼影象與最終預測的影象相似,則準確度高。

 

本例是基於數位,大小寫組合的驗證碼,驗證碼識別可滿足大多數場景,也可以改造支援如下特殊的場景:
  1. 字元驗證碼不區分大小寫:本演演算法不變,一樣支援。
  1. 多字元驗證碼識別:基於具體的位數,如6位驗證碼,調整分類數值為62*6,改造one-hot,調整為6位,調整acc匹配函數,即可訓練支援。
  1. 純數位驗證碼:改造one-hot函數,現有演演算法的每一位字串,均存在62種情況(10個數位,52個大小寫字母),數位版本只存在10種情況,比現有模式更簡單,調整輸出分類數值為40,改造one-hot,調整acc匹配函數,即可訓練支援。
  1. 簡單數學計算驗證碼識別:如5-1=?,4+3=?,這種模式的驗證碼識別,本質還是簡單字元識別,
第一位字元存在10種情況(0-9的數位),
第二位存在4種情況(+-*/),
第三位存在10種情況(0-9數位),
調整輸出分類數值為24,改造one-hot,調整acc匹配函數,即可訓練支援。識別到對應的數位和運算方法後,做簡單數學計算即可算出最終的驗證碼數學計算結果。