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

Sigmoid support with linearization #492

Merged

Conversation

tagomaru
Copy link
Contributor

@tagomaru tagomaru commented Dec 4, 2021

This PR is going to support sigmoid constraints with linearization approach.

How does it work?

The below example test code shows how it works.

test_mnist_model_with_sigmoids.py

from maraboupy import Marabou
import numpy as np

# load network
filename = '../../resources/onnx/robust_model_sigmoid_linear.onnx'
network = Marabou.read_onnx(filename)

options = Marabou.createOptions(
    verbosity=0,
    solveWithMILP=True,
    milpTightening="none")

input = np.ones([28, 28]) * 0.5

# UNSAT
vals, stats, maxClass = network.evaluateLocalRobustness(
    input=input, 
    epsilon=0.05,
    originalClass=5,
    options=options,
    targetClass=2)

# UNKNOWN
vals, stats, maxClass = network.evaluateLocalRobustness(
    input=input, 
    epsilon=0.1,
    originalClass=5,
    options=options,
    targetClass=1)

robust_model_sigmoid_linear.onnx is mnist classifier of input -> hidden1(64 sigmoids) -> hidden2(32 sigmoids) -> output.
It was trained by an adversarial training with PGD attack of epsilon 0.1.
Accuracy of test data is 0.9543, and accuracy of PGD test data is 0.8546.

Result

$ python test_mnist_model_with_sigmoids.py 
(removed...)
Academic license - for non-commercial use only - expires 2022-08-26
unsat
Academic license - for non-commercial use only - expires 2022-08-26
Caught a MarabouError. Code: 900. Message: UNKNOWN (Marabou doesn't support UNKNOWN cases with exitCode yet.)
unsat
  • unsat: Solution was not found even in overapproximated linearization way.
  • unknown: Solution was found in overapproximated linearization way. So unknown whether the result of the original query is SAT or UNSAT.

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
@wu-haoze wu-haoze self-requested a review January 14, 2022 16:46
@@ -3,6 +3,7 @@
1628
1668
1628
0
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is this change?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah I think I understand. It denotes the number of transcendental constraints.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes.

src/common/GurobiWrapper.h Show resolved Hide resolved
src/engine/Engine.cpp Show resolved Hide resolved

printf( "Number of variables: %u\n", _numberOfVariables );
printf( "Number of lower bounds: %u\n", _lowerBounds.size() );
printf( "Number of upper bounds: %u\n", _upperBounds.size() );
printf( "Number of equations: %u\n", _equations.size() );
printf( "Number of constraints: %u\n", _plConstraints.size() );
printf( "Number of piecewise-linear constraints: %u\n", _plConstraints.size() );
printf( "Number of transcendental constraints: %u\n", _tsConstraints.size() );
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we can just replace _plConstraints with _nonLinearConstaints which include both the PL constraints and the transcendental constraints. This way the previously generated input query file would remain valid.

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
double sourceLb = _tableau.getLowerBound( sourceVariable );
double sourceUb = _tableau.getUpperBound( sourceVariable );

if ( sourceLb < 0 && sourceUb > 0 )
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use FloatUtils::gt() and FloatUtils::lt() instead of <, >

static bool lt( double x, double y, double epsilon = GlobalConfiguration::DEFAULT_EPSILON_FOR_COMPARISONS )

if ( sourceLb < 0 && sourceUb > 0 )
{
List<GurobiWrapper::Term> terms;
String binVarName = Stringf( "a%u", _binVarIndex ); // a = 1 -> the caes where x_b >= 0, otherwise where x_b <= 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

caes -> case

int binVal = 1;

// tangent line: x_f = tangentSlope * (x_b - tangentPoint) + yAtTangentPoint
double tangentPoint = ( 0 + sourceUb ) / 2;
Copy link
Collaborator

Choose a reason for hiding this comment

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

( 0 + sourceUb ) / 2 => sourceUb / 2

gurobi.addGeqIndicatorConstraint( binVarName, binVal, terms, 0 );
terms.clear();

// upper bound of x_b
Copy link
Collaborator

Choose a reason for hiding this comment

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

Addition of the upper bound is redundant?

gurobi.addGeqIndicatorConstraint( binVarName, binVal, terms, y_l );
terms.clear();

// upper bound of x_f
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, this should be redundant?

0,
1,
GurobiWrapper::BINARY );
//
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove extra // to be consistent with rest of the code base?

binVal = 0;

// tangent line: x_f = tangentSlope * (x_b - tangentPoint) + yAtTangentPoint
tangentPoint = ( sourceLb + 0 ) / 2;
Copy link
Collaborator

Choose a reason for hiding this comment

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

tangentPoint = sourceLb / 2

terms.clear();

// secant line: x_f = secantSlope * (x_b - sourceLb) + y_l
y_u = sigmoid->sigmoid( 0 );
Copy link
Collaborator

@wu-haoze wu-haoze Jan 17, 2022

Choose a reason for hiding this comment

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

No need to recompute this? Instead, y_u = y_l

gurobi.addLeqIndicatorConstraint( binVarName, binVal, terms, y_u );
terms.clear();

//
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove extra //

gurobi.addLeqIndicatorConstraint( binVarName, binVal, terms, -secantSlope * sourceLb + y_l );
terms.clear();

// lower bound of x_b
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, there is no need to add lowerbounds for x_b and x_f in this case?

terms.append( GurobiWrapper::Term( 1, Stringf( "x%u", targetVariable ) ) );
terms.append( GurobiWrapper::Term( -tangentSlope, Stringf( "x%u", sourceVariable ) ) );

if ( sourceLb >= 0 )
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use FloatUtils::gte

static bool gte( double x, double y, double epsilon = GlobalConfiguration::DEFAULT_EPSILON_FOR_COMPARISONS )

{
gurobi.addLeqConstraint( terms, -tangentSlope * tangentPoint + yAtTangentPoint );
}
else if ( sourceUb <= 0 )
Copy link
Collaborator

Choose a reason for hiding this comment

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

else if ( sourceUb <= 0 ) => else

double y_l = sigmoid->sigmoid( sourceLb );
double y_u = sigmoid->sigmoid( sourceUb );

if ( sourceUb != sourceLb )
Copy link
Collaborator

@wu-haoze wu-haoze Jan 17, 2022

Choose a reason for hiding this comment

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

Maybe handle the case of "sourceUb == sourceLb" in the very beginning of this method?

terms.append( GurobiWrapper::Term( 1, Stringf( "x%u", targetVariable ) ) );
terms.append( GurobiWrapper::Term( -secantSlope, Stringf( "x%u", sourceVariable ) ) );

if ( sourceLb >= 0 )
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use FloatUtils::gte

{
gurobi.addGeqConstraint( terms, -secantSlope * sourceLb + y_l );
}
else if ( sourceUb <= 0 )
Copy link
Collaborator

Choose a reason for hiding this comment

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

else if ( sourceUb <= 0 ) => else

Copy link
Collaborator

@wu-haoze wu-haoze left a comment

Choose a reason for hiding this comment

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

Overall looks good and the implementation looks correct. I have a few more minor comments though for code readability/efficiency.

wu-haoze and others added 2 commits January 17, 2022 12:22
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
@wu-haoze wu-haoze merged commit 6ef249d into NeuralNetworkVerification:master Jan 19, 2022
@tagomaru tagomaru deleted the sigmoid-spport-linerlization branch January 19, 2022 05:00
omriisack pushed a commit to omriisack/Marabou that referenced this pull request Feb 6, 2022
* linearlization done, but queryLoader not yet

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* QueryLoader

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* robust_model_sigmoid_linear

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* remove debug print

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* fixed some bugs and removed codes supporting unknown

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* explicitly include GurobiWrapper.h in MILPEncoder.cpp

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* updating GurobiWrapper.h to make a compiler detect the update

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* update indent

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* rollback Marabou.py

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* change ci.yml

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* fix ci error

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* add interfaces in GurobiWrapper

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* fix queryloader bug

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* rollback queryloader stuff

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

* reflect Andrew's comments

Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>

Co-authored-by: Haoze(Andrew) Wu <haozewu@stanford.edu>
@tagomaru tagomaru restored the sigmoid-spport-linerlization branch March 4, 2022 18:22
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.

2 participants