
학습률을 0.1에서 1.0으로 바꿨더니 손실이 갑자기 폭발한 경험, 있으신가요? 😱 그 이유는 놀랍게도 한 줄짜리 부등식으로 설명됩니다.
2부에서는 1부에서 만든 그래디언트를 나침반 삼아, 안개 낀 산을 실제로 내려가 봅니다. 경사하강법의 한 줄 공식, 학습률에 따른 수렴·진동·발산, 선형회귀 학습, 그리고 프레임워크의 자동미분까지 이어집니다.
📘 이 글은 위키독스 도서 Zero to 머신러닝 딥러닝 Master 의 00-04 미분·경사하강법 — 안개 낀 산에서 내려오기 (2부) 을 블로그용으로 다듬은 글입니다.
💻 실습 코드: GitHub · 🏠 mldict.net · ✍️ 기술 블로그
💻 이 글의 코드:
00-04-derivative-gradient-descent· Open in Colab — 핵심 코드와 실제 실행 결과는 글 아래 💻 GitHub 실습 섹션에 있습니다.
⬅️ 이전 글: #05 미분 쉽게 이해하기 — 수치미분·연쇄법칙·그래디언트 (딥러닝 수학, 1부) \
➡️ 다음 글: #07 베이즈 정리 쉽게 이해하기 — 확률·조건부 확률과 병원 검사의 함정 (1부)
x = x - lr*gx입니다.🔗 1부 복습: 미분은 "지금 이 순간의 기울기", 연쇄법칙은 "기울기를 거꾸로 곱해 가는 규칙", 그래디언트는 "편미분을 모은 벡터로, 가장 가파르게 올라가는 방향"이었습니다. 1부를 아직 못 보셨다면 #05 미분 쉽게 이해하기부터 읽고 오세요.
🔎 ① 핵심 정의\
경사하강법(Gradient Descent)은 그래디언트의 반대 방향으로 조금씩 이동하며 함수의 최솟값을 찾는 알고리즘입니다.
❓ 왜 필요한가?\
00-03에서는 정규방정식으로 한 번에 답을 구했습니다. 그런데 신경망의 손실함수는 너무 복잡해 공식으로 답을 구할 수 없습니다. 그럴 때 "조금씩 내려가기"만이 유일한 방법입니다.
⏰ 언제 사용하는가?\
모든 딥러닝 학습. GPT든 이미지 생성 모델이든, 학습 과정은 결국 이 한 줄의 수십억 번 반복입니다.
💜 더 쉽게 이해하기 — 🌫️ 안개 낀 산속의 등산객
👣 시야가 1m도 안 되는 안개 속에서 계곡(가장 낮은 곳)을 찾아야 합니다. 지도도 없고 전체 지형도 볼 수 없습니다. 할 수 있는 건 발밑 경사를 느끼는 것뿐입니다.
① 발밑 경사를 느낀다 (= 그래디언트 계산) → ② 가장 가파른 내리막 방향을 잡는다 (= ) → ③ 한 걸음 이동한다 (= 학습률만큼) → ④ 반복 → ⑤ 평평해지면 멈춘다 (= 기울기 ≈ 0)
🎒 여기서 보폭이 중요합니다. 종종걸음이면 해 지도록 도착 못 하고 🐢, 성큼성큼 뛰면 계곡을 건너뛰어 반대편 산비탈로 날아갑니다 💥. 이 보폭이 바로 학습률입니다.
🔬 수학공식 ⑦ — 경사하강법 업데이트 식 ⭐
1변수 버전
다변수(벡터) 버전 — 신경망은 이쪽입니다
변수 정의\
• / : 번째 스텝에서의 위치(가중치)\
• (에타) : 학습률(learning rate) — 한 걸음의 보폭, 보통 0.001 ~ 0.5\
• / : 현재 위치의 기울기(그래디언트)\
• 마이너스 부호 : "기울기의 반대로 간다"는 뜻 — 이 부호가 전부입니다 ⚠️\
\
왜 마이너스인가? — 부호로 이해하기\
• (오른쪽이 오르막) → → 왼쪽으로 이동 ✅\
• (오른쪽이 내리막) → → 오른쪽으로 이동 ✅\
• (평평) → 이동량 0 → 정지 🏁\
어느 경우든 낮은 쪽으로 갑니다. 부호 하나가 알고리즘 전체를 굴러가게 만듭니다.
🔢 진행 과정 — 5단계 알고리즘
📐 시각화 ⑤ — 경사하강법 알고리즘 흐름도

🧮 단계별 손 계산 — 직접 6스텝 굴려보기\
, 도함수 , 시작점 , 학습률 입니다. 참고로 이 함수의 진짜 최솟값은 → → , 입니다.
| 스텝 | 현재 | 기울기 | 이동량 | 새 | 목표까지 | |
|---|---|---|---|---|---|---|
| 0→1 | 10.0000 | 16.0000 | −1.6000 | 8.4000 | 41.9600 | 6.4000 |
| 1→2 | 8.4000 | 12.8000 | −1.2800 | 7.1200 | 27.2144 | 5.1200 |
| 2→3 | 7.1200 | 10.2400 | −1.0240 | 6.0960 | 17.7772 | 4.0960 |
| 3→4 | 6.0960 | 8.1920 | −0.8192 | 5.2768 | 11.7374 | 3.2768 |
| 4→5 | 5.2768 | 6.5536 | −0.6554 | 4.6214 | 7.8719 | 2.6214 |
| 5→6 | 4.6214 | 5.2429 | −0.5243 | 4.0972 | 5.3980 | 2.0972 |
👀 표에서 세 가지를 발견할 수 있습니다.\
① 기울기가 16 → 12.8 → 10.24 → … 로 매번 정확히 0.8배씩 줄어듭니다.\
② 따라서 이동량도 점점 작아져, 계곡에 가까워질수록 저절로 조심스럽게 걷습니다. 🐾\
③ 마지막 열 ""도 6.4 → 5.12 → 4.096 → … 로 정확히 0.8배씩 줄어듭니다.\
이 "0.8"이라는 숫자는 어디서 온 걸까요? 바로 아래에서 유도합니다. 🔍
🔬 수학공식 ⑧ — 발산 조건 직접 유도하기 🔴⭐ (이 장의 하이라이트)
"학습률이 크면 발산한다"는 말을 막연한 조언이 아니라 정확한 부등식으로 만들어봅시다.\
\
① 업데이트 식에 대입② 목표 지점 로부터의 "오차" 를 정의\
양변에서 2를 빼줍니다.③ 오른쪽의 를 로 묶기
④ 로 묶어내기
⑤ 해석 — 오차가 매 스텝 배씩 곱해집니다. 번 반복하면:
⑥ 수렴 조건 — 오차가 0으로 가려면 곱해지는 수의 크기가 1보다 작아야 합니다.
💡 이 부등식이 알려주는 것 — 표로 정리
| 예측 | 30스텝 후 실제 | ||
|---|---|---|---|
| 0.01 | 0.98 | 🐢 아주 느리게 수렴 | 6.3639 (아직 멀다) |
| 0.1 | 0.80 | 👍 안정적 수렴 | 2.0099 |
| 0.5 | 0.00 | 🚀 단 1스텝에 정답! | 2.0000 (완벽) |
| 0.9 | 0.80 | 🔄 0.1과 같은 속도! (부호만 진동) | 2.0099 |
| 1.0 | 1.00 | 🔁 영원히 제자리 진동 | 10.0000 (출발점!) |
| 1.1 | 1.20 | 💥 발산 | 1901.0105 (폭주 중) |
🤯 놀라운 세 가지 발견\
① 에서 → 오차가 한 번에 0이 됩니다. 실제로 , 정답에 1스텝만에 도착합니다! 🎯\
② 와 이 같은 속도 — 로 같기 때문입니다. 단 0.9는 이 음수라 목표 지점을 좌우로 넘나들며(진동하며) 수렴합니다. 🔄\
③ 은 정확히 경계 — 이라 오차 크기가 전혀 안 줄고, 무한 반복입니다. 그래서 30스텝 뒤에도 출발점 10 그대로입니다! 😵\
수식이 실험 결과를 완벽하게 예측했습니다. 이것이 수학을 배우는 이유입니다. ✨
🚀 일반화 — 왜 딥러닝은 학습률을 0.001처럼 작게 쓸까?
위 함수는 였고, 조건은 즉 였습니다. 일반적으로 2차 미분(곡률)이 클수록 허용 학습률은 작아집니다.
신경망의 손실면은 방향마다 곡률이 제각각이고 어떤 방향은 매우 뾰족합니다. 가장 뾰족한 방향에 맞춰야 하므로 학습률이 0.001처럼 작아지는 것입니다. Adam 같은 옵티마이저는 방향마다 학습률을 자동 조절해 이 문제를 완화합니다. 🛠️
📐 시각화 ⑥ — 실제 이동 경로 (손계산 표와 동일한 값)

🛠️ 실습 4 🔵
① 에 대해 , 로 손으로 3스텝 계산하세요. (힌트: )\
② 이 함수의 수렴 조건을 유도 ⑧의 방식으로 구하세요. (정답: , 동일하게 )\
③ (즉 )에서 로 두면 어떤 일이 벌어지는지 손으로 확인하세요. (힌트: )
🪜 진행 과정: ① 목표함수·도함수 정의 → ② 시작점·학습률 설정 → ③ 반복 루프에서 업데이트 → ④ 이력 기록 → ⑤ 이론 예측과 비교 검증
이번 단계에서는 세 개의 코드를 차례로 만듭니다. 1변수 → 다변수 → 실전 선형회귀 순으로 확장하며, 매번 수식이 어느 코드 줄이 되었는지 확인합니다.
💻 코드 A — 기본 경사하강법 + 이론 검증
🐍 Python — 경사하강법 30회 + 유도한 공식으로 검증
def f(x):
"""목표 함수 f(x) = x² - 4x + 5 (최솟값: x=2 에서 f(2)=1)"""
return x**2 - 4*x + 5
def df(x):
"""도함수 f'(x) = 2x - 4 ← 2단계에서 유도한 공식 그대로"""
return 2*x - 4
def gradient_descent(df, x_init, lr, n_steps):
"""
경사하강법 본체. 수식: x ← x - η·f'(x)
비유: 안개 속 등산객이 발밑 경사(df)를 보고
보폭(lr)만큼 n_steps 번 내려갑니다.
"""
x = x_init
history = [x] # 이동 경로를 기록 (나중에 그래프용)
for step in range(n_steps):
gx = df(x) # ① 기울기 계산 (기울기는 관례상 g 접두)
x = x - lr * gx # ② ★핵심★ 수식 x ← x - η·f'(x) 그 자체
history.append(x) # ③ 기록
if step < 3 or (step + 1) % 10 == 0: # 처음 3번 + 10번마다 출력
print(f"step {step+1:2d}: grad={gx:9.4f} x={x:9.4f} f(x)={f(x):9.4f}")
return x, history
x_final, hist = gradient_descent(df, x_init=10.0, lr=0.1, n_steps=30)
print(f"\n최종 x={x_final:.6f} f(x)={f(x_final):.6f} (정답 x=2, f=1)")
# ★ 4단계에서 유도한 공식 e_k = (1-2η)^k · e_0 으로 결과를 예측해봅니다
# e_0 = 10 - 2 = 8, (1-2×0.1) = 0.8, k = 30
predicted = 8 * (0.8 ** 30)
print(f"이론 예측 x-2 = 8*(0.8)^30 = {predicted:.6f}, 실제 = {x_final-2:.6f}")
🔗 GitHub에서 코드 보기 — 00_04_derivative_gradient_descent.py L187–L222
📘 코드 이해 — 수식 한 줄이 코드 한 줄
| 수학공식 | 파이썬 코드 | 설명 |
|---|---|---|
return x**2 - 4*x + 5 | 목표함수를 1:1로 옮김 | |
return 2*x - 4 | 2단계 규칙으로 유도한 결과 | |
gx = df(x) | 기울기 변수는 지시서 규칙대로 gx | |
x = x - lr * gx | ⭐ 알고리즘 전체가 이 한 줄 | |
8 * (0.8 ** 30) | 유도한 이론으로 결과를 미리 예측 |
🧩 코드 설명 — Python 기초
• 키워드 인자
gradient_descent(df, x_init=10.0, lr=0.1, n_steps=30)— 이름을 붙여 넘기면 순서를 헷갈릴 일이 없고, 읽는 사람도 무엇이 학습률인지 바로 압니다.\
\
• 여러 값 반환return x, history— 파이썬은 튜플로 여러 값을 한 번에 돌려줍니다. 받을 때는x_final, hist = ...로 풀어 받습니다.\
\
•%나머지 연산자 —(step+1) % 10 == 0은 "10의 배수 스텝일 때만"이라는 뜻입니다. 30줄을 다 찍으면 너무 길어서 쓰는 로그 관용구입니다.\
\
• 리스트append—history.append(x)로 매 스텝 값을 쌓아둡니다. 나중에matplotlib으로 수렴 곡선을 그릴 때 그대로 씁니다.\
\
• 변수명 규칙 — 지시서에 따라 기울기는gx,gW,gb형태로 씁니다. 수식의 는 코드에서lr(learning rate)로 통일합니다.
✅🟩 실행 결과
step 1: grad= 16.0000 x= 8.4000 f(x)= 41.9600
step 2: grad= 12.8000 x= 7.1200 f(x)= 27.2144
step 3: grad= 10.2400 x= 6.0960 f(x)= 17.7772
step 10: grad= 2.1475 x= 2.8590 f(x)= 1.7379
step 20: grad= 0.2306 x= 2.0922 f(x)= 1.0085
step 30: grad= 0.0248 x= 2.0099 f(x)= 1.0001
최종 x=2.009904 f(x)=1.000098 (정답 x=2, f=1)
이론 예측 x-2 = 8*(0.8)^30 = 0.009904, 실제 = 0.009904
🎯 가장 중요한 줄은 마지막 줄입니다. 4단계에서 손으로 유도한 가 예측한 0.009904와, 코드가 30번 반복해서 얻은 실제값 0.009904가 소수점 6자리까지 완전히 일치합니다. 손계산 표(1~6스텝)의 값들도 그대로 재현되었습니다. 수학 → 코드 → 검증 고리가 완성된 것입니다. ✨
💻 코드 B — 발산 조건 을 실험으로 확인
🐍 Python — 이론과 실험 대조
# 4단계에서 유도한 조건: |1 - 2η| < 1 ⟺ 0 < η < 1
# 이 예측이 정말 맞는지 여섯 가지 학습률로 확인합니다
for lr in [0.01, 0.1, 0.5, 0.9, 1.0, 1.1]:
x = 10.0
diverged = False
for step in range(30):
x = x - lr * df(x)
if abs(x) > 1e10: # 발산 감지 (안 하면 inf/overflow 경고 발생)
diverged = True
break
ratio = abs(1 - 2 * lr) # 유도한 감쇠율 |1-2η|
result = "발산 💥" if diverged else f"{x:10.4f}"
print(f"η={lr:<5} |1-2η|={ratio:.2f} 30스텝 후 x = {result}")
🔗 GitHub에서 코드 보기 — 00_04_derivative_gradient_descent.py L226–L240
✅🟩 실행 결과
η=0.01 |1-2η|=0.98 30스텝 후 x = 6.3639 🐢 너무 느림
η=0.1 |1-2η|=0.80 30스텝 후 x = 2.0099 👍 적당
η=0.5 |1-2η|=0.00 30스텝 후 x = 2.0000 🚀 1스텝 만에 정답
η=0.9 |1-2η|=0.80 30스텝 후 x = 2.0099 🔄 0.1과 동일 (진동하며 수렴)
η=1.0 |1-2η|=1.00 30스텝 후 x = 10.0000 😵 제자리 진동 (출발점 그대로!)
η=1.1 |1-2η|=1.20 30스텝 후 x = 1901.0105 💥 폭주 중
📌 실험이 이론과 정확히 일치합니다. 가 작을수록 빠르고, 1에 도달하면 멈추지 못하며, 1을 넘으면 발산합니다. 특히 에서 30번을 돌리고도 출발점 10.0으로 되돌아온 것이 인상적입니다 — 짝수 번 반복이라 제자리인 것이죠. 😵
💻 코드 C — 다변수로 확장 (벡터 한 줄이면 끝)
🐍 Python — 2변수 경사하강법
import numpy as np
def grad_circle(p):
"""f(x,y)=x²+y² 의 그래디언트 ∇f=(2x, 2y) — 3단계에서 유도"""
return 2 * p # 벡터 전체에 2를 곱하면 (2x, 2y) 완성!
p = np.array([3.0, 4.0]) # 시작점 (float 필수!)
lr = 0.1
for step in range(30):
gp = grad_circle(p) # 그래디언트 (벡터)
p = p - lr * gp # ★ 1변수와 완전히 같은 한 줄! (벡터 연산)
if step < 2 or (step + 1) % 10 == 0:
print(f"step {step+1:2d}: p={np.round(p,6)} f={float((p**2).sum()):.8f}")
🔗 GitHub에서 코드 보기 — 00_04_derivative_gradient_descent.py L244–L257
✅🟩 실행 결과
step 1: p=[2.4 3.2] f=16.00000000
step 2: p=[1.92 2.56] f=10.24000000
step 10: p=[0.322123 0.429497] f=0.28823038
step 20: p=[0.034588 0.046117] f=0.00332307
step 30: p=[0.003714 0.004952] f=0.00003831
🎉 코드가 1변수 때와 똑같습니다. x = x - lr*gx가 p = p - lr*gp로 바뀌었을 뿐인데, NumPy의 벡터 연산 덕분에 변수가 2개든 200만 개든 코드는 그대로입니다. 이것이 딥러닝이 수십억 개 파라미터를 다룰 수 있는 이유입니다. 🚀\
📌 참고로 가 가 된 것은 둘 다 0.8배된 것이고, 이는 1변수에서 유도한 과 정확히 같습니다. 비율 가 유지되므로 원점을 향해 직선으로 내려갑니다. 📐
🚀 실전 응용 — 00-03의 선형회귀를 경사하강법으로 다시 풀기
00-03에서 정규방정식 로 한 번에 구했던 답을, 이번엔 한 걸음씩 내려가서 구해봅니다. 두 방법이 같은 답에 도달한다면, 우리가 배운 것이 모두 맞았다는 강력한 증거가 됩니다. 🎯
데이터는 00-03과 동일: , , — 정규방정식 정답은 , 이었습니다.
🔬 수학공식 ⑨ — MSE 손실의 그래디언트 (3단계 예제 ③ 재등장)
손실함수 (MSE)
연쇄법칙으로 편미분 — 바깥 , 안쪽 ,
업데이트 식
변수 정의: 데이터 개수, 번째 입력·정답, 기울기(가중치), 절편(편향), 잔차\
\
📌 00-03과의 연결: 00-03에서 배운 "잔차의 합이 0"이라는 성질은 과 같은 말입니다 — 학습이 끝나면 이 되니까요! 🔗
🐍 Python — 선형회귀를 경사하강법으로 학습시키기
import numpy as np
# 00-03과 동일한 데이터: (1,2), (2,3), (3,5)
X = np.array([1.0, 2.0, 3.0])
Y = np.array([2.0, 3.0, 5.0])
m = len(X) # 데이터 개수
w, b = 0.0, 0.0 # 파라미터 초기화 (아무 값이나 OK)
lr = 0.1
for step in range(1, 1001):
# ── ① 순전파: 예측하고 오차 구하기 ─────────────────
pred = w * X + b # ŷ = wx + b (벡터 3개 한 번에)
e = pred - Y # 잔차 e = ŷ - y
# ── ② 역전파: 공식 ⑨를 그대로 코드로 ───────────────
gw = (2 / m) * np.sum(e * X) # ∂L/∂w = (2/m)Σ eᵢxᵢ
gb = (2 / m) * np.sum(e) # ∂L/∂b = (2/m)Σ eᵢ
# ── ③ 업데이트: 경사하강 한 걸음 ───────────────────
w = w - lr * gw
b = b - lr * gb
if step in (1, 10, 100, 500, 1000):
mse = np.mean((w * X + b - Y) ** 2)
print(f"step {step:4d}: w={w:.6f} b={b:.6f} MSE={mse:.6f}")
# ── ④ 00-03의 정규방정식 답과 비교 검증 ────────────────
A = np.column_stack([X, np.ones(m)])
exact = np.linalg.lstsq(A, Y, rcond=None)[0]
print("정규방정식(00-03) 정답:", np.round(exact, 6))
🔗 GitHub에서 코드 보기 — 00_04_derivative_gradient_descent.py L261–L291
📘 코드 이해 — 공식 ⑨가 코드의 어느 줄인가
| 수학공식 | 코드 | 단계 |
|---|---|---|
pred = w * X + b | 순전파 — 브로드캐스트로 3개 동시 계산 | |
e = pred - Y | 잔차 (00-03에서 배운 개념) | |
gw = (2/m)*np.sum(e*X) | → np.sum, 곱 → * | |
gb = (2/m)*np.sum(e) | 가 없는 이유는 이라서 | |
w = w - lr * gw | 4단계 업데이트 식 그대로 |
🧩 코드 설명 — NumPy 기초
• 브로드캐스트
w * X + b—X가 원소 3개짜리 벡터이고w,b는 숫자 하나인데, NumPy가 알아서 3개 데이터에 동시에 적용합니다. 반복문이 필요 없습니다. (00-01장 심화에서 배운 그 브로드캐스트입니다)\
\
•e * X— 벡터끼리의*는 성분별 곱이므로 이 됩니다. 여기에np.sum을 씌우면 가 완성됩니다. 사실 이것은 00-01장에서 배운 내적과 같아서,np.dot(e, X)로 써도 똑같습니다! 🔗\
\
•step in (1, 10, 100, 500, 1000)— 튜플 안에 있는지 검사하는 문법입니다. 특정 스텝에서만 로그를 남길 때 편합니다.\
\
• 왜w, b = 0.0, 0.0부터 시작하나? 어디서 출발하든 같은 골짜기로 굴러갑니다. MSE 손실은 그릇 모양(볼록함수)이라 지역 최솟값이 없기 때문입니다. 🥣
✅🟩 실행 결과
step 1: w=1.533333 b=0.666667 MSE=0.216296
step 10: w=1.405646 b=0.547822 MSE=0.062155
step 100: w=1.489440 b=0.357339 MSE=0.055638
step 500: w=1.499999 b=0.333335 MSE=0.055556
step 1000: w=1.500000 b=0.333333 MSE=0.055556
정규방정식(00-03) 정답: [1.5 0.333333]
🏆 이 장의 결승점입니다. 경사하강법으로 1000번 굴러 도착한 , 이, 00-03에서 공식 한 방으로 구한 답과 소수점 6자리까지 완전히 같습니다. 서로 완전히 다른 두 길(대수적 공식 vs 반복적 하강)이 같은 목적지에 도착한 것입니다. 🎯
📌 MSE 값도 주목하세요. 0.055556으로 수렴했는데, 이는 이며 00-03에서 구한 오차 제곱합 을 데이터 개수 3으로 나눈 값입니다 (). 같은 수치가 두 장에 걸쳐 다시 나타났습니다. 🔗
🛠️ 실습 5 🔴🚀
① 학습률을
[0.001, 0.05, 0.3, 0.7]로 바꿔가며 1000스텝 후 결과를 표로 정리하고, 어디서부터 불안정해지는지 찾아보세요.\
② 00-03의 키·몸무게 데이터(165~185cm)로 같은 코드를 돌려보세요. ⚠️ 아마 발산할 것입니다! 왜 그런지 생각해보고, 를(X - X.mean())/X.std()로 정규화한 뒤 다시 시도해보세요. (힌트: 입력값이 크면 그래디언트도 커집니다 — 실무에서 데이터 정규화가 필수인 이유)\
③ MSE가 매 스텝 어떻게 줄어드는지 리스트에 저장하고,matplotlib으로 학습 곡선을 그려보세요. 📉
🔎 ① 핵심 정의\
자동미분(automatic differentiation, autograd)은 우리가 손으로 유도했던 도함수를, 프레임워크가 계산 과정을 기록해뒀다가 자동으로 만들어주는 기술입니다.
❓ 왜 필요한가?\
층이 100개인 신경망의 도함수를 손으로 유도하는 것은 불가능합니다. 게다가 모델 구조를 바꿀 때마다 다시 유도해야 한다면 실험 자체가 불가능해집니다.
⏰ 언제 사용하는가?\
사실상 모든 딥러닝 코드에서. 우리가 gw = (2/m)*np.sum(e*X)라고 손수 쓴 줄이, loss.backward() 한 줄로 대체됩니다.
💜 더 쉽게 이해하기 — 📹 자동차 블랙박스
자동차 블랙박스는 주행 중 일어난 모든 일을 순서대로 녹화합니다. 사고가 나면 녹화본을 거꾸로 되감아 원인을 추적하죠. 📹
오토그라드도 똑같습니다. 순전파 때 "어떤 연산이 어떤 순서로 일어났는지"를 계산 그래프로 녹화해두고,
backward()를 부르면 녹화본을 거꾸로 재생하며 연쇄법칙을 순서대로 곱해 나갑니다. 2단계에서 배운 그 연쇄법칙이 여기서 자동으로 실행되는 것입니다. ⚙️
🔀 프레임워크 관점 — 우리가 손으로 쓴 줄이 어디로 갔나
| 개념 | NumPy (우리 코드) | DeZero | PyTorch | TensorFlow | JAX |
|---|---|---|---|---|---|
| 미분 계산 | df(x) 손수 작성 | y.backward() | loss.backward() | tape.gradient() | jax.grad(f) |
| 기울기 저장 | 변수 gx로 직접 | x.grad | x.grad | tape가 반환 | 함수가 반환 |
| 업데이트 | x = x - lr*gx | optimizers.SGD | optimizer.step() | apply_gradients | 직접 갱신 |
| 기울기 초기화 | 불필요 (매번 새로 계산) | cleargrads() | zero_grad() ⚠️ | 불필요 | 불필요 (순수함수) |
| GPU | ❌ | 일부 | ✅ | ✅ | ✅ |
🐍 Python — 같은 문제를 세 프레임워크로
# ══════════ ① PyTorch ══════════
import torch
x = torch.tensor(10.0, requires_grad=True) # 미분 대상으로 등록(=녹화 시작)
optimizer = torch.optim.SGD([x], lr=0.1) # SGD = 우리가 만든 그 경사하강법
for step in range(30):
loss = x**2 - 4*x + 5 # 순전파 (계산 과정이 녹화됨)
optimizer.zero_grad() # ⚠️ 이전 기울기 초기화 (안 하면 누적!)
loss.backward() # 역전파 → x.grad 에 2x-4 가 자동으로 채워짐
optimizer.step() # x = x - lr*x.grad 를 자동 수행
print("PyTorch :", round(x.item(), 6)) # 2.009904 — 우리 코드와 동일!
# ══════════ ② TensorFlow ══════════
import tensorflow as tf
x = tf.Variable(10.0)
opt = tf.optimizers.SGD(learning_rate=0.1)
for step in range(30):
with tf.GradientTape() as tape: # '테이프'에 녹화 (블랙박스 비유 그대로!)
loss = x**2 - 4*x + 5
grads = tape.gradient(loss, [x]) # 되감아서 기울기 추출
opt.apply_gradients(zip(grads, [x])) # 업데이트
print("TensorFlow:", round(float(x.numpy()), 6))
# ══════════ ③ JAX ══════════
import jax, jax.numpy as jnp
def loss_fn(x):
"""손실함수를 '순수 함수'로 정의 — JAX 스타일"""
return x**2 - 4*x + 5
grad_fn = jax.grad(loss_fn) # ★ 도함수 '함수'를 통째로 만들어줌!
x = 10.0
for step in range(30):
x = x - 0.1 * grad_fn(x) # 우리가 손으로 쓴 코드와 형태가 똑같음!
print("JAX :", round(float(x), 6))
🔗 GitHub에서 코드 보기 — 00_04_derivative_gradient_descent.py L295–L336
✅🟩 실행 결과 (세 프레임워크 모두 동일)
PyTorch : 2.009904
TensorFlow: 2.009904
JAX : 2.009904
# 참고 — 우리가 NumPy로 손수 만든 코드 A의 결과: 2.009904
# 그리고 손으로 유도한 이론 예측: 2 + 8×(0.8)^30 = 2.009904
🏆 네 가지 구현과 하나의 이론이 모두 같은 값에 도달했습니다. 프레임워크가 마법을 부리는 것이 아니라, 우리가 손으로 한 계산을 그대로 자동화했을 뿐이라는 것을 보여줍니다. 원리를 알면 어떤 프레임워크로 갈아타도 두렵지 않습니다. 💪
🧩 코드 설명 — 프레임워크 문법 기초
•
requires_grad=True— "이 텐서는 미분 대상이니 계산 과정을 녹화하라"는 표시입니다. 이게 없으면x.grad가None입니다.\
\
•with tf.GradientTape() as tape:—with블록 안쪽에서 일어난 연산만 녹화합니다. 블록 밖에서 계산한 손실은 미분할 수 없습니다.\
\
•jax.grad(loss_fn)— 가장 인상적인 문법입니다. 값이 아니라 "도함수 함수" 자체를 만들어 돌려줍니다. 수학에서 로 가는 연산을 코드로 그대로 표현한 것이죠. 🤯\
\
•x.item()— 원소가 하나인 텐서에서 순수 파이썬 숫자를 꺼냅니다.print(x)하면tensor(2.0099, requires_grad=True)처럼 지저분하게 나오기 때문입니다.\
\
•zip(grads, [x])— 기울기와 변수를 짝지어 묶습니다. TensorFlow는 (기울기, 변수) 쌍의 리스트를 요구합니다.
🐞 실수하기 쉬운 부분 — PyTorch의
zero_grad()❌ 왜 틀리나?
optimizer.zero_grad()를 빼면 학습이 이상하게 빨라지거나 발산합니다.\
❌ 원인: PyTorch는backward()를 부를 때 기울기를 덮어쓰지 않고 더합니다(누적). 초기화를 안 하면 2번째 스텝의 기울기는 1+2번 합, 3번째는 1+2+3번 합이 되어 사실상 학습률이 계속 커지는 효과가 납니다.\
❓ 왜 그런 설계인가? 메모리가 부족해 큰 배치를 쪼개 여러 번backward()한 뒤 한 번에 업데이트하는 기법(gradient accumulation)을 지원하기 위해서입니다. 즉 기능이지 버그가 아닙니다.\
✅ 수정: 매 스텝 시작에optimizer.zero_grad()를 반드시 호출하세요. 순서는 zero_grad → backward → step입니다.
🛠️ 실습 6 🔴
① PyTorch 코드에서
zero_grad()를 주석 처리하고 돌려보세요. 결과가 어떻게 달라지나요?\
② 앞의 선형회귀(코드 D)를 PyTorch로 다시 작성하고, NumPy 결과 과 비교하세요.\
③jax.grad(jax.grad(loss_fn))로 2차 미분을 구해보세요. (정답: ) 🤓
지금까지 다룬 나 MSE는 골짜기가 하나뿐인 그릇 모양(볼록함수)이라, 어디서 출발하든 반드시 전역 최솟값에 도착했습니다. 🥣
그런데 실제 신경망의 손실면은 울퉁불퉁한 산맥입니다. 얕은 웅덩이(지역 최솟값)에 빠지면, 주변이 다 오르막이라 기울기가 0이 되어 더 깊은 계곡이 옆에 있어도 멈춰버립니다. 🕳️
💜 더 쉽게 이해하기 — 🎳 볼링공과 관성
경사하강법은 매 순간 멈춰서 경사를 재는 신중한 등산객입니다. 작은 웅덩이에 들어가면 그대로 주저앉습니다.
반면 모멘텀(Momentum)은 등산객 대신 굴러가는 볼링공입니다. 🎳 이전에 굴러온 관성이 있어서 얕은 웅덩이는 탄력으로 넘어가 더 깊은 골짜기까지 갑니다.
🔬 수학공식 ⑩ — 모멘텀 (맛보기)
변수 정의: 는 속도(velocity), 는 관성 계수(보통 0.9)\
기존 경사하강법은 인 특수한 경우입니다. 가 클수록 이전 방향을 더 오래 기억합니다.\
코드로는v = beta*v - lr*gx/x = x + v두 줄뿐입니다. 자세한 내용은 이후 권에서 다룹니다. 🚀
📌 다행히도 실제 딥러닝에서는 지역 최솟값이 생각만큼 큰 문제가 아니라는 연구가 많습니다. 차원이 아주 높으면 "모든 방향이 오르막인 지점"이 확률적으로 드물기 때문입니다. 오히려 안장점(saddle point)이 더 흔한 장애물이며, 모멘텀·Adam이 이를 잘 빠져나옵니다. 🐎
⚠️ 실수하기 쉬운 부분 — 종합
❌ 실수 1 — 부호를 반대로\
왜 틀리나?x = x + lr*gx로 쓰면 오르막으로 올라가 손실이 폭증합니다. 원인: "경사하강"이 빼기라는 것을 잊음. 수정: 반드시x = x - lr*gx. 증상: 손실이 첫 스텝부터 커지면 100% 이 문제입니다.❌ 실수 2 — 학습률 과다\
왜 틀리나? 손실이nan이나inf로 튑니다. 원인: 이 되어 발산. 수정: 학습률을 10분의 1로 줄여 재시도. 그래도 안 되면 또 10분의 1.❌ 실수 3 — 입력 스케일 미조정 ⭐\
왜 틀리나? 키(165~185) 같은 큰 값을 그대로 넣으면 그래디언트가 수천이 되어 발산합니다. 원인: 에서 가 크면 기울기도 커짐. 수정:X = (X - X.mean())/X.std()로 정규화. 실무에서 데이터 정규화가 필수인 이유가 바로 이것입니다.❌ 실수 4 — 정수 배열\
np.array([3, 4])는 정수형이라 미세한 갱신이 잘려버립니다. 그래디언트가 계속 0이면print(p.dtype)부터 확인하세요. 수정:np.array([3.0, 4.0]).❌ 실수 5 — 연쇄법칙 안쪽 미분 누락\
를 까지만 쓰고 를 잊는 실수. 수정:check_grad로 항상 검증하세요.❌ 실수 6 —
zero_grad()누락\
PyTorch에서 기울기가 누적되어 사실상 학습률이 커집니다. 순서: zero_grad → backward → step.
🐞 디버깅 팁 — 증상별 처방전
| 증상 | 의심할 원인 | 확인 방법 |
|---|---|---|
💥 손실이 inf/nan | 학습률 과다, 입력 스케일 | lr을 1/10로, X.std() 출력 |
| 📈 손실이 계속 증가 | 부호 오류 (+를 씀) | 업데이트 줄의 - 확인 |
| 🐢 손실이 거의 안 줄음 | 학습률 과소 | lr을 10배로, 기울기 크기 출력 |
| 🔄 손실이 위아래 진동 | 이 음수 (η가 다소 큼) | lr을 절반으로 |
| 0️⃣ 그래디언트가 전부 0 | 정수 dtype, 원상복구 누락 | print(p.dtype) |
❓ x.grad가 None | requires_grad 미설정 | print(x.requires_grad) |
| 🔍 수치미분이 이상함 | 가 너무 작음(자릿수 손실) | h=1e-4로 고정, 중심차분 사용 |
| ❌ 유도는 맞는데 검증 실패 | 절대오차로 비교함 | 상대오차로 변경 |
🧠 개념 문제
Q1 🟢 그래디언트가 가리키는 방향은 함수 값이 (증가/감소)하는 방향이다. 알맞은 것을 고르고, 경사하강법이 왜 반대로 가는지 설명하시오.
Q2 🔵 수치미분에서 전진차분보다 중심차분을 쓰는 이유를 오차 관점에서 설명하시오. 또 를 무한정 작게 하면 안 되는 이유는?
🧮 계산 문제
Q3 🟢 을 미분하고, 에서 로 경사하강 한 스텝 후의 를 구하시오.
Q4 🔵 를 연쇄법칙으로 미분하시오.
Q5 🔴 에 경사하강법을 적용할 때, 발산하지 않는 의 범위를 유도 ⑧과 같은 방식으로 구하시오.
💻 구현 문제
Q6 🔵 numerical_diff로 의 에서 미분값을 근사하고, 이론값 과 비교하는 코드를 작성하시오.
Q7 🔴 손실 이력을 저장해 "MSE가 이전보다 커지면 자동으로 학습률을 절반으로 줄이는" 경사하강법을 구현하시오.
🚀 사고 확장 문제
Q8 🔴 손실함수에 골짜기가 여러 개라면 경사하강법이 항상 전역 최솟값을 찾는다고 보장할 수 있는가? 이유와 완화 방법을 설명하시오.
Q9 🔴🚀 00-03의 정규방정식은 "한 번에" 답을 준다. 그런데도 딥러닝이 굳이 느린 경사하강법을 쓰는 이유를 두 가지 이상 쓰시오.
✅ 해설
A1. 증가입니다. 그래디언트는 함수가 가장 빠르게 커지는 방향을 가리킵니다. 우리는 손실을 줄이고 싶으므로 정확히 반대 방향 로 이동합니다. 업데이트 식의 마이너스 부호가 바로 이 뜻입니다.
A2. 전진차분의 오차는 , 중심차분은 입니다. 좌우 대칭으로 재면 곡선의 휘어짐에서 오는 오차가 서로 상쇄되기 때문입니다. 하지만 를 너무 작게 하면
f(x+h)와f(x-h)가 부동소수점 정밀도 안에서 거의 같아져 뺄셈 결과가 뭉개집니다(자릿수 손실). 그래서 가 실용적 최적점입니다.A3. , . 따라서 . 참고로 이 함수의 최솟값은 이므로 한 스텝에 정답에 도달했습니다 — 이고 였기 때문입니다! 🎯
A4. 안쪽 → . 바깥 → . 곱하면 .
A5. 이므로 . 최솟값이 이므로 오차가 곧 자신입니다. 수렴 조건은 → . 일반식 과 일치합니다. ✅
A6.
numerical_diff(math.sin, 0.0)→ 1.0000000000 (중심차분은 이 홀함수라 오차가 극히 작습니다). 이론값 과 일치합니다.A7. 매 스텝 MSE를 계산해
prev_loss와 비교하고,if loss > prev_loss: lr = lr * 0.5를 넣으면 됩니다. 이런 기법을 학습률 스케줄링(adaptive learning rate)이라 하며, Adam 같은 옵티마이저가 이를 정교하게 자동화한 것입니다.A8. 보장하지 못합니다. 경사하강법은 현재 위치의 국소 기울기만 보므로 지역 최솟값에 갇힐 수 있습니다. 완화책: ① 모멘텀으로 관성을 주어 얕은 웅덩이를 넘어감, ② SGD의 미니배치 잡음이 흔들어 빠져나오게 함, ③ 여러 시작점에서 실행, ④ Adam 등 적응형 옵티마이저. 다만 고차원에서는 지역 최솟값보다 안장점이 더 흔한 문제입니다.
A9. ① 공식이 존재하지 않음 — 신경망 손실은 비선형·비볼록이라 을 대수적으로 풀 수 없습니다. ② 계산 비용 — 정규방정식은 계산이 파라미터 수의 세제곱에 비례해, 파라미터가 수백만이면 불가능합니다. ③ 메모리 — 전체 데이터를 한 번에 올릴 수 없어도 미니배치로 조금씩 학습할 수 있습니다. ④ 온라인 학습 — 데이터가 계속 들어와도 이어서 학습 가능합니다.
🎯 개념 요약
🔬 미분 — 극한으로 정의된 순간 변화율\
📐 공식 — , 연쇄법칙 \
🧭 그래디언트 — 편미분을 모은 벡터, 최대 증가 방향\
⛰️ 경사하강법 — 한 줄\
⚖️ 발산 조건 — 을 직접 유도\
🚀 응용 — 선형회귀가 정규방정식과 같은 답에 도달\
🤖 자동미분 — 손계산을 프레임워크가 대신
📌 핵심 문장 3가지
① 🔬 미분 = 두 점을 무한히 가깝게 붙였을 때의 기울기이며, 코드에서는 중심차분으로 근사한다.\
\
② 🧭 그래디언트의 반대 방향이 손실이 가장 빠르게 줄어드는 방향이고, 그 한 줄이 x = x - lr*gx이다.\
\
③ ⚖️ 학습률은 감쇠율 로 결정된다 — 너무 크면 발산, 너무 작으면 느림.
☑️ 개념 체크리스트 (성취 기준)
☐ 의 에서 기울기를 손으로 계산할 수 있다 (정답 10) 🟢\
☐ 극한 정의에서 가 약분되는 과정을 직접 쓸 수 있다 🟢\
☐ 을 미분할 수 있다 (정답 ) 🟢\
☐ 를 연쇄법칙으로 미분할 수 있다 (정답 ) 🔵\
☐ 시그모이드 미분 를 유도하고, 왜 출력을 저장하는지 설명할 수 있다 🔵⭐\
☐ 편미분에서 "나머지 변수는 상수"의 의미를 교차항 예제로 설명할 수 있다 🔵\
☐ 그래디언트가 등고선과 수직인 이유를 설명할 수 있다 🔵\
☐ 경사하강법 업데이트 식의 마이너스 부호를 부호별로 설명할 수 있다 🔵⭐\
☐ 를 유도하고 수렴 조건을 말할 수 있다 🔴⭐\
☐ 에서 1스텝에 수렴하는 이유를 설명할 수 있다 🔴\
☐ MSE의 편미분 를 유도하고 코드로 옮길 수 있다 🔴🚀\
☐ PyTorch의 zero_grad()·backward()·step()이 우리 코드의 어느 줄인지 짝지을 수 있다 🔴
🚫 실수 방지 체크리스트
☐ 업데이트 식의 부호가 빼기(−)인가\
☐ 학습률이 보다 작은가 (발산 위험 확인)\
☐ 입력 데이터를 정규화했는가 (스케일이 크면 발산)\
☐ 배열 dtype이 float인가 (정수면 갱신이 잘림)\
☐ 연쇄법칙에서 안쪽 미분을 곱했는가\
☐ 수치미분에서 부근을 썼는가 (너무 작으면 자릿수 손실)\
☐ 검증할 때 절대오차가 아닌 상대오차를 썼는가\
☐ numerical_gradient에서 p[i] = tmp 원상복구를 했는가\
☐ 배열을 함수에 넘길 때 .copy()로 원본을 보호했는가\
☐ PyTorch에서 매 스텝 zero_grad()를 호출했는가\
☐ 수식 변수명과 코드 변수명이 일치하는가 ( ↔ lr, ↔ gx/gw)\
☐ HTML 태그가 모두 닫히고, Mermaid 노드 ID가 ASCII인가
📚 추가 학습
✏️ 직접 구현 — check_grad를 확장해 자주 쓰는 함수 10개의 도함수를 유도하고 전부 검증해보기\
📉 학습 곡선 — matplotlib으로 학습률별 손실 곡선을 한 그래프에 겹쳐 그려 발산 조건을 눈으로 확인하기\
🎳 모멘텀 구현 — v = beta*v - lr*gx 두 줄을 추가해 수렴 속도가 얼마나 빨라지는지 비교하기\
🔥 PyTorch 비교 — 5단계 선형회귀를 nn.Linear와 nn.MSELoss로 다시 작성하기\
🧵 TensorFlow 비교 — GradientTape로 같은 문제를 풀고 문법 차이를 표로 정리하기\
⚡ JAX 비교 — jax.grad와 jax.jit을 함께 써서 속도 차이를 측정하기\
🧮 2차 미분 — jax.hessian으로 헤시안을 구해 곡률 가 학습률 상한과 어떻게 연결되는지 확인하기
➡️
🔮 다음 장 예고 — 00-05. 확률·베이즈 정리 — 병원 검사의 함정 🏥🎲
이번 장에서 우리는 AI가 "기울기를 따라 점점 더 정확해지는" 과정을 배웠습니다. 그런데 아무리 학습해도 AI의 예측은 100% 확신이 아니라 "고양이일 확률 87%"처럼 확률로 나옵니다.
다음 장에서는 "정확도 99%인 검사에서 양성이 나왔는데, 실제로 병에 걸렸을 확률은 겨우 9%"라는 충격적인 계산을 직접 해봅니다. 🤯 직관을 완전히 뒤엎는 베이즈 정리가, 스팸 필터부터 의료 AI까지 어떻게 쓰이는지 살펴봅니다!
📂 이 장의 코드 폴더: book/00-math-overview-for-beginners/00-04-derivative-gradient-descent
책에 실린 코드를 장(절)별로 모아 둔 폴더입니다. 아래 파일을 열어 보거나, Colab에서 바로 실행해 보세요.
| 파일 | 용도 | 링크 |
|---|---|---|
00_04_derivative_gradient_descent.py | 책의 코드 블록 10개를 순서대로 모은 실행 스크립트 (# %% 셀 구분) | 코드 보기 |
00_04_derivative_gradient_descent.ipynb | 같은 코드를 블록당 셀 1개로 담은 주피터 노트북 | 보기 · Colab |
README.md | 코드 블록 목록 · 실행 방법 · 검증 결과 | 보기 |
🎬 medium-series/06-2-gradient-descent/ | Medium 시리즈 예제 — 같은 주제를 블로그용으로 다듬은 실습 | 폴더 · Colab |
🧪 저장소 검증 상태: 🧩 문맥 필요(조각) —
Function이(가) 이 절 안에서 정의되지 않습니다 — 데이터·변수·클래스를 미리 준비했다고 가정한 설명용 코드 조각입니다.
📄 00_04_derivative_gradient_descent.py L187–L222 · Block 5 (📍 5단계 — 코드로 구현하고 실전에 적용하기)
def f(x):
"""목표 함수 f(x) = x² - 4x + 5 (최솟값: x=2 에서 f(2)=1)"""
return x**2 - 4*x + 5
def df(x):
"""도함수 f'(x) = 2x - 4 ← 2단계에서 유도한 공식 그대로"""
return 2*x - 4
def gradient_descent(df, x_init, lr, n_steps):
"""
경사하강법 본체. 수식: x ← x - η·f'(x)
비유: 안개 속 등산객이 발밑 경사(df)를 보고
보폭(lr)만큼 n_steps 번 내려갑니다.
"""
x = x_init
history = [x] # 이동 경로를 기록 (나중에 그래프용)
for step in range(n_steps):
gx = df(x) # ① 기울기 계산 (기울기는 관례상 g 접두)
x = x - lr * gx # ② ★핵심★ 수식 x ← x - η·f'(x) 그 자체
history.append(x) # ③ 기록
if step < 3 or (step + 1) % 10 == 0: # 처음 3번 + 10번마다 출력
print(f"step {step+1:2d}: grad={gx:9.4f} x={x:9.4f} f(x)={f(x):9.4f}")
return x, history
x_final, hist = gradient_descent(df, x_init=10.0, lr=0.1, n_steps=30)
print(f"\n최종 x={x_final:.6f} f(x)={f(x_final):.6f} (정답 x=2, f=1)")
# ★ 4단계에서 유도한 공식 e_k = (1-2η)^k · e_0 으로 결과를 예측해봅니다
# e_0 = 10 - 2 = 8, (1-2×0.1) = 0.8, k = 30
predicted = 8 * (0.8 ** 30)
print(f"이론 예측 x-2 = 8*(0.8)^30 = {predicted:.6f}, 실제 = {x_final-2:.6f}")
▶ 실행 결과 — 위 코드를 실행한 출력 (직접 실행: Windows · Python 3.11.2 · numpy 2.4.6, 앞 블록부터 순서대로 실행)
step 1: grad= 16.0000 x= 8.4000 f(x)= 41.9600
step 2: grad= 12.8000 x= 7.1200 f(x)= 27.2144
step 3: grad= 10.2400 x= 6.0960 f(x)= 17.7772
step 10: grad= 2.1475 x= 2.8590 f(x)= 1.7379
step 20: grad= 0.2306 x= 2.0922 f(x)= 1.0085
step 30: grad= 0.0248 x= 2.0099 f(x)= 1.0001
최종 x=2.009904 f(x)=1.000098 (정답 x=2, f=1)
이론 예측 x-2 = 8*(0.8)^30 = 0.009904, 실제 = 0.009904
📄 00_04_derivative_gradient_descent.py L261–L291 · Block 8 (📍 5단계 — 코드로 구현하고 실전에 적용하기)
import numpy as np
# 00-03과 동일한 데이터: (1,2), (2,3), (3,5)
X = np.array([1.0, 2.0, 3.0])
Y = np.array([2.0, 3.0, 5.0])
m = len(X) # 데이터 개수
w, b = 0.0, 0.0 # 파라미터 초기화 (아무 값이나 OK)
lr = 0.1
for step in range(1, 1001):
# ── ① 순전파: 예측하고 오차 구하기 ─────────────────
pred = w * X + b # ŷ = wx + b (벡터 3개 한 번에)
e = pred - Y # 잔차 e = ŷ - y
# ── ② 역전파: 공식 ⑨를 그대로 코드로 ───────────────
gw = (2 / m) * np.sum(e * X) # ∂L/∂w = (2/m)Σ eᵢxᵢ
gb = (2 / m) * np.sum(e) # ∂L/∂b = (2/m)Σ eᵢ
# ── ③ 업데이트: 경사하강 한 걸음 ───────────────────
w = w - lr * gw
b = b - lr * gb
if step in (1, 10, 100, 500, 1000):
mse = np.mean((w * X + b - Y) ** 2)
print(f"step {step:4d}: w={w:.6f} b={b:.6f} MSE={mse:.6f}")
# ── ④ 00-03의 정규방정식 답과 비교 검증 ────────────────
A = np.column_stack([X, np.ones(m)])
exact = np.linalg.lstsq(A, Y, rcond=None)[0]
print("정규방정식(00-03) 정답:", np.round(exact, 6))
▶ 실행 결과 — 위 코드를 실행한 출력 (직접 실행: Windows · Python 3.11.2 · numpy 2.4.6, 앞 블록부터 순서대로 실행)
step 1: w=1.533333 b=0.666667 MSE=0.216296
step 10: w=1.405646 b=0.547822 MSE=0.062155
step 100: w=1.489440 b=0.357339 MSE=0.055638
step 500: w=1.499999 b=0.333335 MSE=0.055556
step 1000: w=1.500000 b=0.333333 MSE=0.055556
정규방정식(00-03) 정답: [1.5 0.333333]
📝 실행 메모
👉 Open in Colab — 00_04_derivative_gradient_descent.ipynb · 설치 없이 브라우저에서 셀을 위에서부터 순서대로 실행하세요.
git clone https://github.com/otoloism/Zero_to_ML_Master.git
cd Zero_to_ML_Master
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r book/requirements.txt # 책 전체용 (PyTorch 등 포함, 용량 큼)
# 이 장만 실행한다면 이것만으로 충분합니다: pip install numpy
cd book/00-math-overview-for-beginners/00-04-derivative-gradient-descent
python 00_04_derivative_gradient_descent.py
📦 이 장의 일부 블록은 JAX, PyTorch, TensorFlow 이(가) 필요합니다. 설치하지 않으면 해당 블록만 오류가 나고 나머지는 정상 실행됩니다.
⭐ 코드가 도움이 되셨다면 GitHub 저장소에 Star를 눌러 주세요. 새 장의 코드가 올라올 때 가장 먼저 알 수 있습니다.
🍴 직접 고쳐 보며 실험하고 싶다면 Fork 해서 내 저장소에서 마음껏 바꿔 보세요. 오류 제보·개선 제안은 Issues로 환영합니다!
다음 글은 00-05 확률·베이즈 정리 (1부) — 병원 검사의 함정 🏥 입니다. 정확도 99%짜리 검사에서 양성이 나왔는데 실제 환자일 확률은 겨우 1.94% 라는 충격적인 결과를, 확률 → 조건부 확률 → 베이즈 정리 순서로 차근차근 파헤칩니다.
⬅️ 이전 글: #05 미분 쉽게 이해하기 — 수치미분·연쇄법칙·그래디언트 (딥러닝 수학, 1부) \
➡️ 다음 글: #07 베이즈 정리 쉽게 이해하기 — 확률·조건부 확률과 병원 검사의 함정 (1부)
이 시리즈는 수학을 놓았던 비전공자가 AI를 직접 구현하기까지의 공부 기록을 그대로 옮긴 연재입니다. 원문 책, 코드, 개념 사전을 함께 보면 훨씬 빨리 늡니다. 🚀
| 바로가기 | 이런 분께 | |
|---|---|---|
| 📘 책 (위키독스, 무료) | Zero to 머신러닝 딥러닝 Master | 5권 · 20개 장 · 163개 절 전체를 순서대로 읽고 싶은 분 |
| 📄 이 글의 원문 | 00-04 미분·경사하강법 — 안개 낀 산에서 내려오기 (2부) | 연습문제·해설까지 전부 보고 싶은 분 |
| 💻 실습 코드 (GitHub) | otoloism/Zero_to_ML_Master | .py·.ipynb를 Colab에서 바로 돌려보고 싶은 분 |
| 🏠 머신러닝 백과사전 | mldict.net | 본문 속 핵심 개념을 카드로 다시 확인하고 싶은 분 |
| ✍️ 기술 블로그 | wikidocs.net/blog/@mldict | 새 글 알림을 가장 먼저 받고 싶은 분 |
⭐ 도움이 되셨다면 velog 팔로우와 GitHub Star를 눌러 주세요. 다음 글을 쓰는 가장 큰 힘이 됩니다. 궁금한 점은 댓글로 편하게 남겨 주세요! 🙌