-
Notifications
You must be signed in to change notification settings - Fork 28.5k
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
[SPARK-21108] [ML] convert LinearSVC to aggregator framework #18315
Conversation
Will send further update after #18305 merged. |
Test build #78118 has finished for PR 18315 at commit
|
ping! #18305 was merged. This can proceed. |
Test build #79981 has finished for PR 18315 at commit
|
val interceptArray = Array(2.0) | ||
val agg = getNewAggregator(instances, Vectors.dense(coefArray ++ interceptArray), | ||
fitIntercept = true) | ||
withClue("LogisticAggregator does not support negative instance weights") { |
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.
nit: LogisticAggregator
-> HingeAggregator
* "squared_hinge", as used in binary classification for instances in sparse or dense | ||
* vector in an online fashion. | ||
* | ||
* Two LinearSVCAggregator can be merged together to have a summary of loss and gradient of |
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.
should be HingeAggregator
here and elsewhere in this class
Made a few minor comments, overall looks good. |
assert(loss ~== agg.loss relTol 0.01) | ||
assert(gradient ~== agg.gradient relTol 0.01) | ||
} | ||
|
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.
The other aggregator tests have one for "zero standard deviation". We should add one 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.
Sure. Added.
Test build #80028 has finished for PR 18315 at commit
|
Thanks for the review. Updated to address the comments. |
Test build #80661 has finished for PR 18315 at commit
|
Jenkins, test this please. |
@hhbyyh Would you mind to remove |
Test build #80719 has finished for PR 18315 at commit
|
Jenkins, test this please. |
Test build #80739 has finished for PR 18315 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.
@hhbyyh Looks good overall, left some minor comments. Thanks.
@@ -173,7 +174,7 @@ class LinearSVCSuite extends SparkFunSuite with MLlibTestSparkContext with Defau | |||
test("sparse coefficients in SVCAggregator") { |
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.
SVCAggregator
-> HingeAggregator
?
|
||
/** | ||
* HingeAggregator computes the gradient and loss for loss function ("hinge" or | ||
* "squared_hinge", as used in binary classification for instances in sparse or dense |
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.
It seems this only support hinge loss currently. BTW, if we support squared hinge in the future, what is the best way? Add a param loss function for HingeAggregator
or just add a new SquaredHingeAggregator
? The later way should be more clear, but with more code.
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 would prefer to use SquaredHingeAggregator. API-wise, it looks more intuitive and consistent to me. We can continue the review in the other LinearSVC PR.
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.
Yeah, I agree you for separate SquaredHingeAggregator
. Then we should remove squared_hinge
from here?
BTW, you missed right parenthesis here.
val regularization = if (regParamL2 != 0.0) { | ||
val shouldApply = (idx: Int) => idx >= 0 && idx < numFeatures | ||
Some(new L2Regularization(regParamL2, shouldApply, | ||
if ($(standardization)) None else Some(featuresStd))) |
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.
Minor: The third argument applyFeaturesStd
is a function rather than an array in semantics:
private[ml] class L2Regularization(
override val regParam: Double,
shouldApply: Int => Boolean,
applyFeaturesStd: Option[Int => Double]) extends DifferentiableRegularization[Vector]
In LiR and LoR, we use a function:
val getFeaturesStd = (j: Int) => if (j >= 0 && j < numFeatures) featuresStd(j) else 0.0
I think either is ok, but it's better to keep consistent with other algorithms. We can change here to use function or change the third argument of L2Regularization
to Option[Array[Double]]
. I'm prefer the former way, what's your opinion? Thanks.
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.
Sure. Will change it to function
} | ||
} | ||
|
||
test("check sizes binomial") { |
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.
Remove binomial
?
} | ||
|
||
|
||
test("check correctness binomial") { |
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.
Ditto.
} | ||
|
||
test("check with zero standard deviation") { | ||
val instancesConstantFeature = Array( |
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.
What about move the dataset to the beginning of this class?
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.
OK
@hhbyyh I think it's ready to move |
Test build #80884 has finished for PR 18315 at commit
|
assert(gradient ~== agg.gradient relTol 0.01) | ||
} | ||
|
||
test("check with zero standard deviation") { |
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.
As we found out in #18896, this test is not thorough enough. We should check all elements of the gradient for correctness.
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.
|
||
/** | ||
* HingeAggregator computes the gradient and loss for loss function ("hinge" or | ||
* "squared_hinge", as used in binary classification for instances in sparse or dense |
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.
Yeah, I agree you for separate SquaredHingeAggregator
. Then we should remove squared_hinge
from here?
BTW, you missed right parenthesis here.
|
||
test("aggregator add method input size") { | ||
val coefArray = Array(1.0, 2.0) | ||
val interceptArray = Array(2.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.
This is different from LogisticRegression
which supports multi-classification, so interceptArray
should be renamed to intercept
?
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.
Changing it to intercept will cause conflict with intercept[IllegalArgumentException]
in the same test. Do you mind if we keep using interceptArray ?
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.
Okay.
|
||
test("negative weight") { | ||
val coefArray = Array(1.0, 2.0) | ||
val interceptArray = Array(2.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.
ditto
Thanks for the comment @sethah and @yanboliang . Updated according to the comments. |
Test build #81048 has finished for PR 18315 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.
LGTM
|
||
test("aggregator add method input size") { | ||
val coefArray = Array(1.0, 2.0) | ||
val interceptArray = Array(2.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.
Okay.
Merged into master. Thanks for all. |
What changes were proposed in this pull request?
convert LinearSVC to new aggregator framework
How was this patch tested?
existing unit test.