集成方法是將幾種機器學習技術組合成一個預測模型的元算法,以達到減小方差(bagging)、偏差(boosting)或改進預測(stacking)的效果。集成學習潛在的思想是即便某一個弱分類器得到了錯誤的預測,其他的弱分類器也可以將錯誤糾正回來。
目前主要的集成學習算法類型有Voting, Bagging, Boost , Stacking。
目錄
一、Voting 投票
1、voting原理
假設有1000種分類器,每個分類器預測的正確率只有50.5%,如果以預測類別最多的作爲預測結果,則準確率可達到60%,如果有10000種分類器,則準確率可達到84%左右。該結果的前提是分類器彼此獨立,但是現實中它們都在同一個數據集上進行訓練,可能會犯同樣的錯誤,所以準確率會有降低。
2、Voting可以分爲硬投票法(Harding Voting)和軟投票法(Soft Voting)
-- 硬投票法
根據分類器預測的結果出現最多的類別作爲預測值。
-- 軟投票法
如果所有分類器都能夠估算出類別的概率,那麼對所有分類器的類別概率求平均,然後給出平均概率最高的類別作爲預測。
通常來說,它比硬投票法的表現更優,因爲它給予那些高度自信的投票更高的權重。
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
lr = LogisticRegression()
dt = DecisionTreeClassifier()
svm = SVC() # 若是soft voting,則probability=True
voting = VotingClassifier(
estimators=[('lr',lr),('rf',dt),('svc',svm)],
voting='hard' # 軟投票法,voting='soft'
)
二、Bagging
1、原理
bagging中每個基學習器採用的是相同的算法,分別在各自隨機抽取(有放回的採樣)的子數據集上進行訓練,預測的結果出現最多的類別作爲預測值。
# bootstrap = True 爲bagging,bootstrap=False爲pasting(無放回採樣)
# max_samples設置爲整數表示的就是採樣的樣本數,設置爲浮點數表示的是max_samples*x.shape[0]
bag_clf = BaggingClassifier(
SVC(),
n_estimators=500, max_samples=1.0, bootstrap=True, n_jobs=-1
# ,oob_score=True
)
2、隨機森林 RandomFroest
隨機森林是決策樹的集成,通常用bagging方法進行訓練。
# 如果基分類器是決策樹,那麼該bagging算法就是隨機森林
bag_clf = BaggingClassifier(
DecisionTreeClassifier(splitter="random", max_leaf_nodes=16),
n_estimators=500, max_samples=1.0, bootstrap=True, n_jobs=-1
)
# 上面等同於sklearn提供的RF的API
rnd_clf = RandomForestClassifier(n_estimators=500, n_jobs=-1)
rnd_clf.fit(X, y)
print(rnd_clf.__class__.__name__, '=', accuracy_score(y, y_hat))
三、Boosting
Boosting算法要涉及到兩個部分,加法模型和前向分步算法。加法模型就是說強分類器由一系列弱分類器線性相加而成;前向分步就是說在訓練過程中,下一輪迭代產生的分類器是在上一輪的基礎上訓練得來的。
1、AdaBoost
每一輪如何改變訓練數據的權值:AdaBoost改變了訓練數據的權值,也就是樣本的概率分佈,其思想是將關注點放在被錯誤分類的樣本上,減小上一輪被正確分類的樣本權值,提高那些被錯誤分類的樣本權值。然後,再根據所採用的一些基本機器學習算法進行學習,比如邏輯迴歸。
如何將弱分類器組合成一個強分類器:AdaBoost採用加權多數表決的方法,加大分類誤差率小的弱分類器的權重,減小分類誤差率大的弱分類器的權重。這個很好理解,正確率高分得好的弱分類器在強分類器中當然應該有較大的發言權。
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
rf = DecisionTreeClassifier()
model = AdaBoostClassifier(base_estimator=rf,n_estimators=50,algorithm="SAMME.R", learning_rate=0.5)
model.fit(X_train,y_train)
y_train_hat = model.predict(X_train)
2、GBDT
GBDT模型是一個集成模型,基分類器採用CART,集成方式爲Gradient Boosting。GBDT基於boosting增強策略的加法模型,訓練的時候採用前向分佈算法進行貪婪的學習,每次迭代都學習一棵CART樹來擬合之前 t-1 棵樹的預測結果與訓練樣本真實值的殘差。它不像AdaBoost那樣在每個迭代中調整實例權重,而是讓新的預測器針對前一個預測器的殘差進行擬合。
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import GradientBoostingRegressor
gbdt = GradientBoostingRegressor(max_depth=5, n_estimators=3, learning_rate=1.0)
3、XGBoost
XGBoost的核心思想是不斷地添加樹,不斷地進行特徵分裂來生長一棵樹,每次添加一個樹,其實是學習一個新函數f(x),去擬合上次預測的殘差。當我們訓練完成得到k棵樹,我們要預測一個樣本的分數,其實就是根據這個樣本的特徵,在每棵樹中會落到對應的一個葉子節點,每個葉子節點就對應一個分數。最後只需要將每棵樹對應的分數加起來就是該樣本的預測值。
XGBoost對GBDT進行了一系列優化,比如損失函數進行了二階泰勒展開、目標函數加入正則項、支持並行和默認缺失值處理等,在可擴展性和訓練速度上有了巨大的提升,但其核心思想沒有大的變化。
import xgboost as xgb
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
model =xgb.XGBClassifier(max_depth=3,learning_rate=0.3,n_estimators=6,silent=True,objective='binary:logistic')
model.fit(X_train,y_train)
# 訓練集上準確率
train_preds = model.predict(X_train)
train_accuracy = accuracy_score(y_train, train_preds)
# 測試集上準確率
preds = model.predict(X_test)
test_accuracy = accuracy_score(y_test, preds)
print("Test Accuracy: %.2f%%" % (test_accuracy * 100.0))
-- 使用GridSearchCV(網格搜索交叉驗證)搜索最優參數
from sklearn.model_selection import GridSearchCV
model = xgb.XGBClassifier(learning_rate=0.1, silent=True, objective='binary:logistic')
param_grid = {
'n_estimators': range(1, 51, 1),
'max_depth':range(1,10,1)
}
clf = GridSearchCV(model, param_grid, "accuracy",cv=5)
clf.fit(X_train, y_train)
print(clf.best_params_, clf.best_score_)
-- 我們設置驗證valid集,當我們迭代過程中發現在驗證集上錯誤率增加,則提前停止迭代
from sklearn.model_selection import train_test_split
X_train_part, X_validate, y_train_part, y_validate = train_test_split(X_train, y_train, test_size=0.3,random_state=0)
# 設置boosting迭代計算次數
num_round = 100
bst =xgb.XGBClassifier(max_depth=2, learning_rate=0.1, n_estimators=num_round, silent=True, objective='binary:logistic')
eval_set =[(X_validate, y_validate)]
bst.fit(X_train_part, y_train_part, early_stopping_rounds=10, eval_metric="error",
eval_set=eval_set, verbose=True)
results = bst.evals_result()
-- 錯誤率可視化
results = bst.evals_result()
#print(results)
epochs = len(results['validation_0']['error'])
x_axis = range(0, epochs)
# plot log loss
plt.plot(x_axis, results['validation_0']['error'], label='Test')
plt.ylabel('Error')
plt.xlabel('Round')
plt.title('XGBoost Early Stop')
plt.show()
-- 學習曲線
# 設置boosting迭代計算次數
num_round = 100
# 沒有 eraly_stop
bst =xgb.XGBClassifier(max_depth=2, learning_rate=0.1, n_estimators=num_round, silent=True, objective='binary:logistic')
eval_set = [(X_train_part, y_train_part), (X_validate, y_validate)]
bst.fit(X_train_part, y_train_part, eval_metric=["error", "logloss"], eval_set=eval_set, verbose=True)
# retrieve performance metrics
results = bst.evals_result()
#print(results)
epochs = len(results['validation_0']['error'])
x_axis = range(0, epochs)
# plot log loss
fig, ax = plt.subplots()
ax.plot(x_axis, results['validation_0']['logloss'], label='Train')
ax.plot(x_axis, results['validation_1']['logloss'], label='Test')
ax.legend()
plt.ylabel('Log Loss')
plt.title('XGBoost Log Loss')
plt.show()
# plot classification error
fig, ax = plt.subplots()
ax.plot(x_axis, results['validation_0']['error'], label='Train')
ax.plot(x_axis, results['validation_1']['error'], label='Test')
ax.legend()
plt.ylabel('Classification Error')
plt.title('XGBoost Classification Error')
plt.show()
五、stacking
stacking 就是當用初始訓練數據學習出若干個基學習器後,將這幾個學習器的預測結果作爲新的訓練集,來學習一個新的學習器。
如圖所示,我們現在用5折交叉驗證來訓練數據,model1要做滿5次訓練和預測。
第一次,M1,拿traindata的800行做訓練集,200行做驗證集,然後預測出200行的數據a1。
第二次,M1,拿traindata的800行做訓練集,200行做驗證集,然後預測出200行的數據a2。
第三次,M1,拿traindata的800行做訓練集,200行做驗證集,然後預測出200行的數據a3。
第四次,M1,拿traindata的800行做訓練集,200行做驗證集,然後預測出200行的數據a4。
第五次,M1,拿traindata的800行做訓練集,200行做驗證集,然後預測出200行的數據a5。
然後將a1到a5拼接起來,得到一列,共1000行的數據。
針對測試集testdata有兩種方法,一種是全部訓練完成後,一次性預測輸出200行數據;另一種是M1每次做完訓練就那testdata中的數據做預測,一種得到5次200行的數據,然後做平均,得到一列200行的數據。
如果有10個基模型,那麼根據traindata會得到10列數據,作爲x,原來traindata中的label作爲y(很多文章都沒說這點,導致初學者有很多誤解),然後再放到一個模型中做訓練。而根據testdata會得到10列200行的數據,作爲測試數據。
最後,將訓練好的模型預測10列200行的數據,得到的最終結果就是最後需要的數據。
# sklearn並沒有集成stacking,使用前需用如下命令安裝 pip install mlxtend
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from mlxtend.classifier import StackingClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y.ravel(), train_size=0.8, random_state=0)
# 定義基分類器
clf1 = KNeighborsClassifier(n_neighbors=5)
clf2 = RandomForestClassifier(random_state=1)
clf3 = GaussianNB()
# 定義最後輸出的分類器
lr = LogisticRegression()
# 定義堆疊後的模型
sclf = StackingClassifier(classifiers=[clf1, clf2, clf3],
meta_classifier=lr,use_probas=True)
# 對每一個類分類器進行打分
for model in [clf1,clf2,clf3,lr,sclf]:
model.fit(X_train,y_train)
y_test_hat = model.predict(X_test)
print(model.__class__.__name__,',test accuarcy:',accuracy_score(y_test,y_test_hat))