一、SVM概念
1、什么是svm
支持向量机(Support Vector Machine)是是一种强大且广泛应用的分类和回归算法。它具有优秀的泛化性能和较高的鲁棒性,被广泛应用于模式识别、计算机视觉、自然语言处理等多个领域。
SVM 的基本思想是将输入空间映射到高维特征空间,并在特征空间中寻找最优的分类超平面,使得不同类别之间的距离最大化。
2、最大间隔与分类
SVM 的基本原理是通过找到最大间隔超平面对数据进行分类。最大间隔分类器的目标是找到一个超平面,这个超平面可以将不同类别的数据尽可能地分开,并且使得不同类别之间的距离最大化。
超平面是一个在特征空间中的线性判别函数,它可以将特征空间分为两个部分。对于二维数据,超平面是一条直线;对于高维数据,超平面是一个超平面或超曲面。在 SVM 中,我们要找到一个最优的超平面,使得两个类别之间的距离最大化
就如下图,可以使用一个直线来将这两类数据点分开,但是这样有很多条直线可以用来分开这些数据点。那么哪一条是最好的呢?正是最中间那一条,因为这条线使两个类别之间的距离最大化。
同时使用决策函数用来预测新数据点的类别。对于一个新的数据点,我们将其映射到高维空间,并根据该数据点与超平面之间的位置关系,判断它属于哪一类。
3、对偶问题
线性可分的支持向量机时,我们可以通过求解原始问题来得到最优解。然而,当样本不是线性可分的时候,我们需要使用对偶最优化问题来求解支持向量机的参数。
对偶最优化问题的核心思想是将原始问题转化为等价的对偶问题,然后通过对对偶问题进行求解,得到支持向量机的最优解。
对于等式约束的优化问题,可以通过拉格朗日乘子法将其转化为对偶问题。考虑一个等式约束的优化问题:
L(x, λ) = f(x) + λg(x)
其中,λ 是拉格朗日乘子。接下来,通过对拉格朗日函数 分别对 和 求偏导,并令导数等于零,可以获得对偶问题。
这个步骤可以用来消除原始问题中的 ,将其表示为 的函数。
对 求偏导数并令为零:
通过以上两个步骤,可以得到原始问题的对偶形式。
KKT 条件(Karush-Kuhn-Tucker conditions)是一组非充分必要条件,用于判断是否满足最优解。在支持向量机中,KKT 条件可以用来验证是否找到了满足约束的最优解。
KKT 条件包括以下几个方面:
- 原始约束条件:所有样本的约束条件都满足,即 y_i(w·x_i + b) - 1 ≥ 0。
- 对偶互补条件:α_i(y_i(w·x_i + b) - 1) = 0,即拉格朗日乘子与约束条件之间存在关系。
- 互斥条件:α_i ≥ 0,即拉格朗日乘子的取值范围是非负的。
- KKT 公式:对于支持向量,有 α_i [y_i(w·x_i + b) - 1] = 0,对于非支持向量,有 α_i = 0。
当且仅当上述条件满足时,我们可以认为找到了支持向量机的最优解。KKT 条件的满足意味着最优解同时满足原始问题和对偶问题的约束条件。
4、核函数
4.1 核函数的定义
核函数是一种用于支持向量机(SVM)等机器学习算法的数学函数。它的主要作用是将输入数据映射到高维空间,使得在该高维空间中的线性不可分问题在原始空间中变得线性可分。
基本想法:不显式地构造核映射, 而是设计核函数.
4.2 核函数的作用
处理非线性关系: 在原始特征空间中,一些问题可能是非线性可分的。核函数通过映射将样本转换到高维空间,使得在该空间中的决策边界更容易线性划分。
避免显式特征映射: 高维空间可能是无限维的,但通过核函数,我们可以避免显式地计算和存储高维空间中的特征,从而降低计算复杂性。
提高模型性能: 对于某些问题,使用适当的核函数可以提高模型的性能,使得支持向量机等算法更适用于复杂的数据结构。
4.3 常见的核函数类型
-
线性核函数:线性核函数在原始特征空间中执行标准的内积操作。
-
多项式核函数:多项式核函数通过多项式映射将数据映射到高维空间。
-
高斯核函数(径向基函数,RBF): 高斯核函数通过将数据映射到无穷维空间来处理非线性关系。
-
Sigmoid核函数: Sigmoid核函数在神经网络中也常被使用。
5、软间隔与正则化
5.1 软间隔定义
在支持向量机(SVM)中,软间隔是指允许一些样本点不满足线性可分条件,即允许一些样本出现在决策边界(超平面)的错误一侧。软间隔允许在模型构建过程中存在一定程度的分类误差。
5.2 处理线性不可分的情况
当数据在原始特征空间中是线性不可分的时候,可以采取以下方法处理:
引入软间隔: 允许一些样本点位于超平面的错误一侧,通过容忍一定程度的分类误差,来适应数据的噪声或复杂性。
核函数: 使用核函数将数据映射到高维空间,使得在高维空间中数据变得线性可分。
正则化: 使用正则化方法,使得模型在考虑分类准确性的同时,也考虑到模型的复杂度,防止过拟合。
5.3 正则化的作用和影响
正则化是控制模型复杂度的一种技术,它通过在模型的损失函数中引入惩罚项来限制模型参数的大小。常用的正则化包括L1正则化和L2正则化。
作用:
- 防止过拟合: 正则化能够限制模型的复杂度,防止模型在训练集上过度拟合,并提高模型在未见数据上的泛化能力。
- 控制模型参数: 正则化通过惩罚过大的参数值,使得模型的参数保持较小的幅度,有助于提高模型的稳定性和可解释性。
影响:
- 权衡偏差与方差: 正则化帮助平衡模型的偏差(bias)和方差(variance),在降低方差的同时,可能略微增加模型的偏差。
- 调整超参数: 正则化强度通常由超参数(如正则化项的权重)控制。选择合适的正则化强度是对模型泛化性能影响较大的因素之一。
二、代码实现
实现svm
1、导入库:这里导入了numpy用于数值计算,make_classification用于生成示例数据,train_test_split用于划分数据集,matplotlib.pyplot用于绘图。
2、使用make_classification函数生成具有100个样本和2个特征的示例数据。参数n_informative表示有多少个特征对分类有信息,n_redundant表示有多少个特征与其他特征冗余。、
3、转换标签和划分数据集:将标签y中的0转换为-1,以便在SVM中进行二分类,使用train_test_split函数将数据集划分为训练集和测试集,其中测试集占总样本的20%。
y = np.where(y == 0, -1, 1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
4、这里定义了一个SVM类,具有学习率(learning_rate)、正则化参数(lambda_param)和迭代次数(n_iterations)等超参数。初始化函数__init__用于初始化这些参数,fit方法用于训练模型,predict方法用于对数据进行预测。
class SVM:
def __init__(self, learning_rate=0.01, lambda_param=0.01, n_iterations=1000):
self.lr = learning_rate
self.lambda_param = lambda_param
self.n_iterations = n_iterations
self.weights = None
self.bias = None
def fit(self, X, y):
# 初始化权重和偏置
# 训练模型
def predict(self, X):
# 预测结果
6、在SVM类的fit方法中,初始化权重和偏置
self.weights = np.zeros(n_features)
self.bias = 0
7、在SVM类的fit方法中,训练模型:
for _ in range(self.n_iterations):
for i, x in enumerate(X):
condition = y[i] * (np.dot(x, self.weights) - self.bias) >= 1
if condition:
# 更新权重
else:
# 更新权重和偏置
8、 在SVM类的predict方法中,预测结果。创建SVM对象并训练模型以及在测试集上进行预测并计算准确率。
return np.sign(np.dot(X, self.weights) - self.bias)
svm = SVM(learning_rate=0.01, lambda_param=0.01, n_iterations=1000)
svm.fit(X_train, y_train)
predictions = svm.predict(X_test)
accuracy = np.mean(predictions == y_test)
print(f"Accuracy: {accuracy}")
9、绘制决策边界:
def plot_decision_boundary(X, y, model):
# 生成网格点
# 使用模型对网格点进行预测
# 绘制决策边界和样本点
plt.contourf(xx, yy, Z, cmap=plt.cm.Paired, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('SVM Decision Boundary')
plt.show()
plot_decision_boundary(X_test, y_test, svm)
10、运行结果
全部代码:
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# 生成一些示例数据
X, y = make_classification(n_samples=100, n_features=2, n_informative=2, n_redundant=0, random_state=42)
# 将标签转换为 -1 或 1
y = np.where(y == 0, -1, 1)
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
class SVM:
def __init__(self, learning_rate=0.01, lambda_param=0.01, n_iterations=1000):
self.lr = learning_rate
self.lambda_param = lambda_param
self.n_iterations = n_iterations
self.weights = None
self.bias = None
def fit(self, X, y):
n_samples, n_features = X.shape
# 初始化权重和偏置
self.weights = np.zeros(n_features)
self.bias = 0
# 训练模型
for _ in range(self.n_iterations):
for i, x in enumerate(X):
condition = y[i] * (np.dot(x, self.weights) - self.bias) >= 1
# 更新权重和偏置
if condition:
self.weights -= self.lr * (2 * self.lambda_param * self.weights)
else:
self.weights -= self.lr * (2 * self.lambda_param * self.weights - np.dot(x, y[i]))
self.bias -= self.lr * y[i]
def predict(self, X):
return np.sign(np.dot(X, self.weights) - self.bias)
# 初始化和训练SVM模型
svm = SVM(learning_rate=0.01, lambda_param=0.01, n_iterations=1000)
svm.fit(X_train, y_train)
# 在测试集上进行预测
predictions = svm.predict(X_test)
# 计算准确率
accuracy = np.mean(predictions == y_test)
print(f"Accuracy: {accuracy}")
# 绘制决策边界
def plot_decision_boundary(X, y, model):
h = .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)
plt.contourf(xx, yy, Z, cmap=plt.cm.Paired, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('SVM Decision Boundary')
plt.show()
# 绘制决策边界
plot_decision_boundary(X_test, y_test, svm)
实现垃圾邮件分类
import numpy as np
import scipy.io as sio
from sklearn import svm
# 加载数据集
data_train = sio.loadmat('data/spamTrain.mat')
data_test = sio.loadmat('data/spamTest.mat')
X_train = data_train['X']
y_train = data_train['y'].ravel()
X_test = data_test['Xtest']
y_test = data_test['ytest'].ravel()
# 加载词汇表
with open('data/vocab.txt', 'r') as f:
vocab_list = [line.strip().split('\t')[1] for line in f.readlines()]
# 构建词汇表映射
vocab_map = {vocab_list[i]: i for i in range(len(vocab_list))}
# 处理邮件文本
def email_to_feature(email):
feature = np.zeros(len(vocab_list))
words = email.split()
for word in words:
if word in vocab_map:
feature[vocab_map[word]] = 1
return feature
X_train_features = np.array([email_to_feature(' '.join(email.astype(str))) for email in X_train])
X_test_features = np.array([email_to_feature(' '.join(email.astype(str))) for email in X_test])
# 训练SVM分类器
clf = svm.SVC(kernel='linear', C=0.1)
clf.fit(X_train_features, y_train)
# 测试分类器
y_pred = clf.predict(X_test_features)
accuracy = np.mean(y_pred == y_test)
precision = np.sum(np.logical_and(y_pred == 1, y_test == 1)) / np.sum(y_pred == 1)
recall = np.sum(np.logical_and(y_pred == 1, y_test == 1)) / np.sum(y_test == 1)
print('Accuracy: {:.2f}%'.format(accuracy * 100))
print('Precision: {:.2f}%'.format(precision * 100))
print('Recall: {:.2f}%'.format(recall * 100))
运行结果:
根据结果,我们可以看出分类器在这个任务上的性能不理想。可能需要进一步调整模型参数或特征选择,以提高分类器的性能。
三、小结
支持向量机作为一种优秀的机器学习算法,在许多实际问题中都表现出色。通过最大间隔分类和核技巧的引入,支持向量机能够处理线性可分和非线性可分问题,并具有较好的泛化能力。同时,软间隔与正则化的应用使得支持向量机对噪声和异常样本具有一定的鲁棒性。