w~大模型~合集11

张裕  论坛元老 | 2024-10-27 02:43:41 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1407|帖子 1407|积分 4221

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

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

x
我自己的原文哦~ https://blog.51cto.com/whaosoft/12363148
#LLM~幻觉

最近,来自哈尔滨工业大学和华为的研究团队发表了一篇长达50页的综述,过细土地货了有关LLM幻觉问题你该知道的全部事。
幻觉,老朋友了。
自打LLM进入我们的视野,幻觉问题就一直是一道坎,困扰着无数开辟人员。
固然,有关大语言模型幻觉的问题已经有了无数研究。
最近,来自哈工大和华为的团队发表了一篇50页的大综述,对有关LLM幻觉问题的最新希望来了一个全面而深入的概述。
这篇综述从LLM幻觉的创新分类方法出发,深入探究了大概导致幻觉的因素,并对检测幻觉的方法和基准进行了概述。
这此中肯定也少不了业内比较有代表性的减轻幻觉的方法。
论文地址:https://arxiv.org/abs/2311.05232
下面,我们就来看一看本篇综述中主要讲了些什么内容。
想深入学习的朋友,可以移步文章底部的参考链接,阅读论文原文。
幻觉大分类
首先,先来看看有哪些种类的幻觉。


上图中,左边是事实性的幻觉。当LLM被问到谁是第一个在月球上闲步的人时,LLM编了个人物出来,乃至还说得有模有样。
右边则是文本摘要模型中的忠实度问题,可以看到LLM在看到这段新闻后,直接把年份概括错了。
在本篇综述中,研究人员深入分析了LLM中幻觉的起源,涵盖了从数据、练习到推理阶段的一系列促成因素。
在这一框架内,研究人员指出了与数据相干的潜在缘故原由。例如,有缺陷的数据源和未优化的数据利用,或是在预练习和对齐过程中大概会诱发幻觉的练习计谋,以及源于解码计谋的随机性和推理过程中不完善的表征等等。
此外,研究人员还全面概述了专为检测LLM中的幻觉而设计的各种有效方法,以及与LLM幻觉相干的基准的过细概述,和作为评估LLM产生幻觉的水平和检测方法有效性的试验平台。
下图即为本篇综述所涉及到的内容、前人研究,以及论文。


下图是一张更为详细的LLM幻觉种类图。


在事实型幻觉和忠实度幻觉下,还包罗更为过细的分类。
事实型幻觉:
a)事实不一致
当问LLM,谁是第一位登月的人时,LLM答复说是加加林,而非阿姆斯特朗。这种属于答案与事实不一致,因为确有加加林其人,所以不属于捏造。
b)事实捏造
当让LLM介绍一下独角兽的起源时,LLM并没有指出天下上没有独角兽这种生物,反倒是编了一大段。这种现实天下中没有的,称之为捏造。
忠实度幻觉又包罗:指令-答案的不一致、文本不一致,以及逻辑不一致。
a)指令-答案不一致
当LLM被要求翻译一个问句时,LLM输出的答案现实上答复了问题,没有进行翻译。因此是一种指令和答案的不一致。
b)文本不一致
这类不一致更多出如今概括类任务中。LLM大概会罔顾给出的文本,总结一个错的出来。
c)逻辑不一致
在被要求给出2x+3=11的方程解法时,第一步LLM指出,两边同时减去3,得到2x=8.接下来在两边除以2的操作中,LLM输出的答案是3.
8除以2怎么会即是3呢?
幻觉产生原理
数据
接下来,综述开始梳理有关幻觉产生原理的内容。


第一类,数据问题。
·错误信息和偏见。鉴于对大规模语料库的需求日益增长,启发式数据收集方法被用来有效收集大量数据。
这种方法在提供大量数据的同时,大概会无意中引入错误信息,增加出现模仿性错误的风险。此外,社会偏见也会在无意中被引入LLMs的学习过程。
这些偏差主要包罗重复偏差和各种社会偏差(Social Biases)。
要知道,LLM预练习的主要目的是模仿练习分布。所以当LLM在事实不正确的数据上担当练习时,它们大概会无意中放大这些不准确的数据,从而大概导致事实不正确的幻觉。
神经网络,尤其是大型语言模型,具有影象练习数据的内在倾向。研究表明,这种影象趋势会随着模型规模的扩大而增强。
然而,在预练习数据中存在重复信息的环境下,固有的影象本领就会出现问题。这种重复会使 LLM 从泛化转向影象,最终产生重复偏差,即LLM会过度优先回想重复的数据,导致幻觉,最终偏离所需的内容。
除了这些偏见,数据分布的差别也是产生幻觉的潜在缘故原由。
下一种环境是,LLM通常会存在知识边界。
固然大量的预培训语料库为法律硕士提供了广泛的事实知识,但它们自己也有局限性。这种局限性主要体如今两个方面:缺乏最新的事实知识和专业领域知识。
虽说LLM在通用领域的各种卑鄙任务中表现出了卓越的性能,但由于这些通用型LLMs主要是在广泛的公开数据集上进行练习,它们在专业领域的专业知识受到缺乏相干练习数据的内在限制。
因此,当碰到需要特定领域知识的问题时,如医学和法律问题,这些模型大概会表现出显着的幻觉,通常表现为捏造事实。
此外,还有过时的事实知识。除了特定领域知识的不敷,LLMs知识边界的另一个内在限制是其获取最新知识的本领有限。
蕴含在LLM中的事实知识具有明确的时间界限,随着时间的推移大概会过时。
这些模型一旦经过练习,其内部知识就永远不会更新。
而鉴于我们这个天下的动态性和不断变化的本质,这就构成了一个挑战。劈面临超越其时间范围的领域知识时,LLMs每每会接纳捏造事实或提供过去大概正确,但如今已经过时的答案的方法来试图「蒙混过关」。
下图中,上半部分即为LLM缺失特定领域内的专业知识——phenylketonuria(苯丙酮尿)。
下半部分即为最简朴的一个知识过时的案例。2018年韩国平昌举行冬奥会,2022年北京举行冬奥会。LLM并没有有关后者的知识储备。


由此可见,LLM中与数据有关的幻觉主要源于错误的数据源和不佳的数据利用环境。数据源中的错误信息和固有偏差不光会流传模仿性虚假信息,还会引入有偏差的输出,从而导致各种形式的幻觉。
在处理特定领域的知识或碰到快速更新的事实知识时,LLM所拥有知识的局限性就会变得很显着。
在数据利用方面,LLMs 每每会捕获到虚假的相干性,在回想知识(尤其是长尾信息)和复杂推理场景中表现出困难,从而进一步加剧幻觉。
这些挑战突出表明,亟需进步数据质量,增强模型更有效地学习和回想事实知识的本领。
练习
如今,综述把目光转向LLM的练习阶段。
LLM的练习过程主要包罗两个主要阶段:
预练习阶段,LLMs在这一阶段学习通用表征并捕获广泛的知识。
对齐阶段,LLMs在这一阶段进行调解,以更好地利用户指令和人类的根本价值观保持一致。固然这一过程使LLM 具备了还算不错的性能,但这些阶段中的任何不敷都大概无意中导致幻觉的发生。
预练习是LLM的基础阶段,通常接纳基于transformer的架构,在庞大的语料库中进行因果语言建模。
然而,固有的架构设计和研究人员所接纳的特定练习计谋,大概会产生与幻觉相干的问题。如上所说,LLM通常接纳基于transformer的架构,遵循GPT建立的范式,它们通过因果语言建模目的获取表征,OPT和Llama-2等模型都是这一框架的典范。
除了结构缺陷,练习计谋也起着至关紧张的作用。值得注意的是,自回归天生模型的练习和推理之间的差别导致了暴露偏差(Exposure Bias)现象。
而在对齐阶段,一般涉及两个主要过程,即监视微调和从人类反馈中强化学习(RLHF),是释放LLM本领并使其符合人类偏好的关键一步。
固然对齐能显著进步 LLM 相应的质量,但也会带来产生幻觉的风险。
主要分为两方面:本领不对齐和信心不对齐(Capability Misalignment、Belief Misalignment)。
如何检测幻觉?
检测LLM中的幻觉对于确保天生内容的可靠性和可信度来说至关紧张。
传统的衡量尺度主要依赖于词语重叠,无法区分可信内容和幻觉内容之间的细微差别。
这一挑战凸显了针对LLM幻觉接纳更先辈的检测方法的必要性。研究人员指出,鉴于这些幻觉的多样性,检测方法也相应地有所差别。 
这里仅详细介绍一例——
·检索外部事实
如下图所示,为了有效地指出LLM输出中不准确的事实,一种比较直观的计谋是,直接将模型天生的内容与可靠的知识来源进行比较。
这种方法与事实查抄任务的工作流程非常吻合。然而,传统的事实核查方法每每出于实用性考虑而接纳了简化假设,导致在应用于复杂的现实天下场景时有大概会出现偏差。
在熟悉到这些限制因素以后,一些研究者提出,要更加重视真实天下的场景,即从时间受限、未经整理的网络资源中获取证据。
他们首创了一种全主动的工作流,集成多个组成部分,包罗原始文档检索、细粒度检索、真实性分类等等。
固然,还有不少其他研究者提出了另外一些办法,比如FACTSCORE,专门用于长文本天生的细粒度事实度量。


别的方法还包罗不确定性估计,如下图所示。


有关忠实度幻觉的检测,也有不少相干研究,如下图所示。


此中包罗基于事实度量:通过检测天生内容与源内容之间的事实重叠度来评估忠实度。
基于分类器的度量:利用经过练习的分类器来区分天生内容与源内容之间的关联水平。
基于QA的度量方法:利用问题解答系统来验证源内容与天生内容之间的信息一致性。
不确定性估计:通过测量模型对其天生输出的置信度来评估忠实度。
基于prompt的度量方法:让LLM充当评估者,通过特定的prompt计谋来评估天生内容的忠实度。
之后,哈工大团队还将较为前沿的减轻幻觉的方法进行了整理,针对上述提到的各类问题,分别提供可行的办理办法。


总结
总而言之,在论文的最后,哈工大的研究人员表现,在这份全面的综述中,他们对大型语言模型中的幻觉现象进行了深入研究,深入探讨了其潜在缘故原由的复杂性、开创性的检测方法和相干基准,以及有效的缓解计谋。
固然开辟者们在这个问题上已经有了不少进步,但大型语言模型中的幻觉问题仍旧是一个令人关注的持续性问题,需要继续研究。
此外,本篇论文还可以作为推进安全可信的AI的指路明灯。
哈工大团队表现,希望通过对幻觉这一复杂问题的探索,为这些有志之士提供宝贵的见解,推动AI技术向更可靠、更安全的方向发展。
参考资料:
​​https://arxiv.org/abs/2311.05232​​





#LLM-FP4

这篇文章给出了大模型 FP 量化的办理方案。
大语言模型 (LLM) 压缩一直备受关注,后练习量化(Post-training Quantization) 是此中一种常用算法,但是现有 PTQ 方法大多数都是 integer 量化,且当比特数低于 8 时,量化后模型的准确率会下降非常多。想较于 Integer (INT) 量化,Floating Point (FP) 量化能更好的表现长尾分布,因而越来越多的硬件平台开始支持 FP 量化。而这篇文章给出了大模型 FP 量化的办理方案。文章发表在 EMNLP 2023 上。办理LLaMA、BERT等摆设困难:首个4-bit浮点量化LLM来了


  • 论文地址:https://arxiv.org/abs/2310.16836
  • 代码地址:https://github.com/nbasyl/LLM-FP4
要了解本文,必须要先具备根本的有关 Floating Point Format 以及 Floating Point Quantization 的知识,首先 Floating Point Number 可以用以下公式表现:


s 代表正负符号位 (sign bit),m 代表尾数位 (mantissa bits),e 代表指数位 (exponent bits)。p 是一个介于 0 到 2^e - 1 之间的值,用来表现当前数字该被分别到哪一个指数区间,d 取 0 或 1 的值,用来表现第 i 个 mantissa bit。b 是 bias,一个用来调解 exponent 区间的整数值。
接下来介绍 Floating Point Quantization 是怎么运作的,首先输入值必须经过一个 scale and clip 的步骤,先把 input clip 到 Floating Point 能表现的最大区间 (±Qmax),如以下公式所示:


可以看到类似于 integer 量化,FP 量化也会加入一个 full-precision 的缩放因子 (scaling factor) 来缩放 input 到合适的区间。而缩放因子在运算矩阵乘法的时间,和低比特的矩阵乘法分开盘算,所以并不会造成很大的 overhead。融入了这个 full-precision 的缩放因子之后,差别的 quantized tensor 可以或许被相应地 clip 到差别的最大最小值区间。在现实利用过程中,会根据输入 tensor 的值域确定需要的量化区间,然后利用公式 (4) 推导出相对应的 bias。注意公式 (4) 里的 bias 可以被用作实数值的缩放因子,见公式 (2)(3)。
Floating-Point Quantization 的下一个步骤是在决定好量化区间后把区间内的值分配到相对应的量化区间内,这个步骤称为 Compare and Quantize:


上图直观分析了量化的过程,当前的输入值,在用公式 5 比较过后,量化到差别的量化区间中。
在得到量化过的 activation 和 weight 后,这里的 scaling factor 提到前面先盘算,而到达如下的 efficient matrix multiplication,完成矩阵乘法的加速:


接着本文指出 FP 量化的准确度,和 exponent bits 的设定以及量化的区间息息相干。
如下图所示,差别的 FP format (浮点数的指数位 / 尾数位设定) 之间存在巨大的量化偏差差别,只有当选取合适的 FP format 时,FP Quantization 比 INT Quantization 能更好的表现长尾分布。这个现象也在之前的论文中得到验证 [1]。


而这篇文章提出了对应的办理方案,用一个 search-based 浮点量化算法,统筹搜刮出最适合的浮点数的指数位 / 尾数位设定以及对应的量化区间。
除此之外,另一个同时出如今各种差别类别 Transformer 模型 (Bert,LLaMA,ViT) 中的现象也会严肃影响量化的难度:那就是模型的 activation 中差别 channel 之间的数量级会有很高的差别,而同 channel 之间的量级十分一致。之前 LLM.int8 [2] 和 SmoothQuant [3] 也有类似的发现,不外这篇文章指出这个现象不光仅存在于 LLM 中,并且在其他 Transformer 模型里也有类似现象 如下如所示,LLaMA 与 BERT 以及 DeIT-S 中的 activation 的分布都发现了类似的现象:


从图中可以看到,那些非常大的 channel 都比剩余的 channel 大很多,所以在量化 activation tensor 的过程中,量化的精度很大水平会被这些非常值决定,从而抑制其他 channel 值的量化区间,最终降低团体影响量化精度。这会导致量化的最终效果崩坏,尤其当比特数降到一定水平的时间。值得注意的是,只有 tensor-wise 和 token-wise 量化可以在 efficient matrix multipilication 的时间将 scaling factor 提取出来,而 channel-wise 量化是不支持 efficient matrix multipilication 的,见下图。


为了办理这个问题,同时维持高服从矩阵乘法 (Efficient Matrix Multiplication),本文利用少量的校正资料集,预先算出 activation 的每个 channel 的最大值,从而盘算缩放因子。然后将这个缩放因子一拆为二,拆解成一个 per-tensor 的实数乘以 per-channel 的 2 的幂。而这个 2 的整数次方即用 FP 里的 exponent bias 表现。完备的过程可以用以下公式表现:


进一步地,在 calibration 完成之后,这个 per-channel exponent bias 就不再变化,因此可以和 weight quantization 一起进行预盘算 (pre-compute),将这个 per-channel exponent bias 整合进量化后的 weights 中,进步量化精度。完备的过程如以下公式:


可以看到在 pre-shifted 后,原本 activation 中的 full-precision per-channel biases 的位置变成了一个 tensor-wise 的实数 scaling factor ,而被拆解出来的整数 per-channel biases 被移到了 weight 中原本 integer bias 的位置,如公式 4。     
从而这个方法 (pre-shifted exponent bias) 能在维持 efficient matrix multiplication 的原则下,更好得进步量化精度,方法的直观展示如下图所示:


最后本文展示 Floating Point Quantization (FPQ) 方法,在 LLaMA, BERT 以及 ViTs 模型上,4-bit 量化皆取得了远超 SOTA 的效果。特别是,这篇文章展示了 4-bit 量化的 LLaMA-13B 模型,在零样本推理任务上到达平均 63.1 的分数,只比完备精度模型低了 5.8 分,且比之前的 SOTA 方法平滑量高出了 12.7,这是目前少数已知可行的 4-bit 量化方案了。





#LLM~端侧方案

端侧LLM毫无疑问会成为各手机厂商在2024年的主战场。从国内各手机厂透露的信息来看,大家几乎都把希望拜托在了芯片厂身上,自身能做的、会做的工作太少。希望苹果的工作对国内厂商们有启发、借鉴意义。
论文链接:LLM in a flash: Efficient Large Language Model Inference with Limited Memory
1. Flash Memory and DRAM
在移动端装备中(如手机),DRAM可明白为“运行时内存”,Flash Memory可明白为“存储空间”。做一个简朴的类比,在PC中,DRAM对应于内存;Flash Memory对应于硬盘存储(注意:仅仅是对应于,实现方案并不一样)。
在通常的LLM推理阶段,LLM都是直接加载到DRAM中的。一个7B半精度LLM,完全加载进DRAM所需的存储空间超过14GB。考虑到目前主流手机的DRAM最高也就16GB的水平,在端侧直接利用DRAM来加载7B LLM面临巨大挑战。
图1给出了一个移动端尺度的存储结构示意图。


图1 移动端存储结构示意图
Flash Memory的特点是大存储,低带宽。也就是说,Flash Memory可存储的内容多(图中的100GB),但数据传输速率低(图中的1GB/s)。而DRAM的特点是小存储,高带宽。
如今的问题是:模型巨细 > DRAM,所以无法将模型全部加载进DRAM。
苹果的办理方案是将LLM放在Flash Memory中,在每次需要进行推理时,仅仅将部分必要参数加载到DRAM中
苹果的整个方案重点办理两个问题:


  • 如何快速识别出哪些模型参数是必要的
  • 考虑到由Flash memory到DRAM的带宽较低,如何加速由Flash memory到DRAM的传输服从
    论文中从三个差别方面做了尝试,下面分别介绍。
2. 减少数据传输量
这部分介绍论文中接纳的三种降低数据传输量的方法。
2.1 方法一:Selective Persistence Strategy
对于常见的LLM而言,它的模型参数主要由Attention参数MLP参数两部分构成,此中Attention参数占比约为1/3,MLP参数占比约为2/3。除此,还有参数量级可忽略不计的Embedding层的参数。
因为Attention参数量相对较少,所以苹果的方案是将Attention参数和Embedding层的参数直接加载到DRAM中。
这就是所谓的Selective Persistence Strategy,其意为:有选择性地把部分参数常驻在DRAM中。而这部分常驻的参数就是Attention参数和Embedding参数。缘故原由是因为它们占比较小。
2.2 方法二:Anticipating ReLU Sparsity
这里主要借鉴了DejaVu的思绪:MLP层的输出只有不到10%的值是激活状态(不为0)。一般把这种现象称为稀疏性。稀疏性越强,则非激活状态的值就越多。
所以我们也可把这句话“MLP层的输出只有不到10%的值是激活状态”简写作“MLP层的稀疏性超过90%”。
要注意,此处的稀疏性一般称为“Contextual Sparsity”。也就是说,MLP层的哪些神经元会激活,与当前的输入相干
苹果照搬了DejaVu的方法,利用一个两层MLP来预测哪些神经元会激活。方法也很简朴,假设神经元个数为4096,只需要将MLP的输出层的巨细设为4096,并为每一个输出利用sigmoid来做一个二分类即可(“选择”or“不选择”)。
注意1:差别Transformer层利用的预测模型差别。
注意2:同一个Transformer层中的MLP一般有两层。它们的激活神经元始终保持相同。
在可以或许准确预测的前提下,每次在推理时动态加载预测为激活神经元对应的参数即可。
这里有对DejaVu详细介绍的文章:[ICML'23] DejaVu:LLM中的动态剪枝
2.3 方法三:Sliding Window
根据2.2末节中介绍的稀疏性可知,在每一次LLM进行前向推理时,它都需要利用模型预测每一个MLP层中激活神经元的编号,并将所需的神经元所对应的权重由Flash memory加载到DRAM中。
因为LLM的推理阶段是逐token进行的,这意味着在天生差别token的时间,需要加载到DRAM中的MLP的参数也差别。


这就是Sliding Window的核心思想:保留处理过去k个token时的激活神经元所对应的参数在DRAM中,并在处理当前token时只对:1)部分多余的参数进行删除;2)缺少的参数进行加载。图2是原文中的示意图。


图2 Sliding Window示意图
图中上图表如今处理当前token “Was”之前,前5个token(k=5)的激活神经元(淡蓝色偏绿部分)。图中下图表如今处理当前token “Was”之时,需要新加入的神经元(蓝色部分)和需要删除的神经元(分红部分)。
Sliding Window的核心假设是LLM在处理相邻token时产生的稀疏性具有相似性。原文没有仔细分析和论证这个假设。
3 进步传输吞吐量
3.1 Bundling Columns and Rows
通常LLM中的MLP层包含两个全连层。在忽略激活函数的环境下,这两个全连层可以写为:


图3 将两个全连层的列与行拼接存储


这样做的好处是原本需要两次I/O,如今只需要一次了。固然总的数据读取量并没有变,但读取大块、连续的数据会比读取大量小块、非连续数据更加高效,因此团体传输吞吐量提拔了。
3.2 Bundling Based on Co-activation
这是一个原文尝试过,但被验证为无效的计谋。既然原文提到了,所以这里也罗列出来。
原文中推测某些神经元之间大概存在一些精密接洽。比如对于两个神经元a和b,当a激活时,b也会激活(或者当b激活时,a也会激活)。
因此可以通太过析来找到每个神经元的“closest friend”(与该神经元同时激活频率最高的别的某个神经元)。然后在存储Flash memory中存储时,也将它们的参数拼接存在一起。这样的读取服从更高。
但该方法之所以无效,主要缘故原由是大概会存在某个神经元i,它是别的很多神经元的“closest friend”。这样导致的问题则是神经元i被额外传输了太多次,导致现实的I/O成本增加了。
4 Optimized Data Management in DRAM
固然DRAM的数据读取速度比Flash memory快很多,但当需要对此中数据进行大量、高频读写时,它的时耗仍旧不可忽略。在本文介绍的内容中,对DRAM的读写主要发生在对MLP层中所需神经元对应参数的删除与新增(参考图2)。
为此,论文中设计了一种特别的数据结构来对DRAM中的数据做精细化管理。该数据结构的核心变量如下:


  • Matrix:按照“Bundling Columns and Rows”的方法存储激活神经元所对应的参数
  • bias:激活神经元所对应的bias参数
  • num_used:激活神经元的个数
  • last_k_active:过去k个token所对应的激活神经元编号
  • Pointer:当前行参数对应的神经元编号


图4 Optimized Data Management in DRAM


通过预分配一个足够大的空间,可以克制因反复分配而导致的额外开销。下面来分析基于该数据结构的一些操作的高效实现方法。
该矩阵中的行对应的是当前存储在DRAM中激活神经元的参数。前文提到(2.3末节),当处理新的token时,需要将不会被激活的神经元删除,并添加新的会被激活的神经元。所以最紧张的两个操作是“删除”和“新增”。
当需要删除某个神经元时(如图4中左图标红部分,对应的是编号为10的神经元),只需将num_rows的数量减1,并将最后一行Copy至被删除行,效果如图4的中图所示。虚线框表现当前有效的数据。
当需要“新增”时,直接将其对应的参数由Flash memory中copy至该矩阵中即可,无需额外分配存储空间。
5. 实验效果
苹果这篇paper的主要关注点在于:让LLM在运行时内存受限的环境下能高效地跑起来。所以论文的实验主要对比了各种环境下I/O导致的时耗,如下图所示。


图5 实验效果
图5中的实验利用的是OPT 6.7B模型,半精度。表中第一行和第二行都是基准baseline。第一行假设初始模型全部在Flash memory中,那么为了完成一次完备的推理,需要将模型全部加载一遍,整个I/O耗时2130ms。
第二行对应于假设模型有一半参数提前在DRAM中的整个加载耗时。
第三行到第五行对应于应用了Predictor(2.2末节)、Windowing(2.3末节)和Bundling(3.1末节)后对应的耗时。
服从提拔非常显着。




#LLM推理MLSys优化

近日,CMU Catalyst 团队推出了一篇关于高效 LLM 推理的综述,覆盖了 300 余篇相干论文,从 MLSys 的研究视角介绍了算法创新和系统优化两个方面的相干希望。
在人工智能(AI)的快速发展配景下,大语言模型(LLMs)依附其在语言相干任务上的良好表现,已成为 AI 领域的紧张推动力。然而,随着这些模型在各种应用中的普及,它们的复杂性和规模也为其摆设和服务带来了前所未有的挑战。LLM 摆设和服务面临着麋集的盘算强度和巨大的内存斲丧,特别是在要求低耽误和高吞吐量的场景中,如何进步 LLM 服务服从,降低其摆设成本,已经成为了当前 AI 和系统领域亟需办理的问题。
来自卡内基梅隆大学的 Catalyst 团队在他们的最新综述论文中,从机器学习系统(MLSys)的研究视角出发,详细分析了从前沿的 LLM 推理算法系统的革命性厘革,以应对这些挑战。该综述旨在提供对高效 LLM 服务的当前状态和未来方向的全面明白,为研究者和实践者提供了宝贵的洞见,帮助他们降服有效 LLM 摆设的障碍,从而重塑 AI 的未来。
论文链接:https://arxiv.org/abs/2312.15234
该论文的第一作者是卡内基梅隆大学的 Xupeng Miao(苗旭鹏)博士后研究员,合作者还包罗 Tianqi Chen 和 Zhihao Jia 助理传授。此外,其他学生作者也均来自于 CMU Catalyst Group 实验室,该实验室由 Zhihao Jia 与 Tianqi Chen(陈天奇)在 CMU 共同主持,致力于集成来自于机器学习算法、系统、硬件等多方面的优化技术,构造主动化的机器学习系统。此前,该实验室还推出了 SpecInfer, MLC-LLM, SpotServe [ASPLOS‘24] 等开源项目,推进 LLM 大模型相干系统的研究和应用。实验室主页:https://catalyst.cs.cmu.edu。
综述概览
该综述系统地审阅了现有 LLM 推理技术,覆盖了 300 余篇相干论文,从算法创新系统优化两个方面展开介绍。论文以此为基础,对现有工作设计了一套清晰且过细的分类法,突出了各种方法的上风和局限性,逐类别搜集整理并介绍了每种方法的相干论文。除此之外,论文还对当前的主流 LLM 推理框架在系统设计与实现方面进行了深入的对比和分析。最后,作者对未来如何继续进步 LLM 推理服从进行了展望,在技术层面提出了六大潜在发展方向
分类法


算法创新
这一节对提出的各种算法和技术进行了全面分析,旨在改进大规模 Transformer 模型推理的原生性能缺陷,包罗解码算法架构设计、和模型压缩等等。


解码算法:在这一部分中,我们回顾了在图 2 中展示的几种 LLMs 推理优化过程的新颖解码算法。这些算法旨在减少盘算复杂度,并进步语言模型推理在天生任务中的总体服从,包罗:


  • 非自回归解码:现有 LLMs 的一个主要限制是默认的自回归解码机制,它逐个顺序天生输出 token。为办理这一问题,一种代表性的工作方向黑白自回归解码 [97, 104, 108,271],即放弃自回归天生范式,突破单词依赖并假设一定水平的条件独立性,并行解码输出 token。然而,尽管这类方法解码速度有所进步,但大多数非自回归方法的输出质量仍不如自回归方法可靠。
  • 谋利式推理:另一类工作是通过谋利执行思想 [47] 实现并行解码。自回归 LLM 推理过程中的每个解码步骤都可以被视为带有条件分支的步伐执行语句,即决定接下来天生哪个 token。谋利式推理 [51, 155] 先利用较小的草稿模型进行多步解码预测,然后让 LLM 同时验证这些预测以实现加速。然而,将谋利解码应用于 LLMs 时仍旧存在一些现实挑战,例如,如何使解码预测足够轻量且准确,以及如何借助 LLMs 实现高效的并行验证。SpecInfer [177] 首次引入基于 tree-based speculative decoding 和 tree attention,并提出了一个低耽误 LLM 服务系统实现,该机制也被后续多个工作 [48, 118, 168, 185, 229, 236, 274, 310] 直接接纳。
  • 提前退出:这类方法主要利用 LLMs 的深层多层结构,在中间层提前推出推理,中间层输出可以通太过类器转化成输出的 token,从而降低推理开销 [117, 147, 163, 167, 234, 272, 282, 291, 308],它们也被称为自顺应盘算 [68, 219]。
  • 级联推理:这类方法级联了多个差别规模的 LLM 模型,用于分别处理差别复杂度的推理请求,代表性工作包罗 CascadeBERT [157] 和 FrugalGPT [53]。
架构设计:


  • 设置缩小:直接缩小模型设置。
  • 注意力简化:最近出现了很多研究工作,它们主要是将之前的长序列高效注意力机制 [240] 应用在 LLM 上,以缩短上下文,减少 KV 缓存,以及注意力复杂度,同时略微降低解码质量(如滑动窗口 [129, 299]、哈希 [198]、dilated [74]、动态选择等等)。表 1 中总结了一些近期的热门方法和之前的工作之间的对应关系。




  • 激活共享:这类方法主要是通过共享 attention 盘算的中间激活来降低推理内存开销,代表性工作包罗 MQA [220] 和 GQA [32]。
  • 条件盘算:这类方法主要是指稀疏专家混合模型(Sparse MoE),比如最近大火的 Mistrial 7Bx8 模型就属于此类。
  • 循环单位:尽管 Transformer 已经替换了 RNN 模型,但考虑到注意力机制的二次复杂性,人们始终未曾放弃将 recurrent unit 机制重新引入 LLM 的尝试,比如 RWKV [200]、RetNet [235],以及状态空间模型 [91, 102, 103, 176] 等等。 
模型压缩:


  • 知识蒸馏:这类方法以大型的西席模型为监视,练习一个小型的学生模型。大多数之前的方法都在探索白盒蒸馏 [106, 133, 214, 233, 255],需要访问整个西席模型的参数。由于基于 API 的 LLM 服务(如 ChatGPT)的出现,一些黑盒蒸馏模型吸引了很多关注 [238,59, 273, 201, 313],这些模型通常具有更少的模型参数,与原始 LLMs(如 GPT-4 [195])相比,在各种卑鄙任务上表现出了相称的性能。
  • 网络剪枝:过去几年中,网络剪枝方法 [180, 215, 215] 已被广泛研究,但并非全部方法都可以直策应用于 LLMs,需要考虑重新练习大概带来的过高盘算成本,以及评估剪枝是否可以在底层系统实现上取得服从提拔。大致上可以分为结构化剪枝 [80, 149, 174, 216, 172] 和半结构化稀疏化 [40, 87, 232, 251, 276] 等。
系统优化
本节研究 LLM 推理系统优化技术,以加速 LLM 推理,而不改变 LLM 盘算语义。这一工作的目的是通过改进用于大型语言模型推理的底层系统和框架来进步系统服从,包罗低比特量化、并行盘算、内存管理、请求调度、和内核优化等等,详细内容可以拜见论文原文。
软件框架


论文还对一些目前最先辈的基于 GPU 的开源 LLM 推理系统进行了深入的分析,并从多个方面总结了它们在设计与实现伤的差别。
未来方向


  • 专用硬件加速器的发展:天生型 LLM 服务服从的显著提拔大概在很大水平上依赖于专用硬件加速器的发展和提拔,尤其是软硬协同设计方法。例如,让内存单位更加接近处理单位,或是针对 LLM 算法数据流优化芯片架构,这些硬件优化可以在很大水平上为 LLM 推理在软件层面带来便利和机会。
  • 高效有效的解码算法:开辟更高效的解码算法可以显著进步服务服从。受对实时应用更快天生速度的需求驱动,一个有出息的方向是广义的谋利式推理(generalized speculative inference),不光会带来显著加速,同时保持相同的天生质量。正如 SpecInfer 中所指出的,广义的谋利式推理中,用于天生草稿 token 的小模型可以被替换为任何快速的 token 天生方法,比如自界说函数、召回方法、乃至早停机制和非自回归解码等等。
  • 长上下文 / 序列场景优化:随着应用场景变得更加复杂,处理更长的上下文或序列的需求不断增长。服务长序列负载的 LLM 需要办理算法和系统两方面的挑战。在算法方面,它们依然面临长度泛化失效问题,乃至大概出现 “loss in the middle” 的环境。目前的解法主要是通过召回增强、序列压缩和缓存来尽大概缩短序列长度并保存相干信息。
  • 探索替换基础架构:尽管 Transformer 模型和自注意力机制目前主导着 LLM 领域,但探索替换架构是未来研究的一个有前景的方向。例如,一些最新研究探索了无注意力方法,利用纯 MLP(多层感知机)架构来替换注意力机制,大概会改变目前 LLM 推理优化的格局。
  • 在复杂环境中的摆设探索:随着 LLM 应用的扩展,探索并优化它们在各种复杂环境中的摆设成为一个关键的未来方向。这一探索不光限于传统的基于云的摆设,还包罗边缘盘算、混合盘算(cloud+edge)、去中心化盘算以及廉价的可抢占资源等。
  • 特定需求的主动顺应:应用特定需求的多样性创造了一系列创新的 LLM 服务优化机会,例如模型微调(parameter-efficient fine-tuning)、向量数据库检索、多模态负载等等。这些独特的挑战也要求将 LLM 服务技术主动且顺遂地集成到现有 IT 基础办法中,将优化空间扩展到整个 LLM 生命周期。
总结
总的来说,该综述不光是对当前 LLM 服务优化研究的全面概述,也为未来在这一领域的探索和发展指明了方向。通过深入了解这些先辈的办理方案,研究者和实践者可以更好地明白和应对在现实应用中摆设大型语言模型时面临的挑战。





#大模型LLM~技术精要

抛开是否有财力做超大型LLM这个因素,如果单从技术角度看,国表里的差距主要来自于对LLM的认知以及未来应往何处去的发展理念的差别。
ChatGPT出现后惊喜或惊醒了很多人。惊喜是因为没想到大型语言模型(LLM,Large Language Model)效果能好成这样;惊醒是顿悟到我们对LLM的认知及发展理念,间隔天下最先辈的想法,差得有点远。我属于既惊喜又惊醒的那一批,也是典型的中国人,中国人善于自我反思,于是开始反思,而这篇文章正是反思的效果。
实话实说,国内在LLM模型相干技术方面,此刻,间隔最先辈技术的差距进一步加大了。技术领先或技术差距这事故,我觉得要动态地以发展的眼光来看。在Bert出现之后的一到两年间,实在国内在这块的技术追赶速度照旧很快的,也提出了一些很好的改进模型,差距拉开的分水岭应该是在 GPT 3.0出来之后,也就是2020年年中左右。在其时,实在只有很少的人觉察到:GPT 3.0它不光仅是一项详细的技术,实在体现的是LLM应该往何处去的一个发展理念。自此之后,差距拉得越来越远,ChatGPT只是这种发展理念差别的一个天然效果。所以,我个人认为,抛开是否有财力做超大型LLM这个因素,如果单从技术角度看,差距主要来自于对LLM的认知以及未来应往何处去的发展理念的差别。
国内被国外技术甩得越来越远,这个是事实,不承认也不行。前阵子网上很多人担忧说国内AI如今处于“危急生死之秋”,我觉得倒也不至于这么严肃。君不见,这个天下上,具备这么超前眼光的只有OpenAI一家吗?包罗Google在内,实在对于LLM发展理念的明白,显着都落伍OpenAI一个身位。现实是OpenAI表现过于优秀,把全部人都甩开了,不光仅是国内。
我觉得,OpenAI对LLM在理念及相干技术方面,领先国外的Google、DeepMind约莫半年到一年的时间,领先国内大概两年左右的时间。在LLM这个事故上,感觉梯队很显着,Google应该是排在第二位,最能体现Google技术眼光的是PaLM和Pathways,推出时间大概在22年2月到4月间,同一时期,OpenAI推出的却是InstructGPT,从这里就可以看出Google和OpenAI的差距了,至于为何这么说,你看了我后面的正文后大概能明白。DeepMind之前的重心一直在强化学习攻克游戏和AI for science这些方面,切入LLM实在很晚,应该是21年才开始重视这个方向,目前也处于追赶状态。Meta就更不用说了,重心一直不在LLM上,目前感觉也发力开始追赶。这照旧目前做得最好的一批机构,尚且云云,更何况国内呢?我觉得无可非议。至于OpenAI关于LLM的理念是什么,我在本文的最后一部分,谈判谈我的认知。
本文梳理自GPT 3.0出现之后的主流LLM技术,在此之前的主流技术可以参考:
​​https://zhuanlan.zhihu.com/p/254821426​​
我信赖看完这两篇文章,可以或许让您对LLM领域的技术脉络,LLM技术发展过程中出现过的差别发展理念,乃至未来大概的发展趋势,有比较清晰的认知。固然,很多地方讲的内容是我个人看法,有很大的主观性,错漏难免,所以还请审慎参考。
本文试图答复下面一些问题:ChatGPT是否带来了NLP乃至AI领域的研究范式转换?如果是,那会带来怎样的影响?LLM从海量数据中学到了什么知识?LLM又是如何存取这些知识的?随着LLM规模逐步增大,会带来什么影响?什么是In Context Learning?为什么它是一项很神秘的技术?它和Instruct又是什么关系?LLM具备推理本领吗?头脑链CoT又是怎么做的?等等,信赖看完,能让您对这些问题有一个答案。
首先,在谈LLM技术现状前,先宏观地谈下我心目中的研究范式转换问题。这样,我们才能“先见森林,再见树木”,对详细技术为何会是云云变化有个更清晰的认知。
潮水之巅:NLP研究范式的转换
如果我们把时间线往前拉得更长一些,回到NLP领域的深度学习时代,在更长时间窗口内观察技术变迁及其影响,大概会更轻易看清此中的一些关键节点。我个人认为,在最近10年来NLP领域的技术发展过程中,大概存在两次大的研究范型转换。
范式转换1.0:从深度学习到两阶段预练习模型
这个范式转换所涵盖的时间范围,大致在深度学习引入NLP领域(2013年左右),到GPT 3.0出现之前(2020年5月左右)。
在Bert和GPT模型出现之前,NLP领域流行的技术是深度学习模型,而NLP领域的深度学习,主要依托于以下几项关键技术:以大量的改进LSTM模型及少量的改进CNN模型作为典型的特性抽取器;以Sequence to Sequence(或叫encoder-decoder亦可)+Attention作为各种详细任务典型的总体技术框架。
在这些核心技术加持下,NLP领域深度学习的主要研究目的,如果归纳一下,是如何有效增加模型层深或模型参数容量。就是说,怎么才能往encoder和decoder里不断叠加更深的LSTM或CNN层,来达成增加层深和模型容量的目的。这种努力,尽管确实不断增加了模型层深,但是从办理详细任务的效果角度看,总体而言,不算很乐成,或者说和非深度学习方法相对,带来的上风不算大。
深度学习之所以不敷乐成,我认为主要缘故原由来自于两个方面:一方面是某个详细任务有限的练习数据总量。随着模型容量的增加,需要靠更大量的练习数据来支持,否则即使你能把深度做起来,任务效果也做不上去。而在预练习模型出现之前,很显着这是NLP研究领域一个严肃问题;另外一个方面是LSTM/CNN特性抽取器,表达本领不敷强。意思是就算给你再多的数据也没用,因为你不能有效地吸收数据里蕴含的知识。主要应该是这两个缘故原由,拦阻了深度学习在NLP领域的乐成突围。
Bert/GPT这两个预练习模型的出现,无论在学术研究角度看,照旧工业应用角度来看,都代表了NLP领域的一个技术飞跃,并带来了整个领域研究范式的转换。这种范式转换带来的影响,体如今两个方面:首先,是部分NLP研究子领域的阑珊乃至逐步消亡;其次,NLP差别子领域的技术方法和技术框架日趋同一,在Bert出现后一年左右,技术栈根本收敛到两种技术模式中。关于这两点,我们分头来谈。
影响一:中间任务的消亡
NLP是一个宏观研究领域的统称,里面有五花八门详细的子领域与子方向,如果仔细分析,从任务的性子角度,可以把这些任务分成两大类:一类可以叫做“中间任务”,一类可以称为“最终任务”。
典型的中间任务包罗:中文分词、词性标注、NER、句法分析、指代消解、语义Parser等,这类任务一般并不办理应用中的现实需求,大多数是作为那些办理现实需求任务的中间阶段或者辅助阶段存在的,比如几乎没有需求说,我要一个句法Parser,把这个句子的句法分析树给用户看看,用户不需要看到这些NLP的中间阶段处理效果,他只关心某个详细任务你有没有干好。“最终任务”包罗比如文本分类、文本相似性盘算、机器翻译、文本摘要等等,有很多。这类任务的特点是每个子领域都办理某个现实需求,任务效果根本能直接出现给用户,比如用户确实存在给你一句英文,告诉他中文是什么的需求。
按理说,“中间任务”就不应该出现,而之所以会存在,这是NLP技术发展水平不敷高的一种体现。在技术发展早期阶段,因为其时的技术相对落伍,很难一步做好有难度的最终任务。比如机器翻译,早期技术要做好机器翻译是很困难的,于是科研人员就把困难分而治之,分解成分词、词性标注、句法分析等各种中间阶段,先把每个中间阶段做好,然后再拼起来完成最终任务,这也是没办法的事故。
但是自从Bert/GPT出现之后,实在就没有必要做这些中间任务了,因为通过大量数据的预练习,Bert/GPT已经把这些中间任务作为语言学特性,吸收到了Transformer的参数里,此时我们完全可以端到端地直接办理那些最终任务,而无须对这种中间过程专门建模。这里大概争议最大的是中文分词,实在原理也是一样的,哪些字应该组成一个词,这个实在你不用管,让LLM自己当特性去学就行了,只要对于办理任务有帮助,它天然会去学该学的公道分词方式,也未必一定要和我们人类明白的分词规则相同。
基于以上认知,实在在Bert/GPT一出现,你就应该得出这类NLP的中间阶段的任务,会逐步退出历史舞台这个结论。
影响二:差别研究方向技术门路的同一
在分析详细影响前,我们先讨论下另外一种NLP任务分别方式,这对于明白后面内容有帮助。如果对“最终任务”进一步进行分类,又大致可以分为两大差别类型的任务:天然语言明白类任务和天然语言天生类任务。如果排撤除“中间任务”的话,典型的天然语言明白类任务包罗文本分类、句子关系判定、情感倾向判定等,这种任务本质上都是分类任务,就是说输入一个句子(文章),或者两个句子,模型参考全部输入内容,最后给出属于哪个类别的判定。天然语言天生也包含很多NLP研究子方向,比如聊天机器人、机器翻译、文本摘要、问答系统等。天生类任务的特点是给定输入文本,对应地,模型要天生一串输出文本。这两者的差别主要体如今输入输出形式上
自从Bert/GPT模型诞生后,出现了显着的技术同一趋向。首先,NLP中差别的子领域,其特性抽取器都逐渐从LSTM/CNN同一到Transformer上。实在,自Bert公开后不久,就应该意识到,这必然会成为技术趋势。至于其缘故原由,在几年前我写的这篇:
放弃幻想,全面拥抱Transformer:天然语言处理三大特性抽取器(CNN/RNN/TF)比较
​​https://zhuanlan.zhihu.com/p/54743941​​
中做了分析和分析,感兴趣的同学可参考。而且,目前Transformer不光同一了NLP诸多领域,也正在逐步地替换图像处理各种任务中被广泛利用的CNN等别的模型的进程之中,类似的,多模态模型目前也根本都接纳了Transformer模型。这种Transformer从NLP出发,攻城略地逐步同一AI越来越多领域的趋势,起始于2020年底出现的Vision Transformer (ViT) ,之后发达发展,到目前已大获乐成,且其继续向更多领域拓展的势头会越来越迅猛。
其次,大多数NLP子领域的研发模式切换到了两阶段模式:模型预练习阶段+应用微调(Fine-tuning)或应用Zero/Few Shot Prompt模式。更准确地说,NLP各种任务实在收敛到了两个差别的预练习模型框架里:对于天然语言明白类任务,其技术体系同一到了以Bert为代表的“双向语言模型预练习+应用Fine-tuning”模式;而对于天然语言天生类任务,其技术体系则同一到了以GPT 2.0为代表的“自回归语言模型(即从左到右单向语言模型)+Zero /Few Shot Prompt”模式。至于为何会分化成两条技术门路,有其必然性,关于这点我们放在后面表明。
这两种模式,看似比较相像,但其背后蕴含了迥异的发展思绪,也会导向差别的未来发展方向。不外遗憾的是,我们中的绝大多数人,在其时都低估了GPT 这条发展门路的潜力,而把视觉中心聚焦到了Bert这种模式上。
范式转换2.0: 从预练习模型走向通用人工智能 (AGI,Artificial General Intelligence)
这个范式转换所涵盖的时间范围,大致在GPT3.0出现之后(20年6月左右),一直到目前为止,我们应该正处于这个范式转换过程中。
ChatGPT是触发这次范型转换的关键节点,但是在InstructGPT出现之前,实在LLM处于这次范式转换前的一个过渡期。
过渡期:以GPT 3.0为代表的“自回归语言模型+Prompting”模式占据统治地位
前面说过,在预练习模型发展的早期,技术框架收敛到了Bert模式和GPT模式这两种差别的技术范型,而且人们广泛更看好Bert模式一些,相称多数的后续技术改进,都是沿着Bert那条路走的。但是,随着技术的继续发展,你会发现,目前规模最大的LLM模型,几乎清一色都是类似GPT 3.0这种“自回归语言模型+Prompting”模式的,比如GPT 3、PaLM、GLaM、Gopher、Chinchilla、MT-NLG、LaMDA等,没有例外。为什么会这样呢?背后一定有其必然性,我认为大概主要源于两个缘故原由。


首先,Google的T5模型,在形式上同一了天然语言明白和天然语言天生任务的外在表现形式。如上图所示,标为赤色的是个文本分类问题,黄色的是判定句子相似性的回归或分类问题,这都是典型的天然语言明白问题。在T5模型里,这些天然语言明白问题在输入输出形式上和天生问题保持了一致,也就是说,可以把分类问题转换成让LLM模型天生对应类别的字符串,这样明白和天生任务在表现形式就实现了完全的同一。
这分析天然语言天生任务,在表现形式上可以兼容天然语言明白任务,若反过来,则很难做到这一点。这样的好处是:同一个LLM天生模型,可以办理几乎全部NLP问题。而如果仍旧采取Bert模式,则这个LLM模型无法很好处理天生任务。既然这样,我们固然倾向于利用天生模型,这是一个缘故原由。
第二个缘故原由,如果想要以零示例提示语(zero shot prompting)或少数示例提示语(few shot prompting)的方式做好任务,则必须要采取GPT模式。如今已有研究(参考:On the Role of Bidirectionality in Language Model Pre-Training)证明:如果是以fine-tuning方式办理卑鄙任务,Bert模式的效果优于GPT模式;如果以zero shot/few shot prompting这种模式办理卑鄙任务,则GPT模式效果要优于Bert模式。这分析了,天生模型更轻易做好zero shot/few shot prompting方式的任务,而Bert模式以这种方式做任务,是天然有劣势的。这是第二个缘故原由。
但是问题来了:为什么我们要寻求zero shot/few shot prompting这种方式来做任务呢?要表明清晰这个问题,我们首先需要搞清晰另外一个问题:什么样的LLM模型,对我们是最抱负的?


上图展示了一个抱负的LLM该有的样子。首先,LLM应该具备强大的自主学习本领。假设我们把天下上能获得的全部文本或者图片等差别类型的数据喂给它,它应该可以或许主动从中学习到里面包含的全部知识点,学习过程不需要人的参与,并且能机动应用所学知识,来办理现实问题。因为数据是海量的,要吸收全部知识,就要非常多的模型参数来存储知识,所以这个模型必然会是一个巨无霸模型。
其次,LLM应该能办理NLP任何子领域的问题,而不光支持有限领域,乃至它应该可以相应NLP之外别的领域的问题,最好是任意领域的问题都能得到很好地答复。
再者,当我们利用LLM办理某个详细领域问题的时间,应该用我们人类风俗的表达方式,就是说LLM应该明白人类的命令。这体现出让LLM适配人,而不是反过来,让人去适配LLM模型。人适配LLM的典型例子,比如绞尽脑汁去尝试各种差别的prompt,以试图找到好的提示语,才能很好地办理手头问题。关于这点,上图在人类和LLM交互的接口层,举了几个例子,分析什么是好的人利用LLM模型的接口形式。
看完这个抱负中的LLM,我们再转头表明上面遗留的问题:为什么我们要寻求zero shot/few shot prompting这种方式来做任务呢?有两个缘故原由。
第一,这个LLM模型规模必然非常巨大,有本领作出这个模型,或改动这个模型参数的机构必然很少。而任务需求方是千万万万的中小机构乃至是个人,就算你把模型开源出来,他们也无力摆设这个模型,更不用说再用Fine-tuning这种模式去修改模型参数了。所以,我们应该寻求不修正模型参数,就能让任务需求方完成任务的方式,也就是应该采取prompt模式完成任务,而非Fine-tuning模式(由此可看出,soft prompting技术方向是违背这个发展趋势的)。模型制作方则将LLM作成公用服务,以LLM as Service的模式运行。作为服务支持方,考虑到变化多端的用户需求,所以LLM模型制作方更要寻求让LLM能完成尽大概多类型的任务,这是附带的影响,也是为何超级大模型一定会寻求走向AGI的现实因素。
第二,zero shot prompting也好,few shot prompting也好,乃至促进LLM推理本领的头脑链(CoT,Chain of Thought)Prompting也好,就是上图中接口层中的现有技术。详细而言,zero shot prompting的初衷,实在就是人类和LLM的抱负接口,直接用人类所风俗的任务表述方式让LLM服务变,但是发现LLM并不能很好地明白,效果也不好。经过继续研究,转而发现:对于某项任务,如果给LLM几个示例,用这些示例来代表任务描述,效果会比zero shot prompting好,于是大家都去研究更好的few shot prompting技术。可以明白为,本来我们希望LLM可以或许用人类常用的命令方式来执行某个任务,但是目前技术还做不到,所以退而求其次,用这些替换技术来表达人类的任务需求。
如果明白了上述逻辑,很轻易得出如下结论:few shot prompting(也被称为In Context Learning)只是一种过渡时期的技术。如果我们可以或许更天然地去描述一个任务,而且LLM可以明白,那么,我们肯定会毫不犹豫地抛弃这些过渡期的技术,缘故原由很显着,用这些方法来描述任务需求,并不符合人类的利用风俗。
这也是为何我将GPT 3.0+Prompting列为过渡期技术的缘故原由,ChatGPT的出现,改变了这个现状,用Instruct代替了Prompting,由此带来新的技术范式转换,并产生若干后续影响。
影响一:让LLM适配人的新型交互接口
在抱负LLM的配景下,我们再来看ChatGPT,能更好明白它的技术贡献。ChatGPT应该是目前全部的现有技术里,最接近抱负LLM的技术方法。如果归纳下ChatGPT最突出特点的话,我会用下面八个字:“本领强大,善解人意”。
“本领强大”这一点,我信赖应该主要归功于ChatGPT所依托的基础LLM GPT3.5。因为ChatGPT 尽管加入了人工标注数据,但是量级只有数万,这个规模的数据量,和练习GPT 3.5模型利用的几千亿token级别的数据量相比,包含的天下知识(数据中包含的事实与常识)可谓九牛一毛,几可忽略,根本不会对增强GPT 3.5的基础本领发挥什么作用。所以它的强大功能,应该主要来自于隐藏在背后的GPT 3.5。GPT 3.5对标抱负LLM模型中的谁人巨无霸模型。
那么,ChatGPT向GPT 3.5模型注入新知识了吗?应该是注入了,这些知识就包含在几万人工标注数据里,不外注入的不是天下知识,而是人类偏好知识。所谓“人类偏好”,包含几方面的含义:首先,是人类表达一个任务的风俗说法。比如,人风俗说:“把下面句子从中文翻译成英文”,以此表达一个“机器翻译”的需求,但是LLM又不是人,它怎么会明白这句话到底是什么意思呢?你得想办法让LLM明白这句命令的含义,并正确执行。所以,ChatGPT通过人工标注数据,向GPT 3.5注入了这类知识,方便LLM明白人的命令,这是它“善解人意”的关键。其次,对于什么是好的答复,什么是不好的答复,人类有自己的尺度,例如比较详细的答复是好的,带有鄙视内容的答复是不好的,诸云云类。这是人类自身对答复质量优劣的偏好。人通过Reward Model反馈给LLM的数据里,包含这类信息。总体而言,ChatGPT把人类偏好知识注入GPT 3.5,以此来获得一个听得懂人话、也比较礼貌的LLM。
可以看出,ChatGPT的最大贡献在于:根本实现了抱负LLM的接口层,让LLM适配人的风俗命令表达方式,而不是反过来让人去适配LLM,绞尽脑汁地想出一个能Work的命令(这就是instruct技术出来之前,prompt技术在做的事故),而这增加了LLM的易用性和用户体验。是InstructGPT/ChatGPT首先意识到这个问题,并给出了很好的办理方案,这也是它最大的技术贡献。相对之前的few shot prompting,它是一种更符合人类表达风俗的人和LLM进行交互的人机接口技术。
而这必将启发后续的LLM模型,继续在易用人机接口方面做进一步的工作,让LLM更听话。
影响二:很多NLP子领域不再具备独立研究价值
就NLP领域而言,这次范式转换,意味着很多目前独立存在的NLP研究领域,将被纳入LLM的技术体系,进而不再独立存在,逐步消失。经过第一次范式转换,尽管NLP中很多“中间任务”,继续作为独立研究领域存在不再必要,但是大多数“最终任务”,仍旧是以独立研究领域存在的,只是切换成在“预练习+fine-tuning”框架下,面临领域独有问题,连续提出新的改进方案。
目前研究表明,很多NLP任务,随着LLM模型规模增长,效果会大幅提拔。据此,我觉得可得到如下推论:大多数某领域所谓“独有”的问题,大概率只是缺乏领域知识导致的一种外在表象,只要领域知识足够多,这个所谓领域独有的问题,就可以被很好地办理掉,实在并不需要专门针对某个详细领域问题,冥思苦想去提出专用办理方案。也许AGI的真相超乎意料地简朴:你只要把这个领域更多的数据交给LLM,让它自己学习更多知识即可。
在这个配景下,同时,ChatGPT证明了我们如今是可以直接去寻求抱负LLM模型的,那么,未来的技术发展趋势应该是:寻求规模越来越大的LLM模型,通过增加预练习数据的多样性,来涵盖越来越多的领域,LLM自主从领域数据中通过预练习过程学习领域知识,随着模型规模不断增大,很多问题随之得到办理。研究重心会投入到如何构建这个抱负LLM模型,而非去办理某个领域的详细问题。这样,越来越多NLP的子领域会被纳入LLM的技术体系,进而逐步消失。
我认为,判定某个详细领域是否该立即停止独立研究,其判定尺度可采取以下两种方法,占其一即可:第一,判定某个任务,是否LLM的研究效果超过人类表现,对于那些LLM效果超过人类的研究领域,已无独立研究的必要。举个例子,GLUE与SuperGLUE测试集合里的很多任务,目前LLM效果已超过人类表现,与这个数据集合密切相干的研究领域,实在就没有继续独立存在的必要。第二,对比两种模式的任务效果,第一种模式是用较大的领域专用数据进行Fine-tuning,第二种是few-shot prompting或instruct-based方法。如果第二种方法效果到达或超过第一种方法,则意味着这个领域没有继续独立存在的必要性。如果用这个尺度来看,实在很多研究领域,目前fine-tuning效果照旧占优的(因为这种模式领域练习数据量大),看似还可独立存在。但是考虑到很多任务随着模型规模增大,few shot prompting效果持续增长,随着更大模型的出现,这个拐点很大概短期就会到达。
如果上述推测建立,将意味着如下残酷事实:对于很多NLP领域的研究人员,将面临往何处去的选择,是继续做领域独有问题呢?照旧放弃这种看似出息不大的方式,转而去建立更好的LLM?如果选择转向去建立LLM,又有哪些机构有本领、有条件去做这个事故呢?你对这个问题的答复会是什么呢?
影响三:更多NLP之外的研究领域将被纳入LLM技术体系
如果站在AGI的视角,参照之前描述的抱负LLM模型,它所能完成的任务,不应局限于NLP领域,或某一两个学科领域,抱负中的LLM应该是领域无关的通用人工智能模型,它如今在某一两个领域做得好,不代表只能做这些任务。ChatGPT的出现,证明了如今这个时期,我们去寻求AGI是有可行性的,而如今是抛开“领域学科”这个头脑束缚的时间了。
ChatGPT除了展示出以流畅的对话形式办理各种NLP任务外,也具备强大的代码本领。很天然的,之后越来越多别的的研究领域,也会被逐步纳入LLM体系中,成为通用人工智能的一部分。


LLM从NLP向外进行领域拓展,一个天然的选择就是图像处理及多模态相干任务。目前已经有些工作在尝试把多模态融入,让LLM成为一个支持多模态输入输出的通用人机接口,典型的例子包罗DeepMind的Flamingo和微软的“Language Models are General-Purpose Interfaces”,上图展示了这种方式的概念结构。
我的判定是无论是图像照旧多模态,未来被融入LLM成为好用的功能,大概比我们想象的进度要慢。主要缘故原由在于:尽管图像领域最近两年也一直在模仿Bert预练习的路子,尝试引入自监视学习,释放模型自主从图像数据中学习知识的本领,典型技术就是“对比学习”和MAE,这是两条差别的技术门路。然而,从目前效果来看,尽管取得了很大的技术进步,但貌似这条路尚未走通,这体如今图像领域预练习模型应用到卑鄙任务,带来的效果收益,远不如Bert或GPT应用在NLP卑鄙任务那样显著。所以,图像预处理模型仍需深入探索,以释放图像数据的潜力,而这会迟滞它们被同一到LLM大模型的时间。固然,如果哪天这条路被趟通,大概率会复现NLP领域目前的局面,就是图像处理各个研究子领域大概会逐步消失,被融入到大型LLM中来,直接完成终端任务。
除了图像与多模态,很显着,别的领域也会逐渐被纳入到抱负LLM中来,这个方向方兴未艾,是具备高价值的研究主题。
以上是我对范式转换的个人思考,接下来,我们来梳理下GPT 3.0之后LLM模型的主流技术希望。如抱负LLM模型所示,相干的技术实在可以分为两大类;一类是关于LLM模型如何从数据中吸收知识,也包罗模型规模增长对LLM吸收知识本领带来的影响;第二类是关于人如何利用LLM内在本领来办理任务的人机接口,包罗In Context Learning和Instruct两种模式。头脑链(CoT)prompting这种LLM推理技术,本质上也属于In Context Learning,因为比较紧张,我就把它们单独拎出来讲一下。
学习者:从无尽数据到海量知识
从目前研究效果看,Transformer是足够强大的特性抽取器,尚不需要做特别的改进。那么通过预练习过程,Transformer学到了什么?知识是如何存取的?我们又如何修正错误知识?本节报告这方面的研究希望。
求知之路:LLM学到了什么知识
LLM从海量自由文本中学习了大量知识,如果把这些知识做粗略分类的话,可以分为语言类知识和天下知识两大类。
语言类知识指的是词法、词性、句法、语义等有助于人类或机器明白天然语言的知识。关于LLM能否捕获语言知识有较长研究历史,自从Bert出现以来就不断有相干研究,很早就有结论,各种实验充分证明LLM可以学习各种层次类型的语言学知识,这也是为何利用预练习模型后,各种语言明白类天然语言任务获得大幅效果提拔的最紧张缘故原由之一。另外,各种研究也证明了浅层语言知识比如词法、词性、句法等知识存储在Transformer的低层和中层,而抽象的语言知识比如语义类知识,广泛分布在Transformer的中层和高层结构中。
天下知识指的是在这个天下上发生的一些真实事故(事实型知识,Factual Knowledge),以及一些常识性知识(Common Sense Knowledge)。比如“拜登是现任美国总统”、“拜登是美国人”、“乌克兰总统泽连斯基与美国总统拜登举行会晤”,这些都是和拜登相干的事实类知识;而“人有两只眼睛”、“太阳从东方升起”这些属于常识性知识。关于LLM模型能否学习天下知识的研究也有很多,结论也比较一致:LLM确实从练习数据中吸收了大量天下知识,而这类知识主要分布在Transformer的中层和高层,尤其聚集在中层。而且,随着Transformer模型层深增加,可以或许学习到的知识数量逐渐以指数级增加(可参考:BERTnesia: Investigating the capture and forgetting of knowledge in BERT)。实在,你把LLM看作是一种以模型参数体现的隐式知识图谱,如果这么明白,我认为是一点问题也没有的。
“When Do You Need Billions of Words of Pre-training Data?”这篇文章研究了预练习模型学习到的知识量与练习数据量的关系,它的结论是:对于Bert类型的语言模型来说,只用1000万到1亿单词的语料,就能学好句法语义等语言学知识,但是要学习事实类知识,则要更多的练习数据。这个结论实在也是在意料中的,毕竟语言学知识相对有限且静态,而事实类知识则数量巨大,且处于不断变化过程中。而目前研究证明了随着增加练习数据量,预练习模型在各种卑鄙任务中效果越好,这分析了从增量的练习数据中学到的更主要是天下知识。
影象之地:LLM如何存取知识
由上可知,LLM确实从数据中学到了很多语言类及天下知识。那么,对于某条详细的知识,LLM把它存储到了那里?又是如何提取出来的?这也是一个有意思的问题。
显然,知识一定存储在Transformer的模型参数里。从Transformer的结构看,模型参数由两部分构成:多头注意力(MHA)部分占了约莫参数总体的三分之一,三分之二的参数会合在FFN结构中。MHA主要用于盘算单词或知识间的相干强度,并对全局信息进行集成,更大概是在建立知识之间的接洽,大概率不会存储详细知识点,那么很轻易推论出LLM模型的知识主体是存储在Transformer的FFN结构里。


但这样的定位,粒度照旧太粗,无法很好答复详细某条知识是如何存储与提取的,比如 “中国的都城是北京”这条知识,以三元组表达就是<北京,is-capital-of,中国>,此中“is-capital-of”代表实体间关系。这条知识它存储在LLM的那里呢?
“Transformer Feed-Forward Layers Are Key-Value Memories”给出了一个比较新颖的观察视角,它把Transformer的FFN看成存储大量详细知识的Key-Value存储器。如上图所示(图左是原始论文图,实在不太好明白,可以看做了注释的图右,更好明白些),FFN的第一层是个MLP宽隐层,这是Key层;第二层是MLP窄隐层,是Value层。FFN的输入层实在是某个单词对应的MHA的输出效果Embedding,也就是通过Self Attention,将整个句子有关的输入上下文集成到一起的Embedding,代表了整个输入句子的团体信息。


而且这篇文章还指出,Transformer低层对句子的表层模式作出反应,高层对语义模式作出反应,就是说低层FFN存储词法、句法等表层知识,中层和高层存储语义及事实概念知识,这和别的研究结论是一致的。
要我猜,把FFN看成Key-Value存储器这种思绪,很大概不是最终的正确答案,但是间隔最终正确答案的间隔,估计也不太远。
知识涂改液:如何修正LLM里存储的知识
既然我们已知详细的某条天下知识存储在某个或者某些FFN节点的参数里,天然会引发另外一个问题:我们能否修正LLM模型里存储的错误或者过时的知识呢?比如对于问题:“英国的现任宰衡是谁?”鉴于比年来英国宰衡频繁更迭,你猜LLM更倾向输出“鲍里斯”照旧更青睐“苏纳克”?很显着练习数据中包含“鲍里斯”的数据会更多,这种环境很大大概LLM会给堕落误答复,于是我们就有修正LLM里存储的过时知识的必要性。
如果归纳下,目前有三类差别方法来修正LLM里蕴含的知识:
第一类方法从练习数据的源头来修正知识。“Towards Tracing Factual Knowledge in Language Models Back to the Training Data”这篇文章的研究目的是:对于指定的某条知识,我们是否可以定位到是哪些练习数据导致LLM学会了这条知识?答案是肯定的,这意味着我们可以逆向追踪到某条知识对应的练习数据源头。如果利用这项技术,假设我们想要删除某条知识,则可首先定位到其对应的数据源头,删除数据源,然后重新预练习整个LLM模型,这样即可达成删除LLM中相干知识的目的。但是这里有个问题,如果修正一小部分知识,我们就需要重新做一次模型预练习,这样做显着成本太高。所以这种方法不会太有发展前景,大概比较适合那种对于某个特定类别数据的一次性大规模删除场合,不适合少量多次的常规知识修正场景,比如大概比较适合用来做去除偏见等去toxic内容的处理。
第二类方法是对LLM模型做一次fine-tuning来修正知识。一个直观能想到的方法是:我们可以根据要修正成的新知识来构建练习数据,然后让LLM模型在这个练习数据上做fine-tuning,这样引导LLM记着新的知识,遗忘旧的知识。这个方法简朴直观,但是也有一些问题,首先它会带来劫难遗忘问题,就是说除了忘掉该忘的知识,还忘掉了不该忘的知识,导致这么做了之后有些卑鄙任务效果下降。另外,因为目前的LLM模型规模非常大,即使是做fine-tuning,如果次数频繁,实在成本也相称高。对这种方法感兴趣的可以参考“Modifying Memories in Transformer Models”。
另外一类方法直接修改LLM里某些知识对应的模型参数来修正知识。假设我们想要把旧知识<英国,现任宰衡,鲍里斯>,修正到<英国,现任宰衡,苏纳克>。首先我们想办法在LLM模型参数中,定位到存储旧知识的FFN节点,然后可以强行调解更改FFN中对应的模型参数,将旧知识替换成新的知识。可以看出,这种方法涉及到两项关键技术:首先是如何在LLM参数空间中定位某条知识的详细存储位置;其次是如何修正模型参数,来实现旧知识到新知识的修正。关于这类技术的细节,可以参考“Locating and Editing Factual Associations in GPT”和“Mass-Editing Memory in a Transformer”。明白这个修正LLM知识的过程,实在对于更深入明白LLM的内部运作机制是很有帮助的。
规模效应:当LLM越来越大时会发生什么
我们知道,比年来,LLM模型规模在快速增长,目前效果最好的LLM模型,其参数规模大都超过了千亿(100B)参数规模。比如,OpenAI的GPT 3的规模为175B,Google的LaMDA规模为137B,PaLM的规模为540B,DeepMind的Gogher规模为280B等,不一而足。国内也有中文巨型模型,比如清华&智谱GLM规模130B,华为“盘古”规模200B,百度“文心”规模260B,浪潮“源1.0”规模245B。那么,一个很天然的问题就是:随着LLM模型规模不断增长,会发生些什么呢?
预练习模型的应用每每是两阶段的:预练习阶段,及详细场景应用阶段。在预练习阶段,其优化目的是交织熵,对GPT这种自回归语言模型来说,也就是看LLM是否正确预测到了下一个单词;而场景应用阶段,一般要看详细场景的评价指标。一般我们的直觉是:如果LLM模型在预练习阶段的指标越好,天然它办理卑鄙任务的本领就越强。然而,事实并非完全云云。现有研究已证明,预练习阶段的优化指标确实和卑鄙任务表现出正相干关系,但是并非完全正相干。也就是说,只看预练习阶段的指标,来判定一个LLM模型是否够好,这是不敷的。基于此,我们分头来看在这两个差别阶段,随着LLM模型增大,有什么影响。


首先,我们先看在预练习阶段,随着模型规模逐步增大,会发生什么。OpenAI在“Scaling Laws for Neural Language Models”中专门研究了这个问题,并提出LLM模型所遵循的“伸缩法则”(scaling law)。如上图所示,这个研究证明:当我们独立增加练习数据量、模型参数规模或者延长模型练习时间(比如从1个Epoch到2个Epoch),预练习模型在测试集上的Loss都会单调降低,也就是说模型效果越来越好。
既然三个因素都紧张,那么我们在现实做预练习的时间,就有一个算力如何分配的决策问题:假设用于练习LLM的算力总预算(比如多少GPU小时或者GPU天)给定,那么是应该多增加数据量、减少模型参数呢?照旧说数据量和模型规模同时增加,减少练习步数呢?此消彼长,某个要素规模增长,就要降低别的因素的规模,以维持总算力不变,所以这里有各种大概的算力分配方案。最终OpenAI选择了同时增加练习数据量和模型参数,但是接纳早停计谋(early stopping)来减少练习步数的方案。因为它证明了:对于练习数据量和模型参数这两个要素,如果只单独增加此中某一个,这不是最好的选择,最好能按照一定比例同时增加两者,它的结论是优先增加模型参数,然后才是练习数据量。假设用于练习LLM的算力总预算增加了10倍,那么应该增加5.5倍的模型参数量,1.8倍的练习数据量,此时模型效果最佳。
DeepMind的一项研究(参考:Training Compute-Optimal Large Language Models)更深入地探究了这个问题,其根本结论和OpenAI的结论差不多,比如确实需要同时增加练习数据量和模型参数,模型效果才会更好。而很多大模型在做预练习的时间,并没有考虑这一点,很多LLM大模型只是单调增加模型参数,而固定住了练习数据量,这个做法实在是不对的,限制了LLM模型的潜力。但是它修正了两者的比例关系,认为练习数据量和模型参数是划一紧张的,也就是说,假设用于练习LLM的算力总预算增加了10倍,那么应该增加3.3倍的模型参数量,3.3倍的练习数据量,这样模型效果才最好。
这意味着:增加练习数据量的紧张性,比我们之前所认为的,还要紧张。基于这个认知,DeepMind在设计Chinchilla模型时,在算力分配上选择了另外一种设置:对标数据量300B、模型参数量280B的Gopher模型,Chinchilla选择增加4倍的练习数据,但是将模型参数降低为Gopher的四分之一,约莫为70B。但是无论预练习指标,照旧很多卑鄙任务指标,Chinchilla效果都要优于规模更大的Gopher。
这带给我们如下启示:我们可以选择放大练习数据,并同比例地减少LLM模型参数,以到达在不降低模型效果的前提下,极大缩小模型规模的目的。缩小模型规模有很多好处,比如在应用的时间,推理速度会快很多等,无疑这是一个很有出息的LLM发展门路。
以上是从预练习阶段来看模型规模的影响,如果从LLM办理卑鄙详细任务效果的角度来看,随着模型规模增大,差别类型的任务有差别的表现,详细而言,有以下三类环境。


第一类任务完美体现了LLM模型的scaling law,就是说随着模型规模逐步放大,任务的表现越来越好,如上图里的(a)图所示。这类任务通常符合如下共性:它们每每都是知识麋集型任务,也就是说如果LLM模型包含的知识量越多,这类任务表现越好。而很多研究已经证明越大的LLM模型学习服从越高,也就是说相同练习数据量,模型越大任务效果越好,分析面临的即使是同样的一批练习数据,更大的LLM模型相对规模小一些的模型,从中学到了更多的知识。更何况一般环境下,在增大LLM模型参数的时间,每每会同步增加练习数据量,这意味着大模型可以从更多数据中学习更多的知识点。这些研究可以很好地表明上图,为何随着模型规模增大,这些知识麋集型的任务效果越来越好。大多数传统的天然语言明白类任务,实在都属于这种知识麋集型任务,而很多任务在近两年获得了极大的效果提拔,乃至超过了人类表现。很显着,这大概率是LLM模型的规模增长带来的,而非归功于某项详细的技术改进。
第二类任务展现出LLM具备某种“涌现本领(Emergent Ability)”,如上图(b)所示。所谓“涌现本领”,指的是当模型参数规模未能到达某个阀值时,模型根本不具备办理此类任务的任何本领,体现为其性能和随机选择答案效果相称,但是当模型规模跨过阀值,LLM模型对此类任务的效果就出现突然的性能增长。也就是说,模型规模是解锁(unlock)LLM新本领的关键,随着模型规模越来越大,会逐渐解锁LLM越来越多的新本领。这是个很神奇的现象,因为它意味着如下让人对未来可报乐观预期的大概:大概很多任务,目前LLM还不能很好地办理,乃至站在如今这个时候的我们看起来,LLM完全没有本领办理这类任务,但因LLM具备“涌现本领”,所以如果我们继续推大模型,也许某一天它的这项本领就被突然解锁了。LLM模型的规模增长会给我们带来意想不到的精彩礼物。
“Beyond the Imitation Game: Quantifying and extrapolating the capabilities of language models”这篇文章指出,这类体现出“涌现本领”的任务也有一些共性:这些任务一般由多步骤构成,要办理这些任务,每每需要先办理多个中间步骤,而逻辑推理本领在最终办理这类任务中发挥紧张作用。头脑链(Chain of Thought)Prompting是典型的增强LLM推理本领的技术,能大幅提拔此类任务的效果,关于CoT技术,在随后末节内容会做表明,此处暂不展开。
问题是,为何LLM会出现这种“涌现本领”现象呢?上述文章以及“Emergent Abilities of Large Language Models”给出了几个大概的表明:
一种大概表明是有些任务的评价指标不敷平滑。比如说有些天生任务的判定尺度,它要求模型输出的字符串,要和尺度答案完全匹配才算对,否则就是0分。所以,即使随着模型增大,其效果在逐步变好,体现为输出了更多的正确字符片段,但是因为没有完全对,只要有任何小错误都给0分,只有当模型足够大,输出片段全部正确才能得分。也就是说,因为指标不敷平滑,所以不能体现LLM实在正在逐步改善任务效果这一现实,看起来就是“涌现本领”这种外在表现。
另外一种大概的表明是:有些任务由若干中间步骤构成,随着模型规模增大,办理每个步骤的本领也在逐步增强,但是只要有一个中间步骤是错的,最终答案就是错的,于是也会导致这种表面的“涌现本领”现象。
固然,上面的表明目前还都是猜想,至于为何LLM会出现这种现象,还需要进一步更深入的研究。


还有少部分任务,随着模型规模增长,任务的效果曲线展现出U形特性:随着模型规模逐渐变大,任务效果逐渐变差,但是当模型规模进一步增长,则效果开始越来越好,出现出U形增长趋势,如上图所示的粉赤色PaLM模型在两个任务上的指标走势。为何这些任务表现得云云特别呢?“Inverse scaling can become U-shaped”这篇文章给出了一种表明:这些任务,内部实在隐含了两种差别类型的子任务,一种是真正的任务,另外一种是“干扰任务(distractor task)”。当模型规模小的时间,无法识别任意一种子任务,所以模型的表现跟随机选择答案差不多,当模型增长到中等规模的时间,主要执行的是干扰任务,所以对真正的任务效果有负面影响,体现为真正任务效果的下降,而当进一步增加模型规模,则LLM可以忽略干扰任务,执行真正的任务,体现为效果开始增长。
对于那些随着模型规模增大,效果一直下降的任务,如果接纳头脑链(CoT)Prompting,则部分任务的表现转换为遵循Scaling law,即模型规模越大效果越好,而别的任务则转换为U性增长曲线。这实在侧面分析了:此类任务应属于推理类型的任务,所以加入CoT后任务表现会发生质的变化。
人机接口:从In Context Learning到Instruct明白
一般我们经常提到的人和LLM的接口技术包罗:zero shot prompting、few shot prompting、In Context Learning,以及Instruct。这些实在都是表达某个详细任务的描述方式。不外如果你看文献,会发现叫法比较乱。
此中Instruct 是ChatGPT的接口方式,就是说人以天然语言给出任务的描述,比如“把这个句子从中文翻译成英文”,类似这种。zero shot prompting我明白实在就是如今的Instruct的早期叫法,从前大家风俗叫zero shot,如今很多改成叫Instruct。尽管是一个内涵,但是详细做法是两种做法。早期大家做zero shot prompting,现实上就是不知道怎么表达一个任务才好,于是就换差别的单词或者句子,反复在尝试好的任务表达方式,这种做法目前已经被证明是在拟合练习数据的分布,实在没啥意思。目前Instruct的做法则是给定命令表述语句,试图让LLM明白它。所以尽管表面都是任务的表述,但是思绪是差别的。
而In Context Learning和few shot prompting意思类似,就是给LLM几个示例作为范本,然后让LLM办理新问题。我个人认为In Context Learning也可以明白为某项任务的描述,只是Instruct是一种抽象的描述方式,In Context Learning是一种例子示范的例子分析法。固然,鉴于目前这几个叫法用的有点乱,所以上述明白仅代表个人看法。
所以我们此处只对In Context Learning和Instruct进行介绍,不再提zero shot和few shot了。
神秘的In Context Learning


Fine-tuning和In Context Learning表面看似都提供了一些例子给LLM,但两者有质的差别(参考上图示意):Fine-tuning拿这些例子当作练习数据,利用反向流传去修正LLM的模型参数,而修正模型参数这个动作,确实体现了LLM从这些例子学习的过程。但是,In Context Learning只是拿出例子让LLM看了一眼,并没有根据例子,用反向流传去修正LLM模型参数的动作,就要求它去预测新例子。既然没有修正模型参数,这意味着貌似LLM并未履历一个学习过程,如果没有履历学习过程,那它为何可以或许做到仅看一眼,就能预测对新例子呢?这正是In Context Learning的神奇之处。这是否让你想起了一句歌词:“只是因为在人群中多看了你一眼 再也没能忘掉你容颜”,而这首歌名叫“传奇”。你说传奇不传奇?
看似In Context Learning没从例子里学习知识,现实上,岂非LLM通过一种希奇的方式去学习?照旧说,它确实也没学啥?关于这个问题的答案,目前照旧未解之谜。现有一些研究各有各的说法,五花八门,很难判定哪个报告的是事实的真相,乃至有些研究结论还相互矛盾。这里提供几个目前的说法,至于谁对谁错,只能你自己把握了。固然,我认为寻求这个神奇现象背后的真相,是一个好的研究课题。


总而言之,目前这照旧一个未解之谜。
神奇的Instruct明白
我们可以把Instruct当作一种方便人类明白的任务表述,在这个前提下,目前关于Instruct的研究可以分成两种:偏学术研究的Instruct,以及关于人类真实需求描述的Instruct。


我们先来看第一种:偏学术研究的Instruct。它的核心研究主题是多任务场景下,LLM模型对Instruct明白的泛化本领。如上图中FLAN模型所示,就是说有很多NLP任务,对于每个任务,研究人员构造一个或者多个Prompt模版作为任务的Instruct,然后用练习例子对LLM模型进行微调,让LLM以同时学习多个任务。练习好模型后,给LLM模型一个它没见过的全新任务的Instruct,然后让LLM 办理zero shot任务,从任务办理得是否足够好,来判定LLM模型是否有对Instruct明白的泛化本领。
如果归纳下目前的研究结论(可参考“Scaling Instruction-Fine-tuned Language Models”/“Super-NaturalInstructions: Generalization via Declarative Instructions on 1600+ NLP Tasks”),可以或许有效增加LLM模型Instruct泛化本领的因素包罗:增加多任务的任务数量、增加LLM模型巨细、提供CoT Prompting, 以及增加任务的多样性。如果采取任意一项措施,都可以增加LLM模型的Instruct明白本领。
第二种是人类真实需求下的Instruct,这类研究以InstructGPT和ChatGPT为代表。这类工作也是基于多任务的,但是和偏向学术研究类工作最大的差别,在于它是面向人类用户真实需求的。为什么这么说呢?因为它们用于LLM多任务练习的任务描述Prompt,是从大量用户提交的真实请求中抽样而来的,而不是固定好研究任务的范围,然后让研究人员来写任务描述prompt。这里所谓的“真实需求”,体如今两个方面:首先,因为是从用户提交的任务描述里随机抽取的,所以涵盖的任务类型更多样化,也更符合用户的真实需求;其次,某个任务的prompt描述,是用户提交的,体现了一般用户在表达任务需求时会怎么说,而不是你认为用户会怎么说。很显着,这类工作改出来的LLM模型,用户体验会更好。
InstructGPT论文里,也拿这种方法和FLAN那种Instruct based方法做了比较。首先在GPT3上用FLAN提到的任务、数据以及Prompt模版进行微调,来在GPT 3上复现FLAN方法,然后和InstructGPT进行比较,因为InstructGPT的基础模型也是GPT3,所以只有数据和方法的差别,两者可比,效果发现FLAN方法的效果,间隔InstructGPT有很大的差距。那么背后的缘故原由是什么呢?论文分析数据后认为,FLAN方法涉及到的任务领域相对少,是InstructGPT涉及领域的子集,所以效果不好。也就是说,FLAN论文里涉及到的任务和用户真实需求是不符的,而这导致在真实场景下效果不敷好。而这对我们的启示是:从用户数据中收集真实需求,这事故是很紧张的。
In Context Learning和Instruct的接洽
如果我们假设In Context Learning是用一些例子来具象地表达任务命令,Instruct是一种更符合人类风俗的抽象任务描述。那么,一个很天然的问题是:它们之间有什么接洽吗?比如,我们是否可以或许提供给LLM完成某个任务的若干详细示例,让LLM找出其对应的天然语言描述的Instruct命令?


目前有零星的工作在探索这个问题,我认为这个方向是很有研究价值的。先说答案,答案是:Yes,LLM Can。“Large Language Models Are Human-Level Prompt Engineers”是做这个方向很有趣的工作,如上图所示,对于某项任务,给LLM一些示例,让LLM主动天生可以或许描述这项任务的天然语言命令,然后它再用LLM天生的任务描述去测试任务效果。它利用的基础模型是GPT 3和InstructGPT,经过这项技术加持后,LLM天生的Instruct的效果相比未接纳这项技术的GPT 3 以及InstuctGPT来说,指标有极大地提拔,而且在一些任务上超过人类的表现。
这分析了:具象的任务示例和任务的天然语言描述之间,有种神秘的内在接洽。至于这种接洽到底是什么?我们目前对此还一无所知。
聪明之光:如何增强LLM的推理本领
目前很多研究已证明LLM对于知识具有强大的影象本领,但是,一般我们不会因为一个人影象本领强,就说这人很聪明,是否具有强大的推理本领,每每是我们判定一个人是否聪明的紧张尺度。类似的,如果LLM的效果想让人觉得很惊艳,强大的推理本领是必备的。推理本领本质上是综合运用很多相干知识点,去推导出新知识或新结论。关于LLM的推理本领,是最近一年来LLM里最紧张和热门的研究领域之一。于是,我们关心的问题就是:LLM具备推理本领吗?如果具备,那么它的推理本领够强吗?
这两个问题目前的答案似乎应该是:当模型规模足够大的时间,LLM自己是具备推理本领的,在简朴推理问题上,LLM已经到达了很好的本领,但是复杂推理问题上,还需要更多深入的研究。
如果梳理现有LLM推理相干工作的话,我把它们归到两大类,体现出挖掘或促进LLM推理本领差别的技术思绪:第一类研究比较多,可以统称为基于Prompt的方法,核心思想是通过合适的提示语或提示样本,更好地引发出LLM自己就具备的推理本领,Google在这个方向做了大量很有成效的工作。第二类做法是在预练习过程中引入步伐代码,和文本一起参与预练习,以此进一步增强LLM的推理本领,这应该是OpenAI实践出的思绪。比如ChatGPT肯定具备很强的推理本领,但它并不要求用户必须提供一些推理示例,所以ChatGPT强大的推理本领,大概率来源于利用代码参与GPT 3.5的预练习。
这两种思绪实在大方向是迥异的:利用代码增强LLM推理本领,这体现出一种通过增加多样性的练习数据,来直接增强LLM推理本领的思绪;而基于Prompt的方法,它并不会促进LLM自己的推理本领,只是让LLM在办理问题过程中更好地展示出这种本领的技术方法。可以看出,前者(代码方法)治本,后者治标。固然,两者实在也是互补的,但从长远看,治本的方法更紧张。
基于Prompt的方法
这方面工作非常多,如果归纳一下的话,大致可以分为三条技术门路。


第一种思绪是直接在问题上追加辅助推理Prompt。这种方法简朴直接,但在众多领域都很有效。这个做法是由“Large language models are zero-shot reasoners”提出的,也被称为zero-shot CoT。详细而言,分为两个阶段(如上图所示),第一阶段在提问的问题上追加“Let’s think step by step”这句提示语,LLM会输出详细的推理过程;第二阶段,在第一阶段的问题后,拼接LLM输出的详细推理过程,并再追加Prompt=“Therefore, the answer (arabic numerals) is”,此时LLM会给出答案。云云简朴的操作,却可以大幅增加LLM在各项推理任务中的效果,比如在数学推理测试集GSM8K上,加上提示语后,推理准确率直接从原先的10.4%提拔到了40.4%,可谓神奇。
为什么LLM会具备给一句“Let’s think step by step”提示语,就能列出详细的推理步骤并算出答案呢?其缘故原由目前尚无定论,我的推测是:很大概因为预练习数据里面存在大量的此种数据,就是以“Let’s think step by step”开头,然后后面是详细的推理步骤,最后给出答案,而LLM在预练习的时间记着了这些模式。而当我们输入这个提示语的时间,引发LLM模糊得“回想”起某些例子的推导步骤,于是即可模仿这些例子进行步骤推理并给出答案。固然这只是我的无依据推论,若事实真的云云,如果你看过后面介绍的尺度CoT做法,会发现Zero-shot CoT 本质上和尺度CoT很大概没什么区别,只是尺度CoT由人工来写推理步骤的示例,而Zero-shot CoT大概率是通过提示语,激活了影象中的某些包含推理步骤的示例,很大概是云云区别。而尺度CoT效果比Zero-Shot CoT效果好也完全可以明白,因为毕竟靠LLM回想示例,精准性估计不会太高,而人工给出的示例,准确性是有保障的,所以天然尺度CoT效果会更好。
这侧面分析了一个原理,就是LLM自己是具备推理本领的,只是我们没有办法把它的这种本领引发出来而已,通过合适的提示语来进行两步提示,就在一定水平上可以释放出它的这种潜力。另外,对于中文,很大概存在另外一个黄金提示语,比如“详细解题思绪如下”,类似这种,因为中文语料在解说推理步骤的时间,经常用的引导句和“让我们一步一步来思考”应该是差别的,这是显着的西方说法,而探索出这个中文黄金提示语,实在也是很有必要的。
第二种思绪一般被称为基于示例的头脑链(few-shot CoT,Chain of Thought)Prompting。这个方向目前是LLM推理研究的主方向,很多工作都是在这个思绪上做的,我们简朴介绍几个效果显著的代表性工作,根本能代表CoT的技术发展方向。


CoT的主体思想实在很直白;为了教会LLM模型学会推理,给出一些人工写好的推理示例,示例里把得到最终答案前,一步步的详细推理步骤说清晰,而这些人工写的详细推理过程,就是头脑链Prompting,详细例子可参照上图中蓝色笔墨部分。CoT的意思是让LLM模型明确一个原理;就是在推理过程中,步子不要迈得太大,否则很轻易堕落,改变头脑模式,化大问题为小问题,步步为营,积小胜为大胜。最早明确提出CoT这个概念的文章是“Chain of thought prompting elicits reasoning in large language models”,论文发布于22年1月份,固然做法很简朴,但是应用CoT后LLM模型的推理本领得到了巨大提拔,GSM8K数学推理测试集准确率进步到60.1%左右。固然,这种给出详细推理步骤和中间过程的思想,并非CoT最早提出的,更早一些的“scratchpad”技术(可参考:Show Your Work: Scratchpads for Intermediate Computation with Language Models)首先接纳了类似的思绪。


CoT提出不久,很快在22年3月份,一项被称为“Self-Consistency”的改进技术就将GSM8K测试集准确率进步到74.4%,提出这项改进的论文是“Self-Consistency Improves Chain of Thought Reasoning in Language Models”。“Self-Consistency”的思绪也很直观(参考上图):首先可以利用CoT给出几个写了推理过程的示例,然后要求LLM对给定的问题进行推理,如果是CoT,直接输出一个推理过程和答案,整个过程就结束了。“Self-Consistency”则不然,它要求LLM输出多个差别的推理过程和答案,然后接纳投票的方式选出最佳答案,思绪非常简朴直接,但是效果也确实好。“Self-Consistency”实在是教导LLM学会这么一个原理:孔乙己说过茴香豆的“茴”字有四种写法,类似的,一个数学题的正确解法也可以有很多种,每个差别的推导过程都指向最终的答案。条条大路通罗马,虽说也有个别迷路走到北京的,但是迷路的毕竟是少数,看看大多数人走到那里,那里就是正确答案。简朴的方法每每蕴含着深刻的哲学含义,是不是这原理?
再往后,“On the Advance of Making Language Models Better Reasoners”这个工作在“Self-Consistency”基础上,进一步集成了“从一个Prompt问题拓展到多个Prompt问题、查抄推理中间步骤的正确性以及对多个输出的答复加权投票”这三个改进点,将GSM8K测试集准确率进步到83%左右。


第三种思绪体现了一种分治算法的思想。固然这个所谓“分治”是我归纳的,别人没这么说。这种思绪的核心思想是:对于一个复杂的推理问题,我们把它分解成若干轻易办理的子问题,一一办理掉子问题后,我们再从子问题的答案推导复杂问题的答案。你看这确实比较类似分治算法的思想吧。我个人觉得,这种思绪大概才是展现问题本质、最终办理LLM复杂推理问题正宗的道路。我们以“Least-to-most prompting”技术为例来分析这种思绪的一种详细实现方式,如上图所示:它分为两个阶段,第一个阶段,从原始问题我们可以得知最终要问的问题是什么,我们假设最终问题是Final Q,然后从原始问题填充Prompt模版:“如果要办理Final Q问题,那么我需要先办理”,然后把原始问题和这个Prompt交给LLM,让LLM模型给出答案,即是让LLM给出最终问题的前置子问题Sub Q;接下来我们进入第二个阶段,让LLM先答复刚才拿到的子问题Sub Q,并拿到对应的答案,然后原始问题拼接子问题Sub Q及对应答案,再去问LLM最终谁人问题Final Q,此时LLM会给出最后的答案。云云这般,体现出拆解子问题,并从子问题的答案逐步找出最终答案的思绪。
代码预练习增强LLM推理本领
以上是目前利用Prompt引发LLM模型推理本领的三种主流做法,而关于LLM的推理本领,目前还观察到一个有趣且费解的现象:除了文本外,如果可以或许加入步伐代码一起参与模型预练习,则能大幅提拔LLM模型的推理本领。这个结论从不少论文的实验部分都可以得出(可以参考:AUTOMATIC CHAIN OF THOUGHT PROMPTING IN LARGE LANGUAGE MODELS/Challenging BIG-Bench tasks and whether chain-of-thought can solve them等论文的实验部分)。


上图给出了一份实验数据,来自于论文“On the Advance of Making Language Models Better Reasoners”,此中GPT3 davinci就是尺度的GPT 3模型,基于纯文本练习;code-davinci-002(OpenAI内部称为Codex)是同时在Code和NLP数据上练习的模型。如果比较两者效果,可以看出,不论接纳详细哪种推理方法,仅仅是从纯文本预练习模型切换到文本和Code混合预练习模型,在几乎全部测试数据集合上,模型推理本领都得到了巨大的效果提拔,比如我们以“Self Consistency”方法为例,在大多数据集合上的性能提拔,都直接超过了20到50个百分点,这是很可怕的性能提拔,而实在在详细推理模型层面,我们什么也没做,仅仅是预练习的时间除了文本,额外加入了步伐代码而已。
除了这个现象,从上图数据中,我们还可以得出别的一些结论,比如GPT 3这种纯文本预练习模型,实在是具备相称水平的推理本领的,除了在GSM8K这种数学推理上效果比较差外,别的推理数据数据集合表现也还可以,前提你需要接纳合适的方法,来引发出它自己就具备的这种本领;再比如,text-davinci-002,也就是在code-davinci-002基础上加入instruct fine-tuning后的模型(就是加入InstructGPT或ChatGPT模型的第一步),其推理本领要弱于Codex,但是有别的研究表明它在天然语言处理任务又要强于Codex。而这貌似分析了,加入instruct fine-tuning,会侵害LLM模型的推理本领,但是会在一定水平上提拔天然语言明白本领。而这些结论实在都是很有意思的,也能启发后续进一步的思考和探索。
那么,一个天然的疑问是:为何预练习模型可以从代码的预练习中获得额外的推理本领?确切缘故原由目前未知,值得深入探索。我推测大概是因为原始版本的Codex(只利用代码练习,可参考文献:Evaluating Large Language Models Trained on Code)的代码练习是从文本天生代码,而且代码中每每包含很多文本注释,本质上这类似于预练习模型做了<文本,Code>两种数据的多模态对齐工作。而数据中必然包含相称比例的数学或逻辑问题的代码、描述和注释,很显着这些数学类或逻辑推理类的数据,对于办理卑鄙数学推理问题是有帮助的,我猜大概率缘故原由在此。
关于LLM推理本领的思考
上面介绍了LLM推理的主流技术思绪和现有的一些结论,接下来谈谈我对LLM模型推理技术的思考,以下内容纯个人推断,没有太多证据,还请审慎参考。我的判定是:固然最近一年来,关于引发LLM的推理本领,这方面的技术希望很快,也取得了很大的技术进步,但是总体感觉是,我们大概走在正确的方向上,但是间隔接触到真正的问题本质还有一段间隔,对此要有更深入的思考和探索。
首先,我比较赞同上述分治算法的主体思绪,对于复杂的推理问题,我们应该把它拆解成若干简朴的子问题,因为子问题对于LLM来说答复正确的概率就大很多,让LLM一一答复子问题后,再逐步推导出最终答案。受到“Least-to-most prompting”技术的启发,如果进一步思考,我觉得LLM推理本质上很大概会是如下两种大概的此中之一:不断和LLM进行交互的图上推理问题,抑或是不断和LLM进行交互的步伐流程图执行问题。


先说图上推理问题,如上图所示,假设我们有办法可以或许把复杂问题拆解成由子问题或者子步骤构成的图结构,图中的节点是子问题或者子步骤,图中的边代表了子问题之间的依赖关系,就是说只有答复好子问题A,才能答复子问题B,而且图中大概率存在循环结构,就是反复做某几个子步骤。假设我们可以或许得到上述的子问题拆解图,那么可以根据依赖关系,引导LLM一步一步按照图结构,答复必须首先答复的子问题,直到推导出最终答案。


再说步伐流程图问题,参考上图,假设我们有办法把复杂问题拆解成子问题或子步骤,并产生一个由子步骤构成的类似步伐流程图的结构,在这个结构里,有些步骤会反复执行多次(循环结构),有些步骤的执行需要进行条件判定(条件分支)。总而言之,在执行每个子步骤的时间和LLM进行交互,得到子步骤的答案,然后按照流程不断执行,直到输出最终答案。类似这种模式。假设这个思绪大致正确的话,也许可以从这个角度来表明为何加入代码会增强预练习模型的推理本领:大概率因为<文本,代码>的多模态预练习模型,在模型内部是通过类似这种隐含的步伐流程图作为两个模态的桥梁,将两者接洽起来的,即由文本描述到隐含的流程图,再映射到由流程图产生详细的代码。也就是说,这种多模态预练习,可以增强LLM模型从文本构建出隐含的流程图并按照流程图执行的本领,也就是加强了它的推理本领。
固然,上述思绪最大的问题是,我们如何根据文本描述的问题,可以或许靠LLM模型,或者别的模型,得到图结构或者流程图结构?这个大概是此中的难点。一种大概的思绪就类似继续增强文本和更高质量的代码预练习,走隐式学习内部隐含结构的方法。而目前的CoT技术,如果套到上述思绪来思考的话,可以这么明白:尺度CoT,实在就是靠天然语言文本来描述图结构或者步伐流程图的;而“Least-to-most prompting”技术,则是试图根据最后一个图节点,靠倒推来试图推导出此中的图结构,但是很显着,目前的方法限制了它倒推的深度,也就是说它只能推导出非常简朴的图结构,这正是限制它本领的所在。
未来之路:LLM研究趋势及值得研究的重点方向
这里列出一些我个人认为比较紧张的LLM研究领域,或值得深入探索的研究方向。
探索LLM模型的规模天花板
尽管继续推大LLM模型的规模,这事看似没有技术含量,但是实在这个事故非常紧张。我个人判定,自从Bert出现以来,到GPT 3,再到ChatGPT,大概率这些给人印象深刻的关键技术突破,核心贡献都来自于LLM模型规模的增长,而非某项详细技术。说不定,揭开AGI真正的钥匙就是:超大规模及足够多样性的数据、超大规模的模型,以及充分的练习过程。再者,做超大规模的LLM模型,对技术团队的工程实现本领要求黑白常高的,也不能认为这事故缺乏技术含量。
那么继续推大LLM模型规模,有什么研究意义呢?我觉得有两方面的价值。首先,如上所述,我们已知,对于知识麋集型的任务,随着模型规模越大,各种任务的效果会越来越好;而对很多推理类型的有难度的任务,加上CoT Prompting后,其效果也出现出遵循Scaling law的趋向。那么,很天然的一个问题就是:对于这些任务,LLM的规模效应,能将这些任务办理到何种水平?这是包罗我在内,很多人关心的问题。其次,考虑到LLM具备的神奇的“涌现本领”,如果我们继续增加模型规模,它会解锁哪些让我们意想不到的新本领呢?这也是很有意思的问题。考虑到以上两点,我们仍旧需要不断增大模型规模,看看模型规模对办理各类任务的天花板在那里。
固然,这种事故也就只能说说,对99.99%的从业者来说,是没有机会和本领做这个事故的。要做这个事故,对研究机构的财力及投入意愿、工程本领、技术热情,都有极高的要求,缺一不可。能做这事故的机构,粗估下来,国外不超过5家,国内不超过3家。固然,考虑到成本问题,未来也许会出现“股份制大模型”,就是有本领的几家机构合作,群策群力,一起来共建超级大模型的现象。
增强LLM的复杂推理本领
正如之前对LLM推理本领的叙述,尽管LLM在最近一年推理本领得到了很大的提拔,但是很多研究(参考:Limitations of Language Models in Arithmetic and Symbolic Induction/Large Language Models Still Can’t Plan)表明,目前LLM可以或许办理得比较好的推理问题,每每都相对简朴,LLM的复杂推理本领仍旧薄弱,比如即使是简朴的字符拷贝推理或者加减乘除运算,当字符串或者数字非常长的时间,LLM推理本领会极速下降,再比如举动规划本领等复杂推理本领很弱。总而言之,加强LLM的复杂推理本领,应该是LLM未来研究中最紧张的环节之一。
前文有述,加入代码加入预练习,这是一种直接增强LLM推理本领的方向。这个方向目前研究尚显不敷,更像是实践经验的总结,探索背后的原理,并进而引入更多类型除代码外的新型数据来增强LLM的推理本领,这大概是更本质提拔推理本领的方向。
LLM纳入NLP之外更多别的研究领域
目前的ChatGPT擅长NLP和Code任务,作为通向AGI的紧张种子选手,将图像、视频、音频等图像与多模态集成进入LLM,乃至AI for Science、机器人控制等更多、差别化更显着的别的领域逐步纳入LLM,是LLM通往AGI的必经之路。而这个方向才刚刚开始,因此具备很高的研究价值。
更易用的人和LLM的交互接口
如前所述,ChatGPT的最大技术贡献即在此。但是很显着,目前的技术并不完美,肯定还有很多命令LLM明白不了。所以,沿着这个方向,探求更好的技术,来让人类利用自己风俗的命令表达方式,而LLM又能听懂,这是个新的,且非常有前景的技术方向。
建立高难度的综合任务评测数据集
好的评测数据集,是引导技术不断进步的基石。随着LLM模型逐步增大,任务效果快速提拔,导致很多尺度测试集快速过时。也就是说,这些数据集合相对现有技术来说,太轻易了,在没有难度的测试集合下,我们不知道目前技术的缺陷和盲点在那里。所以构建高难度的测试集合,是促进LLM技术进步的关键所在。
目前行业应出现了一些新的测试集,有代表性的包罗BIGBench、OPT-IML等。这些测试集合体现出一些特性,比如相对LLM现有技术具备一定的难度、综合了各种各样多种类型的任务等。
受到ChatGPT的启发,我觉得除此外应纳入另一考虑因素:体现真实用户需求。就是说,这些任务的表述由用户真实发起,这种方式构建出来的LLM模型,才能办理用户现实需求。
除此外,信赖LLM会快速将本领溢出到NLP之外的领域,而如何融入更多别的领域的评测数据,也是需要提前去考虑。
高质量数据工程
对于预练习模型来说,数据是其根本,预练习过程可以明白为从数据中汲取此中所包含知识的过程。因此,我们需要进一步加强对高质量数据的挖掘、收集及洗濯等工作。
关于数据,需要考虑两个方面:数据的质量和数量。而根据T5的对比实验,我们可以得出结论:在数量和质量两个因素里,质量优先,正确的道路应该是在保证数据质量的前提下,再去增大数据规模。
数据质量,包罗数据的信息含量以及数据的多样性等多个衡量尺度,比如Wiki显着就属于天下知识密度极高的高质量数据,这是从信息含量来说的;而增加数据类型的多样性,无疑是引发LLM各种新本领的根本,比如加入问答网站的数据,对于LLM的QA本领提拔是有直接帮助的。多样化的数据赋予了LLM更好办理更多差别类型任务的本领,所以,这大概是数据质量里最关键的尺度。
关于数据数量,原则上互联网上公开辟布的数据都可以纳入LLM模型的预练习过程。那么,它的极限在那里?“Will we run out of data? An analysis of the limits of scaling datasets in Machine Learning” 对此进行了估算,结论是到2026年左右,高质量的NLP数据将会用光,低质量NLP数据会在2030到2050年用光,而低质量图像数据会在2030到2060年用光。而这意味着:要么到时我们有新类型的数据源,要么我们必须增加LLM模型对数据的利用服从。否则,目前这种数据驱动的模型优化方式将会停止进步,或者收益减少。
超大LLM模型Transformer的稀疏化
目前规模最大的LLM中,有相称比例的模型采取了稀疏(Sparse)结构,比如GPT 3、PaLM、GLaM等,GPT 4大概率也会走稀疏模型门路。之所以接纳Sparse 化的模型,主要好处是它可以极大减少LLM的练习时间和在线推理时间。Switch Transformer论文里指出:在相同算力预算的前提下,利用稀疏化Transformer,相对Dense Transformer,LLM模型的练习速度可以提拔4倍到7倍。为何Sparse模型可以加速练习和推理时间呢?这是因为尽管模型参数巨大,但是对于某个练习实例,Sparse模型通过路由机制,只利用整个参数中的一小部分,参与练习和推理的活跃参数量比较少,所以速度快。
我认为未来超大的LLM模型大概率会收敛到稀疏模型。主要有两个缘故原由:一方面,现有研究表明(参考:Large Models are Parsimonious Learners: Activation Sparsity in Trained Transformers),尺度的Dense Transformer在练习和推理时,它自己也是稀疏激活的,就是说只有部分参数会被激活,大部分参数没有参与练习和推理过程。既然这样,我们不如直接迁徙到稀疏模型;另外,毫无疑问LLM模型的规模会继续推大,而高昂的练习成本是妨碍其进一步扩大模型的紧张阻力,利用稀疏模型可以极大降低超大模型的练习成本,所以随着模型规模越大,稀疏模型带来的收益越显着。考虑到这两个方面,大概率未来更大的LLM模型会接纳稀疏模型方案。
那为何目前别的大规模模型不走稀疏模型的门路呢?因为Sparse模型存在练习不稳固、轻易过拟合等问题,不太轻易练习好。所以,如何修正稀疏模型面临的问题,设计出更轻易练习的稀疏模型,是很紧张的未来研究方向。 
取经之路:复刻ChatGPT时要注意些什么
如果希望能复刻类似ChatGPT这种效果令人惊艳的LLM模型,综合目前的各种研究结论,在做技术选型时需要重点权衡如下问题:
首先,在预练习模式上,我们有三种选择:GPT这种自回归语言模型,Bert这种双向语言模型,以及T5这种混合模式(Encoder-Decoder架构,在Encoder采取双向语言模型,Decoder采取自回归语言模型,所以是一种混合结构,但其本质仍属于Bert模式)。我们应选择GPT这种自回归语言模型,其缘故原由在本文范式转换部分有做分析。目前看,国内LLM在做这方面技术选型的时间,貌似很多都走了Bert双向语言模型或T5混合语言模型的技术门路,很大概方向走偏了。
第二,强大的推理本领是让用户认可LLM的紧张心理基础,而如果希望LLM可以或许具备强大的推理本领,根据目前经验,最好在做预练习的时间,要引入大量代码和文本一起进行LLM练习。至于此中的原理,在本文前面相干部分有对应分析。
第三,如果希望模型参数规模不要那么巨大,但又希望效果仍旧足够好,此时有两个技术选项可做设置:要么增强高质量数据收集、挖掘、清理等方面的工作,意思是我模型参数可以是ChatGPT/GPT 4的一半,但是要想到达类似的效果,那么高质量练习数据的数量就需要是ChatGPT/GPT 4模型的一倍(Chinchilla的路子);另外一个可以有效减小模型规模的门路是采取文本检索(Retrieval based)模型+LLM的门路,这样也可以在效果相称的前提下,极大减少LLM模型的参数规模。这两个技术选型不互斥,反而是互补的,也便是说,可以同时采取这两个技术,在模型规模相对比较小的前提下,到达超级大模型类似的效果。
第四,超级大模型因为模型规模大,所以练习成本过高,导致很少有机构有本领去做这件事。而且由上文分析可见,继续不断推大LLM模型规模是肯定会发生、也应该去做的事故。于是,如何通过技术本领降低LLM的练习成本就很紧张。LLM的特性抽取器Sparse化是有效降低模型练习及推理成本的技术选择。由此可见,随着模型越来越大,LLM模型Sparse化是一个应该考虑的选项。
第五,ChatGPT是目前最接近抱负LLM的技术方案,而抱负中的LLM应该是以一个几乎无所不能的基础通用大模型作为依托,来支持各种各样的上层任务类型。目前看,支持越来越多的任务类型,主要是通过增加LLM预练习数据的多样性来达成的,数据多样性越好,LLM可以或许支持的任务类型就越丰富。所以,应该重视通过增加数据多样性来增加LLM新本领的思绪。
第六,易用的人机操作接口。人类用他们自己风俗的表达方式来描述任务,而LLM要可以或许明白这些Instruct的真实含义。另外,也要注意这些Instruct是符合人类真实需求的,就是说,要从最终用户那边收集任务表述方式,而不能靠研发人员自己的臆想或推测。ChatGPT给我最大的启发实在是这一点,至于是否用增强学习我倒觉得不紧张,别的替换技术应该也能做类似的事故。
ChatGPT:为什么是OpenAI
为什么是OpenAI作出了ChatGPT,而不是别的机构呢?我们在这里可以做个简朴分析。
在本文开头,我们提到了OpenAI看待LLM的理念。OpenAI是怎么看待LLM的呢?回顾它不断推出的技术,可以看出,它实在从GPT 1.0开始,根本就刚强地把LLM看做是通往AGI的一条必由之路。详细而言,在OpenAI眼中,未来的AGI应该长这个样子:有一个任务无关的超大型LLM,用来从海量数据中学习各种知识,这个LLM以天生一切的方式,来办理各种各样的现实问题,而且它应该能听懂人类的命令,以便于人类利用。实在对LLM发展理念的明白,在前半部分,就是“构建一个任务无关的超大型LLM,让它从海量数据中学习各种知识”,这一点几乎是大家的共识,能体现出OpenAI眼光的实在是后半部分。
OpenAI的理念比较超前,对自我定位从一开始就定得比较高,始终刚强不移地探索上述方式是否可以实现AGI。OpenAI之所以能作出ChatGPT,胜在一个是定位比较高,另一个是不受外界干扰,态度上刚强不移。
我们可以回顾下它走的一些关键路程:GPT 1.0走的是天生模式的自回归语言模型门路,比Bert出来的还早些。Bert证明了:双向语言模型对于很多NLP明白类任务,效果比自回归这种单向语言模型效果更好。尽管云云,GPT 2.0并没有因此切换到双向语言模型这条路上,仍旧走文本天生的路,而且开始尝试零示例(zero shot)prompt和少量示例(few shot)prompt。实在这时间, OpenAI心目中的AGI已经开始浮出水面,逐渐表现出轮廓了。只是因为zero shot/few shot效果比Bert+fine-tuning差的比较远,所以大家都没太当回事,乃至不明白它为什么要始终对峙走单向语言模型的门路。这个时间,我估计即使是OpenAI自己,也不一定能确保这条路肯定能走通。
但是,这不妨碍它继续在这条路上往后走。GPT 3.0已经展示出了比较强大的zero shot/few shot prompt本领,这时间OpenAI心目中的AGI已经完全漏出水面,轮廓清晰,而且它的效果也证明了这条路,是有较大大概走得通的。GPT 3.0是一个决定LLM发展方向的叉路口和分水岭,与之对应的另外一条路是“Bert+fine-tuning”模式。在这个岔路口,差别的从业者选择走上了差别的道路,后面的技术差距也是从这里开始拉开的。很遗憾地是,国内很多从业者选择继续在“Bert+fine-tuning”这条路上往后走,这也是造成今天落伍局面的一个关键时间节点。再往后,就是InstructGPT和ChatGPT了,OpenAI通过ChatGPT证明了一点;固然我们间隔真正的AGI,大概还有很长的路要走,但是通过超大LLM走向AGI这条路,目前看是可行的。





#LLM~找不到推理错误

本年,大型语言模型(LLM)成为 AI 领域关注的焦点。LLM 在各种天然语言处理(NLP)任务上取得了显著的希望,在推理方面的突破尤其令人惊艳。但在复杂的推理任务上,LLM 的表现仍旧欠佳。
那么,LLM 能否判定出自己的推理存在错误?最近,剑桥大学和 Google Research 联合开展的一项研究发现:LLM 找不到推理错误,但却能利用该研究提出的回溯(backtracking)方法改正错误。LLM 找不到推理错误,但却能改正错误!


  • 论文地址:https://arxiv.org/pdf/2311.08516.pdf
  • 数据集地址:https://github.com/WHGTyen/BIG-Bench-Mistake
这篇论文引起了一些争论,有人提出异议,比如在 Hacker News 上,有人评论这篇论文的标题言过实在,有些标题党。也有人批评说此中提出的校正逻辑错误的方法基于模式匹配,而非接纳逻辑方法,这种方法实在轻易失败。
Huang 等人在论文《Large language models cannot self-correct reasoning yet》中指出:自我校正大概是能有效地提拔模型输出的风格和质量,但鲜有证据表明 LLM 有本领在没有外部反馈的环境下识别和改正自身的推理和逻辑错误。比如 Reflexion 和 RCI 都利用了根本真值的改正效果作为停止自我校正循环的信号。
剑桥大学和 Google Research 的研究团队提出了一种新思绪:不再把自我校正看作一个单一过程,而是分成错误发现和输出校正两个过程:


  • 错误发现是一种基础推理技能,已经在哲学、心理学和数学领域得到了广泛的研究和应用,并催生了批判性头脑、逻辑和数学谬误等概念。我们可以公道地认为发现错误的本领也应该是 对 LLM 的一项紧张要求。但是,本文效果表明:当前最佳的 LLM 目前还无法可靠地发现错误。
  • 输出校正涉及部分或完全修改之前天生的输出。自我校正是指由天生输出的同一模型来完成校正。尽管 LLM 没有发现错误的本领,但本文表明:如果能提供有关错误的信息(如通过一个小型的监视式奖励模型),LLM 可以利用回溯方法校正输出。
本文的主要贡献包罗:


  • 利用头脑链 prompt 设计方法,任何任务都可以变成错误发现任务。研究者为此收集并发布了一个 CoT 类型的轨迹信息数据集 BIG-Bench Mistake,该数据集由 PaLM 天生,并标注了第一个逻辑错误的位置。研究者表现,BIG-Bench Mistake 在它的同类数据会合,是首个不局限于数学问题的数据集。
  • 为了测试当前最佳 LLM 的推理本领,研究者基于新数据集对它们进行了基准评测。效果发现,当前 SOTA LLM 也难以发现错误,即便是客观的明确的错误。他们推测:LLM 无法发现错误是 LLM 无法自我校正推理错误的主要缘故原由,但这方面还有待进一步研究。
  • 本文提出利用回溯方法来校正输出,利用错误的位置信息来提拔在原始任务上的性能。研究表明这种方法可以校正原本错误的输出,同时对原本正确的输出影响极小。
  • 本文将回溯方法表明成了「言语强化学习」的一种形式,从而可实现对 CoT 输出的迭代式提拔,而无需任何权重更新。研究者提出,可以通过一个经过练习的分类器作为奖励模型来利用回溯,他们也通过实验证明了在差别奖励模型准确度下回溯的有效性。
BIG-Bench Mistake数据集
BIG-Bench 由 2186 个 CoT 风格的轨迹信息集合组成。每个轨迹由 PaLM 2-L-Unicorn 天生,并标注了第一个逻辑错误的位置。表 1 展示了一个轨迹示例,此中错误位于第 4 步。


这些轨迹来自 BIG-Bench 数据会合的 5 个任务:词排序、跟踪经过混洗的对象、逻辑推演、多步算术和 Dyck 语言。
他们利用 CoT prompt 设计法来调用 PaLM 2,使其解答每个任务的问题。为了将 CoT 轨迹分成明确的步骤,他们利用了论文《React: Synergizing reasoning and acting in language  models》中提出的方法,分开天生每一步,并利用了换行符作为停止 token。
在该数据会合,天生全部轨迹时,temperature = 0。答案的正确性由精确匹配决定。
基准测试效果 
表 4 报告了 GPT-4-Turbo、GPT-4 和 GPT-3.5-Turbo 在新的错误发现数据集上的准确度。


对于每个问题,大概的答案有两种环境:要么没有错误,要么就有错误。如有错误,则数值 N 则会指示第一个错误出现的步骤。
全部模型都被输入了同样的 3 个 prompt。他们利用了三种差别的 prompt 设计方法:


  • 直接的轨迹层面的 prompt 设计 
  • 直接的步骤层面的 prompt 设计 
  • CoT 步骤层面的 prompt 设计
相干讨论
研究效果表明,这三个模型都难以应对这个新的错误发现数据集。GPT 的表现最好,但其在直接的步骤层面的 prompt 设计上也只能到达 52.87 的总体准确度。
这分析当前最佳的 LLM 难以发现错误,即使是在最简朴和明确的案例中。相较之下,人类在没有特定专业知识时也能发现错误,并且具有很高的一致性。
研究者推测:LLM 无法发现错误是 LLM 无法自我校正推理错误的主要缘故原由。
prompt 设计方法的比较
研究者发现,从直接轨迹层面的方法到步骤层面的方法再到 CoT 方法,无错误的轨迹准确度显著下降。图 1 展示了这种权衡。


研究者推测其缘故原由是模型天生的输出的数量。这三种方法涉及到天生越来越复杂的输出:直接的轨迹层面的 prompt 设计方法需要单个 token,直接的步骤层面的 prompt 设计方法每步需要一个 token,CoT 步骤层面的 prompt 设计每步需要多个句子。如果每次天生调用都有一定的概率识别堕落误,那么对每条轨迹的调用越多,模型识别出至少一个错误的大概性就越大。
将错误位置作为正确性代理的少样本 prompt 设计
研究者探究了这些 prompt 设计方法能否可靠地决定一个轨迹的正确性,而不是错误位置。
他们盘算了平均 F1 分数,依据为模型能否预测轨迹中是否存在错误。如果存在错误,则假设模型预测的是该轨迹是 incorrect_ans。否则就假设模型预测的是该轨迹是 correct_ans。
利用 correct_ans 和 incorrect_ans 作为正例标签,并根据每个标签的出现次数进行加权,研究者盘算了平均 F1 分数,效果见表 5。


这个加权 F1 分数表明,对于确定最终答案的正确性而言,通过 prompt 探求错误是一个很糟糕的计谋。
回溯
Huang 等人指出 LLM 无法在没有外部反馈的环境下自我校正逻辑错误。但是,在许多真实天下应用中,通常没有可用的外部反馈。
研究者在这项研究中接纳了一种替换方案:用一个在少量数据上练习的轻量级分类器替换外部反馈。与传统强化学习中的奖励模型类似,这个分类器可以检测 CoT 轨迹中的任何逻辑错误,然后再将其反馈给天生器模型以提拔输出。如果想要最大化提拔,可以进行多次迭代。
研究者提出了一种简朴的回溯方法,可以根据逻辑错误的位置来提拔模型的输出:


  • 模型首天赋生一个初始的 CoT 轨迹。在实验中,设置 temperature = 0。
  • 然后利用奖励模型确定轨迹中错误的位置。
  • 如果没有错误,就转向下一个轨迹。如果有错误,则再次向模型输入 prompt 以执行相同的步骤,但这一次 temperature = 1,天生 8 个输出。这里会利用同样的 prompt 以及包含错误步骤之前全部步骤的部分轨迹。
  • 在这 8 个输出中,过滤掉与之前的错误一样的选项。再从剩下的输出中选择对数概率最高的一个。
  • 最后,用新的重新天生的步骤替换之前步骤,再重新设置 temperature = 0,继续天生该轨迹的剩余步骤。
相比于之前的自我校正方法,这种回溯方法有诸多上风:


  • 新的回溯方法不需要对答案有预先的知识。相反,它依赖于有关逻辑错误的信息(比如来自练习奖励模型的信息),这可以利用奖励模型一步步地确定。逻辑错误大概出如今 correct_ans 轨迹中,也大概不出如今 incorrect_ans 轨迹中。 
  • 回溯方法不依赖于任何特定的 prompt 文本或说话,从而可减少相干的偏好。 
  • 相比于需要重新天生整个轨迹的方法,回溯方法可以通过复用已知逻辑正确的步骤来降低盘算成本。
  • 回溯方法可直接提拔中间步骤的质量,这大概对需要正确步骤的场景来说很有效(比如天生数学问题的解),同时还能提拔可表明性。       
研究者基于 BIG-Bench Mistake 数据集实验了回溯方法能否帮助 LLM 校正逻辑错误。效果见表 6。


∆accuracy✓ 是指在原始答案是 correct_ans 时,在轨迹集合上的 accuracy_ans 之差。
∆accuracy✗ 则是对于 incorrect_ans 轨迹的效果。
这些分数效果表明:校正 incorrect_ans 轨迹的收益大于改变原本正确的答案所造成的丧失。此外,尽管随机基准也获得了提拔,但它们的提拔显著小于利用真正错误位置时的提拔。注意,在随机基准中,涉及步骤更少的任务更大概获得性能提拔,因为这样更大概找到真正错误的位置。
为了探索在没有好的标签时,需要哪种准确度品级的奖励模型,他们实验了通过模仿的奖励模型利用回溯;这种模仿的奖励模型的设计目的是产生差别准确度品级的标签。他们利用 accuracy_RM 表现模仿奖励模型在指定错误位置的准确度。
当给定奖励模型的 accuracy_RM 为 X% 时,便在 X% 的时间利用来自 BIG-Bench Mistake 的错误位置。对于剩余的 (100 − X)%,就随机采样一个错误位置。为了模仿典型分类器的举动,会按照与数据集分布相匹配的方式来采样错误位置。研究者也想办法确保了采样的错误位置与正确位置不匹配。效果见图 2。


可以看到 ∆accuracy✓ 的丧失在 65% 时开始趋于稳固。事实上,对于大多数任务,在 accuracy_RM 约莫为 60-70% 时,∆accuracy✓ 就已经大于 ∆accuracy✗ 了。这表明尽管更高的准确度能得到更好的效果,但即便没有黄金尺度的错误位置标签,回溯也依然有效。





#LLM~推理优化

近日,CMU Catalyst 团队推出了一篇关于高效 LLM 推理的综述,覆盖了 300 余篇相干论文,从 MLSys 的研究视角介绍了算法创新和系统优化两个方面的相干希望。
在人工智能(AI)的快速发展配景下,大语言模型(LLMs)依附其在语言相干任务上的良好表现,已成为 AI 领域的紧张推动力。然而,随着这些模型在各种应用中的普及,它们的复杂性和规模也为其摆设和服务带来了前所未有的挑战。LLM 摆设和服务面临着麋集的盘算强度和巨大的内存斲丧,特别是在要求低耽误和高吞吐量的场景中,如何进步 LLM 服务服从,降低其摆设成本,已经成为了当前 AI 和系统领域亟需办理的问题。
来自卡内基梅隆大学的 Catalyst 团队在他们的最新综述论文中,从机器学习系统(MLSys)的研究视角出发,详细分析了从前沿的 LLM 推理算法系统的革命性厘革,以应对这些挑战。该综述旨在提供对高效 LLM 服务的当前状态和未来方向的全面明白,为研究者和实践者提供了宝贵的洞见,帮助他们降服有效 LLM 摆设的障碍,从而重塑 AI 的未来。
论文链接:https://arxiv.org/abs/2312.15234
该论文的第一作者是卡内基梅隆大学的 Xupeng Miao(苗旭鹏)博士后研究员,合作者还包罗 Tianqi Chen 和 Zhihao Jia 助理传授。此外,其他学生作者也均来自于 CMU Catalyst Group 实验室,该实验室由 Zhihao Jia 与 Tianqi Chen(陈天奇)在 CMU 共同主持,致力于集成来自于机器学习算法、系统、硬件等多方面的优化技术,构造主动化的机器学习系统。此前,该实验室还推出了 SpecInfer, MLC-LLM, SpotServe [ASPLOS‘24] 等开源项目,推进 LLM 大模型相干系统的研究和应用。实验室主页:https://catalyst.cs.cmu.edu。
综述概览
该综述系统地审阅了现有 LLM 推理技术,覆盖了 300 余篇相干论文,从算法创新系统优化两个方面展开介绍。论文以此为基础,对现有工作设计了一套清晰且过细的分类法,突出了各种方法的上风和局限性,逐类别搜集整理并介绍了每种方法的相干论文。除此之外,论文还对当前的主流 LLM 推理框架在系统设计与实现方面进行了深入的对比和分析。最后,作者对未来如何继续进步 LLM 推理服从进行了展望,在技术层面提出了六大潜在发展方向
分类法


算法创新
这一节对提出的各种算法和技术进行了全面分析,旨在改进大规模 Transformer 模型推理的原生性能缺陷,包罗解码算法架构设计、和模型压缩等等。


解码算法:在这一部分中,我们回顾了在图 2 中展示的几种 LLMs 推理优化过程的新颖解码算法。这些算法旨在减少盘算复杂度,并进步语言模型推理在天生任务中的总体服从,包罗:


  • 非自回归解码:现有 LLMs 的一个主要限制是默认的自回归解码机制,它逐个顺序天生输出 token。为办理这一问题,一种代表性的工作方向黑白自回归解码 [97, 104, 108,271],即放弃自回归天生范式,突破单词依赖并假设一定水平的条件独立性,并行解码输出 token。然而,尽管这类方法解码速度有所进步,但大多数非自回归方法的输出质量仍不如自回归方法可靠。
  • 谋利式推理:另一类工作是通过谋利执行思想 [47] 实现并行解码。自回归 LLM 推理过程中的每个解码步骤都可以被视为带有条件分支的步伐执行语句,即决定接下来天生哪个 token。谋利式推理 [51, 155] 先利用较小的草稿模型进行多步解码预测,然后让 LLM 同时验证这些预测以实现加速。然而,将谋利解码应用于 LLMs 时仍旧存在一些现实挑战,例如,如何使解码预测足够轻量且准确,以及如何借助 LLMs 实现高效的并行验证。SpecInfer [177] 首次引入基于 tree-based speculative decoding 和 tree attention,并提出了一个低耽误 LLM 服务系统实现,该机制也被后续多个工作 [48, 118, 168, 185, 229, 236, 274, 310] 直接接纳。
  • 提前退出:这类方法主要利用 LLMs 的深层多层结构,在中间层提前推出推理,中间层输出可以通太过类器转化成输出的 token,从而降低推理开销 [117, 147, 163, 167, 234, 272, 282, 291, 308],它们也被称为自顺应盘算 [68, 219]。
  • 级联推理:这类方法级联了多个差别规模的 LLM 模型,用于分别处理差别复杂度的推理请求,代表性工作包罗 CascadeBERT [157] 和 FrugalGPT [53]。
架构设计:


  • 设置缩小:直接缩小模型设置。
  • 注意力简化:最近出现了很多研究工作,它们主要是将之前的长序列高效注意力机制 [240] 应用在 LLM 上,以缩短上下文,减少 KV 缓存,以及注意力复杂度,同时略微降低解码质量(如滑动窗口 [129, 299]、哈希 [198]、dilated [74]、动态选择等等)。表 1 中总结了一些近期的热门方法和之前的工作之间的对应关系。




  • 激活共享:这类方法主要是通过共享 attention 盘算的中间激活来降低推理内存开销,代表性工作包罗 MQA [220] 和 GQA [32]。
  • 条件盘算:这类方法主要是指稀疏专家混合模型(Sparse MoE),比如最近大火的 Mistrial 7Bx8 模型就属于此类。
  • 循环单位:尽管 Transformer 已经替换了 RNN 模型,但考虑到注意力机制的二次复杂性,人们始终未曾放弃将 recurrent unit 机制重新引入 LLM 的尝试,比如 RWKV [200]、RetNet [235],以及状态空间模型 [91, 102, 103, 176] 等等。
模型压缩:


  • 知识蒸馏:这类方法以大型的西席模型为监视,练习一个小型的学生模型。大多数之前的方法都在探索白盒蒸馏 [106, 133, 214, 233, 255],需要访问整个西席模型的参数。由于基于 API 的 LLM 服务(如 ChatGPT)的出现,一些黑盒蒸馏模型吸引了很多关注 [238,59, 273, 201, 313],这些模型通常具有更少的模型参数,与原始 LLMs(如 GPT-4 [195])相比,在各种卑鄙任务上表现出了相称的性能。   
  • 网络剪枝:过去几年中,网络剪枝方法 [180, 215, 215] 已被广泛研究,但并非全部方法都可以直策应用于 LLMs,需要考虑重新练习大概带来的过高盘算成本,以及评估剪枝是否可以在底层系统实现上取得服从提拔。大致上可以分为结构化剪枝 [80, 149, 174, 216, 172] 和半结构化稀疏化 [40, 87, 232, 251, 276] 等。
系统优化
本节研究 LLM 推理系统优化技术,以加速 LLM 推理,而不改变 LLM 盘算语义。这一工作的目的是通过改进用于大型语言模型推理的底层系统和框架来进步系统服从,包罗低比特量化、并行盘算、内存管理、请求调度、和内核优化等等,详细内容可以拜见论文原文。
软件框架


论文还对一些目前最先辈的基于 GPU 的开源 LLM 推理系统进行了深入的分析,并从多个方面总结了它们在设计与实现伤的差别。
未来方向


  • 专用硬件加速器的发展:天生型 LLM 服务服从的显著提拔大概在很大水平上依赖于专用硬件加速器的发展和提拔,尤其是软硬协同设计方法。例如,让内存单位更加接近处理单位,或是针对 LLM 算法数据流优化芯片架构,这些硬件优化可以在很大水平上为 LLM 推理在软件层面带来便利和机会。
  • 高效有效的解码算法:开辟更高效的解码算法可以显著进步服务服从。受对实时应用更快天生速度的需求驱动,一个有出息的方向是广义的谋利式推理(generalized speculative inference),不光会带来显著加速,同时保持相同的天生质量。正如 SpecInfer 中所指出的,广义的谋利式推理中,用于天生草稿 token 的小模型可以被替换为任何快速的 token 天生方法,比如自界说函数、召回方法、乃至早停机制和非自回归解码等等。
  • 长上下文 / 序列场景优化:随着应用场景变得更加复杂,处理更长的上下文或序列的需求不断增长。服务长序列负载的 LLM 需要办理算法和系统两方面的挑战。在算法方面,它们依然面临长度泛化失效问题,乃至大概出现 “loss in the middle” 的环境。目前的解法主要是通过召回增强、序列压缩和缓存来尽大概缩短序列长度并保存相干信息。
  • 探索替换基础架构:尽管 Transformer 模型和自注意力机制目前主导着 LLM 领域,但探索替换架构是未来研究的一个有前景的方向。例如,一些最新研究探索了无注意力方法,利用纯 MLP(多层感知机)架构来替换注意力机制,大概会改变目前 LLM 推理优化的格局。
  • 在复杂环境中的摆设探索:随着 LLM 应用的扩展,探索并优化它们在各种复杂环境中的摆设成为一个关键的未来方向。这一探索不光限于传统的基于云的摆设,还包罗边缘盘算、混合盘算(cloud+edge)、去中心化盘算以及廉价的可抢占资源等。
  • 特定需求的主动顺应:应用特定需求的多样性创造了一系列创新的 LLM 服务优化机会,例如模型微调(parameter-efficient fine-tuning)、向量数据库检索、多模态负载等等。这些独特的挑战也要求将 LLM 服务技术主动且顺遂地集成到现有 IT 基础办法中,将优化空间扩展到整个 LLM 生命周期。
总结
总的来说,该综述不光是对当前 LLM 服务优化研究的全面概述,也为未来在这一领域的探索和发展指明了方向。通过深入了解这些先辈的办理方案,研究者和实践者可以更好地明白和应对在现实应用中摆设大型语言模型时面临的挑战。




#LLM-Shearing

手把手教你剪「羊驼」,陈丹琦团队提出LLM-Shearing大模型剪枝法,给 Llama 2(羊驼)大模型剪一剪驼毛,会有怎样的效果呢?今天普林斯顿大学陈丹琦团队提出了一种名为 LLM-Shearing 的大模型剪枝法,可以用很小的盘算量和成本实现优于划一规模模型的性能。
自大型语言模型(LLM)出现以来,它们便在各种天然语言任务上取得了显著的效果。不外,大型语言模型需要海量的盘算资源来练习。因此,业界对构建同样强大的中型规模模型越来越感兴趣,出现了 LLaMA、MPT 和 Falcon,实现了高效的推理和微调。
这些规模不等的 LLM 适用于差别的用例,但从头开始练习每个单独的模型(即使是 10 亿参数小模型)照旧需要大量盘算资源,这对于大多数科研机构而言照旧很大的负担。
因此在本文中,普林斯顿大学陈丹琦团队试图办理以下问题:能否利用现有预练习 LLM 来构建一个规模更小、通用且在性能上有竞争力的 LLM,同时比从头开始练习需要的盘算量少得多?
研究者探索利用结构化剪枝来实现目的。这里的问题是,对于通用 LLM,剪枝后的模型会出现性能下降,尤其是在剪枝后没有大量盘算投入的环境。他们利用的高效剪枝方法可以用来开辟规模更小但仍具有性能竞争力的 LLM,并且与从头开始练习相比,练习需要的盘算量也大大减少。


  • 论文地址: https://arxiv.org/abs/2310.06694
  • 代码地址: https://github.com/princeton-nlp/LLM-Shearing
  • ModelsSheared-LLaMA-1.3B, Sheared-LLaMA-2.7B
在对 LLM 进行剪枝之前,研究者确定了两个关键技术挑战,一是如何确定最终的性能强大、推理高效的剪枝结构?LLM 目前的结构化剪枝技术没有指定的目的结构,导致剪枝后模型在性能和推理速度方面不抱负;二是如何继续预练习剪枝后的模型以到达预期性能?他们观察到,与从头开始练习模型相比,利用原始预练习数据来练习会导致差别域出现差别的丧失减少。
针对这两个挑战,研究者提出了「LLM - shearing」算法。这种新颖的剪枝算法被称为「定向结构化剪枝」,它将源模型剪枝为指定的目的架构,该结构通过现有预练习模型的设置来确定。他们表现,该剪枝方法在源模型中搜刮子结构,并在资源受限的环境下最大水平地保持性能。此外设计一种动态批量加载算法,它能根据丧失减少率按比例加载每个域的练习数据,从而高效利用数据并加速团体性能的提拔。
最终,研究者将 LLaMA2-7B 模型剪枝成了两个较小的 LLM,分别是 Sheared-LLaMA-1.3B 和 Sheared-LLaMA-2.7B,证实了其方法的有效性。


他们仅仅利用 500 亿个 token(即 OpenLLaMA 预练习预算的 5%)进行剪枝和继续预练习,但对于 11 个代表性卑鄙任务(如常识、阅读明白和天下知识)以及开放式天生的指令调解,这两个模型的性能仍旧优于其他划一规模的流行 LLM,包罗 Pythia、INCITE 和 OpenLLaMA。


不外要提到一点,在这篇论文发布 Sheared-LLaMA-3B 的时间,最强 3B 开源模型的纪录已经被 StableLM-3B 突破了。
此外,卑鄙任务性能轨迹表明,利用更多 token 来进一步练习剪枝后的模型,将带来更大的收益。研究者只对最多 70 亿参数的模型进行了实验,但 LLM-shearing 具有高度通用性,可以在未来的工作中扩展到任何规模的大型语言模型。
方法介绍
给定一个现有的大模型 M_S(源模型),本文目的是研究如何有效地天生一个更小、更强的模型 M_T(目的模型)。该研究认为这需要两个阶段来完成:


  • 第一阶段将 M_S 剪枝为 M_T,固然这样减少了参数数量,但不可克制地导致性能下降;
  • 第二阶段持续预练习 M_T,使其性能更强。
结构化剪枝
结构化剪枝可以去除模型大量参数,从而到达压缩模型并加速推理的效果。然而,现有的结构化剪枝方法会导致模型偏离常规架构的设置。例如 CoFiPruning 方法产生的模型具有不同一的层设置,与尺度的同一层设置相比,这样会产生额外的推理开销。
本文对 CoFiPruning 进行了扩展,以答应将源模型剪枝为指定的任何目的设置。例如,本文在天生 2.7B 模型时利用 INCITE-Base-3B 架构作为目的结构。
此外,本文还在差别粒度的模型参数上学习一组剪枝掩码( pruning mask),掩码变量如下所示:


每个掩码变量控制是否剪枝或保留相干的子结构。例如,如果对应的 z^layer= 0,则需要删除这个层。下图 2 分析了剪枝掩码如何控制被剪枝的结构。


剪枝之后,本文通过保留与每个子结构中的掩码变量相干的最高得分组件来最终确定剪枝后的架构,并继续利用语言建模目的对剪枝后的模型进行预练习。
动态批量加载
该研究认为对剪枝后的模型进行大量预练习是很有必要的,这样才能恢复模型性能。
受其他研究的启发,本文提出了一种更有效的算法,即动态批量加载,其可以根据模型性能简朴地动态调解域比例。算法如下:


实验及效果
模型设置:本文将 LLaMA2-7B 模型作为源模型,然后进行结构化剪枝实验,他们将 LLaMA2-7B 压缩成两个较小的目的尺寸 2.7 B 和 1.3B 参数,并将剪之后的模型与相同尺寸的模型进行了性能比较,包罗 OPT-1.3B、Pythia-1.4B、OPT-2.7B、 Pythia-2.8B、INCITE-Base-3B、OpenLLaMA-3B-v1、OpenLLaMA-3B-v2。表 8 总结了全部这些模型的模型体系结构细节。


数据:由于 LLaMA2 的练习数据并不是公开访问的,因此本文利用了 RedPajama 数据集 。表 1 提供了本文模型和基线模型利用的预练习数据。


练习:研究者在全部实验中最多利用了 16 个 Nvidia A100 GPU (80GB)。
SHEARED-LLAMA 优于划一巨细的 LM
本文表明,Sheared-LLaMA 显着优于现有的类似规模的 LLM,同时只利用一小部分盘算预算来从头开始练习这些模型。
卑鄙任务:表 2 展示了 Sheared-LLaMA 和类似巨细的现有预练习模型的零样本和少样本在卑鄙任务上的性能。


指令调优:如图 3 所示,与划一规模的全部其他预练习模型相比,指令调优的 Sheared-LLaMA 实现了更高的得胜率。


图 4 表现了 INCITEBase-3B 模型开始时的精度要高得多,但其性能在持续的预练习过程中趋于稳固。


分析
最后,研究者对本文方法的上风进行了分析。
动态批量加载的有效性
此中,研究者从以下三个方面的影响来分析动态批量加载的有效性:(1) 跨域的最终 LM 丧失,(2) 整个练习过程中每个域的数据利用环境,(3) 卑鄙任务性能。效果均基于 Sheared-LaMA-1.3B 算法。
跨域丧失差别。动态批量加载的目的是均衡各域的丧失降低率,使丧失在大致相同的时间内到达参考值。图 5 中绘制了模型消耗(原始批量加载和动态批量加载)与参考消耗之间的差别,相比之下,动态批量加载能均匀地减少丧失,各域的丧失差别也非常相似,这表明数据利用服从更高。


数据利用环境。表 3 对比了 RedPajama 的原始数据比例和动态加载的域数据利用环境(图 7 展示了整个练习过程中域权重的变化)。与其他域相比,动态批量加载增加了 Book 和 C4 域的权重,这表明这些域更难恢复剪枝模型。 


卑鄙性能。如图 6 所示,与在原始 RedPajama 分布上练习的模型相比,利用动态批量加载练习的剪枝模型获得了更好的卑鄙性能。这表明,动态批量加载所带来的更均衡的丧失减少可以进步卑鄙性能。


与其他剪枝方法的对比
此外,研究者将 LLM-shearing 方法与其他剪枝方法进行了比较,并报告了验证狐疑度,它是衡量团体模型本领的一个有力指标。
由于盘算上的限制,下面的实验控制了全部比较方法的总盘算预算,而不是将每种方法运行到最后。
如表 4 所示,在相同稀疏度下,本文的目的剪枝模型的推理吞吐量比非均匀剪枝 CoFiPruning 模型更高,但狐疑度略高。


其他分析
表 5 表现,在控制 token 总量的环境下,增加剪枝开销可以持续改善狐疑度。然而,由于剪枝比持续的预练习更昂贵,研究者将 0.4B 的 token 分配给剪枝。






#LLMの推理过程

以LLAMA为例
本文借助llama这个模型快速入门LLM的推理过程,技术细节很多都是通用的,也适合其他的LLM。本篇也可以作为CV快速入门NLP浅易指南。
这篇也算是总结的性子,仅需要的知识点都列出来,简朴介绍一番。大概说的不敷详细,想看详细细节的小伙伴可参考文中引用的相干详细文章。
本篇也可以作为CV快速入门NLP浅易指南(固然也是老潘的个人笔记)。固然只是推理哈,不涉及到练习,仅是从摆设的角度考虑这个模型,个人也不是NLP专家,如对文中内容有疑问,欢迎讨论。
什么是LLM
llama是LLM(LLM指大语言模型)模型的一种结构,llama这种模型的任务就是在阅读前n个单词后预测句子中下一个单词,输出取决于过去和如今输入,与未来无关。
过程大概是这样,每次输入模型会带上上一次输出的效果(差别于CV模型,CV模型输入只需要一次即可,一次推理即可得到效果):


LLM-天生
一般来说,LLM模型主要由两个块组成:


  • 编码器(左侧):编码器接收输入并构建其表现形式(特性)。这意味着该模型被优化为从输入中获取明白(比如输入文本判定这段话是高兴照旧惆怅)。
  • 解码器(右侧):解码器利用编码器的表现形式(特性)以及其他输入来天生目的序列。这意味着该模型被优化用于天生输出。


编码器息争码器
这些部分都可以根据任务独立利用:


  • Encoder-only models:适用于需要明白输入的任务,例如句子分类和命名实体识别。
  • Decoder-only models:适用于天生性任务,如文本天生
  • Encoder-decoder models or sequence-to-sequence models:适用于需要输入的天生性任务,例如翻译或摘要。whao~开辟板商城~aiot物联网装备
LLAMA属于Decoder-only models,只有decoder层。
llama相干的知识点
llama的decoder部分的结构取自transformer,关于transformer的介绍知乎上太多,这里不赘述了,想详细明白transformer的发起看知乎上的其他优秀文章以及b站沐神的解说
transformer的结构如下:


transformer
对于llama来说,只用了decoder部分,重点关注这几个概念:


  • Tokenization 分词器
  • Embedding 嵌入层
  • Positional Encoding 位置编码
  • Self-attention 自注意力机制
  • Multi-head attention 多头注意力与接纳掩码机制的多头注意力
  • Batch Norm & Layer Norm 批尺度化/层尺度化   llama用的是RMSNorm
  • ResNet 残差网络
详细的可以查阅相干的博文,这里同样不进行赘述,只会简朴描述下过程。拿到llama模型后,主要关注:


  • 模型的结构,包含哪些算子哪些op,模型复杂度
  • 模型的前后处理,前后处理实现细节,模型的执行方式
  • 模型各种参数设置以及其他一些细节
llama的整个执行过程分了好多步,涉及到很多前后处理,前后处理又涉及到很多细节的步骤,之后也会介绍。
先过一下知识点。
分词器、token、embeding
主要是分词、编码、Tokenizer(tokenization)、embed(embedding)的过程。


分词过程
什么是分词?也就是Tokenizer的作用。
分词器可将原始文本转换为由token组成的文本的初始数值表征。
分词器之所以是模型的紧张构成部分之一,是因为模型可借此妥善应对人类语言的复杂性。例如,分词器可将凝集性语言中的词分解为更易管理的组成部分、处理原始语料库中不存在的新词或外来词/特别字符,并确保模型天生紧凑(尽大概精简)的文本表征。
每种语言都有可选用的数量众多且各不相同的分词器。大部分基于 Transformer 的架构均利用经过练习的分词器,这些分词器旨在充分缩短典型序列的长度。WordPiece(应用于 BERT)、SentencePiece(应用于 T5 或 RoBerta)等分词器同样具有多个变体,这是因为众多的语言和差别的专业领域(如医疗健康语料库)接纳了这些分词器,且这些分词器在练习方式上也有所差别(选用差别的最大标记数,或以差别的方式处理巨细写)。
然后看回代码。
首先看tokenizer,运行llama的时间我们会调用​​tokenizer = AutoTokenizer.from_pretrained(args.model, use_fast=False)​​。
如果我们模型传入的是llama的某个模型(llama-7b)那么返回的就是LlamaTokenizer:
  1. class LlamaTokenizer(PreTrainedTokenizer):  
  2.     """  
  3.     Construct a Llama tokenizer. Based on byte-level Byte-Pair-Encoding.  
  4. ...
复制代码
这个类是LLAMA模型的分词器(tokenizer)的实现,基于字节级的字节对编码(Byte-Pair Encoding)。这个分词器的主要功能是将文本字符串转换为模型可以明白的数字序列,反之亦然。这里假设输入为 '"this is a python code:"'  。
详细我们看干了些啥,创建好tokenizer之后我们执行:​​input_ids = tokenizer.encode(args.text, return_tensors="pt").to(dev)​​,这里又分两步:


  • 第一步是 Converts a string in a sequence of tokens (string), using the tokenizer. Split in words for word-based vocabulary or sub-words for sub-word-based vocabularies (BPE/SentencePieces/WordPieces). 这里调用 ​​self.sp_model.encode(text, out_type=str)​​,sp_model就是sentencepiece中的一个函数,执行完出来变为  ['▁"', 'this', '▁is', '▁a', '▁python', '▁code', ':"']
  • 第二步将token string转变为 token id  -> Converts a token string (or a sequence of tokens) in a single integer id (or a sequence of ids), using the vocabulary. 详细就是个for循环,对之前分好的tokens一个一个转。
最后我们得到:
  1. input_ids  
  2. tensor([[   0,  376, 1366,  338,  263, 3017,  775, 6160]], device='cuda:0')  
  3. input_ids.shape  
  4. torch.Size([1, 8])
复制代码
至于如何转换为embedding,之后会调用:
​​inputs_embeds = self.embed_tokens(input_ids)​​​,此中embeds的shape是​​torch.Size([1, 8, 4096])​​。
在天然语言处理(NLP)中,嵌入(Embedding)是一种将离散变量(如单词、短语、或者文档)转换为连续向量的方法。这种转换的目的是让盘算机能更好地明白和处理天然语言数据。embedding矩阵的本质是一个查找表 ,每个单词会定位这个表中的某一行,而这一行就是这个单词学习到的在嵌入空间的语义。
自注意力 Self-Attention
Transformer模型的一个关键特点是利用了称为注意力层的特别层。“Attention Is All You Need”。
这一层会告诉模型,在处理每个单词的表现时,要对你传递给它的句子中某些单词特别关注(并且忽略其他单词)。
把这个概念放到配景中,考虑从英语翻译成法语的任务。给定输入“你喜好这门课程”,翻译模型还需要关注相邻单词“你”,以获得正确翻译动词“like”的形式,因为在法语中,“like”根据主语差别而有差别变化形式。然而,句子其余部分对于该单次翻转来说没有效处。同样地,在翻转“This”时,模型也需要注意到单次“course”,因为“This”根据相干名字是否男性或女性而有差别意思表达方式。再次强调,在更复杂的句子(和更复杂的语法规则)中,模型需要特别关注大概出如今句子较远位置上以正确翻译每个单次的单词。
同样的概念适用于与天然语言相干的任何任务:一个单次自己具有意义,但是该意义深受上下文影响,这可以是正在研究的单次之前或之后的任何其他单次(或多个)。


Self-attention是Transformer的核心,其答应模型考虑到序列中的其他标记,以便更好地明白每个标记的上下文。每个标记的新表现形式是由它自己和其他标记的交互得到的。
位置编码
由于Transformer的结构没有考虑到标记的顺序,所以我们需要加入位置编码来给模型提供词元在序列中的位置信息。这些编码会被添加到词嵌入向量中。
多头注意力(Multi-head Attention)
多头注意力是对自注意力机制的扩展。它将自注意力分解为多个“头”,每个头在差别的表现空间中学习和应用自注意力。这答应模型同时捕获到各种差别类型的信息。在有掩码的多头注意力中,掩码被用于阻止模型查看某些不应该看到的信息,例如在天生新的标记时阻止查看未来的信息。如今根本都利用MHA,一般不用单头。
批尺度化(Batch Norm) & 层尺度化(Layer Norm)
这些都是用于正规化激活的技术,可以加速学习,进步模型的性能。
批尺度化是在整个批次的数据上进行尺度化,而层尺度化则是在单个数据样本上进行尺度化。RMSNorm是一种新的归一化方法,是对LayerNorm的一个改进,没有做re-center操作(移除了此中的均值项),可以看作LayerNorm在均值为0时的一个特例。
残差网络(ResNet)
老熟人了。通过在网络中添加跳跃连接(或称为“skip”连接),可以使得模型更轻易地学习到恒等映射,从而克制了练习深度网络时常见的梯度消失问题。在Transformer中,每个子层(如自注意力层和前馈神经网络层)都有一个对应的残差连接,并且每个子层的输出都会进行层尺度化。
LLAMA的模型结构
我们可以很轻易的通过huggingface代码库中看到llama的模型结构。
以hugging库中的7B模型为例,运行​​model = LlamaForCausalLM.from_pretrained(model, torch_dtype='auto')​​后,可以通过print看模型结构:
  1. LlamaForCausalLM(  
  2.   (model): LlamaModel(  
  3.     (embed_tokens): Embedding(32000, 4096, padding_idx=31999)  
  4.     (layers): ModuleList(  
  5.       (0-31): 32 x LlamaDecoderLayer(  
  6.         (self_attn): LlamaAttention(  
  7.           (q_proj): Linear(in_features=4096, out_features=4096, bias=False)  
  8.           (k_proj): Linear(in_features=4096, out_features=4096, bias=False)  
  9.           (v_proj): Linear(in_features=4096, out_features=4096, bias=False)  
  10.           (o_proj): Linear(in_features=4096, out_features=4096, bias=False)  
  11.           (rotary_emb): LlamaRotaryEmbedding()  
  12.         )  
  13.         (mlp): LlamaMLP(  
  14.           (gate_proj): Linear(in_features=4096, out_features=11008, bias=False)  
  15.           (down_proj): Linear(in_features=11008, out_features=4096, bias=False)  
  16.           (up_proj): Linear(in_features=4096, out_features=11008, bias=False)  
  17.           (act_fn): SiLUActivation()  
  18.         )  
  19.         (input_layernorm): LlamaRMSNorm()  
  20.         (post_attention_layernorm): LlamaRMSNorm()  
  21.       )  
  22.     )  
  23.     (norm): LlamaRMSNorm()  
  24.   )  
  25.   (lm_head): Linear(in_features=4096, out_features=32000, bias=False)  
  26. )
复制代码
7B有32个​​LlamaDecoderLayer​​,每个Decoder包含一个LlamaAttention和LlamaMLP,然后是LlamaRMSNorm和head部分,核心的结构是transformer。
先看核心的​​LlamaDecoderLayer​​,7B有32个,而30B的话有60个,30B和7B的差别也就是decoder的个数和decoder的差别设置。


7B-config和30B-config区别
看下7B模型的config,可以看到模型类型为float16,use_cache设置为true
  1. {  
  2.     "architectures": [  
  3.         "LLaMAForCausalLM"  
  4.     ],  
  5.     "bos_token_id": 0,  
  6.     "eos_token_id": 1,  
  7.     "hidden_act": "silu",  
  8.     "hidden_size": 4096,  
  9.     "intermediate_size": 11008,  
  10.     "initializer_range": 0.02,  
  11.     "max_sequence_length": 2048,  
  12.     "model_type": "llama",  
  13.     "num_attention_heads": 32,  
  14.     "num_hidden_layers": 32,  
  15.     "pad_token_id": 0,  
  16.     "rms_norm_eps": 1e-06,  
  17.     "torch_dtype": "float16",  
  18.     "transformers_version": "4.27.0.dev0",  
  19.     "use_cache": true,  
  20.     "vocab_size": 32000  
  21. }
复制代码
运行pipeline
llama的运行流程和大多数的LLM一样,流程如下:     


  • 分词encode,我明白相称于预处理
  • 输入input_ids后模型开始运行,这里会for循环运行好多次
  • 运行完后得到logits进行后处理预测下一个token
  • 循环往复直到全部要求数量的token都输出或者输出碰到了end_id


LLM团体流程图,来自huggingface
再回顾下LLAMA的模型组成:
  1. class LlamaModel(LlamaPreTrainedModel):  
  2.     def __init__(self, config: LlamaConfig):  
  3.         super().__init__(config)  
  4.         self.padding_idx = config.pad_token_id  
  5.         self.vocab_size = config.vocab_size  
  6.         self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)  
  7.         self.layers = nn.ModuleList([LlamaDecoderLayer(config) for _ in range(config.num_hidden_layers)])  
  8.         self.norm = LlamaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)  
  9.         self.gradient_checkpointing = False  
  10.         # Initialize weights and apply final processing  
  11.         self.post_init()  
  12.     ...
复制代码
主要部分就三个:


  • embed_tokens   嵌入层,它将输入的词(或者词的索引)映射到一个高维空间中,使得具有相似语义的词在这个空间中的间隔更近。这个嵌入层在练习过程中会被优化,以更好地捕获单词之间的语义关系。这里利用的嵌入层是一个 PyTorch 的 nn.Embedding 层,它将巨细为 config.vocab_size 的词汇表中的每个词映射到一个 config.hidden_size 维的向量。self.padding_idx 是用于指定在嵌入矩阵中,哪个索引是用于填充的,通常这个填充索引对应的嵌入向量会被初始化为零,并在练习过程中保持为零
  • layers   num_hidden_layers个解码器
  • norm   RMSNorm 归一化函数,和layernorm类似
第一步 分词
调用​​tokenizer.encode(args.text, return_tensors="pt").to(DEV)​​,详细流程如下:
输入prompt -> '"this is a python code:"'
->  ['▁"', 'this', '▁is', '▁a', '▁python', '▁code', ':"']
->  [376, 1366, 338, 263, 3017, 775, 6160]
-> {'input_ids': tensor([[   0,  376, 1366,  338,  263, 3017,  775, 6160]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1]])}
-> 然后开始generation,开始第二步
第二步 设置
主要就是设置一些信息和做预处理。
设置min_length、max_length、top_p、temperature进入model.generate
-> 处理、设置、验证generation_config 设置 generation parameters
-> 设置模型的输入
  1. inputs_tensor, model_input_name, model_kwargs = self._prepare_model_inputs(   
  2.             inputs, generation_config.bos_token_id, model_kwargs   
  3.         )
复制代码
bos_token_id 是 0, inputs就是刚才传过来的input_ids。
-> 设置模型的参数
  1. model_kwargs["attention_mask"] = self._prepare_attention_mask_for_generation(   
  2.                 inputs_tensor, generation_config.pad_token_id, generation_config.eos_token_id   
  3.             )
复制代码
此中 pad_token_id为 -1,eos_token_id为1,一般从config中获取。
-> 设置 max_length -> input_ids_seq_length = input_ids.shape[-1]
-> 决定generation mode
-> prepare distribution pre_processing samplers
-> prepare stopping criteria
-> 设置好 sample_gen_mode、 prepare logits warper
-> expand input_ids with num_return_sequences additional sequences per batch


设置好的模型参数
第三步 sample
继续预备模型的输入,调用​​self.prepare_inputs_for_generation​​(如果提供了past_key_values,那么input_ids = input_ids[:, -1:],同时根据attention_mask和是否提供past_key_values盘算出position_ids,也就是说提供了past_key_values的话,input_ids可以少盘算很多)返回model_inputs。


model_inputs
-> 开始进入auto-regressive generation的循环,是个while True
进入LlamaForCausalLM函数,输入刚才的​​model_inputs​​,主要包含input_ids、attention_mask、position_ids、past_key_values
-> 根据是否存在 past_key_values 更新 past_key_values_length 和 seq_length_with_past
-> 判定inputs_embeds是否存在判定是否需要调用​​self.embed_tokens​​,也就是说如果自己提供了embeds就不需要在这里单独embed了
这里调用 inputs_embeds = self.embed_tokens(input_ids),embeds的shape是​​torch.Size([1, 8, 4096])​​,8代表输入input_ids的长度,我这里是8
-> 调用_prepare_decoder_attention_mask函数,调用后的attention_mask维度为​​torch.Size([1, 1, 8, 8])​​:
  1. attention_mask = self._prepare_decoder_attention_mask(  
  2.             attention_mask, (batch_size, seq_length), inputs_embeds, past_key_values_length  
  3.         )
复制代码
进入一个for循环,  因为llama有很多​​self.layers = nn.ModuleList([LlamaDecoderLayer(config) for _ in range(config.num_hidden_layers)])​​​,都是一模一样的​​LlamaDecoderLayer​​:
  1. for idx, decoder_layer in enumerate(self.layers):  
  2.     if output_hidden_states:  
  3.         all_hidden_states += (hidden_states,)  
  4.     # 如果存在past_key_value,则一并传进去  
  5.     past_key_value = past_key_values[idx] if past_key_values is not None else None  
  6.     layer_outputs = decoder_layer(  
  7.         hidden_states,  
  8.         attention_mask=attention_mask,  
  9.         position_ids=position_ids,  
  10.         past_key_value=past_key_value,  
  11.         output_attentions=output_attentions,  
  12.         use_cache=use_cache  
  13.     )  
  14.   
  15.     hidden_states = layer_outputs[0]  
  16.     if use_cache:  
  17.         next_decoder_cache += (layer_outputs[2 if output_attentions else 1],)  
  18.     if output_attentions:  
  19.         all_self_attns += (layer_outputs[1],)
复制代码
这里插播个结构分析,我们重点看LlamaDecoderLayer函数。
LlamaDecoderLayer
函数的传入的参数:​​hidden_states [1,8,4096]、attention_mask [1,1,8,8]、position_ids [1,8]、past_key_value [[]] or None​​。
执行的步伐也很简朴,顺序执行以下步骤:


  • 首先归一化 self.input_layernorm(hidden_states)
  • 然后经过 self_attn
  • 残差 hidden_states = residual(之前residual = hidden_states) + hidden_states(经过self_attn后)
  • 全连接 self.post_attention_layernorm(hidden_states) -> self.mlp(hidden_states)
  • 继续残差 hidden_states = residual(之前经过self_attn后) + hidden_states(经过全连接后)
代码如下:
  1. residual = hidden_states  
  2. hidden_states = self.input_layernorm(hidden_states)  
  3. # Self Attention  
  4. hidden_states, self_attn_weights, present_key_value = self.self_attn(  
  5.     hidden_states=hidden_states,  
  6.     attention_mask=attention_mask,  
  7.     position_ids=position_ids,  
  8.     past_key_value=past_key_value,  
  9.     output_attentions=output_attentions,  
  10.     use_cache=use_cache,  
  11. )  
  12. hidden_states = residual + hidden_states  
  13. # Fully Connected  
  14. residual = hidden_states  
  15. hidden_states = self.post_attention_layernorm(hidden_states)  
  16. hidden_states = self.mlp(hidden_states)  
  17. hidden_states = residual + hidden_states  
  18. outputs = (hidden_states,)  
  19. if output_attentions:  
  20.     outputs += (self_attn_weights,)  
  21. if use_cache:  
  22.     outputs += (present_key_value,)  
  23. return outputs
复制代码
然后我们看下核心的LlamaAttention:
LlamaAttention
这个就是Multi-headed attention from 'Attention Is All You Need' paper。这个类的成员变量如下:
  1. class LlamaAttention(nn.Module):  
  2.     def __init__(self, config: LlamaConfig):  
  3.         super().__init__()  
  4.         self.config = config  
  5.         self.hidden_size = config.hidden_size  
  6.         self.num_heads = config.num_attention_heads # head的数量 这里是32  
  7.         self.head_dim = self.hidden_size // self.num_heads  # head的大小 这里是128  
  8.         self.max_position_embeddings = config.max_position_embeddings  
  9.         if (self.head_dim * self.num_heads) != self.hidden_size:  
  10.             raise ValueError(  
  11.                 f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"  
  12.                 f" and `num_heads`: {self.num_heads})."  
  13.             )  
  14.         # 线性层self.q_proj, self.k_proj, self.v_proj将输入hidden_states映射到num_heads * head_dim的维度,以分别获得查询、键、值tensor。  
  15.         self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)  
  16.         self.k_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)  
  17.         self.v_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)  
  18.         self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)  
  19.         self.rotary_emb = LlamaRotaryEmbedding(self.head_dim, max_position_embeddings=self.max_position_embeddings)  
  20.     ...
复制代码
此中,多头机制的自注意力:


  • self.num_heads界说了attention head的数量
  • self.head_dim界说了每个head的巨细,是hidden_size除以num_heads
  • 线性层self.q_proj, self.k_proj, self.v_proj将输入hidden_states映射到num_heads * head_dim的维度,以分别获得查询、键、值tensor
操作代码如下:
  1. def forward(  
  2.     self,  
  3.     hidden_states: torch.Tensor,  
  4.     attention_mask: Optional[torch.Tensor] = None,  
  5.     position_ids: Optional[torch.LongTensor] = None,  
  6.     past_key_value: Optional[Tuple[torch.Tensor]] = None,  
  7.     output_attentions: bool = False,  
  8.     use_cache: bool = False,  
  9. ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:  
  10.     bsz, q_len, _ = hidden_states.size()  
  11.   
  12.     query_states = self.q_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)  
  13.     key_states = self.k_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)  
  14.     value_states = self.v_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)  
  15.   
  16.     kv_seq_len = key_states.shape[-2]  
  17.     if past_key_value is not None:  
  18.         kv_seq_len += past_key_value[0].shape[-2]  
  19.     cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len)  
  20.     query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin, position_ids)  
  21.     # [bsz, nh, t, hd]  
  22.     if past_key_value is not None:  
  23.         # reuse k, v, self_attention  
  24.         key_states = torch.cat([past_key_value[0], key_states], dim=2)  
  25.         value_states = torch.cat([past_key_value[1], value_states], dim=2)  
  26.     past_key_value = (key_states, value_states) if use_cache else None  
  27.     attn_weights = torch.matmul(query_states, key_states.transpose(2, 3)) / math.sqrt(self.head_dim)  
  28.     if attn_weights.size() != (bsz, self.num_heads, q_len, kv_seq_len):  
  29.         raise ValueError(  
  30.             f"Attention weights should be of size {(bsz * self.num_heads, q_len, kv_seq_len)}, but is"  
  31.             f" {attn_weights.size()}"  
  32.         )  
  33.     if attention_mask is not None:  
  34.         if attention_mask.size() != (bsz, 1, q_len, kv_seq_len):  
  35.             raise ValueError(  
  36.                 f"Attention mask should be of size {(bsz, 1, q_len, kv_seq_len)}, but is {attention_mask.size()}"  
  37.             )  
  38.         attn_weights = attn_weights + attention_mask  
  39.         attn_weights = torch.max(attn_weights, torch.tensor(torch.finfo(attn_weights.dtype).min))  
  40.     # upcast attention to fp32  
  41.     attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query_states.dtype)  
  42.     attn_output = torch.matmul(attn_weights, value_states)  
  43.   
  44.     if attn_output.size() != (bsz, self.num_heads, q_len, self.head_dim):  
  45.         raise ValueError(  
  46.             f"`attn_output` should be of size {(bsz, self.num_heads, q_len, self.head_dim)}, but is"  
  47.             f" {attn_output.size()}"  
  48.         )  
  49.     attn_output = attn_output.transpose(1, 2)  
  50.     attn_output = attn_output.reshape(bsz, q_len, self.hidden_size)  
  51.     attn_output = self.o_proj(attn_output)  
  52.     if not output_attentions:  
  53.         attn_weights = None  
  54.     return attn_output, attn_weights, past_key_value
复制代码
此中两点需要注意:


  • ​​query_states = self.q_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)​​​就是多头盘算,得到的效果query_states、key_states、value_states的维度是​​torch.Size([1, 32, 8, 128])​​,32代表头的数量、8是输入的input_ids长度,128代表头的巨细,剩下的盘算也是多头自注意力的部分,不多介绍了
  • 如果提供了past_key_value,则利用cache的机制,直接​​torch.cat([past_key_value[0], key_states], dim=2)​​即可,每次传入的input_ids只有最新的一个id
好了回到刚才的pipeline:
接着for循环完32个decoder层之后,需要进行norm操作:​​hidden_states = self.norm(hidden_states)​​。
输出后得到​​outputs.logits​​​维度为​​torch.Size([1, 8, 32000])​​,接下来对这个logits进行操作,以下是相干代码,比较长:
  1. next_token_logits = outputs.logits[:, -1, :]  
  2. # pre-process distribution  
  3. next_token_scores = logits_processor(input_ids, next_token_logits)  
  4. next_token_scores = logits_warper(input_ids, next_token_scores)  
  5. ...  
  6. probs = nn.functional.softmax(next_token_scores, dim=-1)  
  7. next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)  
  8.   
  9. # logits_processor中执行的操作  
  10.   
  11. class MinLengthLogitsProcessor(LogitsProcessor):  
  12.     [`LogitsProcessor`] enforcing a min-length by setting EOS probability to 0.  
  13.     def __init__(self, min_length: int, eos_token_id: Union[int, List[int]]):  
  14.         if not isinstance(min_length, int) or min_length < 0:  
  15.             raise ValueError(f"`min_length` has to be a non-negative integer, but is {min_length}")  
  16.   
  17.         if isinstance(eos_token_id, int):  
  18.             eos_token_id = [eos_token_id]  
  19.         if not all([isinstance(i, int) for i in eos_token_id]) or any([i < 0 for i in eos_token_id]):  
  20.             logger.warning(f"`eos_token_id` has to be a list of positive integers, but is {eos_token_id}")  
  21.   
  22.         self.min_length = min_length  
  23.         self.eos_token_id = eos_token_id  
  24.   
  25.     def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:  
  26.         cur_len = input_ids.shape[-1]  
  27.         if cur_len < self.min_length:  
  28.             for i in self.eos_token_id:  
  29.                 scores[:, i] = -float("inf")  
  30.         return scores  
  31. ...  
  32. # logits_warper调用的三个函数  
  33. class TemperatureLogitsWarper(LogitsWarper):  
  34.     def __init__(self, temperature: float):  
  35.         if not isinstance(temperature, float) or not (temperature > 0):  
  36.             raise ValueError(f"`temperature` has to be a strictly positive float, but is {temperature}")  
  37.         self.temperature = temperature  
  38.     def __call__(self, input_ids: torch.Tensor, scores: torch.Tensor) -> torch.FloatTensor:  
  39.         scores = scores / self.temperature  
  40.         return scores  
  41.   
  42. class TopKLogitsWarper(LogitsWarper):  
  43.     def __init__(self, top_k: int, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1):  
  44.         if not isinstance(top_k, int) or top_k <= 0:  
  45.             raise ValueError(f"`top_k` has to be a strictly positive integer, but is {top_k}")  
  46.   
  47.         self.top_k = max(top_k, min_tokens_to_keep)  
  48.         self.filter_value = filter_value  
  49.   
  50.     def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:  
  51.         top_k = min(self.top_k, scores.size(-1))  # Safety check  
  52.         # Remove all tokens with a probability less than the last token of the top-k  
  53.         indices_to_remove = scores < torch.topk(scores, top_k)[0][..., -1, None]  
  54.         scores = scores.masked_fill(indices_to_remove, self.filter_value)  
  55.         return scores  
  56.   
  57. class TopPLogitsWarper(LogitsWarper):  
  58.     def __init__(self, top_p: float, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1):  
  59.         top_p = float(top_p)  
  60.         if top_p < 0 or top_p > 1.0:  
  61.             raise ValueError(f"`top_p` has to be a float > 0 and < 1, but is {top_p}")  
  62.         self.top_p = top_p  
  63.         self.filter_value = filter_value  
  64.         self.min_tokens_to_keep = min_tokens_to_keep  
  65.   
  66.     def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:  
  67.         sorted_logits, sorted_indices = torch.sort(scores, descending=False)  
  68.         cumulative_probs = sorted_logits.softmax(dim=-1).cumsum(dim=-1)  
  69.         # Remove tokens with cumulative top_p above the threshold (token with 0 are kept)  
  70.         sorted_indices_to_remove = cumulative_probs <= (1 - self.top_p)  
  71.         if self.min_tokens_to_keep > 1:  
  72.             # Keep at least min_tokens_to_keep  
  73.             sorted_indices_to_remove[..., -self.min_tokens_to_keep :] = 0  
  74.         # scatter sorted tensors to original indexing  
  75.         indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)  
  76.         scores = scores.masked_fill(indices_to_remove, self.filter_value)  
  77.         return scores
复制代码
此中主要的过程:


  • 将 logits 传递给 logits_processor 和 logits_warper,在这两个方法中进行一些预处理过程,例如添加惩罚项或对概率分布进行修改,使得天生的效果更符合盼望,详细调用了(这里用到了temperature参数,作用是调节模型天生的随机性,temperature通常被用于控制softmax函数的外形,从而影响天生序列的多样性,当temperature值接近0时,模型趋向于输出最大概的单个效果,也就是模型的输出趋向于确定性。这种环境下,全部的概率质量都会合在概率最大的谁人输出上,其他的输出的概率几乎为0,当temperature值比较大(大于1)时,模型趋向于输出更多样化的效果,也就是增加了模型输出的随机性。在这种环境下,差别的输出之间的概率差别减小,使得即使概率较小的输出也有大概被选中)
  • TopKLogitsWarper类是一个用于处理模型输出分数(scores)的工具,主要用于进行所谓的“Top-K截断”。在天然语言天生的过程中,Top-K截断是一种常见的技巧,它的目的是在每个天生步骤中,只保留K个最大概的输出选项,而忽略其他的选项。这种方法可以降低天生过程的复杂性,并且可以减少不太大概的输出的干扰。
  • TopPLogitsWarper类实现了被称为"Top-p(或nucleus)抽样"的计谋。该计谋用于限制模型在每个天生步骤中所考虑的大概输出的范围。在Top-p抽样中,我们不再固定考虑概率最高的K个输出,而是根据概率分布的累积分布函数(CDF)来选择大概的输出。我们设置一个阈值P,然后选择输出,直到它们的累积概率大于即是P。由于这个方法根据概率分布动态地调解输出的数量,所以它可以更好地处理差别的分布环境,从而在某些环境下可以天生更天然的文本。
  • 最后,利用 softmax 函数将经过预处理的 logits 转换为概率分布,并利用 multinomial 方法从中采样得到下一个 token。最后,将该 token 添加到原始输入序列中,并进行下一次迭代,天生新的文本内容。如果需要记载中间变量,则将它们存储在相应的变量中,以便之后访问根据得到的新token,随后更新input_ids得到下一个token_id之后
得到next_tokens之后,执行以下代码:
  1. input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1)  
  2. if streamer is not None:  
  3.     streamer.put(next_tokens.cpu())  
  4. model_kwargs = self._update_model_kwargs_for_generation(  
  5.     outputs, model_kwargs, is_encoder_decoder=self.config.is_encoder_decoder  
  6. )  
  7.   
  8. # if eos_token was found in one sentence, set sentence to finished  
  9. if eos_token_id_tensor is not None:  
  10.     unfinished_sequences = unfinished_sequences.mul(  
  11.         next_tokens.tile(eos_token_id_tensor.shape[0], 1).ne(eos_token_id_tensor.unsqueeze(1)).prod(dim=0)  
  12.     )  
  13.   
  14. # stop when each sentence is finished, or if we exceed the maximum length  
  15. if unfinished_sequences.max() == 0 or stopping_criteria(input_ids, scores):  
  16.     if not synced_gpus:  
  17.         break  
  18.     else:  
  19.         this_peer_finished = True
复制代码
此中:


  • 根据条件判定语句对天生效果进行一些后处理,例如将已完成的序列末尾添加 padding token,更新 model inputs 和 length 等等
  • 如果已经天生完备的句子,则将其标记为 finished,并查抄是否满足终止条件。当全部句子都结束或到达最大长度时,停止天生
最后通太过词器进行decode即可得到全部效果,这个是支持batch的:
  1. print(tokenizer.batch_decode(generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0])
复制代码
过程差不多就是这样的,团体pipeline结束。
落地相干
摆设的时间,除了模型要搞好,确保模型可以输出正常效果的设置参数也需要整明确,需要暴暴露来以便上游去调节。
CV模型的话,我们一般可调节的就是预处理的均值方差、图片RGB照旧BGR(通道什么的)、输入数据范围,图像resize巨细什么的;后处理的话,如果是检测模型,那就是iou阈值、nms阈值什么的。
对于llama来说,可调节的参数就有点多了。
举个例子,随便拿出一个gradio展示的LLM模型,可调节的参数如下:


  • seed   seed如果固定了,我们的输出也就固定了,我们利用chatgpt的时间,seed每次应该不一样
  • gen mode   sample 一般来说就是用这个 do_sample  ;greed  貌似这个最快;beam search  这个效果最好
  • end_id 模型练习的时间设置的结束id,模型在预测的时间,如果预测下一个token是end_id的话就应该终止预测了,就是这段话说完了
  • start_id 一般来说是在tokenizer中设置,在将输入文本encode的时间,设置的第一个token id
padding
LLM和CV模型一样,组batch的时间需求输入巨细一致,比如[4,3,128,128],而NLP中输入的是input_ids,padding的方法如下:
  1. input_sentences = [  
  2.     "DeepSpeed is a machine learning framework",  
  3.     "He is working on",  
  4.     "He has a",  
  5.     "He got all",  
  6.     "Everyone is happy and I can",  
  7.     "The new movie that got Oscar this year",  
  8.     "In the far far distance from our galaxy,",  
  9.     "Peace is the only way",  
  10. ]  
  11. tokenizer.pad_token = 0  
  12. input_tokens = tokenizer.batch_encode_plus(input_sentences, return_tensors="pt", padding=True)
复制代码
bad_words和stop_words
LLM在天生token的时间需要克制一些:


  • 不想天生的token
  • 碰到就停止的token
kv-cache
kv-cache是LLM推理过程的一个优化点,可以减少盘算,不外需要额外显存去存这些cache。
总结
可以参考hugglingface搭建服务的规则和一些细节,一般要支持:


  • 对于stream模式,要支持多个同时的请求
  • 对于非stream模式,需要支持多batch输入,也可以支持组batch
  • 对于stream模式,如果服务端认为天生token到结尾了,则可以主动断开连接并且返回终止符给客户端
一些概念
unconditional generation
在天然语言处理(NLP)中,"unconditional generation"是指模型在没有特定输入或提示的环境下天生文本。对比之下,"conditional generation"是指模型在给定某些输入或提示(例如,开头的一段文本或特定的任务描述)的环境下天生文本。
举个例子,如果我们让一个语言模型写一个故事,但不给它任何详细的主题或开头,这就是无条件天生。如果我们给模型一个特定的开头,比如"在一个深深的森林里,有一个小屋……",然后要求模型继续写下去,这就是有条件天生。
"start token"和"end token"是用来标识天生任务的开始和结束的特别标记。在GPT-J(我在2021年9月之前的知识库中没有关于GPT-J的信息,因此我不确定这是何种模型)中,对于无条件天生任务,开始标记和结束标记大概是相同的。这大概意味着,在没有输入或提示的环境下开始和结束文本天生。
context len
在天然语言处理(NLP)中,"context window"(上下文窗口)是一种常见的概念,它指的是在处理某个词或词组时考虑的前后相干词汇的范围。这个范围可以是固定的,也可以是动态的,取决于详细的模型和任务。
例如,如果我们利用一个巨细为5的上下文窗口来处理一个词,那么我们会考虑这个词前后各2个词作为其上下文。这样做的目的是获取更多的上下文信息,以便更好地明白和处理这个词。此外,某些类型的模型,如Transformer模型,现实上可以看作是没有固定上下文窗口巨细的,因为它们利用的机制(如自注意力机制)答应它们在处理每个词时考虑整个输入序列的全部词。
值得注意的是,上下文窗口的巨细和处理计谋大概会对模型的性能和效果产生显著影响。较大的上下文窗口大概会捕获更多的长间隔依赖关系,但也大概增加模型的盘算复杂度。相反,较小的上下文窗口大概会减少盘算复杂度,但大概无法捕获一些紧张的长间隔依赖关系。因此,选择合适的上下文窗口巨细和处理计谋通常需要根据详细的任务和数据进行调解。
参考
文中全部图片来源于以下的链接:
Transformer论文逐段精读【论文精读】(https://www.bilibili.com/video/BV1pu411o7BE/?spm_id_from=333.880.my_history.page.click&vd_source=eec038509607175d58cdfe2e824e8ba2)
​​https://chat.openai.com/​​
​​https://huggingface.co/learn/nlp-course/chapter1/3?fw=pt​​
​​https://www.eet-china.com/mp/a256824.html​​


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

张裕

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表