損失函數作爲建模的一個重要環節,一個針對模型、數據集都合適的損失函數對於建模的好壞至關重要,現查詢相關資料,將常見的分類、迴歸損失函數及常用的 Tensorflow 代碼總結於此,僅用於學習交流。
常見迴歸和分類損失函數比較
損失函數的定義爲 ,衡量真實值 和預測值 之間不一致的程度,一般越小越好。爲了便於不同損失函數的比較,常將其表示爲單變量的函數,在迴歸問題中這個變量爲 ,在分類問題中則爲 。
下面分別進行討論。
1. 迴歸問題的損失函數
迴歸問題中 和 皆爲實數 ,因此用殘差 來度量二者的不一致程度。殘差(的絕對值)越大,則損失函數越大,學習出來的模型效果就越差(這裏不考慮正則化問題)。
常見的迴歸損失函數有:
- 平方損失 (squared loss) :
- 絕對值 (absolute loss) :
- Huber損失 (huber loss) :
其中最常用的是平方損失,然而其缺點是對於異常點會施以較大的懲罰,因而不夠robust。
如果有較多異常點,則絕對值損失表現較好,但絕對值損失的缺點是在 處不連續可導,因而不容易優化。
Huber損失是對二者的綜合,當 小於一個事先指定的值 時,變爲平方損失,大於 時,則變成類似於絕對值損失,因此也是比較robust的損失函數。
1.1 tf.losses.mean_squared_error:均方根誤差(MSE) —— 迴歸問題中最常用的損失函數
優點:便於梯度下降,誤差大時下降快,誤差小時下降慢,有利於函數收斂。
缺點:受明顯偏離正常範圍的離羣樣本的影響較大
# Tensorflow中集成的函數
mse = tf.losses.mean_squared_error(y_true, y_pred)
# 利用Tensorflow基礎函數手工實現
mse = tf.reduce_mean(tf.square(y_true - y_pred))
1.2 tf.losses.absolute_difference:平均絕對誤差(MAE) —— 想格外增強對離羣樣本的健壯性時使用
優點:克服了 MSE 的缺點,受偏離正常範圍的離羣樣本影響較小。
缺點:收斂速度比 MSE 慢,因爲當誤差大或小時其都保持同等速度下降,而且在某一點處還不可導,計算機求導比較困難。
maes = tf.losses.absolute_difference(y_true, y_pred)
maes_loss = tf.reduce_sum(maes)
1.3 tf.losses.huber_loss:Huber loss —— 集合 MSE 和 MAE 的優點,但是需要手動調超參數
核心思想是,檢測真實值(y_true)和預測值(y_pred)之差的絕對值在超參數 δ 內時,使用 MSE 來計算 loss, 在 δ 外時使用類 MAE 計算 loss。sklearn 關於 huber 迴歸的文檔中建議將 δ=1.35 以達到 95% 的有效性。
hubers = tf.losses.huber_loss(y_true, y_pred)
hubers_loss = tf.reduce_sum(hubers)
三者的圖形比較如下:
1.2 迴歸損失函數優缺點對比:
2. 分類問題的損失函數
對於二分類問題,,損失函數常表示爲關於 的單調遞減形式。
如下圖:
被稱爲 margin,其作用類似於迴歸問題中的殘差 。
二分類問題中的分類規則通常爲:
可以看到如果 ,則樣本分類正確, 則分類錯誤,而相應的分類決策邊界即爲 。
所以最小化損失函數也可以看作是最大化 margin 的過程,任何合格的分類損失函數都應該對 margin < 0 的樣本施以較大的懲罰。
2.1 0-1損失 (zero-one loss)
0-1 損失對每個錯分類點都施以相同的懲罰,這樣那些“錯的離譜“ (即 )的點並不會收到大的關注,這在直覺上不是很合適。
另外0-1損失不連續、非凸,優化困難,因而常使用其他的代理損失函數進行優化。
2.2 Logistic loss
logistic Loss 爲 Logistic Regression 中使用的損失函數,下面做一下簡單證明:
Logistic Regression 中使用了 Sigmoid 函數表示預測概率:
而
因此利用,可寫爲,此爲一個概率模型,利用極大似然的思想:
兩邊取對數,又因爲是求損失函數,則將極大轉爲極小:
這樣就得到了logistic loss。
如果定義 ,則極大似然法可寫爲:
取對數並轉爲極小得:
上式被稱爲交叉熵損失 (cross entropy loss),可以看到在二分類問題中 logistic loss和交叉熵損失是等價的,二者區別只是標籤 y 的定義不同。
其標準形式爲:
交叉熵損失函數作爲二分類損失函數中最常用的損失函數,其對應的 TensorFlow 實現形式有如下幾種:
2.2.1 tf.nn.sigmoid_cross_entropy_with_logits:先 sigmoid 再求交叉熵 —— 二分類問題首選
使用時,一定不要將預測值(y_pred)進行 sigmoid 處理,否則會影響訓練的準確性,因爲函數內部已經包含了 sigmoid 激活(若已先行 sigmoid 處理過了,則 tensorflow 提供了另外的函數) 。真實值(y_true)則要求是 One-hot 編碼形式。
函數求得的結果是一組向量,是每個維度單獨的交叉熵,如果想求總的交叉熵,使用 tf.reduce_sum() 相加即可;如果想求 loss ,則使用 tf.reduce_mean() 進行平均。
# Tensorflow中集成的函數
sigmoids = tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=y_pred)
sigmoids_loss = tf.reduce_mean(sigmoids)
# 利用Tensorflow基礎函數手工實現
y_pred_si = 1.0/(1+tf.exp(-y_pred))
sigmoids = -y_true*tf.log(y_pred_si) - (1-y_true)*tf.log(1-y_pred_si)
sigmoids_loss = tf.reduce_mean(sigmoids)
2.2.2 tf.losses.log_loss:交叉熵 —— 效果同上,預測值格式略有不同
預測值(y_pred)計算完成後,若已先行進行了 sigmoid 處理,則使用此函數求 loss ,若還沒經過 sigmoid 處理,可直接使用 sigmoid_cross_entropy_with_logits。
# Tensorflow中集成的函數
logs = tf.losses.log_loss(labels=y, logits=y_pred)
logs_loss = tf.reduce_mean(logs)
# 利用Tensorflow基礎函數手工實現
logs = -y_true*tf.log(y_pred) - (1-y_true)*tf.log(1-y_pred)
logs_loss = tf.reduce_mean(logs)
2.2.3 tf.nn.softmax_cross_entropy_with_logits_v2:先 softmax 再求交叉熵 —— 多分類問題首選
使用時,預測值(y_pred)同樣是沒有經過 softmax 處理過的值,真實值(y_true)要求是 One-hot 編碼形式。
softmaxs = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=y_pred)
softmaxs_loss = tf.reduce_mean(softmaxs)
v1.8之前爲 tf.nn.softmax_cross_entropy_with_logits(),新函數修補了舊函數的不足,兩者在使用方法上是一樣的。
2.2.4 tf.nn.sparse_softmax_cross_entropy_with_logits:效果同上,真實值格式略有不同
若真實值(y_true)不是 One-hot 格式的,可以使用此函數,可省略一步轉換
softmaxs_sparse = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=y_pred)
softmaxs_sparse_loss = tf.reduce_mean(softmaxs_sparse)
2.2.5 tf.nn.weighted_cross_entropy_with_logits:帶權重的 sigmoid 交叉熵 —— 適用於正、負樣本數量差距過大時
增加了一個權重的係數,用來平衡正、負樣本差距,可在一定程度上解決差距過大時訓練結果嚴重偏向大樣本的情況。
# Tensorflow中集成的函數
sigmoids_weighted = tf.nn.weighted_cross_entropy_with_logits(targets=y, logits=y_pred, pos_weight)
sigmoids_weighted_loss = tf.reduce_mean(sigmoids_weighted)
# 利用Tensorflow基礎函數手工實現
sigmoids_weighted = -y_true*tf.log(y_pred) * weight - (1-y_true)*tf.log(1-y_pred)
sigmoids_loss = tf.reduce_mean(sigmoids)
2.3 Hinge loss
hinge loss 爲 svm 中使用的損失函數,hinge loss 使得 的樣本損失皆爲 0,由此帶來了稀疏解,使得 svm 僅通過少量的支持向量就能確定最終超平面。
對應 TensorFlow 實現:
2.3.1 tf.losses.hinge_loss:hinge 損失函數 —— SVM 中使用
hing_loss 是爲了求出不同類別間的“最大間隔”,此特性尤其適用於 SVM(支持向量機)。使用 SVM 做分類,與 LR(Logistic Regression 對數機率迴歸)相比,其優點是小樣本量便有不錯效果、對噪點包容性強,缺點是樣本量大時效率低、有時很難找到合適的區分方法。
hings = tf.losses.hinge_loss(labels=y, logits=y_pred, weights)
hings_loss = tf.reduce_mean(hings)
2.4 指數損失(Exponential loss)
exponential loss爲AdaBoost中使用的損失函數,使用exponential loss能比較方便地利用加法模型推導出AdaBoost算法 (具體推導過程可見)。然而其和squared loss一樣,對異常點敏感,不夠robust。
2.5 modified Huber loss
modified huber loss 結合了 hinge loss 和 logistic loss 的優點,既能在 時產生稀疏解提高訓練效率,又能進行概率估計。另外其對於 樣本的懲罰以線性增加,這意味着受異常點的干擾較少,比較robust。scikit-learn中的SGDClassifier同樣實現了modified huber loss。
最後來張全家福:
從上圖可以看出上面介紹的這些損失函數都可以看作是 0-1 損失的單調連續近似函數,而因爲這些損失函數通常是凸的連續函數,因此常用來代替 0-1 損失進行優化。它們的相同點是都隨着 而加大懲罰;不同點在於,logistic loss 和 hinge loss 都是線性增長,而 exponential loss 是以指數增長。
值得注意的是上圖中 modified huber loss 的走向和 exponential loss 差不多,並不能看出其 robust 的屬性。其實這和算法時間複雜度一樣,成倍放大了之後才能體現出巨大差異:
3. Tensorflow 自定義損失函數
標準的損失函數並不合適所有場景,有些實際的背景需要採用自己構造的損失函數,Tensorflow 也提供了豐富的基礎函數供自行構建。
例如下面的例子:當預測值(y_pred)比真實值(y_true)大時,使用 (y_pred-y_true)*loss_more 作爲 loss,反之,使用 (y_true-y_pred)*loss_less
loss = tf.reduce_sum(tf.where(tf.greater(y_pred, y_true), (y_pred-y_true)*loss_more,(y_true-y_pred)*loss_less))
tf.greater(x, y):判斷 x 是否大於 y,當維度不一致時廣播後比較
tf.where(condition, x, y):當 condition 爲 true 時返回 x,否則返回 y
tf.reduce_mean():沿維度求平均
tf.reduce_sum():沿維度相加
tf.reduce_prod():沿維度相乘
tf.reduce_min():沿維度找最小
tf.reduce_max():沿維度找最大
使用 Tensorflow 提供的方法可自行構造想要的損失函數。