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)
Tout d'abord, ce sera la forme de base.
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.)
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".)
Vérifiez la méthode de XGBoost, qui est un représentant de Gradient Boosting.
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.
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.
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.
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.)
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
Développez l'axe vertical et regardez le voisinage de y = 0.
Fig.2 Feature Importance vs. StatsModels' p-value
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.
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
É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.
--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