개요
그동안 계속 미뤄둔 질문 두 개를 여기서 정리한다.
- 선형회귀보다 복잡한 관계는 어떻게 표현하나? → 신경망
- 공식으로 안 풀리면 값을 어떻게 정하나? → 경사 하강법과 역전파
첫 글에서 “학습 = 가설 공간에서 가장 덜 틀리는 함수 찾기”라고 했는데, 이번 글은 그 두 부분을 각각 넓히고 채우는 이야기다. 신경망이 가설 공간을 넓히고, 경사 하강법이 그 안에서 찾아간다.
1. 다시 출발점 — 모델은 값이 채워진 함수다
신경망으로 넘어가기 전에 관점을 하나 정리하고 간다.
단순 선형 모델은 이렇게 생겼다.
$$ y = \phi_0 + \phi_1 x $$
φ(파이)라는 새 기호가 나왔지만 겁먹을 것 없다. 앞 글에서 쓴 β와 같은 역할, 즉 우리가 조정해야 하는 값이다. 이렇게 함수의 모양은 정해져 있고 그 안의 숫자만 바꿔 쓰는 함수를 모수적(parametric) 함수라고 부른다.
여기서 두 가지가 확실해진다.
- 값이 정해지면 함수가 하나 정해진다 → 예측할 수 있다
- 훈련 데이터가 있으면 손실 함수를 정의할 수 있다 → 손실이 작아지도록 값을 조정하면 된다
그러니 학습은 결국 “손실을 가장 작게 만드는 값들을 찾는 문제”로 바뀐다. 첫 글에서 내린 정의를 한 번 더 좁힌 셈이다.
2. 얕은 신경망 — 직선을 여러 개 꺾어 붙이기
직선 모델의 한계는 명확하다. 직선밖에 못 그린다. 그럼 곡선은 어떻게 만드나?
신경망의 답이 재밌다. 직선을 여러 개 만들어서, 각각 한 번씩 꺾고, 다시 합친다.
구조는 세 층이다.
| 층 | 하는 일 |
|---|---|
| 입력층 | 데이터가 들어온다 |
| 은닉층 (hidden layer) | 여기서 꺾인다. 이게 핵심 |
| 출력층 | 결과가 나온다 |
은닉층이 한 겹뿐인 걸 얕은(shallow) 신경망이라고 부른다.
은닉 유닛이 하는 일
은닉층의 각 유닛(h₁, h₂, h₃)은 딱 두 단계를 거친다.
- 입력에 각자 다른 기울기와 높이를 적용한다 (= 직선 하나 만들기)
- 그 결과를 활성화 함수에 통과시킨다 (= 꺾기)
가장 많이 쓰는 활성화 함수가 ReLU다. 하는 일은 어이없을 만큼 단순하다.
$$ \text{ReLU}(z) = \max(0, z) $$
| 입력 $z$ | 출력 |
|---|---|
| $z \lt 0$ | $0$ |
| $z \geq 0$ | $z$ 그대로 |
음수는 전부 0으로 눌러버리고, 양수는 손대지 않는다. 그림에서 보듯 딱 한 번 꺾인 선이 된다.
왜 꺾어야 하나
이게 핵심 질문이다. 답은 이렇다.
꺾지 않으면, 아무리 층을 쌓아도 결국 직선 하나다.
말로만 들으면 잘 안 와닿아서, 숫자로 직접 따라가 봤다.
① 활성화 함수 없이 두 층을 쌓아보면
1층이 이렇게 계산한다고 하자.
$$ \text{1층 출력} = 2x + 1 $$
이 값을 그대로 2층에 넣는다. 2층도 곱하고 더하는 일만 한다.
$$ \text{2층 출력} = 3 \times (\text{1층 출력}) - 4 = 3 \times (2x + 1) - 4 = 6x + 3 - 4 = 6x - 1 $$
결과가 $6x - 1$이다. 여전히 직선이다.
숫자만 2와 3에서 6으로 바뀌었을 뿐, 모양은 그대로다. 이건 층을 100개 쌓아도 똑같다. 곱하고 더하는 일만 반복하면 아무리 겹쳐도 결국 하나의 직선으로 합쳐진다.
그림 위쪽이 그 상황이다. 왼쪽도 직선, 오른쪽도 직선. 층을 하나 더 쌓았는데 표현력이 조금도 늘지 않았다.
다시 말해 활성화 함수 없는 신경망은 그냥 복잡하게 쓴 선형회귀다. 파라미터만 잔뜩 늘려놓고 할 수 있는 일은 그대로인 셈이다.
② ReLU를 넣으면 무슨 일이 생기나
ReLU가 하는 일은 음수를 0으로 눌러버리는 것 하나뿐이다. 그런데 이 단순한 동작에 결정적인 성질이 하나 있다.
꺾이는 지점을 기준으로, 왼쪽과 오른쪽의 기울기가 달라진다.
$\text{ReLU}(x)$를 보자.
| 구간 | 값 | 기울기 |
|---|---|---|
| $x \lt 0$ | 0 | 0 (평평하다) |
| $x \geq 0$ | $x$ | 1 (올라간다) |
한 함수 안에 기울기가 두 개다. 직선은 어디를 가도 기울기가 하나인데, ReLU는 x = 0을 경계로 성격이 바뀐다. 이게 비선형성의 정체다.
그리고 여기가 진짜 재밌는 부분인데, 꺾이는 위치를 옮길 수 있다.
| 함수 | 꺾이는 지점 |
|---|---|
| $\text{ReLU}(x - 1)$ | $x = 1$ |
| $\text{ReLU}(x - 2)$ | $x = 2$ |
안쪽에서 빼주는 숫자만 바꾸면 꺾이는 지점이 이동한다. 그리고 이 숫자는 학습으로 정해지는 값이다. 즉 모델이 “어디서 꺾을지”를 스스로 배운다.
③ 꺾인 직선 세 개로 봉우리 만들기
이제 꺾인 조각들을 조합해보자. 그림 아래쪽이 이 계산이다.
$$ y = 1 \times \text{ReLU}(x) - 2 \times \text{ReLU}(x-1) + 1 \times \text{ReLU}(x-2) $$
구간별로 어떤 조각이 살아 있는지 따져보면 기울기가 이렇게 바뀐다.
| 구간 | 살아 있는 조각 | 기울기 | 모양 |
|---|---|---|---|
| x < 0 | 없음 | 0 | 평지 |
| 0 ≤ x < 1 | 첫 번째 | +1 | 오르막 |
| 1 ≤ x < 2 | 첫 번째, 두 번째 | 1 − 2 = −1 | 내리막 |
| x ≥ 2 | 셋 다 | 1 − 2 + 1 = 0 | 평지 |
실제 값을 몇 개 넣어보면 이렇게 나온다.
| x | 0 | 0.5 | 1 | 1.5 | 2 | 3 |
|---|---|---|---|---|---|---|
| y | 0 | 0.5 | 1 | 0.5 | 0 | 0 |
올라갔다가 내려오는 봉우리가 만들어졌다. 직선을 곱하고 더하기만 했는데, 직선으로는 절대 못 만드는 모양이 나온 것이다.
앞에서 두 층을 쌓아도 $6x - 1$ 하나였던 것과 비교해보면 차이가 확실하다. 중간에 한 번 꺾어줬을 뿐인데 표현할 수 있는 모양이 완전히 달라졌다.
④ 그래서 유닛을 늘리면
봉우리 하나를 만드는 데 조각 세 개가 들었다. 조각을 더 넣으면 꺾이는 지점이 더 늘어난다.
- 조각 10개 → 꺾인 곳 10개짜리 선
- 조각 100개 → 촘촘하게 꺾여서 곡선처럼 보인다
이게 뒤에 나올 “유닛을 늘리면 어떤 함수든 근사할 수 있다”의 실체다. 마법이 아니라 꺾인 직선을 촘촘히 이어 붙이는 것이다.
더 쉬운 비유로. 자를 대고 그린 직선만으로는 동그라미를 못 그린다. 그런데 짧은 직선을 조금씩 각도를 바꿔가며 이어 붙이면 동그라미와 거의 구별이 안 된다. 각을 촘촘하게 할수록 더 매끄러워진다.
지도 앱의 도로도 사실은 짧은 직선의 연속이다. 확대하면 각져 보이지만 평소 축척에서는 곡선으로 보인다.
ReLU 말고 다른 것들
활성화 함수는 ReLU 하나가 아니다. 꺾는 방식이 조금씩 다른 함수들이 여럿 있다.
| 함수 | 성격 | 좋은 점 | 아쉬운 점 |
|---|---|---|---|
| Sigmoid | 0 ~ 1로 눌러 담는다 | 확률·게이트를 만들기 좋다 | 양 끝이 납작해 기울기가 죽는다 |
| tanh | −1 ~ 1로 눌러 담는다 | 중심이 0이라 값이 한쪽으로 안 쏠린다 | 역시 양 끝에서 기울기가 죽는다 |
| ReLU | 음수를 0으로 자른다 | 계산이 싸고 양수 쪽 기울기가 항상 1 | 죽은 뉴런 문제가 생긴다 |
| Leaky ReLU | 음수도 살짝 통과시킨다 | 죽은 뉴런을 막는다 | 기울기 값을 하나 더 정해야 한다 |
| GELU | 부드럽게 꺾는다 | 학습이 안정적. Transformer 계열의 기본 | 계산이 ReLU보다 조금 비싸다 |
왜 시그모이드에서 ReLU로 넘어갔나
초기 신경망은 은닉층에도 시그모이드를 썼다. 그런데 시그모이드 그래프를 보면 양 끝이 거의 평평하다.
평평하다는 건 기울기가 거의 0이라는 뜻이다. 그리고 역전파는 기울기를 곱해가며 거슬러 올라간다. 앞에서 본 기울기 소실이 여기서 그대로 터진다.
- 시그모이드의 최대 기울기는 0.25다
- 층을 거칠 때마다 최소 4분의 1로 줄어든다
- 10층이면 0.25를 10번 곱한 만큼 — 100만분의 1 수준
ReLU는 이 문제가 없다. 양수 구간의 기울기가 정확히 1이라, 아무리 거슬러 올라가도 그 구간에서는 신호가 줄어들지 않는다. 게다가 계산도 “0보다 큰가?”를 보는 게 전부라 비교 한 번이면 끝난다.
시그모이드가 나쁜 함수라는 건 아니다. 자리가 다를 뿐이다. 확률을 만들거나 게이트를 여닫는 자리에서는 여전히 시그모이드가 맞다. 다만 층을 깊게 쌓는 은닉층 자리에는 맞지 않았던 것이다.
죽은 ReLU와 Leaky ReLU
ReLU에도 약점이 있다. 음수 구간의 기울기가 0이라는 점이다.
어떤 유닛의 입력이 계속 음수 쪽으로만 들어오면 어떻게 될까?
- 출력이 항상 0이다
- 기울기도 항상 0이다
- 기울기가 0이면 값이 갱신되지 않는다
- 갱신되지 않으니 영영 음수 구간에 머문다
이걸 죽은 ReLU(dying ReLU) 문제라고 부른다. 유닛 하나가 통째로 아무 일도 안 하는 상태가 되어버린다.
학습률을 너무 크게 잡았을 때 특히 잘 생긴다. 한 번 크게 밀려서 음수 쪽으로 넘어가면 돌아올 방법이 없다. 되돌리려면 기울기가 있어야 하는데, 그 기울기가 0이기 때문이다.
Leaky ReLU는 여기서 딱 한 군데만 고친다.
$$ \text{Leaky ReLU}(z) = \max(0.01z, z) $$
| 입력 $z$ | 출력 |
|---|---|
| $z \lt 0$ | $0.01 \times z$ — 0이 아니라 아주 작은 값 |
| $z \geq 0$ | $z$ 그대로 |
음수 쪽 기울기를 0이 아니라 0.01 정도로 살짝 남겨둔다. 그러면 음수 구간에 빠져도 아주 느리게나마 되돌아올 길이 생긴다.
문을 완전히 잠그는 대신 아주 살짝 열어두는 것과 같다. 평소엔 거의 안 지나가지만, 되돌아 나와야 할 때 나올 구멍은 있다.
기울기 값(0.01)을 사람이 정해야 하는 게 부담인데, 그 값마저 학습으로 정하게 만든 변형도 있다.
GELU — 요즘의 기본값
Transformer 계열에서는 GELU를 많이 쓴다. ReLU가 0에서 각지게 꺾이는 반면, GELU는 그 근처를 부드럽게 넘어간다.
그림에서 보면 차이가 확실하다. ReLU는 0에서 뾰족하게 꺾이는데 GELU는 완만하게 휜다. 그리고 0 근처에서 살짝 음수로 내려갔다 올라온다.
각진 지점은 기울기가 갑자기 바뀌는 곳이라 학습이 흔들리기 쉬운데, 부드럽게 넘어가면 그런 일이 줄어든다. 계산은 ReLU보다 비싸지만, 큰 모델에서는 그만한 값을 한다고 알려져 있다.
정리하면 이렇다. 하는 일은 다 똑같다 — 직선을 꺾어서 곡선을 만들 수 있게 하는 것. 어디서 어떻게 꺾느냐의 차이일 뿐이고, 그 미묘한 차이가 학습 안정성을 가른다.
여기서 지난 글과 이어지는 지점이 하나 있다. 로지스틱회귀에서 쓴 시그모이드도 활성화 함수의 일종이다. 그러니 로지스틱회귀는 은닉층 없이 활성화 함수만 한 번 씌운, 가장 작은 신경망이라고 볼 수 있다.
3. 유닛을 늘리면 어디까지 되나
은닉 유닛 하나가 꺾인 선 하나를 만든다면, 유닛을 늘릴수록 꺾을 수 있는 횟수가 늘어난다.
- 유닛 3개 → 구간 몇 개짜리 거친 근사
- 유닛 10개 → 꽤 매끄러워짐
- 유닛 20개 → 원본과 구별이 잘 안 됨
그래서 이런 정리가 나온다.
보편적 근사 정리(Universal Approximation Theorem) 은닉 유닛을 충분히 많이 두면, 얕은 신경망은 임의의 연속 함수를 원하는 정밀도로 근사할 수 있다.
처음 봤을 때 “오 만능인걸?” 이라 생각했다. 은닉층 한 겹만으로 이론상 뭐든 표현할 수 있다는 말이니까.
그런데 여기엔 함정이 있다. “충분히 많이”가 얼마인지는 안 알려준다. 이론적으로 가능한 것과 현실적으로 만들 수 있는 건 다르다.
4. 깊은 신경망 — 넓히기보다 쌓기
얕은 신경망으로 뭐든 된다면, 왜 딥러닝은 층을 깊게 쌓을까?
효율 때문이다. 강의에서 본 비교가 인상적이었다.
| 구조 | 값의 개수 | 표현할 수 있는 구간 수 |
|---|---|---|
| 은닉층 2겹 (각 3유닛) | 20개 | 최대 16개 |
| 은닉층 1겹 (6유닛) | 19개 | 최대 7개 |
비슷한 개수의 값을 쓰는데 표현력은 2배 이상 차이가 난다. 옆으로 늘리는 것보다 위로 쌓는 게 훨씬 남는 장사인 것이다.
이유는 접기(folding)로 설명된다. 첫 층이 만든 꺾인 결과를 두 번째 층이 또 꺾는다. 종이를 한 번 접고 그 상태에서 또 접으면, 펼쳤을 때 접힌 자국이 2배가 아니라 훨씬 많이 생기는 것과 같다.
층을 쌓는 건 덧셈이 아니라 곱셈이다. 이 한 문장이 “왜 깊게(deep) 가는가”에 대한 답이었다.
표기가 갑자기 깔끔해지는 지점
층이 깊어지면 식이 걷잡을 수 없이 길어진다. 그래서 행렬과 벡터로 묶어서 쓴다.
$$ h_1 = a(b_0 + W_0 x) $$
$$ h_2 = a(b_1 + W_1 h_1) $$
$$ y = b_2 + W_2 h_2 $$
($b$ 는 편향 $\beta$, $W$ 는 가중치 행렬 $\Omega$, $a$ 는 활성화 함수)
| 기호 | 뜻 |
|---|---|
| Ω (오메가) | 가중치 행렬 — 연결선마다 붙은 값들을 모아놓은 표 |
| β (베타) | 편향 벡터 — 각 유닛에 더해지는 기본값 |
| a( ) | 활성화 함수 (ReLU 등) |
말로 옮기면 “입력에 가중치를 곱하고, 편향을 더하고, 꺾는다. 그걸 층마다 반복한다.” 층이 100개여도 이 문장은 그대로다.
여기가 개인적으로 반가웠던 부분이다. 결국 신경망의 정체는 행렬 곱셈의 반복이다.
얼마 전 NumPy를 정리하면서 “반복문 대신 배열 통째로 계산하는 게 왜 훨씬 빠른가”를 봤는데, 그 벡터화가 여기서 그대로 이어진다. 신경망 한 층의 계산은 파이썬 for문으로 유닛을 하나씩 도는 게 아니라, 행렬 곱 한 방이다. 그리고 행렬 곱은 각 칸을 서로 상관없이 동시에 계산할 수 있다.
GPU가 딥러닝에 쓰이는 이유가 여기서 설명된다. 순서를 지켜야 하는 일은 CPU가 빠르지만, 서로 독립인 계산 수천 개를 한꺼번에 하는 데는 GPU가 압도적이다. 신경망 계산이 정확히 그 모양이다.
5. 이제 진짜 문제 — 값은 어떻게 정하나
구조는 알았다. 그런데 그 안에 들어갈 수많은 값(가중치)을 어떻게 정하나?
선형회귀 편에서 봤듯 단순선형회귀는 공식으로 한 번에 풀렸다. 하지만 신경망은 공식이 없다. 로지스틱회귀부터 이미 없었다.
그래서 다른 전략을 쓴다. 한 번에 못 찾으면, 조금씩 나은 쪽으로 옮겨가면 된다.
준비운동 — 미분은 기울기다
경사 하강법을 이해하려면 미분이 필요한데, 알아야 할 건 딱 하나다.
미분값 = 그 지점에서의 기울기 = “지금 어느 쪽으로 얼마나 가파른가”
포물선 위에서 미분값을 보면 이렇게 읽힌다.
| 위치 | 미분값 | 뜻 |
|---|---|---|
| 최저점 오른쪽 | 양수 | 오른쪽으로 가면 올라간다 |
| 최저점 왼쪽 | 음수 | 오른쪽으로 가면 내려간다 |
| 최저점 | 0 | 어느 쪽으로도 평평하다 |
그리고 기울기의 절댓값이 클수록 더 가파르다.
여기서 결론이 자동으로 나온다. 미분값의 반대 방향으로 가면 내려간다.
6. 경사 하강법
경사 하강법(Gradient Descent) = 손실 함수의 기울기를 구하고, 그 반대 방향으로 값을 조금씩 옮기는 것
절차는 두 단계의 반복이다.
- 현재 위치에서 손실의 기울기를 계산한다
- 기울기의 반대 방향으로 한 걸음 옮긴다
이걸 손실이 더는 안 줄어들 때까지 반복한다.
더 쉬운 비유로. 안개 낀 산에서 눈을 감고 내려오는 것과 같다. 앞이 안 보이니 발밑을 더듬어 어느 쪽이 내리막인지만 알아낸 다음, 그쪽으로 한 걸음 옮긴다. 이걸 계속 반복하면 언젠가 골짜기에 닿는다.
학습률 — 보폭을 정하는 값
한 걸음을 얼마나 크게 뗄지 정하는 값이 학습률(learning rate)이다. 이게 생각보다 훨씬 까다롭다.
| 학습률 | 결과 |
|---|---|
| 너무 크면 | 최저점을 지나쳐 반대편으로 튕긴다. 심하면 발산 |
| 너무 작으면 | 조금씩만 움직여서 끝없이 오래 걸린다 |
위 그림의 오른쪽이 학습률이 큰 경우다. 골짜기를 넘어 반대편 벽으로 튕기고, 다시 더 세게 튕겨 나간다. 학습이 되기는커녕 점점 나빠진다.
산을 내려오는데 한 걸음이 100m라면, 골짜기를 훌쩍 넘어 건너편 산중턱에 착지한다. 반대로 한 걸음이 1cm라면 해가 질 때까지 못 내려온다.
학습률은 사람이 정해줘야 하는 값이라, 딥러닝에서 가장 손이 많이 가는 설정 중 하나다.
7. 골짜기가 하나가 아닐 때
경사 하강법에는 치명적인 약점이 있다. 손실 함수의 모양에 따라 난이도가 완전히 달라진다.
| Convex (볼록) | Non-convex | |
|---|---|---|
| 모양 | U자처럼 아래로 볼록 | 봉우리와 웅덩이가 섞임 |
| 최저점 | 하나뿐 | 여러 개 |
| 결과 | 어디서 출발해도 같은 답 | 출발점에 따라 다른 답 |
Convex 문제는 마음이 편하다. 내려가다 보면 무조건 정답에 도착한다.
문제는 신경망의 손실 함수는 대부분 non-convex라는 것이다. 웅덩이(지역 최솟값)에 빠지면, 주변이 다 오르막이라 더 갈 데가 없어 보인다. 실제로는 저 멀리 진짜 골짜기가 있는데도.
이 개념, 교차검증 편의 K-means에서 이미 만났다. 초기값에 따라 결과가 달라진다던 그 문제와 정확히 같다. 이름만 다르지 구조가 똑같다.
8. 확률적 경사 하강법(SGD)
지금까지 설명한 방식에는 두 가지 문제가 있다.
- 매 걸음마다 전체 데이터의 기울기를 계산한다 → 데이터가 100만 개면 한 걸음에 100만 번 계산
- 웅덩이에 빠지면 못 나온다
해결책은 어이없을 만큼 간단하다.
전체 데이터를 다 보지 말고, 무작위로 뽑은 일부(미니배치)만 보고 걸음을 정하자.
이게 확률적 경사 하강법(SGD)이다.
두 가지가 동시에 해결된다
① 계산이 싸진다
100만 개 대신 32개만 보고 한 걸음을 정한다. 한 걸음의 비용이 확 줄어서, 같은 시간에 훨씬 많이 걸을 수 있다.
② 웅덩이에서 빠져나온다
이게 훨씬 흥미로운 부분이다. 일부만 보고 정한 방향은 정확하지 않다. 뽑힌 데이터에 따라 방향이 조금씩 흔들린다.
그런데 이 흔들림이 오히려 도움이 된다. 웅덩이에 빠져도 흔들리다 보면 툭 튀어나올 수 있기 때문이다.
눈 감고 산을 내려오는데 가끔 발을 헛디딘다고 하자. 평소엔 손해지만, 작은 웅덩이에 빠졌을 때는 그 헛디딤 덕분에 밖으로 나오게 된다.
부정확함이 약점이 아니라 무기가 되는 구조라는 게 인상적이었다. 정확한 방향으로만 가면 갇히고, 대충 가면 빠져나온다.
정리하면
| 경사 하강법 (전체) | SGD (일부) | |
|---|---|---|
| 한 걸음에 쓰는 데이터 | 전부 | 무작위 일부 |
| 한 걸음 비용 | 비쌈 | 쌈 |
| 이동 경로 | 매끄러움 | 지그재그 |
| 웅덩이 탈출 | 어려움 | 상대적으로 쉬움 |
주의 — SGD가 항상 더 빠른 건 아니다. Convex 문제처럼 모양이 좋은 경우엔 전체를 보는 쪽이 더 빨리 수렴하기도 한다. “SGD가 더 빠르다”는 말은 조건부다.
9. 옵티마이저 — Adam은 뭘 더 해주나
여기까지 오면 걸음을 정하는 규칙이 하나뿐이었다. 기울기 반대 방향으로 학습률만큼 간다. 그런데 이 단순한 규칙에는 아직 불편한 점이 남아 있다.
- 학습률을 사람이 정해야 한다. 크면 튕기고 작으면 하염없이 걸린다
- 모든 값에 같은 보폭을 쓴다. 어떤 값은 크게 움직여야 하고 어떤 값은 미세 조정만 필요한데도
- 골짜기가 길쭉하면 지그재그가 된다. 좁은 쪽 벽을 계속 왔다 갔다 하느라 정작 목표 쪽으로는 조금씩만 나아간다
왼쪽 그림이 마지막 문제다. 좁은 방향은 기울기가 가파르니 크게 튕기고, 넓은 방향은 완만하니 찔끔 움직인다. 가야 할 방향으로는 거의 못 가면서 옆으로만 요동친다.
이 걸음 규칙을 개선한 것들을 옵티마이저(optimizer)라고 부른다. 개선 아이디어는 크게 두 갈래다.
① 모멘텀 — 관성을 준다
이번 걸음을 이번 기울기만 보고 정하지 않는다. 직전까지 가던 방향을 일정 비율로 이어받는다.
$$ \text{이번 이동} = (\text{직전에 가던 방향} \times \text{관성}) + (\text{이번 기울기} \times \text{학습률}) $$
효과가 두 방향으로 나온다.
- 같은 방향이 반복되면 점점 빨라진다 — 계속 내리막이면 가속이 붙는다
- 왔다 갔다 하는 성분은 서로 상쇄된다 — 지그재그가 저절로 줄어든다
더 쉬운 비유로. 매 걸음 멈춰서 발밑을 확인하고 다시 딛는 게 기본 경사 하강법이라면, 모멘텀은 비탈에서 공을 굴리는 것에 가깝다. 공은 이미 가던 방향으로 계속 가려 하기 때문에, 작은 웅덩이 정도는 관성으로 그냥 넘어간다.
② 적응적 학습률 — 값마다 보폭을 다르게
모든 값에 같은 보폭을 쓰는 대신, 각 값이 그동안 얼마나 크게 움직였는지를 기억해두고 보폭을 조절한다.
- 최근에 기울기가 크게 나온 값 → 이미 많이 흔들렸으니 보폭을 줄인다
- 최근에 거의 안 움직인 값 → 아직 자리를 못 잡았으니 보폭을 키운다
이렇게 하면 사람이 학습률 하나만 대충 정해줘도, 세부 보폭은 알아서 맞춰진다.
Adam = 둘을 합친 것
Adam(Adaptive Moment Estimation) = 모멘텀 + 적응적 학습률
지금 딥러닝에서 기본값처럼 쓰이는 옵티마이저다. 흔히 쓰는 설정은 이렇다.
| 값 | 흔히 쓰는 기본값 | 역할 |
|---|---|---|
| 학습률 | 0.001 | 전체 보폭의 기준 |
| β₁ | 0.9 | 관성을 얼마나 유지할지 |
| β₂ | 0.999 | 보폭 조절에 과거를 얼마나 반영할지 |
β 값들은 특별한 이유가 없으면 거의 건드리지 않는다. 실제로 조정하는 건 대개 학습률 하나다.
| 걸음 정하는 방식 | 특징 | |
|---|---|---|
| 기본 경사 하강법 | 기울기 × 고정 학습률 | 단순. 학습률에 아주 민감 |
| 모멘텀 추가 |
|
지그재그 감소, 가속 |
| Adam |
|
손이 덜 간다. 초기 수렴이 빠름 |
그렇다고 만능은 아니다
Adam이 기본값처럼 쓰이니 “무조건 Adam”으로 생각하기 쉬운데, 그렇지는 않다고 한다.
- 초반에 빠르게 내려가는 건 대체로 Adam이 낫다
- 하지만 잘 조정한 모멘텀 방식이 최종 일반화 성능에서 더 좋게 나오는 경우도 많다
그래서 실무에서는 Adam으로 빠르게 시작하고, 성능을 더 짜내야 할 때 다른 방식으로 바꿔 비교하는 식으로 쓴다. 여기서도 결국 같은 결론이다. 기본값은 출발점이지 정답이 아니다.
10. 잘 맞히는 것과 잘 외우는 것은 다르다 — 규제
모델을 크게 만들면 훈련 데이터는 더 잘 맞힐 수 있지만, 잡음까지 외워 오버피팅되기 쉽다. 규제는 모델이 너무 복잡한 답으로 달아나지 못하도록 제약을 거는 방법이다.
| 방법 | 하는 일 | 기억할 점 |
|---|---|---|
| L1 규제 | 필요 없는 가중치를 정확히 0으로 보내려 한다 | Feature 선택 같은 효과가 생길 수 있다 |
| L2 규제 | 큰 가중치에 벌점을 주어 값이 골고루 작아지게 한다 | 딥러닝에서는 Weight Decay라는 이름으로 자주 쓴다 |
| Dropout | 훈련할 때 뉴런 일부를 무작위로 잠시 끈다 | 특정 뉴런끼리만 의존하는 것을 막는다 |
Dropout은 매번 조금 다른 팀으로 문제를 풀게 하는 것과 비슷하다. 항상 같은 한 명에게 답을 맡길 수 없으니 여러 뉴런이 쓸 만한 특징을 나누어 배우게 된다.
여기서 아주 중요한 차이가 있다.
- 훈련할 때는 일부 뉴런을 무작위로 끈다
- 검증·추론할 때는 모든 뉴런을 사용한다
따라서 모델이 지금 훈련 중인지 추론 중인지 알려주는 상태 전환을 빼먹으면 결과가 흔들린다. Dropout을 많이 넣는다고 무조건 좋아지는 것도 아니다. 너무 많이 끄면 모델이 필요한 규칙조차 배우지 못해 언더피팅된다.
규제의 목적은 훈련 점수를 가장 높이는 것이 아니라, 훈련 점수를 조금 양보하고 처음 보는 데이터의 점수를 높이는 것이다.
11. 역전파 — 기울기를 실제로 구하는 방법
마지막 조각이 남았다. 경사 하강법은 “기울기를 구해서 반대로 간다”인데, 층이 여러 겹인 신경망에서 그 기울기를 어떻게 구하나?
문제는 이렇다. 첫 번째 층의 가중치 하나를 바꾸면, 그 영향이 2층 → 3층 → 출력 → 손실까지 줄줄이 퍼진다. “이 값 하나가 최종 손실에 얼마나 영향을 주는가”를 어떻게 계산하나?
연쇄법칙
답은 연쇄법칙(chain rule)이다. 핵심 아이디어는 한 줄로 요약된다.
전체 변화율 = 바깥쪽 변화율 × 안쪽 변화율
말로 풀면 이렇다.
톱니바퀴 A가 B를 돌리고, B가 C를 돌린다고 하자. A를 1만큼 돌리면 C는 얼마나 도나? → (A가 B를 돌리는 비율) × (B가 C를 돌리는 비율)
곱하기만 하면 된다. 층이 몇 겹이든 마찬가지다. 각 단계의 변화율을 구해서 쭉 곱하면 된다.
그래서 거꾸로 간다
연쇄법칙을 쓰려면 뒤쪽부터 계산하는 게 훨씬 효율적이다. 그래서 학습 한 걸음은 왕복 구조가 된다.
| 방향 | 이름 | 하는 일 |
|---|---|---|
| 앞으로 → | 순전파(forward) | 입력을 층마다 통과시켜 예측을 만들고, 정답과 비교해 손실을 구한다 |
| 뒤로 ← | 역전파(backward) | 손실에서 출발해 거꾸로 돌아오며, 각 값이 손실에 준 영향(기울기)을 계산한다 |
그리고 구한 기울기로 값들을 조금 옮긴다. 이 왕복 한 번이 학습의 한 걸음이다.
더 쉬운 비유로. 여러 사람이 릴레이로 그림을 이어 그렸는데 결과가 이상하다고 하자. 순전파는 앞사람부터 순서대로 그려나가는 것, 역전파는 마지막 사람부터 거꾸로 물어보며 “여기서 얼마나 어긋났지?”를 따져 올라가는 것이다. 그래야 누구 탓인지 정확히 알 수 있다.
“역전파”라는 이름이 거창해서 뭔가 대단한 게 있나 했는데, 결국 미분의 연쇄법칙을 뒤에서부터 순서대로 적용하는 것이었다. 개념 자체는 오히려 단순하다.
전체 그림 — 학습 한 걸음
여기까지 배운 걸 하나로 이으면 이렇게 된다.
- 데이터에서 미니배치를 뽑는다 (SGD)
- 순전파 — 예측을 만들고 손실을 계산한다
- 역전파 — 각 값이 손실에 준 영향(기울기)을 구한다
- 옵티마이저가 그 기울기로 값을 옮긴다 (Adam이면 관성과 보폭 조절까지 함께)
- 1번으로 돌아간다
그리고 이 루프를 데이터 전체에 대해 한 바퀴 돈 것을 1 에폭(epoch)이라고 부른다.
첫 글에서 그렸던 데이터 → 모델 → 학습 → 평가 루프의 “학습” 칸 안을 확대하면 정확히 이게 들어 있다. 결국 처음 그림으로 돌아온 셈이다.
이미지로 넓히기 — CNN
지금까지 본 MLP는 표 데이터처럼 한 줄로 정리된 입력에는 잘 맞는다. 그러나 이미지를 한 줄로 펼치면 옆 픽셀끼리 만드는 선·모서리·모양 정보가 흐려지고, 모든 입력과 모든 뉴런을 연결하느라 가중치 수도 폭발한다.
CNN은 이 문제를 세 가지 생각으로 줄인다.
- 작은 필터로 가까운 영역만 본다
- 같은 필터를 이미지 전체에 반복해서 쓴다
- 아래층의 선·색을 위층에서 눈·코·얼굴 같은 큰 특징으로 조합한다
같은 필터를 공유하므로 가중치가 크게 줄고, 찾는 특징이 사진의 왼쪽에 있든 오른쪽에 있든 같은 방식으로 잡아낼 수 있다.
합성곱에서 실제로 움직이는 것
- 커널은 한 채널을 훑는 작은 2차원 가중치 행렬이다
- 필터는 입력의 모든 채널을 함께 처리하는 커널 묶음이다
- 특징맵은 필터가 어느 위치에서 강하게 반응했는지 기록한 결과다
- Padding은 가장자리에 값을 덧붙여 크기와 가장자리 정보를 지킨다
- Stride는 필터가 몇 칸씩 움직일지 정한다. 커질수록 출력은 작아지고 계산은 줄어든다
입력 한 변의 길이를 I, 필터 크기를 F, Padding을 P, Stride를 S라고 하면 출력 한 변의 길이는 다음처럼 정해진다.
출력 크기 = 내림((I - F + 2P) ÷ S) + 1
예를 들어 7칸 입력에 3칸 필터, Padding 0, Stride 1을 쓰면 출력은 5칸이다. CNN을 구현할 때 가장 흔한 오류가 이 크기를 잘못 계산해 다음 층과 모양이 맞지 않는 것이다.
한 장이 답으로 바뀌는 순서
| 단계 | 하는 일 |
|---|---|
| Convolution | 선·색·무늬 같은 특징을 찾아 특징맵을 만든다 |
| ReLU | 비선형성을 넣고 음수 반응을 0으로 만든다 |
| Pooling 또는 큰 Stride | 공간 크기와 계산량을 줄인다 |
| 반복 | 작은 특징을 더 큰 모양으로 조합한다 |
| Flatten 또는 GAP | 3차원 특징을 분류기가 읽을 수 있게 요약한다 |
| Fully Connected·Softmax | 특징을 조합해 범주별 점수를 만든다 |
예전에는 특징맵을 전부 Flatten해서 큰 Fully Connected Layer에 넣었지만, 가중치가 너무 많아지는 문제가 있었다. 최근 구조에서는 채널마다 공간 평균 하나만 남기는 GAP, Global Average Pooling을 자주 쓴다. 가중치 수와 과적합 위험을 함께 줄일 수 있다.
CNN도 새로운 학습법을 쓰는 것은 아니다. 순전파로 예측하고, 교차 엔트로피로 틀린 정도를 재고, 역전파와 옵티마이저로 필터 안의 값까지 함께 고친다. 즉 앞에서 배운 학습 루프는 그대로이고, 이미지 구조에 맞는 층만 바뀐다.
구조가 발전한 방향
- AlexNet은 ReLU·Dropout·GPU 학습으로 깊은 CNN의 가능성을 보여줬다
- VGGNet은 작은 3×3 필터를 반복해 단순하고 깊게 쌓았다
- GoogLeNet은 여러 크기의 필터를 병렬로 사용했다
- ResNet은 입력을 뒤쪽에 바로 더하는 잔차 연결로 아주 깊은 망의 학습을 도왔다
- ViT는 이미지를 작은 Patch로 나누어 단어처럼 보고 Self-Attention을 적용했다
이 이름들을 외우는 것보다 파라미터를 줄이는가, 기울기를 잘 전달하는가, 가까운 특징과 전체 관계를 어떻게 함께 보는가를 살피는 편이 훨씬 중요하다.
이미 배운 모델 재사용하기 — 전이학습
CNN을 이해했다고 해서 매번 수백만 장의 이미지로 처음부터 학습할 필요는 없다. 큰 데이터에서 이미 선·질감·모양을 배운 모델을 가져와 내 문제에 맞게 바꾸는 것이 전이학습이다.
사전학습된 이미지 모델은 크게 둘로 볼 수 있다.
- Backbone: 이미지에서 공통 특징을 뽑는 부분
- Head: 뽑은 특징으로 내 범주를 판정하는 마지막 부분
가장 안전한 출발은 다음 순서다.
- 사전학습 가중치를 불러온다
- Backbone을 얼리고 내 범주 수에 맞는 Head만 새로 학습한다 — Linear Probing
- 검증 성능이 부족하면 마지막 Block부터 조금씩 푼다
- 아주 작은 학습률로 일부 또는 전체를 다시 학습한다 — Fine-tuning
- 매 단계에서 검증 점수가 실제로 좋아지는지 비교한다
| 상황 | 먼저 시도할 방법 |
|---|---|
| 데이터가 적고 원래 이미지와 비슷함 | Linear Probing |
| 데이터가 충분하고 원래 분야와 다름 | 일부 또는 전체 Fine-tuning |
| 빠른 기준 점수가 필요함 | Linear Probing으로 Baseline부터 측정 |
Fine-tuning에서 학습률을 크게 잡으면 이미 잘 배운 가중치를 한 번에 망가뜨리는 파괴적 망각이 생길 수 있다. 그래서 새 Head에는 비교적 큰 학습률, 사전학습 Backbone에는 더 작은 학습률을 주는 방식도 쓴다.
성능을 올릴 때 함께 보는 것
- 데이터 증강: 회전·자르기·뒤집기 등으로 같은 대상을 여러 모습으로 보여준다. 단 6을 뒤집어 9가 되는 것처럼 정답의 뜻을 바꾸는 변환은 쓰면 안 된다
- Batch Normalization: 미니배치 통계로 층의 값을 정돈한다. 훈련 때와 추론 때 사용하는 통계가 다르므로 상태 전환이 중요하다
- Learning Rate Scheduler: 초반에는 크게 배우고 정답 근처에서는 보폭을 줄인다
옵티마이저와 Scheduler는 역할이 다르다. 옵티마이저는 매 배치마다 각 가중치를 어느 방향으로 움직일지 정하고, Scheduler는 에폭이 지나거나 성능이 멈췄을 때 기준 학습률 자체를 바꾼다.
그리고 증강과 전처리는 반드시 데이터를 나눈 뒤 훈련 데이터에만 적용해야 한다. 원본과 거의 같은 증강 이미지가 훈련·검증 양쪽에 들어가면 검증 점수가 실제보다 높아지는 데이터 누수가 생긴다.
정리
- 모델 = 모양이 정해진 함수 + 조정 가능한 값들. 학습은 그 값을 찾는 문제
-
활성화 함수가 직선을 꺾어 비선형성을 만든다. 없으면 층을 쌓아도 결국 직선 하나
- 꺾이는 지점을 기준으로 기울기가 달라지는 것이 비선형성의 정체
- 꺾인 조각을 더하고 빼면 봉우리가 생기고, 조각을 늘리면 어떤 곡선이든 흉내 낼 수 있다
- Sigmoid·tanh는 양 끝이 납작해 기울기가 죽는다 → 깊은 은닉층에 부적합
- ReLU는 양수 쪽 기울기가 1이라 신호가 안 죽고 계산도 싸다. 단 죽은 뉴런 문제
- Leaky ReLU는 음수 쪽에 작은 기울기를 남겨 되돌아올 길을 만든다. GELU는 부드럽게 꺾어 학습을 안정시킨다
- 보편적 근사 정리 — 유닛이 충분하면 얕은 신경망도 뭐든 근사할 수 있다. 단 “충분히”가 문제
- 깊게 쌓는 게 넓히는 것보다 효율적이다. 층을 쌓는 건 덧셈이 아니라 곱셈
- 신경망의 정체는 행렬 곱셈의 반복. GPU를 쓰는 이유가 여기 있다
- 경사 하강법 = 기울기의 반대 방향으로 조금씩 이동. 학습률이 보폭
- 신경망의 손실은 대부분 non-convex — 웅덩이(지역 최솟값)에 빠질 수 있다
- SGD는 일부만 보고 걷는다 → 싸고, 흔들림 덕분에 웅덩이에서 잘 빠져나온다
- 모멘텀은 관성으로 지그재그를 줄이고, 적응적 학습률은 값마다 보폭을 조절한다
- Adam = 모멘텀 + 적응적 학습률. 기본값처럼 쓰이지만 항상 최선은 아니다
- L1·L2·Dropout은 모델이 훈련 데이터를 통째로 외우지 못하게 제약해 일반화를 돕는다
- 역전파 = 연쇄법칙을 뒤에서부터 적용해 기울기를 구하는 절차
- 학습 한 걸음 = 순전파 → 역전파 → 값 갱신
- CNN은 작은 필터와 가중치 공유로 이미지의 공간 특징을 효율적으로 배운다
- 전이학습은 사전학습 Backbone을 재사용한다. Linear Probing으로 시작하고 필요할 때 조심스럽게 Fine-tuning한다
다음 글에서
신경망의 공통 학습 원리와 이미지 처리 흐름까지 왔다. 다음 글부터는 또 다른 가지인 자연어처리로 넘어가, 컴퓨터가 단어를 숫자로 표현하는 방법부터 정리한다.
한줄 평
- 몰?루 대신 렐?루 학습 방식에 대한 기초적인 큰 흐름을 알게되어 유익한 강의였다! (아직 끝이 아니다)