pytorch 多数字识别加定位(MNIST数据集)

学习过深度学习中的卷积神经网络的同志应该都接触过MNIST数据集,并且做过手写数字识别吧,还记得第一次做成功手写数字识别的那种成就感哈哈哈。

本深度学习小白最近尝试了对MNIST数据集进行了修改,将多个数字合成在一张图片中,用pyplot来显示,然后对其进行识别和定位。大概是这样的:

 比较简陋啊,但是大体上能满足要求就可以了。

具体过程

一  、导入所需要的库

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader,TensorDataset

二 、定义超参数

# 定义超参数
BATCH_SIZE = 16  # 每批次处理的数据
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 是否用GPU或者CPU
EPOCHS = 10  # 训练的数据集的轮次

三 、下载并加载数据

"""构建pipeline,对图像处理"""
pipeline = transforms.Compose([
    transforms.ToTensor(),  # 将图片转换成tensor
    transforms.Normalize((0.1307,), (0.3081,))  # 正则化 第一个参数是均值,第二个参数是标准差 这些系数都是数据集提供方计算好的数据
])

"""下载数据集 """

train_set = datasets.MNIST("data", train=True, download=True, transform=pipeline)

test_set = datasets.MNIST("data", train=False, download=True, transform=pipeline)


"""加载数据"""
train_loader = DataLoader(train_set, shuffle=True)

test_loader = DataLoader(test_set, shuffle=True)

四、制作训练集和测试集数据

""" 制作训练集数据和标签"""
number_x_train = np.zeros((30000, 1, 100, 100), dtype="float64")
coordinate_y_train = np.zeros((30000, 1, 4), dtype="float64")
number_y_train = np.zeros((30000, 1, 20), dtype="float64")

j = 0
for i, (data, label) in enumerate(train_loader):
    data = data.squeeze(axis=0)
    data = data.squeeze(axis=0)
    data = np.array(data)
    data[0, :], data[27, :], data[:, 0], data[:, 27] = 1, 1, 1, 1
    if i % 2 == 0:
        """背景板"""
        blank = np.zeros((100, 100))
        """临时存放标签和坐标 """
        multi_label = np.zeros((1, 20))
        multi_coordinate = np.array([[0, 0, 0, 0]])
        """只能生成在左半边的坐标"""
        dx = np.random.randint(0, 70)
        dy = np.random.randint(0, 20)
        multi_coordinate[0][0] = dx
        multi_coordinate[0][1] = dy
        """采用类似独热编码的方式存储 对应序号的数字为1 其余为0"""
        multi_label[0,int(label.numpy().item())] = 1
    else:
        """只能生成在右半边的坐标"""
        dx = np.random.randint(0, 70)
        dy = np.random.randint(50, 70)
        multi_coordinate[0][2] = dx
        multi_coordinate[0][3] = dy
        multi_label[0,int(label.numpy().item())+10] = 1
    """将数字放在背景板上"""
    blank[dx:dx + 28, dy:dy + 28] = blank[dx:dx + 28, dy:dy + 28] + data
    if i % 2 == 1:
        number_x_train[j, :, :, :] = blank
        number_y_train[j, :] = multi_label
        coordinate_y_train[j, :] = multi_coordinate
        plt.imshow(number_x_train[j].squeeze(0),cmap='binary')
        plt.show()
        j += 1

number_X_train = torch.from_numpy(number_x_train).float()
coordinate_Y_train = torch.from_numpy(coordinate_y_train).float()
number_Y_train = torch.from_numpy(number_y_train).float()


train_data = TensorDataset(number_X_train, number_Y_train)
train_loader = DataLoader(train_data, batch_size=BATCH_SIZE, shuffle=True)

就是每次都生成一个100*100 的背景板,对原MNIST数据集中的所有训练集和测试集中的所有数据,进行两两配对,并且随机生成坐标,将其显示在背景板上就可以了。我这里为了防止两个数字重叠,生成坐标的时候将其中一个固定在左半边,另一个固定在右半边。最后将这些数据保存下来就可以了。这里是训练集的代码,测试集几乎差不多也就不重复贴代码了。

五、识别数字模型

"""识别数字模型"""
class Multi_Digit(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=3,padding=1,stride=2)  # 1: 灰度图片的通道 10: 输出通道 3:kernel大小3*3
        self.conv2 = nn.Conv2d(10, 20, 3)  # 10: 输入通道 20: 输出通道 3:kernel大小3*3
        self.conv3 = nn.Conv2d(20, 40, 3)  # 10: 输入通道 20: 输出通道 3:kernel大小3*3
        self.fc1 = nn.Linear(3240, 500)  # 3240: 输入通道 500: 输出通道
        self.fc2 = nn.Linear(500, 20)  # 500:输入通道 20:输出通道

    def forward(self, x):  #  x 为  batch size *1 *100 *100   
        input_size = x.size(0)  # batch size
        x = self.conv1(x)  # 卷积   输入:batch *1 *100 *100 ,输出 : batch*10*50*50
        x = F.relu(x)  # 激活函数 输出 : batch*10*46*46
        x = F.max_pool2d(x, 2, 2)  # 池化层 f=2 s=2 输入 :batch*10*50*50 输出 :batch*10*25*25

        x = self.conv2(x)  # 卷积   输入:batch *10 *25 *25 ,输出 : batch*20*23*23
        x = F.relu(x)
        x =F.max_pool2d(x,2,2)  # 卷积   输入:batch *20 *23 *23 ,输出 : batch*20*11*11

        x =self.conv3(x) # 卷积   输入:batch *20 *11 *11 ,输出 : batch*40*9*9
        x = F.relu(x)
        x = x.view(input_size, -1) 
        x = self.fc1(x)  # 输入 :batch * 3240 输出:batch * 500
        x = F.relu(x)  
        x = self.fc2(x)  # 输入 batch *500   输出 :batch *20
        return x

这个是识别数字的模型,因为图片也不是很复杂,所以这个模型也很简单。就卷积,激活,池化叠加起来就行了,最后全连接输出。

六、定义训练方法和测试方法

model = Multi_Digit().to(DEVICE)  # 创建模型部署到设备上 (CPU或者GPU)

optimizer = optim.Adam(model.parameters())  # 定义一个优化器 ,将模型的参数作为输入,优化参数

def train_model(model, device, train_loader, optimizer, epoch):  # train_loader为训练的数据
    # 数字识别模型训练
    model.train()  #启用batch normalization(批标准化)和drop out,model.train()是保证BN层能够用到每一批数据的均值和方差。对于Dropout,model.train()是随机取一部分网络连接来训练更新参数。
    for batch_index, (data, label) in enumerate(train_loader):  # data 为图片 label 为标签
        # 部署到DEVICE上去
        data, label = data.to(device), label.to(device)
        label = label.squeeze(axis=1)
        # 梯度初始化为 0
        optimizer.zero_grad() # 即将 optimizer中的weight置零
        # 训练后的结果
        output = model(data)
        # 计算损失
        MSE = nn.MSELoss()
        loss = MSE(output, label)
        # 反向传播
        loss.backward()
        # 参数优化
        optimizer.step()
        if batch_index % 10000 == 0:
            print("Train Epoch :{} \t Loss :{:.6f}".format(epoch, loss.item()))


# 定义数字识别测试方法
def test_model(model, device, test_loader):
    # 模型验证
    model.eval() 
    # 正确率
    correct = 0.0
    # 测试损失
    test_loss = 0.0
    n1,n2=0,0
    with torch.no_grad():  # 不会计算梯度,也不会反向传播
        for data, label in test_loader:
            # 部署到设备上
            data, label = data.to(device), label.to(device)
            # 测试数据
            output = model(data)
            # 计算测试损失
            label =label.squeeze(axis=1)
            MSE = nn.MSELoss()
            test_loss += MSE(output, label).item()
            # 累计正确率
            correct += np.round(output).eq(label.view_as(output)).sum().item()/20  
        test_loss /= len(test_loader.dataset)
        print("Test --- Average loss : {:.4f}  Accuracy:{:.4f}".format(test_loss,100.0 * correct / len(test_loader.dataset)))

#
for epoch in range(1, EPOCHS + 1):
    train_model(model, DEVICE, train_loader, optimizer, epoch)
    test_model(model, DEVICE, test_loader)

准确率还挺高的哈哈哈

七、定位模型

"""定位模型"""
class Multi_Number_Coordinate(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=3, padding=1, stride=2)  # 1: 灰度图片的通道 10: 输出通道 3:kernel大小3*3
        self.conv2 = nn.Conv2d(10, 20, 3)  # 10: 输入通道 20: 输出通道 3:kernel大小3*3
        self.conv3 = nn.Conv2d(20, 40, 3)  # 10: 输入通道 20: 输出通道 3:kernel大小3*3
        self.fc1 = nn.Linear(3240, 500)  # 8820: 输入通道 500: 输出通道
        self.fc2 = nn.Linear(500, 4)  # 500:输入通道 4:输出通道

    def forward(self, x):  # 前向传播  x 为  batch size *1 *100 *100  1是通道(灰色)
        input_size = x.size(0)  # batch size
        x = self.conv1(x)  # 卷积   输入:batch *1 *100 *100 ,输出 : batch*10*50*50
        x = F.relu(x)  # 激活函数 输出 : batch*10*46*46
        x = F.max_pool2d(x, 2, 2)  # 池化层 f=2 s=2 输入 :batch*10*50*50 输出 :batch*10*25*25

        x = self.conv2(x)  # 卷积   输入:batch *10 *25 *25 ,输出 : batch*20*23*23
        x = F.relu(x)
        x = F.max_pool2d(x, 2, 2)  # 卷积   输入:batch *20 *23 *23 ,输出 : batch*20*11*11

        x = self.conv3(x)  # 卷积   输入:batch *20 *11 *11 ,输出 : batch*40*9*9
        x = F.relu(x)
        x = x.view(input_size, -1)  # 转化成一维 , -1 激动计算维度

        x = self.fc1(x)  # 输入 :batch * 8820 输出:batch * 500
        x = F.relu(x)  # 保持shape不变
        x = self.fc2(x)  # 输入 batch *500   输出 :batch *10
        return x

定位模型几乎和识别数字模型差不多,一定有人问为什么还分开来两个模型,直接用一个不就好了嘛。的确可以这样,不过没试过这样准确率高不高。大家可以把修改的数据集将坐标和数字标签放在一起,然后只训练一个模型然后把最后全连接层改成24通道的就可以了。定位模型的训练方法和测试方法也和识别数字的差不多,这里也不重复贴代码了。

八、模型效果

然后就可以随便取出一组数据来输出看看效果啦。这准确率还算可以吧。

九、总结

虽然大概做出来了,但是要改进的应该也有很多,首先就是可以缩成一个模型。还有一些代码很笨重的地方。当然如果有其他修改意见欢迎提出来。之前一直纠结该用什么损失函数,那个时候不是用类似独热编码的方式保存标签的,只是单纯把数字记录下来,但这样似乎没有一个损失函数可以拿来用,后来才想到可以用这种方式,经验还是太少,以后接触的多了,应该会有更多想法吧。

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
可以使用以下代码基于MNIST数据集PyTorch实现手写数字识别: ```python import torch import torch.nn as nn import torchvision import torchvision.transforms as transforms # 定义超参数 input_size = 784 # 28x28 hidden_size = 100 num_classes = 10 num_epochs = 5 batch_size = 100 learning_rate = 0.001 # 数据集,并进行标准化处理 train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transforms.ToTensor(), download=True) test_dataset = torchvision.datasets.MNIST(root='./data', train=False, transform=transforms.ToTensor()) train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True) test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False) # 定义神经网络模型 class NeuralNet(nn.Module): def __init__(self, input_size, hidden_size, num_classes): super(NeuralNet, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, num_classes) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out model = NeuralNet(input_size, hidden_size, num_classes) # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) # 训练模型 total_step = len(train_loader) for epoch in range(num_epochs): for i, (images, labels) in enumerate(train_loader): images = images.reshape(-1, 28*28) # 前向传播 outputs = model(images) loss = criterion(outputs, labels) # 反向传播和优化 optimizer.zero_grad() loss.backward() optimizer.step() if (i+1) % 100 == 0: print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}' .format(epoch+1, num_epochs, i+1, total_step, loss.item())) # 测试模型 with torch.no_grad(): correct = 0 total = 0 for images, labels in test_loader: images = images.reshape(-1, 28*28) outputs = model(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Test Accuracy of the model on the 10000 test images: {} %'.format(100 * correct / total)) ``` 运行该代码后,将输出模型的训练过程和在测试集上的准确率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值