ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

一个只服务一个人的语言模型,是怎么把速度做到两倍的

一个只服务一个人的语言模型,是怎么把速度做到两倍的 你有没有想过你手机上跑的AI助手和数据中心里那些动辄几千张显卡同时轰鸣服务几万用户的大模型本质上其实不该是同一种东西大部分人不会想这个问题因为现在的做法就是先在GPU集群上训练一个大模型追求各种榜单分数然后想办法把它压缩塞进手机或电脑里砍掉一些层量化一下精度凑合能跑就行。这是行业默认的路径几乎没人质疑过。但有一群人反过来想了这件事。他们说等一下如果我们一开始就知道这个模型最终只会在CPU上运行只服务一个用户一次只吐出一个字那为什么不从这个约束出发去设计架构而是事后削足适履这就是这篇论文里Daedalus-150M模型的起点。它不是又一个更小的大模型而是一个从终端使用场景反推出来的架构。结果挺有意思它用不到300亿token七分之一都不到的数据量打赢了好几个用了三到六倍数据训练出来的同尺寸模型解码速度还快了将近两倍。为什么CPU上跑模型和GPU完全是两回事先说清楚一件事这篇论文瞄准的不是更省参数而是更省内存带宽。这两者听起来像近义词其实差得很远。在GPU服务器上一次会同时处理很多用户的请求这叫批处理。批处理的好处是模型的权重只要从显存读一次就能被一堆用户的请求同时复用读取权重的成本被摊薄了。这时候真正的瓶颈是算力也就是显卡一秒钟能做多少次乘法加法。但如果只有一个用户在跟模型对话呢没有其他请求可以拿来摊薄成本。每生成一个字模型都得把自己全部的参数完整地从内存里读一遍。这时候瓶颈就不再是算力了而是内存带宽也就是硬件一秒钟能从内存里搬多少字节的数据。现在的CPU算力其实过剩真正卡脖子的是数据搬运的速度。打个比方这就像一个厨师做菜。批处理模式下厨师一次性给二十个客人炒同一道菜切菜、开火这些准备工作只需要做一次二十份菜均摊了这个成本效率很高。但要是厨师一次只给一个客人做一道菜做完立刻要换下一道完全不同的菜那准备工作没法摊薄每道菜都要重新经历一次完整的准备流程。这时候决定你吃饭速度的不是厨师炒菜的手速有多快而是他从冰箱里把食材搬到灶台上要花多长时间。如果不认清这一点还在死磕让厨师炒得更快那优化方向就从一开始就错了。带宽*指硬件在单位时间内能从内存搬运多少字节数据的能力这里是决定CPU推理速度的核心瓶颈。除此之外还有第三个麻烦叫KV缓存的问题。KV缓存*Key-Value Cache的缩写是注意力机制里用来记住之前所有字的一种存储结构每生成一个新字都要重新读一遍这个缓存。在标准的Transformer架构里每一层的注意力机制都需要回头看前面所有已经生成的内容才能决定下一个字该是什么。这意味着你聊得越久对话越长模型每生成一个新字需要读取的历史信息就越多。这个成本是随着对话长度线性增长的。在GPU上批处理的场景下这个成本被很多用户摊薄了还能忍。但单用户CPU场景下这个成本会随着对话变长直接拖垮整个系统的响应速度。这就是这篇论文要解决的核心矛盾怎么让模型在长对话里也不至于越聊越卡。三分之二的层不再回头看历史一个混合架构的设计Daedalus-150M的解法是把整个模型的18层拆成两种角色。其中6层保留标准的全注意力机制另外12层换成一种叫短卷积的结构。短卷积*一种只关注最近几个字、状态大小固定不变的计算方式不管对话进行到第几个字它需要记住的东西永远只有那么多。这12个卷积层的状态窗口只有2个时间步宽不管你和它聊了2句话还是2000句话它每次要处理的历史信息量是完全一样的是个常数不会随对话变长而膨胀。而剩下的6个注意力层还是老老实实地保留了回头看整个历史的能力只是层数被压缩到了原来的四分之一。这里有个细节特别巧妙。论文里给出了具体的字节计算混合架构每往前多聊一个字需要多读的缓存字节数是6144字节而如果换成传统的、24层全部用注意力的对照模型这个数字是12288字节正好是两倍。原因不只是层数少了四倍这么简单混合架构还在保留的6个注意力层里用了一种叫分组查询注意力的技术把缓存进一步压小了三倍。分组查询注意力*英文缩写GQA让多个查询头共享同一组键值头从而减少每层需要保存的缓存数据量是一种在保留大部分能力的同时压缩内存占用的技术。回到之前那个厨师的比喻。如果厨师是全职翻看菜谱的类型每做一道菜都要把之前做过的所有菜谱翻一遍以确保风味一致那么随着他做的菜越来越多翻菜谱的时间会越来越长最后可能比炒菜本身还慢。而这个混合架构相当于让厨师团队里三分之二的人换了一种工作方式他们只需要记住刚做的上一道菜和上上道菜的味道就行完全不用翻旧菜谱剩下三分之一的人依然保留翻菜谱的能力负责那些真正需要记住很久以前细节的菜式。如果不这么分工让所有厨师都去翻菜谱团队整体速度就会被拖得越来越慢聊天聊得越久这个拖累就越明显。这正是论文里反复强调的一点混合架构的速度优势不是一个固定倍数而是随着对话变长不断拉大的差距这恰恰证明了问题出在要不要回头看历史这件事上而不是别的什么原因。论文里那张架构图图1画得很清楚18个色块交替排列深色的是保留注意力的层位置分别在第4、7、9、11、13、16层其余全是浅色的卷积层。深色块旁边标着缓存随对话线性增长这是要交的税浅色块旁边标着状态固定为2个时间步跟对话长度无关。这张图其实就是整篇论文的浓缩版用最少的税换最多的能力保留。为什么不干脆把所有注意力层都换掉因为纯粹靠卷积没法做精确的长距离信息检索比如你在对话很早期提到的一个具体名字或数字纯卷积结构很难原封不动地把它捞回来。注意力机制在这件事上是独一无二的所以论文选择保留六层而且这六层是均匀撒在18层里的不是堆在一起这样不同深度的表示都能获得检索能力。除了架构还有几个不起眼但重要的小设计论文里提到几个容易被忽略的选择其实都在为省字节这个目标服务。第一个是词嵌入共享也就是让输入层的查字典表和输出层的查字典表用同一份参数而不是各自维护一份。这个词表有49152个词对应的矩阵占了3770万参数接近整个模型的四分之一。共享之后相当于直接省掉了一整份的存储和读取开销。第二个是把前馈网络的中间层宽度从常规的4倍模型宽度收窄到2.67倍。这个选择听起来很技术但背后的逻辑很直白前馈网络的这个中间层是模型里最胖、每次读取消耗字节最多的部分之一收窄它就是把参数预算从最耗带宽的地方挪到更有效率的地方比如层数和注意力机制上。引用块前馈网络*Transformer里每层负责在两次注意力计算之间做非线性变换的子模块通常会先把维度放大再缩小这个放大的倍数是个可调参数。第三点是量化格式的选择。论文选用了一种叫Q4_0的4比特量化格式理由不是它的数值误差最小而是它在目标硬件上对应的底层计算指令是优化得最好的。这个选择背后透露一个态度这篇论文从头到尾优先考虑的都是这东西真的能在普通CPU上跑得快而不是这个数字在纸面上好看。量化*把模型参数从32位或16位浮点数压缩成更少比特比如4比特表示的技术能大幅减小模型体积和内存占用但通常会牺牲一些精度。这就好比你去买一双跑步鞋有的鞋在实验室测试里各项数据都很漂亮但穿上跑起来却不趁手因为它的设计目标是测试台而不是真实路面。这篇论文里的每一个小选择都在反复确认自己是不是真的适配真实路面也就是那台安静地待在用户口袋里或者笔记本电脑里的CPU。数据从哪来怎么喂给模型模型的训练数据是十个英文数据源混合而成的总共有169亿个不重复token但整个训练过程消耗了599亿token这意味着平均每份数据被模型看了大约3.5遍。这里有个细节挺有意思论文给每个数据源设了一个上限最多重复四次这是参考了此前的研究说重复读四遍以内对模型质量的损伤很小。但问题是如果严格按比例分配某些体量很小的数据源比如一个只有大约40万token的日常对话数据集想要达到它2%的目标占比就得被反反复复读上千遍这已经完全偏离了2%占比原本想表达的意思。所以论文用了一种叫水位填充的策略把超出上限的部分匀给还有余量的其他数据源。这就像分蛋糕你说每个人应该分到蛋糕的2%但如果某种口味的蛋糕总共只做了很小的一份硬要让它占满2%那就得把这一小份反复端上桌几十次客人吃到最后早就腻了还没吃到别的口味。水位填充的做法相当于某种口味不够就用别的口味补上保证蛋糕总量凑够但不会让某一种口味被迫无限重复。如果不做这个调整模型很可能会对着那几十万token的日常对话数据反复咀嚼上千遍学到的可能是重复带来的偏差而不是这类内容本身该有的语言风格。数据切分上也藏着一个容易踩的坑。论文严格按照整份文件来划分训练集和验证集不允许把一份文档从中间切开一半拿去训练一半拿去验证。这样做的代价是不同数据源实际预留出来的验证集比例并不完全等于设定的2%有个数据源因为最后一份文件恰好比较大实际预留比例达到了9.16%。论文没有藏着掖着这个问题而是把它明确写了出来还专门指出如果不这样处理用holdout的token数量而不是采样概率去加权计算验证集指标算出来的数字会偏高9.7%因为那些偏难的数据源恰好文件比较大。这种连细枝末节的加权方式都要交代清楚的态度在论文里出现了不止一次。训练过程里一个容易被误读的停滞现象论文用了一种叫WSD的学习率调度方式全称是预热-稳定-衰减。学习率调度*训练过程中控制模型学习速度快慢变化的策略这里采用的方式是先快速升温然后长时间保持在最高点最后线性下降到零。有意思的是在稳定阶段也就是大约从第2万步到第6.8万多步这段时间里学习率一直保持在峰值不变这时候模型的训练损失几乎不怎么下降看起来像是卡住了。论文里特别提到这次训练过程中确实有观察者把这段平台期误判成了训练失败或者停滞但实际上这是调度策略本身的设计真正的质量提升绝大部分发生在后面学习率开始下降的阶段。这让我想起跑步训练里的一个现象有些跑者会经历一段体感没有进步的平台期明明每天都在练配速却纹丝不动很多人会在这个阶段选择放弃觉得练不动了。但实际上身体在这段时间做的是打基础的工作真正看得见的突破往往发生在放弃临界点之后不久。如果论文的研究者也被这个平台期唬住了提前砍掉训练那后面通过学习率衰减换来的大部分质量提升根本不会发生。核心实验混合架构真的比纯注意力架构强吗这是整篇论文里最关键的一次对照实验。研究者训练了两个参数量几乎相等的模型一个是刚才说的混合架构160.49M参数另一个是传统的、24层全部用注意力机制的对照组161.25M参数两者相差不到0.5%。它们用完全相同的数据和训练流程各训练了50亿token。更值得称道的是评判这次实验胜负的标准是在实验开始之前就写死的不是等结果出来之后再挑一个对自己有利的指标。这个预先定好的标准是验证集上的bits-per-byte指标同时设了一个0.5%的最低胜出门槛如果传统架构赢过这个门槛那么整个正式训练所用的架构就要推翻重来。结果混合架构在这个预先约定的指标上赢了0.81%越过了那道门槛。但在下游任务的五项测试平均分上传统架构反而略微领先0.14分这个差距在统计上大约只有0.24个标准差考虑到这类测试本身的噪声在0.58个标准差左右这个差距基本可以视为噪声谁也没赢谁。论文对此的态度非常坦诚五个任务里两边互有胜负胜负模式看不出规律这正是噪声的典型特征而不是真实差距的信号。引用块bits-per-byte*简称bpb衡量语言模型预测能力的一种指标按字节而不是按词元计算好处是不同分词方式的模型之间也能公平比较。这里说句实话我觉得这个诚实的态度挺难得。很多论文在两个指标打架的时候会选择性地突出对自己有利的那个闭口不提另一个。这篇论文直接把两个指标都摆出来说这是打平不试图把它包装成一场胜利。真正决定性的差距出现在解码速度上。研究者测试了三个不同的对话长度空白开局、聊到512个字、聊到2048个字也就是模型训练时设定的最长对话长度。结果发现两个模型的速度差距在对话刚开始时几乎可以忽略不计只有1.20倍但随着对话变长差距持续拉大到2048个字时达到了1.76倍。这个差距随长度增长的模式极其重要因为它恰恰印证了差距的根源确实是要不要回头翻旧账这个设计而不是别的什么原因导致的。如果混合架构只是单纯地更瘦那不管对话多长它应该始终快一个固定的倍数而不是越聊越占优势。论文还专门去测了和一个完全不同团队做的、参数量135M的外部模型对比结果同样的模式复现了空白对话时两者速度接近只快6%聊到2048字时Daedalus快了整整2.08倍而且Daedalus本身参数量还比对方多19%。这个复现在论文里被认为是比内部对照实验更有说服力的证据因为它没法用这是本项目训练代码或者导出代码的巧合来解释。|对话长度|混合架构 (tok/s)|对照架构 (tok/s)|速度比||---|---|---|---||0空白|1111.9|922.8|1.20×||512|960.3|664.4|1.45×||**2048**|**739.3**|**420.3**|**1.76×**|一个算出来的数字居然比实测的还保守论文里做了个挺有意思的动作先用一个简单的数学公式去预测这个速度优势应该是多少然后拿实测结果去检验这个公式对不对。这个公式的逻辑很简单每生成一个字需要读取的总字节数等于模型权重的字节数加上所有注意力层缓存字节数乘以当前对话长度。按这个公式算下来在2048字长度时混合架构应该比对照架构快17%。但实测结果是76%的优势比预测值高出四倍还不止。论文很诚实地承认这个纯粹靠字节数计算的模型方向是对的但数量级错得离谱而且这个偏差还会随对话变长继续扩大。他们给出的解释是公式没考虑到两个东西一是注意力机制在读取缓存时要做一个依赖性很强的运算步骤这个步骤受限于运算延迟而不是单纯的数据搬运速度尤其当要处理的数据量超过CPU高速缓存容量时会明显变慢反观卷积结构只需要处理两个元素的固定状态数据访问的局部性非常好。二是对照组模型本身层数更多24层对18层每层都有的一些固定开销也因此多付出了三分之一。这就好比你去预测两条路线开车到目的地分别要多久你只计算了路程长度除以限速得出A路线应该比B路线快17%。但实际测下来快了76%因为你没算上B路线沿途有一堆红绿灯路口需要停下来等而且路口越多车流积压得越严重这种停等造成的延误跟路程长度不是简单的线性关系会随着红绿灯数量的增加而加速恶化。如果只按路程算账会严重低估拥堵路线的实际损失。这也是论文特别强调的一点单纯优化底层的注意力计算内核能缩小差距但没法消除它因为混合架构根本没有保留那份需要被优化的缓存。揭晓成绩单这个模型到底考了多少分论文设定的评测标准是在五个常见任务上的平均分HellaSwag、ARC-Easy、PIQA、OpenBookQA和WinoGrande。这五项测试涉及常识推理、科学知识、物理常识等不同方面。在正式训练开始之前研究者就把及格线定好了42.2分这是几个对照模型里表现最强的一个的分数。最终Daedalus-150M考出了47.31分超过及格线5.11分。这个成绩单最有意思的地方在于对照组的构成。表格里列出的对照模型包括GPT-2 124M、Pythia-160M、OPT-125M、GPT-neo-125M全都用了三到六倍于Daedalus的数据量进行训练结果还是被超过了。甚至连MobileLLM-125M这个用了整整1万亿token训练出来的模型公开发表的分数也被Daedalus超过了。要知道Daedalus总共只用了599亿tokenMobileLLM用的数据量是它的近17倍。|模型|训练token数|五项任务平均分||---|---|---||**Daedalus-150M**|59.9B|**47.31**||MobileLLM-125M|1T|46.3发表值||GPT-2 124M|—|42.2||OPT-125M|180B|42.1||GPT-neo-125M|300B|41.9||Pythia-160M|300B|41.0||Peer-135M|2T|51.2|表里最后一行Peer-135M用了2万亿token比Daedalus多了三十多倍的数据分数还是领先3.9分。论文对此没有回避直接说这是训练开始前就承认会输的部分这项研究本质上是在拿质量换取解码速度在参数量固定的前提下做取舍不是想在所有维度上都当第一。论文还特别提醒读者这里所有的对照模型分数都是研究者自己重新在同一套评测工具上跑出来的而不是直接引用各自论文里公布的数字。原因是公开发表的分数往往用的评测任务子集或计分方式略有不同直接拿来比较容易制造出并不真实存在的优势差距可能有0.5到1.5分之多。这种自己动手统一复现的做法虽然麻烦但公平性高得多。论文没有藏着的失败那些没成功的尝试这篇论文一个很打动我的地方是专门辟出篇幅讲了三件没做成的事而不是只报喜不报忧。第一件事是量化感知训练的失败。研究者原本计划在训练的最后5%阶段让模型提前适应4比特量化会带来的误差这样正式量化之后的精度损失能小很多。但这个方案一启动训练第一步就出现了数值上的崩溃直接被叫停。最终发布的模型是训练完之后再做量化的量化损失达到了大约6%的困惑度而如果量化感知训练成功了这个数字本可能只有2.5%。研究者坦承没有查出崩溃的具体原因。困惑度*衡量语言模型预测下一个词准确程度的指标数值越低说明模型预测得越准。第二件事是关于卷积通道里存在大量死掉的部分。研究者发现模型里将近47.9%的短卷积通道对最终输出完全没有贡献这个比例在训练过程中稳定不变从第9896步到第3万步几乎纹丝不动相当于白白浪费了大约1360万参数占整个模型参数量的8.5%。既然这些通道是死的那把它们直接砍掉、瘦身模型文件不就行了研究者确实这么试了但推理引擎在加载模型文件时会检查每个张量的形状是否符合预设标准砍窄之后的文件直接被引擎拒绝加载报错信息明确指出期望的维度和实际维度对不上。研究者甚至专门做了个对照实验把没有砍窄、维度完全正常的文件用同样的重写工具重新生成一遍结果是能正常加载的这就排除了是重写工具本身有问题的可能性确认问题就出在砍窄这个操作本身。要修复这个问题得去修改推理引擎本身的代码但这样一来就没法用现成的、未经修改的标准推理程序来运行模型了而这恰恰是整个项目能在普通CPU上跑起来这个卖点的根基。为了省下不到8MB的文件体积去打破和标准推理软件的兼容性这笔账怎么算都不划算所以研究者选择放弃这个方向把它列为留给下一代模型在设计初期就要解决的问题。这就好比你买了一件衣服发现内衬有一块布料完全用不上纯属浪费材料想着不如把这块布料剪掉省点重量。但裁缝店的标准生产线要求衣服必须符合特定的裁剪模板剪掉那块布料就没法通过质检流程上架销售。你要么违反标准流程冒险改造要么就接受这块布料一直存在。研究者选择了后者还诚实地把这个衣服里有块用不上的布料的事实告诉了所有读者。第三件事是词表选得偏大。这个模型用的词表有49152个词但论文引用的经验规律显示像这个尺寸的模型词表大小理想值应该在24000到32000之间。之所以选了偏大的词表是因为最早的项目计划里这个模型要跟另一个更大的老师模型共享同一套分词方式后来那个计划因为别的原因取消了但词表这个选择却被保留了下来没人回头去改。这多花的参数占了模型整体的23%如果换成理想大小的词表能省出大约1300万参数用在真正做计算的层上。论文特意提到他们专门验证了分词器是否真的和量化后的模型文件保持一致直接从量化文件里读取词元编号和标准分词器逐一核对结果完全没有偏差。研究者说得很直白如果这里出了偏差模型会看起来一切正常各项指标都很健康实际生成的却是一堆读起来通顺但毫无意义的胡话而且整个流程里没有任何其他环节能捕捉到这种错误。部署到真实设备上会是什么样子论文最后聊了些落地层面的细节。整个量化后的模型文件只有95.56 MiB加上2048字长对话所需的缓存空间大约12.6MB一次完整的单用户会话所需内存能控制在128MB以内。这意味着这个模型完全可以常驻在手机或电脑里跟着某个应用程序一起运行不需要单独占用一个进程。论文还提到一个和直觉不太一致的现象因为CPU上单用户解码是被内存带宽卡住的所以增加处理器核心数到一定程度后速度就不再提升了混合架构因为搬运的数据量本身更少达到这个饱和点所需要的核心数也更少。这对于要在共享设备上运行的场景是个额外的好处。写在后面读完这篇论文最触动我的其实是那种近乎较真的诚实。大部分论文写到实验对比的时候会本能地把镜头对准自己赢的那部分但这篇论文把平局清清楚楚地摆出来了甚至专门解释了为什么下游任务的0.14分差距其实是噪声不该被解读成任何一方赢了。这种克制在学术写作里其实不常见。另一个让我意外的细节是那个死通道砍不掉的故事。研究者花了力气去验证瘦身方案确实可行、确实能省空间最后却因为推理引擎的形状校验卡死在最后一步。这种技术上能做工程上做不了的落差比单纯说我们尝试了剪枝但没成功要真实得多也更有教育意义它提醒我们很多时候限制一个想法落地的不是理论而是周围整套生态系统愿不愿意配合。还有一个小地方值得琢磨论文反复强调速度优势是随对话长度增长的而不是一个固定倍数。这其实是个很好的判断方法论判断一个改进是不是真的来自你声称的那个机制就看它的表现是否符合那个机制该有的形状。如果速度提升是个常数倍数那更可能只是代码写得更精简了只有当提升幅度随着你动的那个变量变化时才说明你真的改对了地方。这个思路其实不止适用于模型架构任何时候想验证我做的这个改动是不是真的解决了我以为的那个问题都可以问一句如果我的解释是对的效果应该随什么变量变化那个词表选大了却一直没人改的细节也挺值得咂摸。一个早已作废的计划留下的选择就这样悄悄地在模型里躺了23%的参数份额直到写论文复盘的时候才被翻出来重新审视。这大概是所有工程项目里都会发生的事某个决定的原因早就不存在了但决定本身还在。QAQ1Daedalus-150M是什么ADaedalus-150M是一个专门为CPU单用户推理设计的语言模型参数量约1.6亿18层里只有6层用传统注意力机制其余12层用短卷积结构训练数据量约599亿token在五项常识推理任务上平均得分47.31超过了多个用三到六倍数据训练的同尺寸模型。Q2混合架构比传统全注意力架构到底快多少A在对话长度2048个字时混合架构解码速度比参数量相近的全注意力对照模型快1.76倍跟外部一个135M参数模型对比快2.08倍。速度优势会随着对话变长持续扩大对话刚开始时差距很小说明这个优势确实来自缓存机制的差异而不是模型本身更精简。Q3这个模型有哪些没解决的问题A论文坦承了三个问题量化感知训练在训练时因数值崩溃被迫取消导致最终4比特量化的精度损失达到6%模型里约47.9%的卷积通道完全不起作用却因为推理引擎的形状校验无法被物理删除词表规模49152个词偏大理想值应在24000到32000之间多余部分浪费了约1300万参数。
返回列表