-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-20602] [ML]Adding LBFGS optimizer and Squared_hinge loss for LinearSVC #17862
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
Changes from 3 commits
d46e5ed
f7d5559
8a7c10f
4ce0787
c8afc63
3707580
2ffd0eb
2ca5a74
5f7f456
d19f619
0297057
15d611e
a545267
7be6bac
aaf35ec
ea82f35
93f7b68
cec628b
55ce6b9
0f5cad5
bf4d955
1f8e984
a6b4cda
f778f97
0bb5afe
64bc339
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,21 +75,25 @@ class LinearSVCSuite extends SparkFunSuite with MLlibTestSparkContext with Defau | |
| } | ||
|
|
||
| test("Linear SVC binary classification") { | ||
| val svm = new LinearSVC() | ||
| val model = svm.fit(smallBinaryDataset) | ||
| assert(model.transform(smallValidationDataset) | ||
| .where("prediction=label").count() > nPoints * 0.8) | ||
| val sparseModel = svm.fit(smallSparseBinaryDataset) | ||
| checkModels(model, sparseModel) | ||
| LinearSVC.supportedOptimizers.foreach { opt => | ||
| val svm = new LinearSVC().setSolver(opt) | ||
| val model = svm.fit(smallBinaryDataset) | ||
| assert(model.transform(smallValidationDataset) | ||
| .where("prediction=label").count() > nPoints * 0.8) | ||
| val sparseModel = svm.fit(smallSparseBinaryDataset) | ||
| checkModels(model, sparseModel) | ||
| } | ||
| } | ||
|
|
||
| test("Linear SVC binary classification with regularization") { | ||
| val svm = new LinearSVC() | ||
| val model = svm.setRegParam(0.1).fit(smallBinaryDataset) | ||
| assert(model.transform(smallValidationDataset) | ||
| .where("prediction=label").count() > nPoints * 0.8) | ||
| val sparseModel = svm.fit(smallSparseBinaryDataset) | ||
| checkModels(model, sparseModel) | ||
| LinearSVC.supportedOptimizers.foreach { opt => | ||
| val svm = new LinearSVC().setSolver(opt).setMaxIter(10) | ||
| val model = svm.setRegParam(0.1).fit(smallBinaryDataset) | ||
| assert(model.transform(smallValidationDataset) | ||
| .where("prediction=label").count() > nPoints * 0.8) | ||
| val sparseModel = svm.fit(smallSparseBinaryDataset) | ||
| checkModels(model, sparseModel) | ||
| } | ||
| } | ||
|
|
||
| test("params") { | ||
|
|
@@ -112,6 +116,7 @@ class LinearSVCSuite extends SparkFunSuite with MLlibTestSparkContext with Defau | |
| assert(lsvc.getFeaturesCol === "features") | ||
| assert(lsvc.getPredictionCol === "prediction") | ||
| assert(lsvc.getRawPredictionCol === "rawPrediction") | ||
| assert(lsvc.getSolver === "owlqn") | ||
| val model = lsvc.setMaxIter(5).fit(smallBinaryDataset) | ||
| model.transform(smallBinaryDataset) | ||
| .select("label", "prediction", "rawPrediction") | ||
|
|
@@ -154,22 +159,23 @@ class LinearSVCSuite extends SparkFunSuite with MLlibTestSparkContext with Defau | |
|
|
||
| test("linearSVC with sample weights") { | ||
| def modelEquals(m1: LinearSVCModel, m2: LinearSVCModel): Unit = { | ||
| assert(m1.coefficients ~== m2.coefficients absTol 0.05) | ||
| assert(m1.coefficients ~== m2.coefficients absTol 0.07) | ||
| assert(m1.intercept ~== m2.intercept absTol 0.05) | ||
| } | ||
|
|
||
| val estimator = new LinearSVC().setRegParam(0.01).setTol(0.01) | ||
| val dataset = smallBinaryDataset | ||
| MLTestingUtils.testArbitrarilyScaledWeights[LinearSVCModel, LinearSVC]( | ||
| dataset.as[LabeledPoint], estimator, modelEquals) | ||
| MLTestingUtils.testOutliersWithSmallWeights[LinearSVCModel, LinearSVC]( | ||
| dataset.as[LabeledPoint], estimator, 2, modelEquals, outlierRatio = 3) | ||
| MLTestingUtils.testOversamplingVsWeighting[LinearSVCModel, LinearSVC]( | ||
| dataset.as[LabeledPoint], estimator, modelEquals, 42L) | ||
| LinearSVC.supportedOptimizers.foreach { opt => | ||
| val estimator = new LinearSVC().setRegParam(0.02).setTol(0.01).setSolver(opt) | ||
| val dataset = smallBinaryDataset | ||
| MLTestingUtils.testArbitrarilyScaledWeights[LinearSVCModel, LinearSVC]( | ||
| dataset.as[LabeledPoint], estimator, modelEquals) | ||
| MLTestingUtils.testOutliersWithSmallWeights[LinearSVCModel, LinearSVC]( | ||
| dataset.as[LabeledPoint], estimator, 2, modelEquals, outlierRatio = 3) | ||
| MLTestingUtils.testOversamplingVsWeighting[LinearSVCModel, LinearSVC]( | ||
| dataset.as[LabeledPoint], estimator, modelEquals, 42L) | ||
| } | ||
| } | ||
|
|
||
| test("linearSVC comparison with R e1071 and scikit-learn") { | ||
| val trainer1 = new LinearSVC() | ||
| test("linearSVC OWLQN comparison with R e1071 and scikit-learn") { | ||
| val trainer1 = new LinearSVC().setSolver(LinearSVC.OWLQN) | ||
| .setRegParam(0.00002) // set regParam = 2.0 / datasize / c | ||
| .setMaxIter(200) | ||
| .setTol(1e-4) | ||
|
|
@@ -223,6 +229,25 @@ class LinearSVCSuite extends SparkFunSuite with MLlibTestSparkContext with Defau | |
| assert(model1.coefficients ~== coefficientsSK relTol 4E-3) | ||
| } | ||
|
|
||
| test("linearSVC L-BFGS comparison with R e1071 and scikit-learn") { | ||
| val trainer1 = new LinearSVC().setSolver(LinearSVC.LBFGS) | ||
| .setRegParam(0.00003) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we switch to different
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. I can use your help here since I cannot find the theory proof for this case. LR may have the same behavior. Since if I set the optimizer of LR to OWLQN, all the L2 regularization cases will fail. |
||
| .setMaxIter(200) | ||
| .setTol(1e-4) | ||
| val model1 = trainer1.fit(binaryDataset) | ||
|
|
||
| // refer to last unit test for R and python code | ||
| val coefficientsR = Vectors.dense(7.310338, 14.89741, 22.21005, 29.83508) | ||
| val interceptR = 7.440177 | ||
| assert(model1.intercept ~== interceptR relTol 2E-2) | ||
| assert(model1.coefficients ~== coefficientsR relTol 1E-2) | ||
|
|
||
| val coefficientsSK = Vectors.dense(7.24690165, 14.77029087, 21.99924004, 29.5575729) | ||
| val interceptSK = 7.36947518 | ||
| assert(model1.intercept ~== interceptSK relTol 1E-2) | ||
| assert(model1.coefficients ~== coefficientsSK relTol 1E-2) | ||
| } | ||
|
|
||
| test("read/write: SVM") { | ||
| def checkModelData(model: LinearSVCModel, model2: LinearSVCModel): Unit = { | ||
| assert(model.intercept === model2.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.
Can I ask why we set
regParam = 2.0 / datasize / cto match solution ofsklearn.svm.LinearSVC? AFAIK, sklearn called liblinear to train linear SVM classification model, I can understand liblinear usingCfor penalty parameter of the error term which is different fromregParam, and it doesn't multiply1/non the error term, but where is the2.0come from? 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.
http://www.robots.ox.ac.uk/~az/lectures/ml/lect2.pdf
Please refer to page 36.
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 slides also explain it...Please see slide 32...the max can be replaced by soft-max with the softness lambda can be tuned...log-sum-exp is a standard soft-max that can be used which is similar to ReLu functions and we can re-use it from MLP:
ftp://ftp.cs.wisc.edu/math-prog/talks/informs99ssv.ps
ftp://ftp.cs.wisc.edu/pub/dmi/tech-reports/99-03.pdf
I can add the formulation if there is interest...it needs some tuning for soft-max parameter but the convergence will be good with LBFGS (OWLQN is not 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.
@debasish83 Not sure if it's a better solution than squared hinge loss. But I would be interested to learn the performance (accuracy and speed). Can you please help try to evaluate it?
Uh oh!
There was an error while loading. Please reload this page.
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.
hinge loss is not differentiable...how are you smoothing it before you can use a quasi newton solver ? Since the papers smooth the max, a newton/quasi-newton solver should work well...if you are keeping the non-differentiable loss better will be to use a sub-gradient solver as suggested by the talk...I will evaluate the formulation...
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 I saw some posts that hinge loss is not differentiable but squared hinge loss is for practical purposes...can you please point to a reference on squared hinge loss ?