-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-17139][ML] Add model summary for MultinomialLogisticRegression #15435
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
Conversation
|
@sethah This pr seems to be discussed about several details, I am pleasure to hear your opinion, thanks! |
|
Test build #66748 has finished for PR 15435 at commit
|
|
I'll try to take a look before too long. For now, I see there are no tests, could you please add tests, using the summary tests for binary classification as a guide? Thanks! |
|
Test build #66804 has finished for PR 15435 at commit
|
|
Test build #66805 has finished for PR 15435 at commit
|
|
So I've been reading through some of the history with logistic regression summaries. There was a lot of discussion on how to design the abstractions for this, here and here. I'm reposting some of the relevant snippets (I will comment on them in a follow up): "We'll need to use traits to fix the multiple inheritance issue:" "Are we planning to have a MulticlassLogisticRegressionSummary inheriting from LogisticRegressionSummary in the future because without that I'm unable to understand how using a trait would help since there is no access to the predictions dataframe." "Yes, MulticlassLogisticRegressionSummary should be analogous to the binary version, with both inheriting from LogisticRegressionSummary." ... "Synced with @jkbradley offline. Summary: We should not require end users to perform any sort of downcasting in the stabilized API. This is OK for now since the API is still experimental. Eventually we could provide two methods, a summary : LogisticRegressionSummary and a binarySummary : BInaryLogisticRegressionSummary which errors when called on a multiclass LRModel. This will be easy to implement because summary is returning the base LogisticRegressionSummary class so will not require any public API change." |
|
So, based on my interpretation of this and how this can actually work, we need to have: sealed trait LogisticRegressionSummary
sealed trait LogisticRegressionTrainingSummary
class MulticlassLogisticRegressionSummary extends LogisticRegressionSummary
class MulticlassLogisticRegressionTrainingSummary extends MulticlassLogisticRegressionSummary with LogisticRegressionTrainingSummary
class BinaryLogisticRegressionSummary extends MulticlassLogisticRegressionSummary
class BinaryLogisticRegressionTrainingSummary extends BinaryLogisticRegressionSummary with LogisticRegressionTrainingSummaryThen, in def summary: LogisticRegressionTrainingSummary
def binarySummary: BinaryLogisticRegressionTrainingSummary = summary match {
case b: BinaryLogisticRegressionTrainingSummary => b
case _ => throw new Exception()
}And we avoid downcasting in the summary case since |
|
@sethah Good suggestion. code updated, thanks! |
|
Test build #67017 has finished for PR 15435 at commit
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-double numeric datatypes other than LongType maybe also needed to test. Any thoughts? @sethah
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhengruifeng Thanks for carefule review, because in the Summary class code we use labelValue.toDouble to do the cast so that we use one type LongType as the test is OK IMO, is there some possible case that LongType cast succeed but other legal numeric value will fails?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, from a completeness standpoint I agree that it's better to test all the types that it's intended to work for. However, since it's just calling cast under the hood, it does seem a bit redundant. I'm ok leaving it as is, but I don't feel strongly about it.
|
re-ping @jkbradley Would be great to get your thoughts on the above discussion since you were involved in the original design |
|
@sethah jkbradley seems not online recently we can invite @yanboliang to give some advice. |
|
@sethah Thanks for pinging, and for bringing up those old design discussions (which are still the best options I know of). @WeichenXu123 sorry I've been slow to respond; I'm trying to keep up! I'll take a look at the PR. UPDATE: Regarding the class hierarchy above, the only one I'm not sure about is
I'm unsure here. What do you think? |
|
@jkbradley Thanks for your input. I'm happy to review this, but wanted to get clarification before proceeding. I can take a look in the next couple of days. |
jkbradley
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sethah Sorry, didn't want to stomp on your review. I had started reviewing, but I'll just go ahead and submit these partial review comments. Thanks!
And thanks @WeichenXu123 for adding this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd actually just remove these 2 lines since they don't say anything useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here: remove these 2 lines since they don't say anything useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add Scala doc, especially saying that this will throw an exception when numClasses > 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use SparkException here; that's primarily for failures within Spark jobs AFAIK. I guess I'd use RuntimeException.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the current error doesn't really help the user. How about "Cannot create a binarySummary for a non-binary model (with numClasses = $numClasses). Use multinomialSummary instead."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add doc here too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only place where you need the probability2predictionUDF. Rather than passing a UDF or the model, I'd like us to modify findSummaryModelAndProbabilityCol to also set predictionCol if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These since tags should all be 2.1.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need these 2 lines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add scala doc for all of these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix indentation
|
@jkbradley No problems at all, it's always better to have more reviewers. |
|
Now I am considering elemating the currently, the base interface And, whether should we make |
|
It seems that many metrics in |
|
@WeichenXu123 About breaking APIs: I'm OK adding predictionCol to LogisticRegressionSummary, even though I agree it technically breaks a public API:
So I don't see a way this could break a non-hack use case, but let me know if I'm not thinking of an edge case. @zhengruifeng I'm OK refactoring to create a MulticlassClassificationSummary (or MulticlassSummary?) now or later. Now does sound better. Let's keep it |
|
All right. I will create a new class And thanks @zhengruifeng for good suggestion~ |
1bf5aa4 to
9c0e3fe
Compare
|
Test build #68283 has finished for PR 15435 at commit
|
9c0e3fe to
3b459f9
Compare
|
Test build #68285 has finished for PR 15435 at commit
|
3b459f9 to
f0523f9
Compare
|
Test build #68864 has finished for PR 15435 at commit
|
f0523f9 to
79c5dda
Compare
|
Test build #68865 has finished for PR 15435 at commit
|
46d49c9 to
d338a94
Compare
|
Test build #80926 has finished for PR 15435 at commit
|
|
Jenkins, test this please. |
|
Test build #80928 has finished for PR 15435 at commit
|
d338a94 to
b6cde56
Compare
|
Test build #80932 has finished for PR 15435 at commit
|
|
Test build #80947 has finished for PR 15435 at commit
|
|
Jenkins, test this please. |
|
Test build #80954 has finished for PR 15435 at commit
|
| } | ||
|
|
||
| /** | ||
| * Returns the sequence of labels in ascending order |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify: "Returns the sequence of labels in ascending order. This order matches the order used in metrics which are specified as arrays over labels, e.g., truePositiveRateByLabel."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see; I think this should work.
jkbradley
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has there been discussion of removing the Experimental tags from the summary types? I'd prefer to leave them since there is still talk of further refactoring (e.g., generalizing out a ClassificationSummary across different models).
Other than that + my 1 small comment, this looks ready. @sethah and @yanboliang any more comments?
Thanks everyone!
|
I agree keeping the |
|
Test build #81022 has finished for PR 15435 at commit
|
|
OK, then let's reinstate the Experimental tags. @WeichenXu123 could you please mark the 4 public summary traits Experimental? |
|
Test build #81111 has finished for PR 15435 at commit
|
|
Jenkins test this please |
|
Test build #81119 has finished for PR 15435 at commit
|
|
Jenkins test this please |
|
Test build #81124 has finished for PR 15435 at commit
|
|
LGTM |
What changes were proposed in this pull request?
Add 4 traits, using the following hierarchy:
LogisticRegressionSummary
LogisticRegressionTrainingSummary: LogisticRegressionSummary
BinaryLogisticRegressionSummary: LogisticRegressionSummary
BinaryLogisticRegressionTrainingSummary: LogisticRegressionTrainingSummary, BinaryLogisticRegressionSummary
and the public method such as
def summaryonly return trait type listed above.and then implement 4 concrete classes:
LogisticRegressionSummaryImpl (multiclass case)
LogisticRegressionTrainingSummaryImpl (multiclass case)
BinaryLogisticRegressionSummaryImpl (binary case).
BinaryLogisticRegressionTrainingSummaryImpl (binary case).
How was this patch tested?
Existing tests & added tests.