跳至内容

添加特征类型

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 中。

输入和输出特征类定义在同一个文件中,例如 CategoryInputFeatureCategoryOutputFeature 定义在 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