python复现SMO算法
-
- 任务要求重述
- 步骤一:数据读取和预处理
- 步骤二:初步数据分析
- 步骤三:构建`高斯核`函数
-
- 高斯核函数
- [1. 函数定义:](#1. 函数定义:)
- [2. 处理不同形状的输入:](#2. 处理不同形状的输入:)
- 步骤四:预测输出函数
- [步骤五:选择违反KKT条件最严重的 α 1 \alpha_1 α1【启发式方法】](#步骤五:选择违反KKT条件最严重的 α 1 \alpha_1 α1【启发式方法】)
- [步骤六:基于 α 1 \alpha_1 α1 找到最优的 α 2 \alpha_2 α2](#步骤六:基于 α 1 \alpha_1 α1 找到最优的 α 2 \alpha_2 α2)
- [步骤七:优化 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 并更新模型偏置和误差](#步骤七:优化 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 并更新模型偏置和误差)
-
- [优化 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2](#优化 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2)
- [更新模型的偏置 b b b](#更新模型的偏置 b b b)
- [更新误差 E E E](#更新误差 E E E)
- 代码实现
- 步骤七:SMO算法核心框架
- 步骤八:主函数中调用
- 步骤九:记录时间和精度
- 步骤十:对比libsvm实现精度和时间
任务要求重述
-
复现带有
高斯核
的SMO算法 -
数据集规模需要大于1000条(数据为a5a,二分类问题)
-
与
libsvm
对比训练精度和时间,要求在 超参一致 的情况下,精度相差不超过1%。
步骤一:数据读取和预处理
在支持向量机(SVM)的实践应用中,正确地加载和预处理数据是非常重要的。我们通常从以 LIBSVM格式 存储的文件中读取数据,每一行代表一个样本,其中包含一个样本的标签和随后的特征值。在这个教程中,我们将使用 scikit-learn
库中的工具来辅助我们处理数据。
首先,安装 scikit-learn
:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scikit-learn
我们将定义一个函数来加载数据,并确保所有特征维度和标签格式一致:
python
import numpy as np
from sklearn.datasets import load_svmlight_file
def load_and_combine_features(train_file, test_file):
X_train, y_train = load_svmlight_file(train_file, n_features=123)
X_test, y_test = load_svmlight_file(test_file, n_features=123)
# 确保训练集和测试集具有相同的特征空间
return X_train.toarray(), y_train, X_test.toarray(), y_test
# 加载数据
X_train, y_train,X_test, y_test = load_and_combine_features("../data/a5a.txt","../data/a5a.t.txt")
这里,我们使用 load_svmlight_file
函数直接读取LIBSVM格式的数据,并通过指定 n_features=123
来确保所有数据样本具有相同数量的特征。我们还将稀疏矩阵转换为密集数组,以便于后续处理。
步骤二:初步数据分析
首先,我们查看一下训练数据和测试数据的维度,以及正负样本的分布情况。
python
def analyze_data(X, y):
# 数据维度
n_samples, n_features = X.shape
# 标签类别统计
positive_samples = np.sum(y == 1)
negative_samples = np.sum(y == -1)
return n_samples, n_features, positive_samples, negative_samples
# 分析训练数据和测试数据
train_info = analyze_data(X_train, y_train)
test_info = analyze_data(X_test, y_test)
print("训练数据:样本数 = {}, 特征数 = {}, 正样本数 = {}, 负样本数 = {}".format(*train_info))
print("测试数据:样本数 = {}, 特征数 = {}, 正样本数 = {}, 负样本数 = {}".format(*test_info))
对SVM重要的数据特征
由于SVM对特征尺度敏感,尤其是使用高斯核时,不均匀的特征尺度可能会显著影响模型的性能。接下来,我们将计算特征的均值和标准差,以决定是否需要进行特征缩放:
python
def feature_statistics(X):
# 计算特征的均值和标准差
mean_features = np.mean(X, axis=0)
std_features = np.std(X, axis=0)
return mean_features, std_features
train_mean, train_std = feature_statistics(X_train)
test_mean, test_std = feature_statistics(X_test)
print("训练数据特征均值: ", train_mean)
print("训练数据特征标准差: ", train_std)
print("测试数据特征均值: ", test_mean)
print("测试数据特征标准差: ", test_std)
数据分析总结
特征均值和标准差:
- 训练数据和测试数据的特征均值及标准差
相近
,说明两个数据集在特征分布上具有一定的一致性。 - 特征的均值和标准差差异较大,表明数据各维度的 尺度不一 ,可能需要进行特征标准化以提高SVM的性能。
特征标准化
基于以上分析,特征标准化是必要的,以确保所有特征具有相同的尺度。这可以通过减去均值并除以标准差来实现:
python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
通过上述步骤,我们为SVM模型的训练和测试准备了数据,同时确保了数据的质量和一致性,为接下来的模型训练和评估奠定了基础。
步骤三:构建高斯核
函数
高斯核函数
高斯核也称为径向基函数(RBF)核,它是支持向量机(SVM)中使用的一种非常流行的核函数。其主要作用是将原始特征空间中的样本映射到一个更高维的空间中,帮助处理那些在原始空间中非线性可分的数据。高斯核的公式是:
K ( x i , x j ) = exp ( − ∥ x i − x j ∥ 2 2 σ 2 ) K(x_i, x_j) = \exp\left(-\frac{\|x_i - x_j\|^2}{2\sigma^2}\right) K(xi,xj)=exp(−2σ2∥xi−xj∥2)
其中, x i x_i xi 和 x j x_j xj 是两个样本点, σ \sigma σ 是核函数的带宽参数,控制了数据映射到新空间的分散程度
。
在很多机器学习库中,比如 sklearn,高斯核的表达式常写作:
K ( x , x ′ ) = e − γ ∥ x − x ′ ∥ 2 K(x, x') = e^{-\gamma \|x - x'\|^2} K(x,x′)=e−γ∥x−x′∥2
其中, γ \gamma γ 通常定义为 1 2 σ 2 \frac{1}{2\sigma^2} 2σ21, γ \gamma γ 能够控制函数的宽度,
1. 函数定义:
python
def gaussian_kernel(x1, x2, gamma=0.00819672131147541):
首先我们定义一个名为 gaussian_kernel
的函数,接收两个样本集 x1
和 x2
,以及一个可选的 gamma
参数,默认值为 1 122 = 0.00819672131147541 \frac{1}{122} = 0.00819672131147541 1221=0.00819672131147541。
默认设置 γ = 1 特征数 \gamma = \frac{1}{\text{特征数}} γ=特征数1 是一种常见的
启发式方法
,尤其是在我们没有足够信息对 σ \sigma σ 进行优化调整的情况下,这可以提供一个合理的起点。这种设置假设数据在所有维度上 均匀分布 ,且各特征的重要性相同,但在实际应用中,根据数据的具体特性来调整 γ \gamma γ (从而影响 σ \sigma σ)通常会获得更好的结果。
当然,我们可以考虑使用如交叉验证
等方法来找到最优的 γ \gamma γ 值。
2. 处理不同形状的输入:
-
两个一维向量的情况:
pythonif np.ndim(x1) == 1 and np.ndim(x2) == 1: result = np.exp(-np.linalg.norm(x1-x2)**2 * gamma )
这种情况下,
x1
和x2
都是单个样本(一维数组)。使用np.linalg.norm(x1 - x2)
计算两个向量之间的 欧氏距离 ,然后根据高斯核函数的公式计算结果。 -
一个是一维向量,一个是二维数组的情况:
pythonelif (np.ndim(x1) > 1 and np.ndim(x2) == 1) or (np.ndim(x1) == 1 and np.ndim(x2) > 1): result = np.exp(-np.linalg.norm(x1-x2, axis=1)**2 * gamma)
这里处理的是一个样本与多个样本之间的高斯核计算。例如,
x1
可能是一个二维数组(多个样本),而x2
是一个一维数组(单个样本),或反之。np.linalg.norm(x1 - x2, axis=1)
沿着第一个轴( 每个样本的特征轴 )计算距离,适用于批量处理,从而得到一个向量,其中包含了与x2
每个样本的核值。 -
两个都是二维数组的情况:
pythonelse: result = np.exp(-np.linalg.norm(x1[:, np.newaxis] - x2[np.newaxis, :], axis=2)** 2 * gamma)
当
x1
和x2
都是二维数组时,该代码计算所有可能的成对样本之间的核。使用np.newaxis
增加一个新的轴,使两个数组广播能够生成一个距离矩阵,其中包含了每对样本之间的距离。
python
def gaussian_kernel(x1, x2, gamma=0.0081967213114754):
"""
计算两个输入数组x1和x2之间的高斯核矩阵。
参数:
x1: 第一个样本数组,可以是一维或二维numpy数组。
x2: 第二个样本数组,可以是一维或二维numpy数组。
gamma: 高斯核的带宽参数,控制核的宽度。
返回:
高斯核矩阵,其中每个元素都是x1和x2中对应样本点的核函数值。
"""
if np.ndim(x1) == 1 and np.ndim(x2) == 1:
result = np.exp(-np.linalg.norm(x1 - x2)**2 * gamma)
elif (np.ndim(x1) > 1 and np.ndim(x2) == 1) or (np.ndim(x1) == 1 and np.ndim(x2) > 1):
result = np.exp(-np.linalg.norm(x1 - x2, axis=1)**2 * gamma)
else:
result = np.exp(-np.linalg.norm(x1[:, np.newaxis] - x2[np.newaxis, :], axis=2)**2 * gamma)
return result
步骤四:预测输出函数
首先,我们从所有训练样本中选择一个违反KKT条件
最严重的样本作为 α 1 \alpha_1 α1。违反KKT条件的程度可以通过计算 ∣ y i f ( x i ) − 1 ∣ |y_i f(x_i) - 1| ∣yif(xi)−1∣ 来量化,其中 f ( x i ) f(x_i) f(xi) 是模型在 x i x_i xi 上的 预测输出 。我们首先需要定义如何计算 f ( x i ) f(x_i) f(xi),它基于当前模型的参数:
python
def compute_fx(X, y, alpha, b, x_i, gamma=0.0081967213114754):
"""
计算模型在单个样本 x_i 上的预测输出。
参数:
X: 训练样本集。
y: 标签数组。
alpha: 拉格朗日乘数数组。
b: 偏置项。
x_i: 当前样本点。
gamma: 高斯核的带宽参数,控制核的宽度。
返回:
f(x_i): 模型对 x_i 的预测输出。
"""
# 调用高斯核函数计算核矩阵
kernel_values = gaussian_kernel(X, x_i, gamma)
return np.sum(alpha * y * kernel_values) + b
在这种情况下, x 1 x_1 x1 对应 X X X(二维数组), x 2 x_2 x2 对应 x_i(一维数组)。函数通过
axis=1
参数计算每行(每个样本)与 x i x_i xi 的欧式距离的平方,然后应用高斯函数变换
。这样,输出结果 result 将是一个 一维数组 ,其中包含了 X X X 中每个样本与 x i x_i xi 的高斯核值。
步骤五:选择违反KKT条件最严重的 α 1 \alpha_1 α1【启发式方法】
关于整体SMO算法框架,一般会在一个循环中执行以下步骤:
- 选择违反KKT条件最严重的 α 1 \alpha_1 α1。
- 基于 α 1 \alpha_1 α1找到最优的 α 2 \alpha_2 α2。
- 对这两个 α \alpha α进行优化。
- 更新 b b b和 E E E。
这种方法是直接在
KKT条件违反的判断
在所有支持向量机的训练样本中,选择违反KKT条件最严重的样本作为 α 1 \alpha_1 α1。这一选择是基于以下KKT条件违反的判断:
- 当 α i = 0 \alpha_i = 0 αi=0 时,样本应在决策边界的正确一侧,即 y i f ( x i ) ≥ 1 y_i f(x_i) \geq 1 yif(xi)≥1。
- 当 0 < α i < C 0 < \alpha_i < C 0<αi<C 时,样本应恰好在边界上,即 y i f ( x i ) = 1 y_i f(x_i) = 1 yif(xi)=1。
- 当 α i = C \alpha_i = C αi=C 时,样本应在决策边界的错误一侧,即 y i f ( x i ) ≤ 1 y_i f(x_i) \leq 1 yif(xi)≤1。
违反程度可以通过 ∣ y i f ( x i ) − 1 ∣ |y_i f(x_i) - 1| ∣yif(xi)−1∣ 来量化,选择这个量值最大的样本。
KKT条件违反的测量
对于每个给定的训练样本 ( x i , y i ) (x_i, y_i) (xi,yi),其对应的拉格朗日乘子 α i \alpha_i αi,模型预测的输出为 f ( x i ) f(x_i) f(xi)。根据 α i \alpha_i αi 的值和 y i f ( x i ) y_i f(x_i) yif(xi),KKT条件违反我们可以通过以下方式测量:
- 当 α i = 0 \alpha_i = 0 αi=0 且 y i f ( x i ) < 1 y_i f(x_i) < 1 yif(xi)<1,违反程度为 1 − y i f ( x i ) 1 - y_i f(x_i) 1−yif(xi)。
- 当 0 < α i < C 0 < \alpha_i < C 0<αi<C,使用一个
小的容忍度
ϵ \epsilon ϵ 来判断,若 ∣ y i f ( x i ) − 1 ∣ > ϵ |y_i f(x_i) - 1| > \epsilon ∣yif(xi)−1∣>ϵ,则视为违反。 - 当 α i = C \alpha_i = C αi=C 且 y i f ( x i ) > 1 y_i f(x_i) > 1 yif(xi)>1,违反程度为 y i f ( x i ) − 1 y_i f(x_i) - 1 yif(xi)−1。
函数实现
通过以下步骤实现选择 α 1 \alpha_1 α1 的逻辑:
输入:
X
: 训练样本的特征集合。y
: 训练样本的标签集合。alpha
: 拉格朗日乘数的当前值。b
: 当前的偏置项。gamma
: 核函数宽度。C
: 正则化参数,控制优化问题的边界硬度。
输出:
idx
: 违反KKT条件最严重的样本的索引。E[idx]
: 该样本的违反程度。
处理逻辑:
- 初始化误差数组
E
。 - 遍历每个样本计算其预测输出 f ( x i ) f(x_i) f(xi)。
- 计算每个样本的误差 E i = f ( x i ) − y i E_i = f(x_i) - y_i Ei=f(xi)−yi 和KKT违反程度。
- 根据 α i \alpha_i αi 的值和 y i f ( x i ) y_i f(x_i) yif(xi),计算KKT违反程度。
- 选择违反程度最大的样本的索引作为 α 1 \alpha_1 α1。
代码示例
python
def select_alpha1(X, y, alpha, b, gamma, C):
m = len(y)
E = np.zeros(m)
tol = 1e-4 # 容忍度
for i in range(m):
fx_i = compute_fx(X, y, alpha, b, X[i], gamma) # 注意这里直接传入 gamma 而不是 kernel
E[i] = fx_i - y[i]
KKT_violation = y[i] * fx_i
# 根据KKT条件计算违反程度
if (alpha[i] == 0 and KKT_violation < 1) or \
(0 < alpha[i] < C and not (1-tol < KKT_violation < 1+tol)) or \
(alpha[i] == C and KKT_violation > 1):
E[i] = np.abs(KKT_violation - 1)
idx = np.argmax(E)
return idx, E[idx]
步骤六:基于 α 1 \alpha_1 α1 找到最优的 α 2 \alpha_2 α2
选择标准
在这一步中, α 2 \alpha_2 α2 的选择依赖于找到与 α 1 \alpha_1 α1 的预测误差 E 1 E_1 E1 差距最大的 α 2 \alpha_2 α2 。理想的 α 2 \alpha_2 α2 应该最大化误差差 ∣ E 1 − E 2 ∣ |E_1 - E_2| ∣E1−E2∣,这通常可以带来最大的目标函数变化。
实现步骤
从误差向量 E E E 中选择 α 2 \alpha_2 α2:
- 给定已选的 α 1 \alpha_1 α1 的索引 i d x 1 idx1 idx1 和对应的误差向量 E E E,找出误差差 ∣ E 1 − E 2 ∣ |E_1 - E_2| ∣E1−E2∣ 最大的 α 2 \alpha_2 α2。
- 排除 α 1 \alpha_1 α1 自身,确保 α 2 \alpha_2 α2 是不同的样本。
代码实现
这个函数首先需要计算了除 α 1 \alpha_1 α1 外所有
样本的误差差的绝对值,然后从中选择误差差最大的索引作为 α 2 \alpha_2 α2。
python
def select_alpha2(E, idx1):
"""
选择最优的 alpha2。
参数:
E: 所有样本的误差数组。
idx1: alpha1 的索引。
返回:
idx2: 最优 alpha2 的索引。
"""
# 计算与 alpha1 的误差差的绝对值
E_diff = np.abs(E - E[idx1])
# 确保不会选择同一个样本作为 alpha2
E_diff[idx1] = 0
# 选择最大误差差的索引作为 alpha2
idx2 = np.argmax(E_diff)
return idx2
步骤七:优化 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 并更新模型偏置和误差
选择了 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 后,下一步是优化这两个拉格朗日乘子,并更新模型的偏置项 b b b 和误差向量 E E E。这一步是实现模型优化的核心
,涉及到核函数的计算、乘子的更新和偏置的调整。
优化 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2
优化过程包括以下几个关键步骤:
-
计算 η \eta η:
- η \eta η 是核函数的负二阶导数,代表了样本 x 1 x_1 x1 和 x 2 x_2 x2 在特征空间中的距离,其计算公式为:
η = K ( x 1 , x 1 ) + K ( x 2 , x 2 ) − 2 K ( x 1 , x 2 ) \eta = K(x_1, x_1) + K(x_2, x_2) - 2K(x_1, x_2) η=K(x1,x1)+K(x2,x2)−2K(x1,x2) - 如果 η \eta η 为非正,则停止当前的优化步骤。
- η \eta η 是核函数的负二阶导数,代表了样本 x 1 x_1 x1 和 x 2 x_2 x2 在特征空间中的距离,其计算公式为:
-
更新 α 2 \alpha_2 α2:
- 利用计算得到的 η \eta η,更新 α 2 \alpha_2 α2:
α 2 n e w = α 2 o l d + y 2 ( E 1 − E 2 ) η \alpha_2^{new} = \alpha_2^{old} + \frac{y_2 (E_1 - E_2)}{\eta} α2new=α2old+ηy2(E1−E2) - 对 α 2 n e w \alpha_2^{new} α2new 进行剪辑,以确保其值位于合法区间 [0, C] 内。
- 利用计算得到的 η \eta η,更新 α 2 \alpha_2 α2:
-
更新 α 1 \alpha_1 α1:
- 根据拉格朗日乘子的求和约束,更新 α 1 \alpha_1 α1:
α 1 n e w = α 1 o l d + y 1 y 2 ( α 2 o l d − α 2 n e w ) \alpha_1^{new} = \alpha_1^{old} + y_1 y_2 (\alpha_2^{old} - \alpha_2^{new}) α1new=α1old+y1y2(α2old−α2new)
- 根据拉格朗日乘子的求和约束,更新 α 1 \alpha_1 α1:
更新模型的偏置 b b b
偏置 b b b 的更新至关重要,它确保了模型的决策边界正确地反映了数据的分布:
- 通过比较 α 1 n e w \alpha_1^{new} α1new 和 α 2 n e w \alpha_2^{new} α2new 是否位于其合法区间 (0, C),选择适当的方法来更新 b b b。
- 新的偏置值 b n e w b_{new} bnew 是根据 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 的更新量以及它们在核空间中的位置变化计算得出的。
更新误差 E E E
更新每个样本的误差值 E i E_i Ei 是为了反映拉格朗日乘子和偏置项的最新影响:
- 误差 E i E_i Ei 为模型在样本 x i x_i xi 上的实际输出与预期输出的差异,更新公式为:
E [ i ] = f ( x i ) − y [ i ] E[i] = f(x_i) - y[i] E[i]=f(xi)−y[i]
代码实现
以下是对 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 进行优化以及更新 b b b 和 E E E 的 Python 函数实现:
python
def update_alpha_and_b(X,y,alpha , b , idx1, idx2 , E , C):
x1 , x2 = X[idx1] , X[idx2]
y1 , y2 = y[idx1] , y[idx2]
alpha1_old , alpha2_old = alpha[idx1] , alpha[idx2]
# 进行迭代,迭代主要公式 alpha2_new = alpha2_old + y2(E1 - E2) / (K11 - 2K12 + K22)
# 计算 eta = (K11 - 2K12 + K22)
K11 = gaussian_kernel(x1, x1)
K12 = gaussian_kernel(x1, x2)
K22 = gaussian_kernel(x2, x2)
eta = K11 - 2 * K12 + K22
if eta <= 0:
return False
# 更新 alpha2_new
alpha2_new = alpha2_old + y2*( abs(E[idx1] - E[idx2]) ) / eta
L , H = 0 , C
# 剪辑,使用公式
if y1 != y2:
L = max(0 , alpha2_old - alpha1_old)
H = min(C , C + alpha2_old - alpha1_old)
else:
L = max(0 , alpha1_old + alpha2_old - C)
H = min(C , alpha1_old + alpha2_old)
alpha2_new = max(min(alpha2_new, H), L)
# 计算a1,公式 a1new = a1old + y1y2(a2old - a2new)
alpha1_new = alpha1_old + y1 * y2 * (alpha2_old - alpha2_new)
# 计算新旧alpha之间的变化量
delta_alpha1 = alpha1_new - alpha1_old
delta_alpha2 = alpha2_new - alpha2_old
b1_new = b - E[idx1] - y1 * K11 * delta_alpha1 - y2 * K12 * delta_alpha2
b2_new = b - E[idx2] - y1 * K12 * delta_alpha1 - y2 * K22 * delta_alpha2
if alpha1_new > 0 and alpha1_new < C:
b = b1_new
elif alpha2_new > 0 and alpha2_old < C:
b = b2_new
else:
b = (b1_new + b2_new) / 2
alpha[idx1] , alpha[idx2] = alpha1_new , alpha2_new
# 更新 E
for i in range(len(E)):
E[i] = compute_fx(X, y, alpha, b, X[i]) - y[i]
return True
这段代码实现了在给定 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 的情况下,对这两个乘数进行优化,并相应地更新了模型的偏置和误差。通过这些更新,我们可以确保模型逐渐逼近最优解。
步骤七:SMO算法核心框架
对于每个 α 1 \alpha_1 α1,首先计算对应的预测输出 f ( x i ) f(x_i) f(xi),然后计算预测误差 E i = f ( x i ) − y i E_i = f(x_i) - y_i Ei=f(xi)−yi。接下来,它检查是否满足KKT条件,如果违反了KKT条件,则将该 α 1 \alpha_1 α1 选择为待优化的对象。
注意: 在这里不是直接选择
违反KKT条件最严重的 α 1 \alpha_1 α1,而是进行逐个检查,该方法在实践中通常能够获得不错的结果。
参数介绍
- 迭代次数(max_passes):这是外循环的最大迭代次数。
- 容忍度(tol):用于KKT条件违反的测量。
- C:SVM的正则化参数。
- gamma:核函数的参数。
python
def smo_algorithm(X, y, C, tol, max_passes, gamma):
''''
1.初始化
2.循环优化
3.判断违反KKT条件
4.选择a2
5.优化a1,a2,并更新
'''
m = len(y)
b = 0
passes = 0
alpha = np.zeros(m)
E = np.zeros(m)
while passes < max_passes :
alpha_change_times = 0
for i in range(m):
E[i] = compute_fx(X , y , alpha , b , X[i]) - y[i]
if (y[i]*E[i] < -tol and alpha[i] < C) or (y[i]*E[i] > tol and alpha[i] > 0):
j = select_alpha2(E,i)
if update_alpha_and_b(X , y , alpha , b , i , j , E , C):
alpha_change_times += 1
if alpha_change_times == 0:
passes += 1;
else:
passes = 0
return alpha , b
步骤八:主函数中调用
在这一步中,我们将配置 SMO 算法的参数并调用算法函数。这包括设置正则化参数 C,容忍度 tol,最大迭代次数 max_passes,以及高斯核的带宽参数 gamma(默认为1÷特征数
)。
python
# 运行SMO算法
C = 1.0
tol = 1e-3
max_passes = 10
gamma = 0.0081967213114754
alpha, b = smo_algorithm(X_train_scaled, y_train, C, tol, max_passes, gamma)
步骤九:记录时间和精度
此步骤涉及计算 SMO 算法的训练时间以及在测试集上的精度。这允许我们评估算法的效率和效果。首先记录算法开始的时间,然后进行预测并计算完成预测的时间,最后计算并打印出算法的精度。
python
start_time = time.time()
# 进行预测
predictions = predict(X_train_scaled, y_train, X_test_scaled, alpha, b, gamma)
training_time = time.time() - start_time
# 计算准确率
accuracy = np.mean(predictions == y_test)
print("SMO训练时间: {:.2f}秒".format(training_time))
print("SMO测试集准确率: {:.2%}".format(accuracy))
步骤十:对比libsvm实现精度和时间
在这一步中,我们使用 sklearn 库的 SVM 实现(libsvm)进行同样的任务,以便与我们自己实现的 SMO 算法进行性能对比。我们配置与自定义 SMO 相同的参数,运行 libsvm 的 SVC,记录时间,并计算精度,以展示两种方法的性能差异。
python
from sklearn import svm
# 使用libsvm的SVC
model = svm.SVC(kernel='rbf',tol=tol, C=1.0, gamma=0.0081967213114754)
start_time = time.time()
model.fit(X_train_scaled, y_train)
training_time = time.time() - start_time
# 进行预测
predictions_libsvm = model.predict(X_test_scaled)
accuracy_libsvm = accuracy_score(y_test, predictions_libsvm)
print("libsvm训练时间: {:.2f}秒".format(training_time))
print("libsvm测试集准确率: {:.2%}".format(accuracy_libsvm))