Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

svm_with_kernels.py

35_kernel_functions/svm_with_kernels.py  ·  175 lines  ·  view on GitHub

"""
SVM with Different Kernels: Complete Example
Shows how to use different kernels in practice
"""
import numpy as np
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

def create_nonlinear_data():
    """
    Create non-linearly separable data (concentric circles)
    """
    np.random.seed(42)
    n_samples = 200
    
    # Inner circle (class 0)
    angles = np.random.uniform(0, 2*np.pi, n_samples//2)
    radii = np.random.uniform(0, 1, n_samples//2)
    x_inner = radii * np.cos(angles)
    y_inner = radii * np.sin(angles)
    inner = np.column_stack([x_inner, y_inner])
    labels_inner = np.zeros(n_samples//2)
    
    # Outer circle (class 1)
    angles = np.random.uniform(0, 2*np.pi, n_samples//2)
    radii = np.random.uniform(2, 3, n_samples//2)
    x_outer = radii * np.cos(angles)
    y_outer = radii * np.sin(angles)
    outer = np.column_stack([x_outer, y_outer])
    labels_outer = np.ones(n_samples//2)
    
    X = np.vstack([inner, outer])
    y = np.hstack([labels_inner, labels_outer])
    
    return X, y

def compare_kernels_on_data():
    """
    Compare different kernels on the same dataset
    """
    print("Comparing Kernels on Non-Linear Data")
    print("=" * 60)
    
    # Create data
    X, y = create_nonlinear_data()
    
    # Scale features (important for SVM!)
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # Split
    X_train, X_test, y_train, y_test = train_test_split(
        X_scaled, y, test_size=0.2, random_state=42
    )
    
    # Try different kernels
    kernels = {
        'Linear': {'kernel': 'linear', 'C': 1.0},
        'Polynomial (degree=2)': {'kernel': 'poly', 'degree': 2, 'C': 1.0},
        'Polynomial (degree=3)': {'kernel': 'poly', 'degree': 3, 'C': 1.0},
        'RBF (gamma=0.1)': {'kernel': 'rbf', 'gamma': 0.1, 'C': 1.0},
        'RBF (gamma=1.0)': {'kernel': 'rbf', 'gamma': 1.0, 'C': 1.0},
        'RBF (gamma=10.0)': {'kernel': 'rbf', 'gamma': 10.0, 'C': 1.0},
    }
    
    results = {}
    
    for name, params in kernels.items():
        svm = SVC(**params)
        svm.fit(X_train, y_train)
        
        train_acc = svm.score(X_train, y_train)
        test_acc = svm.score(X_test, y_test)
        n_support = len(svm.support_vectors_)
        
        results[name] = {
            'train_acc': train_acc,
            'test_acc': test_acc,
            'n_support': n_support
        }
    
    # Print results
    print("\nResults:")
    print(f"{'Kernel':<25} {'Train Acc':<12} {'Test Acc':<12} {'Support Vectors':<15}")
    print("-" * 60)
    
    for name, result in results.items():
        print(f"{name:<25} {result['train_acc']:<12.4f} {result['test_acc']:<12.4f} {result['n_support']:<15}")
    
    print("\nObservations:")
    print("  - Linear kernel: Fails (can't separate circles)")
    print("  - Polynomial: Works (can handle quadratic boundaries)")
    print("  - RBF: Works best (flexible boundaries)")
    print("  - Higher gamma: More support vectors (more complex)")

def kernel_parameter_tuning():
    """
    Show how to tune kernel parameters
    """
    print("\nKernel Parameter Tuning")
    print("=" * 60)
    
    X, y = create_nonlinear_data()
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    X_train, X_test, y_train, y_test = train_test_split(
        X_scaled, y, test_size=0.2, random_state=42
    )
    
    # Tune gamma for RBF
    print("\nTuning RBF Gamma:")
    gammas = [0.001, 0.01, 0.1, 1.0, 10.0]
    
    print(f"{'Gamma':<10} {'Train Acc':<12} {'Test Acc':<12} {'Support Vectors':<15}")
    print("-" * 50)
    
    for gamma in gammas:
        svm = SVC(kernel='rbf', gamma=gamma, C=1.0)
        svm.fit(X_train, y_train)
        
        train_acc = svm.score(X_train, y_train)
        test_acc = svm.score(X_test, y_test)
        n_support = len(svm.support_vectors_)
        
        print(f"{gamma:<10.3f} {train_acc:<12.4f} {test_acc:<12.4f} {n_support:<15}")
    
    print("\nInterpretation:")
    print("  - Low gamma (0.001): Underfitting (too simple)")
    print("  - Medium gamma (0.1-1.0): Good balance")
    print("  - High gamma (10.0): Overfitting (too complex, many support vectors)")

def kernel_trick_visualization():
    """
    Explain kernel trick with example
    """
    print("\nKernel Trick: How It Works")
    print("=" * 60)
    
    print("""
Example: Polynomial Kernel (degree=2)

Original space: x = [x₁, x₂]

Without kernel trick:
  Transform to: φ(x) = [x₁, x₂, x₁², x₂², √2x₁x₂, √2x₁, √2x₂, 1]
  Compute: φ(x) · φ(y) (expensive, 8 dimensions)

With kernel trick:
  Just compute: K(x, y) = (x · y)²
  Same result, but only 2 dimensions!

Why it works:
  φ(x) · φ(y) = (x₁y₁ + x₂y₂)²
              = x₁²y₁² + 2x₁x₂y₁y₂ + x₂²y₂² + ...
  
  This equals: (x · y)² = (x₁y₁ + x₂y₂)²
  
  So we get the same result without computing high-dimensional features!

Benefit:
  - Original: O(d²) computation (d = dimension of φ(x))
  - Kernel: O(d) computation (d = dimension of x)
  - Much faster!
    """)

# Usage
if __name__ == "__main__":
    print("SVM with Kernels: Practical Examples")
    print("=" * 60)
    
    compare_kernels_on_data()
    kernel_parameter_tuning()
    kernel_trick_visualization()