集成學(xué)習(xí)算法應(yīng)用比較廣泛,只要包含Bagging,Boosting與Stacking三大類方法窖认,這是本文的主要內(nèi)容:
??1. 集成算法的主要思想;
??2. 集成算法的Bagging告希,Boosting與Stacking方法的數(shù)學(xué)模型與推導(dǎo)扑浸;
??3. 集成算法的實(shí)現(xiàn)與應(yīng)用;
??4. XGBoost算法介紹燕偶;
??到目前為止機(jī)器學(xué)習(xí)入門告一個(gè)結(jié)束喝噪;后面繼續(xù)寫框架擴(kuò)展部分并深入補(bǔ)充部分優(yōu)化算法;并使用C++實(shí)現(xiàn)一個(gè)系列指么,包含OpenCV與Tensorflow酝惧;
集成學(xué)習(xí)算法說明
集成學(xué)習(xí)介紹
集成學(xué)習(xí)(ensemble learning)本身不是一個(gè)單獨(dú)的機(jī)器學(xué)習(xí)算法,而是通過構(gòu)建并結(jié)合多個(gè)機(jī)器學(xué)習(xí)器來完成學(xué)習(xí)任務(wù)伯诬。
-
集成學(xué)習(xí)可以用于:
- 分類問題集成
- 回歸問題集成
- 特征選取集成
- 異常點(diǎn)檢測(cè)集成
-
怎么通過多個(gè)機(jī)器學(xué)習(xí)的結(jié)果得到一個(gè)最終的學(xué)習(xí)結(jié)果晚唇,這是集成學(xué)習(xí)算法的關(guān)鍵,這需要組織不同機(jī)器學(xué)習(xí)的策略盗似。一般從兩個(gè)角度出發(fā):
- 數(shù)據(jù)集
- 算法
-
名詞說明:
- 個(gè)體學(xué)習(xí)器哩陕,也稱:基學(xué)習(xí)器
- 在基學(xué)習(xí)器上形成的學(xué)習(xí)器稱為元學(xué)習(xí)器。(下面會(huì)有詳細(xì)的介紹)
-
集成學(xué)習(xí)的核心:
- 個(gè)體機(jī)器學(xué)習(xí)器
- 結(jié)合策略
其他集成學(xué)習(xí)的說明
-
集成學(xué)習(xí)算法的有效性:
- 比如用線性函數(shù)去擬合曲線,效果當(dāng)然不會(huì)很好萌踱;
- 如果把數(shù)據(jù)集拆分開葵礼,使用多個(gè)線性回歸集成,這些線性回歸組合而成的折線其實(shí)也算集成學(xué)習(xí)并鸵。
通過集成學(xué)習(xí)鸳粉,可以提高算法的泛化能力,所謂泛化能力就是通用性园担、廣泛性届谈、穩(wěn)定性更強(qiáng)。
-
集成學(xué)習(xí)集成不同算法的條件:
- 需要選擇有差異性的機(jī)器學(xué)習(xí)算法弯汰; 比如沒有差異的分類器艰山,集成起來的分類效果也不會(huì)有什么變化。
- 集成的學(xué)習(xí)算法的準(zhǔn)確率需要50%以上咏闪;比如分類器的準(zhǔn)確率小于0.5曙搬,隨著集成規(guī)模的增加,分類準(zhǔn)確率會(huì)不斷下降鸽嫂;如果準(zhǔn)確度大于0.5纵装,那么最終分類準(zhǔn)曲率可以趨近于1。
個(gè)體學(xué)習(xí)器構(gòu)建
個(gè)體學(xué)習(xí)器構(gòu)建方式
- 算法不同:比如對(duì)相同數(shù)據(jù)集据某,使用貝葉斯分類橡娄,SVM分類算法,訓(xùn)練出多個(gè)基學(xué)習(xí)器癣籽。
- 算法相同:比如對(duì)不同的數(shù)據(jù)集挽唉,使用決策樹訓(xùn)練出多個(gè)決策樹基學(xué)習(xí)器。
- 訓(xùn)練集不同筷狼;
- 參數(shù)不同
個(gè)體學(xué)習(xí)構(gòu)建算法
- 常見的基學(xué)習(xí)器的構(gòu)建算法有:
- boosting:用于減少偏差的boosting
- AdaBoost算法
- GradientBoosting算法
- HistGradientBoosting算法
- XGBoost
- Bagging:用于減少方差
- 隨機(jī)森林
- Stacking:用于提升預(yù)測(cè)結(jié)果
- boosting:用于減少偏差的boosting
個(gè)體學(xué)習(xí)器結(jié)合策略
- 把多個(gè)基學(xué)習(xí)器(弱學(xué)習(xí)器)的按照不同的結(jié)合策略瓶籽,形成強(qiáng)學(xué)習(xí)器,常見結(jié)合策略有兩種:
- Averaging 平均法
- 一般在回歸問題中采用
- Voting 投票法
- 一般在分類問題中采用
- Averaging 平均法
Averaging 平均法結(jié)合策略
-
假設(shè)基學(xué)習(xí)器數(shù)量是個(gè)埂材,每個(gè)基學(xué)習(xí)器的預(yù)測(cè)輸出是:
-
最終平均值輸出是:
-
如果對(duì)基學(xué)習(xí)器考慮加權(quán),每個(gè)基學(xué)習(xí)器的加權(quán)為塑顺,則加權(quán)輸出為:
Voting 投票法結(jié)合策略
-
假設(shè)我們的預(yù)測(cè)類別是,對(duì)于任意一個(gè)預(yù)測(cè)樣本楞遏,我們的個(gè)弱學(xué)習(xí)器的預(yù)測(cè)結(jié)果分別是:
-
投票法是相對(duì)多數(shù)投票法茬暇,也就是少數(shù)服從多數(shù):
- 也就是個(gè)弱學(xué)習(xí)器的對(duì)樣本的預(yù)測(cè)結(jié)果中首昔,數(shù)量最多的類別為最終的分類類別寡喝。
- 如果不止一個(gè)類別獲得最高票,則隨機(jī)選擇一個(gè)做最終類別勒奇。
- 投票方式:
- 硬投票(hard):基于分類標(biāo)簽投票
- 軟投票(soft):基于分類標(biāo)簽概率投票
sklearn集成學(xué)習(xí)應(yīng)用體驗(yàn)
import sklearn.datasets as ds
from sklearn.model_selection import train_test_split
# HistGradientBoostingClassifier 分類器不存在
from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier
from sklearn.ensemble import BaggingClassifier, ExtraTreesClassifier,RandomForestClassifier, IsolationForest
from sklearn.ensemble import VotingClassifier
# 加載數(shù)據(jù)集
data,target = ds.load_iris(return_X_y=True)
# 切分?jǐn)?shù)據(jù)集
data_train, data_test, target_train, target_test = train_test_split(
data, # 數(shù)據(jù)集
target, # 數(shù)據(jù)集標(biāo)簽
test_size=0.2)
Boosting 算法集成學(xué)習(xí)算法
AdaBoostClassifier
classifier = AdaBoostClassifier(
base_estimator=None,
n_estimators=100,
learning_rate=0.01,
algorithm='SAMME.R',
random_state=None)
classifier.fit(data_train, target_train)
pre = classifier.predict(data_test)
correct_num = (pre == target_test).sum()
print(F'測(cè)試樣本總數(shù): {len(target_test)}预鬓,分類正確數(shù):{correct_num}' )
測(cè)試樣本總數(shù): 30,分類正確數(shù):29
GradientBoostingClassifier
classifier = GradientBoostingClassifier(
loss='deviance', # 損失度量方式:默認(rèn)偏差
learning_rate=0.1, # 學(xué)習(xí)率
n_estimators=100, # 基學(xué)習(xí)器
subsample=1.0,
criterion='friedman_mse',
min_samples_split=2, # 決策樹的特性
min_samples_leaf=1,
min_weight_fraction_leaf=0.0,
max_depth=3,
min_impurity_decrease=0.0,
min_impurity_split=None,
init=None,
random_state=None,
max_features=None,
verbose=0,
max_leaf_nodes=None,
warm_start=False,
# presort='auto',
# validation_fraction=0.1,
# n_iter_no_change=None,
# tol=0.0001 # 結(jié)束誤差
)
classifier.fit(data_train, target_train)
pre = classifier.predict(data_test)
correct_num = (pre == target_test).sum()
print(F'測(cè)試樣本總數(shù): {len(target_test)},分類正確數(shù):{correct_num}' )
測(cè)試樣本總數(shù): 30格二,分類正確數(shù):29
Bagging 與隨機(jī)森林集成學(xué)習(xí)算法
BaggingClassifier
classifier = BaggingClassifier(
base_estimator=None,
n_estimators=50,
max_samples=1.0,
max_features=1.0,
bootstrap=True,
bootstrap_features=False,
oob_score=False,
warm_start=False,
n_jobs=-1, # 使用None 報(bào)異常
random_state=None,
verbose=0)
classifier.fit(data_train, target_train)
pre = classifier.predict(data_test)
correct_num = (pre == target_test).sum()
print(F'測(cè)試樣本總數(shù): {len(target_test)}劈彪,分類正確數(shù):{correct_num}' )
測(cè)試樣本總數(shù): 30,分類正確數(shù):29
RandomForestClassifier
classifier =RandomForestClassifier(
n_estimators= 100, # 森林?jǐn)?shù)量
criterion='gini',
max_depth=None,
min_samples_split=2,
min_samples_leaf=1,
min_weight_fraction_leaf=0.0,
max_features='auto',
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
bootstrap=True,
oob_score=False,
n_jobs= -1, # 指定數(shù)量顶猜,與默認(rèn)值的區(qū)別
random_state=None,
verbose=0,
warm_start=False,
class_weight=None)
classifier.fit(data_train, target_train)
pre = classifier.predict(data_test)
correct_num = (pre == target_test).sum()
print(F'測(cè)試樣本總數(shù): {len(target_test)}沧奴,分類正確數(shù):{correct_num}' )
測(cè)試樣本總數(shù): 30,分類正確數(shù):29
ExtraTreesClassifier 極限樹
classifier = ExtraTreesClassifier(
n_estimators=100,
criterion='gini',
max_depth=None,
min_samples_split=2,
min_samples_leaf=1,
min_weight_fraction_leaf=0.0,
max_features='auto',
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
bootstrap=False,
oob_score=False,
n_jobs=-1,
random_state=None,
verbose=0,
warm_start=False,
class_weight=None)
classifier.fit(data_train, target_train)
pre = classifier.predict(data_test)
correct_num = (pre == target_test).sum()
print(F'測(cè)試樣本總數(shù): {len(target_test)}长窄,分類正確數(shù):{correct_num}' )
測(cè)試樣本總數(shù): 30滔吠,分類正確數(shù):29
IsolationForest 離群點(diǎn)檢測(cè)
outlier= IsolationForest(
n_estimators=100,
max_samples='auto',
contamination=0.1,
max_features=1.0,
bootstrap=False,
# n_jobs=-1,
# behaviour='old',
# random_state=None,
# verbose=0,
# warm_start=False
)
outlier.fit(data_train)
y_pred_train = outlier.predict(data_train)
y_pred_test = outlier.predict(data_test) # 返回 1(正常inlier ),-1(異常outlier)
y_pred_test
array([-1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1,
-1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1])
其他集成學(xué)習(xí)算法
VotingClassifier投票分類器
# import warnings
# warnings.filterwarnings(module='sklearn*', action='ignore', category=DeprecationWarning)
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
import sklearn.datasets as ds
from sklearn.model_selection import train_test_split
from sklearn.ensemble import VotingClassifier
# 加載數(shù)據(jù)集
data,target = ds.load_iris(return_X_y=True)
# data = data[0:100]
# target = target[0:100]
# 切分?jǐn)?shù)據(jù)集
data_train, data_test, target_train, target_test = train_test_split(
data, # 數(shù)據(jù)集
target, # 數(shù)據(jù)集標(biāo)簽
test_size=0.2)
c1 =RandomForestClassifier(n_estimators= 50, criterion='gini')
c2 = LogisticRegression()
c3 = SVC(C=1.0, kernel='rbf')
classifier = VotingClassifier(
estimators=[ # 參數(shù)是元組列表
('c1', c1),
('c2', c2),
('c3', c3)],
voting='hard',
weights=None,
n_jobs=-1,
flatten_transform=True)
classifier.fit(data_train, target_train)
pre = classifier.predict(data_test)
correct_num = (pre == target_test).sum()
print(F'測(cè)試樣本總數(shù): {len(target_test)}挠日,分類正確數(shù):{correct_num}' )
測(cè)試樣本總數(shù): 30疮绷,分類正確數(shù):30
sklearn與bumpy版本不兼容的問題
因?yàn)閟klearn默認(rèn)調(diào)用當(dāng)前python環(huán)境的numpy模塊,如果sklearn與numpy版本不匹配嚣潜,會(huì)導(dǎo)致出現(xiàn)一些警告。
-
一般版本不兼容,都是因?yàn)閚umpy版本更新速度超過sklearn的更新速度導(dǎo)致慌洪,一般官網(wǎng)提示都是最低版本株憾,沒有對(duì)最高版本進(jìn)行說明,所以容易出現(xiàn)問題犯犁。下面是官網(wǎng)對(duì)版本的要求說明:
- 我當(dāng)前sklearn版本:0.19.2属愤,匹配的版本是1.13.3(當(dāng)前最新版本是:1.16.3)
-
更新sklearn最新版本:
pip install -U scikit-learn
-
提示
-
上面代碼可能報(bào)警告如下:
上面的警告問題是numpy自身的問題,改問題已經(jīng)被修正酸役,但沒有體現(xiàn)在最新的版本中住诸。
-
-
sklearn最新版本下載安裝:
關(guān)閉警告
import warnings
warnings.filterwarnings(module='sklearn*', action='ignore', category=DeprecationWarning)
- 降低numpy的版本也可以解決這個(gè)問題,可能不同的sklearn版本匹配不同的numpy版本涣澡。
pip install numpy==1.13.3
Bagging集成
Bagging算法思想
Bagging是Bootstrap Aggregating(自助結(jié)合)的簡寫
-
Bagging是通過組合隨機(jī)生成的訓(xùn)練集而改進(jìn)分類的集成算法贱呐。
- Bagging把訓(xùn)練數(shù)據(jù)集采用放回抽樣,產(chǎn)生個(gè)訓(xùn)練集(是原始數(shù)據(jù)集的子集)入桂,使用自定的分類器算法對(duì)個(gè)子集進(jìn)行訓(xùn)練而得到個(gè)分類器(基分類器)奄薇;
- 對(duì)待分類樣本進(jìn)行分類是,調(diào)用個(gè)基學(xué)習(xí)器抗愁,得到個(gè)分類結(jié)果馁蒂,然后采用投票結(jié)合策略,得到的分類結(jié)果作為最終的分類結(jié)果蜘腌。
-
Baggind算法的特點(diǎn):
- 數(shù)據(jù)集不同(對(duì)已知數(shù)據(jù)集隨機(jī)抽樣產(chǎn)生多個(gè)不同的數(shù)據(jù)集)
- 算法相同
-
Bagging算法的關(guān)鍵點(diǎn)
- Bagging算法的關(guān)鍵在抽樣的方式沫屡,一般采用的抽樣方法是:自助采樣法(Bootstrap Sampling)
Bootstrap采樣
Bootstrap方法最初由美國斯坦福大學(xué)統(tǒng)計(jì)學(xué)教授Efron在1977年提出。作為一種嶄新的增廣樣本統(tǒng)計(jì)方法撮珠,Bootstrap方法為解決小規(guī)模子樣試驗(yàn)評(píng)估問題提供了很好的思路沮脖。
-
Bootstrap采樣的思想:
- 假設(shè)原始樣本數(shù)據(jù)集,樣本容量為;
- Bootstrap采樣就是在原始數(shù)據(jù)的范圍內(nèi)作有放回的再抽樣, 樣本容量仍為勺届,原始數(shù)據(jù)中每個(gè)樣本每次被抽到的概率相等, 為 (這個(gè)在python編程中可以通過先獲取之間的均勻隨機(jī)數(shù)作為下標(biāo)得到), 所得樣本稱為Bootstrap樣本驶俊。
-
Bootstrap采樣法解決了小樣本時(shí)主成分分析的置信區(qū)間的計(jì)算方法:
- 比如鳶尾花的四個(gè)特征,我們?cè)谧鯬CA主成分分析的時(shí)候免姿,通過協(xié)方差矩陣得到特征值與特征向量饼酿,當(dāng)樣本數(shù)據(jù)集產(chǎn)生改變(數(shù)量與順序)的時(shí)候,這個(gè)四個(gè)特征值也會(huì)產(chǎn)生改變胚膊,從而得到不同的結(jié)果嗜湃,這個(gè)結(jié)果可能導(dǎo)致很大的學(xué)習(xí)誤差。
- 怎么評(píng)估這個(gè)特征值隨著樣本變化澜掩,有多大的可信度购披,Bootstrap方法就解決了這個(gè)問題:
- 采用次boostrap抽樣,可以得到個(gè)樣本集(與原樣本集容量一樣:樣本肯定有重復(fù))肩榕,從而得到個(gè)特征值
- 采用特征值在某個(gè)分位區(qū)間出現(xiàn)的頻次(記得matplotlib中密度圖與直方圖不刚陡?就是那個(gè)意思),可以得到特征值的置信區(qū)間的計(jì)算方式:值落在在某個(gè)范圍內(nèi)的概率株汉。
- 最終可以證明Bootstrap采樣的有效性在于:只要采樣的次數(shù)足夠多筐乳,鳶尾花的主成分分析中最大特征值95%的概率落在一個(gè)比較窄的范圍內(nèi),這種主成分就是在樣本放生變化的時(shí)候就是可信的乔妈,特征的穩(wěn)定性在未知的測(cè)試樣本中也是可信的蝙云。
- Bootstrap采樣次數(shù)越多,其最后分類的結(jié)果置信度越高路召,方差越小勃刨。
Bagging算法的每個(gè)基學(xué)習(xí)器之間沒有聯(lián)系,都是采用獨(dú)立抽樣的樣本集訓(xùn)練出來的股淡,我們稱這種方式是并行方式身隐。
-
隨機(jī)森林是bagging的一個(gè)特例優(yōu)化版本:
- 算法都采用決策樹。
- 在樣本隨機(jī)采樣基礎(chǔ)上唯灵,又加上了特征的隨機(jī)選擇贾铝。
Bagging基礎(chǔ)算法的實(shí)現(xiàn)
# 略
Boosting集成
Boosting算法思想
-
Boosting(增長)算法是通過對(duì)數(shù)據(jù)集中樣本采用不同的加權(quán),形成不同數(shù)據(jù)集的方式改進(jìn)分類效果:
- 首先從訓(xùn)練集用初始權(quán)重訓(xùn)練出一個(gè)基學(xué)習(xí)器1埠帕;
- 根據(jù)基學(xué)習(xí)器1的分類結(jié)果來更新訓(xùn)練樣本的權(quán)重垢揩,分類錯(cuò)誤的樣本認(rèn)為是分類困難樣本,權(quán)重增加敛瓷,反之權(quán)重降低叁巨;改變權(quán)重后得到新的樣本;
- 對(duì)新的樣本集琐驴,再訓(xùn)練一個(gè)新的基學(xué)習(xí)器2俘种;
- 重復(fù)步驟2,得到個(gè)基學(xué)習(xí)器绝淡;
- 采用結(jié)合策略宙刘,得到最終的分類結(jié)果。
-
Baggind算法的特點(diǎn):
- 數(shù)據(jù)集不同(對(duì)已知數(shù)據(jù)集采用不同加權(quán)產(chǎn)生多個(gè)不同的數(shù)據(jù)集)
- 算法相同(可以采用不同的算法)
-
Boosting算法的核心是權(quán)重的更新方式牢酵,因?yàn)闄?quán)重的更新方式悬包,產(chǎn)生不同的Boosting算法的
- Ada Boosting算法
- Gradient Boosting算法
AdaBoosting算法
AdaBoosting算法模型
分類與預(yù)測(cè)模型
-
假設(shè),訓(xùn)練出個(gè)基學(xué)習(xí)器馍乙,則最終強(qiáng)學(xué)習(xí)器輸出模型為:
AdaBoosting算法就是要找出一組使得G(x)輸出的損失最小布近。
損失函數(shù)模型
-
AdaBoosting模型的損失最小的損失函數(shù)定義如下:
-
如果假設(shè)對(duì)所有樣本:,損失函數(shù)最小模型為:
-
- 其中:
-
-
最終完整表示損失函數(shù)為
損失函數(shù)模型簡化
-
上面是復(fù)雜的全局優(yōu)化問題丝格,通常我們使用其簡化版撑瞧,即假設(shè)在第k次迭代中,前k-1次的系數(shù)與基學(xué)習(xí)器是已知與固定的显蝌,則第個(gè)基學(xué)習(xí)器是基于前面個(gè)與基學(xué)習(xí)器的预伺。
-
損失最小模型,轉(zhuǎn)換為如下問題求解:
- 在已知與基學(xué)習(xí)器下曼尊,求使得下面公式最小:
- 在已知與基學(xué)習(xí)器下曼尊,求使得下面公式最小:
備注:選擇指數(shù)作為損失函數(shù)定義的理由
-
選擇指數(shù)損失函數(shù)的理由是:可以通過數(shù)學(xué)推導(dǎo)骆撇,使得最后結(jié)果得到貝葉斯最優(yōu)錯(cuò)誤率:
- 即對(duì)于每個(gè)樣本都選擇后驗(yàn)概率最大的類別瞒御。
- 若指數(shù)損失最小化,則分類錯(cuò)誤率也將最小化神郊。
-
指數(shù)損失函數(shù)是分類任務(wù)損失函數(shù)的一致性替代函數(shù)肴裙。
- 由于這個(gè)替代函數(shù)是單調(diào)連續(xù)可微函數(shù),因此用它代替0-1損失函數(shù)作為優(yōu)化目標(biāo)涌乳。
下面算法過程都是由上面模型推導(dǎo)出來的践宴,具體的推導(dǎo)過程,這里省略爷怀。
AdaBoosting算法實(shí)現(xiàn)過程
AdaBoosting除了權(quán)重計(jì)算不同阻肩,最后的結(jié)合策略采用的是線性結(jié)合策略(加權(quán)表決),形成最終的強(qiáng)學(xué)習(xí)器运授。
-
假設(shè)訓(xùn)練集是:
-
烤惊,
- 其中為樣本容量(樣本總數(shù))
- 表示第個(gè)樣本;
- 標(biāo)識(shí)第個(gè)樣本的分類標(biāo)簽(假設(shè)是二分類吁朦,并且標(biāo)簽標(biāo)準(zhǔn)化為-1與1)柒室;
-
烤惊,
-
初始化樣本的權(quán)重為:
-
- 為樣本容量。
- 逗宜,目前是第個(gè)雄右。
-
-
使用權(quán)重得到新的數(shù)據(jù)集:
- 空骚,
-
使用新的數(shù)據(jù)集,訓(xùn)練出基學(xué)習(xí)器:
-
- 擂仍,目前是第個(gè)囤屹。
-
-
使用基學(xué)習(xí)器對(duì)樣本進(jìn)行分類,并計(jì)算出樣本分類的錯(cuò)誤率:
-
- 肋坚,目前是第個(gè)。
- 如果肃廓,則錯(cuò)誤率就是
-
-
計(jì)算每個(gè)分類器在最終結(jié)合的線性系數(shù):
-
- 其中表示自然對(duì)數(shù)智厌。
-
【注意】上面函數(shù)有一個(gè)良好的性質(zhì):
- 越小,越大盲赊,這表示誤差率越小的基學(xué)習(xí)器在最后強(qiáng)學(xué)習(xí)器中的權(quán)重就越大铣鹏。
-
-
計(jì)算規(guī)范化因子
-
新的權(quán)值計(jì)算
-
【注意】上面公式具有一個(gè)特點(diǎn):
- 約大,就越大哀蘑,這樣下一個(gè)基學(xué)習(xí)器產(chǎn)生的時(shí)候吝沫,對(duì)應(yīng)樣本就強(qiáng)化(越難分類的樣本,越強(qiáng)化)递礼。
-
基學(xué)習(xí)器的結(jié)合策略
-
- 其中表示最終基學(xué)習(xí)器總數(shù)惨险。
-
-
為了便于分類,使用符號(hào)函數(shù)處理下:
- 因?yàn)樯厦婕僭O(shè)我們的類別輸出是脊髓,我們的強(qiáng)學(xué)習(xí)器的輸出也表示成同樣的格式辫愉,這個(gè)使用符號(hào)函數(shù)實(shí)現(xiàn):
- 因?yàn)樯厦婕僭O(shè)我們的類別輸出是脊髓,我們的強(qiáng)學(xué)習(xí)器的輸出也表示成同樣的格式辫愉,這個(gè)使用符號(hào)函數(shù)實(shí)現(xiàn):
AdaBoosting的其他改進(jìn)
離散與連續(xù)
- AdaBoost算法被稱為"Discrete AdaBoost",因?yàn)槠涓鱾€(gè)基學(xué)習(xí)器Gm(x)的輸出為{-1,1}
- 連續(xù)AdaBoost算法采用的是概率輸出将硝,sklearn中的連續(xù)與離散指定:SAMME.R恭朗,連續(xù)的情況,可以提高訓(xùn)練速度依疼。
學(xué)習(xí)率與過擬合
-
防止學(xué)的太快產(chǎn)生過擬合痰腮,對(duì)每個(gè)基學(xué)習(xí)器乘以一個(gè)系數(shù) (又稱學(xué)習(xí)率),
- scikit-learn中由learning rate參數(shù)指定律罢。
一般較小的產(chǎn)生更多的基學(xué)習(xí)器膀值,可以通過設(shè)置基學(xué)習(xí)器數(shù)量來提早終止學(xué)習(xí)。
樣本刪除
- Weight trimming:
- 在AdaBoost的每一輪基學(xué)習(xí)器訓(xùn)練過程中误辑,只有小部分樣本的權(quán)重較大沧踏,因而能產(chǎn)生較大的影響,而其他大部分權(quán)重小的樣本則對(duì)訓(xùn)練影響甚微巾钉。
- Weight trimming的思想是每一輪迭代中刪除那些低權(quán)重的樣本翘狱,只用高權(quán)重樣本進(jìn)行訓(xùn)練。具體是設(shè)定一個(gè)閾值 (比如90%或99%)砰苍,再將所有樣本按權(quán)重排序潦匈,計(jì)算權(quán)重的累積和阱高,累積和大于閾值的權(quán)重 (樣本) 被舍棄,不會(huì)用于訓(xùn)練茬缩。注意每一輪訓(xùn)練完成后所有樣本的權(quán)重依然會(huì)被重新計(jì)算赤惊,這意味著之前被舍棄的樣本在之后的迭代中如果權(quán)重增加,可能會(huì)重新用于訓(xùn)練寒屯。
AdaBoosting算法實(shí)現(xiàn)
AdaBoosting的算法實(shí)現(xiàn),我們這里沒有子集實(shí)現(xiàn)黍少,是抄襲引用網(wǎng)絡(luò)作者massquantity的文章與代碼寡夹。
代碼參考:https://github.com/massquantity/ML-algorithms-implemetation
import numpy as np
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.base import clone
from sklearn.ensemble import AdaBoostRegressor
from sklearn.metrics import zero_one_loss
import time
def clock(func):
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
print("training time: ", time.time() - start_time)
return wrapper
class AdaBoost(object):
def __init__(self, M, clf, learning_rate=1.0, method="discrete", tol=None, weight_trimming=None):
self.M = M
self.clf = clf
self.learning_rate = learning_rate
self.method = method
self.tol = tol
self.weight_trimming = weight_trimming
@clock
def fit(self, X, y):
if self.tol is not None:
X, X_val, y, y_val = train_test_split(X, y, random_state=2)
w = np.array([1 / len(X)] * len(X))
self.clf_total = []
self.alpha_total = []
former_loss = 1
count = 0
tol_init = self.tol
for m in range(self.M):
classifier = clone(self.clf)
if self.method == "discrete":
if m >= 1 and self.weight_trimming is not None:
sort_w = np.sort(w)[::-1]
cum_sum = np.cumsum(sort_w)
percent_w = sort_w[np.where(cum_sum >= self.weight_trimming)][0] # 0.999
w_fit, X_fit, y_fit = w[w >= percent_w], X[w >= percent_w], y[w >= percent_w]
y_pred = classifier.fit(X_fit, y_fit, sample_weight=w_fit).predict(X)
# if m % 100 == 0:
# print("round {}: {}".format(m, len(w_fit)))
else:
y_pred = classifier.fit(X, y, sample_weight=w).predict(X)
loss = np.zeros(len(X))
loss[y_pred != y] = 1
err = np.sum(w * loss)
alpha = 0.5 * np.log((1 - err) / err) * self.learning_rate
w = (w * np.exp(-y * alpha * y_pred)) / np.sum(w * np.exp(-y * alpha * y_pred))
self.alpha_total.append(alpha)
self.clf_total.append(classifier)
elif self.method == "real":
if m >= 1 and self.weight_trimming is not None:
sort_w = np.sort(w)[::-1]
cum_sum = np.cumsum(sort_w)
percent_w = sort_w[np.where(cum_sum >= self.weight_trimming)][0]
w_fit, X_fit, y_fit = w[w >= percent_w], X[w >= percent_w], y[w >= percent_w]
y_pred = classifier.fit(X_fit, y_fit, sample_weight=w_fit).predict_proba(X)[:, 1]
else:
y_pred = classifier.fit(X, y, sample_weight=w).predict_proba(X)[:, 1]
y_pred = np.clip(y_pred, 1e-15, 1 - 1e-15)
clf = 0.5 * np.log(y_pred / (1 - y_pred)) * self.learning_rate
w = (w * np.exp(-y * clf)) / np.sum(w * np.exp(-y * clf))
self.clf_total.append(classifier)
'''early stopping'''
if m % 10 == 0 and m > 300 and self.tol is not None:
if self.method == "discrete":
p = np.array([self.alpha_total[m] * self.clf_total[m].predict(X_val) for m in range(m)])
elif self.method == "real":
p = []
for m in range(m):
ppp = self.clf_total[m].predict_proba(X_val)[:, 1]
ppp = np.clip(ppp, 1e-15, 1 - 1e-15)
p.append(self.learning_rate * 0.5 * np.log(ppp / (1 - ppp)))
p = np.array(p)
stage_pred = np.sign(p.sum(axis=0))
# print("round {}".format(m), zero_one_loss(stage_pred,y_val))
later_loss = zero_one_loss(stage_pred, y_val)
if later_loss > (former_loss + self.tol):
count += 1
self.tol = self.tol / 2
# print(self.tol)
else:
count = 0
self.tol = tol_init
if count == 2:
self.M = m - 20
print("early stopping in round {}, best round is {}, M = {}".format(m, m - 20, self.M))
break
former_loss = later_loss
return self
def predict(self, X):
if self.method == "discrete":
pred = np.array([self.alpha_total[m] * self.clf_total[m].predict(X) for m in range(self.M)])
elif self.method == "real":
pred = []
for m in range(self.M):
p = self.clf_total[m].predict_proba(X)[:, 1]
p = np.clip(p, 1e-15, 1 - 1e-15)
pred.append(0.5 * np.log(p / (1 - p)))
# pred = np.array([0.5 * np.log(self.clf_total[m].predict_proba(X)[:,1] / (1-self.clf_total[m].predict_proba(X)[:,1])) for m in range(self.M)])
return np.sign(np.sum(pred, axis=0))
def stage_predict(self, X):
pred = None
if self.method == "discrete":
for alpha, clf in zip(self.alpha_total, self.clf_total):
current_pred = alpha * clf.predict(X)
if pred is None:
pred = current_pred
else:
pred += current_pred
yield np.sign(pred)
elif self.method == "real":
for clf in self.clf_total:
p = clf.predict_proba(X)[:, 1]
p = np.clip(p, 1e-15, 1 - 1e-15)
current_pred = 0.5 * np.log(p / (1 - p))
if pred is None:
pred = current_pred
else:
pred += current_pred
yield np.sign(pred)
if __name__ == "__main__":
X, y = datasets.make_hastie_10_2(n_samples=2000, random_state=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)
model = AdaBoost(M=450, clf=DecisionTreeClassifier(max_depth=1, random_state=1), learning_rate=1.0, method="real",
tol=0.01, weight_trimming=0.999)
model.fit(X_train, y_train)
pred = model.predict(X_test)
acc = np.zeros(pred.shape)
acc[np.where(pred == y_test)] = 1
accuracy = np.sum(acc) / len(pred)
print('score: ', accuracy) # 0.9656667
base_model = DecisionTreeClassifier(max_depth=1,random_state=1)
model = AdaBoostRegressor(n_estimators=450, learning_rate=1.0, base_estimator=base_model)
model.fit(X_train, y_train)
pred = model.predict(X_test)
acc = np.zeros(pred.shape)
acc[np.where(pred == y_test)] = 1
accuracy = np.sum(acc) / len(pred)
print('score_sklearn: ', accuracy)
training time: 1.3744990825653076
score: 0.918
score_sklearn: 0.734
GradientBoosting算法
AdaBoost使用的是指數(shù)損失,這個(gè)損失函數(shù)的缺點(diǎn)是對(duì)于異常點(diǎn)非常敏感厂置。因而通常在噪音比較多的數(shù)據(jù)集上表現(xiàn)不佳菩掏。
-
GradientBoosting算法的特點(diǎn)就是在損失函數(shù)上面做了改進(jìn):
- GradientBoosting可以使用任何損失函數(shù) ,從而可以挑選適合數(shù)據(jù)集的損失函數(shù)來度量最終的分類與預(yù)測(cè)誤差昵济。
GradientBoosting算法模型
-
GradientBoosting算法模型與AdaBoosting的算法模型是一樣的智绸。但有個(gè)很大的區(qū)別是GradientBoosting最終要得到最強(qiáng)學(xué)習(xí)器的方式不一樣:
- GradientBoosting算法把最終的最強(qiáng)學(xué)習(xí)器看成是一個(gè)最優(yōu)的學(xué)習(xí)器,滿足識(shí)別誤差最小访忿。
- GradientBoosting算法的過程是函數(shù)梯度下降的算法瞧栗,每次下降的梯度就是一個(gè)學(xué)習(xí)器,為了控制下降速度海铆,提供一個(gè)學(xué)習(xí)率迹恐。
-
算法的示意圖如下:
分類與預(yù)測(cè)模型
- 上述示意圖最后的強(qiáng)學(xué)習(xí)器可以表示為:
- 除第一個(gè)基學(xué)習(xí)器,其他基學(xué)習(xí)器都是使用上一次的殘差作為標(biāo)簽作為訓(xùn)練得到卧斟。
損失函數(shù)模型
- 由于GradientBoosting算法可以使用任何損失函數(shù)殴边,所以使用通用模式表示:
-
- 其中表示訓(xùn)練樣本的容量。
- 其中表示最終的學(xué)習(xí)器
-
GradientBoosting算法思想
-
GradientBoosting算法算法的基本思想是梯度下降珍语,基于權(quán)重變量的梯度下降是:
- 爭對(duì)損失函數(shù)是:
-
GradientBoosting算法中下降的是函數(shù):
- 爭對(duì)損失函數(shù)是:
-
如果考慮第次基學(xué)習(xí)器的訓(xùn)練锤岸,公式表示為:
-
如果上面是第次使用樣本與上次殘差作為標(biāo)簽訓(xùn)練出來的基學(xué)習(xí)器,則第k次最終學(xué)習(xí)器是:
-
如果集成的基學(xué)習(xí)器是m個(gè),則最終的最強(qiáng)學(xué)習(xí)器是:
-
總結(jié):
- 使用機(jī)器學(xué)習(xí)去擬合每次的識(shí)別誤差板乙,這就是GradientBoosting算法的特點(diǎn)是偷。
GradientBoosting算法過程
- 假設(shè)訓(xùn)練集是:
-
,
- 其中為樣本容量(樣本總數(shù))
- 表示第個(gè)樣本募逞;
- 標(biāo)識(shí)第個(gè)樣本的分類標(biāo)簽(假設(shè)是二分類晓猛,并且標(biāo)簽標(biāo)準(zhǔn)化為-1與1);
-
,
-
第一個(gè)基學(xué)習(xí)器
- 使用指定的算法凡辱,與已知樣本訓(xùn)練出一個(gè)基學(xué)習(xí)器:
開始次基學(xué)習(xí)器的訓(xùn)練戒职,使用表示第次
-
計(jì)算第k次的殘差:
-
注意:
- 如果損失函數(shù)是均方差函數(shù),殘差是:
-
使用數(shù)據(jù)集訓(xùn)練得到基學(xué)習(xí)器
- 這樣得到的基學(xué)習(xí)器是使得最小透乾。
-
直到m步洪燥,完成集成學(xué)習(xí)過程磕秤,得到最強(qiáng)學(xué)習(xí)器:
GradientBoosting算法實(shí)現(xiàn)
- 備注:
- 下面算法直接抄襲引用網(wǎng)絡(luò)資源:
https://github.com/massquantity/ML-algorithms-implemetation/blob/master/Gradient%20Boosting/GradientBoosting_article.py
- 下面算法直接抄襲引用網(wǎng)絡(luò)資源:
import numpy as np
from sklearn.base import clone
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.ensemble import GradientBoostingRegressor, GradientBoostingClassifier
from sklearn.metrics import zero_one_loss
from sklearn import datasets
from scipy import stats
def SquaredLoss_NegGradient(y_pred, y):
return y - y_pred
def Huberloss_NegGradient(y_pred, y, alpha):
diff = y - y_pred
delta = stats.scoreatpercentile(np.abs(diff), alpha * 100)
g = np.where(np.abs(diff) > delta, delta * np.sign(diff), diff)
return g
def logistic(p):
return 1 / (1 + np.exp(-2 * p))
def LogisticLoss_NegGradient(y_pred, y):
g = 2 * y / (1 + np.exp(1 + 2 * y * y_pred)) # logistic_loss = log(1+exp(-2*y*y_pred))
return g
def modified_huber(p):
return (np.clip(p, -1, 1) + 1) / 2
def Modified_Huber_NegGradient(y_pred, y):
margin = y * y_pred
g = np.where(margin >= 1, 0, np.where(margin >= -1, y * 2 * (1-margin), 4 * y))
# modified_huber_loss = np.where(margin >= -1, max(0, (1-margin)^2), -4 * margin)
return g
class GradientBoosting(object):
def __init__(self, M, base_learner, learning_rate=1.0, method="regression", tol=None, subsample=None,
loss="square", alpha=0.9):
self.M = M
self.base_learner = base_learner
self.learning_rate = learning_rate
self.method = method
self.tol = tol
self.subsample = subsample
self.loss = loss
self.alpha = alpha
def fit(self, X, y):
# tol為early_stopping的閾值,如果使用early_stopping捧韵,則從訓(xùn)練集中分出驗(yàn)證集
if self.tol is not None:
X, X_val, y, y_val = train_test_split(X, y, random_state=2)
former_loss = float("inf")
count = 0
tol_init = self.tol
init_learner = self.base_learner
y_pred = init_learner.fit(X, y).predict(X) # 初始值
self.base_learner_total = [init_learner]
for m in range(self.M):
if self.subsample is not None: # subsample
sample = [np.random.choice(len(X), int(self.subsample * len(X)), replace=False)]
X_s, y_s, y_pred_s = X[sample], y[sample], y_pred[sample]
else:
X_s, y_s, y_pred_s = X, y, y_pred
# 計(jì)算負(fù)梯度
if self.method == "regression":
if self.loss == "square":
response = SquaredLoss_NegGradient(y_pred_s, y_s)
elif self.loss == "huber":
response = Huberloss_NegGradient(y_pred_s, y_s, self.alpha)
elif self.method == "classification":
if self.loss == "logistic":
response = LogisticLoss_NegGradient(y_pred_s, y_s)
elif self.loss == "modified_huber":
response = Modified_Huber_NegGradient(y_pred_s, y_s)
base_learner = clone(self.base_learner)
y_pred += base_learner.fit(X_s, response).predict(X) * self.learning_rate
self.base_learner_total.append(base_learner)
'''early stopping'''
if m % 10 == 0 and m > 300 and self.tol is not None:
p = np.array([self.base_learner_total[m].predict(X_val) for m in range(1, m+1)])
p = np.vstack((self.base_learner_total[0].predict(X_val), p))
stage_pred = np.sum(p, axis=0)
if self.method == "regression":
later_loss = np.sqrt(mean_squared_error(stage_pred, y_val))
if self.method == "classification":
stage_pred = np.where(logistic(stage_pred) >= 0.5, 1, -1)
later_loss = zero_one_loss(stage_pred, y_val)
if later_loss > (former_loss + self.tol):
count += 1
self.tol = self.tol / 2
print(self.tol)
else:
count = 0
self.tol = tol_init
if count == 2:
self.M = m - 20
print("early stopping in round {}, best round is {}, M = {}".format(m, m - 20, self.M))
# print("loss: ", later_loss)
break
former_loss = later_loss
return self
def predict(self, X):
pred = np.array([self.base_learner_total[m].predict(X) * self.learning_rate for m in range(1, self.M + 1)])
pred = np.vstack((self.base_learner_total[0].predict(X), pred)) # 初始值 + 各基學(xué)習(xí)器
if self.method == "regression":
pred_final = np.sum(pred, axis=0)
elif self.method == "classification":
if self.loss == "modified_huber":
p = np.sum(pred, axis=0)
pred_final = np.where(modified_huber(p) >= 0.5, 1, -1)
elif self.loss == "logistic":
p = np.sum(pred, axis=0)
pred_final = np.where(logistic(p) >= 0.5, 1, -1)
return pred_final
def stage_predict(self, X):
pred = None
for base_learner in self.base_learner_total:
current_pred = base_learner.predict(X)
if pred is None:
pred = current_pred
else:
pred += current_pred * self.learning_rate
if self.method == "regression":
yield pred
elif self.method == "classification":
if self.loss == "logistic":
yield np.where(logistic(pred) >= 0.5, 1, -1)
elif self.loss == "modified_huber":
yield np.where(modified_huber(pred) >= 0.5, 1, -1)
class GBRegression(GradientBoosting):
def __init__(self, M, base_learner, learning_rate, method="regression", loss="square",tol=None, subsample=None, alpha=0.9):
super(GBRegression, self).__init__(M=M, base_learner=base_learner, learning_rate=learning_rate, method=method,
loss=loss, tol=tol, subsample=subsample, alpha=alpha)
class GBClassification(GradientBoosting):
def __init__(self, M, base_learner, learning_rate, method="classification", loss="logistic", tol=None, subsample=None):
super(GBClassification, self).__init__(M=M, base_learner=base_learner, learning_rate=learning_rate, method=method,
loss=loss, tol=tol, subsample=subsample)
if __name__ == "__main__":
X, y = datasets.make_regression(n_samples=20000, n_features=10, n_informative=4, noise=1.1, random_state=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
model = GBRegression(M=1000, base_learner=DecisionTreeRegressor(max_depth=2, random_state=1), learning_rate=0.1,
loss="huber")
model.fit(X_train, y_train)
pred = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, pred))
print('RMSE: ', rmse)
X, y = datasets.make_classification(n_samples=20000, n_features=10, n_informative=4, flip_y=0.1,
n_clusters_per_class=1, n_classes=2, random_state=1)
y[y==0] = -1
X_train, X_test, y_train, y_test = train_test_split(X, y)
model = GBClassification(M=1000, base_learner=DecisionTreeRegressor(max_depth=1, random_state=1), learning_rate=1.0,
method="classification", loss="logistic")
model.fit(X_train, y_train)
pred = model.predict(X_test)
acc = np.zeros(pred.shape)
acc[np.where(pred == y_test)] = 1
accuracy = np.sum(acc) / len(pred)
print('accuracy logistic score: ', accuracy)
model = GBClassification(M=1000, base_learner=DecisionTreeRegressor(max_depth=1, random_state=1), learning_rate=1.0,
method="classification", loss="modified_huber")
model.fit(X_train, y_train)
pred = model.predict(X_test)
acc = np.zeros(pred.shape)
acc[np.where(pred == y_test)] = 1
accuracy = np.sum(acc) / len(pred)
print('accuracy modified_huber score: ', accuracy)
RMSE: 8.45962295476
accuracy logistic score: 0.9374
accuracy modified_huber score: 0.9356
關(guān)于GradientBoosting算法的說明
-
Gradient Boosting算法理論上可以選擇多種不同的學(xué)習(xí)算法作為基學(xué)習(xí)器市咆,但實(shí)際使用地最多的無疑是決策樹,這并非偶然再来。
- 決策樹有很多優(yōu)良的特性:
- 能靈活處理各種類型的數(shù)據(jù)蒙兰,包括連續(xù)值和離散值;
- 對(duì)缺失值不敏感芒篷;
- 不需要做特征標(biāo)準(zhǔn)化/歸一化搜变;
- 可解釋性好;
-決策樹致命缺點(diǎn)是不穩(wěn)定针炉,導(dǎo)致容易過擬合挠他,因而很多時(shí)候準(zhǔn)確率不如其他算法。
決策樹是非參數(shù)模型篡帕,這并不意味著其沒有參數(shù)殖侵,而是在訓(xùn)練之前參數(shù)數(shù)量是不確定的,因此完全生長的決策樹有著較大的自由度镰烧,能最大化地?cái)M合訓(xùn)練數(shù)據(jù)拢军。然而單顆決策樹是不穩(wěn)定的,樣本數(shù)相同的訓(xùn)練集產(chǎn)生微小變動(dòng)就能導(dǎo)致最終模型的較大差異怔鳖,即模型的方差大朴沿,泛化性能不好。
集成學(xué)習(xí)的另一代表Bagging可以解決這個(gè)問題败砂。而Bagging的拓展算法 —— 隨機(jī)森林赌渣,通過在樹內(nèi)部結(jié)點(diǎn)的分裂過程中,隨機(jī)選取固定數(shù)量的特征納入分裂的候選項(xiàng)昌犹,這樣就進(jìn)一步降低了單模型之間的相關(guān)性坚芜,總體模型的方差也比Bagging更低。
-
決策樹和Gradient Boosting結(jié)合誕生了GBDT斜姥,GBDT繼承了決策樹的諸多優(yōu)點(diǎn)鸿竖,同時(shí)也改進(jìn)了其缺點(diǎn)。
- 由于GBDT采用的樹都是復(fù)雜度低的樹铸敏,所以方差很小缚忧,通過梯度提升的方法集成多個(gè)決策樹,最終能夠很好的解決過擬合的問題杈笔。
- 然而Boosting共有的缺點(diǎn)為訓(xùn)練是按順序的闪水,難以并行,這樣在大規(guī)模數(shù)據(jù)上可能導(dǎo)致速度過慢蒙具;
- 近年來XGBoost和LightGBM的出現(xiàn)都極大緩解了這個(gè)問題球榆。
- 決策樹有很多優(yōu)良的特性:
Stacking集成
Stacking算法思路
-
Stacking(有時(shí)候也稱之為stacked generalization)是指訓(xùn)練一個(gè)模型用于組合(combine)其他各個(gè)模型朽肥。
- 首先訓(xùn)練多個(gè)不同的模型;
- 然后用訓(xùn)練好的多個(gè)模型的輸出作為為輸入來訓(xùn)練一個(gè)模型持钉,以得到一個(gè)最終的輸出衡招。
我們稱組合的訓(xùn)練模型為元(Meta)學(xué)習(xí)器;
-
注意:
- 通常使用單層logistic回歸作為組合模型每强。
Stacking算法過程
- 假設(shè)訓(xùn)練集是:
-
始腾,
- 其中為樣本容量(樣本總數(shù))
- 表示第個(gè)樣本;
-
始腾,
-
使用不同算法空执,訓(xùn)練多個(gè)基學(xué)習(xí)器
-
- 其中表示基學(xué)習(xí)器的個(gè)數(shù)浪箭。
-
-
使用基學(xué)習(xí)器得到新的訓(xùn)練集
-
使用作為訓(xùn)練集訓(xùn)練一個(gè)元學(xué)習(xí)器
就是最后的強(qiáng)學(xué)習(xí)器
Stacking算法的其他方式-Blend結(jié)合
-
Blend結(jié)合策略:
- 把訓(xùn)練集分成A,B兩個(gè)人部分:
- 使用A訓(xùn)練對(duì)個(gè)基學(xué)習(xí)器脆烟;
- 使用多個(gè)基學(xué)習(xí)器多數(shù)據(jù)集B做預(yù)測(cè)山林;
- 使用預(yù)測(cè)值作為元學(xué)習(xí)器的訓(xùn)練集房待;
- 最終訓(xùn)練出元學(xué)習(xí)器邢羔。
-
上述算法還有很多變形處理,比如使用交叉驗(yàn)證的方式:
使用折驗(yàn)證桑孩,把數(shù)據(jù)集分成個(gè)訓(xùn)練方案拜鹤;
使用訓(xùn)練集訓(xùn)練,使用測(cè)試集得到測(cè)試輸出流椒,并使用測(cè)試集輸出作為元學(xué)習(xí)器的訓(xùn)練樣本敏簿,元學(xué)習(xí)器的輸入維數(shù)為基學(xué)習(xí)器個(gè)數(shù),K折中每折預(yù)測(cè)值一共K宣虾,取K個(gè)的平均值作為輸出惯裕。
使用K折驗(yàn)證輸出(K)構(gòu)成的數(shù)據(jù)集訓(xùn)練的元學(xué)習(xí)器就是最終最強(qiáng)學(xué)習(xí)器。
Stacking算法的實(shí)現(xiàn)
import numpy as np
from sklearn.model_selection import KFold
def get_stacking(clf, x_train, y_train, x_test, n_folds=10):
"""
這個(gè)函數(shù)是stacking的核心绣硝,使用交叉驗(yàn)證的方法得到次級(jí)訓(xùn)練集
x_train, y_train, x_test 的值應(yīng)該為numpy里面的數(shù)組類型 numpy.ndarray .
如果輸入為pandas的DataFrame類型則會(huì)把報(bào)錯(cuò)"""
train_num, test_num = x_train.shape[0], x_test.shape[0]
second_level_train_set = np.zeros((train_num,))
second_level_test_set = np.zeros((test_num,))
test_nfolds_sets = np.zeros((test_num, n_folds))
kf = KFold(n_splits=n_folds)
for i,(train_index, test_index) in enumerate(kf.split(x_train)):
x_tra, y_tra = x_train[train_index], y_train[train_index]
x_tst, y_tst = x_train[test_index], y_train[test_index]
clf.fit(x_tra, y_tra)
second_level_train_set[test_index] = clf.predict(x_tst)
test_nfolds_sets[:,i] = clf.predict(x_test)
second_level_test_set[:] = test_nfolds_sets.mean(axis=1)
return second_level_train_set, second_level_test_set
#我們這里使用5個(gè)分類算法蜻势,為了體現(xiàn)stacking的思想,就不加參數(shù)了
from sklearn.ensemble import (RandomForestClassifier, AdaBoostClassifier,
GradientBoostingClassifier, ExtraTreesClassifier)
from sklearn.svm import SVC
rf_model = RandomForestClassifier()
adb_model = AdaBoostClassifier()
gdbc_model = GradientBoostingClassifier()
et_model = ExtraTreesClassifier()
svc_model = SVC()
#在這里我們使用train_test_split來人為的制造一些數(shù)據(jù)
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
train_x, test_x, train_y, test_y = train_test_split(iris.data, iris.target, test_size=0.2)
train_sets = []
test_sets = []
for clf in [rf_model, adb_model, gdbc_model, et_model, svc_model]:
train_set, test_set = get_stacking(clf, train_x, train_y, test_x)
train_sets.append(train_set)
test_sets.append(test_set)
meta_train = np.concatenate([result_set.reshape(-1,1) for result_set in train_sets], axis=1)
meta_test = np.concatenate([y_test_set.reshape(-1,1) for y_test_set in test_sets], axis=1)
#使用決策樹作為我們的次級(jí)分類器
from sklearn.tree import DecisionTreeClassifier
dt_model = DecisionTreeClassifier()
dt_model.fit(meta_train, train_y)
df_predict = dt_model.predict(meta_test)
print((df_predict==test_y).sum())
29
XGBoost算法
XGBoost算法介紹
-
XGBoost全名叫(eXtreme Gradient Boosting)極限梯度提升
- 經(jīng)常被用在一些比賽中鹉胖,其效果顯著握玛。
- 它是大規(guī)模并行boosted tree的工具,它是目前最快最好的開源boosted tree工具包甫菠。
- XGBoost 所應(yīng)用的算法就是 GBDT(gradient boosting decision tree)的改進(jìn)挠铲,改進(jìn)Boosting算法串行的局限性;
- 既可以用于分類也可以用于回歸問題中寂诱。
XGBoost是以分類回歸樹(CART樹)進(jìn)行組合
XGBoost算法模型
預(yù)測(cè)與分類模型
與其他Boosting算法模型一樣
-
- 是基學(xué)習(xí)器的個(gè)數(shù)拂苹;
- 表示第個(gè)基學(xué)習(xí)器;
損失函數(shù)模型
-
- 其中表示樹的復(fù)雜度的函數(shù)痰洒,越小復(fù)雜度越低醋寝,泛化能力越強(qiáng)搞挣;
- 實(shí)際是GradientBoosting梯度下降算法的改進(jìn),后面增加了一個(gè)樹的復(fù)雜度正則項(xiàng)(懲罰項(xiàng))音羞。
-
通常正則項(xiàng)(懲罰項(xiàng))可以定義如下:
-
- 其中是所有葉子節(jié)點(diǎn)輸出構(gòu)成的向量囱桨;
- 其中表示葉子節(jié)點(diǎn)的個(gè)數(shù);
- 其中表示葉子切分的難度嗅绰;
- 其中是正則化系數(shù)舍肠;
-
-
如果使用標(biāo)識(shí)每個(gè)節(jié)點(diǎn)的輸出值,則損失函數(shù)可以表示如下:
由損失函數(shù)推導(dǎo)的算法以及算法過程窘面,這類不深入推導(dǎo)與解釋翠语,我們直接使用第三方的實(shí)現(xiàn)模塊;
XGBoost算法
官方網(wǎng)站介紹
-
官方地址:
https://xgboost.readthedocs.io/en/latest/
-
項(xiàng)目代碼地址:
https://github.com/dmlc/xgboost
安裝
-
所有安裝的幫助在官網(wǎng)都有說明:
-
安裝指令
pip3 install xgboost
-
安裝過程:
-
安裝后的版本:
- 安裝注意:
- 官方安裝提示需要編譯安裝xgboost共享庫與動(dòng)態(tài)庫财边;
XGBoost的模塊結(jié)構(gòu)
- 可以使用多種方式的幫助獲得模塊結(jié)構(gòu)
XGBoost使用例子
from numpy import loadtxt
from xgboost import XGBClassifier
from sklearn import datasets as ds
from sklearn.model_selection import train_test_split
data,target = ds.load_iris(return_X_y=True)
data_train, data_test, target_train, target_test = train_test_split(data, target, test_size=0.2)
classifier = XGBClassifier()
classifier.fit(data_train, target_train)
pre = classifier.predict(data_test)
(pre==target_test).sum()
28
- 注意:
- 其中XGBClassifier類的構(gòu)造器參數(shù)比較多肌括,關(guān)注其中與算法有關(guān)的參數(shù):
% matplotlib inline
from xgboost import plot_importance
plot_importance(classifier)
<matplotlib.axes._subplots.AxesSubplot at 0x113c134e0>
% matplotlib inline
from xgboost import plot_tree,to_graphviz
g = to_graphviz(classifier,num_trees=10)
#存儲(chǔ)決策樹到圖像
import codecs
f=codecs.open('xgb_tree.svg', mode='wb')
f.write(g.pipe('svg'));
f.close()
# plot_tree 缺少png圖像轉(zhuǎn)換環(huán)境
g