1. 程式人生 > >整合演算法(ensemble learning)--競賽和論文神器

整合演算法(ensemble learning)--競賽和論文神器

就拿決策樹來說,比如如果一顆決策樹效果不行,就用多顆決策樹,這樣就構成隨機森林。

目的:讓機器學習效果更好,單個不行,就用多個一起。

整合演算法一、

bagging演算法:並行訓練多個M模型(如決策樹)取平均或者其他方式彙總,如果就拿決策樹來說,訓練M顆決策樹取預測資料,就會有M個結果,把這M個結果取平均來做最後的結果。

典型:“隨機森林”,並行訓練多個樹,各個獨立,各個樹之間有些許差別,如預測一個人的成績,把資料輸入隨機森林中,會有多個結果,最後去個平均來做最終預測的成績。比如用來做分類,如做把資料把很多資料輸入隨機森林,發現大部分在a類,最終預測結果取a類。

森林的意思是有多顆決策樹並行放在一起,隨機的意思指資料取樣隨機(比如有100個數據去訓練有三顆決策樹隨機森林,我們有放回的隨機取60個取分別訓練各個決策樹),特徵選擇隨機(比如每個資料都有十個特徵,每顆數隨機取6個特徵)。

隨機森林的優勢:

1、它能夠處理很高維度的資料(因為可以特徵隨機取樣),並且不用做特徵選擇,因為特徵很多的時候或許需要做降維處理,隨機森林就有優勢了。

2、訓練完後,能夠給出哪些特徵比較重要。

關於特徵評估:特徵評估1、特徵評估2

隨機森林之特徵選擇:

from sklearn.datasets import load_wine
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import numpy as np

data=load_wine()
print(data.data.shape)#資料有178個,每個資料有13個特徵
print(data.target.shape)#資料有178個
x_train,x_test,y_train,y_test=train_test_split(data.data,data.target,test_size=0.3,random_state=22)
forest=RandomForestClassifier(n_estimators=10,n_jobs=-1,random_state=9)#10顆數,-1表示適用於所有處理器,隨機種子為9
forest.fit(x_train,y_train)

importances=forest.feature_importances_#隨機森林特徵的重要性
print('每個特徵對應的重要性因子:\n',importances)
indices1 = np.argsort(importances)# a[::-1]讓矩陣中資料逆序輸出,np.argsort是按照importance中資料中數值從小到大排序
indices2 = np.argsort(importances)[::-1]# a[::-1]讓矩陣中資料逆序輸出,np.argsort是按照importance中資料中數值從大到小排序
print('特徵重要程度從小到大排序的序號:\n',indices1)
print('特徵重要程度從大到小排序的序號:\n',indices2)
most_import = indices2[:3]#取最總要的3個
print(x_train[:,most_import])


3、容易做成並行方法,速度比較快,比如可以用處理器的多個核並行訓練。

4、可以進行視覺化展示,便於分析。

整合演算法二、

boosting演算法:從弱學習器開始加強,通過加權來進行訓練,串聯的方法,多個樹求和得來,每一次加入決策樹都提升了原模型。

比如需要預測一個東西,這個東西為最好1000,比如我們分三棵樹去預測,用主樹去預測得出預測值為950,第二顆樹拿1000-950去預測,結果為30,第三課樹拿20去預測,結果為18,把這三個預測值加起來950+30+18=998得到最終總的預測值,這樣的預測比只有一顆樹預測值為950更好,因為第二顆樹的在第一顆樹的基礎上再去預測,第三顆樹在第二顆數的基礎上再去預測,所以不能做並行處理,只能序列。

說白了就是先多第一步預測,看差多少,用第二個模型去預測殘差,每次預測後再往裡加一個模型。

典型代表:AdaBoost,Xgboost。

AdaBoost:會根據前一次的分類效果調整資料權重,比如有五棵1,2,3,4,5樹模型,把樣本平均分為五分,權重為0.2,0.2,0.2,0.2,0.2,分別取訓練和預測,發現第1,2,4,5分類比較準確,第3分類不太準備,那我們調整樣本權重,權重分別為0.1,0.1,0.6,0.1,01去訓練,發現1,2,3樹模型比較準確,4,5不太準確,再調整...最終訓練出的每一顆決策樹都是比較好。然後拿出最終的合體決策樹取做最終預測。

其他說法:

例項:

以下例子先對特徵進行人工分析,然後選擇其中7個特徵進行訓練

import pandas as pd
from sklearn.model_selection import GridSearchCV
import numpy as np

def load_data_and_preprocessing():
    train = pd.read_csv('./data/titanic_train.csv')
    test = pd.read_csv('./data/test.csv')
    print(train['Name'])
    print(train.describe())
    print(train.head())
    print(train.info())
    train_y = train['Survived']
    selected_features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']#我們可以看到,捨棄了一些特徵
    train_x = train[selected_features]
    train_x['Age'].fillna(train_x['Age'].mean(), inplace=True)  # 以均值填充空缺值
    print(train_x['Age'])
    print(train_x['Embarked'].value_counts())
    train_x['Embarked'].fillna('S', inplace=True)#空缺值用S填充
    # print(train_x.info())

    test_x = test[selected_features]
    test_x['Age'].fillna(test_x['Age'].mean(), inplace=True)
    test_x['Fare'].fillna(test_x['Fare'].mean(), inplace=True)
    # print(test_x.info())

    train_x.loc[train_x['Embarked'] == 'S', 'Embarked'] = 0
    train_x.loc[train_x['Embarked'] == 'C', 'Embarked'] = 1
    train_x.loc[train_x['Embarked'] == 'Q', 'Embarked'] = 2
    train_x.loc[train_x['Sex'] == 'male', 'Sex'] = 0
    train_x.loc[train_x['Sex'] == 'female', 'Sex'] = 1
    print(train_x)
    x_train = train_x.as_matrix()#轉換為矩陣,也就是去除索引標籤
    print(x_train)
    y_train = train_y.as_matrix()

    test_x.loc[test_x['Embarked'] == 'S', 'Embarked'] = 0
    test_x.loc[test_x['Embarked'] == 'C', 'Embarked'] = 1
    test_x.loc[test_x['Embarked'] == 'Q', 'Embarked'] = 2
    test_x.loc[test_x['Sex'] == 'male', 'Sex'] = 0
    test_x.loc[test_x['Sex'] == 'female', 'Sex'] = 1
    x_test = test_x
    print(x_train)
    return x_train, y_train, x_test


def logistic_regression():#用邏輯迴歸演算法來預測
    from sklearn.linear_model import LogisticRegression
    x_train, y_train, x_test = load_data_and_preprocessing()
    model = LogisticRegression()
    paras = {'C': np.linspace(0.1, 10, 50)}#正則化強度逆
    gs = GridSearchCV(model, paras, cv=5, verbose=3)
    gs.fit(x_train, y_train)
    print('best score:', gs.best_score_)
    print('best parameters:', gs.best_params_)


def decision_tree():#用決策樹來預測
    from sklearn.tree import DecisionTreeClassifier
    x_train, y_train, x_test = load_data_and_preprocessing()
    model = DecisionTreeClassifier()
    paras = {'criterion': ['gini', 'entropy'], 'max_depth': np.arange(5, 50, 5)}#引數是用基尼係數還是資訊增益
    gs = GridSearchCV(model, paras, cv=5, verbose=3)
    gs.fit(x_train, y_train)
    print('best score:', gs.best_score_)
    print('best parameters:', gs.best_params_)


def random_forest():#用隨機森林來預測
    from sklearn.ensemble import RandomForestClassifier
    x_train, y_train, x_test = load_data_and_preprocessing()
    model = RandomForestClassifier()
    paras = {'n_estimators': np.arange(10, 100, 10), 'criterion': ['gini', 'entropy'], 'max_depth': np.arange(5, 50, 5)}
    gs = GridSearchCV(model, paras, cv=5, verbose=3)
    gs.fit(x_train, y_train)
    print('best score:', gs.best_score_)
    print('best parameters:', gs.best_params_)


def gradient_boosting():
    from sklearn.ensemble import GradientBoostingClassifier
    x_train, y_train, x_test = load_data_and_preprocessing()
    model = GradientBoostingClassifier()
    paras = {'learning_rate': np.arange(0.1, 1, 0.1), 'n_estimators': range(80, 120, 10), 'max_depth': range(5, 10, 1)}#引數分別為學習速率,決策樹的數目,樹的最大深度
    gs = GridSearchCV(model, paras, cv=5, verbose=3,n_jobs=2)#cv表交叉迭代數,verbose表顯示訓練過程,n_jobs表並行運算核數
    gs.fit(x_train, y_train)
    print('best score:', gs.best_score_)#平均交叉驗證得分
    print('best parameters:', gs.best_params_)


if __name__ == '__main__':
    #logistic_regression()  # 0.7979
   # decision_tree()#0.813
   # random_forest()  # 0.836  {'criterion': 'entropy', 'max_depth': 10, 'n_estimators': 60}
    gradient_boosting()#0.830  {'learning_rate': 0.1, 'max_depth': 5, 'n_estimators': 90}

資料:泰坦尼克號資料  提取碼: wvmf

以下例子先對特徵進行評估,然後選擇合適的特徵進行訓練:

import pandas as pd
import numpy as np
from sklearn.model_selection import GridSearchCV

def feature_selection():
    from sklearn.feature_selection import SelectKBest, f_classif
    import matplotlib.pyplot as plt
    train = pd.read_csv('./data/titanic_train.csv')
    selected_features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']#特徵的數量為7
    train_x = train[selected_features]
    train_y = train['Survived']
    train_x['Age'].fillna(train_x['Age'].mean(), inplace=True)  # 以均值填充
    train_x['Embarked'].fillna('S', inplace=True)
    train_x.loc[train_x['Embarked'] == 'S', 'Embarked'] = 0
    train_x.loc[train_x['Embarked'] == 'C', 'Embarked'] = 1
    train_x.loc[train_x['Embarked'] == 'Q', 'Embarked'] = 2
    train_x.loc[train_x['Sex'] == 'male', 'Sex'] = 0
    train_x.loc[train_x['Sex'] == 'female', 'Sex'] = 1

    selector = SelectKBest(f_classif, k=5)#k為要選擇特徵的數量,預設第一個引數為f_classif
    selector.fit(train_x, train_y)#擬合訓練資料
    scores = selector.scores_#給各個特徵打分的分數
    plt.bar(range(len(selected_features)), scores)
    plt.xticks(range(len(selected_features)), selected_features,rotation='horizontal')
    plt.show()

    x_train = train_x[['Pclass', 'Sex', 'Fare']]#人工選擇特徵,從影象上可以看到這三個特徵比較重要,所以我們選擇這三個特徵
    y_train = train_y.as_matrix()
    train_x = x_train.as_matrix()
    return train_x, y_train
def logistic_regression():
    from sklearn.linear_model import LogisticRegression
    x_train, y_train= feature_selection()
    model = LogisticRegression()
    paras = {'C': np.linspace(0.1, 10, 50)}
    gs = GridSearchCV(model, paras, cv=5, verbose=3)
    gs.fit(x_train, y_train)
    print('best score:', gs.best_score_)
    print('best parameters:', gs.best_params_)


def decision_tree():
    from sklearn.tree import DecisionTreeClassifier
    x_train, y_train = feature_selection()
    model = DecisionTreeClassifier()
    paras = {'criterion': ['gini', 'entropy'], 'max_depth': np.arange(5, 50, 5)}
    gs = GridSearchCV(model, paras, cv=5, verbose=3)
    gs.fit(x_train, y_train)
    print('best score:', gs.best_score_)
    print('best parameters:', gs.best_params_)


def random_forest():
    from sklearn.ensemble import RandomForestClassifier
    x_train, y_train = feature_selection()
    model = RandomForestClassifier()
    paras = {'n_estimators': np.arange(10, 100, 10), 'criterion': ['gini', 'entropy'], 'max_depth': np.arange(5, 50, 5)}
    gs = GridSearchCV(model, paras, cv=5, verbose=3)
    gs.fit(x_train, y_train)
    print('best score:', gs.best_score_)
    print('best parameters:', gs.best_params_)

if __name__ == '__main__':
    # feature_selection()
    # logistic_regression()#0.783
    # decision_tree()#0.814
    random_forest()# 0.814

Xgboost:可以用來做迴歸,迴歸的意思是預測出來的不是一個類別,而是一個數。比如,下面用兩個決策樹來預測一個人是否喜歡打遊戲,正數表示喜歡打遊戲,負數表示不喜歡打遊戲,對於同一個人如小男孩,兩個相加後為2.9,說明小男孩打遊戲。老爺爺兩個預測結果相加為-0.1,說明老爺爺不打遊戲。這種方法也是一種整合的方法。

演算法過程:

1、第一個式子中w是權重,也就是葉子節點上的數值。

2、y帽子表示預測值。

3、目標函式表示真實值與預測值之間的誤差。

4、最優函式解的方法表示所有樣本的誤差之和再求一個平均,也就成了誤差期望,然後使用某種方法使得函式最小,這裡的方法是加入決策樹,使得目標函式下降。

5、整合演算法的表示中K表示決策樹,因為是整合演算法,所有要把所有的決策樹考慮進來。

6、演算法核心思想,並不是一下子就加好多顆決策樹進去的,因為不知道一下子這麼多決策樹效果是不是一定會很好,他的決策樹的一次一次加的,比如先用一顆決策樹來預測,發現效果不太好,然後我們再加一個決策樹進去共同預測,若發現不太好可以再加,然後再除錯。

舉個例子:

開始沒有模型,預測值為0,然後加入一顆決策樹,構建模型後發現效果不太好,然後再加入決策樹構建新的模型....加入模型的目的是使得目標函式下降,這個過程也就是優化的過程。

我們知道,如果葉子節點過多會導致過擬合,權重過多也會導致過擬合。所以我們加入懲罰項(懲罰葉子個數和權重的個數):

所以優化目標變成:

其中第一個圈表示決策樹和的模型和懲罰項,圈裡麵包含之前的決策樹和的模型和當前加入新的決策樹的模型和懲罰項,常數表示計算中可能產生一些常數。

我們要做的是不斷加入新的模型,使得目標函式越來越小。

7、xgboost融合了串聯方法。

 

整合演算法三、

堆疊模型:聚合多個分類或迴歸模型演算法(可以分階段來做)

堆疊的意思很暴力,拿一堆模型直接上,比如堆疊多種分類器,如SVM,KNN等。

分階段:第一階段選擇四個演算法,第二個階段用第一個階段的結果在預測。如下:第一個階段1234個數據在各個模型做成預測值,第二個階段把第一個階段的結果再做一個邏輯迴歸,得到最終結果。

堆疊模型例項:

from suijitishenshu import load_data_and_preprocessing#這個是從另一個python檔案中呼叫裡面的函式
from sklearn.model_selection import KFold#K折交叉驗證
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier#隨機森林分類和boosting分類


def stacking():
    s = 0
    x_train, y_train, x_test = load_data_and_preprocessing()
    kf = KFold(n_splits=5)#5折交叉驗證
    rfc = RandomForestClassifier(criterion='entropy', max_depth=10, n_estimators=60)
    gbc = GradientBoostingClassifier(learning_rate=0.1, max_depth=5, n_estimators=90)

    for train_index, test_index in kf.split(x_train):#因為是五折交叉驗證(把資料平均分為5分,每次拿其中四份訓練,另外一份做測試,迴圈五次,每一份都輪了一次用來測試),所以會迴圈五次,這裡賦值過去的是索引,
       # print(train_index)
        #print(test_index)
        train_x, test_x = x_train[train_index], x_train[test_index]
        train_y, test_y = y_train[train_index], y_train[test_index]
        rfc.fit(train_x, train_y)
        rfc_pre = rfc.predict_proba(test_x)[:,1]#預測概率
        print(rfc.predict_proba(test_x))#這個的結果有兩列,分別為y = 0和y = 1的概率
        print(rfc.predict_proba(test_x).shape)
        print(rfc_pre)
        print(rfc_pre.shape)

        gbc.fit(train_x, train_y)
        gbc_pre = gbc.predict_proba(test_x)[:,1]#預測概率
        y_pre = ((rfc_pre+gbc_pre)/2 >= 0.5)*1#取值為1或者0
        acc = sum((test_y == y_pre)*1)/len(y_pre)
        s += acc
        print(acc)
    print('Accuracy: ',s/5)# 0.823對五次求出的精確度取個平均值


if __name__ == '__main__':
    stacking()