机器量化分析(二)——模型评估与仓位管理
在上一篇中,我们介绍了简单的数据收集,数据预处理与建模案例,本篇承接上篇内容,主要介绍两部分: 模型评估,仓位管理 。
模型评估
在机器学习领域,有诸多评估模型的方法和指标,本篇介绍一个最简单常用的指标—— F1 分值 。
要计算模型的 F1 分值,就要了解 混淆矩阵 。但在介绍混淆矩阵之前,我们先从实际场景出发,去理解模型评估的思路。
当我们想设计一个模型评估的指标时,一个朴素的想法就是准确率,即模型正确预测的数量, Acc = T(正确预测的次数)/ N(总预测次数) 。但在实际的场景中,我们关注的往往是单边的预测准确率,比如在上篇的数据建模里,想要盈利,我们最关心的是预测股价上涨的概率,而预测不上涨的概率则不那么重要;那么公式就需要稍微修改一下: Acc = Tp(预测为上涨且预测正确的次数)/ N(总预测次数) 。这个公式虽然能描述正样本的预测准确率,但却有个 致命缺陷——当模型把所有样本都预测为正样本时,该公式的准确率是 100% !反映在实际场景中,就是无脑买多,这显然是不合理的。究其根本,问题出在公式的分母中,由于无论如何预测,分母都是不变的,那么显然把所有样本点都预测为正得到的准确率最大。为此,我们还需要修改公式,而且是从分母入手,可以有两种方案:
- 1. Acc(Precision) = Tp(预测为上涨且正确的次数)/ Tp + Fp(预测为上涨但实际不上涨的次数)。 该公式的分母,是所有预测为上涨(无论实际上涨与否)的次数。
- 2. Acc(Recall) = Tp(预测为上涨且正确的次数)/ Tp + Fn(预测不上涨但实际上涨的次数)。 该公式的分母,是所有实际上涨(无论预测上涨与否)的次数。
这两个公式没有好坏优劣之分,而是通过不同的角度来对模型进行评价。
现在,我们再来看混淆矩阵就非常简单了:
上面的两个公式,实际上是有区分的,第一个公式计算得到的值叫 精度(也叫查准率) ,反映的是 预测能力 。第二个公式计算得到的值叫 召回率(也叫查全率) ,反映的是对正样本的 拟合能力 。
下面我们结合一个实际的场景来解释这个混淆矩阵(图片来自 sklearn 官档,略作修改,侵删):
样本背景 :在过去 n 个交易日中(即图中圆点个数),个股股价相较于前一交易日, 涨的话是红色点,不涨是蓝色点 。横纵坐标分别是个股的两个自定义的 状态 (比如上篇中的收盘价和成交量)。
现在我们有一个最简单粗暴的分类预测器—— 画线,而且是直线 。
用第一个公式计算的话, Precision = 图中直线右上的所有红点个数 / 图中直线右上所有(红点 + 蓝点)的个数 。显然图中红灰黑三条直线中, 黑线的 Precision 最高,高达 100% ,但在所有红点中占比很小,反映在实际场景中,即遇到机会非常少,实用性下降。
再看第二个公式, Recall= 图中直线右上的所有红点个数 / 图中所有红点个数 。显然图中红灰黑三条直线中, 红线的 Recall 最大,接近 100% ,但同时也会有许多误判(红线右上的蓝点都被预测成了红点),反映在实际场景中,即遇到的机会增大,但误判的概率也变大了。
回到这个场景和图片本身,一个真正好的分类预测模型,应该是类似图中灰色直线的效果, 在 Precision 和 Recall 之间取一个平衡,在两者间取加权平均值的话就是 F1 分值 :
F1 = ( 2 * Precision * Recall ) / ( Precision + Recall )
图中红灰黑三条直线的 F1 分值,灰色线的 F1 最高,而红线由于 Precision 较低,黑线由于 Recall 较低,F1 分值不会太高。
接下来,我们通过代码来实现计算 F1 分值并评估模型,在实践中,我们不仅记录 F1 分值,同时还记录 Precision 和 Recall 以及负样本的 Precision,综合这几个指标 可以粗略判断出模型的状态,过拟合或欠拟合,从而为优化指出方向 。
代码的实现需要用到数据库操作,主要用到两张表,一张是 结果表 ,用于记录模型的 F1 分值等。另一张是 中间表 ,用于存储 F1 计算过程的一些变量,功能上与内存相似。
结果表——库名:stock 表名:model_ev_resu
字段名 | 字段类型 | 字段说明 |
---|---|---|
state_dt | varchar2(45) | 评估日期 |
stock_code | varchar2(45) | 股票代码 |
acc | decimal(20, 4) | 查准率 |
recall | decimal(20, 4) | 查全率 |
f1 | decimal(20, 4) | f1 分值 |
acc_neg | decimal(20, 4) | 查准率(负样本) |
bz | varchar2(45) | 用于标注模型类别,比如 svm、决策树等 |
predict | varchar2(45) | 对评估日后一个交易日的预测值 |
中间表——库名:stock 表名:model_ev_mid
字段名 | 字段类型 | 字段说明 |
---|---|---|
state_dt | varchar2(45) | 回测日期 |
stock_code | varchar2(45) | 股票代码 |
resu_predict | decimal(20, 2) | 预测值 |
resu_real | decimal(20, 2) | 真实值 |
在数据库内建好两张表,就可以对模型进行评估了,本篇代码用的是推进式建模(即每天获得最新的股票数据后添加到训练集中,重新建模并对第二天进行预测),部分代码如下:
# 计算查全率
sql_resu_recall_son = "select count(*) from model_ev_mid a where a.resu_real is not null and a.resu_predict = 1 and a.resu_real = 1"
cursor.execute(sql_resu_recall_son)
recall_son = cursor.fetchall()[0][0]
sql_resu_recall_mon = "select count(*) from model_ev_mid a where a.resu_real is not null and a.resu_real = 1"
cursor.execute(sql_resu_recall_mon)
recall_mon = cursor.fetchall()[0][0]
recall = recall_son / recall_mon
# 计算查准率
sql_resu_acc_son = "select count(*) from model_ev_mid a where a.resu_real is not null and a.resu_predict = 1 and a.resu_real = 1"
cursor.execute(sql_resu_acc_son)
acc_son = cursor.fetchall()[0][0]
sql_resu_acc_mon = "select count(*) from model_ev_mid a where a.resu_real is not null and a.resu_predict = 1"
cursor.execute(sql_resu_acc_mon)
acc_mon = cursor.fetchall()[0][0]
if acc_mon == 0:
acc = recall = acc_neg = f1 = 0
else:
acc = acc_son / acc_mon
基本的实现思路是:
- 1. 建回测时间序列。
- 2. 进行第一次时间序列的遍历,推进式建模,向中间表存入相关过程变量(包含每次的预测值)。
- 3. 进行第二次时间序列的遍历,向中间表中更新每次迭代的真实值。
- 4. 对中间表进行统计,计算 Precision,Recall,F1 分值等,并存入结果表。
(完整的模型评估代码详见 Model_Evaluate.py 文件)
仓位管理
在投资领域,交易择时和风险控制是同等重要的两大模块。前述机器学习的模型解决了交易择时的问题,而马科维茨投资组合理论,则是在投资组合确定的条件下,通过仓位配比来实现风险控制的强大工具。
我们取下面 5 只股票作为一套投资组合
股票代码 | 名称 | 行业 |
---|---|---|
603912 | 佳力图 | 通用设备/5G/次新股 |
300666 | 江丰电子 | 半导体/芯片/次新股 |
300618 | 寒锐钴业 | 有色金属/锂电池 |
002049 | 紫光国芯 | 半导体/5G/两融股 |
300672 | 国科微 | 半导体/芯片/次新股 |
现在我们计算一下 2018 年 1 月 1 日的 风险系数 和其对应的 头寸比例 ,采样长度 90 天。
计算的过程非常简单,步骤如下:
- 1. 计算投资组合在采样区间内的 每日收益率 ,组成一个 m*n 的矩阵 A(其中 m 为交易日天数,n 为投资组合股票或基金数,m 必须大于 n)。
- 2. 计算该矩阵 A 的 协方差矩阵 。
- 3. 求解该协方差矩阵的 特征值和特征向量 。
- 4. (可选) 计算 夏普率 。
在求解特征值和特征向量后,我们能得到若干(不大于 n)个特征值和其对应的特征向量。这些特征值就是马科维茨理论中的投资组合的 风险 ,其对应的特征向量做 归一化处理 后就是投资组合中各个股票或基金的 头寸比例 。
注意,特征向量中会有负值,一起归一化后是作为卖空账户的保证金头寸比例的,鉴于 A 股融券市场的高门槛和扭曲,不建议做空操作。号主是将特征向量中的负值剔除,将剩余的做归一化处理。
由于特征值和特征向量有多组,显然我们倾向于选风险小的,也即特征值最小的,这代表着市场的方向。本篇的这套 portfolio 运算后的风险和头寸如下:
市场的方向往往也是大盘的方向,投资组合的收益围绕大盘小范围波动。对于激进型投资者来说,需要一个风险稍稍提高但收益带来明显提升的方案。对于这种需求,则是取 次最小的特征值和特征向量 ,剔除负值并归一化,如下图:
完整的模型评估代码详见: https://www.wenjiangs.com/wp-content/uploads/2024/09/k2.rar
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论