Tensorflow入门T2周学习打卡:CIFAR10彩色图片识别
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
文章目录
前言
本篇是我训练营 TensorFlow 篇的第 2 次学习,主要目标是使用 TensorFlow 完成 CIFAR10 彩色图片识别——和 PyTorch 篇 P2 周完全相同的数据集、相同的 10 分类任务。
感谢 K同学啊 老师的教学,以及 ChatGPT 和 Kimi。
P2 与 T2 的主要对比
| 对比项目 | P2:PyTorch 实现 CIFAR10 | T2:TensorFlow 实现 CIFAR10 |
|---|---|---|
| 深度学习框架 | PyTorch 2.11(CPU 版) | TensorFlow 2.10 + DirectML(AMD GPU) |
| 数据集 | CIFAR10(torchvision 下载约 11 分钟) | 同一个 CIFAR10(tf.keras.datasets 内置) |
| 标签形状 | [32] 一维索引 |
(50000, 1) 二维列向量 |
| 彩色图可视化 | 必须 transpose((1, 2, 0)) 调整通道顺序 |
直接 imshow(NHWC 天生适配 matplotlib) |
| 网络结构 | 3 卷积(64/64/128) + 3 池化 | 3 卷积(32/64/64) + 2 池化 |
| Flatten 后维度 | 128 × 2 × 2 = 512 |
4 × 4 × 64 = 1024 |
| 全连接层 | 512 → 256 → 10 | 1024 → 64 → 10 |
| 模型参数量 | 246,474 | 122,570(约为 P2 的一半) |
| 优化器 | SGD(lr=0.01) | adam |
| 每 epoch 步数 | 1563 个 batch(50000/32) | 1563 steps(完全一致) |
| 10 轮训练准确率 | 59.0% | 78.1% |
| 10 轮测试准确率 | 58.5% | 70.5%(最高 70.85%) |
一、前期工作
1. 设置 GPU
和 T1 周完全相同的流程,AMD DirectML 环境(py310)直接使用:
import tensorflow as tf
gpus = tf.config.list_physical_devices("GPU")
if gpus:
gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU
tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量按需使用
tf.config.set_visible_devices([gpu0],"GPU")
2. 导入数据
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()
3. 归一化
# 将像素的值标准化至0到1的区间内。
train_images, test_images = train_images / 255.0, test_images / 255.0
train_images.shape,test_images.shape,train_labels.shape,test_labels.shape
((50000, 32, 32, 3), (10000, 32, 32, 3), (50000, 1), (10000, 1))
这一段的意思是:训练集 50,000 张 + 测试集 10,000 张,图片形状 (32, 32, 3)——高 32、宽 32、RGB 3 通道,通道在最后是 TensorFlow 的 NHWC 约定。
标签的形状是 (50000, 1) 而不是 (50000,)。T1 周 MNIST 的标签是一维数组,CIFAR10 的标签是二维列向量(每个标签外面还包了一层)。这个差异会在可视化取标签时体现出来(train_labels[i][0] 要多取一层)。
4. 可视化
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer','dog', 'frog', 'horse', 'ship', 'truck']
plt.figure(figsize=(20,10))
for i in range(20):
plt.subplot(5,10,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.xlabel(class_names[train_labels[i][0]])
plt.show()
![![在这里插入数据示例图片]](https://i-blog.csdnimg.cn/direct/571941e9e77541c89fb8ef1b80acbbb7.png)
为什么 T2 不需要 transpose((1, 2, 0))? P2 周彩色图可视化时必须用 transpose((1, 2, 0)) 把 PyTorch 的 [C, H, W] 转成 matplotlib 要的 [H, W, C];而 TensorFlow 的数据天生就是 (H, W, C) 的 NHWC 格式,恰好是 matplotlib 需要的格式,直接 imshow 即可。
为什么设置了 cmap=plt.cm.binary 显示的还是彩色图? cmap 只对单通道灰度图生效,传入的是 RGB 三通道彩色图时,cmap 参数会被直接忽略。所以 T1 周 MNIST 灰度图受 cmap 控制显示黑白,本周彩色图不受任何影响。
二、构建 CNN 网络
1. 网络结构
model = models.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)), #卷积层1,卷积核3*3
layers.MaxPooling2D((2, 2)), #池化层1,2*2采样
layers.Conv2D(64, (3, 3), activation='relu'), #卷积层2,卷积核3*3
layers.MaxPooling2D((2, 2)), #池化层2,2*2采样
layers.Conv2D(64, (3, 3), activation='relu'), #卷积层3,卷积核3*3
layers.Flatten(), #Flatten层,连接卷积层与全连接层
layers.Dense(64, activation='relu'), #全连接层,特征进一步提取
layers.Dense(10) #输出层,输出预期结果
])
model.summary() # 打印网络结构
这一段的意思是:输入形状从 T1 周的 (28, 28, 1) 变成 (32, 32, 3)(彩色图 3 通道),网络也从"2 卷积 + 2 池化"加深到"3 卷积 + 2 池化"。和 P2 周 PyTorch 版的结构对比:
| 层 | P2(PyTorch) | T2(TensorFlow/Keras) | 差异 |
|---|---|---|---|
| 卷积 1 | nn.Conv2d(3, 64, 3) |
Conv2D(32, (3,3)) |
输出通道 64 → 32 |
| 卷积 2 | nn.Conv2d(64, 64, 3) |
Conv2D(64, (3,3)) |
一致 |
| 卷积 3 | nn.Conv2d(64, 128, 3) |
Conv2D(64, (3,3)) |
输出通道 128 → 64 |
| 池化次数 | 3 次(conv3 后还有 pool3) | 2 次(conv3 后直接展平) | T2 少一次池化 |
| 展平维度 | 128×2×2 = 512 |
64×4×4 = 1024 |
殊途不同归 |
| 全连接 | 512→256→10 |
1024→64→10 |
T2 的隐藏层更小 |
两个网络都是"3 卷积 + 2 全连接"的骨架,但 P2 靠第三次池化把特征图压到 2×2、用 128 通道堆出 512 维;T2 少一次池化,保留 4×4 的空间尺寸、用 64 通道得到 1024 维。这也导致 T2 的参数量(12.2 万)只有 P2(24.6 万)的一半,P2 的大头在全连接层 512×256,T2 把特征压到 64 维就输出,省了近 7 万参数。
2. 模型结构与 shape 推导
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 30, 30, 32) 896
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 15, 15, 32) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 13, 13, 64) 18496
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 6, 6, 64) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 4, 4, 64) 36928
_________________________________________________________________
flatten (Flatten) (None, 1024) 0
_________________________________________________________________
dense (Dense) (None, 64) 65600
_________________________________________________________________
dense_1 (Dense) (None, 10) 650
=================================================================
Total params: 122,570
Trainable params: 122,570
Non-trainable params: 0
_________________________________________________________________
shape 推导规则不变(3×3 卷积无 padding 尺寸减 2,2×2 池化尺寸减半),和 P2 周的推导过程逐项对照:
P2(3 次池化): 32 → conv 30 → pool 15 → conv 13 → pool 6 → conv 4 → pool 2 → 128×2×2 = 512
T2(2 次池化): 32 → conv 30 → pool 15 → conv 13 → pool 6 → conv 4 → Flatten 4×4×64 = 1024
参数量验算(方法见 P2、P4 周笔记):
| 层 | 计算式 | 参数量 |
|---|---|---|
| conv2d | 3 × 32 × 3 × 3 + 32 |
896 |
| conv2d_1 | 32 × 64 × 3 × 3 + 64 |
18,496 |
| conv2d_2 | 64 × 64 × 3 × 3 + 64 |
36,928 |
| dense | 1024 × 64 + 64 |
65,600 |
| dense_1 | 64 × 10 + 10 |
650 |
| 合计 | 122,570 ✓ |
与 model.summary() 输出完全一致。CIFAR10 的参数量比 MNIST(T1 周 121,930)只多了 640,因为网络骨架几乎一样,只是第一层卷积的输入通道从 1 变成 3(多了 32×2×3×3=576 个参数)加上输出通道配置略有不同。
三、编译
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
和 T1 周完全一致:Adam 优化器 + 稀疏交叉熵(标签是索引不是 one-hot)+ from_logits=True(输出层没接 softmax)。
四、训练模型
history = model.fit(train_images, train_labels, epochs=10,
validation_data=(test_images, test_labels))
fit 默认 batch_size=32,每个 epoch 是 ⌈50000 / 32⌉ = 1563 个 step——和 P2 周 DataLoader 的 1563 个 batch 完全一致,框架不同、数学相同。
Epoch 1/10
1563/1563 [==============================] - 7s 4ms/step - loss: 1.5787 - accuracy: 0.4231 - val_loss: 1.3765 - val_accuracy: 0.5084
Epoch 2/10
1563/1563 [==============================] - 6s 4ms/step - loss: 1.1883 - accuracy: 0.5767 - val_loss: 1.1512 - val_accuracy: 0.5957
Epoch 3/10
1563/1563 [==============================] - 7s 4ms/step - loss: 1.0238 - accuracy: 0.6399 - val_loss: 1.0110 - val_accuracy: 0.6442
Epoch 4/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.9214 - accuracy: 0.6760 - val_loss: 0.9369 - val_accuracy: 0.6651
Epoch 5/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.8500 - accuracy: 0.7019 - val_loss: 0.9450 - val_accuracy: 0.6725
Epoch 6/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.7923 - accuracy: 0.7220 - val_loss: 0.8756 - val_accuracy: 0.7023
Epoch 7/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.7426 - accuracy: 0.7392 - val_loss: 0.9143 - val_accuracy: 0.6812
Epoch 8/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.6967 - accuracy: 0.7540 - val_loss: 0.8602 - val_accuracy: 0.7011
Epoch 9/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.6542 - accuracy: 0.7705 - val_loss: 0.8588 - val_accuracy: 0.7085
Epoch 10/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.6187 - accuracy: 0.7809 - val_loss: 0.8609 - val_accuracy: 0.7050
训练结果分析:
本次共训练 10 个 epoch。训练侧收敛迅速:训练 loss 从 1.5787 降至 0.6187,训练准确率从 42.31% 一路升到 78.09%,10 轮内没有看到平台期。验证侧同样稳步爬升:验证准确率从 50.84% 升至 70.50%,第 9 轮达到最高的 70.85%。
教程的达标要求是测试准确率 72%,本次最高 70.85%,略低于目标。但对比 P2 周 PyTorch 版同样 10 轮的 58.5%,提升了约 12 个百分点。
过拟合信号在尾声出现:第 9 → 10 轮,训练准确率继续上升(77.05% → 78.09%),但验证准确率回落(70.85% → 70.50%)、val_loss 微升(0.8588 → 0.8609),训练与验证的准确率差距拉大到 7.6 个百分点——和 T1 周 MNIST 尾段的轻度过拟合现象一致,只是 CIFAR10 上来得更早、更明显。这说明继续加 epoch 收益有限,想把准确率推过 72%,应该换思路:数据增强(P5 周学过)、加入 BatchNorm(P3 周)、换更深的网络(P7、P8 周的 VGG-16、ResNet-34)。
与 P2 周的对比
- P2(PyTorch CPU + SGD):训练 59.0% / 测试 58.5%,两者几乎贴合,是典型的欠拟合——SGD 固定学习率收敛慢,10 轮还没学够;
- T2(TensorFlow GPU + Adam):训练 78.1% / 测试 70.5%,训练明显超过验证,是轻度过拟合——Adam 收敛快,10 轮已经开始记忆训练集细节。
同一数据集上呈现出欠拟合与过拟合两种截然不同的状态,主要变量是优化器(SGD lr=0.01 vs Adam 自适应学习率,两者对比见 P9 周笔记);当然两边网络细节也不完全相同(P2 三次池化 128 通道、T2 两次池化 64 通道),不是严格的单变量实验,但 Adam 带来的收敛速度差异是主导因素。
五、预测
通过模型进行预测得到的是每一个类别的分数,数字越大该图片为该类别的可能性越大。
plt.imshow(test_images[1])

import numpy as np
pre = model.predict(test_images)
print(class_names[np.argmax(pre[1])])
313/313 [==============================] - 0s 1ms/step
ship
pre 的 shape 是 (10000, 10),取 pre[1] 的 argmax 得到类别索引,再查 class_names 还原成类别名——test_images[1] 是一张**船(ship)**的图片,预测正确。
六、模型评估
1. 训练曲线
import matplotlib.pyplot as plt
plt.plot(history.history['accuracy'], label='accuracy')
plt.plot(history.history['val_accuracy'], label = 'val_accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.ylim([0.5, 1])
plt.legend(loc='lower right')
plt.show()
![![在这里插入训练曲线图片]](https://i-blog.csdnimg.cn/direct/b5fba6ba3fa5450ca7c79dc573bb4fb8.png)
从曲线可以清楚看到四、训练结果分析中的判断:训练准确率持续爬升、训练 loss 持续下降;验证准确率在 70% 附近开始走平,val_loss 在最后两轮停止下降并微微抬头——过拟合的拐点就出现在第 9 轮附近。
2. 测试集最终评估
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print(test_acc)
0.7049999833106995
model.evaluate() 在全部 10,000 张测试图片上评估,结果与最后一轮的 val_accuracy 一致(评估的就是同一个测试集):测试准确率 70.50%。313 是评估步数(⌈10000 / 32⌉)。
总结
本周学习了以下几个方面:
- 完成了 TensorFlow 版 CIFAR10 全流程:从
load_data()→ 归一化 →Sequential建模 →compile/fit→ 预测 →evaluate,T1 周建立的 Keras 流程感在更难的彩色数据集上得到巩固,测试准确率达到 70.50%(最高 70.85%)。 - 理清了彩色图可视化的框架差异:PyTorch 的 NCHW 必须
transpose((1, 2, 0))才能用 matplotlib 显示,TensorFlow 的 NHWC 天生适配、imshow直接显示;并理解了cmap只对单通道灰度图生效、对彩色图无效的原因。 - 完成了第三轮 shape 推导与参数验算:
32 → 30 → 15 → 13 → 6 → 4,展平4×4×64 = 1024,总参数量 122,570 与 summary 完全一致;并通过与 P2 网络(3 池化、512 展平、24.6 万参数)的结构对照,理解了"池化次数与通道配置如何共同决定展平维度和参数规模"。
更多推荐


所有评论(0)