3 분 소요

본 게시물은 W&B가 제공한 MLOps의 Model Development에 대한 내용을 기술한다.

1. Introduce

Model development를 위해 W&B의 여러 기능을 사용하여 End to End Prototype을 생성해 봄

1.1 Agenda

📍 Build to End to End Prototype

Prototype을 설계하기 위한 단계는 아래와 같다.

  1. Understand the Business Context
  2. Frame the Data Science Problem
  3. Explore & Understand Your Data
  4. Establish Baseline Metrics & Models
  5. Communicate Your Result

1.2 Case Study

📍 Autonomous Vehicles Perception

  • 자율주행을 위한 인식, 예측, 동작 중 인식에 초점을 맞춤
  • BDD100K라는 기존 데이터세트의 서브세트로 학습
  • Semantic Segmentation을 사용

👀 Object Detection Vs Semantic Segmentation

  1. Object Detection (객체 검출):
    • Object detection은 이미지 내에서 물체를 감지하고 해당 물체가 어디에 있는지를 바운딩 박스(bounding box)로 표시한다.
    • 이미지에서 여러 물체를 동시에 감지하고, 각 물체의 위치와 클래스를 식별한다.
  2. Semantic Segmentation (의미적 분할):
    • Semantic segmentation은 이미지를 픽셀 수준에서 분할하여 각 픽셀을 특정 클래스(물체 또는 배경)로 할당한다.
    • 각 픽셀에 대해 해당 픽셀이 어떤 물체에 속하는지 또는 배경에 속하는지를 식별한다.

2. EDA

2.1 의존성 설치 및 데이터셋 다운로드

# Install dependencies (run once)
!wget https://raw.githubusercontent.com/wandb/edu/main/mlops-001/lesson1/requirements.txt
!wget https://raw.githubusercontent.com/wandb/edu/main/mlops-001/lesson1/params.py
!wget https://raw.githubusercontent.com/wandb/edu/main/mlops-001/lesson1/utils.py
!pip install -r requirements.txt

DEBUG = False # set this flag to True to use a small subset of data for testing

from fastai.vision.all import *
import params

import wandb

URL = 'https://storage.googleapis.com/wandb_course/bdd_simple_1k.zip'
path = Path(untar_data(URL, force_download=True))
path.ls()
(#3) [Path('/root/.fastai/data/bdd_simple_1k/labels'),Path('/root/.fastai/data/bdd_simple_1k/LICENSE.txt'),Path('/root/.fastai/data/bdd_simple_1k/images')]
  • 이미지 파일과 라벨링 그리고 라이선스 파일이 있음을 확인

2.2 W&B Table

run = wandb.init(project=params.WANDB_PROJECT, entity=params.ENTITY, job_type="upload")
raw_data_at = wandb.Artifact(params.RAW_DATA_AT, type="raw_data")

  • W&B initialize 및 수행 화면
def label_func(fname):
    return (fname.parent.parent/"labels")/f"{fname.stem}_mask.png"

def get_classes_per_image(mask_data, class_labels):
    unique = list(np.unique(mask_data))
    result_dict = {}
    for _class in class_labels.keys():
        result_dict[class_labels[_class]] = int(_class in unique)
    return result_dict

def _create_table(image_files, class_labels):
    "Create a table with the dataset"
    # W&B Table 생성 코드
    labels = [str(class_labels[_lab]) for _lab in list(class_labels)]
    table = wandb.Table(columns=["File_Name", "Images", "Split"] + labels)
    
    for i, image_file in progress_bar(enumerate(image_files), total=len(image_files)):
        image = Image.open(image_file)
        mask_data = np.array(Image.open(label_func(image_file)))
        class_in_image = get_classes_per_image(mask_data, class_labels)
        table.add_data(
            str(image_file.name),
            wandb.Image(
                    image,
                    masks={
                        "predictions": {
                            "mask_data": mask_data,
                            "class_labels": class_labels,
                        }
                    }
            ),
            "None", # we don't have a dataset split yet
            *[class_in_image[_lab] for _lab in labels]
        )
    
    return table
raw_data_at.add_file(path/'LICENSE.txt', name='LICENSE.txt')
raw_data_at.add_dir(path/'images', name='images')
raw_data_at.add_dir(path/'labels', name='labels')
image_files = get_image_files(path/"images", recurse=False)
  • raw_data_at에 파일 및 폴더 추가
  • fastai의 get_image_files을 사용해 이미지 가져옴
# sample a subset if DEBUG
if DEBUG: image_files = image_files[:10]

table = _create_table(image_files, params.BDD_CLASSES)
raw_data_at.add(table, "eda_table")
run.log_artifact(raw_data_at)
run.finish()
  • 생성된 테이블(eda_table)에 이미지 데이터를 임포트

📍 Artifacts import 수행화면

  • W&B Table을 통해 데이터를 쉽게 EDA 할 수 있게 됨

3. Split Dataset

import os, warnings
import pandas as pd
from sklearn.model_selection import StratifiedGroupKFold
warnings.filterwarnings('ignore')

run = wandb.init(project=params.WANDB_PROJECT, entity=params.ENTITY, job_type="data_split")
raw_data_at = run.use_artifact(f'{params.RAW_DATA_AT}:latest')

fnames = os.listdir(path/'images')
groups = [s.split('-')[0] for s in fnames]
orig_eda_table = raw_data_at.get("eda_table")
y = orig_eda_table.get_column('bicycle')
  • 이전 저장한 Table과 데이터들을 불러옴
df = pd.DataFrame()
df['File_Name'] = fnames
df['fold'] = -1

cv = StratifiedGroupKFold(n_splits=10)
for i, (train_idxs, test_idxs) in enumerate(cv.split(fnames, y, groups)):
    df.loc[test_idxs, ['fold']] = i
    
df['Stage'] = 'train'
df.loc[df.fold == 0, ['Stage']] = 'test'
df.loc[df.fold == 1, ['Stage']] = 'valid'
del df['fold']
df.Stage.value_counts()
train    800
valid    100
test     100
Name: Stage, dtype: int64
df.to_csv('data_split.csv', index=False)
processed_data_at = wandb.Artifact(params.PROCESSED_DATA_AT, type="split_data")
processed_data_at.add_file('data_split.csv')
processed_data_at.add_dir(path)
data_split_table = wandb.Table(dataframe=df[['File_Name', 'Stage']])
join_table = wandb.JoinedTable(orig_eda_table, data_split_table, "File_Name")
processed_data_at.add(join_table, "eda_table_data_split")

run.log_artifact(processed_data_at)
run.finish()
  • Train, Validation, Test로 분리된 데이터를 다시 Artifacts 및 Table에 저장

4. Baseline Model

from fastai.callback.wandb import WandbCallback
from utils import get_predictions, create_iou_table, MIOU, BackgroundIOU, \
                  RoadIOU, TrafficLightIOU, TrafficSignIOU, PersonIOU, VehicleIOU, BicycleIOU

train_config = SimpleNamespace(
    framework="fastai",
    img_size=(180, 320),
    batch_size=8,
    augment=True, # use data augmentation
    epochs=10, 
    lr=2e-3,
    pretrained=True,  # whether to use pretrained encoder
    seed=42,
)

set_seed(train_config.seed, reproducible=True)
run = wandb.init(project=params.WANDB_PROJECT, entity=params.ENTITY, job_type="training", config=train_config)
  • config를 통해 실험의 조건을 설정
processed_data_at = run.use_artifact(f'{params.PROCESSED_DATA_AT}:latest')
processed_dataset_dir = Path(processed_data_at.download())
df = pd.read_csv(processed_dataset_dir / 'data_split.csv')
  • 앞서 저장된 artifacts를 사용해 데이터를 불러옴
df = df[df.Stage != 'test'].reset_index(drop=True)
df['is_valid'] = df.Stage == 'valid'

# assign paths
df["image_fname"] = [processed_dataset_dir/f'images/{f}' for f in df.File_Name.values]
df["label_fname"] = [label_func(f) for f in df.image_fname.values]
  • valid와 test를 구분
def get_data(df, bs=4, img_size=(180, 320), augment=True):
    block = DataBlock(blocks=(ImageBlock, MaskBlock(codes=params.BDD_CLASSES)),
                  get_x=ColReader("image_fname"),
                  get_y=ColReader("label_fname"),
                  splitter=ColSplitter(),
                  item_tfms=Resize(img_size),
                  batch_tfms=aug_transforms() if augment else None,
                 )
    return block.dataloaders(df, bs=bs)
  • fastai의 datablock을 사용해 dataloaders 생성
config = wandb.config

dls = get_data(df, bs=config.batch_size, img_size=config.img_size, augment=config.augment)

metrics = [MIOU(), BackgroundIOU(), RoadIOU(), TrafficLightIOU(), \
           TrafficSignIOU(), PersonIOU(), VehicleIOU(), BicycleIOU()]

learn = unet_learner(dls, arch=resnet18, pretrained=config.pretrained, metrics=metrics)

callbacks = [
    SaveModelCallback(monitor='miou'),
    WandbCallback(log_preds=False, log_model=True)
]
  • fastai의 unet_learner를 통해 학습
  • Metric 중 miou를 통해 모델 학습을 모니터링하도록 설정
learn.fit_one_cycle(config.epochs, config.lr, cbs=callbacks)

samples, outputs, predictions = get_predictions(learn)
table = create_iou_table(samples, outputs, predictions, params.BDD_CLASSES)
wandb.log({"pred_table":table})

scores = learn.validate()
metric_names = ['final_loss'] + [f'final_{x.name}' for x in metrics]
final_results = {metric_names[i] : scores[i] for i in range(len(scores))}
for k,v in final_results.items(): 
    wandb.summary[k] = v

wandb.finish()
  • 학습 및 결과 로깅

5. Future Plan

  • 이번 포스팅에선 case study를 통해 W&B의 artifacts와 table을 사용하여 효과적인 EDA를 수행하였고, baseline 모델의 학습 결과를 기록하였다.
  • 다음 포스팅에서는 현재 만들어진 baseline 모델을 developement 시키도록 하는 과정을 살펴볼 것이다.

댓글남기기