#AI挑战营第一站#基于Pytorch的卷积神经网络识别MNIST数据集-小白从零开始篇
[复制链接]
本帖最后由 爱吃鱼的加菲猫 于 2024-4-21 16:30 编辑
作为一个AI领域纯纯的小白,想要用Pytorch基于MNIST数据集训练个卷积网络模型,本帖子也作为我的学习log吧,高手们轻点拍。
CPU:Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz 3.20 GHz 四核四线程
GPU:NVIDIA GeForce GTX 750 Ti(未装CUDA)
RAM:8G
Windows 10 专业版
Python 3.12.1
cmd命令行内输入nvidia-smi.exe,查看本机显卡驱动版本及可用的CUDA版本。
这里要注意,Pytorch对CUDA版本有要求,不能乱装,所以得到官网看看匹配的版本。
https://pytorch.org/get-started/locally/
这里可以看到,最新稳定版本的PyTorch能支持到CUDA12.1版本。
把我参考的安装教程链接贴上来,具体大家可以照着操作。
CUDA官网下载链接:https://developer.nvidia.com/cuda-toolkit-archive
根据自己系统进行选择,下载安装包。
下载完参照教程双击安装即可,接下来确认下环境变量。
cmd命令行里输入nvcc -version命令查看软件版本。
参考安装教程:https://blog.csdn.net/MCYZSF/article/details/116525159
anaconda是一个集成的工具软件,管理它安装Pytorch会很方便https://www.anaconda.com/
再来个GPU版本使用清华源的安装教程。https://cloud.tencent.com/developer/article/2405758
教程二https://www.zhihu.com/tardis/zm/art/681417713?source_id=1005
开始菜单栏找到Anaconda Prompt,我前面创建了虚拟环境名字叫pytorch,输入python,输入activate pytorch切换环境,输入import torch后回车,再输入torch.cuda.is_available()回车,
如果返回True,则证明环境已经配置好。
接下来就可以正式开始模型训练工作了,这里我参考的实现是下面的教程,非常详细。
https://blog.csdn.net/qq_45588019/article/details/120935828
教程二:https://zhuanlan.zhihu.com/p/137571225
- 导入相关包文件
import torch
import torchvision
from torch.utils.data import DataLoader
- 引入MNIST数据集
定义超参数
n_epochs = 3
batch_size_train = 64
batch_size_test = 1000
learning_rate = 0.01
momentum = 0.5
log_interval = 10
random_seed = 1
torch.manual_seed(random_seed)
下载获取数据集(训练集及测试集),并用Dataloader 包起来,即载入数据集
train_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size_train, shuffle=True)
test_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size_test, shuffle=True)
运行上面的程序后,会自动将数据集下载到目录下的data文件夹。
- 展示MNIST数据集(如果要直接训练,这步可以忽略)
首先看看测试数据有啥。
examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
print(example_targets)
print(example_data.shape)
安装相关包文件,在Anaconda Prompt下执行以下命令:
conda install matplotlib
举例展示6幅图,包含图片内容和标签。
fig = plt.figure()
for i in range(12):
plt.subplot(3, 4, i+1)
plt.tight_layout()
plt.imshow(train_dataset.train_data[i], cmap='gray', interpolation='none')
plt.title("Labels: {}".format(train_dataset.train_labels[i]))
plt.xticks([])
plt.yticks([])
plt.show()
- 构建模型
此处使用两个2d卷积层,然后是两个全连接(或线性)层。作为激活函数,我们将选择整流线性单元(简称ReLUs),作为正则化的手段,我们将使用两个dropout层。
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x)
- 实例化模型
network = Net()
- 初始化网络及优化器
criterion = torch.nn.CrossEntropyLoss() # 交叉熵损失
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum) # lr学习率,momentum冲量
- 定义训练及测试轮
训练轮代码:
def train(epoch):
running_loss = 0.0 # 这整个epoch的loss清零
running_total = 0
running_correct = 0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
# forward + backward + update
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
# 把运行中的loss累加起来,为了下面300次一除
running_loss += loss.item()
# 把运行中的准确率acc算出来
_, predicted = torch.max(outputs.data, dim=1)
running_total += inputs.shape[0]
running_correct += (predicted == target).sum().item()
if batch_idx % 300 == 299: # 不想要每一次都出loss,浪费时间,选择每300次出一个平均损失,和准确率
print('[%d, %5d]: loss: %.3f , acc: %.2f %%'
% (epoch + 1, batch_idx + 1, running_loss / 300, 100 * running_correct / running_total))
running_loss = 0.0 # 这小批300的loss清零
running_total = 0
running_correct = 0 # 这小批300的acc清零
完整代码:
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
from torchsummary import summary
import time
# 创建神经网络
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
)
self.layer2 = nn.Sequential(
nn.Conv2d(16, 32, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)
)
self.output_layer = nn.Linear(32*7*7, 10)
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = x.reshape(x.size(0), -1)
output = self.output_layer(x)
return output
# 超参数
EPOCH = 2
BATCH_SIZE = 100
LR = 0.001
DOWNLOAD = False # 若已经下载mnist数据集则设为False
# 下载mnist数据
train_data = datasets.MNIST(
root='./data', # 保存路径
train=True, # True表示训练集,False表示测试集
transform=transforms.ToTensor(), # 将0~255压缩为0~1
download=DOWNLOAD
)
# 旧的写法
print(train_data.train_data.size())
print(train_data.train_labels.size())
# 新的写法
print(train_data.data.size())
print(train_data.targets.size())
# 打印部分数据集的图片
for i in range(2):
print(train_data.targets[i].item())
plt.imshow(train_data.data[i].numpy(), cmap='gray')
plt.show()
# DataLoader
train_loader = Data.DataLoader(
dataset=train_data,
batch_size=BATCH_SIZE,
shuffle=True,
num_workers=2
)
# 如果train_data下载好后,test_data也就下载好了
test_data = datasets.MNIST(
root='./data',
train=False
)
print(test_data.data.size())
print(test_data.targets.size())
# 新建网络
cnn = CNN()
# 将神经网络移到GPU上
cnn.cuda()
print(cnn)
# 查看网络的结构
model = CNN()
if torch.cuda.is_available():
model.cuda()
summary(model, input_size=(1,28,28))
# 优化器
optimizer = torch.optim.Adam(cnn.parameters(), lr=LR)
# 损失函数
loss_func = nn.CrossEntropyLoss()
# 为了节约时间,只使用测试集的前2000个数据
test_x = Variable(
torch.unsqueeze(test_data.data, dim=1),
volatile=True
).type(torch.FloatTensor)[:2000]/255 # 将将0~255压缩为0~1
test_y = test_data.targets[:2000]
# # 使用所有的测试集
# test_x = Variable(
# torch.unsqueeze(test_data.test_data, dim=1),
# volatile=True
# ).type(torch.FloatTensor)/255 # 将将0~255压缩为0~1
# test_y = test_data.test_labels
# 将测试数据移到GPU上
test_x = test_x.cuda()
test_y = test_y.cuda()
# 开始计时
start = time.time()
# 训练神经网络
for epoch in range(EPOCH):
for step, (batch_x, batch_y) in enumerate(train_loader):
# 将训练数据移到GPU上
batch_x = batch_x.cuda()
batch_y = batch_y.cuda()
output = cnn(batch_x)
loss = loss_func(output, batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 每隔50步输出一次信息
if step%50 == 0:
test_output = cnn(test_x)
# 将预测结果移到GPU上
predict_y = torch.max(test_output, 1)[1].cuda().data.squeeze()
accuracy = (predict_y == test_y).sum().item() / test_y.size(0)
print('Epoch', epoch, '|', 'Step', step, '|', 'Loss', loss.data.item(), '|', 'Test Accuracy', accuracy)
# 结束计时
end = time.time()
# 训练耗时
print('Time cost:', end - start, 's')
# 预测
test_output = cnn(test_x[:100])
# 为了将CUDA tensor转化为numpy,需要将数据移回CPU上
# 否则会报错:TypeError: can't convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
predict_y = torch.max(test_output, 1)[1].cpu().data.numpy().squeeze()
real_y = test_y[:100].cpu().numpy()
print(predict_y)
print(real_y)
# 打印预测和实际结果
for i in range(10):
print('Predict', predict_y[i])
print('Real', real_y[i])
plt.imshow(test_data.data[i].numpy(), cmap='gray')
plt.show()
训练结果如下:
用时100s左右,精度可以到0.97了,足够手写识别。
# 保存模型的状态字典
torch.save(model.state_dict(), 'mnist.pth')
model.load_state_dict(torch.load('mnist.pth'))
# 将模型设置为评估模式(如果需要)
model.eval()
#导出为onnx模型
input = torch.randn(1, 28, 28)
torch.onnx.export(model, input, "mnist.onnx", verbose=True)
|