SELU
- 縮放指數線性單元:SELU(Scaled Exponential Linear Unit)
函數+導函數
-
SELU
函數
S E L U ( x ) = { λ x x > 0 λ α ( e x ? 1 ) x ≤ 0 \rm SELU(x) = \left\{ \begin{array}{} \lambda x \quad & x > 0 \\ \lambda \alpha (e^x - 1) \quad & x \le 0 \end{array} \right. SELU(x)={λxλα(ex?1)?x>0x≤0? -
SELU
函數導數
d d x S E L U ( x ) = { λ x > 0 λ α e x x ≤ 0 \frac{d}{dx} \rm SELU(x) = \left\{ \begin{array}{} \lambda \quad & x > 0 \\ \lambda \alpha e^x \quad & x \le 0 \end{array} \right. dxd?SELU(x)={λλαex?x>0x≤0?
其中, λ \lambda λ 和 α \alpha α 是預先定義的常數,通常取值 為
λ ≈ 1.0507 α ≈ 1.6732 \lambda \approx 1.0507 \\ \alpha \approx 1.6732 λ≈1.0507α≈1.6732
函數和導函數圖像
-
畫圖
下面代碼中的scale對應 λ \lambda λ, alpha 對應 α \alpha α
import numpy as np from matplotlib import pyplot as plt# 定義 SELU 函數 def selu(x, alpha=1.6732, scale=1.0507):return scale * np.where(x > 0, x, alpha * (np.exp(x) - 1))# 定義 SELU 的導數 def selu_derivative(x, alpha=1.6732, scale=1.0507:return scale * np.where(x > 0, 1, alpha * np.exp(x))# 生成數據 x = np.linspace(-2, 2, 1000) y = selu(x) y1 = selu_derivative(x)# 繪制圖形 plt.figure(figsize=(12, 8)) ax = plt.gca() plt.plot(x, y, label='SELU') plt.plot(x, y1, label='Derivative') plt.title('SELU and Derivative')# 設置上邊和右邊無邊框 ax.spines['right'].set_color('none') ax.spines['top'].set_color('none')# 設置 x 坐標刻度數字或名稱的位置 ax.xaxis.set_ticks_position('bottom')# 設置邊框位置 ax.spines['bottom'].set_position(('data', 0)) ax.yaxis.set_ticks_position('left') ax.spines['left'].set_position(('data', 0))plt.legend(loc=2) plt.show()
?
優缺點
- SELU函數針對ReLU函數的改進
- 當其中參數 $\lambda \approx 1.0507 ,\alpha \approx 1.6732 $ 時候,在網絡權重服從正態分布的條件下,各層輸出的分布會向標準正太分布靠攏。這種【自我標準化】的特性可以避免梯度消失和爆炸。
- SELU激活函數是在自歸一化網絡中定義的,通過調整均值和方差來實現內部的歸一化,這種內部歸一化比外部歸一化更快,這使得網絡收斂的更快。
- SELU是給ELU乘上一個系數,該系數大于 1 。在這篇paper Self-Normalizing Neural Networks中,作者提到,SELU可以使得輸入在經過一定層數之后變為固定的分布。以前的 ReLU,P-ReLU,ELU等激活函數都是在負半軸坡度平緩,這樣在激活的方差過大時可以讓梯度減小,防止了梯度爆炸,但是在正半軸其梯度簡答的設置為了 1 。而 SELU的正半軸大于 1 ,在方差過小的時候可以讓它增大,但是同時防止了梯度消失。這樣激活函數就有了一個不動點,網絡深了之后每一層的輸出都是均值為 0 ,方差為 1 。
-
SELU 的優點
- 自歸一化:SELU具有自歸一化特性,能夠自動調整網絡中的激活值,使其均值和方差保持穩定。這有助于避免梯度爆炸和梯度消失問題。
- 解決“死亡ReLU”問題:與ReLU不同,SELU允許負輸入,并通過參數α對其進行縮放,確保所有輸入都對模型有貢獻。
- 加速收斂:SELU通常能夠更快地收斂,并在訓練的早期階段實現更好的準確率。
- 減少對批量歸一化的依賴:由于SELU的自歸一化特性,它減少了對批量歸一化的需求,從而簡化了模型結構。
- 適用于多種任務:SELU可以用于多分類和二分類任務,并且在深度神經網絡中表現出色。
-
SELU 的缺點
- 對權重初始化敏感:SELU需要特定的權重初始化方法(LeCun正態初始化)才能發揮最佳效果。其他初始化方法可能會導致性能下降。
- 計算復雜度較高:SELU的指數運算比ReLU及其變體更復雜,計算成本更高。
- 資源有限:SELU不如ReLU廣泛使用,這可能會使尋找基于SELU的架構的庫或資源更具挑戰性。
- 可能的過擬合風險:在某些情況下,SELU可能會導致模型過擬合,尤其是在數據集較小或模型復雜度較高時。
pytorch中的SELU函數
-
代碼
import torch# 定義 SELU 函數 f = torch.nn.SELU() # PyTorch 提供的 SELU 激活函數模塊 x = torch.randn(2) # 生成一個隨機張量作為輸入selu_x = f(x) # 應用 SELU 函數print(f"x: \n{x}") print(f"selu_x:\n{selu_x}")"""輸出""" x: tensor([ 0.1895, -0.8333]) selu_x: tensor([ 0.1991, -0.9940])
tensorflow 中的SELU函數
-
代碼
python: 3.10.9
tensorflow: 2.18.0
import tensorflow as tf# 創建 SELU 激活函數 selu = tf.keras.activations.selu# 生成隨機輸入 # x = tf.random.normal([2]) x = x = [ 0.1895, -0.8333]# 應用 SELU 激活函數 selu_x = selu(x)print(f"x: \n{x}") print(f"selu_x:\n{selu_x}")"""輸出""" x: [0.1895, -0.8333] selu_x: [ 0.19910784 -0.99400705]