TensorFlow: Preprocesados y preparación

Aprende las técnicas fundamentales de preprocesamiento de datos para TensorFlow. Desde normalización y limpieza hasta augmentación y transformación de datos, domina las estrategias que garantizan que tus modelos de redes neuronales reciban información ópt

Aprende TensorFlow GRATIS y certifícate

El preprocesado de datos constituye una fase crítica en cualquier proyecto de aprendizaje automático. Antes de que un modelo pueda aprender patrones útiles, los datos deben ser transformados a un formato adecuado, limpiados de inconsistencias y preparados para maximizar la eficacia del entrenamiento. TensorFlow proporciona un conjunto completo de herramientas que facilitan estas tareas, permitiendo construir pipelines de datos eficientes y optimizados.

API tf.data: la base del preprocesado en TensorFlow

La API tf.data es el núcleo del ecosistema de preprocesado en TensorFlow, diseñada para construir pipelines de datos eficientes, flexibles y con alto rendimiento. Esta API permite cargar datos desde diferentes fuentes, transformarlos y entregarlos al modelo en lotes, todo dentro del grafo de computación de TensorFlow, lo que optimiza el rendimiento y evita cuellos de botella.

import tensorflow as tf
import numpy as np

# Crear un dataset simple desde un tensor
datos = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
dataset = tf.data.Dataset.from_tensor_slices(datos)

# Aplicar transformaciones
dataset = dataset.map(lambda x: x * 2)  # Multiplicar cada elemento por 2
dataset = dataset.batch(3)  # Agrupar en lotes de 3 elementos

# Iterar sobre el dataset
for lote in dataset:
    print(lote.numpy())

La creación de un dataset puede realizarse desde diversas fuentes como tensores, archivos TFRecord, archivos de texto o generadores de Python. Una vez creado, podemos aplicar una serie de transformaciones encadenadas para preparar los datos según nuestras necesidades.

Carga de diferentes tipos de datos

Datos tabulares y numéricos

Los datos tabulares pueden cargarse desde diferentes formatos como CSV, Excel o bases de datos. TensorFlow facilita la carga directa de archivos CSV:

csv_dataset = tf.data.experimental.make_csv_dataset(
    file_pattern="datos.csv",
    batch_size=32,
    column_names=["caracteristica1", "caracteristica2", "etiqueta"],
    label_name="etiqueta",
    num_epochs=1
)

for features_batch, labels_batch in csv_dataset:
    print(f"Características: {features_batch}")
    print(f"Etiquetas: {labels_batch}")
    break  # Solo mostramos el primer lote

Para conjuntos de datos más complejos o que requieren un preprocesado específico, podemos utilizar bibliotecas como pandas para la carga inicial y luego convertirlos a objetos tf.data.Dataset:

import pandas as pd

# Cargar datos con pandas
df = pd.read_csv("datos.csv")

# Separar características y etiquetas
features = df.drop("etiqueta", axis=1).values
labels = df["etiqueta"].values

# Crear dataset de TensorFlow
dataset = tf.data.Dataset.from_tensor_slices((features, labels))

Imágenes

El preprocesado de imágenes es crucial en tareas de visión por computador. TensorFlow proporciona funciones específicas para cargar y transformar imágenes:

def cargar_imagen(ruta_archivo):
    # Leer el archivo
    img_raw = tf.io.read_file(ruta_archivo)
    # Decodificar la imagen
    img = tf.io.decode_image(img_raw, channels=3, expand_animations=False)
    # Redimensionar
    img = tf.image.resize(img, [224, 224])
    # Normalizar valores a [0, 1]
    img = tf.cast(img, tf.float32) / 255.0
    return img

# Crear una lista de rutas de archivos
rutas_imagenes = tf.data.Dataset.list_files("imagenes/*.jpg")

# Aplicar la función de carga a cada ruta
dataset_imagenes = rutas_imagenes.map(cargar_imagen, num_parallel_calls=tf.data.AUTOTUNE)

La API tf.image ofrece numerosas funciones para transformar imágenes, incluyendo operaciones de redimensionado, recorte, rotación, ajuste de brillo y contraste, y muchas más. Estas operaciones pueden integrarse perfectamente en el pipeline de datos.

Datos de texto

El procesamiento de texto requiere transformar palabras o caracteres en representaciones numéricas que los modelos puedan entender. TensorFlow proporciona herramientas específicas para esta tarea:

# Lista de frases de ejemplo
frases = ["Hola mundo", "Aprendizaje automático", "TensorFlow es genial"]
dataset_texto = tf.data.Dataset.from_tensor_slices(frases)

# Crear un tokenizador y ajustarlo a los datos
tokenizador = tf.keras.preprocessing.text.Tokenizer()
tokenizador.fit_on_texts(frases)

# Convertir texto a secuencias de índices
def tokenizar(texto):
    secuencias = tokenizador.texts_to_sequences([texto.numpy().decode('utf-8')])
    return tf.constant(secuencias[0])

# Aplicar la tokenización a cada elemento
dataset_texto = dataset_texto.map(
    lambda texto: tf.py_function(func=tokenizar, inp=[texto], Tout=tf.int32)
)

Para manejar secuencias de diferentes longitudes, necesitamos aplicar padding para que todas tengan la misma dimensión:

def aplicar_padding(secuencia):
    return tf.pad(
        secuencia, 
        [[0, 50 - tf.shape(secuencia)[0]]], 
        "CONSTANT", 
        constant_values=0
    )[:50]  # Truncar si es más larga que 50

dataset_texto = dataset_texto.map(aplicar_padding)

Limpieza y transformación de datos

Manejo de valores faltantes

Los valores faltantes son un problema común en conjuntos de datos reales. Para manejarlos, podemos utilizar diversas técnicas como:

def manejar_valores_faltantes(features):
    # Reemplazar NaN con un valor predeterminado
    features = {k: tf.where(tf.math.is_nan(v), tf.zeros_like(v), v) 
               for k, v in features.items()}
    
    # O imputar con la media (calculada previamente)
    medias = {"caracteristica1": 3.5, "caracteristica2": 2.7}
    features = {k: tf.where(tf.math.is_nan(v), tf.constant(medias[k], dtype=v.dtype), v) 
               for k, v in features.items()}
    
    return features

dataset = dataset.map(lambda x, y: (manejar_valores_faltantes(x), y))

Normalización y estandarización

La normalización de datos es esencial para muchos algoritmos de aprendizaje automático, ya que ayuda a que el proceso de entrenamiento sea más estable y eficiente:

def normalizar_min_max(x):
    # Normalización a [0, 1]
    min_val = tf.reduce_min(x)
    max_val = tf.reduce_max(x)
    return (x - min_val) / (max_val - min_val)

def estandarizar(x):
    # Estandarización (media 0, desviación estándar 1)
    media = tf.reduce_mean(x)
    desv_std = tf.math.reduce_std(x)
    return (x - media) / desv_std

# Aplicar normalización a cada característica
dataset = dataset.map(
    lambda x: {k: normalizar_min_max(v) for k, v in x.items()}
)

En la práctica, es recomendable calcular las estadísticas (mínimo, máximo, media, desviación estándar) sobre el conjunto de entrenamiento y aplicar las mismas transformaciones al conjunto de validación y prueba:

# Calcular estadísticas sobre el conjunto de entrenamiento
estadisticas = {}
for batch in dataset_entrenamiento:
    for key, tensor in batch.items():
        if key not in estadisticas:
            estadisticas[key] = {"min": float('inf'), "max": float('-inf'), 
                                "sum": 0, "count": 0, "sum_squared": 0}
        
        estadisticas[key]["min"] = min(estadisticas[key]["min"], tf.reduce_min(tensor))
        estadisticas[key]["max"] = max(estadisticas[key]["max"], tf.reduce_max(tensor))
        estadisticas[key]["sum"] += tf.reduce_sum(tensor)
        estadisticas[key]["count"] += tf.size(tensor)
        estadisticas[key]["sum_squared"] += tf.reduce_sum(tf.square(tensor))

# Calcular media y desviación estándar
for key in estadisticas:
    estadisticas[key]["mean"] = estadisticas[key]["sum"] / estadisticas[key]["count"]
    varianza = (estadisticas[key]["sum_squared"] / estadisticas[key]["count"]) - tf.square(estadisticas[key]["mean"])
    estadisticas[key]["std"] = tf.sqrt(varianza)

# Función de normalización usando las estadísticas calculadas
def normalizar_con_estadisticas(features):
    return {k: (v - estadisticas[k]["mean"]) / estadisticas[k]["std"] 
            for k, v in features.items()}

# Aplicar a los conjuntos de datos
dataset_entrenamiento = dataset_entrenamiento.map(normalizar_con_estadisticas)
dataset_validacion = dataset_validacion.map(normalizar_con_estadisticas)

Codificación de variables categóricas

Las variables categóricas deben transformarse en representaciones numéricas. TensorFlow ofrece diversas opciones para esta codificación:

# One-hot encoding
def one_hot_encoding(valor, num_categorias):
    return tf.one_hot(valor, depth=num_categorias)

# Ejemplo para una variable categórica con 5 posibles valores
dataset = dataset.map(
    lambda x, y: ({**x, "categoria_one_hot": one_hot_encoding(x["categoria"], 5)}, y)
)

# Embedding para categorías con alta cardinalidad
embedding_layer = tf.keras.layers.Embedding(input_dim=1000, output_dim=16)

def aplicar_embedding(x):
    categoria_embedding = embedding_layer(x["categoria"])
    return {**x, "categoria_embedding": categoria_embedding}

dataset = dataset.map(aplicar_embedding)

Partición de datos

La división adecuada del conjunto de datos en entrenamiento, validación y prueba es fundamental para evaluar correctamente el rendimiento del modelo:

def particionar_dataset(dataset, proporciones):
    """
    Particiona un dataset en múltiples subconjuntos según las proporciones dadas.
    
    Args:
        dataset: Dataset a particionar
        proporciones: Lista de proporciones para cada partición (debe sumar 1)
        
    Returns:
        Lista de datasets particionados
    """
    assert sum(proporciones) == 1.0, "Las proporciones deben sumar 1"
    
    # Obtener el tamaño total del dataset
    dataset = dataset.cache()
    tamanio = 0
    for _ in dataset:
        tamanio += 1
    
    # Crear índices aleatorios
    indices = tf.range(tamanio)
    indices = tf.random.shuffle(indices)
    
    # Calcular los puntos de corte
    puntos_corte = [0]
    for i in range(len(proporciones) - 1):
        puntos_cort
Empezar curso de TensorFlow

Lecciones de este módulo de TensorFlow

Lecciones de programación del módulo Preprocesados y preparación del curso de TensorFlow.