1. 階躍函數
1.1 理論
式(3.3)表示的激活函數以閾值爲界,一旦輸入超過閾值,就切換輸出。這樣的函數稱爲“階躍函數”
因此,可以說感知機中使用了階躍函數作爲激活函數。也就是說,在激活函數的衆多候選函數中,感知機使用了階躍函數。
那麼,如果感知機使用其他函數作爲激活函數的話會怎麼樣呢?實際上,如果將激活函數從階躍函數換成其他函數,就可以進入神經網絡的世界了。
1.2 實現
階躍函數如式(3.3)所示,當輸入超過0 時,輸出1,否則輸出0。代碼實現如下:
def step_function(x):
if x <= 0:
return 0
else:
return 1
要想上面函數接受 Numpy
數組,即 step_function(np.array([1.0, 2.0]))
需要做如下改寫:
def step_function(x):
y = x > 0
return y.astype(np.int)
對上述函數進行分解:
In [1]: import numpy as np
In [2]: x = np.array([-1.0, 1.0, 2.0])
In [3]: x
Out[3]: array([-1., 1., 2.])
In [4]: y = x > 0
In [5]: y
Out[5]: array([False, True, True])
In [6]:
對 NumPy
數組進行不等號運算後,數組的各個元素都會進行不等號運算,生成一個布爾型數組。這裏,數組 x
中大於 0 的元素被轉換爲 True
,小於等於 0 的元素被轉換爲 False
,從而生成一個新的數組 y
。
數組 y
是一個布爾型數組,但是我們想要的階躍函數是會輸出 int
型的 0 或 1 的函數。因此,需要把數組 y
的元素類型從布爾型轉換爲 int
型。
In [6]: y.astype(np.int)
Out[6]: array([0, 1, 1])
In [7]:
如上所示,可以用 astype()
方法轉換 Numpy
數組的類型。 astype()
方法通過參數指定期望的類型,這個例子中是 np.int
型。 Python
中將布爾型轉換爲 int
型後, True
會轉換爲 1, False
會轉換爲 0。
1.3 圖形
圖形化代碼如下:
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=np.int)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y軸的範圍
plt.show()
np.arange(-5.0, 5.0, 0.1)
在 −5.0 到 5.0 的範圍內,以 0.1 爲單位,生成Numpy
數組([-5.0, -4.9, …, 4.9])。step_function()
以該Numpy
數組爲參數,對數組的各個元素執行階躍函數運算,並以數組形式返回運算結果。
對數組x、y進行繪圖,顯示如下:
2. sigmoid
函數
2.1 理論
神經網絡中經常使用的一個激活函數就是式(3.6)表示的 sigmoid
函數( sigmoid function
)。
式(3.6)中的 exp(−x)
表示 e−x 的意思。e
是納皮爾常數2.7182 . . .。
神經網絡中用 sigmoid
函數作爲激活函數,進行信號的轉換,轉換後的信號被傳送給下一個神經元。實際上,上一章介紹的感知機和接下來要介紹的神經網絡的主要區別就在於這個激活函數。其他方面,比如神經元的多層連接的構造、信號的傳遞方法等,基本上和感知機是一樣的。
2.2 實現
sigmoid
函數的 Python
代碼如下:
In [7]: def sigmoid(x):
...: return 1/(1+np.exp(-x))
這裏, np.exp(-x)
對應 e−x,當參數 x
爲 Numpy
數組時,結果也能被正確計算。
In [8]: x = np.array([-1, 1, 2])
In [9]: sigmoid(x)
Out[9]: array([0.26894142, 0.73105858, 0.88079708])
In [10]:
之所以 sigmoid
函數的實現能支持 Numpy
數組,祕密就在於 Numpy
的廣播功能, 即如果在標量和 Numpy
數組之間進行運算,則標量會和 Numpy
數組的各個元素進行運算。如下:
In [10]: a = np.array([-1, 1, 2])
In [11]: 10 + a
Out[11]: array([ 9, 11, 12])
In [12]:
2.3 圖形
實現代碼:
import numpy as np
import matplotlib.pylab as plt
def sigmoid(x):
return 1/(1+np.exp(-x))
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y軸的範圍
plt.show()
圖形化展示如下:
3. sigmoid
函數和階躍函數的比較
圖形比較代碼:
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=np.int)
def sigmoid(x):
return 1/(1+np.exp(-x))
x = np.arange(-5.0, 5.0, 0.1)
y1 = step_function(x)
y2 = sigmoid(x)
plt.plot(x, y1, label="step_function")
plt.plot(x, y2, linestyle="--", label="sigmoid") # 用虛線繪製
plt.xlabel("x") # x軸標籤
plt.ylabel("y") # y軸標籤
plt.title('step_function & sigmoid') # 標題
plt.legend()
plt.show()
兩者之間的圖形化展示如下:
差異點:
-
sigmoid
函數是一條平滑的曲線,輸出隨着輸入發生連續性的變化。而階躍函數以 0 爲界,輸出發生急劇性的變化; -
相對於階躍函數只能返回 0 或1,
sigmoid
函數可以返回 0.731 . . .、0.880 . . . 等實數,也就是說,感知機中神經元之間流動的是 0 或 1 的二元信號,而神經網絡中流動的是連續的實數值信號;
共同點:
- 輸入較小時,輸出接近 0(爲 0),隨着輸入增大,輸出向 1 靠近(變成 1),也就是說,當輸入信號爲重要信息時,階躍函數和
sigmoid
函數都會輸出較大的值,當輸入信號爲不重要的信息時,兩者都輸出較小的值; - 不管輸入信號有多小,或者有多大,輸出信號的值都在 0 到 1 之間;
- 兩者均爲非線性函數。
sigmoid
函數是一條曲線,階躍函數是一條像階梯一樣的折線;
4. 非線性函數
在介紹激活函數時,經常會看到“非線性函數”和“線性函數”等術語。函數本來是輸入某個值後會返回一個值的轉換器。向這個轉換器輸入某個值後,輸出值是輸入值的常數倍的函數稱爲線性函數(用數學式表示爲 h(x) = cx
。 c
爲常數)。
因此,線性函數是一條筆直的直線。而非線性函數,顧名思義,指的是不像線性函數那樣呈現出一條直線的函數。
神經網絡的激活函數必須使用非線性函數。換句話說,激活函數不能使用線性函數。爲什麼不能使用線性函數呢?因爲使用線性函數的話,加深神經網絡的層數就沒有意義了。
線性函數的問題在於,不管如何加深層數,總是存在與之等效的“無隱藏層的神經網絡”。爲了具體地(稍微直觀地)理解這一點,我們來思考下面這個簡單的例子。
這裏我們考慮把線性函數 h(x) = cx
作爲激活函數,把 y(x) = h(h(h(x)))
的運算對應 3 層神經網絡 A。這個運算會進行 y(x) = c × c × c × x
的乘法運算,但是同樣的處理可以由 y(x) = ax
(注意,a = c 3
)這一次乘法運算(即沒有隱藏層的神經網絡)來表示。
如本例所示,使用線性函數時,無法發揮多層網絡帶來的優勢。因此,爲了發揮疊加層所帶來的優勢,激活函數必須使用非線性函數。
5. ReLU
函數
在神經網絡發展的歷史上, sigmoid
函數很早就開始被使用了,而最近則主要使用 ReLU
線性整流函數 ( Rectified Linear Unit
)函數。
ReLU
函數在輸入大於 0 時,直接輸出該值;在輸入小於等於 0 時,輸出 0 (圖 3-9)。
ReLU
函 數 是 一 個 非 常 簡 單 的 函 數。因 此, ReLU
函數的實現也很簡單,可以寫成如下形式。
def relu(x):
return np.maximum(0, x)
maximum
函數會從輸入的數值中選擇較大的那個值進行輸出。
參考:《深度學習入門:基於Python的理論與實現》