Fixed (groupd)conv2d layout infer bug

And added a weight_as_input case to test

Type: Bug Fix

Signed-off-by: Chen Xin <jack.chen@verisilicon.com>
This commit is contained in:
Chen Xin 2022-12-01 16:24:55 +08:00 committed by Sven
parent ac4517b5c1
commit 0e211c8efd
4 changed files with 215 additions and 138 deletions

View File

@ -59,3 +59,61 @@ TEST(LayoutInference, simple_conv2d) {
tim::vx::ShapeType expect_shape({1, 2, 2, 1}); tim::vx::ShapeType expect_shape({1, 2, 2, 1});
EXPECT_EQ(infer_out_shape, expect_shape); EXPECT_EQ(infer_out_shape, expect_shape);
} }
TEST(LayoutInference, weight_as_input_conv2d) {
auto ctx = tim::vx::Context::Create();
auto src_graph = ctx->CreateGraph();
tim::vx::ShapeType input_shape({3, 3, 1, 1}); //WHCN
tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, input_shape,
tim::vx::TensorAttribute::INPUT);
auto input = src_graph->CreateTensor(input_spec);
tim::vx::ShapeType kernel_shape({1, 2, 2, 1}); //IWHO
tim::vx::TensorSpec kernel_spec(tim::vx::DataType::FLOAT32, kernel_shape,
tim::vx::TensorAttribute::INPUT);
auto kernel = src_graph->CreateTensor(kernel_spec);
tim::vx::ShapeType bias_shape({1});
tim::vx::TensorSpec bias_spec(tim::vx::DataType::FLOAT32, bias_shape,
tim::vx::TensorAttribute::INPUT);
auto bias = src_graph->CreateTensor(bias_spec);
tim::vx::ShapeType output_shape({2, 2, 1, 1}); //WHCN
tim::vx::TensorSpec output_spec(tim::vx::DataType::FLOAT32, output_shape,
tim::vx::TensorAttribute::OUTPUT);
auto output = src_graph->CreateTensor(output_spec);
auto conv2d = src_graph->CreateOperation<tim::vx::ops::Conv2d>(
0, tim::vx::PadType::AUTO, std::array<uint32_t, 2>({0, 0}),
std::array<uint32_t, 2>({1, 1}), std::array<uint32_t, 2>({1, 1}),
std::array<uint32_t, 4>({0, 0, 0, 0}), 0, tim::vx::DataLayout::WHCN,
tim::vx::DataLayout::IcWHOc);
(*conv2d).BindInputs({input, kernel, bias}).BindOutput(output);
// Do layout inference
auto transform = tim::transform::LayoutInference(src_graph, ctx);
auto infer_graph = transform.first;
auto graph_io_map = transform.second;
infer_graph->Compile();
std::vector<float> input_data = {1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f};
std::vector<float> kernel_data = {0.25f, 0.25f, 0.25f, 0.25f};
std::vector<float> bias_data = {0.0f};
auto infer_input = graph_io_map[src_graph->InputsTensor()[0]];
auto infer_weight = graph_io_map[src_graph->InputsTensor()[1]];
auto infer_bias = graph_io_map[src_graph->InputsTensor()[2]];
auto infer_output = graph_io_map[src_graph->OutputsTensor()[0]];
infer_input->CopyDataToTensor(input_data.data(), input_data.size() * sizeof(float));
infer_weight->CopyDataToTensor(kernel_data.data(), kernel_data.size() * sizeof(float));
infer_bias->CopyDataToTensor(bias_data.data(), bias_data.size() * sizeof(float));
infer_graph->Run();
std::vector<float> out_data;
auto infer_out_shape = infer_output->GetShape();
out_data.resize(infer_out_shape[0] * infer_out_shape[1] * infer_out_shape[2] *
infer_out_shape[3]);
infer_output->CopyDataFromTensor(out_data.data());
std::vector<float> expect_output = {0.875f, 0.875f, 0.875f, 0.875f};
EXPECT_TRUE(0 == memcmp((void*)out_data.data(), (void*)expect_output.data(),
sizeof(float) * out_data.size()));
tim::vx::ShapeType expect_shape({2, 2, 1, 1});
EXPECT_EQ(infer_out_shape, expect_shape);
}

View File

@ -40,93 +40,91 @@ class Conv2dLayoutInfer : public OpLayoutInfer {
: OpLayoutInfer(op, context) {} : OpLayoutInfer(op, context) {}
void OnInputs( void OnInputs(
std::vector<std::shared_ptr<vx::Tensor>>& next_tensors) override { std::vector<std::shared_ptr<vx::Tensor>>& next_tensors) override {
vx::DataLayout layout = op_->impl()->layout_;
auto required_pv = MakeShared(4);
if (layout == vx::DataLayout::CWHN) {
required_pv = std::make_shared<PermuteVector<4>>(kCWHN2WHCN);
}
auto input_tensors = op_->impl()->InputsTensor();
for (const auto& in : input_tensors) {
std::shared_ptr<vx::Tensor> infer_tensor;
std::shared_ptr<IPermuteVector> trans_pv;
if (in->IsConstTensor() &&
!(in->GetSpec().attr_ & vx::TensorAttribute::INPUT)) {
// For bias
if (in->GetShape().size() == 1) {
infer_tensor = context_->infer_graph_->CreateTensor(
in->GetSpec(), in->GetDataRef());
trans_pv = MakeShared(1);
} else {
// For input/weight
if (!required_pv->IsAligned()) {
auto src_conv2d = std::static_pointer_cast<vx::ops::Conv2d>(op_); auto src_conv2d = std::static_pointer_cast<vx::ops::Conv2d>(op_);
// Support TVM Kernel Layout vx::DataLayout layout = op_->impl()->layout_;
if (src_conv2d->KernelDataLayout() == vx::DataLayout::OcIcWH) { auto kernel_layout = src_conv2d->KernelDataLayout();
trans_pv = std::make_shared<PermuteVector<4>>(kOcIcWH2WHIcOc); std::shared_ptr<IPermuteVector> required_pv, weight_required_pv;
infer_tensor = PermuteConstTensor( switch (layout)
in, trans_pv); { // kernel layout must be IWHO in tflite & nnapi
} else if (src_conv2d->KernelDataLayout() == vx::DataLayout::IcOcWH) { case vx::DataLayout::CWHN:
trans_pv = std::make_shared<PermuteVector<4>>(kIcOcWH2WHIcOc); required_pv = std::make_shared<PermuteVector<4>>(kCWHN2WHCN);
infer_tensor = PermuteConstTensor( break;
in, trans_pv); case vx::DataLayout::WHCN:
} else { required_pv = MakeShared(4);
infer_tensor = PermuteConstTensor(in, required_pv); break;
trans_pv = required_pv; default:
VSILOGE("The layout of input is not support.");
required_pv = MakeShared(4);
break;
} }
} else { switch (kernel_layout) {
infer_tensor = context_->infer_graph_->CreateTensor( case vx::DataLayout::OcIcWH: // Support TVM Kernel Layout
in->GetSpec(), in->GetDataRef()); weight_required_pv = std::make_shared<PermuteVector<4>>(kOcIcWH2WHIcOc);
trans_pv = MakeShared(required_pv->Rank()); break;
} case vx::DataLayout::IcOcWH:
} weight_required_pv = std::make_shared<PermuteVector<4>>(kIcOcWH2WHIcOc);
} else { break;
// For bias case vx::DataLayout::IcWHOc: // Support nnapi & tflite Kernel Layout
if (in->GetShape().size() == 1) { weight_required_pv = std::make_shared<PermuteVector<4>>(kIcWHOc2WHIcOc);
infer_tensor = context_->GetMapedTensor(in); break;
trans_pv = MakeShared(1); default: // Default set to IWHO for compatibility with previous APIs
} else { weight_required_pv = std::make_shared<PermuteVector<4>>(kIcWHOc2WHIcOc);
// For input/weight break;
auto pv = context_->GetPermuteVector(in);
auto final_pv = pv->Reverse()->Add(required_pv);
if (!final_pv->IsAligned()) {
infer_tensor =
InsertPermute(context_->GetMapedTensor(in), final_pv);
trans_pv = required_pv;
} else {
infer_tensor = context_->GetMapedTensor(in);
trans_pv = pv;
}
}
}
context_->UpdateTensorMap(in, infer_tensor);
context_->SetPermuteVector(in, trans_pv);
} }
auto pad_type = TranslatePadType(op_->impl()->node()->nn_param.conv2d.pad_type); auto input_tensors = op_->impl()->InputsTensor();
std::array<uint32_t, 2> ksize = { std::shared_ptr<vx::Tensor> infer_input, infer_weight, infer_bias;
op_->impl()->node()->nn_param.conv2d.ksize[0], // For input
op_->impl()->node()->nn_param.conv2d.ksize[1] auto input_pv = context_->GetPermuteVector(input_tensors[0]);
}; auto final_pv = input_pv->Reverse()->Add(required_pv);
std::array<uint32_t, 2> stride = { if (!final_pv->IsAligned()) {
op_->impl()->node()->nn_param.conv2d.stride[0], infer_input =
op_->impl()->node()->nn_param.conv2d.stride[1] InsertPermute(context_->GetMapedTensor(input_tensors[0]), final_pv);
}; context_->SetPermuteVector(input_tensors[0], required_pv);
std::array<uint32_t, 2> dilation = { } else {
op_->impl()->node()->nn_param.conv2d.dilation[0], infer_input = context_->GetMapedTensor(input_tensors[0]);
op_->impl()->node()->nn_param.conv2d.dilation[1] context_->SetPermuteVector(input_tensors[0], input_pv);
}; }
std::array<uint32_t, 4> pad = { context_->UpdateTensorMap(input_tensors[0], infer_input);
op_->impl()->node()->nn_param.conv2d.pad[0],
op_->impl()->node()->nn_param.conv2d.pad[1], // For weight
op_->impl()->node()->nn_param.conv2d.pad[2], if (input_tensors[1]->IsConstTensor()) {
op_->impl()->node()->nn_param.conv2d.pad[3] if (!weight_required_pv->IsAligned()) {
}; infer_weight = PermuteConstTensor(input_tensors[1], weight_required_pv);
int32_t multiplier = op_->impl()->node()->nn_param.conv2d.multiplier; } else {
int32_t out_channels = op_->impl()->node()->nn_param.conv2d.weights; infer_weight = context_->infer_graph_->CreateTensor(
auto conv2d = context_->infer_graph_->CreateOperation<vx::ops::Conv2d>( input_tensors[1]->GetSpec(), input_tensors[1]->GetDataRef());
out_channels, pad_type, ksize, stride, dilation, pad, multiplier, }
vx::DataLayout::WHCN, vx::DataLayout::WHIcOc); context_->SetPermuteVector(input_tensors[1], weight_required_pv);
context_->UpdateTensorMap(input_tensors[1], infer_weight);
} else {
auto weight_pv = context_->GetPermuteVector(input_tensors[1]);
auto final_pv = weight_pv->Reverse()->Add(weight_required_pv);
if (!final_pv->IsAligned()) {
infer_weight =
InsertPermute(context_->GetMapedTensor(input_tensors[1]), final_pv);
context_->SetPermuteVector(input_tensors[1], weight_required_pv);
} else {
infer_weight = context_->GetMapedTensor(input_tensors[1]);
context_->SetPermuteVector(input_tensors[1], weight_pv);
}
context_->UpdateTensorMap(input_tensors[1], infer_weight);
}
// For bias
if (input_tensors.size() == 3) {
if (input_tensors[2]->IsConstTensor()) {
infer_bias = context_->infer_graph_->CreateTensor(
input_tensors[2]->GetSpec(), input_tensors[2]->GetDataRef());
} else {
infer_bias = context_->GetMapedTensor(input_tensors[2]);
}
auto bias_pv = MakeShared(1);
context_->UpdateTensorMap(input_tensors[2], infer_bias);
context_->SetPermuteVector(input_tensors[2], bias_pv);
}
auto conv2d = op_->Clone(context_->infer_graph_);
auto otensor_infer = CreateOutputsTensor(required_pv); auto otensor_infer = CreateOutputsTensor(required_pv);
for (const auto& i_src : input_tensors) { for (const auto& i_src : input_tensors) {
(*conv2d).BindInput(context_->GetMapedTensor(i_src)); (*conv2d).BindInput(context_->GetMapedTensor(i_src));

View File

@ -40,75 +40,96 @@ class GroupedConv2dLayoutInfer : public OpLayoutInfer {
: OpLayoutInfer(op, context) {} : OpLayoutInfer(op, context) {}
void OnInputs( void OnInputs(
std::vector<std::shared_ptr<vx::Tensor>>& next_tensors) override { std::vector<std::shared_ptr<vx::Tensor>>& next_tensors) override {
auto src_grouped_conv2d = std::static_pointer_cast<vx::ops::Conv2d>(op_);
vx::DataLayout layout = op_->impl()->layout_; vx::DataLayout layout = op_->impl()->layout_;
auto required_pv = MakeShared(4); auto kernel_layout = src_grouped_conv2d->KernelDataLayout();
if (layout == vx::DataLayout::CWHN) { std::shared_ptr<IPermuteVector> required_pv, weight_required_pv;
switch (layout)
{ // kernel layout must be IWHO in tflite & nnapi
case vx::DataLayout::CWHN:
required_pv = std::make_shared<PermuteVector<4>>(kCWHN2WHCN); required_pv = std::make_shared<PermuteVector<4>>(kCWHN2WHCN);
break;
case vx::DataLayout::WHCN:
required_pv = MakeShared(4);
break;
default:
VSILOGE("The layout of input is not support.");
required_pv = MakeShared(4);
break;
} }
switch (kernel_layout) {
case vx::DataLayout::OcIcWH: // Support TVM Kernel Layout
weight_required_pv = std::make_shared<PermuteVector<4>>(kOcIcWH2WHIcOc);
break;
case vx::DataLayout::IcOcWH:
weight_required_pv = std::make_shared<PermuteVector<4>>(kIcOcWH2WHIcOc);
break;
case vx::DataLayout::IcWHOc: // Support nnapi & tflite Kernel Layout
weight_required_pv = std::make_shared<PermuteVector<4>>(kIcWHOc2WHIcOc);
break;
default: // Default set to IWHO for compatibility with previous APIs
weight_required_pv = std::make_shared<PermuteVector<4>>(kIcWHOc2WHIcOc);
break;
}
auto input_tensors = op_->impl()->InputsTensor(); auto input_tensors = op_->impl()->InputsTensor();
std::shared_ptr<vx::Tensor> infer_input, infer_weight, infer_bias;
for (const auto& in : input_tensors) { // For input
std::shared_ptr<vx::Tensor> infer_tensor; auto input_pv = context_->GetPermuteVector(input_tensors[0]);
std::shared_ptr<IPermuteVector> trans_pv; auto final_pv = input_pv->Reverse()->Add(required_pv);
if (in->IsConstTensor() &&
!(in->GetSpec().attr_ & vx::TensorAttribute::INPUT)) {
// For bias
if (in->GetShape().size() == 1) {
infer_tensor = context_->infer_graph_->CreateTensor(
in->GetSpec(), in->GetDataRef());
trans_pv = MakeShared(1);
} else {
// For input/weight
if (!required_pv->IsAligned()) {
auto src_grouped_conv2d = std::static_pointer_cast<vx::ops::GroupedConv2d>(op_);
// Support TVM Kernel Layout
if (src_grouped_conv2d->KernelDataLayout() == vx::DataLayout::OcIcWH) {
trans_pv = std::make_shared<PermuteVector<4>>(kOcIcWH2WHIcOc);
infer_tensor = PermuteConstTensor(
in, trans_pv);
} else if (src_grouped_conv2d->KernelDataLayout() == vx::DataLayout::IcOcWH) {
trans_pv = std::make_shared<PermuteVector<4>>(kIcOcWH2WHIcOc);
infer_tensor = PermuteConstTensor(
in, trans_pv);
} else {
infer_tensor = PermuteConstTensor(in, required_pv);
trans_pv = required_pv;
}
} else {
infer_tensor = context_->infer_graph_->CreateTensor(
in->GetSpec(), in->GetDataRef());
trans_pv = MakeShared(required_pv->Rank());
}
}
} else {
// For bias
if (in->GetShape().size() == 1) {
infer_tensor = context_->GetMapedTensor(in);
trans_pv = MakeShared(1);
} else {
// For input/weight
auto pv = context_->GetPermuteVector(in);
auto final_pv = pv->Reverse()->Add(required_pv);
if (!final_pv->IsAligned()) { if (!final_pv->IsAligned()) {
infer_tensor = infer_input =
InsertPermute(context_->GetMapedTensor(in), final_pv); InsertPermute(context_->GetMapedTensor(input_tensors[0]), final_pv);
trans_pv = required_pv; context_->SetPermuteVector(input_tensors[0], required_pv);
} else { } else {
infer_tensor = context_->GetMapedTensor(in); infer_input = context_->GetMapedTensor(input_tensors[0]);
trans_pv = pv; context_->SetPermuteVector(input_tensors[0], input_pv);
} }
context_->UpdateTensorMap(input_tensors[0], infer_input);
// For weight
if (input_tensors[1]->IsConstTensor()) {
if (!weight_required_pv->IsAligned()) {
infer_weight = PermuteConstTensor(input_tensors[1], weight_required_pv);
} else {
infer_weight = context_->infer_graph_->CreateTensor(
input_tensors[1]->GetSpec(), input_tensors[1]->GetDataRef());
} }
context_->SetPermuteVector(input_tensors[1], weight_required_pv);
context_->UpdateTensorMap(input_tensors[1], infer_weight);
} else {
auto weight_pv = context_->GetPermuteVector(input_tensors[1]);
auto final_pv = weight_pv->Reverse()->Add(weight_required_pv);
if (!final_pv->IsAligned()) {
infer_weight =
InsertPermute(context_->GetMapedTensor(input_tensors[1]), final_pv);
context_->SetPermuteVector(input_tensors[1], weight_required_pv);
} else {
infer_weight = context_->GetMapedTensor(input_tensors[1]);
context_->SetPermuteVector(input_tensors[1], weight_pv);
} }
context_->UpdateTensorMap(in, infer_tensor); context_->UpdateTensorMap(input_tensors[1], infer_weight);
context_->SetPermuteVector(in, trans_pv);
} }
auto conv2d = op_->Clone(context_->infer_graph_); // For bias
if (input_tensors.size() == 3) {
if (input_tensors[2]->IsConstTensor()) {
infer_bias = context_->infer_graph_->CreateTensor(
input_tensors[2]->GetSpec(), input_tensors[2]->GetDataRef());
} else {
infer_bias = context_->GetMapedTensor(input_tensors[2]);
}
auto bias_pv = MakeShared(1);
context_->UpdateTensorMap(input_tensors[2], infer_bias);
context_->SetPermuteVector(input_tensors[2], bias_pv);
}
auto grouped_conv2d = op_->Clone(context_->infer_graph_);
auto otensor_infer = CreateOutputsTensor(required_pv); auto otensor_infer = CreateOutputsTensor(required_pv);
for (const auto& i_src : input_tensors) { for (const auto& i_src : input_tensors) {
(*conv2d).BindInput(context_->GetMapedTensor(i_src)); (*grouped_conv2d).BindInput(context_->GetMapedTensor(i_src));
} }
(*conv2d).BindOutput(otensor_infer[0]); (*grouped_conv2d).BindOutput(otensor_infer[0]);
context_->SetPermuteVector(op_->impl()->OutputsTensor()[0], required_pv); context_->SetPermuteVector(op_->impl()->OutputsTensor()[0], required_pv);
// Add out tensor of src_graph into next_tensor // Add out tensor of src_graph into next_tensor
next_tensors.push_back(op_->impl()->OutputsTensor()[0]); next_tensors.push_back(op_->impl()->OutputsTensor()[0]);