SVM 是 Support Vector Machine 的缩写, 中文叫支持向量机, 通过它可以对样本数据进行分类. 以股票为例, SVM 能根据若干特征样本数据, 把待预测的目标结果划分成 "涨" 和 "跌" 两种, 从而实现预测股票涨跌的效果.
1 通过简单案例了解 SVM 的分类作用
在 Sklearn 库里, 封装了 SVM 分类的相关方法, 也就是说, 我们无需了解其中复杂的算法, 即可用它实现基于 SVM 的分类. 通过如下 SimpleSVMDemo.py 案例, 我们来看下通过 SVM 库实现分类的做法, 以及相关方法的调用方式.
- #!/usr/bin/env python
- #coding=utf-8
- import numpy as np
- import matplotlib.pyplot as plt
- from sklearn import svm
- #给出平面上的若干点
- points = np.r_[[[-1,1],[1.5,1.5],[1.8,0.2],[0.8,0.7],[2.2,2.8],[2.5,3.5],[4,2]]]
- #按 0 和 1 标记成两类
- typeName = [0,0,0,0,1,1,1]
在第 5 行里, 我们引入了基于 SVM 的库. 在第 7 行, 我们定义了若干个点, 并在第 9 行把这些点分成了两类, 比如 [-1,1] 点是第一类, 而 [4,2] 是第二类.
这里请注意, 在第 7 行定义点的时候, 是通过 np.r_方法, 把数据转换成 "列矩阵", 这样做的目的是让数据结构满足 fit 方法的要求.
- # 建立模型
- svmTool = svm.SVC(kernel='linear')
- svmTool.fit(points,typeName) #传入参数
- # 确立分类的直线
- sample = svmTool.coef_[0] #系数
- slope = -sample[0]/sample[1] #斜率
- lineX = np.arange(-2,5,1)# 获取 - 2 到 5, 间距是 1 的若干数据
- lineY = slope*lineX-(svmTool.intercept_[0])/sample[1]
在第 11 行里, 我们创建了基于 SVM 的对象, 并指定该 SVM 模型采用比较常用的 "线性核" 来实现分类操作.
在第 14 行, 通过 fit 训练样本. 这里 fit 方法和之前基于线性回归案例中的 fit 方法是一样的, 只不过这里是基于线性核的相关算法, 而之前是基于线性回归的相关算法(比如最小二乘法). 训练完成后, 通过第 14 行和第 15 行的代码, 我们得到了能分隔两类样本的直线, 包括直线的斜率和截距, 并通过第 16 行和第 17 行的代码设置了分隔线的若干个点.
- # 画出划分直线
- plt.plot(lineX,lineY,color='blue',label='Classified Line')
- plt.legend(loc='best') #绘制图例
- plt.scatter(points[:,0],points[:,1],c='R')
- plt.show()
计算完成后, 我们通过第 19 行的 plot 方法绘制了分隔线, 并在第 21 行通过 scatter 方法绘制所有的样本点. 由于 points 是 "列矩阵" 的数据结构, 所以是用 points[:,0]来获取绘制点的 x 坐标, 用 points[:,1]来获取 y 坐标, 最后是通过第 22 行的 show 方法绘制图形. 运行上述代码, 我们能看到如下图 13.8 的效果, 从中我们能看到, 蓝色的边界线能有效地分隔两类样本.
从这个例子中我们能看到, SVM 的作用是, 根据样本, 训练出能划分不同种类数据的边界线, 由此实现 "分类" 的效果. 而且, 在根据训练样本确定好边界线的参数后, 还能根据其它没有明确种类样本, 计算出它的种类, 以此实现 "预测" 效果.
2 数据标准化处理
标准化 (normalization) 处理是将特征样本按一定算法进行缩放, 让它们落在某个范围比较小的区间, 同时去掉单位限制, 让样本数据转换成无量纲的纯数值.
在用机器学习方法进行训练时, 一般需要进行标准化处理, 原因是 Sklearn 等库封装的一些机器学习算法对样本有一定的要求, 如果有些特征值的数量级偏离大多数特征值的数量级, 或者有特征值偏离正态分布, 那么预测结果会不准确.
需要说明的是, 虽然在训练前对样本进行了标准化处理, 改变了样本值, 但由于在标准化的过程中是用同一个算法对全部样本进行转换, 属于 "数据优化", 不会对后继的训练起到不好的作用.
这里我们是通过 sklearn 库提供的 preprocessing.scale 方法实现标准化, 该方法是让特征值减去平均值然后除以标准差. 通过如下 ScaleDemo.py 案例, 我们实际用下 preprocessing.scale 方法.
- #!/usr/bin/env python
- #coding=utf-8
- from sklearn import preprocessing
- import numpy as np
- origVal = np.array([[10,5,3],
- [8,6,12],
- [14,7,15]])
- # 计算均值
- avgOrig = origVal.mean(axis=0)
- # 计算标准差
- stdOrig=origVal.std(axis=0)
- # 减去均值, 除以标准差
- print((origVal-avgOrig)/stdOrig)
- scaledVal=preprocessing.scale(origVal)
- # 直接输出 preprocessing.scale 后的结果
- print(scaledVal)
在第 6 行里, 我们初始化了一个长宽各为 3 的矩阵, 在第 10 行, 通过 mean 方法计算了该矩阵的均值, 在第 12 行则通过 std 方法计算标准差.
第 14 行是用原始值减去均值, 再除以标准差, 在第 17 行, 是直接输出 preprocessing.scale 的结果. 第 14 行和第 17 行的输出结果相同, 均是下值, 从中我们验证了标准化的具体做法.
- [[-0.26726124 -1.22474487 -1.37281295]
- [-1.06904497 0. 0.39223227]
- [ 1.33630621 1.22474487 0.98058068]]
3 预测股票涨跌
在之前的案例中, 我们用基于 SVM 的方法, 通过一维直线来分类二维的点. 据此可以进一步推论: 通过基于 SVM 的方法, 我们还可以分类具有多个特征值的样本.
比如可以通过开盘价, 收盘价, 最高价, 最低价和成交量等特征值, 用 SVM 的算法训练出这些特征值和股票 "涨" 和 "跌" 的关系, 即通过特征值划分指定股票 "涨" 和 "跌" 的边界, 这样的话, 一旦输入其它的股票特征数据, 即可预测出对应的涨跌情况. 在如下的 PredictStockBySVM.py 案例中, 我们给出了基于 SVM 预测股票涨跌的功能.
- #!/usr/bin/env python
- #coding=utf-8
- import pandas as pd
- from sklearn import svm,preprocessing
- import matplotlib.pyplot as plt
- origDf=pd.read_csv('D:/stockData/ch13/6035052018-09-012019-05-31.csv',encoding='gbk')
- df=origDf[['Close', 'Low','Open' ,'Vol','Date']]
- #diff 列表示本日和上日收盘价的差
- df['diff'] = df["Close"]-df["Close"].shift(1)
- df['diff'].fillna(0, inplace = True)
- #up 列表示本日是否上涨, 1 表示涨, 0 表示跌
- df['up'] = df['diff']
- df['up'][df['diff']>0] = 1
- df['up'][df['diff']<=0] = 0
- # 预测值暂且初始化为 0
- df['predictForUp'] = 0
第 6 行里, 我们从指定文件读取了包含股票信息的 CSV 文件, 该 CSV 格式的文件其实是从网络数据接口获取得到的, 具体做法可以参考前面博文.
从第 9 行里, 我们设置了 df 的 diff 列为本日收盘价和前日收盘价的差值, 通过第 12 行到第 14 行的代码, 我们设置了 up 列的值, 具体是, 如果当日股票上涨, 即本日收盘价大于前日收盘价, 则 up 值是 1, 反之如果当日股票下跌, up 值则为 0.
在第 16 行里, 我们在 df 对象里新建了表示预测结果的 predictForUp 列, 该列的值暂且都设置为 0, 在后继的代码里, 将根据预测结果填充这列的值.
- # 目标值是真实的涨跌情况
- target = df['up']
- length=len(df)
- trainNum=int(length*0.8)
- predictNum=length-trainNum
- # 选择指定列作为特征列
- feature=df[['Close', 'High', 'Low','Open' ,'Volume']]
- # 标准化处理特征值
- feature=preprocessing.scale(feature)
在第 18 行里, 我们设置训练目标值是表示涨跌情况的 up 列, 在第 20 行, 设置了训练集的数量是总量的 80%, 在第 23 行则设置了训练的特征值, 请注意这里去掉了日期这个不相关的列, 而且, 在第 25 行, 对特征值进行了标准化处理.
- # 训练集的特征值和目标值
- featureTrain=feature[1:trainNum-1]
- targetTrain=target[1:trainNum-1]
- svmTool = svm.SVC(kernel='liner')
- svmTool.fit(featureTrain,targetTrain)
在第 27 行和第 28 行里, 我们通过截取指定行的方式, 得到了特征值和目标值的训练集, 在第 26 行里, 以线性核的方式创建了 SVM 分类器对象 svmTool.
在第 30 行里, 通过 fit 方法, 用特征值和目标值的训练集训练 svmTool 分类对象. 从上文里我们已经看到, 训练所用的特征值是开盘收盘价, 最高最低价和成交量, 训练所用的目标值是描述涨跌情况的 up 列. 在训练完成后, svmTool 对象中就包含了能划分股票涨跌的相关参数.
- predictedIndex=trainNum
- # 逐行预测测试集
- while predictedIndex<length:
- testFeature=feature[predictedIndex:predictedIndex+1]
- predictForUp=svmTool.predict(testFeature)
- df.ix[predictedIndex,'predictForUp']=predictForUp
- predictedIndex = predictedIndex+1
在第 33 行的 while 循环里, 我们通过 predictedIndex 索引值, 依次遍历测试集.
在遍历过程中, 通过第 35 行的 predict 方法, 用训练好的 svmTool 分类器, 逐行预测测试集中的股票涨跌情况, 并在第 36 行里, 把预测结果设置到 df 对象的 predictForUp 列中.
- # 该对象只包含预测数据, 即只包含测试集
- dfWithPredicted = df[trainNum:length]
- # 开始绘图, 创建两个子图
- figure = plt.figure()
- # 创建子图
- (axClose, axUpOrDown) = figure.subplots(2, sharex=True)
- dfWithPredicted['Close'].plot(ax=axClose)
- dfWithPredicted['predictForUp'].plot(ax=axUpOrDown,color="red", label='Predicted Data')
- dfWithPredicted['up'].plot(ax=axUpOrDown,color="blue",label='Real Data')
- plt.legend(loc='best') #绘制图例
- # 设置 x 轴坐标标签和旋转角度
- major_index=dfWithPredicted.index[dfWithPredicted.index%2==0]
- major_xtics=dfWithPredicted['Date'][dfWithPredicted.index%2==0]
- plt.xticks(major_index,major_xtics)
- plt.setp(plt.gca().get_xticklabels(), rotation=30)
- plt.title("通过 SVM 预测 603505 的涨跌情况")
- plt.rcParams['font.sans-serif']=['SimHei']
- plt.show()
由于在之前的代码里, 我们只设置测试集的 predictForUp 列, 并没有设置训练集的该列数据, 所以在第 39 行里, 用切片的手段, 把测试集数据放置到 dfWithPredicted 对象中, 请注意这里切片的起始和结束值是测试集的起始和结束索引值. 至此完成了数据准备工作, 在之后的代码里, 我们将用 matplotlib 库开始绘图.
在第 43 行里, 我们通过 subplots 方法设置了两个子图, 并通过 sharex=True 让这两个子图的 x 轴具有相同的刻度和标签. 在第 44 行代码里, 在 axClose 子图中, 我们用 plot 方法绘制了收盘价的走势. 在第 45 行代码里, 在 axUpOrDown 子图中, 我们绘制了预测到的涨跌情况, 而在第 46 行里, 还是在 axUpOrDown 子图里, 绘制了这些天的股票真实的涨跌情况.
在第 49 行到第 52 行的代码里, 我们设置了 x 标签的文字以及旋转角度, 这样做的目的是让标签文字看上去不至于太密集. 在第 53 行里, 我们设置了中文标题, 由于要显示中文, 所以需要第 54 行的代码, 最后在 55 行通过 show 方法展示了图片. 运行上述代码, 能看到如下图所示的效果.
其中上图展示了收盘价, 下图的蓝色线条表示真实的涨跌情况, 0 表示跌, 1 表示上涨, 而红色则表示预测后的结果.
4 结论
对比一下, 虽有偏差, 但大体相符. 综上所述, 本案例是数学角度, 演示了通过 SVM 分类的做法, 包括如果划分特征值和目标值, 如何对样本数据进行标准化处理, 如何用训练数据训练 SVM, 还有如何用训练后的结果预测分类结果.
5 总结和版权说明
本文是给程序员加财商系列, 之前还有两篇博文
用 python 的 matplotlib 和 numpy 库绘制股票 K 线均线和成交量的整合效果(含量化验证交易策略代码)
用 python 的 matplotlib 和 numpy 库绘制股票 K 线均线的整合效果(含从网络接口爬取数据和验证交易策略代码)
本文的内容即将出书, 在出版的书里, 是用股票案例和大家讲述 Python 入门时的知识点, 敬请期待
有不少网友转载和想要转载我的博文, 本人感到十分荣幸, 这也是本人不断写博文的动力. 关于本文的版权有如下统一的说明, 抱歉就不逐一回复了.
来源: https://www.cnblogs.com/JavaArchitect/p/11350683.html