深度卷积神经网络图像分类
1. 卷积操作扩展
通常,卷积操作是将具有多个颜色通道的输入图像视为矩阵堆栈来进行的。即分别对每个矩阵进行卷积,然后将结果相加。不过,如果处理的是 3D 数据集,卷积也可以扩展到 3D 体积。
2. 神经网络正则化之 Dropout
2.1 网络大小选择问题
选择网络大小一直是一个具有挑战性的问题。小型网络参数少,容量低,可能会欠拟合,无法学习复杂数据集的潜在结构,导致性能不佳;而大型网络可能会过拟合,在训练数据集上表现出色,但在测试数据集上表现不佳。在处理实际机器学习问题时,我们事先并不知道网络应该多大。
2.2 解决方法
一种解决方法是构建一个容量相对较大的网络(实际中选择略大于必要的容量)以在训练数据集上表现良好,然后应用一种或多种正则化方案来防止过拟合,从而在新数据(如测试数据集)上实现良好的泛化性能。
2.3 L1 和 L2 正则化
L1 和 L2 正则化可以通过在损失中添加惩罚项来防止或减少过拟合的影响,在训练过程中缩小权重参数。L2 正则化更为常用。在卷积或全连接(密集)网络中使用 L2 正则化,可通过 Keras API 设置特定层的
kernel_regularizer
来添加 L2 惩罚项到损失函数,示例代码如下:
from tensorflow import keras
conv_layer = keras.layers.Conv2D(
filters=16,
kernel_size=(3,3),
kernel_regularizer=keras.regularizers.l2(0.001))
fc_layer = keras.layers.Dense(
units=16,
kernel_regularizer=keras.regularizers.l2(0.001))
2.4 Dropout 正则化
2.4.1 基本原理
Dropout 是一种流行的正则化(深度)神经网络的技术,用于避免过拟合,提高泛化性能。在神经网络的训练阶段,每个迭代中会以概率
p_drop
随机丢弃一部分隐藏单元(保留概率
p_keep = 1 - p_drop
),常见的
p
值为 0.5。丢弃部分输入神经元时,会对剩余神经元的权重进行重新缩放,以考虑缺失的(丢弃的)神经元。
2.4.2 效果
这种随机丢弃迫使网络学习数据的冗余表示,不能依赖任何一组隐藏单元的激活,从而学习到更通用和鲁棒的模式,有效防止过拟合。
2.4.3 训练和预测阶段的区别
训练时单元可能随机丢弃,而在评估(推理)阶段,所有隐藏单元必须激活(
p_drop = 0
或
p_keep = 1
)。为确保训练和预测期间整体激活值在同一尺度,需要对激活值进行适当缩放。TensorFlow 等工具通常在训练期间进行缩放,即逆 Dropout。
2.4.4 与模型集成的关系
Dropout 可以解释为模型集成的共识(平均)。在每个小批量中,由于权重随机置零,相当于有不同的模型。通过迭代小批量,本质上是对
M = 2^h
个模型进行采样(
h
是隐藏单元的数量)。与常规集成不同的是,Dropout 共享这些“不同模型”的权重,可视为一种正则化形式。在推理时,可以对训练期间采样的所有不同模型进行平均,但这计算成本很高。不过,Dropout 可以通过将训练期间最后一个(或最终)模型的预测结果按
1/(1 - p)
进行缩放来近似模型集成的几何平均值,这比显式计算几何平均值便宜得多。
3. 分类损失函数
3.1 激活函数与输出
不同的激活函数有不同的用途。ReLU 等主要用于神经网络的中间(隐藏)层,为模型添加非线性;而 sigmoid(用于二分类)和 softmax(用于多分类)添加在最后(输出)层,使模型输出类成员概率。如果输出层不包含 sigmoid 或 softmax 激活,则模型将计算 logits 而不是类成员概率。
3.2 损失函数选择
根据问题类型(二分类与多分类)和输出类型(logits 与概率),应选择合适的损失函数来训练模型。二分类使用二元交叉熵损失函数,多分类使用分类交叉熵损失函数。在 Keras API 中,分类交叉熵损失函数根据真实标签是 one-hot 编码格式还是整数标签(稀疏表示)提供了两种选择。
3.3 损失函数代码示例
import tensorflow_datasets as tfds
####### Binary Crossentropy
bce_probas = tf.keras.losses.BinaryCrossentropy(from_logits=False)
bce_logits = tf.keras.losses.BinaryCrossentropy(from_logits=True)
logits = tf.constant([0.8])
probas = tf.keras.activations.sigmoid(logits)
tf.print(
'BCE (w Probas): {:.4f}'.format(
bce_probas(y_true=[1], y_pred=probas)),
'(w Logits): {:.4f}'.format(
bce_logits(y_true=[1], y_pred=logits)))
####### Categorical Crossentropy
cce_probas = tf.keras.losses.CategoricalCrossentropy(
from_logits=False)
cce_logits = tf.keras.losses.CategoricalCrossentropy(
from_logits=True)
logits = tf.constant([[1.5, 0.8, 2.1]])
probas = tf.keras.activations.softmax(logits)
tf.print(
'CCE (w Probas): {:.4f}'.format(
cce_probas(y_true=[0, 0, 1], y_pred=probas)),
'(w Logits): {:.4f}'.format(
cce_logits(y_true=[0, 0, 1], y_pred=logits)))
####### Sparse Categorical Crossentropy
sp_cce_probas = tf.keras.losses.SparseCategoricalCrossentropy(
from_logits=False)
sp_cce_logits = tf.keras.losses.SparseCategoricalCrossentropy(
from_logits=True)
tf.print(
'Sparse CCE (w Probas): {:.4f}'.format(
sp_cce_probas(y_true=[2], y_pred=probas)),
'(w Logits): {:.4f}'.format(
sp_cce_logits(y_true=[2], y_pred=logits)))
3.4 特殊情况
在二分类任务中,模型通常返回单个输出值作为正类的概率。但有时会返回两个输出值,分别作为每个类的概率,此时建议使用 softmax 函数归一化输出,并使用分类交叉熵作为损失函数。
4. 使用 TensorFlow 实现深度 CNN
4.1 背景
之前使用 TensorFlow Estimators 进行手写数字识别,使用 DNNClassifier 估计器和两个隐藏层达到了约 89% 的准确率。现在实现一个 CNN,看看是否能在手写数字分类上取得比 MLP(DNNClassifier)更好的预测性能。
4.2 多层 CNN 架构
输入是 28×28 的灰度图像,输入张量维度为
batch_size × 28 × 28 × 1
。数据经过两个卷积层(核大小为 5×5,第一个卷积层有 32 个输出特征图,第二个有 64 个),每个卷积层后接一个最大池化层(
P2×2
),然后通过一个全连接层传递到第二个全连接层,作为最终的 softmax 输出层。各层张量维度如下:
| 层 | 维度 |
| ---- | ---- |
| 输入 |
[batch_size × 28 × 28 × 1]
|
| Conv_1 |
[batch_size × 28 × 28 × 32]
|
| Pooling_1 |
[batch_size × 14 × 14 × 32]
|
| Conv_2 |
[batch_size × 14 × 14 × 64]
|
| Pooling_2 |
[batch_size × 7 × 7 × 64]
|
| FC_1 |
[batch_size × 1024]
|
| FC_2 和 softmax 层 |
[batch_size × 10]
|
卷积核使用步长为 1 以保留输入维度,池化层使用步长为 2 对图像进行下采样。
4.3 加载和预处理数据
使用
tensorflow_datasets
模块的三步法加载 MNIST 数据集:
import tensorflow_datasets as tfds
## Loading the data
mnist_bldr = tfds.builder('mnist')
mnist_bldr.download_and_prepare()
datasets = mnist_bldr.as_dataset(shuffle_files=False)
mnist_train_orig = datasets['train']
mnist_test_orig = datasets['test']
为创建验证集,在
.as_dataset()
方法中设置
shuffle_files=False
防止初始洗牌。然后进行如下处理:
BUFFER_SIZE = 10000
BATCH_SIZE = 64
NUM_EPOCHS = 20
mnist_train = mnist_train_orig.map(
lambda item: (tf.cast(item['image'], tf.float32)/255.0,
tf.cast(item['label'], tf.int32)))
mnist_test = mnist_test_orig.map(
lambda item: (tf.cast(item['image'], tf.float32)/255.0,
tf.cast(item['label'], tf.int32)))
tf.random.set_seed(1)
mnist_train = mnist_train.shuffle(buffer_size=BUFFER_SIZE,
reshuffle_each_iteration=False)
mnist_valid = mnist_train.take(10000).batch(BATCH_SIZE)
mnist_train = mnist_train.skip(10000).batch(BATCH_SIZE)
4.4 使用 TensorFlow Keras API 实现 CNN
4.4.1 配置 CNN 层
使用 Keras 的
Sequential
类堆叠不同层,Keras 层 API 提供了相关类:
-
tf.keras.layers.Conv2D
:二维卷积层,需指定输出滤波器数量和核大小,还有可选参数如步长和填充。默认输入为 NHWC 格式。
-
tf.keras.layers.MaxPool2D
和
tf.keras.layers.AvgPool2D
:分别用于最大池化和平均池化,
pool_size
确定窗口大小,
strides
可配置池化层。
-
tf.keras.layers.Dropout
:用于正则化,
rate
确定丢弃输入单元的概率,
training
参数可控制调用时的行为。
4.4.2 构建 CNN
model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(
filters=32, kernel_size=(5, 5),
strides=(1, 1), padding='same',
data_format='channels_last',
name='conv_1', activation='relu'))
model.add(tf.keras.layers.MaxPool2D(
pool_size=(2, 2), name='pool_1'))
model.add(tf.keras.layers.Conv2D(
filters=64, kernel_size=(5, 5),
strides=(1, 1), padding='same',
name='conv_2', activation='relu'))
model.add(tf.keras.layers.MaxPool2D(
pool_size=(2, 2), name='pool_2'))
使用
model.compute_output_shape
方法可计算输出形状:
model.compute_output_shape(input_shape=(16, 28, 28, 1))
添加全连接层前需将前层输出展平:
model.add(tf.keras.layers.Flatten())
model.compute_output_shape(input_shape=(16, 28, 28, 1))
添加两个全连接层和一个 Dropout 层:
model.add(tf.keras.layers.Dense(
units=1024, name='fc_1',
activation='relu'))
model.add(tf.keras.layers.Dropout(
rate=0.5))
model.add(tf.keras.layers.Dense(
units=10, name='fc_2',
activation='softmax'))
对于多分类问题且标签为整数(稀疏)格式,使用
SparseCategoricalCrossentropy
作为损失函数,编译模型:
tf.random.set_seed(1)
model.build(input_shape=(None, 28, 28, 1))
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss=tf.keras.losses.SparseCategoricalCrossentropy(),
metrics=['accuracy'])
4.5 Adam 优化器
Adam 优化器是一种适用于非凸优化和机器学习问题的基于梯度的鲁棒优化方法,受 RMSProp 和 AdaGrad 启发。其关键优势在于更新步长的选择基于梯度矩的运行平均值。
4.6 训练和评估模型
history = model.fit(mnist_train, epochs=NUM_EPOCHS,
validation_data=mnist_valid,
shuffle=True)
训练完成后,可视化学习曲线:
import matplotlib.pyplot as plt
hist = history.history
x_arr = np.arange(len(hist['loss'])) + 1
fig = plt.figure(figsize=(12, 4))
ax = fig.add_subplot(1, 2, 1)
ax.plot(x_arr, hist['loss'], '-o', label='Train loss')
ax.plot(x_arr, hist['val_loss'], '--<', label='Validation loss')
ax.legend(fontsize=15)
ax = fig.add_subplot(1, 2, 2)
ax.plot(x_arr, hist['accuracy'], '-o', label='Train acc.')
ax.plot(x_arr, hist['val_accuracy'], '--<',
label='Validation acc.')
ax.legend(fontsize=15)
plt.show()
评估模型:
test_results = model.evaluate(mnist_test.batch(20))
print('Test Acc.: {:.2f}%'.format(test_results[1]*100))
CNN 模型达到了 99.39% 的准确率。最后获取预测结果并可视化:
batch_test = next(iter(mnist_test.batch(12)))
preds = model(batch_test[0])
tf.print(preds.shape)
preds = tf.argmax(preds, axis=1)
print(preds)
fig = plt.figure(figsize=(12, 4))
for i in range(12):
ax = fig.add_subplot(2, 6, i+1)
ax.set_xticks([]); ax.set_yticks([])
img = batch_test[0][i, :, :, 0]
ax.imshow(img, cmap='gray_r')
4.7 整体流程 mermaid 流程图
graph LR
A[加载和预处理数据] --> B[构建 CNN 模型]
B --> C[编译模型]
C --> D[训练模型]
D --> E[评估模型]
E --> F[可视化结果]
综上所述,通过上述步骤,我们详细介绍了卷积操作扩展、神经网络正则化、分类损失函数以及使用 TensorFlow 实现深度 CNN 的过程,并通过实际代码展示了如何构建、训练和评估模型,最终 CNN 模型在手写数字分类任务上取得了良好的性能。
5. 总结与注意事项
5.1 关键要点回顾
- 卷积操作 :卷积操作可处理多通道图像,也能扩展到 3D 数据集。
- 正则化 :Dropout 是一种有效的正则化技术,可防止过拟合,与模型集成有一定关联。
- 损失函数 :根据问题类型和输出类型选择合适的损失函数,如二分类用二元交叉熵,多分类用分类交叉熵。
- CNN 实现 :使用 TensorFlow Keras API 可方便地构建、训练和评估 CNN 模型。
5.2 注意事项
- 数据格式 :Conv2D 类默认输入为 NHWC 格式,若数据为 NCHW 格式,需进行相应处理。
- Dropout 应用 :训练和推理阶段 Dropout 行为不同,需正确设置参数。
- 损失函数输入 :计算交叉熵损失时,提供 logits 通常更具数值稳定性。
5.3 性能对比
| 模型 | 准确率 |
|---|---|
| DNNClassifier(2 个隐藏层) | 约 89% |
| CNN | 99.39% |
从对比结果可以看出,CNN 在手写数字分类任务上具有明显的性能优势。
6. 常见问题解答
6.1 为什么要使用 Dropout 正则化?
Dropout 可以防止神经网络过拟合,使网络学习到更通用和鲁棒的模式。通过随机丢弃部分隐藏单元,网络不能依赖特定的神经元组合,从而提高泛化能力。
6.2 如何选择合适的损失函数?
根据问题类型(二分类或多分类)和输出类型(logits 或概率)来选择。二分类通常使用二元交叉熵,多分类使用分类交叉熵。若标签为整数(稀疏)格式,使用 SparseCategoricalCrossentropy。
6.3 为什么 CNN 在手写数字分类上比 MLP 表现更好?
CNN 能够自动提取图像的局部特征,通过卷积层和池化层减少参数数量,提高计算效率。而 MLP 是全连接网络,参数较多,容易过拟合,对于图像数据的处理能力相对较弱。
6.4 如何处理不同格式的数据?
如果数据的通道维度不在最后一维(非 NHWC 格式),可以通过交换轴或设置
data_format="channels_first"
来处理。
7. 拓展应用与展望
7.1 拓展应用
CNN 不仅在手写数字分类任务上表现出色,还广泛应用于其他图像分类问题,如人脸识别、物体检测、医学图像分析等。在这些领域,CNN 能够有效地提取图像特征,提高分类和检测的准确性。
7.2 未来发展
随着深度学习技术的不断发展,CNN 也在不断创新和改进。例如,引入注意力机制可以增强网络对重要特征的关注,提高模型性能;使用更复杂的架构,如 ResNet、Inception 等,可以进一步提升网络的表达能力。此外,结合其他技术,如强化学习、生成对抗网络等,也为 CNN 的应用带来了更多的可能性。
7.3 学习建议
对于想要深入学习 CNN 的读者,建议学习更多的深度学习理论知识,如卷积神经网络的原理、优化算法等。同时,多参与实际项目,通过实践来加深对 CNN 的理解和应用能力。可以从简单的数据集开始,逐步尝试更复杂的任务,不断提高自己的技术水平。
8. 总结
本文围绕深度卷积神经网络图像分类展开,详细介绍了卷积操作扩展、神经网络正则化、分类损失函数以及使用 TensorFlow 实现深度 CNN 的过程。通过实际代码展示了如何构建、训练和评估模型,并对比了 CNN 和 MLP 在手写数字分类任务上的性能。CNN 在图像分类领域具有显著的优势,未来还有很大的发展空间。希望本文能够帮助读者更好地理解和应用深度卷积神经网络。
8.1 整体流程回顾 mermaid 流程图
graph LR
A[卷积操作扩展] --> B[神经网络正则化(Dropout)]
B --> C[分类损失函数选择]
C --> D[使用 TensorFlow 实现 CNN]
D --> E[训练和评估模型]
E --> F[拓展应用与展望]
8.2 关键步骤总结列表
- 了解卷积操作的扩展,包括 3D 数据集的处理。
- 掌握 Dropout 正则化的原理和应用,以及与模型集成的关系。
- 根据问题类型和输出类型选择合适的损失函数。
- 使用 TensorFlow Keras API 构建、训练和评估 CNN 模型。
- 处理不同格式的数据,确保模型正常运行。
- 对比不同模型的性能,了解 CNN 的优势。
- 探索 CNN 的拓展应用和未来发展方向。
超级会员免费看
41万+

被折叠的 条评论
为什么被折叠?



