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實例來分別讀取訓練數據集和測試數據集中的所有圖像文件。

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)確實是有效的加快深度學習訓練、提升準確率的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章