VAE: 잠재 변수를 분포로 뽑아 그리기
이 장의 물음
오토인코더는 그림마다 생김새를 적은 잠재 변수를 사람 손 없이 찾아 주었다. 그 값을 학습 그림에서 베껴 오지 않고 새로 골라 디코더에 넣으면 새 그림이 나올 것 같다. 그런데 학습 그림이 쓴 값만 넣으면 다시 룩업 테이블이 되고, 아무 데서나 고른 값을 넣으면 무엇이 나올지는 학습이 정해 주지 않았다. 망가진 그림을 고치는 디노이저도 고칠 줄만 알지 새 그림을 만들어 본 적은 없다. 이 장은 다음 물음을 차례로 따라간다.
- 생김새 값을 학습 그림들이 쓴 값 사이에서 골라 넣어도 제대로 된 숫자가 나올까? 새로 고를 값은 어디서, 어떻게 뽑아야 할까?
- 그림 한 장이 값 하나 대신 값들의 분포를 갖는다는 것은 무슨 뜻일까? 그렇게 배운 모델이 실제로 올리는 값은 무엇이고, 우리가 바라던 값과 얼마나 떨어져 있을까?
- 뽑은 그림이 흐리다면 라벨이나 생김새를 조건으로 더 주면 선명해질까? 조건을 한없이 잘게 주면 흐림이 사라질까, 그리고 그 조건은 공짜일까? 이미 학습한 모델 안에서 「안경」 같은 속성을 찾아 바꿀 수도 있을까?
- 고치는 법만 배운 디노이저에 아무 그림도 담기지 않은 순수한 노이즈를 주면 무엇이 나올까? 사람이 재서 정해 준 조건 대신 앞 스텝의 결과를 조건으로 삼으면 무엇이 달라질까?
생성 모델로서의 한계: 이웃은 비어 있다
오토인코더는 그림마다 생김새를 적은 값인 잠재 변수를 사람 손 없이 찾아 주었다. 그 잠재 변수를 새로 골라 디코더에 넣으면 새 그림이 나올 것 같다. 그런데 새로 고를 잠재 변수는 어디서 뽑아야 하고, 학습 그림들이 쓰지 않은 자리에서도 디코더가 제대로 된 숫자를 그려 줄까? 생성 모델로 쓰기에는 두 군데가 모자란다. 오토인코더의 이 한계 두 가지(사이가 비어 있음, 뽑을 곳이 정해지지 않음)를 이 장이 한 단계씩 푼다.
- 한계 ① 사이가 비어 있다. 학습 그림들의 잠재 변수 사이, 곧 데이터의 중간 영역에서 잠재 변수를 골라 넣어도 제대로 된 숫자가 나왔으면 한다. 그런데 디코더는 학습 그림의 잠재 변수에서만 배웠고, 그 사이에서 무엇을 그릴지는 학습이 정해 주지 않았다. 티셔츠 가게가 표에 없는 2.5번을 받았을 때 무엇을 줄지 모르는 것과 같다.
- 한계 ② 뽑을 곳이 정해지지 않았다. 무작위 요소, 곧 잠재 변수를 어디서 어떻게 뽑을지를 쉽게 정의하고 싶다. 그런데 잠재 변수를 열 배로 늘리거나 돌려도 손실이 그대로라서, 잠재 변수가 어디에 놓일지 아무도 정하지 않았다. 그런 잠재 변수를 아무 데서나 뽑으면 어떤 그림이 나오는지는 아래 문제 1에서 직접 뽑아 본다.
둘 가운데 손보기 쉬워 보이는 것은 ②다. 잠재 변수를 표준정규분포 N(0, I) 안쪽으로 끌어당기는 페널티를 손실에 더하면, 새 그림을 뽑을 때도 N(0, I)에서 뽑으면 되지 않을까? 이 절은 잠재 변수를 모으는 페널티만 더해 보고, ①이 함께 풀리는지 본다.
흑백 그림과 칸마다 던지는 동전
이 절부터는 그림을 밝기 0.5에서 잘라 칸마다 잉크(1)와 바탕(0)만 있는 흑백으로 쓴다. 디코더는 784칸 각각이 잉크일 확률을 내놓고, 그 확률로 칸마다 따로 동전을 던진다고 본다. 그러면 분류기의 소프트맥스가 라벨 하나 대신 라벨들의 분포를 내놓던 것처럼, 디코더도 그림 한 장 대신 그림들의 분포를 내놓는다. 손실은 제곱 오차 대신, 이 분포가 실제 그림에 주는 확률의 로그에 마이너스를 붙인 값이다.
페널티는 두 가지를 해 봤는데, 먼저 잠재 변수 하나하나를 원점 쪽으로 당기는 ½‖z‖²를 더했다. 잠재 변수가 원 안으로 들어오기는 했지만, 생각보다 더 작게 오그라들었다(칸마다 표준편차 0.65, 0.64). 인코더 출력에 c(0 < c < 1)를 곱하고 디코더 첫 층 가중치를 1/c 배로 키우면 재구성은 그대로인데 페널티는 c² 배로 줄기 때문이다. 잠재 변수를 열 배로 늘려도 손실이 같던 것과 같은 구조라, 이 페널티는 「모여라」는 정해도 「얼마나 크게 모여라」는 정하지 못한다. 그래서 N(0, I)에서 뽑으면 지도 바깥 들판을 자주 밟아 숫자 0(21.0%), 1(18.6%), 7(20.8%)로 쏠렸다. 둘째로, 한 묶음(128장)의 잠재 변수들이 칸마다 평균 0, 분산 1이 되도록 당기는 페널티를 썼다. 점 하나하나 대신 점들 전체의 모양을 N(0, I)에 맞추는 것이다. 몸통(784-128-64-2)과 데이터, 학습 횟수(50번 돌기)는 앞으로 비교할 모델과 모두 같다.
이 오토인코더의 잠재 변수는 칸마다 표준편차 1.03, 1.05로 N(0, I)과 크기가 맞게 모였다. N(0, I)에서 잠재 변수를 1만 개 뽑아 디코더에 넣으면 분류기가 90% 넘게 확신하는 그림이 78.9%이고, 숫자는 0부터 9까지 5.1% ~ 15.4%로 나온다. 잠재 변수가 N(0, I) 크기로 모였으니, 묶음 단위 페널티로 뽑을 곳이 정해졌다. 페널티가 없는 오토인코더에서 뽑으면 어떻게 다른지는 아래 문제 1에서 비교해 본다.
학습한 자리에서 조금만 옮기면
한계 ②는 페널티 하나로 풀렸지만, ①, 학습한 점들 사이와 둘레는 어떨까? 디코더는 학습하는 동안 학습 그림의 잠재 변수, 딱 그 점들만 보았다. 시험 그림 1,000장을 인코더에 넣어 얻은 잠재 변수에서 되살리면, 분류기가 원래 숫자로 읽는 비율이 74.4%다. 그 잠재 변수를 여덟 방향으로 조금씩 옮겨 디코더에 넣고, 옮기기 전 그림과의 제곱 거리(784칸 합)를 재 보았다. 옆에는 같은 몸통·같은 데이터로 다르게 학습한 모델 B를 함께 두었는데, 모델 B 를 어떻게 학습했는지는 다음 절에서 밝힌다. 모델 B 도 잠재 변수가 N(0, I) 크기로 모여 있고(칸마다 표준편차 1.08, 1.05), 옮기기 전의 잠재 변수에서는 원래 숫자로 73.8%를 읽어 오토인코더와 거의 같다.
| 옮긴 거리 | 0 | 0.1 | 0.2 | 0.3 | 0.5 |
|---|---|---|---|---|---|
| 점을 모은 오토인코더: 그림이 바뀐 양 | 0 | 2.77 | 9.06 | 16.13 | 27.51 |
| 모델 B: 그림이 바뀐 양 | 0 | 1.43 | 5.08 | 9.95 | 20.16 |
| 점을 모은 오토인코더: 원래 숫자로 읽힘 | 74.4% | 70.4% | 62.7% | 55.3% | 42.5% |
| 모델 B: 원래 숫자로 읽힘 | 73.8% | 72.3% | 67.4% | 60.8% | 47.8% |
같은 잠재 변수에서 시작해 같은 만큼 옮겼는데, 오토인코더의 그림이 두 배쯤 빨리 바뀌고 원래 숫자에서 먼저 벗어난다. 이웃한 시험 그림의 잠재 변수끼리는 0.01 정도 떨어져 있으니, 0.1만 옮겨도 이웃 여러 장을 지나는 거리다. 학습 그림의 잠재 변수 사이를 오토인코더의 디코더는 한 번도 배운 적이 없다. 뽑을 곳을 N(0, I) 하나로 정해도, 뽑힌 잠재 변수가 학습한 점에서 조금만 비켜 나면 그림이 무너진다. 묶음 단위 페널티는 ②를 풀었을 뿐 ①은 그대로 남겼다.
ML에서: 점을 모으는 방법도 다시 나왔다
점만 모으는 오토인코더가 이 절의 숫자처럼 꽤 쓸 만한 그림을 뽑는다는 것은 뒤의 연구에서도 다시 확인되었다. 2017년 톨스티힌(Ilya Tolstikhin)과 동료들의 바서슈타인 오토인코더는 이 절의 둘째 페널티처럼 「인코딩한 학습 데이터 전체의 분포」를 사전분포에 맞추는 페널티를 쓴다. 2019년 고시(Partha Ghosh)와 동료들은 다음 절의 인코더 분포에서 잠재 변수를 뽑는 단계를 다른 규제로 바꿔도 똑같이 매끄럽고 뜻 있는 잠재 공간을 얻을 수 있다고 보고했다. 그러니 다음 절의 방법(인코더 분포에서 뽑기)은 그림을 뽑는 유일한 방법이 아니다. 다만 이 장은 그 방법을 따라간다. 이웃을 채우는 방식이 가장 단순하고, 그 손실이 뒤의 ELBO 절에서 로그우도의 하한이라는 뜻을 얻기 때문이다.
문제 1. 잠재 변수를 아무 데서나 뽑으면
위젯 2의 오토인코더(잠재 변수 2칸, 회색조 그림을 제곱 오차로만 학습하고 잠재 변수를 모으는 페널티는 더하지 않음)에서 새 그림을 얻으려고 잠재 변수를 무작위로 뽑는다. 두 가지 방법을 쓴다: (ㄱ) 표준정규분포 N(0, I)(평균 0, 칸마다 표준편차 1)에서 뽑기, (ㄴ) 시험 그림의 잠재 변수를 모두 감싸는 직사각형 안에서 고르게 뽑기. 되살린 그림을 손글씨 숫자 MLP 분류기(784-256-256-10, 시험 정확도 97.59%)에 넣고, 어느 숫자 하나에 90% 넘게 확신하는 그림의 비율과 분류된 숫자의 비율을 본다. (가) 어느 방법이 더 나을 것 같은가? (나) 실제로 각각 1만 개를 뽑은 결과를 보고 무엇이 문제인지 말하라. (다) 위에서 묶음 단위 페널티를 더한 오토인코더와 비교하면 무엇이 달라졌는가? (위젯 2의 「9개 뽑기」 두 단추로 먼저 눈으로 확인해 보자.)


정규분포가 낫겠죠. 잠재 변수가 원점 근처에 많이 모여 있으니까요.

뽑아 봐요.

(ㄱ)은 분류기가 90% 넘게 확신하는 그림이 39.4%예요. 시험 그림은 94.8%인데요. 위젯에서 보면 원점 근처는 여러 숫자의 잠재 변수 무리가 겹쳐 있어서 숫자인지 뭔지 모를 얼룩이 많이 나와요.

(ㄴ)은 확신 비율이 87.3%야. 이게 더 좋은데?

확신한 그림들이 무슨 숫자인지도 봐요.

1이 31.7%, 0이 21.6%예요. 3은 0.3%, 9는 1.0%뿐이고요. 시험 그림은 숫자마다 10% 안팎인데. 상자가 넓어서 대부분 숫자별 무리 바깥 들판을 뽑았고, 들판에서는 가까운 숫자 무리 끝의 모양을 길게 늘인 굵은 0이나 1이 나와요. 확신은 하는데 고르게 나오지 않아요.

정규분포는 숫자 무리가 겹친 가운데를 뽑아서 망하고, 상자는 들판을 뽑아서 0과 1로 쏠리고. 둘 다 「어디서 뽑아야 하는지」를 모르는 거네요. 시험 그림의 잠재 변수 가운데 원점에서 3 안쪽에 있는 게 33.6%뿐인데 상자 넓이는 1,824나 되니까요.

맞아요. 잠재 변수를 열 배로 늘려도 손실이 같았던 것처럼, 학습은 잠재 변수가 어디에 놓일지 정해 주지 않아요. 그러니 뽑을 곳도 모르는 거예요.

확률론 시간에 표본 공간을 정하지 않고 「무작위로 고른다」고 하면 답이 여러 개 나오던 베르트랑의 역설 같아요. 어떤 분포에서 뽑는지부터 정해야 하네요.

그럼 (다)요. 위에서 묶음마다 잠재 변수를 N(0, I) 모양으로 당긴 오토인코더는, 같은 N(0, I)에서 뽑았을 때 어땠어요?

본문 숫자를 다시 보면… 확신 비율이 78.9%였어요. 39.4%의 두 배쯤이고, 숫자도 0부터 9까지 5.1% ~ 15.4%라서 0과 1로 쏠리지 않았어요.

같은 N(0, I)에서 뽑았는데 왜 이렇게 달라졌을까요?

음, 지도의 크기부터 달라요. 페널티 없는 오토인코더는 잠재 변수가 −19부터 24까지 퍼져 있으니까 N(0, I)는 그 가운데 좁은 곳만 밟았어요. 페널티를 더한 쪽은 잠재 변수를 칸마다 표준편차 1 정도로 모아 두었으니, N(0, I)에서 뽑으면 학습 그림의 점들이 있는 곳을 고루 밟고요.

그래서 뽑을 곳은 페널티 하나로 정해졌어요. 남은 것은 뽑힌 잠재 변수가 학습한 점에서 조금 비켜 났을 때예요. 다음 문제에서 학습한 점의 잠재 변수를 조금씩 옮겨 봐요.
문제 2. 조금만 옮겨도 무너지는 디코더
위젯 1에서 시험 그림 「3」을 고르고 두 모델을 위로 0.2만큼 옮긴다(「문제 2 불러오기」). (가) 두 디코더의 그림은 옮기기 전과 얼마나 달라지는가? (나) 이번에는 그림 「0」과 「7」을 오른쪽으로 옮겨 보라. 언제나 같은 쪽이 먼저 무너지는가? (다) 디코더는 학습하는 동안 이렇게 옮긴 잠재 변수를 본 적이 있는가? 오토인코더의 학습을 어떻게 바꾸면 옮긴 잠재 변수도 배우게 할 수 있을까?


끌어 봤어요. 오토인코더는 제곱 거리 16.78, 모델 B는 1.95예요. 오토인코더 쪽 3은 왼쪽이 닫히면서 8처럼 바뀌었는데, 모델 B 쪽은 그대로 3이에요.

「0」을 오른쪽으로 0.1 옮기면 오히려 오토인코더 0.32, 모델 B 0.51이야. 「7」도 오른쪽으로 0.2 에서 4.19 대 6.12로 오토인코더가 덜 바뀌어. 같은 「3」도 오른쪽으로 옮기면 모델 B 가 5 쪽으로 넘어가고. 한 그림 한 방향만 보고 「오토인코더는 언제나 무너진다」고 하면 안 되겠어.

좋은 지적이에요. 본문의 표는 시험 그림 1,000장과 방향 여덟 개의 평균이었죠. 평균으로는 어땠어요?

0.1에서 2.77 대 1.43, 0.2에서 9.06 대 5.08이요. 그림마다 들쭉날쭉해도 평균으로는 오토인코더가 두 배쯤 빨리 바뀌어요.

그럼 (다)요. 오토인코더의 디코더는 학습하는 동안 어떤 잠재 변수를 받았어요?

인코더가 학습 그림마다 내놓은 점, 딱 그 점들만요. 3의 점에서 0.2 옮긴 잠재 변수는 3의 인코딩도 아니고, 근처에 다른 그림의 점이 있으면 그 그림 쪽으로 끌려갈 뿐이에요. 옮긴 잠재 변수에서 무엇을 그려야 하는지는 아무도 가르쳐 주지 않았어요.

3의 점에서 0.2 옮긴 곳에 있을 법한 그림은 무엇일까요?

3 바로 옆이니까… 거의 같은 3이어야 맞죠. 뜻이 가까운 곳이니까요. 아, 그럼 학습할 때 3의 점만 넣지 말고 그 둘레 잠재 변수들도 넣어서 「여기도 이 3이야」라고 가르치면 되잖아요!

둘레의 잠재 변수를 하나하나 다 넣을 수는 없으니까, 학습할 때마다 점 둘레의 작은 범위에서 아무 자리나 하나 뽑아서 넣으면 되겠다. 여러 번 돌면 그 범위 전체가 그 그림으로 배워지고. 그러면 이웃 그림들의 범위가 겹치는 곳에서는 두 그림 사이쯤을 배우겠지.

바로 그거예요. 모델 B 가 그렇게 배운 모델이에요. 점 하나 대신 점 둘레의 작은 정규분포에서 뽑은 잠재 변수를 디코더에 넣었어요. 그런데 그렇게 하려면 그 정규분포가 무엇인지부터 분명히 해야 해요. 그림 한 장에 왜 분포가 붙는지요.

운전 연수 때 늘 같은 칸에만 주차해 본 사람은 옆 칸으로 옮기기만 해도 헤매잖아요. 주차 칸을 조금씩 바꿔 가며 연습해야 그 둘레가 다 익숙해지고요. 연습한 칸이 학습 그림의 점, 바꿔 본 칸들이 점 둘레네요.
