Lesson · 40 min · Free
Deploying a PyTorch Model
Deploying a PyTorch Model body { font-family: sans-serif; line-height: 1.6; margin: 20px; } h1 { color: #2c3e50; } h2 { color: #34495e; } pre { background-color: #ecf0f1; padding: 15px; border-radius: 5px; overflow-x: au
Deploying a PyTorch Model
Welcome to this lesson on deploying a PyTorch model. As future professionals in pharmacy and biotechnology, understanding how to take an AI model from a research environment to a practical application is crucial. Whether it's for predicting drug interactions, analyzing medical images, or optimizing biochemical processes, the ability to deploy your models allows them to create real-world impact. Deployment is the process of making your trained machine learning model available for use by other applications or users. This often involves packaging the model, setting up an environment where it can run efficiently, and creating an interface for it to receive inputs and return predictions. For PyTorch models, this typically means saving the model's learned parameters and then loading them into an inference environment. The core idea is to separate the training phase (which is often resource-intensive and iterative) from the inference phase (where the model is used to make predictions on new, unseen data). For deployment, we primarily focus on inference.
Saving and Loading PyTorch Models
The most common way to save a PyTorch model is by serializing its internal state dictionary. This dictionary contains all the learnable parameters (weights and biases) of your model. PyTorch's torch.save() function is used for this purpose, and torch.load() is used to retrieve it. It's good practice to save only the state_dict rather than the entire model object, as this makes the saved file independent of the specific model class definition at the time of saving, making it more robust for deployment. Let's consider a simple neural network for illustration. Imagine we've trained a model ( MyNeuralNet ) to classify certain biological data points. import torch import torch.nn as nn import torch.optim as optim # Define a simple neural network (as you would during training) class MyNeuralNet(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(MyNeuralNet, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, output_size) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out # Assume these are the dimensions you used for training input_dim = 10 hidden_dim = 50 output_dim = 2 # e.g., binary classification # 1. Instantiate your model (this would typically be after training) model = MyNeuralNet(input_dim, hidden_dim, output_dim) # (For demonstration, let's pretend we just finished training and have a trained model) # You would have run model.train() and optimized weights here. # For simplicity, we'll just save the initial state. # In a real scenario, this 'model' would be the fully trained one. # 2. Save the model's state dictionary model_path = 'my_trained_model.pth' torch.save(model.state_dict(), model_path) print(f"Model state dictionary saved to {model_path}") # --- Deployment Environment --- # 3. In a new script or deployment environment, # first define the exact same model architecture. # This is crucial! The class definition must match. deployment_model = MyNeuralNet(input_dim, hidden_dim, output_dim) # 4. Load the saved state dictionary into the new model instance deployment_model.load_state_dict(torch.load(model_path)) # 5. Set the model to evaluation mode # This disables dropout and batch normalization layers if present, # ensuring consistent predictions. deployment_model.eval() print("Model successfully loaded for deployment!") # Now, the 'deployment_model' is ready to make predictions. # Example: Making a prediction sample_input = torch.randn(1, input_dim) # A single sample with 'input_dim' features with torch.no_grad(): # Disable gradient calculations for inference to save memory and speed up computation prediction = deployment_model(sample_input) print(f"Sample input: {sample_input}") print(f"Prediction output: {prediction}") In the code above, observe the critical steps: Saving: We use torch.save(model.state_dict(), path) . The .pth or .pt extension is commonly used. Loading: We first instantiate an empty model with the exact same architecture . Then, model.load_state_dict(torch.load(path)) populates this empty model with the saved weights. Evaluation Mode: model.eval() is essential. It tells PyTorch to set all layers to inference mode, which is important for layers like dropout and batch normalization that behave differently during training and inference. No Gradient: with torch.no_grad(): disables gradient calculation, which is unnecessary for inference and saves computational resources.
Considerations for Production Deployment
While saving and loading the state_dict is fundamental, real-world deployment often involves more: Environment Setup: Ensuring the deployment environment has all necessary libraries (PyTorch, NumPy, etc.) and the correct Python version. API Endpoint: For web applications, you'd typically wrap your model in a web framework like Flask or FastAPI to create a REST API. This allows other services or user interfaces to send data to your model and receive predictions. Containerization: Technologies like Docker can package your application and its dependencies into a portable container, ensuring it runs consistently across different environments. Cloud Platforms: Services like AWS SageMaker, Google Cloud AI Platform, or Azure Machine Learning provide managed services for deploying and scaling machine learning models. ONNX Export: For cross-platform deployment or performance optimization, you might export your PyTorch model to ONNX (Open Neural Network Exchange) format. This allows it to be run with various inference runtimes (like ONNX Runtime) which can offer performance benefits and broader compatibility. Let's look at a very basic example of how you might wrap your loaded model in a simple Flask API. This is a conceptual example to show the flow, not a production-ready application. # This code snippet would be in a separate file, e.g., 'app.py' from flask import Flask, request, jsonify import torch import torch.nn as nn import numpy as np # Re-define your model architecture (must be identical to training) class MyNeuralNet(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(MyNeuralNet, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, output_size) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out app = Flask(__name__) # Load the model globally when the application starts # This avoids reloading the model for every request, which would be inefficient. model_path = 'my_trained_model.pth' # Ensure this file exists from the previous example input_dim = 10 hidden_dim = 50 output_dim = 2 try: deployed_model = MyNeuralNet(input_dim, hidden_dim, output_dim) deployed_model.load_state_dict(torch.load(model_path)) deployed_model.eval() print("Model loaded successfully for API.") except Exception as e: print(f"Error loading model: {e}") deployed_model = None # Indicate that model loading failed @app.route('/predict', methods=['POST']) def predict(): if deployed_model is None: return jsonify({'error': 'Model not loaded'}), 500 try: data = request.get_json(force=True) # Expecting input as a list of numbers input_array = np.array(data['features'], dtype=np.float32) # Ensure input dimensions match the model's expectation if input_array.shape[0] != input_dim: return jsonify({'error': f'Expected {input_dim} features, got {input_array.shape[0]}'}), 400 input_tensor = torch.from_numpy(input_array).unsqueeze(0) # Add batch dimension with torch.no_grad(): output = deployed_model(input_tensor) # Convert tensor output to a Python list/scalar probabilities = torch.softmax(output, dim=1).squeeze().tolist() predicted_class = torch.argmax(output, dim=1).item() return jsonify({ 'prediction': predicted_class, 'probabilities': probabilities }) except Exception as e: return jsonify({'error': str(e)}), 400 if __name__ == '__main__': # To run this, you would typically save the first code block's output # to 'my_trained_model.pth' first, then run this 'app.py' file. # From your terminal: python app.py # Then you can test with curl: # curl -X POST -H "Content-Type: application/json" -d '{"features": [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]}' http://127.0.0.1:5000/predict app.run(debug=True) # debug=True is for development, set to False for production In this Flask example: The model is loaded once when the Flask app starts, not for every request. A /predict endpoint is created that accepts POST requests with JSON data. It expects a 'features' key in the JSON, containing a list of numbers. It converts this input into a PyTorch tensor, makes a prediction, and returns the result as JSON.
Key Takeaways
Deployment is moving a trained model from research to practical use. PyTorch models are typically saved and loaded using their state_dict() . The model architecture must be identical for saving and loading. Always use model.eval() and with torch.no_grad(): for inference. Real-world deployment often involves web frameworks (Flask, FastAPI), containerization (Docker), and cloud services. Exporting to ONNX can provide performance and compatibility benefits.
Practice Exercise
Watch the full lesson — free
This topic is part of AI for Beginners, a complete AI-narrated video course. Press play once and watch the entire lecture like a movie.
Start the course free →