シグモイド関数の定義と実装
シグモイド関数 (Sigmoid function) は
\[y=\frac{1}{1+e^{-ax}}\quad (a\gt 0)\tag{1}\]
によって定義され、ニューラルネットワーク においてニューロンの特性を表す活性化関数として登場します。入力値 $x$ に対して、出力値 $y$ は $0$ から $1$ の値をとるという性質があります。Matplotlib と NumPy を使って $a$ を変化させながらシグモイド関数のグラフを描いてみます。
# PYTHON_SIGMOID_FUNCTION
# In[1]
import numpy as np
import matplotlib.pyplot as plt
# シグモイド関数を定義
def sigmoid(x, a):
return 1 / (1 + np.exp(-a * x))
# フィギュアを設定
fig = plt.figure()
# グラフ描画領域を追加
ax = fig.add_subplot(111)
# グリッド線を表示
ax.grid(linestyle="--")
# グラフタイトルを設定
ax.set_title("Sigmoid Function", fontsize=16)
# x軸, y軸のラベルを設定
ax.set_xlabel("x", fontsize=16)
ax.set_ylabel("y", fontsize=16)
# -10~10,256分割の点
x = np.linspace(-10, 10, 256)
# シグモイド関数をプロット
a = [0.5, 1, 4]
for k in range(3):
y = sigmoid(x, a[k])
ax.plot(x, y, label="a = {}".format(a[k]))
# 凡例を表示
ax.legend()
plt.show()
シグモイド という名称は、曲線の形がギリシャ文字の $\varsigma$ (シグマ) に似ていることに由来します。$a$ はゲイン (gain) とよばれるパラメータです。図にあるように、$a$ が大きくなるほど原点付近の傾斜は大きくなって階段関数に近づいていきます。特にゲイン $a$ が $1$ のときは 標準シグモイド関数 とよばれます。
シグモイド関数の微分
シグモイド関数を微分して導関数を求めてみます。
定義式 (1) の両辺に $1+e^{-ax}$ を掛けて右辺の分母を払うと
\[(1+e^{-ax})y=1\tag{2}\]
となります。両辺を $x$ で微分すると、積の微分公式により
\[\begin{align*}(1+e^{-ax})'y+(1+e^{-ax})y'=&0\tag{3}\\[6pt]
-ae^{-ax}y+(1+e^{-ax})y'=&0\tag{4}\\[6pt]\end{align*}\]
となるので、一次導関数
\[y'=ay\,\frac{e^{-ax}}{1+e^{-ax}}=ay(1-y)\tag{5}\]
が得られます。この式をもう一度微分すると 二次導関数を得ます。(5) の表式を用いれば簡単です。
\[\begin{align*}y''=&a\{y'(1-y)-yy'\}\\[6pt]=&a\{ay(1-y)(1-y)-yay(1-y)\}\\[6pt]
=&a^2y(1-y)(1-2y)\\[6pt]\end{align*}\tag{6}\]
ある関数 $f(x)$ と、その導関数 $f'(x),\ f''(x)$ はクラスの中にメソッドとしてまとめてしまうと便利です。以下にクラス定義の一例を掲載しておくので、参考にしてください。
# In[2]
# シグモイドクラスを定義
class Sigmoid:
# ゲインの値をインスタンス変数aに格納
def __init__(self, gain):
self.a = gain
# シグモイド関数
def sigmoid(self, x):
return 1 / (1 + np.exp(-self.a * x))
# シグモイド関数のデータ
def sigmoid_data(self, x_range, n):
x_data = np.linspace(x_range[0], x_range[1], n)
y_data = self.sigmoid(x_data)
return x_data, y_data
# シグモイド関数の一次導関数
def first(self, x):
y = self.sigmoid(x)
return self.a * y * (1 - y)
# 一次導関数のデータ
def first_data(self, x_range, n):
x_data = np.linspace(x_range[0], x_range[1], n)
y_data = self.first(x_data)
return x_data, y_data
# シグモイド関数の二次導関数
def second(self, x):
y = self.sigmoid(x)
return self.a**2 * y * (1 - y)*(1 - 2*y)
# 二次導関数のデータ
def second_data(self, x_range, n):
x_data = np.linspace(x_range[0], x_range[1], n)
y_data = self.second(x_data)
return x_data, y_data
Sigmoid クラスのインスタンスは、コンストラクタにゲイン a を指定して生成します。たとえば Sigmoid(1) はゲイン 1 のシグモイド関数、およびその一次導関数、二次導関数をまとめたインスタンスです。
Sigmoid.sigmoid() は任意の引数 x におけるシグモイド関数の値を返します。引数 x には数値または配列を指定できます。
Sigmoid.sigmoid_data() はシグモイド関数のプロット用の連続データを生成するメソッドです。引数 x_range には変数 x の範囲 (x の最小値と最大値) をリストやタプルで指定します。n は分割数です。
Sigmoid.first() は引数 x (数値または配列) におけるシグモイド関数の一次導関数の値を返します。
Sigmoid.first_data() はシグモイド一次導関数のプロット用のデータを生成します。引数 x_range には変数 x の範囲を指定します。第 2 引数 n で分割数を指定できます。
Sigmoid.second() は引数 x (数値または配列) におけるシグモイド関数の二次導関数の値を返します。
Sigmoid.second_data() はシグモイド二次導関数のプロット用のデータを生成します。引数 x_range には変数 x の範囲を指定します。第 2 引数 n で分割数を指定します。
ゲイン 1 のシグモイドクラスのインスタンスを生成して、シグモイド関数および一次導関数、二次導関数のプロット用データを生成してみます。
# In[3]
# ゲイン1のシグモイドクラスのインスタンスを生成
my_func = Sigmoid(1)
# 変数xの範囲を設定
x_range = [-8, 8]
# 連続データの分割数を設定
n = 257
# シグモイド関数のデータを取得
data_0 = my_func.sigmoid_data(x_range, n)
# 一次導関数のデータを取得
data_1 = my_func.first_data(x_range, n)
# 二次導関数のデータを取得
data_2 = my_func.second_data(x_range, n)
作成したデータを使って、Matplotlib でグラフを表示してみます。
# In[4]
# フィギュアとサブプロット
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid(linestyle="--")
ax.set_title("Sigmoid Function (a=1)", fontsize=16)
ax.set_xlabel("x", fontsize=16)
ax.set_ylabel("y", fontsize=16)
# シグモイド関数をプロット
ax.plot(data_0[0], data_0[1], label="Sigmoid function")
# 一次導関数をプロット
ax.plot(data_1[0], data_1[1], label="first derivative")
# 二次導関数をプロット
ax.plot(data_2[0], data_2[1], label="second derivative")
# 凡例を表示
ax.legend()
plt.show()
逆に微分方程式
\[\frac{dy}{dx}=ay(1-y)\tag{7}\]
解いてシグモイド関数を導いてみましょう。上式を変数分離すると
\[\frac{dy}{y(1-y)}=adx\tag{8}\]
となります。左辺を分解すると
\[\left(\frac{1}{y}+\frac{1}{1-y}\right)dy=adx\tag{9}\]
両辺を積分すると
\[\begin{align*}\log|y|-\log|1-y|&=ax+C\\[6pt]\log\left|\frac{y}{1-y}\right|&=ax+C\\[6pt]\frac{y}{1-y}&=\pm e^{ax+C}\\[6pt]\end{align*}\tag{10}\]
$A=\pm e^C$ とおいて式を整理すると、
\[y=\frac{A}{A+e^{-ax}}\tag{11}\]
が得られます。初期条件 $y(0)=1/2$ を代入すると $A=1$ が決まるので、
\[y=\frac{1}{1+e^{-ax}}\tag{12}\]
となってシグモイド関数が現れます。
scipy.special.expit()
SciPyパッケージ の scipy.special.expit(x) は標準シグモイド関数を計算します。
# SCIPY_SPECIAL_EXPIT
# In[1]
import numpy as np
from scipy.special import expit
# 配列xを定義
x = np.array([-0.5, 0, 0.5])
# シグモイド関数を計算
s = expit(x)
print(s)
# [0.37754067 0.5 0.62245933]
$a$ の値を指定してシグモイド関数の値を計算したい場合は、以下のような関数を定義します。
# In[2]
# シグモイド関数
def sp_sigmoid(a, x):
return expit(a*x)
# a=2を指定してシグモイド関数を計算
s = sp_sigmoid(2, x)
print(s)
# [0.26894142 0.5 0.73105858]
下記は誤植と思われますので、ご確認ください。
(2) 式の下の文章で、部分積分により → 積の微分公式により
シグモイドクラス定義のプログラムの下から 2 番目のコメントで、
シグモイド関数の一次導関数 → シグモイド関数の二次導関数
さっそくありがとうございます。訂正しておきました。
この記事で扱っているシグモイド関数は、もう少し先の記事で機械学習のプログラムに実装されます。楽しみにしていてください。
下記は誤植と思われますので、ご確認ください。
FUNCTION_01 In[2] プログラムのコメントで、
14行目 # 一次導関数のデータ → # シグモイド関数のデータ
25行目 # 二次導関数のデータ → # 一次導関数のデータ
修正しました。
いつもありがとうございます。m(_ _)m
【GPT講義】シグモイド関数の応用例
シグモイド関数は幅広い分野で応用されています。以下にいくつかの応用例をいくつか挙げてみましょう。
生物学と神経科学: シグモイド関数は、神経細胞の活動ポテンシャルの伝達や神経系のモデリングに使用されます。神経細胞が信号を伝達するプロセスでは、シグモイド関数に類似した非線形な振る舞いが見られます。
医学と医療: シグモイド関数は、医学的な診断や予測モデルの構築にも応用されます。例えば、病気の進行や治療効果のモデリングに使用され、患者の状態の変化を捉えるために活用されます。
経済学とファイナンス: シグモイド関数は、市場の成長や変動をモデリングする際に使用されます。特に、市場の成長率や需要曲線の変化を表現するために利用されることがあります。
機械学習とニューラルネットワーク: シグモイド関数は、ニューラルネットワークの活性化関数として使用されます。これにより、ニューラルネットワークが非線形な関数をモデル化し、複雑なデータのパターンを捉える能力を持つようになります。
心理学と社会科学: シグモイド関数は、感情や意識の変化、人々の態度や行動の予測モデルに使用されることがあります。人間の心理的なプロセスをモデル化する際に活用されます。
自然言語処理: シグモイド関数は、自然言語処理において、テキストの感情分析や確率的なモデリングに応用されることがあります。テキストの極性(ポジティブ、ネガティブ、ニュートラル)を判定するために使用されることがあります。
これらはシグモイド関数の一部の応用例ですが、実際には他にも多くの分野で利用されています。シグモイド関数はその非線形な性質から、さまざまな現象やプロセスのモデリングに有用なツールとして広く活用されています。