深入浅出SVM(PART III)
导读
今天是该系列第九篇文章,介绍支持向量机原理(PART III)。
支持向量机(Support Vector Machine)是由Vapnik等人于1995年提出来的,之后随着统计理论的发展,支持向量机SVM也逐渐受到了各领域研究者的关注,在很短的时间就得到了很广泛的应用。支持向量机是被公认的比较优秀的分类模型,同时,在支持向量机的发展过程中,其理论方面的研究得到了同步的发展,为支持向量机的研究提供了强有力的理论支撑。
本文为第三部分,介绍SVM原理第三章。
支持向量机的实践
核函数计算
开始编程实现前,可以先回顾下SVM理论介绍的文章——“深入浅出SVM(PART I)”和求解过程的文章——“深入浅出SVM(PART II)”
首先,导入基本库:
import numpy as np
import cPickle as pickle
先为SVM模型声明一个类,:
class SVM:
def __init__(self, dataSet, labels, C, toler, kernel_option):
self.train_x = dataSet # 训练特征
self.train_y = labels # 训练标签
self.C = C # 惩罚参数
self.toler = toler # 迭代的终止条件之一
self.n_samples = np.shape(dataSet)[0] # 训练样本个数
self.alphas = np.mat(np.zeros((self.n_samples, 1))) # 拉格朗日乘子
self.b = 0
self.error_tmp = np.mat(np.zeros((self.n_samples, 2))) # 保存误差E
self.kernel_opt = kernel_option # 选用的核函数及其参数
self.kernel_mat = calc_kernel(self.train_x, self.kernel_opt) # 核函数的输出
在类中,包含了模型的训练数据和参数等。其中calc_kernel函数的功能为根据指定的核函数kernel_opt计算样本的核函数矩阵,具体实现如下:
def calc_kernel(train_x, kernel_option):
'''
input: train_x 训练样本的特征值
kernel_option 核函数的类型以及参数
output: kernel_matrix 样本的核函数的值'''
m = np.shape(train_x)[0] # 样本个数
kernel_matrix = np.mat(np.zeros((m, m))) # 初始化样本之间的核函数值
for i in range(m):
kernel_matrix[:, i] = cal_kernel_value(train_x, train_x[i, :], kernel_option)
return kernel_matrix
其中核函数矩阵里的每一个值表示为第i个样本和第j个样本的核函数的值,在计算过程中,用cal_kernel_value实现每一个样本与其它样本的核函数值,实现如下:
def cal_kernel_value(train_x, train_x_i, kernel_option):
'''
input: train_x 训练样本
train_x_i 第i个训练样本
kernel_option 核函数的类型以及参数
output: kernel_value 样本之间的核函数的值 '''
kernel_type = kernel_option[0] # 核函数的类型
m = np.shape(train_x)[0] # 样本个数
kernel_value = np.mat(np.zeros((m, 1)))
if kernel_type == 'rbf': # rbf核函数
sigma = kernel_option[1]
if sigma == 0:
sigma = 1.0
for i in range(m):
diff = train_x[i, :] - train_x_i
kernel_value[i] = np.exp(diff * diff.T / (-2.0 * sigma**2))
else:
kernel_value = train_x * train_x_i.T
return kernel_value
在计算核函数值时只用了高斯核函数,其它核函数形式可自行实现。
训练
定义好核函数的计算方法后,接下来可以实现求解方法——SMO算法来进行模型的训练,如下:
def SVM_training(train_x, train_y, C, toler, max_iter, kernel_option = ('rbf', 0.431029)):
'''
input: train_x 训练特征
train_y 训练标签
C 惩罚系数
toler 迭代的终止条件
max_iter 最大迭代次数
kerner_option 核函数的类型及其参数
output: svm模型'''
svm = SVM(train_x, train_y, C, toler, kernel_option)
# 开始训练
entireSet = True
alpha_pairs_changed = 0
iteration = 0
while (iteration < max_iter) and ((alpha_pairs_changed > 0) or entireSet):
print("t iterration: ", iteration)
alpha_pairs_changed = 0
if entireSet:
# 遍历所有样本
for x in range(svm.n_samples):
alpha_pairs_changed += choose_and_update(svm, x)
iteration += 1
else:
# 非边界样本
bound_samples = []
for i in range(svm.n_samples):
if svm.alphas[i,0] > 0 and svm.alphas[i,0] < svm.C:
bound_samples.append(i)
for x in bound_samples:
alpha_pairs_changed += choose_and_update(svm, x)
iteration += 1
# 在所有样本和非边界样本之间交替
if entireSet:
entireSet = False
elif alpha_pairs_changed == 0:
entireSet = True
return svm
函数SVM_training通过在非边界样本或所有样本中交替遍历,选择出第一个需要优化的ai,优先选择遍历非边界样本,因为非边界样本更有可能需要调整,而边界样本常常不能得到进一步调整而留在边界上。循环遍历非边界样本并选出它们当中不满足KKT条件的样本进行调整,直到非边界样本全部满足KKT条件为止。当某一次遍历发现没有非边界样本需要调整时,就遍历所有样本,以检验是否整个集合都满足KKT条件。如果在整个集合的检验中又有样本需要被进一步调整时,就有必要再遍历非边界样本。最终不停地在“遍历所有样本”和“遍历非边界样本”之间切换,直到整个训练集都满足KKT条件为止。
在选择出第一个变量ai后,需要判断该变量是否满足KKT条件;然后选择第二个变量aj,具体实现函数如下:
def choose_and_update(svm, alpha_i):
'''
input: svm SVM模型
alpha_i 选择出的第一个变量'''
# 计算第一个样本的E_i
error_i = cal_error(svm, alpha_i)
# 判断选择出的第一个变量是否违反了KKT条件
if (svm.train_y[alpha_i] * error_i < -svm.toler) and (svm.alphas[alpha_i] < svm.C) or
(svm.train_y[alpha_i] * error_i > svm.toler) and (svm.alphas[alpha_i] > 0):
# 1、选择第二个变量
alpha_j, error_j = select_second_sample_j(svm, alpha_i, error_i)
alpha_i_old = svm.alphas[alpha_i].copy()
alpha_j_old = svm.alphas[alpha_j].copy()
# 2、计算上下界
if svm.train_y[alpha_i] != svm.train_y[alpha_j]:
L = max(0, svm.alphas[alpha_j] - svm.alphas[alpha_i])
H = min(svm.C, svm.C + svm.alphas[alpha_j] - svm.alphas[alpha_i])
else:
L = max(0, svm.alphas[alpha_j] + svm.alphas[alpha_i] - svm.C)
H = min(svm.C, svm.alphas[alpha_j] + svm.alphas[alpha_i])
if L == H:
return 0
# 3、计算eta
eta = 2.0 * svm.kernel_mat[alpha_i, alpha_j] - svm.kernel_mat[alpha_i, alpha_i]
- svm.kernel_mat[alpha_j, alpha_j]
if eta >= 0:
return 0
# 4、更新alpha_j
svm.alphas[alpha_j] -= svm.train_y[alpha_j] * (error_i - error_j) / eta
# 5、确定最终的alpha_j
if svm.alphas[alpha_j] > H:
svm.alphas[alpha_j] = H
if svm.alphas[alpha_j] < L:
svm.alphas[alpha_j] = L
# 6、判断是否结束
if abs(alpha_j_old - svm.alphas[alpha_j]) < 0.00001:
update_error_tmp(svm, alpha_j)
return 0
# 7、更新alpha_i
svm.alphas[alpha_i] += svm.train_y[alpha_i] * svm.train_y[alpha_j]
* (alpha_j_old - svm.alphas[alpha_j])
# 8、更新b
b1 = svm.b - error_i - svm.train_y[alpha_i] * (svm.alphas[alpha_i] - alpha_i_old)
* svm.kernel_mat[alpha_i, alpha_i]
- svm.train_y[alpha_j] * (svm.alphas[alpha_j] - alpha_j_old)
* svm.kernel_mat[alpha_i, alpha_j]
b2 = svm.b - error_j - svm.train_y[alpha_i] * (svm.alphas[alpha_i] - alpha_i_old)
* svm.kernel_mat[alpha_i, alpha_j]
- svm.train_y[alpha_j] * (svm.alphas[alpha_j] - alpha_j_old)
* svm.kernel_mat[alpha_j, alpha_j]
if (0 < svm.alphas[alpha_i]) and (svm.alphas[alpha_i] < svm.C):
svm.b = b1
elif (0 < svm.alphas[alpha_j]) and (svm.alphas[alpha_j] < svm.C):
svm.b = b2
else:
svm.b = (b1 + b2) / 2.0
# 9、更新error
update_error_tmp(svm, alpha_j)
update_error_tmp(svm, alpha_i)
return 1
else:
return 0
在SMO的实现函数中,先判断选择出第一个变量是否满足要求,同时需要计算第一个变量的误差值,见cal_error;当检查完第一个变量满足条件后,开始选择第二个变量,对第二个变量的选择标准是使得其变化最大,见select_second_sample。两个变量都完成更新后,此时需要重新计算b的值,最后再重新计算两个变量对应的误差,见update_error_tmp。
def cal_error(svm, alpha_k):
'''
input: svm SVM模型
alpha_k 选择出的变量
output: error_k 误差值 '''
output_k = float(np.multiply(svm.alphas, svm.train_y).T * svm.kernel_mat[:, alpha_k] + svm.b)
error_k = output_k - float(svm.train_y[alpha_k])
return error_k
def update_error_tmp(svm, alpha_k):
'''
input: svm SVM模型
alpha_k 选择出的变量
output: 对应误差值 '''
error = cal_error(svm, alpha_k)
svm.error_tmp[alpha_k] = [1, error]
def select_second_sample(svm, alpha_i, error_i):
'''
input: svm SVM模型
alpha_i 选择出的第一个变量
error_i E_i
output: alpha_j 选择出的第二个变量
error_j E_j '''
svm.error_tmp[alpha_i] = [1, error_i]
candidateAlphaList = np.nonzero(svm.error_tmp[:, 0].A)[0]
maxStep = 0
alpha_j = 0
error_j = 0
if len(candidateAlphaList) > 1:
for alpha_k in candidateAlphaList:
if alpha_k == alpha_i:
continue
error_k = cal_error(svm, alpha_k)
if abs(error_k - error_i) > maxStep:
maxStep = abs(error_k - error_i)
alpha_j = alpha_k
error_j = error_k
else: # 随机选择
alpha_j = alpha_i
while alpha_j == alpha_i:
alpha_j = int(np.random.uniform(0, svm.n_samples))
error_j = cal_error(svm, alpha_j)
return alpha_j, error_j
到这里整个核心训练过程基本就结束了~
- 解读GEO数据存放规律及下载,一文就够
- 从GEO数据库下载得到表达矩阵 一文就够
- 机器学习各类算法比较
- Kerberos环境下删除ZooKeeper服务注册信息问题分析
- 根据分组信息做差异分析- 这个一文不够的
- 如何使用Cloudera Manager启用YARN的HA
- 如何使用java代码通过JDBC连接Hive(附github源码)
- 差异分析得到的结果注释一文就够
- 如何使用Cloudera Manager禁用HDFS HA
- 转录组表达数据分析的一些可视化
- 如何使用java代码通过JDBC连接Impala(附Github源码)
- 点击此文,无需转发,即可下载上千个免费R包
- 如何使用Cloudera Manager启用HDFS的HA
- 刷剧不忘学CNN:TF+Keras识别辛普森一家人物 | 教程+代码+数据集
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 自定义注解详解及应用
- 微服务开源框架TARS 之 框架服务解析
- dotnet 在 UOS 国产系统上使用 Xamarin Forms 创建 xaml 界面的 GTK 应用
- K8s集群上使用Helm部署2.4.6版本Rancher集群
- VMware下安装CentOS
- leetcode多线程之按序打印
- 表格的实现
- 使用 Node.js 定制你的技术雷达:中篇
- 数据库PostrageSQL-在Windows上从源代码安装
- Redis的过期策略和内存淘汰策略及LRU算法详解
- 群晖Docker安装GitLab及腾讯企业邮件配置踩坑记录
- 基于docker搭建DNSmasq
- Django-admin配置和显示图标
- redis学习(八)
- 【剑指Offer】打印从1到最大的n位数