当前位置: 首页 > 科技观察

教你用TensorFlow2识别验证码

时间:2023-03-17 12:07:58 科技观察

验证码是根据随机字符生成一张图片,然后在图片中加入干扰像素。用户必须手动填写,以防止有人使用机器人自动批量注册、注水、发送垃圾广告等。数据集来源:https://www.kaggle.com/fournierp/captcha-version-2-images图像是可以包含数字的5个字母的单词。这些图像应用了噪声(模糊和线条)。它们是200x50PNG。我们的任务是尝试制作光学字符识别算法的模型。对于数据集中的验证码png图片,对应的label就是图片的名称。importosimportnumpyasnpimportpandasaspdimportcv2importmatplotlib.pyplotaspltimportseabornassns#imgaug图片数据增强importimgaug.augmentersasiaaimporttensorflowastf#Conv2DMaxPooling2DDropoutFlattenDenseBNGAPfromtensorflow.keras.layersimportConv2D,MaxPooling2D,Dropout,Flatten,Dense,Layer,BatchNormalization,GlobalAveragePooling2Dfromtensorflow.keras.optimizersimportAdamfromtensorflow.kerasimportModel,Inputfromtensorflow.keras.callbacksimportEarlyStopping,ReduceLROnPlateau#图片处理器fromtensorflow.keras.preprocessing.imageimportImageDataGeneratorimportplotly.expressaspximportplotly.graph_objectsasgoimportplotly.offlineaspyopyo.init_notebook_mode()对数据进行了简单的分析,统计图像中出现了什么样的符号。#数据路径DIR='../input/captcha-version-2-images/samples/samples'#存放验证码的标签captcha_list=[]characters={}forcaptchainos.listdir(DIR):captcha_list.append(captcha)#每个验证码的captcha_codecaptcha_code=captcha.split(".")[0]foriincaptcha_code:#traversecaptcha_codecharacters[i]=characters.get(i,0)+1symbols=list(characters.keys())len_symbols=len(symbols)print(f'Only{len_symbols}symbolsareusedintheimage')plt.bar(*zip(*characters.items()))plt.title('Frequencyofsymbols')plt.show()如何提取image数据创建X,y??#如何提取图片创建modelX的shape1070*50*200*1#y的shape5*1070*19fori,captchainenumerate(captcha_list):captcha_code=captcha.split('.')[0]#cv2.IMREAD_GRAYSCALE灰度captcha_cv2=cv2.imread(os.path.join(DIR,captcha),cv2.IMREAD_GRAYSCALE)#scalecaptcha_cv2=captcha_cv2/255.0#print(captcha_cv2.shape)(50,200)#设置captcha_cv2的(50,200)切换为(50,200,1)captcha_cv2=np.reshape(captcha_cv2,img_shape)#(5,19)targs=np.zeros((len_captcha,len_symbols))fora,binenumerate(captcha_code):targs[a,symbols.index(b)]=1X[i]=captcha_cv2y[:,i]=目标print("shapeofX:",X.shape)print("shapeofy:",y.shape)输出如下Numpy随机选取数据,划分训练集和测试集#从numpy生成随机数。randomimportdefault_rngrng=default_rng(seed=1)test_numbers=rng.choice(1070,size=int(1070*0.3),replace=False)X_test=X[test_numbers]X_full=np.delete(X,test_numbers,0)y_test=y[:,test_numbers]y_full=np.delete(y,test_numbers,1)val_numbers=rng.choice(int(1070*0.7),size=int(1070*0.3),replace=False)X_val=X_full[val_numbers]X_train=np.delete(X_full,val_numbers,0)y_val=y_full[:,val_numbers]y_train=np.delete(y_full,val_numbers,1)在这里验证在代码数据中,容易出现过拟合。大家可能会想到加入更多的新数据,加入正则化项等等,但是这里我们使用数据增强的方式,尤其是对于机器视觉任务。数据增强技术尤为重要和常用的数据增强操作:imgaug库。imgaug是一个提供各种图像增强操作的python库https://github.com/aleju/imgaug。imgaug包括几乎所有主流的数据增强图像处理操作。增强方法详见github#Sequential(C,R)大小增加5倍。#选择一系列sub-enhancersC作用于每张图片的位置,第二个A参数表示是否对每批图片应用不同顺序的Augmenterlist#rotate=(-8,8)旋转#iaa.CropAndPad截取(crop)或padding(pad),填充时,填充的区域为黑色。#px:需要裁剪(负值)或填充(正值)的像素。#(top,right,bottom,left)#当pad_mode=constant时,选择填充值aug=iaa.Sequential([iaa.CropAndPad(px=((0,10),(0,35),(0,10),(0,35)),pad_mode=['edge'],pad_cval=1),iaa.Rotate(rotate=(-8,8))])X_aug_train=Noney_aug_train=y_trainforiinrange(40):X_aug=aug(images=X_train)ifX_aug_trainisnotNone:X_aug_train=np.concatenate([X_aug_train,X_aug],axis=0)y_aug_train=np.concatenate([y_aug_train,y_train],axis=1)else:X_aug_train=X_aug让我们看看一些数据增强训练图像.图,ax=plt.subplots(nrows=2,ncols=5,figsize=(16,16))foriinrange(10):index=np.random.randint(X_aug_train.shape[0])ax[i//5][i%5].imshow(X_aug_train[index],cmap='gray')这次使用函数式API创建模型。FunctionalAPI是另一种创建模型的方式,它具有更大的灵活性,包括创建更复杂的模型。需要确定输入和输出#函数式API模型创建captcha=Input(shape=(50,200,channels))x=Conv2D(32,(5,5),padding='valid',activation='relu')(captcha)x=MaxPooling2D((2,2),padding='same')(x)x=Conv2D(64,(3,3),padding='same',activation='relu')(x)x=MaxPooling2D((2,2),padding='same')(x)x=Conv2D(128,(3,3),padding='same',activation='relu')(x)maxpool=MaxPooling2D((2,2),padding='same')(x)outputs=[]foriinrange(5):x=Conv2D(256,(3,3),padding='same',activation='relu')(maxpool)x=MaxPooling2D((2,2),padding='same')(x)x=Flatten()(x)x=Dropout(0.5)(x)x=BatchNormalization()(x)x=Dense(64,activation='relu')(x)x=Dropout(0.5)(x)x=BatchNormalization()(x)x=Dense(len_symbols,activation='softmax',name=f'char_{i+1}')(x)outputs.append(x)model=Model(inputs=captcha,outputs=outputs)#ReduceLROnPlateau更新学习率reduce_lr=ReduceLROnPlateau(patience=3,factor=0.5,verbose=1)model.compile(loss='categorical_crossentropy',optimizer=Adam(learning_rate=0.0005),metrics=["accuracy"])#EarlyStopping用于提前停止训练的callbacks具体可以在训练集上的loss不减少的情况下实现earlystopping=EarlyStopping(monitor="val_loss",mode="min",patience=10,min_delta=1e-4,restore_best_weights=True)history=model.fit(X_train,[y_train[i]foriinrange(5)],batch_size=32,epochs=30,verbose=1,validation_data=(X_val,[y_val[i]foriinrange(5)]),callbacks=[earlystopping,reduce_lr])下面是对模型的测试和评估。score=model.evaluate(X_test,[y_test[0],y_test[1],y_test[2],y_test[3],y_test[4]],verbose=1)metrics=['loss','char_1_loss','char_2_loss','char_3_loss','char_4_loss','char_5_loss','char_1_acc','char_2_acc','char_3_acc','char_4_acc','char_5_acc']fori,jinzip(metrics,score):print(f'{i}:{j}')具体输出如下:11/11[===================================]-0s11ms/step-loss:0.7246-char_1_loss:0.0682-char_2_loss:0.1066-char_3_loss:0.2730-char_4_loss:0.2636-char_5_loss:0.0132-char_1_accuracy:0.9844-char_2_accuracy:0.9657-char_3_accuracy:2_04accuracy-0.9657-char_6._6.09-0char_5_accuracy:0.9938loss:0.7246273756027222char_1_loss:0.06818050146102905char_2_loss:0.10664034634828568char_3_loss:0.27299806475639343char_4_loss:0.26359987258911133char_5_loss:0.013208594173192978char_1_acc:0.9844236969947815char_2_acc:0.9657320976257324char_3_acc:0.940809965133667char_4_acc:0.9626168012619019char_5_acc:0.9937694668769836字母1到字母5的精确值都大于绘制loss和scoremetrics_df=pd.DataFrame(历史。历史)列=[colforcolinmetrics_df.columnsif'loss'incolandlen(col)>8]fig=px.line(metrics_df,y=columns)fig.show()plt.figure(figsize=(15,8))plt.plot(history.history['loss'])plt.plot(history.history['val_loss'])plt.title('modelloss')plt.ylabel('loss')plt.xlabel('epoch')plt.legend(['train','val'],loc='upperright',prop={'size':10})plt.show()#预测数据defpredict(captcha):captcha=np.reshape(captcha,(1,50,200,channels))result=model.predict(captcha)result=np.reshape(result,(5,len_symbols))#取出输出label=''.join([symbols[np.argmax(i)]foriinresult])returnlabelpredict(X_test[2])#25277预测以下所有数据actual_pred=[]foriinrange(X_test.shape[0]):actual=''.join([symbols[i]foriin(np.argmax(y_test[:,i],axis=1))])pred=predict(X_test[i])actual_pred.append((actual,pred))print(actal_pred[:10])输出如下:[('n4b4m','n4b4m'),('42nxy','42nxy'),('25257','25277'),('cewnm','cewnm'),('w46ep','w46ep'),('cdcb3','edcb3'),('8gf7n','8gf7n'),('nny5e','nny5e'),('gm2c2','gm2c2'),('g7fmc','g7fmc')]sameCount=0diffCount=0letterDiff={i:0foriinrange(5)}incorrectness={i:0foriinrange(1,6)}forreal,predinactual_pred:#预测和输出相同ifreal==pred:sameCount+=1else:#failurediffCount+=1#遍历incorrectnessPoint=0foriinrange(5):ifreal[i]!=pred[i]:letterDiff[i]+=1incorrectnessPoint+=1incorrectness[incorrectnessPoint]+=1x=['Truepredicted','Falsepredicted']y=[sameCount,diffCount]fig=go.Figure(data=[go.Bar(x=x,y=y)])fig.show()在预测的数据中,一共有287条数据预测正确这里,我们可以看到是什么哪个指标错了。x1=["字符"+str(x)forxinrange(1,6)]fig=go.Figure(data=[go.Bar(x=x1,y=list(letterDiff.values()))])fig.show()要计算每个单词的错误数,请绘制相关的条形图。x2=[str(x)+"incorrect"forxinincorrectness.keys()]y2=list(incorrectness.values())fig=go.Figure(data=[go.Bar(x=x2,y=y2)])fig.show()绘制错误的验证码图片如下,区分标准正确与错误。fig,ax=plt.subplots(nrows=8,ncols=4,figsize=(16,20))count=0fori,(actual,pred)inenumerate(actual_pred):ifactual!=pred:img=X_test[i]try:ax[count//4][count%4].imshow(img,cmap='gray')ax[count//4][count%4].title.set_text(pred+'-'+actual)count+=1除外:通过