벡터를 옮기면 달라진다
출발 문제
당신이 지금 북극점에 서 있다고 상상해 보자. 손에는 남쪽을 가리키는 화살표가 들려 있다. 이제 아주 중요한 규칙 하나를 정한다: 이 화살표를 절대로, 어떤 이유로도 회전시키지 않겠다. 오직 "앞으로 밀기"만 허용하고, 화살표가 가리키는 방향을 내 의지로 돌리는 행위는 금지다.
이 규칙을 지키며 걸어가 보자. 먼저 경도 0° 자오선(북극과 남극을 잇는 반원)을 따라 적도까지 내려가면, 화살표는 여전히 남쪽을 가리키고 여기까지는 자연스럽다. 이제 적도를 따라 동쪽으로 90° 이동한다. 적도 위에서 화살표를 회전시키지 않고 옮기면, 화살표는 계속 남쪽을 가리킨다(적도에서 "남쪽"은 적도와 수직 방향이다). 마지막으로 경도 90° 자오선을 따라 다시 북극으로 올라온다. 화살표를 한 번도 회전시키지 않았으니, 당연히 원래 방향을 가리키고 있어야 하지 않을까?
놀랍게도, 화살표는 원래 방향에서 정확히 90° 돌아가 있다. 처음에는 경도 0° 자오선 쪽을 가리키던 화살표가, 이제는 방금 올라온 경도 90° 자오선 쪽을 가리킨다. 한 번도 회전시키지 않았는데 돌아간 것이다. 마술이 아니라, 평평한 바닥에서는 절대 일어나지 않는 이 현상은 구면이 휘어져 있기 때문에 벌어진다.
이 실험에서 한 가지만 바꿔 보자. 적도를 따라 90°가 아니라 180° 이동한 뒤 북극으로 돌아오면 화살표는 180° 돌아가 있다. 적도를 360° 전부 돌면 360°, 곧 원래 방향으로 돌아온다. 적도를 따라 간 거리가 두 배가 되면 경로가 둘러싼 넓이도 두 배가 되고 회전량도 두 배가 된다. 회전량은 경로가 둘러싼 넓이를 따라가는 것일까? 그리고 그 전에, 곡면 위에서 "돌리지 않고 옮긴다"는 것은 정확히 무엇일까?
돌리지 않고 옮기기: 바깥 공간에서 옮긴 뒤 접선공간에 정사영
출발 문제의 화살표는 한 번도 돌리지 않았는데 돌아가 있었으니, "돌리지 않는다"는 말이 정확히 무슨 뜻인지부터 따져 봐야 한다.
평면 위에서는 답이 분명하다. 화살표를 들고 어떤 경로를 따라 걸어간 뒤 출발점으로 돌아오면, 화살표는 반드시 원래 방향을 가리킨다. 경로가 직선이든 구불구불하든, 크든 작든 상관없다. 곡면 위에서는 그렇지 않은데, 곡면 자체가 휘어 있으니, 화살표를 3차원 공간에서 방향을 조금도 바꾸지 않고 들고 가면 화살표가 곡면에서 떠 버린다. 곡면 위에서도 이 "그냥 밀기"를 정의할 수 있을까?
접선공간에서 밀고 곡면으로 되돌리기
곡면을 바깥 3차원 공간에 놓고 보면 방법이 하나 떠오른다. 3차원에서는 "방향을 바꾸지 않는다"가 분명하니, 3차원에서 그대로 들고 간 뒤 곡면에 맞게 눌러 내리면(접선공간 위로 수직 사영하면) 된다. 곡면 위의 "돌리지 않고 옮기기"를 처음 수학 개념으로 세운 레비-치비타도 곡면을 이렇게 바깥 공간에 놓고 생각했다.
숫자로 한 번 해 보자. 무대는 출발 문제의 구다. 구는 어느 점에서나 휜 정도가 같아서, 한 스텝을 계산해 두면 어디서나 같은 계산이 된다. 반지름 1인 구의 북극에서, 경도 0° 쪽(남쪽)을 가리키는 길이 1인 화살표를 들고 그 방향으로 0.1 rad 걸어간다.
- 화살표를 3차원 공간에서 방향을 조금도 바꾸지 않고 들고 간다.
- 새 자리에서는 곡면이 0.1 rad 기울어 있어서 화살표 끝이 곡면에서 떠 있다. 새 자리의 접선공간(그 점에서 곡면에 닿는 평면) 위로 수직으로 눌러 내린다(사영).
- 떠 있던 부분
이 잘려 나가고, 길이는 가 된다. 방향은 새 자리의 남쪽이다.
돌리지 않고 옮긴다면서 길이가 0.5% 줄었으니 곤란하다. 같은 0.1 rad을 잘게 쪼개 스텝마다 접선공간으로 사영해 보자.
| 쪼갠 스텝 수 | 한 스텝 | 도착한 화살표의 길이 |
|---|---|---|
| 1 | 0.1 | 0.99500 |
| 10 | 0.01 | 0.99950 |
| 100 | 0.001 | 0.99995 |
열 배 잘게 쪼갤 때마다 줄어든 양도 10분의 1이 되는데, 왜 그럴까? 한 스텝에 줄어드는 길이
이렇게 끝없이 쪼갠 극한이 곡면 위의 “돌리지 않고 옮기기”, 곧 평행이동(곡선을 따라 돌리지 않고 옮기기)이다. 좌표로 적으면 공변미분(좌표축이 점마다 돌아가서 생기는 성분 변화를 뺀 미분)이 0이 되게 옮기는 것과 같은 규칙이다. 평면에서는 이렇게 옮긴 화살표가 어떤 경로로 돌아와도 제자리였는데, 이제 곡면 위에서 이 규칙대로 한 바퀴를 돌면 무엇이 남는지 볼 차례다.
문제 1: 비스듬한 화살표를 접선공간에 사영하면
계산 반지름 1인 구의 북극에서 경도 0° 자오선을 따라 남쪽으로 0.1 rad 걷는다. 이번에는 화살표가 걷는 방향(남쪽)과 45°를 이루며 경도 90° 쪽으로 기울어 있고, 길이는 1이다. (가) 0.1 rad을 한 번에 들고 가서 접선공간에 사영하면, 도착한 화살표가 걷는 방향과 이루는 각과 길이는 얼마인가? 0.1 rad을 10스텝, 100스텝으로 쪼개면 어떻게 되는가? (나) 출발 문제의 세 구간은 모두 대원(구의 중심을 지나는 평면이 구면을 자른 가장 큰 원) 조각이다. (가)에서 얻은 사실만으로, 출발 문제의 화살표는 왜 90° 돌아가 있는지 설명하라.
같이 풀기 세션 1

numpy로 해 봤어요. 앞(남쪽) 성분 0.707은 한 번 사영하면

옆 성분은 왜 안 잘려?

북극에서 경도 90° 쪽은 이 자오선 위에서는 어디서나 동쪽이고, 동쪽은 자오선을 따라 내려가도 3차원에서 같은 방향이거든. 새 자리의 접선공간에 처음부터 놓여 있어.

그런데 이상해요. 길이 손실은 쪼개면 사라진다고 했지만, 각도는 스텝마다 늘 옆쪽으로만 기울잖아요. 같은 쪽으로 쌓이니까 쪼개도 45.14°보다 작아지지 않을 것 같아요. 그러면 평행이동도 비스듬한 화살표는 옆으로 돌리는 거네요.

쪼개서 직접 계산해 볼까요? 10조각이면 한 조각에서 각이 얼마나 기울어요?

한 조각에 0.0014°쯤이고, 10조각을 합해도 45.014°예요. 100조각이면 45.0014°. 같은 쪽으로 쌓이긴 하는데, 합이 쪼갤 때마다 10분의 1로 줄어요.

한 스텝에서 기우는 양이 스텝 크기의 무엇에 비례하길래 그럴까요?

앞 성분이

그럼 스텝 크기를 10분의 1로 줄이면, 한 스텝에서 기우는 양과 스텝 수는 각각 어떻게 돼요?

한 스텝에서 기우는 양은 100분의 1, 스텝 수는 10배라 합은 10분의 1이에요. 길이가 줄어든 것과 같은 구조네요. 끝없이 쪼개면 각은 45°에 그대로 머물러요.

그럼 (나)요. 대원을 따라 옮기는 동안 화살표와 경로 사이의 각이 그대로라는 걸 출발 문제에 써 볼까요?

첫 구간에서 화살표는 경로와 같은 방향이니 각이 0°예요. 대원을 따라가는 동안은 0°가 그대로고요. 적도에 닿으면 경로가 남쪽에서 동쪽으로, 왼쪽으로 90° 꺾이는데 화살표는 안 꺾이니까… 이제 화살표는 경로의 오른쪽 90°예요.

둘째 모퉁이에서는요?

또 왼쪽으로 90° 꺾으니까 화살표는 경로의 정반대가 돼요.

그럼 마지막 구간에서 북쪽으로 올라가는 내내 화살표는 뒤쪽을 가리키는 거네?

응, 뒤쪽이니까 경도 90° 자오선을 따라 남쪽이야. 처음엔 경도 0° 자오선을 따라 남쪽이었으니까 90° 어긋났어요.

모퉁이에서 꺾인 건 경로였지 화살표가 아니었네요. 조교님이 채점 기준표의 줄을 하나 밀어 놓은 걸 보고 제 점수가 바뀐 줄 알고 따지러 간 적이 있는데, 제 점수는 그대로였고 기준이 움직였던 거예요.
문제 2: 라이브러리가 모멘텀을 옮기는 법
계산 길이를 1로 맞춘 임베딩처럼 파라미터가 반지름 1인 구면 위에 묶인 모델을 모멘텀과 함께 학습한다. 최적화 라이브러리 Manopt(매트랩)와 geoopt(PyTorch)의 구면은 한 스텝 옮긴 뒤, 지난 모멘텀을 새 점의 접선공간 위로 수직으로 사영해 옮긴다. 지금 모멘텀은 길이 1이고, 이번 스텝(대원을 따라 0.2 rad)의 방향과 60°를 이룬다. 그래디언트(손실이 가장 가파르게 커지는 방향의 벡터)가 새로 더하는 항은 빼고 지난 모멘텀만 보자. (가) 한 번 사영한 모멘텀의 길이와, 스텝 방향과 이루는 각은 얼마인가? (나) 학습률을 10분의 1로 줄여 같은 0.2 rad을 10스텝에 가면? (다) 사영 대신 평행이동으로 옮기면?
같이 풀기 세션 2

스텝마다 모멘텀을 사영해 잘라 내면, 학습이 길어질수록 잘려 나간 성분이 쌓여서 모멘텀이 다 닳아 없어지는 거 아니에요? 그러면 모멘텀을 쓰는 의미가 없는데요.

한 스텝에서 얼마나 잘리는지부터 볼까요? 잘려 나가는 부분은 어느 방향을 향하고 있죠?

문제 1처럼 성분을 나눠 볼게요. 옆 성분 0.866은 새 자리에서도 접선이라 그대로고, 스텝 방향 성분 0.5만 곡면에서 떠요.

뜬 부분은 얼마고, 사영하면 무엇이 남아요?

떠 있는 높이는

0.5%밖에 안 줄었네요. 잘려 나간 건 곡면 밖으로 삐져나온 부분이라, 원래 길이에는 거의 보태지 않고 있었어요.

그럼 스텝을 쪼개면 쌓이는 양은 어떻게 될까요? (나)예요.

numpy로 10스텝 돌리니까 길이 0.9995, 각 60.05°예요. 스텝을 잘게 쪼갰더니 쌓인 길이 손실과 각도 변화가 오히려 10분의 1로 줄었어요. (다)는 평행이동이니까 길이 1, 각 60° 그대로고요.

아까 문제 1(45° 기운 화살표)이랑 같은 계산이네요. 그때도 같은 쪽으로 쌓일 줄 알았는데 쪼갤수록 줄었죠. 라이브러리가 평행이동 대신 접선공간 사영을 쓰는 건, 스텝이 작으면 둘이 거의 같아서였어요.

맞아요. 접선공간 사영은 평행이동을 한 스텝으로 근사한 것이고, 스텝이 작을수록 평행이동에 가까워져요. 벡터에서 법선 방향 성분을 한 번 빼기만 하면 되니 계산도 싸고요. Manopt는 정확한 평행이동도 따로 준비해 두었어요.

수치해석 시간에 오일러 방법으로 미분방정식을 풀 때, 한 스텝의 오차는 스텝 크기의 제곱이라 스텝을 줄이면 전체 오차도 따라 준다고 배웠는데, 한 스텝에서 기우는 양이 스텝 크기의 제곱인 것과 같은 구조네.