diff --git a/tf_adapter/kernels/deformable_conv2d_ops.cc b/tf_adapter/kernels/deformable_conv2d_ops.cc new file mode 100644 index 0000000000000000000000000000000000000000..15b202704827c88c9b1bbdbf647cc8eb76db8367 --- /dev/null +++ b/tf_adapter/kernels/deformable_conv2d_ops.cc @@ -0,0 +1,56 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. +Licensed 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. + +Copyright (C) 2019-2020. Huawei Technologies Co., Ltd. All rights reserved. +Licensed 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. +==============================================================================*/ + +#include "tensorflow/core/framework/op_kernel.h" + +namespace tensorflow { +namespace{ +template +class DeformableConv2DOp : public tensorflow::OpKernel { + public: + explicit DeformableConv2DOp(tensorflow::OpKernelConstruction *context) + : OpKernel(context) { + LOG(INFO) << "new DeformableConv2DOp"; + } + ~DeformableConv2DOp() override { LOG(INFO) << "del DeformableConv2DOp"; } + void Compute(OpKernelContext *context) override { + LOG(INFO) << "DeformableConv2DOp Compute, num_inputs: " + << context->num_inputs(); + } + bool IsExpensive() override { return false; } +}; + +#define REGISTER_KERNEL(type) \ +REGISTER_KERNEL_BUILDER(Name("DeformableConv2D") \ + .Device(tensorflow::DEVICE_CPU) \ + .TypeConstraint("T"), \ + DeformableConv2DOp) +REGISTER_KERNEL(float); +REGISTER_KERNEL(Eigen::half); +#undef REGISTER_KERNEL +} // namespace +} // namespace tensorflow diff --git a/tf_adapter/ops/npu_cube_ops.cc b/tf_adapter/ops/npu_cube_ops.cc new file mode 100644 index 0000000000000000000000000000000000000000..17e67833a55f975adde4c4866d542de6ac71c445 --- /dev/null +++ b/tf_adapter/ops/npu_cube_ops.cc @@ -0,0 +1,165 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. +Licensed 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. + +Copyright (C) 2019-2020. Huawei Technologies Co., Ltd. All rights reserved. +Licensed 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. +==============================================================================*/ + +#include "tensorflow/core/framework/common_shape_fns.h" +#include "tensorflow/core/framework/op.h" + +namespace tensorflow { +using shape_inference::DimensionHandle; +using shape_inference::InferenceContext; +using shape_inference::ShapeHandle; + +namespace{ +Status Conv2dInferShape(shape_inference::InferenceContext *c) { + std::string dt_format; + const std::set kVaildFormat = {"NHWC", "NCHW"}; + if (!c->GetAttr("data_format", &dt_format).ok()) { + dt_format = "NHWC"; + } + if (kVaildFormat.find(dt_format) == kVaildFormat.end()) { + return errors::InvalidArgument("Invalid data format string: ", + dt_format); + } + size_t pos_n = dt_format.find("N"); + size_t pos_c = dt_format.find("C"); + size_t pos_h = dt_format.find("H"); + size_t pos_w = dt_format.find("W"); + + const int rank = 4; + ShapeHandle x_shape; + TF_RETURN_IF_ERROR(c->WithRank(c->input(0), rank, &x_shape)); + DimensionHandle x_n_dim = c->Dim(x_shape, pos_n); + DimensionHandle x_c_dim = c->Dim(x_shape, pos_c); + DimensionHandle x_h_dim = c->Dim(x_shape, pos_c); + DimensionHandle x_w_dim = c->Dim(x_shape, pos_c); + + // Filter format is always HWCN + ShapeHandle filter_shape; + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), rank, &filter_shape)); + DimensionHandle filter_h_dim = c->Dim(filter_shape, 0); + DimensionHandle filter_w_dim = c->Dim(filter_shape, 1); + DimensionHandle filter_c_dim = c->Dim(filter_shape, 2); + DimensionHandle filter_n_dim = c->Dim(filter_shape, 3); + + int32 groups; + TF_RETURN_IF_ERROR(c->GetAttr("groups", &groups)); + if (groups < 1) { + return errors::InvalidArgument("Groups must be >= 1"); + } + if (c->ValueKnown(x_c_dim) && c->ValueKnown(filter_c_dim)) { + int64 x_c = c->Value(x_c_dim); + int64 filter_c = c->Value(filter_c_dim); + if (x_c != filter_c * groups) + return errors::InvalidArgument( + "In_channels (", x_c, + ") should be equal to filter channels (", filter_c, + ")* groups (", groups, ")"); + + if (c->ValueKnown(filter_n_dim)) { + int64 filter_n = c->Value(filter_n_dim); + if (filter_n % groups != 0) + return errors::InvalidArgument( + "Out_channels (", filter_n, + ") should be divisiable by groups (", groups, ")"); + } + } + + std::vector dilations; + TF_RETURN_IF_ERROR(c->GetAttr("dilations", &dilations)); + if (dilations.size() != 4) { + return errors::InvalidArgument("Dilations list should be 4D, actual is: ", + dilations.size()); + } + const int32 dil_h = dilations[pos_h]; + const int32 dil_w = dilations[pos_w]; + if (dil_h < 1 || dil_w < 1) { + return errors::InvalidArgument("Dilation rate must be >= 1"); + } + std::vector strides; + TF_RETURN_IF_ERROR(c->GetAttr("strides", &strides)); + if (strides.size() != 4) { + return errors::InvalidArgument("Strides list should be 4D, actual is: ", + strides.size()); + } + const int32 str_h = strides[pos_h]; + const int32 str_w = strides[pos_w]; + if (str_h < 1 || str_w < 1) { + return errors::InvalidArgument("Stride must be > 0"); + } + + std::vector pads; + TF_RETURN_IF_ERROR(c->GetAttr("pads", &pads)); + if (pads.size() != 4) { + return errors::InvalidArgument("Pads list should be 4D, actual is: ", + pads.size()); + } + int64 pad_t = pads[0]; + int64 pad_b = pads[1]; + int64 pad_l = pads[2]; + int64 pad_r = pads[3]; + if (pad_t < 0 || pad_b < 0 || pad_l < 0 || pad_r < 0) { + return errors::InvalidArgument("Pad must be > 0"); + } + + int64 dil_filter_h = dil_h * (c->Value(filter_h_dim) - 1) + 1; + int64 dil_filter_w = dil_w * (c->Value(filter_w_dim) - 1) + 1; + int64 x_h = c->Value(x_h_dim); + int64 x_w = c->Value(x_w_dim); + int64 out_h = (x_h + pad_t + pad_b - dil_filter_h) / str_h + 1; + int64 out_w = (x_w + pad_l + pad_r - dil_filter_w) / str_w + 1; + if (out_h < 0 || out_w < 0) { + return errors::InvalidArgument("Image size after padding should not be " + "smaller than filter size after dilation"); + } + + DimensionHandle out_h_dim = c->MakeDim(out_h); + DimensionHandle out_w_dim = c->MakeDim(out_w); + std::vector out_dims(rank); + out_dims[pos_n] = x_n_dim; + out_dims[pos_c] = filter_n_dim; + out_dims[pos_h] = out_h_dim; + out_dims[pos_w] = out_w_dim; + c->set_output(0, c->MakeShape(out_dims)); + return Status::OK(); +} + +REGISTER_OP("DeformableConv2D") + .Input("x: T") + .Input("filter: T") + .Input("offsets: T") + .Input("bias: T") + .Output("y: T") + .Attr("T: {float16, float32}") + .Attr("strides: list(int) = [1,1,1,1]") + .Attr("pads: list(int) = [0,0,0,0]") + .Attr("data_format: {'NHWC', 'NCHW'} = 'NHWC'") + .Attr("dilations: list(int) = [1,1,1,1]") + .Attr("groups: int = 1") + .Attr("deformable_groups: int = 1") + .SetShapeFn(Conv2dInferShape); +} // namespace +} // namespace tensorflow \ No newline at end of file diff --git a/tf_adapter/python/npu_bridge/tbe/npu_cube_ops.py b/tf_adapter/python/npu_bridge/tbe/npu_cube_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..3b4697cb13bbca7ff809dd91149999b2861bfd15 --- /dev/null +++ b/tf_adapter/python/npu_bridge/tbe/npu_cube_ops.py @@ -0,0 +1,104 @@ +# Copyright 2016 The TensorFlow Authors. All Rights Reserved. +# +# Licensed 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. +# +# Copyright (C) 2019-2020. Huawei Technologies Co., Ltd. All rights reserved. +# Licensed 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. +# ============================================================================== +"""Ops for aicore cube.""" +from tensorflow.python.eager import context +from npu_bridge.helper import helper +gen_npu_ops = helper.get_gen_ops() + + +def deformable_conv2d( + x, + filters, + offsets, + bias, + strides=(1, 1, 1, 1), + pads=(0, 0, 0, 0), + data_format='NHWC', + dilations=(1, 1, 1, 1), + groups=1, + deformable_groups=1, + name=None): + + """Computes a 2-D deformable convolution given 4-D `inputs`、`filters` and + `offsets` tensors. + + Inputs: + x: A 4D `Tensor` of input `image`. With the `data_format` `NHWC`, the + data is stored in the order of: [batch, in_height, in_width, + in_channels]. Must be one of the following types: `float16`, `float32`. + filters: A 4D `Tensor` of learnable filters. Must have the same type as + `x`. The data is stored in the order of: `[filter_height, filter_width, + in_channels / groups, out_channels]`. + offsets: A 4D `Tensor` of x y coordinates offset and mask. With the + `data_format` `NHWC`, the data is stored in the order of: `[in_height, + in_width, deformable_groups * filter_height * filter_width * 3]`. Must + be one of the following types: `float16`, `float32`. + + Attributes: + strides: Required. An list of `4` `ints`. The stride of the sliding + window for each dimension of `image`. The dimension order is + interpreted according to the value of `data_format`. The `N` and `C` + dimensions must be set to 1. + pads: Required. An list of `4` `ints`. The number of pixels to add to + each `(pad_top, pad_bottom, pad_left, pad_right)` side of the `image`. + data_format: Optional. A `string` from: `"NHWC", "NCHW"`. Specify the + data format of the input and output data. Defaults to `"NHWC"`. + dilations: Optional. An list of `4` `ints`. The dilation factor for each + dimension of `image`. The dimension order is interpreted according to + the value of `data_format`. The `N` and `C` dimensions must be set to + 1. Defaults to `(1, 1, 1, 1)`. + groups: Optional. An `int`. The number of blocked connections from + `in_channels` to `out_channels`. `In_channels` and `out_channels` must + both be divisible by `groups`. Defaults to 1. + deformable_groups: Optional. An `int`. The number of deformable group + partitions. `In_channels` must be divisible by `deformable_groups`. + Defaults to 1. + name: Optional. A name for the operation. + + Returns: + A 4D `Tensor` of output feature map. Has the same type as `x`. With the + `data_format` `NHWC`, the data is stored in the order of: `[batch, + out_height, out_width, out_channels]`. + """ + + if context.executing_eagerly(): + raise RuntimeError("tf.deformable_conv2d() is not compatible with " + "eager execution.") + + return gen_npu_ops.deformable_conv2d( + x=x, + filter=filters, + offsets=offsets, + bias=bias, + strides=strides, + pads=pads, + data_format=data_format, + dilations=dilations, + groups=groups, + deformable_groups=deformable_groups, + name=name)