刚学了一点CNN,就来练练手
前言
MNIST(Modified National Institute of Standards and Technology database)是一个常用的手写数字识别数据集,常被用作机器学习和深度学习的基准数据集。它的历史可以追溯到20世纪80年代末和90年代初。当时,美国国家标准与技术研究所(NIST)收集了大量手写数字图像,并将其用于开发和评估基于光学字符识别(OCR)的技术和算法。
MNIST数据集包含了大量的手写数字图片,每张图片都是28x28像素的灰度图像。数据集被划分为训练集(training set)和测试集(test set),其中训练集包含60,000个样本,测试集包含10,000个样本。这些样本都是由来自美国国家标准与技术研究所(NIST)的员工和美国人口普查局的高中生手写而成。
总结一下,MNIST是一个包含手写数字图像的数据集,常用于机器学习和深度学习中的手写数字识别任务。它是一个相对简单的数据集,被广泛用于算法验证和学术研究。而这个Kaggle的比赛的就是基于MNIST的一个入门项目。
数据处理
虽然pytorch里面已经提供了MNIST数据集,但是还是需要下载一下。这里用的是Kaggle的比赛数据集。我们拿pytorch的MNIST数据集来测试模型的准确率。
Kaggle的比赛数据是一个CSV文件,该文件有28x28 + 1列,除第一列外,每一列代表一个像素点的灰度值。第一列则代表图片的结果。
我们可以简单看一下这些图片。
import pandas as pd
import matplotlib.pyplot as plt
# 读取CSV文件
data = pd.read_csv('train.csv')
# 获取图像数据和标签
image_data = data.iloc[:, 1:].values
labels = data.iloc[:, 0].values
# 将图像数据转换为图像格式
images = image_data.reshape(-1, 28, 28)
# 显示图像
fig, axes = plt.subplots(5, 5, figsize=(8, 8))
for i, ax in enumerate(axes.flat):
ax.imshow(images[i], cmap='gray')
ax.axis('off')
ax.set_title(f"Label: {labels[i]}")
plt.tight_layout()
plt.show()
如图所示:
大概就是这样的,没有倒置,没有左右旋转的毒瘤操作。
模型设计瞎搞(不是
虽然torchvision里面已经提供了一些常用的模型,像ResNet,VGG,AlexNet等,但是考虑到第一次写,确定手动定义一个,考虑到我的训练时间问题,最终选择了AlexNet。电脑太拉了
AlexNet的大体结构图如下,但是由于我们只需要处理28x28的数据,最后类别数为10,所以我们简单胡乱改一下
# myNet.py
import torch.nn as nn
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 定义神经网络的层结构
self.features = nn.Sequential(
nn.Conv2d(1, 96, 11, 1, 5), nn.ReLU(inplace=True), # 第一个卷积层
nn.MaxPool2d(kernel_size=3, stride=2), # 第一个最大池化层
nn.Conv2d(96, 256, 5, 1, 2), nn.ReLU(inplace=True), # 第二个卷积层
nn.MaxPool2d(kernel_size=3, stride=2), # 第二个最大池化层
nn.Conv2d(256, 384, 3, 1, 1), nn.ReLU(inplace=True), # 第三个卷积层
nn.Conv2d(384, 384, 3, 1, 1), nn.ReLU(inplace=True), # 第四个卷积层
nn.Conv2d(384, 256, 3, 1, 1), nn.ReLU(inplace=True), # 第五个卷积层
nn.MaxPool2d(kernel_size=3, stride=2), # 第三个最大池化层
nn.Flatten(), # 将多维输入展平为一维
nn.Linear(1024, 256), nn.ReLU(inplace=True), # 第一个全连接层
nn.Dropout(p=0.5), # Dropout层,随机失活一部分神经元
nn.Linear(256, 84), nn.ReLU(inplace=True), # 第二个全连接层
nn.Dropout(p=0.5), # Dropout层,随机失活一部分神经元
nn.Linear(84, 10) # 第三个全连接层
)
def forward(self, x):
x = self.features(x)
return x
大概就是这样了,没啥经验,层的超参数我乱设的,比原本的就小了一点点~~
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import *
from torchvision.datasets import MNIST
from torchvision.transforms import ToTensor
import pandas as pd
from myNet import *
# 加载训练数据
train_data = pd.DataFrame(pd.read_csv('train.csv'))
image_train = torch.tensor(
train_data.iloc[:, 1:].values, dtype=torch.float32).reshape(-1, 1, 28, 28)
outputs_train = torch.tensor(train_data.iloc[:, 0].values, dtype=torch.long)
train_dataset = TensorDataset(image_train, outputs_train)
# 加载测试数据,这里使用自带的
test_data = MNIST(root='data/', train=False,
download=True, transform=ToTensor())
# 小批量数据加载
data_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_data, batch_size=32, shuffle=False)
# 定义模型,在myNet.py中
net = Net()
# 定义损失函数和优化器
loss = nn.CrossEntropyLoss()
opt = optim.Adam(net.parameters(), lr=0.001)
# 我是Mac设备,加速用的mps
device = torch.device('mps')
net.to(device)
cnt = 20
for epoch in range(cnt):
# 模型训练
net.train()
train_loss = 0.0
train_acc = 0.0
for image, number in data_loader:
image = image.to(device)
number = number.to(device)
# 前向传播
outputs = net(image)
l = loss(outputs, number)
# 反向传播
opt.zero_grad()
l.backward()
opt.step()
train_loss += l.item()
_, train_ans = torch.max(outputs.data, 1)
train_acc += (train_ans == number).sum().item()
train_loss /= (len(data_loader))
train_acc = 100 * train_acc / (len(train_dataset))
# 模型测试
net.eval()
test_acc = 0.0
test_loss = 0.0
with torch.no_grad():
for image, number in test_loader:
image = image.to(device)
number = number.to(device)
outputs = net(image)
l = loss(outputs, number)
test_loss += l.item()
_, test_ans = torch.max(outputs.data, 1)
test_acc += (test_ans == number).sum().item()
test_loss /= len(test_loader)
test_acc = 100 * test_acc / len(test_data)
print(f'Epoch {epoch+1}/{epoch}:')
print(f'训练 Loss: {train_loss:.4f}, 训练精准度: {train_acc:.2f}%')
print(f'测试 Loss: {test_loss:.4f}, 测试精准度: {test_acc:.2f}%')
torch.save(net.state_dict(), 'model_final.pth')
Epoch 1:
训练 Loss: 0.5177, 训练精准度: 84.43%
测试 Loss: 2.2420, 测试精准度: 30.26%
Epoch 2:
训练 Loss: 0.1744, 训练精准度: 96.22%
测试 Loss: 2.2501, 测试精准度: 36.55%
Epoch 3:
训练 Loss: 0.1589, 训练精准度: 96.75%
测试 Loss: 2.2226, 测试精准度: 31.70%
Epoch 4:
训练 Loss: 0.1490, 训练精准度: 96.91%
测试 Loss: 2.2276, 测试精准度: 20.78%
Epoch 5:
训练 Loss: 0.1385, 训练精准度: 97.00%
测试 Loss: 2.1943, 测试精准度: 32.70%
......
不知道为什么,测试数据精确度比较低,可能是比赛的数据强度比较低,是在不行可以使用自带的训练,我试过用自带的训练得分高0.01(
还有,不要重复太多次,我有一次循环了70多次结果梯度消失了(
生成答案
import torch
from torch.utils.data import *
from myNet import *
import pandas as pd
data = pd.DataFrame(pd.read_csv('test.csv'))
image_train = torch.tensor(
data.iloc[:, :].values, dtype=torch.float32).reshape(-1, 1, 28, 28)
dataset = TensorDataset(image_train)
data_loader = DataLoader(dataset, batch_size=32, shuffle=False)
# 定义模型并加载
net = Net()
net.load_state_dict(torch.load('model_final.pth'))
device = torch.device('mps')
net.to(device)
number = []
label = []
net.eval()
with torch.no_grad():
for image in data_loader:
image = image[0].to(device)
outputs = net(image)
_, ans = torch.max(outputs.data, 1)
for i in ans:
number.append(cnt)
# 要把数据从GPU移动到CPU
label.append(i.data.cpu().numpy())
cnt += 1
answer = pd.DataFrame({'ImageId': number, 'Label': label})
answer.to_csv("answer.csv", index=False, sep=',')
还能怎么玩——用tkinter来做GUI手写识别
得意于我薄弱的GUI编程知识,我用AI生成了GUI绘图的代码并魔改(
首先程序分为前端和后端,前端使用tkinter来绘图,并将图片数据传给pytorch来处理,pytorch加载之前训练好的模型参数。可能是书写习惯的缘故?我写的数字6好像识别的不太好诶(也可能是我写的太丑了)
项目放github上了:DigitRecognizer
Comments NOTHING