MNIST Pytorch 中的验证错误意外增加

Unexpected increase in validation error in MNIST Pytorch

我对整个领域有点陌生,因此决定处理 MNIST 数据集。我几乎改编了 https://github.com/pytorch/examples/blob/master/mnist/main.py, with only one significant change: Data Loading. I didn't want to use the pre-loaded dataset within Torchvision. So I used MNIST in CSV.

的整个代码

我通过继承数据集并制作新的数据加载器从 CSV 文件加载数据。 这是相关代码:

mean = 33.318421449829934
sd = 78.56749081851163
# mean = 0.1307
# sd = 0.3081
import numpy as np
from torch.utils.data import Dataset, DataLoader

class dataset(Dataset):
    def __init__(self, csv, transform=None):
        data = pd.read_csv(csv, header=None)
        self.X = np.array(data.iloc[:, 1:]).reshape(-1, 28, 28, 1).astype('float32')
        self.Y = np.array(data.iloc[:, 0])

        del data
        self.transform = transform

    def __len__(self):
        return len(self.X)

    def __getitem__(self, idx):
        item = self.X[idx]
        label = self.Y[idx]

        if self.transform:
            item = self.transform(item)

        return (item, label)

import torchvision.transforms as transforms
trainData = dataset('mnist_train.csv', transform=transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((mean,), (sd,))
]))
testData = dataset('mnist_test.csv', transform=transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((mean,), (sd,))
]))

train_loader = DataLoader(dataset=trainData,
                         batch_size=10, 
                         shuffle=True,
                         )
test_loader = DataLoader(dataset=testData, 
                        batch_size=10, 
                        shuffle=True,
                        )

然而,这段代码给了我你在图片中看到的绝对奇怪的训练错误图,以及 11% 的最终验证错误,因为它将所有内容都归类为“7”。

我设法将问题归结为我如何规范化数据,以及我是否将示例代码中给出的值(0.1307 和 0.3081)用于 transforms.Normalize,并将数据读取为类型 'uint8' 它完美地工作。 请注意,在这两种情况下提供的数据 中存在非常小的 差异。通过 0.1307 和 0.3081 对从 0 到 1 的值进行归一化与通过 33.31 和 78.56 对从 0 到 255 的值进行归一化具有相同的效果。这些值甚至大部分相同(黑色像素对应于第一种情况下的 -0.4241 和 -0.4242在第二个)。

如果您想查看清楚地看到此问题的 IPython 笔记本,请查看 https://colab.research.google.com/drive/1W1qx7IADpnn5e5w97IcxVvmZAaMK9vL3

我无法理解是什么导致这两种略有不同的数据加载方式出现如此巨大的行为差异。任何帮助将不胜感激。

长话短说:您需要将 item = self.X[idx] 更改为 item = self.X[idx].copy()

长话短说:T.ToTensor() 运行 torch.from_numpy, which returns a tensor which aliases the memory of your numpy array dataset.X. And T.Normalize() works inplace,因此每次绘制样本时都会减去 mean 并除以 std,导致你的数据集。

编辑:关于为什么它在原始 MNIST 加载器中工作,兔子洞更深。 MNIST 中的关键行是图像是 transformed into a PIL.Image instance. The operation claims to only copy in case the buffer is not contiguous (it is in our case), but under the hood 它检查它是否跨步而不是(它是),从而复制它。所以幸运的是,默认的 torchvision 管道涉及一个副本,因此 T.Normalize() 的就地操作不会破坏我们 MNIST 实例的内存中 self.data