Files
FoodClassifier/demo/识别CIFAR10数据集.py
T

774 lines
45 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import random
import torch
# torchvision是PyTorch中专门用来处理图像的库,使用了torchvision就非常方便
import torchvision
# transforms是torchvision库用来对图像进行变换的
import torchvision.transforms as transforms
#time 是Python自带的库,引入它主要是用于记录训练时间
import time
import sys
from torch.utils.data import random_split
import numpy as np
from tqdm import tqdm
from colorama import Fore, Style, Back
import os
from torchvision.models import resnet18,resnet34,resnet50,resnet101
import matplotlib.pyplot as plt
# print('火山引擎,我来了')
# 记录开始时间
start_time = time.time()
# 加上设备,然后准备放到赠送的线上GPU上训练
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
transforms.RandomHorizontalFlip(), #随机水平翻转
transforms.RandomCrop(32, padding=4), #随机裁剪
transforms.RandomErasing(scale=(0.04, 0.2), ratio=(0.5, 2)) #随机遮挡
])
# 下载CIFAR10训练集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
# 加载训练集,训练集需要打乱,每次训练4个样本
# trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=0)
# trainloader = torch.utils.data.DataLoader(trainset, batch_size=1, shuffle=True, num_workers=0)
# train_batch_size = 10
# 每次给模型扔128张图片
# train_batch_size = 128
train_batch_size = 512
# val_batch_size = 50
val_batch_size = 512
# test_batch_size = 30
test_batch_size = 512
# train_batch_size = 100
# 假设我们要将数据集划分为90%训练集和10%验证集
split = int(len(trainset) * 0.9)
# 把随机种子固定下来,这样多次训练,就不会变化,从而避免验证集精度虚高
torch.manual_seed(42)
np.random.seed(42)
random.seed(42)
# 使用random_split函数随机划分数据集,它是torch封装的一个工具
trainset, valset = random_split(trainset, [split, len(trainset) - split])
# 创建数据加载器
# train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)
# val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers=2)
# 打乱数据
trainloader = torch.utils.data.DataLoader(trainset, batch_size=train_batch_size, shuffle=True, num_workers=0)
valloader = torch.utils.data.DataLoader(valset, batch_size=val_batch_size, shuffle= False, num_workers=0)
# trainloader = torch.utils.data.DataLoader(trainset, batch_size=1, shuffle=True, num_workers=2)
# trainloader = torch.utils.data.DataLoader(trainset, batch_size=9, shuffle=True, num_workers=0)
# 下载CIFAR10测试集,所以train这个参数这里要设置为False
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
# 加载测试集,测试集不需要打乱
# testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=0)
# 测试的时候可以不用打乱数据
# testloader = torch.utils.data.DataLoader(testset, batch_size=1, shuffle=False, num_workers=0)
# testloader = torch.utils.data.DataLoader(testset, batch_size=10, shuffle=False, num_workers=0)
testloader = torch.utils.data.DataLoader(testset, batch_size=test_batch_size, shuffle=False, num_workers=0)
# testloader = torch.utils.data.DataLoader(testset, batch_size=9, shuffle=False, num_workers=0)
# 定义CIFAR10数据集的十分类类别
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
import numpy as np
# import matplotlib.pyplot as plt
# 定义一个展示图片的函数
def imshow(img):
img = img / 2 + 0.5
npimg = img.numpy()
# plt.imshow(np.transpose(npimg, (1, 2, 0)))
# plt.show()
#因为这块只是展示图片,没太多必要,所以先注释掉
# 从数据迭代器中读取一张图片
#dataiter = iter(trainloader)
#images, labels = next(dataiter)
# # 展开图片
#imshow(torchvision.utils.make_grid(images))
# #打印标签
#print(''.join('%15s' % classes[labels[j]] for j in range(4)))
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
#定义两个卷积层
# self.conv1 = nn.Conv2d(3,6,5)
# self.conv2 = nn.Conv2d(6,16,5)
# 第一个卷积层,输入通道数为3(因为是RGB图片,有3个颜色通道),输出通道数为6,这个是可以随意设置,卷积核大小为3*3
# self.conv1 = nn.Conv2d(3,6,3)
# 如果卷积核是3*3,那么给周围填充1,刚好尺寸不变
self.conv1 = nn.Conv2d(3,64,3,padding=1)
# 第二个卷积层,输入通道数为6,因为上次卷积之后输出是6个特征图,输出通道数为16,这个是可以随意设置,卷积核大小同样为3*3
self.conv2 = nn.Conv2d(64,64,3,padding=1)
#新增加一个卷积层,因为上一个卷积层输出是16个特征图,所以输入通道数为16,输出通道数为16,这个是可以随意设置,卷积核大小同样为3*3
self.conv3 = nn.Conv2d(64,128,3,padding=1)
self.conv4 = nn.Conv2d(128,128,3,padding=1)
self.conv5 = nn.Conv2d(128,256,3,padding=1)
self.conv6 = nn.Conv2d(256,256,3,padding=1)
self.conv7 = nn.Conv2d(256,256,3,padding=1)
self.conv8 = nn.Conv2d(256,512,3,padding=1)
self.conv9 = nn.Conv2d(512,512,3,padding=1)
self.conv10 = nn.Conv2d(512,512,3,padding=1)
# self.conv11 = nn.Conv2d(512,512,3,padding=1)
# self.conv12 = nn.Conv2d(512,512,3,padding=1)
# self.conv13 = nn.Conv2d(512,512,3,padding=1)
# 做了批归一化,能有效避免梯度消失和梯度爆炸
self.bn64 = nn.BatchNorm2d(64)
self.bn128 = nn.BatchNorm2d(128)
self.bn256 = nn.BatchNorm2d(256)
self.bn8 = nn.BatchNorm2d(512)
self.bn9 = nn.BatchNorm2d(512)
self.bn10 = nn.BatchNorm2d(512)
# self.bn11 = nn.BatchNorm2d(512)
# self.bn12 = nn.BatchNorm2d(512)
# self.bn13 = nn.BatchNorm2d(512)
# self.conv3 = nn.Conv2d(16,9,3)
#再增加一个卷积层
# self.conv4 = nn.Conv2d(16,6,1)
#定义池化层,池化核大小为2*2
self.pool = nn.MaxPool2d(2,2)
#定义三个全连接层
# self.fc1 = nn.Linear(16*5*5,120)
#每次输入4个样本,每个样本有3个通道,所以输入通道数为4*3*3*3
# self.fc1 = nn.Linear(16*3*3,120)
# self.fc1 = nn.Linear(16*4*3*3,120)
# self.fc1 = nn.Linear(16*4,120)
# self.fc1 = nn.Linear(16*16*2*169,120)
# self.fc1 = nn.Linear(73728,120)
# self.fc1 = nn.Linear(256*22*22,120)
# self.fc1 = nn.Linear(256*4*4,120)
# self.fc1 = nn.Linear(512*4*4,120)
# self.fc1 = nn.Linear(512*4*4,800)
self.fc1 = nn.Linear(512*2*2,800)
# self.fc1 = nn.Linear(256*5*5,120)
# self.fc1 = nn.Linear(144*16*16*2,120)
# self.fc1 = nn.Linear(6,120)
# self.fc1 = nn.Linear(4*4*4*3*3,120)
# self.fc2 = nn.Linear(120,84)
self.fc2 = nn.Linear(800,84)
# self.fc4 = nn.Linear(84,84)
# self.fc5 = nn.Linear(84,84)
# self.fc2 = nn.Linear(120,84)
self.fc3 = nn.Linear(84,10)
# 增加drop-out层,丢弃概率
# self.dropout = nn.Dropout(0.5)
# 定义前向传播,模型的关键还在于前向传播
def forward(self,x):
# x = self.pool(F.relu(self.conv1(x)))
x = F.relu(self.bn64(self.conv1(x)))
x = self.pool(F.relu(self.bn64(self.conv2(x))))
# x = self.pool(F.relu(self.conv2(x)))
# x = F.relu(self.conv2(x))
# x = self.pool(F.relu(self.conv3(x)))
# x = F.relu(self.conv3(x))
x = F.relu(self.bn128(self.conv3(x)))
# x = F.relu(self.conv4(x))
# x = self.pool(F.relu(self.conv4(x)))
x = self.pool(F.relu(self.bn128(self.conv4(x))))
# x = F.relu(self.conv5(x))
x = F.relu(self.bn256(self.conv5(x)))
# x = self.pool(F.relu(self.conv5(x)))
x = F.relu(self.bn256(self.conv6(x)))
# x = F.relu(self.conv7(x))
# x = self.pool(F.relu(self.conv7(x)))
x = self.pool(F.relu(self.bn256(self.conv7(x))))
# x = self.pool(F.relu(self.bn8(self.conv8(x))))
x = F.relu(self.bn8(self.conv8(x)))
# x = F.relu(self.bn9(self.conv9(x)))
x = self.pool(F.relu(self.bn9(self.conv9(x))))
x = F.relu(self.bn10(self.conv10(x)))
# x = self.pool(F.relu(self.bn10(self.conv10(x))))
# x = F.relu(self.bn11(self.conv11(x)))
# x = F.relu(self.bn12(self.conv12(x)))
# x = F.relu(self.bn13(self.conv13(x)))
#变换x的形状以适配全连接的输入,因为全连接层输入必须是二维的,所以需要将x的形状变成二维的,因为图片是32*32*3,需要将三维变量拉长
# x = x.view(-1,16*5*5)
# x = x.view(-1,16*3*3)
# x = x.view(-1,16*4*3*3)
# x = x.view(-1,16*4)
# x = x.view(-1,16*16*2*169)
# x = x.view(-1,73728)
# x = x.view(-1,256*22*22)
# x = x.view(-1,256*4*4)
# x = x.view(-1,512*4*4)
x = x.view(-1,512*2*2)
# x = x.view(-1,256*5*5)
# x = x.view(-1,144*16*16*2)
# x = x.view(-1,6)
# x = x.view(-1,4*4*4*3*3)
# x = self.dropout(x)
x = F.relu(self.fc1(x))
# x = self.dropout(x)
x = F.relu(self.fc2(x))
# x = self.dropout(x)
# x = F.tanh(self.fc2(x))
# x = F.sigmoid(self.fc2(x))
# x = F.relu(self.fc4(x))
# x = F.relu(self.fc5(x))
x = self.fc3(x)
# print(x.shape)
return x
net = Net().to(device)
# 直接加载训练好的模型,就不需要从头开始训练了。如果不加载,一上来损失就是2.290,很高。如果加载了模型,一上来损失只有0.127,效果太明显了,这就是微调的作用
# net.load_state_dict(torch.load('./cifar_net56.pth'))
# num_classes,代表最终分类的数量,因为CIFAR10有10个分类,所以这里要设置为10
# net = resnet18(pretrained=False, num_classes=10).to(device)
# net = resnet18(pretrained=True, num_classes=10).to(device)
# net = resnet18(pretrained=True)
# net = resnet34(pretrained=True)
# net = resnet50(pretrained=False)
# net = resnet101(pretrained=True)
# 冻结模型的所有层,除了最后的全连接层
# for param in net.parameters():
# param.requires_grad = False
#
# last_layer = net.fc
# # 替换最后一个全连接层
# num_classes = 10 # CIFAR10的数据类数为10
# new_last_layer = torch.nn.Linear(last_layer.in_features, num_classes)
# net.fc = new_last_layer
# num_ftrs = net.fc.in_features
# net.fc = nn.Linear(num_ftrs, 10)
# 检查是否有可用的GPU,如果有多个GPU,则使用 DataParallel
if torch.cuda.device_count() > 1:
print(f"Let's use {torch.cuda.device_count()} GPUs")
net = nn.DataParallel(net)
net.to(device)
print(net)
import torch.optim as optim
# 定义损失函数,CrossEntropyLoss()是交叉熵损失函数
criterion = nn.CrossEntropyLoss()
#原本是0。001,改为0.0001
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9)
optimizer = optim.Adam(net.parameters(),lr = 0.0001)
# 只更新全连接层的参数,把卷积层的参数冻结,不更新
# optimizer = optim.SGD(net.fc.parameters(),lr = 0.0001, momentum = 0.9)
# optimizer = optim.SGD(net.fc.parameters(),lr = 0.0002, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 0.0002, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9,weight_decay=0.001)
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9,weight_decay=0.01)
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9,weight_decay=0.005)
# optimizer = optim.SGD(net.parameters(),lr = 0.0005, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 0.00001, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 0.001, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 0.3, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 0.5, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 1, momentum = 0.9)
# 为了避免过拟合,考虑加入正则化,1e-4
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9, weight_decay=0.003)
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9, weight_decay=0.005)
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9, weight_decay=0.01)
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9, weight_decay=0.001)
# optimizer = optim.SGD(net.parameters(),lr = 0.0001, momentum = 0.9, weight_decay=0.0001)
# optimizer = optim.SGD(net.parameters(),lr = 0.001, momentum = 0.9)
# optimizer = optim.SGD(net.parameters(),lr = 0.01, momentum = 0.9)
# 使用ADAM梯度下降方法
# optimizer = optim.Adam(net.parameters(),lr = 0.01)
# min_loss = 0.3
# min_loss = 0.3
#
midModelPath = './cifar_net103.pth'
finallPATH = './cifar_net104.pth'
max_val_score = 0
break_max_val = False
epoch_list = []
val_acc_list = []
# 每次验证的正确率
every_val_acc = ''
def train(epoch):
# net = Net().to(device)
# print(net)
# net.load_state_dict(torch.load('./cifar_net76.pth'))
# net.load_state_dict(torch.load('./cifar_net75.pth',map_location= 'cpu'))
min_loss = 0.3
for epoch in range(epoch):
running_loss = 0.0
# 该代码片段使用enumerate()函数遍历trainloader数据加载器,并为每个数据项提供一个索引。
# enumerate()函数从0开始计数,并将每个数据项和相应的索引存储在一个元组中,
# 然后将这些元组传递给循环中的变量i和data。这样可以在遍历数据时轻松地访问每个数据项的索引,
# 以便执行相应的操作。
for i,data in enumerate(trainloader,0):
inputs,labels = data
# print(inputs)
#将梯度清零,如果不清零,梯度会累加,导致梯度爆炸,或者梯度消失,导致训练效果不好
optimizer.zero_grad()
#得到输出张量
outputs = net(inputs.to(device))
# print(outputs.shape)
# print(labels.shape)
#计算损失值
loss = criterion(outputs,labels.to(device))
#进行反向传播
loss.backward()
#更新参数
optimizer.step()
#打印训练信息
running_loss += loss.item()
# if(i +1)% 2000 == 0:
# if(i +1)% 100 == 0:
if(i +1)% 10 == 0:
runTime = time.strftime("%H小时%M分%S秒", time.gmtime(time.time() - start_time))
tqdm.write(f"Runtime:{Fore.GREEN}{runTime}{Style.RESET_ALL}")
# print('[第%d轮, 训练%5d个样本] loss : %.3f' % (epoch +1, (i+1)*train_batch_size,running_loss /2000))
tqdm.write(
f"第{Fore.YELLOW}{epoch+1}{Style.RESET_ALL}轮,训练{Fore.YELLOW}{(i+1)*train_batch_size}{Style.RESET_ALL}个样本, loss: {Fore.CYAN}{running_loss/2000}{Style.RESET_ALL}")
sys.stdout.flush() # 强制刷新输出缓冲区
# 以时分秒的形式打印下程序运行时长
# print('Runtime',time.strftime("%H:%M:%S",time.gmtime(time.time()-start_time)))
# print('Runtime',time.strftime("%H小时%M分%S秒",time.gmtime(time.time()-start_time)))
torch.save(net.state_dict(), midModelPath)
verify()
global break_max_val
if (break_max_val):
tqdm.write(
# f"{Back.LIGHTBLUE_EX} {Fore.LIGHTRED_EX}🔥 打破记录了!!!{Back.RESET}{Style.RESET_ALL}")
f"{Fore.LIGHTRED_EX}🔥 打破记录了!!!{Style.RESET_ALL}")
mytestTrainSet()
eval()
# if running_loss/2000 < min_loss:
# if running_loss/2000 < min_loss:
# print('打破记录')
# min_loss = running_loss/2000
# # PATH = './cifar_net70.pth'
# PATH = midModelPath
# torch.save(net.state_dict(),PATH)
# # 打印验证集精度
# verify()
# # 打印训练集精度
# testTrainSet()
# 刷新输出缓冲区,作用是确保输出信息立即显示在控制台上,不然在云服务器上半天显示不出来
sys.stdout.flush()
running_loss = 0.0
print("")
print('完成训练')
#
# PATH = './cifar_net71.pth'
PATH = finallPATH
# # # # #保存模型
torch.save(net.state_dict(),PATH)
eval()
def trainResNet(epoch):
net.load_state_dict(torch.load('./cifar_net101.pth',weights_only=True))
min_loss = 0.3
# 加载预训练的ResNet模型(如果需要从头开始训练,则可以去掉预训练权重)
# net = resnet18(pretrained=False, num_classes=10)
# print(net)
for epoch in range(epoch):
running_loss = 0.0
# 该代码片段使用enumerate()函数遍历trainloader数据加载器,并为每个数据项提供一个索引。
# enumerate()函数从0开始计数,并将每个数据项和相应的索引存储在一个元组中,
# 然后将这些元组传递给循环中的变量i和data。这样可以在遍历数据时轻松地访问每个数据项的索引,
# 以便执行相应的操作。
for i,data in enumerate(trainloader,0):
inputs,labels = data
#将梯度清零,如果不清零,梯度会累加,导致梯度爆炸,或者梯度消失,导致训练效果不好
optimizer.zero_grad()
#得到输出张量
outputs = net(inputs.to(device))
#计算损失值
loss = criterion(outputs,labels.to(device))
#进行反向传播
loss.backward()
#更新参数
optimizer.step()
#打印训练信息
running_loss += loss.item()
if(i +1)% 2000 == 0:
runTime = time.strftime("%H小时%M分%S秒", time.gmtime(time.time() - start_time))
tqdm.write(f"Runtime:{Fore.GREEN}{runTime}{Style.RESET_ALL}")
# print('[第%d轮, 训练%5d个样本] loss : %.3f' % (epoch +1, (i+1)*train_batch_size,running_loss /2000))
tqdm.write(
f"第{Fore.YELLOW}{epoch + 1}{Style.RESET_ALL}轮,训练{Fore.YELLOW}{(i + 1) * train_batch_size}{Style.RESET_ALL}个样本, loss: {Fore.CYAN}{running_loss / 2000}{Style.RESET_ALL}")
# 以时分秒的形式打印下程序运行时长
# print('Runtime',time.strftime("%H:%M:%S",time.gmtime(time.time()-start_time)))
# print('Runtime',time.strftime("%H小时%M分%S秒",time.gmtime(time.time()-start_time)))
# if running_loss/2000 < min_loss:
# print('打破记录')
# min_loss = running_loss/2000
# PATH = midModelPath
# # PATH = './cifar_net67.pth'
# # #保存模型
# torch.save(net.state_dict(),PATH)
# verify()
torch.save(net.state_dict(), midModelPath)
verify()
global break_max_val
if (break_max_val):
tqdm.write(
# f"{Back.LIGHTBLUE_EX} {Fore.LIGHTRED_EX}🔥 打破记录了!!!{Back.RESET}{Style.RESET_ALL}")
f"{Fore.LIGHTRED_EX}🔥 打破记录了!!!{Style.RESET_ALL}")
mytestTrainSet()
eval()
# 刷新输出缓冲区,作用是确保输出信息立即显示在控制台上,不然在云服务器上半天显示不出来
sys.stdout.flush()
running_loss = 0.0
print("")
epoch_list.append(epoch + 1)
val_acc_list.append(every_val_acc)
if (epoch + 1) % 5 == 0:
draw(epoch_list, val_acc_list)
print('完成训练')
#
# PATH = './cifar_net68.pth'
PATH = finallPATH
# # # # #保存模型
torch.save(net.state_dict(),PATH)
eval()
#
#在整个测试集上测试模型的准确率,53%,比之前55%还降了两个百分点。加了一层,正确率变成了46%,妹的,打算训练十次,看看,训练了十次,正确率变成39%,奶奶个熊
#直接把学习率改成0.0001,训练了十次,损失变为1.163,降了很多。突然正确率又提升到了56%
#决定把卷积层又恢复成两层,看看效果。因为batch给了9,一下子就训练完了,loss值为1.455,正确率为48%,效果不佳,
#加层数反而效果不好,可能加的不够好吧,然后把训练十次,改为30次,试试,第12轮的时候,损失为1.087,感觉正确率一定会超过56%,再训练第30轮的时候,有一次损失才0.793
#最终的损失是0.854,有点回升了。最终正确率为63%,如果在最低的损失值时,保存模型,那么正确率应该会更高。
#发现卷积层和全连接层没有必然联系,所以去掉了一个全连接层,试试效果。第12轮的时候,损失为1.027,但是很可惜,没有保存模型。
# 打算训练100轮,试试效果,这就好比把卷子做50遍,效果可能不太好,那就做100遍,相信效果会好很多。迭代100次太慢了,第42抡,有一次损失是0.727,也不确定会不会降低到0.6以下。
# 回家路上没有关电脑,不然又要重新训练。电脑烫死了,在59轮时,有一次损失是0.642。感觉100轮下来,还真有可能降低到0.6以下。70论的时候,有一次损失是0.616.第82轮的时候,
# 有一次降到了0.581,可惜因为之前程序写的不对,没法保存那个模型。最终的损失是0.697.正确率为61%,好气人,竟然降了。然后在训练集上跑了下,正确率为78%。不是很高,也就是说
#给它开卷考试,满分100分,也只能得到78分,说明学的还是不够。
#准备采用VGG网络架构,第一层卷积层直接从6个卷积核提升到64个卷积核,第二层也是64个卷积核,第三层128个卷积核,第四层也是128个卷积核。准备训练10轮,看看效果
# 在第九轮的时候,直接损失降低到0.560,这可是之前从来没有过的好成绩,说明还是得卷积盒多一些才可以。但是因为程序没写好,被多次覆盖掉。第十轮有一次损失是0.482
# 最终损失是0.565.在训练集上的正确率是84%,显著提升,在测试集上的正确率是73%,很厉害了
# 现在直接改造成了10层卷积层,哈哈,按照VGG的前十层架构,进行训练,然后把池化层去掉了,因为池化层经过三轮之后,变成2*2大小了,用3*3的卷积核没法计算了
# 网络层数加深之后,明显计算太慢了。下班也训练不完,打算训练五轮试试,看看一个小时能不能训练完,发现层数太深了,根本就训练不动,可能是由于链式法则,乘的数太多了,
# 导致每次只往前走一丁点,所以,可以考虑把学习率提高。
# 还是用三层架构,训练到第13抡,损失为0.019.我的天呐,牛啊,训练到第16抡,有一次损失为0.015,牛逼。训练到第18轮,有一次损失为0.006,不断降低损失,刷新记录。
# 在第29轮的时候,有一次损失为0.001.太吊了吧。在测试集上的正确率是69%,在训练集上的正确率是99%,很棒,说明是过拟合了。
#准备加上正则化,看看效果。计划训练30轮,在服务器上训练。
# 加上正则化后,在测试集上正确率是68%,在训练集上正确率是97%,我的天呐,还是解决不了过拟合的问题。
# 打算把正则化提升点,试试效果,加入正则化之后,在第17轮的时候,损失降到0.012.在训练集上的正确率是99%,在测试集上的正确率是69%,并且测试了两个模型,
# 还包括一个损失为0.010的,测试集上正确率也是69%,说明还是过拟合了。决定再次把正则化的系数从0.001,改为0.01,看看效果。
# 修改成0.01之后,训练了20轮,用时15个小时,最终损失为0.761,降不下去,然后在训练集上正确率为81%,在测试集上正确率是67%,反而效果还差了,
# 那就又将正则化惩罚项系数改为0.005,看看效果。在第14轮的时候,损失降到0.208.到时候看看结果,如果结果不理想,就继续把系数往下降。
# 第15轮的时候降到了0.188,看来有机会低于0.1啊。
# 最终损失是0.303,在测试集上正确率是66%,在训练集上正确率是96%。
# 打算把正则化惩罚项系数改为0.003,看看效果,最低的损失是0.058,在测试集上的正确率是66%,在训练集上的正确率是96%。过拟合问题太难搞了
# 加入drop-out,试试效果,给三个卷积层都加入了dropout
# dropout比例设置为0.5,损失到1.8附近,就不往下降了。调到0.1试试
# 不知道咋回事,加入dropout效果并不好,训练了三十轮,损失降不下去,也不知道是不是因为学习率为0.001的缘故,
# 现在把dropout去掉,加入数据增加,平移,反转,遮盖,来试试效果。
# 不知道咋回事,训练了30抡,损失是0.542,也太差了吧。在训练集上精度是80%,在测试集上精度是75%,竟然不过拟合了,不过损失为什么降不下去
# 打算再加一层卷积层,一共四个卷积层,试试,最终损失0.482,在训练集上的精度是83%,在测试集上的精度是79%,哇塞,有提升,终于快接近80%了。
# 打算再加一层,加到五个卷积层,这样,应该就可以上80%了
# 五层卷积层,最终损失为0.470,在训练集上的精度是84%,在测试集上的精度是80%,哇偶,终于上80%了
# 五层卷积层,最佳损失为0.422,在训练集上的精度是83%,在测试集上的精度是80%
# 打算第二层卷积层后面加个池化层,在五层卷积层后面也加个池化层,看看效果。
# 加入池化层之后,第六轮22000样本,只用了1小时15分钟,没加池化层之前用了1小时17分钟。我以为训练速度会加快的,并没有
# 训练完30轮,最终只用了7小时4分钟,不加池化层,用了6小时58分钟,我咋记得得二十几个小时呢。训练的模型只需要4.04M,之前都是40M-58M这样
# 加入两个池化层之后,最终损失是0.512,在训练集上的精度是84%,在测试集上的精度是80%
# 加入两个池化层之后,最佳损失是0.423,在训练集上的精度是84%,在测试集上的精度是80%
# 看来加入池化层,对于提高精度作用不大,那就继续增加网络层数吧。
# 再加上六七两个卷积层,但是加上这两层之后,训练不动了,损失不往下降,试着把学习率从0.0001改为0.00001试试
# 好像还是不行,那就把学习率提高为0.001,试试,后来发现能训练得动,比较慢而已
# 再加两个卷积层之后,最终损失是0.461,在训练集上的精度是84%,在测试集上的精度是80%。30轮训练用时12个小时,之前也就7个小时左右,因为网络更复杂了
# 也不知道是不是因为30轮,还不够,打算再训练20轮,看看效果,看看损失能不能降到0.4以下
# 在进行到第43轮的时候,损失为0.334,在训练集上的精度是87%,在测试集上的精度是82%,哇塞,果然被自己猜对了,我就是加了两层网络,怎么可能精度没有变化呢。
# 最佳损失为0.298,在训练集上的精度是89%,在测试集上的精度是83%,哇偶,开心,果然增加模型复杂度,可以提升精度
# 最终损失为0.371,在训练集上的精度是88%,在测试集上的精度82%
# 决定再加上三层卷积层,3个512特征图,然后训练轮数从50轮提升到70轮,看看效果。并将学习率从0.0001提高到0.0005,避免训练不动
# 将学习率提升到0.5,训练了一个晚上,损失还是3.7,根本不往下降。
# 问了下ai,这种情况应该是属于梯度消失了,可以采用批归一化,试试
# 哇塞,果然是梯度消失了,给新加的三个卷积层加入批归一化之后,采用0.0001的学习率,明显可以训练得动了。
# 训练了70轮,用了四五天,第一次花这么长时间用来训练,耗时为什么这么长,主要是因为模型更复杂了,而且batch_size给的太小了
# 最佳损失0.404,在训练集上的精度是85%,在测试集上的精度是80%
# 最终损失0.504,在训练集上的精度是84%,在测试集上的精度是79%
# 加了三层卷积层之后,竟然效果还没有之前好,有点欠拟合。但是我发现好像增加训练轮数,也训练不动了。
# 弄上阿里云的gpu之后,再训练30轮试试,毕竟用上了GPU,一轮也就6分钟,30轮,也就3小时左右,
# 在GPU上训练可太快了,而且是英伟达A10的GPU,简直狂飙啊。一轮只用了3分钟,比昨天自己用的那个GPU快多了
# 而且因为自己的batch_size设置的是1,而且num_workers设置的是0,不然会更快,我的天!
# 不知道咋搞的,设置了30轮,但是只训练了15轮
# 最佳损失是0.341,在训练集上的精度是88%,在测试集上的精度是82%
# 最终损失是0.412,在训练集上的精度是88%,在测试集上的精度是81%
# 又训练了十轮
# 最佳损失是0.270,在训练集上的精度是%,在测试集上的精度是81%
# 最终损失是0.309,在训练集上的精度是%,在测试集上的精度是82%
# 还有一点,我觉得最后的全连接,从8192直接到120,是不是120给的太小了,可以给到1000?
# 发现这里面没有padding层,可以考虑加个padding层,看看效果。
# 给每个卷积层增加了padding=1,这样卷积过程中尺寸不变,然后弄了三个池化层,最终的特征图尺寸是4*4*512,
# 增加padding了之后,扫描用的时间可能会多一些。
# 效果竟然出奇的好,在24轮的时候,损失为0.254,在训练集上的精度是90%,在测试集上的精度是85%
# 最终损失0.138,在训练集上的精度是95%,在测试集上的精度是87%
# 最佳损失为0.111,在训练集上的精度是95%,在测试集上的精度是87%
#打算把第二个全连接层参数从120,换成800,然后把num_workers从0换成2,试试效果
# 在第44轮的时候,损失为0.113,在训练集上的精度是95%,在测试集上的精度是88%
# 我的天,太炸裂了,又提升了一个百分点,而且这次因为不急着回去,可以训练70轮,我觉得最终还会涨一点点
# 最佳损失0.067,在训练集上的精度是%,在测试集上的精度是87%
# 最终损失0.089,在训练集上的精度是97%,在测试集上的精度是89%,这个就训练的很充分了
# 我的天,也太牛逼了,果然又提升了一个百分点,哈哈,这真是可喜可贺啊
# 然后决定再加上三层的512特征图,这样就是完整的VGG16了,看看还能不能提高一两个百分点。冲90%有希望了呀。
# 训练了46轮,损失是0.154,因为要下班了,所以就暂停,明天继续训练,然后明天再训练30轮,我觉得会破记录的。
# 最佳损失0.087,在训练集上的精度是96%,在测试集上的精度是87%
# 最终损失0.109,在训练集上的精度是%,在测试集上的精度是87%
# 我觉得可能是训练不够充分吧,不然不可能还不如从前了,要么就是过拟合了,现在有两种办法,第一,增加20轮训练,第二,增加dropout。
# 又训练了20轮
# 最佳损失0.063,在训练集上的精度是%,在测试集上的精度是88%
# 最终损失XX,在训练集上的精度是96%,在测试集上的精度是88%
# 打算增加dropout,看看效果,之前可能dropout加的不对
# 在第一个全连接层,和第二个全连接层,增加dropout,比例为0.2
# 训练40轮,试试,之前那个GPU-DSW打不开了,重新弄了个,下载数据集好快啊
# 后面可以考虑把batch_size调大一些,这样训练更快
# 训练了40轮,最佳损失为0.212,在训练集上的精度是90%,在测试集上的精度是84%
# 最终损失0.260,在训练集上的精度是89%,在测试集上的精度是83%
# 好像泛化能力比之前能好一点,从8-9个点,减少到6个点,
# 因为今天周六,现在已经晚上八点,训练不了多久,再来十轮,把batch_size设置为10
# 把batch_size设置为10,现在训练速度更快了,太厉害了
# 既然训练速度这么快,那就干脆再训练20轮,反正20轮,20分钟就完了,原本每轮得4分钟,需要80分钟,
# 哈哈,自己太笨了,不然早点把batch_size调大了。
# 再经过了20轮,相当于总共训练了70轮
# 最佳损失是0.109,在训练集上的精度是96%,在测试集上的精度是88%
# 最终损失是0.115,在训练集上的精度是%,在测试集上的精度是88%
# 再训练20轮,相当于训练了90轮
# 最佳损失是0.092,在训练集上的精度是96%,在测试集上的精度是88%
# 最终损失是0.096,在训练集上的精度是%,在测试集上的精度是%
# 明天打算把dropout丢弃比例设置为0.5,训练100轮试试看
# 训练了100轮,只用了1小时14分钟,果然batch_size作用很大啊
# 但是大的batch_size可能需要更多的轮数。因为反向传播的次数相对比较少
# 最终损失0.159,最佳损失0.149。可能是因为drop-out0.5,所以损失有点大.
# 打算再训练50轮,看看效果
# 最佳损失0.088,在训练集上的精度是97%,在测试集上的精度是87%
# 我晕死,我感觉这个drop-out没有发挥作用啊。
# 那不要drop-out了,继续搞正则化试试,反正就是炼丹嘛,哈哈
# 先把正则化惩罚项弄个0.001试试。
# 直接训练150轮,试试
# 最佳损失为0.091,在训练集上的精度是96%,在测试集上的精度是87%
# 真是一个让人失望的结果啊,明天把正则化系数提升下,训练150轮试试
# 将正则化系数设置为0.01,训练150轮
# 训练了150轮,最终损失为0.351,在训练集上的精度是89%,在测试集上的精度是84%
# 再训练上100轮,看看效果,损失不太好往下降。
# 又训练了50轮,损失还是0.345,降不下去了。
# 打算把正则化惩罚项系数调整为0.005,训练200轮试试
# 最终损失为0.200,在训练集上的精度是93%,在测试集上的精度是85%
# 最佳损失为0.176,在训练集上的精度是94%,在测试集上的精度是86%
# 效果不明显啊,打算再训练100轮试试,感觉还是没有解决过拟合的问题
# 又训练了40轮,损失为0.167,在训练集上的精度是%,在测试集上的精度是86%
# 训练到100轮,最终损失为0.173,没有太大变化,在测试集上的精度是85%
# 直接把正则化惩罚项系数去掉,然后直接用一个预训练的ResNet18来训练下,看看效果
# 这一次pretrained=False,是一个空的模型,需要自己训练,下次可以直接pretrained=True,用一下它训练好的参数,试试
# 训练了100轮,损失降到0.60左右,打算再训练100轮试试
# 又训练了150轮,共250轮,最终损失为0.355,那就再训练150轮。
# 为什么要训练那么多轮数,是因为真的Resnet18比自己之前的网络要更复杂。
# 共训练了400轮,损失为0.233,在训练集上的精度是91%,在测试集上的精度是81%
# 再训练100轮吧。
# 这个ResNet18收敛速度太慢了,
# 共训练了500轮,最佳损失为0.193,模型保存文件为64
# 打算继续训练下去,但是现在没时间了,只能明天,打算试一下预训练模型,看看效果
# 把卷积层的参数冻结了,只训练全连接层的参数,看看效果
# 共训练了700轮,最终损失为0.189,在测试集上的精度是81%
# 最佳损失为0.167,在训练集上的精度是%,在测试集上的精度是82%
# 原来这200轮训练的只是全连接层的参数,难怪损失降不下去,重新训练200轮吧。
# 第648轮的时候,损失为0.145,在训练集上的精度是94%,在测试集上的精度是81%
# 严重过拟合,干脆先不用ResNet了,先用自己搭建的VGG试试,给每层加上批归一化,说不定可以上90%
# 第84轮,损失为0.111,在训练集上的精度是95%,在测试集上的精度是87%
# 第116轮,损失为0.076,在训练集上的精度是97%,在测试集上的精度是88%
# 第176轮,损失为0.049,在训练集上的精度是98%,在测试集上的精度是88%
# 最终损失0.050,在测试集上的精度是89%
# 最佳损失0.044,在训练集上的精度是98%,在测试集上的精度是89%
# 又训练了60轮,最佳损失为0.031,最终损失为0.038,在测试集上的精度都是89%
# 又训练了40轮,最佳损失为0.029,在训练集上的精度是98%,在测试集上的精度是90%,明天看看能不能把损失降到0.01以下
# 我的天,上了90%,对得起自己熬夜到了23:26,哈哈。
# 再训练100轮试试,看看能降到0.01以下不。
# 第54轮,损失为0.024,在训练集上的精度是99%,在测试集上的精度是90%,稳稳妥妥的上了90%,看来批归一化效果还是很明显的,之前一直上不来
# 最佳损失0.021,不训练了,换成Adam试试
# 明天可以考虑把SGD换成Adam试试,然后也可以在程序中加入在测试集上的精度,随时打印输出
# 还有一个AdamW,是Adam的变种,也可以试试
# 换成Adam,然后加入了验证集,训练200轮试试。
# 带上验证集还挺好的,可以损失监督模型在验证集上的精度
# 训练了90轮,损失为0.096,在验证集上的精度是88%
# 明天继续训练,再来200轮,看看会不会上91%
# 我明白验证集昨天好好的,但是今天突然就很高,就是因为训练集和验证集是随便划分的,今天的验证集中的数据,昨天是出现在训练集里的,所以拟合程度会更高一些。
# 第261轮,损失为0.042,在训练集上的精度是98%,在测试集上的精度是90%
# 第300轮,损失为0.047,在测试集上的精度是90%
# 第414轮,损失为0.035,在训练集上的精度是98%,在测试集上的精度是90%
# 第459轮,损失为0.034,在测试集上的精度是90%,损失好像也降不下去了。
# 自从换成彩色的日志之后,训练集的精度小于验证集的精度,这个不合理,可能还是程序写的有问题。
# 训练到后面,训练集的精度就高于验证集和测试集的了
# 把网络结构搞简单一点,说不定泛化能力更强呢。去掉后面三个512的卷积层,试试效果
# 在第123轮时,损失为0.075,在验证集上的精度是90.8%,在训练集上的精度是97.7%,在测试集上的精度是89.54%
# 在第154轮时,损失为0.064,在验证集上的精度是90.94%,在训练集上的精度是97.97%,在测试集上的精度是90.12%
# 在第178轮时,损失为0.060,在验证集上的精度是91.14%,在训练集上的精度是98.24%,在测试集上的精度是90.21%
# 使用预训练ResNet18,加入了验证集,以及验证集精度曲线图
# 训练到82轮时,损失是为0.285,在验证集上的精度是85.02%,在训练集上的精度是90.92%,在测试集上的精度是84.22%
# 训练到106轮时,损失为0.2314,在验证集上的精度是85.28%,在训练集上的精度是91.79%,在测试集上的精度是83.95%
# 直接改用ResNet34试下,因为我感觉ResNet18泛化能力挺强的,但是有点欠拟合,因此要选择更复杂一点的网络结构
# 使用ResNet50训练
# 训练到37轮时,损失为0.3688,在验证集上的精度是84.96%,在训练集上的精度是88.33%,在测试集上的精度是84.13%
# 训练到69轮时,损失为0.265,在验证集上的精度是87.18%,在训练集上的精度是89.01%,在测试集上的精度是84.04%
# 训练到101轮时,损失为0.198,在验证集上的精度是87.44%,在训练集上的精度是93.75%,在测试集上的精度是86.28%
# 训练到119轮时,损失为0.174,在验证集上的精度是87.5%,在训练集上的精度是94.51%,在测试集上的精度是86.54%
# 训练到134轮时,损失为0.1638,在验证集上的精度是87.68%,在训练集上的精度是94.88%,在测试集上的精度是86.00%
# 训练到143轮时,损失为0.1514,在验证集上的精度是87.9%,在训练集上的精度是95.25%,在测试集上的精度是86.83%
# 训练到156轮时,损失为0.1493,在验证集上的精度是87.96%,在训练集上的精度是95.41%,在测试集上的精度是86.41%
# 再继续训练了109轮,损失降为0.104,不过在验证集上的精度是87.4%,不再往下降了
# 我发现用预训练的效果反而不好,那我就直接只用ResNet50网络结构,而不用它的预训练参数
# 第120轮时,损失为0.517,在验证集上的精度是80.16%,在训练集上的精度是82.43%,在测试集上的精度是78.76%
# 第200轮时,损失为0.420,在验证集上的精度是81.84%,在训练集上的精度是86.39%,在测试集上的精度是80.4%
# 使用ResNet101预训练模型,并且冻结前面的参数,只更新全连接层
# 训练到100多轮,其中在验证集上的精度达到46%,基本上就上不去了。
# 二供上云弄了云服务器,有英伟达V100的显卡,训练了2小时35分钟,第174轮的时候,在验证集上89.9%,训练集上98.49%,在测试集上88.08%
# 后面还可以加上ResNet,据说ResNet在ImageNet比赛的时候,错误率只有3%,好恐怖
def modelInfo():
params_num = sum(p.numel() for p in net.parameters());
print("当前模型共有%d个参数",params_num)
def verify():
PATH = midModelPath
net.load_state_dict(torch.load(PATH, map_location=device,weights_only=True))
correct = 0
total = 0
# 验证的时候,不需要记录梯度,加快速度
with torch.no_grad():
for data in valloader:
images, labels = data
outputs = net(images.to(device))
# _, predicted = torch.max(outputs.data.to('cpu'), 1)
_, predicted = torch.max(outputs.data.to('cpu'), 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
# print('验证集正确率:%d %%' % (100 * correct / total))
tqdm.write(
f"验证集正确率: {Fore.BLUE}{100 * correct / total}{Style.RESET_ALL}%")
global max_val_score
global break_max_val
global every_val_acc
every_val_acc = np.mean(correct / total)
if(correct/total > max_val_score):
max_val_score = correct/total
break_max_val = True
else:
break_max_val = False
def mytestTrainSet():
PATH = midModelPath
net.load_state_dict(torch.load(PATH, map_location=device,weights_only=True))
correct = 0
total = 0
# 验证的时候,不需要记录梯度,加快速度
with torch.no_grad():
for data in trainloader:
images, labels = data
outputs = net(images.to(device))
_, predicted = torch.max(outputs.data.to(device), 1)
total += labels.size(0)
correct += (predicted == labels.to(device)).sum().item()
tqdm.write(
f"训练集正确率: {Fore.BLUE}{round(100 * correct / total,2)}{Style.RESET_ALL}%")
def eval():
# net = Net().to(device)
# print(net)
# net = Net()
# # #加载模型
PATH = midModelPath
# PATH = finallPATH
net.load_state_dict(torch.load(PATH,map_location=device,weights_only=True))
# net.load_state_dict(torch.load(PATH))
# for param in net.parameters():
# print(param)
correct = 0
total = 0
# 测试的时候,不需要记录梯度,加快速度
with torch.no_grad():
for data in testloader:
# for data in trainloader:
images,labels = data
outputs = net(images.to(device))
_,predicted = torch.max(outputs.data.to(device),1)
total += labels.size(0)
correct += (predicted == labels.to(device)).sum().item()
# print('正确率:%d %%'% (100*correct /total))
tqdm.write(
f"测试集正确率: {Fore.BLUE}{round(100 * correct / total,2)}{Style.RESET_ALL}%")
def draw(epoch_list,val_acc_list):
plt.plot(epoch_list, val_acc_list)
plt.xlabel("epoch")
plt.ylabel("val_acc")
plt.show()
if __name__ == '__main__':
# eval();
# verify()
train(200);
# trainResNet(300);
# modelInfo();