Incremento inesperado en el error de validación en MNIST Pytorch

Soy un poco nuevo en todo el campo y, por lo tanto, decidí trabajar en el conjunto de datos MNIST. Adapté prácticamente todo el código de https://github.com/pytorch/examples/blob/master/mnist/main.py , con solo un cambio significativo: Carga de datos. No quería usar el conjunto de datos precargado en Torchvision. Así que utilicé MNIST en CSV .

Cargué los datos de un archivo CSV heredándolos de Dataset y creando un nuevo cargador de datos. Aquí está el código relevante:

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, ) 

Sin embargo, este código me da el gráfico de error de entrenamiento absolutamente extraño que ves en la imagen, y un error de validación final del 11% porque lo clasifica todo como un ‘7’. Gráfico de error de validación

Logré rastrear el problema hasta cómo normalizo los datos y si uso los valores dados en el código de ejemplo (0.1307 y 0.3081) para las transformaciones. Normalizar, junto con la lectura de los datos como tipo ‘uint8’ funciona perfectamente. Tenga en cuenta que hay una diferencia mínima en los datos que se proporcionan en estos dos casos. La normalización de 0.1307 y 0.3081 en los valores de 0 a 1 tiene el mismo efecto que la normalización de 33.31 y 78.56 en los valores de 0 a 255. Los valores son casi los mismos (un píxel negro corresponde a -0.4241 en el primer caso y -0.4242 en el segundo).

Si desea ver un cuaderno de IPython donde este problema se ve claramente, consulte https://colab.research.google.com/drive/1W1qx7IADpnn5e5w97IcxVvmZAaMK9vL3

No puedo entender qué ha causado una diferencia tan enorme en el comportamiento dentro de estas dos formas ligeramente diferentes de cargar datos. Cualquier ayuda sería apreciada masivamente.

Larga historia corta: necesitas cambiar item = self.X[idx] a item = self.X[idx].copy() .

Larga historia: T.ToTensor() ejecuta torch.from_numpy , que devuelve un tensor que alias la memoria de su dataset de matriz numpy.X. Y T.Normalize() funciona in situ , por lo que, cada vez que se extrae la muestra, se resta la mean y se divide por std , lo que lleva a la degradación de su conjunto de datos.

Edición: en cuanto a por qué funciona en el cargador original MNIST, el agujero del conejo es aún más profundo. La línea clave en MNIST es que la imagen se transforma en una instancia de PIL.Image . La operación afirma que solo se copia en caso de que el búfer no sea contiguo (en nuestro caso), pero bajo el capó comprueba si se trata de una zancada (que es) y, por lo tanto, la copia. Así que, por suerte, la canalización predeterminada de torchvision implica una copia y, por lo tanto, la operación in situ de T.Normalize() no corrompe la self.data en memoria de nuestra instancia de MNIST .