Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@OmarManzoor
Copy link
Contributor

@OmarManzoor OmarManzoor commented Nov 24, 2025

Reference Issues/PRs

Follow up of #31856 (comment)

What does this implement/fix? Explain your changes.

  • Fixes the formulation of alpha in SGDOneClassSVM by using alpha = nu (instead of alpha = nu / 2)

Any other comments?

CC: @antoinebaker

@github-actions
Copy link

github-actions bot commented Nov 24, 2025

✔️ Linting Passed

All linting checks passed. Your pull request is in excellent shape! ☀️

Generated for commit: 383e0bd. Link to the linter CI: here

@OmarManzoor OmarManzoor changed the title FIX Correct the formulation of SGDOneClassSVM FIX Correct the formulation of SGDOneClassSVM Nov 24, 2025
@OmarManzoor
Copy link
Contributor Author

OmarManzoor commented Nov 24, 2025

@antoinebaker Could you kindly have a look? There is one test that is still failing i.e. test_sgd_oneclass

def test_sgd_oneclass():
# Test fit, decision_function, predict and score_samples on a toy
# dataset
X_train = np.array([[-2, -1], [-1, -1], [1, 1]])
X_test = np.array([[0.5, -2], [2, 2]])
clf = SGDOneClassSVM(
nu=0.5, eta0=1, learning_rate="constant", shuffle=False, max_iter=1
)
clf.fit(X_train)
assert_allclose(clf.coef_, np.array([-0.125, 0.4375]))
assert clf.offset_[0] == -0.5

but that has more to do with matching exact coefficients which will be different now.
If you think the changes look okay we can update this test and update the user guide to finalize this PR.

@OmarManzoor OmarManzoor changed the title FIX Correct the formulation of SGDOneClassSVM FIX Correct the formulation of alpha in SGDOneClassSVM Nov 25, 2025
@antoinebaker
Copy link
Contributor

Thanks for the follow up @OmarManzoor ! For sure I'll take a look at your PR soon. I need first to refresh my memory on the mathematical formulation :)

@OmarManzoor
Copy link
Contributor Author

Thanks for the follow up @OmarManzoor ! For sure I'll take a look at your PR soon. I need first to refresh my memory on the mathematical formulation :)

Thank you!

Copy link
Contributor

@antoinebaker antoinebaker left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @OmarManzoor ! Here a few comments.

Y_32 = np.array(Y, dtype=np.float32)

sgd_64 = SGDEstimator(max_iter=20)
max_iter = 18 if SGDEstimator == SGDOneClassSVM else 20
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need max_iter=18 for SGDOneClassSVM ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It converges quicker I think. At 20 the first value is quite different.

Copy link
Member

Choose a reason for hiding this comment

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

Isn't that a bit worrying? Generally, shouldn't things be stable when we increase max_iter?

Copy link
Contributor Author

@OmarManzoor OmarManzoor Dec 15, 2025

Choose a reason for hiding this comment

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

We actually don't get to a specific solution but wander around it. In some iterations the coefs converge quite close whereas in some other iterations one of them mismatch like the following:

E       Max absolute difference among violations: 2.56630536e-09
E       Max relative difference among violations: 0.99999999
E        ACTUAL: array([-2.868070e-17,  3.157518e-02])
E        DESIRED: array([-2.566305e-09,  3.157517e-02], dtype=float32)

or

E       Mismatched elements: 1 / 2 (50%)
E       Max absolute difference among violations: 7.6989162e-09
E       Max relative difference among violations: 1.
E        ACTUAL: array([-2.868070e-17,  3.157518e-02])
E        DESIRED: array([7.698916e-09, 3.157517e-02], dtype=float32)

But the mismatched elements are way too small and I think float64 can be expected to give much more precise results than float32. If we want to keep the same number of iterations i.e. 20 then an atol of 1e-8 should be good enough I think. Otherwise I adjusted the test to use 22 iterations with shuffle=False and we don't need to use any tolerance.

@antoinebaker
Copy link
Contributor

There is a small related typo in the doc (1/2 missing in the second equation)

- :math:`L_2` norm: :math:`R(w) := \frac{1}{2} \sum_{j=1}^{m} w_j^2 = ||w||_2^2`,

It should be:

- :math:`L_2` norm: :math:`R(w) := \frac{1}{2} \sum_{j=1}^{m} w_j^2 =  \frac{1}{2} ||w||_2^2`,

@OmarManzoor
Copy link
Contributor Author

@antoinebaker Do we need to add anything else in this PR?

We already have some convergence tests here:

def test_sgd_oneclass_convergence():
# Check that the optimization does not end early and that the stopping criterion
# is working. Non-regression test for #30027
for nu in [0.1, 0.5, 0.9]:
# no need for large max_iter
model = SGDOneClassSVM(
nu=nu, max_iter=100, tol=1e-3, learning_rate="constant", eta0=1e-3
)
model.fit(iris.data)
# 6 is the minimal number of iterations that should be surpassed, after which
# the optimization can stop
assert model.n_iter_ > 6

Also I think it's difficult to match the offset_ between the SGDOneClassSVM and the Linear OneClassSVM

@OmarManzoor
Copy link
Contributor Author

Closing this for now as I am unsure about this.

@OmarManzoor OmarManzoor closed this Dec 5, 2025
@OmarManzoor OmarManzoor deleted the fix_sgd_one_class_svm branch December 5, 2025 12:55
@antoinebaker
Copy link
Contributor

Well it seems that the offset check uncovered a new bug, as it is already present in main.
Maybe we can skip this check for now, do a dedicated issue/PR for the offset, and merge this PR as is ?

@OmarManzoor
Copy link
Contributor Author

That should be okay 👍 but I am not sure if we can match the offsets. I tried with other make_classification as well and only in one case was I able to get them close enough, in all other cases they just weren't close. I think you would have a better opinion about this though 😄

@OmarManzoor
Copy link
Contributor Author

@antoinebaker Should we then merge this PR with the current changes?

@antoinebaker
Copy link
Contributor

@antoinebaker Should we then merge this PR with the current changes?

I would say so. @OmarManzoor could you reopen this PR so that I can approve the changes ? Hopefully a second reviewer can approve it too.

Could you also open an issue for the offset ?

@OmarManzoor OmarManzoor restored the fix_sgd_one_class_svm branch December 9, 2025 14:42
@lorentzenchr
Copy link
Member

While I think the math is correct, our doc certainly is not:

I‘d like to see a simple test using a scipy.optimize guctuon that shows that we actually minimize the right objective function.

@OmarManzoor
Copy link
Contributor Author

@lorentzenchr Do you mean the formulation in main is correct or the one in this PR (leaving the docs aside right now)?

@lorentzenchr
Copy link
Member

The formula of this PR (nu=alpha) seems correct. For the implementation I don’t know. Therefore my proposal for a test.

@OmarManzoor
Copy link
Contributor Author

@lorentzenchr I tried my best to add a test comparing with scipy minimize using LBFGS but I am not sure exactly sure if the test is as expected. Can you please have a look and verify?

Copy link
Member

@lorentzenchr lorentzenchr left a comment

Choose a reason for hiding this comment

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

Yes, the test is what I meant and proofs that this PR does the right thing.

@OmarManzoor
Copy link
Contributor Author

@lorentzenchr Do you think we can merge this now?

@lorentzenchr
Copy link
Member

Do you think we can merge this now?

Almost, just a few nitpicks left.

@lorentzenchr lorentzenchr enabled auto-merge (squash) December 23, 2025 14:55
@lorentzenchr
Copy link
Member

@OmarManzoor Thanks for this PR. Always a pleasure to work with you on PRs.

@OmarManzoor
Copy link
Contributor Author

@lorentzenchr Thank you for the review and guidance.

scipy_output = minimize(
objective,
w0,
method="Nelder-Mead",
Copy link
Member

Choose a reason for hiding this comment

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

No action required, just fyi: Method "Powell" could be worth a trial for such a non-smooth problem.

@lorentzenchr lorentzenchr merged commit eec13cc into scikit-learn:main Dec 23, 2025
38 checks passed
@github-project-automation github-project-automation bot moved this from In progress to Done in Labs Dec 23, 2025
@OmarManzoor OmarManzoor deleted the fix_sgd_one_class_svm branch December 23, 2025 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cython module:linear_model Waiting for Second Reviewer First reviewer is done, need a second one!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants