Clasificación de imágenes multiclase usando Transfer learning

La clasificación de imágenes es uno de los problemas de aprendizaje automático supervisado que tiene como objetivo categorizar las imágenes de un conjunto de datos en sus respectivas categorías o etiquetas. La clasificación de imágenes de varias razas de perros es un problema clásico de clasificación de imágenes. Por lo tanto, tenemos que clasificar más de una clase, por eso el nombre de clasificación multiclase y, en este artículo, haremos lo mismo utilizando un modelo preentrenado InceptionResNetV2 y personalizándolo.

Primero analicemos algunas de las terminologías.

Aprendizaje por transferencia: el aprendizaje por transferencia es un método popular de aprendizaje profundo que sigue el enfoque de usar el conocimiento que se aprendió en alguna tarea y aplicarlo para resolver el problema de la tarea de destino relacionada. Entonces, en lugar de crear una red neuronal desde cero, » transferimos » las características aprendidas que son básicamente los » pesos » de la red. Para implementar el concepto de transferencia de aprendizaje, hacemos uso de » modelos pre-entrenados «.

Necesidades para el aprendizaje de transferencia : las características de bajo nivel del modelo A (tarea A) deberían ser útiles para aprender el modelo B (tarea B).

 Modelo preentrenado: los modelos preentrenados son los modelos de aprendizaje profundo que se entrenan en conjuntos de datos muy grandes, se desarrollan y están disponibles por otros desarrolladores que desean contribuir a esta comunidad de aprendizaje automático para resolver tipos similares de problemas. Contiene los sesgos y pesos de la red neuronal que representan las características del conjunto de datos en el que se entrenó. Las características aprendidas son siempre transferibles. Por ejemplo, un modelo entrenado en un gran conjunto de datos de imágenes de flores contendrá características aprendidas como esquinas, bordes, forma, color, etc.

InceptionResNetV2: InceptionResNetV2 es una red neuronal convolucional que tiene 164 capas de profundidad, entrenada en millones de imágenes de la base de datos de ImageNet y puede clasificar imágenes en más de 1000 categorías, como flores, animales, etc. El tamaño de entrada de las imágenes es 299- por-299.

Descripción del conjunto de datos:

  • El conjunto de datos utilizado comprende 120 razas de perros en total.
  • Cada imagen tiene un nombre de archivo que es su identificación única.
  • Conjunto de datos de tren (train.zip) : contiene 10,222 imágenes que se utilizarán para entrenar nuestro modelo
  • Test dataset (test.zip) : contiene 10.357 imágenes que tenemos que clasificar en las respectivas categorías o etiquetas.
  • etiquetas.csv : contiene nombres de raza correspondientes a la identificación de la imagen.
  • sample_submission.csv : contiene la forma correcta de envío de la muestra que se debe realizar

Todos los archivos mencionados anteriormente se pueden descargar desde aquí .

NOTA : para un mejor rendimiento, use GPU.

Primero importamos todas las bibliotecas necesarias.

Python3

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
 
from sklearn.metrics import classification_report, confusion_matrix
 
# deep learning libraries
import tensorflow as tf
import keras
from keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras import applications
from keras.models import Sequential, load_model
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D, Flatten, Dense, Dropout
from keras.preprocessing import image
 
import cv2
 
import warnings
warnings.filterwarnings('ignore')

Cargar conjuntos de datos y carpetas de imágenes

Python3

from google.colab import drive
drive.mount("/content/drive")
 
# datasets
labels = pd.read_csv("/content/drive/My Drive/dog/labels.csv")
sample = pd.read_csv('/content/drive/My Drive/dog/sample_submission.csv')
 
# folders paths
train_path = "/content/drive/MyDrive/dog/train"
test_path = "/content/drive/MyDrive/dog/test"

Visualización de los primeros cinco registros del conjunto de datos de etiquetas para ver sus atributos.

Python3

labels.head()

Producción: 

Agregar la extensión ‘.jpg’ a cada identificación 

Esto se hace para obtener las imágenes de la carpeta, ya que el nombre de la imagen y la identificación son los mismos, por lo que agregar la extensión .jpg nos ayudará a recuperar las imágenes fácilmente.

Python3

def to_jpg(id):
    return id+".jpg"
 
 
labels['id'] = labels['id'].apply(to_jpg)
sample['id'] = sample['id'].apply(to_jpg)

Aumento de datos:

Es una técnica de preprocesamiento en la que aumentamos el conjunto de datos existente con versiones transformadas de las imágenes existentes. Podemos realizar escalado, rotaciones, aumento de brillo y otras transformaciones afines. Esta es una técnica útil ya que ayuda al modelo a generalizar bien los datos no vistos.

La clase ImageDataGenerator se utiliza para este propósito, que proporciona un aumento de datos en tiempo real.a 

Descripción de algunos de sus parámetros que se utilizan a continuación:

  • rescale : cambia la escala de los valores por el factor dado
  • volteo horizontal : voltear aleatoriamente las entradas horizontalmente.
  • validación_split : esta es la fracción de imágenes reservadas para la validación (entre 0 y 1).

Python3

# Data agumentation and pre-processing using tensorflow
gen = ImageDataGenerator(
                  rescale=1./255.,
                  horizontal_flip = True,
                  validation_split=0.2 # training: 80% data, validation: 20% data
                 )
 
train_generator = gen.flow_from_dataframe(
    labels, # dataframe
    directory = train_path, # images data path / folder in which images are there
    x_col = 'id',
    y_col = 'breed',
    subset="training",
    color_mode="rgb",
    target_size = (331,331), # image height , image width
    class_mode="categorical",
    batch_size=32,
    shuffle=True,
    seed=42,
)
 
 
validation_generator = gen.flow_from_dataframe(
    labels, # dataframe
    directory = train_path, # images data path / folder in which images are there
    x_col = 'id',
    y_col = 'breed',
    subset="validation",
    color_mode="rgb",
    target_size = (331,331), # image height , image width
    class_mode="categorical",
    batch_size=32,
    shuffle=True,
    seed=42,
)

Producción:  

Veamos cómo se ve un solo lote de datos.

Python3

x,y = next(train_generator)
x.shape # input shape of one record is (331,331,3) , 32: is the batch size

Producción:   

(32, 331, 331, 3)

Trazado de imágenes del conjunto de datos del tren

Python3

a = train_generator.class_indices
class_names = list(a.keys())  # storing class/breed names in a list
 
 
def plot_images(img, labels):
    plt.figure(figsize=[15, 10])
    for i in range(25):
        plt.subplot(5, 5, i+1)
        plt.imshow(img[i])
        plt.title(class_names[np.argmax(labels[i])])
        plt.axis('off')
 
 plot_images(x,y)

Producción:   

Construyendo nuestro modelo

Este es el paso principal donde se construye el modelo de convolución neuronal.

Python3

# load the InceptionResNetV2 architecture with imagenet weights as base
base_model = tf.keras.applications.InceptionResNetV2(
                     include_top=False,
                     weights='imagenet',
                     input_shape=(331,331,3)
                     )
 
base_model.trainable=False
# For freezing the layer we make use of layer.trainable = False
# means that its internal state will not change during training.
# model's trainable weights will not be updated during fit(),
# and also its state updates will not run.
 
model = tf.keras.Sequential([
        base_model,  
        tf.keras.layers.BatchNormalization(renorm=True),
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.Dense(512, activation='relu'),
        tf.keras.layers.Dense(256, activation='relu'),
        tf.keras.layers.Dropout(0.5),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dense(120, activation='softmax')
    ])

Normalización por lotes :

  • Es una técnica de normalización que se realiza a lo largo de mini lotes en lugar del conjunto de datos completo.
  • Se utiliza para acelerar el entrenamiento y usar tasas de aprendizaje más altas.
  • Mantiene la salida media cerca de 0 y la desviación estándar de salida cerca de 1.

GlobalAveragePooling2D :

  • Toma un tensor de tamaño (ancho de entrada) x (alto de entrada) x (canales de entrada) y calcula el valor promedio de todos los valores en toda la array (ancho de entrada) x (alto de entrada) para cada uno de los (canales de entrada).
  • La dimensionalidad de las imágenes se reduce al reducir el número de píxeles en la salida de la capa de red neuronal anterior.
  • Al usar esto, obtenemos un tensor de tamaño unidimensional (canales de entrada) como nuestra salida.
  • Operación de agrupación promedio global 2D. Aquí ‘Profundidad’ = ‘Filtros’

Capas densas : estas capas son capas regulares de redes neuronales totalmente conectadas conectadas después de las capas convolucionales.

Drop out layer : también se utiliza cuya función es soltar aleatoriamente algunas neuronas de la unidad de entrada para evitar el sobreajuste. El valor de 0,5 indica que se deben descartar 0,5 fracciones de neuronas.

Compilar el modelo:

Antes de entrenar nuestro modelo, primero debemos configurarlo y eso lo hace model.compile(), que define la función de pérdida, los optimizadores y las métricas para la predicción.

Python3

model.compile(optimizer='Adam',loss='categorical_crossentropy',metrics=['accuracy'])
# categorical cross entropy is taken since its used as a loss function for
# multi-class classification problems where there are two or more output labels.
# using Adam optimizer for better performance
# other optimizers such as sgd can also be used depending upon the model

Visualización de un informe resumido del modelo

Desplegando el resumen podemos comprobar nuestro modelo para confirmar que todo está como se esperaba.

Python3

model.summary()

Producción:  

Definición de devoluciones de llamada para preservar los mejores resultados:

Devolución de llamada: es un objeto que puede realizar acciones en varias etapas de entrenamiento (por ejemplo, al comienzo o al final de una época, antes o después de un solo lote, etc.).

Python3

early = tf.keras.callbacks.EarlyStopping( patience=10,
                                          min_delta=0.001,
                                          restore_best_weights=True)
# early stopping call back

Entrenamiento del modelo: significa que estamos encontrando un conjunto de valores para pesos y sesgos que tienen una pérdida baja en promedio en todos los registros.

Python3

batch_size=32
STEP_SIZE_TRAIN = train_generator.n//train_generator.batch_size
STEP_SIZE_VALID = validation_generator.n//validation_generator.batch_size
 
# fit model
history = model.fit(train_generator,
                    steps_per_epoch=STEP_SIZE_TRAIN,
                    validation_data=validation_generator,
                    validation_steps=STEP_SIZE_VALID,
                    epochs=25,
                    callbacks=[early]

Producción: 

Guardar el modelo

Podemos guardar el modelo para su uso posterior.

Python3

model.save("Model.h5")

Visualización del rendimiento del modelo.

Python3

# store results
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
 
 
# plot results
# accuracy
plt.figure(figsize=(10, 16))
plt.rcParams['figure.figsize'] = [16, 9]
plt.rcParams['font.size'] = 14
plt.rcParams['axes.grid'] = True
plt.rcParams['figure.facecolor'] = 'white'
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.ylabel('Accuracy')
plt.title(f'\nTraining and Validation Accuracy. \nTrain Accuracy:
          {str(acc[-1])}\nValidation Accuracy: {str(val_acc[-1])}')

Salida:   Texto (0.5, 1.0, ‘\nPrecisión de entrenamiento y validación. \nPrecisión de entrenamiento: 0.9448809027671814\nPrecisión de validación: 0.9022817611694336’)

Python3

# loss
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.title(f'Training and Validation Loss. \nTrain Loss:
          {str(loss[-1])}\nValidation Loss: {str(val_loss[-1])}')
plt.xlabel('epoch')
plt.tight_layout(pad=3.0)
plt.show()

Producción:   

También se trazó un gráfico lineal de entrenamiento frente a precisión y pérdida de validación. El gráfico indica que las precisiones de validación y entrenamiento fueron casi consistentes entre sí y superiores al 90 %. La pérdida del modelo CNN es un gráfico de retraso negativo que indica que el modelo se está comportando como se esperaba con una pérdida decreciente después de cada época.

Evaluación de la precisión del modelo

Python3

accuracy_score = model.evaluate(validation_generator)
print(accuracy_score)
print("Accuracy: {:.4f}%".format(accuracy_score[1] * 100))
 
print("Loss: ",accuracy_score[0])

Producción: 

Visualización de la imagen de prueba

Python3

test_img_path = test_path+"/000621fb3cbb32d8935728e48679680e.jpg"
 
img = cv2.imread(test_img_path)
resized_img = cv2.resize(img, (331, 331)).reshape(-1, 331, 331, 3)/255
 
plt.figure(figsize=(6,6))
plt.title("TEST IMAGE")
plt.imshow(resized_img[0])

Producción: 

Hacer predicciones sobre los datos de prueba

Python3

predictions = []
 
for image in sample.id:
    img = tf.keras.preprocessing.image.load_img(test_path +'/'+ image)
    img = tf.keras.preprocessing.image.img_to_array(img)
    img = tf.keras.preprocessing.image.smart_resize(img, (331, 331))
    img = tf.reshape(img, (-1, 331, 331, 3))
    prediction = model.predict(img/255)
    predictions.append(np.argmax(prediction))
 
my_submission = pd.DataFrame({'image_id': sample.id, 'label':  predictions})
my_submission.to_csv('submission.csv', index=False)
 
# Submission file ouput
print("Submission File: \n---------------\n")
print(my_submission.head()) # Displaying first five predicted output

Producción: 

Publicación traducida automáticamente

Artículo escrito por jaimabhagwati y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *