当前位置: 首页 > 后端技术 > Python

knn识别简单验证码

时间:2023-03-26 16:47:23 Python

参考https://www.biaodianfu.com/kn...内容大致相同,只是根据自己的想法做了一些改动。KNN(k近邻算法)原理请看:https://www.biaodianfu.com/kn...先说说sklearn中knn的属性和方法sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,weights='uniform',algorithm='auto',leaf_size=30,p=2,metric='minkowski',metric_params=None,n_jobs=None)n_neighbors:knn中的K值weights:样本权重数组algorithm:算法used,有{'auto','ball_tree','kd_tree','brute'}leaf_size:使用'ball_tree'和'kd_tree'时的属性(不懂,无视他)p:距离选择,p=2是欧氏距离,默认为2metric:距离测量方法,和参数p有什么关系和区别?我还不明白。metric_params:距离附加参数,如{'w':weights,'p':2}n_jobs:使用的CPU数量,默认-1表示这些参数全部用于识别数字验证码你只需要注意n_neighbors,其他保持默认。方法fit(x,y):使用样本x和标签y进行训练。其实knn的训练只是把数据保存下来get_params(deep=True):获取模型的所有参数。不知道deepkneighbors(test_x=None,n_neighbors=None,return_distance=True)有什么用:返回距离训练样本最接近样本test_x的n_neighbors个样本的值和距离,return_distance是是否返回thedistancekneighbors_graph(x=None,n_neighbors=None,mode='connectivity'):返回x中k个相邻点对应的权重或距离,根据模式选择'connectivity'或'distance'predict(test_x):根据对样本test_x,返回预测ypredict_proba(test_x):返回样本test_x属于各个类别的概率,也就是说返回的是一个二维数组,维度为(样本数,k),其和一维数组每行元素全为1,数组长度为k。score(test_x,y,sample_weight=None):根据样本test_x预测test_y,然后比较实际y返回的正确分数,sample_weight为权重set_params(**args):重新设置模型参数当然,knn分类器还有RadiusNeighborsClassifier,不同的是,KNeighborsClassifier是找到最近的K个样本,然后投票决定x的类别,而RadiusNeighborsClassifier是根据x半径r范围内的所有样本投票决定x的类别。数据预处理如果你对图中的数组不清楚,可以看另外一篇博客下载验证码https://download.csdn.net/ind...有一个地方要注意,如果你直接请求这个接口,你会得到只是一个HTML源码,但是在浏览器上查看时,是验证码,F12查看时,也是返回的验证码。但是我用抓包工具抓包发现居然发了两个请求。第一次请求是更新cookie,第二次是真正的返回验证码。链接相同,但cookie不同。我们只需要保存第二次请求的cookie,使用requests去请求即可。已经下载:https://www.lanzous.com/i8enhah基本操作im=Image.open(img)im_gray=im.convert('L')#灰度图像pix=np.array(im_gray)#二进制值Threshold=180#Thresholdpix=(pix2:print(k)split_pix=new_pix[:,L[i-1]:L[i]+1]print(split_pix.shape)#7是根据实际值判断的,大部分都是9,所以需要统一大小如果k==7:tmp=np.zeros((18,10))tmp+=255tmp[:,1:-1]=split_pixout=Image.fromarray(tmp).convert('L')out.save(f'1/{uuid.uuid4()}.jpg')如果split_pix。shape!=(18,10):continueout=Image.fromarray(split_pix).convert('L')out.save(f'1/{uuid.uuid4()}.jpg')但是当我把这个方法当应用于所有图像时,将有少量连接字符。最后我直接选择了指定的区间来切字。该值是通过实际测试得到的。代码如下:img1=new_pix[:,3:13]out=Image.fromarray(img1).convert('L')out.save('1.jpg')img2=new_pix[:,12:22]out=Image.fromarray(img2).convert('L')out.save('2.jpg')img3=new_pix[:,21:31]out=Image.fromarray(img3).convert('L')out.save('3.jpg')img4=new_pix[:,30:40]out=Image.fromarray(img4).convert('L')out.save('4.jpg')手动标注是最烦人的部分,这是浪费时间。我为每个字符注释了120张图像,这花了我一个小时。那么我一个人怎么可能享受这种幸福呢。从sklearn生成模型importneighborsimportosfromPILimportImageimportnumpyasnpiportshutilx=[]y=[]forlabelinos.listdir('train'):forfileinos.listdir(f'train/{label}'):im=Image.open(f'train/{label}/{file}')pix=np.array(im)pix=(pix>180)*1pix=pix.ravel()x.append(list(pix))y.append(int(label))train_x=np.array(x)train_y=np.array(y)model=neighbors.KNeighborsClassifier(n_neighbors=10)model.fit(train_x,train_y)x=[]y=[]forlabelinos.listdir('test'):forfileinos.listdir(f'test/{label}'):im=Image.open(f'test/{label}/{file}')pix=np.array(im)pix=(pix>180)*1pix=pix.ravel()x.append(list(pix))y.append(int(label))predict_y=model.predict(np.array(x))print(predict_y==np.array(y))这里我把所有的像素值作为图片的特征,一共18x10=180个特征值。按照博客开头的说法,我们可以取每行黑色像素的个数得到10个特征,每列黑色像素的个数得到6个特征。这只剩下16个特征。计算时间会有一些改善。不过由于图片很小,数量也不多,所以实际测试时的时间差只有几秒。而180个特征训练基本100%正确率,16个特征会出现个别判断错误,但正确率都在98%以上。当然实际应用中必须选择16个特征,这个错误率是可以接受的。以下是16个特征的代码:fromsklearnimportneighboursimportosfromPILimportImageimportnumpyasnpx=[]y=[]forlabelinos.listdir('train'):forfileinos.listdir(f'train/{label}'):x_=[]im=Image.open(f'train/{label}/{file}')pix=np.array(im)pix=(pix>180)*1foriinrange(18):x_.append(np.sum(pix[i]==0))forjinrange(10):x_.append(np.sum(pix[:,j]==0))x.append(x_)y.append(int(label))train_x=np.array(x)train_y=np.array(y)model=neighbors.KNeighborsClassifier(n_neighbors=10)model.fit(train_x,train_y)test_x=[]复制代码test_y=[]forlabelinos.listdir('test'):forfileinos.listdir(f'test/{label}'):x_=[]im=Image.open(f'test/{label}/{file}')pix=np.array(im)pix=(pix>180)*1foriinrange(18):x_.append(np.sum(pix[i]==0))forj在范围(10)内:x_.append(np.sum(pix[:,j]==0))test_x.append(x_)test_y.append(int(label))predict_y=model.predict(x)print(predict_y==test_y)以为我一开始为每个字符标记了120个样本,那么如果减少样本数,会不会影响准确率,不影响多少呢?我们看一下随着样本数的增加,score的变化(左侧数字表示每个字符的样本数,右侧:10.0708661417322834620.401574803149606330.685039370078740240.80314960606299212650.85826771671671653543543333161.061.061.061.07andandandandandtomentyandandandandandandandandandandandandandandandandandandandand。.我们在看KNN的k对正确率的影响:11.021.031.041.051.061.071.081.091.0,好像k选什么无所谓。这是因为样本类别太少,特征明显。当然,这种验证码的识别只是练习,KNN只能用于简单的验证码。真正复杂的验证码还是需要CNN来识别。这是标注的数据:https://www.lanzous.com/i8epywd最后在学习一些机器学习算法,会把一些需要记录的内容分享到博客和微信上公众号(python成长之路),欢迎关注。平时,我一般会分享一些爬虫或者Python的内容。