점만 모으면: 오토인코더의 이웃은 비어 있다
오토인코더에서 새 그림을 뽑지 못한 까닭은 둘이었다. 잠재 변수를 열 배로 늘리거나 돌려도 손실이 그대로라서 잠재 변수가 어디에 놓일지 아무도 정하지 않았고, 학습한 잠재 변수들 사이의 빈 곳이나 바깥 들판을 넣으면 무엇이 나올지도 정해지지 않았다. 가장 먼저 떠오르는 고침은 첫째 까닭만 손보는 것이다. 잠재 변수를 표준정규분포 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%로 나온다. 회색조로 학습한 벌점 없는 오토인코더의 39.4%와 견주면 뽑을 곳은 정해진 셈이다.
학습한 자리에서 조금만 옮기면
그렇다면 남은 것은 둘째 까닭, 점들 사이와 둘레다. 디코더는 학습하는 동안 학습 그림이 놓인 바로 그 자리만 보았다. 시험 그림 1,000장을 인코더에 넣은 자리에서 되살리면, 분류기가 원래 숫자로 읽는 비율이 74.4%다. 그 자리에서 여덟 방향으로 조금씩 옮겨 디코더에 넣고, 옮기기 전 그림과의 제곱 거리(784칸 합)를 재 보았다. 옆에는 같은 몸통·같은 데이터로 다르게 학습한 모델 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만 옮겨도 이웃 여러 장을 지나는 거리다. 그 사이를 오토인코더의 디코더는 한 번도 배운 적이 없다.
ML에서: 점을 모으는 길도 다시 나왔다
점만 모으는 오토인코더가 이 절의 숫자처럼 꽤 쓸 만한 그림을 뽑는다는 것은 뒤의 연구에서도 다시 확인되었다. 2017년 톨스티힌(Ilya Tolstikhin)과 동료들의 바서슈타인 오토인코더는 이 절의 둘째 벌점처럼 「인코딩한 학습 데이터 전체의 분포」를 사전분포에 맞추는 벌점을 쓴다. 2019년 고시(Partha Ghosh)와 동료들은 구름에서 뽑는 단계를 다른 규제로 바꿔도 똑같이 매끄럽고 뜻 있는 잠재 공간을 얻을 수 있다고 보고했다. 그러니 다음 절의 방법은 그림을 뽑는 유일한 길이 아니다. 다만 이 장은 그 방법을 따라간다. 이웃을 채우는 방식이 가장 단순하고, 그 손실이 뒤의 ELBO 절에서 로그우도의 바닥이라는 뜻을 얻기 때문이다.
문제 7. 조금만 옮겨도 무너지는 쪽
위젯 2에서 시험 그림 「3」을 고르고 두 모델을 위로 0.2만큼 옮긴다(「문제 7 불러오기」). (가) 두 디코더의 그림은 옮기기 전과 얼마나 달라지는가? (나) 이번에는 그림 「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 바로 옆이니까… 거의 같은 3이어야 맞죠. 뜻이 가까운 자리니까요. 아, 그럼 학습할 때 3의 점만 넣지 말고 그 둘레 자리들도 넣어서 「여기도 이 3이야」라고 가르치면 되잖아요!

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

바로 그거예요. 모델 B 가 그렇게 배운 모델이에요. 점 하나 대신 점 둘레의 작은 가우시안 구름을 디코더에 넣었어요. 그런데 그렇게 하려면 「구름」이 무엇인지부터 분명히 해야 해요. 그림 한 장에 왜 분포가 붙는지요.

기출문제 답 번호만 외운 친구는 보기 순서만 바꿔도 틀리는데, 비슷한 문제를 여러 번 바꿔 푼 친구는 버티는 거랑 같네요.