Hugging Face Course-Introduction学习小记 (part1)

在这里插入图片描述

1.Transformer models

首先介绍了 - what is NLP

  • Classifying whole sentences: Getting the sentiment of a review, detecting if an email is spam, determining if a sentence is grammatically correct or whether two sentences are logically related or not

  • Classifying each word in a sentence: Identifying the grammatical components of a sentence (noun, verb, adjective), or the named entities (person, location, organization)

  • Generating text content: Completing a prompt with auto-generated text, filling in the blanks in a text with masked words

  • Extracting an answer from a text: Given a question and a context, extracting the answer to the question based on the information provided in the context

  • Generating a new sentence from an input text: Translating a text into another language, summarizing a text
    NLP isn’t limited to written text though. It also tackles complex challenges in speech recognition and computer vision, such as generating a transcript of an audio sample or a description of an image.

然后介绍了transformer模型的架构和它的衍生模型

encoder model

编码器模型仅使用 Transformer 模型的编码器。在每个阶段,注意力层都可以访问初始句子中的所有单词。这些模型通常具有“双向”注意力的特征,通常称为自动编码模型。

这些模型的预训练通常围绕着以某种方式破坏给定的句子(例如,通过屏蔽其中的随机单词)并让模型找到或重建初始句子。

编码器模型最适合需要理解完整句子的任务,例如句子分类、命名实体识别(以及更一般的单词分类)和提取式问答。

一些主要的encoder模型:

decoder model

解码器模型仅使用 Transformer 模型的解码器。在每个阶段,对于给定的单词,注意力层只能访问句子中位于它之前的单词。这些模型通常称为自回归模型。

解码器模型的预训练通常围绕预测句子中的下一个单词。

这些模型最适合涉及文本生成的任务。

一些主要的decoder模型:

seq2seq model

编码器-解码器模型(也称为序列到序列模型)使用 Transformer 架构的两个部分。在每个阶段,编码器的注意力层可以访问初始句子中的所有单词,而解码器的注意力层只能访问输入中位于给定单词之前的单词。

这些模型的预训练可以使用编码器或解码器模型的目标来完成,但通常涉及更复杂的事情。例如,T5是通过用单个掩码特殊词替换随机文本跨度(可以包含多个词)来预训练的,然后目标是预测这个掩码词替换的文本。

序列到序列模型最适合围绕根据给定输入生成新句子的任务,例如摘要、翻译或生成式问答。

一些主要的seq2seq模型:

总结:
在这里插入图片描述

2.Using Transformers

首先介绍了transformer库的一些特点:

  • 易于使用:只需两行代码即可下载、加载和使用最先进的 NLP 模型进行推理。

  • 灵活性:从本质上讲,所有模型都是简单的 PyTorchnn.ModuleTensorFlowtf.keras.Model类,并且可以像其各自机器学习 (ML) 框架中的任何其他模型一样进行处理。

  • 简单性:在整个库中几乎没有任何抽象。“一体化文件”是一个核心概念:模型的前向传递完全定义在单个文件中,因此代码本身是可理解和可破解的。

然后以一些完整的例子来介绍:

from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier(
    [
        "I've been waiting for a HuggingFace course my whole life.",
        "I hate this so much!",
    ]
)

输出:

[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]

Preprocessing with a tokenizer

正如我们在第 1 章中看到的,这个pipeline将三个步骤组合在一起:预处理、通过模型传递输入和后处理:

在这里插入图片描述
与其他神经网络一样,Transformer 模型不能直接处理原始文本,因此我们pipeline的第一步是将文本输入转换为模型可以理解的数字。为此,我们使用了一个tokenizer,它将负责:

  • 将输入拆分为称为标记的单词、子词或符号(如标点符号)
  • 将每个标记映射到一个整数
  • 添加可能对模型有用的其他输入

所有这些预处理都需要以与模型预训练时完全相同的方式完成,因此我们首先需要从Model Hub下载该信息。为此,我们使用AutoTokenizer类及其from_pretrained()方法。

from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

一旦我们有了tokenizer,我们就可以直接将我们的句子传递给它,我们将得到一个准备好提供给我们模型的字典!唯一要做的就是将输入 ID 列表转换为张量。

raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt") #pt代表pytorch
print(inputs)

输出:

{
    'input_ids': tensor([
        [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172, 2607,  2026,  2878,  2166,  1012,   102],
        [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
    ]), 
    'attention_mask': tensor([
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    ])
}

然后载入模型:

from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)

The vector output by the Transformer module is usually large. It generally has three dimensions:

  • Batch size: The number of sequences processed at a time (2 in our example).
  • Sequence length: The length of the numerical representation of the sequence (16 in our example).
  • Hidden size: The vector dimension of each model input.
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)

输出

torch.Size([2, 16, 768])

Model heads: 从数字中寻找意义

The model heads将隐藏状态的高维向量作为输入,并将它们投影到不同的维度上。它们通常由一个或几个线性层组成:
在这里插入图片描述

Transformer 模型的输出直接送到模型头进行处理。

在此图中,模型由其嵌入层和后续层表示。嵌入层将标记化输入中的每个输入 ID 转换为表示相关标记的向量。随后的层使用注意力机制操纵这些向量以产生句子的最终表示。

对于我们的示例,我们需要一个带有序列分类头的模型(能够将句子分类为正面或负面)。所以,我们实际上不会使用这个AutoModel类,而是AutoModelForSequenceClassification:

from transformers import AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
print(outputs.logits.shape)

输出维度

torch.Size([2, 2])

Postprocessing the output 将输出处理为我们需要的类型

我们从模型中获得的作为输出的值本身并不一定有意义。让我们来看看:

print(outputs.logits)

输出:

tensor([[-1.5607,  1.6123],
        [ 4.1692, -3.3464]], grad_fn=<AddmmBackward>)

我们的模型预测[-1.5607, 1.6123]了第一句话和[ 4.1692, -3.3464]第二句话。这些不是概率,而是一些实数,即模型最后一层输出的原始非标准化分数。要转换为概率,它们需要经过一个SoftMax层(所有🤗 Transformers 模型都输出 logits,因为训练的损失函数通常会将最后一个激活函数(例如 SoftMax)与实际的损失函数(例如 cross熵):

import torch

predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)

输出:

tensor([[4.0195e-02, 9.5980e-01],
        [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)

现在我们可以看到模型预测[0.0402, 0.9598]了第一个句子和[0.9995, 0.0005]第二个句子。这些是可识别的概率分数。

要获得每个位置对应的标签,我们可以检查id2label模型配置的属性(下一节将详细介绍):

>>>model.config.id2label
>>>{0: 'NEGATIVE', 1: 'POSITIVE'}

Now we can conclude that the model predicted the following:

  • First sentence: NEGATIVE: 0.0402, POSITIVE: 0.9598
  • Second sentence: NEGATIVE: 0.9995, POSITIVE: 0.0005

We have successfully reproduced the three steps of the pipeline:
preprocessing with tokenizers,
passing the inputs through the model,
and postprocessing!
Now let’s take some time to dive deeper into each of those steps.

Models

在本节中,我们将仔细研究创建和使用模型。我们将使用AutoModel该类,当您想从检查点实例化任何模型时,该类非常方便。

AutoModel类及其所有亲属的实际上是在各种各样的库中的现有模式的简单wrappers。这是一个聪明的wrappers,因为它可以自动为您的检查点猜测合适的模型架构,然后使用该架构实例化模型。

但是,如果您知道要使用的模型类型,则可以直接使用定义其架构的类。让我们来看看它是如何与 BERT 模型配合使用的。

构造一个transformer

初始化 BERT 模型需要做的第一件事是加载配置对象:

from transformers import BertConfig, BertModel

# Building the config
config = BertConfig()

# Building the model from the config
model = BertModel(config)

The configuration contains many attributes that are used to build the model:

>>>print(config)
>>>BertConfig {
  [...]
  "hidden_size": 768,
  "intermediate_size": 3072,
  "max_position_embeddings": 512,
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  [...]
}

不同的载入方法:

from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")

正如之前看到的,我们可以替换BertModel为等效的AutoModel类。我们将从现在开始这样做,因为这会产生与checkpoint无关的代码;如果您的代码适用于一个checkpoint,它应该可以与另一个无缝协作。即使架构不同,只要checkpoint是针对类似任务(例如,情感分析任务)训练的,这也适用。

保存模型

model.save_pretrained("directory_on_my_computer")

将会存在该目录下:

ls directory_on_my_computer

config.json pytorch_model.bin

Tokenizers

Tokenizers是 NLP 管道的核心组件之一。它们有一个目的:将文本转换为模型可以处理的数据。模型只能处理数字,因此Tokenizers需要将我们的文本输入转换为数字数据。在本节中,我们将确切地探讨tokenization pipeline中发生的事情。

在 NLP 任务中,通常处理的数据是原始文本。这是此类文本的示例:

Jim Henson was a puppeteer

Word-based 基于单词的分词

想到的第一种标记器是基于单词的。它通常很容易设置和使用,只需几条规则,并且通常会产生不错的结果。例如,在下图中,目标是将原始文本拆分为单词并为每个单词找到一个数字表示:
在这里插入图片描述

>>>tokenized_text = "Jim Henson was a puppeteer".split()
>>>print(tokenized_text)
['Jim', 'Henson', 'was', 'a', 'puppeteer']

每个单词都分配了一个 ID,从 0 开始一直到词汇表的大小。该模型使用这些 ID 来识别每个单词。
如果我们想用基于单词的分词器完全覆盖一种语言,我们需要为语言中的每个词都有一个标识符,这将生成大量的标记。例如,英语中有超过 500,000 个单词,因此要构建从每个单词到输入 ID 的映射,我们需要跟踪这么多 ID。此外,像“dog”这样的词与“dogs”这样的词的表示方式不同,模型最初无法知道“dog”和“dogs”是相似的:它会将这两个词识别为不相关。这同样适用于其他相似的词,例如“run”和“running”,模型最初不会认为它们是相似的。

最后,我们需要一个自定义标记来表示不在我们词汇表中的单词。这被称为“未知”标记,通常表示为“[UNK]”或“”。如果您看到分词器正在生成大量这样的标记,这通常是一个不好的迹象,因为它无法检索单词的合理表示,并且您在此过程中会丢失信息。制作词汇表的目标是使分词器将尽可能少的单词分词成未知标记。

减少未知标记数量的一种方法是使用基于字符的标记器更深一层。

Character-based 基于字符

这是一种非常简单粗暴的方法,
优点是字符集会很小,词汇外(未知)标记要少得多,因为每个单词都可以从字符构建。
缺点是每个字符本身并没有多大意义,而单词就是这种情况。然而,这又因语言而异;例如,在中文中,每个字符比拉丁语言中的字符包含更多的信息。

为了两全其美,我们可以使用结合这两种方法的第三种技术:subword tokenization 子词标记化。

Subword Tokenization 子词标记化

子词分词算法依赖于这样一个原则,即不应将常用词拆分为更小的子词,而应将稀有词分解为有意义的子词。

例如,“annoyingly”可能被认为是一个罕见的词,可以分解为“annoying”和“ly”。这两者都可能作为独立的子词出现得更频繁,同时“annoyingly”的含义由“annoying”和“ly”的复合含义保持。

这是一个示例,展示了子词标记化算法如何标记序列“让我们做标记化!:
在这里插入图片描述
这些子词最终提供了很多语义含义:例如,在上面的示例中,“tokenization”被拆分为“token”和“ization”,这两个具有语义意义同时节省空间的令牌(只需要两个tokens代表一个长词)。这使我们能够对较小的词汇表进行相对较好的覆盖,并且几乎没有未知的标记。

这种方法在土耳其语等粘着语言中特别有用,您可以通过将子词串在一起来形成(几乎)任意长的复杂词。

And more!

Unsurprisingly, there are many more techniques out there. To name a few:

  • Byte-level BPE, as used in GPT-2
  • WordPiece, as used in BERT
  • SentencePiece or Unigram, as used in several multilingual models

Loading and saving

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
>>>tokenizer("Using a Transformer network is simple")
{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}

保存模型:

tokenizer.save_pretrained("directory_on_my_computer")

encoding

将文本转换为数字称为encoding。encoding分两步完成:标记化,然后转换为输入 ID。

正如我们所见,第一步是将文本拆分为单词(或单词的一部分、标点符号等),通常称为标记。有多个规则可以管理该过程,这就是为什么我们需要使用模型名称来实例化分词器,以确保我们使用模型预训练时使用的相同规则。

第二步是将这些标记转换为数字,这样我们就可以用它们构建一个张量并将它们提供给模型。为此,分词器有一个词汇表,这是我们在使用from_pretrained()方法实例化它时下载的部分。同样,我们需要使用模型预训练时使用的相同词汇。

Tokenization

The tokenization process is done by the tokenize() method of the tokenizer:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)

print(tokens)

输出

['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']

从token到输入 ID的转换由convert_tokens_to_ids()tokenizer 方法处理:

>>>ids = tokenizer.convert_tokens_to_ids(tokens)
>>>print(ids)
[7993, 170, 11303, 1200, 2443, 1110, 3014]
Decoding

Decoding是相反的:从词汇索引中,我们想要得到一个字符串。这可以通过以下decode()方法完成:

decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)

输出:

'Using a Transformer network is simple'

注意该decode方法不仅将索引转换回标记,还将属于相同单词的标记组合在一起以生成可读的句子。当我们使用预测新文本的模型(根据提示生成的文本,或序列到序列问题(如翻译或摘要))时,这种行为将非常有用。

到现在为止,您应该了解标记器可以处理的原子操作:标记化、转换为 ID 以及将 ID 转换回字符串。然而,我们只是刮了冰山一角。在下一节中,我们将采用我们的方法来克服它的限制,并看看如何克服它们。

Handling multiple sequences 处理多个序列

在上一节中,我们探讨了最简单的用例:对单个小长度序列进行推理。然而,一些问题已经出现:

  • 我们如何处理多个序列?
  • 我们如何处理不同长度的多个序列?
  • 词汇索引是唯一能让模型正常工作的输入吗?
  • 有没有序列太长这样的事情?

Putting it all together

  • 学习了 Transformer 模型的基本构建块。
  • 了解了tokenization pipeline的组成部分。
  • 了解如何在实践中使用 Transformer 模型。
  • 学习了如何利用tokenizer将文本转换为模型可理解的张量。
  • 一起设置tokenizer和模型以从文本到预测。
  • 了解了输入 ID 的局限性,并了解了注意力掩码。
  • 使用多功能和可配置的 tokenizer 方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值