Tutorial ([wiki: FMin rev: a663e] in der Python-Bibliothek hyperopt zur Optimierung böser Suchräume in realen, diskreten und bedingten Dimensionen (https://github.com/hyperopt/hyperopt) (https://github.com/hyperopt/hyperopt/wiki/FMin/a663e64546eb5cd3ed462618dcc1e41863ad8688)) wurde von Google übersetzt. Lizenz
Diese Seite ist ein Tutorial zur grundlegenden Verwendung von "hyperopt.fmin ()". Beschreibt, wie eine Zielfunktion geschrieben wird, die fmin optimieren kann, und wie ein Suchraum geschrieben wird, den fmin durchsuchen kann.
Die Aufgabe von Hyperopt besteht darin, den bestmöglichen stochastischen Funktionswert für einen Skalarwert zu finden und nicht die Menge möglicher Argumente für diese Funktion. Während viele Optimierungspakete erwarten, dass diese Eingaben aus dem Vektorraum abgeleitet werden, empfiehlt Hyperopt, Ihren Suchraum detaillierter zu beschreiben. Durch die Bereitstellung weiterer Informationen darüber, wo Ihre Funktion definiert ist und wo der optimale Wert liegt, können die Algorithmen von hyperopt effizienter gesucht werden.
Die Möglichkeit, Hyperopt zu verwenden, besteht darin, Folgendes zu schreiben:
Dieses (grundlegendste) Tutorial zeigt Ihnen, wie Sie Funktionen und Suchräume mithilfe der Standard-Testdatenbank und des Dummy-Zufallssuchalgorithmus erstellen. In Abschnitt (1) geht es um verschiedene Aufrufkonventionen für die Kommunikation zwischen der Zielfunktion und Hyperopt. In Abschnitt (2) geht es um die Beschreibung des Suchraums.
Eine parallele Suche ist möglich, indem die Datenbank "Trials" durch die Datenbank "MongoTrials" ersetzt wird. Es gibt eine weitere Wiki-Seite über die Verwendung von Mongodb für parallele Suchvorgänge.
Die Auswahl eines Suchalgorithmus ist so einfach wie die Übergabe von "algo = hyperopt.tpe.suggest" anstelle von "algo = hyperopt.random.suggest". Der Suchalgorithmus ist eigentlich ein aufrufbares Objekt, und sein Konstruktor akzeptiert Konfigurationsargumente, bei denen es darum geht, wie der Suchalgorithmus ausgewählt wird.
Hyperopt bietet mehrere Ebenen erhöhter Flexibilität und Komplexität bei der Angabe, um die Zielfunktion zu minimieren. Fragen, über die man als Designer nachdenken sollte
In den nächsten Abschnitten werden verschiedene Möglichkeiten zur Implementierung von Zielen untersucht, mit denen das quadratische Ziel für eine einzelne Variable minimiert wird. Suchen Sie in jedem Abschnitt im Bereich von -10 bis +10. Dies kann in * Suchraum * beschrieben werden.
space = hp.uniform('x', -10, 10)
Below, Section 2, covers how to specify search spaces that are more complicated.
Das einfachste Protokoll für die Kommunikation zwischen dem Optimierungsalgorithmus von hyperopt und der Zielfunktion besteht darin, dass die Zielfunktion einen gültigen Punkt aus dem Suchraum empfängt und diesem Punkt ein Gleitkomma-Verlust (auch als negatives Dienstprogramm bezeichnet) zugeordnet ist. Rückkehr.
from hyperopt import fmin, tpe, hp
best = fmin(fn=lambda x: x ** 2,
space=hp.uniform('x', -10, 10),
algo=tpe.suggest,
max_evals=100)
print best
Dieses Protokoll hat den Vorteil, dass es sehr gut lesbar und einfach zu tippen ist. Wie Sie sehen können, ist es fast ein Liner. Der Nachteil dieses Protokolls ist (1) Mit dieser Art von Funktion können keine zusätzlichen Informationen zu jeder Bewertung an die Testdatenbank zurückgegeben werden. Und (2) Diese Art von Funktion kann nicht mit Suchalgorithmen oder anderen parallelen Funktionsbewertungen interagieren. Das folgende Beispiel zeigt, warum Sie diese Dinge tun möchten.
Wenn die Zielfunktion komplex ist und die Ausführung lange dauert, möchten Sie möglicherweise mehr statistische und diagnostische Informationen sowie den letzten Gleitkommaverlust speichern. In solchen Fällen kann die Funktion fmin das Wörterbuch als Rückgabewert behandeln. Das bedeutet, dass Ihre Verlustfunktion ein Wörterbuch zurückgeben kann, das alle gewünschten Statistiken und Diagnosen verschachtelt. Die Realität ist etwas weniger flexibel. Wenn Sie beispielsweise mongodb verwenden, muss das Wörterbuch ein gültiges JSON-Dokument sein. Dennoch gibt es viel Flexibilität, um domänenspezifische Hilfsergebnisse zu speichern.
Wenn die Zielfunktion ein Wörterbuch zurückgibt, sucht die Funktion fmin nach einigen speziellen Schlüssel / Wert-Paaren im Rückgabewert und übergibt sie an den Optimierungsalgorithmus. Es sind zwei Schlüssel-Wert-Paare erforderlich.
status
- Einer der Schlüssel für hyperopt.STATUS_STRINGS
. Zum Beispiel 'ok' für erfolgreiche Beendigung oder 'fehlgeschlagen', wenn keine Funktion definiert ist.loss
- Der Wert der Gleitkommafunktion, die Sie minimieren möchten. Wenn der Status "ok" ist, muss dieser vorhanden sein.Die Funktion fmin reagiert auch auf einige Optionstasten:
Anhänge
-Schlüssel und Werte, deren Schlüssel kurze Zeichenfolgen wie Dateinamen und deren Werte lange Zeichenfolgen (wie Dateiinhalte) sind, die nicht bei jedem Zugriff auf einen Datensatz aus der Datenbank geladen werden sollten. Paarwörterbuch. (Außerdem begrenzt MongoDB die Länge regulärer Schlüssel / Wert-Paare. Sie sollten sie daher anhängen, wenn der Wert Megabyte erreicht.)loss_variance
--float - Unsicherheit der probabilistischen Zielfunktiontrue_loss
--float - Wenn Sie Hyperparameter optimieren und den Modellverallgemeinerungsfehler unter diesem Namen speichern, erhalten Sie eine klarere Ausgabe der integrierten Plotroutine.true_loss_variance
--float --GeneralisierungsfehlerunsicherheitWörterbücher verwenden eine Vielzahl von Back-End-Speichermechanismen. Sie müssen daher sicherstellen, dass sie mit JSON kompatibel sind. Wenn es sich um ein Diagramm mit einer Baumstruktur aus Wörterbuch, Liste, Tapple, Nummer, Zeichenfolge, Datum und Uhrzeit handelt, gibt es kein Problem.
** Hinweis: ** Um numpy Arrays zu speichern, sollten Sie sie in Strings serialisieren und als Anhänge speichern.
Das Schreiben der obigen Funktion in einem Stil, der ein Wörterbuch zurückgibt, sieht folgendermaßen aus:
import pickle
import time
from hyperopt import fmin, tpe, hp, STATUS_OK
def objective(x):
return {'loss': x ** 2, 'status': STATUS_OK }
best = fmin(objective,
space=hp.uniform('x', -10, 10),
algo=tpe.suggest,
max_evals=100)
print best
Um den Zweck der Rückgabe eines Wörterbuchs tatsächlich zu erkennen, ändern Sie die Zielfunktion, um einige zurückzugeben, und übergeben Sie ein explizites Argument "Versuche" an "fmin".
import pickle
import time
from hyperopt import fmin, tpe, hp, STATUS_OK, Trials
def objective(x):
return {
'loss': x ** 2,
'status': STATUS_OK,
# -- store other results like this
'eval_time': time.time(),
'other_stuff': {'type': None, 'value': [0, 1, 2]},
# -- attachments are handled differently
'attachments':
{'time_module': pickle.dumps(time.time)}
}
trials = Trials()
best = fmin(objective,
space=hp.uniform('x', -10, 10),
algo=tpe.suggest,
max_evals=100,
trials=trials)
print best
In diesem Fall ist der Aufruf von fmin derselbe wie zuvor, Sie können das Testobjekt jedoch direkt übergeben, um alle während des Experiments berechneten Rückgabewerte zu überprüfen.
Also zum Beispiel:
trials.trials
- Liste der Wörterbücher, die alle Suchvorgänge darstellentrials.results
- Liste der Wörterbücher, die während der Suche von 'objective' zurückgegeben wurdentrials.losses ()
- Fließkomma-Liste der Verluste (für jeden 'ok'-Versuch)trials.statuses ()
- Liste der StatuszeichenfolgenSie können dieses Testobjekt speichern, an eine integrierte Plotroutine übergeben oder mit Ihrem eigenen benutzerdefinierten Code analysieren.
"Anhänge" werden von einem speziellen Mechanismus verwaltet, mit dem der gleiche Code sowohl für "Versuche" als auch für "MongoTrials" verwendet werden kann.
Sie können einen solchen Testanhang erhalten. Dies wird das Zeitmodul für den 5. Versuch erhalten.
msg = trials.trial_attachments(trials.trials[5])['time_module']
time_module = pickle.loads(msg)
Anhänge sind große Zeichenfolgen. Wenn Sie also MongoTrials verwenden, müssen Sie nicht mehr herunterladen, als Sie benötigen. Strings können auch global über Versuche an das gesamte Testobjekt angehängt werden. Anhänge verhalten sich wie ein String-zu-String-Wörterbuch.
** NB ** Derzeit werden versuchsspezifische Anhänge an Testobjekte im Anhangswörterbuch für denselben globalen Test abgelegt, können sich jedoch in Zukunft ändern und gelten nicht für MongoTrials.
Es ist möglich, dass fmin ()
Ihrer Zielfunktion den Griff von Mongodb gibt, der in parallelen Experimenten verwendet wird. Mit diesem Mechanismus können Sie die Datenbank mit Teilergebnissen aktualisieren und mit anderen parallelen Prozessen kommunizieren, die unterschiedliche Punkte bewerten. Die Zielfunktion kann sogar neue Suchpunkte hinzufügen, z. B. "random.suggest".
Die grundlegenden Techniken sind:
fmin_pass_expr_memo_ctrl
Verwenden von Dekoratorenpyll.rec_eval
in Ihrer eigenen Funktion auf, um einen Suchraumpunkt aus expr
und memo
zu erstellen.ctrl
, eine Instanz von hyperopt.Ctrl
, um mit anderen Testobjekten zu kommunizieren.Ich werde es in diesem kurzen Tutorial nicht behandeln, aber ich möchte erwähnen, was mit der aktuellen Codebasis möglich ist. Es enthält auch Hyperopt-Quellen, Unit-Tests und Beispielprojekte wie hyperopt-convnet. Bitte senden Sie mir eine E-Mail oder senden Sie ein Github-Problem, um diesen Teil des Codes zu beschleunigen.
Der Suchraum besteht aus verschachtelten funktionalen Ausdrücken, die probabilistische Ausdrücke enthalten. Die probabilistische Darstellung ist ein Hyperparameter. Die Abtastung aus diesem verschachtelten stochastischen Programm definiert einen Zufallssuchalgorithmus. Der Hyperparameter-Optimierungsalgorithmus ersetzt die übliche "Abtast" -Logik durch eine adaptive Suchstrategie und versucht nicht, aus der angegebenen Verteilung im Suchraum abzutasten.
Stellen Sie sich den Suchraum am besten als ein Stichprobenprogramm für probabilistische Argumente vor. Zum Beispiel
from hyperopt import hp
space = hp.choice('a',
[
('case 1', 1 + hp.lognormal('c1', 0, 1)),
('case 2', hp.uniform('c2', -10, 10))
])
Das Ergebnis der Ausführung dieses Codes ist die Variable "Leerzeichen", die auf das Diagramm der Ausdruckskennung und ihre Argumente verweist. Es wurde tatsächlich nichts probiert. Es ist nur eine Grafik, die beschreibt, wie Punkte abgetastet werden. Der Code für die Arbeit mit dieser Art von Darstellungsdiagramm befindet sich in "hyperopt.pyll", und wir nennen diese Diagramme "Pyll" -Diagramme oder "Pyll-Programme".
Falls gewünscht, kann der Probenraum abgetastet und ausgewertet werden.
import hyperopt.pyll.stochastic
print hyperopt.pyll.stochastic.sample(space)
Dieser Suchraum, der durch "Leerzeichen" beschrieben wird, hat drei Parameter:
Hierbei ist zu beachten, dass alle optimierbaren probabilistischen Ausdrücke als erstes Argument ein * label * haben. Diese Bezeichnungen werden verwendet, um Parameterauswahl an den Aufrufer zurückzugeben, und werden intern auf verschiedene Arten verwendet.
Eine andere Sache, die zu beachten ist, ist die Verwendung von Taples in der Mitte des Diagramms (um jeden von "Fall 1" und "Fall 2"). Listen, Wörterbücher und Tapples werden zu "deterministischen funktionalen Ausdrücken" aktualisiert und werden Teil des stochastischen Programms für den Suchraum.
Der dritte bemerkenswerte ist der numerische Ausdruck "1 + hp.lognormal (" c1 ", 0, 1)", der in die Beschreibung des Suchraums eingebettet ist. Was den Optimierungsalgorithmus betrifft, gibt es keinen Unterschied darin, 1 direkt zum Suchraum und 1 in der Logik der Zielfunktion selbst hinzuzufügen. Designer können wählen, wo eine solche Verarbeitung platziert werden soll, um die Art von Modularität zu erreichen, die sie benötigen. Das Ergebnis eines Zwischenausdrucks im Suchraum kann ein beliebiges Python-Objekt sein, auch wenn es parallel mit Mongodb optimiert wurde. Es ist einfach, der Beschreibung des Suchraums eine neue Art von nicht-probabilistischer Darstellung hinzuzufügen (siehe Abschnitt 2.3 unten).
Viertens sind 'c1'und'c2' Beispiele, die als bedingte Parameter bezeichnet werden. Jedes von 'c1' und 'c2' zeigt nur die Zahlen in der Stichprobe, die für einen bestimmten Wert von 'a' zurückgegeben wurden. Wenn 'a'is 0 ist, wird' c1 'verwendet, aber' c2 'wird nicht verwendet. Wenn 'a'is 1 ist, wird' c2 'verwendet, aber' c1 'wird nicht verwendet. Wenn es sinnvoll ist, sollten Sie die Parameter auf diese Weise als bedingt codieren, anstatt die Parameter in der Zielfunktion einfach zu ignorieren. Sie können effizienter suchen, wenn Sie feststellen, dass 'c1' die Zielfunktion möglicherweise nicht beeinflusst (da dies die Argumente der Zielfunktion nicht beeinflusst).
Die derzeit vom Optimierungsalgorithmus von hyperopt erkannten Wahrscheinlichkeitsformeln sind:
hp.choice(label, options)
Gibt eine der Optionen zurück. Muss eine Liste oder ein Tapple sein. Die Elemente von "Optionen" können in ihren eigenen probabilistischen Ausdrücken verschachtelt sein. In diesem Fall ist die probabilistische Auswahl, die nur in wenigen Optionen angezeigt wird, der Parameter * condition *.
hp.randint(label, upper)
Gibt eine Zufallszahl im Bereich [0, oben] zurück. Die Semantik dieser Verteilung besteht darin, dass es keine Korrelation in der Verlustfunktion zwischen nahegelegenen ganzzahligen Werten im Vergleich zu weiter ganzzahligen Werten gibt. Dies ist eine gute Verteilung, um beispielsweise einen zufälligen Startwert zu beschreiben. Wenn die Verlustfunktion wahrscheinlich mit nahegelegenen ganzzahligen Werten korreliert, sollten Sie eine der "quantisierten" kontinuierlichen Verteilungen verwenden, z. B. "quniform", "qloguniform", "qnormal" oder "qlognormal".
hp.uniform(label, low, high)
Gibt einen Wert gleichmäßig zwischen "niedrig" und "hoch" zurück.
Bei der Optimierung wird diese Variable durch den Abstand auf beiden Seiten eingeschränkt.
hp.quniform(label, low, high, q)
Gibt einen Wert wie "rund (gleichmäßig (niedrig, hoch) / q) * q" zurück.
Etwas "glatt" im Zweck, aber geeignet für diskrete Werte, die sowohl oben als auch unten eingeschränkt werden sollten.
hp.loguniform(label, low, high)
Gibt eine logarithmische Gleichverteilung zurück, z. B. "exp (gleichmäßig (niedrig, hoch))".
Bei der Optimierung ist diese Variable auf das Intervall "[exp (niedrig), exp (hoch)]" beschränkt.
hp.qloguniform(label, low, high, q)
round (exp (uniform (niedrig, hoch)) / q) * Gibt einen Wert wie q
zurück.
Geeignet für diskrete Variablen, deren Zweck "glatt" ist und deren Wertgröße glatter ist, die jedoch sowohl oben als auch unten beschränkt sind.
hp.normal(label, mu, sigma)
Gibt den tatsächlichen Wert zurück, der normalerweise mit dem Mittelwert mu und dem Sigma der Standardabweichung verteilt ist. Bei der Optimierung ist dies eine uneingeschränkte Variable.
hp.qnormal(label, mu, sigma, q)
round (normal (mu, sigma) / q) * Gibt einen Wert wie q
zurück.
Nimmt wahrscheinlich einen Wert in der Nähe von mu an, ist aber grundsätzlich für unbegrenzte diskrete Variablen geeignet.
hp.lognormal(label, mu, sigma)
Gibt den gemäß "exp (normal (mu, sigma))" gezeichneten Wert zurück, so dass der logarithmische Wert des Rückgabewerts normal verteilt ist. Bei der Optimierung ist diese Variable auf positive Werte beschränkt.
hp.qlognormal(label, mu, sigma, q)
round (exp (normal (mu, sigma)) / q) * Gibt einen Wert wie q
zurück.
Geeignet für diskrete Variablen, deren Zweck durch die Größe der von einer Seite begrenzten Variablen geglättet und geglättet wird.
2.2 A Search Space Example: scikit-learn
Um all diese Möglichkeiten in Aktion zu sehen, sehen wir uns an, wie scikit-learn die Hyperparameterräume des Klassifizierungsalgorithmus beschreibt. (Diese Idee wurde unter hyperopt-sklearn entwickelt.)
from hyperopt import hp
space = hp.choice('classifier_type', [
{
'type': 'naive_bayes',
},
{
'type': 'svm',
'C': hp.lognormal('svm_C', 0, 1),
'kernel': hp.choice('svm_kernel', [
{'ktype': 'linear'},
{'ktype': 'RBF', 'width': hp.lognormal('svm_rbf_width', 0, 1)},
]),
},
{
'type': 'dtree',
'criterion': hp.choice('dtree_criterion', ['gini', 'entropy']),
'max_depth': hp.choice('dtree_max_depth',
[None, hp.qlognormal('dtree_max_depth_int', 3, 1, 1)]),
'min_samples_split': hp.qlognormal('dtree_min_samples_split', 2, 1, 1),
},
])
Sie können Knoten wie Argumente für die Pyll-Funktion verwenden (siehe Pyll). Wenn Sie mehr darüber erfahren möchten, reichen Sie bitte ein Github-Problem ein.
Einfach ausgedrückt, dekorieren Sie einfach die Funktionen der obersten Ebene (dh pickelfreundlich) für die Verwendung durch das Objekt "scope".
import hyperopt.pyll
from hyperopt.pyll import scope
@scope.define
def foo(a, b=0):
print 'runing foo', a, b
return a + b / 2
# -- this will print 0, foo is called as usual.
print foo(0)
#In der Beschreibung des Suchraums, wie bei normalem Python`foo`Kann verwendet werden.
#Diese beiden Anrufe nennen eigentlich nicht foo,
#Notieren Sie nur, dass Sie foo aufrufen müssen, um das Diagramm auszuwerten.
space1 = scope.foo(hp.uniform('a', 0, 10))
space2 = scope.foo(hp.uniform('a', 0, 10), hp.normal('b', 0, 1))
# -- this will print an pyll.Apply node
print space1
# -- this will draw a sample by running foo()
print hyperopt.pyll.stochastic.sample(space1)
Wenn möglich, sollte das Hinzufügen neuer Arten probabilistischer Darstellungen zur Beschreibung des Parametersuchraums vermieden werden. Damit alle Suchalgorithmen in allen Räumen funktionieren, müssen die Suchalgorithmen mit dem Typ des Hyperparameters übereinstimmen, der den Raum beschreibt. Als Bibliotheksbetreuer eröffne ich die Möglichkeit, dass von Zeit zu Zeit eine Art Ausdruck hinzugefügt werden sollte, aber wie gesagt, ich möchte dies so weit wie möglich vermeiden. Das Hinzufügen einer neuen Art von probabilistischer Darstellung ist keine der Möglichkeiten, wie Hyperopt erweiterbar ist.
Copyright (c) 2013, James Bergstra All rights reserved.
Recommended Posts