본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 28. · 13 Views
편미분과 그래디언트 완벽 가이드
머신러닝의 핵심인 편미분과 그래디언트 개념을 초급 개발자도 이해할 수 있도록 쉽게 설명합니다. 다변수 함수부터 경사하강법, 신경망 학습까지 실무 예제와 함께 다룹니다.
목차
1. 다변수 함수 이해
김개발 씨는 머신러닝 프로젝트를 처음 맡게 되었습니다. 손실 함수 코드를 보던 중 변수가 여러 개인 함수를 발견했습니다.
"이건 고등학교 때 배운 f(x)와는 다른데, 어떻게 이해해야 하지?"
다변수 함수는 한마디로 입력이 여러 개인 함수입니다. 마치 요리사가 여러 재료를 조합해 하나의 요리를 만들어내는 것과 같습니다.
머신러닝에서는 수백, 수천 개의 가중치가 모두 입력이 되어 하나의 손실값을 계산합니다.
다음 코드를 살펴봅시다.
import numpy as np
# 다변수 함수 예시: 두 변수 x, y를 입력받아 하나의 값 반환
def f(x, y):
# z = x^2 + y^2 형태의 포물면
return x**2 + y**2
# 여러 입력값에 대한 함수값 계산
x, y = 3, 4
result = f(x, y) # 9 + 16 = 25
# 실제 머신러닝에서의 다변수 함수 (손실 함수)
def loss_function(weights, bias, X, y_true):
# 예측값 계산
y_pred = np.dot(X, weights) + bias
# MSE 손실 반환
return np.mean((y_true - y_pred)**2)
김개발 씨는 입사 3개월 차 주니어 개발자입니다. 오늘 처음으로 머신러닝 프로젝트에 투입되었는데, 코드를 열어보니 생소한 수식들이 가득했습니다.
그중에서도 특히 눈에 띄는 것이 있었습니다. 변수가 두 개, 세 개, 심지어 수천 개나 되는 함수들이었습니다.
선배 개발자 박시니어 씨가 다가와 물었습니다. "혹시 다변수 함수에 대해 알고 있어요?" 김개발 씨는 고개를 저었습니다.
"고등학교 때 배운 y = f(x)만 기억나는데요." 그렇다면 다변수 함수란 정확히 무엇일까요? 쉽게 비유하자면, 다변수 함수는 마치 레스토랑 주방과 같습니다.
요리사는 소금, 후추, 올리브유, 마늘 등 여러 재료를 적절한 비율로 조합합니다. 각 재료의 양이 조금씩 달라지면 최종 요리의 맛도 달라집니다.
이처럼 다변수 함수도 여러 입력값을 받아서 하나의 결과값을 내놓습니다. 고등학교에서 배운 f(x) = x 제곱 같은 함수는 입력이 x 하나뿐입니다.
그래프로 그리면 2차원 평면에 곡선이 됩니다. 하지만 f(x, y) = x 제곱 + y 제곱처럼 입력이 두 개가 되면 이야기가 달라집니다.
이 함수의 그래프는 3차원 공간에서 그릇 모양의 곡면이 됩니다. 왜 이런 다변수 함수가 머신러닝에서 중요할까요?
머신러닝 모델은 수많은 가중치와 편향을 가지고 있습니다. 예를 들어 간단한 선형 회귀 모델만 해도 각 특성마다 가중치가 하나씩 필요합니다.
이미지를 분류하는 딥러닝 모델이라면 가중치가 수백만 개에 달하기도 합니다. 이 모든 가중치가 손실 함수의 입력이 됩니다.
위의 코드를 살펴보겠습니다. 먼저 간단한 이변수 함수 f(x, y)를 정의했습니다.
x와 y 두 값을 입력받아 x 제곱 + y 제곱을 반환합니다. x가 3이고 y가 4이면 결과는 25가 됩니다.
아래쪽의 loss_function은 실제 머신러닝에서 사용하는 손실 함수의 형태입니다. weights, bias, X, y_true를 입력받습니다.
여기서 weights 자체가 여러 개의 숫자를 담은 배열입니다. 결국 이 함수는 수십, 수백 개의 숫자를 입력받아 하나의 손실값을 계산하는 다변수 함수입니다.
실제 현업에서는 어떻게 활용할까요? 추천 시스템을 개발한다고 가정해봅시다.
사용자의 나이, 성별, 과거 구매 이력 등 수십 가지 특성이 입력이 됩니다. 모델은 이 모든 입력을 조합해서 "이 사용자가 이 상품을 좋아할 확률"이라는 하나의 값을 예측합니다.
주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 변수가 많아지면 머릿속으로 시각화하려고 애쓰는 것입니다.
3차원까지는 그릇 모양으로 상상할 수 있지만, 4차원 이상은 시각화가 불가능합니다. 하지만 걱정할 필요 없습니다.
수학적 원리는 차원이 늘어나도 동일하게 적용됩니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 결국 입력이 여러 개일 뿐, 기본 원리는 같군요!" 이제 다변수 함수를 이해했으니, 다음 단계인 편미분으로 넘어갈 준비가 되었습니다.
실전 팁
💡 - 다변수 함수는 입력 개수가 늘어나도 "입력을 받아 출력을 내는" 기본 원리는 동일합니다
- 머신러닝의 손실 함수는 모든 가중치를 입력으로 받는 거대한 다변수 함수입니다
- 3차원 이상은 시각화보다 수학적 직관에 의존하세요
2. 편미분 정의
김개발 씨가 손실 함수를 최소화하는 방법을 찾고 있습니다. "변수가 여러 개인데, 미분은 어떻게 하죠?" 박시니어 씨가 웃으며 대답합니다.
"그래서 편미분이라는 게 있어요."
편미분은 한마디로 다른 변수는 고정하고 하나의 변수에 대해서만 미분하는 것입니다. 마치 에어컨 온도와 습도를 조절할 때 온도 버튼만 눌러보면서 체감 온도 변화를 살피는 것과 같습니다.
이렇게 하면 각 변수가 결과에 얼마나 영향을 주는지 개별적으로 파악할 수 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
# 이변수 함수: f(x, y) = x^2 + 2xy + y^2
def f(x, y):
return x**2 + 2*x*y + y**2
# x에 대한 편미분 (y는 상수 취급)
# df/dx = 2x + 2y
def partial_x(x, y):
return 2*x + 2*y
# y에 대한 편미분 (x는 상수 취급)
# df/dy = 2x + 2y
def partial_y(x, y):
return 2*x + 2*y
# 수치적 편미분 (작은 변화량으로 근사)
def numerical_partial_x(x, y, h=0.0001):
return (f(x + h, y) - f(x, y)) / h
# 점 (1, 2)에서의 편미분값
print(f"df/dx at (1,2): {partial_x(1, 2)}") # 6
print(f"df/dy at (1,2): {partial_y(1, 2)}") # 6
김개발 씨는 이제 다변수 함수가 무엇인지 알게 되었습니다. 하지만 새로운 의문이 생겼습니다.
머신러닝에서는 손실 함수를 최소화해야 하는데, 그러려면 미분이 필요합니다. 변수가 여러 개인 함수는 어떻게 미분할까요?
박시니어 씨가 화이트보드에 그림을 그리며 설명을 시작했습니다. "일단 미분이 뭔지 다시 생각해봐요.
미분은 함수가 얼마나 빠르게 변하는지를 알려주는 거잖아요?" 맞습니다. 고등학교에서 배운 미분은 x가 아주 조금 변할 때 f(x)가 얼마나 변하는지를 나타냅니다.
이것을 변화율 또는 기울기라고 합니다. 그런데 변수가 두 개라면 어떨까요?
f(x, y)에서 x도 변할 수 있고 y도 변할 수 있습니다. 둘 다 동시에 변하면 복잡해집니다.
그래서 수학자들은 현명한 방법을 생각해냈습니다. 하나씩 따로 보자는 것입니다.
편미분의 아이디어는 간단합니다. y는 잠시 상수라고 생각하고, x에 대해서만 미분합니다.
마치 "y야, 너는 잠깐 가만히 있어. x만 움직여볼게"라고 말하는 것과 같습니다.
쉽게 비유하자면, 에어컨 리모컨을 생각해보세요. 에어컨에는 온도 버튼과 습도 버튼이 있습니다.
체감 온도는 온도와 습도 두 가지 모두에 영향을 받습니다. 온도 버튼만 눌러보면서 "온도를 1도 올리면 체감 온도가 얼마나 올라가지?"를 확인할 수 있습니다.
이것이 바로 온도에 대한 편미분입니다. 위의 코드를 살펴보겠습니다.
f(x, y) = x 제곱 + 2xy + y 제곱이라는 함수가 있습니다. x에 대해 편미분할 때는 y를 상수 취급합니다.
x 제곱을 미분하면 2x가 됩니다. 2xy에서 y는 상수이므로 2y가 됩니다.
y 제곱은 상수이므로 미분하면 0이 됩니다. 따라서 df/dx = 2x + 2y입니다.
y에 대해 편미분할 때도 마찬가지입니다. 이번에는 x를 상수 취급합니다.
같은 방식으로 계산하면 df/dy = 2x + 2y가 됩니다. 수치적 편미분 함수도 눈여겨보세요.
실제 컴퓨터에서는 아주 작은 값 h를 더해서 변화량을 계산합니다. f(x + h, y)에서 f(x, y)를 빼고 h로 나누면 x에 대한 편미분의 근삿값을 구할 수 있습니다.
실무에서 편미분은 어디에 쓰일까요? 머신러닝에서 각 가중치가 손실에 얼마나 영향을 주는지 알아내는 데 사용됩니다.
가중치 w1을 조금 바꿨을 때 손실이 얼마나 변하는지, w2를 조금 바꿨을 때는 얼마나 변하는지를 각각 계산합니다. 주의할 점이 있습니다.
편미분 기호는 일반 미분 기호와 다릅니다. 일반 미분은 d를 쓰지만, 편미분은 둥근 모양의 기호를 씁니다.
코드에서는 구분이 안 되지만, 수학 수식에서는 이 차이를 알아두어야 합니다. 김개발 씨가 물었습니다.
"그러면 변수가 100개면 편미분도 100번 해야 하나요?" 박시니어 씨가 고개를 끄덕였습니다. "맞아요.
그리고 그 100개의 편미분값을 모아놓은 것을 그래디언트라고 해요. 다음에 알아볼 내용이죠."
실전 팁
💡 - 편미분은 "다른 변수를 고정하고 하나만 미분"이라고 기억하세요
- 수치적 편미분은 해석적으로 미분하기 어려울 때 유용합니다
- 편미분 기호와 일반 미분 기호의 차이를 구분하세요
3. 그래디언트 벡터
김개발 씨는 편미분을 이해했습니다. 하지만 변수가 수천 개일 때 편미분값을 어떻게 관리할까요?
"이걸 다 따로 저장하면 너무 복잡할 것 같은데요." 박시니어 씨가 답합니다. "그래서 벡터로 묶어서 관리해요.
그게 바로 그래디언트예요."
그래디언트는 모든 편미분값을 하나의 벡터로 모아놓은 것입니다. 마치 여러 개의 화살표를 하나의 큰 화살표로 합친 것과 같습니다.
그래디언트 벡터는 함수가 가장 가파르게 증가하는 방향을 가리킵니다.
다음 코드를 살펴봅시다.
import numpy as np
# 이변수 함수
def f(x, y):
return x**2 + y**2
# 그래디언트 함수 (모든 편미분을 벡터로 반환)
def gradient(x, y):
df_dx = 2 * x # x에 대한 편미분
df_dy = 2 * y # y에 대한 편미분
return np.array([df_dx, df_dy])
# 점 (3, 4)에서의 그래디언트
point = (3, 4)
grad = gradient(*point)
print(f"Gradient at {point}: {grad}") # [6, 8]
# 그래디언트의 크기 (변화의 가파른 정도)
magnitude = np.linalg.norm(grad)
print(f"Magnitude: {magnitude}") # 10.0
# 그래디언트의 방향 (단위 벡터)
direction = grad / magnitude
print(f"Direction: {direction}") # [0.6, 0.8]
편미분을 배운 김개발 씨에게 새로운 과제가 주어졌습니다. 손실 함수의 가중치가 1000개라면, 편미분도 1000개가 나옵니다.
이것들을 어떻게 효율적으로 다룰 수 있을까요? 박시니어 씨가 화이트보드에 화살표를 그리기 시작했습니다.
"산을 등반한다고 생각해봐요. 어느 방향으로 가면 가장 빨리 정상에 도착할까요?" 산의 높이는 위치(x, y)에 따라 달라집니다.
즉 h(x, y)라는 다변수 함수입니다. 동쪽으로 갈 때 높이 변화, 북쪽으로 갈 때 높이 변화를 각각 알 수 있습니다.
이 두 정보를 합치면 "어느 방향으로 가야 가장 가파르게 올라가는지"를 알 수 있습니다. 바로 이것이 그래디언트입니다.
각 방향(변수)에 대한 편미분값을 하나의 화살표(벡터)로 합친 것입니다. 코드를 살펴보겠습니다.
f(x, y) = x 제곱 + y 제곱인 함수가 있습니다. 이 함수의 그래디언트는 (df/dx, df/dy) = (2x, 2y)입니다.
gradient 함수는 이 두 편미분값을 NumPy 배열로 반환합니다. 점 (3, 4)에서 그래디언트를 계산하면 (6, 8)이 나옵니다.
이 벡터는 두 가지 정보를 담고 있습니다. 방향과 크기입니다.
그래디언트의 크기는 np.linalg.norm으로 계산합니다. (6, 8)의 크기는 10입니다.
이 숫자가 클수록 함수가 가파르게 변한다는 뜻입니다. 평평한 곳에서는 그래디언트의 크기가 0에 가깝습니다.
그래디언트의 방향은 단위 벡터로 나타냅니다. 그래디언트를 크기로 나누면 (0.6, 0.8)이 됩니다.
이 방향으로 이동하면 함수값이 가장 빠르게 증가합니다. 여기서 중요한 사실이 있습니다.
그래디언트는 함수가 가장 가파르게 증가하는 방향을 가리킵니다. 그러면 함수를 감소시키려면 어떻게 해야 할까요?
간단합니다. 그래디언트의 반대 방향으로 이동하면 됩니다.
이것이 바로 경사하강법의 핵심 아이디어입니다. 실무에서 그래디언트는 머신러닝의 심장과 같습니다.
신경망을 학습시킬 때 손실 함수의 그래디언트를 계산합니다. 그래디언트가 가리키는 방향의 반대로 가중치를 조금씩 업데이트합니다.
이 과정을 수천, 수만 번 반복하면 손실이 최소가 되는 가중치를 찾을 수 있습니다. 주의할 점이 있습니다.
그래디언트는 국소적인 정보입니다. 현재 위치에서 가장 가파른 방향을 알려줄 뿐, 전체 지형을 알려주지는 않습니다.
마치 안개 낀 산에서 발밑만 보고 내려가는 것과 같습니다. 김개발 씨가 고개를 끄덕였습니다.
"아, 그래서 그래디언트라는 이름이 붙었군요. 기울기를 뜻하는 gradient잖아요." 박시니어 씨가 웃었습니다.
"정확해요. 다차원 공간에서의 기울기를 벡터로 표현한 거예요."
실전 팁
💡 - 그래디언트의 방향은 함수가 가장 빠르게 증가하는 방향입니다
- 그래디언트의 크기는 그 방향으로의 변화율(가파른 정도)입니다
- 함수를 최소화하려면 그래디언트의 반대 방향으로 이동하세요
4. 방향 도함수
김개발 씨가 궁금해졌습니다. "그래디언트가 가장 가파른 방향이라면, 다른 방향으로 갈 때는 얼마나 변할까요?" 박시니어 씨가 답합니다.
"좋은 질문이에요. 그게 바로 방향 도함수예요."
방향 도함수는 특정 방향으로 이동할 때 함수가 얼마나 변하는지를 나타냅니다. 마치 산에서 특정 방향으로 한 걸음 내딛었을 때 고도가 얼마나 변하는지 알려주는 것과 같습니다.
그래디언트와 방향 벡터의 내적으로 쉽게 계산할 수 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
def f(x, y):
return x**2 + y**2
def gradient(x, y):
return np.array([2*x, 2*y])
# 방향 도함수 계산
def directional_derivative(grad, direction):
# 방향 벡터를 단위 벡터로 정규화
unit_dir = direction / np.linalg.norm(direction)
# 그래디언트와 방향의 내적
return np.dot(grad, unit_dir)
# 점 (3, 4)에서의 그래디언트
point = (3, 4)
grad = gradient(*point) # [6, 8]
# 다양한 방향으로의 방향 도함수
dir_x = np.array([1, 0]) # x축 방향
dir_y = np.array([0, 1]) # y축 방향
dir_diag = np.array([1, 1]) # 대각선 방향
print(f"x방향: {directional_derivative(grad, dir_x)}") # 6
print(f"y방향: {directional_derivative(grad, dir_y)}") # 8
print(f"대각선: {directional_derivative(grad, dir_diag)}") # 약 9.9
그래디언트가 가장 가파른 방향이라는 것은 알았습니다. 하지만 현실에서는 항상 그 방향으로만 갈 수 있는 것이 아닙니다.
산을 내려갈 때 절벽이 있으면 돌아가야 하듯이, 특정 방향으로 이동할 때 함수가 얼마나 변하는지도 알아야 합니다. 박시니어 씨가 예를 들었습니다.
"스키장에서 내려온다고 생각해봐요. 가장 가파른 방향은 직선으로 내려가는 거예요.
하지만 초보자는 지그재그로 내려가잖아요. 그 지그재그 방향으로 갈 때 경사가 얼마나 되는지가 바로 방향 도함수예요." 방향 도함수는 특정 방향 벡터 v를 따라 이동할 때 함수의 변화율입니다.
계산 방법은 의외로 간단합니다. 그래디언트와 방향 벡터의 내적을 구하면 됩니다.
내적이 무엇인지 간단히 복습해봅시다. 두 벡터 a = (a1, a2)와 b = (b1, b2)의 내적은 a1 곱하기 b1 + a2 곱하기 b2입니다.
기하학적으로는 두 벡터가 얼마나 같은 방향을 향하는지를 나타냅니다. 코드를 살펴보겠습니다.
directional_derivative 함수는 그래디언트와 방향 벡터를 받습니다. 먼저 방향 벡터를 단위 벡터로 정규화합니다.
그리고 그래디언트와 내적을 계산합니다. 점 (3, 4)에서 그래디언트는 (6, 8)입니다.
x축 방향 (1, 0)으로의 방향 도함수는 6입니다. 이것은 x에 대한 편미분값과 같습니다.
y축 방향 (0, 1)으로의 방향 도함수는 8입니다. 역시 y에 대한 편미분값과 같습니다.
대각선 방향 (1, 1)로의 방향 도함수는 약 9.9입니다. 그래디언트 방향 (6, 8)과 비교해보면 10보다 작습니다.
이것은 당연합니다. 그래디언트 방향이 가장 가파른 방향이기 때문입니다.
흥미로운 사실이 있습니다. 그래디언트와 수직인 방향으로의 방향 도함수는 0입니다.
내적이 0이 되기 때문입니다. 이 방향으로 이동하면 함수값이 변하지 않습니다.
이것이 바로 등고선의 방향입니다. 산에서 등고선을 따라 걸으면 고도가 변하지 않는 것과 같습니다.
실무에서 방향 도함수는 최적화 알고리즘에 활용됩니다. 예를 들어 공액 그래디언트법은 이전 탐색 방향과 수직인 방향으로 다음 탐색을 수행합니다.
이때 방향 도함수 개념이 필요합니다. 김개발 씨가 정리했습니다.
"그러니까 편미분은 축 방향으로의 방향 도함수이고, 그래디언트는 모든 편미분을 모은 것이고, 방향 도함수는 임의의 방향으로 확장한 거군요." 박시니어 씨가 엄지를 치켜세웠습니다. "완벽해요!"
실전 팁
💡 - 방향 도함수 = 그래디언트와 방향 벡터의 내적입니다
- 그래디언트 방향으로의 방향 도함수가 가장 큽니다
- 그래디언트와 수직인 방향으로의 방향 도함수는 0입니다 (등고선 방향)
5. 경사하강법 원리
드디어 김개발 씨가 기다리던 순간입니다. "이제 실제로 머신러닝 모델을 학습시키는 방법을 알려주세요!" 박시니어 씨가 화이트보드를 지우며 말했습니다.
"좋아요, 지금까지 배운 모든 것을 합쳐볼 시간이에요. 바로 경사하강법입니다."
경사하강법은 그래디언트의 반대 방향으로 조금씩 이동하며 함수의 최솟값을 찾는 알고리즘입니다. 마치 안개 낀 산에서 발밑의 경사만 보고 가장 낮은 곳을 찾아가는 것과 같습니다.
단순하지만 머신러닝의 가장 핵심적인 최적화 방법입니다.
다음 코드를 살펴봅시다.
import numpy as np
# 손실 함수: L(w) = (w - 3)^2 (최솟값은 w=3)
def loss(w):
return (w - 3)**2
# 손실 함수의 그래디언트
def gradient(w):
return 2 * (w - 3)
# 경사하강법 구현
def gradient_descent(start, learning_rate, iterations):
w = start
history = [w]
for i in range(iterations):
grad = gradient(w)
w = w - learning_rate * grad # 핵심: 그래디언트 반대 방향으로 이동
history.append(w)
if i % 5 == 0:
print(f"Step {i}: w={w:.4f}, loss={loss(w):.4f}")
return w, history
# 학습 실행
final_w, history = gradient_descent(
start=10, # 시작점
learning_rate=0.1, # 학습률
iterations=20 # 반복 횟수
)
print(f"Final w: {final_w:.4f}") # 3에 가까운 값
김개발 씨는 긴장했습니다. 지금까지 배운 다변수 함수, 편미분, 그래디언트가 모두 이 순간을 위한 것이었습니다.
경사하강법은 머신러닝 모델을 학습시키는 가장 기본적이고 중요한 알고리즘입니다. 박시니어 씨가 비유를 들었습니다.
"짙은 안개가 낀 산에 있다고 상상해봐요. 눈앞이 보이지 않아서 산 전체 지형을 알 수 없어요.
오직 발밑의 경사만 느낄 수 있죠. 이 상황에서 가장 낮은 곳을 찾으려면 어떻게 해야 할까요?" 답은 간단합니다.
발밑에서 가장 가파르게 내려가는 방향으로 한 걸음씩 내딛으면 됩니다. 이것을 반복하면 언젠가 골짜기에 도착합니다.
이것이 경사하강법의 핵심 아이디어입니다. 수학적으로 표현하면 이렇습니다.
현재 위치에서 그래디언트를 계산합니다. 그래디언트는 가장 가파르게 올라가는 방향입니다.
우리는 내려가고 싶으니까 그래디언트의 반대 방향으로 이동합니다. 새로운 위치에서 다시 그래디언트를 계산하고, 또 반대 방향으로 이동합니다.
코드를 살펴보겠습니다. 간단한 손실 함수 L(w) = (w - 3) 제곱을 정의했습니다.
이 함수의 최솟값은 w = 3일 때입니다. 그래디언트는 2(w - 3)입니다.
gradient_descent 함수가 핵심입니다. 시작점, 학습률, 반복 횟수를 입력받습니다.
매 반복마다 그래디언트를 계산하고, w에서 learning_rate 곱하기 grad를 뺍니다. 이 한 줄이 경사하강법의 전부입니다.
**학습률(learning rate)**은 매우 중요한 하이퍼파라미터입니다. 한 번에 얼마나 이동할지를 결정합니다.
학습률이 너무 크면 최솟값을 지나쳐버립니다. 너무 작으면 수렴하는 데 너무 오래 걸립니다.
적절한 학습률을 찾는 것이 머신러닝 엔지니어의 중요한 일입니다. 실행 결과를 보면 w가 10에서 시작해서 점점 3에 가까워지는 것을 볼 수 있습니다.
처음에는 그래디언트가 크기 때문에 큰 폭으로 이동합니다. 최솟값에 가까워질수록 그래디언트가 작아지므로 이동 폭도 줄어듭니다.
실무에서는 몇 가지 변형이 사용됩니다. **확률적 경사하강법(SGD)**은 전체 데이터 대신 일부 샘플만 사용해 그래디언트를 계산합니다.
모멘텀은 이전 이동 방향의 관성을 추가합니다. Adam은 학습률을 자동으로 조절합니다.
주의할 점도 있습니다. 경사하강법은 국소 최솟값에 빠질 수 있습니다.
산에 여러 개의 골짜기가 있으면, 시작 위치에 따라 다른 골짜기에 도착할 수 있습니다. 다행히 신경망의 손실 함수는 대부분의 국소 최솟값이 전역 최솟값과 비슷한 성능을 낸다는 것이 알려져 있습니다.
김개발 씨가 눈을 빛냈습니다. "와, 이렇게 간단한 원리였군요.
근데 실제 신경망에서는 가중치가 수백만 개잖아요. 그래디언트를 어떻게 계산하죠?" 박시니어 씨가 미소 지었습니다.
"좋은 질문이에요. 다음에 역전파에 대해 알아볼게요."
실전 팁
💡 - 학습률이 너무 크면 발산하고, 너무 작으면 수렴이 느립니다
- 실무에서는 Adam 같은 적응적 학습률 옵티마이저를 많이 사용합니다
- 국소 최솟값 문제는 여러 시작점에서 실행하거나 모멘텀으로 완화할 수 있습니다
6. 신경망에서의 그래디언트
김개발 씨가 드디어 핵심 질문을 던졌습니다. "가중치가 수백만 개인 신경망에서 그래디언트를 어떻게 계산하나요?
일일이 편미분하면 시간이 너무 오래 걸릴 것 같은데요." 박시니어 씨가 고개를 끄덕였습니다. "맞아요.
그래서 역전파라는 똑똑한 방법을 사용해요."
신경망에서의 그래디언트 계산은 역전파(Backpropagation) 알고리즘으로 효율적으로 수행됩니다. 연쇄 법칙을 활용해 출력에서 입력 방향으로 그래디언트를 전파합니다.
마치 도미노가 역방향으로 쓰러지는 것처럼, 각 층의 그래디언트가 이전 층으로 전달됩니다.
다음 코드를 살펴봅시다.
import numpy as np
# 간단한 2층 신경망
class SimpleNN:
def __init__(self):
self.w1 = np.random.randn(2, 3) # 입력층 -> 은닉층
self.w2 = np.random.randn(3, 1) # 은닉층 -> 출력층
def forward(self, x):
# 순전파: 입력에서 출력 방향으로 계산
self.z1 = np.dot(x, self.w1) # 선형 변환
self.a1 = np.maximum(0, self.z1) # ReLU 활성화
self.z2 = np.dot(self.a1, self.w2)
return self.z2
def backward(self, x, y_true, y_pred):
# 역전파: 출력에서 입력 방향으로 그래디언트 계산
m = x.shape[0]
# 출력층 그래디언트
dz2 = (y_pred - y_true) / m
dw2 = np.dot(self.a1.T, dz2) # w2에 대한 그래디언트
# 은닉층 그래디언트 (연쇄 법칙 적용)
da1 = np.dot(dz2, self.w2.T)
dz1 = da1 * (self.z1 > 0) # ReLU의 도함수
dw1 = np.dot(x.T, dz1) # w1에 대한 그래디언트
return dw1, dw2
김개발 씨는 경사하강법을 이해했습니다. 하지만 실제 신경망을 보니 막막해졌습니다.
입력층, 은닉층, 출력층이 여러 개 쌓여 있고, 각 층마다 수천 개의 가중치가 있습니다. 이 모든 가중치에 대한 그래디언트를 어떻게 계산할까요?
박시니어 씨가 핵심을 짚었습니다. "비밀은 **연쇄 법칙(Chain Rule)**에 있어요." 연쇄 법칙은 합성 함수의 미분 규칙입니다.
y = f(g(x))라면, dy/dx = dy/dg 곱하기 dg/dx입니다. 신경망은 본질적으로 거대한 합성 함수입니다.
입력이 첫 번째 층을 통과하고, 그 결과가 두 번째 층을 통과하고, 이런 식으로 계속됩니다. 역전파의 아이디어는 이렇습니다.
먼저 순전파로 입력에서 출력 방향으로 계산합니다. 예측값과 실제값의 차이(손실)를 구합니다.
그다음 역방향으로, 출력에서 입력 방향으로 그래디언트를 전파합니다. 코드를 살펴보겠습니다.
SimpleNN 클래스는 2층 신경망입니다. w1은 입력층에서 은닉층으로 가는 가중치, w2는 은닉층에서 출력층으로 가는 가중치입니다.
forward 메서드는 순전파를 수행합니다. 입력 x에 w1을 곱하고, ReLU 활성화 함수를 적용하고, 다시 w2를 곱합니다.
중간 계산 결과(z1, a1, z2)를 저장해둡니다. 역전파에서 필요하기 때문입니다.
backward 메서드가 핵심입니다. 먼저 출력층의 그래디언트를 계산합니다.
예측값과 실제값의 차이가 바로 손실의 그래디언트입니다. 이것을 dz2라고 합니다.
w2에 대한 그래디언트는 은닉층의 출력(a1)과 dz2의 곱입니다. 연쇄 법칙에 따라 두 그래디언트를 곱하는 것입니다.
다음으로 은닉층의 그래디언트를 계산합니다. dz2를 w2로 곱해서 역방향으로 전파합니다.
ReLU의 도함수를 곱합니다. ReLU의 도함수는 입력이 양수면 1, 음수면 0입니다.
마지막으로 w1에 대한 그래디언트를 계산합니다. 이렇게 연쇄 법칙을 차례로 적용하면 모든 가중치에 대한 그래디언트를 효율적으로 계산할 수 있습니다.
일일이 편미분하는 것보다 훨씬 빠릅니다. 실무에서는 PyTorch나 TensorFlow 같은 프레임워크가 역전파를 자동으로 처리합니다.
**자동 미분(Automatic Differentiation)**이라는 기능입니다. 개발자는 순전파 코드만 작성하면, 프레임워크가 알아서 그래디언트를 계산해줍니다.
주의할 점이 있습니다. 그래디언트 소실 문제입니다.
층이 깊어지면 그래디언트가 점점 작아져서 앞쪽 층의 가중치가 거의 업데이트되지 않을 수 있습니다. ReLU 활성화 함수, 배치 정규화, 스킵 연결 같은 기법이 이 문제를 완화합니다.
김개발 씨가 정리했습니다. "다변수 함수의 편미분을 모아서 그래디언트를 만들고, 그래디언트의 반대 방향으로 이동하는 게 경사하강법이고, 신경망에서는 역전파로 그래디언트를 효율적으로 계산하는 거군요!" 박시니어 씨가 박수를 쳤습니다.
"완벽해요. 이제 딥러닝의 기초를 다 익혔어요."
실전 팁
💡 - PyTorch의 loss.backward()나 TensorFlow의 tape.gradient()가 역전파를 자동으로 수행합니다
- 그래디언트 소실 문제에 주의하세요. ReLU와 배치 정규화가 도움됩니다
- 순전파에서 중간 결과를 저장해야 역전파에서 사용할 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.