首页 > 其他分享 >复现经典:《统计学习方法》第 7 章 支持向量机

复现经典:《统计学习方法》第 7 章 支持向量机

时间:2022-11-09 13:31:07浏览次数:40  
标签:i1 self i2 new 复现 经典 alpha 向量


本文是李航老师的《统计学习方法》[1]一书的代码复现。

作者:黄海广[2]

备注:代码都可以在github[3]中下载。

我将陆续将代码发布在公众号“机器学习初学者”,敬请关注。

代码目录
  • 第 1 章 统计学习方法概论
  • 第 2 章 感知机
  • 第 3 章 k 近邻法
  • 第 4 章 朴素贝叶斯
  • 第 5 章 决策树
  • 第 6 章 逻辑斯谛回归
  • 第 7 章 支持向量机
  • 第 8 章 提升方法
  • 第 9 章 EM 算法及其推广
  • 第 10 章 隐马尔可夫模型
  • 第 11 章 条件随机场
  • 第 12 章 监督学习方法总结

代码参考:wzyonggege[4],WenDesi[5],火烫火烫的[6]

第 7 章 支持向量机

1.支持向量机最简单的情况是线性可分支持向量机,或硬间隔支持向量机。构建它的条件是训练数据线性可分。其学习策略是最大间隔法。可以表示为凸二次规划问题,其原始最优化问题为

求得最优化问题的解为 , ,得到线性可分支持向量机,分离超平面是

分类决策函数是

最大间隔法中,函数间隔与几何间隔是重要的概念。

线性可分支持向量机的最优解存在且唯一。位于间隔边界上的实例点为支持向量。最优分离超平面由支持向量完全决定。二次规划问题的对偶问题是

通常,通过求解对偶问题学习线性可分支持向量机,即首先求解对偶问题的最优值

,然后求最优值 和 ,得出分离超平面和分类决策函数。

2.现实中训练数据是线性可分的情形较少,训练数据往往是近似线性可分的,这时使用线性支持向量机,或软间隔支持向量机。线性支持向量机是最基本的支持向量机。

对于噪声或例外,通过引入松弛变量 ,使其“可分”,得到线性支持向量机学习的凸二次规划问题,其原始最优化问题是

求解原始最优化问题的解 和 ,得到线性支持向量机,其分离超平面为

分类决策函数为

线性可分支持向量机的解 唯一但 不唯一。对偶问题是

线性支持向量机的对偶学习算法,首先求解对偶问题得到最优解 ,然后求原始问题最优解 和 ,得出分离超平面和分类决策函数。

对偶问题的解 中满 的实例点 称为支持向量。支持向量可在间隔边界上,也可在间隔边界与分离超平面之间,或者在分离超平面误分一侧。最优分离超平面由支持向量完全决定。

线性支持向量机学习等价于最小化二阶范数正则化的合页函数

3.非线性支持向量机

对于输入空间中的非线性分类问题,可以通过非线性变换将它转化为某个高维特征空间中的线性分类问题,在高维特征空间中学习线性支持向量机。由于在线性支持向量机学习的对偶问题里,目标函数和分类决策函数都只涉及实例与实例之间的内积,所以不需要显式地指定非线性变换,而是用核函数来替换当中的内积。核函数表示,通过一个非线性转换后的两个实例间的内积。具体地, 是一个核函数,或正定核,意味着存在一个从输入空间 x 到特征空间的映射 ,对任意 ,有

对称函数

,任意正整数

,对称函数 对应的 Gram 矩阵是半正定的。

所以,在线性支持向量机学习的对偶问题中,用核函数 替代内积,求解得到的就是非线性支持向量机

4.SMO 算法

SMO 算法是支持向量机学习的一种快速算法,其特点是不断地将原二次规划问题分解为只有两个变量的二次规划子问题,并对子问题进行解析求解,直到所有变量满足 KKT 条件为止。这样通过启发式的方法得到原二次规划问题的最优解。因为子问题有解析解,所以每次计算子问题都很快,虽然计算子问题次数很多,但在总体上还是高效的。


分离超平面:

点到直线距离:

为 2-范数:

直线为超平面,样本可表示为:

margin:

函数间隔

几何间隔: ,当数据被正确分类时,几何间隔就是点到超平面的距离

为了求几何间隔最大,SVM 基本问题可以转化为求解:( 为几何间隔,( 为函数间隔)

分类点几何间隔最大,同时被正确分类。但这个方程并非凸函数求解,所以要先 ① 将方程转化为凸函数,② 用拉格朗日乘子法和 KKT 条件求解对偶问题。

① 转化为凸函数:

先令 ,方便计算(参照衡量,不影响评价结果)

再将 转化成 求解凸函数,1/2 是为了求导之后方便计算。

② 用拉格朗日乘子法和 KKT 条件求解最优值:

整合成:

推导:

根据 KKT 条件:

代入

再把 max 问题转成 min 问题:

以上为 SVM 对偶问题的对偶形式


kernel

在低维空间计算获得高维空间的计算结果,也就是说计算结果满足高维(满足高维,才能说明高维下线性可分)。

soft margin & slack variable

引入松弛变量 ,对应数据点允许偏离的 functional margin 的量。

目标函数:

对偶问题:


Sequential Minimal Optimization

首先定义特征到结果的输出函数: .

因为


参考资料:

[1] :Lagrange Multiplier and KKT[7]

[2] :推导 SVM[8]

[3] :机器学习算法实践-支持向量机(SVM)算法原理[9]

[4] :Python 实现 SVM[10]

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
%matplotlib inline
# data
def create_data():
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = [
'sepal length', 'sepal width', 'petal length', 'petal width', 'label'
]
data = np.array(df.iloc[:100, [0, 1, -1]])
for i in range(len(data)):
if data[i, -1] == 0:
data[i, -1] = -1
# print(data)
return data[:, :2], data[:, -1]
X, y = create_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
plt.scatter(X[:50,0],X[:50,1], label='0')
plt.scatter(X[50:,0],X[50:,1], label='1')
plt.legend()

class SVM:
def __init__(self, max_iter=100, kernel='linear'):
self.max_iter = max_iter
self._kernel = kernel


def init_args(self, features, labels):
self.m, self.n = features.shape
self.X = features
self.Y = labels
self.b = 0.0


# 将Ei保存在一个列表里
self.alpha = np.ones(self.m)
self.E = [self._E(i) for i in range(self.m)]
# 松弛变量
self.C = 1.0


def _KKT(self, i):
y_g = self._g(i) * self.Y[i]
if self.alpha[i] == 0:
return y_g >= 1
elif 0 < self.alpha[i] < self.C:
return y_g == 1
else:
return y_g <= 1


# g(x)预测值,输入xi(X[i])
def _g(self, i):
r = self.b
for j in range(self.m):
r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])
return r


# 核函数
def kernel(self, x1, x2):
if self._kernel == 'linear':
return sum([x1[k] * x2[k] for k in range(self.n)])
elif self._kernel == 'poly':
return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2


return 0


# E(x)为g(x)对输入x的预测值和y的差
def _E(self, i):
return self._g(i) - self.Y[i]


def _init_alpha(self):
# 外层循环首先遍历所有满足0<a<C的样本点,检验是否满足KKT
index_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C]
# 否则遍历整个训练集
non_satisfy_list = [i for i in range(self.m) if i not in index_list]
index_list.extend(non_satisfy_list)


for i in index_list:
if self._KKT(i):
continue


E1 = self.E[i]
# 如果E2是+,选择最小的;如果E2是负的,选择最大的
if E1 >= 0:
j = min(range(self.m), key=lambda x: self.E[x])
else:
j = max(range(self.m), key=lambda x: self.E[x])
return i, j


def _compare(self, _alpha, L, H):
if _alpha > H:
return H
elif _alpha < L:
return L
else:
return _alpha


def fit(self, features, labels):
self.init_args(features, labels)


for t in range(self.max_iter):
# train
i1, i2 = self._init_alpha()


# 边界
if self.Y[i1] == self.Y[i2]:
L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)
H = min(self.C, self.alpha[i1] + self.alpha[i2])
else:
L = max(0, self.alpha[i2] - self.alpha[i1])
H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])


E1 = self.E[i1]
E2 = self.E[i2]
# eta=K11+K22-2K12
eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(
self.X[i2],
self.X[i2]) - 2 * self.kernel(self.X[i1], self.X[i2])
if eta <= 0:
# print('eta <= 0')
continue


alpha2_new_unc = self.alpha[i2] + self.Y[i2] * (
E1 - E2) / eta #此处有修改,根据书上应该是E1 - E2,书上130-131页
alpha2_new = self._compare(alpha2_new_unc, L, H)


alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (
self.alpha[i2] - alpha2_new)


b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (
alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
self.X[i2],
self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.b
b2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (
alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
self.X[i2],
self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.b


if 0 < alpha1_new < self.C:
b_new = b1_new
elif 0 < alpha2_new < self.C:
b_new = b2_new
else:
# 选择中点
b_new = (b1_new + b2_new) / 2


# 更新参数
self.alpha[i1] = alpha1_new
self.alpha[i2] = alpha2_new
self.b = b_new


self.E[i1] = self._E(i1)
self.E[i2] = self._E(i2)
return 'train done!'


def predict(self, data):
r = self.b
for i in range(self.m):
r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])


return 1 if r > 0 else -1


def score(self, X_test, y_test):
right_count = 0
for i in range(len(X_test)):
result = self.predict(X_test[i])
if result == y_test[i]:
right_count += 1
return right_count / len(X_test)


def _weight(self):
# linear model
yx = self.Y.reshape(-1, 1) * self.X
self.w = np.dot(yx.T, self.alpha)
return self.w
svm = SVM(max_iter=200)
svm.fit(X_train, y_train)
'train done!'
svm.score(X_test, y_test)
0.92

scikit-learn 实例

from sklearn.svm import SVC
clf = SVC()
clf.fit(X_train, y_train)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
clf.score(X_test, y_test)
0.96

sklearn.svm.SVC

(C=1.0, kernel='rbf', degree=3, gamma='auto', coef0=0.0, shrinking=True, probability=False,tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape=None,random_state=None)

参数:

  • C:C-SVC 的惩罚参数 C?默认值是 1.0

C 越大,相当于惩罚松弛变量,希望松弛变量接近 0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C 值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。

  • kernel :核函数,默认是 rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’
    – 线性:u'v
    – 多项式:(gamma*u'*v + coef0)^degree
    – RBF 函数:exp(-gamma|u-v|^2)
    – sigmoid:tanh(gamma*u'*v + coef0)
  • degree :多项式 poly 函数的维度,默认是 3,选择其他核函数时会被忽略。
  • gamma :‘rbf’,‘poly’ 和‘sigmoid’的核函数参数。默认是’auto’,则会选择 1/n_features
  • coef0 :核函数的常数项。对于‘poly’和 ‘sigmoid’有用。
  • probability :是否采用概率估计?.默认为 False
  • shrinking :是否采用 shrinking heuristic 方法,默认为 true
  • tol :停止训练的误差值大小,默认为 1e-3
  • cache_size :核函数 cache 缓存大小,默认为 200
  • class_weight :类别的权重,字典形式传递。设置第几类的参数 C 为 weight*C(C-SVC 中的 C)
  • verbose :允许冗余输出?
  • max_iter :最大迭代次数。-1 为无限制。
  • decision_function_shape :‘ovo’, ‘ovr’ or None, default=None3
  • random_state :数据洗牌时的种子值,int 值

主要调节的参数有:C、kernel、degree、gamma、coef0。

参考资料

[1] 《统计学习方法》: https://baike.baidu.com/item/统计学习方法/10430179
[2] 黄海广: https://github.com/fengdu78
[3] github: https://github.com/fengdu78/lihang-code
[4] wzyonggege: https://github.com/wzyonggege/statistical-learning-method
[5] WenDesi: https://github.com/WenDesi/lihang_book_algorithm
[6] 火烫火烫的

[7]:[Lagrange Multiplier and KK
[8]:[推导SVM
[9]:[机器学习算法实践-支持向量机(SVM)算法原理: http://pytlab.org/2017/08/15/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AE%97%E6%B3%95%E5%AE%9E%E8%B7%B5-%E6%94%AF%E6%8C%81%E5%90%91%E9%87%8F%E6%9C%BA-SVM-%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86/
[10] :[Python实现SVM

复现经典:《统计学习方法》第 7 章 支持向量机_核函数

复现经典:《统计学习方法》第 7 章 支持向量机_核函数

复现经典:《统计学习方法》第 7 章 支持向量机_支持向量机_03

标签:i1,self,i2,new,复现,经典,alpha,向量
From: https://blog.51cto.com/u_15671528/5835976

相关文章

  • 京东前端经典react面试题合集
    为什么调用setState而不是直接改变state?解答如果您尝试直接改变组件的状态,React将无法得知它需要重新渲染组件。通过使用setState()方法,React可以更新组件的UI。另......
  • 祥云杯2022-部分pwn复现
    1.bitheap2.27限制数量0xf、限制大小0x200、无UAFadd:存在一个off-by-oneedit:输入内容时,edit会把2进制转成16进制然后按位取反foriinrange(12): add(i,0xf8)f......
  • android 经典笔记总结
    pro android media developing graphics,music,videoand richmedia apps for smartphones and tablets第一章 android introduction  pla......
  • 01 向量与坐标 | 解析几何
    1.向量的概念与运算1.向量向量:既有大小又有方向的量叫做向量,或称矢量标量:只有大小(可用一个数值表示)向量的几何表示:有向线段\(\overrightarrow{P_1P_2}\)或者\(\vec......
  • 信呼v2.2.1文件上传漏洞复现
    前言:这个漏洞的复现呢也是借鉴了Y4tacker的博客(地址:https://blog.csdn.net/solitudi/article/details/118675321)环境配置:环境:win10phpamb下载地址:http://www.rockoa.c......
  • 孙荣辛|大数据穿针引线进阶必看——Google经典大数据知识
    大数据技术的发展是一个非常典型的技术工程的发展过程,荣辛通过对于谷歌经典论文的盘点,希望可以帮助工程师们看到技术的探索、选择过程,以及最终历史告诉我们什么是正确的选......
  • DNS区域传送漏洞复现
    DNS区域传送漏洞复现区域传送,是指DNS主从服务器之间的数据同步,保证数据的一致性,传送会利用DNS域,所以就称为DNS区域传送DNS区域传送有两种方式*axfr:完全区域传送*ixfr:增量......
  • 一个超经典 WinForm 卡死问题的再反思
    一:背景1.讲故事这篇文章起源于昨天的一位朋友发给我的dump文件,说它的程序出现了卡死,看了下程序的主线程栈,居然又碰到了OnUserPreferenceChanged导致的挂死问题,真的是经......
  • MySQL_事务_ACID经典面试题
    事务控制语言事务:一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行(事务是由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语......
  • 向量点乘与叉乘
    点乘又称向量内积,输入两个向量,输出一个标量(实数)。公式:向量内积表示了两个向量“对齐”的程度:易知,两个向量互相垂直时,他们的内积为零。叉乘又称向量外积,输入两......