BERT 뉴스 분류 #4 Label Smoothing, 학습률 스케줄링, Early Stopping

BERT 뉴스 분류 #4 Label Smoothing, 학습률 스케줄링, Early Stopping #

#2026-03-01


#1 Label Smoothing

일반적인 분류 학습에서 정답 레이블은 원-핫 벡터다. Business 기사라면 [0, 0, 1, 0]이다. 이건 모델에게 “Business일 확률이 정확히 100%이고 나머지는 정확히 0%가 되어야 한다"고 요구하는 것이다.

이 요구가 왜 문제가 되는지 생각해보자. 모델이 Business에 99%를 부여해도 [0, 0, 1, 0]과는 여전히 차이가 있으니 손실이 남아 있다. 모델은 이 잔여 손실을 줄이려고 logit을 점점 더 극단적으로 만든다. Business의 logit을 8, 10, 15로 키우고 나머지를 -5, -10으로 밀어낸다. Softmax를 통과하면 [0.00, 0.00, 1.00, 0.00]에 수렴한다. 수학적으로는 정답에 가까워지지만, 이 과정에서 모델은 훈련 데이터에 과도하게 확신을 갖게 된다. 새로운 데이터에서 애매한 기사가 나왔을 때 “99.9% Business"라고 자신만만하게 틀리는 모델이 되는 것이다.

일반 Cross-Entropy의 문제:
정답 라벨이 Business(2번)인 경우, 목표 확률 분포가 `[0, 0, 1, 0]`이다. 모델에게 "Business에 100% 확신을 가져라"고 요구하는 것이다. 이 요구를 만족시키기 위해 모델은 점점 극단적인 logit을 만들어낸다.

학습 후반부:
logits: [-5.0, -4.0, 8.0, -3.0]
probs:  [0.00, 0.00, 1.00, 0.00]   ← 과도한 확신 → 과적합

Label Smoothing은 이 문제를 해결한다. 정답 레이블을 살짝 “부드럽게” 만든다.

training_args = TrainingArguments(
    label_smoothing_factor=0.1,   # ← 추가
    # ...
)

ε=0.1로 설정하면 정답 클래스의 목표가 1.0에서 0.925로 낮아지고, 나머지 클래스의 목표가 0에서 0.025로 올라간다. [0, 0, 1, 0]이 [0.025, 0.025, 0.925, 0.025]가 되는 것이다.

ε=0.1로 설정하면 목표 분포:
  정답 클래스: 1.0 → 1.0 - ε + ε/K = 1.0 - 0.1 + 0.1/4 = 0.925
  나머지 클래스: 0.0 → ε/K = 0.1/4 = 0.025

[0, 0, 1, 0]  →  [0.025, 0.025, 0.925, 0.025]

이게 어떤 효과를 만드는가? 모델이 Business에 0.925 이상의 확률을 내놓으면 더 이상 보상이 없다. 목표 자체가 0.925이니까. logit을 극단적으로 키워봐야 손실이 줄어들지 않는다. 모델은 자연스럽게 적당한 수준의 확신에서 멈추게 된다.

이전 분석에서 본 정규화 기법들과 비교하면 이렇다. Dropout은 뉴런을 랜덤으로 꺼서 특정 뉴런에 대한 과도한 의존을 막았다. L2 정규화는 가중치가 커지는 것에 벌점을 줘서 모델의 복잡도를 제한했다. Label Smoothing은 출력의 확신도에 상한을 둬서 극단적인 예측을 막는다. 세 가지 모두 과적합을 방지하는 정규화 기법이지만 작용하는 지점이 다르다. Dropout은 중간 레이어에서, L2는 가중치에서, Label Smoothing은 출력단에서 작동한다.

#

#2 Warmup + 코사인 스케줄러

지금까지 학습률을 다양한 방식으로 조절하는 걸 봐왔다. VAE에서는 ExponentialDecay로 에포크마다 줄였고, 바로 이전 단계에서는 레이어 깊이에 따라 차등을 뒀다. 이번에는 학습의 시간 흐름에 따른 더 정교한 스케줄링을 본다.

먼저 Warmup이 왜 필요한지를 이해하자. BERT를 파인튜닝할 때 분류 헤드는 랜덤 초기화 상태에서 시작한다. 학습 초반에 이 랜덤한 분류 헤드가 역전파로 내보내는 그래디언트는 엉뚱한 방향을 가리키고 있다. 아직 아무것도 모르는 신입이 지시를 내리는 것과 같다. 이 시점에 학습률이 크면 이 엉뚱한 그래디언트가 BERT 본체의 사전학습 가중치를 크게 흔들어서 손상시킨다.

Warmup은 이걸 방지한다. 학습 시작 시 학습률을 0에서 출발해서 점진적으로 올린다. 전체 학습 스텝의 10%를 워밍업에 쓴다고 하면, 처음 10% 동안은 학습률이 0에서 목표값까지 천천히 올라간다. 이 기간 동안 분류 헤드가 어느 정도 합리적인 그래디언트를 만들어낼 수 있는 수준까지 안정화된다. 그 다음에야 본격적인 학습률로 전체 모델을 조정하기 시작한다.

# Warmup: 학습 초반에 랜덤 초기화된 분류 헤드가 엉뚱한 그래디언트를 뿜어낸다. 이 시기에 학습률이 크면 사전학습 가중치가 망가진다. 처음에는 학습률을 0에서 시작해 천천히 올린다.
스텝  0%:   lr = 0       (아직 준비 중)
스텝  5%:   lr = 목표 × 0.5
스텝 10%:   lr = 목표    (본격 학습 시작)

워밍업이 끝나면 학습률을 어떻게 줄여갈 것인가? 여기서는 코사인 감쇠를 사용했다.

training_args = TrainingArguments(
    warmup_ratio=0.1,             # 전체 스텝의 10%를 워밍업에 사용
    lr_scheduler_type="cosine",   # linear 대신 cosine 감쇠
    # ...
)

선형 감쇠와 코사인 감쇠의 차이는, 선형 감쇠는 학습률이 일정한 속도로 꾸준히 줄어든다. 직선으로 떨어지는 것이다. 단순하고 직관적이지만, 학습 중반부터 이미 학습률이 상당히 작아져서 미세 조정의 여지가 줄어든다.

코사인 감쇠는 다른 형태로 줄어든다. 코사인 곡선의 위쪽 절반을 떠올려보자. 처음에는 거의 수평에 가깝게 천천히 줄어들다가, 중반부에 약간 가팔라지고, 끝으로 갈수록 다시 완만해진다. 핵심적인 차이는 중반부에 있다. 선형 감쇠는 중반에 이미 학습률이 절반으로 떨어지지만, 코사인 감쇠는 중반에서도 상대적으로 높은 학습률을 유지한다. 이 덕분에 학습 중반까지 적극적인 학습이 지속되고, 후반부에서도 완전히 0에 수렴하기보다 약간의 학습률이 남아서 미세 조정이 계속된다.

파인튜닝에서 코사인 감쇠가 선호되는 이유가 여기 있다. 파인튜닝은 이미 좋은 가중치를 아주 살짝 조정하는 작업이다. 학습 후반부의 미세 조정이 특히 중요한데, 코사인 감쇠는 이 후반부에서 학습률이 너무 빨리 사라지지 않게 해준다.

# 코사인 감쇠 (vs 선형 감쇠):
선형 감쇠: lr이 직선으로 감소
  ████████████████████░░░░░░░░░░░░░░
  초반 크게 줄고 → 후반 더 줄고 (균일하게)

코사인 감쇠: lr이 코사인 곡선으로 감소
  ████████████████████▓▓▒▒░░░░░░░
  초반 천천히 줄고 → 후반 더 천천히 (미세 조정 지속)

# 코사인 스케줄러는 후반부에 학습률이 더 완만하게 줄어들어, 가중치의 미세한 조정이 오래 지속된다.

#

#4 Early Stopping — 과적합 자동 감지

이전 DNA 분석 챕터에서 매 10 에포크마다 훈련 성능과 검증 성능을 출력해서 사람이 직접 과적합 여부를 확인했다. 훈련 성능은 계속 오르는데 검증 성능이 정체되거나 떨어지면 과적합이 시작된 것이고, 학습을 멈춰야 한다.

Early Stopping은 이 판단을 자동화한다. 200 스텝마다 검증 세트의 F1을 계산하고, 이전 최고 기록과 비교한다. 개선되면 그 시점의 모델을 저장한다. 개선되지 않으면 카운터가 올라간다. patience=3이니까 3번 연속 개선이 없으면 “이제 더 이상 나아지지 않는다"고 판단하고 학습을 중단한다. 그리고 가장 F1이 높았던 체크포인트의 모델을 복원한다.

이 메커니즘의 아름다운 점은 에포크 수를 미리 정확히 결정하지 않아도 된다는 것이다. “10 에포크면 될까? 20 에포크면 될까?“라는 질문에 답할 필요가 없다. 그냥 넉넉하게 잡아놓으면 모델이 알아서 최적의 시점에 멈춘다. 너무 일찍 멈추면 학습이 부족하고, 너무 늦게 멈추면 과적합이 생기는데, Early Stopping이 그 사이의 최적점을 자동으로 찾아준다.

from transformers import EarlyStoppingCallback

training_args = TrainingArguments(
    evaluation_strategy="steps",      # 에포크 단위가 아닌 스텝 단위 평가
    eval_steps=200,                   # 200 스텝마다 검증
    save_strategy="steps",
    save_steps=200,
    load_best_model_at_end=True,
    metric_for_best_model="f1",
    greater_is_better=True,
)

trainer = Trainer(
    callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
    # patience=3: 검증 F1이 3번 연속 개선되지 않으면 중단
    # ...
)

evaluation_strategy=“steps"와 eval_steps=200은 에포크 단위가 아닌 스텝 단위로 평가한다는 뜻이다. 12만 건 데이터에 배치 크기 8이면 한 에포크가 15,000 스텝이다. 에포크 단위로 평가하면 과적합이 에포크 중간에 시작되어도 에포크가 끝나야 알 수 있다. 200 스텝마다 평가하면 훨씬 세밀하게 최적점을 잡을 수 있다.

스텝  200: F1=0.880  → 개선 (저장)
스텝  400: F1=0.891  → 개선 (저장)
스텝  600: F1=0.895  → 개선 (저장)
스텝  800: F1=0.893  → 개선 안 됨 (patience 1/3)
스텝 1000: F1=0.891  → 개선 안 됨 (patience 2/3)
스텝 1200: F1=0.890  → 개선 안 됨 (patience 3/3) → 학습 중단!
→ 스텝 600의 체크포인트 복원

#

#5 정리

이 네 가지 기법은 모두 같은 질문에 대한 다른 각도의 답이다. “사전학습된 모델을 새로운 과제에 맞게 조정할 때, 기존 지식을 보존하면서 새로운 능력을 추가하려면 어떻게 해야 하는가?”

Label Smoothing은 출력의 과신을 막아서 일반화를 돕는다. Warmup과 코사인 스케줄러는 학습의 시간 흐름에 따라 강도를 조절해서 초반의 불안정과 후반의 과적합을 동시에 방지한다. Gradual Unfreezing은 모델의 부분별로 학습 시작 시점을 다르게 해서 안정적인 조정을 보장한다. Early Stopping은 과적합이 시작되는 순간을 자동으로 감지해서 최적의 시점에 멈춘다.

이 모든 것이 합쳐져서, 이미 영어를 이해하는 BERT가 그 이해력을 잃지 않으면서도 뉴스 분류라는 새로운 일을 안정적으로 배울 수 있게 만드는 것이다.

#

#cf 레이어 동결, 레이어별 차등학습률, Gradual Unfreezing

셋은 같은 문제(“사전학습 레이어를 얼마나, 어떻게 건드릴 것인가”)에 대한 서로 다른 전략이다. 동시에 전부 적용하는 게 아니라, 상황에 따라 하나를 선택하거나 두 가지를 조합한다.

레이어 동결은 가장 단순하다. 하위 레이어를 완전히 얼리고 상위 레이어만 학습시킨다. 구현이 쉽고 효과도 분명하지만, 얼리느냐 마느냐의 이진적 선택만 가능하다.

레이어별 차등 학습률은 동결의 연속적 일반화다. 완전히 얼리는 대신 하위 레이어에 아주 작은 학습률을 주고, 상위로 갈수록 키운다. 모든 레이어가 조금씩은 조정되므로 동결보다 유연하지만, 학습률 배치를 설계해야 하는 복잡도가 있다.

Gradual Unfreezing은 시간 축을 추가한 전략이다. 처음에는 분류 헤드만 학습하고, 단계적으로 아래 레이어를 풀어간다. 동결 전략을 시간에 따라 변화시키는 것이다.

실무에서는 조합도 가능하다. 예를 들어 Gradual Unfreezing을 하면서 각 단계에서 해동된 레이어들에 차등 학습률을 적용하는 식이다. 하지만 “동결 + 차등 학습률"을 같은 레이어에 동시에 적용하는 건 모순이다. 동결된 레이어는 학습률이 아예 의미가 없으니까.

결국 셋은 복잡도와 유연성의 스펙트럼 위에 있다. 동결이 가장 단순하고, 차등 학습률이 중간이고, Gradual Unfreezing이 가장 정교하다. 데이터가 충분하고 실험 시간이 넉넉하면 정교한 전략을 시도하고, 빠르게 결과를 내야 하면 단순한 동결부터 시작하는 것이 일반적이다.