안녕하세요. 지난 포스팅의 [DA] ResizeMix: Mixing Data with Preserved Object Information and True Labels (arxiv2020)에서는 Saliency information 보다는 데이터 증강을 통한 데이터 다양성을 증가시키는 것이 더욱 중요하다는 것을 강조하였으며 이를 기반으로 ResizeMix를 제안하였습니다. 오늘도 CutMix 기반의 논문인 Attentive CutMix에 대해서 소개해드리도록 하겠습니다.
Background
일반적으로 많이 사용되는 데이터 증강은 MixUp, CutOut, CutMix와 같은 방법이 있습니다. 이러한 방법들은 모두 심층 신경망 모델의 객체 인식 능력과 지역화 능력을 크게 향상시키는 데 도움이 되었죠. 하지만, 문제는 Randomness 입니다. (ResizeMix와 서로 다른 주장을 펼치고 있네요.)
그림1은 Randomness로 인한 MixUp, CutOut, CutMix의 문제점을 보여주고 있습니다. 핵심은 심층 신경망이 "개"라고 판단하거나 "고양이"라고 판단할만한 discriminative feature가 삭제된다는 것을 보여주고 있습니다. 그렇다면 이 discriminative feature를 어떻게 보존할 수 있을까요? 본 논문에서는 이를 위해 Attentive CutMix를 제안합니다. 그림1의 오른쪽 아래 그림과 같이 고양이라고 판단할만한 패치들은 개 사진에 붙히는 것을 볼 수 있죠.
Method
Attentive CutMix의 방식은 아주 단순합니다. 그림2를 보시면 쉽게 이해할 수 있습니다. 일단, 알고리즘의 최종 목표는 임의의 서로 다른 두 영상과 레이블을 입력받아 새로운 영상 $(\tilde{x}, \tilde{y})$를 만드는 것 입니다. 이는 기본적으로 CutMix 알고리즘과 동일하고 다른 점은 이 패치를 어떻게 선택할 것이냐 입니다.
이를 위해 본 논문에서는 입력 영상을 ImageNet-1K에 pretrained된 feature extraction network에 입력합니다. 그러면 그림2의 상단과 같이 $7 \times 7$ 크기의 heat map을 얻을 수 있죠. 이미 ImageNet-1K에 잘 학습된 모델을 통해 특징 맵을 뽑았기 때문에 고양이라고 판단할 만한 영역이 강하게 활성화될 것 입니다. 따라서, 높게 활성화된 $N$개의 패치를 선택합니다. 여기서, $N$은 하이퍼파라미터로 CutMix와 같이 두 영상의 비율을 결정하는 요소입니다. 높게 활성화된 $N$개의 패치를 입력 영상과 동일한 크기로 바꾼 뒤 해당 패치를 타겟 영상에 붙여주면 됩니다.
Experiment Results
1). CIFAR10 Classification Results
2). CIFAR100 Classification Results
Implementation Code
class AttentiveTargetTransform(object):
def __init__(self, placeholder={}):
self.placeholder = placeholder
def __call__(self, target):
target = [target, AttentiveInputTransform.PLACEHOLDER['rand_target']]
return torch.tensor(target)
class AttentiveInputTransform(object):
TOP_K = 6
GRID_SIZE = 32
PLACEHOLDER = {}
def __init__(self, args, dataset):
super(AttentiveInputTransform, self).__init__()
model = models.resnet50(pretrained=True)
self.device = args.device
self.data_type = args.data_type
self.dataset = dataset
temp_model = nn.Sequential(*list(model.children())[:-2])
self.model = temp_model.cuda()
def __call__(self, image):
rand_index = np.random.randint(0, len(self.dataset))
rand_img, rand_target = self.dataset[rand_index]
AttentiveInputTransform.PLACEHOLDER['rand_target'] = rand_target
ori_size = image.size
image = image.resize((224, 224))
rand_img = rand_img.resize((224, 224))
attentive_regions = self._get_attentive_regions(image)
rand_img = self._replace_attentive_regions(rand_img, image, attentive_regions)
return rand_img.resize(ori_size)
def _get_attentive_regions(self, image):
"""
CIFAR return top k from 8x8
ImageNet return top k from 7x7
"""
x = F.to_tensor(image).unsqueeze_(0).cuda()
output = self.model(x)
last_feature_map = output[0][-1].detach().cpu().numpy()
return self._top_k(last_feature_map)
def _top_k(self, features):
idx = np.argpartition(features.ravel(), features.size - AttentiveInputTransform.TOP_K)[-AttentiveInputTransform.TOP_K:]
return np.column_stack(np.unravel_index(idx, features.shape))
def _replace_attentive_regions(self, rand_img, image, attentive_regions):
"""
rand_img: the img to be replaced
image: where the 'patches' come from
attentive_regions: an array contains the coordinates of attentive regions
"""
np_rand_img, np_img = np.array(rand_img), np.array(image)
for attentive_region in attentive_regions:
self._replace_attentive_region(np_rand_img, np_img, attentive_region)
return Image.fromarray(np_rand_img)
def _replace_attentive_region(self, np_rand_img, np_img, attentive_region):
x, y = attentive_region
x1, x2, y1, y2 = AttentiveInputTransform.GRID_SIZE * x, AttentiveInputTransform.GRID_SIZE * (x+1), AttentiveInputTransform.GRID_SIZE * y, AttentiveInputTransform.GRID_SIZE * (y+1)
region = np_img[x1:x2, y1: y2]
np_rand_img[x1:x2, y1:y2] = region