41、深度卷积神经网络图像分类技术解析

深度卷积神经网络图像分类技术解析

1. 卷积基础概念

在卷积操作中,填充(padding)和步长(stride)是两个重要的超参数。填充大小可以为任意非负整数 ( p \geq 0 ),不同的填充值会影响输入向量中各元素的使用频率。例如,当输入向量长度 ( n = 5 ),滤波器长度 ( m = 3 ) 时,若 ( p = 0 ),输入向量的边界元素使用频率较低,而中间元素使用频率较高;若 ( p = 2 ),则每个输入元素会参与到相同数量的输出元素计算中。

步长 ( s ) 表示每次移动滤波器时的偏移量,它必须是小于输入向量大小的正整数。例如,在某个例子中,步长 ( s = 2 )。

交叉相关(cross - correlation)与卷积类似,但在交叉相关中,乘法是在相同方向进行的,不需要对滤波器矩阵进行旋转。其数学定义为:( \mathbf{y} = \mathbf{x} \star \mathbf{w} \to y[i] = \sum_{k = -\infty}^{+\infty} x[i + k] w[k] )。大多数深度学习框架(如 TensorFlow)实现的是交叉相关,但将其称为卷积。

2. 填充模式

常见的填充模式有三种:
- 全填充(Full) :填充参数 ( p = m - 1 ),会增加输出的维度,在卷积神经网络(CNN)架构中很少使用。
- 相同填充(Same) :确保输出向量与输入向量大小相同,填充参数 ( p ) 根据滤波器大小以及输入输出大小相同的要求来计算。这是 CNN 中最常用的填充模式,它可以保留向量的大小,在处理图像相关任务时,能保留输入图像的高度和宽度,方便网络架构的设计。
- 有效填充(Valid) :( p = 0 ),即不进行填充。这种模式在多层神经网络中会使张量的体积大幅减小,可能对网络性能产生不利影响。

建议在卷积层使用相同填充来保留空间大小,通过池化层来减小空间大小。

3. 卷积输出大小的确定

卷积输出的大小由滤波器在输入向量上的移动次数决定。假设输入向量大小为 ( n ),滤波器大小为 ( m ),填充为 ( p ),步长为 ( s ),则输出大小 ( o ) 的计算公式为:( o = \lfloor \frac{n + 2p - m}{s} \rfloor + 1 ),其中 ( \lfloor \cdot \rfloor ) 表示向下取整操作。

例如:
- 输入向量大小 ( n = 10 ),卷积核大小 ( m = 5 ),填充 ( p = 2 ),步长 ( s = 1 ) 时,( o = \lfloor \frac{10 + 2 \times 2 - 5}{1} \rfloor + 1 = 10 ),此为相同填充模式。
- 输入向量大小不变,卷积核大小 ( m = 3 ),步长 ( s = 2 ) 时,( o = \lfloor \frac{10 + 2 \times 2 - 3}{2} \rfloor + 1 = 6 )。

以下是一维卷积的简单实现代码,并与 numpy.convolve 函数的结果进行比较:

import numpy as np

def conv1d(x, w, p=0, s=1):
    w_rot = np.array(w[::-1])
    x_padded = np.array(x)
    if p > 0:
        zero_pad = np.zeros(shape=p)
        x_padded = np.concatenate([zero_pad, 
                                   x_padded, 
                                   zero_pad])
    res = []
    for i in range(0, int(len(x)/s), s):
        res.append(np.sum(x_padded[i:i+w_rot.shape[0]] * 
                          w_rot))
    return np.array(res)

## Testing:
x = [1, 3, 2, 4, 5, 6, 1, 3]
w = [1, 0, 3, 1, 2]
print('Conv1d Implementation:', 
      conv1d(x, w, p=2, s=1))
print('NumPy Results:', 
      np.convolve(x, w, mode='same'))
4. 二维离散卷积

前面介绍的一维卷积概念很容易扩展到二维。当处理二维输入矩阵 ( \mathbf{X} {n_1 \times n_2} ) 和滤波器矩阵 ( \mathbf{W} {m_1 \times m_2} )(其中 ( m_1 \leq n_1 ) 且 ( m_2 \leq n_2 ))时,二维卷积的结果矩阵 ( \mathbf{Y} = \mathbf{X} * \mathbf{W} ) 的数学定义为:( \mathbf{Y} = \mathbf{X} * \mathbf{W} \to Y[i, j] = \sum_{k_1 = -\infty}^{+\infty} \sum_{k_2 = -\infty}^{+\infty} X[i - k_1, j - k_2] W[k_1, k_2] )。

一维卷积中的零填充、旋转滤波器矩阵和步长等技术同样适用于二维卷积,只是需要在两个维度上分别扩展。

以下是二维卷积的简单实现代码,并与 scipy.signal.convolve2d 函数的结果进行比较:

import numpy as np
import scipy.signal

def conv2d(X, W, p=(0, 0), s=(1, 1)):
    W_rot = np.array(W)[::-1,::-1]
    X_orig = np.array(X)
    n1 = X_orig.shape[0] + 2*p[0]
    n2 = X_orig.shape[1] + 2*p[1]
    X_padded = np.zeros(shape=(n1, n2))
    X_padded[p[0]:p[0]+X_orig.shape[0], 
             p[1]:p[1]+X_orig.shape[1]] = X_orig
    res = []
    for i in range(0, int((X_padded.shape[0] - 
                            W_rot.shape[0])/s[0])+1, s[0]):
        res.append([])
        for j in range(0, int((X_padded.shape[1] - 
                               W_rot.shape[1])/s[1])+1, s[1]):
            X_sub = X_padded[i:i+W_rot.shape[0], 
                             j:j+W_rot.shape[1]]
            res[-1].append(np.sum(X_sub * W_rot))
    return(np.array(res))

X = [[1, 3, 2, 4], [5, 6, 1, 3], [1, 2, 0, 2], [3, 4, 3, 2]]
W = [[1, 0, 3], [1, 2, 1], [0, 1, 1]]
print('Conv2d Implementation:\n', 
      conv2d(X, W, p=(1, 1), s=(1, 1)))
print('SciPy Results:\n', 
      scipy.signal.convolve2d(X, W, mode='same'))

需要注意的是,上述简单实现的二维卷积在内存需求和计算复杂度方面效率较低,不适合实际的神经网络应用。近年来,已经开发出了更高效的算法,如使用傅里叶变换来计算卷积。

5. 子采样层(池化层)

在 CNN 中,子采样通常以两种池化操作的形式出现:最大池化(max - pooling)和平均池化(mean - pooling,也称为 average - pooling)。池化层通常表示为 ( P_{n_1 \times n_2} ),下标表示进行最大或平均操作的邻域大小。

池化的优点有两个方面:
- 引入局部不变性 :最大池化对局部邻域内的小变化不敏感,有助于生成对输入数据中的噪声更鲁棒的特征。例如,两个不同的输入矩阵经过 ( P_{2 \times 2} ) 最大池化后可能得到相同的输出。
- 减小特征大小 :可以提高计算效率,并且减少特征数量可能会降低过拟合的程度。

传统上,池化操作通常是非重叠的,即步长参数等于池化大小。但也存在重叠池化的情况,即步长小于池化大小。一些 CNN 架构不使用池化层,而是通过步长为 2 的卷积层来减小特征大小。

6. 多输入或颜色通道的处理

卷积层的输入可能包含一个或多个二维数组或矩阵,这些矩阵称为通道。传统的卷积层实现期望输入为三维张量 ( \mathbf{X} {N_1 \times N_2 \times C {in}} ),其中 ( C_{in} ) 是输入通道的数量。例如,彩色图像使用 RGB 颜色模式时,( C_{in} = 3 );灰度图像则 ( C_{in} = 1 )。

在处理多通道输入时,对每个通道分别进行卷积操作,然后将结果相加。假设输入为 ( \mathbf{X} {n_1 \times n_2 \times C {in}} ),卷积核矩阵为 ( \mathbf{W} {m_1 \times m_2 \times C {in}} ),偏置值为 ( b ),则预激活值 ( \mathbf{Z} {conv} = \sum {c = 1}^{C_{in}} \mathbf{W}[:, :, c] * \mathbf{X}[:, :, c] ),最终的预激活值 ( \mathbf{Z} = \mathbf{Z}_{conv} + b ),特征图 ( \mathbf{A} = \phi(\mathbf{Z}) ),其中 ( \phi ) 是激活函数。

如果使用多个特征图,卷积核张量将变为四维 ( \mathbf{W} {width \times height \times C {in} \times C_{out}} ),其中 ( C_{out} ) 是输出特征图的数量。此时,预激活值和特征图的计算需要考虑输出特征图的索引 ( k ):
( \mathbf{Z} {conv}[:, :, k] = \sum {c = 1}^{C_{in}} \mathbf{W}[:, :, c, k] * \mathbf{X}[:, :, c] )
( \mathbf{Z}[:, :, k] = \mathbf{Z}_{conv}[:, :, k] + b[k] )
( \mathbf{A}[:, :, k] = \phi(\mathbf{Z}[:, :, k]) )

7. 图像文件读取

在处理图像时,可以使用 uint8 (无符号 8 位整数)数据类型将图像读取到 NumPy 数组中,以减少内存使用。以下是使用 TensorFlow 和 imageio 读取图像的示例:

使用 TensorFlow:

import tensorflow as tf
img_raw = tf.io.read_file('example-image.png')
img = tf.image.decode_image(img_raw)
print('Image shape:', img.shape)

使用 imageio

import imageio
img = imageio.imread('example-image.png')
print('Image shape:', img.shape)
print('Number of channels:', img.shape[2])
print('Image data type:', img.dtype)
print(img[100:102, 100:102, :])
8. 可训练参数数量对比

以一个包含三个输入通道、五个输出特征图的卷积层为例,卷积层的卷积核参数数量为 ( m_1 \times m_2 \times 3 \times 5 ),偏置向量大小为 5。而如果使用全连接层来达到相同数量的输出单元,参数数量会大得多,全连接层的权重矩阵参数数量为 ( (n_1 \times n_2 \times 3) \times (n_1 \times n_2 \times 5) ),偏置向量大小为 ( n_1 \times n_2 \times 5 )。由于 ( m_1 < n_1 ) 且 ( m_2 < n_2 ),可以看出卷积层在可训练参数数量上具有显著优势。

综上所述,深度卷积神经网络在图像分类等任务中具有独特的优势,通过合理选择填充模式、池化操作以及处理多通道输入等技术,可以构建高效、准确的网络模型。

深度卷积神经网络图像分类技术解析

9. 构建 CNN 的步骤总结

将前面介绍的各个组件组合起来,就可以实现一个完整的 CNN。下面是构建 CNN 的主要步骤:
1. 确定输入数据 :输入可以是单通道或多通道的二维矩阵,如灰度图像或彩色图像。使用合适的数据类型(如 uint8 )读取图像数据,以减少内存使用。
2. 选择卷积层参数 :包括滤波器大小、填充模式、步长等。通常选择相同填充模式来保留空间大小,通过调整步长或使用池化层来减小特征图的尺寸。
3. 计算卷积 :对于多通道输入,对每个通道分别进行卷积操作,然后将结果相加。如果使用多个特征图,卷积核张量为四维。
4. 应用激活函数 :将卷积层的预激活值传递给激活函数,得到特征图。
5. 选择池化层 :可以选择最大池化或平均池化,以引入局部不变性和减小特征大小。池化层可以是重叠或非重叠的。
6. 重复步骤 2 - 5 :根据需要添加多个卷积层和池化层,构建更深的网络。
7. 添加全连接层(可选) :在网络的最后,可以添加全连接层进行分类或回归任务。

10. 不同填充模式的对比表格

为了更直观地对比三种填充模式的特点,下面给出一个表格:
| 填充模式 | 填充参数 ( p ) | 输出尺寸变化 | 使用场景 |
| — | — | — | — |
| 全填充(Full) | ( p = m - 1 ) | 增加输出维度 | 信号处理应用,CNN 架构中很少使用 |
| 相同填充(Same) | 根据滤波器大小和输入输出大小相同的要求计算 | 输出与输入大小相同 | CNN 中最常用,方便网络架构设计 |
| 有效填充(Valid) | ( p = 0 ) | 减小输出尺寸 | 可能导致多层网络中张量体积大幅减小,影响性能 |

11. 卷积和池化操作的流程图

下面是一个简单的 mermaid 流程图,展示了 CNN 中卷积和池化操作的基本流程:

graph LR
    A[输入图像] --> B[卷积层]
    B --> C[激活函数]
    C --> D[池化层]
    D --> E{是否还有卷积层?}
    E -- 是 --> B
    E -- 否 --> F[全连接层(可选)]
    F --> G[输出结果]
12. 代码示例总结

为了方便回顾,下面总结了前面提到的主要代码示例:
- 一维卷积实现

import numpy as np

def conv1d(x, w, p=0, s=1):
    w_rot = np.array(w[::-1])
    x_padded = np.array(x)
    if p > 0:
        zero_pad = np.zeros(shape=p)
        x_padded = np.concatenate([zero_pad, 
                                   x_padded, 
                                   zero_pad])
    res = []
    for i in range(0, int(len(x)/s), s):
        res.append(np.sum(x_padded[i:i+w_rot.shape[0]] * 
                          w_rot))
    return np.array(res)

## Testing:
x = [1, 3, 2, 4, 5, 6, 1, 3]
w = [1, 0, 3, 1, 2]
print('Conv1d Implementation:', 
      conv1d(x, w, p=2, s=1))
print('NumPy Results:', 
      np.convolve(x, w, mode='same'))
  • 二维卷积实现
import numpy as np
import scipy.signal

def conv2d(X, W, p=(0, 0), s=(1, 1)):
    W_rot = np.array(W)[::-1,::-1]
    X_orig = np.array(X)
    n1 = X_orig.shape[0] + 2*p[0]
    n2 = X_orig.shape[1] + 2*p[1]
    X_padded = np.zeros(shape=(n1, n2))
    X_padded[p[0]:p[0]+X_orig.shape[0], 
             p[1]:p[1]+X_orig.shape[1]] = X_orig
    res = []
    for i in range(0, int((X_padded.shape[0] - 
                            W_rot.shape[0])/s[0])+1, s[0]):
        res.append([])
        for j in range(0, int((X_padded.shape[1] - 
                               W_rot.shape[1])/s[1])+1, s[1]):
            X_sub = X_padded[i:i+W_rot.shape[0], 
                             j:j+W_rot.shape[1]]
            res[-1].append(np.sum(X_sub * W_rot))
    return(np.array(res))

X = [[1, 3, 2, 4], [5, 6, 1, 3], [1, 2, 0, 2], [3, 4, 3, 2]]
W = [[1, 0, 3], [1, 2, 1], [0, 1, 1]]
print('Conv2d Implementation:\n', 
      conv2d(X, W, p=(1, 1), s=(1, 1)))
print('SciPy Results:\n', 
      scipy.signal.convolve2d(X, W, mode='same'))
  • 图像读取示例
# 使用 TensorFlow 读取图像
import tensorflow as tf
img_raw = tf.io.read_file('example-image.png')
img = tf.image.decode_image(img_raw)
print('Image shape:', img.shape)

# 使用 imageio 读取图像
import imageio
img = imageio.imread('example-image.png')
print('Image shape:', img.shape)
print('Number of channels:', img.shape[2])
print('Image data type:', img.dtype)
print(img[100:102, 100:102, :])
13. 总结与建议

通过对 CNN 中卷积、填充、池化等操作的介绍,我们可以看到 CNN 在图像分类等任务中具有强大的能力。以下是一些总结和建议:
- 填充模式选择 :在大多数情况下,选择相同填充模式,以保留空间大小,方便网络架构的设计。有效填充模式可能导致特征图尺寸大幅减小,影响网络性能。全填充模式在 CNN 中很少使用。
- 池化层的使用 :池化层可以引入局部不变性和减小特征大小,但不是所有的 CNN 架构都需要池化层。一些架构通过调整卷积层的步长来达到相同的效果。
- 多通道处理 :在处理多通道输入时,要正确计算每个通道的卷积结果,并将它们相加。使用多个特征图时,卷积核张量为四维。
- 代码效率 :简单实现的卷积代码在内存需求和计算复杂度方面效率较低,实际应用中应使用更高效的算法或深度学习框架提供的函数。

通过合理选择和组合这些组件,可以构建出高效、准确的 CNN 模型,用于图像分类、目标检测等各种计算机视觉任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值