
Compartir:
Desarrollador IOS convertido en entusiasta de la Ciencia de Datos / Aprendizaje Automático. Quiero que la gente entienda qué es el aprendizaje automático y cómo podemos utilizarlo en nuestras aplicaciones.
Creación de un detector de razas de perros mediante aprendizaje automático
Tiempo de lectura: 10 minutos
Aquí, en Nexmo, utilizamos Facebook Workplace como uno de nuestros muchos canales de comunicación. Si no lo has utilizado o no has oído hablar de él, es como Facebook, pero para las empresas. Todos nosotros aquí en Nexmo tenemos una Account, y somos capaces de ver y unirnos a diferentes grupos en toda la organización.
Hace unos meses, uno de nuestros compañeros de trabajo creó un grupo para mostrar nuestras mascotas, y fue una gran idea, y muchos miembros del equipo publican fotos de sus mascotas. Visito el grupo casi todos los días y es una buena forma de disfrutar de las cosas buenas de la vida (¡los cachorros!).
wp-group
Así que después de ver la foto de cada uno de su perro, gato e incluso conejitos, algunas personas preguntaron: "¿De qué raza es?". Cuando vi eso, se me ocurrió una idea: crear un algoritmo de aprendizaje automático para averiguar qué raza de perro había en la foto.
En este post, vamos a aprender a construir un detector de razas de perros usando Kerasque es un framework muy popular para construir modelos de aprendizaje automático.
Requisitos previos
Este post asume que usted sabe algo de Python, así como tener una comprensión muy básica de aprendizaje automático. Deberías saber qué es Keras y cómo entrenar un modelo básico de aprendizaje automático.
¿Por dónde empiezo?
Para abordar muchos problemas de aprendizaje automático, se necesitan datos, y muchos. En concreto, necesitamos fotos de muchos perros, y de qué tipo de razas hay. Para este proyecto, vamos a utilizar el conjunto de datos del Reto de identificación de razas de perros en Kaggle. Este conjunto de datos contiene más de 10.000 imágenes de perros, clasificadas por razas.
Construir el modelo
En primer lugar, vamos a empezar con la construcción del modelo. Voy a utilizar Google Colab para construir mi Jupyter Notebooken Python. Un Jupyter Notebook es una aplicación web de código abierto que te permite escribir código, así como texto e imágenes. Es una buena manera de empezar. Google Colab es un servicio gratuito que alojará tus Jupyter Notebooks.
Nota: Si quieres ver cómo se construye el modelo, puedes ver mi cuaderno aquí.
Antes de construir el modelo, necesitamos obtener los datos, que están alojados en Kaggle. Para cargar los datos, tenemos que utilizar un paquete para descargar los datos a nuestro cuaderno, utilizando la API de Kaggle. Esto nos permitirá descargar el conjunto de datos del Concurso de Razas Caninas. Antes de poder descargar el conjunto de datos, tenemos que crear una Account en Kaggle, y obtener la clave y el secreto de la API de Kaggle.
kaggle-create-api-token
Ve a "Create New API Token", y guarda el archivo en tu máquina. Para descargar los datos, ejecutaremos esto celda.
# Run this cell and select the kaggle.json file downloaded
# from the Kaggle account settings page.
from google.colab import files
files.upload()
# Let's make sure the kaggle.json file is present.
!ls -lha kaggle.json
# Next, install the Kaggle API client.
!pip install -q kaggle
# The Kaggle API client expects this file to be in ~/.kaggle,
# so move it there.
!mkdir -p ~/.kaggle
!cp kaggle.json ~/.kaggle/
# This permissions change avoids a warning on Kaggle tool startup.
!chmod 600 ~/.kaggle/kaggle.json
#download the dataset for the dog-breed identification challenge https://www.kaggle.com/c/dog-breed-identification
!kaggle competitions download -c dog-breed-identification
#unzip the downloaded files
!unzip labels.csv.zip
!unzip test.zip
!unzip train.zipSi no entiendes cada línea de este código y de cualquier otra sección de código, no te preocupes. Podrás copiar y pegar el código fuente para ejecutarlo todo tú mismo, sin tener que preocuparte por los detalles.
Cuando ejecutes esta celda, te pedirá que selecciones un archivo. Busque el archivo JSON que se descargó de Kaggle y cárguelo en la celda. A continuación, podrá ejecutar la API de Kaggle y descargar el conjunto de datos en el cuaderno. Una vez descargados los archivos, los descomprimiremos utilizando !unzip. El ! que precede al comando permite ejecutar una acción de línea de comandos dentro de Google Colab. El comando !unzip simplemente descomprime cada archivo.
Los archivos descargados de Kaggle contienen lo siguiente:
Imágenes de formación, localice la
\traincarpetaImágenes de prueba, situadas en la carpeta
\testcarpetaUn archivo CSV llamado
labels.csvque contiene el nombre de la raza y el nombre del archivo, que apunta a la imagen de la carpeta de formación.
Ahora, podemos cargar nuestros datos en un Dataframe, utilizando Pandas. A DataFramees una estructura de datos simple que contiene filas y columnas, algo así como un CSV. Pandas es un paquete de Python que proporciona estructuras de datos y herramientas de análisis de datos de alto rendimiento y fáciles de usar. Se utiliza en muchas aplicaciones de aprendizaje automático. Si haces cualquier aplicación de aprendizaje automático, uno de los primeros paquetes que usarás es Pandas. Si quieres aprender más sobre Pandas, echa un vistazo a su propio tutorial, tutorial de 10 minutos sobre pandas.
Usando Pandas, podemos importar el csv del conjunto de datos de Kaggle a un Dataframe de Pandas.
#import the necessary packages
import pandas as pd
import numpy as np
#constants
num_classes = 12 # the number of breeds we want to classify
seed = 42 # makes the random numbers in numpy predictable
im_size = 299 # This size of the images
batch_size = 32
#read the csv into a dataframe, group the breeds by name and append the path the to image in the `filename` column
df = pd.read_csv('labels.csv')
selected_breed_list = list(df.groupby('breed').count().sort_values(by='id', ascending=False).head(num_classes).index)
df = df[df['breed'].isin(selected_breed_list)]
df['filename'] = df.apply(lambda x: ('train/' + x['id'] + '.jpg'), axis=1)
breeds = pd.Series(df['breed'])
print("total number of breeds to classify",len(breeds.unique()))
df.head()Esto toma el csv, lo carga en un DataFrame pd.read_csv('labels.csv')y ordena el DataFrame por raza alfabéticamente. A continuación, imprimimos las 10 primeras filas utilizando df.head()
df_head
A continuación, tenemos que escribir una función que redimensione todas las imágenes al tamaño que necesitamos, que es 299x299px. Estará claro por qué tenemos que cambiar el tamaño de la imagen más tarde.
from keras.preprocessing import image
def read_img(img_id, train_or_test, size):
"""Read and resize image.
# Arguments
img_id: string
train_or_test: string 'train' or 'test'.
size: resize the original image.
# Returns
Image as numpy array.
"""
path = train_or_test + "/" + img_id + ".jpg"
img = image.load_img(path, target_size=size)
return image.img_to_array(img)La función read_img() cargará la imagen al tamaño que necesitamos (299x299px) y la convertirá en un array numpy multidimensional. Numpy es otro paquete de Python que se utiliza en el aprendizaje automático con mucha frecuencia. Facilita el trabajo con este tipo de matrices en Python.
A continuación, tenemos que convertir los nombres de las razas (basenji, scottish_deerhound) en vectores (matrices unidimensionales de números), ya que nuestro modelo de aprendizaje automático sólo puede trabajar con números. Para ello, utilizaremos la herramienta de Scikit Learn LabelEncoder. Un LabelEncoder toma todos los nombres de las razas y los convierte en números enteros. Cada número será diferente para cada raza (0 para basenji1 para scottish_deerhound etc.). Scikit-Learn es otro paquete de código abierto que facilita el aprendizaje automático.
A continuación, dividiremos el conjunto de datos en dos, uno para el entrenamiento y otro para las pruebas. Cuando entrenemos nuestro modelo, utilizaremos los datos del conjunto de entrenamiento para entrenar el modelo, y luego, cuando necesitemos ver lo bien que lo ha hecho, probaremos el modelo en el conjunto de pruebas.
from sklearn.preprocessing import LabelEncoder
label_enc = LabelEncoder()
np.random.seed(seed=seed)
rnd = np.random.random(len(df))
train_idx = rnd < 0.9 valid_idx = rnd >= 0.9
y_train = label_enc.fit_transform(df["breed"].values)
ytr = y_train[train_idx]
yv = y_train[valid_idx]
Por último, tomaremos todas las imágenes del conjunto de entrenamiento y cambiaremos su tamaño utilizando la función read_img que hemos creado antes. A continuación, tenemos que procesar cada imagen para ponerla en el formato correcto que nuestro modelo está esperando .
from tqdm import tqdm
from keras.applications import xception
x_train = np.zeros((train_idx.sum(), im_size, im_size, 3), dtype='float32')
x_valid = np.zeros((valid_idx.sum(), im_size, im_size, 3), dtype='float32')
train_i = 0
valid_i = 0
for i, img_id in tqdm(enumerate(df['id'])):
img = read_img(img_id, 'train', (im_size, im_size))
x = xception.preprocess_input(np.expand_dims(img.copy(), axis=0))
if train_idx[i]:
x_train[train_i] = x
train_i += 1
elif valid_idx[i]:
x_valid[valid_i] = x
valid_i += 1
print('Train Images shape: {} size: {:,}'.format(x_train.shape, x_train.size))
[00:06, 201.73it/s]Train Images shape: (1218, 299, 299, 3) size: 326,671,254En esta función, recorremos cada elemento de nuestro DataFrame (for i, img_id in tqdm(enumerate(df['id'])): y llamamos a la función read_image que toma el valor img_idque es el ID de la imagen, que se correlaciona con el nombre de archivo de la imagen de la carpeta \train y se redimensiona a 299x299px. Luego llamamos a la función xception.preprocess_input función.
Antes de llegar a lo que hace esta función, tendremos que entender lo que xception es.
Entrenar modelos desde cero requiere muchas más imágenes de las que tenemos (10.000), así como mucho tiempo y recursos informáticos. Para acelerar este proceso, podemos utilizar una técnica llamada Aprendizaje por transferencia. Esto significa que podemos utilizar un modelo previamente entrenado en otro conjunto de datos, como por ejemplo conjunto de datos Imagenet. Xception es uno de esos modelos preentrenados. Podemos contar con el modelo preentrenado para extraer características de la imagen. A continuación, nos limitaremos a entrenar el modelo para nuestro caso de uso específico: determinar la raza.
He experimentado con algunos modelos y he descubierto que Xception da los mejores resultados para el caso de uso de detección de razas, con el conjunto de datos de imágenes que estamos utilizando.
Para otros conjuntos de datos, puede haber otros modelos que se adapten mejor a sus necesidades para obtener mejores resultados. Así que asegúrate de hacer algunas pruebas antes de decidir qué modelo utilizar. Para obtener más información sobre este y otros modelos preentrenados, consulta este enlace (https://www.pyimagesearch.com/2017/03/20/imagenet-vggnet-resnet-inception-xception-keras/). Este post también explica qué es ImageNet y cómo se relaciona con estos modelos preformados.
Bien, ahora volvamos a lo que hace la función xception.preprocess_input() hace. Esto toma la imagen, que ahora es una matriz numpy, y la convierte en un formato que el modelo Xception está esperando, en el que todos los valores de la matriz están entre -1 y 1, lo que se conoce como normalización.
Ahora, podemos construir nuestro modelo.
Dado que estamos utilizando Xception como modelo base, nuestro modelo personalizado es muy sencillo. Para nuestro propio modelo, cargaremos la salida de Xception, que son todas las capas que ya han sido entrenadas en imágenes de Imagenet, y luego construiremos un modelo Secuencial.
Del blog de Keras: "El modelo Secuencial es una pila lineal de capas". Introducción al modelo secuencial de Keras
Esto significa que podemos apilar otras capas sobre el modelo Xception. Esto permitirá que nuestro modelo se entrene con las imágenes de nuestros perros.
Aquí está nuestro modelo. Fuente
from keras.layers import GlobalAveragePooling2D, Dense, BatchNormalization, Dropout
from keras.optimizers import Adam, SGD, RMSprop
from keras.models import Model, Input
# create the base pre-trained model
base_model = xception.Xception(weights='imagenet', include_top=False)
# first: train only the top layers (which were randomly initialized)
# i.e. freeze all convolutional Xception layers
for layer in base_model.layers:
layer.trainable = False
# add a global spatial average pooling layer
x = base_model.output
x = BatchNormalization()(x)
x = GlobalAveragePooling2D()(x)
# let's add a fully-connected layer
x = Dropout(0.5)(x)
x = Dense(1024, activation='relu')(x)
x = Dropout(0.5)(x)
# and a logistic layer and set it to the number of breeds we want to classify,
predictions = Dense(num_classes, activation='softmax')(x)
# this is the model we will train
model = Model(inputs=base_model.input, outputs=predictions)Primero tomamos nuestro base_modelque es Xception, entonces Freeze las capas. Esto significa que no haremos ningún entrenamiento en esas capas, puesto que ya han sido entrenadas. A continuación, tomaremos la salida de la capa base_model y añadiremos las siguientes capas:
BatchNormalization - aplica una transformación que mantiene la media de activación cercana a 0 y la desviación estándar de activación cercana a 1.
GlobalAveragePooling2D reduce el número de parámetros que hay que aprender.
Desconexión - desactiva aleatoriamente las entradas para evitar sobreajuste.
Densa que conecta todas las neuronas de la red.
seguido de otro Dropout capa.
Por último, creamos otro densa y la configuramos con el número de razas para las que estamos entrenando.
La selección de estas capas se basa en el método de ensayo y error. No existe una forma conocida de determinar una buena estructura de red cuando se construye el modelo. Cuando construyas tus propios modelos, Stack Overflow es tu mejor amigo.
Entrenamiento del modelo
Ahora, vamos a empezar a construir realmente entrenar el modelo. La forma en que estoy entrenando el modelo es muy básica. Verás este tipo de código cuando revises otros modelos en Keras. Fuente
import datetime
from keras.callbacks import EarlyStopping, ModelCheckpoint
epochs = 1
learning_rate = 0.001
# checkpoints
early_stopping = EarlyStopping(monitor='val_acc', patience=5)
STAMP = "{}_dog_breed_model".format(datetime.date.today().strftime("%Y-%m-%d"))
bst_model_path = "{}.h5".format(STAMP)
model_checkpoint = ModelCheckpoint(bst_model_path,
save_best_only=True,
save_weights_only=False,
verbose=1)
# compile the model
optimizer = RMSprop(lr=learning_rate, rho=0.9)
model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=["accuracy"])
hist = model.fit_generator(train_generator,
steps_per_epoch=train_idx.sum() // batch_size,
epochs=epochs, callbacks=[early_stopping, model_checkpoint],
validation_data=valid_generator,
validation_steps=valid_idx.sum() // batch_size)
model.save(bst_model_path)Primero añadimos algunas funciones de retrollamadaque son funciones que ejecutamos después de cada ronda de entrenamiento, también conocidas como epoch. Tenemos 2 callbacks:
early_stoppingcon el parámetropatiencede 5: Esto detendrá el entrenamiento si el modelo no mejora después de 5 épocas.model_checkpoint: Guarda el modelo en un archivo para su uso posterior.
A continuación ajustamos el optimizador a RMSprop. El optimizador optimizador es la forma en que el modelo "aprende". Para cada época, el modelo calcula la función de pérdida, que es lo mal que lo hizo el modelo en comparación con el conjunto de pruebas. El objetivo es que esta pérdida sea lo más baja posible, lo que se denomina Ascenso Gradiente. Keras soporta muchos optimizadores, y en mis experimentos, RMSProp, que realiza Gradient Descent, parecía funcionar mejor.
A continuación construiremos el modelo utilizando model.compile que acepta el optimizador, la función de pérdida que queremos calcular (sparse_categorical_crossentropy) y el parámetro metrics a accuracyque nos dirá cómo de preciso es el modelo después de cada época.
Después, haremos nuestra formación llamando a model.fit_generator(). Los parámetros de esta función son: ImageDataGenerator's tanto para nuestro conjunto de entrenamiento como para el conjunto de prueba, cuántos pasos ejecutaremos, número de epochs, sobre qué validar y el número de pasos a validar. Vamos a entrenar este modelo para 10 épocas por ahora, sólo para ver cómo lo hicimos.
Epoch 1/10
38/38 [==============================] - 40s 1s/step - loss: 0.5477 - acc: 0.8281 - val_loss: 0.0555 - val_acc: 0.9766
*skipping output for readability*
Epoch 10/10
38/38 [==============================] - 33s 857ms/step - loss: 0.2426 - acc: 0.9358 - val_loss: 0.0457 - val_acc: 0.9905Así pues, ¡tenemos un modelo que tiene una precisión del 99% a la hora de predecir 12 razas!
Ahora, podemos probar nuestro modelo con algunas imágenes de perros y ver si somos capaces de obtener la raza correcta a partir de esa imagen. Escribiremos una función que tome una imagen de internet, la formatee a lo que el modelo espera (imagen de 299x299px) y haga la predicción usando model.predict(). Esta función toma una imagen, como un array numpy, y devuelve la salida como una lista de probabilidades para cada raza. Usamos np.argmax() para encontrar el índice de la probabilidad más alta de la salida de model.predict(). Para devolver el nombre de la raza, utilizamos el método labels.csv que cargamos del conjunto de datos de Kaggle que contiene los 12 nombres de razas. A continuación, ordenaremos la lista alfabéticamente y devolveremos el nombre de la raza.
from keras.models import load_model
from keras.preprocessing import image
import matplotlib.pyplot as plt
import numpy as np
import os
def predict_from_image(img_path):
img = image.load_img(img_path, target_size=(299, 299))
img_tensor = image.img_to_array(img) # (height, width, channels)
img_tensor = np.expand_dims(img_tensor, axis=0) # (1, height, width, channels), add a dimension because the model expects this shape: (batch_size, height, width, channels)
img_tensor /= 255.
pred = model.predict(img_tensor)
predicted_class = sorted_breeds_list[np.argmax(pred)]
return predicted_classAhora vamos a probar esta función, para asegurarnos de que funciona. Vamos a descargar una foto de un Deerhound escocésusando wgetque es una utilidad de línea de comandos para descargar archivos, y veremos cómo funciona el modelo.
predict
¡Qué bonito! El modelo predice que el perro de la foto es un Deerhound escocés, ¡y lo es!
Conclusión
A partir de este post, hemos aprendido a construir nuestro propio modelo de aprendizaje automático usando Keras, entrenar nuestro modelo usando Transfer Learning y aprendido a hacer predicciones usando nuestro modelo.
En un próximo post, veremos cómo desplegar este modelo en un servidor como una API simple para que otros lo utilicen. A continuación, vamos a construir un Workplace Bot que permite a cualquier persona en nuestro grupo Workplace` preguntar qué raza de perro está en una foto de un post de Workplace.