tracks
딥 신경망을 학습시키다 보면 손실 값이 NaN으로 표시된 걸 몇 번이나 보셨나요?
몇 시간 동안 학습이 진행되어 손실 곡선이 멀쩡해 보이다가, 갑자기 무한대로 치솟는 경우가 있습니다. 보통 그 원인은 폭주하는 그래디언트입니다. 역전파 도중 그래디언트 값이 지나치게 커져서 파라미터 업데이트가 불안정해지고 모델이 망가지는 현상이죠. 이 문제는 순환 신경망에서 특히 심하지만, 트랜스포머나 깊은 피드포워드 네트워크에서도 나타납니다.
그래디언트 클리핑은 옵티마이저에 도달하기 전에 그래디언트의 크기를 제한해 이 문제를 해결합니다. 학습 루프에 한 줄만 추가하면 업데이트 폭을 제한하면서도 모델 자체는 건드리지 않습니다.
이 글에서는 그래디언트 클리핑의 직관, 두 가지 주요 방법, 임계값 선택법, 그리고 PyTorch와 TensorFlow에서의 구현 방법을 다룹니다.
그런데 데이터 사이언스에서 손실이란 정확히 무엇일까요? 자세한 내용은 머신 러닝의 손실 함수 블로그 글을 확인하세요.
그래디언트 클리핑이란?
그래디언트 클리핑은 학습 중 그래디언트의 크기를 제한해 불안정한 파라미터 업데이트를 방지하는 기법입니다.
그래디언트가 너무 커지면 옵티마이저가 파라미터 공간에서 거대한 보폭으로 이동해 가중치를 손실이 폭발하는 영역으로 밀어 넣습니다. 클리핑은 그 보폭을 미리 제한해 피해를 막아줍니다.
중요한 점은 그래디언트 클리핑이 모델 아키텍처에는 영향을 주지 않는다는 것입니다. 레이어를 추가하거나 활성화 함수를 바꾸지 않습니다. 역전파와 옵티마이저 단계 사이에서 그래디언트를 가로채 학습 과정만 수정합니다.
즉, 시도하기도 쉽고 제거하기도 쉽습니다. 곧 보시겠지만 코드 한 줄이면 충분합니다.
그래디언트 클리핑의 작동 방식
메커니즘은 간단합니다. 클리핑 연산은 역전파와 옵티마이저 단계 사이에 위치하며, 매 반복마다 동일한 네 가지 단계를 따릅니다.
- 그래디언트 계산: 순전파를 수행하고 손실을 계산한 뒤 역전파를 실행합니다. 여기서는 아무것도 바뀌지 않으며, 그래디언트는 평소와 같은 방식으로 네트워크를 따라 흐릅니다.
- 그래디언트 크기 확인: 그래디언트가 얼마나 큰지 측정합니다. 방법에 따라 개별 값들을 보거나 모든 파라미터에 걸친 전체 노름을 계산합니다.
- 임계값 초과 시 그래디언트 축소: 크기가 설정한 한계를 넘으면 그래디언트를 축소합니다. 넘지 않으면 그대로 둡니다.
- 모델 파라미터 업데이트: 클리핑된 그래디언트를 옵티마이저에 전달해 가중치를 업데이트합니다.
대부분의 경우 그래디언트는 임계값 아래에 머물고, 클리핑 없이 학습하는 것과 동일하게 진행됩니다. 스파이크가 발생할 때만 옵티마이저가 반응하기 전에 클리핑이 이를 잡아냅니다.
이게 전부입니다.
일반적인 그래디언트 클리핑 방법
그래디언트를 클리핑하는 방법은 두 가지가 있으며, 차이는 무엇을 측정하고 무엇을 스케일링하느냐에 있습니다.
값 기준 클리핑
값 기준 클리핑은 각 그래디언트 원소를 개별적으로 상한/하한에 고정합니다.
예를 들어 [-1.0, 1.0] 범위를 정하면, 이 범위를 벗어나는 값은 가장 가까운 경계값으로 잘립니다. 2.5는 1.0이 되고, -2.5는 -1.0이 됩니다. 이미 범위 안에 있는 값은 변하지 않습니다.

값 기준 클리핑 예시
이 접근의 장점은 매우 단순하다는 점입니다. 최솟값/최댓값 연산만 있으면 되고, 실행도 빠릅니다.
하지만 단점도 있습니다. 개별 값을 클리핑하면 그래디언트 벡터의 방향이 바뀔 수 있습니다. 어떤 성분은 잘리고 다른 성분은 안 잘리면, 업데이트 벡터는 더 이상 역전파가 지시한 방향을 가리키지 않습니다. 옵티마이저는 약간 잘못된 방향으로 이동하게 됩니다.
그래서 실무에서는 값 기준 클리핑이 덜 흔합니다.
노름 기준 클리핑
노름 기준 클리핑은 전체 그래디언트 벡터의 크기가 임계값을 넘을 때 벡터 전체를 스케일링합니다.
개별 값을 보지 않고, 모든 그래디언트의 노름(보통 L2 노름)을 계산해 최대값과 비교합니다. 노름이 임계값보다 작으면 아무 일도 일어나지 않습니다. 크면 모든 그래디언트를 동일한 스케일링 계수로 곱해 노름을 제한 값으로 낮춥니다.

노름 기준 클리핑 예시
장점은 방향을 보존한다는 점입니다. 모든 성분을 동일한 비율로 줄이기 때문에 그래디언트 벡터는 원래 방향을 그대로 가리킵니다. 보폭만 줄이는 것이지 방향을 바꾸는 것이 아닙니다.
이 때문에 노름 기준 클리핑이 표준이 되었습니다. PyTorch의 clip_grad_norm_와 TensorFlow의 clipnorm 모두 이 방법을 구현하며, 대부분의 최신 학습 파이프라인은 기본으로 사용합니다.
폭주 그래디언트 vs 소실 그래디언트
폭주와 소실 그래디언트는 모두 딥러닝에서 흔한 문제지만, 그래디언트 클리핑이 해결하는 것은 그중 하나뿐입니다.
폭주 그래디언트
폭주 그래디언트는 역전파 도중 그래디언트 값이 지나치게 커질 때 발생합니다.
이는 보통 깊은 네트워크나 순환 구조에서 나타나며, 그래디언트가 여러 층이나 시간 스텝을 거치면서 계속 곱해지기 때문입니다. 곱셈이 잘못된 방향으로 누적되면 그래디언트 크기가 폭발합니다. 그러면 옵티마이저가 아주 큰 업데이트를 수행해 가중치가 극단 값으로 튀고, 손실이 NaN이나 Inf로 변하는 경우가 많습니다.
손실이 갑자기 급등하거나, 멀쩡하다가 모델이 어느 순간 발산하는 모습으로 나타납니다.
소실 그래디언트
소실 그래디언트는 반대의 문제입니다. 네트워크를 거슬러 올라가며 그래디언트 값이 0에 가까워집니다.
그래디언트가 너무 작아지면 가중치 업데이트도 미세해집니다. 초기 층은 학습을 멈추고, 더 깊은 층은 매우 천천히 학습하며, 사실상 학습이 정체됩니다. 손실 곡선은 평평해지고 여러 에폭이 지나도 개선되지 않습니다.
이는 LSTM과 GRU가 등장하기 전, RNN이 긴 시퀀스를 다루기 어려웠던 주된 이유였습니다.
그래디언트 클리핑의 역할
그래디언트 클리핑은 폭주 그래디언트를 해결하며, 소실 그래디언트에는 효과가 없습니다.
클리핑은 너무 큰 그래디언트를 줄여주지만, 너무 작은 그래디언트에는 아무런 조치를 하지 않습니다. 소실 그래디언트에는 더 나은 가중치 초기화, 잔차 연결, 배치 정규화, 혹은 그래디언트 흐름을 보존하도록 설계된 아키텍처가 필요합니다.
노름 기준 그래디언트 클리핑 자세히 보기
노름 기준 클리핑은 대부분의 독자가 그래디언트 클리핑을 검색할 때 찾는 방법입니다.
절차는 세 단계입니다. 첫째, 모든 그래디언트를 합쳐 노름을 계산합니다. 둘째, 그 노름을 선택한 임계값과 비교합니다. 셋째, 노름이 너무 크면 그래디언트를 리스케일합니다.
노름은 보통 L2 노름으로, 모든 그래디언트 값을 제곱해 합한 뒤 제곱근을 취합니다. 모델 파라미터 전반의 그래디언트 g_1, g_2, ..., g_n이 있다면 L2 노름은 다음과 같습니다:

노름 기준 클리핑 공식
노름을 구했으면 임계값 c와 비교합니다. ||g|| <= c이면 그래디언트를 그대로 통과시키고, ||g|| > c이면 모든 그래디언트에 스케일링 계수 c / ||g||를 곱합니다. 이렇게 하면 새로운 노름이 정확히 c가 됩니다.
이 방식의 핵심은 모든 성분이 같은 비율로 줄어든다는 점입니다. 그래디언트 값들의 상대적 비율이 유지되므로, 벡터는 여전히 원래 방향을 가리킵니다. 옵티마이저의 보폭만 줄일 뿐, 향하는 곳은 바꾸지 않습니다.
바로 이 방향 보존 특성 때문에 노름 클리핑이 기본 선택이 됩니다. 값 기준 클리핑은 그래디언트 벡터의 방향을 비틀 수 있지만, 노름 기준 클리핑은 길이만 바꿉니다.
PyTorch의 clip_grad_norm_와 TensorFlow의 clipnorm는 정확히 이 동작을 수행합니다. 누군가 "그래디언트 클리핑을 쓴다"고 하면, 거의 항상 노름 기준 클리핑을 의미합니다.
그래디언트 클리핑 임계값 선택
임계값은 하이퍼파라미터이므로, 모든 모델에 통하는 보편값은 없습니다.
너무 높게 설정하면 클리핑이 거의 작동하지 않습니다. 그래디언트가 대부분 한계 이하에 머물러 안전망이 아무것도 잡지 못합니다. 클리핑이 없는 것처럼 학습이 진행되어, 그래디언트가 폭주할 때 손실 급등을 그대로 보게 됩니다.
너무 낮게 설정하면 과도하게 클리핑합니다. 매 배치마다 그래디언트가 줄어들어 가중치 업데이트가 필요 이상으로 작아집니다. 학습이 느려지고 수렴까지 더 오래 걸릴 수 있습니다.
일반적인 시작점은 1.0이며, 많은 아키텍처에서 잘 동작합니다. 0.5에서 5.0 사이의 값이 대부분의 실무 사례를 포괄합니다.
더 나은 방법은 학습 중 그래디언트 노름을 모니터링하는 것입니다. 매 스텝에서 클리핑 전 노름을 로깅하고 분포를 확인하세요. 대부분의 노름이 0.3 근처에 있고 가끔 50까지 튄다면, 전형적인 범위보다 충분히 높고 스파이크보다는 한참 낮은 곳에 임계값을 두세요 — 여기서는 2.0이나 3.0이 합리적입니다.
다른 하이퍼파라미터처럼 다루세요. 1.0으로 시작해 관찰하고, 학습 행동에 따라 조정합니다.
순환 신경망에서의 그래디언트 클리핑
RNN은 그래디언트 클리핑이 표준 기법으로 자리 잡은 출발점입니다.
그 이유는 RNN이 시간에 걸쳐 그래디언트를 전파하는 방식 때문입니다. 시간마다 같은 가중치 행렬을 반복 곱하는 역전파를 수행하는데, 이 반복 곱셈이 거대한 값으로 누적될 수 있습니다. 시퀀스가 길수록 문제가 악화됩니다.
LSTM과 GRU는 게이팅 메커니즘으로 문제를 줄였지만 완전히 없애지는 못했습니다. 두 아키텍처 모두 특히 긴 시퀀스를 학습하거나 학습률이 높을 때 클리핑의 이점을 봅니다.
RNN 학습에서는 노름 기준 클리핑과 1.0에서 5.0 사이 임계값이 보통의 기본값입니다. PyTorch의 nn.LSTM이나 nn.GRU를 쓰는 중에 손실이 폭주한다면, clip_grad_norm_을 추가하는 것이 대개 가장 먼저 시도할 일입니다.
현대 딥러닝에서의 그래디언트 클리핑
트랜스포머가 RNN을 대체했어도 그래디언트 클리핑은 사라지지 않았습니다.
GPT와 BERT 같은 대형 언어 모델은 사전학습과 파인튜닝에서 클리핑을 사용합니다. 비전 트랜스포머, 디퓨전 모델, 수백 층의 대부분의 딥 아키텍처에도 마찬가지입니다. 현대 학습을 장악한 Adam과 AdamW 옵티마이저는 종종 1.0 전후의 임계값으로 노름 클리핑과 함께 사용됩니다.
이유는 RNN 때와 같습니다. 깊은 네트워크는 여러 층에 걸쳐 그래디언트를 곱해 나가고, 큰 배치 크기와 높은 학습률이 결합되면 간헐적으로 그래디언트 스파이크가 생길 수 있습니다. 클리핑은 정상적인 학습 스텝에는 영향을 주지 않으면서 이런 스파이크를 처리합니다.
대부분의 레퍼런스 구현은 기본으로 클리핑을 포함합니다. Hugging Face의 Trainer, PyTorch Lightning, DeepSpeed 모두 표준 설정 옵션으로 클리핑을 제공합니다. 장난감 수준을 넘어서는 모델을 학습한다면, 클리핑은 거의 확실히 파이프라인의 일부입니다.
코드 한 줄 추가로 비용은 거의 들지 않으면서, 수 시간의 연산 끝에 학습이 충돌하는 일을 막아줍니다. 그래서 지금까지도 쓰입니다.
PyTorch에서의 그래디언트 클리핑
PyTorch는 단일 유틸리티 함수 torch.nn.utils.clip_grad_norm_로 그래디언트 클리핑을 처리합니다.
클리핑 호출은 loss.backward()와 optimizer.step() 사이에 둡니다. 먼저 역전파로 그래디언트를 채우고, 필요하면 클리핑으로 줄인 뒤, 옵티마이저가 업데이트를 적용합니다. 다른 곳에 두면 동작하지 않습니다.
다음은 합성 회귀 데이터에 대해 작은 MLP를 학습시키는, 그래디언트 클리핑이 활성화된 완전 실행 가능한 학습 스크립트입니다:
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
torch.manual_seed(42)
# Synthetic regression data
n_samples = 1000
n_features = 20
inputs = torch.randn(n_samples, n_features)
targets = (inputs.sum(dim=1, keepdim=True) * 2.0 + torch.randn(n_samples, 1) * 0.1)
dataset = TensorDataset(inputs, targets)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
# Small feedforward network
model = nn.Sequential(
nn.Linear(n_features, 64),
nn.ReLU(),
nn.Linear(64, 64),
nn.ReLU(),
nn.Linear(64, 1),
)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.MSELoss()
# Training loop with gradient clipping
n_epochs = 5
max_grad_norm = 1.0
for epoch in range(n_epochs):
epoch_loss = 0.0
for batch_inputs, batch_targets in dataloader:
optimizer.zero_grad()
predictions = model(batch_inputs)
loss = loss_fn(predictions, batch_targets)
loss.backward()
# Gradient clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_grad_norm)
optimizer.step()
epoch_loss += loss.item()
print(f"Epoch {epoch + 1}: loss = {epoch_loss / len(dataloader):.4f}")

PyTorch 출력
clip_grad_norm_ 함수는 두 가지 주요 인수를 받습니다:
-
parameters: 클리핑할 그래디언트가 있는 모델 파라미터입니다. 전체 모델을 포함하려면model.parameters()를 전달하세요. -
max_norm: 그래디언트 노름의 임계값입니다.1.0이 흔한 시작점입니다.
선택적 인수 norm_type는 기본이 L2 노름인 2.0입니다. 바꿔야 할 일은 드뭅니다.
clip_grad_norm_의 마지막 언더스코어는 제자리(in-place) 연산을 의미합니다. 이 함수는 각 파라미터의 .grad 속성 안의 그래디언트를 직접 수정하므로, 반환값을 따로 관리할 필요가 없습니다. 다만 클리핑 전 전체 노름을 반환하므로, 로깅에 유용합니다.
값 기준 클리핑을 쓰려면 PyTorch의 torch.nn.utils.clip_grad_value_가 있습니다:
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5)
하지만 앞서 설명했듯, 이 구현을 사용할 일은 거의 없습니다.
설정은 이것이 전부입니다. 학습 루프에 두 줄만 추가하세요.
TensorFlow에서의 그래디언트 클리핑
TensorFlow는 별도 함수 호출이 아닌 옵티마이저 수준에서 클리핑을 처리합니다.
옵티마이저를 생성할 때 clipnorm 또는 clipvalue를 인수로 전달합니다. 옵티마이저가 각 스텝에서 내부적으로 클리핑을 적용하므로, 학습 루프를 수정할 필요가 없습니다.
다음은 Keras API를 사용해 합성 회귀 데이터를 학습하는 전체 예제입니다:
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
np.random.seed(42)
# Synthetic regression data
n_samples = 1000
n_features = 20
x_train = np.random.randn(n_samples, n_features).astype(np.float32)
y_train = (x_train.sum(axis=1, keepdims=True) * 2.0
+ np.random.randn(n_samples, 1).astype(np.float32) * 0.1)
# Small feedforward network
model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation="relu", input_shape=(n_features,)),
tf.keras.layers.Dense(64, activation="relu"),
tf.keras.layers.Dense(1),
])
# Optimizer with gradient clipping by norm
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, clipnorm=1.0)
model.compile(optimizer=optimizer, loss="mse")
model.fit(x_train, y_train, epochs=5, batch_size=32)

TensorFlow 출력
두 인수는 서로 다른 동작을 합니다:
-
clipnorm은 각 그래디언트 텐서의 L2 노름을 기준으로 클리핑합니다. 노름이 임계값을 넘으면 텐서를 비례적으로 축소합니다. -
clipvalue는 각 그래디언트 원소를 개별적으로 클리핑합니다. 임계값을 넘는 값은 임계값으로 고정하고, 음수 방향으로 임계값보다 작은 값은 음의 임계값으로 고정합니다.
노름 클리핑에서 값 클리핑으로 바꾸려면 인수만 바꾸면 됩니다:
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, clipvalue=0.5)
이 인수들은 모든 Keras 옵티마이저(Adam, SGD, RMSprop, AdamW 등)에서 동작합니다. 또한 global_clipnorm 인수도 있어, 텐서별이 아니라 모든 그래디언트를 합친 노름을 기준으로 클리핑합니다. 이는 PyTorch의 기본 동작과 더 가깝습니다.
만약 tf.GradientTape로 커스텀 학습 루프를 작성하더라도, apply_gradients를 호출할 때 옵티마이저가 클리핑을 처리합니다:
for epoch in range(5):
for batch_x, batch_y in zip(np.array_split(x_train, 32), np.array_split(y_train, 32)):
with tf.GradientTape() as tape:
predictions = model(batch_x, training=True)
loss = tf.reduce_mean(tf.square(predictions - batch_y))
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
두 프레임워크의 차이는 여기에 있습니다. PyTorch는 루프 안에서 사용자가 클리핑을 수행하고, TensorFlow는 옵티마이저에 이를 위임합니다. 기본 논리는 동일합니다.
그래디언트 클리핑 vs 기타 안정화 기법
그래디언트 클리핑만이 학습을 안정화하는 유일한 방법은 아니며, 항상 정답도 아닙니다.
다른 기법들은 관련 있지만 다른 문제를 다룹니다. 어떤 것은 애초에 그래디언트가 커지는 것을 막고, 어떤 것은 소실을 방지하며, 어떤 것은 손실 표면을 더 최적화하기 쉽게 만듭니다. 몇 가지를 살펴보겠습니다.
배치 정규화
배치 정규화는 학습 중 각 미니배치 내의 활성화를 정규화합니다.
레이어 출력을 안정적인 범위로 유지해 그래디언트 크기를 더 예측 가능하게 만듭니다. 배치 정규화를 사용한 네트워크는 더 높은 학습률을 견디고 더 빠르게 수렴하며, 가중치 초기화 선택에도 덜 민감합니다.
하지만 배치 정규화가 폭주 그래디언트를 직접 막지는 않습니다. 발생 빈도를 줄여줄 뿐, 발생했을 때 어떻게 할지는 별개입니다. 이런 이유로 많은 모델이 배치 정규화와 그래디언트 클리핑을 함께 사용합니다.
잔차 연결
잔차 연결은 하나 이상의 레이어를 건너뛰는 지름길 경로를 추가해, 그래디언트가 뒤쪽 레이어에서 앞쪽 레이어로 직접 흐를 수 있게 합니다.
이는 깊은 네트워크에서 소실 그래디언트 문제를 해결합니다. 잔차 연결이 없으면 20~30층을 넘는 네트워크를 학습하기 어려운데, 역전파 중 그래디언트가 0에 수렴하기 때문입니다. 잔차 연결을 사용하면 수백 층의 네트워크도 무리 없이 학습됩니다.
잔차 연결은 클리핑과는 반대편 문제를 겨냥합니다. 클리핑은 너무 큰 그래디언트를 다루고, 잔차 연결은 너무 작은 그래디언트를 다룹니다.
신중한 가중치 초기화
초기 가중치 값은 활성화와 그래디언트의 시작 크기를 결정합니다. 잘못된 초기화는 첫 스텝부터 그래디언트 폭주나 소실을 일으킬 수 있습니다.
Xavier와 He 초기화 같은 방법은 레이어 크기에 따라 초기 가중치를 스케일링합니다. 학습 초기에 레이어 전반의 활성화 분산을 안정적으로 유지해, 많은 그래디언트 문제를 사전에 방지합니다.
좋은 초기화는 클리핑이 필요할 가능성을 낮추지만, 완전히 없애지는 못합니다. 학습률이 높거나 이례적인 배치가 있을 때는 학습 후반에도 그래디언트 스파이크가 나타날 수 있습니다.
함께 쓰는 방법
이 기법들은 상호 대체재가 아닙니다. 같은 큰 문제의 서로 다른 부분을 해결하는 보완적인 도구입니다.
전형적인 현대 학습 설정은 시작 시 신중한 초기화를 사용하고, 아키텍처에 잔차 연결을 두며, 네트워크 내부에는 배치 정규화(또는 레이어 정규화)를 적용하고, 최적화 중 안전망으로 그래디언트 클리핑을 더합니다. 각각이 특정 실패 모드를 다루며, 함께 하면 깊은 네트워크가 학습 가능해집니다.
결론
그래디언트 클리핑은 딥러닝에서 가장 간단한 해결책 중 하나이며, 한 번의 스텝으로 수시간의 학습을 망칠 수 있는 문제를 해결합니다.
좋은 소식은 모델 아키텍처를 바꾸거나 학습 코드를 다시 작성할 필요가 없다는 것입니다. PyTorch에서는 한 줄, TensorFlow에서는 인수 하나면 그래디언트 클리핑을 구현할 수 있습니다.
클리핑은 더 큰 구성의 일부로 사용할 때 가장 효과적입니다. 신중한 초기화, 잔차 연결, 배치 또는 레이어 정규화와 함께 사용하면 여러 측면에서 불안정을 다루는 학습 파이프라인을 갖출 수 있습니다.
손실이 폭주한다면 클리핑부터 시작하세요. 소실된다면 다른 방법을 찾으세요. 장난감 수준을 넘어서는 모델을 학습한다면, 기본으로 클리핑을 파이프라인에 추가하고 잊어버리셔도 됩니다.
그래디언트 클리핑은 머신 러닝 엔지니어라면 반드시 알아야 할 수많은 용어 중 하나일 뿐입니다. 다른 개념도 함께 익혀 2026년에 취업 경쟁력을 갖추고 싶다면, 지금 바로 Machine Learning Engineer 트랙에 등록하세요.
그래디언트 클리핑 FAQs
딥러닝에서 그래디언트 클리핑이란 무엇인가요?
그래디언트 클리핑은 신경망 학습 중 그래디언트의 크기를 제한해 불안정한 파라미터 업데이트를 막는 기법입니다. 역전파 도중 그래디언트가 너무 커지면 옵티마이저가 거대한 보폭으로 이동해 가중치를 나쁜 영역으로 밀고, 손실이 폭발합니다. 클리핑은 옵티마이저 단계 전에 그래디언트 크기에 상한을 두어, 원시 그래디언트가 스파이크하더라도 업데이트가 한계 내에 머물게 합니다.
언제 그래디언트 클리핑을 사용해야 하나요?
학습이 불안정할 때마다 사용하세요. 특히 손실이 갑자기 급등하거나 NaN 값이 보이거나, 수시간 학습 후 발산한다면 더욱 그렇습니다. LSTM, GRU 같은 순환 네트워크와, 높은 학습률로 학습하는 모든 깊은 아키텍처에서는 표준 관행입니다. 손실 곡선이 안정적이라면 생략해도 되지만, 안전망으로 추가해도 해가 없습니다.
값 기준 클리핑과 노름 기준 클리핑의 차이는 무엇인가요?
값 기준 클리핑은 각 그래디언트 원소를 개별적으로 제한해, 그래디언트 벡터의 방향을 바꿀 수 있습니다. 노름 기준 클리핑은 전체 벡터의 크기가 임계값을 넘을 때 벡터 전체를 스케일링해, 보폭만 줄이고 원래 방향은 보존합니다. 업데이트 방향을 왜곡하지 않기 때문에, 현대 딥러닝에서는 노름 기준 클리핑이 표준 선택입니다.
적절한 그래디언트 클리핑 임계값은 어떻게 고르나요?
우선 1.0에서 시작해, 학습 중 관찰에 따라 조정하세요. 배치별로 클리핑 전 그래디언트 노름을 로깅하고 분포를 확인합니다. 전형적인 노름 범위보다 위이되, 잡아내고 싶은 스파이크보다는 충분히 낮게 임계값을 두세요. 너무 높으면 클리핑이 작동하지 않고, 너무 낮으면 불필요하게 학습을 느리게 합니다.
그래디언트 클리핑이 소실 그래디언트도 해결하나요?
아니요. 그래디언트 클리핑은 값이 너무 커지는 폭주 그래디언트만 다룹니다. 소실 그래디언트는 그 반대로 값이 0에 수렴해 학습이 사실상 멈춥니다. 소실 그래디언트에는 더 나은 가중치 초기화, 잔차 연결, 배치 정규화, LSTM/GRU 같은 아키텍처를 사용하세요.