作者:mobiledu2502852923 | 来源:互联网 | 2023-09-11 17:22
目录
介绍
什么是CNN?
设计CNN
实现CNN
训练CNN
下一步?
介绍
如果您看过《少数派报告》电影,您可能还记得汤姆·克鲁斯(Tom Cruise)走进一家Gap商店的场景。视网膜扫描仪读取他的眼睛,并为他播放定制的广告。好吧,这是2020年。我们不需要视网膜扫描仪,因为我们拥有人工智能(AI)和机器学习(ML)!
在本系列中,我们将向您展示如何使用深度学习进行面部识别,然后基于被识别的面部,使用神经网络语音合成(TTS)引擎播放自定义广告。
我们假设您熟悉AI/ML的基本概念,并且可以找到使用Python的方法。
在本文中,我们将讨论CNN,然后设计一个并使用Keras在Python中实现它。
什么是CNN?
CNN是一种神经网络(NN),通常用于图像分类任务(例如人脸识别)以及任何其他输入具有网格状拓扑的问题。在CNN中,并非每个节点都连接到下一层的所有节点。换句话说,它们不是完全连接的NN。这有助于防止完全连接的NN中出现过拟合问题,更不用说由NN中的过多连接导致的超慢收敛。
CNN的概念依赖于称为卷积的数学运算,这在数字信号处理领域非常普遍。卷积被定义为两个函数(第三个函数)的乘积,表示前两个函数之间的重叠量。在CNN区域,通过在图像中滑动过滤器(即内核)来实现卷积。
在人脸识别中,卷积操作使我们能够检测图像中的不同特征。不同的过滤器可以检测垂直和水平边缘、纹理、曲线和其他图像特征。这就是为什么任何CNN中的第一层都是卷积层的原因。
CNN中另一个常见的层是池化层。池化用于减小图像表示的大小,这意味着减少了参数数量,并最终减少了计算量。最常见的池化类型是“max”,它使用滑动窗口(类似于卷积操作中的窗口)在每个位置从匹配的单元格组中获取最大值。最后,它根据收获的最大值构建图像的新表示形式。
最常见的CNN架构通常从卷积层开始,然后是激活层,然后是池化层,最后以传统的全连接网络(例如多层NN)结束。这种层次化的模型称为顺序模型。为什么最后要建立全连接网络?要学习变换图像中特征的非线性组合(在卷积和合并之后)。
设计CNN
这是我们将在CNN中实现的架构:
- 输入层——NumPy数组(img_width,img_height,1);“ 1” 因为我们正在处理灰度图像;对于RGB图像,应该是(img_width,img_height,3)
- Conv2D层——32个过滤器,过滤器大小为3
- 激活层——必须使用非线性函数进行学习,在这种情况下,该函数为ReLU
- Conv2D层——32个过滤器,过滤器大小为3,步幅为3
- 使用ReLU功能的激活层
- MaxPooling2D层——应用(2,2)合并窗口
- 25%的DropOut层——通过从前一层中随机删除一些值(将它们设置为0)来防止过度拟合;又名稀释技术
- Conv2D层——64个过滤器,过滤器大小为3
- 使用ReLU功能的激活层
- Conv2D层——64个过滤器,过滤器大小为3,步幅为3
- 使用ReLU功能的激活层
- MaxPooling2D层——应用(2,2)合并窗口
- DropOut层,占25%
- 展平层——转换要在下一层使用的数据
- 致密层——代表完全连接的传统NN
- 使用ReLU功能的激活层
- DropOut层,占25%
- 密集层,节点数与问题中的类数匹配——Yale数据集为15
- 使用ReLU功能的激活层
上面的架构很常见;层参数已通过实验进行了微调。
实现CNN
现在,让我们在代码中实现我们的CNN架构-我们选择的一组图层。为了创建易于扩展的解决方案,我们将ML模型与一组抽象方法结合使用:
class MLModel(metaclass=abc.ABCMeta):def __init__(self, dataSet=None):if dataSet is not None:self.objects = dataSet.objectsself.labels = dataSet.labelsself.obj_validation = dataSet.obj_validationself.labels_validation = dataSet.labels_validationself.number_labels = dataSet.number_labelsself.n_classes = dataSet.n_classesself.init_model()@abstractmethoddef init_model(self):pass@abstractmethoddef train(self):pass@abstractmethoddef predict(self, object):pass@abstractmethoddef evaluate(self):score = self.get_model().evaluate(self.obj_validation, self.labels_validation, verbose=0)print("%s: %.2f%%" % (self.get_model().metrics_names[1], score[1] * 100))@abstractmethoddef get_model(self):pass
在我们的例子中,dataset是本系列上一篇文章中描述的FaceDataSet类的实例。ConvolutionalModel类,它从MLModel中继承和实现其所有的抽象方法,是一个将包含我们的CNN架构的类。这里是:
class ConvolutionalModel(MLModel):def __init__(self, dataSet=None):if dataSet is None:raise Exception("DataSet is required in this model")self.shape = numpy.array([constant.IMG_WIDTH, constant.IMG_HEIGHT, 1])super().__init__(dataSet)self.cnn.compile(loss=constant.LOSS_FUNCTION,optimizer=Common.get_sgd_optimizer(),metrics=[constant.METRIC_ACCURACY])def init_model(self):self.cnn = Sequential()self.cnn.add(Convolution2D(32, 3, padding=constant.PADDING_SAME, input_shape=self.shape))self.cnn.add(Activation(constant.RELU_ACTIVATION_FUNCTION))self.cnn.add(Convolution2D(32, 3, 3))self.cnn.add(Activation(constant.RELU_ACTIVATION_FUNCTION))self.cnn.add(MaxPooling2D(pool_size=(2, 2)))self.cnn.add(Dropout(constant.DROP_OUT_O_25))self.cnn.add(Convolution2D(64, 3, padding=constant.PADDING_SAME))self.cnn.add(Activation(constant.RELU_ACTIVATION_FUNCTION))self.cnn.add(Convolution2D(64, 3, 3))self.cnn.add(Activation(constant.RELU_ACTIVATION_FUNCTION))self.cnn.add(MaxPooling2D(pool_size=(2, 2)))self.cnn.add(Dropout(constant.DROP_OUT_O_25))self.cnn.add(Flatten())self.cnn.add(Dense(constant.NUMBER_FULLY_CONNECTED))self.cnn.add(Activation(constant.RELU_ACTIVATION_FUNCTION))self.cnn.add(Dropout(constant.DROP_OUT_0_50))self.cnn.add(Dense(self.n_classes))self.cnn.add(Activation(constant.SOFTMAX_ACTIVATION_FUNCTION))self.cnn.summary()def train(self, n_epochs=20, batch=32):self.cnn.fit(self.objects, self.labels,batch_size=batch,epochs=n_epochs, shuffle=True)def get_model(self):return self.cnndef predict(self, image):image = Common.to_float(image)result = self.cnn.predict(image)print(result)def evaluate(self):super(ConvolutionalModel, self).evaluate()
在构造函数中,我们设置self.shape变量,该变量定义输入层的形状。在我们的例子中,对于Yale数据集,图像高度为320像素,宽度为243像素,self.shape =(320, 243, 1)。
然后,我们调用super()从父构造函数获取所有与数据集相关的变量集,并调用init_model()初始化模型的方法。
最后,我们调用compile方法,该方法配置用于训练的模型并设置要在loss参数中使用的目标函数。在训练过程中,目标功能得到了优化(最小化或最大化)。accuracy参数定义在训练中评估模型的度量。optimizer参数定义权重的计算方式。最常见的优化器是“梯度下降”。
我们的CNN模型定义为顺序的,并根据体系结构的要求添加所有层。train()方法使用表示层排列的sequential类的fit方法来训练CNN。此方法接收训练CNN的数据作为输入,该数据的正确分类以及一些可选参数,例如要运行的时期数。
训练CNN
现在,代码已经准备就绪,该开始训练我们的CNN了。让我们实例化ConvolutionalModel类,在Yale数据集上进行训练,然后调用评估方法。
cnn = ConvolutionalModel(dataSet)
cnn.train(n_epochs=50)
cnn.evaluate()
在进行了50个时期的训练后,我们在测试图像上的准确性达到了近85%。
这意味着我们的CNN现在将以85%的概率识别出数据集中15个主题中的每个主题。简短的练习还不错吧?
现在,我们已经训练了CNN,如果我们想预测新的传入数据(意味着来自图像的新面孔),则可以使用之前详细介绍的ConvolutionalModel类中的predict(image)方法来进行。如何运作?该调用看起来像下一个调用,它应符合某些假设。
cnn.predict(np.expand_dims(image, axis=0))
首先,输入图像必须具有与先前训练的CNN输入层相同的尺寸或形状。其次,在我们归一化数据的predict()方法中,它应该是相同类型的输入,即像素值矩阵,因此无需提供归一化的图像像素矩阵。第三,我们可能需要为输入的面部图像添加一个维度,因为在经过训练的CNN中,我们考虑了数据集中样本数量的第4个维度。这可以使用numpy的expand_dims()方法来实现。第四,假定将提供面部图像,在较大图片的情况下,先前文章中提供的面部检测方法可证明是有用的。
最后,predict()方法的输出可以在上图中看到。此方法将输出脸部属于每个可能类别或个人的概率(对于训练后的数据集为15)。在这种情况下,我们可以看到类别4的可能性最高,这正是输入的面部图像所指的类别或人物。
下一步?
现在我们知道了如何从头开始构建自己的CNN。在下一篇文章中,我们将研究一种替代方法——利用预先训练的模型。我们将使用一个经过训练的CNN来对具有数百万个图像的数据集进行人脸识别训练,并对其进行调整以解决我们的问题。敬请关注!