Clasificador CNN de razas de perros

Deep Learning: ¿Cómo construir un detector de perros y un clasificador CNN de razas de perros?!

Este trabajo es parte del proyecto final de Data Science de Udacity.

Descripción general:

Puede pensar en reconocer la raza de un perro en una imagen es una tarea fácil para usted. ¡Tiene razón!.

Puede que no sea difícil encontrar parejas de razas de perros con una variación mínima entre clases, por ejemplo, Curly-Coated Retrievers y American Water Spaniels.

Pero, ¿qué tal estos dos?!

¡Huh, no es tan fácil! En la última década, es mucho más fácil usar técnicas de aprendizaje profundo con unas pocas líneas de código python para distinguir entre razas de perros en las imágenes.

En este blog, lo guiaré a través de cómo crear Redes neuronales convolucionales (CNN) desde cero y aprovechar las últimas técnicas de clasificación de imágenes de última generación en ImageNet.

Este modelo se puede usar como parte de una aplicación móvil o web para el mundo real y las imágenes proporcionadas por el usuario. Dada una imagen al modelo, determina si un perro está presente y devuelve la raza estimada.

Si la imagen es humana, devolverá la raza de perro más parecida. Puede encontrar el código en mi repositorio de GitHub .

Detector humano

Utilicé la implementación de OpenCV del clasificador de objetos en cascada basado en características de Haar para detectar rostros humanos . La función en cascada es un enfoque basado en el aprendizaje automático entrenado en muchas imágenes con etiquetas positivas (con una cara) y negativas (sin ninguna cara).

DetectMultiScale obtiene las coordenadas de todas las caras y luego las devuelve como una lista de rectángulos. Pero no olvide convertir la imagen RGB en escala de grises antes de usarla.

La ​​siguiente función face_detector cuenta cuántos rostros humanos hay en la foto:

def face_detector(img_path):
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray)
return len(faces) > 0

El rendimiento de face_detector evaluado en 100 muestras de imágenes de humanos y perros. Este detector reconoce todos los rostros humanos de los datos humanos, pero no funcionó bien en el conjunto de datos del perro. Tenía alrededor del 11% de falsos positivos.

Detector de perro

Es hora de usar otro detector que funcione mejor en el conjunto de datos del perro. Utilicé pesas ResNet50 previamente entrenadas en ImageNet en Keras, que está entrenado en más de 10 millones de imágenes que contienen 1000 etiquetas.

En el siguiente código, routes_to_tensor toma la ruta a una imagen y devuelve un tensor 4D listo para ResNet50. Pero, todos los modelos pre-entrenados en Keras necesitan un procesamiento adicional como la normalización que se puede hacer usando preprocess_input. La función dog_detector devuelve “True” si se detecta un perro en la imagen almacenada en img_path.

from keras.preprocessing import image
from tqdm import tqdm
from keras.applications.resnet50 import preprocess_input,
decode_predictions

def path_to_tensor(img_path):
img = image.load_img(img_path, target_size=(224, 224)
return np.expand_dims(x, axis=0)

def paths_to_tensor(img_paths):
list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
return np.vstack(list_of_tensors)

def ResNet50_predict_labels(img_path):
img = preprocess_input(path_to_tensor(img_path))
return np.argmax(ResNet50_model.predict(img))

def dog_detector(img_path):
prediction = ResNet50_predict_labels(img_path)
return ((prediction <= 268) & (prediction >= 151))

Para evaluar el detector de perros, verifiqué si la clase predicha de RestNet50 en ImageNet cae en las categorías de raza de perro. El detector de perros funciona bien sin falsos negativos.

Ahora que reconocemos a los perros en imágenes, es hora de predecir las razas. Pero primero debemos analizar un poco más los datos del tren:

Conjunto de datos

Este conjunto de datos tiene 8.351 imágenes en total con 133 razas diferentes. El número de imágenes disponibles para que el modelo aprenda es de aproximadamente 62 por tipo, lo que podría no ser suficiente para CNN.

En este entorno del mundo real, las imágenes tienen diferentes resoluciones, tamaños, condiciones de iluminación, también algunas imágenes tienen más de un perro.

Al comparar la distribución de intensidad de píxeles de las mismas imágenes etiquetadas, noté, por ejemplo, que las fotos de American_staffordshire_terrier varían en contraste, tamaño y brillo.

Los valores de intensidad de los canales rojo, verde y azul que se encuentran en estas dos imágenes se distribuyen de manera diferente. Esta variación en los datos hace que la tarea de asignar raza a perros sea aún más desafiante.

La distribución de las etiquetas de raza en los datos de entrenamiento muestra que los datos están ligeramente desequilibrados, con un promedio de 53 muestras por clase. La mayoría de las razas tienen casi la misma distribución en conjuntos de datos de tren, válidos y de prueba.

Además, como parte del procesamiento de datos, descalcifiqué las imágenes dividiendo cada píxel en cada imagen por 255.

Métricas

Dado que aquí estamos lidiando con un problema de clasificación múltiple y los datos están ligeramente desequilibrados, utilicé la evaluación de precisión métrica y la función de costo categorical_crossentropy.

Pero, primero, las etiquetas tienen que estar en un formato categórico. Los archivos de destino son la lista de etiquetas de perro codificadas relacionadas con la imagen con este formato.

Esta pérdida de registro de varias clases castiga al clasificador CNN si la probabilidad pronosticada lleva a una etiqueta diferente a la real y causa una mayor precisión. Un clasificador CNN perfecto tiene una pérdida de cero y una precisión del 100%.

Clasificar razas de perros

Aquí creé un CNN de 4 capas en Keras con la función de activación Relu. El modelo comienza con una imagen de entrada de 224 * 224 * 3 canales de color. Esta imagen de entrada es grande pero muy poco profunda, solo R, G y B.

Las capas de convolución comprimen las imágenes reduciendo el ancho y la altura al tiempo que aumentan la profundidad capa por capa. Al agregar más filtros, la red puede aprender características más significativas en las fotos y generalizar mejor.

Mi primera capa produce una salida con 16 canales de características que se utiliza como entrada para la siguiente capa. Los filtros son la colección de 16 matrices cuadradas, mapas de características de salida, que son sumas ponderadas de características de entrada y kernel.

Los pesos del núcleo se calculan durante el proceso de capacitación mediante datos de ImageNet, y lo que hace es deslizarse por los mapas de características de entrada y producir características de salida. Entonces, la forma de las características de salida depende del tamaño del núcleo y las características de entrada.

Consulte esta página para comprender mejor cómo funciona CNN.

Creo que sería ideal para la entrada y características de salida para tener el mismo tamaño. Entonces, decidí usar el mismo relleno para salir del borde de las imágenes con cero almohadillas para todas las capas con el paso de 2.

También utilicé la operación de agrupación máxima para asegurarme de que no estoy perdiendo información en la imagen al tiempo que disminuyo la posibilidad de sobreajuste.

La agrupación máxima toma el máximo de píxeles alrededor de una ubicación.Después de cuatro capas convolucionales y la agrupación máxima, seguidas de dos capas completamente conectadas, entrené al clasificador.

Las capas convolucionales extraen las características de la imagen y el clasificador las clasifica en función de las características obtenidas previamente.

La imagen a continuación muestra cómo la secuencia de bloques de características y clasificador CNN en la parte superior transfiere la información de la imagen en bruto y predice los valores objetivo solicitados.

La arquitectura del modelo Clasificador CNN es:

from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D
from keras.layers import Dropout, Flatten, Dense
from keras.models import Sequential

# Model Architecture
model = Sequential()
model.add(Conv2D(filters=16, kernel_size=2, padding='same',activation='relu',
input_shape=(224,224,3)))
model.add(MaxPooling2D(pool_size=2))

model.add(Conv2D(filters=32, kernel_size=2 , padding='same' , activation='relu'))
model.add(MaxPooling2D(pool_size=2))

model.add(Conv2D(filters=64 , kernel_size=2 , padding='same' , activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.4))

model.add(Conv2D(filters=128 , kernel_size=2 , padding='same' , activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.4))

model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(133,activation='softmax'))model.summary()

 

El modelo creado desde cero no funciona bien, precisión 12%, debido a que no tiene suficientes datos de imágenes para entrenar el modelo. Una mejora potencial es el aumento de datos para agregar más datos.

Esta estrategia modifica las imágenes rellenando, recortando y rotando imágenes al azar. También permite que el modelo se generalice mejor sin sobreajustar, por supuesto, con los parámetros apropiados.

Como se mencionó anteriormente, entrenar un clasificador CNN hecho desde cero en datos pequeños como este conducirá a un ajuste insuficiente y con tantas capas , y el ajuste de parámetros a menudo provoca un sobreajuste.

Por lo tanto, es hora de utilizar redes pre-entrenadas de aprendizaje de transferencia para crear un clasificador CNN de raza de perros a pesar de que estos modelos no están hechos explícitamente para esta tarea.

Pero una ventaja de estas redes es que están capacitadas en grandes conjuntos de datos, ImageNet con millones de imágenes etiquetadas, y alcanzaron el 90% de precisión. Además, pueden generalizarse a otras imágenes fuera de ImageNet.

Implementé los modelos previamente entrenados como VGG, Inception V3 y ResNet en Keras y comparé su precisión. Los utilicé como extractores de características fijas y ajusté la capa completamente conectada minimizando la función de pérdida de entropía cruzada utilizando el descenso de gradiente estocástico.

La tasa de aprendizaje de 0.001 y el impulso de Nesterov. Además, la última capa completamente conectada se cambia por el número de razas de perros, 133. Luego, cada modelo se entrenó en 25 épocas con 20 muestras por lote.

# model Architecture
Xception_model = Sequential()
Xception_model.add(GlobalAveragePooling2D(input_shape=(train_Xception.shape[1:])))
Xception_model.add(Dense(133, activation='softmax'))

checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.Xception.hdf5', verbose = 0, save_best_only=True)

sgd = SGD(lr= 1e-3 , decay=1e-6, momentum=0.9 , nesterov = True)

# compile the model
Xception_model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])

# Training the model
Xception_model.fit(train_Xception , train_targets,
validation_data = (valid_Xception, valid_targets),
shuffle = True,
batch_size = 20,
epochs = 25,
verbose = 1)

El modelo Xception supera a todos los demás modelos con una precisión del 86% y una pérdida de 0,4723 en los datos de prueba. En general, los modelos basados ​​en Inception superan ligeramente a VGG y ResNet en ImageNet; Además, es más eficiente computacionalmente. El rendimiento del modelo podría mejorarse aplicando técnicas de ajuste más apropiadas. Por ejemplo, entrenar algunas capas superiores relacionadas con características específicas del perro y congelar las otras que detectan características más genéricas. Debido a que ya están capturados en los pesos de ImageNet.

Resultado

Ahora, es hora de combinar los detectores y Xception-predict-breed, también, pruébelos en varias imágenes con diferentes tamaños y resoluciones. Como se mencionó anteriormente, la precisión se eligió como una medida de evaluación del modelo para este clasificador CNN. Este código toma una ruta a una imagen y primero determina si la imagen es un perro, humano o ninguno de los dos devuelve la raza que se asemeja predicha:

def Xception_predict_breed (img_path):
bottleneck_feature = extract_Xception(path_to_tensor(img_path))
predicted_vector = Xception_model.predict(bottleneck_feature)
return dog_names[np.argmax(predicted_vector)]

def display_img(img_path):
img = cv2.imread(img_path)
cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
imgplot = plt.imshow(cv_rgb)
return imgplot

def breed_identifier(img_path):
display_img(img_path)
prediction = Xception_predict_breed(img_path)
if dog_detector(img_path) == True:
print('picture is a dog')
return print (f"This dog is a {prediction}\n")

if face_detector(img_path) == True:
print('picture is a human')
return print (f"This person looks like a {prediction}\n")
else:
return print('The picture is neither dog nor human')

El resultado muestra que el modelo está funcionando bien. Por ejemplo, podría predecir American_staffordshire_terrier en los datos de prueba mientras se entrenaba en imágenes con diferentes brillos, tamaños y niveles de zoom.

Y el siguiente es el resultado en imágenes humanas:

La matriz de confusión de las predicciones en el Los datos de prueba se dan a continuación. Pero, no es fácil visualizar qué razas están más mal clasificadas.

Para una mejor comprensión de los casos mal clasificados, emparejé los valores predichos y reales. El modelo no pudo clasificarse entre Great_pyrenees y Kuvasz, que son blancos, grandes y esponjosos. Tampoco podía distinguir entre mastín y bullmastiff, que es una mezcla entre un bulldog y un mastín. Al agregar más datos, el modelo podría aprender características más específicas y distinguir entre estas razas similares, lo que es incluso confuso para los humanos.

Mejoras potenciales

La precisión del modelo podría mejorar mediante el aumento y la adición de datos más entrenamiento etiquetado datos ya que el modelo estaba ligeramente desequilibrado. Con la adquisición de más datos, los modelos Convnet serían más capaces de aprender las características de perro más relevantes y específicas de las imágenes.

Pero, requiere una cantidad considerable de tiempo y uso de memoria. Otra posible forma de mejorar la precisión del modelo es promediando las predicciones de varias redes para obtener la predicción final conocida como técnicas de conjunto.

Como mencioné antes, una mejora podría ser la aplicación de estrategias de ajuste más apropiadas para ajustar los pesos basados ​​en los datos. Además, un análisis adecuado del modelo final sería agregar algo de ruido a las imágenes y ver cómo funciona.

Conclusión

En este trabajo, he implementado el clasificador CNN de cascada de haar y ResNet50 pre pesas entrenadas para detectar rostros humanos y perros. También pasé un tiempo explorando los datos para encontrar las características de las imágenes, lo que me ayudó a aplicar técnicas más apropiadas.

Para la clasificación de razas de perros, comencé con algunos números de capas convolucionales como una red de referencia. El modelo funcionó mejor que una suposición aleatoria, 1 en 133, pero aún no se ajusta.

Con el tiempo y la potencia de memoria dados, decidí aprovechar varias arquitecturas de aprendizaje de transferencia para mejorar el rendimiento de CNN, lo que resultó en una mejora significativa.

Utilicé los modelos previamente entrenados como extractor de características fijas y cambié la capa de agrupación promedio global, la capa totalmente conectada y apliqué otros enfoques de ajuste fino.

Xception y ResNet50 produjeron mayor precisión que otros, alrededor del 86%. Es bastante difícil obtener una mayor precisión debido a la complejidad de este tipo de conjuntos de datos con cierta similitud entre razas mixtas.

Al final, probé el modelo en las mismas fotos etiquetadas con diferentes distribuciones de intensidad, y funcionó bastante

La ​​parte más emocionante de este proyecto fue cómo los modernos ConvNets con algunas líneas de códigos nos facilitaron hacer las tareas que incluso los humanos encuentran desafiantes.

Pero es bastante difícil mejorar el modelo con el tiempo y la potencia de memoria dados. Planeo explorar los enfoques de mejora mencionados y actualizaré este blog con mejores resultados.

Siéntase libre de usar este código si lo encuentra interesante.

Dejá un comentario