安全的關(guān)鍵.png)
使用這些基本 REST API 最佳實(shí)踐構(gòu)建出色的 API
上圖為大家熟悉的機(jī)器學(xué)習(xí)建模流程圖(擴(kuò)展閱讀:一文全覽機(jī)器學(xué)習(xí)建模流程(Python代碼)),整個(gè)建模流程非常重要的一步,是對(duì)于數(shù)據(jù)的預(yù)處理和特征工程,它很大程度決定了最后建模效果的好壞。
首先我們來(lái)了解一下『特征工程』。事實(shí)上大家在ShowMeAI的實(shí)戰(zhàn)系列文章?Python機(jī)器學(xué)習(xí)綜合項(xiàng)目-電商銷量預(yù)估[2]?和?Python機(jī)器學(xué)習(xí)綜合項(xiàng)目-電商銷量預(yù)估(進(jìn)階)[3]?中已經(jīng)看到了我們做了特征工程的處理。
如果我們對(duì)特征工程(feature engineering)做一個(gè)定義,那它指的是:利用領(lǐng)域知識(shí)和現(xiàn)有數(shù)據(jù),創(chuàng)造出新的特征,用于機(jī)器學(xué)習(xí)算法;可以手動(dòng)或自動(dòng)。
在業(yè)界有一個(gè)很流行的說(shuō)法:據(jù)與特征工程決定了模型的上限,改進(jìn)算法只不過(guò)是逼近這個(gè)上限而已。
這是因?yàn)?,在?shù)據(jù)建模上,『理想狀態(tài)』和『真實(shí)場(chǎng)景』是有差別的,很多時(shí)候原始數(shù)據(jù)并不是規(guī)矩干凈含義明確充分的形態(tài):
而特征工程處理,相當(dāng)于對(duì)數(shù)據(jù)做一個(gè)梳理,結(jié)合業(yè)務(wù)提取有意義的信息,以干凈整齊地形態(tài)進(jìn)行組織:特征工程有著非常重要的意義:
本篇內(nèi)容,ShowMeAI帶大家一起來(lái)系統(tǒng)學(xué)習(xí)一下特征工程,包括『1.特征類型』『2.數(shù)據(jù)清洗』『3.特征構(gòu)建』『4.特征變換』『5.特征選擇』等板塊內(nèi)容。
我們這里用最簡(jiǎn)單和常用的Titanic數(shù)據(jù)集給大家講解。
Titanic 數(shù)據(jù)集是非常適合數(shù)據(jù)科學(xué)和機(jī)器學(xué)習(xí)新手入門練習(xí)的數(shù)據(jù)集,數(shù)據(jù)集為1912年泰坦尼克號(hào)沉船事件中一些船員的個(gè)人信息以及存活狀況。我們可以根據(jù)數(shù)據(jù)集訓(xùn)練出合適的模型并預(yù)測(cè)新數(shù)據(jù)(測(cè)試集)中的存活狀況。
Titanic 數(shù)據(jù)集可以通過(guò) Seaborn 工具庫(kù)直接加載,如下代碼所示:
import pandas as pd
import numpy as np
import seaborn as sns
df_titanic = sns.load_dataset('titanic')
其中數(shù)據(jù)集的數(shù)據(jù)字段描述如下圖所示:
在具體演示 Titanic 的數(shù)據(jù)預(yù)處理與特征工程之前,ShowMeAI再給大家構(gòu)建一些關(guān)于數(shù)據(jù)的基礎(chǔ)知識(shí)。
數(shù)據(jù)可以分為『結(jié)構(gòu)化數(shù)據(jù)』和『非結(jié)構(gòu)化數(shù)據(jù)』,比如在互聯(lián)網(wǎng)領(lǐng)域,大部分存儲(chǔ)在數(shù)據(jù)庫(kù)內(nèi)的表格態(tài)業(yè)務(wù)數(shù)據(jù),都是結(jié)構(gòu)化數(shù)據(jù);而文本、語(yǔ)音、圖像視頻等就屬于非結(jié)構(gòu)化數(shù)據(jù)。
對(duì)于我們記錄到的數(shù)據(jù),我們通常又可以以『定量數(shù)據(jù)』和『定性數(shù)據(jù)』對(duì)齊進(jìn)行區(qū)分,其中:
實(shí)際數(shù)據(jù)挖掘或者建模之前,我們會(huì)有『數(shù)據(jù)預(yù)處理』環(huán)節(jié),對(duì)原始態(tài)的數(shù)據(jù)進(jìn)行數(shù)據(jù)清洗等操作處理。
因?yàn)楝F(xiàn)實(shí)世界中數(shù)據(jù)大體上都是不完整、不一致的『臟數(shù)據(jù)』,無(wú)法直接進(jìn)行數(shù)據(jù)挖掘,或者挖掘結(jié)果差強(qiáng)人意。
『臟數(shù)據(jù)』產(chǎn)生的主要成因包括:篡改數(shù)據(jù)、數(shù)據(jù)不完整、數(shù)據(jù)不一致、數(shù)據(jù)重復(fù)、異常數(shù)據(jù)等。
數(shù)據(jù)清洗過(guò)程包括『2.1 數(shù)據(jù)對(duì)齊』、『2.2 缺失值處理』、『2.3 異常值處理』『2.4 數(shù)據(jù)轉(zhuǎn)化等』數(shù)據(jù)處理方法。我們對(duì)這些處理方法做詳細(xì)講解。
采集到的原始數(shù)據(jù),格式形態(tài)不一,我們會(huì)對(duì)時(shí)間、字段以及相關(guān)量綱等進(jìn)行數(shù)據(jù)對(duì)齊處理,數(shù)據(jù)對(duì)齊和規(guī)整化之后的數(shù)據(jù)整齊一致,更加適合建模。如下圖為一些處理示例:
① 時(shí)間
2022-02-20
、20220220
、2022/02/20
、20/02/2022
〗。② 字段
③ 量綱
數(shù)據(jù)缺失是真實(shí)數(shù)據(jù)中常見(jiàn)的問(wèn)題,因?yàn)榉N種原因我們采集到的數(shù)據(jù)并不一定是完整的,我們有一些缺失值的常見(jiàn)處理方式。具體的處理方式可以展開(kāi)成圖:
下面回到我們的Titanic數(shù)據(jù)集,我們演示一下各種方法。我們先對(duì)數(shù)據(jù)集的缺失值情況做一個(gè)了解(匯總分布):
df_titanic.isnull().sum()
survived 0
pclass 0
sex 0
age 177
sibsp 0
parch 0
fare 0
embarked 2
class 0
who 0
adult_male 0
deck 688
embark_town 2
alive 0
alone 0
最直接粗暴的處理是剔除缺失值,即將存在遺漏信息屬性值的對(duì)象 (字段,樣本/記錄) 刪除,從而得到一個(gè)完備的信息表。優(yōu)缺點(diǎn)如下:
在我們當(dāng)前Titanic的案例中,embark_town
字段有 2 個(gè)空值,考慮刪除缺失處理下。
df_titanic[df_titanic["embark_town"].isnull()]
df_titanic.dropna(axis=0,how='any',subset=['embark_town'],inplace=True)
(2) 數(shù)據(jù)填充 · 缺失值處理
第2大類是我們可以通過(guò)一些方法去填充缺失值。比如基于統(tǒng)計(jì)方法、模型方法、結(jié)合業(yè)務(wù)的方法等進(jìn)行填充。
① 手動(dòng)填充。根據(jù)業(yè)務(wù)知識(shí)來(lái)進(jìn)行人工手動(dòng)填充。
② 特殊值填充。將空值作為一種特殊的屬性值來(lái)處理,它不同于其他的任何屬性值。如所有的空值都用unknown
填充。一般作為臨時(shí)填充或中間過(guò)程。
df_titanic['embark_town'].fillna('unknown', inplace=True)
③ 統(tǒng)計(jì)量填充。若缺失率較低,可以根據(jù)數(shù)據(jù)分布的情況進(jìn)行填充。常用填充統(tǒng)計(jì)量如下:
中位數(shù)填充——fare:缺失值較多,使用中位數(shù)填充。
df_titanic['fare'].fillna(df_titanic['fare'].median(), inplace=True)
眾數(shù)填充——embarked:只有兩個(gè)缺失值,使用眾數(shù)填充。
df_titanic['embarked'].isnull().sum()
#執(zhí)行結(jié)果:2
df_titanic['embarked'].fillna(df_titanic['embarked'].mode(), inplace=True)
df_titanic['embarked'].value_counts()
#執(zhí)行結(jié)果:
#S 64
同類均值填充。
age:根據(jù) sex、pclass 和 who 分組,如果落在相同的組別里,就用這個(gè)組別的均值或中位數(shù)填充。
df_titanic.groupby(['sex', 'pclass', 'who'])['age'].mean()
age_group_mean = df_titanic.groupby(['sex', 'pclass', 'who'])['age'].mean().reset_index()
def select_group_age_median(row):
condition = ((row['sex'] == age_group_mean['sex']) &
(row['pclass'] == age_group_mean['pclass']) &
(row['who'] == age_group_mean['who']))
return age_group_mean[condition]['age'].values[0]
df_titanic['age'] =df_titanic.apply(lambda x: select_group_age_median(x) if np.isnan(x['age']) else x['age'],axis=1)
④ 模型預(yù)測(cè)填充。如果其他無(wú)缺失字段豐富,我們也可以借助于模型進(jìn)行建模預(yù)測(cè)填充,將待填充字段作為L(zhǎng)abel,沒(méi)有缺失的數(shù)據(jù)作為訓(xùn)練數(shù)據(jù),建立分類/回歸模型,對(duì)待填充的缺失字段進(jìn)行預(yù)測(cè)并進(jìn)行填充。
我們以 Titanic 案例中的 age 字段為例,講解一下:
age 缺失量較大,這里我們用 sex、pclass、who、fare、parch、sibsp 六個(gè)特征構(gòu)建隨機(jī)森林模型,填充年齡缺失值。
df_titanic_age = df_titanic[['age', 'pclass', 'sex', 'who','fare', 'parch', 'sibsp']]
df_titanic_age = pd.get_dummies(df_titanic_age)
df_titanic_age.head()
# 乘客分成已知年齡和未知年齡兩部分
known_age = df_titanic_age[df_titanic_age.age.notnull()]
unknown_age = df_titanic_age[df_titanic_age.age.isnull()]
# y 即目標(biāo)年齡
y_for_age = known_age['age']
# X 即特征屬性值
X_train_for_age = known_age.drop(['age'], axis=1)
X_test_for_age = unknown_age.drop(['age'], axis=1)
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
rfr.fit(X_train_for_age, y_for_age)
# 用得到的模型進(jìn)行未知年齡結(jié)果預(yù)測(cè)
y_pred_age = rfr.predict(X_test_for_age)
# 用得到的預(yù)測(cè)結(jié)果填補(bǔ)原缺失數(shù)據(jù)
df_titanic.loc[df_titanic.age.isnull(), 'age'] = y_pred_age
sns.distplot(df_titanic.age)
⑤ 插值法填充。還可以用插值法對(duì)數(shù)據(jù)填充,細(xì)分一下包括線性插值、多重插補(bǔ)、熱平臺(tái)插補(bǔ)、拉格朗日插值、牛頓插值等。
線性插值法
使用插值法可以計(jì)算缺失值的估計(jì)值,所謂的插值法就是通過(guò)兩點(diǎn) , 估計(jì)中間點(diǎn)的值。假設(shè) 是一條直線,通過(guò)已知的兩點(diǎn)來(lái)計(jì)算函數(shù) ,然后只要知道 就能求出 ,以此方法來(lái)估計(jì)缺失值。
.interpolate(method = 'linear', axis)
方法將通過(guò)linear
插值使用沿著給定axis
的值替換 NaN 值,這個(gè)差值也就是前后或者上下的中間值
df_titanic['fare'].interpolate(method = 'linear', axis = 0)
同時(shí),也可用行值插入
df_titanic['fare'].interpolate(method = 'linear', axis = 1)
多重插補(bǔ)(Multiple Imputation)
多值插補(bǔ)的思想來(lái)源于貝葉斯估計(jì),認(rèn)為待插補(bǔ)的值是隨機(jī)的,它的值來(lái)自于已觀測(cè)到的值。具體實(shí)踐上通常是估計(jì)出待插補(bǔ)的值,然后再加上不同的噪聲,形成多組可選插補(bǔ)值。根據(jù)某種選擇依據(jù),選取最合適的插補(bǔ)值。 多重插補(bǔ)方法分為三個(gè)步驟:
⑥ 啞變量填充。有另外一種非常有意思的填充方式,叫做『?jiǎn)∽兞刻畛洹?,在變量為離散型,且不同值較少的情況下可以采用。以 Titanic 數(shù)據(jù)為例:
IS_SEX_MALE
、IS_SEX_FEMALE
、IS_SEX_NA
。other
,降低維度。此做法可最大化保留變量的信息。以下為參考代碼示例:
sex_list = ['MALE', 'FEMALE', np.NaN, 'FEMALE', 'FEMALE', np.NaN, 'MALE']
df = pd.DataFrame({'SEX': sex_list})
display(df)
df.fillna('NA', inplace=True)
df = pd.get_dummies(df['SEX'],prefix='IS_SEX')
display(df)
# 原始數(shù)據(jù)
SEX
0 MALE
1 FEMALE
2 NaN
3 FEMALE
4 FEMALE
5 NaN
6 MALE
# 填充后
IS_SEX_FEMALE IS_SEX_MALE IS_SEX_NA
0 0 1 0
1 1 0 0
2 0 0 1
3 1 0 0
4 1 0 0
5 0 0 1
6 0 1
當(dāng)特征值缺失超過(guò)??以上,建議刪除〖或加入『是』『否』標(biāo)記位信息〗,容易影響模型效果
df_titanic.drop(["deck"],axis=1)
數(shù)據(jù)質(zhì)量也會(huì)很大程度影響機(jī)器學(xué)習(xí)應(yīng)用效果,數(shù)據(jù)的錯(cuò)誤值或異常值可能會(huì)造成測(cè)量誤差或異常系統(tǒng)條件的結(jié)果,給模型學(xué)習(xí)帶來(lái)很大的問(wèn)題。實(shí)際我們很多時(shí)候會(huì)有異常值檢測(cè)與處理環(huán)節(jié),下面給大家做一個(gè)梳理。
① 基于統(tǒng)計(jì)分析。通常用戶用某個(gè)統(tǒng)計(jì)分布對(duì)數(shù)據(jù)點(diǎn)進(jìn)行建模,再以假定的模型,根據(jù)點(diǎn)的分布來(lái)確定是否異常。如通過(guò)分析統(tǒng)計(jì)數(shù)據(jù)的散度情況,即數(shù)據(jù)變異指標(biāo),對(duì)數(shù)據(jù)的分布情況有所了解,進(jìn)而通過(guò)數(shù)據(jù)變異指標(biāo)來(lái)發(fā)現(xiàn)數(shù)據(jù)中的異常點(diǎn)數(shù)據(jù)。
常用的數(shù)據(jù)變異指標(biāo)有極差、四分位數(shù)間距、均差、標(biāo)準(zhǔn)差、變異系數(shù)等等,如變異指標(biāo)的值大表示變異大、散布廣;值小表示離差小,較密集。
比如,最大最小值可以用來(lái)判斷這個(gè)變量的取值是否超過(guò)了合理的范圍,如客戶的年齡為 歲或 歲,為異常值。
② 3σ原則。如果數(shù)據(jù)近似正態(tài)分布,在??原則下,異常值為一組測(cè)定值中與平均值的偏差超過(guò)??倍標(biāo)準(zhǔn)差的值。
如果數(shù)據(jù)服從正態(tài)分布,距離平均值 之外的值出現(xiàn)的概率為 ,屬于極個(gè)別的小概率事件。 如果數(shù)據(jù)不服從正態(tài)分布,也可以用遠(yuǎn)離平均值的多少倍標(biāo)準(zhǔn)差來(lái)描述。
③ 箱線圖分析。大家還記得在數(shù)據(jù)分析部分有一個(gè)很有效的工具叫做箱線圖[6]。箱型圖判斷異常值的方法以四分位數(shù)和四分位距為基礎(chǔ),四分位數(shù)具有魯棒性,因此箱型圖識(shí)別異常值比較客觀,在識(shí)別異常值時(shí)有一定的優(yōu)越性。
箱線圖提供了識(shí)別異常值的一個(gè)標(biāo)準(zhǔn):如果一個(gè)值小于 或大于 的值,則被稱為異常值。
sns.catplot(y="fare",x="survived", kind="box", data=df_titanic,palette="Set2")
④ 基于模型檢測(cè)。我們也可以基于模型對(duì)異常值檢測(cè)?;舅悸肥窍冉⒁粋€(gè)數(shù)據(jù)模型,那些同模型不能完美擬合的對(duì)象就視作異常。如果模型是簇的集合,則異常是不顯著屬于任何簇的對(duì)象。在使用回歸模型時(shí),異常是相對(duì)遠(yuǎn)離預(yù)測(cè)值的對(duì)象。
⑤ 基于距離。我們還有基于距離的方法可以用于異常檢測(cè)。這類方法基于下面這個(gè)假設(shè):如果一個(gè)數(shù)據(jù)對(duì)象和大多數(shù)點(diǎn)距離都很遠(yuǎn),那這個(gè)對(duì)象就是異常。通過(guò)定義對(duì)象之間的臨近性度量,根據(jù)距離判斷異常對(duì)象是否遠(yuǎn)離其他對(duì)象,主要使用的距離度量方法有絕對(duì)距離(曼哈頓距離)、歐氏距離和馬氏距離等方法。
⑥ 基于密度。一個(gè)很直接的異常檢測(cè)思路是基于分布密度來(lái)做。具體為:考察當(dāng)前點(diǎn)周圍密度,局部異常點(diǎn)/離群點(diǎn)的局部密度顯著低于大部分近鄰點(diǎn)。這類方法適用于非均勻的數(shù)據(jù)集。
⑦ 基于聚類。我們可以基于聚類的方法進(jìn)行異常檢測(cè),遠(yuǎn)離 cluster 的樣本更可能是異常值。
不過(guò)該方法會(huì)受到聚類 cluster 個(gè)數(shù) 的影響,一種策略是對(duì)于不同的簇個(gè)數(shù)重復(fù)該分析;另一種方法是找出大量小簇,其想法是較小的簇傾向于更加凝聚。如果存在大量小簇時(shí)一個(gè)對(duì)象是異常點(diǎn),則它多半是一個(gè)真正的異常點(diǎn)。不利的一面是一組異常點(diǎn)可能形成小簇而逃避檢測(cè)。
⑧ 基于鄰近度的異常點(diǎn)檢測(cè)。同樣的,我們也有基于近鄰度的思路來(lái)做異常檢測(cè),我們認(rèn)為異常點(diǎn)遠(yuǎn)離大部分的點(diǎn)。這種方法比統(tǒng)計(jì)學(xué)方法更一般、更容易使用,因?yàn)榇_定數(shù)據(jù)集的有意義的鄰近性度量比確定它的統(tǒng)計(jì)分布更容易。
一個(gè)對(duì)象的異常點(diǎn)得分由到它的 最近鄰的距離給定,所以異常點(diǎn)得分對(duì) 的取值高度敏感:
在數(shù)據(jù)處理階段將離群點(diǎn)作為影響數(shù)據(jù)質(zhì)量的異常點(diǎn)考慮,而不是作為通常所說(shuō)的異常檢測(cè)目標(biāo)點(diǎn),一般采用較為簡(jiǎn)單直觀的方法,結(jié)合箱線圖和 MAD 的統(tǒng)計(jì)方法判斷變量的離群點(diǎn)。如下為繪制散點(diǎn)圖根據(jù)分布直接判斷。
sns.scatterplot(x="fare", y="age", hue="survived",data=df_titanic,palette="Set1")
對(duì)異常值處理,需要具體情況具體分析,異常值處理方法常用的有以下幾種:
前序的數(shù)據(jù)預(yù)處理過(guò)程能保證我們拿到干凈整齊準(zhǔn)確的數(shù)據(jù),但這些數(shù)據(jù)未必對(duì)于建模是最有效的,下一步我們通常會(huì)進(jìn)行特征構(gòu)建,結(jié)合業(yè)務(wù)場(chǎng)景產(chǎn)生衍生變量來(lái)提升數(shù)據(jù)表達(dá)能力和模型建模效果。
統(tǒng)計(jì)特征是一類非常有效的特征,尤其在時(shí)序問(wèn)題場(chǎng)景中,以下為統(tǒng)計(jì)特征構(gòu)建的一些思考維度和方法:
回到Titanic數(shù)據(jù)集,我們來(lái)看看結(jié)合業(yè)務(wù)理解,我們可以做哪些新特征:
① 年齡處理。我們對(duì)年齡 age 字段進(jìn)行進(jìn)一步處理,考慮到不同的年齡段對(duì)應(yīng)的人群可能獲救概率不同,我們根據(jù)年齡值分成不同區(qū)間段,對(duì)應(yīng)到 child、young、midlife、old 等
def age_bin(x):
if x <= 18:
return 'child'
elif x <= 30:
return 'young'
elif x <= 55:
return 'midlife'
else:
return 'old'
df_titanic['age_bin'] = df_titanic['age'].map(age_bin)
df_titanic['age_bin'].unique()
執(zhí)行結(jié)果:
array(['young', 'midlife', 'child', 'old'], dtype=object)
② 抽取『稱呼』特征。 我們?cè)?name 字段里,可以看到各種不同的稱呼,如『Mr』『Master』『Dr』等,這些稱呼體現(xiàn)了乘客的身份等信息,我們可以對(duì)其做抽取構(gòu)建新的特征。
# 提取稱呼
df_titanic['title'] = df_titanic['name'].map(lambda x: x.split(',')[1].split('.')[0].strip())
df_titanic['title'].value_counts()
執(zhí)行結(jié)果如下:
Mr 757
Miss 260
Mrs 197
Master 61
Rev 8
Dr 8
Col 4
Ms 2
Major 2
Mlle 2
Dona 1
Sir 1
Capt 1
Don 1
Lady 1
Mme 1
the Countess 1
Jonkheer 1
我們做一個(gè)簡(jiǎn)單的『稱呼』統(tǒng)計(jì)
# 對(duì)稱呼細(xì)分,是官員,還是皇室,還是女士、先生、小姐
df_titanic['title'].unique()
執(zhí)行結(jié)果:
array(['Mr', 'Mrs', 'Miss', 'Master', 'Don', 'Rev', 'Dr', 'Mme', 'Ms',
'Major', 'Lady', 'Sir', 'Mlle', 'Col', 'Capt', 'the Countess',
'Jonkheer', 'Dona'], dtype=object)
下面我們對(duì)這些『稱呼』『稱謂』做一個(gè)規(guī)范化統(tǒng)一。
title_dictionary = {
"Mr": "Mr",
"Mrs": "Mrs",
"Miss": "Miss",
"Master": "Master",
"Don": "Royalty",
"Rev": "Officer",
"Dr": "Officer",
"Mme": "Mrs",
"Ms": "Mrs",
"Major": "Officer",
"Lady": "Royalty",
"Sir": "Royalty",
"Mlle": "Miss",
"Col": "Officer",
"Capt": "Officer",
"the Countess": "Royalty",
"Jonkheer": "Royalty",
"Dona": 'Mrs'
}
df_titanic['title'] = df_titanic['title'].map(title_dictionary)
df_titanic['title'].value_counts()
執(zhí)行結(jié)果如下:
Mr 757
Miss 262
Mrs 201
Master 61
Officer 23
Royalty 5
③ 抽取家庭規(guī)模。在 Titanic 上,有的成員之間有親屬關(guān)系,考慮到家族大小對(duì)于最終是否獲救也有影響,我們可以構(gòu)建一個(gè)?family_size
?的特征,用于表征家庭規(guī)模。
df_titanic['family_size'] = df_titanic['sibsp'] + df_titanic['parch'] + 1
df_titanic['family_size'].head()
執(zhí)行結(jié)果如下:
0 2
1 2
2 1
3 2
4 1
在電商等場(chǎng)景下,數(shù)據(jù)有一定的周期規(guī)律,我們可以提取一些周期值作為有效信息。時(shí)序周期的一些考慮維度如下:
數(shù)據(jù)分桶,是對(duì)連續(xù)值屬性處理的一種常用方法,它指的是我們把連續(xù)數(shù)值切段,并把連續(xù)值歸屬到對(duì)應(yīng)的段中。數(shù)據(jù)分桶也叫做數(shù)據(jù)分箱或離散化。
① 等頻、等距分桶
(a) 自定義分箱。指根據(jù)業(yè)務(wù)經(jīng)驗(yàn)或者常識(shí)等自行設(shè)定劃分的區(qū)間,然后將原始數(shù)據(jù)歸類到各個(gè)區(qū)間中。
(b) 等距分箱。按照相同寬度將數(shù)據(jù)分成幾等份。
從最小值到最大值之間,均分為 等份。如果 、 為最小最大值,則每個(gè)區(qū)間的長(zhǎng)度為 ,區(qū)間邊界值為 、、、。
等距分箱只考慮邊界,每個(gè)等份里面的實(shí)例數(shù)量可能不等。等距分桶的缺點(diǎn)是受到異常值的影響比較大。
(c) 等頻分箱。將數(shù)據(jù)分成幾等份,每等份數(shù)據(jù)里面的個(gè)數(shù)是一樣的。
在等頻分箱中,區(qū)間的邊界值要經(jīng)過(guò)計(jì)算獲得,最終每個(gè)區(qū)間包含大致相等的實(shí)例數(shù)量。比如說(shuō) ,每個(gè)區(qū)間應(yīng)該包含大約 的實(shí)例。
數(shù)值變量分箱。我們先對(duì)船票價(jià)格做一個(gè)等頻切分(大家如果對(duì)船票價(jià)格進(jìn)行分布繪圖,會(huì)發(fā)現(xiàn)是很長(zhǎng)尾的分布,并不適合等距切分),看看分開(kāi)的區(qū)間段。
# qcut 等頻率分箱
df_titanic['fare_bin'], bins = pd.qcut(df_titanic['fare'], 5, retbins=True)
df_titanic['fare_bin'].value_counts()
結(jié)果如下:
(7.854, 10.5] 184
(21.679, 39.688] 180
(-0.001, 7.854] 179
(39.688, 512.329] 176
(10.5, 21.679] 172
bins #array([ 0. , 7.8542, 10.5 , 21.6792, 39.6875, 512.3292])
下面根據(jù)區(qū)間段對(duì)其進(jìn)行等頻切分
# 對(duì)船票fare進(jìn)行分段分桶
def fare_cut(fare):
if fare <= 7.8958:
return 0
if fare <= 10.5:
return 1
if fare <= 21.6792:
return 2
if fare <= 39.6875:
return 3
return 4
df_titanic['fare_bin'] = df_titanic['fare'].map(fare_cut)
相比船票價(jià)格,年齡 age 字段的分布更加集中,且區(qū)間大小比較明確,我們采用等距切分,代碼如下:
# cut 等距離分箱
bins = [0, 12, 18, 65, 100]
pd.cut(df_titanic['age'], bins).value_counts
② Best-KS分桶。
實(shí)現(xiàn)步驟:
- 將特征值值進(jìn)行從小到大的排序。
- 計(jì)算出 最大的那個(gè)值,即為切點(diǎn),記為 。然后把數(shù)據(jù)切分成兩部分。
- 重復(fù)步驟2,進(jìn)行遞歸, 左右的數(shù)據(jù)進(jìn)一步切割。直到 的箱體數(shù)達(dá)到我們的預(yù)設(shè)閾值即可。
- 連續(xù)型變量:分箱后的 值 分箱前的 值
- 分箱過(guò)程中,決定分箱后的 值是某一個(gè)切點(diǎn),而不是多個(gè)切點(diǎn)的共同作用。這個(gè)切點(diǎn)的位置是原始 值最大的位置。
③ 卡方分桶。自底向上的(即基于合并的)數(shù)據(jù)離散化方法,依賴于卡方檢驗(yàn):具有最小卡方值的相鄰區(qū)間合并在一起,直到滿足確定的停止準(zhǔn)則。
基本思想:如果兩個(gè)相鄰的區(qū)間具有非常類似的類分布,則這兩個(gè)區(qū)間可以合并;否則,它們應(yīng)當(dāng)保持分開(kāi)。而低卡方值表明它們具有相似的類分布。
實(shí)現(xiàn)步驟:
- 預(yù)先定義一個(gè)卡方的閾值
- 初始化;根據(jù)要離散的屬性對(duì)實(shí)例進(jìn)行排序,每個(gè)實(shí)例屬于一個(gè)區(qū)間
- 合并區(qū)間:計(jì)算每一對(duì)相鄰區(qū)間的卡方值,將卡方值最小的一對(duì)區(qū)間合并
代碼實(shí)現(xiàn):https://github.com/Lantianzz/Scorecard-Bundle
④ 最小熵法分箱。還有最小熵分箱法,需要使總熵值達(dá)到最小,也就是使分箱能夠最大限度地區(qū)分因變量的各類別。
熵是信息論中數(shù)據(jù)無(wú)序程度的度量標(biāo)準(zhǔn),提出信息熵的基本目的是找出某種符號(hào)系統(tǒng)的信息量和冗余度之間的關(guān)系,以便能用最小的成本和消耗來(lái)實(shí)現(xiàn)最高效率的數(shù)據(jù)存儲(chǔ)、管理和傳遞。
數(shù)據(jù)集的熵越低,說(shuō)明數(shù)據(jù)之間的差異越小,最小熵劃分就是為了使每箱中的數(shù)據(jù)具有最好的相似性。給定箱的個(gè)數(shù),如果考慮所有可能的分箱情況,最小熵方法得到的箱應(yīng)該是具有最小熵的分箱。
我們?cè)谟行﹫?chǎng)景下會(huì)考慮特征組合構(gòu)建強(qiáng)特征,如下為常用的特征組合構(gòu)建方式:(擴(kuò)展閱讀:一文歸納Python特征生成方法(全))
多項(xiàng)式特征。針對(duì)連續(xù)值特征,我們對(duì)幾個(gè)特征構(gòu)建多項(xiàng)式特征,以達(dá)到特征組合與高階增強(qiáng)的作用。
在Titanic的例子中,如下為數(shù)值型特征:
df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size']]
df_titanic_numerical.head()
我們可以參考下述代碼構(gòu)建多項(xiàng)式特征
# 擴(kuò)展數(shù)值特征
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False, interaction_only=False)
df_titanic_numerical_poly = poly.fit_transform(df_titanic_numerical)
pd.DataFrame(df_titanic_numerical_poly, columns=poly.get_feature_names()).head()
在構(gòu)建完成特征后,我們查看下衍生新特征變量的相關(guān)性情況,下面的熱力圖heatmap里顏色越深相關(guān)性越大:
sns.heatmap(pd.DataFrame(df_titanic_numerical_poly, columns=poly.get_feature_names()).corr())
我們對(duì)于構(gòu)建完的特征,會(huì)做一些『特征變換』的操作,以適應(yīng)不同的模型,更好地完成建模。
標(biāo)準(zhǔn)化操作也稱作 Z-score 變換,它使數(shù)值特征列的算數(shù)平均為 ,方差(以及標(biāo)準(zhǔn)差)為 ,如圖所示。
注意:如果數(shù)值特征列中存在數(shù)值極大或極小的outlier(通過(guò)EDA發(fā)現(xiàn)),應(yīng)該使用更穩(wěn)健(robust)的統(tǒng)計(jì)數(shù)據(jù):用中位數(shù)而不是算術(shù)平均數(shù),用分位數(shù)(quantile)而不是方差。這種標(biāo)準(zhǔn)化方法有一個(gè)重要的參數(shù):(分位數(shù)下限,分位數(shù)上限),最好通過(guò)EDA的數(shù)據(jù)可視化確定。免疫outlier。
from sklearn.preprocessing import StandardScale
#標(biāo)準(zhǔn)化模型訓(xùn)練
Stan_scaler = StandardScaler()
Stan_scaler.fit(x)
x_zscore = Stan_scaler.transform(x)
x_test_zscore = Stan_scaler.transform(x_test)
joblib.dump(Stan_scaler,'zscore.m') #寫入文件
歸一化操作會(huì)基于向量模長(zhǎng)調(diào)整數(shù)據(jù)幅度大小,但并不會(huì)改變?cè)紨?shù)據(jù)的順序。如圖所示。
幅度縮放是為了讓不同特征的取值在大體一致的數(shù)量級(jí)和數(shù)據(jù)區(qū)間內(nèi),比較常用的方法是最大最小值縮放,如圖所示。
from sklearn import preprocessing
min_max_scaler = preprocessing.MinMaxScaler()
min_max_scaler.fit_transform(x)
x_minmax = min_max_scaler.transform(x)
x_test_minmax = min_max_scaler.transform(x_test)
joblib.dump(min_max_scaler,'min_max_scaler.m') #寫入文件
歸一化和標(biāo)準(zhǔn)化是兩個(gè)非常常見(jiàn)的特征變換操作,下面我們來(lái)對(duì)比一下標(biāo)準(zhǔn)化和歸一化:
它們分別的適用場(chǎng)景可以歸納總結(jié)如下:
參考ShowMeAI教程 圖解機(jī)器學(xué)習(xí)算法:從入門到精通系列教程[7]
具體模型參考ShowMeAI教程?圖解機(jī)器學(xué)習(xí)算法:從入門到精通系列教程[8]
我們?cè)谟行﹫?chǎng)景下,還會(huì)對(duì)數(shù)值字段進(jìn)行分布調(diào)整或者校正,利用統(tǒng)計(jì)或數(shù)學(xué)變換來(lái)減輕數(shù)據(jù)分布傾斜的影響。使原本密集的區(qū)間的值盡可能的分散,原本分散的區(qū)間的值盡量的聚合。 大部分變換函數(shù)都屬于冪變換函數(shù)簇,主要作用是穩(wěn)定方差,保持分布接近于正態(tài)分布并使得數(shù)據(jù)與分布的平均值無(wú)關(guān)。我們來(lái)看看一些典型的非線性統(tǒng)計(jì)變換。
① log變換。log 變換通常用來(lái)創(chuàng)建單調(diào)的數(shù)據(jù)變換。主要作用為穩(wěn)定方差,始終保持分布接近于正態(tài)分布并使得數(shù)據(jù)與分布的平均值無(wú)關(guān)。
log 變換屬于冪變換函數(shù)簇,數(shù)學(xué)表達(dá)式為
下面我們對(duì) Titanic 數(shù)據(jù)集中的船票價(jià)格字段進(jìn)行 log1p 變換,示例代碼如下:
sns.distplot(df_titanic.fare,kde=False)
df_titanic['fare_log'] = np.log((1+df_titanic['fare']))
sns.distplot(df_titanic.fare_log,kde=False)
② box-cox變換。box-cox 變換是 box 和 cox 在1964年提出的一種廣義冪變換方法,是統(tǒng)計(jì)建模中常用的一種數(shù)據(jù)變換,用于連續(xù)的響應(yīng)變量不滿足正態(tài)分布的情況。box-cox 變換之后,可以一定程度上減小不可觀測(cè)的誤差和預(yù)測(cè)變量的相關(guān)性。
box-cox 變換的主要特點(diǎn)是引入一個(gè)參數(shù),通過(guò)數(shù)據(jù)本身估計(jì)該參數(shù)進(jìn)而確定應(yīng)采取的數(shù)據(jù)變換形式,box-cox 變換可以明顯地改善數(shù)據(jù)的正態(tài)性、對(duì)稱性和方差相等性,對(duì)許多實(shí)際數(shù)據(jù)都是行之有效的。
box-cox 變換函數(shù)數(shù)學(xué)表達(dá)式如下:
生成的變換后的輸出 ,是輸入 和變換參數(shù)的函數(shù);當(dāng) 時(shí),該變換就是自然對(duì)數(shù) log 變換,前面我們已經(jīng)提到過(guò)了。 的最佳取值通常由最大似然或最大對(duì)數(shù)似然確定。
下面我們對(duì)Titanic數(shù)據(jù)集中的船票價(jià)格字段進(jìn)行 box-cox 變換,示例代碼如下:
# 從數(shù)據(jù)分布中移除非零值
fare_positive_value = df_titanic[(~df_titanic['fare'].isnull()) & (df_titanic['fare']>0)]['fare']
import scipy.stats as spstats
# 計(jì)算最佳λ值
l, opt_lambda = spstats.boxcox(fare_positive_value)
print('Optimal lambda value:', opt_lambda) # -0.5239075895755266
# 進(jìn)行 Box-Cox 變換
fare_boxcox_lambda_opt = spstats.boxcox(df_titanic[df_titanic['fare']>0]['fare'],lmbda=opt_lambda)
sns.distplot(fare_boxcox_lambda_opt,kde=Fal
對(duì)于類別型的字段特征(比如顏色、類型、好壞程度),有很多模型并不能直接處理,我們對(duì)其進(jìn)行編碼后能更好地呈現(xiàn)信息和支撐模型學(xué)習(xí)。有以下常見(jiàn)的類別型變量編碼方式:
① 標(biāo)簽編碼(label encoding)。標(biāo)簽編碼(label encoding)是最常見(jiàn)的類別型數(shù)據(jù)編碼方式之一,編碼值介于??和 n_classes-1 之間的標(biāo)簽。例如:比如有?我們把其轉(zhuǎn)換為?。
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit(["超一線", "一線", "二線", "三線"])
print('特征:{}'.format(list(le.classes_)))
# 輸出 特征:['一線', '三線', '二線', '超一線']
print('轉(zhuǎn)換標(biāo)簽值:{}'.format(le.transform(["超一線", "一線", "二線"])))
# 輸出 轉(zhuǎn)換標(biāo)簽值:array([3 0 2]...)
print('特征標(biāo)簽值反轉(zhuǎn):{}'.format(list(le.inverse_transform([2, 2, 1]))))
# 輸出 特征標(biāo)簽值反轉(zhuǎn):['二線', '二線', '三線
② 獨(dú)熱向量編碼(one hot encoding)。獨(dú)熱編碼通常用于處理類別間不具有大小關(guān)系的特征。例如:特征:血型,一共有四種類別?,采用獨(dú)熱編碼后,會(huì)把血型變成有一個(gè)4維的稀疏向量(最終生成的稀疏向量的維度,和類別數(shù)相同):
如果借助于pandas工具庫(kù)(查看ShowMeAI的?數(shù)據(jù)分析系列教程[9]?和?數(shù)據(jù)科學(xué)工具速查 | Pandas使用指南[10]?進(jìn)行詳細(xì)了解),獨(dú)熱向量編碼的 Python 代碼參考示例如下:
sex_list = ['MALE', 'FEMALE', np.NaN, 'FEMALE', 'FEMALE', np.NaN, 'MALE']
df = pd.DataFrame({'SEX': sex_list})
display(df)
df.fillna('NA', inplace=True)
df = pd.get_dummies(df['SEX'],prefix='IS_SEX')
display(df)
最終變換前后的結(jié)果如下:
# 原始數(shù)據(jù)
SEX
0 MALE
1 FEMALE
2 NaN
3 FEMALE
4 FEMALE
5 NaN
6 MALE
# 獨(dú)熱向量編碼后
IS_SEX_FEMALE IS_SEX_MALE IS_SEX_NA
0 0 1 0
1 1 0 0
2 0 0 1
3 1 0 0
4 1 0 0
5 0 0 1
下面我們對(duì)’sex’, ‘class’, ‘pclass’, ’embarked’, ‘who’, ‘family_size’, ‘age_bin’這些字段都進(jìn)行獨(dú)熱向量編碼。
pd.get_dummies(df_titanic, columns=['sex', 'class', 'pclass', 'embarked', 'who', 'family_size', 'age_bin'],drop_first=True)
當(dāng)然,我們也可以借助SKLearn(查看ShowMeAI教程?SKLearn最全應(yīng)用指南?和?AI建模工具速查 | Scikit-learn使用指南?詳細(xì)學(xué)習(xí)),進(jìn)行獨(dú)熱向量編碼實(shí)現(xiàn):
import numpy as np
from sklearn.preprocessing import OneHotEncoder
# 非負(fù)整數(shù)表示的標(biāo)簽列表
labels = [0,1,0,2]
# 行向量轉(zhuǎn)列向量
labels = np.array(labels).reshape(len(labels), -1)
# 獨(dú)熱向量編碼
enc = OneHotEncoder()
enc.fit(labels)
targets = enc.transform(labels).toarray()
# 如果不加 toarray() 的話,輸出的是稀疏的存儲(chǔ)格式,即索引加值的形式,也可以通過(guò)參數(shù)指定 sparse = False 來(lái)達(dá)到同樣的效果
輸出結(jié)果如下:
array([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 1., 0., 0.],
[ 0., 0., 1.]])
③ 標(biāo)簽二值化(LabelBinarizer)。功能與 OneHotEncoder 一樣,但是 OneHotEncoder 只能對(duì)數(shù)值型變量二值化,無(wú)法直接對(duì)字符串型的類別變量編碼,而 LabelBinarizer 可以直接對(duì)字符型變量二值化。
from sklearn.preprocessing import LabelBinarizer
lb=LabelBinarizer()
labelList=['yes', 'no', 'no', 'yes','no2']
# 將標(biāo)簽矩陣二值化
dummY=lb.fit_transform(labelList)
print("dummY:",dummY)
# 逆過(guò)程
yesORno=lb.inverse_transform(dummY)
print("yesOrno:",yesORno)
輸出如下:
dummY: [[0 0 1]
[1 0 0]
[1 0 0]
[0 0 1]
[0 1 0]]
yesOrno: ['yes' 'no' 'no' 'yes' 'no2']
在實(shí)際的機(jī)器學(xué)習(xí)項(xiàng)目中,我們可能還會(huì)做降維[11]處理,主要因?yàn)閿?shù)據(jù)存在以下幾個(gè)問(wèn)題:
通過(guò)特征降維希望達(dá)到的目的:減少特征屬性的個(gè)數(shù),確保特征屬性之間是相互獨(dú)立的。
常用的降維方法有:PCA、SVD、LDA、T-sne等非線性降維。
這里降維的講解,我們給大家基于 iris 數(shù)據(jù)集講解:
from sklearn import datasets
iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target
def draw_result(X, y):
plt.figure()
# 提取 Iris-setosa
setosa = X[y == 0]
# 繪制點(diǎn):參數(shù) 1 x 向量,y 向量
plt.scatter(setosa[:, 0], setosa[:, 1], color="red", label="Iris-setosa")
versicolor = X[y == 1]
plt.scatter(versicolor[:, 0], versicolor[:, 1], color="orange", label="Iris-versicolor")
virginica = X[y == 2]
plt.scatter(virginica[:, 0], virginica[:, 1], color="blue", label="Iris-virginica")
plt.legend()
plt.show()
draw_result(X, y)
① PCA(Principal Component Analysis)。關(guān)于PCA主成分分析降維算法,大家可以查閱ShowMeAI文章?圖解機(jī)器學(xué)習(xí) | 降維算法詳解[12]?進(jìn)行詳細(xì)學(xué)習(xí)。
② SVD(Singular Value Decomposition)
SVD方法的主要步驟如下:
所以??是??特征值分解的特征向量按列組成的正交矩陣,??是??特征值組成的對(duì)角矩陣,也可以看出??的奇異值??是??特征值??的平方根。
假如 的特征向量為 , 中對(duì)應(yīng)的 則可以由下式求出:
也即奇異值分解的關(guān)鍵在于對(duì)??進(jìn)行特征值分解。
from sklearn.decomposition import TruncatedSVD
iris_2d = TruncatedSVD(2).fit_transform(X)
draw_result(iris_2d, y)
PCA VS SVD
PCA求解關(guān)鍵在于求解協(xié)方差矩陣 的特征值分解。
SVD關(guān)鍵在于 的特征值分解。
很明顯二者所解決的問(wèn)題非常相似,都是對(duì)一個(gè)實(shí)對(duì)稱矩陣進(jìn)行特征值分解,如果?。?/p>
則有:
此時(shí)SVD與PCA等價(jià),所以PCA問(wèn)題可以轉(zhuǎn)化為SVD問(wèn)題求解。
③ ?LDA(Linear Discriminant Analysis)。是有監(jiān)督的降維,通過(guò)最小化類內(nèi)離散度與最大化類間離散度來(lái)獲得最優(yōu)特征子集。
上圖解讀:LD1通過(guò)線性判定,可以很好的將呈正態(tài)分布的兩個(gè)類分開(kāi)。LD2的線性判定保持了數(shù)據(jù)集的較大方差,但LD2無(wú)法提供關(guān)于類別的信息,因此LD2不是一個(gè)好的線性判定。
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
lda = LDA(n_components=2)
iris_2d = lda.fit_transform(X, y)
draw_result(iris_2d, y)
LDA VS PCA
PCA 試圖尋找到方差最大的正交的主成分分量軸 LDA 發(fā)現(xiàn)可以最優(yōu)化分類的特征子空間 LDA 和 PCA 都是可用于降低數(shù)據(jù)集維度的線性轉(zhuǎn)換技巧 PCA 是無(wú)監(jiān)督算法 LDA 是監(jiān)督算法 LDA 是一種更優(yōu)越的用于分類的特征提取技術(shù)
④ ?T-SNE。T-SNE(t-distributed stochastic neighbor embedding)是一種非線性降維方法:
from sklearn.manifold import TSNE
tsne = TSNE(n_components=2)
iris_2d = tsne.fit_transform(X)
draw_result(iris_2d, y)
特征選擇是在建模過(guò)程中經(jīng)常會(huì)用到的一個(gè)處理,也有重要意義:
總體來(lái)說(shuō),進(jìn)行特征選擇有2個(gè)主要考慮方向:
對(duì)特征選擇的方法進(jìn)行歸類,又大體可以歸納為下述3種:
feature_selection
?庫(kù)來(lái)進(jìn)行特征選擇。① 方差過(guò)濾。這是通過(guò)特征本身的方差來(lái)篩選特征的類。比如一個(gè)特征本身的方差很小,就表示樣本在這個(gè)特征上基本沒(méi)有差異,可能特征中的大多數(shù)值都一樣,甚至整個(gè)特征的取值都相同,那這個(gè)特征對(duì)于樣本區(qū)分沒(méi)有什么作用。
我們會(huì)剔除掉方差非常小的字段特征,參考代碼實(shí)現(xiàn)如下:
from sklearn.feature_selection import VarianceThreshold
variancethreshold = VarianceThreshold() #實(shí)例化,默認(rèn)方差為 0.方差<=0 的過(guò)濾掉
df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size']]
X_var = variancethreshold.fit_transform(df_titanic_numerical) #獲取刪除不合格特征后的新特征矩陣
del_list = df_titanic_numerical.columns[variancethreshold.get_support()==0].to_list() #獲得刪除
② 卡方過(guò)濾??ǚ綑z驗(yàn),專用于分類算法,捕捉相關(guān)性,追求p小于顯著性水平的特征??ǚ竭^(guò)濾是專門針對(duì)離散型標(biāo)簽(即分類問(wèn)題)的相關(guān)性過(guò)濾。
p值和取到這一個(gè)統(tǒng)計(jì)量的概率取值其實(shí)是正相關(guān)的:?值越大,取到這個(gè)統(tǒng)計(jì)量的概率就越大,即越合理;?值越小,取到這個(gè)統(tǒng)計(jì)量的概率就越小,即越不合理,此時(shí)應(yīng)該拒絕原假設(shè),接收備擇假設(shè)。
df_titanic_categorical = df_titanic[['sex', 'class', 'embarked', 'who', 'age_bin','adult_male','alone','fare_bin']]
df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size','pclass']]
df_titanic_categorical_one_hot = pd.get_dummies(df_titanic_categorical, columns=['sex', 'class', 'embarked', 'who', 'age_bin','adult_male','alone','fare_bin'], drop_first=True)
df_titanic_combined = pd.concat([df_titanic_numerical,df_titanic_categorical_one_hot],axis=1)
y = df_titanic['survived']
X = df_titanic_combined.iloc[:,1:]
from sklearn.feature_selection import chi2
from sklearn.feature_selection import SelectKBest
chi_value, p_value = chi2(X,y)
#根據(jù) p 值,得出 k 值
k = chi_value.shape[0] - (p_value > 0.05).sum() #要保留的特征的數(shù)量 14
#根據(jù)卡方值,選擇前幾特征,篩選后特征
X_chi = SelectKBest(chi2, k=14).fit_transform(X, y)
③ F檢驗(yàn)。?檢驗(yàn)捕捉線性相關(guān)性,要求數(shù)據(jù)服從正態(tài)分布,追求??值小于顯著性水平特征。
from sklearn.feature_selection import f_classif
f_value, p_value = f_classif(X,y)
#根據(jù) p 值,得出 k 值
k = f_value.shape[0] - (p_value > 0.05).sum()
#篩選后特征
X_classif = SelectKBest(f_classif, k=14).fit_transform(X, y)
④ 互信息法?;バ畔⒎ㄊ怯脕?lái)捕捉每個(gè)特征與標(biāo)簽之間的任意關(guān)系(包括線性和非線性關(guān)系)的過(guò)濾方法。
from sklearn.feature_selection import mutual_info_classif as MIC
#互信息法
mic_result = MIC(X,y) #互信息量估計(jì)
k = mic_result.shape[0] - sum(mic_result <= 0) #16
X_mic = SelectKBest(MIC, k=16).fit_transform(X, y)
① 歸特征刪除法。遞歸消除刪除法使用一個(gè)基模型來(lái)進(jìn)行多輪訓(xùn)練,每輪訓(xùn)練后,消除若干權(quán)值系數(shù)的特征,再基于新的特征集進(jìn)行下一輪訓(xùn)練。使用feature_selection
?庫(kù)的 RFE 類來(lái)選擇特征的代碼如下:
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#遞歸特征消除法,返回特征選擇后的數(shù)據(jù)
#參數(shù) estimator 為基模型
#參數(shù) n_features_to_select 為選擇的特征個(gè)數(shù)
X_ref = RFE(estimator=LogisticRegression(), n_features_to_select=10).fit_transform(X, y)
② 特征重要性評(píng)估。我們基于一些模型(如各類樹(shù)模型)可以得到特征重要度,進(jìn)而進(jìn)行篩選
from sklearn.ensemble import ExtraTreesClassifier
# 建模與獲取特征重要度
model = ExtraTreesClassifier()
model.fit(X, y)
print(model.feature_importances_)
# 特征重要度排序
feature=list(zip(X.columns,model.feature_importances_))
feature=pd.DataFrame(feature,columns=['feature','importances'])
feature.sort_values(by='importances',ascending=False).head(20)
③ 排列重要性評(píng)估。我們還有一類方法可以評(píng)估特征重要度,進(jìn)而進(jìn)行篩選,叫作排列重要度。
原理:在訓(xùn)練機(jī)器學(xué)習(xí)模型之后計(jì)算置換重要性。這種方法在向模型提出假設(shè),如果在保留目標(biāo)和所有其他列的同時(shí)隨機(jī)打亂一列驗(yàn)證集特征數(shù)據(jù),對(duì)預(yù)測(cè)機(jī)器學(xué)習(xí)模型的準(zhǔn)確性的影響程度。對(duì)于一個(gè)具有高度重要性的特征,random-reshuffle 會(huì)對(duì)機(jī)器學(xué)習(xí)模型預(yù)測(cè)的準(zhǔn)確性造成更大的損害。
優(yōu)點(diǎn):快速計(jì)算;易于使用和理解;特征重要性度量的屬性;追求特征穩(wěn)定性。
參考代碼實(shí)現(xiàn)如下:
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import eli5
from eli5.sklearn import PermutationImportance
my_model = RandomForestClassifier(random_state=0).fit(train_X, train_y)
perm = PermutationImportance(my_model, random_state=1).fit(val_X, val_y)
eli5.show_weights(perm, feature_names = val_X.columns.tolist())
① 基于懲罰項(xiàng)的特征選擇法。使用帶懲罰項(xiàng)的基模型,除了篩選出特征外,同時(shí)也進(jìn)行了降維。
使用feature_selection
庫(kù)的SelectFromModel
類結(jié)合帶 L1 懲罰項(xiàng)的邏輯回歸模型,來(lái)選擇特征的代碼如下:
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
#帶 L1 和 L2 懲罰項(xiàng)的邏輯回歸作為基模型的特征選擇,這個(gè)設(shè)置帶 L1 懲罰項(xiàng)的邏輯回歸作為基模型的特征選擇
lr = LogisticRegression(solver='liblinear',penalty="l1", C=0.1)
X_sfm = SelectFromModel(lr).fit_transform(X, y)
X_sfm.shape
(891, 7
使用 feature_selection 庫(kù)的 SelectFromModel 類結(jié)合 SVM 模型,來(lái)選擇特征的代碼如下:
from sklearn.feature_selection import SelectFromModel
from sklearn.svm import LinearSVC
lsvc = LinearSVC(C=0.01,penalty='l1',dual=False).fit(X, y)
model = SelectFromModel(lsvc,prefit=True)
X_sfm_svm = model.transform(X)
X_sfm_svm.shape
(891, 7
② 基于樹(shù)模型。樹(shù)模型中 GBDT 也可用來(lái)作為基模型進(jìn)行特征選擇,使用 feature_selection 庫(kù)的 SelectFromModel 類結(jié)合 GBDT 模型,來(lái)選擇特征的代碼如下:
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT 作為基模型的特征選擇
gbdt = GradientBoostingClassifier()
X_sfm_gbdt = SelectFromModel(gbdt).fit_transform(X, y)
關(guān)于特征選擇,做一個(gè)經(jīng)驗(yàn)總結(jié),如下:
最后,ShowMeAI結(jié)合實(shí)際工業(yè)應(yīng)用經(jīng)驗(yàn),總結(jié)一些特征工程要點(diǎn):
構(gòu)建特征的有效性,和業(yè)務(wù)及數(shù)據(jù)分布強(qiáng)相關(guān),因此建議在此步驟之前做EDA探索性數(shù)據(jù)分析來(lái)充分理解數(shù)據(jù)。
可以參考ShowMeAI文章?Python機(jī)器學(xué)習(xí)綜合項(xiàng)目-電商銷量預(yù)估[13]?和?Python機(jī)器學(xué)習(xí)綜合項(xiàng)目-電商銷量預(yù)估<進(jìn)階>[14]?了解EDA的基本過(guò)程和方法。
我們可能會(huì)做的一些數(shù)據(jù)預(yù)處理與特征處理如下:
① 連續(xù)特征離散化
② 數(shù)值截?cái)?/strong>
.clip(low,upper)
方法結(jié)合業(yè)務(wù)場(chǎng)景和數(shù)據(jù)分布,進(jìn)行合理的缺失值、異常值處理。
建議不要上來(lái)就做PCA或LDA降維,最好先構(gòu)建特征并對(duì)特征做篩選。
① 線性組合(linear combination)
median(N1)_by(C1) 中位數(shù)
mean(N1)_by(C1) 算術(shù)平均數(shù)
mode(N1)_by(C1) 眾數(shù)
min(N1)_by(C1) 最小值
max(N1)_by(C1) 最大值
std(N1)_by(C1) 標(biāo)準(zhǔn)差
var(N1)_by(C1) 方差
freq(C2)_by(C1) 頻數(shù)
③ 統(tǒng)計(jì)特征+線性組合
N1 - median(N1)_by(C1)
N1 - mean(N1)_by(C1)
④ 基于樹(shù)模型創(chuàng)造新特征
在 Scikit-Learn 和 XGBoost 里,可以基于
apply()
以及decision_path()
等方法實(shí)現(xiàn)。
我們?cè)诓煌愋偷哪P屠?,也?huì)考慮不同的特征工程方法:
① 樹(shù)模型
② 依賴樣本距離的模型
文章轉(zhuǎn)自微信公眾號(hào)@算法進(jìn)階
對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力
一鍵對(duì)比試用API 限時(shí)免費(fèi)