Pythonでのsigmoid関数の高速な実装


概要

機械学習アルゴリズムで頻繁に利用されるsigmoid関数だが、Pythonで実現しようとすると複数の方法が存在します。そこで、いくつかの実装の中でどれが一番速いのかを調べてみました。

方法

思いついた以下の方法について、sigmoidかけた値を算出して比較します。ただし、4と5に関しては使えるシーンが限定されている方法です。

  1. scipy.special.expitを呼ぶ (シンプルで良く使われている印象)
  2. 定義 $\frac{1}{1 + exp(-x)}$ 通り計算
  3. tanhを使って $0.5 \cdot \tanh(0.5 \cdot x) + 0.5$ で計算
  4. 与えられる値のrangeが決まっている場合、事前に計算しておき配列で保存
  5. 0付近の値が与えられない場合、$\frac{x}{1 + |x|}$ で算出

実装

import numpy as np
from scipy.special import expit # 1.

# 2.
normal_expit = lambda x: 1 / (1 + np.exp(-x))
# 3.
tanh_expit = lambda x: 0.5 * np.tanh(0.5 * x) + 0.5

# 4.
min_range, max_range = (-5, 5)
exponent = 6
pre_computed = expit(np.arange(min_range, max_range, 10 ** - exponent))

def computed_expit(x, min_range, pre_computed):
    return pre_computed[np.floor((x - min_range) * (10 ** exponent)).astype(int)]

abs_expit = lambda x: x / (1 + np.abs(x)) # 5.

結果

scipy.special.expitが他の方法と比較して2.5倍以上遅いことが分かりました。制約なしの中では定義通りに計算するのが速く、rangeの範囲の問題はある可能性はありますが、4は性能改善が誤差程度で、当然ですが5は高速ですが0付近で異なる値を返すという欠点がありました。

あとがたり

exp(-x)を計算する部分がボトルネックなので、ここを解消するためにフーリエ変換を用いて多項式に分解する方法とかもあるんですが、結局精度がいまいちでず、汎用的な近似にはなりませんでした…残念