.. _chap-tpForetsAleatoires: ##################################### Travaux pratiques - Forets aléatoires ##################################### L'objectif de cette séance de travaux pratiques est de montrer l'utilisation des méthodes d'agrégation (bagging, forets aléatoires, boosting) pour les problèmes de classification et de régression en python (sklearn). Ce document reprend librement un nombre d'exemples montrés dans la documentation de Scikit-Learn. Références externes utiles : * `Documentation NumPy `_ * `Documentation SciPy `_ * `Documentation MatPlotLib `_ * `Site scikit-learn `_ * `Site langage python `_ Méthodes d'agrégation ********************* Les méthodes ensemblistes (ou d'agrégation) pour les algorithmes d'apprentissage statistique (en anglais: ensemble learning) sont basées sur l'idée de combiner les prédictions de plusieurs prédicteurs (ou classifieurs) pour une meilleure généralisation et pour compenser les défauts éventuels de prédicteurs individuels. En général, on distingue deux familles de méthodes de ce type : 1. Méthodes par moyennage (Bagging, Forest aléatoires) ou le principe est de faire la moyenne de plusieurs prédictions en espérant un meilleur résultat suite à la réduction de variance de l'éstimateur moyenne. 2. Méthodes adaptatives (Boosting) ou les paramètres sont itérativement adaptés pour produire un meilleur mélange. Par la suite on va explorer chacune de ces classes d'algorithme en Scikit-learn, et présenter quelques comparaisons. Bagging ******* Les méthodes de type Bagging construisent plusieurs instances d'un estimateur, calculés sur des échantillons aléatoires tirés de la base d'apprentissage (et éventuellement une partie des attributs, également sélectionnés de façon aléatoire), et ensuite combine les prédictions individuelles par leur moyenne pour réduire la variance de l'estimateur. Leur avantage principal réside dans le fait qu'ils construisent une version améliorée de l'algorithme de base, sans demander des modifications sur cet algorithme. Le prix à payer est un effort de calcul plus élevé. Comme ils réduisent le sur-apprentissage, les méthodes Bagging fonctionnent très bien avec des prédicteurs forts. Par contraste, les methodes Boosting sont plus adaptées pour des prédicteurs faibles (weak learners). Dans Scikit-learn, les méthodes Bagging sont implémentées via la classe ``BaggingClassifier`` et ``BaggingRegressor``. Les constructeurs prennent en paramètres un estimateur de base et la stratégie de sélection des points et attributs : * ``base_estimator`` : optionnel (default=None). Si None alors l'estimateur est un arbre de décision * ``max_samples`` : la taille de l'échantillon aléatoire tiré de la base d'apprentissage * ``max_features`` : le nombre d'attributs tirés aléatoirement * ``bootstrap`` : boolean, optional (default=True). Tirage des points avec remise ou non. * ``bootstrap_features`` : boolean, optional (default=False). Tirage des attributs avec remise ou non. * ``oob_score`` : boolean. Estimer ou non l'erreur de généralisation OOB (Out of Bag) Le code suivant construit un ensemble des classifieurs de base de type KNeighborsClassifier, chacun utilisant un échantillon de 50% de points d'apprentissage et 50% des attributs (features) : .. code-block:: python from sklearn.ensemble import BaggingClassifier from sklearn.neighbors import KNeighborsClassifier bagging = BaggingClassifier(KNeighborsClassifier(), max_samples=0.5, max_features=0.5) Dans cet exemple nous allons utiliser la base de données ``digits``, qui contient 10 classes (images des chiffres en écriture manuscrite). Il y a 1797 éléments, chaque élément à 64 attributs. .. code-block:: python from sklearn.datasets import load_digits digits = load_digits() print(digits.data.shape) # afficher une des images import matplotlib.pyplot as plt plt.gray() plt.matshow(digits.images[1]) plt.show() Le classifieur de base va être un arbre de décision : .. code-block:: python import numpy as np from sklearn import tree from sklearn.ensemble import BaggingClassifier digits = load_digits() X=digits.data y=digits.target clf = tree.DecisionTreeClassifier() clf.fit(X, y) accuracy=clf.score(X,y) Sur la base d'apprentissage accuracy = 1. Pour plus de réalisme, on va couper la base en apprentissage/test pour voir le comportement de l'arbre sur des données différentes des celles d'apprentissage : .. code-block:: python from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.90) clf = tree.DecisionTreeClassifier() clf.fit(X_train, y_train) Z = clf.predict(X_test) accuracy=clf.score(X_test,y_test) .. admonition:: Question : Construire la variance de la valeur "accuracy" sur 100 tirages test/split. Quelle conclusion on tire ? Pour comparer, on va construire un classifieur Bagging sur nos données avec un classifieur de base ``DecisionTreeClassifier`` : .. code-block:: python clf = BaggingClassifier(tree.DecisionTreeClassifier(), max_samples=0.5, max_features=0.5, n_estimators=200) clf.fit(X_train, y_train) Z = clf.predict(X_test) accuracy=clf.score(X_test,y_test) .. admonition:: Question : Construire la variance de la valeur "accuracy" sur 100 tirages test/split. Comparer avec la variance du classifieur de base. Quelle conclusion on tire ? .. admonition:: Question : Construire le graphique 1-"accuracy" vs n_estimators. Que constatez vous ? .. admonition:: Question : Faites varier les parametres max_samples et max_features. Pour quelles valeurs on obtient les meilleur résultat ? Forets aléatoires ***************** L'algorithme forets aléatoires propose une optimisation des arbres de décision. Le but est de réduire la variance de l'estimateur obtenu par une étape supplémentaire de randomisation dans la sélection des attributs des noeuds. Les deux objets Python qui implémentent les forets aléatoires sont ``RandomForestClassifier`` et ``RandomForestRegressor``. Ils s'utilisent sur un principe similaire au Bagging. Les paramètres les plus importants sont : * ``n_estimators`` : integer, optional (default=10). Le nombre d'arbres * ``max_features`` : le nombre d'attributs à considérer a chaque split * ``max_samples`` : la taille de l'échantillon aléatoire tiré de la base d'apprentissage * ``min_samples_leaf`` : le nombre minimal d'éléments dans un noeud feuille * ``oob_score`` : boolean. Estimer ou non l'erreur de généralisation OOB (Out of Bag) Par la suite nous allons refaire la classification sur la base "digits" en utilisant un classifieur ``RandomForestClassifier`` : .. code-block:: python from sklearn.ensemble import RandomForestClassifier import numpy as np from sklearn import tree digits = load_digits() X=digits.data y=digits.target from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.90) clf = RandomForestClassifier(n_estimators=200) clf.fit(X_train, y_train) Z = clf.predict(X_test) accuracy=clf.score(X_test,y_test) .. admonition:: Question : Comment la valeur de la variable "accuracy" se compare avec le cas Bagging qui utilise le même nombre d'arbres (200 dans notre cas) ? .. admonition:: Question : Construire la variance de la valeur "accuracy" sur 100 tirages test/split. Quelle conclusion tirer quand on compare avec la séction précedente (Bagging) ? .. admonition:: Question : Construire le graphique 1-"accuracy" vs n_estimators. Que constatez vous ? A partir de quelle valeur on ne gagne plus ? .. admonition:: Question : Regardez dans la documentation les ExtraTreesClassifier et refaites la classification avec ce type de classifieur. Comparer avec RandomForestClassifier. Boosting ******** Le principe du Boosting est d'évaluer une séquence de classifieurs faible (weak learners) sur plusieurs versions légèrement modifiés des données d'apprentissage. Les décisions obtenues sont alors combinées par une somme ponderée pour obtenir le modèle final. En Scikit-learn c'est la classe AdaBoostClassifier qui implemente l'algorithme. Les paramètres les plus importants sont : * ``n_estimators`` : integer, optional (default=10). Le nombre de classifieurs faibles * ``learning_rate`` : contrôle la vitesse de changement des poids par itération * ``base_estimator`` : (default=DecisionTreeClassifier) le classifieur faible utilisé Par la suite nous allons refaire la classification sur la base "digits" en utilisant un classifieur ``RandomForestClassifier`` : .. code-block:: python from sklearn.ensemble import AdaBoostClassifier import numpy as np from sklearn import tree digits = load_digits() X=digits.data y=digits.target from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.90) clf = AdaBoostClassifier(base_estimator=tree.DecisionTreeClassifier(max_depth=5), n_estimators=200, learning_rate=2) clf.fit(X_train, y_train) Z = clf.predict(X_test) accuracy=clf.score(X_test,y_test) .. admonition:: Question : Le paramètre "max_depth" controle la profondeur de l'arbre. Essayez plusieurs valeurs pour voir l'impact de l'utilisation d'un classiffieur "faible" vs plus fort (max_depth grand ou eliminer le parametre). Testez aussi l'effet du parametre "learning_rate" et le nombre de classifieurs.