Deep Learning y Big Data
Del ecosistema Big Data a los centros de datos para IA: historia y evolución de las tecnologías de datos masivos, scaling laws, aceleradores hardware, computación eficiente (vectorización, GPU, TPU), y entrenamiento distribuido (Data Parallelism, Model Parallelism, FSDP, DeepSpeed, Megatron-LM).
¿Qué es Big Data?
Big Data se refiere a conjuntos de datos cuyo volumen, velocidad de generación o variedad hacen que las herramientas tradicionales de procesamiento sean insuficientes. El término no describe sólo un tamaño, sino un paradigma: nuevas arquitecturas, algoritmos y tecnologías diseñadas para capturar, almacenar, procesar y analizar datos a escalas antes inimaginables.
El concepto de Big Data nació a principios de los años 2000, impulsado por la necesidad de empresas como Google, Yahoo y Facebook de gestionar cantidades de información que crecían a un ritmo exponencial. Google fue la pionera: para indexar toda la web necesitaba un sistema de almacenamiento y procesamiento radicalmente diferente a las bases de datos relacionales que existían hasta entonces. Sus publicaciones internas —el Google File System (GFS, 2003) y MapReduce (2004)— sentaron las bases técnicas de lo que hoy conocemos como el ecosistema Big Data.
Desde entonces, el paradigma ha evolucionado en tres grandes eras: la era Hadoop (2006–2012), centrada en el procesamiento batch sobre clusters de máquinas commodity; la era Spark (2012–2018), que introdujo el procesamiento in-memory y el análisis iterativo necesario para el machine learning; y la era cloud-native + AI (2018–actualidad), donde la infraestructura se despliega en la nube, los datos se almacenan en lakehouses y los pipelines están optimizados para alimentar modelos de deep learning a escala masiva.
Línea temporal del Big Data
La siguiente línea temporal recorre los hitos tecnológicos más importantes en la evolución del ecosistema Big Data, desde la publicación del paper de GFS hasta las arquitecturas AI-native actuales. Cada uno de estos hitos supuso un cambio fundamental en cómo se almacenan, procesan y explotan los datos a gran escala.
Las V's del Big Data
El modelo original de Doug Laney (2001) describió tres V's. Con el tiempo se han añadido más dimensiones que caracterizan la complejidad de los datos a gran escala. Comprender estas dimensiones es fundamental para diseñar sistemas de procesamiento adecuados: no es lo mismo un problema donde el reto principal es el volumen (como almacenar petabytes de imágenes médicas) que uno donde el reto es la velocidad (como procesar millones de transacciones financieras por segundo).
En la práctica, un proyecto de Big Data rara vez se enfrenta a las cinco V's con la misma intensidad. Un pipeline de IoT industrial, por ejemplo, genera datos a altísima velocidad (miles de sensores muestreando a kHz) pero con poca variedad (siempre los mismos tipos de señal). En cambio, un proyecto de procesamiento de lenguaje natural a escala web tiene enorme variedad (texto, HTML, PDFs, imágenes con texto, audio transcrito) pero la velocidad de ingesta puede ser moderada (batch de crawls periódicos). Identificar cuáles son las V's dominantes en tu caso de uso es el primer paso para elegir la arquitectura correcta.
Tecnologías clave del ecosistema Big Data
Almacenamiento distribuido
| Tecnología | Tipo | Modelo | Caso de uso |
|---|---|---|---|
| HDFS | Filesystem distribuido | Bloques replicados (3×) | Data lakes on-premise, Hadoop |
| Amazon S3 | Object storage | Objetos + metadata | Data lakes cloud, modelo estándar |
| Google Cloud Storage | Object storage | Objetos + buckets | Datos para BigQuery, Vertex AI |
| Azure Data Lake | Object storage | Namespaces jerárquicos | Ecosistema Microsoft, Synapse |
| MinIO | Object storage (S3-compat) | On-premise / hybrid | Alternativa S3 auto-hospedada |
| Delta Lake | Table format | ACID sobre Parquet | Lakehouse, versionado datos |
| Apache Iceberg | Table format | Snapshots, schema evolution | Analytics a escala, Netflix |
Procesamiento batch
Hadoop (2006) fue el primer framework open-source para procesamiento distribuido masivo, inspirado directamente en los papers de Google sobre GFS (Ghemawat et al., 2003) y MapReduce (Dean & Ghemawat, 2004). Divide los datos en bloques de 128 MB distribuidos en un cluster, y ejecuta funciones map (transformación local) y reduce (agregación global) en paralelo.
Limitación: alto I/O a disco entre etapas → lento para algoritmos iterativos como los del machine learning. Esto motivó la creación de Spark.
Dean, J. & Ghemawat, S. (2004). MapReduce: Simplified Data Processing on Large Clusters. OSDI'04.
Ghemawat, S., Gobioff, H. & Leung, S.-T. (2003). The Google File System. SOSP'03.
Spark (Zaharia et al., 2010, UC Berkeley) introdujo los Resilient Distributed Datasets (RDDs): colecciones distribuidas inmutables que se procesan en memoria, evitando el I/O a disco de MapReduce. Hasta 100× más rápido que Hadoop para workloads iterativos.
Componentes: Spark SQL (consultas estructuradas), MLlib (machine learning distribuido), Spark Streaming (micro-batches), GraphX (grafos).
Zaharia, M. et al. (2010). Spark: Cluster Computing with Working Sets. HotCloud'10.
Zaharia, M. et al. (2012). Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing. NSDI'12.
Dask (2015, Anaconda): extiende NumPy, Pandas y Scikit-learn a datos que no caben en RAM. Usa grafos de tareas perezosos (lazy DAG) y ejecuta en paralelo. API casi idéntica a Pandas → curva de aprendizaje mínima.
Ray (2017, UC Berkeley / Anyscale): framework general de computación distribuida para Python. Incluye Ray Train (entrenamiento distribuido DL), Ray Data (pipelines de datos) y Ray Serve (serving). Usado internamente en OpenAI, Uber, Ant Financial.
Moritz, P. et al. (2018). Ray: A Distributed Framework for Emerging AI Applications. OSDI'18.
Procesamiento en streaming
| Tecnología | Modelo | Latencia | Caso de uso típico |
|---|---|---|---|
| Apache Kafka | Pub/sub log distribuido | ms | Event streaming, pipelines de datos real-time |
| Apache Flink | Stream processing nativo | ms | Procesamiento de eventos complejo (CEP) |
| Spark Structured Streaming | Micro-batch | ~100ms–s | ETL near-real-time con Spark |
| Apache Pulsar | Pub/sub + queuing | ms | Multi-tenancy, geo-replicación |
| AWS Kinesis | Managed streaming | ~200ms | Ingesta cloud-native AWS |
| Google Pub/Sub | Managed messaging | ~100ms | Ingesta cloud-native GCP |
Bases de datos para Big Data
| Base de datos | Tipo | Escala | Uso típico |
|---|---|---|---|
| Cassandra | Wide-column | PB, write-heavy | IoT, time-series, alta disponibilidad |
| MongoDB | Documento (JSON) | TB–PB | Datos semi-estructurados, flexibilidad |
| HBase | Wide-column (Hadoop) | PB | Random read/write sobre HDFS |
| Redis | Key-value (in-memory) | GB–TB | Caching, sesiones, features real-time |
| Elasticsearch | Search engine | TB | Búsqueda full-text, logs (ELK) |
| ClickHouse | Columnar analytics | PB | OLAP, analytics tiempo real |
| CockroachDB | NewSQL (distributed SQL) | TB–PB | SQL distribuido, ACID global |
Con la era de los LLMs y la generación de embeddings, las bases de datos vectoriales se han convertido en infraestructura esencial:
| Base de datos | Tipo | Índice | Mejor para |
|---|---|---|---|
| Pinecone | Managed | HNSW | RAG en producción, serverless |
| Weaviate | Open-source | HNSW | Búsqueda semántica multimodal |
| Milvus | Open-source | IVF, HNSW, DiskANN | Escala masiva, on-premise |
| Qdrant | Open-source | HNSW | Rust-based, alto rendimiento |
| ChromaDB | Open-source | HNSW | Prototipado rápido, in-process |
| pgvector | Extensión PostgreSQL | IVFFlat, HNSW | Vectores + SQL en misma DB |
Las mayores fuentes de producción de datos
La cantidad de datos generados globalmente ha crecido exponencialmente. Según IDC (International Data Corporation), en 2025 se generarán más de 180 zettabytes (ZB) de datos, frente a los ~2 ZB de 2010.
| Fuente | Volumen estimado | Tipo de datos | Velocidad |
|---|---|---|---|
| 🌐 Internet / Web | ~5 EB/día (2025) | Texto, imágenes, vídeo, logs | Continuo |
| 📱 Redes sociales | ~500 PB/día | Posts, imágenes, vídeos, stories | Real-time streaming |
| 📡 IoT / Sensores | ~73 ZB (acumulado 2025) | Series temporales, telemetría | Alta frecuencia (ms–s) |
| 🏥 Salud / Genómica | ~1 GB/genoma, 30 EB/año | Secuencias, imágenes médicas | Batch + streaming |
| 🔬 Investigación científica | CERN: 1 PB/día; SKA: 1 EB/día | Eventos físicos, señales | Burst (colisiones, observaciones) |
| 🚗 Vehículos autónomos | ~20 TB/vehículo/día | LiDAR, cámaras, radar, GPS | Continuo, alta frecuencia |
| 💰 Finanzas | NYSE: ~1 TB/día de transacciones | Ticks, órdenes, posiciones | Ultra-baja latencia (μs) |
| 🛰️ Observación de la Tierra | Sentinel: ~12 TB/día | Imágenes satelitales multiespectrales | Batch (órbitas) |
📊 Widget: Calculadora de volumen de datos
Estima el volumen total de datos generados por diferentes fuentes en un periodo.
Evolución del almacenamiento de datos
La capacidad de almacenamiento y su coste han evolucionado dramáticamente, siendo un factor clave en la viabilidad del Big Data:
| Año | Medio | Capacidad típica | Coste/GB |
|---|---|---|---|
| 1956 | IBM 305 RAMAC (primer HDD) | 5 MB | ~$10,000,000 |
| 1980 | HDD estándar | 26 MB | ~$500 |
| 1995 | HDD | 1 GB | ~$10 |
| 2000 | HDD | 20 GB | ~$1 |
| 2010 | HDD / SSD | 1–2 TB | $0.10 / $1.00 |
| 2020 | NVMe SSD / HDD | 8–20 TB | $0.02 / $0.10 |
| 2025 | NVMe SSD / Cloud | 30+ TB / ilimitado | $0.01 / $0.023/mo (S3) |
📚 Referencias históricas clave
- GFS paper: Ghemawat, S., Gobioff, H. & Leung, S.-T. (2003). "The Google File System". SOSP'03. PDF
- MapReduce paper: Dean, J. & Ghemawat, S. (2004). "MapReduce: Simplified Data Processing on Large Clusters". OSDI'04. PDF
- Bigtable paper: Chang, F. et al. (2006). "Bigtable: A Distributed Storage System for Structured Data". OSDI'06. PDF
- Dynamo paper: DeCandia, G. et al. (2007). "Dynamo: Amazon's Highly Available Key-value Store". SOSP'07. PDF
- Kafka paper: Kreps, J., Narkhede, N. & Rao, J. (2011). "Kafka: a Distributed Messaging System for Log Processing". NetDB'11. PDF
- Spark paper: Zaharia, M. et al. (2012). "Resilient Distributed Datasets". NSDI'12. PDF
- Lakehouse paper: Armbrust, M. et al. (2021). "Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics". CIDR'21. PDF
💡 Para profundizar en los fundamentos del procesamiento de datos, consulta también el submódulo de Tensores, donde se explica cómo estas estructuras de datos son la base del cómputo en deep learning.
¿Por qué el Deep Learning necesita Big Data?
La relación entre Big Data y Deep Learning es simbiótica: los modelos de DL necesitan cantidades masivas de datos para aprender representaciones útiles, y al mismo tiempo son los únicos modelos capaces de escalar su rendimiento proporcionalmente con la cantidad de datos disponibles.
Esta relación simbiótica no es un accidente: tiene raíces teóricas profundas. Los modelos de deep learning son aproximadores universales —con suficientes parámetros, pueden aproximar cualquier función continua— pero esa capacidad de representación viene con un coste: necesitan enormes cantidades de ejemplos para aprender las regularidades estadísticas del dominio sin caer en overfitting. Un modelo con millones o miles de millones de parámetros tiene un espacio de hipótesis tan grande que, sin datos suficientes, memorizará el training set en lugar de generalizar. Aquí es donde el Big Data se vuelve indispensable: cuantos más datos diversos y representativos alimenten al modelo, mejor será su capacidad de generalización. Para profundizar en conceptos de overfitting y generalización, consulta el submódulo de Machine Learning Clásico.
La siguiente gráfica ilustra la idea clave: mientras que los algoritmos de ML tradicional (SVM, Random Forest, XGBoost) saturan su rendimiento a partir de cierta cantidad de datos —porque su capacidad de representación es limitada—, los modelos de deep learning escalan sin saturar, siempre que el modelo sea suficientemente grande. Este fenómeno, demostrado empíricamente por las scaling laws de Kaplan et al. (2020), es la razón fundamental por la que los grandes avances en IA de los últimos años han venido de la mano de datasets cada vez más masivos.
Leyes de escala (Scaling Laws)
Las leyes de escala son relaciones empíricas que describen cómo el rendimiento de un modelo (medido como pérdida L) depende del número de parámetros (N), cantidad de datos (D) y compute (C) como leyes de potencias.
El descubrimiento de estas leyes fue un punto de inflexión para la industria del deep learning. Antes de 2020, la intuición dominante era que "más grande no siempre es mejor": se asumía que existían rendimientos decrecientes al escalar modelos. Los trabajos de Kaplan et al. en OpenAI demostraron que, al menos para modelos de lenguaje, el rendimiento mejora de forma predecible y suave siguiendo leyes de potencias, sin señales de saturación en los rangos estudiados (hasta cientos de miles de millones de parámetros). Esto transformó la estrategia de entrenamiento: en lugar de buscar arquitecturas más ingeniosas, las empresas empezaron a invertir en escalar los modelos existentes con más datos y más compute. Para una visión completa del entrenamiento de redes, consulta el submódulo de Optimización avanzada.
📐 Kaplan et al. (2020) — Leyes de escala para LLMs
Donde \(N_c, D_c, C_c\) son constantes y \(\alpha\) son los exponentes de escala. Los tres factores (modelo, datos, compute) contribuyen al rendimiento, pero con retornos decrecientes predecibles.
Kaplan, J. et al. (2020). "Scaling Laws for Neural Language Models". arXiv:2001.08361.
🦫 Chinchilla (Hoffmann et al., 2022)
El paper de Chinchilla demostró que los modelos anteriores (GPT-3, Gopher) estaban under-trained: tenían demasiados parámetros para la cantidad de datos usados. La regla óptima:
Para un modelo de 70B parámetros, la cantidad óptima de datos es ~1.4T tokens. Chinchilla (70B, 1.4T tokens) superó a Gopher (280B, 300B tokens) con 4× menos parámetros.
Hoffmann, J. et al. (2022). "Training Compute-Optimal Large Language Models". arXiv:2203.15556.
| Modelo | Parámetros | Tokens entrenamiento | Ratio D/N | ¿Chinchilla-optimal? |
|---|---|---|---|---|
| GPT-3 | 175B | 300B | 1.7 | ❌ Under-trained |
| Chinchilla | 70B | 1.4T | 20 | ✅ Óptimo |
| LLaMA-1 7B | 7B | 1T | 143 | ✅ Over-trained (eficiente en inferencia) |
| LLaMA-2 70B | 70B | 2T | 29 | ✅ Ligeramente sobre-óptimo |
| LLaMA-3 70B | 70B | 15T | 214 | ✅ Deliberadamente over-trained |
| Mistral-7B | 7.3B | ~8T (est.) | ~1000 | ✅ Over-trained (optimizar inferencia) |
| DeepSeek-V3 | 671B (37B activos) | 14.8T | 22 (total) / 400 (activos) | ✅ MoE-optimized |
Datasets masivos para Deep Learning
El entrenamiento de modelos de deep learning a escala requiere datasets cada vez más grandes, diversos y cuidadosamente curados:
Datasets de texto (NLP / LLMs)
| Dataset | Tamaño | Fuente | Usado en |
|---|---|---|---|
| Common Crawl | ~250B páginas web (PBs) | Web crawl mensual | Base de casi todos los LLMs |
| FineWeb | 15T tokens | Common Crawl filtrado (HF) | Open-source training |
| RedPajama v2 | 30T tokens | Web + libros + papers + código | LLMs open-source |
| The Pile | 825 GB | 22 fuentes diversas | GPT-NeoX, Pythia |
| C4 | ~750 GB (~156B tokens) | Common Crawl limpiado (Google) | T5, mT5 |
| RefinedWeb | 5T tokens | Common Crawl (Falcon) | Falcon 40B/180B |
| StarCoder Data | ~783 GB | Código de GitHub | StarCoder, Code Llama |
Datasets de visión
| Dataset | Tamaño | Contenido | Usado en |
|---|---|---|---|
| ImageNet | 14M imágenes, 1000 clases | Objetos clasificados | ResNet, ViT (pre-training) |
| LAION-5B | 5.85B pares imagen-texto | Web crawl | Stable Diffusion, OpenCLIP |
| JFT-300M | 300M imágenes | Internal Google | ViT, EfficientNet (Google) |
| WebLI | 10B pares imagen-texto | Internal Google | PaLI, Gemini |
| COCO | 330K imágenes | Detección, segmentación | Object detection benchmarks |
| SA-1B | 11M imágenes, 1.1B máscaras | Segmentación (Meta) | SAM (Segment Anything) |
Datasets de audio y multimodal
| Dataset | Tamaño | Contenido | Usado en |
|---|---|---|---|
| LibriSpeech | ~1000 h audio | Audiolibros en inglés | ASR benchmarks |
| Common Voice | ~19,000 h, 100+ idiomas | Voz crowdsourced (Mozilla) | Whisper, multilingüe ASR |
| Whisper data (internal) | 680,000 h audio | Web audio supervisado | Whisper (OpenAI) |
| WebVid-10M | 10M vídeos + captions | Web vídeos | Video generation models |
- Calidad vs cantidad: datos web contienen ruido, duplicados, contenido tóxico, PII. Se necesitan pipelines de limpieza sofisticados.
- Sesgo: datos web reflejan sesgos culturales, de género, raciales. Modelos amplifican estos sesgos.
- Copyright: uso de datos con copyright (libros, artículos, código) es legalmente controvertido. Casos: NYT vs OpenAI, Getty vs Stability AI.
- Data contamination: benchmarks de evaluación pueden estar en los datos de training, inflando métricas.
- Coste de curación: filtrar, deduplicar y etiquetar datos masivos requiere compute significativo.
El coste del compute para DL
El compute necesario para entrenar modelos SOTA ha crecido ~10× cada año desde 2012, mucho más rápido que la ley de Moore:
| Modelo | Año | Compute (PF-days) | Coste estimado | Datos |
|---|---|---|---|---|
| AlexNet | 2012 | ~0.01 | ~$100 | ImageNet (1.2M) |
| ResNet-152 | 2015 | ~0.5 | ~$1K | ImageNet (1.2M) |
| BERT-large | 2018 | ~64 | ~$10K | BooksCorpus + Wikipedia |
| GPT-2 | 2019 | ~256 | ~$50K | WebText (40GB) |
| GPT-3 | 2020 | ~3,640 | ~$5M | ~300B tokens |
| PaLM | 2022 | ~25,000 | ~$10M | 780B tokens |
| GPT-4 (est.) | 2023 | ~80,000–200,000 | ~$50–100M | ~13T tokens (est.) |
| Gemini Ultra (est.) | 2024 | ~100,000+ | ~$100M+ | Multimodal masivo |
| LLaMA-3 405B | 2024 | ~40,000 | ~$30M (est.) | 15T tokens |
📚 Papers fundamentales sobre escala
- Scaling Laws: Kaplan, J. et al. (2020). arXiv:2001.08361
- Chinchilla: Hoffmann, J. et al. (2022). arXiv:2203.15556
- Compute Trends: Sevilla, J. et al. (2022). arXiv:2202.05924
- Beyond Chinchilla: Sardana, N. & Frankle, J. (2023). arXiv:2401.00448
- Scaling Data-Constrained LMs: Muennighoff, N. et al. (2023). arXiv:2305.16264
- Epoch AI: epochai.org/trends
Centros de datos para Deep Learning
Entrenar modelos de Deep Learning a escala requiere infraestructura especializada que va mucho más allá de un simple servidor con GPU. Los AI data centers modernos son instalaciones de miles de millones de dólares diseñadas específicamente para workloads de entrenamiento e inferencia de modelos de IA.
Un AI data center se diferencia de un data center tradicional en prácticamente todos sus aspectos: el hardware está dominado por aceleradores (GPUs, TPUs) en lugar de CPUs genéricas; la red necesita un ancho de banda entre nodos órdenes de magnitud superior al de un data center web convencional (porque el entrenamiento distribuido requiere sincronizar gradientes constantemente entre miles de GPUs); el almacenamiento necesita un throughput sostenido de TB/s para alimentar los pipelines de datos; la energía se multiplica (una sola GPU H100 consume 700W, frente a los ~100W de un servidor web) y la refrigeración requiere sistemas de liquid cooling directo porque la densidad de calor por rack es demasiado alta para los sistemas de aire acondicionado tradicionales.
Los siguientes componentes forman la arquitectura típica de un AI data center moderno. Cada uno de ellos es un cuello de botella potencial: si cualquier componente está subdimensionado, las costosas GPUs pasan tiempo ociosas.
Arquitectura de un AI Data Center
El corazón del data center. Miles de aceleradores organizados en nodos (típicamente 8 GPUs por nodo).
- NVIDIA: H100 / H200 / B200 (Blackwell)
- AMD: MI300X (192 GB HBM3)
- Google: TPU v5p (pods de 8960 chips)
Cada nodo conecta sus GPUs internamente mediante NVLink (900 GB/s en H100) o NVSwitch, permitiendo comunicación GPU-a-GPU de latencia ultra-baja (<1 μs). Un cluster típico para entrenar un LLM frontier tiene entre 2,000 y 100,000 GPUs.
La red es el cuello de botella #1 en entrenamiento distribuido. Cada paso de training requiere sincronizar gradientes entre todas las GPUs (all-reduce).
- InfiniBand NDR/XDR: 400–800 Gb/s, latencia ~1 μs
- RoCE v2 (RDMA over Ethernet): alternativa más económica
- Topología: fat-tree o rail-optimized
La biblioteca NCCL (NVIDIA Collective Communication Library) gestiona las operaciones de all-reduce, broadcast y all-gather entre GPUs de forma optimizada.
El storage tiene dos roles: alimentar datos de entrenamiento a las GPUs a velocidad suficiente y guardar checkpoints del modelo.
- NVMe SSD (local): 1–8 TB/nodo, cache rápido
- Filesystem paralelo: Lustre, GPFS, WekaFS (TB/s agregados)
- Object store: S3, GCS, MinIO (almacenamiento a largo plazo)
Un error común es invertir millones en GPUs pero no dimensionar el storage. Si el pipeline de datos no alimenta las GPUs a velocidad suficiente, están idle.
Un AI data center moderno puede consumir entre 100 y 500 MW, equivalente al consumo de una ciudad pequeña.
- H100: 700W TDP por GPU → un rack de 8 GPUs consume ~7 kW solo en GPUs
- B200: 1000W TDP → aún más demanda
- PUE target: 1.07–1.3 (energía total / energía IT)
Microsoft, Google y Amazon están invirtiendo en energía nuclear (SMR) y renovables para alimentar sus futuros centros de IA. Google firmó un acuerdo con Kairos Power para reactores nucleares de sal fundida, y Microsoft reactivó la planta nuclear de Three Mile Island.
La densidad de calor de los aceleradores modernos hace imposible la refrigeración por aire convencional. Las soluciones actuales:
- Liquid cooling directo: tubos de refrigerante en contacto con las GPUs
- Rear-door heat exchangers: intercambiadores detrás de cada rack
- Inmersión: servidores sumergidos en líquido dieléctrico
Un nodo DGX B200 con 8 GPUs Blackwell genera ~10 kW de calor, exigiendo sistemas capaces de disipar esa energía sin afectar a los nodos vecinos. El liquid cooling puede reducir el PUE de 1.4 (aire) a 1.07 (líquido directo).
El software que orquesta el entrenamiento distribuido es tan crítico como el hardware:
- Schedulers: Slurm (HPC), Kubernetes (cloud-native)
- Runtimes: CUDA, ROCm, JAX/XLA
- Comunicación: NCCL, Gloo, MPI (OpenMPI/MPICH)
- Frameworks: PyTorch DDP/FSDP, DeepSpeed, Megatron-LM
- Monitoring: Prometheus, Grafana, DCGM (GPU metrics)
Un fallo de software en un job de training de 10,000 GPUs puede desperdiciar millones de dólares en compute. Por eso los equipos de infra desarrollan sistemas robustos de checkpointing, fault tolerance y automatic restart.
Aceleradores de hardware para DL
GPUs NVIDIA
| GPU | Arq. | VRAM | FP16 TFLOPS | FP8 TFLOPS | Interconexión | TDP | Coste cloud ($/h) |
|---|---|---|---|---|---|---|---|
| V100 | Volta | 32 GB HBM2 | 125 | — | NVLink 300 GB/s | 300W | ~$1.5 |
| A100 | Ampere | 80 GB HBM2e | 312 | — | NVLink 600 GB/s | 400W | ~$2.0 |
| H100 SXM | Hopper | 80 GB HBM3 | 990 | 1979 | NVLink 900 GB/s | 700W | ~$3.5 |
| H200 | Hopper | 141 GB HBM3e | 990 | 1979 | NVLink 900 GB/s | 700W | ~$4.5 |
| B200 | Blackwell | 192 GB HBM3e | 2250 | 4500 | NVLink 1800 GB/s | 1000W | ~$6 (est.) |
| GB200 (Grace) | Blackwell | 384 GB (2×192) | 4500 | 9000 | NVLink + Grace CPU | 2700W | Enterprise |
Otros aceleradores
Las TPUs son ASICs diseñados por Google específicamente para operaciones de tensor (MatMul + activaciones). Ventajas: eficiencia energética, integración con JAX/TensorFlow, disponibilidad en Google Cloud.
| TPU | Año | HBM | BF16 TFLOPS | Interconexión |
|---|---|---|---|---|
| TPU v2 | 2017 | 16 GB HBM | 46 | ICI (custom) |
| TPU v3 | 2018 | 32 GB HBM | 123 | ICI 656 GB/s |
| TPU v4 | 2021 | 32 GB HBM2 | 275 | ICI 1.1 TB/s |
| TPU v5e | 2023 | 16 GB HBM2 | 197 | ICI |
| TPU v5p | 2023 | 95 GB HBM2e | 459 | ICI 4800 GB/s |
| Trillium (v6) | 2024 | 32 GB HBM | ~900 | ICI (next-gen) |
TPU pods pueden escalar a miles de chips interconectados con ICI (Inter-Chip Interconnect), formando un mesh 3D de alta bandwidth. Un TPU v5p pod tiene 8960 chips con 459 TFLOPS cada uno.
Jouppi, N. et al. (2017). "In-Datacenter Performance Analysis of a Tensor Processing Unit". ISCA'17.
AMD Instinct MI300X ofrece 192 GB HBM3 (más que H100) con ~5.3 TB/s de bandwidth. Usa ROCm (stack open-source alternativo a CUDA). Adoptado por Microsoft Azure, Meta, y varios supercomputadores (Frontier).
MI325X (2024): 256 GB HBM3e, mayor bandwidth. Compite directamente con H200 en memoria.
| Acelerador | Empresa | Enfoque |
|---|---|---|
| Gaudi 2/3 | Intel (Habana Labs) | Training/inference, integración Intel |
| Trainium2 | AWS (Annapurna Labs) | Training en AWS, optimizado para SageMaker |
| Inferentia2 | AWS | Inferencia eficiente en AWS |
| WSE-3 | Cerebras Systems | Wafer-scale: un chip = un wafer completo |
| CS-3 | Cerebras | 900K cores en un solo chip, 44 GB SRAM |
| Groq LPU | Groq | Inferencia: deterministic scheduling, ultra-baja latencia |
Redes de interconexión para AI
La red es el cuello de botella #1 en entrenamiento distribuido. El entrenamiento de un LLM requiere sincronizar gradientes entre miles de GPUs, lo que genera tráfico de red intensísimo (all-reduce).
| Tecnología | Bandwidth | Latencia | Nivel | Uso típico |
|---|---|---|---|---|
| NVLink (NVIDIA) | 900 GB/s (H100) | <1 μs | Intra-nodo (GPU↔GPU) | Tensor parallelism dentro de un nodo |
| NVSwitch | All-to-all 900 GB/s | <1 μs | Intra-nodo (8 GPUs) | Full mesh entre 8 GPUs en DGX |
| InfiniBand NDR | 400 Gb/s (50 GB/s) | ~1 μs | Inter-nodo | All-reduce entre nodos |
| InfiniBand XDR | 800 Gb/s (100 GB/s) | <1 μs | Inter-nodo | Next-gen clusters |
| RoCE v2 | 100-400 Gb/s | ~2-5 μs | Inter-nodo (Ethernet) | Alternativa Ethernet a IB |
| ICI (Google TPU) | 4.8 TB/s (v5p) | <1 μs | Inter-chip (TPU) | Mesh 3D dentro de TPU pod |
Almacenamiento para pipelines de DL
El almacenamiento en un AI data center tiene dos roles: datos de entrenamiento (TB–PB de datasets) y checkpoints (snapshots del modelo durante training).
| Capa | Tecnología | Capacidad | Throughput | Rol |
|---|---|---|---|---|
| L1: GPU HBM | HBM3/HBM3e | 80–192 GB/GPU | ~3 TB/s | Pesos, activaciones, gradientes |
| L2: NVMe local | NVMe SSD | 1–8 TB/nodo | ~7 GB/s | Cache de datos, checkpoints rápidos |
| L3: Filesystem paralelo | Lustre, GPFS, WekaFS | PBs | TB/s (agregado) | Datasets de training compartidos |
| L4: Object store | S3, GCS, MinIO | Ilimitado | GB/s | Almacenamiento a largo plazo, archival |
Un error común es invertir millones en GPUs pero no dimensionar el storage. Si el data pipeline no puede alimentar las GPUs a velocidad suficiente, las GPUs están idle (esperando datos):
- Prefetching: cargar el siguiente batch en CPU/RAM mientras la GPU procesa el actual.
- Multi-worker DataLoader: PyTorch
num_workers > 0, TFtf.dataconprefetch(AUTOTUNE). - Memory mapping: archivos Parquet, Arrow, WebDataset evitan copias innecesarias.
- Streaming datasets: HuggingFace
load_dataset(streaming=True)para datos que no caben en disco local. - DALI (NVIDIA): pipeline de data augmentation acelerado por GPU.
Clusters de referencia en la industria
| Cluster | Organización | Aceleradores | Red | Modelos entrenados |
|---|---|---|---|---|
| Grand Teton | Meta | 16,000× A100 → 24,576× H100 | InfiniBand NDR 400G | LLaMA 2, LLaMA 3 |
| Colossus | xAI (Elon Musk) | 100,000× H100 | InfiniBand | Grok-2 |
| Eagle | Microsoft/OpenAI | ~25,000× A100 / H100 | InfiniBand NDR | GPT-4, GPT-4o |
| TPU v5p pod | 8,960× TPU v5p | ICI 4.8 TB/s | Gemini | |
| Frontier | ORNL (DOE) | 37,888× AMD MI250X | HPE Slingshot | 1.194 EFLOPS (primer exascale) |
| Leonardo | CINECA (EU) | 13,824× A100 | InfiniBand HDR | Modelos científicos europeos |
⚡ El reto de la energía
Un cluster de 10,000 H100 consume ~7 MW solo en GPUs (sin cooling ni overhead). Un AI data center completo puede consumir 100–500 MW, equivalente al consumo de una ciudad pequeña. Microsoft, Google y Amazon están invirtiendo en energía nuclear (SMR) y renovables para alimentar sus futuros centros de IA.
PUE (Power Usage Effectiveness) = Energía total / Energía IT. Un PUE de 1.1 significa que por cada 1W de compute, se gastan 0.1W en cooling/overhead. Los mejores DCs modernos alcanzan PUE ~1.07 (Google).
🏗️ Widget: Estimador de coste de cluster
Estima el coste mensual de un AI cluster según su configuración.
Computación eficiente para Deep Learning
La eficiencia computacional es crucial tanto a nivel de usuario (optimizar el código en tu máquina) como a nivel de centro de datos (maximizar la utilización del hardware). Comprender las diferencias entre procesamiento secuencial, paralelo en CPU y paralelo en GPU es fundamental para escribir código de DL eficiente.
En deep learning, la operación dominante es la multiplicación de matrices (MatMul): cada capa fully-connected, cada atención en un Transformer, cada convolución se reduce internamente a multiplicaciones de matrices. Estas operaciones son inherentemente paralelizables: cada elemento del resultado es independiente de los demás y puede calcularse simultáneamente. Por eso, la elección del hardware de ejecución —CPU, GPU o ASIC especializado— tiene un impacto dramático en el rendimiento. La diferencia no es de porcentajes, sino de órdenes de magnitud.
Los tres tipos principales de hardware para deep learning difieren en su filosofía de diseño. Comprender estas diferencias ayuda a elegir el hardware correcto para cada tarea y a escribir código que explote al máximo la arquitectura subyacente. Para una introducción a las operaciones tensoriales que subyacen a todo esto, consulta el submódulo de Tensores.
- 4–128 cores a ~5 GHz
- Branch prediction, ejecución fuera de orden
- Gran caché L1/L2/L3
- RAM: hasta 2 TB DDR5
- Excelente para lógica compleja e I/O
- 16,000+ CUDA cores a ~2 GHz
- Tensor Cores para MatMul (FP16/BF16/FP8)
- HBM3e: hasta 192 GB, ~5 TB/s bandwidth
- NVLink: 900 GB/s intra-nodo
- Programable (CUDA, ROCm)
- MXU: Systolic Arrays 128×128 para BF16 MatMul
- Hasta 95 GB HBM2e (TPU v5p)
- ICI: 4.8 TB/s inter-chip (mesh 3D)
- Pods de miles de chips
- Programable vía JAX/TensorFlow
De bucles for a operaciones vectorizadas
La diferencia de rendimiento entre código Python puro y código vectorizado puede ser de órdenes de magnitud. Veamos con el ejemplo de la multiplicación de matrices:
import time
def matmul_python(A, B):
"""Multiplicación de matrices con bucles for puros.
Complejidad: O(n³). Extremadamente lento para n > 500.
"""
n = len(A)
m = len(B[0])
k = len(B)
C = [[0.0] * m for _ in range(n)]
for i in range(n):
for j in range(m):
for p in range(k):
C[i][j] += A[i][p] * B[p][j]
return C
# Benchmark: matrices 512×512
n = 512
A = [[float(i + j) for j in range(n)] for i in range(n)]
B = [[float(i * j) for j in range(n)] for i in range(n)]
start = time.time()
C = matmul_python(A, B)
print(f"Python puro: {time.time() - start:.2f}s")
# → Python puro: ~45-120 segundos (¡minutos para n=1024!)
import numpy as np
import time
def matmul_numpy(A, B):
"""NumPy usa BLAS optimizado (OpenBLAS/MKL) internamente.
Operaciones vectorizadas en C/Fortran con SIMD y multi-core.
"""
return A @ B # equivalente a np.matmul(A, B)
# Benchmark: matrices 512×512
n = 512
A = np.random.randn(n, n).astype(np.float32)
B = np.random.randn(n, n).astype(np.float32)
start = time.time()
C = matmul_numpy(A, B)
print(f"NumPy (CPU): {time.time() - start:.4f}s")
# → NumPy (CPU): ~0.005-0.02 segundos (¡~2000-10000× más rápido!)
# ¿Por qué? NumPy llama a BLAS (Basic Linear Algebra Subprograms)
# implementado en C/Fortran con instrucciones SIMD (AVX-512)
# y multi-threading automático.
# Para matrices más grandes:
n = 4096
A = np.random.randn(n, n).astype(np.float32)
B = np.random.randn(n, n).astype(np.float32)
start = time.time()
C = A @ B
print(f"NumPy 4096×4096: {time.time() - start:.3f}s")
# → ~1-3 segundos (4096³ = 68B operaciones)
import cupy as cp
import time
def matmul_cupy(A, B):
"""CuPy: API idéntica a NumPy pero ejecuta en GPU.
Internamente usa cuBLAS (NVIDIA's BLAS para CUDA).
"""
return A @ B
# Benchmark: matrices 4096×4096 en GPU
n = 4096
A_gpu = cp.random.randn(n, n, dtype=cp.float32)
B_gpu = cp.random.randn(n, n, dtype=cp.float32)
# Warm-up (primera ejecución compila kernels)
_ = A_gpu @ B_gpu
cp.cuda.Stream.null.synchronize()
start = time.time()
C_gpu = matmul_cupy(A_gpu, B_gpu)
cp.cuda.Stream.null.synchronize() # Esperar a que GPU termine
print(f"CuPy (GPU): {time.time() - start:.4f}s")
# → CuPy (GPU): ~0.005-0.02 segundos para 4096×4096
# ¡~100-500× más rápido que NumPy para matrices grandes!
# Transferencia CPU ↔ GPU (el coste oculto)
A_cpu = np.random.randn(n, n).astype(np.float32)
start = time.time()
A_gpu = cp.asarray(A_cpu) # CPU → GPU
elapsed_to_gpu = time.time() - start
start = time.time()
A_back = cp.asnumpy(A_gpu) # GPU → CPU
elapsed_to_cpu = time.time() - start
print(f"CPU→GPU: {elapsed_to_gpu:.4f}s, GPU→CPU: {elapsed_to_cpu:.4f}s")
# ¡La transferencia puede ser más lenta que el cómputo!
| Método | 4096×4096 MatMul | Speedup vs Python | Dónde ejecuta | Cuándo usar |
|---|---|---|---|---|
| Python puro (for) | ~horas | 1× | CPU (1 core, interpretado) | Nunca para cómputo numérico |
| NumPy | ~1-3 s | ~10,000× | CPU (multi-core, BLAS) | Datos pequeños-medianos, CPU |
| CuPy | ~0.01-0.02 s | ~500,000× | GPU (CUDA, cuBLAS) | Datos grandes, GPU disponible |
| PyTorch tensor | ~0.01-0.02 s | ~500,000× | GPU (CUDA) | Training DL, autograd |
| JAX (jit) | ~0.005-0.01 s | ~1,000,000× | GPU/TPU (XLA compiled) | Máxima performance, TPUs |
for en Python para
operaciones numéricas. Usa operaciones vectorizadas (NumPy, CuPy,
PyTorch, JAX) que delegan el cómputo a bibliotecas compiladas (BLAS, cuBLAS, XLA)
que explotan paralelismo SIMD, multi-core y GPU automáticamente.
Devices en TensorFlow y PyTorch
Tanto TensorFlow como PyTorch permiten mover tensores y modelos entre diferentes devices (CPU, GPU, TPU). Entender cómo gestionar devices es esencial para el rendimiento.
import torch
# ═══════════════════════════════════════════════════
# Detectar devices disponibles
# ═══════════════════════════════════════════════════
print(f"CUDA disponible: {torch.cuda.is_available()}")
print(f"MPS disponible: {torch.backends.mps.is_available()}") # Apple Silicon
print(f"Nº GPUs CUDA: {torch.cuda.device_count()}")
if torch.cuda.is_available():
print(f"GPU actual: {torch.cuda.get_device_name(0)}")
# ═══════════════════════════════════════════════════
# Crear tensores en devices específicos
# ═══════════════════════════════════════════════════
x_cpu = torch.randn(1000, 1000) # CPU (por defecto)
x_gpu = torch.randn(1000, 1000, device='cuda') # GPU directamente
x_gpu2 = torch.randn(1000, 1000, device='cuda:1') # Segunda GPU
# ═══════════════════════════════════════════════════
# Mover tensores entre devices
# ═══════════════════════════════════════════════════
x_to_gpu = x_cpu.to('cuda') # CPU → GPU
x_to_cpu = x_gpu.cpu() # GPU → CPU
x_to_gpu_fp16 = x_cpu.to('cuda', dtype=torch.float16) # + cambio de tipo
# ═══════════════════════════════════════════════════
# Mover modelos completos
# ═══════════════════════════════════════════════════
model = torch.nn.Linear(1000, 100)
model = model.to('cuda') # Todos los parámetros a GPU
# ⚠️ Los datos de entrada también deben estar en el mismo device
output = model(x_to_gpu)
# ═══════════════════════════════════════════════════
# Device-agnostic code (buena práctica)
# ═══════════════════════════════════════════════════
device = torch.device(
'cuda' if torch.cuda.is_available()
else 'mps' if torch.backends.mps.is_available()
else 'cpu'
)
model = model.to(device)
data = data.to(device)
import tensorflow as tf
# ═══════════════════════════════════════════════════
# Detectar devices disponibles
# ═══════════════════════════════════════════════════
print("Devices:", tf.config.list_physical_devices())
gpus = tf.config.list_physical_devices('GPU')
print(f"GPUs: {gpus}")
tpus = tf.config.list_physical_devices('TPU')
print(f"TPUs: {tpus}")
# ═══════════════════════════════════════════════════
# Configurar memoria GPU
# ═══════════════════════════════════════════════════
# TF por defecto reserva TODA la memoria GPU.
# Para permitir crecimiento dinámico:
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
# O limitar a una cantidad fija:
# tf.config.set_logical_device_configuration(
# gpus[0],
# [tf.config.LogicalDeviceConfiguration(memory_limit=4096)] # 4 GB
# )
# ═══════════════════════════════════════════════════
# Placement explícito de operaciones
# ═══════════════════════════════════════════════════
with tf.device('/CPU:0'):
a = tf.random.normal([1000, 1000]) # En CPU
with tf.device('/GPU:0'):
b = tf.random.normal([1000, 1000]) # En GPU
# TF automáticamente mueve datos entre devices si es necesario
c = tf.matmul(a, b) # a se copia a GPU automáticamente
# ═══════════════════════════════════════════════════
# TPU setup (Google Cloud / Colab)
# ═══════════════════════════════════════════════════
# resolver = tf.distribute.cluster_resolver.TPUClusterResolver()
# tf.config.experimental_connect_to_cluster(resolver)
# tf.tpu.experimental.initialize_tpu_system(resolver)
# strategy = tf.distribute.TPUStrategy(resolver)
Comparativa de devices soportados
| Característica | PyTorch | TensorFlow |
|---|---|---|
| CPU | ✅ Siempre | ✅ Siempre |
| NVIDIA GPU (CUDA) | ✅ Nativo (device='cuda') | ✅ Nativo (auto-detect) |
| Multi-GPU | ✅ DataParallel, DistributedDataParallel | ✅ MirroredStrategy, MultiWorkerStrategy |
| Google TPU | ⚠️ Via PyTorch/XLA (limitado) | ✅ Nativo (TPUStrategy) |
| Apple Silicon (MPS) | ✅ device='mps' (M1/M2/M3/M4) | ⚠️ Soporte limitado (metal plugin) |
| AMD GPU (ROCm) | ✅ Nativo (PyTorch ROCm) | ⚠️ Soporte parcial |
| Intel GPU (XPU) | ⚠️ Via Intel Extensions | ⚠️ Via Intel plugin |
| AWS Trainium/Inferentia | ⚠️ Via Neuron SDK | ⚠️ Via Neuron SDK |
| Gestión de memoria | Manual (.to(device)) | Automática (soft placement) |
| Mixed precision | ✅ torch.cuda.amp | ✅ tf.keras.mixed_precision |
| Compilación (graph) | ✅ torch.compile (2.0+) | ✅ tf.function + XLA |
- PyTorch: control explícito (
.to('cuda')). Más verboso, pero más predecible. Error si tensores en devices distintos. - TensorFlow: placement automático ("soft placement"). Más cómodo, pero a veces difícil de depurar qué está en qué device.
- Para TPUs: TensorFlow/JAX tienen soporte nativo muy superior.
- Para GPUs NVIDIA: ambos excelentes, PyTorch domina en investigación.
Prácticas de eficiencia computacional
A nivel de usuario
Usar half-precision (FP16 o BF16) en lugar de FP32 para la mayor parte del cómputo reduce el uso de memoria a la mitad y duplica el throughput en GPUs con Tensor Cores. Se mantiene una copia "master" en FP32 para estabilidad.
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for batch in dataloader:
optimizer.zero_grad()
# Forward pass en FP16
with autocast(dtype=torch.float16):
output = model(batch['input'].cuda())
loss = criterion(output, batch['target'].cuda())
# Backward con gradient scaling (evita underflow en FP16)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
# → ~2× más rápido, ~50% menos memoria
Simula batch sizes grandes acumulando gradientes durante varios mini-batches
antes de hacer optimizer.step(). Útil cuando la GPU no tiene
suficiente memoria para el batch size deseado.
accumulation_steps = 4 # Effective batch = micro_batch × 4
for i, batch in enumerate(dataloader):
output = model(batch['input'].cuda())
loss = criterion(output, batch['target'].cuda())
loss = loss / accumulation_steps # Normalizar
loss.backward()
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
# Effective batch size = 32 × 4 = 128, usando memoria de batch=32
En lugar de almacenar todas las activaciones intermedias para el backward pass (que consume mucha memoria), gradient checkpointing re-computa las activaciones durante el backward. Reduce memoria ~5× a cambio de ~30% más de tiempo de cómputo.
from torch.utils.checkpoint import checkpoint
class EfficientModel(torch.nn.Module):
def forward(self, x):
# En lugar de almacenar activaciones de cada bloque,
# re-computa durante backward:
x = checkpoint(self.block1, x, use_reentrant=False)
x = checkpoint(self.block2, x, use_reentrant=False)
x = checkpoint(self.block3, x, use_reentrant=False)
return self.head(x)
# En HuggingFace Transformers:
model.gradient_checkpointing_enable()
torch.compile (PyTorch 2.0+) compila el modelo en un grafo optimizado que fusiona operaciones, elimina overhead de Python y genera kernels CUDA eficientes. Puede dar 20-50% de speedup sin cambiar código.
# PyTorch 2.0+
model = torch.compile(model, mode='reduce-overhead')
# Opciones de mode:
# 'default': balance entre compilación y rendimiento
# 'reduce-overhead': minimiza overhead de Python (mejor para inference)
# 'max-autotune': prueba múltiples estrategias (más lento en compilar)
# TensorFlow equivalente:
# @tf.function(jit_compile=True) # Activa XLA
# def train_step(data, labels):
# with tf.GradientTape() as tape:
# predictions = model(data, training=True)
# loss = loss_fn(labels, predictions)
# gradients = tape.gradient(loss, model.trainable_variables)
# optimizer.apply_gradients(zip(gradients, model.trainable_variables))
A nivel de centro de datos
| Práctica | Qué hace | Ahorro típico |
|---|---|---|
| MFU optimization | Maximizar Model FLOPs Utilization (~30-60% típico) | 2× throughput efectivo |
| Job scheduling (Slurm) | Empaquetar jobs para minimizar GPUs idle | 20-40% utilización |
| Spot/preemptible instances | Usar instancias baratas con checkpointing | 60-80% coste |
| Liquid cooling | Reducir PUE de 1.4 a 1.1 | ~20% energía total |
| Flash Attention | Atención IO-aware, evita materializar la matriz de atención | 2-4× speedup atención |
| Quantización (INT8/INT4) | Reducir precisión de pesos en inferencia | 2-4× throughput inference |
⚡ Widget: Calculadora de speedup
Estima el speedup de un pipeline al aplicar diferentes optimizaciones.
¿Por qué entrenamiento distribuido?
Los modelos modernos de Deep Learning son demasiado grandes para caber en una sola GPU y requieren demasiado compute para entrenarse en un tiempo razonable. El entrenamiento distribuido resuelve ambos problemas dividiendo el trabajo entre múltiples aceleradores.
Para entender por qué es necesario, consideremos las cifras: entrenar LLaMA-3 70B desde cero requirió aproximadamente 6.4 millones de GPU-horas. En una sola GPU H100, eso serían ~730 años. Meta utilizó 2,048 H100 durante ~30 días, reduciendo el tiempo casi linealmente. Sin entrenamiento distribuido, los modelos que hoy consideramos state-of-the-art simplemente no existirían: nadie puede esperar siglos para entrenar un modelo. El entrenamiento distribuido no es un lujo o una optimización; es un requisito fundamental para el deep learning a escala.
El reto principal del entrenamiento distribuido es la comunicación entre GPUs. Cada GPU calcula gradientes de forma local, pero estos deben sincronizarse con las demás GPUs para que el modelo aprenda de forma coherente. Esta sincronización (llamada all-reduce) genera un tráfico de red enorme que puede convertirse en el cuello de botella dominante si la infraestructura no está diseñada adecuadamente. Para más contexto sobre cómo funcionan los gradientes y el backpropagation, consulta el submódulo de Perceptrón y MLP.
📐 ¿Qué consume memoria en training?
Para un modelo de \(N\) parámetros entrenado con Adam en FP32:
| Componente | Memoria | Ejemplo (7B) |
|---|---|---|
| Pesos del modelo | \(4N\) bytes (FP32) | 28 GB |
| Gradientes | \(4N\) bytes | 28 GB |
| Optimizer states (Adam: m, v) | \(8N\) bytes | 56 GB |
| Activaciones (forward) | Variable (batch × seq × hidden) | ~10-50 GB |
| Total | \(\geq 16N\) bytes + activaciones | ~120-160 GB |
Con mixed precision (FP16/BF16): pesos 2N + gradientes 2N + optimizer master 4N + states 8N = 16N bytes. Para 70B params → ~1.1 TB mínimo. Se necesitan ≥14 H100s solo para memoria.
Data Parallelism (DP)
En Data Parallelism, cada GPU tiene una copia completa del modelo. Los datos de entrenamiento se dividen entre GPUs, cada una computa gradientes locales, y luego se sincronizan (all-reduce) antes de actualizar los pesos.
# ═══════════════════════════════════════════════════
# PyTorch DDP — Data Parallelism distribuido
# ═══════════════════════════════════════════════════
import torch
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
from torch.utils.data import DataLoader, DistributedSampler
def train(rank, world_size):
# 1. Inicializar proceso distribuido
dist.init_process_group("nccl", rank=rank, world_size=world_size)
torch.cuda.set_device(rank)
# 2. Crear modelo y wrappear con DDP
model = MyModel().cuda(rank)
model = DDP(model, device_ids=[rank])
# 3. DataLoader con DistributedSampler
sampler = DistributedSampler(dataset, num_replicas=world_size, rank=rank)
dataloader = DataLoader(dataset, batch_size=32, sampler=sampler)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
for epoch in range(num_epochs):
sampler.set_epoch(epoch) # Shuffle diferente cada epoch
for batch in dataloader:
inputs = batch['input'].cuda(rank)
targets = batch['target'].cuda(rank)
output = model(inputs)
loss = criterion(output, targets)
optimizer.zero_grad()
loss.backward() # DDP sincroniza gradientes automáticamente
optimizer.step()
dist.destroy_process_group()
# Lanzar con: torchrun --nproc_per_node=4 train.py
# O: python -m torch.distributed.launch --nproc_per_node=4 train.py
# ═══════════════════════════════════════════════════
# TensorFlow MirroredStrategy — Data Parallelism
# ═══════════════════════════════════════════════════
import tensorflow as tf
# 1. Crear strategy (auto-detecta GPUs)
strategy = tf.distribute.MirroredStrategy()
print(f"Número de replicas: {strategy.num_replicas_in_sync}")
# 2. Crear modelo dentro del scope de la strategy
with strategy.scope():
model = tf.keras.Sequential([
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dense(10)
])
model.compile(
optimizer=tf.keras.optimizers.Adam(1e-4),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
# 3. Crear dataset distribuido
batch_size_per_replica = 32
global_batch_size = batch_size_per_replica * strategy.num_replicas_in_sync
dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
dataset = dataset.shuffle(10000).batch(global_batch_size).prefetch(tf.data.AUTOTUNE)
# 4. Entrenar — TF distribuye automáticamente
model.fit(dataset, epochs=10)
# Para multi-nodo:
# strategy = tf.distribute.MultiWorkerMirroredStrategy()
Model Parallelism
En Model Parallelism, el modelo se divide entre múltiples GPUs. Cada GPU solo tiene una parte del modelo. Existen dos variantes principales.
La necesidad de model parallelism surge cuando el modelo es demasiado grande para caber en una sola GPU, incluso con mixed precision. Un modelo de 70B parámetros en FP16 ocupa ~140 GB solo para los pesos, sin contar gradientes, optimizer states ni activaciones. Como ninguna GPU actual tiene tanta memoria (la H200 llega a 141 GB, pero el overhead la hace insuficiente), es necesario distribuir el modelo entre varias GPUs. A diferencia del data parallelism —donde cada GPU tiene el modelo completo y los datos se dividen—, en model parallelism cada GPU tiene solo una fracción del modelo, y la comunicación entre GPUs es necesaria incluso durante un solo paso de forward. Esto hace que la latencia y el ancho de banda de la red sean mucho más críticos.
Tensor Parallelism (TP)
✂️ Tensor Parallelism
Divide capas individuales (tensores de pesos) entre GPUs. Por ejemplo, una capa linear de dimensión \(d \times d\) se divide en \(N\) partes, cada GPU computa su fragmento y luego se agrega.
- Cuándo: modelos grandes, GPUs dentro del mismo nodo (requiere bandwidth alta: NVLink)
- Implementación: Megatron-LM (NVIDIA), tensor_parallel en DeepSpeed
- Comunicación: all-reduce dentro de cada capa → requiere NVLink (900 GB/s intra-nodo)
- Típico: TP=8 (8 GPUs por nodo, una capa dividida en 8)
Shoeybi, M. et al. (2019). "Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism". arXiv:1909.08053.
Pipeline Parallelism (PP)
🔗 Pipeline Parallelism
Divide el modelo en stages (grupos de capas consecutivas), cada stage en una GPU diferente. Los micro-batches fluyen como en un pipeline industrial, permitiendo que múltiples GPUs trabajen simultáneamente.
- Cuándo: modelos muy profundos, distribución entre nodos (tolera mayor latencia)
- Implementación: GPipe (Google), PipeDream (Microsoft), DeepSpeed pipeline
- Problema: "pipeline bubbles" — GPUs idle al inicio/fin del pipeline
- Solución: micro-batching (más micro-batches → menos bubbles)
Huang, Y. et al. (2019). "GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism". NeurIPS'19.
Narayanan, D. et al. (2019). "PipeDream: Generalized Pipeline Parallelism for DNN Training". SOSP'19.
3D Parallelism y FSDP
Cuando un modelo es suficientemente grande —cientos de miles de millones de parámetros—, ninguna de las técnicas anteriores es suficiente por sí sola. Data parallelism necesita que el modelo quepa en cada GPU; tensor parallelism satura a pocos GPUs por la comunicación all-reduce dentro de cada operación; y pipeline parallelism introduce burbujas que limitan la eficiencia. La solución es combinar las tres en lo que se denomina 3D parallelism: una cuadrícula tridimensional donde cada eje corresponde a una dimensión de partición diferente.
Esta idea fue formalizada por NVIDIA en su framework Megatron-LM v2 (2021). La clave es asignar cada eje de paralelismo al nivel de interconexión adecuado: TP dentro del nodo (NVLink, alta velocidad), PP entre nodos cercanos, y DP en la dimensión más externa (tolera mayor latencia). Esta asignación jerárquica minimiza la comunicación en los enlaces más lentos.
🧊 3D Parallelism (DP × TP × PP)
Los LLMs modernos combinan las tres técnicas simultáneamente. Configuración típica para entrenar un modelo de 70B en 256 GPUs (32 nodos × 8 GPUs):
- TP = 8: cada capa se divide entre las 8 GPUs del nodo (NVLink intra-nodo)
- PP = 4: el modelo se divide en 4 stages, cada stage en un grupo de 8 GPUs
- DP = 8: 8 réplicas del pipeline completo, datos divididos entre réplicas
- Total: 8 × 4 × 8 = 256 GPUs
Narayanan, D. et al. (2021). "Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM". SC'21.
FSDP (PyTorch) / ZeRO (DeepSpeed) es un híbrido entre data parallelism y model parallelism. En lugar de replicar el modelo completo en cada GPU, fragmenta (shard) los pesos, gradientes y optimizer states entre todas las GPUs:
| Stage | Qué fragmenta | Memoria por GPU |
|---|---|---|
| ZeRO Stage 1 | Optimizer states | \(\approx 4N + 4N + \frac{8N}{P}\) |
| ZeRO Stage 2 | + Gradientes | \(\approx 4N + \frac{4N+8N}{P}\) |
| ZeRO Stage 3 / FSDP | + Pesos del modelo | \(\approx \frac{16N}{P}\) (todo fragmentado) |
Con \(P\) GPUs, FSDP/ZeRO-3 reduce la memoria por GPU \(P\) veces. 32 GPUs con un modelo de 7B: de ~112 GB/GPU (DP) a ~3.5 GB/GPU (FSDP).
Rajbhandari, S. et al. (2020). "ZeRO: Memory Optimizations Toward Training Trillion Parameter Models". SC'20.
# ═══════════════════════════════════════════════════
# PyTorch FSDP — Fully Sharded Data Parallel
# ═══════════════════════════════════════════════════
import torch
from torch.distributed.fsdp import (
FullyShardedDataParallel as FSDP,
MixedPrecision,
ShardingStrategy,
)
from torch.distributed.fsdp.wrap import transformer_auto_wrap_policy
from transformers import AutoModelForCausalLM
# 1. Configurar mixed precision
mp_policy = MixedPrecision(
param_dtype=torch.bfloat16,
reduce_dtype=torch.bfloat16,
buffer_dtype=torch.bfloat16,
)
# 2. Definir qué capas se fragmentan
auto_wrap_policy = transformer_auto_wrap_policy(
transformer_layer_cls={transformers.models.llama.modeling_llama.LlamaDecoderLayer}
)
# 3. Crear modelo FSDP
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
model = FSDP(
model,
sharding_strategy=ShardingStrategy.FULL_SHARD, # ZeRO-3
mixed_precision=mp_policy,
auto_wrap_policy=auto_wrap_policy,
device_id=torch.cuda.current_device(),
)
# 4. Training loop normal — FSDP gestiona fragmentación automáticamente
optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5)
for batch in dataloader:
loss = model(**batch).loss
loss.backward()
optimizer.step()
optimizer.zero_grad()
# Lanzar: torchrun --nproc_per_node=8 --nnodes=4 train_fsdp.py
Frameworks de entrenamiento distribuido
El ecosistema de entrenamiento distribuido ha madurado enormemente desde 2020. Mientras que antes cada organización construía sus propias soluciones, hoy existen frameworks de código abierto que encapsulan las técnicas de paralelismo descritas arriba y las hacen accesibles con pocas líneas de configuración.
La elección del framework depende fundamentalmente de tres factores: la escala del modelo (modelos de <10B suelen basta con DDP/FSDP, mientras que modelos de 100B+ necesitan Megatron-DeepSpeed), el hardware disponible (GPUs NVIDIA vs TPUs Google), y la complejidad que estés dispuesto a asumir. La siguiente tabla resume las opciones más relevantes en 2024.
| Framework | Organización | Parallelism | Mejor para |
|---|---|---|---|
| PyTorch DDP | Meta | Data Parallel | Multi-GPU estándar |
| PyTorch FSDP | Meta | Data + Sharding | Modelos grandes (7B–70B) |
| DeepSpeed | Microsoft | ZeRO 1-3 + PP + TP | Modelos muy grandes, ZeRO-Offload |
| Megatron-LM | NVIDIA | TP + PP + DP (3D) | Pre-training LLMs a escala máxima |
| HuggingFace Accelerate | Hugging Face | Wrapper sobre DDP/FSDP/DeepSpeed | Simplicidad, fine-tuning |
| Ray Train | Anyscale | Data Parallel | Integración con Ray ecosystem |
| JAX (pjit/xmap) | SPMD, automatic sharding | TPU pods, máxima eficiencia | |
| tf.distribute | Mirrored, MultiWorker, TPU | Ecosistema TensorFlow/Keras | |
| Colossal-AI | HPC-AI Tech | Gemini, secuencia parallelism | Eficiencia memoria, sequence TP |
DeepSpeed (Microsoft) es el framework más completo para entrenamiento distribuido eficiente. Además de ZeRO (stages 1-3), incluye:
- ZeRO-Offload: mueve optimizer states y gradientes a CPU RAM (entrena modelos grandes en pocas GPUs).
- ZeRO-Infinity: extiende offload a NVMe SSD (entrena modelos de trillones de params).
- DeepSpeed-MoE: soporte nativo para Mixture of Experts.
- FlexGen: inferencia eficiente con offloading.
- 1-bit Adam: compresión de comunicación para reducir tráfico de red.
# deepspeed_config.json
{
"bf16": {"enabled": true},
"zero_optimization": {
"stage": 3,
"offload_optimizer": {"device": "cpu"},
"offload_param": {"device": "none"},
"overlap_comm": true,
"contiguous_gradients": true,
"reduce_scatter": true
},
"gradient_accumulation_steps": 4,
"train_micro_batch_size_per_gpu": 2,
"wall_clock_breakdown": false
}
# Lanzar: deepspeed --num_gpus=8 train.py --deepspeed ds_config.json
Rajbhandari, S. et al. (2020). "ZeRO: Memory Optimizations Toward Training Trillion Parameter Models". SC'20.
Ren, J. et al. (2021). "ZeRO-Offload: Democratizing Billion-Scale Model Training". USENIX ATC'21.
Accelerate simplifica el entrenamiento distribuido abstrayendo DDP, FSDP y DeepSpeed detrás de una API unificada. Permite cambiar entre strategies sin cambiar código:
# ═══════════════════════════════════════════════════
# HuggingFace Accelerate — wrapper unificado
# ═══════════════════════════════════════════════════
from accelerate import Accelerator
accelerator = Accelerator(
mixed_precision='bf16', # o 'fp16'
gradient_accumulation_steps=4,
)
# Preparar modelo, optimizer, dataloader
model, optimizer, dataloader = accelerator.prepare(
model, optimizer, dataloader
)
for batch in dataloader:
with accelerator.accumulate(model):
outputs = model(**batch)
loss = outputs.loss
accelerator.backward(loss)
optimizer.step()
optimizer.zero_grad()
# Configurar: accelerate config (wizard interactivo)
# Lanzar: accelerate launch --num_processes=8 train.py
¿Cuándo usar qué técnica?
La regla de oro es empezar siempre con la técnica más simple que funcione. Data parallelism con DDP debería ser el primer recurso; solo cuando el modelo no cabe en una GPU (o la velocidad es insuficiente) se justifica pasar a sharding (FSDP/ZeRO) y, más allá, a combinaciones de paralelismo más complejas. Cada nivel de complejidad introduce overhead de comunicación, dificultades de debugging y puntos de fallo adicionales.
Para fine-tuning de LLMs, la combinación de LoRA + cuantización (QLoRA) ha democratizado enormemente el acceso: permite adaptar modelos de 70B en una o dos GPUs consumer (24 GB VRAM). Consulta el submódulo de Optimización avanzada para detalles sobre estas técnicas de eficiencia paramétrica.
| Escenario | Técnica recomendada | Detalle |
|---|---|---|
| Modelo cabe en 1 GPU, quiero más velocidad | Data Parallelism (DDP) | La más sencilla. Escala lineal con GPUs. |
| Modelo cabe en 1 GPU, quiero batch size grande | DDP + Gradient Accumulation | Simula batch grande sin más memoria. |
| Modelo NO cabe en 1 GPU (7B–13B) | FSDP / ZeRO Stage 3 | Fragmenta pesos entre GPUs. Mínimo cambio de código. |
| Modelo grande (30B–70B) | FSDP + TP o 3D Parallelism | Tensor parallelism intra-nodo + FSDP entre nodos. |
| Modelo muy grande (100B+) | 3D Parallelism (Megatron + DeepSpeed) | TP × PP × DP. Requiere ingeniería de infraestructura. |
| Fine-tuning de LLM grande | LoRA + FSDP/DeepSpeed | LoRA reduce los parámetros entrenables → cabe en menos GPUs. |
| TPU pods | JAX pjit / TF TPUStrategy | SPMD: automatic sharding optimizado para TPU mesh. |
| Pocos recursos (1-2 GPUs) | QLoRA + DeepSpeed ZeRO-Offload | Offload a CPU, quantización 4-bit. Fine-tune 70B en 2×24GB. |
🧩 Widget: Selector de estrategia de paralelismo
Recomienda la estrategia óptima según tu hardware y modelo.
Papers y recursos fundamentales
El entrenamiento distribuido es una de las áreas de investigación más activas en sistemas para deep learning. Los siguientes papers constituyen las referencias canónicas que todo practicante debería conocer. Están ordenados cronológicamente para reflejar la evolución del campo, desde los primeros trabajos en data parallelism hasta las técnicas de sharding que hacen posible entrenar modelos de cientos de miles de millones de parámetros.
📚 Papers esenciales de entrenamiento distribuido
- Data Parallelism: Li, M. et al. (2020). "PyTorch Distributed: Experiences on Accelerating Data Parallel Training". arXiv:2006.15704
- Megatron-LM: Shoeybi, M. et al. (2019). "Training Multi-Billion Parameter Language Models Using Model Parallelism". arXiv:1909.08053
- GPipe: Huang, Y. et al. (2019). "Efficient Training of Giant Neural Networks using Pipeline Parallelism". arXiv:1811.06965
- PipeDream: Narayanan, D. et al. (2019). "Generalized Pipeline Parallelism for DNN Training". arXiv:1806.03377
- ZeRO: Rajbhandari, S. et al. (2020). "Memory Optimizations Toward Training Trillion Parameter Models". arXiv:1910.02054
- ZeRO-Offload: Ren, J. et al. (2021). "Democratizing Billion-Scale Model Training". arXiv:2101.06840
- FSDP: Zhao, Y. et al. (2023). "PyTorch FSDP: Experiences on Scaling Fully Sharded Data Parallel". arXiv:2304.11277
- Flash Attention: Dao, T. et al. (2022). "IO-Aware Exact Attention with IO-Awareness". arXiv:2205.14135
- DeepSpeed: github.com/microsoft/DeepSpeed
- Megatron-DeepSpeed: github.com/microsoft/Megatron-DeepSpeed
- Lilian Weng — "How to Train Really Large Models on Many GPUs": lilianweng.github.io (excelente artículo divulgativo)
💡 Para entender los fundamentos de las redes neuronales sobre las que se construyen estos modelos distribuidos, consulta los submódulos de Perceptrón y MLP y Large Language Models. Para técnicas de eficiencia como LoRA y quantización mencionadas en esta sección, ver Optimización avanzada.