动手学LLM(ch2)

news2024/9/27 15:22:45

2.1 理解词嵌入

深度神经网络模型,包括大型语言模型(LLMs),无法直接处理原始文本,因为文本是分类数据,与神经网络的数学运算不兼容。为了达到这个目的,需要将单词转换为连续值向量。记住一句话就行,为了兼容神经网络的数学运算,把所有原始数据类型转变为连续值向量就是嵌入(embedding)。

如下图所示,通过特定的神经网络层或预训练模型,可以将视频、音频和文本等不同类型的数据嵌入为密集向量表示,使深度学习架构能够理解和处理这些原始数据。

需要注意的是,不同的数据格式需要各自专用的嵌入模型,例如,针对文本设计的嵌入模型不适用于音频或视频数据。

嵌入本质上是将离散对象(如单词、图像或文档)映射到连续向量空间的过程,目的是将非数字数据转换为神经网络可处理的格式。虽然词嵌入是最常见的形式,但也可以扩展到句子、段落或整个文档。当前,词嵌入的生成有多种算法和框架,其中Word2Vec是早期和流行的选择,它通过预测上下文生成词嵌入,基于相似上下文中词的相似含义。词嵌入可以有不同维度,从一维到数千维,高维度可以捕捉更多细微关系,尽管计算效率较低。大型语言模型(LLMs)通常生成自己的嵌入,优化以适应特定任务和数据。以GPT-2和GPT-3为例,嵌入大小根据模型变种变化,最小的GPT-2和GPT-3使用768维,而最大的GPT-3使用12,288维,这反映了性能与效率之间的权衡。

如下图所示,如果词嵌入是二维的,我们可以将它们绘制在二维散点图中以便于可视化。在使用词嵌入技术,例如 Word2Vec 时,对应于相似概念的词在嵌入空间中通常彼此接近。例如,在嵌入空间中,不同类型的鸟类相对于国家和城市更为靠近。

注意的是,LLMs 还可以创建上下文化的输出嵌入。这个后面会去讨论

2.2 文本分词(序列化)

如何将输入文本分割成单独的token,这是大型语言模型(LLM)创建嵌入的必需预处理步骤。

如下图所示,文本处理步骤在大型语言模型(LLM)中的最开始的阶段。我们将输入文本分割成单独的token,这些token可能是单词或特殊字符,例如标点符号。后续,我们会将把文本转换成token ID 并创建token嵌入。

读取本地的文本文件

这里没什么需要注意的,就是很简单的读取文件的代码。

# 设置要读取的文件路径,这里是本地文件
file_path = "./the-verdict.txt"
# 打开文件并读取内容
with open(file_path, 'r', encoding='utf-8') as file:
    raw_text = file.read()  # 读取文件内容
# 输出文本的总字符数
print("Total number of characters:", len(raw_text))
# 输出文件内容的前99个字符,以便预览
print(raw_text[:99])

使用 Python 的正则表达式库 re 模块来示例说明分词

这里其实看看就行,知道分词后大概什么样子就好了,因为我们后面转用预构建的分词器。

# 设置要读取的文件路径,这里是本地文件
file_path = "./the-verdict.txt"
# 打开文件并读取内容
with open(file_path, 'r', encoding='utf-8') as file:
    raw_text = file.read()  # 读取文件内容
# # 输出文本的总字符数
# print("Total number of characters:", len(raw_text))
# # 输出文件内容的前99个字符,以便预览
# print(raw_text[:99])

import re  # 导入正则表达式模块

# 定义一个包含标点和空格的字符串
text = "Hello, world. This, is a test."

# 使用正则表达式按照空格进行分割,并保留空格作为分割符
result = re.split(r'(\s)', text)
print(result)  # 输出分割结果

# 使用正则表达式按照标点(逗号、句号)和空格进行分割,并保留分割符
result = re.split(r'([,.]|\s)', text)
print(result)  # 输出分割结果

# 去除结果中的空字符串
result = [item.strip() for item in result if item.strip()]
print(result)  # 输出处理后的结果

# 另一个包含不同标点和空格的字符串
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)  # 输出处理后的结果

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

2.3 将token转换为tokenID

如下图所示,我们将文本token转换为tokenID,稍后可以通过嵌入层进行处理。

# 将预处理后的单词列表去重并排序
all_words = sorted(set(preprocessed))
# 计算词汇表的大小
vocab_size = len(all_words)
# 创建一个字典,将每个单词映射到唯一的整数索引
vocab = {token: integer for integer, token in enumerate(all_words)}
# 输出词汇表的大小
print(vocab_size)

# 遍历词汇字典并输出前50个单词及其对应的索引
for i, item in enumerate(vocab.items()):
    print(item)  # 输出单词及其索引
    if i >= 50:  # 当索引达到50时停止输出
        break

封装标记器类

该类实现了一个简单的文本编码和解码器。构造函数接收一个词汇表,将其映射为字符串到整数和整数到字符串的字典。encode 方法将输入文本分割、预处理并转换为整数 ID 列表,decode 方法则将 ID 列表转换回文本,并处理标点前的空格。如下图所示

class SimpleTokenizerV1:
    def __init__(self, vocab):
        # 初始化时接收一个词汇表字典
        self.str_to_int = vocab  # 字符串到整数的映射
        # 创建整数到字符串的反向映射
        self.int_to_str = {i: s for s, i in vocab.items()}

    def encode(self, text):
        # 使用正则表达式分割输入文本,保留标点和空格作为分割符
        preprocessed = re.split(r'([,.:;?_!"()\']|--|\s)', text)

        # 去除每个分割后的项的前后空白,并过滤掉空字符串
        preprocessed = [
            item.strip() for item in preprocessed if item.strip()
        ]
        # 将处理后的每个单词转换为对应的整数ID
        ids = [self.str_to_int[s] for s in preprocessed]
        return ids  # 返回编码后的ID列表

    def decode(self, ids):
        # 根据ID列表生成对应的文本字符串
        text = " ".join([self.int_to_str[i] for i in ids])
        # 替换指定标点前的空格
        text = re.sub(r'\s+([,.?!"()\'])', r'\1', text)
        return text  # 返回解码后的文本
# 创建一个 SimpleTokenizerV1 实例,传入词汇表
tokenizer = SimpleTokenizerV1(vocab)
# 定义要编码的文本
text = """"It's the last he painted, you know," 
           Mrs. Gisburn said with pardonable pride."""

# 使用 tokenizer 的 encode 方法将文本编码为 ID 列表
ids = tokenizer.encode(text)
# 打印编码后的 ID 列表
print("Encoded IDs:", ids)
# 使用 tokenizer 的 decode 方法将 ID 列表解码回文本
decoded_text = tokenizer.decode(ids)
# 打印解码后的文本
print("Decoded text:", decoded_text)
# 再次使用 encode 和 decode 方法,验证编码和解码的一致性
# 编码文本后再解码
consistent_decoded_text = tokenizer.decode(tokenizer.encode(text))
# 打印一致性验证的结果
print("Consistent decoded text:", consistent_decoded_text)

2.4 添加特殊上下文token

在文本处理中,为了提供额外的上下文,添加一些“特殊”标记是非常有用的,特别是在处理未知单词和文本结束时。一些标记化器使用特殊标记来帮助大语言模型(LLM)理解文本,如下图所示。以下是一些常见的特殊标记:

  • [BOS](序列开始)表示文本的开头。
  • [EOS](序列结束)标记文本结束的位置,通常用于连接多段不相关的文本,例如两篇不同的维基百科文章或两本不同的书籍。
  • [PAD](填充)在训练 LLM 时,当批量大小大于 1 时,可能会包含长度不同的多个文本;使用填充标记可以将较短的文本填充到最长的长度,以使所有文本具有相同的长度。
  • [UNK] 用于表示不在词汇表中的单词。 

需要注意的是,GPT-2 不需要上述提到的任何标记,只使用 <|endoftext|> 标记以简化处理,如下图所示。这个 <|endoftext|> 标记类似于 [EOS] 标记。GPT 还使用 <|endoftext|> 进行填充,因为在训练批量输入时通常使用掩码,填充标记不会被关注,因此这些标记的具体内容并不重要。

此外,GPT-2 不使用 <UNK> 标记来处理超出词汇表的单词,而是采用字节对编码(BPE)标记化器,将单词分解为子词单元,这将在后面中讨论。

注意,在进行文本标记化时,如果输入文本中的单词不在词汇表中,例如 "Hello",则会产生错误。为了解决这种情况,可以在词汇表中添加特殊标记,如 "<|unk|>",用于表示未知单词。此外,由于我们已经在扩展词汇表,可以再添加一个名为 "<|endoftext|>" 的标记,它在 GPT-2 的训练中用于表示文本的结束(并且在连接多个文本时也会使用,例如当训练数据集包含多篇文章、书籍等时)。

tokenizer = SimpleTokenizerV1(vocab)
text = "Hello, do you like tea. Is this-- a test?"
tokenizer.encode(text)

为了让标记化器能够正确使用新的 <|unk|> 标记,我们需要对其进行相应的调整。接下来,我们可以尝试使用修改后的标记化器进行标记化

import re  # 导入正则表达式模块

class SimpleTokenizerV2:
    def __init__(self, vocab):
        # 初始化时接收一个词汇表字典
        self.str_to_int = vocab  # 字符串到整数的映射
        # 创建整数到字符串的反向映射
        self.int_to_str = {i: s for s, i in vocab.items()}

    def encode(self, text):
        # 使用正则表达式分割输入文本,保留标点和空格作为分割符
        preprocessed = re.split(r'([,.:;?_!"()\']|--|\s)', text)
        # 去除每个分割后的项的前后空白,并过滤掉空字符串
        preprocessed = [item.strip() for item in preprocessed if item.strip()]
        # 将不在词汇表中的单词替换为 "<|unk|>"
        preprocessed = [
            item if item in self.str_to_int
            else "<|unk|>" for item in preprocessed
        ]

        # 将处理后的每个单词转换为对应的整数ID
        ids = [self.str_to_int[s] for s in preprocessed]
        return ids  # 返回编码后的ID列表

    def decode(self, ids):
        # 根据ID列表生成对应的文本字符串
        text = " ".join([self.int_to_str[i] for i in ids])
        # 替换指定标点前的空格
        text = re.sub(r'\s+([,.:;?!"()\'])', r'\1', text)
        return text  # 返回解码后的文本

# 文件路径
file_path = "./the-verdict.txt"
# 打开文件并读取内容
with open(file_path, 'r', encoding='utf-8') as file:
    raw_text = file.read()  # 读取文件内容

# 对文本进行预处理,分割并去除空白
preprocessed = re.split(r'([,.?_!"()\']|--|\s)', raw_text)
preprocessed = [item.strip() for item in preprocessed if item.strip()]

# 将预处理后的单词列表去重并排序
all_tokens = sorted(set(preprocessed))
# 在词汇表中添加特殊标记
all_tokens.extend(["<|endoftext|>", "<|unk|>"])
# 计算词汇表的大小
vocab_size = len(all_tokens)

# 创建一个字典,将每个单词映射到唯一的整数索引
vocab = {token: integer for integer, token in enumerate(all_tokens)}

# 打印词汇表中最后五个单词及其索引
for i, item in enumerate(list(vocab.items())[-5:]):
    print(item)

# 创建标记化器实例
tokenizer = SimpleTokenizerV2(vocab)

# 定义两个示例文本
text1 = "Hello, do you like tea?"
text2 = "In the sunlit terraces of the palace."
# 连接两个文本,用 "<|endoftext|>" 标记分隔
text = " <|endoftext|> ".join((text1, text2))
print(text)  # 打印连接后的文本

# 使用标记化器进行编码
ids = tokenizer.encode(text)

# 打印编码后的 ID 列表
print("Encoded IDs:", ids)

# 使用标记化器的 decode 方法将 ID 列表解码回文本
decoded_text = tokenizer.decode(ids)
# 打印解码后的文本
print("Decoded text:", decoded_text)

# 再次使用 encode 和 decode 方法,验证编码和解码的一致性
# 编码文本后再解码
consistent_decoded_text = tokenizer.decode(tokenizer.encode(text))
# 打印一致性验证的结果
print("Consistent decoded text:", consistent_decoded_text)

2.5 字节对编码(BPE)

  • GPT-2 使用字节对编码(BytePair Encoding, BPE)作为其标记化器。
  • 这种方法允许模型将不在预定义词汇表中的单词拆分为更小的子词单元,甚至是单个字符,从而能够处理超出词汇表的单词。
  • 例如,如果 GPT-2 的词汇表中没有单词 "unfamiliarword",它可能将其标记化为 ["unfam", "iliar", "word"] 或其他子词拆分,这取决于其训练时的 BPE 合并策略。
  • 原始的 BPE 标记化器代码可以在这里找到:https://github.com/openai/gpt-2/blob/master/src/encoder.py。
  • 在本章中,我们使用了 OpenAI 的开源库 tiktoken 中的 BPE 标记化器,该库的核心算法用 Rust 实现,以提高计算性能。
  • 我在 ./bytepair_encoder 目录中创建了一个笔记本,比较了这两种实现的性能(tiktoken 在样本文本上的速度约快 5 倍)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2170341.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“Y模型”—我在3年实操后的个人总结

一直想写一篇关于【需求分析】及【产品设计】方面个人最常用的一些方式方法&#xff0c;对于一些刚入行以及埋头苦干的同学来说&#xff0c;大多数情况都是粗放式凭感觉的分析产品。 因为自己也有过这样的阶段&#xff0c;深知这种思考方式的弊端。从用户场景/反馈到具象化的产…

Linux标准IO(四)-格式化I/O输入

C 库函数提供了 3 个格式化输入函数&#xff0c;包括&#xff1a;scanf()、fscanf()、sscanf()&#xff0c;其函数定义如下所示&#xff1a; #include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *stream, const char *format, ...); int sscanf(c…

2023年金融科技建模大赛(初赛)开箱点评,多分类模型实战

原创作者Toby&#xff0c;文章来源公众号&#xff1a;python风控模型&#xff0c;2023年金融科技建模大赛&#xff08;初赛&#xff09;开箱点评 各位同学大家好&#xff0c;我是Toby老师。2023年金融科技建模大赛&#xff08;初赛&#xff09;从今年10月14日开始&#xff0c;…

最强反推更新!Joy Caption Alpha One详细测评、在线免费使用

免费教程网站&#xff1a;AI教程_深度学习入门指南 - 站长素材 (chinaz.com) 原文链接&#xff1a;最强反推更新&#xff01;Joy Caption Alpha One详细测评、在线免费使用 (chinaz.com) JoyCaption在一周前悄悄上线了最新版本Joycaption alpha one Joycaption alpha one免费在…

Python进阶:利用NotImplemented优化你的对象交互逻辑,让Python对象间的操作更加智能与灵活

推荐阅读&#xff1a;从混乱到清晰&#xff1a;用NotImplementedError重构你的Python代码&#xff0c;NotImplementedError如何助你打造更健壮的API NotImplemented 在Python中&#xff0c;NotImplemented并不是一个异常类&#xff0c;而是一个特殊的值&#xff0c;用于在二元…

linux桌面软件(wps)内嵌到其他窗口

程序测试环境是&#xff1a;slackware系统&#xff0c;属于linux系统&#xff0c;有桌面&#xff08;Xface Session&#xff09;。系统镜像是&#xff1a;slackware64-15.0-install-dvd.iso。qt、c代码实现。 程序功能&#xff1a;将已经打开的wps&#xff08;word、pdf等都可…

【优选算法】(第五篇)

目录 ⻓度最⼩的⼦数组&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⽆重复字符的最⻓⼦串&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⻓度最⼩的⼦数组&#xff08;medium&#xff09; 题目解析 1.题目链接&#xff1a;. - …

分割数组的最大值

题目链接 分割数组的最大值 题目描述 注意点 0 < nums[i] < 10^61 < nums.length < 10001 < k < min(50, nums.length) 解答思路 首先需要理解题意&#xff0c;需要将这个数组分成 k 个非空的连续子数组&#xff0c;找到划分组合中子数组和的最大值最小…

el-table+el-form实现表单校验和解决不垂直居中导致的问题

el-tableel-form实现表单校验 1.实现el-table的表单校验 关键点123 2.解决不垂直居中导致的问题 问题效果图 解决方案 .item-align-center {display: inline-flex; }

OJ在线评测系统 原生Java代码沙箱核心实现流程三 整理封装输出结果 拿到程序执行时间(stopwatch类) 和 运行内存

我们在之前的操作中已经拿到程序进行了编译和运行 接下来我们要将我们的结果输出 整理输出 // 4.收集整理输出结果 ExecuteCodeResponse executeCodeResponse new ExecuteCodeResponse(); ArrayList<String> outputList new ArrayList<>();for (ExecuteMessage…

Library介绍(一)

之前和大家介绍过cell delay是如何计算的。那么&#xff0c;本文将着重和大家介绍一些timing lib中的各个参数定义是什么意思。会分以下几个部分介绍&#xff1a;库属性描述、时序弧介绍、环境描述、单元描述。之前介绍的cell delay template就是单元描述中的一部分。本文主要介…

网络安全入门必备:这四点你做到了吗?

数据的鸿沟无疑是显而易见的&#xff0c;网络安全领域亟需熟练的专业人员。 组织在这方面投入巨大资金&#xff0c;但挑战依旧存在。 根据最新的研究&#xff0c;有64%的违规行为是导致机构过去一年收入损失及/或罚款的主要原因。 60%的组织在努力招聘网络安全人才&#xff…

【市场解读】新能源汽车换代问题

参考文献&#xff1a;百分点舆情中心《新能源汽车换代问题消费者情绪洞察报告》 行业背景 新能源汽车市场竞争加剧&#xff0c;车企不断推陈出新政府发布《汽车以旧换新补贴实施细则》&#xff0c;激励市场发展 *对汽车换代问题媒体关注度与网友讨论度高&#xff0c;正面声量…

电脑退域后系统黑屏

之前加入域时迁移了账号系统&#xff0c;导致退域后本地账号系统没了东西黑屏但能看到鼠标。也登不了域账号了一顿慌张&#xff08;操作如下&#xff09; 解决&#xff1a;又加回了域哈哈哈 重启电脑按F8进不去安全模式&#xff0c;找不到触发时间... winr打开运行&#xff0c;…

都说网络安全缺口那么大,但为何招聘数量却不多?总算明白了!

为啥网安领域缺口多达300多万人&#xff0c;但网安工程师也就是白帽黑客却很少&#xff0c;难道又是砖家在忽悠人&#xff1f; 原因主要为这三点: 首先是学校的原因&#xff0c;很多学校网络安全课程用的还都是十年前的老教材&#xff0c;教学脱离社会需求&#xff0c;实操技能…

【Python报错已解决】TypeError: expected Tensor as element 1 in argument 0, but got int

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

NHANES数据(复杂调查数据)亚组交互函数2.3版(P for interaction)发布---用于一键生成交互效应表

写在前面的话&#xff0c;本函数只支持NHANES数据(复杂调查数据)的逻辑回归和线性回归&#xff0c;其他类型均不支持&#xff0c;请注意甄别&#xff0c;电子产品&#xff0c;买错不能退换。 在SCI文章中&#xff0c;交互效应表格&#xff08;通常是表五&#xff09;能为文章锦…

多类别物体检测系统源码分享

多类别物体检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

“领航猿1号” 正式更名为 “AGI舰长”

亲爱的朋友们&#xff0c;很高兴的告诉大家&#xff1a; 我各个平台的账号昵称正式 由“领航猿1号” 更名为 “AGI舰长” 为什么更名&#xff1a; 为了更好的更专注的为大家提供关于“AI大模型全栈”的分享&#xff0c;特此以 AI 为关键元素更名账号名称&#xff0c;大家可以…

企业内网知识问答库小程序源码系统 收录好+排名高 带完整的安装代码包以及搭建部署教程

系统概述 企业内网知识问答库小程序源码系统是一款集知识收集、整理、检索与分享于一体的综合解决方案。它基于现代Web技术和小程序框架开发&#xff0c;旨在为企业内部员工提供一个便捷、高效的知识交流平台。该系统不仅支持文本、图片、视频等多种形式的内容输入&#xff0c…