개요

22~24편은 어떤 구조를 쓸 것인가의 이야기였다. 이번 글의 출발점은 이 한 줄이다.

같은 구조, 같은 데이터셋인데 성능이 크게 다르다. 왜?

좋은 모델을 골랐다고 좋은 결과가 나오지 않는다. 학습 과정에서 불안정, 과적합, 느린 수렴이 발생한다. 강의에서 이걸 요리에 비유했는데 꽤 정확하다.

재료(데이터)와 레시피(구조)가 같아도, 불 세기와 손맛(학습 전략)이 다르면 결과가 다르다.

이번 글은 그 손맛에 해당하는 것들 — 활성화 함수, 데이터 전처리, 초기화, 정규화, 학습률, 하이퍼파라미터 탐색 — 을 순서대로 본다. 마지막에는 실습에서 쓰는 전이학습까지 붙인다.

교육 자료는 대외비라 슬라이드 이미지나 예제 데이터는 싣지 않는다. 개념 흐름만 내가 이해한 방식으로 재구성했고, 그림은 전부 생성했다.


1. 학습에서 자주 겪는 세 가지 문제

문제 증상
학습 불안정 손실이 급격히 커지거나(폭발), 더 이상 줄지 않거나(멈춤), 최적값에 못 가고 끝남
과적합 훈련 데이터에는 맞는데 검증·실전 성능이 떨어짐
느린 수렴 결국 도달은 하는데 불필요하게 많은 에폭을 태움

이 셋의 원인이 서로 다르고, 처방도 다르다. 아래 항목들은 각각 이 중 하나를 겨냥한다.


2. 활성화 함수 — 일단 ReLU부터

먼저 왜 필요한지부터. 활성화 함수가 없으면 신경망은 아무리 깊어도 선형 모델 하나다. 22편에서 본 그 이야기다.

활성화 함수 비교

Sigmoid — 문제 세 가지

$$ a(x) = \frac{1}{1 + e^{-x}} $$

출력이 0~1이라 확률로 읽기 좋다. 그런데 문제가 셋이다.

  1. 기울기 소실 — 입력이 아주 크거나 아주 작으면 출력이 0이나 1에 딱 붙는다. 그 구간에서 기울기가 0이라 역전파 신호가 끊긴다
  2. 출력이 항상 양수 — 기울기 평균이 한쪽으로 치우쳐 편향된 업데이트가 발생한다
  3. 계산 비용 — 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)인지 확인하라는 한 줄, 이건 앞으로 계속 써먹게 될 것 같다.