Улучшение Adaboost с использованием взвешенной логистической регрессии вместо деревьев решений
Я реализовал Adaboost, который использовал взвешенную логистическую регрессию вместо деревьев решений, и мне удалось получить ошибку 0,5%, я пытаюсь улучшить ее в течение нескольких дней безуспешно, и я знаю, что с ним можно получить ошибку 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
Мой Adaboost с использованием 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()
Результат 20-й итерации:

Результат 50-й итерации:

Мне кажется, что машина не учится на каждой итерации регрессии. Я получаю тот же результат даже после 50-й итерации. Я хотел бы знать, что я делаю не так, может быть, моя функция соответствия не реализована должным образом? или, может быть, моя функция стоимости?
Ответы
Это кажется довольно стандартным. Если в качестве базовой оценки используется логистическая регрессия, адаптивное повышение перестает добавлять ценность после нескольких итераций. Для иллюстрации я собрал небольшую записную книжку , используя sklearn
s AdaBoostClassifier, что позволяет вам создавать свои собственные base_estimator
.)
Обратите внимание, что в отличие от повышения градиента, вы теоретически можете получить нелинейную модель в конце, потому что функция сигмовидной связи применяется к каждой из базовых моделей до усреднения прогнозов, а не после суммирования. Однако примеры в записной книжке не показывают сильно нелинейных результатов. Я подозреваю, что это просто потому, что логистическая регрессия подходит слишком хорошо, так что неправильно классифицированные точки «сбалансированы» таким образом, что более поздние итерации не имеют большого эффекта.
Чтобы построить еще один комментарий, повышение с помощью линейной базовой оценки не добавляет сложности, как это было бы с деревьями. Таким образом, чтобы повысить точность в этой настройке, вы должны ввести эту сложность (дополнительные измерения, в которых данные линейно разделяются), как правило, путем добавления условий взаимодействия или условий полиномиального расширения, и позволить усилению позаботиться о регуляризации ваших коэффициентов.
В Sklearn есть простой метод полиномиального разложения с элементами взаимодействия и без них: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html