(60) 多クラス分類で使う Softmax

投稿者: | 2025年6月15日

1,127 views

【1】やりたいこと

PyTorchや TensorFlowなどの機械学習ライブラリを使わない場合、ライブラリが提供してくれている機能を自力実装する必要がある。
本項では、MNISTや CIFAR-10のような多クラス分類で使用する Softmax関数を実装 してみる。

機械学習ライブラリを使わない Python自力実装シリーズ は以下の通り。
(56) 逆伝播で使う tanh(x)の微分
(57) 逆伝播で使う sigmoid(x)の微分
(58) 逆伝播で使う ReLU(x)の微分
(59) 逆伝播で使う MSE(平均二乗誤差)の微分
(60) 他クラス分類で使う Softmax ←今回
(61) 機械学習で多用されるネイピア数とは?

【2】やってみる

多クラス分類タスクでは、
 出力層における各クラススコアを Softmax 関数に通すことで、
  全クラスに対する確率分布に変換する。
→ 扱いやすくなる!

1) Softmaxの計算式

Softmaxとは、
複数の入力値に対して、
 出力値の合計が 1になるように変換・出力し、
  確率っぽい値を作るロジックのこと。

n出力層のニューロン数
zi出力層のニューロン[i:1,2,3,…]の出力値
pisoftmax関数の出力値(=予測確率) ※ラベルごとに確率を出す。

2) 分かりやすい例で確認

出力層のニューロン数 3個の場合について見てみる。
🔸出力層のニューロンの位置によって変わるのは、分子の指数だけ。
🔸また、分母には全ニューロンの出力値が含まれており、確率 p の算出には他ラベルの出力値も考慮されている。

例えば、3クラス分類タスクにおいて、出力層から得られるスコアが [2, 3, 5] であったとする。
これらの値は未正規化のスコア(logits)であり、そのままでは確率として解釈できない ため、直接的な利用が難しい。

そこで、Softmax関数を適用することで、[0.04, 0.12, 0.84] のような 総和が 1となる確率分布 に変換する。
このように正規化された出力は [0, 1, 0] のような one-hot 表現の正解ラベルと照合可能になり、損失関数(例えば交差エントロピー損失)による学習が容易になる。

積算値が 1 になるのは当然 だ。softmaxはそう定義しているのだから・・・

3) Softmaxの特徴

(1) ただの Maxではなく Softとは?

Max関数最大値を選ぶ。
Softmax関数最大値(=最も可能性の高いラベル)を選びつつ、他ラベルの可能性の大きさも表せる。

(2) e を使うと何が嬉しい?

出力層から得られるスコア zi[2, 3, 5] の場合について、Softmax関数の出力値 pi を下図に記す。
Softmaxは線形比率ではなく指数関数で重み付けする ため、
下図に示すように zi よりも pi の方が 強者が強調 される。

これにより・・・
正解ラベルの確率を高めることができ、分類器の訓練が進みやすくなる。

4) Softmaxを Pythonで実装

"""/////////////////////////////////////////////////////////////////////////////
softmax関数
入力: x は 1次元または 2次元のnumpy配列
出力: 各要素を確率に変換した numpy配列(全要素の合計=1)
- 1次元: [z0, z1, z2] → [p0, p1, p2]
- 2次元: 各行ごとにsoftmax(バッチデータ対応)
/////////////////////////////////////////////////////////////////////////////"""
def softmax(x):
    x = np.asarray(x)
    #---------------------------------------------------------------------------
    if x.ndim == 1:                                         # 入力データが 1次元?(単一データ)
        e_x = np.exp(x - np.max(x))                         # 数値安定化(1)
        #e_x = np.exp(x)                                    # 数値安定化(2) ※(1),(2)のどちらでも同じ出力値になる。
        return e_x / e_x.sum()
    #---------------------------------------------------------------------------
    elif x.ndim == 2:                                       # 入力データが 2次元?(複数データ)
        e_x = np.exp(x - np.max(x, axis=1, keepdims=True))
        return e_x / e_x.sum(axis=1, keepdims=True)
    #---------------------------------------------------------------------------
    else:
        raise ValueError('入力は1次元または2次元の配列である必要があります。')

シンプルな実装に見えるが、ここには Python + Numpyの底力が隠れている。
numpyのベクトル化関数を使っているので、入力値 xは多次元配列(ndarray)を使える。
→ ミニバッチデータ等、複数データをまとめて処理できる。

※実際のデータ並列処理はライブラリ内部に隠蔽されている。

いくつか試してみる。

>>> z = np.array([5, 5])
>>> p = softmax(z)
>>> p
array([0.5, 0.5])
>>>
>>> z = np.array([2, 3, 5])
>>> p = softmax(z)
>>> p
array([0.04201007, 0.1141952 , 0.84379473])

ミニバッチの場合(=複数の訓練データを一括処理する場合)

>>> z = np.array([[2, 3, 5],[1, 5, 7]])
>>> p = softmax(z)
>>> p
array([[0.04201007, 0.1141952 , 0.84379473],
       [0.00217852, 0.11894324, 0.87887824]])

【3】所感

本当は、Softmax → CrossEntropy → 誤差算出 → 勾配初期値算出(CrossEntropy, Softmaxの微分)までを一気通貫で記録しておきたかった。
でも…
文量がかなり増えそうだったので、本項では順伝播時の Softmaxだけを扱った。

今後は、以下の順に投稿していこう。
・Softmaxの微分
・順伝播: CrossEntropyLossによる損失算出
・CrossEntropyLossの微分
・CrossEntropyLoss + Softmaxの導関数を使った勾配初期値算出

その後、以下のような全体の訓練の流れについて投稿する。
・順伝播
・逆伝播
・パラメータ更新

で・・・
これらを用いて、以下の応用例(基礎編だが)を作る。
・Sin関数の近似(回帰タスク)
・MNIST分類(分類タスク)

まずは 機械学習ライブラリを使わない Python自力実装シリーズ をここまで進めよう。
目標は 7/末まで。

あっ、オセロAIを全然進めていないぞ・・・


アクセス数(直近7日): ※試験運用中、BOT除外簡易実装済
  • 2026-05-04: 0回
  • 2026-05-03: 0回
  • 2026-05-02: 1回
  • 2026-05-01: 2回
  • 2026-04-30: 2回
  • 2026-04-29: 1回
  • 2026-04-28: 0回
  • コメントを残す

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です