阅读路线:
- 项目背景
- 问题分析
- 数据介绍
- 模型使用
一、项目背景
近来有同学问道,有没有数据挖掘的案例可以来练习下,主要是来通过案例来知道算法是如何使用的。
下面就以港股打新这个金融项目为例,来做个预测,先来说下什么是打新;打新,就是用资金参与新股申购,如果中签的话,就买到了即将上市的股票。
此次分析的目的是为了深入打新数据,找到最优算法,挖掘出影响打新的关键因素,找到可能要破发的新股,从而减少新股破发的风险,提高盈利。
二、问题分析
打新的本质,也即是在股票上市后卖出,赚取其中的差价。一般在买到的第一天就会卖掉,当然第一天上升的股票有涨有跌,为了能够减少风险,会根据历史数据来做个预判,这里把涨幅10%以下记为0,涨幅10%以上记为1,很明显这也是二分类的预测问题
对于本项目而言,最终的评价标准是要求在精确度达到97%的情况下,最大化召回率。这里是要求尽可能提高召回率,自己本身对风险比较厌恶,宁可错杀,也不会愿意申购上市就要的破发的新股
对于评价标准,一般用的是PR曲线和ROC曲线。ROC曲线有一个突出优势,就是不受样本不均衡的影响ROC曲线不受样本不均衡问题的影响
三、数据介绍
1.数据总体情况
港股数据主要来自两个方面,利弗莫尔证券数据和阿思达克保荐人近两年数据,处理之后是这样的:
数据一共有17个特征,除了目标变量is_profit,还有16个特征。
目标变量is_profit存在一定程度的不均衡,但是程度不大,因此可以用PR曲线做模型性能评估以上的数据指标可以梳理为两类,一类是股票相,如 关,一类是保荐人指标,
2.数据处理方面不用管
一般特征工程主要从以下方面来进行:衍生特征、异常值处理、缺失值处理、连续特征离散化、分类变量one-hot-encode、标准化等,本篇文章主要讲解随机森林算法使用,暂不对特征工程做过多的展示了
四、模型使用
1.导入相应的模型包
import xgboost as xgb
from xgboost import plot_importance
from sklearn.preprocessing import OneHotEncoder
from sklearn import linear_model
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import accuracy_score
from xgboost.sklearn import XGBClassifier
from sklearn import metrics
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
2.划分数据集
# 将数据和标签进行分裂
x=pd_merge_all.loc[:,pd_merge_all.columns!='is_profit']
y=pd_merge_all.loc[:,pd_merge_all.columns=='is_profit']
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.3)
#注意这样的索引就会乱了,最好将数据进行重新索引,方便后面操作
#这个操作就完成了将索引从0开始
for i in [x_train,x_test,y_train,y_test]:
i.index=range(i.shape[0])
3.随机森林模型使用
3.1使用随机森林默认的参数
rf0 = RandomForestClassifier(oob_score=True, random_state=10)
rf0.fit(x_train,y_train)
test_prob = rf0.predict_proba(x_test)[:,1] #标签为零的概率
#fpr假正率(False Positive Rate,FPR)
#tpr真正率(True Positive Rate,TPR)
fpr,tpr,thresholds = metrics.roc_curve(y_test,test_prob)
#print(fpr,tpr,thresholds)
# 0.5 < AUC < 1,优于随机分类器
auc = metrics.auc(fpr,tpr)
#分类准确率分数是指所有分类正确的百分比。
score = metrics.accuracy_score(y_test,rf0.predict(x_test))
print(auc)
输出结果:0.7590545177277925
从使用随机森林默认的参数带来的模型结果来看,auc指标是0.76,效果还可以。
为了更好的理解上述,这里有几个知识点需要来解释下:
- predict_proba:
返回的是一个n行k列的数组,第i行第j列上的数值是模型预测第i个预测样本的标签为j的概率。所以每一行的和应该等于1;本文中predict_proba(x_test)[:,1]返回的是标签为0的概率。
- fpr、tpr、auc理解
(a).混淆矩阵
混淆矩阵如下图分别用”0“和”1“代表负样本和正样本。FP代表实际类标签为”0“,但预测类标签为”1“的样本数量。其余,类似推理。
image.png(b).假正率和真正率
假正率(False Positive Rate,FPR)是实际标签为”0“的样本中,被预测错误的比例。真正率(True Positive Rate,TPR)是实际标签为”1“的样本中,被预测正确的比例。其公式如下:
(3).ROC曲线
下图的黑色线即为ROC曲线,ROC曲线是一系列threshold下的(FPR,TPR)数值点的连线。此时的threshold的取值分别为测试数据集中各样本的预测概率。但,取各个概率的顺序是从大到小的。然后也就是有了不同的RPR、TPR,且测试样本中的数据点越多,曲线越平滑:
AUC(Area Under roc Cure),顾名思义,其就是ROC曲线下的面积,在此例子中AUC=0.62。AUC越大,说明分类效果越好。
5.随机森林模型需要调节的参数
5.1.RF框架
下面我们来看看RF重要的Bagging框架的参数,主要有以下几个:
(1) n_estimators:
也就是最大的弱学习器的个数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,计算量会太大,并且n_estimators到一定的数量后,再增大n_estimators获得的模型提升会很小,所以一般选择一个适中的数值。默认是100。
(2) oob_score:
即是否采用袋外样本来评估模型的好坏。默认识False。个人推荐设置为True,因为袋外分数反应了一个模型拟合后的泛化能力。
(3) criterion:
即CART树做划分时对特征的评价标准。分类模型和回归模型的损失函数是不一样的。分类RF对应的CART分类树默认是基尼系数gini,另一个可选择的标准是信息增益。回归RF对应的CART回归树默认是均方差mse,另一个可以选择的标准是绝对值差mae。一般来说选择默认的标准就已经很好的。
从上面可以看出,RF重要的框架参数比较少,主要需要关注的是n_estimators,即RF最大的决策树个数。
5.2.RF决策树参数
下面我们再来看RF的决策树参数,它要调参的参数如下:
(1) RF划分时考虑的最大特征数max_features:
可以使用很多种类型的值,默认是"auto",意味着划分时最多考虑 个特征;如果是"log2"意味着划分时最多考虑 个特征;如果是"sqrt"或者"auto"意味着划分时最多考虑 个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,一般我们用默认的"auto"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。(2) 决策树最大深度max_depth:
默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。
(3) 内部节点再划分所需最小样本数min_samples_split:
这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
(4) 叶子节点最少样本数min_samples_leaf:
这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
(5)叶子节点最小的样本权重和min_weight_fraction_leaf:
这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
(6) 最大叶子节点数max_leaf_nodes:
通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。
(7) 节点划分最小不纯度min_impurity_split:
这个值限制了决策树的增长,如果某节点的不纯度(基于基尼系数,均方差)小于这个阈值,则该节点不再生成子节点。即为叶子节点 。一般不推荐改动默认值1e-7。
上面决策树参数中最重要的包括最大特征数max_features, 最大深度max_depth, 内部节点再划分所需最小样本数min_samples_split和叶子节点最少样本数min_samples_leaf
6.Grid SearchCV(网格搜索)进行模型调参
GridSearchCV的名字其实可以拆分为两部分,GridSearch和CV,即网格搜索和交叉验证。这两个名字都非常好理解。网格搜索,搜索的是参数,即在指定的参数范围内,按步长依次调整参数,利用调整的参数训练学习器,从所有的参数中找到在验证集上精度最高的参数,这其实是一个训练和比较的过程。
GridSearchCV可以保证在指定的参数范围内找到精度最高的参数,但是这也是网格搜索的缺陷所在,他要求遍历所有可能参数的组合,在面对大数据集和多参数的情况下,非常耗时。
6.1选定需要调节的参数
通过RF框架以及RF决策树参数能够了解到重点需要调节以下的参数
- RF重要的框架参数
主要需要关注的是n_estimators,即RF最大的决策树个数。
- 决策树参数
决策树参数中最重要的包括最大特征数max_features, 最大深度max_depth, 内部节点再划分所需最小样本数min_samples_split和叶子节点最少样本数min_samples_leaf
6.2对n_estimators进行网格搜索
param_test1 = {'n_estimators':range(10,71,10)}
# n_estimators的范围是10到71,步长是10
gsearch1 = GridSearchCV(estimator = RandomForestClassifier(min_samples_split=100,
min_samples_leaf=20,max_depth=8,max_features='sqrt' ,random_state=10),
param_grid = param_test1, scoring='roc_auc',cv=5)
gsearch1.fit(x_train,y_train)
print(gsearch1.best_params_, gsearch1.best_score_)
输出结果为:
{'n_estimators': 20} 0.7322150550757045
6.3最佳的弱学习器迭代次数,接着我们对决策树最大深度max_depth和内部节点再划分所需最小样本数min_samples_split进行网格搜索
param_test2 = {'max_depth':range(3,14,2), 'min_samples_split':range(5,50,5)}
gsearch2 = GridSearchCV(estimator = RandomForestClassifier(n_estimators= 20,
min_samples_leaf=20,max_features='sqrt' ,oob_score=True, random_state=10),
param_grid = param_test2, scoring='roc_auc',iid=False, cv=5)
gsearch2.fit(x_train,y_train)
print(gsearch2.best_params_, gsearch2.best_score_)
输出结果
{'max_depth': 3, 'min_samples_split': 45} 0.7297801529886148
6.4最大特征数max_features做调参
param_test4 = {'max_features':range(3,11,2)}
gsearch4 = GridSearchCV(estimator = RandomForestClassifier(n_estimators= 20, max_depth=3, min_samples_split=45,
min_samples_leaf=20 ,oob_score=True, random_state=10),
param_grid = param_test4, scoring='roc_auc',iid=False, cv=5)
gsearch4.fit(x_train,y_train)
print(gsearch4.best_params_, gsearch4.best_score_)
输出结果:
{'max_features': 7} 0.7519586841793169
6.5根据模型最佳参数进行测试
rf1 = RandomForestClassifier(n_estimators= 20, max_depth=3, min_samples_split=45,
min_samples_leaf=20,max_features=7 ,oob_score=True, random_state=10)
rf1.fit(x_train,y_train)
test_prob = rf1.predict_proba(x_test)[:,1] #标签为零的概率
#fpr假正率(False Positive Rate,FPR)
#tpr真正率(True Positive Rate,TPR)
fpr,tpr,thresholds = metrics.roc_curve(y_test,test_prob)
#print(fpr,tpr,thresholds)
# 0.5 < AUC < 1,优于随机分类器
auc = metrics.auc(fpr,tpr)
#分类准确率分数是指所有分类正确的百分比。
score = metrics.accuracy_score(y_test,rf1.predict(x_test))
print(auc)
输出结果:0.7805947388486466,相比没有调参前,模型有不少的提高的,方便观察,用图形来看下ROC曲线图
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('bmh')
plt.figure(figsize=(10,8))
plt.plot(fpr,tpr,label='RandomForestClassifier')
plt.legend(loc='lower right',prop={'size':25})
plt.xlabel('fpr')
plt.ylabel('tpr')
plt.title('ROC')
plt.show()
6.6观察模型的重要特征
import pandas as pd
# Extract feature importances
fi = pd.DataFrame({'feature': list(pd_merge_all.columns)[:-1],
'importance': rf1.feature_importances_}).\
sort_values('importance', ascending = False)
# Display
fi
6.7最大召回率
precision, recall, thresholds = precision_recall_curve(y_test, test_prob)
pr = pd.DataFrame({"precision": precision, "recall": recall})
prc = pr[pr.precision >= 0.97].recall.max()
print(prc)
最后得出我们想要的结果,精准率在0.97下,召回率0.046
参考文章:
网友评论