NLP之什么是Hugging Face Transformers?

HuggingFaceTransformers是一个自然语言处理(Natural Language Processing,NLP)的开源库,旨在让用户能够轻松地使用最新的机器学习模型来进行文本分类、问题回答、文本生成、人机对话等任务。该库集成了众多优秀的预训练模型(如BERT、GPT-2等)和用于快速训练和评估这些模型的工具。本文将为大家介绍HuggingFaceTransformers的使用流程和两个基础示例。

安装HuggingFaceTransformers

在使用HuggingFaceTransformers之前,需先安装相关库:

pip install transformers

加载预训练模型

在HuggingFaceTransformers中,我们通过from_pretrained()方法来加载预训练模型。在方法中,我们需要指定使用的模型名称和文件路径。以BERT模型为例,我们来给大家演示如何加载:

from transformers import BertModel, BertTokenizer

# 加载预训练的Bert模型和分词器
model_name = 'bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertModel.from_pretrained(model_name)

示例1:中文情感分析

下面我们通过一个中文情感分析的例子来展示HuggingFaceTransformers的使用过程。

数据准备和处理

我们从网上下载了一个中文情感分析的数据集,包含了1500条文本和对应的情感标签(分别为0表示消极、1表示中性、2表示积极)。代码如下:

import pandas as pd

df = pd.read_csv('data/sentiment.csv')
sentences = df['sentence'].values
labels = df['label'].values

接着,我们需要使用BertTokenizer对原始文本进行分词,得到模型需要的输入格式。代码如下:

input_ids = []
attention_masks = []

# 对每个句子通过BertTokenizer进行分词,并获取特殊token
for sentence in sentences:
    encoded_dict = tokenizer.encode_plus(
                        sentence,                      # sentence to encode.
                        add_special_tokens = True,     # 增加特殊token
                        max_length = 64,               # 截断或padding每个sequence的长度
                        truncation=True,               # 是否截断
                        pad_to_max_length = True,      # 是否padding
                        return_attention_mask = True,  # 增加attention mask
                        return_tensors = 'pt',         # 返回pytorch tensors格式
                   )
    # 将encoded representation添加到列表中
    input_ids.append(encoded_dict['input_ids'])
    attention_masks.append(encoded_dict['attention_mask'])

最后,我们将输入数据划分为训练集和测试集,并定义一些超参数。代码如下:

from torch.utils.data import TensorDataset, random_split
from torch.utils.data import DataLoader, RandomSampler, SequentialSampler

# 导入pytorch库
import torch

# 定义一些超参数
batch_size = 32
test_size = 0.3
random_seed = 2021

# 将数据转化为TensorDataset格式
dataset = TensorDataset(torch.cat(input_ids, dim=0), 
                        torch.cat(attention_masks, dim=0), 
                        torch.tensor(labels))

# 划分数据集
train_size = int((1-test_size)*len(dataset))
test_size = len(dataset) - train_size
train_dataset, test_dataset = random_split(dataset, [train_size, test_size])

# 定义sampler和dataloader
train_sampler = RandomSampler(train_dataset)
train_dataloader = DataLoader(train_dataset, 
                              sampler=train_sampler, batch_size=batch_size)

test_sampler = SequentialSampler(test_dataset)
test_dataloader = DataLoader(test_dataset, 
                             sampler=test_sampler, batch_size=batch_size)

模型微调和评估

我们使用了一个分类任务的版本的预训练Bert模型,将其微调用于中文情感分析任务。在训练过程中,我们使用了Adam优化器、交叉熵损失函数、accuracy指标,并定义了一个训练函数。代码如下:

from transformers import AdamW
from tqdm.notebook import tqdm
import numpy as np

# 将模型放到GPU上
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model.to(device)

# 定义优化器和损失函数、评估指标
optimizer = AdamW(model.parameters(), lr=2e-5, eps=1e-8)
loss_func = torch.nn.CrossEntropyLoss()
metric_func = lambda y_pred, y_true: np.sum(np.argmax(y_pred, axis=1) == y_true)
num_epochs = 4

def train(model, optimizer, train_loader, loss_func, metric_func):
    model.train()
    train_loss = 0.0
    train_metric = 0.0
    for batch_data in tqdm(train_loader):
        # 获取数据
        batch_input_ids, batch_attention_mask, batch_label = batch_data
        batch_input_ids.to(device)
        batch_attention_mask.to(device)
        batch_label.to(device)
        # 清空历史梯度
        optimizer.zero_grad()
        # 前向计算
        model_output = model(input_ids=batch_input_ids, 
                             attention_mask=batch_attention_mask)
        logits = model_output.last_hidden_state[:, 0, :]
        # 计算损失值
        loss = loss_func(logits, batch_label)
        # 反向传播
        loss.backward()
        # 更新参数
        optimizer.step()
        # 计算评估指标
        train_loss += loss.item() * batch_label.size(0)
        train_metric += metric_func(logits.cpu().detach().numpy(), 
                                    batch_label.cpu().detach().numpy())
    # 计算平均损失值和评估指标
    train_loss = train_loss / len(train_loader.dataset)
    train_metric = train_metric / len(train_loader.dataset)
    return train_loss, train_metric

# 微调模型
for epoch in range(num_epochs):
    train_loss, train_metric = train(model, optimizer, train_dataloader, loss_func, metric_func)
    print('Epoch {}/{}:'.format(epoch+1, num_epochs))
    print('\tTrain - Loss: {:.4f}, Accuracy: {:.4%}'.format(train_loss, train_metric))

经过4个epoch迭代,训练集上的Loss为0.3941,Accuracy为89.05%。

最后,我们使用测试集来对模型进行评估。代码如下:

def evaluate(model, test_loader, loss_func, metric_func):
    model.eval()
    loss = 0.0
    metric = 0.0
    for batch_data in tqdm(test_loader):
        # 获取数据
        batch_input_ids, batch_attention_mask, batch_label = batch_data
        batch_input_ids.to(device)
        batch_attention_mask.to(device)
        batch_label.to(device)
        # 前向计算
        model_output = model(input_ids=batch_input_ids, 
                             attention_mask=batch_attention_mask)
        logits = model_output.last_hidden_state[:, 0, :]
        # 计算损失值
        loss += loss_func(logits, batch_label).item() * batch_label.size(0)
        # 计算评估指标
        metric += metric_func(logits.cpu().detach().numpy(), 
                              batch_label.cpu().detach().numpy())
    # 计算平均损失值和评估指标
    loss = loss / len(test_loader.dataset)
    metric = metric / len(test_loader.dataset)
    return loss, metric

test_loss, test_metric = evaluate(model, test_dataloader, loss_func=loss_func, metric_func=metric_func)
print('Test - Loss: {:.4f}, Accuracy: {:.4%}'.format(test_loss, test_metric))

在测试集上,模型的Loss为0.4759,Accuracy为81.33%。

示例2:文本生成任务

下面我们将演示如何使用HuggingFaceTransformers来实现文本生成任务。我们以GPT-2模型为例。

加载和准备模型和输入

首先,我们需要下载并加载模型以及分词器:

from transformers import GPT2Tokenizer, GPT2LMHeadModel

tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
model = GPT2LMHeadModel.from_pretrained('gpt2')

接下来,我们为模型准备输入数据。我们可以用分词器将原始文本分成tokens,然后将tokens编码成整数格式:

input_ids = tokenizer.encode('I love writing for Hugging Face')

# 将input充当模型的输入数据,包裹在tensor中
input = torch.tensor(input_ids).unsqueeze(0)

预测和生成文本

接下来我们可以对模型做出预测,并生成一些文本。我们可以调用模型的generate()函数来生成文本。代码如下:

import random
import torch

# 将模型放到GPU上
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)

# 设置生成文本的序列长度
length = 50

# 切换到生成模式
model.eval()

# 使用贪婪算法生成文本
generated_ids = model.generate(input_ids=input, 
                               max_length=length, 
                               repetition_penalty=1.5, 
                               fluency_penalty=1.5, 
                               temperature=0.7, 
                               no_repeat_ngram_size=2)

# 解码生成的ids,输出文本
print(tokenizer.decode(generated_ids[0], skip_special_tokens=True))

在上述代码中,我们为GPT-2模型设置了50个tokens的序列长度,并设置了几个文本生成的超参数:temperature: 控制生成文本的多样性;repetition_penalty: 控制生成文本的重复程度,值越大,重复的可能性越低;fluency_penalty: 控制生成文本的连贯性和流畅度,值越大,结果越流畅。最后,我们使用了贪婪算法(generate()函数)生成文本。

除了贪婪生成,HuggingFaceTransformers还提供了Beam Search、Top-K Sampling、Top-P Sampling等多种生成文本的策略,具体使用请参考官方文档。

以上是HuggingFaceTransformers的使用介绍和两个基础示例。务必注意选择合适的模型和超参数,才能取得好的结果。