【问题标题】:How to use pretrained BERT word embedding vector to finetune (initialize) other networks?如何使用预训练的 BERT 词嵌入向量微调(初始化)其他网络?
【发布时间】:2021-04-24 10:03:16
【问题描述】:

当我过去使用 textcnn 进行分类工作时,我曾使用预训练的词嵌入(如 Word2Vec 和 fasttext)对 textcnn 进行微调。我使用这个过程:

  1. 在 textcnn 中创建嵌入层
  2. 加载Word2Vec或本次使用的词的嵌入矩阵 快速文本
  3. 由于在训练过程中嵌入层的向量值会发生变化,所以网络是 正在微调。

最近我也想试试 BERT 来做这件事。我想,“因为使用 BERT 预训练嵌入来初始化其他网络的嵌入层和微调应该没有什么区别,所以应该很容易!”但其实昨天我试了一天还是不行。
我发现的事实是,由于BERT的embedding是contextual embedding,尤其是在提取词embeddings的时候,每个句子中每个词的向量都会变化,所以似乎没有办法使用那个embedding来初始化embedding layer像往常一样的另一个网络...

最后,我想出了一种“微调”的方法,步骤如下:

  1. 首先,不要在 textcnn 中定义嵌入层。
  2. 在网络训练部分,我没有使用嵌入层,而是 首先将序列标记传递给预训练的 BERT 模型并得到 每个句子的词嵌入。
  3. 将 2. 中的 BERT 词嵌入放入 textcnn 并训练 textcnn 网络。

通过使用这种方法我终于可以训练了,但认真思考,我认为我根本没有在做微调......
因为如您所见,每次我开始一个新的训练循环时,BERT 生成的词嵌入总是相同的向量,所以只是将这些不变的向量输入到 textcnn 不会让 textcnn 被微调,对吧?

更新: 我想出了一种新方法来使用 BERT 嵌入和“训练”BERT 和 textcnn。
我的部分代码是:

    BERTmodel = AutoModel.from_pretrained('bert- 
                base-uncased',output_hidden_states=True).to(device)
    TextCNNmodel = TextCNN(EMBD_DIM, CLASS_NUM, KERNEL_NUM, 
                   KERNEL_SIZES).to(device)
    optimizer = torch.optim.Adam(TextCNNmodel.parameters(), lr=LR)
    loss_func = nn.CrossEntropyLoss()
  for epoch in range(EPOCH):
    TextCNNmodel.train()
    BERTmodel.train()
    for step, (token_batch, seg_batch, y_batch) in enumerate(train_loader):
        token_batch = token_batch.to(device)
        y_batch = y_batch.to(device)

        BERToutputs = BERTmodel(token_batch)
        # I want to use the second-to-last hidden layer as the embedding, so
        x_batch = BERToutputs[2][-2]

        output = TextCNNmodel(x_batch)
        output = output.squeeze()
        loss = loss_func(output, y_batch)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

我认为通过启用 BERTmodel.train() 并在获取嵌入时删除 torch.no_grad() ,损失梯度也可能落后于 BERTmodel。 TextCNNmodel 的训练过程也很顺利。
为了以后使用这个模型,我保存了TextCNNmodel和BERTmodel的参数。
然后为了测试 BERTmodel 是否真的被训练和改变了,在另一个程序中我加载了 BERTModel,并输入了一个句子来测试 BERTModel 是否真的被训练了。
但是,我发现原始“bert-base-uncased”模型的输出(嵌入)和我的“BERTmodel”是相同的,令人失望...
我真的不知道为什么 BERTmodel 部分没有改变......

【问题讨论】:

  • 你能显示一些代码吗?我的猜测是您只将下游模型的参数传递给优化器和 BERT 的参数。
  • 感谢您的评论! @Jindřich 我上传了一些我的代码和想法。请参考!
  • 哦,我的代码好像发现了一个缺陷……可能我没有为BERTmodel做优化器,所以BERT没有改变?对吧?..

标签: machine-learning nlp pytorch conv-neural-network bert-language-model


【解决方案1】:

在这里我要感谢@Jindřich,谢谢你给我重要的提示!
我想我在使用更新后的版本代码时差不多到了,但我忘了为 BERTmodel 设置优化器。
在我设置优化器并再次进行训练过程后,这次加载我的 BERTmodel 时,我发现原始“bert-base-uncased”模型的输出(嵌入)和我的“BERTmodel”最终不同,这意味着此 BERT 模型已更改,应进行微调。
这是我的最终代码,希望它也能对你有所帮助。

    BERTmodel = AutoModel.from_pretrained('bert- 
                base-uncased',output_hidden_states=True).to(device)
    TextCNNmodel = TextCNN(EMBD_DIM, CLASS_NUM, KERNEL_NUM, 
                   KERNEL_SIZES).to(device)
    optimizer = torch.optim.Adam(TextCNNmodel.parameters(), lr=LR)
    optimizer_bert = torch.optim.Adamw(BERTmodel.parameters(), lr=2e-5, weight_decay=1e-2)
    loss_func = nn.CrossEntropyLoss()
  for epoch in range(EPOCH):
    TextCNNmodel.train()
    BERTmodel.train()
    for step, (token_batch, seg_batch, y_batch) in enumerate(train_loader):
        token_batch = token_batch.to(device)
        y_batch = y_batch.to(device)

        BERToutputs = BERTmodel(token_batch)
        # I want to use the second-to-last hidden layer as the embedding, so
        x_batch = BERToutputs[2][-2]

        output = TextCNNmodel(x_batch)
        output = output.squeeze()
        loss = loss_func(output, y_batch)

        optimizer.zero_grad()
        optimizer_bert.zero_grad()
        loss.backward()
        optimizer.step()
        optimizer_bert.step()

我将继续我的实验,看看我的 BERT 模型是否真的被微调了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    • 2016-05-01
    • 2019-11-14
    • 2021-01-16
    • 2021-01-18
    • 2021-06-08
    相关资源
    最近更新 更多