수학

MATH / 중급 32번

도함수는 민감도다: 국소 선형근사에서 그래디언트까지

옵티마이저가 하는 일은 p -= lr * p.grad 한 줄입니다. 왜 하필 그래디언트의 반대 방향인가. 도함수를 «최선의 선형근사»로 다시 읽고, 편도함수들을 벡터 하나로 묶은 뒤, 코시-슈바르츠로 −∇f가 가장 가파른 내리막임을 증명합니다.

PALDYN Team26 MIN READ

학습 루프의 심장은 세 줄입니다.

loss.backward()                 # 파라미터마다 grad 를 채운다
for p in model.parameters():
    p -= lr * p.grad            # 그래디언트의 반대 방향으로 조금 간다

p.grad는 p와 똑같은 모양의 텐서입니다. 파라미터가 70억 개면 그래디언트도 70억 개이고, 갱신은 그 둘을 원소별로 빼는 것이 전부입니다.

여기서 물음이 둘 생깁니다. 수십억 개의 미분이 어떻게 텐서 하나로 정리되는가, 그리고 왜 하필 그 방향의 반대인가. 두 번째 물음의 답이 특히 그냥 넘어가기 쉽습니다 — 「기울기의 반대로 내려가니까」는 설명이 아니라 같은 말의 반복입니다. 이 글은 두 물음에 답합니다.

지난 글까지 무엇을 최소화하는지를 봤으니, 이제 어떻게 최소화하는지로 넘어갑니다.

민감도로 읽는 도함수

극한이 아니라 근사로 읽기

초급의 미분 글에서 도함수를 평균변화율의 극한으로 정의했습니다. 계산할 때는 그 정의가 맞지만, 딥러닝에서 실제로 쓰는 것은 같은 값의 다른 읽기입니다.

극한이 존재한다는 것은 hh 가 작을 때 다음이 성립한다는 뜻입니다.

f(a+h)≈f(a)+f′(a) hf(a + h) \approx f(a) + f'(a)\, h

오른쪽은 hh 에 대한 일차식입니다. 즉 도함수는 「aa 근처에서 ff 를 대신할 수 있는 가장 좋은 직선의 기울기」입니다. 여기서 「가장 좋은」이 무슨 뜻인지가 요점입니다.

정의. f(a+h)=f(a)+Lh+ε(h)f(a+h) = f(a) + Lh + \varepsilon(h) 로 적었을 때 오차가 hh 보다 빨리 0으로 가는, 즉 ε(h)/h→0\varepsilon(h)/h \to 0 인 LL 이 존재하면 ff 는 aa 에서 미분 가능하고 L=f′(a)L = f'(a) 다.

h보다 빨리 주는 오차

「빨리 0으로 간다」를 수로 확인합니다. f(x)=x2f(x) = x^2, a=3a = 3 이면 f′(3)=6f'(3) = 6 입니다.

hh 실제 변화 f(3+h)−f(3)f(3+h)-f(3) 선형근사 6h6h 오차
0.1 0.61 0.6 0.01
0.01 0.0601 0.06 0.0001
0.001 0.006001 0.006 0.000001

접선은 a 근처에서 곡선을 대신한다

hh 를 10분의 1로 줄이면 오차는 100분의 1이 됩니다. 오차가 h2h^2 짜리라 hh 에 비하면 없는 것이나 마찬가지가 되고, 그래서 아주 작은 걸음에서는 직선이 곡선을 완전히 대신합니다.

이 읽기가 주는 말이 민감도입니다. f′(a)=6f'(a) = 6 은 「입력을 hh 만큼 흔들면 출력이 대략 6h6h 만큼 흔들린다」는 뜻입니다. 손실함수라면 「이 파라미터를 조금 키우면 손실이 그 6배만큼 는다」가 됩니다.

미분이 안 되는 자리

정의를 「선형근사」로 읽어 두면 미분이 안 되는 자리도 곧바로 읽힙니다. 오차가 hh 보다 빨리 0으로 가는 일차식이 하나로 정해지지 않는 자리입니다.

f(x)=∣x∣f(x) = |x| 의 원점이 그렇습니다. 오른쪽에서 다가가면 기울기가 1이고 왼쪽에서 다가가면 −1-1 이라, 양쪽을 함께 대신할 직선이 없습니다. 딥러닝에서 매일 쓰는 ReLU max⁡(0,x)\max(0, x) 도 같은 자리에 같은 문제를 갖습니다 — 왼쪽 기울기 0, 오른쪽 기울기 1입니다.

절댓값과 ReLU의 꺾인 점에서 그래프 아래에 놓이는 직선들

이런 점에서는 접선 하나 대신 그래프 아래를 지나는 직선들의 기울기 전체를 씁니다. 그 집합을 서브그래디언트(subgradient)라고 합니다. ∣x∣|x| 의 원점에서는 [−1,1][-1, 1] 이고 ReLU의 원점에서는 [0,1][0, 1] 입니다. 값 하나가 아니라 구간이라는 점만 다르고, 「그 방향으로 가면 값이 적어도 이만큼은 는다」는 쓰임새는 같습니다.

실무에서는 프레임워크가 그 구간에서 하나를 골라 넣습니다. PyTorch는 ReLU의 0에서 도함수를 0으로 둡니다. 입력이 정확히 0이 되는 일이 거의 없어서 대개 문제가 되지 않고, 되더라도 서브그래디언트 중 하나를 쓰는 것은 이론상 허용되는 선택입니다.

그렇다고 없는 문제는 아닙니다. 꺾인 점이 정답인 경우가 있습니다 — L1 정규화는 가중치를 정확히 0으로 몰아붙이는 것이 목적이라, 최적해가 바로 그 미분 불가능한 자리에 놓입니다. 이럴 때는 그래디언트를 대충 하나 골라 쓰는 대신 근접 연산자 같은 다른 도구를 씁니다. 꺾인 점이 지나가는 자리인지 도착하는 자리인지가 갈림길입니다.

손잡이가 여럿일 때 — 그래디언트

편도함수를 벡터로 묶기

초급 45번의 등고선 글에서 편미분을 세웠습니다. 나머지 변수를 상수로 고정하고 하나만 흔들어 미분하는 것이었고, 그 값이 ∂f/∂x\partial f/\partial x 였습니다. 여기서는 그것을 그대로 받아 쓰고, 여러 개를 한꺼번에 흔들면 어떻게 되는가부터 시작합니다.

변수가 둘일 때 xx 를 h1h_1, yy 를 h2h_2 만큼 함께 흔들면 변화가 더해집니다.

f(a+h)≈f(a)+∂f∂xh1+∂f∂yh2f(a + h) \approx f(a) + \frac{\partial f}{\partial x}h_1 + \frac{\partial f}{\partial y}h_2

오른쪽 두 항의 모양이 내적입니다. 내적 글의 ∑aibi\sum a_i b_i 그대로입니다. 그러니 편도함수들을 벡터로 묶어 두면 식이 한 덩어리로 접힙니다.

정의. ff 의 그래디언트(gradient)는 편도함수를 성분으로 갖는 벡터 ∇f=(∂f∂x1, …, ∂f∂xn)\nabla f = \left(\frac{\partial f}{\partial x_1},\, \dots,\, \frac{\partial f}{\partial x_n}\right) 이고, 이때 선형근사는 f(a+h)≈f(a)+∇f(a)⋅hf(a+h) \approx f(a) + \nabla f(a) \cdot h 로 적힌다.

편도함수들을 벡터 하나로 묶으면 선형근사가 내적이 된다

f(x,y)=x2+3xyf(x,y) = x^2 + 3xy 를 점 (1,2)(1, 2) 에서 봅니다. 편도함수는 ∂f/∂x=2x+3y\partial f/\partial x = 2x + 3y, ∂f/∂y=3x\partial f/\partial y = 3x 이므로

∇f(1,2)=(2+6,  3)=(8, 3)\nabla f(1,2) = (2 + 6,\; 3) = (8,\, 3)

입니다. h=(0.1, −0.05)h = (0.1,\, -0.05) 만큼 움직여 봅니다.

∇f⋅h=8(0.1)+3(−0.05)=0.8−0.15=0.65\nabla f \cdot h = 8(0.1) + 3(-0.05) = 0.8 - 0.15 = 0.65

실제 값은 f(1.1, 1.95)−f(1,2)=7.645−7=0.645f(1.1,\, 1.95) - f(1,2) = 7.645 - 7 = 0.645 이니 오차가 0.005입니다. 한 변수일 때와 같은 이야기이고, 달라진 것은 곱셈이 내적이 된 것뿐입니다.

방향도함수 — 어느 쪽으로 얼마나

이제 「어느 방향으로 가면 얼마나 변하는가」를 물을 수 있습니다. 길이가 1인 벡터 uu 를 하나 잡고 그 방향으로 tt 만큼 갑니다.

f(a+tu)−f(a)t≈∇f(a)⋅(tu)t=∇f(a)⋅u\frac{f(a + tu) - f(a)}{t} \approx \frac{\nabla f(a) \cdot (tu)}{t} = \nabla f(a) \cdot u

정의. 단위벡터 uu 방향의 방향도함수는 Duf(a)=∇f(a)⋅uD_u f(a) = \nabla f(a) \cdot u 다.

방향마다 미분을 새로 하지 않아도 됩니다. 그래디언트 하나를 구해 두면 어느 방향이든 내적 한 번으로 답이 나옵니다. 편도함수는 그중 축 방향인 특별한 경우입니다 — u=(1,0)u = (1,0) 이면 Duf=8D_u f = 8, u=(0,1)u = (0,1) 이면 3입니다. 대각선 방향 u=(1/2, 1/2)u = (1/\sqrt{2},\, 1/\sqrt{2}) 로 가면

Duf=8+32=7.778D_u f = \frac{8 + 3}{\sqrt{2}} = 7.778

입니다. 축 방향 둘 중 어느 쪽보다도 가파릅니다.

−∇f가 가장 가파른 내리막인 이유

1을 넘지 못하는 코사인

그러면 가장 가파른 방향은 어디인가. 방향도함수를 내적의 기하적 표현으로 바꿔 적습니다. uu 는 단위벡터이므로 ∥u∥=1\|u\| = 1 이고,

Duf=∇f⋅u=∥∇f∥ ∥u∥cos⁡θ=∥∇f∥cos⁡θD_u f = \nabla f \cdot u = \|\nabla f\|\,\|u\| \cos\theta = \|\nabla f\| \cos\theta

입니다. 여기서 θ\theta 는 uu 와 ∇f\nabla f 가 이루는 각입니다. 오른쪽에서 우리가 고를 수 있는 것은 cos⁡θ\cos\theta 하나뿐이고, 그 값은 코시-슈바르츠 부등식이 보장하는 대로 −1-1 과 1 사이입니다.

방향에 따른 변화율은 코사인 곡선을 그린다

  • cos⁡θ=1\cos\theta = 1, 즉 uu 가 ∇f\nabla f 와 같은 방향일 때 DufD_u f 가 최대이고 그 값은 ∥∇f∥\|\nabla f\| 다.
  • cos⁡θ=−1\cos\theta = -1, 즉 uu 가 반대 방향일 때 최소이고 그 값은 −∥∇f∥-\|\nabla f\| 다.
  • cos⁡θ=0\cos\theta = 0, 즉 수직일 때 변화율이 0이다.

우리 예에서 ∥∇f∥=64+9=8.544\|\nabla f\| = \sqrt{64 + 9} = 8.544 이므로, 어떤 방향으로 가도 변화율은 −8.544-8.544 와 8.5448.544 사이입니다. 그리고 가장 빨리 내려가는 길은 정확히 −∇f-\nabla f 방향 하나입니다.

p -= lr * p.grad\texttt{p -= lr * p.grad} 한 줄의 근거가 이것입니다. 「기울기의 반대」가 아니라 「그 점에서 손실을 가장 빨리 줄이는 유일한 방향」이고, 증명은 코사인이 1을 넘지 못한다는 사실 한 줄입니다.

단서를 둘 달아 둡니다. 첫째, «가장 가파른»은 그 점 근처에서만 참입니다. 선형근사가 성립하는 범위를 벗어나면 보장이 사라지고, 그래서 학습률이 필요합니다. 둘째, 이 결론은 길이를 유클리드 노름으로 쟀기 때문에 나왔습니다. 「길이 1인 걸음」의 뜻을 다르게 정하면 최급강하 방향도 달라지는데, 좌표마다 눈금을 달리 잡는 옵티마이저들이 하는 일이 바로 그것입니다.

등고선에 수직인 그래디언트

위의 세 번째 줄이 그림 하나를 곧바로 내놓습니다. 등고선은 ff 값이 같은 점들을 이은 선이므로, 그 위를 걸으면 값이 변하지 않습니다. 즉 등고선을 따라가는 방향 uu 에 대해 Duf=0D_u f = 0 이고, 따라서

∇f⋅u=0\nabla f \cdot u = 0

입니다. 내적이 0이면 수직이니, 그래디언트는 언제나 등고선에 수직입니다.

그래디언트는 등고선에 수직이고, 반대 방향이 골짜기로 향한다

f(x,y)=x2+2y2f(x,y) = x^2 + 2y^2 로 확인해 봅시다. 등고선은 타원이고 점 (2,1)(2,1) 에서 ∇f=(2x, 4y)=(4, 4)\nabla f = (2x,\, 4y) = (4,\, 4) 입니다. 그 점에서 타원의 접선 방향은 (1, −1)(1,\, -1) 인데

(4, 4)⋅(1, −1)=4−4=0(4,\, 4) \cdot (1,\, -1) = 4 - 4 = 0

이라 정말 수직입니다. 손실 지형을 등고선으로 그린 그림에서 갱신 화살표가 언제나 선을 가로질러 그려지는 이유가 이것입니다.

그래디언트의 크기가 뜻하는 것

여기까지 방향만 이야기했는데, ∇f\nabla f 는 벡터라 길이도 있습니다. 앞의 식이 그 길이의 뜻을 이미 말해 줍니다 — DufD_u f 의 최댓값이 ∥∇f∥\|\nabla f\| 이므로, 길이는 가장 가파른 방향에서의 변화율입니다. 우리 예에서 8.544라는 것은 「가장 좋은 방향으로 걸음 1을 옮기면 ff 가 8.544쯤 는다」는 뜻입니다.

이 길이가 갱신식에서 학습률과 곱해집니다. 걸음의 실제 길이가

∥θnew−θ∥=η ∥∇L(θ)∥\|\theta_{\text{new}} - \theta\| = \eta\,\|\nabla L(\theta)\|

이므로 학습률이 걸음을 다 정하는 것이 아닙니다. 같은 η\eta 라도 그래디언트가 크면 크게 움직이고 작으면 거의 안 움직입니다. 그래서 손실을 배치 합으로 재느냐 평균으로 재느냐만 바꿔도 — 그래디언트가 배치 크기 배만큼 달라지므로 — 학습률을 같이 손봐야 합니다.

여기서 두 가지 실무 장치가 나옵니다.

장치 크기에 하는 일
그래디언트 클리핑 ∥∇L∥\|\nabla L\| 이 상한을 넘으면 그 길이만 줄인다. 방향은 그대로 두고 걸음만 자른다
Adam 계열 좌표마다 최근 크기로 나눠, 크기 정보를 거의 버리고 방향 위주로 걷는다

둘 다 길이가 폭주하는 자리에서 걸음이 함께 폭주하는 것을 막는 장치입니다. 반대로 길이가 0에 가까워지면 걸음이 멈추는데, 그 자리가 극값일 수도 있고 평평한 고원일 수도 있습니다 — 길이만 봐서는 구별되지 않고, 그 구별에는 2차 정보가 필요합니다.

수십억 개를 벡터 하나로

θ와 같은 모양의 ∇L

이제 처음의 물음으로 돌아갑니다. 모델의 파라미터를 전부 한 줄로 늘어놓으면 그것은 벡터 θ∈Rd\theta \in \mathbb{R}^d 하나입니다. dd 가 70억이어도 벡터는 벡터입니다. 손실은 그 벡터를 받아 수 하나를 내놓는 함수 L(θ)L(\theta) 이고, 그래디언트는

∇L(θ)=(∂L∂θ1, …, ∂L∂θd)∈Rd\nabla L(\theta) = \left(\frac{\partial L}{\partial \theta_1},\, \dots,\, \frac{\partial L}{\partial \theta_d}\right) \in \mathbb{R}^d

로 θ\theta 와 같은 모양의 벡터입니다. p.grad가 p와 같은 shape인 것이 우연이 아니라 정의 그대로인 셈입니다. 그리고 갱신 한 줄은

θ←θ−η ∇L(θ)\theta \leftarrow \theta - \eta \,\nabla L(\theta)

입니다. 성분마다 「이 손잡이를 키우면 손실이 얼마나 느는가」를 재어 두고, 그 반대로 η\eta 배만큼 움직이는 것 — 그것이 전부입니다. 차원이 70억이라는 사실은 이 식 어디에도 나타나지 않습니다.

코드로 확인하기

import numpy as np

f = lambda v: v[0] ** 2 + 3 * v[0] * v[1]
a = np.array([1.0, 2.0])
g = np.array([2 * a[0] + 3 * a[1], 3 * a[0]])       # 손으로 구한 그래디언트
print(g)                                             # [8. 3.]

# ① 유한차분으로 검산 — 정의를 그대로 실행한다
eps = 1e-6
num = np.array([(f(a + eps * e) - f(a - eps * e)) / (2 * eps) for e in np.eye(2)])
print(num.round(6))                                  # [8. 3.]

# ② 선형근사의 오차는 h보다 빨리 준다
for h in [0.1, 0.01, 0.001]:
    d = np.array([h, -h / 2])
    print(h, round(float(f(a + d) - f(a) - g @ d), 9))
# 0.1 -0.005 / 0.01 -5e-05 / 0.001 -5e-07

# ③ 어느 방향으로 가도 ‖∇f‖ 를 넘지 못한다
u = g / np.linalg.norm(g)
print(round(float(g @ u), 4), round(float(g @ -u), 4))   # 8.544 -8.544
best = max(float(g @ np.array([np.cos(t), np.sin(t)]))
           for t in np.linspace(0, 2 * np.pi, 100000))
print(round(best, 4))                                    # 8.544  꼭 같다

# ④ 등고선 방향의 방향도함수는 0
gc = np.array([4.0, 4.0])                            # x² + 2y² 의 (2,1)에서
print(float(gc @ np.array([1.0, -1.0])))             # 0.0

③이 이 글의 증명을 무식하게 확인한 것입니다. 방향 10만 개를 훑어도 ∥∇f∥\|\nabla f\| 를 넘는 방향은 없습니다.

연습 문제

답은 문항을 눌러 펼칩니다.

연습 1 — 그래디언트 손계산

  1. f(x,y)=x2y+y3f(x,y) = x^2y + y^3 의 ∇f(2,1)\nabla f(2, 1) 을 구하세요.
    ∂f∂x=2xy=4\dfrac{\partial f}{\partial x} = 2xy = 4, ∂f∂y=x2+3y2=4+3=7\dfrac{\partial f}{\partial y} = x^2 + 3y^2 = 4 + 3 = 7 이므로 ∇f(2,1)=(4, 7)\nabla f(2,1) = (4,\, 7) 입니다.
  2. f(x,y)=3x−2y+xyf(x,y) = 3x - 2y + xy 의 ∇f(1,1)\nabla f(1, 1) 을 구하세요.
    ∂f∂x=3+y=4\dfrac{\partial f}{\partial x} = 3 + y = 4, ∂f∂y=−2+x=−1\dfrac{\partial f}{\partial y} = -2 + x = -1 이므로 (4, −1)(4,\, -1) 입니다. 두 성분의 부호가 다른 것은 한 손잡이는 키우면 값이 늘고 다른 손잡이는 줄인다는 뜻입니다.
  3. f(x,y,z)=xyzf(x,y,z) = xyz 의 ∇f(1,2,3)\nabla f(1, 2, 3) 을 구하세요.
    ∇f=(yz, xz, xy)\nabla f = (yz,\, xz,\, xy) 이므로 (6, 3, 2)(6,\, 3,\, 2) 입니다. 성분이 셋이어도 하는 일은 같습니다 — 나머지를 고정하고 하나만 흔듭니다.

연습 2 — 방향도함수

  1. 1번에서 얻은 ∇f(2,1)=(4,7)\nabla f(2,1) = (4,7) 에 대해 u=(35, 45)u = \left(\tfrac35,\, \tfrac45\right) 방향의 방향도함수를 구하세요.
    ∥u∥=1\|u\| = 1 인지부터 봅니다 — 0.36+0.64=10.36 + 0.64 = 1 로 단위벡터가 맞습니다. Duf=4×0.6+7×0.8=2.4+5.6=8.0D_u f = 4\times0.6 + 7\times0.8 = 2.4 + 5.6 = 8.0 입니다.
  2. 같은 점에서 방향도함수가 0이 되는 단위벡터를 모두 구하세요.
    (4,7)⋅u=0(4,7)\cdot u = 0 이어야 하므로 uu 는 (7,−4)(7,-4) 와 평행합니다. 길이를 1로 맞추면 ±(7,−4)65≈±(0.868, −0.496)\pm\dfrac{(7,-4)}{\sqrt{65}} \approx \pm(0.868,\, -0.496) 두 개입니다. 이 두 방향이 그 점을 지나는 등고선의 접선 방향입니다.

연습 3 — 등고선과 방향 고르기

  1. f(x,y)=x2+4y2f(x,y) = x^2 + 4y^2 의 점 (2,1)(2,1) 에서 ∇f\nabla f 를 구하고, 그 점을 지나는 등고선의 접선 방향을 하나 적으세요.
    ∇f=(2x, 8y)=(4, 8)\nabla f = (2x,\, 8y) = (4,\, 8) 입니다. 접선 방향은 이것과 수직이어야 하므로 예컨대 (2, −1)(2,\, -1) 이고, (4,8)⋅(2,−1)=8−8=0(4,8)\cdot(2,-1) = 8 - 8 = 0 으로 확인됩니다.
  2. 같은 점에서 −∇f-\nabla f 방향으로 걸으면 ff 의 변화율이 얼마이고, 그보다 더 빨리 줄이는 방향이 없는 이유는 무엇인가요.
    ∥∇f∥=16+64=80≈8.944\|\nabla f\| = \sqrt{16 + 64} = \sqrt{80} \approx 8.944 이므로 변화율은 −8.944-8.944 입니다. 어떤 단위벡터를 잡아도 Duf=∥∇f∥cos⁡θD_u f = \|\nabla f\|\cos\theta 이고 cos⁡θ≥−1\cos\theta \ge -1 이라 이보다 작아질 수 없습니다.

연습 4 — 코시-슈바르츠로 보이기

  1. ∥u∥=1\|u\| = 1 인 모든 uu 에 대해 ∣∇f⋅u∣≤∥∇f∥|\nabla f \cdot u| \le \|\nabla f\| 임을 보이고, 등호가 성립하는 uu 를 적으세요.
    코시-슈바르츠 부등식 ∣a⋅b∣≤∥a∥∥b∥|a\cdot b| \le \|a\|\|b\| 에 a=∇fa = \nabla f, b=ub = u 를 넣고 ∥u∥=1\|u\| = 1 을 쓰면 곧바로 ∣∇f⋅u∣≤∥∇f∥|\nabla f \cdot u| \le \|\nabla f\| 입니다. 등호는 두 벡터가 평행할 때, 즉 u=±∇f∥∇f∥u = \pm\dfrac{\nabla f}{\|\nabla f\|} 일 때 성립하고 ++ 쪽이 최대, −- 쪽이 최소입니다. ∇f=0\nabla f = 0 이면 모든 방향에서 변화율이 0이라 고를 방향이 없습니다.

정리

  • 도함수는 극한이자 «최선의 선형근사»의 기울기다. 오차가 hh 보다 빨리 0으로 가는 유일한 일차식이 접선이다.
  • 그래서 도함수는 민감도로 읽힌다 — 입력을 hh 만큼 흔들면 출력이 f′(a)hf'(a)h 만큼 흔들린다.
  • 그래디언트는 편도함수를 모은 벡터이고, 다변수 선형근사는 f(a+h)≈f(a)+∇f⋅hf(a+h) \approx f(a) + \nabla f \cdot h 라는 내적 하나로 접힌다.
  • 방향도함수 Duf=∇f⋅uD_u f = \nabla f \cdot u — 그래디언트 하나만 있으면 어느 방향이든 내적 한 번으로 답이 나온다. 편도함수는 축 방향인 특수한 경우다.
  • Duf=∥∇f∥cos⁡θD_u f = \|\nabla f\| \cos\theta 이므로 최대는 ∇f\nabla f 방향, 최소는 −∇f-\nabla f 방향이다. 코시-슈바르츠가 그 범위를 보장한다.
  • 「가장 가파르다」는 그 점 근처에서, 유클리드 노름으로 길이를 쟀을 때 참이다. 둘 다 조건이지 정리의 일부가 아니다.
  • 등고선 방향의 변화율은 0이므로 그래디언트는 등고선에 수직이다.
  • 꺾인 점에서는 접선이 하나로 안 정해진다. 그래프 아래를 지나는 직선들의 기울기 전체가 서브그래디언트이고, ReLU의 0에서는 [0,1][0,1] 이다. 지나가는 자리면 하나 골라 쓰면 되지만 최적해가 그 자리에 놓이면(L1 정규화) 다른 도구가 필요하다.
  • 그래디언트의 길이는 가장 가파른 방향의 변화율이고, 걸음의 실제 길이는 η∥∇L∥\eta\|\nabla L\| 이다. 클리핑은 그 길이만 자르고 방향은 남기며, Adam 계열은 길이 정보를 거의 버린다.
  • 파라미터가 수십억이어도 θ\theta 와 ∇L(θ)\nabla L(\theta) 는 같은 모양의 벡터 하나다. 차원은 식 어디에도 나타나지 않는다.

p -= lr * p.grad로 돌아갑니다. 저 한 줄은 이제 「기울기의 반대로 간다」가 아니라 「그 점에서 손실을 가장 빨리 줄이는 유일한 방향으로, 선형근사를 믿을 만한 만큼만 간다」로 읽힙니다. 두 개의 단서가 붙은 문장이고, 학습률과 옵티마이저를 둘러싼 논의는 대부분 그 단서 둘을 다루는 이야기입니다.

남은 물음은 「그 수십억 개의 편도함수를 실제로 어떻게 구하는가」입니다. 층을 지날 때마다 미분이 어떻게 곱해지는지 — 다음 글이 그 법칙 하나를 세웁니다.


읽어주셔서 감사합니다. 😊

LATEST

수학의 최신 글

수학2026.09.07

양자화 오차: 격자 사상, 오차 분산, 이상치 채널

실수를 2^b개 격자에 사상할 때 오차의 분산이 왜 Δ²/12인지 유도하고, 그것이 비트당 6.02dB라는 SNR로 번역되는 과정을 실측과 대조했습니다. 이상치 하나가 나머지 값의 유효 비트를 어떻게 먹는지, 그리고 int4에서 성능이 무너지는 지점을 오차 예산으로 미리 계산하는 법까지.

중급18 MIN
수학2026.09.07

수치적으로 안정한 계산 패턴 모음

최댓값 빼기, 로그 공간, log1p·expm1, 분산의 두 공식, 정규화의 ε, fp32 누산, 역행렬 대신 solve — 프레임워크가 몰래 해 주는 일곱 가지를 하나씩 꺼내 각각 어떤 고장을 막는지 직접 재 봤습니다. 수식을 그대로 옮긴 코드가 왜 라이브러리보다 나쁜지에 대한 목록입니다.

중급22 MIN
수학2026.09.07

부동소수점은 어디서 새는가: 반올림, 상쇄, 더하는 순서

0.1 + 0.2가 0.3이 아닌 이유부터 시작해 머신 엡실론을 유도하고, 같은 16비트인데 fp16과 bf16이 서로 다른 지점에서 터지는 이유, 비슷한 수를 뺄 때 유효자리가 사라지는 파괴적 상쇄, 그리고 1,000만 개를 순서만 바꿔 더했을 때 오차가 백만 배 갈리는 실험까지 직접 재 봤습니다.

중급23 MIN