当前位置: 首页 > news >正文

从零构建大模型 Build a large language model from scratch by Sebastian Raschka 阅读笔记

在这里插入图片描述
Build a large language model from scratch by Sebastian Raschka
本书介绍了大模型三个阶段:准备数据、预训练、微调(分类、指令)适合有一些深度学习基础的人快速了解所谓的大语言模型是什么,要做什么事情,能做什么事情。同时提供了基于pytorch的代码,帮助新手理解和构建基础大模型的结构并训练。

第二章 处理文本数据

embeddings 嵌入是从离散对象(如词语、图像或整篇文档)到连续向量空间中的点的映射——嵌入的主要目的是将非数值数据转换为神经网络可以处理的格式
生成词嵌入目的:word2vec 比如GPT-2是768维,GPT-3是12288维

  • 文本预处理,生成干净的单位和符号序列,对应成数字(tokenize)。文本序列变成了数字序列 text = ( "Hello, do
    you like tea? " “In the sunlit terraces” “of someunknownPlace.” )
    [15496, 11, 466, 345, 588, 8887, 30, 220, 50256, 554, 262, 4252,
    18250, 8812, 2114, 286, 617, 34680, 27271, 13]
  • 训练目标与样本:预测下一个词。所以要构造输入-目标对。利用滑动窗口获取。对一段文本的数字序列,长以下这个样子
    文本序列:x: [290, 4920, 2241, 287] y: [4920, 2241, 287, 257] 形成tensor。
    输入-训练目标对:
    [290] ----> 4920
    [290, 4920] —> 2241
    [290, 4920, 2241] ----> 287
    [290, 4920, 2241, 287] ----> 257
    具体实现,使用pytorch中的dataloader实现。上例子max length=4,batch_size=1 步长stride=1的意思是滑动窗口的大小,下一个批次只向右挪动一位。较小的批量大小在训练期间需要更少的内 存,但会导致模型更新更加不稳定。
  • 创建词嵌入:token ID -> embedding
    嵌入的本质是可学习的查找表。嵌入层权重通过反向传播优化,使语义相似的词在向量空间中靠近。将离散的数据(单词/图像)转换为连续的空间向量。
  • 嵌入层是一个权重矩阵 E∈RV×D,其中: V 为词表大小(如 GPT-3 的 50,257); D 为嵌入维度(如 GPT-3 的
    12,288)GPT-2更小模型是768维。 嵌入层仅编码词义,​​不包含位置信息​​。需额外添加位置编码(如绝对/相对位置编码)
    预训练嵌入(如 Word2Vec):可加速收敛,但可能限制任务适配性

第三章 attention机制

在Transformer出现前,使用编码器-解码器结构的RNN作为机器翻译的模型,编码器部分将整个输入文本处理成隐藏状态(记忆单元)。然后解码器接收这 个隐藏状态以生成输出。

  • 自注意力机制:允许输入序列的每个位置在计算序列表示时考虑同一序列中所有其他位置的相关性或注意。
    自的意思是内部的注意力机制,可以建立全局的依赖关系,扩大图像的感受野,获取更多的上下文信息。传统的Attention是基于source端和target端的隐变量(hidden
    state)计算Attention的,得到的结果是源端(source端)的每个词与目标端(target端)每个词之间的依赖关系。但Self
    -Attention不同,它首先分别在source端和target端进行自身的attention,仅与source input或者target input自身相关的Self
    -Attention,以捕捉source端或target端自身的词与词之间的依赖关系;然后再把source端的得到的self -Attention加入到target端得到的Attention中,称作为Cross-Attention,以捕捉source端和target端词与词之间的依赖关系。
  • 具体实现:计算每个元素的注意力权重,和上下文向量。
    首先计算注意权重,对一个长度为T的序列,某个元素与序列中所有元素通过点积再归一化后计算相似度,得到注意力权重,表示该元素与序列中所有元素的相关程度。
    序列:Your journey starts with one step.
    以第2个输入元素journey为例,它的与自己journey和start相关度都很高。attention weight :
    tensor([0.1385, 0.2379, 0.2333, 0.1240, 0.1082, 0.1581]) 和为1。
    对第2个元素journey来说,通过将每一个输入与对应的注意力权重相乘,再相加,即加权和得到上下文向量。
    一个长度为6的序列,每个元素计算与序列中所有元素的注意力权重,得到6x6的二维张量,归一化后每行和为1。第k行表示第k个元素的注意力权重。
  • 实现可训练权重的自注意力机制:目的是生成“良好”的上下文向量。 三个可训练权重矩阵Q K
    V,分别是查询query,键key,值value。随机初始化,维度是元素维度d_in x
    输出维度d_out。比如每个单词journey的embedding是3维,希望新向量维度是2维,那么qkv就是3x2。一般情况下比词向量的维度小,比如词向量是512维度,新向量是64维。
  • 计算注意力score:当前单词的查询向量q与其他各个单词的键向量k做内积,除以8,通过softmax归一化得到注意力权重矩阵。
    得到上下文向量:权重矩阵与每个元素的值向量v相乘,加权和得到最终的上下文向量z。 softmax(Q x K/8)*V = z
  • 模型实际中大小:每个词向量维度是512,希望得到的嵌入维度是64。那么Q K V都是512x64大小的权重矩阵。
    一个语句长度为L,包含L个词。每个词的词向量xQ得到每个词的query向量,维度是64。同理xK得到key向量,维度是64。
    计算attention矩阵,attention表示各个词与其他词的注意力强度,每个词的query向量与其他词的key向量做点积,1x64
    点积 64x1 得到1x1的值。为了保证数据不膨胀要做归一化,除以根号64,即除以8,得到该词与另一个词的注意力。
    这样注意力矩阵是LXL。 所有词的词向量xV得到长度为64的value向量。
    某个词,它对应的attention矩阵那一行表示它与其他词的注意力关系。把整个句子的所有词的Value向量,通过该行的attention值加权求和,得到的就是某个词的最终value,仍然是64维向量。(实际中会屏蔽对角线以上权重。比如第2个词,只看它与前2个词的attention关系,后面看不到)
    attention矩阵的计算
    上图,单词的嵌入是3维,序列长度是6,最终输出向量是2,那么qkv矩阵就是3x2。 比起上节不可训练的注意力,计算词与词本身,这里首先要把词本身与q相乘得到query向量作为词的代表,与k相乘得到key向量作为计算权重的参考,与v相乘作为计算最终上下文向量的参考。 attention权重是6×6的,每个元素的query向量与其他元素的key相乘归一化后得到,再和每个value向量相乘得到最终的输出Z。QKV矩阵是可学习的权重。
def forward(self, x):
keys = self.W_key(x)
queries = self.W_query(x)
values = self.W_value(x)
attn_scores = queries @ keys.T //得到注意力权重
attn_weights = torch.softmax( attn_scores / keys.shape[-1]**0.5, dim=-1 ) //归一化
context_vec = attn_weights @ values //得到上下文向量
return context_vec
  • 模型改进:因果注意力机制、多头注意力
    在因果注意力中,我们屏蔽对角线以上的注意力权重,这样对于给定输入,LLM在使用注意力权重计算上下文向量时无法访问未来的标记。例如,对于第二行中的单词“journey”,我们只保留之前单词(“Your”)和当前位置(“journey)的注意权重。
    实现:使用pytorch的tril创建掩码,其中对角线以上值为0,相乘即可将注意力权重矩阵对角线上的值置0。重新归一化使每行和为0。
    在Transformer架构中,包括像GPT这样的模型,注意力机制中的dropout通常在两个特定的时间点应用:在计算注意力权重之后或在将注意力权重应用于值向量之后。
    多头注意力:
    将单头注意力模块堆叠在一起,多个QKV矩阵,得到多个上下文向量z,将它们组合成一个单独的上下文向量矩阵z。上例中,两个注意力头得到两个维度2的向量z,
    合并成长度为4的向量。

这个视频很好:https://www.bilibili.com/video/BV1TZ421j7Ke/?spm_id_from=333.1007.top_right_bar_window_history.content.click

第四章 从头实现一个GPT模型以生成文本

以GPT-2为例学习,大概1.5B参数
GPT_CONFIG_124M = { “vocab_size”: 50257, # 词汇表大小 “context_length”: 1024, # 上下文长度 “emb_dim”: 768, # 嵌入维度 “n_heads”: 12, # 注意力头数 “n_layers”: 12, # 层数 “drop_rate”: 0.1, # Dropout率 “qkv_bias”: False # Query-Key-Value偏置 }
词汇表里一共有五万的单词,最长能接受的输入序列是1024个token,把每个词tokenize并且嵌入后的维度是768维。多头注意力机制是12头。
模型输入是token embeddings,模型输出是768维的output vector,也要通过后处理变成单词。在LLM中,嵌入输入标记的维度通常与输出维度匹配。这里的输出嵌入表示上下文向量。
输入形状:[2, 10](2个句子,每句10个token)
输出Logits形状:[2, 10, 50257]
使用层归一化归一化激活
训练具有许多层的深度神经网络有时会面临诸如梯度消失或梯度爆炸等问题,这 使得训练变得不稳定,并且使网络难以有效地调整其权重。这意味着学习过程很 难找到一组能使损失函数最小化的参数(权重)。层归一化的主要思想是调整神经网络层的激活(输出),使其均值为0,方差为1,也称为单位方
差。这种调整可以加快收敛到有效的权重,并确保一致且可靠的训练。

  • GELU激活函数 GELU 的平滑性可以在训练期间带来更好的优化特性,因为它允许对模型参数进 行更细致的调整。相比之下,ReLU
    在零处有一个尖角(图 4.18,右),这有时会 使优化更加困难,尤其是在非常深或具有复杂架构的网络中。此外,与任何负输 入都输出零的
    ReLU 不同,GELU 允许对负值产生小的非零输出。这一特性意味 着在训练过程中,接收负输入的神经元仍然可以对学习过程做出贡献,尽管程度
    不如正输入。
  • 残差连接:解决梯度消失问题,梯度在反向传播中逐渐变小,难以有效训练前面的层。目的:使得梯度可以通过网络流动。这是通过将一层的输出添加到后面一
    层的输出来实现的。 每一层 x = x + layer_output 而不是x = layer_output
  • GPT-2 small的参数量

在这里插入图片描述
attention层的QKV矩阵是(768768 + 768)×3
两层全连接层,维度变化:输入 768 到 768
4维 到 768维输出
GPT-2 架构在其输出层中重用了标记嵌入层的权重,去掉词表映射的38M就是124M。
12头注意力机制是将QKV矩阵拆分,不新增参数。张量拆分​​:将 Q,K,V 拆分为12份(形状 [batch, 12, seq_len, 64]),​​仅改变张量形状,不新增参数

假设模型配置:
隐藏层维度 dmodel=768
注意力头数 h=12
单头维度 dk=dv=dmodel/h=64
​​Q/K/V投影层​​:
WQ,WK,WV 的参数量 = 3×(dmodel×dk)=3×(768×64)=147,456
(不乘以12,因为权重共享)
​​输出投影层​​:
WO 的参数量 = dmodel×dmodel=768×768=589,824

  • 本章要点:层归一化通过确保每一层的输出具有一致的均值和方差来稳定训练。
    快捷连接是通过将一层的输出直接馈送到更深的一层而跳过一个或多个层的连 接,有助于缓解训练深度神经网络(如LLM)时的梯度消失问题。
    Transformer块是GPT模型的核心结构组件,结合了掩码多头注意力模块和使用 GELU激活函数的全连接前馈网络。
    GPT模型是具有数百万到数十亿参数的LLM,由许多重复的Transformer块组成。
    GPT模型有不同的规模,例如124、345、762和1,542百万参数,我们可以使用相同 的GPTModel Python类实现它们。
    GPT类LLM的文本生成能力涉及基于给定输入上下文逐个预测标记,并将输出张 量解码为人类可读的文本。
    未经训练的GPT模型生成不连贯的文本,这突显了模型训练对于连贯文本生成的 重要性。

第五章 在未标注数据上预训练

code参考 llms-from-scratch-cn/Codes/ch05/01_main-chapter-code/ch05.ipynb at main · datawhalechina/llms-from-scratch-cn · GitHub

准备:模型前后需要text_to_token_ids和token_ids_to_text转换。
样本输入与教师: 教师是输入向右移动一个位置的结果。inputs = torch.tensor([ [16833, 3626, 6100], # [“every effort moves”] [40, 1107, 588] # [“I really like”] ]) 与这些输入相匹配的目标包含我们希望模型生成的标记ID: targets = torch.tensor([ [3626, 6100, 345], # [" effort moves you"] [1107, 588, 11311] # [" really like chocolate"] ])
损失函数:交叉熵。目标是增加正确对应标记的索引位置的softmax概率。 输出的一个样本的维度是token长度x词典数量50627
训练:Adam优化器是训练深度神经网络的流行选择。然而,在我们的训练循环中,我们选择了AdamW优化器。AdamW是Adam的一个变体,改进了权重衰减方法,旨在通过惩罚较大的权重来最小化模型复杂度并防止过拟合。这种调整使AdamW能够实现更有效的正则化和更好的泛化;因此AdamW经常用于LLM的训练。
温度缩放与topK:温度缩放只是将logits除以一个大于0的数,scaled_logits = logits / temperature 大于1的温度会导致更均匀分布的标记概率,而小于1的温度会导致更自信(更尖 锐或更高峰)的分布。
应用非常小的温度(如0.1)会导致更尖锐的分布,使得 multinomial函数几乎100%选择最可能的标记,接近argmax 函数的行为。同样,温度为5会导致更均匀的分布,其中其他标记更常被选择。这可以为生成的文本添加更多的多样性,但也更常导致无意义的文本。
top-k 方法将所有未选中的 logit 值替换为负无穷大值(-inf),使得在计算 softmax 值时,非 top-k token 的概率分数为 0,剩余的概率总和为 1。K=3表示会选3个最高的token中的一个以生成下一个token。
pytorch的加载和保存模型权重

torch.save torch.load
checkpoint = torch.load("model_and_optimizer.pth", map_location=device) 
model = GPTModel(GPT_CONFIG_124M) 
model.load_state_dict(checkpoint["model_state_dict"]) 
optimizer = torch.optim.AdamW(model.parameters(), lr=5e-4, weight_decay=0.1) optimizer.load_state_dict(checkpoint["optimizer_state_dict"]) model.train(); 
openAI的chatGPT-2模型大小: 多头注意力模块,计算attention score的缩放都是除以8。 这里emb_dim/n_heads都是64。
model_configs = { "gpt2-small (124M)": {"emb_dim": 768, "n_layers": 12, "n_heads": 12}"gpt2-medium (355M)": {"emb_dim": 1024, "n_layers": 24, "n_heads": 16},
"gpt2-large (774M)": {"emb_dim": 1280, "n_layers": 36, "n_heads": 20},
"gpt2-xl (1558M)": {"emb_dim": 1600, "n_layers": 48, "n_heads": 25},}

第六章 分类微调

应用例如垃圾邮件分类。
数据准备:邮件长度不一致,用[pad]填充。
修改模型,原始输出层是从768维到50257维的映射,现在修改为768到2分类的映射。除了输出层,将最终的layer-norm和最后一个transformer块设置为可训练,其余11个transformer和嵌入层保持不可训练。

for param in model.trf_blocks[-1].parameters():
param.requires_grad = True
for param in model.final_norm.parameters():
param.requires_grad = True
GPTModel((tok_emb): Embedding(50257, 768)(pos_emb): Embedding(1024, 768)(drop_emb): Dropout(p=0.0, inplace=False)(trf_blocks): Sequential((11): TransformerBlock((att): MultiHeadAttention((W_query): Linear(in_features=768, out_features=768, bias=True)(W_key): Linear(in_features=768, out_features=768, bias=True)(W_value): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True)(dropout): Dropout(p=0.0, inplace=False))(ff): FeedForward((layers): Sequential((0): Linear(in_features=768, out_features=3072, bias=True)(1): GELU()(2): Linear(in_features=3072, out_features=768, bias=True)))(norm1): LayerNorm()(norm2): LayerNorm()(drop_resid): Dropout(p=0.0, inplace=False) ) )(final_norm): LayerNorm()(out_head): Linear(in_features=768, out_features=50257, bias=False) )//调成2以做二分类

第七章 指令微调

数据集准备:
Alpaca格式:指令+输入+相应,比如:指令: 识别下列单词的正确拼写。
输入: Ocassion
响应: 正确拼写是 ‘Occasion’
Phi-3格式 user + assistant
需要自定义collate函数,集成到pytorch的dataloader中。【pad】允许不同批次有不同的长度,用50256表示。目标用token -100表示结束,所有的pad的占位符都用-100代替。
示例:
输入tensor([[ 0, 1, 2, 3, 4], [ 5, 6, 50256, 50256, 50256], [ 7, 8, 9, 50256, 50256]])
输出tensor([[ 1, 2, 3, 4, 50256], [ 6, 50256, -100, -100, -100], [ 8, 9, 50256, -100, -100]])
​特殊 token 插入​
添加开始/结束符(如 [CLS]、[EOS]);
角色标识符(如 [Human]、[Assistant])。
输入 "Human: 解释为什么以下分数等同于1/4\n输入:4/16\n\nAssistant: "
输出label的指令部分用-100可以屏蔽指令部分,原因:不屏蔽指令有利于LLM性能?
-100 使损失函数跳过 Human 文本的预测; 交叉熵的默认设置是ignore_index=-100。
模型只学习生成 Assistant 的回复内容。

http://www.dtcms.com/a/482699.html

相关文章:

  • 基于Chainlit和Llamalndex的智能RAG聊天机器人实现详解
  • 18.5 GLM-4大模型私有化部署实战:3秒响应+显存降低40%优化全攻略
  • Prisma 命令安全指南
  • Linux系统下文件操作系统调用详解
  • 网站备案后需要年检吗官方网站搭建
  • 515ppt网站建设北京朝阳区属于几环
  • 5~20.数学基础
  • HTML应用指南:利用POST请求获取全国鸿蒙智行门店位置信息
  • 优先级队列(堆)-295.数据流的中位数-力扣(LeetCode)
  • 大语言模型推理本质与技术演进
  • 福田区网站建最牛视频网站建设
  • 踩坑实录:Go 1.25.x 编译的 exe 在 Windows 提示“此应用无法运行”
  • 学习网站建设有前景没wordPress登不上数据库
  • 互联网大厂Java面试:从缓存技术到安全框架的深度探索
  • 本地部署开源集成工具 Jenkins 并实现外网访问( Linux 版本)
  • HackerNews 播客生成器
  • 新网站优化品牌营销策略四种类型
  • Linux 命令:umount
  • springboot159基于springboot框架开发的景区民宿预约系统的设计与实现
  • LatchUtils:简化Java异步任务同步的利器
  • 数据库设计基础知识(3)关系运算
  • uniapp 编译支付宝小程序canvas 合成图片实例,支付宝小程序 canvas 渲染图片 可以换成自己的图片即可
  • jmeter环境搭建
  • 专业的免费网站建设网站开发怎么销售
  • 浙江网站建设cms免费无限建站
  • Java Redis “底层结构” 面试清单(含超通俗生活案例与深度理解)
  • Windows10停服!7-Zip被爆组合漏洞|附安全指南
  • 从 0 到 1 搭建完整 Python 语言 Web UI自动化测试学习系列 17--测试框架Pytest基础 1--介绍使用
  • 太原市微网站建设上海网站建设服务电话
  • QT6(鼠标键盘事件)