介紹
想象一下-你已經在給定的數據集上訓練了機器學習模型,并準備好將它交付給客戶。但是,你如何確定該模型能夠提供最佳結果?是否有指標或技術可以幫助你快速評估數據集上的模型?
當然是有的,簡而言之,機器學習中損失函數可以解決以上問題。
損失函數是我們喜歡使用的機器學習算法的核心。但大多數初學者和愛好者不清楚如何以及在何處使用它們。
它們并不難理解,反而可以增強你對機器學習算法的理解。那么,什么是損失函數,你如何理解它們的意義?
在本文中,我將討論機器學習中使用的7種常見損失函數,并解釋每種函數的使用方法。
目錄
- 什么是損失函數?
- 回歸損失函數
- 平方誤差損失
- 絕對誤差損失
- Huber損失
- 二分類損失函數
- 二分類交叉熵
- Hinge損失
- 多分類損失函數
- 多分類交叉熵損失
- KL散度(Kullback Leibler Divergence Loss)
1. 什么是損失函數?
假設你在山頂,需要下山。你如何決定走哪個方向?

我要做的事情如下:
- 環顧四周,看看所有可能的路徑
- 拒絕那些上升的路徑。這是因為這些路徑實際上會消耗更多的體力并使下山任務變得更加艱難
- 最后,走我認為的坡度最大的路徑
關于我判斷我的決策是否好壞的直覺,這正是損失函數能夠提供的功能。
損失函數將決策映射到其相關成本
決定走上坡的路徑將耗費我們的體力和時間。決定走下坡的路徑將使我們受益。因此,下坡的成本是更小的。
在有監督的機器學習算法中,我們希望在學習過程中最小化每個訓練樣例的誤差。這是使用梯度下降等一些優化策略完成的。而這個誤差來自損失函數。
損失函數(Loss Function)和成本函數(Cost Function)之間有什么區別?
在此強調這一點,盡管成本函數和損失函數是同義詞并且可以互換使用,但它們是不同的。
損失函數用于單個訓練樣本。它有時也稱為誤差函數(error function)。另一方面,成本函數是整個訓練數據集的平均損失(average function)。優化策略旨在最小化成本函數。
2. 回歸損失函數
此時你必須非常熟悉線性回歸。它涉及對因變量Y和幾個獨立變量 X_i 之間的線性關系進行建模。因此,我們在空間中對這些數據擬合出一條直線或者超平面。
Y = a0 + a1 * X1 + a2 * X2 + ....+ an * Xn
我們將使用給定的數據點來找到系數a0,a1,…,an。

我們將使用著名的波士頓住房數據集來理解這個概念。為了簡單起見,我們將只使用一個特征-每個住宅的平均房間數(Average number of rooms per dwelling)(X)來預測因變量-1000美元價位的房屋的中位數價值(Median Value)(Y)。

我們將使用梯度下降(Gradient Descent)作為優化策略來查找回歸線。我不會詳細介紹Gradient Descent的細節,但這里提醒一下權重更新規則:

這里,θ_j 是要更新的權重,α 是學習率,J 是成本函數。成本函數由 θ 參數化。我們的目標是找到產生最小總成本的 θ 值。
我已經為下面的每個損失函數定義了我們將遵循的步驟:
- 寫出預測函數f(X)的表達式,并確定我們需要找到的參數
- 確定每個訓練樣本計算得到的損失
- 找到成本函數(所有樣本的平均損失)的表達式
- 找到與每個未知參數相關的成本函數的梯度
- 確定學習率并在固定次數中進行迭代執行權重更新規則
2.1. 平方誤差損失
每個訓練樣本的平方誤差損失(也稱為L2 Loss)是實際值和預測值之差的平方:

相應的成本函數是這些平方誤差的平均值(MSE)。
推薦你引用以下代碼時先嘗試自己計算出梯度
def update_weights_MSE(m, b, X, Y, learning_rate): m_deriv = 0 b_deriv = 0 N = len(X) for i in range(N): # 計算偏導數為 # -2x(y - (mx + b)) m_deriv += -2*X[i] * (Y[i] - (m*X[i] + b)) # -2(y - (mx + b)) b_deriv += -2*(Y[i] - (m*X[i] + b)) # 我們減去它,因為導數指向最陡的上升方向 m -= (m_deriv / float(N)) * learning_rate b -= (b_deriv / float(N)) * learning_rate return m, b
在波士頓住房數據上,在不同的學習率中分別迭代了500次得到下圖:

讓我們再談談MSE損失函數,它是一個二次函數(形式為ax^2+bx+c),并且值大于等于0。二次函數的圖形如下圖所示:

二次函數僅具有全局最小值。由于沒有局部最小值,所以我們永遠不會陷入它。因此,可以保證梯度下降將收斂到全局最小值(如果它完全收斂)。
MSE損失函數通過平方誤差來懲罰模型犯的大錯誤。把一個比較大的數平方會使它變得更大。但有一點需要注意,這個屬性使MSE成本函數對異常值的健壯性降低。因此,如果我們的數據容易出現許多的異常值,則不應使用這個它。
2.2. 絕對誤差損失
每個訓練樣本的絕對誤差是預測值和實際值之間的距離,與符號無關。絕對誤差也稱為L1 Loss:

正如我之前提到的,成本是這些絕對誤差的平均值(MAE)。
與MSE相比,MAE成本對異常值更加健壯。但是,在數學方程中處理絕對或模數運算符并不容易。我們可以認為這是MAE的缺點。
以下是MAE成本更新權重的代碼
def update_weights_MAE(m, b, X, Y, learning_rate): m_deriv = 0 b_deriv = 0 N = len(X) for i in range(N): #計算偏導數 # -x(y - (mx + b)) / |mx + b| m_deriv += - X[i] * (Y[i] - (m*X[i] + b)) / abs(Y[i] - (m*X[i] + b)) # -(y - (mx + b)) / |mx + b| b_deriv += -(Y[i] - (m*X[i] + b)) / abs(Y[i] - (m*X[i] + b)) #我們減去它,因為導數指向最陡的上升方向 m -= (m_deriv / float(N)) * learning_rate b -= (b_deriv / float(N)) * learning_rate return m, b
在不同學習速率中分別迭代500次后,我們得到以下圖:

2.3. Huber損失
Huber損失結合了MSE和MAE的最佳特性。對于較小的誤差,它是二次的,否則是線性的(對于其梯度也是如此)。Huber損失需要確定 δ 參數:

def update_weights_Huber(m, b, X, Y, delta, learning_rate): m_deriv = 0 b_deriv = 0 N = len(X) for i in range(N): # 小值的二次導數,大值的線性導數 if abs(Y[i] - m*X[i] - b) <= delta: m_deriv += -X[i] * (Y[i] - (m*X[i] + b)) b_deriv += - (Y[i] - (m*X[i] + b)) else: m_deriv += delta * X[i] * ((m*X[i] + b) - Y[i]) / abs((m*X[i] + b) - Y[i]) b_deriv += delta * ((m*X[i] + b) - Y[i]) / abs((m*X[i] + b) - Y[i]) #我們減去它,因為導數指向最陡的上升方向 m -= (m_deriv / float(N)) * learning_rate b -= (b_deriv / float(N)) * learning_rate return m, b
我們以0.0001的學習速率分別對 δ 參數的不同值進行500次權重更新迭代得到下圖:

Huber損失對于異常值比MSE更強。它用于穩健回歸(robust regression),M估計法(M-estimator)和可加模型(additive model)。Huber損失的變體也可以用于分類。
3. 二分類損失函數
意義如其名。二分類是指將物品分配到兩個類中的一個。該分類基于應用于輸入特征向量的規則。二分類的例子例如,根據郵件的主題將電子郵件分類為垃圾郵件或非垃圾郵件。
我將在乳腺癌數據集^2上說明這些二分類損失函數。
我們希望根據平均半徑,面積,周長等特征將腫瘤分類為"惡性(Malignant)"或"良性(Benign)"。為簡化起見,我們將僅使用兩個輸入特征(X_1和X_2),即"最差區域(worst area)"和"平均對稱性(mean symmetry)"用于分類。Y是二值的,為0(惡性)或1(良性)。
這是我們數據的散點圖:

cancer
3.1. 二元交叉熵損失
讓我們從理解術語"熵"開始。 通常,我們使用熵來表示無序或不確定性。測量具有概率分布p(X)的隨機變量X:

負號用于使最后的結果為正數。
概率分布的熵值越大,表明分布的不確定性越大。同樣,一個較小的值代表一個更確定的分布。
這使得二元交叉熵適合作為損失函數(你希望最小化其值)。我們對輸出概率p的分類模型使用二元交叉熵損失。
元素屬于第1類(或正類)的概率=p 元素屬于第0類(或負類)的概率=1-p
然后,輸出標簽y(可以取值0和1)的交叉熵損失和和預測概率p定義為:

這也稱為Log-Loss(對數損失)。為了計算概率p,我們可以使用sigmoid函數。這里,z是我們輸入功能的函數:

sigmoid函數的范圍是[0,1],這使得它適合于計算概率。

推薦你引用以下代碼時先嘗試自己計算出梯度
def update_weights_BCE(m1, m2, b, X1, X2, Y, learning_rate): m1_deriv = 0 m2_deriv = 0 b_deriv = 0 N = len(X1) for i in range(N): s = 1 / (1 / (1 + math.exp(-m1*X1[i] - m2*X2[i] - b))) # 計算偏導數 m1_deriv += -X1[i] * (s - Y[i]) m2_deriv += -X2[i] * (s - Y[i]) b_deriv += -(s - Y[i]) # 我們減去它,因為導數指向最陡的上升方向 m1 -= (m1_deriv / float(N)) * learning_rate m2 -= (m2_deriv / float(N)) * learning_rate b -= (b_deriv / float(N)) * learning_rate return m1, m2, b
在不同alpha值里使用權重更新規則進行1000次迭代得到下圖:

3.2. Hinge損失
Hinge損失主要用于帶有類標簽-1和1的支持向量機(SVM)。因此,請確保將數據集中"惡性"類的標簽從0更改為-1。
Hinge損失不僅會懲罰錯誤的預測,還會懲罰不自信的正確預測。
數據對(x,y)的Hinge損失如圖:

def update_weights_Hinge(m1, m2, b, X1, X2, Y, learning_rate): m1_deriv = 0 m2_deriv = 0 b_deriv = 0 N = len(X1) for i in range(N): # 計算偏導數 if Y[i]*(m1*X1[i] + m2*X2[i] + b) <= 1: m1_deriv += -X1[i] * Y[i] m2_deriv += -X2[i] * Y[i] b_deriv += -Y[i] # 否則偏導數為0 # 我們減去它,因為導數指向最陡的上升方向 m1 -= (m1_deriv / float(N)) * learning_rate m2 -= (m2_deriv / float(N)) * learning_rate b -= (b_deriv / float(N)) * learning_rate return m1, m2, b
在使用三個不同的alpha值運行2000次迭代的更新函數之后,得到下圖:

Hinge損失簡化了SVM的數學運算,同時最大化了損失(與對數損失(Log-Loss)相比)。當我們想要做實時決策而不是高度關注準確性時,就可以使用它。
4. 多分類損失函數
電子郵件不僅被歸類為垃圾郵件或垃圾郵件(這不再是90年代了!)。它們分為各種其他類別-工作,家庭,社交,促銷等。
我們將使用Iris數據集^3來理解剩余的兩個損失函數。我們將使用2個特征 X_1 萼片長度(Sepal length)和特征 X_2 花瓣寬度(Petal width)來預測鳶尾花的類別(Y) -Setosa,Versicolor或Virginica
我們的任務是使用神經網絡模型和Keras內置的Adam優化器來實現分類器。這是因為隨著參數數量的增加,數學以及代碼將變得難以理解。
這是我們數據的散點圖:

4.1. 多分類交叉熵損失
多分類交叉熵損失是二元交叉熵損失的推廣。輸入向量 X_i 和相應的one-hot編碼目標向量 Y_i 的損失是:

我們使用softmax函數來找到概率 P_ij:

"Softmax層是接在神經網絡的輸出層前。Softmax層必須與輸出層具有相同數量的節點。"google Developer's Blog

最后,我們的輸出是具有給定輸入的最大概率的類別。
我們使用一個輸入層和一個輸出層建立一個模型,并用不同的學習速度編譯它。在model.compile()語句中將損失函數指定為' categorical_crossentropy ':
# 導入包 from keras.layers import Dense from keras.models import Sequential from keras.optimizers import adam #alpha設置為0.001,如adam優化器中的lr參數所示 # 創建模型 model_alpha1 = Sequential() model_alpha1.add(Dense(50, input_dim=2, activation='relu')) model_alpha1.add(Dense(3, activation='softmax')) # 編譯模型 opt_alpha1 = adam(lr=0.001) model_alpha1.compile(loss='categorical_crossentropy', optimizer=opt_alpha1, metrics=['accuracy']) # 擬合模型 # dummy_Y是one-hot形式編碼的 # history_alpha1用于為繪圖的驗證和準確性評分 history_alpha1 = model_alpha1.fit(dataX, dummy_Y, validation_data=(dataX, dummy_Y), epochs=200, verbose=0)
在不同的學習率經過200輪訓練后成本和準確度的圖如下:


4.2. KL散度
KL散度概率分布與另一個概率分布區別的度量。KL散度為零表示分布相同。

請注意,發散函數不對稱。即:

這就是為什么KL散度不能用作距離度量的原因。
我將描述使用KL散度作為損失函數而不進行數學計算的基本方法。在給定一些近似分布Q的情況下,我們希望近似關于輸入特征的目標變量的真實概率分布P. 由于KL散度不對稱,我們可以通過兩種方式實現:

第一種方法用于監督學習,第二種方法用于強化學習。KL散度在功能上類似于多分類交叉熵,KL散度也可以稱為P相對于Q的相對熵:
我們在compile()函數中指定'kullback_leibler_divergence'作為損失函數,就像我們之前在處理多分類交叉熵損失時所做的那樣。

# 導入包 from keras.layers import Dense from keras.models import Sequential from keras.optimizers import adam # alpha設置為0.001,如adam優化器中的lr參數所示 # 創建模型 model_alpha1 = Sequential() model_alpha1.add(Dense(50, input_dim=2, activation='relu')) model_alpha1.add(Dense(3, activation='softmax')) # 編譯模型 opt_alpha1 = adam(lr=0.001) model_alpha1.compile(loss='kullback_leibler_divergence', optimizer=opt_alpha1, metrics=['accuracy']) # 擬合模型 # dummy_Y是one-hot形式編碼的 # history_alpha1用于為繪圖的驗證和準確性評分 history_alpha1 = model_alpha1.fit(dataX, dummy_Y, validation_data=(dataX, dummy_Y), epochs=200, verbose=0)
在不同的學習率經過200輪訓練后成本和準確度的圖如下:


與多分類分類相比,KL散度更常用于逼近復雜函數。我們在使用變分自動編碼器(VAE)等深度生成模型時經常使用KL散度。