Back to Blog
Python

Python PyTorch Model Eval no_grad and inference_mode

python pytorch model eval no_grad and inference_mode: Learn how model.eval(), torch.no_grad(), and torch.inference_mode() affect PyTorch inference, when to combine the...

PyTorchinferenceautogradmodel evaluationgradient trackingperformance
Diagram showing a PyTorch model in eval mode with gradient tracking disabled, comparing no_grad and inference_mode contexts.

python pytorch model eval no_grad and inference_mode requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you run a PyTorch model for inference, you typically wrap the forward pass in torch.no_grad() or torch.inference_mode() and call model.eval(). But what do these actually do, and why do you need both? This article explains the differences between model.eval(), torch.no_grad(), and torch.inference_mode() in Python PyTorch, and how to use them correctly for inference.

Why Inference Needs a Different Context

During training, PyTorch's autograd engine records every operation on tensors that require gradients. This graph enables backpropagation, but it also consumes memory and adds overhead. When you switch to inference, you no longer need gradients, so you want to disable autograd to reduce memory usage and improve latency. However, disabling autograd alone is not enough if your model contains layers that behave differently in training versus evaluation, such as dropout or batch normalization.

The model.eval() method sets the model to evaluation mode, which changes the behavior of those layers. But model.eval() does not disable gradient tracking. To disable autograd, you need a context manager like torch.no_grad() or torch.inference_mode(). Understanding the interaction between these two mechanisms is essential for correct and efficient inference.

What model.eval() Actually Changes

model.eval() is a method on nn.Module that sets the module and all its submodules to evaluation mode. This affects layers that have different training and inference behaviors:

  • Dropout: In training mode, dropout randomly zeroes some activations. In eval mode, it becomes an identity function.
  • BatchNorm: In training mode, batch normalization uses the statistics of the current batch. In eval mode, it uses the running mean and variance accumulated during training.
  • Other layers like nn.Dropout2d, nn.InstanceNorm, etc., also follow this pattern.

Calling model.eval() does not stop gradient computation. If you pass a tensor with requires_grad=True through the model, autograd will still build a graph. This is why you need a separate mechanism to disable gradient tracking.

import torch import torch.nn as nn model = nn.Sequential(nn.Linear(10, 5), nn.ReLU()) model.eval() # sets eval mode x = torch.randn(1, 10, requires_grad=True) y = model(x) print(y.requires_grad) # True, because x requires grad

In the example above, model.eval() changes layer behavior but does not prevent autograd from tracking operations. For inference, you almost always want to combine model.eval() with a gradient-free context.

torch.no_grad() and Gradient Tracking

torch.no_grad() is a context manager that disables autograd for the enclosed block. Any tensor operation performed inside it does not build a computation graph, and tensors created inside do not have requires_grad set to True. This reduces memory usage because intermediate activations are not stored for backward, and it speeds up computation by skipping gradient bookkeeping.

model.eval() with torch.no_grad(): y = model(x) print(y.requires_grad) # False

torch.no_grad() is the traditional way to run inference. It is safe to use with any model and does not impose restrictions on the operations you can perform. However, it still allows some internal autograd metadata to be created, which can add a small overhead compared to torch.inference_mode().

torch.inference_mode() and Its Restrictions

torch.inference_mode() was introduced in PyTorch 1.9 as a more aggressive alternative to no_grad(). It disables autograd entirely and also marks tensors created inside as "inference tensors." These tensors are optimized for inference and cannot be used in operations that require gradients. For example, you cannot call .backward() on a tensor produced inside inference_mode(), and you cannot move an inference tensor to a different device and then use it in a training context without converting it first.

model.eval() with torch.inference_mode(): y = model(x) print(y.requires_grad) # False # y.backward() would raise an error

The main advantage of inference_mode() is performance. Because it disables more internal tracking than no_grad(), it can be faster, especially for small models or CPU inference. The trade-off is that you lose flexibility: if you need to use the output for anything that requires autograd, you must first call .clone().detach().requires_grad_(True) or run the operation outside the context.

Combining Eval Mode with Gradient-Free Contexts

For standard inference, the recommended pattern is to call model.eval() first and then wrap the forward pass in either torch.no_grad() or torch.inference_mode(). The eval mode ensures correct layer behavior, while the gradient-free context prevents autograd overhead.

model.eval() with torch.inference_mode(): predictions = model(batch)

This combination is what you should use for validation loops, model serving, and any scenario where you only need forward passes. If you are using a model that contains no training-dependent layers (e.g., a simple linear stack), model.eval() is not strictly necessary, but it is still a good habit to keep your code consistent.

Performance and Memory Tradeoffs

Both no_grad() and inference_mode() reduce memory usage by not storing activations for backpropagation. The difference between them is subtle but measurable. inference_mode() avoids even the minimal bookkeeping that no_grad() still performs, such as tracking version counters for tensors. For most applications, the performance difference is small, but for high-throughput inference or models with many small operations, inference_mode() can provide a noticeable improvement.

Memory savings are significant compared to training: you no longer keep intermediate activations, which can reduce memory consumption by a large factor. This is especially important when running large models on GPU with limited memory.

There is no universal benchmark that applies to all models, so the best approach is to profile your specific model. If you need to measure, use PyTorch's torch.profiler to compare the two contexts. In general, prefer inference_mode() for pure inference, but switch to no_grad() if you encounter compatibility issues or need to perform operations that require autograd on the outputs.

Common Pitfalls When Running Inference

One common mistake is forgetting to call model.eval(). If your model contains dropout or batch norm, running inference in training mode produces incorrect results because those layers behave differently. Another mistake is using no_grad() or inference_mode() without model.eval(). This disables gradients but leaves dropout active, which adds randomness to your predictions.

A subtler issue arises when you reuse a model that was previously in training mode. If you call model.train() for training and then switch to inference, you must explicitly call model.eval(). The context managers do not change the model's mode.

Finally, be careful with inference_mode() if you need to compute gradients on the output later. For example, in some meta-learning or gradient-based hyperparameter tuning scenarios, you might run a forward pass in inference mode and then want to backpropagate through the output. This will fail because inference tensors are not part of the autograd graph. In such cases, use no_grad() and then re-enable gradients with requires_grad_() on the output.

Choosing the Right Context for Your Use Case

The choice between no_grad() and inference_mode() depends on your specific requirements:

  • Use torch.no_grad() when you need maximum flexibility, such as when you might later need to compute gradients on the output, or when you are working with code that expects tensors to be part of the autograd system.
  • Use torch.inference_mode() for pure inference where you know you will not need gradients on the results. This includes model serving, batch prediction, and validation loops.
  • Always call model.eval() when your model contains layers that differ between training and evaluation, regardless of which gradient-free context you choose.

If you are unsure, start with torch.no_grad() because it is more permissive. If profiling shows that inference_mode() gives you a meaningful speedup and your use case is strictly inference, switch to it. The code changes are minimal: just replace the context manager.

# Option 1: no_grad model.eval() with torch.no_grad(): output = model(input) # Option 2: inference_mode model.eval() with torch.inference_mode(): output = model(input)

Both are valid, but inference_mode() is the more modern and optimized choice for inference-only workloads. Understanding the difference allows you to make an informed decision based on your model's behavior and your operational constraints.

python pytorch model eval no_grad and inference_mode: Practi | RYUSLOG DEV