Here’s an artificial example
import torch
import torch.nn as nn

class ExampleModule(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Linear(2, 2, bias=False)
        self.layer2 = nn.Linear(2, 2, bias=False)
        self.layer3 = nn.Linear(2, 2, bias=False)
        self.layer3 = self.layer1
        # Same result if you do
        #     self.layer3.weight = self.layer1.weight
    
# Moving model to device: works!
example_module = ExampleModule().cuda()
state_dict = example_module.cpu().state_dict()
assert state_dict["layer1.weight"].data_ptr() == state_dict["layer3.weight"].data_ptr()  
# passes

# Moving individual tensors: doesn't work
example_module = ExampleModule().cuda()
state_dict = {k: v.cpu() for k, v in example_module.state_dict().items()}
assert state_dict["layer1.weight"].data_ptr() == state_dict["layer3.weight"].data_ptr()  
# fails

In the first case, moving the whole model to a device correctly handles this. But there are other use-cases where you don’t want to move the model (e.g. if you’re just storing a checkpoint of the state_dict). In cases where you need to manually move the weights to a different device, the data_ptrs end up being different.