A trained PyTorch model becomes useful outside training when it can turn a prepared tensor batch into scores, probabilities, or labels. Inference is the handoff from learned weights to application code, so the model has to use evaluation behavior and the forward pass should avoid gradient bookkeeping.
model.eval() changes the module's training flag and switches affected layers such as Dropout and BatchNorm into evaluation behavior. torch.inference_mode() disables autograd tracking for the forward pass and removes extra overhead when the output will not feed a later backward() call.
The smoke script uses a small CPU classifier so the inference path can be verified without a trained checkpoint. In a real project, load or instantiate the same model class that produced the weights, prepare tensors with the shape and device the model expects, and treat the output shape plus predicted labels as the first proof that the handoff works.
Related: How to install PyTorch with pip
Related: How to save and load a PyTorch model
Related: How to select a device in PyTorch
Related: How to run evaluation in PyTorch
import torch from torch import nn class TicketClassifier(nn.Module): def __init__(self): super().__init__() self.dropout = nn.Dropout(p=0.5) self.linear = nn.Linear(4, 3) def forward(self, inputs): return self.linear(self.dropout(inputs)) class_names = ["standard", "priority", "urgent"] model = TicketClassifier() with torch.no_grad(): model.linear.weight.copy_( torch.tensor( [ [0.4, -0.2, 0.1, 0.3], [-0.1, 0.5, 0.2, -0.2], [0.2, 0.1, -0.1, 0.6], ], dtype=torch.float32, ) ) model.linear.bias.copy_(torch.tensor([0.0, 0.1, -0.1])) features = torch.tensor( [ [0.9, 0.1, 0.2, 0.3], [0.1, 0.8, 0.4, 0.2], [0.2, 0.2, 0.1, 0.9], ], dtype=torch.float32, ) model.eval() with torch.inference_mode(): inference_mode_active = torch.is_inference_mode_enabled() logits = model(features) probabilities = torch.softmax(logits, dim=1) predicted_indexes = probabilities.argmax(dim=1) predicted_labels = [class_names[index] for index in predicted_indexes.tolist()] print(f"model training mode: {model.training}") print(f"dropout training mode: {model.dropout.training}") print(f"inference mode active: {inference_mode_active}") print(f"logits shape: {tuple(logits.shape)}") print(f"probabilities shape: {tuple(probabilities.shape)}") print(f"predicted labels: {', '.join(predicted_labels)}") print(f"logits require grad: {logits.requires_grad}")
The Dropout layer makes the evaluation-mode check visible. Replace TicketClassifier and features with the model class and input tensor shape used by the trained model.
$ python inference_run_demo.py model training mode: False dropout training mode: False inference mode active: True logits shape: (3, 3) probabilities shape: (3, 3) predicted labels: standard, priority, urgent logits require grad: False
model training mode: False and dropout training mode: False confirm evaluation behavior. inference mode active: True and logits require grad: False confirm that the forward pass did not track gradients.
def run_model(model, inputs): model.eval() with torch.inference_mode(): logits = model(inputs) return logits
model.eval() is sticky until model.train() is called. Switch back to training mode before another optimizer step.
def classify(model, inputs, class_names): model.eval() with torch.inference_mode(): logits = model(inputs) probabilities = torch.softmax(logits, dim=1) predicted_indexes = probabilities.argmax(dim=1) return [ { "label": class_names[index], "confidence": probabilities[row, index].item(), } for row, index in enumerate(predicted_indexes.tolist()) ]
Use softmax(dim=1) for a batch of class logits shaped like batch_size x class_count. Regression models usually return raw numeric predictions instead of class probabilities.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = model.to(device) inputs = inputs.to(device) with torch.inference_mode(): logits = model(inputs)
Mixed devices raise runtime errors during the forward pass. Choose the device once near the application entry point and move both model parameters and input tensors before inference.
$ rm inference_run_demo.py