系統學習機器學習之特徵工程(二)--離散型特徵編碼方式:LabelEncoder、one-hot與啞變數*
轉自:https://www.cnblogs.com/lianyingteng/p/7792693.html
在機器學習問題中,我們通過訓練資料集學習得到的其實就是一組模型的引數,然後通過學習得到的引數確定模型的表示,最後用這個模型再去進行我們後續的預測分類等工作。在模型訓練過程中,我們會對訓練資料集進行抽象、抽取大量特徵,這些特徵中有離散型特徵也有連續型特徵。若此時你使用的模型是簡單模型(如LR),那麼通常我們會對連續型特徵進行離散化操作,然後再對離散的特徵,進行one-hot編碼或啞變數編碼。這樣的操作通常會使得我們模型具有較強的非線效能力。那麼這兩種編碼方式是如何進行的呢?它們之間是否有聯絡?又有什麼樣的區別?是如何提升模型的非線效能力的呢?下面我們一一介紹:
one-hot encoding
one-hot的基本思想:將離散型特徵的每一種取值都看成一種狀態,若你的這一特徵中有N個不相同的取值,那麼我們就可以將該特徵抽象成N種不同的狀態,one-hot編碼保證了每一個取值只會使得一種狀態處於“啟用態”,也就是說這N種狀態中只有一個狀態位值為1,其他狀態位都是0。舉個例子,假設我們以學歷為例,我們想要研究的類別為小學、中學、大學、碩士、博士五種類別,我們使用one-hot對其編碼就會得到:
dummy encoding
啞變數編碼直觀的解釋就是任意的將一個狀態位去除。還是拿上面的例子來說,我們用4個狀態位就足夠反應上述5個類別的資訊,也就是我們僅僅使用前四個狀態位 [0,0,0,0] 就可以表達博士了。只是因為對於一個我們研究的樣本,他已不是小學生、也不是中學生、也不是大學生、又不是研究生,那麼我們就可以預設他是博士,是不是。(額,當然他現實生活也可能上幼兒園,但是我們統計的樣本中他並不是,^-^)。所以,我們用啞變數編碼可以將上述5類表示成:
one-hot編碼和dummy編碼:區別與聯絡
通過上面的例子,我們可以看出它們的“思想路線”是相同的,只是啞變數編碼覺得one-hot編碼太羅嗦了(一些很明顯的事實還說的這麼清楚),所以它就很那麼很明顯的東西省去了。這種簡化不能說到底好不好,這要看使用的場景。下面我們以一個例子來說明:
假設我們現在獲得了一個模型,這裡自變數滿足(因為特徵是one-hot獲得的,所有隻有一個狀態位為1,其他都為了0,所以它們加和總是等於1),故我們可以用表示第三個特徵,將其帶入模型中,得到:
這時,我們就驚奇的發現和這兩個引數是等價的!那麼我們模型的穩定性就成了一個待解決的問題。這個問題這麼解決呢?有三種方法:
(1)使用正則化手段,將引數的選擇上加一個限制,就是選擇引數元素值小的那個作為最終引數,這樣我們得到的引數就唯一了,模型也就穩定了。
(2)把偏置項去掉,這時我們發現也可以解決同一個模型引數等價的問題。
因為有了bias項,所以和我們去掉bias項的模型是完全不同的模型,不存在引數等價的問題。
(3)再加上bias項的前提下,使用啞變數編碼代替one-hot編碼,這時去除了,也就不存在之前一種特徵可以用其他特徵表示的問題了。
總結:我們使用one-hot編碼時,通常我們的模型不加bias項 或者 加上bias項然後使用正則化手段去約束引數;當我們使用啞變數編碼時,通常我們的模型都會加bias項,因為不加bias項會導致固有屬性的丟失。
選擇建議:我感覺最好是選擇正則化 + one-hot編碼;啞變數編碼也可以使用,不過最好選擇前者。雖然啞變數可以去除one-hot編碼的冗餘資訊,但是因為每個離散型特徵各個取值的地位都是對等的,隨意取捨未免來的太隨意。
連續值的離散化為什麼會提升模型的非線效能力?
簡單的說,使用連續變數的LR模型,模型表示為公式(1),而使用了one-hot或啞變數編碼後的模型表示為公式(2)
式中表示連續型特徵,、、分別是離散化後在使用one-hot或啞變數編碼後的若干個特徵表示。這時我們發現使用連續值的LR模型用一個權值去管理該特徵,而one-hot後有三個權值管理了這個特徵,這樣使得引數管理的更加精細,所以這樣拓展了LR模型的非線效能力。
這樣做除了增強了模型的非線效能力外,還有什麼好處呢?這樣做了我們至少不用再去對變數進行歸一化,也可以加速引數的更新速度;再者使得一個很大權值管理一個特徵,拆分成了許多小的權值管理這個特徵多個表示,這樣做降低了特徵值擾動對模型為穩定性影響,也降低了異常資料對模型的影響,進而使得模型具有更好的魯棒性。
LabelEncoder
LabelEncoder是對不連續的數字或文字編號。
# LabelEncoder例子
1 # -*- coding: utf-8 -*-
2 from sklearn.preprocessing import LabelEncoder
3 le = LabelEncoder().fit([1,111,122,188,999])
4 le_transform = le.transform([999,122,111])
5 print(le_transform)
6 """
7 [4 2 1]
8 """