nextrec 0.3.1__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nextrec-0.3.1 → nextrec-0.3.3}/PKG-INFO +10 -4
- {nextrec-0.3.1 → nextrec-0.3.3}/README.md +9 -3
- {nextrec-0.3.1 → nextrec-0.3.3}/README_zh.md +10 -4
- nextrec-0.3.3/asserts/Feature Configuration.png +0 -0
- nextrec-0.3.3/asserts/Model Parameters.png +0 -0
- nextrec-0.3.3/asserts/Training Configuration.png +0 -0
- nextrec-0.3.3/asserts/Training logs.png +0 -0
- nextrec-0.3.3/asserts/nextrec_diagram_en.png +0 -0
- nextrec-0.3.3/asserts/nextrec_diagram_zh.png +0 -0
- nextrec-0.3.3/asserts/test data.png +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/conf.py +1 -1
- nextrec-0.3.3/nextrec/__version__.py +1 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/features.py +10 -23
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/layers.py +18 -61
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/loggers.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/metrics.py +55 -33
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/model.py +258 -394
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/data/__init__.py +2 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/data/data_utils.py +80 -4
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/data/dataloader.py +36 -57
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/data/preprocessor.py +5 -4
- nextrec-0.3.3/nextrec/models/generative/__init__.py +5 -0
- nextrec-0.3.3/nextrec/models/generative/hstu.py +399 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/match/dssm.py +2 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/match/dssm_v2.py +2 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/match/mind.py +2 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/match/sdm.py +2 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/match/youtube_dnn.py +2 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/multi_task/esmm.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/multi_task/mmoe.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/multi_task/ple.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/multi_task/poso.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/multi_task/share_bottom.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/afm.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/autoint.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/dcn.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/deepfm.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/dien.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/din.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/fibinet.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/fm.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/masknet.py +2 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/pnn.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/widedeep.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/xdeepfm.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/utils/__init__.py +2 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/utils/common.py +21 -2
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/utils/optimizer.py +7 -3
- {nextrec-0.3.1 → nextrec-0.3.3}/pyproject.toml +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/test/test_losses.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/test/test_multitask_models.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/example_match_dssm.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/example_multitask.py +1 -1
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/example_ranking_din.py +9 -11
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/movielen_match_dssm.py +3 -2
- nextrec-0.3.3/tutorials/run_all_tutorials.py +59 -0
- nextrec-0.3.1/nextrec/__version__.py +0 -1
- nextrec-0.3.1/nextrec/models/generative/hstu.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/.github/workflows/publish.yml +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/.github/workflows/tests.yml +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/.gitignore +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/.readthedocs.yaml +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/CODE_OF_CONDUCT.md +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/CONTRIBUTING.md +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/LICENSE +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/MANIFEST.in +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/asserts/logo.png +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/asserts/mmoe_tutorial.png +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/dataset/ctcvr_task.csv +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/dataset/match_task.csv +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/dataset/movielens_100k.csv +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/dataset/multitask_task.csv +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/dataset/ranking_task.csv +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/en/Getting started guide.md +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/Makefile +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/index.md +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/make.bat +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/modules.rst +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/nextrec.basic.rst +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/nextrec.data.rst +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/nextrec.loss.rst +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/nextrec.rst +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/nextrec.utils.rst +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/rtd/requirements.txt +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/docs/zh//345/277/253/351/200/237/344/270/212/346/211/213.md" +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/__init__.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/__init__.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/activation.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/callback.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/basic/session.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/loss/__init__.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/loss/listwise.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/loss/loss_utils.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/loss/pairwise.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/loss/pointwise.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/generative/tiger.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/match/__init__.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/__init__.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/models/ranking/dcn_v2.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/utils/embedding.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/nextrec/utils/initializer.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/pytest.ini +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/requirements.txt +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/__init__.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/conftest.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/run_tests.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/test_layers.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/test_match_models.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/test_preprocessor.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/test_ranking_models.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test/test_utils.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/test_requirements.txt +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/movielen_ranking_deepfm.py +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/notebooks/en/Hands on dataprocessor.ipynb +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/notebooks/en/Hands on nextrec.ipynb +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/notebooks/zh/Hands on dataprocessor.ipynb +0 -0
- {nextrec-0.3.1 → nextrec-0.3.3}/tutorials/notebooks/zh/Hands on nextrec.ipynb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nextrec
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A comprehensive recommendation library with match, ranking, and multi-task learning models
|
|
5
5
|
Project-URL: Homepage, https://github.com/zerolovesea/NextRec
|
|
6
6
|
Project-URL: Repository, https://github.com/zerolovesea/NextRec
|
|
@@ -63,7 +63,7 @@ Description-Content-Type: text/markdown
|
|
|
63
63
|

|
|
64
64
|

|
|
65
65
|

|
|
66
|
-

|
|
67
67
|
|
|
68
68
|
English | [中文文档](README_zh.md)
|
|
69
69
|
|
|
@@ -75,13 +75,19 @@ English | [中文文档](README_zh.md)
|
|
|
75
75
|
|
|
76
76
|
NextRec is a modern recommendation framework built on PyTorch, delivering a unified experience for modeling, training, and evaluation. It follows a modular design with rich model implementations, data-processing utilities, and engineering-ready training components. NextRec focuses on large-scale industrial recall scenarios on Spark clusters, training on massive offline parquet features.
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
## Why NextRec
|
|
79
79
|
|
|
80
80
|
- **Unified feature engineering & data pipeline**: Dense/Sparse/Sequence feature definitions, persistent DataProcessor, and batch-optimized RecDataLoader, matching offline feature training/inference in industrial big-data settings.
|
|
81
81
|
- **Multi-scenario coverage**: Ranking (CTR/CVR), retrieval, multi-task learning, and more marketing/rec models, with a continuously expanding model zoo.
|
|
82
82
|
- **Developer-friendly experience**: Stream processing/training/inference for csv/parquet/pathlike data, plus GPU/MPS acceleration and visualization support.
|
|
83
83
|
- **Efficient training & evaluation**: Standardized engine with optimizers, LR schedulers, early stopping, checkpoints, and detailed logging out of the box.
|
|
84
84
|
|
|
85
|
+
## Architecture
|
|
86
|
+
|
|
87
|
+
NextRec adopts a modular and low-coupling engineering design, enabling full-pipeline reusability and scalability across data processing → model construction → training & evaluation → inference & deployment. Its core components include: a Feature-Spec-driven Embedding architecture, the BaseModel abstraction, a set of independent reusable Layers, a unified DataLoader for both training and inference, and a ready-to-use Model Zoo.
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
85
91
|
> The project borrows ideas from excellent open-source rec libraries. Early layers referenced [torch-rechub](https://github.com/datawhalechina/torch-rechub) but have been replaced with in-house implementations. torch-rechub remains mature in architecture and models; the author contributed a bit there—feel free to check it out.
|
|
86
92
|
|
|
87
93
|
---
|
|
@@ -104,7 +110,7 @@ To dive deeper, Jupyter notebooks are available:
|
|
|
104
110
|
- [Hands on the NextRec framework](/tutorials/notebooks/en/Hands%20on%20nextrec.ipynb)
|
|
105
111
|
- [Using the data processor for preprocessing](/tutorials/notebooks/en/Hands%20on%20dataprocessor.ipynb)
|
|
106
112
|
|
|
107
|
-
> Current version [0.3.
|
|
113
|
+
> Current version [0.3.3]: the matching module is not fully polished yet and may have compatibility issues or unexpected errors. Please raise an issue if you run into problems.
|
|
108
114
|
|
|
109
115
|
## 5-Minute Quick Start
|
|
110
116
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|

|
|
8
8
|

|
|
9
9
|

|
|
10
|
-

|
|
11
11
|
|
|
12
12
|
English | [中文文档](README_zh.md)
|
|
13
13
|
|
|
@@ -19,13 +19,19 @@ English | [中文文档](README_zh.md)
|
|
|
19
19
|
|
|
20
20
|
NextRec is a modern recommendation framework built on PyTorch, delivering a unified experience for modeling, training, and evaluation. It follows a modular design with rich model implementations, data-processing utilities, and engineering-ready training components. NextRec focuses on large-scale industrial recall scenarios on Spark clusters, training on massive offline parquet features.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Why NextRec
|
|
23
23
|
|
|
24
24
|
- **Unified feature engineering & data pipeline**: Dense/Sparse/Sequence feature definitions, persistent DataProcessor, and batch-optimized RecDataLoader, matching offline feature training/inference in industrial big-data settings.
|
|
25
25
|
- **Multi-scenario coverage**: Ranking (CTR/CVR), retrieval, multi-task learning, and more marketing/rec models, with a continuously expanding model zoo.
|
|
26
26
|
- **Developer-friendly experience**: Stream processing/training/inference for csv/parquet/pathlike data, plus GPU/MPS acceleration and visualization support.
|
|
27
27
|
- **Efficient training & evaluation**: Standardized engine with optimizers, LR schedulers, early stopping, checkpoints, and detailed logging out of the box.
|
|
28
28
|
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
NextRec adopts a modular and low-coupling engineering design, enabling full-pipeline reusability and scalability across data processing → model construction → training & evaluation → inference & deployment. Its core components include: a Feature-Spec-driven Embedding architecture, the BaseModel abstraction, a set of independent reusable Layers, a unified DataLoader for both training and inference, and a ready-to-use Model Zoo.
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
29
35
|
> The project borrows ideas from excellent open-source rec libraries. Early layers referenced [torch-rechub](https://github.com/datawhalechina/torch-rechub) but have been replaced with in-house implementations. torch-rechub remains mature in architecture and models; the author contributed a bit there—feel free to check it out.
|
|
30
36
|
|
|
31
37
|
---
|
|
@@ -48,7 +54,7 @@ To dive deeper, Jupyter notebooks are available:
|
|
|
48
54
|
- [Hands on the NextRec framework](/tutorials/notebooks/en/Hands%20on%20nextrec.ipynb)
|
|
49
55
|
- [Using the data processor for preprocessing](/tutorials/notebooks/en/Hands%20on%20dataprocessor.ipynb)
|
|
50
56
|
|
|
51
|
-
> Current version [0.3.
|
|
57
|
+
> Current version [0.3.3]: the matching module is not fully polished yet and may have compatibility issues or unexpected errors. Please raise an issue if you run into problems.
|
|
52
58
|
|
|
53
59
|
## 5-Minute Quick Start
|
|
54
60
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|

|
|
8
8
|

|
|
9
9
|

|
|
10
|
-

|
|
11
11
|
|
|
12
12
|
[English Version](README.md) | 中文文档
|
|
13
13
|
|
|
@@ -19,14 +19,20 @@
|
|
|
19
19
|
|
|
20
20
|
NextRec 是一个基于 PyTorch 构建的现代推荐系统框架,为研究人员与工程团队提供统一的建模、训练与评估体验。框架采用模块化设计,内置丰富的模型实现、数据处理工具和工程化训练组件,可快速覆盖多种推荐场景,主要面向Spark集群下,基于大数据量parquet离线特征训练的工业推荐召回算法场景。
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Why NextRec
|
|
23
23
|
|
|
24
24
|
- **统一的特征工程与数据流水线**:NextRec框架提供了 Dense/Sparse/Sequence 特征定义、可持久化的 DataProcessor、批处理优化的 RecDataLoader,符合工业大数据场景下,基于离线特征的模型训练推理流程。
|
|
25
25
|
- **多场景推荐能力**:同时覆盖排序(CTR/CVR)、召回、多任务学习等推荐/营销模型,并且持续扩充模型库中。
|
|
26
26
|
- **友好的工程体验**:NextRec框架支持各种格式数据(csv/parquet/pathlike)的流式处理/训练/推理,并支持 GPU/MPS 加速与可视化监控。
|
|
27
27
|
- **高效训练与评估**:NextRec框架的标准化训练引擎内置多种优化器、学习率调度、早停、模型检查点与详细的日志管理,开箱即用。
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
## 架构
|
|
30
|
+
|
|
31
|
+
NextRec采用模块化、低耦合的工程设计,使得推荐系统从数据处理 → 模型构建 → 训练评估 → 推理部署 全链路都具备复用性与可扩展性。其中的核心组件包括:Feature Spec驱动的Embedding架构;模型基类BaseModel;独立Layer模块;支持训练和推理的统一的DataLoader;开箱即用的模型库。
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
> 项目的架构借鉴了一些优秀的开源推荐算法库,其中最初版的layer层借鉴了DataWhaleChina社区的[torch-rechub](https://github.com/datawhalechina/torch-rechub),现已替换为了自有实现。torch-rechub在开发架构和模型实现上相对成熟,本人也参与了其中一小部分的维护,欢迎感兴趣的开发者前往了解。
|
|
30
36
|
|
|
31
37
|
---
|
|
32
38
|
|
|
@@ -48,7 +54,7 @@ NextRec 是一个基于 PyTorch 构建的现代推荐系统框架,为研究人
|
|
|
48
54
|
- [如何上手NextRec框架](/tutorials/notebooks/zh/Hands%20on%20nextrec.ipynb)
|
|
49
55
|
- [如何使用数据处理器进行数据预处理](/tutorials/notebooks/zh/Hands%20on%20dataprocessor.ipynb)
|
|
50
56
|
|
|
51
|
-
> 当前版本[0.3.
|
|
57
|
+
> 当前版本[0.3.3],召回模型模块尚不完善,可能存在一些兼容性问题或意外报错,如果遇到问题,欢迎开发者在Issue区提出问题。
|
|
52
58
|
|
|
53
59
|
## 5分钟快速上手
|
|
54
60
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.3"
|
|
@@ -2,19 +2,16 @@
|
|
|
2
2
|
Feature definitions
|
|
3
3
|
|
|
4
4
|
Date: create on 27/10/2025
|
|
5
|
-
Checkpoint: edit on
|
|
5
|
+
Checkpoint: edit on 02/12/2025
|
|
6
6
|
Author: Yang Zhou, zyaztec@gmail.com
|
|
7
7
|
"""
|
|
8
8
|
import torch
|
|
9
9
|
from nextrec.utils.embedding import get_auto_embedding_dim
|
|
10
|
+
from nextrec.utils.common import normalize_to_list
|
|
10
11
|
|
|
11
12
|
class BaseFeature(object):
|
|
12
13
|
def __repr__(self):
|
|
13
|
-
params = {
|
|
14
|
-
k: v
|
|
15
|
-
for k, v in self.__dict__.items()
|
|
16
|
-
if not k.startswith("_")
|
|
17
|
-
}
|
|
14
|
+
params = {k: v for k, v in self.__dict__.items() if not k.startswith("_") }
|
|
18
15
|
param_str = ", ".join(f"{k}={v!r}" for k, v in params.items())
|
|
19
16
|
return f"{self.__class__.__name__}({param_str})"
|
|
20
17
|
|
|
@@ -93,11 +90,8 @@ class DenseFeature(BaseFeature):
|
|
|
93
90
|
else:
|
|
94
91
|
self.use_embedding = use_embedding # user decides for dim <= 1
|
|
95
92
|
|
|
96
|
-
class
|
|
97
|
-
|
|
98
|
-
Mixin that normalizes dense/sparse/sequence feature lists and target/id columns.
|
|
99
|
-
"""
|
|
100
|
-
def _set_feature_config(
|
|
93
|
+
class FeatureSet:
|
|
94
|
+
def set_all_features(
|
|
101
95
|
self,
|
|
102
96
|
dense_features: list[DenseFeature] | None = None,
|
|
103
97
|
sparse_features: list[SparseFeature] | None = None,
|
|
@@ -111,21 +105,14 @@ class FeatureSpecMixin:
|
|
|
111
105
|
|
|
112
106
|
self.all_features = self.dense_features + self.sparse_features + self.sequence_features
|
|
113
107
|
self.feature_names = [feat.name for feat in self.all_features]
|
|
114
|
-
self.target_columns =
|
|
115
|
-
self.id_columns =
|
|
108
|
+
self.target_columns = normalize_to_list(target)
|
|
109
|
+
self.id_columns = normalize_to_list(id_columns)
|
|
116
110
|
|
|
117
|
-
def
|
|
111
|
+
def set_target_id(
|
|
118
112
|
self,
|
|
119
113
|
target: str | list[str] | None = None,
|
|
120
114
|
id_columns: str | list[str] | None = None,
|
|
121
115
|
) -> None:
|
|
122
|
-
self.target_columns =
|
|
123
|
-
self.id_columns =
|
|
116
|
+
self.target_columns = normalize_to_list(target)
|
|
117
|
+
self.id_columns = normalize_to_list(id_columns)
|
|
124
118
|
|
|
125
|
-
@staticmethod
|
|
126
|
-
def _normalize_to_list(value: str | list[str] | None) -> list[str]:
|
|
127
|
-
if value is None:
|
|
128
|
-
return []
|
|
129
|
-
if isinstance(value, str):
|
|
130
|
-
return [value]
|
|
131
|
-
return list(value)
|
|
@@ -18,23 +18,6 @@ from nextrec.basic.features import DenseFeature, SequenceFeature, SparseFeature
|
|
|
18
18
|
from nextrec.utils.initializer import get_initializer
|
|
19
19
|
from nextrec.basic.activation import activation_layer
|
|
20
20
|
|
|
21
|
-
__all__ = [
|
|
22
|
-
"PredictionLayer",
|
|
23
|
-
"EmbeddingLayer",
|
|
24
|
-
"InputMask",
|
|
25
|
-
"LR",
|
|
26
|
-
"ConcatPooling",
|
|
27
|
-
"AveragePooling",
|
|
28
|
-
"SumPooling",
|
|
29
|
-
"MLP",
|
|
30
|
-
"FM",
|
|
31
|
-
"CrossLayer",
|
|
32
|
-
"SENETLayer",
|
|
33
|
-
"BiLinearInteractionLayer",
|
|
34
|
-
"MultiHeadSelfAttention",
|
|
35
|
-
"AttentionPoolingLayer",
|
|
36
|
-
]
|
|
37
|
-
|
|
38
21
|
class PredictionLayer(nn.Module):
|
|
39
22
|
def __init__(
|
|
40
23
|
self,
|
|
@@ -44,12 +27,10 @@ class PredictionLayer(nn.Module):
|
|
|
44
27
|
return_logits: bool = False,
|
|
45
28
|
):
|
|
46
29
|
super().__init__()
|
|
47
|
-
if isinstance(task_type, str)
|
|
48
|
-
self.task_types = [task_type]
|
|
49
|
-
else:
|
|
50
|
-
self.task_types = list(task_type)
|
|
30
|
+
self.task_types = [task_type] if isinstance(task_type, str) else list(task_type)
|
|
51
31
|
if len(self.task_types) == 0:
|
|
52
32
|
raise ValueError("At least one task_type must be specified.")
|
|
33
|
+
|
|
53
34
|
if task_dims is None:
|
|
54
35
|
dims = [1] * len(self.task_types)
|
|
55
36
|
elif isinstance(task_dims, int):
|
|
@@ -64,7 +45,7 @@ class PredictionLayer(nn.Module):
|
|
|
64
45
|
self.total_dim = sum(self.task_dims)
|
|
65
46
|
self.return_logits = return_logits
|
|
66
47
|
|
|
67
|
-
#
|
|
48
|
+
# slice offsets per task
|
|
68
49
|
start = 0
|
|
69
50
|
self._task_slices: list[tuple[int, int]] = []
|
|
70
51
|
for dim in self.task_dims:
|
|
@@ -85,27 +66,25 @@ class PredictionLayer(nn.Module):
|
|
|
85
66
|
logits = x if self.bias is None else x + self.bias
|
|
86
67
|
outputs = []
|
|
87
68
|
for task_type, (start, end) in zip(self.task_types, self._task_slices):
|
|
88
|
-
task_logits = logits[..., start:end] #
|
|
69
|
+
task_logits = logits[..., start:end] # logits for the current task
|
|
89
70
|
if self.return_logits:
|
|
90
71
|
outputs.append(task_logits)
|
|
91
72
|
continue
|
|
92
|
-
|
|
73
|
+
task = task_type.lower()
|
|
74
|
+
if task == 'binary':
|
|
75
|
+
activation = torch.sigmoid
|
|
76
|
+
elif task == 'regression':
|
|
77
|
+
activation = lambda x: x
|
|
78
|
+
elif task == 'multiclass':
|
|
79
|
+
activation = lambda x: torch.softmax(x, dim=-1)
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(f"[PredictionLayer Error]: Unsupported task_type '{task_type}'.")
|
|
93
82
|
outputs.append(activation(task_logits))
|
|
94
83
|
result = torch.cat(outputs, dim=-1)
|
|
95
84
|
if result.shape[-1] == 1:
|
|
96
85
|
result = result.squeeze(-1)
|
|
97
86
|
return result
|
|
98
87
|
|
|
99
|
-
def _get_activation(self, task_type: str):
|
|
100
|
-
task = task_type.lower()
|
|
101
|
-
if task == 'binary':
|
|
102
|
-
return torch.sigmoid
|
|
103
|
-
if task == 'regression':
|
|
104
|
-
return lambda x: x
|
|
105
|
-
if task == 'multiclass':
|
|
106
|
-
return lambda x: torch.softmax(x, dim=-1)
|
|
107
|
-
raise ValueError(f"[PredictionLayer Error]: Unsupported task_type '{task_type}'.")
|
|
108
|
-
|
|
109
88
|
class EmbeddingLayer(nn.Module):
|
|
110
89
|
def __init__(self, features: list):
|
|
111
90
|
super().__init__()
|
|
@@ -145,7 +124,7 @@ class EmbeddingLayer(nn.Module):
|
|
|
145
124
|
self.dense_input_dims[feature.name] = in_dim
|
|
146
125
|
else:
|
|
147
126
|
raise TypeError(f"[EmbeddingLayer Error]: Unsupported feature type: {type(feature)}")
|
|
148
|
-
self.output_dim = self.
|
|
127
|
+
self.output_dim = self.compute_output_dim()
|
|
149
128
|
|
|
150
129
|
def forward(
|
|
151
130
|
self,
|
|
@@ -181,7 +160,7 @@ class EmbeddingLayer(nn.Module):
|
|
|
181
160
|
sparse_embeds.append(pooling_layer(seq_emb, feature_mask).unsqueeze(1))
|
|
182
161
|
|
|
183
162
|
elif isinstance(feature, DenseFeature):
|
|
184
|
-
dense_embeds.append(self.
|
|
163
|
+
dense_embeds.append(self.project_dense(feature, x))
|
|
185
164
|
|
|
186
165
|
if squeeze_dim:
|
|
187
166
|
flattened_sparse = [emb.flatten(start_dim=1) for emb in sparse_embeds]
|
|
@@ -212,7 +191,7 @@ class EmbeddingLayer(nn.Module):
|
|
|
212
191
|
raise ValueError("[EmbeddingLayer Error]: squeeze_dim=False requires at least one sparse/sequence feature or dense features with identical projected dimensions.")
|
|
213
192
|
return torch.cat(output_embeddings, dim=1)
|
|
214
193
|
|
|
215
|
-
def
|
|
194
|
+
def project_dense(self, feature: DenseFeature, x: dict[str, torch.Tensor]) -> torch.Tensor:
|
|
216
195
|
if feature.name not in x:
|
|
217
196
|
raise KeyError(f"[EmbeddingLayer Error]:Dense feature '{feature.name}' is missing from input.")
|
|
218
197
|
value = x[feature.name].float()
|
|
@@ -228,11 +207,7 @@ class EmbeddingLayer(nn.Module):
|
|
|
228
207
|
dense_layer = self.dense_transforms[feature.name]
|
|
229
208
|
return dense_layer(value)
|
|
230
209
|
|
|
231
|
-
def
|
|
232
|
-
"""
|
|
233
|
-
Compute flattened embedding dimension for provided features or all tracked features.
|
|
234
|
-
Deduplicates by feature name to avoid double-counting shared embeddings.
|
|
235
|
-
"""
|
|
210
|
+
def compute_output_dim(self, features: list[DenseFeature | SequenceFeature | SparseFeature] | None = None) -> int:
|
|
236
211
|
candidates = list(features) if features is not None else self.features
|
|
237
212
|
unique_feats = OrderedDict((feat.name, feat) for feat in candidates) # type: ignore[assignment]
|
|
238
213
|
dim = 0
|
|
@@ -249,14 +224,13 @@ class EmbeddingLayer(nn.Module):
|
|
|
249
224
|
return dim
|
|
250
225
|
|
|
251
226
|
def get_input_dim(self, features: list[object] | None = None) -> int:
|
|
252
|
-
return self.
|
|
227
|
+
return self.compute_output_dim(features) # type: ignore[assignment]
|
|
253
228
|
|
|
254
229
|
@property
|
|
255
230
|
def input_dim(self) -> int:
|
|
256
231
|
return self.output_dim
|
|
257
232
|
|
|
258
233
|
class InputMask(nn.Module):
|
|
259
|
-
"""Utility module to build sequence masks for pooling layers."""
|
|
260
234
|
def __init__(self):
|
|
261
235
|
super().__init__()
|
|
262
236
|
|
|
@@ -271,7 +245,6 @@ class InputMask(nn.Module):
|
|
|
271
245
|
return mask.unsqueeze(1).float()
|
|
272
246
|
|
|
273
247
|
class LR(nn.Module):
|
|
274
|
-
"""Wide component from Wide&Deep (Cheng et al., 2016)."""
|
|
275
248
|
def __init__(
|
|
276
249
|
self,
|
|
277
250
|
input_dim: int,
|
|
@@ -287,7 +260,6 @@ class LR(nn.Module):
|
|
|
287
260
|
return self.fc(x)
|
|
288
261
|
|
|
289
262
|
class ConcatPooling(nn.Module):
|
|
290
|
-
"""Concatenates sequence embeddings along the temporal dimension."""
|
|
291
263
|
def __init__(self):
|
|
292
264
|
super().__init__()
|
|
293
265
|
|
|
@@ -295,7 +267,6 @@ class ConcatPooling(nn.Module):
|
|
|
295
267
|
return x.flatten(start_dim=1, end_dim=2)
|
|
296
268
|
|
|
297
269
|
class AveragePooling(nn.Module):
|
|
298
|
-
"""Mean pooling with optional padding mask."""
|
|
299
270
|
def __init__(self):
|
|
300
271
|
super().__init__()
|
|
301
272
|
|
|
@@ -308,7 +279,6 @@ class AveragePooling(nn.Module):
|
|
|
308
279
|
return sum_pooling_matrix / (non_padding_length.float() + 1e-16)
|
|
309
280
|
|
|
310
281
|
class SumPooling(nn.Module):
|
|
311
|
-
"""Sum pooling with optional padding mask."""
|
|
312
282
|
def __init__(self):
|
|
313
283
|
super().__init__()
|
|
314
284
|
|
|
@@ -319,7 +289,6 @@ class SumPooling(nn.Module):
|
|
|
319
289
|
return torch.bmm(mask, x).squeeze(1)
|
|
320
290
|
|
|
321
291
|
class MLP(nn.Module):
|
|
322
|
-
"""Stacked fully connected layers used in the deep component."""
|
|
323
292
|
def __init__(
|
|
324
293
|
self,
|
|
325
294
|
input_dim: int,
|
|
@@ -345,7 +314,6 @@ class MLP(nn.Module):
|
|
|
345
314
|
return self.mlp(x)
|
|
346
315
|
|
|
347
316
|
class FM(nn.Module):
|
|
348
|
-
"""Factorization Machine (Rendle, 2010) second-order interaction term."""
|
|
349
317
|
def __init__(self, reduce_sum: bool = True):
|
|
350
318
|
super().__init__()
|
|
351
319
|
self.reduce_sum = reduce_sum
|
|
@@ -359,7 +327,6 @@ class FM(nn.Module):
|
|
|
359
327
|
return 0.5 * ix
|
|
360
328
|
|
|
361
329
|
class CrossLayer(nn.Module):
|
|
362
|
-
"""Single cross layer used in DCN (Wang et al., 2017)."""
|
|
363
330
|
def __init__(self, input_dim: int):
|
|
364
331
|
super(CrossLayer, self).__init__()
|
|
365
332
|
self.w = torch.nn.Linear(input_dim, 1, bias=False)
|
|
@@ -370,7 +337,6 @@ class CrossLayer(nn.Module):
|
|
|
370
337
|
return x
|
|
371
338
|
|
|
372
339
|
class SENETLayer(nn.Module):
|
|
373
|
-
"""Squeeze-and-Excitation block adopted by FiBiNET (Huang et al., 2019)."""
|
|
374
340
|
def __init__(
|
|
375
341
|
self,
|
|
376
342
|
num_fields: int,
|
|
@@ -388,7 +354,6 @@ class SENETLayer(nn.Module):
|
|
|
388
354
|
return v
|
|
389
355
|
|
|
390
356
|
class BiLinearInteractionLayer(nn.Module):
|
|
391
|
-
"""Bilinear feature interaction from FiBiNET (Huang et al., 2019)."""
|
|
392
357
|
def __init__(
|
|
393
358
|
self,
|
|
394
359
|
input_dim: int,
|
|
@@ -416,7 +381,6 @@ class BiLinearInteractionLayer(nn.Module):
|
|
|
416
381
|
return torch.cat(bilinear_list, dim=1)
|
|
417
382
|
|
|
418
383
|
class MultiHeadSelfAttention(nn.Module):
|
|
419
|
-
"""Multi-head self-attention layer from AutoInt (Song et al., 2019)."""
|
|
420
384
|
def __init__(
|
|
421
385
|
self,
|
|
422
386
|
embedding_dim: int,
|
|
@@ -438,13 +402,6 @@ class MultiHeadSelfAttention(nn.Module):
|
|
|
438
402
|
self.dropout = nn.Dropout(dropout)
|
|
439
403
|
|
|
440
404
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
441
|
-
"""
|
|
442
|
-
Args:
|
|
443
|
-
x (torch.Tensor): Tensor of shape (batch_size, num_fields, embedding_dim)
|
|
444
|
-
|
|
445
|
-
Returns:
|
|
446
|
-
torch.Tensor: Output tensor of shape (batch_size, num_fields, embedding_dim)
|
|
447
|
-
"""
|
|
448
405
|
batch_size, num_fields, _ = x.shape
|
|
449
406
|
Q = self.W_Q(x) # [batch_size, num_fields, embedding_dim]
|
|
450
407
|
K = self.W_K(x)
|
|
@@ -106,7 +106,7 @@ def setup_logger(session_id: str | os.PathLike | None = None):
|
|
|
106
106
|
|
|
107
107
|
console_format = '%(message)s'
|
|
108
108
|
file_format = '%(asctime)s - %(levelname)s - %(message)s'
|
|
109
|
-
date_format = '%H:%M:%S'
|
|
109
|
+
date_format = '%Y-%m-%d %H:%M:%S'
|
|
110
110
|
|
|
111
111
|
logger = logging.getLogger()
|
|
112
112
|
logger.setLevel(logging.INFO)
|