当前位置: 首页 > 后端技术 > Python

随机森林算法性能对比测试:scikit-learn、SparkMLlib、DolphinDB、xgboost

时间:2023-03-26 17:14:35 Python

随机森林是一种常用的机器学习算法,既可以用于分类问题,也可以用于回归问题。本文对比测试了scikit-learn、SparkMLlib、DolphinDB、xgboost四个平台的随机森林算法实现。评估指标包括内存使用、运行速度和分类准确率。该测试使用模拟数据作为二分类训练的输入,并使用生成的模型对模拟数据进行预测。1.测试软件本次测试使用的平台版本如下:scikit-learn:Python3.7.1,scikit-learn0.20.2SparkMLlib:Spark2.0.2,Hadoop2.7.2DolphinDB:0.82xgboost:Pythonpackage,0.812。环境配置CPU:Intel(R)Xeon(R)CPUE5-2650v42.20GHz(共24核48线程)RAM:512G??B操作系统:CentOSLinuxrelease7.5.1804在各平台测试时,数据为加载到内存中进行计算,因此随机森林算法的性能与磁盘无关。3.数据生成本次测试使用DolphinDB脚本生成模拟数据,并导出为CSV文件。训练集平均分为两类,每一类的特征列服从两个不同的中心,相同的标准差,两个独立的多元正态分布N(0,1)和N(2/sqrt(20),1).训练集中没有空值。假设训练集的大小是n行和p列。本次测试中,n取值为10000、100000、1000000,p取值为50。由于测试集和训练集独立同分布,所以测试集的大小对模型影响不大准确性评估。本次测试使用1000行模拟数据作为所有不同规模训练集的测试集。生成模拟数据的DolphinDB脚本见附录1。4.模型参数各平台随机森林模型训练使用以下参数:树数:500最大深度:划分节点时选择的特征在最大深度分别为10和30的4个平台进行测试数量:正方形特征总数的根,即integer(sqrt(50))=7划分节点时的不纯度指标:基尼指数(Giniindex),该参数只对Pythonscikit-learn、SparkMLlib和DolphinDB有效桶数sampled:32,该参数只对SparkMLlib和DolphinDB有效并发任务数:CPU线程数,Pythonscikit-learn为48,SparkMLlib和DolphinDB为24,xgboost为24。在测试xgboost时,尝试了参数nthread(代表运行时并发线程数)的不同取值。但是当这个参数的值为本次测试环境的线程数(48)时,表现并不理想。进一步观察到,当线程数小于10时,性能与该值呈正相关。当线程数大于10小于24时,不同值的性能差异不明显。之后,随着线程数量的增加,性能会下降。这种现象在xgboost社区也有讨论。因此本次测试最终在xgboost中使用的线程数为24。5.测试结果测试脚本见附录2~5。当树数为500,最大深度为10时,测试结果如下表所示:当树数为500,最大深度为30时,测试结果如下表:来自从准确率来看,Pythonscikit-learn、SparkMLlib和DolphinDB的准确率差不多,略高于xgboost的实现;性能方面,从高到低分别是DolphinDB、Pythonscikit-learn、xgboost、SparkMLlib。在此测试中,Pythonscikit-learn实现使用了所有CPU内核。SparkMLlib的实现并没有完全使用所有的CPU核,内存占用是最高的。当数据量为10000条时,峰值CPU使用率约为8%。当数据量为10万条时,CPU峰值使用率约为25%。当数据量为1,000,000时,它会因为内存不足而中断执行。DolphinDB数据库的实现使用了所有的CPU核,是所有实现中最快的,但是内存占用是scikit-learn的2-7倍,xgboost的3-9倍。DolphinDB的随机森林算法实现提供了numJobs参数,可以调整该参数以降低并行度,减少内存占用。详情请参考DolphinDB用户手册。xgboost常用于boosted树的训练,也可以进行随机森林算法。算法迭代次数为1时的特例。实际上xgboost在24线程左右性能最高,CPU线程利用率不如Python和DolphinDB,速度快两者都不如。它的优点是内存占用最少。另外,xgboost的具体实现也与其他平台不同。例如,在没有引导过程的情况下,使用无放回抽样而不是对数据进行放回抽样。这可以解释为什么它的准确性略低于其他平台。6.总结Pythonscikit-learn的随机森林算法在性能、内存开销和准确率方面比较均衡,而SparkMLlib的实现在性能和内存开销方面远不如其他平台。DolphinDB的随机森林算法达到最佳性能,并且DolphinDB的随机森林算法和数据库无缝集成,用户可以直接对数据库中的数据进行训练和预测,并提供numJobs参数,实现内存和速度的平衡。xgboost的随机森林只是迭代次数为1时的特例,具体实现与其他平台有很大区别。最好的应用场景是boostedtree。附录1。模拟生成数据的DolphinDB脚本defgenNormVec(cls,a,stdev,n){returnnorm(cls*a,stdev,n)}defgenNormData(dataSize,colSize,clsNum,scale,stdev){t=table(dataSize:0,`clsjoin("col"+string(0..(colSize-1))),INTjointake(DOUBLE,colSize))classStat=groupby(count,1..dataSize,rand(clsNum,dataSize))for(classStat中的行){cls=row.groupingKeyclassSize=row.countcols=[take(cls,classSize)]for(iin0:colSize)cols.append!(genNormVec(cls,scale,stdev,classSize))tmp=table(dataSize:0,`clsjoin("col"+string(0..(colSize-1))),INTjointake(DOUBLE,colSize))insertintotvalues(cols)cols=NULLtmp=NULL}returnt}colSize=50clsNum=2t1m=genNormData(10000,colSize,clsNum,2/sqrt(20),1.0)saveText(t1m,"t10k.csv")t10m=genNormData(100000,colSize,clsNum,2/sqrt(20),1.0)saveText(t10m,"t100k.csv")t100m=genNormData(1000000,colSize,clsNum,2/sqrt(20),1.0)saveText(t100m,"t1m.csv")t1000=genNormData(1000,colSize,clsNum,2/sqrt(20),1.0)saveText(t1000,"t1000.csv")复制代码2.Pythonscikit-learn的训练和预测脚本importpandasaspdimportnumpyasnpfromsklearn.ensembleimportRandomForestClassifier,RandomForestRegressorfromtimeimport*test_df=pd.read_csv("t1000.csv")defevaluate(path,model_name,num_trees=500,depth=30,num_jobs=1):df=pd.read_csv(path)y=df.values[:,0]x=df.values[:,1:]test_y=test_df.values[:,0]test_x=test_df.values[:,1:]rf=RandomForestClassifier(n_estimators=num_trees,max_depth=depth,n_jobs=num_jobs)start=time()rf.fit(x,y)end=time()elapsed=end-startprint("训练模型%s的时间:%.9f秒"%(model_name,elapsed))acc=np.mean(test_y==rf.predict(test_x))print("模型%s精度:%.3f"%(model_name,acc))evaluate("t10k.csv","10k",500,10,48)#选择你自己的参数3.火花男Llib的训练和预测代码(Scala实现)importorg.apache.spark.mllib.tree.configuration.FeatureType.Continuousimportorg.apache.spark.mllib.tree.model.{DecisionTreeModel,Node}objectRf{defmain(args:Array[String])={evaluate("/t100k.csv",500,10)//选择你自己的参数}defprocessCsv(row:Row)={vallabel=row.getString(0).toDoublevalfeatureArray=(for(i<-1to(row.size-1))yieldrow.getString(i).toDouble).toArrayvalfeatures=Vectors.dense(featureArray)LabeledPoint(label,features)}defevaluate(path:String,numTrees:Int,maxDepth:Int)={valspark=SparkSession.builder.appName("Rf").getOrCreate()导入spark.implicits._valnumClasses=2valcategoricalFeaturesInfo=Map[Int,Int]()valfeatureSubsetStrategy="sqrt"valimpurity="gini"valmaxBins=32vald_test=spark.read.format("CSV").option("header","true").load("/t1000.csv").map(processCsv).rddd_test.cache()println("加载表(1M*50)")vald_train=spark.read.format("CSV").option("header","true").load(path).map(processCsv).rddd_train.cache()println("训练表(1M*50)")valnow=System.nanoTimevalmodel=RandomForest.trainClassifier(d_train,numClasses,categoricalFeaturesInfo,numTrees,featureSubsetStrategy,impurity,maxDepth,maxBins)println((System.nanoTime-现在)/1e9)valscoreAndLabels=d_test.map{point=>valscore=model.trees.map(tree=>softPredict2(tree,point.features)).sumif(score*2>model.numTrees)(1.0,point.label)else(0.0,point.label)}valmetrics=newMulticlassMetrics(scoreAndLabels)println(metrics.accuracy)}defsoftPredict(node:Node,features:Vector):Double={如果(node.isLeaf){//if(node.predict.predict==1.0)node.predict.probelse1.0-node.predict.probnode.predict.predict}else{if(node.split.get.featureType==Continuous){if(features(node.split.get.feature)<=node.split.get.threshold){softPredict(node.leftNode.get,features)}else{softPredict(node.rightNode.get,features)}}else{if(node.split.get.categories.contains(features(node.split.get.feature))){softPredict(node.leftNode.get,features)}else{softPredict(node.rightNode.get,features)}}}}defsoftPredict2(dt:DecisionTreeModel,features:Vector):Double={softPredict(dt.topNode,features)}}4.DolphinDB的训练和预测脚本defcreateInMemorySEQTable(t,seqSize){db=database("",SEQ,seqSize)dataSize=t.size()ts=()for(iin0:seqSize){ts.append!(t[(i*(dataSize/seqSize)):((i+1)*(dataSize/seqSize))])}returndb.createPartitionedTable(ts,`tb)}defaccuracy(v1,v2){返回(v1==v2).sum()v2.size()}defevaluateUnparitioned(filePath,numTrees,maxDepth,numJobs){test=loadText("t1000.csv")t=loadText(文件路径);clsNum=2;colSize=50计时器res=randomForestClassifier(sqlDS(),`cls,`col+string(0..(colSize-1)),clsNum,sqrt(colSize).int(),numTrees,32,maxDepth,0.0,numJobs)print("Unpartitionedtableaccuracy="+accuracy(res.predict(test),test.cls).string())}evaluateUnpartitioned("t10k.csv",500,10,48)//选择你自己的参数5.xgboost的训练和预测脚本importpandasaspdimportnumpyasnpimportxgboostasxgbfromtimeimport*defload_csv(path):df=pd.read_csv(path)target=df['cls']df=df.drop(['cls'],axis=1)返回xgb.DMatrix(df.values,label=target.values)dtest=load_csv('/hdd/hdd1/twonormData/t1000.csv')defevaluate(path,num_trees,max_depth,num_jobs):dtrain=load_csv(path)param={'num_parallel_tree':num_trees,'max_depth':max_depth,'objective':'binary:logistic','nthread':num_jobs,'colsample_bylevel':1/np.sqrt(50)}start=time()model=xgb.train(param,dtrain,1)end=time()elapsed=end-startprint("时间到训练模型:%.9f秒"%elapsed)prediction=model.predict(dtest)>0.5print("Accuracy=%.3f"%np.mean(prediction==dtest.get_label()))evaluate('t10k.csv',500,10,24)//选择你自己的参数