Usarás el conjunto de datos de MRI cerebral 3T para entrenar tu red. Para comprobar la eficacia del modelo, lo probarás con:
Imágenes de MRI 3T no vistas,
Imágenes de MRI 3T con ruido y
Un indicador cualitativo: la relación señal‑ruido de pico (PSNR) para evaluar el rendimiento de las reconstrucciones.
Este tutorial no entra en las particularidades del diagnóstico por imagen, sino que se centra en la parte de deep learning. Nota: el objetivo principal es la implementación práctica de autoencoders convolucionales. Si aún no conoces las redes neuronales convolucionales (CNN) ni los autoencoders, quizá te interese revisar los tutoriales de CNN y Autoencoder. La mejor parte es que cargarás volúmenes 3D como imágenes 2D para alimentarlas al modelo. En resumen, hoy verás:
Luego, el dataset de MRI cerebral: qué tipo de imágenes incluye, módulos a importar, cómo leerlas, crear un array con ellas, preprocesarlas para el modelo y, por último, explorarlas.
Implementación del autoencoder convolucional: ajustarás los datos preprocesados, visualizarás las curvas de pérdida de entrenamiento y validación, guardarás el modelo entrenado y, por último, predecirás sobre el conjunto de prueba.
Después, pondrás a prueba la robustez del modelo añadiendo ruido a las imágenes de test y evaluando su rendimiento de forma cuantitativa.
Por último, evaluarás tus predicciones con la métrica cuantitativa PSNR (peak signal‑to‑noise ratio) para medir el rendimiento del modelo.
Breve introducción a las imágenes de RM
En imagen médica se emplean distintos sistemas: desde equipos de RM abiertos con intensidades de campo magnético de 0,3 teslas (T), a sistemas para extremidades de hasta 1,0 T y escáneres de cuerpo completo de hasta 3,0 T (en uso clínico). El tesla es la unidad que mide la intensidad del campo magnético en RM. Los escáneres de RM de alto campo (7T, 11,5T) ofrecen mayor SNR (relación señal‑ruido) incluso con vóxeles más pequeños (parches o rejillas tridimensionales) y, por tanto, se prefieren para diagnósticos más precisos.
Un vóxel más pequeño implica mejor resolución, lo que puede ayudar al diagnóstico clínico. Sin embargo, la intensidad del campo magnético del escáner impone un límite inferior al tamaño del vóxel para mantener una buena SNR y preservar los detalles de la imagen. Pese a la superior calidad de imagen de 7T y 11,5T, rara vez se usan en producción por su coste.
Según trabajos recientes, hay ~20.000 escáneres de 3T frente a solo ~40 de 7T.
Entender el dataset de MRI cerebral 3T
El dataset de MRI cerebral está formado por volúmenes 3D; cada volumen contiene 207 cortes/imágenes de MRI obtenidas a diferentes alturas del cerebro. Cada corte tiene dimensiones 173 × 173. Las imágenes son en escala de grises de un solo canal. Hay 30 sujetos en total, cada uno con la RM de un paciente. El formato de imagen no es jpeg, png, etc., sino NIfTI. Más adelante verás cómo leer imágenes en formato NIfTI.
El conjunto contiene imágenes de RM en modalidad T1, tradicionalmente adecuadas para evaluar estructuras anatómicas. El dataset con el que trabajarás hoy es de MRI cerebral 3T.
El dataset es público y puedes descargarlo desde esta fuente.
Truco: si quieres aprender a implementar un perceptrón multicapa (MLP) para tareas de clasificación con el dataset MNIST, echa un vistazo a este tutorial.
Nota : antes de empezar, ten en cuenta que el modelo se entrenará en un sistema con GPU Nvidia 1080 Ti, procesador Xeon e5 GeForce y 32 GB de RAM. Si usas Jupyter Notebook, tendrás que añadir tres líneas de código para especificar el orden del dispositivo CUDA y los dispositivos visibles de CUDA con el módulo os.
En el siguiente código defines variables de entorno en el notebook con os.environ. Es recomendable hacerlo antes de inicializar Keras para limitar el backend TensorFlow a la primera GPU. Si el equipo con el que entrenas tiene la GPU en 0, usa 0 en lugar de 1. Puedes comprobarlo con un comando en tu terminal, por ejemplo: nvidia-smi
import os
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="1"#model will be trained on GPU 1Con la tecnología de
Importación de módulos
Primero, importa todos los módulos necesarios: cv2, numpy, matplotlib y, sobre todo, Keras, ya que será el framework que usarás hoy. Para leer imágenes en formato NIfTI, también necesitas el módulo nibabel.
import os
import cv2
from keras.layers import Input,Dense,Flatten,Dropout,merge,Reshape,Conv2D,MaxPooling2D,UpSampling2D,Conv2DTranspose
from keras.layers.normalization import BatchNormalization
from keras.models import Model,Sequential
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adadelta, RMSprop,SGD,Adam
from keras import regularizers
from keras import backend as K
Con la tecnología de
import numpy as np
import scipy.misc
import numpy.random as rng
from PIL import Image, ImageDraw, ImageFont
from sklearn.utils import shuffle
import nibabel as nib #reading MR imagesfrom sklearn.cross_validation import train_test_split
import math
import glob
from matplotlib import pyplot as plt
%matplotlib inline
Con la tecnología de
/usr/local/lib/python3.5/dist-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interfaceof the newCV iterators are different from that ofthis module. This module will be removed in0.20."This module will be removed in 0.20.", DeprecationWarning)Con la tecnología de
Carga de datos
Usarás el módulo glob, que devolverá una lista con todos los volúmenes de la carpeta que indiques.
Ya puedes cargar los volúmenes 3D con nibabel. Ten en cuenta que al cargar un volumen NIfTI, Nibabel no obtiene inmediatamente el array de imagen: espera a que se lo pidas con el método get_data().
Como te interesan los cortes 2D en lugar del 3D completo, inicializarás una lista en la que, cada vez que leas un volumen, iterarás por los 207 cortes del volumen 3D y añadirás cada corte a la lista.
Imprimamos también la forma de uno de los volúmenes 3D: debería ser 173 × 207 × 173 (coordenadas x, y, z).
Nota: usarás solo los 51 cortes centrales del cerebro, no los 207. Veamos cómo seleccionar y cargar esos cortes centrales.
for f inrange(len(ff)):
a = nib.load(ff[f])
a = a.get_data()
a = a[:,78:129,:]for i inrange(a.shape[1]):
images.append((a[:,i,:]))print(a.shape)Con la tecnología de
Las imágenes son en escala de grises de 173 × 173, así que antes de alimentar los datos al modelo conviene preprocesarlos. Primero, convierte cada imagen 173 × 173 en una matriz de 173 × 173 × 1 para poder pasarla a la red:
Paso importante: rellenar con ceros los bordes para tener dimensiones pares y poder reducir a la mitad al pasar por el modelo. Añadamos tres filas y tres columnas de ceros para obtener 176 × 176.
Después, es clave particionar los datos. Para que el modelo generalice bien, divide en entrenamiento y validación. Entrenarás con el 80% y validarás con el 20% restante.
Esto reduce el riesgo de sobreajuste, ya que validarás con datos no vistos durante el entrenamiento.
Puedes usar train_test_split de scikit‑learn para hacerlo correctamente:
from sklearn.model_selection import train_test_split
train_X,valid_X,train_ground,valid_ground = train_test_split(images,
images,
test_size=0.2,
random_state=13)Con la tecnología de
Nota: para esta tarea no necesitas etiquetas de entrenamiento ni prueba. Por eso pasas las imágenes de entrenamiento dos veces: actúan como entrada y como ground truth, igual que las etiquetas en clasificación.
Exploración de datos
Veamos cómo son las imágenes del dataset y confirmemos sus dimensiones tras añadir las tres filas y columnas extra usando el atributo .shape de NumPy:
# Shapes of training setprint("Dataset (images) shape: {shape}".format(shape=images.shape))Con la tecnología de
Como ves, los datos tienen forma 6210 × 176 × 176, pues hay 6210 muestras de matrices 176 × 176 × 1.
Ahora, veamos un par de imágenes de entrenamiento y validación:
plt.figure(figsize=[5,5])# Display the first image in training data
plt.subplot(121)
curr_img = np.reshape(train_X[0],(176,176))
plt.imshow(curr_img, cmap='gray')# Display the first image in testing data
plt.subplot(122)
curr_img = np.reshape(valid_X[0],(176,176))
plt.imshow(curr_img, cmap='gray')Con la tecnología de
Las dos salidas corresponden a entrenamiento y validación. Verás que son diferentes; será interesante comprobar si el autoencoder convolucional aprende las características y reconstruye correctamente.
Ya puedes definir la red y alimentar los datos. ¡Vamos al lío!
El autoencoder convolucional
Las imágenes son de 176 × 176 × 1 (un vector de 30.976 dimensiones). Convertirás la matriz en array, la reescalarás entre 0 y 1, la reformatearás a 176 × 176 × 1 y la pasarás como entrada a la red.
Usarás un tamaño de lote de 128; también podrías usar 256 o 512 según tu máquina, lo que influye en el aprendizaje y la precisión. Entrenarás 50 épocas.
batch_size =128
epochs =300
inChannel =1
x, y =176,176
input_img = Input(shape =(x, y, inChannel))Con la tecnología de
Como sabrás, un autoencoder tiene dos partes: encoder y decoder.
Encoder: 3 bloques convolucionales; cada bloque tiene una capa de convolución seguida de batch normalization. Tras el primer y segundo bloque, hay max‑pooling.
Primer bloque: 32 filtros de 3 × 3 y luego una capa de reducción (max‑pooling),
Segundo bloque: 64 filtros de 3 × 3 y otra reducción,
Último bloque del encoder: 128 filtros de 3 × 3.
Decoder: 2 bloques convolucionales; cada uno con convolución y batch normalization. Tras el primero y el segundo, hay upsampling.
Primer bloque: 128 filtros de 3 × 3 seguido de upsampling,
Segundo bloque: 64 filtros de 3 × 3 seguido de otro upsampling,
Capa final: 1 filtro de 3 × 3 para reconstruir la entrada de un canal.
Max‑pooling reduce a la mitad cada vez que lo aplicas; upsampling duplica cada vez que se aplica.
Nota: número y tamaño de filtros, capas, épocas de entrenamiento… son hiperparámetros. Ajústalos, prueba variaciones y mide el rendimiento: así desarrollarás tu intuición en deep learning.
defautoencoder(input_img):#encoder#input = 28 x 28 x 1 (wide and thin)
conv1 = Conv2D(32,(3,3), activation='relu', padding='same')(input_img)#28 x 28 x 32
conv1 = BatchNormalization()(conv1)
conv1 = Conv2D(32,(3,3), activation='relu', padding='same')(conv1)
conv1 = BatchNormalization()(conv1)
pool1 = MaxPooling2D(pool_size=(2,2))(conv1)#14 x 14 x 32
conv2 = Conv2D(64,(3,3), activation='relu', padding='same')(pool1)#14 x 14 x 64
conv2 = BatchNormalization()(conv2)
conv2 = Conv2D(64,(3,3), activation='relu', padding='same')(conv2)
conv2 = BatchNormalization()(conv2)
pool2 = MaxPooling2D(pool_size=(2,2))(conv2)#7 x 7 x 64
conv3 = Conv2D(128,(3,3), activation='relu', padding='same')(pool2)#7 x 7 x 128 (small and thick)
conv3 = BatchNormalization()(conv3)
conv3 = Conv2D(128,(3,3), activation='relu', padding='same')(conv3)
conv3 = BatchNormalization()(conv3)#decoder
conv4 = Conv2D(64,(3,3), activation='relu', padding='same')(conv3)#7 x 7 x 128
conv4 = BatchNormalization()(conv4)
conv4 = Conv2D(64,(3,3), activation='relu', padding='same')(conv4)
conv4 = BatchNormalization()(conv4)
up1 = UpSampling2D((2,2))(conv4)# 14 x 14 x 128
conv5 = Conv2D(32,(3,3), activation='relu', padding='same')(up1)# 14 x 14 x 64
conv5 = BatchNormalization()(conv5)
conv5 = Conv2D(32,(3,3), activation='relu', padding='same')(conv5)
conv5 = BatchNormalization()(conv5)
up2 = UpSampling2D((2,2))(conv5)# 28 x 28 x 64
decoded = Conv2D(1,(3,3), activation='sigmoid', padding='same')(up2)# 28 x 28 x 1return decoded
Con la tecnología de
Tras crear el modelo, compílalo con el optimizador RMSProp.
También debes especificar la función de pérdida mediante el argumento loss. Aquí será el error cuadrático medio, calculado píxel a píxel entre la predicción del lote y el ground truth:
autoencoder = Model(input_img, autoencoder(input_img))
autoencoder.compile(loss='mean_squared_error', optimizer = RMSprop())Con la tecnología de
Visualicemos las capas con summary: verás el número de parámetros (pesos y sesgos) por capa y el total del modelo.
¡Hora de entrenar el modelo con fit() de Keras! Entrenaremos 50 épocas. fit() devuelve un history; si lo guardas, podrás trazar después la pérdida de entrenamiento y validación para analizar el rendimiento visualmente.
Entrena el modelo
autoencoder_train = autoencoder.fit(train_X, train_ground, batch_size=batch_size,epochs=epochs,verbose=1,validation_data=(valid_X, valid_ground))Con la tecnología de
Train on 1224 samples, validate on 306 samples
Epoch 1/3001224/1224[==============================]- 7s - loss:0.1201- val_loss:0.0838
Epoch 2/3001224/1224[==============================]- 7s - loss:0.0492- val_loss:0.0534...
Epoch 299/3001224/1224[==============================]- 7s - loss:1.3101e-04- val_loss:6.1086e-04
Epoch 300/3001224/1224[==============================]- 7s - loss:1.0711e-04- val_loss:3.9641e-04Con la tecnología de
¡Listo! Has entrenado el modelo con este dataset durante 200 épocas. Ahora, representemos la pérdida de entrenamiento y validación para visualizar el rendimiento.
loss = autoencoder_train.history['loss']
val_loss = autoencoder_train.history['val_loss']
epochs =range(300)
plt.figure()
plt.plot(epochs, loss,'bo', label='Training loss')
plt.plot(epochs, val_loss,'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()Con la tecnología de
La pérdida de validación y de entrenamiento se mantienen alineadas. No hay sobreajuste: la pérdida de validación disminuye y apenas hay brecha entre ambas durante el entrenamiento.
Por tanto, la capacidad de generalización del modelo es buena.
Ahora, vamos a reconstruir las imágenes de test con predict() de Keras y ver qué tal rinde el modelo.
Guarda el modelo
Guardemos el modelo entrenado. Es un paso importante en deep learning: ¡los pesos son el corazón de tu solución!
Podrás cargar los pesos guardados en el mismo modelo y continuar el entrenamiento donde lo dejaste. Por ejemplo, si vuelves a entrenar, parámetros como pesos, sesgos y la pérdida no empezarán de cero.
Con una sola línea puedes guardar y cargar los pesos.
autoencoder = autoencoder.save_weights('autoencoder_mri.h5')Con la tecnología de
plt.figure(figsize=(20,4))print("Test Images")for i inrange(5):
plt.subplot(1,5, i+1)
plt.imshow(valid_ground[i,...,0], cmap='gray')
plt.show()
plt.figure(figsize=(20,4))print("Reconstruction of Test Images")for i inrange(5):
plt.subplot(1,5, i+1)
plt.imshow(pred[i,...,0], cmap='gray')
plt.show()Con la tecnología de
Como puedes observar, el modelo hace un gran trabajo reconstruyendo las imágenes de test. Al menos cualitativamente, las originales y las reconstruidas son casi idénticas.
Quizá podría mejorar un poco en algunos detalles locales de las imágenes 3T originales.
Predicción sobre imágenes 3T con ruido
Primero añadiremos ruido a las imágenes de validación con media cero y desviación estándar 0,03.
[a,b,c,d]= np.shape(valid_X)
mean =0
sigma =0.03
gauss = np.random.normal(mean,sigma,(a,b,c,d))
noisy_images = valid_X + gauss
Con la tecnología de
Es momento de predecir sobre las imágenes ruidosas. Veamos qué tal rinde el modelo pese a no haber visto ruido durante el entrenamiento.
plt.figure(figsize=(20,4))print("Noisy Test Images")for i inrange(5):
plt.subplot(1,5, i+1)
plt.imshow(noisy_images[i,...,0], cmap='gray')
plt.show()
plt.figure(figsize=(20,4))print("Reconstruction of Noisy Test Images")for i inrange(5):
plt.subplot(1,5, i+1)
plt.imshow(pred_noisy[i,...,0], cmap='gray')
plt.show()Con la tecnología de
Parece que el modelo se comporta bastante bien, ¿verdad? Las reconstrucciones se ven mejor que las imágenes con ruido, y eso sin haber sido entrenado para manejarlo.
Métrica cuantitativa: peak signal‑to‑noise ratio (PSNR)
El bloque PSNR calcula la relación señal‑ruido de pico, en decibelios (dB), entre dos imágenes. Es una medida habitual de la calidad entre la original y la reconstruida. Cuanto mayor sea el PSNR, mejor calidad tendrá la reconstrucción.
Primero, calculemos el rendimiento entre las imágenes de validación y sus reconstrucciones.
valid_pred = autoencoder.predict(valid_X)
mse = np.mean((valid_X - valid_pred)**2)
psnr =20* math.log10(1.0/ math.sqrt(mse))Con la tecnología de
print('PSNR of reconstructed validation images: {psnr}dB'.format(psnr=np.round(psnr,2)))Con la tecnología de
Cuantitativamente, la diferencia es de apenas 1,54 dB entre reconstrucciones sin ruido y con ruido. Y ni siquiera entrenaste el modelo para gestionar ruido. ¿No es sorprendente?
Sigue investigando
Este tutorial es un buen punto de partida para aprender a leer imágenes MRI en formato NIfTI, analizarlas, preprocesarlas y alimentarlas a un modelo usando un dataset cerebral 3T. Te mostró una aplicación práctica y potente de los autoencoders. Si has podido seguirlo, aunque sea con algo de esfuerzo, ¡enhorabuena!
Puedes experimentar con la arquitectura y tratar de mejorar las predicciones, tanto cuantitativa como cualitativamente. ¿Quizá añadiendo capas? ¿O entrenando más tiempo? Prueba combinaciones y comprueba si ayudan.
Aún queda mucho por explorar, así que ¿por qué no haces el curso de DataCamp Deep Learning in Python si aún no lo has hecho? Empezarás por lo básico y avanzarás hasta dominar el área: será un recurso imprescindible para aprender a trabajar con redes convolucionales en Python, detección de caras, objetos, etc.
Una guía completa para entender las CNN, su impacto en el análisis de imágenes y algunas estrategias clave para combatir el sobreajuste en aplicaciones robustas de CNN frente al aprendizaje profundo.
Este tutorial te introducirá en el concepto de detección de objetos en Python utilizando la biblioteca OpenCV y cómo puedes utilizarla para realizar tareas como la detección facial.
Usarás el conjunto de datos de MRI cerebral 3T para entrenar tu red. Para comprobar la eficacia del modelo, lo probarás con:
Este tutorial no entra en las particularidades del diagnóstico por imagen, sino que se centra en la parte de deep learning. Nota: el objetivo principal es la implementación práctica de autoencoders convolucionales. Si aún no conoces las redes neuronales convolucionales (CNN) ni los autoencoders, quizá te interese revisar los tutoriales de CNN y Autoencoder. La mejor parte es que cargarás volúmenes 3D como imágenes 2D para alimentarlas al modelo. En resumen, hoy verás:
Breve introducción a las imágenes de RM
En imagen médica se emplean distintos sistemas: desde equipos de RM abiertos con intensidades de campo magnético de 0,3 teslas (T), a sistemas para extremidades de hasta 1,0 T y escáneres de cuerpo completo de hasta 3,0 T (en uso clínico). El tesla es la unidad que mide la intensidad del campo magnético en RM. Los escáneres de RM de alto campo (7T, 11,5T) ofrecen mayor SNR (relación señal‑ruido) incluso con vóxeles más pequeños (parches o rejillas tridimensionales) y, por tanto, se prefieren para diagnósticos más precisos.
Un vóxel más pequeño implica mejor resolución, lo que puede ayudar al diagnóstico clínico. Sin embargo, la intensidad del campo magnético del escáner impone un límite inferior al tamaño del vóxel para mantener una buena SNR y preservar los detalles de la imagen.
Pese a la superior calidad de imagen de 7T y 11,5T, rara vez se usan en producción por su coste.
Según trabajos recientes, hay ~20.000 escáneres de 3T frente a solo ~40 de 7T.
Entender el dataset de MRI cerebral 3T
El dataset de MRI cerebral está formado por volúmenes 3D; cada volumen contiene 207 cortes/imágenes de MRI obtenidas a diferentes alturas del cerebro. Cada corte tiene dimensiones 173 × 173. Las imágenes son en escala de grises de un solo canal. Hay 30 sujetos en total, cada uno con la RM de un paciente. El formato de imagen no es jpeg, png, etc., sino NIfTI. Más adelante verás cómo leer imágenes en formato NIfTI.
El conjunto contiene imágenes de RM en modalidad T1, tradicionalmente adecuadas para evaluar estructuras anatómicas. El dataset con el que trabajarás hoy es de MRI cerebral 3T.
El dataset es público y puedes descargarlo desde esta fuente.
Truco: si quieres aprender a implementar un perceptrón multicapa (MLP) para tareas de clasificación con el dataset MNIST, echa un vistazo a este tutorial.
Nota : antes de empezar, ten en cuenta que el modelo se entrenará en un sistema con GPU Nvidia 1080 Ti, procesador Xeon e5 GeForce y 32 GB de RAM. Si usas Jupyter Notebook, tendrás que añadir tres líneas de código para especificar el orden del dispositivo CUDA y los dispositivos visibles de CUDA con el módulo os.
En el siguiente código defines variables de entorno en el notebook con os.environ. Es recomendable hacerlo antes de inicializar Keras para limitar el backend TensorFlow a la primera GPU. Si el equipo con el que entrenas tiene la GPU en 0, usa 0 en lugar de 1. Puedes comprobarlo con un comando en tu terminal, por ejemplo: nvidia-smi
Importación de módulos
Primero, importa todos los módulos necesarios: cv2, numpy, matplotlib y, sobre todo, Keras, ya que será el framework que usarás hoy.
Para leer imágenes en formato NIfTI, también necesitas el módulo nibabel.
Carga de datos
Usarás el módulo glob, que devolverá una lista con todos los volúmenes de la carpeta que indiques.
Imprimamos el primer elemento de la lista y comprobemos su longitud: en nuestro caso debería ser 30.
Ya puedes cargar los volúmenes 3D con nibabel. Ten en cuenta que al cargar un volumen NIfTI, Nibabel no obtiene inmediatamente el array de imagen: espera a que se lo pidas con el método get_data().
Como te interesan los cortes 2D en lugar del 3D completo, inicializarás una lista en la que, cada vez que leas un volumen, iterarás por los 207 cortes del volumen 3D y añadirás cada corte a la lista.
Imprimamos también la forma de uno de los volúmenes 3D: debería ser 173 × 207 × 173 (coordenadas x, y, z).
Nota: usarás solo los 51 cortes centrales del cerebro, no los 207. Veamos cómo seleccionar y cargar esos cortes centrales.
Analicemos la forma de un corte de esos 207.
Preprocesamiento de datos
Dado que images es una lista, usarás NumPy para convertirla en un array.
Comprueba la forma del array: la primera dimensión debería ser 207 × 30 = 6210 y las otras dos, 173 × 173.
Las imágenes son en escala de grises de 173 × 173, así que antes de alimentar los datos al modelo conviene preprocesarlos. Primero, convierte cada imagen 173 × 173 en una matriz de 173 × 173 × 1 para poder pasarla a la red:
A continuación, reescala los datos con normalización máx‑mín:
Verifica que el mínimo y el máximo tras el reescalado sean 0,0 y 1,0:
Paso importante: rellenar con ceros los bordes para tener dimensiones pares y poder reducir a la mitad al pasar por el modelo. Añadamos tres filas y tres columnas de ceros para obtener 176 × 176.
Después, es clave particionar los datos. Para que el modelo generalice bien, divide en entrenamiento y validación. Entrenarás con el 80% y validarás con el 20% restante.
Esto reduce el riesgo de sobreajuste, ya que validarás con datos no vistos durante el entrenamiento.
Puedes usar train_test_split de scikit‑learn para hacerlo correctamente:
Nota: para esta tarea no necesitas etiquetas de entrenamiento ni prueba. Por eso pasas las imágenes de entrenamiento dos veces: actúan como entrada y como ground truth, igual que las etiquetas en clasificación.
Exploración de datos
Veamos cómo son las imágenes del dataset y confirmemos sus dimensiones tras añadir las tres filas y columnas extra usando el atributo .shape de NumPy:
Como ves, los datos tienen forma 6210 × 176 × 176, pues hay 6210 muestras de matrices 176 × 176 × 1.
Ahora, veamos un par de imágenes de entrenamiento y validación:
Las dos salidas corresponden a entrenamiento y validación. Verás que son diferentes; será interesante comprobar si el autoencoder convolucional aprende las características y reconstruye correctamente.
Ya puedes definir la red y alimentar los datos. ¡Vamos al lío!
El autoencoder convolucional
Las imágenes son de 176 × 176 × 1 (un vector de 30.976 dimensiones). Convertirás la matriz en array, la reescalarás entre 0 y 1, la reformatearás a 176 × 176 × 1 y la pasarás como entrada a la red.
Usarás un tamaño de lote de 128; también podrías usar 256 o 512 según tu máquina, lo que influye en el aprendizaje y la precisión. Entrenarás 50 épocas.
Como sabrás, un autoencoder tiene dos partes: encoder y decoder.
Encoder: 3 bloques convolucionales; cada bloque tiene una capa de convolución seguida de batch normalization. Tras el primer y segundo bloque, hay max‑pooling.
Decoder: 2 bloques convolucionales; cada uno con convolución y batch normalization. Tras el primero y el segundo, hay upsampling.
Max‑pooling reduce a la mitad cada vez que lo aplicas; upsampling duplica cada vez que se aplica.
Nota: número y tamaño de filtros, capas, épocas de entrenamiento… son hiperparámetros. Ajústalos, prueba variaciones y mide el rendimiento: así desarrollarás tu intuición en deep learning.
Tras crear el modelo, compílalo con el optimizador RMSProp.
También debes especificar la función de pérdida mediante el argumento loss. Aquí será el error cuadrático medio, calculado píxel a píxel entre la predicción del lote y el ground truth:
Visualicemos las capas con summary: verás el número de parámetros (pesos y sesgos) por capa y el total del modelo.
¡Hora de entrenar el modelo con fit() de Keras! Entrenaremos 50 épocas. fit() devuelve un history; si lo guardas, podrás trazar después la pérdida de entrenamiento y validación para analizar el rendimiento visualmente.
Entrena el modelo
¡Listo! Has entrenado el modelo con este dataset durante 200 épocas. Ahora, representemos la pérdida de entrenamiento y validación para visualizar el rendimiento.
La pérdida de validación y de entrenamiento se mantienen alineadas. No hay sobreajuste: la pérdida de validación disminuye y apenas hay brecha entre ambas durante el entrenamiento.
Por tanto, la capacidad de generalización del modelo es buena.
Ahora, vamos a reconstruir las imágenes de test con predict() de Keras y ver qué tal rinde el modelo.
Guarda el modelo
Guardemos el modelo entrenado. Es un paso importante en deep learning: ¡los pesos son el corazón de tu solución!
Podrás cargar los pesos guardados en el mismo modelo y continuar el entrenamiento donde lo dejaste. Por ejemplo, si vuelves a entrenar, parámetros como pesos, sesgos y la pérdida no empezarán de cero.
Con una sola línea puedes guardar y cargar los pesos.
Predicción sobre datos de validación
Como no tienes un conjunto de test separado, usaremos la validación para predecir con el modelo recién entrenado.
Vas a predecir sobre las 306 imágenes de validación y a trazar algunas reconstrucciones para ver la calidad de los resultados.
Como puedes observar, el modelo hace un gran trabajo reconstruyendo las imágenes de test. Al menos cualitativamente, las originales y las reconstruidas son casi idénticas.
Quizá podría mejorar un poco en algunos detalles locales de las imágenes 3T originales.
Predicción sobre imágenes 3T con ruido
Primero añadiremos ruido a las imágenes de validación con media cero y desviación estándar 0,03.
Es momento de predecir sobre las imágenes ruidosas. Veamos qué tal rinde el modelo pese a no haber visto ruido durante el entrenamiento.
Parece que el modelo se comporta bastante bien, ¿verdad? Las reconstrucciones se ven mejor que las imágenes con ruido, y eso sin haber sido entrenado para manejarlo.
Métrica cuantitativa: peak signal‑to‑noise ratio (PSNR)
El bloque PSNR calcula la relación señal‑ruido de pico, en decibelios (dB), entre dos imágenes. Es una medida habitual de la calidad entre la original y la reconstruida. Cuanto mayor sea el PSNR, mejor calidad tendrá la reconstrucción.
Primero, calculemos el rendimiento entre las imágenes de validación y sus reconstrucciones.
Ahora, calculemos el PSNR entre las originales de validación y las imágenes predichas a partir de las ruidosas.
Cuantitativamente, la diferencia es de apenas 1,54 dB entre reconstrucciones sin ruido y con ruido. Y ni siquiera entrenaste el modelo para gestionar ruido. ¿No es sorprendente?
Sigue investigando
Este tutorial es un buen punto de partida para aprender a leer imágenes MRI en formato NIfTI, analizarlas, preprocesarlas y alimentarlas a un modelo usando un dataset cerebral 3T. Te mostró una aplicación práctica y potente de los autoencoders. Si has podido seguirlo, aunque sea con algo de esfuerzo, ¡enhorabuena!
Puedes experimentar con la arquitectura y tratar de mejorar las predicciones, tanto cuantitativa como cualitativamente. ¿Quizá añadiendo capas? ¿O entrenando más tiempo? Prueba combinaciones y comprueba si ayudan.
Aún queda mucho por explorar, así que ¿por qué no haces el curso de DataCamp Deep Learning in Python si aún no lo has hecho? Empezarás por lo básico y avanzarás hasta dominar el área: será un recurso imprescindible para aprender a trabajar con redes convolucionales en Python, detección de caras, objetos, etc.