Итак, если вы устали копать кучу бесполезных ссылок, желая найти подробное объяснение того, как построить границу решения и линии маржи с помощью алгоритма Support Vector Machine, мои поздравления - вы, наконец, нашли его! И последний шаг - прочтите мое простое руководство по этой теме. Давайте начнем!

На основе: https://scikit-learn.org/stable/auto_examples/svm/plot_svm_margin.html

Пожалуйста, не проходите мимо указанной выше ссылки. Возможно, это поможет вам лучше разобраться в этой теме.

0. Пусть модель учится!

Я уверен, что вы уже знакомы с этим шагом. Здесь мы создаем набор данных, затем разделяем его на обучающие и тестовые образцы и, наконец, обучаем модель с sklearn.svm.SVC(kernel='linear'). Обратите внимание, что мы используем именно тип ядра linear (ссылка на примеры). Внимательно прочтите комментарии:

import numpy as np
from sklearn.svm import SVC
# Creating a random dataset of 2,000 samples and only 2 features
# (for 2–dimensional space). And yeah, it's a binary classification
# here (`y` contains two classes: 0 and 1).
X, y = make_classification(n_samples=2000, n_features=2,
                           n_informative=2, n_redundant=0,
                           n_classes=2,
                           random_state=32)
# Splitting our dataset by train and test parts.
# `stratify` is here to make our splitting balanced
# in terms of classes.
X_train, X_test, y_train, y_test = train_test_split(X, y,
                                   test_size=0.3, stratify=y,
                                   random_state=32)
# And here we train our model. IMPORTANT: we use kernel='linear'.
svc_model = SVC(kernel='linear', random_state=32)
svc_model.fit(X_train, y_train)

Хорошо! Модель обучена, и теперь вы хотите построить границу принятия решения (гиперплоскость), но подождите… вы не знаете, с чего начать. Не волнуйтесь, я буду вести вас шаг за шагом.

1. Что нам нужно для построения границы принятия решения?

Нам понадобится его общее уравнение:

Примечание. поскольку мы работаем в двухмерном пространстве, граница принятия решения будет представлена ​​двухмерной прямой линией, поэтому w и x оба состоят из двух элементов.

И вот в чем фокус. Преобразуем скалярное произведение векторов w и x, а затем решаем уравнение для второго элемента x:

В результате мы получили классическое линейное уравнение в форме пересечения наклона, которое в нашем случае описывает двумерную прямую линию. Хорошо, мы поняли, но у вас, вероятно, остались вопросы, например:

  1. Где именно w_1 и w_2 хранятся в нашем sklearn объекте модели?
  2. а что такое x_1 и x_2?

Посмотрите на картинку:

w содержится в атрибуте coef_ нашей модели (svc_model.coef_), и это координаты вектора нормали к границе нашего решения (этот вектор ортогонален гиперплоскости).

b - это атрибут intercept_ нашей модели (svc_model.intercept_).

x_1 - это массив x-точек координатной плоскости.

x_2 - это результирующий массив y-точек координатной плоскости.

Как вы заметили, у нас всего 2 элемента для w и столько же для x. Это потому, что мы изучаем классификацию SVM в 2-мерном пространстве, чтобы сделать идею построения границ и полей решений SVM более ясной.

2. Соберите все вместе

Просто сделайте некоторые замены в формуле 2D-линии ...

… И в коде мы получаем:

import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(10, 8))
# Plotting our two-features-space
sns.scatterplot(x=X_train[:, 0], 
                y=X_train[:, 1], 
                hue=y_train, 
                s=8);
# Constructing a hyperplane using a formula.
w = svc_model.coef_[0]           # w consists of 2 elements
b = svc_model.intercept_[0]      # b consists of 1 element
x_points = np.linspace(-1, 1)    # generating x-points from -1 to 1
y_points = -(w[0] / w[1]) * x_points - b / w[1]  # getting corresponding y-points
# Plotting a red hyperplane
plt.plot(x_points, y_points, c='r');

Результатом этого является:

Здорово! Граница принятия решения нанесена! Итак, пора перейти к марже SVM.

4. Построение полей SVM

Здесь я создал изображение границы решения и полей. Серые пунктирные линии - это «границы полей», которые демонстрируют, на чем они основаны и как рассчитать их величину. Внимательно посмотрите на все аспекты:

Итак, пунктирные линии - это просто линия границы решения, сдвинутая вдоль направления вектора w на расстояние, равное margin. И мы можем сделать это с помощью этого алгоритма:

Step 1. Find a normal vector to the decision boundary;
Step 2. Calculate a unit vector of that normal vector -- let's call it `w_hat`;
Step 3. Get a distance between the lines (margin);
Step 4. Translate all points of the decision boundary to a new location by this formula:
# for a line above
new_points_above = hyperplane_points + w_hat * margin
# for a line below
new_points_below = hyperplane_points - w_hat * margin

Шаг 1: Мы уже знаем, что w - это нормальный вектор, который нам нужен - он всегда ортогонален гиперплоскости (кстати, вот почему - ссылка на stackoverflow). И мы также знаем, что он содержится в атрибуте coef_ нашей модели (svc_model.coef_).

Шаг 2. Рассчитаем единичный вектор:

# Calculating the unit vector of w
w_hat = svc_model.coef_[0] / (np.sqrt(np.sum(svc_model.coef_[0] ** 2)))

Шаг 3. Теперь рассчитаем маржу:

# Calculating margin
margin = 1 / np.sqrt(np.sum(svc_model.coef_[0] ** 2))

Шаг 4: Наконец, мы вычисляем точки новых линий:

# Calculating margin lines
new_points_up   = hyperplane_points + w_hat * margin
new_points_down = hyperplane_points - w_hat * margin

И еще кое-что ... Ага, давайте соберем все вместе и построим график результата:

plt.figure(figsize=(10, 8))
# Plotting our two-features-space
sns.scatterplot(x=X_train[:, 0], 
                y=X_train[:, 1], 
                hue=y_train, 
                s=8);
# Constructing a hyperplane using a formula.
w = svc_model.coef_[0]           # w consists of 2 elements
b = svc_model.intercept_[0]      # b consists of 1 element
x_points = np.linspace(-1, 1)    # generating x-points from -1 to 1
y_points = -(w[0] / w[1]) * x_points - b / w[1]  # getting corresponding y-points
# Plotting a red hyperplane
plt.plot(x_points, y_points, c='r');
# Encircle support vectors
plt.scatter(svc_model.support_vectors_[:, 0],
            svc_model.support_vectors_[:, 1], 
            s=50, 
            facecolors='none', 
            edgecolors='k', 
            alpha=.5);
# Step 2 (unit-vector):
w_hat = svc_model.coef_[0] / (np.sqrt(np.sum(svc_model.coef_[0] ** 2)))
# Step 3 (margin):
margin = 1 / np.sqrt(np.sum(svc_model.coef_[0] ** 2))
# Step 4 (calculate points of the margin lines):
decision_boundary_points = np.array(list(zip(x_points, y_points)))
points_of_line_above = decision_boundary_points + w_hat * margin
points_of_line_below = decision_boundary_points - w_hat * margin
# Plot margin lines
# Blue margin line above
plt.plot(points_of_line_above[:, 0], 
         points_of_line_above[:, 1], 
         'b--', 
         linewidth=2)
# Green margin line below
plt.plot(points_of_line_below[:, 0], 
         points_of_line_below[:, 1], 
         'g--',
         linewidth=2)

И готово! Надеюсь, это было полезно. Нажмите thumbs up 👍, если это было.

Спасибо за уделенное время! 🎉
Подпишитесь на меня, чтобы узнать больше о других интересных темах 😉👍