MADE의 Limtation과 Goal 그리고 Background

인공지능 분야에서 가장 중요한 영역 중 하나인 Distribution estimation (i.e., 분포 추정)은 데이터의 샘플을 통해 joint distribution p(x)를 추정하는 task이며 이는 데이터의 속성 이해 뿐 아니라 모델 그자체를 파악하는데 사용될 수 있습니다.

기계 학습 분야에서 joint distribution의 specific한 property만의 학습을 통해 많은 task를 수행할 수 있기 때문에 distribution estimation은 인공지능 분야에서 필수불가결 하다고 할 수 있겠습니다.

그림1 [figure 1] src: https://www.ai-summary.com/summary-a-gentle-introduction-to-probability-density-estimation/

그림1과 같이 distribution estimation으로 테스트 샘플로 joint probability $p(x)$를 추정할 수 있습니다.

하지만 뉴럴네트워크에서 복잡한 연산을 하는 딥러닝 모델은 입력데이터의 dimension을 계속해서 부풀리게 되는데요, 이는 결국 데이터가 sparse해져 성능저하를 초래합니다. 이를 curse of dimenionality라고 하는데요 (그림2), 이는 distribution estimation의 크나큰 limitation이라고 할 수 있겠습니다.

그림2 [figure 2] src: https://aiaspirant.com/curse-of-dimensionality/

그렇기에 저자는 distribution estimation은 high dimensional data에 적용이 힘들다고 지적하는데요, 그러면 어떡하죠? distribution estimation은 인공지능 task에서 꼭 필요하지만 high dimensional data에는 적용이 힘들다니..

어떻게 방법이 없을까요?

저자는 여기서 오토인코더를 선택합니다. 오토인코더는 딥뉴럴넷의 하나로 레이블 없이 비지도 학습을 통해 잠재 차원 표현으로 학습하는 신경망입니다. 이러한 잠재 표현은 뉴런의 크기가 입력보다 더 작지만 같은 분포를 가지게끔 학습됩니다.

“저자가 오토인코더를 선택한 이유”

오토인코더는 입력데이터를 z스페이스로 압축하여 디코딩을 통해 데이터를 복원하는 모델인데요 (그림3), 여기서 주목할점은 바로 압축입니다. Distribution estimation은 dataset의 dimension에 비롯되고 curse of dimensionality는 이러한 high dimensional dataset의 distribution estimation을 방해하니 high dimension을 low dimension으로 변환하여 이러한 task를 해결하는 방법을 제안합니다.

그림3 [figure 3] src: https://lilianweng.github.io/posts/2018-08-12-vae/

Auto-Encoder를 통해 high dimensional dataset의 distribution estimation을 추정하는 것 외에도 오토인코더를 선택한 이유가 있습니다. 저자는 또한 오토인코더에 autoregressive property가 있다고 주장합니다. Autoencoder는 autoregressive model과 같이 지도학습에서 사용되는 ground truth label과 출력값간의 cost를 줄이는 supervised learning이 아닌 neg log likelihood를 minimize하는 동일한 objective를 가져 학습방법이 비슷합니다. 또한, Autoregressive property에서 출력값인 $x_d$ 는 $x < d$ 로 학습되므로 출력값을 통해
$x < d$ 를 추정할 수 있습니다.

그림4 [figure 4]

마찬가지로 오토인코더 역시 previous input $x$에 dependent하게 학습하여 $x\hat{}$ 를 출력하도록 학습되니 은닉층의 뉴런들은 과거의 데이터를 받아 미래값을 추정하는 autoregressive한 특성을 지녀 distribution estimation의 feasibility가 있다는 뜻입니다.

그림5 [figure 5] src: dial-lab

하지만 오토인코더의 고질적인 문제가 있어 저자는 이를 지적합니다.

오토인코더의 그림에서 알 수 있듯, 우리는 우리가 가정한 분포 파라미터를 예측하는 출력층을 가지게 될겁니다. 하지만 $p(x)$ 의 분포를 알기위해선 모든 분포곱으로 계산이 되겠지만 (i.e., $p(x) = p(x_1)p(x_2)p(x_3)…p(x_n)$ ) 이는 independence함이 conponent들 간에 가정되었을때 성립됩니다.

만약 conditional probability 라면 어떨까요? conditional probability에서 를 구하기 위해서는 이러한 공식이 성립되어야 합니다. $p(x) = p(x_1)p(x_2 \mid x_1)p(x_3 \mid x_2,x_1)…p(x_n \mid x_n-1)$

오토인코더는 fully connected feed forward network로 모든 뉴런들이 서로 연결되어있는데 이는 독립적이지 않으며 동시에 조건부라고 하기엔 조건부로 종속되는 방식을 알 수 없는 문제점이 있습니다. 자세하게 설명하자면, 입력 노드들이 제각각으로 fully connected 되어 있기에 은닉층을 지날때 벡터안의 element들은 순서가 뒤죽박죽 바뀌게 됩니다. 이의 경우 다음층은 랜덤하게 입력을 받아 conditional 하게 순서를 입력받을 수 없게 되는 겁니다.

그림6 [figure 6] src: dial-lab

이러한 문제는 그림6과같이 conditional probability를 구할때 joint distribution을 알 수 없습니다.

connectivity는 conditional dependence를 respect 해야합니다. conditional probability의 특징은 미래는 과거를 보는데 이의 경우 과거가 있지도 않은 미래를 보게끔 설계가 되는 어불성설이 일어나는 겁니다. 이러한 arbitrary ordering과 joint probability limitation을 해결하는 auto encoder의 joint probability distribution을 estimator를 constrain 하는것이 저자의 motivation이라고 할 수 있습니다.

그래서 탄생한 solution, MADE

이에 저자는 오토인코더의 autoregressive한 특징을 사용하되 모든 연결층을 사용하지않고 과거층만을 사용하는 masking을 연결층에 적용한 Masked autoencoder distribution estimation, MADE를 제안합니다. MADE의 method는 간단합니다. 오토인코더를 masking 함으로써 일부 conncection을 terminate 하는것입니다. 잎서말한 ordering의 arbitrary문제를 해결하기 위해 컴포넌트의 랜덤한 배치를 고려한 입력부터 랜덤하게 assign하여 모델이 랜덤한 인풋에 대해 robust한 학습을 하게 합니다. 또한 connected node가 conditional probability에 필요한 노드만을 입력으로 받고 불필요한 노드와의 연결을 끊게끔 connection을 동적으로 마스킹 합니다. 이러한 마스크엔 두가지 종류가 있는데 압축차원으로 들어가는 encoding connection mask와 출력하는 decoding connection mask가 있습니다.

첫번째 인코딩 마스킹은 마스킹된 입력 connection이 feed forward 한 파라미터가 원본데이터와 비교했을때 그 값이 같거나 클때 1을 반환하고 작을때 1을 반환하는 treshhold입니다.

마스킹인코더

그림7 [figure 7] src: MADE

m(k)는 마스킹을 지난 은닉층의 파라미터이고 d는 오리지널 파라미터입니다.

그림7과 같이 입력데이터가 인코더 레이어를 지나 이러한 마스킹 레이어를 통과할때, 뉴런들이 값을 잃지 않습니다. 예를들어 d의 값이 2라면 m(k) 1,2 둘다 feed forward 하게 되는거죠.

코드로써 마스킹이 initial되는 설명을 드립니다.

def set_mask(self, mask):
    self.mask.data.copy_(th.from_numpy(mask.astype(np.uint8).T))

이렇게 마스킹을 생성하는 함수를 선언하구요.

#Encoder W
for layer_idx, hsize in enumerate(self.encoder_hiddens):
    # 이전 layer에 없는 connect count를 샘플링하지 않도록 하여 실제로 사용되는 가중치의 수를 줄입니다.
    layer_count_low = 1 if len(layer_connect_counts) == 0 else np.min(layer_connect_counts)
    layer_connect_counts = [rng.randint(low=layer_count_low,high=INPUT_DIM) for _ in range(hsize)]

    # 레이어별 마스크 생성 및 저장
    mask = [[ 1 if layer_connect_counts[k] >= d else 0 for k in range(hsize)] for d in current_input_orders]
    self.encoder_layers[layer_idx].set_mask(np.array(mask))

    current_dim = hsize
    current_input_orders = layer_connect_counts

랜덤하게 re-sampling된 인풋에대해 인코딩하는 코드입니다. 코드를 보시면 인코더 mask는 for loop를 돌면서 노드 하나하나의 값이 오리지널 데이터인 current_input_orders의 d element와 비교연산자를 통해 1과 0을 반환합니다. 노드는 d보다 크거나 같을때 1을 반환하고 그외엔 0을 반환하는 모습을 볼 수 있습니다.

마스킹디코더

그림8 [figure 8] src: MADE 인코더 레이어와 반대로 디코더 레이어는 connection은 연결된 뉴런값이 더 작을때만 1을 받습니다. 이의 경우 출력층은 conditional probability를 구할 수 있게 됩니다. 예를 들어 d의 값이 3일 경우 m(k)값을 1,2만 받고 3을 받지 않는다는 말입니다. 이부분 역시 코드로써 설명드리겠습니다.

# Decoder V
for layer_idx, hsize in enumerate(self.decoder_hiddens):
    if layer_idx == len(self.decoder_hiddens)-1:
        layer_connect_counts = self.input_ordering

        #인코더와 달리 d와 같은 값도 terminate함
        mask = [[ 1 if layer_connect_counts[k] > d else 0 for k in range(hsize)] for d in current_input_orders] 

    self.decoder_layers[layer_idx].set_mask(np.array(mask)) #마스크를 생성하는 함수

    current_dim = hsize
    current_input_orders = layer_connect_counts

인코딩된 hidden layer value에대해 디코딩하는 코드입니다. 코드를 보시면 디코더 mask는 for loop를 돌면서 d노드 하나하나의 값이 hidden size 데이터의 k element와 비교연산자를 통해 1과 0을 반환합니다. 노드는 k보다 클때만 1을 반환하고 그외엔 0을 반환하는 모습을 볼 수 있습니다.

MADE의 전체적인 아키텍쳐는 그림9에서 볼 수 있습니다.

그림9 [figure 9] src: MADE

W 컨넥션에서는 노드값이 자신보다 같거나 작은 값을 feed forward하고 자신보다 큰값은 받지 않는것을 확인할 수 있습니다. V 컨넥션에서는 자신보다 작은값만 accept하는 모습도 확인할 수 있습니다. 그렇게해서 최종 출력에서 각 노드는 conditional probability 계산이 feed forward 네트워크 상에서 가능하게 됩니다.

논문에서 말하길 Autoencoder의 advantage는 flexibility라고 합니다. 이말 즉은 레이어를 깊게 쌓을수록 오토인코더의 성능이 올라간다는 뜻입니다. 이에 저자는 layer를 하나 더 그림9와 같이 쌓아서 은닉층 W가 2층인 MADE를 구현하였습니다.

그림9에서 MADE에 입력되는 인풋의 순서가 뒤죽박죽으로 보여지는데 이는 Order agnostic traning인, 인풋의 order를 random하게 하는 방법입니다.

    def shuffle_input_ordering(self):
        # Totally random input ordeing generation. Quite unstable
        if self.input_orderings == -1:
            random.shuffle(self.input_ordering)

        if self.input_orderings in [0,1]:
            pass # Using natural order

        elif self.input_orderings > 1:
            rng = np.random.RandomState(self.input_ordering_seed)

            self.input_ordering = rng.randint(1, input_dim+1, input_dim)
            self.input_ordering_seed = (self.input_ordering_seed + 1) % self.input_orderings
        else:
            raise NotImplementedError

위 코드와 같이 인풋으로 들어가는 데이터는 random하게 re-sampling 됩니다. 이 방법을 통해 모델이 autoencoder의 random한 연산에 robust하게끔 conditional probability를 구할 수 있게 하기 위해서 그림과 같은 모습이라고 합니다.

Experiment

Experiment로 저자는 두개의 dataset을 사용했는데요, UCI binary dataset과 Mnist dataset을 사용하였습니다.

그림10 [figure 10] src: MADE

그림10에 나와있는 Performance로 보아 MADE가 NADE등의 다른 모델들보다 성능이 더 잘 나온것을 볼 수 있었습니다. 투고당시 Sate of the Art 의 성능을 내었다고 봐도 되겠죠?

그림11 [figure 11 src: MADE

그림11은 은닉층에서 샘플링한 mnist 인데요, 주관적이긴 하나 은닉층에서 생각보다 log likelihood가 준수하게 학습되어있는것을 엿볼 수 있습니다.

마무리 및 토론

정리하자면 MADE는 Autoencoder를 사용하여 입력 벡터 구성 요소의 조건부 확률 분포를 출력합니 다. 조건부 종속성을 달성하기위해 오토인코더의 연결을 마스킹 하여 conditional probability 계산이 가능하게끔 하여 distribution estimatior를 구현하였고 order agnostic에 대응하기위해 입력의 order을 random하게 샘플링 하였습니다.

MADE는 분명 SOTA한 성능을 냈다고 논문으로써 증명하였지만 저는 읽으면서 몇가지 의문이 들었는데요, 마스킹 알고리즘을 보면서 여러분들도 느끼셨겠지만 마스킹을 사용한 autoregressive property는 가중치에 0이 곱해져 학습시에 상당 노드들의 가중치가 기여가 되지 않아 높은 차원의 데이터로 학습시킬때 문제가 있을 것 같습니다. 즉, 사용되지 않을 노드들이 과연 Autoregressive 학습에 항상 좋은 성능을 낼지가 의문점입니다. Experiment를 보면 Mnist에 한정된 실험이었으며 고화질의 데이터를 사용하지 않아 어떤 결과를 낼지 저는 의문점을 가지고 있습니다.

비록 이런 의문점은 존재하나 MADE는 정말 의미가 있는 연구라고 생각됩니다. 다들 눈치채셨겠지만 MADE의 분포추정방식은 Normalize Flow와 흡사한 분포들의 곱으로 전체 분포를 추정합니다. NF가 MADE에 영향을 받았다고는 할 수 없으나 implicit 하게 기여하지 않았을까요?

긴 글 읽어주셔서 감사합니다