MXNET深度学习框架-27-微调(Fine-tuning)-迁移学习

        众所周知,深度学习最重要的一个特性就是所需数据量大,一旦数据量比较小,而采用深度学习去训练的话,最终的结果只有一个:过拟合!但自己的数据集能达到上万的量吗?估计很少吧,就算有,也可能是斥巨资收集的吧。所以,为了应对这种情况,迁移学习(Transfer Learning)就被提出来了。它的主要思想是将从源数据集学到的知识迁移到目标数据集上

                                在这里插入图片描述
        上图中,源数据是一个比较大的数据集如ImageNet,目标数据就是自己的数据集,除了最后一次需要初始化之外,把其它层的参数全部挪过来,这样就完成了迁移学习。

下面使用mxnet实现如何进行迁移学习(本文实现的是kaggle竞赛的猫狗分类大战):
相关图片如下:
在这里插入图片描述
在这里插入图片描述

1) 读取预训练模型

from mxnet.gluon import  model_zoo
pretrained_net = model_zoo.vision.resnet18_v2(pretrained=True)# 读取预训练模型,会自动下载
print(pretrained_net)

看看ResNet18_v2的结构:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
        通常预训练好的模型包含两个模块:Features和Output,前者包含模型除输出层以外的所有层,后者包括最后一层全连接。

print("最后一层:",pretrained_net.output)

结果:
在这里插入图片描述
该模型文件里面不仅有结构,还包含了参数,比如,第一个卷积层的参数:

print("第一个卷积层的参数:",pretrained_net.features[1].params.get("weights").data()[0][0])

结果:
在这里插入图片描述
2)创建模型
        在微调里,我们一般创建一个全新的模型,它跟之前训练好的模型一样,只是最后的分类数目不一样而已,并且最后一层需要重新初始化。微调代码如下:

# 微调
fine_tune_model=model_zoo.vision.resnet18_v2(classes=2) # 分类数为2
# 前面的(参数、结构)完全复制
fine_tune_model.features=pretrained_net.features
# 最后一层随机初始化
fine_tune_model.output.initialize(init=init.Xavier())

通过这种方式我们就已经创建好了模型。
3) 图像预处理

# 指定RGB三个通道的均值和方差来将图像通道归一化
normalize = gn.data.vision.transforms.Normalize(
    [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])

train_augs = gn.data.vision.transforms.Compose([
    gn.data.vision.transforms.Resize(224),
    # gn.data.vision.transforms.RandomFlipLeftRight(),
    gn.data.vision.transforms.ToTensor(),
    normalize])

test_augs = gn.data.vision.transforms.Compose([
    gn.data.vision.transforms.Resize(224),
    # gn.data.vision.transforms.CenterCrop(224),
    gn.data.vision.transforms.ToTensor(),
    normalize])

4)加载图像
        我们创建两个ImageFolderDataset实例来分别读取训练数据集和测试数据集中的所有图像文件。训练集(测试集)文件夹下包含两个子文件夹,分别为"cat"和"dog"文件夹,大致如下:
在这里插入图片描述

batch_size=32
train_imgs = gn.data.vision.ImageFolderDataset("F:/dog and cat/train")
test_imgs = gn.data.vision.ImageFolderDataset("F:/dog and cat/test")
train_iter = gn.data.DataLoader(dataset=train_imgs.transform_first(train_augs),batch_size=batch_size, shuffle=True)
test_iter = gn.data.DataLoader(dataset=test_imgs.transform_first(test_augs),batch_size=batch_size, shuffle=False)

接下来就是常规化操作了,loss定义、准确率定义以及模型训练均与前几章一样,接下来放上所有代码:

from mxnet.gluon import  model_zoo
import mxnet as mx
import mxnet.ndarray as nd
import mxnet.autograd as ag
import mxnet.gluon as gn
import mxnet.initializer as init
import zipfile
import matplotlib.pyplot as plt
import cv2 as cv
from mxnet.gluon import utils as gutils

pretrained_net = model_zoo.vision.resnet18_v2(pretrained=True)
print(pretrained_net)
print("最后一层:",pretrained_net.output)
print("第一个卷积层的参数:",pretrained_net.features[1].params.get("weight").data()[0][0])

# 微调
fine_tune_model=model_zoo.vision.resnet18_v2(classes=2) # 分类数为2
# 前面的(参数、结构)完全复制
# fine_tune_model.initialize(init=init.Xavier()) #从头开始训练
fine_tune_model.features=pretrained_net.features
# # 最后一层随机初始化
fine_tune_model.output.initialize(init=init.Xavier())

'''---数据读取---'''
batch_size=32
# 指定RGB三个通道的均值和方差来将图像通道归一化
normalize = gn.data.vision.transforms.Normalize(
    [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])

train_augs = gn.data.vision.transforms.Compose([
    gn.data.vision.transforms.Resize(224),
    # gn.data.vision.transforms.RandomFlipLeftRight(),
    gn.data.vision.transforms.ToTensor(),
    normalize])

test_augs = gn.data.vision.transforms.Compose([
    gn.data.vision.transforms.Resize(224),
    # gn.data.vision.transforms.CenterCrop(224),
    gn.data.vision.transforms.ToTensor(),
    normalize])

train_imgs = gn.data.vision.ImageFolderDataset("F:/dog and cat/train")
test_imgs = gn.data.vision.ImageFolderDataset("F:/dog and cat/test")
train_iter = gn.data.DataLoader(dataset=train_imgs.transform_first(train_augs),batch_size=batch_size, shuffle=True)
test_iter = gn.data.DataLoader(dataset=test_imgs.transform_first(test_augs),batch_size=batch_size, shuffle=False)

ctx=mx.gpu()
fine_tune_model.collect_params().reset_ctx(ctx=ctx) # 因为要用GPU训练,而微调的模型被读取在CPU上
fine_tune_model.hybridize()

fine_tune_model.output.collect_params().setattr('lr_mult', 10)
# softmax和交叉熵分开的话数值可能会不稳定
cross_loss=gn.loss.SoftmaxCrossEntropyLoss()

# 定义准确率
def accuracy(output,label):
    return nd.mean(output.argmax(axis=1)==label).asscalar()

def evaluate_accuracy(data_iter,net):# 定义测试集准确率
    acc=0
    for data,label in data_iter:
        data, label = data.as_in_context(ctx), label.as_in_context(ctx)
        label=label.astype("float32")
        output=net(data)
        acc+=accuracy(output,label)
    return acc/len(data_iter)


# 优化

train_step=gn.Trainer(fine_tune_model.collect_params(),'sgd'
                      ,{"learning_rate":0.002}) # 如果从头开始训练,lr需要大一点

# 训练

epochs=20
for epoch in range(epochs):
    n=0
    train_loss=0
    train_acc=0
    for image,y in train_iter:
        image, y = image.as_in_context(ctx), y.as_in_context(ctx)
        y = y.astype("float32")
        with ag.record():
            output = fine_tune_model(image)
            loss = cross_loss(output, y)
        loss.backward()
        train_step.step(batch_size)
        train_loss += nd.mean(loss).asscalar()
        train_acc += accuracy(output, y)
    nd.waitall()
    test_acc = evaluate_accuracy(test_iter, fine_tune_model)
    print("Epoch %d, Loss:%f, Train acc:%f, Test acc:%f"
          %(epoch,train_loss/len(train_iter),train_acc/len(train_iter),test_acc))

训练结果:
在这里插入图片描述
        可以看到,模型准确率只跑了一个epoch就达到了98%,已经非常高了。这结果怕是在kaggle竞赛上也能拿到前5的名次了,放上现在kaggle之猫狗大战的的比赛名次(排名来源于kaggle网站:https://www.kaggle.com/c/dogs-vs-cats/leaderboard
截图至2020.5.1):
在这里插入图片描述

为了对比迁移学习的优势,我们从头初始化所有参数,重新开始训练:

fine_tune_model.initialize(init=init.Xavier()) #从头开始训练

结果:
在这里插入图片描述
        可以看到,虽然准确率有在增加,但是很慢很慢,所以微调(Fine-tuning)确实是有效的加快深度学习训练、提升准确率的方法。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值