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

提高方法--AdaBoost

前言

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

AdaBoost算法

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

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

  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)\)在訓練數據集上的分類偏差率:spa

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

    • 計算\(G_m(x)\)的係數code

      \(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++的指針使用其它類,也能夠用其它的分類器,單是以前寫那些算法並無提供被調用的接口(當時並無想要調用),改了改感知機的代碼才勉強能用,之後寫代碼仍是須要多思考。

相關文章
相關標籤/搜索