TensorFlow and PyTorch dominate deep learning. Google open-sourced TensorFlow in 2015; Facebook open-sourced PyTorch in 2016. By 2023–2025 PyTorch surged to ~70–80% of research papers, while TensorFlow held ground in enterprise production deployments. This guide compares both frameworks across every dimension that matters so you can pick the right one — or know when to use both.
At a glance
| TensorFlow 2.x | PyTorch 2.x | |
|---|---|---|
| Created by | Google Brain (2015) | Meta AI (Facebook, 2016) |
| Language | Python, C++, Java, JS | Python, C++ (LibTorch) |
| Primary paradigm | Eager (v2) + optional static graph | Eager (define-by-run) |
| Production deployment | TensorFlow Serving, TFLite, TF.js | TorchServe, TorchScript, ONNX |
| Mobile/edge | TFLite, TensorFlow.js, Coral | ExecuTorch (new, PyTorch Mobile) |
| High-level API | Keras (built-in) | Lightning, Ignite (3rd-party) |
| Research adoption | ~20–30% of papers | ~70–80% of papers |
| Compilation | XLA, tf.function, Grappler |
torch.compile (Inductor, Triton) |
| Ecosystem | TFX, TFHub, Vertex AI | Hugging Face, timm, Lightning |
| License | Apache 2.0 | BSD 3-Clause |
Execution model
TensorFlow: static graph → eager hybrid
TensorFlow 1.x used static computation graphs — you defined a graph first, then ran it in a Session. TF 2.x switched to eager execution by default (like PyTorch), but keeps the option to trace a function into a static graph with tf.function:
import tensorflow as tf
@tf.function # compiles to optimized XLA graph
def train_step(x, y):
with tf.GradientTape() as tape:
pred = model(x, training=True)
loss = loss_fn(y, pred)
grads = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
return loss
tf.function uses tracing — the first call builds a graph, subsequent calls reuse it. This is great for performance but can cause subtle bugs with Python control flow.
PyTorch: dynamic graph (define-by-run)
PyTorch builds the computation graph on-the-fly as Python code runs. No special decorators needed for basic training:
import torch
import torch.nn as nn
def train_step(model, x, y, optimizer, loss_fn):
optimizer.zero_grad()
pred = model(x)
loss = loss_fn(pred, y)
loss.backward() # autograd traces the dynamic graph
optimizer.step()
return loss.item()
PyTorch 2.0+ adds torch.compile() for optional graph compilation:
model = torch.compile(model) # ~1.5–2× speedup on supported ops
Defining models
TensorFlow / Keras
import tensorflow as tf
from tensorflow import keras
# Functional API
inputs = keras.Input(shape=(784,))
x = keras.layers.Dense(256, activation="relu")(inputs)
x = keras.layers.Dropout(0.3)(x)
outputs = keras.layers.Dense(10, activation="softmax")(x)
model = keras.Model(inputs, outputs)
model.compile(
optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
model.fit(x_train, y_train, epochs=10, validation_split=0.2)
PyTorch
import torch
import torch.nn as nn
class MLP(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(256, 10),
)
def forward(self, x):
return self.net(x)
model = MLP()
optimizer = torch.optim.Adam(model.parameters())
loss_fn = nn.CrossEntropyLoss()
for epoch in range(10):
for x, y in dataloader:
loss = train_step(model, x, y, optimizer, loss_fn)
Key difference: Keras hides most boilerplate; PyTorch makes the training loop explicit — verbose but fully transparent.
Performance
| Workload | TensorFlow 2.x | PyTorch 2.x |
|---|---|---|
| Image classification (ResNet-50, A100) | Baseline | ~5–10% faster with torch.compile |
| Transformer training (BERT, V100) | Competitive with XLA | Faster in most benchmarks |
| Small model, CPU inference | TFLite faster | Slower without optimization |
| Data loading | tf.data highly optimized |
DataLoader + num_workers, competitive |
| Distributed training | tf.distribute.MirroredStrategy |
torch.distributed + FSDP |
| Mixed precision (AMP) | tf.keras.mixed_precision |
torch.cuda.amp.autocast |
| Graph compilation speedup | tf.function + XLA: 2–4× |
torch.compile (Inductor): 1.5–3× |
Bottom line: Both are within 5–15% of each other for typical workloads. Framework choice rarely determines training speed — model architecture, hardware, and data pipeline do.
Debugging and research experience
This is where PyTorch wins the research community:
| Experience | TensorFlow 2.x | PyTorch |
|---|---|---|
Debugging with pdb / print |
Works in eager, breaks inside tf.function |
Works everywhere — Python is Python |
| Stack traces | Often cryptic graph-level errors | Native Python tracebacks |
| Modifying architecture mid-run | Requires re-tracing | Just change Python code |
| Custom CUDA kernels | Possible but complex | torch.utils.cpp_extension easier |
| Experiment flexibility | Good, but more ceremony | Minimal ceremony |
| Jupyter notebook experience | Good | Excellent |
# PyTorch — easy mid-loop debugging
for batch in dataloader:
loss = model(batch)
if torch.isnan(loss):
import pdb; pdb.set_trace() # full Python debugger
loss.backward()
Deployment & production
This is TensorFlow's historical advantage, though the gap is narrowing.
| Deployment target | TensorFlow | PyTorch |
|---|---|---|
| REST API server | TensorFlow Serving (Docker image) | TorchServe, FastAPI + torch.jit |
| Mobile (Android/iOS) | TFLite (mature, wide hardware support) | ExecuTorch (PyTorch Mobile v2, newer) |
| Browser | TensorFlow.js | ONNX.js, onnxruntime-web |
| Edge / microcontrollers | TFLite Micro | Limited |
| Cloud ML platforms | Vertex AI (GCP), SageMaker | SageMaker, Azure ML |
| ONNX export | tf2onnx converter |
Native torch.onnx.export |
| TensorRT (NVIDIA) | Supported | Supported (often easier) |
| Quantisation | TFLite quantisation (post + QAT) | torch.quantization, bitsandbytes |
# TensorFlow — save and serve
model.save("saved_model/") # SavedModel format
# → docker run tensorflow/serving --model_base_path=/models/my_model
# PyTorch — TorchScript for production
scripted = torch.jit.script(model)
scripted.save("model.pt")
# or ONNX export
torch.onnx.export(model, dummy_input, "model.onnx", opset_version=17)
Ecosystem
TensorFlow ecosystem
| Component | Purpose |
|---|---|
| Keras | High-level API (built-in since TF2) |
| TFX | ML production pipelines |
| TFLite | Mobile & edge inference |
| TensorFlow.js | Browser and Node.js ML |
| TensorFlow Hub | Pre-trained model repository |
| TensorBoard | Training visualisation |
| Vertex AI | Google Cloud managed ML |
| JAX | Next-gen Google framework (separate but related) |
PyTorch ecosystem
| Component | Purpose |
|---|---|
| Hugging Face Transformers | 500k+ models, NLP/vision/audio |
| timm | 700+ SOTA vision models |
| PyTorch Lightning | Training loop abstraction |
| torchvision / torchaudio / torchtext | Domain libraries |
| TorchServe | Model serving |
| ExecuTorch | Mobile/edge deployment |
| Ax / BoTorch | Bayesian optimisation |
| DeepSpeed | Large-scale distributed training |
| vLLM / TGI | LLM inference (both built on PyTorch) |
Hugging Face alone makes PyTorch the de facto framework for LLMs, diffusion models, and modern AI research. GPT-2, LLaMA, Stable Diffusion, Whisper — all PyTorch-first.
Distributed training
# PyTorch DDP — Data Parallel
# torchrun --nproc_per_node=4 train.py
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
dist.init_process_group("nccl")
model = DDP(model.cuda(), device_ids=[local_rank])
# PyTorch FSDP — for very large models
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
model = FSDP(model)
# TensorFlow MirroredStrategy
strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
model = build_model()
model.compile(...)
Both support multi-GPU and multi-node training. PyTorch FSDP is the dominant approach for training 7B–70B parameter LLMs; TF's TPU support via XLA is unique and unmatched for Google Cloud users.
Where TensorFlow wins
| Scenario | Why TF |
|---|---|
| Google Cloud / TPU training | Native TPU support, XLA, cost-effective for massive scale |
| Mobile deployment | TFLite is more mature and has broader hardware support |
| Browser ML | TensorFlow.js is the clear leader |
| Enterprise Keras teams | Keras is polished, stable API with long-term support |
| TFX production pipelines | TFX handles data validation, serving, and monitoring end-to-end |
| Strict model versioning | SavedModel format is well-defined and tooled |
| Mixed teams (non-ML engineers) | Keras abstractions are easier to understand |
| Legacy codebases | Large TF1/TF2 codebases are well-established |
Where PyTorch wins
| Scenario | Why PyTorch |
|---|---|
| Research & publications | 70–80% of ML papers use PyTorch |
| LLMs & generative AI | Hugging Face, vLLM, TGI — all PyTorch |
| Diffusion models | Stable Diffusion, FLUX, ComfyUI — PyTorch |
| Custom model architectures | Dynamic graphs make experimentation fast |
| Debugging complex models | Python-native debugging, no graph surprises |
| NVIDIA GPU optimisation | Deep CUDA integration, easy TensorRT export |
| Academic collaborations | Default framework in most university labs |
| Rapid prototyping | Less boilerplate for novel architectures |
Learning curve
| Phase | TensorFlow / Keras | PyTorch |
|---|---|---|
| Week 1 — First model | Very easy (Keras model.fit()) |
Moderate (explicit training loop) |
| Week 2–4 — Custom training | Complexity jumps (GradientTape, tf.function) | Consistent — just Python |
| Month 2–3 — Debugging | Hard (graph errors, tracing bugs) | Natural (Python debugger) |
| Month 3–6 — Production | Strong (TFX, Serving) | Growing (TorchServe, Lightning) |
| Long-term — Cutting edge | Often behind PyTorch | Most papers have PyTorch code |
For beginners: Keras (
tf.keras) has the gentlest initial curve. But PyTorch's curve is more consistent — the hardest part is just learning ML, not the framework.
Job market 2025
| Metric | TensorFlow | PyTorch |
|---|---|---|
| Job listings (LinkedIn, ~2025) | ~15k | ~22k |
| Research papers | ~20–30% | ~70–80% |
| Hugging Face model repos | ~10% | ~90% |
| Kaggle competition kernels | ~35% | ~65% |
| Salary premium | Similar | Slight edge at top-tier AI labs |
| Demand trend | Stable / slight decline | Growing strongly |
Full comparison
| Feature | TensorFlow 2.x | PyTorch 2.x |
|---|---|---|
| Execution model | Eager + static graph (tf.function) | Dynamic graph + optional compile |
| API simplicity | High (Keras) | Medium (explicit loops) |
| Debugging | Harder (graph tracing) | Easy (native Python) |
| Research adoption | 20–30% | 70–80% |
| LLM ecosystem | Limited | Dominant (HF, vLLM, TGI) |
| Mobile deployment | Excellent (TFLite) | Growing (ExecuTorch) |
| Browser | TensorFlow.js | ONNX/onnxruntime-web |
| TPU support | Native (Google Cloud) | Via XLA (partial) |
| GPU optimisation | Good | Excellent (deep CUDA) |
| Distributed training | MirroredStrategy, TPUStrategy | DDP, FSDP, DeepSpeed |
| Graph compilation speedup | XLA: 2–4× | torch.compile: 1.5–3× |
| ONNX export | tf2onnx (indirect) | Native torch.onnx.export |
| Model serving | TF Serving (mature) | TorchServe |
| Production ML pipelines | TFX (mature) | Metaflow, MLflow |
| Community (GitHub stars) | ~185k | ~85k (but higher velocity) |
| Backing | Meta AI | |
| License | Apache 2.0 | BSD 3-Clause |
| Version stability | TF1→TF2 breaking change | Stable since 1.0 |
TensorFlow vs PyTorch vs JAX vs Keras 3
| Framework | Best for | Backed by |
|---|---|---|
| TensorFlow 2 | Enterprise, mobile, Google Cloud | |
| PyTorch 2 | Research, LLMs, generative AI | Meta AI |
| JAX | Research requiring XLA, functional style | Google DeepMind |
| Keras 3 | Multi-backend (TF/PyTorch/JAX) high-level API | Independent + Google |
| MXNet | AWS (legacy, largely abandoned) | Apache / AWS |
| PaddlePaddle | China market | Baidu |
JAX is gaining traction for research requiring custom gradient computation. Keras 3 can run on any backend — useful if your team needs portability.
Decision guide
Starting a new ML project?
├── Research / novel architecture → PyTorch
├── Working with LLMs / Hugging Face → PyTorch
├── Diffusion / generative models → PyTorch
│
├── Need TFLite for mobile → TensorFlow
├── Google Cloud / TPUs → TensorFlow
├── Browser ML (JS) → TensorFlow.js
│
├── Enterprise team, need Keras stability → TensorFlow / Keras 3
├── Quick prototype, familiar with PyTorch → PyTorch
└── Multi-backend portability → Keras 3
Common mistakes
| Mistake | Why it hurts | Fix |
|---|---|---|
Using TF1-style Session in TF2 |
Unnecessary complexity | Use eager mode, tf.function only when needed |
Calling loss.backward() without zero_grad() |
Gradient accumulation bugs | Always call optimizer.zero_grad() first |
Forgetting model.eval() for inference |
Dropout/BatchNorm active → wrong predictions | Always switch modes |
| Loading entire dataset into GPU memory | OOM errors | Use DataLoader with streaming |
tf.function + Python side effects |
Silent bugs (function only traces once) | Keep side effects outside @tf.function |
Mixing .numpy() with GPU tensors |
Forces CPU transfer | Stay on device until final output |
| No mixed precision | 2× slower training, 2× more VRAM | Enable AMP / mixed_precision |
| Picking framework, not algorithm | Framework != results | Model architecture matters more than TF vs PT |
FAQ
Q: Should a beginner learn TensorFlow or PyTorch?
Start with PyTorch. The explicit training loop teaches you what's actually happening. Once you understand gradient descent and backpropagation, pick up Keras for fast prototyping — it runs on both backends now (Keras 3).
Q: Is PyTorch replacing TensorFlow?
In research and generative AI — yes, PyTorch has become the default. In enterprise and mobile deployment, TensorFlow (especially TFLite and TF Serving) retains significant production usage. Both will coexist for years.
Q: Which is faster — TensorFlow or PyTorch?
Within 5–15% for most workloads. torch.compile (PyTorch 2.0+) matches or beats XLA on many NVIDIA workloads. TF has an edge on TPUs. Profile your specific model — don't assume framework determines speed.
Q: Can I use both in the same project?
Yes, via ONNX. Export a PyTorch model to ONNX, then load it in TensorFlow (or any runtime). ONNX is the interoperability layer for the ML ecosystem.
Q: What about Hugging Face — does it work with both?
Hugging Face Transformers supports both, but PyTorch is the primary backend. Most model checkpoints are PyTorch-first; TensorFlow versions exist for major models but can lag behind.
Q: Which should I learn for getting a job in AI/ML?
PyTorch is more in demand at top AI labs, startups, and research roles. TensorFlow is preferred at Google and enterprises with legacy deployments. For most job seekers in 2025 — learn PyTorch first.