利用 Resnet50 微调图像分类模型,完成宠物数据集的识别,附源代码。。

这篇文章中,我们从头训练了一个简单的图像识别模型,但是实际上,从头训练一个模型需要太多的时间成本了。

而且从头开始训练,需要你的数据集足够优秀,才可以训练出一个效果不错的模型。

那么,如果我想总有一个适合自己数据集的模型,除了从头开始训练之外,还有没有更好的方法呢?

当然有,这就是"微调"(Fine-tuning)。

这篇文章,就以 Resnet50 模型为例子,使用"Oxford-IIIT Pet Dataset" 这个包含了 37 类宠物的样本作为数据集,来详细介绍下微调的过程。

本文最后最后附有完整的微调代码。

Attention:全网最全的 AI 小白到 AI 大神的天梯成长学习路线,几十万原创专栏和硬核视频,点击这里查看:AI小白到AI大神的天梯之路

先看下什么是微调?

微调其实很简单,就是用一个已经预训练过的模型(预训练模型),稍微调整一下模型中某些层的权值,使其可以在新的任务上表现的更好。

ResNet 本身是一个深度卷积神经网络模型,这个模型在 ImageNet 数据集(包含 1000 种物品)上已经训练过了。

所以,我们可以认为,这个模型已经学会了图像识别的基础技能,比如怎么识别图像的边缘、颜色和形状等。

如果我们想用它来进一步识别宠物,其实不用从零开始训练,在这个预训练模型的基础上进行微调,才是一个更好地选择。

微调的原理:只改模型的关键点

如果你深入了解过卷积神经网络这一类的模型原理,你可能会知道,对 ResNet50 而言,前面的几层卷积,也就是浅层卷积,负责识别图片的基本特征信息。

比如"这条线是什么"、"这块颜色像什么"。

而越往后的卷积,识别到的图像特征越抽象。

比如最后一层全连接,其实负责的是把所有的特征信息综合起来,判断"这是哪种物体分类"了。

所以,有些时候最后的全连接层,也可以认为是起到分类器的作用。

对于卷积神经网络而言,微调的基本基本原理就是:

首先,冻结模型的浅层参数。

因为靠前的卷积层已经学会了如何识别图像的基础特征,这个功能很重要,也对所有的图片都适用。

所以我们在微调的时候就不改它们,这些参数冻结不动。

然后,改深层卷积的权值。

比如全连接层,它原来认识 1000 种物体,现在要认识 37 种宠物。

因此需要修改这一层的参数,使其输出37个分类。

最后,用新的数据集稍加训练。

接下来,我们将新的数据集(比如本例子中的宠物样本)输入给调整后的模型。

让它在原有识图能力的基础上,更专注识别新数据集的任务。

这样,一次微调任务就完成了。

接下来,我们就分几步把微调的过程用代码实现。

步骤1:准备数据

python 复制代码
transform = transforms.Compose([    
    transforms.Resize(256), # 把图片缩到256x256    
    transforms.CenterCrop(224),# 剪成224x224        
    transforms.ToTensor(),# 转成数字格式        
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 调整颜色值])   

train_dataset = datasets.OxfordIIITPet(root="./data", split="trainval", download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)

ResNet50 的输入只能是 224x224 的图片,因此需要对输入图片进行裁剪和缩放。

同时还需要调整图片的数据分布,这样才能和该模型在预训练时的数据分布参数匹配,达到更好地识图效果。

步骤2:加载现成模型

plain 复制代码
model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)

我们直接用 ResNet50 这个训练好的模型,权重选择:

ResNet50_Weights.IMAGENET1K_V1。

这个参数说明使用的是在 imagenet 数据集上完成训练的权重。

步骤3:冻结浅层

python 复制代码
for name, param in model.named_parameters():        
    if "layer4" not in name and "fc" not in name:        
        param.requires_grad = False  # 冻结这些层的参数

模型的前面几层卷积识别特征的通用能力很强。

这里,我们冻结这些层,只让最后一层卷积(layer4)和全连接层(fc)的参数可以变化。

步骤4:修改分类数目

plain 复制代码
model.fc = nn.Linear(model.fc.in_features, 37)

ResNet50 原来输出1000种分类,我们需要将其改成 37 种,以此来匹配宠物数据集的分类。

步骤5:用新数据训练

python 复制代码
optimizer = optim.Adam([    
    {"params": model.fc.parameters(), "lr": 1e-3}, # 最后一层学快点        
    {"params": model.layer4.parameters(), "lr": 1e-5}  # 前一层学慢点])    

for epoch in range(1, 16):    
    train(epoch)  # 训练    
    test(epoch)   # 测试

训练过程中,layer4 设置较小的学习率,最后一层设置较大的学习率。

经过 15 轮的微调训练后,模型就可以很好的识别出测试集里的37种宠物,准确率可以达到到80%-90%。

所以,比起从零开始训练,微调用时短,效果也不错。

事实上,模型微调的核心其实就三点:

第一点,找一个现成的预训练模型。

第二点,冻结预训练模型的基础部分,改动关键部分。

第三点,用新数据稍微训练一下。

如果你想让 AI 完成其他的物体分类,比如花、车或者其他图片,微调确实是个简单又好用的办法。

大家可以试一试。

本文完整代码在下面的链接:

github.com/dongdongcan...

我创建了一个《小而精的AI学习圈》知识星球,星球上有几十万字原创高质量的技术专栏分享,同时你也可以在星球向我提问。 点击这里,我们星球见! >>>
点击这里查看所有 AI 技术专栏

相关推荐
飞奔的马里奥12 分钟前
力扣Hot100——136. 只出现一次的数字
算法·leetcode·职场和发展
FAREWELL0007528 分钟前
Leetcode做题记录----3
算法·leetcode·职场和发展
limbo012635 分钟前
2025-3-17算法打卡
数据结构·算法·leetcode
白云千载尽1 小时前
LMDrive大语言模型加持的自动驾驶闭环系统 原理与复现过程记录
人工智能·经验分享·python·算法·机器学习·语言模型·自动驾驶
CoovallyAIHub1 小时前
99.22%准确率!EfficientNet优化算法实现猪肉新鲜度无损快检
深度学习·算法·计算机视觉
仟濹1 小时前
【递归与动态规划(DP) C/C++】(1)递归 与 动态规划(DP)
算法·动态规划
tpoog2 小时前
[贪心算法]-最大数(lambda 表达式的补充)
算法·贪心算法
橘颂TA2 小时前
【C++】树和二叉树的实现(上)
数据结构·算法·二叉树·
maisui121382 小时前
牛客周赛 Round 85
算法·深度优先·codeforces
Ronin-Lotus2 小时前
嵌入式硬件篇---龙芯GPIO控制
嵌入式硬件·算法·c·gpio·龙芯·2k1000