Adaboost算法
集成學習概述
集成學習算法定義
- 集成學習(Ensemble learning)就是講若干個弱分類器通過一定策略組合後產生一個強分類器。弱分類器(weak Classifier)指的就是那些分類準確率只比隨機猜測好一點的分類器。而強分類器(strong Classifier)的分類準確率會高很多,這裏的弱和強是相對的,弱分類器也叫做基分類器
-
分類:
bagging
boosting
bagging(裝袋)
-
bagging方法又叫做自舉匯聚法(boostrap aggregating),是一種根據均勻概率分佈從數據集中重複抽樣(有放回)的技術,每個數據集和原始數據集大小相等,由於新數據集的每一個樣本都是從原數據集合中有放回隨機抽樣出來的,所以每個數據集中可能有重複的值,而原始數據集中的某些樣本可能根本就沒有出現在新數據集中
-
有放回的隨機抽樣:自主採樣法(Bootstap sampling),也就是說對於m個樣本的原始數據集,每次隨機選取一個樣本放回採樣集合中,然後這個樣本重新放回,再進行下一次隨機抽樣,直到採樣集合中樣本數量達到m,這樣一個採樣集合就構建好了,重複過程,生成n個採樣集合
-
將n個採樣集合,分別進行訓練,得到n個弱分類器,根據每個結果進行組合,得到強分類器
-
降低弱分類器的方差
boosting(提升)
-
迭代過程,用來自適應的改變訓練樣本的分佈,使得弱分類器聚焦到那些很難分類的樣本上,它的做法是給每一個訓練樣本賦予一個權重,在每一輪訓練中自動調整權重
-
組合策略
-
平均法:
-
投票法:
-
學習法:
-
Adaboost算法(自適應提升算法)
-
收集數據:可以使用任意方法
準備數據:依賴於所使用的弱分類器類型,本章使用的是單層決策樹,這種分類器可以處理任何數據類型。
當然也可以使用任意分類器作爲弱分類器,第2章到第6章中的任一分類器都可以充當弱分類器。
作爲弱分類器,簡單分類器的效果更好。
分析數據:可以使用任意方法。
訓練算法:AdaBoost 的大部分時間都用在訓練上,分類器將多次在同一數據集上訓練弱分類器。
測試算法:計算分類的錯誤率。
使用算法:通SVM一樣,AdaBoost 預測兩個類別中的一個。如果想把它應用到多個類別的場景,那麼就要像多類 SVM 中的做法一樣對 AdaBoost -
優點:泛化(由具體的、個別的擴大爲一般的)錯誤率低,易編碼,可以應用在大部分分類器上,無參數調節。
缺點:對離羣點敏感。
適用數據類型:數值型和標稱型數據
案例一:自適應算法實現
-
導入數據:
def load_sim_data(): """ 測試數據, :return: data_arr feature對應的數據集 label_arr feature對應的分類標籤 """ data_mat = np.matrix([[1.0, 2.1], [2.0, 1.1], [1.3, 1.0], [1.0, 1.0], [2.0, 1.0]]) class_labels = [1.0, 1.0, -1.0, -1.0, 1.0] return data_mat, class_labels
-
算法測試:將符合條件的數據轉化,測試是否有某個值小於或者大於我們正在測試的閾值,如果大於某個閾值就是錯誤的
def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (將數據集,按照feature列的value進行 二分法切分比較來賦值分類) :param data_mat: Matrix數據集 :param dimen: 特徵的哪一個列 :param thresh_val: 特徵列要比較的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示數據集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左邊的值,gt表示修改右邊的值 # (這裏其實我建議理解爲轉換左右邊,就是一棵樹的左右孩子,可能有點問題。。。待考證) if thresh_ineq == 'lt':# 假設左邊比較一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假設右邊比較一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array
-
單層決策樹的實現:最優化單層決策樹
# 這個算法是爲了尋找最好的單層決策樹 def build_stump(data_arr, class_labels, D): """ 得到決策樹的模型 (這個比較重要,需要看懂) :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param D: 最初的特徵權重值 :return: best_Stump 最優的分類器模型 min_error 錯誤率 best_class_est 訓練後的結果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#訓練後的結果集 # 無窮大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 這裏是矩陣乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示樹的分界值 inequal 表示計算樹左右顛倒的錯誤率的情況 weighted_error 表示整體結果的錯誤率 best_class_est 預測的最優結果 (與class_labels對應) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的結果集儲存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 閾值 best_stump['ineq'] = inequal#比較範圍,是用大於還是用小於 # best_stump 表示分類器的結果,在第幾個列上,用大於/小於比較,閾值是多少 (單個弱分類器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#賦值相同的權重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5)))
-
構建自適應算法
def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost訓練過程放大 :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param num_it: 迭代次數 :return: weak_class_arr 弱分類器的集合 agg_class_est 預測的分類結果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,設置每個特徵的權重值,平均分爲m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩陣 for i in range(num_it): # 得到決策樹的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 尋找最佳的單層決策樹 print('D: {}'.format(D.T)) # alpha 目的主要是計算每一個分類器實例的權重(加和就是分類結果) # 計算每個分類器的 alpha 權重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分類正確:乘積爲1,不會影響結果,-1主要是下面求e的-alpha次方 # 分類錯誤:乘積爲 -1,結果會受影響,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判斷正確的,就乘以-1,否則就乘以1, 爲什麼? 書上的公式有問題 # print('(-1取反)預測值 expon=', expon.T) # 計算e的expon次方,然後計算得到一個綜合的概率的值 # 結果發現: 判斷錯誤的樣本,D對於的樣本權重值會變大。 # multiply是對應項相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 預測的分類結果值,在上一輪結果的基礎上,進行加和操作 # print('疊加前的分類結果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est print('疊加後的分類結果agg_class_est: {}'.format(agg_class_est.T)) # sign 判斷正爲1, 0爲0, 負爲-1,通過最終加和的權重值,判斷符號。 # 結果爲:錯誤的樣本標籤集合,因爲是 !=,那麼結果就是0 正, 1 負,這裏1就是表示是錯誤分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break print(D) return weak_class_arr
-
測試代碼
def ada_classify(data_to_class, classifier_arr): """ 通過剛剛上面那個函數得到的弱分類器的集合進行預測 :param data_to_class: 數據集 :param classifier_arr: 分類器列表 :return: 正負一,也就是表示分類的結果 """ data_mat = np.mat(data_to_class)# 測試9 m = np.shape(data_mat)[0] agg_class_est = np.mat(np.zeros((m, 1))) for i in range(len(classifier_arr)): class_est = stump_classify( data_mat, classifier_arr[i]['dim'], classifier_arr[i]['thresh'], classifier_arr[i]['ineq'] ) agg_class_est += classifier_arr[i]['alpha'] * class_est print(agg_class_est) return np.sign(agg_class_est)
-
完整代碼:
# 自適應算法實現實例一 import numpy as np def load_sim_data(): """ 測試數據, :return: data_arr feature對應的數據集 label_arr feature對應的分類標籤 """ data_mat = np.matrix([[1.0, 2.1], [2.0, 1.1], [1.3, 1.0], [1.0, 1.0], [2.0, 1.0]]) class_labels = [1.0, 1.0, -1.0, -1.0, 1.0] return data_mat, class_labels datMat,classLables = load_sim_data() def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (將數據集,按照feature列的value進行 二分法切分比較來賦值分類) :param data_mat: Matrix數據集 :param dimen: 特徵的哪一個列 :param thresh_val: 特徵列要比較的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示數據集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左邊的值,gt表示修改右邊的值 # (這裏其實我建議理解爲轉換左右邊,就是一棵樹的左右孩子,可能有點問題。。。待考證) if thresh_ineq == 'lt':# 假設左邊比較一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假設右邊比較一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array # 這個算法是爲了尋找最好的單層決策樹 def build_stump(data_arr, class_labels, D): """ 得到決策樹的模型 (這個比較重要,需要看懂) :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param D: 最初的特徵權重值 :return: best_Stump 最優的分類器模型 min_error 錯誤率 best_class_est 訓練後的結果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#訓練後的結果集 # 無窮大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 這裏是矩陣乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示樹的分界值 inequal 表示計算樹左右顛倒的錯誤率的情況 weighted_error 表示整體結果的錯誤率 best_class_est 預測的最優結果 (與class_labels對應) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的結果集儲存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 閾值 best_stump['ineq'] = inequal#比較範圍,是用大於還是用小於 # best_stump 表示分類器的結果,在第幾個列上,用大於/小於比較,閾值是多少 (單個弱分類器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#賦值相同的權重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5))) def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost訓練過程放大 :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param num_it: 迭代次數 :return: weak_class_arr 弱分類器的集合 agg_class_est 預測的分類結果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,設置每個特徵的權重值,平均分爲m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩陣 for i in range(num_it): # 得到決策樹的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 尋找最佳的單層決策樹 print('D: {}'.format(D.T)) # alpha 目的主要是計算每一個分類器實例的權重(加和就是分類結果) # 計算每個分類器的 alpha 權重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分類正確:乘積爲1,不會影響結果,-1主要是下面求e的-alpha次方 # 分類錯誤:乘積爲 -1,結果會受影響,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判斷正確的,就乘以-1,否則就乘以1, 爲什麼? 書上的公式有問題 # print('(-1取反)預測值 expon=', expon.T) # 計算e的expon次方,然後計算得到一個綜合的概率的值 # 結果發現: 判斷錯誤的樣本,D對於的樣本權重值會變大。 # multiply是對應項相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 預測的分類結果值,在上一輪結果的基礎上,進行加和操作 # print('疊加前的分類結果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est print('疊加後的分類結果agg_class_est: {}'.format(agg_class_est.T)) # sign 判斷正爲1, 0爲0, 負爲-1,通過最終加和的權重值,判斷符號。 # 結果爲:錯誤的樣本標籤集合,因爲是 !=,那麼結果就是0 正, 1 負,這裏1就是表示是錯誤分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break print(D) return weak_class_arr # print(ada_boost_train_ds(datMat,classLables,9)) classLables_arr = ada_boost_train_ds(datMat,classLables,9) def ada_classify(data_to_class, classifier_arr): """ 通過剛剛上面那個函數得到的弱分類器的集合進行預測 :param data_to_class: 數據集 :param classifier_arr: 分類器列表 :return: 正負一,也就是表示分類的結果 """ data_mat = np.mat(data_to_class)# 測試9 m = np.shape(data_mat)[0] agg_class_est = np.mat(np.zeros((m, 1))) for i in range(len(classifier_arr)): class_est = stump_classify( data_mat, classifier_arr[i]['dim'], classifier_arr[i]['thresh'], classifier_arr[i]['ineq'] ) agg_class_est += classifier_arr[i]['alpha'] * class_est print(agg_class_est) return np.sign(agg_class_est) # print(ada_classify([0,0],classLables_arr))
疝病馬數據使用自適應算法實現
-
和上邊代碼差不多,就是加了一個rank測試
import numpy as np def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (將數據集,按照feature列的value進行 二分法切分比較來賦值分類) :param data_mat: Matrix數據集 :param dimen: 特徵的哪一個列 :param thresh_val: 特徵列要比較的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示數據集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左邊的值,gt表示修改右邊的值 # (這裏其實我建議理解爲轉換左右邊,就是一棵樹的左右孩子,可能有點問題。。。待考證) if thresh_ineq == 'lt':# 假設左邊比較一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假設右邊比較一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array # 這個算法是爲了尋找最好的單層決策樹 def build_stump(data_arr, class_labels, D): """ 得到決策樹的模型 (這個比較重要,需要看懂) :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param D: 最初的特徵權重值 :return: best_Stump 最優的分類器模型 min_error 錯誤率 best_class_est 訓練後的結果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#訓練後的結果集 # 無窮大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 這裏是矩陣乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示樹的分界值 inequal 表示計算樹左右顛倒的錯誤率的情況 weighted_error 表示整體結果的錯誤率 best_class_est 預測的最優結果 (與class_labels對應) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的結果集儲存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 閾值 best_stump['ineq'] = inequal#比較範圍,是用大於還是用小於 # best_stump 表示分類器的結果,在第幾個列上,用大於/小於比較,閾值是多少 (單個弱分類器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#賦值相同的權重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5))) def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost訓練過程放大 :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param num_it: 迭代次數 :return: weak_class_arr 弱分類器的集合 agg_class_est 預測的分類結果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,設置每個特徵的權重值,平均分爲m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩陣 for i in range(num_it): # 得到決策樹的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 尋找最佳的單層決策樹 # print('D: {}'.format(D.T)) # alpha 目的主要是計算每一個分類器實例的權重(加和就是分類結果) # 計算每個分類器的 alpha 權重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分類正確:乘積爲1,不會影響結果,-1主要是下面求e的-alpha次方 # 分類錯誤:乘積爲 -1,結果會受影響,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判斷正確的,就乘以-1,否則就乘以1, 爲什麼? 書上的公式有問題 # print('(-1取反)預測值 expon=', expon.T) # 計算e的expon次方,然後計算得到一個綜合的概率的值 # 結果發現: 判斷錯誤的樣本,D對於的樣本權重值會變大。 # multiply是對應項相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 預測的分類結果值,在上一輪結果的基礎上,進行加和操作 # print('疊加前的分類結果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est # print('疊加後的分類結果agg_class_est: {}'.format(agg_class_est.T)) # sign 判斷正爲1, 0爲0, 負爲-1,通過最終加和的權重值,判斷符號。 # 結果爲:錯誤的樣本標籤集合,因爲是 !=,那麼結果就是0 正, 1 負,這裏1就是表示是錯誤分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break # print(D) return weak_class_arr def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t'))# 22個列 21個特徵 1個標籤 dataMat = [];labelMat = [] fr = open(fileName) for line in fr.readlines(): lineArr = [] curLine = line.strip().split('\t') # 將21個特徵保存起來 for i in range(numFeat-1): lineArr.append((float(curLine[i]))) # print(len(lineArr)) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat,labelMat datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt') # print(ada_boost_train_ds(datArr,labelArr,10)) classifiterArray = ada_boost_train_ds(datArr,labelArr,10000) def ada_classify(data_to_class, classifier_arr): """ 通過剛剛上面那個函數得到的弱分類器的集合進行預測 :param data_to_class: 數據集 :param classifier_arr: 分類器列表 :return: 正負一,也就是表示分類的結果 """ data_mat = np.mat(data_to_class)# 測試9 m = np.shape(data_mat)[0] agg_class_est = np.mat(np.zeros((m, 1))) for i in range(len(classifier_arr)): class_est = stump_classify( data_mat, classifier_arr[i]['dim'], classifier_arr[i]['thresh'], classifier_arr[i]['ineq'] ) agg_class_est += classifier_arr[i]['alpha'] * class_est # print(agg_class_est) return np.sign(agg_class_est) def test(): testArr,testLabelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTest2.txt') pridict = ada_classify(testArr,classifiterArray) errArr = np.mat(np.ones((67,1))) error = errArr[pridict!=np.mat(testLabelArr).T].sum() print('error = {}'.format(error/len(testLabelArr))) test()
-
和書上結果差不多:
處理非均衡問題
-
代碼
import numpy as np def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (將數據集,按照feature列的value進行 二分法切分比較來賦值分類) :param data_mat: Matrix數據集 :param dimen: 特徵的哪一個列 :param thresh_val: 特徵列要比較的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示數據集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左邊的值,gt表示修改右邊的值 # (這裏其實我建議理解爲轉換左右邊,就是一棵樹的左右孩子,可能有點問題。。。待考證) if thresh_ineq == 'lt':# 假設左邊比較一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假設右邊比較一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array # 這個算法是爲了尋找最好的單層決策樹 def build_stump(data_arr, class_labels, D): """ 得到決策樹的模型 (這個比較重要,需要看懂) :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param D: 最初的特徵權重值 :return: best_Stump 最優的分類器模型 min_error 錯誤率 best_class_est 訓練後的結果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#訓練後的結果集 # 無窮大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 這裏是矩陣乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示樹的分界值 inequal 表示計算樹左右顛倒的錯誤率的情況 weighted_error 表示整體結果的錯誤率 best_class_est 預測的最優結果 (與class_labels對應) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的結果集儲存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 閾值 best_stump['ineq'] = inequal#比較範圍,是用大於還是用小於 # best_stump 表示分類器的結果,在第幾個列上,用大於/小於比較,閾值是多少 (單個弱分類器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#賦值相同的權重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5))) def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost訓練過程放大 :param data_arr: 特徵標籤集合 :param class_labels: 分類標籤集合 :param num_it: 迭代次數 :return: weak_class_arr 弱分類器的集合 agg_class_est 預測的分類結果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,設置每個特徵的權重值,平均分爲m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩陣 for i in range(num_it): # 得到決策樹的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 尋找最佳的單層決策樹 # print('D: {}'.format(D.T)) # alpha 目的主要是計算每一個分類器實例的權重(加和就是分類結果) # 計算每個分類器的 alpha 權重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分類正確:乘積爲1,不會影響結果,-1主要是下面求e的-alpha次方 # 分類錯誤:乘積爲 -1,結果會受影響,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判斷正確的,就乘以-1,否則就乘以1, 爲什麼? 書上的公式有問題 # print('(-1取反)預測值 expon=', expon.T) # 計算e的expon次方,然後計算得到一個綜合的概率的值 # 結果發現: 判斷錯誤的樣本,D對於的樣本權重值會變大。 # multiply是對應項相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 預測的分類結果值,在上一輪結果的基礎上,進行加和操作 # print('疊加前的分類結果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est # print('疊加後的分類結果agg_class_est: {}'.format(agg_class_est.T)) # sign 判斷正爲1, 0爲0, 負爲-1,通過最終加和的權重值,判斷符號。 # 結果爲:錯誤的樣本標籤集合,因爲是 !=,那麼結果就是0 正, 1 負,這裏1就是表示是錯誤分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break # print(D) return weak_class_arr,agg_class_est def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t'))# 22個列 21個特徵 1個標籤 dataMat = [];labelMat = [] fr = open(fileName) for line in fr.readlines(): lineArr = [] curLine = line.strip().split('\t') # 將21個特徵保存起來 for i in range(numFeat-1): lineArr.append((float(curLine[i]))) # print(len(lineArr)) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat,labelMat datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt') # print(ada_boost_train_ds(datArr,labelArr,10)) classifiterArray,aggClassEst = ada_boost_train_ds(datArr,labelArr,100) def plot_roc(pred_strengths, class_labels): """ (打印ROC曲線,並計算AUC的面積大小) :param pred_strengths: 最終預測結果的權重值 :param class_labels: 原始數據的分類結果集 :return: """ import matplotlib.pyplot as plt # variable to calculate AUC y_sum = 0.0 # 對正樣本的進行求和 num_pos_class = np.sum(np.array(class_labels) == 1.0) # 正樣本的概率 y_step = 1 / float(num_pos_class) # 負樣本的概率 x_step = 1 / float(len(class_labels) - num_pos_class) # np.argsort函數返回的是數組值從小到大的索引值 # get sorted index, it's reverse sorted_indicies = pred_strengths.argsort() # 測試結果是否是從小到大排列 # 可以選擇打印看一下 # 開始創建模版對象 fig = plt.figure() fig.clf() ax = plt.subplot(111) # cursor光標值 cur = (1.0, 1.0) # loop through all the values, drawing a line segment at each point for index in sorted_indicies.tolist()[0]: if class_labels[index] == 1.0: del_x = 0 del_y = y_step else: del_x = x_step del_y = 0 y_sum += cur[1] # draw line from cur to (cur[0]-delX, cur[1]-delY) # 畫點連線 (x1, x2, y1, y2) # print cur[0], cur[0]-delX, cur[1], cur[1]-delY ax.plot([cur[0], cur[0] - del_x], [cur[1], cur[1] - del_y], c='b') cur = (cur[0] - del_x, cur[1] - del_y) # 畫對角的虛線線 ax.plot([0, 1], [0, 1], 'b--') plt.xlabel('False positive rate') plt.ylabel('True positive rate') plt.title('ROC curve for AdaBoost horse colic detection system') # 設置畫圖的範圍區間 (x1, x2, y1, y2) ax.axis([0, 1, 0, 1]) plt.show() ''' 參考說明:http://blog.csdn.net/wenyusuran/article/details/39056013 爲了計算 AUC ,我們需要對多個小矩形的面積進行累加。 這些小矩形的寬度是x_step,因此可以先對所有矩形的高度進行累加,最後再乘以x_step得到其總面積。 所有高度的和(y_sum)隨着x軸的每次移動而漸次增加。 ''' print("the Area Under the Curve is: ", y_sum * x_step) plot_roc(aggClassEst.T,labelArr)
-
運行結果:
Skleanring實現adaboost算法
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
import numpy as np
def loadDataSet(fileName):
numFeat = len(open(fileName).readline().split('\t'))# 22個列 21個特徵 1個標籤
dataMat = [];labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr = []
curLine = line.strip().split('\t')
# 將21個特徵保存起來
for i in range(numFeat-1):
lineArr.append((float(curLine[i])))
# print(len(lineArr))
dataMat.append(lineArr)
labelMat.append(float(curLine[-1]))
return dataMat,labelMat
datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt')
clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2,min_samples_split=5,min_samples_leaf=5),n_estimators=40,random_state=10000)
clf.fit(np.mat(datArr),labelArr)
datArr1,labelArr1 = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTest2.txt')
print(clf.score(np.mat(datArr1),labelArr1))