活性化関数一覧 (2020)

# 対象者
活性化関数について、どんなものがあるのかまとめました。
**最新のSwishとMish、さらにtanhExpも載せています！**
一覧探してもなかなか良いの見つからないな〜という層をターゲットにしてます。
新しいのは見つけ次第追加します。
新しい関数の情報や、下記のTODOリストにある関数の情報をお持ちの方はぜひ教えてください！
勾配降下法も[こちら](https://qiita.com/kuroitu/items/36a58b37690d570dc618)でまとめています。

何かの役に立てばぜひLGTMやストック、コメントしていただけると励みになります！


# 更新履歴(2020/9/22~)
- 2020/9/22
    - softmax関数の微分の修正
    - softmin関数の微分の訂正とグラフの差し替え
    - logSoftmax関数の微分の訂正
    - バッチ法、ミニバッチ法に対応したコードに差し替え
    - ~~メモリを犠牲に計算回数を削減する工夫をしてみました。有効かは未検証です。~~
- 2020/9/23
    - 効果がほぼなかったので修正
- 2020/9/27
    - hardSwish関数を追加
    - `_act_dic`と`get_act`関数の実装を少し修正し、入力の自由度を向上させました。
- 2020/10/20
    - ACON関数の情報を追加
    - 実装はしばしお待ちください...
- 2020/10/21
    - ACON関数の情報をちょっと修正
    - ACON関数の実装を追加
        - ただしハイパーパラメータの学習については実装してみましたが動作未確認です。
        - いずれ動くかどうか確認します。


# TODOリスト
- hardShrink関数の補足情報を調べる
- softShrink関数の補足情報を調べる
- Threshold関数の補足情報を調べる
- logSigmoid関数の補足情報を調べる
- tanhShrink関数の補足情報を調べる
- hardtanh関数の補足情報を調べる
- ReLU6関数の補足情報を調べる
- CELU関数の補足情報を調べる
- softmin関数の補足情報を調べる
- logSoftmax関数の補足情報を調べる
- 未実装のいくつかの関数について調べる
    - Pytorch
        - MultiheadAttention関数とは
        - PReLU関数の学習メソッド
        - RReLU関数の実装
        - GELU関数に出てくる累積分布関数とは？
        - GELU関数の実装
        - Softmax2d関数の実装


# 目次
 - [ステップ関数(step)](#ステップ関数step)
 - [恒等関数(identity)](#恒等関数identity)
 - [Bent Identity関数](#bent-identity関数)
 - [hardShrink関数](#hardshrink関数)
 - [softShrink関数](#softshrink関数)
 - [Threshold関数](#threshold関数)
 - [シグモイド関数(sigmoid)](#シグモイド関数sigmoid)
 - [hardSigmoid関数](#hardsigmoid関数)
 - [logSigmoid関数](#logsigmoid関数)
 - [tanh関数](#tanh関数)
 - [tanhShrink関数](#tanhshrink関数)
 - [hardtanh関数](#hardtanh関数)
 - [ReLU関数](#relu関数)
 - [ReLU6関数](#relu6関数)
 - [leaky-ReLU関数](#leaky-relu関数)
 - [ELU関数](#elu関数)
 - [SELU関数](#selu関数)
 - [CELU関数](#celu関数)
 - [ソフトマックス関数(softmax)](#ソフトマックス関数softmax)
 - [softmin関数](#softmin関数)
 - [logSoftmax関数](#logsoftmax関数)
 - [softplus関数](#softplus関数)
 - [softsign関数](#softsign関数)
 - [Swish関数](#swish関数)
 - [hardSwish関数](#hardswish関数)
 - [ACON関数](#acon関数)
 - [Mish関数](#mish関数)
 - [tanhExp関数](#tanhexp関数)
 - [コード例](#コード例)


# ステップ関数(step)
まずはステップ関数から。もっとも歴史ある活性化関数でしょう。
![step.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/c5c5949e-14ef-6275-4ed7-0cc55f05af91.png)
当時はパーセプトロンの実装に用いられていましたが、昨今の深層学習ではまず見かけません。
<font>その理由は微分が($x \ne 0$の)全ての実数で$0$となるため、誤差逆伝播によるパラメータの最適化ができないためですね。</font>

順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    1 & (x \gt 0) \\
    0 & (x \le 0)
  \end{array}
\right.
```

こんな感じで、逆伝播の数式は当然ですが

```math
\cfrac{\partial y}{\partial x} = 0 \quad (x \ne 0)
```

となり、これを流れてきた誤差に乗算します。つまり何も流しません。
このため、誤差逆伝播法を適用できず、深層学習においては日陰に追いやられることになりました。


# 恒等関数(Identity)
恒等関数は入力をそのまま出力します。回帰分析の出力層の活性化関数に使われますね。中間層での出番はありません。
わざわざこのような活性化関数を使うのは**一意的に実装するため**です。一意的な実装とは、ここでは条件分岐などで処理をわけないことを意図しています。
![identity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/57b57530-fdbc-c6ff-f8f5-d1e9cdbb3dc2.png)
<font>微分値は$1$なので誤差がそのまま前の層へ伝播します。誤差計算は二乗誤差を用いるので次の層への伝播は$y - t$になりますね〜</font>

順伝播の数式は

```math
y = x
```

で、逆伝播は

```math
\cfrac{\partial y}{\partial x} = 1
```

となります。流れてきたそのままの値を流すことになることが確認できますね！


# Bent Identity関数
[Identity関数](#恒等関数identity)と類似の関数です。
ただし直線的ではなく少し曲がっているのが特徴です。
![bent-identity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/eb57ee1b-0b10-294a-2373-9566ee2db1a3.png)

順伝播の数式は

```math
y = \cfrac{1}{2}(\sqrt{x^2 + 1} - 1) + x
```

こんな感じで、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \cfrac{x}{2 \sqrt{x^2 + 1}} + 1
```

となります。なんとなく[ReLU関数](#relu関数)っぽい(個人の感想です)。
日本語で紹介してる記事はぱっと見で探したところ見つからなかったのでマイナーな活性化関数なのかなと思います。


# hardShrink関数
Pytorchより、とりあえず紹介だけです。
**TODO 補足情報を調べる**
![hard-shrink.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/f68a484a-8c4e-5e31-35bf-556305f99868.png)
順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    x & (x \lt -\lambda \quad \textrm{or} \quad \lambda \lt x) \\
    0 & (\textrm{otherwise})
  \end{array}
\right.
```

で、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    1 & (x \lt -\lambda \quad \textrm{or} \quad \lambda \lt x) \\
    0 & (\textrm{otherwise})
  \end{array}
\right.
```

となります。<font>$\lambda$のデフォルト値は$0.5$です。</font>


# softShrink関数
Pytorchより、こちらも紹介だけ。
**TODO 補足情報を調べる**
![soft-shrink.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/9a1e48e1-f093-fafb-d6a3-8a048a601091.png)
順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    x + \lambda & (x \lt -\lambda) \\
    x - \lambda & (x \gt \lambda) \\
    0 & (\textrm{otherwise})
  \end{array}
\right.
```

で、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    1 & (x \lt -\lambda \quad \textrm{or} \quad \lambda \lt x) \\
    0 & (\textrm{otherwise})
  \end{array}
\right.
```

となります。<font>こちらの$\lambda$の初期値も$0.5$ですね。</font>


# Threshold関数
Pytorchより、これまた紹介だけです。
**TODO 補足情報を調べる**
![threshold.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/b59e611d-501c-546d-44b2-ae8a4acfb696.png)
順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    x & (x \gt threshold) \\
    value & (\textrm{otherwise})
  \end{array}
\right.
```

で、逆伝播は

```math
y = \left\{
  \begin{array}{cc}
    1 & (x \gt threshold) \\
    0 & (\textrm{otherwise})
  \end{array}
\right.
```

<font>となります。ここで、変数$threshold$と$value$は事前に与えられるべき値です。グラフではとりあえず適当に</font>

```math
threshold = -1 \\
value = -2
```

としています。


# シグモイド関数(sigmoid)
シグモイド関数は誤差逆伝播法が登場した頃によく使用されていた活性化関数ですね。しかし今は中間層に使われることは少なく、2値分類問題の出力層に使われることが多いですね。
理由は後述のデメリットのためです。
![sigmoid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/54bbccf4-25f3-c25c-9b95-334dde1c53dd.png)
順伝播は

```math
y = \cfrac{1}{1 + e^{-x}}
```

逆伝播は

```math
\cfrac{\partial y}{\partial x} = y(1 - y)
```

のように書くことができます。
<font>微分を出力から簡単に求められることが最大の特徴ですが、極端に大きい・小さい入力に対しての応答が悪いことや、微分の最大値が$0.25$であるために層を重ねると**勾配消失**の問題が起こるなどのデメリットもあります。</font>
あと指数計算と割り算があるため、[ReLU関数](#relu関数)などのシンプルな関数と比べてどうしても計算負荷が高くなります。


# hardSigmoid関数
シグモイド関数を1次関数などの直線で近似したものがhardSigmoid関数です。
![hard-sigmoid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/45c44607-6a8d-0729-cb1b-4d2990950800.png)
数式的には、順伝播は

```math
y = \left\{
  \begin{array}{cc}
    1 & (x \gt 2.5) \\
    0.2x + 0.5 & (-2.5 \le x \le 2.5) \\
    0 & (x \lt -2.5)
  \end{array}
\right.
```

逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    0.2 & (-2.5 \le x \le 2.5) \\
    0 & (\textrm{otherwise})
  \end{array}
\right.
```

のようになります。
[詳しく検証している記事](https://qiita.com/hsjoihs/items/88d1569aaef01659bbd5)があるので、もっと知りたい方はそちらへどうぞ！
1次関数の係数が$0.2$であることにこんな複雑な理論的理由があるなんて...
~~読んだけどさっぱりわかりませんでした~~


# logSigmoid関数
これもPytorchより、紹介だけです。[sigmoid関数](#シグモイド関数sigmoid)の対数を取ります。
**TODO 補足情報を調べる**
![log-sigmoid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/072bd12c-b7bf-5ed4-af1a-214a3837328c.png)
順伝播の数式は

```math
y = \log \left( \cfrac{1}{1 + e^{-x}} \right)
```

となり、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \cfrac{1}{1 + e^x}
```

となります。逆伝播の分母は$-x$乗ではないので注意してください。


# tanh関数
<font>[sigmoid関数](#シグモイド関数sigmoid)の微分の最大値が$0.25$であるという弱点を解決する関数の一つとして提案されたのが双曲線関数の一つであるtanh関数です。</font>
![tanh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/169ed9e9-7b6d-2bae-c3ca-42f9372479c4.png)
<font>図を見ての通り、微分の最大値が$1$となり、勾配消失の原因を取り除くことができました。
しかし、依然として極端に大きい・小さい入力での微分が$0$となってしまう問題は残っています。</font>

```math
y = \tanh x = \cfrac{e^x - e^{-x}}{e^x + e^{-x}}
```

逆伝播は

```math
\cfrac{\partial y}{\partial x} = \textrm{sech}^2 x = \cfrac{1}{\cosh^2 x} = \cfrac{4}{(e^x + e^{-x})^2}
```

となります。
最近[Mish関数](#mish関数)や[tanhExp関数](#tanhexp関数)などの期待の新星に一部使われてますね〜注目度が高い関数みたいです。


# tanhShrink関数
これもPytorchより。紹介だけです。
**TODO 補足情報を調べる**
![tanh-shrink.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/d1774d43-642c-23ba-a667-8125740a4767.png)
順伝播の数式は

```math
y = x - \tanh x
```

で、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \tanh^2 x
```

となります。


# hardtanh関数
これもPytorchです。紹介だけ...
**TODO 補足情報を調べる**
![hard-tanh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/47c88f81-5e58-8f1e-3325-3183152ae1fd.png)
順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    1 & (x \gt 1) \\
    -1 & (x \lt -1) \\
    x & (\textrm{otherwise})
  \end{array}
\right.
```

で、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    0 & (x \lt -1 \quad \textrm{or} \quad 1 \le x) \\
    1 & (\textrm{otherwise})
  \end{array}
\right.
```

となります。


# ReLU関数
ReLU関数(一般にランプ関数と呼ばれているそうです)は結構最近に提案されて覇権を握っている活性化関数です。その特徴はなんと言ってもそのシンプルで高速な演算ですね。
![ReLU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/830b7112-2936-91b2-9989-4f328192e79e.png)
順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    x & (x \gt 0) \\
    0 & (x \le 0)
  \end{array}
\right.
```

逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    1 & (x \gt 0) \\
    0 & (x \le 0)
  \end{array}
\right.
```

<font>となります。入力が正の値だと勾配は常に$1$となるので勾配消失が起こりにくく、層を重ねやすいというメリットがありますが、負の入力に対しては一切学習が進まないというデメリットもあります。
また、$x=0$で~~不連続~~**微分不可能**であることを基本的に無視しています。
誤差逆伝播法では連鎖律を用いた勾配の伝播を元に学習を進めるため、本来であれば活性化関数は全ての実数で微分可能であるべきですが、実際のところピッタリ$x=0$となる場合の方が少ない&どうせ$0$なので問題になっていません。</font>

# ReLU6関数
Pytorchより、紹介だけです。
**TODO 補足情報を調べる**
![ReLU6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/80db9449-a39d-1c92-193b-c5f199991469.png)
順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    0 & (x \le 0) \\
    6 & (x \ge 6) \\
    x & (\textrm{otherwise})
  \end{array}
\right.
```

となり、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    0 & (x \le 0 \quad \textrm{or} \quad 6 \le x) \\
    1 & (\textrm{otherwise})
  \end{array}
\right.
```

となります。


# leaky-ReLU関数
leaky-ReLU関数は[ReLU関数](#relu関数)の「負の入力に対しては学習が進まない」という欠点を補うべく、負の入力の時はごく小さな傾きの1次関数を出力するようにしたものです。
![leaky-ReLU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/8b96e34a-0c66-ec1c-6ac3-0976bef05c94.png)
グラフではほとんど分かりませんが、数式では

```math
y = \left\{
  \begin{array}{cc}
    x & (x \gt 0) \\
    0.01x & (x \le 0)
  \end{array}
\right.
```

のように、負の入力の時の出力が異なります。そのため、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    1 & (x \gt 0) \\
    0.01 & (x \le 0)
  \end{array}
\right.
```

となります。これもやっぱり$x=0$で~~不連続~~**微分不可能**です。
また、調べてる中で色々なところで見かけたのですが、どうやらこの関数の命名者が「特にこれを使う意味はなかった」と言っているようです。ちょっと意外ですね。少しは改善されそうなのに...


# ELU関数
[ReLU関数](#relu関数)とよく似た形をしたグラフで、$x=0$の時により滑らかな関数の一つにELU関数というものがあります。
![ELU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/e3c5264f-4d0f-628b-ee84-a084d3314a7a.png)
<div><font>グラフからわかるように、負の入力でも勾配が$0$にはなりません($x\to-\infty$で$0$)。数式では</font></div>

```math
y = \left\{
  \begin{array}{cc}
    x & (x \ge 0) \\
    \alpha (e^x - 1) & (x \lt 0)
  \end{array}
\right.
```

となり、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    1 & (x \ge 0) \\
    \alpha e^x & (x \lt 0)
  \end{array}
\right.
```

となります。
~~$\alpha$の値は次の[SELU関数](#selu関数)で理論的に適切な値を取ることが多いみたいです(多分)。~~
**2020/6/2修正**
<font>$\alpha$のデフォルト値は大抵$1$のようです。$\alpha=1$のグラフに差し替えさせていただきます。</font>
誤った情報を発信してしまい申し訳ありませんでした...


# SELU関数
[ELU関数](#elu関数)の出力を$\lambda$倍したのがSELU関数です。
![SeLU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/9609aba5-feda-7716-3f8b-233d4ccd81da.png)
数式では

```math
y = \left\{
  \begin{array}{cc}
    \lambda x & (x \ge 0) \\
    \lambda \alpha (e^x - 1) & (x \lt 0)
  \end{array}
\right.
```

であり、逆伝播も

```math
\cfrac{\partial y}{\partial x} = \left\{
  \begin{array}{cc}
    \lambda & (x \ge 0) \\
    \lambda \alpha e^x & (x \lt 0)
  \end{array}
\right.
```

のように$\lambda$倍されます。
理論的に最適なパラメータ値が求まるらしく、その値が

```math
\alpha = 1.67326\ldots, \quad \lambda = 1.0507\ldots
```

となるようです。そのうち論文読むかも...読んだら補足します。


# CELU関数
こちらもPytorchより、紹介のみです。
**TODO 補足情報を調べる**
![CeLU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/7b38ed09-a2c5-0c8e-a071-ef9ae275260c.png)
順伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    x & (x \ge 0) \\
    \alpha \left( e^{\frac{x}{\alpha}} - 1 \right) & (\textrm{otherwise})
  \end{array}
\right.
```

で、逆伝播の数式は

```math
y = \left\{
  \begin{array}{cc}
    1 & (x \ge 0) \\
    e^{\frac{x}{\alpha}} & (\textrm{otherwise})
  \end{array}
\right.
```

となります。


# ソフトマックス関数(softmax)
ソフトマックス関数は多値分類問題の出力層の活性化関数として用いられます。
その計算の特性上出力を確率として見なすことが可能です。
また、sigmoid関数の拡張版の関数となっています。
![softmax.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/96a3cf1b-1d83-bed0-1254-dc2f780f6023.png)
グラフの縦軸はあまり気にしないでOKです。重要なのは積分する(コンピュータは離散的なので総和を取る)と$1$になるということだけです。
数式的には

```math
y_i = \cfrac{e^{x_i}}{\displaystyle\sum_{k=1}^{n}{e^{x_k}}} \quad (i = 1, 2, \ldots, n)
```

という感じですね。逆伝播は一応

```math
\left( \cfrac{\partial y}{\partial x} \right)_i = \sum_{j=1}^{n}{\cfrac{\partial y_j}{\partial x_i}} = \sum_{j=1}^{n}{\left\{ \begin{align}
  e^{x_i} \cfrac{\displaystyle\sum_{k=1}^{n}{e^{x_k}} - e^{x_j}}{\left( \displaystyle\sum_{k=1}^{n}{e^{x_k}} \right)^2} &= y_i (1 - y_j) & (j = i) \\
  -\cfrac{e^{x_i} e^{x_j}}{\left( \displaystyle\sum_{k=1}^{n}{e^{x_k}} \right)^2} &= -y_i y_j & (j \ne i)
\end{align}\right\} } = y_i (1 - y_i) - \sum_{j=1, j \ne i}^{n}{y_i y_j} = \sum_{j=1}^{n}{y_i(\delta_{ij} - y_j)}
```

となります。$\delta_\{ij}$はDiracのデルタ関数です。
こうしてみるとsigmoid関数の拡張系であるということに納得しやすいですね。

```math
\delta_{ij} = \left\{ \begin{align}
  1 && (i = j) \\
  0 && (i \ne j)
\end{align} \right.
```

また、**交差エントロピー誤差**

```math
Error = t \log y
```

を取ることで、出力層から中間層への逆伝播が

```math
y - t
```

という非常にシンプルなものになります。
ちなみにこれは偶然ではなく、交差エントロピー誤差というものはsoftmax関数にフィッティングして、勾配が$y - t$となるようわざわざ設計された関数です。
~~いつか計算グラフで紹介するかもしれません。~~[ここ](https://qiita.com/kuroitu/items/a6725ac0139d8eeb1e19#%E4%BA%A4%E5%B7%AE%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AD%E3%83%94%E3%83%BC)で紹介しています。


# softmin関数
これもPytorchより。[softmax関数](#ソフトマックス関数softmax)の逆で、小さい値の確率が大きくなります。
**TODO 補足情報を調べる**
![softmin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/ac2dd105-1a0a-bb25-31a2-e78c4b079ee9.png)
順伝播の数式は

```math
y_i = \cfrac{e^{-x_i}}{\displaystyle\sum_{k=1}^{n}{e^{-x_k}}} \quad (i = 1, 2, \ldots, n)
```

で、逆伝播の数式は

```math
\left( \cfrac{\partial y}{\partial x} \right)_i = \sum_{j=1}^{n}{\cfrac{\partial y_j}{\partial x_i}} = \sum_{j=1}^{n}{\left\{ \begin{align}
  -e^{-x_i} \cfrac{\displaystyle\sum_{k=1}^{n}{e^{-x_k}} - e^{-x_j}}{\left( \displaystyle\sum_{k=1}^{n}{e^{-x_k}} \right)^2} &= - y_i (1 - y_j) & (j = i) \\
  -\cfrac{e^{-x_i} \times (-e^{-x_j})}{\left( \displaystyle\sum_{k=1}^{n}{e^{-x_k}} \right)^2} &= y_i y_j & (j \ne i)
\end{align}\right\} } = - y_i (1 - y_i) + \sum_{j=1, j \ne i}^{n}{y_i y_j} = - \sum_{j=1}^{n}{y_i(\delta_{ij} - y_j)}
```

となります。こちらも交差エントロピー誤差を用いれば綺麗に誤差が逆伝播するんでしょうか...今度調べてみます。


# logSoftmax関数
Pytorchより、こちらは[softmax関数](#ソフトマックス関数softmax)の対数をとったものです。
**TODO 補足情報を調べる**
![log-softmax.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/1810a4ac-81b4-f190-6a4d-6858ce803f1d.png)
ほぼ直線に見えますね。合ってるんでしょうか...一応コードは合ってると思ってます。
順伝播の数式は

```math
y_i = \log \left( \cfrac{e^{x_i}}{\displaystyle\sum_{k=1}^{n}{e^{x_k}}} \right)
```

で、逆伝播は

```math
\left( \cfrac{\partial y}{\partial x} \right)_i = \sum_{j=1}^{n}{\cfrac{\partial y_j}{\partial x_i}} = \sum_{j=1}^{n}{\left\{ \begin{align}
  \cfrac{e^{x_i}\cfrac{\displaystyle\sum_{k=1}^{n}{e^{x_k}} - e^{x_j}}{\left( \displaystyle\sum_{k=1}^{n}{e^{x_k}} \right)^2}}{\cfrac{e^{x_i}}{\displaystyle\sum_{k=1}^{n}{e^{x_k}}}} &= 1 - e^{y_j} & (j = i) \\
  \cfrac{\cfrac{-e^{x_i}e^{x_j}}{\left( \displaystyle\sum_{k=1}^{n}{e^{x_k}} \right)^2}}{\cfrac{e^{x_i}}{\displaystyle\sum_{k=1}^{n}{e^{x_k}}}} &= - e^{y_j} & (j \ne i)
\end{align}\right\} } = 1 - e^{y_i} - \sum_{j=1, j \ne i}^{n}{e^{y_j}} = \sum_{j=1}^{n}{(\delta_{ij} - e^{y_j})}
```

となります。


# softplus関数
softplus関数は[softmax関数](#ソフトマックス関数softmax)と似た名前ですが、本質は[ReLU関数](#relu関数)に似ています。
![softplus.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/792ca998-f9b8-7088-946f-2388b227677a.png)

数式では

```math
y = \log{(1 + e^x)} = \ln{(1 + e^x)}
```

と表され、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \cfrac{e^x}{1 + e^x} = \cfrac{1}{1 + e^{-x}}
```

のようになります。[ReLU関数](#relu関数)と微分も含めて見た目そっくりですね。

ちなみに、$\ln x$とは底がネイピア数の対数関数であることを明示するためのものです。つまり

```math
\ln x = \log_ex
```

ということですね。


# softsign関数
こちらも名前こそ[softmax関数](#ソフトマックス関数softmax)と似ていますが、実際は[tanh関数](#tanh関数)に似ています。
![softsign.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/3c5b7eed-e816-51c3-ad42-34366373d1d3.png)

順伝播の見た目はtanh関数そっくりですが、逆伝播が全く異なりますね。すごく尖ってます。
順伝播を数式で見ると

```math
y = \cfrac{x}{1 + |x|}
```

となり、逆伝播は

```math
\cfrac{\partial y}{\partial x} = \cfrac{1}{(1 + |x|)^2}
```

となっています。~~$x=0$で微分が不連続関数になってしまっていますね。~~
**2020/6/2修正**
関数の連続性を勘違いしていました。正しくは不連続ではありません。微分不可能です。

```math
\lim_{x \to \pm 0}{\cfrac{1}{(1 + |x|)^2}} = 1
\Leftrightarrow
\lim_{x \to 0}{\cfrac{1}{(1 + |x|)^2}} = 1 
```

であり、

```math
\cfrac{\partial y}{\partial x} = \cfrac{1}{(1 + |0|)^2} = 1 \quad (\because x = 0)
```

なので、

```math
\lim_{x \to 0}{\cfrac{1}{(1 + |x|)^2}} = \cfrac{1}{(1 + |0|)^2}
```

となり、連続であることが示されます。


# Swish関数
こちらは2017年登場の、[ReLU関数](#relu関数)の後継と期待されているSwish関数です。
![Swish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/de3d273a-5f5b-f8e1-58e2-f5d230eaf389.png)
<font>見た目も[ReLU関数](#relu関数)とそっくりですが、[ELU関数](#elu関数)や[SELU関数](#selu関数)とは異なり、$x=0$でも~~連続な~~**微分可能な**関数となっています。
さらに$C^{\infty}$級の関数であるのも特徴です。
さらにさらに、負の入力に対して負の値をわずかに取ることも分かります。最小値ありの最大値なしというのが嬉しい点(らしい)です。
順伝播を数式で表すと</font>

```math
y = x \sigma_{sigmoid}(\beta x) = \cfrac{x}{1 + e^{-\beta x}}
```

<font>という感じですね。上のグラフでは$\beta=1$と設定しています。
ちなみに$\beta$は誤差逆伝播法で最適化できるそうです(未実装)。</font>
逆伝播は

```math
\cfrac{\partial y}{\partial x} = \beta y + \sigma_{sigmoid}(\beta x)(1 - \beta y) = \beta y + \cfrac{1 - \beta y}{1 + e^{-\beta x}}
```

のように書けます。[sigmoid関数](#シグモイド関数sigmoid)の面影が垣間見える感じですね。


# hardSwish関数
こちらは@tsubota-kougaさんより情報をいただいたhardSwish関数です!
[論文](https://arxiv.org/pdf/1905.02244v5.pdf)はそのうち読みます...
![hard-swish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/52a129ab-668e-1c1c-bb1d-d7c3e12c0e02.png)
Swish関数はsigmoid関数が使用されているため、つまり指数計算があるため計算量が多くなっていましたが、こちらはReLU6関数を利用してsigmoid関数を$-3 \le x \le 3$の範囲で近似するようになっています。

```math
y = x\cfrac{\sigma_{ReLU6}(x+3)}{6}
  = \left\{ \begin{align}
    0 && (x \le -3) \\
    \cfrac{x(x+3)}{6} && (-3 \le x \le 3) \\
    x && (3 \le x)
    \end{align} \right.
```

ReLU6関数に投げる値が$+3$されているため、範囲が$-3 \le x \le 3$となっています。逆伝播は

```math
\cfrac{\partial y}{\partial x} = \left\{ \begin{align}
  0 && (x \le 3) \\
  \cfrac{2x + 3}{6} && (-3 \le x \le 3) \\
  1 && (3 \le x)
\end{align} \right.
```

で計算できます。
Swish関数と比べてみましょう。
![Swish_vs_hard-swish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/640cc3cb-4fee-a0eb-f455-a4b26325c483.png)
Swish関数をそれなりに近似できてる感はありますね。でももう少し近づけられそうな気がしないでもない...


# ACON関数
こちらはhardSwish関数の情報をくださった@tsubota-kougaさんより情報をいただいたACON関数です!
簡単に[論文](https://arxiv.org/pdf/2009.04759.pdf)をまとめていきます。~~それでも他の関数と比較すると圧倒的に長くなりますがご了承ください。~~
やっぱ長いので詳細は折り畳んでおきます。

<details><summary>論文詳細</summary>

ACON: ACtivate Or Notそのものは関数というよりアルゴリズムの方が近いかもしれません。というのも、ACONはその名の由来通り**ニューロンを活性化するかしないかを明示的に学習する**ことを意図して導入された考え方だからです。論文の大筋としては<div>

1. Swish関数がReLUの自然な拡張であるとする
2. ReLUファミリーをReLU-Swish拡張と同じように拡張し、ACONファミリーとして提案する
3. 明示的にニューロンの活性/非活性スイッチを学習するmeta-ACON関数を提案する
</div>
といった感じです。それでは論文で提案されている関数について見ていきましょう。<div><br></div>
まずは`max`関数の連続化を紹介します。<div>

```math
S_{\max}(x_1, \ldots, x_n) = \cfrac{\displaystyle\sum_{i=1}^{n}{x_i e^{\beta x_i}}}{\displaystyle\sum_{i=1}^{n}{e^{\beta x_i}}}
```
</div>これを用いると<div>

```math
\begin{align}
  S_{\max}(\eta_l(x), \eta_r(x)) &= \eta_l(x) \cfrac{e^{\beta \eta_l(x)}}{e^{\beta \eta_l(x)} + e^{\beta \eta_r(x)}} + \eta_r(x) \cfrac{e^{\beta \eta_r(x)}}{e^{\beta \eta_l(x)} + e^{\beta \eta_r(x)}} = \eta_l(x) \cfrac{1}{1 + e^{- \beta (\eta_l(x)-\eta_r(x))}} + \eta_r(x) \cfrac{1}{1+e^{\beta (\eta_l(x)-\eta_r(x))}} \\
  &= \eta_l(x) \sigma(\beta(\eta_l(x) - \eta_r(x))) + \eta_r(x) \left\{1 - \cfrac{1}{1+e^{-\beta (\eta_l(x)-\eta_r(x))}}\right\} \\
  &= (\eta_l(x) - \eta_r(x))\sigma(\beta (\eta_l(x) - \eta_r(x))) + \eta_r(x)
\end{align}
```
</div>とすることができます(ただし$\sigma(•)$はsigmoid関数)。これがACON関数です。
論文では$\eta_l(x), \eta_r(x)$に各種関数を入れています。

![ACON_fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/75d87628-acaf-fc51-d85d-f2c71bec8d5d.png)
![ACON_fig3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/f4d2caad-718f-e9ef-6b8b-a2ae597488f8.png)
<div>

### ACON-A関数
</div>こちらは$\eta_l(x)=x, \eta_r(x)=0$としたACON関数で、Swish関数はこれに属します。<div>

```math
\sigma_{\textrm{ACON-A}}(x) = x \sigma(\beta x)
```

### ACON-B関数
</div>こちらはLeaky-ReLU関数やPReLU関数の一般形にあたります。$p \lt 1$において$\eta_l(x) = x, \eta_r(x)=px$とした<div>

```math
\sigma_{\textrm{ACON-B}}(x) = (1-p)x \sigma(\beta (1-p)x) + px
```
</div>
で表されます。$p$は学習可能なハイパーパラメータで、デフォルトは$0.25$です。
<div>

### ACON-C関数
</div>こちらはACON-B関数をさらに一般化したもので、$p_1 \ne p_2$なる学習可能ハイパーパラメータ$p_1, p_2$を用いて$\eta_l(x) = p_1 x, \eta_r(x) = p_2 x$とした<div>

```math
\sigma_{\textrm{ACON-C}}(x) = (p_1-p_2)x \sigma(\beta (p_1-p_2)x) + p_2 x
```
</div>で表されます。基本的にこれをベースに議論を進めればいいので、以降の話や実装もそうするつもりです。<div><hr></div>もちろん$\eta_l(x), \eta_r(x)$により複雑な関数を用いることでさらに複雑なReLUファミリーをACONファミリーに変換することが可能です。<div><br></div>
他にも、ハイパーパラメータ$p_1, p_2$が学習できることから、**1階微分の最大値/最小値が可変**となっているなどが論文に記述されています。<div>

```math
\cfrac{\partial \sigma_{\textrm{ACON-C}}(x)}{\partial x} = \cfrac{(p_1 - p_2)\left( 1 + e^{-\beta (p_1 - p_2)x} \right) + \beta (p_1-p_2)^2 e^{-\beta (p_1 - p_2)x}x}{\left(1 + e^{-\beta (p_1 - p_2)x} \right)^2} + p2 \\
\lim_{x \to \infty}{\cfrac{\partial \sigma_{\textrm{ACON-C}}(x)}{\partial x}} = p_1, \quad \lim_{x \to -\infty}{\cfrac{\partial \sigma_{\textrm{ACON-C}}(x)}{\partial x}} = p_2 \qquad (\beta \gt 0)
```

### meta-ACON
</div>ハイパーパラメータである$\beta$はスイッチの役割をしており、これを適応的に学習することで**ニューロンごとに活性/非活性(つまり非線形/線形)を切り替える**ことができます。これにより**汎化性能と転送性能(ちょっとわかりません...学習効率のことでしょうか)の向上**が見込めます。
論文ではデザインスペース(拡張可能性とかでいいでしょう)として<div>

```math
\beta = G(x)
```
</div>を提案しています。また、$G(x)$の例として<div>

```math
\begin{align}
  \beta &= G(x) = \sigma \left( \sum_{c=1}^{C}{\sum_{h=1}^{H}{\sum_{w=1}^{W}{x_{c, h, w}}}} \right) = \sigma \left( \sum_{c=1}^{C}{HW\textrm{GAP}(x_c)} \right) && (\textrm{レイヤで共通のスイッチ}) \\
  \beta_c &= G(x_c) = \sigma \left( W_1 W_2 \sum_{h=1}^{H}{\sum_{w=1}^{W}{x_{c, h, w}}} \right) = \sigma \left( fc[fc[HW\textrm{GAP}(x_c)]] \right) && (\textrm{チャンネルで共通のスイッチ}, W_1 \in \mathbb{R}^{C \times C/r}, W_2 \in \mathbb{R}^{C/r \times C}, fc[•]\textrm{は全結合層}) \\
  \beta_{c, h, w} &= G(x_{c, h, w}) = \sigma (x_{c, h, w}) && (ニューロン固有のスイッチ)
\end{align}
```
</div>
~~**※数式中の$\sigma$がsigmoid関数なのか標準偏差なのかが不明です。理由は論文のこの部分にだけ括弧がないためです。おそらくsigmoid関数でいいはずですが、念のため記しておきます。**~~
2020/10/21修正:実験結果の方を見るとsigmoid関数として記述されていました。<div><br></div>
が挙げられています。なお、$\textrm{GAP}(•)$関数はGlobal Average Poolingを行う関数です。詳細は[参考サイト](https://qiita.com/mine820/items/1e49bca6d215ce88594a)をご覧下さい。
まあ要するにチャンネルごとの入力を平均するだけですね。数式的に入力サイズを乗算してありますが、たぶん無くても大差ない...はず。
2020/10/21追記:実験結果の方に載っているものには乗算されていません。<div><hr></div>
実験については省略します。
</details>

必要なところだけ纏めると、順伝播は学習可能なハイパーパラメータ$\beta, p_1, p_2$を用いて

```math
\sigma_{\textrm{ACON-C}}(x) = (p_1-p_2)x \sigma(\beta (p_1-p_2)x) + p_2 x
```

と表され、逆伝播は

```math
\cfrac{\partial \sigma_{\textrm{ACON-C}}(x)}{\partial x} = \cfrac{(p_1 - p_2)\left( 1 + e^{-\beta (p_1 - p_2)x} \right) + \beta (p_1-p_2)^2 e^{-\beta (p_1 - p_2)x}x}{\left(1 + e^{-\beta (p_1 - p_2)x} \right)^2} + p2
```
と表されます。ちなみに$\beta$は

```math
\begin{align}
  \beta &= G(x) = \sigma \left( \sum_{c=1}^{C}{\sum_{h=1}^{H}{\sum_{w=1}^{W}{x_{c, h, w}}}} \right) = \sigma \left( \sum_{c=1}^{C}{HW\textrm{GAP}(x_c)} \right) && (\textrm{レイヤで共通のスイッチ}) \\
  \beta_c &= G(x_c) = \sigma \left( W_1 W_2 \sum_{h=1}^{H}{\sum_{w=1}^{W}{x_{c, h, w}}} \right) = \sigma \left( fc[fc[HW\textrm{GAP}(x_c)]] \right) && (\textrm{チャンネルで共通のスイッチ}, W_1 \in \mathbb{R}^{C \times C/r}, W_2 \in \mathbb{R}^{C/r \times C}, fc[•]\textrm{は全結合層}) \\
  \beta_{c, h, w} &= G(x_{c, h, w}) = \sigma (x_{c, h, w}) && (ニューロン固有のスイッチ)
\end{align}
```

が論文では提案されていますが、実際はデザイナブルです。
また、$\beta, p_1, p_2$への誤差の逆伝播は計算グラフを用いると次のように計算されることがわかります。
![ACON_backward_cal_graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/aaa7c4a6-ff4f-58f1-3334-649b31bc7c86.png)
![ACON_beta_backward.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/6bfe8fc0-b49e-df22-980b-bae91fdbd482.png)

```math
\begin{align}
  \cfrac{\partial y}{\partial \beta} &= E \sigma \left( \beta (p_1-p_2)x \right) \left\{ 1 - \sigma \left( \beta (p_1-p_2)x \right) \right\} (p_1-p_2)x \\
  \cfrac{\partial y}{\partial p_1} &= E \sigma \left( \beta (p_1-p_2)x \right) \left\{ 2 - \sigma \left( \beta (p_1-p_2)x \right) \right\} \beta x \\
  \cfrac{\partial y}{\partial p_2} &= Ex - E \sigma \left( \beta (p_1-p_2)x \right) \left\{ 2 - \sigma \left( \beta (p_1-p_2)x \right) \right\} \beta x
\end{align}
```

~~さて、実装はちょっと時間ないので今度やります...しばしお待ちください。~~
2020/10/21追記:実装してみました。ただしハイパーパラメータの学習は動作未確認のため多分動きません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/1bf86f83-aa92-831b-da71-1ef510786dbe.png" width=33%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/c9e7b502-6c67-a1ec-cd3f-a0ead57f4d0d.png" width=33%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/20d08dfb-f048-5f71-ec4c-beb8e80812a8.png" width=33%>
![ACON-A_beta_0.0_1.5_0.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/4c895769-d66d-31bb-ccd1-03f1eef30dd8.png)
![ACON-B_p2_0.0_0.75_0.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/9bed329c-3699-31cf-7c30-4104464a768a.png)
![ACON-C_p1_0.0_0.75_0.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/1ee9aaee-faee-60f8-f70c-29903160765d.png)
どのグラフも指定していないパラメータはデフォルト値を利用しています。今回の実装におけるデフォルト値は

```math
\beta = 1.0, \quad p_1 = 1.0, \quad p_2 = 0.25
```

としています。


# Mish関数
Mish関数は[Swish関数](#swish関数)よりもさらに最近の2019年に提案された[ReLU関数](#relu関数)の後継です。多くの場合でSwish関数よりも良い性能を発揮することが論文で示されています。(まだ論文をきちんと読んでませんがそう書いているそうです)
![Mish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/ba47ce55-6679-ba7e-8ce5-0755b579d711.png)
Swish関数と見た目ほぼ同じですが、ごくわずかに異なっています。
![swish_vs_mish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/6b31d54c-d803-f3aa-f3ba-79f6426a0027.png)
<font>一番右のグラフが最も違いが表れていますね。このグラフはそれぞれの2回微分を計算したものです。
つまり、勾配の変化の度合いを表しているものです。
グラフから読み取れることとしては、特に$x=0$付近でMish関数の方がダイナミックに変化している$\Rightarrow$勾配計算においてより顕著に誤差が伝わる、ということです。</font>
順伝播の数式としては

```math
y = x \tanh{(\varsigma(x))} = x \tanh{(\ln{(1 + e^x)})}
```

となっており、逆伝播は少々複雑で

```math
\cfrac{\partial y}{\partial x} = \cfrac{e^x \omega}{\delta^2}\\
\omega = 4(x + 1) + 4e^{2x} + e^{3x} + (4x + 6)e^x \\
\delta = 2e^x + e^{2x} + 2
```

のように計算されます。指数計算がたくさん登場するため計算負荷が高く、学習にReLU関数などより(ごく僅かですが)時間がかかってしまいます。
しかし精度の面でReLU関数を用いるよりも良い場合が多いため、学習時間と精度のトレードオフを加味して活性化関数を選択しましょう。


# tanhExp関数
こちらは@reppy4620さんより情報をいただいたtanhExp関数です！
[論文](https://arxiv.org/pdf/2003.09855.pdf)によると2020年3月のものですね〜凄まじく最近です。
![tanhExp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/73db18a2-c8fb-874b-5487-338a7b74bbdf.png)
論文にある通り、[ReLU関数](#relu関数)の仲間ですね(ReLU familyと呼ぶみたい？)。
どうやらMNISTやCIFER-10、CIFER-100などの有名データセットにおいて[Mish関数](#mish関数)をも超える性能を発揮してるっぽいです(まだちゃんと読んでません)。
[Swish関数](#swish関数)と比較してみると
![Swish_vs_tanhExp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/6faf3809-0f0d-1dd2-4150-6f433f3b28ba.png)
<font>順伝播の出力はほぼ同じに見えますが、tanhExp関数の方が逆伝播においてより急勾配かつ微分値$1$を超える範囲が小さくなっていますね。勾配というのはすごくデリケートで、微分の絶対値が$1$より小さすぎるとすぐ勾配消失しますし、逆に$1$より大きすぎると勾配爆発という現象が起こります。その点でもtanhExp関数は優秀に見えますね。</font>
続いて[Mish関数](#mish関数)とも比較します。
![Mish_vs_tanhExp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/4801b52f-52f1-2b09-c85c-fa834b27aff2.png)
Mish関数はSwish関数よりかはtanhExp関数に追従してますね。<font>しかしそれでもtanhExp関数の方が$0$付近での急勾配っぷりが優秀です。</font>
では順伝播を数式で見てみましょう。

```math
y = x \tanh(e^x)
```

Mish関数と同じくtanh関数を用いてますね。ここにきてtanh関数の注目度が高まっているんですかね〜
逆伝播は

```math
\begin{align}
  \cfrac{\partial y}{\partial x} &= \tanh(e^x) + xe^x\textrm{sech}^2(e^x) \\
  &= \tanh(e^x) - xe^x(\tanh^2(e^x) - 1)
\end{align}
```

となります。Mish関数よりもずっとシンプルに計算できるのが良いですね〜


# コード例
ここにはグラフを書く時に使用したコード例を載せておきます。実装する際の参考にでもしてください。
jupyter notebookを用いています。

<details>
<summary>activators.py</summary>
<div>

```activators.py
import numpy as np


class Activator():
    def __init__(self, *args, **kwds):
        pass

    def forward(self, *args, **kwds):
        raise NotImplemented
        #raise Exception("Not Implemented")


    def backward(self, *args, **kwds):
        raise NotImplemented
        #raise Exception("Not Implemented")


    def update(self, *args, **kwds):
        raise NotImplemented
        #pass


class step(Activator):
    def forward(self, x, *args, **kwds):
        return np.where(x > 0, 1, 0)


    def backward(self, x, *args, **kwds):
        return np.zeros_like(x)


class identity(Activator):
    def forward(self, x, *args, **kwds):
        return x


    def backward(self, x, *args, **kwds):
        return np.ones_like(x)


class bentIdentity(Activator):
    def forward(self, x, *args, **kwds):
        return 0.5*(np.sqrt(x**2 + 1) - 1) + x


    def backward(self, x, *args, **kwds):
        return 0.5*x/np.sqrt(x**2 + 1) + 1


class hardShrink(Activator):
    def __init__(self, lambda_=0.5, *args, **kwds):
        self.lambda_ = lambda_
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return np.where((-self.lambda_ <= x) & (x <= self.lambda_), 0, x)


    def backward(self, x, *args, **kwds):
        return np.where((-self.lambda_ <= x) & (x <= self.lambda_), 0, 1)


class softShrink(Activator):
    def __init__(self, lambda_=0.5, *args, **kwds):
        self.lambda_ = lambda_
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return np.where(x < -self.lambda_, x + self.lambda_,
                        np.where(x > self.lambda_, x - self.lambda_, 0))


    def backward(self, x, *args, **kwds):
        return np.where((-self.lambda_ <= x) & (x <= self.lambda_), 0, 1)


class threshold(Activator):
    def __init__(self, threshold, value, *args, **kwds):
        self.threshold = threshold
        self.value = value
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return np.where(x > self.threshold, x, self.value)


    def backward(self, x, *args, **kwds):
        return np.where(x > self.threshold, 1, 0)


class sigmoid(Activator):
    def forward(self, x, *args, **kwds):
        return 1/(1 + np.exp(-x))


    def backward(self, x, y, *args, **kwds):
        return y*(1 - y)


class hardSigmoid(Activator):
    def forward(self, x, *args, **kwds):
        return np.clip(0.2*x + 0.5, 0, 1)


    def backward(self, x, *args, **kwds):
        return np.where((x > 2.5) | (x < -2.5), 0, 0.2)


class logSigmoid(Activator):
    def forward(self, x, *args, **kwds):
        return -np.log(1 + np.exp(-x))


    def backward(self, x, *args, **kwds):
        return 1/(1 + np.exp(x))


class act_tanh(Activator):
    def forward(self, x, *args, **kwds):
        return np.tanh(x)


    def backward(self, x, *args, **kwds):
        return 1 - np.tanh(x)**2


class hardtanh(Activator):
    def forward(self, x, *args, **kwds):
        return np.clip(x, -1, 1)


    def backward(self, x, *args, **kwds):
        return np.where((-1 <= x) & (x <= 1), 1, 0)


class tanhShrink(Activator):
    def forward(self, x, *args, **kwds):
        return x - np.tanh(x)


    def backward(self, x, *args, **kwds):
        return np.tanh(x)**2


class ReLU(Activator):
    def forward(self, x, *args, **kwds):
        return np.maximum(0, x)


    def backward(self, x, *args, **kwds):
        return np.where(x > 0, 1, 0)


class ReLU6(Activator):
    def forward(self, x, *args, **kwds):
        return np.clip(x, 0, 6)


    def backward(self, x, *args, **kwds):
        return np.where((0 < x) & (x < 6), 1, 0)


class leakyReLU(Activator):
    def __init__(self, alpha=1e-2, *args, **kwds):
        self.alpha = alpha
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return np.maximum(self.alpha * x, x)


    def backward(self, x, *args, **kwds):
        return np.where(x < 0, self.alpha, 1)


class ELU(Activator):
    def __init__(self, alpha=1., *args, **kwds):
        self.alpha = alpha
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return np.where(x >= 0, x, self.alpha*(np.exp(x) - 1))


    def backward(self, x, *args, **kwds):
        return np.where(x >= 0, 1, self.alpha*np.exp(x))


class SELU(Activator):
    def __init__(self, lambda_=1.0507, alpha=1.67326, *args, **kwds):
        self.lambda_ = lambda_
        self.alpha = alpha
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return np.where(x >= 0, self.lambda_*x,
                        self.lambda_*self.alpha*(np.exp(x)-1))


    def backward(self, x, *args, **kwds):
        return np.where(x >= 0, self.lambda_, self.lambda_*self.alpha*np.exp(x))


class CELU(Activator):
    def __init__(self, alpha=1., *args, **kwds):
        self.alpha = alpha
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return np.where(x >= 0, x, self.alpha*(np.exp(x/self.alpha)-1))


    def backward(self, x, *args, **kwds):
        return np.where(x >= 0, 1, np.exp(x/self.alpha))


class softmax(Activator):
    def forward(self, x, *args, **kwds):
        exp_x = np.exp(x-np.max(x, axis=1, keepdims=True))
        return exp_x/np.sum(exp_x, axis=1, keepdims=True)


    def backward(self, x, y, *args, **kwds):
        return y*(1 - y)


class softmin(Activator):
    def forward(self, x, *args, **kwds):
        exp_mx = np.exp(-x)
        return exp_mx/np.sum(exp_mx, axis=1, keepdims=True)


    def backward(self, x, y, *args, **kwds):
        return -y*(1 - y)


class logSoftmax(Activator):
    def forward(self, x, *args, **kwds):
        exp_x = np.exp(x)
        return np.log(exp_x/np.sum(exp_x, axis=1, keepdims=True))


    def backward(self, x, y, *args, **kwds):
        return 1 - np.exp(y)


class softplus(Activator):
    def forward(self, x, *args, **kwds):
        return np.logaddexp(x, 0)


    def backward(self, x, *args, **kwds):
        return 1/(1 + np.exp(-x))


class softsign(Activator):
    def forward(self, x, *args, **kwds):
        return x/(1 + np.abs(x))


    def backward(self, x, *args, **kwds):
        return 1/(1 + np.abs(x))**2


class Swish(Activator):
    def __init__(self, beta=1, *args, **kwds):
        self.beta = beta
        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        return x/(1 + np.exp(-self.beta*x))


    def backward(self, x, y, *args, **kwds):
        return self.beta*y + (1 - self.beta*y)/(1 + np.exp(-self.beta*x))


    def d2y(self, x, *args, **kwds):
        return (-0.25*self.beta*(self.beta*x*np.tanh(0.5*self.beta*x) - 2)
                               *(1 - np.tanh(0.5*self.beta*x)**2))


class hardSwish(Activator):
    def forward(self, x, *args, **kwds):
        return x*np.clip(x+3, 0, 6)/6
    
    
    def backward(self, x, *args,**kwds):
        return np.where((-3 < x) & (x < 3), (2*x + 3)/6, np.clip(x, 0, 1))


class ACON(Activator):
    def __init__(self, acon_type, *args, p1=1, p2=0.25, beta=1,
                 opt="adam", **kwds):
        if acon_type == "acona":
            self.p1 = p1
            self.p2 = 0
            self.acon_type = "acona"
        elif acon_type == "aconb":
            self.p1 = p1
            self.p2 = p2
            self.acon_type = "aconb"
        elif acon_type == "aconc":
            self.p1 = p1
            self.p2 = p2
            self.acon_type = "aconc"
        else:
            raise KeyError("Unknown ACON type: <{}>.".format(acon_type))
        if acon_type in ["aconb", "aconc"]:
            #self.opt = get_opt(opt, *args, **kwds)
            pass
        
        # ベータの設定
        switch_args = []
        self.beta = None
        class BaseSwitch():
            def forward(self, x):
                pass
            def backward(self, grad):
                pass
            def update(self, *args, **kwds):
                pass
        if isinstance(beta, str):
            if "pixel" in beta:
                class Beta(BaseSwitch):
                    def forward(self, x):
                        self.value = 1/(1+np.exp(-x))
            elif "channel" in beta:
                if "n_channel" in kwds:
                    C = kwds["n_channel"]
                else:
                    raise KeyError("I must need 'n_channel' value to use channel-wise switch.")
                if "r" in kwds:
                    r = kwds["r"]
                else:
                    r = 16
                class Beta(BaseSwitch):
                    def __init__(self, C, r, opt, **kwds):
                        self.C = C
                        self.r = r
                        self.w1 = np.random.randn((C, C//r))
                        self.w2 = np.random.randn((C//r, C))
                        self.opt = get_opt(opt, **kwds)
                    def forward(self, x):
                        self.sumx = np.sum(x, axis=(2, 3))
                        self.first = self.sumx@self.w1
                        self.value = 1/(1+np.exp(-self.first@self.w2))
                    def backward(self, grad):
                        self.grad_w2 = self.first.T@(grad * self.value*(1-self.value))
                        self.grad_w1 = self.sumx.T@((grad * self.value*(1-self.value))@self.w2.T)
                    def update(self, *args, **kwds):
                        dw1, dw2 = self.opt.update(self.grad_w1, self.grad_w2, *args, **kwds)
                        self.w1 -= dw1
                        self.w2 -= dw2
                switch_args.append(C)
                switch_args.append(r)
                switch_args.append(opt)
                switch_args.append(kwds)
            elif "layer" in kwds:
                class Beta(BaseSwitch):
                    def forward(self, x):
                        self.value = 1/(1+np.exp(-np.sum(x, axis=(1, 2, 3))))
            else:
                raise KeyError("Unknown switch type: <{}>.".format(beta))
        else:
            if isinstance(beta, int) or isinstance(beta, float):
                class Beta(BaseSwitch):
                    def __init__(self, value):
                        self.value = value
                switch_args.append(beta)
            else:
                if not (hasattr(beta, "forward") and hasattr(beta, "backward") and hasattr(beta, "update")):
                    raise KeyError("Using designed function, the type of switch 'beta' must be a <class>"
                                + " and have method 'forward' and 'backward' and 'update'.")
                self.beta = beta
        if self.beta is None:
            self.beta = Beta(*switch_args)
    
    
    def forward(self, x, *args, **kwds):
        self.beta.forward(x)
        self.sigmoid_value = 1/(1+np.exp(-self.beta.value*(self.p1-self.p2)*x))
        return (self.p1-self.p2)*x*self.sigmoid_value + self.p2*x
    
    
    def backward(self, x, grad, *args, **kwds):
        if self.acon_type in ["aconb", "aconc"]:
            self.grad_p1 = np.sum(grad*self.sigmoid_value*(2-self.sigmoid_value)*self.beta.value*x)
            self.grad_p2 = np.sum(grad*x - grad*self.sigmoid_value*(2-self.sigmoid_value)*self.beta.value*x)
        self.beta.backward(grad)
        return (((self.p1-self.p2)*(1+np.exp(-self.beta.value*(self.p1-self.p2)*x))
               + self.beta.value*(self.p1-self.p2)**2*np.exp(-self.beta.value*(self.p1-self.p2)*x)*x)
                 /(1+np.exp(-self.beta.value*(self.p1-self.p2)*x))**2
               + self.p2)
    
    
    def update(self, *args, **kwds):
        if self.acon_type in ["aconb", "aconc"]:
            dp1, dp2 = self.opt.update(self.grad_p1, self.grad_p2, *args, **kwds)
            if self.acon_type in ["aconc"]:
                self.p1 -= dp1
            self.p2 -= dp2
        self.beta.update(*args, **kwds)


class Mish(Activator):
    def forward(self, x, *args, **kwds):
        return x*np.tanh(np.logaddexp(x, 0))


    def backward(self, x, *args, **kwds):
        omega = (4*(x + 1) + 4*np.exp(2*x)
              + np.exp(3*x) + (4*x + 6)*np.exp(x))
        delta = 2*np.exp(x) + np.exp(2*x) + 2
        return np.exp(x)*omega/delta**2


    def d2y(self, x, *args, **kwds):
        omega = (2*(x + 2)
                 + np.exp(x)*(np.exp(x)*(-2*np.exp(x)*(x - 1) - 3*x + 6)
                              + 2*(x + 4)))
        delta = np.exp(x)*(np.exp(x) + 2) + 2
        return 4*np.exp(x)*omega/delta**3


class tanhExp(Activator):
    def forward(self, x, *args, **kwds):
        return x*np.tanh(np.exp(x))


    def backward(self, x, *args, **kwds):
        tanh_exp_x = np.tanh(np.exp(x))
        return tanh_exp_x - x*np.exp(x)*(tanh_exp_x**2 - 1)


    def d2y(self, x, *args, **kwds):
        tanh_exp = np.tanh(np.exp(x))
        return (np.exp(x)*(-x + 2*np.exp(x)*x*tanh_exp - 2)
                         *(tanh_exp**2 - 1))


class maxout(Activator):
    def __init__(self, n_prev, n, k, wb_width=5e-2, *args, **kwds):
        self.n_prev = n_prev
        self.n = n
        self.k = k
        self.w = wb_width*np.random.rand((n_prev, n*k))
        self.b = wb_width*np.random.rand(n*k)

        super().__init__(*args, **kwds)


    def forward(self, x, *args, **kwds):
        self.x = x.copy()
        self.z = np.dot(self.w.T, x) + self.b
        self.z = self.z.reshape(self.n, self.k)
        self.y = np.max(self.z, axis=1)
        return self.y

    def backward(self, g, *args, **kwds):
        self.dw = np.sum(np.dot(self.w, self.x))
```
</div></details>

<details>
<summary>test_activators.py</summary>
<div>

```test_activators.py
import string


import numpy as np
import matplotlib.pyplot as plt


_act_dic = {"step": step,
            "identity": identity,
            "bentidentity": bentIdentity,
            "hardshrink": hardShrink,
            "softshrink": softShrink, 
            "threshold": threshold,
            "sigmoid": sigmoid,
            "hardsigmoid": hardSigmoid,
            "logsigmoid": logSigmoid,
            "tanh": act_tanh,
            "tanhshrink": tanhShrink,
            "hardtanh":hardtanh,
            "relu": ReLU,
            "relu6": ReLU6,
            "leakyrelu": leakyReLU, "lrelu": leakyReLU,
            "elu": ELU,
            "selu": SELU,
            "celu": CELU,
            "softmax": softmax,
            "softmin": softmin,
            "logsoftmax": logSoftmax,
            "softplus": softplus,
            "softsign": softsign,
            "swish": Swish,
            "hardswish":  hardSwish,
            "acona": ACON, "aconb": ACON, "aconc": ACON,
            "mish": Mish,
            "tanhexp": tanhExp,
           }


def get_act(name, *args, **kwds):
    name = name.lower().translate(str.maketrans('', '',string.punctuation))
    if name in _act_dic.keys():
        if "acon" in name:
            activator = _act_dic[name](name, *args, **kwds)
        else:
            activator = _act_dic[name](*args, **kwds)
    else:
        raise ValueError(name + ": Unknown activator")

    return activator


def plot_graph(x, name, *args, **kwds):
    activator = get_act(name, *args, **kwds)
    
    y = activator.forward(x, *args, **kwds)
    dx = activator.backward(x, y, *args, **kwds)
    
    fig, ax = plt.subplots(1)
    ax.plot(x[0], y[0], label="forward")
    ax.plot(x[0], dx[0], label="backward")
    fig.suptitle(name)
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.grid()
    ax.legend(loc="best")
    fig.savefig("{}.png".format(name))
    #fig.show()
    #plt.close(fig)


def vs_plot(x, A, B):
    A_activator = get_act(A)
    B_activator = get_act(B)
    
    y_A = {}
    y_B = {}
    
    y_A["{} y".format(A)] = A_activator.forward(x)
    y_B["{} y".format(B)] = B_activator.forward(x)
    y_A["{} dy".format(A)] = A_activator.backward(x, 
                                                  y_A["{} y".format(A)])
    y_B["{} dy".format(B)] = B_activator.backward(x,
                                                  y_B["{} y".format(B)])
    y_A["{} d2y".format(A)] = A_activator.d2y(x, y_A["{} y".format(A)])
    y_B["{} d2y".format(B)] = B_activator.d2y(x, y_B["{} y".format(B)])
    
    fig, ax = plt.subplots(1, 3, figsize=(18, 6))
    for i, key in enumerate(y_A):
        ax[i].plot(x[0], y_A[key][0], label=key)
        ax[i].set_xlabel("x")
        ax[i].set_ylabel("y")
        ax[i].grid()
    for i, key in enumerate(y_B):
        ax[i].plot(x[0], y_B[key][0], label=key)
        ax[i].legend(loc="best")
    ax[0].set_title("forward")
    ax[1].set_title("backward")
    ax[2].set_title("second-order derivative")
    fig.tight_layout()
    fig.savefig("{}_vs_{}.png".format(A, B))
    plt.show()


def param_plot(x, name, param_dic, *args, **kwds):
    for param in param_dic:
        fig, ax = plt.subplots(1, 2, figsize=(12, 6))
        for i in param_dic[param]:
            kwds[param] = i
            activator = get_act(name, *args, **kwds)
            y = activator.forward(x, *args, **kwds)
            dx = activator.backward(x, y, *args, **kwds)

            ax[0].plot(x[0], y[0], label="{}={}".format(param, np.round(i, 2)))
            ax[1].plot(x[0], dx[0], label="{}={}".format(param, np.round(i, 2)))
        fig.suptitle(name+" {}={}~{} step by {}".format(param,
                                                        np.round(param_dic[param][0], 2),
                                                        np.round(param_dic[param][-1], 2),
                                                        np.round(param_dic[param][1]-param_dic[param][0], 2)))
        ax[0].set_title("forward")
        ax[1].set_title("backward")
        ax[0].set_xlabel("x")
        ax[0].set_ylabel("y")
        ax[1].set_xlabel("x")
        ax[1].set_ylabel("y")
        ax[0].grid()
        ax[1].grid()
        ax[0].legend(loc="best")
        ax[1].legend(loc="best")
        fig.savefig("{}.png".format("_".join([name, param,
                                              str(np.round(param_dic[param][0], 2)),
                                              str(np.round(param_dic[param][-1], 2)),
                                              str(np.round(param_dic[param][1]-param_dic[param][0], 2))])))


x = np.arange(-5, 5, 5e-2).reshape(1, -1)

plot_graph(x, "step")
plot_graph(x, "identity")
plot_graph(x, "bent-identity")
plot_graph(x, "hard-shrink")
plot_graph(x, "soft-shrink")
plot_graph(x, "threshold", -1, -2)
plot_graph(x, "sigmoid")
plot_graph(x, "hard-sigmoid")
plot_graph(x, "log-sigmoid")
plot_graph(x, "tanh")
plot_graph(x, "tanh-shrink")
plot_graph(x, "hard-tanh")
plot_graph(x, "ReLU")
plot_graph(x + 2, "ReLU6")
plot_graph(x, "leaky-ReLU")
plot_graph(x, "ELU")
plot_graph(x, "SELU")
plot_graph(x, "CELU")
plot_graph(x, "softmax")
plot_graph(x, "softmin")
plot_graph(x, "log-softmax")
plot_graph(x, "softplus")
plot_graph(x, "softsign")
plot_graph(x, "Swish")
plot_graph(x, "hard-swish")
plot_graph(x, "ACON-A")
plot_graph(x, "ACON-B")
plot_graph(x, "ACON-C")
plot_graph(x, "Mish")
plot_graph(x, "tanhExp")

vs_plot(x, "Swish", "hard-swish")
vs_plot(x, "Swish", "Mish")
vs_plot(x, "Swish", "tanhExp")
vs_plot(x, "Mish", "tanhExp")

param_plot(x, "ACON-A", {"beta": np.arange(0, 2, 0.5)})
param_plot(x, "ACON-B", {"p2": np.arange(0, 1, 0.25)}, beta=2)
param_plot(x, "ACON-C", {"p1": np.arange(0, 1, 0.25)}, beta=2)
```
</div></details>

他にも実装中のものもいくつかあります。そのうち追加すると思います...


# 参考
 - [【人工知能】活性化関数の種類と違い。メリットデメリット。](https://fresopiya.com/2019/05/14/activefunc/)
 - [Bent Identity Activation Function](https://www.gabormelli.com/RKB/Bent_Identity_Activation_Function)
 - [Kerasのhard_sigmoidが max(0, min(1, (0.2 * x) + 0.5)) である話](https://qiita.com/hsjoihs/items/88d1569aaef01659bbd5)
 - [Swish活性化関数の紹介](https://www.renom.jp/ja/notebooks/tutorial/basic_algorithm/activation_swish/notebook.html)
 - [ついに誕生！期待の新しい活性化関数「Mish」解説](https://ai-scholar.tech/articles/treatise/mish-ai-374)
 - [活性化関数業界の期待のルーキー”Mish”について](https://medium.com/lsc-psd/活性化関数業界の期待のルーキー-mish-について-b1982782e186)
 - [Pytorch](https://pytorch.org/docs/stable/nn.html)
 - [Global Average Pooling（GAP）を理解してみる](https://qiita.com/mine820/items/1e49bca6d215ce88594a)


# 追記リスト＆謝辞
- @reppy4620さんよりtanhExp関数の情報をいただきました！ご丁寧に論文のリンクまで載せてくださり誠にありがとうございました！
- @tsubota-kougaさんよりhardSwish関数の情報をいただきました！論文の検証サイトですかね？必要な情報を見やすく載せてあるサイトのリンクを載せてくださり誠にありがとうございました！
- 再び@tsubota-kougaさんより、ACON関数の情報をいただきました！コメントに綺麗に纏まっていますのでそちらも是非ご参照ください！いつもありがとうございます！


# 深層学習シリーズ
- [深層学習入門 ~基礎編~](https://qiita.com/kuroitu/items/221e8c477ffdd0774b6b)
- [深層学習入門 ~コーディング準備編~](https://qiita.com/kuroitu/items/884c62c48c2daa3def08)
- [深層学習入門 ~順伝播編~](https://qiita.com/kuroitu/items/d22c8750e34d5d75fb6c)
- [深層学習入門 ~逆伝播編~](https://qiita.com/kuroitu/items/ea6ed8f614e65ec44976)
- [深層学習入門 ~学習則編~](https://qiita.com/kuroitu/items/ab5ad4ac716ae7a04891)
- [深層学習入門 ~ローカライズと損失関数編~](https://qiita.com/kuroitu/items/a6725ac0139d8eeb1e19)
- [深層学習入門 ~関数近似編~](https://qiita.com/kuroitu/items/4a8badcf7d3139d9ec7b)
- [深層学習入門 ~畳み込みとプーリング編~](https://qiita.com/kuroitu/items/48bf6bc42e4dfee52cde)
- [深層学習入門 ~CNN実験編~](https://qiita.com/kuroitu/items/d4716e754594a29b059d)
- [深層学習外伝 ~GPUプログラミング編~](https://qiita.com/kuroitu/items/2618c598f46d6ab3889c)
- [深層学習入門 ~ドロップアウト編~](https://qiita.com/kuroitu/items/d1bed8c216950d87be74)
- [活性化関数一覧 (2020)](https://qiita.com/kuroitu/items/73cd401afd463a78115a)
- [勾配降下法一覧 (2020)](https://qiita.com/kuroitu/items/36a58b37690d570dc618)
- [見てわかる！最適化手法の比較 (2020)](https://qiita.com/kuroitu/items/6695e0c79e888543e150)
- [im2col徹底理解](https://qiita.com/kuroitu/items/35d7b5a4bde470f69570)
- [col2im徹底理解](https://qiita.com/kuroitu/items/7877c002e192955c7857)
- [numpy.pad関数完全理解](https://qiita.com/kuroitu/items/51f4c867c8a44de739ec)