Scikit-learn: Ajustement d'estimateurs supplémentaires pour les méthodes d'ensemble

Créé le 16 janv. 2013  ·  73Commentaires  ·  Source: scikit-learn/scikit-learn

Je voudrais proposer une méthode d'instance supplémentaire aux estimateurs d'ensemble pour ajuster des sous-estimateurs supplémentaires. J'ai mis au point une implémentation pour l'amplification du gradient qui semble fonctionner pendant mes tests limités. Je pensais que la signature serait quelque chose comme

def fit_extend(self, X, y, n_estimators):

self.n_estimators += n_estimators est mis à jour comme tel. Je ne pense pas que fit_extend soit un nom particulièrement génial, donc j'accueillerais d'autres suggestions. Peut-être voudrions-nous hacher les fonctionnalités et les étiquettes lorsque fit() est appelé afin de pouvoir vérifier que les mêmes fonctionnalités et étiquettes sont fournies à cette fonction.

Si les gens pensent que ce serait un ajout utile, je serais prêt à créer un PR, il semble que cela devrait être simple à mettre en œuvre et à ajouter des tests/documents pour.

New Feature

Tous les 73 commentaires

C'est définitivement une fonctionnalité que nous voulons. La question est : quelle serait la meilleure façon de l'implémenter (en termes d'API) ?
Il y a quelque chose de légèrement similaire dans le pr adaboost : #522. Cela implémente la prédiction avec un sous-ensemble d'estimateurs, ce qui est également très utile.

À votre avis, à quoi ressemble le scénario/code, où un utilisateur veut fit_extend ? C'est probablement plus utile dans un cadre interactif, n'est-ce pas ?

Il existe une fonction légèrement liée dans SGD, partial_fit . C'est en fait pour l'apprentissage en ligne, donc il obtient des données différentes.

J'aimerais obtenir cette fonctionnalité en ajoutant le moins d'API et de noms possible ;)

Au fait, je ne hacherais pas X et y . Je ne vois aucune raison de forcer l'utilisateur à fournir les mêmes données d'entrée.

Je voudrais former un petit nombre de sous-estimateurs à la fois (et attendre relativement peu de temps). Ensuite, testez-le sur mon ensemble de validation croisée et si mon score de CV continue de baisser, je peux continuer à m'entraîner. Au lieu de former un grand nombre de sous-estimateurs et d'attendre longtemps (plusieurs heures pour moi). C'était ma motivation.

Je peux comprendre d'hésiter à ajouter une autre méthode d'instance. J'ai pensé qu'il pourrait être utile d'ajouter un autre paramètre optionnel à fit() mais j'ai vu cette citation sur la page de contribution.

les paramètres d'ajustement doivent être limités aux variables directement dépendantes des données

Je n'étais donc pas sûr que ce serait une bonne idée. Voudrais

def fit(self, X, y, n_estimators=self.n_estimators)

être acceptable ? Ensuite, si n_estimators > self.n_estimators , nous formerons alors autant d'estimateurs supplémentaires.

Je conviens que l'ajout du paramètre n_estimators à la méthode de prédiction est agréable, mais je pense que vous conviendrez que cela résout un problème différent. Pour mon problème, effectuer une recherche de grille sur n_estimators n'est pas vraiment une option car cela prend tellement de temps.

Jusqu'à ce que nous nous mettions d'accord sur une interface appropriée pour le faire, vous pouvez utiliser le hack suivant :

# Train a forest of 10 trees
clf1 = RandomForestClassifier(n_estimators=10)
clf1.fit(X, y)

# Train a second forest of 10 trees
clf2 = RandomForestClassifier(n_estimators=10)
clf2.fit(X, y)

# Extend clf1 with clf2
clf1.estimators_.extend(clf2.estimators_)
clf1.n_estimators += clf2.n_estimators

# clf1 now counts 20 trees

Notez que cela ne fonctionne que pour RandomForest et ExtraTrees. La même astuce ne peut pas être utilisée avec Gradient Boosting.

Voir #1626. L'arrêt anticipé serait-il une solution acceptable pour vous ?

@amueller Je partage le même avis que @glouppe ici https://github.com/scikit-learn/scikit-learn/issues/1626#issuecomment -12785168. J'aime arrêter tôt mais cela ne résout pas cela à mon avis.

D'accord. Ensuite, nous devrions rechercher une solution qui permette un arrêt précoce et l'ajout d'estimateurs supplémentaires.

En y réfléchissant un peu plus, je pense que la méthode partial_fit serait la bonne interface. Dans SGD, vous pouvez appeler partial_fit avec les mêmes données ou de nouvelles données et il continue d'apprendre. La différence est que dans SGD, si vous itérez manuellement sur des lots, vous obtenez l'algorithme d'origine. Pour les ensembles, ce ne serait pas vrai. Vous auriez besoin d'utiliser toutes les données à chaque appel à partial_fit .

En y réfléchissant un peu plus, je pense que la méthode partial_fit serait la bonne
interface.

J'aime cette suggestion. Qu'en pensent les autres ?

Juste pour clarifier, que se passerait-il exactement dans partial_fit en cas d'ensembles ? Cela ajouterait-il n_estimators plus d'estimateurs, où n_estimators est la valeur du paramètre du constructeur ? (ou pourrions-nous changer cette valeur ?)

Bonne question. J'y ai aussi pensé ;) en fait, vous voudriez changer ça, non ? vous pouvez changer cela par la suite par set_params mais cela semble gênant :-/

désolé d'avoir rejoint la discussion si tard.

Je suis d'accord que nous avons besoin d'une telle fonctionnalité, cependant, je ne sais pas si fit_extends est la meilleure solution au problème décrit par @jwkvam . Afin de faire un arrêt précoce, l'utilisateur doit écrire du code qui appelle essentiellement à plusieurs reprises fit_extends , puis vérifie l'erreur CV.

Je proposerais plutôt le paramètre d'ajustement monitor dont nous avons parlé dans le passé : est.fit(X, y, monitor=some_callable)some_callable sera appelé après chaque itération et recevra l'état complet de l'estimateur. L'appelable peut également renvoyer une valeur indiquant si la formation doit ou non se poursuivre.

En utilisant une telle API, on pourrait implémenter non seulement un arrêt précoce, mais également des rapports personnalisés (par exemple, un traçage interactif du score d'entraînement par rapport au test) et un instantané (toutes les itérations X vident l'objet estimateur et le copient à un emplacement; c'est génial si vous exécutez sur des instances ponctuelles EC2 ou sur d'autres matériels non fiables ;-)

Même avec une telle API monitor , cependant, je pense qu'il serait nécessaire qu'une API adapte plus d'estimateurs une fois que le modèle a été ajusté (c'est-à-dire fit_extends ) - souvent on forme un modèle et fait une introspection pour découvrir qu'il est probablement préférable d'avoir exécuté plus d'itérations - les estimateurs existants utilisent le paramètre warm_start pour implémenter une telle fonctionnalité (par exemple, voir linear_model.ElasticNet ) - voici la docstring du paramètre : :

warm_start : bool, optional
When set to True, reuse the solution of the previous call to fit as initialization, otherwise, just erase the previous solution.

Personnellement, je préférerais fit_extends (ou fit_more ) à warm_start - le démarrage à chaud est assez implicite - vous devez :

est = GradientBoostingRegressor(n_estimators=1000)
est.fit(X, y)

# now we want to fit more estimators to ``est`` 
# if you forget warm_start=True you nuke your previous estimators - quite implicit
est.fit(X, y, n_estimators=2000, warm_start=True)

# alternatively - more explicit
est.fit_more(X, y, n_estimators=1000)

alternativement - plus explicite

est.fit_more(X, y, n_estimators=1000)

Pour moi, fit_more correspond vraiment au partial_fit que nous avons dans
autres estimateurs.

@pprett Je pense qu'il devrait y avoir un moyen facile de faire des choses faciles. une API de surveillance est très flexible, mais en fait, vous voulez vous arrêter tôt chaque fois que vous utilisez un estimateur, n'est-ce pas ? Il ne devrait donc pas être nécessaire d'écrire un rappel pour le faire. De plus, il doit être compatible avec GridSearchCV.

Pour moi, fit_more correspond vraiment au partial_fit que nous avons dans
autres estimateurs.

Je ne pense pas. Dans partial_fit , "partial" signifie un accès partiel aux données : vous vous attendez à ce que les données ne tiennent pas en mémoire immédiatement, vous vous adaptez donc à un bloc à la fois et mettez à jour le modèle de manière incrémentielle tout en parcourant les données. .

Dans ce cas, nous souhaitons modifier le nombre de sous-estimateurs, mais nous souhaitons peut-être réutiliser exactement les mêmes données à chaque appel.

Pour une raison similaire, ElasticNet a un paramètre de constructeur warm_start au lieu d'une méthode partial_fit et SGDClassifier a un paramètre warm_start et une méthode partial_fit : ils servent différents fins.

Je suis d'accord que l'API de surveillance serait très utile en général (pour traiter l'instantané, l'arrêt précoce et autres) mais ne résoudrait pas le problème de l'augmentation du nombre de sous-estimateurs de manière interactive.

On pourrait aussi avoir :

est.fit(X, y, n_additional_estimators=1, warm_start=True)

Ou même de croître de 110 % (10 % d'estimateurs en plus) :

est.fit(X, y, additional_estimators=0.1, warm_start=True)

hum, je n'ai pas beaucoup regardé l'API de démarrage à chaud que nous avons actuellement. Il n'y a pas de documentation centrale pour cela, n'est-ce pas ?
Il faudrait vraiment réfléchir à l'organisation des docs. Nous avons reçu pas mal de commentaires à ce sujet dans l'enquête :-/

@ogrisel Je devrais jeter un œil à l'implémentation de SGD pour voir les détails, mais quelle est la différence dans ce qui se passe réellement entre les démarrages à chaud et partial_fit ? Je pense que nous sommes d'accord sur le point de données identiques / changeantes.
Est-ce que warm_start fait plusieurs époques et que partial_fit ne le fait pas ? Cela aurait du sens pour moi, et nous devrions probablement les garder séparés.
Si nous avons déjà l'API de démarrage à chaud, nous devrions certainement "juste" l'implémenter pour les estimateurs d'ensemble.

warm_start empêche juste d'oublier l'état précédent (en supposant que l'état interne du modèle le fera probablement converger plus rapidement vers la solution du nouvel appel avec le nouvel hyperparamètre).

2013/1/30 Andreas Mueller [email protected]

@ogrisel https://github.com/ogrisel faudrait que je regarde le SGD
mise en œuvre pour voir les détails, mais quelle est la différence dans ce que
se passe-t-il réellement entre les démarrages à chaud et l'ajustement partiel ? Je pense que nous sommes d'accord sur
le point de données identiques / changeantes.
warm_start fait-il plusieurs époques et partial_fit non ? Qui serait
sens pour moi, et nous devrions probablement les garder séparés.
Si nous avons déjà l'API de démarrage à chaud, nous devrions certainement "juste"
implémenter cela pour les estimateurs d'ensemble.

Je pense que la principale différence est la _sémantique_ : l'idée principale derrière
warm_start est de converger plus rapidement - mais quelle que soit la valeur
warm_start vous donne la même solution !
L'ajustement partiel, en revanche, modifie le modèle sous-jacent. Prendre en compte
exemple suivant :

# this is the intended use-case for warm_start is faster convergence

clf = SGDClassifier(n_epochs=10)
clf.fit(X, y)

clf2 = clone(clf)
clf3 = SGDClassifier(n_epochs=10)

clf2.fit(X, y, warm_start=True)
clf3.fit(X, y)

# clf2 and clf3 should converge to the same solution - but since clf3

peut réutiliser les poids ajustés de clf, il pourrait converger plus rapidement
# sous le capot SGDClassifier.fit réinitialise le mode d'état "entraînement"
l'estimateur (taux d'apprentissage adaptatif pour sgd)

# now partial fit
clf = SGDClassifier(n_epochs=10)
clf.partial_fit(X, y, classes)
# training has not completed yet "training" state (adaptive learning rate) is stored.

clf.partial_fit(X, y)  # resume with previous learning rate

Avis de non-responsabilité : cet exemple est peut-être pédant, car les différences de termes
des poids appris est minime - mais conceptuellement, ils sont à mon humble avis totalement
différentes choses...


Répondez directement à cet e-mail ou consultez-le sur Gi tHubhttps://github.com/scikit-learn/scikit-learn/issues/1585#issuecomment -12883146.

Pierre Prettenhofer

L'API warm_start a été initialement introduite pour permettre un calcul plus rapide d'une série de modèles linéaires identiques lors de l'utilisation d'un chemin de régularisateurs alpha . Ceci est quelque peu similaire à l'augmentation itérative du nombre de sous-estimateurs dans un modèle d'ensemble boosté afin que nous puissions décider de réutiliser warm_start pour traiter également ce cas d'utilisation, mais si cette API se révèle fastidieuse pour les modèles boostés, cela pourrait être mieux à repenser maintenant que nous avons un cas d'utilisation supplémentaire.

Je suis d'accord avec l'analyse de @pprett .

Je ne sais pas quoi penser de l'analyse de @pprett .

Dans le cas des modèles linéaires, l'estimateur convergera vers le même résultat, même lorsque le démarrage à chaud obtient des données différentes de celles de l'ajustement d'origine. Si nous "démarrions à chaud" des ensembles/arbres, ce ne serait pas le cas.
Nous pourrions essayer de nous assurer que les données fournies lors du démarrage à chaud sont les mêmes que celles d'origine.

À l'heure actuelle, le "démarrage à chaud" fait référence à une procédure d'optimisation, ce qui n'existe pas dans les méthodes basées sur les arbres.
Tandis que partial fit conserve tout l'état de l'estimateur et continue à s'ajuster.

D'autre part, les appels ultérieurs à l'ajustement partiel sur les lots conduisent au même modèle que l'entraînement sur l'ensemble des données.

Encore une fois, ceci est différent du cas arbre/ensemble. Je pense que cela revient à mon argument selon lequel il s'agit plus d'un algorithme de chemin qu'autre chose;)

Je vois donc deux solutions possibles : assurez-vous que le démarrage à chaud est toujours appelé avec les mêmes données, puis ajouter des estimateurs serait un démarrage à chaud.
Sinon, nous avons besoin d'une troisième façon de réajuster un modèle donné.
Où sont les docs pour ça actuellement, btw ;)

assurez-vous que le démarrage à chaud est toujours appelé avec les mêmes données.

Pourquoi donc? Laissez l'utilisateur décider comment et pourquoi il veut utiliser warm_start .

Où sont les docs pour ça actuellement, btw ;)

http://scikit-learn.org/dev/modules/generated/sklearn.linear_model.ElasticNet.html

warm_start : bool, optional
When set to True, reuse the solution of the previous call to fit as initialization, otherwise, just erase the previous solution.

Je suis d'accord que donner la motivation serait utile, par exemple dans ce cas :

"Ceci est utile pour calculer efficacement un chemin de régularisation des modèles ElasticNet comme le fait la fonction :func: enet_path ".

Je pensais que l'argument portait sur la sémantique. Je pense qu'une sémantique est définie en donnant à l'utilisateur une certaine garantie de ce qui va se passer. De cette façon, l'utilisateur n'a pas besoin de connaître tous les détails de l'algorithme.
Je pensais que la garantie de warm_start était "warm_start ne change pas le résultat", tandis que la garantie de partial_fit était "l'itération sur les lots ne change pas le résultat".

S'il n'y a pas de garantie, alors je ne vois pas comment il peut y avoir une sémantique commune.

Nous garantissons à l'utilisateur que s'il fournit à nouveau les mêmes données avec warm_start=true, il obtiendra les mêmes résultats (juste plus rapidement). Mais nous ne devons pas empêcher l'utilisateur d'utiliser des données différentes s'il fait une supposition éclairée qu'un démarrage à chaud sur les nouvelles données l'aidera à résoudre son problème (par exemple, résoudre plus rapidement les nouvelles données s'il fait l'hypothèse que les nouvelles données sont distribuées raisonnablement de la même manière que les premières données et donc démarrer l'optimiseur à partir de la position précédente devrait accélérer les choses).

Pour les estimateurs linéaires, c'est correct. Mais si vous voulez utiliser warm_start sur des ensembles, cela aura soudainement une sémantique très différente.

En effet, développer un ensemble boosté sur des données changeantes est étrange et probablement inutile (à moins que ce ne soit un moyen d'injecter une randomisation pour un estimateur de méta-méta-ensemble qui fait peut-être un sac sur des modèles boostés?). Je ne pense pas que nous devrions essayer d'imposer que les données ne changent pas d'un appel à l'autre. Documentons plutôt le scénario d'utilisation attendu pour cette option dans la docstring.

d'accord. Donc, fondamentalement, la docstring devrait dire "utiliser warm_start avec les mêmes données à moins que vous ne sachiez exactement ce que vous faites".
Ça va pour moi. Quelqu'un s'oppose-t-il à l'utilisation warm_start ?

Je dois encore voir comment cela est géré dans SGD et ENet, cependant ...

En effet, un ensemble croissant sur des données changeantes est bizarre et probablement inutile

Non, pas inutile : c'est une stratégie de sous-échantillonnage spécifique. La pratique
la différence avec une méthode en ligne est que vous voulez que le lot soit important.

Le 30/01/2013 à 11h42, Peter Prettenhofer a écrit :

Je pense que la principale différence est la _sémantique_ : l'idée principale derrière
warm_start est de converger plus rapidement - mais quelle que soit la valeur
warm_start vous donne la même solution !
En y réfléchissant, c'est faux.
Dans SGDClassifier, si vous rentrez deux fois avec warm_start vous allez certainement
obtenir des solutions différentes. Selon ce que vous avez fait auparavant, vous pourriez obtenir
des solutions meilleures ou pires, mais le temps de formation sera exactement le même.

Ainsi, warm_start dans SGDClassifier ne peut pas être utilisé pour la sélection de modèle.
D'autre part, partial_fit pourrait être utilisé pour trouver le meilleur
max_iter .

Plus j'y pense, plus c'est confus pour moi :-/

Btw, y a-t-il une raison pour laquelle warm_start est un paramètre init
et partial_fit est une fonction ?
Ne serait-il pas plus facile si partial_fit était également un paramètre d'initialisation ?

Btw, y a-t-il une raison pour laquelle warm_start est un paramètre init
et partial_fit est une fonction ?

Parce que partial_fit est une stratégie spécifique qui peut différer de la
stratégie utilisée dans fit .

Ne serait-il pas plus facile si partial_fit était également un paramètre d'initialisation ?

Je pense que ce serait déroutant. L'objectif de partial_fit est d'être un
bloc de construction utilisable dans un framework out-of-core. Utiliser fit pour cela
objectif pourrait conduire à des résultats assez catastrophiques.

Je ne comprends pas votre argumentation. Ce que fait fit est essentiellement "oublier le modèle, appeler partial_fit ".

Hm peut-être que ce que vous voulez dire, c'est que fit pourrait avoir besoin de faire moins de travail que partial_fit parce que partial_fit a besoin de stocker les "statistiques suffisantes" des données précédentes et que l'ajustement ne le fait pas besoin de faire ça?

Je ne comprends pas votre argumentation. Ce que fait l'ajustement, c'est essentiellement "oublier le modèle,
appeler partial_fit".

Il peut faire plus. Typiquement, il mélange les données avant d'appeler partial
ajuster. Il peut également le diviser en mini-lots d'une taille sélectionnable par l'utilisateur.

Hm peut-être que ce que vous voulez dire, c'est que fit pourrait avoir besoin de faire moins de travail que partial_fit
parce que partial_fit doit stocker les "statistiques suffisantes" du précédent
data and fit n'a pas besoin de faire ça ?

C'est peut-être le cas. Il se peut aussi que fit doive faire plus
travailler pour transformer un grand ensemble de données par lots en un ensemble de mini-lots.

hm ok peut-être que ce n'est pas si important en ce moment.

J'aimerais minimiser le nombre de mécanismes que nous avons dans sklearn, et nous en avons certainement besoin d'un (plus?) Pour une sélection de modèle efficace. Dans les algorithmes décents de coordonnées, l'option warm_start a été introduite exactement dans ce but. Je ne suis pas sûr que ce soit assez général pour vraiment le faire (et s'il y a plus d'un paramètre ?) et cela ne remplit plus cette exigence dans SGDClassifier.

(je viens de supprimer une grande partie du commentaire précédent car je me répétais).

J'aimerais minimiser le nombre de mécanismes que nous avons dans sklearn, et nous en avons certainement besoin d'un (plus?) Pour une sélection de modèle efficace. Dans les algorithmes décents de coordonnées, l'option warm_start a été introduite exactement dans ce but. Je ne suis pas sûr que ce soit assez général pour vraiment le faire (et s'il y a plus d'un paramètre ?) et cela ne remplit plus cette exigence dans SGDClassifier.

Je ne comprends pas cette dernière remarque. warm_start est parfaitement valide pour SGDClassifier (en plus de partial_fit ) : pour le moment, SGDClassifier n'a pas de contrôle de convergence / d'arrêt anticipé. Mais dès qu'il l'aura fait, warm_starting permettra de calculer plus rapidement le chemin de régularisation, exactement comme pour ElasticNet.

SGDClassifier effectue n_iter époques de mises à jour, puis s'arrête. L'endroit où il se retrouve après les étapes n_iter dépend fortement de l'endroit où vous avez commencé.
Même si vous effectuez un "arrêt précoce", il s'agirait d'un arrêt précoce sur l'ensemble de validation, et non d'un arrêt précoce de l'optimisation. SGDClassfier n'a pas pour but d'optimiser complètement l'objectif jusqu'au bout. Donc, où vous vous retrouverez dépendra de l'initialisation.
En particulier, pour un arrêt précoce (sur un ensemble de validation !), il pourrait être préférable de faire moins d'itérations, ce qui réduit le biais.

En particulier, je ne pense pas qu'un "chemin de régularisation pour alpha" ait un sens dans le paramètre SGD. Le "chemin" est une suite d'optima. SGD ne trouvera jamais l'optimum, donc les endroits où vous vous retrouverez dépendront probablement autant de la mise à l'échelle du taux d'apprentissage que de la régularisation réelle.

En particulier, je ne pense pas qu'un "chemin de régularisation pour alpha" ait un sens dans le paramètre SGD. Le "chemin" est une suite d'optima. SGD ne trouvera jamais l'optimum,

Pour les modèles linéaires, le problème est convexe. Si n_iter est suffisamment grand, SGD avec un bon calendrier d'apprentissage convergera vers l'optimum (si vous ne vous arrêtez pas avant la convergence). La vitesse de convergence lorsqu'on se rapproche de l'optimum n'est tout simplement pas aussi bonne que la descente de coordonnées, mais c'est un problème différent.

nous sommes donc d'accord : les modèles seront différents à moins que n_iter soit suffisamment grand et que le calendrier soit juste - ce qui est peu probable dans la pratique.

de plus, une garantie sous la forme "les résultats seront les mêmes si les autres paramètres sont correctement réglés" ne ressemble pas vraiment à une garantie.

Olivier Grisel [email protected] écrit :

En particulier, je ne pense pas qu'un "chemin de régularisation pour alpha" fasse
sens dans le paramètre SGD. Le "chemin" est une suite d'optima. SGD va
ne jamais trouver l'optimum,

Pour les modèles linéaires, le problème est convexe. Si n_iter est assez grand, SGD
avec un bon calendrier d'apprentissage convergera vers l'optimum (si vous
ne pas s'arrêter avant la convergence). La vitesse de convergence lors de l'obtention
plus proche de l'optimum n'est tout simplement pas aussi bon que la descente de coordonnées mais
c'est un problème différent.


Répondez directement à cet e-mail ou consultez-le sur GitHub :
https://github.com/scikit-learn/scikit-learn/issues/1585#issuecomment -12958852

Ce n'est pas le cas de mon Android-Mobiltelefon mit K-9 Mail gesendet.

Alors qu'en est-il

clf = RandomForestClassifier(n_estimators=10)
clf.fit(X, y)
print(clf.score(X, y))
clf.set_params(warm_start=true, n_estimators=20)
clf.fit(X, y)

Est-ce un modèle d'utilisation acceptable ?

Ou voulez-vous que ces paramètres soient fit ? Dans SGD, warm_start est un paramètre __init__ selon la documentation.

Relançons la discussion. dans #1044 @GaelVaroquaux dit qu'il préfère toujours partial_fit .
Actuellement, je pense que warm_start est plus dans la bonne direction, mais je n'ai pas d'opinion bien arrêtée. @ogrisel @pprett @glouppe @larsmans quel est votre avis sur le schéma d'utilisation que j'ai posté plus haut ? Ou aimeriez-vous avoir une autre interface utilisant warm_start ou partial_fit ?

Actuellement, je pense que warm_start est plus dans la bonne direction, mais je n'ai pas
un avis fort.

Ce que je n'aime pas dans l'utilisation de 'warm_start', c'est qu'actuellement le
contrat avec les estimateurs scikit-learn est que vous pouvez appeler "fit" et obtenir
une réponse valide/utile quel que soit l'historique de l'objet. Ça peut aller
plus rapide ou plus lent, mais c'est un peu infaillible. Si vous passez différents
données à un estimateur d'ensemble, et utilisez le 'warm_start' pour ajuster plus
estimateurs, vous obtiendrez des absurdités. J'ai peur de devoir écrire
code "défensif" pour éviter de tels problèmes.

comment partial_fit fonctionnerait-il dans notre cadre - est-ce correct ::

est = GradientBoostingRegressor(n_estimators=1000)
est.fit(X, y)
...
est.fit_partial(X, y, n_estimators=1000)  # train another 1000

il faudrait donc arbitrairement fit_params ou juste n_estimators ?

Personnellement, je suis en faveur d'un fit_more car le cas d'utilisation que nos partial_fit servent actuellement est assez différent et fit_more est plus explicite.

Je ne suis pas non plus très satisfait du nom partial_fit en cas d'ensembles. De mon point de vue, ce nom suggère qu'il construira des estimateurs à partir du nombre total demandé dans le constructeur, mais pas plus.

Si nous optons pour warm_start , quelle serait la spécification ? Vous définissez n_estimators dans le constructeur et appelez fit ajoutez n_estimators plus d'estimateurs ? Tout comme @amueller l'a fait ci-dessus ? Bon je ne suis pas contre ce schéma, mais ça ne me semble pas très intuitif quand même.

D'un point de vue très pratique, j'aime bien fit_more . C'est explicite. Aucune explication requise. Cependant, cela ajoute une autre fonction à notre API...

(Je n'ai pas encore d'opinion tranchée, ces remarques reflètent simplement ce que je pense en ce moment)

Je ne suis pas complètement contre l'ajout d'une fonction, mais je ne voudrais pas qu'elle soit trop spécifique aux ensembles.
Je vois vraiment un lien avec les algorithmes de chemin, donc je pense que partager une interface serait bien.

Considérez la situation hypothétique suivante (peut-être pas si réaliste) :
Vous avez adapté un ensemble mais maintenant vous voyez que vous êtes sous-équipé et que vous voulez rendre vos arbres plus profonds (disons que nous l'avons implémenté). Ce serait un autre exemple de comportement de type chemin. Le feriez-vous également via fit_more ? Ou ajouter une fonction fit_deeper ?

Je suppose qu'il y a un compromis entre la généralité et l'explicitation.

@GaelVaroquaux Le contrat avec partial_fit est à mon humble avis que si vous parcourez les données par lots, vous obtiendrez le même résultat. Ce ne sera certainement pas le cas s'il est utilisé ici. Donc à dessein on romprait le contrat ?!

En y repensant, il y a peut-être de la place pour une nouvelle méthode que nous pourrions utiliser pour implémenter #1626.
Cela ne me dérangerait pas de l'appeler fit_more , mais dans le sens de do some more fitting along the parameter path pas dans le sens de fit additional estimators in the ensemble .

Donc, à mon humble avis, nous devrions soit faire warm_start (+ peut-être une programmation défensive), soit ajouter une autre méthode que nous pouvons généralement utiliser pour nous adapter à un chemin de paramètres.

Est-ce que fit_more serait alors défensif ou non ? ;)

-1 en défensive. Je préfère bien documenter et laisser l'utilisateur décider ce
est bon pour soi.

Le 11 février 2013 à 22h14, Andreas Mueller [email protected] a écrit :

Fit_more serait-il alors défensif ou non ? ;)


Répondez directement à cet e-mail ou consultez-le sur Gi tHubhttps://github.com/scikit-learn/scikit-learn/issues/1585#issuecomment -13403244.

Je serais aussi contre la défensive. Je me demandais simplement si l'ajout de la fonction résolvait vraiment un problème ou si nous ajoutions simplement une autre façon de faire des démarrages à chaud. Les deux ont le même problème défensif / non défensif, n'est-ce pas ?

Mes excuses si je ne fais que répéter ce qui a déjà été dit. Mais il semble que vous pourriez diviser les estimateurs en deux classes : ceux qui gèlent les paramètres une fois qu'ils sont ajustés (ensembles, DT) et ceux qui ne le font pas (modèles linéaires). J'entends par là qu'avec warm_start, vous ne réajusterez pas les n premiers sous-estimateurs d'un ensemble ou les divisions existantes dans un arbre de décision. Le fait de ne pas pouvoir atteindre n'importe où dans l'espace des paramètres avec warm_start pour les ensembles et les DT me fait penser qu'une méthode d'instance serait plus appropriée.

Si une méthode d'instance est choisie, doit-elle être plus générale comme l'a noté @amueller ? Si, à un moment donné, quelqu'un voulait la possibilité d'augmenter le max_depth des sous-estimateurs, cela pourrait également être géré avec fit_more() ?

Pour ce que ça vaut, je serais aussi contre défensive. Comme @GaelVaroquaux l' a souligné plus tôt, il fournit une stratégie de sous-échantillonnage, par exemple, si vos données d'entraînement ne tiennent pas dans la mémoire principale.

Après quelques réflexions, je pense que nous devrions voir la situation dans son ensemble. Dans un futur proche, j'aimerais implémenter des méta-ensembles génériques qui pourraient combiner n'importe quel type d'estimateurs ensemble. Ce que je vois plutôt, c'est un mécanisme de "combinaison" qui prendrait en entrée une liste d'estimateurs (ajustés) et produirait un méta-estimateur les combinant tous.

En pratique, je pense que nous pouvons y parvenir sans ajouter de nouvelle fonction à notre API. Par exemple, on pourrait simplement transmettre une telle liste d'estimateurs ajustés au constructeur du méta-ensemble.

En termes d'API, on pourrait (en gros) implémenter de tels ensembles de la manière suivante :

a) Ensachage :

  • constructeur : base_estimator (facultatif), n_estimators (>=0), une liste L d'estimateurs ajustés (facultatif).
  • fit : étendez L avec n_estimators nouvelles instances de base_estimators ajustées sur (copies d'amorçage de) les échantillons d'apprentissage. Si aucun estimateur de base n'est donné, cela équivaut à combiner les estimateurs dans L .

b) Empilage :

  • constructeur : base_estimator (facultatif), n_estimators (>=0), une liste L d'estimateurs ajustés (facultatif).
  • ajustement : étendre L avec n_estimators nouvelles instances de base_estimators ajustées d'échantillons bootstrap, puis réajuster un modèle sur les prédictions des estimateurs.

c) Forêt :

  • constructeur : base_estimator (facultatif), n_estimators (>=0), une liste L d'estimateurs ajustés ou une forêt (facultatif).
  • fit : étendez L avec n_estimators nouvelles instances de base_estimators ajustées sur les échantillons d'apprentissage. Ici, nous pourrions également vérifier si les estimateurs dans L sont des forêts ou des arbres de décision. Les forêts seraient aplanies afin de mettre tous les arbres au même niveau.

Aussi, dans un tel cadre, le calcul d'un ensemble pourrait facilement être réparti sur plusieurs machines : construisez vos estimateurs ; marinez-les; puis les recombiner en un seul méta-estimateur. On pourrait même envelopper cette interface dans un cluster MapReduce, sans creuser du tout dans notre implémentation !

Qu'est-ce que tu penses? Je suis conscient que cela ne concerne que certains types d'ensembles. Par exemple, GBRT et AdaBoost sont (à mon avis) plus adaptés à warm_restart ou partial_fit .

Juste pour être clair, pour étendre une forêt, on ferait quelque chose comme :

forest = RandomForestClassifier(n_estimators=100)
forest.fit()
forest_extended = RandomForestClassifier(n_estimators=100, L=forest)
forest_extended.fit() # now counts 200 trees

Quelle est la motivation de cette interface ? Je suis totalement d'accord avec vous pour soutenir davantage de méthodes d'ensemble. Je pense juste qu'il est assez gênant d'avoir une interface différente pour GBRT et la forêt aléatoire. Je ne vois pas vraiment la motivation pour cela.

Si la principale motivation est de distribuer des tâches parallèles embarrassantes, alors je pense que nous devrions nous attaquer à cela en mettant en œuvre une parallélisation plus puissante. Le faire comme vous l'avez décrit semble assez manuel et hacky.

Fondamentalement, je pense que votre proposition ne résout qu'un cas très particulier et laisse la plupart des cas non résolus.

Bon ok... J'ai juste l'impression que l'extension d'ensembles de type boosté et d'ensembles de type moyen sont des choses assez différentes.

Quel est le cas d'utilisation de votre interface à part la parallélisation ? Ou mieux : dans quels cas d'utilisation avez-vous besoin d'une interface différente pour les ensembles boostés et le bagging ?

Le cas d'utilisation est lorsque vous souhaitez combiner plusieurs estimateurs ensemble. Il est naturel pour les ensembles de type moyen, mais n'a aucun sens dans les ensembles boostés. Dans cette perspective, je vois "étendre un estimateur" comme le "combiner" avec plus d'estimateurs de base.

Donc, le contexte est que vous avez formé des estimateurs d'ensachage et que vous souhaitez les combiner, n'est-ce pas ?
Dans quel cadre voulez-vous faire cela, à part la parallélisation ? Ce n'est pas si clair pour moi mais peut-être que j'oublie quelque chose d'évident.

En cas d'empilement, les estimateurs peuvent être complètement différents (disons que vous voulez fusionner des forêts avec svms).

(Indirectement, cela pourrait également être utilisé pour mettre en œuvre des stratégies de sous-échantillonnage ou pour surveiller le processus d'ajustement.)

Je ne suis pas sûr de comprendre l'exemple d'empilement. J'aurais imaginé que si nous avions une interface d'empilement, vous pourriez spécifier un estimateur comme estimateur de base et un autre comme celui du dessus.

Selon moi, l'intérêt de l'empilement est de combiner les prédictions d'estimateurs de nature différente. Plus ils sont variés, souvent mieux c'est.

Ok, donc les estimateurs de base seraient différents. Mais nous pourrions également intégrer cela dans l'interface d'empilement, n'est-ce pas ?

Résolu avec #2570

@jwkvam Nous avons récemment convenu dans # 2570 d'implémenter cette fonctionnalité en utilisant le paramètre warm_start . Il est maintenant implémenté dans GBRT. Je vais essayer de mettre à jour les forêts avec le même mécanisme avant la sortie.

@glouppe Tu as raison, j'avais oublié que j'avais écrit ça pour n'importe quel ensemble. Mais vraiment, je le voulais juste pour GBRT :) donc dans ma hâte, j'ai décidé que ce problème était résolu. Si vous le souhaitez, vous pouvez le rouvrir et le fermer lorsque vous avez terminé, cela m'est égal.

Cette page vous a été utile?
0 / 5 - 0 notes