Unverified Commit 9bb0b653 authored by Jayaram Bobba's avatar Jayaram Bobba Committed by GitHub

Added more convolution variants to DEX (#1223)

* CPU Direct Execution: Implement ConvertLayout and refactor

* CPU Direct Execution: Implement Convolution

* 1) Adds computation reuse to direct execution
2) Add avg_pool, broadcast and convolution_bias to direct execution
3) Moved some computation reuse utility functions to graph_utils

* Use lists instead of vectors to avoid reallocation overheads

* - Added convolution variants to direct execution
- Removed ConvolutionBiasRelu, use ConvolutionBias instead
- Reduced code duplication by moving functionality to mkldnn_emitter
  from cpu_emitter

* Style fix

* Moved mkldnn build_convolution to a templated method

* Style fix

* refactored mkldnn conv bprop builders

* Style fix
parent cce0c224
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "ngraph/runtime/cpu/mkldnn_invoke.hpp" #include "ngraph/runtime/cpu/mkldnn_invoke.hpp"
#include "ngraph/runtime/cpu/mkldnn_utils.hpp" #include "ngraph/runtime/cpu/mkldnn_utils.hpp"
#include "ngraph/runtime/cpu/op/conv_bias.hpp" #include "ngraph/runtime/cpu/op/conv_bias.hpp"
#include "ngraph/runtime/cpu/op/conv_relu.hpp"
using namespace std; using namespace std;
using namespace ngraph; using namespace ngraph;
...@@ -48,44 +49,9 @@ namespace ngraph ...@@ -48,44 +49,9 @@ namespace ngraph
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto input_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto output_format =
runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_data_desc = auto conv_index =
mkldnn_emitter->build_memory_descriptor(args[0], input_format); mkldnn_emitter->build_convolution<ngraph::op::Convolution>(node, args, out);
auto weights_desc =
mkldnn_emitter->build_memory_descriptor(args[1], weights_format);
auto result_desc =
mkldnn_emitter->build_memory_descriptor(out[0], output_format);
size_t conv_index = 0;
conv_index = mkldnn_emitter->build_convolution_forward(
input_data_desc,
weights_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index); auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
auto functor = [&, conv_index](CPURuntimeContext* ctx) { auto functor = [&, conv_index](CPURuntimeContext* ctx) {
...@@ -143,16 +109,42 @@ namespace ngraph ...@@ -143,16 +109,42 @@ namespace ngraph
} }
template <> template <>
void Builder::BUILDER_DECL(ngraph::op::ConvolutionBias) void Builder::BUILDER_DECL(ngraph::op::ConvolutionRelu)
{ {
auto convolution = static_cast<const ngraph::op::ConvolutionBias*>(node);
auto& functors = external_function->get_functors(); auto& functors = external_function->get_functors();
auto& tensor_data = external_function->get_tensor_data(); auto& tensor_data = external_function->get_tensor_data();
auto arg0_shape = args[0].get_shape(); auto& arg0_tensor = tensor_data[args[0].get_name()];
auto arg1_shape = args[1].get_shape(); auto& arg1_tensor = tensor_data[args[1].get_name()];
auto result_shape = out[0].get_shape(); auto& out_tensor = tensor_data[out[0].get_name()];
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{
auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto conv_index =
mkldnn_emitter->build_convolution<ngraph::op::ConvolutionRelu>(
node, args, out);
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
auto functor = [&, conv_index](CPURuntimeContext* ctx) {
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[0], arg0_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[1], arg1_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[2], out_tensor);
cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, conv_index);
};
functors.emplace_back(functor);
}
else
{
throw ngraph_error("ConvolutionRelu is only supported with MKLDNN kernel.");
}
}
template <>
void Builder::BUILDER_DECL(ngraph::op::ConvolutionBias)
{
auto& functors = external_function->get_functors();
auto& tensor_data = external_function->get_tensor_data();
auto& arg0_tensor = tensor_data[args[0].get_name()]; auto& arg0_tensor = tensor_data[args[0].get_name()];
auto& arg1_tensor = tensor_data[args[1].get_name()]; auto& arg1_tensor = tensor_data[args[1].get_name()];
...@@ -161,47 +153,10 @@ namespace ngraph ...@@ -161,47 +153,10 @@ namespace ngraph
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto input_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto bias_format = mkldnn_utils::get_input_mkldnn_format(node, 2);
auto output_format =
runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_data_desc = auto conv_index =
mkldnn_emitter->build_memory_descriptor(args[0], input_format); mkldnn_emitter->build_convolution<ngraph::op::ConvolutionBias>(
auto weights_desc = node, args, out);
mkldnn_emitter->build_memory_descriptor(args[1], weights_format);
auto bias_desc = mkldnn_emitter->build_memory_descriptor(args[2], bias_format);
auto result_desc =
mkldnn_emitter->build_memory_descriptor(out[0], output_format);
size_t conv_index = 0;
conv_index = mkldnn_emitter->build_convolution_forward(
input_data_desc,
weights_desc,
bias_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index); auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
auto functor = [&, conv_index](CPURuntimeContext* ctx) { auto functor = [&, conv_index](CPURuntimeContext* ctx) {
...@@ -219,6 +174,40 @@ namespace ngraph ...@@ -219,6 +174,40 @@ namespace ngraph
} }
} }
template <>
void Builder::BUILDER_DECL(ngraph::op::ConvolutionBiasAdd)
{
auto& functors = external_function->get_functors();
auto& tensor_data = external_function->get_tensor_data();
auto& arg0_tensor = tensor_data[args[0].get_name()];
auto& arg1_tensor = tensor_data[args[1].get_name()];
auto& arg2_tensor = tensor_data[args[2].get_name()];
auto& out_tensor = tensor_data[out[0].get_name()];
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{
auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto conv_index =
mkldnn_emitter->build_convolution<ngraph::op::ConvolutionBiasAdd>(
node, args, out);
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
auto functor = [&, conv_index](CPURuntimeContext* ctx) {
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[0], arg0_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[1], arg1_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[2], arg2_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[3], out_tensor);
cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, conv_index);
};
functors.emplace_back(functor);
}
else
{
throw ngraph_error("ConvolutionBiasAdd is only supported with MKLDNN kernel.");
}
}
template <> template <>
void Builder::BUILDER_DECL(ngraph::op::ConvolutionBackpropData) void Builder::BUILDER_DECL(ngraph::op::ConvolutionBackpropData)
{ {
...@@ -237,44 +226,18 @@ namespace ngraph ...@@ -237,44 +226,18 @@ namespace ngraph
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides_forward())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
// HACK to help MKLDNN pick the right implementation auto conv_index =
auto weights_format = mkldnn_emitter
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0); ->build_convolution_backward<ngraph::op::ConvolutionBackpropData>(
if (weights_format == mkldnn::memory::format::nchw) node, args, out);
{ auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
weights_format = mkldnn::memory::format::oihw;
} auto functor = [&, conv_index](CPURuntimeContext* ctx) {
auto weights_desc =
mkldnn_emitter->build_memory_descriptor(args[0], weights_format);
auto delta_desc = mkldnn_emitter->build_memory_descriptor(
args[1], runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1));
auto result_desc = mkldnn_emitter->build_memory_descriptor(
out[0], runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0));
size_t conv_bwd_data_index = mkldnn_emitter->build_convolution_backward_data(
weights_desc,
delta_desc,
result_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_bwd_data_index);
auto functor = [&, conv_bwd_data_index](CPURuntimeContext* ctx) {
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[0], arg0_tensor); cpu::mkldnn_utils::set_memory_ptr(ctx, deps[0], arg0_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[1], arg1_tensor); cpu::mkldnn_utils::set_memory_ptr(ctx, deps[1], arg1_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[2], out_tensor); cpu::mkldnn_utils::set_memory_ptr(ctx, deps[2], out_tensor);
cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, conv_bwd_data_index); cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, conv_index);
}; };
functors.emplace_back(functor); functors.emplace_back(functor);
} }
...@@ -344,38 +307,18 @@ namespace ngraph ...@@ -344,38 +307,18 @@ namespace ngraph
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides_forward())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_desc = mkldnn_emitter->build_memory_descriptor( auto conv_index =
args[0], runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0)); mkldnn_emitter
auto delta_desc = mkldnn_emitter->build_memory_descriptor( ->build_convolution_backward<ngraph::op::ConvolutionBackpropFilters>(
args[1], runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1)); node, args, out);
auto result_desc = mkldnn_emitter->build_memory_descriptor( auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
out[0], runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0));
auto functor = [&, conv_index](CPURuntimeContext* ctx) {
size_t conv_bwd_weights_index =
mkldnn_emitter->build_convolution_backward_weights(
input_desc,
delta_desc,
result_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_bwd_weights_index);
auto functor = [&, conv_bwd_weights_index](CPURuntimeContext* ctx) {
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[0], arg0_tensor); cpu::mkldnn_utils::set_memory_ptr(ctx, deps[0], arg0_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[1], arg1_tensor); cpu::mkldnn_utils::set_memory_ptr(ctx, deps[1], arg1_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[2], out_tensor); cpu::mkldnn_utils::set_memory_ptr(ctx, deps[2], out_tensor);
cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, conv_bwd_weights_index); cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, conv_index);
}; };
functors.emplace_back(functor); functors.emplace_back(functor);
} }
...@@ -426,6 +369,40 @@ namespace ngraph ...@@ -426,6 +369,40 @@ namespace ngraph
functors.emplace_back(functor); functors.emplace_back(functor);
} }
} }
template <>
void Builder::BUILDER_DECL(ngraph::op::ConvolutionBiasBackpropFiltersBias)
{
auto& functors = external_function->get_functors();
auto& tensor_data = external_function->get_tensor_data();
auto& arg0_tensor = tensor_data[args[0].get_name()];
auto& arg1_tensor = tensor_data[args[1].get_name()];
auto& out0_tensor = tensor_data[out[0].get_name()];
auto& out1_tensor = tensor_data[out[1].get_name()];
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{
auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto conv_index = mkldnn_emitter->build_convolution_backward<
ngraph::op::ConvolutionBiasBackpropFiltersBias>(node, args, out);
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
auto functor = [&, conv_index](CPURuntimeContext* ctx) {
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[0], arg0_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[1], arg1_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[2], out0_tensor);
cpu::mkldnn_utils::set_memory_ptr(ctx, deps[3], out1_tensor);
cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, conv_index);
};
functors.emplace_back(functor);
}
else
{
throw ngraph_error(
"ConvolutionBiasBackpropFiltersBias is only supported with MKLDNN kernel.");
}
}
} }
} }
} }
...@@ -405,12 +405,18 @@ namespace ngraph ...@@ -405,12 +405,18 @@ namespace ngraph
&runtime::cpu::Builder::build<ngraph::runtime::cpu::op::ConvertLayout>}, &runtime::cpu::Builder::build<ngraph::runtime::cpu::op::ConvertLayout>},
{TI(ngraph::op::Convolution), {TI(ngraph::op::Convolution),
&runtime::cpu::Builder::build<ngraph::op::Convolution>}, &runtime::cpu::Builder::build<ngraph::op::Convolution>},
{TI(ngraph::op::ConvolutionRelu),
&runtime::cpu::Builder::build<ngraph::op::ConvolutionRelu>},
{TI(ngraph::op::ConvolutionBias), {TI(ngraph::op::ConvolutionBias),
&runtime::cpu::Builder::build<ngraph::op::ConvolutionBias>}, &runtime::cpu::Builder::build<ngraph::op::ConvolutionBias>},
{TI(ngraph::op::ConvolutionBiasAdd),
&runtime::cpu::Builder::build<ngraph::op::ConvolutionBiasAdd>},
{TI(ngraph::op::ConvolutionBackpropData), {TI(ngraph::op::ConvolutionBackpropData),
&runtime::cpu::Builder::build<ngraph::op::ConvolutionBackpropData>}, &runtime::cpu::Builder::build<ngraph::op::ConvolutionBackpropData>},
{TI(ngraph::op::ConvolutionBackpropFilters), {TI(ngraph::op::ConvolutionBackpropFilters),
&runtime::cpu::Builder::build<ngraph::op::ConvolutionBackpropFilters>}, &runtime::cpu::Builder::build<ngraph::op::ConvolutionBackpropFilters>},
{TI(ngraph::op::ConvolutionBiasBackpropFiltersBias),
&runtime::cpu::Builder::build<ngraph::op::ConvolutionBiasBackpropFiltersBias>},
{TI(ngraph::op::Relu), &runtime::cpu::Builder::build<ngraph::op::Relu>}, {TI(ngraph::op::Relu), &runtime::cpu::Builder::build<ngraph::op::Relu>},
{TI(ngraph::op::Reshape), &runtime::cpu::Builder::build<ngraph::op::Reshape>}, {TI(ngraph::op::Reshape), &runtime::cpu::Builder::build<ngraph::op::Reshape>},
{TI(ngraph::op::Result), &runtime::cpu::Builder::build<ngraph::op::Result>}, {TI(ngraph::op::Result), &runtime::cpu::Builder::build<ngraph::op::Result>},
......
...@@ -2664,69 +2664,20 @@ namespace ngraph ...@@ -2664,69 +2664,20 @@ namespace ngraph
template <> template <>
void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionRelu) void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionRelu)
{ {
auto convolution = static_cast<const ngraph::op::ConvolutionRelu*>(node);
auto arg0_shape = args[0].get_shape();
auto arg1_shape = args[1].get_shape();
auto result_shape = out[0].get_shape();
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto input_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto output_format =
runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_data_desc = auto conv_index =
mkldnn_emitter->build_memory_descriptor(args[0], input_format); mkldnn_emitter->build_convolution<ngraph::op::ConvolutionRelu>(
auto weights_desc = node, args, out);
mkldnn_emitter->build_memory_descriptor(args[1], weights_format);
auto result_desc =
mkldnn_emitter->build_memory_descriptor(out[0], output_format);
size_t conv_index = 0;
const float ops_scale = 1.f;
const float ops_alpha = -0.f; // relu negative slope
const float ops_beta = 0.f;
mkldnn::post_ops ops;
ops.append_eltwise(
ops_scale, mkldnn::algorithm::eltwise_relu, ops_alpha, ops_beta);
conv_index = mkldnn_emitter->build_convolution_forward(
input_data_desc,
weights_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above(),
ops);
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index); auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << args[0].get_name() << ");\n"; << ", " << args[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
<< ", " << args[1].get_name() << ");\n"; << ", " << args[1].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[2]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[2])
<< ", " << out[0].get_name() << ");\n"; << ", " << out[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, " writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, "
<< to_string(conv_index) << ");\n"; << to_string(conv_index) << ");\n";
} }
...@@ -2863,45 +2814,11 @@ namespace ngraph ...@@ -2863,45 +2814,11 @@ namespace ngraph
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto input_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto output_format =
runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_data_desc = auto conv_index =
mkldnn_emitter->build_memory_descriptor(args[0], input_format); mkldnn_emitter->build_convolution<ngraph::op::Convolution>(node, args, out);
auto weights_desc =
mkldnn_emitter->build_memory_descriptor(args[1], weights_format);
auto result_desc =
mkldnn_emitter->build_memory_descriptor(out[0], output_format);
size_t conv_index = 0;
conv_index = mkldnn_emitter->build_convolution_forward(
input_data_desc,
weights_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index); auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << args[0].get_name() << ");\n"; << ", " << args[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
...@@ -2946,32 +2863,13 @@ namespace ngraph ...@@ -2946,32 +2863,13 @@ namespace ngraph
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides_forward())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_desc = mkldnn_emitter->build_memory_descriptor( auto conv_index =
args[0], runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0)); mkldnn_emitter
auto delta_desc = mkldnn_emitter->build_memory_descriptor( ->build_convolution_backward<ngraph::op::ConvolutionBackpropFilters>(
args[1], runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1)); node, args, out);
auto result_desc = mkldnn_emitter->build_memory_descriptor( auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
out[0], runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0));
size_t conv_bwd_weights_index =
mkldnn_emitter->build_convolution_backward_weights(
input_desc,
delta_desc,
result_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_bwd_weights_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << args[0].get_name() << ");\n"; << ", " << args[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
...@@ -2980,7 +2878,7 @@ namespace ngraph ...@@ -2980,7 +2878,7 @@ namespace ngraph
<< ", " << out[0].get_name() << ");\n"; << ", " << out[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, " writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, "
<< to_string(conv_bwd_weights_index) << ");\n"; << to_string(conv_index) << ");\n";
} }
else else
{ {
...@@ -3016,38 +2914,13 @@ namespace ngraph ...@@ -3016,38 +2914,13 @@ namespace ngraph
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides_forward())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
// HACK to help MKLDNN pick the right implementation auto conv_index =
auto weights_format = mkldnn_emitter
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0); ->build_convolution_backward<ngraph::op::ConvolutionBackpropData>(
if (weights_format == mkldnn::memory::format::nchw) node, args, out);
{ auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
weights_format = mkldnn::memory::format::oihw;
}
auto weights_desc =
mkldnn_emitter->build_memory_descriptor(args[0], weights_format);
auto delta_desc = mkldnn_emitter->build_memory_descriptor(
args[1], runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1));
auto result_desc = mkldnn_emitter->build_memory_descriptor(
out[0], runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0));
size_t conv_bwd_data_index = mkldnn_emitter->build_convolution_backward_data(
weights_desc,
delta_desc,
result_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_bwd_data_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << args[0].get_name() << ");\n"; << ", " << args[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
...@@ -3056,7 +2929,7 @@ namespace ngraph ...@@ -3056,7 +2929,7 @@ namespace ngraph
<< ", " << out[0].get_name() << ");\n"; << ", " << out[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, " writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, "
<< to_string(conv_bwd_data_index) << ");\n"; << to_string(conv_index) << ");\n";
} }
else else
{ {
...@@ -3085,135 +2958,14 @@ namespace ngraph ...@@ -3085,135 +2958,14 @@ namespace ngraph
template <> template <>
void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionBias) void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionBias)
{ {
auto convolution = static_cast<const ngraph::op::ConvolutionBias*>(node);
const TensorViewWrapper& data = args[0];
const TensorViewWrapper& weights = args[1];
const TensorViewWrapper& bias = args[2];
const TensorViewWrapper& result = out[0];
using namespace runtime::cpu::mkldnn_utils;
if (mkldnn_utils::use_mkldnn_kernel(node))
{
auto data_format = mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format = mkldnn_utils::get_input_mkldnn_format(node, 1);
auto bias_format = mkldnn_utils::get_input_mkldnn_format(node, 2);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto result_format = mkldnn_utils::get_output_mkldnn_format(node, 0);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto data_desc = mkldnn_emitter->build_memory_descriptor(data, data_format);
auto weights_desc =
mkldnn_emitter->build_memory_descriptor(weights, weights_format);
auto bias_desc = mkldnn_emitter->build_memory_descriptor(bias, bias_format);
auto result_desc =
mkldnn_emitter->build_memory_descriptor(result, result_format);
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
size_t conv_index = mkldnn_emitter->build_convolution_forward(
data_desc,
weights_desc,
bias_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << data.get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
<< ", " << weights.get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[2])
<< ", " << bias.get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[3])
<< ", " << result.get_name() << ");\n";
writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, "
<< to_string(conv_index) << ");\n";
}
else
{
throw ngraph_error("ConvolutionBias is only supported with MKLDNN kernel.");
}
}
template <>
void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionBiasRelu)
{
auto convolution = static_cast<const ngraph::op::ConvolutionBiasRelu*>(node);
auto arg0_shape = args[0].get_shape();
auto arg1_shape = args[1].get_shape();
auto result_shape = out[0].get_shape();
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto input_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1);
auto bias_format = mkldnn_utils::get_input_mkldnn_format(node, 2);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto output_format =
runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_data_desc = auto conv_index =
mkldnn_emitter->build_memory_descriptor(args[0], input_format); mkldnn_emitter->build_convolution<ngraph::op::ConvolutionBias>(
auto weights_desc = node, args, out);
mkldnn_emitter->build_memory_descriptor(args[1], weights_format);
auto bias_desc = mkldnn_emitter->build_memory_descriptor(args[2], bias_format);
auto result_desc =
mkldnn_emitter->build_memory_descriptor(out[0], output_format);
size_t conv_index = 0;
const float ops_scale = 1.f;
const float ops_alpha = -0.f; // relu negative slope
const float ops_beta = 0.f;
mkldnn::post_ops ops;
ops.append_eltwise(
ops_scale, mkldnn::algorithm::eltwise_relu, ops_alpha, ops_beta);
conv_index = mkldnn_emitter->build_convolution_forward(
input_data_desc,
weights_desc,
bias_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above(),
ops);
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index); auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << args[0].get_name() << ");\n"; << ", " << args[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
...@@ -3226,79 +2978,23 @@ namespace ngraph ...@@ -3226,79 +2978,23 @@ namespace ngraph
writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, " writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, "
<< to_string(conv_index) << ");\n"; << to_string(conv_index) << ");\n";
} }
else
{
throw ngraph_error("ConvolutionBias is only supported with MKLDNN kernel.");
}
} }
template <> template <>
void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionBiasAdd) void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionBiasAdd)
{ {
auto convolution = static_cast<const ngraph::op::ConvolutionBiasAdd*>(node);
auto arg0_shape = args[0].get_shape();
auto arg1_shape = args[1].get_shape();
auto arg2_shape = args[2].get_shape();
auto result_shape = out[0].get_shape();
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node)) if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node))
{ {
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto input_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format =
runtime::cpu::mkldnn_utils::get_input_mkldnn_format(node, 1);
auto bias_format = mkldnn_utils::get_input_mkldnn_format(node, 2);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto output_format =
runtime::cpu::mkldnn_utils::get_output_mkldnn_format(node, 0);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto input_data_desc = auto conv_index =
mkldnn_emitter->build_memory_descriptor(args[0], input_format); mkldnn_emitter->build_convolution<ngraph::op::ConvolutionBiasAdd>(
auto weights_desc = node, args, out);
mkldnn_emitter->build_memory_descriptor(args[1], weights_format);
auto bias_desc = mkldnn_emitter->build_memory_descriptor(args[2], bias_format);
// Since this is an in-place kernel, args[3] and out[0] will share the same
// memory buffer and descriptor
auto result_desc =
mkldnn_emitter->build_memory_descriptor(out[0], output_format);
size_t conv_index = 0;
mkldnn::post_ops ops;
ops.append_sum(1.f);
const float ops_scale = 1.f;
const float ops_alpha = -0.f; // relu negative slope
const float ops_beta = 0.f;
if (convolution->with_relu())
{
ops.append_eltwise(
ops_scale, mkldnn::algorithm::eltwise_relu, ops_alpha, ops_beta);
}
conv_index = mkldnn_emitter->build_convolution_forward(
input_data_desc,
weights_desc,
bias_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above(),
ops);
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index); auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << args[0].get_name() << ");\n"; << ", " << args[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
...@@ -3307,7 +3003,6 @@ namespace ngraph ...@@ -3307,7 +3003,6 @@ namespace ngraph
<< ", " << args[2].get_name() << ");\n"; << ", " << args[2].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[3]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[3])
<< ", " << out[0].get_name() << ");\n"; << ", " << out[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, " writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, "
<< to_string(conv_index) << ");\n"; << to_string(conv_index) << ");\n";
} }
...@@ -3320,55 +3015,21 @@ namespace ngraph ...@@ -3320,55 +3015,21 @@ namespace ngraph
template <> template <>
void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionBiasBackpropFiltersBias) void CPU_Emitter::EMITTER_DECL(ngraph::op::ConvolutionBiasBackpropFiltersBias)
{ {
auto convolution =
static_cast<const ngraph::op::ConvolutionBiasBackpropFiltersBias*>(node);
const TensorViewWrapper& data = args[0];
const TensorViewWrapper& delta = args[1];
const TensorViewWrapper& weights_delta = out[0];
const TensorViewWrapper& bias_delta = out[1];
using namespace runtime::cpu::mkldnn_utils;
if (mkldnn_utils::use_mkldnn_kernel(node)) if (mkldnn_utils::use_mkldnn_kernel(node))
{ {
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides_forward())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto data_format = mkldnn_utils::get_input_mkldnn_format(node, 0);
auto delta_format = mkldnn_utils::get_input_mkldnn_format(node, 1);
auto weights_delta_format = mkldnn_utils::get_output_mkldnn_format(node, 0);
auto bias_delta_format = mkldnn_utils::get_output_mkldnn_format(node, 1);
auto& mkldnn_emitter = external_function->get_mkldnn_emitter(); auto& mkldnn_emitter = external_function->get_mkldnn_emitter();
auto data_desc = mkldnn_emitter->build_memory_descriptor(data, data_format); auto conv_index = mkldnn_emitter->build_convolution_backward<
auto delta_desc = mkldnn_emitter->build_memory_descriptor(delta, delta_format); ngraph::op::ConvolutionBiasBackpropFiltersBias>(node, args, out);
auto weights_delta_desc = mkldnn_emitter->build_memory_descriptor(
weights_delta, weights_delta_format);
auto bias_delta_desc =
mkldnn_emitter->build_memory_descriptor(bias_delta, bias_delta_format);
size_t conv_index = mkldnn_emitter->build_convolution_backward_weights_bias(
data_desc,
delta_desc,
weights_delta_desc,
bias_delta_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
auto& deps = mkldnn_emitter->get_primitive_deps(conv_index); auto& deps = mkldnn_emitter->get_primitive_deps(conv_index);
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[0])
<< ", " << data.get_name() << ");\n"; << ", " << args[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[1])
<< ", " << delta.get_name() << ");\n"; << ", " << args[1].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[2]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[2])
<< ", " << weights_delta.get_name() << ");\n"; << ", " << out[0].get_name() << ");\n";
writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[3]) writer << "cpu::mkldnn_utils::set_memory_ptr(ctx, " << to_string(deps[3])
<< ", " << bias_delta.get_name() << ");\n"; << ", " << out[1].get_name() << ");\n";
writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, " writer << "cpu::mkldnn_utils::mkldnn_invoke_primitive(ctx, "
<< to_string(conv_index) << ");\n"; << to_string(conv_index) << ");\n";
......
...@@ -267,8 +267,6 @@ static const runtime::cpu::OpMap dispatcher{ ...@@ -267,8 +267,6 @@ static const runtime::cpu::OpMap dispatcher{
{TI(ngraph::op::GroupConvolution), &runtime::cpu::CPU_Emitter::emit<op::GroupConvolution>}, {TI(ngraph::op::GroupConvolution), &runtime::cpu::CPU_Emitter::emit<op::GroupConvolution>},
{TI(ngraph::op::ConvolutionBias), &runtime::cpu::CPU_Emitter::emit<op::ConvolutionBias>}, {TI(ngraph::op::ConvolutionBias), &runtime::cpu::CPU_Emitter::emit<op::ConvolutionBias>},
{TI(ngraph::op::ConvolutionRelu), &runtime::cpu::CPU_Emitter::emit<op::ConvolutionRelu>}, {TI(ngraph::op::ConvolutionRelu), &runtime::cpu::CPU_Emitter::emit<op::ConvolutionRelu>},
{TI(ngraph::op::ConvolutionBiasRelu),
&runtime::cpu::CPU_Emitter::emit<op::ConvolutionBiasRelu>},
{TI(ngraph::op::ConvolutionBiasAdd), &runtime::cpu::CPU_Emitter::emit<op::ConvolutionBiasAdd>}, {TI(ngraph::op::ConvolutionBiasAdd), &runtime::cpu::CPU_Emitter::emit<op::ConvolutionBiasAdd>},
// conv+bias backprop for data share the same implementation as ConvolutionBackpropData // conv+bias backprop for data share the same implementation as ConvolutionBackpropData
{TI(ngraph::op::ConvolutionBiasBackpropFiltersBias), {TI(ngraph::op::ConvolutionBiasBackpropFiltersBias),
......
...@@ -24,6 +24,11 @@ ...@@ -24,6 +24,11 @@
#include <mkldnn.hpp> #include <mkldnn.hpp>
#include "ngraph/coordinate_diff.hpp" #include "ngraph/coordinate_diff.hpp"
#include "ngraph/node.hpp"
#include "ngraph/op/convolution.hpp"
#include "ngraph/runtime/cpu/mkldnn_utils.hpp"
#include "ngraph/runtime/cpu/op/conv_bias.hpp"
#include "ngraph/runtime/cpu/op/conv_relu.hpp"
#include "ngraph/shape.hpp" #include "ngraph/shape.hpp"
#include "ngraph/strides.hpp" #include "ngraph/strides.hpp"
#include "ngraph/type/element_type.hpp" #include "ngraph/type/element_type.hpp"
...@@ -94,6 +99,98 @@ namespace ngraph ...@@ -94,6 +99,98 @@ namespace ngraph
const ngraph::CoordinateDiff& padding_above, const ngraph::CoordinateDiff& padding_above,
const mkldnn::post_ops& pops = mkldnn::post_ops()); const mkldnn::post_ops& pops = mkldnn::post_ops());
template <typename OP>
size_t build_convolution(const ngraph::Node* node,
const std::vector<TensorViewWrapper>& args,
const std::vector<TensorViewWrapper>& out)
{
auto convolution = static_cast<const OP*>(node);
// For dilation, MKLDNN wants to know how many elements to insert between, not how far
// apart to space the elements like nGraph. So we have to subtract 1 from each pos.
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto data_format = mkldnn_utils::get_input_mkldnn_format(node, 0);
auto weights_format = mkldnn_utils::get_input_mkldnn_format(node, 1);
// HACK to help MKLDNN pick the right implementation
if (weights_format == mkldnn::memory::format::nchw)
{
weights_format = mkldnn::memory::format::oihw;
}
auto result_format = mkldnn_utils::get_output_mkldnn_format(node, 0);
auto data_desc = build_memory_descriptor(args[0], data_format);
auto weights_desc = build_memory_descriptor(args[1], weights_format);
auto result_desc = build_memory_descriptor(out[0], result_format);
mkldnn::post_ops ops;
if (std::is_same<OP, ngraph::op::ConvolutionBiasAdd>())
{
ops.append_sum(1.f);
}
auto add_relu = [&]() {
if (dynamic_cast<const ngraph::op::ConvolutionBias*>(node))
{
return (dynamic_cast<const ngraph::op::ConvolutionBias*>(node))
->with_relu();
}
if (dynamic_cast<const ngraph::op::ConvolutionBiasAdd*>(node))
{
return (dynamic_cast<const ngraph::op::ConvolutionBiasAdd*>(node))
->with_relu();
}
if (dynamic_cast<const ngraph::op::ConvolutionRelu*>(node))
{
return true;
}
return false;
};
if (add_relu())
{
const float ops_scale = 1.f;
const float ops_alpha = -0.f; // relu negative slope
const float ops_beta = 0.f;
ops.append_eltwise(
ops_scale, mkldnn::algorithm::eltwise_relu, ops_alpha, ops_beta);
}
if (std::is_same<OP, ngraph::op::ConvolutionBias>() ||
std::is_same<OP, ngraph::op::ConvolutionBiasAdd>())
{
auto bias_format = mkldnn_utils::get_input_mkldnn_format(node, 2);
auto bias_desc = build_memory_descriptor(args[2], bias_format);
return build_convolution_forward(data_desc,
weights_desc,
bias_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above(),
ops);
}
else
{
return build_convolution_forward(data_desc,
weights_desc,
result_desc,
convolution->get_window_movement_strides(),
window_dilation_strides_adjusted,
convolution->get_padding_below(),
convolution->get_padding_above(),
ops);
}
}
mkldnn::memory::format query_convolution_forward_weight_format( mkldnn::memory::format query_convolution_forward_weight_format(
const mkldnn::memory::desc& input_data_desc, const mkldnn::memory::desc& input_data_desc,
const mkldnn::memory::desc& weights_desc_any, const mkldnn::memory::desc& weights_desc_any,
...@@ -142,6 +239,73 @@ namespace ngraph ...@@ -142,6 +239,73 @@ namespace ngraph
const ngraph::Strides& ng_dilation_strides, const ngraph::Strides& ng_dilation_strides,
const ngraph::CoordinateDiff& ng_padding_below, const ngraph::CoordinateDiff& ng_padding_below,
const ngraph::CoordinateDiff& ng_padding_above); const ngraph::CoordinateDiff& ng_padding_above);
template <typename OP>
size_t build_convolution_backward(const ngraph::Node* node,
const std::vector<TensorViewWrapper>& args,
const std::vector<TensorViewWrapper>& out)
{
auto convolution = static_cast<const OP*>(node);
Strides window_dilation_strides_adjusted;
for (size_t s : convolution->get_window_dilation_strides_forward())
{
window_dilation_strides_adjusted.push_back(s - 1);
}
auto arg0_format = mkldnn_utils::get_input_mkldnn_format(node, 0);
if (std::is_same<OP, ngraph::op::ConvolutionBackpropData>())
{
// HACK to help MKLDNN pick the right implementation
arg0_format = (arg0_format == mkldnn::memory::format::nchw)
? mkldnn::memory::format::oihw
: arg0_format;
}
auto arg0_desc = build_memory_descriptor(args[0], arg0_format);
auto arg1_format = mkldnn_utils::get_input_mkldnn_format(node, 1);
auto arg1_desc = build_memory_descriptor(args[1], arg1_format);
auto out0_format = mkldnn_utils::get_output_mkldnn_format(node, 0);
auto out0_desc = build_memory_descriptor(out[0], out0_format);
if (std::is_same<OP, ngraph::op::ConvolutionBackpropData>())
{
return build_convolution_backward_data(
arg0_desc,
arg1_desc,
out0_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
}
if (std::is_same<OP, ngraph::op::ConvolutionBackpropFilters>())
{
return build_convolution_backward_weights(
arg0_desc,
arg1_desc,
out0_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
}
if (std::is_same<OP, ngraph::op::ConvolutionBiasBackpropFiltersBias>())
{
auto out1_format = mkldnn_utils::get_output_mkldnn_format(node, 1);
auto out1_desc = build_memory_descriptor(out[1], out1_format);
return build_convolution_backward_weights_bias(
arg0_desc,
arg1_desc,
out0_desc,
out1_desc,
convolution->get_window_movement_strides_forward(),
window_dilation_strides_adjusted,
convolution->get_padding_below_forward(),
convolution->get_padding_above_forward());
}
}
size_t build_pooling_forward(mkldnn::algorithm pooling_algorithm, size_t build_pooling_forward(mkldnn::algorithm pooling_algorithm,
const mkldnn::memory::desc& input_desc, const mkldnn::memory::desc& input_desc,
const mkldnn::memory::desc& result_desc, const mkldnn::memory::desc& result_desc,
......
...@@ -50,7 +50,8 @@ void op::util::validate_convbias_shapes(const Shape& data_shape, ...@@ -50,7 +50,8 @@ void op::util::validate_convbias_shapes(const Shape& data_shape,
} }
op::ConvolutionBias::ConvolutionBias(const shared_ptr<op::Convolution>& conv, op::ConvolutionBias::ConvolutionBias(const shared_ptr<op::Convolution>& conv,
const shared_ptr<Node>& bias) const shared_ptr<Node>& bias,
const bool with_relu)
: RequiresTensorViewArgs("ConvolutionBias", : RequiresTensorViewArgs("ConvolutionBias",
{conv->get_argument(0), conv->get_argument(1), bias}) {conv->get_argument(0), conv->get_argument(1), bias})
, m_window_movement_strides(conv->get_window_movement_strides()) , m_window_movement_strides(conv->get_window_movement_strides())
...@@ -58,6 +59,7 @@ op::ConvolutionBias::ConvolutionBias(const shared_ptr<op::Convolution>& conv, ...@@ -58,6 +59,7 @@ op::ConvolutionBias::ConvolutionBias(const shared_ptr<op::Convolution>& conv,
, m_padding_below(conv->get_padding_below()) , m_padding_below(conv->get_padding_below())
, m_padding_above(conv->get_padding_above()) , m_padding_above(conv->get_padding_above())
, m_data_dilation_strides(conv->get_data_dilation_strides()) , m_data_dilation_strides(conv->get_data_dilation_strides())
, m_with_relu(with_relu)
{ {
if (conv->get_element_type() != bias->get_element_type()) if (conv->get_element_type() != bias->get_element_type())
{ {
...@@ -77,13 +79,15 @@ op::ConvolutionBias::ConvolutionBias(const shared_ptr<Node>& data_batch, ...@@ -77,13 +79,15 @@ op::ConvolutionBias::ConvolutionBias(const shared_ptr<Node>& data_batch,
const Strides& window_dilation_strides, const Strides& window_dilation_strides,
const CoordinateDiff& padding_below, const CoordinateDiff& padding_below,
const CoordinateDiff& padding_above, const CoordinateDiff& padding_above,
const Strides& data_dilation_strides) const Strides& data_dilation_strides,
const bool with_relu)
: RequiresTensorViewArgs("ConvolutionBias", {data_batch, filters, bias}) : RequiresTensorViewArgs("ConvolutionBias", {data_batch, filters, bias})
, m_window_movement_strides(window_movement_strides) , m_window_movement_strides(window_movement_strides)
, m_window_dilation_strides(window_dilation_strides) , m_window_dilation_strides(window_dilation_strides)
, m_padding_below(padding_below) , m_padding_below(padding_below)
, m_padding_above(padding_above) , m_padding_above(padding_above)
, m_data_dilation_strides(data_dilation_strides) , m_data_dilation_strides(data_dilation_strides)
, m_with_relu(with_relu)
{ {
auto& data_batch_shape = data_batch->get_shape(); auto& data_batch_shape = data_batch->get_shape();
auto& data_batch_et = data_batch->get_element_type(); auto& data_batch_et = data_batch->get_element_type();
...@@ -131,7 +135,8 @@ shared_ptr<Node> op::ConvolutionBias::copy_with_new_args(const NodeVector& new_a ...@@ -131,7 +135,8 @@ shared_ptr<Node> op::ConvolutionBias::copy_with_new_args(const NodeVector& new_a
get_window_dilation_strides(), get_window_dilation_strides(),
get_padding_below(), get_padding_below(),
get_padding_above(), get_padding_above(),
get_data_dilation_strides())); get_data_dilation_strides(),
m_with_relu));
} }
void op::ConvolutionBias::generate_adjoints(autodiff::Adjoints& adjoints, const NodeVector& deltas) void op::ConvolutionBias::generate_adjoints(autodiff::Adjoints& adjoints, const NodeVector& deltas)
......
...@@ -28,7 +28,8 @@ namespace ngraph ...@@ -28,7 +28,8 @@ namespace ngraph
{ {
public: public:
ConvolutionBias(const std::shared_ptr<op::Convolution>& conv, ConvolutionBias(const std::shared_ptr<op::Convolution>& conv,
const std::shared_ptr<Node>& bias); const std::shared_ptr<Node>& bias,
const bool with_relu = false);
ConvolutionBias(const std::shared_ptr<Node>& data_batch, ConvolutionBias(const std::shared_ptr<Node>& data_batch,
const std::shared_ptr<Node>& filters, const std::shared_ptr<Node>& filters,
...@@ -37,7 +38,8 @@ namespace ngraph ...@@ -37,7 +38,8 @@ namespace ngraph
const Strides& window_dilation_strides, const Strides& window_dilation_strides,
const CoordinateDiff& padding_below, const CoordinateDiff& padding_below,
const CoordinateDiff& padding_above, const CoordinateDiff& padding_above,
const Strides& data_dilation_strides); const Strides& data_dilation_strides,
const bool with_relu = false);
const Strides& get_window_movement_strides() const { return m_window_movement_strides; } const Strides& get_window_movement_strides() const { return m_window_movement_strides; }
const Strides& get_window_dilation_strides() const { return m_window_dilation_strides; } const Strides& get_window_dilation_strides() const { return m_window_dilation_strides; }
...@@ -47,6 +49,7 @@ namespace ngraph ...@@ -47,6 +49,7 @@ namespace ngraph
std::shared_ptr<Node> get_bias() { return get_argument(2); } std::shared_ptr<Node> get_bias() { return get_argument(2); }
std::shared_ptr<Node> get_filters() { return get_argument(1); } std::shared_ptr<Node> get_filters() { return get_argument(1); }
std::shared_ptr<Node> get_data_batch() { return get_argument(0); } std::shared_ptr<Node> get_data_batch() { return get_argument(0); }
bool with_relu() const { return m_with_relu; }
virtual std::shared_ptr<Node> virtual std::shared_ptr<Node>
copy_with_new_args(const NodeVector& new_args) const override; copy_with_new_args(const NodeVector& new_args) const override;
...@@ -58,6 +61,7 @@ namespace ngraph ...@@ -58,6 +61,7 @@ namespace ngraph
CoordinateDiff m_padding_below; CoordinateDiff m_padding_below;
CoordinateDiff m_padding_above; CoordinateDiff m_padding_above;
Strides m_data_dilation_strides; Strides m_data_dilation_strides;
bool m_with_relu;
}; };
/// \brief Filters and bias backprop for batched convolution operation. Data backprop is /// \brief Filters and bias backprop for batched convolution operation. Data backprop is
......
...@@ -95,78 +95,3 @@ std::shared_ptr<Node> op::ConvolutionRelu::copy_with_new_args(const NodeVector& ...@@ -95,78 +95,3 @@ std::shared_ptr<Node> op::ConvolutionRelu::copy_with_new_args(const NodeVector&
get_padding_above(), get_padding_above(),
get_data_dilation_strides())); get_data_dilation_strides()));
} }
op::ConvolutionBiasRelu::ConvolutionBiasRelu(const std::shared_ptr<op::ConvolutionBias>& conv)
: RequiresTensorViewArgs("ConvolutionBiasRelu",
{conv->get_argument(0), conv->get_argument(1), conv->get_argument(2)})
, m_window_movement_strides(conv->get_window_movement_strides())
, m_window_dilation_strides(conv->get_window_dilation_strides())
, m_padding_below(conv->get_padding_below())
, m_padding_above(conv->get_padding_above())
, m_data_dilation_strides(conv->get_data_dilation_strides())
{
set_value_type_checked(conv->get_element_type(), conv->get_shape());
}
op::ConvolutionBiasRelu::ConvolutionBiasRelu(const std::shared_ptr<Node>& data_batch,
const std::shared_ptr<Node>& filters,
const std::shared_ptr<Node>& bias,
const Strides& window_movement_strides,
const Strides& window_dilation_strides,
const CoordinateDiff& padding_below,
const CoordinateDiff& padding_above,
const Strides& data_dilation_strides)
: RequiresTensorViewArgs("ConvolutionBiasRelu", {data_batch, filters, bias})
, m_window_movement_strides(window_movement_strides)
, m_window_dilation_strides(window_dilation_strides)
, m_padding_below(padding_below)
, m_padding_above(padding_above)
, m_data_dilation_strides(data_dilation_strides)
{
auto& data_batch_shape = data_batch->get_shape();
auto& data_batch_et = data_batch->get_element_type();
auto& filters_shape = filters->get_shape();
auto& filters_et = filters->get_element_type();
//
// Make sure data batch and filter element types match.
//
if (data_batch_et != filters_et)
{
throw ngraph_error("Convolution data batch and filter element types do not match");
}
set_value_type_checked(
data_batch_et,
util::infer_convolution_output_shape(data_batch_shape,
filters_shape,
window_movement_strides,
window_dilation_strides,
padding_below,
padding_above,
data_dilation_strides,
0, /* batch_axis_data, */
1, /* input_channel_axis_data, */
1, /* input_channel_axis_filters, */
0, /* output_channel_axis_filters, */
0, /* batch_axis_result, */
1, /* output_channel_axis_result, */
""));
}
std::shared_ptr<Node> op::ConvolutionBiasRelu::copy_with_new_args(const NodeVector& new_args) const
{
if (new_args.size() != 3)
{
throw ngraph_error("Incorrect number of new arguments");
}
return std::shared_ptr<Node>(new ConvolutionBiasRelu(new_args.at(0),
new_args.at(1),
new_args.at(2),
get_window_movement_strides(),
get_window_dilation_strides(),
get_padding_below(),
get_padding_above(),
get_data_dilation_strides()));
}
...@@ -55,38 +55,5 @@ namespace ngraph ...@@ -55,38 +55,5 @@ namespace ngraph
CoordinateDiff m_padding_above; CoordinateDiff m_padding_above;
Strides m_data_dilation_strides; Strides m_data_dilation_strides;
}; };
/// \brief Relu(Convolution) forward prop for batched convolution operation with bias
class ConvolutionBiasRelu : public util::RequiresTensorViewArgs
{
public:
ConvolutionBiasRelu(const std::shared_ptr<op::ConvolutionBias>& conv);
ConvolutionBiasRelu(const std::shared_ptr<Node>& data_batch,
const std::shared_ptr<Node>& filters,
const std::shared_ptr<Node>& bias,
const Strides& window_movement_strides,
const Strides& window_dilation_strides,
const CoordinateDiff& padding_below,
const CoordinateDiff& padding_above,
const Strides& data_dilation_strides);
const Strides& get_window_movement_strides() const { return m_window_movement_strides; }
const Strides& get_window_dilation_strides() const { return m_window_dilation_strides; }
const CoordinateDiff& get_padding_below() const { return m_padding_below; }
const CoordinateDiff& get_padding_above() const { return m_padding_above; }
const Strides& get_data_dilation_strides() const { return m_data_dilation_strides; }
std::shared_ptr<Node> get_filters() { return get_argument(1); }
std::shared_ptr<Node> get_data_batch() { return get_argument(0); }
virtual std::shared_ptr<Node>
copy_with_new_args(const NodeVector& new_args) const override;
protected:
Strides m_window_movement_strides;
Strides m_window_dilation_strides;
CoordinateDiff m_padding_below;
CoordinateDiff m_padding_above;
Strides m_data_dilation_strides;
};
} }
} }
...@@ -192,30 +192,6 @@ namespace ngraph ...@@ -192,30 +192,6 @@ namespace ngraph
} }
} }
template <>
void CPUAssignment::ASSIGN_DECL(ngraph::op::ConvolutionBiasRelu)
{
auto convolution = static_cast<op::ConvolutionBiasRelu*>(node);
auto arg0_rank = node->get_input_shape(0).size();
auto arg1_rank = node->get_input_shape(1).size();
bool data_dilated = false;
for (size_t s : convolution->get_data_dilation_strides())
{
data_dilated = data_dilated || (s != 1);
}
if (!data_dilated && arg0_rank == 4 && arg1_rank == 4 &&
node->get_input_element_type(0) == element::f32)
{
auto op_annotations =
std::make_shared<ngraph::runtime::cpu::CPUOpAnnotations>();
op_annotations->set_mkldnn_op(true);
convolution->set_op_annotations(op_annotations);
}
}
template <> template <>
void CPUAssignment::ASSIGN_DECL(ngraph::op::ConvolutionBiasAdd) void CPUAssignment::ASSIGN_DECL(ngraph::op::ConvolutionBiasAdd)
{ {
...@@ -747,8 +723,6 @@ static const runtime::cpu::pass::AssignOpMap s_dispatcher{ ...@@ -747,8 +723,6 @@ static const runtime::cpu::pass::AssignOpMap s_dispatcher{
&runtime::cpu::pass::CPUAssignment::assign<ngraph::op::GroupConvolution>}, &runtime::cpu::pass::CPUAssignment::assign<ngraph::op::GroupConvolution>},
{TI(ngraph::op::ConvolutionRelu), {TI(ngraph::op::ConvolutionRelu),
&runtime::cpu::pass::CPUAssignment::assign<ngraph::op::ConvolutionRelu>}, &runtime::cpu::pass::CPUAssignment::assign<ngraph::op::ConvolutionRelu>},
{TI(ngraph::op::ConvolutionBiasRelu),
&runtime::cpu::pass::CPUAssignment::assign<ngraph::op::ConvolutionBiasRelu>},
{TI(ngraph::op::ConvolutionBiasAdd), {TI(ngraph::op::ConvolutionBiasAdd),
&runtime::cpu::pass::CPUAssignment::assign<ngraph::op::ConvolutionBiasAdd>}, &runtime::cpu::pass::CPUAssignment::assign<ngraph::op::ConvolutionBiasAdd>},
{TI(ngraph::op::BatchNormRelu), {TI(ngraph::op::BatchNormRelu),
......
...@@ -993,7 +993,15 @@ void ngraph::runtime::cpu::pass::CPUFusion::construct_conv_bias_relu() ...@@ -993,7 +993,15 @@ void ngraph::runtime::cpu::pass::CPUFusion::construct_conv_bias_relu()
return false; return false;
} }
auto conv_relu = std::shared_ptr<Node>(new op::ConvolutionBiasRelu(conv)); auto conv_relu = std::make_shared<op::ConvolutionBias>(conv->get_argument(0),
conv->get_argument(1),
conv->get_argument(2),
conv->get_window_movement_strides(),
conv->get_window_dilation_strides(),
conv->get_padding_below(),
conv->get_padding_above(),
conv->get_data_dilation_strides(),
true);
ngraph::replace_node(m.get_match_root(), conv_relu); ngraph::replace_node(m.get_match_root(), conv_relu);
return true; return true;
}; };
......
...@@ -442,25 +442,6 @@ namespace ngraph ...@@ -442,25 +442,6 @@ namespace ngraph
} }
} }
template <>
void CPULayout::LAYOUT_DECL(ngraph::op::ConvolutionBiasRelu)
{
if (runtime::cpu::mkldnn_utils::use_mkldnn_kernel(node.get()))
{
vector<memory::format> prim_input_formats;
vector<memory::format> prim_output_formats;
ConvolutionLayout<ngraph::op::ConvolutionBiasRelu, true, false>(
node, prim_input_formats, prim_output_formats);
node =
insert_input_conversions(external_function, node, prim_input_formats);
set_output_layouts(node, prim_output_formats);
}
else
{
set_default_layouts(external_function, node);
}
}
template <> template <>
void CPULayout::LAYOUT_DECL(ngraph::op::ConvolutionBiasAdd) void CPULayout::LAYOUT_DECL(ngraph::op::ConvolutionBiasAdd)
{ {
...@@ -1533,8 +1514,6 @@ static const runtime::cpu::pass::LayoutOpMap s_dispatcher{ ...@@ -1533,8 +1514,6 @@ static const runtime::cpu::pass::LayoutOpMap s_dispatcher{
&runtime::cpu::pass::CPULayout::layout<ngraph::op::ConvolutionBias>}, &runtime::cpu::pass::CPULayout::layout<ngraph::op::ConvolutionBias>},
{TI(ngraph::op::ConvolutionRelu), {TI(ngraph::op::ConvolutionRelu),
&runtime::cpu::pass::CPULayout::layout<ngraph::op::ConvolutionRelu>}, &runtime::cpu::pass::CPULayout::layout<ngraph::op::ConvolutionRelu>},
{TI(ngraph::op::ConvolutionBiasRelu),
&runtime::cpu::pass::CPULayout::layout<ngraph::op::ConvolutionBiasRelu>},
{TI(ngraph::op::ConvolutionBiasAdd), {TI(ngraph::op::ConvolutionBiasAdd),
&runtime::cpu::pass::CPULayout::layout<ngraph::op::ConvolutionBiasAdd>}, &runtime::cpu::pass::CPULayout::layout<ngraph::op::ConvolutionBiasAdd>},
{TI(ngraph::op::ConvolutionBiasBackpropFiltersBias), {TI(ngraph::op::ConvolutionBiasBackpropFiltersBias),
......
...@@ -865,8 +865,7 @@ TEST(cpu_fusion, conv_bias_relu_n2c1h2w2_2) ...@@ -865,8 +865,7 @@ TEST(cpu_fusion, conv_bias_relu_n2c1h2w2_2)
auto weights = std::make_shared<op::Parameter>(element::f32, shape_weights); auto weights = std::make_shared<op::Parameter>(element::f32, shape_weights);
auto bias = std::make_shared<op::Parameter>(element::f32, shape_bias); auto bias = std::make_shared<op::Parameter>(element::f32, shape_bias);
auto conv = std::make_shared<op::Convolution>(A, weights, Strides{2, 2}, Strides{1, 1}); auto conv = std::make_shared<op::Convolution>(A, weights, Strides{2, 2}, Strides{1, 1});
auto conv_bias_relu = std::make_shared<op::ConvolutionBiasRelu>( auto conv_bias_relu = std::make_shared<op::ConvolutionBias>(conv, bias, true);
std::make_shared<op::ConvolutionBias>(conv, bias));
auto f = make_shared<Function>(NodeVector{conv_bias_relu}, auto f = make_shared<Function>(NodeVector{conv_bias_relu},
op::ParameterVector{A, weights, bias}); op::ParameterVector{A, weights, bias});
return f; return f;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment