使用pytorch和sklearn进行MNIST数据集的交叉验证

8

我刚开始学习pytorch,尝试使用前馈神经网络对mnist数据集进行分类,但在使用交叉验证时遇到了一些问题。我的数据形状如下:

x_traintorch.Size([45000, 784])y_traintorch.Size([45000])

我尝试使用来自sklearn的KFold。

kfold = KFold(n_splits=10)

以下是我的训练方法的第一部分,其中我将数据划分为几个折叠:

for  train_index, test_index in kfold.split(x_train, y_train): 
        x_train_fold = x_train[train_index]
        x_test_fold = x_test[test_index]
        y_train_fold = y_train[train_index]
        y_test_fold = y_test[test_index]
        print(x_train_fold.shape)
        for epoch in range(epochs):
         ...
y_train_fold变量的索引是正确的,它只是:[ 0 1 2 ... 4497 4498 4499],但对于x_train_fold变量来说不是这样,它是[ 4500 4501 4502 ... 44997 44998 44999]。测试折叠也是一样的。
对于第一次迭代,我希望x_train_fold变量是前4500张图片,换句话说,它应该有形状torch.Size([4500, 784]),但它的形状是torch.Size([40500, 784]) 有什么提示可以让这个正确吗?
4个回答

9

我觉得你有些困惑!

暂时忽略第二个维度,当你有45000个数据点并使用10折交叉验证时,每折的大小是多少?45000/10即4500。

这意味着每个折将包含4500个数据点,其中一个折将用于测试,其余用于训练,即:

测试:一个折叠 => 4500个数据点 => 大小:4500
训练:剩余折叠 => 45000-4500个数据点 => 大小:45000-4500=40500

因此,在第一次迭代中,前4500个数据点(对应索引)将用于测试,其余用于训练。(请参见下面的图片)

假设你的数据是x_train: torch.Size([45000, 784])y_train: torch.Size([45000]),那么你的代码应该如下所示:

for train_index, test_index in kfold.split(x_train, y_train):  
    print(train_index, test_index)

    x_train_fold = x_train[train_index] 
    y_train_fold = y_train[train_index] 
    x_test_fold = x_train[test_index] 
    y_test_fold = y_train[test_index] 

    print(x_train_fold.shape, y_train_fold.shape) 
    print(x_test_fold.shape, y_test_fold.shape) 
    break 

[ 4500  4501  4502 ... 44997 44998 44999] [   0    1    2 ... 4497 4498 4499]
torch.Size([40500, 784]) torch.Size([40500])
torch.Size([4500, 784]) torch.Size([4500])

因此,当你说:

我希望变量x_train_fold是前4500张图片...形状为torch.Size([4500, 784])。

你错了。这个大小对应的是x_test_fold。在第一次迭代中,基于10个fold,x_train_fold将有40500个点,因此它的大小应该是torch.Size([40500, 784])

K-fold validation image


如果您能够看懂我下面的代码,我会非常高兴! - Kimmen
@kHarshit 这里的1次迭代和1个时期(epoch)是一样的吗? - user14675723
1
@helperFunction 这里的迭代是指 KFold 迭代,而不是训练循环中的 epoch/iteration。 - kHarshit

8

我现在认为我做对了,但是我感觉代码有点杂乱,有3层嵌套循环。是否有更简单的方法或这种方法可以接受?

以下是我的交叉验证训练代码:

def train(network, epochs, save_Model = False):
    total_acc = 0
    for fold, (train_index, test_index) in enumerate(kfold.split(x_train, y_train)):
        ### Dividing data into folds
        x_train_fold = x_train[train_index]
        x_test_fold = x_train[test_index]
        y_train_fold = y_train[train_index]
        y_test_fold = y_train[test_index]

        train = torch.utils.data.TensorDataset(x_train_fold, y_train_fold)
        test = torch.utils.data.TensorDataset(x_test_fold, y_test_fold)
        train_loader = torch.utils.data.DataLoader(train, batch_size = batch_size, shuffle = False)
        test_loader = torch.utils.data.DataLoader(test, batch_size = batch_size, shuffle = False)

        for epoch in range(epochs):
            print('\nEpoch {} / {} \nFold number {} / {}'.format(epoch + 1, epochs, fold + 1 , kfold.get_n_splits()))
            correct = 0
            network.train()
            for batch_index, (x_batch, y_batch) in enumerate(train_loader):
                optimizer.zero_grad()
                out = network(x_batch)
                loss = loss_f(out, y_batch)
                loss.backward()
                optimizer.step()
                pred = torch.max(out.data, dim=1)[1]
                correct += (pred == y_batch).sum()
                if (batch_index + 1) % 32 == 0:
                    print('[{}/{} ({:.0f}%)]\tLoss: {:.6f}\t Accuracy:{:.3f}%'.format(
                        (batch_index + 1)*len(x_batch), len(train_loader.dataset),
                        100.*batch_index / len(train_loader), loss.data, float(correct*100) / float(batch_size*(batch_index+1))))
        total_acc += float(correct*100) / float(batch_size*(batch_index+1))
    total_acc = (total_acc / kfold.get_n_splits())
    print('\n\nTotal accuracy cross validation: {:.3f}%'.format(total_acc))

我认为这没问题。训练通常有两个循环,KFold的一个循环就可以了。你可能想看一下skorch - 一个用于pytorch的sklearn包装器,虽然我没有使用过它。 - kHarshit
1
不错!但是需要注意的是,这并不是我们从交叉验证中期望得到的准确性。我们需要的是测试数据集准确度的平均值,而不是训练数据集的准确度。(或者我漏掉了什么?) - dak
3
每次交叉验证后,模型的权重是否需要重新初始化?由于优化器使用模型的参数,每次交叉验证不需要为每个折叠创建一个新的优化器实例吗? - Melike
1
@Kimmen 在每次折叠后,模型不应该被重置吗? - user14675723

4
你搞混了索引。
x_train = x[train_index]
x_test = x[test_index]
y_train = y[train_index]
y_test = y[test_index]
    x_fold = x_train[train_index]
    y_fold = y_train[test_index]

应该是:

x_fold = x_train[train_index]
y_fold = y_train[train_index]

你说得对!我已经更新了代码和问题,但是我的x_train_fold仍然有些问题。 - Kimmen

0

尽管以上所有答案都提供了如何分割数据集的良好示例,但我对实现K折交叉验证的方式很感兴趣。 K-fold旨在估计机器学习模型在未见过的数据上的表现能力。使用有限样本来估计模型在不参与训练的数据上进行预测时的表现情况(有关概念和解释请参见维基百科https://en.wikipedia.org/wiki/Cross-validation_(statistics))。因此,在每个折叠的开头初始化即将训练的模型的参数是必要的。否则,您的模型将在K倍交叉验证后看到数据集中的每个样本,并且没有验证(所有样本都是训练样本)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接