Focal Lossとは?クラス不均衡に強い損失関数を導出から実装まで完全解説

「99%が背景、1%が検出したい物体」——物体検出ではこんな極端なクラス不均衡が日常的に起こります。このデータをそのまま交差エントロピー損失で学習すると、モデルは「とりあえず全部背景と答える」という怠惰な解に落ち着いてしまい、肝心の物体をまったく見つけられません。なぜこんなことが起きるのでしょうか。そして、どうすれば防げるのでしょうか。

この問題に対する驚くほどシンプルで強力な答えが Focal Loss(フォーカルロス) です。Focal Lossは交差エントロピーにたった一つの掛け算(変調項)を加えただけの損失関数ですが、「すでにうまく分類できている易しいサンプル」の損失を自動的に小さく抑え、「まだ間違えている難しいサンプル」に学習を集中させます。

Focal Lossは2017年に物体検出器 RetinaNet のために提案され、それまで2段階検出器に一歩劣るとされていた1段階検出器を一気に最先端へ押し上げました。現在では物体検出だけでなく、医療画像のセグメンテーション、異常検知、レアイベントの分類など、「正例が極端に少ない」あらゆる不均衡分類で標準的な選択肢になっています。

本記事の内容

  • クラス不均衡で通常の交差エントロピーが失敗する理由(易しい多数派が勾配を支配する)
  • Focal Lossの定義と各項(変調項・αバランス)の意味
  • 変調項 $(1-p_t)^\gamma$ が易しいサンプルを抑える仕組みと $\gamma$ の効果
  • $\gamma=0$ で交差エントロピーに一致することの確認
  • RetinaNetでの起源と勾配の比較
  • 数値安定なlogits版の実装(numpyとPyTorch風)

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

クラス不均衡という問題

まず、Focal Lossが解こうとしている問題を具体的にイメージしましょう。1段階の物体検出器は、画像をびっしりと格子状に区切った無数の候補領域(アンカー)について、それぞれ「物体か背景か」を判定します。1枚の画像から生まれる候補領域は数万〜十万個にのぼりますが、その大半は何も写っていない背景です。実際に検出したい物体に対応する領域は、せいぜい数個から数十個しかありません。

つまり、データは 負例(背景)が圧倒的に多く、正例(物体)が極端に少ない という強い不均衡を抱えています。比率にして1対1000、ときに1対10000にもなります。

クラス不均衡で大量の易しい背景サンプルが損失の合計を支配する概念図

上の図は不均衡の構造を表しています。左の灰色の点が大量の易しい背景、右の赤い点がわずかな難しい前景です。背景は1個あたりの損失が小さくても、数が膨大なので合計すると学習全体を支配します。本当に学びたい難しい少数派の信号は、その中に埋もれて消えてしまうのです。

ここで重要なのは、不均衡には2つの側面があるという点です。1つは 数の不均衡(背景が圧倒的に多い)、もう1つは 難易度の不均衡(背景の大部分は「明らかに背景」で分類が易しい)です。Focal Lossはこの2つを別々の仕組みで扱います。まずは通常の交差エントロピーがなぜこの状況で破綻するのかを、損失の形から見ていきましょう。

通常の交差エントロピーの弱点

二値分類の交差エントロピー損失を思い出しましょう。正解ラベルを $y \in \{0, 1\}$、モデルが出力するクラス1の確率を $p$ とすると、損失は次のように書けます。

$$ \mathrm{CE}(p, y) = -y \log(p) – (1-y)\log(1-p) $$

この式は正解クラスに割り当てた確率だけで決まります。そこで、表記を簡潔にするために「正解クラスの予測確率」を $p_t$ と定義しましょう。

$$ p_t = \begin{cases} p & (y = 1) \\ 1 – p & (y = 0) \end{cases} $$

この $p_t$ を使うと、交差エントロピーはひとつの式にまとまります。

$$ \mathrm{CE}(p_t) = -\log(p_t) $$

$p_t$ は「モデルが正解にどれだけ自信を持てたか」を表す量です。$p_t$ が1に近いほど正しく自信を持って分類できており、損失は0に近づきます。逆に $p_t$ が小さいと損失は大きくなります。ここまでは交差エントロピーの素直な性質です。

問題は、「すでにうまく分類できている易しいサンプル」でも損失が完全には0にならない点にあります。例えば $p_t = 0.9$(かなり自信を持って正解)でも、損失は $-\log(0.9) \approx 0.105$ です。1サンプルなら無視できる小ささですが、こうした易しい背景が1万個もあれば、合計損失は $0.105 \times 10000 \approx 1050$ に達します。

一方、難しい前景が100個あり、それぞれ $p_t = 0.3$ で苦戦しているとすると、その損失合計は $-\log(0.3) \times 100 \approx 120$ にすぎません。易しい多数派の損失合計(1050)が、難しい少数派の損失合計(120)を10倍近く上回るのです。

勾配降下法はこの合計損失を下げる方向にパラメータを動かします。すると、勾配の大半は「すでにできている背景をもう少しだけ完璧にする」ことに費やされ、「まだできていない前景を学ぶ」信号はかき消されてしまいます。これがクラス不均衡で交差エントロピーが破綻する本質です。

ここで「クラスごとに重みを掛ければよいのでは」と思うかもしれません。実際、希少な正例の損失を大きく重み付けする手法は古くから使われています。しかし、これだけでは不十分です。なぜなら、重み付けが扱えるのは「正例 vs 負例」という クラス単位の偏り だけだからです。負例の中にも、明らかに背景とわかる易しいものと、物体に紛らわしい難しいものが混在しています。クラス重みでは、この「同じクラス内の易しい・難しい」を区別できません。本当に必要なのは、クラスではなく サンプル1個ずつの分類しやすさ に応じて重みを変える仕組みです。Focal Lossはまさにこれを実現します。

交差エントロピーとFocal Lossの損失曲線の比較(γを変えた効果)

上の図の灰色の曲線が通常の交差エントロピーです。$p_t$ が大きい右側でも曲線が完全には0に張り付かず、わずかに浮いているのが見えます。この「わずかな浮き」が大量に積み重なって学習を支配するわけです。色のついた曲線がFocal Lossで、$\gamma$ を大きくするほど右側が地面に押し付けられていきます。では、この曲線をどうやって作るのか、定義を見ていきましょう。

Focal Lossの定義

Focal Lossの発想は「易しいサンプルの損失を、易しさに応じて自動的に小さくする」ことです。交差エントロピーに、サンプルごとに変わる重みを掛けてやればよいわけです。その重みを 変調項(modulating factor) と呼び、$(1 – p_t)^\gamma$ という形を取ります。Focal Lossの定義は次の通りです。

$$ \mathrm{FL}(p_t) = -\alpha_t (1 – p_t)^\gamma \log(p_t) $$

各項の意味を整理しましょう。

  • $-\log(p_t)$ : 元の交差エントロピー。正解への自信が低いほど大きい。
  • $(1 – p_t)^\gamma$ : 変調項。$p_t$ が1に近い(易しい)ほど0に近づき、損失を強く削る。$p_t$ が小さい(難しい)ほど1に近づき、損失をほぼそのまま残す。
  • $\gamma \ge 0$ : フォーカスパラメータ。変調項の効き具合を調整する。大きいほど易しいサンプルを強く抑える。
  • $\alpha_t \in [0, 1]$ : αバランス重み。クラスの頻度の偏りを補正する重み。難易度ではなく「数」の不均衡を扱う。

ここで注目すべきは、変調項 $(1 – p_t)^\gamma$ がモデルの予測 $p_t$ に依存して 動的に変わる という点です。固定の重みではなく、「そのサンプルが今どれだけ易しく分類できているか」をリアルタイムに見て重みを決めます。学習が進んで $p_t$ が上がれば、自動的にそのサンプルの寄与は下がっていきます。これがFocal Lossの賢いところです。

なぜ「$1 – p_t$」を底に取るのでしょうか。$1 – p_t$ は「正解までの距離」、いわば誤差の大きさです。誤差が小さい($p_t$ が1に近い)ほど変調項は小さくなり、損失を削ります。そこに指数 $\gamma$ を乗せることで、削り具合を自由に強められるわけです。次の節で、この変調項の挙動を詳しく見ていきましょう。

変調項とγの効果

変調項 $(1 – p_t)^\gamma$ だけを取り出して、$\gamma$ を変えたときの挙動を観察します。

変調項(1-p_t)^γの挙動。易しい例ほど0に近づく

この図から変調項の本質が読み取れます。$\gamma = 0$ のとき(灰色の水平線)、変調項はどの $p_t$ でも常に1で、損失をまったく変えません。$\gamma$ を大きくすると曲線は右下に押し下げられ、$p_t$ が大きい易しいサンプルの重みが急速に0へ向かいます。一方で $p_t$ が小さい難しいサンプル(左側)では、どの $\gamma$ でも変調項は1近くを保ち、損失はほとんど削られません。

具体的な数値で効果を確認しましょう。$\gamma = 2$ のとき、よく分類できた易しい例 $p_t = 0.9$ の変調項は $(1 – 0.9)^2 = 0.01$ です。つまり損失が 100分の1 に潰されます。一方、難しい例 $p_t = 0.3$ では $(1 – 0.3)^2 = 0.49$ で、損失は約半分にしか減りません。さらに苦戦している $p_t = 0.1$ なら $(1 – 0.1)^2 = 0.81$ で、ほぼそのまま残ります。

この「易しいほど強く削り、難しいほど残す」という非対称な作用を、損失の削減率として見るとさらに鮮明になります。

Focal Lossによる易しい例の損失削減率

縦軸は交差エントロピーに対する損失の削減率です。$\gamma = 2$ のオレンジ色の曲線では、$p_t = 0.9$ の易しい例の損失が99%も削減されます。一方、$p_t$ が0.5を下回る難しい例では削減率は急激に小さくなります。$\gamma$ を5まで上げると、易しい例はほぼ完全に無視され、学習は難しい例だけに集中します。論文では $\gamma = 2$ が経験的に最良とされていますが、データの不均衡度に応じて0.5〜5の範囲で調整します。

ここまでで変調項が「易しいサンプルの損失を選択的に削る」ことがわかりました。では、実際のサンプル群でこの作用がどう効くのか、1サンプルあたりの損失寄与を比較してみましょう。

サンプル別の寄与とバッチ全体の効果

代表的な4つのサンプルについて、交差エントロピーとFocal Loss($\gamma = 2$)の損失を比べます。

易しい例と難しい例の1サンプルあたりの損失寄与の比較

灰色がCE、オレンジがFocalです。易しい例($p_t = 0.9$)ではCEの0.11がFocalで0.001まで激減しています。一方、とても難しい例($p_t = 0.1$)ではCEの2.30がFocalで1.87と、ほとんど減っていません。Focal Lossは易しい例だけを狙い撃ちで小さくし、難しい例の重みは温存していることが一目でわかります。

この効果を「数の不均衡」と組み合わせると、学習信号の配分が劇的に変わります。冒頭の例(易しい背景9900個、難しい前景100個)でバッチ全体の損失内訳を計算してみましょう。

バッチ全体の損失内訳。Focalで難しい例の比重が上がる

左の通常のCEでは、易しい背景の損失合計が圧倒的で、本当に学びたい難しい前景の損失はバッチ全体のわずか20%しか占めていません。ところが右のFocal Loss($\gamma = 2$)では、易しい背景の損失が変調項でほぼ消され、難しい前景が損失の96%を占めるようになります。勾配の流れる方向が「背景の微調整」から「前景の学習」へと完全に逆転したわけです。これこそがFocal Lossが不均衡データで劇的に効く理由です。

ところで、これまで $\gamma = 0$ のときは変調項が1になると述べてきました。これは「Focal Lossは交差エントロピーの一般化である」ことを意味します。次に、この関係を式で確認しておきましょう。

γ=0 で交差エントロピーに一致する

Focal Lossの定義に $\gamma = 0$ を代入します。αバランスもいったん外して $\alpha_t = 1$ とすると、変調項は次のようになります。

$$ (1 – p_t)^\gamma = (1 – p_t)^0 = 1 $$

任意の実数(0でない底)について、0乗は1です。したがって変調項は消え、Focal Lossは次のように崩れます。

$$ \mathrm{FL}(p_t) = -1 \cdot 1 \cdot \log(p_t) = -\log(p_t) = \mathrm{CE}(p_t) $$

つまり $\gamma = 0$ のとき、Focal Lossは通常の交差エントロピーそのものです。これは「Focal Lossは交差エントロピーを特別な場合として含む、より一般的な損失関数である」ことを示しています。$\gamma$ を0から少しずつ大きくすると、連続的に「易しい例への注目を弱める」効果が強まっていく——そう理解すると、$\gamma$ という1つのつまみで挙動を滑らかに調整できることの意味がよくわかります。

この「交差エントロピーの自然な拡張」という性質のおかげで、既存の分類器の損失をFocal Lossに差し替えるのは容易です。実装上も、交差エントロピーに変調項を掛けるだけで済みます。では、なぜこのアイデアが物体検出から生まれたのか、その背景を見ておきましょう。

RetinaNetでの起源

Focal Lossは2017年の論文「Focal Loss for Dense Object Detection」(Lin ら)で提案されました。当時、物体検出器には2つの系統がありました。1つは候補領域を絞り込んでから分類する 2段階検出器(Faster R-CNN など)で、精度は高いが遅い。もう1つは画像全体を密に走査して一気に検出する 1段階検出器(SSD、YOLO など)で、速いが精度で一歩劣るとされていました。

著者らは、1段階検出器の精度が伸び悩む原因が 極端なクラス不均衡 にあると突き止めました。1段階検出器は画像を密に走査するため、1枚あたり10万個ものアンカーを評価しますが、その大半は易しい背景です。この易しい背景の損失合計が学習を支配し、肝心の物体を学べていなかったのです。

それまでは「ハードネガティブマイニング」(損失の大きい難しい負例だけを選んで学習する)などの手作業的な対策が使われていました。Focal Lossの貢献は、こうしたサンプリングの工夫を 損失関数の設計だけで自動化 した点にあります。難易度の重み付けを損失に埋め込むことで、すべてのサンプルを使いつつ易しいものを自動的に軽視できるようになりました。

この損失を採用した検出器が RetinaNet です。RetinaNetはFocal Lossによって、初めて1段階検出器でありながら2段階検出器を上回る精度を達成し、「速さと精度の両立」という長年の課題を解きました。

ここで一つ強調しておきたいのは、Focal Lossの本質が「データ側の操作ではなく損失側の設計」にある点です。不均衡対策には、少数派を水増しするオーバーサンプリングや、多数派を間引くアンダーサンプリング、難しい負例だけを選ぶハードネガティブマイニングなど、データの選び方を工夫する方法が数多くあります。これらは効果的な反面、サンプリング比率や閾値といった追加のハイパーパラメータを増やし、学習パイプラインを複雑にします。Focal Lossは、すべてのサンプルをそのまま使いながら、損失関数の中で自動的に重み付けを行います。データの前処理を変えずに、損失の式を差し替えるだけで済むという手軽さが、広く普及した大きな理由です。Focal Lossが分類の信号配分をどう変えるのかは、勾配の観点から見るとさらに明確になります。

勾配の比較

学習を駆動するのは損失そのものではなく、その勾配です。Focal Lossが「易しい例の勾配を消す」ことを確認しましょう。まず交差エントロピーの $p_t$ に関する勾配は、

$$ \frac{\partial}{\partial p_t}\left(-\log p_t\right) = -\frac{1}{p_t} $$

で、$p_t$ が1に近づいても大きさは1までしか小さくなりません。つまり易しい例でも勾配が残り続けます。一方、Focal Loss $-(1 – p_t)^\gamma \log p_t$ を $p_t$ で微分すると、積の微分から次のようになります。

$$ \frac{\partial \mathrm{FL}}{\partial p_t} = \gamma (1 – p_t)^{\gamma – 1}\log(p_t) – \frac{(1 – p_t)^\gamma}{p_t} $$

右辺の各項を見ると、どちらにも変調項に由来する $(1 – p_t)$ のべき乗が掛かっています。$p_t \to 1$(易しい例)では $(1 – p_t) \to 0$ となり、勾配全体が0へ吸い込まれます。実際に勾配の大きさを数値的に比較してみましょう。

交差エントロピーとFocal Lossの勾配の大きさの比較

灰色のCEの勾配は $p_t$ が大きくなっても緩やかにしか減りません。一方、Focal Loss(緑・橙・赤)の勾配は $p_t$ が0.6を超えるあたりから急速に0へ向かい、$\gamma$ が大きいほど早く消えます。これは「すでにできている易しい例は、もうパラメータをほとんど動かさない」ことを意味します。学習の力が、まだ間違えている難しい例に自動的に振り向けられるわけです。

数の不均衡そのものは、この勾配の図だけでは完全には解消できません。そこで登場するのがαバランスです。

αバランスの役割

変調項 $(1 – p_t)^\gamma$ は 難易度 の不均衡を扱います。しかし、難易度とは別に の不均衡もあります。正例が負例より100倍少ないとき、たとえ各正例の損失が大きくても、負例の数が多すぎて全体への寄与が薄まることがあります。この「数の偏り」を直接補正するのが $\alpha_t$ です。

αバランスによるクラス頻度の偏りの補正の概念図

考え方は単純です。正例(希少なクラス)には大きめの重み $\alpha$、負例(多数派のクラス)には $1 – \alpha$ を掛けます。$p_t$ と同じ流儀で $\alpha_t$ を定義すると、

$$ \alpha_t = \begin{cases} \alpha & (y = 1) \\ 1 – \alpha & (y = 0) \end{cases} $$

となります。例えば $\alpha = 0.25$ とすると、正例には0.25、負例には0.75が掛かります。一見すると負例の重みを上げているようですが、論文では変調項と組み合わせたときにこの値が最良でした。変調項がすでに易しい負例を強く抑えているため、αは残った寄与のバランスを微調整する役割に回るのです。

重要なのは、$\alpha$ と $\gamma$ が 役割分担 している点です。$\gamma$ は「易しい例 vs 難しい例」の重み付け(難易度)、$\alpha$ は「正例 vs 負例」の重み付け(頻度)を担います。この2つを組み合わせることで、Focal Lossは難易度と頻度の両方の不均衡に同時に対処できます。論文の標準設定は $\gamma = 2,\ \alpha = 0.25$ です。

理論が揃ったので、最後に実装に移ります。実装では数値安定性に注意が必要です。

実装上の注意:数値安定性とlogits版

Focal Lossを定義式の通りに (1 - p)**gamma * -log(p) と実装すると、$p$ が0に近いとき $\log(p)$ が $-\infty$ に発散し、計算が壊れます。確率 $p$ は通常シグモイド関数 $p = \sigma(z) = 1/(1 + e^{-z})$ でロジット $z$ から作りますが、$z$ が大きな負の値だと $p$ がアンダーフローして厳密に0になり、$\log(0)$ が現れてしまうのです。

数値安定性。logitから直接計算する安定形のBCE

この図は、確率を経由する素朴な計算(赤の破線)が $z \ll 0$ で不安定になる様子を示しています。対策は、確率 $p$ を経由せず、ロジット $z$ から直接損失を計算する ことです。正例 $y = 1$ の交差エントロピー $-\log \sigma(z)$ は、ソフトプラス関数を使って次のように書き直せます。

$$ -\log \sigma(z) = \log(1 + e^{-z}) = \mathrm{softplus}(-z) $$

ソフトプラスは $\log(1 + e^{x}) = \max(x, 0) + \log(1 + e^{-|x|})$ という恒等式で安定に計算でき、オーバーフローもアンダーフローも起こしません(青の実線)。実用上は、フレームワークが提供する binary_cross_entropy_with_logits のような「logits版」の関数を土台にし、それに変調項とαを掛けるのが定石です。これなら数値的に安全な交差エントロピーの上にFocal Lossを構築できます。

もう一つの注意点は、$p_t$ の計算です。実装では $y = 1$ のとき $p_t = p$、$y = 0$ のとき $p_t = 1 – p$ を、ラベルで場合分けせずに p_t = y*p + (1-y)*(1-p) のように1行で書くと簡潔でバグが減ります。同様に $\alpha_t$ も alpha_t = y*alpha + (1-y)*(1-alpha) とまとめられます。これらを踏まえて、実際のコードを書きましょう。

Pythonでの実装

まず、定義に忠実なnumpy版から実装します。外部依存はnumpyだけです。確率 $p$ とラベル $y$ を受け取り、Focal Lossを返す関数です。

import numpy as np

def focal_loss_prob(p, y, gamma=2.0, alpha=0.25, eps=1e-8):
    """確率 p とラベル y から Focal Loss を計算(数値安定のため clip)。
    p: クラス1の予測確率, y: 0/1ラベル"""
    p = np.clip(p, eps, 1.0 - eps)          # log(0) を防ぐ
    p_t = y * p + (1 - y) * (1 - p)          # 正解クラスの予測確率
    alpha_t = y * alpha + (1 - y) * (1 - alpha)
    loss = -alpha_t * (1 - p_t) ** gamma * np.log(p_t)
    return loss

# 動作確認: 易しい例と難しい例で比べる
p = np.array([0.9, 0.9, 0.3, 0.1])
y = np.array([1,   1,   1,   1])
print("CE   :", -np.log(p).round(4))
print("Focal:", focal_loss_prob(p, y, gamma=2.0, alpha=1.0).round(4))

このコードを実行すると、CEが [0.1054 0.1054 1.204 2.3026]、Focal($\alpha=1$)が [0.0011 0.0011 0.5899 1.8651] となります。易しい例($p_t=0.9$)の損失が0.1054から0.0011へと約100分の1に潰れ、難しい例($p_t=0.1$)は2.30から1.87とわずかしか減っていません。先ほどの図4の値と一致しており、変調項が易しい例だけを狙い撃ちしていることが確認できます。

次に、数値安定性を確保したlogits版を実装します。実務ではこちらを使います。確率ではなくロジット $z$ を直接受け取り、ソフトプラスを使って安定に計算します。

import numpy as np

def focal_loss_logits(z, y, gamma=2.0, alpha=0.25):
    """ロジット z から数値安定に Focal Loss を計算する。
    z: シグモイド前のロジット, y: 0/1ラベル"""
    # 安定なシグモイド
    p = np.where(z >= 0, 1 / (1 + np.exp(-z)), np.exp(z) / (1 + np.exp(z)))
    p_t = y * p + (1 - y) * (1 - p)
    # 安定な log(p_t): 正例は -softplus(-z), 負例は -softplus(z)
    def log_sigmoid(x):  # log(sigmoid(x)) = -softplus(-x)
        return -(np.maximum(-x, 0) + np.log1p(np.exp(-np.abs(x))))
    log_pt = y * log_sigmoid(z) + (1 - y) * log_sigmoid(-z)
    alpha_t = y * alpha + (1 - y) * (1 - alpha)
    loss = -alpha_t * (1 - p_t) ** gamma * log_pt
    return loss

# 大きな負のロジットでも壊れないことを確認
z = np.array([-30.0, -5.0, 0.0, 5.0, 30.0])
y = np.array([1, 1, 1, 1, 1])
print(focal_loss_logits(z, y, gamma=2.0, alpha=1.0))

このlogits版は、$z = -30$(正例なのに強く背景と予測=大誤分類)のような極端な入力でも infnan を出さずに大きな有限の損失を返します。確率版で log(0) の発散を心配する必要がなくなり、log1pmaximum による安定形のおかげでオーバーフローも避けられます。これが実務でlogits版が好まれる理由です。

最後に、PyTorch風の書き方も示しておきます。実際のディープラーニングでは、フレームワークの提供する安定なBCE関数を土台にするのが定石です。

import torch
import torch.nn.functional as F

def focal_loss_torch(logits, targets, gamma=2.0, alpha=0.25, reduction="mean"):
    """PyTorch風 Focal Loss。logits と 0/1 の targets を受け取る。"""
    # 安定なBCEを土台にする(要素ごと、reduction なし)
    ce = F.binary_cross_entropy_with_logits(logits, targets, reduction="none")
    p = torch.sigmoid(logits)
    p_t = targets * p + (1 - targets) * (1 - p)        # 正解クラスの確率
    alpha_t = targets * alpha + (1 - targets) * (1 - alpha)
    loss = alpha_t * (1 - p_t) ** gamma * ce            # 変調項とαを掛ける
    if reduction == "mean":
        return loss.mean()
    elif reduction == "sum":
        return loss.sum()
    return loss

このPyTorch版は binary_cross_entropy_with_logits(内部で安定形を使う)の出力に変調項 $(1 – p_t)^\gamma$ とαバランス $\alpha_t$ を掛けるだけの構成です。ce がすでに $-\log(p_t)$ に相当するため、これに重みを掛ければそのままFocal Lossになります。$\gamma = 0,\ \alpha = 1$ にすれば、F.binary_cross_entropy_with_logits と完全に一致することも確認できます。たった数行で、不均衡データに強い損失関数が手に入るわけです。

まとめ

本記事では、Focal Lossについて、クラス不均衡の問題から定義・導出・実装までを解説しました。

  • 問題: クラス不均衡では、大量の易しいサンプルの損失合計が学習を支配し、難しい少数派が埋もれる。これは数と難易度の二重の不均衡が原因。
  • 定義: $\mathrm{FL}(p_t) = -\alpha_t (1 – p_t)^\gamma \log(p_t)$。交差エントロピーに変調項とαバランスを掛けたもの。
  • 変調項 $(1 – p_t)^\gamma$: 易しい例($p_t$ 大)の損失を強く削り、難しい例は残す。$\gamma$ で効き具合を調整する。
  • αバランス $\alpha_t$: 正例と負例の数の偏りを補正する。$\gamma$ が難易度、$\alpha$ が頻度を担い役割分担する。
  • $\gamma = 0$ で変調項が1になり、Focal Lossは交差エントロピーに一致する(一般化)。
  • 起源: 物体検出器RetinaNetで提案され、1段階検出器を最先端へ押し上げた。
  • 実装: 確率を経由せずロジットから直接計算する安定形(softplus利用)が定石。標準設定は $\gamma = 2,\ \alpha = 0.25$。

Focal Lossは交差エントロピーへのほんの小さな修正でありながら、不均衡データでの学習を根本から変えます。アイデアの核心は「損失関数の設計だけで、難しいサンプルへ学習を集中させる」ことです。この発想は物体検出にとどまらず、レアイベント分類や異常検知など「正例が少ない」あらゆる場面で応用できます。

次のステップとして、以下の記事も参考にしてください。