点亮BERT:3个步骤进行NLP迁移学习

2019-11-29 12:24:06 浏览数 (1)

作者 | 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的迁移学习:

  1. 将从huggingface库中导入BERT 。
  2. 将创建一个LightningModule,它使用BERT提取的功能进行微调
  3. 将使用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进行微调:

代码语言:javascript复制
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中定义了两个核心部分。

  1. 预先训练的模型(即:特征提取器)
  2. 微调模型。

可以将预训练的模型视为特征提取器。这可以以boolean 或某些表格映射更好的方式表示对象或输入。

例如,如果有一个文档集合,则可以通过预训练的模型运行每个文档,并使用输出向量将文档彼此进行比较。

微调模型可以任意复杂。它可以是一个深层网络,也可以是一个简单的线性模型或SVM。

与BERT进行微调

在这里,将使用预训练的BERT来微调名为MNLI的任务。这实际上只是试图将文本分为三类。这是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}

在这种情况下,将使用来自huggingface库的预训练BERT,并添加自己的简单线性分类器,以将给定的文本输入分类为三个类之一。

但是,仍然需要定义验证循环,以计算验证准确性

代码语言:javascript复制
    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

0 人点赞