[CPU] Ngram node fusion (#16131)
This commit is contained in:
1 parent
f4da729a19
commit
df3c06ecb4
15 files changed
+2650
-5
No files matched your search
@@ -4,12 +4,17 @@
|
||||
|
||||
set(TARGET_NAME "ov_tensorflow_frontend_tests")
|
||||
|
||||
list(APPEND TF_TESTS_DEPENDENCIES tensorflow_test_models tensorflow_fe_standalone_build_test openvino_intel_cpu_plugin)
|
||||
if (NOT ENABLE_INTEL_CPU)
|
||||
list(REMOVE_ITEM TF_TESTS_DEPENDENCIES openvino_intel_cpu_plugin)
|
||||
set(EXCLUDED_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/compilation.cpp)
|
||||
endif()
|
||||
|
||||
ov_add_test_target(
|
||||
NAME ${TARGET_NAME}
|
||||
ROOT ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDENCIES
|
||||
tensorflow_test_models
|
||||
tensorflow_fe_standalone_build_test
|
||||
EXCLUDED_SOURCE_PATHS ${EXCLUDED_TESTS}
|
||||
DEPENDENCIES ${TF_TESTS_DEPENDENCIES}
|
||||
LINK_LIBRARIES
|
||||
gtest_main_manifest
|
||||
frontend_shared_test_classes
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <openvino/frontend/manager.hpp>
|
||||
#include <openvino/openvino.hpp>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "tf_utils.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
std::shared_ptr<ov::Model> convert_model(const std::string& model_path) {
|
||||
ov::frontend::FrontEndManager fem;
|
||||
auto front_end = fem.load_by_framework(TF_FE);
|
||||
if (!front_end) {
|
||||
throw "TensorFlow Frontend is not initialized";
|
||||
}
|
||||
auto model_filename = FrontEndTestUtils::make_model_path(std::string(TEST_TENSORFLOW_MODELS_DIRNAME) + model_path);
|
||||
auto input_model = front_end->load(model_filename);
|
||||
if (!input_model) {
|
||||
throw "Input model is not read";
|
||||
}
|
||||
auto model = front_end->convert(input_model);
|
||||
if (!model) {
|
||||
throw "Model is not converted";
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class CompileModelsTests : public ::testing::Test {};
|
||||
|
||||
TEST_F(CompileModelsTests, NgramCompilation) {
|
||||
ov::Core core;
|
||||
auto model = convert_model("model_ngram/model_ngram.pb");
|
||||
ov::CompiledModel compiled_model = core.compile_model(model, "CPU");
|
||||
const auto runtime_model = compiled_model.get_runtime_model();
|
||||
|
||||
EXPECT_EQ(runtime_model->get_ordered_ops().size(), 4);
|
||||
EXPECT_EQ(runtime_model->get_parameters().size(), 2);
|
||||
EXPECT_EQ(runtime_model->get_results().size(), 1);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -205,7 +205,8 @@ const InferenceEngine::details::caseless_unordered_map<std::string, Type> type_t
|
||||
{ "PriorBoxClustered", Type::PriorBoxClustered},
|
||||
{"Interaction", Type::Interaction},
|
||||
{ "MHA", Type::MHA},
|
||||
{ "Unique", Type::Unique}
|
||||
{ "Unique", Type::Unique},
|
||||
{ "Ngram", Type::Ngram}
|
||||
};
|
||||
|
||||
Type TypeFromName(const std::string& type) {
|
||||
@@ -403,6 +404,8 @@ std::string NameFromType(const Type type) {
|
||||
return "MHA";
|
||||
case Type::Unique:
|
||||
return "Unique";
|
||||
case Type::Ngram:
|
||||
return "Ngram";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -110,7 +110,8 @@ enum class Type {
|
||||
PriorBoxClustered,
|
||||
Interaction,
|
||||
MHA,
|
||||
Unique
|
||||
Unique,
|
||||
Ngram
|
||||
};
|
||||
|
||||
enum class Algorithm {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "ngraph_transformations/op/power_static.hpp"
|
||||
#include "ngraph_transformations/op/swish_cpu.hpp"
|
||||
#include "ngraph_transformations/op/mha.hpp"
|
||||
#include "ngraph_transformations/op/ngram.hpp"
|
||||
#include "snippets_transformations/op/load_convert.hpp"
|
||||
#include "snippets_transformations/op/store_convert.hpp"
|
||||
#include "snippets_transformations/op/brgemm_cpu.hpp"
|
||||
@@ -52,6 +53,7 @@ std::map<std::string, ngraph::OpSet> Extension::getOpSets() {
|
||||
NGRAPH_OP(PowerStaticNode, ov::intel_cpu)
|
||||
NGRAPH_OP(SwishNode, ov::intel_cpu)
|
||||
NGRAPH_OP(MHANode, ov::intel_cpu)
|
||||
NGRAPH_OP(NgramNode, ov::intel_cpu)
|
||||
NGRAPH_OP(LoadConvertSaturation, ov::intel_cpu)
|
||||
NGRAPH_OP(LoadConvertTruncation, ov::intel_cpu)
|
||||
NGRAPH_OP(StoreConvertSaturation, ov::intel_cpu)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "transformations/utils/utils.hpp"
|
||||
#include "rnn_sequences_optimization.hpp"
|
||||
#include "transformations/common_optimizations/reshape_sequence_fusion.hpp"
|
||||
#include "ngram_fusion.hpp"
|
||||
|
||||
#include "itt.hpp"
|
||||
|
||||
@@ -45,6 +46,7 @@ inline void ConvertToCPUSpecificOpset(std::shared_ptr<ngraph::Function> &nGraphF
|
||||
manager.register_pass<ov::pass::ReshapeSequenceFusion>();
|
||||
manager.register_pass<ngraph::pass::ConstantFolding>();
|
||||
manager.register_pass<ov::pass::ConvertPrecision>(precisions_map {{ ngraph::element::i64, ngraph::element::i32 }});
|
||||
manager.register_pass<NgramFusion>();
|
||||
manager.register_pass<ov::pass::Validate>();
|
||||
|
||||
manager.run_passes(nGraphFunc);
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ngram_fusion.hpp"
|
||||
#include "op/ngram.hpp"
|
||||
#include <openvino/opsets/opset1.hpp>
|
||||
#include <openvino/opsets/opset10.hpp>
|
||||
#include <openvino/core/graph_util.hpp>
|
||||
#include <openvino/pass/pattern/op/wrap_type.hpp>
|
||||
#include <openvino/pass/pattern/op/or.hpp>
|
||||
#include <transformations/utils/utils.hpp>
|
||||
|
||||
#include "itt.hpp"
|
||||
|
||||
using namespace ov::pass::pattern;
|
||||
ov::intel_cpu::NgramFusion::NgramFusion() {
|
||||
MATCHER_SCOPE(NgramFusion);
|
||||
auto concat_matches = [](ov::Output<ov::Node> output) -> bool {
|
||||
if (auto concat = ov::as_type_ptr<ov::opset1::Concat>(output.get_node_shared_ptr())) {
|
||||
return ov::pass::pattern::rank_equals(2)(output) && concat->get_axis() == 1;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto concat_m = ov::pass::pattern::wrap_type<ov::opset1::Concat>(concat_matches);
|
||||
|
||||
ov::matcher_pass_callback callback = [=](ov::pass::pattern::Matcher& m) {
|
||||
const auto concat = m.get_match_root();
|
||||
const auto& inputs = concat->input_values();
|
||||
|
||||
const size_t k = inputs.size();
|
||||
const size_t as_is_idx = k % 2 == 0 ? (k - 1) / 2 : k / 2;
|
||||
// Ngram must contain only one non-select branch (that propagates original tensor as is)
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
const bool is_select = ov::is_type<ov::opset1::Select>(inputs[i].get_node());
|
||||
if ((i == as_is_idx && is_select) || (i != as_is_idx && !is_select)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto check_bias = [](const PatternValueMap& pattern_map,
|
||||
const std::shared_ptr<ov::Node> matched_constant_to_check,
|
||||
const size_t expected_bias) {
|
||||
auto out_it = pattern_map.find(matched_constant_to_check);
|
||||
if (expected_bias == 0) {
|
||||
return out_it == pattern_map.end();
|
||||
} else if (out_it == pattern_map.end()) {
|
||||
return false;
|
||||
}
|
||||
const auto constant = ov::as_type_ptr<ov::opset1::Constant>(out_it->second.get_node_shared_ptr());
|
||||
return constant != nullptr && ov::op::util::constantIsEqualTo(constant, expected_bias);
|
||||
};
|
||||
|
||||
auto tokens_match = [](ov::Output<ov::Node> output) -> bool {
|
||||
return ov::pass::pattern::rank_equals(2)(output) && ov::pass::pattern::type_matches(ov::element::f32)(output);
|
||||
};
|
||||
auto idces_match = [](ov::Output<ov::Node> output) -> bool {
|
||||
return ov::pass::pattern::rank_equals(2)(output) && ov::pass::pattern::type_matches(ov::element::i32)(output);
|
||||
};
|
||||
|
||||
ov::Output<ov::Node> tokens;
|
||||
// "as_is" input validation
|
||||
{
|
||||
using namespace ov::opset1;
|
||||
auto tokens_m = any_input(tokens_match);
|
||||
auto padded_tokens_inputs = as_is_idx == 0
|
||||
? ov::OutputVector{tokens_m, wrap_type<Constant>()}
|
||||
: ov::OutputVector{wrap_type<Constant>(), tokens_m, wrap_type<Constant>()};
|
||||
auto padded_tokens_m = wrap_type<Concat>(padded_tokens_inputs);
|
||||
auto shape_of_m = wrap_type<ShapeOf>({tokens_m});
|
||||
auto cropped_shape_m = wrap_type<StridedSlice>({shape_of_m, wrap_type<Constant>(), wrap_type<Constant>(), wrap_type<Constant>()});
|
||||
|
||||
auto ss_bias = wrap_type<Constant>();
|
||||
auto ss_biased_shape_m =
|
||||
as_is_idx == 0 ? cropped_shape_m : wrap_type<Add>({cropped_shape_m, ss_bias});
|
||||
auto ss_end_reshape_m = wrap_type<Reshape>({ss_biased_shape_m, any_input()});
|
||||
auto cropped_tokens_m = wrap_type<StridedSlice>({padded_tokens_m, wrap_type<Constant>(), ss_end_reshape_m, wrap_type<Constant>()});
|
||||
Matcher matcher(cropped_tokens_m);
|
||||
|
||||
if (!matcher.match(inputs[as_is_idx])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& pattern_map = matcher.get_pattern_value_map();
|
||||
tokens = pattern_map.at(tokens_m);
|
||||
const auto& concat_shape = concat->get_output_partial_shape(0);
|
||||
const auto& tokens_shape = tokens.get_partial_shape();
|
||||
|
||||
// To confirm that a subgraph can be replaced with NgramNode we only need to
|
||||
// 1. Check Add's constant to make sure that data values have a right bias in result tensor
|
||||
// 2. Check subgraph input and output shapes to make sure that all rest constants in the subgraph are correct
|
||||
if (!check_bias(pattern_map, ss_bias, as_is_idx) ||
|
||||
concat_shape.rank() != tokens_shape.rank() || tokens_shape[1] * k != concat_shape[1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ov::Output<ov::Node> indices;
|
||||
// select branches validation
|
||||
{
|
||||
using namespace ov::opset1;
|
||||
auto tokens_m = any_input(tokens_match);
|
||||
auto shape_of_m = wrap_type<ShapeOf>({tokens_m});
|
||||
auto cropped_shape_m = wrap_type<StridedSlice>({shape_of_m, wrap_type<Constant>(), wrap_type<Constant>(), wrap_type<Constant>()});
|
||||
auto idces_m = any_input(idces_match);
|
||||
auto idces_concat_inputs = as_is_idx == 0
|
||||
? ov::OutputVector{idces_m, wrap_type<Constant>()}
|
||||
: ov::OutputVector{wrap_type<Constant>(), idces_m, wrap_type<Constant>()};
|
||||
auto idces_concat_m = wrap_type<Concat>(idces_concat_inputs);
|
||||
|
||||
// left equal branch
|
||||
auto crop_left_bias_m = wrap_type<Constant>();
|
||||
auto crop_left_cropped_shape_m = std::make_shared<ov::pass::pattern::op::Or>(
|
||||
ov::OutputVector{cropped_shape_m, wrap_type<Add>({cropped_shape_m, crop_left_bias_m})});
|
||||
auto crop_left_reshape_m = wrap_type<Reshape>({crop_left_cropped_shape_m, any_input()});
|
||||
auto idxes_crop_left_concat_m = wrap_type<Concat>({crop_left_reshape_m, wrap_type<Constant>()});
|
||||
auto idxes_crop_left_m = wrap_type<StridedSlice>({idces_concat_m, wrap_type<Constant>(), idxes_crop_left_concat_m, wrap_type<Constant>()});
|
||||
|
||||
// right equal branch
|
||||
auto crop_right_bias_m = wrap_type<Constant>();
|
||||
auto crop_right_cropped_shape_m = std::make_shared<ov::pass::pattern::op::Or>(
|
||||
ov::OutputVector{cropped_shape_m, wrap_type<Add>({cropped_shape_m, crop_right_bias_m})});
|
||||
auto crop_right_reshape_m = wrap_type<Reshape>({crop_right_cropped_shape_m, any_input()});
|
||||
auto idxes_crop_right_concat_m = wrap_type<Concat>({crop_right_reshape_m, wrap_type<Constant>()});
|
||||
auto idxes_crop_right_m = wrap_type<StridedSlice>({idces_concat_m, wrap_type<Constant>(), idxes_crop_right_concat_m, wrap_type<Constant>()});
|
||||
|
||||
auto equal_m = wrap_type<Equal>({idxes_crop_left_m, idxes_crop_right_m});
|
||||
auto condition_m = wrap_type<Reshape>({equal_m, any_input()});
|
||||
|
||||
// then branch
|
||||
auto padded_tokens_inputs = as_is_idx == 0
|
||||
? ov::OutputVector{tokens_m, wrap_type<Constant>()}
|
||||
: ov::OutputVector{wrap_type<Constant>(), tokens_m, wrap_type<Constant>()};
|
||||
auto padded_tokens_m = wrap_type<Concat>(padded_tokens_inputs);
|
||||
|
||||
auto then_cropped_shape_bias_m = wrap_type<Constant>();
|
||||
auto then_cropped_shape_m = std::make_shared<ov::pass::pattern::op::Or>(
|
||||
ov::OutputVector{cropped_shape_m, wrap_type<Add>({cropped_shape_m, then_cropped_shape_bias_m})});
|
||||
auto then_crop_end_reshape_m = wrap_type<Reshape>({then_cropped_shape_m, wrap_type<Constant>()});
|
||||
auto then_m = wrap_type<StridedSlice>({padded_tokens_m, wrap_type<Constant>(), then_crop_end_reshape_m, wrap_type<Constant>()});
|
||||
|
||||
// else branch
|
||||
auto else_target_shape_reshape_m = wrap_type<Reshape>({cropped_shape_m, any_input()});
|
||||
auto else_target_shape_concat_m = wrap_type<Concat>({else_target_shape_reshape_m, wrap_type<Constant>()});
|
||||
auto else_m = wrap_type<Broadcast>({wrap_type<Constant>(), else_target_shape_concat_m, wrap_type<Constant>()});
|
||||
auto select_m = wrap_type<Select>({condition_m, then_m, else_m});
|
||||
Matcher select_matcher(select_m);
|
||||
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
if (i == as_is_idx)
|
||||
continue;
|
||||
if (!select_matcher.match(inputs[i])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& pattern_map = select_matcher.get_pattern_value_map();
|
||||
const auto& cur_tokens_input = pattern_map.at(tokens_m);
|
||||
const auto& cur_indices_input = pattern_map.at(idces_m);
|
||||
if (indices.get_node() == nullptr) {
|
||||
indices = cur_indices_input;
|
||||
}
|
||||
|
||||
// To confirm that a subgraph can be replaced with NgramNode we need to
|
||||
// 1. Check that "tokens" input is equal to the input that was matched earlier
|
||||
// 2. Check that "indices" input for all Select branches is the same
|
||||
// 3. Check Add's constants to make sure that data values have a right bias in result tensor
|
||||
const bool validate_eq_biases = (check_bias(pattern_map, crop_left_bias_m, i) && check_bias(pattern_map, crop_right_bias_m, as_is_idx)) ||
|
||||
(check_bias(pattern_map, crop_right_bias_m, i) && check_bias(pattern_map, crop_left_bias_m, as_is_idx));
|
||||
if (cur_tokens_input != tokens || cur_indices_input != indices ||
|
||||
!validate_eq_biases || !check_bias(pattern_map, then_cropped_shape_bias_m, i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto ngram = std::make_shared<ov::intel_cpu::NgramNode>(tokens, indices, k);
|
||||
ngram->set_friendly_name(concat->get_friendly_name());
|
||||
ov::copy_runtime_info(concat, ngram);
|
||||
ov::replace_node(concat, ngram);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ov::pass::pattern::Matcher>(concat_m, matcher_name);
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvino/pass/graph_rewrite.hpp>
|
||||
|
||||
namespace ov {
|
||||
namespace intel_cpu {
|
||||
|
||||
class NgramFusion: public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("NgramFusion", "0");
|
||||
NgramFusion();
|
||||
};
|
||||
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ngram.hpp"
|
||||
#include "../itt.hpp"
|
||||
|
||||
ov::intel_cpu::NgramNode::NgramNode(const ov::Output<Node>& embeddings, const ov::Output<Node>& batch_idces, const size_t k)
|
||||
: Op({embeddings, batch_idces}), m_k(k) {
|
||||
validate_and_infer_types();
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Node> ov::intel_cpu::NgramNode::clone_with_new_inputs(const ov::OutputVector& new_args) const {
|
||||
INTERNAL_OP_SCOPE(NgramNode_clone_with_new_inputs);
|
||||
check_new_args_count(this, new_args);
|
||||
return std::make_shared<ov::intel_cpu::NgramNode>(new_args.at(0), new_args.at(1), m_k);
|
||||
}
|
||||
|
||||
bool ov::intel_cpu::NgramNode::visit_attributes(ov::AttributeVisitor &visitor) {
|
||||
INTERNAL_OP_SCOPE(NgramNode_visit_attributes);
|
||||
visitor.on_attribute("k", m_k);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ov::intel_cpu::NgramNode::validate_and_infer_types() {
|
||||
INTERNAL_OP_SCOPE(NgramNode_validate_and_infer_types);
|
||||
NGRAPH_CHECK(m_k > 0, "k attribute must be greater than zero");
|
||||
|
||||
const auto& idces_et = get_input_element_type(1);
|
||||
const auto& idces_shape = get_input_partial_shape(0);
|
||||
NGRAPH_CHECK(idces_shape.rank() == 2, "'batch_idces' input must have 2D shape whereas current shape is", idces_shape);
|
||||
NGRAPH_CHECK(idces_et.is_integral_number(), "'batch_idces' input must be integer whereas current element type is", idces_et);
|
||||
|
||||
const auto& embeddings_et = get_input_element_type(0);
|
||||
const auto& embeddings_shape = get_input_partial_shape(0);
|
||||
NGRAPH_CHECK(embeddings_et.is_real(), "'embeddings' input must be real whereas current element type is", embeddings_et);
|
||||
NGRAPH_CHECK(embeddings_shape.rank() == 2, "'embeddings' input must have 2D shape whereas current shape is", embeddings_shape);
|
||||
|
||||
auto out_shape = embeddings_shape;
|
||||
out_shape[1] *= m_k;
|
||||
set_output_type(0, embeddings_et, out_shape);
|
||||
}
|
||||
|
||||
size_t ov::intel_cpu::NgramNode::get_k() const {
|
||||
return m_k;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvino/core/node.hpp>
|
||||
#include <openvino/op/op.hpp>
|
||||
|
||||
namespace ov {
|
||||
namespace intel_cpu {
|
||||
/**
|
||||
* The operation flattens embedding tensor of token vectors and traverse it by a sliding window of size times the original embedding sizes.
|
||||
* Inputs:
|
||||
* 1. Embedding vectors of type T1 - shape [N, m], where N - number of tokens, m - embedding size. Required
|
||||
* 2. Indices of type T2 - shape [N, 2]. Contains pairs <batch_idx;idx> for the corresponding tokens. This op uses only batch indices. Required
|
||||
* Outputs:
|
||||
* 1. New embedding vector of type T1 and of shape [N, m * k], where k - operation attribute.
|
||||
* Types:
|
||||
* T1 - only FP32 is supported
|
||||
* T2 - I32 and I64 are supported
|
||||
*/
|
||||
class NgramNode : public ov::op::Op {
|
||||
public:
|
||||
OPENVINO_OP("Ngram", "cpu_plugin_opset");
|
||||
|
||||
NgramNode() = default;
|
||||
NgramNode(const ov::Output<Node>& embeddings, const ov::Output<Node>& batch_idces, const size_t k);
|
||||
std::shared_ptr<ov::Node> clone_with_new_inputs(const ov::OutputVector& new_args) const override;
|
||||
bool visit_attributes(ov::AttributeVisitor& visitor) override;
|
||||
void validate_and_infer_types() override;
|
||||
size_t get_k() const;
|
||||
|
||||
private:
|
||||
size_t m_k;
|
||||
};
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ngram.h"
|
||||
#include "ie_parallel.hpp"
|
||||
#include "common/cpu_memcpy.h"
|
||||
#include "ngraph_transformations/op/ngram.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace intel_cpu {
|
||||
namespace node {
|
||||
namespace {
|
||||
class NgramShapeInfer : public ShapeInferEmptyPads {
|
||||
public:
|
||||
NgramShapeInfer(const size_t k) : m_k(k) {}
|
||||
Result infer(
|
||||
const std::vector<std::reference_wrapper<const VectorDims>>& input_shapes,
|
||||
const std::unordered_map<size_t, MemoryPtr>& data_dependency) override {
|
||||
auto output_shape = input_shapes[0].get();
|
||||
output_shape[1] *= m_k;
|
||||
return {{std::move(output_shape)}, ShapeInferStatus::success};
|
||||
}
|
||||
port_mask_t get_port_mask() const override {
|
||||
return EMPTY_PORT_MASK;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_k;
|
||||
};
|
||||
|
||||
class NgramShapeInferFactory : public ShapeInferFactory {
|
||||
public:
|
||||
NgramShapeInferFactory(const std::shared_ptr<ov::Node>& op) : m_op(op) {}
|
||||
ShapeInferPtr makeShapeInfer() const override {
|
||||
auto ngram = ov::as_type_ptr<NgramNode>(m_op);
|
||||
if (!ngram) {
|
||||
IE_THROW(Unexpected) << "Wrong operation type";
|
||||
}
|
||||
return std::make_shared<NgramShapeInfer>(ngram->get_k());
|
||||
}
|
||||
private:
|
||||
std::shared_ptr<ov::Node> m_op;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool Ngram::isSupportedOperation(const std::shared_ptr<const ov::Node>& op, std::string& errorMessage) noexcept {
|
||||
try {
|
||||
const auto ngram = ov::as_type_ptr<const NgramNode>(op);
|
||||
if (!ngram) {
|
||||
errorMessage = "Only Ngram from CPU internal opset is supported";
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Ngram::Ngram(const std::shared_ptr<ov::Node>& op, const GraphContext::CPtr& context)
|
||||
: Node(op, context, NgramShapeInferFactory(op)) {
|
||||
std::string errorMessage;
|
||||
if (!isSupportedOperation(op, errorMessage)) {
|
||||
IE_THROW(NotImplemented) << errorMessage;
|
||||
}
|
||||
|
||||
const auto ngram = ov::as_type_ptr<const NgramNode>(op);
|
||||
k = ngram->get_k();
|
||||
leftPad = k % 2 == 0 ? (k - 1) / 2 : k / 2;
|
||||
rightPad = k / 2;
|
||||
|
||||
const auto& windowStrideDim = ngram->get_input_partial_shape(0)[1];
|
||||
if (windowStrideDim.is_static()) {
|
||||
windowStride = windowStrideDim.get_length();
|
||||
windowSize = k * windowStride;
|
||||
leftPaddingSize = windowStride * leftPad;
|
||||
rightPaddingSize = windowStride * rightPad;
|
||||
}
|
||||
}
|
||||
|
||||
void Ngram::initSupportedPrimitiveDescriptors() {
|
||||
if (!supportedPrimitiveDescriptors.empty())
|
||||
return;
|
||||
|
||||
idcesPrecision = getOriginalInputPrecisionAtPort(1);
|
||||
if (idcesPrecision != InferenceEngine::Precision::I32 && idcesPrecision != InferenceEngine::Precision::I64) {
|
||||
idcesPrecision = InferenceEngine::Precision::I32;
|
||||
}
|
||||
|
||||
addSupportedPrimDesc({{LayoutType::ncsp, InferenceEngine::Precision::FP32},
|
||||
{LayoutType::ncsp, idcesPrecision}},
|
||||
{{LayoutType::ncsp, InferenceEngine::Precision::FP32}},
|
||||
ref_any,
|
||||
isDynamicNode());
|
||||
}
|
||||
|
||||
void Ngram::prepareParams() {
|
||||
const auto& srcDataDims = getParentEdgeAt(0)->getMemoryPtr()->getStaticDims();
|
||||
const auto& srcIndicesDims = getParentEdgeAt(1)->getMemoryPtr()->getStaticDims();
|
||||
const auto& outDims = getChildEdgeAt(0)->getMemoryPtr()->getStaticDims();;
|
||||
|
||||
idcesShapeSize = std::accumulate(srcIndicesDims.begin(), srcIndicesDims.end(), 1, std::multiplies<size_t>());
|
||||
numOutElems = std::accumulate(outDims.begin(), outDims.end(), 1, std::multiplies<size_t>());
|
||||
idcesStride = getParentEdgeAt(1)->getMemoryPtr()->GetDescWithType<BlockedMemoryDesc>()->getStrides()[0];
|
||||
numIdces = srcIndicesDims[0];
|
||||
|
||||
windowStride = srcDataDims[1];
|
||||
windowSize = k * windowStride;
|
||||
leftPaddingSize = windowStride * leftPad;
|
||||
rightPaddingSize = windowStride * rightPad;
|
||||
}
|
||||
|
||||
template <typename idces_type>
|
||||
std::vector<size_t> Ngram::computeBatchLenghts() {
|
||||
auto* srcIndices = reinterpret_cast<const idces_type*>(getParentEdgeAt(1)->getMemoryPtr()->GetPtr());
|
||||
|
||||
std::vector<size_t> batchLenghts{0};
|
||||
batchLenghts.reserve(numIdces + 1);
|
||||
for (size_t i = idcesStride; i < idcesShapeSize; i += idcesStride) {
|
||||
if (srcIndices[i - idcesStride] != srcIndices[i]) {
|
||||
batchLenghts.push_back(i / idcesStride);
|
||||
}
|
||||
}
|
||||
batchLenghts.push_back(idcesShapeSize / idcesStride);
|
||||
|
||||
return batchLenghts;
|
||||
}
|
||||
|
||||
void Ngram::execute(dnnl::stream strm) {
|
||||
auto* srcData = reinterpret_cast<const float*>(getParentEdgeAt(0)->getMemoryPtr()->GetPtr());
|
||||
auto* dstData = reinterpret_cast<float*>(getChildEdgeAt(0)->getMemoryPtr()->GetPtr());
|
||||
|
||||
std::vector<size_t> batchLenghts;
|
||||
if (idcesPrecision == InferenceEngine::Precision::I32) {
|
||||
batchLenghts = computeBatchLenghts<std::int32_t>();
|
||||
} else if (idcesPrecision == InferenceEngine::Precision::I64) {
|
||||
batchLenghts = computeBatchLenghts<std::int64_t>();
|
||||
} else {
|
||||
IE_THROW() << "Unsupported idces precision: " << idcesPrecision;
|
||||
}
|
||||
|
||||
/* The following procedure applied to each batch:
|
||||
1. Pad both corners of current embedding with zeros. Left/Right pad are computed depending on k.
|
||||
2. Apply sliding window of windowSize with a step windowStride and form k new embedding vectors for the embedding
|
||||
*/
|
||||
memset(dstData, 0, numOutElems * sizeof(float));
|
||||
parallel_for(batchLenghts.size() - 1, [&](const size_t batchIdx) {
|
||||
size_t srcWindowBias = 0;
|
||||
size_t dstWindowBias = 0;
|
||||
|
||||
const size_t niter = batchLenghts[batchIdx + 1] - batchLenghts[batchIdx];
|
||||
const size_t srcBatchBias = batchLenghts[batchIdx] * windowStride;
|
||||
const size_t dstBatchBias = batchLenghts[batchIdx] * windowStride * k;
|
||||
for (size_t i = 0; i < niter; ++i) {
|
||||
const size_t curLeftPad = leftPad >= i ? leftPaddingSize - i * windowStride : 0;
|
||||
const size_t curRightPad = rightPad >= niter - 1 - i ? rightPaddingSize - (niter - 1 - i) * windowStride : 0;
|
||||
const size_t dataSize = windowSize - curLeftPad - curRightPad;
|
||||
|
||||
dstWindowBias += curLeftPad;
|
||||
cpu_memcpy(dstData + dstBatchBias + dstWindowBias, srcData + srcBatchBias + srcWindowBias, dataSize * sizeof(float));
|
||||
dstWindowBias += dataSize + curRightPad;
|
||||
if (curLeftPad == 0)
|
||||
srcWindowBias += windowStride;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Ngram::executeDynamicImpl(dnnl::stream strm) {
|
||||
execute(strm);
|
||||
}
|
||||
|
||||
bool Ngram::created() const {
|
||||
return getType() == Type::Ngram;
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <node.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ov {
|
||||
namespace intel_cpu {
|
||||
namespace node {
|
||||
|
||||
class Ngram : public Node {
|
||||
public:
|
||||
Ngram(const std::shared_ptr<ov::Node>& op, const GraphContext::CPtr& context);
|
||||
|
||||
void getSupportedDescriptors() override {};
|
||||
void initSupportedPrimitiveDescriptors() override;
|
||||
void execute(dnnl::stream strm) override;
|
||||
bool created() const override;
|
||||
|
||||
static bool isSupportedOperation(const std::shared_ptr<const ov::Node>& op, std::string& errorMessage) noexcept;
|
||||
|
||||
protected:
|
||||
void executeDynamicImpl(dnnl::stream strm) override;
|
||||
void prepareParams() override;
|
||||
|
||||
private:
|
||||
template <typename idces_type>
|
||||
std::vector<size_t> computeBatchLenghts();
|
||||
|
||||
size_t k = 0;
|
||||
size_t windowSize = 0;
|
||||
size_t windowStride = 0;
|
||||
|
||||
size_t leftPad = 0;
|
||||
size_t rightPad = 0;
|
||||
size_t leftPaddingSize = 0;
|
||||
size_t rightPaddingSize = 0;
|
||||
|
||||
size_t idcesShapeSize = 0;
|
||||
size_t idcesStride = 0;
|
||||
size_t numIdces = 0;
|
||||
size_t numOutElems = 0;
|
||||
|
||||
InferenceEngine::Precision idcesPrecision;
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
@@ -91,6 +91,7 @@
|
||||
#include "nodes/interaction.h"
|
||||
#include "nodes/mha.h"
|
||||
#include "nodes/unique.hpp"
|
||||
#include "nodes/ngram.h"
|
||||
|
||||
namespace ov {
|
||||
namespace intel_cpu {
|
||||
@@ -194,6 +195,7 @@ Node::NodesFactory::NodesFactory()
|
||||
INTEL_CPU_NODE(Interaction, Type::Interaction);
|
||||
INTEL_CPU_NODE(MHA, Type::MHA);
|
||||
INTEL_CPU_NODE(Unique, Type::Unique);
|
||||
INTEL_CPU_NODE(Ngram, Type::Ngram);
|
||||
}
|
||||
|
||||
#undef INTEL_CPU_NODE
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <debug.h>
|
||||
#include <shared_test_classes/base/ov_subgraph.hpp>
|
||||
#include <ngraph_functions/builders.hpp>
|
||||
#include "common_test_utils/common_utils.hpp"
|
||||
#include <common_test_utils/ov_tensor_utils.hpp>
|
||||
#include "test_utils/cpu_test_utils.hpp"
|
||||
#include <openvino/opsets/opset1.hpp>
|
||||
|
||||
using namespace CPUTestUtils;
|
||||
using namespace ov::test;
|
||||
using namespace ngraph::helpers;
|
||||
|
||||
namespace CPUSubgraphTestsDefinitions {
|
||||
|
||||
typedef std::tuple<
|
||||
std::vector<InputShape>,
|
||||
ElementType,
|
||||
ElementType,
|
||||
size_t
|
||||
> NgramTestParams;
|
||||
|
||||
static std::shared_ptr<ov::Node> getStridedSlice(const std::shared_ptr<ov::Node>& data,
|
||||
const std::shared_ptr<ov::Node>& begin,
|
||||
const std::shared_ptr<ov::Node>& end,
|
||||
const std::vector<int64_t>& shrink_axis_mask = {}) {
|
||||
std::vector<int64_t> default_mask(begin->get_shape()[0], 0);
|
||||
return std::make_shared<ov::opset1::StridedSlice>(data, begin, end, default_mask, default_mask,
|
||||
std::vector<int64_t>{}, shrink_axis_mask);
|
||||
}
|
||||
|
||||
static std::shared_ptr<ov::Node> getReshape(const std::shared_ptr<ov::Node>& data,
|
||||
const std::vector<int64_t>& requested_shape,
|
||||
const ov::element::Type& prc) {
|
||||
auto requested_shape_node = ov::opset1::Constant::create(prc, {requested_shape.size()}, requested_shape);
|
||||
return std::make_shared<ov::opset1::Reshape>(data, requested_shape_node, true);
|
||||
}
|
||||
|
||||
static std::shared_ptr<ov::Model> initNgram(std::vector<ov::PartialShape>& input_shapes,
|
||||
const ov::element::Type& data_et,
|
||||
const ov::element::Type& idces_et,
|
||||
const size_t k) {
|
||||
const size_t left_pad = k % 2 == 0 ? (k - 1) / 2 : k / 2;
|
||||
const size_t right_pad = k / 2;
|
||||
const size_t mid_idx = left_pad;
|
||||
|
||||
ov::element::TypeVector input_precisions{data_et, idces_et};
|
||||
auto params = ngraph::builder::makeDynamicParams(input_precisions, input_shapes);
|
||||
|
||||
auto shape_of = std::make_shared<ov::opset10::ShapeOf>(params[0], idces_et);
|
||||
auto shape_ss_begin = ov::opset1::Constant::create(idces_et, {1}, {0});
|
||||
auto shape_ss_end = ov::opset1::Constant::create(idces_et, {1}, {1});
|
||||
auto shape_ss = getStridedSlice(shape_of, shape_ss_begin, shape_ss_end, {1});
|
||||
|
||||
auto getInputsToPad = [&](const ov::Output<ov::Node> data, const int pad_value) {
|
||||
const size_t length = data.get_partial_shape()[1].get_length();
|
||||
ov::OutputVector inputs;
|
||||
if (left_pad > 0) {
|
||||
inputs.push_back(ov::opset1::Constant::create(data.get_element_type(), {left_pad, length}, {pad_value}));
|
||||
}
|
||||
inputs.push_back(data);
|
||||
if (right_pad > 0) {
|
||||
inputs.push_back(ov::opset1::Constant::create(data.get_element_type(), {right_pad, length}, {pad_value}));
|
||||
}
|
||||
return inputs;
|
||||
};
|
||||
|
||||
auto data_padded = std::make_shared<ov::opset1::Concat>(getInputsToPad(params[0], 0), 0);
|
||||
auto idces_padded = std::make_shared<ov::opset1::Concat>(getInputsToPad(params[1], -1), 0);
|
||||
|
||||
std::shared_ptr<ov::Node> as_is_bias = shape_ss;
|
||||
if (mid_idx != 0) {
|
||||
auto bias_const = ov::opset1::Constant::create(idces_et, {}, {mid_idx});
|
||||
as_is_bias = std::make_shared<ov::opset1::Add>(shape_ss, bias_const);
|
||||
}
|
||||
auto as_is_ss_begin = ov::opset1::Constant::create(idces_et, {1}, {mid_idx});
|
||||
auto as_is_ss_end = getReshape(as_is_bias, {1}, idces_et);
|
||||
auto as_is_ss = getStridedSlice(data_padded, as_is_ss_begin, as_is_ss_end);
|
||||
|
||||
auto getSelectBranch = [&](const size_t cur_idx, const size_t mid_idx) {
|
||||
std::shared_ptr<ov::Node> eq_left_bias = shape_ss;
|
||||
if (cur_idx != 0) {
|
||||
auto bias_const = ov::opset1::Constant::create(idces_et, {}, {cur_idx});
|
||||
eq_left_bias = std::make_shared<ov::opset1::Add>(shape_ss, bias_const);
|
||||
}
|
||||
auto eq_left_reshape = getReshape(eq_left_bias, {1}, idces_et);
|
||||
auto eq_left_concat_const = ov::opset1::Constant::create(idces_et, {1}, {1});
|
||||
auto eq_left_concat = std::make_shared<ov::opset1::Concat>(ov::OutputVector{eq_left_reshape, eq_left_concat_const}, 0);
|
||||
auto eq_left_ss_begin = ov::opset1::Constant::create(idces_et, {2}, std::vector<size_t>{cur_idx, 0ul});
|
||||
auto eq_left_ss = getStridedSlice(idces_padded, eq_left_ss_begin, eq_left_concat, {0, 1});
|
||||
|
||||
std::shared_ptr<ov::Node> eq_right_bias = shape_ss;
|
||||
if (mid_idx != 0) {
|
||||
auto bias_const = ov::opset1::Constant::create(idces_et, {}, {mid_idx});
|
||||
eq_right_bias = std::make_shared<ov::opset1::Add>(shape_ss, bias_const);
|
||||
}
|
||||
auto eq_right_reshape = getReshape(eq_right_bias, {1}, idces_et);
|
||||
auto eq_right_concat_const = ov::opset1::Constant::create(idces_et, {1}, {1});
|
||||
auto eq_right_concat = std::make_shared<ov::opset1::Concat>(ov::OutputVector{eq_right_reshape, eq_right_concat_const}, 0);
|
||||
auto eq_right_ss_begin = ov::opset1::Constant::create(idces_et, {2}, std::vector<size_t>{mid_idx, 0ul});
|
||||
auto eq_right_ss = getStridedSlice(idces_padded, eq_right_ss_begin, eq_right_concat, {0, 1});
|
||||
|
||||
auto equal = std::make_shared<ov::opset1::Equal>(eq_left_ss, eq_right_ss);
|
||||
auto cond = getReshape(equal, {-1, 1}, idces_et);
|
||||
|
||||
std::shared_ptr<ov::Node> then_bias = shape_ss;
|
||||
if (cur_idx != 0) {
|
||||
auto bias_const = ov::opset1::Constant::create(idces_et, {}, {cur_idx});
|
||||
then_bias = std::make_shared<ov::opset1::Add>(shape_ss, bias_const);
|
||||
}
|
||||
auto then_reshape = getReshape(then_bias, {1}, idces_et);
|
||||
auto then_ss_begin = ov::opset1::Constant::create(idces_et, {1}, {cur_idx});
|
||||
auto then = getStridedSlice(data_padded, then_ss_begin, then_reshape);
|
||||
|
||||
auto else_reshape = getReshape(shape_ss, {1}, idces_et);
|
||||
auto else_concat_const = ov::opset1::Constant::create(idces_et, {1}, {input_shapes[0][1].get_length()});
|
||||
auto else_concat = std::make_shared<ov::opset1::Concat>(ov::OutputVector{else_reshape, else_concat_const}, 0);
|
||||
auto else_bcast_const = ov::opset1::Constant::create(data_et, {}, {0});
|
||||
auto else_bcast = std::make_shared<ov::opset1::Broadcast>(else_bcast_const, else_concat);
|
||||
|
||||
auto select = std::make_shared<ov::opset1::Select>(cond, then, else_bcast);
|
||||
return select;
|
||||
};
|
||||
|
||||
ov::OutputVector concat_inputs(k);
|
||||
concat_inputs[mid_idx] = as_is_ss;
|
||||
for (size_t i = 0; i < k; ++i) {
|
||||
if (i == mid_idx)
|
||||
continue;
|
||||
concat_inputs[i] = getSelectBranch(i, mid_idx);
|
||||
}
|
||||
|
||||
auto final_concat = std::make_shared<ov::opset10::Concat>(concat_inputs, 1);
|
||||
return std::make_shared<ov::Model>(final_concat, params, "ngram");
|
||||
}
|
||||
|
||||
class NgramCPUTest : public testing::WithParamInterface<NgramTestParams>, virtual public SubgraphBaseTest, public CPUTestsBase {
|
||||
public:
|
||||
static std::string getTestCaseName(const testing::TestParamInfo<NgramTestParams> &obj) {
|
||||
std::vector<InputShape> input_shapes;
|
||||
size_t k;
|
||||
ElementType data_et;
|
||||
ElementType idces_et;
|
||||
std::tie(input_shapes, data_et, idces_et, k) = obj.param;
|
||||
std::ostringstream results;
|
||||
|
||||
results << "IS=(";
|
||||
for (const auto& shape : input_shapes) {
|
||||
results << CommonTestUtils::partialShape2str({shape.first}) << "_";
|
||||
}
|
||||
results << ")_TS=(";
|
||||
for (const auto& shape : input_shapes) {
|
||||
for (const auto& item : shape.second) {
|
||||
results << CommonTestUtils::vec2str(item) << "_";
|
||||
}
|
||||
}
|
||||
results << ")_data_prc=" << data_et << "_idces_prc=" << idces_et << "_k=" << k;
|
||||
return results.str();
|
||||
}
|
||||
|
||||
void generate_inputs(const std::vector<ov::Shape>& targetInputStaticShapes) override {
|
||||
inputs.clear();
|
||||
const auto& model_inputs = function->inputs();
|
||||
|
||||
const auto& data_et = model_inputs[0].get_element_type();
|
||||
const auto& data_shape = targetInputStaticShapes[0];
|
||||
auto embeddings_tensor = ov::test::utils::create_and_fill_tensor_consistently(data_et, data_shape, 100, 1, 1);
|
||||
inputs.insert({model_inputs[0].get_node_shared_ptr(), embeddings_tensor});
|
||||
|
||||
|
||||
const auto& indices_et = model_inputs[1].get_element_type();
|
||||
const auto& indices_shape = targetInputStaticShapes[1];
|
||||
const size_t batch_size = data_shape[0];
|
||||
auto indices_tensor = ov::test::utils::create_and_fill_tensor(indices_et, indices_shape, batch_size, 0);
|
||||
|
||||
if (indices_et == ov::element::i32) {
|
||||
auto* indices_data = indices_tensor.data<int32_t>();
|
||||
std::sort(indices_data, indices_data + indices_tensor.get_size());
|
||||
} else if (indices_et == ov::element::i64) {
|
||||
auto* indices_data = indices_tensor.data<int64_t>();
|
||||
std::sort(indices_data, indices_data + indices_tensor.get_size());
|
||||
} else {
|
||||
IE_THROW() << "Unexpected indices precision: " << indices_et;
|
||||
}
|
||||
inputs.insert({model_inputs[1].get_node_shared_ptr(), indices_tensor});
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
targetDevice = CommonTestUtils::DEVICE_CPU;
|
||||
std::vector<InputShape> inputShapes;
|
||||
ElementType data_et;
|
||||
ElementType idces_et;
|
||||
size_t k;
|
||||
std::tie(inputShapes, data_et, idces_et, k) = this->GetParam();
|
||||
init_input_shapes(inputShapes);
|
||||
function = initNgram(inputDynamicShapes, data_et, idces_et, k);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(NgramCPUTest, CompareWithRefs) {
|
||||
run();
|
||||
CheckNumberOfNodesWithType(compiledModel, "Ngram", 1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::vector<InputShape>> inputShapes = {
|
||||
{
|
||||
InputShape{{-1, 2}, {{3, 2}, {5, 2}, {7, 2}}},
|
||||
InputShape{{-1, 2}, {{3, 2}, {5, 2}, {7, 2}}}
|
||||
},
|
||||
{
|
||||
InputShape{{-1, 256}, {{12, 256}, {25, 256}}},
|
||||
InputShape{{-1, 2}, {{12, 2}, {25, 2}}}
|
||||
},
|
||||
{
|
||||
InputShape{{-1, 1}, {{12, 1}}},
|
||||
InputShape{{-1, 2}, {{12, 2}}}
|
||||
},
|
||||
};
|
||||
|
||||
std::vector<size_t> k_values = {2, 3, 5, 7};
|
||||
std::vector<ElementType> idces_precisions = {ElementType::i32, ElementType::i64};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(smoke_Ngram, NgramCPUTest,
|
||||
::testing::Combine(::testing::ValuesIn(inputShapes),
|
||||
::testing::Values(ElementType::f32),
|
||||
::testing::ValuesIn(idces_precisions),
|
||||
::testing::ValuesIn(k_values)),
|
||||
NgramCPUTest::getTestCaseName);
|
||||
} // namespace
|
||||
} // namespace CPUSubgraphTestsDefinitions
|
||||
Reference in new issue
Block a user