手把手教你入門和實踐特徵工程 的全方位萬字筆記,附代碼下載

🙊 提及特徵工程,都說是機器學習建模中最爲重要並且費時的一項工做,並且它涉及的知識點會很是地多,經驗老道的老司機天然是輕車熟路了,但對於剛剛入門的新手司機,學習到的知識點都是東一點西一點的,不夠系統化,本篇文章是在閱讀了一本評分極高的特徵工程書籍 📚 《特徵工程入門與實踐》後的一篇筆記文,記錄下相對比較系統的知識點以及可運行復現的代碼,但願對各位同行有所幫助哈。

file

🚗 目錄

  • 🔍 特徵理解
  • 🔋 特徵加強
  • 🔨 特徵構建
  • ✅ 特徵選擇
  • 💫 特徵轉換
  • 📖 特徵學習

你們能夠先看下思惟導圖:python

file

🔍 01 特徵理解

在拿到數據的時候,咱們第一步須要作的是理解它,通常咱們能夠從下面幾個角度入手:算法

(注:本節用到了兩個數據集,分別是Salary_Ranges_by_Job_Classification 和 GlobalLandTemperaturesByCity)網絡

1. 區分結構化數據與非結構化數據

如一些以表格形式進行存儲的數據,都是結構化數據;而非結構化數據就是一堆數據,相似於文本、報文、日誌之類的。

2. 區分定量和定性數據

定量數據:指的是一些數值,用於衡量某件東西的數量;

定性數據:指的是一些類別,用於描述某件東西的性質。數據結構

其實區分了定量和定性數據,還能夠繼續細分下去,分爲定類(nominal)、定序(ordinal)、定距(interval)、定比數據(ratio),下面咱們分別對這4類數據進行舉例說明,加深你們對它們的印象。架構

1)定類(nominal)dom

也就是分類,好比:血型(A/B/O/AB型)、性別(男/女)、貨幣(人民幣/美圓/日元),並且值得注意的是這些分類之間沒有大小可比性。通常畫圖的話就只能看下分佈佔比,能夠用條形圖、餅圖來表示。機器學習

file

2)定序(ordinal)函數

定序相比於定類,也就是多了一個「可排序」的屬性,也就是說雖然它們是類別變量,可是它們的變量值之間是存在「大小」之分的。好比:期末績點(A、B、C、D、E、F)、問卷答案(很是滿意、滿意、通常、不滿意)。可視化方面,和定類同樣,不過就是多了一個 箱體圖 能夠用(由於定序變量能夠有中位數)。性能

file

3)定距(interval)學習

定距的話,就是變量值之間能夠作加減法計算,也就是能夠引入均值、方差之類的名詞了,並且可以畫的圖也多了,包括先前的那些,還包括了直方圖。

file

4)定比(ratio)

定比相比於定距更加嚴格,不只僅有定距的全部屬性,同時,有一個 絕對零點 的概念,能夠作加減乘除運算,好比說某個商品的價格是另外一個的2倍。值得注意的是,溫度通常不納入定比,而是定距,沒有說20度是10度的兩倍這種說法。

file

最後把上面的內容總結一下:

file

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() 的個數,不過須要注意的是,可能統計出來沒有缺失,並非由於真的沒有缺失,並且缺失被人用某個特殊值填充了,通常會用 -九、blank、unknown、0之類的,須要注意⚠️識別,後面須要對缺失進行合理填充

(2.1)怎麼識別缺失值呢?通常能夠經過 data.describe() 獲取基本的描述性統計,根據均值、標準差、極大極小值等指標,結合變量含義來判斷。

file

(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]) 就能夠統計出來了。固然,刪除以後,咱們還須要看看數據的分佈,對比目標占比、特徵分佈與先前的是否存在明顯差別,若是是的話,建議不要使用這種辦法。

file

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.73177

Step3: 標準化和歸一化

通過上面的處理,模型的精度能夠達到0.73177,但咱們能夠繼續優化嗎?那是確定的。

咱們能夠先看看全部特徵的分佈(特徵少的時候能夠這麼看):

pima_imputed_mean.hist(figsize=(15,15))

file

從上圖中咱們能夠看出一個問題,那就是每一個特徵之間的量綱都是不同的,這對於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),有L一、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.infodata.describe() 來查看,同時結合數據等級(定類、定序、定距、定比)來理解變量。

🙊 基礎操做

本小節中咱們使用一個自定義數據集。

# 本次案例使用的數據集
import pandas as pd

X = 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

file

首先咱們須要對分類變量進行填充操做,類別變量通常用衆數或者特殊值來填充,回顧以前的內容,咱們也仍是採起Pipeline的方式來進行,所以能夠事先基於TransformMixin基類來對填充的方法進行封裝,而後直接在Pipeline中進行調用,代碼能夠參考:

# 填充分類變量(基於TransformerMixin的自定義填充器,用衆數填充)
from sklearn.base import TransformerMixin

class CustomCategoryzImputer(TransformerMixin):
    def __init__(self, cols=None):
        self.cols = cols
        
    def transform(self, df):
        X = df.copy()
        for col in self.cols:
            X[col].fillna(X[col].value_counts().index[0], inplace=True)
        return X
    
    def fit(self, *_):
        return self   
    
    
# 調用自定義的填充器
cci = CustomCategoryzImputer(cols=['city','boolean'])
cci.fit_transform(X)

file

又或者利用 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 = cols
        self.strategy = strategy
        
    def transform(self, df):
        X = df.copy()
        impute = Imputer(strategy=self.strategy)
        for col in self.cols:
            X[col] = impute.fit_transform(X[[col]])
        return X
    
    def fit(self, *_):
        return self
    
    
# 調用自定義的填充器
cqi = CustomQuantitativeImputer(cols = ['quantitative_column'], strategy='mean')
cqi.fit_transform(X)

file

對上面的兩種填充進行流水線封裝:

# 所有填充
from sklearn.pipeline import Pipeline

imputer = Pipeline([('quant',cqi),
                    ('category',cci)
])

imputer.fit_transform(X)

完成了分類變量的填充工做,接下來就須要對分類變量進行編碼了(由於大多數的機器學習算法都是沒法直接對類別變量進行計算的),通常有兩種辦法:獨熱編碼以及標籤編碼。

1)獨熱編碼

獨熱編碼主要是針對定類變量的,也就是不一樣變量值之間是沒有順序大小關係的,咱們通常可使用 scikit_learn 裏面的 OneHotEncoding來實現的,但咱們這裏仍是使用自定義的方法來加深理解。

# 類別變量的編碼(獨熱編碼)
class CustomDummifier(TransformerMixin):
    def __init__(self, cols=None):
        self.cols = cols
        
    def 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)

file

2)標籤編碼

標籤編碼是針對定序變量的,也就是有順序大小的類別變量,就好像案例中的變量ordinal_column的值(dislike、somewhat like 和 like 能夠分別用0、一、2來表示),一樣的能夠寫個自定義的標籤編碼器:

# 類別變量的編碼(標籤編碼)
class CustomEncoder(TransformerMixin):
    def __init__(self, col, ordering=None):
        self.ordering = ordering
        self.col = col
        
    def transform(self, df):
        X = df.copy()
        X[self.col] = X[self.col].map(lambda x: self.ordering.index(x))
        return X
    
    def fit(self, *_):
        return self
    

# 調用自定義的填充器
ce = CustomEncoder(col='ordinal_column', ordering=['dislike','somewhat like','like'])
ce.fit_transform(X)

file

3)數值變量分箱操做

以上的內容是對類別變量的一些簡單處理操做,也是比較經常使用的幾種,接下來咱們就對數值變量進行一些簡單處理方法的講解。

有的時候,雖然變量值是連續的,可是隻有轉換成類別纔有解釋的可能,好比年齡,咱們須要分紅年齡段,這裏咱們可使用pandas的 cut函數來實現。

# 數值變量處理——cut函數
class CustomCutter(TransformerMixin):
    def __init__(self, col, bins, labels=False):
        self.labels = labels
        self.bins = bins
        self.col = col
        
    def transform(self, df):
        X = df.copy()
        X[self.col] = pd.cut(X[self.col], bins=self.bins, labels=self.labels)
        return X
    
    def fit(self, *_):
        return self
    

# 調用自定義的填充器
cc = CustomCutter(col='quantitative_column', bins=3)
cc.fit_transform(X)

file

綜上,咱們能夠對上面自定義的方法一併在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()

file

這邊只介紹一種多項式生成新特徵的辦法,調用PolynomialFeatures來實現。

# 擴展數值特徵
from sklearn.preprocessing import PolynomialFeatures

x = 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()

file

還能夠查看下衍生新變量後的相關性狀況,顏色越深相關性越大:

# 查看熱力圖(顏色越深表明相關性越強)
%matplotlib inline
import seaborn as sns

sns.heatmap(pd.DataFrame(x_poly, columns=poly.get_feature_names()).corr())

file

在流水線中的實現代碼:

# 導入相關庫
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline

knn = 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 GridSearchCV

def 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 Pipeline

knn = 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有沒有顯著關係。可使用 SelectKBestf_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 inline

from 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")

file

以上是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")

file

📖 06 特徵學習

來到最後一章了,這章的主題是「以AI促AI」。看起來還蠻抽象的,反正我是以爲有點奇怪,特徵學習算法是非參數方法,也就是不依賴數據結構而構建出來的新算法。

🙊 數據的參數假設

參數假設指的是算法對數據形狀的基本假設。好比上一章的PCA,咱們是假設:

原始數據的形狀能夠被(特徵值)分解,而且能夠用單個線性變換(矩陣計算)表示。

而特徵學習算法,就是要去除這個「假設」來解決問題,由於這算法不會依賴數據的形狀,而是依賴於隨機學習(Stochastic Learning),指的是這些算法並非每次輸出相同的結果,而是一次次按輪(epoch)去檢查數據點以找到要提取的最佳特徵,而且能夠擬合出一個最優的解決方法。

而在特徵學習領域,有兩種方法是比較經常使用的,也是下面來說解的內容:受限玻爾茲曼機(RBM)和詞嵌入。

🙊 受限玻爾茲曼機(RBM)

RBM是一種簡單的深度學習架構,是一組無監督的特徵學習算法,根據數據的機率模型學習必定數量的新特徵,每每使用RBM以後去用線性模型(線性迴歸、邏輯迴歸、感知機等)的效果極佳。

從概念上說,RBM是一個淺層(2層)的神經網絡,屬於深度信念網絡(DBN,deep belief network)算法的一種。它也是一種無監督算法,能夠學習到的 特徵數量只受限於計算能力,它可能學習到比原始要少或者多的特徵,具體要學習的特徵數量取決於要解決的問題。

file

「受限」的說法是由於它只容許層與層之間的鏈接(層間鏈接),而不容許同一層內的節點鏈接(層內鏈接)。

file

在這裏須要理解一下「重建」(Reconstruction),也就是這個操做,使得在不涉及更深層網絡的狀況下,可見層(輸入層)和隱含層之間能夠存在數次的前向和反向傳播。

在重建階段,RBM會反轉網絡,可見層變成了隱含層,隱含層變成了可見層,用相同的權重將激活變量a反向傳遞到可見層,可是誤差不同,而後用前向傳導的激活變量重建原始輸入向量。RBM就是用這種方法來進行「自我評估」的,經過將激活信息進行反向傳導並獲取原始輸入的近似值,該網絡能夠調整權重,讓近似值更加接近原始輸入。

在訓練開始時,因爲權重是隨機初始化的(通常作法),近似值與真實值的差別可能會極大的,接下來就會經過反向傳播的方法來調整權重,最小化原始輸入與近似值的距離,一直重複這個過程,直到近似值儘量接近原始輸入。(這個過程發生的次數叫 迭代次數

大體的原理就是上面的說法了,更加詳細的解釋能夠自行百度哦。下面咱們來說講RBM在機器學習管道中的應用,咱們仍是使用MNIST數據集,這個數據集在以前講Keras的時候也用到了,就是一堆數字的像素點數據,而後用來識別數字。

# RBM的使用
# 咱們使用MNIST數據集來說解
# 導入相關庫
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

from 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中的CountVectorizerTfidfVectorizer 來將這些字符串進行轉爲向量,但這只是一些單詞特徵的集合而已,爲了理解這些特徵,咱們更加要關注一個叫 gensim的包。

經常使用的詞嵌入方法有兩種:Word2vec和GloVe。

Word2vec: Google發明的一種基於深度學習的算法。Word2vec也是一個淺層的神經網絡,含有輸入層、隱含層和輸出層,其中輸入層和輸出層的節點個數同樣。

GloVe: 來自斯坦福大學的算法,經過一系列矩陣統計進行學習。

詞嵌入的應用不少,好比信息檢索,意思是當咱們輸入關鍵詞時,搜索引擎能夠回憶並準確返回和關鍵詞匹配的文章或者新聞。

本文由博客一文多發平臺 OpenWrite 發佈!
相關文章
相關標籤/搜索