Adaptive basis function models -----Adaboost

Adaptive basis function models

------七月算法*機器學習筆記

核方法:一個函數可以表示爲多個基函數的加權求和

           f(x)=w^T\phi (x)  --當\phi (x)=x時,爲線性基函數,這會給模型帶來巨大的侷限性,因此將輸⼊變量的固定的⾮線性函數進⾏線性組合來擬合函數

                \phi (x)=[k(x,\mu_1 ),...,k(x,\mu_N)],\mu_i爲所有數據或部分數據,k(x,\mu_i )可認爲變量x到每一個數據的距離

核函數k(x,x'),可以看作度量兩者之間距離的一個函數

                          k(x,x')=exp(-\frac{1}{2}(x-x')^T\Sigma ^{-1}(x-x'))(1),高斯核

                          k(x,x')=\theta _0exp(-\frac{1}{2}\sum_{j=1}^{D}\theta_j(x_j-x_j')^2),當(1)式中\Sigma爲對角陣時,可簡化爲下面的核函數,其中\theta_j表示對角線元素

                         k(x,x')=exp(-\frac{||x-x'||^2}{2\sigma ^2}),當(1)式中\Sigma爲球形(對角陣,且對角線各元素相等)時,可簡化爲該式,即svm中的rbf kernel

                         k(x,x')=\frac{x_i^Tx_{i'}}{||x_i||_2||x_{i'}||_2},餘弦相似度,前期的nlp中xi表示一個向量,xij表示wordj在document i中出現的次數。但這樣做效果不好:1.stop words是沒有區分度的,但將其考慮進來就會影響距離的度量;2.對於有區分度的詞,如果在一個document中出現了很多次,相似度就被人爲地放大了。所以後來不直接用xij這種方式,而是TF-IDF(term frequency(解決2,針對xij)-inverse document frequency(解決1,針對j))

好的kernel的定義是很難的,因此學者們先後提出:

      1.Maximizing likelihood(最大似然估計),指定核函數,但會設置參數,然後使用最大似然估計去估計參數

      2.MKL(multiple kernel learning)k(x,x')=\sum_jw_jk_j(x,x'),核函數由多個核加權組成的,主要還是學參數

      前面兩種方式kernel都是提前定義的,或者說固定不變的,只是學的參數

      3.Adaptive basis function model(ABM):f(x)=w_o+\sum_{m=1}^Mw_m\phi _m(x),\phi _m(x)即basis function,它不是人爲定義的,而是learned from data!前面提到的CART(classification and regression tree)就是ABM的一種

Boosting

a greedy algorithm for fitting ABM

成熟應用--人臉檢測

weak learner(弱學習機):對一定分佈的訓練樣本給出的假設(僅僅強於隨機猜測,用簡單的比例表示的話正確率50%-60%)

strong learner(強學習機):根據得到的弱學習機和相應的權重給出假設(最大程度符合實際情況,80%以上)

弱學習機-------------boosting------------>強學習機,目標:最小化損失,min_f\sum_{i=1}^NL(y_i,f(x_i))

 

boosting算法:

     • 首先給出任意一個弱學習算法和訓練集 (x1 , y1 ) , ( x2 , y2 ) , ⋯, ( xn,yn ) , xi ∈X, X 表示某個實例空間,在分類問題中是一個帶類別標誌的集合, yi∈Y = { + 1, - 1}。
    •初始化時, Adaboost爲訓練集指定分佈爲1 /n, 即每個訓練例的權重都相同 爲1 /n。
    • 接着,調用弱學習算法進行T 次迭代,每次迭代後 按照訓練結果更新訓練集上的分佈,對於訓練失敗的訓練例賦予較大的權重,使得下一次迭代更加關注這些 訓練例,從而得到一個預測函數序列h1 , h2 , ⋯, ht ,每個預測函數ht也賦予一個權重, 預測效果好的, 相應的權重越大                     

    • T 次迭代之後,在分類問題中最終的預測函數 H 採用帶權重的投票法產生。
    • 單個弱學習器的學習準確率不高,經過運用 Boosting 算法之後,最終結果準確率將得到提高。

下面是多種boosting算法,\pi_i爲誤差分類率,\tilde{y}\in \left \{ -1,1 \right \},y_i\in \left \{ 0,1 \right \}

Adaboost(指數loss)

               L(\tilde{y},f)=exp(-\tilde{y}f)

f求導並等0得:

                    \begin{align*} \frac{\partial E\left [ e^{-\tilde{y}f(x)}|x \right ]}{\partial x}&=\frac{\partial \left [ p(\tilde{y}=1|x)e^{-f(x)}+p(\tilde{y}=-1|x)e^{f(x)} \right ]}{\partial x}\\ &=-p(\tilde{y}=1|x)e^{-f(x)}+p(\tilde{y}=-1|x)e^{f(x)} \\ &=0\Rightarrow \frac{p(\tilde{y}=1|x)}{p(\tilde{y}=-1|x)}=e^{2f(x)} \\ &\Rightarrow f(x)=\frac{1}{2}log\frac{\tilde{\pi}}{1-\tilde{\pi}}\end{align*}

初始化:

            

迭代求解:(\beta_m,\gamma_m)分別是弱學習機和弱學習機內部元素x的參數

                   

實踐中通常添加一個參數\upsilon=0.1,避免得到的弱學習機過於’強勢‘,導致之前的弱學習機影響很小

                

Adaboost損失函數

          L_m(\phi )=\sum_{i=1}^Nexp\left [ -\tilde{y_i}(f_{m-1}(x_i)+\beta\phi(x_i)) \right ]=\sum_{i=1}^Nw_{i,m}exp(-\beta\tilde{y_i}\phi(x_i)),其中定義w_{i,m}=exp(-\tilde{y_i}f_{m-1}(x_i))

對於二元分類,\tilde{y_i}\in \left \{-1,1}{ \right \},即\tilde{y_i}\phi(x_i)相等即同號爲1,不等異號爲-1,

化簡,

                 \begin{align*} L_m &=e^{-\beta}\sum_{\tilde{y_i}=\phi(x_i)}w_{i,m}+e^{\beta}\sum_{\tilde{y_i}\neq \phi(x_i)}w_{i,m} \\ &= (e^\beta-e^{-\beta})\sum_{i=1}^Nw_{i,m}\mathbb{I}(\tilde{y}\neq\phi(x_i))+e^{-\beta}\sum_{i=1}^Nw_{i,m} \end{align*},即設定了一個標準,錯誤分類個數

因此,觀察上式L_m,對\phi(x_i)求偏導時求最小值時,僅與w_{i,m}有關,故得

                        參數\phi_m=argmin_\phi w_{i,m}\mathbb{I}(\tilde{y}\neq\phi(x_i))

                       \beta_m=\frac{1}{2}log\frac{1-err_m}{err_m},err_m=\frac{\sum_{i=1}^Nw_{i,m}\mathbb{I}(\tilde{y}\neq\phi(x_i)}{\sum_{i=1}^Nw_{i,m}}

Update:

                 f_m(x)=f_{m-1}(x)+\beta_m\phi_m(x)

                 \phi_m=argmin_\phi w_{i,m}\mathbb{I}(\tilde{y}\neq\phi(x_i))

                 \beta_m=\frac{1}{2}log\frac{1-err_m}{err_m},err_m=\frac{\sum_{i=1}^Nw_{i,m}\mathbb{I}(\tilde{y}\neq\phi(x_i)}{\sum_{i=1}^Nw_{i,m}}

                 w_{i,m}=exp(-\tilde{y_i}f_{m-1}(x_i))

                 \begin{align*} w_{i,m+1}&= exp(-\tilde{y}_i(f_{m-1}(x)+\beta_m\phi_m(x)))\\ &=w_{i,m}e^{-\beta_m\tilde{y}_i\phi_m(x_i)} \\ \end{align*}

實現步驟:

           1. w_i=1/N

           2. for \large m=1:M do

           3.        Fit a classifer \large \phi_m(x) to the trainning set using weights \LARGE w

           4.        Compute err_m=\frac{\sum_{i=1}^Nw_{i,m}\mathbb{I}(\tilde{y}\neq\phi(x_i)}{\sum_{i=1}^Nw_{i,m}}

           5.        Compute\alpha_m=\frac{1}{2}log\frac{1-err_m}{err_m},

           6.         Update  w_{i}&= w_{i}e^{-\alpha_m\mathbb{I}(\tilde{y}_i\neq\phi_m(x_i))}

           7. Return f(x)=sgn\left [ \sum_{m=1}^M\alpha_m\phi_m(x) \right ],sgn=0 or 1

– 樣本的權重:

              • 沒有先驗知識的情況下,初始的分佈應爲等概分佈,也就是訓 練集如果有N個樣本,每個樣本的分佈概率爲1/N
              • 每次循環後提高錯誤樣本的分佈概率,分錯樣本在訓練集中所 佔權重增大 使得下一次循環的弱學習機能夠集中力量對  這些錯誤樣本進行判斷。
– 弱學習機的權重

              • 準確率越高的弱學習機權重越高
– 循環控制:損失函數達到最小
               • 在強學習機的組合中增加一個加權的弱學習機 使準確率提高,損失函數值減小。

缺點:

– 速度慢,在一定程度上依賴於訓練數據集合和弱學習器的選擇,訓練數據不充足或者弱學習器太過“弱”,都將導致其訓練精度的下降。
– Boosting易受到噪聲的影響,這是因爲它在迭代過程中總是給噪聲分配較大的權重,使得這些噪聲在以後的迭代中受到更多的關注。

 

from numpy import *

def loadSimpData():
    datMat = matrix([[ 1. ,  2.1],
        [ 2. ,  1.1],
        [ 1.3,  1. ],
        [ 1. ,  1. ],
        [ 2. ,  1. ]])
    classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
    return datMat,classLabels

def weakClassifier(dataMatrix,dimen,threshVal,threshIneq):#just classify the data,,lt:less than
    retArray = ones((shape(dataMatrix)[0],1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:,dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:,dimen] > threshVal] = -1.0
    return retArray
    

def BuildweakClassifier(dataArr,classLabels,weight_):
    dataMatrix = mat(dataArr); labelMat = mat(classLabels).T
    m,n = shape(dataMatrix)
    numSteps = 10.0; bestStump = {}; bestClasEst = mat(zeros((m,1)))
    minError = inf #init error sum, to +infinity
    for i in range(n):#loop over all dimensions
        rangeMin = dataMatrix[:,i].min(); rangeMax = dataMatrix[:,i].max();
        stepSize = (rangeMax-rangeMin)/numSteps
        for j in range(-1,int(numSteps)+1):#loop over all range in current dimension
            for inequal in ['lt', 'gt']: #go over less than and greater than
                threshVal = (rangeMin + float(j) * stepSize)
                predictedVals = weakClassifier(dataMatrix,i,threshVal,inequal)#call stump classify with i, j, lessThan
                errArr = mat(ones((m,1)))
                errArr[predictedVals == labelMat] = 0
                weightedError = weight_.T*errArr  #calc total error multiplied by weight_
                #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 = shape(dataArr)[0]
    weight_ = mat(ones((m,1))/m)   #init weight_ to all equal
    aggClassEst = mat(zeros((m,1)))
    for i in range(numIt):
        bestStump,error,classEst = BuildweakClassifier(dataArr,classLabels,weight_)#build Stump
        print ("weight_:",weight_.T)
        beta = float(0.5*log((1.0-error)/max(error,1e-16)))#calc beta, throw in max(error,eps) to account for error=0
        bestStump['beta'] = beta  
        weakClassArr.append(bestStump)                  #store Stump Params in Array
        #print "classEst: ",classEst.T
        expon = multiply(-1*beta*mat(classLabels).T,classEst) #exponent for weight_ calc, getting messy
        weight_ = multiply(weight_,exp(expon))                              #Calc New weight_ for next iteration
        weight_ = weight_/weight_.sum()
        #calc training error of all classifiers, if this is 0 quit for loop early (use break)
        aggClassEst += beta*classEst
        #print "aggClassEst: ",aggClassEst.T
        aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1)))
        errorRate = aggErrors.sum()/m
        print ("total error: ",errorRate)
        if errorRate == 0.0: break
    print ("#######################################################"  )
    print ("Number of Iteration: ",i+1)
    return weakClassArr,aggClassEst

def adaClassify(datToClass,classifierArr):
    dataMatrix = mat(datToClass)#do stuff similar to last aggClassEst in adaBoostTrainDS
    m = shape(dataMatrix)[0]
    aggClassEst = mat(zeros((m,1)))
    for i in range(len(classifierArr)):
        classEst = weakClassifier(dataMatrix,classifierArr[i]['dim'],\
                                 classifierArr[i]['thresh'],\
                                 classifierArr[i]['ineq'])#call stump classify
        aggClassEst += classifierArr[i]['beta']*classEst
    #print aggClassEst
    return sign(aggClassEst)


def change_data(x):
    x[0:100,0:50]=1
    x[0:100,50:] = -1
    return x


if __name__=="__main__":
    #dat,lab = loadSimpData()
    
    from sklearn import svm, datasets

    iris = datasets.load_iris()
    print ('type of iris: ', type(iris)) #<class 'sklearn.datasets.base.Bunch'>
    print ('keys:', iris.keys()) #['target_names', 'data', 'target', 'DESCR', 'feature_names']

    dat = iris.data[0:100,0:3] #only use the first two features        
    # change data
    dat = change_data(dat)
    
    lab = iris.target[0:100]
    lab[lab==0]=-1    

    weakClassArr,aggClassEst = adaBoostTrainDS(dat,lab)
    print (weakClassArr)
    pred = adaClassify(dat,weakClassArr)
    

                

           

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章