Reconocimiento de imágenes con Aprendizaje automático en Python, Red Neuronal Convolucional

fuente: the irish times, Pól Ó Muirí

Después de procesar previamente los datos, es hora de construir nuestro modelo para realizar la tarea de reconocimiento de imágenes. Una de las técnicas es usar la Red Neuronal de Convolución.

Este artículo sigue al artículo que escribí sobre procesamiento de imágenes. Después de hacer que los datos estén disponibles para la tarea de reconocimiento de imágenes, es hora de crear un algoritmo que realice la tarea. Entre las muchas técnicas utilizadas para reconocer imágenes como modelo perceptrón multicapa, la Red Neuronal de Convolución (CNN) aparece como una muy eficiente. En este artículo, veremos cómo crear una CNN y cómo aplicarla en un conjunto de datos de imágenes.

Cuando comenzamos a construir un modelo de reconocimiento de imágenes por primera vez, generalmente es una buena idea entrenarlo y evaluarlo en un conjunto de datos relativamente simple.

Una de las tareas más simples que podemos realizar es el reconocimiento de dígitos escritos a mano. Dada una imagen de un manuscrito dígitos (es decir,, 0, 1, …, 9), queremos que nuestro modelo sea capaz de clasificar correctamente su valor numérico. Aunque esta tarea parece relativamente simple, en realidad se usa con bastante frecuencia en la vida real, como extraer automáticamente números de tarjetas de crédito de una imagen. El conjunto de datos que utilizaremos para el reconocimiento de dígitos es el conjunto de datos MNIST, que es el conjunto de datos utilizado para el reconocimiento de dígitos basado en aprendizaje automático.

La base de datos del MNIST (Instituto Nacional de Normas y Tecnología Modificado) contiene 60.000 ejemplos de capacitación y 10.000 ejemplos de pruebas. La base de datos contiene dígitos escritos a mano en escala de grises que se redimensionaron para encajar en una caja de 20×20 píxeles, que luego se centró en una imagen de 28×28 (rellenada con espacios en blanco). La base de datos MNIST es accesible a través de Python.

En este artículo, le mostraré cómo codificar su Red Neuronal Convolucional utilizando keras, la API de alto nivel de TensorFlow. Estoy usando tensorflow 2.0 en este artículo.

1-Inicialización

Dado que cada imagen en escala de grises tiene dimensiones de 28×28, hay 784 píxeles por imagen. Por lo tanto, cada imagen de entrada corresponde a un tensor de 784 valores de punto flotante normalizados entre 0.0 y 1.0. La etiqueta de una imagen es un tensor de un solo calor con 10 clases (cada clase representa un dígito). En términos de nuestro código, tenemos img_rows = 28, img_cols = 28 y num_classes = 10. Por lo tanto, la entrada tiene forma (number_examples, img_rows, img_cols), por lo tanto, 60000x28x28. Otro elemento importante a configurar es la semilla aleatoria, ya que queremos mantener el punto de inicio cuando un equipo genera una secuencia de números aleatorios.

También importamos el conjunto de datos MNIST.

import tensorflow as tf # tensorflow 2.0
from keras.datasets import mnist
import numpy as npseed=0
np.random.seed(seed) # fix random seed
tf.random.set_seed(seed)# input image dimensions
num_classes = 10 # 10 digits
img_rows, img_cols = 28, 28 # number of pixels
# the data, shuffled and split between train and test sets
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

2-Remodelado y reescalado

Como se mencionó en la sección anterior, las entradas tienen forma (number_examples, img_rows, img_cols). Sin embargo, para usar los datos con nuestra red neuronal convolucional, necesitamos introducirlos en el formato NHWC.

El formato NHWC tiene una forma con cuatro dimensiones:

  1. Número de muestras de datos de imagen (tamaño de lote)
  2. Altura de cada imagen
  3. Ancho de cada imagen
  4. Canales por imagen

La altura y el ancho de cada imagen del conjunto de datos son img_rows e img_cols, mientras que el número de canales es 1 (ya que las imágenes son en escala de grises).

Además, cada píxel contiene un valor de escala de grises cuantificado por un entero entre 0 y 255. Por lo tanto, la base de datos se normaliza para tener valores de coma flotante entre 0.0 y 1.0. En este caso, 0,0 corresponde a un valor de píxel en escala de grises de 255 (blanco puro), mientras que 1.0 corresponde a un valor de píxel en escala de grises de 0 (negro puro).

X_train = X_train.reshape(X_train.shape, img_rows, img_cols, 1) 
X_test = X_test.reshape(X_test.shape, img_rows, img_cols, 1)input_shape = (img_rows, img_cols, 1)# cast floats to single precision
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')# rescale data in interval
X_train /= 255
X_test /= 255

3 vectores de etiquetas de fundición Y

Necesitamos transformar nuestras clases en vectores. Hacemos esto por tocar la línea siguiente:

Y_train = keras.utils.to_categorical(Y_train, num_classes)
Y_test = keras.utils.to_categorical(Y_test, num_classes)

Para tener una mejor explicación de este paso, usted debe ver a este artículo.

Ahora que hemos procesado nuestros datos, podemos empezar a construir el modelo.

Modelo de red neuronal de convolución

Como se mencionó al final del artículo que escribí sobre el procesamiento de imágenes, los filtros desempeñan un papel importante en el reconocimiento de imágenes. Utilizamos filtros para transformar entradas y extraer características que permiten a nuestro modelo reconocer ciertas imágenes. Un ejemplo de muy alto nivel de esto sería un filtro de detección de curvas, que permite a nuestro modelo distinguir entre dígitos con curvas y dígitos sin curvas.

1 – Filtros

Al igual que todos los pesos de redes neuronales, los pesos del filtro son variables entrenables. Entrenamos nuestra red neuronal (a través de los pesos de matriz del núcleo) para producir filtros que puedan extraer las características ocultas más útiles.

Cuando los datos de entrada tienen múltiples canales, un filtro tendrá una matriz de núcleo separada por canal. El conjunto de datos MNIST solo tiene un canal, pero para otros tipos de datos de imagen (por ejemplo, RGB), entrenaríamos el modelo para obtener pesos óptimos para la matriz del núcleo de cada canal.

2-Convolución

Hemos llegado al punto focal de las redes neuronales convolucionales: la convolución. La convolución representa cómo aplicamos nuestros pesos de filtro a los datos de entrada. La operación principal utilizada por una convolución es el producto de punto de matriz, es decir, una suma sobre el producto en cuanto a elementos de dos matrices.

El número de matriz de punto productos en una convolución depende de las dimensiones de los datos de entrada y el núcleo de la matriz, así como el tamaño de la zancada. El tamaño de zancada es el desplazamiento vertical / horizontal de la matriz del núcleo a medida que se mueve a lo largo de los datos de entrada.

3-Relleno

A veces, cuando hacemos la operación del producto escalar como se ha visto anteriormente, no usamos una fila o una columna. Para evitar este fenómeno podemos usar relleno.

Por lo tanto, si queremos usar todos los datos de entrada en nuestra convolución, podemos rellenar la matriz de datos de entrada con 0. Esto significa que agregamos filas/columnas hechas completamente de 0 a los bordes de la matriz de datos de entrada. Dado que 0 multiplicado por cualquier número da como resultado 0, el relleno no afecta a los productos de puntos de matriz. Esto es importante porque no queremos añadir distorsiones a nuestra convolución.

Capa de 4 convoluciones

Una capa de convolución en una CNN aplica varios filtros al tensor de entrada. Si bien cada filtro tiene una matriz de núcleo separada para cada uno de los canales de entrada, el resultado general de la convolución de un filtro es la suma de las convoluciones a través de todos los canales de entrada.

Agregar más filtros a una capa de convolución permite que la capa extraiga mejor las entidades ocultas. Sin embargo, esto tiene un costo de tiempo de entrenamiento adicional y complejidad computacional, ya que los filtros agregan pesos adicionales al modelo. El número de canales para los datos de salida es igual al número de filtros que utiliza la capa de convolución.

Agrupación

Mientras que la capa de convolución extrae importantes entidades ocultas, el número de entidades puede seguir siendo bastante grande. Podemos usar la agrupación para reducir el tamaño de los datos en las dimensiones de altura y anchura. Esto permite al modelo realizar menos cálculos y, en última instancia, entrenar más rápido. También evita el sobreajuste, extrayendo solo las características más sobresalientes e ignorando posibles distorsiones o características poco comunes que se encuentran en solo unos pocos ejemplos.

¿Cómo funciona la agrupación?

Similar a una convolución, usamos matrices de filtro en la agrupación. Sin embargo, el filtro de agrupación no tiene pesos, ni realiza productos matrix dot. En su lugar, aplica una operación de reducción a las subsecciones de los datos de entrada.

El tipo de pooling que se usa generalmente en CNNs se conoce como pooling máximo. Los filtros de max pooling utilizan la operación max para obtener el número máximo en cada submatriz de los datos de entrada.

Capas múltiples

1: Al agregar capas adicionales

Al igual que todas las redes neuronales, los CNN pueden beneficiarse de capas adicionales. Las capas adicionales permiten que una CNN apile esencialmente varios filtros para usarlos en los datos de la imagen. Sin embargo, al igual que en la construcción de cualquier red neuronal, debemos tener cuidado con cuántas capas adicionales agregamos. Si añadimos demasiadas capas a un modelo, corremos el riesgo de que se ajuste demasiado a los datos de entrenamiento y, por lo tanto, se generalice muy mal. Además, cada capa adicional agrega complejidad computacional y aumenta el tiempo de entrenamiento para nuestro modelo.

2 – Aumentar filtros

Normalmente aumentamos el número de filtros en una capa de convolución cuanto más profunda sea en nuestro modelo. En este caso, nuestra segunda capa de convolución tiene 64 filtros, en comparación con los 32 filtros de la primera capa de convolución. Cuanto más profunda sea la capa de convolución, más detalladas serán las características extraídas. Por ejemplo, la primera capa de convolución puede tener filtros que extraen características como líneas, bordes y curvas. Cuando llegamos al segundo nivel, los filtros de la capa de convolución ahora podrían extraer más características distintivas, como el ángulo agudo de un 77 o las curvas de intersección de un 88.

Capa totalmente conectada

1-Capa totalmente conectada

Aplicamos una capa totalmente conectada de tamaño 1024 (es decir, el número de neuronas en la capa) a los datos de salida de la segunda capa de agrupación. El número de unidades es algo arbitrario. Lo suficiente para ser poderoso,pero no tanto como para consumir demasiados recursos. El propósito de la capa completamente conectada es agregar las entidades de datos antes de convertirlas en clases. Esto permite que el modelo haga mejores predicciones que si acabáramos de convertir la salida de la agrupación directamente en clases.

2-Aplanamiento

Los datos que hemos estado utilizando en nuestro modelo son del formato NHWC. Sin embargo, para usar una capa completamente conectada, necesitamos que los datos sean una matriz, donde el número de filas represente el tamaño del lote y las columnas representen las entidades de datos. Esta vez necesitamos remodelar en la dirección opuesta y convertir de NHWC a una matriz 2-D.

Abandono

1-Coadaptación

La coadaptación se refiere a cuando varias neuronas en una capa extraen las mismas características ocultas, o muy similares, de los datos de entrada. Esto puede suceder cuando los pesos de conexión para dos neuronas diferentes son casi idénticos.

Cuando una capa completamente conectada tiene un gran número de neuronas, es más probable que ocurra la adaptación conjunta. Esto puede ser un problema por dos razones. En primer lugar, es un desperdicio de computación cuando tenemos neuronas redundantes que computan la misma salida. En segundo lugar, si muchas neuronas están extrayendo las mismas características, agrega más importancia a esas características para nuestro modelo. Esto conduce a un sobreajuste si las características extraídas duplicadas son específicas solo para el conjunto de entrenamiento.

2-Abandono

La forma en que minimizamos la coadaptación para capas completamente conectadas con muchas neuronas es aplicando abandono durante el entrenamiento. En dropout, apagamos aleatoriamente una fracción de las neuronas de una capa en cada paso de entrenamiento al reducir a cero los valores neuronales.

Capa Soft-max

Dado que hay 10 dígitos posibles que puede tener una imagen MNIST, utilizamos una capa de 10 neuronas completamente conectada para obtener las clases para cada clase de dígitos. La función Softmax se aplica a las clases para convertirlas en probabilidades por clase.

Construyendo el modelo

Ahora estamos listos para construir nuestro modelo. Aquí está el código:

from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten
from keras.layers import MaxPooling2D, Dropoutmodel = Sequential()#add model layers
model.add(Conv2D(32, kernel_size=(5, 5),
activation='relu',
input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))# add second convolutional layer with 20 filters
model.add(Conv2D(64, (5, 5), activation='relu'))
# add 2D pooling layer
model.add(MaxPooling2D(pool_size=(2, 2)))
# flatten data
model.add(Flatten())
# add a dense all-to-all relu layer
model.add(Dense(1024, activation='relu'))
# apply dropout with rate 0.5
model.add(Dropout(0.5))
# soft-max layer
model.add(Dense(num_classes, activation='softmax'))

El tipo de modelo que utilizaremos es Secuencial. Secuencial es la forma más fácil de construir un modelo en Keras. Le permite construir un modelo capa por capa.

Utilizamos el método add () para adjuntar capas a nuestro modelo. Para los propósitos de nuestro ejemplo introductorio, basta con centrarse en capas densas para simplificar. Cada capa Dense () acepta como primer argumento requerido un entero que especifica el número de neuronas. El tipo de función de activación para la capa se define mediante el argumento activation optional, cuya entrada es el nombre de la función de activación en formato de cadena. Los ejemplos incluyen relu, tanh, elu, sigmoid, softmax.

En esta red neuronal, tenemos 2 capas de convolución seguidas cada vez por una capa de agrupación. Luego aplanamos los datos para agregar una capa densa en la que aplicamos dropout con una tasa de 0.5. Finalmente, agregamos una capa densa para asignar cada imagen con la clase correcta.

Compilar el modelo

A continuación, necesitamos compilar nuestro modelo. La compilación del modelo requiere tres parámetros: optimizador, pérdida y métricas.

El optimizador controla la velocidad de aprendizaje. Usaremos ‘adam’ como nuestro optimizador. Adam es generalmente un buen optimizador para usar en muchos casos. El optimizador adam ajusta la velocidad de aprendizaje a lo largo del entrenamiento.

La tasa de aprendizaje determina la rapidez con la que se calculan los pesos óptimos para el modelo. Una tasa de aprendizaje menor puede llevar a pesos más precisos (hasta cierto punto), pero la reducción es el tiempo de computación.

Usaremos ‘categorical_crossentropy’ para nuestra función de pérdida. Esta es la opción más común para la clasificación. Una puntuación más baja indica que el modelo está funcionando mejor.

Para hacer las cosas aún más fáciles de interpretar, usaremos la métrica de ‘precisión’ para ver la puntuación de precisión en el conjunto de validación cuando entrenemos el modelo.

#compile model using accuracy to measure model performance
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=)

Entrenando al modelo

Ahora entrenaremos a nuestro modelo. Para entrenar, usaremos la función ‘fit ()’ en nuestro modelo con los siguientes parámetros: datos de entrenamiento (X_train), datos de destino (Y_train), datos de validación y el número de épocas.

Para nuestros datos de validación, utilizaremos el conjunto de pruebas que se nos proporciona en nuestro conjunto de datos, que hemos dividido en X_test e Y_test.

El número de épocas es el número de veces que el modelo recorrerá los datos. Cuantas más épocas corramos, más mejorará el modelo, hasta cierto punto. Después de ese punto, el modelo dejará de mejorar durante cada época. Para nuestro modelo, estableceremos el número de épocas en 3.

#train the model
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=3)

Evaluar el modelo

Ahora hemos capacitado a nuestro modelo podemos evaluar su rendimiento:

# evaluate the model
score = model.evaluate(X_test, Y_test, verbose=1)
# print performance
print()
print('Test loss:', score)
print('Test accuracy:', score)

por Lo tanto, contamos con una precisión de 99,3% y una pérdida de 0.025 en el conjunto de pruebas que es muy bueno. Aún podemos mejorar el modelo aumentando el número de épocas e introduciendo un tamaño de lote.

Hacer predicciones

Si desea ver las predicciones reales que nuestro modelo ha hecho para los datos de prueba, podemos usar la función predict_classes. También podemos hacer esto mediante el uso de la función predict, que dará una matriz con 10 números. Estos números son las probabilidades de que la imagen de entrada represente cada dígito (0-9). El índice de matriz con el número más alto representa la predicción del modelo. La suma de cada matriz es igual a 1 (ya que cada número es una probabilidad).

Para mostrar esto, mostraremos las predicciones para las primeras 4 imágenes en el conjunto de pruebas.

Nota: Si tenemos nuevos datos, podemos ingresar nuestros nuevos datos en la función predecir para ver las predicciones que hace nuestro modelo sobre los nuevos datos. Dado que no tenemos ningún dato nuevo invisible, mostraremos predicciones usando el conjunto de pruebas por ahora.

#predict first 4 images in the test set
model.predict_classes(X_test)

#predict first 4 images in the test set
model.predict(X_test)

We can see that our model predicted 7, 2, 1 and 0 for the first four images.

Let’s compare this with the actual results.

#actual results for first 4 images in test set
y_test

Los resultados muestran que las primeras cuatro imágenes también son 7, 2,1 y 0. Nuestro modelo predijo correctamente!

Modelo completo

import tensorflow as tf # tensorflow 2.0
from keras.datasets import mnist
import numpy as np
seed=0
np.random.seed(seed) # fix random seed
tf.random.set_seed(seed)
# input image dimensions
num_classes = 10 # 10 digitsimg_rows, img_cols = 28, 28 # number of pixels# the data, shuffled and split between train and test sets
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()X_train = X_train.reshape(X_train.shape, img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape, img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)# cast floats to single precision
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')# rescale data in interval
X_train /= 255
X_test /= 255Y_train = keras.utils.to_categorical(Y_train, num_classes)
Y_test = keras.utils.to_categorical(Y_test, num_classes)from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten
from keras.layers import MaxPooling2D, Dropout
model = Sequential()#add model layers
model.add(Conv2D(32, kernel_size=(5, 5),
activation='relu',
input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))
# add second convolutional layer with 20 filters
model.add(Conv2D(64, (5, 5), activation='relu'))
# add 2D pooling layer
model.add(MaxPooling2D(pool_size=(2, 2)))
# flatten data
model.add(Flatten())
# add a dense all-to-all relu layer
model.add(Dense(1024, activation='relu'))
# apply dropout with rate 0.5
model.add(Dropout(0.5))
# soft-max layer
model.add(Dense(num_classes, activation='softmax'))#compile model using accuracy to measure model performance
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=)#train the model
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=3)# evaluate the model
score = model.evaluate(X_test, Y_test, verbose=1)# print performance
print()
print('Test loss:', score)
print('Test accuracy:', score)#predict first 4 images in the test set
model.predict(X_test)model.predict_classes(X_test)#actual results for first 4 images in test set
Y_test

Para continuar

En este artículo, abordé la segunda parte del reconocimiento de imágenes que consiste en construir una Red Neuronal de Convolución.

Espero que hayas encontrado lo que viniste a buscar en este artículo y quédate conmigo para los próximos episodios de este viaje de reconocimiento de imágenes!

PD: Actualmente soy estudiante de Maestría en Ingeniería en Berkeley, y si desea discutir el tema, no dude en comunicarse conmigo. Aquí está mi correo electrónico.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.