PyTorch 2.15 was released on 28 October 2026. According to the PyTorch team it contains 3,117 commits from 583 contributors. The release focuses on distributed training, low-precision inference and wider hardware support. It also has a few breaking changes that can surprise you if you upgrade without checking.

This guide covers the changes most ML engineers will notice, with code to check your setup and adopt the new features.

Breaking changes to check first

Python 3.11 or newer is required

PyTorch 2.15 no longer supports Python 3.10. No 3.10 wheels are built and 3.10 is no longer tested in CI. Supported versions are Python 3.11 to 3.15, including the free-threaded 3.14t and 3.15t builds. torch.compile also works on Python 3.15 now.

Check before you upgrade:

python --version   # must be 3.11+
pip install --upgrade torch
python -c "import torch, sys; print(torch.__version__, sys.version)"

If you’re stuck on 3.10, pin torch<2.15 in your requirements until you can move.

macOS 14 is no longer supported for MPS

For Apple Silicon GPU (MPS) acceleration, PyTorch 2.15 requires macOS 15 or newer. The team says it may still run on macOS 14, but it isn’t tested there and issues won’t be fixed.

“nccl” now means the rewritten backend

In 2.15, every nccl process group uses the rewritten NCCL backend by default (it was the opt-in nccl2 backend in 2.14). If your distributed jobs behave differently, you can roll back:

# Option 1: environment variable
export TORCH_DIST_USE_NCCL2=0
# Option 2: name the legacy backend explicitly
import torch.distributed as dist
dist.init_process_group(backend="nccl-legacy")

Use the rollback only as a temporary measure, and report the problem upstream so it gets fixed.

FSDP2 gradient dtype behaviour changed

FSDP2 now separates sharded and unsharded gradient dtypes. Gradients accumulate directly in .grad and follow each parameter’s grad_dtype, which changes the default when reduce_dtype=None. If you train with FSDP2 and mixed precision, compare loss curves before and after the upgrade on a short run.

New: debugging hangs with the flight recorder

Anyone who has trained on many GPUs knows the pain: one stalled rank freezes the whole job and leaves no clues. The rewritten NCCL backend includes a flight recorder that writes a post-mortem to disk when a collective hangs. When a timeout happens on any rank, every rank dumps what it was doing at that moment. That makes hangs much easier to diagnose without reproducing them by hand.

New: keeping sensitive parameters in fp32 with FSDP2

Some parameters, such as MoE router gates and norm weights, are sensitive to low precision. FSDP2 can now keep them in fp32 while the rest of the model trains in bf16, still with one all-gather per fully_shard. The release notes name the hook as MixedPrecisionPolicy(param_dtype_override_fn=...). The API is marked unstable, so check the FSDP2 docs for the exact signature before you rely on it.

Apple Silicon gets FP8 and stream APIs

Mac users get some of the most visible improvements in this release:

  • torch.float8_e4m3fn is supported on MPS, along with _scaled_mm.
  • A new MPS grouped_mm with a decode fast path gave 1.32-1.34x higher MoE decode throughput on an M3 Ultra in PyTorch’s tests.
  • F.linear now uses gemv kernels for decode-sized shapes.
  • A public stream API modelled on CUDA’s: torch.mps.Stream, current_stream, set_stream, the torch.mps.stream context manager and Tensor.record_stream.

Check that MPS works on your Mac and try FP8 tensors:

import torch

if torch.backends.mps.is_available():
    device = torch.device("mps")
    x = torch.randn(4, 4, device=device)
    x8 = x.to(torch.float8_e4m3fn)
    print(x8.dtype, x8.device)
    print(x8.to(torch.float32))  # back to fp32 to inspect values
else:
    print("MPS not available")

And use the new stream API to overlap work, the same way you would on CUDA:

import torch

device = torch.device("mps")
s = torch.mps.Stream()

a = torch.randn(1024, 1024, device=device)
with torch.mps.stream(s):
    b = a @ a          # queued on the side stream

torch.mps.synchronize()   # wait for queued work before using b
print(b.sum().item())

These stream APIs are new, so test them on your own workload, and check the PyTorch MPS docs if a method behaves differently from CUDA.

Several MPS operators also moved to native Metal kernels in this release. The PyTorch team reports large speedups for some of them, such as single-row softmax over a large vocabulary and QR decomposition.

Platform updates at a glance

  • CUDA: the default wheel moves to CUDA 13.2, with CUDA 13.4 wheels alongside. The 13.4 wheels add experimental support for NVIDIA Rubin GPUs.
  • NVIDIA Blackwell: faster NVFP4 decoding through NVGEMM, with fewer, better-overlapped kernel launches.
  • AMD ROCm: ROCm 10.0 wheels are available, all ROCm wheels are built on AMD’s TheRock packages, and ROCm is installed as a pip dependency instead of being bundled. FlyDSL kernels arrive on gfx950.
  • Intel XPU: simpler SYCL kernel authoring, IPC support for torch.xpu.Event, and better multi-GPU profiling.

To install a specific CUDA or ROCm build, use the selector on pytorch.org/get-started, which gives you the correct index URL for your platform.

A safe upgrade checklist

  1. Confirm Python 3.11+ (and macOS 15+ if you use MPS).
  2. Upgrade in a fresh virtual environment first, not your main one (commands below).
  3. Run a short training job and compare loss and throughput with your previous version.
  4. For distributed jobs, keep TORCH_DIST_USE_NCCL2=0 ready as a fallback.
  5. If you use FSDP2 with mixed precision, check the gradient dtype change.
  6. Pin the version in requirements.txt once everything checks out.
python -m venv torch215 && source torch215/bin/activate
pip install torch torchvision
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"

Takeaway

PyTorch 2.15 is mainly about scale and hardware: a sturdier NCCL backend with built-in hang diagnostics, finer FSDP2 control, and big improvements for Apple Silicon and AMD. Before you upgrade, check the Python 3.10 and macOS 14 drops and the NCCL default change, and test on a short run first.

Sources: PyTorch 2.15 Release Blog · PyTorch releases on GitHub