激活函數的主要目的是制造非線性。如果不用激勵函數,每一層輸出都是上層輸入的線性函數,無論神經網絡有多少層,輸出都是輸入的線性組合。
如果使用的話,激活函數給神經元引入了非線性因素,使得神經網絡可以任意逼近任何非線性函數,這樣神經網絡就可以應用到眾多的非線性模型中。
理論上來說,神經網絡和多項式展開一樣,或者傅里葉變換,通過一種方法來表達(或逼近)任意的函數,因此只有線性肯定是不夠的,加上了非線性才能有表達非線性函數的能力。
softmax函數:定義Softmax函數,或稱歸一化指數函數,是邏輯函數的一種推廣。它能將一個含任意實數的K維向量 “壓縮”到另一個K維實向量
中,使得每一個元素的范圍都在
之間,並且所有元素的和為1。實際上就是對數歸一化,將實數映射到(0,1)之間,而且單調性不變,凸顯其中最大的值並抑制遠低於最大值的其他分量。
import math z = [1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0] z_exp = [math.exp(i) for i in z] print(z_exp) # Result: [2.72, 7.39, 20.09, 54.6, 2.72, 7.39, 20.09] sum_z_exp = sum(z_exp) print(sum_z_exp) # Result: 114.98 softmax = [round(i / sum_z_exp, 3) for i in z_exp] print(softmax) # Result: [0.024, 0.064, 0.175, 0.475, 0.024, 0.064, 0.175]
scores = np.array([123, 456, 789]) # example with 3 classes and each having large scores scores -= np.max(scores) # scores becomes [-666, -333, 0] p = np.exp(scores) / np.sum(np.exp(scores))
使用 Softmax 需要注意數值溢出的問題。因為有指數運算,如果 V 數值很大,經過指數運算后的數值往往可能有溢出的可能。所以,需要對 V 進行一些數值處理:即 V 中的每個元素減去 V 中的最大值。
Sigmoid函數:Sigmoid就是極端情況(類別數為2)下的Softmax 。優點是可以解釋,比如將0-1之間的取值解釋成一個神經元的激活率(firing rate)
缺點:激活函數計算量大,反向傳播求誤差梯度時,求導涉及除法,反向傳播時,很容易就會出現梯度消失的情況,從而無法完成深層網絡的訓練。Sigmoids函數飽和且kill掉梯度。Sigmoids函數收斂緩慢。
對比 | Softmax | Sigmoid |
公式 |
![]() |
![]() |
本質 | 離散概率分布 | 非線性映射 |
任務 | 多分類 | 二分類 |
定義域 | 某個一維向量 | 單個數值 |
值域 | [0,1] | (0,1) |
結果之和 | 一定為1 | 為某個正數 |
提問:softmax VS k個二元分類器
如果開發一個音樂分類的應用,需要對k種類型的音樂進行識別,那么是選擇使用 softmax 分類器呢,還是使用 logistic 回歸算法建立 k 個獨立的二元分類器呢?
這一選擇取決於類別之間是否互斥,例如,如果有四個類別的音樂,分別為:古典音樂、鄉村音樂、搖滾樂和爵士樂,那么可以假設每個訓練樣本只會被打上一個標簽(即:一首歌只能屬於這四種音樂類型的其中一種),此時應該使用類別數 k = 4 的softmax回歸。(如果在數據集中,有的歌曲不屬於以上四類的其中任何一類,那么可以添加一個“其他類”,並將類別數 k 設為5。)
如果四個類別如下:人聲音樂、舞曲、影視原聲、流行歌曲,那么這些類別之間並不是互斥的。例如:一首歌曲可以來源於影視原聲,同時也包含人聲 。這種情況下,使用4個二分類的 logistic 回歸分類器更為合適。這樣,對於每個新的音樂作品 ,算法可以分別判斷它是否屬於各個類別。
一個計算視覺領域的例子,任務是將圖像分到三個不同類別中。(i) 假設這三個類別分別是:室內場景、戶外城區場景、戶外荒野場景。會使用sofmax回歸還是 3個logistic 回歸分類器呢?
(ii) 現在假設這三個類別分別是室內場景、黑白圖片、包含人物的圖片,會選擇 softmax 回歸還是多個 logistic 回歸分類器呢?
在第一個例子中,三個類別是互斥的,因此更適於選擇softmax回歸分類器 。而在第二個例子中,建立三個獨立的 logistic回歸分類器更加合適。
提問:softmax 反向梯度
其實就是對權重參數進行反向求導。softmax可以看做是一個線性分類器,求導過程的程序設計分為兩種方法:一種是使用嵌套 for 循環,另一種是直接使用矩陣運算。
使用嵌套 for 循環,對權重 W 求導函數定義如下:
def softmax_loss_naive(W, X, y, reg): """ Softmax loss function, naive implementation (with loops) Inputs have dimension D, there are C classes, and we operate on minibatches of N examples. Inputs: - W: A numpy array of shape (D, C) containing weights. - X: A numpy array of shape (N, D) containing a minibatch of data. - y: A numpy array of shape (N,) containing training labels; y[i] = c means that X[i] has label c, where 0 <= c < C. - reg: (float) regularization strength Returns a tuple of: - loss as single float - gradient with respect to weights W; an array of same shape as W """ # Initialize the loss and gradient to zero. loss = 0.0 dW = np.zeros_like(W) num_train = X.shape[0] num_classes = W.shape[1] for i in xrange(num_train): scores = X[i,:].dot(W) scores_shift = scores - np.max(scores) right_class = y[i] loss += -scores_shift[right_class] + np.log(np.sum(np.exp(scores_shift))) for j in xrange(num_classes): softmax_output = np.exp(scores_shift[j]) / np.sum(np.exp(scores_shift)) if j == y[i]: dW[:,j] += (-1 + softmax_output) * X[i,:] else: dW[:,j] += softmax_output * X[i,:] loss /= num_train loss += 0.5 * reg * np.sum(W * W) dW /= num_train dW += reg * W return loss, dW
使用矩陣運算,對權重 W 求導函數定義如下:
def softmax_loss_vectorized(W, X, y, reg): """ Softmax loss function, vectorized version. Inputs and outputs are the same as softmax_loss_naive. """ # Initialize the loss and gradient to zero. loss = 0.0 dW = np.zeros_like(W) num_train = X.shape[0] num_classes = W.shape[1] scores = X.dot(W) scores_shift = scores - np.max(scores, axis = 1).reshape(-1,1) softmax_output = np.exp(scores_shift) / np.sum(np.exp(scores_shift), axis=1).reshape(-1,1) loss = -np.sum(np.log(softmax_output[range(num_train), list(y)])) loss /= num_train loss += 0.5 * reg * np.sum(W * W) dS = softmax_output.copy() dS[range(num_train), list(y)] += -1 dW = (X.T).dot(dS) dW = dW / num_train + reg * W return loss, dW
實際驗證表明,矩陣運算速度要比嵌套循環快很多,特別是在訓練樣本數量多的情況下。我們使用 CIFAR-10 數據集中約5000個樣本對兩種求導方式進行測試對比:
tic = time.time() loss_naive, grad_naive = softmax_loss_naive(W, X_train, y_train, 0.000005) toc = time.time() print('naive loss: %e computed in %fs' % (loss_naive, toc - tic)) tic = time.time() loss_vectorized, grad_vectorized = softmax_loss_vectorized(W, X_train, y_train, 0.000005) toc = time.time() print('vectorized loss: %e computed in %fs' % (loss_vectorized, toc - tic)) grad_difference = np.linalg.norm(grad_naive - grad_vectorized, ord='fro') print('Loss difference: %f' % np.abs(loss_naive - loss_vectorized)) print('Gradient difference: %f' % grad_difference)
結果顯示為:
naive loss: 2.362135e+00 computed in 14.680000s vectorized loss: 2.362135e+00 computed in 0.242000s Loss difference: 0.000000 Gradient difference: 0.000000
顯然,此例中矩陣運算的速度要比嵌套循環快60倍。所以,當我們在編寫機器學習算法模型時,盡量使用矩陣運算,少用 嵌套循環,以提高運算速度。(求線性回歸的最小二乘解涉及矩陣求逆。在樣本量大/維度高的情況下計算量較大。這時可以使用梯度下降法近似來求最小二乘解。)
實際上,這又回歸到解方程,線性代數那部分知識去了,如果通常來說參數比樣本少,多元方程肯定有解,如果參數太多,多余樣本,也可以找到一個較小的loss值作為求參結束的標志。參考以下網址:http://cs231n.github.io/classification/
https://blog.csdn.net/red_stone1/article/details/80687921
ReLU函數:
輸入信號 <0 時,輸出都是0,>0 的情況下,輸出等於輸入。Krizhevsky et al. 發現使用 ReLU 得到的 SGD 的收斂速度會比 sigmoid/tanh 快很多。
ReLU更容易學習優化。因為其分段線性性質,導致其前傳,后傳,求導都是分段線性。而傳統的sigmoid函數,由於兩端飽和,在傳播過程中容易丟棄信息。
ReLU激活函數在AlexNet[3]中大放異彩,使用ReLU作為激活函數的網絡比使用tanh作為激活函數的網絡收斂快6倍。這個時候大家突然發現激活函數的主要目的是制造非線性,有沒有生物學解釋其實不那么重要,函數光滑不光滑也沒那么重要。
缺點:訓練的時候很”脆弱”,很容易就”die”了,還是非zero-centered。非zero-centered會導致后一層的神經元將得到上一層輸出的非0均值的信號作為輸入。 產生的一個結果就是對w求局部梯度則都為正,
這樣在反向傳播的過程中w要么都往正方向更新,要么都往負方向更新,導致有一種捆綁的效果,使得收斂緩慢。詳細可以參考:https://www.jianshu.com/p/917f71b06499
例如,一個非常大的梯度流過一個 ReLU 神經元,更新過參數之后,這個神經元再也不會對任何數據有激活現象了,那么這個神經元的梯度就永遠都會是 0.
如果 learning rate 很大,那么很有可能網絡中的 40% 的神經元都”dead”了。
Tanh函數:
也稱為雙切正切函數,取值范圍為[-1,1]。
tanh在特征相差明顯時的效果會很好,在循環過程中會不斷擴大特征效果。
與 sigmoid 的區別是,tanh 是 0 均值的,因此實際應用中 tanh 會比 sigmoid 更好,但是雖然tanh是zero-centered,但是還是會飽和。
Maxout函數:
maxout是通過分段線性函數來擬合所有可能的凸函數來作為激活函數的,但是由於線性函數是可學習,所以實際上是可以學出來的激活函數。具體操作是對所有線性取最大,也就是把若干直線的交點作為分段的界,然后每一段取最大。
maxout可以看成是relu家族的一個推廣。
缺點在於增加了參數量。
結尾: 應用中如何選擇合適的激活函數?
這個問題目前沒有確定的方法,憑一些經驗吧。
1)深度學習往往需要大量時間來處理大量數據,模型的收斂速度是尤為重要的。所以,總體上來講,訓練深度學習網絡盡量使用zero-centered數據 (可以經過數據預處理實現) 和zero-centered輸出。所以要盡量選擇輸出具有zero-centered特點的激活函數以加快模型的收斂速度。
2)如果使用 ReLU,那么一定要小心設置 learning rate,而且要注意不要讓網絡出現很多 “dead” 神經元,如果這個問題不好解決,那么可以試試 Leaky ReLU、PReLU 或者 Maxout.
3)最好不要用 sigmoid,你可以試試 tanh,不過可以預期它的效果會比不上 ReLU 和 Maxout.