核心思想:类似的输入必会产生类似的输出。node
原理:首先从训练样本矩阵中选择第一个特征进行划分,使每一个子表中该特征的值所有相同(好比第一个特征是男女,则能够划分出两个子表,男表和女表),而后再在每一个子表中选择下一个特征按照一样的规则继续划分更小的子表(好比第二个特征是年龄,我能够划分红三个子表(固然根据状况的不一样而不一样),小于18,大于18小于60,大于60,则在男女表中分别又有三个子表,每一个子表下的特征值都相同),不断重复直到全部的特征所有使用完为止,此时便获得叶级子表,其中全部样本的特征值所有相同。python
解释:决策树是一种分类方法,用于对样本的特征分类。而分类完成以后,获得的结果是同一类(或者称为表)的全部特征基本相同,而后根据某一类的全部样本经过平均(回归)或者投票(分类)获得一个输出。那么,当有新的待预测样本须要预测输出时,我只需知道样本属于哪一个类(表)。算法
工程优化(剪枝):没必要用尽全部的特征,叶级子表中容许混杂不一样的特征值,以此下降决策树的层数,在精度牺牲可接受的前提下,提升模型的性能。一般状况下,能够优先选择使信息熵减小量最大的特征做为划分子表的依据。(通俗的讲就是有些特征值并不区分,好比第一个特征是男女,我并不分红两个表,而是放在一个表里,这种状况通常是男女这个特征对输出的影响不大),如何区分有用特征和无用特征或者说影响不大的特征呢?经过信息熵或基尼指数来区分。也能够用PCA和ICA等方法对特征先进行降维操做。api
class sklearn.tree.DecisionTreeClassifier()
参数数组
前面使用的验证码特征和类别对应过于明显,因此咱们选择接口的另外一种验证码,即70x25大小的,以下:
虽然一样很简单,可是加入了字符。
至于预处理和数字验证码同样,正常验证码->灰度图->二值化->切割->标注。不过通过测试发现,不管我如何调参,准确率都比较低。看了全部的字符才发现,图片的字符虽然没有倾斜变形但有粗体和细体的区别,而我在标注的时候并无严格让粗体和细体的样本数同样。并且字符的位置不在图片的中间,字符大小也不同,有的偏上,有的偏下,有的偏小,有的又偏大。即便从新标注的准确率仍是难达到我要的标准。微信
对于这种分割线和字符边缘明显的验证码来讲,咱们能够将字符从切割后的图片中提取出来,也就是去掉边缘外的空白,而后都调整到同样的大小。这样就去掉了字符位置和大小对算法的干扰,至于粗体和细体,只要保证这两个的训练样本数量相同就能够了。代码以下:app
def img_preprocess(file): img1 = Image.open(file) pix = np.array(img1) pix = (pix > 180) * 255 width, height = pix.shape for i in range(width): if np.sum(pix[i]==0): xstart = i break for i in range(width-1, 0, -1): if np.sum(pix[i]==0): xend = i + 1 break for i in range(height): if np.sum(pix[:,i]==0): ystart = i break for i in range(height-1, 0, -1): if np.sum(pix[:,i]==0): yend = i + 1 break new_pix = pix[xstart:xend, ystart:yend] img = Image.fromarray(new_pix).convert('L') if new_pix.size != (8, 10): img = img.resize((8, 10), resample=Image.NEAREST) img.save(file)
接着咱们使用决策树从新训练样本并调整参数,咱们先看max_depth这个参数,代码以下:dom
from sklearn.tree import DecisionTreeClassifier import os from PIL import Image import numpy as np import matplotlib.pyplot as mp def func(k): x = [] y = [] for label in os.listdir('train'): for file in os.listdir(f'train/{label}'): im = Image.open(f'train/{label}/{file}') pix = np.array(im) pix = (pix > 180) * 1 pix = pix.ravel() x.append(list(pix)) y.append(label) train_x = np.array(x) train_y = np.array(y) model = DecisionTreeClassifier(max_depth=k) model.fit(train_x, train_y) x = [] y = [] for label in os.listdir('test'): for file in os.listdir(f'test/{label}'): im = Image.open(f'test/{label}/{file}') pix = np.array(im) pix = (pix > 180) * 1 pix = pix.ravel() x.append(list(pix)) y.append(label) test_x = np.array(x) test_y = np.array(y) score = model.score(test_x, test_y) return score if __name__ == "__main__": os.chdir('G:\\knn\\字符验证码\\') x = list(range(1, 15)) y = [func(i) for i in x] mp.scatter(x, y) mp.show()
运行结果:
能够看到当max_depth=8的时候,准确率已经很接近1了,因此咱们直接将max_depth取8就好了。既然识别的准确率已经接近1,其余的参数调不调整好像并不重要了,不过由于这是验证码的识别,不容易出现过拟合的状况,在其余状况下,若是准确率接近1就更要去调整随机参数(random_state和splitter)和剪枝参数(min_samples_leaf等)来防止过拟合。我后面也试着调整了一下其余参数,发现模型的准确率变化不大,默认便可。机器学习
训练测试数据集:https://www.lanzous.com/i8joo0f性能
最后,我正在学习一些机器学习的算法,对于一些我须要记录的内容我都会分享到博客和微信公众号(python成长路),欢迎关注。平时的话通常分享一些爬虫或者Python的内容。