4장 — 라그랑지안과 최소 작용 원리

작용: 경로의 점수를 미분하면 뉴턴 법칙이 나온다

구조요원 문제에서 변수는 x 하나뿐이었다. 그것은 경로가 두 직선 조각으로 이루어져 있다고 미리 정했기 때문이다. 그렇다면 경로의 모양 자체를 모를 때는 변수가 몇 개나 필요할까?

역사: 요한 베르누이의 공개 도전

경로의 모양 자체를 찾는 문제는 틀린 답을 한 번 거쳐 풀렸다. 1638년 갈릴레이는 높이가 다른 두 점 사이를 가장 빨리 내려오는 길은 직선이 아니라 원의 호라고 보았다. 직선보다 빠르다는 것은 맞았지만, 원의 호도 가장 빠른 길은 아니었다.

1696년 6월 요한 베르누이는 학술지 『악타 에루디토룸』에 이 문제를 공개 도전으로 실었다. 높이가 다른 두 점을 잇는 비탈면을 따라 공이 마찰 없이 굴러 내려올 때, 가장 빨리 내려오는 비탈면은 어떤 모양인가? 여섯 달 동안 답이 오지 않자 라이프니츠의 부탁으로 기한을 늘렸고, 결국 다섯 사람이 답을 냈다. 그 가운데 잘 알려진 일화가 하나 있다. 뉴턴은 1697년 1월 문제를 받은 날 오후부터 이튿날 새벽까지 하룻밤 만에 풀어 이름을 밝히지 않고 답을 보냈는데, 베르누이는 답안을 보고 "발톱을 보고 사자를 알아본다"고 말했다고 전해진다. 이름을 밝히지 않았어도 풀이만 보고 누가 보냈는지 알아보았다는 것이다.

요한 베르누이(1667–1748, 왼쪽)와 아이작 뉴턴(1643–1727, 오른쪽). 왼쪽은 요한 루돌프 후버의 그림(1740년경), 오른쪽은 고드프리 넬러의 그림(1689). 출처: Wikimedia Commons, 퍼블릭 도메인
요한 베르누이(1667–1748, 왼쪽)와 아이작 뉴턴(1643–1727, 오른쪽). 왼쪽은 요한 루돌프 후버의 그림(1740년경), 오른쪽은 고드프리 넬러의 그림(1689). 출처: Wikimedia Commons, 퍼블릭 도메인

베르누이 자신의 풀이는 빛에서 빌려 왔다. 공은 떨어진 높이만큼 빨라진다. 그래서 아래로 갈수록 빛이 빨라지는 매질을 얇은 층으로 나누어 상상하고, 층마다 굴절 법칙을 적용해 빛이 지나가는 선을 구했다. 걸리는 시간이 가장 짧은 길을 찾는 페르마의 생각이 이렇게 역학의 문제로 건너왔다.

비탈면 모양마다 붙는 점수

답은 직선이 아니라 사이클로이드였다. 바퀴가 곧은 선을 따라 구를 때 테두리의 한 점이 그리는 곡선을 위아래로 뒤집은 모양이다. 이렇게 공이 가장 빨리 내려오는 비탈면을 최단강하선(brachistochrone)이라 한다.

반지름 1m인 바퀴가 위쪽 선을 따라 반 바퀴 구르는 동안 테두리의 점이 그리는 선이 사이클로이드다(가로 π ≈ 3.14m, 높이 차는 바퀴 지름 2m). 같은 두 점을 잇는 네 모양: 직선 비탈, 수직으로 떨어진 뒤 수평 이동, 사이클로이드, 사이클로이드를 한가운데서 0.1m 부풀린 모양.
반지름 1m인 바퀴가 위쪽 선을 따라 반 바퀴 구르는 동안 테두리의 점이 그리는 선이 사이클로이드다(가로 π ≈ 3.14m, 높이 차는 바퀴 지름 2m). 같은 두 점을 잇는 네 모양: 직선 비탈, 수직으로 떨어진 뒤 수평 이동, 사이클로이드, 사이클로이드를 한가운데서 0.1m 부풀린 모양.
최단강하선 문제. 가로 3.14m, 높이 차 2m를 내려올 때 직선 비탈은 1.19초, 사이클로이드는 1.00초가 걸린다. 점은 0.1초 간격의 공 위치로, 사이클로이드 위의 공은 처음에 가파르게 떨어지며 속도를 먼저 얻는다.
최단강하선 문제. 가로 3.14m, 높이 차 2m를 내려올 때 직선 비탈은 1.19초, 사이클로이드는 1.00초가 걸린다. 점은 0.1초 간격의 공 위치로, 사이클로이드 위의 공은 처음에 가파르게 떨어지며 속도를 먼저 얻는다.

이 문제도 점수로 읽을 수 있다. 비탈면의 모양 하나를 정하면 공이 내려오는 시간이 하나 나오고, 이 시간이 그 모양의 점수다. 그림과 같은 조건에서 몇 가지 모양을 비교해 보자. 가로 3.14m는 반지름 1m인 바퀴가 반 바퀴 굴러간 거리 π이고, 높이 차 2m는 그 바퀴의 지름이다. 마찰은 없고 g = 9.8m/s²로 둔다.

비탈면 모양 내려오는 시간
직선 비탈 (가장 짧은 길) 1.19초
2m를 수직으로 떨어진 뒤 3.14m를 수평으로 이동 1.14초
사이클로이드 1.004초
사이클로이드를 한가운데서 위나 아래로 최대 0.1m 비튼 모양 1.005초 (약 0.001초 느림)

구조는 구조요원 문제와 같다. 직선은 가장 짧지만 처음에 기울기가 완만해서 속도를 늦게 얻는다. 수직으로 떨어지면 속도는 빨리 얻지만 길이가 길어진다. 사이클로이드는 처음에 가파르게 떨어져 속도를 먼저 얻고, 그 속도로 남은 거리를 간다.

마지막 줄이 중요하다. 사이클로이드를 0.1m나 비틀어도 시간은 0.001초 남짓 늘 뿐이다. 모양을 조금 바꿔도 점수가 거의 변하지 않는 것, 이것이 점수의 기울기가 0이라는 뜻이다. 구조요원과 다른 점은 변수의 수다. 구조요원의 길은 x 하나로 정해졌지만, 비탈면의 모양은 곡선 전체라서 변수가 무한히 많다.

경로를 점으로 자르기

변수가 무한히 많은 경로는 어떻게 다룰까? 경로를 일정한 시간 간격마다 점 N개로 잘라 각 점의 위치를 변수로 삼으면, 경로 하나는 숫자 N개를 늘어놓은 벡터 하나가 된다. 이 벡터의 한 성분, 즉 어느 한 순간의 위치를 아주 조금 옮기면 점수도 조금 바뀐다. 따라서 성분마다 "조금 옮겼을 때 점수가 얼마나 바뀌는가"를 모으면 점수의 기울기를 만들 수 있는데, 이것은 손실 함수를 매개변수마다 미분해 모아 놓은 기울기와 같은 것이다. 그렇다면 이 기울기를 0으로 두면 무엇이 나올까? 힘은 어디에도 쓰지 않았으니, 힘에 관한 법칙이 나올 것 같지는 않다.

위로 던진 공

공을 위로 던졌더니 2초 뒤 같은 높이로 돌아왔다고 하자. 처음과 끝의 높이는 y(0) = y(2) = 0으로 정해져 있지만, 그 사이에 높이가 어떻게 변했는지는 모른다고 가정한다.

그런데 경로마다 무엇을 점수로 붙여야 할까? 구조요원과 비탈면에서는 걸리는 시간이 점수였지만, 던진 공은 어느 경로든 2초 뒤에 돌아오도록 정해 두었으므로 걸리는 시간이 모두 같다. 시간은 점수가 될 수 없다. 역학이 쓰는 답은 이렇다. 시간을 Δt 간격으로 자르고 각 시각의 높이 y₀, y₁, …, y_N을 변수로 둔 뒤, 각 구간마다 "운동에너지 − 위치에너지"에 시간 Δt를 곱해 모두 더한다. 운동에너지는 빠르기에 담긴 에너지로 ½ × 질량 × 속도²이고, 위치에너지는 높이에 담긴 에너지로 질량 × 중력가속도 × 높이다. 이때 각 구간의 속도는 이웃한 두 높이의 차이를 Δt로 나눈 값으로 계산한다. 왜 하필 두 에너지의 차인지는 뒤에서 따져 보고, 지금은 이 점수로 계산하면 무엇이 나오는지부터 보자. 역학에서는 이 점수를 작용 (경로 점수, action)이라 하고 𝒜로 쓴다. 강화학습의 action(행동)과는 영어 이름만 같다. 물리 교과서는 작용을 흔히 S로 쓰지만 이 책에서 S는 엔트로피의 글자라서, 겹치지 않게 필기체 𝒜를 쓴다. 글자만 바꾼 것이다.

A(y1,…,yN−1)=∑kΔt[12m(yk+1−ykΔt)2−mg yk]\textcolor{#66a61e}{\mathcal{A}}(\textcolor{#1b9e77}{y_1}, \dots, \textcolor{#1b9e77}{y_{N-1}}) = \sum_{k} \Delta \textcolor{#a67a63}{t} \left[ \tfrac{1}{2} \textcolor{#428da6}{m} \left( \frac{\textcolor{#1b9e77}{y_{k+1}} - \textcolor{#1b9e77}{y_k}}{\Delta \textcolor{#a67a63}{t}} \right)^2 - \textcolor{#428da6}{m} \textcolor{#d62ee6}{g}\, \textcolor{#1b9e77}{y_k} \right]
A경로의 점수 (이산 작용)ykk번째 시각의 공의 높이 (양 끝은 고정)Δt시간 간격m공의 질량g중력가속도\begin{array}{ll} \textcolor{#66a61e}{\mathcal{A}} & \text{경로의 점수 (이산 작용)} \\ \textcolor{#1b9e77}{y_k} & \text{k번째 시각의 공의 높이 (양 끝은 고정)} \\ \Delta \textcolor{#a67a63}{t} & \text{시간 간격} \\ \textcolor{#428da6}{m} & \text{공의 질량} \\ \textcolor{#d62ee6}{g} & \text{중력가속도} \end{array}

이 점수를 가운데 점 y_k 하나로 편미분해 보자. 점수 가운데 y_k가 들어 있는 항은 앞뒤 두 구간의 운동에너지와 y_k의 위치에너지, 이렇게 세 개뿐이므로 계산은 간단하다. 그 기울기를 0으로 두고 정리하면 다음을 얻는다.

m yk+1−2yk+yk−1Δt2=−mg\textcolor{#428da6}{m}\, \frac{\textcolor{#1b9e77}{y_{k+1}} - 2 \textcolor{#1b9e77}{y_k} + \textcolor{#1b9e77}{y_{k-1}}}{\Delta \textcolor{#a67a63}{t}^2} = -\textcolor{#428da6}{m} \textcolor{#d62ee6}{g}
ykk번째 시각의 공의 높이Δt시간 간격m공의 질량g중력가속도\begin{array}{ll} \textcolor{#1b9e77}{y_k} & \text{k번째 시각의 공의 높이} \\ \Delta \textcolor{#a67a63}{t} & \text{시간 간격} \\ \textcolor{#428da6}{m} & \text{공의 질량} \\ \textcolor{#d62ee6}{g} & \text{중력가속도} \end{array}

왼쪽은 가속도를 이웃한 높이들의 차(차분)로 어림한 것이고 오른쪽은 중력이므로, 이 식은 질량과 가속도의 곱이 중력과 같다는 말이다. 다시 말해 F = ma를 한 번도 쓰지 않았는데 점수의 기울기가 0인 조건이 곧 F = ma다.

경로를 점으로 자른 모습(그림은 점 11개, 본문 계산은 41개). 가운데 점 하나를 위아래로 움직이면 앞뒤 두 구간의 운동에너지와 그 자리의 위치에너지만 바뀌므로, 이 세 항만 미분하면 된다.
경로를 점으로 자른 모습(그림은 점 11개, 본문 계산은 41개). 가운데 점 하나를 위아래로 움직이면 앞뒤 두 구간의 운동에너지와 그 자리의 위치에너지만 바뀌므로, 이 세 항만 미분하면 된다.

실제로 풀면

이 결과를 식으로만 확인하지 말고 실제로 풀어 보자. Δt = 0.05초(점 41개)로 두고, 모든 높이를 0에서 시작해 경사하강으로 점수를 줄여 나가면 다음 결과에 도달한다.

값
시작 경로(모두 0)의 점수 0
경사하강이 찾은 경로의 점수 약 −32.0 (단위질량당)
최고 높이 4.9m (1초에)
공식 y(t) = (g/2)·t·(2 − t)와의 차이 10⁻¹³m 이하

경사하강이 찾은 경로는 1초에 4.9m까지 올라갔다가 내려오는 포물선이고, 공식과의 차이는 10⁻¹³m 이하다. 아래는 이 계산을 PyTorch로 적은 코드다. 운동방정식은 코드 어디에도 쓰지 않는다는 점에 주목하자. 경사하강이 작용을 줄여 가는 것만으로 포물선이 나온다.

import torch

g, T, N = 9.8, 2.0, 40
dt = T / N
inner = torch.zeros(N - 1, requires_grad=True)   # 가운데 점들의 높이 (학습 대상)

def action(inner):
    y = torch.cat([torch.zeros(1), inner, torch.zeros(1)])  # 양 끝은 0으로 고정
    v = (y[1:] - y[:-1]) / dt
    kinetic = 0.5 * (v ** 2).sum() * dt
    potential = (g * inner).sum() * dt
    return kinetic - potential                     # L = K - U를 시간에 대해 더함

opt = torch.optim.SGD([inner], lr=0.02)
for _ in range(20000):
    opt.zero_grad()
    action(inner).backward()
    opt.step()

t = torch.linspace(0, T, N + 1)[1:-1]
exact = g / 2 * t * (T - t)
print(inner.max().item())                   # 4.9
print((inner - exact).abs().max().item())   # 거의 0

코드를 돌려 보았다면 kinetic - potential을 kinetic + potential로 바꿔 보자. 그러면 공이 아래로 휘는 경로가 나온다. 왜 그런지는 연속인 경로의 방정식을 세운 뒤 "왜 두 에너지의 차인가"를 따지며 다시 보자.

직접 움직여 보기경로의 경사하강새 창에서 열기 ↗

ML에서: 경로를 가중치처럼 학습하기

우리가 한 일은 신경망의 가중치를 학습하는 방식과 똑같다. "경로"라는 매개변수 벡터를 두고, 작용을 손실 함수로 삼아 경사하강으로 학습한 것이다. 코드에서 달라진 것은 손실 함수를 계산하는 부분뿐이고, zero_grad, backward, step으로 이어지는 학습 루프는 신경망을 학습할 때와 한 줄도 다르지 않다.

문제 3. 공 대신 용수철

위로 던진 공 대신 질량 1인 추가 용수철에 매달려 있다고 하자. 추가 평형 위치에서 y만큼 벗어나면 위치에너지는 ½y²이다. 점으로 자른 작용 𝒜 = Σ Δt[½((yₖ₊₁ − yₖ)/Δt)² − ½yₖ²]을 가운데 점 yₖ로 편미분해 0으로 두면 어떤 식이 나오는가? 그 식이 말하는 힘은 무엇인가?

김민준 M04
김민준

운동에너지 쪽은 공과 똑같으니까 (2yₖ − yₖ₊₁ − yₖ₋₁)/Δt이고, 위치에너지 ½yₖ²을 미분하면 yₖ. 합쳐서 0으로 두면 (yₖ₊₁ − 2yₖ + yₖ₋₁)/Δt = −yₖ예요.

선생님 T02
선생님

서연 학생, 이 식에서 Δt를 아주 작게 하면 어떻게 되죠?

이서연 S06
이서연

왼쪽 분자는 이웃한 점끼리의 차라서 Δt²쯤으로 작아지고, 그걸 Δt로 나누면 Δt쯤이에요. Δt가 0으로 가면 왼쪽은 0이 되니까 yₖ = 0이어야 해요. 추가 늘 제자리에 서 있다는 말인데요?

김민준 M05
김민준

그럴 리가 없는데. 어디서 틀렸지?

선생님 T01
선생님

위치에너지 항에는 무엇이 곱해져 있었죠?

김민준 M08
김민준

Δt요! ½yₖ²Δt를 미분하면 yₖΔt예요. 그럼 (yₖ₊₁ − 2yₖ + yₖ₋₁)/Δt² = −yₖ, 가속도 = −위치. 늘어난 만큼 되돌리는 힘, 훅의 법칙이네요.

이서연 S07
이서연

위치에너지만 바꿨는데 왼쪽의 가속도 부분은 공과 똑같네. 운동에너지 쪽이 질량 × 가속도를 만들고, 위치에너지 쪽이 힘을 만드는 거구나.

선생님 T13
선생님

맞아요. 위치에너지를 위치로 미분해 마이너스를 붙인 것이 힘이에요. 공에서는 그게 −mg였고, 여기서는 −y예요.

김민준 M10
김민준

성적 합산할 때 다른 항목은 다 반영 비율을 곱했는데 한 항목만 빼먹은 거랑 같네요. 그러니 단위가 안 맞았죠.

문제 4. 점을 두 배로 늘렸더니 학습이 터졌다

본문의 PyTorch 코드(Δt = 0.05, 점 41개, 학습률 0.02)는 포물선을 잘 찾는다. 더 정밀하게 하려고 점을 81개로 늘려 Δt = 0.025로 줄였더니, 같은 학습률에서 30여 번 만에 높이가 수백만 미터를 넘었다. 왜 그럴까? 학습률은 얼마보다 작게 잡아야 하는가? (질량은 1로 둔다. 풀어 본 뒤 위젯 2의 「문제 4 불러오기」로 확인해 보자.)

김민준 M11
김민준

점이 많아지면 더 정확해지기만 해야 하는데 이상하네요. 코드에 버그가 생긴 거 아니에요?

선생님 T02
선생님

점 하나 y_k만 움직일 때 작용이 얼마나 가파르게 휘는지, 곧 y_k로 두 번 미분한 값을 구해 봐요.

이서연 S07
이서연

y_k가 든 운동에너지 항은 앞뒤 두 개고, 각각 (y_k − 이웃)²/(2Δt)예요. 두 번 미분하면 1/Δt + 1/Δt = 2/Δt고, 위치에너지는 y_k에 대해 직선이라 0이에요. Δt = 0.05면 40, 0.025면 80. 점을 촘촘히 할수록 작용이 더 가파르게 휘네요.

선생님 T14
선생님

맞아요. 그런데 점 하나만 움직이는 방향이 가장 가파르게 휘는 방향일까요? 이웃한 점들이 서로 반대로, 톱니 모양으로 움직이면요?

이서연 S01
이서연

점 하나를 올릴 때 양옆은 내려가니까 이웃과의 차이가 두 배가 돼서 운동에너지가 더 가파르게 늘어요. 휨이 거의 두 배, 약 4/Δt네요. Δt = 0.025면 160이고요.

선생님 T01
선생님

휨이 λ(람다)인 방향에서 경사하강 한 걸음은 그 방향 성분에 무엇을 곱하죠?

김민준 M04
김민준

지금 값에서 학습률 × 기울기를 빼니까 (1 − 학습률 × λ)요. 학습률 0.02에 λ = 160이면 1 − 3.2 = −2.2. 한 걸음마다 부호가 바뀌면서 2.2배씩 커지네요. 그래서 30여 번 만에 수백만 미터가 됐구나.

선생님 T01
선생님

그러니 학습률 × λ가 2를 넘으면 안 돼요. 학습률은 몇보다 작아야 하죠?

이서연 S08
이서연

2/(4/Δt) = Δt/2요. Δt = 0.05면 0.025, Δt = 0.025면 0.0125라서 0.02는 첫 경우에만 안전했네요.

김민준 M09
김민준

딥러닝 수업에서 들은 거랑 같네요. 손실이 가장 가파르게 휜 방향의 곡률로 2를 나눈 값보다 학습률이 작아야 한다고요. 해상도를 높였더니 그 곡률이 커진 거고요.

선생님 T13
선생님

샤워기 온도 손잡이를 한 번에 너무 크게 돌리면 뜨거웠다 차가웠다 하며 점점 심해지는 것과 같아요. 학습률을 0.01로 낮추면 다시 포물선이 나와요. 대신 완만한 방향은 더 천천히 움직이니 반복이 더 필요하고요.