Skip to content

Feature Definitions

Torch-RecHub provides three core feature classes for different data types.

Feature types to model inputs

DenseFeature

Numeric features (e.g., age, income).

python
from torch_rechub.basic.features import DenseFeature

dense_feature = DenseFeature(name="age", embed_dim=1)

Parameters: name, embed_dim (always 1).

SparseFeature

Categorical features (e.g., city, gender).

python
from torch_rechub.basic.features import SparseFeature

sparse_feature = SparseFeature(
    name="city",
    vocab_size=100,
    embed_dim=16,
    shared_with=None,  # share embeddings with another feature if needed
)

Parameters: name, vocab_size, embed_dim (auto if None), shared_with, padding_idx, initializer.

SequenceFeature

Sequence or multi-hot features (e.g., behavior history, tags).

python
from torch_rechub.basic.features import SequenceFeature

sequence_feature = SequenceFeature(
    name="user_history",
    vocab_size=10000,
    embed_dim=32,
    pooling="mean",  # mean, sum, concat
)

Parameters: name, vocab_size, embed_dim (auto if None), pooling (mean/sum/concat), shared_with, padding_idx, initializer.

Feature Instances and Embedding Ownership

Warning: SparseFeature and SequenceFeature cache the nn.Embedding created by get_embedding_layer(). If the same Feature instance is passed to multiple models, those models use the same embedding parameters. Training or loading weights into one model therefore changes the embedding observed by the others.

The two EmbeddingLayer instances below unintentionally share the city embedding:

python
from torch_rechub.basic.features import SparseFeature
from torch_rechub.basic.layers import EmbeddingLayer

features = [SparseFeature(name="city", vocab_size=100, embed_dim=16)]

embedding_a = EmbeddingLayer(features)
embedding_b = EmbeddingLayer(features)

assert embedding_a.embed_dict["city"] is embedding_b.embed_dict["city"]

For independent model comparisons, cross-validation, or ensemble training, create new Feature instances for every model:

python
def build_features():
    return [SparseFeature(name="city", vocab_size=100, embed_dim=16)]

embedding_a = EmbeddingLayer(build_features())
embedding_b = EmbeddingLayer(build_features())

assert embedding_a.embed_dict["city"] is not embedding_b.embed_dict["city"]

Copying only the list does not help: features.copy() still contains the original Feature instances. This accidental cross-model sharing is different from explicitly using shared_with to share an embedding inside one model; the latter is intentional.

Usage Example

python
from torch_rechub.basic.features import DenseFeature, SparseFeature, SequenceFeature

dense_features = [
    DenseFeature(name="age", embed_dim=1),
    DenseFeature(name="income", embed_dim=1),
]

sparse_features = [
    SparseFeature(name="city", vocab_size=100, embed_dim=16),
    SparseFeature(name="gender", vocab_size=3, embed_dim=8),
]

sequence_features = [
    SequenceFeature(name="user_history", vocab_size=10000, embed_dim=32, pooling="mean"),
]

all_features = dense_features + sparse_features + sequence_features