摘要: 去年年末的那段時間裡,看了很多天池大賽里面得高分的選手的算法思路,大概總結了有監督學習中的一些核心流程及重要細節
前言
去年年末的那段時間裡,看了很多天池大賽里面得高分的選手的算法思路,大概總結了有監督學習中的一些核心流程及重要細節:
- feature processing tricks
這個是老生常談的問題,但是我還是看到了一些不錯的點,比如根據high importance feature剔除高度缺失的cases這些等等
- single feature + crossing feature
交叉特徵組合原始特徵,可以顯著的提升auc,提高命中的準確程度,這邊除了FM,我們也可以在常規的算法中去實現這個trick
- 有監督學習架構思路
下面,我們來看看針對每個點,具體是如何實現的,及我們需要注意哪些相關的東西:
feature processing tricks
case and feature selection
我們在做模型訓練之前通常會對模型的feature做一些刪減,比如共線性檢驗,去除掉相似度過高的連續feature;比如變異度檢驗,去除掉一些數據變化差異過小的feature等等。然而,在常規的樣本處理中,我們通常只會根據初始的數據分佈去看,比如用戶在feature上缺失大於來某個閾值才回去剔除這個用戶;其實,在深入的思考一下這個問題,會發現,如果用戶在高重要性的feature缺失程度高去剔除才更合理一些,這樣想可能不是很清晰,這邊看下面這個feature flow:
針對uid來看,如果普通的統計的話,uid3的null的個數5個,uid5的null的個數4個,我們應該優先剔除uid3,再考慮剔除uid5,因為null過多的用戶所能提供的信息量會相對的少,會增大泛化誤差。
但如果我們提前知道,對於判斷label的能力,feature3>feature5>feature6>feature8>其他,那麼uid5在高重要性的缺失情況極度嚴重於uid3,所以我們應該優先剔除uid5,相對於上面一種情況,我們預先知道feature的重要性排序就顯得很重要了。關於如何判斷提供了幾種簡單的方法:
- 方差膨脹係數:我們認為,在數據歸一化之後,數據波動的更大的feature能夠提供的信息量相對而言也是更大的,舉個很明顯的例子,如果feature1全都是1的話,它對我們判斷用戶是否下單這樣結果毫無意義。
- 互信息:我一直認為,互信息是判斷feature重要性的非常好的方法。方差膨脹係數只單純了考慮feature本身的特徵,而互信息在考慮feature的同時也考慮了label之間的關係,H(X,Y) = H(X) - H(X/Y),這個信息量的公式很好的解釋了這一點。
- xgb's importance:如果互信息是方差膨脹係數的進階,那麼xgb's importance則是互信息的進階,在考慮label與feature之間的關係的時候,同時還考慮了feature與feature之間的關係,這樣得出來的重要性排序更加全面了一些。
除此之外:
- Logistic regression的params的參數
- Recursive feature elimination(遞歸參數選擇方法)
- Pearson Correlation
- Distance correlation
- Mean decrease impurity/Mean decrease accuracy
- …
諸如這樣的方法很多,需要根據數據的形式,目標變量的形式,時間成本,效率等等綜合考慮,這邊只是給大家梳理一下常規的方法,至於實際使用的情況,需要大家累積項目經驗。
null-feature treatment method
在空值或者異常值的處理上,基本上分為2個派別,要么剔除這個feature或者case,要么填充這個feature或者case,它們的缺點也顯而易見,隨意剔除會減少判斷的信息,如果數據較少的時候,會降低模型的效果;填充的話會造成困惑,到底是眾數?平均數?中位數?最大值?最小值?現在很多人的處理方法都是觀察數據的分佈,如果偏態分佈就考慮分位數填充,如果是正態分佈就考慮均值或者眾數填充,相對而言,這樣處理的時間成本會更高,而且很多時候解釋的說服力不是很強。
我在看了17年3月份JD的訂單預估賽,17年的天池工業賽等等的高分答案中,不得不說,有一個分箱的方法確實能夠提高0.5-1.5的auc,我之前思考過,可能存在的原因:
- 保存了原始的信息,沒有以填充或者刪除的方式改變真實的數據分佈
- 讓feature存在的形式更加合理,比如age這個字段,其實我們在乎的不是27或者28這樣的差別,而是90後,80後這樣的差別,如果不採取分箱的形式,一定程度上誇大了27與26之前的差異
- 在數據計算中,不僅僅加快了計算的速度而且消除了實際數據記錄中的隨機偏差,平滑了存儲過程中可能出現的噪音
這邊就直接給大家分享一下我的梳理:
這邊涉及到一個問題,連續數值特徵是否一定要切為離散特徵,建議綜合考慮以下幾個問題:a.所使用的算法是否為knn、svm這樣的距離計算的算法b.是否在實際業務中依賴於離散判斷c.連續數值特徵的實際意義是否支持離散化。如果以上問題都沒有問題的話,我建議優先考慮離散化連續特徵,在一定程度上,離散完的feature有更好的解釋意義。
single feature + crossing feature
我們在之前的FM理論解析及應用中提到過特徵交叉這個概念,當時的文章中緊接著通過矩陣的計算技巧:
構造了全部feature的C(n,2)的形式,後面追加了線性模型,這樣一定程度上可以提高分類算法的準確度。這是一個非常好的將低維特徵向高維轉化的方式,所以在我們其他算法的過程中也可以藉鑑這種思路,但是假設我們初始的feature量特別多,比如我在日常的CTR預估或者feature梳理的過程中,很容易就整理500以上的feature集合,如果僅考慮C(n,2)的形式的話,就有250 499個feature的新增組合,這個是不可能接受的,所以回到我們上面一節feature processing tricks中提到的case and feature selection就是一個非常好的解決辦法,我們可以先通過比如xgboost中的importance:
我這邊實際的畫出了我做下單概率預測時初始篩選完成後的417個feature經過xgboost初步分類後的importance,可以很明顯看前37,前53,前94個feature對應了三次importance的拐點,我們可以在這些拐點中選擇一個既能夠涵蓋絕大多數的信息量,又不會造成後續交叉特徵個數過多的值,比如我這邊選擇的是60,那麼我接下來 生成的新的的交叉feature就是30`59个,比不做处理下的417
*`208要小很多倍,而且相對而言不會減少很多的信息量。
整體的流程我這邊也畫出來了,希望能夠給大家一個比較清晰的認識:
可以看到,樣本cases在經過了最初的空值篩選及第一輪高重要性feature後的空值篩選後,就保持不變了,而特徵feature的篩選過程則貫穿了整個交叉特徵生成流。
bagging及stacking的思路架構
我相信在讀的各位,不論是機器學習從業者抑或是算法工程師甚至是其他研發工程師,一定看過類似如下的快速拖動的模塊流:
它相當於把每個功能封裝到一個固定的盒子中,當我們需要使用某個模塊的時候,進行模塊的操作,不需要的時候直接切斷模塊的流向即可,我們甚至可以空值每個模塊的var及bias的偏向程度,在bagging和stacking的思路框架中,我非常常用的就是類似這樣的思想:確定好我要進行的組合模塊的組合方式(stacking還是bagging還是blending),再確定這次為想要做的子模塊是什麼,在根據組合形式及子模塊細微調節每個子模塊。
首先,子模塊可以有哪些?
- svm分類/回歸
- logistic分類/回歸
- 神經網絡分類/回歸
- xgboost分類/回歸
- gbdt分類/回歸
- xgboost葉子節點index
- gbdt葉子節點index
- randomforest分類/回歸
- elastic net
除了這些,還有麼?當然,如果你願意的話,每一個你自己構造出來的分類或者回歸的single model都可以成為你bagging或者stacking或者blending之前的子模塊。
如何訓練子模塊?
這邊的方法可謂是多種多樣,百花齊放,很大程度上來講,你在天池也好,kaggle也好,你能前十還是前十開外決定因素是你的feature處理的好壞,但是你能拿第一還是第十很大程度上就是依賴你的子模塊構造及子模塊組合上。這邊給大家分享我最近看到的比較有意思的三個子模塊形式:
1. wepon的Large-Scale SVM
讀過我之前寫的SVM理論解析及python實現這篇文章的朋友應該還記得,我當時說過svm在10.7%的數據集中取得第一,算是傳統的機器學習方法中非常值得一學的算法,但是實際應用中,在處理大規模數據問題時存在訓練時間過長和內存空間需求過大的問題比較讓人頭疼,wepon同學採取的方法如下:
這種方法看似增加了計算複雜度,實際上是卻是減小的,假設原始訓練數據大小是n,則在原始數據上訓練的複雜度是o(n^2),將數據集n分成p份,則每份數據量是(n/p),每一份訓練一個子svm,複雜度是o((n/p)^2),全加起來o(n^2/p),複雜度比在原始數據上訓練減小了p倍。變向的解決了在量大的數據集合上使用svm,提高速度同時保證質量這個問題。論文支持建議參考Ensemble SVM。
我們分別來解釋一下左右的Dense features 和Spare Features。
首先,左側這塊很好理解,在上一次的文章中 ,我們已經講瞭如何利用xgboost或者gbdt獲得用戶的數據落在的每棵樹上面的葉子節點的index值:
如果有不清楚的同學,請回顧一下上次講的內容。
右側這塊分別寫了user preference 和video content,當然這是因為它是視頻公司的原因,在我實際的使用中,我用的是user preference 和item content,這裡的preference和content其實就是你個人信息及行為的向量化的形式。
最簡單的表示就是把你的基本信息和item信息先onehotencoding,再首尾相接成一個超長的vector,這就是一個稀疏的Spare Features。
當然除了這種粗暴的辦法,還有比如我們在若干天之前講過的深度學習下的電商商品推薦中的word2vec的技巧,先將所有的用戶隨機生成為我們需要的長度N維下一一對應的向量,在通過huffman編碼的形式找到每個item對應的Huffman樹子的唯一路徑,再通過在每個節點上生成一個logsitic分類的辦法,使得所有該路徑成立的概率最高,以此來修正我們最初隨便生成的N維向量,最後這個N維向量就可以看作是一個Spare Features。
還有麼?當然,我私下問了我之前在該公司任職的同學,他們還有一種思路就是劃分數據集到M個子集,每個子集上面生成一個xgboost,然後每個子集取xgboost的葉子節點,相當於把左側的Dense features複製了M份Dense features放在了右邊的Spare Features,最後會得到一個M+1個Dense features。實際使用起來的效果完全不比word2vec的結果差。
3.基於GRU的潛藏層
Domonkos Tikk和Alexandros Karatzoglou在《Session-based Recommendations with Recurrent Neural Networks》文章中提到了可以用循環神經網絡RNN來預估用戶的行為,如下圖:
我們可以清晰的看到,針對每個用戶Session1,他的行為由i1.1變化至i1.4其實是一個有序的過程,我們可以設計一個從i1.1—->i1.2,i1. 2—->i1.3,i1.3—->i1.4這樣的一個循環流程。同時在他的文章中還解釋了這樣的設計解決的兩個問題:
- the length of sessions can be very different
- breaking down into fragments
一來通過了首尾相接,解決不同用戶的session不同長度;二來通過了embedding layer,解決了不完整session下預測的可能。具體網絡設計如下:
模型的更新流可以參考下面:
我們只需要拿到每個用戶的item流下所對應的state即可,這state就包含了這個用前M次的操作潛藏信息,同時我們還可以隨意定義這個信息向量的長度,這個就可以看作用戶狀態向量,作為子模塊的輸出。
這個思路的缺點就是,要預測的基礎數據不存在時序性,效果極差。比如滴滴打車的下單過程,從登陸到打到車的時間最短在20s,最長在1分鐘,否則用戶就退出了app,這樣的情況下,時序性質就顯得格外薄弱,強行用這樣的RNN獲得的用戶屬性非常不存在代表性。
如何組合子模塊?
bagging
這個是我們Kaggle&TianChi分類問題相關純算法理論剖析就強調過的bagging的最簡單的形式,在每個子模塊的設計選擇過程中要盡可能的保證:
- low biase
- high var
也就是說子模塊可以適當的過擬合,增加子模型擬合準確程度,通過加權平均的時候可以降低泛化誤差
stacking
這個是我們Kaggle&TianChi分類問題相關純算法理論剖析就強調過的stacking的最簡單的形式,在每個子模塊1、子模塊2的設計選擇過程中要盡可能的保證:
- high biase
- low var
在子模塊3的時候,要保證:
- low biase
- high var
也就是說,在子模塊1,2的選擇中,我們需要保證可稍欠擬合,在子模塊3的擬合上再保證擬合的準確度及強度
blending
我們知道單個組合子模塊的結果不夠理想,如果想得到更好的結果,需要把很多單個子模塊的結果融合在一起:
這種方法也可以提高我們最後的預測的效果。
關於有監督學習的方法大概就梳理到這邊,最後希望能夠給一些新人同學對有監督的理解和實戰有一些幫助。
轉貼自: 數據分析網
留下你的回應
以訪客張貼回應