手写数字数据集、单通道、尺寸为 28 * 28、训练集60000张图片;测试集10000张图片。
1、使用pytorch下载:
'''加载数据集'''
# 加载数据集,下载到当前文件夹下./data,mnist数据集训练集图像有60000张,测试集10000张图片,
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=100, shuffle=True, num_workers=0)
'''测试集10000张'''
testset = torchvision.datasets.MNIST(root='./data', train=False, download=False, transform=transform) #用transform模块对数据进行处理
testloader = torch.utils.data.DataLoader(testset, batch_size=10000, shuffle=True, num_workers=0)
下载后可见文件夹:
-ubyte.gz是压缩文件。
2、查看mnist数据集可用以下代码:
在这里插入代码片'''查看mnist数据集'''
import os
import struct
import numpy as np
# 读取标签数据集
with open('D:\homework\\alexnet\data\MNIST\\raw\\t10k-labels-idx1-ubyte', 'rb') as lbpath:
labels_magic, labels_num = struct.unpack('>II', lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
# 读取图片数据集
with open('D:\homework\\alexnet\data\MNIST\\raw\\t10k-images-idx3-ubyte', 'rb') as imgpath:
images_magic, images_num, rows, cols = struct.unpack('>IIII', imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(images_num, rows * cols)
# 打印数据信息
print('labels_magic is {} \n'.format(labels_magic),
'labels_num is {} \n'.format(labels_num),
'labels is {} \n'.format(labels))
print('images_magic is {} \n'.format(images_magic),
'images_num is {} \n'.format(images_num),
'rows is {} \n'.format(rows),
'cols is {} \n'.format(cols),
'images is {} \n'.format(images))
# 测试取出一张图片和对应标签
import matplotlib.pyplot as plt
choose_num = 1 # 指定一个编号,你可以修改这里
label = labels[choose_num]
image = images[choose_num].reshape(28,28)
plt.imshow(image)
plt.title('the label is : {}'.format(label))
plt.show()
可以看到,mnist数据集原本为np.ndarray的格式,但在下载数据集,调试时,显示mnist的数据类型为tensor类型(如下图),目前还不知道这个data到底指的是什么数据—transform前or transform后?因为此处我对比了cifar-10的数据集,cifar-10在此处调试的data类型为np.ndarray;我曾怀疑是否是transform环节无效,与两个数据集本身的数据类型有关,但验证标明,必须要加上transform.ToTensor后才可以用,而且上文也可知mnist本身的数据类型也是np.ndarray,所以这里为什么显示tensor?—idk.
3、关于mnist数据集
更多咨询可以参考一下
网址:https://www.cnblogs.com/gshang/p/13022239.html
共有八层结构(5个卷积层;3个全连接层;输入图片尺寸应该是227 * 227(原文是224 * 224 有错);池化使用overlap叠层;padding是根据论文中的图片尺寸算出来的;conv1和conv2后又局部响应归一化处理LRN;每层后有Relu;fc1和fc2采用dropout):
layer | filter |
---|---|
conv1 | 96个11* 11 * 3,s=4 |
maxpooling | s=2 , z=3 |
conv2 | 256个 5* 5 * 48,s=1,p=2 |
maxpooling | s=2 , z=3 |
conv3 | 384个 3* 3* 256,s=1,p=1 |
conv4 | 384个3* 3* 192,s=1,p=1 |
conv5 | 256个3* 3* 192,s=1,p=1 |
maxpooling | s=2 , z=3 |
fc1 | (13* 13* 256, 4092) |
fc2 | (4092,4092) |
fc3 | (4092,1000) |
1、alexnet网络 pytorch代码,代码中没又加入局部归一化操作:
'''原论文中作者将参数指定在两个GPU上训练,在本代码中,将所有参数减半,使用CPU训练'''
import torch.nn as nn
class AlexNet(nn.Module):
def __init__(self, num_classes=1000): # num_classes指定分成几类
super(AlexNet, self).__init__()
self.features = nn.Sequential( # 深层网络用sequential打包更加整洁
nn.Conv2d(1, 48, 11, stride=4),
nn.ReLU(inplace=True),
nn.MaxPool2d(3, 2),
nn.Conv2d(48, 128, 5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(3, 2),
nn.Conv2d(128, 192, 3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, 3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, 3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(3, 2)
)
self.classfier = nn.Sequential(
nn.Dropout(p=0.5), # 在第一个全连接层使用dropout
nn.Linear(128 * 6 * 6, 2048),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5), # dropout
nn.Linear(2048, 2048),
nn.ReLU(inplace=True),
nn.Linear(2048, num_classes), # 最后一层可以relu也可以不relu 悬学
)
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, start_dim=1) # 从维度进行展平,dim1是通道数,也可以 x.view(-1,)
x = self.classfier(x)
return x
可以用一下代码来检查一下网络能否运行通常:
import torch
input1 = torch.rand([10, 3, 227, 227])
model = AlexNet()
print(model)
output = model(input1)
将原论文结构片段截取如下:
2、训练Mnist数据集的代码
注释掉的代码没用,是为了研究代码写的,
import torch
import torchvision
import torch.nn as nn
from model import AlexNet
import torch.optim as optim
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
# 对图片进行预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Resize(227),
transforms.Normalize((0.5,), (0.5,))
])
'''加载数据集'''
# 加载数据集,下载到当前文件夹下./data,mnist数据集训练集图像有60000张,测试集10000张图片,
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=100, shuffle=True, num_workers=0)
'''测试集10000张'''
testset = torchvision.datasets.MNIST(root='./data', train=False, download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=10000, shuffle=True, num_workers=0)
'''iteration'''
test_data_iter = iter(testloader)
test_image, test_label = test_data_iter.next()
classes = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
'''minst一共有60000张训练集图片'''
# print(len(list(enumerate(trainloader))))
# for i, data in enumerate(trainloader):
# inputs, labels = data
# print(inputs.shape, labels)
'''查看图片和标签'''
# def imshow(img):
# img = img / 2 + 0.5
# npimg = img.numpy()
# plt.imshow(np.transpose(npimg, (1, 2, 0)))
# plt.show()
#
# # 打印标签
# print(''.join('%5s' % classes[test_label[j]] for j in range(4))) # 打印四张照片,%5s至少输出5个字符,不够用空格补
# # 打印图片
# imshow(torchvision.utils.make_grid(test_image))
'''训练'''
net = AlexNet()
'''loss function'''
loss_function = nn.CrossEntropyLoss() # 带有softmax
optimizer = optim.Adam(net.parameters(), lr=0.001)
for epoch in range(2): # 训练两轮
running_loss = 0.0 # 计算训练集上的损失
for step, data in enumerate(trainloader, start=0): # enumerate会将trainloader变成:序号step、图片数据、标签
inputs, labels = data
optimizer.zero_grad() # 梯度需要清零,不然的话每循环一次梯度值就会累加
outputs = net(inputs) # 得到的outputs形状将是 [batch_size, 类别数的大小]
'''反向传播'''
loss = loss_function(outputs, labels)
loss.backward()
optimizer.step() # 梯度更新
running_loss += loss.item() # loss是一个tensor变量,取item是取得标量,可以节约计算时间、内存
if step % 200 == 199: # 训练集训练200次之后, 丢入一张测试集,试一试准不准
with torch.no_grad(): # 测试集不需要进行梯度计算,所以‘简化’这些数据,节约时间、内存
outputs = net(test_image)
predict_y = torch.max(outputs, dim=1)[1] # outputs维度1里面装的是各类预测的分数,取最大值也就是最可能的分类,[1]标示返回其索引
accuracy = (predict_y == test_label).sum() / test_label.size(0)
print('[%d, %5d] train_loss: %.3f test_accuracy: %.3f' %
(epoch + 1, step + 1, running_loss / 200, accuracy))
running_loss = 0.0
print('finished training')
save_path = './alexnet.pth' # 保存权重训练参数
torch.save(net.state_dict(), save_path)
3、预测一张图片的代码
import torch
import torchvision.transforms as transforms
from PIL import Image
from model import AlexNet
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Resize((227, 227)),
transforms.Normalize((0.5,), (0.5,))
])
classes = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
net = AlexNet()
net.load_state_dict(torch.load('alexnet.pth')) # 加载权重模型
'''mnist是单通道,下载好的图片要转成灰度图'''
im = Image.open('4.jpg') # 网上下一张
im_gray = im.convert('L')
im_gray = transform(im_gray)
im_gray = torch.unsqueeze(im_gray, dim=0)
plt.imshow(im)
plt.figure('number')
plt.axis('off')
plt.show()
with torch.no_grad():
outputs = net(im_gray)
predict_y = torch.max(outputs, dim=1)[1] # 输出标签
print(classes[predict_y])
!!但是,这一套流程下来,训练集和测试集的准确率已经非常高了,可是我在网上下载的三张图片都预测错了,目前我也不知道是为啥。 ------已解决:因为下载的图片像素太大了,剪裁后失真了,下载一个像素低一点就可以了!
1、b c w h 参数不匹配,
2、改用括号括起来的地方没扩
3、是否可callable,是否要加.()
4、关于transforms模块的resize方法的使用,源码介绍如下:
所以要写好参数设置。
关于dropout、nomalize
参考文章:
[1]https://blog.csdn.net/junbaba_/article/details/105673998
[2]https://blog.csdn.net/qq_38765642/article/details/109779370