Améliorer Adaboost en utilisant la régression logistique pondérée au lieu des arbres de décision
J'ai implémenté Adaboost en utilisant une régression logistique pondérée au lieu d'arbres de décision et j'ai réussi à obtenir une erreur de 0,5%, j'essaie de l'améliorer pendant des jours sans succès et je sais qu'il est possible d'obtenir avec lui une erreur de 0%, j'espère que vous les gars pourraient m'aider à le faire.
Mon algorithme de régression logistique:
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
Mon Adaboost utilisant WLR:
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()
Le résultat de la 20e itération:

Le résultat de la 50e itération:

Il me semble que la machine n'apprend pas à chaque itération de régression. J'obtiens le même résultat, même après la 50e itération. Je voudrais savoir ce que je fais mal, peut-être que ma fonction fit n'est pas bien implémentée? ou peut-être ma fonction de coût?
Réponses
Cela semble être assez standard. Avec la régression logistique comme estimateur de base, l'amplification adaptative cesse d'ajouter de la valeur après très peu d'itérations. J'ai rassemblé un petit cahier pour illustrer, en utilisant sklearn
s AdaBoostClassifier, qui vous permet de définir le vôtre base_estimator
.)
Notez que contrairement à l'augmentation de gradient, vous pouvez théoriquement obtenir un modèle non linéaire à la fin, car la fonction de lien sigmoïde est appliquée à chacun des modèles de base avant de calculer la moyenne des prédictions, pas après la sommation. Cependant, les exemples du bloc-notes ne montrent aucun résultat fortement non linéaire. Je soupçonne que c'est simplement parce que la régression logistique s'adapte trop bien, de sorte que les points mal classés sont "équilibrés" de telle sorte que les itérations ultérieures n'ont pas beaucoup d'effet.
Pour construire à partir d'un autre commentaire, augmenter avec un estimateur de base linéaire n'ajoute pas de complexité comme il le ferait avec les arbres. Donc, pour augmenter la précision dans cette configuration, vous devez injecter cette complexité (dimensions supplémentaires où les données sont linéairement séparables) généralement en ajoutant des termes d'interaction ou des termes d'expansion polynomiale et laisser le boosting s'occuper de la régularisation de vos coefficients.
Sklearn a une méthode simple pour l'expansion polynomiale avec et sans termes d'interaction: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html