SVM法即支持向量机(Support Vector Machine,SVM)法,由Vapnik等人于1995年提出,具有相对优良的性能指标。该方法是建立在统计学理论基础上的机器学习方法。通过学习算法,SVM可以自动寻找那些对分类有较好区分能力的支持向量,由此构造出的分类器可以最大化类与类的间隔,因而有较好的适应能力和较高的分准率。该方法只需要由各类域的边界样本的类别来决定最后的分类结果。
SVM属于有监督学习方法,即已知训练点的类别,求训练点和类别之间的对应关系,以便将训练集按照类别分开,或者是预测新的训练点所对应的类别。由于SVM在实例的学习中能够提供清晰直观的解释,所以其在文本分类、文字识别、图像分类、升序序列分类等方面的实际应用中,都呈现了非常好的性能。
SVM构建了一个分割两类的超平面(也可以扩展到多类问题)。在构建的过程中,SVM算法试图使两类之间的分割达到最大化,如图1所示。
图1 SVM划分算法示意图
以一个很大的边缘分隔两个类可以使期望泛化误差最小化。“最小化泛化误差”的含义是:当对新的样本(数值未知的数据点)进行分类时,基于学习所得的分类器(超平面),使得我们对其所属分类预测错误的概率被最小化。直觉上,这样的一个分类器实现了两个分类之间分离边缘最大化。图1解释了“最大化边缘”的概念。和分类器平面平行,分别穿过数据集中的一个或多个点的两个平面称为边界平面(Bounding Plane),这些边界平面的距离称为边缘(Margin),而“通过SVM学习”的含义是找到最大化这个边缘的超平面。落在边界平面上的数据集中的点称为支持向量(Support Vector)。这些点在这一理论中的作用至关重要,故称为“支持向量机”。支持向量机的基本思想简单总结起来,就是与分类器平行的两个平面,此两个平面能很好地分开两类不同的数据,且穿越两类数据区域集中的点,现在欲寻找最佳超几何分隔平面使之与两个平面的距离最大,如此便能实现分类总误差最小。
支持向量机最初是在研究线性可分问题的过程中提出的,所以这里先来介绍线性SVM的基本原理。不失一般性,假设容量为n的训练样本集 由两个类别组成,若 属于第一类,则记为 ;若 属于第二类,则记为 。
若存在分类超平面:
能够将样本正确地划分成两类,即相同类别的样本都落在分类超平面的同一侧,则称该样本是线性可分的,即满足:
此处,可知平面 和 即为该分类问题中的边界超平面,这个问题可以回归到线性规划问题。边界超平面 到原点的距离为 ;而边界超平面 到原点的距离为 。所以这两个边界超平面的距离是 。同时注意,这两个边界超平面是平行的。而根据SVM的基本思想,最佳超平面应该使两个边界平面的距离最大化,即最大化 ,也就是最小化其倒数,即:
为了求解这个超平面的参数,可以以最小化上式为目标,其要求满足式SVM-a的表达式,而该式中的两个表达式,可以综合表达为:
为此可以得到如下目标规划问题:
到这个形式以后,就可以很明显地看出来,它是一个凸优化问题,或者更具体地说,它是一个二次优化问题——目标函数是二次的,约束条件是线性的。这个问题可以用现成的QP(Quadratic Programming)的优化包进行求解。虽然这个问题确实是一个标准QP问题,但是它也有它的特殊结构,通过拉格朗日变换到对偶变量(Dual Variable)的优化问题之后,可以找到一种更加有效的方法来进行求解,而且通常情况下这种方法比直接使用通用的QP优化包进行优化高效得多,而且便于推广。拉格朗日变换的作用,简单地说,就是通过给每一个约束条件加上一个拉格朗日乘子(Lagrange multiplier)ɑ,就可以将约束条件融合到目标函数里去。也就是说把条件融合到一个函数里,现在只用一个函数表达式便能清楚地表达出我们的问题。该问题的拉格朗日表达式为:
其中, ,为Lagrange系数。然后依据拉格朗日对偶理论将其转化为对偶问题,即:
这个问题可以用二次规划方法求解。设求解所得的最优解为 ,则可以得到最优的 和 为:
其中, 和 为两个类别中任意的一对支持向量。最终得到的最优分类函数为:
在输入空间中,如果数据不是线性可分的,支持向量机通过非线性映射 将数据映射到某个其它点积空间(称为特征空间)F,然后在F 中执行上述线性算法。这只需计算点积 即可完成映射。在文献中,这一函数称为核函数(Kernel),用 表示。
支持向量机的理论有三个要点,即:
对于线性SVM,还有一种更便于理解的求解方法,即引入松弛变量,转化为纯线性规划问题。同时引入松弛变量后,SVM更符合大部分样本,因为对于大部分的情况,很难将所有的样本明显地分成两类,总有少数样本导致寻找不到最佳超平面的情况。
一个典型的线性SVM模型可以表示为:
Mangasarian证明该模型与下面模型的解几乎完全相同:
这样,对于二分类的SVM问题就可以转化为非常便于求解的线性规划问题了。
SVM具有许多很好的性质,因此它已经成为广泛使用的分类算法之一。下面简要总结一下SVM的一般特征。
(1)语法
SVM分类训练函数具有以下格式:
svm_classification(
source_table,
model_table,
dependent_varname,
independent_varname,
kernel_func,
kernel_params,
grouping_col,
params,
verbose
)
(2)参数
参数名称 |
数据类型 |
描述 |
source_table |
TEXT |
包含训练数据的表的名称。 |
model_table |
TEXT |
包含模型的输出表名称,输出表列如表2所示。 |
dependent_varname |
TEXT |
因变量列的名称。对于分类,此列可以包含任何类型的值,但必须假定两个不同的值。否则,将会抛出错误。 |
independent_varname |
TEXT |
自变量表达式列表。截取变量不应该包含在这个表达式中。有关截取信息,参阅内核参数中的‘fit_intercept’。注意表达式应该能够转换为DOUBLE PRECISION[]类型。 |
kernel_func(可选) |
TEXT |
核函数类型,缺省值为‘linear’。当前支持三种核类型,‘linear’、‘gaussian’和‘polynomial’。文本可以是三个字符串的任何子集,例如,kernel_func ='ga'将创建一个高斯核函数。 |
kernel_params(可选) |
TEXT |
缺省值为NULL。用逗号分隔的键值对中的非线性内核的参数。实际参数根据kernel_func的值而有所不同。详见后面的描述。 |
grouping_col(可选) |
TEXT |
缺省值为NULL。与SQL中的“GROUP BY”类似,是一个表达式列表,用于将输入数据集分组为离散组,每个组训练一个模型。注意,如果使用分组,则不支持交叉验证。 |
params(可选) |
TEXT |
缺省值为NULL。用逗号分隔的键值对中的优化和正则化参数。如果提供了值列表,则将执行交叉验证以从列表中选择最佳值。详见后面的描述。 |
verbose(可选) |
BOOLEAN |
缺省值为FALSE。是否详细输出训练结果。 |
表1 svm_classification函数参数说明
SVM生成的模型表包含以下列:
列名 |
数据类型 |
描述 |
coef |
FLOAT8 |
系数向量。 |
grouping_key |
TEXT |
标识数据所属的组。 |
num_rows_processed |
BIGINT |
处理的行数。 |
num_rows_skipped |
BIGINT |
由于缺失值或失败而跳过的行数。 |
num_iterations |
INTEGER |
随机梯度下降算法完成的迭代次数。该算法要么收敛在这个迭代次数中,要么达到优化参数中指定的最大数。 |
loss |
FLOAT8 |
SVM的目标函数值。 |
norm_of_gradient |
FLOAT8 |
目标函数(子)梯度的L2范数的值。 |
dep_var_mapping |
TEXT[] |
因变量标签的向量。第一项对应于-1,第二项对应+1。仅限内部使用。 |
表2 svm_classification函数输出表列说明
如果核函数不是线性的,则会创建一个名为
训练函数在产生输出表的同时,还会创建一个名为
列名 |
数据类型 |
描述 |
method |
TEXT |
‘svm’ |
version_number |
TEXT |
用于生成模型的MADlib版本。 |
source_table |
TEXT |
源数据表名。 |
model_table |
TEXT |
模型表名。 |
dependent_varname |
TEXT |
因变量。 |
independent_varname |
TEXT |
自变量。 |
kernel_func |
TEXT |
核函数。 |
kernel_parameters |
TEXT |
核参数以及随机特征映射数据。 |
grouping_col |
TEXT |
分组列。 |
optim_params |
TEXT |
优化参数字符串。 |
reg_params |
TEXT |
正则化参数字符串。 |
num_all_groups |
INTEGER |
SVM训练的组数。 |
num_failed_groups |
INTEGER |
SVM训练失败的组数。 |
total_rows_processed |
BIGINT |
所有组处理的总行数。 |
total_rows_skipped |
BIGINT |
由于缺少值或失败,所有组跳过的总行数。 |
表3 svm_classification函数概要输出表列说明
SVM回归训练函数具有以下格式:
svm_regression(source_table,
model_table,
dependent_varname,
independent_varname,
kernel_func,
kernel_params,
grouping_col,
params,
verbose
)
回归函数格式与分类基本相同,但在其输出的模型表中,没有因变量映射。以下参数的格式与svm_classification不同:
新颖性检测函数是一类SVM分类器,具有以下格式:
svm_one_class(
source_table,
model_table,
independent_varname,
kernel_func,
kernel_params,
grouping_col,
params,
verbose
)
新颖性检测函数的格式与分类函数基本相同,但未指定因变量名称。生成的模型表格式与分类函数相同。
核参数以包含逗号分隔的名-值对列表的字符串形式提供。所有这些命名参数都是可选的,它们的顺序无关紧要。必须使用格式“<参数名> = <值>”来指定参数的值,否则该参数将被忽略。
(1)通用核参数
本小节中的参数在params参数中作为字符串提供,其中包含逗号分隔的名-值对列表。所有这些命名参数都是可选的,它们的顺序无关紧要。必须使用“<参数名> = <值>” 格式指定参数的值,否则该参数将被忽略。
可以使用内置的交叉验证机制来执行超参数优化,该机制通过为params中的参数n_folds分配一个大于1的值来激活。注意,如果使用分组,则不支持交叉验证。
应该在列表中提供交叉验证参数的值。例如,如果想用L1范数正则化并使用集合{0.3,0.4,0.5}中的λ值,可在params中输入'lambda = {0.3,0.4,0.5},norm = L1,n_folds = 10'。使用'{}'和'[]'在这里都有效。
下面的参数并非都可以进行交叉验证。对于允许交叉验证的参数,其默认值以列表格式显示,例如[0.01]。
'init_stepsize = ,
decay_factor = ,
max_iter = ,
tolerance = ,
lambda = ,
norm = ,
epsilon = ,
eps_table = ,
validation_result = ,
n_folds = ,
class_weight = '
(1)语法
预测函数用于估计给定新预测变量的条件均值。相同的语法用于分类,回归和新颖性检测:
svm_predict(model_table,
new_data_table,
id_col_name,
output_table)
(2)参数
参数名称 |
数据类型 |
描述 |
model_table |
TEXT |
由训练函数产生的模型表。 |
new_data_table |
TEXT |
包含预测数据的表的名称。应该包含与训练期间使用的相同的特征。该表还应包含用于标识每一行的id_col_name列。 |
id_col_name |
TEXT |
输入表中id列的名称。 |
output_table |
TEXT |
输出预测写入的表的名称。如果该表名已被使用,则返回错误。 |
表4 svm_predict函数参数说明
预测函数输出表包含以下三列:
我们将利用MADlib的SVM相关函数解决根据天气情况预测是否打高尔夫球的问题。问题描述及其已知数据参见“MADlib——基于SQL的数据挖掘解决方案(21)——分类之KNN”。
1. 准备训练数据
drop table if exists dt_golf;
create table dt_golf
( id integer not null,
"outlook" int,
temperature double precision,
humidity double precision,
windy int,
class int );
insert into dt_golf values
(1, 1,85,85,0, 0), (2, 1,80,90,1, 0), (3, 2,83,78,0, 1),
(4, 3,70,96,0, 1), (5, 3,68,80,0, 1), (6, 3,65,70,1, 0),
(7, 2,64,65,1, 1), (8, 1,72,95,0, 0), (9, 1,69,70,0, 1),
(10,3,75,80,0, 1), (11,1,75,70,1, 1), (12,2,72,90,1, 1),
(13,2,81,75,0, 1), (14,3,71,80,1, 0);
drop table if exists dt_golf_svm, dt_golf_svm_summary;
select madlib.svm_classification
( 'dt_golf',
'dt_golf_svm',
'class',
'array[1, "outlook", temperature, humidity, windy]' );
\x on
select * from dt_golf_svm;
结果:
-[ RECORD 1 ]------+------------------------------------------------------------------------------------
coef | {-0.00432713875183,-0.0200483862661,0.463932931975,-0.421325305231,-0.170382962628}
loss | 24.0201945919
norm_of_gradient | 108.477372773758
num_iterations | 100
num_rows_processed | 14
num_rows_skipped | 0
dep_var_mapping | {0,1}
drop table if exists dt_golf_pred;
select madlib.svm_predict('dt_golf_svm', 'dt_golf', 'id', 'dt_golf_pred');
select t1.id, t1.class, t2.prediction, t2.decision_function,
case when class = t2.prediction then 'T' else 'F' end
from dt_golf t1 join dt_golf_pred t2 using (id) order by id;
结果:
id | class | prediction | decision_function | case
----+-------+------------+--------------------+------
1 | 0 | 1 | 3.59727274822207 | F
2 | 0 | 0 | -0.999401400435932 | T
3 | 1 | 1 | 5.59863563462298 | T
4 | 1 | 0 | -8.03639636147613 | F
5 | 1 | 0 | -2.22305734173013 | F
6 | 0 | 1 | 0.428013952026874 | F
7 | 1 | 1 | 2.09075593247297 | T
8 | 0 | 0 | -6.64710841976293 | T
9 | 1 | 1 | 2.49422541508707 | T
10 | 1 | 1 | 1.02447318209487 | T
11 | 1 | 1 | 5.10744004430908 | T
12 | 1 | 0 | -4.73091324250203 | F
13 | 1 | 1 | 5.93474568636598 | T
14 | 0 | 0 | -1.00164150843313 | T
(14 rows)
为了观察模型的正确性,这里就训练数据集进行预测。可以看到,使用线性模型14条记录预测错了5个。
drop table if exists
dt_golf_svm_gaussian,
dt_golf_svm_gaussian_summary,
dt_golf_svm_gaussian_random;
select madlib.svm_classification
( 'dt_golf',
'dt_golf_svm_gaussian',
'class',
'array[1, "outlook", temperature, humidity, windy]',
'gaussian',
'n_components=10',
'',
'init_stepsize=1, max_iter=200'
);
这次使用高斯核函数生成非线性模型。我们指定要运行的初始步长和最大迭代次数,并选择10作为训练SVM的空间维度。n_components参数值越大,产生的模型拟合度越高,但会产生过拟合风险。该模型将是一个10维向量,而不是像线性模型那样的5个向量。
\x on
select * from dt_golf_svm_gaussian;
结果:
-[ RECORD 1 ]------+------------------------------------------------------------------------------------------------------------------------------------------------------
coef | {-2.31367614318,2.78056086932,0.487193906431,2.44449162608,-1.83489385489,0.291183458912,4.44955544212,0.807401127481,-0.602291386893,-2.26545918234}
loss | 0.243473184802
norm_of_gradient | 0.0697815426601548
num_iterations | 184
num_rows_processed | 14
num_rows_skipped | 0
dep_var_mapping | {0,1}
drop table if exists dt_golf_pred_gaussian;
select madlib.svm_predict
('dt_golf_svm_gaussian', 'dt_golf', 'id', 'dt_golf_pred_gaussian');
select t1.id, t1.class, t2.prediction, t2.decision_function,
case when class = t2.prediction then 'T' else 'F' end
from dt_golf t1 join dt_golf_pred_gaussian t2 using (id) order by id;
结果:
id | class | prediction | decision_function | case
----+-------+------------+-------------------+------
1 | 0 | 0 | -1.75387189946328 | T
2 | 0 | 0 | -1.00586122649354 | T
3 | 1 | 1 | 1.46533752838822 | T
4 | 1 | 1 | 1.00087996595449 | T
5 | 1 | 1 | 1.18064534149082 | T
6 | 0 | 0 | -1.10258771667805 | T
7 | 1 | 1 | 1.02774696458501 | T
8 | 0 | 0 | -2.18628420555613 | T
9 | 1 | 1 | 1.18644089897747 | T
10 | 1 | 1 | 1.16863507721741 | T
11 | 1 | 1 | 4.65860062300603 | T
12 | 1 | 1 | 1.02528665961242 | T
13 | 1 | 1 | 2.73963443726011 | T
14 | 0 | 0 | -1.61538323627932 | T
(14 rows)
使用高斯非线性模型的预测全部正确。
drop table if exists
dt_golf_svm_gaussian,
dt_golf_svm_gaussian_summary,
dt_golf_svm_gaussian_random;
select madlib.svm_classification
( 'dt_golf',
'dt_golf_svm_gaussian',
'class',
'array[1, "outlook", temperature, humidity, windy]',
'gaussian',
'n_components=10',
'',
'init_stepsize=1, max_iter=200, class_weight=balanced'
);
select * from dt_golf_svm_gaussian;
结果:
-[ RECORD 1 ]------+------------------------------------------------------------------------------------------------------------------------------------------------------
coef | {-2.18145534331,3.31062345245,0.141540860754,2.36708359752,-1.82865677305,-0.275270727707,4.54041087912,0.966886454987,0.315504405212,-1.87737006094}
loss | 0.249680574733
norm_of_gradient | 0.0706654901253297
num_iterations | 184
num_rows_processed | 14
num_rows_skipped | 0
dep_var_mapping | {0,1}
drop table if exists dt_golf_pred_gaussian;
select madlib.svm_predict
('dt_golf_svm_gaussian', 'dt_golf', 'id', 'dt_golf_pred_gaussian');
select t1.id, t1.class, t2.prediction, t2.decision_function,
case when class = t2.prediction then 'T' else 'F' end
from dt_golf t1 join dt_golf_pred_gaussian t2 using (id) order by id;
结果:
id | class | prediction | decision_function | case
----+-------+------------+-------------------+------
1 | 0 | 0 | -2.34442488642669 | T
2 | 0 | 0 | -1.00369614179274 | T
3 | 1 | 1 | 1.26282962942157 | T
4 | 1 | 1 | 1.00044107011296 | T
5 | 1 | 1 | 1.2460139165158 | T
6 | 0 | 0 | -1.24176161354924 | T
7 | 1 | 1 | 1.00012511351823 | T
8 | 0 | 0 | -1.88185540622382 | T
9 | 1 | 1 | 1.22073804684154 | T
10 | 1 | 1 | 1.47846097886927 | T
11 | 1 | 1 | 4.04252542659997 | T
12 | 1 | 1 | 1.00208058062533 | T
13 | 1 | 1 | 2.63821631706999 | T
14 | 0 | 0 | -1.78130113014144 | T
(14 rows)