1. 程式人生 > >統計學習方法c++實現之七 提升方法--AdaBoost

統計學習方法c++實現之七 提升方法--AdaBoost

std learn 次數 比較 cal 算法 eat pre predict

提升方法--AdaBoost

前言

AdaBoost是最經典的提升方法,所謂的提升方法就是一系列弱分類器(分類效果只比隨機預測好一點)經過組合提升最後的預測效果。而AdaBoost提升方法是在每次訓練弱分類器的時候,提升上一個弱分類器誤分類的數據的比重來讓本次訓練的分類器能夠彌補上次分類器的不足。AdaBoost的概念和算法還是很好理解的,而且通過書上的例題可以很明顯的感覺用一個很簡單(計算量很小)的分類器,經過提升後的最終分類器的效果很好,本篇還是著重實現部分,並且將我在實現時候遇到的問題和思考記錄下來。代碼地址。

AdaBoost算法

輸入: 訓練數據集$T = { (x_1,y_1), (x_2,y_2),...,(x_N,y_N) } $; 某個弱分類算法(比如訓練不足夠的感知機,我的實現中就選的叠代次數比較少的感知機)。

輸出: 最終分類器G(x)。

  1. 初始化訓練數據的權值分布(這個權值每個訓練數據都有, 但是並不是意味著訓練的時候將數據點乘上這個權值,它的作用主要是在計算誤差率的時候用到)。

    \(D_1 = (w_{11}, ...,w_{1i},...,w_{1N}), w_{1i}=\frac{1}{N}, i=1,2,...,N\)

  2. 對每一次提升(也就是弱分類器個數)m:

    • 根據訓練數據和權重訓練分類器\(G_m(x)\)。這裏的權值分布是在訓練分類器的時候用到的,具體來說就是在優化目標函數(比如最大似然函數或者誤分類率)的時候,考慮每個數據點的權值

    • 計算\(G_m(x)\)在訓練數據集上的分類誤差率:

      \(e_m = \sum^{N}_{i=1}w_{mi}I(G_m(x_i)\neq y_i)\)

    • 計算\(G_m(x)\)的系數

      \(a_m = 0.5log(\frac{1-e_m}{e_m})\)

    • 更新訓練數據的權值分布

      \(D_{m+1}=(w_{m+1,1}, ...,w_{m+1,i},...,w_{m+1,N})\)

      \(w_{m+1,i} = \frac{w_{mi}}{Z_m}exp(-a_my_iG_m(x_i))\)

      \(Z_m = \sum^{N}_{i=1}w_{mi}exp(-a_my_iG_m(x_i))\)

  3. 構建基本分類器的線性組合

    \(G(x) = sign(\sum^{M}_{m=1}a_mG_m(x))\)

說明:對於訓練數據的權值問題,是我在實際實現的時候發現的,這個要特別註意。還有就是基本分類器的選擇問題,一定要選取比隨機預測效果好的分類器,比如二分類問題,一定要選擇分類誤差率小於0.5的分類器,否則後續無法提升。

C++實現

代碼結構

技術分享圖片

重要代碼

這裏放上求數據權值的代碼:

int AdaBoost::computeWeights(Perceptron* classifier) {
    vector<double> trainGT;
    //由於我的感知機算法(見前面的系列代碼)采用的loss函數裏面包含訓練數據的真值
    //於是這裏我就通過改變真值的比重來反應訓練數據的比重
    for(int i =0; i<trainDataGT.size();++i)
        trainGT.push_back(trainDataGT[i]*featrWeight[i]);
    classifier->setTrainD(trainDataF, trainGT);//將本算法的訓練數據設為感知機的訓練數據
    classifier->setDim(indim);
    classifier->train(100, 0.9);
    //這裏第一個參數是感知機的訓練次數,第二個參數是學習率。
    //100次叠代時學習的是一個強分類器,直接將全部數據分類正確,經過實驗,將訓練步數設置為90就可以得到弱分類器
    //會有分類錯誤的情況,但是分類誤差率小於0.5。
    double erroeRate = 0;
    for(int i = 0; i<trainDataF.size();++i) {
        if (classifier->predict(trainDataF[i])!=int(trainDataGT[i]))
            erroeRate += featrWeight[i];
    }
    if(erroeRate==0){
        if(clsfWeight.size()==0)
            clsfWeight.push_back(1);
        return 0;
    }

    double clsW;
    clsW = 0.5*std::log((1-erroeRate)/erroeRate);
    clsfWeight.push_back(clsW);


    double zm=0;
    for(int i = 0; i<trainDataF.size();++i) {
        zm+=featrWeight[i]*std::exp(-clsW*trainDataGT[i]*classifier->predict(trainDataF[i]));
    }

    for(int i = 0; i<featrWeight.size();++i ){
        featrWeight[i] = featrWeight[i]/zm*std::exp(-clsW*trainDataGT[i]*classifier->predict(trainDataF[i]));
    }
    return 1;
}

再次強調,更改感知機的訓練次數和學習率會有不同的結果,但是我的結果得到的最終的分類器卻不如一個訓練次數多的強分類器好,可能是因為我的訓練數據太小。

這裏主要是想練習用c++的指針使用其它類,也可以用其它的分類器,單是之前寫那些算法並沒有提供被調用的接口(當時並沒有想要調用),改了改感知機的代碼才勉強能用,以後寫代碼還是需要多思考。

統計學習方法c++實現之七 提升方法--AdaBoost