Skip to content

Repository files navigation

UniDot

A Unified Network for Sequence Modeling and Feature Interaction in Large-scale Recommendation

UniDot is a post-click conversion-rate (pCVR) model that unifies the two model families industrial recommenders usually keep separate — feature-interaction models over multi-field user/item features, and sequential models over user-behavior histories — into a single stackable block. It was the runner-up on the Industrial track of the TAAC × KDD Cup 2026 (Tencent Uni-Rec Challenge), scoring 0.83217 test AUC — 0.037% behind the winning entry.

The design starts from the factorization-machine point of view: the embedding inner product that powers collaborative filtering is the same primitive as attention's query·key scoring, so one dot-product of tokens can underlie both feature interaction and sequence modeling. UniDot follows the SlimPer framework, which frames ranking as iterative refinement of a personalization state.

The paper, slides, and poster are in paper/.


Architecture at a glance

Non-sequential fields and multi-domain behavioral sequences are tokenized into one shared token space. The model stacks a single macro-block (UniDotBlock) in which two buses run in parallel and exchange state every layer:

  • Token-mixing bus — a swappable cross-token slot (Wukong: parallel LCB + FMB by default) over concatenated user / item / pooled-sequence tokens.
  • Sequence-retrieval bus — item tokens cross-attend the behavioral histories (per-domain + a merged cross-domain stream).
  • MLP-Mixer fusion (FuseFFN) — exchanges residual deltas between the two buses each layer.
  • FM Highway — routes explicit per-layer dot-product interactions (per-sequence dots, aggregated grams, cross-bus user×item dots) around the residual stack straight to the classifier, keeping second-order signal alive at negligible cost.

The sequence side is embedded once per forward pass and shared by all consumers, which bounds inference latency. Training uses a dual sparse/dense optimizer (Adagrad for embedding tables, Muon for matrix weights with an AdamW auxiliary group for 1-D params), an auxiliary conversion-delay head, and multi-path mutual learning (DML) — used by the submitted configuration.

See .claude/CLAUDE.md for a full architectural walkthrough and paper/ for the formal description.


Results

Final leaderboard (Industrial track, round 2)

Official metric is ROC AUC on a 12M-example held-out test set.

Rank AUC Gap to #1
1 0.83254
2 — UniDot (ours) 0.83217 0.037%
UniDot, single path 0.83184 0.070%
3 0.83145 0.109%
4 0.83080 0.174%
5 0.83073 0.181%

★ Serving a single path of the multi-path model — at 1× inference cost — still scores 0.83184, which would itself have placed second. Multi-path training pulls even a single served model to a better minimum, so DML is a training boost you don't have to pay for at serving time (config.DML_INFER_MODE='single').

What moved the number

From the competition baseline to the final submission, +1.818 AUC points:

Step Test AUC Δ
Competition baseline 0.81398
+ UniDot architecture 0.82500 +1.102
+ item-id hash embedding, token/batch tuning 0.82704 +0.204
+ more FM-Highway dot products, depthwise-conv pre-trunk 0.82736 +0.032
+ auxiliary delay loss, FAFE, LR tuning 0.82894 +0.158
+ EMA weights 0.82993 +0.099
+ scale d_model 64 → 128 0.83043 +0.050
+ multi-path DML (N=2) 0.83196 +0.153
+ all-data retrain 0.83217 +0.021

The architecture itself is the single largest jump (+1.10), from the released schema alone — no hand-crafted cross features.

Ablations

Component ablations (tiny mode, single path, d_model=64, in-distribution held-out — so absolute numbers differ from the leaderboard):

Variant AUC LogLoss ΔAUC
Full UniDot (6 macro-layers) 0.83657 0.2151
− FM Highway (all) 0.83530 0.2159 −0.127%
− cross-bus dots only 0.83570 0.2156 −0.087%
− token-mixing bus (identity) 0.83590 0.2161 −0.067%
− sequence cross-attention 0.83604 0.2153 −0.053%
− merged cross-domain stream 0.83619 0.2152 −0.038%
− multi-channel seq pool 0.83624 0.2155 −0.033%
− FuseFFN (no bus fusion) 0.83679 0.2154 +0.022%

The FM Highway is the costliest component to remove (−0.127%), which is the paper's central claim: explicit second-order signal routed around the residual stack is worth keeping.

Honest caveat: dropping FuseFFN slightly raises AUC (+0.022%) while worsening LogLoss, so that module's contribution is not conclusive. An input-conditioned (second-order) fuser is the clearest open direction — hence the --fuse_type {mixer,cross_attn,bilinear,asym} variants in the code.

Scaling: held-out AUC grows log-linearly at ≈+0.0025 AUC per doubling of training data (4M → 32M) and had not saturated. Dense capacity was the productive axis; doubling embedding width alone gave no A/B win.

Scale and cost

Training / test samples 35M / 12M (round 2)
Behavioral domains 4 (9 / 14 / 12 / 10 fields) + 1 merged stream
Largest ID feature ~9.4M distinct values
Total parameters ~2.1B (embedding-dominated)
Dense parameters / FLOPs 120.6M / 42.5 GFLOP (d_model=128, 2-path)
Training hardware 4 GPUs, effective batch ≈12k
Inference on 12M examples ≈14,500 s (2-path) / ≈7,200 s (single path)

bf16 autocast costs <0.0001 AUC. The submitted configuration is d_model=128, 6 macro-layers × 2 mix-blocks, sequence windows a:256 b:256 c:512 d:512 plus a merged abcd:512 stream. Full hyperparameters are in the paper's appendix.


Repository layout

File Description
unified_model.py The UnifiedModel (UniDot) architecture — tokenizers, sequence stage, UniDotBlock, FM Highway, classifier head.
common.py Shared building blocks — RoPE/attention (PyTorch SDPA), SwiGLU, compression blocks (LCB/NCB), sequence embedder.
dataset.py PCVRParquetDataset (streaming IterableDataset) + feature schema and time bucketing.
trainer.py Pointwise BCE training loop with AUC + LogLoss eval, dual optimizer, cold restart, EMA, self-contained checkpointing.
dml.py Deep Mutual Learning trainer (DMLTrainer / DMLModel) — N mutually-distilling peers.
train.py Entry point — argument parser, data/model/trainer wiring, build_model_from_args.
infer.py Streaming inference — rebuilds the model from the checkpoint and writes predictions.
muon.py Muon optimizer implementation.
utils.py Logging, early stopping, seeding.
config.py Launch MODE + inference-time defaults.
run.sh / run_dml.sh Reference training launchers (single-model / multi-path).

Installation

Requires Python 3.10+ and a CUDA-capable GPU.

pip install torch pyarrow numpy scikit-learn tqdm tensorboard python-dotenv

Attention uses PyTorch's built-in scaled-dot-product attention (SDPA); no FlashAttention install is required.

Optional:

  • muon-optimize — needed only for --dense_optimizer muon (the default in run.sh; use --dense_optimizer adamw otherwise).

Data

The model streams Parquet files containing per-event user/item features and behavioral sequences (see schema.json). The task is binary: predict conversion (label_type == 2), with roughly 12% positives.

The competition data is 35M training / 12M test events over 142 columns: 54 user-int and 17 user-dense fids, 17 item-int and 4 item-dense fids, 11 pre-trained embedding fids, and 4 behavioral domains (9 / 14 / 12 / 10 fields each). The full dataset is not redistributed here, but a 1,000-row sample with the same schema is available for smoke tests: TAAC2026/second_round_sample_1000 (CC-BY-NC-4.0 — note this is more restrictive than this repo's MIT code license).

Paths are supplied via environment variables (loaded from .env in local mode). Copy the template and fill in your paths:

cp .env.example .env
# then edit .env:
#   TRAIN_DATA_PATH       a parquet file or a directory of shards
#   TRAIN_CKPT_PATH       where checkpoints are written
#   TRAIN_LOG_PATH        text logs
#   TRAIN_TF_EVENTS_PATH  TensorBoard events
#   SCHEMA_SRC_PARQUET    a parquet whose schema matches the training data

Quick start

Smoke test on a small parquet

config.MODE == 'local' (the default) auto-loads .env and pins one GPU. The launcher picks torchrun DDP when more than one GPU is visible, so pin a single device for a smoke test. Point --train_data_path at any small parquet with the expected schema (a single-row-group file needs --split_by_time so the row-level train/val split has rows on both sides).

Grab the 1,000-row sample:

huggingface-cli download TAAC2026/second_round_sample_1000 --repo-type dataset --local-dir ./sample_data
CUDA_VISIBLE_DEVICES=0 bash run.sh \
    --train_data_path ./sample_data \
    --split_by_time \
    --num_epochs 2 \
    --reinit_cardinality_threshold 0 \
    --skip_num_slots 0

Training

run.sh holds the reference configuration (6 macro-layers, 4 behavioral domains plus a merged stream, per-position fid compression, DIN conditioning, Muon dense optimizer). Override any flag on the command line:

bash run.sh                                     # reference config
bash run.sh --num_epochs 20 --dropout_rate 0.1  # override flags

Multi-path mutual learning (DML)

bash run_dml.sh

run_dml.sh mirrors run.sh's training recipe and trains N mutually-distilling peers; at inference config.DML_INFER_MODE selects 'mean' (average all peers, best accuracy) or 'single' (serve one peer at 1× cost).

Inference

infer.py reads paths from environment variables, rebuilds the model from the checkpoint's train_config.json, and writes predictions:

MODEL_OUTPUT_PATH=/path/to/checkpoint \
EVAL_DATA_PATH=/path/to/test.parquet \
EVAL_RESULT_PATH=/path/to/predictions \
python3 infer.py

Inference-time behavior (batch size, torch.compile, autocast, matmul precision) is configured in config.py.


Launch modes

config.MODE selects the run profile:

  • local — auto-pins one GPU and loads .env; for demo / single-box development.
  • tiny — real-data path, single GPU, capped sample count for rapid A/B.
  • remote — full multi-GPU DDP run.

run.sh reads MODE and appends the appropriate flags (sample caps, learning rates, DDP launcher).


Citation

If you use this code, please cite the paper:

@inproceedings{unidot2026,
  title     = {UniDot: A Unified Network for Sequence Modeling and
               Feature Interaction in Large-scale Recommendation},
  author    = {Lin, Rongcheng and Sun, Yan and Zhang, Jamey and
               Xiong, Guanglei and Ji, Ivan and Chen, Xianjie and Bu, Shujian},
  booktitle = {KDD Cup 2026 Tencent UniRec Challenge Workshop},
  year      = {2026}
}

License

Released under the MIT License — free to use, modify, and distribute, including for commercial purposes.

About

kdd cup 2026 TAAC runner-up solution in industrial track

Resources

Stars

16 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages