← All courses CSIS 3352 · Module 8

Hands-on Classification with Support Vector Machines (SVM)

📅 Assignment 4 (50 pts) — individual in-class SVM presentation, due Tue Oct 20 · On the Midterm (Project, Modules 1–9) — moved to Tue Oct 27
📌 The Lecture 8 deck states no learning objectives. The 19 objectives below are derived from the deck's sections and the class notebook's steps — 5 groups: margin & support vectors · soft margin, C & kernels · math, trade-offs & uses · code: prep & train · code: evaluate & plot. Each objective's bullets are also its Discussion rubric.
📌 Assignment 4: the syllabus pairs it with Module 8 (SVM), worth 50 pts. It's an individual ~5-minute, 5-slide presentation to the class, due Tue Oct 20 (per Michael; not yet in Dean's Oct 11 Blackboard scan). Dr. Al Amin encourages AI use on this one: the grade is on the live presentation and Q&A (presenting earns 95; up to 100 at his judgment). The syllabus otherwise prohibits generative AI on assignments.

Study Guide

Module 8 · 7 parts · Lecture 8 deck + the class notebook
📌 Parts 1–5 come from the Lecture 8 deck. Parts 6–7 walk through the class notebook (Social_Network_Ads.csv), whose outputs were reproduced to confirm every number. The notebook has no written explanations, so code behavior comes from the scikit-learn / NumPy / Matplotlib documentation (links on the Notes tab). All diagrams are redrawn for study. Purple Study aid boxes are memory tricks, OOP reminders, extra runs on the class data, and links to earlier modules — not lecture content.

What it is

  • Support Vector Machine (SVM) — a powerful supervised learning algorithm used mainly for classification. A version for regression, SVR, also exists.
  • What it finds — the best possible boundary, a hyperplane, that separates two classes.
  • "Best" — the boundary with the maximum possible margin between the classes.
  • Where it shines — high-dimensional spaces, and data where there's a clear gap between the classes.
⚠️ Not spelled out in the source: the lecture says "SVR" without expanding it. It stands for Support Vector Regression (scikit-learn documentation).
🧩 Study aid — "hyperplane" in plain words. With 2 features the boundary is a line; with 3 features it's a flat plane; with more features it's the same flat idea in more dimensions. "Hyperplane" is the general name. The class notebook has 2 features (Age, EstimatedSalary), so its hyperplane is a line.

The core idea: maximize the margin

Class 0Class 1boundarymarginsupport vector Feature 1Feature 2Margin
  • Many lines could work — lots of lines separate the two classes above. SVM picks the one with the largest margin.
  • Margin — the distance between the boundary and the closest data points from each class (the gap between the dashed lines).
  • Why wider is better — a wider margin generally means better generalization to new, unseen data.

Support vectors: what gives SVM its name

  • Support vectors — the data points closest to the decision boundary (the ringed points above).
  • They set the boundary — they directly determine its position and orientation.
  • Everyone else — every other point could be removed without changing the boundary at all.
  • Efficient predictions — only the support vectors matter.
⚠️ Clarification beyond the lecture (scikit-learn documentation). The lecture's pictures show a clean gap, where the support vectors are just the points touching the dashed lines. With a soft margin (Part 2), points that sit inside the margin or on the wrong side also count as support vectors. That's why the class notebook's model ends up with 128 support vectors out of 300 training points (64 per class): the classes overlap. Keep the lecture's definition for exams.
💡 Study aid — memory hook. The support vectors are the tent poles. Pull out any other stake and the tent stays up; move a pole and the whole tent shifts.

Hard margin vs. soft margin

Hard margin — perfectly separableSoft margin — overlapping classesyellow ring = margin violator (allowed)no point allowed inside the margin
Hard marginSoft margin
RulePerfect separation — no points allowed inside the marginSome points may be misclassified or inside the margin
Works onOnly perfectly separable dataReal, noisy data
UsedRarelyAlmost always — real data is rarely perfectly separable

The C parameter

SettingMisclassificationMargin
Large CPunished heavilyNarrower
Small CMore flexibility allowedWider
  • Where it shows up in code — the notebook never sets C, so it uses the default printed in the model: C=1.0.
  • Tuning C — one of SVM's weaknesses (Part 5): it can be tricky.
🧪 Study aid — extra run on the class data (not in the notebook). Same split, same scaling, linear kernel, only C changed. Margin is in scaled units. It matches the lecture: as C grows, the margin narrows.
CMargin width (2/||w||)Support vectorsTest accuracy
0.012.762060.87
0.11.461480.89
1 (notebook)1.071280.90
101.031260.89
1001.021250.89
A wider margin catches more points inside it, so small C has more support vectors. One split of 100 test people is too small to call any C "best" — it just shows the direction.

The problem: data that isn't linearly separable

Original space — not linearly separableAfter mapping — a flat cut worksdashed circle = SVM’s curved boundarynew feature z = (distance from center)²flat separating cut
  • The problem — real data is often not linearly separable in its original form (left: one class inside a ring of the other).
  • The kernel trick — maps the data into a higher-dimensional space, where a linear boundary can separate it.
  • Efficient — it happens mathematically, without ever directly computing the new coordinates.
  • Result — SVM can draw curved, complex decision boundaries back in the original space.
📌 The lecture draws this as a 3D picture and doesn't name the mapping. The side view above uses "squared distance from the center" as the extra feature only to show the idea; it's a study aid, not a formula from class.

Common kernel types

KernelWhat it doesscikit-learn
LinearNo transformation — used when data is already linearly separablekernel='linear' ← the class notebook
PolynomialCurved boundaries from polynomial combinations of featureskernel='poly'
RBF (Radial Basis Function)The most popular choice for complex, nonlinear boundarieskernel='rbf'
SigmoidBehaves similarly to a neural network activation functionkernel='sigmoid'
📌 The scikit-learn column comes from the scikit-learn documentation, as does this: the printed degree=3 is the polynomial kernel's degree and is ignored by the linear kernel. Also, scikit-learn's own default kernel is 'rbf'; the notebook overrides it with 'linear'.
🧪 Study aid — extra run: every kernel on the class data (not in the notebook). Same split and scaling, all other settings left at their defaults (no tuning).
KernelConfusion matrixAccuracyMissed buyers (FN)
linear (notebook)[[66 2] [8 24]]0.908
poly[[67 1] [13 19]]0.8613
rbf[[64 4] [3 29]]0.933
sigmoid[[54 14] [12 20]]0.7412
The lecture calls RBF the most popular choice for nonlinear boundaries, and on this data it does best — the same score as K-NN in Module 7. The purchase pattern isn't a straight line (Part 7). Untuned defaults on one small split, so don't read these as each kernel's real ceiling.

Decision boundary and classification rule

Feature 1 (x₁)Feature 2 (x₂)w·x + b = 0w·x + b = −1w·x + b = +1wmargin = 2 / ||w||w·x + b < 0 → Class 0w·x + b > 0 → Class 1
PieceFormulaMeaning
Decision boundaryw · x + b = 0The hyperplane itself
wweight vectorPerpendicular to the boundary
xinput featuresThe point being classified
bbiasShifts the boundary
Classification ruley = sign(w · x + b)Which side of the boundary a point falls on determines its predicted class
Margin width2 / ||w||So maximizing the margin = minimizing ||w||
TrainingoptimizationFind the best w and b
⚠️ Not in the lecture: the ±1 lines in the drawing and the fact that sign returns −1 or +1. The usual math convention labels the classes −1 and +1 and puts the margin edges at w·x + b = ±1, which is where the 2 / ||w|| comes from. scikit-learn maps those back to your labels, so the notebook still prints 0 and 1. ("||w||" means the length of w.)
🧪 Study aid — the notebook's actual w and b (read from the trained model, not printed in class). After .fit, classifier.coef_ = w ≈ [1.60, 0.97] and classifier.intercept_ = b ≈ −0.77, in scaled units. So ||w|| ≈ 1.87 and the margin ≈ 2 / 1.87 ≈ 1.07. Age has the bigger weight, so it tilts the line more than salary does.
Check the single prediction: age 30, $87,000 scales to about (−0.81, 0.51). Then w·x + b ≈ 1.60(−0.81) + 0.97(0.51) − 0.77 ≈ −1.57 → negative → Class 0, matching the notebook's [0].
🧩 Study aid — the trailing underscore. In scikit-learn, attributes ending in _ (coef_, intercept_, support_vectors_, n_support_) only exist after .fit — they're what the object learned. Same idea as Java instance fields that a method fills in.

Strengths vs. weaknesses

StrengthsWeaknesses
Effective in high-dimensional spacesSlow to train on very large datasets
Memory-efficient — only uses the support vectorsPerformance drops with lots of noise or overlapping classes
Versatile, thanks to different kernel choicesChoosing the right kernel and tuning C can be tricky
Works well with a clear margin of separationDoesn't directly output probabilities
📌 "Doesn't directly output probabilities" shows up in the notebook's printout as probability=False. Per the scikit-learn documentation, setting probability=True makes it estimate probabilities with an extra internal cross-validation step, which slows training — consistent with the lecture's "not directly."
💡 Study aid — pairs to remember. Each strength has a matching weakness: clear margin ↔ noise/overlap; versatile kernels ↔ tricky kernel and C choice.

Real-world use cases

Use caseWhat gets classified
Text classification & spam filteringDocuments and emails
Image classification & face detectionImages
BioinformaticsGenes and proteins
Handwriting recognitionHandwritten characters
🔐 Study aid — course tie-in. Spam filtering is a security task, and the course text is AI for Cybersecurity. The lecture doesn't go further than naming it.

Key takeaways

  • SVM finds the maximum-margin boundary between classes.
  • Support vectors are the few points that define that boundary.
  • Soft margins and kernels make it flexible for real, messy, non-linear data.

The whole notebook on one line

import→ load CSV → X, y→ split 75 / 25→ scale→ SVC .fit (find w, b)→ predict (which side?)→ confusion matrix + accuracy→ plot train & test
🧩 Study aid — third time, same pipeline. Modules 6, 7 and 8 run the same notebook on the same CSV. Only the classifier lines change (plus plot titles and the grid step). Part 7 compares all three.

1 · Import the libraries

import numpy as np import matplotlib.pyplot as plt import pandas as pd

2 · Import the dataset → features X, target y

dataset = pd.read_csv('Social_Network_Ads.csv') X = dataset.iloc[:, :-1].values # features: every column but the last y = dataset.iloc[:, -1].values # target: the last column
ColumnRoleRange in the CSV
Agefeature → X18 – 60
EstimatedSalaryfeature → X15,000 – 150,000
Purchasedtarget → y0 (didn't buy) or 1 (bought)
  • Uppercase X — the features (a 2-D table: 400 rows × 2 columns). Lowercase y — the target (one column of 0s and 1s).
  • 400 rows, 143 of them buyers.

3 · Split into training and test sets

from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 0)
train · 300 rows (75%)
test · 100
  • test_size = 0.25 — 25% held out: 100 test people (32 buyers, 68 non-buyers).
  • random_state = 0 — the same split every run, so these numbers are repeatable (and match Modules 6–7).

4 · Feature scaling

from sklearn.preprocessing import StandardScaler sc = StandardScaler() X_train = sc.fit_transform(X_train) # learn mean & std from TRAIN, then scale X_test = sc.transform(X_test) # reuse the train numbers — never fit on test
FeatureTraining meanTraining stdExample: raw → scaled
Age≈ 38.1≈ 10.144 → 0.58
EstimatedSalary≈ 69,583≈ 34,49139,000 → −0.89
📌 The mean and std aren't printed in the notebook; they were read from the fitted scaler (sc.mean_, sc.scale_) on the same split. The example row is the first training person, whose raw and scaled values both appear in the notebook's printouts.
⚠️ Not explained in the source: the lecture doesn't say why SVM needs scaling. Study-aid reasoning: the margin is a distance, so a feature measured in tens of thousands (salary) would outweigh one measured in tens (age) unless both are put on the same scale.

5 · Create and train the SVM classifier

from sklearn.svm import SVC classifier = SVC(kernel = 'linear', random_state = 0) classifier.fit(X_train, y_train)
SVC(C=1.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape='ovr', degree=3, gamma='scale', kernel='linear', max_iter=-1, probability=False, random_state=0, shrinking=True, tol=0.001, verbose=False)
PieceWhat it meansLecture link
SVCscikit-learn's class for SVM classification"used mainly for classification"
kernel = 'linear'Straight-line boundary, no transformationLinear kernel
random_state = 0Repeatable results—
C=1.0 (default)Soft-margin penaltyThe C parameter
probability=False (default)No probability output"Doesn't directly output probabilities"
.fit(X_train, y_train)Solves the optimization for the best w and b"Training an SVM"
📌 The meanings of the printed arguments come from the scikit-learn documentation (Notes tab); the notebook has no written explanations. Newer scikit-learn versions print only the arguments you changed, e.g. SVC(kernel='linear', random_state=0).
🧩 Study aid — class → object → method. SVC is the class (the blueprint). classifier = SVC(...) creates an object (an instance) and the arguments go to its constructor, like new SVC(...) in Java. .fit and .predict are methods called on that object. It's the same .fit / .predict pair as LogisticRegression and KNeighborsClassifier, which is why swapping models only changes these lines.
🧪 Study aid — see the support vectors (not in the notebook). classifier.n_support_ → [64 64] (128 support vectors). classifier.support_vectors_ holds those 128 points. The classes overlap, so many points sit inside the soft margin (Part 1 note).

6 · Predict a single new result

print(classifier.predict(sc.transform([[30, 87000]])))
[0]
  • Read it inside-out — [[30, 87000]] (one person, 2-D: 1 row × 2 features) → sc.transform (scale with the training numbers) → classifier.predict (which side of the boundary?) → [0].
  • Meaning — a 30-year-old earning $87,000 is predicted not to purchase.
  • Why double brackets — scikit-learn always expects a 2-D input (rows × features), even for one row.

7 · Predict the test set results

y_pred = classifier.predict(X_test) print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
  • Output — 100 rows of [predicted actual]. A row like [0 1] is a missed buyer; [1 0] is a false alarm.
  • reshape(len, 1) turns each flat list into a column; concatenate(..., 1) glues the two columns side by side.

8 · Confusion matrix and accuracy

from sklearn.metrics import confusion_matrix, accuracy_score cm = confusion_matrix(y_test, y_pred) # actual first, predicted second print(cm) accuracy_score(y_test, y_pred)
[[66 2] [ 8 24]] 0.9
Predicted 0Predicted 1
Actual 0TN = 66
correctly said "won't buy"
FP = 2
said "buy" — didn't
Actual 1FN = 8
said "won't buy" — did
TP = 24
correctly said "buy"
  • Accuracy — (TP + TN) / total = (24 + 66) / 100 = 0.9.
  • Mistakes — 2 + 8 = 10 of 100.
  • Row totals — 66 + 2 = 68 actual 0s; 8 + 24 = 32 actual 1s (matches y_test).
⚠️ Layout reminder (same trap as Modules 6–7). scikit-learn prints class 0 first: [[TN FP] [FN TP]]. The layout comes from the scikit-learn documentation; the notebook doesn't label it.
💡 Study aid — Module 5 formulas on this result (not computed in class). Precision = TP / (TP + FP) = 24 / 26 ≈ 0.92. Recall = TP / (TP + FN) = 24 / 32 = 0.75. F1 = 2·P·R / (P + R) ≈ 0.83. Few false alarms, but it misses a quarter of the buyers.

9 · Visualize the training and test set results

from matplotlib.colors import ListedColormap X_set, y_set = sc.inverse_transform(X_train), y_train # A · back to real units (X_test, y_test for the test plot) X1, X2 = np.meshgrid(np.arange(start = X_set[:, 0].min() - 10, stop = X_set[:, 0].max() + 10, step = 0.25), np.arange(start = X_set[:, 1].min() - 1000, stop = X_set[:, 1].max() + 1000, step = 0.25)) # B · grid plt.contourf(X1, X2, classifier.predict(sc.transform(np.array([X1.ravel(), X2.ravel()]).T)).reshape(X1.shape), alpha = 0.75, cmap = ListedColormap(('red', 'green'))) # C · color regions plt.xlim(X1.min(), X1.max()) plt.ylim(X2.min(), X2.max()) for i, j in enumerate(np.unique(y_set)): plt.scatter(X_set[y_set == j, 0], X_set[y_set == j, 1], c = ListedColormap(('red', 'green'))(i), label = j) # D · dots plt.title('SVM (Training set)') # 'SVM (Test set)' in the second cell plt.xlabel('Age') plt.ylabel('Estimated Salary') plt.legend() plt.show()
PieceWhat it does
A · sc.inverse_transform(...)Undoes the scaling so the axes show real ages and salaries.
B · np.meshgrid(...), step = 0.25Builds a grid of (age, salary) points 10 years past the age range and 1,000 past the salary range, in steps of 0.25. That's 248 ages × 548,000 salaries ≈ 136 million points — 16× more than Module 7's step = 1.
C · plt.contourf(...)Flattens the grid, scales it, predicts every point with the SVM, folds the answers back into the grid shape, and paints the background: red = predicted 0, green = predicted 1.
D · plt.scatter loopPlots each real person in their true color: red for 0, green for 1.
Class notebook output: SVM (Training set). Age on the horizontal axis from about 8 to 70, Estimated Salary on the vertical axis from about 14,000 to 151,000. A single straight line splits the chart: red (predicted 0) covers the lower left, green (predicted 1) the upper right. The line runs from about age 29 at the top of the chart down to about age 52 at the bottom. Red and green dots are the 300 training people; some green dots sit in the red region at the lower right, older people with lower salaries. Class notebook output: SVM (Test set). Same straight-line split, red lower left and green upper right, with the 100 test people on top. Most dots match their background. Several green dots, older buyers with salaries between about 20,000 and 45,000, sit in the red region; two red dots sit in the green region. Those are the 8 missed buyers and 2 false alarms.
  • Boundary shape — a straight line, because the kernel is linear. It runs from young people with high salaries down to older people with low salaries.
  • Pattern — younger, lower-salary people fall in red (didn't purchase); older people and high earners fall in green.
  • Misclassified points — dots on the wrong color. On the test plot: the 2 false positives (red dots on green) and 8 false negatives (green dots on red).
  • Where the line fails — 5 of the 8 missed buyers are aged 45–49 earning $22,000–$41,000: a group of older, lower-income buyers below the line. A straight line can't bend to include them.
  • The warning above each chart — the same harmless Matplotlib c-argument warning as Modules 6–7.
📌 The missed-buyer ages and salaries come from matching the notebook's [predicted actual] printout to its X_test printout. The grid size was computed from the code.
💡 Study aid — slow plot ≠ slow SVM. In the notebook's saved run, each plotting cell took about 2½ minutes. That's the 136-million-point grid, not the model: the lecture says SVM is efficient at prediction time, but no model is instant across 136 million points.

10 · Logistic regression vs. K-NN vs. SVM — same data

Logistic regression (M6)K-NN, K = 5 (M7)SVM, linear (M8)
Model lineLogisticRegression(random_state = 0)KNeighborsClassifier(n_neighbors = 5, metric = 'minkowski', p = 2)SVC(kernel = 'linear', random_state = 0)
What .fit doesLearns weights and a biasStores the training data (lazy)Finds the maximum-margin w and b
BoundaryStraight lineIrregular, jaggedStraight line
Confusion matrix[[65 3] [8 24]][[64 4] [3 29]][[66 2] [8 24]]
Accuracy0.890.930.90
Missed buyers (FN)838
False alarms (FP)342
New person (30, $87,000)000
  • The two straight-line models — almost identical; the SVM trades one fewer false alarm for the same 8 missed buyers.
  • Why K-NN wins here — its flexible boundary can reach the older, lower-income buyers a straight line misses.
  • The kernel connection — an RBF-kernel SVM on the same data scores 0.93 with [[64 4] [3 29]] (Part 3 extra run): the curved boundary the lecture promises.
📌 The logistic regression and K-NN numbers come from the Module 6 and 7 class notebooks (same CSV, same split, same scaling), confirmed by rerunning them. The lecture doesn't compare models; this table is a study aid.

Flashcards

Question
Answer

Practice Quiz

📌 Picture questions show the class notebook's own charts or this page's redrawn diagrams. Fill-in-the-blank answers must match exactly (case doesn't matter).

Concept Check

Explain each objective in your own words, then self-grade against the rubric

Notes Template

Module 8 · Print this tab for handwritten recall practice

📚 Reference documentation (official — for lookup, not page content)

What SVM is · supervised · hyperplane

Margin: definition + why wider

Support vectors: what & why they matter

Hard vs. soft margin

C: large vs. small

Kernel trick in 3 sentences

4 kernels + when to use

w · x + b = 0 — label every symbol

sign rule + margin = 2 / ||w||

4 strengths

4 weaknesses

4 real-world uses

SVC(...) arguments + printed defaults

Single prediction: [[30, 87000]] → ? (why)

sklearn confusion matrix: [[? ?] [? ?]]

LogReg vs. K-NN vs. SVM