Wie zeichne ich ein dreidimensionales Python-Level-Set?
Ich habe einige Probleme beim Zeichnen des Bildes in meinem Kopf. Ich möchte den Kernel-Trick mit Support Vector Machines visualisieren. Also habe ich einige zweidimensionale Daten erstellt, die aus zwei Kreisen bestehen (einem inneren und einem äußeren Kreis), die durch eine Hyperebene getrennt werden sollten. Offensichtlich ist dies in zwei Dimensionen nicht möglich - also habe ich sie in 3D umgewandelt. Sei n die Anzahl der Abtastwerte. Jetzt habe ich ein (n, 3) -Array (3 Spalten, n Zeilen) X von Datenpunkten und ein (n, 1) -Array y mit Beschriftungen. Mit sklearn erhalte ich den linearen Klassifikator über
clf = svm.SVC(kernel='linear', C=1000)
clf.fit(X, y)
Ich zeichne die Datenpunkte bereits als Streudiagramm über
plt.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=plt.cm.Paired)
Jetzt möchte ich die trennende Hyperebene als Flächendiagramm darstellen. Mein Problem hierbei ist die fehlende explizite Darstellung der Hyperebene, da die Entscheidungsfunktion nur eine implizite Hyperebene über ergibt decision_function = 0. Daher muss ich den Levelsatz (von Level 0) eines 4-dimensionalen Objekts zeichnen.
Da ich kein Python-Experte bin, würde ich mich freuen, wenn mir jemand helfen könnte! Und ich weiß, dass dies nicht wirklich der "Stil" der Verwendung einer SVM ist, aber ich brauche dieses Bild als Illustration für meine Diplomarbeit.
Edit: mein aktueller "Code"
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_blobs, make_circles
from tikzplotlib import save as tikz_save
plt.close('all')
# we create 50 separable points
#X, y = make_blobs(n_samples=40, centers=2, random_state=6)
X, y = make_circles(n_samples=50, factor=0.5, random_state=4, noise=.05)
X2, y2 = make_circles(n_samples=50, factor=0.2, random_state=5, noise=.08)
X = np.append(X,X2, axis=0)
y = np.append(y,y2, axis=0)
# shifte X to [0,2]x[0,2]
X = np.array([[item[0] + 1, item[1] + 1] for item in X])
X[X<0] = 0.01
clf = svm.SVC(kernel='rbf', C=1000)
clf.fit(X, y)
plt.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=plt.cm.Paired)
# plot the decision function
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# create grid to evaluate model
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = clf.decision_function(xy).reshape(XX.shape)
# plot decision boundary and margins
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--','-','--'])
# plot support vectors
ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=100,
linewidth=1, facecolors='none', edgecolors='k')
################## KERNEL TRICK - 3D ##################
trans_X = np.array([[item[0]**2, item[1]**2, np.sqrt(2*item[0]*item[1])] for item in X])
fig = plt.figure()
ax = plt.axes(projection ="3d")
# creating scatter plot
ax.scatter3D(trans_X[:,0],trans_X[:,1],trans_X[:,2], c = y, cmap=plt.cm.Paired)
clf2 = svm.SVC(kernel='linear', C=1000)
clf2.fit(trans_X, y)
ax = plt.gca(projection='3d')
xlim = ax.get_xlim()
ylim = ax.get_ylim()
zlim = ax.get_zlim()
### from here i don't know what to do ###
xx = np.linspace(xlim[0], xlim[1], 3)
yy = np.linspace(ylim[0], ylim[1], 3)
zz = np.linspace(zlim[0], zlim[1], 3)
ZZ, YY, XX = np.meshgrid(zz, yy, xx)
xyz = np.vstack([XX.ravel(), YY.ravel(), ZZ.ravel()]).T
Z = clf2.decision_function(xyz).reshape(XX.shape)
#ax.contour(XX, YY, ZZ, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--','-','--'])
Gewünschte Ausgabe
Ich möchte so etwas wie bekommen , dass . Im Allgemeinen möchte ich rekonstruieren, was sie in diesem Artikel tun , insbesondere "Nichtlineare Transformationen".
Antworten
Ein Teil Ihrer Frage wird in dieser Frage zu SVM mit linearem Kernel behandelt . Dies ist eine teilweise Antwort, da nur lineare Kernel auf diese Weise dargestellt werden können, dh dank Hyperebenenkoordinaten, auf die bei Verwendung des linearen Kernels über den Schätzer zugegriffen werden kann.
Eine andere Lösung besteht darin, die Isofläche mit zu finden marching_cubes
Diese Lösung beinhaltet die Installation des scikit-imageToolkits (https://scikit-image.org), mit dem eine Isofläche mit einem bestimmten Wert (hier habe ich 0 berücksichtigt, da sie den Abstand zur Hyperebene darstellt) aus dem Netzgitter der 3D-Koordinaten gefunden werden kann.
Im folgenden Code (von Ihrem kopiert) implementiere ich die Idee für jeden Kernel (im Beispiel habe ich den RBF-Kernel verwendet), und die Ausgabe wird unter dem Code angezeigt. Bitte beachten Sie meine Fußnote zum 3D-Plotten mit matplotlib, was in Ihrem Fall ein weiteres Problem sein kann.
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from skimage import measure
from sklearn.datasets import make_blobs, make_circles
from tikzplotlib import save as tikz_save
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
plt.close('all')
# we create 50 separable points
#X, y = make_blobs(n_samples=40, centers=2, random_state=6)
X, y = make_circles(n_samples=50, factor=0.5, random_state=4, noise=.05)
X2, y2 = make_circles(n_samples=50, factor=0.2, random_state=5, noise=.08)
X = np.append(X,X2, axis=0)
y = np.append(y,y2, axis=0)
# shifte X to [0,2]x[0,2]
X = np.array([[item[0] + 1, item[1] + 1] for item in X])
X[X<0] = 0.01
clf = svm.SVC(kernel='rbf', C=1000)
clf.fit(X, y)
plt.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=plt.cm.Paired)
# plot the decision function
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# create grid to evaluate model
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = clf.decision_function(xy).reshape(XX.shape)
# plot decision boundary and margins
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--','-','--'])
# plot support vectors
ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=100,
linewidth=1, facecolors='none', edgecolors='k')
################## KERNEL TRICK - 3D ##################
trans_X = np.array([[item[0]**2, item[1]**2, np.sqrt(2*item[0]*item[1])] for item in X])
fig = plt.figure()
ax = plt.axes(projection ="3d")
# creating scatter plot
ax.scatter3D(trans_X[:,0],trans_X[:,1],trans_X[:,2], c = y, cmap=plt.cm.Paired)
clf2 = svm.SVC(kernel='rbf', C=1000)
clf2.fit(trans_X, y)
z = lambda x,y: (-clf2.intercept_[0]-clf2.coef_[0][0]*x-clf2.coef_[0][1]*y) / clf2.coef_[0][2]
ax = plt.gca(projection='3d')
xlim = ax.get_xlim()
ylim = ax.get_ylim()
zlim = ax.get_zlim()
### from here i don't know what to do ###
xx = np.linspace(xlim[0], xlim[1], 50)
yy = np.linspace(ylim[0], ylim[1], 50)
zz = np.linspace(zlim[0], zlim[1], 50)
XX ,YY, ZZ = np.meshgrid(xx, yy, zz)
xyz = np.vstack([XX.ravel(), YY.ravel(), ZZ.ravel()]).T
Z = clf2.decision_function(xyz).reshape(XX.shape)
# find isosurface with marching cubes
dx = xx[1] - xx[0]
dy = yy[1] - yy[0]
dz = zz[1] - zz[0]
verts, faces, _, _ = measure.marching_cubes_lewiner(Z, 0, spacing=(1, 1, 1), step_size=2)
verts *= np.array([dx, dy, dz])
verts -= np.array([xlim[0], ylim[0], zlim[0]])
# add as Poly3DCollection
mesh = Poly3DCollection(verts[faces])
mesh.set_facecolor('g')
mesh.set_edgecolor('none')
mesh.set_alpha(0.3)
ax.add_collection3d(mesh)
ax.view_init(20, -45)
plt.savefig('kerneltrick')
Durch Ausführen des Codes wird mit Matplotlib das folgende Bild erzeugt, wobei die grüne halbtransparente Oberfläche die nichtlineare Entscheidungsgrenze darstellt.
Fußnote: 3D-Plotten mit Matplotlib
Beachten Sie, dass Matplotlib 3D in einigen Fällen die "Tiefe" von Objekten nicht verwalten kann, da dies in Konflikt mit der zorderdieses Objekts stehen kann. Dies ist der Grund, warum die Hyperebene manchmal so aussieht, als würde sie "über" den Punkten gezeichnet, selbst wenn sie "hinter" sein sollte. Dieses Problem ist ein bekannter Fehler, der in der Dokumentation zu matplotlib 3d und in dieser Antwort behandelt wird .
Wenn Sie bessere Renderergebnisse erzielen möchten, können Sie Mayavi , wie von den Matplotlib-Entwicklern empfohlen, oder eine andere 3D-Python-Plotbibliothek verwenden.