Hugging Face Transformer:从原理到实战的全面指南

一、前言

前面我面介绍过ChatGPT的基本原理以及预训练大语言模型的发展史,我们知道ChatGPT和所有预训练大语言模型的核心是什么?其实就是 Transformer,Hugging Face 的火爆离不开他们开源的这个 Transformers 库。这个开源库里有数万个我们可以直接调用的模型。很多场景下,这个开源模型已经足够我们使用了。接下来我们就从Transformer的架构和具体的案例来介绍Hugging Face Transformer。

二、Transformer 架构

Transformer 是一种用于自然语言处理和其它序列到序列任务的神经网络模型,它是在2017年由Vaswani等人提出来的 ,Transformer的核心模块是通过自注意力机制(Self Attention) 捕捉序列之间的依赖关系。

 如果只有词向量本身,没有注意力机制和位置编码,那么语言模型就不能够分配。 比如一个单词 apple 它到底是水果还是一个科技公司呢,它是不知道的,只有通过自注意力机制,捕捉到了上下文关联信息之后,这个语言模型才能够建立起整个序列的选举表征。 

自注意力机制是由三个部分组成的:

  • Query Key Value: 查询键值,自注意力机制输入进去是一个序列,每一个元素都是一个向量,也就是词向量,对于每一个元素我们都会给它计算出来三个向量 Query、Key还有Value,这三个向量通许序列本身的词向量再乘以Query、Key和Value的参数矩阵,通过矩阵乘法,我们从词向量本身衍生出来三个额外的向量,就是Query、Key和Value。

  • Attention Scores:注意力的得分,对于每个查询向量,我们都要计算它和所有其它键向量之间的相似度得分,这个得分就叫注意力得分。这个得分就是通过对查询向量和键向量进行点击Dot Product计算得到的。

  • Attention Weight:注意力的权重,得到了注意力的得分之后,它会通过Softmax进行一个归一化,然后就得到了注意力的权重。这个权重实际上就是每个值向量的加权平均值,也是自注意力机制的输出,经过这一系列的计算,这个序列中每一个词向量的彼此就开始从完全互不相通变成有了很多关系,那输出的向量就能够为 Transformer 所用,可以进行更进一步的分析处理。

  • 所有的注意力机制都是多头的,多头注意力就是将注意力机制增加了一点小小的改进,也是通过把输入向量进行线性变换,生产很多组注意力机制,这些注意力机制就可以并行计算,同时计算很多组Query、Key和Value,然后聚合成一个新的向量表示,把多头再进行聚合,这样做的好处是它可以同时关注不同位置的和不同语义层面上的信息,从而更好的捕捉序列的全局特征和局部特征,因此,多头注意力在处理复杂序列数据时表现的更好了。

除了自注意力和多头注意力机制之外,Transformer 还包括编码器(Encoder)和解码器(Decoder):Encoder:将序列映射成一组隐藏状态,经过了注意力机制的处理,Decoder再将隐藏状态映射到输出序列,这是Transformer基本的一个机制,这些隐藏状态通过多个堆叠的自注意力层还有前馈神经网络层形成比较复杂的并行结构。

上面这张图展示一个输入的文本序列在编码器Encoder和解码器Decoder内部,以及从Encoder到Decoder怎么样流动的一个过程,可以了解一下。

Encoder和Decoder在Transformer内部是由多个堆叠组成的,所以在Encoder中,每一个Transformer模块都是包含两个子层:

  • 多头自注意力层(Multi Head Self Attention)

  • 前馈神经网络层(Feed Forward Neuro Network)

在Decoder中也包括两个子层,多头注意力机制层和Encoder Decoder注意力层,这是两个不同的注意力层,一个是Self Attention,序列本身自己的注意力;一个是Encoder的输入、Encoder过来的输出和Decoder本身的输入结合的Attention,所以向量序列通过多层的 Transformer 模块进行处理,每一个模块都会对序列进行一系列的自注意力、前馈、再自注意力、前馈、然后传递到Decoder过来又进行自注意力,然后再进行Encoder Decoder Attention,再进行Feed Forward等一系列传递的过程,层层叠加,重重叠叠,这样Transformer就能够得到越来越多的输入和输出序列之间的依赖关系,逐层学习各个序列之间的高级特征,从而可以有效的捕捉到序列到序列的语义,进行语义方面的学习。

三、最具影响力的 Transformers

以下是 Transformer 模型(简短)历史中的一些关键节点:

Transformer 架构 于 2017 年 6 月推出。原本研究的重点是翻译任务。随后推出了几个有影响力的模型,包括

  • 2018 年 6 月: GPT, 第一个预训练的 Transformer 模型,用于各种 NLP 任务并获得极好的结果

  • 2018 年 10 月: BERT, 另一个大型预训练模型,该模型旨在生成更好的句子摘要

  • 2019 年 2 月: GPT-2, GPT 的改进(并且更大)版本,由于道德问题没有立即公开发布

  • 2019 年 10 月: DistilBERT, BERT 的提炼版本,速度提高 60%,内存减轻 40%,但仍保留 BERT 97% 的性能

  • 2019 年 10 月: BARTT5, 两个使用与原始 Transformer 模型相同架构的大型预训练模型(第一个这样做)

  • 2020 年 5 月, GPT-3, GPT-2 的更大版本,无需微调即可在各种任务上表现良好(称为零样本学习)

其中,最具影响力的应该要属Google 2018年提出的BERT模型,它是最流行的自然语言处理模型之一,它是通过双向的 Transformer 编码器来学习上下文相关的单词表示。BERT诞生之后,很多人就开始对BERT加以改进,看看能不能找到更好的 Transformer。

  • RoBERTa:它是Facebook提出来的,基于BERT进一步训练的语言模型,通过改变一些内部结构还有训练过程,提升了一定的模型表现力,实际上对于下游的一些任务来说,RoBERTa和BERT是各有特点,主要看你的具体任务是什么?有些任务还是BERT表现不错,而有些任务上,RoBERTa会比BERT稍微好一些。

  • ALBERT:它是一种基于BERT轻量级的语言模型,是Google和Toyota团队2019年提出的。它通过参数共享和范围这种技术缩短了模型的大小和训练时间,同时还能保持跟BERT差不多的表现能力,它会更轻量级一些。

  • DistillBERT:也是一种基于BERT轻量级的语言模型,由Hugging Face团队2019年推出的,使用了一种知识蒸馏的方法,能够保持BERT模型的效率,还能把BERT模型压缩到一半以上,只剩下一半左右的参数,但是同时保持类似的表现力,可以说DistillBERT是一个很小的大模型,效率比较高。

四、Hugging Face Transformers

Hugging Face Transformers 是一家公司,在Hugging Face提供的API中,我们几乎可以下载到所有前面提到的预训练大模型的全部信息和各种参数。我们可以认为这些模型在Hugging Face基本就是开源的了,我们只需要拿过来微调或者重新训练这些模型。用官方的话来说,Hugging Face Transformers 是一个用于自然语言处理的Python库,提供了预训练的语言模型和工具,使得研究者和工程师能够轻松的训练使用共享最先进的NLP模型,其中包括BERT、GPT、RoBERTa、XLNet、DistillBERT等等。

通过 Transformers 可以轻松的用这些预训练模型进行文本分类、命名实体识别、机器翻译、问答系统等NLP任务。这个库还提供了方便的API、示例代码和文档,让我们使用这些模型或者学习模型变得非常简单。

4.1、Transformers Pipeline

Pipeline 的基本功能

我们先来看看,Transformers 这个开源库到底能干些什么。下面的代码都是直接使用开源模型,需要利用 GPU 的算力,所以你最好还是在 Colab 里运行,注意不要忘记把 Runtime 的类型修改为 GPU。

from transformers import pipeline

classifier = pipeline(task="sentiment-analysis", device=0)
preds = classifier("I am really happy today!")
print(preds)

输出结果:

No model was supplied, defaulted to distilbert-base-uncased-finetuned-sst-2-english and revision af0f99b (https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english).
Using a pipeline without specifying a model name and revision in production is not recommended.
[{'label': 'POSITIVE', 'score': 0.9998762607574463}]

这个代码非常简单,第一行代码,我们定义了一个 task 是 sentimental-analysis 的 Pipeline,也就是一个情感分析的分类器。里面 device=0 的意思是我们指定让 Transformer 使用 GPU 资源。如果你想要让它使用 CPU,你可以设置 device=-1。然后,调用这个分类器对一段文本进行情感分析。从输出结果看,它给出了正确的 Positive 预测,也给出了具体的预测分数。因为我们在这里没有指定任何模型,所以 Transformers 自动选择了默认的模型,也就是日志里看到的 distilbert-base-uncased-finetuned-sst-2-english 这个模型。

看名字我们可以知道,这个模型是一个针对英语的模型。如果想要支持中文,我们也可以换一个模型来试试。

classifier = pipeline(model="uer/roberta-base-finetuned-jd-binary-chinese", task="sentiment-analysis", device=0)
preds = classifier("这家店有点黑,鱼香肉丝也太难吃了。")
print(preds)

输出结果:

[{'label': 'negative (stars 1, 2 and 3)', 'score': 0.934112012386322}]

这里,我们指定模型的名称,就能换用另一个模型来进行情感分析了。这次我们选用的是 roberta-base-finetuned-jd-binary-chinese 这个模型。RoBERTa 这个模型是基于 BERT 做了一些设计上的修改而得来的。而后面的 finetuned-jd-binary-chinese 是基于京东的数据进行微调过的一个模型。

Pipeline 是 Transformers 库里面的一个核心功能,它封装了所有托管在 HuggingFace 上的模型推理预测的入口。你不需要关心具体每个模型的架构、输入数据格式是什么样子的。我们只要通过 model 参数指定使用的模型,通过 task 参数来指定任务类型,运行一下就能直接获得结果。

比如,我们现在不想做情感分析了,而是想要做英译中,我们只需要把 task 换成 translation_en_to_zh,然后选用一个合适的模型就好了

translation = pipeline(task="translation_en_to_zh", model="Helsinki-NLP/opus-mt-en-zh", device=0)

text = "Artificial intelligence is really amazing. I believe you will fall in love with it."
translated_text = translation(text)
print(translated_text)

输出结果:

[{'translation_text': '人工智能真的太神奇啦,我相信你会喜欢上它'}]

在这里,我们选用了赫尔辛基大学的 opus-mt-en-zh 这个模型来做英译中,运行一下就可以看到,我们输入的英文被翻译成了中文。不过,我们怎么知道应该选用哪个模型呢?这个如魔法一般的 Helsinki-NLP/opus-mt-en-zh 模型名字从哪里可以找到呢?

五、Hugging Face实战

Hugging Face是一个AI社区,致力于分享机器学习模型和数据集。它的主要产品包括Hugging Face Dataset、Hugging Face Tokenizer、Hugging Face Transformer和Hugging Face Accelerate。

  • Hugging Face Dataset是一个库,用于轻松访问和共享音频、计算机视觉和自然语言处理(NLP)任务的数据集。只需一行代码即可加载数据集,并使用强大的数据处理方法快速准备好数据集,以便在深度学习模型中进行训练。在Apache Arrow格式的支持下,以零拷贝读取处理大型数据集,没有任何内存限制,以实现最佳速度和效率。

  • Hugging Face Tokenizer是一个用于将文本转换为数字表示形式的库。它支持多种编码器,包括BERT、GPT-2等,并提供了一些高级对齐方法,可以用于映射原始字符串(字符和单词)和标记空间之间的关系。

  • Hugging Face Transformer是一个用于自然语言处理(NLP)任务的库。它提供了各种预训练模型,包括BERT、GPT-2等,并提供了一些高级功能,例如控制生成文本的长度、温度等。

  • Hugging Face Accelerate是一个用于加速训练和推理的库。它支持各种硬件加速器,例如GPU、TPU等,并提供了一些高级功能,例如混合精度训练、梯度累积等。

5.1、Hugging Face Dataset

Hugging Face Dataset是一个公共数据集仓库,用于轻松访问和共享音频、计算机视觉和自然语言处理(NLP)任务的数据集。只需一行代码即可加载数据集,并使用强大的数据处理方法快速准备好数据集,以便在深度学习模型中进行训练。

在Apache Arrow格式的支持下,以零拷贝读取处理大型数据集,没有任何内存限制,以实现最佳速度和效率。Hugging Face Dataset还与拥抱面部中心深度集成,使您可以轻松加载数据集并与更广泛的机器学习社区共享数据集。

在花时间下载数据集之前,快速获取有关数据集的一些常规信息通常会很有帮助。数据集的信息存储在 DatasetInfo 中,可以包含数据集描述、要素和数据集大小等信息。

使用 load_dataset_builder() 函数加载数据集构建器并检查数据集的属性,而无需提交下载:

>>> from datasets import load_dataset_builder
>>> ds_builder = load_dataset_builder("rotten_tomatoes")

# Inspect dataset description
>>> ds_builder.info.description
Movie Review Dataset. This is a dataset of containing 5,331 positive and 5,331 negative processed sentences from Rotten Tomatoes movie reviews. This data was first used in Bo Pang and Lillian Lee, ``Seeing stars: Exploiting class relationships for sentiment categorization with respect to rating scales.'', Proceedings of the ACL, 2005.

# Inspect dataset features
>>> ds_builder.info.features
{'label': ClassLabel(num_classes=2, names=['neg', 'pos'], id=None),
 'text': Value(dtype='string', id=None)}

如果您对数据集感到满意,请使用 load_dataset() 加载它:

from datasets import load_dataset

dataset = load_dataset("rotten_tomatoes", split="train")

5.2、Hugging Face Tokenizer

Tokenizers 提供了当今最常用的分词器的实现,重点是性能和多功能性。这些分词器也用于Transformers。

Tokenizer 把文本序列输入到模型之前的预处理,相当于数据预处理的环节,因为模型是不可能直接读文字信息的,还是需要经过分词处理,把文本变成一个个token,每个模型比如BERT、GPT需要的Tokenizer都不一样,它们都有自己的字典,因为每一个模型它的训练语料库是不一样的,所以它的token和它的字典大小、token的格式都会各有不同,整体来讲,就是给各种各样的词进行分词,然后编码,以123456来代表词的状态,这个就是Tokenizer的作用。

所以,Tokenizer的任务就是把输入的文本转换成一个一个的标记,它还可以负责对文本序列的清洗、截断、填充进行处理。简而言之,就是为了满足具体模型所要求的格式。

主要特点:

  • 使用当今最常用的分词器训练新的词汇表并进行标记化。

  • 由于Rust实现,因此非常快速(训练和标记化),在服务器CPU上对1GB文本进行标记化不到20秒。

  • 易于使用,但也非常多功能。

  • 旨在用于研究和生产。

  • 完全对齐跟踪。即使进行破坏性规范化,也始终可以获得与任何令牌对应的原始句子部分。

  • 执行所有预处理:截断、填充、添加模型所需的特殊令牌。

这里演示如何使用 BPE 模型实例化一个:classTokenizer

from tokenizers import Tokenizer
from tokenizers.models import BPE
tokenizer = Tokenizer(BPE(unk_token="[UNK]"))

5.3、Hugging Face Transformer

Transformers提供API和工具,可轻松下载和训练最先进的预训练模型。使用预训练模型可以降低计算成本、碳足迹,并节省训练模型所需的时间和资源。这些模型支持不同模态中的常见任务,例如:

  • 自然语言处理:文本分类、命名实体识别、问答、语言建模、摘要、翻译、多项选择和文本生成。

  • 计算机视觉:图像分类、目标检测和分割。

  • 音频:自动语音识别和音频分类。

  • 多模式:表格问答、光学字符识别、从扫描文档中提取信息、视频分类和视觉问答。

Transformers支持PyTorch、TensorFlow和JAX之间的框架互操作性。这提供了在模型的每个阶段使用不同框架的灵活性;在一个框架中用三行代码训练一个模型,在另一个框架中加载它进行推理。模型还可以导出到ONNX和TorchScript等格式,以在生产环境中部署。

# 导入必要的库
from transformers import AutoModelForSequenceClassification

# 初始化分词器和模型
model_name = "bert-base-cased"
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

# 将文本编码为模型期望的张量格式
inputs = tokenizer(dataset["train"]["text"][:10], padding=True, truncation=True, return_tensors="pt")

# 将编码后的张量输入模型进行预测
outputs = model(**inputs)

# 获取预测结果和标签
predictions = outputs.logits.argmax(dim=-1)

5.4、Hugging Face Accelerate

Accelerate 是一个库,只需添加四行代码,即可在任何分布式配置中运行相同的 PyTorch 代码!简而言之,大规模的训练和推理变得简单、高效和适应性强。

from accelerate import Accelerator

accelerator = Accelerator()

model, optimizer, training_dataloader, scheduler = accelerator.prepare(
    model, optimizer, training_dataloader, scheduler
)

5.5、基于Hugging Face Transformer实现的文本分类示例

安装Hugging Face必要的库

pip install torch
pip install transformers
pip install datasets

# 导入必要的库
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from datasets import load_dataset

# 定义数据集名称和任务类型
dataset_name = "imdb"
task = "sentiment-analysis"

# 下载数据集并打乱数据
dataset = load_dataset(dataset_name)
dataset = dataset.shuffle()

# 初始化分词器和模型
model_name = "bert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

# 将文本编码为模型期望的张量格式
inputs = tokenizer(dataset["train"]["text"][:10], padding=True, truncation=True, return_tensors="pt")

# 将编码后的张量输入模型进行预测
outputs = model(**inputs)

# 获取预测结果和标签
predictions = outputs.logits.argmax(dim=-1)
labels = dataset["train"]["label"][:10]

# 打印预测结果和标签
for i, (prediction, label) in enumerate(zip(predictions, labels)):
    prediction_label = "正面评论" if prediction == 1 else "负面评论"
    true_label = "正面评论" if label == 1 else "负面评论"
    print(f"Example {i+1}: Prediction: {prediction_label}, True label: {true_label}")

输出结果:

100%|██████████| 3/3 [00:00<00:00, 65.66it/s]
Downloading model.safetensors: 100%|██████████| 436M/436M [00:19<00:00, 22.0MB/s]
Some weights of the model checkpoint at bert-base-cased were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.dense.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.seq_relationship.weight', 'cls.seq_relationship.bias', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.dense.weight']
- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Example 1: Prediction: 正面评论, True label: 正面评论
Example 2: Prediction: 正面评论, True label: 负面评论
Example 3: Prediction: 正面评论, True label: 正面评论
Example 4: Prediction: 正面评论, True label: 负面评论
Example 5: Prediction: 正面评论, True label: 负面评论
Example 6: Prediction: 正面评论, True label: 正面评论
Example 7: Prediction: 正面评论, True label: 正面评论
Example 8: Prediction: 负面评论, True label: 正面评论
Example 9: Prediction: 正面评论, True label: 负面评论
Example 10: Prediction: 正面评论, True label: 负面评论

从上面的结果来看,效果其实不太好,因为我们没有做任务相关数据的训练,直接使用bert模型进行文本情感分析,自然效果不太理想的,从运行的日志也能看到,提示我们应该在下游任务上训练这个模型,以便能够用于预测和推理。

六、Reference

猜你喜欢

转载自blog.csdn.net/FrenzyTechAI/article/details/131958687