0
点赞
收藏
分享

微信扫一扫

Hive进阶(2)----HDFS写入数据流程(赋图助君理解)

构建大型语言模型(从头开始)

2 理解大型语言模型(Working with Text Data)

在上一章中,我们深入研究了大型语言模型(LLM)的一般结构,并了解到它们是在大量文本上进行预训练的。具体来说,我们的重点是基于 Transformer 架构的纯解码器 LLM,该架构是 ChatGPT 和其他流行的类似 GPT 的 LLM 中使用的模型的基础。

在预训练阶段,LLM一次处理一个单词的文本。使用下一个单词预测任务训练具有数百万到数十亿个参数的LLM,可以产生具有令人印象深刻的功能的模型。然后可以进一步微调这些模型以遵循一般指令或执行特定的目标任务。但在我们在接下来的章节中实现和训练 LLM 之前,我们需要准备训练数据集,这是本章的重点如图 2.1 所示

图 2.1 编码LLM(大型语言模型)的三个主要阶段的心智模型,先在一个通用文本数据集上对LLM进行预训练,然后在一个标注过的数据集上进行微调。本章将解释并编写数据准备和采样管道的代码,该管道为预训练提供给LLM的文本数据。。

在本章中,您将学习如何为训练LLM准备输入文本。这涉及将文本拆分为单独的单词和子词tokens,然后将其编码为 LLM 的向量表示。您还将了解高级tokenization方案,例如字节对编码,该方案在 GPT 等流行的 LLM 中使用。最后,我们将实施采样和数据加载策略,以生成后续章节中训练LLM所需的输入输出对。

2.1 理解词嵌入(Understanding word embeddings)

深度神经网络模型(包括LLM)无法直接处理原始文本。由于文本是分类的,因此它与用于实现和训练神经网络的数学运算不兼容。因此,我们需要一种将单词表示为连续值向量的方法。

将数据转换为矢量格式的概念通常称为嵌入。使用特定的神经网络层或另一个预训练的神经网络模型,我们可以嵌入不同的数据类型,例如视频、音频和文本,如图2.2所示。

图 2.2 深度学习模型无法处理原始形式的视频、音频和文本等数据格式。因此,我们使用==嵌入模型==将这些原始数据转换为深度学习架构可以轻松理解和处理的==密集向量==表示。具体来说,该图说明了将原始数据转换为三维数值向量的过程。

图2.2所示,我们可以通过嵌入模型处理各种不同的数据格式。然而,值得注意的是,不同的数据格式需要不同的嵌入模型。例如,为文本设计的嵌入模型不适合嵌入音频或视频数据。

嵌入的核心是从离散对象(例如单词、图像甚至整个文档)到连续向量空间中的点的映射——嵌入的主要目的是将非数字数据转换为神经网络可以识别的格式。网络可以处理。

虽然词嵌入是最常见的文本嵌入形式,但也有句子、段落或整个文档的嵌入。句子或段落嵌入是检索增强生成的流行选择。检索增强生成将生成(如生成文本)与检索(如搜索外部知识库)结合起来,在生成文本时提取相关信息,这是一种超出了本书范围的技术。由于我们的目标是训练类似 GPT 的 LLM,它学习一次生成一个单词的文本,因此本章重点讨论单词嵌入。

已经开发了多种算法和框架来生成词嵌入。早期且最流行的示例之一是Word2Vec方法。 Word2Vec 训练神经网络架构,通过预测给定目标单词的单词上下文来生成单词嵌入,反之亦然。 Word2Vec 背后的主要思想是,出现在相似上下文中的单词往往具有相似的含义。因此,当出于可视化目的投影到二维词嵌入时,可以看到相似的术语聚集在一起,如图 2.3 所示。

如果词嵌入是二维的,我们可以将它们绘制在二维散点图中以实现可视化目的,如图 2.3所示。当使用单词嵌入技术(例如 Word2Vec)时,与相似概念相对应的单词通常在嵌入空间中彼此靠近出现。例如,与国家和城市相比,不同类型的鸟类在嵌入空间中显得彼此更接近。

词嵌入可以有不同的维度,从一到数千。如图2.3所示,我们可以选择二维词嵌入来实现可视化目的。更高的维度可能会捕获更细微的关系,但代价是计算效率。

虽然我们可以使用 Word2Vec 等预训练模型来生成机器学习模型的嵌入,但LLM通常会生成自己的嵌入,这些嵌入是输入层的一部分,并在训练期间更新。在 LLM 训练中优化嵌入而不是使用 Word2Vec 的优点是嵌入针对特定任务和手头的数据进行了优化。我们将在本章后面实现这样的嵌入层。此外,正如我们在第 3 章中讨论的,LLM还可以创建上下文理解的输出嵌入

不幸的是,高维嵌入对可视化提出了挑战,因为我们的感官知觉和常见的图形表示本质上仅限于三个维度或更少,这就是为什么图 2.3 在二维散点图中显示了二维嵌入。然而,在使用 LLM 时,我们通常使用比图 2.3 所示更高维度的嵌入。对于 GPT-2 和 GPT-3,嵌入大小(通常称为模型隐藏状态的维度)根据特定模型变体和大小而变化。这是性能和效率之间的权衡。最小的 GPT-2 模型(117M 和 125M 参数)使用 768 维的嵌入大小来提供具体示例。最大的 GPT-3 模型(175B 参数)使用 12,288 维的嵌入大小。

本章接下来的部分将介绍准备 LLM 使用的嵌入所需的步骤,其中包括将文本拆分为单词、将单词转换为tokens以及将tokens转换为嵌入向量。

2.2 文本Tokenizing(Tokenizing text)

本节介绍我们如何将输入文本拆分为单独的tokens,这是为 LLM 创建嵌入所需的预处理步骤。这些tokens要么是单个单词,要么是特殊字符,包括标点符号,如图 2.4 所示。

图 2.4 本节在LLM背景下涵盖的文本处理步骤的视图。在这里,我们将输入文本分割成单独的tokens,这些tokens可以是单词或特殊字符,例如标点符号。在接下来的部分中,我们将把文本转换为tokens ID 并创建tokens嵌入。

我们将为 LLM 训练tokenize的文本是 Edith Wharton 的短篇小说《The Verdict》,该小说已发布到公共领域,因此允许用于 LLM 训练任务。该文本可在 Wikisource 上找到,网址为https://en.wikisource.org/wiki/The_Verdict,您可以将其复制并粘贴到文本文件中,我将其复制到文本文件中"the-verdict.txt" 以使用 Python 的标准文件读取实用程序进行加载:

# 清单 2.1 将一个短篇故事作为文本示例读入 Python
with open("the-verdict.txt", "r", encoding="utf-8") as f:
    raw_text = f.read()
    
print("Total number of character:", len(raw_text))
print(raw_text[:99])

或者,您可以在本书的 GitHub 存储库中找到此“the-verdict.txt" 文件: https://github.com/rasbt/LLMs-from-scratch/tree/main/ch02/01_main-chapter-code。
出于说明目的,打印命令打印该文件的总字符数,后跟该文件的前 100 个字符:

我们的目标是将这个 20,479 个字符的短篇故事tokenize为单个单词和特殊字符,然后我们可以将其转化为后续章节中 LLM 训练的嵌入。

文本样本大小
请注意,在与LLM合作时,处理数百万篇文章和数十万本书(数千兆字节的文本)是很常见的。然而,出于教育目的,使用较小的文本样本(例如一本书)就足以说明文本处理步骤背后的主要思想,并使其能够在合理的时间内在消费类硬件上运行。

我们怎样才能最好地分割这个文本以获得tokens列表?为此,我们进行了一次小游览,并使用 Python 的正则表达式库re进行说明。(请注意,您不必学习或记住任何正则表达式语法,因为我们将在本章后面过渡到预构建的分词器。)

使用一些简单的示例文本,我们可以使用re.split具有以下语法的命令来根据空白字符拆分文本:

正则表达式r’(\s)':这里\s代表空白字符(例如空格、换行符等)。圆括号()表示捕获组,意味着在分割的同时,匹配到的空白字符也会作为独立的元素包含在结果列表中。


import re

text = "Hello, world. This, is a test."
result = re.split(r'(\s)', text)

print(result)

结果是单个单词、空格和标点符号的列表:
‘Hello,’
’ ’
‘world.’
’ ’

请注意,上面的简单tokenization方案主要用于将示例文本分成单独的单词,但是,某些单词仍然连接到我们希望作为单独列表条目的标点符号。我们也避免将所有文本都小写,因为大写有助于LLM区分专有名词和普通名词,理解句子结构,并学习生成具有正确大写的文本。

让我们修改按空格 ( \s) 和逗号以及句点 ( [,.])分隔的正则表达式:

在正则表达式 r’([,.]|\s)’ 中:
[,.] 指的是一个字符集,匹配任何一个逗号 , 或者句号 .。
| 是一个逻辑或操作符,表示匹配左边的 [,.] 或者右边的 \s。
\s 匹配任何空白字符,包括空格、制表符、换行符等。
圆括号 () 表示捕获组,这意味着在使用 re.split 进行分割时,匹配到的逗号、句号或空白字符也会作为分割结果的一部分返回

result = re.split(r'([,.]|\s)', text)

print(result)

我们可以看到单词和标点符号现在是单独的列表条目,正如我们想要的那样:

‘Hello’
‘,’
’ ’
‘world’

剩下的一个小问题是列表仍然包含空白字符。或者,我们可以安全地删除这些冗余字符,如下所示:

# Strip whitespace from each item and then filter out any empty strings.从每个项中去除空格,然后过滤掉任何空字符串。
result = [item for item in result if item.strip()]
print(result)

生成的无空格输出如下所示:

是否删除空格
在开发简单的tokenizer时,我们是否应该将空格编码为单独的字符,或者只是删除它们取决于我们的应用程序及其要求。删除空格可以减少内存和计算需求。但是,如果我们训练对文本的确切结构敏感的模型(例如,对缩进和间距敏感的 Python 代码),保留空格可能会很有用。在这里,为了tokenized输出的简单性和简洁性,我们删除了空格。稍后,我们将切换到包含空格的tokenization方案。

我们上面设计的tokenization方案在简单的示例文本上效果很好。让我们进一步修改它,以便它还可以处理其他类型的标点符号,例如问号、引号和我们之前在Edith Wharton短篇小说的前 100 个字符中看到的双破折号,以及其他特殊字符:

text = "Hello, world. Is this-- a test?"
result = re.split(r'([,.:;?_!"()\']|--|\s)', text)
result = [item.strip() for item in result if item.strip()]
print(result)

结果输出如下:

图 2.5 中总结的结果可以看出,我们的tokenization方案现在可以成功处理文本中的各种特殊字符。

图 2.5 到目前为止,我们实现的tokenization方案将文本拆分为单独的单词和标点符号。在此图所示的具体示例中,示例文本被分为== 10 个单独的tokens==。

现在我们已经有了一个基本的tokenizer(分词器),让我们将它应用到Edith Wharton的整个短篇小说中:

preprocessed = re.split(r'([,.?_!"()\']|--|\s)', raw_text)
preprocessed = [item.strip() for item in preprocessed if item.strip()]
print(len(preprocessed))
print(preprocessed[:30])

上面的 print 语句输出4649,它是该文本中的tokens数(不包含空格)。

让我们打印前 30 个tokens以进行快速目视检查:

结果输出显示我们的tokenizer(分词器)似乎能够很好地处理文本,因为所有单词和特殊字符都被整齐地分开

2.3 将tokens转换为tokensID(Converting tokens into token IDs)




举报

相关推荐

0 条评论