nextrec 0.4.16__py3-none-any.whl → 0.4.18__py3-none-any.whl
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/__version__.py +1 -1
- nextrec/basic/heads.py +99 -0
- nextrec/basic/loggers.py +5 -5
- nextrec/basic/model.py +217 -88
- nextrec/cli.py +1 -1
- nextrec/data/dataloader.py +93 -95
- nextrec/data/preprocessor.py +108 -46
- nextrec/loss/grad_norm.py +13 -13
- nextrec/models/multi_task/esmm.py +10 -11
- nextrec/models/multi_task/mmoe.py +20 -19
- nextrec/models/multi_task/ple.py +35 -34
- nextrec/models/multi_task/poso.py +23 -21
- nextrec/models/multi_task/share_bottom.py +18 -17
- nextrec/models/ranking/afm.py +4 -3
- nextrec/models/ranking/autoint.py +4 -3
- nextrec/models/ranking/dcn.py +4 -3
- nextrec/models/ranking/dcn_v2.py +4 -3
- nextrec/models/ranking/deepfm.py +4 -3
- nextrec/models/ranking/dien.py +2 -2
- nextrec/models/ranking/din.py +2 -2
- nextrec/models/ranking/eulernet.py +4 -3
- nextrec/models/ranking/ffm.py +4 -3
- nextrec/models/ranking/fibinet.py +2 -2
- nextrec/models/ranking/fm.py +4 -3
- nextrec/models/ranking/lr.py +4 -3
- nextrec/models/ranking/masknet.py +4 -5
- nextrec/models/ranking/pnn.py +5 -4
- nextrec/models/ranking/widedeep.py +8 -8
- nextrec/models/ranking/xdeepfm.py +5 -4
- nextrec/utils/console.py +20 -6
- nextrec/utils/data.py +154 -32
- nextrec/utils/model.py +86 -1
- {nextrec-0.4.16.dist-info → nextrec-0.4.18.dist-info}/METADATA +5 -6
- {nextrec-0.4.16.dist-info → nextrec-0.4.18.dist-info}/RECORD +37 -36
- {nextrec-0.4.16.dist-info → nextrec-0.4.18.dist-info}/WHEEL +0 -0
- {nextrec-0.4.16.dist-info → nextrec-0.4.18.dist-info}/entry_points.txt +0 -0
- {nextrec-0.4.16.dist-info → nextrec-0.4.18.dist-info}/licenses/LICENSE +0 -0
nextrec/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.4.
|
|
1
|
+
__version__ = "0.4.18"
|
nextrec/basic/heads.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Task head implementations for NextRec models.
|
|
3
|
+
|
|
4
|
+
Date: create on 23/12/2025
|
|
5
|
+
Author: Yang Zhou, zyaztec@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Literal
|
|
11
|
+
|
|
12
|
+
import torch
|
|
13
|
+
import torch.nn as nn
|
|
14
|
+
import torch.nn.functional as F
|
|
15
|
+
|
|
16
|
+
from nextrec.basic.layers import PredictionLayer
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TaskHead(nn.Module):
|
|
20
|
+
"""
|
|
21
|
+
Unified task head for ranking/regression/multi-task outputs.
|
|
22
|
+
|
|
23
|
+
This wraps PredictionLayer so models can depend on a "Head" abstraction
|
|
24
|
+
without changing their existing forward signatures.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
task_type: str | list[str] = "binary",
|
|
30
|
+
task_dims: int | list[int] | None = None,
|
|
31
|
+
use_bias: bool = True,
|
|
32
|
+
return_logits: bool = False,
|
|
33
|
+
) -> None:
|
|
34
|
+
super().__init__()
|
|
35
|
+
self.prediction = PredictionLayer(
|
|
36
|
+
task_type=task_type,
|
|
37
|
+
task_dims=task_dims,
|
|
38
|
+
use_bias=use_bias,
|
|
39
|
+
return_logits=return_logits,
|
|
40
|
+
)
|
|
41
|
+
# Expose commonly used attributes for compatibility with PredictionLayer.
|
|
42
|
+
self.task_types = self.prediction.task_types
|
|
43
|
+
self.task_dims = self.prediction.task_dims
|
|
44
|
+
self.task_slices = self.prediction.task_slices
|
|
45
|
+
self.total_dim = self.prediction.total_dim
|
|
46
|
+
|
|
47
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
48
|
+
return self.prediction(x)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RetrievalHead(nn.Module):
|
|
52
|
+
"""
|
|
53
|
+
Retrieval head for two-tower models.
|
|
54
|
+
|
|
55
|
+
It computes similarity for pointwise training/inference, and returns
|
|
56
|
+
raw embeddings for in-batch negative sampling in pairwise/listwise modes.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
similarity_metric: Literal["dot", "cosine", "euclidean"] = "dot",
|
|
62
|
+
temperature: float = 1.0,
|
|
63
|
+
training_mode: Literal["pointwise", "pairwise", "listwise"] = "pointwise",
|
|
64
|
+
apply_sigmoid: bool = True,
|
|
65
|
+
) -> None:
|
|
66
|
+
super().__init__()
|
|
67
|
+
self.similarity_metric = similarity_metric
|
|
68
|
+
self.temperature = temperature
|
|
69
|
+
self.training_mode = training_mode
|
|
70
|
+
self.apply_sigmoid = apply_sigmoid
|
|
71
|
+
|
|
72
|
+
def forward(
|
|
73
|
+
self,
|
|
74
|
+
user_emb: torch.Tensor,
|
|
75
|
+
item_emb: torch.Tensor,
|
|
76
|
+
similarity_fn=None,
|
|
77
|
+
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
|
|
78
|
+
if self.training and self.training_mode in {"pairwise", "listwise"}:
|
|
79
|
+
return user_emb, item_emb
|
|
80
|
+
|
|
81
|
+
if similarity_fn is not None:
|
|
82
|
+
similarity = similarity_fn(user_emb, item_emb)
|
|
83
|
+
else:
|
|
84
|
+
if user_emb.dim() == 2 and item_emb.dim() == 3:
|
|
85
|
+
user_emb = user_emb.unsqueeze(1)
|
|
86
|
+
|
|
87
|
+
if self.similarity_metric == "dot":
|
|
88
|
+
similarity = torch.sum(user_emb * item_emb, dim=-1)
|
|
89
|
+
elif self.similarity_metric == "cosine":
|
|
90
|
+
similarity = F.cosine_similarity(user_emb, item_emb, dim=-1)
|
|
91
|
+
elif self.similarity_metric == "euclidean":
|
|
92
|
+
similarity = -torch.sum((user_emb - item_emb) ** 2, dim=-1)
|
|
93
|
+
else:
|
|
94
|
+
raise ValueError(f"Unknown similarity metric: {self.similarity_metric}")
|
|
95
|
+
|
|
96
|
+
similarity = similarity / self.temperature
|
|
97
|
+
if self.training_mode == "pointwise" and self.apply_sigmoid:
|
|
98
|
+
return torch.sigmoid(similarity)
|
|
99
|
+
return similarity
|
nextrec/basic/loggers.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
NextRec Basic Loggers
|
|
3
3
|
|
|
4
4
|
Date: create on 27/10/2025
|
|
5
|
-
Checkpoint: edit on
|
|
5
|
+
Checkpoint: edit on 24/12/2025
|
|
6
6
|
Author: Yang Zhou, zyaztec@gmail.com
|
|
7
7
|
"""
|
|
8
8
|
|
|
@@ -148,18 +148,18 @@ class TrainingLogger:
|
|
|
148
148
|
def __init__(
|
|
149
149
|
self,
|
|
150
150
|
session: Session,
|
|
151
|
-
|
|
151
|
+
use_tensorboard: bool,
|
|
152
152
|
log_name: str = "training_metrics.jsonl",
|
|
153
153
|
) -> None:
|
|
154
154
|
self.session = session
|
|
155
|
-
self.
|
|
155
|
+
self.use_tensorboard = use_tensorboard
|
|
156
156
|
self.log_path = session.metrics_dir / log_name
|
|
157
157
|
self.log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
158
158
|
|
|
159
159
|
self.tb_writer = None
|
|
160
160
|
self.tb_dir = None
|
|
161
161
|
|
|
162
|
-
if self.
|
|
162
|
+
if self.use_tensorboard:
|
|
163
163
|
self._init_tensorboard()
|
|
164
164
|
|
|
165
165
|
def _init_tensorboard(self) -> None:
|
|
@@ -169,7 +169,7 @@ class TrainingLogger:
|
|
|
169
169
|
logging.warning(
|
|
170
170
|
"[TrainingLogger] tensorboard not installed, disable tensorboard logging."
|
|
171
171
|
)
|
|
172
|
-
self.
|
|
172
|
+
self.use_tensorboard = False
|
|
173
173
|
return
|
|
174
174
|
tb_dir = self.session.logs_dir / "tensorboard"
|
|
175
175
|
tb_dir.mkdir(parents=True, exist_ok=True)
|