HuggingFace-transformers系列的介绍以及在下游任务中的使用

2024-02-02 11:32

本文主要是介绍HuggingFace-transformers系列的介绍以及在下游任务中的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这篇博客主要面向对Bert系列在Pytorch上应用感兴趣的同学,将涵盖的主要内容是:Bert系列有关的论文,Huggingface的实现,以及如何在不同下游任务中使用预训练模型。

看过这篇博客,你将了解:

  • Transformers实现的介绍,不同的Tokenizer和Model如何使用。
  • 如何利用HuggingFace的实现自定义你的模型,如果你想利用这个库实现自己的下游任务,而不想过多关注其实现细节的话,那么这篇文章将会成为很好的参考。

所需的知识

安装Huggface库(需要预先安装pytorch)

在阅读这篇文章之前,如果你能将以下资料读一遍,或者看一遍的话,在后续的阅读过程中将极大地减少你陷入疑惑的概率。

  • 视频类内容:根据排序观看更佳
    • 李宏毅关于Elmo, Bert, GPT的讲解
    • Goebels关于transformerXL的讲解
    • Kilcher关于XLnet的讲解
    • McCormick关于ALBERT的讲解

或者,你更愿意去看论文的话:

  • 相关论文:根据排序阅读更佳
    • arXiv:1810.04805, BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding, Authors: Jacob Devlin, Ming-Wei Chang, Kenton Lee, Kristina Toutanova
    • arXiv:1901.02860, Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context, Authors: Zihang Dai, Zhilin Yang, Yiming Yang, William W. Cohen, Jaime Carbonell, Quoc V. Le and Ruslan Salakhutdinov.
    • XLNet论文
    • ALBERT论文

HuggingFace模型加载+下游任务使用

项目组件

一个完整的transformer模型主要包含三部分:

  1. Config,控制模型的名称、最终输出的样式、隐藏层宽度和深度、激活函数的类别等。将Config类导出时文件格式为 json格式,就像下面这样:

    {"attention_probs_dropout_prob": 0.1,"hidden_act": "gelu","hidden_dropout_prob": 0.1,"hidden_size": 768,"initializer_range": 0.02,"intermediate_size": 3072,"max_position_embeddings": 512,"num_attention_heads": 12,"num_hidden_layers": 12,"type_vocab_size": 2,"vocab_size": 30522
    }
    

    当然,也可以通过config.json来实例化Config类,这是一个互逆的过程。

  2. Tokenizer,这是一个将纯文本转换为编码的过程。注意,Tokenizer并不涉及将词转化为词向量的过程,仅仅是将纯文本分词,添加[MASK]标记、[SEP]、[CLS]标记,并转换为字典索引。Tokenizer类导出时将分为三个文件,也就是:

    • vocab.txt

      词典文件,每一行为一个词或词的一部分

    • special_tokens_map.json 特殊标记的定义方式

      {"unk_token": "[UNK]", "sep_token": "[SEP]", "pad_token": "[PAD]", "cls_token": "[CLS]", "mask_token": "[MASK]"}
      
    • tokenizer_config.json 配置文件,主要存储特殊的配置。

  3. Model,也就是各种各样的模型。除了初始的Bert、GPT等基本模型,针对下游任务,还定义了诸如BertForQuestionAnswering等下游任务模型。模型导出时将生成config.jsonpytorch_model.bin参数文件。前者就是1中的配置文件,这和我们的直觉相同,即config和model应该是紧密联系在一起的两个类。后者其实和torch.save()存储得到的文件是相同的,这是因为Model都直接或者间接继承了Pytorch的Module类。从这里可以看出,HuggingFace在实现时很好地尊重了Pytorch的原生API。

导入Bert系列基本模型的方法

通过官网自动导入

官方文档中初始教程提供的方法为:

# Load pre-trained model (weights)
# model = BertModel.from_pretrained('bert-base-uncased')

这个方法需要从官方的s3数据库下载模型配置、参数等信息(代码中已配置好位置)。这个方法虽然简单,但是在国内并不可用。当然你可以先尝试一下,不过会有很大的概率无法下载模型。

手动下载模型信息并导入

  1. 在HuggingFace官方模型库上找到需要下载的模型,点击模型链接, 这个例子使用的是bert-base-uncased模型

  2. 点击List all files in model,将其中的文件一一下载到同一目录中。例如,对于XLNet:

    # List of model files
    config.json	782.0B
    pytorch_model.bin	445.4MB
    special_tokens_map.json	202.0B
    spiece.model	779.3KB
    tokenizer_config.json	2.0B
    

    但是这种方法有时也会不可用。如果您可以将Transformers预训练模型上传到迅雷等网盘的话,请在评论区告知,我会添加在此博客中,并为您添加博客友链。

  3. 通过下载好的路径导入模型:

    import transformers
    MODEL_PATH = r"D:\transformr_files\bert-base-uncased/"
    # a.通过词典导入分词器
    tokenizer = transformers.BertTokenizer.from_pretrained(r"D:\transformr_files\bert-base-uncased\bert-base-uncased-vocab.txt") 
    # b. 导入配置文件
    model_config = transformers.BertConfig.from_pretrained(MODEL_PATH)
    # 修改配置
    model_config.output_hidden_states = True
    model_config.output_attentions = True
    # 通过配置和路径导入模型
    model = transformers.BertModel.from_pretrained(MODEL_PATH,config = model_config)
    

利用分词器分词

利用分词器进行编码

  • 对于单句:

    # encode仅返回input_ids
    tokenizer.encode("i like you")
    Out : [101, 1045, 2066, 2017, 102]
    
  • 对于多句:

    # encode_plus返回所有编码信息
    tokenizer.encode_plus("i like you", "but not him")
    Out : {'input_ids': [101, 1045, 2066, 2017, 102, 2021, 2025, 2032, 102],'token_type_ids': [0, 0, 0, 0, 0, 1, 1, 1, 1],'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
    

模型的所有分词器都是在PreTrainedTokenizer中实现的,分词的结果主要有以下内容:

{
input_ids: list[int],
token_type_ids: list[int] if return_token_type_ids is True (default)
attention_mask: list[int] if return_attention_mask is True (default)
overflowing_tokens: list[int] if a max_length is specified and 		return_overflowing_tokens is True
num_truncated_tokens: int if a max_length is specified and return_overflowing_tokens is True
special_tokens_mask: list[int] if add_special_tokens if set to True and return_special_tokens_mask is True
}

编码解释:

  • 'input_ids':顾名思义,是单词在词典中的编码
  • 'token_type_ids', 区分两个句子的编码
  • 'attention_mask', 指定对哪些词进行self-Attention操作
  • 'overflowing_tokens', 当指定最大长度时,溢出的单词
  • 'num_truncated_tokens', 溢出的token数量
  • 'return_special_tokens_mask',如果添加特殊标记,则这是[0,1]的列表,其中0指定特殊添加的标记,而1指定序列标记

将分词结果输入模型,得到编码

# 添加batch维度并转化为tensor
input_ids = torch.tensor([input_ids])
token_type_ids = torch.tensor([token_type_ids])
# 将模型转化为eval模式
model.eval()
# 将模型和数据转移到cuda, 若无cuda,可更换为cpu
device = 'cuda'
tokens_tensor = input_ids.to(device)
segments_tensors = token_type_ids.to(device)
model.to(device)# 进行编码
with torch.no_grad():# See the models docstrings for the detail of the inputsoutputs = model(tokens_tensor, token_type_ids=segments_tensors)# Transformers models always output tuples.# See the models docstrings for the detail of all the outputs# In our case, the first element is the hidden state of the last layer of the Bert modelencoded_layers = outputs
# 得到最终的编码结果encoded_layers

Bert最终输出的结果为:

sequence_output, pooled_output, (hidden_states), (attentions)

以输入序列长度为14为例

index名称维度描述
0sequence_outputtorch.Size([1, 14, 768])输出序列
1pooled_outputtorch.Size([1, 768])对输出序列进行pool操作的结果
2(hidden_states)tuple,13*torch.Size([1, 14, 768])隐藏层状态(包括Embedding层),取决于modelconfig中output_hidden_states
3(attentions)tuple,12*torch.Size([1, 12, 14, 14])注意力层,取决于参数中output_attentions

Bert总结

这一节我们以Bert为例对模型整体的流程进行了了解。之后的很多模型都基于Bert,并基于Bert进行了少量的调整。其中的输出和输出参数也有很多重复的地方。

利用预训练模型在下游任务上微调

如开头所说,这篇文章重点在于"如何进行模型的调整以及输入输出的设定", 以及"Transformer的实现进行简要的提及", 所以,我们不会去介绍、涉及如何写train循环等话题,而仅仅专注于模型。也就是说,我们将止步于跑通一个模型,而不计批量数据预处理、训练、验证等过程。

同时,这里更看重如何基于Bert等初始模型在实际任务上进行微调,所以我们不会仅仅地导入已经在下游任务上训练好的模型参数,因为在这些模型上使用的方法和上一章的几乎完全相同。

这里的输入和输入以模型的预测过程为例。

问答任务 via Bert

模型的构建

from transformers import BertTokenizer, BertForQuestionAnswering
import torchMODEL_PATH = r"D:\transformr_files\bert-base-uncased/"
# 实例化tokenizer
tokenizer = BertTokenizer.from_pretrained(r"D:\transformr_files\bert-base-uncased\bert-base-uncased-vocab.txt")
# 导入bert的model_config
model_config = transformers.BertConfig.from_pretrained(MODEL_PATH)
# 首先新建bert_model
bert_model = transformers.BertModel.from_pretrained(MODEL_PATH,config = model_config)
# 最终有两个输出,初始位置和结束位置(下面有解释)
model_config.num_labels = 2
# 同样根据bert的model_config新建BertForQuestionAnswering
model = BertForQuestionAnswering(model_config)
model.bert = bert_model

一般情况下,一个基本模型对应一个Tokenizer, 所以并不存在对应于具体下游任务的Tokenizer。这里通过bert_model初始化BertForQuestionAnswering。

任务输入:问题句,答案所在的文章 "Who was Jim Henson?", "Jim Henson was a nice puppet"

任务输出:答案 "a nice puppet"

# 设定模式
model.eval()
question, text = "Who was Jim Henson?", "Jim Henson was a nice puppet"
# 获取input_ids编码
input_ids = tokenizer.encode(question, text)
# 手动进行token_type_ids编码,可用encode_plus代替
token_type_ids = [0 if i <= input_ids.index(102) else 1 for i in range(len(input_ids))]
# 得到评分, 
start_scores, end_scores = model(torch.tensor([input_ids]), token_type_ids=torch.tensor([token_type_ids]))
# 进行逆编码,得到原始的token 
all_tokens = tokenizer.convert_ids_to_tokens(input_ids)
#['[CLS]', 'who', 'was', 'jim', 'henson', '?', '[SEP]', 'jim', 'henson', 'was', 'a', 'nice', 'puppet', '[SEP]']

模型输入:inputids, token_type_ids

模型输出:start_scores, end_scores 形状都为torch.Size([1, 14]),其中14为序列长度,代表每个位置是开始/结束位置的概率。

将模型输出转化为任务输出:

# 对输出的答案进行解码的过程
answer = ' '.join(all_tokens[torch.argmax(start_scores) : torch.argmax(end_scores)+1])
# assert answer == "a nice puppet" 
# 这里因为没有经过微调,所以效果不是很好,输出结果不佳。
print(answer)
# 'was jim henson ? [SEP] jim henson was a nice puppet [SEP]'

文本分类任务(情感分析等) via XLNet

模型的构建

from transformers import XLNetConfig, XLNetModel, XLNetTokenizer, XLNetForSequenceClassification
import torch
# 定义路径,初始化tokenizer
XLN_PATH = r"D:\transformr_files\XLNetLMHeadModel"
tokenizer = XLNetTokenizer.from_pretrained(XLN_PATH)
# 加载配置
model_config = XLNetConfig.from_pretrained(XLN_PATH)
# 设定类别数为3
model_config.num_labels = 3
# 直接从xlnet的config新建XLNetForSequenceClassification(和上一节方法等效)
cls_model = XLNetForSequenceClassification.from_pretrained(XLN_PATH, config=model_config)

任务输入:句子 "i like you, what about you"

任务输出:句子所属的类别 class1

# 设定模式
model.eval()
token_codes = tokenizer.encode_plus("i like you, what about you")

模型输入:inputids, token_type_ids

模型输出:logits, hidden states, 其中logits形状为torch.Size([1, 3]), 其中的3对应的是类别的数量。当训练时,第一项为loss。

其他的任务,将继续更新

其他的模型和之前的两个大致是相同的,你可以自己发挥。我会继续在相关的库上进行实验,如果发现用法不一样的情况,将会添加在这里。

参考

本文章主要对HuggingFace库进行了简要介绍。具体安装等过程请参见官方github仓库。

这篇关于HuggingFace-transformers系列的介绍以及在下游任务中的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/670571

相关文章

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali