mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-30 20:18:08 +00:00
Add FusedMatMul contrib op (#5213)
* bug fix transformer * fuse cpu kernel for transposescalematmul and matmul * fuse transpose_scale_matmul cpu kernel with matmul * fix test * Add FusedMatMul Contrib Op * fix test * fix typo * plus more updates per review
This commit is contained in:
parent
fe7e7bfe60
commit
16220f3848
29 changed files with 343 additions and 290 deletions
|
|
@ -21,6 +21,7 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, Range
|
|||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, WordConvEmbedding);
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, GatherND);
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, TransposeMatMul);
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, FusedMatMul);
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, MurmurHash3);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, MaxpoolWithMask);
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, Pad);
|
||||
|
|
@ -158,6 +159,7 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) {
|
|||
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, GatherND)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, MurmurHash3)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, TransposeMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, FusedMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, MaxpoolWithMask)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, Pad)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, Unique)>,
|
||||
|
|
|
|||
26
onnxruntime/contrib_ops/cpu/fused_matmul.cc
Normal file
26
onnxruntime/contrib_ops/cpu/fused_matmul.cc
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "core/providers/cpu/math/matmul.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace contrib {
|
||||
|
||||
ONNX_OPERATOR_KERNEL_EX(
|
||||
TransposeMatMul,
|
||||
kMSDomain,
|
||||
1,
|
||||
kCpuExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
MatMul<float>);
|
||||
|
||||
ONNX_OPERATOR_KERNEL_EX(
|
||||
FusedMatMul,
|
||||
kMSDomain,
|
||||
1,
|
||||
kCpuExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
MatMul<float>);
|
||||
|
||||
} // namespace contrib
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "contrib_ops/cpu/transpose_scale_matmul.h"
|
||||
#include "core/providers/cpu/math/matmul_helper.h"
|
||||
#include "core/util/math.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace contrib {
|
||||
|
||||
ONNX_OPERATOR_KERNEL_EX(
|
||||
TransposeMatMul,
|
||||
kMSDomain,
|
||||
1,
|
||||
kCpuExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
TransposeMatMul);
|
||||
|
||||
TransposeMatMul::TransposeMatMul(const OpKernelInfo& info)
|
||||
: OpKernel{info} {
|
||||
ORT_THROW_IF_ERROR(info.GetAttr("alpha", &alpha_attr_));
|
||||
ORT_THROW_IF_ERROR(info.GetAttr("transA", &trans_a_attr_));
|
||||
ORT_THROW_IF_ERROR(info.GetAttr("transB", &trans_b_attr_));
|
||||
}
|
||||
|
||||
Status TransposeMatMul::Compute(OpKernelContext* context) const {
|
||||
concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool();
|
||||
|
||||
const Tensor* A = context->Input<Tensor>(0);
|
||||
const Tensor* B = context->Input<Tensor>(1);
|
||||
|
||||
// match CUDA kernel implementation, ignore transpose for vectors
|
||||
const bool trans_a = trans_a_attr_ && A->Shape().NumDimensions() != 1;
|
||||
const bool trans_b = trans_b_attr_ && B->Shape().NumDimensions() != 1;
|
||||
|
||||
MatMulComputeHelper helper;
|
||||
ORT_RETURN_IF_ERROR(helper.Compute(A->Shape(), B->Shape(), trans_a, trans_b));
|
||||
|
||||
Tensor* Y = context->Output(0, helper.OutputShape());
|
||||
|
||||
// Bail out early if the output is going to be empty
|
||||
if (Y->Shape().Size() == 0)
|
||||
return Status::OK();
|
||||
|
||||
const size_t num_offsets = helper.OutputOffsets().size();
|
||||
for (size_t i = 0; i < num_offsets; ++i) {
|
||||
math::Gemm<float, concurrency::ThreadPool>(
|
||||
trans_a ? CblasTrans : CblasNoTrans,
|
||||
trans_b ? CblasTrans : CblasNoTrans,
|
||||
helper.M(), helper.N(), helper.K(),
|
||||
alpha_attr_,
|
||||
A->Data<float>() + helper.LeftOffsets()[i],
|
||||
B->Data<float>() + helper.RightOffsets()[i],
|
||||
0.0f,
|
||||
Y->MutableData<float>() + helper.OutputOffsets()[i],
|
||||
thread_pool);
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace contrib
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/framework/op_kernel.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace contrib {
|
||||
|
||||
class TransposeMatMul final : public OpKernel {
|
||||
public:
|
||||
TransposeMatMul(const OpKernelInfo& info);
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
private:
|
||||
float alpha_attr_;
|
||||
int64_t trans_a_attr_, trans_b_attr_;
|
||||
};
|
||||
|
||||
} // namespace contrib
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -20,6 +20,9 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1
|
|||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, float, TransposeMatMul);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, double, TransposeMatMul);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, MLFloat16, TransposeMatMul);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, float, FusedMatMul);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, double, FusedMatMul);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, MLFloat16, FusedMatMul);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, float, Rfft);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, double, Rfft);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, MLFloat16, Rfft);
|
||||
|
|
@ -92,6 +95,9 @@ Status RegisterCudaContribKernels(KernelRegistry& kernel_registry) {
|
|||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, float, TransposeMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, double, TransposeMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, MLFloat16, TransposeMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, float, FusedMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, double, FusedMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, MLFloat16, FusedMatMul)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, float, Rfft)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, double, Rfft)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kMSDomain, 1, MLFloat16, Rfft)>,
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ namespace onnxruntime {
|
|||
namespace contrib {
|
||||
namespace cuda {
|
||||
|
||||
#define REGISTER_KERNEL_TYPED(T) \
|
||||
#define REGISTER_KERNEL_TYPED(op_name, T) \
|
||||
ONNX_OPERATOR_TYPED_KERNEL_EX( \
|
||||
TransposeMatMul, \
|
||||
op_name, \
|
||||
kMSDomain, \
|
||||
1, \
|
||||
T, \
|
||||
|
|
@ -18,9 +18,13 @@ namespace cuda {
|
|||
.TypeConstraint("T", DataTypeImpl::GetTensorType<T>()), \
|
||||
onnxruntime::cuda::MatMul<T>);
|
||||
|
||||
REGISTER_KERNEL_TYPED(float)
|
||||
REGISTER_KERNEL_TYPED(double)
|
||||
REGISTER_KERNEL_TYPED(MLFloat16)
|
||||
REGISTER_KERNEL_TYPED(TransposeMatMul, float)
|
||||
REGISTER_KERNEL_TYPED(TransposeMatMul, double)
|
||||
REGISTER_KERNEL_TYPED(TransposeMatMul, MLFloat16)
|
||||
|
||||
REGISTER_KERNEL_TYPED(FusedMatMul, float)
|
||||
REGISTER_KERNEL_TYPED(FusedMatMul, double)
|
||||
REGISTER_KERNEL_TYPED(FusedMatMul, MLFloat16)
|
||||
|
||||
} // namespace cuda
|
||||
} // namespace contrib
|
||||
|
|
@ -198,6 +198,115 @@ void ValidateTypeAndShapeForScaleAndZP(ONNX_NAMESPACE::InferenceContext& ctx, in
|
|||
}
|
||||
}
|
||||
|
||||
void FusedMatMulShapeInference(ONNX_NAMESPACE::InferenceContext& ctx) {
|
||||
propagateElemTypeFromInputToOutput(ctx, 0, 0);
|
||||
auto transAAttr = ctx.getAttribute("transA");
|
||||
bool transa = transAAttr ? static_cast<int>(transAAttr->i()) != 0 : false;
|
||||
auto transBAttr = ctx.getAttribute("transB");
|
||||
bool transb = transBAttr ? static_cast<int>(transBAttr->i()) != 0 : false;
|
||||
int input1Idx = 0;
|
||||
int input2Idx = 1;
|
||||
if (!hasInputShape(ctx, input1Idx) || !hasInputShape(ctx, input2Idx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto shape0_raw = getInputShape(ctx, input1Idx);
|
||||
const auto shape1_raw = getInputShape(ctx, input2Idx);
|
||||
|
||||
if (shape0_raw.dim_size() == 0 || shape1_raw.dim_size() == 0) {
|
||||
fail_shape_inference("Input tensors of wrong rank (0).");
|
||||
}
|
||||
|
||||
// numpy transpose on a vector does not change anything.
|
||||
if (shape0_raw.dim_size() == 1) {
|
||||
transa = false;
|
||||
}
|
||||
if (shape1_raw.dim_size() == 1) {
|
||||
transb = false;
|
||||
}
|
||||
|
||||
ONNX_NAMESPACE::TensorShapeProto shape0, shape1;
|
||||
auto rank0 = shape0_raw.dim_size();
|
||||
if (rank0 == 1) {
|
||||
// for vector input, transa does not make impact on the dim.
|
||||
shape0 = shape0_raw;
|
||||
} else {
|
||||
for (int i = 0; i < rank0 - 2; ++i) {
|
||||
*shape0.add_dim() = shape0_raw.dim(i);
|
||||
}
|
||||
*shape0.add_dim() = shape0_raw.dim(transa ? rank0 - 1 : rank0 - 2);
|
||||
*shape0.add_dim() = shape0_raw.dim(transa ? rank0 - 2 : rank0 - 1);
|
||||
}
|
||||
|
||||
auto rank1 = shape1_raw.dim_size();
|
||||
if (rank1 == 1) {
|
||||
// for vector input, transb does not make impact on the dim.
|
||||
shape1 = shape1_raw;
|
||||
} else {
|
||||
for (int i = 0; i < rank1 - 2; ++i) {
|
||||
*shape1.add_dim() = shape1_raw.dim(i);
|
||||
}
|
||||
*shape1.add_dim() = shape1_raw.dim(transb ? rank1 - 1 : rank1 - 2);
|
||||
*shape1.add_dim() = shape1_raw.dim(transb ? rank1 - 2 : rank1 - 1);
|
||||
}
|
||||
|
||||
ONNX_NAMESPACE::TensorShapeProto shapeL, shapeR;
|
||||
|
||||
// First promote each shape to at least rank-2. This logic is
|
||||
// specific to matmul, not generic broadcasting.
|
||||
{
|
||||
if (shape0.dim_size() == 1) {
|
||||
shapeL.add_dim()->set_dim_value(1);
|
||||
*shapeL.add_dim() = shape0.dim(0);
|
||||
} else {
|
||||
*shapeL.mutable_dim() = shape0.dim();
|
||||
}
|
||||
if (shape1.dim_size() == 1) {
|
||||
*shapeR.add_dim() = shape1.dim(0);
|
||||
shapeR.add_dim()->set_dim_value(1);
|
||||
} else {
|
||||
*shapeR.mutable_dim() = shape1.dim();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for compatible matrix multiply dimensions
|
||||
{
|
||||
auto dimL = shapeL.dim(shapeL.dim_size() - 1);
|
||||
auto dimR = shapeR.dim(shapeR.dim_size() - 2);
|
||||
if (dimL.has_dim_value() && dimR.has_dim_value() &&
|
||||
dimL.dim_value() != dimR.dim_value()) {
|
||||
fail_shape_inference("Incompatible dimensions for matrix multiplication");
|
||||
}
|
||||
}
|
||||
|
||||
ONNX_NAMESPACE::TensorShapeProto resultShape;
|
||||
|
||||
// Now call out to generic multidimensional broadcasting for
|
||||
// the broadcastable prefixes.
|
||||
{
|
||||
ONNX_NAMESPACE::TensorShapeProto prefixShapeL, prefixShapeR;
|
||||
for (int i = 0; i < shapeL.dim_size() - 2; ++i) {
|
||||
*prefixShapeL.add_dim() = shapeL.dim(i);
|
||||
}
|
||||
for (int i = 0; i < shapeR.dim_size() - 2; ++i) {
|
||||
*prefixShapeR.add_dim() = shapeR.dim(i);
|
||||
}
|
||||
bidirectionalBroadcastShapeInference(
|
||||
prefixShapeL, prefixShapeR, resultShape);
|
||||
}
|
||||
|
||||
// Back to matmul-specific. Add the trailing dimensions back in.
|
||||
{
|
||||
if (shape0.dim_size() != 1) {
|
||||
*resultShape.add_dim() = shapeL.dim(shapeL.dim_size() - 2);
|
||||
}
|
||||
if (shape1.dim_size() != 1) {
|
||||
*resultShape.add_dim() = shapeR.dim(shapeR.dim_size() - 1);
|
||||
}
|
||||
}
|
||||
updateOutputShape(ctx, 0, resultShape);
|
||||
}
|
||||
|
||||
std::function<void(OpSchema&)> QLinearMathDocGenerator(const char* name, const char* additionalDocumentation) {
|
||||
return [=](OpSchema& schema) {
|
||||
std::string doc = R"DOC(
|
||||
|
|
@ -1775,13 +1884,17 @@ Matrix product that behaves like numpy.matmul: https://docs.scipy.org/doc/numpy-
|
|||
});
|
||||
|
||||
static const char* TransposeMatMul_doc = R"DOC(
|
||||
Deprecated. Going forward FusedMatMul should be used. This OP will be supported for backward compatibility.
|
||||
Matrix product that behaves like numpy.matmul: https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.matmul.html
|
||||
)DOC";
|
||||
|
||||
static const char* FusedMatMul_doc = R"DOC(
|
||||
Matrix product that behaves like numpy.matmul: https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.matmul.html
|
||||
)DOC";
|
||||
|
||||
ONNX_CONTRIB_OPERATOR_SCHEMA(TransposeMatMul)
|
||||
.SetDomain(kMSDomain)
|
||||
.SinceVersion(1)
|
||||
.SetDoc("TransposeMatMul")
|
||||
.Input(0, "A", "N-dimensional matrix A", "T")
|
||||
.Input(1, "B", "N-dimensional matrix B", "T")
|
||||
.Attr(
|
||||
|
|
@ -1806,112 +1919,37 @@ Matrix product that behaves like numpy.matmul: https://docs.scipy.org/doc/numpy-
|
|||
"Constrain input and output types to float tensors.")
|
||||
.SetDoc(TransposeMatMul_doc)
|
||||
.TypeAndShapeInferenceFunction([](ONNX_NAMESPACE::InferenceContext& ctx) {
|
||||
propagateElemTypeFromInputToOutput(ctx, 0, 0);
|
||||
auto transAAttr = ctx.getAttribute("transA");
|
||||
bool transa = transAAttr ? static_cast<int>(transAAttr->i()) != 0 : false;
|
||||
auto transBAttr = ctx.getAttribute("transB");
|
||||
bool transb = transBAttr ? static_cast<int>(transBAttr->i()) != 0 : false;
|
||||
int input1Idx = 0;
|
||||
int input2Idx = 1;
|
||||
if (!hasInputShape(ctx, input1Idx) || !hasInputShape(ctx, input2Idx)) {
|
||||
return;
|
||||
}
|
||||
FusedMatMulShapeInference(ctx);
|
||||
});
|
||||
|
||||
const auto shape0_raw = getInputShape(ctx, input1Idx);
|
||||
const auto shape1_raw = getInputShape(ctx, input2Idx);
|
||||
|
||||
if (shape0_raw.dim_size() == 0 || shape1_raw.dim_size() == 0) {
|
||||
fail_shape_inference("Input tensors of wrong rank (0).");
|
||||
}
|
||||
|
||||
// numpy transpose on a vector does not change anything.
|
||||
if (shape0_raw.dim_size() == 1) {
|
||||
transa = false;
|
||||
}
|
||||
if (shape1_raw.dim_size() == 1) {
|
||||
transb = false;
|
||||
}
|
||||
|
||||
ONNX_NAMESPACE::TensorShapeProto shape0, shape1;
|
||||
auto rank0 = shape0_raw.dim_size();
|
||||
if (rank0 == 1) {
|
||||
// for vector input, transa does not make impact on the dim.
|
||||
shape0 = shape0_raw;
|
||||
} else {
|
||||
for (int i = 0; i < rank0 - 2; ++i) {
|
||||
*shape0.add_dim() = shape0_raw.dim(i);
|
||||
}
|
||||
*shape0.add_dim() = shape0_raw.dim(transa ? rank0 - 1 : rank0 - 2);
|
||||
*shape0.add_dim() = shape0_raw.dim(transa ? rank0 - 2 : rank0 - 1);
|
||||
}
|
||||
|
||||
auto rank1 = shape1_raw.dim_size();
|
||||
if (rank1 == 1) {
|
||||
// for vector input, transb does not make impact on the dim.
|
||||
shape1 = shape1_raw;
|
||||
} else {
|
||||
for (int i = 0; i < rank1 - 2; ++i) {
|
||||
*shape1.add_dim() = shape1_raw.dim(i);
|
||||
}
|
||||
*shape1.add_dim() = shape1_raw.dim(transb ? rank1 - 1 : rank1 - 2);
|
||||
*shape1.add_dim() = shape1_raw.dim(transb ? rank1 - 2 : rank1 - 1);
|
||||
}
|
||||
|
||||
ONNX_NAMESPACE::TensorShapeProto shapeL, shapeR;
|
||||
|
||||
// First promote each shape to at least rank-2. This logic is
|
||||
// specific to matmul, not generic broadcasting.
|
||||
{
|
||||
if (shape0.dim_size() == 1) {
|
||||
shapeL.add_dim()->set_dim_value(1);
|
||||
*shapeL.add_dim() = shape0.dim(0);
|
||||
} else {
|
||||
*shapeL.mutable_dim() = shape0.dim();
|
||||
}
|
||||
if (shape1.dim_size() == 1) {
|
||||
*shapeR.add_dim() = shape1.dim(0);
|
||||
shapeR.add_dim()->set_dim_value(1);
|
||||
} else {
|
||||
*shapeR.mutable_dim() = shape1.dim();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for compatible matrix multiply dimensions
|
||||
{
|
||||
auto dimL = shapeL.dim(shapeL.dim_size() - 1);
|
||||
auto dimR = shapeR.dim(shapeR.dim_size() - 2);
|
||||
if (dimL.has_dim_value() && dimR.has_dim_value() &&
|
||||
dimL.dim_value() != dimR.dim_value()) {
|
||||
fail_shape_inference("Incompatible dimensions for matrix multiplication");
|
||||
}
|
||||
}
|
||||
|
||||
ONNX_NAMESPACE::TensorShapeProto resultShape;
|
||||
|
||||
// Now call out to generic multidimensional broadcasting for
|
||||
// the broadcastable prefixes.
|
||||
{
|
||||
ONNX_NAMESPACE::TensorShapeProto prefixShapeL, prefixShapeR;
|
||||
for (int i = 0; i < shapeL.dim_size() - 2; ++i) {
|
||||
*prefixShapeL.add_dim() = shapeL.dim(i);
|
||||
}
|
||||
for (int i = 0; i < shapeR.dim_size() - 2; ++i) {
|
||||
*prefixShapeR.add_dim() = shapeR.dim(i);
|
||||
}
|
||||
bidirectionalBroadcastShapeInference(
|
||||
prefixShapeL, prefixShapeR, resultShape);
|
||||
}
|
||||
|
||||
// Back to matmul-specific. Add the trailing dimensions back in.
|
||||
{
|
||||
if (shape0.dim_size() != 1) {
|
||||
*resultShape.add_dim() = shapeL.dim(shapeL.dim_size() - 2);
|
||||
}
|
||||
if (shape1.dim_size() != 1) {
|
||||
*resultShape.add_dim() = shapeR.dim(shapeR.dim_size() - 1);
|
||||
}
|
||||
}
|
||||
updateOutputShape(ctx, 0, resultShape);
|
||||
ONNX_CONTRIB_OPERATOR_SCHEMA(FusedMatMul)
|
||||
.SetDomain(kMSDomain)
|
||||
.SinceVersion(1)
|
||||
.Input(0, "A", "N-dimensional matrix A", "T")
|
||||
.Input(1, "B", "N-dimensional matrix B", "T")
|
||||
.Attr(
|
||||
"alpha",
|
||||
"Scalar multiplier for the product of the input tensors.",
|
||||
AttributeProto::FLOAT,
|
||||
1.0f)
|
||||
.Attr(
|
||||
"transA",
|
||||
"Whether A should be transposed on the last two dimensions before doing multiplication",
|
||||
AttributeProto::INT,
|
||||
static_cast<int64_t>(0))
|
||||
.Attr(
|
||||
"transB",
|
||||
"Whether B should be transposed on the last two dimensions before doing multiplication",
|
||||
AttributeProto::INT,
|
||||
static_cast<int64_t>(0))
|
||||
.Output(0, "Y", "Matrix multiply results", "T")
|
||||
.TypeConstraint(
|
||||
"T",
|
||||
{"tensor(float16)", "tensor(float)", "tensor(double)", "tensor(bfloat16)"},
|
||||
"Constrain input and output types to float tensors.")
|
||||
.SetDoc(FusedMatMul_doc)
|
||||
.TypeAndShapeInferenceFunction([](ONNX_NAMESPACE::InferenceContext& ctx) {
|
||||
FusedMatMulShapeInference(ctx);
|
||||
});
|
||||
|
||||
ONNX_CONTRIB_OPERATOR_SCHEMA(ReduceSumInteger)
|
||||
|
|
@ -2755,11 +2793,11 @@ Example 4:
|
|||
propagateShapeAndTypeFromFirstInput(ctx);
|
||||
propagateElemTypeFromInputToOutput(ctx, 0, 0);
|
||||
auto type = ctx.getAttribute("stash_type")->i();
|
||||
if (ctx.getNumOutputs() > 1){
|
||||
if (ctx.getNumOutputs() > 1) {
|
||||
auto output_type = ctx.getOutputType(1);
|
||||
output_type->mutable_tensor_type()->set_elem_type(static_cast<int32_t>(type));
|
||||
}
|
||||
if (ctx.getNumOutputs() > 2){
|
||||
if (ctx.getNumOutputs() > 2) {
|
||||
auto output_type = ctx.getOutputType(2);
|
||||
output_type->mutable_tensor_type()->set_elem_type(static_cast<int32_t>(type));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@ Status ProcessNode(
|
|||
}
|
||||
|
||||
if (!graph_utils::IsSupportedOptypeVersionAndDomain(node, "MatMul", {9, 13}) &&
|
||||
!graph_utils::IsSupportedOptypeVersionAndDomain(node, "FusedMatMul", {1}, kMSDomain) &&
|
||||
!graph_utils::IsSupportedOptypeVersionAndDomain(node, "TransposeMatMul", {1}, kMSDomain)) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
|
@ -205,7 +206,7 @@ Status ProcessNode(
|
|||
}
|
||||
|
||||
NodeAttributes fused_node_attrs =
|
||||
node.OpType() == "TransposeMatMul" ? node.GetAttributes() : NodeAttributes{};
|
||||
(node.OpType() == "TransposeMatMul") || (node.OpType() == "FusedMatMul") ? node.GetAttributes() : NodeAttributes{};
|
||||
|
||||
{
|
||||
ONNX_NAMESPACE::AttributeProto& alpha_attr = fused_node_attrs["alpha"];
|
||||
|
|
@ -240,7 +241,7 @@ Status ProcessNode(
|
|||
|
||||
Node& matmul_scale_node = graph.AddNode(
|
||||
graph.GenerateNodeName(node.Name() + "_FusedMatMulAndScale"),
|
||||
"TransposeMatMul",
|
||||
"FusedMatMul",
|
||||
"Fused MatMul and Scale",
|
||||
fused_node_inputs,
|
||||
fused_node_outputs,
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ namespace onnxruntime {
|
|||
|
||||
/**
|
||||
* Fuses MatMul with surrounding scales (multiplies or divides) by a constant
|
||||
* scalar into TransposeMatMul (which supports scaling the product).
|
||||
* scalar into FusedMatMul (which supports scaling the product).
|
||||
*
|
||||
* For example, given matrices A and B and constant scalars t, u, and v:
|
||||
* Mul(v, MatMul(Mul(t, A), Mul(u, B))
|
||||
* -> TransposeMatMul(A, B, alpha=t*u*v)
|
||||
* -> FusedMatMul(A, B, alpha=t*u*v)
|
||||
*
|
||||
* Note: Since both leading and following scales may be fused into a single
|
||||
* scale, the order and number of mathematical operations may change. This may
|
||||
|
|
|
|||
|
|
@ -97,7 +97,8 @@ Status MatmulTransposeFusion::ApplyImpl(Graph& graph, bool& modified, int graph_
|
|||
ORT_RETURN_IF_ERROR(Recurse(node, modified, graph_level, logger));
|
||||
|
||||
if ((!graph_utils::IsSupportedOptypeVersionAndDomain(node, "MatMul", {9, 13}) &&
|
||||
!graph_utils::IsSupportedOptypeVersionAndDomain(node, "TransposeMatMul", {1, 13}, kMSDomain)) ||
|
||||
!graph_utils::IsSupportedOptypeVersionAndDomain(node, "FusedMatMul", {1}, kMSDomain) &&
|
||||
!graph_utils::IsSupportedOptypeVersionAndDomain(node, "TransposeMatMul", {1}, kMSDomain)) ||
|
||||
!graph_utils::IsSupportedProvider(node, GetCompatibleExecutionProviders())) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -130,14 +131,14 @@ Status MatmulTransposeFusion::ApplyImpl(Graph& graph, bool& modified, int graph_
|
|||
const std::vector<NodeArg*> output_defs{node.MutableOutputDefs()[0]};
|
||||
|
||||
Node& matmul_node = graph.AddNode(graph.GenerateNodeName("MatMul_With_Transpose"),
|
||||
"TransposeMatMul",
|
||||
"FusedMatMul",
|
||||
"fused MatMul and Transpose ",
|
||||
input_defs,
|
||||
output_defs, {}, kMSDomain);
|
||||
bool transpose_left = (left != nullptr);
|
||||
bool transpose_right = (right != nullptr);
|
||||
float alpha = 1.0f;
|
||||
if (node.OpType() == "TransposeMatMul") {
|
||||
if ((node.OpType() == "TransposeMatMul") || (node.OpType() == "FusedMatMul")) {
|
||||
transpose_left ^= static_cast<bool>(node.GetAttributes().at("transA").i());
|
||||
transpose_right ^= static_cast<bool>(node.GetAttributes().at("transB").i());
|
||||
alpha = node.GetAttributes().at("alpha").f();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/providers/cpu/math/matmul.h"
|
||||
#include "core/providers/cpu/math/matmul_helper.h"
|
||||
#include "core/util/math.h"
|
||||
#include "core/util/math_cpuonly.h"
|
||||
|
|
@ -9,31 +9,6 @@
|
|||
|
||||
namespace onnxruntime {
|
||||
|
||||
template <typename T>
|
||||
class MatMul final : public OpKernel {
|
||||
public:
|
||||
MatMul(const OpKernelInfo& info) : OpKernel(info) {}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
};
|
||||
|
||||
#if !defined(USE_MKLML_FOR_BLAS)
|
||||
|
||||
template <>
|
||||
class MatMul<float> final : public OpKernel {
|
||||
public:
|
||||
MatMul(const OpKernelInfo& info) : OpKernel(info) {}
|
||||
|
||||
Status PrePack(const Tensor& tensor, int input_idx, bool& is_packed) override;
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
private:
|
||||
TensorShape b_shape_;
|
||||
BufferUniquePtr packed_b_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
ONNX_CPU_OPERATOR_VERSIONED_TYPED_KERNEL(
|
||||
MatMul,
|
||||
1, 8,
|
||||
|
|
@ -117,7 +92,6 @@ Status MatMul<T>::Compute(OpKernelContext* ctx) const {
|
|||
}
|
||||
|
||||
#if !defined(USE_MKLML_FOR_BLAS)
|
||||
|
||||
Status MatMul<float>::PrePack(const Tensor& tensor, int input_idx, bool& is_packed) {
|
||||
is_packed = false;
|
||||
|
||||
|
|
@ -130,8 +104,11 @@ Status MatMul<float>::PrePack(const Tensor& tensor, int input_idx, bool& is_pack
|
|||
return Status::OK();
|
||||
}
|
||||
|
||||
const size_t K = static_cast<size_t>(b_shape_[0]);
|
||||
const size_t N = static_cast<size_t>(b_shape_[1]);
|
||||
const bool trans_b = trans_b_attr_ && b_shape_.NumDimensions() != 1;
|
||||
const size_t K = trans_b ? static_cast<size_t>(b_shape_[1])
|
||||
: static_cast<size_t>(b_shape_[0]);
|
||||
const size_t N = trans_b ? static_cast<size_t>(b_shape_[0])
|
||||
: static_cast<size_t>(b_shape_[1]);
|
||||
|
||||
const size_t packed_b_size = MlasGemmPackBSize(N, K);
|
||||
if (packed_b_size == 0) {
|
||||
|
|
@ -141,20 +118,31 @@ Status MatMul<float>::PrePack(const Tensor& tensor, int input_idx, bool& is_pack
|
|||
auto alloc = Info().GetAllocator(0, OrtMemTypeDefault);
|
||||
auto* packed_b_data = alloc->Alloc(packed_b_size);
|
||||
packed_b_ = BufferUniquePtr(packed_b_data, BufferDeleter(alloc));
|
||||
MlasGemmPackB(CblasNoTrans, N, K, tensor.Data<float>(), N, packed_b_data);
|
||||
MlasGemmPackB(trans_b ? CblasTrans : CblasNoTrans,
|
||||
N,
|
||||
K,
|
||||
tensor.Data<float>(),
|
||||
static_cast<int>(trans_b ? K : N),
|
||||
packed_b_data);
|
||||
is_packed = true;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
#endif
|
||||
|
||||
Status MatMul<float>::Compute(OpKernelContext* ctx) const {
|
||||
concurrency::ThreadPool* thread_pool = ctx->GetOperatorThreadPool();
|
||||
|
||||
const Tensor* a = ctx->Input<Tensor>(0);
|
||||
const Tensor* b = packed_b_ ? nullptr : ctx->Input<Tensor>(1);
|
||||
const auto& b_shape = b ? b->Shape() : b_shape_;
|
||||
|
||||
// match CUDA kernel implementation, ignore transpose for vectors
|
||||
const bool trans_a = trans_a_attr_ && a->Shape().NumDimensions() != 1;
|
||||
const bool trans_b = trans_b_attr_ && b_shape.NumDimensions() != 1;
|
||||
|
||||
MatMulComputeHelper helper;
|
||||
ORT_RETURN_IF_ERROR(helper.Compute(a->Shape(), b ? b->Shape() : b_shape_));
|
||||
ORT_RETURN_IF_ERROR(helper.Compute(a->Shape(), b_shape, trans_a, trans_b));
|
||||
Tensor* y = ctx->Output(0, helper.OutputShape());
|
||||
|
||||
// Bail out early if the output is going to be empty
|
||||
|
|
@ -168,34 +156,39 @@ Status MatMul<float>::Compute(OpKernelContext* ctx) const {
|
|||
// TODO: replace it with GemmBatch for performance, it's OK for now as GemmBatch unrolls as well
|
||||
size_t max_len = helper.OutputOffsets().size();
|
||||
for (size_t i = 0; i < max_len; i++) {
|
||||
#if !defined(USE_MKLML_FOR_BLAS)
|
||||
if (packed_b_) {
|
||||
MlasGemm(CblasNoTrans,
|
||||
static_cast<size_t>(helper.M()),
|
||||
static_cast<size_t>(helper.N()),
|
||||
static_cast<size_t>(helper.K()),
|
||||
1.0f,
|
||||
a_data + helper.LeftOffsets()[i],
|
||||
static_cast<size_t>(helper.K()),
|
||||
packed_b_.get(),
|
||||
0.0f,
|
||||
y_data + helper.OutputOffsets()[i],
|
||||
static_cast<size_t>(helper.N()),
|
||||
thread_pool);
|
||||
} else {
|
||||
math::MatMul(
|
||||
static_cast<int>(helper.M()),
|
||||
static_cast<int>(helper.N()),
|
||||
static_cast<int>(helper.K()),
|
||||
MlasGemm(
|
||||
trans_a ? CblasTrans : CblasNoTrans,
|
||||
static_cast<size_t>(helper.M()),
|
||||
static_cast<size_t>(helper.N()),
|
||||
static_cast<size_t>(helper.K()),
|
||||
alpha_attr_,
|
||||
a_data + helper.LeftOffsets()[i],
|
||||
b_data + helper.RightOffsets()[i],
|
||||
static_cast<size_t>(trans_a ? helper.M() : helper.K()),
|
||||
packed_b_.get(),
|
||||
0.0f,
|
||||
y_data + helper.OutputOffsets()[i],
|
||||
static_cast<size_t>(helper.N()),
|
||||
thread_pool);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
math::Gemm<float, concurrency::ThreadPool>(
|
||||
trans_a ? CblasTrans : CblasNoTrans,
|
||||
trans_b ? CblasTrans : CblasNoTrans,
|
||||
helper.M(),
|
||||
helper.N(),
|
||||
helper.K(),
|
||||
alpha_attr_,
|
||||
a_data + helper.LeftOffsets()[i],
|
||||
b_data + helper.RightOffsets()[i],
|
||||
0.0f,
|
||||
y_data + helper.OutputOffsets()[i],
|
||||
thread_pool);
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace onnxruntime
|
||||
|
|
|
|||
43
onnxruntime/core/providers/cpu/math/matmul.h
Normal file
43
onnxruntime/core/providers/cpu/math/matmul.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/framework/op_kernel.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
template <typename T>
|
||||
class MatMul final : public OpKernel {
|
||||
public:
|
||||
MatMul(const OpKernelInfo& info) : OpKernel(info) {}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
};
|
||||
|
||||
template <>
|
||||
class MatMul<float> final : public OpKernel {
|
||||
public:
|
||||
MatMul(const OpKernelInfo& info) : OpKernel(info) {
|
||||
info.GetAttrOrDefault<int64_t>("transA", &trans_a_attr_, 0);
|
||||
info.GetAttrOrDefault<int64_t>("transB", &trans_b_attr_, 0);
|
||||
info.GetAttrOrDefault<float>("alpha", &alpha_attr_, 1.0);
|
||||
}
|
||||
|
||||
#if !defined(USE_MKLML_FOR_BLAS)
|
||||
Status PrePack(const Tensor& tensor, int input_idx, bool& is_packed) override;
|
||||
#endif
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
private:
|
||||
TensorShape b_shape_;
|
||||
BufferUniquePtr packed_b_;
|
||||
|
||||
// For FusedMatMul and TransposeMatMul contrib ops
|
||||
float alpha_attr_;
|
||||
int64_t trans_a_attr_;
|
||||
int64_t trans_b_attr_;
|
||||
};
|
||||
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -131,10 +131,10 @@ void ProcessInputs(const std::vector<int64_t>& input_dims, const std::vector<T>&
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void RunTransposeMatMulTest(int32_t opset_version = 7, bool transa = false, bool transb = false, float alpha = 1.0f) {
|
||||
void RunFusedMatMulTest(const char* op_name, int32_t opset_version = 7, bool transa = false, bool transb = false, float alpha = 1.0f, bool is_b_constant = false) {
|
||||
std::vector<T> common_input_vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
for (auto t : GenerateSimpleTestCases<T>()) {
|
||||
OpTester test("TransposeMatMul", opset_version, onnxruntime::kMSDomain);
|
||||
OpTester test(op_name, opset_version, onnxruntime::kMSDomain);
|
||||
|
||||
std::vector<int64_t> input0_dims(t.input0_dims);
|
||||
std::vector<T> input0_vals;
|
||||
|
|
@ -146,7 +146,7 @@ void RunTransposeMatMulTest(int32_t opset_version = 7, bool transa = false, bool
|
|||
|
||||
test.AddInput<T>("A", input0_dims, input0_vals);
|
||||
|
||||
test.AddInput<T>("B", input1_dims, input1_vals);
|
||||
test.AddInput<T>("B", input1_dims, input1_vals, is_b_constant);
|
||||
|
||||
test.AddAttribute("transA", (int64_t)transa);
|
||||
test.AddAttribute("transB", (int64_t)transb);
|
||||
|
|
@ -165,32 +165,57 @@ void RunTransposeMatMulTest(int32_t opset_version = 7, bool transa = false, bool
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransposeMatMulOpTest, FloatTypeNoTranspose) {
|
||||
RunTransposeMatMulTest<float>(1);
|
||||
TEST(FusedMatMulOpTest, FloatTypeNoTranspose) {
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1);
|
||||
}
|
||||
|
||||
#ifdef USE_CUDA // double support only implemented in CUDA kernel
|
||||
TEST(TransposeMatMulOpTest, DoubleTypeNoTranspose) {
|
||||
RunTransposeMatMulTest<double>(1);
|
||||
RunFusedMatMulTest<double>("TransposeMatMul", 1);
|
||||
}
|
||||
|
||||
TEST(FusedMatMulOpTest, DoubleTypeNoTranspose) {
|
||||
RunFusedMatMulTest<double>("FusedMatMul", 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(TransposeMatMulOpTest, FloatTypeTransposeA) {
|
||||
RunTransposeMatMulTest<float>(1, true, false);
|
||||
TEST(FusedMatMulOpTest, FloatTypeTransposeA) {
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, true, false);
|
||||
}
|
||||
|
||||
TEST(TransposeMatMulOpTest, FloatTypeTransposeB) {
|
||||
RunTransposeMatMulTest<float>(1, false, true);
|
||||
TEST(FusedMatMulOpTest, FloatTypeTransposeB) {
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, false, true);
|
||||
// b is constant. This tests weight packing logic
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, false, true, 1.0f, true);
|
||||
}
|
||||
|
||||
TEST(TransposeMatMulOpTest, FloatTypeTransposeAB) {
|
||||
RunTransposeMatMulTest<float>(1, true, true);
|
||||
TEST(FusedMatMulOpTest, FloatTypeTransposeAB) {
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, true, true);
|
||||
|
||||
// b is constant. This tests weight packing logic
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, true, true, 1.0f, true);
|
||||
}
|
||||
|
||||
TEST(FusedMatMulOpTest, FloatTypeScale) {
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, false, false, 0.5f);
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, true, false, 2.0f);
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, true, true, 4.0f);
|
||||
|
||||
// now run tests with b constant.
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, false, false, 0.5f, true);
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, true, false, 2.0f, true);
|
||||
RunFusedMatMulTest<float>("FusedMatMul", 1, true, true, 4.0f, true);
|
||||
}
|
||||
|
||||
TEST(TransposeMatMulOpTest, FloatTypeScale) {
|
||||
RunTransposeMatMulTest<float>(1, false, false, 0.5f);
|
||||
RunTransposeMatMulTest<float>(1, true, false, 2.0f);
|
||||
RunTransposeMatMulTest<float>(1, true, true, 4.0f);
|
||||
RunFusedMatMulTest<float>("TransposeMatMul", 1, false, false, 0.5f);
|
||||
RunFusedMatMulTest<float>("TransposeMatMul", 1, true, false, 2.0f);
|
||||
RunFusedMatMulTest<float>("TransposeMatMul", 1, true, true, 4.0f);
|
||||
|
||||
// now run tests with b constant.
|
||||
RunFusedMatMulTest<float>("TransposeMatMul", 1, false, false, 0.5f, true);
|
||||
RunFusedMatMulTest<float>("TransposeMatMul", 1, true, false, 2.0f, true);
|
||||
RunFusedMatMulTest<float>("TransposeMatMul", 1, true, true, 4.0f, true);
|
||||
}
|
||||
|
||||
} // namespace transpose_matmul
|
||||
|
|
@ -711,7 +711,7 @@ TEST_F(GraphTransformationTests, TransposeMatmulFusion) {
|
|||
std::map<std::string, int> op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_TRUE(op_to_count["Transpose"] == 0);
|
||||
ASSERT_TRUE(op_to_count["MatMul"] == 0);
|
||||
ASSERT_TRUE(op_to_count["TransposeMatMul"] == 1);
|
||||
ASSERT_TRUE(op_to_count["FusedMatMul"] == 1);
|
||||
}
|
||||
|
||||
TEST_F(GraphTransformationTests, TransposeMatmulFusionOnTwoTranspose) {
|
||||
|
|
@ -728,10 +728,10 @@ TEST_F(GraphTransformationTests, TransposeMatmulFusionOnTwoTranspose) {
|
|||
std::map<std::string, int> op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_TRUE(op_to_count["Transpose"] == 0);
|
||||
ASSERT_TRUE(op_to_count["MatMul"] == 0);
|
||||
ASSERT_TRUE(op_to_count["TransposeMatMul"] == 1);
|
||||
ASSERT_TRUE(op_to_count["FusedMatMul"] == 1);
|
||||
|
||||
auto& node = *graph.Nodes().begin();
|
||||
ASSERT_TRUE(node.OpType() == "TransposeMatMul");
|
||||
ASSERT_TRUE(node.OpType() == "FusedMatMul");
|
||||
ASSERT_TRUE(static_cast<bool>(node.GetAttributes().at("transA").i()));
|
||||
ASSERT_TRUE(static_cast<bool>(node.GetAttributes().at("transB").i()));
|
||||
}
|
||||
|
|
@ -750,10 +750,10 @@ TEST_F(GraphTransformationTests, TransposeMatmulFusionOnThreeTranspose) {
|
|||
std::map<std::string, int> op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_TRUE(op_to_count["Transpose"] == 0);
|
||||
ASSERT_TRUE(op_to_count["MatMul"] == 0);
|
||||
ASSERT_TRUE(op_to_count["TransposeMatMul"] == 1);
|
||||
ASSERT_TRUE(op_to_count["FusedMatMul"] == 1);
|
||||
|
||||
auto& node = *graph.Nodes().begin();
|
||||
ASSERT_TRUE(node.OpType() == "TransposeMatMul");
|
||||
ASSERT_TRUE(node.OpType() == "FusedMatMul");
|
||||
ASSERT_FALSE(static_cast<bool>(node.GetAttributes().at("transA").i()));
|
||||
ASSERT_TRUE(static_cast<bool>(node.GetAttributes().at("transB").i()));
|
||||
}
|
||||
|
|
@ -776,7 +776,7 @@ TEST_F(GraphTransformationTests, TransposeMatmulNoFusionOnInvalidPerm) {
|
|||
std::map<std::string, int> op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_EQ(op_to_count["Transpose"], 1);
|
||||
ASSERT_EQ(op_to_count["MatMul"], 1);
|
||||
ASSERT_EQ(op_to_count["TransposeMatMul"], 0);
|
||||
ASSERT_EQ(op_to_count["FusedMatMul"], 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -791,7 +791,7 @@ TEST_F(GraphTransformationTests, TransposeMatmulFusionFromTransposeMatMul) {
|
|||
auto transpose_scale_matmul_node =
|
||||
std::find_if(
|
||||
graph.Nodes().cbegin(), graph.Nodes().cend(),
|
||||
[](const Node& node) { return node.Name() == "TransposeMatMul"; });
|
||||
[](const Node& node) { return node.Name() == "FusedMatMul"; });
|
||||
ASSERT_NE(transpose_scale_matmul_node, graph.Nodes().cend());
|
||||
expected_alpha = transpose_scale_matmul_node->GetAttributes().at("alpha").f();
|
||||
}
|
||||
|
|
@ -804,10 +804,10 @@ TEST_F(GraphTransformationTests, TransposeMatmulFusionFromTransposeMatMul) {
|
|||
std::map<std::string, int> op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_EQ(op_to_count["Transpose"], 0);
|
||||
ASSERT_EQ(op_to_count["MatMul"], 0);
|
||||
ASSERT_EQ(op_to_count["TransposeMatMul"], 1);
|
||||
ASSERT_EQ(op_to_count["FusedMatMul"], 1);
|
||||
|
||||
auto& transpose_scale_matmul_node = *graph.Nodes().begin();
|
||||
ASSERT_EQ(transpose_scale_matmul_node.OpType(), "TransposeMatMul");
|
||||
ASSERT_EQ(transpose_scale_matmul_node.OpType(), "FusedMatMul");
|
||||
ASSERT_FALSE(static_cast<bool>(transpose_scale_matmul_node.GetAttributes().at("transA").i()));
|
||||
ASSERT_FALSE(static_cast<bool>(transpose_scale_matmul_node.GetAttributes().at("transB").i()));
|
||||
ASSERT_EQ(transpose_scale_matmul_node.GetAttributes().at("alpha").f(), expected_alpha);
|
||||
|
|
@ -827,7 +827,7 @@ TEST_F(GraphTransformationTests, TransposeMatmulFusionWithPreservedTranspose) {
|
|||
std::map<std::string, int> op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_EQ(op_to_count["Transpose"], 1);
|
||||
ASSERT_EQ(op_to_count["MatMul"], 0);
|
||||
ASSERT_EQ(op_to_count["TransposeMatMul"], 1);
|
||||
ASSERT_EQ(op_to_count["FusedMatMul"], 1);
|
||||
|
||||
ASSERT_FALSE(graph.GraphResolveNeeded());
|
||||
}
|
||||
|
|
@ -3177,17 +3177,17 @@ TEST_F(GraphTransformationTests, MatMulScaleFusionFusableModels) {
|
|||
EXPECT_EQ(transformed_op_counts["Mul"], 0);
|
||||
EXPECT_EQ(transformed_op_counts["Div"], 0);
|
||||
EXPECT_EQ(transformed_op_counts["MatMul"], 0);
|
||||
EXPECT_EQ(transformed_op_counts["TransposeMatMul"], 1);
|
||||
EXPECT_EQ(transformed_op_counts["FusedMatMul"], 1);
|
||||
|
||||
// check combined scale, individual scales should all have the same value
|
||||
const float scale_value = 3.0f;
|
||||
|
||||
const int num_scales =
|
||||
original_op_counts["Mul"] + original_op_counts["Div"] + original_op_counts["TransposeMatMul"];
|
||||
original_op_counts["Mul"] + original_op_counts["Div"] + original_op_counts["FusedMatMul"];
|
||||
|
||||
auto fused_node = std::find_if(
|
||||
graph.Nodes().cbegin(), graph.Nodes().cend(),
|
||||
[](const Node& node) { return node.OpType() == "TransposeMatMul"; });
|
||||
[](const Node& node) { return node.OpType() == "FusedMatMul"; });
|
||||
ASSERT_NE(fused_node, graph.Nodes().cend());
|
||||
|
||||
auto alpha_attr = fused_node->GetAttributes().find("alpha");
|
||||
|
|
@ -3225,7 +3225,7 @@ TEST_F(GraphTransformationTests, MatMulScaleFusionReusedInputScale) {
|
|||
EXPECT_EQ(transformed_op_counts["Mul"], 0);
|
||||
EXPECT_EQ(transformed_op_counts["Div"], 0);
|
||||
EXPECT_EQ(transformed_op_counts["MatMul"], 0);
|
||||
EXPECT_EQ(transformed_op_counts["TransposeMatMul"], 2);
|
||||
EXPECT_EQ(transformed_op_counts["FusedMatMul"], 2);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ def save(model_path, nodes, inputs, outputs, initializers):
|
|||
def gen(model_path,
|
||||
use_transpose_matmul,
|
||||
scale_input_0, scale_input_1, scale_output):
|
||||
matmul_op = "TransposeMatMul" if use_transpose_matmul else "MatMul"
|
||||
matmul_op = "FusedMatMul" if use_transpose_matmul else "MatMul"
|
||||
matmul_domain = "com.microsoft" if use_transpose_matmul else ""
|
||||
matmul_attrs = {"alpha": scale_value} if use_transpose_matmul else {}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -32,10 +32,10 @@ def gen_from_transpose_scale_matmul(model_path):
|
|||
["input_0"],
|
||||
["transposed_input_0"]),
|
||||
helper.make_node(
|
||||
"TransposeMatMul",
|
||||
"FusedMatMul",
|
||||
["transposed_input_0", "input_1"],
|
||||
["output"],
|
||||
"TransposeMatMul",
|
||||
"FusedMatMul",
|
||||
"",
|
||||
msdomain.domain,
|
||||
alpha=3.0, transA=1)
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ IMPLEMENT_GRADIENT_BUILDER(GetMatMulGradient) {
|
|||
} else {
|
||||
ArgDef pre_reduce_grad_1 = IA("PreReduceGrad1");
|
||||
result.push_back(
|
||||
NodeDef(OpDef{"TransposeMatMul", kMSDomain, 1},
|
||||
NodeDef(OpDef{"FusedMatMul", kMSDomain, 1},
|
||||
{A, GO(0)},
|
||||
{pre_reduce_grad_1},
|
||||
{{"transA", MakeAttribute("transA", int64_t(1))}}));
|
||||
|
|
|
|||
Loading…
Reference in a new issue