3.2 AdaBoost from scratch
Maintenant vous le savez, implémenter un algorithme from scratch est pour moi la meilleure façon d’être sûr d’avoir bien compris son fonctionnement.
Dans cette partie, nous allons implémenter l’algorithme AdaBoost from scratch en Python.
Pour plus de simplicité, nous n’allons pas implémenter l’algorithme d’arbre de décision mais utiliser ce qui est proposé par le package Sklearn.
Si vous voulez avoir plus d’informations sur l’implémentation de l’arbre de décision, n’hésitez pas à revenir dans le premier chapitre.
A/ L'initialisation de la classe AdaBoost
Nous allons coder la classe du modèle qui va nous permettre d’entraîner nos modèles AdaBoost.
On va commencer par créer la fonction d’initialisation de la classe.
C’est à cette étape que l’on va demander à l’utilisateur de spécifier toutes les informations dont on a besoin pour entraîner notre modèle.
class adaBoostClassifier():
def __init__(self, n_estimators=100, max_depth=1, random_state=None):
self._n_estimators = n_estimators
self._max_depth = max_depth
self._random_state = random_state
self._sample_weight = np.array([])
self._weight_model = dict()
self._model = dict()
self._features = dict()
B/ La fonction d'entraînement
La classe fit, entraîne un nombre d’arbres de notre forêt égal au nombre spécifié dans n_estimators_.
Nous allons aléatoirement choisir un nombre d’exemples et un nombre de colonnes, cet échantillon est assigné à x_samp.
Nous allons entraîner un arbre en utilisant x_samp comme données d’entraînement. Cet arbre sera sauvé dans le dictionnaire model et les variable à utiliser pour cette arbre seront sauvées dans le dictionnaire features.
A chaque arbre est associé un poids rangé dans le dictionnaire weight_model.
Les poids des exemples contenus dans le dictionnaire sample_weight sont mis-à-jour après chaque entraînement d’arbre.
def fit(self, x, y):
# Initialisation des poids
x_length = len(x)
self._sample_weight = np.ones(x_length)/x_length
np.random.seed(seed=9999)
for est in range(self._n_estimators) :
# Sélection aléatoire des indices colonnes et des indices exemples
rand_index = np.random.randint(low=0, high=x.shape[0], size=round(x.shape[0]*0.8))
rand_column = np.random.randint(low=0, high=x.shape[1], size=round(x.shape[1]*0.8))
# Sélection aléatoire des colonnes et des exemples
x_samp = x[rand_index, :]
x_samp = x_samp[:, rand_column]
y_samp = y[rand_index]
weight_samp = self._sample_weight[rand_index]
# Initialisation du modèle d'arbre de décision
decision_tree_model = DecisionTreeClassifier(max_depth=self._max_depth, random_state=self._random_state)
# Entraînement du modèle d'arbre de décision
self._model[est] = decision_tree_model.fit(x_samp, y_samp, sample_weight=weight_samp)
# Sauvegarde des colonnes utlisées pour l'arbre
self._features[est] = rand_column
# Prédition des données d'entraînement
y_pred = self._model[est].predict(x_samp)
# Calcul de l'erreur total pondérée par le poids de chaque exemple
total_error = np.sum((y_pred!=y_samp) * weight_samp)
# Calcul du poids de l'arbre
self._weight_model[est] = (1/2) * np.log((1-total_error)/(total_error))
# Mise à jour des poids de l'arbre
self._sample_weight[rand_index] = (y_pred!=y_samp) * self._sample_weight[rand_index] * np.exp(self._weight_model[est]) + (y_pred==y_samp) * self._sample_weight[rand_index] * np.exp(-self._weight_model[est])
# Normalisation des poids
self._sample_weight /= np.sum(self._sample_weight)
C/ La fonction de prédiction
La classe predict utilise les données passées en paramètres dans x et boucle sur les arbres de notre forêts pour obtenir une prédiction pour chaque arbre.
def predict(self, x):
# Initialiser le vecteur de prédiction
pred = np.zeros(x.shape[0])
# On ajoute à pred la prédiction de chaque arbre
for i in range(self._n_estimators):
pred += self._model[i].predict(x[:, self._features[i]]) * self._weight_model[i]
# Normalisation des prédictions
pred /= self._n_estimators
# Passage d'une probabilité à une prédiction
pred = np.where(pred >= 0.5, 1, 0)
return pred