复现经典:《统计学习方法》第 7 章 支持向量机
本文是李航老师的《统计学习方法》[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.targetdf.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_iterself._kernel = kerneldef init_args(self, features, labels):self.m, self.n = features.shapeself.X = featuresself.Y = labelsself.b = 0.0# 将Ei保存在一个列表里self.alpha = np.ones(self.m)self.E = [self._E(i) for i in range(self.m)]# 松弛变量self.C = 1.0def _KKT(self, i):y_g = self._g(i) * self.Y[i]if self.alpha[i] == 0:return y_g >= 1elif 0 < self.alpha[i] < self.C:return y_g == 1else:return y_g <= 1# g(x)预测值,输入xi(X[i])def _g(self, i):r = self.bfor 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)**2return 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的样本点,检验是否满足KKTindex_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):continueE1 = 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, jdef _compare(self, _alpha, L, H):if _alpha > H:return Helif _alpha < L:return Lelse:return _alphadef fit(self, features, labels):self.init_args(features, labels)for t in range(self.max_iter):# traini1, 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-2K12eta = 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')continuealpha2_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.bb2_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.bif 0 < alpha1_new < self.C:b_new = b1_newelif 0 < alpha2_new < self.C:b_new = b2_newelse:# 选择中点b_new = (b1_new + b2_new) / 2# 更新参数self.alpha[i1] = alpha1_newself.alpha[i2] = alpha2_newself.b = b_newself.E[i1] = self._E(i1)self.E[i2] = self._E(i2)return 'train done!'def predict(self, data):r = self.bfor i in range(self.m):r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])return 1 if r > 0 else -1def score(self, X_test, y_test):right_count = 0for i in range(len(X_test)):result = self.predict(X_test[i])if result == y_test[i]:right_count += 1return right_count / len(X_test)def _weight(self):# linear modelyx = self.Y.reshape(-1, 1) * self.Xself.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] 火烫火烫的: https://blog.csdn.net/tudaodiaozhale
[7]:[Lagrange Multiplier and KKT: http://blog.csdn.net/xianlingmao/article/details/7919597
[8]:[推导SVM: https://my.oschina.net/dfsj66011/blog/517766
[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: http://blog.csdn.net/wds2006sdo/article/details/53156589
往期精彩回顾
那些年做的学术公益-你不是一个人在战斗
适合初学者入门人工智能的路线及资料下载
吴恩达机器学习课程笔记及资源(github标星12000+,提供百度云镜像)
吴恩达深度学习笔记及视频等资源(github标星8500+,提供百度云镜像)
《统计学习方法》的python代码实现(github标星7200+)
机器学习的数学精华(在线阅读版)
备注:加入本站微信群或者qq群,请回复“加群”
加入知识星球(4300+用户,ID:92416895),请回复“知识星球”
复现经典:《统计学习方法》第 7 章 支持向量机相关推荐
- 统计学习方法 | 第7章 支持向量机
第7章 支持向量机 <统计学习方法>Python代码实现 [转载自Github开源项目]https://github.com/fengdu78/lihang-code 1.支持向量机最简单 ...
- 统计学习方法——第1章(个人笔记)
统计学习方法--第1章 统计学习及监督学习概论 <统计学习方法>(第二版)李航,学习笔记 1.1 统计学习 1.特点 (1)以计算机及网络为平台,是建立在计算机及网络上的: (2)以数据为 ...
- 李航《统计学习方法》第二章课后答案链接
李航<统计学习方法>第二章课后答案链接 李航 统计学习方法 第二章 课后 习题 答案 http://blog.csdn.net/cracker180/article/details/787 ...
- 李航《统计学习方法》第一章课后答案链接
李航<统计学习方法>第一章课后答案链接 李航 统计学习方法 第一章 课后 习题 答案 http://blog.csdn.net/familyshizhouna/article/detail ...
- 统计学习方法笔记第二章-感知机
统计学习方法笔记第二章-感知机 2.1 感知机模型 2.2感知机学习策略 2.2.1数据集的线性可分型 2.2.2感知机学习策略 2.3感知机学习算法 2.3.1感知机算法的原始形式 2.3.2算法的 ...
- 【统计学习方法】线性可分支持向量机对鸢尾花(iris)数据集进行二分类
本文摘要 · 理论来源:[统计学习方法]第七章 SVM · 技术支持:pandas(读csv).numpy.sklearn.svm.svm思想.matplotlib.pyplot(绘图) · 代码目的 ...
- 统计学习方法 - 第1章 - 概论
全书章节 第1章 统计学习方法概论 第2章 感知机 第3章 k近邻法 第4章 朴素贝叶斯法 第5章 决策树 第6章 逻辑斯谛回归与最大熵模型 第7章 支持向量机 第8章 提升方法 第9章 EM算法及其 ...
- 统计学习方法笔记(七)-线性支持向量机原理及python实现
线性支持向量机 线性支持向量机 定义 线性支持向量机 线性支持向量机学习算法 代码案例 TensorFlow 案例地址 线性支持向量机 实际场景中训练数据往往不是线性可分的,当训练数据近似线性可分时, ...
- 统计学习方法笔记(六)-非线性支持向量机原理及python实现
非线性支持向量机 非线性支持向量机 定义 非线性支持向量机 算法 非线性支持向量机学习算法 代码案例 TensorFlow 案例地址 非线性支持向量机 定义 非线性支持向量机 从非线性分类训练集,通过 ...
- 一篇详解带你再次重现《统计学习方法》——第二章、感知机模型
个性签名:整个建筑最重要的是地基,地基不稳,地动山摇. 而学技术更要扎稳基础,关注我,带你稳扎每一板块邻域的基础. 博客主页:七归的博客 专栏:<统计学习方法>第二版--个人笔记 创作不易 ...
最新文章
- 图片的宽铺满全屏 微信小程序_2020冬至快乐图片!,图片祝福贺卡,朋友圈冬至早安问候语动态图片微信小程序...
- 自己珍藏一些有趣的Python子程序
- 2020年人工神经网络第二次作业-参考答案第七题
- LINUX应用修改硬件寄存器l,郝健: Linux内存管理学习笔记-第2节课【转】
- C++和Rust_C 还是 Rust:选择哪个用于硬件抽象编程 | Linux 中国
- 学习用户连接性要素之连接性(附XMIND整理笔记)
- C++虚函数表和多态
- 计算机网络 第六章 链路层与局域网
- 使用javamail通过代理发送邮件
- Android 第二课——命令行基本操作
- PHP中的数组(数据结构)
- 图解stylegan
- 怎么在qq空间引流?QQ空间生日栏怎么引流?
- 赛程表 (递归调用, 非递归调用)
- 使用Java 2D绘制黑白太极图案
- 【ES知识】ES基础查询语法一览
- 帝国cms小程序端源码
- laravel框架搭建voyager
- amdgpu kfd TTM create GTT
- mysql忘记服务器上密码怎么修改密码,MySQL忘记密码怎么办-MySQL修改密码(亲测可用)...