Visualizing SVM with Python
In my previous article, I introduced the idea behind the classification algorithm Support Vector Machine. Here, I’m going to show you a practical application in Python of what I’ve been explaining, and I will do so by using the well-known Iris dataset.
Following the same structure of that article, I will first deal on linearly separable data, then I will move towards no-linearly separable data, so that you can appreciate the power of SVM which lie in the so-called Kernel Trick.
Linearly-Separable Data
For this purpose, I’m going to use only two features and two classes of the Iris dataset (which contains 4 features and 3 classes). To do so, let’s first have a look at the correlation among features, so that we can pick those features and classes which guarantee a linearly-separable demo dataset.
import seaborn as sns
iris = sns.load_dataset("iris")
print(iris.head())
y = iris.species
X = iris.drop('species',axis=1)sns.pairplot(iris, hue="species",palette="bright")
Since I first want to deal with linearly separable and two-class data, I will focus on the graph petal_width-petal_length, for the classes Setosa and Versicolor.
Let’s visualize it in the form we are going to use:
df=iris[(iris['species']!='virginica')]
df=df.drop(['sepal_length','sepal_width'], axis=1)
df.head()
#let's convert categorical values to numerical targetdf=df.replace('setosa', 0)
df=df.replace('versicolor', 1)X=df.iloc[:,0:2]
y=df['species']
plt.scatter(X.iloc[:, 0], X.iloc[:, 1], c=y, s=50, cmap='autumn')
Nice, now let’s train our algorithm:
from sklearn.svm import SVC
model = SVC(kernel='linear', C=1E10)
model.fit(X, y)We can also call and visualize the coordinates of our support vectors:
model.support_vectors_
plt.scatter(X.iloc[:, 0], X.iloc[:, 1], c=y, s=50, cmap='autumn')
plt.scatter(model.support_vectors_[:,0],model.support_vectors_[:,1])
Now let’s visualize all the elements of our algorithm:
ax = plt.gca()plt.scatter(X.iloc[:, 0], X.iloc[:, 1], c=y, s=50, cmap='autumn')xlim = ax.get_xlim()
ylim = ax.get_ylim()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 = model.decision_function(xy).reshape(XX.shape)ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5,
linestyles=['--', '-', '--'])
ax.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=100,
linewidth=1, facecolors='none', edgecolors='k')
plt.show()
As you can see, our algorithm found those coefficients for our hyperplane which maximize the margin.
If you recall, in my last article I’ve been talking about the importance of Support Vectors. These points define a very interesting property of SVM optimization problem: only a few points actually end up in the final solution for creating the vector of parameters w which will define the best hyperplane. Hence, every point that is not a support vector can be moved anytime you want, yet it will not affect our decision boundary parameters.
Here, I’m going to show you the proof of this property, by reducing the size of our sample (yet without removing support vectors).
red_sample=df.sample(frac=0.7)
X=red_sample.iloc[:,0:2]
y=red_sample['species']
plt.scatter(X.iloc[:, 0], X.iloc[:, 1], c=y, s=50, cmap='autumn')
Now, if we train again our SVM here, knowing that the two support vectors are still there, we will obtain exactly the same hyperplane:

That’s because, again, only data which are support vectors end up building the best hyperplane.
Now I’m going to move to no-linearly separable data.
No-Linearly-Separable Data
For this purpose, I’m going to manually generate some no-linearly separable data. I’m doing so, rather than using the previous dataset, so that you can see the kernel function we are going to use (the Radial Basis Function) perfectly working.
from sklearn.datasets.samples_generator import make_circles
X, y = make_circles(100, factor=.1, noise=.1)plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
As you can see, data cannot be segregated by a straight line. Indeed, by using the same linear kernel of above, we obtain the following:
model=SVC(kernel='linear').fit(X, y)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 = model.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(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=100,
linewidth=1, facecolors='none', edgecolors='k')
plt.show()
However, if we were able to move from a 2D to a 3D task, we would be able to lift all the yellow points and then use a plan to segregate the two clouds of points:
from mpl_toolkits import mplot3d#setting the 3rd dimension with RBF centered on the middle clumpr = np.exp(-(X ** 2).sum(1))ax = plt.subplot(projection='3d')
ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('r')
By doing so, we could separate those two classes with a plan. However, working with 3-dimensions on huge datasets is computationally costly in terms of speed. Furthermore, we should make sure that the elevation function we computed (our r) is the one which best put far apart our two classes.
Luckily, we do not need to worry about these pitfalls, we don’t even have to compute the third dimension. It is implicitly done by our SVM algorithm through the kernel trick, and it is done in such a way that the two caveats above are bypassed.
So, if we simply fit our model with kernel=rbf rather than kernel=linear:
model=SVC(kernel='rbf').fit(X, y)we obtain the following:

As you can see, without making any further computation, but simply changing one parameter of our model, we converted a no-linear problem to a linear one, thanks to the kernel trick.






