Skip to content

CLI 和 C++ 用户手册

本页面深入介绍了 Yggdrasil Decision Forests (YDF) 的 CLI API。本页内容通常不是使用 YDF 所必需的,但它将帮助用户加深理解和使用高级选项。

新用户应首先查看 快速开始

这里介绍的大多数概念也适用于其他 API,特别是 C++ API。

数据集

数据集示例 的列表。数据集可以存储在内存中、磁盘上,或者即时生成。数据集中的示例是 有序的,即数据集中的一个示例可以通过其在数据集中的 索引 明确标识。

属性(在其他库中也称为 特征变量)指的是表格数据集中某一特定信息。例如,一个人的 年龄姓名出生国家 都是属性。

每个属性都有一个 语义(也称为 类型)。语义决定了模型如何使用该属性。例如,年龄(例如,出生后的年数)通常是 数值型,而 国家(例如,ISO 3166 国家代码)是分类型的。

YDF 支持以下语义:

  • NUMERICAL:数值。通常用于具有完整顺序的量或计数。例如,一个人的年龄,或袋子中的物品数量。可以是浮点数或整数。缺失值用 NaN 表示。

  • CATEGORICAL:分类值。通常用于具有有限可能值集的类型/类别,且无顺序。例如,集合 {RED, BLUE, GREEN} 中的颜色 RED。可以是字符串或整数。在内部,分类值存储为 int32,因此应小于 ~2B。

  • CATEGORICAL_SET:分类值的集合。非常适合表示分词后的文本。与 CATEGORICAL 不同,CATEGORICAL_SET 中的项目数量可以变化,且每个项目的顺序/索引无关紧要。

  • BOOLEAN:布尔值。类似于 CATEGORICAL,但只有两个可能值。

  • DISCRETIZED_NUMERICAL:自动离散化为箱的数值。离散化数值特征比(非离散化)数值特征训练速度更快。如果这些特征的唯一值数量少于箱数,从模型的角度来看,离散化是无损的。如果这些特征的唯一值数量大于箱数,从模型的角度来看,离散化是有损的。有损离散化可以减少模型的质量,有时也可能增加(由于正则化)模型的质量。

  • HASH:字符串值的哈希。当仅值之间的相等性重要(而不是值本身)时使用。目前,仅用于排名问题中的组,例如查询/文档问题中的查询。哈希计算使用 farmhash

示例属性 值的集合。以下是一个包含三个属性的 示例

"attribute_1": 5.1 # NUMERICAL
"attribute_2": "CAT" # CATEGORICAL 存储为字符串
"attribute_3": [1, 2, 3] # CATEGORICAL-SET 存储为整数

数据规范(或简称 dataspec)是属性列表的定义。此定义包含数据集中每个属性的名称、语义和元数据。dataspec 存储为 ProtoBuffer(参见其定义在 dataset/data_spec.proto)。可以使用 show_dataspec CLI 命令以可读方式打印 dataspec。

Google ProtoBuffers 在 YDF 中广泛用于配置。与 XML 或 JSON 类似,Protobuf 是一种用于数据交换的语言无关序列化格式。Protobuf 可以是文本或二进制格式。YDF 配置始终是包含文本格式 Protobuf V2 的文件。

Protobuf 总是附带一个列出允许字段名称和类型的 Protobuf 定义。在本文档中,几个示例包含 Protobuf 文本和相应 Protobuf 定义的链接。

以下是 Protobuf 及其相应 Protobuf 定义的示例:

Protobuf

field_a: 10
field_b: 9.0

Protobuf 定义

message MyMessage{
  optional int32 field_a = 1 [default = 5];
  optional float field_b = 2 [default = 8.0];
}

在定义中,每个字段都分配有一个唯一的整数 ID,格式为 <字段名称> = <字段 ID> [默认= <默认值>]

这是 部署配置(稍后将解释的分布式训练配置)的 Protobuf 定义。

dataspec 示例:

记录数: 1
列数: 3

按类型划分的列数:
    NUMERICAL: 1 (33%)
    CATEGORICAL: 1 (33%)
    CATEGORICAL_SET: 1 (33%)

列:

NUMERICAL: 6 (40%)
    0: "feature_1" NUMERICAL 均值:38.5816

CATEGORICAL: 1 (33%)
    1: "feature_2" CATEGORICAL 有字典 词汇量:2 零-ood-项 最频繁:"CAT" 1 (50%)

CATEGORICAL_SET: 1 (33%)
    2: "feature_3" CATEGORICAL_SET 整数化

术语:
    nas: 不可用(即缺失)值的数量。
    ood: 字典外。
    manually-defined: 由用户手动定义的属性类型,即类型不是自动推断的。
    tokenized: 属性值通过分词获得。
    has-dict: 属性附加到字符串字典,例如存储为字符串的分类属性。
    vocab-size: 唯一值的数量。
当以原始文件形式存储时,dataspec可以直接读取。以下是上述dataspec的原始定义。

columns {
    name: "attribute_1"
    type: NUMERICAL
    numerical {
        mean: 10
    }
}
columns {
    name: "attribute_2"
    type: CATEGORICAL
    categorical {
        items {
          key: "CAT"
          value { index: 1 }
        }
        items {
            key: "COT"
            value { index: 2 }
        }
    }
}
columns {
    name: "attribute_3"
    type: CATEGORICAL_SET
    categorical_set {
        is_already_integerized: true
    }
}

dataspec已经通过dataspec推理工具(即infer_dataspec)自动创建。如果dataspec的某些部分推断不正确,用户可以使用dataspec指南(参见dataset/data_spec.proto)覆盖它。

例如,默认情况下,看起来像整数的值会被检测为数值型。然而,表示ENUM值的整数值更适合作为分类型。以下是一个dataspec指南的示例,它强制所有名称以"enum_"前缀开头的属性被检测为分类型。

column_guides {
    column_name_pattern: "^foo.*" # 正则表达式
    type: CATEGORICAL # 强制此语义。
}

以下两个示例展示了使用CLI和C++ API进行dataspec推断:

CLI

infer_dataspec --dataset=csv:/my/dataset.csv --output=dataspec.pbtxt

# 人类可读的dataspec描述。
show_dataspec --dataspec=dataspec.pbtxt

C++

using namespace yggdrasil_decision_forests;
dataset::proto::DataSpecification data_spec;
dataset::CreateDataSpec("csv:/my/dataset.csv", false, guide, &data_spec);

# 人类可读的dataspec描述。
std::cout << dataset::PrintHumanReadable(data_spec);

数据集路径和格式

数据集的格式由数据集文件路径中的前缀和冒号(":")指定。例如,csv:/path/to/my/dataset.csv是一个指向csv文件的数据集路径。

支持的格式有:

  • csv: : CSV文件。
  • tfrecord+tfe: : 包含序列化TensorFlow Example protobuffers的GZIP压缩TensorFlow Record文件

单个数据集可以分为多个文件。拆分数据集可以加快数据集读取速度。根据经验,单个数据集文件不应超过100MB。可以使用分片、全局匹配和逗号分隔来指定数据集的文件列表。以下是包含多个文件的数据集路径示例:

  • csv:/path/to/my/dataset.csv@10
  • csv:/path/to/my/dataset_*.csv
  • csv:/path/to/my/dataset_1.csv,/path/to/my/dataset_1.csv

此外,逗号分隔可以与分片/全局匹配结合使用。例如:csv:/1/data.csv*,/2/data.csv*

学习器和模型

YDF使用学习器模型的概念进行操作。学习器是一个函数(在数学意义上),它接受一个数据集并输出一个模型。模型是一个函数,它接受一个示例并输出一个预测。

模型和学习器抽象

在模型-学习器抽象中,学习算法和一组超参数是学习器,而训练好的模型是模型

模型可以存储在内存或磁盘上。可以使用show_model命令显示有关模型的信息(例如输入特征、标签、验证分数、学习器)。

模型还包含元数据。元数据不用于计算预测。相反,用户或工具可以查询此元数据进行分析(例如特征重要性、训练损失)。可以使用edit_model工具删除模型元数据。

模型可以编译(服务)引擎引擎是一个没有元数据且内部结构优化用于快速推理的模型。虽然模型是平台无关的,但引擎与特定硬件绑定(例如需要GPU或AVX512指令)。引擎推理的速度比标准模型推理快几个数量级。

C++方法model.BuildFastEngine()将模型编译为当前硬件上最有效的引擎。cli/benchmark_inference工具可以测量模型和兼容服务引擎的推理速度。

学习器TrainingConfig proto实例化。TrainingConfig定义了标签、输入特征、任务、学习器类型和学习超参数。

通用超参数(GHPs)是TrainingConfig protobuffer的替代表示。GHP是一个字符串到值的映射,适用于快速配置和自动超参数优化。GHPs由TensorFlow Decision Forests(TF-DF)使用:

超参数页面列出了学习器及其超参数。 可选地,可以为学习者配置一个部署规范部署规范定义了训练期间使用的计算资源,包括线程数或工作节点数(在分布式训练的情况下)。更改部署配置(或不提供任何配置)可能会影响训练速度,但不会影响最终模型:使用相同学习者但不同部署规范训练的两个模型保证是相同的。

默认情况下,学习者配置为使用6个线程进行本地训练。

学习者的日志目录是一个可选目录,用于指定学习者导出学习过程信息的位置。这些信息是学习者特定的,可能包括图表、表格和HTML报告,有助于理解模型。学习者的日志目录没有定义的结构,因为不同的学习者可以导出不同类型的信息。

以下两个示例展示了使用CLI和C++ API训练模型的过程(参见上文如何在dataspec.pbtxt文件中创建dataspec):

CLI

cat <<EOF > hparams.pbtxt
task: CLASSIFICATION
label: "income"
learner: "GRADIENT_BOOSTED_TREES"
EOF

train \
  --dataset=csv:/my/dataset.csv \
  --dataspec=dataspec.pbtxt \
  --config=hparams.pbtxt \
  --output=/my/model

# 模型的人类可读描述。
show_model --model=dataspec.pbtxt

C++

using namespace yggdrasil_decision_forests;

dataset::proto::DataSpecification data_spec = ... // 之前的示例。

// 配置训练。
model::proto::TrainingConfig config;
config.set_task(model::proto::CLASSIFICATION);
config.set_label("income");
config.set_learner("GRADIENT_BOOSTED_TREES");

// 实例化学习者。
std::unique_ptr<model::AbstractLearner> learner;
CHECK_OK(model::GetLearner(config, &learner));

// 训练
std::unique_ptr<model::AbstractModel> model = learner->Train("csv:/my/dataset.csv", data_spec);

// 将模型导出到磁盘。
CHECK_OK(model::SaveModel("/my/model", model.get()));

// 模型的人类可读描述。
std::string description;
model->AppendDescriptionAndStatistics(false, &description);
std::cout << description;

为了最小化二进制文件大小并支持自定义学习算法和模型,模型和学习者依赖于注册机制:用户可以通过依赖注入限制或扩展支持的模型和学习者列表。

所有官方支持的模型和学习者可以通过依赖项yggdrasil_decision_forests/model:all_modelsyggdrasil_decision_forests/learners:all_learners分别注入。

元学习者

学习者可以组合。包含另一个学习者的学习者称为元学习者。这包括超参数优化学习者、校准学习者、集成学习者和特征选择学习者。

模型/学习者评估

模型评估可以通过evaluate CLI命令或model::AbstractModel::Evaluate C++函数实现。

以下两个示例展示了使用CLI和C++ API评估模型的过程:

CLI

evaluate --dataset=csv:/my/dataset.csv --model=/my/model

C++

using namespace yggdrasil_decision_forests;

std::unique_ptr<model::AbstractModel> model = ... // 之前的示例。

// 加载数据集。
dataset::VerticalDataset dataset;
CHECK_OK(LoadVerticalDataset("csv:/my/dataset.csv", model->data_spec(), &dataset));

// 评估模型。
utils::RandomEngine rnd;
metric::proto::EvaluationResults evaluation = model->Evaluate(dataset, {}, &rnd);

// 以人类可读格式导出评估结果。
std::string text_report;
metric::AppendTextReport(evaluation, &text_report);
std::cout << text_report;

学习者评估包括评估学习者训练的一个或多个模型(例如交叉验证;C++(参见model::EvaluateLearner))。当数据集较小时(例如少于50k个样本,或学习者不稳定时),学习者评估比模型评估更准确。

模型分析

show_model CLI命令显示有关模型的以下信息:

  • 模型的输入特征和标签。
  • 每个特征的变量重要性。
  • 模型的详细或汇总结构(例如节点数量)。
  • 兼容的服务引擎。
  • 训练日志。

或者,可以直接使用TensorFlow Decision Forests Python检查器以编程方式可视化和分析模型。有关更多详细信息,请参阅检查和调试笔记本。可以使用Yggdrasil模型转换为TensorFlow Decision Forests模型。 tfdf.keras.core.yggdrasil_model_to_keras_model 函数。调用 model.summary() 显示的信息与 show_model CLI 相同。

变量重要性

变量重要性通过 CLI(show_model)、C++ API(model.GetVariableImportance())和 Python API(tfdf.inspector.make_inspector(path).variable_importances())获取。

可用的变量重要性包括:

模型无关

  • MEAN_{INCREASE,DECREASE}_IN_{metric}:通过排列重要性估计的移除特征后的指标变化。根据学习算法和超参数,变量重要性可以通过验证、交叉验证或袋外数据计算。例如,特征的 MEAN_DECREASE_IN_ACCURACY 是打乱特征值导致的准确率下降(越大,特征越重要)。例如,MEAN_DECREASE_IN_AUC_3_VS_OTHERS 是当将标签类别“3”与其他类别比较时,AUC 的预期下降。

决策森林特定

  • SUM_SCORE:使用特定特征的分裂分数总和。越大,特征越重要。

  • NUM_AS_ROOT:使用特定特征的根节点数量。越大,特征越重要。

  • NUM_NODES:使用特定特征的节点数量。越大,特征越重要。

  • INV_MEAN_MIN_DEPTH:特征在所有树路径中首次出现的最小深度的平均值的倒数(即 1/(1+x))。注意: MEAN_MIN_DEPTH 已被移除。

自动超参数调优

优化学习器的超参数可以提高模型的质量。选择最佳超参数可以通过手动方式(参见如何改进模型)或使用自动超参数优化器(HPO)来完成。HPO 通过一系列试错计算自动选择最佳超参数。

查看示例目录中的超参数调优示例。

HPO 是一个带有 HYPERPARAMETER_OPTIMIZER 键配置的元学习器,用于优化子学习器。它支持本地和分布式训练。

HPO 的各个方面可以进行配置。主要的配置选项包括:

  • 搜索空间:要优化的超参数集合。
  • 评估协议:如何评估一组超参数,例如自评估、交叉验证。
  • 优化器:用于在搜索空间中导航的优化算法。

以下示例展示了梯度提升树模型的超参数优化器的 TrainingConfig 配置。

任务: 分类
学习器: "超参数优化器"
标签: "收入"

[yggdrasil_decision_forests.model.hyperparameters_optimizer_v2.proto
      .hyperparameters_optimizer_config] {
  最终模型重训练: true  # 调优后重新训练模型。
  优化器 {
    # 新超参数值是随机生成的。
    优化器键: "随机"
    [yggdrasil_decision_forests.model.hyperparameters_optimizer_v2.proto
          .random] {
            试验次数: 25  # 25次随机试验。
            }
  }
  # 要优化的基础学习器。
  基础学习器 {
    # 注意:无需配置基础学习器的标签和输入特征。
    # 这些值会自动从HPO配置中复制。
    学习器: "梯度提升树"

    [yggdrasil_decision_forests.model.gradient_boosted_trees.proto
          .gradient_boosted_trees_config] {
            # 树的数量是固定的。
            树的数量: 100
            }
  }
  基础学习器部署 {
    # 每个候选学习器在一个线程上运行。
    线程数: 1
  }
  # 要优化的超参数列表。
  搜索空间 {
    字段 {
      名称: "候选属性比例"
      离散候选值 {
        可能值 { 实数: 1.0 }
        可能值 { 实数: 0.8 }
        可能值 { 实数: 0.6 }
      }
    }
    字段 {
      名称: "使用海森增益"
      离散候选值 {
        可能值 { 分类: "true" }
        可能值 { 分类: "false" }
      }
    }
    字段 {
      名称: "生长策略"
      离散候选值 {
        可能值 { 分类: "局部" }
        可能值 { 分类: "最佳优先全局" }
      }
      子字段 {
        父离散值 {
          可能值 { 分类: "局部" }
        }
        名称: "最大深度"
        离散候选值 {
          可能值 { 整数: 4 }
          可能值 { 整数: 5 }
          可能值 { 整数: 6 }
          可能值 { 整数: 7 }
        }
      }
      子字段 {
        父离散值 {
          可能值 { 分类: "最佳优先全局" }
        }
        名称: "最大节点数"
        离散候选值 {
          可能值 { 整数: 16 }
          可能值 { 整数: 32 }
          可能值 { 整数: 64 }
          可能值 { 整数: 128 }
        }
      }
    }
  }
}

分布式训练

分布式训练 指的是在多台计算机上训练模型。 分布式训练在大数据集(例如,>10M 样本)或昂贵的学习算法(例如,超参数调优)中特别有价值。

当使用分布式训练时,执行“训练”操作的进程(即运行 learner->Train() 函数的程序,或运行 train CLI 命令的进程)成为管理者。其余的工作由工作者执行。工作者是工作者二进制文件的实例。

并非所有学习器都支持分布式训练。例如,GRADIENT_BOOTED_TREES 学习器不支持分布式训练,但 DISTRIBUTED_GRADIENT_BOOSTED_TREES 支持。一些学习器(例如,HYPERPARAMETER_OPTIMIZER)同时支持分布式和非分布式训练。

以下学习器支持分布式训练:

  • DISTRIBUTED_GRADIENT_BOOSTED_TREES:梯度提升树模型的精确分布式训练。

  • HYPERPARAMETER_OPTIMIZER:模型的超参数自动优化。

在大数据集上训练时,建议将数据集分成多个文件。有关更多详细信息,请参阅数据集路径和格式部分。

以下是使用 DISTRIBUTED_GRADIENT_BOOSTED_TREES 算法进行分布式训练的示例。

训练数据集是一组位于 /remote/dataset/ 远程目录中的 csv 文件。例如:

ls /remote/dataset/
> /remote/dataset/train_0.csv
> /remote/dataset/train_1.csv
> /remote/dataset/train_2.csv
...
> /remote/dataset/train_1000.csv

因此,数据集路径为 csv:/remote/dataset/train_*.csv

重要的是,对于 DISTRIBUTED_GRADIENT_BOOSTED_TREES 算法(不同的算法有不同的约束),所有工作者都应该能够访问此数据集。

训练配置如下:

train_config.pbtxt

learner: "DISTRIBUTED_GRADIENT_BOOSTED_TREES"
task: CLASSIFICATION
label: "income"
[yggdrasil_decision_forests.model.distributed_gradient_boosted_trees.proto
      .distributed_gradient_boosted_trees_config] {
}

通过配置分布式执行引擎(DEE)并设置 DeploymentConfig 中的 cache_path 字段来启用分布式训练。DEE 定义了工作者数量以及如何访问它们(例如,IP 地址列表)。

以下是配置为分布式训练的 DeploymentConfig 示例:

deploy_config.pbtxt

# work_directory 是一个所有工作者都可以访问的远程目录。
cache_path: "/remote/work_directory"

num_threads: 8 # 每个工作者将运行 8 个线程。
try_resume_training: true # 允许训练中断并恢复。

distribute {
  implementation_key: "GRPC"
  [yggdrasil_decision_forests.distribute.proto.grpc] {
    socket_addresses {
      # 配置 3 个工作者。
      addresses { ip: "192.168.0.10" port: 8123 }
      addresses { ip: "192.168.0.11" port: 8123 }
      addresses { ip: "192.168.0.12" port: 8123 }
      }
    }
  }

在启动分布式训练之前,您需要启动工作者。在此示例中,我们配置了 3 个工作者在三台不同的计算机上运行。在每台计算机上,我们需要使用相应的端口启动一个工作者进程。例如:

# 通过 ssh 连接到 192.168.0.10,然后运行:

# 下载并解压工作者二进制文件。
wget https://github.com/google/yggdrasil-decision-forests/releases/download/1.0.0/cli_linux.zip
unzip cli_linux.zip

# 启动工作者
./grpc_worker_main --port=8123

有关更多详细信息,请参阅 yggdrasil_decision_forests/utils/distribute/implementations/grpc/grpc.proto

一旦工作者启动,就可以在管理者上启动训练:

# 在管理者上。

# 检查所有配置文件是否可用
ls
> train_config.pbtxt
> deploy_config.pbtxt

# 确定数据集的 dataspec
# 有关更多详细信息,请参阅 "CLI / 快速入门" 中的 "创建 dataspec" 部分。
./infer_dataspec \
  --dataset=csv:/remote/dataset/train_*.csv \
  --output=dataspec.pbtxt

# 启动分布式训练。
./train \
  --dataset=csv:/remote/dataset/train_*.csv \
  --dataspec=dataspec.pbtxt \
  --config=train_config.pbtxt\
  --deployment=deploy_config.pbtxt\
  --output=model

此示例 通过在同一台机器上运行所有工作者来演示分布式训练。这实际上不是分布式训练,但这是测试它的简单方法。

上述示例使用了 GRPC DEE。不同的 DEE 决定了分布式训练的方式。 计算原语被执行。DEE的选择不会影响有效训练的模型。

例如,TensorFlow Decision Forests使用TF_DIST DEE,它可以运行TensorFlow参数服务器的分布式计算。TF_DIST DEE支持套接字地址和TF_CONFIG配置。

推荐在CLI或C++ API中使用GRPC分布引擎。当使用TensorFlow Decision Forests接口时,GRPC和TF_DIST都是可行的选项(各有优缺点)。

特征工程

改进特征对于模型质量至关重要。为了简单性(YDF只做一件事)和执行效率(直接代码执行通常比中间表示执行快得多),YDF核心库不包含可定制的处理组件,除了文本分词。

注册类

Yggdrasil决策森林的源代码分为模块。不同的模块实现不同的功能,如学习算法、模型、数据集格式支持等。

模块通过Bazel依赖规则的注册机制进行控制:要启用一个模块,应将对该模块的依赖添加到代码的任何cc_librarycc_binary中。请注意,添加模块也会增加二进制文件的大小。

为了简单起见,yggdrasil_decision_forests/cli中的CLI工具是用所有可用模块编译的。

当使用C++ API时,应手动添加对模块的依赖。例如,要支持RANDOM_FOREST模型的训练,二进制文件/库需要依赖于"yggdrasil_decision_forests/learner/random_forest"规则。

类型为“没有用键...注册的类”或“类池中未知项...”的错误表明缺少对所需模块的依赖。

Yggdrasil决策森林还定义了包含某种类型所有模块的模块组。例如,规则yggdrasil_decision_forests/learner:all_learners注入了所有可用的学习算法(包括上面提到的:random_forest)。

以下是可用模块路径和注册键的列表。

学习算法

  • learner/cart CART
  • learner/distributed_gradient_boosted_trees DISTRIBUTED_GRADIENT_BOOSTED_TREES
  • learner/gradient_boosted_trees GRADIENT_BOOSTED_TREES
  • learner/random_forest RANDOM_FOREST
  • learner/hyperparameters_optimizer HYPERPARAMETER_OPTIMIZER

模型

  • model/gradient_boosted_trees GRADIENT_BOOSTED_TREES
  • model/random_forest RANDOM_FOREST

推理引擎

  • serving/decision_forest:register_engines

数据集IO

  • dataset:csv_example_reader FORMAT_CSV(仅读取)
  • dataset:csv_example_writer FORMAT_CSV(仅写入)
  • dataset:tf_example_io_tfrecord FORMAT_TFE_TFRECORD
  • dataset:capacitor_example_reader FORMAT_CAPACITOR
  • dataset:tf_example_io_recordio FORMAT_TFE_RECORDIO
  • dataset:tf_example_io_sstable FORMAT_TFE_SSTABLE

分布式计算后端

  • utils/distribute/implementations/multi_thread MULTI_THREAD
  • utils/distribute/implementations/grpc GRPC
  • tensorflow_decision_forests/tensorflow/distribute:tf_distribution TF_DIST

梯度提升树学习器的损失

  • learner/gradient_boosted_trees/loss/loss_imp_*

高级功能

大多数YDF高级文档都写在.h头文件(对于库)和.cc头文件(对于CLI工具)中。~~~