想象你是一位餐厅老板,从国宴厨房请来一位做过五十万道菜的大厨老周。他刀工精准、火候老练,见过上千种食材——这就是“预训练模型”:它已经在 ImageNet 这样的百万级图像数据集上学会了识别边缘、纹理、形状等通用视觉能力。但你的餐厅主打“城市观鸟图谱”——从客人随手拍的照片里认出三十种本地鸟类,相当于一道地方特色菜。老周不会做?没关系,他不需要从零学起,只需在原有手艺上做针对性调整:保留刀工火候,精修调味摆盘。这个过程,就是深度学习里的“微调(Fine-tuning)”。 如果你从零开始训练一个模型,好比把学徒从颠勺练起,少则数月、多则数年,还需要海量食材(数据)喂养。而微调让模型“站在巨人的肩膀上”:起点高、爬得快、终点还更好。本文将从微调的基本概念出发,带你理解它的原理、关键策略、优化技巧、评估方法、工程流程与常见陷阱,让这条“老周进店”的路线图完整呈现在你面前。
掌握微调能让你用少量数据、有限算力快速交付高质量的图像分类模型,这是 AI 工程落地中最常用、也最吃功底的一项核心技能。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
微调基础概念
老周进店第一天,老板小王递给他一本“观鸟图鉴”和三百张本地鸟类照片。老周翻了翻,笑了:“我十五年练的是刀工火候,认识三十种鸟还不简单?只要在基本功上加点本地知识就行。”老周的底气,正来自他“预训练”过的深厚功底——他在国宴厨房见过上百万种食材,早已把通用视觉特征刻进了肌肉记忆。
这里的“国宴厨房”对应 ImageNet 这样的大规模公开数据集。在它上面完成训练的模型叫作预训练模型,它已经学会了图像分类的通用能力。把这份成品带到具体任务(识别三十种鸟)上继续训练,就是迁移学习;其中最主流的手段,就是微调(Fine-tuning)。与全量训练(从随机参数白手起家)不同,微调的起点不是零,而是预训练权重。
微调的操作分三步:加载预训练网络,把末端的分类层替换成新任务的类别数(三十种鸟就是 30 个输出);用新数据对网络做小步幅训练;在独立的验证集上检验效果。它的训练目标和普通分类训练一致——最小化损失函数,唯一的区别写在初始条件里:
对小王来说,老周最大的价值不是会做国宴菜,而是十五年基本功让“学会三十种鸟”只需几周试炼。微调正是模型的“老周”——起点高、爬得快、终点也往往更好。
- 样本与标签$(x_i, y_i)$ 是新任务数据集中第 i 张图像和它的类别标签,共 N 个样本参与训练。
- 网络前向$f(x_i;\theta)$ 表示参数为 $\theta$ 的分类网络对第 i 张图给出的预测结果。
- 平均损失$\frac{1}{N}\sum_{i=1}^{N}\mathcal{L}(\cdot)$ 把所有样本的损失累加后取平均,衡量整体预测误差。
- 最小化目标$\arg\min_{\theta}$ 表示寻找让平均损失最小的参数组合。
- 初始参数$\theta_0 = \theta_{\text{pre}}$ 说明优化从预训练权重出发,而非随机初始化。
微调核心原理
老周做菜讲究“工序分层练”:刀工练切丝切片,火候练油温控制,摆盘练视觉美感。这些能力一层层地存着,互不干扰。深度卷积网络的结构恰好与之对应:底层卷积核学习边缘、颜色、纹理等高度通用的特征——相当于刀工;中层学会翅膀、眼睛、羽毛等部件——相当于备料;高层则表征“这是麻雀还是喜鹊”这类语义概念——相当于最终的调味摆盘。
预训练的价值,在于把这条“能力阶梯”从头到尾都打磨过一遍。微调时,网络从这套已成型的分层特征出发,用新数据把每一层略微校正,让高层语义更贴合本地的三十种鸟,而底层通用特征基本原样保留。这就是知识继承:通用能力留下,专用能力补充。
梯度反传是这套“校正”的传导机制。前向传播逐层计算特征,反向传播把损失对参数的梯度逐层回传,每层参数按梯度方向做小幅修正:
老周换餐厅不重练刀工,只需依本地食材微调几道工序。微调同理:底层特征近乎冻结,高层特征快速适配——这正是分层特征带来的红利。
- 上一层输出$h^{(l-1)}$ 是第 $l-1$ 层输出的特征图,也是第 $l$ 层的输入。
- 权重矩阵$W^{(l)}$ 是第 $l$ 层的可学习权重,微调时它会被梯度小幅更新。
- 偏置向量$b^{(l)}$ 是第 $l$ 层的偏置项,作用是对线性输出做平移。
- 线性变换与激活先计算 $W^{(l)}h^{(l-1)} + b^{(l)}$,再通过非线性激活函数 $\sigma$(如 ReLU)加工,得到本层输出。
关键策略选择
请来老周只是第一步。小王接下来要做三个决定:请哪位师傅、让师傅改多少、先改哪道工序。这三个决定,对应微调中的骨干网络选择、冻结策略和学习率设置。
选师傅就是选骨干网络:ResNet、EfficientNet、ViT 各有擅场,参数量越大“厨艺越精”,但也越吃数据。改多少是冻结策略:若只训练新换的分类头,骨干完全不动,叫特征提取;若所有层都参与更新,叫全量微调。数据越少、任务与预训练域越接近,越倾向多冻结。先改哪道工序则是微调范围:浅层通用、深层专用,通常让浅层小步挪、深层大步走。
学习率是这一切的“步幅总闸”。预训练权重已落在损失曲面的良好区域,步子太大会一脚踢翻老本,因此微调学习率通常是从头训练的十分之一。更精细的做法是层间递减,让靠近输入的层更新得更慢:
给老周定规矩的道理,与给网络定学习率如出一辙:基本功不许大改,风味可以大胆调。选好师傅、控好步幅,微调就成功了一半。
- 基础学习率$\eta_{\text{base}}$ 是整体学习率基准,微调时设为从头训练的 $1/10$ 左右。
- 衰减系数$q = 0.9$ 表示每向输入方向走一层,学习率乘以 $0.9$。
- 层位置指数$L-l$ 中 $L$ 是总层数,$l$ 是当前层号,差值越大说明离输入端越近。
- 整体乘积两者相乘得到该层实际学习率,越浅越小,从而保护通用特征。
训练优化技巧
试菜两周后,小王发现一个怪现象:老周把三张练习照片背得滚瓜烂熟,客人换个光线角度,他就认不出鸟了——好比大厨只会做反复练过的那几道菜,换个灶台就露怯。这正是过拟合的苗头。要治它,得靠“加配料”和“控火候”两套功夫。
加配料是数据增强:把每张图随机裁剪、翻转、调亮度、改颜色,相当于同一种菜换着花样练。它逼着模型学到“翻转后的鸟还是鸟”这类不变性,而不是死记像素。控火候是学习率调度:训练初期小步慢热(warmup),中后期逐步降温(cosine/step decay),让损失稳稳滑向谷底。此外,权重衰减惩罚过大的参数,克制模型“炫技式”的复杂拟合;早停则见好就收——验证集不再提升就立刻停火。
这些技巧共同服务于一个目标:让模型在训练集上不偷懒、在测试集上不翻车。而模型学得好不好,最终都要靠损失函数这把“标尺”来量。图像分类的标尺是交叉熵损失,它衡量预测概率分布与真实标签的差距:
数据增强让老周见多识广,学习率调度让他收放自如,交叉熵这把“尺子”则时刻告诉他每道菜离标准还差多远。四者配合,招牌菜才经得起真实客人考验。
- 类别总数$K$ 是分类任务的类别数,比如三十种鸟就是 $K=30$。
- 真实标签$y_k$ 是第 $k$ 类的真实指示(one-hot 编码),真实类别取 1,其余取 0。
- Softmax 概率$p_k$ 将原始分数 $z_k$ 归一化为概率,所有类别概率之和为 1。
- 负对数似然$-\log p_k$ 对真实类别概率取负对数,概率越接近 1,损失越接近 0。
性能评估调优
老周到底出师没有,不能只看他复刻练习菜有多像,要看客人真实点单的满意度。深度学习同理:训练集上的损失下降,不代表模型真学会了——泛化能力才是关键。
收敛监控看的是训练曲线:训练损失与验证损失同步下降,说明学得健康;训练损失继续降而验证损失掉头回升,就是经典的过拟合信号,模型开始在“背菜单”。泛化评估则在独立测试集上算准召率,分品类看哪些鸟容易认错,再针对性补数据或调策略。效果对比也很重要:把微调模型和随机初始化训练、纯特征提取放在同一条跑道上比准确率,才能量化微调的“增益”到底有多大。
若发现过拟合,常见的“刹车”动作包括:加强数据增强、加大权重衰减、冻结更多浅层;若欠拟合(验证损失居高不下),则反向操作,解冻更多层并调大学习率。权重衰减这一类正则化手段,可以统一写成在损失函数上增加惩罚项:
评估不是为了给老周打分,而是为了决定下一轮怎么教。用验证集反馈指导训练决策,微调才能真正“越调越准”。
- 数据损失$\mathcal{L}_{\text{CE}}$ 是前文定义的交叉熵,衡量预测与真实标签的匹配度。
- 正则强度$\lambda$ 控制惩罚力度,越大模型越“保守”,通常取 $10^{-4}$ 量级。
- 权重平方和$\sum_{l}\|W^{(l)}\|_F^2$ 是对各层权重矩阵的 Frobenius 范数平方求和,权重越大惩罚越重。
- 总损失两部分的加权和,优化器同时让预测更准、权重更克制。
工程实践流程
把微调放进真实项目,流程与开餐厅惊人地一致:备料 → 选师傅 → 试菜迭代 → 正式开店。每一步都有讲究,跳过任何一环都可能让前面的努力白费。
备料(数据准备):采图、清洗、标注类别,按 8:1:1 划分训练/验证/测试集,检查类别均衡度。鸟种 A 有八千张、鸟种 B 只有三十张,模型必然偏心——这时需要过采样或类别加权。选师傅(模型选型):小数据优先轻量骨干(如 ResNet18、MobileNet),数据量大且充足时可上大模型全量微调。试菜迭代(训练迭代):记录每次实验的学习率、冻结范围、增强策略和指标,建立实验表格,用验证集驱动下一轮调整。正式开店(部署上线):把最优模型导出、量化压缩,接入推理服务,并持续监控线上数据分布变化。
整个流程的“验收尺”最终落到准确率上,它的定义朴素却必须严格:
从三百张鸟图到上线试运行,微调项目的成功从来不是“跑一个脚本”那么简单,而是备料、选型、迭代、部署四道工序环环相扣的结果。
- 样本总数$M$ 是评估集的样本数量,如一千张标注好的测试鸟图。
- 指示函数$\mathbb{I}[\hat{y}_i = y_i]$ 当预测类别与真实类别相等时取 1,否则取 0。
- 正确计数$\sum_{i=1}^{M}\mathbb{I}[\cdot]$ 累加所有正确预测,得到正确样本总数。
- 百分比换算除以 $M$ 再乘 $100\%$,得到 0 到 100 之间的准确率。
问题风险规避
三个月后,一位老顾客点老周的拿手菜松鼠鱼,老周竟连片鱼都手生——他这三个月只顾着练新菜,把看家本领忘了个干净。这在微调里有一个专有名词:灾难性遗忘(Catastrophic Forgetting)。
灾难性遗忘的根源是学习率过大或微调过久,把预训练学到的通用特征强行覆盖。对策是严格控制学习率、监控旧能力指标,必要时对预训练权重施加“记忆保护”。域差异则是另一个坑:预训练用的是影棚级美图,你的真实数据是游客随手拍的模糊照片,分布对不上,迁移效果就打折。对策是尽量收集目标域数据,或做风格迁移式增强来“拉近两个域”。数据不足时,新类别只有几张样本,容易过拟合,应多冻结层、强增强、用小网络。调参陷阱则藏在细节里:类别不平衡、批大小忽大忽小、验证集泄露,都会让实验结论失真。
如何量化“老周忘了多少”?可以在微调前后分别测一遍旧任务(比如预训练域)的准确率,做差即得遗忘程度:
好师傅既要学得进新菜,也不能丢了本门功夫。盯住遗忘量、守住学习率上限、补足目标域数据,才能做到“新菜上手、老菜不丢”。
- 微调前旧能力$A_{\text{old}}^{\text{pre}}$ 是加载预训练权重后、尚未微调时在旧任务上的准确率。
- 微调后旧能力$A_{\text{old}}^{\text{ft}}$ 是微调结束后再测旧任务得到的准确率。
- 遗忘量两者相减,得到预训练知识被覆盖的程度。
AI 应用场景
医疗影像分类:肺炎 X 光片筛查
阶段:模型训练与调优
解决的问题:医院影像数据通常只有几千张甚至几百张,标注成本极高,从零训练深度网络必然严重过拟合。微调让 ImageNet 上学到的纹理、边缘特征迁移到肺野结构识别上,用少量数据也能训练出可用模型。
具体案例:某三甲医院放射科用 ImageNet 预训练的 ResNet 微调检测胸片是否患肺炎:只解冻高层特征,配合随机旋转、翻转增强,将验证集 AUC 从 0.78 提升到 0.93,两周内完成了从数据标注到科室试用。这就像请来老周,只让他学“看胸片”这一道菜,刀工火候全是现成的。
工业质检:手机外壳划痕分类
阶段:模型部署与迭代
解决的问题:产线缺陷样本稀少且缺陷形态随工艺漂移,无法预先收集全部情况。微调支持“共享基础视觉特征 + 快速适配新产线”,一条新产线的模型几天即可更新。
具体案例:某代工厂使用 MobileNet 预训练权重微调外壳划痕/压伤二分类任务,冻结骨干、只训练新分类头,单张 GPU 训练两小时即达到 98.5% 检出率,随后量化为 INT8 部署到产线边缘盒子。老周只需学“认划痕”一个动作,成本低、见效快。
移动端植物识别 App
阶段:数据准备与模型选型
解决的问题:用户拍摄照片光照杂乱、拍摄角度千奇百怪,创业团队没有算力从零训练,必须借助轻量骨干微调在手机端落地。
具体案例:某创业团队用 EfficientNet-Lite 微调识别三百种观赏植物,数据增强模拟不同光照与背景,最终模型仅 15MB,Top-5 准确率达 91%。这正是“轻量师傅 + 小步调整”的组合拳。
Python 演示
import torchimport torch.nn as nnimport torch.optim as optimimport torchvisionimport torchvision.transforms as transforms def main(): transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)), ]) transform_test = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)), ]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test) testloader = torch.utils.data.DataLoader(testset, batch_size=256, shuffle=False, num_workers=2) model = torchvision.models.resnet18(weights=torchvision.models.ResNet18_Weights.IMAGENET1K_V1) model.fc = nn.Linear(512, 10) for param in model.parameters(): param.requires_grad = False for param in model.fc.parameters(): param.requires_grad = True optimizer = optim.SGD(model.fc.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4) criterion = nn.CrossEntropyLoss() scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device) for epoch in range(20): model.train() total_loss, correct, total = 0.0, 0, 0 for images, labels in trainloader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() total_loss += loss.item() * images.size(0) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() scheduler.step() train_acc = 100.0 * correct / total model.eval() test_correct, test_total = 0, 0 with torch.no_grad(): for images, labels in testloader: images, labels = images.to(device), labels.to(device) outputs = model(images) _, predicted = torch.max(outputs, 1) test_total += labels.size(0) test_correct += (predicted == labels).sum().item() print('epoch %d loss %.3f train_acc %.2f%% test_acc %.2f%%' % ( epoch + 1, total_loss / total, train_acc, 100.0 * test_correct / test_total)) if __name__ == '__main__': main()
model = torchvision.models.resnet18(weights=torchvision.models.ResNet18_Weights.IMAGENET1K_V1)请来“老周”:加载在 ImageNet 上预训练好的 ResNet18 权重。model.fc = nn.Linear(512, 10)换掉分类层:原分类头输出 1000 类,替换成 CIFAR-10 的 10 类。param.requires_grad = False冻结骨干:让预训练学到的通用“刀工火候”不再被改动。param.requires_grad = True解冻分类头:只让新学的“摆盘调味”参与更新。optimizer = optim.SGD(model.fc.parameters(), lr=0.01, ...)优化器只接收分类头参数,学习率 0.01,相比从头训练常用的 0.1 小一个量级。scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)每 10 轮学习率减半,让火候随训练逐步收小。loss.backward()反向传播:把分类损失对参数的梯度逐层回传。model.eval()` 与 `torch.no_grad()评估模式:不更新 BN 统计量、不记录梯度,公平测量泛化能力。
常见误区
误区我以为微调就是把预训练模型拿来再正常训练一遍,和从零开始训练没什么本质区别,反正都是跑 for 循环更新参数,最后看准确率就行。
正解区别在“起点”。从头训练从随机参数出发,需要海量数据和漫长迭代才能学到可用特征;微调从预训练权重出发,起点已是大规模数据学到的良好区域,只需小步修正。好比老周十五年练就的刀工,与学徒从零开始颠勺,起点完全不同。
为何常见:教程里展示的都是“训练循环”代码,初学者只盯着 for epoch,却忽略了权重初始化这个决定性的细节。
误区预训练模型那么强,把骨干全部冻结、只训练最后的分类层,又保险又省事,效果肯定最好。
正解冻结越多,模型对新任务的适应能力越弱。预训练特征虽通用,但你的任务可能有独特语义结构。目标域与预训练域差异不大且数据少时,“只训分类头”确实明智;差异大时则应解冻高层甚至全量微调。好比老周去一家川菜馆,连调味手法都得重新学,只让他学摆盘肯定不行。
为何常见:把“省计算”误当成“效果好”,忽略了特征适配的必要性。
误区预训练模型已经很强了,微调时把学习率调大点,应该能更快收敛,反正它起点高,不容易学坏。
正解恰恰相反。预训练权重已落在损失曲面的良好区域,用从头训练那样的大学习率(如 0.1)一步就可能把学好的特征“踢”坏,造成灾难性遗忘。微调学习率通常设为从头训练的 1/10 甚至更小,让参数在原有解附近小幅修正。
为何常见:混淆了“模型能力强”和“参数稳定”两回事,忽略了参数空间中大步幅的破坏性。
误区数据增强无非是把图翻一翻、裁一裁,让训练集数量变大,数量够了模型自然就收敛了。
正解增强的本质是制造输入扰动,是一种正则化手段。它告诉模型“翻转后的鸟还是鸟”,迫使模型学到对旋转、光照、裁剪不变的语义特征,而不是死记像素。它提升的是泛化能力,而非单纯堆体积。
为何常见:看到增强后数据集“变大”,容易从量的角度理解,忽略了不变性约束这个质的作用。
误区只要用了预训练权重微调,不管数据怎样、任务怎样,最终准确率一定超过从头训练。
正解多数时候微调占优,但不是定理。当新任务数据量很大、且分布与预训练域差异极大时,微调可能受限于预训练特征空间,反而不如从头训练灵活;小数据集上微调同样会过拟合。业界实践以实验为准:微调和从头训练各跑一遍,用验证集对比。
为何常见:把“通常更优”的统计结论误读为“必然更优”的因果结论。
面试题
Q1. 微调(Fine-tuning)的初始参数从哪里来?
- A.随机初始化生成
- B.预训练模型的权重← 正确答案
- C.全零矩阵初始化
- D.手工设计的特征
答案:B。微调的核心特征就是“站在预训练肩膀上”,初始参数来自预训练模型权重而非随机值。随机初始化属于从头训练,全零或手工特征都不是深度学习的常规做法。
Q2. 图像分类的预训练模型通常先在哪个数据集上训练?
- A.CIFAR-10 小图集
- B.ImageNet 百万图集← 正确答案
- C.MNIST 手写数字集
- D.COCO 场景理解集
答案:B。ImageNet 是图像分类领域最经典的百万级预训练数据集,ResNet 等骨干网络的标准预训练权重都在其上训练。CIFAR-10 和 MNIST 规模小、常作为下游任务数据,COCO 则侧重目标检测。
Q3. 微调时,新分类头的输出维度由什么决定?
- A.骨干网络的宽度
- B.目标任务的类别数← 正确答案
- C.预训练的类别数
- D.输入图像的尺寸
答案:B。分类头输出维度必须等于新任务的类别数,比如识别三十种鸟就输出 30。骨干宽度决定特征通道数而不决定类别数,预训练类别数与新任务无关。
Q4. 关于微调的学习率,下列哪种说法正确?
- A.设置成与从头训练相同
- B.约从头训练的十分之一← 正确答案
- C.越大越好收敛越快
- D.训练途中从不调整
答案:B。预训练权重已在损失曲面良好区域,大学习率会破坏旧特征,因此微调学习率通常取从头训练的 1/10 左右,并在训练中配合调度策略逐步衰减。
Q5. 冻结骨干网络、只训练新的分类头,属于哪种策略?
- A.全量参数微调策略
- B.特征提取迁移策略← 正确答案
- C.随机初始化训练策略
- D.知识蒸馏压缩策略
答案:B。骨干完全冻结、只更新分类头,是把预训练网络当作固定特征提取器,即特征提取策略。全量微调会更新所有层,知识蒸馏则涉及教师-学生网络。
Q6. 微调中“灾难性遗忘”的本质是什么?
- A.旧训练数据被全部删除
- B.预训练知识被过度覆盖← 正确答案
- C.新数据样本严重不足
- D.模型参数量设置太小
答案:B。学习率过大或微调过久,梯度更新大幅改写预训练权重,把通用特征“覆盖”掉,导致模型在新任务上进步、在旧能力上退步。这与数据删除、样本不足、参数量无关。
Q7. 目标域与预训练域差异很大时,较合适的做法是?
- A.骨干网络完全冻结
- B.解冻高层并调低学习率← 正确答案
- C.全部随机初始化重训
- D.主力依赖数据增强
答案:B。域差异大时完全冻结会阻碍特征适配,随机重训又浪费预训练价值。折中方案是解冻高层让语义特征适配新域,同时调低学习率保护浅层通用特征。
Q8. 相比从头训练,微调带来的主要优势有哪些?
- A.训练收敛速度更快← 正确答案
- B.小数据下泛化更好← 正确答案
- C.必然获得更高精度
- D.显著降低训练成本← 正确答案
答案:ABD。微调从良好起点出发,收敛更快、小数据下泛化更稳、需要的数据与算力也更少。“必然更高精度”过于绝对——当新数据很大且域差异极大时从头训练可能反超。
Q9. 下列哪些做法可以有效缓解微调中的过拟合?
- A.合理使用数据增强← 正确答案
- B.添加权重衰减正则← 正确答案
- C.大幅提高学习速率
- D.验证集不升就早停← 正确答案
答案:ABD。数据增强制造多样性、权重衰减压制过大参数、早停避免过度拟合,三者都是标准防过拟合手段。大幅提高学习率反而会破坏预训练特征,加剧不稳定。
Q10. 关于微调策略,下列说法正确的是?
- A.数据稀缺时多冻结一些层← 正确答案
- B.底层浅层学习率应更小← 正确答案
- C.全量微调一定优于特征提取
- D.监控验证损失及时早停← 正确答案
答案:ABD。数据少时冻结更多层能防止过拟合;浅层特征通用,学习率应更小以保护它们;早停由验证集驱动。全量微调并非总优于特征提取,需结合数据量与域差异实验验证。