Advertisement

Understanding Convolutional Neural Networks

阅读量:

作者:禅与计算机程序设计艺术

1.简介

卷积神经网络(CNN),作为一种新兴的深度学习模型,在多个领域如图像处理、语音识别和人脸识别等领域展现出显著的应用价值。其核心技术在于通过多层特征提取过程从输入数据中获取高度抽象的表征信息,并在此基础上生成具有判别能力的输出结果。本文将循序渐进地阐述卷积神经网络的工作原理及其关键技术要点。通过深入理解卷积神经网络的相关知识与技术细节,读者将能够更加熟练地将其应用于实际问题的解决过程中。

2.CNN的背景

深度学习系统通常由数据预处理单元、计算单元和结果后处理单元组成。该系统中的数据预处理部分会执行标准化、裁剪和旋转等操作;其中的数据预处理阶段主要负责对原始输入进行标准化转换;而计算部分则通过多级特征提取机制生成目标输出值;最后的结果后处理器则用于分类识别和目标检测任务

CNN的主要技术优势体现在其利用卷积神经网络构建独特的架构模式而非传统的全连接神经网络架构。相较于传统全连接神经网络架构,在CNN中使用卷积核能够有效提取图像或信号中的局部特性。这种架构设计使得CNN在减少计算复杂度的同时显著降低了模型参数数量和内存占用需求,并在此基础上实现了更高的计算效率和预测性能。

由LeCun教授于1998年首次提出该方法,在图像识别领域实现了当时计算机视觉技术的最佳水平。该方法首先设计了一系列卷积核,并结合最大池化方法有效地提取了图像的局部特征;随后将多个卷积层串联起来构建了一个多级联结特征提取系统;最后通过分类器对系统的输出结果进行了识别或定位。此外还能够借助跳跃连接和1x1卷积等技术手段进一步优化模型性能

CNN的几个主要特性如下:

  1. 模块化设计:CNN采用模块化设计,在各个功能组件之间实现了相互连接与协同工作,在不同深度层级上能够灵活地适应性学习输入信号的各种特征表示。
  2. 局部感受野特性:卷积神经网络中的卷积层通过提取图像空间上的局部区域特征来构建表征信息网络架构,在此过程中完全摒弃了对全局空间信息模式的学习。
  3. 权值共享机制:该网络结构基于权值共享机制的设计理念,在不同位置上重复使用的过滤器能够有效降低计算复杂度的同时显著提升了模型参数的有效利用率。
  4. 数据驱动特性:基于深度学习理论支撑的大规模数据训练体系下,在不依赖人工工程化的前提下该网络系统能够在海量数据样本中自主构建出具有优异泛化的深度表征提取能力。

3.核心概念及术语

(1)卷积

卷积运算是一种数学操作,在两个函数之间建立对应关系,并反映它们在位置上的相关性。具体而言,在处理f(t)与g(t)时,在t点处取a,b两元素进行卷积运算,则会生成新的函数h(t),其表达式为h(t)=a·[f(t−k)]·b·[f(t+l)](注:此处可能存在笔误)。这里k和l是具体数值由这两个正整数决定的参数。该过程属于线性变换领域,并将原始信号转换到频域空间中进行分析。因此,在分析两信号之间的关联性和比较方面具有广泛的应用。

在卷积神经网络中,卷积运算又称作互相关运算,它的定义如下:

其中f和g分别代表两个信号并且满足|u||v|均受限于无穷大范围二维离散傅里叶变换\mathcal{F}的意义在于它能够在时间域上实现两函数间的卷积操作考虑到卷积运算通常应用于连续函数领域在实际操作中必须将原始数据转换为时域序列后才能应用该算法

(2)特征映射

在卷积神经网络中,特征映射(Feature Map)是一个关键概念。它可以被描述为一种特殊的矩阵。每个元素代表的是局部区域内的像素点或激活值在特征空间中的位置信息。其数学形式如下所示:

其中,
F(\cdot;\Theta)被定义为卷积操作的核心函数,
\Theta代表该操作的所有可训练参数,
X则表示输入图像。
该操作在图像处理领域中被广泛应用于特征提取阶段。
n_Hn_W分别描述了特征图的空间维度,
其中n_H对应高度维度,
n_W则对应宽度维度。

(3)池化

池化(Pooling)是一种用于特征降维的操作,在深度学习模型中被广泛采用。其主要目标是减少特征图的空间维度以避免过拟合现象的发生。具体而言,在实际应用中通常采用的是局部区域的最大值或平均值提取方法来替代全局平均池化操作。当窗口完成滑动后,在每个局部区域内取最大值或计算均值作为最终输出结果。这样不仅能够有效降低运算复杂度还能显著提升特征学习效率

常用的池化方法包括最大池化和平均池化技术。其中max pooling仅提取了window内部的最大值而average pooling则计算了window内部的average value

(4)全连接层

全连接层(Fully Connected Layer)是最基本形式的神经网络层,在计算过程中通过输入向量与权重矩阵的乘法运算生成输出向量。全连接层指的是各神经元之间实现深度信息交互的结构基础,在深度学习模型中占据重要地位。该结构常被应用于分类和回归等核心任务中。

(5)ReLU激活函数

该激活函数在深度学习领域中被广泛认为是最常用的一种;其主要特性在于将输入值为负时的输出设为零,在输入值为正时则保持输入数值不变。

(6)Softmax分类

基于软最大算法(Softmax Algorithm)的分类方法是一种广受欢迎的机器学习模型。该模型特别适用于多标签分类问题,并通过将输入样本分配到各个可能的类别中来实现目标识别的任务。具体来说,在这种情况下,我们假设输入样本x属于第k类的概率为:

在该分类问题中,在模型中我们使用符号K来代表类别总数,并用符号z_k来代表样本x被归类到第k个类别中的信心程度。通常采用的是交叉熵作为分类器的损失函数(Cross Entropy Loss),我们的优化目标则是尽可能地减小这一损失值。

(7)多任务学习

多任务学习(Multi-task Learning)是一种重要的深度学习研究方向。它所依据的基本假设认为:每个任务应由独立的模型进行学习;同时要求各模型之间保持弱相关性。这种方法的好处体现在能够部分地解决特征提取与表示优化等问题。

多任务学习主要采用两种途径:交替训练技术与集成学习策略。交替训练技术的核心理念在于同步优化多个模型,在不同数据分布下协同工作,并通过多任务评估机制检验其性能。而集成方法则综合运用多个基础模型,在融合各自优势的基础上构建综合预测体系。

4.核心算法原理

(1)CNN的卷积层

Convolutional layer是构成CNN的核心组件之一,并主要负责从输入图像中识别细节特征。
CNN的卷积层被分为三个组成部分:

卷积核(Kernel):这是卷积运算的核心单元,在图像处理中发挥着关键作用。与传统的全连接层不同的是,在CNN中每一层都仅包含一个独特的滤波器权重矩阵。在设计神经网络时选择合适的滤波器尺寸至关重要——它们既需要足够大来捕捉复杂的模式又不能过大以免增加模型复杂度和训练难度。
一般来说,在提取图像特征时,较大的滤波器能够捕捉到更多细节信息;然而,在实际应用中使用过大的滤波器可能会导致图像细节丢失。

  1. 激活函数(Activation Function):卷积层通常将结果传递给非线性激活函数进行处理。其中一种常见的是ReLU激活函数(Rectified Linear Unit)。该激活函数接受一个实数作为输入,在输入值小于零时将其映射为零值;反之,则直接返回输入值本身。这种处理方式使得神经信号的响应幅度逐渐减弱,并且在较大的正值范围内保持线性增长特性。由于其特性设计,在实际应用中ReLU能够有效地缓解梯度消失与爆炸的问题,并且显著提升了模型对复杂数据的学习能力

  2. 填充策略(Padding Strategy):卷积核在处理图像边缘时会作用于像素点上。因此,在处理图像边界时必须确保输入区域具有充足的背景知识以便提取有效的特征。填充策略正是通过这种方式来增加背景知识的方法。简而言之,填充策略能够帮助网络识别边界像素的信息从而提高模型的鲁棒性。常用的方法包括全零填充和反射填充两种类型。

  • 零填充(Zero Padding):这是最常用的填充策略。它在输入图像的周围区域进行填充,并在其卷积核中心进行滑动。
  • 有效值补偿(Valid Convolution):这在卷积操作中仅考虑卷积核内部的实际参与运算像素点,在此方式下不会对图像的边缘区域产生额外影响。

(2)CNN的池化层

在卷积神经网络中,
卷积神经网络(CNN)中,
卷积神经网络(CNN)中的
卷积神经网络(CNN)主要由卷积层、激活函数层、全连接层等基本组成。
其中,
卷积神经网络(CNN)中最关键的组成部分之一是激活函数。
这些激活函数主要包括ReLU、sigmoid、tanh等。
其中,
ReLU是一种非线性激活函数。
它通过将输入信号与其自身相乘的方式实现激活。
这种激活方式能够有效避免梯度消失问题,
并具有良好的稀疏性特性。
在实际应用中,
ReLU因其简单高效的特点广受欢迎并被广泛采用。

(3)CNN的全连接层

全连接层(Fully Connected Layer)作为神经网络中的一种核心组件,在结构上是最简单也是应用最为广泛的。其中,该层通过将上一层的所有输出节点与当前层的所有输出节点一一对应建立联系,并对这些信号进行非线性转换。其中通过构建一个完整的连接架构将各层次之间的信息传递通路建立起来,并在此基础上完成复杂的计算任务。这种架构能够有效地模拟和表达高度复杂的非线性函数关系。

(4)CNN的跳跃链接

跳跃链接(Skip Connections)是卷积神经网络中的关键组成部分。其基本思路在于将上一层的输出作为下一层的输入,从而有效缓解梯度消失问题。

(5)CNN的残差网络

该种神经网络架构被称为残差网(ResNet)结构,在深度学习领域具有重要地位。其核心理念在于通过在前馈通路的基础上附加一个差异项来解决较深层神经网络模型中存在的梯度传递问题。该架构使得其输出结果与其前馈通路之间形成了差异项,并且这种设计不仅简化了计算过程还能有效地提升了较深层神经网络模型的准确性。

5.代码实例

(1)实现一个简单的CNN

复制代码
    import tensorflow as tf
    from tensorflow import keras
    
    def build_model():
    model = keras.Sequential()
    
    # First convolution layer with max pooling
    model.add(keras.layers.Conv2D(filters=32, kernel_size=(3,3), activation='relu', padding='same', input_shape=(28,28,1)))
    model.add(keras.layers.MaxPooling2D((2,2)))
    
    # Second convolution layer without max pooling and dropout for regularization
    model.add(keras.layers.Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='same'))
    model.add(keras.layers.Dropout(rate=0.2))
    
    # Flatten the output of previous layers to feed it into fully connected layers
    model.add(keras.layers.Flatten())
    
    # Fully connected layers
    model.add(keras.layers.Dense(units=128, activation='relu'))
    model.add(keras.layers.Dense(units=10, activation='softmax'))
    
    return model
    
    model = build_model()
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

(2)实现一个残差网络

复制代码
    class ResidualBlock(tf.keras.Model):
    def __init__(self, num_channels, num_residuals, first_block=False):
        super().__init__()
    
        self.num_channels = num_channels
        self.first_block = first_block
    
        if not first_block:
            self.bn1 = tf.keras.layers.BatchNormalization()
    
        self.conv1 = tf.keras.layers.Conv2D(filters=num_channels,
                                            kernel_size=(3, 3), 
                                            strides=1,
                                            padding='same')
    
        self.bn2 = tf.keras.layers.BatchNormalization()
        self.conv2 = tf.keras.layers.Conv2D(filters=num_channels,
                                            kernel_size=(3, 3), 
                                            strides=1,
                                            padding='same')
    
    def call(self, inputs, training=None):
        x = tf.identity(inputs)
    
        if not self.first_block:
            x = self.bn1(x, training=training)
            x = tf.nn.relu(x)
    
        y = self.conv1(x)
        y = self.bn2(y, training=training)
        y = tf.nn.relu(y)
        y = self.conv2(y)
    
        if self.num_channels!= inputs.shape[-1]:
            x = tf.pad(inputs[:, :, :-1, :-1], [[0, 0], [0, 0], [1, 0], [1, 0]])
            x = tf.keras.layers.AveragePooling2D()(x)
    
        z = tf.keras.layers.Add()([x, y])
    
        return tf.nn.relu(z)
    
    def build_resnet():
    inputs = tf.keras.Input(shape=[224, 224, 3])
    
    x = tf.keras.layers.Conv2D(filters=64,
                               kernel_size=(7, 7), 
                               strides=2,
                               padding='same')(inputs)
    x = tf.keras.layers.BatchNormalization()(x)
    x = tf.nn.relu(x)
    x = tf.keras.layers.MaxPooling2D((3, 3), strides=2)(x)
    
    for i in range(3):
        num_channels = 64 * 2 ** i
        first_block = True if i == 0 else False
    
        for _ in range(3):
            block = ResidualBlock(num_channels, num_residuals=2, first_block=first_block)
            x = block(x)
    
            first_block = False
    
    x = tf.keras.layers.GlobalAvgPool2D()(x)
    outputs = tf.keras.layers.Dense(units=1000, activation="softmax")(x)
    
    model = tf.keras.Model(inputs=inputs, outputs=outputs)
    
    return model
    
    model = build_resnet()
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

全部评论 (0)

还没有任何评论哟~