Hallo an alle, die den Weg des ML-Samurai gewÀhlt haben!
Einleitung:
In diesem Artikel betrachten wir die Support Vector Machine- Methode ( engl. SVM, Support Vector Machine ) fĂŒr das Klassifizierungsproblem. Die Hauptidee des Algorithmus wird vorgestellt, die Ausgabe der Gewichtseinstellung und eine einfache DIY-Implementierung werden analysiert. Am Beispiel eines Datensatzes Der Betrieb des geschriebenen Algorithmus mit linear trennbaren / untrennbaren Daten im Raum wird demonstriert und Visualisierung von Training / Prognose. ZusĂ€tzlich werden die Vor- und Nachteile des Algorithmus und seiner Modifikationen bekannt gegeben.

Abbildung 1. Open Source-Foto einer Irisblume
Das zu lösende Problem:
Wir werden das Problem der binĂ€ren Klassifikation (wenn es nur zwei Klassen gibt) lösen. Erstens trainiert der Algorithmus Objekte aus dem Trainingssatz, fĂŒr die Klassenbeschriftungen im Voraus bekannt sind. Ferner sagt der bereits trainierte Algorithmus die Klassenbezeichnung fĂŒr jedes Objekt aus der verzögerten / Testprobe voraus. Klassenbeschriftungen können Werte annehmen Y = \ {- 1, +1 \} . Objekt - Vektor mit N Zeichen im Weltraum . Beim Lernen sollte der Algorithmus eine Funktion aufbauen Das braucht ein Argument - ein Objekt aus dem Weltraum und gibt das Klassenetikett .
Allgemeine Wörter ĂŒber den Algorithmus:
Die Klassifizierungsaufgabe bezieht sich auf den Unterricht bei einem Lehrer. SVM ist ein Lernalgorithmus mit einem Lehrer. Visuell finden Sie in diesem erstklassigen Artikel viele Algorithmen fĂŒr maschinelles Lernen (siehe Abschnitt âKarte der Welt des maschinellen Lernensâ). Es sollte hinzugefĂŒgt werden, dass SVM auch fĂŒr Regressionsprobleme verwendet werden kann, aber der SVM-Klassifikator wird in diesem Artikel analysiert.
Das Hauptziel von SVM als Klassifikator ist es, die Gleichung einer trennenden Hyperebene zu finden
im Weltraum , was die beiden Klassen auf eine optimale Weise trennen wĂŒrde. Gesamtansicht der Transformation Einrichtung zum Klassenlabel : . Wir werden uns erinnern, dass wir bezeichnet haben . Nach dem Einrichten des Algorithmus Gewichte und (Training) werden alle Objekte, die auf eine Seite der konstruierten Hyperebene fallen, als erste Klasse und Objekte, die auf die andere Seite fallen - die zweite Klasse, vorhergesagt.
Innenfunktion Es gibt eine lineare Kombination von Merkmalen des Objekts mit den Algorithmusgewichten, weshalb SVM auf lineare Algorithmen verweist. Eine teilende Hyperebene kann auf verschiedene Arten konstruiert werden, jedoch in SVM-Gewichten und sind so konfiguriert, dass Klassenobjekte so weit wie möglich von der trennenden Hyperebene entfernt liegen. Mit anderen Worten, der Algorithmus maximiert die LĂŒcke ( englischer Rand ) zwischen der Hyperebene und den Klassenobjekten, die ihm am nĂ€chsten liegen. Solche Objekte werden StĂŒtzvektoren genannt (siehe Abb. 2). Daher der Name des Algorithmus.

Abbildung 2. SVM (die Basis der Abbildung von hier )
Eine detaillierte Ausgabe der SVM-Skalenanpassungsregeln:
Um die Teilungshyperebene so weit wie möglich von den Abtastpunkten entfernt zu halten, sollte die Streifenbreite maximal sein. Vektor Ist der Richtungsvektor der teilenden Hyperebene. Nachfolgend bezeichnen wir das Skalarprodukt zweier Vektoren als oder Suchen wir die Projektion des Vektors, dessen Enden die StĂŒtzvektoren verschiedener Klassen sind, auf den Richtungsvektor der Hyperebene. Diese Projektion zeigt die Breite des Trennstreifens (siehe Abb. 3):

Abbildung 3. Die Ausgabe der Regeln zum Einstellen der Skalen (die Grundlage der Abbildung von hier )
Der Rand des Objekts x von der Klassengrenze ist der Wert . Der Algorithmus macht genau dann einen Fehler am Objekt, wenn der Einzug aktiviert ist negativ (wenn und verschiedene Charaktere). Wenn Dann fÀllt das Objekt in den Trennstreifen. Wenn wird das Objekt x korrekt klassifiziert und befindet sich in einiger Entfernung vom Trennstreifen. Wir schreiben diesen Zusammenhang auf:
Das resultierende System ist die Standard- SVM- Einstellung mit einer SVM mit festem Rand , wenn kein Objekt in das Trennungsband eintreten darf. Es wird analytisch durch das Kuhn-Tucker-Theorem gelöst. Das resultierende Problem entspricht dem doppelten Problem, den Sattelpunkt der Lagrange-Funktion zu finden.
$$ display $$ \ left \ {\ begin {array} {ll} (w ^ Tw) / 2 \ rightarrow min & \ textrm {} \\ y (w ^ Tx-b) \ geqslant 1 & \ textrm {} \ end {array} \ right. $$ display $$
All dies ist gut, solange unsere Klassen linear trennbar sind. Lassen Sie uns unser System ein wenig transformieren, damit der Algorithmus mit linear untrennbaren Daten arbeiten kann. Lassen Sie den Algorithmus Fehler an den Trainingsobjekten machen, aber versuchen Sie gleichzeitig, weniger Fehler zu vermeiden. Wir fĂŒhren eine Reihe zusĂ€tzlicher Variablen ein Charakterisierung der GröĂe des Fehlers bei jedem Objekt . Wir fĂŒhren eine Strafe fĂŒr den Gesamtfehler in die minimierte Funktion ein:
$$ display $$ \ left \ {\ begin {array} {ll} (w ^ Tw) / 2 + \ alpha \ sum \ xi _i \ rightarrow min & \ textrm {} \\ y (w ^ Tx_i-b) \ geqslant 1 - \ xi _i & \ textrm {} \\ \ xi _i \ geqslant0 & \ textrm {} \ end {array} \ right. $$ display $$
Wir betrachten die Anzahl der Algorithmusfehler (wenn M <0 ist). Nenne es Strafe . Dann entspricht die Strafe fĂŒr alle Objekte der Höhe der GeldbuĂen fĂŒr jedes Objekt wo - Schwellenwertfunktion (siehe Abb. 4):
$$ display $$ [M_i <0] = \ left \ {\ begin {array} {ll} 1 & \ textrm {if} M_i <0 \\ 0 & \ textrm {if} M_i \ geqslant 0 \ end {array} \ right. $$ display $$
Als nĂ€chstes machen wir die Strafe empfindlich fĂŒr die GröĂe des Fehlers und fĂŒhren gleichzeitig eine Strafe fĂŒr die AnnĂ€herung des Objekts an die Klassengrenze ein:
Beim HinzufĂŒgen des Begriffs zum Strafausdruck Wir erhalten die klassische SVM- Verlustfunktion mit weicher LĂŒcke ( soft-margin SVM ) fĂŒr ein Objekt:
- Verlustfunktion, es ist auch eine Verlustfunktion. Das minimieren wir mit Hilfe des Gradientenabfalls bei der Umsetzung der HĂ€nde. Wir leiten die Regeln fĂŒr das Ăndern von Gewichten ab, wo - Abstiegsstufe:
$$ display $$ \ bigtriangledown Q = \ left \ {\ begin {array} {ll} \ alpha w-yx & \ textrm {if} yw ^ Tx <1 \\ \ alpha w & \ textrm {if} yw ^ Tx \ geqslant 1 \ end {array} \ right. $$ display $$
Mögliche Fragen bei Interviews (basierend auf realen Ereignissen):
Nach allgemeinen Fragen zu SVM: Warum maximiert Hinge_loss den Abstand? - Denken Sie zunĂ€chst daran, dass eine Hyperebene ihre Position Ă€ndert, wenn sich die Gewichte Ă€ndern und . Die Gewichte des Algorithmus beginnen sich zu Ă€ndern, wenn die Gradienten der Verlustfunktion ungleich Null sind (sie sagen normalerweise: "Gradientenfluss"). Deshalb haben wir speziell eine solche Verlustfunktion gewĂ€hlt, bei der zum richtigen Zeitpunkt Gradienten zu flieĂen beginnen. sieht so aus: . Denken Sie daran, dass die Freigabe . Wenn die LĂŒcke groĂ genug ( oder mehr) Ausdruck wird kleiner als Null und (Daher flieĂen keine Gradienten, und das Gewicht des Algorithmus Ă€ndert sich in keiner Weise.) Wenn die LĂŒcke m ausreichend klein ist (zum Beispiel, wenn das Objekt in das Trennband fĂ€llt und / oder negativ ist (wenn die Klassifizierungsvorhersage falsch ist), wird Hinge_loss positiv ( ) beginnen Gradienten zu flieĂen und die Algorithmusgewichte Ă€ndern sich. Zusammenfassung: Gradienten flieĂen in zwei FĂ€llen: wenn das Probenobjekt in das Trennband fiel und wenn das Objekt falsch klassifiziert ist.
Um das Niveau einer Fremdsprache zu ĂŒberprĂŒfen, sind Ă€hnliche Fragen möglich: Was sind die Ăhnlichkeiten und Unterschiede zwischen LogisticRegression und SVM? - ZunĂ€chst werden wir ĂŒber Ăhnlichkeiten sprechen: Beide Algorithmen sind lineare Klassifizierungsalgorithmen beim ĂŒberwachten Lernen. Einige Ăhnlichkeiten bestehen in ihren Argumenten fĂŒr Verlustfunktionen: fĂŒr logreg und fĂŒr SVM (siehe Bild 4). Beide Algorithmen können mit Gradientenabstieg konfiguriert werden. Als nĂ€chstes wollen wir uns mit den Unterschieden befassen: SVM gibt die Klassenbezeichnung des Objekts zurĂŒck, im Gegensatz zu LogReg, das die Wahrscheinlichkeit einer Klassenmitgliedschaft zurĂŒckgibt. SVM kann nicht mit Klassenbeschriftungen arbeiten \ {0,1 \} (ohne Klassen umzubenennen) im Gegensatz zu LogReg (LogReg-Verlustfinktion fĂŒr \ {0,1 \} : wo - Echtes Klassenlabel, - RĂŒckkehr des Algorithmus, Wahrscheinlichkeit der Zugehörigkeit zu einem Objekt zum unterricht \ {1 \} ) DarĂŒber hinaus können wir das Problem der margenschwachen SVM ohne Gradientenabnahme lösen. Die Suche nach Hilfsvektoren wird in der Lagrange-Funktion auf den Suchsattelpunkt reduziert - diese Aufgabe bezieht sich nur auf die quadratische Programmierung.
Code der Verlustfunktion:import numpy as np import matplotlib.pyplot as plt %matplotlib inline xx = np.linspace(-4,3,100000) plt.plot(xx, [(x<0).astype(int) for x in xx], linewidth=2, label='1 if M<0, else 0') plt.plot(xx, [np.log2(1+2.76**(-x)) for x in xx], linewidth=4, label='logistic = log(1+e^-M)') plt.plot(xx, [np.max(np.array([0,1-x])) for x in xx], linewidth=4, label='hinge = max(0,1-M)') plt.title('Loss = F(Margin)') plt.grid() plt.legend(prop={'size': 14});

Abbildung 4. Verlustfunktionen
Einfache Implementierung von klassischem Soft-Margin-SVM:
Achtung! Einen Link zum vollstĂ€ndigen Code finden Sie am Ende des Artikels. Unten finden Sie Codeblöcke, die aus dem Kontext genommen wurden. Einige Bausteine ââkönnen erst nach dem Ausarbeiten der vorherigen Bausteine ââgestartet werden. Unter vielen Blöcken werden Bilder platziert, die zeigen, wie der darĂŒber platzierte Code funktioniert.
Zuerst kĂŒrzen wir die erforderlichen Bibliotheken und die Linienzeichenfunktion: import numpy as np import warnings warnings.filterwarnings('ignore') import matplotlib.pyplot as plt import matplotlib.lines as mlines plt.rcParams['figure.figsize'] = (8,6) %matplotlib inline from sklearn.datasets import load_iris from sklearn.decomposition import PCA from sklearn.model_selection import train_test_split def newline(p1, p2, color=None):
Python-Implementierungscode fĂŒr SVM mit weichen RĂ€ndern: def add_bias_feature(a): a_extended = np.zeros((a.shape[0],a.shape[1]+1)) a_extended[:,:-1] = a a_extended[:,-1] = int(1) return a_extended class CustomSVM(object): __class__ = "CustomSVM" __doc__ = """ This is an implementation of the SVM classification algorithm Note that it works only for binary classification ############################################################# ###################### PARAMETERS ###################### ############################################################# etha: float(default - 0.01) Learning rate, gradient step alpha: float, (default - 0.1) Regularization parameter in 0.5*alpha*||w||^2 epochs: int, (default - 200) Number of epochs of training ############################################################# ############################################################# ############################################################# """ def __init__(self, etha=0.01, alpha=0.1, epochs=200): self._epochs = epochs self._etha = etha self._alpha = alpha self._w = None self.history_w = [] self.train_errors = None self.val_errors = None self.train_loss = None self.val_loss = None def fit(self, X_train, Y_train, X_val, Y_val, verbose=False):
Wir betrachten im Detail die Funktionsweise jedes Zeilenblocks:
1) Erstelle eine Funktion add_bias_feature (a) , die automatisch den Vektor von Objekten erweitert und die Zahl 1 an das Ende jedes Vektors anfĂŒgt , um den freien Term âzu vergessenâ. B. Ausdruck Ă€quivalent zum Ausdruck . Wir gehen davon aus, dass die Einheit die letzte Komponente des Vektors fĂŒr alle Vektoren x und x ist . Stellen Sie nun die Gewichte ein und Wir werden zur gleichen Zeit produzieren.
Funktionscode der Feature-Vektorerweiterung: def add_bias_feature(a): a_extended = np.zeros((a.shape[0],a.shape[1]+1)) a_extended[:,:-1] = a a_extended[:,-1] = int(1) return a_extended
2) Dann werden wir den Klassifikator selbst beschreiben. Es hat die Funktionen, init () zu initialisieren, fit () zu lernen, predict () vorherzusagen , den Verlust der Funktion hinge_loss () zu ermitteln und den Gesamtverlust der Funktion des klassischen Algorithmus mit einer weichen LĂŒcke soft_margin_loss () zu ermitteln .
3) Bei der Initialisierung werden 3 Hyperparameter eingefĂŒhrt: _etha - Schritt des Gradientenabfalls ( ), _α - Geschwindigkeitskoeffizient der proportionalen Gewichtsreduktion (vor dem quadratischen Term in der Verlustfunktion ), _epochs - die Anzahl der Trainingsperioden.
Initialisierungsfunktionscode: def __init__(self, etha=0.01, alpha=0.1, epochs=200): self._epochs = epochs self._etha = etha self._alpha = alpha self._w = None self.history_w = [] self.train_errors = None self.val_errors = None self.train_loss = None self.val_loss = None
4) WĂ€hrend des Trainings fĂŒr jede Ăra der Trainingsstichprobe (X_train, Y_train) wird ein Element aus der Stichprobe genommen und der Abstand zwischen diesem Element und der Position der Hyperebene zu einem bestimmten Zeitpunkt berechnet. AbhĂ€ngig von der GröĂe dieser LĂŒcke Ă€ndern wir das Gewicht des Algorithmus unter Verwendung des Gradienten der Verlustfunktion . Gleichzeitig berechnen wir den Wert dieser Funktion fĂŒr jede Epoche und wie oft wir die Gewichte pro Epoche Ă€ndern. Bevor wir mit dem Training beginnen, werden wir sicherstellen, dass nicht mehr als zwei verschiedene Klassenlabels in die Lernfunktion aufgenommen wurden. Vor dem Einrichten der Waage wird diese mit der Normalverteilung initialisiert.
Lernfunktionscode: def fit(self, X_train, Y_train, X_val, Y_val, verbose=False):
ĂberprĂŒfung der Funktionsweise des geschriebenen Algorithmus:
ĂberprĂŒfen Sie, ob unser schriftlicher Algorithmus mit einer Art Spielzeugdatensatz funktioniert. Nehmen Sie den Iris-Datensatz. Wir werden die Daten aufbereiten. Bezeichnen Sie die Klassen 1 und 2 als und Klasse 0 als . Mit dem PCA-Algorithmus (ErklĂ€rung und Anwendung hier ) reduzieren wir den Platz von 4 Attributen optimal auf 2 bei minimalem Datenverlust (es wird uns leichter fallen, das Training und das Ergebnis zu beobachten). Als nĂ€chstes werden wir in eine Trainings- (Zug-) Probe und eine verzögerte (Validierungs-) Probe unterteilen. Wir werden die Trainingsstichprobe trainieren, sie vorhersagen und prĂŒfen. Wir wĂ€hlen die Lernfaktoren so, dass die Verlustfunktion sinkt. WĂ€hrend des Trainings werden wir uns mit der Verlustfunktion des Trainings und der verzögerten Probenahme befassen.
Initialisierungs- und Trainingseinheit: 
Der Visualisierungsblock des resultierenden Trennstreifens: d = {-1:'green', 1:'red'} plt.scatter(X_train[:,0], X_train[:,1], c=[d[y] for y in Y_train]) newline([0,-svm._w[2]/svm._w[1]],[-svm._w[2]/svm._w[0],0], 'blue')

Prognosevisualisierungsblock: 
GroĂartig! Unser Algorithmus handhabte linear trennbare Daten. Nun trennen Sie die Klassen 0 und 1 von der Klasse 2:
Initialisierungs- und Trainingseinheit: 
Der Visualisierungsblock des resultierenden Trennstreifens: d = {-1:'green', 1:'red'} plt.scatter(X_train[:,0], X_train[:,1], c=[d[y] for y in Y_train]) newline([0,-svm._w[2]/svm._w[1]],[-svm._w[2]/svm._w[0],0], 'blue')

Schauen wir uns das GIF an, das zeigt, wie sich die Position der Trennlinie wĂ€hrend des Trainings geĂ€ndert hat (nur 500 Frames fĂŒr das Ăndern der Gewichte. Die ersten 300 in einer Reihe. Die nĂ€chsten 200 StĂŒck fĂŒr jedes 130. Frame):
Erstellungscode der Animation: import matplotlib.animation as animation from matplotlib.animation import PillowWriter def one_image(w, X, Y): axes = plt.gca() axes.set_xlim([-4,4]) axes.set_ylim([-1.5,1.5]) d1 = {-1:'green', 1:'red'} im = plt.scatter(X[:,0], X[:,1], c=[d1[y] for y in Y]) im = newline([0,-w[2]/w[1]],[-w[2]/w[0],0], 'blue')

Prognosevisualisierungsblock: 
RĂ€ume begradigen
Es ist wichtig zu verstehen, dass es bei realen Problemen keinen einfachen Fall mit linear trennbaren Daten gibt. Um mit solchen Daten zu arbeiten, wurde die Idee vorgeschlagen, in einen anderen Raum zu ziehen, in dem die Daten linear getrennt werden können. Ein solcher Raum heiĂt gleichrichten. Das Korrigieren von Leerzeichen und Kerneln ist in diesem Artikel nicht betroffen. Die vollstĂ€ndigste mathematische Theorie finden Sie in der Zusammenfassung von E. Sokolov und in den Vorlesungen von K.V. Vorontsov .
Verwenden von SVM aus sklearn:
TatsĂ€chlich sind fast alle klassischen Algorithmen fĂŒr maschinelles Lernen fĂŒr Sie geschrieben. Lassen Sie uns ein Beispiel fĂŒr den Code geben. Wir werden den Algorithmus aus der sklearn- Bibliothek entnehmen .
Codebeispiel from sklearn import svm from sklearn.metrics import recall_score C = 1.0
Vor- und Nachteile der klassischen SVM:
Vorteile:
- funktioniert gut mit groĂem Funktionsraum;
- funktioniert gut mit kleinen Daten;
- Der Algorithmus ermittelt also, dass das Teilungsband maximiert wird, wodurch sich wie bei einem Airbag die Anzahl der Klassifizierungsfehler verringern lÀsst.
- Da sich der Algorithmus darauf reduziert, das quadratische Programmierproblem in einem konvexen Bereich zu lösen, hat ein solches Problem immer eine eindeutige Lösung (die Trennungs-Hyperebene mit bestimmten Hyperparametern des Algorithmus ist immer dieselbe).
Nachteile:
- lange Einarbeitungszeit (fĂŒr groĂe Datenmengen);
- LĂ€rminstabilitĂ€t: AusreiĂer in Trainingsdaten werden zu Referenzobjekten fĂŒr Eindringlinge und wirken sich direkt auf die Konstruktion einer trennenden Hyperebene aus.
- allgemeine Methoden zur Konstruktion von Kerneln und zur Korrektur von RĂ€umen, die fĂŒr ein bestimmtes Problem bei linearer Untrennbarkeit von Klassen am besten geeignet sind, werden nicht beschrieben. Das AuswĂ€hlen nĂŒtzlicher Datentransformationen ist eine Kunst.
SVM-Anwendung:
Die Wahl des einen oder anderen Algorithmus fĂŒr maschinelles Lernen hĂ€ngt direkt von den Informationen ab, die beim Data Mining erhalten werden. Generell lassen sich folgende Aufgaben unterscheiden:
- Aufgaben mit einem kleinen Datensatz;
- Textklassifizierungsaufgaben. SVM liefert eine gute Ausgangsbasis ([Vorverarbeitung] + [TF-iDF] + [SVM]), die resultierende Prognosegenauigkeit liegt auf dem Niveau einiger faltungsbedingter / wiederkehrender neuronaler Netze (ich empfehle, diese Methode selbst auszuprobieren, um das Material zu konsolidieren). Ein hervorragendes Beispiel finden Sie hier, "Teil 3. Ein Beispiel fĂŒr einen der von uns gelehrten Tricks" ;
- fĂŒr viele Aufgaben mit strukturierten Daten der Link [Feature-Engineering] + [SVM] + [Kernel] "noch Kuchen";
- Da der Scharnierverlust als ziemlich schnell angesehen wird, kann er in Vowpal Wabbit (standardmĂ€Ăig) gefunden werden.
AlgorithmusÀnderungen:
Es gibt verschiedene ErgÀnzungen und Modifikationen der Support-Vektor-Methode, um bestimmte Nachteile zu beseitigen:
- Relevanz-Vektor-Maschine (RVM)
- 1-Norm-SVM (LASSO-SVM)
- Doppelt regulierte SVM (ElasticNet SVM)
- Support Features Machine (SFM)
- Relevanzmerkmale Maschine (RFM)
ZusÀtzliche Quellen zu SVM:
- TextvortrÀge von K.V. Vorontsov
- Zusammenfassungen von E. Sokolov - 14.15.16
- Coole Quelle von Alexandre Kowalczyk
- Auf einem habr gibt es 2 Artikel, die SVM gewidmet sind:
- Auf dem Github kann ich unter den folgenden Links 2 coole SVM-Implementierungen hervorheben:
Fazit:
Vielen Dank fĂŒr Ihre Aufmerksamkeit! FĂŒr Kommentare, Feedback und Tipps wĂ€re ich dankbar.
Sie finden den vollstÀndigen Code aus diesem Artikel auf meinem Github .
ps Danke yorko fĂŒr Tipps zum GlĂ€tten von Ecken. Vielen Dank an Aleksey Sizykh - eine Abteilung fĂŒr Physik und Technologie, die teilweise in den Code investiert hat.