>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客**
>- **🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/)**
我的环境:
python 3.9
使用jupyter
torch==1.13.0+cu116
torchvision==0.14.0+cu116
运行代码:
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision
from torchinfo import summary
from torch.utils.data import DataLoader
import numpy as np
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
import warnings
#导入数据
#分为训练数据集和测试数据集
#读取
train_ds = torchvision.datasets.MNIST('data',train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
test_ds = torchvision.datasets.MNIST('data',train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
batchsize = 32
train_dl = DataLoader(train_ds,batchsize,shuffle=True,num_workers=0)
test_dl = DataLoader(test_ds,batchsize,num_workers=0)
imgs ,labels = next(iter(train_dl)) # iter迭代器,相当于for imgs,labels in train_dl:,next从迭代器取下一个
# for imgs,labels in train_dl:
print(imgs.shape)
# 数据可视化
plt.figure(figsize=(20,5)) # 创建绘图窗口为20宽5长
for i,imgs in enumerate(imgs[:20]): #遍历前20个图像
npimg = np.squeeze(imgs.numpy()) # squeeze移除单维度,使维度对应图像尺寸
plt.subplot(2,10,i+1)# 2行10列,i+指定当前子图位置,matplotlib从1开始
plt.imshow(npimg,cmap = plt.cm.binary)
plt.axis('off') # 关闭坐标轴,不显示坐标轴刻度和标签
plt.show()
#设置硬件设备,有GPU使用GPU,没有则使用CPU
device = torch.device("cuda"if torch.cuda.is_available()else"cpu")
print(device)
import torch.nn.functional as F
num_classes = 10 # 图片的类别数
class Model(nn.Module):
def __init__(self):
super().__init__()
# 特征提取网络
self.conv1 = nn.Conv2d(1, 32, kernel_size=3) # 第一层卷积,卷积核大小为3*3
self.pool1 = nn.MaxPool2d(2) # 设置池化层,池化核大小为2*2
self.conv2 = nn.Conv2d(32, 64, kernel_size=3) # 第二层卷积,卷积核大小为3*3
self.pool2 = nn.MaxPool2d(2)
# 分类网络
self.fc1 = nn.Linear(1600, 64)
self.fc2 = nn.Linear(64, num_classes)
# 前向传播
def forward(self, x):
x = self.pool1(F.relu(self.conv1(x)))
x = self.pool2(F.relu(self.conv2(x)))
x = torch.flatten(x, start_dim=1)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
#打印加载模型
model = Model().to(device) # 到GPU中运行
summary(model)
# 训练模型
# 设置超参数
loss_fn = nn.CrossEntropyLoss()
learn_rate = 1e-2 # 学习率 = 0.01
opt = torch.optim.SGD(model.parameters(),lr = learn_rate)
# 训练循环
def train(dataloader,model,loss_fn,optimizer):
size = len(dataloader.dataset) # 训练集大小,60000
num_batches = len(dataloader) # 批次数目,60000/32 = 1875
train_loss, train_acc = 0,0 # 初始化训练误差和精度
for data in dataloader:
X,y = data # 图片和标签
X,y = X.to(device),y.to(device)
#计算训练误差
pred = model(X) # 网络输出,预测值
loss = loss_fn(pred,y) # 计算差距
# 反向传播
optimizer.zero_grad() # grad属性归零
loss.backward() # 反向传播
optimizer.step() # 自动更新
# 记录acc和loss
# argmax(1) 函数会找出每一行(每个样本)中最大值的索引,这个索引代表了模型预测的类别。
# .item()将求和结果转换为标量值,以便在 Python 中使用或打印。
train_acc += (pred.argmax(1) == y).type(torch.float).sum().item() # 样本预测正确的数量
train_loss += loss.item()
train_acc /=size
train_loss /= num_batches # 平均loss
return train_acc,train_loss
#测试函数
def test(dataloader,model,loss_fn):
size = len(dataloader.dataset) # 训练集大小,60000
num_batches = len(dataloader) # 批次数目,60000/32 = 1875
test_loss, test_acc = 0,0 # 初始化测试误差和精度
with torch.no_grad(): # 阻止追踪梯度
for data in dataloader:
X, y = data # 图片和标签
X, y = X.to(device), y.to(device)
# 计算测试误差
pred = model(X) # 网络输出,预测值
loss = loss_fn(pred, y) # 计算差距
# 记录acc和loss
# argmax(1) 函数会找出每一行(每个样本)中最大值的索引,这个索引代表了模型预测的类别。
# .item()将求和结果转换为标量值,以便在 Python 中使用或打印。
test_acc += (pred.argmax(1) == y).type(torch.float).sum().item() # 样本预测正确的数量
test_loss += loss.item()
test_acc /= size
test_loss /= num_batches # 平均loss
return test_acc, test_loss
# 正式训练
epochs = 5
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):
model.train()
epoch_train_acc,epoch_train_loss = train(train_dl,model,loss_fn,optimizer=opt)
model.eval()
epoch_test_acc,epoch_test_loss = test(test_dl,model,loss_fn)
train_acc.append(epoch_train_acc)
train_loss.append(epoch_train_loss)
test_acc.append(epoch_test_acc)
test_loss.append(epoch_test_loss)
template = ('Epoch:{:2d},Train_acc:{:.1f}%,Train_loss:{:.3f},Test_acc:{:.1f}%,Test_loss:{:.3f}')
print(template.format(epoch+1,epoch_train_acc*100,epoch_train_loss,epoch_test_acc*100,epoch_test_loss))
print('Done')
# 结果可视化
#隐藏警告
warnings.filterwarnings("ignore")
# plt.reParams['font.sana-serif'] = ['SimHei'] # 正常显示中文标签
# plt.reParams['axe.unicode_minus'] = False # 正常显示负号
# plt.reParams['figure.dpi'] = 100 # 分辨率
epoche_range = range(epochs)
plt.figure(figsize=(12,3))
plt.subplot(1,2,1) # 行数,列数,第几个
plt.plot(epoche_range,train_acc,label = 'Training Accuracy') #横坐标,竖坐标,标题
plt.plot(epoche_range,test_acc,label = 'Testing Accuracy')
plt.legend(loc = 'lower right') # 图例位置
plt.title('Training and Validation Accuracy') #大标题
plt.subplot(1,2,2)
plt.plot(epoche_range,train_loss,label = 'Training Loss')
plt.plot(epoche_range,test_loss,label = 'Testing Loss')
plt.legend(loc = 'upper right')
plt.title('Training and Validation Loss')
plt.show()
学习总结:
MNIST数据集包括了70000张手写数字的灰度图像,其中60000张用于训练,10000张用于测试。每张图像是28x28像素的大小,图像中的数字介于0到9之间。学习数据预处理和一个完整的训练和测试代码结构,模型在测试集上达到了约97%的准确率。