好吧。所以有多种方法可以微调变压器:
- 冻结变压器的参数,只有它的最终输出被输入另一个模型(用户训练这个“另一个”模型),
- 带有用户添加的自定义层的整个转换器都经过微调。顶级会议的多篇论文使用第二种方法。那些“如何微调 BERT”的博客文章也是如此,它们通常将 PyTorch 自定义层定义为 nn.Module 对象。一个常见的实现可能是这样的:]
#示例 1 开始)
from transformers import RobertaModel
import pytorch
class ClassificationHead(nn.Module):
def __init__(self):
super().__init__()
self.dense = nn.Linear(args.hidden_dim, args.hidden_dim)
classifier_dropout = (args.drop_out if args.drop_out is not None else 0.1)
self.dropout = nn.Dropout(classifier_dropout)
self.out_proj = nn.Linear(args.hidden_dim, args.num_labels)
def forward(self, features):
x = features[:, 0, :] # take <s> token (equiv. to [CLS])
x = self.dropout(x)
x = self.dense(x)
x = torch.tanh(x)
x = self.dropout(x)
x = self.out_proj(x)
return x
class SequenceClassification(nn.Module):
def __init__(self):
super().__init__()
self.num_labels = args.num_labels
self.model = RobertaModel.from_pretrained(roberta-base)
self.classifier = ClassificationHead()
def forward(self, input_ids, attention_mask):
outputs = self.model(input_ids=input_ids, attention_mask=attention_mask)
sequence_output = outputs[0] #last hidden state
logits = self.classifier(sequence_output)
return logits
#示例 1 结束)
#示例 2 开始)
class BertBinaryClassifier(nn.Module):
def __init__(self, dropout=0.1):
super(BertBinaryClassifier, self).__init__()
self.bert = BertModel.from_pretrained('bert-base-uncased')
self.linear = nn.Linear(768, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, tokens):
_, pooled_output = self.bert(tokens, utput_all=False)
linear_output = self.linear(dropout_output)
proba = self.sigmoid(linear_output)
return proba
#示例 2 结束)
所以,我想知道是否可以使用上面的模型类来训练变压器模型本身。从表面上看,好像转换器模型仅用于提供输出(特征提取),而用户仅训练新的自定义层(如上面的方法 1)。
但是很多博客文章说上面的代码对应于方法 2,其中整个转换器与新的自定义层一起进行了微调。这是怎么回事?
如果上面的代码是方法2,如何冻结BERT模型并只训练新的自定义层?有谁知道一个好的代码实现可以参考?