下文我们将以支持向量机模型的完整 Python 代码来说明如何构建机器学习多因子选股模 型。每一个子模块包含“代码实例”和“代码分析”,分析部分对代码进行逐句讲解。部 分模块还包含“其余变式”,介绍其它机器学习模型的代码写法。 模块导入模块导入通常位于整段代码的开头,将需要用到的包、库、模块等导入程序。 代码实例 代码分析 – import packages#符号之后为注释,本程序用注释这种方法将程序分为各模块,以及用于提示某部分代码 功能。 import numpy as np
import pandas as pd
import 为 Python 中导入包的命令,NumPy 包简写为 np,提供众多科学计算功能;pandas 包简写为 pd,提供 DataFrame 数据类型,便于海量数据的分析。以后当我们需要调用两 个包中的函数时,只需使用 np + 函数名或 pd + 函数名即可。 参数设置本模块将所有可能用到的参数都在程序开头用类(class)的形式加以定义,类名为 Para。 预定义 Para 类使得在我们在调整参数时可以直接在 Para 类中改变值,而无需在后面的程 序中逐个单独改动。 代码实例 代码分析 class Para:
class 的作用为创建一个类,可以给该类下各变量赋值。Python 是通过缩进的方法来表示 定义类这一操作何时结束,在代码中可以看到 svm_c 为类定义操作中的最后一行。 method = ‘SVM’
不同的机器学习模型对应的 Python 命令不尽相同。Method 参数用来确定程序使用的机器 学习模型,‘SVM’对应支持向量机模型。也可以设置为’LR’(线性模型)或 ‘SGD’(随机梯 度下降模型)。 month_in_sample = range(82, 153+1)
该参数表示样本内数据对应的月份。注意函数 range(x, y)将生成一组由 x(含)至 y(不 含)的连续整数数列。如果希望包含 y,则需要写成 range(x, y+1)。在我们的多因子选股 框架中,82 号月对应 2005 年 1 月底截面期,153 号月对应 2010 年 12 月底截面期,样 本内数据共包含 72 个月。 month_test = range(154, 230 + 1)
该参数表示样本外数据对应的月份。在我们的多因子选股框架中,154 号月对应 2011 年 1 月底截面期,230 号月对应 2017 年 5 月底截面期,样本外数据共包含 76 个月。 percent_select = [0.3, 0.3]
在训练 SVM 等分类器模型时,可能需要选取有代表性的数据,而非全部数据。该参数表 示选取排名靠前多少比例与排名靠后多少比例的数据。本程序选用每个月超额收益排名前 30%的股票作为正例,后 30%的股票作为反例。 percent_cv = 0.1
该参数表示交叉验证集占样本内数据的比例。本程序选用样本内数据的 10%作为交互验证 集,剩余 90%作为训练集。 path_data = ‘.\csv_demo\’
该参数表示数据文件所在的路径。 path_results = ‘.\results_demo\’
该参数表示执行程序后生成结果文件所在的路径。 seed = 42
该参数表示随机数种子。Python 的随机数生成方式为伪随机,随机数的值并非真正随机, 而是取决于随机数种子。换言之,当随机数种子固定不变时,每次生成的随机数也将固定 不变。为了保证程序运行的结果可重复,我们需要提前设定随机数种子。 svm_kernel = ‘linear’
该参数表示支持向量机模型的核函数类型,linear 表示线性核。也可以设为 poly(多项式 核,默认 3 次多项式), sigmoid(Sigmoid 核)或 rbf(高斯核) 。 svm_c = 0.01
该参数表示线性支持向量机的惩罚系数 C。 para = Para()
为 Para 类创建一个实例 para。Para 类是一个抽象的概念,para 是一个具体的对象,两 者的关系相当于人类和某个人。 数据标记本模块定义一个函数:输入全部样本,选择超额收益最高和最低的部分样本,分别标记为 1 和 0,再将未被标记的样本剔除,返回标记完成的样本。 代码实例
image.jpg[size=1em]620x527 74.1 KB
代码分析 def label_data(data):
def 的作用为定义函数,以缩进表示函数体的开始和结束位置。定义函数后,我们可以在 后续程序里直接调用该函数,使得代码简洁高效。这里我们定义一个名为 label_data 的函 数,输入变量为包含全部股票样本的 DataFrame,在函数体内的局部变量名为 data。 data[‘return_bin’] = np.nan
向输入的 DataFrame 变量 data 的最后一列添加名为 return_bin 的列,初始化赋予缺失值 (nan,即 not a number 的缩写)。return_bin 列随后将记录每个样本的标签。 data = data.sort_values(by=‘return’, ascending=False)
使用 sort_values 方法,按照超额收益 return 对 DataFrame 进行降序排列,并将排列后的 data 替代原有 data。参数 by 决定依据哪一列为标准进行排序;参数 ascending 决定是否 按照升序排列,设置为 False 则代表降序排列。注意,无论按照升序还是降序排列,nan 始终排在最后,并且每行的索引始终不变。 n_stock_select = np.multiply(para.percent_select, data.shape[0])
调用参数类 para 中的 percent_select 值,即选取前后多少比例的样本参与训练。使用 data.shape 方法得到 data 的维数,data 相当于二维矩阵,[0]表示选取第一个值即行数, 代表 data 中的股票数量。使用 numpy 中的 multiply 函数将两者相乘,即选取前后样本比 例乘以股票总数,最终得到选取前后部分样本的股票个数,并将结果赋予 n_stock_select。 n_stock_select = np.around(n_stock_select).astype(int)
注意到 n_stock_select 是由小数和整数相乘获得,结果可能带有小数,然而股票个数必须 为整数,因此需要进行取整操作。numpy 中的函数 around 默认将输入变量转换成整数。 取整后虽然数值为整数,但是在存储格式上仍为浮点数,因此还需要使用 astype(int)强制转换成整数格式。最后将处理后的整数替代原结果。 data.iloc[0:n_stock_select[0], -1] = 1
使用 iloc 进行矩阵的切分和赋值操作。注意到[]中,逗号前为所取的行,逗号后为所取的 列。n_sock_select 为含有两个整数值的列表,[0]即第一个值为选取表现最好的股票数量。 所以对于排序后的 data,我们取 0 到 n_stock_select[0]行,再用-1 取它们的最后一列, 即我们之前创建的用于记录标签的新列,赋值为 1 作为表现最好的股票的标签。 data.iloc[-n_stock_select[1]:, -1] = 0
类似的,用 iloc 取 data 中-n_stock_select[1]:所在行的样本,注意到负号代表倒着取,而: 后面不加任何变量则代表取到底。之后同样取它们的最后一列(即’return_bin’列)并赋值 为 0,作为表现最差的股票的标签。 data = data.dropna(axis=0)
使用 dropna 函数剔除含有缺失值 nan 的样本。参数 axis=0 表示如果某个元素为缺失值, 则删除该元素所在的整行,axis=1 则删除该元素所在的整列。将剔除缺失值后的 data 替 换原有 data。 return data
通常位于函数体的结尾,返回完成标记的 data。执行该函数后将得到这个新 DataFrame 作为输出。
|