一、网络模型代码
作为深度学习的入门,这个数据集应该是入门必备,本网络以官方下载的手写体数据集作为训练样本,自定义神经网络包含三层卷积,两层线性网络,在3060GPU上训练50轮,batchsize设置为64,优化器选用Adam。注意:with torch.no_grad():在测试前需加上,代码遗漏了
from torch.utils.tensorboard import SummaryWriter
import torch
from torchvision import transforms
from torchvision import datasets
import torch.nn as nn
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
# 在GPU上运行
device = torch.device("cuda")
# prepare data 1 channel, 28x28
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307), (0.3081))]) # 均值,方差
train_data = datasets.MNIST(root="./mydata/mnist/", train=True, transform=transform, download=True)
test_data = datasets.MNIST(root="./mydata/mnist/", train=False, transform=transform, download=True)
train_size = len(train_data)
test_size = len(test_data)
print("训练集长度:", train_size) # 60000
print("测试集长度:", test_size) # 10000
# load data
train_load = DataLoader(train_data, batch_size=64, shuffle=True, drop_last=True)
test_load = DataLoader(test_data, batch_size=64, shuffle=False, drop_last=True)
# create my module
class Mymodel(nn.Module):
def __init__(self):
super(Mymodel, self).__init__()
# 较为简单的模型构建: 定义三层卷积,两层线性层的参数
# input:(64,1,28,28)
self.conv1 = torch.nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1,
padding=1) # ep.parameters
self.conv2 = torch.nn.Conv2d(16, 8, 3, 1, 1)
self.conv3 = torch.nn.Conv2d(8, 4, 3, 1, 1)
self.pooling = torch.nn.MaxPool2d(kernel_size=2) # defalt padding=0,stride=k_z
self.l1 = torch.nn.Linear(in_features=784, out_features=64)
self.l2 = torch.nn.Linear(64, 10)
# 前向传播
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = F.relu(self.conv3(x))
x = F.relu(self.pooling(x)) # 输出为(64,4,14,14)
x = x.view(64, -1) # -1 ="784"得到(64,784)
x = F.relu(self.l1(x))
x = self.l2(x) # last layer 不用激活函数
return x
model = Mymodel()
model = model.to(device) # to gpu
# 损失函数
loss = nn.CrossEntropyLoss()
loss = loss.to(device) # to gpu
# 反向传播(优化器)
optimizer = optim.Adam(model.parameters(), lr=0.01)
# 设置训练的一些参数
# 训练轮数
epoch = 50
# 训练的次数每个batchsize为1次,1个epoch需要训练937.5次
train_step = 0
test_step = 0
# 可视化
writer = SummaryWriter("./mimistlog")
for i in range(epoch):
# train---记录训练误差
print(f"第{i + 1}轮训练开始")
for data in train_load:
inputs, targets = data
inputs, targets = inputs.to(device), targets.to(device) # to gpu
optimizer.zero_grad()
outputs = model(inputs) # 输出为(64,10)
loss_fun = loss(outputs, targets) # 会自动将targets进行one-hot
loss_fun.backward()
optimizer.step()
train_step += 1
if train_step % 100 == 0:
print(f"第{train_step}次训练,loss:{loss_fun.item()}")
writer.add_scalar("train_loss", loss_fun.item(), train_step) # 名称,y为损失,x为训练次数
# test---记录测试误差,测试集的正确率
correct = 0
all = 0
loss_all = 0
for data in test_load:
images, labels = data
images, labels = images.to(device), labels.to(device)
outputs = model(images)
loss_fun = loss(outputs, labels)
predicted = torch.max(outputs.data, dim=1)[1] # 按行取出预测值10个当中最大的,进行acc计算
test_step += 1
all += labels.size(0) # 累加每次的label数,也就是用于测试集的数量
correct += (predicted == labels).sum().item() # 进行张量的比较运算,计和正确个数
loss_all += loss_fun # 计算测试样本总损失
# 可视化测试过程
writer.add_scalar("test_loss", loss_fun.item(), test_step)
acc = (correct / all)
loss_average = (loss_all / all)
writer.add_scalar("test_accuracy", acc, i)
print(f"测试集上的正确率为:{acc}")
print(f"测试集上的平均损失为:{loss_average}")
if i % 9 == 0:
torch.save(model, f"ct1_{i}.pth")
print("模型已保存")
writer.close()
二、模型训练中遇到的问题与优化
1.batchsize问题
在第一次训练遇到如下报错
意思是矩阵形状不匹配,检查后发现是batchsize的设置问题,该手写体测试样本为60000个,设置的size=64,在一个epoch下面会剩下 (60000/64=937+32)32个图片,于是在卷积最后一层会输出32x4x14x14的张量,在经过前向传播view展平后会reshape成(64,392)的张量,与下层线性层要求输入的784的维度不符,报错
解决方法:
1.在dataload的参数加上 drop_last=True 就会自动抛弃最后一个不足64的数据。
2.调整batchsize大小
3.重新分割数据集使其可以整除。
2.过拟合问题
模型在训练集与测试集上一般是有一个很好的临界点,让其在两个集上的效果都较好
加载tensorboard可视化查看训练误差与测试误差
可以发现训练误差不断下降,但是测试误差先下降后上升,说明模型过拟合了,学习到了噪声或者
是学习的过于优秀,而泛化能力很差
解决方法:
1.early stopping观察测试误差何时开始增大,大约在测试集1700step测试,约10个epoch左右停止
效果更好,下次训练设置相应的训练轮数。也可设置save的过程,在进行预测时采用第10次的pth
经过调整后效果还可以。
2.dropout设置丢弃神经元的概率
3.采用更好的主干网络等等
target:需要学习更多优质的网络与数据集更好的进行特征提取,进行参数优化
三、Predict
测试模型效果
1.用手写七张数字图片,裁剪,转为单通道,然后进行识别。
首次识别效果不佳---- 8个只对了4个
继续调整模型,更换了优化器为SGD,训练25轮,此时效果变得更好
继续预测
2.利用画图书写十张0-9的图片,然后进行格式的转换,利用训练好的模型进行按顺序遍历预测
import os
import torch
from torchvision import transforms
import torch.nn as nn
import torch.nn.functional as F
from PIL import Image
path="D:\Pycharm\Imageprocessing\workmyself\predict_data"
img_path=os.listdir(path) #读取图片的路径,以列表的形式存储
transform = transforms.Compose([transforms.ToTensor()])
device=torch.device("cuda")
class Mymodel(nn.Module):
def __init__(self):
super(Mymodel, self).__init__()
# 较为简单的模型构建: 定义三层卷积,两层线性层的参数
# input:(1,1,28,28)
self.conv1 = torch.nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1,
padding=1) # ep.parameters
self.conv2 = torch.nn.Conv2d(16, 8, 3, 1, 1)
self.conv3 = torch.nn.Conv2d(8, 4, 3, 1, 1)
self.pooling = torch.nn.MaxPool2d(kernel_size=2) # defalt padding=0,stride=k_z
self.l1 = torch.nn.Linear(in_features=784, out_features=64)
self.l2 = torch.nn.Linear(64, 10)
# 前向传播
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = F.relu(self.conv3(x))
x = F.relu(self.pooling(x)) # 输出为(1,4,14,14)
x = x.view(1, -1) # -1 ="784"得到(1,784)
x = F.relu(self.l1(x))
x = self.l2(x) # last layer 不用激活函数
return x
model =torch.load("ct2_19.pth")
for i in range(10):
os_img_path = os.path.join(path,img_path[i])#拼接路径读取图片
img=Image.open(os_img_path)
img= img.convert("L")
img_change1=transform(img)
single_img = torch.reshape(img_change1, (1, 1, 28, 28))
single_img = single_img.to(device)
output = model(single_img)
#print(output)
# mean=torch.mean(img_change1,dim=0)
# single_img=mean.unsqueeze(0)
# single_img=torch.reshape(single_img,(1,1,28,28))
# single_img=single_img.to(device)
# output=model(single_img)
print(torch.max(output.data,dim=1)[1])
3.图片的数字与label是对应的,采用训练25次后保存的权重数据
预测结果10对7,达到70%
4.若采用仅训练10次的训练数据,可见效果不够好
模型的优劣以及在未知数据集上的表现与很多参数有关,往后学习更加复杂模型的同时也要重视模型优化的经验。