Support Vector Machine

 Load dataset

     ↓

Understand the data

     ↓

Split into training and testing data

     ↓

Create SVM model

      ↓

 Train model

    ↓

Make predictions

    ↓

Evaluate accuracy

    ↓

Confusion matrix / classification report

    ↓

Predict a new flower

Support Vector Machine (SVM) is a supervised machine-learning algorithm mainly used for:

  1. Classification
  2. Regression

For today's hands-on, we will use SVM for classification.

Imagine we have two types of data:

       ○ ○ ○
      ○ ○ ○
--------------------  ← decision boundary

          × × ×
        × × × ×

The SVM tries to find a boundary that separates the classes.

The important idea is:

SVM tries to find the best separating boundary with the largest possible margin between classes.

What is a hyperplane?

In two dimensions, the boundary is a line:

For more dimensions, we call the separating boundary a hyperplane.

For a beginner, remember:

Hyperplane = decision boundary used by SVM to separate classes.

  Class A       |       Class B

   ○ ○ ○        |        × × ×
   ○ ○ ○        |        × × ×
   ○ ○ ○        |        × × ×

                ↑
        Decision boundary

3. What is a margin?

Suppose we have:

○ ○ ○        |        × × ×
○ ○ ○        |        × × ×
             |

SVM doesn't simply want any line that separates the classes.

It wants a boundary that gives the classes the largest possible margin.

Conceptually:

Class A             Class B

○ ○ ○               × × ×
○ ○ ○             × × ×
 ○ ○           × × ×

      |       |
      |       |
      |       |
      ↑
    margin

The data points closest to the decision boundary are called support vectors.

4. What are support vectors?

Suppose

○ ○ ○       ○      |      ×       × × ×
                   |

The points closest to the boundary are especially important. They are called: Support vectors

These points help determine where the decision boundary should be.

So remember:

       SVM
        ↓
 Find decision boundary
        ↓
  Maximize margin
         ↓
Important boundary points = Support Vectors

5. Our practical dataset — Iris

It has four features:

  1. Sepal length
  2. Sepal width
  3. Petal length
  4. Petal width

And three classes:

0 → Setosa

1 → Versicolor

2 → Virginica

So our problem is: Given the measurements of an iris flower, predict which species it belongs to.

STEP 1 — Import libraries

In [1]:
import pandas as pd

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC ### Support Vector Classifier. This is the built-in scikit-learn implementation we will use.


### Evaluation functions
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report

StandardScaler

from sklearn.preprocessing import StandardScaler

SVM is sensitive to the scale of features.

For example:

Age = 20–60

Salary = 20,000–100,000

The values are on very different scales.

Standardization puts features on a comparable scale.

STEP 2 — Load the Iris dataset

In [2]:
iris = load_iris()
print(iris)
{'data': array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       [5. , 3.6, 1.4, 0.2],
       [5.4, 3.9, 1.7, 0.4],
       [4.6, 3.4, 1.4, 0.3],
       [5. , 3.4, 1.5, 0.2],
       [4.4, 2.9, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [5.4, 3.7, 1.5, 0.2],
       [4.8, 3.4, 1.6, 0.2],
       [4.8, 3. , 1.4, 0.1],
       [4.3, 3. , 1.1, 0.1],
       [5.8, 4. , 1.2, 0.2],
       [5.7, 4.4, 1.5, 0.4],
       [5.4, 3.9, 1.3, 0.4],
       [5.1, 3.5, 1.4, 0.3],
       [5.7, 3.8, 1.7, 0.3],
       [5.1, 3.8, 1.5, 0.3],
       [5.4, 3.4, 1.7, 0.2],
       [5.1, 3.7, 1.5, 0.4],
       [4.6, 3.6, 1. , 0.2],
       [5.1, 3.3, 1.7, 0.5],
       [4.8, 3.4, 1.9, 0.2],
       [5. , 3. , 1.6, 0.2],
       [5. , 3.4, 1.6, 0.4],
       [5.2, 3.5, 1.5, 0.2],
       [5.2, 3.4, 1.4, 0.2],
       [4.7, 3.2, 1.6, 0.2],
       [4.8, 3.1, 1.6, 0.2],
       [5.4, 3.4, 1.5, 0.4],
       [5.2, 4.1, 1.5, 0.1],
       [5.5, 4.2, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.2],
       [5. , 3.2, 1.2, 0.2],
       [5.5, 3.5, 1.3, 0.2],
       [4.9, 3.6, 1.4, 0.1],
       [4.4, 3. , 1.3, 0.2],
       [5.1, 3.4, 1.5, 0.2],
       [5. , 3.5, 1.3, 0.3],
       [4.5, 2.3, 1.3, 0.3],
       [4.4, 3.2, 1.3, 0.2],
       [5. , 3.5, 1.6, 0.6],
       [5.1, 3.8, 1.9, 0.4],
       [4.8, 3. , 1.4, 0.3],
       [5.1, 3.8, 1.6, 0.2],
       [4.6, 3.2, 1.4, 0.2],
       [5.3, 3.7, 1.5, 0.2],
       [5. , 3.3, 1.4, 0.2],
       [7. , 3.2, 4.7, 1.4],
       [6.4, 3.2, 4.5, 1.5],
       [6.9, 3.1, 4.9, 1.5],
       [5.5, 2.3, 4. , 1.3],
       [6.5, 2.8, 4.6, 1.5],
       [5.7, 2.8, 4.5, 1.3],
       [6.3, 3.3, 4.7, 1.6],
       [4.9, 2.4, 3.3, 1. ],
       [6.6, 2.9, 4.6, 1.3],
       [5.2, 2.7, 3.9, 1.4],
       [5. , 2. , 3.5, 1. ],
       [5.9, 3. , 4.2, 1.5],
       [6. , 2.2, 4. , 1. ],
       [6.1, 2.9, 4.7, 1.4],
       [5.6, 2.9, 3.6, 1.3],
       [6.7, 3.1, 4.4, 1.4],
       [5.6, 3. , 4.5, 1.5],
       [5.8, 2.7, 4.1, 1. ],
       [6.2, 2.2, 4.5, 1.5],
       [5.6, 2.5, 3.9, 1.1],
       [5.9, 3.2, 4.8, 1.8],
       [6.1, 2.8, 4. , 1.3],
       [6.3, 2.5, 4.9, 1.5],
       [6.1, 2.8, 4.7, 1.2],
       [6.4, 2.9, 4.3, 1.3],
       [6.6, 3. , 4.4, 1.4],
       [6.8, 2.8, 4.8, 1.4],
       [6.7, 3. , 5. , 1.7],
       [6. , 2.9, 4.5, 1.5],
       [5.7, 2.6, 3.5, 1. ],
       [5.5, 2.4, 3.8, 1.1],
       [5.5, 2.4, 3.7, 1. ],
       [5.8, 2.7, 3.9, 1.2],
       [6. , 2.7, 5.1, 1.6],
       [5.4, 3. , 4.5, 1.5],
       [6. , 3.4, 4.5, 1.6],
       [6.7, 3.1, 4.7, 1.5],
       [6.3, 2.3, 4.4, 1.3],
       [5.6, 3. , 4.1, 1.3],
       [5.5, 2.5, 4. , 1.3],
       [5.5, 2.6, 4.4, 1.2],
       [6.1, 3. , 4.6, 1.4],
       [5.8, 2.6, 4. , 1.2],
       [5. , 2.3, 3.3, 1. ],
       [5.6, 2.7, 4.2, 1.3],
       [5.7, 3. , 4.2, 1.2],
       [5.7, 2.9, 4.2, 1.3],
       [6.2, 2.9, 4.3, 1.3],
       [5.1, 2.5, 3. , 1.1],
       [5.7, 2.8, 4.1, 1.3],
       [6.3, 3.3, 6. , 2.5],
       [5.8, 2.7, 5.1, 1.9],
       [7.1, 3. , 5.9, 2.1],
       [6.3, 2.9, 5.6, 1.8],
       [6.5, 3. , 5.8, 2.2],
       [7.6, 3. , 6.6, 2.1],
       [4.9, 2.5, 4.5, 1.7],
       [7.3, 2.9, 6.3, 1.8],
       [6.7, 2.5, 5.8, 1.8],
       [7.2, 3.6, 6.1, 2.5],
       [6.5, 3.2, 5.1, 2. ],
       [6.4, 2.7, 5.3, 1.9],
       [6.8, 3. , 5.5, 2.1],
       [5.7, 2.5, 5. , 2. ],
       [5.8, 2.8, 5.1, 2.4],
       [6.4, 3.2, 5.3, 2.3],
       [6.5, 3. , 5.5, 1.8],
       [7.7, 3.8, 6.7, 2.2],
       [7.7, 2.6, 6.9, 2.3],
       [6. , 2.2, 5. , 1.5],
       [6.9, 3.2, 5.7, 2.3],
       [5.6, 2.8, 4.9, 2. ],
       [7.7, 2.8, 6.7, 2. ],
       [6.3, 2.7, 4.9, 1.8],
       [6.7, 3.3, 5.7, 2.1],
       [7.2, 3.2, 6. , 1.8],
       [6.2, 2.8, 4.8, 1.8],
       [6.1, 3. , 4.9, 1.8],
       [6.4, 2.8, 5.6, 2.1],
       [7.2, 3. , 5.8, 1.6],
       [7.4, 2.8, 6.1, 1.9],
       [7.9, 3.8, 6.4, 2. ],
       [6.4, 2.8, 5.6, 2.2],
       [6.3, 2.8, 5.1, 1.5],
       [6.1, 2.6, 5.6, 1.4],
       [7.7, 3. , 6.1, 2.3],
       [6.3, 3.4, 5.6, 2.4],
       [6.4, 3.1, 5.5, 1.8],
       [6. , 3. , 4.8, 1.8],
       [6.9, 3.1, 5.4, 2.1],
       [6.7, 3.1, 5.6, 2.4],
       [6.9, 3.1, 5.1, 2.3],
       [5.8, 2.7, 5.1, 1.9],
       [6.8, 3.2, 5.9, 2.3],
       [6.7, 3.3, 5.7, 2.5],
       [6.7, 3. , 5.2, 2.3],
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]]), 'target': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]), 'frame': None, 'target_names': array(['setosa', 'versicolor', 'virginica'], dtype='<U10'), 'DESCR': '.. _iris_dataset:\n\nIris plants dataset\n--------------------\n\n**Data Set Characteristics:**\n\n    :Number of Instances: 150 (50 in each of three classes)\n    :Number of Attributes: 4 numeric, predictive attributes and the class\n    :Attribute Information:\n        - sepal length in cm\n        - sepal width in cm\n        - petal length in cm\n        - petal width in cm\n        - class:\n                - Iris-Setosa\n                - Iris-Versicolour\n                - Iris-Virginica\n                \n    :Summary Statistics:\n\n    ============== ==== ==== ======= ===== ====================\n                    Min  Max   Mean    SD   Class Correlation\n    ============== ==== ==== ======= ===== ====================\n    sepal length:   4.3  7.9   5.84   0.83    0.7826\n    sepal width:    2.0  4.4   3.05   0.43   -0.4194\n    petal length:   1.0  6.9   3.76   1.76    0.9490  (high!)\n    petal width:    0.1  2.5   1.20   0.76    0.9565  (high!)\n    ============== ==== ==== ======= ===== ====================\n\n    :Missing Attribute Values: None\n    :Class Distribution: 33.3% for each of 3 classes.\n    :Creator: R.A. Fisher\n    :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)\n    :Date: July, 1988\n\nThe famous Iris database, first used by Sir R.A. Fisher. The dataset is taken\nfrom Fisher\'s paper. Note that it\'s the same as in R, but not as in the UCI\nMachine Learning Repository, which has two wrong data points.\n\nThis is perhaps the best known database to be found in the\npattern recognition literature.  Fisher\'s paper is a classic in the field and\nis referenced frequently to this day.  (See Duda & Hart, for example.)  The\ndata set contains 3 classes of 50 instances each, where each class refers to a\ntype of iris plant.  One class is linearly separable from the other 2; the\nlatter are NOT linearly separable from each other.\n\n|details-start|\n**References**\n|details-split|\n\n- Fisher, R.A. "The use of multiple measurements in taxonomic problems"\n  Annual Eugenics, 7, Part II, 179-188 (1936); also in "Contributions to\n  Mathematical Statistics" (John Wiley, NY, 1950).\n- Duda, R.O., & Hart, P.E. (1973) Pattern Classification and Scene Analysis.\n  (Q327.D83) John Wiley & Sons.  ISBN 0-471-22361-1.  See page 218.\n- Dasarathy, B.V. (1980) "Nosing Around the Neighborhood: A New System\n  Structure and Classification Rule for Recognition in Partially Exposed\n  Environments".  IEEE Transactions on Pattern Analysis and Machine\n  Intelligence, Vol. PAMI-2, No. 1, 67-71.\n- Gates, G.W. (1972) "The Reduced Nearest Neighbor Rule".  IEEE Transactions\n  on Information Theory, May 1972, 431-433.\n- See also: 1988 MLC Proceedings, 54-64.  Cheeseman et al"s AUTOCLASS II\n  conceptual clustering system finds 3 classes in the data.\n- Many, many more ...\n\n|details-end|', 'feature_names': ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'], 'filename': 'iris.csv', 'data_module': 'sklearn.datasets.data'}

STEP 3 — Look at the features and target

In [3]:
print(iris.feature_names)
print(iris.target_names)
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
['setosa' 'versicolor' 'virginica']

STEP 5 — Separate input and output

In [4]:
X_features = iris.data
y_target = iris.target

STEP 6 — Convert to a DataFrame

In [5]:
df = pd.DataFrame(
    iris.data,
    columns=iris.feature_names
)

df["target"] = iris.target

print(df.head())
   sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)  \
0                5.1               3.5                1.4               0.2   
1                4.9               3.0                1.4               0.2   
2                4.7               3.2                1.3               0.2   
3                4.6               3.1                1.5               0.2   
4                5.0               3.6                1.4               0.2   

   target  
0       0  
1       0  
2       0  
3       0  
4       0  

STEP 7 — Split the dataset

In [6]:
X_train, X_test, y_train, y_test = train_test_split(
    X_features,
    y_target,
    test_size=0.2,
    random_state=42
)

STEP 8 — Standardize the data

This step is especially important for SVM.

In [7]:
scaler = StandardScaler()
In [8]:
X_train = scaler.fit_transform(X_train)
There are two operations here:

       fit
        ↓
Learn scaling parameters

  transform
      ↓
 Apply scaling

fit_transform() performs both.

Transform test data

In [9]:
X_test = scaler.transform(X_test)

Notice that we do not use fit_transform() on the test data.

Why?

Because the test set should remain independent.

We learn the scaling information from the training data and apply that same transformation to the test data.

So:

  Training data
       ↓
 fit + transform
       ↓
 Scaled training data

  Testing data
       ↓
 transform only
       ↓
Scaled testing data

Why do we scale data for SVM?

Suppose we have:

Feature A = 1–5 Feature B = 100–10000

Feature B has much larger numerical values.

Since SVM uses distances/margins, feature scale can affect the model.

Standardization generally converts a feature approximately to:

mean = 0 standard deviation = 1

For beginners, remember: Scaling makes the numerical ranges of features comparable, which is important for many SVM applications.

STEP 9 — Create the SVM model

In [10]:
model = SVC(kernel="linear") 
### Create an SVM classifier using a linear decision boundary.

What is a kernel?

This is an important SVM concept.

The kernel determines how SVM creates the decision boundary.

Common options include:

  1. linear
  2. rbf
  3. poly
  4. sigmoid
    Linear kernel
         ↓
Find a linear decision boundary
         ↓
   Separate classes

STEP 10 — Train the model

In [11]:
model.fit(X_train, y_train)
Out[11]:
SVC(kernel='linear')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
X_train → flower measurements y_train → correct species

STEP 11 — Make predictions

In [12]:
y_pred = model.predict(X_test)

STEP 12 — Calculate accuracy

In [13]:
accuracy = accuracy_score(y_test, y_pred)

print("Accuracy:", accuracy)
Accuracy: 0.9666666666666667

STEP 13 — Confusion Matrix

In [14]:
cm = confusion_matrix(y_test, y_pred)

print(cm)
[[10  0  0]
 [ 0  8  1]
 [ 0  0 11]]
Predicted
Setosa Versicolor Virginica
Actual
0 Setosa 10 0 0
1 Versicolor 0 8 1
2 Virginica 0 0 11
In [15]:
print("\nClassification Report:")

print(
    classification_report(
        y_test,
        y_pred,
        target_names=iris.target_names
    )
)
Classification Report:
              precision    recall  f1-score   support

      setosa       1.00      1.00      1.00        10
  versicolor       1.00      0.89      0.94         9
   virginica       0.92      1.00      0.96        11

    accuracy                           0.97        30
   macro avg       0.97      0.96      0.97        30
weighted avg       0.97      0.97      0.97        30

Precision and Recall

Precision = Be precise about your YES predictions.

Precision is the proportion of correct positive predictions among all the positive predictions made by the model.

For example: Model predicts 100 patients → Cancer

90 actually have cancer

10 do not have cancer

So, 90% of the patients predicted as cancer-positive actually have cancer.

  • However, precision also matters because false positives can lead to unnecessary follow-up testing, anxiety, or other interventions.

Recall = Don't miss the actual YES cases.

Recall is the proportion of correct positive predictions among all the actual positive cases.

For example: 100 patients actually have cancer

Model correctly identifies 95

Model misses 5

So, the model detected 95% of the actual cancer cases.

In cancer screening/classification, a false negative means: The model predicts "no cancer" when cancer is actually present.

  • Missing an actual cancer case can have serious consequences, so recall is an important metric to examine.

Complete Code

In [16]:
# =========================================================
# STEP 1: Import Libraries
# =========================================================

import pandas as pd

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report


# =========================================================
# STEP 2: Load Inbuilt Iris Dataset
# =========================================================

iris = load_iris()


# =========================================================
# STEP 3: Understand the Dataset
# =========================================================

print("Feature Names:")
print(iris.feature_names)

print("\nTarget Names:")
print(iris.target_names)

print("\nDataset Shape:")
print(iris.data.shape)


# =========================================================
# STEP 4: Create X and y
# =========================================================

X = iris.data
y = iris.target

print("\nFirst 5 rows:")
print(X[:5])

print("\nFirst 5 target values:")
print(y[:5])


# =========================================================
# STEP 5: Create DataFrame
# =========================================================

df = pd.DataFrame(
    iris.data,
    columns=iris.feature_names
)

df["target"] = iris.target

print("\nDataset:")
print(df.head())


# =========================================================
# STEP 6: Split Dataset
# =========================================================

X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.2,
    random_state=42
)

print("\nTraining Data Shape:")
print(X_train.shape)

print("\nTesting Data Shape:")
print(X_test.shape)


# =========================================================
# STEP 7: Standardize Features
# =========================================================

scaler = StandardScaler()

X_train = scaler.fit_transform(X_train)

X_test = scaler.transform(X_test)


# =========================================================
# STEP 8: Create SVM Model
# =========================================================

model = SVC(
    kernel="linear"
)


# =========================================================
# STEP 9: Train SVM Model
# =========================================================

model.fit(
    X_train,
    y_train
)


# =========================================================
# STEP 10: Make Predictions
# =========================================================

y_pred = model.predict(
    X_test
)


# =========================================================
# STEP 11: Calculate Accuracy
# =========================================================

accuracy = accuracy_score(
    y_test,
    y_pred
)

print("\nAccuracy:")
print(f"{accuracy * 100:.2f}%")


# =========================================================
# STEP 12: Confusion Matrix
# =========================================================

cm = confusion_matrix(
    y_test,
    y_pred
)

print("\nConfusion Matrix:")
print(cm)


# =========================================================
# STEP 13: Classification Report
# =========================================================

print("\nClassification Report:")

print(
    classification_report(
        y_test,
        y_pred,
        target_names=iris.target_names
    )
)


# =========================================================
# STEP 14: Predict a New Flower
# =========================================================

new_flower = [[
    5.1,    # Sepal length
    3.5,    # Sepal width
    1.4,    # Petal length
    0.2     # Petal width
]]


# Scale the new flower
new_flower_scaled = scaler.transform(
    new_flower
)


# Predict
prediction = model.predict(
    new_flower_scaled
)


# Display prediction
print("\nNew Flower Prediction:")

print(
    iris.target_names[prediction[0]]
)
Feature Names:
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

Target Names:
['setosa' 'versicolor' 'virginica']

Dataset Shape:
(150, 4)

First 5 rows:
[[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]
 [4.6 3.1 1.5 0.2]
 [5.  3.6 1.4 0.2]]

First 5 target values:
[0 0 0 0 0]

Dataset:
   sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)  \
0                5.1               3.5                1.4               0.2   
1                4.9               3.0                1.4               0.2   
2                4.7               3.2                1.3               0.2   
3                4.6               3.1                1.5               0.2   
4                5.0               3.6                1.4               0.2   

   target  
0       0  
1       0  
2       0  
3       0  
4       0  

Training Data Shape:
(120, 4)

Testing Data Shape:
(30, 4)

Accuracy:
96.67%

Confusion Matrix:
[[10  0  0]
 [ 0  8  1]
 [ 0  0 11]]

Classification Report:
              precision    recall  f1-score   support

      setosa       1.00      1.00      1.00        10
  versicolor       1.00      0.89      0.94         9
   virginica       0.92      1.00      0.96        11

    accuracy                           0.97        30
   macro avg       0.97      0.96      0.97        30
weighted avg       0.97      0.97      0.97        30


New Flower Prediction:
setosa