定义模型
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.model=nn.Sequential(
#卷积(-1)*1*28*28 ->(-1)*6*28*28 公式:[W=(W-F+2P)/S+1] [卷积输出大小=(输入大小-卷积核大小+2倍填充)/步长+1]
#输入输出通道根据LeNet模型直接确定为1和6 卷积核大小是人为设定为5 5*5卷积后大小不变一般是padding=2
Conv2d(in_channels=1,out_channels=6,kernel_size=5,padding=2),Sigmoid(),
#池化(-1)*6*28*28 -> (-1)*6*14*14 公式:[W=(W-F)/S+1] [池化输出大小=[(输入大小-卷积核大小)/步长]+1]
#当池化窗口扫描到边缘后池化窗口大于当前被扫窗口时,存在一个参数确定是否保留当前窗口池化后的数值,一般默认保留,可以防止信息丢失。
AvgPool2d(kernel_size=2,stride=2),
# 卷积(-1)*6*14*14 ->(-1)*16*10*10
Conv2d(in_channels=6,out_channels=16,kernel_size=5,padding=0),Sigmoid(),
# 池化(-1)*16*10*10 -> (-1)*16*5*5
AvgPool2d(kernel_size=2,stride=2),
#拉平 (-1)*16*5*5 -> (-1)*400 备注16*5*5=400
Flatten(),
#全连接 (-1)*400 -> (-1)*120
Linear(in_features=400,out_features=120),Sigmoid(),
# 全连接 (-1)*120 -> (-1)*84
Linear(in_features=120,out_features=84),Sigmoid(),
# 全连接 (-1)*84 -> (-1)*10
Linear(in_features=84,out_features=10)
)
def forward(self,x):
output=self.model(x)
return output
# lenet=LeNet()
# print(lenet)
# x=torch.ones((64,1,28,28))
# y=lenet(x)
# print(y.shape)
在整个卷积块中,与上一层相比,每一层特征的高度和宽度都减小了。 第一个卷积层使用 2 个像素的填充,来补偿 5×5 卷积核导致的特征减少。 相反,第二个卷积层没有填充,因此高度和宽度都减少了 4 个像素。 随着层叠的上升,通道的数量从输入时的 1 个,增加到第一个卷积层之后的 6 个,再到第二个卷积层之后的 16 个。 同时,每个池化层的高度和宽度都减半。最后,由每个全连接层减少维数,最终输出一个维数与结果分类数相匹配的输出。
数据预处理
import torchvision
from torch.utils.data import DataLoader
'''数据预处理'''
dataset_transform=torchvision.transforms.Compose([ #将预处理方法打包为一个整体
torchvision.transforms.ToTensor(), #第一个预处理:把PIL图片或者numpy数组转换为张量,转换前的numpy数组通道排列顺序是【】转换为张量之后的通道排序为【batch,channel,height,width】
torchvision.transforms.Normalize(mean=(0.5,0.5,0.5),std=(0.5,0.5,0.5)) #第二个预处理:标准化 【(输入数据-均值)/ 标准差】
#(输入数据-0.5) / 0.5
])
训练网络的一些参数
'''设置训练网络的一些参数'''
BATCH_SIZE=256 #批次大小
#1e-2=1 x (10)^(-2)=1/100=0.01
LEARNING_RATE=0.001 #学习率
EPOCH=1 #训练轮数
total_train_step=0 #记录训练次数
total_val_step=0 #记录验证次数w
用于画图
'''用于画图'''
train_loss_list=[]
Val_loss_list=[]
Val_accuracy_list=[]
下载数据集
'''下载数据集'''
train_set=torchvision.datasets.CIFAR10(root='./cifar10', #将数据集放在同级目录下的cifar10文件夹下
train=True, #True代表为训练数据
download=True, #如果没有下载过数据集,会自动下载
transform=dataset_transform #数据预处理
) #5w张训练图片
val_set=torchvision.datasets.CIFAR10(root='./cifar10',
train=False,
download=True,
transform=dataset_transform
)
加载数据集
'''加载数据集'''
train_loader=DataLoader(
dataset=train_set, #导入训练集
batch_size=BATCH_SIZE, #在训练集中每次随机取出BATCH_SIZE个数据
shuffle=True #将数据打乱
)
val_loader=DataLoader(
dataset=val_set,
batch_size=BATCH_SIZE,
shuffle=False #验证时打乱与否没有影响,只需要查看效果,不会影响模型参数
) # 10000张验证图片
迭代器
train_data_iter=iter(train_loader) #返回一个迭代器 ,dataloader是一个可迭代对象,可以使用iter()进行访问
train_imgs,trian_labels=train_data_iter.next() #可以用next()一个批次一个批次的访问可迭代对象,也可以用下面的enumerate(dataloader)形式访问
计算数据集长度
train_set_size=len(train_set)
val_set_size=len(val_set)
创建网络模型
lenet=LeNet()
# print(lenet)
lenet.to('cuda')
损失函数
'''损失函数'''
loss_function=nn.CrossEntropyLoss()
优化器
optimizer=torch.optim.Adam(lenet.parameters(),lr=LEARNING_RATE) #将lenet可训练的所有参数都进行训练
展示一些图片
# '''尝试展示一些图片,玩玩而已'''
# import matplotlib.pyplot as plt
# import numpy as np
# #定义函数
# def imshow(img):
# img = img / 2 + 0.5 # 反标准化 预处理时将数据进行了标准化操作,(输入数据-0.5) / 0.5,所以此处把标注化给抵消掉
# npimg = img.numpy() # 预处理时将图片转化为了tensor类型,此处要把数据重新转换为numpy数据形式,同时还需要进行下一行代码的转换通道顺序操作
# plt.imshow(np.transpose(npimg, (1, 2, 0)))#此处的 1 2 0 填写规则见博客https://blog.csdn.net/qq_39413850/article/details/121699885
# plt.show()
# #随机取出一些训练图片
# dataiter=iter(train_loader)
# val_imgs,val_labels=dataiter.next()
# #展示图片
# imshow(torchvision.utils.make_grid(val_imgs))
# #打印标签
# classes = ('plane', 'car', 'bird', 'cat',
# 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# print(' '.join('%5s'% classes[val_labels[j]] for j in range(BATCH_SIZE)))
训练与测试
'''训练'''
for enpoch in range(EPOCH):
print("----------Begin epoch{} ----------".format(enpoch + 1))
sum_loss=0 #累计损失
lenet.train()
for idx,data in enumerate(iterable=train_loader,start=0):
imgs, targets = data[0].to('cuda'), data[1].to('cuda')
optimizer.zero_grad() # 梯度清零
outputs=lenet(imgs) #将图片放入lenet模型
loss=loss_function(outputs,targets) #参数一:预测值,参数二:真实值
loss.backward() #反向传播
optimizer.step() #参数更新
sum_loss+=loss.item() #累计损失并且将损失变为一个具体的数
if idx%390==389: #每隔500次打印一次
print('iter:[%d,%5d] loss:%.3f'%(enpoch+1,idx+1,sum_loss)) #打印当前轮次,当前迭代索引,累计损失
train_loss_list.append(sum_loss/390) #Train loss vs. Iters
sum_loss=0.0 #将损失清零,进行下一次迭代
sava_path = './lenet_{}.pth'.format(enpoch)
torch.save(lenet.state_dict(), sava_path)
print("save model :lenet{}".format(enpoch))
# 测试
lenet.eval()
sum_correct=0
total_val_loss = 0
with torch.no_grad():
for data in val_loader:
imgs, targets = data
imgs = imgs.to('cuda')
targets = targets.to('cuda')
outputs = lenet(imgs) #shape:[batch,10]
loss = loss_function(outputs,targets)
total_val_loss = total_val_loss + loss.item()
correct = (outputs.argmax(1) == targets).sum() #sum求True的个数
sum_correct = sum_correct+correct
print("Val Loss:{:.2f}".format(total_val_loss))
Val_loss_list.append(total_val_loss/78)
print("Val Accuracy:{:.2f}%".format(100*(sum_correct)/val_set_size))
Val_accuracy_list.append((100 * (sum_correct.item()) / val_set_size))
绘图
x1=range(0,1)
x2=range(0,1)
x3=range(0,1)
y1=train_loss_list
y2=Val_loss_list
y3=Val_accuracy_list
plt.figure()
plt.plot(x1,y1,'r.-',x2,y2,'g.-')
plt.title('Loss vs. Epoches ')
plt.xlabel('Epoches')
plt.ylabel('Train loss:red | Val loss:green')
plt.figure()
plt.plot(x3,y3,'c.-')
plt.title('Val accuracy vs. Epoches')
plt.xlabel('Epoches')
plt.ylabel('Val accuracy')
plt.show()