Chapter 4.6:Coding the GPT model

4 Implementing a GPT model from Scratch To Generate Text

4.6 Coding the GPT model

  • 本章从宏观视角介绍了 DummyGPTModel,使用占位符表示其构建模块,随后用真实的 TransformerBlock 和 LayerNorm 类替换占位符,组装出完整的 1.24 亿参数 GPT-2 模型,并计划在后续章节进行预训练和加载 OpenAI 的预训练权重,同时通过下图 展示了结合本章所有概念的 GPT-2 整体结构。通过将变换器块插入到本章开头的架构中并重复 12 次(以 124M GPT-2 模型为例),我们构建了一个完整且可用的 GPT 架构。

    4_15

    从底部开始,tokenized text 首先被转换为 token embeddings,然后通过 positional embeddings 进行增强。这些信息组合成一个张量,随后通过一系列 transformer 块(如中心部分所示,每个块包含多头注意力机制和前馈神经网络层,并应用了 dropout 和层归一化),这些块堆叠在一起,重复 12 次,我们通过 GPT_CONFIG_124M 字典中的“n_layers”条目指定。(在拥有 15.42 亿个参数的最大 GPT-2 模型中,该transformer块重复了 36 次)。

  • 上图架构的对应代码实现

    class GPTModel(nn.Module):def __init__(self, cfg):super().__init__()self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])self.drop_emb = nn.Dropout(cfg["drop_rate"])# 创建 TransformerBlock 模块的顺序堆栈self.trf_blocks = nn.Sequential(*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])]) self.final_norm = LayerNorm(cfg["emb_dim"])# self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)def forward(self, in_idx):batch_size, seq_len = in_idx.shapetok_embeds = self.tok_emb(in_idx)pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))x = tok_embeds + pos_embeds  # Shape [batch_size, num_tokens, emb_size]x = self.drop_emb(x)x = self.trf_blocks(x)x = self.final_norm(x)logits = self.out_head(x)return logits

    使用 124M 参数模型的配置,我们现在可以用随机初始权重实例化这个 GPT 模型

    # 初始化实例化GPT模型
    torch.manual_seed(123)
    tokenizer = tiktoken.get_encoding("gpt2")batch = []txt1 = "Every effort moves you"
    txt2 = "Every day holds a"batch.append(torch.tensor(tokenizer.encode(txt1)))
    batch.append(torch.tensor(tokenizer.encode(txt2)))
    batch = torch.stack(batch, dim=0)
    print(batch)model = GPTModel(GPT_CONFIG_124M)out = model(batch)
    print("Input batch:\n", batch)
    print("\nOutput shape:", out.shape)
    print(out)"""输出"""
    Input batch:tensor([[6109, 3626, 6100,  345],[6109, 1110, 6622,  257]])Output shape: torch.Size([2, 4, 50257])
    tensor([[[ 0.1381,  0.0077, -0.1963,  ..., -0.0222, -0.1060,  0.1717],[ 0.3865, -0.8408, -0.6564,  ..., -0.5163,  0.2369, -0.3357],[ 0.6989, -0.1829, -0.1631,  ...,  0.1472, -0.6504, -0.0056],[-0.4290,  0.1669, -0.1258,  ...,  1.1579,  0.5303, -0.5549]],[[ 0.1094, -0.2894, -0.1467,  ..., -0.0557,  0.2911, -0.2824],[ 0.0882, -0.3552, -0.3527,  ...,  1.2930,  0.0053,  0.1898],[ 0.6091,  0.4702, -0.4094,  ...,  0.7688,  0.3787, -0.1974],[-0.0612, -0.0737,  0.4751,  ...,  1.2463, -0.3834,  0.0609]]],grad_fn=<UnsafeViewBackward0>)
    

    如我们所见,输出张量的形状为 [2, 4, 50257],因为我们输入了 2 个文本,每个文本包含 4 个 token。最后一个维度 50,257 对应于 tokenizer 的词汇表大小。在下一节中,我们将了解如何将这些 50,257 维的输出向量转换回 token。

  • 不过,关于其大小需要简要说明:我们之前将其称为 1.24 亿参数模型;我们可以通过以下方式再次确认这一数字:

    使用 numel() 方法(“元素数量”的缩写),我们可以收集模型参数张量中的参数总数:

    total_params = sum(p.numel() for p in model.parameters())
    print(f"Total number of parameters: {total_params:,}")"""输出"""
    Total number of parameters: 163,009,536
    

    模型参数数量为 163M 而非 124M,原因是未应用权重绑定(weight tying),即 GPT-2 中将token embedding层重用作输出层以减少参数;嵌入层将 50,257 维 one-hot 编码标记投影到 768 维嵌入表示,而输出层将其投影回 50,257 维以转换回单词,两者参数数量一致,需进一步验证模型参数数量为 124M。

    print("Token embedding layer shape:", model.tok_emb.weight.shape)
    print("Output layer shape:", model.out_head.weight.shape)"""输出"""
    Token embedding layer shape: torch.Size([50257, 768])
    Output layer shape: torch.Size([50257, 768])
    

    相应地,如果我们减去输出层的参数数量,就会得到一个 124M 参数的模型:

    total_params_gpt2 =  total_params - sum(p.numel() for p in model.out_head.parameters())
    print(f"Number of trainable parameters considering weight tying: {total_params_gpt2:,}")"""输出"""
    Number of trainable parameters considering weight tying: 124,412,160
    

    即$ 163,009,536 - 50257*768 = 124412160$ ,该模型现在只有 1.24 亿个参数,与 GPT-2 模型的原始大小相匹配。

    在实践中,不使用权重共享训练模型更为简便,因此本节未实现权重共享。后续章节将重新考虑权重共享,并在加载预训练权重时应用。此外,计算模型的内存需求也是一个重要的参考点。

  • 计算模型内存需求

    # Calculate the total size in bytes (assuming float32, 4 bytes per parameter)
    total_size_bytes = total_params * 4# Convert to megabytes
    total_size_mb = total_size_bytes / (1024 * 1024)print(f"Total size of the model: {total_size_mb:.2f} MB")"""输出"""
    Total size of the model: 621.83 MB
    

    通过计算 GPTModel 对象中 1.63 亿参数的内存需求,并假设每个参数为 32 位浮点数,占用 4 字节,我们发现模型的总大小为 621.83 MB,这说明了即使是相对较小的 LLMs 也需要较大的存储空间。

  • 在本节中,我们实现了 GPTModel 架构,并看到它输出了形状为 [batch_size, num_tokens, vocab_size] 的数值张量。
    在下一节中,我们将编写代码将这些输出张量转换为文本。## Coding the GPT model

  • 本章从宏观视角介绍了 DummyGPTModel,使用占位符表示其构建模块,随后用真实的 TransformerBlock 和 LayerNorm 类替换占位符,组装出完整的 1.24 亿参数 GPT-2 模型,并计划在后续章节进行预训练和加载 OpenAI 的预训练权重,同时通过下图 展示了结合本章所有概念的 GPT-2 整体结构。通过将变换器块插入到本章开头的架构中并重复 12 次(以 124M GPT-2 模型为例),我们构建了一个完整且可用的 GPT 架构。

    4_15

    从底部开始,tokenized text 首先被转换为 token embeddings,然后通过 positional embeddings 进行增强。这些信息组合成一个张量,随后通过一系列 transformer 块(如中心部分所示,每个块包含多头注意力机制和前馈神经网络层,并应用了 dropout 和层归一化),这些块堆叠在一起,重复 12 次,我们通过 GPT_CONFIG_124M 字典中的“n_layers”条目指定。(在拥有 15.42 亿个参数的最大 GPT-2 模型中,该transformer块重复了 36 次)。

  • 上图架构的对应代码实现

    class GPTModel(nn.Module):def __init__(self, cfg):super().__init__()self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])self.drop_emb = nn.Dropout(cfg["drop_rate"])# 创建 TransformerBlock 模块的顺序堆栈self.trf_blocks = nn.Sequential(*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])]) self.final_norm = LayerNorm(cfg["emb_dim"])# self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)def forward(self, in_idx):batch_size, seq_len = in_idx.shapetok_embeds = self.tok_emb(in_idx)pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))x = tok_embeds + pos_embeds  # Shape [batch_size, num_tokens, emb_size]x = self.drop_emb(x)x = self.trf_blocks(x)x = self.final_norm(x)logits = self.out_head(x)return logits

    使用 124M 参数模型的配置,我们现在可以用随机初始权重实例化这个 GPT 模型

    # 初始化实例化GPT模型
    torch.manual_seed(123)
    tokenizer = tiktoken.get_encoding("gpt2")batch = []txt1 = "Every effort moves you"
    txt2 = "Every day holds a"batch.append(torch.tensor(tokenizer.encode(txt1)))
    batch.append(torch.tensor(tokenizer.encode(txt2)))
    batch = torch.stack(batch, dim=0)
    print(batch)model = GPTModel(GPT_CONFIG_124M)out = model(batch)
    print("Input batch:\n", batch)
    print("\nOutput shape:", out.shape)
    print(out)"""输出"""
    Input batch:tensor([[6109, 3626, 6100,  345],[6109, 1110, 6622,  257]])Output shape: torch.Size([2, 4, 50257])
    tensor([[[ 0.1381,  0.0077, -0.1963,  ..., -0.0222, -0.1060,  0.1717],[ 0.3865, -0.8408, -0.6564,  ..., -0.5163,  0.2369, -0.3357],[ 0.6989, -0.1829, -0.1631,  ...,  0.1472, -0.6504, -0.0056],[-0.4290,  0.1669, -0.1258,  ...,  1.1579,  0.5303, -0.5549]],[[ 0.1094, -0.2894, -0.1467,  ..., -0.0557,  0.2911, -0.2824],[ 0.0882, -0.3552, -0.3527,  ...,  1.2930,  0.0053,  0.1898],[ 0.6091,  0.4702, -0.4094,  ...,  0.7688,  0.3787, -0.1974],[-0.0612, -0.0737,  0.4751,  ...,  1.2463, -0.3834,  0.0609]]],grad_fn=<UnsafeViewBackward0>)
    

    如我们所见,输出张量的形状为 [2, 4, 50257],因为我们输入了 2 个文本,每个文本包含 4 个 token。最后一个维度 50,257 对应于 tokenizer 的词汇表大小。在下一节中,我们将了解如何将这些 50,257 维的输出向量转换回 token。

  • 不过,关于其大小需要简要说明:我们之前将其称为 1.24 亿参数模型;我们可以通过以下方式再次确认这一数字:

    使用 numel() 方法(“元素数量”的缩写),我们可以收集模型参数张量中的参数总数:

    total_params = sum(p.numel() for p in model.parameters())
    print(f"Total number of parameters: {total_params:,}")"""输出"""
    Total number of parameters: 163,009,536
    

    模型参数数量为 163M 而非 124M,原因是未应用权重绑定(weight tying),即 GPT-2 中将token embedding层重用作输出层以减少参数;嵌入层将 50,257 维 one-hot 编码标记投影到 768 维嵌入表示,而输出层将其投影回 50,257 维以转换回单词,两者参数数量一致,需进一步验证模型参数数量为 124M。

    print("Token embedding layer shape:", model.tok_emb.weight.shape)
    print("Output layer shape:", model.out_head.weight.shape)"""输出"""
    Token embedding layer shape: torch.Size([50257, 768])
    Output layer shape: torch.Size([50257, 768])
    

    相应地,如果我们减去输出层的参数数量,就会得到一个 124M 参数的模型:

    total_params_gpt2 =  total_params - sum(p.numel() for p in model.out_head.parameters())
    print(f"Number of trainable parameters considering weight tying: {total_params_gpt2:,}")"""输出"""
    Number of trainable parameters considering weight tying: 124,412,160
    

    即$ 163,009,536 - 50257*768 = 124412160$ ,该模型现在只有 1.24 亿个参数,与 GPT-2 模型的原始大小相匹配。

    在实践中,不使用权重共享训练模型更为简便,因此本节未实现权重共享。后续章节将重新考虑权重共享,并在加载预训练权重时应用。此外,计算模型的内存需求也是一个重要的参考点。

  • 计算模型内存需求

    # Calculate the total size in bytes (assuming float32, 4 bytes per parameter)
    total_size_bytes = total_params * 4# Convert to megabytes
    total_size_mb = total_size_bytes / (1024 * 1024)print(f"Total size of the model: {total_size_mb:.2f} MB")"""输出"""
    Total size of the model: 621.83 MB
    

    通过计算 GPTModel 对象中 1.63 亿参数的内存需求,并假设每个参数为 32 位浮点数,占用 4 字节,我们发现模型的总大小为 621.83 MB,这说明了即使是相对较小的 LLMs 也需要较大的存储空间。

  • 在本节中,我们实现了 GPTModel 架构,并看到它输出了形状为 [batch_size, num_tokens, vocab_size] 的数值张量。
    在下一节中,我们将编写代码将这些输出张量转换为文本。

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

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

相关文章

IDEA的Git界面(ALT+9)log选项不显示问题小记

IDEA的Git界面ALT9 log选项不显示问题 当前问题idea中log界面什么都不显示其他选项界面正常通过命令查询git日志正常 预期效果解决办法1. 检查 IDEA 的 Git 设置2. 刷新 Git Log (什么都没有大概率是刷新不了)3. 检查分支和日志是否存在4. 清理 IDEA 缓存 (我用这个成功解决)✅…

埃安UT正式入局纯电小车之争,海豚能否守擂成功

文/王俣祺 导语&#xff1a;2025年刚刚来临&#xff0c;第一波车市竞争就开打了&#xff0c;早在去年广州车展就吸睛无数的埃安 UT &#xff0c;日前正式开启预售&#xff0c;被称为比亚迪海豚的“最强对手”&#xff0c;主要是其价格和配置也确实全面对标了 比亚迪海豚。那么&…

java中的日期处理:只显示日期,不显示时间的两种处理方式

需要记录某个操作的操作时间,数据库中该字段为DATE类型; 插入数据的时候,使用数据库函数NOW()获取当前日期并插入: <insert id="batchInsertOrgTestersByProjectId">insert into project_org_testers(project_unid, org_tester_id,franchise_date) value…

Nginx代理同域名前后端分离项目的完整步骤

前后端分离项目&#xff0c;前后端共用一个域名。通过域名后的 url 前缀来区别前后端项目。 以 vue php 项目为例。直接上 server 模块的 nginx 配置。 server{ listen 80; #listen [::]:80 default_server ipv6onlyon; server_name demo.com;#二配置项目域名 index index.ht…

【大数据基础】大数据概述

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈大数据技术原理与应用 ⌋ ⌋ ⌋专栏系统介绍大数据的相关知识&#xff0c;分为大数据基础篇、大数据存储与管理篇、大数据处理与分析篇、大数据应用篇。内容包含大数据概述、大数据处理架构Hadoop、分布式文件系统HDFS、分布式数…

解决Qt打印中文字符出现乱码

在 Windows 平台上&#xff0c;默认的控制台编码可能不是 UTF-8&#xff0c;这可能会导致中文字符的显示问题。 下面是在 Qt 应用程序中设置中文字体&#xff0c;并确保控制台输出为 UTF-8 编码&#xff1a; 1. Qt 应用程序代码 在 Qt 中&#xff0c;我们可以使用 QApplic…

测试用例颗粒度说明

当我们在编写测试用例时&#xff0c;总是会遇到一个问题&#xff1a;如何确定测试用例的颗粒度&#xff1f;测试用例过于粗糙&#xff0c;可能无法全面覆盖系统的细节&#xff1b;而颗粒度过细&#xff0c;又会导致测试重复、冗余。掌握合适的颗粒度&#xff0c;不仅可以提高测…

【大模型(LLM)面试全解】深度解析 Layer Normalization 的原理、变体及实际应用

系列文章目录 大模型&#xff08;LLMs&#xff09;基础面 01-大模型&#xff08;LLM&#xff09;面试全解&#xff1a;主流架构、训练目标、涌现能力全面解析 02-【大模型&#xff08;LLM&#xff09;面试全解】深度解析 Layer Normalization 的原理、变体及实际应用 大模型&…

VoiceBox:基于文本引导的多语种通用大规模语音生成

VoiceBox:基于文本引导的多语种通用大规模语音生成 Voicebox: Text-Guided Multilingual Universal Speech Generation at Scale Voicebox是由MetaAI发布的一个类似大语言模型的生成式语音模型。它是一种基础模型,可以完成类似大语言模型的功能,可以针对语音数据进行编辑、…

ModuleNotFoundError: No module named ‘setuptools_rust‘ 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

基于 GEE 制作研究区影像覆盖图

目录 1 研究区影像覆盖图案例 2 基于 GEE 制作研究区影像覆盖图完整代码 3 运行结果 在写论文的时候&#xff0c;会有一小节内容专门介绍自己的研究区和使用的影像数据。为了让论文非常漂亮&#xff0c;有时候就需要做出研究区的地理位置图和所用卫星影像覆盖图&#xff0c;…

Mysql--基础篇--多表查询(JOIN,笛卡尔积)

在MySQL中&#xff0c;多表查询&#xff08;也称为联表查询或JOIN操作&#xff09;是数据库操作中非常常见的需求。通过多表查询&#xff0c;你可以从多个表中获取相关数据&#xff0c;并根据一定的条件将它们组合在一起。MySQL支持多种类型的JOIN操作&#xff0c;每种JOIN都有…

ASA-Cluster集群模式

集群设备数量看授权 整体性能小于单个设备累加 广播选举 优先级1-100 选举完成 不抢占 集群状态 master、standby 接口模式 Spanned EtherChannel、IndividualInterface 心跳线 control link。 数据、控制层面 集群脑裂之后 设备需要手动加入集群 连接的三个角色&#xf…

用于 EV 牵引电机的先进冷却技术

电动汽车牵引电机的冷却挑战 热管理的重要性 有效的热管理在电动汽车 &#xff08;EV&#xff09; 设计中至关重要&#xff0c;尤其是在牵引电机方面。这些电机将电能转化为机械运动&#xff0c;对车辆的整体性能和效率至关重要。 管理它们的热量至关重要&#xff0c;不仅可以…

RK3568平台(USB篇)禁用USB端口

一.linux中怎样查看usb的端口号 在USB口插入U盘: [ 198.141319][ T106] usb 3-1.3: new SuperSpeed Gen 1 USB device number 5 using xhci-hcd [ 198.161695][ T106] usb 3-1.3: New USB device found, idVendor=0781, idProduct=5591, bcdDevice= 1.00 [ 198.161721]…

Redis Exporter 安装与配置指南(v1.67.0)

&#x1f680; 1. 下载 Redis Exporter 首先&#xff0c;登录到目标服务器&#xff0c;下载 Redis Exporter v1.67.0 安装包。 wget https://github.com/oliver006/redis_exporter/releases/download/v1.67.0/redis_exporter-v1.67.0.linux-amd64.tar.gz&#x1f4e6; 2. 解压…

Python 的网页自动化工具 DrissionPage 介绍

DrissionPage 介绍 视频教程 不要再学selenium了&#xff0c;DrissionPage更香_哔哩哔哩_bilibili不要再学selenium了&#xff0c;DrissionPage更香DrissionPage官网 https://www.drissionpage.cn/, 视频播放量 56768、弹幕量 12、点赞数 1012、投硬币枚数 503、收藏人数 316…

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务&#xff0c;解决屏幕录制音乐播放等操作不被挂起 一、前言 1.后台是什么&#xff1f; 了解后台任务和长时任务前&#xff0c;我们需要先明白鸿蒙的后台特性&#xff1a;所谓的后台&#xff0c;指的是设备返回主界面、锁屏、…

nvm实现nodejs的版本管理

部分老旧项目需要使用低版本的node&#xff0c;网上很多是无效的&#xff0c;高版本无法直接安装低版本node&#xff0c;但是低版本nodejs可以安装部分高版本node&#xff0c;从而达到升级效果。 NVM安装 nvm是什么 nvm全英文也叫node.js version management&#xff0c;是一个…

基于华为ENSP的OSPF接口网络类型深入浅出(4)

本篇技术博文摘要 &#x1f31f; OSPF的接口在不同网络类型下的工作方式&#xff1b;不同网络类型下的报文通告方式深入浅出hub-spoke架构 引言 &#x1f4d8; 在这个快速发展的技术时代&#xff0c;与时俱进是每个IT人的必修课。我是肾透侧视攻城狮&#xff0c;一名什么都会一…