Softmax
소프트맥스
로짓을 확률 분포로 변환하는 함수. 다중 분류의 출력층에서 사용. 모든 출력의 합이 1이 되도록 정규화.
소프트맥스
로짓을 확률 분포로 변환하는 함수. 다중 분류의 출력층에서 사용. 모든 출력의 합이 1이 되도록 정규화.
Softmax는 임의의 실수 벡터(로짓)를 확률 분포로 변환하는 활성화 함수입니다. 각 원소에 지수함수(exp)를 적용한 후 전체 합으로 나눠, 모든 출력이 0~1 사이이고 합이 정확히 1이 됩니다. 수식: softmax(x_i) = exp(x_i) / sum(exp(x_j))
1959년 Robert Luce가 선택 모델에서 처음 제안하고, 1989년 John Bridle이 신경망에 적용했습니다. "soft" + "max"라는 이름처럼, argmax의 미분 가능한(soft) 버전으로 볼 수 있습니다. 가장 큰 값이 높은 확률을 가지면서도 다른 클래스의 확률도 유지합니다.
핵심 특성은 지수함수로 인한 차이 증폭입니다. 로짓이 [2, 1, 0]이면 softmax 후 약 [0.67, 0.24, 0.09]가 됩니다. 입력 차이가 클수록 승자독식(winner-take-all)에 가까워집니다. Temperature(T) 파라미터로 이 특성을 조절할 수 있습니다: softmax(x/T)
다중 클래스 분류(이미지 분류, 자연어 처리), Transformer의 Attention 가중치 계산, LLM의 다음 토큰 확률 계산에 사용됩니다. Cross-Entropy Loss와 함께 사용하면 그래디언트가 (예측 - 정답)으로 깔끔하게 계산되어 학습이 안정적입니다.
import numpy as np
import torch
import torch.nn.functional as F
# 1. NumPy로 직접 구현 (수치 안정성 포함)
def softmax_numpy(x):
"""Numerically stable softmax"""
x_max = np.max(x, axis=-1, keepdims=True)
exp_x = np.exp(x - x_max) # overflow 방지
return exp_x / np.sum(exp_x, axis=-1, keepdims=True)
logits = np.array([2.0, 1.0, 0.1])
probs = softmax_numpy(logits)
print(f"로짓: {logits}")
print(f"확률: {probs}") # [0.659, 0.242, 0.099]
print(f"합계: {probs.sum()}") # 1.0
# 2. PyTorch 내장 함수
logits_torch = torch.tensor([2.0, 1.0, 0.1])
probs_torch = F.softmax(logits_torch, dim=-1)
print(f"PyTorch softmax: {probs_torch}")
# 3. Temperature로 분포 조절
def softmax_with_temperature(logits, temperature=1.0):
"""Temperature가 낮을수록 sharp, 높을수록 uniform"""
return F.softmax(logits / temperature, dim=-1)
logits = torch.tensor([3.0, 1.0, 0.5])
print(f"T=0.5 (sharp): {softmax_with_temperature(logits, 0.5)}") # [0.93, 0.05, 0.02]
print(f"T=1.0 (normal): {softmax_with_temperature(logits, 1.0)}") # [0.84, 0.11, 0.05]
print(f"T=2.0 (soft): {softmax_with_temperature(logits, 2.0)}") # [0.65, 0.21, 0.14]
# 4. 분류 모델에서 사용
class Classifier(torch.nn.Module):
def __init__(self, input_dim, num_classes):
super().__init__()
self.linear = torch.nn.Linear(input_dim, num_classes)
def forward(self, x):
logits = self.linear(x)
# 학습 시: CrossEntropyLoss가 내부적으로 softmax 적용
# 추론 시: F.softmax(logits, dim=-1)로 확률 변환
return logits
# CrossEntropyLoss = LogSoftmax + NLLLoss
loss_fn = torch.nn.CrossEntropyLoss()
logits = torch.randn(4, 10) # batch=4, classes=10
labels = torch.randint(0, 10, (4,))
loss = loss_fn(logits, labels) # softmax 내장
"모델 출력이 로짓이라 바로 해석하면 안 됩니다. softmax 적용해서 확률로 바꾼 다음, 가장 높은 클래스가 고양이 0.85, 개 0.10 이런 식으로 보시면 됩니다. 0.85면 신뢰도가 높은 예측이에요."
"Softmax는 로짓을 확률 분포로 변환합니다. 지수함수로 양수화한 뒤 정규화하죠. Cross-Entropy Loss와 함께 쓰면 그래디언트가 (예측-정답)으로 단순해져서 학습이 안정적입니다. 수치 안정성을 위해 max를 빼는 것도 중요합니다."
"Temperature 0.7로 설정하면 softmax가 더 sharp해져서 고확률 토큰 위주로 샘플링됩니다. 창의적 출력이 필요하면 temperature를 1.2 정도로 올리면 분포가 평평해져서 다양한 토큰이 선택돼요."
PyTorch의 nn.CrossEntropyLoss는 내부적으로 LogSoftmax를 적용합니다. 입력에 softmax를 미리 적용하면 이중 적용되어 학습이 망가집니다.
exp(1000)은 inf가 됩니다. 반드시 max를 빼서 수치 안정성을 확보하세요: exp(x - max(x)). 대부분의 라이브러리 내장 함수는 이미 처리되어 있습니다.
학습 시 CrossEntropyLoss에 로짓을 직접 전달하고, 추론 시에만 F.softmax()로 확률 변환하세요. Temperature 조절이 필요하면 softmax 전에 로짓을 나눕니다.