DEiT 전투: DEiT를 사용하여 이미지 분류 작업 달성(1)

요약

DEiT는 2020년 FaceBook에서 제안한 Transformer 모델입니다. 이 모델은 Transformer가 학습하기 어려운 문제를 해결하여 3일 동안 4개의 GPU를 사용하여 외부 데이터를 사용하지 않고 ImageNet의 학습을 완료하여 SOTA 수준에 도달했습니다.
DEiT가 제안한 증류 전략은 다른 중요한 아키텍처를 도입하지 않고 토큰의 증류를 증가시킬 뿐입니다. 아래 그림과 같이
여기에 이미지 설명 삽입
Distilled 토큰은 클래스 토큰과 유사하게 사용됩니다. self-attention을 통해 다른 토큰과 상호 작용하고 마지막 레이어 이후에 네트워크에서 출력됩니다. 증류된 토큰을 사용하면 클래스 토큰을 보완하면서 일반 증류와 마찬가지로 모델이 교사의 출력에서 ​​학습할 수 있습니다. 우리는 이것을 코드에서 찾을 수 있습니다:

 self.dist_token = nn.Parameter(torch.zeros(1, 1, self.embed_dim))
 self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))

cls_tokens는 클래스 토큰이고 dist_token은 증류 토큰이며 실제로 매우 유사하며 면밀히 조사한 결과 차이점을 찾을 수 없습니다.

 def forward(self, x):
        x, x_dist = self.forward_features(x)
        x = self.head(x)
        x_dist = self.head_dist(x_dist)
        if self.training:
            return x, x_dist
        else:
            # during inference, return the average of both classifier predictions
            return (x + x_dist) / 2

모델이 Teacher 모델에서 학습하도록 하려면 if self.training:을 true로 설정하여 논문에서와 같이 RegNet을 Teacher로 사용하고 DeiT 모델을 Student로 사용하여 x_dist를 증류할 수 있습니다. 그렇지 않으면 두 두 가지의 상보성을 달성하기 위해 평균화됩니다.
timm의 코드는 공식 코드와 다르지만 논리는 동일합니다.timm의 코드는 다음과 같습니다.

 def forward_head(self, x, pre_logits: bool = False) -> torch.Tensor:
        if pre_logits:
            return (x[:, 0] + x[:, 1]) / 2
        x, x_dist = self.head(x[:, 0]), self.head_dist(x[:, 1])
        if self.distilled_training and self.training and not torch.jit.is_scripting():
            # only return separate classification predictions when training in distilled mode
            return x, x_dist
        else:
            # during standard train / finetune, inference average the classifier predictions
            return (x + x_dist) / 2

(드디어 알아 냈습니다. 며칠 동안 참았고 공식 코드를 읽을 때까지 이해하지 못했습니다.)
시간이있을 때 외부 모델 증류 사용에 대한 자습서를 작성하겠습니다.

이 기사는 주로 DEiT를 사용하여 이미지 분류 작업을 완료하는 방법을 설명한 다음 프로젝트의 실제 전투를 함께 완료합니다. 이 예제에서 선택한 모델은 deit_small_patch16_224 및 deit_small_distilled_patch16_224이며 식물 묘목 데이터 세트에서 96% 및 97% 정확도를 달성했습니다. deit_small_patch16_224에는 토큰 증류 작업이 없고 deit_small_distilled_patch16_224에는 토큰 증류 작업이 있습니다.결과에서 증류는 여전히 좋은 효과가 있습니다.
논문 링크: https://arxiv.org/abs/2012.12877v2
논문 번역: https://wanghao.blog.csdn.net/article/details/128180419?spm=1001.2014.3001.5502
동영상 설명: https://www. zhihu.com/zvideo/1587194506348040192
DeiT 테스트 결과:
여기에 이미지 설명 삽입

여기에 이미지 설명 삽입

DeiT_dist 테스트 결과:

여기에 이미지 설명 삽입
여기에 이미지 설명 삽입

이 기사를 통해 다음을 배울 수 있습니다.

  1. 변환 향상, CutOut, MixUp, CutMix 및 기타 향상 방법을 포함하여 데이터 향상을 사용하는 방법은 무엇입니까?
  2. 교육을 달성하기 위해 DEit 모델 및 DEiT_dist 모델을 구현하는 방법은 무엇입니까?
  3. pytorch 자체 혼합 정밀도를 사용하는 방법은 무엇입니까?
  4. 그래디언트 클리핑을 사용하여 그래디언트 폭발을 방지하는 방법은 무엇입니까?
  5. DP 다중 그래픽 카드 교육을 사용하는 방법은 무엇입니까?
  6. Loss와 Acc Curve를 그리는 방법은?
  7. val 평가 보고서를 생성하는 방법은 무엇입니까?
  8. 테스트 스위트를 테스트하기 위해 테스트 스크립트를 작성하는 방법은 무엇입니까?
  9. 코사인 어닐링 전략을 사용하여 학습률을 조정하는 방법은 무엇입니까?
  10. AverageMeter 클래스를 사용하여 ACC 및 손실과 같은 사용자 정의 변수를 계산하는 방법은 무엇입니까?
  11. ACC1 및 ACC5를 이해하고 계산하는 방법은 무엇입니까?
  12. EMA를 사용하는 방법?

설치 패키지

팀 설치

pip, 명령을 사용하십시오.

pip install timm

이 문서에서 사용된 timm의 모델입니다.

데이터 증대 컷아웃 및 혼합

성능 향상을 위해 Cutout과 Mixup 두 가지 향상 방법을 코드에 추가했습니다. 이러한 향상된 기능을 모두 구현하려면 torchtoolbox를 설치해야 합니다. 설치 명령:

pip install torchtoolbox

변환에서 컷아웃 구현.

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): 혼합 알파 값, > 0인 경우 혼합이 활성화됩니다.

cutmix_alpha (float): cutmix 알파 값, > 0이면 cutmix가 활성화됩니다.

cutmix_minmax (List[float]): cutmix 최소/최대 이미지 비율, cutmix가 활성화되어 있습니다. None이 아니면 이 대 알파를 사용합니다.

cutmix_minmax가 설정된 경우 cutmix_alpha는 기본적으로 1.0으로 설정됩니다.

prob(float): 배치 또는 요소당 mixup 또는 cutmix를 적용할 확률.

switch_prob (float): 둘 다 활성화되었을 때 컷믹스와 믹스업을 전환할 확률.

mode (str): mixup/cutmix 매개변수를 적용하는 방법(각 'batch', 'pair'(요소 쌍), 'elem'(요소).

correct_lam (bool): cutmix bbox가 이미지 테두리에 의해 잘릴 때 적용됩니다. 람다 보정

label_smoothing(float): 혼합 대상 텐서에 레이블 평활화를 적용합니다.

num_classes(int): 대상의 클래스 수입니다.

EMA

EMA(지수 이동 평균)는 지수 이동 평균입니다. 딥러닝의 관행은 과거 매개변수의 복사본을 저장하는 것입니다.일정 학습 기간이 지난 후 과거 매개변수를 사용하여 현재 학습된 매개변수를 평활화합니다. 구체적인 구현은 다음과 같습니다.

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가 득점에 실패하기 쉬우므로 모든 사람이 이 점에 주의해야 합니다!

프로젝트 구조

DEiT_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
├─mean_std.py
├─makedata.py
├─ema.py
├─train.py
├─train_dist.py
└─test.py

mean_std.py: mean과 std의 값을 계산합니다.
makedata.py: 데이터 세트를 생성합니다.
ema.py: EMA 스크립트
train.py: DEiT 모델 훈련
train_dist.py: 증류 전략으로 DEiT 모델 훈련.

DP 모드에서 혼합 정밀도를 사용하려면 모델의 전달 기능 앞에 @autocast()도 추가해야 합니다 GPU training import package from torch.cuda.amp import autocast를 사용한다면 CPU를 사용한다면 import from torch.cpu.amp import autocast.
여기에 이미지 설명 삽입

평균 및 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))

데이터 세트 구조:

이미지-20220221153058619

작업 결과:

([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)

위의 콘텐츠를 완료한 후 교육 및 테스트를 시작할 수 있습니다.

추천

출처blog.csdn.net/m0_47867638/article/details/131180430