如何用chatgpt把代码做注释 chat gpt怎么让它输出完整的代码
淘宝搜:【天降红包222】领超级红包,京东搜:【天降红包222】
淘宝互助,淘宝双11微信互助群关注公众号 【淘姐妹】
最近一直在做类ChatGPT项目的部署 微调,关注比较多的是两个:一个LLaMA,一个ChatGLM,会发现有不少模型是基于这两个模型去做微调的,说到微调,那具体怎么微调呢,因此又详细了解了一下微调代码,发现微调LLM时一般都会用到Hugging face实现的Transformers库的Trainer类
从而发现,如果大家想从零复现ChatGPT,便得从实现Transformer开始,因此便开启了本文:如何从零起步实现Transformer、ChatGLM(至于LLaMA已在之前的博客里解读过),主要分为两个大部分
- 按照transformer的每一步的原理逐步逐行从零实现,先编码器后解码器,特别是注意力机制(缩放点积、多头注意力)
- 从头到尾解读ChatGLM-6B的整体代码架构,及逐行解读每一行代码 且本文的代码解读与其他代码解读最大的不同是:会对出现在本文的每一行代码都加以注释、解释、说明,甚至对每行代码中的变量都会做解释/说明
总之,一如既往的保持对初学者的足够友好,让即便没有太多背景知识的也能顺畅理解本文
transformer强大到什么程度呢,基本是17年之后绝大部分有影响力模型的基础架构都基于的transformer(比如,这里有200来个,包括且不限于基于decode的GPT、基于encode的BERT、基于encode-decode的T5等等)
通过博客内的这篇文章《Transformer通俗笔记:从Word2Vec、【【微信】】逐步理解到GPT、BERT》,我们已经详细了解了transformer的原理(如果忘了,建议必复习下再看本文,当然,如果你实在不想跳转,就只想呆在本文,也行,我努力..)
如果把上图中的各种细节也显示出来,则如下大图所示(此大图来源于七月在线NLP11里倪老师讲的Transformer模型源码解读,positional encoding、多头等没画)
考虑到Hugging face实现的Transformers库虽然功能强大,但3000多行,对于初次实现的初学者来说,理解难度比较大,因此,咱们一步步结合对应的原理来逐行编码实现一个简易版的transformer
?为了方便后面代码的编写,先引入一些库
对于模型来说,每一句话比如“七月的服务真好,答疑的速度很快”,在模型中都是一个词向量,但如果每句话都临时抱佛脚去生成对应的词向量,则处理起来无疑会费时费力,所以在实际应用中,我们会事先预训练好各种embedding矩阵,这些embedding矩阵包含常用领域常用单词的向量化表示,且提前做好分词
维度1 | 维度2 | 维度3 | 维度4 | ... | 维度512 |
教育 | |||||
机构 | |||||
在线 | |||||
课程 | |||||
.. | |||||
服务 | |||||
答疑 | |||||
老师 |
从而当模型接收到“七月的服务真好,答疑的速度很快”这句输入时,便可以从对应的embedding矩阵里查找对应的词向量,最终把整句输入转换成对应的向量表示
这部分的代码 可以如下表示
然,如此篇文章所述,RNN的结构包含了序列的时序信息,而Transformer却完全把时序信息给丢掉了,比如“他欠我100万”,和“我欠他100万”,两者的意思千差万别,故为了解决时序的问题,Transformer的作者用了一个绝妙的办法:位置编码(Positional Encoding)。
即将每个位置编号,从而每个编号对应一个向量,最终通过结合位置向量和词向量,作为输入embedding,就给每个词都引入了一定的位置信息,这样Attention就可以分辨出不同位置的词了,具体怎么做呢?
- 如果简单粗暴的话,直接给每个向量分配一个数字,比如1到1000之间
- 也可以用one-hot编码表示位置
- transformer论文中作者通过sin函数和cos函数交替来创建 positional encoding,其计算positional encoding的公式如下 其中,pos相当于是每个token在整个序列中的位置,相当于是0, 1, 2, 3...(看序列长度是多大,比如10,比如100),代表位置向量的维度(也是词embedding的维度,transformer论文中设置的512维)?
至于是embedding向量的位置下标对2求商并取整(可用双斜杠表示整数除法,即求商并取整),它的取值范围是,比如,,,,,, ...,,
相当于是指向量维度中的偶数维,即第0维、第2维、第4维...,第510维,用sin函数计算 是向量维度中的奇数维,即第1维、第3维、第5维..,第511维,用cos函数计算
不要小看transformer的这个位置编码,不少做NLP多年的人也不一定对其中的细节有多深入,而网上大部分文章谈到这个位置编码时基本都是千篇一律、泛泛而谈,很少有深入,故本文还是细致探讨下
考虑到一图胜千言 一例胜万语,举个例子,当我们要编码「我 爱 你」的位置向量,假定每个token都具备512维,如果位置下标从0开始时,则根据位置编码的计算公式可得『且为让每个读者阅读本文时一目了然,我计算了每个单词对应的位置编码示例(在此之前,这些示例在其他地方基本没有)』
- 当对上的单词「我」进行位置编码时,它本身的维度有512维
- 当对上的单词「爱」进行位置编码时,它本身的维度有512维
?然后再叠加上embedding向量,可得
- 当对上的单词「你」进行位置编码时,它本身的维度有512维
- ....
最终得到的可视化效果如下图所示
代码实现如下
从下图可知,经过「embedding + 位置编码」得到的输入,会乘以「三个权重矩阵:??」得到查询向量Q、键向量K、值向量V(你可以简单粗暴的理解为弄出来了三个分身)
举个例子,针对「我想吃酸菜鱼」这句话,经过embedding + 位置编码后,可得(注:可以512维,也可以是768维,但由于transformer论文中作者设置的512维,所以除了这个酸菜鱼的例子暂为768维外,其他地方均统一为512维)
然后乘以三个权重矩阵得
?为此,我们可以先创建4个相同的线性层,每个线性层都具有 d_model 的输入维度和 d_model 的输出维度
前三个线性层分别用于对 Q向量、K向量、V向量进行线性变换(至于这第4个线性层在随后的第3点)
我们聚焦下transformer论文中原图的这部分,可知,输入通过embedding+位置编码后,先后做以下两个步骤
- 针对query向量做【【微信】】,得到的结果与原query向量,做相加并归一化 这个相加具体是怎么个相加法呢?事实上,Add代表的Residual Connection(残差连接),是为了解决多层神经网络训练困难的问题,通过将前一层的信息无差的传递到下一层,可以有效的仅关注差异部分,这一方法之前在图像处理结构如ResNet等中常常用到 具体编码时通过 SublayerConnection 函数实现此功能 而Norm则代表了Layer Normalization,通过对层的激活值的归一化,可以加速模型的训练过程,使其更快的收敛,编码时用?LayerNorm?函数实现
- 上面步骤得到的『输出结果output做feed forward』之后,再与『上面步骤的原输出结果output』也做相加并归一化
总而言之,上述过程用公式表达则如下
第一步中的X代表?Multi-Head Attention,第二步中的X代表FFN(本质上就是一个全连接层MLP),最终这个编码器层代码可以完整的写为
接下来,先看下缩放点积注意力(Scaled Dot-Product Attention)的整体实现步骤
- 为了计算每个单词与其他单词之间的相似度,会拿「每个单词/token的q向量」与「包括自身在内所有单词/token的k向量」一一做点积(两个向量之间的点积结果可以代表两个向量的相似度) 对应到矩阵的形式上,则是矩阵Q与K矩阵的转置做相乘 还是拿上面那个例子:「我想吃酸菜鱼」,则Q乘以K的转置如下图所示 最终得到的矩阵有6行6列,从上往下逐行来看的话,每一个格子里都会有一个数值,每一个数值依次代表:? 单词我与「我 想 吃 酸 菜 鱼」各自的点积结果或相似度,比如可能是0.3?0.2 0.2?0.1 0.1 0.1,代表编码1时放在「我 想 吃 酸 菜 鱼」上面的注意力大小 同时,可以看到模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置(当然 这无可厚非,毕竟自己与自己最相似嘛),而可能忽略了其它位置。很快你会看到,作者采取的一种解决方案就是采用多头注意力机制(Multi-Head Attention)? 想与「我 想 吃 酸 菜 鱼」各自的点积结果或相似度? 吃与「我 想 吃 酸 菜 鱼」各自的点积结果或相似度? 酸与「我 想 吃 酸 菜 鱼」各自的点积结果或相似度? 菜与「我 想 吃 酸 菜 鱼」各自的点积结果或相似度? 鱼与「我 想 吃 酸 菜 鱼」各自的点积结果或相似度?
- 由于会随着dimension的增大而增大,为避免过大,所以除以?,相当于对点积的结果做下缩放 其中,是向量的维度,且,如果只设置了一个头,那就是模型的维度,如果设置了8个头,则,且如果模型的维度是512维,则即等于8 上面两步的代码可以如下编写
- 接着使用 Softmax 计算每一个单词对包括自身在内所有单词的 Attention值,这些值加起来的和为1(相当于起到了归一化的效果) 这步对应的代码为
- 最后再乘以矩阵,即对所有values(【【微信】】),根据不同的attention值(???),做加权平均 对应到我想吃酸菜鱼这个例子上,则是
- 最终得到单词的输出,如下图所示(图中V矩阵的4行分别代表【【微信】】): 上述两步对应的代码为
同样的方法,也可以计算出,如下图8所示, b2就是拿q2去对其他的key做attention,最后再与其他的value值相乘取【【微信】】得到,最终每个单词都包含了上下文相关单词的语义信息,不再只是attention计算之前,每个单词只有它自己的信息,和上下文没有关联
另外,这里面还有一点值得注意的是,可能有同学疑问:当我们计算x1与x2、x3、x4的相似度之后,x2会再与x1、x3、x4再依次计算一遍相似度,这两个过程中,前者算过了x1和x2的相似度,后者则再算一遍x2与x1的相似度,这不是重复计算么?其实不然,这是两码事,原因很简单,正如你喜欢一个人 你会觉得她对你很重要,但那个人不一定喜欢你 她不会觉得你对她有多重要..
最终,Scaled Dot-Product Attention这部分对应的完整代码可以写为
先看2个头的例子,依然还是通过生成对应的三个矩阵、、,然后这三个矩阵再各自乘以两个转移矩阵得到对应的分矩阵,如
- 矩阵对应的两个分矩阵、?
- 矩阵对应的两个分矩阵为、
- 矩阵对应的两个分矩阵为、
至于同理,也生成对应的6个分矩阵、、、、、
接下来编码时,分两步
- 先与做点积然后乘以,然后再与做点积再乘以,再把这两个计算的结果相加得到
- 再分别与做点积然后乘以、然后再与做点积再乘以,再把这两个计算的结果相加得到
如果是8个头呢,计算步骤上也是一样的,只是从2个头变化到8个头而已,最终把每个头得到的结果直接concat,最后经过一个linear变换,得到最终的输出,整体如下所示
这部分Multi-Head Attention的代码可以写为
在上文,咱们逐一编码实现了embedding、位置编码、缩放点积/多头注意力,以及Add和Norm,整个编码器部分还剩最后一个模块,即下图框里的Feed Forward Network(简称FFN)
其中包括两个线性变换:维度上先扩大后缩小,最终输入和输出的维数为,内层的维度为,过程中使用ReLU作为激活函数
虽然线性变换在不同位置上是相同的,但它们在层与层之间使用不同的参数,相当于使用了两个内核大小为1的卷积
这部分的代码可以如下编写
N可以等于6或其他数值
其中的clone函数的代码为
咱们再回顾下transformer的整个模型架构,特别是解码器的部分,毕竟BERT外,GPT等很有影响力的模型都用的transformer decode结构
从底至上,
- 输入包括2部分,下方是前一个time step的输出的embedding 再加上一个表示位置的Positional Encoding
- 接着是Masked Multi-Head Self-attention,【【微信】】字面意思是屏蔽 然后做一下Add&Norm
- 再往上是一个不带mask的Multi-Head Attention层,它的Key、Value矩阵使用 Encoder 的编码信息矩阵,而Query使用上一个 【【微信】】 的输出计算 然后再做一下Add&Norm
- 继续往上,经过一个FFN层,也做一下Add&Norm
- 最后做下linear变换后,通过Softmax 层计算下一个翻译单词的概率
由于在第一部分介绍过了embedding、positional encoding、FFN、Add&Norm、linear、softmax、【【微信】】,故本部分只重点介绍下Masked Multi-Head Self-attention
本过程和第一部分介绍的Multi-Head self-attention基本一致,区别在于加了个mask机制
- 输入经过embedding + 位置编码之后,还是乘以三个不同的权重矩阵:、、,依次得到三个不同的矩阵输入:Q、K、V
- Q矩阵乘以K矩阵的转置,得到,注意,紧接着会再乘以一个Mask矩阵,得到Masked Attention矩阵
- ?Masked Attention矩阵经过softmax后,乘以V矩阵得到矩阵
- 最终把、拼接之后,再做一个linear变换得到最终的矩阵
整个解码器架构的代码可以如下编写『有一点值得注意的是,如下文代码中所述
- 在对输入x执行自注意力计算并进行第一个子层的处理(带mask),最后一个参数是tgt_mask,即x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask))
- 但对输入x执行源注意力计算并进行第二个子层的处理时(不带mask),最后一个参数是src_mask,即x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask))?』
且Decoder也是由N=6个相同层组成
最终,整个transformer完整模型的整体封装代码为
当我们把编码器和解码器组合到一起后,看下它两是如何一块协作的
需要注意的是
- Encoder中的Q、K、V全部来自于上一层单元的输出 而Decoder只有Q来自于上一个Decoder单元的输出,K与V都来自于Encoder最后一层的输出。也就是说,Decoder是要通过当前状态与Encoder的输出算出权重后(计算query与各个key的相似度),最后将Encoder的编码加权得到下一层的状态 比如当我们要把“Hello Word”翻译为“你好,世界”时 Decoder会计算“你好”这个query分别与“Hello”、“Word”这两个key的相似度 很明显,“你好”与“Hello”更相似,从而给“Hello”更大的权重,从而把“你好”对应到“Hello”,达到的效果就是“Hello”翻译为“你好”
- 且在解码器中因为加了【【微信】】机制,自注意力层只允许关注已输出位置的信息,实现方法是在自注意力层的softmax之前进行mask,将未输出位置的权重设置为一个非常大的负数(进一步softmax之后基本变为0,相当于直接屏蔽了未输出位置的信息)
具体实现时,先创建批次和掩码
接下来,我们创建一个通用的训练和得分函数来跟踪损失。我们传入一个通用的损失计算函数,它也处理参数更新
下面这段代码定义了一个名为 SimpleLossCompute 的类,实现了简单的损失计算和训练函数
- 在调用该类的实例时,输入预测输出、目标输出和规范化因子,计算损失值并进行梯度更新
- 如果提供了优化器,还会更新模型参数和清空梯度缓存
优化器(optimizer)经常用于在训练过程中更新模型参数以最小化损失函数,而Adam(Adapti【【微信】】)是一种常用的优化器,它结合了两种传统优化算法的优点:Momentum和RMSprop
为了通俗易懂地理解Adam,可以将其比作一个赛车手。训练模型就像是找到一辆赛车在赛道上的最佳行驶速度和路径,以达到最快的速度并取得优异的成绩。在这个过程中,速度的调整(即学习率)非常重要
-
首先,Adam像Momentum一样,具有动量效应。这意味着赛车手(模型)会积累动量,使其在下坡时更快,而在上坡时减速。这有助于模型更快地穿越平坦区域,并避免在最低点附近摆动
-
其次,Adam像RMSprop一样,会自适应地调整每个参数的学习率。在我们的赛车比喻中
必应chatgpt怎么用
3.1ChatGPT厚积薄发
最近,工智能公司OpenAI推出的ChatGPT风靡全球,其上线仅两个月,注册用户破亿。ChatGPT包含丰富的知识,不仅能更好地理解人类的问题和指令,流畅进行多轮对话,还在越来越多领域显示出解决各种通用问题和推理生成能力。许多人相信,ChatGPT不仅是新一代聊天机器人的突破,也将为信息产业带来巨大变革,也预示着AI技术应用将迎来大规模普及。
ChatGPT表现不俗?其背后的技术有哪些?
3.2 从GPT到GPT-3
3.3 从GPT-3到ChatGPT的进化路线图
下图为从最初的GPT-3到GPT-3.5的进化路线图。
图1 GPT-3初版到ChatGPT的进化路线图
其中text―davinci―002是在code―davinci―002的基础上使用InstructGPT训练方法改进的。GPT-3.5在GPT-3的基础上加入了代码的能力,ChatGPT的代码训练中,很多数据来自于类似Stack O【【微信】】这样一些代码问答的网站,所以我们会发现它做简单的任务其实做得还蛮好的。
从图1可知,GPT-3为ChatGPT打下了扎实的基础,但codex、RLHF等技术新增很多新功能,挖掘了GPT-3的潜力。
3.4 使GPT-3初版升级到ChatGPT的多项关键技术
从图1可知,这两项关键技术是代码训练(Codex)、RLHF及TAMER等
1、Codex
Codex 模型系列是 GPT-3 系列的后代,它经过了自然语言和数十亿行代码的训练。该模型系列精通十几种语言,包括 C# Ja【【微信】】、Go、Perl、PHP、Ruby、Swift、TypeScript、SQL甚至Shell,但最擅长 Python。
你可以使用Codex完成各种任务,包括:
将注释转换为代码
在上下文中补全下一行代码或函数
为你提供一些知识,例如为应用程序查找有用的库或 API 调用
添加注释
重写代码以提高效率
Codex如何训练的呢?
首先,在GITHub数据上预训练模型。这个模型可以合理地表征人类编码空间,可以极大地减少搜索量级。使用带【【淘密令】】的GOLD目标函数,结合编程竞赛数据集,微调模型。可有进一步降低搜索空间,给每个编程题目生成一个较大的样本集;过滤这个样本集,得到一个较小的候选结果集。
然后,进行代码补全,代码补全这个任务的特殊性:具体来说,传统的NLP任务,生成的结果越接正确答案,那么模型得分越高,但是代码不是这样的,代码但凡有一点点小Bug,都可能造成毁灭性的结果。所以对于代码补全任务,判断生成代码的正确与否就是使用的单元测试(unittest)。
针对代码补全这样一个特殊问题,作者提出了一个pass@k的一个指标,生成k个结果,只要有一个通过就算通过(k如果比较大,就会对模型的能力过度乐观,当k比较大的时候,虽然模型分数比较高,但是在使用时,会给用户返回一大堆代码,让用户去选,这个也是很难的,所以说需要排算法,但这个分数并没有反映排序)。
在预训练过程中引入程序代码,和文本一起参与预训练,以此进一步增强大型语言模型(Large Language Model,LLM)的推理能力。这个结论从不少论文的实验部分都可以得出。如图3所示。
图3 有关codex的试验数据
从图3给出的实验数据,来自于论文“On the Ad【【微信】】e Models Better Reasoners”,其中GPT-3 davinci就是标准的GPT-3模型,基于纯文本训练;code-da【【微信】】(OpenAI内部称为Codex)是同时在Code和NLP数据上训练的模型。如果比较两者效果,可以看出,不论采用具体哪种推理方法,仅仅是从纯文本预训练模型切换到文本和Code混合预训练模型,在几乎所有测试数据集合上,模型推理能力都得到了巨大的效果提升。
2、RLHF
人类反馈强化学习(ReinforcementLearning from Human Feedback,RHFL)模型将预训练语言模型按照人类反馈进一步微调以符合人类偏好,利用人类反馈信息直接优化模型。Open AI 采用了人类反馈强化学习作为ChatGPT和核心训练方式,并称其是“能有效提升通用人工智能系统与人类意图对齐的技术”。RLHF 的训练包括三个核心步骤:
(1)预训练语言模型(也可以使用额外文本进行微调,监督微调新模型可以让模型更加遵循指令提示,但不一定符合人类偏好)。
(2)对模型根据提示(prompt)生成的文本进行质量标注,由人工标注者按偏好从最 佳到最差进行排名,利用标注文本训练奖励模型,从而学习到了人类对于模型根据给定提示生成的文本序列的偏好性。
(3)使用强化学习进行微调,确保模型输出合理连贯的文本片段,并且基于奖励模型对模型输出的评估分数提升文本的生成质量。
详细过程如图4所示。
图4 RHFL的训练过程,
原图来自:Learning to summarize from human feedback
3、TAMER
TAMER(Training an Agent Manually 【【微信】】cement,评估式强化人工训练代理)框架。该框架将人类标记引入到智能体(即强化学习中的Agents)的学习循环中,可以通过人类向Agents提供奖励反馈(即指导Agents进行训练),从而快速达到训练任务目标。其架构图如下所示。
3.5 ChatGPT训练过程
3.6ChatGPT不断迭代的路线图
3.7ChatGPT的不足
尽管ChatGPT在上下文对话能力甚至编程能力上表现出色,完成了大众对人机对话机器人由“人工智障”到“人工智能”的突破,我们也要看到,ChatGPT仍然有一些局限性,还需不断迭代进步。
(1)ChatGPT在其未经大量语料训练的领域缺乏“人类常识”和引申能力,甚至会一本正经的“胡说八道”。
(2)ChatGPT无法处理复杂冗长或者特别专业的语言结构。对于来自金融、自然科学或医学等专业领域的问题,如果没有进行足够的语料“喂食”,ChatGPT可能无法生成适当的回答。
(3)ChatGPT还没法在线的把新知识纳入其中,而出现一些新知识就去重新预训练GPT模型也是不现实的。
(4)训练ChatGPT需要耗费非常大量的算力,成本还是很大的。
3.8ChatGPT应用场景
ChatGPT能够提供高效的信息获取方式,有望成为重要的生产工具,潜在应用领域广泛。业界普遍认为,ChatGPT将在智能办公、智慧科研、智慧教育、智慧医疗及游戏、新闻等领域迅速落地。在金融、传媒、文娱、电商等领域,ChatGPT可以为各类消费群体提供个性化、高质量的服务,解锁多领域智慧应用。
版权声明:除非特别标注原创,其它均来自互联网,转载时请以链接形式注明文章出处。