(56) 逆伝播で使う tanh(x)の微分

投稿者: | 2025年6月12日

872 views

この記事は最終更新から 368日 が経過しています。

【1】やりたいこと

PyTorchや TensorFlowなどの機械学習ライブラリを使わない場合、ライブラリが提供してくれている機能を自力実装する必要がある。

順伝播と逆伝播を比較すると・・・
順伝播は、相対的に処理が単純で、プログラムの実装難易度は相対的に低い。
逆伝播は、パラメータごとに偏微分関数を計算する必要があり、順伝播と比べて複雑だ。

今回は、活性化関数の一つである y = tanh(x) について、逆伝播時に使う導関数を見てみる。

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

【2】やってみる

1) 活性化関数とは?

活性化関数とは、ニューロンからの出力値を決める関数のこと。
入力値と重み係数をかけ合わせて積算した値は、パラメータ数が増えても線形変換でしかない。

そこで・・・
sigmoidや tanhなどの非線形関数を通すことにより、複雑なパターンを学習可能にする。

2) tanhとは?

双曲線関数 sinh(x), cosh(x) を使って以下のように表す。
tanh(x) = sinh(x) / cosh(x)

参考: 双曲線の方程式
cosh2(x) – sinh2(x) = 1

この三者をグラフ上に表すと下図の通り。
y = sinh(x)
y = cosh(x)
y = tanh(x) = sinh(x) / cosh(x)

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3, 3, 500)    # x の範囲
sinh_x = np.sinh(x)            # 各関数の値を計算
cosh_x = np.cosh(x)
tanh_x = np.tanh(x)
plt.figure(figsize=(10, 6))    # グラフ描画
plt.plot(x, sinh_x, label='sinh(x)', color='blue')
plt.plot(x, cosh_x, label='cosh(x)', color='red')
plt.plot(x, tanh_x, label='tanh(x)', color='green')
plt.title("sinh(x), cosh(x), tanh(x)")
plt.xlabel("x")
plt.ylabel("value")
plt.grid(True)
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.legend()
plt.show()

y = tanh(x) だけを拡大して表示すると下図の通り。
y = sigmoid(x) と同様に、S字に収束する関数だ。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3, 3, 500)       # x の範囲
tanh_x = np.tanh(x)
plt.figure(figsize=(10, 6))       # グラフ描画
plt.plot(x, tanh_x, label='tanh(x)', color='green')
plt.title("tanh(x)")
plt.xlabel("x")
plt.ylabel("value")
plt.grid(True)
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.legend()
plt.show()

3) S字関数を使う理由は?

■長所
非線形なので、複雑な関数やパターンが学べる。
出力が一定範囲に収まるため、安定性がある。
なめらかで微分可能(=連続的)なため、勾配降下法が使える。

■短所
xの絶対値が大きくなると、微分がほぼ 0になる。→ 勾配消失

4) tanhの微分を求める。

(1) tanh, tanh’をグラフ化

まず先に、y = tanh(x) を微分すると、下図のようになる。
微分値は x=0で最大値となり、S字の先へ向かうに従い 0に収束していく。→ 勾配消失

■勾配消失
出力層で算出した誤差に基づき、各パラメータの修正量(=勾配)を入力層へ向かって逆伝播していく際、勾配の値が層を通るごとに次第に小さくなり、最終的に0に近づいてしまう現象。
この結果、入力層近くの重みがほとんど更新されず、学習が進まなくなる。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 500)
tanh_x = np.tanh(x)
tanh_deriv = 1 - tanh_x**2
plt.figure(figsize=(10, 6))
plt.plot(x, tanh_x, label='tanh(x)', color='blue')
plt.plot(x, tanh_deriv, label="d/dx tanh(x) = 1 - tanh^2(x)", color='red', linestyle='dashed')
plt.title("tanh(x) and its derivative")
plt.xlabel("x")
plt.ylabel("value")
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.grid(True)
plt.legend()
plt.show()

(2) tanh’の計算式

■証明
以下、愚直にこれを証明してみる。

この三式を商の微分公式に当てはめると、求める微分値は以下の通り。
・・・(1)

ここで、

これを (1) に代入すると、

OKだ!

5) tanhの微分を Pythonで実装する。

def tanh(x):
    return np.tanh(x)

def tanh_derivative(x):
    return 1.0 - np.tanh(x)**2

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

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


アクセス数(直近7日): ※試験運用中、BOT除外簡易実装済
  • 2026-06-19: 0回
  • 2026-06-18: 0回
  • 2026-06-17: 2回
  • 2026-06-16: 0回
  • 2026-06-15: 0回
  • 2026-06-14: 1回
  • 2026-06-13: 0回
  • コメントを残す

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