添加特征类型
1. 定义新的特征类型¶
特征类型在 ludwig/constants.py
中定义为常量。
将新特征类型的名称添加为常量
BINARY = "binary"
CATEGORY = "category"
...
NEW_FEATURE_TYPE = "new_feature_type_name"
2. 在新的 Python 模块中添加特征类¶
特征类的源代码位于 ludwig/features/
下。将新特征的实现添加到新的 Python 模块 ludwig/feature/<new_name>_feature.py
中。
输入和输出特征类定义在同一个文件中,例如 CategoryInputFeature
和 CategoryOutputFeature
定义在 ludwig/features/category_feature.py
中。
输入特征继承自 ludwig.features.base_feature.InputFeature
和相应的 mixin 特征类
class CategoryInputFeature(CategoryFeatureMixin, InputFeature):
类似地,输出特征继承自 ludwig.features.base_feature.OutputFeature
和相应的 mixin 特征类
class CategoryOutputFeature(CategoryFeatureMixin, OutputFeature):
特征基类(InputFeature
, OutputFeature
)继承自 LudwigModule
,而 LudwigModule
本身是一个 torch.nn.Module,因此开发 Torch 模块的所有常规注意事项都适用。
Mixin 类提供共享的预处理/后处理状态和逻辑,例如从类别到索引的映射,输入和输出特征实现共享这些状态和逻辑。Mixin 类不是 Torch 模块,也不需要提供 forward 方法。
class CategoryFeatureMixin(BaseFeatureMixin):
3. 实现所需方法¶
输入特征¶
构造函数¶
特征参数作为字典(键值对)提供给构造函数。feature
字典通常应在初始化前传递给超类构造函数
def __init__(self, feature: [str, Any], encoder_obj=None):
super().__init__(feature)
# Initialize any modules, layers, or variable state
输入
- feature: (dict) 包含所有特征配置参数。
- encoder_obj: (Encoder, 默认值:
None
) 是支持类型的编码器对象(类别编码器、二进制编码器等)。输入特征通常创建自己的编码器,只有当两个输入特征共享同一个编码器时才指定encoder_obj
。
forward¶
所有输入特征必须实现具有以下签名的 forward
方法
def forward(self, inputs: torch.Tensor) -> torch.Tensor:
# perform forward pass
# ...
# inputs_encoded = result of encoder forward pass
return inputs_encoded
输入
- inputs (torch.Tensor): 输入张量。
返回
- (torch.Tensor): 由输入特征的编码器编码后的输入数据。
input_shape¶
@property
def input_shape(self) -> torch.Size:
返回
- (torch.Size): 特征期望输入的完整指定大小,不包含批处理维度。
输出特征¶
构造函数¶
def __init__(self, feature: Dict[str, Any], output_features: Dict[str, OutputFeature]):
super().__init__(feature, output_features)
self.overwrite_defaults(feature)
# Initialize any decoder modules, layers, metrics, loss objects, etc.
输入
- feature (dict): 包含所有特征参数。
- output_features (dict[Str, OutputFeature]): 其他输出特征的字典,仅当此输出特征依赖于其他输出时使用。
logits¶
从组合器输出(以及此特征所依赖的任何特征)计算特征 logits。
def logits(self, inputs: Dict[str, torch.Tensor], **kwargs):
hidden = inputs[HIDDEN]
# logits = results of decoder operation
return logits
输入
- inputs (dict): 输入字典,包含
HIDDEN
键,其值是组合器的输出。如果此特征依赖于其他输出特征,还将包含其他输入键。
返回
- (torch.Tensor): 特征 logits。
create_predict_module¶
创建并返回一个 torch.nn.Module
,用于将原始模型输出 (logits) 转换为预测。导出模型到 Torchscript 需要此模块。
def create_predict_module(self) -> PredictModule:
返回
- (PredictModule): 一个其 forward 方法将特征 logits 转换为预测的模块。
output_shape¶
@property
def output_shape(self) -> torch.Size:
返回
- (torch.Size): 特征输出的完整指定大小,不包含批处理维度。
特征 Mixin¶
如果您的新特征可以重用现有特征类型的预处理和后处理逻辑,则无需实现新的 mixin 类。如果您的新特征确实需要独特的预处理或后处理,请添加 ludwig.features.base_feature.BaseFeatureMixin
的新子类。实现 BaseFeatureMixin
的所有抽象方法。
4. 将新的特征类添加到相应的特征注册表¶
输入和输出特征注册表定义在 ludwig/features/feature_registries.py
中。导入您的新特征类,并将它们添加到相应的注册表字典中
base_type_registry = {
CATEGORY: CategoryFeatureMixin,
...
}
input_type_registry = {
CATEGORY: CategoryInputFeature,
...
}
output_type_registry = {
CATEGORY: CategoryOutputFeature,
...
}
5. 添加新特征类型的模式类定义¶
为了根据您定义的新特征类型的预期输入和输入类型验证用户输入,我们需要创建将自动生成验证所需的 json 模式的模式类。
如果新特征类型仅作为输入特征使用,您只需定义一个输入特征模式类。以下是类别特征模式类如何定义的示例
输入特征类型¶
from marshmallow_dataclass import dataclass
from ludwig.constants import CATEGORY
from ludwig.schema import utils as schema_utils
from ludwig.schema.encoders.base import BaseEncoderConfig
from ludwig.schema.encoders.utils import EncoderDataclassField
from ludwig.schema.features.base import BaseInputFeatureConfig
from ludwig.schema.features.preprocessing.base import BasePreprocessingConfig
from ludwig.schema.features.preprocessing.utils import PreprocessingDataclassField
from ludwig.schema.features.utils import input_config_registry, output_config_registry
@input_config_registry.register(CATEGORY)
@dataclass
class CategoryInputFeatureConfig(BaseInputFeatureConfig):
"""CategoryInputFeatureConfig is a dataclass that configures the parameters used for a category input
feature."""
preprocessing: BasePreprocessingConfig = PreprocessingDataclassField(feature_type=CATEGORY)
encoder: BaseEncoderConfig = EncoderDataclassField(
feature_type=CATEGORY,
default="dense",
)
tied: str = schema_utils.String(
default=None,
allow_none=True,
description="Name of input feature to tie the weights of the encoder with. It needs to be the name of a "
"feature of the same type and with the same encoder parameters.",
)
如果新特征类型也可以作为输出特征类型,您还需要定义一个输出特征模式类
输出特征类型¶
from marshmallow_dataclass import dataclass
from ludwig.schema import utils as schema_utils
from ludwig.constants import CATEGORY, SOFTMAX_CROSS_ENTROPY
from ludwig.schema.decoders.base import BaseDecoderConfig
from ludwig.schema.decoders.utils import DecoderDataclassField
from ludwig.schema.features.base import BaseOutputFeatureConfig
from ludwig.schema.features.loss.loss import BaseLossConfig
from ludwig.schema.features.loss.utils import LossDataclassField
from ludwig.schema.features.preprocessing.base import BasePreprocessingConfig
from ludwig.schema.features.preprocessing.utils import PreprocessingDataclassField
from ludwig.schema.features.utils import output_config_registry
@output_config_registry.register(CATEGORY)
@dataclass
class CategoryOutputFeatureConfig(BaseOutputFeatureConfig):
"""CategoryOutputFeatureConfig is a dataclass that configures the parameters used for a category output
feature."""
preprocessing: BasePreprocessingConfig = PreprocessingDataclassField(feature_type="category_output")
loss: BaseLossConfig = LossDataclassField(
feature_type=CATEGORY,
default=SOFTMAX_CROSS_ENTROPY,
)
decoder: BaseDecoderConfig = DecoderDataclassField(
feature_type=CATEGORY,
default="classifier",
)
reduce_input: str = schema_utils.ReductionOptions(
default="sum",
description="How to reduce an input that is not a vector, but a matrix or a higher order tensor, on the first "
"dimension (second if you count the batch dimension)",
)
dependencies: list = schema_utils.List(
default=[],
description="List of input features that this feature depends on.",
)
reduce_dependencies: str = schema_utils.ReductionOptions(
default="sum",
description="How to reduce the dependencies of the output feature.",
)
top_k: int = schema_utils.NonNegativeInteger(
default=3,
description="Determines the parameter k, the number of categories to consider when computing the top_k "
"measure. It computes accuracy but considering as a match if the true category appears in the "
"first k predicted categories ranked by decoder's confidence.",
)
calibration: bool = schema_utils.Boolean(
default=False,
description="Calibrate the model's output probabilities using temperature scaling.",
)
最后,您需要在输入特征类型定义中添加对模式类定义的引用。例如,在 CategoryInputFeature
类上,我们需要添加一个 get_schema_cls
方法
class CategoryInputFeature(CategoryFeatureMixin, InputFeature):
...
@staticmethod
def get_schema_cls():
return CategoryInputFeatureConfig
输出特征类也同样
class CategoryOutputFeature(CategoryFeatureMixin, OutputFeature):
...
@staticmethod
def get_schema_cls():
return CategoryOutputFeatureConfig