亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

PyTorch 遷移學(xué)習(xí)實(shí)戰(zhàn)

 更新時(shí)間:2023年01月15日 16:01:30   作者:_23_  
本文主要介紹了PyTorch 遷移學(xué)習(xí)實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1. 實(shí)驗(yàn)環(huán)境

  • Jupyter Notebook
  • Python 3.7
  • PyTorch 1.4.0

2. 實(shí)驗(yàn)?zāi)康?/h2>

遷移學(xué)習(xí),讓機(jī)器擁有能夠“舉一反三”的能力。
本次實(shí)驗(yàn)就以“是螞蟻還是蜜蜂”為例,探索如何將已訓(xùn)練好的大網(wǎng)絡(luò)遷移到小數(shù)據(jù)集上,并經(jīng)過少量數(shù)據(jù)集的訓(xùn)練就讓它獲得非常出眾的效果。

3. 相關(guān)原理

使用 PyTorch 的數(shù)據(jù)集套件從本地加載數(shù)據(jù)的方法
遷移訓(xùn)練好的大型神經(jīng)網(wǎng)絡(luò)模型到自己模型中的方法
遷移學(xué)習(xí)與普通深度學(xué)習(xí)方法的效果區(qū)別
兩種遷移學(xué)習(xí)方法的區(qū)別

4. 實(shí)驗(yàn)步驟

# 下載實(shí)驗(yàn)所需數(shù)據(jù)并解壓
!wget http://labfile.oss.aliyuncs.com/courses/1073/transfer-data.zip
!unzip transfer-data.zip

4.1 數(shù)據(jù)收集

實(shí)驗(yàn)中的數(shù)據(jù)是已經(jīng)準(zhǔn)備好的,訓(xùn)練數(shù)據(jù)集在 ./data/train 中,校驗(yàn)數(shù)據(jù)集在 ./data/val 中。(推薦直接到藍(lán)橋云課上進(jìn)行實(shí)驗(yàn))。如果使用自己的環(huán)境只需要自己準(zhǔn)備相關(guān)圖片數(shù)據(jù),并將代碼中的路徑改成你自己的數(shù)據(jù)集路徑。

#引入實(shí)驗(yàn)所需要的包
import torch
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
import torch.nn.functional as F
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import copy
import os

4.1.1加載數(shù)據(jù)

使用 datasets 的 ImageFolder 方法就可以實(shí)現(xiàn)自動(dòng)加載數(shù)據(jù),因?yàn)閿?shù)據(jù)集中的數(shù)據(jù)可能分別在不同的文件夾中,要讓所有的數(shù)據(jù)一起加載。

# 數(shù)據(jù)存儲(chǔ)總路徑
data_dir = 'transfer-data'
# 圖像的大小為224*224
image_size = 224
# 從data_dir/train加載文件
# 加載的過程將會(huì)對(duì)圖像自動(dòng)作如下的圖像增強(qiáng)操作:
# 1. 隨機(jī)從原始圖像中切下來一塊224*224大小的區(qū)域
# 2. 隨機(jī)水平翻轉(zhuǎn)圖像
# 3. 將圖像的色彩數(shù)值標(biāo)準(zhǔn)化
train_dataset = datasets.ImageFolder(os.path.join(data_dir, 'train'),
                                    transforms.Compose([
                                        transforms.RandomResizedCrop(image_size),
                                        transforms.RandomHorizontalFlip(),
                                        transforms.ToTensor(),
                                        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
                                    ])
                                    )

# 加載校驗(yàn)數(shù)據(jù)集,對(duì)每個(gè)加載的數(shù)據(jù)進(jìn)行如下處理:
# 1. 放大到256*256像素
# 2. 從中心區(qū)域切割下224*224大小的圖像區(qū)域
# 3. 將圖像的色彩數(shù)值標(biāo)準(zhǔn)化
val_dataset = datasets.ImageFolder(os.path.join(data_dir, 'val'),
                                    transforms.Compose([
                                        transforms.Resize(256),
                                        transforms.CenterCrop(image_size),
                                        transforms.ToTensor(),
                                        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
                                    ])
                                    )

下面要為每個(gè)數(shù)據(jù)集創(chuàng)建數(shù)據(jù)加載器。

# 創(chuàng)建相應(yīng)的數(shù)據(jù)加載器
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size = 4, shuffle = True, num_workers=4)
val_loader = torch.utils.data.DataLoader(val_dataset, batch_size = 4, shuffle = True, num_workers=4)

# 讀取得出數(shù)據(jù)中的分類類別數(shù)
# 如果只有蜜蜂和螞蟻,那么是2
num_classes = len(train_dataset.classes)
num_classes

輸出:2

4.1.2 GPU運(yùn)算

第一次了解GPU運(yùn)算是在第一篇博客PyTorch,簡(jiǎn)單的了解了一下。

深度學(xué)習(xí)可以通過 GPU 并行運(yùn)算加速模型的訓(xùn)練。
PyTorch 是支持使用 GPU 并行運(yùn)算的。但是能不能使用 GPU 加速運(yùn)算還取決于硬件,支持 GPU 的硬件(顯卡)一般是比較昂貴的。
如果你想讓自己的程序能夠自動(dòng)識(shí)別 GPU 計(jì)算環(huán)境,并且在 GPU 不具備的情況下也能自動(dòng)使用 CPU 正常運(yùn)行,可以這么做:
這三個(gè)變量,之后會(huì)用來靈活判斷是否需要采用 GPU 運(yùn)算。

# 檢測(cè)本機(jī)器是否安裝GPU,將檢測(cè)結(jié)果記錄在布爾變量use_cuda中
use_cuda = torch.cuda.is_available()

# 當(dāng)可用GPU的時(shí)候,將新建立的張量自動(dòng)加載到GPU中
dtype = torch.cuda.FloatTensor if use_cuda else torch.FloatTensor
itype = torch.cuda.LongTensor if use_cuda else torch.LongTensor

4.2 數(shù)據(jù)預(yù)處理

該函數(shù)作用:將數(shù)據(jù)集中的某張圖片打印出來。

def imshow(inp, title=None):
    # 將一張圖打印顯示出來,inp為一個(gè)張量,title為顯示在圖像上的文字

    # 一般的張量格式為:channels * image_width * image_height
    # 而一般的圖像為 image_width * image_height * channels 
    # 所以,需要將張量中的 channels 轉(zhuǎn)換到最后一個(gè)維度
    inp = inp.numpy().transpose((1, 2, 0)) 

    #由于在讀入圖像的時(shí)候所有圖像的色彩都標(biāo)準(zhǔn)化了,因此我們需要先調(diào)回去
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1) 

    #將圖像繪制出來
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.pause(0.001)  # 暫停一會(huì)是為了能夠?qū)D像顯示出來。

將訓(xùn)練數(shù)據(jù)集的第一個(gè) batch 繪制出來:

#獲取第一個(gè)圖像batch和標(biāo)簽
images, labels = next(iter(train_loader))

# 將這個(gè)batch中的圖像制成表格繪制出來
out = torchvision.utils.make_grid(images)

imshow(out, title=[train_dataset.classes[x] for x in labels])

4.3 創(chuàng)建模型

該實(shí)驗(yàn)先訓(xùn)練一個(gè)普通的卷積神經(jīng)網(wǎng)絡(luò),但正確率勉強(qiáng)達(dá)到50%上下。模型預(yù)測(cè)的效果很差。因?yàn)樵搶?shí)驗(yàn)選用的是螞蟻和蜜蜂的圖像數(shù)據(jù),本身就很難識(shí)別,簡(jiǎn)單的卷積神經(jīng)網(wǎng)絡(luò)應(yīng)付不了這種復(fù)雜的情況。其次,該實(shí)驗(yàn)的圖片訓(xùn)練樣本只有244個(gè),數(shù)量級(jí)太小。

代碼略

簡(jiǎn)單卷積神經(jīng)網(wǎng)絡(luò)取得的效果:(黃色曲線是測(cè)試數(shù)據(jù)集錯(cuò)誤率,藍(lán)色曲線是訓(xùn)練數(shù)據(jù)集錯(cuò)誤率。)

因此,這里提到使用“加載已訓(xùn)練好的 ResNet 進(jìn)行遷移學(xué)習(xí)”。
ResNet 是微軟亞洲研究院何凱明團(tuán)隊(duì)開發(fā)的一種極深的特殊的卷積神經(jīng)網(wǎng)絡(luò)。該網(wǎng)絡(luò)的原始版本曾號(hào)稱是“史上最深的網(wǎng)絡(luò)”,有 152 層,在物體分類等任務(wù)上具有較高的準(zhǔn)確度。

考慮到原始的 ResNet 具有較大的復(fù)雜性,在本次實(shí)驗(yàn)中,實(shí)際遷移的是一個(gè)具有 18 層的精簡(jiǎn)版的 ResNet。該網(wǎng)絡(luò)由 18 個(gè)串聯(lián)在一起的卷積模塊構(gòu)成,其中每一個(gè)卷積模塊都包括一層卷積一層池化。下面將加載 ResNet 模型,并觀察模型的組成部分。如果是第一次運(yùn)行,那么模型會(huì)被下載到 ~/.torch/models/ 文件夾中。

torch.utils.model_zoo.load_url('http://labfile.oss.aliyuncs.com/courses/1073/resnet18-5c106cde.pth')
# 加載模型庫中的residual network,并設(shè)置pretrained為true,這樣便可加載相應(yīng)的權(quán)重
net = models.resnet18(pretrained=True)
#如果存在GPU,就將網(wǎng)絡(luò)加載到GPU上
net = net.cuda() if use_cuda else net
# 將網(wǎng)絡(luò)的架構(gòu)打印出來
net

從模型的組成部分中,可以看到最后有一層全連接層,也就是 (fc): Linear(in_features=512, out_features=1000)。

4.3.1 構(gòu)建遷移模型

下面把 ResNet18 中的卷積模塊作為特征提取層遷移過來,用于提取局部特征。同時(shí),將 ResNet18 中最后的全連接層(fc)替換,構(gòu)建一個(gè)包含 512 個(gè)隱含節(jié)點(diǎn)的全連接層,后接兩個(gè)結(jié)點(diǎn)的輸出層,用于最后的分類輸出。
整個(gè)模型的前面大部分的結(jié)構(gòu)都是 ResNet,最后兩層被替換成了自定義的全連接層。

# 讀取最后線性層的輸入單元數(shù),這是前面各層卷積提取到的特征數(shù)量
num_ftrs = net.fc.in_features

# 重新定義一個(gè)全新的線性層,它的輸出為2,原本是1000
net.fc = nn.Linear(num_ftrs, 2)

#如果存在GPU則將網(wǎng)絡(luò)加載到GPU中
net.fc = net.fc.cuda() if use_cuda else net.fc

criterion = nn.CrossEntropyLoss() #Loss函數(shù)的定義
# 將網(wǎng)絡(luò)的所有參數(shù)放入優(yōu)化器中
optimizer = optim.SGD(net.parameters(), lr = 0.0001, momentum=0.9)

4.3.2 訓(xùn)練模型+測(cè)試+繪制圖表

在訓(xùn)練階段,遷移過來的 ResNet 模塊的結(jié)構(gòu)和所有超參數(shù)都可以保持不變,但是權(quán)重參數(shù)則有可能被新的數(shù)據(jù)重新訓(xùn)練。是否要更新這些舊模塊的權(quán)重參數(shù)完全取決于我們采取的遷移學(xué)習(xí)方式。
遷移學(xué)習(xí)主要有兩種模式:預(yù)訓(xùn)練模式固定值模式
接下來會(huì)分別介紹

4.3.2.1 預(yù)訓(xùn)練模式

record = [] #記錄準(zhǔn)確率等數(shù)值的容器

#開始訓(xùn)練循環(huán)
num_epochs = 20
net.train(True) # 給網(wǎng)絡(luò)模型做標(biāo)記,標(biāo)志說模型在訓(xùn)練集上訓(xùn)練
best_model = net
best_r = 0.0
for epoch in range(num_epochs):
    #optimizer = exp_lr_scheduler(optimizer, epoch)
    train_rights = [] #記錄訓(xùn)練數(shù)據(jù)集準(zhǔn)確率的容器
    train_losses = []
    for batch_idx, (data, target) in enumerate(train_loader):  #針對(duì)容器中的每一個(gè)批進(jìn)行循環(huán)
        data, target = Variable(data), Variable(target) #將Tensor轉(zhuǎn)化為Variable,data為圖像,target為標(biāo)簽
        #如果存在GPU則將變量加載到GPU中
        if use_cuda:
            data, target = data.cuda(), target.cuda()
        output = net(data) #完成一次預(yù)測(cè)
        loss = criterion(output, target) #計(jì)算誤差
        optimizer.zero_grad() #清空梯度
        loss.backward() #反向傳播
        optimizer.step() #一步隨機(jī)梯度下降
        right = rightness(output, target) #計(jì)算準(zhǔn)確率所需數(shù)值,返回正確的數(shù)值為(正確樣例數(shù),總樣本數(shù))
        train_rights.append(right) #將計(jì)算結(jié)果裝到列表容器中
        loss = loss.cpu() if use_cuda else loss
        train_losses.append(loss.data.numpy())


        #if batch_idx % 20 == 0: #每間隔100個(gè)batch執(zhí)行一次
     #train_r為一個(gè)二元組,分別記錄訓(xùn)練集中分類正確的數(shù)量和該集合中總的樣本數(shù)
    train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))

    #在測(cè)試集上分批運(yùn)行,并計(jì)算總的正確率
    net.eval() #標(biāo)志模型當(dāng)前為運(yùn)行階段
    test_loss = 0
    correct = 0
    vals = []
    #對(duì)測(cè)試數(shù)據(jù)集進(jìn)行循環(huán)
    for data, target in val_loader:
        #如果存在GPU則將變量加載到GPU中
        if use_cuda:
            data, target = data.cuda(), target.cuda()
        data, target = Variable(data, requires_grad=True), Variable(target)
        output = net(data) #將特征數(shù)據(jù)喂入網(wǎng)絡(luò),得到分類的輸出
        val = rightness(output, target) #獲得正確樣本數(shù)以及總樣本數(shù)
        vals.append(val) #記錄結(jié)果

    #計(jì)算準(zhǔn)確率
    val_r = (sum([tup[0] for tup in vals]), sum([tup[1] for tup in vals]))
    val_ratio = 1.0*val_r[0].numpy()/val_r[1]

    if val_ratio > best_r:
        best_r = val_ratio
        best_model = copy.deepcopy(net)
    #打印準(zhǔn)確率等數(shù)值,其中正確率為本訓(xùn)練周期Epoch開始后到目前撮的正確率的平均值
    print('訓(xùn)練周期: {} \tLoss: {:.6f}\t訓(xùn)練正確率: {:.2f}%, 校驗(yàn)正確率: {:.2f}%'.format(
        epoch, np.mean(train_losses), 100. * train_r[0].numpy() / train_r[1], 100. * val_r[0].numpy()/val_r[1]))       
    record.append([np.mean(train_losses), 1. * train_r[0].data.numpy() / train_r[1], 1. * val_r[0].data.numpy() / val_r[1]])

#繪制訓(xùn)練誤差曲線
x = [x[0] for x in record]
y = [1 - x[1] for x in record]
z = [1 - x[2] for x in record]
#plt.plot(x)
plt.figure(figsize = (10, 7))
plt.plot(y)
plt.plot(z)
plt.xlabel('Epoch')
plt.ylabel('Error Rate')

測(cè)試模型,繪制分類效果

def visualize_model(model, num_images=6):
    images_so_far = 0
    fig = plt.figure(figsize=(15,10))

    for i, data in enumerate(val_loader):
        inputs, labels = data
        inputs, labels = Variable(inputs), Variable(labels)
        if use_cuda:
            inputs, labels = inputs.cuda(), labels.cuda()
        outputs = model(inputs)
        _, preds = torch.max(outputs.data, 1)
        preds = preds.cpu().numpy() if use_cuda else preds.numpy()
        for j in range(inputs.size()[0]):
            images_so_far += 1
            ax = plt.subplot( 2,num_images//2, images_so_far)
            ax.axis('off')

            ax.set_title('predicted: {}'.format(val_dataset.classes[preds[j]]))
            imshow(data[0][j])

            if images_so_far == num_images:
                return
visualize_model(net)

plt.ioff()
plt.show()

4.3.2.2 固定值模式

遷移過來的部分網(wǎng)絡(luò)在結(jié)構(gòu)和權(quán)重上都保持固定的數(shù)值不會(huì)改變。
要想讓模型在固定值模式下訓(xùn)練,需要先鎖定網(wǎng)絡(luò)模型相關(guān)位置的參數(shù)。鎖定的方法非常簡(jiǎn)單,只要把網(wǎng)絡(luò)的梯度反傳標(biāo)志 requires_grad 設(shè)置為 False 就可以了。

# 加載residual網(wǎng)絡(luò)模型
net = torchvision.models.resnet18(pretrained=True)
# 將模型放入GPU中
net = net.cuda() if use_cuda else net

# 循環(huán)網(wǎng)絡(luò),將所有參數(shù)設(shè)為不更新梯度信息
for param in net.parameters():
    param.requires_grad = False

# 將網(wǎng)絡(luò)最后一層線性層換掉
num_ftrs = net.fc.in_features
net.fc = nn.Linear(num_ftrs, 2)
net.fc = net.fc.cuda() if use_cuda else net.fc

criterion = nn.CrossEntropyLoss() #Loss函數(shù)的定義
# 僅將線性層的參數(shù)放入優(yōu)化器中
optimizer = optim.SGD(net.fc.parameters(), lr = 0.001, momentum=0.9)


#訓(xùn)練模型
record = [] #記錄準(zhǔn)確率等數(shù)值的容器

#開始訓(xùn)練循環(huán)
num_epochs = 4
net.train(True) # 給網(wǎng)絡(luò)模型做標(biāo)記,標(biāo)志說模型在訓(xùn)練集上訓(xùn)練
best_model = net
best_r = 0.0
for epoch in range(num_epochs):
    #optimizer = exp_lr_scheduler(optimizer, epoch)
    train_rights = [] #記錄訓(xùn)練數(shù)據(jù)集準(zhǔn)確率的容器
    train_losses = []
    for batch_idx, (data, target) in enumerate(train_loader):  #針對(duì)容器中的每一個(gè)批進(jìn)行循環(huán)
        data, target = Variable(data), Variable(target) #將Tensor轉(zhuǎn)化為Variable,data為圖像,target為標(biāo)簽
        if use_cuda:
            data, target = data.cuda(), target.cuda()
        output = net(data) #完成一次預(yù)測(cè)
        loss = criterion(output, target) #計(jì)算誤差
        optimizer.zero_grad() #清空梯度
        loss.backward() #反向傳播
        optimizer.step() #一步隨機(jī)梯度下降
        right = rightness(output, target) #計(jì)算準(zhǔn)確率所需數(shù)值,返回正確的數(shù)值為(正確樣例數(shù),總樣本數(shù))
        train_rights.append(right) #將計(jì)算結(jié)果裝到列表容器中
        loss = loss.cpu() if use_cuda else loss
        train_losses.append(loss.data.numpy())


     #train_r為一個(gè)二元組,分別記錄訓(xùn)練集中分類正確的數(shù)量和該集合中總的樣本數(shù)
    train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))

    #在測(cè)試集上分批運(yùn)行,并計(jì)算總的正確率
    net.eval() #標(biāo)志模型當(dāng)前為運(yùn)行階段
    test_loss = 0
    correct = 0
    vals = []
    #對(duì)測(cè)試數(shù)據(jù)集進(jìn)行循環(huán)
    for data, target in val_loader:
        data, target = Variable(data, requires_grad=True), Variable(target)
        if use_cuda:
            data, target = data.cuda(), target.cuda()
        output = net(data) #將特征數(shù)據(jù)喂入網(wǎng)絡(luò),得到分類的輸出
        val = rightness(output, target) #獲得正確樣本數(shù)以及總樣本數(shù)
        vals.append(val) #記錄結(jié)果

    #計(jì)算準(zhǔn)確率
    val_r = (sum([tup[0] for tup in vals]), sum([tup[1] for tup in vals]))
    val_ratio = 1.0*val_r[0].numpy()/val_r[1]

    if val_ratio > best_r:
        best_r = val_ratio
        best_model = copy.deepcopy(net)
    #打印準(zhǔn)確率等數(shù)值,其中正確率為本訓(xùn)練周期Epoch開始后到目前撮的正確率的平均值
    print('訓(xùn)練周期: {} \tLoss: {:.6f}\t訓(xùn)練正確率: {:.2f}%, 校驗(yàn)正確率: {:.2f}%'.format(
        epoch, np.mean(train_losses), 100. * train_r[0].numpy() / train_r[1], 100. * val_r[0].numpy()/val_r[1]))       
    record.append([np.mean(train_losses), 1. * train_r[0].data.numpy() / train_r[1], 1. * val_r[0].data.numpy() / val_r[1]])


# 繪制誤差曲線
x = [x[0] for x in record]
y = [1 - x[1] for x in record]
z = [1 - x[2] for x in record]
#plt.plot(x)
plt.figure(figsize = (10, 7))
plt.plot(y)
plt.plot(z)
plt.xlabel('Epoch')
plt.ylabel('Error Rate')


#展示分類結(jié)果
visualize_model(best_model)

plt.ioff()
plt.show()

4.4 結(jié)論

該實(shí)驗(yàn)中,預(yù)訓(xùn)練遷移模型取得的效果整體的錯(cuò)誤率比簡(jiǎn)單卷積神經(jīng)網(wǎng)絡(luò)低了很多。訓(xùn)練錯(cuò)誤率可以穩(wěn)定在 0.02 之下,測(cè)試錯(cuò)誤率大約在 0.07 左右。因?yàn)樵陬A(yù)訓(xùn)練模式下,模型對(duì)訓(xùn)練數(shù)據(jù)的擬合性比較強(qiáng),所以訓(xùn)練錯(cuò)誤率與測(cè)試錯(cuò)誤率差別較大。
固定值遷移模式下,訓(xùn)練錯(cuò)誤率可以在 0.02 ~ 0.04 之間,比預(yù)訓(xùn)練模式稍高。測(cè)試錯(cuò)誤率大約在 0.07 左右,與預(yù)訓(xùn)練模式差不多。
因?yàn)楣潭ㄖ的J芥i定了大部分權(quán)重,模型對(duì)訓(xùn)練數(shù)據(jù)的擬合性沒那么強(qiáng),所以訓(xùn)練錯(cuò)誤率與測(cè)試錯(cuò)誤率的差別也沒那么大。

到此這篇關(guān)于PyTorch 遷移學(xué)習(xí)實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)PyTorch 遷移內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論