Optimierung von Hyperparametern in Vowpal Wabbit mit dem neuen vw-hyperopt-Modul

    Hallo Habr! Dieser Artikel konzentriert sich auf einen nicht sehr angenehmen Aspekt des maschinellen Lernens wie die Optimierung von Hyperparametern. Vor zwei Wochen wurde das Modul vw-hyperopt.py zu einem sehr bekannten und nützlichen Vowpal Wabbit- Projekt hinzugefügt , das gute Konfigurationen von Hyperparametern von Vowpal Wabbit-Modellen in großdimensionalen Räumen finden kann. Das Modul wurde intern von der DCA (Data-Centric Alliance) entwickelt.


    Для поиска хороших конфигураций vw-hyperopt использует алгоритмы из питоновской библиотеки Hyperopt и может оптимизировать гиперпараметры адаптивно с помощью метода Tree-Structured Parzen Estimators (TPE). Это позволяет находить лучшие оптимумы, чем простой grid search, при равном количестве итераций.

    Эта статья будет интересна всем, кто имеет дело с Vowpal Wabbit, и особенно тем, кто досадовал на отсутствие в исходном коде способов тюнинга многочисленных ручек моделей, и либо тюнил их вручную, либо кодил оптимизацию самостоятельно.

    Гиперпараметры


    Was sind Hyperparameter? Dies sind alles „Freiheitsgrade“ des Algorithmus, die er nicht direkt optimiert, von denen das Ergebnis abhängt. Manchmal hängt das Ergebnis ziemlich stark ab. Wenn es sich nicht um ein Kaggle handelt, können Sie mit Standardwerten auskommen oder es manuell abrufen. Aber manchmal kann eine erfolglose Konfiguration alles ruinieren: Der Algorithmus trainiert entweder stark neu oder kann umgekehrt die meisten Informationen nicht verwenden.

    Im engeren Sinne bedeuten Hyperparameter oft nur Regularisierung und andere „offensichtliche“ Einstellungen von Methoden des maschinellen Lernens. Im weitesten Sinne sind Hyperparameter jedoch im Allgemeinen alle Manipulationen mit Daten, die das Ergebnis beeinflussen können: technische Merkmale, Gewichtungsbeobachtungen, Unterabtastung usw.

    Rastersuche


    Natürlich wäre es schön, einen Algorithmus zu haben, der neben der Optimierung von Parametern auch Hyperparameter optimiert. Noch besser, wenn wir diesem Algorithmus mehr vertrauen könnten als der Intuition. Natürlich wurden einige Schritte in diese Richtung unternommen. Naive Methoden sind in viele Bibliotheken für maschinelles Lernen integriert: Rastersuche - Durchlaufen des Rasters oder zufällige Suche - Stichprobenpunkte aus einer festen Verteilung (die bekanntesten Instanzen sind GridSearchCV und RandomizedGridSearchCV in sklearn). Der Vorteil eines Grid-Passes besteht darin, dass er leicht selbst zu codieren und leicht zu parallelisieren ist. Es hat jedoch auch schwerwiegende Nachteile:

    • Er geht viele offensichtlich erfolglose Punkte durch. Angenommen, Sie haben bereits einige Konfigurationen mit Ergebnissen oder anderen Informationen. Eine Person kann verstehen, welche Konfigurationen definitiv zu einem schlampigen Ergebnis führen, und raten, diese Regionen nicht erneut zu überprüfen. Die Rastersuche kann dies nicht.

    • Wenn es viele Hyperparameter gibt, muss die Größe der „Zelle“ zu groß gemacht werden, und ein gutes Optimum kann übersehen werden. Wenn Sie also viele zusätzliche Hyperparameter in den Suchbereich aufnehmen, die das Ergebnis nicht beeinflussen, funktioniert die Rastersuche bei gleicher Anzahl von Iterationen viel schlechter. Für die zufällige Suche gilt dies jedoch in geringerem Maße:


    Bayesianische Methoden


    Um die Anzahl der Iterationen zu verringern, um eine gute Konfiguration zu finden, wurden adaptive Bayes'sche Methoden erfunden. Sie wählen den nächsten zu überprüfenden Punkt aus und berücksichtigen dabei die Ergebnisse der bereits überprüften Punkte. Die Idee ist, bei jedem Schritt einen Kompromiss zu finden zwischen (a) Erkundung von Regionen in der Nähe der erfolgreichsten Punkte unter den gefundenen und (b) Erkundung von Regionen mit großer Unsicherheit, in denen sich möglicherweise noch erfolgreichere Punkte befinden. Dies wird oft als Explorations-Exploit oder Lernen gegen Verdienst-Dilemma bezeichnet. In Situationen, in denen die Überprüfung jedes neuen Punkts teuer ist (bei der Überprüfung des maschinellen Lernens = Training + Validierung), können Sie sich dem globalen Optimum in einer viel geringeren Anzahl von Schritten nähern.

    Ähnliche Algorithmen in verschiedenen Variationen sind in MOE- Tools implementiert .Grüne Minze , SMAC , BayesOpt und Hyperopt . Wir werden uns näher mit letzterem vw-hyperoptbefassen , da dies ein Wrapper über Hyperopt ist, aber zuerst müssen Sie ein wenig über Vowpal Wabbit schreiben.

    Vowpal Wabbit


    Viele von Ihnen müssen dieses Tool verwendet haben oder zumindest davon gehört haben. Kurz gesagt, dies ist eine der schnellsten (wenn nicht die schnellsten) Bibliotheken für maschinelles Lernen der Welt. Das Trainieren eines Modells für unseren CTR-Prädiktor (binäre Klassifizierung) in 30 Millionen Fällen und zig Millionen von Funktionen erfordert nur wenige Gigabyte RAM und 6 Minuten auf einem Kern. Vowpal Wabbit implementiert mehrere Online-Algorithmen:

    • Stochastischer Gefälleabstieg mit verschiedenen Schnickschnack;
    • FTRL-Proximal, über das hier gelesen werden kann ;
    • Online-Ähnlichkeit von SVM;
    • Online-Boosting;
    • Faktorisierungsmaschinen.

    Darüber hinaus werden Feed-Forward-Neuronale Netze, Batch-Optimierung (BFGS) und LDA implementiert. Sie können Vowpal Wabbit im Hintergrund ausführen und einen Datenstrom als Eingabe erhalten, indem Sie entweder daraus lernen oder einfach Vorhersagen treffen.

    FTRL und SGD können sowohl Regressions- als auch Klassifizierungsprobleme lösen, dies wird nur durch die Verlustfunktion reguliert. Diese Algorithmen sind in Bezug auf Merkmale linear, aber eine Nichtlinearität kann leicht unter Verwendung von Polynommerkmalen erreicht werden. Es gibt einen sehr nützlichen Frühstoppmechanismus, um sich vor Umschulungen zu schützen, wenn Sie zu viele Epochen angegeben haben.

    Vowpal Wabbit ist auch bekannt für sein Feature-Hashing, das bei vielen Features als zusätzliche Regularisierung dient. Dank dessen ist es möglich, kategoriale Merkmale mit Milliarden seltener Kategorien zu lernen und das Modell in den Arbeitsspeicher einzubauen, ohne die Qualität zu beeinträchtigen.

    Vowpal Wabbit erfordert ein spezielles Eingabedatenformat , das jedoch leicht zu verstehen ist. Es ist von Natur aus spärlich und nimmt wenig Platz ein. Es wird jeweils nur eine Beobachtung (oder mehrere für LDA) in den RAM geladen. Das Training ist am einfachsten über die Konsole durchzuführen.

    Interessenten können das Tutorial und andere Beispiele und Artikel in ihrem Repository sowie eine Präsentation lesen. Über die Innenseiten von Vowpal Wabbit finden Sie ausführlich in den Veröffentlichungen von John Langford und in seinem Blog . Es gibt auch einen geeigneten Beitrag auf Habré . Eine Liste von Argumenten kann durch vw --helpeine detaillierte Beschreibung erhalten oder gelesen werden . Wie aus der Beschreibung hervorgeht, gibt es Dutzende von Argumenten, von denen viele als optimierte Hyperparameter betrachtet werden können.

    Vowpal Wabbit verfügt über ein vw-Hypersearch- Modul , mit dem ein Hyperparameter mithilfe der Golden- Ratio- Methode ausgewählt werden kann. Wenn es jedoch mehrere lokale Minima gibt, ist diese Methode wahrscheinlich weit von der besten Option entfernt. Darüber hinaus müssen Sie häufig viele Hyperparameter gleichzeitig optimieren, was bei vw-hypersearch nicht der Fall ist. Vor ein paar Monaten habe ich versucht, eine mehrdimensionale Golden-Section-Methode zu schreiben, aber die Anzahl der Schritte, die er für die Konvergenz benötigte, überstieg jede Rastersuche, sodass diese Option nicht mehr benötigt wurde. Es wurde beschlossen, Hyperopt zu verwenden.

    Hyperopt


    Diese Python-Bibliothek implementiert den Optimierungsalgorithmus TPE (Tree-Structured Parzen Estimators). Sein Vorteil ist, dass es mit sehr „ungeschickten“ Räumen arbeiten kann: Wenn ein Hyperparameter stetig ist, ist der andere kategorisch; der dritte ist diskret, aber seine benachbarten Werte sind miteinander korreliert; Schließlich sind einige Kombinationen von Parameterwerten möglicherweise einfach nicht sinnvoll. TPE nimmt einen hierarchischen Suchraum mit a priori Wahrscheinlichkeiten auf und mischt sie bei jedem Schritt mit einer Gaußschen Verteilung, die an einem neuen Punkt zentriert ist. Sein Autor James Bergstra behauptet, dass dieser Algorithmus das Explore-Exploit-Problem recht gut löst und sowohl für die Rastersuche als auch für die Expertensuche besser funktioniert, zumindest für Deep-Learning-Aufgaben, bei denen es viele Hyperparameter gibt.hier und hier . Über den TPE-Algorithmus können Sie hier lesen . Vielleicht wird es in Zukunft möglich sein, einen ausführlichen Beitrag über ihn zu schreiben.

    Obwohl Hyperopt nicht in den Quellcode bekannter Bibliotheken für maschinelles Lernen eingebettet war, wird es von vielen verwendet. Hier ist zum Beispiel ein großartiges Tutorial zu hyperopt + sklearn . Hier ist die Anwendung von Hyperopt + xgboost. Mein ganzer Beitrag ist dieser ähnliche Wrapper für Vowpal Wabbit, eine mehr oder weniger tolerierbare Syntax zum Festlegen des Suchraums und zum Starten all dessen über die Befehlszeile. Da Vowpal Wabbit noch keine solche Funktionalität hatte, mochte Langford mein Modul und schenkte es ein. Tatsächlich kann jeder Hyperopt für sein bevorzugtes Werkzeug für maschinelles Lernen ausprobieren: Es ist einfach und alles, was Sie brauchen, finden Sie in diesem Tutorial .

    vw-hyperopt


    Fahren wir mit der Verwendung des Moduls fort vw-hyperopt. Zuerst müssen Sie die neueste Version von Vowpal Wabbit vom Github installieren. Das Modul befindet sich im Ordner utl.

    Achtung! Bisherige Änderungen (insbesondere die neue Befehlssyntax) (15. Dezember) werden nicht in das Hauptrepository übernommen. In den kommenden Tagen hoffe ich, dass das Problem gelöst ist, aber jetzt können Sie die neueste Version des Codes aus meiner Branche verwenden . BEARBEITEN: 22. Dezember, die Änderungen werden gegossen, jetzt können Sie das Haupt-Repository verwenden.

    Anwendungsbeispiel:


    ./vw-hyperopt.py --train ./train_set.vw --holdout ./holdout_set.vw --max_evals 200 --outer_loss_function logistic --vw_space '--algorithms=ftrl,sgd --l2=1e-8..1e-1~LO --l1=1e-8..1e-1~LO -l=0.01..10~L --power_t=0.01..1 --ftrl_alpha=5e-5..8e-1~L --ftrl_beta=0.01..1 --passes=1..10~I --loss_function=logistic -q=SE+SZ+DR,SE~O --ignore=T~O'  --plot 

    Das Eingabemodul erfordert Trainings- und Validierungsbeispiele sowie a priori Verteilungen von Hyperparametern (im Inneren angegeben --vw_space). Sie können ganzzahlige, kontinuierliche oder kategoriale Hyperparameter angeben. Für alle außer kategorial können Sie eine einheitliche oder logarithmisch einheitliche Verteilung angeben. Der Suchraum aus dem Beispiel wird vw-hyperoptungefähr in ein solches Objekt umgewandelt Hyperopt(wenn Sie das Tutorial weiter durchlaufen haben Hyperopt, werden Sie dies verstehen):

    from hyperopt import hp
    prior_search_space = hp.choice('algorithm', [
        {'type': 'sgd',
         '--l1': hp.choice('sgd_l1_outer', ['empty', hp.loguniform('sgd_l1', log(1e-8), log(1e-1))]),
         '--l2': hp.choice('sgd_l2_outer', ['empty', hp.loguniform('sgd_l2', log(1e-8), log(1e-1))]),
         '-l': hp.loguniform('sgd_l', log(0.01), log(10)),
         '--power_t': hp.uniform('sgd_power_t', 0.01, 1),
         '-q': hp.choice('sgd_q_outer', ['emtpy', hp.choice('sgd_q', ['-q SE -q SZ -q DR', '-q SE'])]),
         '--loss_function': hp.choice('sgd_loss', ['logistic']),
         '--passes': hp.quniform('sgd_passes', 1, 10, 1),
        },
        {'type': 'ftrl',
         '--l1': hp.choice('ftrl_l1_outer', ['emtpy', hp.loguniform('ftrl_l1', log(1e-8), log(1e-1))]),
         '--l2': hp.choice('ftrl_l2_outer', ['emtpy', hp.loguniform('ftrl_l2', log(1e-8), log(1e-1))]),
         '-l': hp.loguniform('ftrl_l', log(0.01), log(10)),
         '--power_t': hp.uniform('ftrl_power_t', 0.01, 1),
         '-q': hp.choice('ftrl_q_outer', ['emtpy', hp.choice('ftrl_q', ['-q SE -q SZ -q DR', '-q SE'])]),
         '--loss_function': hp.choice('ftrl_loss', ['logistic']),
         '--passes': hp.quniform('ftrl_passes', 1, 10, 1),
         '--ftrl_alpha': hp.loguniform('ftrl_alpha', 5e-5, 8e-1),
         '--ftrl_beta': hp.uniform('ftrl_beta', 0.01, 1.)
        }
    ])

    Optional können Sie die Verlustfunktion für das Validierungsmuster und die maximale Anzahl von Iterationen ändern ( --outer_loss_functionstandardmäßig logisticund --max_evalsstandardmäßig 100). Es ist auch möglich , die Ergebnisse jeder Iteration zu speichern und grafisch darstellen mit --plot, falls vorhanden matplotlib, und vorzugsweise seaborn:



    Die Dokumentation


    Da es nicht üblich ist, detaillierte Dokumentationen auf dem Hub zu erstellen, beschränke ich mich auf einen Link dazu. Sie können die gesamte Semantik im russischsprachigen Wiki in meiner Gabelung lesen oder auf die englische Version im Haupt-Repository von Vowpal Wabbit warten.

    Pläne


    In Zukunft ist geplant, das Modul zu erweitern:

    1. Unterstützung für Regressions- und Mehrklassenklassifizierungsaufgaben.

    2. Unterstützung für einen „Warmstart“: Geben Sie im Voraus bewertete Hyperopt-Punkte aus und beginnen Sie mit der Optimierung, wobei Sie bereits die Ergebnisse berücksichtigen.

    3. Die Option, den Fehler bei jedem Schritt an einem anderen Testmuster zu bewerten (ohne jedoch die Hyperparameter darauf zu optimieren). Dies ist notwendig, um die Generalisierungsfähigkeit besser einschätzen zu können - haben wir nicht umgeschult?

    4. Unterstützung für binäre Parameter, die keine Werte annehmen, wie z --lrqdropout, --normalized, --adaptive. Jetzt können Sie im Prinzip schreiben --adaptive=\ ~O, aber das ist völlig unintuitiv. Sie können so etwas wie --adaptive=~Boder tun --adaptive=~BO.

    Ich würde mich sehr freuen, wenn jemand das Modul benutzt und jemandem hilft. Ich freue mich über Vorschläge, Ideen oder Fehler, die entdeckt wurden. Sie können sie hier schreiben oder ein Problem auf Github erstellen .

    Update 22.12.2015


    Die Pull-Anforderung mit den neuesten Änderungen wurde dem Haupt-Repository von Vowpal Wabbit hinzugefügt, sodass Sie sie jetzt verwenden können und keinen Zweig.

    Jetzt auch beliebt: