作者 | William Falcon
来源 | Medium
编辑 | 代码医生团队
BERT可能是最流行的NLP迁移学习方法。Huggingface的实现提供了许多不错的功能,并在漂亮的API之后抽象了细节。
https://arxiv.org/pdf/1810.04805.pdf
https://github.com/huggingface/transformers
PyTorch Lightning是一个轻量级的框架(实际上更像是重构您的PyTorch代码),它允许使用PyTorch的任何人(例如学生,研究人员和生产团队)轻松扩展深度学习代码,同时使其可再现。它还通过训练者标志提供了42种以上的高级研究功能。
https://github.com/williamFalcon/pytorch-lightning
Lightning不会在PyTorch上添加抽象,这意味着它可以与Huggingface等其他出色的软件包很好地配合使用!在本教程中,将使用其BERT的实现在Lightning中执行微调任务。
在本教程中,将分3个步骤进行NLP的迁移学习:
- 将从huggingface库中导入BERT 。
- 将创建一个LightningModule,它使用BERT提取的功能进行微调
- 将使用Lighting Trainer训练 BertMNLIFinetuner。
https://pytorch-lightning.readthedocs.io/en/latest/LightningModule/RequiredTrainerInterface/
现场演示
如果希望在实际代码中看到此内容,请复制此colab NoteBook!
https://colab.research.google.com/drive/1DovlWenVCuXZ-EZT66wc3GVHZqREyblV
微调(又名迁移学习)
如果是要提高NYU GLUE基准的研究人员,或是想了解产品评论以推荐新内容的数据科学家,则正在寻找一种提取一段文本表示的方法,以便解决不同的任务。
对于迁移学习,通常有两个步骤。使用数据集X预训练模型。然后使用该经过预训练的模型来将该知识带入求解数据集B。在这种情况下,BERT已在BookCorpus和English Wikipedia [1]上进行了预训练。关心的下游任务是解决GLUE任务或对产品评论进行分类。
https://arxiv.org/pdf/1810.04805.pdf
预训练的好处是,在下游任务中不需要太多数据即可获得惊人的结果。
使用PyTorch Lightning进行微调
通常,可以使用以下抽象方法对PyTorch Lightning进行微调:
import pytorch_lightning as pl
class TransferLearningTemplate(pl.LightningModule): def __init__(self): # basically a feature extractor self.pretrained_model = SomePretrainedModel(load_weights=True)
# a model that uses features to do something you care about self.finetune_model = nn.Linear(dim, num_classes)
def forward(self, x): features = self.pretrained_model(x) out = self.finetune_model(features)
def training_step(self, batch, batch_num): x, y = batch
features = self.forward(x) return some_loss(features, y)
为了进行迁移学习,在LightningModule中定义了两个核心部分。
- 预先训练的模型(即:特征提取器)
- 微调模型。
可以将预训练的模型视为特征提取器。这可以以boolean 或某些表格映射更好的方式表示对象或输入。
例如,如果有一个文档集合,则可以通过预训练的模型运行每个文档,并使用输出向量将文档彼此进行比较。
微调模型可以任意复杂。它可以是一个深层网络,也可以是一个简单的线性模型或SVM。
与BERT进行微调
在这里,将使用预训练的BERT来微调名为MNLI的任务。这实际上只是试图将文本分为三类。这是LightningModule:
from transformers import BertModelimport torch.nn.functional as F
class BertMNLIFinetuner(pl.LightningModule):
def __init__(self): super(BertMNLIFinetuner, self).__init__()
# use pretrained BERT self.bert = BertModel.from_pretrained('bert-base-cased', output_attentions=True)
# fine tuner (3 classes) self.W = nn.Linear(bert.config.hidden_size, 3) self.num_classes = 3
def forward(self, input_ids, attention_mask, token_type_ids):
h, _, attn = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
h_cls = h[:, 0] logits = self.W(h_cls) return logits, attn
def training_step(self, batch, batch_nb): # batch input_ids, attention_mask, token_type_ids, label = batch
# fwd y_hat, attn = self.forward(input_ids, attention_mask, token_type_ids)
# loss loss = F.cross_entropy(y_hat, label)
# logs tensorboard_logs = {'train_loss': loss} return {'loss': loss, 'log': tensorboard_logs}
在这种情况下,将使用来自huggingface库的预训练BERT,并添加自己的简单线性分类器,以将给定的文本输入分类为三个类之一。
但是,仍然需要定义验证循环,以计算验证准确性
def validation_step(self, batch, batch_nb): # batch input_ids, attention_mask, token_type_ids, label = batch
# fwd y_hat, attn = self.forward(input_ids, attention_mask, token_type_ids)
# loss loss = F.cross_entropy(y_hat, label)
# acc a, y_hat = torch.max(y_hat, dim=1) val_acc = accuracy_score(y_hat.cpu(), label.cpu()) val_acc = torch.tensor(val_acc)
return {'val_loss': loss, 'val_acc': val_acc}
def validation_end(self, outputs): avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean() avg_val_acc = torch.stack([x['val_acc'] for x in outputs]).mean()
tensorboard_logs = {'val_loss': avg_loss, 'avg_val_acc': avg_val_acc} return {'avg_val_loss': avg_loss, 'progress_bar': tensorboard_logs}
测试循环可以计算出测试准确性
代码语言:javascript复制 def test_step(self, batch, batch_nb): input_ids, attention_mask, token_type_ids, label = batch
y_hat, attn = self.forward(input_ids, attention_mask, token_type_ids)
a, y_hat = torch.max(y_hat, dim=1) test_acc = accuracy_score(y_hat.cpu(), label.cpu())
return {'test_acc': torch.tensor(test_acc)}
def test_end(self, outputs):
avg_test_acc = torch.stack([x['test_acc'] for x in outputs]).mean()
tensorboard_logs = {'avg_test_acc': avg_test_acc} return {'avg_test_acc': avg_test_acc, 'log': tensorboard_logs, 'progress_bar': tensorboard_logs}
最后,定义将要使用的优化器和数据集。该数据集应该是要解决的下游数据集。
代码语言:javascript复制def configure_optimizers(self): return torch.optim.Adam([p for p in self.parameters() if p.requires_grad], lr=2e-05, eps=1e-08)
@pl.data_loaderdef train_dataloader(self): return bert_mnli_train_dataloader
@pl.data_loaderdef val_dataloader(self): return bert_mnli_val_dataloader
@pl.data_loaderdef test_dataloader(self):return bert_mnli_test_dataloader
完整的LightningModule看起来像这样。
代码语言:javascript复制from transformers import BertModelimport torch.nn.functional as F
class BertMNLIFinetuner(pl.LightningModule):
def __init__(self): super(BertMNLIFinetuner, self).__init__()
# use pretrained BERT self.bert = BertModel.from_pretrained('bert-base-cased', output_attentions=True)
# fine tuner (3 classes) self.W = nn.Linear(bert.config.hidden_size, 3) self.num_classes = 3
def forward(self, input_ids, attention_mask, token_type_ids):
h, _, attn = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
h_cls = h[:, 0] logits = self.W(h_cls) return logits, attn
def training_step(self, batch, batch_nb): # batch input_ids, attention_mask, token_type_ids, label = batch
# fwd y_hat, attn = self.forward(input_ids, attention_mask, token_type_ids)
# loss loss = F.cross_entropy(y_hat, label)
# logs tensorboard_logs = {'train_loss': loss} return {'loss': loss, 'log': tensorboard_logs}
def validation_step(self, batch, batch_nb): # batch input_ids, attention_mask, token_type_ids, label = batch
# fwd y_hat, attn = self.forward(input_ids, attention_mask, token_type_ids)
# loss loss = F.cross_entropy(y_hat, label)
# acc a, y_hat = torch.max(y_hat, dim=1) val_acc = accuracy_score(y_hat.cpu(), label.cpu()) val_acc = torch.tensor(val_acc)
return {'val_loss': loss, 'val_acc': val_acc}
def validation_end(self, outputs): avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean() avg_val_acc = torch.stack([x['val_acc'] for x in outputs]).mean()
tensorboard_logs = {'val_loss': avg_loss, 'avg_val_acc': avg_val_acc} return {'avg_val_loss': avg_loss, 'progress_bar': tensorboard_logs}
def test_step(self, batch, batch_nb): input_ids, attention_mask, token_type_ids, label = batch
y_hat, attn = self.forward(input_ids, attention_mask, token_type_ids)
a, y_hat = torch.max(y_hat, dim=1) test_acc = accuracy_score(y_hat.cpu(), label.cpu())
return {'test_acc': torch.tensor(test_acc)}
def test_end(self, outputs):
avg_test_acc = torch.stack([x['test_acc'] for x in outputs]).mean()
tensorboard_logs = {'avg_test_acc': avg_test_acc} return {'avg_test_acc': avg_test_acc, 'log': tensorboard_logs, 'progress_bar': tensorboard_logs}
def configure_optimizers(self): return torch.optim.Adam([p for p in self.parameters() if p.requires_grad], lr=2e-05, eps=1e-08)
@pl.data_loader def train_dataloader(self): return bert_mnli_train_dataloader
@pl.data_loader def val_dataloader(self): return bert_mnli_val_dataloader
@pl.data_loader def test_dataloader(self): return bert_mnli_test_dataloader
总结
在这里,学习了将Huggingface BERT用作LightningModule中的特征提取器。这种方法意味着可以利用强大的文本表示来执行以下操作:
- 情绪分析
- 建议对聊天机器人的回复
- 使用NLP构建推荐引擎
- 改进Google搜索算法
https://www.blog.google/products/search/search-language-understanding-bert/
- 为文档创建嵌入以进行相似性搜索
- 可以创造性地考虑任何事情!
还看到了PyTorch Lightning在包括Huggingface在内的其他库中的表现!
https://github.com/williamFalcon/pytorch-lightning/
https://github.com/huggingface/transformers