都用MINIST?我非要识别自己手写的!

这篇博客介绍了如何使用PyTorch识别手绘的28x28像素手写数字。作者首先通过画图工具创作数字并转换为灰度图,然后利用预训练的LeNet5模型进行识别。在处理过程中,作者发现了输入图像的像素值与MNIST数据集不匹配的问题,并尝试了多种图像转换方法,包括调整像素值范围、归一化处理等。最终,通过开启网络的测试模式,成功实现了对手绘数字的识别。
摘要由CSDN通过智能技术生成

Pytorch识别自己用画图工具写的手写数字

目录

Pytorch识别自己用画图工具写的手写数字

整体思路

第一步

第二步

第三步

网络输入:

网络输出:   

“激活” 函数

又发现问题

最后的最后,我发现,加上这句代码,他就可以了!!!

最后奉上稍微useful一点的一套代码:


整体思路

  1. 画图保存png,用cv2转换成28*28的bmp文件(灰度图)
  2. 用minist数据集训练Lenet5,并保存网络
  3. 预测

第一步

def rc():
    image = cv2.imread("3__.png")
    image2 = cv2.resize(image, (28, 28))
    gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
    cv2.imwrite("gray.bmp", gray)
    print(gray)   # 28*28 矩阵

第二步

训练网络的代码很常见,最后保存网络:

torch.save(net, 'Lenet5_net.pkl')

第三步

# 导入网络
net = torch.load('Lenet5_net.pkl')
# 读灰度图 若没有指定flag, 则有3通道 [28, 28, 3]
img = cv2.imread('gray.bmp', flags=cv2.IMREAD_GRAYSCALE)
imgs = [[img]] * 64   # !!!!! [img] 双重[] 否则维度为 (64,28,28)
# torch.Tensor() 不同于 torch.tensor()   float
imgs = torch.Tensor(imgs)  
# print(imgs.shape) # torch.Size([64, 1, 28, 28])
imgs = Variable(imgs).cuda()
out = net(imgs)
# print(out) # 一堆小数
predicted = torch.max(out, 1)
# print("predicted = ", predicted)  # 不是 8 就是 2

前面的是那张 gray.bmp,为什么预测出 8和2 应该很清楚了吧;所以要想预测准,得转换得好   PS:变量out全是负数;同样这个net预测minist准确率98%

后来发现主要是cv2.resize搞得像素点很散,于是想到直接用画图工具画28*28

得到的bmp简直完美!!! 看起来几乎一模一样

这回总该行了吧

至少能看出3来了,但还是不咋滴,有一个问题:

这里3、8各一半,本应该全都一样的,毕竟我用了64张全一样的这张bmq图片啊??!!!

看了看变量out

out =  tensor([[-0.5666, -0.4227, -0.2641, -0.1588, -0.6122, -0.4571, -0.5924, -0.3251,
         -0.9084, -0.6191],
        [-0.7887, -0.7582, -0.5587, -0.5810, -0.1686, -0.5012, -0.6245, -0.3596,
         -0.0476, -0.2308],
        ................

分别对应predicted 8

原以为这只是意外,,直到我再多写了几个数字识别,效果不堪入目。。。。。

试着寻找原因

打印了out的输入,是许多张28*28的图片;

打印单张图片(image[0]),28*28的一系列像素值;

在这里我发现它的像素值和 我自己造的图片 的像素值 大小不在一个级别

        print('images[0] = ', images[0])   # tensor 28*28
        output_test = net(images)

网络输入:

这是我的images[0]其中一行:     [255., 255., 255., 255., 255., 255., 255.,  24.,   0.,   0.,   0.,
            0.,  48., 255., 255., 255., 255., 255., 255., 255., 255., 255.,
          255., 255., 255., 255., 255., 255.],

而MINIST的是这样的:         [-0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366,
          -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366,
          -0.3366,  0.0962,  2.8837,  2.0054,  0.1344, -0.3366, -0.3366,
          -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366],

网络输出:   

(out是我写的图片的网络输出,output_test 是MINIST数据集作为输入的网络输出)

out =  tensor([[-1.1389, -0.6617, -0.3487, -0.1266, -0.7076, -0.4114, -0.9281, -0.7208,
         -0.7877, -0.2105],
        [-0.4477, -0.6416, -0.5215, -1.2147, -0.4203, -0.5568, -0.4358, -0.2163,
         -0.0528, -0.7060],
MINIST output_test =  tensor([[-2.5220,  7.4153, -3.1206, -4.5774, -2.4503, -4.6098, -1.4864, -2.4603,
 -1.8093, -3.8003],
[-3.8222, -4.2992, -2.3654, -1.5227, -0.5380, -2.1338, -3.0283, -2.5562,
  6.0355, -0.9785],........  

这是问题一,数字大小级别不同;还有黑白背景的问题,经过如下处理:

    for i in range(28):
        for j in range(28):
            gray[i, j] = 255 - gray[i, j]

从白背景黑笔,到黑背景白笔,这时像素值成了大概这样:

         [  0.,   0.,   0.,   0.,   0.,   0.,   0., 231., 255., 255., 255.,
          255., 207.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
            0.,   0.,   0.,   0.,   0.,   0.],
         [  0.,   0.,   0.,   0.,   0.,   0., 207., 239.,   0.,   0.,   0.,
            0., 239., 207.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
            0.,   0.,   0.,   0.,   0.,   0.],

输出依旧不好:

      out =   [-0.4068, -0.3297, -0.4208, -0.3434, -0.2488, -0.6222, -0.7919, -0.1241,
         -0.5921, -0.7793],
        [-1.2336, -1.0043, -0.4402, -1.0629, -0.9021, -0.3701, -0.5665, -0.8160,
         -0.2371, -0.0844],......

“激活” 函数

既然它们数字大小级别不同,不如用一个函数,调整一下

忙猜 -0.3366 应该对应 0

print("max = ", torch.max(images[0], 1))

max =  torch.return_types.max(
values=tensor([[-0.3366, -0.3366, -0.3366, -0.3366,  1.2035,  2.8837,  2.8837,  2.8837,
          2.8837,  2.8837,  2.8837,  2.8837,  2.8837,  2.8837,  2.9091,  2.8837,
          2.8837,  2.8837,  2.8837,  2.8837,  2.8709,  2.8709,  2.6927, -0.3366,
         -0.3366, -0.3366, -0.3366, -0.3366]], device='cuda:0'),

输出了MINIST中每行像素的最大值,255 大概对应3吧

下面这个就大概是两个蓝色(往上翻翻看)的数据之间的关系了!!

对于每一个像素值:MINIST = (sigmoid(ME - 150)- 0.11)*3      ((激动不已,说不定真可以就此预测呢??

开始转换:

def transform_img(imgs):
    # torch.Size([64, 1, 28, 28])
    a = [150]*64*28*28
    a = torch.Tensor(a)
    a.resize_(64, 1, 28, 28)
    a = Variable(a).cuda()
    c = torch.sigmoid(imgs-a)
    # -0.11
    f = [-0.11]*64*28*28
    f = torch.Tensor(f)
    f.resize_(64, 1, 28, 28)
    f = Variable(f).cuda()
    d = torch.add(c, f)
    e = torch.add(d, d)
    g = torch.add(d, e)
    return g

images[0]变成了         [-0.3300, -0.3300, -0.3300, -0.3300, -0.3300, -0.3300,  2.6700,
           2.6700, -0.3300, -0.3300, -0.3300, -0.3300,  2.6700,  2.6700,
          -0.3300, -0.3300, -0.3300, -0.3300, -0.3300, -0.3300, -0.3300,
          -0.3300, -0.3300, -0.3300, -0.3300, -0.3300, -0.3300, -0.3300],

最终预测结果还是不理想。想想也是,神经网络就是一个函数,对于MINIST内的数据,网络拟合得很好。但是对于我的数据,虽然在图片上看来很相似,但是具体的输入的一个个数字并不一致。

又发现问题

训练数据有预处理;被注释掉的两行是关键! 他们的作用在  这里

train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data', train = True, download = True,
              transform = transforms.Compose([
                  # transforms.ToTensor(),   
                  # transforms.Normalize((0.1037,), (0.3081,))
              ])),

ToTensor()能够把灰度范围从0-255变换到0-1之间,而后面的transform.Normalize()则把0-1变换到(-1,1).具体地说,对每个通道而言,Normalize执行以下操作:

image=(image-mean)/std

来试试看吧!!:

def transform(imgs):
    # scale 255 1
    a = [1/255]*64*28*28
    a = torch.Tensor(a)
    a.resize_(64, 1, 28, 28)
    a = Variable(a).cuda()
    imgs = imgs * a
    # Normalize
    mean = [0.1037]*64*28*28
    std = [0.3081]*64*28*28
    mean = torch.Tensor(mean)
    mean.resize_(64, 1, 28, 28)
    mean = Variable(mean).cuda()
    std = torch.Tensor(std)
    std.resize_(64, 1, 28, 28)
    std = Variable(std).cuda()

    imgs = imgs - mean
    imgs = imgs/std
    return imgs

转换后数据:         [-0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366,
           0.5799,  2.5018,  2.9091,  2.9091,  2.9091,  2.9091,  2.9091,
           2.9091,  2.8073,  0.8853, -0.3366, -0.3366, -0.3366, -0.3366,
          -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366],

一篇好文章:python中PIL.Image,OpenCV,Numpy图像格式相互转换  

def transfor():
    image = cv2.imread("0.png")
    image2 = cv2.resize(image, (28, 28))
    gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
    for i in range(28):
        for j in range(28):
            gray[i, j] = 255 - gray[i, j]
    # pimg = Image.frombytes("RGB",(28, 28), gray.tostring())  # pimg is a PIL

    pimg = Image.fromarray(gray)

    test_transform = transforms.Compose(
        [transforms.ToTensor(),  # range [0, 255] -> [0.0,1.0]
         transforms.Normalize(mean=0.1037, std=0.3081)])

    im_tensor = test_transform(pimg).to(torch.device("cuda:0" if torch.cuda.is_available() else "cpu"))
    # print(im_tensor.size())  # torch.Size([1, 28, 28])
    im_tensor = im_tensor.expand(64, 1, 28, 28)
    return im_tensor

tranfor()函数完全是按照对MINIST原始数据(0-255)进行的处理方式写的,再把它用于画图软件的图,效果依旧差。

这是转换后的数据:         [-0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366,
           2.9091,  2.9091,  2.9091, -0.3366, -0.3366, -0.3366, -0.3366,
          -0.3366, -0.3366, -0.3366, -0.3366,  2.9091,  2.9091,  2.9091,
          -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366, -0.3366],

两种transfor 确实有差别    32%

    imgs1 = transform(imgs)
    ret = 0
    imgs = transfor()
    for i in range(64):
        for j in range(28):
            for m in range(28):
                if imgs[i][0][j][m] != imgs1[i][0][j][m]:
                    ret += 1
    print("不相等 = ", ret, "in 64*28*28, percent = ", ret/(64*28*28))
    # 不相等 =  16128 in 64*28*28, percent =  0.32142857142857145

处理数据的均值和方差来自MINIST,而直接作用于我手画的其实不合理。那么,不如不处理,直接对0-1的数据进行学习

还是不太行

最后的最后,我发现,加上这句代码,他就可以了!!!

net.eval()

WoC。

======================================上面是落魄,下面是壮举=====================================================

原来,对于这个网络而言,只要开启测试模式,就连输入0-255像素值的图像也是能够预测的,只不过输出值大一点,但是输出值之间的大小关系还是很有区别的。

比如这是数字3的输出:

        [-685.5930, -512.3919, -414.1933, 1424.1006, -948.8557, -432.5031,
         -546.2578, -509.3032, -673.2338, -471.2930],

可以看到3对应的输出值明显大于其他的

(为了匹配训练网络的时候一个batch的大小,但是又不想写很多个数字,所以就直接repeat了64个。。。)

最后奉上稍微useful一点的一套代码:

import cv2
import torch
import torch.nn as nn
from torchvision import datasets, transforms
from torch.autograd import Variable
from PIL import Image

class LeNet(nn.Module): # 继承class:nn.Module
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Sequential(nn.Conv2d(1, 6, 5, 1, 2), nn.ReLU(),  # Sequential() 是容器,里面是操作
        # 定义conv1函数的是图像卷积函数:输入为图像(1个通道,即灰度图),输出为 6张特征图(6通道)(6个卷积核), 卷积核为5x5正方形,stride:步长,padding:填充
                                   nn.MaxPool2d(2, 2))
        self.conv2 = nn.Sequential(nn.Conv2d(6, 16, 5), nn.ReLU(),
                                   nn.MaxPool2d(2, 2))  #使用2x2的窗口进行最大池化,取池子里最大值,突出特征

        self.fc1 = nn.Sequential(nn.Linear(16 * 5 * 5, 120),  # 定义fc1(fullconnect)全连接函数1为线性函数:y = Wx + b,并将16*5*5个节点连接到120个节点上。
                                 nn.BatchNorm1d(120), nn.ReLU())
        self.fc2 = nn.Sequential(
            nn.Linear(120, 84),
            nn.BatchNorm1d(84),
            nn.ReLU(),nn.Linear(84, 10))
        # 最后的结果一定要变为 10,因为数字的选项是 0 ~ 9

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size()[0], -1)
        x = self.fc1(x)
        x = self.fc2(x)
        return x

def run_net():
    # 导入网络
    net = torch.load('Lenet5_net.pkl')
    net.eval()

    imgs = transfor()

    out = net(imgs)
    predicted = torch.max(out, 1)  # 找第一维最大的

    print(predicted)

def transfor():
    image = cv2.imread("8.png")
    # image2 = cv2.resize(image, (28, 28))
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    for i in range(28):
        for j in range(28):
            gray[i, j] = 255 - gray[i, j]

    pimg = Image.fromarray(gray)

    test_transform = transforms.Compose(
        [transforms.ToTensor(), 
        ])

    im_tensor = test_transform(pimg).to(torch.device("cuda:0" if torch.cuda.is_available() else "cpu"))
    # print(im_tensor.size())  # torch.Size([1, 28, 28])
    im_tensor = im_tensor.expand(64, 1, 28, 28)
    return im_tensor
rc()
run_net()

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值