这算是我真正意义上认真去读的第一篇ML论文了, but, 我还是很多地方没有搞懂, 想想, 缓缓吧, 还是先熟练调用API 哈哈

原论文地址: https://www.microsoft.com/en-us/research/publication/sequential-minimal-optimization-a-fast-algorithm-for-training-support-vector-machines/

求解w, b

这其实是一个在运筹学里面的经典二次规划 (Quadratic Programming)问题, 可以在多项式时间内求解.

坐标轮转法

Coordinate Descent

  • 每次搜索值允许一个变量变化, 其余不变(像求偏导数), 沿坐标轴轮流进行搜索寻求优化
  • 把多元变量的优化问题,轮转地转为了单个变量的优化问题, 感觉就是偏导数的思想
  • 搜索过程不需要对目标函数求导(不像梯度下降那样来找方向)

将多维的无约束优化问题,转为一系列的最优化问题. 每次搜索值允许一个变量变化(其余不变), 即沿着n个线性无关的方向(通常是坐标方向) 轮流进行搜索.

我感觉最好的对比是梯度下降法, 不清楚可以看我之前的搬运: (有一个小难点是理解向量的方向,及在数学及代码该如何表示)

梯度下降 Python 实现: https://www.cnblogs.com/chenjieyouge/p/11667959.html

每一轮的迭代公式:

\(x_i ^k= x_{(i-1)}^k + \alpha_i ^k d_i ^k\)

  • 坐标方向: \(d_i = e_i\)

  • 轮次: k = 0,1,2....

  • 坐标: i = 1,2...

收敛条件:

\(||x_{now} ^k - x_{before} ^k|| <= \xi\)

即每两次变换后, 收敛达到了 设定的误差, 这样就 break了.

SMO

回顾Daul SVM

\(max_w \ W(a) = \sum \limits _{i=1}^n a_i - \frac {1}{2} \sum \limits_{i=1}^n \sum \limits_{j=1}^n y_i y_j a_i a_j <x_i, x_j> \\ s.t.\)

\(0<= a_i \le C \\ \sum \limits_{i=1}^n a_i y_i = 0\)

其KKT条件为:

  • \(a_i = 0, \ \rightarrow y_i(w^Tx_i + b) \ge 1\)

  • \(a_i = C, \ \rightarrow y_i(w^Tx_i + b) \le 1\)

  • \(0 \le a_i \le C, \ \rightarrow y_i(w^Tx_i + b) \ge 1\)

认识SMO

在前面已经贴出paper了, 就还是有点不太懂, 道阻且跻. 革命尚未成功, 同志仍需努力.

其特点是, 一次必须两个元素 \(a_i, a_j\), 原因在于要满足约束 \(\sum \limits _{i=1}^{n} a_i y_i = 0\)

max_iter , count = n, 0

while True:

​ count += 1

​ 1. 选择下一步需优化的 \(a_i, a_j\) (选择 使得值 向 最大值发展快的方向, 的 \(a_i, a_j\)

​ 2. 固定其他 a 值, 仅仅通过改变 \(a_i, a_j\) 来优化\(W(a)\)

​ IF 满足KKT条件收敛:

​ break

​ IF count >= max_iter :

​ break

SMO 算法过程

假设现在要优化 \(a1 和a2\), 根据约束条件 \(\sum \limits _{i=1}^{n} a_i y_i = 0\) , 可展开,移项得:

$a1y1 + a2y2 = 0 - \sum \limits _{i=3}^n a_i y_i $

就类似 已知 1+2+3+4 = 10 必然 1 + 2 = 10 - (3 + 4)

而等式右边必然是固定的, 可以用一个常数 \(\zeta\) 读作(\zeta) 来代替, 则:

\(a1y1 + a2y2 = \zeta\)

则 a1 可以用 a2 的函数, 即:

\(a1 = \frac {(\zeta - a2y2)} {y1} = (\zeta -a2y2)y1\)

因为 y 的值只能去 + - 1, 除和乘是一样的

原优化问题可转化为:

\(W(a1, a2...an) = W((\zeta - a2y2)y1, \ a2, a3....an)\)

将 a3, a4...an 固定, 看作常数, 于是优化函数可以写为:

\(m\ a2^2 + n \ a2 + c\) 的形式

如果忽略 a1, a2 的约束关系, 对该二次函数求导就可以得到最优解.

如果此时 a2' 是 a2 的解, 那么 a2 的新值为:

a1, a2 还受到 \(a1y1 + a2y2 = \zeta\) 的约束, 则必然存在一个上下界 H, L

$a2^{new} = $

IF \(a2^{new} \ge H: \rightarrow H\)

IF \(L \le a2^{new} \ge H: \rightarrow a2^{new}\)

IF \(a2^{new} \le L : \rightarrow L\)

斯坦福smo: http://cs229.stanford.edu/materials/smo.pdf

Python 实现SMO

我目前的半吊子水平, 就大概明白, 还是在 github 搬运一波 jerry 大佬的代码., 也是简化版.

我只是做了详细的注释, 当然, 要真正理解下面的代码, 需要掌握SVM的关键点

  • 凸优化推导及KKT条件

  • margin 的推导

  • svm 目标函数推导

  • svm 的拉格朗日函数 形式 推导

  • svm 的对偶形式推导

  • 带松弛变量的 svm 推导

  • 核函数的 svm 推导

"""
Author: Lasse Regin Nielsen
""" from __future__ import division, print_function
import os
import numpy as np
import random as rnd
filepath = os.path.dirname(os.path.abspath(__file__)) class SVM():
"""
Simple implementation of a Support Vector Machine using the
Sequential Minimal Optimization (SMO) algorithm for training.
"""
def __init__(self, max_iter=10000, kernel_type='linear', C=1.0, epsilon=0.001):
self.kernels = {
'linear' : self.kernel_linear,
'quadratic' : self.kernel_quadratic
}
self.max_iter = max_iter
self.kernel_type = kernel_type
self.C = C
self.epsilon = epsilon
def fit(self, X, y):
# Initialization
n, d = X.shape[0], X.shape[1]
alpha = np.zeros((n))
kernel = self.kernels[self.kernel_type]
count = 0
while True:
count += 1
alpha_prev = np.copy(alpha)
for j in range(0, n):
i = self.get_rnd_int(0, n-1, j) # Get random int i~=j
x_i, x_j, y_i, y_j = X[i,:], X[j,:], y[i], y[j]
k_ij = kernel(x_i, x_i) + kernel(x_j, x_j) - 2 * kernel(x_i, x_j)
if k_ij == 0:
continue
alpha_prime_j, alpha_prime_i = alpha[j], alpha[i]
(L, H) = self.compute_L_H(self.C, alpha_prime_j, alpha_prime_i, y_j, y_i) # Compute model parameters
self.w = self.calc_w(alpha, y, X)
self.b = self.calc_b(X, y, self.w) # Compute E_i, E_j
E_i = self.E(x_i, y_i, self.w, self.b)
E_j = self.E(x_j, y_j, self.w, self.b) # Set new alpha values
alpha[j] = alpha_prime_j + float(y_j * (E_i - E_j))/k_ij
alpha[j] = max(alpha[j], L)
alpha[j] = min(alpha[j], H) alpha[i] = alpha_prime_i + y_i*y_j * (alpha_prime_j - alpha[j]) # Check convergence
diff = np.linalg.norm(alpha - alpha_prev)
if diff < self.epsilon:
break if count >= self.max_iter:
print("Iteration number exceeded the max of %d iterations" % (self.max_iter))
return
# Compute final model parameters
self.b = self.calc_b(X, y, self.w)
if self.kernel_type == 'linear':
self.w = self.calc_w(alpha, y, X)
# Get support vectors
alpha_idx = np.where(alpha > 0)[0]
support_vectors = X[alpha_idx, :]
return support_vectors, count
def predict(self, X):
return self.h(X, self.w, self.b)
def calc_b(self, X, y, w):
b_tmp = y - np.dot(w.T, X.T)
return np.mean(b_tmp)
def calc_w(self, alpha, y, X):
return np.dot(X.T, np.multiply(alpha,y))
# Prediction
def h(self, X, w, b):
return np.sign(np.dot(w.T, X.T) + b).astype(int)
# Prediction error
def E(self, x_k, y_k, w, b):
return self.h(x_k, w, b) - y_k
def compute_L_H(self, C, alpha_prime_j, alpha_prime_i, y_j, y_i):
if(y_i != y_j):
return (max(0, alpha_prime_j - alpha_prime_i), min(C, C - alpha_prime_i + alpha_prime_j))
else:
return (max(0, alpha_prime_i + alpha_prime_j - C), min(C, alpha_prime_i + alpha_prime_j))
def get_rnd_int(self, a,b,z):
i = z
cnt=0
while i == z and cnt<1000:
i = rnd.randint(a,b)
cnt=cnt+1
return i
# Define kernels
def kernel_linear(self, x1, x2):
return np.dot(x1, x2.T)
def kernel_quadratic(self, x1, x2):
return (np.dot(x1, x2.T) ** 2)

详细注释版

我这里只是做了一个可能无关痛痒的注释, 然后从我平时写代码的习惯顺序, 重写排了下

"""
Author: Lasse Regin Nielsen
"""
import numpy as np
import random as rnd class SVM():
"""
Simple implementation of a Support Vector Machine using the
Sequential Minimal Optimization (SMO) algorithm for training.
""" def __init__(self, max_iter=10000, kernel_type='linear', C=1.0, epsilon=0.001):
self.kernels = {
'linear': self.kernel_linear,
'quadratic': self.kernel_quadratic
}
# 最大迭代次数,比如1000次, 在之内没有解也程序结束
self.max_iter = max_iter
# 核函数: 自定义了线性核, 二次项核 两种可选其一
self.kernel_type = kernel_type
# C 表示 alpha 的上界, 受KKT中, 偏导为0的约束
self.C = C
# 误差精度(判断收敛的)
self.epsilon = epsilon # 定义两个核函数, 多个也行,随意
def kernel_linear(self, x1, x2):
"""线性核(无核)"""
return np.dot(x1, x2.T) def kernel_quadratic(self, x1, x2):
"""多项式核(2次方)"""
return (np.dot(x1, x2.T) ** 2) def get_rnd_int(self, a, b, z):
"""随处初始化 [a,b]内的整数,作为下标"""
i = z
cnt = 0
while i == z and cnt < 1000:
i = rnd.randint(a, b)
cnt = cnt + 1
return i def compute_L_H(self, C, alpha_prime_j, alpha_prime_i, y_j, y_i):
"""计算函数的上下界,之前的 \zeta 约束"""
if (y_i != y_j):
return (max(0, alpha_prime_j - alpha_prime_i), min(C, C - alpha_prime_i + alpha_prime_j))
else:
return (max(0, alpha_prime_i + alpha_prime_j - C), min(C, alpha_prime_i + alpha_prime_j)) def calc_w(self, alpha, y, X):
"""计算w的值"""
return np.dot(X.T, np.multiply(alpha, y)) def calc_b(self, X, y, w):
"""计算偏置量b"""
b_tmp = y - np.dot(w.T, X.T)
return np.mean(b_tmp) # Prediction
def h(self, X, w, b):
"""根据值的正负号来分类"""
# y = np.sign(*args)
# -1 if y < 0, 0 if y == 0
# 1 if y > 0
return np.sign(np.dot(w.T, X.T) + b).astype(int) def predict(self, X):
"""输入x,对其做预测是 1 or -1"""
return self.h(X, self.w, self.b) # Prediction error
def E(self, x_k, y_k, w, b):
"""计算误差"""
return self.h(x_k, w, b) - y_k def fit(self, X, y):
# Initialization
# n表示样本的个数(行), d表示每个样本的维度(列)
n, d = X.shape[0], X.shape[1]
# 初始化 alpha为 [0,0,0...0], 每个分量对一个样本(1行数据)
alpha = np.zeros((n))
# 选择定义的核函数, kernels是字典, key是名称, value是自定义的方法名
kernel = self.kernels[self.kernel_type]
# 用监测控制最外层,最大迭代次数的, 每迭代一次,则 计数+1
count = 0
while True:
count += 1
# 对alpha 变化前的值,进行深拷贝
alpha_prev = np.copy(alpha)
for j in range(0, n):
# 随机选择[0, n-1]的整数作为下标
i = self.get_rnd_int(0, n - 1, j)
# 选中优化的2个样本xi, xj, yi, yj
x_i, x_j, y_i, y_j = X[i, :], X[j, :], y[i], y[j]
# 计算该样本在核函数映射(R^n->n 下的实数值
k_ij = kernel(x_i, x_i) + kernel(x_j, x_j) - 2 * kernel(x_i, x_j)
# dual是要求max,等于0的就跳过,不用优化了呀
if k_ij == 0:
continue alpha_prime_j, alpha_prime_i = alpha[j], alpha[i]
# 计算出当前 a_i 的上下界
(L, H) = self.compute_L_H(self.C, alpha_prime_j, alpha_prime_i, y_j, y_i) # Compute model parameters
self.w = self.calc_w(alpha, y, X)
self.b = self.calc_b(X, y, self.w) # Compute E_i, E_j
E_i = self.E(x_i, y_i, self.w, self.b)
E_j = self.E(x_j, y_j, self.w, self.b) # Set new alpha values
alpha[j] = alpha_prime_j + float(y_j * (E_i - E_j)) / k_ij
alpha[j] = max(alpha[j], L)
alpha[j] = min(alpha[j], H) alpha[i] = alpha_prime_i + y_i * y_j * (alpha_prime_j - alpha[j]) # 计算向量alpha新前后的范数,得到实数值,并判断是否收敛
diff = np.linalg.norm(alpha - alpha_prev)
if diff < self.epsilon:
break if count >= self.max_iter:
print("Iteration number exceeded the max of %d iterations" % (self.max_iter))
return
# Compute final model parameters
self.b = self.calc_b(X, y, self.w)
if self.kernel_type == 'linear':
self.w = self.calc_w(alpha, y, X)
# Get support vectors
alpha_idx = np.where(alpha > 0)[0]
support_vectors = X[alpha_idx, :]
return support_vectors, count

小结

关于写篇还是很有难度的, 涉及到2篇论文和前面的smv推导是想联系的. 此篇目的除了巩固svm之外, 更多是想回归到代码层面, 毕竟能写出代码,才是我的本职, 与我而言, 理论推导的目的为了成功更加自信的调参侠, 当发现调参搞不定业务需求的时候, 那我就自己写一个, 也不是很难, 毕竟,如果数学原理都整明白了, 代码还很难嘛, 我不相信

其次, 关于代码,正如 jerry 大佬说认为, 跟咱平时写作文是一样的. 一开始都是认识单词, 然后能组句子, 然后能写一个段落, 然后抄满分作文, 增删改一下, 然后开始去大量阅读, 慢慢地才有了自己的思路和想法, 才著文章. 抄和前人的认知,我觉得是最为重要的一点.

钱钟书先生曾认为, "牢骚之人善著文章". 包含两个关键点: 一是阅读量, 一个是"牢骚", 即不安于现状, 善于发现不合理和提出自己的观点. 这岂不就是我写代码的真谛? 有空就多抄写和模仿大佬的代码, 然后有想法,自己去实现, "改变世界"?, 不存在的, 改变自我认识的同时, 顺带解决一些工作问题, 嗯....coding 其乐无穷, 我目标是能做当将其当做作文来写哦

还是重复一些, smo吧, 我也就掌握了70%左右, 代码自己整还是有点难度, 但要理解代码, 嗯, 如下的理论要求,真的是必须的:

  • 凸优化推导及KKT条件

  • margin 的推导

  • svm 目标函数推导

  • svm 的拉格朗日函数 形式 推导

  • svm 的对偶形式推导

  • 带松弛变量的 svm 推导

  • 核函数的 svm 推导

ML-求解 SVM 的SMO 算法的更多相关文章

  1. SVM之SMO算法(转)

    支持向量机(Support Vector Machine)-----SVM之SMO算法(转) 此文转自两篇博文 有修改 序列最小优化算法(英语:Sequential minimal optimizat ...

  2. 支持向量机(Support Vector Machine)-----SVM之SMO算法(转)

    此文转自两篇博文 有修改 序列最小优化算法(英语:Sequential minimal optimization, SMO)是一种用于解决支持向量机训练过程中所产生优化问题的算法.SMO由微软研究院的 ...

  3. 统计学习方法c++实现之六 支持向量机(SVM)及SMO算法

    前言 支持向量机(SVM)是一种很重要的机器学习分类算法,本身是一种线性分类算法,但是由于加入了核技巧,使得SVM也可以进行非线性数据的分类:SVM本来是一种二分类分类器,但是可以扩展到多分类,本篇不 ...

  4. <转>SVM实现之SMO算法

    转自http://blog.csdn.net/zouxy09/article/details/17292011 终于到SVM的实现部分了.那么神奇和有效的东西还得回归到实现才可以展示其强大的功力.SV ...

  5. SMO算法--SVM(3)

    SMO算法--SVM(3) 利用SMO算法解决这个问题: SMO算法的基本思路: SMO算法是一种启发式的算法(别管启发式这个术语, 感兴趣可了解), 如果所有变量的解都满足最优化的KKT条件, 那么 ...

  6. 深入理解SVM,详解SMO算法

    今天是机器学习专题第35篇文章,我们继续SVM模型的原理,今天我们来讲解的是SMO算法. 公式回顾 在之前的文章当中我们对硬间隔以及软间隔问题都进行了分析和公式推导,我们发现软间隔和硬间隔的形式非常接 ...

  7. 机器学习——支持向量机(SVM)之Platt SMO算法

    Platt SMO算法是通过一个外循环来选择第一个alpha值的,并且其选择过程会在两种方式之间进行交替: 一种方式是在所有数据集上进行单遍扫描,另一种方式则是在非边界alpha中实现单遍扫描. 所谓 ...

  8. [笔记]关于支持向量机(SVM)中 SMO算法的学习(一)理论总结

    1. 前言 最近又重新复习了一遍支持向量机(SVM).其实个人感觉SVM整体可以分成三个部分: 1. SVM理论本身:包括最大间隔超平面(Maximum Margin Classifier),拉格朗日 ...

  9. 关于SVM数学细节逻辑的个人理解(三) :SMO算法理解

    第三部分:SMO算法的个人理解 接下来的这部分我觉得是最难理解的?而且计算也是最难得,就是SMO算法. SMO算法就是帮助我们求解: s.t.   这个优化问题的. 虽然这个优化问题只剩下了α这一个变 ...

随机推荐

  1. Springboot之自定义配置

    SpringBoot自定义配置 springboot在这里就不过多介绍了,大家都应该了解springboot零配置文件,所以配置信息都装配在属性文件(properties.yml.yaml)中,有时我 ...

  2. (近万字)一篇文章带你了解HTML5和CSS3开发基础与应用-适合前端面试必备

    作者 | Jeskson 来源 | 达达前端小酒馆 HTML5和CSS3开发基础与应用,详细说明HTML5的新特性和新增加元素,CSS3的新特性,新增加的选择器,新的布局,盒子模型,文本,边框,渐变, ...

  3. postgresql小计

    1. postgresql执行结束后,判断结果是否成功,有几种结果 typedef enum { PGRES_EMPTY_QUERY = 0, /* empty query string was ex ...

  4. ASP.NET之MVC 微信公众号授权给第三方平台的技术实现流程(获取第三方平台access_token)

    “出于安全考虑,在第三方平台创建审核通过后,微信服务器每隔10分钟会向第三方的消息接收地址推送一次component_verify_ticket,用于获取第三方平台接口调用凭据”.这是文档中的原话,也 ...

  5. ThreadPoolExecutor源码2

    public class ThreadPoolExecutor1 extends AbstractExecutorService1 { // 11100000000000000000000000000 ...

  6. 读懂 ECMA 规格

    一般我们都不关心 ECMA 规范,只需要学习怎么使用就好了.但有时候遇到一些难以解释的问题/现象,就不得不看一下规范是如何要求实现的了.规范内容庞杂,理解其中的术语有利于我们快速看懂规范. Envir ...

  7. Oracle 层次查询 connect by

      oracle 层次查询 语法:       SELECT ... FROM            [WHERE condition]                             --过 ...

  8. 原生Ajax的怎么用?

    <script> function createXMLHttpRequest() { var xmlhttp; try { //先直接创建XMLHttpRequest xmlhttp = ...

  9. 把jar包安装到本地Maven仓库

    使用的场景 自己写的工具类想安装到本地 从Maven仓库中下载不下来的jar 使用的步骤       首先要保证自己的Maven配置全局环境变量,如果没有配置过maven全局变量,可以按照下面的步骤配 ...

  10. day60——单表操作补充(批量插入、查询、表结构)

    day60 批量插入(bulk_create) # bulk_create obj_list = [] for i in range(20): obj = models.Book( title=f'金 ...