techAI te enseña: Machine Learning paso a paso

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.

Compartir

Share on facebook
Share on twitter
Share on linkedin

Entradas relacionadas

¿Quieres recibir más información sobre tendencias en desarrollo de software, DevOps, innovación o productividad?

Últimas entradas