안녕하세요. 지난 포스티의 [DA] Data Augmentation Using Random Cropping and Patch for Depp CNNs (IEEE TCSVT2020)에서는 4장의 영상을 Mix 하는 RICAP에 대해서 알아보았습니다. 오늘은 ResizeMix라는 방법에 대해서 소개하도록 하겠습니다.
Background
많은 심층신경망 모델들은 현재 영상 분류, 객체 탐지, 영상 분할과 같이 저레벨 및 고레벨의 다양한 컴퓨터 비전 태스크에서 활용되고 있습니다. 이를 위해서 중요한 것은 데이터셋의 확보가 중요하죠. 여기서, 데이터 증강은 충분한 양의 데이터가 없을 때 주어진 모델의 일반화 성능을 향상시키는 데 도움이 됩니다.
이러한 데이터 증강의 가장 중요한 목표는 데이터의 다양성 (Data Diversity)를 향상시켜 모델의 일반화 능력을 증가시키는 것 입니다. 가장 간단한 예시로 지난 포스팅들에서 보앗던 CutMix, MixUp과 같은 방법들이 있습니다. 하지만, CutMix는 영상의 전체 정보를 활용하지 않기도 하고 가끔 중요한 객체의 정보를 가리기도 하기 때문에 학습의 효율성을 떨어뜨릴 수도 있습니다. 이러한 문제를 방지하게 위해 Saliency라고 부르는 모델의 입력 영상 내에서 중요하게 생각되는 영역을 보존하는 것을 생각해볼 수 있습니다. 하지만, Saliency information을 사용하게 되면 기본적으로 추가적인 모듈을 사용해야하기 때문에 복잡하고 추가 비용이 발생합니다. 이러한 방법이 대표적으로 SaliencyMix, PuzzleMix 등이 있습니다.
본 논문에서는 이러한 문제를 지적하며 Saliency information이 데이터 증강에 굳이 필요한지 검증합니다. 이를 기반으로 ResizeMix라고 하는 새로운 데이터 증강 방법론을 제시합니다.
Checking the Importance of Saliency Information for Mixing Data
1). Preliminaries
일단, $I_{s}$와 $I_{t}$를 각각 $\mathbb{R}^{H \times W}$ 크기를 가지는 소스 영상 및 타겟 영상이라고 정의하겠습니다. 여기서 "소스 (source)"는 패치를 추출하는 영상을 의미하고 "타켓 (Target)"는 추출한 패치를 붙여넣는 영상을 의미합니다. 그리고 $P \in \mathbb{R}^{H_{P} \times W_{P}}$를 소스 영상 $I_{s}$에 추출한 패치라고 가정하겠습니다.
저희는 이번 절에서 saliency 정보가 얼마나 중요한 지 판단해야합니다. 이를 위해, saliency 정보를 정의해야합니다. 일반적으로 다양한 방법이 있지만 본 논문에서는 Grad-CAM이라는 방법을 이용해서 해당 영역이 높게 활성화되면 saliency region, 활성화가 안되면 non-saliency region으로 정의합니다. 이를 다음과 같이 적도록 하죠.
$$\begin{cases} C_{s} &= \{(x, y) | A(x, y) \ge t_{u}\} \\ C_{ns} &= \{ (x, y) | A(x, y) \le t_{l} \}\end{cases}$$
여기서, $t_{u}$와 $t_{l}$는 saliency 및 non-saliency를 판단하는 threshold입니다. $A(x, y)$를 Grad-CAM을 이용해 추출된 activation map을 의미하죠. 그리고 $R(x_{l}, x_{r}, y_{b}, y_{t})$를 영상 내의 작은 영역이라고 부르도록 하겠습니다. 그러면 $R_{s}$와 $R_{ns}$는 각각 영상 내에 saliency 및 non-saliency한 영역을 의미하겠죠. 마지막으로 $Paste$라는 연산을 다음과 같이 정의합니다.
$$Paste (P, I_{t}, R) : I_{t}[R] = I_{t}[x_{l} : x_{r}, y_{b} : y_{t}] = P$$
그러면 저희가 보았던 CutMix는 다음과 같이 쓸 수 있습니다.
$$\begin{cases} &P = I_{s}[R_{r}] \\ &I_{m} = Paste(P, I_{t}, R_{r}) \\ &l_{m} = \lambda l_{s} + (1 - \lambda) l_{t} \end{cases}$$
여기서 $R_{r}$는 무작위로 샘플링된 영상의 영역입니다. 그리고 $\lambda = \frac{H_{P} \times W_{P}}{H \times W}$로 정의됩니다. 즉, 섞인 두 영상의 비율에 따라서 새로운 레이블을 생성하는 것이죠. CutMix의 중요한 특징 중 하나는 추출된 패치의 위치 그대로 타겟 영상에 붙여지는 Corresponing 전략을 사용한다는 점 입니다. 이와 같이 한정된 위치로 붙이게 되면 생성되는 데이터의 다양성을 잃어버리는 문제가 있겠죠.
2). Checking Saliency Information
표1은 어떻게 (How) 소스 패치를 선택할 지와 어디에 (Where) 소스 패치를 타겟 영상에 붙일 것인 지에 대한 다양한 셋팅의 실험 결과를 보여주고 있습니다. 일반 저희는 "어디에" 패치를 붙이는 게 좋을지부터 알아보도록 하겠습니다.
그림2와 같이 이 부분에서는 3가지 전략을 생각해볼 수 있습니다. 각각 Saliency region, Non-saliency region, Random region 입니다. 표1에서 CutMix 아래의 (3), (4), (5)가 그 실험결과입니다. 일반적으로 저희가 생각했을 때는 타겟 영상의 Saliency 정보를 살리기 위해서 Non-saliency region에 붙이는 것이 성능이 가장 좋을 것으로 예상하지만 의외로 성능이 가장 낮았습니다. 뿐만 아니라 Random region에 붙이는 것이 가장 높은 성능을 달성하였죠. 이는 데이터의 다양성을 증대시키는 것이 더 중요한 결과임을 의미하죠.
다음에는 어떻게 소스 패치를 선택할지는 분석해보도록 하겠습니다. 그림3에서와 같이 3가지 전략을 생각해볼 수 있죠. 각각 Saliency region, Non-saliency region, Random region 입니다. 표1에서 (6), (7), (8)가 그 실험결과입니다. 일반적으로 저희가 생각했을 때는 소스 영상의 Saliency 정보를 살리기 위해서 saliency 패치를 붙이는 것이 성능이 가장 좋을 것으로 예상하지만 의외로 성능이 가장 낮았습니다. 뿐만 아니라 Random 패치를 붙이는 것이 가장 높은 성능을 달성하였죠. 이는 데이터의 다양성을 증대시키는 것이 더 중요한 결과임을 의미하죠.
ResizeMix
위 실험들을 토대로 saliency 정보는 데이터 증강에 있어 큰 도움이 되지 않는 다는 것을 알았습니다. 본 논문에서는 이러한 점을 지적하며 타겟 영상의 임의의 위치에 붙이는 것을 적용하고 소스 영상의 전체 정보를 활용하기 위해 영상의 크기를 통째로 줄인 뒤 타겟 영상에 붙이는 ResizeMix를 제안합니다. 이를 다음과 같이 쓸 수 있습니다.
$$\begin{cases} &P = T(I_{s}) \\ &I_{m} = Paste(P, I_{t}, R_{r}) \\ &l_{m} = \lambda l_{s} + (1 - \lambda) l_{t} \end{cases}$$
여기서, $T(\cdot)$은 resize 연산입니다. 그리고 scale factor인 $\tau$는 균등분포 $U(\alpha, \beta)$에서 무작위로 샘플링하여 사용합니다. ($\tau \sim U(\alpha, \beta)$) 그러면 $W_{P} = \tau W$ 이고 $H_{P} = \tau H$이기 때문에 $\lambda = \frac{H_{P} \times W_{P}}{H \times W} = \tau^{2}$임을 알 수 있습니다.
Experiment Results
1). CIFAR Classification Results
2). ImageNet Classification Results
Implementation Code
def resizemix_bbox(img, tau):
size = img.size()
W, H = size[2], size[3]
cut_w = np.int(W * tau)
cut_h = np.int(H * tau)
cx = np.random.randint(W)
cy = np.random.randint(H)
bbx1 = np.clip(cx - cut_w // 2, 0, W)
bby1 = np.clip(cy - cut_h // 2, 0, H)
bbx2 = np.clip(cx + cut_w // 2, 0, W)
bby2 = np.clip(cy + cut_h // 2, 0, H)
return bbx1, bby1, bbx2, bby2
def resizemix_forward(image, target, scope=(0.1, 0.8)):
# STEP0. Random Permutation
rand_index = torch.randperm(image.size()[0])
# STEP1. Extract patch from source image
tau = np.random.uniform(scope[0], scope[1])
bbx1, bby1, bbx2, bby2 = resizemix_bbox(image, tau)
source_patch = F.interpolate(image[rand_index], (bby2 - bby1, bbx2 - bbx1), mode='nearest')
# STEP2. Paste source patch into target image
image[:, :, bby1:bby2, bbx1:bbx2] = source_patch
lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (image.size()[2] * image.size()[3]))
return image, target, lam