Tip
์ด ํํ ๋ฆฌ์ผ์ ์ต๋ํ ํ์ฉํ์๋ ค๋ฉด, ๋ค์์ ๋งํฌ๋ฅผ ์ด์ฉํ์๊ธธ ์ถ์ฒํฉ๋๋ค. Colab ๋ฒ์ . ์ด๋ฅผ ํตํด ์๋์ ์ ์๋ ์ ๋ณด๋ก ์คํ์ ํด ๋ณผ ์ ์์ต๋๋ค.
๋ณธ ํํ ๋ฆฌ์ผ์์๋ Penn-Fudan Database for Pedestrian Detection and Segmentation ๋ฐ์ดํฐ์ ์ผ๋ก ๋ฏธ๋ฆฌ ํ์ต๋ Mask R-CNN ๋ชจ๋ธ์ ๋ฏธ์ธ์กฐ์ ํด ๋ณผ ๊ฒ์ ๋๋ค. ์ด ๋ฐ์ดํฐ์ ์๋ ๋ณดํ์ ์ธ์คํด์ค(instance, ์ญ์์ฃผ: ์ด๋ฏธ์ง ๋ด์์ ์ฌ๋์ ์์น ์ขํ์ ํฝ์ ๋จ์์ ์ฌ๋ ์ฌ๋ถ๋ฅผ ๊ตฌ๋ถํ ์ ๋ณด๋ฅผ ํฌํจํฉ๋๋ค.) 345๋ช ์ด ์๋ 170๊ฐ์ ์ด๋ฏธ์ง๊ฐ ํฌํจ๋์ด ์์ผ๋ฉฐ, ์ฐ๋ฆฌ๋ ์ด ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ ์ ๋ฐ์ดํฐ์ ์ ์ธ์คํด์ค ๋ถํ (Instance Segmentation) ๋ชจ๋ธ์ ํ์ตํ๊ธฐ ์ํด torchvision์ ์๋ก์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํ ์์ ์ ๋๋ค.
๊ฐ์ฒด ๊ฒ์ถ, ์ธ์คํด์ค ๋ถํ ๋ฐ ์ฌ์ฉ์ ํคํฌ์ธํธ(Keypoint) ๊ฒ์ถ์ ํ์ตํ๊ธฐ ์ํ ์ฐธ์กฐ ์คํฌ๋ฆฝํธ๋ฅผ ํตํด
์๋ก์ด ์ฌ์ฉ์ ์ ์ ๋ฐ์ดํฐ์
์ถ๊ฐ๋ฅผ ์ฝ๊ฒ ์งํํด ๋ณผ ์ ์์ต๋๋ค.
๋ฐ์ดํฐ์
์ ํ์ค torch.utils.data.Dataset
ํด๋์ค๋ฅผ
์์ ๋ฐ์์ผ ํ๋ฉฐ, __len__
์ __getitem__
๋ฉ์๋๋ฅผ ๊ตฌํํด ์ฃผ์ด์ผ ํฉ๋๋ค.
๋ฐ์ดํฐ์
์์ ํ์ํ ์ ์ผํ ํน์ฑ์ __getitem__
๋ฉ์๋๊ฐ ๋ค์์
๋ฐํ ํด์ผ ํ๋ ๊ฒ์
๋๋ค:
- ์ด๋ฏธ์ง : PIL(Python Image Library) ์ด๋ฏธ์ง์ ํฌ๊ธฐ
(H, W)
- ๋์: ๋ค์์ ํ๋๋ฅผ ํฌํจํ๋ ์ฌ์ ํ์
boxes (FloatTensor[N, 4])
:N
๊ฐ์ ๋ฐ์ด๋ฉ ๋ฐ์ค(Bounding box)์ ์ขํ๋ฅผ[x0, y0, x1, y1]
ํํ๋ก ๊ฐ์ง๋๋ค. x์ ๊ด๋ จ๋ ๊ฐ ๋ฒ์๋0
๋ถํฐW
์ด๊ณ y์ ๊ด๋ จ๋ ๊ฐ์ ๋ฒ์๋0
๋ถํฐH
๊น์ง์ ๋๋ค.labels (Int64Tensor[N])
: ๋ฐ์ด๋ฉ ๋ฐ์ค ๋ง๋ค์ ๋ผ๋ฒจ ์ ๋ณด์ ๋๋ค.0
์ ํญ์ ๋ฐฐ๊ฒฝ์ ํด๋์ค๋ฅผ ํํํฉ๋๋ค.image_id (Int64Tensor[1])
: ์ด๋ฏธ์ง ๊ตฌ๋ถ์์ ๋๋ค. ๋ฐ์ดํฐ์ ์ ๋ชจ๋ ์ด๋ฏธ์ง ๊ฐ์ ๊ณ ์ ํ ๊ฐ์ด์ด์ผ ํ๋ฉฐ ํ๊ฐ ์ค์๋ ์ฌ์ฉ๋ฉ๋๋ค.area (Tensor[N])
: ๋ฐ์ด๋ฉ ๋ฐ์ค์ ๋ฉด์ ์ ๋๋ค. ๋ฉด์ ์ ํ๊ฐ ์ ์์,์ค๊ฐ,ํฐ ๋ฐ์ค ๊ฐ์ ์ ์๋ฅผ ๋ด๊ธฐ ์ํ ๊ธฐ์ค์ด๋ฉฐ COCO ํ๊ฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ํฉ๋๋ค.iscrowd (UInt8Tensor[N])
: ์ด ๊ฐ์ด ์ฐธ์ผ ๊ฒฝ์ฐ ํ๊ฐ์์ ์ ์ธํฉ๋๋ค.- (์ ํ์ )
masks (UInt8Tensor[N, H, W])
:N
๊ฐ์ ๊ฐ์ฒด ๋ง๋ค์ ๋ถํ ๋ง์คํฌ ์ ๋ณด์ ๋๋ค. - (์ ํ์ )
keypoints (FloatTensor[N, K, 3])
:N
๊ฐ์ ๊ฐ์ฒด๋ง๋ค์ ํคํฌ์ธํธ ์ ๋ณด์ ๋๋ค. ํคํฌ์ธํธ๋[x, y, visibility]
ํํ์ ๊ฐ์ ๋๋ค. visibility ๊ฐ์ด 0์ธ ๊ฒฝ์ฐ ํคํฌ์ธํธ๋ ๋ณด์ด์ง ์์์ ์๋ฏธํฉ๋๋ค. ๋ฐ์ดํฐ ์ฆ๊ฐ(Data augmentation)์ ๊ฒฝ์ฐ ํคํฌ์ธํธ ์ข์ฐ ๋ฐ์ ์ ๊ฐ๋ ์ ๋ฐ์ดํฐ ํํ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ฉฐ, ์๋ก์ด ํคํฌ์ธํธ ํํ์ ๋ํด "references/detection/transforms.py" ์ฝ๋ ๋ถ๋ถ์ ์์ ํด์ผ ํ ์๋ ์์ต๋๋ค.
๋ชจ๋ธ์ด ์์ ๋ฐฉ๋ฒ๋๋ก ๋ฆฌํด์ ํ๋ฉด, ํ์ต๊ณผ ํ๊ฐ ๋ ๋ค์ ๋ํด์ ๋์์ ํ ๊ฒ์ด๋ฉฐ
ํ๊ฐ ์คํฌ๋ฆฝํธ๋ pip install pycocotools` ๋ก ์ค์น ๊ฐ๋ฅํ pycocotools
๋ฅผ ์ฌ์ฉํ๊ฒ ๋ ๊ฒ์
๋๋ค.
Note
์๋์ฐ์ฆ์์๋ pip install git+https://github.com/gautamchitnis/cocoapi.git@cocodataset-master#subdirectory=PythonAPI
๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ pycocotools
๋ฅผ gautamchitnis ๋ก๋ถํฐ ๊ฐ์ ธ์ ์ค์นํฉ๋๋ค.
labels
์ ๋ํ ์ฐธ๊ณ ์ฌํญ. ์ด ๋ชจ๋ธ์ ํด๋์ค 0
์ ๋ฐฐ๊ฒฝ์ผ๋ก ์ทจ๊ธํฉ๋๋ค. ๋ง์ฝ ์ค๋นํ ๋ฐ์ดํฐ์
์ ๋ฐฐ๊ฒฝ์ ํด๋์ค๊ฐ ์๋ค๋ฉด, labels
์๋ 0
์ด ์์ด์ผ ํฉ๋๋ค.
์๋ฅผ ๋ค์ด, ๊ณ ์์ด ์ ๊ฐ์์ง ์ ์ค์ง 2๊ฐ์ ํด๋์ค๋ง ๋ถ๋ฅํ๋ค๊ณ ๊ฐ์ ํ๋ฉด, (0
์ด ์๋) 1
์ด ๊ณ ์์ด ๋ฅผ, 2
๊ฐ ๊ฐ์์ง ๋ฅผ ๋ํ๋ด๋๋ก ์ ์ํด์ผ ํฉ๋๋ค.
๋ฐ๋ผ์, ์ด ์์์์, ์ด๋ค ์ด๋ฏธ์ง์ ๋ ๊ฐ์ ํด๋์ค๋ฅผ ๋ชจ๋ ์๋ค๋ฉด, labels
ํ
์๋ [1,2]
์ ๊ฐ์ ์์ด ๋์ด์ผ ํฉ๋๋ค.
์ถ๊ฐ๋ก, ํ์ต ์ค์ ๊ฐ๋ก ์ธ๋ก ๋น์จ ๊ทธ๋ฃนํ๋ฅผ ์ฌ์ฉํ๋ ค๋ ๊ฒฝ์ฐ(๊ฐ ๋ฐฐ์น์ ์ ์ฌํ ๊ฐ๋ก ์ธ๋ก ๋น์จ์ด ์๋ ์์๋ง ํฌํจ๋๋๋ก),
์ด๋ฏธ์ง์ ๋์ด, ๋์ด๋ฅผ ๋ฆฌํดํ ์ ์๋๋ก get_height_and_width
๋ฉ์๋๋ฅผ ๊ตฌํํ๊ธฐ๋ฅผ ์ถ์ฒํฉ๋๋ค.
์ด ๋ฉ์๋๊ฐ ๊ตฌํ๋์ง ์์ ๊ฒฝ์ฐ์๋ ๋ชจ๋ ๋ฐ์ดํฐ์
์ __getitem__
๋ฅผ ํตํด ๋ฉ๋ชจ๋ฆฌ์ ์ด๋ฏธ์ง๊ฐ ๋ก๋๋๋ฉฐ
์ฌ์ฉ์ ์ ์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ ๊ฒ๋ณด๋ค ๋๋ฆด ์ ์์ต๋๋ค.
PennFudan ๋ฐ์ดํฐ์ ์ ์ํ ์ฝ๋๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค. `๋ค์ด๋ก๋ ํ ์์ถ ํ์ผ์ ํด์ ํ๋ฉด<https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip>`__, ๋ค์์ ํด๋ ๊ตฌ์กฐ๋ฅผ ๋ณผ ์ ์์ต๋๋ค:
PennFudanPed/ PedMasks/ FudanPed00001_mask.png FudanPed00002_mask.png FudanPed00003_mask.png FudanPed00004_mask.png ... PNGImages/ FudanPed00001.png FudanPed00002.png FudanPed00003.png FudanPed00004.png
ํ ์์ ์์๊ณผ ๋ถํ ๋ง์คํฌ์ ํ ๊ฐ์ง ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๊ฐ ์ด๋ฏธ์ง์๋ ํด๋นํ๋ ๋ถํ ๋ง์คํฌ๊ฐ ์์ผ๋ฉฐ, ์ฌ๊ธฐ์ ๊ฐ๊ฐ์ ์์์ ๋ค๋ฅธ ์ธ์คํด์ค์ ํด๋นํฉ๋๋ค. ๋ฐ์ดํฐ์ ์ ํด๋นํ๋ torch.utils.data.Dataset` ํด๋์ค๋ฅผ ์์ฑํฉ์๋ค.
import os
import numpy as np
import torch
from PIL import Image
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])
img = Image.open(img_path).convert("RGB")
# ๋ถํ ๋ง์คํฌ๋ RGB๋ก ๋ณํํ์ง ์์์ ์ ์ํ์ธ์
# ์๋ํ๋ฉด ๊ฐ ์์์ ๋ค๋ฅธ ์ธ์คํด์ค์ ํด๋นํ๋ฉฐ, 0์ ๋ฐฐ๊ฒฝ์ ํด๋นํฉ๋๋ค
mask = Image.open(mask_path)
# numpy ๋ฐฐ์ด์ PIL ์ด๋ฏธ์ง๋ก ๋ณํํฉ๋๋ค
mask = np.array(mask)
# ์ธ์คํด์ค๋ค์ ๋ค๋ฅธ ์๋ค๋ก ์ธ์ฝ๋ฉ ๋์ด ์์ต๋๋ค.
obj_ids = np.unique(mask)
# ์ฒซ๋ฒ์งธ id ๋ ๋ฐฐ๊ฒฝ์ด๋ผ ์ ๊ฑฐํฉ๋๋ค
obj_ids = obj_ids[1:]
# ์ปฌ๋ฌ ์ธ์ฝ๋ฉ๋ ๋ง์คํฌ๋ฅผ ๋ฐ์ด๋๋ฆฌ ๋ง์คํฌ ์ธํธ๋ก ๋๋๋๋ค
masks = mask == obj_ids[:, None, None]
# ๊ฐ ๋ง์คํฌ์ ๋ฐ์ด๋ฉ ๋ฐ์ค ์ขํ๋ฅผ ์ป์ต๋๋ค
num_objs = len(obj_ids)
boxes = []
for i in range(num_objs):
pos = np.where(masks[i])
xmin = np.min(pos[1])
xmax = np.max(pos[1])
ymin = np.min(pos[0])
ymax = np.max(pos[0])
boxes.append([xmin, ymin, xmax, ymax])
# ๋ชจ๋ ๊ฒ์ torch.Tensor ํ์
์ผ๋ก ๋ณํํฉ๋๋ค
boxes = torch.as_tensor(boxes, dtype=torch.float32)
# ๊ฐ์ฒด ์ข
๋ฅ๋ ํ ์ข
๋ฅ๋ง ์กด์ฌํฉ๋๋ค(์ญ์์ฃผ: ์์ ์์๋ ์ฌ๋๋ง์ด ๋์์
๋๋ค)
labels = torch.ones((num_objs,), dtype=torch.int64)
masks = torch.as_tensor(masks, dtype=torch.uint8)
image_id = idx
area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])
# ๋ชจ๋ ์ธ์คํด์ค๋ ๊ตฐ์ค(crowd) ์ํ๊ฐ ์๋์ ๊ฐ์ ํฉ๋๋ค
iscrowd = torch.zeros((num_objs,), dtype=torch.int64)
target = {}
target["boxes"] = boxes
target["labels"] = labels
target["masks"] = masks
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)
๋ฐ์ดํฐ์ ์ฝ๋๋ ์ฌ๊ธฐ๊น์ง์ ๋๋ค. ์ด์ ์ด ๋ฐ์ดํฐ์ ์ ๋ํ ์์ธก์ ์ํํ ์ ์๋ ๋ชจ๋ธ์ ์ ์ํด ๋ด ์๋ค.
์ด๋ฒ ํํ ๋ฆฌ์ผ์์๋ Faster R-CNN ์ ๊ธฐ๋ฐํ Mask R-CNN ๋ชจ๋ธ์ ์ฌ์ฉํ ์์ ์ ๋๋ค. Faster R-CNN์ ์ด๋ฏธ์ง์ ์กด์ฌํ ์ ์๋ ๊ฐ์ฒด์ ๋ํ ๋ฐ์ด๋ฉ ๋ฐ์ค์ ํด๋์ค ์ ์๋ฅผ ๋ชจ๋ ์์ธกํ๋ ๋ชจ๋ธ์ ๋๋ค.
Mask R-CNN์ ๊ฐ ์ธ์คํด์ค์ ๋ํ ๋ถํ ๋ง์คํฌ ์์ธกํ๋ ์ถ๊ฐ ๋ถ๊ธฐ(๋ ์ด์ด)๋ฅผ Faster R-CNN์ ์ถ๊ฐํ ๋ชจ๋ธ์ ๋๋ค.
Torchvision ๋ชจ๋ธ์ฃผ(model zoo, ์ญ์์ฃผ:๋ฏธ๋ฆฌ ํ์ต๋ ๋ชจ๋ธ๋ค์ ๋ชจ์ ๋์ ๊ณต๊ฐ)์์ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ธ๋ค ์ค ํ๋๋ฅผ ์ด์ฉํด ๋ชจ๋ธ์ ์์ ํ๋ ค๋ฉด ๋ณดํต ๋๊ฐ์ง ์ํฉ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ ๋ฏธ๋ฆฌ ํ์ต๋ ๋ชจ๋ธ์์ ์์ํด์ ๋ง์ง๋ง ๋ ์ด์ด ์์ค๋ง ๋ฏธ์ธ ์กฐ์ ํ๋ ๊ฒ์ ๋๋ค. ๋ค๋ฅธ ํ๋๋ ๋ชจ๋ธ์ ๋ฐฑ๋ณธ์ ๋ค๋ฅธ ๋ฐฑ๋ณธ์ผ๋ก ๊ต์ฒดํ๋ ๊ฒ์ ๋๋ค.(์๋ฅผ ๋ค๋ฉด, ๋ ๋น ๋ฅธ ์์ธก์ ํ๋ ค๊ณ ํ ๋) (์ญ์์ฃผ: ๋ฐฑ๋ณธ ๋ชจ๋ธ์ ResNet101 ์์ MobilenetV2 ๋ก ๊ต์ฒดํ๋ฉด ์ํ ์๋ ํฅ์์ ๊ธฐ๋ํ ์ ์์ต๋๋ค. ๋์ ์ธ์ ์ฑ๋ฅ์ ์ ํ ๋ ์ ์์ต๋๋ค.)
๋ค์ ์น์ ์์ ์ฐ๋ฆฌ๊ฐ ์ด๋ป๊ฒ ํ ์ ์๋์ง ์์ ๋ณด๊ฒ ์ต๋๋ค.
COCO์ ๋ํด ๋ฏธ๋ฆฌ ํ์ต๋ ๋ชจ๋ธ์์ ์์ํ์ฌ ํน์ ํด๋์ค๋ฅผ ์ํด ๋ฏธ์ธ ์กฐ์ ์ ์ํ๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค. ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๊ฐ๋ฅํฉ๋๋ค:
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)
์ฐ๋ฆฌ์ ๊ฒฝ์ฐ, ๋ฐ์ดํฐ ์ธํธ๊ฐ ๋งค์ฐ ์๊ธฐ ๋๋ฌธ์, ์ฐ๋ฆฌ๋ 1๋ฒ ์ ๊ทผ๋ฒ์ ๋ฐ๋ฅผ ๊ฒ์ด๋ผ๋ ์ ์ ๊ณ ๋ คํ์ฌ ๋ฏธ๋ฆฌ ํ์ต๋ ๋ชจ๋ธ์์ ๋ฏธ์ธ ์กฐ์ ํ๋ ๋ฐฉ์์ผ๋ก ์งํ ํ๊ฒ ์ต๋๋ค.
์ฌ๊ธฐ์ ์ธ์คํด์ค ๋ถํ ๋ง์คํฌ๋ ๊ณ์ฐํ๊ธฐ๋ฅผ ์ํ๊ธฐ ๋๋ฌธ์ Mask R-CNN๋ฅผ ์ฌ์ฉํฉ๋๋ค:
import torchvision
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
from torchvision.models.detection.mask_rcnn import MaskRCNNPredictor
def get_model_instance_segmentation(num_classes):
# COCO ์์ ๋ฏธ๋ฆฌ ํ์ต๋ ์ธ์คํด์ค ๋ถํ ๋ชจ๋ธ์ ์ฝ์ด์ต๋๋ค
model = torchvision.models.detection.maskrcnn_resnet50_fpn(weights="DEFAULT")
# ๋ถ๋ฅ๋ฅผ ์ํ ์
๋ ฅ ํน์ง ์ฐจ์์ ์ป์ต๋๋ค
in_features = model.roi_heads.box_predictor.cls_score.in_features
# ๋ฏธ๋ฆฌ ํ์ต๋ ํค๋๋ฅผ ์๋ก์ด ๊ฒ์ผ๋ก ๋ฐ๊ฟ๋๋ค
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)
# ๋ง์คํฌ ๋ถ๋ฅ๊ธฐ๋ฅผ ์ํ ์
๋ ฅ ํน์ง๋ค์ ์ฐจ์์ ์ป์ต๋๋ค
in_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels
hidden_layer = 256
# ๋ง์คํฌ ์์ธก๊ธฐ๋ฅผ ์๋ก์ด ๊ฒ์ผ๋ก ๋ฐ๊ฟ๋๋ค
model.roi_heads.mask_predictor = MaskRCNNPredictor(in_features_mask,
hidden_layer,
num_classes)
return model
๊ทธ๋ ์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ธ
์ ์ฌ์ฉ์ ์ ์ ๋ฐ์ดํฐ์
์์ ํ์ตํ๊ณ ํ๊ฐํ ์ค๋น๊ฐ ๋ ๊ฒ๋๋ค.
references/detection/
ํด๋ ๋ด์ ๊ฒ์ถ ๋ชจ๋ธ๋ค์ ํ์ต๊ณผ ํ๊ณผ๋ฅผ ์ฝ๊ฒ ํ๊ธฐ ์ํ ๋์ ํจ์๋ค์ด ์์ต๋๋ค.
์ฌ๊ธฐ์ references/detection/engine.py
, references/detection/utils.py
,
references/detection/transforms.py
๋ฅผ ์ฌ์ฉ ํ ๊ฒ์
๋๋ค.
references/detection
์๋์ ๋ชจ๋ ํ์ผ๊ณผ ํด๋๋ค์ ์ฌ์ฉ์์ ํด๋๋ก ๋ณต์ฌํ ๋ค ์ฌ์ฉํฉ๋๋ค.
๋ฐ์ดํฐ ์ฆ๊ฐ / ๋ณํ์ ์ํ ๋์ ํจ์๋ฅผ ์์ฑํด ๋ด ์๋ค
import transforms as T
def get_transform(train):
transforms = []
transforms.append(T.PILToTensor())
transforms.append(T.ToDtype(torch.float, scale = True))
if train:
# (์ญ์์ฃผ: ํ์ต์ 50% ํ๋ฅ ๋ก ํ์ต ์์์ ์ข์ฐ ๋ฐ์ ๋ณํํฉ๋๋ค)
transforms.append(T.RandomHorizontalFlip(0.5))
return T.Compose(transforms)
๋ฐ์ดํฐ์ ์ ๋ฐ๋ณตํ๊ธฐ ์ ์, ์ํ ๋ฐ์ดํฐ๋ก ํ์ต๊ณผ ์ถ๋ก ์ ๋ชจ๋ธ์ด ์์๋๋ก ๋์ํ๋์ง ์ดํด๋ณด๋ ๊ฒ์ด ์ข์ต๋๋ค.
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(weights="DEFAULT")
dataset = PennFudanDataset('PennFudanPed', get_transform(train=True))
data_loader = torch.utils.data.DataLoader(
dataset, batch_size=2, shuffle=True, num_workers=4,
collate_fn=utils.collate_fn)
# ํ์ต ์
images,targets = next(iter(data_loader))
images = list(image for image in images)
targets = [{k: v for k, v in t.items()} for t in targets]
output = model(images,targets) # Returns losses and detections
# ์ถ๋ก ์
model.eval()
x = [torch.rand(3, 300, 400), torch.rand(3, 500, 400)]
predictions = model(x) # Returns predictions
ํ์ต(train)๊ณผ ๊ฒ์ฆ(validation)์ ์ํํ๋๋ก ๋ฉ์ธ ํจ์๋ฅผ ์์ฑํฉ์๋ค:
from engine import train_one_epoch, evaluate
import utils
def main():
# ํ์ต์ GPU๋ก ์งํํ๋ GPU๊ฐ ๊ฐ์ฉํ์ง ์์ผ๋ฉด CPU๋ก ํฉ๋๋ค
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
# ์ฐ๋ฆฌ ๋ฐ์ดํฐ์
์ ๋ ๊ฐ์ ํด๋์ค๋ง ๊ฐ์ง๋๋ค - ๋ฐฐ๊ฒฝ๊ณผ ์ฌ๋
num_classes = 2
# ๋ฐ์ดํฐ์
๊ณผ ์ ์๋ ๋ณํ๋ค์ ์ฌ์ฉํฉ๋๋ค
dataset = PennFudanDataset('PennFudanPed', get_transform(train=True))
dataset_test = PennFudanDataset('PennFudanPed', get_transform(train=False))
# ๋ฐ์ดํฐ์
์ ํ์ต์ฉ๊ณผ ํ
์คํธ์ฉ์ผ๋ก ๋๋๋๋ค(์ญ์์ฃผ: ์ฌ๊ธฐ์๋ ์ ์ฒด์ 50๊ฐ๋ฅผ ํ
์คํธ์, ๋๋จธ์ง๋ฅผ ํ์ต์ ์ฌ์ฉํฉ๋๋ค)
indices = torch.randperm(len(dataset)).tolist()
dataset = torch.utils.data.Subset(dataset, indices[:-50])
dataset_test = torch.utils.data.Subset(dataset_test, indices[-50:])
# ๋ฐ์ดํฐ ๋ก๋๋ฅผ ํ์ต์ฉ๊ณผ ๊ฒ์ฆ์ฉ์ผ๋ก ์ ์ํฉ๋๋ค
data_loader = torch.utils.data.DataLoader(
dataset, batch_size=2, shuffle=True, num_workers=4,
collate_fn=utils.collate_fn)
data_loader_test = torch.utils.data.DataLoader(
dataset_test, batch_size=1, shuffle=False, num_workers=4,
collate_fn=utils.collate_fn)
# ๋์ ํจ์๋ฅผ ์ด์ฉํด ๋ชจ๋ธ์ ๊ฐ์ ธ์ต๋๋ค
model = get_model_instance_segmentation(num_classes)
# ๋ชจ๋ธ์ GPU๋ CPU๋ก ์ฎ๊น๋๋ค
model.to(device)
# ์ตํฐ๋ง์ด์ (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)
# ํ์ต๋ฅ ์ค์ผ์ฅด๋ฌ๋ฅผ ๋ง๋ญ๋๋ค
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
step_size=3,
gamma=0.1)
# 10 ์ํฌํฌ๋งํผ ํ์ตํด๋ด
์๋ค
num_epochs = 10
for epoch in range(num_epochs):
# 1 ์ํฌํฌ๋์ ํ์ตํ๊ณ , 10ํ ๋ง๋ค ์ถ๋ ฅํฉ๋๋ค
train_one_epoch(model, optimizer, data_loader, device, epoch, print_freq=10)
# ํ์ต๋ฅ ์ ์
๋ฐ์ดํธ ํฉ๋๋ค
lr_scheduler.step()
# ํ
์คํธ ๋ฐ์ดํฐ์
์์ ํ๊ฐ๋ฅผ ํฉ๋๋ค
evaluate(model, data_loader_test, device=device)
print("That's it!")
์ฒซ๋ฒ์งธ ์ํฌํฌ์ ์ถ๋ ฅ๊ฐ์ ์๋์ ๊ฐ์ ๊ฒ์ ๋๋ค:
Epoch: [0] [ 0/60] eta: 0:01:18 lr: 0.000090 loss: 2.5213 (2.5213) loss_classifier: 0.8025 (0.8025) loss_box_reg: 0.2634 (0.2634) loss_mask: 1.4265 (1.4265) loss_objectness: 0.0190 (0.0190) loss_rpn_box_reg: 0.0099 (0.0099) time: 1.3121 data: 0.3024 max mem: 3485 Epoch: [0] [10/60] eta: 0:00:20 lr: 0.000936 loss: 1.3007 (1.5313) loss_classifier: 0.3979 (0.4719) loss_box_reg: 0.2454 (0.2272) loss_mask: 0.6089 (0.7953) loss_objectness: 0.0197 (0.0228) loss_rpn_box_reg: 0.0121 (0.0141) time: 0.4198 data: 0.0298 max mem: 5081 Epoch: [0] [20/60] eta: 0:00:15 lr: 0.001783 loss: 0.7567 (1.1056) loss_classifier: 0.2221 (0.3319) loss_box_reg: 0.2002 (0.2106) loss_mask: 0.2904 (0.5332) loss_objectness: 0.0146 (0.0176) loss_rpn_box_reg: 0.0094 (0.0123) time: 0.3293 data: 0.0035 max mem: 5081 Epoch: [0] [30/60] eta: 0:00:11 lr: 0.002629 loss: 0.4705 (0.8935) loss_classifier: 0.0991 (0.2517) loss_box_reg: 0.1578 (0.1957) loss_mask: 0.1970 (0.4204) loss_objectness: 0.0061 (0.0140) loss_rpn_box_reg: 0.0075 (0.0118) time: 0.3403 data: 0.0044 max mem: 5081 Epoch: [0] [40/60] eta: 0:00:07 lr: 0.003476 loss: 0.3901 (0.7568) loss_classifier: 0.0648 (0.2022) loss_box_reg: 0.1207 (0.1736) loss_mask: 0.1705 (0.3585) loss_objectness: 0.0018 (0.0113) loss_rpn_box_reg: 0.0075 (0.0112) time: 0.3407 data: 0.0044 max mem: 5081 Epoch: [0] [50/60] eta: 0:00:03 lr: 0.004323 loss: 0.3237 (0.6703) loss_classifier: 0.0474 (0.1731) loss_box_reg: 0.1109 (0.1561) loss_mask: 0.1658 (0.3201) loss_objectness: 0.0015 (0.0093) loss_rpn_box_reg: 0.0093 (0.0116) time: 0.3379 data: 0.0043 max mem: 5081 Epoch: [0] [59/60] eta: 0:00:00 lr: 0.005000 loss: 0.2540 (0.6082) loss_classifier: 0.0309 (0.1526) loss_box_reg: 0.0463 (0.1405) loss_mask: 0.1568 (0.2945) loss_objectness: 0.0012 (0.0083) loss_rpn_box_reg: 0.0093 (0.0123) time: 0.3489 data: 0.0042 max mem: 5081 Epoch: [0] Total time: 0:00:21 (0.3570 s / it) creating index... index created! Test: [ 0/50] eta: 0:00:19 model_time: 0.2152 (0.2152) evaluator_time: 0.0133 (0.0133) time: 0.4000 data: 0.1701 max mem: 5081 Test: [49/50] eta: 0:00:00 model_time: 0.0628 (0.0687) evaluator_time: 0.0039 (0.0064) time: 0.0735 data: 0.0022 max mem: 5081 Test: Total time: 0:00:04 (0.0828 s / it) Averaged stats: model_time: 0.0628 (0.0687) evaluator_time: 0.0039 (0.0064) Accumulating evaluation results... DONE (t=0.01s). Accumulating evaluation results... DONE (t=0.01s). IoU metric: bbox Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.606 Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.984 Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.780 Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.313 Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.582 Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.612 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.270 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.672 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.672 Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.650 Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.755 Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.664 IoU metric: segm Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.704 Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.979 Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.871 Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.325 Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.488 Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.727 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.316 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.748 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.749 Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.650 Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.673 Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.758
๊ทธ๋์ 1 ์ํฌํฌ(epoch) ํ์ต์ ๊ฑฐ์ณ 60.6์ COCO ์คํ์ผ mAP์ 70.4์ ๋ง์คํฌ mAP๋ฅผ ์ป์์ต๋๋ค.
10 ์ํฌํฌ ํ์ต ํ, ๋ค์๊ณผ ๊ฐ์ ์์น๋ฅผ ์ป์์ต๋๋ค.
IoU metric: bbox Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.799 Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.969 Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.935 Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.349 Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.592 Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.831 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.324 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.844 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.844 Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.400 Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.777 Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.870 IoU metric: segm Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.761 Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.969 Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.919 Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.341 Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.464 Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.788 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.303 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.799 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.799 Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.400 Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.769 Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.818
ํ์ง๋ง ์์ธก๋ค์ ๊ฒฐ๊ณผ๊ฐ ์ด๋ป๊ฒ ๋์์๊น์? ๋ฐ์ดํฐ ์ ์ ์ด๋ฏธ์ง ํ๋๋ฅผ ๊ฐ์ ธ์์ ํ์ธํด ๋ด ์๋ค.
ํ์ต๋ ๋ชจ๋ธ์ด ์ด๋ฏธ์ง์์ 9๊ฐ์ ์ฌ๋ ์ธ์คํด์ค๋ฅผ ์์ธกํ์ต๋๋ค. ๊ทธ ์ค ๋์ด๊ฐ๋ฅผ ํ์ธํด ๋ด ์๋ค:
๊ฒฐ๊ณผ๊ฐ ๊ฝค ์ข์ ๋ณด์ ๋๋ค!
์ด ํํ ๋ฆฌ์ผ์์๋ ์ฌ์ฉ์ ์ ์ ๋ฐ์ดํฐ์
์์ ์ธ์คํด์ค ๋ถํ ๋ชจ๋ธ์ ์ํ ์์ฒด ํ์ต ํ์ดํ๋ผ์ธ์ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ์ต๋๋ค.
์ด๋ฅผ ์ํด ์์๊ณผ ์ ๋ต ๋ฐ ๋ถํ ๋ง์คํฌ๋ฅผ ๋ฐํํ๋ torch.utils.data.Dataset
ํด๋์ค๋ฅผ ์์ฑํ์ต๋๋ค.
๋ํ ์ด ์๋ก์ด ๋ฐ์ดํฐ ์
์ ๋ํ ์ ์ก ํ์ต(Transfer learning)์ ์ํํ๊ธฐ ์ํด
COCO train2017์ ๋ํด ๋ฏธ๋ฆฌ ํ์ต๋ Mask R-CNN ๋ชจ๋ธ์ ํ์ฉ ํ์ต๋๋ค.
๋ค์ค๋จธ์ / ๋ค์คGPU ์์์ ํ์ต์ ํฌํจํ๋ ๋ ๋ณต์กํ ์์ ๋ฅผ ์๊ณ ์ถ๋ค๋ฉด
torchvision ์ ์ฅ์์ ์๋ references/detection/train.py
๋ฅผ ํ์ธํด ๋ณด์ธ์.
์ฌ๊ธฐ ์์ ์ด๋ฒ ํํ ๋ฆฌ์ผ์ ์ ์ฒด ์์ค์ฝ๋๋ฅผ ๋ค์ด ๋ฐ์ผ์ค ์ ์์ต๋๋ค.