Support Vector Machine (svm) Classifier
Data¶
We load training areas from
data/training_areas_ml.geojson.Spectral band values (Green, Red, NIR) are simulated from class-specific reflectance profiles.
The binary target is already encoded:
ml_label = 1(Corn/Maize) vsml_label = 0(Non-Corn).We normalize the features to [0, 1] and split into training and testing sets.
import json
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
OUTPUT_DIR = 'outputs'
os.makedirs(OUTPUT_DIR, exist_ok=True)
# Load training areas
with open('data/training_areas_ml.geojson') as f:
gj = json.load(f)
# Spectral profiles (Green, Red, NIR) per original land cover class
SPECTRAL_PROFILES = {
0: {'mean': [0.12, 0.08, 0.35], 'std': [0.02, 0.02, 0.04]}, # Rice Paddies
1: {'mean': [0.16, 0.07, 0.55], 'std': [0.03, 0.02, 0.05]}, # Corn / Maize
2: {'mean': [0.10, 0.05, 0.65], 'std': [0.02, 0.01, 0.05]}, # Other Vegetation / Trees
3: {'mean': [0.30, 0.35, 0.25], 'std': [0.04, 0.05, 0.04]}, # Built-up / Bare Land
4: {'mean': [0.08, 0.05, 0.04], 'std': [0.02, 0.01, 0.01]}, # Water / Fishponds
}
N_SAMPLES = 100
rng = np.random.default_rng(42)
records = []
for feat in gj['features']:
cl_id = feat['properties']['Cl_Id']
ml_label = feat['properties']['ml_label']
p = SPECTRAL_PROFILES[cl_id]
samples = rng.normal(loc=p['mean'], scale=p['std'], size=(N_SAMPLES, 3)).clip(0, 1)
for s in samples:
records.append({'Green': s[0], 'Red': s[1], 'NIR': s[2], 'Class': ml_label})
data = pd.DataFrame(records)
# Normalize the features (Green, Red, NIR)
scaler = MinMaxScaler()
data[['Green', 'Red', 'NIR']] = scaler.fit_transform(data[['Green', 'Red', 'NIR']])
# Split into training and test sets
X = data[['Green', 'Red', 'NIR']].values
y = data['Class'].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
# Visualize the dataset in 2D (Green vs NIR)
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 2], c=y, cmap='coolwarm', edgecolors='k', alpha=0.7)
plt.xlabel('Green (Normalized)')
plt.ylabel('NIR (Normalized)')
plt.title('Corn/Maize vs Non-Corn Areas (Green vs NIR)')
plt.colorbar(label='Class (0 = Non-Corn, 1 = Corn/Maize)')
plt.savefig(os.path.join(OUTPUT_DIR, 'svm_scatter_green_nir.png'), dpi=150, bbox_inches='tight')
plt.show()
print(data.head())Function for decision boundaries¶
Next, we are going to define a function to plot the decision boundary.
# Define function
def plot_decision_boundary(model, X, y, title):
"""
Plots the decision boundary for a 2D dataset.
"""
x_min, x_max = X[:, 0].min() - 0.1, X[:, 0].max() + 0.1
y_min, y_max = X[:, 1].min() - 0.1, X[:, 1].max() + 0.1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 200),
np.linspace(y_min, y_max, 200))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# Plot into the *current* axes
plt.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', edgecolors='k')
plt.xlabel('Green')
plt.ylabel('NIR')
plt.title(title)
plt.colorbar(label='Class (0 = Non-Corn, 1 = Corn/Maize)')# Import the support vector machine (SVM) classifier
from sklearn.svm import SVC
# Initialize the Support Vector Machine (SVM) classifier with linear kernel
svm_linear_1 = SVC(kernel='linear', C=10, random_state=42) # Regularization parameter C=10
svm_linear_2 = SVC(kernel='linear', C=100, random_state=42) # Regularization parameter C=100
# Train the SVM models using the first two features
svm_linear_1.fit(X_train[:, :2], y_train)
svm_linear_2.fit(X_train[:, :2], y_train)
# Plot both decision boundaries
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
plot_decision_boundary(svm_linear_1, X_train[:, :2], y_train, 'SVM (Linear Kernel) with C=10')
plt.subplot(1, 2, 2)
plot_decision_boundary(svm_linear_2, X_train[:, :2], y_train, 'SVM (Linear Kernel) with C=100')
plt.tight_layout()
plt.savefig(os.path.join(OUTPUT_DIR, 'svm_linear_boundaries.png'), dpi=150, bbox_inches='tight')
plt.show()SVM (Polynomial kernel)¶
Polynomial kernel maps the input space to a higher-degree polynomial feature space. Useful when the relationship between features is non-linear.
Formula:
where d is the degree of the polynomial and c is a free parameter.
# Initialize the SVM classifier with a polynomial kernel
svm_poly_1 = SVC(kernel='poly', degree=3, C=10, random_state=42) # Polynomial degree 3, C=10
svm_poly_2 = SVC(kernel='poly', degree=3, C=100, random_state=42) # Polynomial degree 3, C=100
# Train the SVM models using the first two features
svm_poly_1.fit(X_train[:, :2], y_train)
svm_poly_2.fit(X_train[:, :2], y_train)
# Plot both decision boundaries
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
plot_decision_boundary(svm_poly_1, X_train[:, :2], y_train, 'SVM (Polynomial Kernel) with C=10')
plt.subplot(1, 2, 2)
plot_decision_boundary(svm_poly_2, X_train[:, :2], y_train, 'SVM (Polynomial Kernel) with C=100')
plt.tight_layout()
plt.savefig(os.path.join(OUTPUT_DIR, 'svm_poly_boundaries.png'), dpi=150, bbox_inches='tight')
plt.show()SVM (RBF kernel)¶
Radial basis function (RBF) kernel computes similarity between two points using a Gaussian function.
# Create two SVM classifiers with the RBF kernel and different C values
svm_rbf_c1 = SVC(kernel='rbf', C=10, random_state=42)
svm_rbf_c10 = SVC(kernel='rbf', C=100, random_state=42)
# Train both models on the first two features
svm_rbf_c1.fit(X_train[:, :2], y_train)
svm_rbf_c10.fit(X_train[:, :2], y_train)
# Plot both decision boundaries side by side
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
plot_decision_boundary(svm_rbf_c1, X_train[:, :2], y_train, 'SVM (RBF Kernel), C=10')
plt.subplot(1, 2, 2)
plot_decision_boundary(svm_rbf_c10, X_train[:, :2], y_train, 'SVM (RBF Kernel), C=100')
plt.tight_layout()
plt.savefig(os.path.join(OUTPUT_DIR, 'svm_rbf_boundaries.png'), dpi=150, bbox_inches='tight')
plt.show()# Initialize the SVM classifier with a Sigmoid kernel
svm_sigmoid_1 = SVC(kernel='sigmoid', C=10, gamma='scale', random_state=42) # C=10
svm_sigmoid_2 = SVC(kernel='sigmoid', C=100, gamma='scale', random_state=42) # C=100
# Train the SVM models using the first two features
svm_sigmoid_1.fit(X_train[:, :2], y_train)
svm_sigmoid_2.fit(X_train[:, :2], y_train)
# Plot both decision boundaries
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
plot_decision_boundary(svm_sigmoid_1, X_train[:, :2], y_train, 'SVM (Sigmoid Kernel) with C=10')
plt.subplot(1, 2, 2)
plot_decision_boundary(svm_sigmoid_2, X_train[:, :2], y_train, 'SVM (Sigmoid Kernel) with C=100')
plt.tight_layout()
plt.savefig(os.path.join(OUTPUT_DIR, 'svm_sigmoid_boundaries.png'), dpi=150, bbox_inches='tight')
plt.show()👩🏽💻 TEST IT OUT
How about if we want to check for 5 classes? Try it out with training_areas.geojson.