sai_nash
@sai_nash

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

focallossを実装したい

損失関数 focallossを実装したい

初投稿ですので諸々ご容赦ください
当方python学び始めて半年の初学者なので、必要な情報が足りないかもしれませんが、何かあれば指摘ください。

pytorchを使いある、不平衡データの2値分類の問題を学習させています。
そこで、focallossを使いどんなものか興味本位で実装を試してみたものの実装がうまくいかず壁にぶつかってしまいました。

解決方法を教えて下さい。

発生している問題・エラー

RuntimeError                              Traceback (most recent call last)
Input In [17], in <cell line: 2>()
      1 num_epochs = 1
----> 2 history_test = fit(net, optimizer, criterion, num_epochs, 
      3         train_dataloader, device, history)

Input In [16], in fit(net, optimizer, criterion, num_epochs, train_dataloader, device, history)
     37 outputs=torch.max(outputs, 1)[1]
     38 #print(BCEloss_out)
     39 #print(type(BCEloss_out))
     40 #print(BCEloss_out.shape)
   (...)
     45 #loss = criteria(output, target, features)
     46 #loss = log_loss(outputs, labels)#損失計算 #損失関数の定義
---> 47 loss = criterion(outputs, labels)#損失計算
     48 #↑の中身   tensor(0.2962, device='cuda:0', grad_fn=<NllLossBackward0>)
     49 train_loss += loss.item()#ここは何してるんだ? テンサー中身だけを取りだしている

File ~\Pytorch_lec\lib\site-packages\torch\nn\modules\module.py:1130, in Module._call_impl(self, *input, **kwargs)
   1126 # If we don't have any hooks, we want to skip the rest of the logic in
   1127 # this function, and just call forward.
   1128 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
   1129         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1130     return forward_call(*input, **kwargs)
   1131 # Do not call functions when jit is used
   1132 full_backward_hooks, non_full_backward_hooks = [], []

Input In [13], in Focal_MultiLabel_Loss.forward(self, outputs, targets)
     48 def forward(self, outputs, targets): 
---> 49   bce = self.bcewloss(outputs, targets)
     50   bce_exp = torch.exp(-bce)
     51   focal_loss = (1-bce_exp)**self.gamma * bce

File ~\Pytorch_lec\lib\site-packages\torch\nn\modules\module.py:1130, in Module._call_impl(self, *input, **kwargs)
   1126 # If we don't have any hooks, we want to skip the rest of the logic in
   1127 # this function, and just call forward.
   1128 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
   1129         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1130     return forward_call(*input, **kwargs)
   1131 # Do not call functions when jit is used
   1132 full_backward_hooks, non_full_backward_hooks = [], []

File ~\Pytorch_lec\lib\site-packages\torch\nn\modules\loss.py:714, in BCEWithLogitsLoss.forward(self, input, target)
    713 def forward(self, input: Tensor, target: Tensor) -> Tensor:
--> 714     return F.binary_cross_entropy_with_logits(input, target,
    715                                               self.weight,
    716                                               pos_weight=self.pos_weight,
    717                                               reduction=self.reduction)

File ~\Pytorch_lec\lib\site-packages\torch\nn\functional.py:3150, in binary_cross_entropy_with_logits(input, target, weight, size_average, reduce, reduction, pos_weight)
   3147 if not (target.size() == input.size()):
   3148     raise ValueError("Target size ({}) must be the same as input size ({})".format(target.size(), input.size()))
-> 3150 return torch.binary_cross_entropy_with_logits(input, target, weight, pos_weight, reduction_enum)

RuntimeError: result type Float can't be cast to the desired output type Long

損失関数の定義コード

# 事前学習済みモデルのロード
# pretraind = True で学習済みパラメータも一緒に読み込む
net = models.resnet18(pretrained = True)

# 最終レイヤー関数の入力次元数を確認
fc_in_features = net.fc.in_features

# 最終レイヤー関数の付け替え
n_output=2#2値分類なのでoutputを変更  求めるラベルの数だけ変化させる
net.fc = nn.Linear(fc_in_features, n_output)

# GPUの利用
net = net.to(device)#GPU側にモデルを送っている

# 学習率
lr = 0.001

# 損失関数定義
class Focal_MultiLabel_Loss(nn.Module):
    def __init__(self, gamma):
      super(Focal_MultiLabel_Loss, self).__init__()
      self.gamma = gamma
      self.bcewloss = nn.BCEWithLogitsLoss()

    def forward(self, outputs, targets): 
      bce = self.bcewloss(outputs, targets)
      bce_exp = torch.exp(-bce)
      focal_loss = (1-bce_exp)**self.gamma * bce
      return focal_loss.mean()

criterion = Focal_MultiLabel_Loss(gamma=1)#損失関数の定義


# 最適化関数定義
optimizer = optim.Adam(net.parameters())

history = np.zeros((0, 3))

fitの定義コード

# 学習用関数
def fit(net, optimizer, criterion, num_epochs, train_dataloader, device, history):

    # tqdmライブラリのインポート
    from tqdm.notebook import tqdm

    base_epochs = len(history)

    for epoch in range(base_epochs, num_epochs+base_epochs):
        train_loss = 0#初期化
        train_acc = 0
        val_loss = 0
        val_acc = 0
        True_pos=0
        False_pos=0
        Turu_neg=0
        False_neg=0

        #訓練フェーズ
        net.train()
        count = 0

        for inputs, labels in tqdm(train_dataloader):
            count += len(labels)#実行回数
            inputs = inputs.to(device)#GPUに値を送信させてる
            labels = labels.to(device)#GPUに値を送信させてる

            # 勾配の初期化
            optimizer.zero_grad()#前の傾きが入ってるからそれを初期化している

            # 予測計算
            outputs = net(inputs)#推論開始
            outputs=torch.max(outputs, 1)[1]

            # 損失計算
            loss = criterion(outputs, labels)#損失計算
            train_loss += loss.item()#ここは何してるんだ? テンサー中身だけを取りだしている
            
            # 勾配計算
            loss.backward()#傾き計算

            # パラメータ修正
            optimizer.step()#学習率の分だけ傾むける

            # 予測値算出
            predicted = torch.max(outputs, 1)[1]
            train_acc += (predicted == labels).sum().item()
            predicted_list=predicted.tolist()
            label_list=labels.tolist()
            for i in range(len(label_list)):
                if predicted_list[i]==1 and label_list[i]==1:
                    True_pos+=1
                elif predicted_list[i]==1 and label_list[i]==0:
                    False_pos+=1
                elif predicted_list[i]==0 and label_list[i]==0:
                    Turu_neg+=1
                elif predicted_list[i]==0 and label_list[i]==1:
                    False_neg+=1
                else:
                    pass
            if True_pos==0 or False_pos==0 or Turu_neg==0 or False_neg==0:
                pass
            else:
                precision=True_pos/(True_pos+False_pos)
                recall=True_pos/(True_pos+False_neg)
            # 損失と精度の計算
            avg_train_loss = train_loss / count
            avg_train_acc = train_acc / count
            

       
        print (f'Epoch [{(epoch+1)}/{num_epochs+base_epochs}], loss: {avg_train_loss:.5f} acc: {avg_train_acc:.5f}')
        print("True_pos=",True_pos,"False_pos=",False_pos,"Turu_neg=",Turu_neg,"False_neg=", False_neg)
        #print("precision=",precision,"recall=",recall)
        #item = np.array([epoch+1, avg_train_loss, avg_train_acc, avg_val_loss, avg_val_acc])
        item = np.array([epoch+1, avg_train_loss, avg_train_acc])
        history = np.vstack((history, item))#
    return history
num_epochs = 1
history_test = fit(net, optimizer, criterion, num_epochs, 
        train_dataloader, device, history)
0

2Answer

エラー

RuntimeError: result type Float can't be cast to the desired output type Long

検索して出てきた記事

からするに

# 損失関数定義
class Focal_MultiLabel_Loss(nn.Module):
    def __init__(self, gamma):
      super(Focal_MultiLabel_Loss, self).__init__()
      self.gamma = gamma
      self.bcewloss = nn.BCEWithLogitsLoss()

    def forward(self, outputs, targets): 
-     bce = self.bcewloss(outputs, targets)
+     bce = self.bcewloss(outputs, targets.float())
      bce_exp = torch.exp(-bce)
      focal_loss = (1-bce_exp)**self.gamma * bce
      return focal_loss.mean()

でどうでしょうか.

0Like

Comments

  1. @sai_nash

    Questioner

    解答ありがとうございます。
    指摘通り以下のように変更してみましたが、同様のエラーが発生します。

    ---------------------------------------------------------------------------
    RuntimeError Traceback (most recent call last)
    Input In [24], in <cell line: 2>()
    1 num_epochs = 2
    ----> 2 history_test = fit(net, optimizer, criterion, num_epochs,
    3 train_dataloader, device, history)

    Input In [23], in fit(net, optimizer, criterion, num_epochs, train_dataloader, device, history)
    37 outputs=torch.max(outputs, 1)[1]
    38 #print(BCEloss_out)
    39 #print(type(BCEloss_out))
    40 #print(BCEloss_out.shape)
    (...)
    45 #loss = criteria(output, target, features)
    46 #loss = log_loss(outputs, labels)#損失計算 #損失関数の定義
    ---> 47 loss = criterion(outputs, labels)#損失計算
    48 #↑の中身   tensor(0.2962, device='cuda:0', grad_fn=<NllLossBackward0>)
    49 train_loss += loss.item()#ここは何してるんだ? テンサー中身だけを取りだしている

    File ~\Pytorch_lec\lib\site-packages\torch\nn\modules\module.py:1130, in Module._call_impl(self, *input, **kwargs)
    1126 # If we don't have any hooks, we want to skip the rest of the logic in
    1127 # this function, and just call forward.
    1128 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
    1129 or _global_forward_hooks or _global_forward_pre_hooks):
    -> 1130 return forward_call(*input, **kwargs)
    1131 # Do not call functions when jit is used
    1132 full_backward_hooks, non_full_backward_hooks = [], []

    Input In [22], in Focal_MultiLabel_Loss.forward(self, outputs, targets)
    49 def forward(self, outputs, targets):
    50 #bce = self.bcewloss(outputs, targets)
    ---> 51 bce = self.bcewloss(outputs, targets.float())
    52 bce_exp = torch.exp(-bce)
    53 focal_loss = (1-bce_exp)**self.gamma * bce

    File ~\Pytorch_lec\lib\site-packages\torch\nn\modules\module.py:1130, in Module._call_impl(self, *input, **kwargs)
    1126 # If we don't have any hooks, we want to skip the rest of the logic in
    1127 # this function, and just call forward.
    1128 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
    1129 or _global_forward_hooks or _global_forward_pre_hooks):
    -> 1130 return forward_call(*input, **kwargs)
    1131 # Do not call functions when jit is used
    1132 full_backward_hooks, non_full_backward_hooks = [], []

    File ~\Pytorch_lec\lib\site-packages\torch\nn\modules\loss.py:714, in BCEWithLogitsLoss.forward(self, input, target)
    713 def forward(self, input: Tensor, target: Tensor) -> Tensor:
    --> 714 return F.binary_cross_entropy_with_logits(input, target,
    715 self.weight,
    716 pos_weight=self.pos_weight,
    717 reduction=self.reduction)

    File ~\Pytorch_lec\lib\site-packages\torch\nn\functional.py:3150, in binary_cross_entropy_with_logits(input, target, weight, size_average, reduce, reduction, pos_weight)
    3147 if not (target.size() == input.size()):
    3148 raise ValueError("Target size ({}) must be the same as input size ({})".format(target.size(), input.size()))
    -> 3150 return torch.binary_cross_entropy_with_logits(input, target, weight, pos_weight, reduction_enum)

    RuntimeError: result type Float can't be cast to the desired output type Long
  2. @sai_nash

    Questioner

    コメントありがとうございます。

    提示していただいた記事を参考に実装してみるとエラーなく実行することができました。
    以下のコードを参考にしました。

    エラーなく改善することはできたのですが、なぜBCELossを使用するとエラーが発生するのか原因がわからないままなのでもやもやしている状態です、、、



    class FocalLoss(nn.Module):

    def __init__(self, weight=torch.tensor([1.0, 10.0]).cuda(),
    gamma=2.5, reduction='mean'):
    nn.Module.__init__(self)
    self.weight=weight
    self.gamma = gamma
    self.reduction = reduction

    def forward(self, input_tensor, target_tensor):
    log_prob = F.log_softmax(input_tensor, dim=-1)
    prob = torch.exp(log_prob)
    return F.nll_loss(
    ((1 - prob) ** self.gamma) * log_prob,
    target_tensor,
    weight=self.weight,
    reduction = self.reduction
    )

self.bceloss = nn.BCEWithLogitsLoss()
これは活性化関数に通す前のニューラルネットワークの出力を使うもののようですが、実際の出力(target)はそのようになっていますか?
もし、活性化関数を通して出力しているのであれば、
self.bceloss = nn.BCELoss()
の方が適しているのかもしれません。

0Like

Comments

  1. @sai_nash

    Questioner

    コメントありがとうございます。

    すいません読解能力がなく
    ニューラルネットワークの最後の出力はどのように指定していますか?という解釈で問題ないでしょうか?

    モデルは事前学習済みモデルを使っており、最後の部分をファインチューニングで最終レイヤー関数の付け替えを行っております。
    以下のコードを参考ください

    net = models.resnet18(pretrained = True)

    # 最終レイヤー関数の入力次元数を確認
    fc_in_features = net.fc.in_features

    # 最終レイヤー関数の付け替え
    n_output=2#2値分類なのでoutputを変更  求めるラベルの数だけ変化させる
    net.fc = nn.Linear(fc_in_features, n_output)


    そもそも前提が間違っていたら指摘ください。
    よろしくお願いします。


  2. まず、解釈はそれであっています。

    コードだと
    nn.Linearで出力を得ているようですね。
    これは全結合層なので確率マップが出力されているわけではないようです。
    であれば、Logitsであると考えて良さそうなのでBCEWithLogitsLossのままで問題ないように思えます...

    ほかの回答者の方で解決されたようで良かったです。

    解決できたコードを見るとsoftmaxという活性化関数を用いてニューラルネットワークの出力から確率マップを得ているようです。
    これはあくまで感覚ですが、活性化関数に通して、出力を確率マップに直した方が今回のような型に関するエラーは少なくなるのかな~と思います。

Your answer might help someone💌