找回密码
 立即注册
查看: 682|回复: 5

ChatGPT道理,技术架构是什么?

[复制链接]

1

主题

0

回帖

21

积分

新手上路

积分
21
发表于 2024-1-20 10:40:02 | 显示全部楼层 |阅读模式
ChatGPT道理,技术架构是什么?
如何基于这些架构和道理实现的chatgpt,对整个技术产物进行复现呢?
回复

使用道具 举报

0

主题

3

回帖

5

积分

新手上路

积分
5
发表于 2024-1-20 10:40:50 | 显示全部楼层
ChatGPT是一个庞大的神经网络—GPT-3 拥有1750亿个权重。它最显著的特点是一个称为Transformer 的神经网络架构。


ChatGPT(或者说它基于的 GPT-3 网络)到底是在做什么呢?

它的总体目标是,根据所接受的训练(查看来自互联网的数十亿页文本,等等),以“合理”的方式续写文本。所以在任意给定时刻,它都有一定量的文本,而目标是为要添加的下一个标记做出适当的选择。
它的操作分为三个基本阶段。
第一阶段

它获取与目前的文本相对应的标记序列,并找到表示这些标记的一个嵌入(即由数组成的数组)。
第二阶段

它以“标准的神经网络的方式”对此嵌入进行操作,值“像涟漪一样依次通过”网络中的各层,从而产生一个新的嵌入(即一个新的数组)。
第三阶段

它获取此数组的最后一部分,并据此生成包含约 50000 个值的数组,这些值就成了各个可能的下一个标记的概率。(没错,使用的标记数量恰好与英语常用词的数量相当,尽管其中只有约 3000 个标记是完整的词,其余的则是片段。)
关键

这条流水线的每个部分都由一个神经网络实现,其权重是通过对神经网络进行端到端的训练确定的。换句话说,除了整体架构,实际上没有任何细节是有“明确设计”的,一切都是从训练数据中“学习”来的。
ChatGPT 的原始输入是一个由数组成的数组(到目前为止标记的嵌入向量)。当 ChatGPT“运行”以产生新标记时,这些数就会“依次通过”神经网络的各层,而每个神经元都会“做好本职工作”并将结果传递给下一层的神经元。没有循环和“回顾”。一切都是在网络中“向前馈送”的。
这是与典型的计算系统(如图灵机)完全不同的设置—在这里,结果不会被同一个计算元素“反复处理”。至少在生成给定的输出标记时,每个计算元素(神经元)仅使用了一次。
但是在某种意义上,即使在 ChatGPT 中,仍然存在一个重复使用计算元素的“外部循环”。因为当 ChatGPT 要生成一个新的标记时,它总是“读取”(即获取为输入)之前的整个标记序列,包括ChatGPT 自己先前“写入”的标记。我们可以认为这种设置意味着ChatGPT 确实,至少在其最外层,包含一个“反馈循环”,尽管其中的每次迭代都明确显示为它所生成文本中的一个标记。
ChatGPT 的核心

神经网络被反复用于生成每个标记。在某种程度上,它非常简单:就是完全相同的人工神经元的一个集合。网络的某些部分仅由(“全连接”的)神经元层组成,其中给定层的每个神经元都与上一层的每个神经元(以某种权重)相连。但是由于特别的 Transformer 架构,ChatGPT 的一些部分具有其他的结构,其中仅连接不同层的特定神经元。(当然,仍然可以说“所有神经元都连接在一起”,但有些连接的权重为零。)
此外,ChatGPT 中神经网络的有些方面并不能被顺理成章地认为只由“同质”层组成。例如,(正如本节中单个“注意力块”的示意图所示)在注意力块内有一些对传入的数据“制作多个副本”的地方,每个副本都会通过不同的“处理路径”,可能涉及不同数量的层,然后才被重新组合。虽然这可能简便地表示了正在发生的事情,但至少原则上总是可以将事实考虑为“密集填充”各层,只是有一些权重为零。
关于chatGPT的详细介绍推荐一篇知乎文章,写的真是全面,很不错:
唔讲粗口:关于ChatGPT:GPT和BERT的差别(易懂版)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

0

主题

1

回帖

0

积分

新手上路

积分
0
发表于 2024-1-20 10:41:40 | 显示全部楼层
ChatGPT似乎终于降温了一点。但它实现了我当年玩微软小冰时候的一个梦想,终于可以联系上下文,不再像个人工智障。时间原因一直没有写写它相关的内容,这次补上这块内容。
读论文的手艺不能丢,我准备带着大家一起解析一下ChatGPT的原理,同时回顾一下ChatGPT如何一步一步发展成了今天这个样子。
本文适合想了解ChatGPT原理的同学。虽然几乎没有公式,但Neural Network (NN)要有一定的基础,否则可能看不懂。
1. 问题描述

1.1 要解决的问题

这个东西要首先写清楚。


chatgpt到底要解决一个啥问题?你可以理解为,输入一段文本,它负责理解文本并给你输出一段你期望的文本。
专业一点讲,叫NLP (Natural language processing,自然语言处理)。
1.2 这个问题有啥难点

肯定是有难点,否则不会出现小爱这些一大堆人工智障。首先,由于数据量很大,依靠监督学习(人来告诉神经网络什么是对的, 采用labeled data)是不现实的。训练这种模型只能使用非监督学习,也就是Network自己从unlabeled的文本里学习。那搞无监督学习也有两个难点

  • 第一,既然没有人监督,你至少要告诉NN优化目标,也就是让NN自己判断什么结果是符合预期的。这个优化目标就不太好找。
  • 第二,找了也不太通用,可能语言的一个方向上好用,换到另一个就瞎了。举个例子,以前很难从把数学课上学到的语境迁移到煤矿挖掘语境下。
因为有这两个大坑的在,所以chatgpt之前,自然语言处理的人工智能还只能是人工智障。
首先,GPT= Generative-Pre-Trained Transformer, 你看出了什么?
chatgpt采用的基础神经网络是transformer, 所以第一篇文章我们先简要介绍一下transformer是怎么工作的。
2. ChatGPT的基础——Transformer

2.1 Transformer是个什么东西?

通俗的讲,你可以理解为Transformer是一种特殊的神经网络。Transformer最初是是google的一帮人搞出来的玩意儿。名字叫《Attention is All You Need》, 感兴趣的哥们可以去读一下。从名字也可以看出来,这个东西是利用注意力来训练网络的一个方法。
插一句,Transformer前面还有RNN,但RNN有梯度消失的问题,基本上属于鱼的记忆,如果句子一长它基本就把前面的东西忘了。所以曹雪芹那种草蛇灰线的操作一下就把RNN个干废了。这一部分为了好讲,我引用了国外Jay Alammar老哥的一些图,源地址看这里The Illustrated Transformer
首先,这个网络从功能上看是解决我们上面NLP问题的一个网络。输入一段文字,给你输出另一段。


2.1 Transformer网络的基础架构是什么样的?

其实内部原理呢,就是一个编解码器。我们先把输入编码,然后用解码器解码成另一个东西。


是不是很容易理解。但是实际上人类语言都是很复杂的,怎么可能一个简单的编解码器搞定。我们实际的编解码器都有若干组。


自然语言经过层层编码,最后再通过层层解码,最后变成我们要的文字。你可能这个时候要问,为什么encoder结果要连接给所有的decoder, 简单理解是我们希望神经网络理解的是一个句子,不是一个词,显然要看的到上下文。
2.1 Encoder和Decoder里面是怎么计算的?

那好奇宝宝可能要问了,encoder和decoder里面都是啥东西?


encoder里两块内容。self-attention和feed forward。 decoder多一块,encoder-decoder attention。feed forward好理解,就是一个普通的前馈神经网络(NN),这个self-attention和encoder-decoder attention是干啥的?别急,我们接下来讲。
2.2 Self-Attention是个什么东西,怎么算的?

这个attention是什么意思?简单理解,就是我们现在处理的词和其他词之间的关系。
比如一句话
The animal didn't cross the street becasue it was too tired.
这个it指的是animal, street其实机器不好分辨的。我们处理it的时候显然得有办法让他和animal联系起来。这就是attention.


如上图,这也简单,只要給任何两个词之间给个权重,自然就能让NN把他两联系起来,你也可以把这个权重称之为,attention。那么问题来了,如果计算这个attention?
这个attention谁transformer中最绕的部分。待我通俗的用三句话给你讲。
第一句,attention最原始的想法是什么?求两个词之间的相关度。
第二句,求相关怎么算最方便?直接把词编码成向量,然后用点积算向量之间的夹角就行。
第三句,要是直接算效果不好呢?加入神经网络训练的玄学权重,先乘以一个权重转换一下再做相关。
理解了上面三句,基本上attention你就会算了。我们举两个单词的句子为例: Thinking machines.



attention的计算

首先,我们把句子切分成thinking和machines两个词。
然后编码成两个向量x1, x2. (至于怎么编成x1,x2两个向量的不是我们的重点,基本原理是同时考虑单词的意义和单词在句子中的坐标,算出来一组向量,如果感兴趣留言我们专门讲,你这个地方只需要知道我们可以把一个个词换成一个个向量就好。)
然后分别乘以训练的权重Wq, Wk,  Wv得到q值和k值和v值。
然后用thinking的q值和所有(包括自己,这个案例中就是thinking和machines)的k值做点积求相关。然后用softmax做个归一化,得到的一个小数值,就是thinking与thinking以及machines的相关度,我们用v乘以这个相关度。然后我们的attention最后要变成一个向量,往神经网络后面传递,所以直接把v1,v2加起来作为了thinking的attention。
此处你可能要问了,为啥生产了Q,K,V三个矩阵而不是用一个,或者干脆不用,别问,问本质原因就是想多加入几组权重实现神经网络的玄学目标。。。就比如你问为啥一个神经网络有XX层一样。
换个用矩阵的算法,大致就是下面两个步骤。



步骤1



步骤二

那你这个时候就要问题了,玄学权重W_k,v,q怎么来的,炼丹训练出来的。。。
总之,到这个地方,你应该是会算attention了。
2.3 实际网络中使用的多头Self-Attention是个什么东西?

我们朴素的想法,每个词算一个attention输入到feedforward, 一个encoder就大功告成了。如下。


实际上会这么简单么?显然不啊。
这么朴素算出来的attention有个致命的缺点。。。一套K,V,Q矩阵搞出来的attention是不是有点太简单了。毕竟人类的语言总是可以从多个角度解析。不可能用一个方面(KVQ)就给概括了。


比如还是上面这个例子,你觉得it和animal关系最大,那换一种理解方法,是不是it和tire关系最大?毕竟管它是不是animal, 我知道it tired不就拉倒了。
怎么办?一套不行就上上强度,多套呗。从不同角度介解析一下语义。(炼丹量加倍)。上面里的例子来讲,我们提供若干套KVQ, 一套让it关注animal, 另一套让it关注tired。。。
如下图,我提供8套KVQ, 你给我一次算出8中attention来。



那么新问题又来了,你算出来了8个,后级神经网络只能接收1个啊,怎么办?
这个时候就要请出神经网络里的吃内存的老大爷,全连接层。。。大不了再训练一组参数,把算出来的8个结果再用全连接层混一混。(只要你了解过神经网络,全连接层应当不陌生?)


如上图所示,Z0~Z8搞一个长序列,乘以W0(炼的丹),最终搞出一个Z传送给后级。
2.4 实际网络中使用的encoder和decoder长啥样?

讲到这里,attention你应该是会算了。回顾一下下面这张图。Feed Forward你知道NN应当会算,self-attention会算以后还差个encoder-decoder attention。其实这个也是算self-attention, 只是输入有两个来源,一个是encorder的输出,一个是self-attention的输出而已。


我们再push一步,实际网络里为了梯度更更好的传递,其实使用了何凯明搞出来的residual结构。实际上真的一个encoder计算流程如下。


emmm, 至此,一个单词怎么用transform算你应该是明白了。
2.5 实际的transformer是怎么处理文本的?

那还有一个问题,一个句子是怎么算出来的。
首先,第一步,我们把我们的句子通过层层encoder算出来。这一步是可以并行算出来的。然后把结果和K,V矩阵存起来。


然后把encoder的输出,作为decoder的输入(怎么连接输入的见上面encoder与decoder的图,encoder的输入实际上要连接到每个decoder),生成我们需要的词 (单词I)。
然后把decoders的输出(单词I)和encoder的输入重行作为decoder的输出,生成下一个词am。


不断重复,最后生成我们需要的句子(输出end of sentence标识符)。


至此,你应该对Transformer如何工作的有了一个大致的了解。
3. 总结

这篇文章我们讲了ChatGPT使用的基础神经网络transformer, 它是一个新的处理NLP问题的利器。有了transformer以后,它是如何一步步被发展成ChatGPT的呢?我们下篇文章讲。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

0

主题

5

回帖

11

积分

新手上路

积分
11
发表于 2024-1-20 10:42:03 | 显示全部楼层
ChatGPT的原理和技术架构如下:

  • 原理:ChatGPT是一种基于深度学习模型的自然语言处理技术,其核心原理是使用大量的文本数据进行训练,并将其转化为高维向量空间中的表示形式,从而实现对语言文本的表征和预测。具体来说,ChatGPT使用了一种基于Transformer架构的模型,在训练过程中通过不断地学习和调整模型参数,从而实现对自然语言文本的理解、生成和对话等任务。
  • 技术架构:ChatGPT技术架构主要分为以下几个部分:


  • 输入层:输入层接收用户输入的自然语言文本,例如:问题、句子或段落等。
  • Transformer编码器层:Encode输入的文本,并将文本转换为一些有意义的信息(称为隐藏向量)。
  • Transformer解码器层:Decode前面产生的隐藏向量,生成新的词汇序列。
  • 词汇量表:将所有输入和输出的单词或标记集中到词汇量表(Vocabulary Table)中,帮助模型更好地处理这些单词和标记。
  • 模型参数:这些参数包括模型权重、偏置和超参数等,是在训练过程中优化的。
在ChatGPT的技术架构中,Transformer编码器层和解码器层是非常重要的组件,其可以实现掌握长距离上下文信息的能力,因而在生成对话、文章等任务中具有相当高的表现力。同时,为了确保模型的可靠性,ChatGPT还采用了一些针对性的算法和技术策略,例如:dropout、层归一化等,从而进一步提升了模型的稳定性和实用性。


有大佬在2月份就撰写了一个原创有所有权版‬认证以在及‬北国京‬信公证处进行公证的近8万字的实时在线文档《chatgpt无障碍使用珍藏手册》,目前国内有很多行业大佬就是靠这个手册启蒙的,所以它很适合刚接触chatgpt的朋友!


哪怕你是小白,你也可以不用注册、不用登录、不用科学上网、不限时长、纯免费无限制畅玩chatgpt,更有大量的精准搜索指令供你在短时间内学会让chatgpt来提升你的工作技能让你一个人轻松干10个人的活!更有不少利用chatgpt创业和变现的小项目供你参考,具体的完整介绍,您可以直接查看下面这个链接:
chatgpt无障碍使用珍藏手册如果你已经是精通chatgpt使用的大佬,或者你更侧重于利用chatgpt来创业和变现,那么这个26万字《玩赚:108种chatgpt创业变现和创业思维手册》更适合你!它包含了《chatgpt无障碍使用手册》的内容,有108种chatgpt变现和创业的项目,每个项目都包含了项目名称、项目概述、适合人群、项目变现方式、操作步骤提示、网络宣传渠道、网络宣传文案参考、扩展思路、注意事项、chatgpt指令参考(截图)等十个方面进行了阐述。
更有不用科学上网、不用注册、不用账号和密码,更不限时长就能纯免费畅玩chatgpt4.0的镜像站推荐,而且还是联网的!(稀缺资源)。具体的完整介绍可直接查看下面的内容:
《玩赚:108种ChatGPT变现和创业思维手册》—— 让“风口”带你去致富(智慧进阶版)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

0

主题

6

回帖

4

积分

新手上路

积分
4
发表于 2024-1-20 10:42:21 | 显示全部楼层
ChatGPT的原理可以参考如下:

一、ChatGPT 是什么


  • ChatGPT 是由 OpenAI 开发的一种基于语言模型的人工智能程序,它可以与人类进行自然语言交互。
  • 基于 GPT(Generative Pre-trained Transformer)技术构建。
  • GPT 代表“生成式预训练”,它是一种基于深度学习的自然语言处理技术,利用海量的语言数据进行预训练,从而能够在多个自然 语言任务上表现出色。


1.1、ChatGPT如何使用?

ChatGPT能做的事情非常多,从普通的知识性问答,聊天,对话、教学、科研及时代码生成、代码分析、Debug的能力,ChatGPT都是具备的。
ChatGPT是一个融合了巨量人类智慧的超级结晶体,拥有了它,你可以随时获取任何领域的知识,ChatGPT通过理解你的问题,整理汇总出比较简单的、简洁的答案输出,这比我们平时自己去百度、Google搜索然后自己汇总有非常明显的优势,所以ChatGPT这种超强的知识提取和总结能力,真的很令人惊艳,如何使用ChatGPT才能最大化它的作用。
简单的说,其实就是一句话,提出好的问题,对于ChatGPT来说,问题比答案更重要,因为GPT模型本身就是基于提示(Prompt)来起作用的,它的回答,取决于你给他的提示的内容和质量,那么怎么才能提出好的问题呢?
1)、增加细节(增加提示的细节和要求)
2)、不断追问(基于ChatGPT生成的内容不断追问)
3)、心存疑问(对于ChatGPT的回答不能盲目相信)


1.2、ChatGPT 是万能的吗?

ChatGPT不是万能的,它的回答没有经过验证,因为这是它的模型自动推理产生的,这就是深度学习神经网络的局限性,在上亿、百亿、甚至千亿的网络参数中,我们不可能知道是哪些参数在发挥作用,也就是说我们不可能知道它的答案到底有多准确,所以ChatGPT本身也有几个明显的问题:
1)、中文训练语料库比英文训练语料库要少,所以中文知识也少
2)、它无法给出这个信息提供的来源,这就跟百度和Google有本质的不同,在搜索引擎中,我们知道文章是谁写的,所以ChatGPT只能使用它训练的知识
3)、无法获取最新的数据,只能获取训练时间节点的数据来提供知识
当然,以上存在的局限性可能会随着ChatGPT不断的训练和进化,它的答案会越来越好,但是答案可能出错的可能性是永远存在的。


当一个人一本正经说的时候,我们很容易就觉得他是在说真话,如果他说了很多真话之后,偶尔说几句假话,就非常具有欺骗性了,这就是ChatGPT的问题,所以大家在使用ChatGPT的时候一定要有批判性思维,不能盲从,必须要对答案进行验证。
1.3、ChatGPT 的底层原理

我们可以让ChatGPT自己来回答一下这个问题吧,目前来看最新的GPT模型回答还算比较准确。


我们都知道两个非常经典的深度学习模型,一个是RNN,一个是LSTM,循环神经网络,长短时记忆网络主要用于处理序列类型数据的经典模型,而ChatGPT是基于比这两个模型更新的Transformer架构。
二、预训练大语言模型的发展

2.1、Transformer 架构出现之前



在Transformer架构和与训练大模型出现之前,NLP历史上已经有很多思潮的涌现和发展了,深度学习在NLP领域的突破其实是比较晚的,最早深度学习的突破都集中在计算机视觉领域。
从2012年开始,AlexNet模型在ImageNet图像识别大赛中,取得了很大的突破,这个标志着CNN卷积神经网络的兴起,后来有GoogleNet、VGG ResNet各种深度学习模型的出现,大幅度的提升了图像分类,目标检测、语义分割等CV任务的准确性,在目标检测人脸识别有R-CNN、Fast R-CNN、YOLO SSD一系列算法;语义分割方面也有SENet UNet,再加上生成对抗网络GAN的出现,然后DCGAN、PsychoGAN这种图像生成、风格迁移领域都取得了非常显著的进展。
所以,在2018年以前,CV的发展是风生水起,在NLP领域,虽然有RNN和LSTM,但是并没有特别多的真正落地应用的突破性进展。


是什么带来了NLP领域第一轮的飞跃呢? 到了2018年之后,两个核心技术就产生了,一个就是Transformer,另外一个就是BERT,随着这两个模型的诞生,NLP的节奏逐渐就追上来了,紧接着一系列的与训练大模型像雨后春笋个般的出现了,我们就可以下载这些与训练大模型,然后通过微调,在自己的自然语言处理任务上去使用它们,这里的自然语言处理任务就是我们实际的问题,比如语音识别、文本分类、情感分类、命名实体识别、机器翻译、文本摘要、文本生成等。
2.2、用深度神经网络解决 NLP 问题



深度神经网络是怎么解决这种自然语言处理实际问题的呢,深度神经网路其实是由很多层组成的,每一层都有很多神经元,也就是节点,每个节点都有一系列参数,数据输入模型的时候,这些节点就通过这些参数对自然语言处理的数据进行运算,其实从输入空间到输出空间都有对应的函数和一个映射。
比如我们要做微博的情感分类任务,每条微博200字,我们就需要把200字转化为200个token输入进去,那输入序列就是200维,然后经过神经网络所代表的函数、经过参数的计算,最后给你输出一个二维的结果,比如0或者1,0就代表博主的情绪比较负面,1代表博主的情绪是正面的,这就是一个典型的NLP分类任务。
其实整个机器学习就是把它抽象出来变成一个函数的形式,我们去理解深度神经网络也不会那么复杂。
2.3、大型预训练网络 Before 2021



从早起的预训练网络ELMo开始到2021年左右,这个时期的预训练网络发展历程,可以看到预训练网络越来越大,从初代的GPT、初代的BERT到后面的RoBERTTa、BART、T5一个清晰的趋势就是模型变得越来越大,参数越来越多,它所训练的语料库也就越来越大。
比如BERT就是基于Wikidata来训练的,后面的模型就又收录了更多的公开的书籍、网络上的一些文本、语料变得越来越庞大,它的知识库也就越来越大,这是一个很典型的趋势。
2.4、大型预训练网络 As of Dec 2022



截止到2022年12月,此刻的预训练大模型就已经非常庞大了,ChatGPT所基于的GPT-3模型,它有1750亿个参数,在整个大语言模型的生态中,它还不算事最大的模型,但它的效果可能超过其它更大的模型。
2.5、Transformer 架构



Transformer架构是所有预训练语言模型的基础,它是自然语言处理的神经网络,当然,Transformer架构现在已经延展到了CV和其它领域,很多处理自然语言处理之外的问题,也都采取了Transformer架构来解决。
它最早是由Google在 2017 年提出,它的目的就是解决传统训练模型,就是循环神经网络中存在的效率问题和并行计算问题。 因为Transformer模型使用了自注意力机制(self-attention)来捕捉输入序列的上下文关系,所以它就使得每个词都能够建立和整个序列其它词之间的连接,从而它就可以有效的捕捉文本中的长距离依赖关系,它不需要像RNN这样来循环的一直去用递归的方式回访过去的数据,所以它跟传统的神经网络相比,它快,它不需要一个词接一个词地处理,它是做整体的并行处理,所以它在训练速度和上下文捕捉这种性能方面的表现都更加优秀。
另外一点就是,Transformer架构它还可以通过堆叠多个层来构建深度学习模型,所以它也是一种深度学习模型,它能够堆叠,所以它就能够不断的来扩大规模,进一步提高模型的性能,现在最新的研究表示,模型越大,它就越有可能出现更多的涌现能力(不知道什么能力就突然被解锁了),就好比今天这个模型还不能对话,随着模型的参数越来越大,层数越来越多的时候,它突然可能就拥有了和你流畅对话的能力,这是一个很不可思议的事情。
所以,现在Transformer模型在NLP任务中已经全面取代了RNN为代表的循环神经网络模型,循环神经网络模型在实际的应用中,以后就不多了。
2.6、Seq2Seq:序列到序列



Transformer本身是一种序列到序列的架构,在Seq2Seq架构中,输入的序列会被深度神经网络处理产生一个输出的文本序列,比如机器翻译、聊天机器人就是很典型的场景。
序列到序列有两个结构,一个是编码器,一个是解码器,其中编码器负责将输入序列编码成一个固定长度的向量表示,然后解码器需要负责把这个向量再生成对应的输出序列,在一个ChatBox中,输入序列通常就是用户的提问,输出序列就是ChatBox的回答或者响应了,这其实就是一个把编码给逐渐解码,然后形成我们最后所需要的答案的一个过程。
当我们训练的时候就会给它一些语料, 一个问题的答案应该是怎么样的,它看到这些就自己不断的纠错,然后神经网络就会给出越来越接近我们期望的答案。
2.7、BERT vs GPT

两个最经典的基于Transformer的预训练模型,Google的BERT模型和OpenAI的GPT-3
BERT



1)、Bidirectional Encoder Representations from Transformers
——双向编码,同时考虑了左右两边的词
2)、Masked Language Model(MLM)——擅长做完形填空
3)、Next Sentence Prediction(NSP)——是不是下一句
4)、Fine-tuning ——微调完成下游任务
BERT只需要编码器的内容,不需要生成文本。
GPT-3



1)、Generative Pre- trained Transformer 生成式
2)、Autoregressive 自回归
3)、Prompt/Instruction 基于提示/指令,完成下游任务
BERT和GPT两个模型没有优劣而言,它是针对不同的任务类型设计的,BERT比较擅长于掌控全局,它就能够做很好的自然语言推理任务,比如情感分类、完形填空、命名实体识别、关系抽取这些都依赖优秀的全文理解能力;而GPT它的目标就是文本生成,所以对于聊天机器人或者问答系统来说,它就有比较天然的优势了。
2.8、从 GPT 到 ChatGPT 的演变



GPT是OpenAI的序列生成模型系列,能够产生高质量的自然语言文本。从GPT-1到GPT-3,它的原理其实是很类似的,但是它的参数数量每次都是翻很多倍的增长,GPT-2到GPT-3,参数翻了100倍,输入数据维度也翻了接近8倍,可以使它一次性理解很多很多的token,也就是一次性输入很大的文本都可以同时理解。
到了GPT-3的时候,已经能够生成非常流畅、准确的自然语言文本了,它生成的文本质量基本能够跟人类的写作相媲美,参数数量增加的好处就是让它能够更好的学习自然语言的规律,能够理解序列中更多的上下文信息,生成更连贯更准确的文本,GPT-3还增加了多语言的支持,能够处理更复杂的任务。
ChatGPT是怎么从GPT-3模型的基础上再进一步演进的呢? 因为它是GPT-3模型上转么负责聊天机器人任务上的应用,它是GPT-3.5,叫做3.5优化版,它作为GPT的第三代,在万亿词汇量通用文字数据集上面训练完成的。
它还有另一个兄弟模型,叫InstructGPT,都是建立在GPT-3.5基础上,为了让ChatGPT表现出色,OpenAI对预训练数据集还做了微调,给它增加了基于人类反馈的强化学习,让它更能够了解人类想听什么,能够更好的处理哪些是人类想要的答案。
ChatGPT在InstructGPT基础上又增加了一层Stafety Layer,因为它是一个面向大众的聊天工具,它不能说错话,不能够说危害公众安全的回复,不可以宣传不应该宣传的东西,这个很重要,要控制它的回答是合理的。
三、ChatGPT 的训练过程

ChatGPT系列模型的基本思路是让AI在通用的数据上学习文字接龙,掌握生成后续文本的能力,这样的训练有一个好处,就是它不需要人类去标注,只需要把一大堆的语料库输给它,它就会自己去训练,然后你可以给它打分,遮住下文让它去预测。


一个问题可能有很多个答案,作为一个聊天对话机器人来说,我们想得到比较确定性的答案,GPT在进行文本生成的时候,需要进行一些人类的指导,让人类告诉它,什么样的回答才是我最想听到的,这就是基于人类反馈的训练的核心思路。它的RLHF训练过程主要有三步:




3.1、监督调优模型

收集演示数据,用监督学习去训练生成规则(把一些问题写出答案,把问题和答案都丢给GPT去训练,这个是有监督的训练,已经有答案了,让AI一葫芦画瓢,这种方法可以引导AI往人类所期望的方向去做答)
但是,我们不可能人工穷举出所有可能的问题和答案,这个显然是不现实的,所以OpenAI只是提供了可能几万个这种有答案的数据,主要是为了让它在这个基础上进行泛化,然后提供一个方向上的引导,就是告诉模型,你就往这个方向上去答。
3.2、训练回报模型

让简化版的GPT监督训练之后变得更强,通过人工标注所有输出的优劣势
先让ChatGPT输出很多个答案,然后基于它所生成的答案给他排序,我们只需要人工标注哪个答案是最好的,所以OpenAI做了大量的这种标注,
3.3、使用 PPO 模型微调 SFT 模型

通过PPO强化学习算法,实现模型的自我优化,强化学习就是让AI在不断的试错过程中自我调整优化策略,然后最大化预期的长期奖励,简单来说,就是让AI自己去不断尝试,前两步学习的模型在强化学习这一步都能派上用场。
首先用监督版学习的ChatGPT来初始化PPO模型,让Reward模型去指导它,去给回答一个评分,然后AI就基于这个评分去调整自己的参数,试图在下一个回答中得到更高的分数,不断的重复这个过程,这个幼儿版的ChatGPT就成熟起来了,能够自我更新了。
经历这样的三个步骤,一个真正的ChatGPT就训练好了,就能形成我们人类更期待的回答。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

0

主题

4

回帖

11

积分

新手上路

积分
11
发表于 2024-1-20 10:43:12 | 显示全部楼层
本文梳理技术路线和代码复现
前言

之前的内容中,将入门+跟进chatGPT-SOTA并形成自己的认知体系的内容做了整理:
叶兀:入门与Follow GPT的路径分析:LLM道阻且长,行则将至但由于篇幅所限,内容又涵盖了技术和非技术的部分,对于很多技术的介绍不够。本文就与大家一起梳理训练一个ChatGPT的流程和示例代码,做中学。基于此,通过对各个模块的优化改善,就可以得到自己的模型。尽管当前开源实现已经茫茫多,但如果想要做出创新或者优化,甚至理解模型的一些表现,也需要对基础的底层实现有所了解。
本文一共分为五个小节,第一节核心是以GPT为例梳理实现一个基座模型LLM;第二节则是介绍增量学习与微调,同时介绍了SFT和alignment(对齐)两个在chatGPT中发挥重要作用的概念,到此为止就可以拿到一个表现不错的chatllm了;第三节介绍奖励模型和PPO,这部分让人类的参与释放了出来,使得模型自优化“对齐”变得自动化;第四节针对一些微调的关键点进行分析,并对训练使用的数据相关要点做了一个整合。第五节是一个小结,将一些信息做了回顾。
PS:多卡的实验我没有做,因为穷(求包养 谢谢~
代码link见 第五节
第一节:实现一个LLM

实现一个LLM,首先需要实现一个LM,基于之前对语言模型的介绍
叶兀:ChatGPT原理介绍:从语言模型走近chatgpt我们这里跳过细节,直接实现自回归的神经语言模型。Autoregressive Language Models,当前我们以GPT为代表,本质学习的是给定前序队列,然后预估下一个单词是什么的问题。为了深入了解GPT是怎么进化到ChatGPT的,我们会从GPT1开始逐步升级直到GPT3。
PS:GPT刚出来的论文名叫Generative Pre-Training,这是GPT的全名,当前的盛况让我想到了熊猫和猫熊。
模型介绍
GPT4预计参数量1-1.7w亿,支持文本和图像,输出文本(但是可以支持编程绘图),在各项任务上表现更好
GPT3.5(instructGPT和chatGPT)1750亿参数,文字输入输出;规范了Alignment这个概念,规范了训练流程:SFT、RLHF(RW+PPO);基于上文,我们看到这里集合了WebGPT和CodeX的优点。
GPT31750亿参数,文字输入输出。提出in-context learning(0/few-shot)
GPT215亿参数,文字输入输出。弱化版GPT3,也是大家摸索GPT3的重要参考
GPT11.17亿参数,文字输入输出,无监督预训练,task oriented finetuning->下游任务上需要finetune,没有足够泛化性,同时finetune需要数据
GPT的模型结构设计

这个小节单独对模型细节做一次介绍,代码实现在后续单独讲解。
「GPT1明确-学习目标 」
论文中明确了学习目标,就是学习训练一个语言模型。给定一个数据集,最大化以下log-likelihood:

k表示上下文窗口设定,表示神经网络的参数,L(U)表示联合概率,注意对数函数相加,底数和对数的变化。 我们都知道GPT使用了Transformer的解码器部分来作为语言模型,该模型在输入上应用多头自注意操作,然后通过位置感知的前馈层生成目标token的输出分布。在整个模型架构中,可以表示为以下三个公式,同时我们在模型架构图中表示出了对应的输入和输出:

其中u是上下文的token的向量,n是层数,W_e是word embeeding matrix,W_p是position embedding matrix。


「GPT1奠定基础-模型结构设计」
要点归结如下,GPT-2会基于此进行一定修改:

  • 主要基于原始的Transformer工作使用了一个12层的deocoder-only Transformer模型,其中包括masked self-attention heads(768d和12个heads)
  • 对于position-wise feed forward networks,使用了3072d inner states
  • 使用了Adam优化器,最大学习速率为2.5e-4。学习率在前2000次更新期间线性增加,并基于cosine-schedule降至0
  • 模型采用残差、嵌入和注意力的dropout,正则化率为0.1。同时还采用了L2正则化的修改版本,其中所有非偏置或增益权重的w=0.01
  • 激活函数使用了GELU
  • 使用了基于学习的位置嵌入,而非transformer中的正弦版本
  • 我们使用ftfy库清理BooksCorpus中的原始文本,对一些标点和空格进行标准化,并使用spaCy分词器。
  • 由于Layernorm在整个模型中被广泛使用,所以简单的权重初始化N(0, 0.02)就足够了。
  • 数据输入:我们使用了一个BPE词汇表,其中包括40,000个merges
  • 训练设置:使用batchsize为64,seq长度为512的随机采样数据,训练了100个epochs
「GPT2-放大数据和模型参数」
We would like to move towards more general systems which can perform many tasks – eventually without the need to manually create and label a training dataset for each one.
牢记这一点,才能更好的利用LLM GPT-2 vs GPT-1的模型修改

  • 从一开始的表中我们看到GPT-2相比GPT-1有显著的模型参数上升。
  • 由于此时已经希望GPT-2是一个通才,可以zero-shot解决下游任务,所以构建了新的训练数据,清洗得到约40G
  • GPT-2基于GPT-1做了一些修改:

    • 将Layer Normalization移动到了每一个sub-block的前面(我甚至怀疑过,但后来发现官方开源代码确实是这样的 hhh
    • 在最后的self-attention block之后增加了一个额外的layer normalization
    • 考虑到模型深度造成的残差积累,对初始化做了修改。在初始化时,通过一个因子 1/√N 对残差层的权重进行缩放,其中 N 是残差层的数量。
    • 将词汇表扩展到50,257个词汇。
    • 上下文长度从512增加到了1024
    • 训练batchsize增加到了512

「GPT-3」
论文中号称使用与GPT-2相同的模型和架构,包括其中描述的修改初始化、预归一化和可逆标记化。
在transformer的层中,使用了Sparse Transformer,transformer中是用了alternating dense & locally banded sparse attention patterns。



表2.1显示了8个模型的大小和架构。其中,nparams是可训练参数的总数,nlayers是总层数,dmodel是每个bottleneck layer中单元的数量(始终使feedforward layer的大小是bottleneck layer的4倍,d_ff = 4 * d_model),dhead是每个注意头的维数。所有模型都使用n_ctx = 2048个标记的上下文窗口。
论文中提到:将模型沿着深度和宽度维度分配到GPU上,以最小化节点之间的数据传输。每个模型的精确架构参数是基于计算效率和在GPU布局中的负载平衡而选择的。先前的工作表明,在合理的范围内,validation loss对这些参数并不敏感。
至于本文很重要的工作,比如in-context learning,则与模型训练没有关系。
「GPT-4
Multimodal model:image & text->model->text。chatgpt的实现暂时不需要GPT-4。
代码实现

至此,我们就了解了一个Pre-train LLM的一些基本模型细节。GPT1和2在前几年都有很多很多开源实现,很有利于我们了解学习。由于当前没有足够的机器和数据,所以可以基于此跑一个小数据的小模型进行流程测试,这就让我想起当初实验Bert的时候了:
叶兀:如何训练并使用Bert【未完不用看】由于看到GPT1和GPT2的模型主要架构相差不大,而很多模型的细节调整本质是需要基于数据和训练框架进行适配的,所以当前直接对GPT-2的训练进行整理和复现。
NOTE:当前由于torch和huggingface的存在,导致我们在实际编码上变简单了很多,很多细节自己抄一遍会更有感觉。
基于上面我们提到的技术路线,可以很明确的将整个过程进行实现。在一个模型训练定义的时候,我们一般会将其分为几个模块:
model.py:模型定义
train.py:模型训练调用代码
inference.py:模型推理调用代码
conf.py:保存公共配置信息,譬如全局随机种子等
data:文件夹下包括数据预处理代码和一些常用demo数据,由于不同数据源格式不同,会有多个数据处理文件的可能。训练全量数据一般也可以保存在这里,只是不会放在github上。(PS:对于常用数据,可以保存在公共目录下,方便不同项目使用)
bash:文件夹下保存多种常用bash文件,可以直接运行
其他可选:基于模型复杂度等,有可能会拆分不同的代码文件来保存模型用到的公共代码 下面我们解释一下BPE和transformer_block
「BPE」
这部分简化实现复制自:https://www.cnblogs.com/wwj99/p/12503545.html
举例来说明,我们要对下面的字符进行编码: aaabdaaabac

  • 字节对 aa 出现的次数最多,所以我们将它替换成一个没在字符串中被用过的字符 Z ,ZabdZabac Z=aa
  • 然后我们重复这个过程,用 Y 替换 ab ,ZYdZYac Y=ab Z=aa
  • 继续,用 X 替换 ZY ,
  • XdXac X=ZY Y=ab Z=aa
  • 这个过程重复进行,直到没有字节对出现超过一次。当需要解码时,就将上述替换过程反向进行。
https://github.com/openai/tiktoken这个是openai实现的tiktoken
bpe的优势,是可以将vocab size 变多,但是每次输入就可以变短,这也是模型可以处理越来越长token的一种方式。坏处在于理解起来需要一个转化成本。同时这种操作可以避免OOV的问题。
实际使用中,可以用tiktoken来实现。 tiktok...tiktok...tiktok->tiktoken hhh
「Transformer_block」
关于self-attention和transformer的介绍也是比较古老了,这里我复制一下自己之前给Bert的介绍,做一些修改
数据输入:GPT用了两种输入进行相加输入到模型中:词向量参数,位置向量参数。并且位置向量的参数是可学习的。
一个transformerblock中包括了masked-multi-head-self-attention和feedforward,以及穿插在他们之间的normalization或者dropout等。下面我们介绍一下multiheadattention和feedforward。至于mask,在这个decoder-only的结构中,我们主要是用其来盖住当前timestep中后面的词语,不让解码器看到future words,避免用答案预估答案。
Multi-heads self-attention与feedforward




左边的图是一个self attention。即一个head,右边是多个head。
从结构中可以看到,Q,K,V就是我们输入的三个(句子词向量),从之前的词向量分析可知,输出向量大小从len -> len x hidden_size,即len x 768。
如果是self-attention,Q=K=V,如果是普通的attention,Q !=(K=V)。
上次说过了Q和K就是两个要比较的东西。在NLP中的attention中K和V一般是一样的【或许可以修改V得到一种新的attention,应该有了类似的研究了吧】
但是,不管用的是self-attention还是普通的attention,参数计算并不影响。因为在输入单头head时,对QKV的向量均进行了不同的线性变换,引入了三个参数,W1,W2,W3。其维度均为:768 x 64。为什么是64呢,
768/12 . 选择了12个头。所谓的12个头就是有12个scaled dot-product attention. 所以每个头的输入都是64维的。 得出:W1,W2,W3的维度都是768 x 64。为什么W123是一样的维度,因为他们的输入是一样的,本身就是同一个东西在做self-attention。 所以参数计算结果为:
一个head:768 * 768/12 * 3 输入768,本身维度的输入是768,输出是64,有三个
有12个head。所以有768 * 768/12 * 3 * 12
有一个线性变化的W,768 * 768
所以在multi-heads的参数量为768 * 768/12 * 3 * 12 + 768 * 768=1769472+589824=2359296
对应的每个W都会有一个相应维度的bias 12个 64 以及一个 768.合起来就是两个768

看到参数有W1,W2。其中用到了两个参数W1和W2,Bert沿用了惯用的全连接层大小设置,即4 * dmodle(768,上一层的输出,x就是768 * 1),为3072,因此,W1,W2大小都是为768 * 3072,2个为 2 * 768 * 3072=4718592。
有两个bisa,那么大小为3072*2
解释一下,W1负责把768变成3072, W2再变回来。BTW这个max就是relu
参考实现如下:
class CausalSelfAttention(nn.Module):
    def __init__(self, config):
        super().__init__()
        assert config.n_embd % config.n_head == 0
        # key, query, value projections for all heads, but in a batch
        self.c_attn = nn.Linear(config.n_embd, 3 * config.n_embd, bias=config.bias)
        # output projection
        self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=config.bias)
        # regularization
        self.attn_dropout = nn.Dropout(config.dropout)
        self.resid_dropout = nn.Dropout(config.dropout)
        self.n_head = config.n_head
        self.n_embd = config.n_embd
        self.dropout = config.dropout
        # flash attention make GPU go brrrrr but support is only in PyTorch >= 2.0
        self.flash = hasattr(torch.nn.functional, 'scaled_dot_product_attention')
        if not self.flash:
            print("WARNING: using slow attention. Flash Attention requires PyTorch >= 2.0")
            # causal mask to ensure that attention is only applied to the left in the input sequence
            self.register_buffer("bias", torch.tril(torch.ones(config.block_size, config.block_size))
                                        .view(1, 1, config.block_size, config.block_size))

    def forward(self, x):
        B, T, C = x.size() # batch size, sequence length, embedding dimensionality (n_embd)

        # calculate query, key, values for all heads in batch and move head forward to be the batch dim
        q, k, v  = self.c_attn(x).split(self.n_embd, dim=2)
        k = k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
        q = q.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
        v = v.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)

        # causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)
        if self.flash:
            # efficient attention using Flash Attention CUDA kernels
            y = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=self.dropout if self.training else 0, is_causal=True)
        else:
            # manual implementation of attention
            att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
            att = att.masked_fill(self.bias[:,:,:T,:T] == 0, float('-inf'))
            att = F.softmax(att, dim=-1)
            att = self.attn_dropout(att)
            y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)
        y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side

        # output projection
        y = self.resid_dropout(self.c_proj(y))
        return y
        
class Trans_Block(nn.Module):

    def __init__(self, config):
        super().__init__()
        self.ln_1 = LayerNorm(config.n_embd, bias=config.bias)
        self.attn = CausalSelfAttention(config)
        self.ln_2 = LayerNorm(config.n_embd, bias=config.bias)
        self.mlp = MLP(config)

    def forward(self, x):
        x = x + self.attn(self.ln_1(x))
        x = x + self.mlp(self.ln_2(x))
        return x第二节:增量学习与微调

增量学习

如果我们从头开始训练,明显成本过高,所以我们可以退而求其次,进行continue leanring。增量学习,有时候也叫做继续学习,是基于一个模型的进一步学习。在LLM场景下,其目的往往是进一步增强LLM的基础能力。学习方式也与预训练基本一致,加载好之前训练的模型,然后继续学习。在训练技巧上,有时候会有一些训练设置上的变化,要根据实际情况而定。
与Finetuning相比,CL在定义上更加倾向于轻目的倾向性,强调优化模型的通用语言能力
注意,在数据输入和模型设置上必须与原始模型一样。这部分的训练代码配置和微调基本一致,可以参见微调部分
微调finetune

所谓微调,与增量学习一样,即基于给定模型进行进一步学习。与增量学习上的唯一区别在于,其往往会有更强烈的领域/任务目的性。 微调的流程是从一个已经训练好的模型加载,然后开始训练,在训练设置时,一般以较小的学习率,这是为了避免对整体的大模型进行波动干扰,影响到原始的通用能力。同时训练所使用的数据往往会与自己的目的有关,譬如某个垂直领域,或者某种形式的对齐(例如iGPT或者cGPT) 下面是m1笔记本上训练代码示例,代码见最后:
# 训练
python train.py config/train_shakespeare_char.py --device=cpu --compile=False --eval_iters=20 --log_interval=1 --block_size=64 --batch_size=12 --n_layer=4 --n_head=4 --n_embd=128 --max_iters=100 --lr_decay_iters=2000 --dropout=0.0
# 推理
python sample.py --out_dir=out-shakespeare-char --device=cpuSupervised fine-tuning

GPT1在实际应用到下游任务的时候,需要进行fine-tuning来优化效果。在后面了解对齐任务的时候,这部分了解有帮助。 在第一部分的语言模型训练好之后,接下来进行监督学习。给定一个有标签的数据集C,X是输入token,y是label。 输入X经过预训练模型得到最终的transformer block的激活函数,之后将其喂入一个线性输出层(参数为W_y)中来预估label y

基于此,最大化以下目标函数

这里也提到了,将语言模型作为一个额外的目标进行优化可以提升监督学习模型的泛化性,加速收敛。


alignment(对齐)-SFT

基于上面的介绍,我们了解了如何进行模型的finetuning。同时OpenAI有明确的提出,对齐是让模型了解到人类的习惯,思想,说话方式和价值观。这里我们要让模型来学习人类针对一些prompt的回复,同样通过SFT。
那么训练数据应该如何构造?先看一下在预训练的时候数据应该如何构造
def get_batch(split):
    """
    GPT生成~
    这段代码定义了一个函数get_batch(split),用于生成输入数据(x)和目标数据(y)的小批量样本。
    首先,根据split参数选择使用训练集还是验证集的数据。如果split是'train',则使用训练集数据,否则使用验证集数据。
    然后,通过随机生成一个长度为(batch_size,)的整数张量ix,其取值范围是从data数据中减去block_size的长度。
    接下来,通过将索引ix应用于data,使用列表推导式生成输入数据x和目标数据y。具体而言,对于每个索引i,从data中截取长度为block_size的子序列作为输入x,从索引i+1到索引i+block_size+1的子序列作为目标y。
    最后,将生成的x和y转移到设备(通常是GPU)上,并返回这对数据作为函数的输出。
    这个函数的目的是为了生成模型训练过程中的小批量样本,其中batch_size表示每个批次的样本数量,block_size表示输入和目标序列的长度。
    """
    # generate a small batch of data of inputs x and targets y
    data = train_data if split == 'train' else val_data
    ix = torch.randint(len(data) - block_size, (batch_size,))
    x = torch.stack([data[i:i+block_size] for i in ix])
    y = torch.stack([data[i+1:i+block_size+1] for i in ix])
    x, y = x.to(device), y.to(device)
    return x, y
ok这段代码也很简单,基于此,我们思考一下alignment的数据应该如何构造,下面是找的一段开源的prompt指令训练数据:
{"instruction": "在以下文本中提取所有的日期。", "input": "6月21日是夏至,这是一年中白天最长的一天。", "output": "6月21日"}
{"instruction": "", "input": "请生成一个新闻标题,描述一场正在发生的大型自然灾害。\\n\n", "output": "\"强烈飓风肆虐,数百万人疏散!\""}
对于一个生成模型来说,他的输入应该是指令和input,输出就是output,由于基础的生成能力我们其实并不需要在这个过程中训练,所以我们就会有明确的训练目标:即基于给定的指令+input生成output。基于此,训练数据的生成代码可以是以output的结尾idx为标记开始构造预估目标。
def get_instruction_batch(split):
    # generate a small batch of data of inputs x and targets y
    data = train_data if split == 'train' else val_data
    ix = torch.randint(low=data.find("output") + 11, len(data) - block_size, (batch_size,))
    x = torch.stack([data[i:i+block_size] for i in ix])
    y = torch.stack([data[i+1:i+block_size+1] for i in ix])
    x, y = x.to(device), y.to(device)
    return x, y
但是我们想一想,似乎在使用chatgpt的时候,它会自动补全我们的一些没有写完或者没有写对的指令,所以这里的训练是可以直接保留一开始getbatch的数据构造代码的。
【绕了一下~】 至此,可以说就可以得到一个像模像样的chatGPT了。
第三节:RLHF:RM与PPO

至此,就已经完成了所有的预训练+SFT的工作,也就是说我们已经得到了一个初步可用的模型,也可以说就可以得到一个像模像样的chatGPT了。如果是基于开源模型微调的话,这个模型的效果或许已经和很多开源的模型效果相当,因为当前开源的工作也没有明确表示他们做过后续的RM和PPO的工作。
NOTE:为什么我们这里要用RL来学习用户反馈而不是继续用SFT呢?因为SFT的继续学习,鉴于目标函数,其依然是在学习一个概率,不同的数据是在改变概率分布。但我们这里的目标是进一步学习alignment。如果继续堆积SFT,无非就是让模型输出的习惯和内容更加符合“人话”,即语言模型本身的性能会越来越好。
而RM+PPO想要做的是让模型学习在N句都是人话的选项中,排序出哪些更好。相比SFT照葫芦画瓢,此时其中隐含的是一种价值观、事实性以及labeler偏好的判断,会让模型更不容易胡说八道。
SFT:侧重学习给定prompt下的语言表达形式
RLHF:侧重学习给定prompt下的语言偏好
在基于给定模型finetune的过程中,如果不断强调SFT的学习,可能会让RLHF性能下降;此时合理的情况应该是SFT和RLHF一起作用来实现对模型的迭代优化,甚至仅RLHF。
ref:https://www.youtube.com/watch?v=hhiLw5Q_UFg
流程

简单介绍一下这部分的工作流程,下图中的step2和3:




  • 收集对比数据,训练一个奖励(Reward)模型

    • 采样获取propmt和几个语言模型输出的结果,构成pair
    • 标注者对这些输出结果进行从好到坏的排序
    • 使用标注数据来训练奖励模型

  • 基于RW模型,使用RL对语言模型进行优化

    • 从训练数据中采样一个prompt
    • 语言模型针对这个prompt输出结果
    • 奖励模型为这个输出的结果计算一个分数
    • 语言模型利用这个计算出来的分数进行模型优化

从上面的流程中,我们可以看到:基于此再进行后续的工作,RW+PPO,则是将对人类的依赖释放,利用RW+PPO不断进行模型优化。下面我们分别介绍奖励模型、PPO算法以及RLHF的流程与建模。
Reward模型训练

为了实现RM+PPO,首先我们需要训练一个Reward模型,训练数据的格式:(prompt, winning_response, losing_response)。

  • Prompt(提示): 表示输入的问题或上下文。
  • Winning response(获胜回答): 表示模型认为是正确或优秀的回答。
  • Losing response(失败回答): 表示模型认为是错误或较差的回答。
  • 数据规模:10万到100万个example

    • InstructGPT:50,000个prompt。每个prompt有4到9个response,形成了6到36个(winning_response, losing_response)pair。这意味着在(prompt, winning_response, losing_response)格式中有30万到180万个训练示例。
    • Constitutional AI,可能是Claude(Anthropic)的backbone:318,000个comparison pair(其中13.5万个由人类生成,18.3万个由人工智能生成)。Anthropic还有一个旧版本的数据开源(hh-rlhf:https://huggingface.co/datasets/Anthropic/hh-rlhf),其中大约包含17万个comparison pair。

训练设置:
rθ:被训练的奖励模型,由参数θ进行参数化。训练过程的目标是找到使损失最小化的θ。

  • 训练数据的格式如下:

    • x:提示(prompt)
    • yw:获胜回答(winning response)
    • yl:失败回答(losing response)

  • 对于每个训练样本(x,yw,yl):

    • sw = rθ(x, yw):获胜回答的奖励模型得分
    • sl = rθ(x, yl):失败回答的奖励模型得分
    • 损失值的计算公式为:−log(σ(sw−sl))

  • 目标是找到参数θ,以最小化所有训练样本的期望损失,即−Exlog(σ(sw−sl))。
这里我们的奖励模型可以基于之前训练好的SFT进行,即参考GPT-1时代介绍过的finetune工作,来实现对训练样本的打分。
PPO

PPO是OpenAI推出的RL算法,其提出的目的是为了解决Policy Gradient中低效与更新不稳定的问题,具体特点如下:

  • Mini-batch training:由on-policy修改成为off-policy,可以提升对受限数据集的使用效率
  • Regularization KL:PPO利用了KL作为约束来避免对小型数据集的过拟合
  • Clip Objective:使用了clip来避免不稳定的变化,也减少了过拟合的风险
PPO在chatGPT中作用的节点如下



关于上图中左边绿色的部分,严格意义上应该是一个指令微调后的模型,如果我们直接用RL进行模型调整的话,一方面不一定能够很好的针对给定的prompt生成想要的格式,特别是一个general的LLM要应对对话这种形式,另一方面效率这样的调整效率也不够高。SFT与RL实际要做的事情的侧重还是不同的,上面我们已经分析过了。
Meta与CMU也放出了相应的研究,即使没有经过RLHF的训练,仅仅通过详细的SFT也能够拿到很好的效果。论文见:https://arxiv.org/pdf/2305.11206.pdf
关于PPO详细介绍可以看论文,也推荐这个视频进行了解:https://www.bilibili.com/video/BV1sg4y1p7hw/?vd_source=510479c0ff6cd8893c73f934b4f22
RLHF

这里回顾一下SFT的流程:从prompt数据集中采样prompt,然后由标注人员进行标注,最终使用prompt和标注结果构成的数据来finetune语言模型。
综上,整个流程中标注人员参与的环节有SFT和RW模型训练的环节。也就是在线上实际应用模型的时候,SFT和RW模型训练这两个环节是可以将用户反馈的信息引入并进行模型优化的。
「训练流程」
这里,我们进一步训练SFT模型,生成的输出回答将最大化奖励模型的得分。OpenAI使用Proximal Policy Optimization (PPO)进行这部分的训练。在此过程中,随机选择一批prompt,将每个prompt输入到LLM中,得到一个回答,并由RM给出得分,基于给定的得分进行模型参数更新。
从这个阶段得到的模型不应该与SFT阶段得到的模型相差太远,这一点在下面的公式中以两个模型的KL散度作为约束条件表现。其原因是对于任何给定的prompt,有许多可能的回答,其中绝大部分奖励模型以前从未见过。对于那些未知的(prompt,answer)对中的许多情况,奖励模型可能会错误地给出极高或极低的得分。如果没有这个约束条件,我们可能会偏向那些得分极高的回答,尽管它们可能不是好的回答。
「强化学习建模」

  • 动作空间(Action space):LLM使用的词汇表中的token。执行动作意味着完成一次要生成的token的选择。
  • 观察空间(Observation space):所有可能的prompt的分布。
  • Policy:给定一个observation(prompt)下的所有可能采取的action的概率分布。LLM就是这个Policy,因为它会决策一个token有多大的可能性被选择。
  • 奖励函数(Reward function):奖励模型,就刚才我们讲过的reward模型训练
  • 训练数据:随机选择的prompt。
  • 数据规模:10,000 - 100,000个prompt(InstructGPT:40,000个prompt。)
「数学表示」

  • RM: 奖励模型
  • LLM^SFT: SFT的结果,有监督微调模型。

    • 给定prompt x,它输出一系列回答的分布。
    • 在InstructGPT论文中,LLM^SFT被表示为πSFT,这是因为在强化学习的建模中经常这样表示

  • LLM^RL_ϕ: 使用强化学习训练的模型,由参数ϕ进行参数化。

    • 目标是找到使得根据RM得分最大化的ϕ。
    • 给定prompt x,它输出一系列回答的分布。
    • 在InstructGPT论文中,LLMRLϕ被表示为πRLϕ。原因同上

  • x: prompt
  • D_RL: 明确用于RL模型的prompt分布。
  • D_pretrain: 预训练模型的训练数据分布。 对于每个训练步骤,从D_RL中抽取一个x_RL批次,从D_pretrain中抽取一个x_pretrain批次。每个样本的目标函数取决于样本来自哪个分布。 x_RL = prompt_batch x_pretrain = batch of seqs
「详细训练步骤如下:」

  • 对于每个x_RL(即prompt),使用LLM_RL生成回复y。objective方程如下,公式第二项是KL散度,目的是为了不让RL训练后的模型与SFT差异过大。
2. 对于每个x_pretrain,目标函数的计算如下。从直观上讲,这个目标是确保RL模型在文本完成任务上表现不会比预训练模型更差。

在这里,x_pretrain表示预训练数据集中的样本(例如,预定义的对话数据)。目标函数objective2的计算涉及使用LLMRLϕ模型对xpretrain进行采样,并计算其对数概率。
通过最大化这个目标函数,我们希望确保RL模型在文本完成任务上的表现不会比预训练模型更差。这有助于保持模型的基本能力,并防止在优化过程中产生负面效果。通过控制目标函数中的γ参数,可以调整这个任务对优化过程的相对重要性。
3. 最终的目标是以上两个公式之和。在RL设置中,最大化objective作为我们的学习目标

Tips:已知当前基于RLHF的思路和流程,但实际效果不一定是最优的,需要根据实际情况来看。这个情况在OpenAI的WebGPT中也同样有所讨论
第四节:微调的关键要点

在我们的实际开发和应用过程中,大多数人的工作都是基于开源模型的微调优化;即使是当前一些开源出来的模型,也有很多模型基底是基于开源进行的,毕竟大把的时间和金钱,还是能省则省。这一节,我们讨论一些流行的微调方法以及并对各个模块的优化点进行梳理,并将数据相关工作也做了简单讨论。
开源基底模型

当前业界开源模型主要由外国研究者贡献,国内当前开源较为有代表性的有复旦的MOSS,清华的chatGLM以及近期火热的RWKV等工作。 在前序内容中我们介绍了GPT系列工作的架构,这也是当前大多数模型的架构。由于之前的文章已经提供过了一些基础的模型介绍,如何入门 GPT 并快速跟上当前的大语言模型 LLM 进展?,包括当前模型百花齐放,每天都有新模型~,所以我们就不重复介绍了,随手一搜就是一大片。
在之前介绍的基础上,新加两个工作:
微调手段

本节图片主要来自于chatGLM的微调教程PPT。
「全参数微调」
所谓全参数微调,就是对模型的所有参数都进行了调整,也是上文中提到的SFT的实现方式。由于在实际中存在客观困难(模型大,数据多,参数多)等,所以我们有一些优化的微调手段。
混合精度微调:混合精度是指训练时在模型中同时使用 16 位和 32 位浮点类型,从而加快运行速度,减少内存使用的一种训练方法。通过让模型的某些部分保持使用 32 位类型以保持数值稳定性,可以缩短模型的单步用时,而在评估指标(如准确率)方面仍可以获得同等的训练效果。现代加速器使用 16 位 dtype 执行运算的速度更快,因为它们有执行 16 位计算的专用硬件,并且从内存中读取 16 位 dtype 的速度也更快。实际工作中要注意的点:

  • 实际工作中,经常在内存中用fp16做储存和乘法从而加速计算,用fp32做累加避免下溢出误差
  • 为了避免梯度过小,可以手动在避免梯度爆炸的基础上,在梯度上进行放大,这样当实际发生fp16和fp32的计算的时候,由于我们放大了值,就不容易出现下溢出。
  • 小结:省显存,加快训练速度,但是会丢失精度


「并行训练」

  • Data Parallel:多张卡使用相同的模型,一张卡跑一部分batch,各自bp计算gradient,然后整体求均值,然后各自进行优化然后进行参数更新,这样的好处是速度比较快,通信很少。缺点是浪费了memory,因为每张卡都要load同样的模型,都要计算一次gradient。同时如果模型太大,单卡太小也放不进去。
  • Model Parallel:将一个模型拆分到不同的卡上,使用同一个batch的数据,在实际训练的时候将中间结果在卡与卡之间移动,分别训练模型的不同部分。优点是克服了上面说的缺点,但由于这种频繁的通讯,也导致了整体的计算效率的问题。
  • ZeRO: ZeRO(The Zero Redundancy Optimizer)是一种用于大规模分布式深度学习的新型内存优化技术。ZeRO可以在当前的GPU集群上训练具有1000亿个参数的深度学习模型,其吞吐量是当前最佳系统的三到五倍。它有三个优化步骤,分别对应Optimizer State Partitioning, Add Gradient Partitioning以及Add Parameter Partitioning。简单来说就是将每个步骤都拆分到不同的卡上,相比baseline(data parallel), GPU可以节约很多。


「P-tuning」


P-tuning v1 主要结构是利用了一个prompt encoder(例如BiLSTM+MLP),将prompt 通过encode得到向量,然后再与input embedding进行拼接,具体拼接位置不一定是前缀,也可以是中间位置。其初衷是为了增加模型的理解能力。
P-tuning v2 则是一个改进,不仅会添加prompt的embedding调整,还会对应在每一层中都加上prompt对应的权重参数,然后对这部分进行调优(实际实现中有一种很好理解的方式,就是要额外拼接一部分参数,但这种生效方式很不优雅,可以参见chatGLM中的代码实现https://huggingface.co/THUDM/chatglm-6b/commit/812f43f9ff5becd90e7a607fad51e5cdc4d664df,添加到了每一层中 以及苏剑林在P-tuning讨论过的实现方式https://zhuanlan.zhihu.com/p/364141928
P-tuning v2这种方式,可能会有灾难性遗忘以及过拟合的问题,因为其往往用了比较少的数据(这也是其优点)实现高性价比的finetune。
「LoRA」
LoRA(Low-Rank Adaptation)是一种用于对大型模型进行低成本微调的方法。基本原理是冻结预训练好的模型权重参数,在冻结原模型参数的情况下,通过往模型中加入额外的网络层,并只训练这些新增的网络层参数。由于这些新增参数数量较少,这样不仅 finetune 的成本显著下降,还能获得和全模型微调类似的效果。


当我们训练大型的语言模型时,它通常有非常多的参数,这使得训练过程非常耗时和昂贵。LoRA方法的核心思想是,这些大型模型其实是过度参数化的,其中的参数变化可以被视为一个低秩矩阵。因此,我们可以将这个参数矩阵分解成两个较小的矩阵的乘积。在微调过程中,我们不需要调整整个大型模型的参数,只需要调整低秩矩阵的参数。这样做可以显著减少微调所需的参数数量,从而降低训练成本。
具体来说,我们假设大型模型的参数矩阵A可以分解为两个较小矩阵U和V的乘积:A = UV。我们将U和V看作是微调的参数,而A是固定的。实际作用中,前向传播时A和UV都会计算作用,将最终的结果相加,然后进行损失计算。接下来进行反向微调,此时仅仅调整UV即可。通过调整U和V的数值,我们实际上是在微调整个模型。一旦我们调整好了矩阵U和V的数值,可以将U和V的乘积作为一个低秩矩阵的近似,记作UV^T。然后,我们将UV^T与原始参数矩阵A进行加法操作,即 A_new = A + UV^T。最终用A_new替换掉A,就得到了新的调整后的模型
下图中的W0就是A,delteW就是UV。


这种低秩矩阵的分解方法可以有效地减少微调所需的参数量,因为U和V的大小较小。这使得微调过程更加高效和经济。同时,由于大部分参数是固定的,我们可以更快地完成微调过程。
chatGLM推出了微调的教程,参见:https://www.bilibili.com/video/BV1fd4y1Z7Y5/?vd_source=132b252bcf85dbd4cad2015817e0f42b(赞),其中也附带了代码。
优化点

除了微调以外,还有很多地方可以优化,简单列举如下
训练优化:当前的训练成本依然很高,如何能够高效省钱的完成训练,除了训练中的一些tricks以外,软硬件结合等相关的操作或许也会对未来的微调有所影响。
模型优化:模型结构上我们依然存在优化点,比如当前的transformer是最优的的么,参见RWKV:https://github.com/BlinkDL/RWKV-LM;GLM的模型细节也和GPT有所不同;T5这样的架构效果究竟如何?
推理优化:当前已经有很多人在研究如何在消费级显卡上进行推理,也有人研究进行端上的部署,这块如何在确保模型效果的基础上,实现低成本部署则是会很大程度上影响到模型最终的应用和落地,以及未来应用场景的拓展。
tokenizer优化:在不同的语言中,不同的tokenizer或许会有不同的效果;同时 tokenizer的存在是否是足够有价值的,也是当前被诟病的一点(https://twitter.com/karpathy/status/1657949234535211009),比如


生成速度优化:从左往右的生成具有速度上的问题,可以参见https://arxiv.org/pdf/2205.07459.pdf 字节跳动DA-transformer在生成上做到了加速。
上下文长度:Vcc: Scaling Transformers to 128K Tokens or More by Prioritizing Important Tokens将上下文长度做了延伸 https://arxiv.org/abs/2305.04241(当前还在不断研究扩展中)
数据

训练数据非常重要,可以说是决定了模型性能的天花板,这一点已经被所有人公认了。但是当前有个关键点是数据、模型和效果三者之间的关系暂时还无法做出具体建模。有研究指出当前GPT的模型其实还没有被完全训练到,当前使用的训练数据还不足够。而数据(规模,质量)要如何与模型结构以及参数搭配才能得到最好的效果(还有具体的ROI上的问题),这块的研究暂时还没有结论,也是一个非常有前(需要钱)景的研究方向。
关于数据这块,相信也是各个机构都在认真思考和研究的方向,当前关于数据的很多问题其实也没有明确的答案,也处在一个讨论的阶段:

  • 预训练阶段:建立语言模型的基础通用能力,也是最消耗算力和数据的部分。

    • 要如何对数据进行清洗,比如数据去重、数据normalization等操作。
    • 数据质量:如何衡量数据的质量,比如红楼梦等四大名著和当代一篇长文网络小说,古诗词和现代口水歌之间的优劣。
    • 数据规模,在算力有限的情况下,数据规模应该如何选择

  • SFT+RLHF:

    • 数据标注机制如何设计才能提升数据质量
    • 有没有一些trick的思路来获取数据
    • 增量数据或者finetune的数据要如何与trained模型结合

  • 数据自动合成与寻找:针对一个模型,随着训练的进行,可以学习的数据会越来越少;而模型训练者需要明确了解模型的能力,然后帮助其寻找到对它有用的数据。这个过程理论上是可以自动化进行的。
  • 评估数据:如何设计评估数据,如何获取评估数据,评估维度等,由于这个方向除了数据还会涉及到对模型评估的维度或者具体的评估标准的设计问题,所以不单是一个简单的数据问题。这里可以见当前
  • 部分已知开源数据:严格意义上,我们需要将数据分为预训练数据集和对齐数据集两部分。详细的开源数据有很多,限于精力这里就不穷举了,很容易可以找到很多。

第五节:小结

这个内容可以分为三大块,第一节与第二节属于大语言模型的基础知识,具备这些逻辑上就可以自己训练得到一个LLM;第三节则是对RLHF进行了拆解,这是chatGPT避免“胡说八道”重要的一个步骤,但也是当前不太好评估效果的一个维度;第四节则是给出了微调的要点,也是当前要开展模型相关工作时候必须了解的内容。
本文没有详细介绍的包括:

  • 数据收集与校验机制:机制上如何获取数据、标注数据质量如何从机制上优化、数据清洗trick,数据收集和处理pipeline
  • RLHF实现细节:Reward模型训练与基于PPO进行LLM优化的实现细节。
  • 工程优化相关细节。
关于文中提到的第一第二小节的代码见:https://github.com/DukeEnglish/chatllm。后续会添加RLHF部分的代码和细节。
后续TODO:

  • 增加基于中文数据的模型训练和微调
  • 增加RLHF的详细实现
  • 以某个开源中文模型为例进行上述流程测试
NOTE:文中少数部分内容与chatgpt进行过一定交流
附录

原始gpt-1代码:https://github.com/openai/finetune-transformer-lm (手动实现了transformer,但是依赖了旧版tensorflow)
原始gpt-2代码:https://github.com/openai/gpt-2
MAC环境配置可以参考:https://zhuanlan.zhihu.com/p/548685817
当前我们这个行业也存在一个问题,大家代码和论文有时候不知道是故意还是不小心,会有一些版本不重合(就像各位的注释、文档和代码的不一致一样,嘿嘿 手动狗头)这导致的是在学习的时候,搞不明白代码为啥这样写,为啥跟论文写的不太一样,而且不一样的地方又很少……。
参考

本文参考了很多网络资料,重点参考内容包括但不限于下列内容,感谢大佬们的分享:
https://www.bilibili.com/video/BV1tm4y1h7e7/?spm_id_from=333.337.search-card.all.click
https://www.jiqizhixin.com/articles/2023-05-08-3
https://www.cnblogs.com/wwj99/p/12503545.html
https://www.youtube.com/watch?v=kCc8FmEb1nY
https://www.qbitai.com/2023/01/41445.html
https://medium.com/pytorch/colossalchat-an-open-source-solution-for-cloning-chatgpt-with-a-complete-rlhf-pipeline-5edf08fb538b
https://github.com/Morizeyao/GPT2-Chinese/blob/old_gpt_2_chinese_before_2021_4_22/requirements.txt
https://huyenchip.com/2023/05/02/rlhf.html#3_1_reward_model
https://www.zhihu.com/column/c_1451236880973426688
https://arxiv.org/pdf/2009.01325.pdf
https://www.bilibili.com/video/BV1fd4y1Z7Y5/?vd_source=132b252bcf85dbd4cad2015817e0f42b
https://yaofu.notion.site/C-Eval-6b79edd91b454e3d8ea41c59ea2af873
https://blog.csdn.net/qq_41771998/article/details/129958753

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|T9AI - 深度人工智能平台 ( 沪ICP备2023010006号 )

GMT+8, 2024-11-23 21:27 , Processed in 0.061207 second(s), 23 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表