机器学习实训(4)——支持向量机(补充)

发布于:2022-12-25 ⋅ 阅读:(468) ⋅ 点赞:(0)

目录

1 一些问题

2 训练LinearSVC

3 训练SVM分类器

4 训练SVM回归模型


1 一些问题

  • 支持向量机的基本思想是什么?

        拟合类别之间可能的、最宽的“街道”。简而言之,它的目的是使决策边界之间的间隔最大化,从而分隔出两个类别的训练实例。

  • 什么是支持向量?

        位于“街道”之上的实例被称为支持向量,也包括处于边界上的实例。      

  • 使用SVM时,对输入值缩放为什么重要?

        如果训练集不经缩放,SVM将趋于忽略值较小的特征。

  • 如果训练集有上千万个实例和几百个特征,我们应该使用SVM原始问题还是对偶问题来训练模型?

        这个问题仅适用于线性支持向量机,因为核SVM只能使用对偶问题。

  • 假设我们用RBF核训练了一个SVM分类器,不过好像对训练集拟合不足,我们应该提升还是降低 \gamma (gamma)? 那么C ?

        这可能是由于过度正则化导致的,因此我们可以提升 gamma 或 C 来降低正则化。

2 训练LinearSVC

在一个线性可分离数据集上训练LinearSVC,然后在同一数据集上训练 SVC 和 SGDClassifier 。看看是否可以用它们产生出大致相同的模型。

这里我们使用鸢尾花数据集,因为Iris Setosa和Iris Versicolor类是线性可分离的。

下面是代码实现:


import numpy as np
from sklearn import datasets
from sklearn.svm import SVC, LinearSVC
from sklearn.linear_model import SGDClassifier
from sklearn.preprocessing import StandardScaler


#加载数据
iris = datasets.load_iris()
X = iris["data"][:, (2, 3)]  # 花瓣长度和宽度
y = iris["target"]

setosa_or_versicolor = (y == 0) | (y == 1)
X = X[setosa_or_versicolor]
y = y[setosa_or_versicolor]

#训练

C = 5
alpha = 1 / (C * len(X))

lin_clf = LinearSVC(loss="hinge", C=C, random_state=42)
svm_clf = SVC(kernel="linear", C=C)
sgd_clf = SGDClassifier(loss="hinge", learning_rate="constant", eta0=0.001, alpha=alpha,
                        max_iter=100000, tol=-np.infty, random_state=42)

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

lin_clf.fit(X_scaled, y)
svm_clf.fit(X_scaled, y)
sgd_clf.fit(X_scaled, y)

print("LinearSVC:                   ", lin_clf.intercept_, lin_clf.coef_)
print("SVC:                         ", svm_clf.intercept_, svm_clf.coef_)
print("SGDClassifier(alpha={:.5f}):".format(sgd_clf.alpha), sgd_clf.intercept_, sgd_clf.coef_)

运行结果如下:

LinearSVC:                    [0.28475098] [[1.05364854 1.09903804]]
SVC:                          [0.31896852] [[1.1203284  1.02625193]]
SGDClassifier(alpha=0.00200): [0.319] [[1.12087283 1.02679408]]

我们对三种模型的决策边界进行可视化:

import matplotlib.pyplot as plt

# Compute the slope and bias of each decision boundary
w1 = -lin_clf.coef_[0, 0]/lin_clf.coef_[0, 1]
b1 = -lin_clf.intercept_[0]/lin_clf.coef_[0, 1]
w2 = -svm_clf.coef_[0, 0]/svm_clf.coef_[0, 1]
b2 = -svm_clf.intercept_[0]/svm_clf.coef_[0, 1]
w3 = -sgd_clf.coef_[0, 0]/sgd_clf.coef_[0, 1]
b3 = -sgd_clf.intercept_[0]/sgd_clf.coef_[0, 1]

# Transform the decision boundary lines back to the original scale
line1 = scaler.inverse_transform([[-10, -10 * w1 + b1], [10, 10 * w1 + b1]])
line2 = scaler.inverse_transform([[-10, -10 * w2 + b2], [10, 10 * w2 + b2]])
line3 = scaler.inverse_transform([[-10, -10 * w3 + b3], [10, 10 * w3 + b3]])

# Plot all three decision boundaries
plt.figure(figsize=(11, 4))
plt.plot(line1[:, 0], line1[:, 1], "k:", label="LinearSVC")
plt.plot(line2[:, 0], line2[:, 1], "b--", linewidth=2, label="SVC")
plt.plot(line3[:, 0], line3[:, 1], "r-", label="SGDClassifier")
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs") # label="Iris-Versicolor"
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo") # label="Iris-Setosa"
plt.xlabel("Petal length", fontsize=14)
plt.ylabel("Petal width", fontsize=14)
plt.legend(loc="upper center", fontsize=14)
plt.axis([0, 5.5, 0, 2])

plt.show()

运行结果如下:

3 训练SVM分类器

在MNIST数据集上训练SVM分类器。

首先,让我们加载数据集,并将其分成训练集和测试集。我们可以使用train_test_split()函数,但人们通常只取前60000个实例作为训练集,最后10000个实例作为测试集:

from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1, cache=True, as_frame=False)

X = mnist["data"]
y = mnist["target"]

#数据集划分
X_train = X[:60000]
y_train = y[:60000]
X_test = X[60000:]
y_test = y[60000:]

#随机数种子,结果复现
np.random.seed(42)
#数据洗牌
rnd_idx = np.random.permutation(60000)
X_train = X_train[rnd_idx]
y_train = y_train[rnd_idx]

我们从一个简单的线性SVM分类器开始。它会自动使用One-vs-All策略,所以我们不需要做什么特别的事情。

lin_clf = LinearSVC(random_state=42)
lin_clf.fit(X_train, y_train)

我们可以先在训练集上进行预测并测量精度(我们还不想在测试集上测量,因为我们还没有选择和训练最终模型): 

from sklearn.metrics import accuracy_score

y_pred = lin_clf.predict(X_train)
accuracy_score(y_train, y_pred)

运行结果如下:

0.8656166666666667

MNIST测试中86%的准确率是糟糕的表现。这个线性模型对MNIST来说当然太简单了,但也许我们只是需要先缩放数据:

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.astype(np.float32))
X_test_scaled = scaler.transform(X_test.astype(np.float32))
lin_clf = LinearSVC(random_state=42)
lin_clf.fit(X_train_scaled, y_train)

y_pred = lin_clf.predict(X_train_scaled)
accuracy_score(y_train, y_pred)

运行结果如下: 

0.92025

我们已将降低了错误率。如果我们想使用SVM,我们就必须使用核函数。让我们尝试一个带有RBF内核(默认)的SVC。

svm_clf = SVC(decision_function_shape="ovr", gamma="auto")
svm_clf.fit(X_train_scaled[:10000], y_train[:10000])

y_pred = svm_clf.predict(X_train_scaled)
accuracy_score(y_train, y_pred)

运行结果如下: 

0.9476

我们得到了更好的性能即使我们用6倍少的数据训练模型。下面我们可以通过使用交叉验证进行随机搜索来调整超参数。我们将在一个小数据集上这样做,只是为了加快过程: 

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import reciprocal, uniform

param_distributions = {"gamma": reciprocal(0.001, 0.1), "C": uniform(1, 10)}
rnd_search_cv = RandomizedSearchCV(svm_clf, param_distributions, n_iter=10, verbose=2, cv=3)
rnd_search_cv.fit(X_train_scaled[:1000], y_train[:1000])
rnd_search_cv.best_estimator_

运行结果如下: 

SVC(C=8.852316058423087, gamma=0.001766074650481071)
rnd_search_cv.best_score_

运行结果如下: 

0.8630037222851593

结果看起来很低,不过我们只在1000个实例上训练模型。下面我们在整个训练集上重新训练最佳估计器(注意:它将花费数小时,谨慎!!!): 

rnd_search_cv.best_estimator_.fit(X_train_scaled, y_train)

运行结果如下: 

SVC(C=8.852316058423087, gamma=0.001766074650481071)
y_pred = rnd_search_cv.best_estimator_.predict(X_train_scaled)
accuracy_score(y_train, y_pred)

运行结果如下: 

0.99843

我们可以选择这个模型。那么在测试集中测试它: 

y_pred = rnd_search_cv.best_estimator_.predict(X_test_scaled)
accuracy_score(y_test, y_pred)

运行结果如下: 

0.967891

还不错,但显然模型有点过拟合。稍微调整超参数能优化效果(如降低C和/或gamma)。

4 训练SVM回归模型

在加州住房数据集上训练一个SVM回归模型。

我们可以使用Scikit-Learn的fetch_california_housing()函数来加载数据集:

from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()
X = housing["data"]
y = housing["target"]

接下来是数据集划分:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

特征缩放:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

我们先训练一个简单的LinearSVR:

from sklearn.svm import LinearSVR

lin_svr = LinearSVR(random_state=42)
lin_svr.fit(X_train_scaled, y_train)

我们看看它在训练集上的表现:

from sklearn.metrics import mean_squared_error

y_pred = lin_svr.predict(X_train_scaled)
mse = mean_squared_error(y_train, y_pred)
mse

运行结果如下:

0.9641780189948642

我们再来看看RMSE:

import numpy as np
np.sqrt(mse)

运行结果如下:

0.9819256687727764

我们看看是否可以用RBF内核做得更好。我们将使用交叉验证的随机搜索来找到C和gamma的适当超参数值:

from sklearn.svm import SVR
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import reciprocal, uniform

param_distributions = {"gamma": reciprocal(0.001, 0.1), "C": uniform(1, 10)}
rnd_search_cv = RandomizedSearchCV(SVR(), param_distributions, n_iter=10, verbose=2, cv=3, random_state=42)
rnd_search_cv.fit(X_train_scaled, y_train)

运行结果如下:

RandomizedSearchCV(cv=3, estimator=SVR(),
                   param_distributions={'C': <scipy.stats._distn_infrastructure.rv_frozen object at 0x000001989C2C4160>,
                                        'gamma': <scipy.stats._distn_infrastructure.rv_frozen object at 0x000001989F252DF0>},
                   random_state=42, verbose=2)
rnd_search_cv.best_estimator_

运行结果如下:

SVR(C=4.745401188473625, gamma=0.07969454818643928)

现在我们再来测量训练集上的RMSE: 

y_pred = rnd_search_cv.best_estimator_.predict(X_train_scaled)
mse = mean_squared_error(y_train, y_pred)
np.sqrt(mse)

运行结果如下:

0.5727524770785372

看起来比线性模型好多了。那么我们将选择这个模型并在测试集中评估它: 

y_pred = rnd_search_cv.best_estimator_.predict(X_test_scaled)
mse = mean_squared_error(y_test, y_pred)
np.sqrt(mse)

运行结果如下:

0.5929168385528758

That's all.

学习笔记——《机器学习实战:基于Scikit-Learn和TensorFlow》

本文含有隐藏内容,请 开通VIP 后查看