线性支持向量机
SVM 是一个最大边距分类器,即它最大化将正类与负类分开的宽度或边距。线性SVM在二分类情况下的损失函数如下。
它可以从下面所示的更广义的多类线性 SVM 损失(也称为铰链损失)推导出来(Δ = 1)。
注意:在上述所有等式中,权重向量w 包括偏差b
到底是怎么有人想出这个损失的?让我们深入挖掘。
上图显示了属于正类的数据点与属于负类的数据点通过分离超平面(显示为实线)分开。然而,可以有许多这样的分离超平面。 SVM 找到分离超平面,使得超平面到最近的正数据点和最近的负数据点的距离最大(显示为虚线)。
在数学上,SVM 找到权重向量 w(包括偏差)使得
如果+ve类和-ve类的标签(y)分别是+1和-1,那么SVM找到w这样
• 如果数据点位于超平面的正确一侧(正确分类),则
• 如果数据点位于错误的一侧(未分类),则
所以一个数据点的损失,它是一个未分类的度量,可以写成
正则化
如果权重向量 w 正确分类数据 (X),则这些权重向量 λw 的任意倍数 λ>1 也将正确分类数据(零损失)。这是因为转换λW 拉伸了所有分数幅度,因此也拉伸了它们的绝对差异。 L2 正则化通过将正则化损失添加到铰链损失来惩罚大权重。
例如,如果x=[1,1,1,1] 和两个权重向量w1=[1,0,0,0],w2=[0.25,0.25,0.25,0.25]。然后dot(W1,x) =dot(w2,x) =1 即两个权重向量导致相同的点积,因此导致相同的铰链损失。但是w1的L2惩罚是1.0,而w2的L2惩罚只有0.25。因此 L2 正则化更喜欢 w2 而不是 w1。鼓励分类器将所有输入维度考虑到少量而不是少数输入维度并且非常强烈。这提高了模型的泛化能力并减少了过拟合。
L2 惩罚导致 SVM 中的最大边距属性。如果将 SVM 表示为优化问题,则约束二次优化问题的广义拉格朗日形式如下
现在我们知道了线性 SVM 的损失函数,我们可以使用梯度下降(或其他优化器)来找到最小化损失的权重向量。
代码
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
# Load Data
iris = datasets.load_iris()
X = iris.data[:, :2][iris.target != 2]
y = iris.target[iris.target != 2]
# Change labels to +1 and -1
y = np.where(y==1, y, -1)
# Linear Model with L2 regularization
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, activation='linear', kernel_regularizer=tf.keras.regularizers.l2()))
# Hinge loss
def hinge_loss(y_true, y_pred):
return tf.maximum(0., 1- y_true*y_pred)
# Train the model
model.compile(optimizer='adam', loss=hinge_loss)
model.fit(X, y, epochs=50000, verbose=False)
# Plot the learned decision boundary
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, 0.01),
np.arange(y_min, y_max, 0.01))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Set1)
plt.show()
SVM 也可以表示为一个有约束的二次优化问题。这个公式的优点是我们可以使用核技巧来分类非线性可分数据(使用不同的核)。 LIBSVM 为核化支持向量机 (SVM) 实现了序列最小优化 (SMO) 算法。
代码
from sklearn.svm import SVC
# SVM with linear kernel
clf = SVC(kernel='linear')
clf.fit(X, y)
# Plot the learned decision boundary
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, 0.01),
np.arange(y_min, y_max, 0.01))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Set1)
plt.show()
终于
可用于问题陈述的使用 tf 的线性 SVM 模型是
# Prepare Data
# 10 Binary features
df = pd.DataFrame(np.random.randint(0,2,size=(1000, 10)))
# 1 floating value feature
df[11] = np.random.uniform(0,100000, size=(1000))
# True Label
df[12] = pd.DataFrame(np.random.randint(0, 2, size=(1000)))
# Convert data to zero mean unit variance
scalar = StandardScaler().fit(df[df.columns.drop(12)])
X = scalar.transform(df[df.columns.drop(12)])
y = np.array(df[12])
# convert label to +1 and -1. Needed for hinge loss
y = np.where(y==1, +1, -1)
# Model
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, activation='linear',
kernel_regularizer=tf.keras.regularizers.l2()))
# Hinge Loss
def my_loss(y_true, y_pred):
return tf.maximum(0., 1- y_true*y_pred)
# Train model
model.compile(optimizer='adam', loss=my_loss)
model.fit(X, y, epochs=100, verbose=True)
K-Fold 交叉验证和预测
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import KFold
from sklearn.metrics import roc_curve, auc
# Load Data
iris = datasets.load_iris()
X = iris.data[:, :2][iris.target != 2]
y_ = iris.target[iris.target != 2]
# Change labels to +1 and -1
y = np.where(y_==1, +1, -1)
# Hinge loss
def hinge_loss(y_true, y_pred):
return tf.maximum(0., 1- y_true*y_pred)
def get_model():
# Linear Model with L2 regularization
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, activation='linear', kernel_regularizer=tf.keras.regularizers.l2()))
model.compile(optimizer='adam', loss=hinge_loss)
return model
def sigmoid(x):
return 1 / (1 + np.exp(-x))
predict = lambda model, x : sigmoid(model.predict(x).reshape(-1))
predict_class = lambda model, x : np.where(predict(model, x)>0.5, 1, 0)
kf = KFold(n_splits=2, shuffle=True)
# K Fold cross validation
best = (None, -1)
for i, (train_index, test_index) in enumerate(kf.split(X)):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
model = get_model()
model.fit(X_train, y_train, epochs=5000, verbose=False, batch_size=128)
y_pred = model.predict_classes(X_test)
val = roc_auc_score(y_test, y_pred)
print ("CV Fold {0}: AUC: {1}".format(i+1, auc))
if best[1] < val:
best = (model, val)
# ROC Curve using the best model
y_score = predict(best[0], X)
fpr, tpr, _ = roc_curve(y_, y_score)
roc_auc = auc(fpr, tpr)
print (roc_auc)
# Plot ROC
plt.figure()
lw = 2
plt.plot(fpr, tpr, color='darkorange',
lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc="lower right")
plt.show()
# Make predictions
y_score = predict_class(best[0], X)
做出预测
由于模型的输出是线性的,我们必须将其标准化为概率以进行预测。如果是二分类,我们可以使用sigmoid;如果是多类分类,我们可以使用softmax。下面的代码用于二进制分类
predict = lambda model, x : sigmoid(model.predict(x).reshape(-1))
predict_class = lambda model, x : np.where(predict(model, x)>0.5, 1, 0)
参考文献
-
CS231n
更新 1:
为了使代码与 tf 2.0 兼容,y 的数据类型应与X 相同。为此,在y = np.where(..... 行之后添加y = y.astype(np.float64) 行。