Redes neuronales con tensorflow
En este ejemplo vemos como entrenar una red neuronal para reconocer números escritos a mano. Este modelo permite:
- 📩 Dada una entrada de una imagen con un número dibujado.
- 📤 Devuelve el número presente en esa imagen de
0
a9
.
Empezamos importando todo lo que necesitamos. Ambos keras
y tensorflow
son los paquetes más usados para tareas relacionadas con machine learning e inteligencia artificial.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import numpy as np
import matplotlib.pyplot as plt
Ahora vamos a importar un conjunto de datos llamado MNIST. Este es un conjunto de 70.000 imágenes que contiene dígitos del 0
al 9
escritos a mano. Lo usaremos para entrenar nuestro modelo. Mostramos unos ejemplos de nuestro dataset para ver que pinta tienen.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
plt.figure()
for i in range(4):
plt.subplot(2, 2, i + 1)
plt.imshow(X_train[i], cmap='gray')
plt.title(f"Etiqueta: {y_train[i]}")
plt.axis('off')
plt.tight_layout()
plt.show()
Como puedes ver, todo son imágenes de 28x28 píxeles de dígitos escritos a mano. Tenemos un total de 70.000.
Para entender mejor nuestro dataset, puedes ver que tenemos dos tipos de variables.
- 🖼️
X_
: Es la imagen. Un vector con 28x28 píxeles en blanco y negro. - 🏷️
y_
: Es la etiqueta de la imagen. Es decir, si es un0
, un1
, etc.
Esto que estamos haciendo se llama aprendizaje supervisado, ya que sabemos a priori el contenido de cada imagen. Estas etiquetas las ha puesto un humano.
Por otro lado tenemos otros dos tipos de variables:
- 🏃🏼
train
: Son las imágenes que usaremos para entrenar nuestro modelo. De las 70.000 se toman 60.000 para entrenar. - 🧪
test
: Son las imágenes usadas para verificar que nuestro modelo funciona correctamente. No se usan para entrenar el modelo. Permiten evaluar si el modelo es capaz de generalizar y clasificar correctamente imágenes no vistas anteriormente.
Ahora que tenemos nuestros datos de test y entrenamiento, tenemos que realizar un pequeño procesado.
- Normalizamos los píxeles. Es decir, hacemos que el nivel de negro varíe entre 0 y 1 en vez de 0 y 255.
- Redimensionamos los datos, añadiendo una dimensión extra. En realidad, no cambia nada, ya que nuestras imágenes tienen un único canal, blanco y negro.
- Convertimos las etiquetas.
Por ejemplo, de3
a[0 0 0 1 0 0 0 0 0 0]
. Lo mismo pero expresado de otra forma.
X_train, X_test = X_train / 255.0, X_test / 255.0
X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
Ahora definimos la arquitectura de nuestro modelo. Se trata de una arquitectura típica de CNN (Convolutional Neural Network).
cnn_model = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
Dense(10, activation='softmax')
])
Compilamos el modelo, indicando la función de loss que vamos a usar. Esta función de loss indica como evaluar lo bien o mal que el modelo realiza la predicción. Entrenar un modelo consiste en buscar minimizar ese error.
Es importante notar que hasta ahora el modelo “está vacío” y aún no sirve para nada.
cnn_model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
Ahora ya podemos entrenar el modelo con fit
. Esto consiste en usar nuestros datos de entrenamiento para darle forma a nuestra red neuronal. Concretamente en calcular sus weights. Iterativamente se van buscando los weights que reducen el error, aplicando la función de loss indicada para mejorar el modelo que se adapta a nuestros datos.
cnn_model.fit(X_train, y_train,
epochs=5, batch_size=32,
validation_data=(X_test, y_test))
Después de unos minutos, tendremos nuestro modelo entrenado. Pero antes de usarlo, tenemos que evaluar como de bueno es. Imagina que entrenas un modelo que falla el 50% de las veces. Serviría de poco.
Con evaluate
podemos ver lo bueno que es nuestro modelo. Para ello usamos los datos de test, ya que son datos que el modelo no ha visto anteriormente. No sería justo usar datos ya conocidos por el modelo. Veamos que la accuracy es del 99.23%.
_, accuracy = cnn_model.evaluate(X_test, y_test, verbose=0)
print(f"La accuracy es: {accuracy * 100:.2f}%")
# La accuracy es: 99.23%
Por último, con predict
puedes usar el modelo para clasificar tu imagen. Hemos tomado una del set de test, pero podrías usar tu propia imagen.
predicciones = cnn_model.predict(X_test[0:2])
esperado = y_test[0:2]
for p, e in zip(predicciones, esperado):
prediccion = np.argmax(p)
print(f"Esperado: {e}. Predicción: {prediccion}")
# Esperado: 7. Predicción: 7
# Esperado: 2. Predicción: 2
Un apunte importante es que predict
no devuelve exactamente el número que hay en la imagen. Nos devuelve una lista con probabilidades. Nos dice que existe una probabilidad del:
0.0001
de que sea0
.0.003
de que sea1
.0.99
de que sea2
....
Con argmax
obtenemos la predicción que mayor probabilidad tiene. En este caso la mayor probabilidad es de que sea 2
. Por lo tanto aceptamos el 2
como resultado.
✏️ Ejercicios:
- Almacena el modelo entrenado en disco para que puedas usarlo sin tener que entrenarlo cada vez. Usa ese modelo almacenado en el inicio de tu script.
- Junta los datos de test y entrenamiento y entrena el modelo usando solo un 1% de los datos para validación. Indica si esto mejora o empeora la accuracy. Pista, usa
validation_split
enfit
. - Dibuja un número a mano en un papel. Haz una foto. Conviértelo a 28x28 píxeles y usa
predict
sobre él.