可以自己手动更改EPOCH和LR来使预测更佳准确
import torch
import torch.nn as nn
import torch.utils.data as Data
import torchvision
import matplotlib.pyplot as plt
import os
import cv2
import time
#from google.colab.patches import cv2_imshow
tis1 =time.perf_counter()
torch.manual_seed(1) # 使用随机化种子使神经网络的初始化每次都相同
# 超参数
EPOCH = 1 # 训练整批数据的次数
BATCH_SIZE = 50
LR = 0.1 # 学习率
#Run the program for the first time Change the value to true
#Change to false after the first run
DOWNLOAD_MNIST=False # 表示还没有下载数据集,如果数据集下载好了就写False
# 下载mnist手写数据集
train_data = torchvision.datasets.MNIST(
root='./data/', # 保存或提取的位置 会放在当前文件夹中
train=True, # true说明是用于训练的数据,false说明是用于测试的数据
transform=torchvision.transforms.ToTensor(), # 转换PIL.Image or numpy.ndarray
download=DOWNLOAD_MNIST, # 已经下载了就不需要下载了
)
test_data = torchvision.datasets.MNIST(
root='./data/',
train=False # 表明是测试集
)
# 批训练 50个samples, 1 channel,28x28 (50,1,28,28)
# Torch中的DataLoader是用来包装数据的工具,它能帮我们有效迭代数据,这样就可以进行批训练
train_loader = Data.DataLoader(
dataset=train_data,
batch_size=BATCH_SIZE,
shuffle=True # 是否打乱数据,一般都打乱
)
# 进行测试
# 为节约时间,测试时只测试前2000个
#
test_x = torch.unsqueeze(test_data.train_data, dim=1).type(torch.FloatTensor)[:2000] / 255
# torch.unsqueeze(a) 是用来对数据维度进行扩充,这样shape就从(2000,28,28)->(2000,1,28,28)
# 图像的pixel本来是0到255之间,除以255对图像进行归一化使取值范围在(0,1)
test_y = test_data.test_labels[:2000]
train_losses = []
train_counter = []
test_losses = []
test_counter = [i*len(train_loader.dataset) for i in range(EPOCH + 1)] #test_losses为横坐标,test_losses为纵坐标
test_acc = []
# 用class类来建立CNN模型
# CNN流程:卷积(Conv2d)-> 激励函数(ReLU)->池化(MaxPooling)->
# 卷积(Conv2d)-> 激励函数(ReLU)->池化(MaxPooling)->
# 展平多维的卷积成的特征图->接入全连接层(Linear)->输出
class CNN(nn.Module): # 我们建立的CNN继承nn.Module这个模块
def __init__(self):
super(CNN, self).__init__()
# 建立第一个卷积(Conv2d)-> 激励函数(ReLU)->池化(MaxPooling)
self.conv1 = nn.Sequential(
# 第一个卷积con2d
nn.Conv2d( # 输入图像大小(1,28,28)
in_channels=1, # 输入图片的高度,因为minist数据集是灰度图像只有一个通道
out_channels=16, # n_filters 卷积核的高度
kernel_size=5, # filter size 卷积核的大小 也就是长x宽=5x5
stride=1, # 步长
padding=2, # 想要con2d输出的图片长宽不变,就进行补零操作 padding = (kernel_size-1)/2
), # 输出图像大小(16,28,28)
# 激活函数
nn.ReLU(),
# 池化,下采样
nn.MaxPool2d(kernel_size=2), # 在2x2空间下采样
# 输出图像大小(16,14,14)
)
# 建立第二个卷积(Conv2d)-> 激励函数(ReLU)->池化(MaxPooling)
self.conv2 = nn.Sequential(
# 输入图像大小(16,14,14)
nn.Conv2d( # 也可以直接简化写成nn.Conv2d(16,32,5,1,2)
in_channels=16,
out_channels=32,
kernel_size=5,
stride=1,
padding=2
),
# 输出图像大小 (32,14,14)
nn.ReLU(),
nn.MaxPool2d(2),
# 输出图像大小(32,7,7)
)
# 建立全卷积连接层
self.out = nn.Linear(32 * 7 * 7, 10) # 输出是10个类
# 下面定义x的传播路线
def forward(self, x):
x = self.conv1(x) # x先通过conv1
x = self.conv2(x) # 再通过conv2
# 把每一个批次的每一个输入都拉成一个维度,即(batch_size,32*7*7)
# 因为pytorch里特征的形式是[bs,channel,h,w],所以x.size(0)就是batchsize
x = x.view(x.size(0), -1) # view就是把x弄成batchsize行个tensor
output = self.out(x)
return output
cnn = CNN()
print(cnn)
# 训练
# 把x和y 都放入Variable中,然后放入cnn中计算output,最后再计算误差
# 优化器选择Adam
optimizer = torch.optim.Adam(cnn.parameters(), lr=LR)
# 损失函数
loss_func = nn.CrossEntropyLoss() # 目标标签是one-hotted
# 开始训练
for epoch in range(EPOCH):
for step, (b_x, b_y) in enumerate(train_loader): # 分配batch data
output = cnn(b_x) # 先将数据放到cnn中计算output
loss = loss_func(output, b_y) # 输出和真实标签的loss,二者位置不可颠倒
optimizer.zero_grad() # 清除之前学到的梯度的参数
loss.backward() # 反向传播,计算梯度
optimizer.step() # 应用梯度
if step % 50 == 0:#Control ouput frequency
test_output = cnn(test_x)#fifty times a check
pred_y = torch.max(test_output, 1)[1].data.numpy()
accuracy = float((pred_y == test_y.data.numpy()).astype(int).sum()) / float(test_y.size(0))
print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.numpy(), '| test accuracy: %.2f' % accuracy)
#train_losses.append(loss.data.numpy().astype(int))#put the loss function value in
test_acc.append(accuracy)
train_counter.append((step * 64) + (epoch * len(train_loader.dataset)))
train_losses.append(loss.data.numpy())
torch.save(cnn.state_dict(), 'cnn2.pkl')#保存模型
cnn.eval()
#print 10 predictions from test data
inputs = test_x[:1000] # 测试n个数据
test_output = cnn(inputs)#将测试数据放入神经网络进行测试
pred_y = torch.max(test_output, 1)[1].data.numpy()#获取模型预测后得到的数据
#accuracy = float((pred_y == test_y[:1000].data.numpy()).astype(int).sum()) / float(test_y.size(0))
#print(accuracy)#output accuracy
print(pred_y[:32], 'prediction number') # 打印模型对图片预测后的预测数据
print(test_y[:32].numpy(), 'real number') #打印被测试图片的正确数据
for i in range(6):
plt.subplot(2,3,i+1)
plt.tight_layout();
plt.imshow(inputs[i][0],cmap='gray',interpolation='none')
plt.title("Ground Truth:{}".format(test_y[i]))
plt.xticks([])
plt.yticks([])
plt.show()
tis2 =time.perf_counter()
fig=plt.figure()
plt.plot(train_counter,test_acc,color='blue')
plt.plot(train_counter,train_losses,color='red')
plt.legend(["Test_acc","Train loss"],loc='upper right')
plt.xlabel('number of training example seen')
plt.ylabel('the value')
plt.show()