利用 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 技术专栏

相关推荐
weixin_5275504022 分钟前
初级程序员入门指南
javascript·python·算法
嘉陵妹妹2 小时前
深度优先算法学习
学习·算法·深度优先
GalaxyPokemon3 小时前
LeetCode - 53. 最大子数组和
算法·leetcode·职场和发展
hn小菜鸡3 小时前
LeetCode 1356.根据数字二进制下1的数目排序
数据结构·算法·leetcode
zhuiQiuMX3 小时前
分享今天做的力扣SQL题
sql·算法·leetcode
music&movie5 小时前
算法工程师认知水平要求总结
人工智能·算法
laocui15 小时前
Σ∆ 数字滤波
人工智能·算法
yzx9910136 小时前
Linux 系统中的算法技巧与性能优化
linux·算法·性能优化
全栈凯哥6 小时前
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
java·算法·leetcode·链表
全栈凯哥6 小时前
Java详解LeetCode 热题 100(27):LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)详解
java·算法·leetcode·链表