[PYTHON] Mesurer l'importance des entités avec un outil de forêt aléatoire

introduction

Lors de l'analyse d'un ensemble de données contenant plusieurs entités, un analyseur d'ensemble basé sur un arbre de décision représenté par une forêt aléatoire peut calculer l'importance des entités. Jusqu'à présent, j'ai utilisé cette fonction comme une boîte noire, mais comme le nombre d'outils que j'utilise a augmenté, j'aimerais savoir comment l'utiliser.

Tout d'abord, l'algorithme de calcul de l'importance est cité dans «Première reconnaissance de forme» (chapitre 11).

Out-Of-Bag (OOB) Le taux d'erreur est calculé comme suit. Random Forest sélectionne les données d'entraînement à utiliser par le bootstrap lors de la création d'un arbre. En conséquence, environ 1/3 des données ne sont pas utilisées pour la formation. Pour un certain apprentissage, seuls les arbres de décision pour lesquels les données d'apprentissage n'ont pas été utilisées peuvent être collectés pour former une forêt partielle, et les données d'apprentissage peuvent être utilisées comme données de test pour évaluer les erreurs.

Lors de l'exécution d'un ensemble basé sur un arbre de décision, il semble que l'importance des quantités de caractéristiques qui conduisent à une précision améliorée puisse être mesurée en surveillant le taux d'erreur OOB en utilisant la méthode ci-dessus.

Ci-dessous, nous examinerons les outils suivants (package Python).

XGBoost et LightGBM ont des API qui sont plus proches des API natives et Scikit-learn, mais j'aimerais autant que possible utiliser les API Scikit-learn en tenant compte de l'efficacité de l'apprentissage.

(Les outils et bibliothèques utilisés sont les suivants. Python 3.5.2, Scikit-learn 0.18.1, XGBoost 0.6a2, LightGBM 0.1)

RandomForest de Scikit-learn

Tout d'abord, ce sera la forme de base.

Problème de classification

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=0)

clf_rf = RandomForestClassifier()
clf_rf.fit(X_train, y_train)
y_pred = clf_rf.predict(X_test)

accu = accuracy_score(y_test, y_pred)
print('accuracy = {:>.4f}'.format(accu))

# Feature Importance
fti = clf_rf.feature_importances_   

print('Feature Importances:')
for i, feat in enumerate(iris['feature_names']):
    print('\t{0:20s} : {1:>.6f}'.format(feat, fti[i]))

Comme beaucoup d'entre vous le savent peut-être, tout ce que vous avez à faire est de définir une instance du classificateur, fit (), puis d'obtenir le numéro feature_importances_.

Feature Importances:
	sepal length (cm)    : 0.074236
	sepal width (cm)     : 0.015321
	petal length (cm)    : 0.475880
	petal width (cm)     : 0.434563

Comme mentionné ci-dessus, dans le jeu de données «iris», les résultats montrent que les caractéristiques liées à «pétale» sont importantes. (Cette importance est un nombre relatif.)

Problème de retour

Nous avons traité du jeu de données "Boston" fourni avec Scikit-learn.

rf_reg = RandomForestRegressor(n_estimators=10)
rf_reg = rf_reg.fit(X_train, y_train)

fti = rf_reg.feature_importances_

print('Feature Importances:')
for i, feat in enumerate(boston['feature_names']):
    print('\t{0:10s} : {1:>.6f}'.format(feat, fti[i]))

Le nom de la fonction devient RandomForestRegressor (), mais l'importance des fonctionnalités est calculée de la même manière.

Feature Importances:
	CRIM       : 0.040931
	ZN         : 0.001622
	INDUS      : 0.005131
	CHAS       : 0.000817
	NOX        : 0.042200
	RM         : 0.536127
	AGE        : 0.018797
	DIS        : 0.054397
	RAD        : 0.001860
	TAX        : 0.010357
	PTRATIO    : 0.011388
	B          : 0.007795
	LSTAT      : 0.268579

Dans le problème de régression, «Importance des caractéristiques» a été obtenu de la même manière que le problème de classification. Le résultat est que les caractéristiques de «RM» et «LSTAT» sont importantes pour l'ensemble de données «Boston». (Veuillez noter que cette fois, nous n'avons guère ajusté les hyper paramètres dans le but "d'obtenir l'importance de la quantité de caractéristiques".)

Classificateur XGBoost, régressionr

Vérifiez la méthode de XGBoost, qui est un représentant de Gradient Boosting.

Problème de classification

clf_xgb = xgb.XGBClassifier(objective='multi:softmax',
                        max_depth = 5,
                        learning_rate=0.1,
                        n_estimators=100)
clf_xgb.fit(X_train, y_train,
        eval_set=[(X_test, y_test)],
        eval_metric='mlogloss',
        early_stopping_rounds=10)
y_pred_proba = clf_xgb.predict_proba(X_test)
y_pred = np.argmax(y_pred_proba, axis=1)

accu = accuracy_score(y_test, y_pred)
print('accuracy = {:>.4f}'.format(accu))

# Feature Importance
fti = clf_xgb.feature_importances_   

print('Feature Importances:')
for i, feat in enumerate(iris['feature_names']):
    print('\t{0:20s} : {1:>.6f}'.format(feat, fti[i]))

Étant donné que l'API Scikit-learn peut être utilisée, nous avons pu trouver l'importance de la fonctionnalité exactement de la même manière que le RandomForestClassifier mentionné ci-dessus.

Problème de retour

Comme pour la classification, je voulais utiliser l'API Scikit-learn, mais il semble que le calcul de l'importance des fonctionnalités ne soit pas pris en charge pour le moment. Il a été publié en tant que problème ouvert sur GitHub.

https://github.com/dmlc/xgboost/issues/1543

Au lieu de cela, utilisez l'API qui est plus proche du natif de XGBoost.

'''
# Error
reg_xgb = xgb.XGBRegressor(max_depth=3)
reg_xgb.fit(X_train, y_train)
fti = reg_xgb.feature_importances_
'''

def create_feature_map(features):
    outfile = open('boston_xgb.fmap', 'w')
    i = 0
    for feat in features:
        outfile.write('{0}\t{1}\tq\n'.format(i, feat))
        i = i + 1

    outfile.close()

create_feature_map(boston['feature_names'])

xgb_params = {"objective": "reg:linear", "eta": 0.1, "max_depth": 6, "silent": 1}
num_rounds = 100

dtrain = xgb.DMatrix(X_train, label=y_train)
gbdt = xgb.train(xgb_params, dtrain, num_rounds)

fti = gbdt.get_fscore(fmap='boston_xgb.fmap')

print('Feature Importances:')
for feat in boston['feature_names']:
    print('\t{0:10s} : {1:>12.4f}'.format(feat, fti[feat]))

Il est traité après avoir été généré une fois dans le fichier de carte d'entités. Les résultats suivants ont été obtenus à partir de ce qui précède.

Feature Importances:
	CRIM       :     565.0000
	ZN         :      55.0000
	INDUS      :      99.0000
	CHAS       :      10.0000
	NOX        :     191.0000
	RM         :     377.0000
	AGE        :     268.0000
	DIS        :     309.0000
	RAD        :      50.0000
	TAX        :      88.0000
	PTRATIO    :      94.0000
	B          :     193.0000
	LSTAT      :     285.0000

Il y a quelques incohérences avec les résultats de Scikit-learn RandomForestRegressor, mais on suppose que cela est dû à un ajustement insuffisant des paramètres.

Classificateur LightGBM, régressionr

Ici, l'API Scikit-learn pourrait être utilisée à la fois pour la classification / régression.

Classification

gbm = lgb.LGBMClassifier(objective='multiclass',
                        num_leaves = 23,
                        learning_rate=0.1,
                        n_estimators=100)
gbm.fit(X_train, y_train,
        eval_set=[(X_test, y_test)],
        eval_metric='multi_logloss',
        early_stopping_rounds=10)
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
y_pred_proba = gbm.predict_proba(X_test, num_iteration=gbm.best_iteration)

accu = accuracy_score(y_test, y_pred)
print('accuracy = {:>.4f}'.format(accu))

# Feature Importance
fti = gbm.feature_importances_

print('Feature Importances:')
for i, feat in enumerate(iris['feature_names']):
    print('\t{0:20s} : {1:>.6f}'.format(feat, fti[i]))

Revenir

gbm_reg = lgb.LGBMRegressor(objective='regression',
                        num_leaves = 31,
                        n_estimators=100)
gbm_reg.fit(X_train, y_train,
        eval_set=[(X_test, y_test)],
        eval_metric='l2',
        verbose=0)

# Feature Importances
fti = gbm_reg.feature_importances_

print('Feature Importances:')
for i, feat in enumerate(boston['feature_names']):
    print('\t{0:10s} : {1:>12.4f}'.format(feat, fti[i]))

C'est encore un outil "jeune", mais LightGBM est utile!

Jusqu'à présent, nous avons vu trois types d'outils. L'importance des quantités de caractéristiques montre une tendance similaire. Certaines des incohérences sont (encore) dues à un ajustement insuffisant des hyper paramètres.

(Supplément) Relation avec la valeur p dans la modélisation statistique

En complément, j'étais curieuse, j'ai donc réfléchi un peu à la "sélection de fonctionnalités" dans la modélisation statistique. Je pense que la bonne façon est de créer plusieurs modèles qui correspondent à un sous-ensemble des caractéristiques des données, de les adapter aux données et de comparer les valeurs des critères de sélection du modèle (par exemple AIC, le critère d'information d'Akaike) pour chaque modèle. .. Cependant, il n'est pas possible de déterminer le degré d'influence de toutes les caractéristiques en un seul processus.

À propos, il existe une valeur p (ou valeur z) en tant que valeur calculée lors d'un calcul de modélisation. Je voudrais voir s'il existe une relation entre cette valeur et l'importance des fonctionnalités des outils forestiers aléatoires.

J'ai utilisé ** StatsModels ** comme outil de modélisation statistique. Lorsque j'ai essayé de l'utiliser pour la première fois depuis longtemps, la version 0.8.0 est sortie. (Il semble que la version 0.7.x, qui était en cours de développement, ait été ignorée. De plus, la documentation était auparavant sur SourceForge, mais dans 0.8.0, un autre site http://www.statsmodels.org/stable/ Je déménageais.)

Modèle de régression linéaire de "Boston"

import statsmodels.api as sm

from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import KFold, train_test_split

print("Boston Housing: regression")
boston = load_boston()
y = boston['target']
X = boston['data']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.33, random_state=201703
)

# Using StatsModels
X1_train = sm.add_constant(X_train)
X1_test = sm.add_constant(X_test)
model = sm.OLS(y_train, X1_train)
fitted = model.fit()

print('summary = \n', fitted.summary())

Le résumé dans le code ci-dessus est le suivant.

                             OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.758
Model:                            OLS   Adj. R-squared:                  0.749
Method:                 Least Squares   F-statistic:                     78.38
Date:                Fri, 10 Mar 2017   Prob (F-statistic):           8.08e-92
Time:                        15:14:41   Log-Likelihood:                -1011.3
No. Observations:                 339   AIC:                             2051.
Df Residuals:                     325   BIC:                             2104.
Df Model:                          13                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const         38.3323      6.455      5.939      0.000      25.634      51.031
x1            -0.1027      0.035     -2.900      0.004      -0.172      -0.033
x2             0.0375      0.018      2.103      0.036       0.002       0.073
x3             0.0161      0.081      0.198      0.843      -0.144       0.176
x4             2.8480      1.058      2.692      0.007       0.767       4.929
x5           -19.4905      5.103     -3.819      0.000     -29.530      -9.451
x6             3.9906      0.501      7.973      0.000       3.006       4.975
x7             0.0004      0.016      0.024      0.980      -0.031       0.032
x8            -1.5980      0.256     -6.236      0.000      -2.102      -1.094
x9             0.3687      0.090      4.099      0.000       0.192       0.546
x10           -0.0139      0.005     -2.667      0.008      -0.024      -0.004
x11           -0.9445      0.167     -5.640      0.000      -1.274      -0.615
x12            0.0086      0.004      2.444      0.015       0.002       0.015
x13           -0.6182      0.063     -9.777      0.000      -0.743      -0.494
==============================================================================
Omnibus:                      115.727   Durbin-Watson:                   2.041
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              485.421
Skew:                           1.416   Prob(JB):                    3.91e-106
Kurtosis:                       8.133   Cond. No.                     1.53e+04
==============================================================================

Les principales informations statistiques ont été produites, mais cette fois, je voudrais me concentrer sur la valeur p. Je le sortie avec le code suivant.

pvalues = fitted.pvalues
feats = boston['feature_names']
print('P>|t| :')
for i, feat in enumerate(feats):
    print('\t{0:10s} : {1:>.6f}'.format(feat, pvalues[(i + 1)]))
P>|t| :
	CRIM       : 0.003980
	ZN         : 0.036198
	INDUS      : 0.843357
	CHAS       : 0.007475
	NOX        : 0.000160
	RM         : 0.000000
	AGE        : 0.980493
	DIS        : 0.000000
	RAD        : 0.000053
	TAX        : 0.008030
	PTRATIO    : 0.000000
	B          : 0.015065
	LSTAT      : 0.000000

Sur les 13 quantités de caractéristiques, «INDUS» et «AGE» sont proches de 1,0, et tous sont à moins de 0,05. Je l'ai tracé pour voir la relation entre cela et l'importance de la fonctionnalité obtenue par LightGBM.

Fig.1 Feature Importance vs. StatsModels' p-value feature_im1.png

Développez l'axe vertical et regardez le voisinage de y = 0.

Fig.2 Feature Importance vs. StatsModels' p-value feature_im2.png

L'axe horizontal correspond à l'importance de la fonction et l'axe vertical correspond à la valeur p. Dans ce domaine, à mesure que l'axe horizontal augmente, la variation sur l'axe vertical semble diminuer.

Problème de classification "Titanic"

Ensuite, j'ai étudié le problème de classification (classification binaire) du "Titanic" repris par Kaggle. Nous avons effectué une régression logistique de StatsModels comme suit.

import statsmodels.api as sm

# by StatsModels
    X_train = sm.add_constant(X_train)
    X_test = sm.add_constant(X_test)

    model_lr = sm.Logit(y_train, X_train)
    res = model_lr.fit()
    print('res.summary = ', res.summary())

    y_pred = res.predict(X_test)
    y_pred = np.asarray([int(y_i > 0.5) for y_i in y_pred])
   
    # check feature importance
    pvalues = res.pvalues

    print('P>|z|:')
    for i, feat in enumerate(feats):
        print('\t{0:15s} : {1:>12.4f}'.format(feat, pvalues[i]))

Comme mentionné ci-dessus, sm.Logit () est utilisé. Le résultat est le suivant.

                            Logit Regression Results                           
==============================================================================
Dep. Variable:                      y   No. Observations:                  668
Model:                          Logit   Df Residuals:                      657
Method:                           MLE   Df Model:                           10
Date:                Fri, 10 Mar 2017   Pseudo R-squ.:                  0.3219
Time:                        15:36:15   Log-Likelihood:                -305.07
converged:                       True   LL-Null:                       -449.89
                                        LLR p-value:                 2.391e-56
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const          1.2615   8.99e+06    1.4e-07      1.000   -1.76e+07    1.76e+07
x1             0.0001      0.000      0.309      0.758      -0.001       0.001
x2            -0.9141      0.162     -5.644      0.000      -1.232      -0.597
x3             2.7590      0.231     11.939      0.000       2.306       3.212
x4            -0.0322      0.009     -3.657      0.000      -0.049      -0.015
x5            -0.4421      0.132     -3.350      0.001      -0.701      -0.183
x6            -0.0709      0.141     -0.503      0.615      -0.347       0.206
x7          2.456e-07   1.77e-07      1.386      0.166   -1.02e-07    5.93e-07
x8             0.0023      0.003      0.926      0.354      -0.003       0.007
x9             0.6809   8.99e+06   7.57e-08      1.000   -1.76e+07    1.76e+07
x10            0.3574   8.99e+06   3.97e-08      1.000   -1.76e+07    1.76e+07
x11            0.2231   8.99e+06   2.48e-08      1.000   -1.76e+07    1.76e+07
==============================================================================

P>|z|:
	PassengerId     :       1.0000
	Pclass          :       0.7575
	Sex             :       0.0000
	Age             :       0.0000
	SibSp           :       0.0003
	Parch           :       0.0008
	Ticket          :       0.6151
	Fare            :       0.1657
	C               :       0.3543
	Q               :       1.0000
	S               :       1.0000

Je n'expliquerai pas le prétraitement des données avant de les mettre dans le modèle, mais "Embarqué" qui était dans l'ensemble de données (Port d'embarquement) est changé en 3 quantités de caractéristiques de ['C', 'Q', 'S'] par encodage One-Hot. ("Embarqué" n'était, comme prévu, pas une fonction très importante.) Je vais essayer de le comparer à nouveau avec le résultat de LightGBM. L'axe horizontal correspond à l'importance des fonctionnalités de LightGBM et l'axe vertical correspond à la valeur p.

Fig.3 Feature Importance vs. StatsModels' p-value feature_im3.png

Étant donné que le concept d'apprentissage automatique, en particulier le modèle du système d'ensemble d'arbres de décision et la modélisation statistique, est complètement différent, Il peut être déraisonnable d'essayer de voir la corrélation dans chaque graphique (Fig. 1, 2, 3).

C'est ma compréhension vague, mais les deux chiffres sont basés sur des concepts différents. Cependant, ce n'est pas complètement sans rapport, et j'imagine qu'il peut y avoir une relation causale (selon la situation) selon laquelle l'importance de la fonctionnalité n'augmente pas à moins que la valeur p devienne petite dans une certaine mesure. (Il n'y a pas de base, c'est juste une imagination.)

De plus, dans le but d'obtenir un "bon modèle", "des caractéristiques efficaces sont incorporées dans le modèle telles quelles", "pour des caractéristiques inefficaces et des caractéristiques dont la valeur p n'est pas faible, prétraiter ou améliorer le modèle", " Je pense que l'approche de «jeter ce qui est improbable» est importante dans les deux méthodes de modélisation.

Références, site web

--Première reconnaissance de formes, par M. Hirai, Morikita Publishing --Introduction à la modélisation statistique pour l'analyse des données, par M. Kubo, Iwanami Shoten

Recommended Posts

Mesurer l'importance des entités avec un outil de forêt aléatoire
Compter la partie concaténée maximale d'un graphe aléatoire avec NetworkX
Créez un programme de jugement de compatibilité avec le module aléatoire de python.
Mesurer la force de l'association dans un tableau croisé
Créez un outil de traduction avec Translate Toolkit
Prenez des captures d'écran LCD avec Python-LEGO Mindstorms
Visualisez le vocabulaire caractéristique d'un document avec D3.js
Calculer le produit des matrices avec une expression de caractère?
Un outil qui transforme automatiquement le gacha de Soshage
Un diagramme de réseau a été créé avec les données du COVID-19.
Obtenez l'identifiant d'un GPU avec une faible utilisation de la mémoire
Obtenez UNIXTIME au début d'aujourd'hui avec une commande
À propos des fonctionnalités de Python
[Golang] Un programme qui détermine le tour avec des nombres aléatoires
Analysez le modèle thématique pour devenir romancier avec GensimPy3
L'histoire de la création d'un bot de boîte à questions avec discord.py
Essayez d'extraire les caractéristiques des données de capteur avec CNN
Prédisez le nombre de coussins qui peuvent être reçus en tant que répondants rires avec Word2Vec + Random Forest
Mémorandum de l'outil de gestion de paquets Python ez_setup
Traitez le contenu du fichier dans l'ordre avec un script shell
Une histoire coincée avec l'installation de la bibliothèque de machine learning JAX
Trouvez la valeur optimale de la fonction à l'aide d'un algorithme génétique (partie 2)
[Statistiques] Saisir l'image de la théorie de la limitation du pôle central avec un graphe
[python, ruby] sélénium-Obtenez le contenu d'une page Web avec le pilote Web
[Introduction à StyleGAN] J'ai joué avec "The Life of a Man" ♬
Si vous donnez une liste avec l'argument par défaut de la fonction ...
L'histoire de la création d'un pilote standard pour db avec python.
[Python3] Définition d'un décorateur qui mesure le temps d'exécution d'une fonction
Obtenir l'URL du ticket JIRA créé par la bibliothèque jira-python
L'idée d'alimenter le fichier de configuration avec un fichier python au lieu de yaml
Préparez un environnement de test de charge distribué avec l'outil de test de charge Python Locust
L'histoire de la création d'un module qui ignore le courrier avec python
Maîtrisez les riches fonctionnalités d'IPython
Maîtriser les riches fonctionnalités d'IPython (2)
L'histoire de l'exportation d'un programme
Jetons un coup d'œil à l'incendie de forêt sur la côte ouest des États-Unis avec des images satellites.
L'histoire de la création d'un outil pour charger une image avec Python ⇒ l'enregistrer sous un autre nom
Une histoire qui visualise le présent de Qiita avec Qiita API + Elasticsearch + Kibana
L'histoire de la création d'un robot LINE pour le petit-déjeuner d'une université de 100 yens avec Python
[Explication AtCoder] Contrôlez les problèmes A, B, C d'ABC182 avec Python!
Calculer l'itinéraire le plus court d'un graphe avec la méthode Dyxtra et Python
Générez une liste contenant le nombre de jours du mois en cours.
[Introduction à Python] Comment trier efficacement le contenu d'une liste avec le tri par liste
Calculez la probabilité d'être une pièce de calmar avec le théorème de Bayes [python]
Hit une méthode d'une instance de classe avec l'API Web Python Bottle
Recevez une liste des résultats du traitement parallèle en Python avec starmap
L'histoire de la création d'une caméra sonore avec Touch Designer et ReSpeaker
Parlez de la probabilité d'évasion d'une marche aléatoire sur une grille entière
J'ai fait GAN avec Keras, donc j'ai fait une vidéo du processus d'apprentissage.
Créez DNN-CRF avec Chainer et reconnaissez la progression des accords de la musique
J'ai essayé de visualiser tous les arbres de décision de la forêt aléatoire avec SVG
Obtenez le salaire moyen d'un emploi avec des conditions spécifiées sur Indeed.com
J'ai fait une erreur en récupérant la hiérarchie avec MultiIndex of pandas
Python: je souhaite mesurer proprement le temps de traitement d'une fonction
[Explication AtCoder] Contrôle ABC184 Problèmes A, B, C avec Python!