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

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

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

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

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

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

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

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

先看下什么是微调?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

步骤1:准备数据

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:加载现成模型

model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)

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

ResNet50_Weights.IMAGENET1K_V1。

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

步骤3:冻结浅层

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

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

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

步骤4:修改分类数目

model.fc = nn.Linear(model.fc.in_features, 37)

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

步骤5:用新数据训练

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 完成其他的物体分类,比如花、车或者其他图片,微调确实是个简单又好用的办法。

大家可以试一试。

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

https://github.com/dongdongcan/ai_model_samples/blob/main/resnet50_fine_tune_oxford_iiit_pet/oxford_iiit_pet_fine_tune.py

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董董灿是个攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值