損失関数(Loss)の種類を比較してみた【Keras実験付き】

投稿日:2025年10月31日金曜日 最終更新日:

Google Colab Keras 損失関数

X f B! P L
アイキャッチ画像 損失関数(Loss)の種類を比較してみた【Keras実験付き】

はじめに

機械学習モデルを学習させる際、「損失関数(Loss Function)」は結果を大きく左右する重要な要素です。 どの損失関数を選ぶかによって、学習の安定性や最終精度が大きく変化します。 本記事では、Kerasを用いて代表的な損失関数を比較し、 実験を通してその挙動を可視化します。


損失関数(Loss)とは?

損失関数とは、モデルの出力と正解データの「差」を数値化したものです。学習では、この値を小さくする方向にパラメータが更新されます。

Lossは「モデルがどれだけ間違えているか」を示す指標です。

Kerasでよく使われる損失関数一覧

損失関数(Loss Function)は、モデルの予測と正解との「誤差」を数値化する指標です。
この値を小さくしていくことでモデルの精度が高まります。
以下の表では、Kerasで使われる主要な損失関数を「どのような特徴を持ち」「どんなタスクに適しているか」を一覧でまとめています。

損失関数名 概要 主なタスク
mean_squared_error (MSE)誤差の二乗平均。大きな誤差に強いペナルティ。回帰
mean_absolute_error (MAE)誤差の絶対値を平均。外れ値に強い。回帰
huber小さな誤差はMSE、大きな誤差はMAEとして扱う。回帰
binary_crossentropy確率(0〜1)を出力する2値分類用。2値分類/多ラベル分類
categorical_crossentropyone-hot形式の多クラス分類。多クラス分類
sparse_categorical_crossentropy整数ラベルの多クラス分類。多クラス分類

タスクの種類と対応関係

  • 回帰: 数値を予測(例:価格、スコア)
  • 2値分類: Yes/No、0/1の判定(例:スパム検出)
  • 多ラベル分類: 複数クラス同時所属(例:「猫」「動物」)
  • 多クラス分類: 1つを選択(例:数字分類0〜9)

例:Keras MNIST分類実験では、sparse_categorical_crossentropyを使用しています。


損失関数の違いで学習はどう変わる?

ここからは、同じデータセット(MNIST)を使い、 異なる損失関数によって学習曲線がどのように変化するかを確認します。

⚠️ 実験上の注意: 本来MNISTは分類タスクですが、ここでは損失関数の挙動比較を目的に、 出力を連続値として扱う疑似的な回帰タスクとしています。

1. 回帰タスク(Regression)

連続値を予測するタスクでは、以下の3種類の損失を比較します。

  • MSE(平均二乗誤差)
  • MAE(平均絶対誤差)
  • Huber損失

Kerasでは、タスクの種類(回帰・分類)によって最適な損失関数(Loss Function)が異なります。 ここでは、MNISTデータセットを使って代表的な損失関数の学習挙動を比較します。

以下のコードでは、各損失関数を切り替えながら学習曲線を可視化します。全て同一のモデル構成・ハイパーパラメータで比較しています。

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
import numpy as np

# === 共通設定 ===
EPOCHS = 15
VAL_SPLIT = 0.2

# === 1. 回帰系 Loss関数の比較 ===================================
print("=== 回帰系 Loss関数の比較 ===")

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(-1, 28 * 28) / 255.0
x_test = x_test.reshape(-1, 28 * 28) / 255.0
y_train = y_train.astype('float32')
y_test = y_test.astype('float32')

def build_regression_model(loss):
    model = keras.Sequential([
        layers.Input((784,)),
        layers.Dense(64, activation='relu'),
        layers.Dense(1)
    ])
    model.compile(optimizer='adam', loss=loss, metrics=['mae'])
    return model

regression_losses = ['mean_squared_error', 'mean_absolute_error', 'huber']
reg_histories = {}

for loss in regression_losses:
    model = build_regression_model(loss)
    print(f"{loss}")
    history = model.fit(x_train, y_train, validation_split=VAL_SPLIT, epochs=EPOCHS, verbose=1)
    reg_histories[loss] = history

plt.figure(figsize=(6, 4))
for loss_name, history in reg_histories.items():
    plt.plot(history.history['val_loss'], label=loss_name)
plt.title('Validation Loss Comparison (Regression)')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()

回帰系 損失関数の比較 実行結果

=== 回帰系 Loss関数の比較 ===
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11490434/11490434 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step
mean_squared_error
Epoch 1/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 13s 7ms/step - loss: 3.9113 - mae: 1.4593 - val_loss: 1.5013 - val_mae: 0.8894
Epoch 2/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 7s 5ms/step - loss: 1.3213 - mae: 0.8234 - val_loss: 1.0793 - val_mae: 0.7308
Epoch 3/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - loss: 1.0103 - mae: 0.7140 - val_loss: 1.0136 - val_mae: 0.7120
Epoch 4/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 8s 3ms/step - loss: 0.8776 - mae: 0.6569 - val_loss: 0.9329 - val_mae: 0.6651
Epoch 5/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.7767 - mae: 0.6184 - val_loss: 0.8826 - val_mae: 0.6457
Epoch 6/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.7147 - mae: 0.5929 - val_loss: 0.8351 - val_mae: 0.6197
Epoch 7/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.6668 - mae: 0.5692 - val_loss: 0.8215 - val_mae: 0.6119
Epoch 8/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.6455 - mae: 0.5619 - val_loss: 0.8431 - val_mae: 0.6270
Epoch 9/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5983 - mae: 0.5442 - val_loss: 0.8048 - val_mae: 0.6139
Epoch 10/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5894 - mae: 0.5359 - val_loss: 0.8004 - val_mae: 0.6076
Epoch 11/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5481 - mae: 0.5171 - val_loss: 0.7943 - val_mae: 0.6102
Epoch 12/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5277 - mae: 0.5112 - val_loss: 0.7891 - val_mae: 0.6033
Epoch 13/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5255 - mae: 0.5079 - val_loss: 0.7845 - val_mae: 0.6048
Epoch 14/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5017 - mae: 0.4999 - val_loss: 0.7644 - val_mae: 0.5888
Epoch 15/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.4822 - mae: 0.4906 - val_loss: 0.7630 - val_mae: 0.5851
mean_absolute_error
Epoch 1/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 3ms/step - loss: 1.4553 - mae: 1.4553 - val_loss: 0.8546 - val_mae: 0.8546
Epoch 2/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.8254 - mae: 0.8254 - val_loss: 0.7066 - val_mae: 0.7066
Epoch 3/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.6977 - mae: 0.6977 - val_loss: 0.6778 - val_mae: 0.6778
Epoch 4/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.6406 - mae: 0.6406 - val_loss: 0.6342 - val_mae: 0.6342
Epoch 5/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.6052 - mae: 0.6052 - val_loss: 0.6316 - val_mae: 0.6316
Epoch 6/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5785 - mae: 0.5785 - val_loss: 0.5964 - val_mae: 0.5964
Epoch 7/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.5580 - mae: 0.5580 - val_loss: 0.6235 - val_mae: 0.6235
Epoch 8/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5363 - mae: 0.5363 - val_loss: 0.5773 - val_mae: 0.5773
Epoch 9/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5227 - mae: 0.5227 - val_loss: 0.5622 - val_mae: 0.5622
Epoch 10/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.5207 - mae: 0.5207 - val_loss: 0.5487 - val_mae: 0.5487
Epoch 11/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5048 - mae: 0.5048 - val_loss: 0.5586 - val_mae: 0.5586
Epoch 12/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.5003 - mae: 0.5003 - val_loss: 0.5367 - val_mae: 0.5367
Epoch 13/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.4846 - mae: 0.4846 - val_loss: 0.5360 - val_mae: 0.5360
Epoch 14/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.4781 - mae: 0.4781 - val_loss: 0.5222 - val_mae: 0.5222
Epoch 15/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.4748 - mae: 0.4748 - val_loss: 0.5477 - val_mae: 0.5477
huber
Epoch 1/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 3ms/step - loss: 0.9499 - mae: 1.3454 - val_loss: 0.4369 - val_mae: 0.7758
Epoch 2/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.4206 - mae: 0.7567 - val_loss: 0.3780 - val_mae: 0.7103
Epoch 3/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.3318 - mae: 0.6457 - val_loss: 0.3194 - val_mae: 0.6251
Epoch 4/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.2933 - mae: 0.5960 - val_loss: 0.3065 - val_mae: 0.6130
Epoch 5/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.2709 - mae: 0.5666 - val_loss: 0.2948 - val_mae: 0.5915
Epoch 6/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 3ms/step - loss: 0.2531 - mae: 0.5398 - val_loss: 0.2958 - val_mae: 0.5979
Epoch 7/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.2454 - mae: 0.5323 - val_loss: 0.3103 - val_mae: 0.6209
Epoch 8/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.2335 - mae: 0.5165 - val_loss: 0.2836 - val_mae: 0.5774
Epoch 9/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.2265 - mae: 0.5069 - val_loss: 0.2717 - val_mae: 0.5593
Epoch 10/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.2122 - mae: 0.4866 - val_loss: 0.2797 - val_mae: 0.5764
Epoch 11/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.2146 - mae: 0.4905 - val_loss: 0.2694 - val_mae: 0.5590
Epoch 12/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.2081 - mae: 0.4815 - val_loss: 0.2694 - val_mae: 0.5618
Epoch 13/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.1991 - mae: 0.4693 - val_loss: 0.2843 - val_mae: 0.5864
Epoch 14/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - loss: 0.1964 - mae: 0.4650 - val_loss: 0.2669 - val_mae: 0.5566
Epoch 15/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - loss: 0.1915 - mae: 0.4582 - val_loss: 0.2965 - val_mae: 0.6049
回帰系Loss関数の比較

グラフから、Huber損失は学習の安定性が高く、 MSEよりも外れ値に強い傾向が見られます。 一方、MAEは誤差全体に均等なペナルティを与えるため、 収束は遅めですが頑健です。

今回の実験では、mean_squared_error(MSE)mean_absolute_error(MAE)huber の3種類の損失関数を用いて、同一のデータセット・モデル構成で学習を行いました。

比較結果の概要

  • MSE(平均二乗誤差):誤差を二乗して扱うため、外れ値の影響を強く受ける。
  • MAE(平均絶対誤差):誤差をそのまま足し合わせるため、外れ値の影響が小さい。
  • Huber損失:MSEとMAEの中間的な性質を持ち、小さな誤差にはMSEのように、大きな誤差にはMAEのように動作する。

結果の傾向

学習曲線を比較したところ、Huber損失が他の2つよりも全体的に低い値を示しました。 これは異常ではなく、Huber損失の「外れ値に対して穏やかに反応する特性」によるものです。 MSEやMAEと比べると、Huber損失は過大な誤差を抑制するため、平均的な損失値が小さくなりやすい傾向があります。

損失値の大小について

注意点として、損失関数の値の大小を単純に比較しても、モデルの精度を直接的に評価することはできません。 各損失関数はスケールや設計目的が異なるため、「どの損失関数がより小さいか」よりも、「どの損失関数がタスクに適しているか」が重要です。

まとめ

  • Huber損失が低く出るのは正常で、外れ値に強い特性のため。
  • MSEは誤差が大きい場合に敏感に反応する。
  • MAEは安定しているが、勾配が一定なため最適化がやや遅い。
  • HuberはMSEとMAEの中間的性質で、ノイズの多い回帰タスクに向く。

実際のタスクでは、Huber損失を使うことで、外れ値やノイズの影響を抑えつつ安定した学習が期待できます。


2. 分類タスク(Classification)

ラベルの形式に応じて3つの損失を比較します。

  • sparse_categorical_crossentropy(整数ラベル)
  • categorical_crossentropy(one-hotラベル)
  • binary_crossentropy(偶数/奇数分類)

以下のコードでは、各損失関数を切り替えながら学習曲線を可視化します。全て同一のモデル構成・ハイパーパラメータで比較しています。

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
import numpy as np

# === 共通設定 ===
EPOCHS = 15
VAL_SPLIT = 0.2

# === 2. 分類系 Loss関数の比較(3種) ==============================
print("\n=== 分類系 Loss関数の比較 ===")

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(-1, 28 * 28) / 255.0
x_test = x_test.reshape(-1, 28 * 28) / 255.0

# 1) 多クラス分類(整数ラベル)→ sparse_categorical_crossentropy
# 2) 多クラス分類(one-hot)→ categorical_crossentropy
# 3) 2値分類(偶数/奇数)→ binary_crossentropy

def build_classification_model(loss, output_dim, activation):
    model = keras.Sequential([
        layers.Input((784,)),
        layers.Dense(128, activation='relu'),
        layers.Dense(output_dim, activation=activation)
    ])
    model.compile(optimizer='adam', loss=loss, metrics=['accuracy'])
    return model

class_histories = {}

# (1) sparse_categorical_crossentropy
print(f"sparse_categorical_crossentropy")
model = build_classification_model('sparse_categorical_crossentropy', 10, 'softmax')
history = model.fit(x_train, y_train, validation_split=VAL_SPLIT, epochs=EPOCHS, verbose=1)
class_histories['sparse_categorical_crossentropy'] = history

# (2) categorical_crossentropy
print(f"categorical_crossentropy")
y_train_onehot = keras.utils.to_categorical(y_train, 10)
model = build_classification_model('categorical_crossentropy', 10, 'softmax')
history = model.fit(x_train, y_train_onehot, validation_split=VAL_SPLIT, epochs=EPOCHS, verbose=1)
class_histories['categorical_crossentropy'] = history

# (3) binary_crossentropy
print(f"binary_crossentropy")
y_train_binary = (y_train % 2).astype('float32')  # 偶数=0, 奇数=1
model = build_classification_model('binary_crossentropy', 1, 'sigmoid')
history = model.fit(x_train, y_train_binary, validation_split=VAL_SPLIT, epochs=EPOCHS, verbose=1)
class_histories['binary_crossentropy'] = history

plt.figure(figsize=(6, 4))
for loss_name, history in class_histories.items():
    plt.plot(history.history['val_loss'], label=loss_name)
plt.title('Validation Loss Comparison (Classification)')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()

実験では、いずれのクロスエントロピー損失も安定して収束しましたが、 ラベル形式を誤ると正しい学習が行われない点に注意が必要です。 (例:整数ラベルで categorical_crossentropy を使うとエラーになります)

分類系 損失関数の比較 実行結果

=== 分類系 Loss関数の比較 ===
sparse_categorical_crossentropy
Epoch 1/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 3ms/step - accuracy: 0.8602 - loss: 0.4853 - val_accuracy: 0.9554 - val_loss: 0.1546
Epoch 2/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9630 - loss: 0.1290 - val_accuracy: 0.9663 - val_loss: 0.1125
Epoch 3/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9751 - loss: 0.0863 - val_accuracy: 0.9716 - val_loss: 0.0970
Epoch 4/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 9s 3ms/step - accuracy: 0.9817 - loss: 0.0647 - val_accuracy: 0.9735 - val_loss: 0.0887
Epoch 5/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9861 - loss: 0.0472 - val_accuracy: 0.9720 - val_loss: 0.0941
Epoch 6/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9888 - loss: 0.0383 - val_accuracy: 0.9705 - val_loss: 0.0963
Epoch 7/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9921 - loss: 0.0287 - val_accuracy: 0.9752 - val_loss: 0.0911
Epoch 8/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9938 - loss: 0.0235 - val_accuracy: 0.9754 - val_loss: 0.0916
Epoch 9/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9949 - loss: 0.0187 - val_accuracy: 0.9766 - val_loss: 0.0906
Epoch 10/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9962 - loss: 0.0139 - val_accuracy: 0.9750 - val_loss: 0.0996
Epoch 11/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9970 - loss: 0.0120 - val_accuracy: 0.9745 - val_loss: 0.1006
Epoch 12/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9978 - loss: 0.0088 - val_accuracy: 0.9746 - val_loss: 0.1047
Epoch 13/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9973 - loss: 0.0098 - val_accuracy: 0.9775 - val_loss: 0.0983
Epoch 14/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9985 - loss: 0.0063 - val_accuracy: 0.9763 - val_loss: 0.1015
Epoch 15/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9987 - loss: 0.0055 - val_accuracy: 0.9732 - val_loss: 0.1190
categorical_crossentropy
Epoch 1/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 3ms/step - accuracy: 0.8655 - loss: 0.4810 - val_accuracy: 0.9509 - val_loss: 0.1682
Epoch 2/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9585 - loss: 0.1437 - val_accuracy: 0.9635 - val_loss: 0.1250
Epoch 3/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9714 - loss: 0.0966 - val_accuracy: 0.9672 - val_loss: 0.1085
Epoch 4/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9787 - loss: 0.0698 - val_accuracy: 0.9701 - val_loss: 0.0985
Epoch 5/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9838 - loss: 0.0532 - val_accuracy: 0.9706 - val_loss: 0.0961
Epoch 6/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9888 - loss: 0.0395 - val_accuracy: 0.9740 - val_loss: 0.0873
Epoch 7/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9909 - loss: 0.0316 - val_accuracy: 0.9747 - val_loss: 0.0882
Epoch 8/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9935 - loss: 0.0241 - val_accuracy: 0.9736 - val_loss: 0.0906
Epoch 9/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9945 - loss: 0.0194 - val_accuracy: 0.9737 - val_loss: 0.0974
Epoch 10/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9949 - loss: 0.0179 - val_accuracy: 0.9725 - val_loss: 0.1022
Epoch 11/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9972 - loss: 0.0118 - val_accuracy: 0.9738 - val_loss: 0.1021
Epoch 12/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9976 - loss: 0.0100 - val_accuracy: 0.9756 - val_loss: 0.0969
Epoch 13/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9976 - loss: 0.0099 - val_accuracy: 0.9771 - val_loss: 0.0953
Epoch 14/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9981 - loss: 0.0072 - val_accuracy: 0.9768 - val_loss: 0.1006
Epoch 15/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9986 - loss: 0.0057 - val_accuracy: 0.9737 - val_loss: 0.1145
binary_crossentropy
Epoch 1/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9139 - loss: 0.2114 - val_accuracy: 0.9753 - val_loss: 0.0752
Epoch 2/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9778 - loss: 0.0659 - val_accuracy: 0.9798 - val_loss: 0.0624
Epoch 3/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9848 - loss: 0.0454 - val_accuracy: 0.9795 - val_loss: 0.0649
Epoch 4/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9884 - loss: 0.0363 - val_accuracy: 0.9803 - val_loss: 0.0600
Epoch 5/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9920 - loss: 0.0261 - val_accuracy: 0.9820 - val_loss: 0.0543
Epoch 6/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9926 - loss: 0.0230 - val_accuracy: 0.9833 - val_loss: 0.0559
Epoch 7/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9949 - loss: 0.0166 - val_accuracy: 0.9797 - val_loss: 0.0653
Epoch 8/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9965 - loss: 0.0122 - val_accuracy: 0.9837 - val_loss: 0.0616
Epoch 9/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9967 - loss: 0.0109 - val_accuracy: 0.9822 - val_loss: 0.0664
Epoch 10/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9978 - loss: 0.0076 - val_accuracy: 0.9838 - val_loss: 0.0641
Epoch 11/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9986 - loss: 0.0053 - val_accuracy: 0.9859 - val_loss: 0.0598
Epoch 12/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9987 - loss: 0.0054 - val_accuracy: 0.9843 - val_loss: 0.0661
Epoch 13/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9991 - loss: 0.0042 - val_accuracy: 0.9837 - val_loss: 0.0748
Epoch 14/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9990 - loss: 0.0038 - val_accuracy: 0.9830 - val_loss: 0.0776
Epoch 15/15
1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9994 - loss: 0.0027 - val_accuracy: 0.9838 - val_loss: 0.0769
分類系Loss関数の比較

分類タスクにおいて、categorical_crossentropysparse_categorical_crossentropybinary_crossentropy の3種類の損失関数を比較しました。 いずれも同じデータセット・モデル構成・学習条件(15エポック)で実行しています。

比較結果の概要

損失関数 最終 val_accuracy 最終 val_loss 特徴
categorical_crossentropy 約0.976 約0.10 ラベルをワンホット表現として扱う。多クラス分類に一般的。
sparse_categorical_crossentropy 約0.977 約0.09 整数ラベルを直接扱う形式。結果はcategoricalとほぼ同等。
binary_crossentropy 約0.985 約0.07 2値分類に最適化されており、単一出力ノードとシグモイド活性化で精度が高い。

結果の解釈

binary_crossentropy の損失値が他より低いのは、タスクが実質的に「2クラス分類(例えば0と1)」であり、この損失関数がその性質に最も適しているためです。 2クラス分類の場合、出力層を1ノード+シグモイド活性化で構成すると、binary_crossentropy は確率的誤差を最も直接的に最小化できます。

一方、categorical_crossentropy および sparse_categorical_crossentropy は、 出力層が複数ノード(例:10クラス)を想定しているため、ラベル数に応じて出力分布全体を考慮する設計です。 そのため、2クラス問題に用いるとわずかに非効率となり、損失値が相対的に高く見えます。

まとめ

  • 今回のタスクが2クラス分類の場合、binary_crossentropyが最適
  • 多クラス分類(例:10クラス)では categorical_crossentropy または sparse_categorical_crossentropy を使用する。
  • 損失値の低さは関数のスケールにも依存するため、値の大小だけで優劣を判断しない。

今回の結果は、モデル構成とタスクの整合性がとれていることを示す正常な結果です。 binary_crossentropy の低い損失値は過学習ではなく、目的関数の適合性が高いことを反映しています。

損失関数の選び方のポイント

  • 回帰問題: MSEかHuber。外れ値が多い場合はMAE。
  • 二値分類: Binary Crossentropy。
  • 多クラス分類: Sparse Categorical Crossentropy。
  • 異常検知・確率分布: KLDivergenceなども有効。

損失関数はタスクによって使い分けるのが基本です。特に実データではノイズや外れ値が多いため、Huber損失が安定して使える場面が多いです。


まとめ

  • MSEは誤差が大きいほど強く反応するため、外れ値に敏感。
  • MAEは安定しているが、微小な誤差に対して学習が鈍くなる。
  • Huber損失はその中間で、ノイズの多いデータに最適。
  • 分類系ではラベル形式(整数 or one-hot)に応じてLossを選択する。

実験結果からも、タスクとデータの特性に合った損失関数を選ぶことが、学習の安定化と精度向上の鍵であることが分かります。