Как добавить многоклассовый слой с несколькими метками поверх предварительно обученной модели BERT?

Я пытаюсь выполнить многозадачную задачу классификации предложений с несколькими классами, используя предварительно обученную модель BERT из библиотеки huggingface transformers. Я пытался использовать модель BERTForSequenceClassification оттуда, но проблема, с которой я столкнулся, заключается в том, что я не могу расширить ее для нескольких задач. Я постараюсь сделать его более информативным на этом примере.

Предположим, у нас есть четыре разных задачи, и для каждого предложения и для каждой задачи у нас есть такие ярлыки, как показано в примерах:

  1. A :[ 'a' , 'b' , 'c' , 'd' ]
  2. B :[ 'e' , 'f' , 'g' , 'h' ]
  3. C :[ 'i' , 'j' , 'k' , 'l' ]
  4. D :[ 'm' , 'n' , 'o' , 'p' ]

Теперь, если у меня есть предложение для этой модели, я хочу, чтобы выходные данные давали мне выходные данные для всех четырех различных задач (A, B, C, D).

Это то, что я делал раньше

   model = BertForSequenceClassification.from_pretrained(
    "bert-base-uncased", # Use the 12-layer BERT model, with an uncased vocab.
    num_labels = 4, # The number of output labels--2 for binary classification.
                    # You can increase this for multi-class tasks.   
    output_attentions = False, # Whether the model returns attentions weights.
    output_hidden_states = False, # Whether the model returns all hidden-states.
)

Затем я попытался реализовать такую ​​модель CustomBERT:

class CustomBERTModel(nn.Module):
    def __init__(self):
          super(CustomBERTModel, self).__init__()
          self.bert = BertModelForSequenceClassification.from_pretrained("bert-base-uncased")
          ### New layers:
          self.linear1 = nn.Linear(768, 256)
          self.linear2 = nn.Linear(256, num_classes) ## num_classes is the number of classes in this example

    def forward(self, ids, mask):
          sequence_output, pooled_output = self.bert(
               ids, 
               attention_mask=mask)

          # sequence_output has the following shape: (batch_size, sequence_length, 768)
          linear1_output = self.linear1(sequence_output[:,0,:].view(-1,768)) 
          linear2_output = self.linear2(linear2_output)

          return linear2_output

Я просмотрел ответы на вопросы, аналогичные ранее доступным, но ни один из них не дал ответа на мой вопрос. Я попытался прояснить все моменты, которые, на мой взгляд, могут быть полезны для понимания моей проблемы, и постараюсь прояснить еще больше в случае каких-либо несоответствий, сделанных мною в объяснении вопроса. Любые ответы, связанные с этим, будут очень полезны.


comment
Вы должны использовать BertModel, а не BertModelForSequenceClassification для своего CustomBERTModel класса   -  person Ashwin Geet D'Sa    schedule 14.12.2020
comment
Хорошо, спасибо, я сделаю это. Что делать, если возникает проблема с многозадачностью?   -  person nikhil6041    schedule 14.12.2020
comment
stackoverflow.com/questions/52855843/   -  person Ashwin Geet D'Sa    schedule 17.12.2020


Ответы (1)


Вы должны использовать BertModel, а не BertModelForSequenceClassification, поскольку BertModelForSequenceClassification добавляет линейный слой для классификации поверх модели BERT и использует CrossEntropyLoss, который предназначен для мультиклассовой классификации.

Следовательно, сначала используйте BertModel вместо BertModelForSequenceClassification:

class CustomBERTModel(nn.Module):
    def __init__(self):
          super(CustomBERTModel, self).__init__()
          self.bert = BertModel.from_pretrained("bert-base-uncased")
          ### New layers:
          self.linear1 = nn.Linear(768, 256)
          self.linear2 = nn.Linear(256, 4) ## as you have 4 classes in the output
          self.sig = nn.functional.sigmoid()

    def forward(self, ids, mask):
          sequence_output, pooled_output = self.bert(
               ids, 
               attention_mask=mask)

          # sequence_output has the following shape: (batch_size, sequence_length, 768)
          linear1_output = self.linear1(sequence_output[:,0,:].view(-1,768)) 
          linear2_output = self.linear2(linear2_output)
          linear2_output = self.sig(linear2_output)

          return linear2_output

Далее, в классификации с несколькими метками используется активация «Сигмоид» вместо «Софтмакс» (здесь в приведенном выше коде добавлен сигмовидный слой).

Кроме того, для классификации по нескольким меткам необходимо использовать BCELoss вместо CrossEntropyLoss.

person Ashwin Geet D'Sa    schedule 16.12.2020
comment
Я бы порекомендовал этот пост: stackoverflow.com/questions/52855843 / - person Ashwin Geet D'Sa; 17.12.2020
comment
До этого я понимал, но почему мы здесь используем сигмовидную кишку? У нас есть 4 разных класса, и каждый класс имеет 4 разных метки. Сигмовидная кишка различает два класса. Если у нас их больше двух, мы должны использовать softmax, верно? Поправьте меня, если я ошибаюсь. - person nikhil6041; 17.12.2020
comment
В мультиклассовой классификации вы хотите выбрать 1 из n классов, следовательно, вы уменьшаете сумму выходных данных до 1 (softmax), то есть вероятности каждого класса. Но в многозначной классификации вы хотите знать вероятности каждого класса, то есть сигмоида. - person Ashwin Geet D'Sa; 17.12.2020
comment
Хорошо, спасибо большое. Теперь все понятно. - person nikhil6041; 17.12.2020