特征语义¶
训练模型时,YDF 需要理解如何解释训练数据中的特征值。例如,特征可能是一个数值量、一个类别或一组标签。对特征值的解释称为特征语义 (feature semantic)。
特征的语义与特征的表示形式(即特征的技术数据类型)相关,但又有所不同。例如,由 64 位整数表示的特征可能具有数值语义或类别语义。
在基本情况下,YDF 会自动检测特征的语义,因此您只需在训练后检查它们(例如,使用 model.describe())。如果 YDF 未检测到正确的语义,您可以手动覆盖它。使用错误的语义会对模型的训练速度和质量产生负面影响。此外,YDF 无法使用所有类型的特征。在这种情况下,需要将特征预处理为支持的语义。
本指南解释了 YDF 中可用的不同语义,如何检查/选择它们,并提供了如何将不同类型的特征输入模型的建议。
本指南假设您已基本熟悉 YDF,例如阅读过入门教程。
引言:如何指定特征语义¶
除非给定额外信息,否则 YDF 在训练模型时会自动确定特征语义
model = ydf.RandomForestLearner(label="label").train(ds)
# The "Dataspec" tab of the model description shows the feature semantics
# used for this model.
model.describe()
可以使用 ydf.Semantic
枚举手动覆盖特征语义
model = ydf.RandomForestLearner(
features=[("f1", ydf.Semantic.NUMERICAL), ("f2", ydf.Semantic.CATEGORICAL)],
include_all_columns=True, # Also use not explicitly defined features.
label="label"
).train(ds)
model.describe()
目前,YDF 支持 5 种输入特征语义。随着我们的研究,新语义会不时添加。这些语义是
ydf.Semantic.NUMERICAL
ydf.Semantic.CATEGORICAL
ydf.Semantic.BOOLEAN
ydf.Semantic.CATEGORICAL_SET
ydf.Semantic.DISCRETIZED_NUMERICAL
下一节将更详细地解释各个语义。
特征语义¶
ydf.Semantic.NUMERICAL¶
NUMERICAL 特征代表数量、金额或更一般地,任何有序值。例如,年龄(年)、持续时间(秒)、净资产(美元)、请求数(计数)、分数(点),甚至分布的中位数,都是 NUMERICAL 特征。
YDF 会自动将整数和浮点值识别为 NUMERICAL。
# A dataset with 4 numerical features.
dataset = {
"age": np.array([1, 55, 24, 8]),
"number of cats": np.array([0, 10, 4, 2]),
"net worth": np.array([0.0, 123456.78, -4000.0, 315.42]),
"score": np.array([1.1, 5.2, math.nan, 1.2]),
}
ydf.Semantic.CATEGORICAL¶
CATEGORICAL 特征代表类别、枚举值、标签,或更一般地,任何无序值。例如,物种(猫、鸟或鱼)、血型(A、B、AB、O)、国家/地区、语言和项目状态(计划中、进行中、已完成、已取消)。YDF 会自动将字符串特征识别为 CATEGORICAL。
类别型特征的其他注意事项包括
- 不要分桶:在使用神经网络时,数值特征有时会被分桶为类别区间(例如,0-5、5-10、10-20)。这对 YDF 没有益处。如果可以,直接将值作为数值输入。如果您只有分桶数据,也可以将其作为数值特征输入。
- 不要使用独热编码:在使用树算法时,类别型特征的独热编码性能始终较差,不应在 YDF 中使用。
- 预处理:为了避免过拟合,YDF 会自动将稀有类别值替换为 OOD(“词典外”)。这通常会带来更好的模型。此行为由超参数
max_vocab_count
(用于限制类别数量)和min_vocab_frequency
(用于修剪稀有类别)控制。 - 未知值:在推理过程中,任何未知类别值都被视为 OOD,这与缺失值不同。例如,考虑一个模型,其中有一个“物种”特征,训练数据集中包含“猫”、“鸟”和“鱼”等值。如果在模型推理期间,某个实例的“物种”特征值为“老虎”,则模型会将“老虎”隐式转换为 OOD 标记。
-
Python 枚举:数值型 Python 枚举通常应具有类别语义,但由于它们是整数,会被自动识别为 NUMERICAL。因此,应手动将枚举指定为 CATEGORICAL。
# A dataset with 4 categorical features. dataset = { "species": np.array(["cat", "bird", "bird", "fish", "fish"]), "country": np.array(["US", "US", "Switzerland", "India", "India"]), "month": np.array([1, 1, 4, 6, 1]), # CATEGORICAL features can be integer. "blood type": np.array(["A", "B", "AB", "B", ""]), # Missing values are empty. } model = ydf.RandomForestLearner( label="blood type", # Since integers are auto-detected as NUMERICAL, specify the semantic manually. features=[("month", ydf.Semantic.CATEGORICAL)], min_vocab_frequency=2, # Prune vocabulary items that appears only once ).train(dataset) # Check that all features and the label are CATEGORICAL. # Check model.data_spec() column to see the feature's categories. model.describe()
ydf.Semantic.BOOLEAN¶
布尔特征的值只能是 true、false 或缺失。例如,“已订阅”、“是垃圾邮件”、“有库存”等。布尔特征是类别型特征的一种特殊情况。对于大多数数据集格式,YDF 会自动将布尔特征识别为 BOOLEAN。
模型的标签永远不能是 BOOLEAN。请注意,二分类使用 CATEGORICAL 标签。
# A dataset with 3 boolean features.
dataset = {
"has subscribed": np.array([True, False, False, True]),
"spam": np.array([1,0,1,1], dtype=bool), # Ensure the dtype is not integer.
"happy": np.array([True, False, False, True]),
}
警告
避免将 ID 用作特征:许多数据集具有“ID”/“标识符”、“唯一哈希”等类型的特征,这些特征对于数据集中的每个示例来说都是(几乎)唯一的。这些特征对训练机器学习模型没有用处,它们会减慢模型训练速度并可能增加模型大小。因此,在训练之前从数据集中删除这些值非常重要。
特殊语义¶
ydf.Semantic.DISCRETIZED_NUMERICAL¶
DISCRETIZED_NUMERICAL 并不是一个新语义。相反,它用于告诉学习算法使用特殊的离散化算法优化训练。任何 NUMERICAL 特征都可以配置为 DISCRETIZED_NUMERICAL。训练会更快(通常快约 2 倍),但这可能会损害模型质量。将所有 NUMERICAL 特征设置为 DISCRETIZED_NUMERICAL 等同于设置超参数 detect_numerical_as_discretized_numerical=True
。
data = {
"age": np.array([1, 55, 24, 8]),
"net worth": np.array([0.0, 123456.78, -4000.0, 315.42]),
"weight": np.array([9, 63, 70, np.nan]),
}
model = ydf.RandomForestLearner(
label="weight",
discretize_numerical_columns=False, # Default
features=[("net worth", ydf.Semantic.DISCRETIZED_NUMERICAL)],
task=ydf.Task.REGRESSION,
).train(data)
# `net worth` is DISCRETIZED_NUMERICAL, `age` and `weight` are NUMERICAL.
model.describe()
ydf.Semantic.CATEGORICAL_SET¶
类别集特征的值是一组类别值。换句话说,ydf.Semantic.CATEGORICAL 只能有一个值,而 ydf.Semantic.CATEGORICAL_SET 特征可以有零个、一个或多个类别值。将其用于离散值集,例如分词文本或标签集(例如,网页讨论 {政治, 选举, 美国})。
当处理文本特征时,分词非常重要。在英语中,按空格分割尚可接受,但在中文中效果很差。例如,“A cat sits on a tree”变成 {a, cat, on, sits, tree}。使用更强大的分词器可能会更好。由于集合不编码位置,{a, cat, on, sits, tree} 等同于 {a, tree, sits, on, cat}。一种解决方案是使用多词组(例如,二元词组),它们编码连续的词。例如,在我们示例中的二元词组是 {a_cat, cat_sit, sit_on, on_a, a_tree}。
- 预处理:与 CATEGORICAL 特征一样,YDF 会自动将 CATEGORICAL_SET 的稀有类别值替换为 OOD(“词典外”)。此行为由超参数
max_vocab_count
(用于限制类别数量)和min_vocab_frequency
(用于修剪稀有类别)控制。 - 训练速度:CATEGORICAL_SET 特征的训练速度比 NUMERICAL 或 CATEGORICAL 特征慢。不要使用 CATEGORICAL_SET 代替 CATEGORICAL 特征(结果相同,但模型训练会变慢)。
- 分词:将 CATEGORICAL_SET 用于文本特征时,文本必须在输入 YDF 之前进行分词。CSV 文件会自动按空格进行分词,详情请参阅 CSV 文件部分。
# A dataset with 2 categorical set features and one categorical feature.
dataset = {
"title": [["Next", "week", "are", "us", "elections"], ["Reform", "started", "this", "month"], ["Funniest", "politics", "speeches"]],
"tags": [["politics", "election"], ["politics"], ["funny", "politics"]],
"interesting": ["yes", "yes", "no"],
}
model = ydf.RandomForestLearner(
label="interesting",
min_vocab_frequency=1, # Don't prune the categories.
features=[("title", ydf.Semantic.CATEGORICAL_SET), ("tags", ydf.Semantic.CATEGORICAL_SET)],
).train(dataset)
多维特征¶
YDF 支持固定大小的向量作为特征。这通常用于处理向量嵌入。例如,考虑一个文本特征 text
,它在使用 Universal Sentence Encoder 模型进行预处理后,被转换为一个包含 512 个条目的数值向量。这个向量可以直接输入到 YDF。
YDF 将向量的每个条目“展开”为单个特征。这些特征命名为 text.0_of_512
、text.1_of_512
等。请注意,所有向量的大小必须完全相同——如果向量大小不同,请考虑 CATEGORICAL_SET 语义。有关更详细的示例,请参见此处。
# A dataset with a two-dimensional numerical feature
# and a two-dimensional categorical feature.
dataset = {
"categorical_vector": np.array([["a", "b"], ["a", "c"], ["b", "c"]]),
"numerical_embeeding": np.array([[1, 2], [3, 4], [5, 6]]),
"label": np.array([1, 2, 1]),
}
model = ydf.RandomForestLearner(
label="label",
).train(dataset)
# `categorical_vector` is unrolled to two CATEGORICAL features:
# `categorical_vector.0_of_2` and `categorical_vector.1_of_2`.
# `numerical_embeeding` is unrolled to two NUMERICAL features:
# `numerical_embeeding.0_of_2` and `numerical_embeeding.1_of_2`.
model.describe()
注意
ydf.Semantic.HASH 仅在内部使用,不能用于决策树训练。
非原生支持的语义¶
有些特征 YDF 无法原生使用,而需要进行预处理。本节详细介绍了最常见的场景。
时间戳¶
时间戳应转换为 NUMERICAL 语义。一种常用的方法是将时间戳分解为日历特征,例如星期几、一年中的第几周、一天中的小时等。简单地将时间戳转换为数值型的 Unix 时间通常效果不佳。
警告
时间戳的存在通常表明数据集包含时间序列数据(请参阅下一节)。
时间序列¶
时间序列数据集需要高级特征预处理以获得良好的模型质量。有关更多信息,请查看 YDF 文档中的专门指南。
警告
时间序列需要仔细建模,以防止未来数据泄露或模型质量不佳。对于复杂问题,建议使用 Temporian 等预处理工具。
重复的 proto 消息¶
重复的 proto 消息必须“展平”为单个特征。重复的数值条目可以创建统计特征,如最大值、最小值、均值、中位数、方差等,这很有用。重复的类别条目可以转换为 CATEGORICAL_SET 特征。
图像¶
决策森林模型并不是图像处理领域最先进的模型架构。在许多情况下,应优先探索其他模型架构(特别是神经网络)。
按数据集格式划分的详细信息¶
Numpy¶
标量类型
下表显示了哪些标量 Numpy 数据类型对应于哪些 YDF 语义,以及在手动指定特征语义后 YDF 可以执行哪些转换。
Numpy\YDF | NUMERICAL [3] | CATEGORICAL | BOOLEAN | CATEGORICAL SET [6] | DISCRETIZED NUMERICAL |
---|---|---|---|---|---|
int [1] | 默认 | 转换 [4] | 不支持 | 不支持 | 转换 |
float [2] | 默认 | 不支持 | 不支持 | 不支持 | 转换 |
bool | 转换 | 转换 [5] | 默认 | 不支持 | 转换 |
str | 不支持 | 默认 | 不支持 | 不支持 | 不支持 |
bytes | 不支持 | 默认 | 不支持 | 不支持 | 不支持 |
[1]: 包括无符号整数
[2]: 不支持 float128
[3]: YDF 内部将数值转换为 float32。
[4]: 内部将值转换为字符串并按频率排序。标签值按字典顺序(字符串)或升序(整数)排序。
[5]: 内部将值转换为“false”和“true”,其中“false”排在前面。
[6]: 使用类型 object
支持 CATEGORICAL SET,请参阅下文。
二维数组(即矩阵)
YDF 按列展开二维数组(即矩阵)。
Object
YDF 检查作为类型 object
的 numpy 数组给出的特征,并根据其内容区别对待。
如果数组的第一个元素是标量类型,YDF 会尝试将数组转换为 bytes
类型并将其视为 CATEGORICAL。
如果数组的第一个元素是 Python 列表或 Numpy 数组(无论 dtype 如何),YDF 会检查它是否仅包含列表(数组),否则失败。如果所有列表具有相同的长度,YDF 会尝试将子列表(子数组)转换为 np.bytes
类型,并将整个特征视为 CATEGORICAL 特征的矩阵。然后将该矩阵展开为单个特征。
如果子列表(子数组)大小不同,YDF 会尝试将子列表(子数组)转换为 np.bytes
类型,并以 CATEGORICAL_SET 语义处理整个特征。
不支持任何其他类型或类型的组合。
缺失值
缺失的 NUMERICAL 值是 np.Nan
,缺失的 CATEGORICAL 值是空字符串。无法表示缺失的 BOOLEAN 或 CATEGORICAL_SET 值。
Python 列表¶
YDF 可以将 Python 列表作为特征使用。但是,Python 列表不支持自动语义检测。此外,多维特征(CATEGORICAL_SET 除外)不能使用 Python 列表输入。
CSV¶
自动语义检测
- 仅包含 0 和 1 的列被识别为 BOOLEAN。
- 仅包含数值的列被识别为 NUMERICAL。
- 其他列被识别为 CATEGORICAL(见下文)。
- 不支持多维特征。
示例代码
"""!cat mycsv.csv
num,bool,cat,catset,label
1.0,1,a,x y,1
1.5,0,b,y z,2
2.0,1,1,x y z,3"""
model = ydf.RandomForestLearner(
label="label",
# Note that CATEGORICAL_SET columns are tokenized by whitespace.
features=[("catset", ydf.Semantic.CATEGORICAL_SET)],
min_vocab_frequency=1,
include_all_columns=True,
).train("csv:mycsv.csv")
# Column "num" is NUMERICAL.
# Column "bool" is BOOLEAN
# Column "cat" is CATEGORICAL
# Column "catset" is CATEGORICAL_SET (as specified).
model.describe()
警告
仅检查前 100000 行以确定列的类型。调整 max_num_scanned_rows_to_infer_semantic 以增加此值。
自动分词
从 CSV 文件读取时,YDF 通过沿空格分割来对具有 CATEGORICAL_SET 语义的特征进行分词。
一些 YDF 接口可能会自动推断包含空格的字符串列的类型为具有 CATEGORICAL_SET 语义。请注意,即使读取 CSV 文件,YDF 的 Python API 也不会自动推断类型为 CATEGORICAL_SET。
缺失值
缺失值用字符串 na
或空字符串表示。
Avro¶
自动语义检测
Avro 列是强类型的,因此 YDF 默认使用列的类型。
- 布尔列被识别为 BOOLEAN
- Long、Int、Float 和 Double 列被识别为 NUMERICAL
- Bytes 和 String 列被识别为 CATEGORICAL
- 数组列会根据数组中数据的类型进行展开。此外,Bytes 或 String 数组的类型可以是 CATEGORICAL_SET。
- 目前不支持嵌套数组,但将来可能会支持。
进阶:决策森林如何使用特征语义¶
本节对于使用 YDF 并非必需,但它可能为各个语义以及常见决策森林学习算法如何使用它们提供额外背景信息。
回想一下,决策树递归地分割数据集,直到达到停止条件。特征语义既决定了考虑哪种类型的分割,也决定了算法如何找到最佳分割。
-
NUMERICAL:学习算法根据阈值创建数据分割(例如,“age >= 30”)。
对于更强大的模型,请启用斜向分割。这使得 YDF 可以学习组合多个数值特征的分割(例如,“0.3 * age + 0.7 * income >= 50”)。这对于较小的数据集特别有用,但需要更多的训练时间。
-
CATEGORICAL:学习算法通过将值分组到集合中来创建分割(例如,“country in {美国, 瑞士, 卢森堡}”)。这比数值分割更具表现力,但计算成本更高。
-
BOOLEAN:与使用 NUMERICAL 或 CATEGORICAL 语义相比,此语义提供了内存和速度优化的方式来处理布尔数据。
CATEGORICAL_SET:学习算法基于集合交集创建分割(例如,“
{a, cat, on, sits, tree}
与{cat, dog, bird}
相交”)。此语义计算成本高,训练速度可能较慢。有关类别集的深入信息,请参阅Guillame-Bert 等人,2020 年。
缺失值¶
YDF 有效地处理缺失值,每种语义的表示方式不同。训练期间,YDF 使用全局填充
- NUMERICAL 和 DISCRETIZED_NUMERICAL:缺失值被替换为特征的均值。
- CATEGORICAL 和 BOOLEAN:缺失值被替换为最频繁的值。
- CATEGORICAL_SET:缺失值始终被路由到分割的否定分支。
如果超参数 allow_na_conditions
启用,学习算法还可以创建“特征为 NA”形式的分割。请注意,这通常不是必需的:全局填充将缺失值替换为“特殊”值,该值可以被算法快速学习。
注意
标签列不允许有缺失值。
标签列语义¶
标签列的语义由模型任务决定
- REGRESSION:需要 NUMERICAL 标签。
- CLASSIFICATION:需要 CATEGORICAL 标签。
- RANKING:需要具有整数值的 NUMERICAL 标签。
- CATEGORICAL_UPLIFT:需要 NUMERICAL 标签。
- NUMERICAL_UPLIFT:需要 NUMERICAL 标签。
更改任务会更改模型的损失函数和输出,从而得到一个根本不同的模型。如果标签列无法用所选任务的语义解释,模型训练将失败。
数据规范 (Data Spec)¶
在内部,YDF 模型将特征语义信息存储在“数据规范 (data spec)”中。模型的数据规范是一个 proto 消息,可以使用 model.data_spec()
显示。数据规范还包含训练期间看到的特征值信息。其中一些信息可能在训练和推理期间使用。特别是,数据规范存储以下信息:
- 对于 NUMERICAL 和 DISCRETIZED_NUMERICAL 特征,统计信息以及(如果相关)分桶信息。
- 对于 CATEGORICAL 和 CATEGORICAL_SET 特征,训练期间看到的值字典及其频率。请注意,排序不能保证在不同版本之间保持稳定,不应依赖它。
- 对于 BOOLEAN 值,训练期间看到的 true 和 false 值的频率。
数据规范的摘要显示在 model.describe()
中。原始数据规范主要用于调试 YDF 相关问题。