自己做的网站打不开怎么搞,重庆市建设工程信息官方网站,苏州网站建设caiyiduo,做国外有那些网站前言
本文最初和第一代ChatGLM-6B的内容汇总在一块#xff0c;但为了阐述清楚FlashAttention、Multi-Query Attention等相关的原理#xff0c;以及GLM2的微调、源码解读等内容#xff0c;导致之前那篇文章越写越长#xff0c;故特把ChatGLM2相关的内容独立抽取出来成本文 …前言
本文最初和第一代ChatGLM-6B的内容汇总在一块但为了阐述清楚FlashAttention、Multi-Query Attention等相关的原理以及GLM2的微调、源码解读等内容导致之前那篇文章越写越长故特把ChatGLM2相关的内容独立抽取出来成本文 第一部分 相比第一代的改进点FlashAttention与Multi-Query Attention
ChatGLM2-6B(GitHub项目地址、HuggingFace地址)是开源中英双语对话模型 ChatGLM-6B 的第二代版本相比第一代第二点引入了如下新特性
数据集上 经过了 1.4T 中英标识符的预训练与人类偏好对齐训练更长的上下文 基于 FlashAttention 技术将基座模型的上下文长度(Context Length)由 ChatGLM-6B 的 2K 扩展到了 32K并在对话阶段使用 8K 的上下文长度训练允许更多轮次的对话 (当前版本的 ChatGLM2-6B 对单轮超长文档的理解能力有限会在后续迭代升级中着重进行优化)更高效的推理 基于 Multi-Query Attention 技术ChatGLM2-6B 有更高效的推理速度和更低的显存占用在官方的模型实现下推理速度相比初代提升了 42%INT4 量化下6G 显存支持的对话长度由 1K 提升到了 8K模型架构上变成了decoder only的架构 chatglm还是encoder架构但是到了chatglm2 变成了decoder only的架构(这点很少有资料会提及到)何以见得呢 如七月黄老师所说chatglm2仓库的modeling用了新版pytorch的这个函数context_layer context_layer 这个函数实现了attention机制的计算入参 is_causalTrue 表示遮后看前的mask(这种类型的注意力通常用在transformer的decoder部分以确保当前位置只能关注到之前的位置俗称“看不见未来”从而使模型可以进行自回归预测 ) 允许商业使用准确性不足 尽管模型在训练的各个阶段都尽力确保数据的合规性和准确性但由于 ChatGLM2-6B 模型规模较小且模型受概率随机性因素影响无法保证输出内容的准确性且模型易被误导
第二部分 FlashAttention减少内存访问提升计算速度——更长上下文的关键
FlashAttention是斯坦福联合纽约州立大学在22年6月份提出的一种具有 IO 感知且兼具快速、内存高效的新型注意力算法「对应论文为FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness这是其GitHub地址这是其解读之一该解读也是本第二部分的重要参考」
2.1 FlashAttention相关的背景知识
2.1.1 transformer模型的计算复杂度和空间复杂度
它要解决一个什么样的问题呢
首先GPT3、LLaMA、ChatGLM、BLOOM等大语言模型输入输出的最大序列长度只有2048或4096扩展到更长序列的难度在哪里呢本质原因是transformer模型的计算复杂度和空间复杂度都是 的其中为序列长度 具体地当输入批次大小为 序列长度为 时 层transformer模型的计算量为 是隐藏层维度通常等于词向量维度可能不少同学都会疑问这个计算量是怎么一步一步计算得来的下面详细拆解下这个计算过程
首先我们知道transformer模型由 个相同的层组成每个层分为两部分self-attention块和MLP块self-attention块的模型参数有两部分一部分是、、的权重矩阵、、和偏置另一部分是输出权重矩阵和偏置 故第一步就是计算、、 即 该矩阵乘法的输入和输出形状为 计算量为计算 该部分的输入和输出形状为 计算量为计算在上的加权 该部分矩阵乘法的输入和输出形状为 计算量为 attention后的线性映射矩阵乘法的输入和输出形状为计算量为 最终自注意力层的输出结果为接下来计算MLP层MLP块由2个线性层组成一般地第一个线性层是第二个线性层再将维度从 映射到 第一个线性层的权重矩阵 的形状为 相当于先将维度从 映射到矩阵乘法的输入和输出形状为计算量为 第二个线性层的权重矩阵 的形状为 相当于再将维度从 映射到 矩阵乘法的输入和输出形状为计算量为 将上述所有表粗所示的计算量相加得到每个transformer层的计算量大约为此外另一个计算量的大头是logits的计算(毕竟词嵌入矩阵的参数量也较多)将隐藏向量映射为词表大小说白了词向量维度通常等于隐藏层维度 词嵌入矩阵的参数量为最后的输出层的权重矩阵通常与词嵌入矩阵是参数共享的「解释一下如七月杜老师所说这个是transformer中一个重要的点参数共享可以减小参数量词嵌入矩阵是[vocab_sizehidden_size]输出层矩阵是 [hidden_sizevocab_size]是可以共享的」 其矩阵乘法的输入和输出形状为计算量为 因此对于一个 层的transformer模型输入数据形状为 的情况下一次训练迭代的计算量为
中间激活的显存大小为 其中 为注意力头数「至于这个结果的具体推导过程见此文《分析transformer模型的参数量、计算量、中间激活、KV cache》」 可以看到transformer模型的计算量和储存复杂度随着序列长度 呈二次方增长。这限制了大语言模型的最大序列长度 的大小
其次GPT4将最大序列长度 扩大到了32KClaude更是将最大序列长度 扩大到了100K这些工作一定采用了一些优化方法来降低原生transformer的复杂度那具体怎么优化呢 我们知道每个transformer层分为两部分self-attention块和MLP块。上面计算量中的 项和中间激活中的 项都是self-attention块产生的与MLP块无关
如此FlashAttention提出了一种加速计算、节省显存和IO感知的精确注意力可以有效地缓解上述问题
Meta推出的开源大模型LLaMA阿联酋推出的开源大模型Falcon都使用了Flash Attention来加速计算和节省显存。目前Flash Attention已经集成到了pytorch2.0中另外triton、xformer等开源框架也进行了整合实现
2.1.2 分析GPU的内存分析图计算的瓶颈是显存访问
通过上文可知transformer的核心组件self-attention块的计算复杂度和空间复杂度是序列长度 的二次方
对于self-attention块除了大矩阵乘法是计算受限的其他操作(计算softmax、dropout、mask都是内存受限的。 尽管已经有许多近似注意力的方法尝试减少attention的计算和内存要求。例如稀疏近似和低秩近似的方法将计算复杂度降低到了序列长度的线性或亚线性但这些近似注意力方法方法并没有得到广泛应用。因为这些方法过于关注FLOPs(浮点数计算次数)的减少而忽略了IO读写的内存访问开销导致这并没有效减少运行时间(wall-clock time) 总之在现代GPU中计算速度已经远超过了显存访问速度transformer中的大部分计算操作的瓶颈是显存访问。对于显存受限的操作IO感知是非常重要的因为显存读写占用了大部分的运行时间而Flash Attention则是IO感知的通过减少内存访问来计算精确注意力从而减少运行时间实现计算加速
GPU的内存由多个不同大小和不同读写速度的内存组成。内存越小读写速度越快。对于A100-40GB来说内存分级图如下所示 SRAM内存分布在108个流式多处理器上每个处理器的大小为192K。合计为 高带宽内存HBMHigh Bandwidth Memory也就是我们常说的显存大小为40GB。SRAM的读写速度为19TB/s而HBM的读写速度只有1.5TB/s不到SRAM的1/10
所以上面讲到计算注意力的主要瓶颈是显存访问因此减少对HBM的读写次数有效利用更高速的SRAM来进行计算是非常重要的而GPU有大量的线程来执行某个操作称为kernel。GPU执行操作的典型方式分为三步
每个kernel将输入数据从低速的HBM中加载到高速的SRAM中在SRAM中进行计算计算完毕后将计算结果从SRAM中写入到HBM中
而对于性能受限于内存带宽的操作进行加速的常用方式就是kernel融合。kernel融合的基本思想是避免反复执行“从HBM中读取输入数据SRAM执行计算最后将计算结果写入到HBM中”将多个操作融合成一个操作减少读写HBM的次数(需要注意的是模型训练通常会影响到算子融合的效果因为为了后向传递计算梯度通常需要将某些中间结果写入到HBM中)
2.1.3 safe softmax与Standard Attention
继续行文之前先补充两个背景知识一个是safe softmax一个是Standard Attention
对于第一个背景知识safe softmax而言
考虑到向量 原生softmax的计算过程如下在实际硬件中浮点数表示的范围是有限的 对于float32和bfloat16来说当 时就会变成inf发生数据上溢的问题 故为了避免发生数值溢出的问题保证数值稳定性计算时通常会“减去最大值”称为“safe softmax” 即现在所有的深度学习框架中都采用了“safe softmax”这种计算方式在训练语言模型时通常会采用交叉熵损失函数。交叉熵损失函数等价于先执行log_softmax函数再计算负对数似然函数 且在计算log_softmax时同样会执行“减去最大值”这不仅可以避免数值溢出提高数值稳定性还可以加快计算速度
对于第二个背景知识Standard Attention
首先transformer中注意力机制的计算过程为 其中 其中 是序列长度 是每个注意力头的维度输出可以记为 上面的式子可以拆解为 在标准注意力实现中 都要写回到HBM中占用了 的内存通常 例如对于GPT2 对于GPT3 总之注意力矩阵 需要的内存 远大于 所需要的内存 相当于self-attention中大部分操作都是内存受限的逐点运算例如对 的mask操作、 的softmax操作、对 的dropout操作这些逐点操作的性能是受限于内存带宽的会减慢运行时间下图展示了标准注意力的实现过程 标准注意力实现存在两个问题 1. 显存占用多过程中由于实例化了完整的注意力矩阵 导致了 的内存要求 2. HBM读写次数多减慢了运行时间(wall- clock time)
// 待更..
第三部分 多查询注意力(Muti Query Attention)各自Query矩阵但共享Key 和 Value 矩阵
多查询注意力(Muti Query Attention)是 19 年Google一研究者提出的一种新的 Attention 机制(对应论文为Fast Transformer Decoding: One Write-Head is All You Need、这是其解读之一)其能够在保证模型效果的同时加快 decoder 生成 token 的速度
那其与17年 Google提出的transformer中多头注意力机制(简称MHA)有啥本质区别呢有意思的是区别在于 我们知道MHA的每个头都各自有一份不同的Key、Query、Value矩阵而MQA 让所有的头之间 共享 同一份 Key 和 Value 矩阵每个头只单独保留了一份 Query 参数从而大大减少 Key 和 Value 矩阵的参数量 总之MQA 实际上是将 head 中的 key 和 value 矩阵抽出来单独存为一份共享参数而 query 则是依旧保留在原来的 head 中每个 head 有一份自己独有的 query 参数
下图对比了多头注意力(Multi-Head Attention)、LLaMA2中分组查询注意力(Grouped-Query Attention)、多查询注意力(Muti Query Attention)的差别 总之MHA 和 MQA 之间的区别只在于建立 Wqkv Layer 上 # Multi Head Attention
self.Wqkv nn.Linear( # 【关键】Multi-Head Attention 的创建方法self.d_model, 3 * self.d_model, # 有 query, key, value 3 个矩阵, 所以是 3 * d_modeldevicedevice
)query, key, value qkv.chunk( # 【关键】每个 tensor 都是 (1, 512, 768)3, dim2
)# Multi Query Attention
self.Wqkv nn.Linear( # 【关键】Multi-Query Attention 的创建方法d_model,d_model 2 * self.head_dim, # 只创建 query 的 head 向量所以只有 1 个 d_modeldevicedevice, # 而 key 和 value 不再具备单独的头向量
)query, key, value qkv.split( # query - (1, 512, 768)[self.d_model, self.head_dim, self.head_dim], # key - (1, 512, 96)dim2 # value - (1, 512, 96)
) 对比上面的代码你可以发现
在 MHA 中query, key, value 每个向量均有 768 维度而在 MQA 中只有 query 是 768 维而 key 和 value 均只剩下 96 维了恰好是 1 个 head_dim 的维度
因此可以确认在 MQA 中除了 query 向量还保存着 8 个头key 和 value 向量都只剩 1 个「公共头」了这也正好印证了论文中所说的「所有 head 之间共享一份 key 和 value 的参数」
剩下的问题就是如何将这 1 份参数同时让 8 个头都使用代码里使用矩阵乘法 matmul 来广播使得每个头都乘以这同一个 tensor以此来实现参数共享 def scaled_multihead_dot_product_attention(query,key,value,n_heads,multiqueryFalse,):q rearrange(query, b s (h d) - b h s d, hn_heads) # (1, 512, 768) - (1, 8, 512, 96)kv_n_heads 1 if multiquery else n_headsk rearrange(key, b s (h d) - b h d s, hkv_n_heads) # (1, 512, 768) - (1, 8, 96, 512) if not multiquery # (1, 512, 96) - (1, 1, 96, 512) if multiqueryv rearrange(value, b s (h d) - b h s d, hkv_n_heads) # (1, 512, 768) - (1, 8, 512, 96) if not multiquery # (1, 512, 96) - (1, 1, 512, 96) if multiqueryattn_weight q.matmul(k) * softmax_scale # (1, 8, 512, 512)attn_weight torch.softmax(attn_weight, dim-1) # (1, 8, 512, 512)out attn_weight.matmul(v) # (1, 8, 512, 512) * (1, 1, 512, 96) (1, 8, 512, 96)out rearrange(out, b h s d - b s (h d)) # (1, 512, 768)return out, attn_weight, past_key_value 第四部分 模型的使用/部署、微调
4.1 模型的使用/部署
首先需要下载本仓库 git clone https://github.com/THUDM/ChatGLM2-6B
cd ChatGLM2-6B 然后使用 pip 安装依赖 pip install -r requirements.txt其中 transformers 库版本推荐为 4.30.2torch 推荐使用 2.0 及以上的版本以获得最佳的推理性能代码调用 可以通过如下代码调用 ChatGLM2-6B 模型来生成对话 from transformers import AutoTokenizer, AutoModeltokenizer AutoTokenizer.from_pretrained(THUDM/chatglm2-6b, trust_remote_codeTrue)model AutoModel.from_pretrained(THUDM/chatglm2-6b, trust_remote_codeTrue, devicecuda)model model.eval()response, history model.chat(tokenizer, 你好, history[])print(response) 你好!我是人工智能助手 ChatGLM2-6B,很高兴见到你,欢迎问我任何问题。 response, history model.chat(tokenizer, 晚上睡不着应该怎么办, historyhistory)print(response) 晚上睡不着可能会让你感到焦虑或不舒服,但以下是一些可以帮助你入睡的方法: 1. 制定规律的睡眠时间表:保持规律的睡眠时间表可以帮助你建立健康的睡眠习惯,使你更容易入睡。尽量在每天的相同时间上床,并在同一时间起床。 2. 创造一个舒适的睡眠环境:确保睡眠环境舒适,安静,黑暗且温度适宜。可以使用舒适的床上用品,并保持房间通风。 3. 放松身心:在睡前做些放松的活动,例如泡个热水澡,听些轻柔的音乐,阅读一些有趣的书籍等,有助于缓解紧张和焦虑,使你更容易入睡。 4. 避免饮用含有咖啡因的饮料:咖啡因是一种刺激性物质,会影响你的睡眠质量。尽量避免在睡前饮用含有咖啡因的饮料,例如咖啡,茶和可乐。 5. 避免在床上做与睡眠无关的事情:在床上做些与睡眠无关的事情,例如看电影,玩游戏或工作等,可能会干扰你的睡眠。 6. 尝试呼吸技巧:深呼吸是一种放松技巧,可以帮助你缓解紧张和焦虑,使你更容易入睡。试着慢慢吸气,保持几秒钟,然后缓慢呼气。 如果这些方法无法帮助你入睡,你可以考虑咨询医生或睡眠专家,寻求进一步的建议 从本地加载模型 以上代码会由 transformers 自动下载模型实现和参数 完整的模型实现在 Hugging Face Hub。如果你的网络环境较差下载模型参数可能会花费较长时间甚至失败。此时可以先将模型下载到本地然后从本地加载。 从 Hugging Face Hub 下载模型需要先安装Git LFS然后运行 git clone https://huggingface.co/THUDM/chatglm2-6b 如果你从 Hugging Face Hub 上下载 checkpoint 的速度较慢可以只下载模型实现 GIT_LFS_SKIP_SMUDGE1 git clone https://huggingface.co/THUDM/chatglm2-6b 然后从这里手动下载模型参数文件并将下载的文件替换到本地的 chatglm2-6b 目录下 将模型下载到本地之后将以上代码中的 THUDM/chatglm2-6b 替换为你本地的 chatglm2-6b 文件夹的路径即可从本地加载模型。 模型的实现仍然处在变动中。如果希望固定使用的模型实现以保证兼容性可以在 from_pretrained 的调用中增加 revisionv1.0 参数。v1.0 是当前最新的版本号完整的版本列表参见 Change Log 最后可以通过以下命令启动基于 Gradio 的网页版 demo python web_demo.py 4.2 基于 P-Tuning v2 的微调(官方)
P-Tuning v2 将需要微调的参数量减少到原来的 0.1%再通过模型量化、Gradient Checkpoint 等方法最低只需要 7GB 显存即可运行(当然我司杜老师也会在七月类ChatGPT微调实战课上录一个ChatGLM2-6B的微调视频)
环境配置 在原chatglm-6b的环境中安装以下依赖 pip install rouge_chinese nltk jieba datasets微调数据准备 ADGEN 数据集任务为根据输入content生成一段广告词summary { “content”: “类型#上衣版型#宽松版型#显瘦图案#线条衣样式#衬衫衣袖型#泡泡袖衣款式#抽绳”, “summary”:
“这件衬衫的款式非常的宽松利落的线条可以很好的隐藏身材上的小缺点穿在身上有着很好的显瘦效果。领口装饰了一个可爱的抽绳漂亮的绳结展现出了十足的个性配合时尚的泡泡袖型尽显女性甜美可爱的气息。”
} 从 Google Drive 或者 Tsinghua Cloud 下载处理好的 ADGEN 数据集将解压后的 AdvertiseGen 目录放到本 ptuning 目录下即可微调 修改train.sh文件 去掉最后的 --quantization_bit 4 去掉后为FP16 精度加载 修改模型路径THUDM/chatglm-6b修改为/data/sim_chatgpt/chatglm2-6b 目前专业级GPU Tesla P100也不支持INT4或8量化 执行train.sh文件 bash train.sh 如遇报错 wandb.errors.UsageError: api_key not configured (no-tty). call wandb.login(k… 解决方法 在main.py文件中加入下面两行禁用wandb即可import os os.environ[WANDB_DISABLED] true 其中train.sh 中的 PRE_SEQ_LEN 和 LR 分别是 soft prompt 长度和训练的学习率可以进行调节以取得最佳的效果。
微调过程显存使用情况如下 微调完成后在./output/adgen-chatglm2-6b-pt-128-2e-2 下回生成微调好的模型文件。
我们可以对比下微调前后的效果 以命令行 Demo为例只需修改ptuning路径下web_demo.sh中的模型路径为/data/sim_chatgpt/chatglm2-6b运行 web_demo.py即可 bash web_demo.sh Input: 类型#上衣材质#牛仔布颜色#白色风格#简约图案#刺绣衣样式#外套衣款式#破洞 Label: 简约而不简单的牛仔外套,白色的衣身十分百搭。衣身多处有做旧破洞设计,打破单调乏味,增加一丝造型看点。衣身后背处有趣味刺绣装饰,丰富层次感,彰显别样时尚。
Output[微调前] Output[微调后] // 待更