Wie zeichne ich die Hyperebenen für SVM One-Versus-All?
Ich habe versucht, die Hyperebenen zu zeichnen, als SVM-OVA wie folgt durchgeführt wurde:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.svm import SVC
x = np.array([[1,1.1],[1,2],[2,1]])
y = np.array([0,100,250])
classifier = OneVsRestClassifier(SVC(kernel='linear'))
Basierend auf der Antwort auf diese Frage Plot Hyperplane Linear SVM Python habe ich den folgenden Code geschrieben:
fig, ax = plt.subplots()
# create a mesh to plot in
x_min, x_max = x[:, 0].min() - 1, x[:, 0].max() + 1
y_min, y_max = x[:, 1].min() - 1, x[:, 1].max() + 1
xx2, yy2 = np.meshgrid(np.arange(x_min, x_max, .2),np.arange(y_min, y_max, .2))
Z = classifier.predict(np.c_[xx2.ravel(), yy2.ravel()])
Z = Z.reshape(xx2.shape)
ax.contourf(xx2, yy2, Z, cmap=plt.cm.winter, alpha=0.3)
ax.scatter(x[:, 0], x[:, 1], c=y, cmap=plt.cm.winter, s=25)
# First line: class1 vs (class2 U class3)
w = classifier.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - (classifier.intercept_[0]) / w[1]
ax.plot(xx,yy)
# Second line: class2 vs (class1 U class3)
w = classifier.coef_[1]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - (classifier.intercept_[1]) / w[1]
ax.plot(xx,yy)
# Third line: class 3 vs (class2 U class1)
w = classifier.coef_[2]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - (classifier.intercept_[2]) / w[1]
ax.plot(xx,yy)
Dies ist jedoch, was ich erhalten habe:

Die Linien sind eindeutig falsch: Tatsächlich scheinen die Winkelkoeffizienten korrekt zu sein, aber nicht die Abschnitte. Insbesondere wäre die orange Linie korrekt, wenn sie um 0,5 nach unten übersetzt würde, die grüne, wenn sie um 0,5 nach links übersetzt würde, und die blaue Linie, wenn sie um 1,5 nach oben übersetzt würde.
Bin ich falsch, die Linien zu zeichnen, oder funktioniert der Klassifikator aufgrund der wenigen Trainingspunkte nicht richtig?
Antworten
Das Problem ist, dass der C
Parameter von SVC
zu klein ist (standardmäßig 1.0
). Nach diesem Beitrag ,
Umgekehrt führt ein sehr kleiner Wert von C dazu, dass der Optimierer nach einer Hyperebene mit größerem Rand sucht, selbst wenn diese Hyperebene mehr Punkte falsch klassifiziert.
Daher besteht die Lösung darin, beispielsweise eine viel größere C
zu verwenden1e5
import matplotlib.pyplot as plt
import numpy as np
from sklearn.svm import SVC
from sklearn.multiclass import OneVsRestClassifier
x = np.array([[1,1.1],[1,2],[2,1]])
y = np.array([0,100,250])
classifier = OneVsRestClassifier(SVC(C=1e5,kernel='linear'))
classifier.fit(x,y)
fig, ax = plt.subplots()
# create a mesh to plot in
x_min, x_max = x[:, 0].min() - 1, x[:, 0].max() + 1
y_min, y_max = x[:, 1].min() - 1, x[:, 1].max() + 1
xx2, yy2 = np.meshgrid(np.arange(x_min, x_max, .2),np.arange(y_min, y_max, .2))
Z = classifier.predict(np.c_[xx2.ravel(), yy2.ravel()])
Z = Z.reshape(xx2.shape)
ax.contourf(xx2, yy2, Z, cmap=plt.cm.winter, alpha=0.3)
ax.scatter(x[:, 0], x[:, 1], c=y, cmap=plt.cm.winter, s=25)
def reconstruct(w,b):
k = - w[0] / w[1]
b = - b[0] / w[1]
if k >= 0:
x0 = max((y_min-b)/k,x_min)
x1 = min((y_max-b)/k,x_max)
else:
x0 = max((y_max-b)/k,x_min)
x1 = min((y_min-b)/k,x_max)
if np.abs(x0) == np.inf: x0 = x_min
if np.abs(x1) == np.inf: x1 = x_max
xx = np.linspace(x0,x1)
yy = k*xx+b
return xx,yy
xx,yy = reconstruct(classifier.coef_[0],classifier.intercept_[0])
ax.plot(xx,yy,'r')
xx,yy = reconstruct(classifier.coef_[1],classifier.intercept_[1])
ax.plot(xx,yy,'g')
xx,yy = reconstruct(classifier.coef_[2],classifier.intercept_[2])
ax.plot(xx,yy,'b')
Dieses Mal C
sieht das Ergebnis besser aus , da ein viel größeres übernommen wird
