CutMix augmentation์ ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ์ ์ ์ฉํ ๋ ผ๋ฌธ์ด๋ค. ๋ ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ ๊ฐ์ ์ผ๋์ผ ๋์๊ด๊ณ๋ฅผ ์ฐพ๊ณ , ์ด๋ฅผ ๋ฐํ์ผ๋ก ๋ ๋ฐ์ดํฐ๋ฅผ ์๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ ์ํ๋ค.
Paper: https://arxiv.org/pdf/2101.01461.pdf
Code: https://github.com/cuge1995/PointCutMix
Introduction
์ด๋ฏธ์ง ๋ฐ์ดํฐ์ ๋ํด mixed sample data augmentation (MSDA)๊ฐ ํ๋ฐํ๊ฒ ์ฌ์ฉ๋์ด ์๋ค. ๋ํ์ ์ธ ์์๋ MixUp (Zhang et al., 2018)๊ณผ CutMix (Yun et al., 2019) ๊ฐ ์๋ค.
๋ณธ ๋ ผ๋ฌธ์์๋ ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ์ ๋ํด CutMix๋ฅผ ์ํํ๋ PointCutMix๋ฅผ ์ ์ํ๋ค.
Related Work
- ์ผ๋ฐ์ ์ผ๋ก ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ์ ๋ํด ์ฌ์ฉ๋๋ augmentation์ผ๋ก๋ random rotation, jittering, scaling ๋ฑ์ด ์๋ค. (Qi et al., 2017a;b)
- ์ด์ธ์๋ PointAugment (Li et al., 2020), PointMixUp (Chen et al., 2020) ๋ฑ์ด ์ ์๋์๋ค.
- PointAugment์ ๊ฒฝ์ฐ๋ adversarial training์ ์ด์ฉํด augmenter๊ณผ classifier network๋ฅผ ๋์์ ํ์ต์ํค๋ ๋ฐฉ๋ฒ์ผ๋ก, ํ์ต์ด ์ด๋ ต๋ค๋ ๋จ์ ์ด ์๋ค.
- PointMixUp์ ๋ง ๊ทธ๋๋ก ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ์ ๋ํด MixUp augmentation์ ์ํํ๋ ๋ฐฉ๋ฒ์ผ๋ก, local feature์ ์์ด๋ฒ๋ฆด ์ ์๋ค๋ ๋จ์ ์ด ์๋ค.
Method
1. Optimal assignment of point clouds
MSDA๋ฅผ ์ํํ๊ธฐ ์ํด์๋, ๋ sample์ ์ต์ ๋จ์์ ๋ํด ์ผ๋์ผ ๋์์ด ์ด๋ฃจ์ด์ง ์ ์์ด์ผ ํ๋ค.
์ต์ ๋จ์๋, ์ด๋ฏธ์ง์ ๊ฒฝ์ฐ์๋ ํฝ์ ์ ํด๋นํ๊ณ , ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ์ ๊ฒฝ์ฐ ํ๋์ ํฌ์ธํธ์ ํด๋นํ๋ค.
์ด๋ฏธ์ง์ ๊ฒฝ์ฐ resize, crop ๋ฑ์ ์ด์ฉํ๋ฉด ๋ sample ๊ฐ์ ํฝ์ ๊ฐ ์ผ๋์ผ ๋์์ ๊ฐ๋จํ ์ ์ํ ์ ์์ง๋ง, ํฌ์ธํธํด๋ผ์ฐ๋๋ ํด๋น ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์๋ค.
๋ณธ ๋ ผ๋ฌธ์์๋ PointMixUp (Chen et al., 2020)๊ณผ MSN (Liu et al., 2020a)์์ ์ฌ์ฉํ ๋ฐฉ๋ฒ์ ๋ฐ๋ผ, Earth Mover's Distance (EMD)๋ฅผ ์ํ optimal assignment $\phi^*$๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ ์ํ๋ค.
$\phi^*=argmin_{\phi\in\Phi}\sum_i||x_{1, i}-x_{2,\phi(i)}||_2$
์ด๋ ๋ ์ ์ฌ์ด ๊ฑฐ๋ฆฌ๊ฐ ์ต์๊ฐ ๋๊ฒ ํ๋ ์ผ๋์ผ ๋์ ํจ์๋ฅผ ์ฐพ๋ ๊ฒ๊ณผ ๊ฐ๋ค.
EMD๋ $\phi^*$๋ฅผ ์ด์ฉํด ๋ค์๊ณผ ๊ฐ์ด ์ ์๋๋ค.
$EMD=\frac1N\sum_i||x_{1,i}-x_{2,\phi^*(i)}||_2$
2. Mixing algorithm
PointCutMix์ ๋ชฉํ๋ ์๋ก ๋ค๋ฅธ ๋ ๊ฐ์ ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ ($x_1, y_1$), ($x_2, y_2$)๊ฐ ์ฃผ์ด์ก์ ๋ ์๋ก์ด ํฌ์ธํธํด๋ผ์ฐ๋ ๋ฐ์ดํฐ ($\tilde x, \tilde y$)๋ฅผ ์ฐพ๋ ๊ฒ์ด๋ค. ์ด๋, $x$๋ ํฌ์ธํธํด๋ผ์ฐ๋, $y$๋ classification ๋ผ๋ฒจ์ ์๋ฏธํ๋ค.
- $\tilde x = B\cdot x_1 + (I_N-B)\cdot \tilde x_2$
- $\tilde y = \lambda y_1 + (1-\lambda)y_2$
์ด๋ $B$๋ $b1, b2, ..., b_N$์ ๋๊ฐ์ฑ๋ถ์ผ๋ก ๊ฐ๋ ๋๊ฐํ๋ ฌ๋ก, $b_i$๋ $i$๋ฒ์งธ ํฌ์ธํธ๋ฅผ samplingํ ์ง๋ฅผ ๊ฒฐ์ ํ๋ 0๋๋ 1์ ๊ฐ์ด๋ค.
์ด๋ค ํฌ์ธํธ๋ฅผ samplingํ ์ง ์ ํ๋ ๋ฐฉ๋ฒ์ ๋๊ฐ์ง๊ฐ ์๋๋ฐ, ์ด๋ ๋ค์๊ณผ ๊ฐ๋ค.
1. PointCutMix-R: $x_1$์์ randomํ๊ฒ $n$๊ฐ์ ํฌ์ธํธ๋ฅผ samplingํ๋ค.
2. PoitnCutMix-K: centtral point๋ฅผ ํ๋ ์ ํ๊ณ , nearest neighbor์ ์ด์ฉํด ์ฃผ์์ $n-1$๊ฐ์ ์ ์ ์ ํํด samplingํ๋ค.
$\lambda$๋ sampling ratio๋ฅผ ์๋ฏธํ๋ฉฐ, $Beta(\beta, \beta)$ ๋ถํฌ์์ ์ถ์ถ๋๋ค. $\beta$๊ฐ ํด์๋ก ๋ถํฌ๋ narrowํด์ง๋ฉฐ, ์ด๋ ๋ชจ๋ ์ํ์ ๋ํด ๋น์ทํ sampling ratio๋ฅผ ์ฌ์ฉํจ์ ์๋ฏธํ๋ค.
์๋ก ๋ค๋ฅธ sampling ratio $\lambda$๊ฐ์ ๋ฐ๋ฅธ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
Result
Comparison on different $\rho$ and $\beta$ values
PointCutMix augmentation์ ์ ์ฉํ ํ๋ฅ ์ธ $\rho$์, sampling ratio์ ํธ์ฐจ๋ฅผ ์กฐ์ ํ๋ $\beta$ ๊ฐ์ ๋ฐ๊ฟ๊ฐ๋ฉฐ ์คํํ๋ค.
์ฐ์ $\rho$์ ๊ฒฝ์ฐ 0์ผ ๋ (augmentation์ ์ฌ์ฉํ์ง ์์) ๋ณด๋ค ํญ์ ์ฑ๋ฅ์ด ํฅ์๋๊ณ ๊ทธ ๊ฐ์ ๋ฐ๋ฅธ ์ฐจ์ด๊ฐ ๋ณ๋ก ์๋ ๋ฐ์ ๋ฐํด, PointNet๋ง $\rho$ ๊ฐ์ด ์ปค์ง์๋ก ์ฑ๋ฅ์ด ๊ธ๊ฒฉํ ํ๋ฝํ๋ค. PointNet์ ๊ตฌ์กฐ์ localํ feature์ ํฌ์ฐฉํ์ง ๋ชปํ๋๋ฐ, ์ ์๋ค์ ์ด๋ก ์ธํด PointNet ๊ตฌ์กฐ์๋ PointCutMix๊ฐ ํจ๊ณผ๊ฐ ์๋ค๊ณ ํ๋จ, ์ดํ์ ์คํ์์ PointNet์ ์ ์ธํ๋ค.
$\beta$์ ๊ฒฝ์ฐ๋ ๊ฐ์ ๋ฐ๋ฅธ ์ฑ๋ฅ ์ฐจ์ด๊ฐ ๋งค์ฐ ์ ์ผ๋, RS-CNN์ ๋งค์ฐ ์์ $\beta$๊ฐ์ ์ ํธํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค. $\beta$๊ฐ์ด ์์์๋ก, ๋งค ์ํ๋ง๋ค ๋น์ทํ sampling ratio๋ฅผ ์ฌ์ฉํจ์ ์๋ฏธํ๋ค.
Classification Result
$\rho=1.0$, $\beta=1.0$์ผ๋ก ์ค์ ํ๋ค.
Baseline, PointMixUp, PointAugment ๋ฑ๊ณผ ๋น๊ตํ์ ๋ ๊ฑฐ์ ํญ์ ๊ฐ์ฅ ๋์ ์ฑ๋ฅ์ ๋ณด์๋ค.
Code (PyTorch Implementation)
์ฝ๋๋ Github์ ๊ณต๊ฐ๋ official implementation์ ์ผ๋ถ ์์ ํ๋ค.
1. Optimal assignment of point clouds
๋ Batch ๋ฐ์ดํฐ ๊ฐ์ ์ผ๋์ผ๋์ ํจ์ ๋ถ๋ถ์ด๋ค. ์ด ํจ์ ์ญ์ Gradient backpropagation์ ํตํด ํจ๊ป optimize๋๋ค.
#codes from https://github.com/cuge1995/PointCutMix/blob/main/emd/emd_module.py
class emdFunction(Function):
@staticmethod
def forward(ctx, xyz1, xyz2, eps, iters):
batchsize, n, _ = xyz1.size()
_, m, _ = xyz2.size()
assert(n == m)
assert(xyz1.size()[0] == xyz2.size()[0])
assert(n % 1024 == 0)
assert(batchsize <= 512)
xyz1 = xyz1.contiguous().float().cuda()
xyz2 = xyz2.contiguous().float().cuda()
dist = torch.zeros(batchsize, n, device='cuda').contiguous()
assignment = torch.zeros(batchsize, n, device='cuda', dtype=torch.int32).contiguous() - 1
assignment_inv = torch.zeros(batchsize, m, device='cuda', dtype=torch.int32).contiguous() - 1
price = torch.zeros(batchsize, m, device='cuda').contiguous()
bid = torch.zeros(batchsize, n, device='cuda', dtype=torch.int32).contiguous()
bid_increments = torch.zeros(batchsize, n, device='cuda').contiguous()
max_increments = torch.zeros(batchsize, m, device='cuda').contiguous()
unass_idx = torch.zeros(batchsize * n, device='cuda', dtype=torch.int32).contiguous()
max_idx = torch.zeros(batchsize * m, device='cuda', dtype=torch.int32).contiguous()
unass_cnt = torch.zeros(512, dtype=torch.int32, device='cuda').contiguous()
unass_cnt_sum = torch.zeros(512, dtype=torch.int32, device='cuda').contiguous()
cnt_tmp = torch.zeros(512, dtype=torch.int32, device='cuda').contiguous()
emd.forward(xyz1, xyz2, dist, assignment, price, assignment_inv, bid, bid_increments, max_increments, unass_idx, unass_cnt, unass_cnt_sum, cnt_tmp, max_idx, eps, iters)
ctx.save_for_backward(xyz1, xyz2, assignment)
return dist, assignment
@staticmethod
def backward(ctx, graddist, gradidx):
xyz1, xyz2, assignment = ctx.saved_tensors
graddist = graddist.contiguous()
gradxyz1 = torch.zeros(xyz1.size(), device='cuda').contiguous()
gradxyz2 = torch.zeros(xyz2.size(), device='cuda').contiguous()
emd.backward(xyz1, xyz2, gradxyz1, graddist, assignment)
return gradxyz1, gradxyz2, None, None
class emdModule(nn.Module):
def __init__(self):
super(emdModule, self).__init__()
def forward(self, input1, input2, eps, iters):
return emdFunction.apply(input1, input2, eps, iters)
2. Mixing algorithm (PointCutMix-R)
PointCutMix-R์ ์ฝ๋์ด๋ค. Batch ๋ฐ์ดํฐ์ ์์๋ฅผ ๋ฌด์์๋ก ์์ ํ ์์์ ์ ์ํ emdModule์ ์ด์ฉํด ์๋ ๋ฐ์ดํฐ์์ ์ผ๋์ผ๋์์ ๊ณ์ฐํ ํ, ๊ฐ๊ฐ์ Batch ๋ฐ์ดํฐ๋ฅผ ์ผ์ ๋น์จ๋ก ์๋๋ค.
# codes are modified from https://github.com/cuge1995/PointCutMix/blob/main/train_pointcutmix_r.py#L252
for points, label in train_loader:
target = label
r = np.random.rand(1)
if r < cutmix_prob:
lam = np.random.beta(beta, beta)
B = points.size(0)
rand_index = torch.randperm(B) #shuffled index
target_a = target
target_b = target[rand_index] #shuffled label
point_a = points #[B, num_points, num_features]
point_b = points[rand_index] #shuffled points
point_c = points[rand_index]
remd = emdModule() #optimal assignment of point clouds
_, ind = remd(point_a, point_b, 0.005, 300) #assignment function [B, num_points]
for ass in range(B):
point_c[ass] = point_c[ass][ind[ass].long(), :] #rearrange pixels that correspond to point_a
int_lam = max(int(num_points * lam), 1) #number of points to sample
gamma = np.random.choice(num_points, int_lam, replace=False, p=None) #points to sample (Random sampling)
#cutmix
for i2 in range(B):
points[i2, gamma] = point_c[i2, gamma]
#adjust lambda to exactly match point ratio
lam = int_lam * 1.0 / num_points
#pred and calculate loss
pred = model(points)
loss = loss_f(pred, target_a.long()) * (1. - lam) + loss_f(pred, target_b.long()) * lam
else:
pred = model(points)
loss = loss_f(pred, target.long())