探索 LLM 和 BERT 在语言任务中的应用

介绍
在快速发展的人工智能领域,尤其是在 NLP 领域,大型语言模型 (LLM) 迅速改变了与技术的交互。自 2017 年开创性的“注意力就是你所需要的一切”论文以来,Transformer 架构(尤其是以 ChatGPT 为例)已成为关键。GPT-3 就是一个典型的例子,它擅长生成连贯的文本。本文探讨了如何通过预训练、微调和提示将 LLM 与 BERT 结合使用来完成任务,从而揭示其卓越性能的关键。

先决条件:了解转换器、BERT 和大型语言模型。

目录
什么是LLM?
训练大型语言模型的方法
微调技术
微调 BERT
常见问题解答

什么是LLM?
LLM 代表 大型语言模型。LLM 是深度学习模型,旨在理解类人文本的含义并执行各种任务,例如情感分析、语言建模(下一个单词预测)、文本生成、文本摘要等等。他们接受大量文本数据的训练。
我们每天都在使用基于这些 LLM 的应用程序,甚至没有意识到这一点。Google 将 BERT(Bidirectional Encoder Representations for Transformers)用于各种应用,例如查询完成、了解查询上下文、输出更相关和准确的搜索结果、语言翻译等。

来源:LinkedIn
深度学习技术,特别是深度神经网络和自我注意力等高级方法,支撑着这些模型的构建。他们通过对大量文本数据进行训练来学习语言的模式、结构和语义。鉴于它们依赖于庞大的数据集,从头开始训练它们会消耗大量时间和资源,使其变得不切实际。
通过一些技术,我们可以直接将这些模型用于特定任务。因此,让我们详细讨论它们!

训练大型语言模型的方法
虽然我们可以通过传统的微调来训练这些模型来执行特定任务,但现在还有其他简单的方法,但在此之前,让我们讨论一下 LLM 的预训练。

预训练
在预训练中,大量未标记的文本用作大型语言模型的训练数据。 问题是,“我们如何在未标记的数据上训练模型,然后期望模型准确预测数据?这就是“自我监督学习”的概念。在自监督学习中,模型会屏蔽一个单词,并尝试借助前面的单词来预测下一个单词。
例如,假设我们有一句话:“我是一名数据科学家”。
该模型可以从这句话中创建自己的标记数据,例如:

发短信标签
我是一个
我是数据
我是数据科学家

这是下一个单词的预测,模型是自回归的。这可以通过MLM(掩码语言模型)来完成。BERT是一种被掩蔽的语言模型,它使用这种技术来预测被掩蔽的单词。我们可以将传销视为“填空”概念,其中模型预测空白中可以容纳哪些单词。
有不同的方法可以预测下一个单词,但在本文中,我们只讨论 BERT,即 MLM。BERT可以同时查看前面和后面的单词,以理解句子的上下文并预测被屏蔽的单词。
因此,作为预训练的高级概述,它是一种模型学习预测文本中下一个单词的技术。

微调
微调是调整模型的参数,使其适合执行特定任务。 预训练后,模型会进行微调,您可以在其中针对特定任务进行训练,例如情绪分析、文本生成和查找文档相似性等。我们不必在大文本上再次训练模型。相反,使用经过训练的模型来执行我们想要执行的任务。我们将在本文后面详细讨论如何微调大型语言模型。

促使
提示是所有 3 种技术中最简单的,但有点棘手。它涉及为模型提供上下文 (Prompt),模型基于该上下文执行任务。
可以把它想象成详细地教孩子书中的一章,对解释非常谨慎,然后要求他们解决与该章相关的问题。
在 LLM 的上下文中,以 ChatGPT 为例。我们设置一个上下文,并要求模型按照说明解决给定的问题。
假设我想让 ChatGPT 让我只采访关于变形金刚的问题。
为了获得更好的体验和准确的输出,您需要设置适当的上下文并提供详细的任务描述。
例:
拥有 2 年经验的数据科学家,正在准备在 XYZ 公司进行工作面试。我喜欢解决问题,目前正在使用最先进的 NLP 模型。我了解最新的趋势和技术。关于变压器模型的一个非常棘手的问题,这家公司的面试官可以根据公司以前的经验提出。问我 10 个问题,并给出问题的答案。
提示越详细、越具体,结果越好。最有趣的部分是,您可以从模型本身生成提示,然后添加个人风格或所需的信息。

微调技术
有多种方法可以按常规方式微调模型,不同的方法取决于要解决的特定问题。 让我们讨论微调模型的技术。
传统上有 3 种方法可以微调 LLM。

  • 特征提取:这种技术用于从给定文本中提取特征,但为什么要从给定文本中提取嵌入呢?答案很简单。由于计算机不理解文本,因此必须有一些文本表示形式可用于执行不同的任务。提取嵌入后,它们可以分析情绪、查找文档相似性等。在特征提取中,模型的主干层被冻结,即这些层的参数不更新,只更新分类器层的参数。分类器层涉及完全连接的层网络。
  • 完整模型微调:顾名思义,该技术在自定义数据集上训练每个模型层多个时期。模型中所有层的参数都会根据新的自定义数据集进行调整。这可以提高模型在数据和我们想要执行的特定任务上的准确性。考虑到
    LLM 中有数十亿个参数,它的计算成本很高,并且需要花费大量时间来训练模型。
  • 基于适配器的微调:基于适配器的微调是一个相对较新的概念,其中将额外的随机初始化层或模块添加到网络中,然后针对特定任务进行训练。在这种技术中,模型的参数不受干扰,或者模型的参数不会改变或调整。相反,适配器层参数是经过训练的。这种技术有助于以计算高效的方式调整模型。

微调 BERT
现在我们知道了微调技术,让我们使用 BERT 对 IMDB 电影评论进行情感分析。BERT 是一种大型语言模型,它结合了转换器层,并且仅使用编码器。谷歌开发了它,并被证明在各种任务上表现非常好。BERT 有不同的尺寸和变体,如 BERT-base-uncased、BERT Large、RoBERTa、LegalBERT 等等。
让我们使用 BERT 模型对 IMDB 电影评论进行情感分析。为了获得免费的 GPU 可用性,建议使用 Google Colab。让我们通过加载一些重要的库来开始训练。 由于 BERT(编码器的双向编码器表示)基于 Transformers,因此第一步是在我们的环境中安装变压器。

!pip install transformers

让我们加载一些库,这些库将帮助我们按照 BERT 模型的要求加载数据、标记加载的数据、加载我们将用于分类的模型、执行训练-测试-拆分、加载我们的 CSV 文件以及更多函数。

import pandas as pd

import numpy as np

import os

from sklearn.model_selection import train_test_split

import torch

import torch.nn as nn

from transformers import BertTokenizer, BertModel

我们必须将设备从 CPU 更改为 GPU 以加快计算速度。

device = torch.device("cuda")

下一步是加载我们的数据集并查看数据集中的前 5 条记录。

df = pd.read_csv('/content/drive/MyDrive/movie.csv')

df.head()

训练集和验证集
我们将数据集拆分为训练集和验证集。您还可以将数据拆分为训练集、验证集和测试集,但为了简单起见,我只是将数据集拆分为训练集和验证集。

x_train, x_val, y_train, y_val = train_test_split(df.text, df.label, random_state = 42, test_size = 0.2, stratify = df.label)

让我们导入并加载 BERT 模型和分词器。

from transformers.models.bert.modeling_bert import BertForSequenceClassification

# import BERT-base pre-trained model

BERT = BertModel.from_pretrained('bert-base-uncased')

# Load the BERT tokenizer

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

我们将使用分词器将文本转换为最大长度为 250 的标记,并在需要时进行填充和截断。

train_tokens = tokenizer.batch_encode_plus(x_train.tolist(), max_length = 250, pad_to_max_length=True, truncation=True)

val_tokens = tokenizer.batch_encode_plus(x_val.tolist(), max_length = 250, pad_to_max_length=True, truncation=True)

分词器返回一个字典,其中包含三个键值对,其中包含input_ids,即与特定单词相关的标记;token_type_ids,这是一个整数列表,用于区分输入的不同段或部分;和 attention_mask,指示要处理的令牌。
将这些值转换为张量

train_ids = torch.tensor(train_tokens['input_ids'])

train_masks = torch.tensor(train_tokens['attention_mask'])

train_label = torch.tensor(y_train.tolist())

val_ids = torch.tensor(val_tokens['input_ids'])

val_masks = torch.tensor(val_tokens['attention_mask'])

val_label = torch.tensor(y_val.tolist())

加载 TensorDataset 和 DataLoader 以进一步预处理数据并使其适合模型。

from torch.utils.data import TensorDataset, DataLoader

train_data = TensorDataset(train_ids, train_masks, train_label)

val_data = TensorDataset(val_ids, val_masks, val_label)

train_loader = DataLoader(train_data, batch_size = 32, shuffle = True)

val_loader = DataLoader(val_data, batch_size = 32, shuffle = True)

我们的任务是使用分类器冻结 BERT 的参数,然后在自定义数据集上微调这些层。因此,让我们冻结模型的参数。

for param in BERT.parameters():

 param.requires_grad = False

现在,我们必须为已添加的层定义前向和后向通道。BERT模型将充当特征提取器,而我们必须明确定义前向和后向传递以进行分类。

class Model(nn.Module):

   def __init__(self, bert):

       super(Model, self).__init__()

       self.bert = bert

       self.dropout = nn.Dropout(0.1)

       self.relu = nn.ReLU()

       self.fc1 = nn.Linear(768, 512)

       self.fc2 = nn.Linear(512, 2)

       self.softmax = nn.LogSoftmax(dim=1)

   def forward(self, sent_id, mask):

       # Pass the inputs to the model

       outputs = self.bert(sent_id, mask)

       cls_hs = outputs.last_hidden_state[:, 0, :]

       x = self.fc1(cls_hs)

       x = self.relu(x)

       x = self.dropout(x)

       x = self.fc2(x)

       x = self.softmax(x)

       return x

让我们将模型移动到 GPU。

model = Model(BERT)

# push the model to GPU

model = model.to(device)

Defining the optimizer

# optimizer from hugging face transformers

from transformers import AdamW

# define the optimizer

optimizer = AdamW(model.parameters(),lr = 1e-5)

我们已经对数据集进行了预处理并定义了我们的模型。现在是训练模型的时候了。我们必须编写代码来训练和评估模型。
火车功能:

def train():

   model.train()

   total_loss, total_accuracy = 0, 0

   total_preds = []

   for step, batch in enumerate(train_loader):

       # Move batch to GPU if available

       batch = [item.to(device) for item in batch]

       sent_id, mask, labels = batch

       # Clear previously calculated gradients

       optimizer.zero_grad()

       # Get model predictions for the current batch

       preds = model(sent_id, mask)

       # Calculate the loss between predictions and labels

       loss_function = nn.CrossEntropyLoss()

       loss = loss_function(preds, labels)

       # Add to the total loss

       total_loss += loss.item()

       # Backward pass and gradient update

       loss.backward()

       optimizer.step()

       # Move predictions to CPU and convert to numpy array

       preds = preds.detach().cpu().numpy()

       # Append the model predictions

       total_preds.append(preds)

   # Compute the average loss

   avg_loss = total_loss / len(train_loader)

   # Concatenate the predictions

   total_preds = np.concatenate(total_preds, axis=0)

   # Return the average loss and predictions

   return avg_loss, total_preds

The evaluation function:

def evaluate():

   model.eval()

   total_loss, total_accuracy = 0, 0

   total_preds = []

   for step, batch in enumerate(val_loader):

       # Move batch to GPU if available

       batch = [item.to(device) for item in batch]

       sent_id, mask, labels = batch

       # Clear previously calculated gradients

       optimizer.zero_grad()

       # Get model predictions for the current batch

       preds = model(sent_id, mask)

       # Calculate the loss between predictions and labels

       loss_function = nn.CrossEntropyLoss()

       loss = loss_function(preds, labels)

       # Add to the total loss

       total_loss += loss.item()

       # Backward pass and gradient update

       loss.backward()

       optimizer.step()

       # Move predictions to CPU and convert to numpy array

       preds = preds.detach().cpu().numpy()

       # Append the model predictions

       total_preds.append(preds)

   # Compute the average loss

   avg_loss = total_loss / len(val_loader)

   # Concatenate the predictions

   total_preds = np.concatenate(total_preds, axis=0)

   # Return the average loss and predictions

   return avg_loss, total_preds

训练模型
现在,我们将使用这些函数来训练模型:

# set initial loss to infinite

best_valid_loss = float('inf')

#defining epochs

epochs = 5

# empty lists to store training and validation loss of each epoch

train_losses=[]

valid_losses=[]

#for each epoch

for epoch in range(epochs):

   print('\n Epoch {:} / {:}'.format(epoch + 1, epochs))

   #train model

   train_loss, _ = train()

   #evaluate model

   valid_loss, _ = evaluate()

   #save the best model

   if valid_loss < best_valid_loss:

       best_valid_loss = valid_loss

       torch.save(model.state_dict(), 'saved_weights.pt')

   # append training and validation loss

   train_losses.append(train_loss)

   valid_losses.append(valid_loss)

   print(f'\nTraining Loss: {train_loss:.3f}')

   print(f'Validation Loss: {valid_loss:.3f}')

你有它。您可以使用经过训练的模型来推断您选择的任何数据或文本。

结论
本文探讨了 LLM 和 B 的世界ERT 及其对自然语言处理 (NLP) 的重大影响。我们讨论了预训练过程,其中 LLM 使用自监督学习在大量未标记文本上进行训练。我们还深入研究了微调,这涉及针对特定任务和提示调整预训练模型,其中为模型提供上下文以生成相关输出。此外,我们还研究了不同的微调技术,例如特征提取、完整模型微调和基于适配器的微调。LLM 彻底改变了 NLP,并继续推动各种应用程序的进步。

关键要点
LLM,如 BERT,是在大量文本数据上训练的强大模型,使它们能够理解和生成类似人类的文本。
预训练涉及使用自监督学习技术(如掩码语言建模 (MLM))在未标记的文本上训练 LLM。
微调是通过提取特征、训练整个模型或使用基于适配器的技术(具体取决于要求)来调整预训练的 LLM 以执行特定任务。

常见问题解答
问题1.LLM 和 BERT 如何理解没有明确标签的文本的含义?
答:LLM采用自我监督学习技术,如掩码语言建模,它们根据周围单词的上下文预测下一个单词,有效地从未标记的文本中创建标记数据。
问题2.微调 LLM 的目的是什么?
答:微调允许 LLM 通过调整其参数来适应特定任务,使其适用于情感分析、文本生成或文档相似性任务。它建立在模型的预训练知识之上。
问题3.LLM中提示的意义是什么?
答:提示涉及向 LLM 提供上下文或指令以生成相关输出。用户可以通过设置特定提示来引导模型根据给定的上下文回答问题、生成文本或执行特定任务。
文章来源:https://www.analyticsvidhya.com/blog/2024/01/exploring-the-use-of-llms-and-bert-for-language-tasks/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值