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.
Línea temporal del Big Data
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:
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.
- MapReduce paper: Dean, J. & Ghemawat, S. (2004). "MapReduce: Simplified Data Processing on Large Clusters". OSDI'04.
- Bigtable paper: Chang, F. et al. (2006). "Bigtable: A Distributed Storage System for Structured Data". OSDI'06.
- Dynamo paper: DeCandia, G. et al. (2007). "Dynamo: Amazon's Highly Available Key-value Store". SOSP'07.
- Kafka paper: Kreps, J., Narkhede, N. & Rao, J. (2011). "Kafka: a Distributed Messaging System for Log Processing". NetDB'11.
- Spark paper: Zaharia, M. et al. (2012). "Resilient Distributed Datasets". NSDI'12.
- Lakehouse paper: Armbrust, M. et al. (2021). "Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics". CIDR'21.
¿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.
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.
📐 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.
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.
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.
📐 ¿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:
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
🧊 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
| 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?
| 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
📚 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). arXiv:1909.08053
- GPipe: Huang, Y. et al. (2019). arXiv:1811.06965
- PipeDream: Narayanan, D. et al. (2019). arXiv:1806.03377
- ZeRO: Rajbhandari, S. et al. (2020). arXiv:1910.02054
- ZeRO-Offload: Ren, J. et al. (2021). 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). arXiv:2205.14135
- DeepSpeed: GitHub
- Megatron-DeepSpeed: GitHub
- Lilian Weng — "How to Train Really Large Models on Many GPUs": Blog