Интерактивное обучение моделей в Pytorch

Мне нужно тренировать две модели параллельно. Каждая модель имеет свою функцию активации с обучаемыми параметрами. Я хочу обучить модель 1 и модель 2 таким образом, чтобы параметры функции активации модели 1 (например, alpha1) отделялись от параметров модели 2 (например, alpha2) промежутком в 2; т.е. |alpha_1 - alpha_2| > 2. Интересно, как бы я мог включить это в функцию потерь для обучения.

1 ответ

Пример определения модуля

я использую torch.nn.PReLU как параметрическая активация, о которой вы говорите.get_weight создан для удобства.

import torch


class Module(torch.nn.Module):
    def __init__(self, in_features, out_features):
        super().__init__()
        self.input = torch.nn.Linear(in_features, 2 * in_features)
        self.activation = torch.nn.PReLU()
        self.output = torch.nn.Linear(2 * in_features, out_features)

    def get_weight(self):
        return self.activation.weight

    def forward(self, inputs):
        return self.output(self.activation(self.inputs(inputs)))

Модули и настройка

Здесь я использую один оптимизатор для оптимизации параметров обоих модулей, о которых вы говорите. criterion возможно mean squared error, cross entropy или что-то еще, что вам нужно.

module1 = Module(20, 1)
module2 = Module(20, 1)

optimizer = torch.optim.Adam(
    itertools.chain(module1.parameters(), module2.parameters())
)
critertion = ...

Обучение

Вот один шаг, вы должны упаковать его в цикл для ваших данных, как это обычно делается, надеюсь, этого достаточно, чтобы вы поняли идею:

inputs = ...
targets = ...

output1 = module1(inputs)
output2 = module2(inputs)

loss1 = criterion(output1, targets)
loss2 = criterion(output2, targets)

total_loss = loss1 + loss2
total_loss += torch.nn.functional.relu(
    2 - torch.abs(module1.get_weight() - module2.get_weight()).sum()
)
total_loss.backward()

optimizer.step()

Эта строка - это то, что вам нужно в этом случае:

total_loss += torch.nn.functional.relu(
    2 - torch.abs(module1.get_weight() - module2.get_weight()).sum()
)

reluиспользуется так, чтобы сеть не извлекала бесконечную выгоду только от создания расходящихся весов. Если бы его не было, потери стали бы отрицательными, чем больше была бы разница между весами. В этом случае, чем больше разница, тем лучше, но она не имеет значения после того, как зазор станет больше или равен2.

Возможно, вам придется увеличить 2 к 2.1 или что-то в этом роде, если вам нужно преодолеть порог 2 как стимул для оптимизации стоимости, когда она близка к 2.0 было бы маленьким.

редактировать

Без явно заданного порога это может быть сложно, но, возможно, что-то вроде этого сработает:

total_loss = (
    (torch.abs(module1) + torch.abs(module2)).sum()
    + (1 / torch.abs(module1) + 1 / torch.abs(module2)).sum()
    - torch.abs(module1 - module2).sum()
)

Это своего рода хакерство для сети, но, возможно, стоит попробовать (если вы примените дополнительные L2 регуляризация).

По существу, эта потеря будет оптимальной при -inf, +inf пары гирь в соответствующих позициях и никогда не будут меньше нуля.

Для тех весов

weights_a = torch.tensor([-1000.0, 1000, -1000, 1000, -1000])
weights_b = torch.tensor([1000.0, -1000, 1000, -1000, 1000])

Убыток по каждой части составит:

(torch.abs(module1) + torch.abs(module2)).sum() # 10000
(1 / torch.abs(module1) + 1 / torch.abs(module2)).sum() # 0.0100
torch.abs(module1 - module2).sum() # 10000

В этом случае сеть может получить легкую выгоду, просто увеличивая веса с противоположными знаками в обоих модулях и игнорируя то, что вы хотите оптимизировать (большой L2 веса обоих модулей могут помочь, и я думаю, что оптимальное значение будет 1/-1 в случае L2с alpha равно 1), и я подозреваю, что сеть может быть очень нестабильной.

При использовании этой функции потерь, если сеть получает неверный знак большого веса, она будет серьезно наказана.

В этом случае вы останетесь с L2 Параметр alpha, который нужно настроить, чтобы заставить его работать, что не так строго, но все же требует выбора гиперпараметра.

Другие вопросы по тегам