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
- El problema: las redes comen números, no palabras
- Tokenización y vocabulario
- Longitudes distintas: padding y truncado
- Embeddings: de índices a significado
- La capa Embedding de Keras
- Prototipo: análisis de sentimiento con IMDB (Embedding + LSTM)
- Lectura TecnoMarket: el dashboard de atención al cliente
- 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 (
TextVectorizationaplica 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=Truepropaga 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 testCó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:
- Datos propios: reseñas reales de la tienda con etiquetas propias. Aquí el modelo pasa de binario a tres clases:
positiva,negativayurgente(menciona daños, fraude o plazos legales). Basta cambiar la cabeza del modelo:Dense(3, activation="softmax")con pérdidacategorical_crossentropy— la guía de activaciones/pérdidas por tipo de problema de 02-02 y 02-04 te dice exactamente qué tocar. - 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). - 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_sequencestrunca 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
- Pipeline a mano: con el vocabulario
{'': 0, '[UNK]': 1, 'producto': 2, 'excelente': 3, 'roto': 4, 'llego': 5}y longitud fijaL = 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 unEmbedding(6, 4)? - 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?
- 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ónenrutar_resenay por qué es el comportamiento correcto?
Soluciones
- 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]. TrasEmbedding(6, 4): forma(6, 4)— 6 pasos de tiempo con un vector de 4 números cada uno (con batch:(1, 6, 4)). - Embedding:
10 000 × 32 = 320 000. LSTM:4·64·(32+64+1) = 24 832. Dense:64 + 1 = 65. Total:344 897. El Embedding supone320 000 / 344 897 ≈ 93 %de los parámetros — por eso el tamaño del vocabulario domina el tamaño del modelo. - 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 devuelvedestino: revision_humanaconsugerencia: 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
- ¿Qué es Deep Learning?
- Historia y evolución del Deep Learning
- Aplicaciones de Deep Learning
- Conceptos básicos de redes neuronales
- Preparación del entorno de trabajo
Módulo 2: Fundamentos de Redes Neuronales
- Perceptrón y Perceptrón Multicapa
- Función de activación
- Propagación hacia adelante y hacia atrás
- Optimización y función de pérdida
- Tu primera red neuronal completa
Módulo 3: Redes Neuronales Convolucionales (CNN)
- Introducción a las CNN
- Capas convolucionales y de pooling
- Arquitecturas populares de CNN
- Aplicaciones de CNN en reconocimiento de imágenes
Módulo 4: Redes Neuronales Recurrentes (RNN)
- Introducción a las RNN
- LSTM y GRU
- Aplicaciones de RNN en procesamiento del lenguaje natural
- Secuencias y series temporales
Módulo 5: Técnicas Avanzadas en Deep Learning
- Redes Generativas Adversariales (GAN)
- Autoencoders
- Transfer Learning
- Regularización y técnicas de mejora
- Mecanismos de atención y Transformers
Módulo 6: Herramientas y Frameworks
- Introducción a TensorFlow
- Introducción a PyTorch
- Comparación de frameworks
- Entornos de desarrollo y recursos adicionales
- Guardar, cargar y desplegar modelos
Módulo 7: Proyectos Prácticos
- Clasificación de imágenes con CNN
- Generación de texto con RNN
- Detección de anomalías con Autoencoders
- Creación de una GAN para generación de imágenes
- Fine-tuning de un modelo preentrenado
