Support Vector Machines sind Verfahren für überwachtes Machine Learning, die hauptsächlich für binäre Klassifizierung verwendet werden. Die Trainingsdaten werden im n-dimensionalen Raum geplottet und der Algorithmus versucht, eine Grenze mit dem größtmöglichen Abstand zu der nächsten Stichprobe zu ziehen.
Die Grenze ist mittels der ihr nächstliegenden Objekten definiert, welche deswegen auch Stützvektoren genannt werden. Vektoren, die von der Grenze weiter entfernt liegen sind für die Berechnung nicht entscheidend. Deswegen brauchen sie auch nicht in den Hauptspeicher geladen werden, was SV sehr speichereffizient macht.
Wenn man über eine Grenze spricht, meint man eigenlich eine Hyperebene. Das ist ein Unterraum, dessen Dimension um 1 kleiner ist als seine Umgebung. Zum Beispiel im dreidimensionalen Raum, wäre eine Hyperebene eine zweidimensionale Ebene. Und im zweidimensionalen Raum wäre eine Hyperebene einfach eine gerade Linie.
Unten sind Beispiele für Hyperebenen im zwei- und dreidimensionalen Raum.
Daraus folgt aber, dass die Daten linear trennbar sein müssen, was für die meisten reallen Fälle nicht zutrifft. Deswegen kann man den sogenannten Kernel-Trick verwenden. Die Idee dahinter ist, den Vektorraum in einen höherdimensionalen Raum zu überführen, wo die Objekte linear trennbar sind und dort eine Hyperebene zu definieren. Bei der Rücktransformation, wird diese Hyperebene nichtlinear und oft auch nicht zusammenhängend.
Eines der Probleme ist der hohe Rechenaufwand, der bei der Vergrößerung der Dimensionen und der oft nicht intuitiven und nutzlosen Form der Hyperebene nach der Dimensionsreduktion auftritt. Der Kernel-Trick geht damit um, indem er das Skalarprodukt eines höherdimensionalen Raums verwendet, ohne die Merkmale in diesen Raum zu überführen, was viel recheneffizienter ist.
Wir werden den Irisdatensatz hier verwenden und als Bibliotheken benutzen wir sklearn
und numpy
.
import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.svm import SVC # Datensatz laden data = load_iris() features = np.array(data.data) labels = np.array(data.target)
Typischerweise kann man einen SMV Konstruktur mit svm = SVC()
erstellen. SVC
steht für Support Vector Classificator und im Gegensatz zu Support Vector Regressor wird nur für Klassifikationsprobleme verwendet. Mit svm.fit(features, labels)
trainiert man das Modell und mit svm.predict()
erhält man die Vorhersagen für neue Stichprobe.
Interessant an der Implementierung von SVM sind die Kernel-funktionen und Konstruktorparameter, die man anpassen kann. Wir erklären diese Parameter und visualisieren die Unterschiede. Dazu müssen wir eine neue Funktion definieren.
def plot_data(X, y, kernel_fn="linear", C=1.0, degree=3, gamma='auto'): # 2 Grafiken nebeneinander zeigen fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(11,3)) for i in range(2): ax = [ax1, ax2][i] # 2 Mermale für die Anpassung verwenden svm = SVC(kernel=kernel_fn, C=C, degree=degree, gamma=gamma) svm.fit(X[:, 2*i:2*i+2] ,y) # Minimale und Maximale Werte aus X nehmen und ein # Matrix bilden mit alle Koordinaten mit Abstand 0.05 x_min, x_max = X[:, 0 + (2 * i)].min() - 1, X[:, 0 + (2 * i)].max() + 1 y_min, y_max = X[:, 1 + (2 * i)].min() - 1, X[:, 1 + (2 * i)].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.05), np.arange(y_min, y_max, 0.01)) # Vorhersagen nehmen für alle Koordinaten Z = svm.predict(np.c_[xx.ravel(), yy.ravel()]) # Hintergrund färben für jede Punkt aus xx,yy Z = Z.reshape(xx.shape) ax.contourf(xx, yy, Z, cmap=plt.cm.summer) # Daten auch als Punkte plotten ax.scatter(X[:, 0 + (2 * i)], X[:, 1 + (2 * i)], c=y, cmap=plt.cm.summer, edgecolors='black') ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) ax1.set(ylabel='Sepalum Plot', title = "Kernel Function: %s \n C: %.2f \n gamma: %s " % (kernel_fn, C, str(gamma))) ax2.set(ylabel='Petalum Plot') plt.show()
Scikit-learn verfügt über einige Kernel-Funktionen, die man mit einer Zeichenfolge an den Konstruktor übergeben kann. Der einfachste ist der lineare Kernel, der nur dann gut funktioniert, wenn die Daten linear trennbar sind.
Dann gibt es den Polynomial-Kernel, in dem man
zusätzliche Merkmale erstellt, deren Werte Kombinationen bereits
existierender Merkmalen sind. Zum Beispiel ein Datensatz mit Merkmalen [x. y, z]
erhält nach dem Anwenden des linearen Kernels ein zusätzliches Merkmal, und sieht folgendermaßen aus: [x, y, z, x^3 + y^2 + x]
. Vielleicht könnte man die Daten mit Hilfe dieser neunen Dimension besser klassifizieren.
Sklearn verwendet standardmäßig den rbf
-Kernel (radial basis function), der auch als Gauss-Kernel bezeichnet wird. Der ist dazu geeignet, um “geschlossene” Bereiche zu erkennen, also z.B. Kreise im zweidimenisonalen Raum oder Cluster im Allgemeinen. Der rbf
-Kernel ist sehr nützlich, wenn man wenige Merkmale und eine moderate Anzahl von Samples hat, da viele Merkmale den Algorithmus drastisch verlangsamen können. Außerdem, wird dieser Kernel häufiger verwendet, weil normalierweise nichtlineare Kernel genauer als lineare sind.
# hier Kernelfunktion anpassen - 'linear', 'poly' and 'rbf' plot_data(features, labels, kernel_fn="linear") plot_data(features, labels, kernel_fn="rbf") plot_data(features, labels, kernel_fn="poly")
Der gamma
Parameter wird für nichtlineare SVM verwendet und bestimmt, wie weit der
Einfluss jeder einzelnen Stichprobe ist. Mit einem kleineren Gamma
sieht das Modell wie eine SVM mit einem linearem Kernel aus, weil man
nicht gut seine Komplexität bezeichnen kann. Ein großer Gamma bedeutet,
dass die Stützvektoren nur sich selbst beschreiben und dies auch eine
starke Überanpassung bewirkt.
Falls auto
als Gamma-Parameter eingegeben wird, wird das Gamma 1/n_merkmale
sein.
plot_data(features, labels, kernel_fn="rbf", gamma='auto') plot_data(features, labels, kernel_fn="rbf", gamma=0.1) plot_data(features, labels, kernel_fn="rbf", gamma=40)
Der C-Parameter ist die Kost der Klassifizierung und bestimmt die Größe der Strafe, wenn eine Stichprobe falsch klassifiziert wird. Bei einem großen C-Wert wird das Modell streng bestrafft für jede falsche Klassifizierung. Dies führt zu komplexeren Modellen, aber auch zu einer größeren Überanpassungsefahr. Ein kleines C bietet eine glattere Entscheidungsebene, aber Ausreißer werden deshalb falsch klassifiziert.
plot_data(features, labels, kernel_fn="rbf", C=0.1) plot_data(features, labels, kernel_fn="rbf", C=100)