3. TensorFlow模型训练基础(下):CIFAR-10图像分类实战
上一节我们把TensorFlow的基础API捋了一遍。这一节,咱们直接上手一个真实的数据集——CIFAR-10。说实话,这个数据集在嵌入式部署圈子里特别常见,因为它既有挑战性,又不会大到让训练卡死。我个人习惯,每次评估一个新模型架构,都会先用CIFAR-10跑一遍,看看基础能力。
3.1 CIFAR-10数据集:32x32的小世界
CIFAR-10包含10个类别的彩色图像,每张32x32像素。你想想看,这么小的图,人眼都容易看错,模型要从中提取特征,确实需要点技巧。
| 类别 | 标签 | 训练样本数 | 测试样本数 |
|---|---|---|---|
| 飞机 | 0 | 5000 | 1000 |
| 汽车 | 1 | 5000 | 1000 |
| 鸟 | 2 | 5000 | 1000 |
| 猫 | 3 | 5000 | 1000 |
| 鹿 | 4 | 5000 | 1000 |
| 狗 | 5 | 5000 | 1000 |
| 青蛙 | 6 | 5000 | 1000 |
| 马 | 7 | 5000 | 1000 |
| 船 | 8 | 5000 | 1000 |
| 卡车 | 9 | 5000 | 1000 |
加载数据很简单,Keras直接提供了接口:
import tensorflow as tf
from tensorflow.keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
# 归一化到[0,1]区间
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# 标签转为one-hot编码
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)
print(f'训练集形状: {x_train.shape}')
print(f'测试集形状: {x_test.shape}')
3.2 卷积神经网络:Conv2D与MaxPooling2D
全连接网络处理32x32x3的图像,参数量会爆炸。举个例子,第一层如果接512个神经元,光这一层就有32*32*3*512 ≈ 157万个参数。而卷积层通过局部连接和权值共享,参数量少得多。
我建议的入门级CNN结构是这样的:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
model = Sequential([
# 第一组卷积+池化
Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)),
Conv2D(32, (3, 3), activation='relu', padding='same'),
MaxPooling2D(pool_size=(2, 2)),
# 第二组卷积+池化
Conv2D(64, (3, 3), activation='relu', padding='same'),
Conv2D(64, (3, 3), activation='relu', padding='same'),
MaxPooling2D(pool_size=(2, 2)),
# 第三组卷积+池化
Conv2D(128, (3, 3), activation='relu', padding='same'),
Conv2D(128, (3, 3), activation='relu', padding='same'),
MaxPooling2D(pool_size=(2, 2)),
# 全连接分类层
Flatten(),
Dense(256, activation='relu'),
Dropout(0.5),
Dense(10, activation='softmax')
])
model.summary()
这里有几个关键点:
- Conv2D:卷积核提取局部特征。padding='same'保证输出尺寸不变,否则图像会越卷越小。
- MaxPooling2D:下采样,保留最显著的特征,同时减少参数量。2x2的池化核会把尺寸减半。
- Dropout:随机丢弃一部分神经元,防止过拟合。0.5表示训练时50%的神经元会被随机关闭。
3.3 数据增强:让模型见多识广
CIFAR-10只有5万张训练图,对于深度学习来说其实不多。数据增强就是在原始数据上做随机变换,生成更多样化的训练样本。说白了,就是让模型见过各种姿势的猫,它才能真正认识猫。
Keras的ImageDataGenerator用起来很方便:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
rotation_range=15, # 随机旋转±15度
width_shift_range=0.1, # 水平平移10%
height_shift_range=0.1, # 垂直平移10%
horizontal_flip=True, # 随机水平翻转
zoom_range=0.1, # 随机缩放10%
fill_mode='nearest' # 填充方式
)
# 在训练时使用数据增强
datagen.fit(x_train)
# 训练时用flow方法
history = model.fit(
datagen.flow(x_train, y_train, batch_size=64),
steps_per_epoch=len(x_train) // 64,
epochs=50,
validation_data=(x_test, y_test)
)
3.4 过拟合控制:训练集与验证集的博弈
过拟合是深度学习最常见的坑。模型在训练集上表现完美,一到测试集就拉胯。为什么会这样?因为模型把训练集的噪声也学进去了。
我总结了几种有效的控制手段:
| 方法 | 原理 | 推荐设置 |
|---|---|---|
| Dropout | 随机丢弃神经元,防止共适应 | 全连接层用0.3-0.5 |
| L2正则化 | 在损失函数中加入权重的平方和 | kernel_regularizer=l2(0.001) |
| 早停法 | 验证集损失不再下降时停止训练 | patience=10个epoch |
| 数据增强 | 增加训练数据多样性 | 见3.3节 |
| 减少模型容量 | 减少层数或神经元数量 | 根据验证集表现调整 |
早停法的实现:
from tensorflow.keras.callbacks import EarlyStopping
early_stop = EarlyStopping(
monitor='val_loss',
patience=10,
restore_best_weights=True
)
history = model.fit(
datagen.flow(x_train, y_train, batch_size=64),
steps_per_epoch=len(x_train) // 64,
epochs=100,
validation_data=(x_test, y_test),
callbacks=[early_stop]
)
3.5 模型评估:不只是看准确率
训练完成后,我们需要全面评估模型。准确率只是一个维度,混淆矩阵和分类报告能告诉我们更多信息。
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np
# 预测
y_pred = model.predict(x_test)
y_pred_classes = np.argmax(y_pred, axis=1)
y_true_classes = np.argmax(y_test, axis=1)
# 分类报告
print(classification_report(y_true_classes, y_pred_classes))
# 混淆矩阵
cm = confusion_matrix(y_true_classes, y_pred_classes)
print(cm)
分类报告会给出每个类别的精确率、召回率和F1分数。如果某个类别的F1特别低,说明模型对这个类别的识别能力不足。比如猫和狗经常被混淆,这是CIFAR-10上的常见问题。
3.6 知识体系总览
下面这张图总结了本节的核心流程和知识点之间的关系:
从数据加载到模型评估,每一步都有坑。但只要你按照这个流程走一遍,CIFAR-10的准确率做到80%以上不成问题。我自己的经验是,不加数据增强大概70%左右,加了能到82%-85%,再配合调参和正则化,可以逼近90%。
- CIFAR-10是32x32的彩色小图,适合快速验证模型
- CNN通过Conv2D+MaxPooling2D提取特征,参数量远少于全连接
- 数据增强是提升泛化能力最有效的手段之一
- 过拟合控制需要组合使用Dropout、正则化、早停等方法
- 评估时不要只看准确率,混淆矩阵能暴露更多问题