目录
1 一些问题
- 支持向量机的基本思想是什么?
拟合类别之间可能的、最宽的“街道”。简而言之,它的目的是使决策边界之间的间隔最大化,从而分隔出两个类别的训练实例。
- 什么是支持向量?
位于“街道”之上的实例被称为支持向量,也包括处于边界上的实例。
- 使用SVM时,对输入值缩放为什么重要?
如果训练集不经缩放,SVM将趋于忽略值较小的特征。
- 如果训练集有上千万个实例和几百个特征,我们应该使用SVM原始问题还是对偶问题来训练模型?
这个问题仅适用于线性支持向量机,因为核SVM只能使用对偶问题。
- 假设我们用RBF核训练了一个SVM分类器,不过好像对训练集拟合不足,我们应该提升还是降低
(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》