金融与技术学习兴趣小组
编译整理 | 一只小绿怪兽
在之前的文章中,我们已经介绍过两个分类算法,一个是KNN算法,一个是逻辑回归算法。其中,KNN算法的分类依据是距离,逻辑回归算法的分类依据是概率。本文要介绍的支持向量机,也是在分类问题中被广泛应用的一种算法,先来看一下它的定义。
在机器学习领域,支持向量机(Support Vector Machine,简称SVM)属于一种监督学习算法,可以用来解决分类和回归问题,其中,在分类问题中的应用更加广泛。
支持向量机是通过某种方式,在一个N维空间中找到一个最优超平面(类似于逻辑回归中的决策边界),把样本划分到不同的区域中,在不同区域的样本属于不同的类别,从而实现分类。
在几何体中,超平面(Hyperplane)是一维小于其环境空间的子空间。如果空间是3维的,那么它的超平面是2维平面,而如果空间是2维的,则其超平面是1维线。
在解决线性分类问题时,分类算法的核心是要找到一个分类器,把属于不同类别的样本分开,在找分类器的过程中,本质是通过最小化一个损失函数,确定一个最优的分类器。
对于逻辑回归算法,在确定最优分类器时,借助的工具有sigmoid函数和最大似然估计,对于接下来要介绍的支持向量机算法,借助的工具有最大化间隔和核技巧。
因此,简单理解的话,支持向量机算法是通过某种方式,找到一个最优的分类器,从而实现分类的过程。明白了这一点,下面就具体介绍一下该算法的原理。
最大化间隔 Maximize Margin
先来看一个栗子。
以二分类问题为例,假设有一个1维的数据集,表示重量,现在希望用某个标准,把数据集中的样本分成两类,超重和正常。
通过观察样本,我们可以选择一个阈值(临界值),制定一个规则,当样本值小于阈值时,将它归类为正常,当样本值大于阈值时,将它归类为超重。
现在有一个新样本(绿色球),按照上述规则,由于它的值大于阈值,所以应该把它分类为超重。但这么分的话,似乎并不合适,因为通过观察可以发现,这个新样本距离左侧的正常类别更近。
因此,上面选择的阈值不够合理。
如果把两个类别边缘点的中间点作为阈值,如上图,此时,新样本值小于阈值,所以应该把它分类为正常。这么做似乎更合理,因为新样本离正常类别的距离更近。
可以发现,选择阈值需要有一个标准,这个标准是最大化间隔(Maximize margin),其中,样本值和阈值之间最短的距离叫做间隔(Margin)。
当阈值为中点时,正常类别的margin和超重类别的margin相同,且为最大,符合最大化间隔的标准。
如果将阈值向左侧移动一点,如图中蓝色线,那么正常类别的margin会变小,同理,向右侧移动的话,超重类别的margin会变小,而我们要找的是让两个类别的margin都最大时的阈值。
因此,为了有效进行分类,需要构造一个最大化间隔的分类器,从而找到一个合适的阈值。
下面从公式的角度来看一下,最大化间隔是什么意思。首先,对比逻辑回归,看一下支持向量机的优化目标。
在逻辑回归中,当y的取值为1时,我们希望x轴的值远大于0,将y=1带入损失函数中,可以得到左下角的损失函数公式。图中的蓝色线代表逻辑回归的损失函数,橘色线则代表支持向量机的损失函数。
可以看出,橘色线的特点是,以z=1为分界点,当z大于等于1时,损失函数的值为0,其他情况下,损失函数的值大于0。
同理,当y的取值为0时,我们希望x轴的值远小于0,将y=0带入损失函数中,可以得到右下角的损失函数公式。图中的蓝色线代表逻辑回归的损失函数,橘色线则代表支持向量机的损失函数。
可以看出,橘色线的特点是,以z=-1为分界点,当z小于等于-1时,损失函数的值为0,其他情况下,损失函数的值大于0。
在上面的分析中,根据逻辑回归损失函数的特点,我们引出了支持向量机的损失函数图,它的名字叫铰链损失函数(Hinge loss),这里我们不过多纠结它是怎么来的,先直接记住结论,然后继续往下分析。
接下来,看一下具体的公式,优化目标的公式由两个部分组成,损失函数 + 正则化参数(惩罚项),正则化(Regularization)的概念在之前的文章中有介绍过,它的目的是防止模型过拟合。
将公式中的两个部分一般化为A和B,其中,λ是用来控制两个部分权重的一个参数,在支持向量机中,把这个参数放在A部分,并且用字母C来表示。又由于m为常数项,对最终的优化结果没有影响,所以可以去掉。
为了实现损失函数的最小化,当C的取值很大时,我们希望A的取值很小,最好是0。当A取0时,损失函数就可以简化成红色框线里的表达式。
软间隔 Soft margin
接下来,我们来思考一下,当样本中存在异常值(Outliers)的情况下该如何处理。
观察上图,按照最大化间隔的标准,阈值的位置如上图所示,靠近右侧的超重类别。当输入一个新样本(绿色球)时,会将它预测为正常类别,因为它在阈值的左侧。
但是,这么分似乎也不是很合理,因为新样本的值距离右侧的超重类别更近。这里的问题是,样本的数据集中存在异常值,可以看出,最大化间隔分类器对异常值是极其敏感的。因此,为了让得出的阈值对异常值不那么敏感,必须允许误分类(Misclassifications)。
在允许误分类的情况下, 比如,选择A、B两个样本值的中点作为阈值,此时,属于正常类别的异常值会被误分类为超重类别,新样本会被预测为超重类别。这样的预测结果更合理一些,因为新样本确实距离右侧的超重类别更近。
当允许存在误分类的情况下,样本值和阈值之间的距离叫做软间隔(Soft margin),当允许误分类的程度不同时,soft margin也会不同。在确定最优的soft margin时,用到的方法是交叉验证(Cross validation)。
这种用soft margin决定阈值位置的方法,就是在用soft margin分类器(Soft margin classifier)对样本进行分类,这个分类器也叫支持向量分类器(Support vector classifier)。
在soft margin边缘和里面的样本点叫做支持向量(Support vectors)。
当数据集是2维的,支持向量分类器就是一条1维直线。同样,也是通过交叉验证的方法去确定最优的soft margin。
当数据集是3维的,支持向量分类器就是一个2维平面。
可以看到,支持向量分类器能够解决异常值问题,而且因为它允许误分类,也可以解决相互重叠的分类问题(Overlapping classifications)。
根据前面推导出的表达式,支持向量机对误分类的控制,是通过参数C来实现的。
当C的值很大时,公式前半部分的损失函数对结果的重要性提升,它对异常值很敏感,容易出现过拟合的现象,如下图中的橙色线。
当C的值不是很大时,公式后半部分的正则化项的重要性相对提升,它对异常值的敏感度有所降低,允许一定程度的误分类,如下图中的黑色线。
核技巧 Kernel Trick
前面已经介绍了如何用最大化间隔的方法去找到一个最优的支持向量分类器,但它并不是万能的,对于线性不可分的样本,我们还需要借助另一个方法:核技巧。
对于下图中的样本,不论把阈值放在哪个位置,都不能很好地把两个类别区分开,需要借助核技巧的方法。
核技巧(Kernel trick)是一种用线性分类器去解决非线性问题的方法,它会借助核函数(Kernel functions),把原本在低维空间中线性不可分的样本,映射到线性可分的高维空间。
为了直观地理解核技巧的方法,下面举一个具体的例子来看一下它是怎么操作的。
原始样本是一个1维的数据集。
选择一个核函数:y=x^2,添加一个y轴,把低维数据转换到高维,即把1维数据转换成2维数据。比如,下图A点的值为0.5,对应在y轴的值就是0.5的平方,等于0.25。
将所有样本点都转换完成,原来的1维数据就变成了一个2维数据,此时能够找到一条直线(支持向量分类器),把两个类别的样本分开。
对于一个新样本(绿色球),将它的值平方,由于它落入了支持向量分类器的上方,它应该被分类为正常类别。
在上例中,我们选择的核函数是 y = x^2,但为什么是平方,不是立方,或者是平方根呢?这里就涉及到核函数的选择问题,核函数的形式有很多,需要具体问题具体分析,本文介绍两个核函数的形式,多项式核函数(Polynomial Kernel)和径向内核函数(Radial Basis Function Kernels,RBF Kernel)。
多项式核函数 Polynomial Kernel
在上例中,我们是用了一个带有多项式核函数y = x^2的支持向量机,在转换的高维空间去计算样本之间的关系,然后基于在高维空间中计算出的关系,找到了一个合适的支持向量分类器。
多项式核函数的一般形式如上图所示,其中,a和b代表数据集中两个不同的样本,r决定多项式的系数,d是多项式的次数。
当r=1/2,d=2时,多项式如上所示,最终能够表示成点积(Dot product)的形式,它的计算方法是把两组相同长度的数字序列(向量),对应位置的数字相乘并求和。在这里,点积的计算结果,可以理解为两个向量之间的相关关系。
点积展示了样本数据在高维空间中的坐标信息。括号中的位置1代表x轴,位置2代表y轴,位置3代表z轴,由于位置3的值相同,可以忽略不看。
当r=1,d=2时,多项式如上所示,x轴变成了√2 a,表示原来的样本值a应该向右移动到√2 a的位置,y轴不变,同样z轴是相同的常数,可以忽略不看。
回到r=1/2,d=2的例子,既然左侧的核函数与右侧的点积相等,我们可以将样本点的值带入到核函数中,进而得到它们以点积形式表示的、在高维空间的关系。
计算结果16,002.25就代表A、B两点在2维空间的关系,它被用来确定支持向量分类器的位置,即使我们并没有把数据实际转换到2维空间。
因此,通过上面的分析可以看出,多项式核函数用于计算一组样本之间的关系,因为它可以表示为点积的形式,而这里点积的计算结果就代表输入样本之间的关系。参数r决定多项式的系数,d决定多项式的次数,r和d的取值是用交叉验证法确定的。
一旦r和d的值确定了,就可以直接把原本低维空间的样本值带入多项式核函数中,通过计算,得到它们在高维空间的关系。
径向内核函数 RBF Kernel
类似于多项式核函数(Polynomial Kernel),径向内核函数(RBF Kernel)也是一种用来计算低维空间样本的高维空间关系的函数,它的一般表达式如下所示。
径向内核函数的做法类似于加权最邻近模型(Weighted nearest neighbor model),也就是说,离新样本近的样本,对它的分类结果影响很大,离新样本远的样本,对它的分类结果影响很小。
看一下公式中的参数,a、b代表两个不同的样本点,径向内核函数是关于a和b差值平方(距离的平方)的函数,gamma用来控制a和b差值平方对函数结果的影响程度。
比如,当gamma=1时,将距离比较近的A、B两个样本点的值代入公式,得到的结果是0.11,即低维空间的两点A、B,它们通过径向内核函数计算出的在高维空间的关系为0.11。
当gamma=2时,同样将A、B两个样本点的值代入公式,得到的结果是0.01,比gamma=1时得到的0.11要小。
再回到gamma=1时的例子,如果选择两个距离比较远的A和C点,得到的结果是一个非常接近于0的值。因此,当两个样本点的距离越远时,它们对彼此的影响越小。
和多项式核函数一样,当我们将样本值代入径向内核函数中,会得到它们在高维空间中的关系。0.11代表距离较近的A和B两点在高维空间的关系,非常接近于0的值则代表距离较远的A和C两点在高维空间的关系。
与多项式核函数不同的是,径向内核函数是在无限的高维空间里计算的,下面就从多项式核函数出发,看一下径向内核函数是如何在无限空间里进行操作的。
当r=0时,多项式核函数可以写成关于a和b的点积形式,只不过它还是1维的,比如当d=2时,是将原来1维空间中的样本值进行了平方,样本的位置发生了变动,但仍是在1维空间。
当多项式核函数的r=0,d取从1到无穷的不同值并相加时,就会得到一个包含无限空间的点积,这就是径向内核函数所做的事情。这里的推导会涉及到泰勒级数展开,本文就先不过多介绍啦。
Python 实例
我们可以导入sklearn.svm模块中的SVC类实现支持向量机算法,先利用训练集数据构建模型,再用测试集数据进行预测。
支持向量机算法的实现步骤与逻辑回归和KNN算法类似:
① 先导入相应的模块
② 划分训练集和测试集
③ 创建一个分类器
④ 放入训练集数据进行学习,得到预测模型
⑤ 在测试集数据上进行预测
【工具】Python 3
【数据】tushare.pro
【注】本文注重的是方法的讲解,请大家灵活掌握,代码中参数设置的是默认值,暂时不对参数调整做过多介绍。
import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
pd.set_option("expand_frame_repr", False) # 当列太多时不换行
# 设置token
token = 'your token'
ts.set_token(token)
pro = ts.pro_api()
# 导入000002.SZ前复权日线行情数据,保留收盘价列
df = ts.pro_bar(ts_code='000002.SZ', adj='qfq', start_date='20190101', end_date='20191231')
df.sort_values('trade_date', inplace=True)
df['trade_date'] = pd.to_datetime(df['trade_date'])
df.set_index('trade_date', inplace=True)
df = df[['close']]
# 计算当前、未来1-day涨跌幅
df['1d_future_close'] = df['close'].shift(-1)
df['1d_close_future_pct'] = df['1d_future_close'].pct_change(1)
df['1d_close_pct'] = df['close'].pct_change(1)
df.dropna(inplace=True)
# ====1代表上涨,0代表下跌
df.loc[df['1d_close_future_pct'] > 0, '未来1d涨跌幅方向'] = 1
df.loc[df['1d_close_future_pct'] df = df[['1d_close_pct', '未来1d涨跌幅方向']]
df.rename(columns={'1d_close_pct': '当前1d涨跌幅'}, inplace=True)
print(df.head())
当前1d涨跌幅 未来1d涨跌幅方向
trade_date
2019-01-03 0.007112 1.0
2019-01-04 0.035728 1.0
2019-01-07 0.004815 0.0
2019-01-08 -0.001997 1.0
2019-01-09 0.013203 0.0
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
# 创建特征 X 和标签 y
y = df['未来1d涨跌幅方向'].values
X = df.drop('未来1d涨跌幅方向', axis=1).values
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42)
# 创建一个支持向量分类器
# linear kernel
# svm = SVC(kernel='linear', C=1, random_state=0)
# RBF kernel
svm = SVC(kernel='rbf', random_state=0, gamma=.01, C=1)
# 放入训练集数据进行学习
svm.fit(X_train, y_train)
# 在测试集数据上进行预测
new_prediction = svm.predict(X_test)
print("Prediction: {}".format(new_prediction))
# 测算模型的表现:预测对的个数 / 总个数
print(svm.score(X_test, y_test))
Prediction: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]0.4948453608247423
需要获取可运行的Python程序脚本文件,请在本公众号私信关键字“支持向量机”获取下载链接。
总结
本文介绍了机器学习算法中的支持向量机,包括它的定义、原理、整个推导的过程,涉及到的概念有最大化间隔、软间隔、核技巧、多项式核函数、径向内核函数,并且在最后用Python实现了支持向量机算法。
希望本文能够对大家学习支持向量机算法有帮助啦,如果大家觉得有用,就点亮“在看”让更多小伙伴们看到吧
【推荐阅读】
图文实例教会你逻辑回归
机器学习算法入门之监督学习(I)
机器学习必备技能之“统计思维2.0”
机器学习必备技能之“统计思维1.0”
机器学习必备技能之“数据预处理”
数据科学必备基础之线性回归
Pandas必备技能之“分组聚合操作”
END
更多内容请关注“挖地兔”公众号。
【参考链接】
Support Vector Machine [StatQuest with Josh Starmer]【1】
Support Vector Machines [Andrew Ng]【2】
网页链接【3】
网页链接【4】
网页链接【5】
网页链接【6】
网页链接【7】
网页链接【8】
网页链接【9】
网页链接【10】