news 2026/10/18 18:33:08

MindSpore迁移学习实战:用预训练模型解决小数据集训练难题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MindSpore迁移学习实战:用预训练模型解决小数据集训练难题

MindSpore开发之路(十九):迁移学习:站在巨人的肩膀上

从零训练一个深度学习模型,尤其是在图像分类、目标检测这种任务上,往往意味着你需要准备数以万计的标注样本,配上几块甚至几十块高端显卡,然后烧掉好几个星期的电费。更现实的问题是,即便你有这些资源,训练出来的模型效果还不一定好——过拟合、收敛慢、调参调到心态爆炸,这些都是家常便饭。而迁移学习的出现,很大程度上就是为了解决这个窘境。

这篇是MindSpore开发之路系列的第十九篇,聊的话题是迁移学习。我会用MindSpore框架来讲清楚迁移学习到底是什么、它解决的核心问题是什么、在MindSpore里怎么落地,以及我踩过的一些坑。如果说你是刚入门深度学习不久,手头数据集又不够大,那这篇文章应该能帮你省下不少走弯路的时间。

1. 为什么要“站在巨人肩膀上”

1.1 从零训练的真实成本

先说一个让人不太舒服的真相:大部分个人开发者或者小团队,根本不具备从零训练一个大模型的条件。拿图像分类里经典的ResNet50来说,在ImageNet(一千多万张图片、两万多个类别)上完整训练一轮,用单张消费级显卡可能需要跑上几天。想要达到论文里的精度,通常需要几十个epoch的迭代,配合精细的学习率调整、数据增强策略,甚至分布式训练。这背后的算力成本、时间成本和人力成本,对绝大多数人来说都是难以承受的。

但这还只是训练成本的问题。更致命的是数据。深度学习模型的效果,很大程度上依赖训练数据的规模和多样性。你自己收集的数据集,哪怕是你非常熟悉的业务场景,也很难做到像ImageNet那样覆盖几百个类别、包含各种光照、角度、背景变化的图像。数据量不够,模型就学不到足够泛化的特征,就会过拟合。你可能遇到过这种情况:训练集上准确率能到95%,一到验证集上就掉到70%,这就是典型的过拟合信号。

迁移学习的思路,说白了就是很朴素的复用思维——别人已经花了大价钱在超大数据集上训练好了一个模型,这个模型已经学会了识别边缘、纹理、形状、物体部件这些通用的视觉特征。你不要把这个模型丢掉,而是把它当成一个“转世重修的武林高手”,你只需要让它适应你的新任务就行。

1.2 迁移学习到底“迁移”了什么

这里有一个容易被忽略的核心认知:神经网络是有“层次分工”的。以卷积神经网络为例,浅层的卷积核学到的是最基础的特征,比如横竖边缘、颜色块、简单纹理;中间层学到的是组合特征,比如眼睛、轮子、窗户这种部件级的模式;到了高层,学到的才是跟具体任务强相关的语义特征,比如“这是一只猫”“这是一辆车”。

这个层次结构意味着一个重要结论:底层特征在很大程度上是通用的。不管是识别猫还是识别飞机,边缘检测、纹理分析这些能力都是共通的。因此,一个在ImageNet上训练好的模型,它的浅层和中层特征提取能力可以直接拿来用,不需要重新学一遍。你需要做的,只是让模型的高层部分适应你的新任务。

这也是为什么迁移学习能大幅降低训练成本。你不是让模型从零开始“牙牙学语”,而是让一个已经“满腹经纶”的模型,快速切换一下工作方向。在MindSpore里做这件事,工具链已经非常成熟了,从预训练模型的加载到参数的冻结与微调,都有对应的接口。但用好这些接口,你需要先理解背后的运作机制。

2. MindSpore中迁移学习的两种主流玩法

2.1 特征提取:把预训练模型当成黑盒工具

迁移学习的第一种常见玩法,叫做“特征提取”,也叫“冻结骨干网络”。做法很直接:把预训练模型除最后的全连接层之外的部分,当作一个固定的特征提取器。你需要做的,就是用它把你自己的图片数据变成一组高维特征向量,然后用这些特征向量去训练一个新的、轻量的分类器,比如一个全连接层或者逻辑回归。

在MindSpore里,具体操作方式是加载预训练模型后,把所有骨干网络部分的参数都设置成requires_grad = False,也就是冻结掉。这样在训练过程中,优化器只更新你新加的那个分类头,骨干网络的权重保持不变。这么做的好处非常明显:你需要训练的参数量骤降,可能从几千万降到几万,训练速度自然快得飞起,所需的数据量也大幅减少,几万张小数据集就是典型的小规模任务规模。

这种方法适合什么场景呢?我个人的经验是:你的新任务和预训练模型的原始任务在数据分布上比较接近,但你的数据量又不够支撑全模型微调的时候。比如说,你在ImageNet上拿到的预训练模型,原始任务本来就是通用的物体分类,现在你要做一个花朵种类的分类,花的图像在视觉特征上跟ImageNet里的植物类别是有交集的,那么特征提取通常就能达到不错的精度。但如果你的新任务跟原始任务差异很大,比如要用图像模型去做医学CT片的分类,那特征提取的效果可能就打折扣了,因为预训练模型学到的自然图像特征,在医学影像上并不完全适用。

2.2 微调:让预训练模型“入乡随俗”

第二种主流玩法是微调(Fine-tuning),这应该是目前实际工程中用得最广泛的迁移学习方式。微调的做法是,加载预训练模型后,不冻结骨干参数,而是让整个网络一起参与训练。你可以选择把所有层的学习率设置成一样,也可以用分层学习率策略,让靠近输入层的网络参数变化慢一点、靠近输出层的参数变化快一点。

相比特征提取,微调能让预训练模型更好地适应你的新数据集。因为在新任务上,模型不仅是在学习“怎么分类”,更是在根据你的数据特性对底层的特征提取方式进行小幅修正。这种修正能带来更高的精度上限。我用一个直观的数字来说明:在同一个中等规模的花卉分类数据集上,我只用特征提取,最好成绩能到88%的准确率;换成像下面代码里那种全量微调,同样的训练轮次和数据增强,准确率能到93%左右。这个差距在真实业务里往往是非常关键的。

特征提取与微调的区别对比:

对比维度特征提取微调
训练参数量只训练分类头,几万级全部参数,几千万级
训练速度非常快,几分钟到十几分钟相对慢,几十分钟到小时级
所需数据量较少,千张级可起步较多,建议万张级起步
内存/显存占用低,反向传播只经过分类头高,反向传播要经过骨干网络
精度上限中等,取决于特征通用性更高,模型能自适应新数据
过拟合风险低,因为可训练参数少中等偏高,需要正则手段配合

3. 实操篇:基于MindSpore完成一次完整的迁移学习

3.1 环境准备与数据集说明

在开始写代码之前,先说一下我用的环境。MindSpore 2.2版本,Python 3.9,显卡是单张消费级的,显存8GB。数据集用的是某公开的花卉分类数据集子集,总共五千张图片,分十个类别,每个类别大概五百张。这是典型的“小数据”场景,非常适合演示迁移学习。

数据准备好了之后,MindSpore读图有一套自己的数据流水线机制。我们先把图片读进来,然后做resize、归一化、随机裁剪、水平翻转等处理。数据增强不只是锦上添花,在迁移学习里它是防止过拟合的关键手段之一——因为你数据量本来就小,不做增强,模型很快就记住训练集了。

import mindspore as ms import mindspore.dataset as ds import mindspore.dataset.transforms as C import mindspore.dataset.vision as vision from mindspore import nn # 数据集路径配置 data_path = "/path/to/flower_dataset/" train_path = data_path + "train" val_path = data_path + "val" # 定义数据增强与归一化参数 IMAGE_SIZE = 224 MEAN = [0.485, 0.456, 0.406] STD = [0.229, 0.224, 0.225] # MindSpore的数据集对象:训练集 def create_dataset(data_dir, batch_size=32, training=True): dataset = ds.ImageFolderDataset(data_dir, recursive=True, num_parallel_workers=4) # 定义增强算子列表 transform_list = [] if training: # 训练时做随机裁剪和水平翻转,增强泛化能力 transform_list.extend([ vision.RandomCropDecodeResize(IMAGE_SIZE, scale=(0.8, 1.0)), vision.RandomHorizontalFlip(prob=0.5), ]) else: # 验证时只做缩放和中心裁剪,保持结果稳定 transform_list.extend([ vision.Decode(), vision.Resize(256), vision.CenterCrop(IMAGE_SIZE), ]) transform_list.extend([ vision.Normalize(mean=MEAN, std=STD), vision.HWC2CHW(), # MindSpore期望CHW的输入顺序 ]) # 类型转换 type_cast_op = C.TypeCast(ms.int32) dataset = dataset.map(operations=transform_list, input_columns="image", num_parallel_workers=4) dataset = dataset.map(operations=type_cast_op, input_columns="label", num_parallel_workers=4) if training: dataset = dataset.shuffle(buffer_size=1000) dataset = dataset.batch(batch_size, drop_remainder=training) return dataset train_dataset = create_dataset(train_path, batch_size=32, training=True) val_dataset = create_dataset(val_path, batch_size=32, training=False)

这里有一个操作细节要提醒你:MindSpore的ImageFolderDataset是直接按文件夹结构来解析类别标签的,每个子文件夹的名字对应一个类别。这个设计省去了你手动写标签映射的功夫,但要求你的数据目录结构必须规范,不然训练跑起来才发现类别顺序乱了,那就很被动了。

3.2 加载预训练模型并替换分类头

MindSpore的model_zoo里提供了丰富的预训练模型,ResNet系列、MobileNet系列、ViT系列都有。这里我用ResNet50作为演示,因为它是一个经典的“通用特征提取器”,结构适中,精度和速度的平衡比较好,非常适合迁移学习场景。

关键的一步是替换分类头。预训练模型是在ImageNet上训练的,最后一层全连接输出是1000个类别。而我们自己的数据集只有10个类别,所以需要把这个全连接层换掉,换成输出维度为10的新层。替换的时候要注意:新层的权重是随机初始化的,而骨干网络的权重是预训练好的,这两部分在训练初始阶段的行为会有显著差异。

from mindspore import load_checkpoint, load_param_into_net from mindvision.classification.models import resnet50 # 加载预训练模型结构 backbone = resnet50(pretrained=True) # 获取骨干网络的输出特征维度 in_features = backbone.classifier.in_channels # 替换分类头为适合自己任务的新的全连接层 backbone.classifier = nn.Dense(in_channels=in_features, out_channels=10) # 打印模型结构,确认分类头已替换 print(backbone)

在MindSpore里,pretrained=True会自动从模型仓库下载对应的权重文件到本地缓存目录。如果你网络状况不好,也可以手动下载权重文件,然后用load_checkpoint和load_param_into_net加载。这个机制对科研和工程都很友好,因为很多时候你在内网环境没法直接访问外网,手动下载然后拷贝到本地是常见操作。

3.3 冻结参数与优化器配置

接下来是选择迁移学习的策略。为了让你同时看到两种策略的效果,我这里先演示特征提取:把骨干网络全部冻结,只训练新加的分类头。在MindSpore里冻结参数非常直接——把参数的requires_grad设为False即可。

# 冻结骨干网络参数 for param in backbone.features.get_parameters(): param.requires_grad = False # 设置网络为训练模式 backbone.set_train(True) # 定义损失函数和优化器 loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction="mean") optimizer = nn.SGD(params=backbone.classifier.trainable_params(), learning_rate=0.01, momentum=0.9) # 封装模型 model = ms.Model(backbone, loss_fn=loss_fn, optimizer=optimizer, metrics={"Accuracy": nn.Accuracy()})

这里有个细节值得说明:optimizer传入的params参数我用了backbone.classifier.trainable_params(),而不是全部参数。这就是冻结的意义所在——只有分类头的参数参与梯度更新,骨干网络的权重全程保持不变。因为这个分类头本身的参数量很小,所以训练过程会非常快。我在单张8GB显存的卡上跑这个配置,每个epoch不到两分钟,十个epoch就能基本收敛。

如果你的数据量稍微大一点,而且你想追求更高的精度,那就可以把冻结放开,让骨干网络也参与训练。这里强烈建议使用nn.LearningRateSchedule来设置分层学习率,或者至少用一个较小的基础学习率。直接沿用0.01的学习率去微调整个骨干网络,很容易把预训练权重“冲”坏,因为底层的特征本来就很好,大学习率反而会破坏它们。

3.4 训练过程的监控与策略

在模型训练的过程中,很多人喜欢干一件事:训练脚本一跑起来就盯着loss曲线看。但说实话,在迁移学习场景下,我认为更应该关注的是验证集准确率的变化,而不是训练loss的绝对数值。因为迁移学习一开始的时候,骨干网络的权重已经是一个合理状态,训练loss往往在一开始就比较低,但真正重要的是新分类头能不能在新数据集上区分出类别。

为了能及时看到训练效果,我习惯在每次epoch结束时做一次验证评估,并且动态调整学习率。MindSpore里实现这个逻辑,可以通过model.train的callbacks参数传入自定义回调。这里提一个我常用的经验:前3个epoch可以加入“预热”阶段,学习率从很小值线性增加到目标值,之后再按余弦退火衰减。预热阶段对微调特别重要,因为新分类头是随机初始化的,梯度信号在一开始的几个batch里可能非常不稳定,一上来就用大学习率很容易震荡。

import math from mindspore.train.callback import Callback class CosineWarmupLR(Callback): def __init__(self, base_lr, warmup_epochs, total_epochs, steps_per_epoch): self.base_lr = base_lr self.warmup_epochs = warmup_epochs self.total_epochs = total_epochs self.steps_per_epoch = steps_per_epoch self.current_step = 0 def step_begin(self, run_context): cb_params = run_context.original_args() cur_epoch = cb_params.cur_epoch_num cur_step = cb_params.cur_step_num # 简化示例:按epoch线性预热,之后余弦衰减 if cur_epoch <= self.warmup_epochs: lr = self.base_lr * (cur_epoch / self.warmup_epochs) else: ratio = (cur_epoch - self.warmup_epochs) / (self.total_epochs - self.warmup_epochs) lr = self.base_lr * 0.5 * (1 + math.cos(math.pi * ratio)) cb_params.optimizer.learning_rate = lr

这段代码不是MindSpore库里的内置回调,而是我按照自己项目需求封装的一个简化版本。如果你怕麻烦,可以直接用MindSpore内置的cosine_decay_lr配合warmup参数,效果是等价的。我的建议是:如果你是第一次做迁移学习,别在花哨的学习率策略上浪费太多时间。先固定一个小的学习率(0.001左右)跑通流程,把模型精度和数据集匹配的问题解决了,再回来调学习率曲线。

3.5 模型评估与导出

训练结束后,除了看验证集的Accuracy,我还建议你多看一下每个类别的精确率和召回率。因为花卉分类这种任务各类别之间样本数可能不太均衡,总体Accuracy高,不代表稀有类别也被照顾到了。MindSpore的metrics模块里提供了Precision和Recall,可以单独传入model.eval()来获取。

# 自定义评估指标 from mindspore import Tensor # 使用准确率评估模型在验证集上的表现 eval_metrics = {"Accuracy": nn.Accuracy()} acc = model.eval(val_dataset, dataset_sink_mode=True) print("Validation Accuracy:", acc["Accuracy"]) # 模型导出为MindIR格式,后续可部署到推理环境 import numpy as np input_tensor = Tensor(np.ones([1, 3, 224, 224]), ms.float32) model.predict(input_tensor) ms.export(backbone, input_tensor, file_name="flower_resnet50", file_format="MINDIR") print("Model exported to MindIR format.")

导出这个环节,看起来是流水账操作,但实际工作中非常重要。你训练出来的模型最终是要拿去部署的,不管是在云端推理还是边缘设备上跑,都需要一个固定的推理格式。MindSpore的MindIR格式就是为了这个目的设计的,跨平台、可优化,后续也可以转成其他格式做硬件适配。我导出之后,会把文件放到一个单独的模型仓库目录里,用日期和精度命名,方便后面版本回滚。

4. 更进一步:微调的艺术与细节

4.1 到底该冻结多少层

做迁移学习的时候,一个很常见的纠结是:到底应该冻结多少层,还是全部放开?网上各种说法都有,有人说冻结前几个block效果好,有人说全量微调上限高,还有人说只微调最后一两个block。这种分歧让很多初学者无所适从。

我的观点是——没有绝对正确的答案,但有一个可靠的判断依据:你的新数据集跟预训练模型的原始训练集之间的“距离”。如果新数据集和ImageNet在图像风格上高度相似,比如你做的也是日常物体的分类,那么底层特征几乎完全可以直接复用,只训练分类头,甚至只微调最后一到两个block就好。如果新数据集有明显的domain gap,比如是卫星遥感图、医学影像这种跟自然图像差异较大的图像,那底层特征虽然也有用,但不够精准,你需要让更多层参与微调,甚至全量微调。

实操中的折中方法是:不搞非黑即白,设一个分层学习率。具体来说,靠近输入的前几个block,学习率设为0.0001这样很小的值,中间层设为0.0005,靠近分类层的新增层设成0.001。这样一来,底层特征缓慢调整,不至于被破坏,而分类头则以相对较快的速度适应新任务。MindSpore里实现分层学习率,可以利用nn.Parameter的requires_grad配合自定义优化器分组参数来实现。

# 设置不同的层组 params = [ # 骨干网络低层:学习率小 {"params": backbone.features[:4].get_parameters(), "lr": 0.0001}, # 骨干网络高层:学习率中等 {"params": backbone.features[4:].get_parameters(), "lr": 0.0005}, # 新分类头:学习率较大 {"params": backbone.classifier.get_parameters(), "lr": 0.001}, ] optimizer = nn.Adam(params, weight_decay=1e-4)

注意,用这个优化器之前,你得先把这些层里的参数requires_grad设为True,否则优化器收集不到参数。如果你前面用了冻结的代码,这里就要把冻结效果解除。我刚开始接触这个的时候,就犯过这种低级错误——前面冻结了,后面想全量微调,结果发现骨干网络一直没更新,精度卡住不涨。

4.2 数据增强的加减法

迁移学习通常搭配的是小数据集,而小数据集的宿敌就是过拟合。数据增强是应对过拟合最有效的手段之一,但增强不是越多越好,这个“度”得把握好。

在MindSpore的vision模块里,常用的增强算子有RandomCropDecodeResize、RandomHorizontalFlip、RandomColorAdjust、RandomRotation等。我的建议是,在小数据集(几千张)上,至少加上随机裁剪和水平翻转。如果你的图片本身存在方向性很强的特点,比如某些工业检测图不允许翻转,那水平翻转就要慎用。颜色抖动这种增强,对于分类任务通常是有帮助的,但幅度别太大,否则会让模型学到“变色”这种错误的不变性。

另外还有一个扩充数据的土办法:用预训练模型的特征图做数据混合。比如拿同一个类别的图片做mixup、cutout,这类方法在图像分类里已经被大量验证能提升泛化性能。MindSpore的MixupDataset接口可以直接对接,不需要自己实现。我建议在小数据集场景下,把mixup概率设为0.3到0.5之间比较合适,太高会导致训练过程过于困难、收敛变慢。

4.3 早停与模型保存

训练过程中,我强烈建议开启早停机制。早停的意义在于:当模型在验证集上的表现不再提升时,及时停止训练,避免后续的过拟合。

MindSpore里实现早停需要写一个简单的Callback,在每个epoch结束后检查验证集准确率,如果连续N个epoch没有提升,就终止训练。这里N通常取5到10。同时,把每个epoch结束后的最佳模型权重单独保存一份。一个实用的经验:保存两个模型,一个是最佳验证准确率的模型,一个是最后一个epoch的模型,因为有时候最后阶段的模型虽然验证准确率略低,但泛化性反而更好,这个现象在做小数据集时尤其明显。

class EarlyStopCallback(Callback): def __init__(self, patience=5, save_path="./best_model.ckpt"): self.patience = patience self.save_path = save_path self.best_acc = 0.0 self.wait = 0 def epoch_end(self, run_context): cb_params = run_context.original_args() cur_epoch = cb_params.cur_epoch_num train_metrics = cb_params.train_network # 在实际使用中,可以在epoch结束后调用eval获取验证准确率 cur_acc = self.evaluate_validation() if cur_acc > self.best_acc: self.best_acc = cur_acc self.wait = 0 # 保存最佳模型 ms.save_checkpoint(cb_params.train_network, self.save_path) else: self.wait += 1 if self.wait >= self.patience: run_context.request_stop()

这个代码片段是一个简化示意,实际中你需要在epoch_end里执行验证集的model.eval,并把准确率存成成员变量。注意别忘了设置run_context.request_stop()的调用方式,这是MindSpore回调机制里提前终止训练的正确API。

5. 常见问题与排查技巧实录

5.1 Loss不下降或者震荡

现象:训练开始后,Loss降不下去,或者忽高忽低地振荡。

原因与排查:这是迁移学习里最容易碰到的问题。我自己早期做微调的时候就翻过车——当时学习率设置成了0.01,骨干网络全部放开,Loss在2.3左右原地踏步,准确率跟随机猜差不多。后来把学习率降到0.0001,问题立刻缓解。所以碰到这个问题,第一个要怀疑的就是学习率过大。另外检查一下数据预处理是否跟预训练模型的归一化要求一致。ImageNet预训练模型用的mean和std是固定的几个值,如果你自己图省事没有对数据做同样的归一化,那输入分布对不上,模型的表现就会非常差。

5.2 验证集准确率远低于训练集

现象:训练准确率已经到98%,验证集准确率却只有80%。

原因与排查:这是典型的过拟合。处理思路三条:一是加强数据增强,把随机擦除、颜色抖动加进去;二是增加正则化,在优化器里加上weight_decay,MindSpore的Adam优化器默认支持这个参数;三是减少模型容量,如果预训练模型太大比如ResNet152,而你的数据集只有不到五千张,那模型很容易记住训练集,换成ResNet50或者MobileNetV2会好很多。

5.3 显存不足

现象:训练跑了一两步,报错CUDA out of memory。

原因与排查:这个问题常见于全量微调,因为反向传播要保存中间激活值,显存消耗远超特征提取模式。我的建议是:先看batch_size,调小一半试试;如果还不行,就检查输入图片尺寸,太大的图会显著增加显存占用。MindSpore支持mixed_precision混合精度训练,开启后显存占用能明显下降,不过要确认你的显卡支持。另外,使用dataset_sink_mode=True让数据下沉到设备端,也能减少宿主与设备之间的数据拷贝开销,间接缓解显存压力。

5.4 预训练权重加载不匹配

现象:加载预训练模型时,MindSpore报错,提示Parametername不匹配。

原因与排查:大概率是因为你修改了模型结构,比如替换了分类头,导致预训练权重里最后一层参数的shape跟当前模型对不上。这种情况属于预期内,MindSpore的加载逻辑比较严格,会自动忽略不匹配的参数,同时打印warning。你要做的只是确认:除了你特意修改的分类头,其他层的参数都正确加载了。如果你的改动更复杂,比如改了骨干网络的中间层,那加载就会失败,需要用load_param_into_net配合strict=False参数,手动检查哪些层没加载成功。

5.5 训练速度异常慢

现象:设备没换,数据量也没变大,但训练速度比预期慢不少。

原因与排查:如果你发现数据加载成为瓶颈,优化一下数据流水线:调大num_parallel_workers,使用mindspore.dataset提供的repeat和batch的合理组合。另外别忘了,MindSpore里model.train的dataset_sink_mode参数默认是True,如果设成了False,数据每次通过Host到Device之间的拷贝传输,速度会有明显下降。我在第一次用MindSpore的时候犯过这个错,跑了半天总觉得不对劲,最后发现是sink mode没开。

常见问题速查表:

问题可能原因检查方向解决办法
Loss不降学习率过大打印loss曲线降低学习率到0.0001量级
验证精度低过拟合对比训练/验证精度增强数据增强、加weight_decay
显存不足batch_size过大观察显存占用调小batch,开启混合精度
权重加载报错模型结构不匹配核对层名称与shape只替换分类头,保持骨干不变
训练慢数据成为瓶颈观察CPU负载调大并行线程数,开启sink mode

5.6 一个独家避坑心得:别迷信预训练模型

关于迁移学习有很多幻觉,其中一个就是“用了预训练模型就万事大吉”。但我在实际项目中吃过亏之后才明白,预训练模型到底适不适合你的任务,需要亲自验证。某个细分领域的数据,用ImageNet预训练的ResNet,和用一张都没训练过的随机权重ResNet去对比,有时候差距并没有想象中那么大,尤其是在你的数据集本身质量很高、分布很独特的情况下。

这种情况下,你反而应该考虑“从更强的大模型蒸馏”或者“用你自己的数据集做一个更基础的预训练阶段”,再去做迁移学习。这听起来麻烦,但我试过的确有效果。当然,对于绝大多数普通任务,直接用公开的预训练模型仍然是最优解,只是你要清楚它并不是万能的。

6. 写在后面的一些个人体会

迁移学习是我在实际开发中使用频率最高的一项技术,几乎没有之一。它让我这样一个没有庞大算力资源、也没有海量数据标注团队的普通开发者,也能做出效果尚可的深度学习应用。它的核心价值,不只是省时间省算力,更是让你能把精力聚焦在真正重要的部分——你自己的数据、你自己的业务逻辑,而不是从零开始去调教一个什么都学不会的模型。

如果你现在手头正有一个小数据集分类任务,我的建议是:先别急着自己搭模型,先想想能不能找到合适的预训练模型,跑一遍特征提取作为baseline,然后再决定要不要做更大规模的全量微调。把这两个baseline的数据记下来,你会发现它们能给你接下来的模型优化提供非常明确的指导方向。

最后分享一个小技巧:在做迁移学习时,除了关注最后的准确率,也别忽略训练过程中的“曲线形状”。一个healthy的训练曲线,在微调开始后的第二个epoch左右,验证集准确率就会有一个明显的跃升。如果没有出现这个跃升,你大概率是在哪个环节出了问题——预训练权重没加载成功、标签顺序对不上,或者数据预处理有bug。这个经验在我多次实验中都很灵验,希望能帮你少走点弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 18:32:39

MoltBot + Bocha:为开源大模型快速接入联网搜索增强

在本地把开源模型跑起来之后&#xff0c;很多人都会遇到同一个问题&#xff1a;模型会答&#xff0c;但不会“查”。它不知道今天天气如何&#xff0c;不知道最新新闻&#xff0c;更不知道你问的那款产品现在卖多少钱。原因很简单——模型的知识有截止日期&#xff0c;而搜索是…

作者头像 李华
网站建设 2026/10/18 18:32:28

Google ADK实战:一文吃透Agent Skill五大设计模式

做 Agent 开发一年&#xff0c;我终于把 Skill 设计模式这件事想明白了如果你最近也在玩 Google ADK&#xff0c;大概和我有同感&#xff1a;官方文档里把 Skill 讲得很轻巧&#xff0c;仿佛只是“给模型写一段说明、配两个函数”。但真到自己上手做项目&#xff0c;你会发现同…

作者头像 李华
网站建设 2026/10/18 18:29:42

链表高频面试题:双指针一次遍历求倒数第k个节点

1. 从一道高频面试题说起“面试题.返回倒数第k个节点”这行标题&#xff0c;过去几年我见过太多次了——不管是校招还是社招&#xff0c;不管是后端岗还是客户端岗&#xff0c;这道题出现的频率高得离谱。它考察的知识点不算难&#xff0c;却能把一个人的基本功底子摸得清清楚楚…

作者头像 李华
网站建设 2026/10/18 18:29:25

Claude Code 知识工作插件实战:slash command 设计与定制

1. 从"knowledge-work-plugins"这个仓库名说起第一次看到knowledge-work-plugins这个名字&#xff0c;我脑子里冒出来的第一个念头是&#xff1a;这大概率不是一个普通意义上的代码库&#xff0c;而是一套"给知识工作者用的能力扩展包"。事实也确实如此——…

作者头像 李华
网站建设 2026/10/18 18:27:40

Agent Skills 重构测试流程:从人肉写用例到技能驱动

1. 测试流程的老问题&#xff1a;为什么"人肉写用例"越来越跑不动了做测试这行的&#xff0c;大概都经历过这样的场景&#xff1a;需求评审刚结束&#xff0c;开发还在画架构图&#xff0c;测试负责人就已经在催"用例什么时候能出"。然后你打开文档&#x…

作者头像 李华
网站建设 2026/10/18 18:23:34

知识图谱+图神经网络:电影推荐系统从冷启动到可解释

简介&#xff1a;面向计算机专业毕业设计的电影推荐系统源码包&#xff0c;融合知识图谱与图神经网络&#xff08;KGCN&#xff09;技术&#xff0c;利用电影、用户、实体之间的关系建模&#xff0c;可完成从原始数据预处理、知识图谱构建、模型训练评估到Web端推荐展示的完整流…

作者头像 李华