의사 결정 트리 대신 가중 로지스틱 회귀를 사용하는 Adaboost 개선

Aug 20 2020

나는 의사 결정 트리 대신 가중 로지스틱 회귀를 사용하여 0.5 % 오류를 얻을 수있는 Adaboost를 구현했으며 성공하지 못한 채 며칠 동안 개선하려고 노력하고 있으며 그와 함께 0 % 오류를 얻을 수 있다는 것을 알고 있습니다. 사람들이 나를 도울 수 있습니다.

내 로지스틱 회귀 알고리즘 :

Lg.py :

import numpy as np
from scipy.optimize import fmin_tnc
class LogistReg:
    def __init__(self,X,y,w):
        self.X = np.c_[np.ones((X.shape[0],1)),X]
        self.y = np.copy(y[:,np.newaxis])
        self.y[self.y==-1]=0
        self.theta = np.zeros((self.X.shape[1],1))
        self.weights = w
    
    def sigmoid(self, x):
        return 1.0/(1.0 + np.exp(-x))
    
    def net_input(self, theta, x):
        return np.dot(x,theta)
    
    def probability(self,theta, x):
        return self.sigmoid(self.net_input(theta,x))
    
    def cost_function(self,theta,x,y):
        m = x.shape[0]
        tmp = (y*np.log(self.probability(theta,x)) + (1-y)*np.log(1-self.probability(theta,x)))
        total_cost = -(1.0/m )* np.sum(tmp*self.weights)/np.sum(self.weights)
        return total_cost

    def gradient(self,theta,x,y):
        m = x.shape[0]
        return (1.0/m)*np.dot(x.T,(self.sigmoid(self.net_input(theta,x))-y)*self.weights)

    def fit(self):
        opt_weights = fmin_tnc(func=self.cost_function,x0=self.theta,fprime=self.gradient,
        args=(self.X,self.y.flatten()))
        self.theta = opt_weights[0][:,np.newaxis]
        return self
    
    def predict(self,x):
        tmp_x = np.c_[np.ones((x.shape[0],1)),x]
        probs = self.probability(self.theta,tmp_x)
        probs[probs<0.5] = -1
        probs[probs>=0.5] = 1
        return probs.squeeze()
    
    def accuracy(self,x, actual_clases, probab_threshold = 0.5):
        predicted_classes  = (self.predict(x)>probab_threshold).astype(int)
        predicted_classes = predicted_classes.flatten()
        accuracy = np.mean(predicted_classes == actual_clases)
        return accuracy*100.0

WLR을 사용하는 내 Adaboost :

adaboost_lg.py :


    import numpy as np
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.linear_model import LogisticRegression
    import matplotlib.pyplot as plt
    from sklearn.datasets import make_gaussian_quantiles
    from sklearn.model_selection import train_test_split
    from plotting import plot_adaboost, plot_staged_adaboost
    from Lg import LogistReg
    class AdaBoostLg:
        """ AdaBoost enemble classifier from scratch """
    
        def __init__(self):
            self.stumps = None
            self.stump_weights = None
            self.errors = None
            self.sample_weights = None
    
        def _check_X_y(self, X, y):
            """ Validate assumptions about format of input data"""
            assert set(y) == {-1, 1}, 'Response variable must be ±1'
            return X, y
    
        def fit(self, X: np.ndarray, y: np.ndarray, iters: int):
            """ Fit the model using training data """
            X, y = self._check_X_y(X, y)
            n = X.shape[0]
    
            # init numpy arrays
            self.sample_weights = np.zeros(shape=(iters, n))
            self.stumps = np.zeros(shape=iters, dtype=object)
            self.stump_weights = np.zeros(shape=iters)
            self.errors = np.zeros(shape=iters)
    
            # initialize weights uniformly
            self.sample_weights[0] = np.ones(shape=n) / n
    
            for t in range(iters):
                # fit  weak learner
                curr_sample_weights = self.sample_weights[t]
                stump = LogistReg(X,y,curr_sample_weights)
                #stump = LogisticRegression()
                #stump = stump.fit(X, y, sample_weight=curr_sample_weights)
                stump  = stump.fit()
                # calculate error and stump weight from weak learner prediction
                stump_pred = stump.predict(X)
                err = curr_sample_weights[(stump_pred != y)].sum()# / n
                stump_weight = np.log((1 - err) / err) / 2
    
                # update sample weights
                new_sample_weights = (
                    curr_sample_weights * np.exp(-stump_weight * y * stump_pred)
                )
                
                new_sample_weights /= new_sample_weights.sum()
    
                # If not final iteration, update sample weights for t+1
                if t+1 < iters:
                    self.sample_weights[t+1] = new_sample_weights
    
                # save results of iteration
                self.stumps[t] = stump
                self.stump_weights[t] = stump_weight
                self.errors[t] = err
    
            return self
    
        def predict(self, X):
            """ Make predictions using already fitted model """
            stump_preds = np.array([stump.predict(X) for stump in self.stumps])
            return np.sign(np.dot(self.stump_weights, stump_preds))
    
    def make_toy_dataset(n: int = 100, random_seed: int = None):
        """ Generate a toy dataset for evaluating AdaBoost classifiers """
        
        n_per_class = int(n/2)
        
        if random_seed:
            np.random.seed(random_seed)
    
        X, y = make_gaussian_quantiles(n_samples=n, n_features=2, n_classes=2)
        
        return X, y*2-1
    
    
    # assign our individually defined functions as methods of our classifier
    
    if __name__ =='__main__':
        X, y = make_toy_dataset(n=10, random_seed=10)
        # y[y==-1] = 0
        plot_adaboost(X, y)
    
        clf = AdaBoostLg().fit(X, y, iters=20)
        #plot_adaboost(X, y, clf)
    
        train_err = (clf.predict(X) != y).mean()
        #print(f'Train error: {train_err:.1%}')
        plot_staged_adaboost(X, y, clf, 20)
        plt.show()

20 번째 반복 결과 :

50 번째 반복 결과 :

기계가 모든 회귀 반복에서 학습하지 않는 것 같습니다. 50 번째 반복 후에도 동일한 결과를 얻습니다. 내가 뭘 잘못하고 있는지 알고 싶습니다. 내 맞춤 기능이 제대로 구현되지 않았을 수 있습니까? 아니면 내 비용 함수?

답변

4 BenReiniger Aug 20 2020 at 02:34

이것은 꽤 표준적인 것 같습니다. 로지스틱 회귀를 기본 추정량으로 사용하면 적응 형 부스팅이 매우 적은 반복 후에 값 추가를 중지합니다. 설명하기 위해 s를 사용하여 작은 노트북 을 모아서 직접 설정할 수 있습니다 .)sklearnAdaBoostClassifierbase_estimator

그래디언트 부스팅과는 달리 시그 모이 드 링크 함수는 합산 후가 아니라 예측을 평균화하기 전에 각 기본 모델에 적용되기 때문에 이론적으로 끝에 비선형 모델을 얻을 수 있습니다. 그러나 노트북의 예에는 강력한 비선형 결과가 표시되지 않습니다. 나는 이것이 로지스틱 회귀가 너무 잘 맞아서 잘못 분류 된 포인트가 나중의 반복이 많은 영향을 미치지 않도록 "균형"이되어 있기 때문이라고 생각합니다.

1 Tylerr Aug 21 2020 at 02:13

다른 의견을 바탕으로 선형 기본 추정기로 부스팅해도 나무처럼 복잡성이 추가되지는 않습니다. 따라서이 설정에서 정확도를 높이려면 일반적으로 상호 작용 항 또는 다항식 확장 항을 추가하여 복잡성 (데이터가 선형으로 분리 가능한 추가 차원)을 주입하고 부스팅이 계수의 정규화를 처리하도록해야합니다.

Sklearn에는 상호 작용 항이 있거나없는 다항식 확장을위한 간단한 방법이 있습니다. https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html