본문 바로가기
AI/딥러닝

[AI 부트캠프] DAY 92 - 트랙학습 CV 6

by HOHHOH 2023. 11. 30.

[오늘의 일지]

트랙학습 실시간 강의 - Object Detection

[상세 내용]

Object Detection

정의

- Object detection은 이미지나 비디오에서 여러 객체를 식별하고, 각 객체의 위치를 바운딩 박스로 표시하며, 종종 각 객체의 클래스를 분류하는 작업입니다.

 

종류

- Two-Stage Detection: 두 단계로 진행되며, 먼저 이미지에서 관심 영역을 찾은 후, 해당 영역에서 객체를 감지 및 분류합니다. 예시 모델은 R-CNN, Fast R-CNN, Faster R-CNN이 있습니다.

- One-Stage Detection: 단일 단계에서 객체의 위치를 바로 예측하고 분류합니다. 예시 모델은 YOLO (You Only Look Once), SSD (Single Shot Multibox Detector), RetinaNet 등이 있습니다.

 

튜토리얼 코드(중간중간 생략한 부분이 많습니다.)

import os
import torch

from torchvision.io import read_image
from torchvision.ops.boxes import masks_to_boxes
from torchvision import tv_tensors
from torchvision.transforms.v2 import functional as F


class PennFudanDataset(torch.utils.data.Dataset):
    def __init__(self, root, transforms):
        self.root = root
        self.transforms = transforms
        # 모든 이미지 파일들을 읽고, 정렬하여
        # 이미지와 분할 마스크 정렬을 확인합니다
        self.imgs = list(sorted(os.listdir(os.path.join(root, "PNGImages"))))
        self.masks = list(sorted(os.listdir(os.path.join(root, "PedMasks"))))

    def __getitem__(self, idx):
        # 이미지와 마스크를 읽어옵니다
        img_path = os.path.join(self.root, "PNGImages", self.imgs[idx])
        mask_path = os.path.join(self.root, "PedMasks", self.masks[idx])
        # 분할 마스크는 RGB로 변환하지 않음을 유의하세요
        # 왜냐하면 각 색상은 다른 인스턴스에 해당하며, 0은 배경에 해당합니다
        img = read_image(img_path)
        mask = read_image(mask_path)

        # instances are encoded as different colors
        obj_ids = torch.unique(mask)
        # first id is the background, so remove it
        obj_ids = obj_ids[1:]
        num_objs = len(obj_ids)

        # 컬러 인코딩된 마스크를 바이너리 마스크 세트로 나눕니다
        masks = (mask == obj_ids[:, None, None]).to(dtype=torch.uint8)

        # get bounding box coordinates for each mask
        boxes = masks_to_boxes(masks)

        # 객체 종류는 한 종류만 존재합니다. 예제에서는 사람만이 대상입니다
        labels = torch.ones((num_objs,), dtype=torch.int64)

        image_id = idx
        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])
        # 모든 인스턴스는 군중(crowd) 상태가 아님을 가정합니다
        iscrowd = torch.zeros((num_objs,), dtype=torch.int64)

        # Wrap sample and targets into torchvision tv_tensors:
        img = tv_tensors.Image(img)

        target = {}
        target["boxes"] = tv_tensors.BoundingBoxes(boxes, format="XYXY", canvas_size=F.get_size(img))
        target["masks"] = tv_tensors.Mask(masks)
        target["labels"] = labels
        target["image_id"] = image_id
        target["area"] = area
        target["iscrowd"] = iscrowd

        if self.transforms is not None:
            img, target = self.transforms(img, target)

        return img, target

    def __len__(self):
        return len(self.imgs)

 

import torchvision
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor

# COCO로 미리 학습된 모델 읽기
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(weights="DEFAULT")

# 분류기를 새로운 것으로 교체하는데, num_classes는 사용자가 정의합니다
num_classes = 2  # 1 클래스(사람) + 배경

# 분류기에서 사용할 입력 특징의 차원 정보를 얻습니다
in_features = model.roi_heads.box_predictor.cls_score.in_features

# 미리 학습된 모델의 머리 부분을 새로운 것으로 교체합니다
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)

 

import torchvision
from torchvision.models.detection import FasterRCNN
from torchvision.models.detection.rpn import AnchorGenerator

# 분류 목적으로 미리 학습된 모델을 로드하고 특징들만을 리턴하도록 합니다
backbone = torchvision.models.mobilenet_v2(weights="DEFAULT").features
# Faster RCNN은 백본의 출력 채널 수를 알아야 합니다.
# mobilenetV2의 경우 1280이므로 여기에 추가해야 합니다.
backbone.out_channels = 1280

# RPN(Region Proposal Network)이 5개의 서로 다른 크기와 3개의 다른 측면 비율(Aspect ratio)을 가진
# 5 x 3개의 앵커를 공간 위치마다 생성하도록 합니다.
# 각 특징 맵이 잠재적으로 다른 사이즈와 측면 비율을 가질 수 있기 때문에 Tuple[Tuple[int]] 타입을 가지도록 합니다.

anchor_generator = AnchorGenerator(sizes=((32,64,128,256,512),),
                                   aspect_ratios=((0.5,1.0,2.0),))

# 관심 영역의 자르기 및 재할당 후 자르기 크기를 수행하는 데 사용할 피쳐 맵을 정의합니다.
# 만약 백본이 텐서를 리턴할때, featmap_names 는 [0] 이 될 것이라고 예상합니다.
# 일반적으로 백본은 OrderedDict[Tensor] 타입을 리턴해야 합니다.
# 그리고 특징맵에서 사용할 featmap_names 값을 정할 수 있습니다.
roi_pooler = torchvision.ops.MultiScaleRoIAlign(featmap_names=['0'],
                                                output_size=7,
                                                sampling_ratio=2)

# 조각들을 Faster RCNN 모델로 합칩니다.
model = FasterRCNN(backbone,
                   num_classes=2,
                   rpn_anchor_generator=anchor_generator,
                   box_roi_pool=roi_pooler)

 

- 중간 생략 -

 

import zipfile

os.system("wget https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip")

def unzip_file(zip_path, extract_to):
    """
    zip_path: 압축 해제할 ZIP 파일의 경로
    extract_to: 압축을 풀 폴더의 경로
    """
    # 압축을 풀 폴더가 없으면 생성
    if not os.path.exists(extract_to):
        os.makedirs(extract_to)

    # ZIP 파일 열기
    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        # ZIP 파일 내용을 지정된 폴더에 압축 해제
        zip_ref.extractall(extract_to)

# 사용 예시
zip_file_path = '/content/PennFudanPed.zip'  # ZIP 파일 경로
destination_folder = '/content/data/'  # 압축 해제될 폴더 경로

unzip_file(zip_file_path, destination_folder)

 

model = get_model_instance_segmentation(num_classes) # 결과때문에 resnet을 다시 사용했습니다.

# move model to the right device
model.to(device)

# construct an optimizer
params = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.SGD(
    params,
    lr=0.005,
    momentum=0.9,
    weight_decay=0.0005
)

# and a learning rate scheduler
lr_scheduler = torch.optim.lr_scheduler.StepLR(
    optimizer,
    step_size=3,
    gamma=0.1
)

# let's train it for 5 epochs
num_epochs = 5

for epoch in range(num_epochs):
    # train for one epoch, printing every 10 iterations
    train_one_epoch(model, optimizer, data_loader, device, epoch, print_freq=10)
    # update the learning rate
    lr_scheduler.step()
    # evaluate on the test dataset
    evaluate(model, data_loader_test, device=device)

print("That's it!")

 

결과 예시

 

[마무리]

 오늘은 Object Detection에 대해서 수업을 들었습니다. 기존에는 그냥 분류를 통해 정확도나 loss 값만 확인했기 때문에 뭔가 아직 CV를 한다는 느낌을 못 받았었는데 오늘은 제대로 CV를 배우는 느낌이 들었습니다. 아직은 뭐가 뭔지 자세하게는 모르는 부분이 많지만 앞으로 여러 모델을 사용해 보면서 익숙해질 수 있도록 노력해 봐야 될 거 같습니다.

반응형

댓글