package edu.stanford.nlp.sempre.test;
import java.util.*;
import org.testng.annotations.Test;
import static org.testng.AssertJUnit.*;
import edu.stanford.nlp.sempre.Params;
/**
* Test lazy L1 regularization.
*
* @author ppasupat
*/
public class L1RegularizationTest {
private static final double EPSILON = 1e-3;
class Options {
public double initStepSize = 1.0;
public String l1Reg = "none";
public double l1RegCoeff = 0;
public Options initStepSize(double x) { initStepSize = x; return this; }
public Options l1Reg(String x) { l1Reg = x; return this; }
public Options l1RegCoeff(double x) { l1RegCoeff = x; return this; }
}
private Options originalOptions = null;
private void saveOptions() {
originalOptions = new Options()
.initStepSize(Params.opts.initStepSize)
.l1Reg(Params.opts.l1Reg)
.l1RegCoeff(Params.opts.l1RegCoeff);
}
private void loadOptions(Options options) {
Params.opts.initStepSize = options.initStepSize;
Params.opts.l1Reg = options.l1Reg;
Params.opts.l1RegCoeff = options.l1RegCoeff;
}
private Map<String, Double> constructGradient(double a, double b, double c, double d) {
Map<String, Double> gradient = new HashMap<>();
if (a != 0) gradient.put("a", a);
if (b != 0) gradient.put("b", b);
if (c != 0) gradient.put("c", c);
if (d != 0) gradient.put("d", d);
return gradient;
}
@Test
public void zeroLazyL1Test() {
saveOptions();
{
loadOptions(new Options().l1Reg("none").l1RegCoeff(0));
Params params = new Params();
assertEquals(0.0, params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
assertEquals(1.0 / Math.sqrt(1), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
assertEquals(1.0 / Math.sqrt(1) + 1.0 / Math.sqrt(2), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, -2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(1) + 1.0 / Math.sqrt(2), params.getWeight("a"), EPSILON);
assertEquals(-2.0 / Math.sqrt(4), params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(1) + 1.0 / Math.sqrt(2) + 1.0 / Math.sqrt(3), params.getWeight("a"), EPSILON);
assertEquals(-2.0 / Math.sqrt(4) + 2.0 / Math.sqrt(8), params.getWeight("b"), EPSILON);
}
{
loadOptions(new Options().l1Reg("nonlazy").l1RegCoeff(0));
Params params = new Params();
assertEquals(0.0, params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
// NONLAZY will give a different result as the denominator of the AdaGrad update is incremented by 1
assertEquals(1.0 / Math.sqrt(2), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
assertEquals(1.0 / Math.sqrt(2) + 1.0 / Math.sqrt(3), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, -2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(2) + 1.0 / Math.sqrt(3), params.getWeight("a"), EPSILON);
assertEquals(-2.0 / Math.sqrt(5), params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(2) + 1.0 / Math.sqrt(3) + 1.0 / Math.sqrt(4), params.getWeight("a"), EPSILON);
assertEquals(-2.0 / Math.sqrt(5) + 2.0 / Math.sqrt(9), params.getWeight("b"), EPSILON);
}
{
loadOptions(new Options().l1Reg("lazy").l1RegCoeff(0));
Params params = new Params();
assertEquals(0.0, params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
// LAZY will give the same result as NONLAZY
assertEquals(1.0 / Math.sqrt(2), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
assertEquals(1.0 / Math.sqrt(2) + 1.0 / Math.sqrt(3), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, -2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(2) + 1.0 / Math.sqrt(3), params.getWeight("a"), EPSILON);
assertEquals(-2.0 / Math.sqrt(5), params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(2) + 1.0 / Math.sqrt(3) + 1.0 / Math.sqrt(4), params.getWeight("a"), EPSILON);
assertEquals(-2.0 / Math.sqrt(5) + 2.0 / Math.sqrt(9), params.getWeight("b"), EPSILON);
}
loadOptions(originalOptions);
}
@Test
public void nonZeroLazyL1Test() {
saveOptions();
{
loadOptions(new Options().l1Reg("nonlazy").l1RegCoeff(1.0));
Params params = new Params();
assertEquals(0.0, params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(2.0, 0, -3.14, 0));
assertEquals(1.0 / Math.sqrt(5), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
assertEquals(1.0 / Math.sqrt(5), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, -2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(5) - 1.0 / Math.sqrt(6), params.getWeight("a"), EPSILON);
assertEquals(-1.0 / Math.sqrt(5), params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 2.0, 0, 0));
assertEquals(1.0 / Math.sqrt(5) - 1.0 / Math.sqrt(6), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, 3.0, 0, 0));
assertEquals(0.0, params.getWeight("a"), EPSILON);
assertEquals(2.0 / Math.sqrt(18), params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, 0.0, 0, 0));
assertEquals(0.0, params.getWeight("a"), EPSILON);
assertEquals(1.0 / Math.sqrt(18), params.getWeight("b"), EPSILON);
params.update(constructGradient(-5.0, 0.0, 1.0, 0));
assertEquals(-4.0 / Math.sqrt(32), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, 0.0, -1.0, 0));
assertEquals(-3.0 / Math.sqrt(32), params.getWeight("a"), EPSILON);
assertEquals(0.0, params.getWeight("b"), EPSILON);
assertEquals(0.0, params.getWeight("c"), EPSILON);
}
// LAZY: Randomly access the features in between.
Random r = new Random(42);
for (double t = 1; t > 0; t -= 0.02) {
loadOptions(new Options().l1Reg("lazy").l1RegCoeff(1.0));
Params params = new Params();
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(2.0, 0, -3.14, 0));
if (r.nextDouble() < t) assertEquals(1.0 / Math.sqrt(5), params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 0, 0, 0));
if (r.nextDouble() < t) assertEquals(1.0 / Math.sqrt(5), params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, -2.0, 0, 0));
if (r.nextDouble() < t) assertEquals(1.0 / Math.sqrt(5) - 1.0 / Math.sqrt(6), params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(-1.0 / Math.sqrt(5), params.getWeight("b"), EPSILON);
params.update(constructGradient(1.0, 2.0, 0, 0));
if (r.nextDouble() < t) assertEquals(1.0 / Math.sqrt(5) - 1.0 / Math.sqrt(6), params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, 3.0, 0, 0));
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(2.0 / Math.sqrt(18), params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, 0.0, 0, 0));
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(1.0 / Math.sqrt(18), params.getWeight("b"), EPSILON);
params.update(constructGradient(-5.0, 0.0, 1.0, 0));
if (r.nextDouble() < t) assertEquals(-4.0 / Math.sqrt(32), params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("b"), EPSILON);
params.update(constructGradient(0.0, 0.0, -1.0, 0));
if (r.nextDouble() < t) assertEquals(-3.0 / Math.sqrt(32), params.getWeight("a"), EPSILON);
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("b"), EPSILON);
if (r.nextDouble() < t) assertEquals(0.0, params.getWeight("c"), EPSILON);
}
loadOptions(originalOptions);
}
}