更改 nn.Parameter 的某些元素时的副作用

Sideffects when changind some elements of nn.Parameter

我想要一些 nn.Parameter 依赖于数据,例如不同的人有不同的价值观。我意识到在训练一个人的同时,其他人的结果也会发生变化。我在下面创建了一个小演示。

我试图让一切都变得确定,正如预期的那样,改变 nn.Parameter 的大小不会改变结果。在训练了 100 个 epoch 之后,我总是得到 [1.0605, 1.0715, 1.0622, 0.9702] for all_calibration_params[0]。但是 all_calibration_params 的所有其他参数也会发生变化,并且它们都以相同的值变化,对于 all_calibration_params = nn.Parameter(torch.ones(10, 4)) 我得到所有其他 9 个值的 [0.8455, 0.8455, 0.8455, 0.8455] 。我希望这些值保持 [1, 1, 1, 1]。任何人都可以向我解释一下,为什么即使我在训练期间不使用它们,其他值也会改变?

谢谢。

import torch
from pytorch_lightning import LightningModule, Trainer, seed_everything
from torch import nn
from torch.utils.data import Dataset, DataLoader
import torch.nn.functional as F


class DemoDataset(Dataset):
    """
    Generate random data, only for person 0.
    Data is always random but output is always 1.
    """

    def __init__(self):
        pass

    def __len__(self) -> int:
        return 1_000

    def __getitem__(self, idx):
        return {
            'person': torch.zeros(1),
            'data': torch.rand(1),
            'output': torch.ones(1),
        }


class DemoModel(LightningModule):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.all_calibration_params = nn.Parameter(torch.ones(3, 4))  # all values are 1

        self.fc_1 = nn.Linear(1, 4)
        self.fc_2 = nn.Linear(4 + 4, 1)

    def forward(self, person: torch.Tensor, data: torch.Tensor):
        x = self.fc_1(data)
        calibration_params = self.all_calibration_params[person].squeeze(1)
        x = torch.cat((x, calibration_params), dim=1)
        x = self.fc_2(x)

        return x


class Model(DemoModel):
    def __init__(self, learning_rate: float, weight_decay: float, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.learning_rate = learning_rate
        self.weight_decay = weight_decay

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)

    def training_step(self, train_batch: dict, batch_idx: int):
        data, person, output = train_batch['data'], train_batch['person'].long(), train_batch['output']

        pred = self(person, data)

        loss = F.mse_loss(pred, output)
        return loss


if __name__ == '__main__':
    seed_everything(42)

    model = Model(learning_rate=1e-4, weight_decay=1e-5)

    dataset = DemoDataset()
    dataloader = DataLoader(dataset, batch_size=64)

    print(model.all_calibration_params)

    trainer = Trainer(
        max_epochs=100,
        deterministic=True
    )
    trainer.fit(model, dataloader)

    print(model.all_calibration_params)

    # model.all_calibration_params[1] should be [1, 1, 1, 1]
    print(model.all_calibration_params[1], torch.ones(1, 4))

输出:

tensor([[1.0605, 1.0715, 1.0622, 0.9702],
        [0.8455, 0.8455, 0.8455, 0.8455],
        [0.8455, 0.8455, 0.8455, 0.8455]], requires_grad=True)

编辑:

如果 nn.Parameter 中的其他元素改变了输出,我添加了一个小测试,但没有,所以我不明白为什么它们在训练时会受到影响。

batch = next(iter(dataloader))
data, person = batch['data'], batch['person'].long()
output_org = model(person, data)
print(output_org[0])

model.freeze()
model.all_calibration_params[1:] = torch.ones(2, 4)
model.unfreeze()

output_mod = model(person, data)
print(output_mod[0])
print(torch.equal(output_org, output_mod))

输出:

tensor([0.6334], grad_fn=<SelectBackward>)
tensor([0.6334], grad_fn=<SelectBackward>)
True

您的数据集始终 returns 'person'0 索引, self.all_calibration_params[person] 将始终 select 第一行。

其他两行得到更新的原因是优化器上设置的权重衰减。这将更新所有参数,无论它们是否在模型推理期间被使用。