<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
類別型特徵(categorical feature)主要是指職業,血型等在有限類別內取值的特徵。它的原始輸入通常是字串形式,大多數演演算法模型不接受數值型特徵的輸入,針對數值型的類別特徵會被當成數值型特徵,從而造成訓練的模型產生錯誤。
今天在本文中,我將給大家分享16種類別特徵處理方法
Label Encoding是使用字典的方式,將每個類別標籤與不斷增加的整數相關聯,即生成一個名為class_的範例陣列的索引。
Scikit-learn中的LabelEncoder是用來對分型別特徵值進行編碼,即對不連續的數值或文字進行編碼。其中包含以下常用方法:
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() city_list = ["paris", "paris", "tokyo", "amsterdam"] le.fit(city_list) print(le.classes_) # 輸出為:['amsterdam' 'paris' 'tokyo'] city_list_le = le.transform(city_list) # 進行Encode print(city_list_le) # 輸出為:[1 1 2 0] city_list_new = le.inverse_transform(city_list_le) # 進行decode print(city_list_new) # 輸出為:['paris' 'paris' 'tokyo' 'amsterdam']
多列資料編碼方式:
import pandas as pd from sklearn.preprocessing import LabelEncoder df = pd.DataFrame({ 'pets': ['cat', 'dog', 'cat', 'monkey', 'dog', 'dog'], 'owner': ['Champ', 'Ron', 'Brick', 'Champ', 'Veronica', 'Ron'], 'location': ['San_Diego', 'New_York', 'New_York', 'San_Diego', 'San_Diego', 'New_York'] }) d = {} le = LabelEncoder() cols_to_encode = ['pets', 'owner', 'location'] for col in cols_to_encode: df_train[col] = le.fit_transform(df_train[col]) d[col] = le.classes_
Pandas的factorize()可以將Series中的標稱型資料對映稱為一組數位,相同的標稱型對映為相同的數位。factorize函數的返回值是一個tuple(元組),元組中包含兩個元素。第一個元素是一個array,其中的元素是標稱型元素對映為的數位;第二個元素是Index型別,其中的元素是所有標稱型元素,沒有重複。
import numpy as np import pandas as pd df = pd.DataFrame(['green','bule','red','bule','green'],columns=['color']) pd.factorize(df['color']) #(array([0, 1, 2, 1, 0], dtype=int64),Index(['green', 'bule', 'red'], dtype='object')) pd.factorize(df['color'])[0] #array([0, 1, 2, 1, 0], dtype=int64) pd.factorize(df['color'])[1] #Index(['green', 'bule', 'red'], dtype='object')
Label Encoding只是將文字轉化為數值,並沒有解決文字特徵的問題:所有的標籤都變成了數位,演演算法模型直接將根據其距離來考慮相似的數位,而不考慮標籤的具體含義。使用該方法處理後的資料適合支援類別性質的演演算法模型,如LightGBM。
Ordinal Encoding即最為簡單的一種思路,對於一個具有m個category的Feature,我們將其對應地對映到 [0,m-1] 的整數。當然 Ordinal Encoding 更適用於 Ordinal Feature,即各個特徵有內在的順序。例如對於”學歷”這樣的類別,”學士”、”碩士”、”博士” 可以很自然地編碼成 [0,2],因為它們內在就含有這樣的邏輯順序。但如果對於“顏色”這樣的類別,“藍色”、“綠色”、“紅色”分別編碼成[0,2]是不合理的,因為我們並沒有理由認為“藍色”和“綠色”的差距比“藍色”和“紅色”的差距對於特徵的影響是不同的。
ord_map = {'Gen 1': 1, 'Gen 2': 2, 'Gen 3': 3, 'Gen 4': 4, 'Gen 5': 5, 'Gen 6': 6} df['GenerationLabel'] = df['Generation'].map(gord_map)
在實際的機器學習的應用任務中,特徵有時候並不總是連續值,有可能是一些分類值,如性別可分為male和female。在機器學習任務中,對於這樣的特徵,通常我們需要對其進行特徵數位化,比如有如下三個特徵屬性:
對於某一個樣本,如[“male”,”US”,”Internet Explorer”],我們需要將這個分類值的特徵數位化,最直接的方法,我們可以採用序列化的方式:[0,1,3]。但是,即使轉化為數位表示後,上述資料也不能直接用在我們的分類器中。因為,分類器往往預設資料是連續的,並且是有序的。按照上述的表示,數位並不是有序的,而是隨機分配的。這樣的特徵處理並不能直接放入機器學習演演算法中。
為了解決上述問題,其中一種可能的解決方法是採用獨熱編碼(One-Hot Encoding)。獨熱編碼,又稱為一位有效編碼。其方法是使用N位狀態暫存器來對N個狀態進行編碼,每個狀態都由他獨立的暫存器位,並且在任意時候,其中只有一位有效。可以這樣理解,對於每一個特徵,如果它有m個可能值,那麼經過獨熱編碼後,就變成了m個二元特徵。並且,這些特徵互斥,每次只有一個啟用。因此,資料會變成稀疏的。
對於上述的問題,性別的屬性是二維的,同理,地區是三維的,瀏覽器則是四維的,這樣,我們可以採用One-Hot編碼的方式對上述的樣本[“male”,”US”,”Internet Explorer”]編碼,male則對應著[1,0],同理US對應著[0,1,0],Internet Explorer對應著[0,0,0,1]。則完整的特徵數位化的結果為:[1,0,0,1,0,0,0,0,1]。
為什麼能使用One-Hot Encoding?
使用one-hot編碼,將離散特徵的取值擴充套件到了歐式空間,離散特徵的某個取值就對應歐式空間的某個點。在迴歸,分類,聚類等機器學習演演算法中,特徵之間距離的計算或相似度的計算是非常重要的,而我們常用的距離或相似度的計算都是在歐式空間的相似度計算,計算餘弦相似性,也是基於的歐式空間。
將離散型特徵使用one-hot編碼,可以會讓特徵之間的距離計算更加合理。比如,有一個離散型特徵,代表工作型別,該離散型特徵,共有三個取值,不使用one-hot編碼,計算出來的特徵的距離是不合理。那如果使用one-hot編碼,顯得更合理。
獨熱編碼優缺點
優點:獨熱編碼解決了分類器不好處理屬性資料的問題,在一定程度上也起到了擴充特徵的作用。它的值只有0和1,不同的型別儲存在垂直的空間。
缺點:當類別的數量很多時,特徵空間會變得非常大。在這種情況下,一般可以用PCA(主成分分析)來減少維度。而且One-Hot Encoding+PCA這種組合在實際中也非常有用。
One-Hot Encoding的使用場景
獨熱編碼用來解決類別型資料的離散值問題。將離散型特徵進行one-hot編碼的作用,是為了讓距離計算更合理,但如果特徵是離散的,並且不用one-hot編碼就可以很合理的計算出距離,那麼就沒必要進行one-hot編碼,比如,該離散特徵共有1000個取值,我們分成兩組,分別是400和600,兩個小組之間的距離有合適的定義,組內的距離也有合適的定義,那就沒必要用one-hot 編碼。
基於樹的方法是不需要進行特徵的歸一化,例如隨機森林,bagging 和 boosting等。對於決策樹來說,one-hot的本質是增加樹的深度,決策樹是沒有特徵大小的概念的,只有特徵處於他分佈的哪一部分的概念。
基於Scikit-learn 的one hot encoding
LabelBinarizer:將對應的資料轉換為二進位制型,類似於onehot編碼,這裡有幾點不同:
from sklearn.preprocessing import LabelBinarizer lb = LabelBinarizer() city_list = ["paris", "paris", "tokyo", "amsterdam"] lb.fit(city_list) print(lb.classes_) # 輸出為:['amsterdam' 'paris' 'tokyo'] city_list_le = lb.transform(city_list) # 進行Encode print(city_list_le) # 輸出為: # [[0 1 0] # [0 1 0] # [0 0 1] # [1 0 0]] city_list_new = lb.inverse_transform(city_list_le) # 進行decode print(city_list_new) # 輸出為:['paris' 'paris' 'tokyo' 'amsterdam']
OneHotEncoder只能對數值型資料進行處理,需要先將文字轉化為數值(Label encoding)後才能使用,只接受2D陣列:
import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OneHotEncoder def LabelOneHotEncoder(data, categorical_features): d_num = np.array([]) for f in data.columns: if f in categorical_features: le, ohe = LabelEncoder(), OneHotEncoder() data[f] = le.fit_transform(data[f]) if len(d_num) == 0: d_num = np.array(ohe.fit_transform(data[[f]])) else: d_num = np.hstack((d_num, ohe.fit_transform(data[[f]]).A)) else: if len(d_num) == 0: d_num = np.array(data[[f]]) else: d_num = np.hstack((d_num, data[[f]])) return d_num df = pd.DataFrame([ ['green', 'Chevrolet', 2017], ['blue', 'BMW', 2015], ['yellow', 'Lexus', 2018], ]) df.columns = ['color', 'make', 'year'] df_new = LabelOneHotEncoder(df, ['color', 'make', 'year'])
基於Pandas的one hot encoding
其實如果我們跳出 scikit-learn, 在 pandas 中可以很好地解決這個問題,用 pandas 自帶的get_dummies函數即可
import pandas as pd df = pd.DataFrame([ ['green', 'Chevrolet', 2017], ['blue', 'BMW', 2015], ['yellow', 'Lexus', 2018], ]) df.columns = ['color', 'make', 'year'] df_processed = pd.get_dummies(df, prefix_sep="_", columns=df.columns[:-1]) print(df_processed)
get_dummies的優勢在於:
get_dummies雖然有這麼多優點,但畢竟不是 sklearn 裡的transformer型別,所以得到的結果得手動輸入到 sklearn 裡的相應模組,也無法像 sklearn 的transformer一樣可以輸入到pipeline中進行流程化地機器學習過程。
將類別特徵替換為訓練集中的計數(一般是根據訓練集來進行計數,屬於統計編碼的一種,統計編碼,就是用類別的統計特徵來代替原始類別,比如類別A在訓練集中出現了100次則編碼為100)。這個方法對離群值很敏感,所以結果可以歸一化或者轉換一下(例如使用對數變換)。未知類別可以替換為1。
頻數編碼使用頻次替換類別。有些變數的頻次可能是一樣的,這將導致碰撞。儘管可能性不是非常大,沒法說這是否會導致模型退化,不過原則上我們不希望出現這種情況。
import pandas as pd data_count = data.groupby('城市')['城市'].agg({'頻數':'size'}).reset_index() data = pd.merge(data, data_count, on = '城市', how = 'left')
目標編碼(target encoding),亦稱均值編碼(mean encoding)、似然編碼(likelihood encoding)、效應編碼(impact encoding),是一種能夠對高基數(high cardinality)自變數進行編碼的方法 (Micci-Barreca 2001) 。
如果某一個特徵是定性的(categorical),而這個特徵的可能值非常多(高基數),那麼目標編碼(Target encoding)是一種高效的編碼方式。在實際應用中,這類特徵工程能極大提升模型的效能。
一般情況下,針對定性特徵,我們只需要使用sklearn的OneHotEncoder或LabelEncoder進行編碼。
LabelEncoder能夠接收不規則的特徵列,並將其轉化為從0到n-1的整數值(假設一共有n種不同的類別);OneHotEncoder則能通過啞編碼,製作出一個m*n的稀疏矩陣(假設資料一共有m行,具體的輸出矩陣格式是否稀疏可以由sparse引數控制)。
定性特徵的基數(cardinality)指的是這個定性特徵所有可能的不同值的數量。在高基數(high cardinality)的定性特徵面前,這些資料預處理的方法往往得不到令人滿意的結果。
高基數定性特徵的例子:IP地址、電子郵件域名、城市名、家庭住址、街道、產品號碼。
主要原因:
如果某個類別型特徵基數比較低(low-cardinality features),即該特徵的所有值去重後構成的集合元素個數比較少,一般利用One-hot編碼方法將特徵轉為數值型。One-hot編碼可以在資料預處理時完成,也可以在模型訓練的時候完成,從訓練時間的角度,後一種方法的實現更為高效,CatBoost對於基數較低的類別型特徵也是採用後一種實現。
顯然,在高基數類別型特徵(high cardinality features) 當中,比如 user ID,這種編碼方式會產生大量新的特徵,造成維度災難。一種折中的辦法是可以將類別分組成有限個的群體再進行One-hot編碼。一種常被使用的方法是根據目標變數統計(Target Statistics,以下簡稱TS)進行分組,目標變數統計用於估算每個類別的目標變數期望值。甚至有人直接用TS作為一個新的數值型變數來代替原來的類別型變數。重要的是,可以通過對TS數值型特徵的閾值設定,基於對數損失、基尼係數或者均方差,得到一個對於訓練集而言將類別一分為二的所有可能劃分當中最優的那個。在LightGBM當中,類別型特徵用每一步梯度提升時的梯度統計(Gradient Statistics,以下簡稱GS)來表示。雖然為建樹提供了重要的資訊,但是這種方法有以下兩個缺點:
為了克服這些缺點,LightGBM以損失部分資訊為代價將所有的長尾類別歸為一類,作者聲稱這樣處理高基數類別型特徵時比One-hot編碼還是好不少。不過如果採用TS特徵,那麼對於每個類別只需要計算和儲存一個數位。因此,採用TS作為一個新的數值型特徵是最有效、資訊損失最小的處理類別型特徵的方法。TS也被廣泛應用在點選預測任務當中,這個場景當中的類別型特徵有使用者、地區、廣告、廣告發布者等。接下來我們著重討論TS,暫時將One-hot編碼和GS放一邊。
以下是計算公式:
其中 n 代表的是該某個特徵取值的個數,
代表某個特徵取值下正Label的個數,mdl為一個最小閾值,樣本數量小於此值的特徵類別將被忽略,prior是Label的均值。注意,如果是處理迴歸問題的話,可以處理成相應該特徵下label取值的average/max。對於k分類問題,會生成對應的k-1個特徵。
此方法同樣容易引起過擬合,以下方法用於防止過擬合:
目標編碼屬於有監督的編碼方式,如果運用得當則能夠有效地提高預測模型的準確性 (Pargent, Bischl, and Thomas 2019) ;而這其中的關鍵,就是在編碼的過程中引入正則化,避免過擬合問題。
例如類別A對應的標籤1有200個,標籤2有300個,標籤3有500個,則可以編碼為:2/10,3/10,3/6。中間最重要的是如何避免過擬合(原始的target encoding直接對全部的訓練集資料和標籤進行編碼,會導致得到的編碼結果太過依賴與訓練集),常用的解決方法是使用2 levels of cross-validation求出target mean,思路如下:
比如劃分為10折,每次對9折進行標籤編碼然後用得到的標籤編碼模型預測第10折的特徵得到結果,其實就是常說的均值編碼。
目標編碼嘗試對分類特徵中每個級別的目標總體平均值進行測量。這意味著,當每個級別的資料更少時,估計的均值將與“真實”均值相距更遠,方差更大。
from category_encoders import TargetEncoder import pandas as pd from sklearn.datasets import load_boston # prepare some data bunch = load_boston() y_train = bunch.target[0:250] y_test = bunch.target[250:506] X_train = pd.DataFrame(bunch.data[0:250], columns=bunch.feature_names) X_test = pd.DataFrame(bunch.data[250:506], columns=bunch.feature_names) # use target encoding to encode two categorical features enc = TargetEncoder(cols=['CHAS', 'RAD']) # transform the datasets training_numeric_dataset = enc.fit_transform(X_train, y_train) testing_numeric_dataset = enc.transform(X_test)
Kaggle競賽Avito Demand Prediction Challenge 第14名的solution分享:14th Place Solution: The Almost Golden Defenders。和target encoding 一樣,beta target encoding 也採用 target mean value (among each category) 來給categorical feature做編碼。不同之處在於,為了進一步減少target variable leak,beta target encoding發生在在5-fold CV內部,而不是在5-fold CV之前:
同時beta target encoding 加入了smoothing term,用 bayesian mean 來代替mean。Bayesian mean (Bayesian average) 的思路:某一個category如果資料量較少(<N_min),noise就會比較大,需要補足資料,達到smoothing 的效果。補足資料值 = prior mean。N_min 是一個regularization term,N_min 越大,regularization效果越強。
另外,對於target encoding和beta target encoding,不一定要用target mean (or bayesian mean),也可以用其他的統計值包括 medium, frqequency, mode, variance, skewness, and kurtosis — 或任何與target有correlation的統計值。
# train -> training dataframe # test -> test dataframe # N_min -> smoothing term, minimum sample size, if sample size is less than N_min, add up to N_min # target_col -> target column # cat_cols -> categorical colums # Step 1: fill NA in train and test dataframe # Step 2: 5-fold CV (beta target encoding within each fold) kf = KFold(n_splits=5, shuffle=True, random_state=0) for i, (dev_index, val_index) in enumerate(kf.split(train.index.values)): # split data into dev set and validation set dev = train.loc[dev_index].reset_index(drop=True) val = train.loc[val_index].reset_index(drop=True) feature_cols = [] for var_name in cat_cols: feature_name = f'{var_name}_mean' feature_cols.append(feature_name) prior_mean = np.mean(dev[target_col]) stats = dev[[target_col, var_name]].groupby(var_name).agg(['sum', 'count'])[target_col].reset_index() ### beta target encoding by Bayesian average for dev set df_stats = pd.merge(dev[[var_name]], stats, how='left') df_stats['sum'].fillna(value = prior_mean, inplace = True) df_stats['count'].fillna(value = 1.0, inplace = True) N_prior = np.maximum(N_min - df_stats['count'].values, 0) # prior parameters dev[feature_name] = (prior_mean * N_prior + df_stats['sum']) / (N_prior + df_stats['count']) # Bayesian mean ### beta target encoding by Bayesian average for val set df_stats = pd.merge(val[[var_name]], stats, how='left') df_stats['sum'].fillna(value = prior_mean, inplace = True) df_stats['count'].fillna(value = 1.0, inplace = True) N_prior = np.maximum(N_min - df_stats['count'].values, 0) # prior parameters val[feature_name] = (prior_mean * N_prior + df_stats['sum']) / (N_prior + df_stats['count']) # Bayesian mean ### beta target encoding by Bayesian average for test set df_stats = pd.merge(test[[var_name]], stats, how='left') df_stats['sum'].fillna(value = prior_mean, inplace = True) df_stats['count'].fillna(value = 1.0, inplace = True) N_prior = np.maximum(N_min - df_stats['count'].values, 0) # prior parameters test[feature_name] = (prior_mean * N_prior + df_stats['sum']) / (N_prior + df_stats['count']) # Bayesian mean # Bayesian mean is equivalent to adding N_prior data points of value prior_mean to the data set. del df_stats, stats # Step 3: train model (K-fold CV), get oof predictio
M-Estimate Encoding 相當於 一個簡化版的Target Encoding:
其中
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45