Construir un modelo de clasificación de texto en PyTorch implica ensamblar capas como bloques de Lego: un embedding, una normalización por lotes y una capa lineal final. Aprenderás a definir esta arquitectura paso a paso, entender por qué cada componente importa y cómo inicializar el modelo con los hiperparámetros correctos. Es ideal si ya diste tus primeros pasos con PyTorch y quieres construir un modelo real, pequeño pero eficiente.
Qué librerías necesitas para crear el modelo de clasificación
Antes de escribir la clase, necesitas tres importaciones clave en tu notebook de Google Colab [00:25].
- torch: el núcleo del framework.
- torch.nn: el módulo con clases listas para construir redes neuronales.
- torch.nn.functional: contiene funciones de bajo nivel que sirven de base para las clases de
nn. Por convención se importa con el alias F.
La diferencia es importante. La clase Linear, por ejemplo, se construye internamente con funciones de functional. En este modelo usarás F específicamente para aplicar la función de activación ReLU sobre los tensores normalizados.
¿Qué es torch.nn.functional? Es un módulo de PyTorch con funciones fundamentales (como ReLU, softmax o convoluciones) que las clases de nn usan por debajo. Te da control fino cuando quieres aplicar una operación sin instanciar una capa completa.
Cómo se define la arquitectura del modelo paso a paso
La clase ModeloClasificacionTexto hereda de nn.Module, igual que en clases anteriores, pero esta vez ensambla un modelo real [01:10].
En el __init__ pides al usuario cuatro datos al inicializar: el tamaño del vocabulario (vocab_size), las dimensiones del embedding (embed_dim) y el número de clases (num_class). Con esos parámetros defines tres bloques.
Qué hace cada bloque de la red neuronal
- Capa de embedding con
nn.EmbeddingBag(vocab_size, embed_dim): convierte tokens en vectores densos [02:30].
- Batch normalization con
nn.BatchNorm1d(embed_dim): normaliza los vectores del embedding [03:00].
- Capa fully connected con
nn.Linear(embed_dim, num_class): proyecta las dimensiones del embedding al número final de clases [04:20].
La capa lineal es la que hace la magia de la clasificación. Si tu embedding tiene 100 dimensiones y tienes 14 clases, esta capa proyecta de 100 a 14, donde cada dimensión final representa una clase posible.
Por qué usar batch normalization en tu modelo
Normalizar significa tomar un grupo de datos, calcular su media y varianza, y transformarlos a media cero y varianza uno. Esto compacta valores muy grandes o muy pequeños y trae tres beneficios concretos.
- Acelera el entrenamiento.
- Hace el procesamiento más eficiente.
- Tiene un efecto regularizador que ayuda a prevenir overfitting.
Por eso encontrarás batch normalization en prácticamente todos los modelos modernos.
¿Qué es normalizar en deep learning? Es transformar un grupo de tensores para que tengan media cero y varianza uno. Compacta los datos, acelera el entrenamiento y reduce el riesgo de sobreajuste.
Cómo conectar las capas en el método forward
El método forward define el flujo real de los datos a través de los bloques [05:40]. Recibe dos argumentos del usuario: text y offsets, este último indica dónde inicia cada nuevo texto dentro del tensor.
El flujo es así:
embedded = self.embedding(text, offsets) aplica el embedding.
embedded_norm = self.bn1(embedded) normaliza por lotes.
embedded_activated = F.relu(embedded_norm) aplica la no linealidad ReLU.
return self.fc(embedded_activated) proyecta a las clases finales.
La función ReLU introduce no linealidad y permite que la red aprenda patrones más complejos que una simple combinación lineal.
Cómo inicializar el modelo con los hiperparámetros correctos
Para instanciar el modelo necesitas calcular tres valores a partir del dataset DBpedia [07:50].
- num_class: lo obtienes con
len(set([label for label, text in train_iter])). Itera todo el split de entrenamiento, extrae cada etiqueta, las mete en un set para eliminar duplicados y mide su longitud. Para DBpedia el resultado son 14 clases.
- vocab_size: es simplemente
len(vocab), donde vocab se definió antes. En este caso da 802 mil elementos, que incluyen palabras y símbolos [09:30].
- embedding_size: es un hiperparámetro que tú eliges. Aquí se usa 100 por velocidad, aunque modelos como BERT usan 768. Puedes ajustarlo después con fine tuning.
Con esos tres valores ya puedes ejecutar modelo = ModeloClasificacionTexto(vocab_size, embedding_size, num_class).to(device) para enviarlo a GPU si está disponible o a CPU.
Cómo contar los parámetros entrenables del modelo
Una función reutilizable que vale la pena tener en todos tus proyectos cuenta los parámetros entrenables [11:20]. La lógica es simple: itera sobre model.parameters(), filtra solo los que tienen requires_grad=True y suma p.numel() de cada uno.
Tener gradiente es sinónimo de ser entrenable. Si un parámetro no requiere gradiente, no se actualiza durante el entrenamiento.
Al imprimir el resultado con un formato limpio (separando miles con coma), descubres que el modelo tiene 80 millones de parámetros entrenables. Es un modelo grande, aunque dentro del mundo del deep learning moderno todavía se considera relativamente pequeño y muy eficiente para lo que ofrece.
¿Qué significa que un parámetro sea entrenable? Que tiene requires_grad=True y, por lo tanto, se actualiza durante el backpropagation. Solo esos parámetros cuentan para medir el tamaño real de aprendizaje del modelo.
¿Cómo se compara este modelo con la regresión lineal o el Hello PyTorch que construiste antes? ¿Qué capas extra le agregarías: más normalización, una LSTM, dropout? Déjalo en los comentarios.