web-dev-qa-db-ger.com

Zeichnen einer Entscheidungsgrenze zwischen zwei Klassen mit Matplotlibs Pyplot

Ich könnte wirklich einen Tipp verwenden, um mir zu helfen, eine Entscheidungsgrenze zu erstellen, um sie in Datenklassen zu trennen. Ich habe einige Beispieldaten (aus einer Gaußschen Distribution) über Python NumPy erstellt. In diesem Fall ist jeder Datenpunkt eine 2D-Koordinate, d. H. Ein 1-Spaltenvektor, der aus zwei Zeilen besteht. Z.B.,

[ 1
  2 ]

Nehmen wir an, ich habe 2 Klassen, Klasse1 und Klasse2, und ich habe 100 Datenpunkte für Klasse1 und 100 Datenpunkte für Klasse2 mit dem folgenden Code erstellt (den Variablen x1_samples und x2_samples zugewiesen).

mu_vec1 = np.array([0,0])
cov_mat1 = np.array([[2,0],[0,2]])
x1_samples = np.random.multivariate_normal(mu_vec1, cov_mat1, 100)
mu_vec1 = mu_vec1.reshape(1,2).T # to 1-col vector

mu_vec2 = np.array([1,2])
cov_mat2 = np.array([[1,0],[0,1]])
x2_samples = np.random.multivariate_normal(mu_vec2, cov_mat2, 100)
mu_vec2 = mu_vec2.reshape(1,2).T

Wenn ich die Datenpunkte für jede Klasse zeichne, würde dies folgendermaßen aussehen:

enter image description here

Nun habe ich eine Gleichung für eine Entscheidungsgrenze aufgestellt, um beide Klassen zu trennen, und möchte sie der Grafik hinzufügen. Ich bin mir jedoch nicht wirklich sicher, wie ich diese Funktion plotten kann:

def decision_boundary(x_vec, mu_vec1, mu_vec2):
    g1 = (x_vec-mu_vec1).T.dot((x_vec-mu_vec1))
    g2 = 2*( (x_vec-mu_vec2).T.dot((x_vec-mu_vec2)) )
    return g1 - g2

Ich würde mich über jede Hilfe sehr freuen!

BEARBEITEN: Intuitiv (wenn ich richtig rechnen würde) würde ich erwarten, dass die Entscheidungsgrenze etwas wie diese rote Linie aussieht, wenn ich die Funktion plante ...

enter image description here

34
Sebastian

Das waren einige tolle Vorschläge, vielen Dank für Ihre Hilfe! Am Ende habe ich die Gleichung analytisch gelöst und dies ist die Lösung, mit der ich am Ende gelandet bin (ich möchte sie nur als künftige Referenz veröffentlichen:

enter image description here

Und den Code finden Sie hier

BEARBEITEN:

Ich habe auch eine Komfortfunktion zum Zeichnen von Entscheidungsbereichen für Klassifizierer, die eine fit- und predict-Methode implementieren, z. B. die Klassifizierer in scikit-learn, was nützlich ist, wenn die Lösung nicht analytisch gefunden werden kann. Eine detailliertere Beschreibung, wie es funktioniert, finden Sie hier .

enter image description here

7
Sebastian

Ihre Frage ist komplizierter als eine einfache Zeichnung: Sie müssen die Kontur zeichnen, um den Abstand zwischen den Klassen zu maximieren. Glücklicherweise ist dies ein gut erforschtes Gebiet, insbesondere für das SVM-Machine Learning.

Die einfachste Methode ist das Herunterladen des Moduls scikit-learn, das viele coole Methoden zum Zeichnen von Grenzen bietet: http://scikit-learn.org/stable/modules/svm.html

Code:

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib
from matplotlib import pyplot as plt
import scipy
from sklearn import svm


mu_vec1 = np.array([0,0])
cov_mat1 = np.array([[2,0],[0,2]])
x1_samples = np.random.multivariate_normal(mu_vec1, cov_mat1, 100)
mu_vec1 = mu_vec1.reshape(1,2).T # to 1-col vector

mu_vec2 = np.array([1,2])
cov_mat2 = np.array([[1,0],[0,1]])
x2_samples = np.random.multivariate_normal(mu_vec2, cov_mat2, 100)
mu_vec2 = mu_vec2.reshape(1,2).T


fig = plt.figure()


plt.scatter(x1_samples[:,0],x1_samples[:,1], marker='+')
plt.scatter(x2_samples[:,0],x2_samples[:,1], c= 'green', marker='o')

X = np.concatenate((x1_samples,x2_samples), axis = 0)
Y = np.array([0]*100 + [1]*100)

C = 1.0  # SVM regularization parameter
clf = svm.SVC(kernel = 'linear',  gamma=0.7, C=C )
clf.fit(X, Y)

Lineare Darstellung (entnommen aus http://scikit-learn.org/stable/auto_examples/svm/plot_svm_margin.html )


w = clf.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - (clf.intercept_[0]) / w[1]

plt.plot(xx, yy, 'k-')

enter image description here

MultiLinear Plot (entnommen aus http://scikit-learn.org/stable/auto_examples/svm/plot_iris.html )


C = 1.0  # SVM regularization parameter
clf = svm.SVC(kernel = 'rbf',  gamma=0.7, C=C )
clf.fit(X, Y)

h = .02  # step size in the mesh
# 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
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))


# Plot the decision boundary. For that, we will assign a color to each
# point in the mesh [x_min, m_max]x[y_min, y_max].
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

# Put the result into a color plot
Z = Z.reshape(xx.shape)
plt.contour(xx, yy, Z, cmap=plt.cm.Paired)

enter image description here

Implementierung

Wenn Sie es selbst implementieren möchten, müssen Sie die folgende quadratische Gleichung lösen: boundary equation

Der Wikipedia-Artikel

Für nichtlineare Grenzen wie die von Ihnen gezeichneten ist es leider ein schwieriges Problem, wenn Sie sich auf einen Kernel-Trick stützen, aber es gibt keine klare Lösung.

32
lucasg

Basierend auf der Art und Weise, wie Sie decision_boundary geschrieben haben, sollten Sie die contour-Funktion verwenden, wie Joe es oben angegeben hat. Wenn Sie nur die Begrenzungslinie wünschen, können Sie eine einzige Kontur auf der Ebene 0 zeichnen:

f, ax = plt.subplots(figsize=(7, 7))
c1, c2 = "#3366AA", "#AA3333"
ax.scatter(*x1_samples.T, c=c1, s=40)
ax.scatter(*x2_samples.T, c=c2, marker="D", s=40)
x_vec = np.linspace(*ax.get_xlim())
ax.contour(x_vec, x_vec,
           decision_boundary(x_vec, mu_vec1, mu_vec2),
           levels=[0], cmap="Greys_r")

Was macht:

enter image description here

16
mwaskom

Sie können Ihre eigene Gleichung für die Grenze erstellen:

enter image description here

wo Sie die Positionen x0 und y0 sowie die Konstanten ai und bi für die Radiusgleichung finden müssen Sie haben also 2*(n+1)+2-Variablen. Die Verwendung von scipy.optimize.leastsq ist für diese Art von Problem unkompliziert.

Der unten angefügte Code bildet das Residuum für die leastsq, mit der die Punkte benachteiligt werden, die die Grenze überschreiten. Das Ergebnis für Ihr Problem erhalten Sie mit:

x, y = find_boundary(x2_samples[:,0], x2_samples[:,1], n)
ax.plot(x, y, '-k', lw=2.)

x, y = find_boundary(x1_samples[:,0], x1_samples[:,1], n)
ax.plot(x, y, '--k', lw=2.)

mit n=1: enter image description here

mit n=2:

enter image description here

usng n=5: enter image description here

mit n=7:

enter image description here

import numpy as np
from numpy import sin, cos, pi
from scipy.optimize import leastsq

def find_boundary(x, y, n, plot_pts=1000):

    def sines(theta):
        ans = np.array([sin(i*theta)  for i in range(n+1)])
        return ans

    def cosines(theta):
        ans = np.array([cos(i*theta)  for i in range(n+1)])
        return ans

    def residual(params, x, y):
        x0 = params[0]
        y0 = params[1]
        c = params[2:]

        r_pts = ((x-x0)**2 + (y-y0)**2)**0.5

        thetas = np.arctan2((y-y0), (x-x0))
        m = np.vstack((sines(thetas), cosines(thetas))).T
        r_bound = m.dot(c)

        delta = r_pts - r_bound
        delta[delta>0] *= 10

        return delta

    # initial guess for x0 and y0
    x0 = x.mean()
    y0 = y.mean()

    params = np.zeros(2 + 2*(n+1))
    params[0] = x0
    params[1] = y0
    params[2:] += 1000

    popt, pcov = leastsq(residual, x0=params, args=(x, y),
                         ftol=1.e-12, xtol=1.e-12)

    thetas = np.linspace(0, 2*pi, plot_pts)
    m = np.vstack((sines(thetas), cosines(thetas))).T
    c = np.array(popt[2:])
    r_bound = m.dot(c)
    x_bound = x0 + r_bound*cos(thetas)
    y_bound = y0 + r_bound*sin(thetas)

    return x_bound, y_bound
8

Ich habe gerade ein sehr ähnliches Problem mit einem anderen Ansatz (root-Suche) gelöst und wollte diese Alternative hier als Antwort veröffentlichen, um später darauf zu verweisen:

   def discr_func(x, y, cov_mat, mu_vec):
        """
        Calculates the value of the discriminant function for a dx1 dimensional
        sample given covariance matrix and mean vector.

        Keyword arguments:
            x_vec: A dx1 dimensional numpy array representing the sample.
            cov_mat: numpy array of the covariance matrix.
            mu_vec: dx1 dimensional numpy array of the sample mean.

        Returns a float value as result of the discriminant function.

        """
        x_vec = np.array([[x],[y]])

        W_i = (-1/2) * np.linalg.inv(cov_mat)
        assert(W_i.shape[0] > 1 and W_i.shape[1] > 1), 'W_i must be a matrix'

        w_i = np.linalg.inv(cov_mat).dot(mu_vec)
        assert(w_i.shape[0] > 1 and w_i.shape[1] == 1), 'w_i must be a column vector'

        omega_i_p1 = (((-1/2) * (mu_vec).T).dot(np.linalg.inv(cov_mat))).dot(mu_vec)
        omega_i_p2 = (-1/2) * np.log(np.linalg.det(cov_mat))
        omega_i = omega_i_p1 - omega_i_p2
        assert(omega_i.shape == (1, 1)), 'omega_i must be a scalar'

        g = ((x_vec.T).dot(W_i)).dot(x_vec) + (w_i.T).dot(x_vec) + omega_i
        return float(g)

    #g1 = discr_func(x, y, cov_mat=cov_mat1, mu_vec=mu_vec_1)
    #g2 = discr_func(x, y, cov_mat=cov_mat2, mu_vec=mu_vec_2)

    x_est50 = list(np.arange(-6, 6, 0.1))
    y_est50 = []
    for i in x_est50:
        y_est50.append(scipy.optimize.bisect(lambda y: discr_func(i, y, cov_mat=cov_est_1, mu_vec=mu_est_1) - \
                          discr_func(i, y, cov_mat=cov_est_2, mu_vec=mu_est_2), -10,10))
    y_est50 = [float(i) for i in y_est50]

Hier ist das Ergebnis: (Blau der quadratische Fall, Rot der lineare Fall (gleiche Varianzen) http://i.imgur.com/T16awxM.png?1

1
Sebastian

Ich mag die mglearn-Bibliothek, um Entscheidungsgrenzen zu ziehen. Hier ein Beispiel aus dem Buch "Einführung in das maschinelle Lernen mit Python" von A. Mueller:

fig, axes = plt.subplots(1, 3, figsize=(10, 3))
for n_neighbors, ax in Zip([1, 3, 9], axes):
    clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(X, y)
    mglearn.plots.plot_2d_separator(clf, X, fill=True, eps=0.5, ax=ax, alpha=.4)
    mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
    ax.set_title("{} neighbor(s)".format(n_neighbors))
    ax.set_xlabel("feature 0")
    ax.set_ylabel("feature 1")
axes[0].legend(loc=3)

 enter image description here

0
Junior Koch

Wenn Sie scikit learn verwenden möchten, können Sie Ihren Code folgendermaßen schreiben:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression

# read data
data = pd.read_csv('ex2data1.txt', header=None)
X = data[[0,1]].values
y = data[2]


# use LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X, y)

# Coefficient of the features in the decision function. (from theta 1 to theta n)
parameters = log_reg.coef_[0]
# Intercept (a.k.a. bias) added to the decision function. (theta 0)
parameter0 = log_reg.intercept_

# Plotting the decision boundary
fig = plt.figure(figsize=(10,7))
x_values = [np.min(X[:, 1] -5 ), np.max(X[:, 1] +5 )]
# calcul y values
y_values = np.dot((-1./parameters[1]), (np.dot(parameters[0],x_values) + parameter0))
colors=['red' if l==0 else 'blue' for l in y]
plt.scatter(X[:, 0], X[:, 1], label='Logistics regression', color=colors)
plt.plot(x_values, y_values, label='Decision Boundary')
plt.show()

decision boundary

siehe: Aufbau einer logistischen Regression mit Scikit lernen

0
anasmorahhib

Ich weiß, dass diese Frage analytisch sehr gründlich beantwortet wurde. Ich wollte nur einen möglichen "Hack" an das Problem weitergeben. Es ist schwerfällig, erledigt aber die Arbeit.

Beginnen Sie mit dem Erstellen eines Maschennetzes aus dem 2D-Bereich und erstellen Sie dann anhand des Klassifizierers eine Klassenzuordnung des gesamten Bereichs. Anschließend können Sie Änderungen in der Zeilenreihenfolge erkennen und die Kantenpunkte in einer Liste speichern und die Punkte streuen. 

def disc(x):   # returns the class of the point based on location x = [x,y]
     temp = 0.5  + 0.5*np.sign(disc0(x)-disc1(x)) 
# disc0() and disc1() are the discriminant functions of the respective classes
     return 0*temp + 1*(1-temp) 

num = 200
a = np.linspace(-4,4,num)
b = np.linspace(-6,6,num)
X,Y = np.meshgrid(a,b)

def decColor(x,y):
     temp = np.zeros((num,num))
     print x.shape, np.size(x,axis=0)
     for l in range(num):
         for m in range(num):
             p = np.array([x[l,m],y[l,m]])
             #print p
             temp[l,m] = disc(p)
     return temp
boundColorMap = decColor(X,Y)

group = 0
boundary = []
for x in range(num):
    group = boundColorMap[x,0]
    for y in range(num):
        if boundColorMap[x,y]!=group:
            boundary.append([X[x,y],Y[x,y]])
            group = boundColorMap[x,y]  
boundary = np.array(boundary)

Sample Decision Boundary für einen einfachen bivariaten Gaußschen Klassifizierer

0
Krishna Praveen