Как использовать обратное распространение с самоопределяемой потерей в pytorch?

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

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)

person ce1    schedule 28.08.2018    source источник


Ответы (2)


У вас есть несколько строк, в которых вы генерируете новые тензоры из конструктора или приведения к другому типу данных. Когда вы делаете это, вы отключаете цепочку операций, через которые вы хотите, чтобы команда 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)

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

rankLoss = Variable(rankLossPrev,requires_grad=True)

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

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

person protagonist    schedule 29.08.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

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

person weiyixie    schedule 29.08.2018