手写数据集的深度学习

import numpy as np

import torch

import torchvision

from torchvision.datasets import mnist

import torchvision.transforms as transforms

from torch.utils.data import DataLoader

import torch.nn.functional as F

import torch.optim as optim

from torch import nn

from torch.utils.tensorboard import SummaryWriter

import matplotlib.pyplot as plt

==================== 定义超参数 ====================

train_batch_size = 64 # 训练时每个批次的样本数量

test_batch_size = 128 # 测试时每个批次的样本数量

learning_rate = 0.01 # 初始学习率

num_epoches = 20 # 训练的总轮数

momentum = 0.9 # SGD优化器的动量参数

==================== 数据准备和预处理 ====================

定义预处理函数

transform = transforms.Compose([

transforms.ToTensor(), # 将PIL图像转换为Tensor,并自动归一化到[0,1]范围

transforms.Normalize([0.5], [0.5]) # 标准化到[-1,1]范围,公式:(x-0.5)/0.5

])

下载数据,并对数据进行预处理

train_dataset = mnist.MNIST('../data/', train=True, transform=transform, download=True)

训练数据集:存储在../data/目录,训练模式,应用预处理变换,如果不存在则下载

test_dataset = mnist.MNIST('../data/', train=False, transform=transform)

测试数据集:存储在../data/目录,测试模式,应用相同的预处理变换

得到数据加载器

train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)

训练数据加载器:批次大小64,打乱数据顺序

test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)

测试数据加载器:批次大小128,不打乱数据顺序

==================== 可视化源数据 ====================

获取一批测试数据用于可视化

examples = enumerate(test_loader) # 枚举测试数据加载器,返回索引和批次

batch_idx, (example_data, example_targets) = next(examples) # 获取第一个批次

print(f"示例数据形状: {example_data.shape}") # 打印数据形状,应该是 [128, 1, 28, 28]

显示前6个图像

fig = plt.figure(figsize=(10, 6)) # 创建图形,尺寸10x6英寸

for i in range(6):

plt.subplot(2, 3, i+1) # 创建2行3列的子图,当前是第i+1个

plt.tight_layout() # 自动调整子图参数,使之填充整个图像区域

plt.imshow(example_data[i][0], cmap='gray', interpolation='none') # 显示灰度图像

plt.title('真实标签: {}'.format(example_targets[i])) # 设置标题为真实标签

plt.xticks([]) # 移除x轴刻度

plt.yticks([]) # 移除y轴刻度

plt.savefig('mnist_samples.png') # 保存图像到文件

print("MNIST样本图像已保存到: mnist_samples.png")

==================== 构建模型 ====================

class Net(nn.Module): # 定义神经网络类,继承自nn.Module

def init(self, in_dim, n_hidden_1, n_hidden_2, out_dim):

super(Net, self).init() # 调用父类构造函数

self.flatten = nn.Flatten() # 展平层,将多维输入一维化

使用Sequential构建网络层

self.layer1 = nn.Sequential( # 第一个隐藏层序列

nn.Linear(in_dim, n_hidden_1), # 全连接层,输入维度in_dim,输出维度n_hidden_1

nn.BatchNorm1d(n_hidden_1) # 批归一化层,加速训练并提高稳定性

)

self.layer2 = nn.Sequential( # 第二个隐藏层序列

nn.Linear(n_hidden_1, n_hidden_2), # 全连接层

nn.BatchNorm1d(n_hidden_2) # 批归一化层

)

self.out = nn.Sequential( # 输出层序列

nn.Linear(n_hidden_2, out_dim) # 输出层,输出维度out_dim(10个类别)

)

def forward(self, x): # 定义前向传播过程

x = self.flatten(x) # 展平输入,从[batch,1,28,28]变为[batch,784]

x = F.relu(self.layer1(x)) # 第一层:线性变换+批归一化+ReLU激活

x = F.relu(self.layer2(x)) # 第二层:线性变换+批归一化+ReLU激活

x = F.softmax(self.out(x), dim=1) # 输出层:线性变换+softmax,按行计算概率分布

return x

==================== 模型实例化和配置 ====================

设置设备

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

如果有GPU则使用GPU,否则使用CPU

print(f"使用设备: {device}")

实例化模型

model = Net(28 * 28, 300, 100, 10) # 输入28x28=784,隐藏层300和100,输出10个类别

model.to(device) # 将模型移动到指定设备(GPU或CPU)

定义损失函数和优化器

criterion = nn.CrossEntropyLoss() # 交叉熵损失函数,适用于多分类问题

optimizer = optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)

SGD优化器:传入模型参数、学习率、动量

打印模型结构

print("模型结构:")

print(model)

==================== 训练模型 ====================

def main(): # 定义主函数,封装训练流程

初始化记录列表

losses = [] # 记录每个epoch的训练损失

acces = [] # 记录每个epoch的训练准确率

eval_losses = [] # 记录每个epoch的测试损失

eval_acces = [] # 记录每个epoch的测试准确率

创建TensorBoard写入器

writer = SummaryWriter(log_dir='logs', comment='train-loss')

创建SummaryWriter对象,日志目录为'logs',注释为'train-loss'

print("开始训练...")

for epoch in range(num_epoches): # 遍历每个训练轮次

train_loss = 0 # 初始化当前epoch的训练损失

train_acc = 0 # 初始化当前epoch的训练准确率

设置为训练模式

model.train() # 启用dropout和batch normalization的训练模式

动态修改参数学习率

if epoch % 5 == 0: # 每5个epoch调整一次学习率

optimizer.param_groups[0]['lr'] *= 0.9 # 学习率乘以0.9(衰减)

print("学习率: {:.6f}".format(optimizer.param_groups[0]['lr']))

训练阶段

for img, label in train_loader: # 遍历训练数据加载器中的每个批次

img = img.to(device) # 将图像数据移动到指定设备

label = label.to(device) # 将标签数据移动到指定设备

前向传播

out = model(img) # 模型预测输出

loss = criterion(out, label) # 计算损失

反向传播

optimizer.zero_grad() # 清空之前的梯度

loss.backward() # 反向传播计算梯度

optimizer.step() # 更新模型参数

记录误差

train_loss += loss.item() # 累加批次损失

计算分类的准确率

_, pred = out.max(1) # 获取预测类别(最大概率的索引)

num_correct = (pred == label).sum().item() # 计算正确预测的数量

acc = num_correct / img.shape[0] # 计算准确率

train_acc += acc # 累加批次准确率

记录训练损失和准确率

avg_train_loss = train_loss / len(train_loader) # 计算平均训练损失

avg_train_acc = train_acc / len(train_loader) # 计算平均训练准确率

losses.append(avg_train_loss) # 添加到训练损失列表

acces.append(avg_train_acc) # 添加到训练准确率列表

保存到TensorBoard

writer.add_scalar('Train/Loss', avg_train_loss, epoch) # 记录训练损失

writer.add_scalar('Train/Accuracy', avg_train_acc, epoch) # 记录训练准确率

在测试集上检验结果

eval_loss = 0 # 初始化当前epoch的测试损失

eval_acc = 0 # 初始化当前epoch的测试准确率

将模型改为预测模式

model.eval() # 禁用dropout和使用训练阶段的batch normalization统计量

with torch.no_grad(): # 测试阶段不需要计算梯度,节省内存和计算资源

for img, label in test_loader: # 遍历测试数据加载器中的每个批次

img = img.to(device) # 将图像数据移动到指定设备

label = label.to(device) # 将标签数据移动到指定设备

前向传播

out = model(img) # 模型预测输出

loss = criterion(out, label) # 计算损失

记录误差

eval_loss += loss.item() # 累加批次损失

记录准确率

_, pred = out.max(1) # 获取预测类别

num_correct = (pred == label).sum().item() # 计算正确预测的数量

acc = num_correct / img.shape[0] # 计算准确率

eval_acc += acc # 累加批次准确率

记录测试损失和准确率

avg_eval_loss = eval_loss / len(test_loader) # 计算平均测试损失

avg_eval_acc = eval_acc / len(test_loader) # 计算平均测试准确率

eval_losses.append(avg_eval_loss) # 添加到测试损失列表

eval_acces.append(avg_eval_acc) # 添加到测试准确率列表

保存到TensorBoard

writer.add_scalar('Test/Loss', avg_eval_loss, epoch) # 记录测试损失

writer.add_scalar('Test/Accuracy', avg_eval_acc, epoch) # 记录测试准确率

打印每个epoch的结果

print('epoch: {}, Train Loss: {:.4f}, Train Acc: {:.4f}, Test Loss: {:.4f}, Test Acc: {:.4f}'

.format(epoch, avg_train_loss, avg_train_acc, avg_eval_loss, avg_eval_acc))

关闭TensorBoard写入器

writer.close()

print('训练完成!')

==================== 可视化训练结果 ====================

绘制训练损失

plt.figure(figsize=(12, 4)) # 创建图形,尺寸12x4英寸

plt.subplot(1, 2, 1) # 创建1行2列的子图,当前是第1个

plt.title('训练损失') # 设置子图标题

plt.plot(np.arange(len(losses)), losses, label='Train Loss') # 绘制训练损失曲线

plt.plot(np.arange(len(eval_losses)), eval_losses, label='Test Loss') # 绘制测试损失曲线

plt.xlabel('Epoch') # 设置x轴标签

plt.ylabel('Loss') # 设置y轴标签

plt.legend() # 显示图例

plt.subplot(1, 2, 2) # 创建1行2列的子图,当前是第2个

plt.title('准确率') # 设置子图标题

plt.plot(np.arange(len(acces)), acces, label='Train Accuracy') # 绘制训练准确率曲线

plt.plot(np.arange(len(eval_acces)), eval_acces, label='Test Accuracy') # 绘制测试准确率曲线

plt.xlabel('Epoch') # 设置x轴标签

plt.ylabel('Accuracy') # 设置y轴标签

plt.legend() # 显示图例

plt.tight_layout() # 自动调整子图参数

plt.savefig('training_results.png') # 保存训练结果图

print("训练结果图已保存到: training_results.png")

==================== 最终测试 ====================

model.eval() # 设置为评估模式

final_test_acc = 0 # 初始化最终测试准确数

total_samples = 0 # 初始化总样本数

with torch.no_grad(): # 不计算梯度

for img, label in test_loader: # 遍历测试集

img = img.to(device) # 移动数据到设备

label = label.to(device) # 移动标签到设备

out = model(img) # 模型预测

_, pred = out.max(1) # 获取预测结果

final_test_acc += (pred == label).sum().item() # 累加正确预测数

total_samples += label.size(0) # 累加总样本数

final_accuracy = 100 * final_test_acc / total_samples # 计算最终准确率百分比

print(f'\n最终测试准确率: {final_accuracy:.2f}%') # 打印最终准确率

==================== 显示一些预测结果 ====================

model.eval() # 设置为评估模式

with torch.no_grad():

获取一批测试数据

dataiter = iter(test_loader) # 创建测试数据迭代器

images, labels = next(dataiter) # 获取下一个批次

images, labels = images.to(device), labels.to(device) # 移动数据到设备

进行预测

outputs = model(images) # 模型预测

_, predicted = torch.max(outputs, 1) # 获取预测类别

显示前12个预测结果

fig, axes = plt.subplots(3, 4, figsize=(12, 9)) # 创建3行4列的子图

axes = axes.ravel() # 将子图数组展平为一维

for i in range(12): # 遍历前12个样本

axes[i].imshow(images[i].cpu().numpy()[0], cmap='gray') # 显示图像

axes[i].set_title(f'预测: {predicted[i].item()}, 真实: {labels[i].item()}') # 设置标题

axes[i].axis('off') # 关闭坐标轴

如果预测错误,用红色标题突出显示

if predicted[i] != labels[i]:

axes[i].title.set_color('red') # 预测错误时标题变为红色

plt.tight_layout() # 自动调整布局

plt.savefig('prediction_results.png') # 保存预测结果图

print("预测结果图已保存到: prediction_results.png")

使用if name == 'main'保护主程序入口

if name == 'main':

设置matplotlib后端为Agg,避免显示问题(不显示图形窗口,只保存到文件)

import matplotlib

matplotlib.use('Agg')

main() # 调用主函数开始训练

相关推荐
数在表哥3 小时前
从数据沼泽到智能决策:数据驱动与AI融合的中台建设方法论与技术实践指南(一)
大数据·人工智能
岁月宁静3 小时前
前端添加防删除水印技术实现:从需求拆解到功能封装
前端·vue.js·人工智能
Baihai_IDP3 小时前
驳“AI 泡沫论”:一场被误读的、正在进行中的产业结构性调整
人工智能·llm·aigc
学Linux的语莫3 小时前
机器学习-神经网络-深度学习
人工智能·神经网络·机器学习
Mintopia3 小时前
🧠 对抗性训练如何增强 WebAI 模型的鲁棒性?
前端·javascript·人工智能
CoovallyAIHub3 小时前
YOLO Vision 2025 还没结束!亚洲首场登陆深圳,YOLO26有望亮相
深度学习·算法·计算机视觉
Newfocus!4 小时前
宝宝树以“奇迹2.0”重构营销范式:AI驱动母婴行业迈向全域智能
人工智能
AI人工智能+4 小时前
结婚证识别技术:利用OCR和深度学习实现婚姻证件信息的自动提取与结构化处理
深度学习·ocr·结婚证识别
一车小面包4 小时前
Transformers中从 logits 本质到问答系统中的字符定位机制
pytorch·python·深度学习