728x90
반응형

kitti Dataset을 중립 데이터형태로 변환하여 메모리 로드

# 원본 kitti Dataset는 10개의 Class로 되어 있음. 'Car Van Truck Pedestrian Person_sitting Cyclist Tram Misc DontCare'
CLASSES = ('Car', 'Truck', 'Pedestrian', 'Cyclist')
cat2label = {k:i for i, k in enumerate(CLASSES)}
print(cat2label)
cat2label['Car']

# {'Car': 0, 'Truck': 1, 'Pedestrian': 2, 'Cyclist': 3}
# 0
# data_anno {label}에 넣기 위한것
image_list = mmcv.list_from_file('/content/kitti_tiny/train.txt')

lines = mmcv.list_from_file('/content/kitti_tiny/training/label_2/000064.txt')
#print(lines)
content = [line.strip().split(' ') for line in lines]
bbox_names = [x[0] for x in content]
#print(bbox_names)
# bounding box 읽기
bboxes = [ [float(info) for info in x[4:8]] for x in content]
print(bboxes)

# [[657.65, 179.93, 709.86, 219.92], [731.51, 180.39, 882.28, 275.8], [715.18, 175.63, 762.77, 203.9], [816.58, 59.74, 1112.51, 266.07], [626.78, 174.27, 647.77, 192.18], [546.19, 168.97, 554.01, 177.09]]
import copy
import os.path as osp
import cv2

import mmcv
import numpy as np

from mmdet.datasets.builder import DATASETS
from mmdet.datasets.custom import CustomDataset

# 반드시 아래 Decorator 설정 할것.@DATASETS.register_module() 설정 시 force=True를 입력하지 않으면 Dataset 재등록 불가. 
@DATASETS.register_module(force=True)
class KittyTinyDataset(CustomDataset):
  CLASSES = ('Car', 'Truck', 'Pedestrian', 'Cyclist')
  # __init__ 가 없는것은 customdataset것을 이용
  ##### self.data_root: /content/kitti_tiny/ self.ann_file: /content/kitti_tiny/train.txt self.img_prefix: /content/kitti_tiny/training/image_2
  #### ann_file: /content/kitti_tiny/train.txt
  # annotation에 대한 모든 파일명을 가지고 있는 텍스트 파일을 __init__(self, ann_file)로 입력 받고, 이 self.ann_file이 load_annotations()의 인자로 입력
  def load_annotations(self, ann_file):
    print('##### self.data_root:', self.data_root, 'self.ann_file:', self.ann_file, 'self.img_prefix:', self.img_prefix)
    print('#### ann_file:', ann_file)
    cat2label = {k:i for i, k in enumerate(self.CLASSES)}
    image_list = mmcv.list_from_file(self.ann_file)
    # 포맷 중립 데이터를 담을 list 객체
    data_infos = []
    
    for image_id in image_list: # 000000
      filename = '{0:}/{1:}.jpeg'.format(self.img_prefix, image_id)
      # 원본 이미지의 너비, 높이를 image를 직접 로드하여 구함. 
      image = cv2.imread(filename)
      height, width = image.shape[:2]
      # 개별 image의 annotation 정보 저장용 Dict 생성. key값 filename 에는 image의 파일명만 들어감(디렉토리는 제외)
      data_info = {'filename': str(image_id) + '.jpeg',
                   'width': width, 'height': height}
      # 개별 annotation이 있는 서브 디렉토리의 prefix 변환. 
      label_prefix = self.img_prefix.replace('image_2', 'label_2')
      # 개별 annotation 파일을 1개 line 씩 읽어서 list 로드 
      lines = mmcv.list_from_file(osp.join(label_prefix, str(image_id)+'.txt'))

      # 전체 lines를 개별 line별 공백 레벨로 parsing 하여 다시 list로 저장. content는 list의 list형태임.
      # ann 정보는 numpy array로 저장되나 텍스트 처리나 데이터 가공이 list 가 편하므로 일차적으로 list로 변환 수행.   
      content = [line.strip().split(' ') for line in lines]
      # 오브젝트의 클래스명은 bbox_names로 저장. 
      bbox_names = [x[0] for x in content]
      # bbox 좌표를 저장
      bboxes = [ [float(info) for info in x[4:8]] for x in content]

      # 클래스명이 해당 사항이 없는 대상 Filtering out, 'DontCare'sms ignore로 별도 저장.
      gt_bboxes = []
      gt_labels = []
      gt_bboxes_ignore = []
      gt_labels_ignore = []
	  
      # 파일 내용을 읽는 loop
      for bbox_name, bbox in zip(bbox_names, bboxes):
        # 만약 bbox_name이 클래스명에 해당 되면, gt_bboxes와 gt_labels에 추가, 그렇지 않으면 gt_bboxes_ignore, gt_labels_ignore에 추가
        if bbox_name in cat2label:
          gt_bboxes.append(bbox)
          # gt_labels에는 class id를 입력
          gt_labels.append(cat2label[bbox_name])
        else:
          gt_bboxes_ignore.append(bbox)
          gt_labels_ignore.append(-1)
      # 개별 image별 annotation 정보를 가지는 Dict 생성. 해당 Dict의 value값은 모두 np.array임. 
      data_anno = {
          'bboxes': np.array(gt_bboxes, dtype=np.float32).reshape(-1, 4),
          'labels': np.array(gt_labels, dtype=np.long),
          'bboxes_ignore': np.array(gt_bboxes_ignore, dtype=np.float32).reshape(-1, 4),
          'labels_ignore': np.array(gt_labels_ignore, dtype=np.long)
      }
      # image에 대한 메타 정보를 가지는 data_info Dict에 'ann' key값으로 data_anno를 value로 저장. 
      data_info.update(ann=data_anno)
      # 전체 annotation 파일들에 대한 정보를 가지는 data_infos에 data_info Dict를 추가
      data_infos.append(data_info)

    return data_infos

 

### Config 설정하고 Pretrained 모델 다운로드
config_file = '/content/mmdetection/configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py'
checkpoint_file = '/content/mmdetection/checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'
!cd mmdetection; mkdir checkpoints
!wget -O /content/mmdetection/checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth http://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth

 

from mmcv import Config

cfg = Config.fromfile(config_file)
print(cfg.pretty_text)

# model = dict(
    type='FasterRCNN',
    pretrained='torchvision://resnet50',
    backbone=dict(
        type='ResNet',
        depth=50,
        num_stages=4,
        out_indices=(0, 1, 2, 3),
        frozen_stages=1,
        norm_cfg=dict(type='BN', requires_grad=True),
        norm_eval=True,
        style='pytorch'),
    neck=dict(
        type='FPN',
        in_channels=[256, 512, 1024, 2048],
        out_channels=256,
        num_outs=5),
    rpn_head=dict(
        type='RPNHead',
        in_channels=256,
        feat_channels=256,
        anchor_generator=dict(
            type='AnchorGenerator',
            scales=[8],
            ratios=[0.5, 1.0, 2.0],
            strides=[4, 8, 16, 32, 64]),
        bbox_coder=dict(
            type='DeltaXYWHBBoxCoder',
            target_means=[0.0, 0.0, 0.0, 0.0],
            target_stds=[1.0, 1.0, 1.0, 1.0]),
        loss_cls=dict(
            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
    roi_head=dict(
        type='StandardRoIHead',
        bbox_roi_extractor=dict(
            type='SingleRoIExtractor',
            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
            out_channels=256,
            featmap_strides=[4, 8, 16, 32]),
        bbox_head=dict(
            type='Shared2FCBBoxHead',
            in_channels=256,
            fc_out_channels=1024,
            roi_feat_size=7,
            num_classes=80,
            bbox_coder=dict(
                type='DeltaXYWHBBoxCoder',
                target_means=[0.0, 0.0, 0.0, 0.0],
                target_stds=[0.1, 0.1, 0.2, 0.2]),
            reg_class_agnostic=False,
            loss_cls=dict(
                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
            loss_bbox=dict(type='L1Loss', loss_weight=1.0))),
    train_cfg=dict(
        rpn=dict(
            assigner=dict(
                type='MaxIoUAssigner',
                pos_iou_thr=0.7,
                neg_iou_thr=0.3,
                min_pos_iou=0.3,
                match_low_quality=True,
                ignore_iof_thr=-1),
            sampler=dict(
                type='RandomSampler',
                num=256,
                pos_fraction=0.5,
                neg_pos_ub=-1,
                add_gt_as_proposals=False),
            allowed_border=-1,
            pos_weight=-1,
            debug=False),
        rpn_proposal=dict(
            nms_pre=2000,
            max_per_img=1000,
            nms=dict(type='nms', iou_threshold=0.7),
            min_bbox_size=0),
        rcnn=dict(
            assigner=dict(
                type='MaxIoUAssigner',
                pos_iou_thr=0.5,
                neg_iou_thr=0.5,
                min_pos_iou=0.5,
                match_low_quality=False,
                ignore_iof_thr=-1),
            sampler=dict(
                type='RandomSampler',
                num=512,
                pos_fraction=0.25,
                neg_pos_ub=-1,
                add_gt_as_proposals=True),
            pos_weight=-1,
            debug=False)),
    test_cfg=dict(
        rpn=dict(
            nms_pre=1000,
            max_per_img=1000,
            nms=dict(type='nms', iou_threshold=0.7),
            min_bbox_size=0),
        rcnn=dict(
            score_thr=0.05,
            nms=dict(type='nms', iou_threshold=0.5),
            max_per_img=100)))
dataset_type = 'CocoDataset'
data_root = 'data/coco/'
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(
        type='Normalize',
        mean=[123.675, 116.28, 103.53],
        std=[58.395, 57.12, 57.375],
        to_rgb=True),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(
                type='Normalize',
                mean=[123.675, 116.28, 103.53],
                std=[58.395, 57.12, 57.375],
                to_rgb=True),
            dict(type='Pad', size_divisor=32),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img'])
        ])
]
data = dict(
    samples_per_gpu=2,
    workers_per_gpu=2,
    train=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_train2017.json',
        img_prefix='data/coco/train2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(type='LoadAnnotations', with_bbox=True),
            dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
            dict(type='RandomFlip', flip_ratio=0.5),
            dict(
                type='Normalize',
                mean=[123.675, 116.28, 103.53],
                std=[58.395, 57.12, 57.375],
                to_rgb=True),
            dict(type='Pad', size_divisor=32),
            dict(type='DefaultFormatBundle'),
            dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
        ]),
    val=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_val2017.json',
        img_prefix='data/coco/val2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=32),
                    dict(type='ImageToTensor', keys=['img']),
                    dict(type='Collect', keys=['img'])
                ])
        ]),
    test=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_val2017.json',
        img_prefix='data/coco/val2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=32),
                    dict(type='ImageToTensor', keys=['img']),
                    dict(type='Collect', keys=['img'])
                ])
        ]))
evaluation = dict(interval=1, metric='bbox')
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
lr_config = dict(
    policy='step',
    warmup='linear',
    warmup_iters=500,
    warmup_ratio=0.001,
    step=[8, 11])
runner = dict(type='EpochBasedRunner', max_epochs=12)
checkpoint_config = dict(interval=1)
log_config = dict(interval=50, hooks=[dict(type='TextLoggerHook')])
custom_hooks = [dict(type='NumClassCheckHook')]
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)]

 

from mmdet.apis import set_random_seed

# dataset에 대한 환경 파라미터 수정. 
cfg.dataset_type = 'KittyTinyDataset'
cfg.data_root = '/content/kitti_tiny/'

# train, val, test dataset에 대한 type, data_root, ann_file, img_prefix 환경 파라미터 수정. 
cfg.data.train.type = 'KittyTinyDataset'
cfg.data.train.data_root = '/content/kitti_tiny/'
cfg.data.train.ann_file = 'train.txt'
cfg.data.train.img_prefix = 'training/image_2'

cfg.data.val.type = 'KittyTinyDataset'
cfg.data.val.data_root = '/content/kitti_tiny/'
cfg.data.val.ann_file = 'val.txt'
cfg.data.val.img_prefix = 'training/image_2'

cfg.data.test.type = 'KittyTinyDataset'
cfg.data.test.data_root = '/content/kitti_tiny/'
cfg.data.test.ann_file = 'val.txt'
cfg.data.test.img_prefix = 'training/image_2'

# class의 갯수 수정. 
cfg.model.roi_head.bbox_head.num_classes = 4
# pretrained 모델
cfg.load_from = 'checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'

# 학습 weight 파일로 로그를 저장하기 위한 디렉토리 설정. 
cfg.work_dir = './tutorial_exps'

# 학습율 변경 환경 파라미터 설정. 
cfg.optimizer.lr = 0.02 / 8

cfg.lr_config.warmup = None
cfg.log_config.interval = 10

# config 수행 시마다 policy값이 없어지는 bug로 인하여 설정. 
cfg.lr_config.policy = 'step'

# Change the evaluation metric since we use customized dataset.
cfg.evaluation.metric = 'mAP'
# We can set the evaluation interval to reduce the evaluation times
cfg.evaluation.interval = 12
# We can set the checkpoint saving interval to reduce the storage cost
cfg.checkpoint_config.interval = 12

# Set seed thus the results are more reproducible
cfg.seed = 0
set_random_seed(0, deterministic=False)
cfg.gpu_ids = range(1)


# We can initialize the logger for training and have a look
# at the final config used for training
print(f'Config:\n{cfg.pretty_text}')



# Config:
model = dict(
    type='FasterRCNN',
    pretrained='torchvision://resnet50',
    backbone=dict(
        type='ResNet',
        depth=50,
        num_stages=4,
        out_indices=(0, 1, 2, 3),
        frozen_stages=1,
        norm_cfg=dict(type='BN', requires_grad=True),
        norm_eval=True,
        style='pytorch',
        pretrained='torchvision://resnet50'),
    neck=dict(
        type='FPN',
        in_channels=[256, 512, 1024, 2048],
        out_channels=256,
        num_outs=5),
    rpn_head=dict(
        type='RPNHead',
        in_channels=256,
        feat_channels=256,
        anchor_generator=dict(
            type='AnchorGenerator',
            scales=[8],
            ratios=[0.5, 1.0, 2.0],
            strides=[4, 8, 16, 32, 64]),
        bbox_coder=dict(
            type='DeltaXYWHBBoxCoder',
            target_means=[0.0, 0.0, 0.0, 0.0],
            target_stds=[1.0, 1.0, 1.0, 1.0]),
        loss_cls=dict(
            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
    roi_head=dict(
        type='StandardRoIHead',
        bbox_roi_extractor=dict(
            type='SingleRoIExtractor',
            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
            out_channels=256,
            featmap_strides=[4, 8, 16, 32]),
        bbox_head=dict(
            type='Shared2FCBBoxHead',
            in_channels=256,
            fc_out_channels=1024,
            roi_feat_size=7,
            num_classes=4,
            bbox_coder=dict(
                type='DeltaXYWHBBoxCoder',
                target_means=[0.0, 0.0, 0.0, 0.0],
                target_stds=[0.1, 0.1, 0.2, 0.2]),
            reg_class_agnostic=False,
            loss_cls=dict(
                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
            loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
        train_cfg=dict(
            assigner=dict(
                type='MaxIoUAssigner',
                pos_iou_thr=0.5,
                neg_iou_thr=0.5,
                min_pos_iou=0.5,
                match_low_quality=False,
                ignore_iof_thr=-1),
            sampler=dict(
                type='RandomSampler',
                num=512,
                pos_fraction=0.25,
                neg_pos_ub=-1,
                add_gt_as_proposals=True),
            pos_weight=-1,
            debug=False),
        test_cfg=dict(
            score_thr=0.05,
            nms=dict(type='nms', iou_threshold=0.5),
            max_per_img=100),
        pretrained='torchvision://resnet50'),
    train_cfg=dict(
        rpn=dict(
            assigner=dict(
                type='MaxIoUAssigner',
                pos_iou_thr=0.7,
                neg_iou_thr=0.3,
                min_pos_iou=0.3,
                match_low_quality=True,
                ignore_iof_thr=-1),
            sampler=dict(
                type='RandomSampler',
                num=256,
                pos_fraction=0.5,
                neg_pos_ub=-1,
                add_gt_as_proposals=False),
            allowed_border=-1,
            pos_weight=-1,
            debug=False),
        rpn_proposal=dict(
            nms_pre=2000,
            max_per_img=1000,
            nms=dict(type='nms', iou_threshold=0.7),
            min_bbox_size=0),
        rcnn=dict(
            assigner=dict(
                type='MaxIoUAssigner',
                pos_iou_thr=0.5,
                neg_iou_thr=0.5,
                min_pos_iou=0.5,
                match_low_quality=False,
                ignore_iof_thr=-1),
            sampler=dict(
                type='RandomSampler',
                num=512,
                pos_fraction=0.25,
                neg_pos_ub=-1,
                add_gt_as_proposals=True),
            pos_weight=-1,
            debug=False)),
    test_cfg=dict(
        rpn=dict(
            nms_pre=1000,
            max_per_img=1000,
            nms=dict(type='nms', iou_threshold=0.7),
            min_bbox_size=0),
        rcnn=dict(
            score_thr=0.05,
            nms=dict(type='nms', iou_threshold=0.5),
            max_per_img=100)))
dataset_type = 'KittyTinyDataset'
data_root = '/content/kitti_tiny/'
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(
        type='Normalize',
        mean=[123.675, 116.28, 103.53],
        std=[58.395, 57.12, 57.375],
        to_rgb=True),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(
                type='Normalize',
                mean=[123.675, 116.28, 103.53],
                std=[58.395, 57.12, 57.375],
                to_rgb=True),
            dict(type='Pad', size_divisor=32),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img'])
        ])
]
data = dict(
    samples_per_gpu=2,
    workers_per_gpu=2,
    train=dict(
        type='KittyTinyDataset',
        ann_file='train.txt',
        img_prefix='training/image_2',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(type='LoadAnnotations', with_bbox=True),
            dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
            dict(type='RandomFlip', flip_ratio=0.5),
            dict(
                type='Normalize',
                mean=[123.675, 116.28, 103.53],
                std=[58.395, 57.12, 57.375],
                to_rgb=True),
            dict(type='Pad', size_divisor=32),
            dict(type='DefaultFormatBundle'),
            dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
        ],
        data_root='/content/kitti_tiny/'),
    val=dict(
        type='KittyTinyDataset',
        ann_file='val.txt',
        img_prefix='training/image_2',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=32),
                    dict(type='ImageToTensor', keys=['img']),
                    dict(type='Collect', keys=['img'])
                ])
        ],
        data_root='/content/kitti_tiny/'),
    test=dict(
        type='KittyTinyDataset',
        ann_file='val.txt',
        img_prefix='training/image_2',
        pipeline=[
            dict(type='LoadImageFromWebcam'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=32),
                    dict(type='DefaultFormatBundle'),
                    dict(type='Collect', keys=['img'])
                ])
        ],
        data_root='/content/kitti_tiny/'))
evaluation = dict(interval=12, metric='mAP', by_epoch=True)
optimizer = dict(type='SGD', lr=0.0025, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None, type='OptimizerHook')
lr_config = dict(
    warmup=None,
    warmup_iters=500,
    warmup_ratio=0.001,
    step=[8, 11],
    type='StepLrUpdaterHook',
    policy='step')
runner = dict(type='EpochBasedRunner', max_epochs=12)
checkpoint_config = dict(interval=12, type='CheckpointHook')
log_config = dict(interval=10, hooks=[dict(type='TextLoggerHook')])
custom_hooks = [dict(type='NumClassCheckHook')]
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = 'checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'
resume_from = None
workflow = [('train', 1)]
work_dir = './tutorial_exps'
seed = 0
gpu_ids = range(0, 1)

Config에서 설정한 Dataset과 Model, 동적 학습율, Pipeline 설정에 따라 모델 학습 수행.

  • train용 Dataset을 생성하고 이를 이용하여 학습 수행.
from mmdet.datasets import build_dataset
from mmdet.models import build_detector
from mmdet.apis import train_detector

# train용 Dataset 생성. 
datasets = [build_dataset(cfg.data.train)]
datasets

# [
#  KittyTinyDataset Train dataset with number of images 50, and instance counts: 
#  +----------+-------+-----------+-------+----------------+-------+-------------+-------+---------------+-------+
#  | category | count | category  | count | category       | count | category    | count | category      | count |
#  +----------+-------+-----------+-------+----------------+-------+-------------+-------+---------------+-------+
#  | 0 [Car]  | 147   | 1 [Truck] | 7     | 2 [Pedestrian] | 23    | 3 [Cyclist] | 7     | -1 background | 0     |
#  +----------+-------+-----------+-------+----------------+-------+-------------+-------+---------------+-------+]
datasets[0].CLASSES
# ('Car', 'Truck', 'Pedestrian', 'Cyclist')
model = build_detector(cfg.model, train_cfg=cfg.get('train_cfg'), test_cfg=cfg.get('test_cfg'))
model.CLASSES = datasets[0].CLASSES

# /usr/local/lib/python3.7/dist-packages/mmdet-2.12.0-py3.7.egg/mmdet/models/backbones/resnet.py:400: UserWarning: DeprecationWarning: pretrained is a deprecated, please use "init_cfg" instead
#  warnings.warn('DeprecationWarning: pretrained is a deprecated, '
# 주의, config에 pretrained 모델 지정이 상대 경로로 설정됨 cfg.load_from = 'checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'
# 아래와 같이 %cd mmdetection 지정 필요. 
 
%cd mmdetection 

mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir))
# epochs는 config의 runner 파라미터로 지정됨. 기본 12회 
train_detector(model, datasets, cfg, distributed=False, validate=True)



/content/mmdetection
2021-05-27 02:01:03,488 - mmdet - INFO - load checkpoint from checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth
2021-05-27 02:01:03,489 - mmdet - INFO - Use load_from_local loader
##### self.data_root: /content/kitti_tiny/ self.ann_file: /content/kitti_tiny/val.txt self.img_prefix: /content/kitti_tiny/training/image_2
#### ann_file: /content/kitti_tiny/val.txt
2021-05-27 02:01:03,630 - mmdet - WARNING - The model and loaded state dict do not match exactly

size mismatch for roi_head.bbox_head.fc_cls.weight: copying a param with shape torch.Size([81, 1024]) from checkpoint, the shape in current model is torch.Size([5, 1024]).
size mismatch for roi_head.bbox_head.fc_cls.bias: copying a param with shape torch.Size([81]) from checkpoint, the shape in current model is torch.Size([5]).
size mismatch for roi_head.bbox_head.fc_reg.weight: copying a param with shape torch.Size([320, 1024]) from checkpoint, the shape in current model is torch.Size([16, 1024]).
size mismatch for roi_head.bbox_head.fc_reg.bias: copying a param with shape torch.Size([320]) from checkpoint, the shape in current model is torch.Size([16]).
2021-05-27 02:01:03,639 - mmdet - INFO - Start running, host: root@d7fd648a5af0, work_dir: /content/mmdetection/tutorial_exps
2021-05-27 02:01:03,640 - mmdet - INFO - workflow: [('train', 1)], max: 12 epochs
2021-05-27 02:01:08,457 - mmdet - INFO - Epoch [1][10/25]	lr: 2.500e-03, eta: 0:02:18, time: 0.476, data_time: 0.229, memory: 2226, loss_rpn_cls: 0.0275, loss_rpn_bbox: 0.0179, loss_cls: 0.7554, acc: 73.3594, loss_bbox: 0.4131, loss: 1.2139
2021-05-27 02:01:10,877 - mmdet - INFO - Epoch [1][20/25]	lr: 2.500e-03, eta: 0:01:40, time: 0.242, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0178, loss_rpn_bbox: 0.0128, loss_cls: 0.2395, acc: 92.2559, loss_bbox: 0.3446, loss: 0.6147
2021-05-27 02:01:16,800 - mmdet - INFO - Epoch [2][10/25]	lr: 2.500e-03, eta: 0:01:29, time: 0.464, data_time: 0.228, memory: 2226, loss_rpn_cls: 0.0151, loss_rpn_bbox: 0.0156, loss_cls: 0.2097, acc: 93.5547, loss_bbox: 0.3205, loss: 0.5609
2021-05-27 02:01:19,231 - mmdet - INFO - Epoch [2][20/25]	lr: 2.500e-03, eta: 0:01:20, time: 0.243, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0133, loss_rpn_bbox: 0.0131, loss_cls: 0.1617, acc: 94.0332, loss_bbox: 0.2242, loss: 0.4123
2021-05-27 02:01:25,113 - mmdet - INFO - Epoch [3][10/25]	lr: 2.500e-03, eta: 0:01:15, time: 0.462, data_time: 0.227, memory: 2226, loss_rpn_cls: 0.0048, loss_rpn_bbox: 0.0117, loss_cls: 0.1387, acc: 95.1465, loss_bbox: 0.1990, loss: 0.3543
2021-05-27 02:01:27,568 - mmdet - INFO - Epoch [3][20/25]	lr: 2.500e-03, eta: 0:01:10, time: 0.246, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0069, loss_rpn_bbox: 0.0125, loss_cls: 0.1741, acc: 93.4863, loss_bbox: 0.2716, loss: 0.4651
2021-05-27 02:01:33,510 - mmdet - INFO - Epoch [4][10/25]	lr: 2.500e-03, eta: 0:01:05, time: 0.466, data_time: 0.225, memory: 2226, loss_rpn_cls: 0.0071, loss_rpn_bbox: 0.0151, loss_cls: 0.1503, acc: 94.2578, loss_bbox: 0.2479, loss: 0.4204
2021-05-27 02:01:35,960 - mmdet - INFO - Epoch [4][20/25]	lr: 2.500e-03, eta: 0:01:01, time: 0.245, data_time: 0.013, memory: 2226, loss_rpn_cls: 0.0028, loss_rpn_bbox: 0.0123, loss_cls: 0.1339, acc: 95.1172, loss_bbox: 0.2092, loss: 0.3582
2021-05-27 02:01:41,874 - mmdet - INFO - Epoch [5][10/25]	lr: 2.500e-03, eta: 0:00:57, time: 0.466, data_time: 0.226, memory: 2226, loss_rpn_cls: 0.0038, loss_rpn_bbox: 0.0100, loss_cls: 0.1278, acc: 94.9512, loss_bbox: 0.2143, loss: 0.3559
2021-05-27 02:01:44,334 - mmdet - INFO - Epoch [5][20/25]	lr: 2.500e-03, eta: 0:00:53, time: 0.246, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0071, loss_rpn_bbox: 0.0119, loss_cls: 0.1317, acc: 94.7852, loss_bbox: 0.2035, loss: 0.3542
2021-05-27 02:01:50,296 - mmdet - INFO - Epoch [6][10/25]	lr: 2.500e-03, eta: 0:00:49, time: 0.467, data_time: 0.228, memory: 2226, loss_rpn_cls: 0.0014, loss_rpn_bbox: 0.0094, loss_cls: 0.1039, acc: 96.0059, loss_bbox: 0.1820, loss: 0.2968
2021-05-27 02:01:52,762 - mmdet - INFO - Epoch [6][20/25]	lr: 2.500e-03, eta: 0:00:45, time: 0.247, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0039, loss_rpn_bbox: 0.0104, loss_cls: 0.1045, acc: 96.0840, loss_bbox: 0.1877, loss: 0.3065
2021-05-27 02:01:58,690 - mmdet - INFO - Epoch [7][10/25]	lr: 2.500e-03, eta: 0:00:41, time: 0.465, data_time: 0.225, memory: 2226, loss_rpn_cls: 0.0019, loss_rpn_bbox: 0.0106, loss_cls: 0.1018, acc: 96.2402, loss_bbox: 0.1835, loss: 0.2978
2021-05-27 02:02:01,159 - mmdet - INFO - Epoch [7][20/25]	lr: 2.500e-03, eta: 0:00:38, time: 0.247, data_time: 0.011, memory: 2226, loss_rpn_cls: 0.0039, loss_rpn_bbox: 0.0125, loss_cls: 0.0970, acc: 96.2500, loss_bbox: 0.1859, loss: 0.2993
2021-05-27 02:02:07,094 - mmdet - INFO - Epoch [8][10/25]	lr: 2.500e-03, eta: 0:00:33, time: 0.464, data_time: 0.225, memory: 2226, loss_rpn_cls: 0.0029, loss_rpn_bbox: 0.0088, loss_cls: 0.0875, acc: 96.6309, loss_bbox: 0.1584, loss: 0.2575
2021-05-27 02:02:09,550 - mmdet - INFO - Epoch [8][20/25]	lr: 2.500e-03, eta: 0:00:30, time: 0.246, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0020, loss_rpn_bbox: 0.0089, loss_cls: 0.0976, acc: 96.4160, loss_bbox: 0.1765, loss: 0.2850
2021-05-27 02:02:15,504 - mmdet - INFO - Epoch [9][10/25]	lr: 2.500e-04, eta: 0:00:26, time: 0.466, data_time: 0.225, memory: 2226, loss_rpn_cls: 0.0029, loss_rpn_bbox: 0.0093, loss_cls: 0.0829, acc: 96.8457, loss_bbox: 0.1472, loss: 0.2423
2021-05-27 02:02:17,968 - mmdet - INFO - Epoch [9][20/25]	lr: 2.500e-04, eta: 0:00:23, time: 0.246, data_time: 0.013, memory: 2226, loss_rpn_cls: 0.0012, loss_rpn_bbox: 0.0071, loss_cls: 0.0760, acc: 97.2168, loss_bbox: 0.1358, loss: 0.2201
2021-05-27 02:02:23,924 - mmdet - INFO - Epoch [10][10/25]	lr: 2.500e-04, eta: 0:00:19, time: 0.466, data_time: 0.226, memory: 2226, loss_rpn_cls: 0.0027, loss_rpn_bbox: 0.0095, loss_cls: 0.0899, acc: 96.4160, loss_bbox: 0.1554, loss: 0.2575
2021-05-27 02:02:26,391 - mmdet - INFO - Epoch [10][20/25]	lr: 2.500e-04, eta: 0:00:15, time: 0.247, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0013, loss_rpn_bbox: 0.0064, loss_cls: 0.0731, acc: 97.2168, loss_bbox: 0.1324, loss: 0.2132
2021-05-27 02:02:32,334 - mmdet - INFO - Epoch [11][10/25]	lr: 2.500e-04, eta: 0:00:11, time: 0.465, data_time: 0.225, memory: 2226, loss_rpn_cls: 0.0037, loss_rpn_bbox: 0.0084, loss_cls: 0.0911, acc: 96.3965, loss_bbox: 0.1458, loss: 0.2490
2021-05-27 02:02:34,811 - mmdet - INFO - Epoch [11][20/25]	lr: 2.500e-04, eta: 0:00:08, time: 0.248, data_time: 0.012, memory: 2226, loss_rpn_cls: 0.0012, loss_rpn_bbox: 0.0087, loss_cls: 0.0703, acc: 97.3047, loss_bbox: 0.1367, loss: 0.2168
2021-05-27 02:02:40,779 - mmdet - INFO - Epoch [12][10/25]	lr: 2.500e-05, eta: 0:00:04, time: 0.469, data_time: 0.226, memory: 2226, loss_rpn_cls: 0.0016, loss_rpn_bbox: 0.0065, loss_cls: 0.0630, acc: 97.5586, loss_bbox: 0.1168, loss: 0.1879
2021-05-27 02:02:43,234 - mmdet - INFO - Epoch [12][20/25]	lr: 2.500e-05, eta: 0:00:01, time: 0.245, data_time: 0.013, memory: 2226, loss_rpn_cls: 0.0016, loss_rpn_bbox: 0.0070, loss_cls: 0.0700, acc: 97.2949, loss_bbox: 0.1068, loss: 0.1853
[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 25/25, 13.7 task/s, elapsed: 2s, ETA:     0s
---------------iou_thr: 0.5---------------
2021-05-27 02:02:46,577 - mmdet - INFO - 
+------------+-----+------+--------+-------+
| class      | gts | dets | recall | ap    |
+------------+-----+------+--------+-------+
| Car        | 62  | 145  | 0.935  | 0.829 |
| Truck      | 3   | 16   | 1.000  | 0.600 |
| Pedestrian | 13  | 49   | 0.923  | 0.781 |
| Cyclist    | 7   | 51   | 0.714  | 0.180 |
+------------+-----+------+--------+-------+
| mAP        |     |      |        | 0.598 |
+------------+-----+------+--------+-------+
2021-05-27 02:02:46,581 - mmdet - INFO - Saving checkpoint at 12 epochs
2021-05-27 02:02:47,549 - mmdet - INFO - Epoch(val) [12][25]	AP50: 0.5980, mAP: 0.5975

학습된 model을 이용하여 inference 수행.

from mmdet.apis import inference_detector, init_detector, show_result_pyplot

# BGR Image 사용 
img = cv2.imread('/content/kitti_tiny/training/image_2/000068.jpeg')

model.cfg = cfg

result = inference_detector(model, img)
show_result_pyplot(model, img, result)

 

 

반응형
728x90
반응형
# https://github.com/open-mmlab/mmdetection/blob/master/demo/MMDet_Tutorial.ipynb 에서 발췌
# mmcv를 위해서 mmcv-full을 먼저 설치해야 함. 
!pip install mmcv-full
# mmdetection 설치 
!git clone https://github.com/open-mmlab/mmdetection.git
!cd mmdetection; python setup.py install
# 아래를 수행하기 전에 kernel을 restart 해야 함. 
from mmdet.apis import init_detector, inference_detector
import mmcv

KITTI Dataset 다운로드

  • 작은 용량의 KITTI Dataset을 다운로드하고 /content 밑에 압축 해제
# 튜토리얼용 dataset
!wget https://download.openmmlab.com/mmdetection/data/kitti_tiny.zip
!unzip kitti_tiny.zip > /dev/null
import matplotlib.pyplot as plt
import cv2

img = cv2.cvtColor(cv2.imread('/content/kitti_tiny/training/image_2/000068.jpeg'), cv2.COLOR_BGR2RGB)
plt.figure(figsize=(15, 10))
plt.imshow(img)

KITTI DATA FORMAT 확인

  • 첫번째 컬럼은 해당 오브젝트의 클래스 명.
  • 5번째~8번째가 BOUNDING BOX 정보임. 좌상단(xmin, ymin), 우하단(xmax, ymax) 좌표 임.
!cat /content/kitti_tiny/training/label_2/000068.txt

# Car 0.25 0 1.94 69.26 200.28 435.08 374.00 1.46 1.62 4.04 -3.00 1.79 6.98 1.55
# Car 0.80 1 2.26 0.00 209.20 198.59 374.00 1.46 1.58 3.72 -5.44 1.85 6.22 1.56
# Cyclist 0.97 0 2.34 1210.28 199.77 1241.00 374.00 1.55 0.57 1.73 4.04 1.69 3.57 -3.14
# Car 0.00 2 1.68 478.18 187.68 549.54 249.43 1.57 1.60 3.99 -2.73 2.03 20.96 1.55
# Car 0.00 1 1.66 530.03 187.79 573.10 226.09 1.52 1.54 3.68 -2.53 2.20 31.50 1.58
# Van 0.00 1 1.63 547.61 171.12 584.05 212.41 2.47 1.98 5.81 -2.79 2.41 46.44 1.57
# Car 0.00 1 -0.16 667.74 182.35 790.82 230.38 1.62 1.65 4.14 4.19 1.99 25.95 0.00
# Car 0.00 2 -0.11 657.37 184.48 763.34 221.64 1.55 1.66 4.47 4.35 2.10 32.00 0.02
# Car 0.00 1 -0.01 637.45 180.34 714.44 212.34 1.69 1.76 4.12 3.59 2.12 39.79 0.08
# Van 0.00 1 1.61 572.52 175.02 596.26 199.95 2.13 1.91 6.40 -2.28 2.36 65.43 1.57
# Van 0.00 1 1.77 380.78 167.69 523.29 288.56 1.95 1.75 4.63 -2.89 1.90 14.05 1.57
# Cyclist 0.00 1 1.09 958.95 167.55 1036.88 254.43 1.68 0.53 1.96 7.95 1.59 14.95 1.57

KITTI annotation 파일들의 리스트들을 가지는 파일 확인.

!cat /content/kitti_tiny/train.txt
000000
000001
000002
000003
000004
000005
000006
000007
000008
000009
000010
000011
000012
000013
000014
000015
000016
000017
000018
000019
000020
000021
000022
000023
000024
000025
000026
000027
000028
000029
000030
000031
000032
000033
000034
000035
000036
000037
000038
000039
000040
000041
000042
000043
000044
000045
000046
000047
000048
000049

mmdetection의 중립 annotation 포맷 변환. 해당 포맷은 텍스트로 변환하지 않음. 바로 메모리 상의 list로 생성됨.

filename, width, height, ann을 Key로 가지는 Dictionary를 이미지 개수대로 가지는 list 생성.

  • filename: 이미지 파일명(디렉토리는 포함하지 않음)
  • width: 이미지 너비
  • height: 이미지 높이
  • ann: bbounding box와 label에 대한 정보를 가지는 Dictionary
    • bboxes: 하나의 이미지에 있는 여러 Object 들의 numpy array. 4개의 좌표값(좌상단, 우하단)을 가지고, 해당 이미지에 n개의 Object들이 있을 경우 array의 shape는 (n, 4)
    • labels: 하나의 이미지에 있는 여러 Object들의 numpy array. shape는 (n, )
    • bboxes_ignore: 학습에 사용되지 않고 무시하는 bboxes. 무시하는 bboxes의 개수가 k개이면 shape는 (k, 4)
    • labels_ignore: 학습에 사용되지 않고 무시하는 labels. 무시하는 bboxes의 개수가 k개이면 shape는 (k,)

kitti Dataset을 중립 데이터형태로 변환하여 메모리 로드

 

# 원본 kitti Dataset는 10개의 Class로 되어 있음. 'Car Van Truck Pedestrian Person_sitting Cyclist Tram Misc DontCare'
CLASSES = ('Car', 'Truck', 'Pedestrian', 'Cyclist')
cat2label = {k:i for i, k in enumerate(CLASSES)}
print(cat2label)
cat2label['Car']

# {'Car': 0, 'Truck': 1, 'Pedestrian': 2, 'Cyclist': 3}
# 0
image_list = mmcv.list_from_file('/content/kitti_tiny/train.txt')

lines = mmcv.list_from_file('/content/kitti_tiny/training/label_2/000064.txt')
#print(lines)
content = [line.strip().split(' ') for line in lines]
bbox_names = [x[0] for x in content]
#print(bbox_names)
bboxes = [ [float(info) for info in x[4:8]] for x in content]
print(bboxes)

# [[657.65, 179.93, 709.86, 219.92], [731.51, 180.39, 882.28, 275.8], [715.18, 175.63, 762.77, 203.9], [816.58, 59.74, 1112.51, 266.07], [626.78, 174.27, 647.77, 192.18], [546.19, 168.97, 554.01, 177.09]]

dataset, config의 관계를 이해하는 것이 가장 중요하다.

custom dataset 생성 주요 로직

0. dataset을 위한 config 설정(data_root, ann_file, img_prefix)

- build_dataset()으로 아래 작업해줌

1. customdataset객체를 mmdetection framework에 등록

- @DATASETS.register_module(force=True) customdataset을 상속한 클래스 생성

- load_annotations()을 재정의 하여 middle format으로 원본 소스 변환

2. config에 설정된 주요값으로 customdataset 객체 생성

import copy
import os.path as osp
import cv2

import mmcv
import numpy as np

from mmdet.datasets.builder import DATASETS
from mmdet.datasets.custom import CustomDataset

# 반드시 아래 Decorator 설정 할것.@DATASETS.register_module() 설정 시 force=True를 입력하지 않으면 Dataset 재등록 불가. 
@DATASETS.register_module(force=True)
class KittyTinyDataset(CustomDataset):
  CLASSES = ('Car', 'Truck', 'Pedestrian', 'Cyclist')
  
  ##### self.data_root: /content/kitti_tiny/ self.ann_file: /content/kitti_tiny/train.txt self.img_prefix: /content/kitti_tiny/training/image_2
  #### ann_file: /content/kitti_tiny/train.txt
  # annotation에 대한 모든 파일명을 가지고 있는 텍스트 파일을 __init__(self, ann_file)로 입력 받고, 이 self.ann_file이 load_annotations()의 인자로 입력
  def load_annotations(self, ann_file):
    print('##### self.data_root:', self.data_root, 'self.ann_file:', self.ann_file, 'self.img_prefix:', self.img_prefix)
    print('#### ann_file:', ann_file)
    cat2label = {k:i for i, k in enumerate(self.CLASSES)}
    image_list = mmcv.list_from_file(self.ann_file)
    # 포맷 중립 데이터를 담을 list 객체
    data_infos = []
    
    for image_id in image_list:
      filename = '{0:}/{1:}.jpeg'.format(self.img_prefix, image_id)
      # 원본 이미지의 너비, 높이를 image를 직접 로드하여 구함. 
      image = cv2.imread(filename)
      height, width = image.shape[:2]
      # 개별 image의 annotation 정보 저장용 Dict 생성. key값 filename 에는 image의 파일명만 들어감(디렉토리는 제외)
      data_info = {'filename': str(image_id) + '.jpeg',
                   'width': width, 'height': height}
      # 개별 annotation이 있는 서브 디렉토리의 prefix 변환. 
      label_prefix = self.img_prefix.replace('image_2', 'label_2')
      # 개별 annotation 파일을 1개 line 씩 읽어서 list 로드 
      lines = mmcv.list_from_file(osp.join(label_prefix, str(image_id)+'.txt'))

      # 전체 lines를 개별 line별 공백 레벨로 parsing 하여 다시 list로 저장. content는 list의 list형태임.
      # ann 정보는 numpy array로 저장되나 텍스트 처리나 데이터 가공이 list 가 편하므로 일차적으로 list로 변환 수행.   
      content = [line.strip().split(' ') for line in lines]
      # 오브젝트의 클래스명은 bbox_names로 저장. 
      bbox_names = [x[0] for x in content]
      # bbox 좌표를 저장
      bboxes = [ [float(info) for info in x[4:8]] for x in content]

      # 클래스명이 해당 사항이 없는 대상 Filtering out, 'DontCare'sms ignore로 별도 저장.
      gt_bboxes = []
      gt_labels = []
      gt_bboxes_ignore = []
      gt_labels_ignore = []

      for bbox_name, bbox in zip(bbox_names, bboxes):
        # 만약 bbox_name이 클래스명에 해당 되면, gt_bboxes와 gt_labels에 추가, 그렇지 않으면 gt_bboxes_ignore, gt_labels_ignore에 추가
        if bbox_name in cat2label:
          gt_bboxes.append(bbox)
          # gt_labels에는 class id를 입력
          gt_labels.append(cat2label[bbox_name])
        else:
          gt_bboxes_ignore.append(bbox)
          gt_labels_ignore.append(-1)
      # 개별 image별 annotation 정보를 가지는 Dict 생성. 해당 Dict의 value값은 모두 np.array임. 
      data_anno = {
          'bboxes': np.array(gt_bboxes, dtype=np.float32).reshape(-1, 4),
          'labels': np.array(gt_labels, dtype=np.long),
          'bboxes_ignore': np.array(gt_bboxes_ignore, dtype=np.float32).reshape(-1, 4),
          'labels_ignore': np.array(gt_labels_ignore, dtype=np.long)
      }
      # image에 대한 메타 정보를 가지는 data_info Dict에 'ann' key값으로 data_anno를 value로 저장. 
      data_info.update(ann=data_anno)
      # 전체 annotation 파일들에 대한 정보를 가지는 data_infos에 data_info Dict를 추가
      data_infos.append(data_info)

    return data_infos

data_root, ann_file, img_prefix의 활용

- 학습용, 검증용, 테스트용으로 dataset이 만들어져야한다.

소스 데이터들의 학습용, 검증용, 테스트용 분리유형

- image 들과 annotation파일이 학습용, 검증용, 테스트용 디렉토리에 별도로 분리

- 별도의 메타 파일에서 각 image, annotation 지정

- image들 각 용도에 맞게 디렉토리별 분리, annotation은 각 용도별 하나만 가짐(3개 json)

* img_prefix는 여러개의 image들을 포함할 수 있는 디렉토리 형태로 지정되지만, ann_file은 단하나만 지정할 수 있음

# 학습 data config 셋팅
train.data_root=/content/kitti_tiny
train.ann_file='train.txt'
train.img_prefix='training/image_2'

# 학습 kitti data config 셋팅

train_dataset = kittiYinydataset(ann_file = train.data_root,
				data_root = train.ann_file,
				img_prefix = train.img_prefix )

 

 

 

 

 

 

 

반응형
728x90
반응형

mmdetection의 주요구성요소

dataset을 model에 입력시켜야 함.

model을 weight값을 갱신하면서 고도화시켜야함

dataset에는 image, detection을 위한 annotation이 있고, dataset config file에 저장이 되어있음.

 전체적으로 config 파일에 의해서 통제가 됨.

 

dataset의 변환도 중요함

pv, coco 등 많은 dataset이 있는데 이것을 mmdetection이 받아들이기 쉽게 transformation해야 함.

 

mmdetection dataset 지원

=> mmdetection은 다양한 유형의 dataset을 변환 클래스를 통해서 지원함

custom dataset, cocodataset, vocdataset, widerfaceDataset, XMLdataset 중

customdataset, cocodataset이 쉽게 적용될 수 있는 dataset임

 

pascal voc 데이터 세트 특징

ds_name = 'voc07'으로 pascal 형식을 그대로 따름

VOCdevkit - voc2012 - annotations, imageset, jpegimage, segmentationclass, segmentationobject

=> 디렉토리 의 구조를 맞춰줘야함. 차라리 custom dataset 을 parsing 하는게 나음

 

ms-coco 데이터 세트 특징

모든 이미지들에 대해 단 1개의 annotation 파일을 가짐

train, val, test전부 json 포맷인 한개의 파일, 한 라인으로 구성됨

json { info, license,

images {id, file_name, width, height},

annotations {image, object id, segmentation, bounding box, pixel area},

categories (80개 object categories 에 대한 id, names, group)

}

 

custom dataset에 사용되는 포멧

- 모든 이미지에 대한 annotation정보들을 list객체로 가짐

- list 내 개별 원소는 dict로 구성되면 개별 dict는 1개 이미지에 대한 annotation 정보를 가짐

- 1개 이미지는 여러개의 object bbox와 labels annotation 정보들을 개별 dict로 가짐

- 1개 이미지의 object bbox와 2차원 array로 , label은 1차원 array로 구성됨.

 

반응형
728x90
반응형

Video Inference 수행

  • mmdetection의 video_demo.py 대로 Video inference 수행시 image 처리 시간이 상대적으로 오래 걸림.
  • 이미지 처리 로직을 변경하여 적용
!wget -O /content/data/John_Wick_small.mp4 https://github.com/chulminkw/DLCV/blob/master/data/video/John_Wick_small.mp4?raw=true
from mmdet.apis import init_detector, inference_detector
import mmcv

config_file = '/content/mmdetection/configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py'
checkpoint_file = '/content/mmdetection/checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'
model = init_detector(config_file, checkpoint_file, device='cuda:0')
# https://github.com/open-mmlab/mmdetection/blob/master/demo/video_demo.py 대로 video detection 수행. 

import cv2

video_reader = mmcv.VideoReader('/content/data/John_Wick_small.mp4')
video_writer = None
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video_writer = cv2.VideoWriter('/content/data/John_Wick_small_out1.mp4', fourcc, video_reader.fps,(video_reader.width, video_reader.height))

for frame in mmcv.track_iter_progress(video_reader):
  result = inference_detector(model, frame)
  frame = model.show_result(frame, result, score_thr=0.4)

  video_writer.write(frame)

if video_writer:
        video_writer.release()

Custom된 frame처리 로직을 적용하여 Video Inference 수행.

  • 기존에 사용한 get_detected_img()를 그대로 사용함.
# model과 원본 이미지 array, filtering할 기준 class confidence score를 인자로 가지는 inference 시각화용 함수 생성. 
import numpy as np

# 0부터 순차적으로 클래스 매핑된 label 적용. 
labels_to_names_seq = {0:'person',1:'bicycle',2:'car',3:'motorbike',4:'aeroplane',5:'bus',6:'train',7:'truck',8:'boat',9:'traffic light',10:'fire hydrant',
                        11:'stop sign',12:'parking meter',13:'bench',14:'bird',15:'cat',16:'dog',17:'horse',18:'sheep',19:'cow',20:'elephant',
                        21:'bear',22:'zebra',23:'giraffe',24:'backpack',25:'umbrella',26:'handbag',27:'tie',28:'suitcase',29:'frisbee',30:'skis',
                        31:'snowboard',32:'sports ball',33:'kite',34:'baseball bat',35:'baseball glove',36:'skateboard',37:'surfboard',38:'tennis racket',39:'bottle',40:'wine glass',
                        41:'cup',42:'fork',43:'knife',44:'spoon',45:'bowl',46:'banana',47:'apple',48:'sandwich',49:'orange',50:'broccoli',
                        51:'carrot',52:'hot dog',53:'pizza',54:'donut',55:'cake',56:'chair',57:'sofa',58:'pottedplant',59:'bed',60:'diningtable',
                        61:'toilet',62:'tvmonitor',63:'laptop',64:'mouse',65:'remote',66:'keyboard',67:'cell phone',68:'microwave',69:'oven',70:'toaster',
                        71:'sink',72:'refrigerator',73:'book',74:'clock',75:'vase',76:'scissors',77:'teddy bear',78:'hair drier',79:'toothbrush' }

def get_detected_img(model, img_array,  score_threshold=0.3, is_print=True):
  # 인자로 들어온 image_array를 복사. 
  draw_img = img_array.copy()
  bbox_color=(0, 255, 0)
  text_color=(0, 0, 255)

  # model과 image array를 입력 인자로 inference detection 수행하고 결과를 results로 받음. 
  # results는 80개의 2차원 array(shape=(오브젝트갯수, 5))를 가지는 list. 
  results = inference_detector(model, img_array)

  # 80개의 array원소를 가지는 results 리스트를 loop를 돌면서 개별 2차원 array들을 추출하고 이를 기반으로 이미지 시각화 
  # results 리스트의 위치 index가 바로 COCO 매핑된 Class id. 여기서는 result_ind가 class id
  # 개별 2차원 array에 오브젝트별 좌표와 class confidence score 값을 가짐. 
  for result_ind, result in enumerate(results):
    # 개별 2차원 array의 row size가 0 이면 해당 Class id로 값이 없으므로 다음 loop로 진행. 
    if len(result) == 0:
      continue
    
    # 2차원 array에서 5번째 컬럼에 해당하는 값이 score threshold이며 이 값이 함수 인자로 들어온 score_threshold 보다 낮은 경우는 제외. 
    result_filtered = result[np.where(result[:, 4] > score_threshold)]
    
    # 해당 클래스 별로 Detect된 여러개의 오브젝트 정보가 2차원 array에 담겨 있으며, 이 2차원 array를 row수만큼 iteration해서 개별 오브젝트의 좌표값 추출. 
    for i in range(len(result_filtered)):
      # 좌상단, 우하단 좌표 추출. 
      left = int(result_filtered[i, 0])
      top = int(result_filtered[i, 1])
      right = int(result_filtered[i, 2])
      bottom = int(result_filtered[i, 3])
      caption = "{}: {:.4f}".format(labels_to_names_seq[result_ind], result_filtered[i, 4])
      cv2.rectangle(draw_img, (left, top), (right, bottom), color=bbox_color, thickness=2)
      cv2.putText(draw_img, caption, (int(left), int(top - 7)), cv2.FONT_HERSHEY_SIMPLEX, 0.37, text_color, 1)
      if is_print:
        print(caption)

  return draw_img
import time

def do_detected_video(model, input_path, output_path, score_threshold, do_print=True):
    
    cap = cv2.VideoCapture(input_path)

    codec = cv2.VideoWriter_fourcc(*'XVID')

    vid_size = (round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    vid_fps = cap.get(cv2.CAP_PROP_FPS)

    vid_writer = cv2.VideoWriter(output_path, codec, vid_fps, vid_size) 

    frame_cnt = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print('총 Frame 갯수:', frame_cnt)
    btime = time.time()
    while True:
        hasFrame, img_frame = cap.read()
        if not hasFrame:
            print('더 이상 처리할 frame이 없습니다.')
            break
        stime = time.time()
        img_frame = get_detected_img(model, img_frame,  score_threshold=score_threshold, is_print=False)
        if do_print:
          print('frame별 detection 수행 시간:', round(time.time() - stime, 4))
        vid_writer.write(img_frame)
    # end of while loop

    vid_writer.release()
    cap.release()

    print('최종 detection 완료 수행 시간:', round(time.time() - btime, 4))
do_detected_video(model, '/content/data/John_Wick_small.mp4', '/content/data/John_Wick_small_out2.mp4', score_threshold=0.4, do_print=True)

frame별 detection 수행 시간: 0.304
frame별 detection 수행 시간: 0.3139
frame별 detection 수행 시간: 0.2853
frame별 detection 수행 시간: 0.2918
frame별 detection 수행 시간: 0.2905
frame별 detection 수행 시간: 0.299
frame별 detection 수행 시간: 0.3006
frame별 detection 수행 시간: 0.2887
frame별 detection 수행 시간: 0.295
frame별 detection 수행 시간: 0.2932
frame별 detection 수행 시간: 0.2991
frame별 detection 수행 시간: 0.303
frame별 detection 수행 시간: 0.2913
frame별 detection 수행 시간: 0.2953
frame별 detection 수행 시간: 0.3003
frame별 detection 수행 시간: 0.3022
frame별 detection 수행 시간: 0.3018
frame별 detection 수행 시간: 0.2954
frame별 detection 수행 시간: 0.2975
frame별 detection 수행 시간: 0.3034
frame별 detection 수행 시간: 0.292
frame별 detection 수행 시간: 0.288
frame별 detection 수행 시간: 0.2939
frame별 detection 수행 시간: 0.2911
frame별 detection 수행 시간: 0.2969
frame별 detection 수행 시간: 0.2903
frame별 detection 수행 시간: 0.2912
frame별 detection 수행 시간: 0.2906
frame별 detection 수행 시간: 0.2947
frame별 detection 수행 시간: 0.2956
frame별 detection 수행 시간: 0.2936
frame별 detection 수행 시간: 0.2939
frame별 detection 수행 시간: 0.2925
frame별 detection 수행 시간: 0.2939
frame별 detection 수행 시간: 0.3
frame별 detection 수행 시간: 0.2862
frame별 detection 수행 시간: 0.2961
frame별 detection 수행 시간: 0.2958
frame별 detection 수행 시간: 0.2943
frame별 detection 수행 시간: 0.2931
frame별 detection 수행 시간: 0.2856
frame별 detection 수행 시간: 0.2911
frame별 detection 수행 시간: 0.2967
frame별 detection 수행 시간: 0.2944
frame별 detection 수행 시간: 0.2933
frame별 detection 수행 시간: 0.2886
frame별 detection 수행 시간: 0.2926
frame별 detection 수행 시간: 0.2991
frame별 detection 수행 시간: 0.2963
frame별 detection 수행 시간: 0.2885
frame별 detection 수행 시간: 0.2917
frame별 detection 수행 시간: 0.292
frame별 detection 수행 시간: 0.3013
frame별 detection 수행 시간: 0.2963
frame별 detection 수행 시간: 0.2903
더 이상 처리할 frame이 없습니다.
최종 detection 완료 수행 시간: 17.738

 

 

 

 

반응형
728x90
반응형
# 버전 확인
import torch
print(torch.__version__)
# mmcv를 위해서 mmcv-full을 설치해야 함. 
!pip install mmcv-full
from google.colab import drive
drive.mount('/content/drive')
!git clone https://github.com/open-mmlab/mmdetection.git
!cd mmdetection; python setup.py install
# 아래를 수행하기 전에 kernel을 restart 해야 함. 
# 런타임 초기화하면 설치한 파일 날라감
from mmdet.apis import init_detector, inference_detector
import mmcv

MS-COCO 데이터 기반으로 Faster RCNN Pretrained 모델을 활용하여 Inference 수행

  • Faster RCNN Pretrained 모델 다운로드
  • Faster RCNN용 Config 파일 설정.
  • Inference 용 모델을 생성하고, Inference 적용
# pretrained weight 모델을 다운로드 받기 위해서 mmdetection/checkpoints 디렉토리를 만듬. 
!cd mmdetection; mkdir checkpoints
# pretrained model faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth 설치
!wget -O /content/mmdetection/checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth http://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth
# pretrained model 경로 확인
!ls -lia /content/mmdetection/checkpoints
# total 163376
# 3951436 drwxr-xr-x  2 root root      4096 Oct 10 13:13 .
# 3932841 drwxr-xr-x 19 root root      4096 Oct 10 13:13 ..
# 3951438 -rw-r--r--  1 root root 167287506 Aug 28  2020 faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth

 

 

# config 파일을 설정하고, 다운로드 받은 pretrained 모델을 checkpoint로 설정. 
config_file = '/content/mmdetection/configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py'
checkpoint_file = '/content/mmdetection/checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'
# config 파일과 pretrained 모델을 기반으로 Detector 모델을 생성. 
from mmdet.apis import init_detector, inference_detector

model = init_detector(config_file, checkpoint_file, device='cuda:0') # gpu 지정
# /usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/builder.py:17: UserWarning: ``build_anchor_generator`` would be deprecated soon, please use ``build_prior_generator`` 
#   '``build_anchor_generator`` would be deprecated soon, please use '
# Use load_from_local loader
# mmdetection은 상대 경로를 인자로 주면 무조건 mmdetection 디렉토리를 기준으로 함. 
%cd mmdetection

from mmdet.apis import init_detector, inference_detector

# init_detector() 인자로 config와 checkpoint를 입력함. 
model = init_detector(config='configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py', checkpoint='checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth')

# /content/mmdetection
# /usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/builder.py:17: UserWarning: ``build_anchor_generator`` would be deprecated soon, please use ``build_prior_generator`` 
#   '``build_anchor_generator`` would be deprecated soon, please use '
# Use load_from_local loader

# mmdetection/configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py - _base_

=> 모델 별로 각각 다른 config를 가지는게 아니라 공통의 config를 지님

mmdetection/configs/_base_/models/fast_rcnn_r50_fpn.py에 neck=dict에 type=fpn으로 되어있음

참고로 나중에 커스컴 할 때 num_classes = n을 바꿔야함, coco dataset이 n개라서

%cd /content
# /content
import cv2
import matplotlib.pyplot as plt
img = '/content/mmdetection/demo/demo.jpg'

img_arr  = cv2.cvtColor(cv2.imread(img), cv2.COLOR_BGR2RGB)
plt.figure(figsize=(12, 12))
plt.imshow(img_arr)

 

img = '/content/mmdetection/demo/demo.jpg'
# inference_detector의 인자로 string(file경로), ndarray가 단일 또는 list형태로 입력 될 수 있음. 
results = inference_detector(model, img)

# /usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/datasets/utils.py:69: UserWarning: "ImageToTensor" pipeline is replaced by "DefaultFormatBundle" for batch inference. It is recommended to manually replace it in the test data pipeline in your config file.
#   'data pipeline in your config file.', UserWarning)
# /usr/local/lib/python3.7/dist-packages/torch/nn/functional.py:718: UserWarning: Named tensors and all their associated APIs are an experimental feature and subject to change. Please do not use them for anything important until they are released as stable. (Triggered internally at  /pytorch/c10/core/TensorImpl.h:1156.)
#   return torch.max_pool2d(input, kernel_size, stride, padding, dilation, ceil_mode)
# /usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/anchor_generator.py:324: UserWarning: ``grid_anchors`` would be deprecated soon. Please use ``grid_priors`` 
#   warnings.warn('``grid_anchors`` would be deprecated soon. '
# /usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/anchor_generator.py:361: UserWarning: ``single_level_grid_anchors`` would be deprecated soon. Please use ``single_level_grid_priors`` 
#   '``single_level_grid_anchors`` would be deprecated soon. '
type(results), len(results)
# (list, 80)
# len이 80개인데, detected 가 80개가 아니라 cocodataset이 80개임

MS-COCO pretrained model의 inference 반환 결과

# results는 list형으로 coco class의  0부터 79까지 class_id별로 80개의 array를 가짐. 
# 개별 array들은 각 클래스별로 5개의 값(좌표값과 class별로 confidence)을 가짐. 개별 class별로 여러개의 좌표를 가지면 여러개의 array가 생성됨. 
# 좌표는 좌상단(xmin, ymin), 우하단(xmax, ymax) 기준. 
# 개별 array의 shape는 (Detection된 object들의 수, 5(좌표와 confidence)) 임

results

class)id 0:'person', 1:'bicycle, 2:'car'
[array([[3.75348572e+02, 1.19171005e+02, 3.81950867e+02, 1.34460617e+02,
         1.35454327e-01], # 좌표값 좌상단(xmin, ymin), 우하단(xmax, ymax), class confidence
        [5.32362000e+02, 1.09554726e+02, 5.40526550e+02, 1.25222633e+02,
         8.88783410e-02], # 좌표값 좌상단(xmin, ymin), 우하단(xmax, ymax), class confidence
        [3.61124298e+02, 1.09049202e+02, 3.68625610e+02, 1.22483063e+02,
         7.20723420e-02]], dtype=float32), # 좌표값 좌상단(xmin, ymin), 우하단(xmax, ymax), class confidence
# class id는 별도로 없고, results[0]이 0번임         
# 내부에서 정규화를 해버림 
 array([], shape=(0, 5), dtype=float32), # 0번 class id는 detect가 안됬다.
 array([[6.09650024e+02, 1.13805893e+02, 6.34511658e+02, 1.36951904e+02,
         9.88766134e-01],
        [4.81773712e+02, 1.10480980e+02, 5.22459717e+02, 1.30407104e+02,
         9.87157285e-01],
        [1.01822114e+00, 1.12144730e+02, 6.04374733e+01, 1.44173752e+02,
         9.83206093e-01],
        [2.94623718e+02, 1.17035240e+02, 3.78022705e+02, 1.50550873e+02,
         9.71326768e-01],
        [3.96328979e+02, 1.11203323e+02, 4.32490540e+02, 1.32729263e+02,
         9.67802167e-01],
        [5.90976257e+02, 1.10802658e+02, 6.15401794e+02, 1.26493553e+02,
         9.59414959e-01],
        [2.67582001e+02, 1.05686005e+02, 3.28818756e+02, 1.28226547e+02,
         9.59253132e-01],
        [1.66856735e+02, 1.08006607e+02, 2.19100693e+02, 1.40194809e+02,
         9.56841350e-01],
        [1.89769577e+02, 1.09801109e+02, 3.00310852e+02, 1.53781891e+02,
         9.51012135e-01],
        [4.29822540e+02, 1.05655380e+02, 4.82741516e+02, 1.32376724e+02,
         9.45849955e-01],
        [5.55000916e+02, 1.09785004e+02, 5.92761780e+02, 1.27808495e+02,
         9.43992615e-01],
        [5.96790352e+01, 9.31828003e+01, 8.34545441e+01, 1.06242912e+02,
         9.33143973e-01],
        [9.78446579e+01, 8.96542969e+01, 1.18172356e+02, 1.01011108e+02,
         8.66323531e-01],
        [1.43898987e+02, 9.61869888e+01, 1.64599792e+02, 1.04979256e+02,
         8.26784551e-01],
        [8.55894241e+01, 8.99445801e+01, 9.88920746e+01, 9.85285416e+01,
         7.53480315e-01],
        [9.78282700e+01, 9.07443695e+01, 1.10298050e+02, 9.97373276e+01,
         7.16600597e-01],
        [2.23579224e+02, 9.85184631e+01, 2.49845108e+02, 1.07509857e+02,
         6.00782037e-01],
        [1.68928635e+02, 9.59468994e+01, 1.82843445e+02, 1.05694962e+02,
         5.91998756e-01],
        [1.35021347e+02, 9.08739395e+01, 1.50607025e+02, 1.02798874e+02,
         5.54030240e-01],
        [0.00000000e+00, 1.11521950e+02, 1.45326691e+01, 1.25850288e+02,
         5.43519914e-01],
        [5.53896606e+02, 1.16170540e+02, 5.62602295e+02, 1.26390923e+02,
         4.76758897e-01],
        [3.75809753e+02, 1.19579056e+02, 3.82376495e+02, 1.32113892e+02,
         4.61191744e-01],
        [1.37924118e+02, 9.37975311e+01, 1.54497177e+02, 1.04659683e+02,
         4.00998443e-01],
        [5.55009033e+02, 1.10952698e+02, 5.74925659e+02, 1.26912033e+02,
         3.43850523e-01],
        [5.54043152e+02, 1.00959076e+02, 5.61297913e+02, 1.10927711e+02,
         2.87963450e-01],
        [6.14741028e+02, 1.01987068e+02, 6.35481628e+02, 1.12593704e+02,
         2.61200219e-01],
        [5.70760315e+02, 1.09679398e+02, 5.90286133e+02, 1.27248878e+02,
         2.58404434e-01],
        [4.78544116e-01, 1.11568169e+02, 2.25040989e+01, 1.42623535e+02,
         2.56050110e-01],
        [3.75093140e+02, 1.11696442e+02, 4.20536804e+02, 1.33691055e+02,
         2.55963236e-01],
        [2.62747284e+02, 1.07565620e+02, 3.26765930e+02, 1.43925293e+02,
         2.09969625e-01],
        [7.91312714e+01, 9.03788834e+01, 1.00247879e+02, 1.01080894e+02,
         2.03962341e-01],
        [6.09313477e+02, 1.13308510e+02, 6.25961975e+02, 1.25342514e+02,
         1.97422847e-01],
        [1.35304840e+02, 9.23771439e+01, 1.64080185e+02, 1.04992455e+02,
         1.49973527e-01],
        [6.73540573e+01, 8.85008087e+01, 8.29853592e+01, 9.73942108e+01,
         1.48383990e-01],
        [5.40852417e+02, 1.13848946e+02, 5.61855530e+02, 1.26198776e+02,
         1.47629887e-01],
        [3.51735046e+02, 1.09432655e+02, 4.39310089e+02, 1.34819733e+02,
         1.41735107e-01],
        [9.63179016e+01, 8.98780594e+01, 1.53287781e+02, 1.01776367e+02,
         1.32707968e-01],
        [4.54495049e+01, 1.17444977e+02, 6.18955803e+01, 1.44275055e+02,
         1.25890508e-01],
        [6.06407532e+02, 1.12215973e+02, 6.18935669e+02, 1.24957237e+02,
         1.10722415e-01],
        [1.02152626e+02, 9.36143646e+01, 1.41081863e+02, 1.01598961e+02,
         8.13643038e-02],
        [3.98364838e+02, 1.12081459e+02, 4.09389862e+02, 1.32897766e+02,
         7.64543191e-02],
        [5.39245911e+02, 1.12394836e+02, 5.48756714e+02, 1.21964462e+02,
         7.32642636e-02],
        [6.09156555e+02, 1.04017456e+02, 6.35472107e+02, 1.26777176e+02,
         6.47417754e-02],
        [3.75894284e+00, 9.85745239e+01, 7.45848312e+01, 1.35154999e+02,
         6.32166639e-02],
        [1.68166473e+02, 9.14260483e+01, 2.20303146e+02, 1.07955681e+02,
         5.16179129e-02],
        [7.09724045e+01, 9.02684860e+01, 1.05398132e+02, 1.03825508e+02,
         5.15376776e-02]], dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([[1.8820007e+02, 1.0994706e+02, 3.0047137e+02, 1.5633583e+02,
         9.7509645e-02],
        [4.2774915e+02, 1.0511559e+02, 4.8345541e+02, 1.3294328e+02,
         9.6882291e-02],
        [2.9450479e+02, 1.1764229e+02, 3.7863284e+02, 1.5046356e+02,
         7.4364766e-02]], dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([[2.1920888e+02, 1.7456262e+02, 4.6010886e+02, 3.7704660e+02,
         9.7778010e-01],
        [3.7206638e+02, 1.3631432e+02, 4.3219531e+02, 1.8717290e+02,
         4.1699746e-01]], dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([[9.13259964e+01, 1.07155769e+02, 1.06029366e+02, 1.19777306e+02,
         1.15152948e-01]], dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([[3.72651917e+02, 1.36143082e+02, 4.32053833e+02, 1.88446472e+02,
         7.77875960e-01],
        [2.18404114e+02, 1.75137848e+02, 4.62107605e+02, 3.65541260e+02,
         1.01236075e-01]], dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32),
 array([], shape=(0, 5), dtype=float32)]

요약

# 2차원 배열로 표현됨
# 사람,    자전거,  차,     
results[0].shape, results[1].shape, results[2].shape, results[3].shape
# ((3, 5), (0, 5), (46, 5), (0, 5))
# 3건,      0건,    46건,    0건

결과 직접 확인

from mmdet.apis import show_result_pyplot
# inference 된 결과를 원본 이미지에 적용하여 새로운 image로 생성(bbox 처리된 image)
# Default로 score threshold가 0.3 이상인 Object들만 시각화 적용. show_result_pyplot은 model.show_result()를 호출. 
show_result_pyplot(model, img, results)

 

Model의 Config 설정 확인하기

# configs 파일로 모델을 만들었는데 모델 객체안의 변수 확인
model.__dict__

# 80개 class
{'CLASSES': ('person',
  'bicycle',
  'car',
  'motorcycle',
  'airplane',
  'bus',
  'train',
  'truck',
  'boat',
  'traffic_light',
  'fire_hydrant',
  'stop_sign',
  'parking_meter',
  'bench',
  'bird',
  'cat',
  'dog',
  'horse',
  'sheep',
  'cow',
  'elephant',
  'bear',
  'zebra',
  'giraffe',
  'backpack',
  'umbrella',
  'handbag',
  'tie',
  'suitcase',
  'frisbee',
  'skis',
  'snowboard',
  'sports_ball',
  'kite',
  'baseball_bat',
  'baseball_glove',
  'skateboard',
  'surfboard',
  'tennis_racket',
  'bottle',
  'wine_glass',
  'cup',
  'fork',
  'knife',
  'spoon',
  'bowl',
  'banana',
  'apple',
  'sandwich',
  'orange',
  'broccoli',
  'carrot',
  'hot_dog',
  'pizza',
  'donut',
  'cake',
  'chair',
  'couch',
  'potted_plant',
  'bed',
  'dining_table',
  'toilet',
  'tv',
  'laptop',
  'mouse',
  'remote',
  'keyboard',
  'cell_phone',
  'microwave',
  'oven',
  'toaster',
  'sink',
  'refrigerator',
  'book',
  'clock',
  'vase',
  'scissors',
  'teddy_bear',
  'hair_drier',
  'toothbrush'),
  '_modules': OrderedDict([('backbone', ResNet(
  # 모듈은 pytorch, backbone : resnet
  'cfg': Config (path: configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py)
#print(model.cfg) # 안이쁘게 나옴
print(model.cfg.pretty_text)

model = dict(
    type='FasterRCNN',
    backbone=dict(
        type='ResNet',
        depth=50,
        num_stages=4,
        out_indices=(0, 1, 2, 3),
        frozen_stages=1,
        norm_cfg=dict(type='BN', requires_grad=True),
        norm_eval=True,
        style='pytorch',
        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
    neck=dict(
        type='FPN',
        in_channels=[256, 512, 1024, 2048],
        out_channels=256,
        num_outs=5),
    rpn_head=dict(
        type='RPNHead',
        in_channels=256,
        feat_channels=256,
        anchor_generator=dict(
            type='AnchorGenerator',
            scales=[8],
            ratios=[0.5, 1.0, 2.0],
            strides=[4, 8, 16, 32, 64]),
        bbox_coder=dict(
            type='DeltaXYWHBBoxCoder',
            target_means=[0.0, 0.0, 0.0, 0.0],
            target_stds=[1.0, 1.0, 1.0, 1.0]),
        loss_cls=dict(
            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
    roi_head=dict(
        type='StandardRoIHead',
        bbox_roi_extractor=dict(
            type='SingleRoIExtractor',
            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
            out_channels=256,
            featmap_strides=[4, 8, 16, 32]),
        bbox_head=dict(
            type='Shared2FCBBoxHead',
            in_channels=256,
            fc_out_channels=1024,
            roi_feat_size=7,
            num_classes=80,
            bbox_coder=dict(
                type='DeltaXYWHBBoxCoder',
                target_means=[0.0, 0.0, 0.0, 0.0],
                target_stds=[0.1, 0.1, 0.2, 0.2]),
            reg_class_agnostic=False,
            loss_cls=dict(
                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
            loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
        train_cfg=None,
        test_cfg=dict(
            score_thr=0.05,
            nms=dict(type='nms', iou_threshold=0.5),
            max_per_img=100),
        pretrained=None),
    train_cfg=None,
    test_cfg=dict(
        rpn=dict(
            nms_pre=1000,
            max_per_img=1000,
            nms=dict(type='nms', iou_threshold=0.7),
            min_bbox_size=0),
        rcnn=dict(
            score_thr=0.05,
            nms=dict(type='nms', iou_threshold=0.5),
            max_per_img=100)),
    pretrained=None)
dataset_type = 'CocoDataset'
data_root = 'data/coco/'
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(
        type='Normalize',
        mean=[123.675, 116.28, 103.53],
        std=[58.395, 57.12, 57.375],
        to_rgb=True),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(
                type='Normalize',
                mean=[123.675, 116.28, 103.53],
                std=[58.395, 57.12, 57.375],
                to_rgb=True),
            dict(type='Pad', size_divisor=32),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img'])
        ])
]
data = dict(
    samples_per_gpu=2,
    workers_per_gpu=2,
    train=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_train2017.json',
        img_prefix='data/coco/train2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(type='LoadAnnotations', with_bbox=True),
            dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
            dict(type='RandomFlip', flip_ratio=0.5),
            dict(
                type='Normalize',
                mean=[123.675, 116.28, 103.53],
                std=[58.395, 57.12, 57.375],
                to_rgb=True),
            dict(type='Pad', size_divisor=32),
            dict(type='DefaultFormatBundle'),
            dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
        ]),
    val=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_val2017.json',
        img_prefix='data/coco/val2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=32),
                    dict(type='ImageToTensor', keys=['img']),
                    dict(type='Collect', keys=['img'])
                ])
        ]),
    test=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_val2017.json',
        img_prefix='data/coco/val2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=32),
                    dict(type='DefaultFormatBundle'),
                    dict(type='Collect', keys=['img'])
                ])
        ]))
evaluation = dict(interval=1, metric='bbox')
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
lr_config = dict(
    policy='step',
    warmup='linear',
    warmup_iters=500,
    warmup_ratio=0.001,
    step=[8, 11])
runner = dict(type='EpochBasedRunner', max_epochs=12)
checkpoint_config = dict(interval=1)
log_config = dict(interval=50, hooks=[dict(type='TextLoggerHook')])
custom_hooks = [dict(type='NumClassCheckHook')]
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)]

커스텀 할때 모델의 class 가 몇개인가, bbox_head의 num_classes 설정, dataset 설정을 지정해야 함. transforming, annotation 등 설정

 

array를 inference_detector()에 입력할 경우에는 원본 array를 BGR 형태로 입력 필요(RGB 변환은 내부에서 수행하므로 BGR로 입력 필요)

import cv2

# RGB가 아닌 BGR로 입력
img_arr = cv2.imread('/content/mmdetection/demo/demo.jpg')
results = inference_detector(model, img_arr)

show_result_pyplot(model, img_arr, results)

show_result_pyplot()을 이용하지 않고, inference 결과를 image로 표현하기

  • model과 image array를 입력하면 해당 image를 detect하고 bbox 처리해주는 get_detected_img() 함수 생성.
  • COCO 클래스 매핑은 0 부터 순차적으로 적용됨.
  • results에 들어있는 array 값이 없는 경우는 해당 list의 index에 해당하는 class id값으로 object가 Detection되지 않은 것임.
  • 개별 class의 score threshold가 낮은 값은 제외.
# 0부터 순차적으로 클래스 매핑된 label 적용. 
labels_to_names_seq = {0:'person',1:'bicycle',2:'car',3:'motorbike',4:'aeroplane',5:'bus',6:'train',7:'truck',8:'boat',9:'traffic light',10:'fire hydrant',
                        11:'stop sign',12:'parking meter',13:'bench',14:'bird',15:'cat',16:'dog',17:'horse',18:'sheep',19:'cow',20:'elephant',
                        21:'bear',22:'zebra',23:'giraffe',24:'backpack',25:'umbrella',26:'handbag',27:'tie',28:'suitcase',29:'frisbee',30:'skis',
                        31:'snowboard',32:'sports ball',33:'kite',34:'baseball bat',35:'baseball glove',36:'skateboard',37:'surfboard',38:'tennis racket',39:'bottle',40:'wine glass',
                        41:'cup',42:'fork',43:'knife',44:'spoon',45:'bowl',46:'banana',47:'apple',48:'sandwich',49:'orange',50:'broccoli',
                        51:'carrot',52:'hot dog',53:'pizza',54:'donut',55:'cake',56:'chair',57:'sofa',58:'pottedplant',59:'bed',60:'diningtable',
                        61:'toilet',62:'tvmonitor',63:'laptop',64:'mouse',65:'remote',66:'keyboard',67:'cell phone',68:'microwave',69:'oven',70:'toaster',
                        71:'sink',72:'refrigerator',73:'book',74:'clock',75:'vase',76:'scissors',77:'teddy bear',78:'hair drier',79:'toothbrush' }

labels_to_names = {1:'person',2:'bicycle',3:'car',4:'motorcycle',5:'airplane',6:'bus',7:'train',8:'truck',9:'boat',10:'traffic light',
                    11:'fire hydrant',12:'street sign',13:'stop sign',14:'parking meter',15:'bench',16:'bird',17:'cat',18:'dog',19:'horse',20:'sheep',
                    21:'cow',22:'elephant',23:'bear',24:'zebra',25:'giraffe',26:'hat',27:'backpack',28:'umbrella',29:'shoe',30:'eye glasses',
                    31:'handbag',32:'tie',33:'suitcase',34:'frisbee',35:'skis',36:'snowboard',37:'sports ball',38:'kite',39:'baseball bat',40:'baseball glove',
                    41:'skateboard',42:'surfboard',43:'tennis racket',44:'bottle',45:'plate',46:'wine glass',47:'cup',48:'fork',49:'knife',50:'spoon',
                    51:'bowl',52:'banana',53:'apple',54:'sandwich',55:'orange',56:'broccoli',57:'carrot',58:'hot dog',59:'pizza',60:'donut',
                    61:'cake',62:'chair',63:'couch',64:'potted plant',65:'bed',66:'mirror',67:'dining table',68:'window',69:'desk',70:'toilet',
                    71:'door',72:'tv',73:'laptop',74:'mouse',75:'remote',76:'keyboard',77:'cell phone',78:'microwave',79:'oven',80:'toaster',
                    81:'sink',82:'refrigerator',83:'blender',84:'book',85:'clock',86:'vase',87:'scissors',88:'teddy bear',89:'hair drier',90:'toothbrush',
                    91:'hair brush'}
import numpy as np

# np.where 사용법 예시.
arr1 = np.array([[3.75348572e+02, 1.19171005e+02, 3.81950867e+02, 1.34460617e+02,
         1.35454759e-01],
        [5.32362000e+02, 1.09554726e+02, 5.40526550e+02, 1.25222633e+02,
         8.88786465e-01],
        [3.61124298e+02, 1.09049202e+02, 3.68625610e+02, 1.22483063e+02,
         7.20717013e-02]], dtype=np.float32)
print(arr1.shape)

arr1_filtered = arr1[np.where(arr1[:, 4] > 0.1)] # 0.1 보다 큰것만 저장
print('### arr1_filtered:', arr1_filtered, arr1_filtered.shape)

(3, 5) # 2차원으로
### arr1_filtered: [[3.75348572e+02 1.19171005e+02 3.81950867e+02 1.34460617e+02
  1.35454759e-01]
 [5.32362000e+02 1.09554726e+02 5.40526550e+02 1.25222633e+02
  8.88786495e-01]] (2, 5)
np.where(arr1[:, 4] > 0.1) # row는 전부, 4:class confidence 가 0.1보다 큰 경우
# (array([0, 1]),)
# row 기준으로
# model과 원본 이미지 array, filtering할 기준 class confidence score를 인자로 가지는 inference 시각화용 함수 생성. 
def get_detected_img(model, img_array,  score_threshold=0.3, is_print=True):
  # 인자로 들어온 image_array를 복사. 
  draw_img = img_array.copy()
  bbox_color=(0, 255, 0)
  text_color=(0, 0, 255)

  # model과 image array를 입력 인자로 inference detection 수행하고 결과를 results로 받음. 
  # results는 80개의 2차원 array(shape=(오브젝트갯수, 5))를 가지는 list. 
  results = inference_detector(model, img_array)

  # 80개의 array원소를 가지는 results 리스트를 loop를 돌면서 개별 2차원 array들을 추출하고 이를 기반으로 이미지 시각화 
  # results 리스트의 위치 index가 바로 COCO 매핑된 Class id. 여기서는 result_ind가 class id
  # 개별 2차원 array에 오브젝트별 좌표와 class confidence score 값을 가짐. 
  for result_ind, result in enumerate(results):
    # 개별 2차원 array의 row size가 0 이면 해당 Class id로 값이 없으므로 다음 loop로 진행. 
    if len(result) == 0:
      continue
    
    # 2차원 array에서 5번째 컬럼에 해당하는 값이 score threshold이며 이 값이 함수 인자로 들어온 score_threshold 보다 낮은 경우는 제외. 
    result_filtered = result[np.where(result[:, 4] > score_threshold)]
    
    # 해당 클래스 별로 Detect된 여러개의 오브젝트 정보가 2차원 array에 담겨 있으며, 이 2차원 array를 row수만큼 iteration해서 개별 오브젝트의 좌표값 추출. 
    for i in range(len(result_filtered)):
      # 좌상단, 우하단 좌표 추출. 
      left = int(result_filtered[i, 0])
      top = int(result_filtered[i, 1])
      right = int(result_filtered[i, 2])
      bottom = int(result_filtered[i, 3])
      
      caption = "{}: {:.4f}".format(labels_to_names_seq[result_ind], result_filtered[i, 4])
      cv2.rectangle(draw_img, (left, top), (right, bottom), color=bbox_color, thickness=2)
      cv2.putText(draw_img, caption, (int(left), int(top - 7)), cv2.FONT_HERSHEY_SIMPLEX, 0.37, text_color, 1)
      if is_print:
        print(caption)

  return draw_img
import matplotlib.pyplot as plt

img_arr = cv2.imread('/content/mmdetection/demo/demo.jpg')
detected_img = get_detected_img(model, img_arr,  score_threshold=0.3, is_print=True)
# detect 입력된 이미지는 bgr임. 이를 최종 출력시 rgb로 변환 
detected_img = cv2.cvtColor(detected_img, cv2.COLOR_BGR2RGB)

plt.figure(figsize=(12, 12))
plt.imshow(detected_img)

/usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/anchor_generator.py:324: UserWarning: ``grid_anchors`` would be deprecated soon. Please use ``grid_priors`` 
  warnings.warn('``grid_anchors`` would be deprecated soon. '
car: 0.9888
car: 0.9872
car: 0.9832
car: 0.9713
car: 0.9678
car: 0.9594
car: 0.9593
car: 0.9568
car: 0.9510
car: 0.9458
car: 0.9440
car: 0.9331
car: 0.8663
car: 0.8268
car: 0.7535
car: 0.7166
car: 0.6008
car: 0.5920
car: 0.5540
car: 0.5435
car: 0.4768
car: 0.4612
car: 0.4010
car: 0.3439
bench: 0.9778
bench: 0.4170
chair: 0.7779
/usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/anchor_generator.py:361: UserWarning: ``single_level_grid_anchors`` would be deprecated soon. Please use ``single_level_grid_priors`` 
  '``single_level_grid_anchors`` would be deprecated soon. '
<matplotlib.image.AxesImage at 0x7f7f78bc4a50>

!mkdir data
!wget -O /content/data/beatles01.jpg https://raw.githubusercontent.com/chulminkw/DLCV/master/data/image/beatles01.jpg
!ls -lia /content/data/beatles01.jpg
img_arr = cv2.imread('/content/data/beatles01.jpg')
detected_img = get_detected_img(model, img_arr,  score_threshold=0.5, is_print=True)
# detect 입력된 이미지는 bgr임. 이를 최종 출력시 rgb로 변환 
detected_img = cv2.cvtColor(detected_img, cv2.COLOR_BGR2RGB)

plt.figure(figsize=(12, 12))
plt.imshow(detected_img)

/usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/anchor_generator.py:324: UserWarning: ``grid_anchors`` would be deprecated soon. Please use ``grid_priors`` 
  warnings.warn('``grid_anchors`` would be deprecated soon. '
person: 0.9988
person: 0.9982
person: 0.9980
person: 0.9971
person: 0.9604
car: 0.9693
car: 0.9686
car: 0.9648
car: 0.9517
car: 0.9254
car: 0.9030
car: 0.8312
car: 0.8008
car: 0.7331
car: 0.6208
tie: 0.5924
/usr/local/lib/python3.7/dist-packages/mmdet-2.17.0-py3.7.egg/mmdet/core/anchor/anchor_generator.py:361: UserWarning: ``single_level_grid_anchors`` would be deprecated soon. Please use ``single_level_grid_priors`` 
  '``single_level_grid_anchors`` would be deprecated soon. '
<matplotlib.image.AxesImage at 0x7f7f78bf2e50>

 

반응형
728x90
반응형

Computer Vision Footprint

1998 - LeNet 5

 

2012 - AlexNet

- Gradient vanishin문제를 해결하는 ReLU를 사용 - LRN 사용 안했음

- Overlapping Pooling(영향력이 크진 않음)

- Dropout(Overfitting 방지)

 

2013 - ZFNet

- AlexNet을 Visualization통해 insight를 제공

- HyperParameter tuning 방법론 제시

- Network in Network

- 1x1 Convolution 도입 (1. 차원 조절 2. non-linear 특성 부여 3. fully connected 처럼 사용)

- Flatten 대신 사용하는 방법인 Global Average Pooling 도입

- 같은 형태를 반복하고 겹쳐서 사용하는 Stacking 도입

 

2014 - GoogLeNet v1 (1등)

- 1x1 convolution 활용

- Inception module stacking

- Global average pooling 활용

- VGG (2등)

 

2016 - GoogLeNet v4

GoogLeNet

ReLU와 같은 기법 덕에 Layer를 깊게 쌓을 수 있는 확신이 있었던 배경 때문에 Layer를 어떻게 깊게 쌓는지 여부가 중요한 포인트였다

GoogLeNet 특징

1. 1x1 convolution

- 차원 축소 효과

- nonlinearity 특징 부여

2. Inception module

- 9 inception module로 구성되어 있다

- Inception moddule은 Hebbian rule에 의해 영향력이 가장 부분이 연결성이 강화된다(weight가 커진다)

- 실제 구현에서는 차원을 조절하여 inception module을 적용하였다

3. Global average pooling

4. Auxiliary classifier

- relu로 gradient vanishing 문제를 해결한듯 했으나 layer가 기존에 8 layer였던것에 비해 22 layer로 급격하게 늘었기 때문에 gradient vanishing 가능성이 생겼다

- 따라서 네트워크 중간에 auxiliary classifier를 추가하여 중간 중간 결과를 낸다

Inception module

import tensorflow as tf 

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.cifar10.load_data()

# Inception module, naive version
input_ = tf.keras.Input((32,32,3))
conv1x1 = tf.keras.layers.Conv2D(3,1, padding='same', name='1x1')(input_) 
conv3x3 = tf.keras.layers.Conv2D(3,3, padding='same', name='3x3')(input_) 
conv5x5 = tf.keras.layers.Conv2D(3,3, padding='same', name='5x5')(input_) 
maxpool = tf.keras.layers.MaxPool2D(3,1, padding='same', name='pool')(input_) # unoverlapping 
concatenate = tf.keras.layers.Concatenate()([conv1x1,conv3x3,conv5x5,maxpool]) # 합치기 위해서 구조를 맞추어야 한다 

model = tf.keras.models.Model(input_, concatenate)
model.summary()
# Model: "model_13"
# __________________________________________________________________________________________________
# Layer (type)                    Output Shape         Param #     Connected to                     
# ==================================================================================================
# input_24 (InputLayer)           [(None, 32, 32, 3)]  0                                            
# __________________________________________________________________________________________________
# 1x1 (Conv2D)                    (None, 32, 32, 3)    12          input_24[0][0]                   
# __________________________________________________________________________________________________
# 3x3 (Conv2D)                    (None, 32, 32, 3)    84          input_24[0][0]                   
# __________________________________________________________________________________________________
# 5x5 (Conv2D)                    (None, 32, 32, 3)    84          input_24[0][0]                   
# __________________________________________________________________________________________________
# pool (MaxPooling2D)             (None, 32, 32, 3)    0           input_24[0][0]                   
# __________________________________________________________________________________________________
# concatenate_15 (Concatenate)    (None, 32, 32, 12)   0           1x1[0][0]                        
#                                                                  3x3[0][0]                        
#                                                                  5x5[0][0]                        
#                                                                  pool[0][0]                       
# ==================================================================================================
# Total params: 180
# Trainable params: 180
# Non-trainable params: 0
# __________________________________________________________________________________________________
tf.keras.utils.plot_model(model, show_shapes=True)

 

 

# Inception module with dimension reductions
input_ = tf.keras.Input((32,32,3))
conv1x1 = tf.keras.layers.Conv2D(3,1, padding='same', name='1x1')(input_) 
conv1x1_reduc3x3 = tf.keras.layers.Conv2D(2,1, padding='same', name='1x1_3x3')(input_) 
conv3x3 = tf.keras.layers.Conv2D(2,3, padding='same', name='3x3')(conv1x1_reduc3x3) 
conv1x1_reduc5x5 = tf.keras.layers.Conv2D(2,1, padding='same', name='1x1_5x5')(input_) 
conv5x5 = tf.keras.layers.Conv2D(2,3, padding='same', name='5x5')(conv1x1_reduc5x5) 
maxpool = tf.keras.layers.MaxPool2D(2,1, padding='same', name='pool')(input_) 
conv1x1_reduc_mp = tf.keras.layers.Conv2D(2,1, padding='same', name='1x1_pool')(maxpool) 
concatenate = tf.keras.layers.Concatenate(axis=-1)([conv1x1,conv3x3,conv5x5,conv1x1_reduc_mp]) # 합치기 위해서 구조를 맞추어야 한다 
# concatenate = tf.keras.layers.Concatenate([conv1x1,conv3x3,conv5x5,conv1x1_reduc_mp]) # 옵션을 주지 않을때 

model = tf.keras.models.Model(input_, concatenate)
model.summary()
# Model: "model_14"
# __________________________________________________________________________________________________
# Layer (type)                    Output Shape         Param #     Connected to                     
# ==================================================================================================
# input_25 (InputLayer)           [(None, 32, 32, 3)]  0                                            
# __________________________________________________________________________________________________
# 1x1_3x3 (Conv2D)                (None, 32, 32, 2)    8           input_25[0][0]                   
# __________________________________________________________________________________________________
# 1x1_5x5 (Conv2D)                (None, 32, 32, 2)    8           input_25[0][0]                   
# __________________________________________________________________________________________________
# pool (MaxPooling2D)             (None, 32, 32, 3)    0           input_25[0][0]                   
# __________________________________________________________________________________________________
# 1x1 (Conv2D)                    (None, 32, 32, 3)    12          input_25[0][0]                   
# __________________________________________________________________________________________________
# 3x3 (Conv2D)                    (None, 32, 32, 2)    38          1x1_3x3[0][0]                    
# __________________________________________________________________________________________________
# 5x5 (Conv2D)                    (None, 32, 32, 2)    38          1x1_5x5[0][0]                    
# __________________________________________________________________________________________________
# 1x1_pool (Conv2D)               (None, 32, 32, 2)    8           pool[0][0]                       
# __________________________________________________________________________________________________
# concatenate_16 (Concatenate)    (None, 32, 32, 9)    0           1x1[0][0]                        
#                                                                  3x3[0][0]                        
#                                                                  5x5[0][0]                        
#                                                                  1x1_pool[0][0]                   
# ==================================================================================================
# Total params: 112
# Trainable params: 112
# Non-trainable params: 0
# __________________________________________________________________________________________________
tf.keras.utils.plot_model(model, show_shapes=True, rankdir="BT", show_dtype=True) # wide하게 분포되어 있기 때문에 parameter operator가 엄청 많다 / 이중에서 영향력 있는 것만 강한 연결성을 띄게 된다

in3 = tf.keras.applications.InceptionV3()

tf.keras.utils.plot_model(in3)

Visual Geometry Group

Very Deep Convolutional Networks for large-scale image recognition

Layer가 깊어지면 깊어질 수 록 성능이 좋아진다

Abliation study 방식으로 모델 성능을 비교한다

 

1. AlexNet vs AlexNet + LRN

- LRN 기법은 성능 개선은 없고 메모리 사용량 및 연산 시간만 늘어났기에 사용을 하지 않는다

2. AlexNet vs AlexNet + conv layer 2개 추가

- 성능 좋아짐

3. AlexNet + conv layer 2개 vs AlexNet + conv layer 2개 + 1x1 convolution 3개

- Nonlinearity만 추가 시켜 성능향상을 하는지 확인 => activation이 더 많아서 성능 더 좋아짐 (UAT)

4. AlexNet + conv layer 2개 + 1x1 convolution 3개 vs AlexNet + conv layer 5개

- 성능 좋아짐

5. AlexNet + conv layer 5개 vs AlexNet + conv layer 8개

- 성능 좋아짐

결국 Layer가 늘어나면 늘어날 수록 성능이 좋아졌다

 

 

 

model을 불러와 사용하는 두 가지 방식

1. applications => Computer vision 분야에서 지원되는 모델을 지원한다

2. tensorflow hub

import tensorflow as tf 

vgg16 = tf.keras.applications.VGG16() # ImageNet 대회서 2등, 학습된 모델을 그대로 사용할 수 있다  

vgg16.summary()
# Model: "vgg16"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_2 (InputLayer)         [(None, 224, 224, 3)]     0         
# _________________________________________________________________
# block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
# _________________________________________________________________
# block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
# _________________________________________________________________
# block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
# _________________________________________________________________
# block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
# _________________________________________________________________
# block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
# _________________________________________________________________
# block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
# _________________________________________________________________
# block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
# _________________________________________________________________
# block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
# _________________________________________________________________
# block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
# _________________________________________________________________
# block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
# _________________________________________________________________
# block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
# _________________________________________________________________
# block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
# _________________________________________________________________
# block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
# _________________________________________________________________
# block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
# _________________________________________________________________
# block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
# _________________________________________________________________
# block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
# _________________________________________________________________
# block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
# _________________________________________________________________
# block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
# _________________________________________________________________
# flatten (Flatten)            (None, 25088)             0         
# _________________________________________________________________
# fc1 (Dense)                  (None, 4096)              102764544 
# _________________________________________________________________
# fc2 (Dense)                  (None, 4096)              16781312  
# _________________________________________________________________
# predictions (Dense)          (None, 1000)              4097000   
# =================================================================
# Total params: 138,357,544
# Trainable params: 138,357,544
# Non-trainable params: 0
# _________________________________________________________________
vgg16.weights

vgg16.__class__
# keras.engine.functional.Functional

3x3을 쓰는 이유

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.cifar10.load_data()

input_ = tf.keras.Input((32,32,3))
x = tf.keras.layers.Conv2D(2,5)(input_) # filter 2개 5x5 
model = tf.keras.models.Model(input_, x)

model.summary()
# Model: "model"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_4 (InputLayer)         [(None, 32, 32, 3)]       0         
# _________________________________________________________________
# conv2d (Conv2D)              (None, 28, 28, 2)         152       
# =================================================================
# Total params: 152
# Trainable params: 152
# Non-trainable params: 0
# _________________________________________________________________
input_ = tf.keras.Input((32,32,3))
x = tf.keras.layers.Conv2D(2,3)(input_) 
x = tf.keras.layers.Conv2D(2,3)(x) 
model = tf.keras.models.Model(input_, x)

model.summary() # 3x3이 더 좋다 / 5x5일 때와 shape은 같지만 activation을 여러개 사용했고 파라미터 수가 줄어들었기 때문에 성능이 더 좋아졌다 
# Model: "model_15"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_27 (InputLayer)        [(None, 32, 32, 3)]       0         
# _________________________________________________________________
# conv2d_221 (Conv2D)          (None, 30, 30, 2)         56        
# _________________________________________________________________
# conv2d_222 (Conv2D)          (None, 28, 28, 2)         38        
# =================================================================
# Total params: 94
# Trainable params: 94
# Non-trainable params: 0
# _________________________________________________________________
input_ = tf.keras.Input((32,32,3))
x = tf.keras.layers.Conv2D(2,3)(input_) 
x = tf.keras.layers.Conv2D(2,3)(x)
x = tf.keras.layers.Conv2D(2,3)(x) 
model = tf.keras.models.Model(input_, x)

model.summary() # 3x3으로 더 큰 receptive field를 대체 할 수 있다 

# Model: "model_24"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_36 (InputLayer)        [(None, 32, 32, 3)]       0         
# _________________________________________________________________
# conv2d_243 (Conv2D)          (None, 30, 30, 2)         56        
# _________________________________________________________________
# conv2d_244 (Conv2D)          (None, 28, 28, 2)         38        
# _________________________________________________________________
# conv2d_245 (Conv2D)          (None, 26, 26, 2)         38        
# =================================================================
# Total params: 132
# Trainable params: 132
# Non-trainable params: 0
# _________________________________________________________________

보통 첫번째 layer는 5x5 또는 7x7 filter를 사용하고

(처음부터 3x3을 사용하면 메모리를 너무 많이차지 하기 때문에 오히려 성능 저하가 될 수 있다)

그 다음 layer 부터는 3x3을 사용하면 성능이 좋다

Deep Residual Learning for Image Recognition

ResNet은 인간을 뛰어넘은 첫번째 모델
Residual => 실제값 - 예측값 (잔차)

layer가 56개로 깊어지면서 gradient vanishing문제가 다시 떠오르기 시작했다

(relu로 해결되지 못하는 문제가 발생) underfitting

처음에 학습했던 것들을 점차 잊게되면서 목표로 부터 멀어지게 되는 현상이 생겼다

그래서 해결책으로 Short cut 즉, residual block을 도입하게 되었다

 

LSTM에서 아이디어를 얻어 자신의 값을 더함으로써 값을 유지시키고 목표를 잃지 않고 지속적으로 학습하게 된다

 

기존의 신경망은 입력값 x를 타겟값 y로 매핑하는 함수 H(x)를 얻는 것이 목적이었다.

그러나 ResNet은 F(x) + x를 최소화하는 것을 목적으로 한다.

x는 현시점에서 변할 수 없는 값이므로 F(x)를 0에 가깝게 만드는 것이 목적이 된다.

F(x)가 0이 되면 출력과 입력이 모두 x로 같아지게 된다.

F(x) = H(x) - x이므로 F(x)를 최소로 해준다는 것은 H(x) - x를 최소로 해주는 것과 동일한 의미를 지닌다.

여기서 H(x) - x를 잔차(residual)라고 한다.

즉, 잔차를 최소로 해주는 것이므로 ResNet이란 이름이 붙게 된다.

 

Batch Normalization 사용 => underfitting을 막아주면서 dropout을 사용하지 않고 좋은 성능을 얻게 되었다

vgg19와 34-layer plain을 비교했을 때 34-layer는 가장 앞 layer에서 7x7 filter를 적용했고 레이어 수를 두 배 가까이 늘렸다

그리고 맨마지막에 flatten을 쓰지 않고 average pooling을 사용했더니 34-layer가 성능이 더 좋았다

더 나아가 34-layer에서 residual block을 추가한 결과 추가하지 않은 것보다 오차율이 감소한 것을 확인할 수 있었다

Residual block과 Batch Normalization의 조합으로 훨씬 더 깊은 layer가 가능해졌다

결국 총 152 layer를 쌓을 수 있게 되었다

반응형
728x90
반응형

Visualizing and Understanding Convolutional Networks

CNN을 제대로 이해하기 위한 논문
CNN을 시각화 하여 왜 성능이 향상되었는지를 탐구하는 논문
과거의 관점으로 AlexNet에 대한 '통찰'과 '진단'을 제공한다

Convolutional layer를 통과하고나서 Max pooling을 거칠때 가장 영향력 있는 값들로 축약되기 때문에 큰 특징만 남게 된다

따라서 반대로 pooling을 거치기 전으로 복원시킨다면 큰 특징만 남아 있기 때문에 Detail한 특징들은 사라지게 될 것이다

AlexNet의 네트워크 구조대로 통과시킨 데이터들을 레이어 별로 복원시키게 되면

가장 중요한 특징들만 남도록 복원된다는 것을 시각적으로 확인 있다

 

시각화된 결과를 보면 AlexNet의 구조는 처음 레이어에서 간단한 특징들을 뽑아내다가

레이어가 깊어지면 깊어질 수록 '복잡한 특징' 구분할 있도록 학습이 되는 것을 있다

기존 AlexNet의 첫번째 Layer에서 filter 11x11, stride 4를 filter 7x7, stride 2로 바꾸었다

두번째 layer에서는 stride 1, zero-padding 2에서 stride 2로 바꾸었다

=> 기존에는 filter가 컸기 때문에 작은 것들의 특징을 잘 못잡는다는 문제가 있었다

 

결과 => 파라미터가 줄어들었고, 좀 더 디테일한 특징을 알아낼 수 있도록 학습하게되었다

receptive field를 대신할 수 있다 @@@ 의미 확인

일부분을 가림으로써 어떤 부분이 가장 영향력이 있는지를 확률적으로 계산하였다 (abliation study)

특정 부분을 가렸을 때 가장 성능이 안좋다면 그 부분은 가장 영향력이 있는 부분인 것이다 (파란색 부분이 가장 영향력 있는 부분)

a와 c는 AlexNet b와 d는 ZFNet

 

Layer 1

a 그림에서는 dead feature가 꽤 있고 좀 더 흐릿하게 구분된 반면

c 그림에서는 dead feature가 굉장히 많이 줄고 특징이 좀 더 선명하게 구분되었다

filter사이즈와 stride를 줄임으로써 좀 더 특징을 잘 구분할 수 있게 학습이되었다고 할 수 있다

 

Layer 2

c 그림에서 보면 빈 공간이 많고 aliasing(계단현상)이 발견된 반면

d 그림에서는 빈 공간이 적고 특징이 더 선명하게 잘 구분되도록 학습이 되었다

Network In Network

네트워크 안에 네트워크를 넣는 방식
convolution layer사이에 mlp가 있다 (사실상 1x1 convolution을 사용한 것)

Flatten의 문제

import tensorflow as tf 

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()

X_train = X_train.reshape(-1,28,28,1)/255

# AlexNet
 
input_ = tf.keras.Input((224,224,3))
x = tf.keras.layers.Conv2D(96,11,4)(input_)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Conv2D(256, 5, padding='same')(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.MaxPool2D(3,2)(x)
x = tf.keras.layers.Conv2D(384,3)(x) 
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.MaxPool2D(3,2)(x)
x = tf.keras.layers.Conv2D(384,3)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Conv2D(256, 3)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.MaxPool2D(3,2)(x)
x = tf.keras.layers.Flatten()(x) # 일렬로 만들어 지기 때문에 위치 정보를 잃고 학습할 weight가 많이 생긴다 (FC만 썼을때 위치정보를 잃는다)
x = tf.keras.layers.Dense(4096)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(4096, kernel_initializer=tf.keras.initializers.Constant(1))(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(1000, activation='softmax')(x)

model = tf.keras.models.Model(input_,x)
model.summary()

# Model: "model_1"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_2 (InputLayer)         [(None, 224, 224, 3)]     0         
# _________________________________________________________________
# conv2d_5 (Conv2D)            (None, 54, 54, 96)        34944     
# _________________________________________________________________
# re_lu_6 (ReLU)               (None, 54, 54, 96)        0         
# _________________________________________________________________
# conv2d_6 (Conv2D)            (None, 54, 54, 256)       614656    
# _________________________________________________________________
# re_lu_7 (ReLU)               (None, 54, 54, 256)       0         
# _________________________________________________________________
# max_pooling2d_2 (MaxPooling2 (None, 26, 26, 256)       0         
# _________________________________________________________________
# conv2d_7 (Conv2D)            (None, 24, 24, 384)       885120    
# _________________________________________________________________
# re_lu_8 (ReLU)               (None, 24, 24, 384)       0         
# _________________________________________________________________
# max_pooling2d_3 (MaxPooling2 (None, 11, 11, 384)       0         
# _________________________________________________________________
# conv2d_8 (Conv2D)            (None, 9, 9, 384)         1327488   
# _________________________________________________________________
# re_lu_9 (ReLU)               (None, 9, 9, 384)         0         
# _________________________________________________________________
# conv2d_9 (Conv2D)            (None, 7, 7, 256)         884992    
# _________________________________________________________________
# re_lu_10 (ReLU)              (None, 7, 7, 256)         0         
# _________________________________________________________________
# max_pooling2d_4 (MaxPooling2 (None, 3, 3, 256)         0         
# _________________________________________________________________
# flatten_1 (Flatten)          (None, 2304)              0         
# _________________________________________________________________
# dense_3 (Dense)              (None, 4096)              9441280   
# _________________________________________________________________
# re_lu_11 (ReLU)              (None, 4096)              0         
# _________________________________________________________________
# dropout_2 (Dropout)          (None, 4096)              0         
# _________________________________________________________________
# dense_4 (Dense)              (None, 4096)              16781312  
# _________________________________________________________________
# re_lu_12 (ReLU)              (None, 4096)              0         
# _________________________________________________________________
# dropout_3 (Dropout)          (None, 4096)              0         
# _________________________________________________________________
# dense_5 (Dense)              (None, 1000)              4097000   
# =================================================================
# Total params: 34,066,792
# Trainable params: 34,066,792
# Non-trainable params: 0
# _________________________________________________________________

 

 

# AlexNet Flatten x / Global average pooling 
 
input_ = tf.keras.Input((224,224,3))
x = tf.keras.layers.Conv2D(96,11,4)(input_)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Conv2D(256, 5, padding='same')(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.MaxPool2D(3,2)(x)
x = tf.keras.layers.Conv2D(384,3)(x) 
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.MaxPool2D(3,2)(x)
x = tf.keras.layers.Conv2D(384,3)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Conv2D(256, 3)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.MaxPool2D(3,2)(x)
x = tf.keras.layers.Conv2D(1000, 3)(x) # 1x1 convolution 
x = tf.keras.layers.GlobalAveragePooling2D()(x) # 1차원으로  바뀌었기 때문에 Flatten과 똑같다. 하지만 Flatten보다 Parameter를 훨씬 줄일 수 있다 
x = tf.keras.layers.Dense(1000)(x)

model = tf.keras.models.Model(input_,x)
model.summary()

# Model: "model_6"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_8 (InputLayer)         [(None, 224, 224, 3)]     0         
# _________________________________________________________________
# conv2d_35 (Conv2D)           (None, 54, 54, 96)        34944     
# _________________________________________________________________
# re_lu_42 (ReLU)              (None, 54, 54, 96)        0         
# _________________________________________________________________
# conv2d_36 (Conv2D)           (None, 54, 54, 256)       614656    
# _________________________________________________________________
# re_lu_43 (ReLU)              (None, 54, 54, 256)       0         
# _________________________________________________________________
# max_pooling2d_20 (MaxPooling (None, 26, 26, 256)       0         
# _________________________________________________________________
# conv2d_37 (Conv2D)           (None, 24, 24, 384)       885120    
# _________________________________________________________________
# re_lu_44 (ReLU)              (None, 24, 24, 384)       0         
# _________________________________________________________________
# max_pooling2d_21 (MaxPooling (None, 11, 11, 384)       0         
# _________________________________________________________________
# conv2d_38 (Conv2D)           (None, 9, 9, 384)         1327488   
# _________________________________________________________________
# re_lu_45 (ReLU)              (None, 9, 9, 384)         0         
# _________________________________________________________________
# conv2d_39 (Conv2D)           (None, 7, 7, 256)         884992    
# _________________________________________________________________
# re_lu_46 (ReLU)              (None, 7, 7, 256)         0         
# _________________________________________________________________
# max_pooling2d_22 (MaxPooling (None, 3, 3, 256)         0         
# _________________________________________________________________
# conv2d_40 (Conv2D)           (None, 1, 1, 1000)        2305000   
# _________________________________________________________________
# global_average_pooling2d_2 ( (None, 1000)              0         
# _________________________________________________________________
# dense_13 (Dense)             (None, 1000)              1001000   
# =================================================================
# Total params: 7,053,200
# Trainable params: 7,053,200
# Non-trainable params: 0
# _________________________________________________________________

Flatten vs Global Average Pooling

Flatten은 원본 데이터 그대로 1차원으로 변화 시키지만 GAP은 최종결과에서 평균을 내어 하나의 값으로 바꾸고 그 형태가 1차원으로 변화된다

GAP는 전체 값 중에서 noise나 outlier등 학습이 안된 것들을 뭉뚱그리기 때문에 오히려 성능이 향상 되는 경우가 있다

 

GAP는 전체를 반영하기 때문에 Flatten한 것 보다 값을 더 잘 반영 하는 경우가 있지만

weight가 줄어들기 때문에 하나의 관점보다 여러개의 관점으로 파악하는 경우가 성능이 좋은 경우가 있을 수 있다

따라서 경우에 따라서 결정해야 할 문제이다

Hyperparameter처럼 데이터에 따라서 직접 정해줘야 한다

Grouped Convolution

Grouped Convolution은 입력 값의 채널들을 여러 개의 그룹으로 나누어 독립적으로 Convolution 연산을 수행하는 방식이다.

모델이 복잡할때 parameter를 줄이기 위해서 사용하는 경우가 있다

1x1 Convolution

Point-wise Convolution

1. 1x1의 depth에 따라서 차원이 바뀐다

2. Non-linear 특징을 부여한다

3. Fully connected 처럼 사용한다

GoogLeNet

1. Network in Network (1x1 convolution)

2. Stacking

3. Global average pooling

 

GoogLeNet은 3가지 내용을 참고하여 만들어졌다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
728x90
반응형

논문 구현

import tensorflow as tf 

input_ = tf.keras.Input(shape=(32,32,1))
x = tf.keras.layers.Conv2D(6, 5, activation='tanh')(input_) # filter 개수, filter 크기 / stride는 생략되었기 때문에 1이라 가정한다 / padding: valid
x = tf.keras.layers.AvgPool2D(2)(x) # 기본적으로 non-overlapping 방식이다 
x = tf.keras.layers.Conv2D(16, 5, activation='tanh')(x)
x = tf.keras.layers.MaxPool2D(6, 5)(x)
x = tf.keras.layers.Flatten()(x)
x = tf.keras.layers.Dense(120, activation='tanh')(x)
x = tf.keras.layers.Dense(84, activation='tanh')(x)
x = tf.keras.layers.Dense(10, activation='softmax')(x)

model = tf.keras.models.Model(input_, x)
# pooling 사용했을 때 
model.summary()
# Model: "model_1"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_3 (InputLayer)         [(None, 32, 32, 1)]       0         
# _________________________________________________________________
# conv2d_1 (Conv2D)            (None, 28, 28, 6)         156       
# _________________________________________________________________
# average_pooling2d (AveragePo (None, 14, 14, 6)         0         
# _________________________________________________________________
# conv2d_2 (Conv2D)            (None, 10, 10, 16)        2416      
# _________________________________________________________________
# max_pooling2d (MaxPooling2D) (None, 1, 1, 16)          0         
# _________________________________________________________________
# flatten (Flatten)            (None, 16)                0         
# _________________________________________________________________
# dense (Dense)                (None, 120)               2040      
# _________________________________________________________________
# dense_1 (Dense)              (None, 84)                10164     
# _________________________________________________________________
# dense_2 (Dense)              (None, 10)                850       
# =================================================================
# Total params: 15,626
# Trainable params: 15,626
# Non-trainable params: 0
# _________________________________________________________________
# pooling 사용하지 않았을 때 
model.summary()

# Model: "model_3"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_5 (InputLayer)         [(None, 32, 32, 1)]       0         
# _________________________________________________________________
# conv2d_5 (Conv2D)            (None, 28, 28, 6)         156       
# _________________________________________________________________
# conv2d_6 (Conv2D)            (None, 24, 24, 16)        2416      
# _________________________________________________________________
# flatten_2 (Flatten)          (None, 9216)              0         
# _________________________________________________________________
# dense_6 (Dense)              (None, 120)               1106040   
# _________________________________________________________________
# dense_7 (Dense)              (None, 84)                10164     
# _________________________________________________________________
# dense_8 (Dense)              (None, 10)                850       
# =================================================================
# Total params: 1,119,626
# Trainable params: 1,119,626
# Non-trainable params: 0
# _________________________________________________________________

ImageNet Classification with Deep Convolutional Neural Networks

ImageNet LSVRC 대회에서 ImageNet 데이터를 사용한 CNN

Relu

ReLU는 Sigmoid와 tanh가 갖는 Gradient Vanishing 문제를 해결하기 위한 함수이다

ReLU는 sigmoid, tanh 함수보다 학습이 빠르고, 연산 비용이 적고, 구현이 매우 간단하다

Dying Relu

Dying relu는 모든 0이하의 입력에 대해서 미분 값이 0이 되어 가중치에 곱해지면서 해당 노드가 통째로 죽어버리는 현상을 말한다

이러한 단점을 보완한 reaky relu가 있다

Normalization

layer가 깊어질 수록 값의 범위가 매번 바뀌고 최소값으로 수렴이 안되는 underfitting이 발생한다

이런 경우를 방지하기 위해서 값의 범위를 일정하게 맞춰주는 방법이 바로 normalization이다

 

대표적인 정규화 방법 가지

1. Min-Max Normalization (최소-최대 정규화)

- 최소값은 0, 최대값은 1, 그리고 다른 값들은 0 1 사이의 값으로 변환하는 방식

- (data - MIN) / (MAX-MIN) 2. Z-Score Normalization (Z-점수 정규화)

- 평균은 0, 표준편차는 1 정규 분포를 그리도록 데이터의 분포를 바꾸는 방식

- 이상치(outlier) 문제를 피하는 데이터 정규화 전략-

- (data - 평균) / 표준편차

Pooling

1. overlapping pooling

- 겹쳐가며 pooling을 하는 방법

- 성능에 크게 영향을 미치는 방법은 아니다

2. non overlapping pooling

- 겹치지 않고 pooling을 하는 방법

pooling을 하는 이유

1. parameter 줄여주기 때문에 연산량을 줄여준다

2. invariance한 속성을 더해준다

Data augmentation

Overfitting을 줄이기 위한 방법으로써 이미지 데이터를 변형해서 학습 데이터의 수를 늘리는 방법이다

이미지 데이터를 좌우반전, 상하반전, 회전, 자르기 등을 하여 학습하지 않은 데이터를 대처할 수 있는 일반적인 모델을 만들어주는 데에 효과적인 방법이다

예시

 

Dropout

Dropout은 특정 확률로 hidden layer의 node 출력값을 0으로 설정하는 방법이다 이렇게 dropout된 node들은 foward, backward propagation에 관여를 하지 않는다 dropout이 사용된 NN는 입력이 주어질 때마다 달라진 신경망 구조를 거치게 되고 이는 노드간의 의존성을 약화시켜 가장 영향력 있는 노드의 연결성이 강화되는 방식으로 학습을 하게된다 따라서 성능은 비슷하지만 연산 복잡도를 줄이면서 overfitting 문제를 해결하는 방법이 될 수 있다 ※ Fully connected layer 다음에 Dropuout을 사용한다

Result

Ensemble기법을 사용하고 pre-trained 모델인 경우에 에러율이 더 낮았다

※ CNNs는 Boosting기법(Ensemble)을 사용한 방법

AlexNet

논문 구현

하드웨어 메모리가 부족하여 GPU 병렬 연산을 위해 CNN 구조를 절반으로 나누어 설계되었다

# 평균은 0, 표준분포는 0.01인 정규분포 값으로 가중치를 초기화 한다 
bias = tf.keras.initializers.Constant(1) # relu 때문에 초기화 값을 1로 지정한다 (음수가 들어가면 dying relu현상이 발생하기 때문)

input_ = tf.keras.Input((224,224,3))
x = tf.keras.layers.Conv2D(96,5,4)(input_)
x = tf.keras.layers.MaxPool2D(3,2)(x) # overlapping pooling
x = tf.keras.layers.Conv2D(256,3, padding='same', bias_initializer=bias)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.MaxPool2D(3,2)(x)
x = tf.keras.layers.Conv2D(384,3, padding='same', bias_initializer='ones')(x) # ones 단축키는 버전에따라 지원하지 않을 수 있다 -
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Conv2D(384,3, padding='same', bias_initializer=bias)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Conv2D(256,3, padding='same', bias_initializer=bias)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Flatten()(x)
x = tf.keras.layers.Dense(4096, bias_initializer=bias)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(4096, bias_initializer=bias)(x)
x = tf.keras.layers.ReLU()(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(1000, bias_initializer=bias, activation='softmax')(x)
model = tf.keras.models.Model(input_,x) # tensorflow from_logit
model.summary()
# Model: "model_3"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_4 (InputLayer)         [(None, 224, 224, 3)]     0         
# _________________________________________________________________
# conv2d_15 (Conv2D)           (None, 55, 55, 96)        7296      
# _________________________________________________________________
# max_pooling2d_6 (MaxPooling2 (None, 27, 27, 96)        0         
# _________________________________________________________________
# conv2d_16 (Conv2D)           (None, 27, 27, 256)       221440    
# _________________________________________________________________
# re_lu_18 (ReLU)              (None, 27, 27, 256)       0         
# _________________________________________________________________
# max_pooling2d_7 (MaxPooling2 (None, 13, 13, 256)       0         
# _________________________________________________________________
# conv2d_17 (Conv2D)           (None, 13, 13, 384)       885120    
# _________________________________________________________________
# re_lu_19 (ReLU)              (None, 13, 13, 384)       0         
# _________________________________________________________________
# conv2d_18 (Conv2D)           (None, 13, 13, 384)       1327488   
# _________________________________________________________________
# re_lu_20 (ReLU)              (None, 13, 13, 384)       0         
# _________________________________________________________________
# conv2d_19 (Conv2D)           (None, 13, 13, 256)       884992    
# _________________________________________________________________
# re_lu_21 (ReLU)              (None, 13, 13, 256)       0         
# _________________________________________________________________
# flatten_3 (Flatten)          (None, 43264)             0         
# _________________________________________________________________
# dense_9 (Dense)              (None, 4096)              177213440 
# _________________________________________________________________
# re_lu_22 (ReLU)              (None, 4096)              0         
# _________________________________________________________________
# dropout_4 (Dropout)          (None, 4096)              0         
# _________________________________________________________________
# dense_10 (Dense)             (None, 4096)              16781312  
# _________________________________________________________________
# re_lu_23 (ReLU)              (None, 4096)              0         
# _________________________________________________________________
# dropout_5 (Dropout)          (None, 4096)              0         
# _________________________________________________________________
# dense_11 (Dense)             (None, 10)                40970     
# =================================================================
# Total params: 197,362,058
# Trainable params: 197,362,058
# Non-trainable params: 0
# _________________________________________________________________

Loss 3가지

loss function 사용하는 3가지 방법

1. 객체지향 => 옵션 사용 가능

2. 함수형 => 옵션 사용 가능하지만 parital을 사용해야 한다 (Higer order function)

3. 함수형 단축 => 옵션 사용 불가

 

label encoding 방식에 따라서 loss function 방식도 달라져야 한다

- one-hot encoding (Multi label 일때)

- sparse label encoding (Multi label 아닐때) => Sparse가 붙은 loss function 사용

model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False))

tf.keras.utils.to_categorical # label에서 one-hot으로 바꿔준다
# <function tensorflow.python.keras.utils.np_utils.to_categorical(y, num_classes=None, dtype='float32')>

 

반응형

'Computer_Science > Visual Intelligence' 카테고리의 다른 글

20일차 - 논문 수업 (CNN)  (0) 2021.10.08
19일차 - 논문 수업 (CNN)  (0) 2021.10.08
17일차 - 논문 수업 (CNN)  (0) 2021.10.04
16일차 - 논문 수업 (CNN)  (0) 2021.10.04
15일차 - CNN  (0) 2021.10.04
728x90
반응형

Convolutional Neural Network

위 막대 그래프는 다양한 분류 방법의 테스트 셋에 대한 오류율 이다

[deslant]는 기울어진 데이터 셋을 기반으로 학습된 분류 모델이다

[dist]는 인위적으로 왜곡된 샘플이 추가된 학습 데이터를 사용한 모델이다

다른 알고리즘과 비교했을 때 성능이 가장 좋았고, 자

기 자신의 예전 모델과도 비교해서 가장 성능이 좋았다

(Boosted 된 LeNet-4가 성능이 가장 좋았다)

Bagging

Bagging은 샘플을 무작위로 여러 번 뽑아 각 모델을 학습시켜 결과를 평균내는 방법이다 (Ensemble 기법중 하나)

Bagging 복원 추출을 하고 전체 데이터에서 일부를 뽑아 모델을 학습시킨다

여러 모델중에서 평균을 내는 방법이기 때문에 편차를 줄여주고 Overfitting을 피할 수 있는 방법이다

일반적인 모델을 만드는 것에 강점이 있다

ex) random forest

Boosting

Boosting은 Bagging과 동일하게 전체 데이터에서 일부 데이터만 사용하고 복원 추출을 한다

두 방법의 차이점은 잘 못 분류한 데이터들이 있을 경우 가중치를 두고 모델을 점차 강화한다는 점이다

학습이 끝나면 나온 결과에따라 가중치가 재분배 된다

오답에 대해 높은 가중치를 부여하고, 정답에 대해 낮은 가중치를 부여하기 때문에 오답에 더욱 집중하여 학습을 하게 된다

Bagging보다 좀 더 맞추기 어려운 문제나 성능을 좋은 장점은 있지만 outlier에 취약한 단점을 지니고 있다

ex) XGBoost, AdaBoost, GradientBoost

Convolution 두 가지 관점

1. 미리 자르는 방식

2. 이동하여 자르는 방식 (좌상->우하)

이미지 데이터에 대해서 전통적인 NN보다 CNN이 더 좋은 이유

전통적인 NN에서 이미지 데이터를 2D에서 1D로 변형할 때 두 가지 문제가 발생한다

1. column이 많아지는 문제

2. Locality를 상실한다

그러나 CNN에서는 두 가지 문제를 해결할 수 있다

1. convolution filter를 거치는 것은 데이터의 차원을 줄이지않고 그대로 사용하기 때문에 column이 많이 늘어나지 않는다. 즉, 적은 column을 갖는다

2. convolution filter를 거치면 특징이 있는지 없는지 관한 데이터로 변형 되기 때문에 Locality를 잃지 않는다

NN은 오른쪽 사진이 사람이라는 것과 왼쪽 사진이 사람이 아니라는 것을 잘 구별은 하지만

사람의 얼굴 크기가 많이 달라진다거나 방향이 비틀어졌거나 다른 위치에 있으면 사람이더라도

사람이라고 구별을 잘 못하는 경우가 발생할 수 있다

 

반면 CNN은 오른쪽 사진이 사람이라는 것과 사람의 얼굴 크기가 다르거나, 위치가 다르거나 방향이 뒤틀려도 잘 구별한다

하지만 왼쪽 사진 처럼 사람의 특징이 부분 별로 나뉘어져 있어도 전체를 보고 판단하지 않기 때문에 사람이라고 구별하는 오류를 범할 수 있다

Locally connected neural network

Locally connected neural network는 전통적인 NN에서 특징들의 위치 데이터에 민감하여

다양한 경우의 이미지를 구별하지 못하는 문제점을 보완한 방법이다

(ex) 크기가 다르거나 뒤틀리거나 하는 이미지를 구별하지 못하는 문제)

가까운 노드 끼리만 연결된다

 

연산 복잡도도 줄어들고 상대적인 위치에 대한 정보를 보기 때문에

shared weight를 쓰지 않았기 때문에 같은 값이면 같은 특징이다라는 점을 활용하지 못한다

Convolutional neural network(s

hared-weight local)

CNN은 shared-weight를 사용하면서 locally connected neural network인 모델이다

shared-weight를 사용하기 때문에 같은 값이면 같은 특징이다라는 점을 활용할 수 있고,

locally connected하기 때문에 부분적인 특징을 보고 특징이 있는지 없는지 여부를 판단할 수 있다

(Locality를 잃지 않는다)

CNN의 가정

1. Stationarity of statistics

- 정상성

- 이미지에서의 정상성이란 이미지의 한 부분에 대한 통계가 다른 부분들과 동일하다는 가정을 한다

- 이미지에서 한 특징이 위치에 상관없이 여러 군데 존재할 수 있고 특정 부분에서 학습된 특징 파라미터를 이용해

다른 위치에서도 동일한 특징을 추출할 수 있다는 의미이다

2. Locality of pixel dependencies

- 이미지는 작은 특징들로 구성되어 있기 때문에 각 픽셀들의 종속성은 특징이 있는 작은 지역으로 한정된다.

- 이미지를 구성하는 특징들은 이미지 전체가 아닌 일부 지역에 근접한 픽셀들로만 구성되고

근접한 픽셀들끼리만 종속성을 가진다

위 그림에서 왼쪽의 경우와 오른쪽의 경우는 같은 것이라 판단할 수 있지만

가운데의 것도 같은 것이라고 판단할 수 있을까?

convolutional layer를 한 번만 통과했다면 다른 것이라 판단할 수 있지만

layer를 여러번 통과한다면 세 가지 경우 모두 같은 특성이라고 볼수 있게 된다

=> layer가 많으면 많을 수록 좋은 점

 

Type Markdown and LaTeX: 𝛼^2

import tensorflow as tf 
from sklearn.datasets import load_digits 
import matplotlib.pyplot as plt

tf.keras.layers.Dense 
tf.keras.layers.LocallyConnected2D # weight를 공유하지 않는다 / 애매하기 때문에 이것도 저것도 아닌 상황에서 성능이 좋을 수 있다 
tf.keras.layers.Conv2D
tf.keras.layers.MaxPool2D (tf.keras.layers.MaxPooling2D)
tf.keras.layers.AvgPool2D (tf.keras.layers.AveragePooling2D)

tf.keras.layers.Conv2D is tf.keras.layers.Convolution2D # 단축 표현 
# True

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()
data = load_digits()

padding

import numpy as np 
import scipy 
from scipy.ndimage import convolve
from scipy.signal import convolve, convolve2d

a = np.array([-1,0,1])
b = np.arange(5)

convolve(a, b, 'valid') 
# array([-2, -2, -2])

convolve 연산 방법

-1, 0, 1 => 1, 0, -1 (상하,좌우 반전)

0 0 1 2 3 4 x 1 0 -1 곱, 합 => (0x1) + (0x0) + (1x-1) = -1

0 0 1 2 3 4 x 1 0 -1 곱, 합 => (0x1) + (1x0) + (2x-1) = -2

0 0 1 2 3 4 x 1 0 -1 곱, 합=> (1x1) + (2x0) + (3x-1) = -2

 

a = np.array([-1,0,1])
b = np.arange(4)
convolve(a, b, 'same') # zero padding을 사용한다 
# array([-1, -2, -2])

convolve 연산 방법

-1, 0, 1 => 1, 0, -1 (상하,좌우 반전)

0 0 1 2 3 x 1 0 -1 곱, 합 => (0x1) + (0x0) + (1x-1) = -1

0 0 1 2 3 x 1 0 -1 곱, 합 => (0x1) + (1x0) + (2x-1) = -2

0 0 1 2 3 x 1 0 -1 곱, 합 => (1x1) + (2x0) + (3x-1) = -2

 

a = np.array([-1,0,1])
b = np.arange(4)

convolve(a, b, 'full') 
# array([ 0, -1, -2, -2,  2,  3])

convolve 연산 방법

-1, 0, 1 => 1, 0, -1 (상하,좌우 반전)

# 하나라도 걸치면 연산 할 수 있도록 0을 추가한다

0 0 0 1 2 3 0 0 x 1 0 -1 곱, 합 => (0x1) + (0x0) + (0x-1) = 0

0 0 0 1 2 3 0 0 x 1 0 -1 곱, 합 => (0x1) + (0x0) + (1x-1) = -1

0 0 0 1 2 3 0 0 x 1 0 -1 곱, 합 => (0x1) + (1x0) + (2x-1) = -2

0 0 0 1 2 3 0 0 x 1 0 -1 곱, 합 => (1x1) + (2x0) + (3x-1) = -2

0 0 0 1 2 3 0 0 x 1 0 -1 곱, 합 => (2x1) + (3x0) + (0x-1) = 2

0 0 0 1 2 3 0 0 x 1 0 -1 곱, 합 => (3x1) + (0x0) + (0x-1) = 3 

a = np.array([[-1,0],[1,0]])
b = np.arange(9).reshape(3,3)

convolve2d(a,b,'full') # zero padding을 함으로써 값이 공평한 횟수로 연산된다 즉, 공평하게 특성을 검출할 수 있다 

# array([[ 0, -1, -2,  0],
#        [-3, -3, -3,  0],
#        [-3, -3, -3,  0],
#        [ 6,  7,  8,  0]])

convolve 연산 방법

-1, 0 => 0, 1 (상하,좌우 반전)

1, 0 0,-1

0 0 0 0 0

00 1 2 0 0 1 좌상에서 우하로 연산한다

0 3 4 5 0 x 0 -1

0 6 7 8 0

0 0 0 0 0

convolve2d(a,b,'valid')
# array([[-3, -3],
#        [-3, -3]])
convolve2d(a,b,'same')
# array([[-3, -3],
#        [-3, -3]])

Invariance vs Equivariance

Invariance: 불변성

- CNN은 invariance하다 (Translation에 대해서 invariance하다)

- 똑같은 특징이 있으면 위치와 상관없이 똑같은 값을 예측한다

- CNN은 Rotation, size, viewpoint, illumination에 대해서는 invariance하지 않다

(CNN은 회전되거나, 시점이다르거나, 사이즈가 다르거나 밝기가 다른 이미지는 예측할 수 없다)

 

Equivariance: 동일한

- input의 위치가 달라지면 output의 위치가 달라진다

- convolution 연산은 equivariance의 특징을 갖는다

CNN 모델이 범용적으로 쓰이려면 data augmentation이 필요하다

Pooling

pooling은 대표적인 값으로 줄이기 때문에 회전된 이미지도 같은 값으로 인식하는 경우가 발생할 수 있다

크기가 줄면서 정보의 손실이 발생할 수 있지만 invariance한 특성을 보장하기 때문에 오히려 성능이 늘어날 수 있다 (항상 그런건 아님)

Striving for Simplicity: The All Convolutional Net

stride를 크게하면 pooling 쓰지 않으면서 pooling을 한 것 같은 효과를 볼 수 있다

conv + pooling => stride를 크게 하여 conv하나로 만든다

pooling을 하는 것보다 연산복잡도가 줄어들기 때문에 성능은 유지하면서 학습 속도를 빠르게 할 수 있다는 장점이 있다

논문 LeNet-5 구현

input_ = tf.keras.Input(shape=(32,32,1))
x = tf.keras.layers.Conv2D(6, 5)(input_) # filter 개수, filter 크기 / stride는 생략되었기 때문에 1이라 가정한다 / padding: valid
x = tf.keras.layers.Activation('tanh')(x) # 그 당시 LeRU가 없었다 
model = tf.keras.models.Model(input_, x)

model.summary()
# Model: "model_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 32, 32, 1)]       0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 28, 28, 6)         156       
_________________________________________________________________
activation_1 (Activation)    (None, 28, 28, 6)         0         
=================================================================
Total params: 156
Trainable params: 156
Non-trainable params: 0
_________________________________________________________________

 

반응형

'Computer_Science > Visual Intelligence' 카테고리의 다른 글

19일차 - 논문 수업 (CNN)  (0) 2021.10.08
18일차 - 논문 수업 (CNN)  (0) 2021.10.08
16일차 - 논문 수업 (CNN)  (0) 2021.10.04
15일차 - CNN  (0) 2021.10.04
14일차 - 딥러닝 (Tensorflow) (3)  (0) 2021.10.04
728x90
반응형

Convolutional Neural Network

합성곱 신경망

Conv2D

- feature를 뽑아내는 3x3 filter

- filter 개수는 32개

- neural network에서 perceptron이 32개있는 것과 유사하다

- 3x3 filter는 처음에 랜덤하게 숫자가 정해진다

- linear연산이기 때문에 다양한 조합을 만들수가 없다

- Hyperparameter: filter 개수, filter 모양, padding, strides

- 3차원 연산

Activation

- ReLU - 0보다 작은 값은 0으로 0보다 큰 값은 값 그대로 출력한다

- non-linear한 함수를 활용하여 다양한 조합을 만든다

Conv2D

- filter를 다시 한번 더 통과 시킨다

- 원본 데이터에서 filter를 적용하면서 특징을 배워간다

- 가장 특징이 잘 나타내도록 변화 하는 작업을 수행한다

Activation

- filter 하나당 9개 weight를 갖고 총 32개 filter가 있기 때문에 총 288개 weight가 생긴다

MaxPooling2D

- 계산 복잡도를 줄이기 위해서 Maxpooling을 사용하여 크기를 줄인다

- 숫자 데이터에 대한 서로 다른 32가지 관점으로 해석을 한 후 데이터를 만들어 낸다

Dropout

- dropout을 사용하게 되면 좀 더 의미 있는 특징을 추출하게 된다

- 노드들을 무작위로 생략시키면서 학습을 하게되면 parameter들의 co-adaptation되는 것을 막을 수 있다

※ co-adaptation : 학습하는 도중 같은 층에서 두 개 이상의 노드의 입력 및 연결강도가 같아지게 되면,

아무리 학습이 진행되어도 그 노드들은 같은 일을 수행하게 되어 불필요한 중복이 생기는 문제

Flatten

- 특징이 잘 나타나도록 변경된 데이터 셋을 학습시키기 위해 1차원 데이터로 변환한다

최종적으로 0-9까지의 특징을 가장 잘 파악할 수 있는 32가지 filter를 학습하게 된다

CNN의 문제점 중 하나는 사람의 얼굴 특징이 모두 있지만 분리되어 있는 사진인 경우에도 얼굴이라고 인식하는 오류를 범한다

3차원 연산

CNN은 color이미지 일때 R,G,B 체널로 분리시켜 3차원 연산을 한다

CNN에서는 흑백 이미지일때에도 차원을 증가시켜 3차원 연산을 한다 (채널 데이터를 갖고 있어야 한다)

CNN에서 이미지를 변화시키는 이유는 구분시키는 특징을 잘 파악하는 데이터로 변화시키기 위해서 이다

서로 분리된 채널에 의한 이미지들은 element wise 연산을 통해 하나의 데이터에 대한 특징을 파악할 수 있다

Reference

Basic Convnet for MNIST

!pip install mglearn

초기값의 중요성

import mglearn
# dtype이 uint8일 때 최대값은 255, 최소값은 0 
# MinMaxScaler로 정규화 할 경우 0과 1사이로 값이 바뀌기 때문에 정사각형 형태로 데이터가 분포한다   
# 따라서 방향에 대한 크기변화가 없기 때문에 빠른 학습속도와 정확한 학습결과를 기대할 수 있다 
mglearn.plot_scaling.plot_scaling()

왜 convolution 연산에 대해서 합을 할까?

convolution 연산을 할때 element wise 연산을 하게되면(분리된 채널에서 합쳐질 때)특성을 알 수 없을 수도 있다

물론 depth wise convolution은 각각의 특성만 독립적으로 연산하는 경우도 있다

그러나 더하는 것이 성능이 더 좋고, convolution 연산 결과가 원본 이미지의 의미가 변하는 경우는 거의 나오지 않는다

왜 그런 경우가 나오지 않을까?

weight와 bias는 학습을 통해서 찾기 때문에 채널별로 서로 다른 결과가 나올수 있도록 학습이 되기 때문이다

(kernel은 특성을 잘 파악하도록 학습이 된다)

합하는 것이 왜 좋을까?

전통적인 NN에 영향을 받아서 hierarchy한 것을 고려했기 때문에 hierarchy 특징을 학습할 수 있다

중간 결과 및 filter 이미지 확인하기

 

import tensorflow as tf 
import numpy as np

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()

from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

data = load_digits()

data.data[0]
array([ 0.,  0.,  5., 13.,  9.,  1.,  0.,  0.,  0.,  0., 13., 15., 10.,
       15.,  5.,  0.,  0.,  3., 15.,  2.,  0., 11.,  8.,  0.,  0.,  4.,
       12.,  0.,  0.,  8.,  8.,  0.,  0.,  5.,  8.,  0.,  0.,  9.,  8.,
        0.,  0.,  4., 11.,  0.,  1., 12.,  7.,  0.,  0.,  2., 14.,  5.,
       10., 12.,  0.,  0.,  0.,  0.,  6., 13., 10.,  0.,  0.,  0.])
       
data.images[0]
array([[ 0.,  0.,  5., 13.,  9.,  1.,  0.,  0.],
       [ 0.,  0., 13., 15., 10., 15.,  5.,  0.],
       [ 0.,  3., 15.,  2.,  0., 11.,  8.,  0.],
       [ 0.,  4., 12.,  0.,  0.,  8.,  8.,  0.],
       [ 0.,  5.,  8.,  0.,  0.,  9.,  8.,  0.],
       [ 0.,  4., 11.,  0.,  1., 12.,  7.,  0.],
       [ 0.,  2., 14.,  5., 10., 12.,  0.,  0.],
       [ 0.,  0.,  6., 13., 10.,  0.,  0.,  0.]])
plt.imshow(data.images[0], cmap='gray')

data.images.shape
# (1797, 8, 8)
image = data.images.reshape(1797,8,8,1) # CNN에서 사용하는 연산하기 위해서 데이터 하나가 3차원이 되도록 데이터를 변화 시켰다

layer1 = tf.keras.layers.Conv2D(2, (3,3)) # filter 개수, filter 모양(단축 표현 가능 (3,3)=>3)
layer1.built # 일시키기 전까지 초기화가 안된다 => lazy Evaluation / 내부적으로 im2col
# False
layer1(image[0]) # 동시에 여러개 연산하기 때문에 안된다 / 데이터를 하나 연산하더라도 4차원 형태로 만들어 줘야 한다 
# ValueError: Input 0 of layer conv2d_1 is incompatible with the layer: : expected min_ndim=4, found ndim=3. Full shape received: (8, 8, 1)
layer1(image[0][tf.newaxis]) 
<tf.Tensor: shape=(1, 6, 6, 2), dtype=float32, numpy=
array([[[[-10.622592  ,   0.6645769 ],
		 ...
         [ -3.2092843 ,  -0.36533844]]]], dtype=float32)>
layer1.weights # weight를 xavier glorot uniform방식으로 초기화 하고 연산을 한다 
# [<tf.Variable 'conv2d_1/kernel:0' shape=(3, 3, 1, 2) dtype=float32, numpy=
#  array([[[[ 0.06324342,  0.15853754]],
#          [[-0.14388585, -0.19692683]],
#          [[-0.40798104,  0.04143384]]],
#         [[[-0.24675035, -0.07410842]],
#          [[-0.3730538 ,  0.22583339]],
#          [[-0.22161803,  0.13686094]]],
#         [[[ 0.11666891,  0.40331647]],
#          [[-0.17990309,  0.3350769 ]],
#          [[-0.34412956, -0.15513435]]]], dtype=float32)>,
#  <tf.Variable 'conv2d_1/bias:0' shape=(2,) dtype=float32, numpy=array([0., 0.], dtype=float32)>]
len(layer1.weights)
# 2

layer1.weights[0] # filter / kernel은 (3,3,1)가 2개 있다로 해석해야 한다 
# <tf.Variable 'conv2d_1/kernel:0' shape=(3, 3, 1, 2) dtype=float32, numpy=
# array([[[[ 0.06324342,  0.15853754]],
#         [[-0.14388585, -0.19692683]],
#         [[-0.40798104,  0.04143384]]],
#        [[[-0.24675035, -0.07410842]],
#         [[-0.3730538 ,  0.22583339]],
#         [[-0.22161803,  0.13686094]]],
#        [[[ 0.11666891,  0.40331647]],
#         [[-0.17990309,  0.3350769 ]],
#         [[-0.34412956, -0.15513435]]]], dtype=float32)>
layer1.weights[0][...,0] # 첫번째 filter 
<tf.Tensor: shape=(3, 3, 1), dtype=float32, numpy=
array([[[ 0.06324342],
        [-0.14388585],
        [-0.40798104]],

       [[-0.24675035],
        [-0.3730538 ],
        [-0.22161803]],

       [[ 0.11666891],
        [-0.17990309],
        [-0.34412956]]], dtype=float32)>
        
layer1.weights[0][...,1] # 두번째 filter 
<tf.Tensor: shape=(3, 3, 1), dtype=float32, numpy=
array([[[ 0.15853754],
        [-0.19692683],
        [ 0.04143384]],

       [[-0.07410842],
        [ 0.22583339],
        [ 0.13686094]],

       [[ 0.40331647],
        [ 0.3350769 ],
        [-0.15513435]]], dtype=float32)>
        
np.squeeze(layer1.weights[0][...,1])
array([[ 0.15853754, -0.19692683,  0.04143384],
       [-0.07410842,  0.22583339,  0.13686094],
       [ 0.40331647,  0.3350769 , -0.15513435]], dtype=float32)
       
tf.reshape((np.squeeze(layer1.weights[0][...,1])),(3,3))
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[ 0.15853754, -0.19692683,  0.04143384],
       [-0.07410842,  0.22583339,  0.13686094],
       [ 0.40331647,  0.3350769 , -0.15513435]], dtype=float32)>
plt.imshow(np.squeeze(layer1.weights[0][...,1]), cmap='gray')

plt.imshow(np.squeeze(layer1.weights[0][...,0]), cmap='gray')

layer1.weights[1] # bias
# <tf.Variable 'conv2d_1/bias:0' shape=(2,) dtype=float32, numpy=array([0., 0.], dtype=float32)>

layer1_result = layer1(image[0][tf.newaxis])
layer1_result.shape
# TensorShape([1, 6, 6, 2])

layer1_result[0,...,0]
# <tf.Tensor: shape=(6, 6), dtype=float32, numpy=
array([[-10.622592 , -17.233952 , -14.855642 , -15.18894  , -13.4780655,
         -5.6591477],
       [-14.596352 , -16.461687 ,  -8.46327  , -12.294258 , -13.634556 ,
         -5.975335 ],
       [-14.3555565,  -9.104046 ,  -1.3667734,  -9.231415 , -13.976131 ,
         -5.8030467],
       [-13.614567 ,  -7.204097 ,  -0.2758534,  -9.567868 , -13.996439 ,
         -5.7096176],
       [-13.090911 ,  -9.931416 ,  -5.137371 , -12.04954  , -11.825691 ,
         -4.75425  ],
       [-10.976872 , -13.707218 , -12.3282795, -12.9457   , -10.296714 ,
         -3.2092843]], dtype=float32)>
         
plt.imshow(layer1_result[0,...,0], cmap='gray') # 8x8 이미지에서 3x3 filter를 썻기 때문에 8-3+1 = 6 => 6x6 결과가 나온다

Convolution layer를 통과한 데이터

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()
X_train = X_train.reshape(-1,28,28,1)
input_ = tf.keras.Input(shape=(28,28,1))
x = tf.keras.layers.Conv2D(10,3)(input_)
model = tf.keras.models.Model(input_,x)

plt.imshow(model(X_train)[0][...,0], cmap='gray')

plt.imshow(model(X_train)[0][...,1], cmap='gray')

plt.imshow(model(X_train)[0][...,2], cmap='gray')

Convolution layer -> ReLU를 통과한 데이터

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()
X_train = X_train.reshape(-1,28,28,1)
input_ = tf.keras.Input(shape=(28,28,1))
x = tf.keras.layers.Conv2D(10,3)(input_)
x = tf.keras.layers.ReLU()(x)
model = tf.keras.models.Model(input_,x)

plt.imshow(model(X_train)[0][...,0], cmap='gray')

plt.imshow(model(X_train)[0][...,0], cmap='binary')

plt.imshow(model(X_train)[0][...,1], cmap='gray')

plt.imshow(model(X_train)[0][...,1], cmap='binary')

plt.imshow(model(X_train)[0][...,2], cmap='gray')

plt.imshow(model(X_train)[0][...,2], cmap='binary')

LeNet-5

Gradient-Based Learning Applied to Document Recognition
Yann LeCun, Yoshua Bengio 등..

최초의 Convolutional Neural network가 상업적으로 성공한 논문

Feature extracion module은 trainable classifier module의 성능을 높이기 위한 수단이었다

나중에 Feature extracion module와 trainable classifier module를 결합해서 End-to-End로 만들었다

32x32를 한 이유는 정 중앙에 숫자를 넣기 위해서 조금 더 크게 만들었다

=> 제약 조건을 만들었다

성능을 높이기 위해서 가정을 추가한 것이다

 

이미지 크기가 크면 클수록 연산해야 할것이 많아지고, 많은 데이터가 필요하다

그런데 subsampling을 한다는 것은 줄이는 것이기 때문에 정보의 손실이 발생한다

이 지점에서 trade off가 발생한다

 

데이터가 많을 경우 pooling을 안하는 것이 성능이 더 좋다 하지만, 데이터가 적을 경우는 pooling을 하는 것이 더 좋다

 

subsampling으로 계산 복잡도를 줄였기 때문에 특징을 더 늘려야 한다 (이미지 피라미드)

데이터가 많으면 많을 수록 column이 많이 있는 것이 좋다(풍부한 nuance를 갖기 때문에)

특징이 있는지 없는 데이터 형태로 변환 시킨다

Subsampling

전체에서 일부만 뽑아서 줄이는 것 ex) pooling

 

 

 

 

 

 

 

 

 

반응형

'Computer_Science > Visual Intelligence' 카테고리의 다른 글

18일차 - 논문 수업 (CNN)  (0) 2021.10.08
17일차 - 논문 수업 (CNN)  (0) 2021.10.04
15일차 - CNN  (0) 2021.10.04
14일차 - 딥러닝 (Tensorflow) (3)  (0) 2021.10.04
13일차 - 딥러닝 (Tensorflow) (2)  (0) 2021.09.28

+ Recent posts