ОП.10 Машинное обучение
Практическая работа №7
Тема: Метод опорных векторов (SVM)
Цель работы: Научиться применять метод опорных векторов (SVM) для задач классификации и регрессии, подбирать параметры ядра, настраивать гиперпараметры.
Время выполнения: 2 академических часа
Инструменты: Python + Jupyter Notebook, sklearn, matplotlib, seaborn
ЗАДАНИЕ ДЛЯ СТУДЕНТА
Этап 1. Создание данных и базовая SVM (15 минут)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_classification, make_moons, load_breast_cancer
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.svm import SVC, SVR
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# Создание линейно разделимых данных
X, y = make_classification(n_samples=200, n_features=2, n_redundant=0,
n_clusters_per_class=1, random_state=42, n_classes=2)
# Нормализация
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Визуализация данных
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', s=50)
plt.title('Исходные данные (линейно разделимые)')
plt.xlabel('Признак 1')
plt.ylabel('Признак 2')
# Разделение на выборки
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.3, random_state=42)
# Обучение SVM с линейным ядром
svm_linear = SVC(kernel='linear', random_state=42)
svm_linear.fit(X_train, y_train)
y_pred = svm_linear.predict(X_test)
# Визуализация разделяющей границы
def plot_decision_boundary(X, y, model, ax):
h = 0.02
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
ax.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', s=50, edgecolors='black')
ax.set_xlabel('Признак 1')
ax.set_ylabel('Признак 2')
plt.subplot(1, 2, 2)
plot_decision_boundary(X_test, y_test, svm_linear, plt.gca())
plt.title(f'SVM (линейное ядро) \nAccuracy: {accuracy_score(y_test, y_pred):.3f}')
plt.tight_layout()
plt.show()
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"Количество опорных векторов: {len(svm_linear.support_)}")
Вопрос: Что такое опорные векторы? Как они влияют на разделяющую границу?
Этап 2. SVM для нелинейных данных (15 минут)
# Создание нелинейных данных (полумесяцы)
X_moons, y_moons = make_moons(n_samples=200, noise=0.1, random_state=42)
# Нормализация
X_moons_scaled = scaler.fit_transform(X_moons)
# Разделение
X_train_m, X_test_m, y_train_m, y_test_m = train_test_split(X_moons_scaled, y_moons, test_size=0.3, random_state=42)
# SVM с разными ядрами
kernels = ['linear', 'poly', 'rbf', 'sigmoid']
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
axes = axes.flatten()
for i, kernel in enumerate(kernels):
svm = SVC(kernel=kernel, random_state=42)
svm.fit(X_train_m, y_train_m)
y_pred = svm.predict(X_test_m)
acc = accuracy_score(y_test_m, y_pred)
axes[i].scatter(X_test_m[:, 0], X_test_m[:, 1], c=y_pred, cmap='coolwarm', s=50, alpha=0.7)
axes[i].set_title(f'kernel={kernel}, Accuracy={acc:.3f}')
axes[i].set_xlabel('Признак 1')
axes[i].set_ylabel('Признак 2')
plt.tight_layout()
plt.show()
print("Сравнение ядер на нелинейных данных:")
for kernel in kernels:
svm = SVC(kernel=kernel, random_state=42)
scores = cross_val_score(svm, X_moons_scaled, y_moons, cv=5)
print(f"{kernel}: mean accuracy = {scores.mean():.4f} (+/- {scores.std():.4f})")
Вопрос: Какое ядро показало лучший результат на нелинейных данных? Почему?
Этап 3. Влияние параметра C (15 минут)
# Эксперимент с разными значениями C
C_values = [0.01, 0.1, 1, 10, 100, 1000]
train_scores = []
test_scores = []
plt.figure(figsize=(14, 5))
for i, C in enumerate(C_values):
svm = SVC(kernel='rbf', C=C, random_state=42)
svm.fit(X_train_m, y_train_m)
train_acc = svm.score(X_train_m, y_train_m)
test_acc = svm.score(X_test_m, y_test_m)
train_scores.append(train_acc)
test_scores.append(test_acc)
# Визуализация для некоторых C
if i in [0, 2, 4]:
plt.subplot(1, 3, i//2 + 1)
plot_decision_boundary(X_test_m, y_test_m, svm, plt.gca())
plt.title(f'C={C}\nTrain acc={train_acc:.3f}, Test acc={test_acc:.3f}')
plt.tight_layout()
plt.show()
# График зависимости качества от C
plt.figure(figsize=(10, 6))
plt.semilogx(C_values, train_scores, 'o-', label='Обучающая выборка', linewidth=2)
plt.semilogx(C_values, test_scores, 's-', label='Тестовая выборка', linewidth=2)
plt.xlabel('Параметр C (логарифмическая шкала)')
plt.ylabel('Accuracy')
plt.title('Влияние параметра C на качество SVM')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
print("Результаты при разных C:")
for C, train_acc, test_acc in zip(C_values, train_scores, test_scores):
print(f"C={C}: Train={train_acc:.4f}, Test={test_acc:.4f}")
Вопрос: Как влияет увеличение параметра C на качество модели? Что означает большое/малое C?
Этап 4. Влияние параметра gamma (15 минут)
# Эксперимент с разными значениями gamma
gamma_values = [0.001, 0.01, 0.1, 1, 10, 100]
train_scores_g = []
test_scores_g = []
plt.figure(figsize=(14, 5))
for i, gamma in enumerate(gamma_values):
svm = SVC(kernel='rbf', gamma=gamma, C=1, random_state=42)
svm.fit(X_train_m, y_train_m)
train_acc = svm.score(X_train_m, y_train_m)
test_acc = svm.score(X_test_m, y_test_m)
train_scores_g.append(train_acc)
test_scores_g.append(test_acc)
# Визуализация для некоторых gamma
if i in [0, 2, 4]:
plt.subplot(1, 3, i//2 + 1)
plot_decision_boundary(X_test_m, y_test_m, svm, plt.gca())
plt.title(f'gamma={gamma}\nTrain acc={train_acc:.3f}, Test acc={test_acc:.3f}')
plt.tight_layout()
plt.show()
# График зависимости качества от gamma
plt.figure(figsize=(10, 6))
plt.semilogx(gamma_values, train_scores_g, 'o-', label='Обучающая выборка', linewidth=2)
plt.semilogx(gamma_values, test_scores_g, 's-', label='Тестовая выборка', linewidth=2)
plt.xlabel('Параметр gamma (логарифмическая шкала)')
plt.ylabel('Accuracy')
plt.title('Влияние параметра gamma на качество SVM')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
print("Результаты при разных gamma:")
for gamma, train_acc, test_acc in zip(gamma_values, train_scores_g, test_scores_g):
print(f"gamma={gamma}: Train={train_acc:.4f}, Test={test_acc:.4f}")
Вопрос: Что происходит при слишком маленьком и слишком большом значении gamma?
Этап 5. Подбор гиперпараметров (GridSearchCV) (15 минут)
# Загрузка датасета Breast Cancer
data = load_breast_cancer()
X_cancer = data.data
y_cancer = data.target
# Нормализация
X_cancer_scaled = scaler.fit_transform(X_cancer)
# Разделение
X_train_c, X_test_c, y_train_c, y_test_c = train_test_split(X_cancer_scaled, y_cancer, test_size=0.3, random_state=42)
# Подбор гиперпараметров с помощью GridSearchCV
param_grid = {
'C': [0.1, 1, 10, 100],
'gamma': [0.001, 0.01, 0.1, 1],
'kernel': ['rbf']
}
svm = SVC(random_state=42)
grid_search = GridSearchCV(svm, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X_train_c, y_train_c)
print("Лучшие параметры:", grid_search.best_params_)
print(f"Лучшее CV качество: {grid_search.best_score_:.4f}")
# Оценка на тестовой выборке
best_svm = grid_search.best_estimator_
y_pred_c = best_svm.predict(X_test_c)
print(f"Accuracy на тесте: {accuracy_score(y_test_c, y_pred_c):.4f}")
print("\nClassification Report:")
print(classification_report(y_test_c, y_pred_c, target_names=data.target_names))
# Визуализация результатов GridSearch
results_df = pd.DataFrame(grid_search.cv_results_)
pivot_table = results_df.pivot_table(index='param_C', columns='param_gamma', values='mean_test_score')
plt.figure(figsize=(10, 8))
sns.heatmap(pivot_table, annot=True, cmap='viridis', fmt='.3f')
plt.title('Тепловая карта качества (C vs gamma)')
plt.xlabel('gamma')
plt.ylabel('C')
plt.show()
Вопрос: Какие оптимальные параметры C и gamma подобрались? Почему они дают лучшее качество?
Этап 6. SVM для регрессии (SVR) (15 минут)
from sklearn.svm import SVR
from sklearn.metrics import mean_squared_error, r2_score
# Создание синтетических данных для регрессии
np.random.seed(42)
X_reg = np.sort(5 * np.random.rand(100, 1), axis=0)
y_reg = np.sin(X_reg).ravel() + np.random.normal(0, 0.1, X_reg.shape[0])
# Нормализация
X_reg_scaled = scaler.fit_transform(X_reg)
# Разделение
X_train_r, X_test_r, y_train_r, y_test_r = train_test_split(X_reg_scaled, y_reg, test_size=0.3, random_state=42)
# SVR с разными ядрами
kernels = ['linear', 'poly', 'rbf']
plt.figure(figsize=(12, 4))
for i, kernel in enumerate(kernels):
svr = SVR(kernel=kernel, C=10, gamma='auto')
svr.fit(X_train_r, y_train_r)
y_pred_r = svr.predict(X_test_r)
mse = mean_squared_error(y_test_r, y_pred_r)
r2 = r2_score(y_test_r, y_pred_r)
plt.subplot(1, 3, i+1)
plt.scatter(X_test_r, y_test_r, alpha=0.7, label='Истинные значения')
plt.scatter(X_test_r, y_pred_r, alpha=0.7, label='Предсказания')
plt.xlabel('X')
plt.ylabel('y')
plt.title(f'kernel={kernel}\nMSE={mse:.4f}, R²={r2:.4f}')
plt.legend()
plt.tight_layout()
plt.show()
print("Сравнение SVR с разными ядрами:")
for kernel in kernels:
svr = SVR(kernel=kernel, C=10, gamma='auto')
svr.fit(X_train_r, y_train_r)
y_pred_r = svr.predict(X_test_r)
print(f"{kernel}: MSE={mean_squared_error(y_test_r, y_pred_r):.4f}, R²={r2_score(y_test_r, y_pred_r):.4f}")
Вопрос: Какое ядро лучше всего подходит для регрессии на этих данных? Почему?
Этап 7. Выводы (10 минут)
Напишите вывод, ответив на вопросы:
1. Как работает метод опорных векторов (SVM)? В чём его основная идея?
2. Какие ядра SVM вы изучили? Для каких данных какое ядро подходит?
3. Как параметры C и gamma влияют на качество модели?
4. В чём преимущества и недостатки SVM по сравнению с другими методами?
КРИТЕРИИ ОЦЕНКИ
| Критерий | Макс. балл |
|---|
| Базовая SVM с линейным ядром выполнена | 1 | | Сравнение ядер на нелинейных данных выполнено | 2 | | Эксперимент с параметром C выполнен | 2 | | Эксперимент с параметром gamma выполнен | 2 | | GridSearchCV для подбора гиперпараметров выполнен | 1 | | SVR для регрессии выполнена и выводы написаны | 2 | | Итого | 10 |
Автор: УМК СПО
Лицензия: Бесплатное использование с указанием источника
|