1. 程式人生 > 程式設計 >python實現AdaBoost演算法的示例

python實現AdaBoost演算法的示例

程式碼

'''
資料集:Mnist
訓練集數量:60000(實際使用:10000)
測試集數量:10000(實際使用:1000)
層數:40
------------------------------
執行結果:
  正確率:97%
  執行時長:65m
'''

import time
import numpy as np


def loadData(fileName):
  '''
  載入檔案
  :param fileName:要載入的檔案路徑
  :return: 資料集和標籤集
  '''
  # 存放資料及標記
  dataArr = []
  labelArr = []
  # 讀取檔案
  fr = open(fileName)
  # 遍歷檔案中的每一行
  for line in fr.readlines():
    # 獲取當前行,並按“,”切割成欄位放入列表中
    # strip:去掉每行字串首尾指定的字元(預設空格或換行符)
    # split:按照指定的字元將字串切割成每個欄位,返回列表形式
    curLine = line.strip().split(',')
    # 將每行中除標記外的資料放入資料集中(curLine[0]為標記資訊)
    # 在放入的同時將原先字串形式的資料轉換為整型
    # 此外將資料進行了二值化處理,大於128的轉換成1,小於的轉換成0,方便後續計算
    dataArr.append([int(int(num) > 128) for num in curLine[1:]])
    # 將標記資訊放入標記集中
    # 放入的同時將標記轉換為整型

    # 轉換成二分類任務
    # 標籤0設定為1,反之為-1
    if int(curLine[0]) == 0:
      labelArr.append(1)
    else:
      labelArr.append(-1)
  # 返回資料集和標記
  return dataArr,labelArr


def calc_e_Gx(trainDataArr,trainLabelArr,n,div,rule,D):
  '''
  計算分類錯誤率
  :param trainDataArr:訓練資料集數字
  :param trainLabelArr: 訓練標籤集陣列
  :param n: 要操作的特徵
  :param div:劃分點
  :param rule:正反例標籤
  :param D:權值分佈D
  :return:預測結果, 分類誤差率
  '''
  # 初始化分類誤差率為0
  e = 0
  # 將訓練資料矩陣中特徵為n的那一列單獨剝出來做成陣列。因為其他元素我們並不需要,
  # 直接對龐大的訓練集進行操作的話會很慢
  x = trainDataArr[:,n]
  # 同樣將標籤也轉換成陣列格式,x和y的轉換隻是單純為了提高執行速度
  # 測試過相對直接操作而言效能提升很大
  y = trainLabelArr
  predict = []

  # 依據小於和大於的標籤依據實際情況會不同,在這裡直接進行設定
  if rule == 'LisOne':
    L = 1
    H = -1
  else:
    L = -1
    H = 1

  # 遍歷所有樣本的特徵m
  for i in range(trainDataArr.shape[0]):
    if x[i] < div:
      # 如果小於劃分點,則預測為L
      # 如果設定小於div為1,那麼L就是1,
      # 如果設定小於div為-1,L就是-1
      predict.append(L)
      # 如果預測錯誤,分類錯誤率要加上該分錯的樣本的權值(8.1式)
      if y[i] != L:
        e += D[i]
    elif x[i] >= div:
      # 與上面思想一樣
      predict.append(H)
      if y[i] != H:
        e += D[i]
  # 返回預測結果和分類錯誤率e
  # 預測結果其實是為了後面做準備的,在演算法8.1第四步式8.4中exp內部有個Gx,要用在那個地方
  # 以此來更新新的D
  return np.array(predict),e


def createSigleBoostingTree(trainDataArr,D):
  '''
  建立單層提升樹
  :param trainDataArr:訓練資料集陣列
  :param trainLabelArr: 訓練標籤集陣列
  :param D: 演算法8.1中的D
  :return: 建立的單層提升樹
  '''

  # 獲得樣本數目及特徵數量
  m,n = np.shape(trainDataArr)
  # 單層樹的字典,用於存放當前層提升樹的引數
  # 也可以認為該字典代表了一層提升樹
  sigleBoostTree = {}
  # 初始化分類誤差率,分類誤差率在演算法8.1步驟(2)(b)有提到
  # 誤差率最高也只能100%,因此初始化為1
  sigleBoostTree['e'] = 1

  # 對每一個特徵進行遍歷,尋找用於劃分的最合適的特徵
  for i in range(n):
    # 因為特徵已經經過二值化,只能為0和1,因此分切分時分為-0.5, 0.5, 1.5三擋進行切割
    for div in [-0.5,0.5,1.5]:
      # 在單個特徵內對正反例進行劃分時,有兩種情況:
      # 可能是小於某值的為1,大於某值得為-1,也可能小於某值得是-1,反之為1
      # 因此在尋找最佳提升樹的同時對於兩種情況也需要遍歷執行
      # LisOne:Low is one:小於某值得是1
      # HisOne:High is one:大於某值得是1
      for rule in ['LisOne','HisOne']:
        # 按照第i個特徵,以值div進行切割,進行當前設定得到的預測和分類錯誤率
        Gx,e = calc_e_Gx(trainDataArr,i,D)
        # 如果分類錯誤率e小於當前最小的e,那麼將它作為最小的分類錯誤率儲存
        if e < sigleBoostTree['e']:
          sigleBoostTree['e'] = e
          # 同時也需要儲存最優劃分點、劃分規則、預測結果、特徵索引
          # 以便進行D更新和後續預測使用
          sigleBoostTree['div'] = div
          sigleBoostTree['rule'] = rule
          sigleBoostTree['Gx'] = Gx
          sigleBoostTree['feature'] = i
  # 返回單層的提升樹
  return sigleBoostTree


def createBosstingTree(trainDataList,trainLabelList,treeNum=50):
  '''
  建立提升樹
  建立演算法依據“8.1.2 AdaBoost演算法” 演算法8.1
  :param trainDataList:訓練資料集
  :param trainLabelList: 訓練測試集
  :param treeNum: 樹的層數
  :return: 提升樹
  '''
  # 將資料和標籤轉化為陣列形式
  trainDataArr = np.array(trainDataList)
  trainLabelArr = np.array(trainLabelList)
  # 沒增加一層數後,當前最終預測結果列表
  finallpredict = [0] * len(trainLabelArr)
  # 獲得訓練集數量以及特徵個數
  m,n = np.shape(trainDataArr)

  # 依據演算法8.1步驟(1)初始化D為1/N
  D = [1 / m] * m
  # 初始化提升樹列表,每個位置為一層
  tree = []
  # 迴圈建立提升樹
  for i in range(treeNum):
    # 得到當前層的提升樹
    curTree = createSigleBoostingTree(trainDataArr,D)
    # 根據式8.2計算當前層的alpha
    alpha = 1 / 2 * np.log((1 - curTree['e']) / curTree['e'])
    # 獲得當前層的預測結果,用於下一步更新D
    Gx = curTree['Gx']
    # 依據式8.4更新D
    # 考慮到該式每次只更新D中的一個w,要迴圈進行更新知道所有w更新結束會很複雜(其實
    # 不是時間上的複雜,只是讓人感覺每次單獨更新一個很累),所以該式以向量相乘的形式,
    # 一個式子將所有w全部更新完。
    # 該式需要線性代數基礎,如果不太熟練建議補充相關知識,當然了,單獨更新w也一點問題
    # 沒有
    # np.multiply(trainLabelArr,Gx):exp中的y*Gm(x),結果是一個行向量,內部為yi*Gm(xi)
    # np.exp(-1 * alpha * np.multiply(trainLabelArr,Gx)):上面求出來的行向量內部全體
    # 成員再乘以-αm,然後取對數,和書上式子一樣,只不過書上式子內是一個數,這裡是一個向量
    # D是一個行向量,取代了式中的wmi,然後D求和為Zm
    # 書中的式子最後得出來一個數w,所有數w組合形成新的D
    # 這裡是直接得到一個向量,向量內元素是所有的w
    # 本質上結果是相同的
    D = np.multiply(D,np.exp(-1 * alpha * np.multiply(trainLabelArr,Gx))) / sum(D)
    # 在當前層引數中增加alpha引數,預測的時候需要用到
    curTree['alpha'] = alpha
    # 將當前層新增到提升樹索引中。
    tree.append(curTree)

    # -----以下程式碼用來輔助,可以去掉---------------
    # 根據8.6式將結果加上當前層乘以α,得到目前的最終輸出預測
    finallpredict += alpha * Gx
    # 計算當前最終預測輸出與實際標籤之間的誤差
    error = sum([1 for i in range(len(trainDataList)) if np.sign(finallpredict[i]) != trainLabelArr[i]])
    # 計算當前最終誤差率
    finallError = error / len(trainDataList)
    # 如果誤差為0,提前退出即可,因為沒有必要再計算算了
    if finallError == 0:
      return tree
    # 列印一些資訊
    print('iter:%d:%d,sigle error:%.4f,finall error:%.4f' % (i,treeNum,curTree['e'],finallError))
  # 返回整個提升樹
  return tree


def predict(x,feature):
  '''
  輸出單獨層預測結果
  :param x: 預測樣本
  :param div: 劃分點
  :param rule: 劃分規則
  :param feature: 進行操作的特徵
  :return:
  '''
  # 依據劃分規則定義小於及大於劃分點的標籤
  if rule == 'LisOne':
    L = 1
    H = -1
  else:
    L = -1
    H = 1

  # 判斷預測結果
  if x[feature] < div:
    return L
  else:
    return H


def test(testDataList,testLabelList,tree):
  '''
  測試
  :param testDataList:測試資料集
  :param testLabelList: 測試標籤集
  :param tree: 提升樹
  :return: 準確率
  '''
  # 錯誤率計數值
  errorCnt = 0
  # 遍歷每一個測試樣本
  for i in range(len(testDataList)):
    # 預測結果值,初始為0
    result = 0
    # 依據演算法8.1式8.6
    # 預測式子是一個求和式,對於每一層的結果都要進行一次累加
    # 遍歷每層的樹
    for curTree in tree:
      # 獲取該層引數
      div = curTree['div']
      rule = curTree['rule']
      feature = curTree['feature']
      alpha = curTree['alpha']
      # 將當前層結果加入預測中
      result += alpha * predict(testDataList[i],feature)
    # 預測結果取sign值,如果大於0 sign為1,反之為0
    if np.sign(result) != testLabelList[i]: 
      errorCnt += 1
  # 返回準確率
  return 1 - errorCnt / len(testDataList)


if __name__ == '__main__':
  # 開始時間
  start = time.time()

  # 獲取訓練集
  print('start read transSet')
  trainDataList,trainLabelList = loadData('../Mnist/mnist_train.csv')

  # 獲取測試集
  print('start read testSet')
  testDataList,testLabelList = loadData('../Mnist/mnist_test.csv')

  # 建立提升樹
  print('start init train')
  tree = createBosstingTree(trainDataList[:10000],trainLabelList[:10000],40)

  # 測試
  print('start to test')
  accuracy = test(testDataList[:1000],testLabelList[:1000],tree)
  print('the accuracy is:%d' % (accuracy * 100),'%')

  # 結束時間
  end = time.time()
  print('time span:',end - start)

程式執行結果

start read transSet
start read testSet
start init train
iter:0:40,sigle error:0.0804,finall error:0.0804
iter:1:40,sigle error:0.1448,finall error:0.0804
iter:2:40,sigle error:0.1362,finall error:0.0585
iter:3:40,sigle error:0.1864,finall error:0.0667
iter:4:40,sigle error:0.2249,finall error:0.0474
iter:5:40,sigle error:0.2634,finall error:0.0437

iter:6:40,sigle error:0.2626,finall error:0.0377
iter:7:40,sigle error:0.2935,finall error:0.0361
iter:8:40,sigle error:0.3230,finall error:0.0333
iter:9:40,sigle error:0.3034,finall error:0.0361
iter:10:40,sigle error:0.3375,finall error:0.0325
iter:11:40,sigle error:0.3364,finall error:0.0340
iter:12:40,sigle error:0.3473,finall error:0.0309
iter:13:40,sigle error:0.3006,finall error:0.0294
iter:14:40,sigle error:0.3267,finall error:0.0275
iter:15:40,sigle error:0.3584,finall error:0.0288
iter:16:40,sigle error:0.3492,finall error:0.0257
iter:17:40,sigle error:0.3506,finall error:0.0256
iter:18:40,sigle error:0.3665,finall error:0.0240
iter:19:40,sigle error:0.3769,finall error:0.0251
iter:20:40,sigle error:0.3828,finall error:0.0213
iter:21:40,sigle error:0.3733,finall error:0.0229
iter:22:40,sigle error:0.3785,finall error:0.0218
iter:23:40,sigle error:0.3867,finall error:0.0219
iter:24:40,sigle error:0.3850,finall error:0.0208
iter:25:40,sigle error:0.3823,finall error:0.0201
iter:26:40,sigle error:0.3825,finall error:0.0204
iter:27:40,sigle error:0.3874,finall error:0.0188
iter:28:40,sigle error:0.3952,finall error:0.0186
iter:29:40,sigle error:0.4018,finall error:0.0193
iter:30:40,sigle error:0.3889,finall error:0.0177
iter:31:40,sigle error:0.3939,finall error:0.0183
iter:32:40,sigle error:0.3838,finall error:0.0182
iter:33:40,sigle error:0.4021,finall error:0.0171
iter:34:40,sigle error:0.4119,finall error:0.0164
iter:35:40,sigle error:0.4093,finall error:0.0164
iter:36:40,sigle error:0.4135,finall error:0.0167
iter:37:40,sigle error:0.4099,finall error:0.0171
iter:38:40,sigle error:0.3871,finall error:0.0163
iter:39:40,sigle error:0.4085,finall error:0.0154
start to test
the accuracy is:97 %
time span: 3777.730945825577

以上就是python實現AdaBoost演算法的示例的詳細內容,更多關於python實現AdaBoost演算法的資料請關注我們其它相關文章!