Courses
活性化関数は、ニューラルネットワークを通過する信号と通過しない信号を決めます。誤った関数を選ぶと、学習が遅すぎたり、汎化に失敗したりします。長年、ReLUは高速で多くのタスクに十分だったため、妥当なデフォルトの選択肢でした。
GELU(Gaussian Error Linear Unit)はその状況を変えました。BERTやGPTを含む、史上最も高性能なモデルの一部で使われている活性化関数です。
本記事では、GELUの直感、数式、他の活性化関数との比較、そして実際にどこで使うのかを解説します。
機械学習における活性化関数が初めての場合は、まずRectified Linear Unit(ReLU)入門ガイドのブログ記事をお読みください。
GELU活性化関数とは?
GELU(Gaussian Error Linear Unit)は、入力の大きさに基づいて重み付けを行う、滑らかで確率的なアプローチの活性化関数です。
多くの活性化関数は、信号を通すか遮断するかを二者択一で決めます。たとえばReLUは、負の値をすべてゼロにし、それ以外はそのまま通します。GELUは異なります。しきい値による硬い切り捨てではなく、入力の大小に応じて滑らかにスケーリングします。つまり、小さな負の値であっても出力に寄与し得ます。
ReLUとの違いは、GELUは至るところで滑らかで連続であることです。ゼロの位置に鋭い角がなく、急激な遷移もありません。この滑らかさは学習時に重要となり、最適化器によりきれいな勾配情報を与えます。
GELUの直感
GELUは、すべての入力を同じようには扱わないフィルターだと考えてください。
ReLUは単純で、負の値は常にゼロにします。一方GELUは「この入力値が有用である可能性はどのくらいか?」と問いかけます。明らかに大きく正の値はほぼそのまま通し、小さな値や負の値は完全には切らずにスケールダウンします。
その結果、関連性の低い信号を完全に捨てることなく抑制する、滑らかな曲線が得られます。
就職応募書類の束を審査する場面を想像してください。厳格なフィルターは学位がない人を例外なく除外します。より賢いフィルターは、関連経験で補える可能性があるとして基準に近い候補者も考慮します。GELUはその賢いフィルターのように機能します。厳しい切り捨てはせず、入力の大きさに基づいて重み付けし、どの程度通すかを決めます。
この段階的で確率的なスケーリングこそがGELUの特徴です。鋭い遷移も死んだニューロンもなく、各入力値に対して滑らかに「通すか抑えるか」の判断が下されます。
GELUの数式
GELUの正確な数式はガウス累積分布関数(CDF)に基づき、次のように表されます:

ガウス累積分布関数
ここで、x は入力値、Φ(x) は標準正規分布からの乱数が x 以下である確率です。平易に言えば、Φ(x) はその入力値がどれだけ「正規的」=期待される値かを示します—and GELUはその確率を用いて入力をスケーリングします。
入力が大きいほど Φ(x) は1に近づき、入力はほぼそのまま通ります。入力が小さいほど Φ(x) は0に近づき、入力は抑制されます。
実務で使われる近似
正確な式の問題は、Φ(x) の計算が高コストであることです。閉形式を持たない誤差関数を含み、大規模計算では遅くなります。
そこでディープラーニングのフレームワークは次の近似を用います:

GELUの近似式
この近似は tanh を用い、現代のハードウェアで高速かつ広く最適化されています。実務で重要な入力範囲では正確な式とほぼ同一の結果が得られるため、PyTorchやTensorFlowなどのフレームワークはデフォルトでこの近似を使います。
もちろん、どちらの式も暗記する必要はありません。しかし、近似が存在する理由を知っておくと、コードで GELU を呼び出したときに何が起きているのかを理解しやすくなります。
GELUと他の活性化関数の比較
各活性化関数は入力の扱い方が異なり、その違いはモデルの学習のしやすさに反映されます。
まずは説明に先立って、見た目の違いを示します:

GELUと他の活性化関数の比較グラフ
Sigmoid
Sigmoidは入力を0から1の範囲に押し込みます。滑らかですが、勾配消失というよく知られた問題があります。非常に大きいまたは小さい入力では勾配がゼロに近づき、深い層が学習しなくなります。GELUは、より広い入力範囲で有意な勾配を保つため、この問題が起きにくいです。
Tanh
TanhはSigmoidに似ていますがゼロ中心で、出力は-1から1です。負の入力をSigmoidよりもうまく扱いますが、極端な領域ではやはり勾配消失が起きます。GELUはより滑らかな出力曲線を生み、深いネットワーク全体でより良い勾配の流れを実現します。
ReLU
ReLUは高速で単純です。正の入力はそのまま通し、負の入力はゼロにします。ゼロでの鋭い切り捨てが「死んだニューロン」問題の原因で、長期にわたり負の入力しか受けないニューロンはまったく更新されなくなります。GELUは負の入力を切らずにスケールすることでこれを回避します。
Leaky ReLU
Leaky ReLUは負の入力の一部をわずかに通すことで、死んだニューロン問題を緩和します。ReLUより一歩進んでいますが、ゼロでの遷移は依然として鋭いままです。GELUは全体としてより滑らかな曲線を生成し、勾配の質が重要な深いアーキテクチャでうまく機能する傾向があります。
まとめると、これら5つの活性化関数の違いは次のとおりです:

GELUと他の活性化関数の比較表
トランスフォーマーでGELUが使われる理由
トランスフォーマーは単に深いニューラルネットワークです。ネットワークが深くなるほど、勾配の質が重要になります。
BERTやGPTのようなモデルは何十もの層を積み重ねます。その深さでは、勾配の流れの小さな問題が累積します。活性化関数が特定の領域で不安定またはほぼゼロの勾配を生むと、ネットワークの初期層は学習中にほとんど更新されず、十分に学習できません。
GELUは、より広い入力範囲で勾配を滑らかかつ非ゼロに保つことでこれを回避します。ReLUのゼロ境界のような切り捨てがなく、出力層付近だけでなくすべての層で最適化器がよりきれいなシグナルを受け取れます。
さらに、GELUがトランスフォーマーアーキテクチャに適している理由があります。
トランスフォーマーは、注意機構を通じて入力を処理し、正負の両方を含む広い範囲の活性値を生成します。滑らかな活性化関数は、鋭い遷移を持つ関数よりもその範囲をうまく扱えます。
BERTの元論文が発表された際、著者らはReLUではなくGELUを選択し、ベンチマークでより良い結果を報告しました。GPTも同様の選択をしました。以来、GELUは多くのトランスフォーマー系アーキテクチャでデフォルトの活性化となっています。新しいからではなく、これらのモデルが動作するスケールでよりうまく機能するからです。
GELUの実践
GELUの使用は、他の活性化関数と同じくらい簡単です。PyTorchとTensorFlowのどちらも標準サポートしています。
PyTorch
PyTorchでは、GELUを独立したモジュールとして、またはモデル定義の中でインラインに適用できます。以下はGELUを用いたシンプルなフィードフォワードブロックです:
import torch
import torch.nn as nn
class FeedForwardBlock(nn.Module):
def __init__(self, input_dim, hidden_dim):
super().__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.act = nn.GELU()
self.fc2 = nn.Linear(hidden_dim, input_dim)
def forward(self, x):
return self.fc2(self.act(self.fc1(x)))
block = FeedForwardBlock(input_dim=512, hidden_dim=2048)
x = torch.randn(8, 512)
output = block(x)
nn.GELU() は2つの線形層の間にあり、トランスフォーマーのフィードフォワードサブレイヤーで見られる配置そのものです。活性化は最初の射影の後、2つ目の射影の前に実行されます。
TensorFlow
TensorFlowでは、Keras APIからGELUを利用できます:
import tensorflow as tf
from tensorflow import keras
model = keras.Sequential([
keras.layers.Dense(2048, input_shape=(512,)),
keras.layers.Activation("gelu"),
keras.layers.Dense(512)
])
x = tf.random.normal((8, 512))
output = model(x)
また、Dense レイヤーに文字列引数として直接渡すこともできます:
keras.layers.Dense(2048, activation="gelu")
どちらの方法でも結果は同じです。
ネットワーク内でのGELUの位置
GELUは他の活性化関数と同じ場所、すなわち線形変換の直後で次の層の手前に置きます。トランスフォーマーでは、フィードフォワードサブレイヤー内の2つの全結合(Dense)射影の間に相当します。他の深層ネットワークでも、線形層や畳み込み層の後に配置し、次に渡す前に出力をスケーリングさせます。
GELUの利点
ここまで読めば、他の活性化関数と比較したGELUの主要な長所はご理解いただけたはずです。以下に簡単におさらいします:
- 滑らかな活性化:GELUは不連続のない連続的で微分可能な曲線を生成し、各ステップで最適化器によりクリーンな情報を与えます。
- より良い勾配フロー:GELUは負の入力をゼロにしないため、負の値を受け取るニューロンにも勾配が伝播します。これにより学習中にニューロンが死ぬリスクが減ります。
- 深いモデルでの性能向上:トランスフォーマーのような深層アーキテクチャでは、滑らかな勾配の累積効果が、より単純な活性化関数と比べて学習結果の向上につながる傾向があります。
GELUの制約
GELUが常に最適というわけではありません。注意すべき制約をいくつか挙げます:
-
ReLUより計算コストが高い:GELUは誤差関数または
tanhに基づく近似を伴い、単純なしきい値操作であるReLUよりコストがかかります。多層からなる大規模モデルでは、この差が積み重なります。 -
直感的でない:ReLUのような関数は「正は通す・負は通さない」と理解しやすい一方、GELUの確率的スケーリングは解釈が難しいことがあります。
-
常に必要ではない:浅いネットワークや単純なタスクでは、GELUの利点は限定的です。ReLUやLeaky ReLUの方が低コストで同等に機能することがよくあります。
結論として、トランスフォーマーや他の深層アーキテクチャを構築するなら、GELUは確かなデフォルト選択です。それ以外では、採用前にベンチマークを行ってください。
結論
GELUは万能なアップグレードでも、ReLUを置き換える一律の解ではありません。特定の文脈、すなわち深いネットワークやトランスフォーマーモデルで価値のある、意図的な設計選択です。
BERT、GPT、あるいはトランスフォーマー系モデルを扱っているなら、気づいていなかったとしてもすでにGELUを使っています。今、その理由が分かったはずです。
それ以外では、活性化関数の選択はトレードオフの問題です。常に勝つ関数は存在せず、それぞれの特性を理解することが、惰性ではなく自信を持って選択する助けになります。
活性化関数の違いがまだ分かりにくい場合は、Machine Learning Engineer トラックに参加して、機械学習とMLOpsのキャリア準備を進めましょう。
FAQs
GELU活性化関数とは何ですか?
GELU(Gaussian Error Linear Unit)は、ニューラルネットワークで用いられる活性化関数です。ReLUと異なり、入力の大きさに基づいて確率的に滑らかにスケーリングします。これにより、勾配の質が重要な深いアーキテクチャにより適しています。
GELUはReLUとどう違いますか?
ReLUはゼロでの硬いしきい値により、負の入力をすべてゼロにします。その結果、学習をやめてしまう「死んだニューロン」問題が起き得ます。GELUは負の入力を段階的にスケールダウンすることで、切り捨てを避けます。これにより勾配の流れが滑らかになり、深いネットワークでより良い性能が得られます。
他の活性化関数よりGELUを使うべきなのはいつですか?
GELUは、特にBERTやGPTのようなトランスフォーマー系の深いアーキテクチャで最も効果を発揮します。浅いネットワークや単純なタスクでは、計算オーバーヘッドの観点からReLUから乗り換える理由は乏しいことが多いです。まずはReLUでベンチマークし、必要に応じて他を試すのが良いでしょう。
GELUの正確な式と近似の違いは何ですか?
GELUの正確な式はガウスの累積分布関数を用い、誤差関数の計算が必要になります。これは大規模処理では遅い演算です。近似はこれを tanh を用いた式に置き換え、現代のハードウェアで高速かつ最適化されています。実務上は両者はほぼ同一の結果を出すため、多くのフレームワークがデフォルトで近似を用います。
GELUはPyTorchとTensorFlowの両方で使えますか?
はい、どちらのフレームワークでもGELUをサポートしています。PyTorchでは nn.GELU() をモデル定義内のモジュールとして使えます。TensorFlowでは任意のレイヤーの activation 引数に "gelu" を文字列で渡すか、keras.layers.Activation("gelu") を使用できます。