📖 Teoría

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.

💡 Definición formal (NIST, 2015): "Big Data consists of extensive datasets — primarily in the characteristics of volume, velocity, variety, and/or variability — that require a scalable architecture for efficient storage, manipulation, and analysis." NIST Big Data Interoperability Framework, SP 1500-1 (2015)

Línea temporal del Big Data

2003 Google File System (GFS) 2004 MapReduce (Google) 2006 Hadoop (Doug Cutting) 2009 Spark (UC Berkeley) 2011 Kafka (LinkedIn) 2014 Spark 1.0 + MLlib 2017 Ray / Dask Python-native 2020+ Lakehouse Delta · Iceberg 2023+ AI-native Data + GPU Era Hadoop (batch) Era Spark (in-memory) Era Cloud-native + AI Evolución del ecosistema Big Data (2003–2025)

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:

📦
Volumen
Cantidad de datos: desde TB a EB. CERN genera ~1 PB/día. YouTube recibe 500 horas de vídeo/minuto.
Velocidad
Ritmo de generación y procesamiento. Datos en streaming, real-time analytics, latencia < ms.
🎨
Variedad
Datos estructurados (SQL), semi-estructurados (JSON, XML), no estructurados (imágenes, texto, audio).
Veracidad
Calidad y fiabilidad de los datos. Datos ruidosos, incompletos, sesgados o contradictorios.
💎
Valor
Capacidad de extraer conocimiento útil. El dato crudo sin procesar tiene valor limitado.
📖 Referencia: Laney, D. (2001). "3D Data Management: Controlling Data Volume, Velocity and Variety". META Group Research Note. Posteriormente extendido a 5V's por múltiples autores (Demchenko et al., 2013).

Tecnologías clave del ecosistema Big Data

Almacenamiento distribuido

TecnologíaTipoModeloCaso de uso
HDFSFilesystem distribuidoBloques replicados (3×)Data lakes on-premise, Hadoop
Amazon S3Object storageObjetos + metadataData lakes cloud, modelo estándar
Google Cloud StorageObject storageObjetos + bucketsDatos para BigQuery, Vertex AI
Azure Data LakeObject storageNamespaces jerárquicosEcosistema Microsoft, Synapse
MinIOObject storage (S3-compat)On-premise / hybridAlternativa S3 auto-hospedada
Delta LakeTable formatACID sobre ParquetLakehouse, versionado datos
Apache IcebergTable formatSnapshots, schema evolutionAnalytics 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íaModeloLatenciaCaso de uso típico
Apache KafkaPub/sub log distribuidomsEvent streaming, pipelines de datos real-time
Apache FlinkStream processing nativomsProcesamiento de eventos complejo (CEP)
Spark Structured StreamingMicro-batch~100ms–sETL near-real-time con Spark
Apache PulsarPub/sub + queuingmsMulti-tenancy, geo-replicación
AWS KinesisManaged streaming~200msIngesta cloud-native AWS
Google Pub/SubManaged messaging~100msIngesta cloud-native GCP

Bases de datos para Big Data

Base de datosTipoEscalaUso típico
CassandraWide-columnPB, write-heavyIoT, time-series, alta disponibilidad
MongoDBDocumento (JSON)TB–PBDatos semi-estructurados, flexibilidad
HBaseWide-column (Hadoop)PBRandom read/write sobre HDFS
RedisKey-value (in-memory)GB–TBCaching, sesiones, features real-time
ElasticsearchSearch engineTBBúsqueda full-text, logs (ELK)
ClickHouseColumnar analyticsPBOLAP, analytics tiempo real
CockroachDBNewSQL (distributed SQL)TB–PBSQL 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 datosTipoÍndiceMejor para
PineconeManagedHNSWRAG en producción, serverless
WeaviateOpen-sourceHNSWBúsqueda semántica multimodal
MilvusOpen-sourceIVF, HNSW, DiskANNEscala masiva, on-premise
QdrantOpen-sourceHNSWRust-based, alto rendimiento
ChromaDBOpen-sourceHNSWPrototipado rápido, in-process
pgvectorExtensión PostgreSQLIVFFlat, HNSWVectores + 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.

FuenteVolumen estimadoTipo de datosVelocidad
🌐 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)
📈 Crecimiento exponencial: según Statista e IDC, la datasfera global (datos creados, capturados, copiados y consumidos) pasará de 64.2 ZB (2020) a 181 ZB (2025). Más del 80% serán datos no estructurados (vídeo, imágenes, texto libre). IDC, "Data Age 2025" (Reinsel, Gantz & Rydning, 2018)

📊 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ñoMedioCapacidad típicaCoste/GB
1956IBM 305 RAMAC (primer HDD)5 MB~$10,000,000
1980HDD estándar26 MB~$500
1995HDD1 GB~$10
2000HDD20 GB~$1
2010HDD / SSD1–2 TB$0.10 / $1.00
2020NVMe SSD / HDD8–20 TB$0.02 / $0.10
2025NVMe SSD / Cloud30+ 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.

Cantidad de datos Rendimiento ML tradicional (SVM, RF, XGBoost) DL pequeño DL grande (GPT, ViT...) + datos → + rendimiento DL escala sin saturar (con modelo suficiente)
📐
Representaciones
Los modelos DL aprenden features jerárquicas directamente de los datos crudos. Más datos → features más ricas y generalizables.
🎯
Generalización
Con millones de parámetros, los modelos grandes necesitan millones/billones de ejemplos para evitar overfitting.
📈
Scaling
Las leyes de escala (Kaplan et al., 2020) demuestran que el rendimiento mejora como ley de potencias con N, D y C.
🔄
Pre-training
Modelos como GPT o CLIP se pre-entrenan con internet completo: trillones de tokens, miles de millones de pares imagen-texto.

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

$$L(N) \approx \left(\frac{N_c}{N}\right)^{\alpha_N}, \quad \alpha_N \approx 0.076$$
$$L(D) \approx \left(\frac{D_c}{D}\right)^{\alpha_D}, \quad \alpha_D \approx 0.095$$
$$L(C) \approx \left(\frac{C_c}{C}\right)^{\alpha_C}, \quad \alpha_C \approx 0.050$$

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:

$$D_{\text{optimal}} \approx 20 \times N$$

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.

ModeloParámetrosTokens entrenamientoRatio D/N¿Chinchilla-optimal?
GPT-3175B300B1.7❌ Under-trained
Chinchilla70B1.4T20✅ Óptimo
LLaMA-1 7B7B1T143✅ Over-trained (eficiente en inferencia)
LLaMA-2 70B70B2T29✅ Ligeramente sobre-óptimo
LLaMA-3 70B70B15T214✅ Deliberadamente over-trained
Mistral-7B7.3B~8T (est.)~1000✅ Over-trained (optimizar inferencia)
DeepSeek-V3671B (37B activos)14.8T22 (total) / 400 (activos)✅ MoE-optimized
💡 Tendencia 2024–2025: la industria ha pasado de "modelos Chinchilla-optimal" a "inference-optimal": entrenar modelos más pequeños con muchos más datos de los teóricamente óptimos, porque un modelo más pequeño es más barato de servir (menor latencia, menos GPU en inferencia). Sardana, N. & Frankle, J. (2023). "Beyond Chinchilla-Optimal: Accounting for Inference in Language Model Scaling Laws". arXiv:2401.00448.

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)

DatasetTamañoFuenteUsado en
Common Crawl~250B páginas web (PBs)Web crawl mensualBase de casi todos los LLMs
FineWeb15T tokensCommon Crawl filtrado (HF)Open-source training
RedPajama v230T tokensWeb + libros + papers + códigoLLMs open-source
The Pile825 GB22 fuentes diversasGPT-NeoX, Pythia
C4~750 GB (~156B tokens)Common Crawl limpiado (Google)T5, mT5
RefinedWeb5T tokensCommon Crawl (Falcon)Falcon 40B/180B
StarCoder Data~783 GBCódigo de GitHubStarCoder, Code Llama

Datasets de visión

DatasetTamañoContenidoUsado en
ImageNet14M imágenes, 1000 clasesObjetos clasificadosResNet, ViT (pre-training)
LAION-5B5.85B pares imagen-textoWeb crawlStable Diffusion, OpenCLIP
JFT-300M300M imágenesInternal GoogleViT, EfficientNet (Google)
WebLI10B pares imagen-textoInternal GooglePaLI, Gemini
COCO330K imágenesDetección, segmentaciónObject detection benchmarks
SA-1B11M imágenes, 1.1B máscarasSegmentación (Meta)SAM (Segment Anything)

Datasets de audio y multimodal

DatasetTamañoContenidoUsado en
LibriSpeech~1000 h audioAudiolibros en inglésASR benchmarks
Common Voice~19,000 h, 100+ idiomasVoz crowdsourced (Mozilla)Whisper, multilingüe ASR
Whisper data (internal)680,000 h audioWeb audio supervisadoWhisper (OpenAI)
WebVid-10M10M vídeos + captionsWeb vídeosVideo 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:

ModeloAñoCompute (PF-days)Coste estimadoDatos
AlexNet2012~0.01~$100ImageNet (1.2M)
ResNet-1522015~0.5~$1KImageNet (1.2M)
BERT-large2018~64~$10KBooksCorpus + Wikipedia
GPT-22019~256~$50KWebText (40GB)
GPT-32020~3,640~$5M~300B tokens
PaLM2022~25,000~$10M780B 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 405B2024~40,000~$30M (est.)15T tokens
📊 Epoch AI (2024): el compute de entrenamiento de modelos SOTA se ha multiplicado por ~4× cada año desde 2010, con una aceleración adicional desde 2020 con LLMs. El coste total de un frontier model en 2025 supera los $100M, haciendo del entrenamiento a escala un privilegio de pocas organizaciones. Sevilla, J. et al. (2022). "Compute Trends Across Three Eras of Machine Learning". arXiv:2202.05924.

📚 Papers fundamentales sobre escala

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.

Arquitectura de un AI Data Center 🖥️ GPU Cluster NVIDIA H100/H200/B200 AMD MI300X Google TPU v5p Miles de aceleradores NVLink / NVSwitch intra-nodo 🌐 Red InfiniBand (400 Gb/s) RoCE v2 (RDMA) Topología: fat-tree / rail NCCL para all-reduce Latencia ultra-baja 💾 Storage NVMe SSD (local) Lustre / GPFS (paralelo) Object Store (S3/GCS) TB/s de throughput Data pipeline pre-fetch ⚡ Energía 100–500 MW por DC UPS + generadores PUE target: 1.1–1.3 Energía renovable ❄️ Refrigeración Liquid cooling (directo) Rear-door heat exchangers H100: 700W TDP/GPU B200: 1000W TDP/GPU 🔧 Software Stack Slurm / Kubernetes CUDA / ROCm / JAX NCCL, Gloo, MPI Monitoring (Prometheus)

Aceleradores de hardware para DL

GPUs NVIDIA

GPUArq.VRAMFP16 TFLOPSFP8 TFLOPSInterconexiónTDPCoste cloud ($/h)
V100Volta32 GB HBM2125NVLink 300 GB/s300W~$1.5
A100Ampere80 GB HBM2e312NVLink 600 GB/s400W~$2.0
H100 SXMHopper80 GB HBM39901979NVLink 900 GB/s700W~$3.5
H200Hopper141 GB HBM3e9901979NVLink 900 GB/s700W~$4.5
B200Blackwell192 GB HBM3e22504500NVLink 1800 GB/s1000W~$6 (est.)
GB200 (Grace)Blackwell384 GB (2×192)45009000NVLink + Grace CPU2700WEnterprise

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.

TPUAñoHBMBF16 TFLOPSInterconexión
TPU v2201716 GB HBM46ICI (custom)
TPU v3201832 GB HBM123ICI 656 GB/s
TPU v4202132 GB HBM2275ICI 1.1 TB/s
TPU v5e202316 GB HBM2197ICI
TPU v5p202395 GB HBM2e459ICI 4800 GB/s
Trillium (v6)202432 GB HBM~900ICI (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.

AceleradorEmpresaEnfoque
Gaudi 2/3Intel (Habana Labs)Training/inference, integración Intel
Trainium2AWS (Annapurna Labs)Training en AWS, optimizado para SageMaker
Inferentia2AWSInferencia eficiente en AWS
WSE-3Cerebras SystemsWafer-scale: un chip = un wafer completo
CS-3Cerebras900K cores en un solo chip, 44 GB SRAM
Groq LPUGroqInferencia: 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íaBandwidthLatenciaNivelUso típico
NVLink (NVIDIA)900 GB/s (H100)<1 μsIntra-nodo (GPU↔GPU)Tensor parallelism dentro de un nodo
NVSwitchAll-to-all 900 GB/s<1 μsIntra-nodo (8 GPUs)Full mesh entre 8 GPUs en DGX
InfiniBand NDR400 Gb/s (50 GB/s)~1 μsInter-nodoAll-reduce entre nodos
InfiniBand XDR800 Gb/s (100 GB/s)<1 μsInter-nodoNext-gen clusters
RoCE v2100-400 Gb/s~2-5 μsInter-nodo (Ethernet)Alternativa Ethernet a IB
ICI (Google TPU)4.8 TB/s (v5p)<1 μsInter-chip (TPU)Mesh 3D dentro de TPU pod
💡 ¿Por qué importa tanto la red? En entrenamiento distribuido con data parallelism, cada GPU computa gradientes locales que deben sincronizarse (all-reduce) con todas las demás GPUs cada paso de training. Con un modelo de 70B parámetros (280 GB en FP32), cada all-reduce mueve cientos de GB. Si la red es lenta, las GPUs pasan más tiempo esperando que computando.

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).

CapaTecnologíaCapacidadThroughputRol
L1: GPU HBMHBM3/HBM3e80–192 GB/GPU~3 TB/sPesos, activaciones, gradientes
L2: NVMe localNVMe SSD1–8 TB/nodo~7 GB/sCache de datos, checkpoints rápidos
L3: Filesystem paraleloLustre, GPFS, WekaFSPBsTB/s (agregado)Datasets de training compartidos
L4: Object storeS3, GCS, MinIOIlimitadoGB/sAlmacenamiento 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, TF tf.data con prefetch(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

ClusterOrganizaciónAceleradoresRedModelos 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 Google 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.

CPU Pocos núcleos potentes C1 C2 C3 C4 4–128 cores, ~5 GHz Branch prediction, OOE Ideal: lógica compleja, I/O GPU Miles de cores simples ... × miles más 16K+ CUDA cores, ~2 GHz SIMT, HBM bandwidth Ideal: MatMul, parallel ops TPU / ASIC Diseño ad-hoc MXU Systolic Array MXU Systolic Array 128×128 BF16 MatMul Ideal: DL puro (JAX/TF)

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étodo4096×4096 MatMulSpeedup vs PythonDónde ejecutaCuándo usar
Python puro (for)~horasCPU (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
💡 Lección clave: nunca uses bucles 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ísticaPyTorchTensorFlow
CPU✅ Siempre✅ Siempre
NVIDIA GPU (CUDA)✅ Nativo (device='cuda')✅ Nativo (auto-detect)
Multi-GPUDataParallel, DistributedDataParallelMirroredStrategy, 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 memoriaManual (.to(device))Automática (soft placement)
Mixed precisiontorch.cuda.amptf.keras.mixed_precision
Compilación (graph)torch.compile (2.0+)tf.function + XLA
⚡ PyTorch vs TensorFlow — device management:
  • 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ácticaQué haceAhorro típico
MFU optimizationMaximizar Model FLOPs Utilization (~30-60% típico)2× throughput efectivo
Job scheduling (Slurm)Empaquetar jobs para minimizar GPUs idle20-40% utilización
Spot/preemptible instancesUsar instancias baratas con checkpointing60-80% coste
Liquid coolingReducir PUE de 1.4 a 1.1~20% energía total
Flash AttentionAtención IO-aware, evita materializar la matriz de atención2-4× speedup atención
Quantización (INT8/INT4)Reducir precisión de pesos en inferencia2-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.

💾
Memoria
GPT-3 (175B params) necesita ~700 GB solo para pesos en FP32. Una H100 tiene 80 GB. Se necesitan ≥ 9 GPUs solo para los pesos.
⏱️
Tiempo
LLaMA-3 70B tardó ~30 días en 2048 H100. En una sola GPU: ~170 años. Escalar = reducir tiempo linealmente.
🎯
Batch size
LLMs usan batch sizes de millones de tokens. Distribuir los datos entre GPUs permite batch sizes enormes sin agotar memoria.

📐 ¿Qué consume memoria en training?

Para un modelo de \(N\) parámetros entrenado con Adam en FP32:

ComponenteMemoriaEjemplo (7B)
Pesos del modelo\(4N\) bytes (FP32)28 GB
Gradientes\(4N\) bytes28 GB
Optimizer states (Adam: m, v)\(8N\) bytes56 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.

Mini-batch global GPU 0 Modelo completo + micro-batch 0 GPU 1 Modelo completo + micro-batch 1 GPU 2 Modelo completo + micro-batch 2 🔄 All-Reduce: sincronizar gradientes → promedio
1
Replicar modelo: cada GPU recibe una copia idéntica del modelo y los optimizer states.
2
Dividir datos: el mini-batch se divide en micro-batches, uno por GPU.
3
Forward + backward local: cada GPU computa loss y gradientes con su micro-batch.
4
All-reduce: promediar gradientes de todas las GPUs (NCCL ring/tree all-reduce).
5
Actualizar pesos: cada GPU aplica el mismo update, manteniendo modelos sincronizados.
# ═══════════════════════════════════════════════════
# 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()
⚠️ Limitación de Data Parallelism: el modelo completo debe caber en cada GPU. Para modelos de 70B+ parámetros, esto no es posible con una sola GPU (ni siquiera H200 con 141 GB). Se necesita Model Parallelism.

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.

Pipeline Parallelism (4 stages × 4 micro-batches) GPU 0 (Stage 0) GPU 1 (Stage 1) GPU 2 (Stage 2) GPU 3 (Stage 3) F₀ F₁ F₂ F₃ idle F₀ F₁ F₂ F₃ idle F₀ F₁ F₂ F₃ idle (bubble) F₀ F₁ F₂ F₃ B₀ B₁ Forward passes (Fᵢ) Backward (Bᵢ) Las zonas "idle" son pipeline bubbles — se minimizan con más micro-batches

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:

StageQué fragmentaMemoria por GPU
ZeRO Stage 1Optimizer 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

FrameworkOrganizaciónParallelismMejor para
PyTorch DDPMetaData ParallelMulti-GPU estándar
PyTorch FSDPMetaData + ShardingModelos grandes (7B–70B)
DeepSpeedMicrosoftZeRO 1-3 + PP + TPModelos muy grandes, ZeRO-Offload
Megatron-LMNVIDIATP + PP + DP (3D)Pre-training LLMs a escala máxima
HuggingFace AccelerateHugging FaceWrapper sobre DDP/FSDP/DeepSpeedSimplicidad, fine-tuning
Ray TrainAnyscaleData ParallelIntegración con Ray ecosystem
JAX (pjit/xmap)GoogleSPMD, automatic shardingTPU pods, máxima eficiencia
tf.distributeGoogleMirrored, MultiWorker, TPUEcosistema TensorFlow/Keras
Colossal-AIHPC-AI TechGemini, secuencia parallelismEficiencia 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?

EscenarioTécnica recomendadaDetalle
Modelo cabe en 1 GPU, quiero más velocidadData Parallelism (DDP)La más sencilla. Escala lineal con GPUs.
Modelo cabe en 1 GPU, quiero batch size grandeDDP + Gradient AccumulationSimula batch grande sin más memoria.
Modelo NO cabe en 1 GPU (7B–13B)FSDP / ZeRO Stage 3Fragmenta pesos entre GPUs. Mínimo cambio de código.
Modelo grande (30B–70B)FSDP + TP o 3D ParallelismTensor 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 grandeLoRA + FSDP/DeepSpeedLoRA reduce los parámetros entrenables → cabe en menos GPUs.
TPU podsJAX pjit / TF TPUStrategySPMD: automatic sharding optimizado para TPU mesh.
Pocos recursos (1-2 GPUs)QLoRA + DeepSpeed ZeRO-OffloadOffload 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