J'ai reçu quelques demandes sur la prise en charge de contraintes monotones sur certaines fonctionnalités par rapport à la sortie,
c'est-à-dire que lorsque d'autres caractéristiques sont fixées, forcer la prédiction à être monotone croissante par rapport à la certaine caractéristique spécifiée. J'ouvre ce numéro pour voir l'intérêt général sur cette fonctionnalité. Je peux ajouter ceci s'il y a suffisamment d'intérêt à ce sujet,
J'aurais besoin de l'aide de volontaires de la communauté pour tester la fonctionnalité bêta et contribuer au document et au didacticiel sur l'utilisation de cette fonctionnalité. Veuillez répondre au problème si vous êtes intéressé
Une version expérimentale est fournie dans https://github.com/dmlc/xgboost/pull/1516. Pour l'utiliser avant qu'il ne soit fusionné, clonez le référentiel https://github.com/tqchen/xgboost ,
Activez les options suivantes (probablement possible via python, r API)
monotone_constraints = "(0,1,1,0)"
Il y a deux arguments
monotone_constraints
est une liste en longueur du nombre de caractéristiques, 1 indique une augmentation monotone, - 1 signifie une diminution, 0 signifie aucune contrainte. S'il est plus court que le nombre de fonctionnalités, 0 sera rempli.Actuellement, seul l'algorithme glouton exact est pris en charge sur le multicœur. Pas encore disponible en version distribuée
@tqchen J'ai reçu une demande au travail aujourd'hui pour construire des GBM avec des contraintes monotones pour tester les performances de certains autres modèles. Ce serait avec une perte de déviance tweedie, donc je devrais opter pour une fonction de perte personnalisée telle qu'elle existe aujourd'hui.
Dans tous les cas, cela semble être une bonne occasion d'aider et de faire du travail en même temps.
Sur la base de l'exposé ici , GBM (R Package) n'applique la monotonie que localement.
Pourriez-vous préciser comment XGBoost applique les contraintes monotones ?
Ce serait formidable si XGBoost pouvait appliquer des contraintes globales.
Je ne comprends pas ce que vous entendez par contrainte locale ou globale, pouvez-vous développer ?
Désolé, je colle un mauvais lien, voici le bon (Lien)
Chaque arbre peut uniquement suivre une contrainte monotone dans certains sous-ensembles de la caractéristique intéressée, de sorte que de nombreux arbres ensemble peuvent créer une violation de la monotonie globale sur toute la gamme de cette caractéristique.
OK, à mon avis, il est appliqué à l'échelle mondiale. Vous êtes invités à l'essayer.
Je viens de faire quelques tests simples de contrainte de monotonie dans le cadre d'une régression univariée. Vous pouvez trouver le code et une très brève documentation ici :
Quelques premiers constats :
Il s'avère que j'introduis un bogue dans le cas de la contrainte = -1. J'ai poussé un correctif, veuillez voir si la dernière version fonctionne bien. Veuillez également vérifier si cela fonctionne lorsqu'il y a plusieurs contraintes
@tqchen J'ai testé votre correctif pour le bogue décroissant, il semble que cela fonctionne maintenant.
Laissez-nous confirmer s'il y a une diminution de la vitesse par rapport à la version d'origine sur certains des ensembles de données standard, alors nous pouvons le fusionner dans
@tqchen J'ai testé un modèle à deux variables, l'un avec une contrainte croissante et l'autre avec une contrainte décroissante :
params_constrained = params.copy()
params_constrained['updater'] = "grow_monotone_colmaker,prune"
params_constrained['monotone_constraints'] = "(1,-1)"
Les résultats sont bons
Je vais essayer de trouver un peu de temps pour faire des tests de chronométrage cet après-midi.
J'ai fait une mise à jour vers #1516 pour permettre la détection automatique des options montone, maintenant l'utilisateur n'a plus qu'à passer monotone_constraints = "(0,1,1,0)"
, veuillez vérifier si cela fonctionne.
Je fusionnerai cela si les tests de vitesse se passent bien, et passons à l'étape suivante d'ajout de tutoriels
@madrury @XiaoxiaoWang87
Ajout de tests pour le cas multivarié ici :
no constraint: 964.9 microseconds per iteration
with constraint: 861.7 microseconds per iteration
(veuillez commenter si vous avez une meilleure façon de faire le test de vitesse)
Check failed: (wleft) <= (wright)
lors de la lecture de différents hyper-paramètres.J'ai fait quelques expériences de chronométrage dans un cahier jupyter.
Premier test : quelques données simulées simples. Il y a deux traits, un croissant et un décroissant, mais avec une petite onde sinusoïdale superposée pour que chaque trait ne soit pas vraiment monotone
X = np.random.random(size=(N, K))
y = (5*X[:, 0] + np.sin(5*2*pi*X[:, 0])
- 5*X[:, 1] - np.cos(5*2*pi*X[:, 1])
+ np.random.normal(loc=0.0, scale=0.01, size=N))
Voici les résultats de synchronisation de xgboosts avec et sans contraintes monotones. J'ai désactivé l'arrêt anticipé et augmenté un nombre défini d'itérations pour chacun.
D'abord sans contraintes monotones :
%%timeit -n 100
model_no_constraints = xgb.train(params, dtrain,
num_boost_round = 2500,
verbose_eval = False)
100 loops, best of 3: 246 ms per loop
Et ici avec des contraintes de monotonie
%%timeit -n 100
model_with_constraints = xgb.train(params_constrained, dtrain,
num_boost_round = 2500,
verbose_eval = False)
100 loops, best of 3: 196 ms per loop
Deuxième test : California hHousing data de sklearn. Sans contraintes
%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain,
num_boost_round = 2500,
verbose_eval = False)
10 loops, best of 3: 5.9 s per loop
Voici les contraintes que j'ai utilisées
print(params_constrained['monotone_constraints'])
(1,1,1,0,0,1,0,0)
Et le timing pour le modèle contraint
%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain,
num_boost_round = 2500,
verbose_eval = False)
10 loops, best of 3: 6.08 s per loop
@XiaoxiaoWang87 J'ai poussé un autre PR pour perdre le contrôle sur wleft et wright, veuillez voir que cela fonctionne.
@madrury Pouvez-vous également comparer avec la version précédente de XGBoost sans la fonction de contrainte ?
@tqchen Bien sûr. Pouvez-vous recommander un hachage de validation à comparer ? Dois-je simplement utiliser le commit avant l'ajout des contraintes monotones ?
Oui le précédent fera l'affaire
@tqchen Lors de la reconstruction de la version mise à jour, je reçois des erreurs que je n'avais pas auparavant. J'espère que la raison vous saute aux yeux.
Si j'essaie d'exécuter le même code qu'avant, j'obtiens une exception, voici le retraçage complet :
XGBoostError Traceback (most recent call last)
<ipython-input-14-63a9f6e16c9a> in <module>()
8 model_with_constraints = xgb.train(params, dtrain,
9 num_boost_round = 1000, evals = evallist,
---> 10 early_stopping_rounds = 10)
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in train(params, dtrain, num_boost_round, evals, obj, feval, maximize, early_stopping_rounds, evals_result, verbose_eval, learning_rates, xgb_model, callbacks)
201 evals=evals,
202 obj=obj, feval=feval,
--> 203 xgb_model=xgb_model, callbacks=callbacks)
204
205
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in _train_internal(params, dtrain, num_boost_round, evals, obj, feval, xgb_model, callbacks)
72 # Skip the first update if it is a recovery step.
73 if version % 2 == 0:
---> 74 bst.update(dtrain, i, obj)
75 bst.save_rabit_checkpoint()
76 version += 1
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in update(self, dtrain, iteration, fobj)
804
805 if fobj is None:
--> 806 _check_call(_LIB.XGBoosterUpdateOneIter(self.handle, iteration, dtrain.handle))
807 else:
808 pred = self.predict(dtrain)
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in _check_call(ret)
125 """
126 if ret != 0:
--> 127 raise XGBoostError(_LIB.XGBGetLastError())
128
129
XGBoostError: [14:08:41] src/tree/tree_updater.cc:18: Unknown tree updater grow_monotone_colmaker
Si je change tout pour l'argument mot-clé que vous avez implémenté, j'obtiens également une erreur :
TypeError Traceback (most recent call last)
<ipython-input-15-ef7671f72925> in <module>()
8 monotone_constraints="(1)",
9 num_boost_round = 1000, evals = evallist,
---> 10 early_stopping_rounds = 10)
TypeError: train() got an unexpected keyword argument 'monotone_constraints'
supprimer l'argument de mise à jour et conserver les arguments de contrainte monotone dans les paramètres, maintenant que la mise à jour de contrainte monotone est activée automatiquement lorsque des contraintes monotones sont présentées
@tqchen Mon copain @amontz m'a aidé à comprendre cela immédiatement après avoir posté le message. J'avais interprété votre commentaire comme passant monotone_constraints
comme kwarg à .train
.
Cela fonctionne avec ces ajustements. Merci.
@madrury peux-tu confirmer la vitesse ?
Aussi @madrury et @XiaoxiaoWang87 puisque cette fonctionnalité est maintenant sur le point d'être fusionnée, ce serait formidable si vous pouviez vous coordonner pour créer un didacticiel présentant cette fonctionnalité aux utilisateurs.
Nous ne pouvons pas transférer directement le notebook ipy vers le référentiel principal. mais les images peuvent être poussées vers https://github.com/dmlc/web-data/tree/master/xgboost et markdown vers le référentiel principal.
Nous devons également modifier la conversion de chaîne de l'interface frontale, de sorte que le tuple int puisse être converti au format de tuple de chaîne qui puisse être accepté par le backend.
@hetong007 pour les changements dans R et @slundberg pour Julia
@tqchen Julia est actuellement attachée à la version 0.4 de XGBoost, donc la prochaine fois que je devrai l'utiliser et que j'aurai du temps de côté, je mettrai à jour les liaisons si personne d'autre ne l'a fait d'ici là. À ce stade, ce changement peut également être ajouté.
Voici la comparaison entre les modèles _sans_ une contrainte monotone d'avant l'implémentation à après.
Commit 8cac37 : Avant implémentation de la contrainte monotone.'
Données simulées : 100 loops, best of 3: 232 ms per loop
Données californiennes : 10 loops, best of 3: 5.89 s per loop
Commit b1c224 : Après implémentation de la contrainte monotone.
Données simulées : 100 loops, best of 3: 231 ms per loop
Données californiennes : 10 loops, best of 3: 5.61 s per loop
L'accélération pour la Californie après la mise en œuvre me semble suspecte, mais je l'ai essayé deux fois dans chaque sens, et c'est cohérent.
Je serais heureux de tenter d'écrire un tutoriel. Je vais regarder autour de la documentation existante et mettre quelque chose ensemble dans les prochains jours.
C'est super, le PR est maintenant officiellement fusionné avec le maître. Hâte de voir le tuto
Merci @madrury. Hâte d'y. Faites-moi savoir ce que je peux aider. Je serais certainement disposé à avoir plus d'études sur ce sujet.
Je l'améliorerai demain. Je suis juste curieux de savoir pourquoi communiquer avec C++ via une chaîne au lieu d'un tableau.
Je teste à partir de R. J'ai généré au hasard une donnée à deux variables et j'essaie de faire une prédiction.
Cependant, j'ai trouvé que
monotone_constraints
rend la prédiction légèrement différente.Veuillez le signaler si j'ai fait des erreurs.
Le code pour le reproduire (testé sur la dernière version de github , pas à partir de drat
):
set.seed(1024)
x1 = rnorm(1000, 10)
x2 = rnorm(1000, 10)
y = -1*x1 + rnorm(1000, 0.001) + 3*sin(x2)
train = cbind(x1, x2)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 10,
monotone_constraints = '(1,-1)')
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 10)
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)
La contrainte a été faite sur l'ordre partiel. Donc, la contrainte n'est appliquée que si nous déplaçons l'axe montone, en gardant l'autre axe fixe
@hetong007 Pour faire mes parcelles je
seq
dans R.colmeans
dans R.Voici le code python que j'ai utilisé pour les tracés que j'ai inclus ci-dessus, il devrait assez facilement se convertir en code R équivalent.
def plot_one_feature_effect(model, X, y, idx=1):
x_scan = np.linspace(0, 1, 100)
X_scan = np.empty((100, X.shape[1]))
X_scan[:, idx] = x_scan
left_feature_means = np.tile(X[:, :idx].mean(axis=0), (100, 1))
right_feature_means = np.tile(X[:, (idx+1):].mean(axis=0), (100, 1))
X_scan[:, :idx] = left_feature_means
X_scan[:, (idx+1):] = right_feature_means
X_plot = xgb.DMatrix(X_scan)
y_plot = model.predict(X_plot, ntree_limit=bst.best_ntree_limit)
plt.plot(x_scan, y_plot, color = 'black')
plt.plot(X[:, idx], y, 'o', alpha = 0.25)
Voici comment je fais les tracés de dépendance partielle (pour un modèle arbitraire):
Code:
def plot_partial_dependency(bst, X, y, f_id):
X_temp = X.copy()
x_scan = np.linspace(np.percentile(X_temp[:, f_id], 0.1), np.percentile(X_temp[:, f_id], 99.5), 50)
y_partial = []
for point in x_scan:
X_temp[:, f_id] = point
dpartial = xgb.DMatrix(X_temp[:, feature_ids])
y_partial.append(np.average(bst.predict(dpartial)))
y_partial = np.array(y_partial)
# Plot partial dependence
fig, ax = plt.subplots()
fig.set_size_inches(5, 5)
plt.subplots_adjust(left = 0.17, right = 0.94, bottom = 0.15, top = 0.9)
ax.plot(x_scan, y_partial, '-', color = 'black', linewidth = 1)
ax.plot(X[:, f_id], y, 'o', color = 'blue', alpha = 0.02)
ax.set_xlim(min(x_scan), max(x_scan))
ax.set_xlabel('Feature X', fontsize = 10)
ax.set_ylabel('Partial Dependence', fontsize = 12)
Merci pour l'orientation ! J'ai réalisé que j'avais fait une erreur stupide dans l'intrigue. Voici un autre test sur une donnée univariée, le tracé semble correct :
set.seed(1024)
x = rnorm(1000, 10)
y = -1*x + rnorm(1000, 0.001) + 3*sin(x)
train = matrix(x, ncol = 1)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 100,
monotone_constraints = '(-1)')
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 100)
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)
@hetong007 Donc, le but de l'interface R est de permettre à l'utilisateur de passer le tableau R en plus des chaînes
monotone_constraints=c(1,-1)
S'il vous plaît laissez-nous savoir quand vous êtes PR le tutoriel
@hetong007 Vous êtes également plus que bienvenu pour créer une version r-blogger
@tqchen Désolé les gars, je suis en voyage de travail depuis la semaine.
J'ai envoyé quelques pull request pour un didacticiel de contraintes monotones. S'il vous plaît laissez-moi savoir ce que vous pensez, je suis heureux avec toute critique ou critique.
Espérons qu'il soit approprié de demander ceci ici : cela fonctionnera-t-il maintenant si nous mettons à jour en utilisant l'habituel git clone --recursive https://github.com/dmlc/xgboost
?
Je demande en voyant le nouveau tutoriel mais rien de nouveau sur une modification du code lui-même. Merci à tous!
oui, la nouvelle fonctionnalité est fusionnée avant la fusion du didacticiel
Bonjour,
Je ne suis pas sûr que vous ayez implémenté avec succès la montonicité globale, d'après ce que j'ai vu dans votre code, cela correspond plus à une monotonie locale.
Voici un exemple simple qui brise la monotonie :
`
df <- data.frame(y = c(2,rep(6,100),1,rep(11,100)),
x1= c(rep(1,101),rep(2,101)),x2 = c(1,rep(2,100),1,rep(2,100)))
bibliothèque (xgboost)
set.seed(0)
XGB <- xgboost(data=data.matrix(df[,-1]),label=df[,1],
objective=" reg:linear ",
bag.fraction=1,nround=100,monotone_constraints=c(1,0),
êta=0.1 )
sans_corr <- data.frame(x1=c(1,2,1,2),x2=c(1,1,2,2))
sans_corr$prediction <- predict(XGB,data.matrix(sans_corr))
`
J'espère que ma compréhension de votre code et mon exemple n'est pas faux
Actuellement, cette fonctionnalité n'est pas dans l'API Sklearn. Pouvez-vous, ou quelqu'un d'autre, aider à l'ajouter ? Merci!
Est-il possible d'imposer une monotonie générale sur une variable, sans préciser si elle doit être croissante ou décroissante ?
@davidADSP, vous pouvez effectuer une vérification de corrélation de lanceur sur le prédicteur et la cible souhaités pour voir si l'augmentation ou la diminution est appropriée.
Cette fonctionnalité semble être invalide lorsque 'tree_method':'hist'. @tqchen de l'aide ? Merci a tous.
Comment fonctionne la contrainte pour un objectif multiclasse comme mllogloss ? La contrainte de monotonie est-elle prise en charge pour la perte multiclasse ? Si oui, comment est-il appliqué. (Comme pour chaque classe il y a un arbre)
Existe-t-il un livre blanc sur l'algorithme de monoticité appliqué dans XGBOOST ? Est-ce global ou local ? Local signifie spécifique à certains nœuds, mais des nœuds dans d'autres parties de l'arbre peuvent créer une violation de la monotonie globale. Quelqu'un peut-il également m'aider à comprendre la ligne L412-417 . Pourquoi "w" est délimité - supérieur et inférieur. Comment cela aide à maintenir la monotonie. Ligne 457 - Pourquoi utiliser « mid » ?
Commentaire le plus utile
Actuellement, cette fonctionnalité n'est pas dans l'API Sklearn. Pouvez-vous, ou quelqu'un d'autre, aider à l'ajouter ? Merci!