Pytorch识别自己用画图工具写的手写数字
目录
整体思路
- 画图保存png,用cv2转换成28*28的bmp文件(灰度图)
- 用minist数据集训练Lenet5,并保存网络
- 预测
第一步
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 3 和 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()