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

[RELAY][PASS] FoldScaleAxis Forward #2020

Merged
merged 4 commits into from
Oct 28, 2018
Merged

Conversation

tqchen
Copy link
Member

@tqchen tqchen commented Oct 28, 2018

This is a first serious attempt to implement an NN related optimization pass on relay. This PR contains the following changes:

  • Make squeeze's attrs numpy compatible
  • Add option to type resolver to write back checked_type to type_annotation if the original one is None
    • There could be some debate on whether we should do so, but it is in general helpful in NN optimizations, where later phase of optimization will rely on knowing types of all arguments @jroesch
  • Support a general FoldScaleAxis Forward
    • This pass assumes that the program is in dataflow form and inlines all lets
    • So far only forward direction is implemented.

Hopefully, this can serve as an example of how optimizations can be done in NNVMv2(relay) IR, and how the infrastructure of relay makes writing optimization in a more principled fashion.

Goal

Fold the scaling of axis(usually caused by BatchNorm) into weight of conv2d in the future. For example

Old:

%1 = multiply(%x, %scale)
%2 = conv2d(%1, %w, data_layout="NHWC")

Transformed:

# scale weight's input channel
%1 = multiply(%w, expand_dims(%scale, axis=1, num_newaxis=2))
%2 = conv2d(%x, %1, data_layout="NHWC")

Further constant folding can fold the multiplication and we remove the scaling in the network.

The Algorithm

While so far only the forward direction is implemented. The general idea is that we transform Expr to tuple of (value, axes, scale), where the final result satisfies:

result = value
for i, k in enumerate(axes):
   k-ith dimension of result *= i-th dimension of scale

Then we can propagate this signal along and fold the scale if necessary. However, it is possible that certain scale may never be consumed if there is no dense/conv2d that follow multiplication.

In order to make sure all the scale we sent out can be consumed eventually, we run a backward "preparation phase", which propagates the demand of the potential axes scaling back to its input.

The new pass is more general than the FoldScaleAxis in nnvm

  • The new pass support arbitrary scaling of multiple axes(although the further implementation is necessary), which could be helpful in NHWCc case,
  • The new pass support folding forward into two separate path of conv2d, which was not possible in NNVM.

@tqchen
Copy link
Member Author

tqchen commented Oct 28, 2018

include/tvm/relay/expr_functor.h Show resolved Hide resolved
@@ -49,17 +49,17 @@ def transpose(data, axes=None):
return _make.transpose(data, list(axes))


def squeeze(data, axes=None):
def squeeze(data, axis=None):
"""Squeeze axes in the array.

Parameters
----------
data : relay.Expr
Copy link
Member

Choose a reason for hiding this comment

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

tvm.relay.Expr?

if (!rhs.defined()) return rhs;
AxesSet ret;
size_t i = 0, j = 0;
while (i < lhs.size() && j < rhs.size()) {
Copy link
Member

Choose a reason for hiding this comment

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

Out of curiosity, are both lhs and rhs always sorted?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this is the requirement of axis set, Thanks for pointing this out, will add a comment block about it


/*!
* \brief The transform function, transform an old call to
* new one given the new args.
Copy link
Member

Choose a reason for hiding this comment

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

a new one

std::unordered_map<const Node*, AxesSet> message_;
// Update the message stored at node.
void Update(const Expr& node, const AxesSet& axes) {
// We run interection of messages:
Copy link
Member

Choose a reason for hiding this comment

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

s/interection/intersection

Expr new_e = ExprMutator::VisitExpr_(op);
if (!checked_type.same_as(new_e->checked_type_)) {
// new_call and new_var's code is only going to be valid for VarNode/CallNode.
// Compiler optimization will likely fold the these away for other nodes.
Copy link
Member

Choose a reason for hiding this comment

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

fold these

src/relay/pass/fold_scale_axis.cc Show resolved Hide resolved
src/relay/pass/fold_scale_axis.cc Show resolved Hide resolved

// Conv2D consumes the scale axis during transformation.
STuple Conv2DForwardTransform(const Call& ref_call,
const AxesSet& expected_axes,
Copy link
Member

Choose a reason for hiding this comment

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

expected_axes is unused. Should it be attached to rnode?

Copy link
Contributor

Choose a reason for hiding this comment

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

also indent

src/relay/pass/fold_scale_axis.cc Show resolved Hide resolved
Axes to remove.
If axes = [] or = None, remove all axis of dimensions 1.
If axes = None, remove all axis of dimensions 1.
Copy link
Contributor

Choose a reason for hiding this comment

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

change the comment also

Copy link
Member Author

@tqchen tqchen Oct 28, 2018

Choose a reason for hiding this comment

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

I do not get what do you mean

Copy link
Contributor

Choose a reason for hiding this comment

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

axes should be changed to axis here

Copy link
Member Author

Choose a reason for hiding this comment

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

gotcha

@tqchen
Copy link
Member Author

tqchen commented Oct 28, 2018

Thanks @zhiics @ZihengJiang for helpful reviews, please check again

include/tvm/relay/op.h Show resolved Hide resolved
}
++j;
} else {
if (i >= base) {
Copy link
Contributor

Choose a reason for hiding this comment

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

else if

}

void VisitExpr_(const TupleGetItemNode* op) {
// pass, do nothing
Copy link
Contributor

Choose a reason for hiding this comment

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

why dont you visit inside? maybe there is opt ability inside

Copy link
Member Author

Choose a reason for hiding this comment

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

good catch


void VisitExpr_(const IfNode* op) {
ExprVisitor::VisitExpr_(op);
// do pass through condition.
Copy link
Contributor

Choose a reason for hiding this comment

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

remove .

// AddSub
Array<AxesSet> AddSubForwardPrep(const Call& call, AxesSet out_axes) {
const auto* tlhs = call->args[0]->type_as<TensorTypeNode>();
const auto* trhs = call->args[1]->type_as<TensorTypeNode>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to add check here?

Copy link
Member Author

Choose a reason for hiding this comment

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

type_as does the check

@tqchen
Copy link
Member Author

tqchen commented Oct 28, 2018

Thanks @MarisaKirisame @ZihengJiang , I have make followup changes, please check again

@ZihengJiang ZihengJiang merged commit 05ea601 into apache:master Oct 28, 2018
eqy pushed a commit to eqy/tvm that referenced this pull request Oct 29, 2018
* [RELAY][PASS] FoldScaleAxis Forward

* Introduce helper function type_as

* Update per review comment

* Fix according to comments
eqy pushed a commit to eqy/tvm that referenced this pull request Oct 29, 2018
* [RELAY][PASS] FoldScaleAxis Forward

* Introduce helper function type_as

* Update per review comment

* Fix according to comments
eqy pushed a commit to eqy/tvm that referenced this pull request Oct 29, 2018
* [RELAY][PASS] FoldScaleAxis Forward

* Introduce helper function type_as

* Update per review comment

* Fix according to comments
FrozenGene pushed a commit to FrozenGene/tvm that referenced this pull request Dec 27, 2018
* [RELAY][PASS] FoldScaleAxis Forward

* Introduce helper function type_as

* Update per review comment

* Fix according to comments
@ZihengJiang ZihengJiang mentioned this pull request Feb 2, 2019
wweic pushed a commit to neo-ai/tvm that referenced this pull request Feb 20, 2019
* [RELAY][PASS] FoldScaleAxis Forward

* Introduce helper function type_as

* Update per review comment

* Fix according to comments
wweic pushed a commit to neo-ai/tvm that referenced this pull request Feb 20, 2019
* [RELAY][PASS] FoldScaleAxis Forward

* Introduce helper function type_as

* Update per review comment

* Fix according to comments
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.

4 participants