blob: d219e986aee09a3a9bd75447daed89308df8767f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#if MXNET_USE_MKLDNN == 1
#include <string>
#include <utility>
#include <vector>
#include "../../contrib/transformer-inl.h"
#include "../../quantization/quantization_utils.h"
#include "../../tensor/elemwise_unary_op.h"
#include "../common.h"
#include "./mkldnn_transformer-inl.h"
namespace mxnet {
namespace op {
DMLC_REGISTER_PARAMETER(MKLDNNSelfAttParam);
template <int base_num_inputs>
static bool SgMKLDNNSelfAttShape(const NodeAttrs& attrs,
mxnet::ShapeVector* in_shapes,
mxnet::ShapeVector* out_shapes) {
const auto& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized) {
mxnet::ShapeVector base_in_shapes;
mxnet::ShapeVector base_out_shapes = {out_shapes->at(0)};
for (int i = 0; i < base_num_inputs; i++) {
base_in_shapes.emplace_back(in_shapes->at(i));
}
bool ret = DefaultSubgraphOpShape(attrs, &base_in_shapes, &base_out_shapes);
for (size_t i = 0; i < in_shapes->size(); ++i) {
if (i < base_in_shapes.size())
in_shapes->at(i) = base_in_shapes[i];
else
SHAPE_ASSIGN_CHECK(*in_shapes, i, mxnet::TShape({1}));
}
out_shapes->resize(3);
out_shapes->at(0) = base_out_shapes[0];
if (!param.enable_float_output) {
SHAPE_ASSIGN_CHECK(*out_shapes, 1, mxnet::TShape({1})); // min output
SHAPE_ASSIGN_CHECK(*out_shapes, 2, mxnet::TShape({1})); // max output
}
return ret;
} else {
return DefaultSubgraphOpShape(attrs, in_shapes, out_shapes);
}
}
static bool SgMKLDNNSelfAttQKInferType(const nnvm::NodeAttrs& attrs,
std::vector<int>* in_types,
std::vector<int>* out_types) {
const auto& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized) {
CHECK(in_types->at(0) == mshadow::kInt8)
<< "QuantizedInterleavedMatMulSelfAttQK only supports int8 input, "
"while "
<< in_types->at(0) << " is given.";
TYPE_ASSIGN_CHECK(*in_types, 1, mshadow::kFloat32); // min value
TYPE_ASSIGN_CHECK(*in_types, 2, mshadow::kFloat32); // max value
if (param.enable_float_output) {
TYPE_ASSIGN_CHECK(*out_types, 0, mshadow::kFloat32); // output
} else {
if (param.min_calib_range.has_value() && param.max_calib_range.has_value()) {
TYPE_ASSIGN_CHECK(*out_types, 0, mshadow::kInt8); // output
} else {
TYPE_ASSIGN_CHECK(*out_types, 0, mshadow::kInt32); // output
}
TYPE_ASSIGN_CHECK(*out_types, 1, mshadow::kFloat32); // min output
TYPE_ASSIGN_CHECK(*out_types, 2, mshadow::kFloat32); // max output
}
return true;
} else {
return DefaultSubgraphOpType(attrs, in_types, out_types);
}
}
template <int base_num_inputs>
static bool SgMKLDNNSelfAttStorageType(const nnvm::NodeAttrs& attrs,
const int dev_mask,
DispatchMode* dispatch_mode,
std::vector<int>* in_attrs,
std::vector<int>* out_attrs) {
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized) {
std::vector<int> base_in_attrs;
std::vector<int> base_out_attrs{out_attrs->at(0)};
for (int i = 0; i < base_num_inputs; i++) {
base_in_attrs.emplace_back(in_attrs->at(i));
}
bool ret = DefaultSubgraphOpStorageType(
attrs, dev_mask, dispatch_mode, &base_in_attrs, &base_out_attrs);
for (size_t i = 0; i < in_attrs->size(); ++i) {
if (i < base_in_attrs.size())
in_attrs->at(i) = base_in_attrs[i];
else
type_assign(&in_attrs->at(i), mxnet::kDefaultStorage);
}
out_attrs->at(0) = base_out_attrs[0];
if (!param.enable_float_output) {
type_assign(&out_attrs->at(1), mxnet::kDefaultStorage);
type_assign(&out_attrs->at(2), mxnet::kDefaultStorage);
}
return ret;
} else {
return DefaultSubgraphOpStorageType(attrs, dev_mask, dispatch_mode, in_attrs, out_attrs);
}
}
class SgMKLDNNSelfAttQKOp {
public:
explicit SgMKLDNNSelfAttQKOp(const nnvm::NodeAttrs& attrs)
: param_(nnvm::get<MKLDNNSelfAttParam>(attrs.parsed)) {}
void Forward(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs);
void Backward(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
LOG(FATAL) << "Not implemented: subgraph mkldnn fully connected only supports "
"inference computation.";
}
void Initialize(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs);
bool IsInitialized() {
return initialized_;
}
private:
bool initialized_{false};
MKLDNNSelfAttParam param_;
mkldnn_args_map_t args_;
std::shared_ptr<dnnl::matmul> fwd_;
std::shared_ptr<dnnl::memory> cached_query_mem_;
std::shared_ptr<dnnl::memory> cached_key_mem_;
std::shared_ptr<dnnl::memory> cached_out_mem_;
float min_data_;
float max_data_;
float min_output_;
float max_output_;
float data_scale_{0.0f};
};
static OpStatePtr CreateSgMKLDNNSelfAttQKState(const nnvm::NodeAttrs& attrs,
Context ctx,
const mxnet::ShapeVector& in_shapes,
const std::vector<int>& in_types) {
return OpStatePtr::Create<SgMKLDNNSelfAttQKOp>(attrs);
}
static void SgMKLDNNSelfAttQKForward(const OpStatePtr& state_pointer,
const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
SgMKLDNNSelfAttQKOp& op = state_pointer.get_state<SgMKLDNNSelfAttQKOp>();
if (!op.IsInitialized()) {
op.Initialize(ctx, inputs, req, outputs);
}
op.Forward(ctx, inputs, req, outputs);
}
void SgMKLDNNSelfAttQKOp::Initialize(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
using namespace mkldnn;
const auto qkv_tensor = inputs[0];
const auto out_tensor = outputs[0];
const auto qkv_dtype = get_mkldnn_type(qkv_tensor.dtype());
const memory::dim heads = param_.heads;
const memory::dim sequences = inputs[0].shape()[1];
const memory::dim qkv_seq_len = inputs[0].shape()[0];
const memory::dim output_lin_dim = inputs[0].shape()[2];
const memory::dim embed_dim = output_lin_dim / 3;
const memory::dim head_dim = embed_dim / heads;
const memory::dim attn_batches = heads * sequences;
const memory::dim lead_dim = attn_batches * 3 * head_dim;
const memory::dim batch_stride = 3 * head_dim;
float min_data = 0.0f;
float max_data = 0.0f;
if (param_.quantized) {
min_data_ = inputs[1].data().dptr<float>()[0];
max_data_ = inputs[2].data().dptr<float>()[0];
}
const auto engine = CpuEngine::Get()->get_engine();
memory::dims query_dims = {attn_batches, qkv_seq_len, head_dim};
memory::dims key_dims = {attn_batches, head_dim, qkv_seq_len};
memory::dims out_dims = {attn_batches, qkv_seq_len, qkv_seq_len};
memory::dims query_strides = {batch_stride, lead_dim, 1};
memory::dims key_strides = {batch_stride, 1, lead_dim};
auto query_md = memory::desc(query_dims, qkv_dtype, query_strides);
auto key_md = memory::desc(key_dims, qkv_dtype, key_strides);
memory::desc out_md;
float oscale = 1.0f;
if (param_.quantized) {
data_scale_ = GetQuantizeScale(qkv_tensor.dtype(), min_data_, max_data_);
if (param_.min_calib_range.has_value() && param_.max_calib_range.has_value()) {
min_output_ = param_.min_calib_range.value();
max_output_ = param_.max_calib_range.value();
oscale = GetQuantizeScale(out_tensor.dtype(), min_output_, max_output_) /
(data_scale_ * data_scale_);
out_md = memory::desc(out_dims, memory::data_type::s8, memory::format_tag::abc);
} else if (param_.enable_float_output) {
oscale = 1.0f / (data_scale_ * data_scale_);
out_md = dnnl::memory::desc(out_dims, memory::data_type::f32, memory::format_tag::abc);
} else {
mshadow::Stream<cpu>* s = ctx.get_stream<cpu>();
mxnet_op::Kernel<QuantizationRangeForS8S8MultiplicationStruct, cpu>::Launch(
s, 1, &min_output_, &max_output_, &min_data, &max_data, &min_data, &max_data);
out_md = dnnl::memory::desc(out_dims, memory::data_type::s32, memory::format_tag::abc);
}
} else {
out_md = dnnl::memory::desc(out_dims, memory::data_type::f32, memory::format_tag::abc);
}
oscale /= sqrt(static_cast<float>(head_dim)); // combine quantized scale and sqrt(head_dim)
dnnl::primitive_attr attr;
attr.set_output_scales(0, {oscale});
auto matmul_d = matmul::desc(query_md, key_md, out_md);
auto matmul_pd = matmul::primitive_desc(matmul_d, attr, engine);
fwd_ = std::make_shared<matmul>(matmul_pd);
MSHADOW_TYPE_SWITCH(inputs[0].dtype(), DType, {
DType* query_mem_ptr = inputs[0].data().dptr<DType>();
DType* key_mem_ptr = query_mem_ptr + head_dim;
cached_query_mem_ = std::make_shared<memory>(query_md, engine, query_mem_ptr);
cached_key_mem_ = std::make_shared<memory>(key_md, engine, key_mem_ptr);
});
MSHADOW_TYPE_SWITCH(outputs[0].dtype(), DType, {
cached_out_mem_ = std::make_shared<memory>(out_md, engine, outputs[0].data().dptr<DType>());
});
args_[DNNL_ARG_SRC] = *cached_query_mem_;
args_[DNNL_ARG_WEIGHTS] = *cached_key_mem_;
args_[DNNL_ARG_DST] = *cached_out_mem_;
initialized_ = true;
}
void SgMKLDNNSelfAttQKOp::Forward(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
const size_t head_dim = inputs[0].shape()[2] / 3 / param_.heads;
MSHADOW_TYPE_SWITCH(inputs[0].dtype(), DType, {
DType* query_mem_ptr = inputs[0].data().dptr<DType>();
DType* key_mem_ptr = query_mem_ptr + head_dim;
cached_query_mem_->set_data_handle(query_mem_ptr);
cached_key_mem_->set_data_handle(key_mem_ptr);
});
MSHADOW_TYPE_SWITCH(outputs[0].dtype(), DType, {
cached_out_mem_->set_data_handle(outputs[0].data().dptr<DType>());
});
MKLDNNStream::Get()->RegisterPrimArgs(*fwd_, args_);
MKLDNNStream::Get()->Submit();
if (param_.quantized && !param_.enable_float_output) {
float* output_min = outputs[1].data().dptr<float>();
float* output_max = outputs[2].data().dptr<float>();
*output_min = min_output_;
*output_max = max_output_;
}
}
nnvm::ObjectPtr SgMKLDNNSelfAttQKQuantizedOp(const NodeAttrs& attrs) {
nnvm::ObjectPtr node = nnvm::Node::Create();
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
node->attrs.op = Op::Get("_sg_mkldnn_selfatt_qk");
node->attrs.name = "quantized_" + attrs.name;
node->attrs.dict = attrs.dict;
node->attrs.dict["heads"] = std::to_string(param.heads);
node->attrs.dict["quantized"] = "True";
node->attrs.subgraphs.reserve(attrs.subgraphs.size());
for (auto sub : attrs.subgraphs) {
node->attrs.subgraphs.push_back(sub);
}
node->op()->attr_parser(&(node->attrs));
return node;
}
NNVM_REGISTER_OP(_sg_mkldnn_selfatt_qk)
.describe(R"code(_sg_mkldnn_selfatt_qk)code" ADD_FILELINE)
.set_num_inputs([](const NodeAttrs& attrs) {
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized) {
return 3;
} else {
return 1;
}
})
.set_num_outputs([](const NodeAttrs& attrs) {
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized && !param.enable_float_output) {
return 3;
} else {
return 1;
}
})
.set_attr_parser(ParamParser<MKLDNNSelfAttParam>)
.set_attr<nnvm::FListInputNames>("FListInputNames",
[](const NodeAttrs& attrs) {
auto const& param =
nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
std::vector<std::string> input_names{"queries_keys_values"};
if (param.quantized) {
input_names.emplace_back("min_qkv");
input_names.emplace_back("max_qkv");
}
return input_names;
})
.set_attr<nnvm::FListOutputNames>("FListOutputNames",
[](const NodeAttrs& attrs) {
auto const& param =
nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
std::vector<std::string> output_names{"output"};
if (param.quantized && !param.enable_float_output) {
output_names.emplace_back("min_output");
output_names.emplace_back("max_output");
}
return output_names;
})
.set_attr<mxnet::FInferShape>("FInferShape", SgMKLDNNSelfAttShape<1>)
.set_attr<nnvm::FInferType>("FInferType", SgMKLDNNSelfAttQKInferType)
.set_attr<FInferStorageType>("FInferStorageType", SgMKLDNNSelfAttStorageType<1>)
.set_attr<FCreateOpState>("FCreateOpState", CreateSgMKLDNNSelfAttQKState)
.set_attr<FStatefulComputeEx>("FStatefulComputeEx<cpu>", SgMKLDNNSelfAttQKForward)
.set_attr<bool>("TIsMKLDNN", true)
.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
.set_attr<FQuantizable>("FQuantizable",
[](const NodeAttrs& attrs) { return QuantizeType::kMust; })
.set_attr<FQuantizedOp>("FQuantizedOp", SgMKLDNNSelfAttQKQuantizedOp)
.set_attr<FNeedRequantize>("FNeedRequantize", [](const NodeAttrs& attrs) { return true; })
.add_argument("queries_keys_values",
"NDArray-or-Symbol",
"Interleaved queries, keys and values")
.add_arguments(MKLDNNSelfAttParam::__FIELDS__());
/**********************************_sg_mkldnn_selfatt_valatt**********************************/
static bool SgMKLDNNSelfAttValAttInferType(const nnvm::NodeAttrs& attrs,
std::vector<int>* in_types,
std::vector<int>* out_types) {
const auto& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized) {
TYPE_ASSIGN_CHECK(*in_types, 0, mshadow::kInt8); // qkv input
TYPE_ASSIGN_CHECK(*in_types, 1, mshadow::kUint8); // att input
// min qkv, max qkv, min att, max att
for (size_t i = 2; i < in_types->size(); ++i) {
TYPE_ASSIGN_CHECK(*in_types, i, mshadow::kFloat32);
}
if (param.enable_float_output) {
TYPE_ASSIGN_CHECK(*out_types, 0, mshadow::kFloat32); // output
} else {
if (param.min_calib_range.has_value() && param.max_calib_range.has_value()) {
TYPE_ASSIGN_CHECK(*out_types, 0, mshadow::kInt8); // output
} else {
TYPE_ASSIGN_CHECK(*out_types, 0, mshadow::kInt32); // output
}
TYPE_ASSIGN_CHECK(*out_types, 1, mshadow::kFloat32); // min output
TYPE_ASSIGN_CHECK(*out_types, 2, mshadow::kFloat32); // max output
}
return true;
} else {
return DefaultSubgraphOpType(attrs, in_types, out_types);
}
}
nnvm::ObjectPtr SgMKLDNNSelfAttValAttQuantizedOp(const NodeAttrs& attrs) {
nnvm::ObjectPtr node = nnvm::Node::Create();
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
node->attrs.op = Op::Get("_sg_mkldnn_selfatt_valatt");
node->attrs.name = "quantized_" + attrs.name;
node->attrs.dict = attrs.dict;
node->attrs.dict["heads"] = std::to_string(param.heads);
node->attrs.dict["quantized"] = "True";
node->attrs.subgraphs.reserve(attrs.subgraphs.size());
for (auto sub : attrs.subgraphs) {
node->attrs.subgraphs.push_back(sub);
}
node->op()->attr_parser(&(node->attrs));
return node;
}
class MKLDNNSelfAttValAttOp {
public:
explicit MKLDNNSelfAttValAttOp(const nnvm::NodeAttrs& attrs)
: param_(nnvm::get<MKLDNNSelfAttParam>(attrs.parsed)) {}
void Forward(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs);
void Backward(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
LOG(FATAL) << "Not implemented: subgraph mkldnn fully connected only supports "
"inference computation.";
}
void Initialize(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs);
bool IsInitialized() {
return initialized_;
}
private:
bool initialized_{false};
MKLDNNSelfAttParam param_;
mkldnn_args_map_t args_;
std::shared_ptr<dnnl::matmul> fwd_;
std::shared_ptr<dnnl::memory> cached_att_mem_;
std::shared_ptr<dnnl::memory> cached_qkv_mem_;
std::shared_ptr<dnnl::memory> cached_out_mem_;
float min_qkv_;
float max_qkv_;
float min_att_;
float max_att_;
float min_output_;
float max_output_;
float qkv_scale_{0.0f};
float att_scale_{0.0f};
};
static OpStatePtr CreateMKLDNNSelfAttValAttState(const nnvm::NodeAttrs& attrs,
Context ctx,
const mxnet::ShapeVector& in_shapes,
const std::vector<int>& in_types) {
return OpStatePtr::Create<MKLDNNSelfAttValAttOp>(attrs);
}
static void MKLDNNSelfAttValAttForward(const OpStatePtr& state_pointer,
const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
MKLDNNSelfAttValAttOp& op = state_pointer.get_state<MKLDNNSelfAttValAttOp>();
if (!op.IsInitialized()) {
op.Initialize(ctx, inputs, req, outputs);
}
op.Forward(ctx, inputs, req, outputs);
}
void MKLDNNSelfAttValAttOp::Initialize(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
const dnnl::memory::dim qkv_seq_len = inputs[0].shape()[0];
const dnnl::memory::dim sequences = inputs[0].shape()[1];
const dnnl::memory::dim output_lin_dim = inputs[0].shape()[2];
const dnnl::memory::dim embed_dim = output_lin_dim / 3;
const dnnl::memory::dim head_dim = embed_dim / param_.heads;
const dnnl::memory::dim attn_batches = param_.heads * sequences;
const dnnl::memory::dim lead_dim = attn_batches * 3 * head_dim;
const dnnl::memory::dim batch_stride = 3 * head_dim;
dnnl::memory::dims att_dims = {attn_batches, qkv_seq_len, qkv_seq_len};
dnnl::memory::dims qkv_dims = {attn_batches, qkv_seq_len, head_dim};
dnnl::memory::dims dst_dims = {attn_batches, qkv_seq_len, head_dim};
dnnl::memory::dims att_strides = {qkv_seq_len * qkv_seq_len, qkv_seq_len, 1};
dnnl::memory::dims qkv_strides = {batch_stride, lead_dim, 1};
auto att_dtype = inputs[1].dtype();
auto qkv_dtype = inputs[0].dtype();
auto out_dtype = outputs[0].dtype();
auto att_md = dnnl::memory::desc(att_dims, get_mkldnn_type(att_dtype), att_strides);
auto qkv_md = dnnl::memory::desc(qkv_dims, get_mkldnn_type(qkv_dtype), qkv_strides);
dnnl::memory::desc out_md;
dnnl::primitive_attr attr;
float oscale = 1.0f;
if (param_.quantized) {
min_qkv_ = inputs[2].data().dptr<float>()[0];
max_qkv_ = inputs[3].data().dptr<float>()[0];
min_att_ = inputs[4].data().dptr<float>()[0];
max_att_ = inputs[5].data().dptr<float>()[0];
qkv_scale_ = GetQuantizeScale(qkv_dtype, min_qkv_, max_qkv_);
att_scale_ = GetQuantizeScale(att_dtype, min_att_, max_att_);
if (param_.min_calib_range.has_value() && param_.max_calib_range.has_value()) {
min_output_ = param_.min_calib_range.value();
max_output_ = param_.max_calib_range.value();
oscale = GetQuantizeScale(out_dtype, min_output_, max_output_) / (qkv_scale_ * att_scale_);
attr.set_output_scales(0, {oscale});
} else if (param_.enable_float_output) {
oscale = 1.0f / (qkv_scale_ * att_scale_);
attr.set_output_scales(0, {oscale});
} else {
mshadow::Stream<cpu>* s = ctx.get_stream<cpu>();
mxnet_op::Kernel<QuantizationRangeForS8U8MultiplicationStruct, cpu>::Launch(
s, 1, &min_output_, &max_output_, &min_qkv_, &max_qkv_, &min_att_, &max_att_);
}
}
out_md = dnnl::memory::desc(dst_dims, get_mkldnn_type(out_dtype), dnnl::memory::format_tag::bac);
const auto engine = CpuEngine::Get()->get_engine();
auto matmul_d = dnnl::matmul::desc(att_md, qkv_md, out_md);
auto matmul_pd = dnnl::matmul::primitive_desc(matmul_d, attr, engine);
fwd_ = std::make_shared<dnnl::matmul>(matmul_pd);
MSHADOW_TYPE_SWITCH(att_dtype, DType, {
DType* att_ptr = inputs[1].data().dptr<DType>();
cached_att_mem_ = std::make_shared<dnnl::memory>(att_md, engine, att_ptr);
});
MSHADOW_TYPE_SWITCH(qkv_dtype, DType, {
DType* value_ptr = inputs[0].data().dptr<DType>() + 2 * head_dim;
cached_qkv_mem_ = std::make_shared<dnnl::memory>(qkv_md, engine, value_ptr);
});
MSHADOW_TYPE_SWITCH(out_dtype, DType, {
DType* out_ptr = outputs[0].data().dptr<DType>();
cached_out_mem_ = std::make_shared<dnnl::memory>(out_md, engine, out_ptr);
});
args_[DNNL_ARG_SRC] = *cached_att_mem_;
args_[DNNL_ARG_WEIGHTS] = *cached_qkv_mem_;
args_[DNNL_ARG_DST] = *cached_out_mem_;
initialized_ = true;
}
void MKLDNNSelfAttValAttOp::Forward(const OpContext& ctx,
const std::vector<NDArray>& inputs,
const std::vector<OpReqType>& req,
const std::vector<NDArray>& outputs) {
const auto engine = CpuEngine::Get()->get_engine();
const size_t head_dim = inputs[0].shape()[2] / param_.heads / 3;
MSHADOW_TYPE_SWITCH(inputs[1].dtype(), DType, {
DType* att_ptr = inputs[1].data().dptr<DType>();
cached_att_mem_->set_data_handle(att_ptr);
});
MSHADOW_TYPE_SWITCH(inputs[0].dtype(), DType, {
DType* value_ptr = inputs[0].data().dptr<DType>() + 2 * head_dim;
cached_qkv_mem_->set_data_handle(value_ptr);
});
MSHADOW_TYPE_SWITCH(outputs[0].dtype(), DType, {
DType* out_ptr = outputs[0].data().dptr<DType>();
cached_out_mem_->set_data_handle(out_ptr);
});
MKLDNNStream::Get()->RegisterPrimArgs(*fwd_, args_);
MKLDNNStream::Get()->Submit();
if (param_.quantized && !param_.enable_float_output) {
float* output_min = outputs[1].data().dptr<float>();
float* output_max = outputs[2].data().dptr<float>();
*output_min = min_output_;
*output_max = max_output_;
}
}
NNVM_REGISTER_OP(_sg_mkldnn_selfatt_valatt)
.describe(R"code(_sg_mkldnn_selfatt_valatt)code" ADD_FILELINE)
.set_num_inputs([](const NodeAttrs& attrs) {
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized) {
return 6;
} else {
return 2;
}
})
.set_num_outputs([](const NodeAttrs& attrs) {
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
if (param.quantized && !param.enable_float_output) {
return 3;
} else {
return 1;
}
})
.set_attr_parser(ParamParser<MKLDNNSelfAttParam>)
.set_attr<nnvm::FListInputNames>(
"FListInputNames",
[](const NodeAttrs& attrs) {
auto const& param = nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
std::vector<std::string> input_names{"queries_keys_values", "attention"};
if (param.quantized) {
input_names.emplace_back("min_qkv");
input_names.emplace_back("max_qkv");
input_names.emplace_back("min_attention");
input_names.emplace_back("max_attention");
}
return input_names;
})
.set_attr<nnvm::FListOutputNames>("FListOutputNames",
[](const NodeAttrs& attrs) {
auto const& param =
nnvm::get<MKLDNNSelfAttParam>(attrs.parsed);
std::vector<std::string> output_names{"output"};
if (param.quantized && !param.enable_float_output) {
output_names.emplace_back("min_output");
output_names.emplace_back("max_output");
}
return output_names;
})
.set_attr<mxnet::FInferShape>("FInferShape", SgMKLDNNSelfAttShape<2>)
.set_attr<nnvm::FInferType>("FInferType", SgMKLDNNSelfAttValAttInferType)
.set_attr<FInferStorageType>("FInferStorageType", SgMKLDNNSelfAttStorageType<2>)
.set_attr<FCreateOpState>("FCreateOpState", CreateMKLDNNSelfAttValAttState)
.set_attr<FStatefulComputeEx>("FStatefulComputeEx<cpu>", MKLDNNSelfAttValAttForward)
.set_attr<bool>("TIsMKLDNN", true)
.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
.set_attr<FQuantizable>("FQuantizable",
[](const NodeAttrs& attrs) { return QuantizeType::kMust; })
.set_attr<FQuantizedOp>("FQuantizedOp", SgMKLDNNSelfAttValAttQuantizedOp)
.set_attr<FNeedRequantize>("FNeedRequantize", [](const NodeAttrs& attrs) { return true; })
.add_argument("queries_keys_values",
"NDArray-or-Symbol",
"Queries, keys and values interleaved")
.add_argument("attention", "NDArray-or-Symbol", "Attention maps")
.add_arguments(MKLDNNSelfAttParam::__FIELDS__());
} // namespace op
} // namespace mxnet
#endif