Есть ли способ запустить функцию до того, как оптимизатор обновит веса?

#python #machine-learning #pytorch

Вопрос:

Я просматриваю учебник PyTorch и только что узнал о optimizer.step том, как и как он обновляет параметры сети (здесь).

Есть ли способ, чтобы создать функцию, которая, когда есть градиент обновления , чтобы каждый тренируемый параметр (например.г веса), примет значение веса и потери, и умножьте это значение на какой-то процент, что, мол 90% ?
Так что если обновление должно быть: w1 -= lr * loss_value = 1e-5 * 50 я хочу, чтобы пройти через функцию перед обновлением и сделать его 1e-5 * 50 * 90%

 def func(loss_value, percentage):
    return loss_value * percentage #new update should be w1 -= loss_value * percentage
 

Пример модели:

 import torch
import torch.nn as nn
import torch.optim as optim

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.fc1 = nn.Linear(1, 5)
        self.fc2 = nn.Linear(5, 10)
        self.fc3 = nn.Linear(10, 1)

    def forward(self, x):
        x = self.fc1(x)
        x = torch.relu(x)        
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Model()

opt = optim.Adam(net.parameters())
features = torch.rand((3,1)) 
opt.zero_grad()
out = net(features)
loss = torch.tensor(5) - torch.sum(out)
loss.backward()
# need to have the function change the value of the loss update before the optimizer?
opt.step()
 

Комментарии:

1. Почему бы просто не снизить скорость обучения? Или установите планировщик скорости обучения, который устанавливает ее на 90%, пока вы не сделаете один шаг?

2. Потому что я хочу, чтобы он учитывал индивидуальные веса, а не только 1 функцию (т. Е. 1 скорость обучения).

3. Значит, для разных весов у вас будут разные проценты?

4. Да, в значительной степени. Допустим, вес находится в диапазоне от 0 до 5, процент будет составлять 10%, от 5 до 20, 30% и т. Д. Вот почему мне нужна функция, а не 1 значение (например, планировщик скорости обучения/и т. Д.)

Ответ №1:

Я получил этот фрагмент кода от https://discuss.pytorch.org/t/how-to-modify-the-gradient-manually/7483/2 и слегка отредактировал его:

 loss.backward()
for p in model.parameters():
    weights = p.data
    scales = def_scales(weights)
    p.grad *= scales  # or whatever other operation
optimizer.step()
 

Это проходит через каждый параметр в модели (между loss.backward() и ДО шага оптимизатора) и настраивает сохраненный градиент ПЕРЕД применением backprop.

Пример def_scales будет выглядеть примерно так (ОЧЕНЬ некрасиво), где значения val-это сравниваемые значения параметров, а масштабы-желаемые значения масштабирования потерь:

 def def_scales(weights,scales=[0.1,0.5,1,1],vals=[0,5,10,float('inf')]):
   out = torch.zeros_like(weights)
   for V,v in enumerate(vals[::-1]): #backwards because we're doing less than
      out[weights<=v] = scales[len(scales)-V-1] #might want to compare to abs
   return out
 

Комментарии:

1. Это выглядит потрясающе! Я проверю это через час или около того. Просто чтобы убедиться, что я понимаю, p.grad *= scales изменится значение обновления веса, и когда я позвоню optimizer.step() , оно уменьшит каждый вес на соответствующее значение? т. е. w1-=p1.grad *= scales1 ? Кроме того, я не совсем понял backwards because we're doing less than . Можете ли вы объяснить эту часть?

2. «Меньше, чем» в данном контексте означает » Это банально, но он просматривает значения в vals и сравнивает их с ними, чтобы установить значения в out. Я сделал это таким образом, чтобы каждое значение было равно 1 (для..

3. И да, optimizer.step() зависит от поля градации каждого параметра. См., например, код SGD здесь .

4. Понял! Большое спасибо!

Ответ №2:

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

Комментарии:

1. Я написал loss_value , чтобы посчитать частичную производную от веса потерь. Надеялся, что это будет иметь смысл, но, может быть, и нет. Следовательно, я считаю, что мои уравнения верны. Я хочу учитывать индивидуальные веса в функции, и именно поэтому я не просто создал 1 скорость обучения

2. в вашем коде нет» loss_value», а «loss_value» может просто означать значение убытка, которое рассчитывается на основе определенных вами функций потерь. частные производные вычисляются в » loss.bakward ()». «loss.backward()» вычисляет dloss/dx для каждого параметра x, для которого requires_grad=True. Да, вы можете получить доступ к весам моделей и изменить их, но вы просто добавляете еще один дополнительный гиперпараметр для настройки, делая это, и это не служит какой-либо конкретной цели.