1.1 参数的定义与作用:从权重到模型行为
我刚开始接触大模型的时候,听到"这个模型有70亿参数"之类的话,脑子里其实没什么概念。后来自己做了一些小实验才慢慢明白,参数这东西说白了就是模型内部的一堆数字,是模型从数据里学到的“经验值”。你可以把它想象成一个巨大的旋钮面板,每个旋钮都有一个数值,模型在训练过程中不断调整这些旋钮,直到输出结果跟预期尽量接近。这些旋钮的组合决定了模型看到一句话之后会怎么反应。
从技术层面讲,参数最典型的就是神经网络里的权重和偏置。权重决定了输入信号在层与层之间传递时的放大或缩小程度,偏置则像是给每个神经元一个基础偏移量。我一开始觉得偏置不重要,后来发现没有偏置的网络表达能力会受限很多,它能让激活函数在零点附近更灵活地移动。
参数的作用不只是“记住”训练数据,更重要的是它们构成了模型对语言的抽象表示。比如嵌入层的参数会把“猫”和“狗”映射到相近但不重合的向量空间位置,注意力层的参数则学会了哪些词该关注哪些词。这些行为不是人写规则写出来的,是参数在训练中自己“长”出来的。所以理解参数,本质上是在理解模型行为的物质基础。
1.2 参数与超参数、架构参数、训练数据的区别
我经常看到有人把参数和超参数混着说,其实这两个东西完全不是一回事。参数是模型自己学出来的,比如权重矩阵里的每一个浮点数。超参数是你训练之前就得定好的东西,比如学习率、批次大小、训练轮数。你定超参数,模型定参数,分工很明确。我踩过一个坑,早期调模型的时候拼命改学习率,结果发现模型结构本身就不适合那个任务,超参数调得再好也白搭。
架构参数又是另一个维度。层数、隐藏维度、注意力头数这些决定了模型的“骨架”有多大,它们本身不参与梯度更新,但直接限制了参数总量的上限。你可以把架构参数看成是盖楼时的楼层数和每层面积,参数则是砖块水泥的具体用量。训练数据就更不一样了,那是外部的原材料,参数是模型从原材料里提炼出来的“知识浓缩物”。数据质量差,参数学出来就是歪的,这个我深有体会。
1.3 常见参数类型:嵌入、注意力、前馈网络、归一化与偏置
拆开一个大模型看参数,大致能分成几大块。嵌入层参数是我觉得最直观的,它就是一个大表格,每个词或者每个token对应一行向量。词表有多大,嵌入矩阵就有多少行。这块参数占了不小比例,尤其是多语言模型,词表动辄十几万,嵌入层能吃掉好几个亿的参数。
注意力层的参数主要就是Q、K、V三个投影矩阵,外加输出投影。我画过一张图来理解它们,Q负责“我在找什么”,K负责“我有什么标签”,V负责“我实际能提供什么内容”。这三个矩阵的参数量跟隐藏维度和头数直接相关。前馈网络层的参数通常是最多的,两层线性变换中间夹一个激活函数,隐藏维度往往比模型维度大好几倍,所以FFN层的参数能占到总参数的一半以上。
归一化层的参数很少,就是缩放和偏移两个向量,但作用不小,训练稳定性很大程度上靠它们。偏置项在每个线性层后面都有,数量不大,但少了它们模型拟合能力会打折扣。我自己的经验是,看一个模型的参数分布,基本就能猜出它的设计取向,FFN占比高的通常表达能力更强,嵌入占比高的往往词表覆盖更广。
1.4 参数规模度量:参数量、参数量级、有效参数与计算量
参数量这个概念看起来简单,数一数有多少个可学习的浮点数就行了。实际用起来的时候,大家更常说的是“参数量级”,比如7B、13B、70B,B就是十亿。我第一次看到175B这个数字的时候没什么感觉,后来算了一下,175B个float32参数光存储就要700GB,这才意识到规模意味着什么。
有效参数是最近几年越来越常听到的说法。混合专家模型总参数可能上千亿,但每次推理只激活其中一小部分,真正参与计算的参数远小于总数。我一开始觉得这是文字游戏,后来理解了,有效参数更贴近实际计算成本和能力表现。计算量又是另一个指标,通常用FLOPs来衡量,它跟参数量、训练token数、推理序列长度都有关。参数量大不代表计算量一定大,稀疏激活就是个典型例子。
度量参数量的时候还得注意精度。同样是70亿参数,fp16存储占14GB,int8量化后只要7GB,参数量没变但资源占用差一倍。我习惯在看模型规格时同时关注参数量和精度,只看参数量容易被误导。
1.5 本章小结:理解参数是理解大模型能力与成本的基础
写完这一章我回头想了想,参数这个概念贯穿了大模型的所有环节。训练是在调参数,推理是在用参数,微调是在改参数,压缩是在减参数。你如果不清楚参数怎么分布、怎么度量、跟超参数和数据有什么区别,后面讨论缩放规律、参数效率、微调策略的时候就会一头雾水。
对我自己来说,最大的收获是把“参数量”从一个模糊的数字变成了一个有结构的认识。知道嵌入层占多少、FFN占多少、哪些参数决定表达能力、哪些参数影响训练稳定性,看模型的时候心里就有了一张地图。这张地图也是后面理解成本、选型、优化的起点。
2.1 缩放规律(Scaling Laws)与参数量、数据、算力的平衡
我最初以为模型越大就越聪明,这个直觉在早期确实成立了一段时间。后来读到Kaplan等人的缩放规律研究,才发现事情比我想的精细得多。他们发现模型性能跟参数量、数据量、计算量之间呈现幂律关系,在对数坐标下几乎是一条直线。这意味着你投入更多资源,性能会稳定提升,但提升的速率在放缓。这个发现让我重新理解了“大就是好”这句话——大确实好,但不是无限好。
2022年DeepMind那篇Chinchilla论文给了我更大的冲击。他们指出之前的大模型普遍“参数过剩、数据不足”,训练不充分。在给定计算预算下,参数量和训练token数应该大致按照1:20的比例增长才最优。我看到这个结论的时候挺意外的,因为按照这个比例,一个70B参数的模型需要约1.4万亿token的训练数据,而当时很多模型的数据量远没达到这个水平。
这让我意识到参数、数据、算力三者之间是一种动态平衡关系。你只猛加参数不加数据,模型就像吃得多但消化不了,效果提升有限。你数据很多但参数太少,模型容量不够也吸收不了那么多信息。我自己做小规模实验的时候也验证过这一点,参数翻倍但数据不变,loss下降幅度确实不如参数和数据同时翻倍来得明显。
2.2 参数量增长带来的性能收益与边际递减
说到性能收益,我习惯用“吃包子”来打比方。你饿的时候吃第一个包子特别香,吃到第五个还不错,吃到第十个就开始撑了。参数量增长对性能的提升也是这个道理,从1亿到10亿参数的跨越,效果提升非常显著,但从100亿到175亿,很多基准测试上的提升就没那么夸张了。
我翻过一些评测数据,在MMLU、GSM8K这些基准上,参数量从几十亿到几百亿的区间内,分数确实在涨,但涨的斜率在变小。尤其是某些简单任务,比如文本分类、情感分析,小模型本来就做得不错,大模型带来的增益非常有限。这让我明白边际递减不是理论上的说法,在真实评测里是看得见摸得着的。
但这不意味着大参数就没意义了。我发现边际递减在“简单任务”上特别明显,在“复杂推理任务”上却没那么快衰减。数学推理、代码生成、多步逻辑推断这些任务,大模型和小模型之间的差距依然很大。所以我现在的理解是,参数增长的收益取决于你拿它做什么任务,通用能力提升在放缓,但复杂能力的天花板还在被不断推高。
2.3 涌现能力、任务复杂度与参数规模的匹配
涌现能力这个概念我琢磨了很久。我第一次看到“模型在某个规模突然学会了做算术”这类描述的时候,觉得很神奇,好像模型内部有个开关被啪一下打开了。后来自己做了些实验,发现更像是“量变积累到一定程度后,原本很差的表现在评测上终于超过了随机水平”。所谓“突然”,跟评测指标的设定方式有很大关系。
我拿同样的模型在不同规模下测过一些任务。简单的指代消解、关键词抽取,小模型表现已经不错,规模增大带来的提升很平滑。但到了需要多步推理的任务,比如“如果A比B高,B比C高,谁最矮”这种,小模型几乎是在瞎猜,参数规模到一定量级之后准确率才明显起来。这让我觉得任务复杂度和参数规模之间确实存在一个匹配关系,太小的模型做复杂任务就是在浪费算力和时间。
这个认识对我做模型选型帮助很大。不是所有场景都需要最大的模型,选一个刚好能覆盖你任务复杂度的规模,性价比最高。我现在会先拿小模型跑一个baseline,如果效果差得不多就果断用小模型,差得远再往上跳一档。这样既省钱又不至于为了追求“最先进”而过度配置。
2.4 参数量与推理成本、显存占用、延迟的关系
推理成本这块我踩过不少坑。最早部署一个13B模型的时候,我以为两张24GB的卡就够了,结果实际跑起来发现显存占用远超预期。参数本身占26GB(fp16),加上KV Cache、中间激活值、框架本身的额外开销,两张卡都紧巴巴的。这让我切身体会到参数量对显存的直接影响是刚性的,省不掉。
延迟方面,参数量越大,单次前向传播的计算量就越大,首token延迟和每token生成时间都会上涨。我测过同一个任务在7B和70B上的表现,70B的首token延迟大概是7B的五六倍,流式输出的吐字速度也明显慢一截。如果是对实时性要求高的场景,比如在线客服、实时翻译,大参数模型直接用就是给自己找麻烦。
还有个容易被忽略的点是显存带宽。推理的时候参数需要从显存里反复读取,参数量越大,对带宽的压力越大。我后来做量化部署,int8之后显存占用减半,延迟也有改善,这让我意识到推理成本不只是“参数量除以精度”这么简单,硬件特性、并行策略、批大小都会掺和进来。现在我做方案评估的时候会把参数量、精度、显存、延迟放在一张表里一起看,不再只盯着一个数字。
2.5 评估参数量与性能关系的常用指标与实验方法
评估参数量和性能的关系,我常用的指标分几类。第一类是任务准确率,比如分类任务的F1、生成任务的BLEU或ROUGE、推理任务的精确匹配率。这些指标直观,但单看一个基准容易被误导,我习惯至少看三四个不同难度的基准,综合判断。
第二类是损失值,尤其是验证集上的loss。loss的好处是连续性好,不像准确率那样跳变,能更精细地反映参数量增长带来的收益变化。我做过一组小实验,把参数量从1亿逐步加到10亿,记录每一步的验证loss,画出来的曲线确实是指数下降后逐渐平缓的形状,跟缩放规律的描述吻合。
实验方法上,我一般用控制变量法:固定数据、固定训练步数、固定超参数,只改参数量,看性能怎么变。这样能排除数据质量、训练策略的干扰。但我也知道实际工业场景很少这么干净,数据在变、训练策略在变、任务也在变,所以缩放规律更像是“理想条件下的参考线”,实际偏离是常态。我会拿缩放规律当方向标,不会拿它当精确公式。
2.6 本章小结:参数规模不是唯一变量,需综合数据与训练策略
写完这一章,我对参数量这件事的认知又深了一层。参数量确实是模型能力的底座,但它不是一个孤立的变量。数据质量、数据量、训练策略、模型结构、推理配置,每一个都会影响最终表现。Chinchilla那套比例给了我一个重要的提醒:参数和数据要匹配着来,单方面猛堆一个维度很容易浪费钱。
我现在看一个模型的时候,不会只问“它多大”,而是会问“它用了多少数据训的”“训练策略是什么”“它在我关心的任务上表现如何”。这种多维度视角让我在做选型的时候少交了不少学费。参数规模是起点,不是终点,真正决定落地效果的是参数、数据、算力、策略这几样东西的配合。
3.1 稠密模型参数分布:层间参数与表示容量
我第一次把一个大模型的参数按层拆开来看的时候,说实话有点意外。各层的参数分布一点都不是平均的,嵌入层看着不起眼,实际上占了一大块,尤其是词表大的模型,嵌入矩阵动不动就是几十亿参数。中间那些Transformer Block里,前馈网络的参数量通常是注意力模块的两三倍,我一开始以为注意力才是大头,查了结构之后才发现不是这么回事。
层与层之间也有讲究。浅层参数相对少一些,越往深层参数越多,模型把更多的表示容量放在了后面几层,我猜这跟特征从简单到复杂的加工顺序有关。我手算过一个小模型的参数分配,嵌入层大概占了两成,FFN占了六成,注意力只占一成多,剩下的零碎分布在归一化和输出投影上。这个比例直接决定了训练时的显存峰值和推理时的读取热点。
我自己的感受是,理解这个分布特别有用。你知道参数堆在哪里,就知道该往哪里使劲。想压缩模型,先看嵌入层能不能做权重共享或者缩小词表;想分析容量瓶颈,盯着FFN的宽度和深度调比瞎调整个模型结构高效得多。稠密模型的结构不复杂,但参数在各层的分布不均匀这件事,是后来我做所有效率优化的起点。
3.2 稀疏激活与混合专家(MoE):总参数与有效参数
头一回接触MoE架构,我脑子里的第一个问题是:这玩意儿到底算多大模型?Mixtral 8x7B摆在那,总参数写着46B左右,但每次推理只激活大约12B。你说它是13B级别的模型吧,它训练时确实用到了46B的容量,你说它是46B吧,推理成本又没那么高。这种“总参数”和“有效参数”分开算的玩法,跟我之前理解的稠密模型完全不是一个逻辑。
后来我想通了,MoE的稀疏激活本质上是一种参数上的“分工制”。模型里搞了好几个专家网络,每个token只走其中一两个专家,其余专家闲着。训练的时候所有专家都能学到东西,推理的时候只挑相关的用。这就好比一个公司养了几十号人,但每个项目只调动几个对口的,人力成本按项目算,不是按人头算。
不过MoE不是没有代价的。我试过用MoE模型做推理,显存占用一点没省,因为所有专家的参数都得加载进内存,只是计算量省了。通信开销也是个大麻烦,专家分布在不同卡上,token要跨卡路由,节点间带宽一吃紧延迟就上去了。我现在看MoE模型的心态比较务实:参数利用率高了,但工程复杂度也上了一个台阶,部署之前得先算清楚显存、带宽和路由均衡这三笔账,不然容易掉坑里。
3.3 参数效率:共享参数、低秩结构、模块化设计
参数效率这个话题,是我在显存不够用的时候被逼着去研究的。最早听说ALBERT用跨层参数共享把参数量砍下来,我第一反应是“这不会把模型搞傻吗”。后来自己试了试,发现共享参数确实省显存,效果也没有想象中掉得那么狠,只是训练收敛会慢一些。这件事让我意识到,参数之间不是完全独立的,很多层的参数其实在学相近的东西,强行让它们各管各的是一种浪费。
低秩结构是另一条路子。我把它理解成“用窄脖子装大肚子”。与其让一个巨大的矩阵自由生长,不如把它拆成两个小矩阵相乘,参数数量直接降一个量级。LoRA就是这个思路在微调上的应用,不过这里说的是模型结构本身的内置低秩。我做过对比,同样的参数预算下,用低秩分解的模型比直接砍宽度的模型效果好一些,原因是低秩保留了原始权重的方向性,不是简单粗暴地丢信息。
模块化设计我接触得晚一些。它的思路是把模型拆成可复用的组件,一部分参数专门干一类活,需要的时候拼起来用。这种做法对多任务场景特别友好,不用每个任务都训一个完整的模型。我自己还没在工业级项目里大规模用过模块化方案,但从小规模实验看,参数复用的收益是实打实的。这几条路本质上都在回答同一个问题:怎么用更少的参数干同样多的活。答案没有标准解,得看你的任务需要多大的表示容量。
3.4 参数存储与计算:精度、量化、显存与并行策略
参数在硬盘上是什么格式、在显存里是什么精度,这件事我吃过亏之后才真正重视起来。fp16存一个参数占2个字节,fp32占4个,看着不多,乘上几十亿参数量就是几十甚至上百GB的差距。我最开始用fp32加载一个7B模型,一张80G的卡差点没装下,换fp16之后立刻宽松了。精度直接决定显存门槛,这条规律我现在做任何部署方案的第一步都会先算。
量化是我后来学乖了之后开始用的手段。int8把参数从2字节压到1字节,int4再砍一半,显存压力骤降。我实测过int8量化,大部分任务上的精度损失很小,推理速度还有提升,因为显存带宽的瓶颈缓解了。int4就更激进,有些任务上开始掉点,但对话、摘要这类容错空间大的场景完全能用。我现在养成的习惯是:先跑一个int8版本看效果,能接受就不再折腾,不能接受再往回退。
并行策略这块我也踩过不少坑。参数装不下一张卡的时候,就得切开来放。张量并行是把单个矩阵切开放到多卡上,流水线并行是按层切,数据并行是每张卡放一份完整模型。我最早以为并行就是简单的多卡分摊,实际做起来才发现通信开销、负载均衡、卡间带宽每一个都能卡住你。切得越细,通信越频繁,有时候加卡反而变慢了。我现在做并行方案会先看模型结构和集群拓扑,不会闷头堆卡数。
3.5 本章小结:参数结构决定模型容量、效率与可扩展性
这一章写下来,我最大的感受是:参数怎么组织,比参数有多少更能影响实际表现。稠密模型的参数分布不均匀,你得知道哪里是重点;MoE用稀疏激活换参数利用率,但把复杂度推给了工程侧;共享参数、低秩、模块化这些手段都在试图让参数物尽其用;精度、量化、并行策略则决定了这些参数能不能在你的硬件上跑起来。
我现在的选型思路是先把结构看清楚,再谈规模和成本。同样是70B参数,稠密模型和MoE模型的部署难度、推理成本、适用场景完全不一样。参数量只是一个总量标签,结构才决定了这个总量是怎么被消耗的。效率不是事后优化出来的,很多时候是设计参数结构的时候就埋下的伏笔。理解了这一层,再看后面讲微调和参数管理的内容,心里会更有底。
4.1 全参数微调:流程、资源需求与适用场景
第一次想微调大模型的时候,我天真地以为就是把数据喂进去跑几轮的事。结果看到显存需求那一刻,我整个人是懵的。7B的模型做全参数微调,光模型权重、梯度、优化器状态加起来,一张80G的卡都塞不下,还得开混合精度加梯度检查点才勉强能跑。那时候我才明白,全参数微调这三个字,重点不在“微调”,在“全参数”,你要动模型里每一个权重,对应的代价就是训练显存大约要模型参数量的好几倍。
流程本身倒不复杂。准备任务数据,选个基座模型,配置学习率和批次,然后等。麻烦的地方在于等的过程不太可控,我见过全参数微调把模型练崩的,原本能答的问题突然开始胡言乱语,也见过灾难性遗忘,模型学会了新任务,把旧能力忘得一干二净。我现在的做法是宁可把学习率调小一点,多跑几轮,也不贪快,因为全参数微调一旦跑飞,前面烧的算力全白费。
适用场景我的判断比较窄。如果你有充足的算力预算,任务和基座模型的分布差异很大,需要模型在底层表示上做比较大的调整,那全参数微调是有意义的。反过来,如果你的任务只是让模型学会一种输出格式、一个领域的说话风格,或者你手上就几张卡,那真没必要硬上全参,后面讲的PEFT方法能覆盖八成以上的实际需求。我自己现在做项目,除非客户明确要求或者任务确实特殊,否则很少一上来就全参微调。
4.2 参数高效微调(PEFT)总览:冻结策略与适配器思想
PEFT这个思路我第一次接触时觉得挺反直觉的。原模型参数全部冻住不动,只训练一小部分额外的参数,这能学到东西吗?后来想明白了,大模型预训练已经学到了非常丰富的通用表示,你要让它适应某个具体任务,很多时候不需要重塑它,只需要在关键位置轻轻拨一下方向。冻结大部分参数,等于保住了模型的语言能力,把可学习的空间留给任务适配。
适配器的思想我理解成“外挂”加“接口”。你在模型的某些位置插进一个小模块,前向传播的时候数据经过这个模块被调整一下,反向传播只更新这个小模块的参数。这样训练代价下来了,可训练参数量可能只有原来的百分之零点几,显存占用和训练时长都跟着降。我做过对比,同样一张卡跑LoRA,能用的批次和序列长度比全参微调宽松得多。
冻结策略的选择挺有讲究。哪些层冻、哪些层训、适配模块插在注意力后面还是FFN后面,这些细节会影响效果。我的经验是注意力模块和FFN模块都插上比只插一处稳,具体加在哪,得看你的任务偏理解还是偏生成。这件事没有统一答案,但有一点是确定的:PEFT让微调的门槛从“一个机房”降到了“一张消费级显卡”,这个意义比省那点算力大得多。
4.3 LoRA、QLoRA、Adapter、Prefix/Prompt Tuning 对比
这几种方法我基本都试过,体感差异还挺明显的。LoRA是我用得最多的,原理是把权重更新拆成两个低秩矩阵相乘,训练时只更新这两个小矩阵。它的好处是结构简单、推理的时候可以把增量权重合并回原模型,不增加推理延迟。我一般从秩设成8或者16开始试,大部分任务够用了,秩再往上加收益不明显。LoRA最舒服的地方在于它能换着任务训,训完一个存一个增量权重,几十兆的文件,管理起来没压力。
QLoRA是在LoRA基础上把基座模型量化到4bit再训。我头一回用QLoRA在单张24G卡上微调一个13B模型,跑通那一刻是真的省心。它牺牲了一点训练速度,换来的是显存需求大幅下降,让我这种没几张卡的人也能碰大一点的模型。代价是量化后的基座会有点精度损失,训练稳定性比标准LoRA稍微差一点,学习率得调得保守些。
Adapter和Prefix/Prompt Tuning我用得少一些。Adapter是在层间插小网络,效果稳,但推理时多了一段计算,没法像LoRA那样合并回去,延迟会涨一点。Prefix Tuning和Prompt Tuning走的是另一条路,不动模型内部,只在输入前面加一段可学习的向量,参数量极小,训练极快。我试过Prompt Tuning,简单任务上表现不错,任务一复杂就显得力不从心,因为可调容量实在有限。我的排序大致是:通用场景先试LoRA,显存紧张上QLoRA,需要极致轻量再考虑Prompt类方法,Adapter适合对推理延迟不敏感但对效果稳定性要求高的场景。
4.4 微调数据、超参数与评估:学习率、秩、批次与过拟合控制
微调里我最先翻车的不是参数配置,是数据。我最早拿了几百条质量参差不齐的样本就开跑,结果模型学会了一堆奇怪的表达习惯。后来我才明白,微调数据贵精不贵多,几百条干净、格式一致、覆盖任务边界的样本,比几万条噪声数据管用。数据配比也得注意,新任务数据和通用指令数据掺着来,能缓解遗忘问题,我一般会保留一到两成的通用数据做锚定。
超参数里学习率是最敏感的。LoRA的学习率我通常设得比全参微调大一些,因为可训练的参数少,但也不能太激进,我吃过学习率过大导致训练损失先降后崩的亏。秩的选择前面说过,从低往高试。批次大小受显存限制,我一般是能开多大开多大,配合梯度累积凑出有效的批次规模。轮数上我倾向于少跑,两三轮到五轮之间观察验证集,损失还在降但验证指标平了就停,这是防过拟合最实用的一招。
评估这件事容易被忽略。我现在的习惯是准备一个小规模的评估集,覆盖任务的典型场景和边界case,每轮训完跑一遍,看的不只是准确率,还有输出格式是否稳定、有没有出现重复和跑偏。光看训练损失是不够的,训练损失好看但实际用起来翻车的模型我见过太多了。评估集不用大,但得有代表性,这比盲目多训几轮有价值。
4.5 微调方法选择与落地建议:任务、预算与部署约束
选微调方法我现在的顺序很固定。先看任务,再看预算,最后看部署约束。任务是格式对齐、风格迁移这种浅层调整,Prompt Tuning或者小秩LoRA就够。任务涉及领域知识注入、复杂推理行为改变,那就得上大一点的秩,甚至考虑全参微调。我一般不追求一步到位,先用轻量方法跑个基线,效果不够再往上加,这样试错成本最低。
预算这块没什么好绕的,卡就那么多,显存就那么大。一张24G卡能做的事,我不会去规划需要八卡的任务。QLoRA把门槛拉得很低,很多原本做不了的项目现在一张卡就能启动。我踩过的坑是低估了数据准备和评估的时间,总以为训练本身是大头,实际上数据清洗、格式统一、评估设计这些活儿占的时间一点不少。规划预算的时候得把这些算进去。
部署约束经常被放到最后才考虑,我觉得这是个隐患。LoRA训完可以合并进原模型,推理零额外成本;Adapter不行,推理时得多跑一段;Prompt类方法得在输入侧预留位置。如果你的部署环境对延迟敏感,那就别选推理时加结构的方法。我现在的做法是选方法的时候就把部署形态想清楚,训练和推理是一盘棋,分开考虑早晚要返工。
4.6 本章小结:从全参到PEFT的参数更新策略谱系
这一章的核心其实是一条谱系。一端是全参数微调,参数全动,代价最大,能力调整最彻底;另一端是Prompt Tuning这类方法,几乎不碰模型参数,代价极小,能调整的东西也有限。LoRA、QLoRA、Adapter落在中间地带,各有各的取舍。没有哪个方法绝对更好,只有哪个更适合你手上的任务和资源。
我自己走过从迷信全参到拥抱PEFT的过程。早期总觉得不把所有参数都训一遍心里不踏实,后来发现大部分实际问题用LoRA就能解决,省下来的算力和时间可以拿去多试几版数据和超参,收益反而更高。这个转变背后是个认知:微调的本质是让模型适配任务,不是重新训练模型,能小动就别大动。
把这一章和上一章的参数结构串起来看会更清楚。你理解了参数分布在哪、哪些部分承载什么能力,就更容易判断该冻什么、该训什么。微调方法的选择本质上是对参数更新范围的取舍,而这个取舍的答案,藏在你的任务需求、硬件条件和部署形态里。下一章讲参数管理和趋势,会把视角从“怎么改参数”拉到“怎么管参数”,那是另一层工程问题了。
5.1 模型参数选型:根据任务、预算与部署环境确定规模
选模型规模这件事,我踩过的坑比选微调方法还多。早期总觉得参数越大越好,看到70B就想上,结果部署的时候发现推理一张卡根本塞不下,硬上量化之后延迟又高得没法用。后来我学乖了,先拿任务去反推需要多大的模型。做分类、抽取、格式转换这类任务,7B到13B的模型微调之后完全够用,参数量再往上加,收益小到测不出来。真正需要大参数量的场景,是开放式生成、复杂推理、多轮对话里保持长上下文一致性这些活儿,参数规模不够,模型就是转不过弯来。
预算这条线其实挺现实的。训练预算和推理预算是两本账,很多人只看训练花了多少,忽略推理才是长期开销。我现在的习惯是选型的时候先把推理成本算一遍,每天大概多少请求、单次请求的输入输出长度、目标延迟是多少,拿这些反推能接受的参数量上限。一张24G卡能跑什么规模的模型、每秒能出多少token,这些数心里得有。预算紧的时候,小模型加好数据,比大模型加烂数据划算得多。
部署环境是最容易被低估的变量。云端有弹性算力,本地部署就得看物理卡的数量和显存。我给客户做方案的时候,先问的不是任务是什么,而是你手上有什么卡、能不能联网、数据能不能出内网。这些约束一摆出来,选型空间立刻收窄。边缘设备上跑模型,参数量得压到几亿甚至几千万级别,量化到int8之后还得看内存带宽够不够。选型本质上是任务需求、算力预算、部署约束三个圈的交集,三个圈都画出来,能选的模型就那么几个。
5.2 参数压缩与优化:量化、剪枝、蒸馏与参数共享
量化是我在参数压缩里用得最顺手的一招。原理不复杂,把模型权重从FP16压到INT8或者INT4,显存占用直接砍半甚至砍到四分之一。我第一次把13B模型量化到4bit跑在单卡上,推理速度还快了一截,那种感觉挺爽的。量化的坑在于精度损失,有些模型量化之后数学能力和长文本理解明显掉点,得拿评估集测一遍才知道能不能接受。我现在做部署,基本都会准备FP16和INT8两个版本,精度敏感的场景走FP16,吞吐优先的场景走INT8。
剪枝我试过几次,感受比较复杂。非结构化剪枝能把稀疏度做到很高,但实际推理加速不明显,因为稀疏矩阵在通用硬件上跑不出理论收益。结构化剪枝好一些,直接砍掉整个注意力头或者整个FFN通道,模型结构变小了,推理是真快,但剪多了效果崩得也快。我的经验是剪枝适合在训练阶段一起做,或者做轻量剪枝配合微调恢复,训完再硬剪往往得不偿失。
蒸馏这条路线我用得不多,但见过效果很好的案例。拿一个大模型当老师,训一个小模型当学生,小模型学的是老师的输出分布,不只是硬标签。这在任务边界清晰的场景下很管用,学生模型参数量可能只有老师的十分之一,效果能到老师的八九成。代价是你得有个好老师,还得有足够的无标签数据让学生去学。参数共享我在一些老架构里见过,跨层共享注意力参数,参数量降下来了,训练稳定性也好,但表示容量确实受限,现在的模型架构里用得少了。
5.3 参数版本管理、可复现性与合规风险
参数版本管理这件事,我是被坑过才重视起来的。早期做实验,训完一个模型文件往文件夹里一扔,命名全靠感觉,过两周回头看根本分不清哪个是哪个。后来我定了规矩:每个模型版本必须附带训练配置、数据版本、超参数、评估结果,存成一个模型卡片。权重文件算哈希,训练脚本打tag,数据集的清洗规则也得版本化。这套东西做起来麻烦,但项目一多、人一多,没有它就是一团乱麻。
可复现性比想象中难。同样的数据、同样的代码、同样的超参,换台机器跑出来结果可能就不一样,随机种子、并行策略、框架版本、甚至CUDA版本都会影响。我现在的做法是训练环境用容器锁死,依赖版本写死,随机种子固定,能记录的全记录下来。完全复现确实做不到,但做到“可解释的差异”是必要的,出了问题得知道从哪查起。
合规风险这块很多人吃了亏才反应过来。模型权重不是随便能商用的,基座模型的许可证得看清楚,有些只允许研究用途,有些要求衍生模型也得开源。训练数据的来源同样有风险,爬来的数据里混进个人隐私或者侵权内容,训进模型参数里就删不掉了。我现在的习惯是选基座先看许可证,数据来源做记录和过滤,模型上线前过一遍合规清单。这些事做在前面省心,做在后面可能就是法务问题。
5.4 参数高效训练与推理的工程实践
训练侧的工程优化,我感受最深的是梯度检查点。这个技术说白了是拿计算换显存,前向传播的时候不存中间激活,反向传播的时候重算一遍,显存占用能降一大截,代价是训练速度慢个两成左右。显存不够的时候这招几乎是必选项。混合精度训练也是标配,FP16或者BF16跑前向和反向,主权重用FP32维护,速度和显存都能兼顾。分布式训练我碰得少一些,数据并行、张量并行、流水线并行这几个概念得搞清楚,不然模型大一点就不知道怎么切。
推理侧的优化空间比训练侧还大。KV Cache是第一个要理解的,自回归生成的时候把历史key和value缓存下来,避免重复计算,这个技巧几乎所有的推理框架都默认开着。批处理是第二个大头,把多个请求拼成一个batch一起算,GPU利用率能翻好几倍。量化推理前面说过,INT8和INT4的推理速度差异明显。投机解码这个技巧我最近在试,拿一个小模型先猜几个token,大模型一次性验证,猜对了就省时间,对延迟敏感的场景挺有用。
工程实践里最容易被忽略的是监控。模型上线之后,参数量没变,但输入分布会漂,输出质量会慢慢降。我现在的做法是推理服务上挂指标,延迟、吞吐、显存占用、输出长度分布都盯着,再抽一批请求做人工或者自动评估。参数是静态的,用参数的系统是动态的,管理参数不能只管文件,还得管它跑起来之后的状态。
5.5 未来趋势:多模态参数、稀疏化、自适应参数与绿色AI
多模态这块,参数的玩法正在变。以前一个模型一套参数,现在视觉编码器一套、语言模型一套、中间加个投影层做对齐。参数的组织方式从单一稠密结构变成模块化拼装,训练的时候也是分阶段冻结和放开。我关注的一个方向是原生多模态,不是后期拼接,而是预训练阶段就把多种模态的参数混在一起学,这条路参数效率更高,但对数据和算力的要求也更高。
稀疏化我判断是接下来几年的主线之一。MoE架构把总参数量做上去,但每次推理只激活一小部分,有效参数量和计算量脱钩。这个思路在效果和成本之间找到了一个不错的平衡点,现在开源的MoE模型越来越多,部署工具链也在跟上。稀疏化的难点在负载均衡和通信开销,专家路由做不好,GPU利用率上不去,省下来的算力又赔进去了。
自适应参数这个概念我挺感兴趣。模型根据输入难度动态决定用多少层、激活多少参数,简单问题走浅层快速路径,复杂问题走深层路径。这个思路在学术上研究了不少,工程落地还不多,主要是硬件对动态计算的支持不够友好。绿色AI是另一个绕不开的趋势,参数规模上去之后能耗和碳排跟着涨,训练和推理的能效比会变成硬指标。参数效率这个词,以后指的不只是效果除以参数量,还包括效果除以能耗。
5.6 本章小结:以参数为中心构建可落地的大模型方案
这一章讲的事情,本质上是从“改参数”到“管参数”的视角切换。选型是管参数的起点,你得知道任务需要多大的容量、预算允许多大的开销、部署环境能装多大的模型。压缩是管参数的手段,量化、剪枝、蒸馏各有各的适用面,没有一招通吃。版本管理和合规是管参数的底线,模型文件、训练配置、许可证、数据来源,这些东西平时不出事,出事就是大事。
工程实践是把参数真正跑起来的部分。训练侧的显存优化、推理侧的缓存和批处理、上线后的监控,这些细节决定了参数能不能转化成可用的服务。趋势那部分我写得比较开放,多模态、稀疏化、自适应、绿色AI,这些方向都还在快速演化,现在下结论太早,但方向感得有。
把整章串起来看,参数管理的核心是取舍。模型规模、压缩精度、训练方式、部署形态,每一个选择都有代价,也都有对应的收益。我在项目里越来越倾向于小步快跑:先用小模型加轻量方法跑通闭环,再根据实际瓶颈决定往哪个方向加资源。参数是死的,方案是活的,能落地的方案从来不是参数最大的那个,而是参数和任务、预算、环境匹配得最好的那个。