特徵工程目的
特徵工程思考流程
1. 搜索
查看數據列名:
print(data_train.columns)
查看數據每列信息(數目,空置和類型):
print(data_train.info())
查看每列統計信息(數目、均值、方差、最小值、25%分位值、50%分位值、75%分位值和最大值):
print(data_train.describe())
- 特徵狀況:
- 類型:
- 連續型(均值作爲代表)、離散型(衆數)
- int float object 等
- 考慮是否需要進行離散化
- 空置:NaN
利用Pandas庫判斷數據中是否有空值:
print(pd.isnull(df))
返回值爲等尺寸的DataFram文件,數據類型爲:bool類型(True/Flase)
- 分佈(特徵選擇)
通過散點圖查看單一特徵分佈:
plt.scatter(X,y) #X爲特徵,y爲標籤
- 異常點
方差較小的情況下,Max/Min 離均值很遠,則Max/Min可能爲異常點
通過繪圖觀察,極具偏離大部分樣本點的點稱爲異常點(需要判讀該點是數據性質造成的,還是真是存在的異常點)
異常點的存在,對模型的魯棒性存在影響 - 量綱
任意兩列數據的單位懸殊過大
- 類型:
- 標籤狀況:
- 類型
判斷當前問題類型
- 分佈
判斷當前是否需要做標籤均衡
- 類型
2. 清洗/向量化
- 類別特徵編碼轉爲向量
需要對字符串類別的數據做數值編碼:
- 直接按順序按轉爲 0 1 2 (例:a6_titanic\data_process\1_feature.py)
- Onehot 編碼,解決特徵值的距離問題([1 0 0], [0 1 0], [0 0 1])(例:a6_titanic\data_process\7_feature_one_hot.py)
- 空值處理
- 缺失較多,可以丟棄
df.dropna()
- 填充
- 填充固定數值
df.fillna(value=0) # NaN → 0
- 離散數值 – 衆數或新數值
df = data_train.apply(lambda x:x.fillna(x.value_counts().index[0]))
- 連續數值 – 均值
df = data_train.apply(lambda x:x.mean(),axis = 1)
- 填充固定數值
- ML預測
將已知數據設爲數據集,將NaN值設爲測試集,進行ML從而得到完整的數據
示例如下(使用了隨機森林進行訓練)def set_missing_ages(df): # 把已有的數值型特徵取出來丟進Random Forest Regressor中 age_df = df[['Age', 'Fare', 'Parch', 'SibSp', 'Pclass']] # 乘客分成已知年齡和未知年齡兩部分 known_age = age_df[age_df.Age.notnull()].as_matrix() unknown_age = age_df[age_df.Age.isnull()].as_matrix() # y即目標年齡 y = known_age[:, 0] # X即特徵屬性值 X = known_age[:, 1:] # fit到RandomForestRegressor之中 rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1) rfr.fit(X, y) # 用得到的模型進行未知年齡結果預測 predictedAges = rfr.predict(unknown_age[:, 1::]) # 用得到的預測結果填補原缺失數據 df.loc[(df.Age.isnull()), 'Age'] = predictedAges return df, rfr
- 缺失較多,可以丟棄
3. 標準化
-
異常點
例:a1_numpy_pandas\pandas\3_set_value.py
df.A[df.A>70] = 0 df.A[df.A<=70] = 1
設置取值範圍
-
量綱是否需要統一
- 標準化(也叫Z-score standardization)(對列向量處理):
將服從正態分佈的特徵值轉換成標準正態分佈,標準化需要計算特徵的均值和標準差,公式表達爲:
使用preproccessing庫的StandardScaler類對數據進行標準化的代碼如下:from sklearn.preprocessing import StandardScaler #標準化,返回值爲標準化後的數據 StandardScaler().fit_transform(iris.data)
- 區間縮放(對列向量處理)
區間縮放法的思路有多種,常見的一種爲利用兩個最值進行縮放,公式表達爲:
使用preproccessing庫的MinMaxScaler類對數據進行區間縮放的代碼如下:from sklearn.preprocessing import MinMaxScaler #區間縮放,返回值爲縮放到[0, 1]區間的數據 MinMaxScaler().fit_transform(iris.data)
- 標準化(也叫Z-score standardization)(對列向量處理):
-
是否需要歸一化
- 歸一化(對行向量處理)
歸一化目的在於樣本向量在點乘運算或其他核函數計算相似性時,擁有統一的標準,也就是說都轉化爲“單位向量”。規則爲l2的歸一化公式如下:
使用preproccessing庫的Normalizer類對數據進行歸一化的代碼如下:from sklearn.preprocessing import Normalizer #歸一化,返回值爲歸一化後的數據 Normalizer().fit_transform(iris.data)
- 歸一化(對行向量處理)
-
分桶離散
-
對定量特徵二值化(對列向量處理)
定性與定量區別
定性:博主很胖,博主很瘦
定量:博主有80kg,博主有60kg
一般定性都會有相關的描述詞,定量的描述都是可以用數字來量化處理
定量特徵二值化的核心在於設定一個閾值,大於閾值的賦值爲1,小於等於閾值的賦值爲0,公式表達如下:
使用preproccessing庫的Binarizer類對數據進行二值化的代碼如下:from sklearn.preprocessing import Binarizer #二值化,閾值設置爲3,返回值爲二值化後的數據 Binarizer(threshold=3).fit_transform(iris.data)
目的:希望數據在特定的區間才存在意義
例:a2_feature_engineering\6_continue_to_bin\continue_to_discret_bucketing.py -
Q&A:
Q: CTR預估,發現CTR預估一般都是用LR,而且特徵都是離散的。爲什麼一定要用離散特徵呢?這樣做的好處在哪裏?
A: 在工業界,很少直接將連續值作爲邏輯迴歸模型的特徵輸入,而是將連續特徵離散化爲一系列0、1特徵交給邏輯迴歸模型,這樣做的優勢有以下幾點:- 離散特徵的增加和減少都很容易,易於模型的快速迭代。(離散特徵的增加和減少,模型也不需要調整,重新訓練是必須的,相比貝葉斯推斷方法或者樹模型方法迭代快)
- 稀疏向量內積乘法運算速度快,計算結果方便存儲,容易擴展;
- 離散化後的特徵對異常數據有很強的魯棒性:比如一個特徵是年齡>30是1,否則0。如果特徵沒有離散化,一個異常數據“年齡300歲”會給模型造成很大的干擾;離散化後年齡300歲也只對應於一個權重,如果訓練數據中沒有出現特徵"年齡-300歲",那麼在LR模型中,其權重對應於0,所以,即使測試數據中出現特徵"年齡-300歲",也不會對預測結果產生影響。特徵離散化的過程,比如特徵A,如果當做連續特徵使用,在LR模型中,A會對應一個權重w,如果離散化,那麼A就拓展爲特徵A-1,A-2,A-3…,每個特徵對應於一個權重,如果訓練樣本中沒有出現特徵A-4,那麼訓練的模型對於A-4就沒有權重,如果測試樣本中出現特徵A-4,該特徵A-4也不會起作用。相當於無效。但是,如果使用連續特徵,在LR模型中,y = wa,a是特徵,w是a對應的權重,比如a代表年齡,那麼a的取值範圍是[0…100],如果測試樣本中,出現了一個測試用例,a的取值是300,顯然a是異常值,但是wa還是有值,而且值還非常大,所以,異常值會對最後結果產生非常大的影響。
- 邏輯回歸屬於廣義線性模型,表達能力受限;單變量離散化爲N個後,每個變量有單獨的權重,相當於爲模型引入了非線性,能夠提升模型表達能力,加大擬合;在LR模型中,特徵A作爲連續特徵對應的權重是Wa。A是線性特徵,因爲y = WaA,y對於A的導數就是Wa,如果離散化後,A按區間離散化爲A_1,A_2,A_3。那麼y = w_1A_1+w_2A_2+w_3A_3.那麼y對於A的函數就相當於分段的線性函數,y對於A的導數也隨A的取值變動,所以,相當於引入了非線性。
- 離散化後可以進行特徵交叉,加入特徵A 離散化爲M個值,特徵B離散爲N個值,那麼交叉之後會有M*N個變量,進一步引入非線性,提升表達能力;
- 特徵離散化後,模型會更穩定,比如如果對用戶年齡離散化,20-30作爲一個區間,不會因爲一個用戶年齡長了一歲就變成一個完全不同的人。當然處於區間相鄰處的樣本會剛好相反,所以怎麼劃分區間是門學問;按區間離散化,劃分區間是非常關鍵的。
- 特徵離散化以後,起到了簡化了邏輯迴歸模型的作用,降低了模型過擬合的風險。(當使用連續特徵時,一個特徵對應於一個權重,那麼,如果這個特徵權重較大,模型就會很依賴於這個特徵,這個特徵的一個微小變化可能會導致最終結果產生很大的變化,這樣子的模型很危險,當遇到新樣本的時候很可能因爲對這個特徵過分敏感而得到錯誤的分類結果,也就是泛化能力差,容易過擬合。而使用離散特徵的時候,一個特徵變成了多個,權重也變爲多個,那麼之前連續特徵對模型的影響力就被分散弱化了,從而降低了過擬合的風險。)
模型是使用離散特徵還是連續特徵,其實是一個“海量離散特徵+簡單模型” 同 “少量連續特徵+複雜模型”的權衡。既可以離散化用線性模型,也可以用連續特徵加深度學習。就看是喜歡折騰特徵還是折騰模型了。通常來說,前者容易,而且可以n個人一起並行做,有成功經驗;後者目前看很贊,能走多遠還須拭目以待。
- 離散特徵的增加和減少都很容易,易於模型的快速迭代。(離散特徵的增加和減少,模型也不需要調整,重新訓練是必須的,相比貝葉斯推斷方法或者樹模型方法迭代快)
-
-
標籤均衡
標籤比例不平衡(比如1:9),處理方式如下:- 上採樣(Upsample)(少→多):
有放回的重複採樣,將少量數據採多# Separate majority and minority classes df_majority = df[df.Survived==0] df_minority = df[df.Survived==1] # Upsample minority class df_minority_upsampled = resample(df_minority, replace=True, # sample with replacement n_samples=(len(df_majority) - len(df_minority)), # to match majority class random_state=123) # reproducible results # Combine majority class with upsampled minority class df_upsampled = pd.concat([df_majority, df_minority_upsampled, df_minority]) print(df_upsampled.columns) print(df_upsampled['Survived'].value_counts())
- SMOTE造類似少量數據類別相似的假數據
參考博客:https://blog.csdn.net/weixin_36431280/article/details/82560988 - 下采樣(Downsample)(多→少):
Separate majority and minority classes df_majority = df[df.Survived==0] df_minority = df[df.Survived==1] # # Upsample minority class df_manority_downsampled = resample(df_majority, replace=True, # sample with replacement n_samples=len(df_minority), # to match majority class random_state=123) # reproducible results # Combine majority class with upsampled minority class df_dwonsampled = pd.concat([df_manority_downsampled, df_minority]) print(df_dwonsampled.columns) print(df_dwonsampled['Survived'].value_counts())
- 訓練模型控制(推薦:省時、方便):
使用模型內部超參(class_weight=‘balanced’)進行控制,示例如下:clf = SVC(kernel='linear', class_weight='balanced', # penalize probability=True)
- 特殊情況:
標籤信息太少,將監督學習轉爲無監督學習的聚類問題進行
- 上採樣(Upsample)(少→多):
4.特徵選擇(過擬合後看看)
- Filter
比較單特徵與標籤關係- 卡方檢驗
經典的卡方檢驗是檢驗定性自變量對定性因變量的相關性,參考示例如下:
例子:a2_feature_engineering\4_feature_selection\1_filter\chi2.pyfrom sklearn.feature_selection import SelectKBest from sklearn.feature_selection import chi2#選擇K個最好的特徵,返回選擇特徵後的數據 from sklearn.datasets import load_iris iris = load_iris() # k Number of top features to select. The “all” option bypasses selection, for use in a parameter search. selector = SelectKBest(chi2, k=2).fit(iris.data, iris.target) data = selector.transform(iris.data) print(data) print(selector.scores_)
- 方差選擇法
使用方差選擇法,先要計算各個特徵的方差,然後根據閾值,選擇方差大於閾值的特徵。使用feature_selection庫的VarianceThreshold類來選擇特徵的代碼如下:from sklearn.feature_selection import VarianceThreshold #方差選擇法,返回值爲特徵選擇後的數據 #參數threshold爲方差的閾值 from sklearn.datasets import load_iris iris = load_iris() #print(VarianceThreshold(threshold=3).fit_transform(iris.data)) print(iris.data[0:5]) selector = VarianceThreshold(threshold=3).fit(iris.data, iris.target) data = selector.transform(iris.data) print(data[0:5]) print(selector.variances_)
- 皮爾遜相關係數(Pearson Correlation Coefficient)
該係數是用來衡量兩個數據集合是否在一條線上面,它用來衡量定距變量間的線性關係。
代碼如下:import numpy as np def pcc(X, Y): ''' Compute Pearson Correlation Coefficient. ''' # Normalise X and Y X -= X.mean() Y -= Y.mean() # Standardise X and Y X /= X.std() Y /= Y.std() # Compute mean product return np.mean(X*Y) # Using it on a random example from random import random X = np.array([random() for x in range(100)]) Y = np.array([random() for x in range(100)]) print(pcc(X, Y))
- 皮爾遜相關係數(Pearson Correlation Coefficient)
- 卡方檢驗
- Wrapper
- 遞歸特徵消除法(也是衡量特徵重要性的指標)
進行大規模實驗,進行遞歸特徵消除,返回特徵選擇後的數據。遞歸消除特徵法使用一個基模型來進行多輪訓練,每輪訓練後,消除若干權值係數的特徵,再基於新的特徵集進行下一輪訓練。使用feature_selection庫的RFE類來選擇特徵的代碼如下:from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression from sklearn.datasets import load_iris iris = load_iris() #參數estimator爲基模型 #參數n_features_to_select爲選擇的特徵個數7 print(iris.data[0:5]) selector = RFE(estimator=LogisticRegression(), n_features_to_select=2).fit(iris.data, iris.target) data = selector.transform(iris.data) print(data[0:5]) print(selector.ranking_)
- 遞歸特徵消除法(也是衡量特徵重要性的指標)
- Embed
- 線性迴歸模型:
通過線性模型學習,比較不同特徵的權值,權值越大說明特徵更重要,示例如下:from sklearn.svm import LinearSVC from sklearn.datasets import load_iris from sklearn.feature_selection import SelectFromModel iris = load_iris() X, y = iris.data, iris.target print(X.shape) lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X, y) model = SelectFromModel(lsvc, prefit=True) X_new = model.transform(X) print(X_new.shape)
- 樹模型:
將特徵類別添加至樹結構中,距離頂節點越進說明該特徵更重要示例如下:from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import GradientBoostingClassifier from sklearn.datasets import load_iris iris = load_iris() #GBDT作爲基模型的特徵選擇 #print(SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data, iris.target)) selector = SelectFromModel(GradientBoostingClassifier()).fit(iris.data, iris.target) print(iris.data[0:5]) data = selector.transform(iris.data) print(data[0:5]) print(selector.estimator_.feature_importances_)
總結:filter適合特徵比較少時使用,通過手工挑選特徵;wrapper和embedded適用於自動化處理,用於多特徵,但wrapper方式的運算代價遠遠大於embedded方式,前者需要進行多個模型訓練,後者只需訓練一個模型。綜上所述,推薦使用embedded方式選取特徵。
- 線性迴歸模型:
5.特徵擴展(欠擬合後看看)
- 業務總結
- 根據實際情況整合特徵類型:
- 基本特徵
- 空間:種類、數量、金額、大小、重量、長度
- 時間:時長,次數,頻率,週期
- 統計特徵
- 比值、比例
- 最大值、最小值、平均值、中位數、分位數
- 複雜特徵
參考比賽老手打比賽的特徵聚合經驗 - 自然特徵
圖像、語音、文本、網絡
- 基本特徵
- 根據實際情況整合特徵類型:
- 組合已有的特徵(組合,聚和)
-
agg函數:
DataFrame.agg(func,axis = 0,* args,** kwargs )
-
gropy函數:
groupby()是一個分組函數,對數據進行分組操作的過程可以概括爲:split-apply-combine三步:- 按照鍵值(key)或者分組變量將數據分組
- 對於每組應用我們的函數,這一步非常靈活,可以是python自帶函數,可以是我們自己編寫的函數
- 將函數計算後的結果聚合
參考示例:
import pandas as pd import numpy as np #Create a DataFrame d = { 'Name':['Alisa','Bobby','jodha','jack','raghu','Cathrine', 'Alisa','Bobby','kumar','Alisa','Alex','Cathrine'], 'Age':[26,24,23,22,23,24,26,24,22,23,24,24], 'Score':[85,63,55,74,31,77,85,63,42,62,89,77]} df = pd.DataFrame(d,columns=['Name','Age','Score']) print(df.columns.values) # key內部求和 gp = df.groupby(["Name"])["Age"].sum().reset_index() # reset_index重置index print(gp) gp.rename(columns={"Age":"sum_of_value"},inplace=True) # rename改列名 print(gp) res = pd.merge(df, gp, on=['Name'], how='inner') # default for how='inner' print(res)
參考博客:Pandas groupby常用功能
-
PolynomialFeatures函數:
信息利用率低:不同的機器學習算法和模型對數據中信息的利用是不同的,之前提到在線性模型中,使用對定性特徵啞編碼可以達到非線性的效果。類似地,對定量變量多項式化,或者進行其他的轉換,都能達到非線性的效果。
參考示例如下:from sklearn.preprocessing import PolynomialFeatures import numpy as np # a,b -> a, b, ab, a^2, b^2, 1 X = np.arange(9).reshape(3, 3) print(X[0:5]) poly = PolynomialFeatures(2) print(poly.fit_transform(X[0:5])) poly = PolynomialFeatures(interaction_only=True) print(poly.fit_transform(X))
-