一文講解特徵工程 | 經典外文PPT及中文解析

  • 2019 年 10 月 28 日
  • 筆記

「More data beats clever algorithms, but better data beats more data.」——名人名言哈哈哈哈,更多的數據打敗聰明的演算法,更好的數據打敗更多的數據。

特徵工程

  • 數據科學最需要創意的方面。
  • 像對待其他任何創造性工作一樣對待特徵工程,例如喜劇表演:
  • 一起頭腦風暴
  • 創建特徵工程的模板/公式
  • 檢查/重新檢查以前的工作

類別特徵

  • 幾乎總是需要一些處理
  • 高基數類別特徵會導致非常稀疏的數據
  • 難以做缺失值插補

Onehot編碼

  • 對長度為K的數組進行K編碼。
  • 可以與大多數線性演算法一起使用
  • 刪除第一列可避免共線性(pd.get_dummies中有參數可以達到這個目的,其實就是用全0來表示一種類別其它都用1-0表示)
  • 稀疏格式對於記憶體友好(csr_matrix)
  • 大多數當前的處理方法都不能很好地對待缺失值,以及新數據中的新類別

一個簡單的例子

哈希編碼

  • 對固定長度的數組執行「 OneHot編碼」。(不同的hash編碼通過不同的演算法將類別映射為一個唯一的值,例如對於類別A通過hash編碼可能映射為qwe456這種6維序列,然後我們再去做onehot展開)
  • 避免極為稀疏的數據
  • 可能會引起碰撞(例如10000個類別用2位的hash編碼,很容易出現不同類別最終映射的hash值是相同的,此現象稱為碰撞—collisions)
  • 可以重複使用不同的哈希函數和袋結果,以降低準確性(意思應該是用不同的hash演算法得到不同的編碼值然後concat到一起盡量避免碰撞的發生)
  • 碰撞collisions通常會降低結果,但可能會改善結果(增強泛化性能)。
  • 優雅地處理新變數(例如:新的用戶代理)(新的類別重新hash然後合併即可)(關於hash編碼可見facebook對於文本的處理的那篇論文,忘了叫啥了,回頭補充在編碼的文章里好了)

一個簡單的例子

為每個類別變數賦予唯一的數字ID

  • 對於基於非線性樹的演算法很有用(僅限於lightgbm和catboost這類可以直接處理類別的演算法,xgb還是要進行別的處理)
  • 不增加維度
  • 將cat_var-> num_id映射隨機化,然後進行平均再訓練,以降低準確性。(沒看明白)

一個簡單的例子

計數編碼(頻率編碼)

  • 將類別特徵替換為訓練集中的計數(一般是根據訓練集來進行計數,屬於統計編碼的一種,統計編碼,就是用類別的統計特徵來代替原始類別,比如類別A在訓練集中出現了100次則編碼為100)
  • 對線性和非線性演算法均有用
  • 可能對異常值敏感
  • 可以添加對數轉換,可以很好地處理計數(主要是針對count編碼之後特徵分布不規則的問題和常規的處理不規則分布的連續特徵是一樣的方式)
  • 用'1'替換新數據中沒見過的類別(沒見過的類別如果有n個則編碼為n)
  • 可能會產生衝突:相同的編碼,不同的變數(不同類別出現次數一樣)

一個簡單的例子

LabelCount編碼(就是對count編碼進行排名)

  • 通過訓練集中的計數對分類變數進行排名
  • 對線性和非線性演算法均有用
  • 對異常值不敏感
  • 不會對不同的變數使用相同的編碼
  • 兩全其美

一個簡單的例子

目標編碼

  • 按目標變數的比例對分類變數進行編碼(二分類或回歸)(如果是多分類其實也可以編碼,例如類別A對應的標籤1有100個,標籤2有100個,標籤3有100個,則可以編碼為【1/3,1/3,1/3】)
  • 注意避免過擬合!(原始的target encoding直接對全部的訓練集數據和標籤進行編碼,會導致得到的編碼結果太過依賴與訓練集)
  • 堆疊形式:輸出平均的目標的單變數模型
  • 以交叉驗證的方式進行(一般會進行交叉驗證,比如劃分為10折,每次對9折進行標籤編碼然後用得到的標籤編碼模型預測第10折的特徵得到結果,其實就是常說的均值編碼)
  • 添加平滑以避免將變數編碼設置為0。(某些類別可能只包含部分的類別會出現0值,此時會進行拉普拉斯平滑,不過對於回歸則沒有這種問題)
  • 添加隨機雜訊以應對過擬合(我一般用交叉驗證不怎麼加雜訊)
  • 正確應用時:線性和非線性的最佳編碼

一個簡單的例子

類別的embedding

  • 使用神經網路根據分類變數創建密集的嵌入。
  • 將分類變數映射到歐幾里得空間
  • 更快的模型訓練。
  • 更少的記憶體開銷。
  • 可以提供比1熱編碼更好的精度。
  • Entity Embeddings of Categorical Variables(回頭補充到類別編碼的內容里)

一個簡單的例子

NaN編碼

  • 給NaN值一個明確的編碼,而不是忽略它
  • NaN值可以保存資訊
  • 注意避免過度擬合!
  • 僅當nan值在訓練集測試集中的NaN值是由相同的值引起的,或者當局部驗證證明它可以保留資訊時才使用(這裡涉及到缺失值的缺失原因,比如客戶處於某種不好的目的而故意不提供的情況下表示客戶的某種不良的潛在行為則可以統一使用)

一個簡單的例子

多項式編碼

  • 編碼分類變數之間的交互
  • 沒有交互作用的線性演算法無法解決XOR問題
  • 多項式編碼可以解決XOR
  • 擴展功能空間:使用FS,哈希和/或VW

其實就是做了類別交叉然後再使用其它的編碼方式來處理

一個簡單的例子

擴展編碼

  • 從單個變數創建多個類別變數
  • 一些高基數功能(例如用戶代理)在其中包含更多資訊:
  • is_mobile?
  • is_latest_version?
  • Operation_system
  • Browser_build
  • 等等。

kaggle的常見magic feature的產生方式,這裡需要人工思考和頭腦風暴的結果

一個簡單的例子

合併編碼

  • 將不同的分類變數映射到同一變數
  • 拼寫錯誤,職位描述略有不同,全名或縮寫
  • 真實數據混亂,自由文本尤其如此

其實就是數據預處理中把相同含義的類別統一用一個類別表示

一個簡單的例子

前面都是關於類別特徵的常見處理,下面是關於連續特徵的。

數值特徵

  • 可以更輕鬆地輸入演算法
  • 可以構成浮點數,計數,數字
  • 更容易做缺失值插補

四捨五入

  • 舍入數值變數
  • 保留數據的最重要特徵。
  • 有時精度太高只是噪音
  • 舍入變數可以視為分類變數
  • 可以在四捨五入之前應用對數轉換

當然要確保不損失資訊的情況下使用,比如kaggle ieee的欺詐比賽,不同精度的交易金額代表了不同國家。。。這就不能直接四捨五入了。

分箱

  • 將數值變數放入bin並使用bin-ID進行編碼
  • 可以通過分位數,均勻地務實地設置分箱,或使用模型找到最佳分箱
  • 可以與超出訓練集的範圍的變數正常配合

標準化

  • 將數字變數縮放到一定範圍
  • 標準(Z)縮放 standard scaler
  • MinMax 標準化
  • root scaling(這是啥。。。)
  • log 變換(log變換是box cox變換的特例)

缺失值插補

  • 估算缺失變數
  • 硬編碼可以與插補結合使用
  • 平均值:非常基礎
  • 中位數:對異常值更健壯
  • 忽略:只是忽略問題
  • 使用模型:會引入演算法偏差

(缺失值的處理是一門大學問,這裡寫的太簡單)

連續特徵的交互

  • 編碼數值變數之間的相互作用
  • 嘗試:減法,加法,乘法,除法(還有更騷的,指數。。。)
  • 使用:通過統計測試選擇特徵,或訓練模型特徵的重要性,用於特徵的篩選(這種方法很容易得到雜訊,所以雜訊特徵也要注意篩選掉)
  • 忽略:有時候違背直覺的計算反而可以顯著改善模型的訓練效果!

線性演算法的非線性編碼

  • 硬編碼非線性以改善線性演算法(hash、各類embedding等)
  • 多項式編碼
  • Leafcoding(隨機森林嵌入)(acebook的gbdt+lr這種思路)
  • 遺傳演算法(典型代表gplearn)
  • 局部線性嵌入,頻譜嵌入,t SNE (降維提取重要特徵)

按照行計算統計值

  • 在一行數據上創建統計資訊
  • NaN的數量,這個在拍拍貸的top解決方案上看到過,不過實際效果不穩定
  • 0的數量
  • 負值數量
  • 平均值,最大值,最小值,偏度等。

時間特徵

  • 時間特徵,例如日期,需要更好的局部驗證方案(如回測)
  • 容易在這裡犯錯誤
  • 能夠給模型效果帶來很多好的提升

投射到一個圓圈

  • 將單個要素(例如day_of_week)轉換為圓上的兩個坐標
  • 確保最大和最小之間的距離與最小和最小+1相同。
  • 用於day_of_week,day_of_month,hour_of_day等。

趨勢編碼,簡單說就是根據時間序列來計算某段時間的一些統計值,比如對總支出進行編碼,例如:在上周支出,在上個月支出,在去年支出。這個也是比較常見的方法。

事件編碼

  • 硬編碼分類功能,例如:date_3_days_before_holidays:1
  • 嘗試:國定假日,重大體育賽事,周末,每月的第一個星期六等。
  • 這些因素可能對消費行為產生重大影響。

空間編碼

  • 空間變數是對空間中的位置進行編碼的變數
  • 示例包括:GPS坐標,城市,國家/地區,地址
  • 克里格(這是啥。。。)
  • K-均值聚類
  • 原始緯度
  • 將城市轉換為經度
  • 在街道名稱中添加郵政編碼

位置編碼

  • 查找當前位置與重要地點之間的距離
  • 小城鎮繼承了附近大城市的某些文化/背景
  • 電話位置可以映射到附近的企業和超市

位置所反應出來的欺詐行為

  • 位置事件數據可以指示可疑行為
  • 不可能的旅行速度:在不同國家/地區同時進行多項交易
  • 花費在與住所或送貨地址不同的城鎮
  • 從未在同一地點消費

接下來是關於數據探索的一些資料:

數據探索

  • 數據探索可以發現數據品質問題,異常值,雜訊,要素工程構想,要素清理構想。
  • 可以使用:spyder,jupyter notebook,pandas
  • 嘗試簡單的統計資訊:最小值,最大值
  • 合併目標,以便找到資訊之間的相關性。

迭代/調試

  • 特徵工程是一個迭代過程:使您的管道適合於快速迭代。
  • 使用亞線性調試:輸出有關過程的中間資訊,進行偽記錄。
  • 使用允許快速實驗的工具與方法
  • 失敗的想法多於行之有效的想法

關於標籤的一些處理方法:

  • 可以將標籤/目標變數/因變數視為數據的特徵,反之亦然。
  • 對數轉換:y-> log(y + 1)| exp(y_pred)-1
  • 平方變換
  • Box-Cox變換
  • 創建一個分數,把二分類問題轉化為回歸問題。
  • 訓練回歸器預測測試集中不可用的特徵。

關於自然語言處理的一些方案,當然,很多方法類別特徵也是很合適的。

  • 可以使用來自分類功能的相同想法。
  • 深度學習(自動特徵工程)正在逐漸佔領這一領域,但是具有精心設計的特徵的淺層學習仍然具有競爭力。
  • 數據的稀疏性使您進入「維數的詛咒」
  • 很多挖掘出好特徵的機會:

重要的還是多實戰,多總結,就像打策略遊戲一樣(比如魔獸爭霸3),基本功要紮實,在實戰中形成自己的一套處理問題的風格,不要抄kernel,不要竊取別人的特徵,自己多思考和總結。