Skip to content

Commit

Permalink
Merge pull request #701 from buqieryul/main
Browse files Browse the repository at this point in the history
Fixing the translation Zh-CN/chapter2/
  • Loading branch information
xianbaoqian authored May 21, 2024
2 parents a51913e + c8572a9 commit 2cc0ab7
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 76 deletions.
11 changes: 5 additions & 6 deletions chapters/zh-CN/chapter2/1.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@

创建🤗 Transformers库就是为了解决这个问题。它的目标是提供一个API,通过它可以加载、训练和保存任何Transformer模型。这个库的主要特点是:
- **易于使用**:下载、加载和使用最先进的NLP模型进行推理只需两行代码即可完成。
- **灵活**:所有模型的核心都是简单的PyTorch **nn.Module** 或者 TensorFlow **tf.kears.Model**,可以像它们各自的机器学习(ML)框架中的任何其他模型一样进行处理
- **简单**当前位置整个库几乎没有任何摘要。“都在一个文件中”是一个核心概念:模型的正向传递完全定义在一个文件中,因此代码本身是可以理解的,并且是可以破解的。
- **灵活**:所有模型的核心都是简单的PyTorch **nn.Module** 或者 TensorFlow **tf.kears.Model**类,可以像在其各自的机器学习 (ML) 框架中的任何其他模型一样方便地进行处理
- **简单**库中几乎没有任何抽象。 “All in one file”(所有代码在一个文件中)是一个核心概念:模型的前向传播完全定义在一个文件中,因此代码本身易于理解和修改

最后一个特性使🤗 Transformers与其他ML库截然不同。这些模型不是基于通过文件共享的模块构建的;相反,每一个模型都有自己的网络结构(layers)。除了使模型更加容易接受和更容易理解,这还允许你轻松地在一个模型上实验,而且不影响其他模型。

本章将从一个端到端的示例开始,在该示例中,我们一起使用模型和tokenizer分词器来复制[Chapter 1](/course/chapter1)中引入的函数pipeline(). 接下来,我们将讨论模型API:我们将深入研究模型和配置类,并向您展示如何加载模型以及如何将数值输入处理为输出预测。

然后我们来看看标记器API,它是pipeline()函数的另一个主要组件。它是作用分词器负责第一个和最后一个处理步骤,处理从文本到神经网络数字输入的转换,以及在需要时转换回文本。最后,我们将向您展示如何处理在一个准备好的批处理中通过一个模型发送多个句子的问题,然后详细介绍pipeline()函数。
本章将从一个端到端的示例开始,在该示例中,我们一起使用模型和*tokenizer*分词器来复制[Chapter 1](/course/chapter1)中引入的函数`pipeline()`. 接下来,我们将讨论模型API:我们将深入研究模型和配置类,并向您展示如何加载模型以及如何将数值输入处理为输出预测。

接下来,我们将介绍 *tokenizer* API,它是 `pipeline()` 函数的另一个主要组件。*tokenizer* 负责第一个和最后一个处理步骤,处理从文本到神经网络数值输入的转换,以及在需要时将数值转换回文本。最后,我们将向您展示如何处理将多个句子作为一个准备好的批次发送到模型中,然后通过更深入地了解高级 `tokenizer()` 函数来总结所有内容。
<Tip>
⚠️ 为了从模型中心和🤗Transformers的所有可用功能中获益,我们建议<a href="https://huggingface.co/join">creating an account</a>.
⚠️ 为了从Model Hub和🤗Transformers的所有可用功能中获益,我们建议<a href="https://huggingface.co/join">创建帐户</a>.
</Tip>
89 changes: 46 additions & 43 deletions chapters/zh-CN/chapter2/2.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ classifier(
{'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```

正如我们在[Chapter 1](/course/chapter1)中看到的,此管道将三个步骤组合在一起:预处理、通过模型传递输入和后处理:
正如我们在[Chapter 1](/course/chapter1)中看到的,这个 pipeline 集成了三个步骤:预处理、模型推理和后处理:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/full_nlp_pipeline.svg" alt="The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head."/>
Expand All @@ -64,15 +64,19 @@ classifier(

## 使用分词器进行预处理 [[使用分词器进行预处理]]

与其他神经网络一样,Transformer模型无法直接处理原始文本, 因此我们管道的第一步是将文本输入转换为模型能够理解的数字。 为此,我们使用*tokenizer*(标记器),负责:
与其他神经网络一样,Transformer模型无法直接处理原始文本, 因此pipeline的第一步是将文本输入转换为模型能够理解的数字。 为此,我们使用*tokenizer*(分词器),负责:

- 将输入拆分为单词、子单词或符号(如标点符号),称为标记(*token*)
- 将每个标记(token)映射到一个整数
- 添加可能对模型有用的其他输入
- 添加其他可能对模型有用的输入:例如,位置编码等信息。
- - 位置编码:指示每个词元在句子中的位置。
- - 段落标记:区分不同段落的文本。
- - 特殊标记:例如 [CLS][SEP] 标记,用于标识句子的开头和结尾。

所有这些预处理都需要以与模型预训练时完全相同的方式完成,因此我们首先需要从[Model Hub](https://huggingface.co/models)中下载这些信息。为此,我们使用`AutoTokenizer`类及其`from_pretrained()`方法。使用我们模型的检查点名称,它将自动获取与模型的标记器相关联的数据,并对其进行缓存(因此只有在您第一次运行下面的代码时才会下载)。

因为`sentiment-analysis`(情绪分析)管道的默认检查点是`distilbert-base-uncased-finetuned-sst-2-english`(你可以看到它的模型卡[here](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)),我们运行以下程序:
所有预处理都需要以与模型预训练时完全相同的方式完成(保持分词方式的一致性非常重要,可以确保模型的性能和准确性),因此我们首先需要从[Model Hub](https://huggingface.co/models)中下载这些信息。为此,我们使用`AutoTokenizer`类及其`from_pretrained()`方法。使用我们模型的检查点名称,它将自动获取与模型的标记器相关联的数据,并对其进行缓存(因此只有在您第一次运行下面的代码时才会下载)。

因为`sentiment-analysis`(情绪分析)Pipeline的默认检查点是`distilbert-base-uncased-finetuned-sst-2-english`(你可以看到它的模型介绍页[here](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)),我们运行以下程序:

```python
from transformers import AutoTokenizer
Expand All @@ -81,9 +85,9 @@ checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```

一旦我们有了标记器,我们就可以直接将我们的句子传递给它,然后我们就会得到一本字典,它可以提供给我们的模型!剩下要做的唯一一件事就是将输入ID列表转换为张量
一旦我们有了tokenizer,就可以直接将句子传递给它,然后我们会得到一个字典,它包含了可以输入到模型的数据。接下来,我们只需要将输入 ID 列表转换为张量即可

您可以使用🤗 Transformers,而不必担心哪个ML框架被用作后端;它可能是PyTorch或TensorFlow,或Flax。但是,Transformers型号只接受*张量*作为输入。如果这是你第一次听说张量,你可以把它们想象成NumPy数组。NumPy数组可以是标量(0D)、向量(1D)、矩阵(2D)或具有更多维度。它实际上是张量;其他ML框架的张量行为类似,通常与NumPy数组一样易于实例化
使用 🤗 Transformers 时,您无需担心它背后的机器学习框架,它可以是 PyTorch、TensorFlow或 Flax。不过,Transformer 模型只接受*张量*作为输入。如果您不熟悉张量,可以把它看作 NumPy 数组。NumPy 数组可以是标量 (0D)、向量 (1D)、矩阵 (2D) 或更高维度的数组,本质上就是一个张量。其他机器学习框架中的张量也类似,通常与 NumPy 数组一样容易创建

要指定要返回的张量类型(PyTorch、TensorFlow或plain NumPy),我们使用`return_tensors`参数:

Expand All @@ -107,7 +111,7 @@ print(inputs)
```
{/if}

现在不要担心填充和截断;我们稍后会解释这些。这里要记住的主要事情是,您可以传递一个句子或一组句子,还可以指定要返回的张量类型(如果没有传递类型,您将得到一组列表)。
暂时不用担心填充和截断,我们稍后会解释它们。这里主要需要记住的是,您可以传入单个句子或句子列表,并指定想要返回的张量类型(如果不指定类型,则会返回嵌套列表)。

{#if fw === 'pt'}

Expand Down Expand Up @@ -145,12 +149,11 @@ print(inputs)
```
{/if}

输出本身是一个包含两个键的字典,`input_ids``attention_mask``input_ids`包含两行整数(每个句子一行),它们是每个句子中标记的唯一标记(token)。我们将在本章后面解释什么是`attention_mask`

## 浏览模型 [[浏览模型]]
输出本身是一个包含两个键的字典,`input_ids``attention_mask``input_ids` 包含两行整数(每个句子一行),它们是每个句子中各个 token 的唯一标识符。我们将在本章后面解释 `attention_mask` 的作用。
## 模型推理 [[模型推理]]

{#if fw === 'pt'}
我们可以像使用标记器一样下载预训练模型。🤗 Transformers提供了一个`AutoModel`类,该类还具有`from_pretrained()`方法:
我们可以像使用*tokenizer*一样下载预训练模型。🤗 Transformers提供了一个`AutoModel`类,该类也有`from_pretrained()`方法:

```python
from transformers import AutoModel
Expand All @@ -159,35 +162,33 @@ checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
```
{:else}
我们可以像使用标记器一样下载预训练模型。🤗 Transformers提供了一个`TFAutoModel`类,该类还具有`from_pretrained()`方法:
我们可以像使用*tokenizer*一样下载预训练模型。🤗 Transformers提供了一个`AutoModel`类,该类也有`from_pretrained()`方法:

```python
from transformers import TFAutoModel
from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModel.from_pretrained(checkpoint)
model = AutoModel.from_pretrained(checkpoint)
```
{/if}

在这个代码片段中,我们下载了之前在管道中使用的相同检查点(它实际上应该已经被缓存),并用它实例化了一个模型。
在这段代码中,我们加载了之前在 pipeline 中使用的相同检查点(实际上它应该已经被缓存)并用它实例化了一个模型。

这个架构只包含基本转换器模块:给定一些输入,它输出我们将调用的内容*隐藏状态(hidden states)*,亦称*特征(features)*。对于每个模型输入,我们将检索一个高维向量,表示**Transformer模型对该输入的上下文理解**

如果这不合理,不要担心。我们以后再解释。

虽然这些隐藏状态本身可能很有用,但它们通常是模型另一部分(称为*头部(head)*)的输入。 在[Chapter 1](/course/chapter1)中,可以使用相同的体系结构执行不同的任务,但这些任务中的每个任务都有一个与之关联的不同头。
这个架构只包含基本的 Transformer 模块:给定一些输入,它会输出我们称之为*隐藏状态 (hidden states)* 的内容,也称为*特征 (features)*。对于每个模型输入,我们将检索一个高维向量,该向量表示 **Transformer 模型对该输入的上下文理解**
如果你现在对隐藏状态和上下文理解的概念还不理解,也不用担心,我们会在后续的内容中详细解释这些概念,并举例说明它们是如何工作的。

虽然这些隐藏状态本身可能很有用,但它们通常作为下游任务的输入,例如模型*头部(head)*的输入。在[Chapter 1](/course/chapter1)中,我们介绍了可以使用相同的架构执行不同的任务,但每个任务都会有不同的头部与之关联。
### 高维向量? [[高维向量?]]

Transformers模块的矢量输出通常较大。它通常有三个维度:
Transformers模块的向量输出通常较大。它通常有三个维度:

- **Batch size**: 一次处理的序列数(在我们的示例中为2)。
- **Sequence length**: 序列的数值表示的长度(在我们的示例中为16)。
- **Hidden size**: 每个模型输入的向量维度。
- **批次大小(Batch size)**: 一次处理的序列数(在我们的示例中为2)。
- **序列长度 (Sequence length)**: 序列的数值表示的长度(在我们的示例中为16)。
- **隐藏层维度(Hidden size)**: 每个模型输入的向量维度。

由于最后一个值,它被称为“高维”。隐藏的大小可能非常大(768通常用于较小的型号,而在较大的型号中,这可能达到3072或更大)。
之所以说它是“高维度”的,是因为最后一个维度值。隐藏层维度可以非常大(对于较小的模型768 是常见的,而在较大的模型中,它可以达到 3072 或更多)。

如果我们将预处理的输入输入到模型中,我们可以看到这一点
我们可以通过将预处理后的输入数据传递给模型来验证这一点

{#if fw === 'pt'}
```python
Expand All @@ -209,12 +210,10 @@ print(outputs.last_hidden_state.shape)
```
{/if}

注意🤗 Transformers模型的输出与`namedtuple`或词典相似。您可以通过属性(就像我们所做的那样)或键(`输出["last_hidden_state"]`)访问元素,甚至可以通过索引访问元素,前提是您确切知道要查找的内容在哪里(`outputs[0]`)。

### 模型头:数字的意义 [[模型头:数字的意义]]

模型头将隐藏状态的高维向量作为输入,并将其投影到不同的维度。它们通常由一个或几个线性层组成:
🤗 Transformers 模型的输出类似于 `namedtuple` 或字典。您可以通过属性 (就像我们之前所做的那样) 或键 (例如` outputs["last_hidden_state"]`) 来访问元素,甚至可以通过索引访问元素 (例如 `outputs[0]`),前提是您知道要查找的内容的位置。
### 模型头:数值的意义 [[模型头:从数值中提取意义]]

模型头接收隐藏状态的高维向量作为输入,并将其映射到另一个维度。它们通常由一个或几个线性层构成:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/transformer_and_head.svg" alt="A Transformer network alongside its head."/>
Expand All @@ -238,8 +237,10 @@ Transformers模型的输出直接发送到模型头进行处理。
- 以及其他 🤗

{#if fw === 'pt'}
对于我们的示例,我们需要一个带有序列分类头的模型(能够将句子分类为肯定或否定)。因此,我们实际上不会使用`AutoModel`类,而是使用`AutoModelForSequenceClassification`

对于我们的示例,我们需要一个带有序列分类头部的模型(能够将句子分类为肯定或否定)。 因此,我们实际上不会使用 `AutoModel` 类,而是使用 `AutoModelForSequenceClassification`(`AutoModel` 类只包含基本的 Transformer 模块,它可以提取文本的特征,但不能进行分类。
`AutoModelForSequenceClassification` 类在 `AutoModel` 的基础上添加了一个序列分类头部,可以将文本分类为不同的类别。
):
```python
from transformers import AutoModelForSequenceClassification

Expand All @@ -259,8 +260,8 @@ outputs = model(inputs)
```
{/if}

现在,如果我们观察输出的形状,维度将低得多:模型头将我们之前看到的高维向量作为输入,并输出包含两个值的向量(每个标签一个):

现在,如果我们观察输出的shape,维度将低得多:模型头将我们之前看到的高维向量作为输入,并输出包含两个值的向量(每个标签一个):
正如您所见,输出向量的尺寸与输入向量相比要小得多。这是因为模型头将输入向量中的信息压缩成两个值,每个标签一个
```python
print(outputs.logits.shape)
```
Expand All @@ -279,7 +280,7 @@ torch.Size([2, 2])

{/if}

因为我们只有两个句子和两个标签,所以我们从模型中得到的结果是2 x 2的形状
因为我们只有两个句子和两个标签,所以我们从模型中得到的结果是2 x 2的维度

## 对输出进行后处理 [[对输出进行后处理]]

Expand All @@ -302,8 +303,7 @@ tensor([[-1.5607, 1.6123],
```
{/if}

我们的模型预测第一句为`[-1.5607, 1.6123]`,第二句为`[ 4.1692, -3.3464]`。这些不是概率,而是*logits*,即模型最后一层输出的原始非标准化分数。要转换为概率,它们需要经过[SoftMax](https://en.wikipedia.org/wiki/Softmax_function)层(所有🤗Transformers模型输出logits,因为用于训练的损耗函数通常会将最后的激活函数(如SoftMax)与实际损耗函数(如交叉熵)融合):

我们的模型预测第一句为 `[-1.5607, 1.6123]`,第二句为 `[4.1692, -3.3464]`。这些不是概率,而是 *logits*,即模型最后一层输出的原始、未经归一化的分数。为了将其转换为概率,它们需要经过[SoftMax](https://en.wikipedia.org/wiki/Softmax_function)层(所有 🤗 Transformers 模型都输出 logits,因为用于训练的损失函数通常会将最后的激活函数(例如 SoftMax)与实际的损失函数(例如交叉熵)融合在一起。
{#if fw === 'pt'}
```py
import torch
Expand Down Expand Up @@ -333,9 +333,12 @@ tf.Tensor(
```
{/if}

现在我们可以看到,模型预测第一句为`[0.0402, 0.9598]`,第二句为`[0.9995, 0.0005]`这些是可识别的概率分数
现在我们可以看到,模型预测第一句为`[0.0402, 0.9598]`,第二句为`[0.9995, 0.0005]`这些数字代表了模型预测每个类别(否定或肯定)的概率分值

为了获得每个位置对应的标签,我们可以检查模型配置的`id2label`属性(下一节将对此进行详细介绍):
为了将这些概率分值转换为可识别的标签(“否定”或“肯定”),我们需要参考模型配置中的 id2label 属性。该属性将每个模型输出的 ID 映射到相应的标签。



```python
model.config.id2label
Expand All @@ -345,13 +348,13 @@ model.config.id2label
{0: 'NEGATIVE', 1: 'POSITIVE'}
```

现在我们可以得出结论,该模型预测了以下几点:

根据 id2label 属性,我们可以得出以下结论:


- 第一句:否定:0.0402,肯定:0.9598
- 第二句:否定:0.9995,肯定:0.0005

我们已经成功地复制了管道的三个步骤:使用标记化器进行预处理、通过模型传递输入以及后处理!现在,让我们花一些时间深入了解这些步骤中的每一步。

至此,我们已经成功完成了文本分类任务的三个步骤:使用*tokenizer*对文本进行预处理;将预处理后的文本输入到模型中;对模型的输出结果进行后处理,并将预测结果转换为可识别的标签。接下来,我们将对这三个步骤进行更详细的解释。
<Tip>

✏️ **试试看!** 选择两个(或更多)你自己的文本并在管道中运行它们。然后自己复制在这里看到的步骤,并检查是否获得相同的结果!
Expand Down
Loading

0 comments on commit 2cc0ab7

Please sign in to comment.