Пользовательский оптимизатор PyTorch не работает должным образом и не может получить доступ к градиентам

Я пытаюсь реализовать алгоритм эластичного усреднения стохастического градиентного спуска (EASGD) из статьи Глубокое обучение с эластичным усреднением SGD и столкнулся с некоторыми проблемами.

Я использую класс PyTorch torch.optim.Optimizer и ссылаюсь на официальную реализацию SGD и официальную реализацию Accelerated SGD, чтобы с чего-то начать.

Код, который у меня есть:

import torch.optim as optim


class EASGD(optim.Optimizer):
    def __init__(self, params, lr, tau, alpha=0.001):
        self.alpha = alpha

        if lr < 0.0:
            raise ValueError(f"Invalid learning rate {lr}.")

        defaults = dict(lr=lr, alpha=alpha, tau=tau)
        super(EASGD, self).__init__(params, defaults)

    def __setstate__(self, state):
        super(EASGD, self).__setstate__(state)

    def step(self, closure=None):
        loss = None
        if closure is not None:
            with torch.enable_grad():
                loss = closure()

        for group in self.param_groups:
            tau = group['tau']

            for t, p in enumerate(group['params']):
                x_normal = p.clone()
                x_tilde = p.clone()

                if p.grad is None:
                    continue

                if t % tau == 0:
                    p = p - self.alpha * (x_normal - x_tilde)
                    x_tilde = x_tilde + self.alpha * (x_normal - x_tilde)

                d_p = p.grad.data
                p.data.add_(d_p, alpha=-group['lr'])

        return loss

Когда я запускаю этот код, я получаю следующую ошибку:

/home/user/github/test-repo/easgd.py:50: UserWarning: Доступ к атрибуту .grad тензора, который не является листовым тензором. Его атрибут .grad не будет заполнен во время autograd.backward(). Если вам действительно нужен градиент для нелистового тензора, используйте .retain_grad() на нелистовом тензоре. Если вы по ошибке получили доступ к нелистовому тензору, убедитесь, что вместо этого вы обращаетесь к листовому тензору. См. github.com/pytorch/pytorch/pull/30531 для получения дополнительной информации.

Чтение этого обсуждения PyTorch помогло понять, в чем разница между листовыми и нелистовыми переменными, но я не уверен, как мне исправить свой код, чтобы он работал правильно.

Любые советы о том, что делать или где искать приветствуются. Спасибо.

Вы уверены, что p в for t, p in enumerate(group['params']): является листовым тензором?

Theodor Peifer 10.12.2020 09:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
327
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, проблема в том, что вы копируете p в этой строке:

p = p - self.alpha * (x_normal - x_tilde)

Если эта строка будет выполнена (что имеет место в первом цикле, когда t=0), следующая строка вызовет ошибку, потому что p больше не имеет атрибута .grad.

Вместо этого вы должны использовать операторы на месте, add_, mult_, sub_, divide_ и т. д.

for t, p in enumerate(group['params']):
    if p.grad is None:
        continue
    d_p = p.grad.data

    if t % tau == 0:
        d_p.sub_(self.alpha*0.01)

    p.data.add_(d_p, alpha=-group['lr'])

Выше я удалил x_normal, x_tilde, так как вы не дали им правильных значений. Но я надеюсь, что вы поняли идею. Используйте оператор на месте только при работе с данными внутри функции step.

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