梯度提升二三事:怎麼來自定義損失函式?
介紹
梯度提升技術在工業中得到了廣泛的應用,並贏得了許多Kaggle比賽。()網際網路已經有很多關於梯度提升的很好的解釋,但我們注意到關於自定義損失函式的資訊缺乏:比如原因,時間和方式。這篇文章是我們嘗試總結自定義損失函式在許多實際問題中的重要性,以及如何使用LightGBM梯度提升包實現它們。
常見的ML庫中有許多常用的損失函式。如果你想進一步瞭解這方面的知識,請閱讀這篇文章,這是Prince在攻讀資料科學碩士時寫的。()在現實世界中,這些"現成的"損失函式通常無法很好地適應我們試圖解決的業務問題。輸入自定義損失函式。
自定義損失函式
使用自定義損失函式的一個例子是機場準時的風險不對稱。問題是決定何時離開家,以便在合適的時間到達機場。我們不想太早離開,在機場等幾個小時。與此同時,我們更不想錯過我們的航班。任何一方的損失都是非常不同的:如果我們提前到達,情況真的不是那麼糟糕;如果我們來得太晚而錯過航班,那真的很糟糕。如果我們使用機器學習來決定何時離開房子,我們可能希望直接在我們的模型中處理這種風險不對稱,透過使用自定義損失函式來"懲罰"晚期錯誤而不是早期錯誤。
另一個常見的例子是分類問題。例如,對於疾病檢測,我們可能認為假陰性比假陽性要嚴重得多,因為給予健康人的藥物通常比未治療病人的危害小。在這種情況下,我們可能希望最佳化F-beta評分,其中β取決於我們想要給予誤報的權重大小。這有時被稱為Neyman-Pearson標準。
在Manifold,我們最近遇到了一個需要自定義損失函式的問題。我們的客戶之一Cortex Building Intelligence提供的應用程式可幫助工程師更精確地操作建築物供暖,通風和空調(HVAC)系統。大多數商業建築物具有"租賃義務",以在工作日期間的工作時間內將建築物室內溫度調節在"舒適"溫度範圍內,例如在上午9點至下午6點期間在華氏70和74度之間。與此同時,HVAC是建築物的最大運營成本。高效HVAC執行的關鍵是在不需要時關閉系統,如夜間,並在清晨再次開啟以滿足"租賃義務"。為此,Manifold幫助Cortex建立了一個預測模型,以建議在建築物中開啟HVAC系統的確切時間。
然而,錯誤預測的懲罰是不對稱的。如果我們預測的啟動時間早於實際所需的啟動時間,那麼建築物將過早地達到舒適的溫度並且會浪費一些能量。但是如果預測的時間晚於實際所需的開始時間,那麼建築物的溫度就會升溫較晚,租戶也不會感到高興——沒有人想在冰冷的建築物中工作,購物或學習。因此,晚開比早開更糟糕,因為我們不希望租戶不開心。我們透過建立自定義非對稱Huber損失函式將業務知識編碼到我們的模型中,當殘差為正與負時,該函式具有更高的誤差。有關此問題的更多詳細資訊,請參閱此文章。(https://www.manifold.ai/blog/data-science-at-cortex)
結論:找到一個與你的業務目標緊密匹配的損失函式。通常,這些損失函式在流行的機器學習庫中沒有預設的實現。沒關係:定義自己的損失函式並用它來解決你的問題並不難。
自定義訓練損失和驗證損失
在進一步討論之前,讓我們在定義中明確一點。ML文獻中使用了許多術語來指代不同的東西。我們將選擇一組我們認為最清晰的定義:
·訓練損失。這是在訓練資料上進行最佳化的函式。例如,在神經網路二進位制分類器中,這通常是二進位制交叉熵。對於隨機森林分類器,這是基尼係數。訓練損失通常也稱為"目標函式"。
·驗證損失。這是我們用來評估我們訓練模型在看不見的資料上的效能的函式。這通常與訓練損失不同。例如,在分類器的情況下,這通常是接收器工作特性(ROC)曲線下的區域——儘管這從未直接最佳化,因為它是不可微分。這通常稱為"效能或評估指標"。
在許多情況下,自定義這些損失對於構建更好的模型非常有效。這對於梯度提升特別簡單,如下所示。
訓練損失
在訓練期間最佳化訓練損失。很難為某些演算法自定義,比如隨機森林(見這裡:),但對其他演算法來說比較容易,比如梯度提升和神經網路。因為梯度下降的一些變體通常是最佳化方法,所以訓練損失通常需要一個具有凸梯度(一階導數)和海森(二階導數)的函式。它最好是連續的,有限的和非零的。最後一個很重要,因為函式為零的部分可以凍結梯度下降。
在梯度提升的背景下,訓練損失是使用梯度下降最佳化的函式,例如梯度提升模型的"梯度"部分。具體地,訓練損失的梯度用於改變每個連續樹的目標變數。(如果你對更多細節感興趣,請參閱此文章。)請注意,即使訓練損失定義了"梯度",每個樹仍然使用與此自定義損失函式無關的貪婪分割演算法生長。
定義自定義訓練損失通常需要我們做一些微積分來找到梯度和海森。正如我們接下來將要看到的,首先更改驗證損失更容易一些,因為它不需要那麼多的開銷。
驗證損失
驗證損失用於調整超引數。它通常更容易自定義,因為它沒有像訓練損失那樣多的功能要求。驗證損失可以是非凸的,不可微分的和不連續的。因此,從自定義開始通常是一個更容易的地方。
例如,在LightGBM中,一個重要的超引數是boosting的數量。驗證損失可用於找到最佳數量的boosting次數。 LightGBM中的驗證損失稱為eval_metric。我們可以使用庫中可用的驗證損失之一,也可以定義我們自己的自定義函式。()由於它非常簡單,如果它對你的業務問題很重要,那麼你一定要自定義。
具體而言,我們通常使用early_stopping_rounds變數,而不是直接最佳化num boosting的輪數。當給定數量的早期停止輪次的驗證損失開始增加時,它會停止提升。實際上,它透過監視樣本外驗證集的驗證損失來防止過度擬合。如下圖所示,設定更高的停止輪次會導致模型執行更多boosting 輪次。
藍色:訓練損失。橙色:驗證損失。訓練和驗證都使用相同的自定義損失功能
k折交叉驗證。每個測試摺疊都有驗證損失
請記住,驗證策略也非常重要。上面列出的訓練/驗證是許多可能的驗證策略之一。它可能不適合你的問題。其他包括k-fold交叉驗證和巢狀交叉驗證,我們在HVAC啟動時建模問題中使用了這些驗證。
如果適用於業務問題,我們希望使用自定義函式來進行訓練和驗證損失。在某些情況下,由於自定義損失的函式形式,可能無法將其用作訓練損失。在這種情況下,僅更新驗證損失並使用像MSE這樣的預設訓練損失可能是有意義的。你仍然可以獲益,因為超引數將使用所需的自定義損失進行調整。
在LightGBM中實現自定義損失功能
讓我們來看看它在實踐中的樣子,並對模擬資料進行一些實驗。首先,讓我們假設過高估計比低估更糟糕。另外,假設平方損失是我們在任一方向上的誤差的良好模型。為了對其進行編碼,我們定義了一個自定義MSE函式,它對正殘差的懲罰比負殘差多10倍。下圖說明了我們的自定義損失函式與標準MSE損失函式的對比情況。
根據定義,非對稱MSE很好,因為它具有易於計算的梯度和海森度,如下圖所示。請注意,海森在兩個不同的值上是常量,左邊是2,右邊是20,儘管在下面的圖中很難看到。
LightGBM提供了一種直接的方式來實現自定義訓練和驗證損失。其他梯度提升包,包括XGBoost和Catboost,也提供此選項。這是一個Jupyter筆記本(https://github.com/manifoldai/mf-eng-public/blob/master/notebooks/custom_loss_lightgbm.ipynb),展示瞭如何實現自定義訓練和驗證損失功能。細節在筆記本中,但在較高的層次上,實現略有不同:
-
訓練損失:在LightGBM中自定義訓練損失需要定義一個函式,該函式包含兩個陣列,即目標及其預測。反過來,該函式應該返回每個觀察的兩個梯度和海森的陣列。如上所述,我們需要使用微積分來匯出梯度和海森,然後在Python中實現它。
-
驗證損失:自定義LightGBM中的驗證損失需要定義一個函式,該函式接受相同的兩個陣列,但返回三個值:一個字串,其名稱為metric的字串,損失本身,以及關於更高是否更好的布林值。
用於在LightGBM中實現自定義損失的程式碼
定義自定義驗證和訓練損失功能
在LightGBM中結合訓練和驗證損失(包括Python和scikit-learn API示例)
自定義損失函式的實驗
Jupyter筆記本還對預設隨機森林,預設LightGBM和MSE以及LightGBM與自定義訓練和驗損失函式進行了深入比較。(https://github.com/manifoldai/mf-eng-public/blob/master/notebooks/custom_loss_lightgbm.ipynb)我們使用Friedman 1合成資料集,進行了8,000次訓練觀察,2,000次驗證觀察和5,000次測試觀察。驗證集用於查詢最佳化驗證損失的最佳超引數集。下面報告的分數在測試觀察結果上進行評估,以評估我們模型的普遍性。
我們已經完成了下表中總結的一系列實驗。請注意,我們關心的最重要的分數是非對稱MSE,因為它明確定義了我們的不對稱懲罰問題。
我們的實驗和結果
讓我們詳細看一些比較。
隨機森林→LightGBM
使用預設設定,LightGBM在此資料集上的效能優於Random Forest。隨著更多樹和超引數的更好組合,隨機森林也可能會給出好的結果,但這不是重點。
LightGBM→LightGBM,具有自定義的訓練損失
這表明我們可以使我們的模型最佳化我們關心的內容。預設的LightGBM正在最佳化MSE,因此它可以降低MSE損失(0.24 vs 0.33)。具有自定義訓練損失的LightGBM最佳化了非對稱MSE,因此對於非對稱MSE(1.31 vs. 0.81)表現更好。
LightGBM→LightGBM使用MSE調整早期停止輪次
兩種LightGBM模型都在最佳化MSE。我們看到預設的MSE分數有了很大改善,只需稍微調整一下使用早期停止輪次(MSE:0.24 vs 0.14)。因此,我們應該讓模型使用早期停止超引數來確定最佳提升次數,而不是將提升次數限制為預設值(即100)。超引數最佳化很重要!
LightGBM使用MSE→LightGBM調整早期停止輪次,並使用自定義MSE進行早期停止調整
這兩個模型的得分非常接近,沒有實質性差異。這是因為驗證損失僅用於決定何時停止提升。梯度是在兩種情況下最佳化預設MSE。每個後續樹為兩個模型生成相同的輸出。唯一的區別是具有自定義驗證損失的模型在742次增強迭代時停止,而其他的則執行多次。
LightGBM使用自定義MSE→LightGBM透過自定義損失進行調整,並使用MSE進行早期停止調整
僅在不改變驗證損失的情況下自定義訓練損失會損害模型效能。只有自定義訓練損失的模型比其他情況增加了更多輪次(1848)。如果我們仔細觀察,這個模型的訓練損失非常低(0.013)並且在訓練集上高度過度擬合。每個梯度提升迭代都是使用訓練誤差作為目標變數來建立新樹,但僅當驗證資料的損失開始增加時,提升才會停止。當模型開始過度擬合時,驗證損失通常開始增加,這是停止構建更多樹的訊號。在這種情況下,由於驗證和訓練損失彼此不一致,因此模型似乎沒有"得到訊息"而導致過度擬合。這個配置只是為了完整而包含在內,並不是人們在實踐中應該使用的。
LightGBM具有經過調整的早期停止輪次,MSE→LightGBM訓練自定義訓練損失,並透過自定義驗證損失調整早期停止輪次最終模型使用自定義訓練和驗證損失。它透過相對較少的提升迭代次數給出最佳的非對稱MSE分數。損失與我們關心的一致!
讓我們仔細看看殘差直方圖以獲得更多細節。
請注意,使用LightGBM(即使使用預設的超引數),與隨機森林模型相比,預測效能也有所提高。具有自定義驗證損失的最終模型似乎在直方圖的右側進行更多預測,即實際值大於預測值。 這是由於不對稱的自定義損失函式的緣故。使用殘差的核密度圖可以更好地視覺化殘差的右側偏移。
LightGBM模型的預測與對稱和非對稱評估的比較
結論
沒有哪個模型是完美無缺的,但許多商業問題並沒有不平等的對待低估和過高估。有時,我們有意地希望我們的模型將錯誤偏向某個方向,但這具體取決於哪些錯誤的成本更高。因此,我們不應該限制自己使用普通ML庫中的"現成"對稱損失函式。
LightGBM提供了一個簡單的介面,可以合併自定義訓練和驗證損失函式。在適當的時候,我們應該利用這個功能來做出更好的預測。但是,你不應立即跳轉到使用自定義損失函式。最好採用精益的、迭代的方法,首先從像隨機森林這樣的簡單基線模型開始。在下一次迭代中,你可以移動更復雜的模型,如LightGBM,並進行超引數最佳化。只有在這些基線穩定後,才有必要進行自定義驗證和訓練損失函式。
更多推薦讀物
如果你不清楚一般梯度提升的工作原理,我建議你閱讀如何解釋Terence Parr的梯度提升(),以及Prince從頭開始的梯度提升()。
關於如何在不同的GBM框架中調整超引數,有很多文章。如果你想使用其中一個軟體包,可以花些時間瞭解要搜尋的超引數範圍。 LightGBM GitHub問題可以大致瞭解要使用的值範圍。() Aarshay Jainhas寫了一篇關於調整XGBoost和sklearn梯度提升的部落格。(https://www.analyticsvidhya.com/blog/2016/03/complete-guide-parameter-tuning-xgboost-with-codes-python/)
要了解哪種梯度提升包適合你的情況,請閱讀Alvira Swalin的CatBoost vs. Light GBM vs. XGBoost(),以及Pranjan Khandelwal的哪種演算法取得冠軍:Light GBM vs XGBOOST?。(https://www.analyticsvidhya.com/blog/2017/06/which-algorithm-takes-the-crown-light-gbm-vs-xgboost/)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31545819/viewspace-2215639/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- TensorFlow筆記-06-神經網路優化-損失函式,自定義損失函式,交叉熵筆記神經網路優化函式熵
- 邏輯迴歸:損失函式與梯度下降邏輯迴歸函式梯度
- tensorflow2 自定義損失函式使用的隱藏坑函式
- 損失函式函式
- Ignite自定義函式注意事項函式
- 3D高斯損失函式(1)單純損失函式3D函式
- 簡單介紹tensorflow2 自定義損失函式使用的隱藏坑函式
- Pytorch 常用損失函式PyTorch函式
- 損失函式綜述函式
- Triplet Loss 損失函式函式
- 例項解釋NLLLoss損失函式與CrossEntropyLoss損失函式的關係函式ROS
- DDMP中的損失函式函式
- PyTorch:損失函式loss functionPyTorch函式Function
- Pytorch中的損失函式PyTorch函式
- TensorFlow損失函式專題函式
- 請教,blade模板中怎麼呼叫自定義的函式?函式
- shell自定義函式函式
- Oracle 自定義函式Oracle函式
- SSD的損失函式設計函式
- 談談交叉熵損失函式熵函式
- 邏輯迴歸 損失函式邏輯迴歸函式
- Hive常用函式及自定義函式Hive函式
- 聊聊損失函式1. 噪聲魯棒損失函式簡析 & 程式碼實現函式
- python教程:自定義函式Python函式
- Hive中自定義函式Hive函式
- hive 3.0.0自定義函式Hive函式
- java自定義equals函式和hashCode函式Java函式
- 2.3邏輯迴歸損失函式邏輯迴歸函式
- 詳解常見的損失函式函式
- PHP 自定義函式用法及常用函式集合PHP函式
- Hive函式(內建函式+自定義標準函式UDF)Hive函式
- FlinkSQL自定義函式開發SQL函式
- Laravel自定義輔助函式Laravel函式
- Laravel 新增自定義助手函式Laravel函式
- laravel 自定義全域性函式Laravel函式
- Laravel 自定義函式存放位置Laravel函式
- 【Flutter 專題】124 日常問題小結 (三) 自定義 Dialog 二三事Flutter
- 焦點損失函式 Focal Loss 與 GHM函式