赞
踩
在第2章中,我们和小瓦一起用简单的回测来对交易策略进行了 评估,并且学习了两种基于市场动量的经典交易策略。现在小瓦提出 一个新的问题:如果我们掌握了足够多的数据,不就可以用机器学习 技术来预测股价的涨跌和涨幅了吗?事实上,这是一个非常好的主 意。机器学习技术就是使用样本数据训练模型,并且让模型对新样本 做出预测的技术。本章我们就和小瓦一起来探索一下机器学习在交易 中的基本应用。本章的主要内容如下。
近年来,随着人工智能(Artificial Intelligence,AI)的蓬勃发展, 机器学习在各行各业都有着非常广泛的应用。不过,作为一个非计算机 专业出身的学生,机器学习对于小瓦来说是一个完全陌生的领域。然 而,小瓦坚信世上无难事,只怕有心人。更何况要理解机器学习的基本 概念,其实并不困难。当然,对机器学习的基本概念已经有一定了解的 读者,可以跳过这一部分,直接阅读后面的内容
3.1.1 有监督学习和无监督学习
在机器学习领域,有监督学习(supervised learning)和无监督学习 (unsupervised learning)是常见的两种方法。为了帮助小瓦理解这两种 方法的不同,我们可以用一个小例子来阐述。
例如,我们给小瓦一堆化妆品,相信小瓦与其他女生一样,可以很轻松地叫出每样化妆品 的名字。这是因为在小瓦的认知当中,每种物品已经有了一个标签 (label),如口红、眉笔、粉扑。这些标签对应着不同的特征 (feature),例如,“红色”“用来涂在嘴唇上”的,对应的就是“口红”这 个标签。符合这个特征的化妆品,就会被小瓦归入“口红”这个类 别。对于模型来说,这种有已知标签的任务就是有监督学习的一种。
我们再给小瓦一些不同的物品:计算机主板。
这时我们再来要求小瓦说出计算机主板上零件的名 字,就有些难为她了。毕竟小瓦没有接触过这个领域的知识,也就是 说,在小瓦的脑子里,没有这些零件所对应的标签。即便如此,小瓦还 是可以通过观察这些东西的特征,将它们归到不同的类别中,例如,有 些是“黑色凹槽”,有些是“白色凹槽”,有些是“黑色圆柱”——虽然不知 道它们具体是什么东西,但还是能够看出它们的作用肯定是不同的。这 种没有已知标签,但是让模型通过观察特征将它们放入不同类别的过 程,就是无监督学习的一种。
3.1.2 分类和回归
在有监督学习当中,常见的两种任务就是分类(classification)和 回归(regression)。其中,分类任务指的是,给定样本的分类标签,训 练模型使其可以将新的样本归入正确的分类中——这时模型的目标是离 散的;而回归任务指给定样本的目标值,训练模型使其可以预测出新样 本对应的数值——这时模型的目标是连续的。
用小瓦可以更容易理解的语言来说,假如要预测某只股票在未来 会“涨”还是会“跌”,这时模型所做的就是分类的工作,但如果要预测某 只股票未来会涨1元,还是8角8分,还是–5角,这时模型所做的就是回 归的工作。
3.1.3 模型性能的评估
如果使用算法来进行交易的话,小瓦最关心的就是模型是否可以准 确地预测出股票的涨跌或者涨幅。实际上,模型是不可能做到百分之百 准确的,这就需要我们对模型的性能进行评估,以便找到最可用的模 型。要达到这个目的,我们就需要将掌握的数据集(dataset)拆分为训 练集(trainset)和验证集(testset),使用训练集训练模型,并使用验 证集来评估模型是否可用。
举一个例子,假如小瓦有某只股票100天的价格数据,就可以将前 80天的数据作为训练集,将后20天的数据作为验证集,同时评估模型分 别在训练集与验证集中的准确率。如果模型在训练集中的得分很高,而 在验证集中的得分很低,就说明模型出现了过拟合(over-fitting)的问 题;而如果模型在训练集和验证集中的得分都很低,就说明模型出现了 欠拟合(under-fitting)的问题。
要解决这些问题,小瓦就需要调整模型的参数、补充数据,或者进 行更细致的特征工程。随着小瓦工作的继续深入,我们会一起来探索详 细的解决方案。
对于小瓦这种初学者来说,有一个特别好的消息,即在Python中实 现各种不同的机器学习算法是非常容易的,有各种各样的第三方库可以 直接调用,如久负盛名的scikit-learn、深度学习框架TensorFlow、 Pytorch及很容易上手的Keras等。
在本节中,我们就帮助小瓦来熟悉一下scikit-learn的基本使用方法。
3.2.1 KNN算法的基本原理
在诸多机器学习算法中,KNN(K-Nearest Neighbor,K最近邻)算 得上是较简单且易于理解的算法之一了。不过,简单不意味着KNN能做 的事情比较少,它既可以用于分类任务,也可以用于回归任务。 KNN算法的原理十分容易理解:它识别k个最近的数据点(基于欧 几里得距离)来进行预测,它分别预测邻域中最频繁的分类或是回归情 况下的平均结果。用通俗的话讲,已知大部分皮肤比较白的人是欧洲 人,大部分皮肤比较黑的人是非洲人。现在给你介绍一位朋友,让你判 断他来自哪个国家。通过目测,你发现这位朋友与3位非洲朋友的肤色 比较接近,与1位欧洲朋友的肤色比较接近,这时,你大概率会把这位 新朋友归到非洲人的分类当中。
对于回归任务来说,KNN的工作机理也是相似的。例如,你打算去 买一套房子,在同一个小区当中发现有3套户型和面积都十分接近的房 屋。第一套的售价是500万元,第二套的售价是520万元,而第三套的售 价未知。鉴于第三套的情况与前面两套十分接近,你可以大致估算出它 的售价会是510万元左右,即前两套房子售价的均值。
下面我们来逐一演示一下KNN在分类和回归当中的应用。
3.2.2 KNN算法用于分类
1). 载入数据集并查看
scikit-learn内置了一些供大家学习的玩具数据集(toy dataset),其 中有些是分类任务的数据,有些是回归任务的数据。首先我们使用一个 最简单的数据集来给小瓦演示KNN算法在分类中的应用。输入代码如 下:
- #首先导入鸢尾花数据载入工具
- from sklearn.datasets import load_iris
- #导入KNN分类模型
- from sklearn.neighbors import KNeighborsClassifier
- #为了方便可视化,我们再导入matplotlib和seaborn
- import matplotlib.pyplot as plt
- import seaborn as sns
运行代码,如果程序没有报错,就说明所有的库都已 经成功载入。接下来我们就用数据集载入工具加载数据。输入代码如 下:
- #加载鸢尾花数据集,赋值给iris变量
- iris = load_iris()
- #查看数据集的键名
- iris.keys()
【结果分析】如果读者朋友们也得到了同样的结果,就说明代码运 行成功。我们看到,该数据集存储了若干个键(key),这里我们重点 关注一下其中的target和feature_names,因为这两个键对应的分别是样本 的分类标签和特征名称。 首先我们看下数据集存储了样本的哪些特征,输入代码如下:
- #查看数据集的特征名称
- iris.feature_names
【结果分析】从上述代码结果中可以看出,数据集中的样本共有4 个特征,分别是sepal length(萼片长度)、sepal width(萼片宽度)、 petal length(花瓣长度)和petal width(花瓣宽度)。
下面再来看一下这些样本被分为几类,输入代码如下:
- #查看数据集中的样本分类
- iris.target
【结果分析】观察代码的运行结果,我们可以发现系统返回了一个 数组,数组中的数字有0、1和2。这说明数据集中的样本分为3类,分别 用0、1、2这3个数字来表示。 到这里,相信小瓦也已经明白,这个数据集的目的是:根据样本鸢 尾花萼片和花瓣的长度及宽度,结合分类标签来训练模型,以便让模型 可以预测出某一种鸢尾花属于哪个分类。
2). 拆分数据集
下面,我们就来把数据集拆分为训练集和验证集,以便验证模型的 准确率。先输入如下代码:
- #将样本的特征和标签分别赋值给X和y
- X, y = iris.data, iris.target
- #查看是否成功
- X.shape
【结果分析】从上面的代码运行结果可以看出,我们将数据集的特 征赋值给了X,而将分类标签赋值给了y。通过查看X的形态,可知样本 数量共有150个,每个样本有4个特征。
下面来对数据集进行拆分,输入代码如下:
- #导入数据集拆分工具
- from sklearn.model_selection import train_test_split
- #将X和y拆分为训练集和验证集
- X_train, X_test, y_train, y_test =\
- train_test_split(X, y)
- #查看拆分情况
- X_train.shape
结果分析】从上面的代码运行结果可以看到,通过拆分,训练集 中的样本数量为112个,其余的38个样本则进入了验证集。
3). 训练模型并评估准确率
下面训练一个最简单的KNN模型,输入代码如下:
- #创建KNN分类器,参数保持默认设置
- knn_clf = KNeighborsClassifier()
- #使用训练集拟合模型
- knn_clf.fit(X_train, y_train)
- #查看模型在训练集和验证集中的准确率
- print('训练集准确率:%.2f'%knn_clf.score(X_train, y_train))
- print('验证集准确率:%.2f'%knn_clf.score(X_test, y_test))
【结果分析】从上面的代码运行结果可以看到,使用KNN算法训 练的分类模型,在训练集中的准确率达到了100%,在验证集中的准确率 达到了0.95%。这是一个非常不错的成绩。 需要说明的是,在scikit-learn中,KNN可以通过调节n_neighbors参 数来改进模型的性能。在不手动指定的情况下,KNN默认的近邻参数 n_neighbors为5。那么这个参数是最优的吗?我们可以使用网格搜索法 来寻找到模型的最优参数。输入代码如下:
- # 导入网格搜索
- from sklearn.model_selection import GridSearchCV
- # 定义一个从1到10的n_neighbors
- n_neighbors = tuple(range(1,11,1))
- # 创建网格搜索实例,estimator用KNN分类器
- # 把刚刚定义的n_neighbors传入给param_grid参数
- # 参数指交叉验证次数为5
- cv = GridSearchCV(estimator=KNeighborsClassifier(),
- param_grid = {'n_neighbors':n_neighbors},
- cv=5)
- # 使用网格搜索拟合数据集
- cv.fit(X,y)
- # 查看最优参数
- cv.best_params_
【结果分析】从上面的代码运行结果可以看到,程序将网格搜索找 到的最优参数进行了返回——KNN分类器的最优n_neighbors参数是6。 也就是说,当n_neighbors参数为6时,模型的准确率是最高的。 下面我们就来看一下当把n_neighbors设置为6时,模型的准确率。
输入代码如下:
- #创建KNN分类器,n_neighbors设置为6
- knn_clf = KNeighborsClassifier(n_neighbors=6)
- #使用训练集拟合模型
- knn_clf.fit(X_train, y_train)
- #查看模型在训练集和验证集中的准确率
- print('训练集准确率:%.2f'%knn_clf.score(X_train, y_train))
- print('验证集准确率:%.2f'%knn_clf.score(X_test, y_test))
【结果分析】从上面的代码运行结果可以看到,当把n_neighbors参 数设置为6时,模型在训练集中的准确率100%,可以说这是非 常不错的成绩了;而在验证集中的准确率依旧保持在92%左右,没有提升。
3.2.3 KNN算法用于回归
下面我们就来给小瓦展示KNN算法在回归任务中的应用。
1). 载入数据集并查看
这里依旧使用scikit-learn内置的数据集来给小瓦进行讲解。说到回 归任务,我们自然会想到波士顿房价数据集。该数据集中有506个样 本,每个样本有13个特征,以及对应的价格(target)。下面我们载入 数据集并对其进行初步的了解。
- #载入波士顿房价数据集导入工具
- from sklearn.datasets import load_boston
- #将数据导入
- boston = load_boston()
- #查看数据集的键名
- boston.keys()
【结果分析】从代码运行结果可以看出,数据集中存储了5个键, 这里我们重点关注target(房屋的售价)及feature_names(房屋的特 征)。也就是说,我们需要训练模型,让它学习房屋特征和售价的关 系,并且可以自动预测出新房屋的售价。 下面来看一下数据中存储的特征都有哪些,输入代码如下:
- #查看样本的特征名称
- boston.feature_names
【结果分析】从代码运行结果可以看到,程序返回了样本全部的特 征名称,包括房间数量RM、房龄AGE等共计13个。因为这里只是进行 回归分析的演示,所以我们不展开讲解这些特征具体代表什么。感兴趣 的读者可以自行查看scikit-learn官方文件进行深入了解。 如果读者朋友希望继续了解房屋的价格是什么样子,可以使用下面 这行代码来查看一下:
- #选取前十套房屋,查看售价
- boston.target[:10]
接下来重复进行类似分类任务的步骤,将数据集拆分为训练集和验 证集。
2). 拆分数据集并训练模型
与分类任务一样,在回归任务中,我们也要使用训练集来训练模 型,并使用验证集来验证模型的性能。
下面来进行数据集的拆分,输入代码如下。
- #将样本特征和售价赋值给X,y
- X, y = boston.data, boston.target
- #使用train_test_split拆分为训练集和验证集
- X_train, X_test, y_train, y_test =\
- train_test_split(X, y)
- #查看拆分的结果
- X_train.shape
【结果分析】如果读者朋友也得到了这个结果,就说明你的数据集 拆分成功。训练集中有379个样本,其余127个样本进入了验证集。 下面开始模型的训练,输入代码如下:
- #导入KNN回归算法
- from sklearn.neighbors import KNeighborsRegressor
- #创建一个实例,参数保持默认设置
- knn_reg = KNeighborsRegressor()
- #拟合训练集数据
- knn_reg.fit(X_train, y_train)
- #查看模型在训练集和验证集的性能表现
- print('训练集准确率:%.2f'%knn_reg.score(X_train, y_train))
- print('验证集准确率:%.2f'%knn_reg.score(X_test, y_test))
【结果分析】从上面的代码运行结果可以看到,缺省参数的KNN 回归模型在该数据集中的性能表现差强人意,在训练集中的准确率只有 67%,而在验证集中则更加糟糕,只有58%。这说明模型出现了欠拟合 的问题,我们需要对数据集进行处理,或者对模型进行调优。
到这里,相信读者朋友们和小瓦一样,发现了这样一个事情:不论 是在分类模型中,还是回归模型中,我们都使用了.score( )方法来评 估模型的性能。然而,在两种模型中,.score( )方法所进行的计算是 不一样的。在分类模型中,.score( )返回的是模型预测的准确率 (accuracy),其计算公式为:
在上面这个公式中,TP(True Positive)表示模型预测正确的正样 本数量;TN(True Negative)表示模型预测正确的负样本数量; FP(False Positive)表示原本是负样本,却被模型预测为正样本的数 量,也就是我们平时说的“假阳性”;FN(False Negative)表示原本是正 样本,却被模型预测为负样本的数量,也就是“假阴性”。TP、FP、 TN、FN的和就是所有的样本数量。也就是说,分类模型的准确率是模 型预测正确的样本数量,除以全部参与预测的样本数量。当然,除了准 确率之外,我们还可以用Precision、Recall、F1 score等方法来对分类模 型进行性能评估,这里暂时不展开讲解。
在回归任务中,.score( )方法返回的是模型的R2。对于小瓦来 说,这个概念有些陌生。R2是描述模型预测数值与真实值差距的指标, 它的计算公式为
在这个公式中, y^代表模型对样本的估计值,y可代表的是样本真 实值的均值。也就是说,R^2样本真实值减模型估计值,再进行平方并 求和,除以样本真实值减样本平均值的平方和,最后用1减去这个结 果。因此R^2值为0~1,并且越接近1,说明模型的性能越好。 除了R^2外,回归模型还可以用均方误差(Mean Squared Error, MSE)、绝对中位差(Median Absolute Error,MAE)等指标来进行评 估。如果有需要,我们也会在后面做进一步的讲解。 前面说了,缺省参数的KNN模型在波士顿房价预测这个任务中的表 现并不理想。下面我们尝试对KNN回归的参数进行调整,看是否可以改 进模型的性能。与分类模型一样,我们先使用网格搜索来寻找模型的最 优参数。输入代码如下:
- # 这次让n_neighbors参数从1到20遍历
- n_neighbors = tuple(range(1,21,1))
- # 创建KNN回归的网格搜索实例
- cv_reg = GridSearchCV(estimator = KNeighborsRegressor(),
- param_grid = {'n_neighbors':n_neighbors},
- cv = 5)
- # 用网格搜索拟合数据集
- cv_reg.fit(X, y)
- cv_reg.best_params_
【结果分析】从上面的代码运行结果可以看到,KNN回归模型的 最佳n_neighbors参数是10,也就是说,当n_neighbors取10时,模型的R2 最高。 现在来看一下当n_neighbors取10时,模型的R^2是多少。输入代码如 下:
- #查看最佳参数对应的最佳模型R^2
- cv.best_score_
【结果分析】从代码运行结果可以看到,当我们设置KNN回归模 型的n_neighbors参数为10时,模型的R^2提高到了0.98,可以说在性能方 面有了显著的提升。 注意:在使用网格搜索时,我们没有手动将数据集拆分为训练集 和验证集。这是因为网格搜索内置了交叉验证(cross validation)法。 在网格搜索中,我们设置cv参数为5,也就是说,交叉验证会将数据分 成5份,第一份作为验证集,其余作为训练集,而后再把第二份作为验 证集,其余部分作为训练集……以此类推,直到全部验证完毕,因此 省去了拆分数据集的步骤。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。