第77课:实战:图像分类(CNN)完整项目
学习目标
完成本课学习后,你将能够:
- 理解卷积神经网络(CNN)处理图像数据的基本原理。
- 熟练使用TensorFlow/Keras构建一个完整的CNN模型。
- 掌握图像数据预处理(归一化、标签编码)的关键步骤。
- 完成一个从数据加载、模型训练到评估和可视化的完整项目流程。
- 学会分析模型性能,并初步应用优化技巧。
核心概念
什么是CNN?
想象一下,你看一张猫的照片。你的大脑首先会注意到它的尖耳朵、胡须和毛茸茸的轮廓,然后把这些局部特征组合起来,判断出这是一只猫。卷积神经网络(CNN) 的工作原理与此类似。
它主要由以下几层组成:
- 卷积层(Convolutional Layer):像一个扫描仪,在图像上滑动一个小窗口(称为“滤波器”或“核”),提取局部特征(如边缘、纹理)。
- 池化层(Pooling Layer):像一个简化器,对特征图进行下采样(例如,取2x2区域内的最大值),减少数据量,同时保留最重要的特征。
- 全连接层(Fully Connected Layer):在提取并简化特征后,将所有特征“铺平”,连接到一个传统的神经网络,用于最终的分类决策。
机器学习流水线
一个完整的项目遵循标准流水线:数据获取 -> 数据预处理 -> 模型构建 -> 模型训练 -> 模型评估与调优。
代码示例
我们将使用经典的CIFAR-10数据集(包含飞机、汽车、鸟等10个类别的6万张小图片),构建一个CNN分类器。
# 导入必要的库
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
# 1. 数据加载与预处理
# 加载CIFAR-10数据集(Keras内置)
(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()
# 数据归一化:将像素值从[0, 255]缩放到[0, 1]之间,有助于模型稳定训练
train_images, test_images = train_images / 255.0, test_images / 255.0
# 查看数据形状
print("训练图像形状:", train_images.shape) # (50000, 32, 32, 3)
print("训练标签形状:", train_labels.shape) # (50000, 1)
# 2. 构建CNN模型
model = models.Sequential([
# 第一个卷积块:提取基本特征
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
layers.MaxPooling2D((2, 2)), # 池化,尺寸减半 -> (16, 16)
# 第二个卷积块:提取更复杂的特征
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)), # -> (8, 8)
# 第三个卷积块
layers.Conv2D(64, (3, 3), activation='relu'),
# 此时特征图尺寸为 (6, 6, 64)
# 将多维特征图展平,以便输入全连接层
layers.Flatten(),
# 全连接层:进行分类决策
layers.Dense(64, activation='relu'), # 隐藏层
layers.Dense(10) # 输出层,10个类别,不加激活函数
])
# 打印模型结构摘要
model.summary()
# 3. 编译模型
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
# 4. 训练模型
history = model.fit(train_images, train_labels, epochs=10,
validation_data=(test_images, test_labels))
# 5. 评估模型
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print(f"\n测试集准确率: {test_acc*100:.2f}%")
# 6. 可视化训练过程
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='训练准确率')
plt.plot(history.history['val_accuracy'], label='验证准确率')
plt.xlabel('Epoch')
plt.ylabel('准确率')
plt.legend()
plt.grid(True)
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='训练损失')
plt.plot(history.history['val_loss'], label='验证损失')
plt.xlabel('Epoch')
plt.ylabel('损失')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# 7. 进行预测(示例)
predictions = model.predict(test_images[:5])
predicted_classes = tf.argmax(predictions, axis=1)
# 类别名称
class_names = ['飞机', '汽车', '鸟', '猫', '鹿', '狗', '青蛙', '马', '船', '卡车']
for i in range(5):
print(f"真实标签: {class_names[test_labels[i][0]]}, 预测标签: {class_names[predicted_classes[i]]}")
实践练习
练习1:基础巩固
修改上述代码中的模型结构,将第一个卷积层的滤波器数量从32改为64。观察模型参数量和训练结果的变化。
练习2:早停法应用
使用tf.keras.callbacks.EarlyStopping回调函数来监控验证集损失(val_loss),当验证损失连续3个epoch不再下降时,提前停止训练。请将此集成到模型训练流程中。
练习3:挑战提升(选做)
尝试构建一个更深或使用不同技巧(如Dropout层)的模型,目标是在CIFAR-10测试集上将准确率提升到75%以上。提示:Dropout层可以放在Flatten层之后、Dense层之前,用于防止过拟合。
常见错误
- 忘记数据归一化:像素值范围是0-255,直接输入模型会导致训练不稳定、收敛缓慢。务必将其除以255.0。
- 模型过于简单或复杂:对于CIFAR-10,上面代码的结构是一个不错的起点。模型太简单(层数太少)会导致欠拟合,准确率上不去;太复杂则容易过拟合,在训练集上表现很好,测试集上表现差。
- 不理解模型输出与损失函数:输出层没有使用
softmax激活函数,因此我们在损失函数中设置from_logits=True,让损失函数内部处理。或者,你可以在最后一层加上softmax,并使用普通的sparse_categorical_crossentropy。 - 忽略验证集的作用:训练过程中只看训练准确率是危险的。一定要使用
validation_data监控验证集表现,这是发现过拟合的关键。 - 输出层神经元数量错误:最后一个
Dense层的神经元数量必须等于你的类别数(这里是10)。
小结
本节课,我们完成了一个完整的CNN图像分类项目:
- 数据层面:加载了CIFAR-10数据集,并进行了关键的归一化预处理。
- 模型层面:构建了一个典型的“卷积-池化”堆叠的CNN架构,理解了每一层的作用。
- 流程层面:实践了编译、训练、评估和可视化的标准机器学习项目流程。
- 核心概念:CNN通过逐层抽象,从原始像素中学习到有判别力的高级特征,这是其处理图像等网格状数据的威力所在。
掌握这个项目,你就拥有了用深度学习解决图像问题的基本蓝图。下一课,我们将转向另一个经典领域——自然语言处理(NLP),看看如何处理文本数据。
练习编辑器
rust
Loading...