nextrec 0.4.22__py3-none-any.whl → 0.4.23__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/metrics.py +1 -2
- nextrec/basic/model.py +68 -73
- nextrec/basic/summary.py +36 -2
- nextrec/data/preprocessor.py +137 -5
- nextrec/loss/listwise.py +19 -6
- nextrec/loss/pairwise.py +6 -4
- nextrec/loss/pointwise.py +8 -6
- nextrec/models/multi_task/esmm.py +3 -26
- nextrec/models/multi_task/mmoe.py +2 -24
- nextrec/models/multi_task/ple.py +13 -35
- nextrec/models/multi_task/poso.py +4 -28
- nextrec/models/multi_task/share_bottom.py +1 -24
- nextrec/models/ranking/afm.py +3 -27
- nextrec/models/ranking/autoint.py +5 -38
- nextrec/models/ranking/dcn.py +1 -26
- nextrec/models/ranking/dcn_v2.py +5 -33
- nextrec/models/ranking/deepfm.py +2 -29
- nextrec/models/ranking/dien.py +2 -28
- nextrec/models/ranking/din.py +2 -27
- nextrec/models/ranking/eulernet.py +3 -30
- nextrec/models/ranking/ffm.py +0 -26
- nextrec/models/ranking/fibinet.py +8 -32
- nextrec/models/ranking/fm.py +0 -29
- nextrec/models/ranking/lr.py +0 -30
- nextrec/models/ranking/masknet.py +4 -30
- nextrec/models/ranking/pnn.py +4 -28
- nextrec/models/ranking/widedeep.py +0 -32
- nextrec/models/ranking/xdeepfm.py +0 -30
- nextrec/models/retrieval/dssm.py +0 -24
- nextrec/models/retrieval/dssm_v2.py +0 -24
- nextrec/models/retrieval/mind.py +0 -20
- nextrec/models/retrieval/sdm.py +0 -20
- nextrec/models/retrieval/youtube_dnn.py +0 -21
- nextrec/models/sequential/hstu.py +0 -18
- nextrec/utils/model.py +79 -1
- nextrec/utils/types.py +35 -0
- {nextrec-0.4.22.dist-info → nextrec-0.4.23.dist-info}/METADATA +7 -5
- nextrec-0.4.23.dist-info/RECORD +81 -0
- nextrec-0.4.22.dist-info/RECORD +0 -81
- {nextrec-0.4.22.dist-info → nextrec-0.4.23.dist-info}/WHEEL +0 -0
- {nextrec-0.4.22.dist-info → nextrec-0.4.23.dist-info}/entry_points.txt +0 -0
- {nextrec-0.4.22.dist-info → nextrec-0.4.23.dist-info}/licenses/LICENSE +0 -0
|
@@ -56,7 +56,7 @@ embedding 表示生成一个个性化的 “mask” 向量,通过逐元素的
|
|
|
56
56
|
import torch
|
|
57
57
|
import torch.nn as nn
|
|
58
58
|
import torch.nn.functional as F
|
|
59
|
-
|
|
59
|
+
from typing import Literal
|
|
60
60
|
from nextrec.basic.features import DenseFeature, SequenceFeature, SparseFeature
|
|
61
61
|
from nextrec.basic.layers import MLP, EmbeddingLayer
|
|
62
62
|
from nextrec.basic.heads import TaskHead
|
|
@@ -167,48 +167,28 @@ class MaskNet(BaseModel):
|
|
|
167
167
|
dense_features: list[DenseFeature] | None = None,
|
|
168
168
|
sparse_features: list[SparseFeature] | None = None,
|
|
169
169
|
sequence_features: list[SequenceFeature] | None = None,
|
|
170
|
-
architecture:
|
|
170
|
+
architecture: Literal[
|
|
171
|
+
"serial", "parallel"
|
|
172
|
+
] = "parallel", # "serial" or "parallel"
|
|
171
173
|
num_blocks: int = 3,
|
|
172
174
|
mask_hidden_dim: int = 64,
|
|
173
175
|
block_hidden_dim: int = 256,
|
|
174
176
|
block_dropout: float = 0.0,
|
|
175
177
|
mlp_params: dict | None = None,
|
|
176
|
-
target: list[str] | str | None = None,
|
|
177
|
-
task: str | list[str] | None = None,
|
|
178
|
-
optimizer: str = "adam",
|
|
179
|
-
optimizer_params: dict | None = None,
|
|
180
|
-
loss: str | nn.Module | None = "bce",
|
|
181
|
-
loss_params: dict | list[dict] | None = None,
|
|
182
|
-
embedding_l1_reg=0.0,
|
|
183
|
-
dense_l1_reg=0.0,
|
|
184
|
-
embedding_l2_reg=0.0,
|
|
185
|
-
dense_l2_reg=0.0,
|
|
186
178
|
**kwargs,
|
|
187
179
|
):
|
|
188
180
|
dense_features = dense_features or []
|
|
189
181
|
sparse_features = sparse_features or []
|
|
190
182
|
sequence_features = sequence_features or []
|
|
191
|
-
target = target or []
|
|
192
183
|
mlp_params = mlp_params or {}
|
|
193
|
-
optimizer_params = optimizer_params or {}
|
|
194
184
|
|
|
195
185
|
super().__init__(
|
|
196
186
|
dense_features=dense_features,
|
|
197
187
|
sparse_features=sparse_features,
|
|
198
188
|
sequence_features=sequence_features,
|
|
199
|
-
target=target,
|
|
200
|
-
task=task or self.default_task,
|
|
201
|
-
embedding_l1_reg=embedding_l1_reg,
|
|
202
|
-
dense_l1_reg=dense_l1_reg,
|
|
203
|
-
embedding_l2_reg=embedding_l2_reg,
|
|
204
|
-
dense_l2_reg=dense_l2_reg,
|
|
205
189
|
**kwargs,
|
|
206
190
|
)
|
|
207
191
|
|
|
208
|
-
if loss is None:
|
|
209
|
-
loss = "bce"
|
|
210
|
-
self.loss = loss
|
|
211
|
-
|
|
212
192
|
self.dense_features = dense_features
|
|
213
193
|
self.sparse_features = sparse_features
|
|
214
194
|
self.sequence_features = sequence_features
|
|
@@ -293,12 +273,6 @@ class MaskNet(BaseModel):
|
|
|
293
273
|
self.register_regularization_weights(
|
|
294
274
|
embedding_attr="embedding", include_modules=["mask_blocks", "final_mlp"]
|
|
295
275
|
)
|
|
296
|
-
self.compile(
|
|
297
|
-
optimizer=optimizer,
|
|
298
|
-
optimizer_params=optimizer_params,
|
|
299
|
-
loss=loss,
|
|
300
|
-
loss_params=loss_params,
|
|
301
|
-
)
|
|
302
276
|
|
|
303
277
|
def forward(self, x: dict[str, torch.Tensor]) -> torch.Tensor:
|
|
304
278
|
field_emb = self.embedding(x=x, features=self.mask_features, squeeze_dim=False)
|
nextrec/models/ranking/pnn.py
CHANGED
|
@@ -36,7 +36,7 @@ PNN 是一种 CTR 预估模型,通过将线性信号与乘积信号结合,
|
|
|
36
36
|
|
|
37
37
|
import torch
|
|
38
38
|
import torch.nn as nn
|
|
39
|
-
|
|
39
|
+
from typing import Literal
|
|
40
40
|
from nextrec.basic.features import DenseFeature, SequenceFeature, SparseFeature
|
|
41
41
|
from nextrec.basic.layers import MLP, EmbeddingLayer
|
|
42
42
|
from nextrec.basic.heads import TaskHead
|
|
@@ -59,18 +59,10 @@ class PNN(BaseModel):
|
|
|
59
59
|
sparse_features: list[SparseFeature] | None = None,
|
|
60
60
|
sequence_features: list[SequenceFeature] | None = None,
|
|
61
61
|
mlp_params: dict | None = None,
|
|
62
|
-
product_type:
|
|
62
|
+
product_type: Literal[
|
|
63
|
+
"inner", "outer", "both"
|
|
64
|
+
] = "inner", # "inner" (IPNN), "outer" (OPNN), "both" (PNN*)
|
|
63
65
|
outer_product_dim: int | None = None,
|
|
64
|
-
target: list[str] | str | None = None,
|
|
65
|
-
task: str | list[str] | None = None,
|
|
66
|
-
optimizer: str = "adam",
|
|
67
|
-
optimizer_params: dict | None = None,
|
|
68
|
-
loss: str | nn.Module | None = "bce",
|
|
69
|
-
loss_params: dict | list[dict] | None = None,
|
|
70
|
-
embedding_l1_reg=0.0,
|
|
71
|
-
dense_l1_reg=0.0,
|
|
72
|
-
embedding_l2_reg=0.0,
|
|
73
|
-
dense_l2_reg=0.0,
|
|
74
66
|
**kwargs,
|
|
75
67
|
):
|
|
76
68
|
|
|
@@ -80,20 +72,11 @@ class PNN(BaseModel):
|
|
|
80
72
|
mlp_params = mlp_params or {}
|
|
81
73
|
if outer_product_dim is not None and outer_product_dim <= 0:
|
|
82
74
|
raise ValueError("outer_product_dim must be a positive integer.")
|
|
83
|
-
optimizer_params = optimizer_params or {}
|
|
84
|
-
if loss is None:
|
|
85
|
-
loss = "bce"
|
|
86
75
|
|
|
87
76
|
super(PNN, self).__init__(
|
|
88
77
|
dense_features=dense_features,
|
|
89
78
|
sparse_features=sparse_features,
|
|
90
79
|
sequence_features=sequence_features,
|
|
91
|
-
target=target,
|
|
92
|
-
task=task or self.default_task,
|
|
93
|
-
embedding_l1_reg=embedding_l1_reg,
|
|
94
|
-
dense_l1_reg=dense_l1_reg,
|
|
95
|
-
embedding_l2_reg=embedding_l2_reg,
|
|
96
|
-
dense_l2_reg=dense_l2_reg,
|
|
97
80
|
**kwargs,
|
|
98
81
|
)
|
|
99
82
|
|
|
@@ -144,13 +127,6 @@ class PNN(BaseModel):
|
|
|
144
127
|
embedding_attr="embedding", include_modules=modules
|
|
145
128
|
)
|
|
146
129
|
|
|
147
|
-
self.compile(
|
|
148
|
-
optimizer=optimizer,
|
|
149
|
-
optimizer_params=optimizer_params,
|
|
150
|
-
loss=loss,
|
|
151
|
-
loss_params=loss_params,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
130
|
def compute_inner_products(self, field_emb: torch.Tensor) -> torch.Tensor:
|
|
155
131
|
interactions = []
|
|
156
132
|
for i in range(self.num_fields - 1):
|
|
@@ -38,8 +38,6 @@ Wide & Deep 同时使用宽线性部分(记忆共现/手工交叉)与深网
|
|
|
38
38
|
- 共享特征空间,减少工程开销
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
-
import torch.nn as nn
|
|
42
|
-
|
|
43
41
|
from nextrec.basic.features import DenseFeature, SequenceFeature, SparseFeature
|
|
44
42
|
from nextrec.basic.layers import LR, MLP, EmbeddingLayer
|
|
45
43
|
from nextrec.basic.heads import TaskHead
|
|
@@ -61,40 +59,16 @@ class WideDeep(BaseModel):
|
|
|
61
59
|
sparse_features: list[SparseFeature],
|
|
62
60
|
sequence_features: list[SequenceFeature],
|
|
63
61
|
mlp_params: dict,
|
|
64
|
-
target: list[str] | str | None = None,
|
|
65
|
-
task: str | list[str] | None = None,
|
|
66
|
-
optimizer: str = "adam",
|
|
67
|
-
optimizer_params: dict | None = None,
|
|
68
|
-
loss: str | nn.Module | None = "bce",
|
|
69
|
-
loss_params: dict | list[dict] | None = None,
|
|
70
|
-
embedding_l1_reg=0.0,
|
|
71
|
-
dense_l1_reg=0.0,
|
|
72
|
-
embedding_l2_reg=0.0,
|
|
73
|
-
dense_l2_reg=0.0,
|
|
74
62
|
**kwargs,
|
|
75
63
|
):
|
|
76
64
|
|
|
77
|
-
if target is None:
|
|
78
|
-
target = []
|
|
79
|
-
optimizer_params = optimizer_params or {}
|
|
80
|
-
if loss is None:
|
|
81
|
-
loss = "bce"
|
|
82
|
-
|
|
83
65
|
super(WideDeep, self).__init__(
|
|
84
66
|
dense_features=dense_features,
|
|
85
67
|
sparse_features=sparse_features,
|
|
86
68
|
sequence_features=sequence_features,
|
|
87
|
-
target=target,
|
|
88
|
-
task=task or self.default_task,
|
|
89
|
-
embedding_l1_reg=embedding_l1_reg,
|
|
90
|
-
dense_l1_reg=dense_l1_reg,
|
|
91
|
-
embedding_l2_reg=embedding_l2_reg,
|
|
92
|
-
dense_l2_reg=dense_l2_reg,
|
|
93
69
|
**kwargs,
|
|
94
70
|
)
|
|
95
71
|
|
|
96
|
-
self.loss = loss
|
|
97
|
-
|
|
98
72
|
# Wide part: use all features for linear model
|
|
99
73
|
self.wide_features = sparse_features + sequence_features
|
|
100
74
|
# Deep part: use all features
|
|
@@ -117,12 +91,6 @@ class WideDeep(BaseModel):
|
|
|
117
91
|
self.register_regularization_weights(
|
|
118
92
|
embedding_attr="embedding", include_modules=["linear", "mlp"]
|
|
119
93
|
)
|
|
120
|
-
self.compile(
|
|
121
|
-
optimizer=optimizer,
|
|
122
|
-
optimizer_params=optimizer_params,
|
|
123
|
-
loss=loss,
|
|
124
|
-
loss_params=loss_params,
|
|
125
|
-
)
|
|
126
94
|
|
|
127
95
|
def forward(self, x):
|
|
128
96
|
# Deep part
|
|
@@ -121,41 +121,18 @@ class xDeepFM(BaseModel):
|
|
|
121
121
|
mlp_params: dict,
|
|
122
122
|
cin_size: list[int] | None = None,
|
|
123
123
|
split_half: bool = True,
|
|
124
|
-
target: list[str] | str | None = None,
|
|
125
|
-
task: str | list[str] | None = None,
|
|
126
|
-
optimizer: str = "adam",
|
|
127
|
-
optimizer_params: dict | None = None,
|
|
128
|
-
loss: str | nn.Module | None = "bce",
|
|
129
|
-
loss_params: dict | list[dict] | None = None,
|
|
130
|
-
embedding_l1_reg=0.0,
|
|
131
|
-
dense_l1_reg=0.0,
|
|
132
|
-
embedding_l2_reg=0.0,
|
|
133
|
-
dense_l2_reg=0.0,
|
|
134
124
|
**kwargs,
|
|
135
125
|
):
|
|
136
126
|
|
|
137
127
|
cin_size = cin_size or [128, 128]
|
|
138
|
-
if target is None:
|
|
139
|
-
target = []
|
|
140
|
-
optimizer_params = optimizer_params or {}
|
|
141
|
-
if loss is None:
|
|
142
|
-
loss = "bce"
|
|
143
128
|
|
|
144
129
|
super(xDeepFM, self).__init__(
|
|
145
130
|
dense_features=dense_features,
|
|
146
131
|
sparse_features=sparse_features,
|
|
147
132
|
sequence_features=sequence_features,
|
|
148
|
-
target=target,
|
|
149
|
-
task=task or self.default_task,
|
|
150
|
-
embedding_l1_reg=embedding_l1_reg,
|
|
151
|
-
dense_l1_reg=dense_l1_reg,
|
|
152
|
-
embedding_l2_reg=embedding_l2_reg,
|
|
153
|
-
dense_l2_reg=dense_l2_reg,
|
|
154
133
|
**kwargs,
|
|
155
134
|
)
|
|
156
135
|
|
|
157
|
-
self.loss = loss
|
|
158
|
-
|
|
159
136
|
# Linear part and CIN part: use sparse and sequence features
|
|
160
137
|
self.linear_features = sparse_features + sequence_features
|
|
161
138
|
|
|
@@ -195,13 +172,6 @@ class xDeepFM(BaseModel):
|
|
|
195
172
|
embedding_attr="embedding", include_modules=["linear", "cin", "mlp"]
|
|
196
173
|
)
|
|
197
174
|
|
|
198
|
-
self.compile(
|
|
199
|
-
optimizer=optimizer,
|
|
200
|
-
optimizer_params=optimizer_params,
|
|
201
|
-
loss=loss,
|
|
202
|
-
loss_params=loss_params,
|
|
203
|
-
)
|
|
204
|
-
|
|
205
175
|
def forward(self, x):
|
|
206
176
|
# Get embeddings for linear and CIN (sparse features only)
|
|
207
177
|
input_linear = self.embedding(
|
nextrec/models/retrieval/dssm.py
CHANGED
|
@@ -10,7 +10,6 @@ Reference:
|
|
|
10
10
|
from typing import Literal
|
|
11
11
|
|
|
12
12
|
import torch
|
|
13
|
-
import torch.nn as nn
|
|
14
13
|
|
|
15
14
|
from nextrec.basic.features import DenseFeature, SequenceFeature, SparseFeature
|
|
16
15
|
from nextrec.basic.layers import MLP, EmbeddingLayer
|
|
@@ -54,17 +53,6 @@ class DSSM(BaseMatchModel):
|
|
|
54
53
|
dense_l1_reg=0.0,
|
|
55
54
|
embedding_l2_reg=0.0,
|
|
56
55
|
dense_l2_reg=0.0,
|
|
57
|
-
optimizer: str | torch.optim.Optimizer = "adam",
|
|
58
|
-
optimizer_params: dict | None = None,
|
|
59
|
-
scheduler: (
|
|
60
|
-
str
|
|
61
|
-
| torch.optim.lr_scheduler._LRScheduler
|
|
62
|
-
| type[torch.optim.lr_scheduler._LRScheduler]
|
|
63
|
-
| None
|
|
64
|
-
) = None,
|
|
65
|
-
scheduler_params: dict | None = None,
|
|
66
|
-
loss: str | nn.Module | list[str | nn.Module] | None = "bce",
|
|
67
|
-
loss_params: dict | list[dict] | None = None,
|
|
68
56
|
**kwargs,
|
|
69
57
|
):
|
|
70
58
|
|
|
@@ -159,18 +147,6 @@ class DSSM(BaseMatchModel):
|
|
|
159
147
|
embedding_attr="item_embedding", include_modules=["item_dnn"]
|
|
160
148
|
)
|
|
161
149
|
|
|
162
|
-
if optimizer_params is None:
|
|
163
|
-
optimizer_params = {"lr": 1e-3, "weight_decay": 1e-5}
|
|
164
|
-
|
|
165
|
-
self.compile(
|
|
166
|
-
optimizer=optimizer,
|
|
167
|
-
optimizer_params=optimizer_params,
|
|
168
|
-
scheduler=scheduler,
|
|
169
|
-
scheduler_params=scheduler_params,
|
|
170
|
-
loss=loss,
|
|
171
|
-
loss_params=loss_params,
|
|
172
|
-
)
|
|
173
|
-
|
|
174
150
|
def user_tower(self, user_input: dict) -> torch.Tensor:
|
|
175
151
|
"""
|
|
176
152
|
User tower encodes user features into embeddings.
|
|
@@ -9,7 +9,6 @@ DSSM v2 - DSSM with pairwise training using BPR loss
|
|
|
9
9
|
from typing import Literal
|
|
10
10
|
|
|
11
11
|
import torch
|
|
12
|
-
import torch.nn as nn
|
|
13
12
|
|
|
14
13
|
from nextrec.basic.features import DenseFeature, SequenceFeature, SparseFeature
|
|
15
14
|
from nextrec.basic.layers import MLP, EmbeddingLayer
|
|
@@ -50,17 +49,6 @@ class DSSM_v2(BaseMatchModel):
|
|
|
50
49
|
dense_l1_reg: float = 0.0,
|
|
51
50
|
embedding_l2_reg: float = 0.0,
|
|
52
51
|
dense_l2_reg: float = 0.0,
|
|
53
|
-
optimizer: str | torch.optim.Optimizer = "adam",
|
|
54
|
-
optimizer_params: dict | None = None,
|
|
55
|
-
scheduler: (
|
|
56
|
-
str
|
|
57
|
-
| torch.optim.lr_scheduler._LRScheduler
|
|
58
|
-
| type[torch.optim.lr_scheduler._LRScheduler]
|
|
59
|
-
| None
|
|
60
|
-
) = None,
|
|
61
|
-
scheduler_params: dict | None = None,
|
|
62
|
-
loss: str | nn.Module | list[str | nn.Module] | None = "bce",
|
|
63
|
-
loss_params: dict | list[dict] | None = None,
|
|
64
52
|
**kwargs,
|
|
65
53
|
):
|
|
66
54
|
|
|
@@ -151,18 +139,6 @@ class DSSM_v2(BaseMatchModel):
|
|
|
151
139
|
embedding_attr="item_embedding", include_modules=["item_dnn"]
|
|
152
140
|
)
|
|
153
141
|
|
|
154
|
-
if optimizer_params is None:
|
|
155
|
-
optimizer_params = {"lr": 1e-3, "weight_decay": 1e-5}
|
|
156
|
-
|
|
157
|
-
self.compile(
|
|
158
|
-
optimizer=optimizer,
|
|
159
|
-
optimizer_params=optimizer_params,
|
|
160
|
-
scheduler=scheduler,
|
|
161
|
-
scheduler_params=scheduler_params,
|
|
162
|
-
loss=loss,
|
|
163
|
-
loss_params=loss_params,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
142
|
def user_tower(self, user_input: dict) -> torch.Tensor:
|
|
167
143
|
"""User tower"""
|
|
168
144
|
all_user_features = (
|
nextrec/models/retrieval/mind.py
CHANGED
|
@@ -206,17 +206,6 @@ class MIND(BaseMatchModel):
|
|
|
206
206
|
dense_l1_reg=0.0,
|
|
207
207
|
embedding_l2_reg=0.0,
|
|
208
208
|
dense_l2_reg=0.0,
|
|
209
|
-
optimizer: str | torch.optim.Optimizer = "adam",
|
|
210
|
-
optimizer_params: dict | None = None,
|
|
211
|
-
scheduler: (
|
|
212
|
-
str
|
|
213
|
-
| torch.optim.lr_scheduler._LRScheduler
|
|
214
|
-
| type[torch.optim.lr_scheduler._LRScheduler]
|
|
215
|
-
| None
|
|
216
|
-
) = None,
|
|
217
|
-
scheduler_params: dict | None = None,
|
|
218
|
-
loss: str | nn.Module | list[str | nn.Module] | None = "bce",
|
|
219
|
-
loss_params: dict | list[dict] | None = None,
|
|
220
209
|
**kwargs,
|
|
221
210
|
):
|
|
222
211
|
|
|
@@ -322,15 +311,6 @@ class MIND(BaseMatchModel):
|
|
|
322
311
|
include_modules=["item_dnn"] if self.item_dnn else [],
|
|
323
312
|
)
|
|
324
313
|
|
|
325
|
-
self.compile(
|
|
326
|
-
optimizer=optimizer,
|
|
327
|
-
optimizer_params=optimizer_params,
|
|
328
|
-
scheduler=scheduler,
|
|
329
|
-
scheduler_params=scheduler_params,
|
|
330
|
-
loss=loss,
|
|
331
|
-
loss_params=loss_params,
|
|
332
|
-
)
|
|
333
|
-
|
|
334
314
|
def user_tower(self, user_input: dict) -> torch.Tensor:
|
|
335
315
|
"""
|
|
336
316
|
User tower with multi-interest extraction
|
nextrec/models/retrieval/sdm.py
CHANGED
|
@@ -53,17 +53,6 @@ class SDM(BaseMatchModel):
|
|
|
53
53
|
dense_l1_reg=0.0,
|
|
54
54
|
embedding_l2_reg=0.0,
|
|
55
55
|
dense_l2_reg=0.0,
|
|
56
|
-
optimizer: str | torch.optim.Optimizer = "adam",
|
|
57
|
-
optimizer_params: dict | None = None,
|
|
58
|
-
scheduler: (
|
|
59
|
-
str
|
|
60
|
-
| torch.optim.lr_scheduler._LRScheduler
|
|
61
|
-
| type[torch.optim.lr_scheduler._LRScheduler]
|
|
62
|
-
| None
|
|
63
|
-
) = None,
|
|
64
|
-
scheduler_params: dict | None = None,
|
|
65
|
-
loss: str | nn.Module | list[str | nn.Module] | None = "bce",
|
|
66
|
-
loss_params: dict | list[dict] | None = None,
|
|
67
56
|
**kwargs,
|
|
68
57
|
):
|
|
69
58
|
|
|
@@ -189,15 +178,6 @@ class SDM(BaseMatchModel):
|
|
|
189
178
|
include_modules=["item_dnn"] if self.item_dnn else [],
|
|
190
179
|
)
|
|
191
180
|
|
|
192
|
-
self.compile(
|
|
193
|
-
optimizer=optimizer,
|
|
194
|
-
optimizer_params=optimizer_params,
|
|
195
|
-
scheduler=scheduler,
|
|
196
|
-
scheduler_params=scheduler_params,
|
|
197
|
-
loss=loss,
|
|
198
|
-
loss_params=loss_params,
|
|
199
|
-
)
|
|
200
|
-
|
|
201
181
|
def user_tower(self, user_input: dict) -> torch.Tensor:
|
|
202
182
|
seq_feature = self.user_sequence_features[0]
|
|
203
183
|
seq_input = user_input[seq_feature.name]
|
|
@@ -10,7 +10,6 @@ Reference:
|
|
|
10
10
|
from typing import Literal
|
|
11
11
|
|
|
12
12
|
import torch
|
|
13
|
-
import torch.nn as nn
|
|
14
13
|
|
|
15
14
|
from nextrec.basic.features import DenseFeature, SequenceFeature, SparseFeature
|
|
16
15
|
from nextrec.basic.layers import MLP, EmbeddingLayer
|
|
@@ -54,17 +53,6 @@ class YoutubeDNN(BaseMatchModel):
|
|
|
54
53
|
dense_l1_reg=0.0,
|
|
55
54
|
embedding_l2_reg=0.0,
|
|
56
55
|
dense_l2_reg=0.0,
|
|
57
|
-
optimizer: str | torch.optim.Optimizer = "adam",
|
|
58
|
-
optimizer_params: dict | None = None,
|
|
59
|
-
scheduler: (
|
|
60
|
-
str
|
|
61
|
-
| torch.optim.lr_scheduler._LRScheduler
|
|
62
|
-
| type[torch.optim.lr_scheduler._LRScheduler]
|
|
63
|
-
| None
|
|
64
|
-
) = None,
|
|
65
|
-
scheduler_params: dict | None = None,
|
|
66
|
-
loss: str | nn.Module | list[str | nn.Module] | None = "bce",
|
|
67
|
-
loss_params: dict | list[dict] | None = None,
|
|
68
56
|
**kwargs,
|
|
69
57
|
):
|
|
70
58
|
|
|
@@ -156,15 +144,6 @@ class YoutubeDNN(BaseMatchModel):
|
|
|
156
144
|
embedding_attr="item_embedding", include_modules=["item_dnn"]
|
|
157
145
|
)
|
|
158
146
|
|
|
159
|
-
self.compile(
|
|
160
|
-
optimizer=optimizer,
|
|
161
|
-
optimizer_params=optimizer_params,
|
|
162
|
-
scheduler=scheduler,
|
|
163
|
-
scheduler_params=scheduler_params,
|
|
164
|
-
loss=loss,
|
|
165
|
-
loss_params=loss_params,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
147
|
def user_tower(self, user_input: dict) -> torch.Tensor:
|
|
169
148
|
"""
|
|
170
149
|
User tower to encode historical behavior sequences and user features.
|
|
@@ -323,11 +323,6 @@ class HSTU(BaseModel):
|
|
|
323
323
|
tie_embeddings: bool = True,
|
|
324
324
|
target: Optional[list[str] | str] = None,
|
|
325
325
|
task: str | list[str] | None = None,
|
|
326
|
-
optimizer: str = "adam",
|
|
327
|
-
optimizer_params: Optional[dict] = None,
|
|
328
|
-
scheduler: Optional[str] = None,
|
|
329
|
-
scheduler_params: Optional[dict] = None,
|
|
330
|
-
loss_params: Optional[dict] = None,
|
|
331
326
|
embedding_l1_reg: float = 0.0,
|
|
332
327
|
dense_l1_reg: float = 0.0,
|
|
333
328
|
embedding_l2_reg: float = 0.0,
|
|
@@ -426,19 +421,6 @@ class HSTU(BaseModel):
|
|
|
426
421
|
self.register_buffer("causal_mask", torch.empty(0), persistent=False)
|
|
427
422
|
self.ignore_index = self.padding_idx if self.padding_idx is not None else -100
|
|
428
423
|
|
|
429
|
-
optimizer_params = optimizer_params or {}
|
|
430
|
-
scheduler_params = scheduler_params or {}
|
|
431
|
-
loss_params = loss_params or {}
|
|
432
|
-
loss_params.setdefault("ignore_index", self.ignore_index)
|
|
433
|
-
|
|
434
|
-
self.compile(
|
|
435
|
-
optimizer=optimizer,
|
|
436
|
-
optimizer_params=optimizer_params,
|
|
437
|
-
scheduler=scheduler,
|
|
438
|
-
scheduler_params=scheduler_params,
|
|
439
|
-
loss="crossentropy",
|
|
440
|
-
loss_params=loss_params,
|
|
441
|
-
)
|
|
442
424
|
self.register_regularization_weights(
|
|
443
425
|
embedding_attr="token_embedding",
|
|
444
426
|
include_modules=["layers", "lm_head", "context_proj"],
|
nextrec/utils/model.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Model-related utilities for NextRec
|
|
3
3
|
|
|
4
4
|
Date: create on 03/12/2025
|
|
5
|
-
Checkpoint: edit on
|
|
5
|
+
Checkpoint: edit on 29/12/2025
|
|
6
6
|
Author: Yang Zhou, zyaztec@gmail.com
|
|
7
7
|
"""
|
|
8
8
|
|
|
@@ -72,6 +72,84 @@ def compute_pair_scores(model, data, batch_size: int = 512):
|
|
|
72
72
|
return scores.detach().cpu().numpy()
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def get_training_modes(
|
|
76
|
+
training_mode,
|
|
77
|
+
nums_task: int,
|
|
78
|
+
valid_modes: set[str] | None = None,
|
|
79
|
+
) -> list:
|
|
80
|
+
valid_modes = valid_modes or {"pointwise", "pairwise", "listwise"}
|
|
81
|
+
if isinstance(training_mode, list):
|
|
82
|
+
training_modes = list(training_mode)
|
|
83
|
+
if len(training_modes) != nums_task:
|
|
84
|
+
raise ValueError(
|
|
85
|
+
"[BaseModel-init Error] training_mode list length must match number of tasks."
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
training_modes = [training_mode] * nums_task
|
|
89
|
+
if any(mode not in valid_modes for mode in training_modes):
|
|
90
|
+
raise ValueError(
|
|
91
|
+
"[BaseModel-init Error] training_mode must be one of {'pointwise', 'pairwise', 'listwise'}."
|
|
92
|
+
)
|
|
93
|
+
return training_modes
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_loss_list(
|
|
97
|
+
loss,
|
|
98
|
+
training_modes: list[str],
|
|
99
|
+
nums_task: int,
|
|
100
|
+
default_losses: dict[str, str],
|
|
101
|
+
):
|
|
102
|
+
effective_loss = loss
|
|
103
|
+
if effective_loss is None:
|
|
104
|
+
loss_list = [default_losses[mode] for mode in training_modes]
|
|
105
|
+
elif isinstance(effective_loss, list):
|
|
106
|
+
if not effective_loss:
|
|
107
|
+
loss_list = [default_losses[mode] for mode in training_modes]
|
|
108
|
+
else:
|
|
109
|
+
if len(effective_loss) != nums_task:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"[BaseModel-compile Error] Number of loss functions ({len(effective_loss)}) must match number of tasks ({nums_task})."
|
|
112
|
+
)
|
|
113
|
+
loss_list = list(effective_loss)
|
|
114
|
+
else:
|
|
115
|
+
loss_list = [effective_loss] * nums_task
|
|
116
|
+
|
|
117
|
+
for idx, mode in enumerate(training_modes):
|
|
118
|
+
if isinstance(loss_list[idx], str) and loss_list[idx] in {
|
|
119
|
+
"bce",
|
|
120
|
+
"binary_crossentropy",
|
|
121
|
+
}:
|
|
122
|
+
if mode in {"pairwise", "listwise"}:
|
|
123
|
+
loss_list[idx] = default_losses[mode]
|
|
124
|
+
return loss_list
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def resolve_loss_weights(loss_weights, nums_task: int):
|
|
128
|
+
if loss_weights is None:
|
|
129
|
+
return None
|
|
130
|
+
if nums_task == 1:
|
|
131
|
+
if isinstance(loss_weights, (list, tuple)):
|
|
132
|
+
if len(loss_weights) != 1:
|
|
133
|
+
raise ValueError(
|
|
134
|
+
"[BaseModel-compile Error] loss_weights list must have exactly one element for single-task setup."
|
|
135
|
+
)
|
|
136
|
+
loss_weights = loss_weights[0]
|
|
137
|
+
return [float(loss_weights)]
|
|
138
|
+
if isinstance(loss_weights, (int, float)):
|
|
139
|
+
weights = [float(loss_weights)] * nums_task
|
|
140
|
+
elif isinstance(loss_weights, (list, tuple)):
|
|
141
|
+
weights = [float(w) for w in loss_weights]
|
|
142
|
+
if len(weights) != nums_task:
|
|
143
|
+
raise ValueError(
|
|
144
|
+
f"[BaseModel-compile Error] Number of loss_weights ({len(weights)}) must match number of tasks ({nums_task})."
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
raise TypeError(
|
|
148
|
+
f"[BaseModel-compile Error] loss_weights must be int, float, list or tuple, got {type(loss_weights)}"
|
|
149
|
+
)
|
|
150
|
+
return weights
|
|
151
|
+
|
|
152
|
+
|
|
75
153
|
def prepare_ranking_targets(
|
|
76
154
|
y_pred: torch.Tensor, y_true: torch.Tensor
|
|
77
155
|
) -> tuple[torch.Tensor, torch.Tensor]:
|
nextrec/utils/types.py
CHANGED
|
@@ -61,3 +61,38 @@ ActivationName = Literal[
|
|
|
61
61
|
TrainingModeName = Literal["pointwise", "pairwise", "listwise"]
|
|
62
62
|
|
|
63
63
|
TaskTypeName = Literal["binary", "regression"]
|
|
64
|
+
|
|
65
|
+
MetricsName = Literal[
|
|
66
|
+
"auc",
|
|
67
|
+
"gauc",
|
|
68
|
+
"ks",
|
|
69
|
+
"logloss",
|
|
70
|
+
"accuracy",
|
|
71
|
+
"acc",
|
|
72
|
+
"precision",
|
|
73
|
+
"recall",
|
|
74
|
+
"f1",
|
|
75
|
+
"micro_f1",
|
|
76
|
+
"macro_f1",
|
|
77
|
+
"mse",
|
|
78
|
+
"mae",
|
|
79
|
+
"rmse",
|
|
80
|
+
"r2",
|
|
81
|
+
"mape",
|
|
82
|
+
"msle",
|
|
83
|
+
"auc",
|
|
84
|
+
"gauc",
|
|
85
|
+
"precision@10",
|
|
86
|
+
"hitrate@10",
|
|
87
|
+
"map@10",
|
|
88
|
+
"cosine",
|
|
89
|
+
"recall@5",
|
|
90
|
+
"recall@10",
|
|
91
|
+
"recall@20",
|
|
92
|
+
"ndcg@5",
|
|
93
|
+
"ndcg@10",
|
|
94
|
+
"ndcg@20",
|
|
95
|
+
"mrr@5",
|
|
96
|
+
"mrr@10",
|
|
97
|
+
"mrr@20",
|
|
98
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nextrec
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.23
|
|
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
|
|
@@ -69,7 +69,7 @@ Description-Content-Type: text/markdown
|
|
|
69
69
|

|
|
70
70
|

|
|
71
71
|

|
|
72
|
-

|
|
73
73
|
[](https://deepwiki.com/zerolovesea/NextRec)
|
|
74
74
|
|
|
75
75
|
中文文档 | [English Version](README_en.md)
|
|
@@ -191,6 +191,8 @@ model = DIN(
|
|
|
191
191
|
dense_features=dense_features,
|
|
192
192
|
sparse_features=sparse_features,
|
|
193
193
|
sequence_features=sequence_features,
|
|
194
|
+
behavior_feature_name="sequence_0",
|
|
195
|
+
candidate_feature_name="item_id",
|
|
194
196
|
mlp_params=mlp_params,
|
|
195
197
|
attention_hidden_units=[80, 40],
|
|
196
198
|
attention_activation='sigmoid',
|
|
@@ -204,7 +206,7 @@ model = DIN(
|
|
|
204
206
|
session_id="din_tutorial", # 实验id,用于存放训练日志
|
|
205
207
|
)
|
|
206
208
|
|
|
207
|
-
#
|
|
209
|
+
# 编译模型,优化器/损失/学习率调度器统一在 compile 中设置
|
|
208
210
|
model.compile(
|
|
209
211
|
optimizer = "adam",
|
|
210
212
|
optimizer_params = {"lr": 1e-3, "weight_decay": 1e-5},
|
|
@@ -247,11 +249,11 @@ nextrec --mode=predict --predict_config=path/to/predict_config.yaml
|
|
|
247
249
|
|
|
248
250
|
预测结果固定保存到 `{checkpoint_path}/predictions/{name}.{save_data_format}`。
|
|
249
251
|
|
|
250
|
-
> 截止当前版本0.4.
|
|
252
|
+
> 截止当前版本0.4.23,NextRec CLI支持单机训练,分布式训练相关功能尚在开发中。
|
|
251
253
|
|
|
252
254
|
## 兼容平台
|
|
253
255
|
|
|
254
|
-
当前最新版本为0.4.
|
|
256
|
+
当前最新版本为0.4.23,所有模型和测试代码均已在以下平台通过验证,如果开发者在使用中遇到兼容问题,请在issue区提出错误报告及系统版本:
|
|
255
257
|
|
|
256
258
|
| 平台 | 配置 |
|
|
257
259
|
|------|------|
|