Skip to content

Commit a062d13

Browse files
blonderedDaria Tikhonovich
andauthored
Feature/sasrec configs (#248)
Added configs for SASRecModel and BERT4RecModel --------- Co-authored-by: Daria Tikhonovich <daria.m.tikhonovich@gmail.com>
1 parent 2a59439 commit a062d13

12 files changed

Lines changed: 1088 additions & 308 deletions

examples/9_model_configs_and_saving.ipynb

Lines changed: 365 additions & 167 deletions
Large diffs are not rendered by default.

rectools/compat.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ class DSSMModel(RequirementUnavailable):
4040
requirement = "torch"
4141

4242

43+
class SASRecModel(RequirementUnavailable):
44+
"""Dummy class, which is returned if there are no dependencies required for the model"""
45+
46+
requirement = "torch"
47+
48+
49+
class BERT4RecModel(RequirementUnavailable):
50+
"""Dummy class, which is returned if there are no dependencies required for the model"""
51+
52+
requirement = "torch"
53+
54+
4355
class ItemToItemAnnRecommender(RequirementUnavailable):
4456
"""Dummy class, which is returned if there are no dependencies required for the model"""
4557

rectools/models/nn/bert4rec.py

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@
1414

1515
import typing as tp
1616
from collections.abc import Hashable
17-
from typing import Dict, List, Tuple, Union
17+
from typing import Dict, List, Tuple
1818

1919
import numpy as np
2020
import torch
2121
from pytorch_lightning import Trainer
22-
from pytorch_lightning.accelerators import Accelerator
2322

2423
from .item_net import CatFeaturesItemNet, IdEmbeddingsItemNet, ItemNetBase
2524
from .transformer_base import (
2625
PADDING_VALUE,
26+
SessionEncoderDataPreparatorType,
2727
SessionEncoderLightningModule,
2828
SessionEncoderLightningModuleBase,
2929
TransformerModelBase,
30+
TransformerModelConfig,
3031
)
3132
from .transformer_data_preparator import SessionEncoderDataPreparatorBase
3233
from .transformer_net_blocks import (
@@ -144,7 +145,15 @@ def _collate_fn_recommend(self, batch: List[Tuple[List[int], List[float]]]) -> D
144145
return {"x": torch.LongTensor(x)}
145146

146147

147-
class BERT4RecModel(TransformerModelBase):
148+
class BERT4RecModelConfig(TransformerModelConfig):
149+
"""BERT4RecModel config."""
150+
151+
data_preparator_type: SessionEncoderDataPreparatorType = BERT4RecDataPreparator
152+
use_key_padding_mask: bool = True
153+
mask_prob: float = 0.15
154+
155+
156+
class BERT4RecModel(TransformerModelBase[BERT4RecModelConfig]):
148157
"""
149158
BERT4Rec model.
150159
@@ -190,10 +199,21 @@ class BERT4RecModel(TransformerModelBase):
190199
deterministic : bool, default ``False``
191200
If ``True``, set deterministic algorithms for PyTorch operations.
192201
Use `pytorch_lightning.seed_everything` together with this parameter to fix the random state.
193-
recommend_device : {"cpu", "gpu", "tpu", "hpu", "mps", "auto"} or Accelerator, default "auto"
194-
Device for recommend. Used at predict_step of lightning module.
202+
recommend_batch_size : int, default 256
203+
How many samples per batch to load during `recommend`.
204+
If you want to change this parameter after model is initialized,
205+
you can manually assign new value to model `recommend_batch_size` attribute.
206+
recommend_accelerator : {"cpu", "gpu", "tpu", "hpu", "mps", "auto"}, default "auto"
207+
Accelerator type for `recommend`. Used at predict_step of lightning module.
208+
If you want to change this parameter after model is initialized,
209+
you can manually assign new value to model `recommend_accelerator` attribute.
210+
recommend_devices : int | List[int], default 1
211+
Devices for `recommend`. Please note that multi-device inference is not supported!
212+
Do not specify more then one device. For ``gpu`` accelerator you can pass which device to
213+
use, e.g. ``[1]``.
214+
Used at predict_step of lightning module.
215+
Multi-device recommendations are not supported.
195216
If you want to change this parameter after model is initialized,
196-
you can manually assign new value to model `recommend_device` attribute.
197217
recommend_n_threads : int, default 0
198218
Number of threads to use in ranker if GPU ranking is turned off or unavailable.
199219
If you want to change this parameter after model is initialized,
@@ -222,38 +242,44 @@ class BERT4RecModel(TransformerModelBase):
222242
Function to get validation mask.
223243
"""
224244

245+
config_class = BERT4RecModelConfig
246+
225247
def __init__( # pylint: disable=too-many-arguments, too-many-locals
226248
self,
227-
n_blocks: int = 1,
228-
n_heads: int = 1,
229-
n_factors: int = 128,
249+
n_blocks: int = 2,
250+
n_heads: int = 4,
251+
n_factors: int = 256,
230252
use_pos_emb: bool = True,
231253
use_causal_attn: bool = False,
232254
use_key_padding_mask: bool = True,
233255
dropout_rate: float = 0.2,
234256
epochs: int = 3,
235257
verbose: int = 0,
236258
deterministic: bool = False,
237-
recommend_device: Union[str, Accelerator] = "auto",
259+
recommend_batch_size: int = 256,
260+
recommend_accelerator: str = "auto",
261+
recommend_devices: tp.Union[int, tp.List[int]] = 1,
238262
recommend_n_threads: int = 0,
239263
recommend_use_gpu_ranking: bool = True,
240-
session_max_len: int = 32,
264+
session_max_len: int = 100,
241265
n_negatives: int = 1,
242266
batch_size: int = 128,
243267
loss: str = "softmax",
244268
gbce_t: float = 0.2,
245-
lr: float = 0.01,
269+
lr: float = 0.001,
246270
dataloader_num_workers: int = 0,
247271
train_min_user_interactions: int = 2,
248272
mask_prob: float = 0.15,
249273
trainer: tp.Optional[Trainer] = None,
250274
item_net_block_types: tp.Sequence[tp.Type[ItemNetBase]] = (IdEmbeddingsItemNet, CatFeaturesItemNet),
251275
pos_encoding_type: tp.Type[PositionalEncodingBase] = LearnableInversePositionalEncoding,
252276
transformer_layers_type: tp.Type[TransformerLayersBase] = PreLNTransformerLayers,
253-
data_preparator_type: tp.Type[BERT4RecDataPreparator] = BERT4RecDataPreparator,
277+
data_preparator_type: tp.Type[SessionEncoderDataPreparatorBase] = BERT4RecDataPreparator,
254278
lightning_module_type: tp.Type[SessionEncoderLightningModuleBase] = SessionEncoderLightningModule,
255279
get_val_mask_func: tp.Optional[tp.Callable] = None,
256280
):
281+
self.mask_prob = mask_prob
282+
257283
super().__init__(
258284
transformer_layers_type=transformer_layers_type,
259285
data_preparator_type=data_preparator_type,
@@ -264,28 +290,37 @@ def __init__( # pylint: disable=too-many-arguments, too-many-locals
264290
use_causal_attn=use_causal_attn,
265291
use_key_padding_mask=use_key_padding_mask,
266292
dropout_rate=dropout_rate,
293+
session_max_len=session_max_len,
294+
dataloader_num_workers=dataloader_num_workers,
295+
batch_size=batch_size,
296+
loss=loss,
297+
n_negatives=n_negatives,
298+
gbce_t=gbce_t,
299+
lr=lr,
267300
epochs=epochs,
268301
verbose=verbose,
269302
deterministic=deterministic,
270-
recommend_device=recommend_device,
303+
recommend_batch_size=recommend_batch_size,
304+
recommend_accelerator=recommend_accelerator,
305+
recommend_devices=recommend_devices,
271306
recommend_n_threads=recommend_n_threads,
272307
recommend_use_gpu_ranking=recommend_use_gpu_ranking,
273-
loss=loss,
274-
gbce_t=gbce_t,
275-
lr=lr,
276-
session_max_len=session_max_len + 1,
308+
train_min_user_interactions=train_min_user_interactions,
277309
trainer=trainer,
278310
item_net_block_types=item_net_block_types,
279311
pos_encoding_type=pos_encoding_type,
280312
lightning_module_type=lightning_module_type,
313+
get_val_mask_func=get_val_mask_func,
281314
)
282-
self.data_preparator = data_preparator_type(
283-
session_max_len=session_max_len,
284-
n_negatives=n_negatives if loss != "softmax" else None,
285-
batch_size=batch_size,
286-
dataloader_num_workers=dataloader_num_workers,
287-
train_min_user_interactions=train_min_user_interactions,
315+
316+
def _init_data_preparator(self) -> None:
317+
self.data_preparator: SessionEncoderDataPreparatorBase = self.data_preparator_type(
318+
session_max_len=self.session_max_len - 1, # TODO: remove `-1`
319+
n_negatives=self.n_negatives if self.loss != "softmax" else None,
320+
batch_size=self.batch_size,
321+
dataloader_num_workers=self.dataloader_num_workers,
322+
train_min_user_interactions=self.train_min_user_interactions,
288323
item_extra_tokens=(PADDING_VALUE, MASKING_VALUE),
289-
mask_prob=mask_prob,
290-
get_val_mask_func=get_val_mask_func,
324+
mask_prob=self.mask_prob,
325+
get_val_mask_func=self.get_val_mask_func,
291326
)

rectools/models/nn/item_net.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ def forward(self, items: torch.Tensor) -> torch.Tensor:
8888
torch.Tensor
8989
Item embeddings.
9090
"""
91-
# TODO: Should we use torch.nn.EmbeddingBag?
9291
feature_dense = self.get_dense_item_features(items)
9392

9493
feature_embs = self.category_embeddings(self.feature_catalog.to(self.device))
@@ -252,7 +251,6 @@ def forward(self, items: torch.Tensor) -> torch.Tensor:
252251
Item embeddings.
253252
"""
254253
item_embs = []
255-
# TODO: Add functionality for parallel computing.
256254
for idx_block in range(self.n_item_blocks):
257255
item_emb = self.item_net_blocks[idx_block](items)
258256
item_embs.append(item_emb)

rectools/models/nn/sasrec.py

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,22 @@
1313
# limitations under the License.
1414

1515
import typing as tp
16-
from typing import Dict, List, Tuple, Union
16+
from typing import Dict, List, Tuple
1717

1818
import numpy as np
1919
import torch
2020
from pytorch_lightning import Trainer
21-
from pytorch_lightning.accelerators import Accelerator
2221
from torch import nn
2322

2423
from .item_net import CatFeaturesItemNet, IdEmbeddingsItemNet, ItemNetBase
2524
from .transformer_base import (
2625
PADDING_VALUE,
26+
SessionEncoderDataPreparatorType,
2727
SessionEncoderLightningModule,
2828
SessionEncoderLightningModuleBase,
29+
TransformerLayersType,
2930
TransformerModelBase,
31+
TransformerModelConfig,
3032
)
3133
from .transformer_data_preparator import SessionEncoderDataPreparatorBase
3234
from .transformer_net_blocks import (
@@ -183,7 +185,15 @@ def forward(
183185
return seqs
184186

185187

186-
class SASRecModel(TransformerModelBase):
188+
class SASRecModelConfig(TransformerModelConfig):
189+
"""SASRecModel config."""
190+
191+
data_preparator_type: SessionEncoderDataPreparatorType = SASRecDataPreparator
192+
transformer_layers_type: TransformerLayersType = SASRecTransformerLayers
193+
use_causal_attn: bool = True
194+
195+
196+
class SASRecModel(TransformerModelBase[SASRecModelConfig]):
187197
"""
188198
SASRec model.
189199
@@ -227,8 +237,20 @@ class SASRecModel(TransformerModelBase):
227237
deterministic : bool, default ``False``
228238
If ``True``, set deterministic algorithms for PyTorch operations.
229239
Use `pytorch_lightning.seed_everything` together with this parameter to fix the random state.
230-
recommend_device : {"cpu", "gpu", "tpu", "hpu", "mps", "auto"} or Accelerator, default "auto"
231-
Device for recommend. Used at predict_step of lightning module.
240+
recommend_batch_size : int, default 256
241+
How many samples per batch to load during `recommend`.
242+
If you want to change this parameter after model is initialized,
243+
you can manually assign new value to model `recommend_batch_size` attribute.
244+
recommend_accelerator : {"cpu", "gpu", "tpu", "hpu", "mps", "auto"}, default "auto"
245+
Accelerator type for `recommend`. Used at predict_step of lightning module.
246+
If you want to change this parameter after model is initialized,
247+
you can manually assign new value to model `recommend_accelerator` attribute.
248+
recommend_devices : int | List[int], default 1
249+
Devices for `recommend`. Please note that multi-device inference is not supported!
250+
Do not specify more then one device. For ``gpu`` accelerator you can pass which device to
251+
use, e.g. ``[1]``.
252+
Used at predict_step of lightning module.
253+
Multi-device recommendations are not supported.
232254
If you want to change this parameter after model is initialized,
233255
you can manually assign new value to model `recommend_device` attribute.
234256
recommend_n_threads : int, default 0
@@ -259,26 +281,30 @@ class SASRecModel(TransformerModelBase):
259281
Function to get validation mask.
260282
"""
261283

284+
config_class = SASRecModelConfig
285+
262286
def __init__( # pylint: disable=too-many-arguments, too-many-locals
263287
self,
264-
n_blocks: int = 1,
265-
n_heads: int = 1,
266-
n_factors: int = 128,
288+
n_blocks: int = 2,
289+
n_heads: int = 4,
290+
n_factors: int = 256,
267291
use_pos_emb: bool = True,
268292
use_causal_attn: bool = True,
269293
use_key_padding_mask: bool = False,
270294
dropout_rate: float = 0.2,
271-
session_max_len: int = 32,
295+
session_max_len: int = 100,
272296
dataloader_num_workers: int = 0,
273297
batch_size: int = 128,
274298
loss: str = "softmax",
275299
n_negatives: int = 1,
276300
gbce_t: float = 0.2,
277-
lr: float = 0.01,
301+
lr: float = 0.001,
278302
epochs: int = 3,
279303
verbose: int = 0,
280304
deterministic: bool = False,
281-
recommend_device: Union[str, Accelerator] = "auto",
305+
recommend_batch_size: int = 256,
306+
recommend_accelerator: str = "auto",
307+
recommend_devices: tp.Union[int, tp.List[int]] = 1,
282308
recommend_n_threads: int = 0,
283309
recommend_use_gpu_ranking: bool = True,
284310
train_min_user_interactions: int = 2,
@@ -301,26 +327,35 @@ def __init__( # pylint: disable=too-many-arguments, too-many-locals
301327
use_key_padding_mask=use_key_padding_mask,
302328
dropout_rate=dropout_rate,
303329
session_max_len=session_max_len,
330+
dataloader_num_workers=dataloader_num_workers,
331+
batch_size=batch_size,
304332
loss=loss,
333+
n_negatives=n_negatives,
305334
gbce_t=gbce_t,
306335
lr=lr,
307336
epochs=epochs,
308337
verbose=verbose,
309338
deterministic=deterministic,
310-
recommend_device=recommend_device,
339+
recommend_batch_size=recommend_batch_size,
340+
recommend_accelerator=recommend_accelerator,
341+
recommend_devices=recommend_devices,
311342
recommend_n_threads=recommend_n_threads,
312343
recommend_use_gpu_ranking=recommend_use_gpu_ranking,
344+
train_min_user_interactions=train_min_user_interactions,
313345
trainer=trainer,
314346
item_net_block_types=item_net_block_types,
315347
pos_encoding_type=pos_encoding_type,
316348
lightning_module_type=lightning_module_type,
349+
get_val_mask_func=get_val_mask_func,
317350
)
318-
self.data_preparator = data_preparator_type(
319-
session_max_len=session_max_len,
320-
n_negatives=n_negatives if loss != "softmax" else None,
321-
batch_size=batch_size,
322-
dataloader_num_workers=dataloader_num_workers,
351+
352+
def _init_data_preparator(self) -> None:
353+
self.data_preparator = self.data_preparator_type(
354+
session_max_len=self.session_max_len,
355+
n_negatives=self.n_negatives if self.loss != "softmax" else None,
356+
batch_size=self.batch_size,
357+
dataloader_num_workers=self.dataloader_num_workers,
323358
item_extra_tokens=(PADDING_VALUE,),
324-
train_min_user_interactions=train_min_user_interactions,
325-
get_val_mask_func=get_val_mask_func,
359+
train_min_user_interactions=self.train_min_user_interactions,
360+
get_val_mask_func=self.get_val_mask_func,
326361
)

0 commit comments

Comments
 (0)