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

Implement nGraph transformation to decompose Einsum-7 operation #5529

Conversation

rkazants
Copy link
Contributor

@rkazants rkazants commented May 6, 2021

Ticket: 54320

Description: Currently the plugins do not support Einsum-7 operation. The implemented nGraph transformation decomposes Einsum-7 operation into a sub-graph that contains operations supported by the plugins. The resulted sub-graph can vary too much and depends on Einsum equation. The sub-graph can include MatMul, Reshape, ReduceSum, Unsqueeze, Multiply operations. The quick check of inference performance on the BERT model including 97 Einsum operations proves the functional correctness of the transformation and does not unveil performance degradation. Also, the transformation is tested on 19 layer tests.

Current Limitations:

  1. The transformation supports only Einsum equations that do not have the repeated labels in the single subscript and do not have ellipsis label ('...'). For example, Einsum operation with equation=aab,bc->ac is unsupported due to repeated label a in the first input subscript. In the meantime Einsum operation with equation=abc,dcb->ac is supported.
  2. Currently it does not implement algorithm to compute (pseudo-)optimal einsum_path for multiple operand case. And in such cases the operands are contracted consequently. The current implementation is sufficient for now because we only have models with two operand Einsum operation.

Details: The main idea of the transformation is better to present using an example about how numpy einsum operation can be decomposed into simple numpy operations. Let us consider the following code:
input1 = np.random.random_integers(10, size=(2,3,4,5)).astype(np.float)
input2 = np.random.random_integers(10, size=(6,4,5,3)).astype(np.float)
ref_result = np.einsum('aecd,bcde->abe', input1, input2)

In the first step let us group dimensions of both operands into three groups:

  1. Common dimensions with labels that are met in both input subscripts and in the output subscript. In this case there is just one common dimension with label e.
  2. Reduced dimensions with labels that are met in both input subscripts but are not met in the output subscript. In this case there are two dimensions with labels c and d.
  3. Separate dimensions (for each operand) with labels that are met in just one input subscript. In this case, dimension with label a is separate for the first operand; dimension with a label b - for the second operand.

Transpose the first operand so that it has the common dimensions first, the separate dimensions after, and the reduced dimensions lastly. Transpose the second operand so that it has the common dimensions first, the reduced dimensions after, and the separate dimensions lastly. So both subscripts look like as follows: eacd - for the first operands, ecdb - for the second operand
input1_grouped = np.transpose(input1, [1, 0, 2, 3])
input2_grouped = np.transpose(input2, [3, 1, 2, 0])

In the second step let us collapse separate dimensions into one dimension using Reshape operation and do the same for reduced dimensions. It is needed to utilize MatMul operation that has requirements for input format. The common dimensions are sort of batch dimensions for MatMul operation.
input1_reshaped = np.reshape(input1_grouped, (3, 2, 20))
input2_reshaped = np.reshape(input2_grouped, (3, 20, 6))

In the third step, perform MatMul operation.
matmul = np.matmul(input1_reshaped, input2_reshaped)
The result of MatMul operation will stay the common and separate dimensions.

In the fourth step, unroll previously collapsed dimensions (the separate dimensions) using Reshape operation. In this case it does not have collapsed the separate dimensions.
matmul_reshaped = np.reshape(matmul, (3, 2, 6))

Finally, it needs to adjust layout specified by the output subcript. The intermediate result has a layout corresponding to eab subscript but the output subscipt is abe. Hence, perform the transpose.
result = np.transpose(matmul_reshaped, [1, 2, 0])

Values of ref_result and result are matched. The nGraph transformation relies on this idea and its modification to avoid extra transpose using transpose attributes in MatMul operations.
Consider IR with Einsum operation and how it is decomposed. The original IR looks as follows:
orig_einsum

The resulted IR before constant-folding:
decomposed_einsum

The idea described above is generalized to multiple operand case by computing intermediate output subscript for each pair of operands.

Signed-off-by: Roman Kazantsev [email protected]

@rkazants rkazants requested a review from a team May 6, 2021 10:23
@rkazants rkazants marked this pull request as draft May 6, 2021 10:24
@openvino-pushbot openvino-pushbot added the category: Core OpenVINO Core (aka ngraph) label May 6, 2021
@rkazants rkazants marked this pull request as ready for review May 13, 2021 13:26
@rkazants rkazants requested review from a team, iimironov, sadolini, popovaan and lazarevevgeny and removed request for a team May 13, 2021 13:33
rkazants added 3 commits May 13, 2021 17:43
Signed-off-by: Roman Kazantsev <[email protected]>
Signed-off-by: Roman Kazantsev <[email protected]>
Signed-off-by: Roman Kazantsev <[email protected]>
@rkazants rkazants requested review from a team and sadolini May 16, 2021 19:58
rkazants added 3 commits May 18, 2021 08:28
…einsum_ngraph_transformation

Signed-off-by: Roman Kazantsev <[email protected]>
Signed-off-by: Roman Kazantsev <[email protected]>
Signed-off-by: Roman Kazantsev <[email protected]>
Copy link
Contributor

@lazarevevgeny lazarevevgeny left a comment

Choose a reason for hiding this comment

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

In general it looks good. I didn't go into details of logic of some parts of this PR but I trust Roman.

But I agree with Ilya's comment about moving some methods to private section of the class instead of passing transformation instance pointer.

Copy link
Contributor

@ilyachur ilyachur left a comment

Choose a reason for hiding this comment

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

Please fix comments in the next PR

@ilyachur ilyachur merged commit b1d1f92 into openvinotoolkit:master May 18, 2021
yekruglov pushed a commit to yekruglov/openvino that referenced this pull request Jun 7, 2021
…vinotoolkit#5529)

* Implement nGraph transformation to decompose Einsum-7 operation

Signed-off-by: Roman Kazantsev <[email protected]>

* Use MatMul instead of Eltwise-multiplication and ReduceSum

Signed-off-by: Roman Kazantsev <[email protected]>

* Add description for new methods

Signed-off-by: Roman Kazantsev <[email protected]>

* Fix code style

Signed-off-by: Roman Kazantsev <[email protected]>

* Fix code style #2

Signed-off-by: Roman Kazantsev <[email protected]>

* Remove unused variables.py

Signed-off-by: Roman Kazantsev <[email protected]>

* Apply feedback after review: fix comments, new_register_node use

Signed-off-by: Roman Kazantsev <[email protected]>

* Add Reshape if needed and apply code-review feedback

Signed-off-by: Roman Kazantsev <[email protected]>

* Fix code-style

Signed-off-by: Roman Kazantsev <[email protected]>

* Remove unused variable

Signed-off-by: Roman Kazantsev <[email protected]>
rnugmanx pushed a commit to rnugmanx/openvino that referenced this pull request Aug 26, 2021
…vinotoolkit#5529)

* Implement nGraph transformation to decompose Einsum-7 operation

Signed-off-by: Roman Kazantsev <[email protected]>

* Use MatMul instead of Eltwise-multiplication and ReduceSum

Signed-off-by: Roman Kazantsev <[email protected]>

* Add description for new methods

Signed-off-by: Roman Kazantsev <[email protected]>

* Fix code style

Signed-off-by: Roman Kazantsev <[email protected]>

* Fix code style #2

Signed-off-by: Roman Kazantsev <[email protected]>

* Remove unused variables.py

Signed-off-by: Roman Kazantsev <[email protected]>

* Apply feedback after review: fix comments, new_register_node use

Signed-off-by: Roman Kazantsev <[email protected]>

* Add Reshape if needed and apply code-review feedback

Signed-off-by: Roman Kazantsev <[email protected]>

* Fix code-style

Signed-off-by: Roman Kazantsev <[email protected]>

* Remove unused variable

Signed-off-by: Roman Kazantsev <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category: Core OpenVINO Core (aka ngraph)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants