DeployU
Interviews / AI & MLOps / What is the difference between `apply` and `map` in PyTorch?

What is the difference between `apply` and `map` in PyTorch?

conceptual Advanced Topics Interactive Quiz Code Examples

The Scenario

You are an ML engineer at a research lab. You are working on a new model architecture that has a mix of different types of layers, including linear layers, convolutional layers, and recurrent layers.

You want to initialize the weights of the model in a custom way. Specifically, you want to use Xavier initialization for the linear layers and Kaiming initialization for the convolutional layers.

You also need to be able to easily move the model and all its tensors to a different device, such as a GPU.

The Challenge

Explain how you would use the apply and map methods in PyTorch to solve this problem. What is the difference between these two methods, and when would you use one over the other?

Wrong Approach

A junior engineer might try to solve this problem by manually iterating over the model's parameters and initializing them one by one. This would be verbose and error-prone, and it would not be a good long-term solution. They might also not be aware of the `map` method for moving tensors to a different device.

Right Approach

A senior engineer would know that `apply` is the correct tool for custom weight initialization and that `map` is the correct tool for moving a nested data structure of tensors to a different device. They would be able to explain the difference between these two methods and would have a clear plan for how to use them to solve this problem.

Step 1: apply vs. map

Featureapplymap (from torch.utils.data.nested)
PurposeTo apply a function to all the modules in a model, recursively.To apply a function to all the tensors in a nested data structure.
Use CasesCustom weight initialization, modifying layers.Moving a nested data structure of tensors to a different device.
ScopeOperates on nn.Module objects.Operates on Tensor objects within a nested data structure.

Step 2: Custom Weight Initialization with apply

The apply method is perfect for our custom weight initialization task. We can define a function that checks the type of each module and applies the appropriate initialization.

import torch.nn as nn

def init_weights(m):
    if isinstance(m, nn.Linear):
        nn.init.xavier_uniform_(m.weight)
        m.bias.data.fill_(0.01)
    elif isinstance(m, nn.Conv2d):
        nn.init.kaiming_uniform_(m.weight)
        m.bias.data.fill_(0.01)

model = MyModel()
model.apply(init_weights)

Step 3: Moving the Model with map

The map method is not a method of nn.Module. It is a function in the torch.utils.data.nested module that allows you to apply a function to all the tensors in a nested data structure. While you can move a model to a device with .to(device), map is useful for more complex data structures. For example, if you have a dictionary of tensors, you can use map to move them all to the GPU.

import torch

data = {
    "a": torch.randn(2, 2),
    "b": [torch.randn(3, 3), torch.randn(4, 4)]
}

# Move all the tensors to the GPU
data = torch.utils.data.nested.map(lambda x: x.to("cuda"), data)

In the context of our model, the primary way to move it to a device is model.to(device).

Practice Question

You want to replace all the `ReLU` activation functions in your model with `LeakyReLU`. Which method would you use?