人工智能基于神经网络的模式识别(神经网络入门自动缺陷识别技术科普)
人工智能基于神经网络的模式识别(神经网络入门自动缺陷识别技术科普)表1、 “与”操作的真值表MP神经元可以用于构建感知机。感知机由两层神经元组成,输入层和输出层。输入层用于接收信号,输出层使用MP神经元,用于处理从输入层获得的信号以获得输出信号。对于简单的逻辑运算就可以使用感知机实现。比如对于一个“与”的逻辑运算,两个输入都为1时,输出1,否则输出0,其真值表如表1所示。图1、MP神经元模型可能出乎很多人意料的是,现代神经网络中的神经元和MP神经元的结构并无太多变化,最大的区别是使用的激活函数不同,MP神经元中使用的是跃迁函数,如图2所示,它将输入值映射为输出值“0”或“1”,分别对应生物神经元抑制状态和兴奋状态。现代神经网络中的神经元中使用的激活函数通常要求是连续可导的,以便于使用基于梯度下降的方法对神经元参数的优化,常用的激活函数有Sigmoid函数、ReLU函数等。另一方面上,MP神经元的权重、阈值需要研究者提前设定好,而现代神经网络中的神经元参数
摘要
神经网络作为如今机器学习的主流方法论,也是自动缺陷识别中的重要技术,它的历史、原理因何而来?它又是如何进行应用的?本文通过一个简单的识别案例进行了概述。
神经网络基础想要了解人工神经网络(Artificial Neural Network,ANN),需要先从组成神经网络的神经元讲起。19世界末20世纪初,卡米洛·高尔基、圣地亚哥·拉蒙·卡哈尔等人通过染色法发现生物体中的神经元后,研究者对其结构和功能进行了深入的研究,发现神经元细胞有树突和轴突两种突起:树突短而分枝多,直接由细胞体扩张突出,形成树枝状,可以接受来自其他神经元轴突的神经冲动,并传递给细胞体,当神经元接收到足够多的神经冲动后,它就处于兴奋状态,产生电脉冲;轴突长而分枝少,为粗细均匀的细长突起,可以将细胞体产生电脉冲传递给其他神经元。
根据生物神经元的这一结构,1943年心理学家沃伦·麦卡洛克(Warren McCulloch)和数学家沃尔特·皮茨(Walter Pitts)提出了MP神经元模型,在该模型中,神经元接受n个输入信号、、……,按照一定权重将所有输入求和后,通过激活函数(Activation Function)处理后,就获得了神经元的输出,如图1所示。
图1、MP神经元模型
可能出乎很多人意料的是,现代神经网络中的神经元和MP神经元的结构并无太多变化,最大的区别是使用的激活函数不同,MP神经元中使用的是跃迁函数,如图2所示,它将输入值映射为输出值“0”或“1”,分别对应生物神经元抑制状态和兴奋状态。现代神经网络中的神经元中使用的激活函数通常要求是连续可导的,以便于使用基于梯度下降的方法对神经元参数的优化,常用的激活函数有Sigmoid函数、ReLU函数等。另一方面上,MP神经元的权重、阈值需要研究者提前设定好,而现代神经网络中的神经元参数通常都是使用梯度反向传播训练的方式得到的,毕竟在现代神经网络中,往往有成千上万的神经元,人工设定每个神经元的参数几乎是不可能实现的,这是MP神经元与现代神经元之间在思想上的重大差别。
图2、跃迁函数
MP神经元可以用于构建感知机。感知机由两层神经元组成,输入层和输出层。输入层用于接收信号,输出层使用MP神经元,用于处理从输入层获得的信号以获得输出信号。对于简单的逻辑运算就可以使用感知机实现。比如对于一个“与”的逻辑运算,两个输入都为1时,输出1,否则输出0,其真值表如表1所示。
表1、 “与”操作的真值表
如何构建一个使用跃迁函数作为激活函数的感知机呢?关键在于选取MP神经元的参数(权重和偏置),这里我们选择一组参数=0.5,=0.5,=-0.8,感知机结构如图3所示。
图3、感知机结构
类似地,对于“或”的逻辑运算,只要有一个输入信号是1,输出就为1,否则输出0,其真值表如表2所示,可以为MP神经元选取如下参数=0.5,=0.5,=-0.3,即可解决“或”的逻辑运算问题。
表2、 “或”操作的真值表
对于“异或”的逻辑运算,仅当或中的一方为1时,才会输出1,真值表如表3所示,由于“异或”问题属于线性不可分问题,此时使用单层感知器模型就无法解决了。要想解决这一问题,可以增加感知机的层数,在输入层和输出层中间增加一层神经元,即隐藏层,就能增强感知机的表达能力,如图4所示,此时感知机中三个MP神经元的参数分别是=1,=-1,=-0.5;=-1,=1,=-0.5;=1,=1,=-0.5。
表3、 “异或”操作的真值表
图4、“异或”的逻辑运算的感知机结构
在此基础上,可以增加隐藏层的数量,并调整每个隐藏层中神经元的个数(输入层和输出层的神经元数量是确定的),即形成多层感知器(Multi-Layer Perceptron,MLP)。更一般的,使用连续激活函数的多层感知器,各神经元分别属于不同的层。每一层的神经元可以接收前一层神经元的信号,并产生信号输出到下一层。第一层称为输入层,最后一层称为输出层,其他中间层称为隐藏层。整个网络中没有同层连接,也没有跨层连接,信号从输入层向输出层单向传播,称之为多层前馈神经网络,如图5所示。
图5、多层前馈神经网络
根据通用近似定理(Universal approximation theorem),前馈神经网络可以用来近似任意函数的,并且可以达到任意近似精准度。但它并没有告诉我们如何选择神经网络参数(权重、神经元数量、神经层层数等等)来达到我们想近似的目标函数。在实际的应用中,通过增加神经元数量和神经网络层数,可以增加神经网络的拟合能力,但是这样很容易出现过拟合的问题,需要使用者根据情况不断调整神经网络的结构,直至获得合适的网络结构。
我们以MNIST手写数字图片数据集为例,介绍前馈神经网络的应用。MNIST数据集共包含0~9共10类数字,每个类别有7000张图片。这些数字已经通过尺寸标准化并位于图像中心,图像是固定大小(28×28像素),如图6所示。对于人类来说,能轻而易举的分辨出每张图片上的数字是什么,但是如何让机器也能分辨出图片上的数字呢?我们通过建立一个前馈神经网络来实现这一点,其包含一个输入层,一个输出层和一个隐藏层,结构如图7所示。这里为简单起见,模型输入的每张图像都被平展并转换为784(28×28)的一维向量,因此输入层包含784个神经元,代表28×28图像中每个像素的值,输入层包含10个神经元,使用Softmax激活函数,代表图像属于0~9这10个数字的概率,中间的隐藏层包含512个神经元,使用ReLU激活函数。可以看出,该网络的结构与之前解决“异或”问题的感知器的结构十分类似,都是单隐藏层的前馈神经网络,区别在于神经网络中每层神经元的数量不同,以及使用的激活函数不同。
图6、MNIST 数据集的输入样例
图7、手写数字识别网络
这里我们使用keras(一个用python编写的高级神经网络API)来建立上述的神经网络。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense Dropout
from tensorflow.python.keras.utils import np_utils
from tensorflow.keras.datasets import mnist
# 载入数据
(X_train y_train) (X_test y_test) = mnist.load_data()
# 将X展开为向量,并归一化
X_train = X_train.reshape(len(X_train) -1)
X_test = X_test.reshape(len(X_test) -1)
X_train = X_train.astype('float32')/255
X_test = X_test.astype('float32')/255
# 将y展开为one-hot变量
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
# 使用Sequential构架网络
model = Sequential()
model.add(Dense(512 input_shape=(28*28 ) activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10 activation='softmax'))
model.compile(optimizer='adam' loss='categorical_crossentropy' metrics=['accuracy'])
history = model.fit(X_train y_train epochs=10 batch_size=64 verbose=1 validation_split=0.1)
Testloss Testaccuracy = model.evaluate(X_test y_test verbose=0)
print('Testloss:' Testloss)
print('Testaccuracy:' Testaccuracy)
训练得到了前馈神经网络后,可以尝试使用该模型来识别手写数字。我们手写得到一个数字,如图8所示,并转化为类似MNIST数据集的输入(图9),作为模型的测试数据。
图8、手写数字4
图9、手写数字4(MNIST风格)
使用我们得到的模型分辨图中数字,得到如下结果:(2.679e-07 1.832e-06 2.604e-05 2.938e-06 9.977e-01 2.816e-05 1.313e-05 8.561e-04 3.261e-06 1.326e-03),表示该数字是0~9的概率,可以看出,模型认为数字是4的概率很大,说明训练得到的模型能后完成手写数字识别的问题。
卷积网络基本组件-卷积从前文手写数字识别的例子可以看出,使用前馈神经网络,我们可以解决一些简单的问题,然而使用全连接前馈网络来处理图像问题时,可能会存在一些问题。在MNIST的例子中,输入的是28×28的小图片,因此网络输入层有784个神经元,然而如果想在更大的图像上建立神经网络,其输入层的神经元的数量也要随之增加。比如一张1000×1000×3的图片(图像高度为1000,宽度为1000以及RGB的3个颜色通道),其输入层的神经元高达300万个,第一个隐藏层的每个神经元都与输入层300万个神经元相连接,每对连接都对应一个权重参数,假如第一个隐藏层有10000个神经元(远小于输入层的神经元),则从输入层到第一个隐藏层之间的连接参数就有300亿个。如此巨大规模的参数给神经网络的训练带来了巨大的困难。
为了解决这一问题,研究者使用稀疏连接代替了全连接,其中最具代表性的是卷积操作。假设输入图像是一个6×6的矩阵,而卷积核是一个3×3的矩阵,如图10所示。
图10、输入图像和卷积核
首先将输入图像的(0,0)元素和卷积核的(0,0)元素对齐,卷积核中参数与对应位置图像像素逐位相乘后累加作为第一次卷积操作结果,即输出图像(0,0)处的元素,然后将卷积核沿着输入图像从左至右自上而下依次移动(移动的步长通常为1),最后获得4×4的输出图像(如图11所示)。
图11、卷积操作示例
选取不同的卷积核,就能获得不同的输出结果,我们可以使用边缘检测的例子阐述卷积的思想。通过特定的卷积操作,可以检测输入图像中的物体边缘,具有代表性的就是Sobel算子,如图12所示。Sobel算子其通过检测图片中的灰度的一阶导数来寻找边缘。在之前手写数字图片(图9)上,使用Sobel算子进行卷积操作,输出图像如图13所示,分别是水平和竖直方向上的边缘。
图12、Sobel算子
图13、水平和竖直方向上的边缘
在数字图像处理中,除了Sobel算子,还有Prewitt算子、Scharr算子等边缘检测算子,其思想与Sobel算子类似,都是通过在输入图像上的卷积实现了对边缘的检测。
这里我们可以比较卷积操作和全连接操作的区别。对于一张n×n的图片,其边缘检测结果也是n×n的图片,如果考虑使用全连接的前馈神经网络,则需要n²×n²个参数(不考虑偏置),输出结果图中每个像素的值与输入图所有像素都有关系。然而对于卷积操作,输出结果图中每个像素的值仅与输入图中k×k个像素有关(k为卷积核的尺寸),卷积需要的参数仅为k×k个,远小于全连接的参数量,这就体现了卷积操作稀疏连接、参数共享的特点。
实际上,在使用卷积的网络中,卷积核的参数是通过学习确定的,而非事先指定好的(像Sobel算子),通过在数据上的学习,一个卷积核不仅能检测水平或竖直的边缘,还能检测其他角度的边缘,通过卷积核的堆叠,还可以检测更复杂的图像特征,这正是卷积操作的厉害之处。
卷积网络基本组件-池化池化是卷积神经网络中另一个常用的组件,我们以最大池化(Max Pooling)为例,介绍如何进行池化操作。下图是一个6×6的输入图像,对其进行2×2的最大池化,首先考虑输入图像(0,0)元素为左上角的2×2区域,其中最大值作为池化的输出(7),随后向右移动池化的区域,考虑下一个2×2区域的最大值,作为下一个输出(9),以此类推,进行从左到右,从上到下的移动(移动的步长通常为池化核的尺寸,这里是2),这与卷积操作十分类似,不同的地方在于卷积计算中有参数,而池化计算中没有参数的参与。
图14、池化操作示例
显然,当池化的步长超过1时,池化结果的尺寸将小于输入图像的尺寸,这自然有利于减少中间特征图大小和参数的多少,起到信息浓缩的作用,提高后续计算的效率。更重要的是,池化同时引入了少许平移不变性,在输入进行少许平移时,池化输出结果基本不变,这增加了神经网络提取特征的鲁棒性,尤其是当我们关心某个特征是否出现而不关心它出现的具体位置时。
在实践过程中,池化往往紧接着卷积层,这在很多实验中都有较好的效果,但是池化并不是卷积神经网络必需的元件或操作,通过其他方式也能建立具有类似效果的神经网络。
卷积和池化中的填充和步幅从上面的例子可以看出,图像卷积、池化后的输出的尺寸可能与输入的尺寸不同,这主要受到填充(padding)和步幅(stride)两个参数影响。在卷积的例子中,我们用3×3的卷积核卷积6×6的输入图像,最后获得到4×4的输出图像,一般来说,如果输入图像的尺寸是n×n,卷积核的大小是k×k,则输出图像的尺寸是(n-k 1)×(n-k 1),在k大于1的情况下,输出图像就小于输入图像了。如果在图像上连续进行大于1的卷积,则图像会随着卷积的进行越来越小,直至图像尺寸小于卷积核,在这一过程中,图像包含的信息也在不断降低。
除此之外,图像边缘的像素和中间的像素对卷积结果的影响是不同的,如图15所示,输入图像左上角像素(1)只影响卷积结果左上角的1个像素,而输入图像中间的像素(7)则影响卷积结果左上角的9个像素,这意味着在卷积过程中,可能会丢失许多输入图像边界的信息。
图15、卷积操作示例:边缘像素和中央像素的区别
为了解决这个问题,我们可以在卷积操作之前,在输入图像的边缘填充一些像素(通常填充像素0),使得边缘像素的信息能充分保留。例如,在6×6的输入图像的边缘填充1层0,使图像的尺寸变为了8×8,此时再使用3×3的卷积核进行卷积,则获得6×6卷积结果,如图16所示。如果在n×n输入图像的四周先填充p层像素,再进行k×k的卷积,则输出图像的尺寸是(n 2p-k 1)×(n 2p-k 1)。在卷积核尺寸k是奇数的情况,取p=(k-1)/2,则输出图像的尺寸与输出相同。
图16 带有填充的卷积操作示例
填充像素的多少通常有两种模式,在深度学习框架分别称为“Valid”和“Same”卷积。在“Valid”模式下,填充量p=0,输出图像尺寸小于输入图像,在“Same”模式下,填充量使得输出图像尺寸等于输入图像,当卷积核尺寸k是奇数,p=(k-1)/2,当卷积核尺寸k是偶数,则一侧填充p1=[(k-1)/2],另一侧填充p2=(k-1)/2-[(k-1)/2],此时的填充是不对称的。在计算机视觉中,k通常是奇数。
影响输出尺寸的另一个因素是步幅。在介绍卷积和池化时,我们已经分别使用了stride=1和stride=2的步幅。在卷积和池化,使用大于1的步幅意味着对输入图像进行下采样,在卷积中通常使用1作为步幅,在池化中通常使用池化大小做步幅。当然,在有需求的情况下,也可以选择其他值作为步幅,例如很多卷积网络中使用stride=2的卷积代替池化进行下采样。
考虑了填充和池化之后,n×n的图像与k×k的卷积核进行卷积,输出图像的尺寸是([(n 2p-k)/s] 1)×([(n 2p-k)/s] 1),通过控制卷积和池化过程中的填充和池化,我们可以准确控制输出图像的尺寸。
小结通过组合卷积、池化和之前介绍的全连接层和激活函数,我们就能搭建起各种各样的卷积网络,从而将原始数据(通常是图像)映射到高层语义,进而实现各种预测的功能。
封面来源:Global Tech Outlook
作者:宗艺
工学硕士,适创科技图像算法工程师,曾先后在清华大学材料学院取得学士和硕士学位,求学期间从事金属材料智能设计研究。
编辑:田恒易