Bei der Analyse eines Datensatzes mit mehreren Merkmalen kann ein auf einem Entscheidungsbaum basierender Ensemble-Analysator, der durch eine zufällige Gesamtstruktur dargestellt wird, die Wichtigkeit von Merkmalen berechnen. Bisher habe ich diese Funktion als Black Box verwendet, aber da die Anzahl der von mir verwendeten Tools gestiegen ist, möchte ich herausfinden, wie sie verwendet wird.
Zunächst wird der Algorithmus zur Berechnung der Wichtigkeit aus "Erste Mustererkennung" (Kapitel 11) zitiert.
Out-Of-Bag (OOB) Die Fehlerrate wird wie folgt berechnet. Random Forest wählt die Trainingsdaten aus, die vom Bootstrap beim Erstellen eines Baums verwendet werden sollen. Infolgedessen wird etwa 1/3 der Daten nicht für das Training verwendet. Für ein bestimmtes Lernen können nur die Entscheidungsbäume, für die die Lerndaten nicht verwendet wurden, gesammelt werden, um einen Teilwald zu bilden, und die Lerndaten können als Testdaten zur Bewertung von Fehlern verwendet werden.
Bei der Durchführung eines auf Entscheidungsbäumen basierenden Ensembles kann die Bedeutung von Merkmalsgrößen, die zu einer verbesserten Genauigkeit führen, durch Überwachen der OOB-Fehlerrate unter Verwendung der obigen Methode gemessen werden.
Im Folgenden werden die folgenden Tools (Python-Paket) vorgestellt.
XGBoost und LightGBM haben APIs, die näher an nativen und Scikit-Lern-APIs liegen, aber ich möchte Scikit-Lern-APIs im Hinblick auf die Lerneffizienz so oft wie möglich verwenden.
(Folgende Tools und Bibliotheken werden verwendet. Python 3.5.2, Scikit-learn 0.18.1, XGBoost 0.6a2, LightGBM 0.1)
Erstens wird es die Grundform sein.
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]))
Wie viele von Ihnen vielleicht wissen, müssen Sie lediglich eine Instanz des Klassifikators fit () definieren und dann die Nummer feature_importances_
abrufen.
Feature Importances:
sepal length (cm) : 0.074236
sepal width (cm) : 0.015321
petal length (cm) : 0.475880
petal width (cm) : 0.434563
Wie oben erwähnt, zeigen die Ergebnisse im "Iris" -Datensatz, dass die mit "Blütenblatt" verbundenen Merkmale wichtig sind. (Diese Wichtigkeit ist eine relative Zahl.)
Wir haben uns mit dem "Boston" -Datensatz befasst, der mit Scikit-learn geliefert wird.
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]))
Der Funktionsname ändert sich in "RandomForestRegressor ()", aber die Wichtigkeit von Features wird auf die gleiche Weise berechnet.
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
Im Regressionsproblem wurde "Feature Importances" auf die gleiche Weise wie beim Klassifizierungsproblem erhalten. Das Ergebnis ist, dass die Funktionen von "RM" und "LSTAT" für den Datensatz "Boston" wichtig sind. (Bitte beachten Sie, dass wir diesmal die Hyperparameter kaum angepasst haben, um "die Wichtigkeit der Merkmalsmenge zu ermitteln".)
Überprüfen Sie die Methode von XGBoost, die für Gradient Boosting repräsentativ ist.
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]))
Da die Scikit-Learn-API verwendet werden kann, konnten wir die Feature-Wichtigkeit genauso ermitteln wie den oben erwähnten RandomForestClassifier.
Wie bei der Klassifizierung wollte ich die Scikit-Learn-API verwenden, aber es scheint, dass die Berechnung der Feature-Wichtigkeiten derzeit nicht unterstützt wird. Es wurde als offenes Problem auf GitHub veröffentlicht.
https://github.com/dmlc/xgboost/issues/1543
Verwenden Sie stattdessen die API, die näher an der nativen XGBoost-API liegt.
'''
# 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]))
Es wird verarbeitet, nachdem es einmal in die Feature-Map-Datei ausgegeben wurde. Die folgenden Ergebnisse wurden aus den obigen erhalten.
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
Es gibt einige Inkonsistenzen mit den Ergebnissen von Scikit-learn RandomForestRegressor, aber es wird vermutet, dass dies auf eine unzureichende Anpassung der Parameter zurückzuführen ist.
Hier könnte die Scikit-Learn-API sowohl für die Klassifizierung als auch für die Regression verwendet werden.
Einstufung
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]))
Rückkehr
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]))
Es ist noch ein "junges" Tool, aber LightGBM ist nützlich!
Bisher haben wir drei Arten von Werkzeugen gesehen. Die Bedeutung der Merkmalsgrößen zeigt eine ähnliche Tendenz. Einige der Inkonsistenzen sind (wieder) auf eine unzureichende Anpassung der Hyperparameter zurückzuführen.
Als Ergänzung war ich neugierig und dachte ein wenig über die "Auswahl von Merkmalen" in der statistischen Modellierung nach. Ich denke, der richtige Weg besteht darin, mehrere Modelle zu erstellen, die einer Teilmenge der Datenmerkmale entsprechen, sie an die Daten anzupassen und die Werte der Modellauswahlkriterien (z. B. AIC, Akaikes Informationskriterium) für jedes Modell zu vergleichen. .. Es ist jedoch nicht möglich, den Grad des Einflusses aller Merkmale in einem einzigen Prozess zu bestimmen.
Übrigens gibt es einen p-Wert (oder z-Wert) als Wert, der zum Zeitpunkt einer Modellierungsberechnung berechnet wird. Ich möchte einen Blick darauf werfen, ob es einen Zusammenhang zwischen diesem Wert und der Funktionsbedeutung zufälliger Gesamtstrukturwerkzeuge gibt.
Ich habe ** StatsModels ** als statistisches Modellierungswerkzeug verwendet. Als ich zum ersten Mal seit langer Zeit versuchte, es zu verwenden, wurde Version 0.8.0 veröffentlicht. (Es scheint, dass die Version von 0.7.x, die sich in der Entwicklung befand, übersprungen wurde. Außerdem befand sich die Dokumentation zuvor auf SourceForge, aber in 0.8.0 auf einer anderen Website http://www.statsmodels.org/stable/ Ich zog um.)
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())
Die Zusammenfassung im obigen Code lautet wie folgt.
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
==============================================================================
Die wichtigsten statistischen Informationen wurden ausgegeben, aber dieses Mal möchte ich mich auf den p-Wert konzentrieren. Ich gebe es mit dem folgenden Code aus.
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
Von den 13 Merkmalsgrößen liegen "INDUS" und "AGE" nahe bei 1,0 und alle innerhalb von 0,05. Ich habe es geplottet, um die Beziehung zwischen diesem und der von LightGBM erhaltenen Funktionsbedeutung zu sehen.
Fig.1 Feature Importance vs. StatsModels' p-value
Erweitern Sie die vertikale Achse und betrachten Sie die Umgebung von y = 0.
Fig.2 Feature Importance vs. StatsModels' p-value
Die horizontale Achse ist Feature Importance und die vertikale Achse ist p-Wert. In diesem Bereich scheint die Variation auf der vertikalen Achse mit zunehmender horizontaler Achse abzunehmen.
Als nächstes untersuchte ich das Klassifizierungsproblem (binäre Klassifizierung) von "Titanic", das von Kaggle aufgegriffen wurde. Wir haben eine logistische Regression von StatsModels wie folgt durchgeführt.
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]))
Wie oben erwähnt, wird "sm.Logit ()" verwendet. Das Ergebnis ist wie folgt.
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
Ich werde die Datenvorverarbeitung nicht erklären, bevor ich sie in das Modell eingefügt habe, sondern "Eingeschifft", die im Datensatz enthalten war (Port of Board) wird durch One-Hot-Codierung in 3 Merkmalsgrößen von ['C', 'Q', 'S'] geändert. ("Eingeschifft" war erwartungsgemäß kein sehr wichtiges Merkmal.) Ich werde versuchen, es erneut mit dem Ergebnis von LightGBM zu vergleichen. Die horizontale Achse ist die Feature-Wichtigkeit von LightGBM und die vertikale Achse ist der p-Wert.
Fig.3 Feature Importance vs. StatsModels' p-value
Da das Konzept des maschinellen Lernens, insbesondere das Modell des Entscheidungsbaum-Ensemblesystems und die statistische Modellierung, völlig unterschiedlich sind, Es kann unangemessen sein, zu versuchen, die Korrelation in jedem Diagramm zu erkennen (Abb. 1, 2, 3).
Dies ist mein vages Verständnis, aber die beiden Zahlen basieren auf unterschiedlichen Konzepten. Es ist jedoch nicht völlig unabhängig, und ich stelle mir vor, dass es (je nach Situation) einen Kausalzusammenhang geben kann, dass die Merkmalsbedeutung nicht zunimmt, es sei denn, der p-Wert wird bis zu einem gewissen Grad klein. (Es gibt keine Grundlage, es ist nur eine Einbildung.)
Um ein "gutes Modell" zu erhalten, werden außerdem "effektive Merkmale unverändert in das Modell aufgenommen", "Vorverarbeitung oder Verbesserung des Modells für ineffektive Merkmale und Merkmale, deren p-Wert nicht klein ist", " Ich bin der Meinung, dass der Ansatz, "das Unwahrscheinliche wegzuwerfen", bei beiden Modellierungsmethoden wichtig ist.