【原创】AI-QI量化平台搭建爬坑笔记-小心偏斜类问题

原创文章,转载请注明出处

首先安利一波广告:

最近利用业余时间搭建了一个智能量化投资平台,代码已经全部开源:https://github.com/DannyLee1991/ai_qi

目前已经实现的功能有:

  • 数据的抓取(数据来源tushare})

  • 数据入库,并在界面上可以执行sql操作

  • 数据可视化


  • 数据集创建

数据集管理

数据集创建

数据集查看

  • 数据预处理

// 暂无界面

正在开发中的功能:

  • 数据建模
  • 接入交易接口

各个已有的功能目前也只是做了部分实现,先把各个环节打通,然后在慢慢填充。最终的目标是利用机器学习算法来预测分析各种投资数据。

欢迎各位大牛拍砖指导~


好,以上不是本文的重点,本文重点是这两天在建立第一个模型过程中遇到的一个坑。


数据集介绍

第一个模型,我准备使用日交易数据来预测次日的涨跌幅度,使用的数据来自日交易数据表:

这是对应的原始数据集详情信息:

其中 除了X的‘date’(时间)特征之外,其他的特征都是float类型的数据。暂时先剔除这一维度的数据,所以最后用来训练的X的shape是(292583, 15)

Y的shape不变,是(292583, 1)

Y的数据类型也是float。

这里先解释一下Y的含义:Y对应的数据是涨跌幅,但和与之对应的X的数据并不是同一天的,这里日期间隔1的含义是Y取的是相对于X的数据的时间的下一日的数据,因为我们要预测的是次日涨跌幅。

对于分类模型,我们的Y应该是类别标签,而不能是连续型数值,所以我们应该把当前的float类型的Y,转换成某种类别标签来表示。

一种很容易理解的方式,就是float转int,简单粗暴:

eg:

1
Y = [1.1, 2.6, 3.5, -1.8, ...]

转换为int之后

1
Y = [1, 2, 3, -1, ...]

但这里有一个小坑,负数的标签在带入到tensorflow中训练是会报错的,所以把标签数据+10,全部转为大于等于0的数据,处理之后的效果为:

1
Y = [11, 12, 13, 9, ...]

在经过上述的预处理之后,Y被处理成了21个类别(-10到10之间一共21个int值)的数据集。

然后就是将数据打乱,按照6:2:2的比例将数据分割为训练集验证集测试集


带入训练

仿照tensorflow的demo,将数据带入一个DNN模型,进行训练。

没有进行什么特殊的调参,最终准确率竟然达到了接近50%!

要知道这是21个类别的分类问题,50%的准确率已经远远高于均匀分布情况下随机选择的准确率(大约是4.8%)了。

于是我觉得21类数据,准确率就能达到这么高,那如果我将类别改为2类,准确率应该会大幅提升的。

于是我重新将Y的数据改为了0和1两种类别,0代表跌1代表涨。

但实际情况,并没有好很多,准确率大概达到52%左右。

看到这种结果,第一时间,我是怀疑自己代码有没有哪里写错,但经过排查,并没有发现有什么异常。

后来,又尝试将数据分为4类,8类来训练,得到的结果依然是50%左右。

WTF?

原来是偏斜类在搞鬼

这里有篇关于偏斜类的文章,值得一看。

我将原始数据绘制成柱状图之后,发现了问题所在:

数据在各个类别上并不是均匀分布的,大部分都聚集在了10(对应涨跌幅为0%)的位置上。而我训练出来的模型,带入一批测试数据后,预测结果也都是10。

也就是说,我们的分类器,就算完全没有识别能力,输入任何值,输出的结果都是10这种类别,那么这个分类器就有50%的准确率!!!

这是一个很傻的结果,就好比一个完全不懂股票的人,你问他某只股票明天会不会涨,他只要回答:“明天不涨不跌”,那么他就有50%的概率猜对了。

好吧,这并不是我们想要的效果,那么我们如何避免这种情况呢?

想办法让各个类别的数据呈现均匀分布

我想到的办法是在标签数据生成的过程中做些手脚。使得各个类别的数据呈现出均匀分布的情况。

所以我写了一个函数,用来将数据处理成均匀分布的标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import numpy as np
import math
def uniform_distribution(Y, n, l_type='b'):
'''
平均分布
将Y按照值的大小进行均匀分布的方式分割成n等分
并生成对应的类别标签
:param Y: 数据源 形状为(num,)
:param n: 将数据分割为n等分
:param l_type: 分割数据之后 标签的取值类别{'b','s','n'}
'b' : 类别标签为区间范围内的最大值
's' : 类别标签为区间范围内的最小值
'n' : 类别标签为区间范围内的平均值
:return:
eg:
Y = [1,1,2,3,4,4,5,6,7]
n = 5
l_type = 'b'
result = [1 1 3 3 4 4 6 6 7]
n = 3
l_type = 's'
result = [1 1 1 2 2 2 4 4 4]
n = 5
l_type = 'n'
result = [ 1. 1. 2. 2. 3.5 3.5 5. 5. 6.5]
'''
node_list = gen_nodelist(Y, n)
toY = []
for num in Y:
# 这里虽然得到的是float类型的值,但tensorflow的类别标签 只能是int类型,所以将对应的索引作为标签值来使用
index, val = get_label(num, node_list, l_type)
toY.append(index)
print("node list >>")
print(node_list)
return np.array(toY)
def get_label(num, node_list, l_type):
# 下限
lm = 0
# 上限
um = 0
target = 0
# 节点区间是(lm,um]
for index, node in enumerate(node_list):
if node >= num:
if index > 0:
um = node
lm = node_list[index - 1]
target = index
break
label = ''
if l_type == 's':
label = lm
elif l_type == 'b':
label = um
elif l_type == 'n':
label = (lm + um) / 2
return target, label
def gen_nodelist(data, n):
'''
生成节点列表 节点是指示数据的分割点
:param data: 原始数据 需要被分割的数据
:param n: 分割的份数
:return:
'''
sorted_list = sorted(data)
size = len(sorted_list)
node_list = []
block_size = math.ceil(size / n)
for index, item in enumerate(sorted_list):
if (index + 1) % block_size == 0:
node_list.append(item)
elif index == 0:
node_list.append(item)
elif index == size - 1:
node_list.append(item)
return node_list

用这个函数重新预处理我们的Y,这里我们将Y分为10种类别,Y按照以下的标签值,重新赋值:

1
[-10.08, -2.3900000000000001, -1.4199999999999999, -0.84999999999999998, -0.40999999999999998, 0.0, 0.28999999999999998, 0.68999999999999995, 1.24, 2.25, 10.16]

可以看到,这些标签值并不是线性的,这是因为我们的数据不是均匀分布的。

按照这种标签处理之后,将数据绘制成柱状图后,如下:

接下来带入训练就比较正常了。

坚持原创技术分享,您的支持将鼓励我继续创作!