使用Keras和CNN进行自定义AI人脸识别

目录

介绍

什么是CNN?

设计CNN

实现CNN

训练CNN

下一步?


  • 下载源8.4 KB

介绍

如果您看过《少数派报告》电影,您可能还记得汤姆·克鲁斯(Tom Cruise)走进一家Gap商店的场景。视网膜扫描仪读取他的眼睛,并为他播放定制的广告。好吧,这是2020年。我们不需要视网膜扫描仪,因为我们拥有人工智能AI)和机器学习ML)!

在本系列中,我们将向您展示如何使用深度学习进行面部识别,然后基于被识别的面部,使用神经网络语音合成TTS)引擎播放自定义广告。

我们假设您熟悉AI/ML的基本概念,并且可以找到使用Python的方法。

在本文中,我们将讨论CNN,然后设计一个并使用KerasPython中实现它。

什么是CNN

CNN是一种神经网络(NN),通常用于图像分类任务(例如人脸识别)以及任何其他输入具有网格状拓扑的问题。在CNN中,并非每个节点都连接到下一层的所有节点。换句话说,它们不是完全连接的NN。这有助于防止完全连接的NN中出现过拟合问题,更不用说由NN中的过多连接导致的超慢收敛。

CNN的概念依赖于称为卷积的数学运算,这在数字信号处理领域非常普遍。卷积被定义为两个函数(第三个函数)的乘积,表示前两个函数之间的重叠量。在CNN区域,通过在图像中滑动过滤器(即内核)来实现卷积。

在人脸识别中,卷积操作使我们能够检测图像中的不同特征。不同的过滤器可以检测垂直和水平边缘、纹理、曲线和其他图像特征。这就是为什么任何CNN中的第一层都是卷积层的原因。

CNN中另一个常见的层是池化层。池化用于减小图像表示的大小,这意味着减少了参数数量,并最终减少了计算量。最常见的池化类型是“max”,它使用滑动窗口(类似于卷积操作中的窗口)在每个位置从匹配的单元格组中获取最大值。最后,它根据收获的最大值构建图像的新表示形式。

最常见的CNN架构通常从卷积层开始,然后是激活层,然后是池化层,最后以传统的全连接网络(例如多层NN)结束。这种层次化的模型称为顺序模型。为什么最后要建立全连接网络?要学习变换图像中特征的非线性组合(在卷积和合并之后)。

设计CNN

这是我们将在CNN中实现的架构:

  • 输入层——NumPy数组(img_widthimg_height1);“ 1” 因为我们正在处理灰度图像;对于RGB图像,应该是(img_widthimg_height3
  • Conv2D层——32个过滤器,过滤器大小为3
  • 激活层——必须使用非线性函数进行学习,在这种情况下,该函数为ReLU
  • Conv2D层——32个过滤器,过滤器大小为3,步幅为3
  • 使用ReLU功能的激活层
  • MaxPooling2D层——应用(22)合并窗口
  • 25%的DropOut层——通过从前一层中随机删除一些值(将它们设置为0)来防止过度拟合;又名稀释技术
  • Conv2D层——64个过滤器,过滤器大小为3
  • 使用ReLU功能的激活层
  • Conv2D层——64个过滤器,过滤器大小为3,步幅为3
  • 使用ReLU功能的激活层
  • MaxPooling2D层——应用(22)合并窗口
  • 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.objects
            self.labels = dataSet.labels
            self.obj_validation = dataSet.obj_validation
            self.labels_validation = dataSet.labels_validation
            self.number_labels = dataSet.number_labels
            self.n_classes = dataSet.n_classes
        self.init_model()

    @abstractmethod
    def init_model(self):
        pass

    @abstractmethod
    def train(self):
        pass

    @abstractmethod
    def predict(self, object):
        pass

    @abstractmethod
    def 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))

    @abstractmethod
    def 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.cnn

    def 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类中的predictimage)方法来进行。如何运作?该调用看起来像下一个调用,它应符合某些假设。

cnn.predict(np.expand_dims(image, axis=0))

首先,输入图像必须具有与先前训练的CNN输入层相同的尺寸或形状。其次,在我们归一化数据的predict()方法中,它应该是相同类型的输入,即像素值矩阵,因此无需提供归一化的图像像素矩阵。第三,我们可能需要为输入的面部图像添加一个维度,因为在经过训练的CNN中,我们考虑了数据集中样本数量的第4个维度。这可以使用numpyexpand_dims()方法来实现。第四,假定将提供面部图像,在较大图片的情况下,先前文章中提供的面部检测方法可证明是有用的。

最后,predict()方法的输出可以在上图中看到。此方法将输出脸部属于每个可能类别或个人的概率(对于训练后的数据集为15)。在这种情况下,我们可以看到类别4的可能性最高,这正是输入的面部图像所指的类别或人物。

下一步?

现在我们知道了如何从头开始构建自己的CNN。在下一篇文章中,我们将研究一种替代方法——利用预先训练的模型。我们将使用一个经过训练的CNN来对具有数百万个图像的数据集进行人脸识别训练,并对其进行调整以解决我们的问题。敬请关注!

你可能感兴趣的:(python,人工智能,Keras,CNN,AI,人脸识别,python)