Machine Learning paso a paso
En este post vamos a ver cómo crear un algoritmo de Machine Learning paso a paso en Python para aprender a reconocer si una imagen corresponde o no a un gato. Específicamente, vamos a ver el algoritmo de Regresión Logística, aunque el enfoque usado es el mismo si se quisiera usar otro tipo de algoritmo
Carga de librerías y utilidades con las que vamos a trabajar
Se han definido una serie de librerías y utilidades que nos ayudará a construir el algoritmo de aprendizaje
from utils import load_data, plot_cost_over_time from lr_utils import sigmoid, load_image import matplotlib.pyplot as plt import numpy as np import scipy from scipy import ndimage
Pasos para la construcción y entrenamiento del algoritmo:
Obtener los datos
Entrenar algoritmo de aprendizaje
Obtener modelo y hacer predicciones

1. Obtener los datos
El dataset consiste de un conjunto de imágenes donde el objetivo es determinar si cada una de ellas corresponde o no a un gato. El formato del dataset es HDF5, el cual contiene:
Un conjunto de datos de entrenamiento de 209 imágenes etiquetadas como gato (1) o no-gato (0)
Un conjunto de datos de prueba de 50 imágenes etiquetadas como gato (1) o no-gato (0)
Cada imagen tiene dimensiones (num_px, num_px, 3) donde 3 corresponde al número de canales (RGB).
train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_data()
print(train_set_x_orig.shape, test_set_x_orig.shape)
(209, 64, 64, 3) (50, 64, 64, 3)
Vamos a comprobar que los datos hayan quedado cargados, mostrando una de las imágenes:
index = 50 plt.imshow(train_set_x_orig[index]) print('is the image a cat? {}'.format(train_set_y[0, index]))

is the image a cat? 1
Una imagen para un computador no es mas que una matriz de números, dónde en cada posición esta representado el valor de un pixel (0-255). Dado que las imágenes estan en RGB (tres canales), eso indica que tenemos tres matrices y dado que cada imagen es de tamaño 64×64 pixeles, entonces lo que tenemos es que cada imagen para un computador puede verse como 3 matrices de tamaño 64×64

El algoritmo de aprendizaje requiere que la entrada sea representada como un vector de números, por lo tanto es necesario transformar las tres matrices en un solo vector que va a tener longitud 64∗64∗3=12288
Vamos a hacer una transformación de cada imagen para que pueda ser representada como un arreglo de números (pixeles)
train_set_x = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T test_set_x = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T print(train_set_x.shape)
(12288, 209)
Para que entrenamiento sea mas óptimo, vamos a normalizar el valor de cada pixel para que esté en un rango menor (0-1)
train_set_x = train_set_x / 255. test_set_x = test_set_x / 255. print(train_set_x.shape)
(12288, 209)
2. Entrenar algoritmo de aprendizaje

Para el algoritmo determinar si una imagen corresponde o no a un gato, se sigue el esquema definido en la figura anterior. Pero antes de esto, necesitamos hacer un proceso de entrenamiento para obtener unos pesos adecuados (valores de w) que nos permitan hacer predicciones acertadas y para ello vamos a seguir el siguiente enfoque:

Inicializando los pesos w
Los datos de entrada o X como se menciona en la figura del algoritmo ya los tenemos (imágenes), pero los pesos NO, entonces lo primero que debemos hacer es inicializar los pesos para poder hacer la multiplicación w * X

Recordemos la cantidad de X va a ser igual a la longitud del vector numérico calculado anteriormente, o sea numero_pixeles * numero_pixeles * 3 (RGB), en total 12288 valores. Con esta información podemos contruir una función que inicialice los pesos w dada la longitud del vector numérico
def initialize_parameters(num_px): w = np.zeros((num_px, 1)) b = 0 return w, b
Calculando la estimación del algoritmo (forward propagation)
El siguiente paso consiste en hacer el componente o función que dé una estimación (valor de probabilidad) de si la imágen correponde o no a un gato. Para esto, es necesario hacer dos pasos. En el primero, se calcula la multiplicación de los pesos w por los pixels de la imagen; el segundo, consiste en aplicar una función matemática a ese valor con el fin de obtener una probabilidad.
Nota: el primer paso se puede ver en la siguiente formula:
z = w1 * x1 + w2 * x2 + w3 * x3 + … + w12288 * x12288

Vamos a hacer una función que dados los pesos y la matriz de números respresentando imágenes (cada imagen siendo un vector de números) haga la multiplicación de los X por W y aplique una función a este valor (Sigmoid):
def forward_propagation(parameters, X): w, b = parameters z = np.dot(w.T, X) + b guess = sigmoid(z) return guess
Calculando la medida en que el algoritmo se esta equivocando en sus estimaciones
Lo siguiente por hacer es determinar si la estimación que hizo el algoritmo fue correcta y determinar una medida de cuán equivocado puede estar el algoritmo en sus predicciones en este punto.

Debemos obtener una medida del error para determinar qué tanto se esta equivocando el algoritmo (o visto de otra forma, qué tanto está aprendiendo). La formula para calcular el error teniendo en cuenta una imagen particular es:
L(i) = -(y(i) * log(guess(i)) + (1-y(i)) * log(1-guess(i)))
y(i) = 1 -> cat ó 0 -> non-cat
guess(i) = Probabilidad(gato)
La anterior formula era para una imagen, pero tenemos que calcular el error para el conjunto de datos (imágenes):
cost = 1/m * sum (L(i))
def compute_cost(guess, Y): m = Y.shape[1] return -1/m * np.sum(np.multiply(Y, np.log(guess)) + np.multiply(np.log(1-guess), 1-Y))
Determinando la forma en que los pesos deben ser cambiados para mejorar estimaciones futuras
Ya habiendo calculado el error, es necesario ahora determinar una manera que las estimaciones sean mejoradas en próximas iteraciones, o visto de otra forma que la diferencia o error entre la estimación del algoritmo y el valor real de la imágen (1-gato, 0-no gato) sea reducida. Para esto, se debe determinar una manera en que los pesos w puedan ser cambiados

La siguiente es la función que calcula un cambio en los pesos w de tal forma que el error pueda ser reducido:
def compute_change_in_weights(X, guess, Y): m = guess.shape[1] dw = 1/m * np.dot(X, (guess - Y).T) db = 1/m * np.sum(guess - Y) return dw, db
Actualizando los pesos w
El siguiente paso es actualizar los pesos para que la próxima vez la estimación del algoritmo sea un poco mas acercada a la realidad. La forma de actualizar los pesos está definida por la siguiente formula:
w = w – alpha * dw
dw = cambio en w

El valor de alpha no importa mucho por el momento, pensemos que es una constante que necesita el algoritmo para funcionar. Ya podemos crear la función que actualice los pesos w con base en los valores actuales de w, el cambio en los pesos calculado y el valor de alpha
def update_parameters(parameters, change_in_weights, learning_rate): w, b = parameters dw, db = change_in_weights w = w - learning_rate * dw b = b - learning_rate * db return w, b
Entrenando el algoritmo
En este punto podemos iniciar el proceso de entrenamiento para obtener como resultado un modelo (pesos w optimos para la solución del problema). Es necesario hacer un loop con el fin de que en cada iteración se puedan ir ajustando los pesos, de foma que al final las estimaciones sean más acertadas

Vamos a unir todas las piezas o funciones que creamos anteriormente con el fin de poder realizar nuestro proceso de antrenamiento y observar como iteración tras iteración el algoritmo va a aprendiendo:
def train_learning_algorithm(X, Y, num_iterations=3000, learning_rate=0.001): num_px = X.shape[0] parameters = initialize_parameters(num_px) costs = [] for i in range(num_iterations): guess = forward_propagation(parameters, X) cost = compute_cost(guess, Y) change_in_weights = compute_change_in_weights(X, guess, Y) parameters = update_parameters(parameters, change_in_weights, learning_rate) costs.append(cost) if i % 100 == 0: print('cost after iteration {}:{}'.format(i, cost)) plot_cost_over_time(costs) return parameters
En este punto, solo resta obtener el modelo a partir del proceso iterativo de entrenamiento
model = train_learning_algorithm(train_set_x, train_set_y)
cost after iteration 0:0.6931471805599453 cost after iteration 100:0.5912894260003538 cost after iteration 200:0.5557961107127088 cost after iteration 300:0.5289765131562365 cost after iteration 400:0.5068812917435516 cost after iteration 500:0.4878798632171657 cost after iteration 600:0.47110827803124367 cost after iteration 700:0.4560458096982851 cost after iteration 800:0.4423502279336529 cost after iteration 900:0.4297817153507784 cost after iteration 1000:0.41816382093643273 cost after iteration 1100:0.40736174995821917 cost after iteration 1200:0.39726946872697994 cost after iteration 1300:0.3878016072295409 cost after iteration 1400:0.3788881303593958 cost after iteration 1500:0.3704706898360003 cost after iteration 1600:0.36250004228346355 cost after iteration 1700:0.35493416988240534 cost after iteration 1800:0.34773687946679727 cost after iteration 1900:0.3408767368074387 cost after iteration 2000:0.3343262415502277 cost after iteration 2100:0.3280611786620027 cost after iteration 2200:0.32206010177265726 cost after iteration 2300:0.31630391670931673 cost after iteration 2400:0.31077554225448734 cost after iteration 2500:0.30545963120192193 cost after iteration 2600:0.3003423390438106 cost after iteration 2700:0.29541113067902147 cost after iteration 2800:0.29065461775939866 cost after iteration 2900:0.28606242093772705

Lo importante de observar en este punto, es que iteración tras iteración el valor de error va dismuyendo, lo cual es un indicador que el algoritmo está aprendiendo los patrones para reconocer un gato dentro de una imagen
3. Modelo – realizando predicciones
Ya hemos entrenado nuestro algoritmo de aprendizaje, pero ¿Qué tan bien se comporta haciendo predicciones? Para esto, haremos predicciones primero sobre los datos con los cuales fue entrenado para ver si es capaz de clasificarlos

Recordemos que el valor de la predicción es una probabilidad, por lo tanto es necesario convertirlo en 1(gato) o 0(no-gato)
def predict(X, model): pred = forward_propagation(model, X) return (pred > 0.5)
train_pred = predict(train_set_x, model) print(train_pred)
[[False False True False False False False True False False False True False True False False False False False True False False False False True True False True False True False False False False False False False False True False False False True False False False False True False False True False False True True False True True False True True True False False False False False False False False False True False False False True False False False False False False False True True False False False True False False False False True True False False True False False False False False False True False True True True True True True False False False False False True False False False False False False True False True False True True False False False True True False True True False False False False True False True True True False True True True False False True False True True False False False False False True False True False True False False False True True False False True True False True False False False False False False False True False False True False False False True False False False False True False False True False False False False False False False False]]
Como vemos, necesitamos una métrica que nos diga que tan bien se comporta el algoritmo con los datos de entrenamiento. Para esto calculamos un valor de precisión
def calculate_accuracy(pred, Y): acc = 1 - np.mean(np.abs(Y - pred)) return acc
print('Accuracy on training set: {}'.format(calculate_accuracy(train_pred, train_set_y)))
Accuracy on training set: 0.937799043062201
Ya obtuvimos la predicción sobre los datos de entrenamiento, pero ¿Qué pasa si quiero probar el desempeño del algoritmo sobre datos con los cuales no fue entrenado?. En este caso podemos hacer uso del dataset de prueba test_set_x
que obtumos cuando cargamos los datos en memoria para hacer predicciones y obtener un porcentaje de precición
test_pred = predict(test_set_x, model) print('Accuracy on test set: {}'.format(calculate_accuracy(test_pred, test_set_y)))
Accuracy on test set: 0.74
Probando con nuestras propias imágenes
Ahora la idea es usar nuestras propias imágenes de gatos y hacer predicciones:

Recordemos que el algoritmo espera que los X de entrada para una imagen sea un vector de tamaño 12288. Teniendo esto en cuenta, si queremos validar con nuestra propia imagen, debemos asegurar que el tamaño de ésta sea 64 * 64 pixeles (64 * 64 * 3 = 12288). Para esto usamos la función utilitaria load_image
que se encarga de cargar una imagen desde disco, cambiar su tamaño y generar el vector que será procesado por el algoritmo. Con este vector podemos usar el modelo para hacer una predicción usando la función predict
que se definió anteriormente:
my_cat = 'mycat.jpg' image, image_vector = load_image(my_cat)
pred = predict(image_vector, model) print('Is it a cat?: {}'.format(np.squeeze(pred)))
Is it a cat?: True
En este post vimos paso a paso cómo se construye una solución de Machine Learning usando el algoritmo de Regresión Logística para obtener un modelo que hace predicciones.En particular, pudimos resolver un problema de reconocimiento de imágenes, para identificar si una una imagen corresponde o no a un gato.