手把手教你入门和实践特征工程 的全方位万字笔记,附代码下载
???? 說起特征工程,都說是機器學習建模中最為重要而且費時的一項工作,而且它涉及的知識點會非常地多,經驗老道的老司機自然是輕車熟路了,但對于剛剛入門的新手司機,學習到的知識點都是東一點西一點的,不夠系統化,本篇文章是在閱讀了一本評分極高的特征工程書籍 ???? 《特征工程入門與實踐》后的一篇筆記文,記錄下相對比較系統的知識點以及可運行復現的代碼,希望對各位同行有所幫助哈。
圖:強力推薦這本書?
???? 目錄
???? 特征理解
???? 特征增強
?????特征構建
??特征選擇
?????特征轉換
?????特征學習
大家可以先看下思維導圖:
???? 01 特征理解
在拿到數據的時候,我們第一步需要做的是理解它,一般我們可以從下面幾個角度入手:
(注:本節用到了兩個數據集,分別是Salary_Ranges_by_Job_Classification 和 GlobalLandTemperaturesByCity)
1. 區分結構化數據與非結構化數據
如一些以表格形式進行存儲的數據,都是結構化數據;而非結構化數據就是一堆數據,類似于文本、報文、日志之類的。
2. 區分定量和定性數據
定量數據:指的是一些數值,用于衡量某件東西的數量;
定性數據:指的是一些類別,用于描述某件東西的性質。
其實區分了定量和定性數據,還可以繼續細分下去,分為定類(nominal)、定序(ordinal)、定距(interval)、定比數據(ratio),下面我們分別對這4類數據進行舉例說明,加深大家對它們的印象。
1)定類(nominal)
也就是分類,比如:血型(A/B/O/AB型)、性別(男/女)、貨幣(人民幣/美元/日元),而且值得注意的是這些分類之間沒有大小可比性。一般畫圖的話就只能看下分布占比,可以用條形圖、餅圖來表示。
2)定序(ordinal)
定序相比于定類,也就是多了一個“可排序”的屬性,也就是說雖然它們是類別變量,但是它們的變量值之間是存在“大小”之分的。比如:期末績點(A、B、C、D、E、F)、問卷答案(非常滿意、滿意、一般、不滿意)。可視化方面,和定類一樣,不過就是多了一個 箱體圖 可以用(因為定序變量可以有中位數)。
3)定距(interval)
定距的話,就是變量值之間可以做加減法計算,也就是可以引入均值、方差之類的名詞了,而且能夠畫的圖也多了,包括先前的那些,還包括了直方圖。
4)定比(ratio)
定比相比于定距更加嚴格,不僅僅有定距的所有屬性,同時,有一個 絕對零點 的概念,可以做加減乘除運算,比如說某個商品的價格是另一個的2倍。值得注意的是,溫度一般不歸入定比,而是定距,沒有說20度是10度的兩倍這種說法。
最后把上面的內容總結一下:
3. 關鍵代碼匯集
以下的代碼只是核心片段,完整代碼可在公眾號(SAMshare)后臺輸入關鍵字 特征工程 獲取。
1)常見簡易畫圖
# 繪制條形圖 salary_ranges['Grade'].value_counts().sort_values(ascending=False).head(10).plot(kind='bar')# 繪制餅圖 salary_ranges['Grade'].value_counts().sort_values(ascending=False).head(5).plot(kind='pie')# 繪制箱體圖 salary_ranges['Union Code'].value_counts().sort_values(ascending=False).head(5).plot(kind='box')# 繪制直方圖 climate['AverageTemperature'].hist()# 為每個世紀(Century)繪制平均溫度的直方圖 climate_sub_china['AverageTemperature'].hist(by=climate_sub_china['Century'],sharex=True,sharey=True,figsize=(10,?10),bins=20)# 繪制散點圖 x?=?climate_sub_china['year'] y?=?climate_sub_china['AverageTemperature']fig,?ax?=?plt.subplots(figsize=(10,5)) ax.scatter(x,?y) plt.show()2)檢查缺失情況
# 移除缺失值 climate.dropna(axis=0,?inplace=True)# 檢查缺失個數 climate.isnull().sum()3)變量類別轉換
# 日期轉換, 將dt 轉換為日期,取年份, 注意map的用法 climate['dt'] =?pd.to_datetime(climate['dt']) climate['year'] =?climate['dt'].map(lambda?value:?value.year)# 只看中國 climate_sub_china?=?climate.loc[climate['Country'] ==?'China'] climate_sub_china['Century'] =?climate_sub_china['year'].map(lambda?x:int(x/100+1)) climate_sub_china.head()???? 02 特征增強
這一步其實就是數據清洗了,雖然上一步中也有涉及到部分清洗工作(比如清除空值、日期轉換之類的),但卻是分散的,這節重點講講數據清洗的一些技巧和實踐代碼,供大家在實際項目中去使用。
Step1: 進行EDA(Exploratory Data Analysis),思路如下:
(1)首先看看目標占比情況(針對二分類問題,也就是0和1的占比情況),直接 value_counts()就可以解決,看看樣本是否失衡。
(2)接著看看有沒有空值,直接統計 isnull().sum() 的個數,不過需要注意的是,可能統計出來沒有缺失,并不是因為真的沒有缺失,而且缺失被人用某個特殊值填充了,一般會用 -9、blank、unknown、0之類的,需要注意??識別,后面需要對缺失進行合理填充。
(2.1)怎么識別缺失值呢?一般可以通過 data.describe() 獲取基本的描述性統計,根據均值、標準差、極大極小值等指標,結合變量含義來判斷。
(3)再接著看不同類別之間的特征值分布情況,可通過畫直方圖(數值型變量)和計算變量值占比分布(類別變量)來觀察。
(4)觀察不同變量之間的相關性情況,可以通過繪制 相關矩陣的熱力圖 來觀察大體情況。
Step2: 處理數據缺失問題
缺失處理的辦法有好多種,但最為常用的作者講到有兩種:填充和刪除。
而在處理缺失前,我們在上面的小節中識別出來了部分被人工填充的缺失,
需要還原一下:
# 處理被錯誤填充的缺失值0,還原為 空(單獨處理) pima['serum_insulin'] =?pima['serum_insulin'].map(lambda?x:x?if?x?!=0?else?None) # 檢查變量缺失情況 pima['serum_insulin'].isnull().sum()# 批量操作 還原缺失值 columns?= ['serum_insulin','bmi','plasma_glucose_concentration','diastolic_blood_pressure','triceps_thickness']for?col?in?columns:pima[col].replace([0], [None],?inplace=True)# 檢查變量缺失情況 pima.isnull().sum()1) 刪除含有缺失值的行
這里的話比較簡單,就是使用 dropna() 來處理即可,同時我們還可以檢查下我們到底刪除了多少數據量:round(data.shape[0]-data_dropped.shape[0])/float(data.shape[0]) 就可以統計出來了。當然,刪除之后,我們還需要看看數據的分布,對比目標占比、特征分布與先前的是否存在明顯差異,如果是的話,建議不要使用這種辦法。
2) 缺失值合理填充
缺失填充,這里介紹的有均值填充、-9填充、中位數填充。這里會比較簡單,我們可以通常都是通過 sklearn的 Pipeline以及 Imputer來實現,下面是一個簡單的完整
Demo:
# 使用sklearn的 Pipeline以及 Imputer來實現缺失值填充 from?sklearn.pipeline?import?Pipeline from?sklearn.neighbors?import?KNeighborsClassifier from?sklearn.model_selection?import?GridSearchCV from?sklearn.preprocessing?import?Imputer# 調參候選 knn_params?= {'classify__n_neighbors':[1,2,3,4,5,6]}# 實例化KNN模型 knn?=?KNeighborsClassifier()# 管道設計 mean_impute?=?Pipeline([('imputer',?Imputer(strategy='mean')),('classify',knn)])x?=?pima.drop('onset_disbetes',?axis=1)?# 丟棄y y?=?pima['onset_disbetes']# 網格搜索 grid?=?GridSearchCV(mean_impute,?knn_params) grid.fit(x,?y)# 打印模型效果 print(grid.best_score_,?grid.best_params_) # 0.73177Step3: 標準化和歸一化
經過上面的處理,模型的精度可以達到0.73177,但我們可以繼續優化嗎?那是肯定的。
我們可以先看看所有特征的分布(特征少的時候可以這么看):
pima_imputed_mean.hist(figsize=(15,15))從上圖中我們可以看出一個問題,那就是每個特征之間的量綱都是不一樣的,這對于knn這種基于距離的模型來說是“致命”的bug,因此我們需要進行標準化和歸一化處理。
我們重點關注3種方法:
1)Z分數標準化
最為常用的標準化技術,利用了統計學中的z分數思想,也就是將數據轉換為均值為0,標準差為1的分布,其在python中的調用方法:
# z分數標準化(單一特征) from?sklearn.preprocessing?import?StandardScaler # 實例化方法 scaler?=?StandardScaler() glucose_z_score_standarScaler?=scaler.fit_transform(pima[['plasma_glucose_concentration']].fillna(-9)) # 可以看看轉換之后的均值和標準差是否為0和1 glucose_z_score_standarScaler.mean(),?glucose_z_score_standarScaler.std()# z分數標準化(全部特征) from?sklearn.preprocessing?import?StandardScaler # 實例化方法 scaler?=?StandardScaler() pima_imputed_mean_scaled?=?pd.DataFrame(scaler.fit_transform(pima_imputed_mean),columns=pima_columns) # 看下標準化之后的分布 pima_imputed_mean_scaled.hist(figsize=(15,15),?sharex=True)# 在Pipeline中使用 model?=?Pipeline([('imputer',?Imputer()),('standardize',?StandarScaler()) ])2)min-max標準化
min-max標準化和z-score類似,其公式為:(X - Xmin)/(Xmax - Xmin)
在python中的調用方法:
# min-max標準化 from?sklearn.preprocessing?import?MinMaxScaler # 實例化方法 min_max?=?MinMaxScaler() # 使用min-max標準化 pima_min_maxed?=?pd.DataFrame(min_max.fit_transform(pima.fillna(-9)),columns=pima_columns)3)行歸一化
行歸一化針對的是每一行數據,不同于上面的兩種方法(針對列),對行進行處理是為了保證每行的向量長度一樣(也就是單位范圍,unit norm),有L1、L2范數。
在python中的調用方法:
# 行歸一化 from?sklearn.preprocessing?import?Normalizer # 實例化方法 normalize?=?Normalizer() # 使用行歸一化 pima_normalized?=?pd.DataFrame(normalize.fit_transform(pima.fillna(-9)),columns=pima_columns) # 查看矩陣的平均范數(1) np.sqrt((pima_normalized**2).sum(axis=1)).mean()???? 03 特征構建
如果我們對變量進行處理之后,效果仍不是非常理想,就需要進行特征構建了,也就是衍生新變量。
而在這之前,我們需要了解我們的數據集,先前兩節中我們了解到了可以通過 data.info 和 data.describe() 來查看,同時結合數據等級(定類、定序、定距、定比)來理解變量。
???? 基礎操作
本小節中我們使用一個自定義數據集。
# 本次案例使用的數據集 import?pandas?as?pdX?=?pd.DataFrame({'city':['tokyo',None,'london','seattle','san fancisco','tokyo'],'boolean':['y','n',None,'n','n','y'],'ordinal_column':['somewhat like','like','somewhat like','like','somewhat like','dislike'],'quantitative_column':[1,11,-.5,10,None,20]}) X首先我們需要對分類變量進行填充操作,類別變量一般用眾數或者特殊值來填充,回顧之前的內容,我們也還是采取Pipeline的方式來進行,因此可以事先基于TransformMixin基類來對填充的方法進行封裝,然后直接在Pipeline中進行調用,代碼可以參考:
又或者利用 scikit-learn 的 Imputer類來實現填充,而這個類有一個 Strategy的方法自然就被繼承過來用了,包含的有mean、median、most_frequent可供選擇。
# 填充分類變量(基于Imputer的自定義填充器,用眾數填充) from?sklearn.preprocessing?import?Imputer class?CustomQuantitativeImputer(TransformerMixin):def?__init__(self,?cols=None,?strategy='mean'):self.cols?=?colsself.strategy?=?strategydef?transform(self,?df):X?=?df.copy()impute?=?Imputer(strategy=self.strategy)for?col?in?self.cols:X[col] =?impute.fit_transform(X[[col]])return?Xdef?fit(self,?*_):return?self# 調用自定義的填充器 cqi?=?CustomQuantitativeImputer(cols?= ['quantitative_column'],?strategy='mean') cqi.fit_transform(X)對上面的兩種填充進行流水線封裝:
# 全部填充 from?sklearn.pipeline?import?Pipelineimputer?=?Pipeline([('quant',cqi),('category',cci) ])imputer.fit_transform(X)完成了分類變量的填充工作,接下來就需要對分類變量進行編碼了(因為大多數的機器學習算法都是無法直接對類別變量進行計算的),一般有兩種辦法:獨熱編碼以及標簽編碼。
1)獨熱編碼
獨熱編碼主要是針對定類變量的,也就是不同變量值之間是沒有順序大小關系的,我們一般可以使用 scikit_learn 里面的 OneHotEncoding來實現的,但我們這里還是使用自定義的方法來加深理解。
# 類別變量的編碼(獨熱編碼) class?CustomDummifier(TransformerMixin):def?__init__(self,?cols=None):self.cols?=?colsdef?transform(self,?X):return?pd.get_dummies(X,?columns=self.cols)def?fit(self,?*_):return?self# 調用自定義的填充器 cd?=?CustomDummifier(cols=['boolean','city']) cd.fit_transform(X)2)標簽編碼
標簽編碼是針對定序變量的,也就是有順序大小的類別變量,就好像案例中的變量ordinal_column的值(dislike、somewhat like 和 like 可以分別用0、1、2來表示),同樣的可以寫個自定義的標簽編碼器:
# 類別變量的編碼(標簽編碼) class?CustomEncoder(TransformerMixin):def?__init__(self,?col,?ordering=None):self.ordering?=?orderingself.col?=?coldef?transform(self,?df):X?=?df.copy()X[self.col] =?X[self.col].map(lambda?x:?self.ordering.index(x))return?Xdef?fit(self,?*_):return?self# 調用自定義的填充器 ce?=?CustomEncoder(col='ordinal_column',?ordering=['dislike','somewhat like','like']) ce.fit_transform(X)3)數值變量分箱操作
以上的內容是對類別變量的一些簡單處理操作,也是比較常用的幾種,接下來我們就對數值變量進行一些簡單處理方法的講解。
有的時候,雖然變量值是連續的,但是只有轉換成類別才有解釋的可能,比如年齡,我們需要分成年齡段,這里我們可以使用pandas的 cut函數來實現。
# 數值變量處理——cut函數 class?CustomCutter(TransformerMixin):def?__init__(self,?col,?bins,?labels=False):self.labels?=?labelsself.bins?=?binsself.col?=?coldef?transform(self,?df):X?=?df.copy()X[self.col] =?pd.cut(X[self.col],?bins=self.bins,?labels=self.labels)return?Xdef?fit(self,?*_):return?self# 調用自定義的填充器 cc?=?CustomCutter(col='quantitative_column',?bins=3) cc.fit_transform(X)綜上,我們可以對上面自定義的方法一并在Pipeline中進行調用,Pipeline的順序為:
1)用imputer填充缺失值
2)獨熱編碼city和boolean
3)標簽編碼ordinal_column
4)分箱處理quantitative_column
代碼為:
from?sklearn.pipeline?import?Pipeline# 流水線封裝 pipe?=?Pipeline([('imputer',imputer),('dummify',cd),('encode',ce),('cut',cc) ])# 訓練流水線 pipe.fit(X)# 轉換流水線 pipe.transform(X)???? 數值變量擴展
這一小節我們使用一個新的數據集(人體胸部加速度數據集),我們先導入數據:
# 人體胸部加速度數據集,標簽activity的數值為1-7 ''' 1-在電腦前工作 2-站立、走路和上下樓梯 3-站立 4-走路 5-上下樓梯 6-與人邊走邊聊 7-站立著說話''' df?=?pd.read_csv('./data/activity_recognizer/1.csv',?header=None) df.columns?= ['index','x','y','z','activity'] df.head()這邊只介紹一種多項式生成新特征的辦法,調用PolynomialFeatures來實現。
# 擴展數值特征 from?sklearn.preprocessing?import?PolynomialFeaturesx?=?df[['x','y','z']] y?=?df['activity']poly?=?PolynomialFeatures(degree=2,?include_bias=False,?interaction_only=False)x_poly?=?poly.fit_transform(x) pd.DataFrame(x_poly,?columns=poly.get_feature_names()).head()還可以查看下衍生新變量后的相關性情況,顏色越深相關性越大:
# 查看熱力圖(顏色越深代表相關性越強) %matplotlib?inline import?seaborn?as?snssns.heatmap(pd.DataFrame(x_poly,?columns=poly.get_feature_names()).corr())在流水線中的實現代碼:
# 導入相關庫 from?sklearn.neighbors?import?KNeighborsClassifier from?sklearn.model_selection?import?GridSearchCV from?sklearn.pipeline?import?Pipelineknn?=?KNeighborsClassifier()# 在流水線中使用 pipe_params?= {'poly_features__degree':[1,2,3],'poly_features__interaction_only':[True,False],'classify__n_neighbors':[3,4,5,6]}# 實例化流水線 pipe?=?Pipeline([('poly_features',poly),('classify',knn)])# 網格搜索 grid?=?GridSearchCV(pipe,?pipe_params) grid.fit(x,y)print(grid.best_score_,?grid.best_params_)0.721189408065 {'classify__n_neighbors': 5, 'poly_features__degree': 2, 'poly_features__interaction_only': True}???? 文本變量處理
文本處理一般在NLP(自然語言處理)領域應用最為廣泛,一般都是需要把文本進行向量化,最為常見的方法有 詞袋(bag of words)、CountVectorizer、TF-IDF。
1)bag of words
詞袋法分成3個步驟,分別是分詞(tokenizing)、計數(counting)、歸一化(normalizing)。
2)CountVectorizer
將文本轉換為矩陣,每列代表一個詞語,每行代表一個文檔,所以一般出來的矩陣會是非常稀疏的,在sklearn.feature_extraction.text 中調用 CountVectorizer 即可使用。
3)TF-IDF
TF-IDF向量化器由兩個部分組成,分別為代表詞頻的TF部分,以及代表逆文檔頻率的IDF,這個TF-IDF是一個用于信息檢索和聚類的詞加權方法,在 sklearn.feature_extraction.text 中調用 TfidfVectorizer 即可。
TF:即Term Frequency,詞頻,也就是單詞在文檔中出現的頻率。
IDF:即Inverse Document Frequency,逆文檔頻率,用于衡量單詞的重要度,如果單詞在多份文檔中出現,就會被降低權重。
? 04 特征選擇
好了,經過了上面的特征衍生操作,我們現在擁有了好多好多的特征(變量)了,全部丟進去模型訓練好不好?當然是不行了????,這樣子既浪費資源又效果不佳,因此我們需要做一下 特征篩選 ,而特征篩選的方法大致可以分為兩大類:基于統計的特征篩選 和 基于模型的特征篩選。
在進行特征選擇之前,我們需要搞清楚一個概念:到底什么是更好的?有什么指標可以用來量化呢?
這大致也可以分為兩大類:一類是模型指標,比如accuracy、F1-score、R^2等等,還有一類是元指標,也就是指不直接與模型預測性能相關的指標,如:模型擬合/訓練所需的時間、擬合后的模型預測新實例所需要的時間、需要持久化(永久保存)的數據大小。
我們可以通過封裝一個方法,把上面提及到的指標封裝起來,方便后續的調用,代碼如下:
from?sklearn.model_selection?import?GridSearchCVdef?get_best_model_and_accuracy(model,?params,?x,?y):grid?=?GridSearchCV(model,params,error_score=0.)grid.fit(x,y)# 經典的性能指標print("Best Accuracy:{}".format(grid.best_score_))# 得到最佳準確率的最佳參數print("Best Parameters:{}".format(grid.best_params_))# 擬合的平均時間print("Average Time to Fit (s):{}".format(round(grid.cv_results_['mean_fit_time'].mean(),?3)))# 預測的平均時間print("Average Time to Score (s):{}".format(round(grid.cv_results_['mean_score_time'].mean(),?3)))############### 使用示例 ############### # 導入相關庫 from?sklearn.neighbors?import?KNeighborsClassifier from?sklearn.model_selection?import?GridSearchCV from?sklearn.pipeline?import?Pipelineknn?=?KNeighborsClassifier()# 在流水線中使用 pipe_params?= {'poly_features__degree':[1,2,3],'poly_features__interaction_only':[True,False],'classify__n_neighbors':[3,4,5,6]}# 實例化流水線 pipe?=?Pipeline([('poly_features',poly),('classify',knn)])# 網格搜索 get_best_model_and_accuracy(pipe,?pipe_params,?x,?y)通過上面的操作,我們可以創建一個模型性能基準線,用于對比后續優化的效果。接下來介紹一些常用的特征選擇方法。
1)基于統計的特征選擇
針對于單變量,我們可以采用 皮爾遜相關系數以及假設檢驗 來選擇特征。
(1)皮爾遜相關系數可以通過 corr() 來實現,返回的值在-1到1之間,絕對值越大代表相關性越強;
(2)假設檢驗也就是p值,作為一種統計檢驗,在特征選擇中,假設測試得原則是:” 特征與響應變量沒有關系“(零假設)為真還是假。我們需要對每個變量進行檢測,檢測其與target有沒有顯著關系。可以使用 SelectKBest 和 f_classif 來實現。一般P值是介于0-1之間,簡而言之,p值越小,拒絕零假設的概率就越大,也就是這個特征與target關系更大。
2)基于模型的特征選擇
(1)對于文本特征,sklearn.feature_extraction.text里的 CountVectorizer有自帶的特征篩選的參數,分別是 max_features、min_df、max_df、stop_words,可以通過搜索這些參數來進行特征選擇,可以結合 SelectKBest 來實現流水線。
(2)針對????樹模型,我們可以直接調用不同樹模型算法里的 特征重要度 來返回特征重要度,比如 DecisionTreeClassifier里的feature_importances_,(除此之外還有RandomForest、GBDT、XGBoost、ExtraTreesClassifier等等)都可以直接返回每個特征對于本次擬合的重要度,從而我們可以剔除重要度偏低的特征,可以結合 SelectFromModel來實現流水線。
(3)使用正則化來篩選變量(針對線性模型)。有兩種常用的正則化方法:L1正則化(Lasso)和L2正則化(嶺)。
總結一下,有幾點做特征選擇的方法經驗:
(1)如果特征是分類變量,那么可以從SelectKBest開始,用卡方或者基于樹的選擇器來選擇變量;
(2)如果特征是定量變量,可以直接用線性模型和基于相關性的選擇器來選擇變量;
(3)如果是二分類問題,可以考慮使用 SelectFromModel和SVC;
(4)在進行特征選擇前,還是需要做一下EDA。
???? 05 特征轉換
經過了上面幾個環節的“洗禮”,我們來到特征轉換的環節,也就是使用源數據集的隱藏結構來創建新的列,常用的辦法有2種:PCA和LDA。
? PCA:
PCA,即主成分分析(Principal Components Analysis),是比較常見的數據壓縮的辦法,即將多個相關特征的數據集投影到相關特征較少的坐標系上。也就是說,轉換后的特征,在解釋性上就走不通了,因為你無法解釋這個新變量到底具有什么業務邏輯了。
PCA的原理這里就不展開來講了,太多的文章把它講得十分透徹了。這里主要是復現一下PCA在sklearn上的調用方法,一來繼續熟悉下Pipeline的使用,二來理解一下PCA的使用方法。
# 導入相關庫 from?sklearn.datasets?import?load_iris import?matplotlib.pyplot?as?plt %matplotlib?inlinefrom?sklearn.decomposition?import?PCA# 導入數據集 iris?=?load_iris() iris_x,?iris_y?=?iris.data,?iris.target# 實例化方法 pca?=?PCA(n_components=2) # 訓練方法 pca.fit(iris_x) pca.transform(iris_x)[:5,]# 自定義一個可視化的方法 label_dict?= {i:k?for?i,k?in?enumerate(iris.target_names)} def?plot(x,y,title,x_label,y_label):ax?=?plt.subplot(111)for?label,marker,color?in?zip(range(3),('^','s','o'),('blue','red','green')):plt.scatter(x=x[:,0].real[y?==?label],y?=?x[:,1].real[y?==?label],color?=?color,alpha?=?0.5,label?=?label_dict[label])plt.xlabel(x_label)plt.ylabel(y_label)leg?=?plt.legend(loc='upper right',?fancybox=True)leg.get_frame().set_alpha(0.5)plt.title(title)# 可視化 plot(iris_x,?iris_y,"original iris data","sepal length(cm)","sepal width(cm)") plt.show()plot(pca.transform(iris_x),?iris_y,"Iris: Data projected onto first two PCA components","PCA1","PCA2")以上是PCA在sklearn上的簡單調用和效果展示,另外,作者提出了一個很有意思的問題:
一般而言,對特征進行歸一化處理后會對機器學習算法的效果有比較明顯的幫助,但為什么在書本的例子卻是相反呢?
給出的解釋是:在對數據進行縮放后,列與列之間的協方差會更加一致,而且每個主成分解釋的方差會變得分散,而不是集中在某一個主成分上。所以,在實際操作的時候,都要對縮放的未縮放的數據進行性能測試才是最穩妥的哦。
? LDA:
LDA,即線性判別分析(Linear Discriminant Analysis),它是一個有監督的算法(哦對了, PCA是無監督的),一般是用于分類流水線的預處理步驟。與PCA類似,LDA也是提取出一個新的坐標軸,將原始的高維數據投影到低維空間去,而區別在于LDA不會去專注數據之間的方差大小,而是直接優化低維空間,以獲得最佳的類別可分性。
# LDA的使用 # 導入相關庫 from?sklearn.discriminant_analysis?import?LinearDiscriminantAnalysis # 實例化LDA模塊 lda?=?LinearDiscriminantAnalysis(n_components=2) # 訓練數據 x_lda_iris?=?lda.fit_transform(iris_x,?iris_y) # 可視化 plot(x_lda_iris,?iris_y,?"LDA Projection",?"LDA1",?"LDA2")???? 06 特征學習
來到最后一章了,這章的主題是“以AI促AI”。看起來還蠻抽象的,反正我是覺得有點奇怪,特征學習算法是非參數方法,也就是不依賴數據結構而構建出來的新算法。
???? 數據的參數假設
參數假設指的是算法對數據形狀的基本假設。比如上一章的PCA,我們是假設:
原始數據的形狀可以被(特征值)分解,并且可以用單個線性變換(矩陣計算)表示。
而特征學習算法,就是要去除這個“假設”來解決問題,因為這算法不會依賴數據的形狀,而是依賴于隨機學習(Stochastic Learning),指的是這些算法并不是每次輸出相同的結果,而是一次次按輪(epoch)去檢查數據點以找到要提取的最佳特征,并且可以擬合出一個最優的解決方法。
而在特征學習領域,有兩種方法是比較常用的,也是下面來講解的內容:受限玻爾茲曼機(RBM)和詞嵌入。
???? 受限玻爾茲曼機(RBM)
RBM是一種簡單的深度學習架構,是一組無監督的特征學習算法,根據數據的概率模型學習一定數量的新特征,往往使用RBM之后去用線性模型(線性回歸、邏輯回歸、感知機等)的效果極佳。
從概念上說,RBM是一個淺層(2層)的神經網絡,屬于深度信念網絡(DBN,deep belief network)算法的一種。它也是一種無監督算法,可以學習到的 特征數量只受限于計算能力,它可能學習到比原始要少或者多的特征,具體要學習的特征數量取決于要解決的問題。
“受限”的說法是因為它只允許層與層之間的連接(層間連接),而不允許同一層內的節點連接(層內連接)。
在這里需要理解一下“重建”(Reconstruction),也就是這個操作,使得在不涉及更深層網絡的情況下,可見層(輸入層)和隱含層之間可以存在數次的前向和反向傳播。
在重建階段,RBM會反轉網絡,可見層變成了隱含層,隱含層變成了可見層,用相同的權重將激活變量a反向傳遞到可見層,但是偏差不一樣,然后用前向傳導的激活變量重建原始輸入向量。RBM就是用這種方法來進行“自我評估”的,通過將激活信息進行反向傳導并獲取原始輸入的近似值,該網絡可以調整權重,讓近似值更加接近原始輸入。
在訓練開始時,由于權重是隨機初始化的(一般做法),近似值與真實值的差異可能會極大的,接下來就會通過反向傳播的方法來調整權重,最小化原始輸入與近似值的距離,一直重復這個過程,直到近似值盡可能接近原始輸入。(這個過程發生的次數叫 迭代次數 )
大致的原理就是上面的說法了,更加詳細的解釋可以自行百度哦。下面我們來講講RBM在機器學習管道中的應用,我們還是使用MNIST數據集,這個數據集在之前講Keras的時候(傳送門)也用到了,就是一堆數字的像素點數據,然后用來識別數字。
# RBM的使用 # 我們使用MNIST數據集來講解 # 導入相關庫 import?numpy?as?np import?matplotlib.pyplot?as?plt %matplotlib?inlinefrom?sklearn.linear_model?import?LogisticRegression from?sklearn.neural_network?import?BernoulliRBM from?sklearn.pipeline?import?Pipeline# 導入數據集 images?=?np.genfromtxt('./data/mnist_train.csv',?delimiter=',') print(images.shape) # 劃分數據 images_x,?images_y?=?images[:,1:],?images[:,0]# 縮放特征到0-1 images_x?=?images_x/255.# 用RBM學習新特征 rbm?=?BernoulliRBM(random_state=0) lr?=?LogisticRegression()# 設置流水線的參數范圍 params?= {'clf__C':[1e-1,?1e0,?1e1],'rbm__n_components':[100,?200]} # 創建流水線 pipeline?=?Pipeline([('rbm',?rbm),('clf',?lr)]) # 實例化網格搜索類 grid?=?GridSearchCV(pipeline,?params) # 擬合數據 grid.fit(images_x,?images_y) # 返回最佳參數 grid.best_params_,?grid.best_score_???? 詞嵌入
在NLP領域應用極為廣泛了,它可以將字符串(單詞或短語)投影到n維特征集中,以便理解上下文和措辭的細節,我們可以使用sklearn中的CountVectorizer 和 TfidfVectorizer 來將這些字符串進行轉為向量,但這只是一些單詞特征的集合而已,為了理解這些特征,我們更加要關注一個叫 gensim的包。
常用的詞嵌入方法有兩種:Word2vec和GloVe。
Word2vec:Google發明的一種基于深度學習的算法。Word2vec也是一個淺層的神經網絡,含有輸入層、隱含層和輸出層,其中輸入層和輸出層的節點個數一樣。
GloVe:來自斯坦福大學的算法,通過一系列矩陣統計進行學習。
詞嵌入的應用很多,比如信息檢索,意思是當我們輸入關鍵詞時,搜索引擎可以回憶并準確返回和關鍵詞匹配的文章或者新聞。
完整代碼請在作者公眾號回復“特征工程”下載。
PS:小編還是覺得買本來看比較好,寫得實在不錯~
機器學習初學者
黃海廣博士創建的公眾號,黃海廣博士個人知乎粉絲22000+,github排名全球前120名(31000+)。本公眾號致力于人工智能方向的科普性文章,為初學者提供學習路線和基礎資料。原創作品有:吳恩達機器學習個人筆記、吳恩達深度學習筆記等。
往期精彩回顧
那些年做的學術公益-你不是一個人在戰斗
適合初學者入門人工智能的路線及資料下載
吳恩達機器學習課程筆記及資源(github標星12000+,提供百度云鏡像)
吳恩達深度學習筆記及視頻等資源(github標星8500+,提供百度云鏡像)
《統計學習方法》的python代碼實現(github標星7200+)
精心整理和翻譯的機器學習的相關數學資料
首發:深度學習入門寶典-《python深度學習》原文代碼中文注釋版及電子書
備注:加入本站微信群或者qq群,請回復“加群”
加入知識星球(4100+用戶,ID:92416895),請回復“知識星球”
總結
以上是生活随笔為你收集整理的手把手教你入门和实践特征工程 的全方位万字笔记,附代码下载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 首发:适合初学者入门人工智能的路线及资料
- 下一篇: 谈谈实习这件小事