Clasificación de Imágenes: CNN + Python

En este blog post daremos creación a una Convolutional Neural Network utilizando Python bajo el uso de herramientas: TensorFlow + Keras para el procesamiento de imágenes. Es áltamente recomendable ejecutrar el código desde servicios que corran con GPU en la nube, esto para evitar las limitaciones en el hardware físico.

Se utilizó de referencia el directorio de imágenes llamado sportMINST, dentro del cual se contienen diversas imágenes de los deportes más populares en el mundo - según el criterio de internet - con alrededor de 5000 y 9000 imágenes de cada deporte. Estas imágenes se encuentran en un tamaño diminuto de 21x28 pixeles a color (en caso de probar con datasets de imágenes B&W recomendamos modificar lo parámetros de la red neuronal) y lo más interesante es que esas dimensiones son suficientes para el entrenamiento de nuestra red.

Entonces, como objetivo para nuestra máquina es aprender a clasificar una imagen nueva dada, dentro del conocimiento de deportes en su memoria.

Requisitos del Ejercicio
Python 3.6, o bien una versión superior del mismo. Añadido a esto, se requiere tener instalado Keras + TensorFlow como backend. Necesitarás descargar el ZIPFile con las imágenes (comprimidas) y decomprimirlas en el mismo directorio en donde ejecutarás el código. Al descomprimir, se crearán 10 subdirectorios con las imágenes, un directorio por deporte.

Dividiremos el set de datos en 80-20 para entrenamiento y para testing. A su vez, el conjunto de entrenamiento también lo subdividiremos en otro 80-20 para entrenamiento y Validación en cada iteración EPOCH de aprendizaje. Con esto en mente, la agenda sería importar, cargar, dividir, modelar, entrenar, revisar y con ello estamos del otro lado, ¿sencillo?

Import Libraries :white_check_mark:

import numpy as np
import os
import re
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
import keras
from keras.utils import to_categorical
from keras.models import Sequential,Input,Model
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.layers.normalization import BatchNormalization
from keras.layers.advanced_activations import LeakyReLU

Upload Images :warning:

Recordemos que la función mplt.imread(filepath) cargará a la memoria en un array de más de 70 mil imágenes, por lo que puede tomar varios minutos y consumirá algo de memoria RAM. O bien, usemos la nube.

dirname = os.path.join(os.getcwd(), 'sportimages')
imgpath = dirname + os.sep 

images = []
directories = []
dircount = []
prevRoot=''
cant=0

for root, dirnames, filenames in os.walk(imgpath):
    for filename in filenames:
        if re.search("\.(jpg|jpeg|png|bmp|tiff)$", filename):
            cant=cant+1
            filepath = os.path.join(root, filename)
            image = plt.imread(filepath)
            images.append(image)
            b = "reading..." + str(cant)
            print (b, end="\r")
            if prevRoot !=root:
                print(root, cant)
                prevRoot=root
                directories.append(root)
                dircount.append(cant)
                cant=0
dircount.append(cant)

dircount = dircount[1:]
dircount[0]=dircount[0]+1
print('total images on subdirs:',sum(dircount))

Labels and Classes :white_check_mark:

Con base en el uso de un algoritmo supervisado, crearemos etiquetas en labels bajo una escala numérica en identificación de cada deporte, de manera que en la entrada de datos durante el entrenamiento sabremos a qué deporte corresponde qué etiqueta. Posterior, convertimos las etiquetas e imágenes en NumpyArray con np.array().

labels=[]
indice=0
for cantidad in dircount:
    for i in range(cantidad):
        labels.append(indice)
    indice=indice+1
print("number of labels created: ",len(labels))

deportes=[]
indice=0
for directorio in directories:
    name = directorio.split(os.sep)
    print(indice , name[len(name)-1])
    deportes.append(name[len(name)-1])
    indice=indice+1

y = np.array(labels)
X = np.array(images, dtype=np.uint8) 

# find the unique numbers from the train labels
classes = np.unique(y)
nClasses = len(classes)
print('total number of outputs : ', nClasses)
print('output classes : ', classes)

Training Sets :warning:

De obvservar que shape de los arrays son de 21×28 y x(3), dado que se refiere a los tres canales de colores que tiene cada imagen: RGB. Pre-Procesamos el valor de los pixeles y lo normalizamos para que tengan un valor entre 0 y 1, por eso dividimos en 255. Por último en este bloque, subdividimos los datos en 80-20 para test y entrenamiento con train_test_split() y nuevamente en 80-20 el de training para obtener un subconjunto de validación.

# mezclar todo y crear los grupos de entrenamiento y testing
train_X,test_X,train_Y,test_Y = train_test_split(X,y,test_size=0.2)
print('Training data shape : ', train_X.shape, train_Y.shape)
print('Testing data shape : ', test_X.shape, test_Y.shape)

train_X = train_X.astype('float32')
test_X = test_X.astype('float32')
train_X = train_X / 255.
test_X = test_X / 255.

# change the labels from categorical to one-hot encoding
train_Y_one_hot = to_categorical(train_Y)
test_Y_one_hot = to_categorical(test_Y)

# display the change for category label using one-hot encoding
print('Original label:', train_Y[0])
print('After conversion to one-hot:', train_Y_one_hot[0])

train_X,valid_X,train_label,valid_label = train_test_split(train_X, train_Y_one_hot, test_size=0.2, random_state=13)

print(train_X.shape,valid_X.shape,train_label.shape,valid_label.shape)

Create Network :no_entry:

INIT_LR = 1e-3
epochs = 6
batch_size = 64

sport_model = Sequential()
sport_model.add(Conv2D(32, kernel_size=(3, 3),activation='linear',padding='same',input_shape=(21,28,3)))
sport_model.add(LeakyReLU(alpha=0.1))
sport_model.add(MaxPooling2D((2, 2),padding='same'))
sport_model.add(Dropout(0.5))

sport_model.add(Flatten())
sport_model.add(Dense(32, activation='linear'))
sport_model.add(LeakyReLU(alpha=0.1))
sport_model.add(Dropout(0.5)) 
sport_model.add(Dense(nClasses, activation='softmax'))

sport_model.summary()

sport_model.compile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.Adagrad(lr=INIT_LR, decay=INIT_LR / 100),metrics=['accuracy'])

O bien, mágia tecnológica.

image

Network Training :white_check_mark:

Con esta linea sport_model.fit() iniciaremos el entrenamiento y validación de nuestra máquina. Introduciremos miles de imágenes, pixeles, arrays, colores… filtros y la red se irá regulando sola, aprendiendo los mejores pesos para las más de 150.000 interconexiones para distinguir los deportes. Sí, es un proceso tardado según se vea o se ejecute.

sport_train_dropout = sport_model.fit(train_X, train_label, batch_size=batch_size,epochs=epochs,verbose=1,validation_data=(valid_X, valid_label))

# we save the net, to reuse it in the future, without having to retrain it again
sport_model.save("sports_mnist.h5py")

test_eval = sport_model.evaluate(test_X, test_Y_one_hot, verbose=1)

print('Test loss:', test_eval[0])
print('Test accuracy:', test_eval[1])

Results, or something like this :thinking:

Ya con nuestra red entrenada, es la hora de la verdad: ponerla a prueba con el set de imágenes para test que separamos al principio y que son muestras que nunca fueron vistas por la máquina. En el conjunto de testing vemos que alcanza una precisión del 84% reconociendo las imágenes de deportes.

15426/15426 [==============================] – 5s 310us/step
Test loss: 0.6687967825782881
Test accuracy: 0.8409179307662388

image

¡Cuéntame tu perspectiva del Machine Learning! :thinking: :robot:

4 Me gusta

@l20j45 @Carlosedjr Por áca esta próximo a detonarse una comunidad de amantes y personas involucradas en ML y IA.

1 me gusta

Excelente, actualmente no tengo tantas incursiones en machine learning, pero llegue a trabajar con open cv hace tiempo, seria interesante comenzar a trabajr con tecnologias como estas.