PyTorch torch.nn.CrossEntropyLoss 関数
torch.nn.CrossEntropyLossPyTorch で多クラス分類に使用される損失関数です。
nn.LogSoftmax と nn.NLLLoss を組み合わせたもので、画像分類やテキスト分類などのタスクでよく使用されます。
関数定義
torch.nn.CrossEntropyLoss(weight=None, ignore_index=-100, reduction='mean', label_smoothing=0.0)
パラメータの説明:
weight(Tensor): 各クラスに異なる重みを割り当て、クラスの不均衡な場合に使用します。ignore_index(int): 指定したインデックスの損失計算を無視します。デフォルトは -100 です。reduction(str): 損失の集約方法。選択可能'mean'、'sum'、'none'。デフォルトは'mean'。label_smoothing(float): ラベル平滑化パラメータで、0 から 1 の間の値を取ります。デフォルトは 0 です。
数学的原理
交差エントロピー損失の式:
Loss = -log(exp(y_true) / sum(exp(y_i)))
つまり、正しいクラスの予測確率が大きいほど、損失は小さくなります。
使用例
例1: 基本的な使い方
交差エントロピー損失を作成して使用する:
実例
import torch
import torch.nn as nn
# 損失関数を作成
criterion = nn.CrossEntropyLoss()
# モデルが出力する logits(正規化前)
# 形状: (batch_size, num_classes)
outputs = torch.randn(4, 10)
# 実際のラベル
labels = torch.tensor([2, 5, 1, 7])
# 損失を計算
loss = criterion(outputs, labels)
print("モデル出力 (logits):", outputs[0].tolist())
print("実際のラベル:", labels[0].item())
print("交差エントロピー損失:", loss.item())
import torch.nn as nn
# 損失関数を作成
criterion = nn.CrossEntropyLoss()
# モデルが出力する logits(正規化前)
# 形状: (batch_size, num_classes)
outputs = torch.randn(4, 10)
# 実際のラベル
labels = torch.tensor([2, 5, 1, 7])
# 損失を計算
loss = criterion(outputs, labels)
print("モデル出力 (logits):", outputs[0].tolist())
print("実際のラベル:", labels[0].item())
print("交差エントロピー損失:", loss.item())
例2: クラスの重み
クラスの不均衡を処理する:
実例
import torch
import torch.nn as nn
# クラスの重み:少数クラスに高い重みを与える
# 10 個のクラスを想定し、3 番目と 7 番目のクラスがより重要
weight = torch.ones(10)
weight[3] = 2.0
weight[7] = 2.0
criterion_weighted = nn.CrossEntropyLoss(weight=weight)
outputs = torch.randn(4, 10)
labels = torch.tensor([2, 3, 7, 5])
loss = criterion_weighted(outputs, labels)
print("重み付き交差エントロピー損失:", loss.item())
import torch.nn as nn
# クラスの重み:少数クラスに高い重みを与える
# 10 個のクラスを想定し、3 番目と 7 番目のクラスがより重要
weight = torch.ones(10)
weight[3] = 2.0
weight[7] = 2.0
criterion_weighted = nn.CrossEntropyLoss(weight=weight)
outputs = torch.randn(4, 10)
labels = torch.tensor([2, 3, 7, 5])
loss = criterion_weighted(outputs, labels)
print("重み付き交差エントロピー損失:", loss.item())
例3: ラベル平滑化
ラベル平滑化を使用して過学習を防ぐ:
実例
import torch
import torch.nn as nn
# ラベル平滑化:0.1 は 10% の確率を他のクラスに均等に分配することを意味します
criterion_smooth = nn.CrossEntropyLoss(label_smoothing=0.1)
outputs = torch.randn(4, 10)
labels = torch.tensor([2, 5, 1, 7])
loss = criterion_smooth(outputs, labels)
print("ラベル平滑化ありの損失:", loss.item())
# 比較:ラベル平滑化なし
criterion = nn.CrossEntropyLoss()
loss_no_smooth = criterion(outputs, labels)
print("ラベル平滑化なしの損失:", loss_no_smooth.item())
import torch.nn as nn
# ラベル平滑化:0.1 は 10% の確率を他のクラスに均等に分配することを意味します
criterion_smooth = nn.CrossEntropyLoss(label_smoothing=0.1)
outputs = torch.randn(4, 10)
labels = torch.tensor([2, 5, 1, 7])
loss = criterion_smooth(outputs, labels)
print("ラベル平滑化ありの損失:", loss.item())
# 比較:ラベル平滑化なし
criterion = nn.CrossEntropyLoss()
loss_no_smooth = criterion(outputs, labels)
print("ラベル平滑化なしの損失:", loss_no_smooth.item())
例4: 完全な分類トレーニングフロー
完全なモデルトレーニングの例:
実例
import torch
import torch.nn as nn
import torch.optim as optim
# シンプルな分類モデル
class Classifier(nn.Module):
def __init__(self, input_dim=784, num_classes=10):
super(Classifier, self).__init__()
self.fc = nn.Sequential(
nn.Linear(input_dim, 256),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(256, num_classes)
)
def forward(self, x):
return self.fc(x)
# モデルと損失を初期化
model = Classifier()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# トレーニングデータを模擬
batch_size = 32
x = torch.randn(batch_size, 784) # 入力
y = torch.randint(0, 10, (batch_size,)) # ラベル
# 順伝播
model.train()
outputs = model(x)
loss = criterion(outputs, y)
# 逆伝播
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("バッチ損失:", loss.item())
# 予測
model.eval()
with torch.no_grad():
outputs = model(x)
predictions = outputs.argmax(dim=1)
accuracy = (predictions == y).float().mean()
print("予測精度:", accuracy.item())
import torch.nn as nn
import torch.optim as optim
# シンプルな分類モデル
class Classifier(nn.Module):
def __init__(self, input_dim=784, num_classes=10):
super(Classifier, self).__init__()
self.fc = nn.Sequential(
nn.Linear(input_dim, 256),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(256, num_classes)
)
def forward(self, x):
return self.fc(x)
# モデルと損失を初期化
model = Classifier()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# トレーニングデータを模擬
batch_size = 32
x = torch.randn(batch_size, 784) # 入力
y = torch.randint(0, 10, (batch_size,)) # ラベル
# 順伝播
model.train()
outputs = model(x)
loss = criterion(outputs, y)
# 逆伝播
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("バッチ損失:", loss.item())
# 予測
model.eval()
with torch.no_grad():
outputs = model(x)
predictions = outputs.argmax(dim=1)
accuracy = (predictions == y).float().mean()
print("予測精度:", accuracy.item())
例5: ignore_index の使用
特定のラベルを無視する:
実例
import torch
import torch.nn as nn
# label=-100 のサンプルを無視
criterion = nn.CrossEntropyLoss(ignore_index=-100)
outputs = torch.randn(5, 10)
# 一部のサンプルのラベルが -100 の場合、無視を意味します
labels = torch.tensor([2, -100, 5, -100, 7])
loss = criterion(outputs, labels)
print("特殊なラベルを無視した後の損失:", loss.item())
import torch.nn as nn
# label=-100 のサンプルを無視
criterion = nn.CrossEntropyLoss(ignore_index=-100)
outputs = torch.randn(5, 10)
# 一部のサンプルのラベルが -100 の場合、無視を意味します
labels = torch.tensor([2, -100, 5, -100, 7])
loss = criterion(outputs, labels)
print("特殊なラベルを無視した後の損失:", loss.item())
例6: 異なる reduction 方法
損失の集約方法を制御する:
実例
import torch
import torch.nn as nn
outputs = torch.randn(4, 10)
labels = torch.tensor([2, 5, 1, 7])
# mean: 平均損失を返す
loss_mean = nn.CrossEntropyLoss(reduction='mean')(outputs, labels)
print("mean:", loss_mean.item())
# sum: 合計を返す
loss_sum = nn.CrossEntropyLoss(reduction='sum')(outputs, labels)
print("sum:", loss_sum.item())
# none: 各サンプルの損失を返す
loss_none = nn.CrossEntropyLoss(reduction='none')(outputs, labels)
print("none:", loss_none.tolist())
import torch.nn as nn
outputs = torch.randn(4, 10)
labels = torch.tensor([2, 5, 1, 7])
# mean: 平均損失を返す
loss_mean = nn.CrossEntropyLoss(reduction='mean')(outputs, labels)
print("mean:", loss_mean.item())
# sum: 合計を返す
loss_sum = nn.CrossEntropyLoss(reduction='sum')(outputs, labels)
print("sum:", loss_sum.item())
# none: 各サンプルの損失を返す
loss_none = nn.CrossEntropyLoss(reduction='none')(outputs, labels)
print("none:", loss_none.tolist())
よくある質問
Q1: CrossEntropyLoss と NLLLoss の違いは何ですか?
CrossEntropyLoss = LogSoftmax + NLLLoss です。softmax が組み込まれているため、手動で追加する必要はありません。
Q2: モデルの出力に softmax を使わないのはなぜですか?
CrossEntropyLoss は内部で自動的に softmax を計算するため、logits を直接使用することで数値的安定性が向上します。
Q3: ラベル平滑化はどのような場面に適していますか?
ラベル平滑化はクラス数が多い場合に適しており、モデルの汎化能力を向上させることができます。
使用シーン
nn.CrossEntropyLoss主な適用シーンは次のとおりです:
- 画像分類: CIFAR-10、ImageNet など
- テキスト分類: 感情分析、トピック分類
- 多クラス分類タスク: クラス数が 2 より大きい分類タスク
注意:ラベルはクラスインデックス(0 から num_classes-1)である必要があり、one-hot エンコーディングではありません。
その他の拡張
PyTorch torch.nn リファレンスマニュアル