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

Skip to content

[MRG] Fixing label clamping for LabelPropagation #6727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from

Conversation

boechat107
Copy link

@boechat107 boechat107 commented Apr 27, 2016

Referenced issues

This PR comes to fix #3550 and #5774. It's similar to PR #3758, which seems to be abandoned.

Important note: my development files hack-dev/* would be removed before merging this PR.

Explanations

From the comments in the PR #3758, this PR improves the documentation of the parameter alpha, deprecates it for LabelPropagation and fix the label clamping (the credit is all to @musically-ut).

From [1] and [3], we can see that LabelPropagation doesn't have a clamping factor. In the case of LabelSpreading, from [2] and [3] we have

image

which means that alpha = 0 keeps the initial label information Ŷ(0). As suggested by @amueller in #3758, this should be the only possible value for LabelPropagation.

In the current implementation, line 239, alpha = 0 would not propagate label information to the unlabeled instances, but would propagate label information to the labeled instances. I saw this exact behavior with my data, but I couldn't find yet a simple test case to catch it.

Some additional comments

In #5774, @musically-ut discussed a very important point about the current implementation, the graph construction. There is no mention about directed graphs in the referenced papers, but it's what the current implementation does (line 137). Maybe a specific issue should be created for this.

Referenced papers

[1] Zhu, Xiaojin, and Zoubin Ghahramani. Learning from labeled and unlabeled data with label propagation. Technical Report CMU-CALD-02-107, Carnegie Mellon University, 2002.
[2] Zhou, Dengyong, et al. "Learning with local and global consistency." Advances in neural information processing systems 16.16 (2004): 321-328.
[3] Bengio, Yoshua, Olivier Delalleau, and Nicolas Le Roux. "Label propagation and quadratic criterion." Semi-supervised learning (2006): 193-216.

@amueller
Copy link
Member

Sorry for not reviewing this so far. I think it's an important fix. The maintainers are pretty busy :(

@amueller amueller added this to the 0.19 milestone Aug 31, 2016
@boechat107
Copy link
Author

No worries, I understand. ;-)

@jnothman
Copy link
Member

This needs some love soon.

@amueller amueller added the Bug label Dec 6, 2016
@amueller
Copy link
Member

amueller commented Dec 7, 2016

Can you elaborate on the directed graph issue? The construction creates a symmetric graph matrix, right?

@musically-ut
Copy link
Contributor

@amueller I don't think the default implementation of kneighbors_graph creates symmetric graphs (it would be very odd if it did, since k-neighbors may not be symmetric).

I corrected by making the connectivity matrix symmetric explicitly: https://github.com/musically-ut/semi_supervised/blob/master/semi_supervised/label_propagation.py#L136

Does this answer your question?

Copy link
Member

@amueller amueller left a comment

Choose a reason for hiding this comment

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

Can you please add the test suggested in #5774 (at least)?

@@ -291,7 +292,7 @@ class LabelPropagation(BaseLabelPropagation):
Parameter for knn kernel

alpha : float
Clamping factor
DEPRECATED: It's internally fixed to zero and it'll be removed in 0.19
Copy link
Member

Choose a reason for hiding this comment

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

Deprecated in 0.19 and removed in 0.21. You should raise a deprecation warning if it is set.

alpha=.0, max_iter=30, tol=1e-3, n_jobs=1):
# alpha is deprecated for LabelPropagation because it doesn't have any
# theoretical meaning (from the reference paper).
alpha = .0
Copy link
Member

Choose a reason for hiding this comment

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

you can just put alpha=0 in the line below, and set alpha above to None and if alpha is not None raise a DeprecationWarning.

@amueller
Copy link
Member

amueller commented Dec 7, 2016

@musically-ut yeah, sorry, of course. I was thinking about distance graphs... getting late over here...

@amueller
Copy link
Member

amueller commented Dec 7, 2016

We might want to think about renaming alpha to something more intuitive to force users to rethink what they're doing....

@musically-ut
Copy link
Contributor

@amueller, no worries. Great to see progress on this though!

@musically-ut
Copy link
Contributor

@amueller I'm not inclined to change the name of the parameter, it is called alpha in all the papers regarding semi-supervised learning that I have seen.

@amueller
Copy link
Member

amueller commented Dec 7, 2016

@musically-ut we should have fixed this a looong long time ago. Do you want to open a separate issue about symmetry?

@musically-ut
Copy link
Contributor

@amueller Sure, I can do that.

IIRC, I wasn't sure what the option y was doing and we were facing a dearth of test cases which would fail in one case while work in the other, to avoid regression.

@amueller
Copy link
Member

amueller commented Dec 7, 2016

We usually try to avoid Greek letter parameters and prefer natural language parameters, though not everywhere (if I'd rewrite the linear models, I'd call alpha "regularization"). I don't have a strong opinion though.

The question is whether the parameters is called alpha and the docs say clamping factor or the parameter is called clamping_factor and the docs say alpha in the literature.
Actually "clamping factor" is a pretty bad description because it's the inverse of clamping. Maybe "spreading factor"?

@musically-ut
Copy link
Contributor

@amueller spreading_factor sounds okay. However, maintaining backwards compatibility in this case will become slightly tricky.

@amueller
Copy link
Member

amueller commented Dec 7, 2016

Deprecating parameters is not really tricky, but I'm fine with keeping alpha. I was also thinking about the docs, which currently say "clamping factor" which is bad.

@boechat107
Copy link
Author

I'm going to apply the suggestions regarding the deprecation of alpha, but I'll leave it like alpha for now.

@boechat107
Copy link
Author

Now that the tests are failing, I remember why I didn't use alpha=None.

Suggestions?

"Deprecated in 0.19 and it's going to be removed in 0.21.",
DeprecationWarning
)
alpha = .0
Copy link
Member

Choose a reason for hiding this comment

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

don't put this here. Put it where it's used

Copy link
Author

Choose a reason for hiding this comment

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

Ok, I'll set the alpha of the line below to .0.

But what about the alpha of the constructor above? You said it should be set to None, but the tests fail because the two constructors have different default values (None and 0).

What am I missing?

Copy link
Member

Choose a reason for hiding this comment

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

It shouldn't be changed in the constructor, it should be replaced in fit

Copy link
Author

Choose a reason for hiding this comment

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

Should the fit function behave differently for LabelPropagation and LabelSpreading? I mean, should they have different implementations of fit?

Copy link
Member

Choose a reason for hiding this comment

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

It's a bit unfortunate but it looks like LabelPropagation needs to get it's own fit which calls
super(LabelPropagation, self).fit. But before, it checks whether alpha is set and raises a deprecation warning.
Then in BaseLabelPropagation you need to introduce a local variable alpha that has the right value. One way to do this is to check isinstance(self, LabelSpreading) - which is not great.
The other would be so set self._alpha = 0 in LabelPropagation.fit
and do alpha = getattr(self, "_alpha", self.alpha) in BaseLabelPropagation.fit.

Copy link
Member

Choose a reason for hiding this comment

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

If you have a nicer way, that's also fine ;)

Copy link
Author

Choose a reason for hiding this comment

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

Considering the testing restrictions, I can't see a any better solution. I chose to adopt your second suggestion.

This solution isn't great, but it sets the correct value for alpha
without violating the restrictions imposed by the tests.
Copy link
Member

@jnothman jnothman left a comment

Choose a reason for hiding this comment

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

Ideally, we would have a test that the DeprecationWarning fires when it is meant to and when not.

Please add a bug fix entry to what's new.

@@ -291,7 +295,7 @@ class LabelPropagation(BaseLabelPropagation):
Parameter for knn kernel

alpha : float
Clamping factor
DEPRECATED: Deprecated in 0.19 and it's going to be removed in 0.21.
Copy link
Member

Choose a reason for hiding this comment

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

can use sphinx's .. deprecated

Copy link
Author

Choose a reason for hiding this comment

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

OK, I'll check it out.

# theoretical meaning (from the reference paper). Look at PR 6727.
if self.alpha is not None:
warnings.warn(
"Deprecated in 0.19 and it's going to be removed in 0.21.",
Copy link
Member

Choose a reason for hiding this comment

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

State "Parameter alpha was ... and will be ..."

Copy link
Author

Choose a reason for hiding this comment

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

I agree.

warnings.warn(
"Deprecated in 0.19 and it's going to be removed in 0.21.",
DeprecationWarning
)
Copy link
Member

Choose a reason for hiding this comment

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

Should we be setting _alpha to the user-provided alpha until deprecation is compeleted? Otherwise our deprecation warning should state that alpha is being ignored.

Copy link
Author

Choose a reason for hiding this comment

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

In the case of LabelPropagation, setting a different alpha value is not something supported by the algorithm's description in the cited references.

def fit(self, X, y):
# alpha is deprecated for LabelPropagation because it doesn't have any
# theoretical meaning (from the reference paper). Look at PR 6727.
if self.alpha is not None:
Copy link
Member

Choose a reason for hiding this comment

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

you need to implement __init__ such that alpha=None by default and this message only appears when a non-default value is used.

Copy link
Author

@boechat107 boechat107 Jan 9, 2017

Choose a reason for hiding this comment

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

To follow your suggestion and pass the tests, I would need to change the __init__ of BaseLabelPropagation, which is probably not what we want since LabelSpreading can really use different alpha values.

This problem was discussed with @amueller before, in his code review.

Do you have another suggestion?

Copy link
Member

Choose a reason for hiding this comment

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

I don't see how. I have no problem putting a new __init__ in LabelPropagation which passes alpha=None to BaseLabelPropagation.__init__, while still passing the tests.

@jnothman jnothman modified the milestones: 0.18.2, 0.19 Jan 9, 2017
@amueller amueller modified the milestones: 0.18.2, 0.19 Jan 11, 2017
Copy link
Member

@jnothman jnothman left a comment

Choose a reason for hiding this comment

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

I'd like to see this merged in the coming release!

def fit(self, X, y):
# alpha is deprecated for LabelPropagation because it doesn't have any
# theoretical meaning (from the reference paper). Look at PR 6727.
if self.alpha is not None:
Copy link
Member

Choose a reason for hiding this comment

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

I don't see how. I have no problem putting a new __init__ in LabelPropagation which passes alpha=None to BaseLabelPropagation.__init__, while still passing the tests.

@@ -381,7 +402,10 @@ class LabelSpreading(BaseLabelPropagation):
parameter for knn kernel

alpha : float
clamping factor
Clamping factor [0, 1], it specifies the relative amount of the
Copy link
Member

Choose a reason for hiding this comment

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

A bit more verbose for clarity: "Clamping factor, in [0, 1], specifies the extent to which a sample's label should derive from its neighbors in preference to its initial label."

Copy link
Author

Choose a reason for hiding this comment

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

By a new __init__ do you mean something like this?

What should be the default value for alpha in BaseLabelPropagation.__init__? What about LabelPropagation.__init__? The tests were failing because I ended up with alpha=1 in BaseLabelPropagation.__init__ and alpha=None in LabelPropagation.__init__, i.e., this constructor set a different default value for alpha than the former.

@jnothman
Copy link
Member

jnothman commented May 29, 2017 via email

@jnothman
Copy link
Member

The point is that you should still not have to modify anything in init, and leave warning until fit.

@boechat107
Copy link
Author

If I don't modify __init__, the warning on LabelPropagation.fit will be echoed by default. Is this what you meant?

@jnothman
Copy link
Member

jnothman commented May 30, 2017 via email

@jnothman
Copy link
Member

It seems you'll need to resolve a merge conflict.

I've now patched this to my liking. Could we get at least one other review? @amueller?

@boechat107
Copy link
Author

boechat107 commented Jun 13, 2017

Yes, I noticed. I'm gonna fix it.

X, y = make_classification(n_samples=100)
y[::3] = -1
lp_default = label_propagation.LabelPropagation()
lp_default_y = assert_no_warnings(lp_default.fit, X, y).transduction_
Copy link
Member

Choose a reason for hiding this comment

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

This is getting a warning (although not the one we're concerned about) and the assertion fails:


======================================================================
FAIL: sklearn.semi_supervised.tests.test_label_propagation.test_alpha_deprecation
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/miniconda/envs/testenv/lib/python3.6/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/home/travis/build/scikit-learn/scikit-learn/sklearn/semi_supervised/tests/test_label_propagation.py", line 71, in test_alpha_deprecation
    lp_default_y = assert_no_warnings(lp_default.fit, X, y).transduction_
  File "/home/travis/build/scikit-learn/scikit-learn/sklearn/utils/testing.py", line 230, in assert_no_warnings
    ', '.join(str(warning) for warning in w)))
AssertionError: Got warnings when calling fit: [{message : RuntimeWarning('underflow encountered in exp',), category : 'RuntimeWarning', filename : '/home/travis/build/scikit-learn/scikit-learn/sklearn/metrics/pairwise.py', lineno : 837, line : None}]

I've realised that I didn't fix the random_state in make_classification. We should do that. But I also haven't investigated why we should be getting exp underflow. Do you know?

Copy link
Author

Choose a reason for hiding this comment

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

It is not something that comes to mind right now. I'll have look at it.

Copy link
Member

Choose a reason for hiding this comment

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

I don't seem to get that warning on my system, but all Travis runs get it.

Could I suggest you try setting the kernel='knn' in the test here, and we'll see if that works?

@jnothman
Copy link
Member

I've made a couple of finishing touches at #9192.

@ogrisel
Copy link
Member

ogrisel commented Jun 30, 2017

Closing in favor of #9239.

@ogrisel ogrisel closed this Jun 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Labels don't stay clamped in LabelPropagation
5 participants