Как использовать обратное распространение с самоопределенной потерей в pytorch? - PullRequest
0 голосов
/ 28 августа 2018

Я пытаюсь реализовать сиамскую сеть с потерей ранжирования между двумя изображениями. Если я определю свою собственную потерю, смогу ли я выполнить шаг обратного распространения следующим образом? Когда я запускаю его, иногда мне кажется, что он дает те же результаты, что и одна сеть.

with torch.set_grad_enabled(phase == 'train'):
    outputs1 = model(inputs1)
    outputs2 = model(inputs2)
    preds1 = outputs1;
    preds2 = outputs2;

    alpha = 0.02;
    w_r = torch.tensor(1).cuda(async=True);

    y_i, y_j, predy_i, predy_j = labels1,labels2,outputs1,outputs2;
    batchRankLoss =  torch.tensor([max(0,alpha - delta(y_i[i], y_j[i])*predy_i[i] - predy_j[i])) for i in range(batchSize)],dtype = torch.float)
    rankLossPrev = torch.mean(batchRankLoss)                                             
    rankLoss = Variable(rankLossPrev,requires_grad=True)

    loss1 = criterion(outputs1, labels1)
    loss2 = criterion(outputs2, labels2)


    #total loss = loss1 + loss2 + w_r*rankLoss
    totalLoss = torch.add(loss1,loss2)
    w_r = w_r.type(torch.LongTensor)
    rankLossPrev = rankLossPrev.type(torch.LongTensor)
    mult = torch.mul(w_r.type(torch.LongTensor),rankLossPrev).type(torch.FloatTensor)
    totalLoss = torch.add(totalLoss,mult.cuda(async = True));

     # backward + optimize only if in training phase
         if phase == 'train':
            totalLoss.backward()
            optimizer.step()

            running_loss += totalLoss.item() * inputs1.size(0)

Ответы [ 2 ]

0 голосов
/ 29 августа 2018
   rank_loss = torch.mean([torch.max(0,alpha - delta(y_i[i], y_j[i])*predy_i[i] - predy_j[i])) for i in range(batchSize)], dim=0)
   w_r = 1.0
   loss1 = criterion(outputs1, labels1)
   loss2 = criterion(outputs2, labels2)
   total_loss = loss1 + loss2 + w_r  * rank_loss 
   if phase == 'train':
       total_loss .backward()
       optimizer.step()

Вам не нужно создавать тензор снова и снова. Если у вас есть разные веса для каждой потери, а веса являются просто константами, вы можете просто написать:

total_loss = weight_1 * loss1 + weight_2 * loss2 + weight_3  * rank_loss

В любом случае, это неизлечимая константа, не имеет смысла создавать переменную и устанавливать для require_grad значение True, потому что веса - это просто константы. Пожалуйста, обновите до pytorch 0.4.1, в котором вам не нужно все оборачивать переменной

0 голосов
/ 29 августа 2018

У вас есть несколько строк, в которых вы генерируете новые тензоры из конструктора или приведения к другому типу данных. Когда вы делаете это, вы отключаете цепочку операций, через которую вы хотите, чтобы отличалась команда backwards().

Это приведение отключает график, потому что приведение не дифференцируемо:

w_r = w_r.type(torch.LongTensor)

Построение тензора из конструктора отключит график:

batchRankLoss = torch.tensor([max(0,alpha - delta(y_i[i], y_j[i])*predy_i[i] - predy_j[i])) for i in range(batchSize)],dtype = torch.float)

Из документов, оборачивая тензор в переменную, установим для grad_fn значение None (также отключение графика):

rankLoss = Variable(rankLossPrev,requires_grad=True)

Если предположить, что ваша функция critereon является дифференцируемой, то градиенты в настоящее время текут назад только через loss1 и loss2. Другие ваши градиенты будут перемещаться только до mult, пока их не остановит вызов type(). Это согласуется с вашим наблюдением о том, что ваши пользовательские потери не изменяют выход вашей нейронной сети.

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

...