这里面大多资料均为网上参阅,参考资料过多未能记住您的文章地址望见谅,如涉及您的文章,本文未声明的即可留言,我会将您的原文地址引入。
本文主要是对DHL公司的验证码进行识别,如需识别其他验证码,需要找相关验证码进行训练。
机器学习是人工智能的一个子集,它是利用统计技术提供了向计算机“学习”数据的能力,而不需要复杂的编程。简单来说,机器学习可以被定义为一种科学,它使计算机像人类一样行动和学习,并通过以实际交互和观察的形式向他们提供信息和数据,以独立的方式提高他们的学习能力。机器学习鼓励各种行业的各种自动化跨度和任务,从分析恶意软件或数据安全公司到寻求有利交易的财务专家,都是机器学习的应用场景。常用的集中机器学习算法。例如:
通常,有3类学习算法:
与特定于任务的算法不同,深度学习是基于学习数据的机器学习的子集。它的灵感来自被称为人工神经网络的功能和结构。深度学习通过学习将世界显示为更简单的概念和层次结构,以及基于不那么抽象的概念来计算更抽象的代表,从而获得巨大的灵活性和广泛性。
换言之,机器学习只关注解决现实问题,通过对大量数据的统计,然后分析结果做出预判;深度学生在通过对大量书籍分析后,能提取样本的特征,然后让机器自己“记住”样本的共同特征,从而让机器能有自己的“思想”,使其在遇到有此类特征的样本后自己判定出结果。
近几年深度学习被很多人工智能爱好大肆宣称所深度学习也越来来越受到开发者的广泛关注,尤其是在一些特定领域,例如在自然语言处理、图像识别等应用上。
为了理解上述概念,举一个动物识别器的例子,它用于识别给定的图像是狮子还是鹿。当我们将此解决为传统的机器学习问题时,我们将涉及特定的特征,比如说给定的动物是否有耳朵,是否有胡须或任何其他器官。简单来说,我们将定义面部特征,让系统识别动物。另一方面,在深度学习中,从第一步开始。深度学习将自动对关键特征进行定义和分类。深度学习将首先确定找出狮子或鹿的最相关因素。稍后它将开始识别形状和边缘的组合,以更深入地识别对象。例如,如果对象有耳朵或者有胡须。在定义了这些概念的连续分层识别之后,它将决定哪些特征负责找到正确的答案。
总结:
tensorflow是由谷歌开发,使用比较广泛的深度学习框架,主要用来深度学习(机器学习也用)以及其他涉及大量运算,也是Github上最受欢迎的深度学习;tensorflow在图像分类、音频处理,推荐系统和自然语言处理等领域应用十分广泛,谷歌几乎涉及深度学习或者机器学习的项目,都在使用tensorflow作为运算框架;tensorflow之所以能收到广大开发者的青睐,离不开tensorflow底层优秀的封装,让开发人员能在不了解底层原理的情况下快速上手并且能直接开发。
用张量定义数据模型、把数据模型和操作定义在计算图中,使用会话运行计算,然后把计算定义在图上。
流(Flow)是指一个计算图或简单的一个图,图不能形成环路,图中的每个节点代表一个操作,如加法、减法等。每个操作都会导致新的张量形成
上图展示了一个简单的计算图,所对应的表达式为:
e = (a+b) x (b + 1)
记算图具有以下属性:
叶子顶点或起始顶点始终是张量。意即,操作永远不会发生在图的开头,由此我们可以推断图中的每个操作都应该接受一个张量并产生一个新的张量。同样,张量不能作为非叶子节点出现,这意味着它们应始终作为输入提供给操作/节点。
计算图总是以层次顺序表达复杂的操作。通过将a + b表示为c,将b + 1表示为d,可以分层次组织上述表达式。 因此,我们可以将e写为:
e = (c)x(d) 这里 c = a+b 且 d = b+1.
以反序遍历图形而形成子表达式,这些子表达式组合起来形成最终表达式。
当我们正向遍历时,遇到的顶点总是成为下一个顶点的依赖关系,例如没有a和b就无法获得c,同样的,如果不解决c和d则无法获得e。
同级节点的操作彼此独立,这是计算图的重要属性之一。当我们按照图中所示的方式构造一个图时,很自然的是,在同一级中的节点,例如c和d,彼此独立,这意味着没有必要在计算d之前计算c。 因此它们可以并行执行。
计算图的并行
Caffa、MXNET、Torch、Theano、CNTK、PaddlePaddle(百度开发)
注: tensorflow更多了解请点击
神经网络是一种模拟人脑的神经网络以期能够实现类人工智能的机器学习技术,每一个神经网络都是由简单的神经元(处理单元)成的大规模并行处理器。
下图是一个最基本的神经网(三层结构)的结构图:
卷积的概念:
卷积是对图像(不同的数据窗口数据)和滤波矩阵(一组固定的权重:因为每个神经元的多个权重固定,所以又可以看做一个恒定的滤波器filter)做内积(逐个元素相乘在求和)的操作就是所谓的卷积操作,也是卷积神经网络名字的来源。
下图中红框框起来的部分变可以理解为一个滤波器(filter),即带着一组固定权重的神经元。多个滤波器叠加起来便成了卷积层。
比如下图,左边部分是原始输入数据,图中中间部分是滤波器filter,图中右边是输出的新的二维数据。
中间滤波器filter与数据窗口做内积,其具体计算过程则是:4x0 + 0x0 + 0x0 + 0x0 + 0x1 + 0x1 + 0x0 + 0x1 + -4x2 = -8
图像上的卷积:
左边是图像输入,中间是滤波器filter(带着一组固定权重的神经元),不同的滤波器filter会得到不同的输出数据,比如颜色深浅、轮廓等特征。也是就是说如果想图区图像不同的特征,就需要不同的滤波器filter。
说明: 再具体应用中,往往有很多卷积核,可以认为,每个卷积核代表一种图像模式,如果某个图像块与此卷积核卷积出的值大,则认为此图像块十分接近于此图像十分接近于此卷积核。如果我们设计了6个卷积核,可以理解为:我们认为这个图像有6种底层纹理模式,也就是我们6种基础模式就能描绘出一幅图像。下图就是25种不同的卷积核示例:
因为验证码是图像验证码,所以可以将验证码识别过程理解为图像识别过程。
可分为两个步骤:
图像处理又分为:
在验证码识别的项目中,在对图像处理的时候主要用的是:图像采样、图像标注(也称打标签)、图像增强、图像分割。
图像采样: 自己写了一个程序,从所要识别网站上爬取验证码图片,采集的图片越多越好,训练的样本多了训练出的模型才能有很强的泛性,如果样本数太少模型容易出现过拟合现象,训练出的模型准确度就会很低。
图片标注: 将采集到的图片进行标注,这里我主要是采用人工的方式将采集到的验证命名为图片验证码的内容,如下图:
例如上图中我将图片的名字命名为8hwa7_1573194299620671.png
,"_"前面的是验证码的内容,后面的是随机的字符串或数字;如果涉及其他图像识别人脸识别其图片标注就跟这个不一样,如下图所示:
上图中使用专门的图片标注软件,在人脸上标注的年龄来达到通过人脸来识别年龄的目的。因为需要对大量的样本进行标注,而且大多都是人工标注,标注的是否准确直接想象生成模型的好坏,所以图片标注也是图像识别中最累最重要的一步,不光需要细心,还的有耐心。
图像增强: 图像增强也就是通过相应的算法,去掉图像的干扰项,使图像尽量把所要识别的部分凸显出来;例如上述验证码的干扰项一个是背景颜色,一个是验证码的干扰线;由于干扰线太粗跟字母粘连在一起所以干扰线很难去掉,所以只将图片的背景去掉。在做图像处理的时候为了避免颜色的干扰,要对图像进行灰度处理或者二值化处理,我这里为了让字母更加清晰,先将图片进行灰度处理,然后再二值化并进行去噪,然后将二值化的图片进行裁剪,将多余的本分裁减掉,处理后的照片如下图所示:
-
1)图像灰度处理:
img = cv.imread(image)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
这里采用的是opencv图像处理库,image是图片路径,使用opencv中的 cvtColor(src, code, dst=None, dstCn=None) 将图像转为灰度图
2)图像二值化:
ret, binary = cv.threshold(gray, 69.0, 255,cv.THRESH_BINARY)
gray为灰度处理返回的数组,69.0为二值化法阈值,255表示高于(低于)阈值时赋予的新值,cv.THRESH_BINARY表示返会得到灰白二值图。
3) 图像降噪
def depoint(img):
"""传入二值化后的图片进行降噪"""
pixdata = img.load()
w, h = img.size
for y in range(1, h - 1):
for x in range(1, w - 1):
count = 0
if pixdata[x, y - 1] > 245: # 上
count = count + 1
if pixdata[x, y + 1] > 245: # 下
count = count + 1
if pixdata[x - 1, y] > 245: # 左
count = count + 1
if pixdata[x + 1, y] > 245: # 右
count = count + 1
if pixdata[x - 1, y - 1] > 245: # 左上
count = count + 1
if pixdata[x - 1, y + 1] > 245: # 左下
count = count + 1
if pixdata[x + 1, y - 1] > 245: # 右上
count = count + 1
if pixdata[x + 1, y + 1] > 245: # 右下
count = count + 1
if count > 4:
pixdata[x, y] = 255
return img
这里img是PIL库中Image.open(“图片路径”)
方法返回的结果。
img = Image.open(image_path)
img = img.crop((0, 0, 200, 60))
将图片裁剪为200x60的大小
图像识别是将处理后的图像进行特征提取和分类。识别方法中基本也是常用的方法有统计法(或决策理论法)、句法(或结构)方法、神经网络法、模板匹配法和几何变换法。
在本项目中主要使用的是CNN卷积神经网络做为识别方法, 使用的CNN网络结构为:(卷积层 x 3)+(池化层 x 3)+ (全连接层 x 2)
def model(self):
# self 为当前类对象
x = tf.reshape(self.X, shape=[-1, self.image_height, self.image_width, 1])
print(">>> input x: {}".format(x))
# 卷积层1
wc1 = tf.get_variable(name='wc1', shape=[3, 3, 1, 32], dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
bc1 = tf.Variable(self.b_alpha * tf.random_normal([32]))
conv1 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(x, wc1, strides=[1, 1, 1, 1], padding='SAME'), bc1))
conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
conv1 = tf.nn.dropout(conv1, self.keep_prob)
# # 卷积层2
wc2 = tf.get_variable(name='wc2', shape=[3, 3, 32, 64], dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
bc2 = tf.Variable(self.b_alpha * tf.random_normal([64]))
conv2 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv1, wc2, strides=[1, 1, 1, 1], padding='SAME'), bc2))
conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
conv2 = tf.nn.dropout(conv2, self.keep_prob)
# 卷积层3
wc3 = tf.get_variable(name='wc3', shape=[3, 3, 64, 128], dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
bc3 = tf.Variable(self.b_alpha * tf.random_normal([128]))
conv3 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv2, wc3, strides=[1, 1, 1, 1], padding='SAME'), bc3))
conv3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
conv3 = tf.nn.dropout(conv3, self.keep_prob)
print(">>> convolution 3: ", conv3.shape)
next_shape = conv3.shape[1] * conv3.shape[2] * conv3.shape[3]
# 全连接层1
wd1 = tf.get_variable(name='wd1', shape=[next_shape, 1024], dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
bd1 = tf.Variable(self.b_alpha * tf.random_normal([1024]))
dense = tf.reshape(conv3, [-1, wd1.get_shape().as_list()[0]])
dense = tf.nn.relu(tf.add(tf.matmul(dense, wd1), bd1))
dense = tf.nn.dropout(dense, self.keep_prob)
# 全连接层2
wout = tf.get_variable('name', shape=[1024, self.max_captcha * self.char_set_len], dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
bout = tf.Variable(self.b_alpha * tf.random_normal([self.max_captcha * self.char_set_len]))
with tf.name_scope('y_prediction'):
y_predict = tf.add(tf.matmul(dense, wout), bout)
return y_predict
代码说明:
tf.reshape(self.X, shape=[-1, self.image_height, self.image_width, 1])
作用是将tensor变换为参数shape的形式。第一个参数表示值张量Tensor;第二这个参数也是一个张量Tensor,用于定义输出张量的形状类型必须是int32,int64;第三个参数表示操作名称(可选)tf.get_variable(name='wc1', shape=[3, 3, 1, 32], dtype=tf.float32, initializer=tf.contrib.layers.xavier_initializer())
,创建名字为wc1
,共享变量,shape为变量维度,dtype变量数据类型,initializer变量初始化方式tf.contrib.layers.xavier_initializer()
是一种带权重的初始化方法,旨在使所有图层的渐变比例大致相同。tf.Variable(self.b_alpha * tf.random_normal([32]))
获取具有self.b_alpha * tf.random_normal([32])
这些参数的新变量。tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(x, wc1, strides=[1, 1, 1, 1], padding='SAME'), bc1))
激活函数,tf.nn.bias_add(tf.nn.conv2d(x, wc1, strides=[1, 1, 1, 1], padding='SAME')
返回一个Tensor,作用是将偏差加到value(value:tf.nn.conv2d(x, wc1, strides=[1, 1, 1, 1], padding='SAME')
偏差:第三步返回的Tensorbc1
)上;tf.nn.conv2d(x, wc1, strides=[1, 1, 1, 1], padding='SAME')
将图像进行二维卷积,参数一:张量Tensor,参数二:卷积核参数,参数三:步长参数,参数四:卷积方式(可以取’VALID’ 或者’SAME’),返回一个Tensor。tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
池化操作,第一个参数:value(由第四步卷积后结果的代入),第二个参数:池化窗口大小,第三个参数:窗口每一个维度上滑动步长,低四个参数:池化方式(和卷及类似,可以取’VALID’ 或者’SAME’),结果返回一个Tensor。tf.nn.dropout(conv1, self.keep_prob)
计算dropout,也可以通过修改dropout的方式类避免过拟合现象;第一个参数:将经过池化后的Tensor,第二个参数:dropout的概率。进过算法及不断增加样本数量会(最终样本数量达5w多)识别模型准确度为:
上述文章中有一些概念上面没有解释,这里进行补充一下