Skip to content
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

Closed
wants to merge 9 commits into from

Conversation

hhbyyh
Copy link
Contributor

@hhbyyh hhbyyh commented Jun 15, 2017

What changes were proposed in this pull request?

convert LinearSVC to new aggregator framework

How was this patch tested?

existing unit test.

@hhbyyh
Copy link
Contributor Author

hhbyyh commented Jun 15, 2017

Will send further update after #18305 merged.

@SparkQA
Copy link

SparkQA commented Jun 15, 2017

Test build #78118 has finished for PR 18315 at commit 071ce88.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@sethah
Copy link
Contributor

sethah commented Jun 16, 2017

Thanks for this pr @hhbyyh. I think we need to add a test suite for the aggregator, but since #18305 needs to be merged first, it's fine to wait. If you wouldn't mind helping to review #18305, that would be great :)

@sethah
Copy link
Contributor

sethah commented Jul 26, 2017

ping! #18305 was merged. This can proceed.

@SparkQA
Copy link

SparkQA commented Jul 27, 2017

Test build #79981 has finished for PR 18315 at commit c8228fb.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

val interceptArray = Array(2.0)
val agg = getNewAggregator(instances, Vectors.dense(coefArray ++ interceptArray),
fitIntercept = true)
withClue("LogisticAggregator does not support negative instance weights") {
Copy link
Contributor

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
Copy link
Contributor

@MLnick MLnick Jul 27, 2017

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

@MLnick
Copy link
Contributor

MLnick commented Jul 27, 2017

Made a few minor comments, overall looks good.

assert(loss ~== agg.loss relTol 0.01)
assert(gradient ~== agg.gradient relTol 0.01)
}

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. Added.

@SparkQA
Copy link

SparkQA commented Jul 28, 2017

Test build #80028 has finished for PR 18315 at commit 94e0250.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@hhbyyh
Copy link
Contributor Author

hhbyyh commented Jul 29, 2017

Thanks for the review. Updated to address the comments.

@SparkQA
Copy link

SparkQA commented Aug 15, 2017

Test build #80661 has finished for PR 18315 at commit 94e0250.

  • This patch fails due to an unknown error code, -9.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yanboliang
Copy link
Contributor

Jenkins, test this please.

@yanboliang
Copy link
Contributor

@hhbyyh Would you mind to remove WIP in the PR title if it's applicable. I'll take a look soon. Thanks.

@SparkQA
Copy link

SparkQA commented Aug 16, 2017

Test build #80719 has finished for PR 18315 at commit 94e0250.

  • This patch fails due to an unknown error code, -9.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yanboliang
Copy link
Contributor

Jenkins, test this please.

@SparkQA
Copy link

SparkQA commented Aug 16, 2017

Test build #80739 has finished for PR 18315 at commit 94e0250.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

Copy link
Contributor

@yanboliang yanboliang left a 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") {
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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)))
Copy link
Contributor

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.

Copy link
Contributor Author

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") {
Copy link
Contributor

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") {
Copy link
Contributor

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(
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK

@yanboliang
Copy link
Contributor

yanboliang commented Aug 17, 2017

@hhbyyh I think it's ready to move WIP in the PR title. We can move forward after addressing the comments. Thanks.

@hhbyyh hhbyyh changed the title [SPARK-21108] [ML] [WIP] convert LinearSVC to aggregator framework [SPARK-21108] [ML] convert LinearSVC to aggregator framework Aug 20, 2017
@SparkQA
Copy link

SparkQA commented Aug 20, 2017

Test build #80884 has finished for PR 18315 at commit 3d7bfce.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

assert(gradient ~== agg.gradient relTol 0.01)
}

test("check with zero standard deviation") {
Copy link
Contributor

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.

Copy link
Contributor

@yanboliang yanboliang left a comment

Choose a reason for hiding this comment

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

Looks good overall, only some minor comments. @hhbyyh Could you address them and @sethah 's comment, then we can move forward. Thanks.


/**
* HingeAggregator computes the gradient and loss for loss function ("hinge" or
* "squared_hinge", as used in binary classification for instances in sparse or dense
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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 ?

Copy link
Contributor

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)
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

@hhbyyh
Copy link
Contributor Author

hhbyyh commented Aug 23, 2017

Thanks for the comment @sethah and @yanboliang . Updated according to the comments.

@SparkQA
Copy link

SparkQA commented Aug 23, 2017

Test build #81048 has finished for PR 18315 at commit 4173084.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

Copy link
Contributor

@yanboliang yanboliang left a 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)
Copy link
Contributor

Choose a reason for hiding this comment

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

Okay.

@yanboliang
Copy link
Contributor

Merged into master. Thanks for all.

@asfgit asfgit closed this in f3676d6 Aug 25, 2017
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.

6 participants