Alink漫談(九) :特徵工程 之 特徵雜湊/標準化縮放
阿新 • • 發佈:2020-07-04
# Alink漫談(九) :特徵工程之特徵雜湊/標準化縮放
[Toc]
## 0x00 摘要
Alink 是阿里巴巴基於實時計算引擎 Flink 研發的新一代機器學習演算法平臺,是業界首個同時支援批式演算法、流式演算法的機器學習平臺。本文將剖析Alink “特徵工程” 部分對應程式碼實現。
## 0x01 相關概念
### 1.1 特徵工程
機器學習的特徵工程是將原始的輸入資料轉換成特徵,以便於更好的表示潛在的問題,並有助於提高預測模型準確性的過程。
找出合適的特徵是很困難且耗時的工作,它需要專家知識,而應用機器學習基本也可以理解成特徵工程。但是,特徵工程對機器學習模型的應用有很大影響,有句俗話叫做“資料和特徵決定了機器學習模型的效能上限”。
機器學習的輸入特徵包括幾種:
- 數值特徵:包括整形、浮點型等,可以有順序意義,或者無序資料。
- 分類特徵:如ID、性別等。
- 時間特徵:時間序列如月份、年份、季度、日期、小時等。
- 空間特徵:經緯度等,可以轉換成郵編,城市等。
- 文字特徵:文件,自然語言,語句等。
特徵工程處理技巧大概有:
- 分箱(Binning)
- 獨熱編碼(One-Hot Encoding)
- 特徵雜湊(Hashing Trick)
- 巢狀法(Embedding)
- 取對數(Log Transformation)
- 特徵縮放(Scaling)
- 標準化(Normalization)
- 特徵互動(Feature Interaction)
本文將為大家講解特徵縮放和特徵雜湊的實現。
### 1.2 特徵縮放(Scaling)
特徵縮放是一種用於標準化獨立變數或資料特徵範圍的方法。 在資料處理中,它也稱為資料標準化,並且通常在資料預處理步驟期間執行。特徵縮放可以將很大範圍的資料限定在指定範圍內。由於原始資料的值範圍變化很大,在一些機器學習演算法中,如果沒有標準化,目標函式將無法正常工作。 例如,大多數分類器按歐幾里德距離計算兩點之間的距離。 如果其中一個要素具有寬範圍的值,則距離將受此特定要素的控制。 因此,應對所有特徵的範圍進行歸一化,以使每個特徵大致與最終距離成比例。
應用特徵縮放的另一個原因是梯度下降與特徵縮放比沒有它時收斂得快得多。特徵縮放主要包括兩種:
- 最大最小縮放(Min-max Scaling)
- 標準化縮放(Standard(Z) Scaling)
### 1.3 特徵雜湊(Hashing Trick)
大多數機器學習演算法的輸入要求都是實數矩陣,將原始資料轉換成實數矩陣就是所謂的特徵工程,而特徵雜湊(feature hashing,也稱雜湊技巧,hashing trick)就是一種特徵工程技術。
特徵雜湊的目標就是將一個數據點轉換成一個向量 或者 把原始的高維特徵向量壓縮成較低維特徵向量,且儘量不損失原始特徵的表達能力。
特徵雜湊利用的是雜湊函式將原始資料轉換成指定範圍內的雜湊值,相比較獨熱模型具有很多優點,如支援線上學習,維度減小很多。
比如我們將梁山好漢進行特徵雜湊,以關勝為例:
> 姓 名:關勝
> 排 名:坐第5把交椅
> 籍 貫:運城(今山西省-運城市)
> 綽 號:大刀
> 武 器:青龍偃月刀
> 星 號:天勇星
> 相 貌:堂堂八尺五六身軀,細細三柳髭髯,兩眉入鬢,鳳眼朝天,面如重棗,脣若塗朱。
> 原 型:南宋初,劉豫任濟南知府,金軍攻濟南。劉豫受金人利誘,殺守將關勝,降金。這段故事被清陳忱加以演義,寫入了《水滸後傳》。此關勝可能就是小說中的原型。
> 出場回合:第063回
> 後 代:關鈴,在《說岳全傳》出場,岳雲的義弟。
上面都是原始的輸入資料,包括數值特徵,分類特徵,文字特徵等等,計算機無法識別,必須用特徵雜湊轉換成計算機可以識別的資料。
轉換之後如下(虛構,只是展示使用 ^_^):
```java
// 假設結果是一個 30000 大小的稀疏向量,下面格式是:"index":"value"
"725":"0.8223484445229384" //姓 名
"1000":"0.8444219609970856" //排 名
"4995":"-0.18307661612028242 " //籍 貫
"8049":"0.060151616110215377" //綽 號
"8517":"-0.7340742756048447 " //武 器
"26798":":-0.734299689415312" //星 號
"24390":"0.545435" //相 貌
"25083":"0.4543543" //原 型
"25435":"-0.243432" //出場回合
"25721":"-0.7340742756048447" //後 代
```
這樣關勝就變成了一個可以被程式處理的向量。
## 0x02 資料集
我們的資料集和示例程式碼都是從FTRLExample獲取到的。
首先看資料集。
```java
String schemaStr
= "id string, click string, dt string, C1 string, banner_pos int, site_id string, site_domain string, "
+ "site_category string, app_id string, app_domain string, app_category string, device_id string, "
+ "device_ip string, device_model string, device_type string, device_conn_type string, C14 int, C15 int, "
+ "C16 int, C17 int, C18 int, C19 int, C20 int, C21 int";
//打印出前面幾列看看
trainBatchData.firstN(5).print();
id|click|dt|C1|banner_pos|site_id|site_domain|site_category|app_id|app_domain|app_category|device_id|device_ip|device_model|device_type|device_conn_type|C14|C15|C16|C17|C18|C19|C20|C21
--|-----|--|--|----------|-------|-----------|-------------|------|----------|------------|---------|---------|------------|-----------|----------------|---|---|---|---|---|---|---|---
3199889859719711212|0|14102101|1005|0|1fbe01fe|f3845767|28905ebd|ecad2386|7801e8d9|07d7df22|a99f214a|cfa82746|c6263d8a|1|0|15708|320|50|1722|0|35|-1|79
3200127078337687811|0|14102101|1005|1|e5c60a05|7256c623|f028772b|ecad2386|7801e8d9|07d7df22|a99f214a|ffb0e59a|83ca6fdb|1|0|19771|320|50|2227|0|687|100075|48
3200382705425230287|1|14102101|1005|0|85f751fd|c4e18dd6|50e219e0|98fed791|d9b5648e|0f2161f8|a99f214a|f69683cc|f51246a7|1|0|20984|320|50|2371|0|551|-1|46
320073658191290816|0|14102101|1005|0|1fbe01fe|f3845767|28905ebd|ecad2386|7801e8d9|07d7df22|a99f214a|8e5b1a31|711ee120|1|0|15706|320|50|1722|0|35|100083|79
3200823995473818776|0|14102101|1005|0|f282ab5a|61eb5bc4|f028772b|ecad2386|7801e8d9|07d7df22|a99f214a|9cf693b4|8a4875bd|1|0|18993|320|50|2161|0|35|-1|157
```
## 0x03 示例程式碼
從示例程式碼可以看到,首先做特徵縮放,然後做特徵雜湊。
```java
String[] selectedColNames = new String[]{
"C1", "banner_pos", "site_category", "app_domain",
"app_category", "device_type", "device_conn_type",
"C14", "C15", "C16", "C17", "C18", "C19", "C20", "C21",
"site_id", "site_domain", "device_id", "device_model"};
String[] categoryColNames = new String[]{
"C1", "banner_pos", "site_category", "app_domain",
"app_category", "device_type", "device_conn_type",
"site_id", "site_domain", "device_id", "device_model"};
String[] numericalColNames = new String[]{
"C14", "C15", "C16", "C17", "C18", "C19", "C20", "C21"};
// setup feature engineering pipeline
Pipeline featurePipeline = new Pipeline()
.add( // 特徵縮放
new StandardScaler()
.setSelectedCols(numericalColNames) // 對Double型別的列做變換
)
.add( // 特徵雜湊
new FeatureHasher()
.setSelectedCols(selectedColNames)
.setCategoricalCols(categoryColNames)
.setOutputCol(vecColName)
.setNumFeatures(numHashFeatures)
);
// fit feature pipeline model
PipelineModel featurePipelineModel = featurePipeline.fit(trainBatchData);
```
## 0x04 標準化縮放 StandardScaler
StandardScaler的作用是把資料集的每一個特徵進行標準差(standard deviation)轉換 和/或 零均值化(zero mean)。transforms a dataset, normalizing each feature to have unit standard deviation and/or zero mean.
對於做特徵縮放的好處,網上文章說的挺好:
> 當x全為正或者全為負時,每次返回的梯度都只會沿著一個方向發生變化,即梯度變化的方向就會向圖中紅色箭頭所示,一會向上太多,一會向下太多。這樣就會使得權重收斂效率很低。
>
> 但當x正負數量“差不多”時,就能對梯度變化方向進行“修正”,加速了權重的收斂。
讓我們想想如果做標準化縮放,具體需要怎麼做:
- 需要把需要處理的列的means, stdEnv這樣的都計算出來,這就需要遍歷整個表。所以這個是訓練過程。
- 需要根據上述訓練出來的means, stdEnv等數值,遍歷整個表中的每個資料,應用means, stdEnv結果逐一計算。所以這個是mapper過程。
### 4.1 StandardScalerTrainBatchOp
StandardScalerTrainBatchOp 類做了標準化縮放相關工作。這裡只對數字型別的列做轉換。
```java
/* StandardScaler transforms a dataset, normalizing each feature to have unit standard deviation and/or zero mean. */
public class StandardScalerTrainBatchOp extends Batch