Итак, если вы устали копать кучу бесполезных ссылок, желая найти подробное объяснение того, как построить границу решения и линии маржи с помощью алгоритма 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
:
В результате мы получили классическое линейное уравнение в форме пересечения наклона, которое в нашем случае описывает двумерную прямую линию. Хорошо, мы поняли, но у вас, вероятно, остались вопросы, например:
- Где именно
w_1
иw_2
хранятся в нашемsklearn
объекте модели? - а что такое
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 abovenew_points_above = hyperplane_points + w
_hat * margin # for a line belownew_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 👍
, если это было.
Спасибо за уделенное время! 🎉
Подпишитесь на меня, чтобы узнать больше о других интересных темах 😉👍