Ya dominas la maquinaria recurrente: células con memoria (LSTM/GRU), capas apiladas y bidireccionales. Pero hay un obstáculo previo antes de aplicarla a las reseñas de TecnoMarket: las redes neuronales solo procesan números, y una reseña es texto. Esta lección resuelve ese puente —tokenización, vocabulario, padding y, sobre todo, embeddings— y lo aplica al proyecto estrella del procesamiento del lenguaje natural (PLN/NLP): el análisis de sentimiento. Siguiendo la metodología del curso, primero construiremos un prototipo con el dataset público IMDB (reseñas de cine) y después lo leeremos en clave TecnoMarket: clasificar reseñas de clientes y alimentar el dashboard de atención al cliente. Cerraremos con un panorama de otras tareas NLP con RNN y sus límites, que motivan la evolución hacia la atención.

Contenido

  1. El problema: las redes comen números, no palabras
  2. Tokenización y vocabulario
  3. Longitudes distintas: padding y truncado
  4. Embeddings: de índices a significado
  5. La capa Embedding de Keras
  6. Prototipo: análisis de sentimiento con IMDB (Embedding + LSTM)
  7. Lectura TecnoMarket: el dashboard de atención al cliente
  8. Panorama: otras tareas NLP con RNN y sus límites

El problema: las redes comen números, no palabras

Todo lo que has entrenado hasta ahora recibía números: píxeles entre 0 y 255 (MNIST, 02-05), ventas, secuencias sintéticas. "La batería dura poco" no es un tensor. El pipeline estándar para convertir texto en entrada de red tiene tres etapas:

graph LR
    A["Texto crudo<br>'La batería dura poco'"] --> B["Tokenización<br>['la','bateria','dura','poco']"]
    B --> C["Índices del vocabulario<br>[5, 214, 89, 47]"]
    C --> D["Padding<br>[5, 214, 89, 47, 0, 0, 0, 0]"]
    D --> E["Embedding<br>matriz 8 × 32 de números reales"]
    E --> F["LSTM → clasificación"]

Vamos etapa por etapa.

Tokenización y vocabulario

Tokenizar es trocear el texto en unidades (tokens): lo más intuitivo, palabras. Después se construye un vocabulario: un diccionario que asigna a cada token un índice entero, normalmente ordenado por frecuencia (índices bajos = palabras más comunes).

from tensorflow.keras.layers import TextVectorization
import numpy as np

resenas = [
    "la bateria dura poco",
    "envio rapido y producto excelente",
    "el producto llego roto",
    "excelente calidad precio",
]

vectorizador = TextVectorization(
    max_tokens=1000,            # tamaño máximo del vocabulario
    output_sequence_length=8    # longitud fija de salida (padding/truncado automático)
)
vectorizador.adapt(resenas)     # aprende el vocabulario A PARTIR de los textos

print(vectorizador.get_vocabulary()[:8])
# ['', '[UNK]', 'producto', 'excelente', 'y', 'roto', 'rapido', 'precio']
print(vectorizador(["envio rapido y producto excelente"]).numpy())
# [[ 9  6  4  2  3  0  0  0]]

Detalles que importan:

  • El índice 0 se reserva para el padding y el 1 para [UNK] (unknown): cualquier palabra que no esté en el vocabulario (errores ortográficos, palabras raras) se mapea a [UNK]. Limitar el vocabulario (p. ej., a las 10 000 palabras más frecuentes) es un compromiso: menos parámetros a cambio de perder palabras raras.
  • La tokenización real también resuelve minúsculas, tildes y puntuación (TextVectorization aplica una estandarización por defecto). En modelos modernos se usan subpalabras (trocear "inalámbrico" en "in-alámbric-o"), pero la idea de fondo es la misma.

Longitudes distintas: padding y truncado

Las reseñas tienen longitudes distintas, pero un tensor es rectangular: todas las filas deben medir igual. La solución es fijar una longitud L:

  • Secuencias más cortas → se rellenan con ceros (padding).
  • Secuencias más largas → se cortan (truncado).

Elegir L es un compromiso: demasiado corta pierde el final de las reseñas largas (¡donde suele estar la conclusión!); demasiado larga desperdicia cómputo en ceros. Una regla práctica: mira el percentil 90-95 de las longitudes reales de tu corpus.

Embeddings: de índices a significado

Los índices [5, 214, 89, 47] ya son números, pero números arbitrarios: que "batería" sea 214 y "pila" sea 611 no dice nada de su parecido. Dos opciones para dárselos a la red:

One-hot Embedding
Representación de una palabra Vector de tamaño vocabulario con un único 1 (p. ej., 10 000 dims) Vector denso corto de números reales (p. ej., 32-300 dims)
Dimensión con vocab. de 10 000 10 000 32-300 (tú la eliges)
¿Captura similitud? No: todos los pares de palabras son equidistantes Sí: palabras de uso similar acaban con vectores cercanos
¿Se aprende? No, es fija Sí, por retropropagación como cualquier peso

Un embedding es una tabla de consulta: una matriz de forma (vocabulario, dimensión) donde la fila i es el vector de la palabra i. Esos vectores empiezan aleatorios y se aprenden durante el entrenamiento: si "roto" y "defectuoso" aparecen en contextos parecidos y predicen la misma etiqueta, la retropropagación empuja sus vectores a acercarse — el reparto de culpa (02-03) llega hasta la propia representación de cada palabra.

¿Te suena esta idea? Es la misma de 03-04: allí una CNN convertía cada imagen de producto en un vector de características y medíamos parecido entre productos con la similitud coseno. Un embedding de palabras es exactamente eso para el lenguaje: un espacio donde la geometría codifica el significado. Con embeddings bien entrenados, coseno(v_bateria, v_pila) es alto, coseno(v_bateria, v_alfombra) es bajo, y se observan regularidades famosas como v_rey − v_hombre + v_mujer ≈ v_reina.

# Similitud coseno entre filas de una matriz de embedding (repaso de 03-04)
def coseno(a, b):
    return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))

La capa Embedding de Keras

En Keras, la tabla es una capa más:

from tensorflow.keras import layers

emb = layers.Embedding(
    input_dim=10000,   # tamaño del vocabulario
    output_dim=32,     # dimensión de cada vector de palabra
    mask_zero=True     # opcional: marca el padding (índice 0) para que la LSTM lo ignore
)
# Entrada:  (batch, L)      índices enteros
# Salida:   (batch, L, 32)  un vector de 32 números por palabra
  • Parámetros: 10000 × 32 = 320 000. Suele ser la capa más grande del modelo — otra razón para limitar el vocabulario.
  • Fíjate en la forma de salida: (batch, pasos, características) — exactamente el formato 3D que esperan las capas recurrentes de 04-01. Embedding y LSTM encajan como piezas de Lego.
  • mask_zero=True propaga una "máscara" para que las capas siguientes no procesen los ceros del padding como si fueran palabras.

Prototipo: análisis de sentimiento con IMDB (Embedding + LSTM)

Metodología del curso: prototipo con dataset público primero. IMDB trae 50 000 reseñas de cine en inglés, etiquetadas como positivas (1) o negativas (0), ya tokenizadas a índices — perfecto para centrarnos en el modelo. Es una tarea many-to-one de manual.

import numpy as np
from tensorflow import keras
from tensorflow.keras import layers

VOCAB = 10000   # nos quedamos con las 10 000 palabras más frecuentes
L = 200         # longitud fija de las secuencias

# 1. Cargar y preparar
(x_train, y_train), (x_test, y_test) = keras.datasets.imdb.load_data(num_words=VOCAB)
x_train = keras.preprocessing.sequence.pad_sequences(x_train, maxlen=L)
x_test  = keras.preprocessing.sequence.pad_sequences(x_test,  maxlen=L)
print(x_train.shape)   # (25000, 200): 25 000 reseñas de 200 índices

# 2. Modelo: Embedding -> LSTM -> decisión
modelo = keras.Sequential([
    layers.Input(shape=(L,)),
    layers.Embedding(VOCAB, 32, mask_zero=True),  # (200,) -> (200, 32)
    layers.LSTM(64),                              # lee la reseña, resume en 64 números
    layers.Dense(1, activation="sigmoid")         # probabilidad de "positiva"
])
modelo.compile(optimizer="adam",
               loss="binary_crossentropy",        # BCE: clasificación binaria (02-04)
               metrics=["accuracy"])
modelo.summary()
# Embedding: 320 000 params | LSTM: 4·64·(32+64+1) = 24 832 | Dense: 65

# 3. Entrenar y evaluar
historia = modelo.fit(x_train, y_train, epochs=4, batch_size=64,
                      validation_split=0.2)
print(modelo.evaluate(x_test, y_test))
# Típico: ~86-88 % de accuracy en test

Cómo leer los resultados, como hicimos con MNIST en 02-05:

  • La sigmoide final da la probabilidad de reseña positiva; con BCE como pérdida, es la receta estándar de clasificación binaria de 02-04.
  • Vigila las curvas: en IMDB, hacia la época 3-4 la accuracy de entrenamiento sigue subiendo pero la de validación se estanca o baja — el modelo empieza a memorizar. Detente ahí (las técnicas sistemáticas contra el sobreajuste llegan en 05-04).
  • Un 87 % con un modelo tan pequeño es notable: el azar daría 50 %. Los errores restantes suelen ser reseñas con ironía, negaciones complejas o sentimiento mixto — justo dependencias largas y sutiles.

Lectura TecnoMarket: el dashboard de atención al cliente

Segundo paso de la metodología: trasladarlo a los datos (ficticios) de TecnoMarket. El equipo de atención al cliente recibe cientos de reseñas y mensajes al día; el objetivo es priorizarlos automáticamente.

Cambios respecto al prototipo:

  1. Datos propios: reseñas reales de la tienda con etiquetas propias. Aquí el modelo pasa de binario a tres clases: positiva, negativa y urgente (menciona daños, fraude o plazos legales). Basta cambiar la cabeza del modelo: Dense(3, activation="softmax") con pérdida categorical_crossentropy — la guía de activaciones/pérdidas por tipo de problema de 02-02 y 02-04 te dice exactamente qué tocar.
  2. Vocabulario propio en español: se aprende con TextVectorization.adapt() sobre el corpus de TecnoMarket ("no enfría", "pantalla rota", "devolución" deben estar en el vocabulario).
  3. Umbral de confianza y cola de revisión humana, la misma arquitectura de despliegue que montamos para las fotos de producto en 03-04:
def enrutar_resena(texto, modelo, vectorizador, umbral=0.80):
    """Clasifica una reseña y decide su destino en el dashboard."""
    x = vectorizador([texto])
    probs = modelo.predict(x, verbose=0)[0]     # p. ej. [0.05, 0.15, 0.80]
    clase = ["positiva", "negativa", "urgente"][int(np.argmax(probs))]
    confianza = float(probs.max())
    if confianza < umbral:
        return {"destino": "revision_humana", "sugerencia": clase,
                "confianza": confianza}
    if clase == "urgente":
        return {"destino": "bandeja_prioritaria", "confianza": confianza}
    return {"destino": f"archivo_{clase}", "confianza": confianza}

El principio es el mismo que en 03-04: el modelo automatiza los casos claros y deriva los dudosos a personas. En atención al cliente esto no es opcional: clasificar mal una reseña urgente (una nevera que gotea sobre una regleta) cuesta mucho más que una revisión manual de más. Por eso, más allá de la accuracy, aquí interesa especialmente no dejar escapar urgencias aunque cueste algún falso positivo.

Panorama: otras tareas NLP con RNN y sus límites

El análisis de sentimiento es many-to-one, pero las RNN cubrieron durante años casi todo el NLP:

Tarea Topología Ejemplo TecnoMarket
NER (reconocimiento de entidades) Many-to-many sincronizada Marcar en cada reseña qué tokens son producto, marca o número de pedido, para enlazarla con el catálogo
Traducción automática Seq2seq (many-to-many desfasada) Traducir reseñas de la tienda internacional al español para el equipo de soporte
Resumen Seq2seq Condensar 40 reseñas de un producto en tres frases para la ficha interna

El esquema seq2seq merece dos líneas conceptuales: un encoder (una LSTM) lee la frase de origen entera y la comprime en su estado final; un decoder (otra LSTM) genera la frase destino palabra a palabra a partir de ese estado. Elegante… y con un cuello de botella evidente: toda la frase de origen, tenga 5 o 60 palabras, debe caber en un único vector de estado. Con frases largas, el decoder "olvida" el principio, por muy LSTM que sea. Además, la recurrencia obliga a procesar palabra a palabra en orden, sin paralelizar.

La solución histórica fue permitir que el decoder "mirara atrás" a todas las posiciones del encoder y decidiera a cuál atender en cada paso: el mecanismo de atención, que acabó eliminando la recurrencia por completo y dando lugar a los transformers. No lo desarrollamos aquí: es el tema de la lección 05-05. La generación de texto con RNN (escribir carácter a carácter) tampoco la practicamos ahora: es el proyecto guiado 07-02.

Errores Comunes y Consejos

  • Aprender el vocabulario (o el vectorizador) con datos de test. adapt() debe ejecutarse solo sobre el conjunto de entrenamiento; si no, informaciones del test se cuelan en el modelo y la evaluación queda inflada. Es el mismo principio de fuga de datos que veremos con los scalers en 04-04.
  • Truncar por el lado equivocado. pad_sequences trunca por el principio por defecto (truncating='pre'). En reseñas, la conclusión suele estar al final, así que ese default suele convenir; pero decide conscientemente, no por accidente.
  • Vocabulario desmesurado. 100 000 palabras × 128 dims = 12,8 M de parámetros solo en el Embedding, la mayoría de palabras vistas 2 o 3 veces (imposibles de aprender bien). Recorta a las frecuentes y deja que [UNK] absorba el resto.
  • Olvidar mask_zero=True (o equivalente) con mucho padding. Si la mitad de cada secuencia son ceros y la LSTM los procesa como palabras, diluyes la señal. Con máscara, la red los salta.
  • Evaluar solo con accuracy en clases desbalanceadas. Si el 90 % de reseñas son positivas, un modelo que siempre dice "positiva" tiene 90 % de accuracy y cero utilidad. Mira la matriz de confusión por clase, especialmente el recall de urgente.

Ejercicios

  1. Pipeline a mano: con el vocabulario {'': 0, '[UNK]': 1, 'producto': 2, 'excelente': 3, 'roto': 4, 'llego': 5} y longitud fija L = 6, convierte en secuencia de índices la reseña "llego roto el producto" (la palabra "el" no está en el vocabulario). ¿Qué forma tendrá la salida tras un Embedding(6, 4)?
  2. Recuento de parámetros: calcula los parámetros totales del modelo IMDB de la lección (Embedding 10 000×32, LSTM(64) con entrada 32, Dense(1) sobre 64). ¿Qué porcentaje corresponde al Embedding?
  3. Diseño TecnoMarket: el dashboard clasifica en positiva/negativa/urgente con umbral 0.80. Para la reseña X el modelo devuelve [0.42, 0.45, 0.13]. ¿Qué hace la función enrutar_resena y por qué es el comportamiento correcto?

Soluciones

  1. Tokens: ['llego', 'roto', 'el', 'producto'] → índices [5, 4, 1, 2] ("el" → [UNK] = 1). Con padding a 6 (relleno al final): [5, 4, 1, 2, 0, 0]. Tras Embedding(6, 4): forma (6, 4) — 6 pasos de tiempo con un vector de 4 números cada uno (con batch: (1, 6, 4)).
  2. Embedding: 10 000 × 32 = 320 000. LSTM: 4·64·(32+64+1) = 24 832. Dense: 64 + 1 = 65. Total: 344 897. El Embedding supone 320 000 / 344 897 ≈ 93 % de los parámetros — por eso el tamaño del vocabulario domina el tamaño del modelo.
  3. La clase más probable es negativa (0.45), pero la confianza máxima (0.45) queda muy por debajo del umbral 0.80, así que devuelve destino: revision_humana con sugerencia: negativa. Es lo correcto: el modelo está esencialmente indeciso entre positiva y negativa (0.42 vs. 0.45) — probablemente una reseña mixta o irónica — y decidir automáticamente sería apostar a cara o cruz; una persona resuelve el caso en segundos.

Conclusión

Has cerrado el puente entre el lenguaje y las redes: tokenización y vocabulario para trocear e indexar, padding para rectangular, y embeddings —vectores densos aprendidos donde la cercanía geométrica captura el parecido semántico, como los embeddings de imágenes de 03-04— para dar significado a los índices. Con Embedding + LSTM has construido un clasificador de sentimiento del 87 % sobre IMDB y lo has trasladado al dashboard de TecnoMarket con tres clases y el umbral de confianza con revisión humana de 03-04. También has visto el mapa de tareas NLP (NER, seq2seq para traducción y resumen) y el cuello de botella del vector único que motivó la atención y los transformers (05-05). Queda el segundo proyecto estrella del módulo: las secuencias de TecnoMarket que no son palabras sino números con fecha — la predicción de la demanda diaria, donde nos esperan las series temporales, las ventanas deslizantes y algún error clásico que aprenderemos a esquivar.

Curso de Deep Learning

Módulo 1: Introducción a Deep Learning

Módulo 2: Fundamentos de Redes Neuronales

Módulo 3: Redes Neuronales Convolucionales (CNN)

Módulo 4: Redes Neuronales Recurrentes (RNN)

Módulo 5: Técnicas Avanzadas en Deep Learning

Módulo 6: Herramientas y Frameworks

Módulo 7: Proyectos Prácticos

Módulo 8: Consideraciones Éticas y Futuro del Deep Learning

© Copyright 2026. Todos los derechos reservados