首頁 > 軟體

python人工智慧使用RepVgg實現影象分類範例詳解

2022-10-14 14:01:36

摘要

RepVgg通過結構重引數化讓VGG再次偉大。 所謂“VGG式”指的是:

  • 沒有任何分支結構。即通常所說的plain或feed-forward架構。
  • 僅使用3x3折積。
  • 僅使用ReLU作為啟用函數。

RepVGG的更深版本達到了84.16%正確率!反超若干transformer!

RepVgg是如何到的呢?簡單地說就是:

  • 首先, 訓練一個多分支模型
  • 然後,將多分支模型等價轉換為單路模型
  • 最在,在部署的時候,部署轉換後單路模型

我這篇文章主要講解如何使用RepVgg完成影象分類任務,接下來我們一起完成專案的實戰。

通過這篇文章能讓你學到:

  • 如何使用資料增強,包括transforms的增強、CutOut、MixUp、CutMix等增強手段?
  • 如何實現RepVGG模型實現訓練?
  • 如何將多分支模型等價轉換為單路模型?
  • 如何使用pytorch自帶混合精度?
  • 如何使用梯度裁剪防止梯度爆炸?
  • 如何使用DP多顯示卡訓練?
  • 如何繪製loss和acc曲線?
  • 如何生成val的測評報告?
  • 如何編寫測試指令碼測試測試集?
  • 如何使用餘弦退火策略調整學習率?
  • 如何使用AverageMeter類統計ACC和loss等自定義變數?
  • 如何理解和統計ACC1和ACC5?
  • 如何使用EMA?

安裝包

安裝timm

使用pip就行,命令:

pip install timm

資料增強Cutout和Mixup

為了提高成績我在程式碼中加入Cutout和Mixup這兩種增強方式。實現這兩種增強需要安裝torchtoolbox。安裝命令:

pip install torchtoolbox

Cutout實現,在transforms中。

from torchtoolbox.transform import Cutout
# 資料預處理
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    Cutout(),
    transforms.ToTensor(),
    transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])

需要匯入包:from timm.data.mixup import Mixup,

定義Mixup,和SoftTargetCrossEntropy

  mixup_fn = Mixup(
    mixup_alpha=0.8, cutmix_alpha=1.0, cutmix_minmax=None,
    prob=0.1, switch_prob=0.5, mode='batch',
    label_smoothing=0.1, num_classes=12)
 criterion_train = SoftTargetCrossEntropy()

引數詳解:

mixup_alpha (float): mixup alpha 值,如果 > 0,則 mixup 處於活動狀態。

cutmix_alpha (float):cutmix alpha 值,如果 > 0,cutmix 處於活動狀態。

cutmix_minmax (List[float]):cutmix 最小/最大影象比率,cutmix 處於活動狀態,如果不是 None,則使用這個 vs alpha。

如果設定了 cutmix_minmax 則cutmix_alpha 預設為1.0

prob (float): 每批次或元素應用 mixup 或 cutmix 的概率。

switch_prob (float): 當兩者都處於活動狀態時切換cutmix 和mixup 的概率 。

mode (str): 如何應用 mixup/cutmix 引數(每個'batch','pair'(元素對),'elem'(元素)。

correct_lam (bool): 當 cutmix bbox 被影象邊框剪裁時應用。 lambda 校正

label_smoothing (float):將標籤平滑應用於混合目標張量。

num_classes (int): 目標的類數。

EMA

EMA(Exponential Moving Average)是指數移動平均值。在深度學習中的做法是儲存歷史的一份引數,在一定訓練階段後,拿歷史的引數給目前學習的引數做一次平滑。具體實現如下:

class EMA():
    def __init__(self, model, decay):
        self.model = model
        self.decay = decay
        self.shadow = {}
        self.backup = {}
    def register(self):
        for name, param in self.model.named_parameters():
            if param.requires_grad:
                self.shadow[name] = param.data.clone()
    def update(self):
        for name, param in self.model.named_parameters():
            if param.requires_grad:
                assert name in self.shadow
                new_average = (1.0 - self.decay) * param.data + self.decay * self.shadow[name]
                self.shadow[name] = new_average.clone()
    def apply_shadow(self):
        for name, param in self.model.named_parameters():
            if param.requires_grad:
                assert name in self.shadow
                self.backup[name] = param.data
                param.data = self.shadow[name]
    def restore(self):
        for name, param in self.model.named_parameters():
            if param.requires_grad:
                assert name in self.backup
                param.data = self.backup[name]
        self.backup = {}

加入到模型中。

# 初始化
ema = EMA(model, 0.999)
ema.register()
# 訓練過程中,更新完引數後,同步update shadow weights
def train():
    optimizer.step()
    ema.update()
# eval前,apply shadow weights;eval之後,恢復原來模型的引數
def evaluate():
    ema.apply_shadow()
    # evaluate
    ema.restore()

這個ema最好放在微調的時候使用,否則驗證集不上分,或者上分很慢。

專案結構

RepVgg_demo
├─data1
│  ├─Black-grass
│  ├─Charlock
│  ├─Cleavers
│  ├─Common Chickweed
│  ├─Common wheat
│  ├─Fat Hen
│  ├─Loose Silky-bent
│  ├─Maize
│  ├─Scentless Mayweed
│  ├─Shepherds Purse
│  ├─Small-flowered Cranesbill
│  └─Sugar beet
├─models
│  ├─__init__.py
│  ├─repvgg.py
│  └─se_block.py
├─mean_std.py
├─makedata.py
├─ema.py
├─train.py
└─test.py

mean_std.py:計算mean和std的值。 makedata.py:生成資料集。 ema.py:EMA指令碼 models資料夾下的repvgg.py和se_block.py:來自官方的pytorch版本的程式碼。 - repvgg.py:網路檔案。 - se_block.py:SE注意力機制。

為了能在DP方式中使用混合精度,還需要在模型的forward函數前增加@autocast()。

計算mean和std

為了使模型更加快速的收斂,我們需要計算出mean和std的值,新建mean_std.py,插入程式碼:

from torchvision.datasets import ImageFolder
import torch
from torchvision import transforms
def get_mean_and_std(train_data):
    train_loader = torch.utils.data.DataLoader(
        train_data, batch_size=1, shuffle=False, num_workers=0,
        pin_memory=True)
    mean = torch.zeros(3)
    std = torch.zeros(3)
    for X, _ in train_loader:
        for d in range(3):
            mean[d] += X[:, d, :, :].mean()
            std[d] += X[:, d, :, :].std()
    mean.div_(len(train_data))
    std.div_(len(train_data))
    return list(mean.numpy()), list(std.numpy())
if __name__ == '__main__':
    train_dataset = ImageFolder(root=r'data1', transform=transforms.ToTensor())
    print(get_mean_and_std(train_dataset))

資料集結構:

執行結果:

([0.3281186, 0.28937867, 0.20702125], [0.09407319, 0.09732835, 0.106712654])

把這個結果記錄下來,後面要用!

生成資料集

我們整理還的影象分類的資料集結構是這樣的

data
├─Black-grass
├─Charlock
├─Cleavers
├─Common Chickweed
├─Common wheat
├─Fat Hen
├─Loose Silky-bent
├─Maize
├─Scentless Mayweed
├─Shepherds Purse
├─Small-flowered Cranesbill
└─Sugar beet

pytorch和keras預設載入方式是ImageNet資料集格式,格式是

├─data
│  ├─val
│  │   ├─Black-grass
│  │   ├─Charlock
│  │   ├─Cleavers
│  │   ├─Common Chickweed
│  │   ├─Common wheat
│  │   ├─Fat Hen
│  │   ├─Loose Silky-bent
│  │   ├─Maize
│  │   ├─Scentless Mayweed
│  │   ├─Shepherds Purse
│  │   ├─Small-flowered Cranesbill
│  │   └─Sugar beet
│  └─train
│      ├─Black-grass
│      ├─Charlock
│      ├─Cleavers
│      ├─Common Chickweed
│      ├─Common wheat
│      ├─Fat Hen
│      ├─Loose Silky-bent
│      ├─Maize
│      ├─Scentless Mayweed
│      ├─Shepherds Purse
│      ├─Small-flowered Cranesbill
│      └─Sugar beet

新增格式轉化指令碼makedata.py,插入程式碼:

import glob
import os
import shutil
image_list=glob.glob('data1/*/*.png')
print(image_list)
file_dir='data'
if os.path.exists(file_dir):
    print('true')
    #os.rmdir(file_dir)
    shutil.rmtree(file_dir)#刪除再建立
    os.makedirs(file_dir)
else:
    os.makedirs(file_dir)
from sklearn.model_selection import train_test_split
trainval_files, val_files = train_test_split(image_list, test_size=0.3, random_state=42)
train_dir='train'
val_dir='val'
train_root=os.path.join(file_dir,train_dir)
val_root=os.path.join(file_dir,val_dir)
for file in trainval_files:
    file_class=file.replace("\","/").split('/')[-2]
    file_name=file.replace("\","/").split('/')[-1]
    file_class=os.path.join(train_root,file_class)
    if not os.path.isdir(file_class):
        os.makedirs(file_class)
    shutil.copy(file, file_class + '/' + file_name)
for file in val_files:
    file_class=file.replace("\","/").split('/')[-2]
    file_name=file.replace("\","/").split('/')[-1]
    file_class=os.path.join(val_root,file_class)
    if not os.path.isdir(file_class):
        os.makedirs(file_class)
    shutil.copy(file, file_class + '/' + file_name)

完成上面的內容就可以開啟訓練和測試了。

以上就是python人工智慧使用RepVgg實現影象分類範例詳解的詳細內容,更多關於python人工智慧RepVgg影象分類的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com