前情提要: 《Random Forests C++实现:细节,使用与实验》
OpenCV的ml模块中有随机森林的C++实现(RTrees),也提供了Python接口。本文介绍如何使用OpenCV的随机森林进行分类和回归,并借助源码和官方文档解释算法参数设置某和函数用法。
0.实验环境
操作系统: Ubuntu 18.04
opencv: 4.1.2
python: 3.6.9
1.Python接口调用
以下代码为使用随机森林进行分类的示例
import cv2
import numpy as np
......
# 读取数据集,X:样本,Y:类别
# X, Y 为numpy类型
......
rf=cv2.ml.RTrees_create()
# 节点分裂时候选的特征数量
rf.setActiveVarCount(27)
# 如果达到节点的样本数小于5, 停止分裂
rf.setMinSampleCount(5)
# RTrees的最大深度的最大值为25,这里设置为40,而实际上用于训练的参数为25
rf.setMaxDepth(40)
# 设置终止条件
rf.setTermCriteria((1,200,0.0))
X=X.astype(np.float32)
# 对于分类器,确保类别为整型,否则算法会认为是回归问题
Y=Y.astype(np.int32)
traindata=cv2.ml.TrainData_create(X,cv2.ml.ROW_SAMPLE,Y)
rf.train(traindata)
2.函数和参数说明
OpenCV的随机森林实现在cv::ml::RTrees,在使用过程中发现几个不明白的地方——Random Forests参数(比如树的数量)怎么设置,到底运行的训练算法是分类还是回归——接口设置比较抽象,不像sklearn那么明了。可能是OpenCV要兼顾ml模块接口的通用性,所以对于RF参数设置就不那么直观了,对新手有点不友好。
2.1 关于树深度
需要说明的是opencv实现中,树的最大深度不会超过25。即使用户设置超过25,实际运行时也会被限制。不过对于一般规模的问题,这个值够用了。
2.2 树的数量
随机森林中一个重要的参数就是设置森林中随机树的数量,在RTree类里并没有找到设置树数量的方法。其实,这个值是在终止条件中,具体且看下面【终止条件】终止条件小节。
rf.setTermCriteria((1,200,0.0)) 随机树的数量是终止条件三元组的第二个元素
2.3 分类还是回归
OpenCV中分类和回归的训练都是通过函数train进行,那么使用者如何区分算法运行的是分类还是回归呢?首先以第一节的RF分类代码为例,来看看源码调用过程:
由样本集(X,Y)构造opencv的训练数据对象(traindata)时,调用类TrainDataImpl的方法create,create方法中再调用setData。
traindata=cv2.ml.TrainData_create(X,cv2.ml.ROW_SAMPLE,Y)
在setData方法里通过一系列校验将varType.at(ninputvars)设置为VAR_CATEGORICAL
注意下面图中的条件 “responses.type()<CV_32F”(红线处)。我们python脚本中有一行是Y=Y.astype(np.int32),Y就是图中的reponses,它的类型是32位整型,满足“<CV_32F”的条件。
然后在setData方法的结尾处,满足“varType.at(ninputvars)==VAR_CATEGORICAL”等条件,将 labels 赋值给 classLabels,所以 classLabels 就非空了,记住这点。
然后开始训练,在DTreesImpl类的startTraining方法中,根据将表达式 data->getResponseType() == VAR_CATEGORICAL 的结果赋值给 _isClassifier。
rf.train(traindata)
而 getResponseType()方法依据 classLabels 是否为空返回VAR_CATEGORICAL或者VAR_ORDERED。前面已经讲到过,setData方法结尾已经给 classLabels 赋值了,所以非空成立,getResponse函数返回VAR_CATEGORICAL, 即为 _isClassifier 赋值true。
综上,OpenCV的随机森林根据训练集输出(本文示例代码中Y,opencv源码中的responses)的类型(整型或者浮点型)来确定实际运行的是分类算法还是回归算法,Y为整型即为分类,Y为浮点型即为回归。所以在第一节分类算法的代码中刻意对输出进行了类型转换Y=Y.astype(np.int32)。
2.4 终止条件
Python脚本中关于终止条件的代码: rf.setTermCriteria((1,200,0.0)),这个三元组为 (type, maxCount, epsilon )。对于终止条件的说明:
第一个参数 type, 可以设置1(COUNT),2(EPS),3(COUNT+EPS)三个值之一,通过type值确定终止条件取maxCount或epsilon或两者同时。
第二个参数 maxCount,在RF算法里就是随机树的数量。
第二个参数 epsilon,表示精度。
比如type设置为1,那么系统终止条件取第二个参数(maxCount),即训练生成maxCount棵随机树后停止。
/*
COUNT: the maximum number of iterations or elements to compute
MAX_ITER: ditto
EPS: the desired accuracy or change in parameters at which the iterative algorithm stops
*/
enum Type {
COUNT = 1,
MAX_ITER = COUNT,
EPS = 2
}
setTermCriteria方法的三个参数:
附:完整示例代码
import cv2
import pandas as pd
import numpy as np
pd_data=pd.read_csv('/to/your/dir/spambase-pandas.data',header=None,sep=' ');
npdata=pd_data.values
X=npdata[:,1:-1]
Y=npdata[:,0]
rf=cv2.ml.RTrees_create()
rf.setActiveVarCount(7)
rf.setMinSampleCount(5)
rf.setMaxDepth(40)
rf.setTermCriteria((1,200,0.0))
X=X.astype(np.float32)
Y=Y.astype(np.int32)
traindata=cv2.ml.TrainData_create(X,cv2.ml.ROW_SAMPLE,Y)
rf.train(traindata)
(上面这个spambase数据集可以在这里下载)