Skip to content

feat(flexmf): add Neural Collaborative Filtering (NCF) model#1093

Open
danhdanhtuan0308 wants to merge 4 commits into
lenskit:mainfrom
danhdanhtuan0308:feature/add-ncf-model
Open

feat(flexmf): add Neural Collaborative Filtering (NCF) model#1093
danhdanhtuan0308 wants to merge 4 commits into
lenskit:mainfrom
danhdanhtuan0308:feature/add-ncf-model

Conversation

@danhdanhtuan0308
Copy link
Copy Markdown

No description provided.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 9, 2026

Codecov Report

❌ Patch coverage is 97.14286% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.71%. Comparing base (99bd2cb) to head (9bd4530).
⚠️ Report is 41 commits behind head on main.

Files with missing lines Patch % Lines
src/lenskit/flexmf/_ncf.py 97.10% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1093      +/-   ##
==========================================
+ Coverage   89.64%   89.71%   +0.06%     
==========================================
  Files         240      248       +8     
  Lines       16488    16807     +319     
==========================================
+ Hits        14781    15078     +297     
- Misses       1707     1729      +22     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Member

@mdekstrand mdekstrand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got some comments on inheritance and a question about some unfamiliar PyTorch semantics, but there's a higher-level question: would it make the most sense to make this a separate class, or to make it a set of configuration options on the implicit scorer?

Or, alternatively, to make it more fully extend the implicit scorer, and reuse the implicit scorer's model as-is, combining it with a separate model/module implementing the MLP tower, and a third that combines the first two?

Comment thread src/lenskit/flexmf/_ncf.py Outdated
Comment on lines +23 to +32
class FlexMFNCFConfig(FlexMFConfigBase):
"""
Configuration for NCF (Neural Collaborative Filtering) Scorer.

Stability:
Experimental
"""

gmf_embedding_size: PositiveInt = 8
mlp_embedding_size: PositiveInt = 8
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current design, this class will inherit embedding_size from FlexMFConfigBase, but then not use it in favor of these two embedding sizes. Is there one of the embedding sizes that it would make sense to set to embedding_size, and then just have a different name for a second one?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking adding mlp_layers and embedding_size to FlexMFimplicitConfig and extend FlexMFModel to to optional include the MLP tower which will make one scorer class handles both MF and NCF

Comment thread src/lenskit/flexmf/_ncf.py Outdated
Comment on lines +35 to +50
loss: ImplicitLoss = "logistic"
negative_strategy: NegativeStrategy | None = None
negative_count: PositiveInt = 4
positive_weight: float = 1.0

user_bias: bool | None = False
item_bias: bool = False
convolution_layers: int = 0

def selected_negative_strategy(self) -> NegativeStrategy:
if self.negative_strategy is not None:
return self.negative_strategy
elif self.loss == "warp":
return "misranked"
else:
return "uniform"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of copied code from the implicit model. Is there a reasonable way to enable more reuse?

(Also see the top-level review comment, though — that might be a cleaner solution to a whole set of problems.)

Copy link
Copy Markdown
Author

@danhdanhtuan0308 danhdanhtuan0308 May 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I will change this

Comment on lines +119 to +120
out = torch.cat([gmf_out, mlp_out], dim=-1)
score = self.prediction(out).squeeze(-1)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this do?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It concatenates the GMF output and MLP outputs to produce a single score per user-item pair.

Comment thread src/lenskit/flexmf/_ncf.py Outdated
return "uniform"


class FlexMFNCFModel(FlexMFModel):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment on inheritance as above - we're inheriting FlexMFModel, but then the attributes of this class are not a superset of the attributes of the base class. As a general rule, when we are inheriting a class, the derived class should extend the base class, not silently remove parts of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants