はじめに
機械学習モデルを学習させる際、「損失関数(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_crossentropy | one-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
グラフから、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
分類タスクにおいて、categorical_crossentropy、sparse_categorical_crossentropy、binary_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を選択する。
実験結果からも、タスクとデータの特性に合った損失関数を選ぶことが、学習の安定化と精度向上の鍵であることが分かります。



0 件のコメント:
コメントを投稿