Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 14 additions & 23 deletions onnxruntime/core/optimizer/layer_norm_fusion.cc
Original file line number Diff line number Diff line change
Expand Up @@ -562,20 +562,19 @@ Status LayerNormFusion::ApplyImpl(Graph& graph, bool& modified, int graph_level,
continue;
}

double epsilon = 0.0;
if (!TryGetScalarInitializerAsDouble(graph, *add2_node.MutableInputDefs()[1], epsilon) ||
static_cast<double>(static_cast<float>(epsilon)) != epsilon) {
Comment thread
Copilot marked this conversation as resolved.
Outdated
continue;
}

InlinedVector<NodeArg*> layer_norm_input_defs{x_input, scale, bias};
Node& layer_norm_node = graph.AddNode(graph.GenerateNodeName(mul_node.Name() + "/LayerNormFusion/"),
"LayerNormalization",
"fused LayerNorm subgraphs ",
layer_norm_input_defs,
{}, mul_node, nullptr, kOnnxDomain);

// Get constant "epsilon" from "Add2" node if available. Else, default value will be used.
double epsilon = 0.0;
if (TryGetScalarInitializerAsDouble(graph, *add2_node.MutableInputDefs()[1], epsilon)) {
layer_norm_node.AddAttribute("epsilon", static_cast<float>(epsilon));
} else {
layer_norm_node.AddAttribute("epsilon", DEFAULT_LAYERNORM_EPSILON);
}
layer_norm_node.AddAttribute("epsilon", static_cast<float>(epsilon));

// The axis definition of layer_norm is ranging from axis to the last dim
layer_norm_node.AddAttribute("axis", static_cast<int64_t>(axes_values[0]));
Expand Down Expand Up @@ -814,25 +813,17 @@ Status SimplifiedLayerNormFusion::ApplyImpl(Graph& graph, bool& modified, int gr
continue;
}

double epsilon = 0.0;
if (!TryGetScalarInitializerAsDouble(graph, *epsilon_input, epsilon) ||
static_cast<double>(static_cast<float>(epsilon)) != epsilon) {
continue;
}

InlinedVector<NodeArg*> layer_norm_input_defs{x_input, scale};
Node& layer_norm_node =
graph.AddNode(graph.GenerateNodeName(mul_node.Name() + "/SimplifiedLayerNormFusion/"), "SimplifiedLayerNormalization",
"fused LayerNorm subgraphs ", layer_norm_input_defs, {}, mul_node, nullptr, kOnnxDomain);

// Get constant "epsilon" from "Add" node if available. Else, default value will be used.
const ONNX_NAMESPACE::TensorProto* tensor_proto =
graph_utils::GetConstantInitializer(graph, epsilon_input->Name());
if (tensor_proto != nullptr && tensor_proto->data_type() == ONNX_NAMESPACE::TensorProto_DataType_FLOAT) {
Initializer initializer{graph, *tensor_proto, graph.ModelPath()};
// epsilon must be a scalar/1-element tensor; fall back to default otherwise.
if (initializer.size() == 1) {
layer_norm_node.AddAttribute("epsilon", initializer.data<float>()[0]);
} else {
layer_norm_node.AddAttribute("epsilon", DEFAULT_LAYERNORM_EPSILON);
}
} else {
layer_norm_node.AddAttribute("epsilon", DEFAULT_LAYERNORM_EPSILON);
}
layer_norm_node.AddAttribute("epsilon", static_cast<float>(epsilon));

// Set stash_type to double if any input is double, default value if float.
if (x_input->TypeAsProto()->tensor_type().elem_type() == ONNX_NAMESPACE::TensorProto_DataType_DOUBLE ||
Expand Down
14 changes: 11 additions & 3 deletions onnxruntime/core/optimizer/matmul_scale_fusion.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include "core/optimizer/matmul_scale_fusion.h"

#include <type_traits>

#include "onnx/defs/attr_proto_util.h"

#include "core/common/optional.h"
Expand All @@ -18,10 +20,14 @@ namespace {
template <typename T>
struct ExtractScalarAsFloatDispatchTarget {
Status operator()(const ONNX_NAMESPACE::TensorProto& tensor_proto, const std::filesystem::path& model_path,
float& scalar_float) {
float& scalar_float, bool& lossless) {
T scalar;
ORT_RETURN_IF_ERROR(utils::UnpackTensor(tensor_proto, model_path, &scalar, 1));
scalar_float = static_cast<float>(scalar);
lossless = true;
if constexpr (std::is_same_v<T, double>) {
lossless = static_cast<double>(scalar_float) == scalar;
}
return Status::OK();
}
};
Comment thread
Copilot marked this conversation as resolved.
Expand Down Expand Up @@ -60,13 +66,15 @@ std::optional<float> GetScalarConstantInitializer(const Graph& graph, const Node
}

float scalar{};
bool lossless = false;
utils::MLTypeCallDispatcher<
uint32_t, uint64_t, int32_t, int64_t, MLFloat16, float, double, BFloat16>
dispatcher{initializer->data_type()};
ORT_THROW_IF_ERROR(
(dispatcher.InvokeRet<Status, ExtractScalarAsFloatDispatchTarget>(*initializer, graph.ModelPath(), scalar)));
(dispatcher.InvokeRet<Status, ExtractScalarAsFloatDispatchTarget>(
*initializer, graph.ModelPath(), scalar, lossless)));

return {scalar};
return lossless ? std::optional<float>{scalar} : std::nullopt;
Comment thread
Yuhx141 marked this conversation as resolved.
Outdated
}

// gets the scale value and its input index if node is a fusable scale (Mul or Div by scalar constant)
Expand Down
25 changes: 25 additions & 0 deletions onnxruntime/test/optimizer/graph_transform_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma warning(disable : 4244)
#endif

#include <cmath>
#include <functional>
#include <fstream>
#include <random>
Expand Down Expand Up @@ -11219,6 +11220,30 @@ TEST_F(GraphTransformationTests, MatMulScaleFusionDoubleType) {
{kCpuExecutionProvider});
}

TEST_F(GraphTransformationTests, MatMulScaleFusionRejectsLossyDoubleScale) {
auto build_test_case = [](ModelTestBuilder& builder) {
auto* input_a = builder.MakeInput<double>({{2, 2}});
auto* input_b = builder.MakeInput<double>({{2, 2}});
auto* scale = builder.MakeScalarInitializer<double>(std::nextafter(1.0, 2.0));
auto* scaled_a = builder.MakeIntermediate();
auto* output = builder.MakeOutput();
builder.AddNode("Mul", {input_a, scale}, {scaled_a});
builder.AddNode("MatMul", {scaled_a, input_b}, {output});
};

auto check_not_fused = [](Graph& graph) {
const auto op_to_count = CountOpsInGraph(graph);
TEST_RETURN_IF_NOT(op_to_count.at("Mul") == 1);
TEST_RETURN_IF_NOT(op_to_count.at("MatMul") == 1);
TEST_RETURN_IF(op_to_count.count("com.microsoft.FusedMatMul") != 0);
return Status::OK();
};

ASSERT_STATUS_OK(TestGraphTransformer(
build_test_case, 13, *logger_, std::make_unique<MatMulScaleFusion>(),
TransformerLevel::Level2, 1, check_not_fused, check_not_fused));
}

TEST_F(GraphTransformationTests, MatMulScaleFusionWithScaleInput) {
TestMatMulScaleFusion(
MODEL_FOLDER "fusion/matmul_scale_with_scale_input.onnx", *logger_,
Expand Down
67 changes: 67 additions & 0 deletions onnxruntime/test/optimizer/graph_transform_test_layernorm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,73 @@ TEST_F(GraphTransformationTests, SimplifiedLayerNormFusionTest) {
}
}

TEST_F(GraphTransformationTests, LayerNormFusionsRejectLossyDoubleEpsilon) {
Comment thread
Yuhx141 marked this conversation as resolved.
auto build_layer_norm = [](ModelTestBuilder& builder) {
auto* input = builder.MakeInput<double>({{2, 4}});
auto* scale = builder.MakeInitializer<double>({4}, {1.0, 1.0, 1.0, 1.0});
auto* bias = builder.MakeInitializer<double>({4}, {0.0, 0.0, 0.0, 0.0});
auto* exponent = builder.MakeScalarInitializer<double>(2.0);
auto* epsilon = builder.MakeScalarInitializer<double>(1e-50);
auto* mean1 = builder.MakeIntermediate();
auto* sub = builder.MakeIntermediate();
auto* squared = builder.MakeIntermediate();
auto* mean2 = builder.MakeIntermediate();
auto* variance = builder.MakeIntermediate();
auto* stddev = builder.MakeIntermediate();
auto* normalized = builder.MakeIntermediate();
auto* scaled = builder.MakeIntermediate();
auto* output = builder.MakeOutput();

builder.AddNode("ReduceMean", {input}, {mean1}).AddAttribute("axes", std::vector<int64_t>{-1});
builder.AddNode("Sub", {input, mean1}, {sub});
builder.AddNode("Pow", {sub, exponent}, {squared});
builder.AddNode("ReduceMean", {squared}, {mean2}).AddAttribute("axes", std::vector<int64_t>{-1});
builder.AddNode("Add", {mean2, epsilon}, {variance});
builder.AddNode("Sqrt", {variance}, {stddev});
builder.AddNode("Div", {sub, stddev}, {normalized});
builder.AddNode("Mul", {normalized, scale}, {scaled});
builder.AddNode("Add", {scaled, bias}, {output});
};

auto build_simplified_layer_norm = [](ModelTestBuilder& builder) {
auto* input = builder.MakeInput<double>({{2, 4}});
auto* scale = builder.MakeInitializer<double>({4}, {1.0, 1.0, 1.0, 1.0});
auto* exponent = builder.MakeScalarInitializer<double>(2.0);
auto* epsilon = builder.MakeScalarInitializer<double>(1e-50);
auto* squared = builder.MakeIntermediate();
auto* mean = builder.MakeIntermediate();
auto* variance = builder.MakeIntermediate();
auto* stddev = builder.MakeIntermediate();
auto* normalized = builder.MakeIntermediate();
auto* output = builder.MakeOutput();

builder.AddNode("Pow", {input, exponent}, {squared});
builder.AddNode("ReduceMean", {squared}, {mean}).AddAttribute("axes", std::vector<int64_t>{-1});
builder.AddNode("Add", {mean, epsilon}, {variance});
builder.AddNode("Sqrt", {variance}, {stddev});
builder.AddNode("Div", {input, stddev}, {normalized});
builder.AddNode("Mul", {normalized, scale}, {output});
};

auto check_not_fused = [](Graph& graph) {
const auto op_to_count = CountOpsInGraph(graph);
TEST_RETURN_IF(op_to_count.count("LayerNormalization") != 0);
TEST_RETURN_IF(op_to_count.count("SimplifiedLayerNormalization") != 0);
TEST_RETURN_IF_NOT(op_to_count.at("Pow") == 1);
return Status::OK();
};

const InlinedHashSet<std::string_view> compatible_eps;
ASSERT_STATUS_OK(TestGraphTransformer(
build_layer_norm, 14, *logger_,
std::make_unique<LayerNormFusion>(compatible_eps, TransformerLevel::Level2),
Comment thread
Copilot marked this conversation as resolved.
Outdated
TransformerLevel::Level2, 1, check_not_fused, check_not_fused));
ASSERT_STATUS_OK(TestGraphTransformer(
build_simplified_layer_norm, 14, *logger_,
std::make_unique<SimplifiedLayerNormFusion>(),
TransformerLevel::Level2, 1, check_not_fused, check_not_fused));
}

TEST_F(GraphTransformationTests, SimplifiedLayerNormFusionSharedCastPowExponent) {
for (const bool has_leading_cast : {false, true}) {
auto build_test_case = [has_leading_cast](ModelTestBuilder& builder) {
Expand Down
Loading