集成學習綜述與代碼實現

1. 集成學習

  • 定義

顧名思義,就是將多個單一模型進行組合,最後形成一個更好的模型的過程。之所以組合多個單一學習器,是因為很多時候單一學習器的效果不夠理想,多個模型組合可以互幫互助,各取所長,從而能夠更好的完成任務。集成學習一般的結構是先學習單一的學習器,之後通過某種策略將其組合在一起。

  • 條件

(a)首先應該保證分類器之間的差異性,如果分類器都相同,那麼組合的出來的結果是不會有變化的。

(b)每個個體分類器的精度必須大於0.5,如果個體分類器的精度低於0.5,那集成之後的精度低於規模的增大而降低。但如果精度是大於0.5的,最後的分類結果會趨於1。

  • 分類

根據個體學習器的生成方式,目前的集成學習方法大致可以分成兩類:第一類是單個學習器之間有著很強的依賴關係,需要以串行的序列化的方式生成,代表方法:Boosting。Boosting方法中也有很多分類:Adaboost、GBDT等等。第二類就是個體學習器之間不存在很強的依賴關係,學習器可以並行生成,代表方法:Bagging 和Random Forest。

2. Bagging和Boosting

2.1 Bagging

Bagging又稱為套袋法:

每次從樣本集合中有放回地抽取n個值,一共抽取k輪,形成k個訓練集合,訓練集合之間彼此獨立。對於分類問題,k個訓練器可以訓練出k個模型,從而產生k個結果,對於迴歸問題,取均值作為預測結果。

2.2 Boosting

每次從樣本集合中有放回地抽取n個值,一共抽取k輪,形成k個訓練集合,訓練集合之間彼此獨立。對於分類問題,k個訓練器可以訓練出k個模型,從而產生k個結果,對於迴歸問題,取均值作為預測結果。

梯度提升的Boosting方法則是使用代價函數對上一輪訓練的損失函數f的偏導數來擬合殘差。

2.3 Bagging和Boosting的區別

  • 訓練數據集

Bagging是每次從樣本集中有放回地抽取n個樣本,抽取k次,形成k個樣本集合,樣本集合之間相互獨立;

Boosting則是訓練集合保持不變,每次變化的是訓練集中樣例的權重。

  • 樣例權重

Bagging使用的是均勻採樣,每個樣例權重相等;

Boosting根據上一輪結果調整權重,錯誤率越大權重越大。

  • 預測函數

Bagging所有的預測函數權重相同;

Boosting預測函數權重不同,預測誤差小的權重越大。

  • 並行計算

Bagging各個預測函數可以並行生成;

Boosting只能順序生成,因為後一個預測要用到上一個結果。

2.4 樣本權值和模型權值

Boosting會減小在上一輪正確樣本的權重,增大錯誤樣本的權重(因為正確樣本殘差小,錯誤樣本殘差大。組合模型的時候會將錯誤率高的模型賦予低的權重,將錯誤率低的模型賦予高的權重,有助於增加組合後模型準確率。

3. 組合模型方式

3.1 平均法

(a)簡單平均

將所有的個體學習器的數值型輸出求平均。

(b)加權平均

各個學習器有自身的權重,根據權重來進行加權平均,代表算法:Adaboost。

3.2 學習法

當訓練數據很多時,更加強大的結合策略就是學習法,也就是通過另一個學習器來進行組合。典型代表是Stacking。把個體學習器稱之為初級學習器,用於結合的學習器稱為次級學習器或者元學習器。

Stacking先從初始訓練集訓練出初級學習器,然後生成一個新的數據集來訓練次級學習器。新的數據集是以初級學習器的輸出作為樣例,初始樣本的標記被當做樣例標記。

4. AdaBoost算法

4.1 核心思想

  • 權值的更新取決於上一輪訓練結果,增大誤分類的樣本的權值,減小正確分類的樣本權值,在下一輪訓練的時候誤分類樣本會得到更多的關注,正確分類的樣本得到的關注變少。
  • 因為每次挑選的是誤差最小的分類器,增大誤分類的樣本的權重相當於增大當前分類器的誤差,錯誤率高的分類器對應小權重,錯誤率低的分類器對應大權重。

4.2 前向分步算法

簡單來說就是從前向後,每次只學習一個基函數,最後通過基函數的線性組合,去逼近優化目標函數。

4.3 算法步驟



集成學習綜述與代碼實現


集成學習綜述與代碼實現


集成學習綜述與代碼實現


集成學習綜述與代碼實現


集成學習綜述與代碼實現



AdaBoost的Python實現(iris數據集)

import os
import numpy as np
import pandas as pd
from sklearn.cross_validation import train_test_split
from sklearn.datasets import load_iris
def load_data():
iris = load_iris()#導入數據

#轉換為dataframe
df = pd.DataFrame(iris.data,columns=iris.feature_names)
df['label'] = iris.target#導入標籤
#把0和1類拿出來
df.columns = ['sepal length','sepal width','petal length','petal width','label']#重命名列名
data = np.array(df.iloc[:100,[0,1,-1]])#前100行為分類為0和1兩類
#把為0的變成-1
for i in range(len(data)):
if data[i,-1]==0:#把0類的轉換為-1類
data[i,-1] = -1
return data[:,0:2],data[:,-1]
X,y = load_data()#導入數據
train_x,test_x,train_x,train_y = train_test_split(X,y,test_size=0.2,random_state=2333)#劃分訓練集和測試集
#定義Adaboost類
class Adaboost(object):
def __init__(self,n_estimators=1,learning_rate = 0):
'''
n_estimators 分類器個數
learning_rate 步長
'''
self.n_estimate = n_estimators
self.learning_rate = learning_rate
def init_data(self,X,y):
'''
X:屬性
y:標籤
'''
self.M,self.N = X.shape#屬性一共有M行N列
self.X = X#訓練屬性
self.y = y#標籤數據
self.w = [1.0/self.M]*self.M#初始化權重為1/M
self.alpha = []
self.clf_set = []#模型參數集合
def _alpha(self,error):#誤差越大 alpha值越小
return 0.5*np.log((1-error)/error)
def _Z(self, weights, a, clf):
'''
a:各個分類器的權重
weights:權重

clf:compare_array
'''
return sum([weights[i]*np.exp(-1*a*self.y[i]*clf[i]) for i in range(self.M)])
def _w(self, a, clf, Z):#相當於給權重w做了一層softmax 歸一化
'''
a:各個分類器的權重
weights:權重
clf:compare_array
誤分類self.y[i]*clf[i]=1權重增大 正確分類self.y[i]*clf[i]=-1權重變小
'''
for i in range(self.M):
self.w[i] = self.w[i]*np.exp(-1*a*self.y[i]*clf[i])/ Z
#具體算法
def G(self,feature,label,weights):#每次更新的是w
#求出屬性列中的最大值和最小值
f_min = min(feature)
f_max = max(feature)
#在屬性列上迭代的次數
n_step = (f_max-f_min+self.learning_rate)//self.learning_rate
error = 10000.0#定義為無窮大
best_v = 0.0#表示屬性中最好分類點的值
direct,compare_array = None,None
#direct表示優化方向
#compare_array表示該方向對應的c_o_positive or c_o_negative數組
for i in range(1,int(n_step)):
v = f_min+self.learning_rate*i#每次增加的步長為learning_rate
if v not in feature:#如果v不存在屬性列中
c_o_positive = np.array([1 if feature[k] > v else -1 for k in range(len(feature))])#大於v的label置為1 小於v的置為-1
#print([weights[k] for k in range(len(feature)) if c_o_positive[k]!=label[k]])
error_positive = sum([weights[k] for k in range(len(feature)) if c_o_positive[k]!=label[k]])#記錄不等於label的weight並求和
c_o_negative = np.array([-1 if feature[k] > v else 1 for k in range(len(feature))])#同上 但反向
error_negative = sum([weights[k] for k in range(len(feature)) if c_o_negative[k]!=label[k]])
if error_positive < error_negative:#如果正向誤差小於反向誤差
weight_error = error_positive#記錄誤差

_compare_array = c_o_positive
direct = 'positive'
else:
weight_error = error_negative
_compare_array = c_o_negative
direct = 'negative'
if weight_error < error:
error = weight_error#記錄誤差
best_v = v#記錄最佳分類值
compare_array = _compare_array#記錄label
return best_v,error,compare_array,direct
def _G(self,x,v,direct):
if direct=='positive':
return 1 if x > v else -1
else:
return -1 if x>x else -1
def fit(self,X,y):
self.init_data(X,y)#初始化類內參數
for i in range(self.n_estimate):#迭代次數
best_clf_error,best_v,clf_result = 100000,None,None
#單個屬性
for j in range(self.N):#在屬性列上迭代
feature = self.X[:,j]#
v,error,compare,direct = self.G(feature,self.y,self.w)
if error best_clf_error = error
best_v = v
final_direct = direct
clf_result = compare
axis = j
if best_clf_error == 0:
break
a = self._alpha(best_clf_error)#計算alpha 0.5*np.log((1-error)/error)
self.alpha.append(a)#記錄alpha
z = self._Z(self.w,a,clf_result)
self.clf_set.append((axis,best_v,final_direct))#clf_set集合
self._w(a,clf_result,z)#修改self.w
def predict(self,feature):#預測函數(比較簡單)
result = 0.0
for i in range(len(self.clf_set)):
axis,best_v,final_direct = self.clf_set[i]
f = feature[axis]
result += self.alpha[i] * self._G(f,best_v,final_direct)
return 1 if result >0 else -1
def score(self,X_test,y_test):#打分函數(比較簡單)

count = 0
for i in range(len(X_test)):
feature = X_test[i]
if self.predict(feature) == y_test[i]:
count += 1
return count/len(X_test)


分享到:


相關文章: