模型在测试集上的准确率卡在 95%上不去,能想的办法都试过了就是不行,都 3 天了,求各位大佬帮我看下有没有优化的方案!跪谢! - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
moxiaowei
V2EX    Python

模型在测试集上的准确率卡在 95%上不去,能想的办法都试过了就是不行,都 3 天了,求各位大佬帮我看下有没有优化的方案!跪谢!

  •  
  •   moxiaowei 93 天前 2352 次点击
    这是一个创建于 93 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用 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()
    16 条回复    2025-07-08 21:24:09 +08:00
    0x636a
        1
    0x636a  
       93 天前
    1.看看测试集是哪几张图不对,是不是异常图。
    2.试试正则化手段 dropout ,l1 l2 损失,预先训练权重
    不过为啥非要追求 100%呢
    moxiaowei
        2
    < href="/member/moxiaowei" class="dark">moxiaowei  
    OP
       93 天前
    @0x636a 没有非要 100%,只是有人训练到了 96%以上,我的不行,我很纳闷
    0x636a
        3
    0x636a  
       93 天前
    @moxiaowei 如果说是同样的代码的话,你试试其他随机数,每个机器上相同随机数表现应该不同的。
    faoisdjioga
        4
    faoisdjioga  
       93 天前
    要不要加个 Attention 试试呢?简单的加一个加权 Attention layer 就行,或者加一个 transformer encoder layer 呢? 我之前在做 NLP 时候遇到类似的瓶颈,加上 Attention 会好一些
    hwdq0012
        5
    hwdq0012  
       93 天前
    100%一般是过拟合了
    训练和测试数据集一样比较容易达到 100%

    这个数据集只是 0-9 的分类,基本不存在 label 错误的情况, 否则还有可能是 label 标记错了
    如果不是 label 错了,可以把测试失败的数据 多复制几份,再训练
    opeth
        6
    opeth  
       93 天前
    1. data augmentation 太少了,用 albumentations 加大数据增广的类别
    2. 模型太小了,换 resnet50 试试
    3. 关掉 early stop ,多跑几个 epochs
    opeth
        7
    opeth  
       93 天前
    又看了一下,为啥优化器要用 RMSprop ,不用 Adam ?
    学习率下降曲线可以改成 cosine
    batchsize 还能开更大一点吗?
    54lazycat
        8
    54lazycat  
       93 天前
    @hwdq0012 测试数据拿来训练,测试结果很好看,但实际上没意义
    opeth
        9
    opeth  
       93 天前
    loss 函数也可以改成更强的分类 loss
    hwdq0012
        10
    hwdq0012  
       93 天前
    @54lazycat #8 是的,实际企业能达到 98%都很满意了,他们能接受误判的, 训到 100%也没有意义,换个测试数据集可能又达不到了
    moxiaowei
        11
    moxiaowei  
    OP
       93 天前
    @opeth 好的 大佬 我还在试
    Sawyerhou
        12
    Sawyerhou  
       93 天前
    这 1%的准确率没有也无所谓吧,还是说你在打比赛要拿名次?
    parad
        13
    parad  
       93 天前 via Android
    这是你手写的还是 AI 写的呀? AI 写 ML 相关的代码还是不太行。建议看看相关数据&模型的教程,经典 paper 和 baseline 例子参考。
    moxiaowei
        14
    moxiaowei  
    OP
       93 天前
    @parad 我手写的
    moxiaowei
        15
    moxiaowei  
    OP
       93 天前
    破案了,训练到 96%以上的那个 S..B 用训练集做的测试,感觉到 95%将近 96%已经是极限了,用 Adam 优化器也差不多
    moxiaowei
        16
    moxiaowei  
    OP
       93 天前
    @Sawyerhou 不是打比赛,只是在学习
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3493 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 00:44 PVG 08:44 LAX 17:44 JFK 20:44
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86