Activation関数比較:ReLU / LeakyReLU / ELU / GELU の違いとは?

2025年6月29日日曜日

Activation関数 ELU GELU Google Colab Keras LeakyReLU ReLU 活性化関数

X f B! P L
Activation関数比較 アイキャッチ画像

はじめに:活性化関数の重要性

ニューラルネットワークにおいて活性化関数は、学習の非線形性・収束の速さ・性能改善に 大きく影響します。本記事では、特に普及しているReLU・LeakyReLU・ELU・GELU の4種類を、数学的な定義から実践的な性能比較まで、詳細に解説します。

比較する活性化関数

各関数の数式・グラフ・利点・欠点を整理します。

  • ReLU… f(x)=max(0,x)。計算がシンプルで高速だが、「死んだReLU」問題がある。
  • LeakyReLU… 負領域も小さく通すことで死んだユニットを防止。
  • ELU… 負領域で指数的に変化し、平均を0に近づける効果で収束が速い。
  • GELU… ガウス分布に基づくスムーズな応答。BERT等の高性能モデルで使用。

補足:死んだReLU(Dying ReLU)問題とは?

死んだReLU」とは、ReLU関数が負の入力に対して常に0を返す性質により、一度活性化されなくなったニューロンが訓練データのどの入力に対しても反応しなくなる現象です。
これにより、そのニューロンは勾配が入らず、以降一切の学習貢献ができなくなってしまいます。

補足:なぜ問題なのか?

一度ニューロンが“死ぬ”と、その後勾配がゼロのままになり、学習が停止してしまいます。
ニューロンの数が減ることで、ネットワーク全体の表現力が低下し、性能悪化の原因にもなります。学習率が高すぎると、最大で40%ほどのニューロンが死んでしまうケースもあると言われています。

補足:どうして起きる?

  • 重みの過度な更新:学習率が大きすぎると、活性化入力が大きく負方向にシフトし、回復不能な状態に入る。
  • 負のバイアス:バイアス項が大きく負だと、入力が負になりやすく、ReLUが常に0を返す状態になる。

実験概要

目的:4関数の学習速度・汎化性能を定量比較。

  • データセット:MNIST(6万枚)
  • ライブラリ:TensorFlow 2.x / Keras
  • 構成:Conv層→Flatten→Denseモデル。各関数を使い分け。

環境とライブラリ

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

# GELUは tf.nn.gelu で使用可能

データ準備(MNIST)

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)

モデル構築関数

def build_model(activation_fn):
    model = models.Sequential([
        layers.Input(shape=(28, 28, 1)),
        layers.Conv2D(32, (3, 3)),
        activation_fn,
        layers.MaxPooling2D((2, 2)),
        layers.Flatten(),
        layers.Dense(64),
        activation_fn,
        layers.Dense(10, activation='softmax')
    ])
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
    return model

それぞれのモデル

models_dict = {
    'ReLU': build_model(layers.Activation('relu')),
    'LeakyReLU': build_model(layers.LeakyReLU(negative_slope=0.1)),
    'ELU': build_model(layers.ELU(alpha=1.0)),
    'GELU': build_model(layers.Activation(tf.nn.gelu))
}

訓練と精度の記録

history_dict = {}

for name, model in models_dict.items():
    print(f"Training with {name}")
    history = model.fit(x_train, y_train, epochs=5, batch_size=128,
                        validation_split=0.1, verbose=1)
    history_dict[name] = history

実行結果

Training with ReLU
Epoch 1/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 7s 9ms/step - accuracy: 0.8406 - loss: 0.5529 - val_accuracy: 0.9693 - val_loss: 0.1072
Epoch 2/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - accuracy: 0.9713 - loss: 0.0998 - val_accuracy: 0.9815 - val_loss: 0.0692
Epoch 3/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - accuracy: 0.9819 - loss: 0.0631 - val_accuracy: 0.9857 - val_loss: 0.0553
Epoch 4/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9873 - loss: 0.0452 - val_accuracy: 0.9837 - val_loss: 0.0617
Epoch 5/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9891 - loss: 0.0373 - val_accuracy: 0.9878 - val_loss: 0.0506
Training with LeakyReLU
Epoch 1/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.8536 - loss: 0.5220 - val_accuracy: 0.9730 - val_loss: 0.0992
Epoch 2/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - accuracy: 0.9719 - loss: 0.0961 - val_accuracy: 0.9820 - val_loss: 0.0665
Epoch 3/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - accuracy: 0.9823 - loss: 0.0607 - val_accuracy: 0.9838 - val_loss: 0.0608
Epoch 4/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - accuracy: 0.9864 - loss: 0.0464 - val_accuracy: 0.9837 - val_loss: 0.0568
Epoch 5/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9893 - loss: 0.0353 - val_accuracy: 0.9865 - val_loss: 0.0546
Training with ELU
Epoch 1/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.8654 - loss: 0.4912 - val_accuracy: 0.9700 - val_loss: 0.1047
Epoch 2/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - accuracy: 0.9677 - loss: 0.1089 - val_accuracy: 0.9800 - val_loss: 0.0689
Epoch 3/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9801 - loss: 0.0657 - val_accuracy: 0.9805 - val_loss: 0.0689
Epoch 4/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9874 - loss: 0.0440 - val_accuracy: 0.9833 - val_loss: 0.0598
Epoch 5/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - accuracy: 0.9898 - loss: 0.0341 - val_accuracy: 0.9847 - val_loss: 0.0564
Training with GELU
Epoch 1/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.8462 - loss: 0.5652 - val_accuracy: 0.9703 - val_loss: 0.1155
Epoch 2/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - accuracy: 0.9631 - loss: 0.1264 - val_accuracy: 0.9825 - val_loss: 0.0694
Epoch 3/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9800 - loss: 0.0713 - val_accuracy: 0.9825 - val_loss: 0.0670
Epoch 4/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 3s 3ms/step - accuracy: 0.9855 - loss: 0.0488 - val_accuracy: 0.9847 - val_loss: 0.0526
Epoch 5/5
422/422 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9891 - loss: 0.0375 - val_accuracy: 0.9872 - val_loss: 0.0498

結果の可視化(バリデーション精度)

全モデル5エポック終了後、val_accuracyの変化を可視化しました。

for name, history in history_dict.items():
    plt.plot(history.history['val_accuracy'], label=name)

plt.title('Validation Accuracy per Epoch')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.grid()
plt.show()

精度グラフ

精度グラフ

結果として、以下の傾向が見られました:

  1. ReLU:シンプルだが依然として有力。
  2. LeakyReLU:ReLUより若干滑らかで安定性向上。
  3. ELU:学習初期が速いが中盤で成果が頭打ちに。
  4. GELU:最初の収束がやや遅いが最終精度が高く安定。

考察:どの活性化関数が最適か?

各活性化関数の訓練結果を比較すると、最終的な精度(accuracy)はどの関数も非常に高く、0.989前後に到達しており、明確な差は見られませんでした。ただし、学習の進み方や汎化性能(val_accuracy)には微妙な違いがありました。

活性化関数 最終Accuracy 最終Loss Val Accuracy Val Loss
ReLU 0.9891 0.0373 0.9878 0.0506
LeakyReLU 0.9893 0.0353 0.9865 0.0546
ELU 0.9898 0.0341 0.9847 0.0564
GELU 0.9891 0.0375 0.9872 0.0498

全体精度は0.989〜0.990と大差はないものの、学習の安定性・収束速度・目的による使い分け が重要だと判断できます。

  • 高速処理が求められる場合 → ReLU または LeakyReLU
  • 滑らかな出力分布が必要なタスク → ELU・GELU(Transformer系におすすめ)

まとめ

活性化関数はモデルの学習挙動に影響を与える重要な要素です。今回の比較では、大きな性能差は見られなかったものの、ELUやGELUはより滑らかで安定した学習挙動を示しました。

一方で、ReLUは依然として堅実でシンプルな選択肢であることが分かりました。タスクやデータに応じて適切な関数を選択することが、より良いモデル構築につながります。

参考リンク

このブログを検索

自己紹介

はじめまして、機械学習を独学中のSHOU TAKEと申します。本ブログでは、Python・Keras・Google Colabを活用した画像分類やニューラルネットワークの実験記事を中心に発信しています。初学者の方にも分かりやすく、学んだことをそのまま実験形式でまとめるスタイルです。これまで取り組んだテーマには、学習率やOptimizerの比較、Batch Sizeの検証、事前学習の活用などがあります。ご質問やご感想は、お問い合わせフォームからお気軽にどうぞ。

お問い合わせフォーム

名前

メール *

メッセージ *

プライバシーポリシー

QooQ