集成學習方法
通過組合多個弱基分類器來實現強分類器目的,從而提高分類性能。集成學習是一類算法,並不是指一個算法。集成學習策略有非常多種,包括數據層面、模型層面和算法層面三個方面集成,這方面由於研究非常廣泛,論文非常多,可以去知網下載碩博論文,論文總結非常全面。常用的兩種集成學習方法是:bagging袋裝法,典型代表隨機森林(Random Forests)和boosting提升法,典型代表GBDT(Gradient Boosting Decision Tree)以及最近非常火熱的XGBOOST。
AdaBoost算法原理和優缺點(1995年的算法了)
優點:泛化錯誤率低,易編碼,可以應用在大部分分類器上,無參數調整
缺點:對離群點敏感(這一類都有這個缺點)
首先講一下boosting的流程
先從數據集D中隨機選出一些數據得到數據集D1,通過訓練得到分類器C1。
然后再從D中隨機選出一些數據,用C1來進行測試,會得到D2,此時的D2中會有一些數據是C1分對了的,有一些是C1分錯了的。然后用這些數據進行訓練,會得到C2,。
然后再從D中隨機選出一些數據,用C1、C2一起來測試,丟棄兩個分類器判斷相同的結果,對於某個數據如果兩個分類器的判斷結果不一樣則保留,這些保留下來的數據組成D3,然后對D3進行訓練得到C3分類器。
預測時,將三個分類器同時作用在數據上,對於C1與C2得到的結果如果一樣,就輸出O1,如果不一樣,則輸出C3的結果O3。
以上就是一個簡單的boosting算法
由於AdaBoost是boosting的改進方法,而且性能比較穩定,故而在實際中一般都直接使用AdaBoost而不會使用原始的boosting。
adaboost流程:
給定一個樣本,例如標簽只有-1和1
初始化樣本權重,所有樣本權重都是一樣的
T為分類器的個數
首先訓練第一個弱分類器,弱分類器輸出后會得到一個錯誤率,利用錯誤率來得到一個α,錯誤率越高,α越小,因為這個分類器比較垃圾,在最后算結果時都是α乘以分類器。垃圾點的分類器肯定權重就要小一點。
根據錯誤率將樣本權重更新,對分錯的樣本權重要加大,分對的樣本權重要縮小,經過t輪之后就得到T個弱分類器。
最后將T個弱分類器乘以各自的權重α相加,經過激活函數的處理,得到結果。
構建單層決策樹代碼實現:
# -*-coding:utf-8 -*- import numpy as np import matplotlib.pyplot as plt def loadSimpData(): """ 創建單層決策樹的數據集 Parameters: 無 Returns: dataMat - 數據矩陣 classLabels - 數據標簽 """ datMat = np.matrix([[1., 2.1], #生成一個(5,2)的矩陣 [1.5, 1.6], [1.3, 1.], [1., 1.], [2., 1.]]) classLabels = [1.0, 1.0, -1.0, -1.0, 1.0] #生成一個(1,5)的矩陣 return datMat, classLabels def stumpClassify(dataMatrix, dimen, threshVal, threshIneq): """ 單層決策樹分類函數 Parameters: dataMatrix - 數據矩陣 dimen - 第dimen列,也就是第幾個特征 threshVal - 閾值 threshIneq - 標志 Returns: retArray - 分類結果 """ retArray = np.ones((np.shape(dataMatrix)[0], 1)) # np.shape(dataMatrix)得到(5,2)[0]為5,所以retArray.shape=(5,1),初始化retArray為1 if threshIneq == 'lt': #因為每一個閾值有兩種判斷方式,小於閾值為-1或者大於閾值為-1 retArray[dataMatrix[:, dimen] <= threshVal] = -1.0 # 如果小於閾值,則賦值為-1 else: retArray[dataMatrix[:, dimen] > threshVal] = -1.0 # 如果大於閾值,則賦值為-1 return retArray def buildStump(dataArr, classLabels, D): """ 找到數據集上最佳的單層決策樹 Parameters: dataArr - 數據矩陣 classLabels - 數據標簽 D - 樣本權重 Returns: bestStump - 最佳單層決策樹信息 minError - 最小誤差 bestClasEst - 最佳的分類結果 """ dataMatrix = np.mat(dataArr); #dataMatrix.shape=(5,2) labelMat = np.mat(classLabels).T #labelMat.shape = (5,1) m, n = np.shape(dataMatrix) #m=5,n=2 numSteps = 10.0; bestStump = {}; bestClasEst = np.mat(np.zeros((m, 1))) #初始化為(5,1),值為0 minError = float('inf') # 最小誤差初始化為正無窮大 for i in range(n): # 遍歷所有特征 rangeMin = dataMatrix[:, i].min(); #例如當i=0時,從第0列中取最小值 rangeMax = dataMatrix[:, i].max() # 找到特征中最小的值和最大值 stepSize = (rangeMax - rangeMin) / numSteps # 計算步長 for j in range(-1, int(numSteps) + 1): #對其中一個特征進行閾值划分 for inequal in ['lt', 'gt']: # 大於和小於的情況,均遍歷。lt:less than,gt:greater than threshVal = (rangeMin + float(j) * stepSize) # 計算閾值,例如第一輪閾值為0.9=1+(-1)*0.1 predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal) # 計算分類結果 errArr = np.mat(np.ones((m, 1))) # 初始化誤差矩陣 errArr[predictedVals == labelMat] = 0 # 分類正確的,賦值為0 weightedError = D.T * errArr # 計算誤差,第一輪0.4=0.2*2 print("split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % ( i, threshVal, inequal, weightedError)) if weightedError < minError: # 找到誤差最小的分類方式,然后將數據更新 minError = weightedError bestClasEst = predictedVals.copy() bestStump['dim'] = i bestStump['thresh'] = threshVal bestStump['ineq'] = inequal return bestStump, minError, bestClasEst if __name__ == '__main__': dataArr, classLabels = loadSimpData() D = np.mat(np.ones((5, 1)) / 5) bestStump, minError, bestClasEst = buildStump(dataArr, classLabels, D) print('bestStump:\n', bestStump) print('minError:\n', minError) print('bestClasEst:\n', bestClasEst)
下面實例為簡單的adaboost實現過程:
# -*-coding:utf-8 -*- import numpy as np import matplotlib.pyplot as plt def loadSimpData(): """ 創建單層決策樹的數據集 Parameters: 無 Returns: dataMat - 數據矩陣 classLabels - 數據標簽 """ datMat = np.matrix([[1., 2.1], [1.5, 1.6], [1.3, 1.], [1., 1.], [2., 1.]]) classLabels = [1.0, 1.0, -1.0, -1.0, 1.0] return datMat, classLabels def stumpClassify(dataMatrix, dimen, threshVal, threshIneq): """ 單層決策樹分類函數 Parameters: dataMatrix - 數據矩陣 dimen - 第dimen列,也就是第幾個特征 threshVal - 閾值 threshIneq - 標志 Returns: retArray - 分類結果 """ retArray = np.ones((np.shape(dataMatrix)[0], 1)) # 初始化retArray為1 if threshIneq == 'lt': retArray[dataMatrix[:, dimen] <= threshVal] = -1.0 # 如果小於閾值,則賦值為-1 else: retArray[dataMatrix[:, dimen] > threshVal] = -1.0 # 如果大於閾值,則賦值為-1 return retArray def buildStump(dataArr, classLabels, D): """ 找到數據集上最佳的單層決策樹 Parameters: dataArr - 數據矩陣 classLabels - 數據標簽 D - 樣本權重 Returns: bestStump - 最佳單層決策樹信息 minError - 最小誤差 bestClasEst - 最佳的分類結果 """ dataMatrix = np.mat(dataArr); labelMat = np.mat(classLabels).T m, n = np.shape(dataMatrix) numSteps = 10.0; bestStump = {}; bestClasEst = np.mat(np.zeros((m, 1))) minError = float('inf') # 最小誤差初始化為正無窮大 for i in range(n): # 遍歷所有特征 rangeMin = dataMatrix[:, i].min(); rangeMax = dataMatrix[:, i].max() # 找到特征中最小的值和最大值 stepSize = (rangeMax - rangeMin) / numSteps # 計算步長 for j in range(-1, int(numSteps) + 1): for inequal in ['lt', 'gt']: # 大於和小於的情況,均遍歷。lt:less than,gt:greater than threshVal = (rangeMin + float(j) * stepSize) # 計算閾值 predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal) # 計算分類結果 errArr = np.mat(np.ones((m, 1))) # 初始化誤差矩陣 errArr[predictedVals == labelMat] = 0 # 分類正確的,賦值為0 weightedError = D.T * errArr # 計算誤差 print("split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % ( i, threshVal, inequal, weightedError)) if weightedError < minError: # 找到誤差最小的分類方式 minError = weightedError bestClasEst = predictedVals.copy() bestStump['dim'] = i bestStump['thresh'] = threshVal bestStump['ineq'] = inequal return bestStump, minError, bestClasEst def adaBoostTrainDS(dataArr, classLabels, numIt=40): weakClassArr = [] m = np.shape(dataArr)[0] D = np.mat(np.ones((m, 1)) / m) # 初始化權重 aggClassEst = np.mat(np.zeros((m, 1))) for i in range(numIt): #迭代40次,也就是生成40個弱分類器,可以自己設置次數,但一般不超過50次 bestStump, error, classEst = buildStump(dataArr, classLabels, D) # 構建單層決策樹 print("D:",D.T) alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) # 計算弱學習算法權重alpha,使error不等於0,因為分母不能為0 bestStump['alpha'] = alpha # 存儲弱學習算法權重 bestStump是一個字典類型 weakClassArr.append(bestStump) # 存儲單層決策樹也就是弱分類器 print("classEst: ", classEst.T) #打印分類誤差 expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) # 計算e的指數項 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 根據樣本權重公式,更新樣本權重, 權重的歸一化,分類錯誤的樣本權重會加大,分類正確的樣本權重會減小 # 計算AdaBoost誤差,當誤差為0的時候,退出循環 aggClassEst += alpha * classEst #計算類別估計累計值,這里包括已經訓練好的每一個弱分類器 print("aggClassEst: ", aggClassEst.T) #第一輪aggClassEst: [[-0.69314718 0.69314718 -0.69314718 -0.69314718 0.69314718]] aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1))) # 計算誤差 np.sign()函數時將大於0的數置為1,小於零的數置為-1 #也就是說np.sign(aggClassEst) = [-1,1,-1,-1,1].T,與np.mat = [1,1,-1,-1,1].T相比較,不相等的置為1 #np.sign(aggClassEst) != np.mat(classLabels).T得到的結果為[1,0,0,0,0].T #aggErrors = [1,0,0,0,0].T errorRate = aggErrors.sum() / m # sum函數會將所有元素加起來得到1,所以errorRate = 0.2,集成分類器分類錯誤率,如果錯誤率為0,則整個集成算法停止,訓練完成 print("total error: ", errorRate) if errorRate == 0.0: break # 誤差為0,退出循環 return weakClassArr, aggClassEst if __name__ == '__main__': dataArr, classLabels = loadSimpData() weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, classLabels) print(weakClassArr) print(aggClassEst)
此分類器訓練三個弱分類器即可使得誤差為零,如圖所示: