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

Leaky relu #468

Merged
merged 39 commits into from
Feb 6, 2024
Merged

Leaky relu #468

merged 39 commits into from
Feb 6, 2024

Conversation

wu-haoze
Copy link
Collaborator

@wu-haoze wu-haoze commented Jul 5, 2021

Adding leaky relu support

  1. Support leaky relu in deeppoly
  2. Support solving leaky relu constraint in MILP encoding

----update----
3. Also support solving in DeepSoI
4. Added regression tests on leaky relu networks.

@wu-haoze wu-haoze requested a review from guykatzz July 5, 2021 18:54
xPts,
yPts );
}
catch (GRBException e )
Copy link
Collaborator

Choose a reason for hiding this comment

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

space after (.
Please refrain from abbreviations: pts --> points

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

}
}


Copy link
Collaborator

Choose a reason for hiding this comment

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

remove empty line

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@@ -71,6 +71,12 @@ class GurobiWrapper
// Add a new EQ constraint, e.g. 3x + 4y = -5
void addEqConstraint( const List<Term> &terms, double scalar );

// Add a piece-wise linear constraint
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this a new Gurobi interface we're starting to use? Perhaps all existing encodings should use it, too?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree it's worth studying whether we should switch to gurobi PLW constraints altogether

@@ -23,6 +23,8 @@ class GlobalConfiguration
public:
static void print();

static const double LEAKY_RELU_SLOPE;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it a single slope for all leaky relus in the query? Because the python interface suggested each could have a separate slope.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree that we should allow each leaky relu to have their own slope.


List<NeuronInformation> newNeurons;

// Look for ReLUs where all b variables have already been handled
Copy link
Collaborator

Choose a reason for hiding this comment

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

leaky relus?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@@ -91,7 +94,7 @@ void MILPEncoder::encodeEquation( GurobiWrapper &gurobi, const Equation &equatio
}
}

void MILPEncoder::encodeReLUConstraint( GurobiWrapper &gurobi, ReluConstraint *relu)
void MILPEncoder::encodeReLUConstraint( GurobiWrapper &gurobi, ReluConstraint *relu )
{

Copy link
Collaborator

Choose a reason for hiding this comment

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

remove empty line

@@ -49,6 +49,7 @@ class MarabouError : public Error
NETWORK_LEVEL_REASONER_ACTIVATION_NOT_SUPPORTED = 24,
NETWORK_LEVEL_REASONER_NOT_AVAILABLE = 24,
REQUESTED_NONEXISTENT_CASE_SPLIT= 25,
INVALID_LEAKY_RELU_SLOPE= 26,
Copy link
Collaborator

Choose a reason for hiding this comment

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

space before =. For prevoius line also

_layer = layer;
_size = layer->getSize();
_layerIndex = layer->getLayerIndex();
_alpha = layer->getAlpha();
Copy link
Collaborator

Choose a reason for hiding this comment

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

again, I think slope is better than alpha.
Are we sure all leakyRelus in the layer have the same slope?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

rename slope to _alpha in the DeepPoly LeakyReLUElement.

NeuronIndex sourceIndex = *_neuronToActivationSources[i].begin();
double inputValue = _layerOwner->getLayer( sourceIndex._layer )->getAssignment( sourceIndex._neuron );

_assignment[i] = FloatUtils::max( inputValue, slope * inputValue );
Copy link
Collaborator

Choose a reason for hiding this comment

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

unclear. is slope always <=1?
even if so, lets make this more comprehensible

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, currently we need slope to be <= 1, otherwise the symbolic bounds would be incorrect. I added an assertion and also throw an error when this requirement is not met when constructing a LeakyReLU

NeuronIndex sourceIndex = *_neuronToActivationSources[i].begin();
const Vector<double> &simulations = ( *( _layerOwner->getLayer( sourceIndex._layer )->getSimulations() ) ).get( sourceIndex._neuron );
for ( unsigned j = 0; j < simulationSize; ++j )
_simulations[i][j] = FloatUtils::max( simulations.get( j ), slope * simulations.get( j ) );
Copy link
Collaborator

Choose a reason for hiding this comment

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

same comment, slope > 1?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added an assertion.

@MatthewDaggitt
Copy link
Collaborator

Before merging, this should get a CHANGELOG entry added.

@wu-haoze wu-haoze requested a review from guykatzz January 5, 2024 19:21
@@ -472,6 +489,8 @@ def evaluateWithMarabou(self, inputValues, filename="evaluateWithMarabou.log", o
ipq.setLowerBound(k, inputDict[k])
ipq.setUpperBound(k, inputDict[k])

MarabouCore.saveQuery(ipq, "test.ipq")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this intended? Or a debugging thing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed.


List<PiecewiseLinearConstraint::Fix> LeakyReluConstraint::getPossibleFixes() const
{
return List<PiecewiseLinearConstraint::Fix>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we complete this bit? It's pretty straightforward, no?

}
else
{
// Default: start with the inactive case, because it doesn't
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems stale, neither add new equations now, right?
In general, are the heuristics in this function needed, or just some left overs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agreed. The heuristics in the function are used during the search. I removed this comment though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I also implemented the getPossibleFixes() method above (can't seem to reply to that comment of yours for some reason..)


// If we have existing knowledge about the assignment, use it to
// influence the order of splits
if ( existsAssignment( _f ) )
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same: is this comment correct, or stale?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This comment is correct, since we can still heuristically decide on which case split to try first based on the value of the leaky relu output.

inactiveAuxUpperBound = getUpperBound( _inactiveAux );
}


Copy link
Collaborator

Choose a reason for hiding this comment

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

remove one empty line

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

_score = std::abs( computePolarity() );
}

// Not supporting proof production yet
Copy link
Collaborator

Choose a reason for hiding this comment

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

After we merge this, please ping Omri in case he wants to add proof support here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Will do

}
else
{
if ( relax ) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

new line for {

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

terms.append( GurobiWrapper::Term( lambda, Stringf( "x%u", sourceVariable ) ) );
terms.append( GurobiWrapper::Term( -1, Stringf( "x%u", targetVariable ) ) );
gurobi.addGeqConstraint( terms, ( lambda - 1 ) * sourceUb );
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

}
else
{

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@@ -125,6 +126,9 @@ class Layer
double *getLbs() const;
double *getUbs() const;

void setAlpha( double alpha ) { _alpha = alpha; }
double getAlpha() const { return _alpha; }
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems very very specific to appear in "Layer". I see there is nothing specific to any of the other kinds of constraints. Is this really needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree this is not ideal but I'm not sure there is a easy good alternative. I think in general the Layer class might need a refactoring. Right now we have parameters specific to certain layers existing as field variables (like weights for the WeightedSum layer). I think the approach moving forward is to have different types of layers as subclasses of the layer abstract class. Alternatively, we can keep a parameter map (e.g., Map<String, void *>) in the layer class which can store things like "weights" and "alpha". I think the latter is the way onnx handles it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Okay. Lets at least add a comment that these belong to leaky relus

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Had a comment in that regardes when declaring _alpha in Layer.h. But I added that this can be optimized in the future.

@wu-haoze wu-haoze requested a review from guykatzz February 6, 2024 06:55
@wu-haoze wu-haoze merged commit b6999a4 into NeuralNetworkVerification:master Feb 6, 2024
7 checks passed
@wu-haoze wu-haoze deleted the leaky-relu branch February 6, 2024 16:24
wu-haoze added a commit that referenced this pull request Mar 4, 2024
* add leaky relu class

* leaky relu in nlr construction

* leaky relu layer

* add test for nlr construction

* deeppoly leaky relu

* add test

* add file

* add test for evalution

* leaky relu

* leaky relu

* minor

* multiple-outputs

* minor

* leaky relu

* bug fix

* leaky relu

* handle case where leaky relu's inactive phase has slope greater than 1

* turn off logging

* minor

* update leaky ReLU

* fix merge conflict

* fix leaky relu

* add leaky relu

* add regression test for leaky relu

* add test

* relaxation

* fix merge conflict

* tab width

* add one more regression test

* addressing Guy's comments

* fix merge conflict

* Update Layer.h

Add a note about alpha in Layer.h
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.

3 participants