1. 먼저, 이 질문은 무엇을 묻고 있을까?
Gradient Descent(경사하강법), Adam(아담), Optimizer(옵티마이저) 같은 단어들은 모두 "인공지능이 어떤 원리로 똑똑해지는가?"라는 본질적인 문제를 해결하기 위해 존재한다.
머신러닝에서 학습(Training)이란 모델이 입력 데이터를 보고 예측한 값과 실제 정답을 비교하여, 틀린 만큼 내부 설정을 고쳐나가며 정답률을 올리는 반복적인 과정 전체를 의미한다. 이 흐름을 이해하기 위해서는 핵심 구성 요소인 '손실 함수'와 '옵티마이저'의 역할을 명확히 정리해야 한다.
예측의 기준이 되는 '손실 함수(Loss Function)'
인공지능 모델은 스스로의 예측이 정확한지 판단하지 못한다. 모델이 낸 예측값과 실제 정답의 차이와 불일치 정도를 정량적으로 계산하여 현재 모델이 얼마나 틀렸는지를 하나의 오차 수치로 알려주는 채점 기준표가 바로 손실 함수다.
- 손실 함수는 해결하려는 문제의 성격에 따라 크게 두 가지로 분류된다.
- 회귀(Regression) 문제용 손실 함수 (연속된 숫자를 맞출 때)
- MSE (Mean Squared Error, 평균제곱오차): 큰 오차를 더 강하게 반영하여 강조하고 싶을 때 적합하다.
- MAE (Mean Absolute Error, 평균절대오차): 일반적인 오차의 크기를 평이하게 평가할 때 적합하다.
- 분류(Classification) 문제용 손실 함수 (카테고리를 맞출 때)
- 이진 크로스 엔트로피 (Binary Cross-Entropy): 정답 확률 분포가 두 가지 중 하나(예: Pass/Fail)일 때 정답과의 불일치를 측정한다.
- 범주형 크로스 엔트로피 (Categorical Cross-Entropy): 정답 확률 분포가 여러 개의 카테고리 중 하나일 때 정답과의 불일치를 측정한다.
- 회귀(Regression) 문제용 손실 함수 (연속된 숫자를 맞출 때)
옵티마이저에게 필요한 기울기를 계산하는 '역전파(Backpropagation)'
- 손실 함수가 오차를 계산하면 옵티마이저가 곧바로 가중치를 수정할 수 있을까? 그렇지 않다. 손실 함수는 단순히 "현재 모델이 얼마나 틀렸는지"를 하나의 수치로 알려줄 뿐이다. 하지만 어느 가중치가 이 오차에 얼마나 영향을 주었는지, 그리고 그 가중치를 어느 방향으로 조정해야 손실이 줄어드는지는 알려주지 않는다.
- 이때 필요한 과정이 바로 역전파(Backpropagation)다. 역전파는 손실 함수에서 계산된 손실값을 기준으로, 모델의 출력층에서 입력층 방향으로 거꾸로 미분을 수행한다. 이 과정에서 각 파라미터, 즉 가중치와 편향이 손실에 얼마나 영향을 주었는지를 나타내는 기울기(Gradient)를 계산한다. 이 기울기는 옵티마이저가 파라미터를 업데이트하기 위해 참고하는 핵심 정보(설명서)가 된다.
설명서를 받아 실행하는 '옵티마이저(Optimizer)'
손실 함수는 단순히 "현재 이만큼 틀렸다"는 결과물인 손실 값만 제공할 뿐이다. 그렇다면 모델은 이 손실 값을 어떻게 줄여나갈 수 있을까?
모델은 내부에 있는 가중치(Weight)와 편향(Bias)이라는 파라미터를 조정하면서 예측을 바꾼다. 이 가중치와 편향을 어떤 방향으로, 얼마나 크게 움직여야 가장 빠르고 안정적으로 오차의 최저점에 도달할 수 있을지 이동 법칙을 결정하고, 그 결과에 맞춰 모델 내부의 가중치와 편향을 '실제로 수정(업데이트)'하는 알고리즘이 바로 옵티마이저(Optimizer)다.
💡 핵심 요약
- 손실 함수: 현재 모델이 얼마나 틀렸는지 알려주는 정량적 '기준'
- 옵티마이저: 그 오차를 줄이기 위해 모델의 파라미터를 어떻게 수정할지 결정하는 '방법'
2. 경사하강법(Gradient Descent): 손실을 줄이는 가장 기본적인 방법
모든 옵티마이저의 뿌리이자 가장 클래식한 알고리즘이 바로 경사하강법(Gradient Descent)이다. 앞서 1장에서 살펴본 파이프라인을 떠올려보면, 경사하강법은 3단계 역전파(Backpropagation) 단계로부터 각 파라미터의 기울기(Gradient) 정보를 고스란히 넘겨받아 4단계 최적화(Optimization)를 수행하는 주체다.
💡 경사하강법의 작동 범위와 메커니즘
경사하강법이 정확히 어떤 데이터를 받고 무엇을 실행하는지 경계를 명확히 하면 다음과 같다.
- 전달받는 것 (Input): 역전파 알고리즘이 계산해낸 현재 파라미터 위치에서의 기울기(Gradient) 정보
- 수행하는 것 (Action): 동일한 학습률(Learning Rate) 기준 하에, 기울기가 가리키는 반대 방향(내리막길)으로 가중치와 편향 값을 직접 수정(업데이트)
수학적으로 기울기(Gradient)는 함수 값이 가장 가파르게 '증가'하는 방향을 나타낸다. 따라서 우리의 목표인 손실 함수의 최저점을 찾으려면 기울기 값에 마이너스(-)를 붙여 반대 방향으로 파라미터를 이동시켜야 한다.
🧮 수식과 코드의 1:1 직관적 비교
경사하강법의 수학적 공식과 이를 컴퓨터가 실행할 수 있도록 구현한 파이썬 코드를 매칭해 보면 그 구조를 아주 명확하게 이해할 수 있다.
1. 경사하강법 옵티마이저가 파라미터를 업데이트하는 식

- 다음 파라미터 = 현재 파라미터 - 학습률 × 현재 위치에서의 손실 함수 기울기
- θ (Theta / theta)
- 모델 내부의 가중치(Weight)나 편향(Bias)을 의미하는 파라미터.
- ∇L(θ) (Gradient / gradients)
- 역전파 단계에서 계산되어 넘어온 손실 함수의 기울기.
- 현재 위치에서 손실이 가장 빠르게 증가하는 방향, 즉 오르막길 방향을 알려주는 지표다.
- η (Eta / learning_rate)
- 사용자가 직접 설정하는 학습률이다.
- 파라미터를 한 번에 얼마나 크게 움직일지 결정하는 보폭의 기준 역할을 한다.
2. 파이썬 코드
import time
# [4단계: 최적화] 오직 기울기(gradient)만 넘겨받아 파라미터를 수정하는 경사하강법 함수
def gradient_descent(theta, learning_rate, gradient):
"""
θ_next = θ_current - (learning_rate * gradient)
"""
# 동일한 학습률 기준 하에, 넘어온 기울기 크기에 비례하여 내리막길(-) 방향으로 업데이트
next_theta = theta - learning_rate * gradient
return next_theta
# ==========================================
# 코랩 실습 시뮬레이션 환경 세팅
# ==========================================
current_theta = 10.0 # 시작 가중치 (산 꼭대기 위치)
learning_rate = 0.1 # 보폭 기준 값
total_steps = 3 # 시뮬레이션할 가상 루프 횟수
print("=" * 50)
print(f"🚀 경사하강법(GD) 최적화 시뮬레이션을 시작합니다.")
print(f" - 시작 가중치(theta): {current_theta}")
print(f" - 고정 학습률(learning_rate): {learning_rate}")
print("=" * 50)
time.sleep(0.5)
# 가상으로 역전파가 매 회차 다른 기울기를 계산해서 던져주는 루프
for step in range(1, total_steps + 1):
# [3단계 역전파 가정] 최저점에 가까워질수록 기울기(경사)가 점점 완만해진다고 가정한 가상 데이터
if step == 1: calculated_gradient = 8.0 # 처음엔 매우 가파름
elif step == 2: calculated_gradient = 5.0
elif step == 3: calculated_gradient = 3.0
print(f"\n🔄 [Iteration {step}]")
print(f" 📥 [3단계 역전파 완료] 계산된 기울기(Gradient) = {calculated_gradient}")
# [4단계 최적화] 경사하강법으로 가중치 실제 수정 및 갱신
updated_theta = gradient_descent(current_theta, learning_rate, calculated_gradient)
# 로그 출력으로 변화 확인
print(f" ⚙️ [4단계 최적화 완료] 연산 식: {current_theta:.2f} - ({learning_rate} * {calculated_gradient})")
print(f" 📤 [파라미터 갱신 결과] theta 상태 변경: {current_theta:.2f} ➡️ {updated_theta:.2f}")
# 다음 회차를 위해 현재 가중치를 업데이트된 값으로 교체
current_theta = updated_theta
time.sleep(0.3)
print("\n" + "=" * 50)
print(f"🎯 최종 시뮬레이션 종료! 정답 바닥에 안착한 theta: {current_theta:.2f}")
print("=" * 50)
⚠️ Gradient Descent의 한계점
원리가 단순하고 직관적이라는 강력한 장점이 있지만, 실제 복잡한 딥러닝 모델의 손실 함수 지형에서는 다음과 같은 명확한 한계에 부딪힌다.
참고:
Mastering Gradient Descent: Optimizing Neural Networks with Precision
1) 로컬 미니마(Local Minima)와 안장점(Saddle Point)의 덫
오직 현재 시점에 눈앞에 보이는 기울기 정보만 보고 발을 내딛기 때문에 지형의 함정에 쉽게 빠진다. 진짜 최저점(Global Minimum)으로 가는 길목에 있는 얕은 웅덩이(Local Minima)를 만나거나, 말의 안장처럼 사방이 평평해져 기울기가 0이 되어버리는 평지(안장점, Saddle Point)를 만나면 그곳이 정답인 줄 알고 학습을 멈춰버리는 문제가 발생한다.

3. Adam: 과거 gradient를 기억하는 개선된 방법
Adam(Adaptive Moment Estimation, 적응적 모멘트 추정)은 현대 딥러닝 연구와 실무 환경에서 가장 범용적으로 선택되는 옵티마이저다.
기본 경사하강법이 오직 현재 시점의 단일 기울기(Gradient) 정보만을 파라미터 업데이트에 반영하는 한계를 가졌다면, Adam은 과거 계산된 기울기들의 통계적 방향성과 크기 정보를 지수 이동 평균(Exponential Moving Average) 형태로 누적하여 활용하는 알고리즘이다. 이를 위해 Adam은 기울기의 방향성을 제어하는 기능과 파라미터별 보폭을 조절하는 두 가지 핵심 메커니즘을 결합했다.
💡 Adam의 작동 범위와 메커니즘
Adam이 정확히 어떤 데이터를 받고 무엇을 실행하는지 경계를 명확히 하면 다음과 같다.
- 전달받는 것 (Input): 역전파 알고리즘이 계산해낸 현재 파라미터 위치에서의 기울기(Gradient) 정보
- 수행하는 것 (Action): 과거 기울기들을 기반으로 계산된 '방향(1차 모멘트)'과 파라미터별 '맞춤형 보폭(2차 모멘트)'을 조합하여 가중치와 편향 값을 직접 수정(업데이트)
수학적으로 단순히 현재의 경사면에만 순종하는 GD와 달리, Adam은 직전까지 내려오던 흐름을 유지하려는 관성을 연산에 개입시키고 지형의 가파른 정도에 따라 학습률을 스스로 격차를 두어 조절한다.
🧮 수식과 코드의 1:1 직관적 비교
Adam의 수학적 공식과 이를 컴퓨터가 실행할 수 있도록 구현한 파이썬 코드를 매칭해 보면 파이토치(PyTorch) 내부에서 어떤 연산이 일어나는지 명확히 이해할 수 있다.
1. Adam 옵티마이저가 파라미터를 업데이트하는 식

- θ (Theta / theta): 모델 내부의 가중치(Weight)나 편향(Bias)을 의미하는 파라미터.
- g_t (Gradient / gradient): 역전파 단계에서 계산되어 넘어온 현재 시점(t)의 손실 함수 기울기.
- m_t / m_hat_t (1차 모멘트): 과거 기울기들의 지수 이동 평균. 업데이트의 '최종 방향(관성)'을 결정한다.
- v_t / v_hat_t (2차 모멘트): 과거 기울기 제곱들의 지수 이동 평균. 파라미터별 '적응적 보폭'을 결정한다.
- η (Eta / lr): 사용자가 지정하는 기본 학습률.
- β1, β2 (beta1, beta2): 과거 기억을 얼마나 남겨두고 축적할지 결정하는 하이퍼파라미터.
- ε (Epsilon / eps): 분모가 0이 되는 연산 에러를 막기 위한 수치 안정성 상수.
2. 파이썬 코드
import numpy as np
# 1. 환경 세팅 (PyTorch Adam 기반 파라미터)
lr = 0.1 # 기본 학습률 (보폭 기준)
beta1, beta2 = 0.9, 0.5 # beta2를 0.5로 낮춰 3번 만에 보폭 변화가 보이도록 설정
eps = 1e-08
# 기억 장치 및 시작 가중치 초기화
m, v = 0.0, 0.0
theta = 10.0
# 가상의 절벽 지형: 1회차에 엄청 가파르다가, 2회차부터 순식간에 평지가 됨
gradients = [20.0, 0.1, 0.1]
print("-" * 60)
print(f"🚀 Adam 최적화 시작 (시작 가중치: {theta} | 학습률: {lr})")
print("-" * 60)
for step in range(1, 4):
grad = gradients[step - 1]
# [방향 누적] 과거 기울기 흐름 기록
m = beta1 * m + (1 - beta1) * grad
# [보폭 누적] 과거 기울기 제곱 흐름 기록
v = beta2 * v + (1 - beta2) * (grad ** 2)
# [초기 편향 보정]
m_hat = m / (1 - beta1 ** step)
v_hat = v / (1 - beta2 ** step)
# [최적화 실행] 맞춤형 보폭을 반영하여 업데이트
next_theta = theta - (lr / (np.sqrt(v_hat) + eps)) * m_hat
# 변동량 계산 (현재 위치에서 얼마나 멀리 이동했는가)
step_size = abs(next_theta - theta)
print(f"[Iteration {step}] 입력 기울기: {grad}")
print(f" - 현재 가중치 상태 : {theta:.2f} ➡️ {next_theta:.2f}")
print(f" - 실제 파라미터 보폭: {step_size:.4f}")
print("-" * 60)
theta = next_theta
⚠️ Adam의 한계점
지형의 한계를 똑똑하게 극복하는 만능 옵티마이저처럼 보이지만, 실제 학습 환경에서는 다음과 같은 명확한 부작용을 마주한다.
참고:
Mastering Gradient Descent: Optimizing Neural Networks with Precision
1) 일반화(Generalization) 성능의 한계 (막판 스퍼트 부실)
학습 초·중반에는 온갖 함정을 다 피해 다니며 압도적으로 빠르게 오차를 줄여나간다. 하지만 학습 후반부에 접어들었을 때, 최종 테스트 데이터에 대한 정확도가 투접한 경사하강법(SGD) 계열에 밀리는 현상이 자주 발생한다. 파라미터별로 보폭을 독립적으로 계속 바꾸는 적응적 특성 때문에, 마지막 정밀 튜닝 구간에서 가장 깊고 정교한 최적점(Global Minimum)의 정중앙에 안착하지 못하고 주변을 미세하게 헤매는 경향이 있다.
2) 극심한 GPU 메모리(VRAM) 소모
기본 경사하강법은 현재 가중치 파라미터의 위치만 메모리에 들고 있으면 된다. 반면 Adam은 모든 파라미터마다 1차 모멘트(m, 방향) 주머니와 2차 모멘트(v, 보폭) 주머니를 하나씩 더 차고 있어야 한다. 즉, 기억해야 할 변수가 3배로 늘어나기 때문에 대규모 모델(LLM 등)을 학습할 때 GPU 메모리 압박이 매우 심하다.
4. Gradient Descent와 Adam의 알고리즘적 차이와 선택 기준
💡 알고리즘적 핵심 차이
두 알고리즘의 본질적인 차이는 역전파로 계산된 기울기(g_t)를 받아 과거의 이동 기록을 업데이트에 반영하는가에 있다.
Gradient Descent (경사하강법)
- 방향: 현재 시점의 기울기 방향만을 기준으로 움직인다. 이 때문에 안장점이나 로컬 미니마 부근처럼 기울기가 매우 작아지는 구간에서는 학습이 느려지거나 멈춘 것처럼 보일 수 있다.
- 보폭: 모든 파라미터에 사용자가 지정한 학습률(lr)을 동일하게 적용한다. 손실 함수 지형이 길고 좁은 계곡처럼 생긴 경우, 특정 방향으로 지그재그로 움직이며 수렴이 느려질 수 있다.
Adam (Adaptive Moment Estimation)
- 방향: 과거 기울기들의 이동 평균인 1차 모멘트를 사용해 이전 이동 방향을 어느 정도 기억한다. 덕분에 순간적으로 기울기가 작아지는 구간에서도 더 안정적으로 이동할 수 있다.
- 보폭: 과거 기울기 제곱의 이동 평균인 2차 모멘트를 사용해 파라미터별 업데이트 크기를 조절한다. 변화가 큰 파라미터는 조심스럽게, 변화가 작은 파라미터는 상대적으로 크게 업데이트할 수 있다.
연산 메커니즘 대조
비교 항목 Gradient Descent (경사하강법) Adam
| 핵심 판단 기준 | 현재 기울기 | 과거 기울기 정보 + 현재 기울기 |
| 연산 직관 | theta - lr * gradient | theta - (lr / 적응적 보폭) * 관성 방향 |
| 메모리 소모 | 적음 | 큼. 파라미터별 1차·2차 모멘트를 추가로 저장 |
장단점 및 실무 선택 기준
1) Gradient Descent / SGD 계열
- 장점: 구조가 단순하고 메모리 사용량이 적다. Momentum, learning rate scheduler 등을 잘 조합하면 학습 후반부에 안정적으로 수렴하며 좋은 일반화 성능을 보이는 경우가 많다.
- 단점: 학습률 설정에 민감하고, 초기 수렴 속도가 Adam보다 느릴 수 있다.
- 실무 선택 기준: CNN 기반 이미지 분류처럼 최종 일반화 성능을 중요하게 보는 문제에서 자주 고려된다. 충분한 학습 시간과 튜닝 여유가 있을 때 적합하다.
2) Adam
- 장점: 초기 수렴 속도가 빠르고, 파라미터별로 업데이트 크기를 조절하기 때문에 학습률 설정 부담이 상대적으로 적다.
- 단점: 파라미터마다 1차·2차 모멘트를 저장해야 하므로 메모리 사용량이 크다. 경우에 따라 SGD 계열보다 일반화 성능이 낮게 나올 수 있다.
- 실무 선택 기준: NLP, Transformer, LLM 파인튜닝처럼 파라미터 수가 많고 손실 지형이 복잡한 모델에서 널리 사용된다. 현재는 Adam에 weight decay를 더 적절하게 적용한 AdamW가 실무에서 자주 사용된다.
'IT' 카테고리의 다른 글
| [Python/Pandas] 데이터 리모델링 완벽 이해: 와이드 포맷을 롱 포맷으로 (melt와 pivot) (0) | 2026.06.06 |
|---|---|
| [FastAPI] async/await는 왜 필요할까? 비동기 처리의 본질과 모델 추론 API 예제로 이해하기 (0) | 2026.05.30 |