개념
과적합 스펙트럼(The Overfitting Spectrum)
모든 모델은 과소적합(underfitting, 패턴을 잡기에는 너무 단순함)과 과적합(overfitting, 너무 복잡해서 잡음까지 잡음) 사이 어딘가에 있습니다. 좋은 지점(sweet spot)은 중간이고, 정규화는 과적합 쪽에 있는 모델을 그 지점으로 밀어냅니다.
graph LR
Under["Underfitting<br/>Train: 60%<br/>Test: 58%<br/>Model too simple"] --> Good["Good Fit<br/>Train: 95%<br/>Test: 92%<br/>Generalizes well"]
Good --> Over["Overfitting<br/>Train: 99.9%<br/>Test: 65%<br/>Memorized noise"]
Dropout["Dropout"] -->|"Pushes left"| Over
WD["Weight Decay"] -->|"Pushes left"| Over
BN["BatchNorm"] -->|"Pushes left"| Over
Aug["Data Augmentation"] -->|"Pushes left"| Over
드롭아웃(Dropout)
드롭아웃(dropout)은 가장 단순한 정규화 기법이면서 해석도 우아합니다. 학습(training) 중 각 뉴런 출력(neuron output)을 확률 p로 0으로 만듭니다.
output = activation(z) * mask where mask[i] ~ Bernoulli(1 - p)
p = 0.5라면 매 순전파(forward pass)마다 뉴런 절반이 0이 됩니다. 네트워크는 어떤 뉴런이 살아 있을지 예측할 수 없으므로 중복 표현(redundant representation)을 학습해야 합니다. 특정 뉴런 조합에 의존하는 공적응(co-adaptation)을 막습니다.
앙상블(ensemble) 해석도 있습니다. N개 뉴런을 가진 네트워크에 dropout을 적용하면 켜지고 꺼지는 뉴런 조합에 따라 2^N개의 가능한 서브네트워크(subnetwork)가 생깁니다. dropout 학습은 서로 다른 미니배치(mini-batch)에서 이 2^N개 서브네트워크를 동시에 근사적으로 학습하는 것과 같습니다. 테스트 시점에는 모든 뉴런을 사용하고 dropout 없이 예측합니다. 이는 하나의 모델에서 거대한 앙상블의 평균 예측을 얻는 것과 비슷합니다.
실무에서는 학습 시점에 스케일을 보정해 두는 역드롭아웃(inverted dropout)을 사용합니다.
During training: output = activation(z) * mask / (1 - p)
During testing: output = activation(z)
이렇게 하면 테스트 코드(test code)가 드롭아웃 스케일링(dropout scaling)을 따로 알 필요가 없어 깔끔해집니다. 기본 비율(default rate)은 트랜스포머에서 p=0.1, 다층 퍼셉트론(MLP)에서 p=0.5, 합성곱 신경망(CNN)에서 p=0.2-0.3 정도를 사용합니다. 드롭아웃 비율이 클수록 정규화는 강해지지만 과소적합(underfitting) 위험도 함께 커집니다.
가중치 감쇠(Weight Decay, L2 Regularization)
손실(loss)에 모든 가중치 크기(weight magnitude)의 제곱을 더합니다.
total_loss = task_loss + (lambda / 2) * sum(w_i^2)
정규화 항(regularization term)의 기울기(gradient)는 lambda * w입니다. 즉 매 학습 단계(step)마다 각 가중치는 자기 크기에 비례한 만큼 0 쪽으로 줄어듭니다(shrink). 큰 가중치일수록 더 큰 벌점을 받게 되므로, 모델은 특정 가중치 하나가 모든 것을 지배하지 않는 해(solution)로 밀려갑니다.
이 방식이 일반화에 도움이 되는 이유는, 과적합된 모델일수록 학습 데이터의 잡음(noise)을 증폭하는 큰 가중치를 갖기 쉽기 때문입니다. 가중치 감쇠(weight decay)는 가중치를 작게 유지해 모델의 유효 용량(effective capacity)을 제한하고, 외워 둔 특이점(memorized quirk)이 아니라 견고하고 일반화 가능한 특징(robust, generalizable feature)에 의존하도록 유도합니다.
lambda 하이퍼파라미터(hyperparameter)는 정규화 강도를 조절합니다. 흔히 쓰이는 값은 다음과 같습니다.
- 트랜스포머에서 AdamW를 쓸 때:
0.01
- CNN에서 SGD를 쓸 때:
1e-4
- 심하게 과적합된 모델:
0.1
Lesson 06에서 다뤘듯, SGD에서는 L2 정규화(L2 regularization)와 가중치 감쇠(weight decay)가 수학적으로 동등(equivalent)하지만 Adam에서는 그렇지 않습니다. Adam 계열 옵티마이저(optimizer)를 사용할 때는 분리된 가중치 감쇠(decoupled weight decay)를 제공하는 AdamW를 항상 사용합니다.
배치 정규화(Batch Normalization)
BatchNorm은 다음 층으로 넘기기 전에 각 층 출력(layer output)을 미니배치(mini-batch) 전체 기준으로 정규화(normalize)합니다.
mu = (1/B) * sum(x_i)
sigma^2 = (1/B) * sum((x_i - mu)^2)
x_hat = (x_i - mu) / sqrt(sigma^2 + eps)
y = gamma * x_hat + beta
gamma와 beta는 학습 가능한 파라미터(learnable parameter)로, 필요하다면 네트워크가 정규화를 되돌릴 수 있게 해 줍니다. 이 두 파라미터가 없다면 모든 층 출력을 평균 0, 분산 1로 강제하게 되는데, 이것이 항상 네트워크가 원하는 표현은 아니기 때문입니다.
학습(training) 중에는 현재 미니배치의 평균(mean)과 분산(variance)을 사용하고, 추론(inference) 중에는 학습 중 누적해 둔 실행 평균(running average)을 사용합니다. 보통 모멘텀(momentum) 0.1의 지수 이동 평균(exponential moving average)을 사용하며, 이는 "기존 값의 90%에 새 값의 10%를 더한다"는 뜻입니다. 그래서 model.train()과 model.eval()의 차이가 중요합니다.
BatchNorm이 왜 작동하는지는 여전히 논쟁이 있는 주제입니다. 원 논문은 "내부 공변량 변화(internal covariate shift)"를 줄이기 때문이라고 설명했지만, Santurkar et al.(2018)은 이 설명이 틀렸음을 보였습니다. 실제 이유는 BatchNorm이 손실 지형(loss landscape)을 더 매끄럽게 만들기 때문입니다. 기울기(gradient)가 더 예측 가능해지고 립시츠 상수(Lipschitz constant)가 작아지며, 옵티마이저(optimizer)가 더 큰 단계(step)를 안전하게 밟을 수 있게 됩니다. 그래서 BatchNorm을 사용하면 더 높은 학습률(learning rate)을 쓰면서도 더 빠르게 수렴할 수 있습니다.
BatchNorm에는 근본적인 한계가 있습니다. BatchNorm은 배치 통계(batch statistics)에 의존하기 때문에 배치 크기(batch size)가 1이면 평균과 분산이 의미가 없어집니다. 배치가 작을 때(< 32)는 통계가 잡음(noise)이 많아져 오히려 성능을 해칠 수 있습니다. 이는 메모리 제약으로 배치 크기를 키우기 어려운 객체 탐지(object detection)나, 시퀀스 길이(sequence length)가 들쭉날쭉한 언어 모델링(language modeling)에서 특히 중요합니다.
층 정규화(Layer Normalization)
LayerNorm은 배치 차원이 아니라 특징 차원(feature dimension)을 기준으로 정규화합니다. 단일 샘플(sample) 안에서 계산이 이루어집니다.
mu = (1/D) * sum(x_j)
sigma^2 = (1/D) * sum((x_j - mu)^2)
x_hat = (x_j - mu) / sqrt(sigma^2 + eps)
y = gamma * x_hat + beta
여기서 D는 특징 차원(feature dimension)입니다. 각 샘플은 독립적으로 정규화되므로 배치 크기(batch size)에 의존하지 않습니다. 이 때문에 트랜스포머(transformer)는 BatchNorm 대신 LayerNorm을 사용합니다. 시퀀스 길이(sequence length)가 변하거나 생성(generation) 중에 배치 크기가 1이어도 학습과 추론의 계산이 동일합니다.
트랜스포머에서 LayerNorm은 각 셀프 어텐션 블록(self-attention block)과 피드 포워드 블록(feed-forward block) 뒤(Post-LN), 또는 그 앞(Pre-LN)에 적용됩니다. Pre-LN 방식이 학습 안정성이 더 좋습니다.
RMSNorm
RMSNorm은 평균 빼기(mean subtraction)를 뺀 LayerNorm입니다. Zhang & Sennrich(2019)가 제안했습니다.
rms = sqrt((1/D) * sum(x_j^2))
y = gamma * x / rms
끝입니다. 평균 계산도 없고 beta parameter도 없습니다. 관찰은 간단합니다. LayerNorm의 재중심화(re-centering, mean subtraction)는 모델 성능에 거의 기여하지 않지만 계산 비용은 듭니다. 이를 제거하면 거의 같은 정확도를 약 10% 낮은 오버헤드로 얻을 수 있습니다.
LLaMA, LLaMA 2, LLaMA 3, Mistral을 비롯해 대부분의 현대 LLM(modern LLM)은 LayerNorm 대신 RMSNorm을 사용합니다. 수십억 개 파라미터와 수조 개 토큰(token) 규모에서는 이 10% 절감이 매우 의미 있는 차이를 만듭니다.
정규화 비교(Normalization Comparison)
graph TD
subgraph "Batch Normalization"
BN_D["각 feature에 대해<br/>BATCH 방향으로 normalize"]
BN_S["Batch: [x1, x2, x3, x4]<br/>Feature 1: [x1f1, x2f1, x3f1, x4f1] normalize"]
BN_P["batch > 32 필요<br/>train/eval 계산 다름<br/>CNN에서 사용"]
end
subgraph "Layer Normalization"
LN_D["각 sample에 대해<br/>FEATURE 방향으로 normalize"]
LN_S["Sample x1: [f1, f2, f3, f4] normalize"]
LN_P["batch-independent<br/>train/eval 동일<br/>Transformers에서 사용"]
end
subgraph "RMS Normalization"
RN_D["LayerNorm과 비슷하지만<br/>mean subtraction 생략"]
RN_S["RMS로만 나눔<br/>centering 없음"]
RN_P["LayerNorm보다 약 10% 빠름<br/>정확도 유사<br/>LLaMA, Mistral에서 사용"]
end
정규화로서의 데이터 증강(Data Augmentation)
모델 수정이 아니라 데이터 수정입니다. 레이블을 보존하면서(label-preserving) 학습 입력을 변환합니다.
- 이미지(Images): 무작위 잘라내기(random crop), 좌우 뒤집기(flip), 회전(rotation), 색상 흔들기(color jitter), 컷아웃(cutout)
- 텍스트(Text): 동의어 치환(synonym replacement), 역번역(back-translation), 무작위 단어 삭제(random deletion)
- 오디오(Audio): 시간 늘리기(time stretch), 음높이 변경(pitch shift), 잡음 추가(noise addition)
효과는 정규화와 같습니다. 학습 집합의 유효 크기(effective size)를 키워 모델이 특정 예제를 외우기 어렵게 만듭니다. 원본 형태의 이미지를 한 번만 보는 모델은 외울 수 있습니다. 각 이미지를 50개의 증강 버전으로 보는 모델은 불변 구조(invariant structure)를 학습하도록 강제됩니다.
조기 종료(Early Stopping)
가장 단순한 정규화 기법입니다. 검증 손실(validation loss)이 증가하기 시작하는 시점에 학습을 멈추는 방식으로, 그 시점에는 아직 모델이 과적합되지 않은 상태입니다. 실무에서는 매 에폭(epoch)마다 검증 손실을 추적하면서 가장 좋은 모델을 저장해 두고, 인내 기간(patience window)을 함께 둡니다. 보통 5~20 에폭 정도를 두며, 이 기간 동안 검증 손실이 개선되지 않으면 학습을 멈추고 저장해 둔 최적 모델(best model)을 불러옵니다.
언제 무엇을 적용할까
flowchart TD
Gap{"Train-test<br/>accuracy gap?"} -->|"> 10%"| Heavy["강한 regularization"]
Gap -->|"5-10%"| Medium["중간 regularization"]
Gap -->|"< 5%"| Light["가벼운 regularization"]
Heavy --> D5["Dropout p=0.3-0.5"]
Heavy --> WD2["Weight decay 0.01-0.1"]
Heavy --> Aug["Aggressive data augmentation"]
Heavy --> ES["Early stopping"]
Medium --> D3["Dropout p=0.1-0.2"]
Medium --> WD1["Weight decay 0.001-0.01"]
Medium --> Norm["BatchNorm or LayerNorm"]
Light --> D1["Dropout p=0.05-0.1"]
Light --> WD0["Weight decay 1e-4"]
만들어 보기
Step 1: 드롭아웃(Dropout) — 학습 모드와 평가 모드(Train and Eval Mode)
import random
import math
class Dropout:
def __init__(self, p=0.5):
self.p = p
self.training = True
self.mask = None
def forward(self, x):
if not self.training:
return list(x)
self.mask = []
output = []
for val in x:
if random.random() < self.p:
self.mask.append(0)
output.append(0.0)
else:
self.mask.append(1)
output.append(val / (1 - self.p))
return output
def backward(self, grad_output):
grads = []
for g, m in zip(grad_output, self.mask):
if m == 0:
grads.append(0.0)
else:
grads.append(g / (1 - self.p))
return grads
Step 2: L2 가중치 감쇠(L2 Weight Decay)
def l2_regularization(weights, lambda_reg):
penalty = 0.0
for w in weights:
penalty += w * w
return lambda_reg * 0.5 * penalty
def l2_gradient(weights, lambda_reg):
return [lambda_reg * w for w in weights]
Step 3: 배치 정규화(Batch Normalization)
class BatchNorm:
def __init__(self, num_features, momentum=0.1, eps=1e-5):
self.gamma = [1.0] * num_features
self.beta = [0.0] * num_features
self.eps = eps
self.momentum = momentum
self.running_mean = [0.0] * num_features
self.running_var = [1.0] * num_features
self.training = True
self.num_features = num_features
def forward(self, batch):
batch_size = len(batch)
if self.training:
mean = [0.0] * self.num_features
for sample in batch:
for j in range(self.num_features):
mean[j] += sample[j]
mean = [m / batch_size for m in mean]
var = [0.0] * self.num_features
for sample in batch:
for j in range(self.num_features):
var[j] += (sample[j] - mean[j]) ** 2
var = [v / batch_size for v in var]
for j in range(self.num_features):
self.running_mean[j] = (1 - self.momentum) * self.running_mean[j] + self.momentum * mean[j]
self.running_var[j] = (1 - self.momentum) * self.running_var[j] + self.momentum * var[j]
else:
mean = list(self.running_mean)
var = list(self.running_var)
self.x_hat = []
output = []
for sample in batch:
normalized = []
out_sample = []
for j in range(self.num_features):
x_h = (sample[j] - mean[j]) / math.sqrt(var[j] + self.eps)
normalized.append(x_h)
out_sample.append(self.gamma[j] * x_h + self.beta[j])
self.x_hat.append(normalized)
output.append(out_sample)
return output
Step 4: 층 정규화(Layer Normalization)
class LayerNorm:
def __init__(self, num_features, eps=1e-5):
self.gamma = [1.0] * num_features
self.beta = [0.0] * num_features
self.eps = eps
self.num_features = num_features
def forward(self, x):
mean = sum(x) / len(x)
var = sum((xi - mean) ** 2 for xi in x) / len(x)
self.x_hat = []
output = []
for j in range(self.num_features):
x_h = (x[j] - mean) / math.sqrt(var + self.eps)
self.x_hat.append(x_h)
output.append(self.gamma[j] * x_h + self.beta[j])
return output
Step 5: RMSNorm
class RMSNorm:
def __init__(self, num_features, eps=1e-6):
self.gamma = [1.0] * num_features
self.eps = eps
self.num_features = num_features
def forward(self, x):
rms = math.sqrt(sum(xi * xi for xi in x) / len(x) + self.eps)
output = []
for j in range(self.num_features):
output.append(self.gamma[j] * x[j] / rms)
return output
Step 6: 정규화 적용/미적용 학습 비교(Training With and Without Regularization)
def sigmoid(x):
x = max(-500, min(500, x))
return 1.0 / (1.0 + math.exp(-x))
def make_circle_data(n=200, seed=42):
random.seed(seed)
data = []
for _ in range(n):
x = random.uniform(-2, 2)
y = random.uniform(-2, 2)
label = 1.0 if x * x + y * y < 1.5 else 0.0
data.append(([x, y], label))
return data
class RegularizedNetwork:
def __init__(self, hidden_size=16, lr=0.05, dropout_p=0.0, weight_decay=0.0):
random.seed(0)
self.hidden_size = hidden_size
self.lr = lr
self.dropout_p = dropout_p
self.weight_decay = weight_decay
self.dropout = Dropout(p=dropout_p) if dropout_p > 0 else None
self.w1 = [[random.gauss(0, 0.5) for _ in range(2)] for _ in range(hidden_size)]
self.b1 = [0.0] * hidden_size
self.w2 = [random.gauss(0, 0.5) for _ in range(hidden_size)]
self.b2 = 0.0
def forward(self, x, training=True):
self.x = x
self.z1 = []
self.h = []
for i in range(self.hidden_size):
z = self.w1[i][0] * x[0] + self.w1[i][1] * x[1] + self.b1[i]
self.z1.append(z)
self.h.append(max(0.0, z))
if self.dropout and training:
self.dropout.training = True
self.h = self.dropout.forward(self.h)
elif self.dropout:
self.dropout.training = False
self.h = self.dropout.forward(self.h)
self.z2 = sum(self.w2[i] * self.h[i] for i in range(self.hidden_size)) + self.b2
self.out = sigmoid(self.z2)
return self.out
def backward(self, target):
eps = 1e-15
p = max(eps, min(1 - eps, self.out))
d_loss = -(target / p) + (1 - target) / (1 - p)
d_sigmoid = self.out * (1 - self.out)
d_out = d_loss * d_sigmoid
for i in range(self.hidden_size):
d_relu = 1.0 if self.z1[i] > 0 else 0.0
d_h = d_out * self.w2[i] * d_relu
self.w2[i] -= self.lr * (d_out * self.h[i] + self.weight_decay * self.w2[i])
for j in range(2):
self.w1[i][j] -= self.lr * (d_h * self.x[j] + self.weight_decay * self.w1[i][j])
self.b1[i] -= self.lr * d_h
self.b2 -= self.lr * d_out
def evaluate(self, data):
correct = 0
total_loss = 0.0
for x, y in data:
pred = self.forward(x, training=False)
eps = 1e-15
p = max(eps, min(1 - eps, pred))
total_loss += -(y * math.log(p) + (1 - y) * math.log(1 - p))
if (pred >= 0.5) == (y >= 0.5):
correct += 1
return total_loss / len(data), correct / len(data) * 100
def train_model(self, train_data, test_data, epochs=300):
history = []
for epoch in range(epochs):
total_loss = 0.0
correct = 0
for x, y in train_data:
pred = self.forward(x, training=True)
self.backward(y)
eps = 1e-15
p = max(eps, min(1 - eps, pred))
total_loss += -(y * math.log(p) + (1 - y) * math.log(1 - p))
if (pred >= 0.5) == (y >= 0.5):
correct += 1
train_loss = total_loss / len(train_data)
train_acc = correct / len(train_data) * 100
test_loss, test_acc = self.evaluate(test_data)
history.append((train_loss, train_acc, test_loss, test_acc))
if epoch % 75 == 0 or epoch == epochs - 1:
gap = train_acc - test_acc
print(f" Epoch {epoch:3d}: train_acc={train_acc:.1f}%, test_acc={test_acc:.1f}%, gap={gap:.1f}%")
return history
사용하기
PyTorch는 정규화(normalization)와 규제(regularization)를 모듈(module) 형태로 제공합니다.
import torch
import torch.nn as nn
model = nn.Sequential(
nn.Linear(784, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(256, 128),
nn.BatchNorm1d(128),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(128, 10),
)
model.train()
out_train = model(torch.randn(32, 784))
model.eval()
out_test = model(torch.randn(1, 784))
model.train()과 model.eval() 전환(toggle)은 매우 중요합니다. 이 전환은 드롭아웃(dropout)을 켜고 끄며, BatchNorm이 배치 통계(batch statistics)를 쓸지 실행 통계(running statistics)를 쓸지를 결정합니다. 추론(inference) 전에 model.eval()을 호출하지 않으면 드롭아웃이 계속 활성화(active) 상태이고, BatchNorm 역시 현재 미니배치 통계(current mini-batch statistics)를 사용해 출력(output)이 흔들리게 됩니다.
트랜스포머(transformer)에서는 패턴이 조금 다릅니다.
class TransformerBlock(nn.Module):
def __init__(self, d_model=512, nhead=8, dropout=0.1):
super().__init__()
self.attention = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
self.norm1 = nn.LayerNorm(d_model)
self.ff = nn.Sequential(
nn.Linear(d_model, d_model * 4),
nn.GELU(),
nn.Linear(d_model * 4, d_model),
nn.Dropout(dropout),
)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
attended, _ = self.attention(x, x, x)
x = self.norm1(x + self.dropout(attended))
x = self.norm2(x + self.ff(x))
return x
BatchNorm이 아니라 LayerNorm을 사용하고, 드롭아웃 비율은 p=0.5가 아니라 p=0.1을 씁니다. 이것이 트랜스포머의 기본값(default)입니다.
산출물 만들기
이 lesson의 산출물은 다음과 같습니다.
outputs/prompt-regularization-advisor.md: 과적합(overfitting)을 진단하고 적절한 정규화 전략(regularization strategy)을 추천해 주는 프롬프트(prompt)
연습문제
- (쉬움) 2차원 데이터(2D data)를 위한 공간 드롭아웃(spatial dropout)을 구현합니다. 개별 뉴런(individual neuron)을 끄는 대신 특징 채널(feature channel) 전체를 한꺼번에 드롭합니다. 원형 데이터셋(circle dataset)에서 일반 드롭아웃(standard dropout)과 학습-테스트 격차(train-test gap)를 비교합니다.
- (중간) Lesson 05의 레이블 스무딩(label smoothing)과 이 lesson의 드롭아웃(dropout)을 함께 적용합니다. 둘 다 끔, 드롭아웃만, 레이블 스무딩만, 둘 다 켬의 네 가지 설정(configuration)에 대해 최종 학습-테스트 격차(final train-test gap)를 측정합니다.
- (중간) 은닉층(hidden layer)과 활성화 함수(activation) 사이에 BatchNorm 층(BatchNorm layer)을 추가합니다. 학습률(learning rate)을 0.01, 0.05, 0.1로 바꿔 가며 BatchNorm 유무에 따른 차이를 비교합니다.
- (중간) 조기 종료(early stopping)를 구현합니다. 테스트 손실(test loss)을 추적하면서 가장 좋은 가중치(best weights)를 저장하고, 20 에폭(epoch) 동안 개선이 없으면 학습을 멈춥니다.
- (어려움) 4층 네트워크(4-layer network)에서 LayerNorm과 RMSNorm을 비교합니다. 동일한 가중치(weight)로 초기화한 뒤 정확도(accuracy), 에폭당 소요 시간(epoch time), 첫 번째 층의 기울기 크기(first layer gradient magnitude)를 비교합니다.
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| 과적합(Overfitting) | "데이터를 외움" | 학습 성능(training performance)이 테스트 성능(test performance)보다 크게 높아 잡음(noise)까지 학습한 상태 |
| 정규화(Regularization) | "과적합 방지" | 모델 복잡도(model complexity)를 제한해 일반화(generalization)를 개선하는 모든 기법 |
| 드롭아웃(Dropout) | "무작위 뉴런 삭제(random neuron deletion)" | 학습(training) 중 무작위 뉴런(random neuron)을 0으로 만들어 중복 표현(redundant representation)을 학습하도록 강제하는 기법 |
| 가중치 감쇠(Weight decay) | "L2 페널티(L2 penalty)" | 매 학습 단계(step)마다 lambda * w만큼 가중치(weight)를 0 쪽으로 줄이는 정규화 항(regularizer) |
| 배치 정규화(Batch normalization) | "배치별로 정규화" | 학습 중에는 배치 통계(batch statistics)로, 추론(inference) 중에는 실행 평균(running averages)으로 층 출력(layer output)을 정규화 |
| 층 정규화(Layer normalization) | "샘플별로 정규화" | 각 샘플(sample) 내부의 특징(feature)을 정규화하는 배치 독립적(batch-independent) 정규화 |
| RMSNorm | "평균(mean) 빼기가 없는 LayerNorm" | 평균 빼기(mean subtraction) 없이 제곱평균제곱근(root mean square)으로 스케일(scale)을 정규화하는 기법 |
| 조기 종료(Early stopping) | "과적합되기 전에 멈추기" | 검증 손실(validation loss)이 더 이상 좋아지지 않을 때 학습(training)을 멈추는 정규화 기법 |
| 데이터 증강(Data augmentation) | "적은 데이터로 더 많은 데이터 만들기" | 레이블 보존(label-preserving) 변환을 적용해 유효 데이터셋 크기(effective dataset size)를 키우는 기법 |
| 일반화 격차(Generalization gap) | "학습-테스트 격차(train-test gap)" | 학습 성능(training performance)과 테스트 성능(test performance)의 차이 |
더 읽을거리