Desordenación del dataset de entrenamiento

Ya comentamos que, tal y como está ahora el código, los "batches" son los mismos en cada epoch, lo que podría dificultar el aprendizaje.

Para permitir que la red pueda ser desordenada vamos a añadir un nuevo parámetro a nuestra clase, shuffle, que determinará si el dataset se va a desordenar o no al comienzo de cada epoch. El constructor de la clase será ahora el siguiente:

def __init__(self, sizes, learning_rate = 0.01, batch_size = 16, epochs = 10, shuffle = True):
    """ Constructor de la red neuronal """
    self.num_layers = len(sizes)         # Número total de capas de la red
    self.sizes = sizes                   # Lista conteniendo el número de neuronas por capa
    self.learning_rate = learning_rate   # Tasa de aprendizaje
    self.batch_size = batch_size         # Tamaño del batch
    self.epochs = epochs                 # Número de epochs durante los que entrenar la red
    self.weights = [np.random.randn(x, y) for (x, y) in zip(sizes[1:], sizes[:-1])]
    self.biases = [np.random.randn(n, 1) for n in sizes[1:]]
    self.shuffle = shuffle               # Si toma el valor True, se desordenará el dataset

En el método .fit(), por otro lado, es donde vamos a desordenar físicamente el dataset, para lo que haremos uso de la función np.random.shuffle de NumPy:

def fit(self, X: pd.core.frame.DataFrame, y: pd.core.series.Series):
    """ Entrenamiento de la red neuronal"""
    x_train = [x.values.reshape(-1, 1) for (i, x) in X.iterrows()]
    y_train = [to_categorical(n, self.sizes[-1]) for n in y]
    training_data = [(x, y) for (x, y) in zip(x_train, y_train)]
    n = len(training_data)
    for epoch in range(self.epochs):
        if self.shuffle:
            np.random.shuffle(training_data)

        mini_batches = [training_data[start:start + self.batch_size]
                        for start in range(0, n, self.batch_size)]
        for mini_batch in mini_batches:
            self.__update_parameters(mini_batch)
        print("Epoch {} complete".format(epoch))

Como vemos, esta función -que desordena la estructura in-place- solo se va ejecutar cuando el parámetro shuffle tome el valor True (valor que hemos programado como valor por defecto).