El código de nuestra clase (sin incluir la importación de la librería NumPy ni la definición de las funciones sigmoid ni to_categorical) queda, por lo tanto, de la siguiente forma:
class NNClassifier(object):
def __init__(self, sizes, learning_rate = 0.01, batch_size = 16, epochs = 10):
""" 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:]]
def __update_parameters(self, mini_batch, learning_rate):
""" Actualiza los parámetros de la red (pesos y bias) aplicando descenso de gradiente
a cada mini-batch. Un mini-batch es una lista de tuplas (x, y) """
pass
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):
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, self.learning_rate)
print("Epoch {} complete".format(epoch))
def getOutput(self, x: np.ndarray):
""" Obtención de la predicción para una muestra """
for b, w in zip(self.biases, self.weights):
x = sigmoid(np.dot(w, x) + b)
return x
def predict(self, X: pd.core.frame.DataFrame):
""" Obtención de la predicción para las muestras contenidas en un DataFrame """
predictions = np.zeros(shape = len(X))
X.reset_index(inplace = True, drop = True)
for i, sample in X.iterrows():
prediction = model.getOutput(sample.values.reshape(-1, 1))
predictions[i] = np.argmax(prediction)
return predictions
Vemos que se ha añadido un mensaje de "Epoch X complete" en el método .fit() para indicar que se ha completado cada ciclo de entrenamiento.
También se ha definido el método privado __update_parameters() que recibirá cada mini-batch y actualizará los parámetros de la red.
Si instanciamos nuestra clase:
model = NNClassifier(
sizes = [784, 16, 10],
learning_rate = 0.01,
batch_size = 32,
epochs = 10
)
...y "entrenamos" el algoritmo, el resultado es:
model.fit(x_train, y_train)
Epoch 0 complete
Epoch 1 complete
Epoch 2 complete
Epoch 3 complete
Epoch 4 complete
Epoch 5 complete
Epoch 6 complete
Epoch 7 complete
Epoch 8 complete
Epoch 9 complete
("entrenamos" el algoritmo -entre comillas- pues ya sabemos que todavía no estamos modificando los parámetros aleatorios asignados inicialmente).