이번 주제는 Semi supervised learning 이다.

현업에서 일을 하다 보면 무한정 잘 어노테이션 된 데이터를 늘릴 수 없다. 모두가 잘 알듯 이는 비용과 시간이 어마어마 하게 들기 때문이다. 그래서 이를 극복하기 위해 semi-supervised learning을 도입하기로 했고 서베이 할때 읽은 논문들을 하나 씩 정리하고자 한다. 

 

혹시 semi-superivsed learning 에 대해 기초지식이 전혀 없다면 mit press의 Semi-Supervised Learning 전반부 챕터들이라도 읽어 보길 추천한다. 이보다 더 잘 정리할 수 없을 정도로 잘 정리되어있다. 

 

 

그 첫번째가 아래 논문이다. 제목에서 알 수 있듯이 Semi supervised 로 segmantic segmentation 성능을 높이기 위한 접근방식을 도입했다. 

 

Title: Semi-supervised Semantic Segmentation With Cross-Consistency traning 

link: https://arxiv.org/abs/2003.09005

 

Semi-Supervised Semantic Segmentation with Cross-Consistency Training

In this paper, we present a novel cross-consistency based semi-supervised approach for semantic segmentation. Consistency training has proven to be a powerful semi-supervised learning framework for leveraging unlabeled data under the cluster assumption, in

arxiv.org

 

Contributions

이 논문의 주된 contribution 은 다음 3가지 정도 이다. 

 

1. consistency를 이용하는 대부분의 method들은 입력 이미지에 서로 다른 변화를 적용하고  서로 다른 변화를 적용한 입력에 대해 네트워크 출력이 일정하도록 학습을 하는데 반해 이 논문은 encoder(또는  backbone)의 출력에 서로 다른 변화를 적용해 main decoder 와 axilary decoder의 입력으로 넣고 decoder의 출력이 일정하도록 학습을 시키는 방법을 제안한다. (axilary decoder 란 학습시에만 쓰이는 decoder로 목적은 encoder의 representation learning 능력을 향상시키는데 있다.)

 

2. Encoder의 출력인 feature map 에 적용할 다양한 종류의 변화(perturbation) 방법 을 제안하고 실험을 통해 효과를 보여준다.

 

3. 또한 weakly-labeld data(image level에서의 class 만 가지고 있는 데이터, 이미지넷 과같은 데이터를 생각하면됨)과 서로 다른 도메인의 pixel-level label이 있는 데이터셋을 사용하는 방법도 소개한다.

 

Method

 저자가 입력 이미지가 아닌 encode 출력인 feature map 에  변화(perturbation)을 적용하기로 한 이유는 다음과 같다. 

저자는 cluster assumption[참조]을 확인 하기 위해 입력 이미지의 특정 위치와 그 이웃 픽셀들 간의 유사성(거리)를 유클리디안 거리로 측정하고 대응 되는 라벨도 유클리디안 거리로 측정했다. 그 입력 이미지에서는 cluster assumption 이 잘 맞지 았다고 한다.  동일 한 실험을 encoder의 출력과 같은 hidden layer의 특정 위치와 그 이웃 위치들 그에 대응되는 라벨에도 대해 실행 했을 때 hidden layer의 출력인 feature space 에서 cluster assumption이 더 잘 들어 맞는 것을 확인했기 때문에 feature map에 perturbation을 적용하기로 결정했다. 

아래 Fig 1 은 위 실험의 결과를 그려 놓은 것이다. (a)는 입력 이미지, (b)는 대응 라벨이다. 

(c) 는 입력 이미지에서 지정된 위치와 그 이웃 위치간의 유클리디안 거리를 측정한 결과(실제 논문에서는 지정된 위치를 center로 하는 20x20 size의 patch 를 기준으로 이미지를 각 셀로 나누고 인접셀의 center 와 유클리디안 거리를 측정했다) (d)는 2048 dimension의 hidden representation space 에서의 지정된 위치와 그 이웃 위치간의 유클리디안 거리를 측정한 결과이다.

(c) 와 (d) 에서 검은 부분은 이웃 위치와 유클리디안 거리가 큰 것을 의미하는데 (d) 에서는 배경 클래스와 전경(강아지, 고양지, 양)등 클래스가 구분되는 곳이 주로 검은색(인접픽셀들과의거리가 먼)으로 나타나는 것이 확인되고 (c) 에서는 그런 경향성이 상대적으로 약하다. cluster assumption에 따르면 low density region에서 결정 경계(decision boundary)가 형성된다고 보기 때문에 (d) 의 실험결과가 cluster assumption을 지지하는 결과라고 할 수 있다.

 

Fig 1. cluster assumption in semantic segmentation

왜 검은색으로 표기된 영역이 low density region 인가?
혹시 위 글을 읽고 이런 의문이 생겼다면 이렇게 해석을 해보면 된다. low density 영역이란 data point 가 별로 없는 sparse 한 영역을 말한다. 말인 즉 해당 영역에서 특정 data point 까지의 거리가 멀다는 것이다. data point 가 어느 클래스에 속했는지와는 관계 없이 data point들 까지의 평균거리가 먼 위치 임의의 위치가 low density region 인것이다. 그리고 cluster assumption(low density assumption)에서는 이 공간에 결정 경계가 생성된다고 본다.

 

아래 그림은 논문이 제안하는 네트워크의 구조이다. 이 구조는 하나의 encoder(Shared Encoder, $h$)와 main decoder( $g$ ) 그리고 consistency를 강제 하기 위한 auxiliary decoder($g^{l}_{a}, ..., g^{K}_{a}$) 가 존재한다. auxiliary decoder 는 학습시에만 사용 되고 실제 inference time 에는 사용되지 않는다. 

 

 

Fig 2. Model architecture

total loss는 아래와 같다.

$$ L = L_{s} + \omega_{u}L_{u} $$

 $L_{s}$ 는 label 이 존재하는 데이터로 구한 cross entropy loss , 즉 supervised loss term이고

$L_{u}$ 는 unlabeled data를 이용해 구한 consistency loss 이다.  $\omega_{u}$는 $L{s}$ 와의 밸런스를 맞추기 위한 텀으로 learning rate 처럼 학습 중 규칙에 따라 조절 된다. 

 

$L_{s}$ 는 label 이 존재하는 데이터로 부터 구한 loss 이다. Fig 2 의 model architecture 에서 labeled example $X^{l}$ 이 입력으로 사용 될때 main decoder( $g$ )의 출력 $\hat{y}^{l}$과 label $y$를 이용해 cross entropy loss 를 구한 것이다. 논문에서는 이를 아래 식으로 표현했다.여기서 $f$ 는 encoder와 main decoder 를 포함한 network을 의미 하고 $H$ 는 cross entropy loss 이다. 

 

 

이제 Unlabeled data를 어떻게 이용하는지 보자. 여기가 중요 포인트 이다. 

Shared Encoder( $h$)의 출력을 $z_{i} = h(X^{u}_{i})$ 라 하자.  이 논문의 contribution 1 과 같이 저자들은 이 $z_{i}$ 에 작은 변화(pertubation) 을 적용해

각 auxilary decoder( $g^{l}_{a}, ..., g^{K}_{a}$) 입력으로 들어갈 $K$ 개의 서로 다른 $\tilde{z}^{a}_{i}, ... , \tilde{z}^{K}_{i}$ 를 생성한다.

이렇게 생성된 $\tilde{z}$ 들은 각 auxiliary decoder 의 입력으로 들어가게 되고 대응 되는 출력 $\hat{y}^{(l)}_{a}, .., \hat{y}^{(K)}_{a}$ 을 생성한다.  auxiliary decoder와는 다르게 main decoder( $g$) 에는 변화가 적용되지 않은 순수한 encoder output 인 $z_{i}$ 가 입력으로 들어가 출력 $\hat{y}^{u}$ 를 생성한다.

auxiliary decoder의 출력 $\hat{y}^{(l)}_{a}, .., \hat{y}^{(K)}_{a}$은 main decoder의 출력을 target 으로 mean square error 를 이용해 학습 한다.  즉 auxiliary decoder 의 출력이 main decoder의 출력과 같아지는 것을 목표로 학습을 하는 것이고 이는 feature의 작은 변화에도 모델 안정적인 추론 성능을 갖도록 학습하는 것이다.

또한 main decoder의 출력은 target 즉 ground truth 로 활용 되므로 unlabeled data를 이용해 학습을 할때 main decoder의 weight 들은 학습 되지 않는다.

이를 수식으로 정리하면 다음과 같다.

이렇게 학습 하면 결과적으로  encoder 의 representation 능력을 강화 하는 방식으로 학습이 진행된다.

 

Perturbation

이제 shared encoder의 출력인 $z_{i}$에 어떤 변화(perturbation)을 적용해 auxiliary decoder의 입력으로 활용했는지 알아 보자. 글만 보면 정확히 파악하기 힘들수 있으니 각 perturbation을 구현한 저자의 코드를 같이 정리한다.

 feature based perturbation:

이 타입의 perturbation 기법은 feature 에 noise를 더하거나 drop out 하는 것을 말한다.

  • F-Noise: feature 와 같은 dimension 을 가지는 noise tensor를 uniform distribution 으로 부터 추출하고 feature 더하는 방식이다.
class FeatureNoiseDecoder(nn.Module):
    def __init__(self, upscale, conv_in_ch, num_classes, uniform_range=0.3):
        super(FeatureNoiseDecoder, self).__init__()
        self.upsample = upsample(conv_in_ch, num_classes, upscale=upscale)
        self.uni_dist = Uniform(-uniform_range, uniform_range)

    def feature_based_noise(self, x):
    	#바로 아래 라인이 uniform distribution 으로 부터 noise vector 를 추출하는 라인이다. 
        noise_vector = self.uni_dist.sample(x.shape[1:]).to(x.device).unsqueeze(0) 
        x_noise = x.mul(noise_vector) + x #feature 에 노이즈를 적용한다. 
        return x_noise

    def forward(self, x, _):
        x = self.feature_based_noise(x)
        x = self.upsample(x)
        return
  • F-dropout: feature map($z$)을 채널 축으로 summation 하고 normalization 해 $z^\prime$ 을 생성하고 0.6~0.9 사이의 random 값 $\gamma$ 를 threshold 로 정해 $\gamma$ 보다 작은 위치의 feature 를 0으로 만드는 방식이다. $M_{drop} = { z^\prime < \gamma}_{1}$  로 0으로 만들 위치를 mask 로 만들고 $\tilde{z} = z x M_{drop} $ 과 같이 적용한다.
class FeatureDropDecoder(nn.Module):
    def __init__(self, upscale, conv_in_ch, num_classes):
        super(FeatureDropDecoder, self).__init__()
        self.upsample = upsample(conv_in_ch, num_classes, upscale=upscale)

    def feature_dropout(self, x):
        attention = torch.mean(x, dim=1, keepdim=True)
        max_val, _ = torch.max(attention.view(x.size(0), -1), dim=1, keepdim=True)
        threshold = max_val * np.random.uniform(0.7, 0.9) # 논문은 0.6~0.9 사이로 적었는데 구현은 0.7~0.9 사이이다.
        threshold = threshold.view(x.size(0), 1, 1, 1).expand_as(attention)
        drop_mask = (attention < threshold).float()
        return x.mul(drop_mask)

    def forward(self, x, _):
        x = self.feature_dropout(x)
        x = self.upsample(x)
        return x

Prediction based perturbation:

이 방식은 main decoder의 출력을 $\hat{y} = g(z)$ 이용해  object 가 차지하는 영역의 pixel을 변화시키거나 object 가 차지 하지 않는 영역을 변화시키기 위한 기법이다. object 내의 영역에 작은 변화를 일으키는 이유는 데이터셋에 존재하는 object의 특정 부위에 집중해 하거나, 특정 view angle 에서 바라본 object의 모습 등에 overfitting 되는 것을 피하기 위한 것으로 볼수 있다. object 이외의 영역을 변화시키는 mask 를 생성하는 이유는  학습 데이터셋에  존재하는 context information 로 인한 bias 를 줄이기 위한것으로 해석 할 수 있다. 

context information 이란 
"Context is a statistical property of the world we live in and provides critical information to help us solve perceptual inference tasks faster and more accurately" 이라고 할 수 있다. 우리가 사물을 인지 하는데 이용하는 정보로 object detection task 에서는 특정 object 가 있을 법한 "주위 환경 정보"를 임의의 object 클래스를 정하는데 활용 한다거나 학습 데이터 셋에 존재하는 object 들 간의 관계를 학습하는 것으로 볼수 있다. 예를 들으 밀림이라는 background에 호랑이가 있는 이미지가 학습데이터 셋에 많고, 집안 벽에 걸려 있는 호랑이 사진 또는 그림이 있을 경우 "집안의 가구와 같이 있는 호랑이 이미지"가 주어졌을때 호랑이 자체가 아닌 호랑이와 주변 가구 사이의 관계를 이용해  "호랑이 사진/그림" 으로 prediction 하게 된다던지 하는 것이다. 
  • guided masking: main decoder의 출력을 이용해 object 가 차지하는 영역에만 perturbation 을 적용하거나 object 이외의 context 에만 perturbation 을 적용하는 방식이다. 아래 코드에서 pred 는 main decoder의 출력이다. 
def guided_masking(x, output, upscale, resize, return_msk_context=True):
    if len(output.shape) == 3: 
        masks_context = (output > 0).float().unsqueeze(1)
    else: 
        masks_context = (output.argmax(1) > 0).float().unsqueeze(1)
    
    masks_context = F.interpolate(masks_context, size=resize, mode='nearest')

    x_masked_context = masks_context * x
    if return_msk_context:
        return x_masked_context

    masks_objects = (1 - masks_context)
    x_masked_objects = masks_objects * x
    return x_masked_objects


class ContextMaskingDecoder(nn.Module):
    def __init__(self, upscale, conv_in_ch, num_classes):
        super(ContextMaskingDecoder, self).__init__()
        self.upscale = upscale
        self.upsample = upsample(conv_in_ch, num_classes, upscale=upscale)

    def forward(self, x, pred=None):
        # pred 는 main decoder의 출력결과이다. 
        x_masked_context = guided_masking(x, pred, resize=(x.size(2), x.size(3)),
                                          upscale=self.upscale, return_msk_context=True)
        x_masked_context = self.upsample(x_masked_context)
        return x_masked_context


class ObjectMaskingDecoder(nn.Module):
    def __init__(self, upscale, conv_in_ch, num_classes):
        super(ObjectMaskingDecoder, self).__init__()
        self.upscale = upscale
        self.upsample = upsample(conv_in_ch, num_classes, upscale=upscale)

    def forward(self, x, pred=None):
    	# pred 는 main decoder의 출력결과이다. 
        x_masked_obj = guided_masking(x, pred, resize=(x.size(2), x.size(3)),
                                      upscale=self.upscale, return_msk_context=False)
        x_masked_obj = self.upsample(x_masked_obj)

        return x_masked_obj
  • guided cutout: object 의 특정 부분에 의존적으로 추정하는 것을 방지하기 위해 object 영역내 임의의 위치를 0으로 만드는 perturbation을 적용한다.  segmenation map으로 부터 object의 contour를 찾고 이를 이용해 bounding box 를 만든다 그리고 bouding box 내의 임의 사격형 영역을 0으로 만든다.
def guided_cutout(output, upscale, resize, erase=0.4, use_dropout=False):
    if len(output.shape) == 3:
        masks = (output > 0).float()
    else:
        masks = (output.argmax(1) > 0).float()

    if use_dropout:
        p_drop = random.randint(3, 6)/10
        maskdroped = (F.dropout(masks, p_drop) > 0).float()
        maskdroped = maskdroped + (1 - masks)
        maskdroped.unsqueeze_(0)
        maskdroped = F.interpolate(maskdroped, size=resize, mode='nearest')

    masks_np = []
    for mask in masks:
        mask_np = np.uint8(mask.cpu().numpy())
        mask_ones = np.ones_like(mask_np)
        try: # Version 3.x
            _, contours, _ = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        except: # Version 4.x
            contours, _ = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        polys = [c.reshape(c.shape[0], c.shape[-1]) for c in contours if c.shape[0] > 50]
        for poly in polys:
            min_w, max_w = poly[:, 0].min(), poly[:, 0].max()
            min_h, max_h = poly[:, 1].min(), poly[:, 1].max()
            bb_w, bb_h = max_w-min_w, max_h-min_h
            rnd_start_w = random.randint(0, int(bb_w*(1-erase)))
            rnd_start_h = random.randint(0, int(bb_h*(1-erase)))
            h_start, h_end = min_h+rnd_start_h, min_h+rnd_start_h+int(bb_h*erase)
            w_start, w_end = min_w+rnd_start_w, min_w+rnd_start_w+int(bb_w*erase)
            mask_ones[h_start:h_end, w_start:w_end] = 0
        masks_np.append(mask_ones)
    masks_np = np.stack(masks_np)

    maskcut = torch.from_numpy(masks_np).float().unsqueeze_(1)
    maskcut = F.interpolate(maskcut, size=resize, mode='nearest')

    if use_dropout:
        return maskcut.to(output.device), maskdroped.to(output.device)
    return maskcut.to(output.device)


class CutOutDecoder(nn.Module):
    def __init__(self, upscale, conv_in_ch, num_classes, drop_rate=0.3, spatial_dropout=True, erase=0.4):
        super(CutOutDecoder, self).__init__()
        self.erase = erase
        self.upscale = upscale 
        self.upsample = upsample(conv_in_ch, num_classes, upscale=upscale)

    def forward(self, x, pred=None):
        maskcut = guided_cutout(pred, upscale=self.upscale, erase=self.erase, resize=(x.size(2), x.size(3)))
        x = x * maskcut
        x = self.upsample(x)
        return x
  • Intermediate VAT: inference 를 가장 혼란 스럽게 만들 수 있는 변화를 feature 에 적용 하는 방식으로 virtual adverserial training을 응용했다. VAT에서는 noise 를 샘플링 한 후에, 해당 noise 를 더한 값과 기존의 값의 차이(KL-div)의 gradient 를 이용해 noise 를 생성한다.(todo: 부분은 따로 정리 할 것.)
def get_r_adv(x, decoder, it=1, xi=1e-1, eps=10.0):
    """
    Virtual Adversarial Training
    https://arxiv.org/abs/1704.03976
    """
    x_detached = x.detach()
    with torch.no_grad():
        pred = F.softmax(decoder(x_detached), dim=1)

    d = torch.rand(x.shape).sub(0.5).to(x.device)
    d = _l2_normalize(d)

    for _ in range(it):
        d.requires_grad_()
        pred_hat = decoder(x_detached + xi * d)
        logp_hat = F.log_softmax(pred_hat, dim=1)
        adv_distance = F.kl_div(logp_hat, pred, reduction='batchmean')
        adv_distance.backward()
        d = _l2_normalize(d.grad)
        decoder.zero_grad()

    r_adv = d * eps
    return r_adv


class VATDecoder(nn.Module):
    def __init__(self, upscale, conv_in_ch, num_classes, xi=1e-1, eps=10.0, iterations=1):
        super(VATDecoder, self).__init__()
        self.xi = xi
        self.eps = eps
        self.it = iterations
        self.upsample = upsample(conv_in_ch, num_classes, upscale=upscale)

    def forward(self, x, _):
        r_adv = get_r_adv(x, self.upsample, self.it, self.xi, self.eps)
        x = self.upsample(x + r_adv)
        return x

random perturbations: 

가장 단순한 형태로 drop out을 이용한다. 

class DropOutDecoder(nn.Module):
    def __init__(self, upscale, conv_in_ch, num_classes, drop_rate=0.3, spatial_dropout=True):
        super(DropOutDecoder, self).__init__()
        self.dropout = nn.Dropout2d(p=drop_rate) if spatial_dropout else nn.Dropout(drop_rate)
        self.upsample = upsample(conv_in_ch, num_classes, upscale=upscale)

    def forward(self, x, _):
        x = self.upsample(self.dropout(x))
        return x

 

Avoiding overfitting

라벨이 있는 데이터가 라벨이 없는 데이터 보다 적은 상황에서 각 iteration 마다 동일한 크기의 label, unlabeled 데이터를 추출해 학습하므로 label 이 있는 데이터가 실제 학습에 중복되어 사용되기 때문에 overfitting 이 발생할 수 있다. 이를 방지 하기 위해 bootstrapped-CE(ab-CE, CE=cross entropy)를 사용했다. 

$f(x^(l)_{i})$ 은 라벨이 있는 데이터 $x$ 에 대한 모델의 출력으로 모델이 $x^(l)_{i}$가 특정 클래스에 속할 활률을 $\eta$ 로 추측하는 pixel들만 supervised loss 계산에 사용 하겠다는 의미이다. 즉 모델이 이미 $\eta$ 의 확신을 가지고 추론한 pixel 은 더 이상 학습에 이용하지 않음으로서 overfitting 을 방지하겠다는 전략이다. 

 

결론

학습과 관련된 정확한 정보는 논문을 참조하기로 하고 결론을 보자.(wealky-labeled data, joint leraning of difference domain data 에 대한 것은 결론 이후에 정리한다.)

Fig 3. Ablation studies on camvid with 20, 50 and 100 labeled images
Fig 4. Ablation study on PASCAL VOC

Fig 3은 camvid 데이터 셋에서 라벨이 있는 이미지를 각각 20, 50, 100장 사용해 unlabeled data 와 학께 학습한 결과이고 Fig4는 PASCAL VOC에서 1000장의 라벨이 있는 이미지와 unlabeled data 데이터를 이용해 학습한 결과이다. 

Fig 3에서 보면 당연하게도 label 이 있는 데이터의 사용량이 증가 할 수록 baseline  miou가 20, 30, 38.x 와 같이 증가하는 경향성을 갖는다는 것을 알수 있다. x측은 특정 타입의 auxiliary decoder를 몇개 사용 했는지를 나타내는데 K =2 이라면 CCT F-dop의 경우 F-drop type의 auxiliary decoder를 2개 사용, K=4 이면 해당 타입 decoder 를 4개 사용 했다는 의미이다.  CCT full은 위에 소개된 perturbation을 적용한 decoder 를 모두 사용 한 경우 이고 이 경우 K=2라면 F-noise decoder 2개, F-drop decoder 2개, gropout decoder 2개, G-cutout(guided cutout) decoder 2개, VAT(virtual adverserial training) decoder 2개, object mask decoder 2개, context mask decoder 2개 등 총 number of perturbation x K 개의 decoder 를 사용해 학습 했다고 해석 하면 된다. CCT full +abCE 는 모든 종류의 perturbation을 사용하고 bootstrapped -CE를 함께 사용한 결과이다.

 

camvid dataset 실험결과의 경우 모든 종류의 perturbation을 다 사용하고 각 perturbation type의 decoder 개수가 많아 질수록 성능이 좋아진다. PASCAL의 경우 모든 perturbation type을 사용하고 bootstrapped -CE 까지 사용 할 경우 K=2 일 때 성능이 가장 우수 했다. 

전반적인 결과는 논문에서 제안한 feature perturbation 방식과 consistency loss 를 사용 할 경우 라벨이 있는 데이만 사용 할때 보다 더 좋은 성능을 낼수 있는 것으보인다. 

 

Fig 5.state-of-the-art 들과 비교

Fig 5는 다른 state-of-the-art 방법들과 PASCAL VOC 데이터를 이용해 학습한 결과를 비교한 표인다. 이 논문에서 제안한 CCT 방식이 성능이 가장 좋았다는 결과이다. 

 

 

Use weak-labels

이 논문에서는 weak-label data 를 이용하는 방법에 대해서도 다루고 있다. 간단하게만 살펴 보자. 

weak label 은 이미지 레벨에서 어떤 object 가 있는지에 대한 정보가 있는 상태로 정의 된다. 이미지넷 데이터 셋을 생각해보자. 이를 이용해서 특정 object에 대한 부가 적인 표현 정보(representation)을 encoder가 학습 할 수 있게 만드는게 목표 이다. 

이를 위해 사전학습 된 encoder 와 classification branch를 이용한다. classification branch를 이용해 class activation map을 생성하고 이 map 에서 activation score 가 특정 threshold $\theta_{bg}$(논문에서는 0.05) 미만 이면 background class , 특정 threshold $\theta_{fg}$ (논문에서는 0.3) 초과면 weak-label 에 해당하는 foreground class 로 간주해 pseudo ground truth map($y_{p}$) 을 생성하고 이 pseudo ground truth map 에 dense CRF 를 적용해 한번더 정재 한 후 이를 auxiliary decoder  학습에 사용한다. 

 

$L_{w}$가 weak label data를 이용한 weakly supervised loss 이고 $g^{k}_{a}$는 auxiliary decoder, $z_{i}$는 encoder output, $y_{p}$는 class activation map 기반의 pseudo gt 이다. 

 

Cross-consistency traning on mutiple domains

이번엔 다른 도메인의 label-unlabeled data set 을 이용해 학습 하는 방법이다. 굉장히 쉽고 직관적인 아이디어 이다. 

사실 다음 그림으로 이미 설명이 끝난다. 

서로 다른 도메인의 data set $D^{(1)}, D^{(2)}$ 를 이용하는데 $D^{(*)} = { D^{(*)}_{l} \cup D^{(*)}_{u} }$  로 $ D^{(*)}_{l} $ 는 도메인 *에 속한 라벨이 있는 데이터 $ D^{(*)}_{u} $ 은 도메인 *에 속한 라벨이 없는 데이터 이다. 

이 두 도메인 데이터를 이용해 학습하는데 encoder $h$ 는 공유 하고 각 도메인의 main decoder과 auxiliary decoder를 따로 따로 사용해 학습을 진행한다. 이렇게 하면 두 도메인의 데이터를 모두 이용해 encoder 가 특정 object의 representation 을 학습하는데 도움이 된다고 한다. 

 

 

-끝-

'semi supervised learning' 카테고리의 다른 글

GuidedMix-Net 리뷰  (0) 2023.07.09
[semi-supervised learning] basic assumption  (0) 2023.06.24

+ Recent posts