V2EX 07月08日 14:53
[Python] 模型在测试集上的准确率卡在 95%上不去,能想的办法都试过了就是不行,都 3 天了,求各位大佬帮我看下有没有优化的方案!跪谢!
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文探讨了使用SVHN数据集训练模型时,训练集准确率持续上升但测试集准确率卡在95%的优化问题。文章介绍了基于ResNet18和Vgg16构建的模型结构,以及数据预处理、损失函数和优化器的配置。作者尝试了多种方法,如早停机制、权重复用和参数调整,但测试集准确率仍无法突破95%。文章最后提出了可能的解决方案,如数据增强、模型结构调整或尝试其他网络架构,以进一步提升模型性能。

🔍 文章首先介绍了使用SVHN数据集训练模型时遇到的困境:训练集准确率持续上升,但测试集准确率卡在95%,无法进一步提升。

🌐 作者尝试了基于ResNet18和Vgg16构建的模型结构,并详细描述了数据预处理、损失函数和优化器的配置过程,为读者提供了完整的模型训练框架。

📈 文章详细记录了多种优化方法的尝试,包括早停机制、权重复用和参数调整,但测试集准确率仍无法突破95%,展现了问题的复杂性。

💡 作者在文章最后提出了可能的解决方案,如数据增强、模型结构调整或尝试其他网络架构,为读者提供了进一步研究的方向。

使用 SVHN 的数据集进行模型的训练,但是整个模型在训练集上的准确率是一直在上升的,但是到了测试集上就一直卡在 95%,都 3 天了,求各位大佬帮我看下有没有优化的方案!跪谢!

import torchimport torch.nn as nnimport torch.optim as optimfrom torch.distributed.checkpoint import load_state_dictfrom torch.hub import load_state_dict_from_urlfrom torch.nn.modules.loss import _Lossfrom torch.optim import Optimizerfrom torch.utils.data import Dataset, random_split, DataLoaderimport torchvisionfrom torchvision.transforms import transformsimport torchvision.models as mimport matplotlib.pyplot as pltimport randomimport gc  # 用于垃圾回收from torchinfo import summaryimport numpy as npimport randomimport gc# 设置随机数种子SEED = 420random.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 = Truetorch.backends.cudnn.benchmark = False# 设置使用 gpu 还是 cpu 进行训练device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# 定义训练的论述epochs = 100lr = 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, testCorrectmodel = 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()

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

SVHN 模型优化 深度学习 准确率提升
相关文章