聊聊基于Alink库的决策树模型算法实现

发布时间 2023-09-28 22:29:16作者: 又见阿郎

示例代码及相关内容来源于《Alink权威指南(Java版)》

概述

决策树模型再现了人们做决策的过程,该过程由一系列的判断构成,后面的判断基于前面的判断结果,不断缩小范围,最终推出结果。
如下,基于决策树模型预测天气,是最常见的示例。天气的整个预测过程,就是不断地判断推测的过程。

特征分类

特征数据有多种划分,比如连续特征、离散特征、时序特征。决策树对于离散特征与连续特征的处理是有些差异的。

连续特征

以用户属性数据为例,比如身高、年龄、体重等特征数据,可以看作是连续特征数据,因为其分布一般是连续且不均匀的数值数据。

离散数据

以用户属性数据为例,比如性别数据,只有两种取值,是分布均匀,分组清晰的文本数据。

决策树模型处理这两种数据的区别其实也就是需要考虑数据是否分组聚集,以及是否划分数据,如何划分数据。导致其数学形式的表述有差异。

决策树分类与算法

常见决策树算法

目前常用的决策树模型有三种,分别是

  • ID3
  • C4.5
  • CART

虽然这三种算法的区别以及实现初衷不尽相同,但其实可以都是基于三种不同类型的决策树模型来实现的。而这三种类型分别是:

  • GINI:基尼值,CART算法模型
  • INFOGAIN:信息增益,ID3算法模型
  • INFOGAINRATIO:信息增益率,C4.5算法模型

如上所示,不同的决策树算法对应着不同类型的决策树模型。

决策树分类

决策树分为两大类:回归树与分类树,其分别对应解决机器学习领域中的分类和回归问题。分类树预测分类标签值,回归树用于预测数值。
分类树在每次分枝时,会针对当前枝节点的样本,找出最优的分裂特征和分裂方式,从而得到若干新节点;继续分枝,直到所有样本都被分入类别唯一的叶节点。
回归树的建模也差不多,但分裂指标不同,回归树一般以均方差为分裂指标。

Alink实现决策树算法模型

实现概览

在Alink库中,已经封装好了决策树算法的相关细节,使得业务开发及其简单——简单的API调用即可。
实现Demo如下:

/**
 * 基于C45决策树算法进行模型训练,并输出模型
 * 1. 设置C45决策树模型参数
 * 1.1 设置特征列
 * 1.2 设置离散特征列
 * 1.3 设置标签列
 * 1.4 打印输出模型
 * */
static void c_2_5() throws Exception {
    MemSourceBatchOp source = new MemSourceBatchOp(
        new Row[] {
            Row.of("sunny", 85.0, 85.0, false, "no"),
            Row.of("sunny", 80.0, 90.0, true, "no"),
            Row.of("overcast", 83.0, 78.0, false, "yes"),
            Row.of("rainy", 70.0, 96.0, false, "yes"),
            Row.of("rainy", 68.0, 80.0, false, "yes"),
            Row.of("rainy", 65.0, 70.0, true, "no"),
            Row.of("overcast", 64.0, 65.0, true, "yes"),
            Row.of("sunny", 72.0, 95.0, false, "no"),
            Row.of("sunny", 69.0, 70.0, false, "yes"),
            Row.of("rainy", 75.0, 80.0, false, "yes"),
            Row.of("sunny", 75.0, 70.0, true, "yes"),
            Row.of("overcast", 72.0, 90.0, true, "yes"),
            Row.of("overcast", 81.0, 75.0, false, "yes"),
            Row.of("rainy", 71.0, 80.0, true, "no")
        },
        new String[] {"Outlook", "Temperature", "Humidity", "Windy", "Play"}
    );

    source.lazyPrint(-1);

    source
        .link(
            new C45TrainBatchOp()
                .setFeatureCols("Outlook", "Temperature", "Humidity", "Windy") // 设置特征列
                .setCategoricalCols("Outlook", "Windy")	// 设置离散特征
                .setLabelCol("Play")	// 设置标签列
                .lazyPrintModelInfo()
                .lazyCollectModelInfo(new Consumer <DecisionTreeModelInfo>() {
                    @Override
                    public void accept(DecisionTreeModelInfo decisionTreeModelInfo) {
                        try {
                            decisionTreeModelInfo.saveTreeAsImage(
                                DATA_DIR + "weather_tree_model.png", true);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                })
        );

    BatchOperator.execute();
}

最终生成一个决策树图
weather_tree_model.png

算子函数

C45TrainBatchOp

API说明
简单看看其构造函数


@NameCn("C45决策树分类训练")
@NameEn("C45 Decision Tree Training")
@EstimatorTrainerAnnotation(
    estimatorName = "com.alibaba.alink.pipeline.classification.C45"
)
public class C45TrainBatchOp extends BaseRandomForestTrainBatchOp<C45TrainBatchOp> implements C45TrainParams<C45TrainBatchOp>, WithModelInfoBatchOp<DecisionTreeModelInfo, C45TrainBatchOp, C45ModelInfoBatchOp> {
    private static final long serialVersionUID = -1894634246411633664L;

    public C45TrainBatchOp() {
        this((Params)null);
    }

    /**
    * 主要是这里,构造了决策树模型的类别为INFOGAINRATIO
    */
    public C45TrainBatchOp(Params parameter) {
        super(parameter);
        this.getParams().set(TreeUtil.TREE_TYPE, TreeType.INFOGAINRATIO);
        this.getParams().set(HasNumTreesDefaltAs10.NUM_TREES, 1);
        this.getParams().set(HasFeatureSubsamplingRatio.FEATURE_SUBSAMPLING_RATIO, 1.0D);
        this.getParams().set(HasSubsamplingRatio.SUBSAMPLING_RATIO, 1.0D);
    }

    public C45ModelInfoBatchOp getModelInfoBatchOp() {
        return (C45ModelInfoBatchOp)(new C45ModelInfoBatchOp(this.getParams())).linkFrom(new BatchOperator[]{this});
    }
}