개요
22~24편은 어떤 구조를 쓸 것인가의 이야기였다. 이번 글의 출발점은 이 한 줄이다.
같은 구조, 같은 데이터셋인데 성능이 크게 다르다. 왜?
좋은 모델을 골랐다고 좋은 결과가 나오지 않는다. 학습 과정에서 불안정, 과적합, 느린 수렴이 발생한다. 강의에서 이걸 요리에 비유했는데 꽤 정확하다.
재료(데이터)와 레시피(구조)가 같아도, 불 세기와 손맛(학습 전략)이 다르면 결과가 다르다.
이번 글은 그 손맛에 해당하는 것들 — 활성화 함수, 데이터 전처리, 초기화, 정규화, 학습률, 하이퍼파라미터 탐색 — 을 순서대로 본다. 마지막에는 실습에서 쓰는 전이학습까지 붙인다.
교육 자료는 대외비라 슬라이드 이미지나 예제 데이터는 싣지 않는다. 개념 흐름만 내가 이해한 방식으로 재구성했고, 그림은 전부 생성했다.
1. 학습에서 자주 겪는 세 가지 문제
| 문제 | 증상 |
|---|---|
| 학습 불안정 | 손실이 급격히 커지거나(폭발), 더 이상 줄지 않거나(멈춤), 최적값에 못 가고 끝남 |
| 과적합 | 훈련 데이터에는 맞는데 검증·실전 성능이 떨어짐 |
| 느린 수렴 | 결국 도달은 하는데 불필요하게 많은 에폭을 태움 |
이 셋의 원인이 서로 다르고, 처방도 다르다. 아래 항목들은 각각 이 중 하나를 겨냥한다.
2. 활성화 함수 — 일단 ReLU부터
먼저 왜 필요한지부터. 활성화 함수가 없으면 신경망은 아무리 깊어도 선형 모델 하나다. 22편에서 본 그 이야기다.
Sigmoid — 문제 세 가지
$$ a(x) = \frac{1}{1 + e^{-x}} $$
출력이 0~1이라 확률로 읽기 좋다. 그런데 문제가 셋이다.
- 기울기 소실 — 입력이 아주 크거나 아주 작으면 출력이 0이나 1에 딱 붙는다. 그 구간에서 기울기가 0이라 역전파 신호가 끊긴다
- 출력이 항상 양수 — 기울기 평균이 한쪽으로 치우쳐 편향된 업데이트가 발생한다
- 계산 비용 — exp 연산이 들어가서 단순한 max보다 비싸다
tanh — 하나만 해결
출력 범위가 [-1, 1]이라 0을 중심으로 대칭이다. 2번 문제는 풀린다. 그런데 1번(기울기 소실)과 3번(exp 비용)은 그대로다.
ReLU — 판을 바꾼 단순함
$$ f(x) = \max(0, x) $$
- 양수 영역에서 포화되지 않는다 → 기울기 소실이 크게 줄어든다
- max 연산 하나라 매우 빠르다
- 실제로 Sigmoid·tanh 대비 6배 이상 빠른 수렴이 보고됐다
단점은 죽은 뉴런(Dead ReLU)이다. 입력이 계속 음수인 뉴런은 항상 0만 내놓고, 기울기도 0이라 영원히 안 깨어난다.
Leaky ReLU와 ELU
Leaky ReLU는 음수 영역에 작은 기울기를 준다.
$$ f(x) = \max(0.01x,\ x) $$
죽지는 않는데, 0.01이라는 값의 근거가 임의적이고 여전히 0 중심 대칭은 아니다. ELU는 음수 영역을 지수 곡선으로 만들어 평균 출력을 0 근처로 보내지만, 계산이 복잡해지고 α라는 하이퍼파라미터가 하나 더 생긴다.
그래서 결론
큰 고민 말고 일단 ReLU부터. 성능 경쟁 중이라면 개선판(Leaky ReLU, ELU, SELU)을 시도. Sigmoid와 tanh는 전문가 영역.
강의에서 이 결론을 아주 단호하게 말한 게 좋았다. 실무에서는 “뭘 고를까” 고민하는 시간이 제일 아깝다.
3. 데이터 전처리 — 학습·검증·테스트에 똑같이
의외로 여기서 사고가 많이 난다. 맞춰야 할 것들이다.
| 항목 | 내용 |
|---|---|
| 해상도 | 모델 입력 크기로 통일 |
| 색상 | RGB / BGR / Grayscale 중 하나로 통일 |
| 밝기 | 스케일 범위 통일 |
| 정규화 | 평균 0, 표준편차 1 |
basic_rgb_pipeline = transforms.Compose([
transforms.Resize(256, interpolation=InterpolationMode.BICUBIC), # 크기 통일
transforms.CenterCrop(224), # 센터 크롭
transforms.ToTensor(), # 텐서화
transforms.Normalize(IMAGENET_MEAN, IMAGENET_STD) # 정규화
])
흑백 이미지를 컬러 모델에 넣어야 한다면 3채널로 복제한다.
grayscale_pipeline = transforms.Compose([
transforms.Resize(256, interpolation=InterpolationMode.BICUBIC),
transforms.CenterCrop(224),
transforms.Grayscale(num_output_channels=3), # (H,W) -> (3,H,W)
transforms.ToTensor(),
transforms.Normalize(IMAGENET_MEAN, IMAGENET_STD)
])
모델마다 관행이 다르다
| 모델 | 전처리 방식 |
|---|---|
| AlexNet | 학습 데이터 전체의 평균 이미지를 빼줌 |
| VGG | 채널별 평균을 빼줌 (R, G, B 각각) |
| ResNet | 채널별 평균을 빼고 채널별 표준편차로 나눔 |
ResNet 방식이 한 단계 더 나아간 것이다. 평균만 맞추는 게 아니라 스케일까지 맞춰서 학습 효율을 높인다. 사전학습 모델을 가져다 쓸 때는 그 모델이 학습할 때 쓴 전처리를 그대로 따라야 한다. 여기 안 맞추면 성능이 이유 없이 떨어진다.
4. 초기화 — 첫 단추
0으로 초기화하면?
모든 가중치와 편향을 0으로 두면 모든 뉴런의 출력이 같고, 기울기도 같다. 뉴런들이 서로 달라질 수가 없다. 학습이 아예 안 된다. 뉴런을 100개 두든 1개 두든 똑같다.
작은 랜덤 값으로 하면?
W = 0.01 * np.random.randn(Din, Dout)
얕은 모델에서는 잘 된다. 그런데 깊어지면 문제가 생긴다. 층을 지날 때마다 작은 값이 계속 곱해져서 출력 분포가 0으로 쪼그라든다. 강의 자료의 층별 표준편차가 0.49 → 0.29 → 0.18 → 0.11 → 0.07 → 0.05로 줄어드는 걸 보여준다.
출력이 0으로 수렴하면 역전파 기울기도 0으로 수렴한다. 학습이 멈춘다. 반대로 초깃값이 크면 기울기가 폭발한다.
Xavier 초기화 — 분산을 맞추자
W = np.random.randn(Din, Dout) / np.sqrt(Din)
표준편차를 1/√(입력 차원)으로 맞춘다. 아이디어는 이거다.
입력 차원(뉴런 수)이 늘어나면 더해지는 항이 많아져서 출력 분산이 커진다. 그러니 입력 차원만큼 미리 나눠두면 출력의 분산이 입력의 분산과 같아진다.
실제로 층별 표준편차가 0.63 → 0.49 → 0.41 → 0.36 → 0.32 → 0.30으로, 훨씬 완만해진다.
He 초기화 — ReLU를 위한 보정
Xavier에 ReLU를 붙이면 다시 망가진다. 층별 표준편차가 0.58 → 0.41 → 0.30 → 0.21 → 0.15 → 0.10으로 다시 줄어든다. 왜?
Xavier는 출력이 0을 중심으로 대칭이라는 가정 위에 있다. 그런데 ReLU는 음수를 잘라버린다. 절반이 사라지니 대칭이 깨진다.
He 초기화는 잘려나간 절반만큼 2를 곱해 보정한다.
W = np.random.randn(Din, Dout) * np.sqrt(2) / np.sqrt(Din)
층이 깊어져도 표준편차가 0.83 → 0.83 → 0.83 → 0.81 → 0.81 → 0.81로 유지된다. ReLU를 쓰는 모델의 사실상 기본값이다.
ResNet의 초기화 요령
23편에서 잔차 블록을 봤다. 여기 초기화에도 요령이 있다.
잔차 블록의 첫 번째 conv는 He 초기화, 두 번째 conv는 0으로 초기화
두 번째를 0으로 두면 처음에 F(x) = 0이 되어 출력 = 입력이다. 즉 학습 시작 시점에 모델이 항등 함수에서 출발한다. 분산이 안정된 상태로 안전하게 출발하는 것이다.
이게 왜 필요하냐면, 지름길로 입력이 그대로 더해지기 때문에 초기화가 잘못되면 깊은 층으로 갈수록 출력값이 계속 증폭될 수 있다. Var(x + F(x)) = Var(x) 가 되도록 유도하는 것이다.
5. 정규화 — 과적합을 줄이는 손잡이
학습은 되는데 검증 오차가 오히려 오른다면 과적합이다. 실전에서 가장 자주 겪는 문제다.
가중치 감소 (Weight Decay)
직관은 간단하다.
큰 가중치는 과적합을 부른다 → 줄이자
가중치가 크면 특정 입력에 과도하게 반응하고, 결국 훈련 데이터를 외워버린다. 그래서 가중치 크기에 비례한 페널티를 손실에 더한다.
| 방식 | 페널티 | 결과 |
|---|---|---|
| L2 (Ridge) | 가중치의 제곱을 모두 더해 페널티 | 극단적으로 큰 값 방지, 모든 가중치가 골고루 작아짐 |
| L1 (Lasso) | 가중치의 절댓값을 모두 더해 페널티 | 중요하지 않은 가중치가 완전히 0이 됨 → 희소한 모델 |
| Elastic Net | L2와 L1을 β 비율로 섞음 | 둘의 절충, β가 크면 L2 효과 |
여기서 짚고 갈 함정 하나.
가중치 감소를 완벽하게 만족시키면 모든 가중치가 0인 모델이 된다. 그런데 그건 아무것도 학습하지 않은 모델이다.
즉 정규화는 그 자체로 목표가 아니다. 원래 손실(예: 교차 엔트로피)과 함께 작동할 때만 의미가 있다. 균형이 핵심이다. 훈련 오차도 줄이면서, 가중치도 적당히 작게.
드롭아웃
학습 중에 뉴런 일부를 확률적으로 끈다(0으로 만든다).
- 직관: 무작위로 뉴런을 비워서 특정 뉴런에 과도하게 의존하지 않게 만든다
- 매 스텝마다 다른 네트워크 구조가 샘플링되는 효과 → 여러 작은 모델의 앙상블과 비슷
여기 자주 놓치는 디테일이 하나 있다.
학습 때는 뉴런을 끄고, 테스트 때는 전부 쓴다. 그러면 테스트 시 출력 크기가 학습 때보다 커져서 분포가 어긋난다.
해결책이 Inverted Dropout이다. 학습 시 살아남은 뉴런의 출력을 1/(1−p)배로 미리 키워둔다. p=0.5면 ×2. 이러면 테스트 때 아무것도 안 해도 된다. 예전 방식(테스트 때 (1−p)를 곱함)은 매 추론마다 곱셈이 필요해서 서비스 속도에 손해였다.
드롭아웃이 항상 좋은 건 아니다.
- RNN·LSTM 같은 시계열에서는 변형된 기법이 필요하다
- 배치 정규화가 이미 적용된 모델에서는 비선호된다 (이미 강한 정규화가 걸려 있다)
실제로 23편에서 본 ResNet 학습 레시피에 “드롭아웃 미사용”이 명시돼 있었다. 배치 정규화를 쓰기 때문이다.
6. 학습률 — 가장 중요한 하이퍼파라미터
같은 모델, 같은 전처리, 같은 모듈을 써도 학습률 하나로 결과가 완전히 갈린다. 잘못 잡으면 학습이 통째로 실패한다.
| 학습률 | 증상 |
|---|---|
| 너무 큼 | 손실이 발산하거나 위에서 진동 |
| 약간 큼 | 초반엔 빠른데 일찍 정체 |
| 너무 작음 | 안정적이지만 너무 느림 |
| 적절 | 꾸준히 내려가 낮은 값에 안착 |
기본 전략
큰 값에서 시작해서 에폭이 지날수록 줄인다
처음엔 넓게 훑고, 나중엔 미세하게 조율한다는 뜻이다.
| 방식 | 줄이는 모양 | 특징 |
|---|---|---|
| 계단식 | k 에폭마다 γ배 | 직관적, 그런데 변경 지점을 여러 개 정해야 하고 곡선이 불안정할 수 있음 |
| 코사인 | 코사인 곡선을 따라 부드럽게 | 처음엔 크게, 나중엔 미세하게. 손이 덜 감 |
| 선형 | 직선으로 0까지 | 일정하게 줄여 수렴. 사전학습·미세조정에서 많이 씀 |
| 역제곱근 | 시간의 제곱근에 반비례 | 초반에 크게, 이후 느리게 |
| 일정값 유지 | 줄이지 않음 | 데이터가 많고 과적합 위험이 없을 때 |
조기 종료 (Early Stopping)
검증 손실을 지켜보다가, 더 이상 좋아지지 않고 오히려 나빠지기 시작하면 멈춘다. 과적합을 막는 가장 값싼 방법이다.
7. 하이퍼파라미터 — 다 해볼 수는 없다
파라미터와 하이퍼파라미터를 먼저 구분하자.
| 정의 | 예 | |
|---|---|---|
| 파라미터 | 학습으로 모델이 스스로 얻는 값 | 가중치, 편향 |
| 하이퍼파라미터 | 학습 전에 사람이 정하는 값 | 학습률, 배치 크기, 에폭 수 |
주요 하이퍼파라미터는 세 갈래다.
| 분류 | 항목 |
|---|---|
| 학습 관련 | 학습률(가장 중요), 감소 방식, 배치 크기, 에폭 수 |
| 최적화 관련 | 옵티마이저(SGD/Adam/AdamW), 모멘텀, 가중치 감소 |
| 모델 구조 관련 | 네트워크 깊이, 채널 수, 드롭아웃 비율, 정규화 방식 |
그리드 탐색 vs 랜덤 탐색
그리드 탐색은 후보를 격자처럼 전부 조합한다. 직관적인데 낭비가 크다. 성능에 별 영향을 안 주는 축에서도 똑같이 여러 값을 시도하기 때문이다.
랜덤 탐색은 공간에서 무작위로 뽑는다. 그래서 중요한 축의 값을 더 다양하게 시험하게 된다. 같은 시도 횟수라면 랜덤 쪽이 유리한 경우가 많다.
실전 체크리스트
이 부분이 이번 강의에서 가장 실무적이라고 느꼈다. 빅테크조차 모든 조합을 실험하지 않는다. 순서가 있다.
① 데이터 입력이 정상인지 — 초기 손실값 확인
분류 문제에서 교차 엔트로피 손실을 쓴다면, 학습 전 초기 손실은 log(C) 근처여야 정상이다. C는 클래스 개수다. 아무것도 학습하지 않은 상태에서는 모든 클래스에 균등한 확률을 주기 때문이다.
크게 벗어나면 데이터 로딩이나 라벨이 잘못된 것이다. 이 한 줄로 몇 시간을 아낄 수 있다.
② 작은 샘플로 일부러 과적합시켜 보기
정규화를 끄고, 작은 학습 샘플에 대해 정확도 100%가 나올 때까지 돌린다.
- 손실이 안 줄면 → 학습률/초기화 문제, 모델 구조 오류, 활성화 함수 이슈, 코드 버그
- 손실이 폭발하면 → 학습률을 줄이고 초기화를 바꾼다
외울 수조차 없다면 배울 수도 없다. 좋은 진단이다.
③ 손실이 실제로 줄어드는 학습률 찾기
모든 데이터를 쓰되 100 iteration 정도만 짧게 돌려서, 손실이 빠르게 줄어드는 학습률을 고른다. 탐색 범위는 로그 스케일로 1e-1, 1e-2, 1e-3, 1e-4.
직관: 손실이 실제로 줄어드는 학습률부터 시작하라
④ 좋은 조합을 1~5 에폭 짧게 비교
좋은 조합은 초기 몇 에폭 안에 손실이 줄어든다. 끝까지 다 돌릴 필요가 없다. 단, 검증 세트 성능으로 봐야 한다. 학습 손실만 보면 일반화 성능을 알 수 없다.
⑤ 상위 조합을 10~20 에폭까지 추가 학습
이때는 학습률 감소를 적용하지 않는다. 감소를 쓰면 성능은 좋아지지만 속도가 느려지고, 탐색 단계에서는 순수한 조합의 성능만 보고 싶기 때문이다.
⑥ 곡선을 읽고 처방한다
| 곡선 모양 | 진단 | 처방 |
|---|---|---|
| 초반 손실이 평평 | 초기값 문제 | 초기화 방식 변경 |
| 떨어지다 정체 | 학습률이 큼 | 학습률 감소 시도 |
| 감소 적용 후 즉시 멈춤 | 너무 일찍 줄임 | 감소 시점을 뒤로 |
| 학습·검증 정확도 격차 큼 | 과적합 | 정규화·증강·조기 종료 |
| 두 곡선 차이가 거의 없음 | 모델이 약함 | 더 큰 모델, 더 오래 |
| 둘 다 계속 상승 | 정상 | 계속 학습 |
8. 실전 — 전이학습으로 시작하기
여기까지가 이론이고, 실제로 이미지 모델을 만들 때는 바닥부터 학습하지 않는다. 7편에서 다룬 전이학습이 여기서 다시 나온다.
| 용어 | 정의 |
|---|---|
| 사전 학습(Pre-trained) | 대규모 범용 데이터로 모델을 처음부터 학습시킨 단계 (ResNet, BERT, GPT 등) |
| 전이 학습(Transfer Learning) | 사전 학습된 모델을 기반으로 특정 문제에 맞게 재학습 (LP, FT, Adapter, LoRA 등) |
선형 프로빙 vs 파인 튜닝
| 구분 | 선형 프로빙 (LP) | 파인 튜닝 (FT) |
|---|---|---|
| 학습 범위 | 마지막 선형 레이어만 | 전체 파라미터 |
| 자원 소모 | 적음 → 빠르고 가벼움 | 많음 → 시간·자원 필요 |
| 장점 | 표현 품질 비교에 좋음, 과적합 위험 낮음 | 성능 상한이 높음, 데이터 특화 가능 |
| 단점 | 비선형 구조나 복잡한 상호작용을 놓칠 수 있음 | 계산량 크고 과적합 위험 ↑ |
| 대표 활용 | 표현 품질 측정, 레이어별 정보량 분석 | 실제 서비스 모델 학습 |
데이터가 적으면 LP, 충분하면 FT. 이것도 결국 성능과 비용의 저울이다. 이 시리즈에서 계속 반복돼 온 그 저울이다.
데이터 증강
기존 학습 데이터를 변형해 새 샘플을 만든다. 데이터 수집 비용 없이 데이터를 늘리는 방법이다.
| 도메인 | 기법 |
|---|---|
| 이미지 | 회전, 뒤집기, 자르기(Crop), 랜덤 지우기(Random Erasing) |
| 텍스트 | 무작위 삭제·삽입, 역번역(Back-translation) |
| 음성 | 시간 축 변형(Time Stretch), 잡음 추가 |
24편에서 ViT가 데이터 증강 없이는 성능이 크게 떨어진다고 했던 부분이 여기 연결된다. 구조에 사전 지식이 없을수록 데이터로 채워야 하고, 증강이 그 데이터를 늘려준다.
학습률 스케줄러 코드
# step_size=5: 5 에포크마다
# gamma=0.1: 학습률을 0.1배로 줄임
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)
이러면 1~5 에폭은 lr=0.0005, 6~10 에폭은 0.00005, 11~15 에폭은 0.000005가 된다. 위에서 본 계단식 감소가 코드로는 이 한 줄이다.
정리
- 같은 구조·같은 데이터라도 학습 전략이 성능을 가른다
- 활성화 함수 — 큰 고민 말고 ReLU. Sigmoid의 문제는 기울기 소실·양수 편향·exp 비용
- 전처리 — 학습/검증/테스트에 똑같이. 사전학습 모델은 그 모델의 전처리를 그대로 따라야 한다
-
초기화 — 0은 학습 불가, 작은 랜덤은 깊으면 붕괴. Xavier는 분산 맞추기, He는 ReLU 보정판
- ResNet은 잔차 블록 두 번째 conv를 0으로 초기화해 항등 함수에서 출발시킨다
- 정규화 — L2는 골고루 작게, L1은 희소하게. 드롭아웃은 Inverted 방식으로, 배치 정규화와는 함께 잘 안 씀
- 학습률 — 가장 중요. 크게 시작해 줄인다(계단식·코사인·선형). 조기 종료로 과적합 차단
- 탐색 순서 — 초기 손실 log(C) 확인 → 작은 샘플 과적합 → 학습률 찾기 → 짧게 비교 → 길게 학습 → 곡선 읽기
- 전이학습 — LP는 가볍고 빠르게, FT는 무겁고 강하게. 데이터 증강은 그 사이를 메운다
참고 자료
한줄 평
- 구조를 고르는 재미보다 훨씬 덜 화려한 이야기인데, 실제로 모델이 안 될 때 살려주는 건 언제나 이쪽이었다. 초기 손실이 log(C)인지 확인하라는 한 줄, 이건 앞으로 계속 써먹게 될 것 같다.