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

Return better output shape for Loop with zero iterations #1233

Merged
merged 3 commits into from
Jun 18, 2019

Conversation

skottmckay
Copy link
Contributor

Attempt to provide the correct rank for an output from a Loop node when there are no iterations instead of returning an output with an empty shape.

For a loop output (vs. loop carried dependency) the first dimension is the iteration count so will have a value of 0 and the output size will be zero. Use the rank of the matching subgraph output if available.

If the subgraph output rank is not available output a warning and use a rank 1 shape of {0}. We could possibly make a throw-away call to ExecuteGraph to get the rank from the fetches in that call if this proves to be insufficient.

…en there are no iterations.

For a loop output (vs. loop carried dependency) the first dimension is the iteration count so will have a value of 0 and the output size will be zero. Use the rank of the matching subgraph output if available.

If the subgraph output rank is not available output a warning and use a rank 1 shape of {0}. We could possibly make a throw-away call to ExecuteGraph to get the rank from the fetches in that call if this proves to be insufficient.
@skottmckay skottmckay requested a review from a team as a code owner June 17, 2019 01:48
@@ -399,10 +399,33 @@ Status LoopImpl::Execute(FeedsFetchesManager* ffm, const FeedsFetchesManager* ca
copy_tensor_from_mlvalue_to_output(feeds[i + 2], i); // skip iter# and cond
}

// create empty outputs for loop outputs
TensorShape empty;
// create empty outputs for loop outputs using the subgraph output shapes for the rank
Copy link
Contributor

Choose a reason for hiding this comment

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

It sounds like this is an incompleteness (and ambiguity) in the existing Loop spec. It seems to me that the spec needs to be fixed in ONNX in a compatible fashion. The trouble is that this is making the semantics of the loop dependent on shape-inference … so far, shape-inference has always been for optimization, with no effect on the semantics or specification. That could be a potential concern. Would it be better for the Loop op to have an attribute specifying the desired output-shape (for the no-iteration case) as an attribute? Or, would it be better for the model itself to reshape the output as desired? It would be useful to understand the use-case/example where the current behavior (an output of shape [0]) is insufficient or is a problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The current behavior is to output an empty shape, which I think is incorrect as that really represents a scalar.

Specific use case is the ssd_mobilenet model where there are NonZero nodes that lead up to a Loop. If there are no matches from the NonZero the Loop has zero iterations. The current behavior of outputting an empty shape breaks downstream (which is the issue I'm trying to fix). If I change it to return a rank 1 shape with a value of 0 it's happy, but that is most likely model specific as it's expecting scalar output from each Loop iteration so a rank 1 value is by chance correct.

It would be nicer if the model had a check that the NonZero node produced output but I don't know how viable it would be to require that.

I also don't know whether hitting a Loop with zero iterations will be a common problem so it's hard to say how comprehensive the solution needs to be. Whatever the rank is, the value for Size() returned by the shape is going to be zero so anything downstream is going to get empty data.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, an empty shape denoting a scalar would be wrong. I thought it was returning rank 1 shape with size 0. I agree that the most logical-return-shape would be NumIteration X Per-Iteration-Shape with NumIteration=0. Ideally, the Per-Iteration-Shape should be obtained from the model (e.g., as specified in the output-shape info of the loop-body subgraph). But since that may be unspecified or may have unknown dimensions, we end up with the scenario where the output-shape depends on shape-inference … this means that if somebody "improves" the shape-inference logic for some op, the value returned by inference will change … which seems questionable. I am just thinking aloud as to what the ONNX specification should say … perhaps something like "Users should specify a constant output-shape in the body-graph to get a corresponding output-shape in the zero-iteration case. Alternatively, the model should reshape the output into desired shape explicitly after the loop, e.g., in cases where the shape cannot be statically specified."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The potential ONNX spec update wording sounds fine to me. Did you want to propose that change?

gramalingam
gramalingam previously approved these changes Jun 17, 2019
@skottmckay skottmckay merged commit 6477d4e into master Jun 18, 2019
@raymondxyang raymondxyang deleted the skottmckay/Loop_ImproveZeroIterationsOutputShape branch June 19, 2019 21:45
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