From e623cf91fde1978e7bb201aa581dfd996e19a5a7 Mon Sep 17 00:00:00 2001 From: hyeonseok lee Date: Fri, 15 Jul 2022 14:51:02 +0900 Subject: [PATCH] [multi head attention] added unittest - Added layer/model unittest for multi head attention - Change == operator overloading to pass if both tensor has nan value Signed-off-by: hyeonseok lee --- nntrainer/tensor/tensor.cpp | 3 +- packaging/unittest_layers_v2.tar.gz | Bin 63035 -> 75210 bytes packaging/unittest_models_v2.tar.gz | Bin 25744 -> 43230 bytes test/input_gen/genLayerTests.py | 15 +++ test/input_gen/genModelTests_v2.py | 106 ++++++++++++++- test/input_gen/recorder_v2.py | 7 +- test/input_gen/transLayer.py | 21 ++- test/input_gen/transLayer_v2.py | 39 ++++++ test/unittest/layers/meson.build | 1 + .../unittest_layers_multi_head_attention.cpp | 70 ++++++++++ test/unittest/models/unittest_models.cpp | 145 +++++++++++++++++++++ 11 files changed, 401 insertions(+), 6 deletions(-) create mode 100644 test/unittest/layers/unittest_layers_multi_head_attention.cpp diff --git a/nntrainer/tensor/tensor.cpp b/nntrainer/tensor/tensor.cpp index daa4f89..7addada 100644 --- a/nntrainer/tensor/tensor.cpp +++ b/nntrainer/tensor/tensor.cpp @@ -227,7 +227,8 @@ bool Tensor::operator==(const Tensor &rhs) const { for (size_t i = 0; i < len; ++i) { /** not checking sign change is intentional to avoid float calculation * errors around 0 */ - if (std::isnan(data[i]) || std::isnan(rdata[i]) || + if ((std::isnan(data[i]) && !std::isnan(rdata[i])) || + (!std::isnan(data[i]) && std::isnan(rdata[i])) || std::fabs(data[i] - rdata[i]) > epsilon) return false; } diff --git a/packaging/unittest_layers_v2.tar.gz b/packaging/unittest_layers_v2.tar.gz index 7529d01a288facdd50f5424042bb410eb1f7e24a..1e5b099e5e829ec08d8d8c827f8bf143d476a00f 100644 GIT binary patch delta 14009 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 diff --git a/packaging/unittest_models_v2.tar.gz b/packaging/unittest_models_v2.tar.gz index 42d3dbea7723088a5e26f8f5fea9cf57464bb902..ced1a2323a144e4cacad65d8cde8d4846c04d9df 100644 GIT binary patch literal 43230 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

(76vkprg>rwysbKGZa$Oc+Z;O;UT8j^pL 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 literal 25744 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 NO6!d(3I!_4{{W_eh>QRL diff --git a/test/input_gen/genLayerTests.py b/test/input_gen/genLayerTests.py index 3d88ab8..405bb2c 100644 --- a/test/input_gen/genLayerTests.py +++ b/test/input_gen/genLayerTests.py @@ -98,6 +98,21 @@ if __name__ == "__main__": record_single(attention, [(2, 5, 7), (2, 3, 7), (2, 3, 7)], "attention_batched", {}, input_type='float') + # use float data to generate input here + multi_head_attention = K.layers.MultiHeadAttention(num_heads=2, key_dim=3) + record_single(multi_head_attention, [(1, 5, 7), (1, 3, 7), (1, 3, 7)], + "multi_head_attention_single_batch", {}, input_type='float') + record_single(multi_head_attention, [(2, 5, 7), (2, 3, 7), (2, 3, 7)], + "multi_head_attention", {}, input_type='float') + record_single(multi_head_attention, [(2, 5, 7), (2, 3, 7), (2, 3, 7)], + "multi_head_attention_return_attention_scores", {"return_attention_scores":True}, input_type='float') + multi_head_attention = K.layers.MultiHeadAttention(num_heads=2, key_dim=3, value_dim=5) + record_single(multi_head_attention, [(2, 5, 7), (2, 3, 7), (2, 3, 7)], + "multi_head_attention_value_dim", {}, input_type='float') + multi_head_attention = K.layers.MultiHeadAttention(num_heads=2, key_dim=3, output_shape=5) + record_single(multi_head_attention, [(2, 5, 7), (2, 3, 7), (2, 3, 7)], + "multi_head_attention_output_shape", {}, input_type='float') + rnn = K.layers.SimpleRNN(units=5, activation="tanh", return_sequences=False, diff --git a/test/input_gen/genModelTests_v2.py b/test/input_gen/genModelTests_v2.py index 9ebd599..842d036 100644 --- a/test/input_gen/genModelTests_v2.py +++ b/test/input_gen/genModelTests_v2.py @@ -8,7 +8,7 @@ # @brief Generate model tcs # @author Parichay Kapoor -from recorder_v2 import record_v2, inspect_file +from recorder_v2 import record_v2, inspect_file, _rand_like import torch class ReduceMeanLast(torch.nn.Module): @@ -72,6 +72,53 @@ class MolAttention(torch.nn.Module): return (output, kappa), loss +class MultiHeadAttention(torch.nn.Module): + def __init__(self, embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None, need_weights=True, provide_attention_mask=False): + super(MultiHeadAttention, self).__init__() + self.multi_head_attention = torch.nn.MultiheadAttention(embed_dim, num_heads, dropout, bias, add_bias_kv, add_zero_attn, kdim, vdim, batch_first=True) + self.loss = torch.nn.MSELoss() + self.need_weights = need_weights + self.provide_attention_mask = provide_attention_mask + + def forward(self, inputs, labels): + inputs, attn_mask = (inputs[:-1], inputs[-1]) if self.provide_attention_mask else (inputs, None) + query, *left = inputs + if len(left) == 0: + key = value = query + else: + key, value = left + + output, attention_weight = self.multi_head_attention(query, key, value, need_weights=self.need_weights, attn_mask=attn_mask) + loss = self.loss(output, labels[0]) + if attention_weight is not None: + output = [output, attention_weight] + + return output, loss + + def input_label_reader(input_dims, label_dims, input_dtype): + query_dim, key_dim, value_dim, *left_dim = input_dims + query_dtype, key_dtype, value_dtype, *left_dtype = input_dtype + assert(query_dtype == key_dtype == value_dtype) + if left_dim != []: + mask_dim = left_dim[0] + mask_dtype = left_dtype[0] + if mask_dtype == bool: + # Since nntrainer does not support bool type tensor yet, convert mask to float type + # todo: return bool type mask tensor + mask = torch.randn(mask_dim) > 0.5 + new_attn_mask = torch.zeros_like(mask, dtype=torch.float32) + new_attn_mask.masked_fill_(mask, float("-inf")) + mask = [new_attn_mask] + elif mask_dtype == int: + mask = [torch.randint(0, 1, mask_dim, torch.int32)] + else: + mask = _rand_like([mask_dim], -1e9, mask_dtype) + else: + mask = [] + inputs = _rand_like([query_dim, key_dim, value_dim], dtype=input_dtype if input_dtype is not None else float) + mask + labels = _rand_like(label_dims, dtype=float) + return inputs, labels + class FCRelu(torch.nn.Module): def __init__(self, decay=False): super().__init__() @@ -129,6 +176,63 @@ if __name__ == "__main__": name="mol_attention", ) + record_v2( + MultiHeadAttention(embed_dim=6, num_heads=2, bias=False, need_weights=False), + iteration=2, + input_dims=[(3,3,6), (3,2,6), (3,2,6)], + label_dims=[(3,3,6)], + input_dtype=[float, float, float], + name="multi_head_attention_disable_need_weights", + ) + + record_v2( + MultiHeadAttention(embed_dim=6, num_heads=2), + iteration=2, + input_dims=[(3,3,6), (3,2,6), (3,2,6)], + label_dims=[(3,3,6), (3,3,2)], + input_dtype=[float, float, float], + name="multi_head_attention", + ) + + record_v2( + MultiHeadAttention(embed_dim=6, num_heads=2, kdim=4, vdim=5), + iteration=2, + input_dims=[(3,3,6), (3,2,4), (3,2,5)], + label_dims=[(3,3,6), (3,3,2)], + input_dtype=[float, float, float], + name="multi_head_attention_kdim_vdim", + ) + + record_v2( + MultiHeadAttention(embed_dim=6, num_heads=2, provide_attention_mask=True), + iteration=2, + input_dims=[(3,3,6), (3,2,6), (3,2,6), (6,3,2)], + label_dims=[(3,3,6), (3,3,2)], + input_dtype=[float, float, float, float], + input_label_reader=MultiHeadAttention.input_label_reader, + name="multi_head_attention_float_attn_mask", + ) + + # @todo: change this pseudo bool type tensor to actual bool tensor + record_v2( + MultiHeadAttention(embed_dim=6, num_heads=2, provide_attention_mask=True), + iteration=2, + input_dims=[(3,3,6), (3,2,6), (3,2,6), (6,3,2)], + label_dims=[(3,3,6), (3,3,2)], + input_dtype=[float, float, float, bool], + input_label_reader=MultiHeadAttention.input_label_reader, + name="multi_head_attention_pseudo_bool_attn_mask", + ) + + record_v2( + MultiHeadAttention(embed_dim=6, num_heads=2), + iteration=2, + input_dims=[(3,3,6)], + label_dims=[(3,3,6), (3,3,3)], + input_dtype=[float], + name="multi_head_attention_self_attention", + ) + fc_relu_decay = FCRelu(decay=True) record_v2( fc_relu_decay, diff --git a/test/input_gen/recorder_v2.py b/test/input_gen/recorder_v2.py index 5a58b9d..9bc219c 100644 --- a/test/input_gen/recorder_v2.py +++ b/test/input_gen/recorder_v2.py @@ -79,18 +79,19 @@ def record_v2(model, iteration, input_dims, label_dims, name, clip=False, def record_iteration(write_fn): if input_label_reader != None: - inputs, labels = input_label_reader(input_dims, label_dims) + inputs, labels = input_label_reader(input_dims, label_dims, input_dtype) else: inputs = _rand_like(input_dims, dtype=input_dtype if input_dtype is not None else float) labels = _rand_like(label_dims, dtype=float) write_fn(inputs) write_fn(labels) write_fn(list(t for _, t in params_translated(model))) - output, loss = model(inputs, labels) + output, *losses = model(inputs, labels) write_fn(output) optimizer.zero_grad() - loss.backward() + for loss in losses: + loss.backward() if clip: norm = torch.nn.utils.clip_grad_norm_(model.parameters(), 0.0001) optimizer.step() diff --git a/test/input_gen/transLayer.py b/test/input_gen/transLayer.py index 9af1b42..77a0592 100644 --- a/test/input_gen/transLayer.py +++ b/test/input_gen/transLayer.py @@ -257,7 +257,6 @@ class GRUCellTransLayer(IdentityTransLayer): input = inputs[0] states = inputs[1:] output, states = self.tf_layer.call(input, states) - # print(output) return output def to_nntr_weights(self, tensorOrList): @@ -271,6 +270,23 @@ class GRUCellTransLayer(IdentityTransLayer): def to_nntr_trainable_weights(self, tensorOrList): return self.to_nntr_weights(tensorOrList) +class MultiHeadAttentionTransLayer(IdentityTransLayer): + def build(self, input_shape): + if not self.built: + query = tf.random.normal(input_shape[0]) + key = tf.random.normal(input_shape[1]) if len(input_shape) == 3 else None + value = tf.random.normal(input_shape[2 if len(input_shape) == 3 else 1]) + self.tf_layer(query, value, key) + + ## + # @brief call function + # @param inputs input with nntrainer layout + def call(self, inputs, provide_attention_mask=False, return_attention_scores=False): + inputs, mask = (inputs[:-1], inputs[-1]) if provide_attention_mask else (inputs, None) + query, key, value = (inputs[0], inputs[1], inputs[2]) if len(inputs) == 3 else (inputs[0], None, inputs[1]) + output = self.tf_layer.call(query, value, key, mask, return_attention_scores=return_attention_scores) + return [output[0], output[1]] if return_attention_scores else output + ## # @brief A factory function to attach translayer to existing layer # if nothing should be attached, it does not attach the layer @@ -292,4 +308,7 @@ def attach_trans_layer(layer): if isinstance(layer, K.layers.GRUCell): return GRUCellTransLayer(layer) + if isinstance(layer, K.layers.MultiHeadAttention): + return MultiHeadAttentionTransLayer(layer) + return layer diff --git a/test/input_gen/transLayer_v2.py b/test/input_gen/transLayer_v2.py index ca0b621..691afc4 100644 --- a/test/input_gen/transLayer_v2.py +++ b/test/input_gen/transLayer_v2.py @@ -115,6 +115,45 @@ def gru_translate(model): yield from new_params +@register_for_((torch.nn.MultiheadAttention)) +def multi_head_attention_translate(model): + def transpose_(weight): + return (weight[0], weight[1].transpose(1, 0)) + + params = [(name, tensor.detach()) for name, tensor in model.named_parameters()] + + getParamByName = lambda name: list(filter(lambda param: param[0] == name, params))[0] + + if model._qkv_same_embed_dim: + in_proj_weight = getParamByName('in_proj_weight') + w_q, w_k, w_v = in_proj_weight[1].chunk(3) + q_proj_weight = ('q_proj_weight', w_q) + k_proj_weight = ('k_proj_weight', w_k) + v_proj_weight = ('v_proj_weight', w_v) + else: + q_proj_weight = getParamByName('q_proj_weight') + k_proj_weight = getParamByName('k_proj_weight') + v_proj_weight = getParamByName('v_proj_weight') + + if model.in_proj_bias is not None: + in_proj_bias = getParamByName('in_proj_bias') + w_q, w_k, w_v = in_proj_bias[1].chunk(3) + q_proj_bias = ('q_proj_bias', w_q) + k_proj_bias = ('k_proj_bias', w_k) + v_proj_bias = ('v_proj_bias', w_v) + + out_proj_weight = getParamByName('out_proj.weight') + + if model.in_proj_bias is not None: + out_proj_bias = getParamByName('out_proj.bias') + + if model.in_proj_bias is None: + new_params = [transpose_(q_proj_weight), transpose_(k_proj_weight), transpose_(v_proj_weight), transpose_(out_proj_weight)] + else: + new_params = [transpose_(q_proj_weight), q_proj_bias, transpose_(k_proj_weight), k_proj_bias, transpose_(v_proj_weight), v_proj_bias, transpose_(out_proj_weight), out_proj_bias] + + yield from new_params + def translate(model): for child in model.children(): for registered_classes, fn in handler_book: diff --git a/test/unittest/layers/meson.build b/test/unittest/layers/meson.build index 502f60a..b6f43c5 100644 --- a/test/unittest/layers/meson.build +++ b/test/unittest/layers/meson.build @@ -60,6 +60,7 @@ test_target = [ 'unittest_layers_dropout.cpp', 'unittest_layers_reshape.cpp', # 'unittest_layers_mol_attention.cpp', + 'unittest_layers_multi_head_attention.cpp', ] if get_option('enable-tflite-backbone') diff --git a/test/unittest/layers/unittest_layers_multi_head_attention.cpp b/test/unittest/layers/unittest_layers_multi_head_attention.cpp new file mode 100644 index 0000000..995d628 --- /dev/null +++ b/test/unittest/layers/unittest_layers_multi_head_attention.cpp @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +/** + * Copyright (C) 2022 hyeonseok Lee + * + * @file unittest_layers_mol_attention.cpp + * @date 13 July 2022 + * @brief Multi Head Attention Layer Test + * @see https://github.com/nnstreamer/nntrainer + * @author hyeonseok Lee + * @bug No known bugs except for NYI items + */ +#include + +#include + +#include +#include + +auto semantic_multi_head_attention = LayerSemanticsParamType( + nntrainer::createLayer, + nntrainer::MultiHeadAttentionLayer::type, + {"num_heads=1", "projected_key_dim=1"}, 0, false, 3); + +auto semantic_multi_head_attention_with_mask = LayerSemanticsParamType( + nntrainer::createLayer, + nntrainer::MultiHeadAttentionLayer::type, + {"num_heads=1", "projected_key_dim=1"}, 0, false, 4); + +GTEST_PARAMETER_TEST( + MultiHeadAttention, LayerSemantics, + ::testing::Values(semantic_multi_head_attention, + semantic_multi_head_attention_with_mask)); + +auto multi_head_attention_single_batch = LayerGoldenTestParamType( + nntrainer::createLayer, + {"num_heads=2", "projected_key_dim=3"}, "1:1:5:7,1:1:3:7,1:1:3:7", + "multi_head_attention_single_batch.nnlayergolden", + LayerGoldenTestParamOptions::DEFAULT); + +auto multi_head_attention = LayerGoldenTestParamType( + nntrainer::createLayer, + {"num_heads=2", "projected_key_dim=3"}, "2:1:5:7,2:1:3:7,2:1:3:7", + "multi_head_attention.nnlayergolden", LayerGoldenTestParamOptions::DEFAULT); + +auto multi_head_attention_return_attention_scores = LayerGoldenTestParamType( + nntrainer::createLayer, + {"num_heads=2", "projected_key_dim=3", "return_attention_weight=before", + "average_attention_weight=false"}, + "2:1:5:7,2:1:3:7,2:1:3:7", + "multi_head_attention_return_attention_scores.nnlayergolden", + LayerGoldenTestParamOptions::DEFAULT); + +auto multi_head_attention_value_dim = LayerGoldenTestParamType( + nntrainer::createLayer, + {"num_heads=2", "projected_key_dim=3", "projected_value_dim=5"}, + "2:1:5:7,2:1:3:7,2:1:3:7", "multi_head_attention_value_dim.nnlayergolden", + LayerGoldenTestParamOptions::DEFAULT); + +auto multi_head_attention_output_shape = LayerGoldenTestParamType( + nntrainer::createLayer, + {"num_heads=2", "projected_key_dim=3", "output_shape=5"}, + "2:1:5:7,2:1:3:7,2:1:3:7", "multi_head_attention_output_shape.nnlayergolden", + LayerGoldenTestParamOptions::DEFAULT); + +GTEST_PARAMETER_TEST( + MultiHeadAttention, LayerGoldenTest, + ::testing::Values(multi_head_attention_single_batch, multi_head_attention, + multi_head_attention_return_attention_scores, + multi_head_attention_value_dim, + multi_head_attention_output_shape)); diff --git a/test/unittest/models/unittest_models.cpp b/test/unittest/models/unittest_models.cpp index 9079b82..2e3ab1b 100644 --- a/test/unittest/models/unittest_models.cpp +++ b/test/unittest/models/unittest_models.cpp @@ -97,6 +97,134 @@ static std::unique_ptr makeMolAttentionMasked() { return nn; } +static std::unique_ptr +makeMultiHeadAttention_disable_need_weights() { + std::unique_ptr nn(new NeuralNetwork()); + nn->setProperty({"batch_size=3"}); + + auto outer_graph = makeGraph({ + {"input", {"name=input_0", "input_shape=1:3:6"}}, + {"input", {"name=input_1", "input_shape=1:2:6"}}, + {"input", {"name=input_2", "input_shape=1:2:6"}}, + {"multi_head_attention", + {"name=multi_head_attention", "input_layers=input_0, input_1, input_2", + "disable_bias=true", "num_heads=2"}}, + {"mse", {"name=loss", "input_layers=multi_head_attention"}}, + }); + + for (auto &node : outer_graph) { + nn->addLayer(node); + } + + nn->setOptimizer(ml::train::createOptimizer("sgd", {"learning_rate = 0.1"})); + nn->setProperty({"input_layers=input_0, input_1, input_2"}); + + return nn; +} + +static std::unique_ptr makeMultiHeadAttention() { + std::unique_ptr nn(new NeuralNetwork()); + nn->setProperty({"batch_size=3"}); + + auto outer_graph = makeGraph({ + {"input", {"name=input_0", "input_shape=1:3:6"}}, + {"input", {"name=input_1", "input_shape=1:2:6"}}, + {"input", {"name=input_2", "input_shape=1:2:6"}}, + {"multi_head_attention", + {"name=multi_head_attention", "input_layers=input_0, input_1, input_2", + "num_heads=2", "return_attention_weight=after"}}, + {"mse", {"name=loss1", "input_layers=multi_head_attention(0)"}}, + {"mse", {"name=loss2", "input_layers=multi_head_attention(1)"}}, + }); + + for (auto &node : outer_graph) { + nn->addLayer(node); + } + + nn->setOptimizer(ml::train::createOptimizer("sgd", {"learning_rate = 0.1"})); + nn->setProperty( + {"input_layers=input_0, input_1, input_2", "label_layers=loss1, loss2"}); + + return nn; +} + +static std::unique_ptr makeMultiHeadAttention_kdim_vdim() { + std::unique_ptr nn(new NeuralNetwork()); + nn->setProperty({"batch_size=3"}); + + auto outer_graph = makeGraph({ + {"input", {"name=input_0", "input_shape=1:3:6"}}, + {"input", {"name=input_1", "input_shape=1:2:4"}}, + {"input", {"name=input_2", "input_shape=1:2:5"}}, + {"multi_head_attention", + {"name=multi_head_attention", "input_layers=input_0, input_1, input_2", + "num_heads=2", "return_attention_weight=after"}}, + {"mse", {"name=loss1", "input_layers=multi_head_attention(0)"}}, + {"mse", {"name=loss2", "input_layers=multi_head_attention(1)"}}, + }); + + for (auto &node : outer_graph) { + nn->addLayer(node); + } + + nn->setOptimizer(ml::train::createOptimizer("sgd", {"learning_rate = 0.1"})); + nn->setProperty( + {"input_layers=input_0, input_1, input_2", "label_layers=loss1, loss2"}); + + return nn; +} + +static std::unique_ptr makeMultiHeadAttention_float_attn_mask() { + std::unique_ptr nn(new NeuralNetwork()); + nn->setProperty({"batch_size=3"}); + + auto outer_graph = makeGraph({ + {"input", {"name=input_0", "input_shape=1:3:6"}}, + {"input", {"name=input_1", "input_shape=1:2:6"}}, + {"input", {"name=input_2", "input_shape=1:2:6"}}, + {"input", {"name=input_3", "input_shape=2:3:2"}}, + {"multi_head_attention", + {"name=multi_head_attention", + "input_layers=input_0, input_1, input_2, input_3", "num_heads=2", + "return_attention_weight=after"}}, + {"mse", {"name=loss1", "input_layers=multi_head_attention(0)"}}, + {"mse", {"name=loss2", "input_layers=multi_head_attention(1)"}}, + }); + + for (auto &node : outer_graph) { + nn->addLayer(node); + } + + nn->setOptimizer(ml::train::createOptimizer("sgd", {"learning_rate = 0.1"})); + nn->setProperty({"input_layers=input_0, input_1, input_2, input_3", + "label_layers=loss1, loss2"}); + + return nn; +} + +static std::unique_ptr makeMultiHeadAttention_self_attention() { + std::unique_ptr nn(new NeuralNetwork()); + nn->setProperty({"batch_size=3"}); + + auto outer_graph = makeGraph({ + {"input", {"name=input_0", "input_shape=1:3:6"}}, + {"multi_head_attention", + {"name=multi_head_attention", "input_layers=input_0, input_0, input_0", + "num_heads=2", "return_attention_weight=after"}}, + {"mse", {"name=loss1", "input_layers=multi_head_attention(0)"}}, + {"mse", {"name=loss2", "input_layers=multi_head_attention(1)"}}, + }); + + for (auto &node : outer_graph) { + nn->addLayer(node); + } + + nn->setOptimizer(ml::train::createOptimizer("sgd", {"learning_rate = 0.1"})); + nn->setProperty({"input_layers=input_0", "label_layers=loss1, loss2"}); + + return nn; +} + GTEST_PARAMETER_TEST( model, nntrainerModelTest, ::testing::ValuesIn({ @@ -106,6 +234,23 @@ GTEST_PARAMETER_TEST( ModelTestOption::COMPARE_V2), mkModelTc_V2(makeMolAttentionMasked, "mol_attention_masked", ModelTestOption::COMPARE_RUN_V2), + mkModelTc_V2(makeMultiHeadAttention_disable_need_weights, + "multi_head_attention_disable_need_weights", + ModelTestOption::ALL_V2), + mkModelTc_V2(makeMultiHeadAttention, "multi_head_attention", + ModelTestOption::ALL_V2), + mkModelTc_V2(makeMultiHeadAttention_kdim_vdim, + "multi_head_attention_kdim_vdim", ModelTestOption::ALL_V2), + mkModelTc_V2(makeMultiHeadAttention_float_attn_mask, + "multi_head_attention_float_attn_mask", + ModelTestOption::ALL_V2), + /** @todo:change model if bool type tensor is supported */ + mkModelTc_V2(makeMultiHeadAttention_float_attn_mask, + "multi_head_attention_pseudo_bool_attn_mask", + ModelTestOption::ALL_V2), + mkModelTc_V2(makeMultiHeadAttention_self_attention, + "multi_head_attention_self_attention", + ModelTestOption::ALL_V2), mkModelIniTc(fc_relu_decay, DIM_UNUSED, NOT_USED_, ModelTestOption::COMPARE_V2), }), -- 2.7.4