llms-from-scratch-cn/Translated_Book/ch02/2.8词位置编码.ipynb
2024-04-29 10:59:02 +08:00

184 lines
8.1 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 2.6 使用滑动窗口进行数据采样"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在前面的章节中,我们将 token IDs 转换为连续的向量表示,也就是所谓的 token 编码,作为 LLM 的输入。然而LLM 的一个缺点是自注意力机制(将在第 3 章详细介绍),不包含序列中的 token 位置或顺序信息。\n",
"\n",
"先前介绍的 embedding 层的生成方式中,相同的 token ID 总是被映射成相同的向量表示,不会在乎 token ID 在输入序列中的位置,如图 2.17 所示。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**图 2.17 编码层将 token ID 转换为相同的向量表示不管其在输入序列中的位置如何。例如token ID 5 ,无论是在 token ID 输入向量的第一还是第三个位置,都将产生相同的编码向量。**\n",
"\n",
"![](../img/fig-2-17.jpg)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在原则上具有确定性的、与位置无关的编码对于可重现性是有益的。然而由于LLM的自注意力机制本身也不关注位置因此将额外的位置信息注入到LLM中是有帮助的。\n",
"\n",
"为了实现这一点,有两种常用的位置编码方式:相对位置编码和绝对位置编码。\n",
"\n",
"绝对位置编码与序列中的特定位置相关联。对于输入序列中的每个位置,都会添加一个唯一的位置编码到 token 中,来表示其确切位置。例如,第一个 token 将具有特定的位置编码,第二个 token 将具有另一个不同的位置编码依此类推如图2.18所示。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**图2.18 位置编码被添加到 token 中,以创建 LLM 的输入。位置向量具有与原始 token 相同的维度。出于简化考虑token 编码显示为值为1。**\n",
"\n",
"![](../img/fig-2-18.jpg)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"相对位置编码不是专注于 token 的绝对位置,而是侧重于 token 之间的相对位置或距离。这意味着模型学习的是 “彼此之间有多远” 而不是 “在哪个确切位置”的关系。这样做的好处是,即使模型在训练过程中没有看到这样的长度,它也可以更好地推广到不同长度的序列。\n",
"\n",
"两种类型的位置编码旨在增强 LLM 理解 token 顺序和关系的能力,确保更准确和更具上下文意识的预测。它们的选择通常取决于具体的应用程序和正在处理的数据的性质。\n",
"\n",
"OpenAI 的 GPT 模型使用绝对位置编码,在训练过程中进行了优化,而不是像原始 Transformer 模型中的位置编码那样固定或预定义。这个优化过程是模型训练本身的一部分,我们将在本书的后面部分实现。现在,让我们创建初始的位置编码,为即将到来的章节创建 LLM 输入。\n",
"\n",
"在本章中,我们之前专注于非常小的编码大小,以说明为目的。现在,我们考虑更现实和有用的编码大小,并将输入 token 编码为 256 维向量表示。这比原始的GPT-3 模型使用的要小(在 GPT-3 中,编码大小为 12,288 维),但对于实验来说仍然是合理的。此外,我们假设 token ID 是由我们之前实现的 BPE 标记器创建的,其词汇量大小为 50,257\n",
"> output_dim = 256\n",
"\n",
"> vocab_size = 50257\n",
"\n",
"> token_embedding_layer = torch.nn.Embedding(vocab_size, output_dim)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"使用上面的 token_embedding_layer ,如果我们从 dataloader 中采样数据,我们将每个批次中的每个 token 编码成一个256维的向量中。如果我们的批次大小为8每个批次有四个 token 则结果将是一个8 x 4 x 256的张量。\n",
"\n",
"首先让我们实例化第2.6节 “使用滑动窗口进行数据采样” 的 dataloader\n",
">max_length = 4\n",
"\n",
">dataloader = create_dataloader_v1(\n",
"> raw_text, batch_size=8, max_length=max_length, stride=max_len)\n",
"\n",
">data_iter = iter(dataloader)\n",
"\n",
">inputs, targets = next(data_iter)\n",
"\n",
">print(\"Token IDs:\\n\", inputs)\n",
"\n",
">print(\"\\nInputs shape:\\n\", inputs.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"前面的代码打印如下输出:\n",
">Token IDs:\n",
"\n",
">tensor([[ 40, 367, 2885, 1464],\\\n",
" [ 1807, 3619, 402, 271],\\\n",
" [10899, 2138, 257, 7026],\\\n",
" [15632, 438, 2016, 257],\\\n",
" [ 922, 5891, 1576, 438],\\\n",
" [ 568, 340, 373, 645],\\\n",
" [ 1049, 5975, 284, 502],\\\n",
" [ 284, 3285, 326, 11]])\n",
" \n",
">Inputs shape:\n",
" >torch.Size([8, 4])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"正如我们所看到的token ID 张量是 8x4 维的,这意味着数据批次由 8 个文本样本组成,每个样本有 4 个 token。\n",
"\n",
"现在让我们使用 token_embedding_layer 将这些 token ID 嵌入为 256 维的向量:\n",
"> token_embeddings = token_embedding_layer(inputs)\n",
"> \n",
"> print(token_embeddings.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"前面的代码打印如下输出:\n",
"\n",
">torch.Size([8, 4, 256])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"根据 8x4x256 维的张量输出,我们可以看出每个 token ID 现在被嵌入为一个 256 维的向量。\n",
"对于 GPT 模型的绝对嵌入方法,我们只需要创建另一个具有与 token_embedding_layer 相同维度的嵌入层:\n",
"\n",
">context_length = max_length\n",
"\n",
">pos_embedding_layer = torch.nn.Embedding(context_lengthe, output_dim)\n",
"\n",
">pos_embeddings = pos_embedding_layer(torch.arange(context_length))\n",
"\n",
">print(pos_embeddings.shape)\n",
"\n",
"如前面的代码示例所示pos_embeddings 的输入通常是一个占位符向量 torch.arange(context_length),其中包含一系列数字 0、1、...,一直到最大输入长度减 1。context_length 是一个变量,表示 LLM 支持的输入大小。在这里,我们选择它与输入文本的最大长度类似。在实践中,输入文本可以比支持的上下文长度长。这种情况下,我们必须截断文本。\n",
"\n",
"print 语句的输出如下:\n",
"\n",
">torch.Size([4, 256])\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"正如我们所见,位置嵌入张量由四个 256 维向量组成。我们现在可以直接将它们添加到标记嵌入中PyTorch 将在每个 8 批次中的每个 4x256 维标记嵌入张量中添加 4x256 维的 pos_embeddings 张量:\n",
">input_embeddings = token_embeddings + pos_embeddings\n",
"\n",
">print(input_embeddings.shape)\n",
"\n",
"print 输出如下:\n",
"\n",
">torch.Size([8, 4, 256])\n",
"\n",
"我们创建的 input_embeddings如图 2.19 所总结的,是嵌入的输入示例,现在可以由我们将在第 3 章开始实现的主要 LLM 模块进行处理。\n",
"\n",
"![](../img/fig-2-19.jpg)\n",
"**图 2.19 作为输入处理流程的一部分,输入文本首先被分解为单个 token 。然后使用词汇表将这些标记转换为 token ID。将 token ID 转换为编码向量,然后添加相似大小的位置编码,生成用作主要 LLM 层的输入编码。**\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 2
}