- 🍨本文为🔗365天深度学习训练营中的学习记录博客
- 🍖原作者:K同学啊
学习目的:初步使用tensorflow框架进行深度学习的整体运行
一、 前期准备
关于环境
- 语言环境:Python3.6
- 编译器:vsCode
- 深度学习环境:TensorFlow 2.6.2
1.数据导入
import tensorflow as tf from tensorflow.keras import datasets, layers, models import matplotlib.pyplot as plt # 导入mnist数据,依次分别为训练集图片、训练集标签、测试集图片、测试集标签 (train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()代码解析:
from tensorflow.keras import datasets, layers, models:
从
tensorflow.keras中导入三个子模块:
datasets:包含一些常用的公开数据集(如 MNIST、CIFAR10 等),方便我们加载。
layers:包含构建神经网络所需的各种层(如卷积层、池化层、全连接层等)。
models:包含构建模型的高级接口,比如Sequential(顺序模型)。
2.归一化
# 将像素的值标准化至0到1的区间内。(对于灰度图片来说,每个像素最大值是255,每个像素最小值是0,也就是直接除以255就可以完成归一化。) train_images, test_images = train_images / 255.0, test_images / 255.0 # 查看数据维数信息 train_images.shape,test_images.shape,train_labels.shape,test_labels.shape代码解析:
train_images, test_images = train_images / 255.0, test_images / 255.0:
图像中的像素值是 0~255 的整数。神经网络通常对输入数据的尺度敏感,较大的数值会导致梯度更新不稳定、训练缓慢。
这里将每个像素值除以 255.0,将所有像素值缩放到
[0, 1]区间。这叫做归一化(Normalization),能有效加速收敛并提高模型稳定性。注意:这里用了浮点数除法(
/255.0),结果变为浮点数。
运行结果:
运行结果解读:
返回的数据被拆分为四个变量:
train_images:训练集图片,是一个形状为(60000, 28, 28)的 NumPy 数组,代表 60000 张 28×28 像素的灰度图。
train_labels:训练集标签,形状为(60000,),每个标签是 0~9 的数字,表示图片对应的手写数字。
test_images:测试集图片,形状为(10000, 28, 28),用于评估模型性能。
test_labels:测试集标签,形状为(10000,)。
3.查看图片
# 将数据集前20个图片数据可视化显示 # 进行图像大小为20宽、10长的绘图(单位为英寸inch) plt.figure(figsize=(20,10)) # 遍历MNIST数据集下标数值0~49 for i in range(20): # 将整个figure分成2行10列,绘制第i+1个子图。 plt.subplot(2,10,i+1) # 设置不显示x轴刻度 plt.xticks([]) # 设置不显示y轴刻度 plt.yticks([]) # 设置不显示子图网格线 plt.grid(False) # 图像展示,cmap为颜色图谱,"plt.cm.binary"为matplotlib.cm中的色表 plt.imshow(train_images[i], cmap=plt.cm.binary) # 设置x轴标签显示为图片对应的数字 plt.xlabel(train_labels[i]) # 显示图片 plt.show()运行结果:
4.图片处理
#调整数据到我们需要的格式 train_images = train_images.reshape((60000, 28, 28, 1)) test_images = test_images.reshape((10000, 28, 28, 1)) train_images.shape,test_images.shape,train_labels.shape,test_labels.shape """ 输出:((60000, 28, 28, 1), (10000, 28, 28, 1), (60000,), (10000,)) """运行结果
原本图片形状是
(高度, 宽度),即(28, 28)。但对于卷积层(Conv2D),Keras 要求输入形状为(高度, 宽度, 通道数)。MNIST 是灰度图,通道数为 1(彩色图为 3)。
reshape方法将每个样本从(28, 28)变为(28, 28, 1),相当于添加了最后一个维度,表示通道数。训练集变为
(60000, 28, 28, 1),测试集变为(10000, 28, 28, 1)。
二、训练模型
1. 构建CNN网络模型
# 创建并设置卷积神经网络 # 卷积层:通过卷积操作对输入图像进行降维和特征抽取 # 池化层:是一种非线性形式的下采样。主要用于特征降维,压缩数据和参数的数量,减小过拟合,同时提高模型的鲁棒性。 # 全连接层:在经过几个卷积和池化层之后,神经网络中的高级推理通过全连接层来完成。 model = models.Sequential([ # 设置二维卷积层1,设置32个3*3卷积核,activation参数将激活函数设置为ReLu函数,input_shape参数将图层的输入形状设置为(28, 28, 1) # ReLu函数作为激活励函数可以增强判定函数和整个神经网络的非线性特性,而本身并不会改变卷积层 # 相比其它函数来说,ReLU函数更受青睐,这是因为它可以将神经网络的训练速度提升数倍,而并不会对模型的泛化准确度造成显著影响。 layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)), #池化层1,2*2采样 layers.MaxPooling2D((2, 2)), # 设置二维卷积层2,设置64个3*3卷积核,activation参数将激活函数设置为ReLu函数 layers.Conv2D(64, (3, 3), activation='relu'), #池化层2,2*2采样 layers.MaxPooling2D((2, 2)), layers.Flatten(), #Flatten层,连接卷积层与全连接层 layers.Dense(64, activation='relu'), #全连接层,特征进一步提取,64为输出空间的维数,activation参数将激活函数设置为ReLu函数 layers.Dense(10) #输出层,输出预期结果,10为输出空间的维数 ]) # 打印网络结构 model.summary()代码解析:
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))
二维卷积层,是 CNN 的核心。
32:该层有 32 个卷积核(滤波器),每个卷积核会提取一种特征,因此输出特征图(feature map)的深度为 32。
(3, 3):卷积核的大小为 3×3 像素。
activation='relu':激活函数使用 ReLU(Rectified Linear Unit),公式为max(0, x)。它能引入非线性,并且计算简单,有助于缓解梯度消失问题。
input_shape=(28, 28, 1):指定输入数据的形状,只有第一层需要设置,后续层会自动推断。该层输出形状为
(None, 26, 26, 32)(因为 3×3 卷积不填充时,高度和宽度各减 2)。
layers.MaxPooling2D((2, 2))
最大池化层,池化窗口大小为 2×2,步长默认为 2。
作用:在 2×2 区域内取最大值,将特征图尺寸减半(高度和宽度各减半),减少参数数量,同时保留主要特征,并提高模型的平移不变性。
输出形状为
(None, 13, 13, 32)。
layers.Conv2D(64, (3, 3), activation='relu')
第二个卷积层,64 个 3×3 卷积核,激活函数 ReLU。
输出形状为
(None, 11, 11, 64)(因为 13-3+1=11)。
layers.MaxPooling2D((2, 2))
再次最大池化,输出形状为
(None, 5, 5, 64)。
layers.Flatten()
展平层。将多维特征图(5×5×64)展平成一维向量(长度为 5×5×64=1600),以便输入到全连接层。
layers.Dense(64, activation='relu')
全连接层(稠密层),有 64 个神经元,激活函数 ReLU。
这层对提取的高层特征进行非线性组合,进一步提取特征。
layers.Dense(10)
输出层,10 个神经元,对应 10 个类别(数字 0~9)。这里没有指定激活函数,因为后续损失函数
SparseCategoricalCrossentropy会设置from_logits=True,表示直接输出未经过 softmax 的 logits(原始分数),内部会自动计算 softmax 并计算交叉熵。
运行结果:
2.编辑模型
""" 这里设置优化器、损失函数以及metrics """ # model.compile()方法用于在配置训练方法时,告知训练时用的优化器、损失函数和准确率评测标准 model.compile( # 设置优化器为Adam优化器 optimizer='adam', # 设置损失函数为交叉熵损失函数(tf.keras.losses.SparseCategoricalCrossentropy()) # from_logits为True时,会将y_pred转化为概率(用softmax),否则不进行转换,通常情况下用True结果更稳定 loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), # 设置性能指标列表,将在模型训练时监控列表中的指标 metrics=['accuracy'])3.训练模型
""" 这里设置输入训练数据集(图片及标签)、验证数据集(图片及标签)以及迭代次数epochs 关于model.fit()函数的具体介绍可参考我的博客: https://blog.csdn.net/qq_38251616/article/details/122321757 """ history = model.fit( # 输入训练集图片 train_images, # 输入训练集标签 train_labels, # 设置10个epoch,每一个epoch都将会把所有的数据输入模型完成一次训练。 epochs=10, # 设置验证集 validation_data=(test_images, test_labels))运行结果:
三、模型预测
plt.imshow(test_images[1])运行结果:
pre = model.predict(test_images) # 对所有测试图片进行预测 pre[1] # 输出第一张图片的预测结果运行结果:
四、总结
之前已经学习了pytorch框架的深度学习基础,本次开始学习tensorflow框架。二者在代码运行上还是有不一样的框架
| TensorFlow | PyTorch | |
|---|---|---|
| 数据格式 | 加载后直接是 NumPy 数组((60000, 28, 28))。 | 加载后是Dataset对象,需要配合DataLoader才能批量取出。 |
| 通道顺序 | HWC(高、宽、通道)。灰度图是(28, 28, 1)。 | CHW(通道、高、宽)。灰度图是(1, 28, 28)。你看到的imgs.shape输出为[32, 1, 28, 28] |
| 归一化 | 手动除以255.0。 | 使用transforms.ToTensor(),它不仅把图片转为张量,自动除以 255归一化到 [0,1],还自动把 HWC 转为 CHW。 |
| 批量读取 | 没有专门的加载器,直接用切片。 | 使用DataLoader(train_dl),它能自动打乱(shuffle=True)数据、按批次(batch_size=32)打包,还支持多线程读取。 |
在模型构建上:
TF:用
models.Sequential([层1, 层2, ...])按顺序“组装”层,层与层之间自动传递数据。PyTorch:必须新建一个类,继承
nn.Module。在__init__里定义好“零件”(比如self.conv1,self.fc1)。在forward(self, x)函数里,手动写清楚数据流向(x = self.pool1(F.relu(self.conv1(x))))。
在训练过程也有明显的不同:
TF (T1):一行代码搞定。
history = model.fit(train_images, train_labels, epochs=10, validation_data=(test_images, test_labels))
PyTorch (P1):需要双层循环(
for epoch套for X, y in dataloader):清空梯度:
optimizer.zero_grad()。前向传播:
pred = model(X)。计算损失:
loss = loss_fn(pred, y)。反向传播:
loss.backward()(计算每个参数的梯度)。更新参数:
optimizer.step()(用计算好的梯度去更新权重)。手动计算准确率:
(pred.argmax(1) == y).sum()。