【教程】从0开始搭建大语言模型:Word和位置Embedding

news2024/11/16 7:27:19

从0开始搭建大语言模型:Dataset构造

接上文:【教程】从0开始搭建大语言模型:文本预处理

通过滑动窗口进行数据采样

我们要构造输入-目标对来对模型进行训练。

在LLM中,它通过预测文本中的下一个单词进行训练,如下所示:
在这里插入图片描述
给定一个文本样本,提取输入块作为LLM的输入子样本,LLM在训练期间的预测任务是预测输入块之后的下一个单词。在训练过程中,我们屏蔽掉所有超过目标的单词。

为下一个单词预测任务创建输入-目标对的最简单和最直观的方法之一是创建两个变量,x和y,其中x包含输入token,y包含目标,它们之间的下标偏移为1,代码实现如下所示:

import tiktoken
with open("./data/the_verdict.txt", "r", encoding="utf-8") as f:
    raw_text = f.read()
tokenizer = tiktoken.get_encoding("gpt2")
enc_text = tokenizer.encode(raw_text)
# 移除前面50个
enc_sample = enc_text[50:]
# 构建input-target对
context_size = 4
x = enc_sample[:context_size]
# 跟x相比往后偏移一个距离
y = enc_sample[1:context_size+1]

输入和目标之间的变化用代码输出为:

# token id形式的表示
for i in range(1, context_size+1):
    context = enc_sample[:i]
    desired = enc_sample[i]
    print(context, "---->", desired)
# 转换为token形式
for i in range(1, context_size+1):
    context = enc_sample[:i]
    desired = enc_sample[i]
    print(tokenizer.decode(context), "---->", tokenizer.decode([desired]))

对应的输出为:

[290] ----> 4920
[290, 4920] ----> 2241
[290, 4920, 2241] ----> 287
[290, 4920, 2241, 287] ----> 257
and ---->  established
and established ---->  himself
and established himself ---->  in
and established himself in ---->  a

在训练的时候,我们希望数据加载器返回两个张量:一个是包含LLM看到的文本的输入张量,一个是包含LLM要预测的目标张量,如下图所示:
在这里插入图片描述
图中为了方便理解展示的是文本token,但实际上都是token id,即一些数字。

为了实现这一功能,使用PytorchDatasetDataLoader,它可以高效地实现。
代码如下:

import torch
from torch.utils.data import Dataset, DataLoader
class GPTDatasetV1(Dataset):
    def __init__(self, txt, tokenizer, max_length, stride):
        # max_length表示选中的文本的最大长度,stride表示间隔多少个词再开始
        self.tokenizer = tokenizer
        self.input_ids = []
        self.target_ids = []
        token_ids = tokenizer.encode(txt)
        # 构造训练集
        for i in range(0, len(token_ids) - max_length, stride):
            # 一个input-target对
            input_chunk = token_ids[i:i + max_length]
            target_chunk = token_ids[i + 1: i + max_length + 1]
            # 加到列表里面
            self.input_ids.append(torch.tensor(input_chunk))
            self.target_ids.append(torch.tensor(target_chunk))
    # 返回数据集大小
    def __len__(self):
        return len(self.input_ids)

    def __getitem__(self, idx):
        return self.input_ids[idx], self.target_ids[idx]

# 创建data loader
def create_dataloader_v1(txt, batch_size=4, max_length=256, stride=128, shuffle=True, drop_last=True):
    tokenizer = tiktoken.get_encoding("gpt2")
    dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last)
    return dataloader

调用这两个类,代码为:

# 创建loader,进行数据加载
## stride设置规定了输入在批次中移动的距离,模拟滑动窗口方法
dataloader = create_dataloader_v1(raw_text, batch_size=1, max_length=4, stride=1, shuffle=False)
data_iter = iter(dataloader)
first_batch = next(data_iter)
print(first_batch)

输出为:

[tensor([[  40,  367, 2885, 1464]]), tensor([[ 367, 2885, 1464, 1807]])]

create_dataloader_v1的参数中,stride设置规定了输入在批次中移动的距离,模拟滑动窗口方法,当stride等于窗口大小,即max_length时,可以防止每个batch之间的重叠,过多的重叠可能导致过拟合,如下所示:
在这里插入图片描述

创建token embeddings

准备用于LLM训练的输入文本的最后一步是将token id转换为embedding向量,如下所示:
在这里插入图片描述
一般来说,我们刚开始时用随机值初始化这些embedding权重。这个初始化是LLM学习过程的起点。

在Pytorch中,我们使用nn.Embedding来生成embedding,示例代码为:

input_ids = torch.tensor([2, 3, 5, 1])
vocab_size = 6
output_dim = 3
torch.manual_seed(123)
embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
print(embedding_layer.weight)

这里,vocab_size是字典的大小,output_dim是embedding的尺寸。
输出如下,其中Embedding层的权重尺寸为vocab_size * output_dim

Parameter containing:
tensor([[ 0.3374, -0.1778, -0.1690],
        [ 0.9178,  1.5810,  1.3010],
        [ 1.2753, -0.2010, -0.1606],
        [-0.4015,  0.9666, -1.1481],
        [-1.1589,  0.3255, -0.6315],
        [-2.8400, -0.7849, -1.4096]], requires_grad=True)

我们可以看到权重矩阵有6行3列。词汇表中有6个可能的单词,每个单词对应一行。每个embedding维度都有一列。

对一个token id进行embedding,代码为:

print(embedding_layer(torch.tensor([3])))

此时的输出为:

tensor([[-0.4015,  0.9666, -1.1481]], grad_fn=<EmbeddingBackward0>)

维度是3,表示3这个token id对应的embedding尺寸。

如果我们将ID为3的标记的embedding向量与之前的embedding矩阵进行比较,我们看到它与第4行相同(Python从索引0开始,因此它是与索引3对应的行)。embedding层本质上是一个查找操作,通过token ID从embedding层的权重矩阵中检索行。如下所示:
在这里插入图片描述

编码word位置

LLM的一个小缺点是其中的自注意力机制没有序列中token的位置或顺序的概念。前面介绍的embedding层的工作方式是,相同的token ID总是映射到相同的向量表示,无论令牌ID在输入序列中的位置,如下图所示。
在这里插入图片描述
原则上,token ID的确定性、位置无关的embedding有利于再现性。然而,由于LLM本身的自注意力机制也是位置无关的,因此有助于向LLM中加入额外的位置信息,有两种方式:

  • 相对位置embeddings
  • 绝对位置embeddings

绝对位置embeddings与序列中的特定位置直接关联。对于输入序列中的每个位置,在token的embeddings中添加一个唯一的embeddings,以传递其确切位置。如下图所示:
在这里插入图片描述
相对位置嵌入的重点是标记之间的相对位置或距离。这意味着模型根据“距离多远”而不是“确切位置”来学习关系。这样做的好处是,即使在训练过程中没有看到过长度不同的序列,模型也可以更好地泛化。

这两种类型的位置embedding旨在增强LLM理解token之间的顺序和关系的能力,确保更准确和上下文感知的预测。

OpenAI的GPT模型使用在训练过程中可以优化的绝对位置embedding,而不是像原始Transformer模型中的位置编码那样是固定的或预定义的。

接下来我们来完成对上一节的the_verdict文本内容的embedding,代码如下:

output_dim = 256
vocab_size = 50257
token_embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
max_length = 4
dataloader = create_dataloader_v1(raw_text, batch_size=8, max_length=max_length, stride=max_length, shuffle=False)
data_iter = iter(dataloader)
inputs, targets = next(data_iter)
print("Token IDs:\n", inputs)
print("\nInputs shape:\n", inputs.shape)
token_embeddings = token_embedding_layer(inputs)
print(token_embeddings.shape)

其中raw_text为文本内容,最后输出的内容为:

Token IDs:
 tensor([[   40,   367,  2885,  1464],
        [ 1807,  3619,   402,   271],
        [10899,  2138,   257,  7026],
        [15632,   438,  2016,   257],
        [  922,  5891,  1576,   438],
        [  568,   340,   373,   645],
        [ 1049,  5975,   284,   502],
        [  284,  3285,   326,    11]])

Inputs shape:
torch.Size([8, 4])
torch.Size([8, 4, 256])

然后创建位置编码,代码为:

# 创建position embedding
context_length = max_length
pos_embedding_layer = torch.nn.Embedding(context_length, output_dim)
pos_embeddings = pos_embedding_layer(torch.arange(context_length))
print(pos_embeddings.shape)

其中pos_embeddings的输入通常是一个占位向量torch.arange(context_length),它包含一个数字序列0,1,…,最大值的长度 - 1。context_length是一个变量,表示LLM支持的输入大小。

最后将位置编码加到embedding上,代码为:

input_embeddings = token_embeddings + pos_embeddings
print(input_embeddings.shape)

整个输入处理的流程为:
在这里插入图片描述

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

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

相关文章

C#操作MySQL从入门到精通(9)——Mysql中的数据类型以及对应的C#中的数据类型

前言 本文介绍Mysql中的数据类型以及每种类型对应的c#中的数据类型 1、整数 bit int tinyint smallint mediumint bigint 2、浮点数 float double decimal 3、日期时间 year time date datetime timestamp 4、字符型 char varchar tinytext text mediumtext longtext …

如何开发一 VSCode 插件

如何开发一个 VSCode 插件&#xff0c;本文开发一个 VSCode “Hello World” 插件&#xff0c;通过代码了解 VSCode 插件是如何工作的。 安装脚手架 npx --package yo --package generator-code -- yo code根据提示选择&#xff0c;插件开发语言选择 TypeScript ? What type…

原力、百度、人人文档下载工具

只可下载可预览的文档&#xff0c;格式为pdf&#xff0c;不能完全保证下载成功&#xff0c;X度与我们既是对手也是朋友。 本文的软件来自的大神&#xff0c;仅供学习交流&#xff0c;不可做它用。 向的大神致敬&#xff01;&#xff01;&#xff01;

信息系统项目管理师0148:输出(9项目范围管理—9.3规划范围管理—9.3.3输出)

点击查看专栏目录 文章目录 9.3.3 输出 9.3.3 输出 范围管理计划 范围管理计划是项目管理计划的组成部分&#xff0c;描述将如何定义、制定、监督、控制和确认项 目范围。范围管理计划用于指导如下过程和相关工作&#xff1a; ①制定项目范围说明书&#xff1b;②根据详细项目范…

在npm发布自己的组件包

目录 前言 正文 npm和git的对比 Node环境的配置 具体发布步骤 ※※需要注意的是 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1f46f; I’m studying in University of Nottingham Ni…

信息系统项目管理师0149:输入(9项目范围管理—9.4收集需求—9.4.1输入)

点击查看专栏目录 文章目录 9.4 收集需求9.4.1 输入9.4 收集需求 收集需求是为实现目标而确定,记录并管理干系人的需要和需求的过程。本过程的主要作用是为定义产品范围和项目范围奠定基础。本过程仅开展一次或仅在项目的预定义点开展。收集需求过程的数据流向如图 9-2 所示。…

力扣 74.搜索二维矩阵

题目描述&#xff1a; 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&am…

【MMU】——ARM 二级页表

文章目录 二级页表项即 entry 的格式如下 二级页表项有三种类型 产生中止异常的故障条目。这可能是预取或数据中止、取决于访问类型。这实际上表示虚拟地址未映射 bit[1:0] 00大页 64KB。bit[1:0] 01。小页 4KB。bit[1:0]1x。 一级页表占用 16KB 的内存&#xff0c;二级页表…

攻防世界---misc---Hear-with-your-Eyes

1、题目描述&#xff0c;下载附件&#xff0c;是一个.gz后缀的文件&#xff0c;查找资料发现&#xff0c;这个后缀是Linux系统的压缩包后缀。这里题目提示了用眼睛听音频&#xff0c;说明会有个音频&#xff0c;并且信息就在音频&#xff0c;可以用眼睛看到 2、将文件放在linux…

警务反诈RPA:利用机器人流程自动化技术打击诈骗行为

信息时代&#xff0c;网络技术快速发展&#xff0c;在丰富生活的同时&#xff0c;也带来了持续高发的电信网络诈骗问题&#xff0c;对社会造成了严重威胁。面对海量的数据信息&#xff0c;传统数据融合技术的瓶颈不断显现&#xff0c;无法满足风险防控、打击诈骗的效率要求&…

使用 Spring Boot 开发邮件系统

文章目录 使用 Spring Boot 开发邮件系统邮件发送流程简单使用第 1 步&#xff1a;pom 包配置第 2 步&#xff1a;配置文件163 邮箱配置126 邮箱配置QQ 邮箱配置如下:开启 POP 3 / SMTP 服务、IMAP / SMTP 服务开通设置客户端授权密码 第 3 步&#xff1a;文本邮件发送第 4 步&…

L45---506.相对名次(java)--排序

1.题目描述 2.知识点 &#xff08;1&#xff09;String.join(" ", words) 是 Java 中的一个语法&#xff0c;用于将数组或集合中的元素连接成一个单独的字符串&#xff0c;连接时使用指定的分隔符。这里的 " " 是作为分隔符使用的一个空格字符串。 Strin…

Bond 网卡绑定技术学习

前言&#xff1a; 为了实现网卡的高可用性&#xff0c;需要学习一下 Bond技术 1. 概念 Bond&#xff08;也被称为链路聚合、端口绑定或接口绑定&#xff09;是一种网络技术&#xff0c;用于将多个物理网络接口&#xff08;如以太网接口&#xff09;组合成一个逻辑接口。这样做…

循环语句大揭秘:while、do-while、for、foreach你都掌握了吗?

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

高质量 HarmonyOS 权限管控流程

高质量 HarmonyOS 权限管控流程 在 HarmonyOS 应用开发过程中&#xff0c;往往会涉及到敏感数据和硬件资源的调动和访问&#xff0c;而这部分的调用就会涉及到管控这部分的知识和内容了。我们需要对它有所了解&#xff0c;才可以在应用开发中提高效率和避免踩坑。 权限管控了…

STM32项目分享:智能门禁锁系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板及元器件图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.c…

使用 stress 命令进行Linux CPU 压力测试

大家好&#xff0c;在现代计算机系统中&#xff0c;对系统性能和稳定性的评估是至关重要的。特别是在服务器环境中&#xff0c;我们需要确保系统能够在高负载情况下稳定运行&#xff0c;以满足用户的需求。而 CPU 是系统中最关键的组件之一&#xff0c;其性能直接影响着整个系统…

探索加密货币:打造Web3项目与企业的新篇章

本文来源香港Web3媒体&#xff1a;Techub News 在数字化浪潮席卷全球的今天&#xff0c;加密货币及Web3项目和企业正成为引领创新的重要力量。作为这一领域的观察者&#xff0c;Mark Beylin深入剖析了如何通过打造人们真正需要的代币&#xff0c;来推动Web3项目的成功与企业的蓬…

电脑开机出现英文字母,如何解决这个常见问题?

电脑开机时出现英文字母的情况通常意味着系统在启动过程中遇到了问题。这些英文字母可能是错误信息、系统提示或BIOS设置问题。通过理解这些信息并采取适当的措施&#xff0c;您可以解决大多数启动问题。本文将介绍三种解决电脑开机出现英文字母问题的方法&#xff0c;帮助您恢…

VSCode搭建开发环境--从PyCharm到拥抱vscode

VSCode搭建开发环境 前言安装扩展全局配置文件单个项目的配置快捷键 前言 最近自己的PyCharm Professional的License过期了&#xff0c;导致没有一个好的开发IDE&#xff0c;于是开始拥抱免费的Visual Studio Code啦。 当然&#xff0c;不可否认的是PyCharm对于开发Python来说…