使用 SVHN 的数据集进行模型的训练,但是整个模型在训练集上的准确率是一直在上升的,但是到了测试集上就一直卡在 95%,都 3 天了,求各位大佬帮我看下有没有优化的方案!跪谢!
import torch import torch.nn as nn import torch.optim as optim from torch.distributed.checkpoint import load_state_dict from torch.hub import load_state_dict_from_url from torch.nn.modules.loss import _Loss from torch.optim import Optimizer from torch.utils.data import Dataset, random_split, DataLoader import torchvision from torchvision.transforms import transforms import torchvision.models as m import matplotlib.pyplot as plt import random import gc # 用于垃圾回收 from torchinfo import summary import numpy as np import random import gc # 设置随机数种子 SEED = 420 random.seed(SEED) np.random.seed(SEED) torch.manual_seed(SEED) torch.cuda.manual_seed(SEED) torch.cuda.manual_seed_all(SEED) torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False # 设置使用 gpu 还是 cpu 进行训练 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 定义训练的论述 epochs = 100 lr = 0.0001 # 定义数据集需要的参数 batchSize = 64 # 加载训练集需要的数据转换器 trainT = transforms.Compose([ transforms.RandomCrop(28), transforms.RandomRotation(degrees=[-15, 15]), transforms.ToTensor(), transforms.Normalize(mean = [0.4377, 0.4438, 0.4728], std = [0.1980, 0.2010, 0.1970]) ]) # 加载测试集需要的数据转换器 testT = transforms.Compose([ transforms.CenterCrop(28), transforms.ToTensor(), transforms.Normalize(mean = [0.4377, 0.4438, 0.4728], std = [0.1980, 0.2010, 0.1970]) ]) # 加载训练集数据 svhn_train = torchvision.datasets.SVHN(root='C:\\FashionMNIST' , split="train" , download=True , transform=trainT ) # 加载测试集数据 svhn_test = torchvision.datasets.SVHN(root='C:\\FashionMNIST' , split="test" , download=True , transform=testT ) # 定义神经网络,因为我们的图片的尺寸和样本数量都不是很大,所以选择从 ResNet18 和 Vgg16 中抽取层来构建网络 resnet18_ = m.resnet18() class MyResNet(nn.Module): # 这个是基于 ResNet18 构建的网络 def __init__(self): super(MyResNet, self).__init__() self.block1 = nn.Sequential( nn.Conv2d(3, 64, 3, 1, 1), resnet18_.bn1, resnet18_.relu ) self.block2 = resnet18_.layer2 # 连权重都会复用过来,在 resnet18_ = m.resnet18() 这儿就已经初始化好了权重数据! self.block3 = resnet18_.layer3 self.block4 = resnet18_.layer4 # 从 Resnet18 中哪 layer 新增到自己的模型中 self.avgpool = resnet18_.avgpool self.fc = nn.Linear(512, 10, True) def forward(self, x): x = self.block1(x) x = self.block2(x) x = self.block3(x) x = self.block4(x) # 这儿新增一条处理代码 x = self.avgpool(x) x = x.view(-1, 512) return self.fc(x) vgg16_ = m.vgg16() class MyVgg(nn.Module): # 这个是基于 Vgg16 构建的网络 def __init__(self): super(MyVgg, self).__init__() self.features = nn.Sequential( *vgg16_.features[0:9], # 使用*是 将 .features[0:9]提取出来的层,全部取出来,一个个放到当前的 Sequential 中,而不是组成一个 Sequential 放到当前的 Sequential 中! nn.Conv2d(128, 128, 3, 1, 1), nn.ReLU(inplace=True), nn.MaxPool2d(2, 2, padding=0, dilation=1, ceil_mode=False) ) self.avgpool = vgg16_.avgpool self.fc = nn.Sequential( nn.Linear(6272, 4096, True), *vgg16_.classifier[1:6], nn.Linear(4096, 10, True) ) def forward(self, x): x = self.features(x) x = self.avgpool(x) x = x.view(-1, 6272) x = self.fc(x) return x # summary(MyVgg(), input_size=(10, 3, 28, 28)) # 一定要,实例化跑一下,看看有没有问题! class earlyStopping(): def __init__(self, patience=5, tol=0.0005): # 当连续 patience=5 次,本轮次的迭代的损失与历史最小的损失的差值大于 0.0005 这个阈值,就会停止训练 self.patience = patience self.tol = tol self.counter = 0 # 计数器 self.lowest_loss = None # 记录历史最小损失 self.early_stop = False # 需要返回是否需要提前停止 def __call__(self, val_loss): # val_loss 是记录测试集或训练集上一次 epoch 的损失 if self.lowest_loss is None: self.lowest_loss = val_loss elif self.lowest_loss - val_loss > self.tol: self.lowest_loss = val_loss self.counter = 0 elif self.lowest_loss - val_loss < self.tol: self.counter += 1 print('Notice: Early stopping counter {} of {}'.format(self.counter, self.patience)) if self.counter >= self.patience: print('Notice: Early stopping counter Active') self.early_stop = True return self.early_stop # 定义训练函数 def fit(net: nn.Module, lossFunc: _Loss, op: Optimizer, trainData: DataLoader, testData: DataLoader, epochs: int): transLost = [] # 用于收集每轮训练和测试的结果,用于后面画图表使用 trainCorrect = [] testLost = [] testCorrect = [] trainedSampleNum = 0 # 初始化 earlystopping 类 early_stopping = earlyStopping(patience=15, tol=0.00000005) # 初始化测试集的历史最高准确率 test_highest_correct = None test_lowest_loss = None # 获取到整个训练集中的样本数量 trainTotalNum = trainData.dataset.__len__() # 获取到整个测试集中的样本数量 testTotalNum = testData.dataset.__len__() for epoch in range(epochs): net.train() train_loss = 0 train_correct = 0 for batch_index, (data, target) in enumerate(trainData): data = data.to(device, non_blocking=True) target = target.to(device, non_blocking=True).view(data.shape[0]) # 确保标签是 1 维的结构 trainRes = net(data) # 经过学习,这儿每个样本会输出 10 个特征结果对应的数据(如果模型中有 softmax ,就是概率),可以用于后续计算准确率 loss = lossFunc(trainRes, target) op.zero_grad() # 清空优化器上的梯度 loss.backward() op.step() # 开始计算准确数,并累加 yhat = torch.max(trainRes, 1)[1] # 从 trainRes 一个矩阵中,取出每个样本的最大值和最大值所在的索引,得到[1,2,1,4]这种类型的结果 correct_num = torch.sum( yhat == target) # yhat 、target 都是一维张量,使用 == 会挨个对比张量中的元素是否相等,最终得到[False, True, Flase]这样的数据,然后使用 torch.sum 就可以得到一个数字,因为 True 为 1 ,False 为 0 。 train_correct += correct_num # 将准备数累加 # 计算损失,并累加 train_loss += loss.item() # 这儿需要得到所有样本的损失的和 trainedSampleNum += data.shape[0] # print("本批次训练损失为:", loss.item() / data.shape[0]) if (batch_index + 1) % 125 == 0: # 现在进行到了哪个 epoch 、总共要训练多少个样本、已经训练了多少个样本、已训练的样本的百分比 print("Epoch{}:{} / {} = ({:.0f}%)".format( epoch + 1, trainedSampleNum, epochs * len(trainData) * batchSize, 100 * trainedSampleNum / (epochs * len(trainData) * batchSize) )) print("-------------------------------") avg_correct = (float(train_correct) / trainTotalNum) * 100 # print("本轮训练平均准确率:", avg_correct) trainCorrect.append(avg_correct) avg_loss = (float(train_loss) / trainTotalNum) * 100 # print("本轮训练平均损失率:", avg_loss) transLost.append(avg_loss) del data, target, train_loss, train_correct gc.collect() torch.cuda.empty_cache() # 一轮训练结束,就使用测试集进行测试 net.eval() test_loss = 0 test_correct = 0 for batch_index, (test_data, test_target) in enumerate(testData): with torch.no_grad(): test_data = test_data.to(device, non_blocking=True) test_target = test_target.to(device, non_blocking=True).view(test_data.shape[0]) # 确保标签是 1 维的结构 testRes = net(test_data) loss = lossFunc(testRes, test_target) # 计算损失,并累加 test_loss += loss.item() # 计算准备数,并累加 yhat = torch.max(testRes, 1)[1] # 从 trainRes 一个矩阵中,取出每个样本的最大值和最大值所在的索引,得到[1,2,1,4]这种类型的结果 correct_num = torch.sum( yhat == test_target) # yhat 、target 都是一维张量,使用 == 会挨个对比张量中的元素是否相等,最终得到[False, True, Flase]这样的数据,然后使用 torch.sum 就可以得到一个数字,因为 True 为 1 ,False 为 0 。 test_correct += correct_num # 将准备数累加 avg_test_correct = (float(test_correct) / testTotalNum) * 100 # print("本轮测试平均准确率:", avg_test_correct) testCorrect.append(avg_test_correct) avg_test_loss = (float(test_loss) / testTotalNum) * 100 # print("本轮测试平均损失率:", avg_test_loss) testLost.append(avg_test_loss) print("本轮训练平均准确率:{}, 本轮训练平均损失率: {}, 本轮测试平均准确率:{}, 本轮测试平均损失率:{}".format( avg_correct, avg_loss, avg_test_correct, avg_test_loss)) del test_data, test_target, test_loss, test_correct gc.collect() torch.cuda.empty_cache() # 如果测试集损失出现新低或者准确率出现新高,就保存在模型的权重,防止中途断电等原因需要从头再来 if test_highest_correct is None: test_highest_correct = avg_test_correct if test_highest_correct < avg_test_correct: test_highest_correct = avg_test_correct torch.save(net.state_dict(), './v6/model-' + str(epoch + 1) + '.pth') print("model saved") # 最好在测试集上使用提前停止,如果使用训练集无法预测过拟合这种情况 early_stop = early_stopping(avg_test_loss) # 这儿使用提前停止! if early_stop: break print("mission completed") return transLost, trainCorrect, testLost, testCorrect model = MyResNet().to(device) # model.load_state_dict(torch.load("./v4/model-49.pth")) loss_func = nn.CrossEntropyLoss(reduction='sum') # 因为我们在训练函数中,在计算损失的时候是计算的每个样本的损失的和,所以这儿需要使用 reduction='sum' opt = optim.RMSprop(model.parameters(), lr=lr, weight_decay=0.00005, momentum=0.0001) train_data = DataLoader(svhn_train, batch_size=batchSize, shuffle=True, drop_last=False, pin_memory=True) test_data = DataLoader(svhn_test, batch_size=batchSize, shuffle=False, drop_last=False, pin_memory=True) # 开始训练 transLost, trainCorrect, testLost, testCorrect = fit(model, loss_func, opt, train_data, test_data, epochs) # 训练结果可视化 plt.plot(transLost, label='train loss') plt.plot(testLost, label='test loss') plt.plot(trainCorrect, label='train correcct') plt.plot(testCorrect, label='test correcct') plt.xlabel('Epoch') plt.ylabel('CrossEntropy Loss') plt.title('Training Loss') plt.legend() plt.grid(True) plt.show()
1 0x636a 93 天前 1.看看测试集是哪几张图不对,是不是异常图。 2.试试正则化手段 dropout ,l1 l2 损失,预先训练权重 不过为啥非要追求 100%呢 |
![]() | 2 < href="/member/moxiaowei" class="dark">moxiaowei OP @0x636a 没有非要 100%,只是有人训练到了 96%以上,我的不行,我很纳闷 |
4 faoisdjioga 93 天前 要不要加个 Attention 试试呢?简单的加一个加权 Attention layer 就行,或者加一个 transformer encoder layer 呢? 我之前在做 NLP 时候遇到类似的瓶颈,加上 Attention 会好一些 |
5 hwdq0012 93 天前 100%一般是过拟合了 训练和测试数据集一样比较容易达到 100% 这个数据集只是 0-9 的分类,基本不存在 label 错误的情况, 否则还有可能是 label 标记错了 如果不是 label 错了,可以把测试失败的数据 多复制几份,再训练 |
![]() | 6 opeth 93 天前 1. data augmentation 太少了,用 albumentations 加大数据增广的类别 2. 模型太小了,换 resnet50 试试 3. 关掉 early stop ,多跑几个 epochs |
![]() | 7 opeth 93 天前 又看了一下,为啥优化器要用 RMSprop ,不用 Adam ? 学习率下降曲线可以改成 cosine batchsize 还能开更大一点吗? |
![]() | 9 opeth 93 天前 loss 函数也可以改成更强的分类 loss |
![]() | 12 Sawyerhou 93 天前 这 1%的准确率没有也无所谓吧,还是说你在打比赛要拿名次? |
![]() | 13 parad 93 天前 via Android 这是你手写的还是 AI 写的呀? AI 写 ML 相关的代码还是不太行。建议看看相关数据&模型的教程,经典 paper 和 baseline 例子参考。 |
![]() | 15 moxiaowei OP 破案了,训练到 96%以上的那个 S..B 用训练集做的测试,感觉到 95%将近 96%已经是极限了,用 Adam 优化器也差不多 |