Skip to content

UpstageAILab/upstage-cv-classification-cv5

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 

Repository files navigation

Review Assignment Due Date

Document Type Classification Competitions

Team

image image image image image image
최장원 김영천 배창현 박성우 조예람 이소영B
팀장 팀원 팀원 팀원 팀원 팀원

1. Competitions Info

$\color{red}{\textsf{해당 대회의 Train/Test 데이터는 저작권 문제로 공개가 불가능해 비슷한 이미지로 대체되어있음을 알려드립니다!}}$

Overview

image

Environment

Vscode, RTX 3090 server

Timeline

  • February 05, 2024 - Start Date
  • February 19, 2024 - Final submission deadline

Evaluation

image

2. Components

Directory

├── code
    ├── EDA
    ├── Augmentation
    ├── Modelling
    └── Ensemble

3. Data descrption

Dataset overview

이번 대회는 computer vision domain에서 가장 중요한 태스크인 이미지 분류 대회입니다.

이미지 분류란 주어진 이미지를 여러 클래스 중 하나로 분류하는 작업입니다. 이러한 이미지 분류는 의료, 패션, 보안 등 여러 현업에서 기초적으로 활용되는 태스크입니다. 딥러닝과 컴퓨터 비전 기술의 발전으로 인한 뛰어난 성능을 통해 현업에서 많은 가치를 창출하고 있습니다. image

그 중, 이번 대회는 문서 타입 분류를 위한 이미지 분류 대회입니다. 문서 데이터는 금융, 의료, 보험, 물류 등 산업 전반에 가장 많은 데이터이며, 많은 대기업에서 디지털 혁신을 위해 문서 유형을 분류하고자 합니다. 이러한 문서 타입 분류는 의료, 금융 등 여러 비즈니스 분야에서 대량의 문서 이미지를 식별하고 자동화 처리를 가능케 할 수 있습니다.

이번 대회에 사용될 데이터는 총 17개 종의 문서로 분류되어 있습니다. 1570장의 학습 이미지를 통해 3140장의 평가 이미지를 예측하게 됩니다. 특히, 현업에서 사용하는 실 데이터를 기반으로 대회를 제작하여 대회와 현업의 갭을 최대한 줄였습니다. 또한 현업에서 생길 수 있는 여러 문서 상태에 대한 이미지를 구축하였습니다.

이번 대회를 통해서 문서 타입 데이터셋을 이용해 이미지 분류를 모델을 구축합니다. 주어진 문서 이미지를 입력 받아 17개의 클래스 중 정답을 예측하게 됩니다. computer vision에서 중요한 backbone 모델들을 실제 활용해보고, 좋은 성능을 가지는 모델을 개발할 수 있습니다. 그 밖에 학습했던 여러 테크닉들을 적용해 볼 수 있습니다.

본 대회는 결과물 csv 확장자 파일을 제출하게 됩니다.

EDA & Augmentaion

스크린샷 2024-02-21 151743

Augmentation

스크린샷 2024-02-21 151956

4. Modeling

Model descrition

metaformer: https://github.com/sail-sg/metaformer?tab=readme-ov-file

Modeling Process

image
.
.
.
.
image

# training 코드, evaluation 코드, training_loop 코드
def training(model, dataloader, optimizer, loss_fn, scheduler, device, epoch, num_epochs):
    model.train()  # 모델을 학습 모드로 설정
    train_loss = 0.0
    preds_list = []
    targets_list = []
    
    m = torch.nn.Softmax(dim=-1)

    tbar = tqdm(dataloader)
    for idx, (image, targets) in enumerate(tbar):
        image = image.to(device)
        targets = targets.to(device)
        
        # 순전파
        model.zero_grad(set_to_none=True)
        if (idx + 1) % 10 == 0:
            image, mix_targets = mixup_fn(image, targets)
            preds = model(image)
            loss = mixup_loss_fn(preds, mix_targets)
        else:
            preds = model(image)
            # loss = loss_fn(preds, targets)
            loss = loss_fn(m(preds), targets)

        # 역전파 및 가중치 업데이트
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        scheduler.step()

        # 손실과 정확도 계산
        train_loss += loss.item()
        preds_list.extend(preds.argmax(dim=1).detach().cpu().numpy())
        targets_list.extend(targets.detach().cpu().numpy())
        tbar.set_description(f"Epoch [{epoch+1}/{num_epochs}], Train Loss: {loss.item():.4f}")

    # 에폭별 학습 결과 출력
    train_loss = train_loss / len(dataloader)
    train_acc = accuracy_score(targets_list, preds_list)
    train_f1 = f1_score(targets_list, preds_list, average='macro')
    
    ret = {
        "train_loss": train_loss,
        "train_acc": train_acc,
        "train_f1": train_f1,
    }

    return model, ret

def evaluation(model, dataloader, loss_fn, device, epoch, num_epochs):
    model.eval()  # 모델을 평가 모드로 설정
    valid_loss = 0.0
    preds_list = []
    targets_list = []
    m = torch.nn.Softmax(dim=-1)

    with torch.no_grad(): # model의 업데이트 막기
        tbar = tqdm(dataloader)
        for idx, (image, targets) in enumerate(tbar):
            image = image.to(device)
            targets = targets.to(device)

            # 순전파
            model.zero_grad(set_to_none=True)
            if (idx + 1) % 8 == 0:
                image, mix_targets = mixup_fn(image, targets)
                preds = model(image)
                loss = mixup_loss_fn(preds, mix_targets)
            else:
                preds = model(image)
                loss = loss_fn(m(preds), targets)

            # 손실과 정확도 계산
            valid_loss += loss.item()
            preds_list.extend(preds.argmax(dim=1).detach().cpu().numpy())
            targets_list.extend(targets.detach().cpu().numpy())
            tbar.set_description(f"Epoch [{epoch+1}/{num_epochs}], Valid Loss: {loss.item():.4f}")
            
    # 에폭별 학습 결과 출력
    valid_loss = valid_loss / len(dataloader)
    valid_acc = accuracy_score(targets_list, preds_list)
    valid_f1 = f1_score(targets_list, preds_list, average='macro')
    
    ret = {
        "valid_loss": valid_loss,
        "valid_acc": valid_acc,
        "valid_f1": valid_f1,
    }

    return model, ret


def training_loop(model, train_dataloader, valid_dataloader, trn_dataset, val_dataset, train_dataset_list, val_dataset_list, loss_fn, optimizer, scheduler, device, num_epochs, patience, filename, project_name):
    best_valid_loss = float('inf')  # 가장 좋은 validation loss를 저장
    early_stop_counter = 0  # 카운터
    valid_max_acc = -1
    valid_max_f1 = -1
    
    notes = f"Optimizer: {optimizer.__class__.__name__}, focal_gamma: {loss_fn.gamma}, Image_Size: {img_size},"
    run = wandb.init(project = project_name, tags=[optimizer.__class__.__name__], notes=notes)
    
    for epoch in range(num_epochs):
        
        data_idx = epoch % 200
        T_dataset = ConcatDataset([trn_dataset, train_dataset_list[data_idx]])
        V_dataset = ConcatDataset([val_dataset, val_dataset_list[data_idx]])
        
        train_dataloader = DataLoader(T_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=num_workers, pin_memory=True, drop_last=False)
        valid_dataloader = DataLoader(V_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=num_workers, pin_memory=True, drop_last=False)
        print(f"dataset{data_idx}")
        
        model, train_ret = training(model, train_dataloader, optimizer, loss_fn, scheduler, device, epoch, num_epochs)
        model, valid_ret = evaluation(model, valid_dataloader, loss_fn, device, epoch, num_epochs)
        
        monitoring_value = {'train_loss': train_ret['train_loss'], 'train_accuracy': train_ret['train_acc'], 'train_f1': train_ret['train_f1'], 
                            'valid_loss': valid_ret['valid_loss'], 'valid_accuracy': valid_ret['valid_acc'], 'valid_f1': valid_ret['valid_f1'], 
                            'best_valid_loss': best_valid_loss, 'valid_max_acc': valid_max_acc, 'valid_max_f1': valid_max_f1,
                            'init_lr': optimizer.param_groups[0]['initial_lr'], 'lr': optimizer.param_groups[0]['lr'],
                            'weight decay': optimizer.param_groups[0]['weight_decay'],}
        
        run.log(monitoring_value, step=epoch)
        
        if valid_ret['valid_acc'] > valid_max_acc:
            valid_max_acc = valid_ret['valid_acc']
            
        if valid_ret['valid_f1'] > valid_max_f1:
            name, ext = os.path.splitext(filename)
            torch.save(model.state_dict(), model_path+name+'_f1'+ext)
            valid_max_f1 = valid_ret['valid_f1']
        
        # validation loss가 감소하면 모델 저장 및 카운터 리셋
        if valid_ret['valid_loss'] < best_valid_loss:
            best_valid_loss = valid_ret['valid_loss']
            # model.save_pretrained(model_path)
            name, ext = os.path.splitext(filename)
            torch.save(model.state_dict(), model_path+name+'_loss'+ext)
            early_stop_counter = 0
            
        # validation loss가 증가하거나 같으면 카운터 증가
        else:
            early_stop_counter += 1

        print(f"Epoch [{epoch + 1}/{num_epochs}], T_Train Loss: {train_ret['train_loss']:.4f}, Train Accuracy: {train_ret['train_acc']:.4f}, Train F1: {train_ret['train_f1']:.4f}")
        print(f"Epoch [{epoch + 1}/{num_epochs}], T_Valid Loss: {valid_ret['valid_loss']:.4f}, Valid Accuracy: {valid_ret['valid_acc']:.4f}, Valid F1: {valid_ret['valid_f1']:.4f}")

        # 조기 종료 카운터가 설정한 patience를 초과하면 학습 종료
        if early_stop_counter >= patience:
            print("Early stopping")
            break
            
    run.finish()
    return model, valid_max_acc, valid_max_f1
# 모델 전체 fine tuning
model.to(device)
num_epochs = EPOCHS
lr = LR
patience = 25
filename = 'caformer_s18_sail_in22k_ft_in1k_384.pth'
project_name = "kyc-DC-caformer_s18_386"

# optimizer = optim.RMSprop(model.parameters(), lr=lr, alpha=0.99)
# optimizer = optim.Adam(model.parameters(), lr=lr)
optimizer = optim.AdamW(model.parameters(), lr=lr, weight_decay=0.05)
# optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.9)

num_epoch_steps = len(trn_loader) * 2
# scheduler = optim.lr_scheduler.CyclicLR(optimizer, base_lr=lr/1000, max_lr=lr, step_size_up=10, step_size_down=50, mode='exp_range', gamma=0.995)
# scheduler = CosineAnnealingWarmUpRestarts(optimizer, T_0=num_epoch_steps*80, T_mult=2, eta_max=lr,  T_up=num_epoch_steps*1, gamma=0.5)

# scheduler = optim.lr_scheduler.CyclicLR(optimizer, base_lr=1e-6, max_lr=LR, step_size_up=4, step_size_down=10, mode='triangular')
scheduler = get_scheduler(
    name='cosine', optimizer=optimizer, 
    num_warmup_steps=0, 
    num_training_steps= num_epoch_steps * num_epochs
)

model, valid_max_acc, valid_max_f1 = training_loop(model, trn_loader, val_loader, trn_dataset, val_dataset, aug_trn_dataset_list, aug_val_dataset_list, loss_fn, optimizer, scheduler, device, num_epochs, patience, filename, project_name)
print(f'Valid max accuracy : {valid_max_acc:5f}, Valid max f1 : {valid_max_f1:5f}')

image image image

Ensemble & TTA

https://github.com/qubvel/ttach?tab=readme-ov-file https://github.com/qubvel/ttach/blob/master/ttach/wrappers.py #L52

5. Result

Leader Board

image
최종 리더보드
image

Presentation

ppt 폴더참조

Reference

https://paperswithcode.com/
https://github.com/qubvel/ttach?tab=readme-ov-file
https://github.com/sail-sg/metaformer
Focal Loss: https://github.com/mathiaszinnen/focal_loss_torch
CAFormer: https://arxiv.org/abs/2210.13452
RotNet: https://github.com/d4nst/RotNet

대회 데이터 참고용 링크: https://www.content.upstage.ai/ocr-pack/insurance

About

upstage-cv-classification-cv5 created by GitHub Classroom

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •