| /* |
| * 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. |
| */ |
| |
| /*! |
| * Copyright (c) 2015 by Contributors |
| * \file upsampling_nearest.cc |
| * \brief |
| * \author Bing Xu, Da Zheng |
| */ |
| |
| #include "./upsampling-inl.h" |
| #include <nnvm/op_attr_types.h> |
| #include "./deconvolution-inl.h" |
| |
| namespace mxnet { |
| namespace op { |
| |
| static bool UpSamplingShape(const nnvm::NodeAttrs& attrs, |
| mxnet::ShapeVector *in_shape, mxnet::ShapeVector *out_shape) { |
| const UpSamplingParam& param_ = nnvm::get<UpSamplingParam>(attrs.parsed); |
| CHECK_GE(in_shape->size(), 1U); |
| const mxnet::TShape &dshape = (*in_shape)[0]; |
| mxnet::TShape oshape = dshape; |
| if (param_.sample_type == up_enum::kNearest) { |
| CHECK_EQ(in_shape->size(), static_cast<size_t>(param_.num_args)); |
| oshape[1] = 0; |
| for (auto& shape : *in_shape) { |
| CHECK_EQ(shape.ndim(), 4U) << \ |
| "UpSamplingNearest: Input data should be 4D in (batch, channel, y, x)"; |
| int oh = dshape[2]*param_.scale, ow = dshape[3]*param_.scale; |
| CHECK_EQ(oh%shape[2], 0U) << "UpSamplingNearest: input height of " << shape[2] << \ |
| "does not divide output height of " << oh; |
| CHECK_EQ(ow%shape[3], 0U) << "UpSamplingNearest: input width of " << shape[3] << \ |
| "does not divide output width of " << ow; |
| if (param_.multi_input_mode == up_enum::kSum) { |
| CHECK(oshape[1] == 0 || oshape[1] == shape[1]) << \ |
| "Number of channels must be the same when multi_input_mode==sum"; |
| oshape[1] = shape[1]; |
| } else { |
| oshape[1] += shape[1]; |
| } |
| } |
| } else { |
| CHECK_EQ(in_shape->size(), 2U) << "Input:[data, weight]"; |
| CHECK_EQ(dshape.ndim(), 4U) << \ |
| "UpSamplingBilinear: Input data should be 4D in (batch, channel, y, x)"; |
| if (!shape_is_known(dshape)) return false; |
| int kernel = 2 * param_.scale - param_.scale % 2; |
| SHAPE_ASSIGN_CHECK(*in_shape, |
| up_enum::kWeight, |
| mshadow::Shape4(dshape[1], 1, kernel, kernel)); |
| oshape = dshape; |
| } |
| oshape[2] = dshape[2] * param_.scale; |
| oshape[3] = dshape[3] * param_.scale; |
| out_shape->clear(); |
| out_shape->push_back(oshape); |
| return true; |
| } |
| |
| static inline std::vector<std::string> ListArguments(const UpSamplingParam& param) { |
| if (param.sample_type == up_enum::kNearest) { |
| std::vector<std::string> ret; |
| ret.reserve(param.num_args); |
| for (int i = 0; i < param.num_args; ++i) { |
| ret.push_back(std::string("arg") + std::to_string(i)); |
| } |
| return ret; |
| } else { |
| return {"data", "weight"}; |
| } |
| } |
| |
| static bool UpSamplingType(const nnvm::NodeAttrs& attrs, |
| std::vector<int> *in_type, std::vector<int> *out_type) { |
| const UpSamplingParam& param = nnvm::get<UpSamplingParam>(attrs.parsed); |
| CHECK_GE(in_type->size(), 1U); |
| int dtype = (*in_type)[0]; |
| CHECK_NE(dtype, -1) << "First input must have specified type"; |
| for (size_t i = 0; i < in_type->size(); ++i) { |
| if ((*in_type)[i] == -1) { |
| (*in_type)[i] = dtype; |
| } else { |
| UNIFORM_TYPE_CHECK((*in_type)[i], dtype, ListArguments(param)[i]); |
| } |
| } |
| out_type->clear(); |
| out_type->push_back(dtype); |
| return true; |
| } |
| |
| struct UpSamplingGrad { |
| const char *op_name; |
| std::vector<nnvm::NodeEntry> operator()(const nnvm::ObjectPtr& n, |
| const std::vector<nnvm::NodeEntry>& ograds) const { |
| const UpSamplingParam& param_ = nnvm::get<UpSamplingParam>(n->attrs.parsed); |
| std::vector<nnvm::NodeEntry> heads(ograds.begin(), ograds.end()); |
| if (param_.sample_type != up_enum::kNearest) { |
| heads.push_back(n->inputs[up_enum::kData]); |
| heads.push_back(n->inputs[up_enum::kWeight]); |
| } |
| return MakeGradNode(op_name, n, heads, n->attrs.dict); |
| } |
| }; |
| |
| DMLC_REGISTER_PARAMETER(UpSamplingParam); |
| |
| NNVM_REGISTER_OP(UpSampling) |
| .describe(R"code(Upsamples the given input data. |
| |
| Two algorithms (``sample_type``) are available for upsampling: |
| |
| - Nearest Neighbor |
| - Bilinear |
| |
| **Nearest Neighbor Upsampling** |
| |
| Input data is expected to be NCHW. |
| |
| Example:: |
| |
| x = [[[[1. 1. 1.] |
| [1. 1. 1.] |
| [1. 1. 1.]]]] |
| |
| UpSampling(x, scale=2, sample_type='nearest') = [[[[1. 1. 1. 1. 1. 1.] |
| [1. 1. 1. 1. 1. 1.] |
| [1. 1. 1. 1. 1. 1.] |
| [1. 1. 1. 1. 1. 1.] |
| [1. 1. 1. 1. 1. 1.] |
| [1. 1. 1. 1. 1. 1.]]]] |
| |
| **Bilinear Upsampling** |
| |
| Uses `deconvolution` algorithm under the hood. You need provide both input data and the kernel. |
| |
| Input data is expected to be NCHW. |
| |
| `num_filter` is expected to be same as the number of channels. |
| |
| Example:: |
| |
| x = [[[[1. 1. 1.] |
| [1. 1. 1.] |
| [1. 1. 1.]]]] |
| |
| w = [[[[1. 1. 1. 1.] |
| [1. 1. 1. 1.] |
| [1. 1. 1. 1.] |
| [1. 1. 1. 1.]]]] |
| |
| UpSampling(x, w, scale=2, sample_type='bilinear', num_filter=1) = [[[[1. 2. 2. 2. 2. 1.] |
| [2. 4. 4. 4. 4. 2.] |
| [2. 4. 4. 4. 4. 2.] |
| [2. 4. 4. 4. 4. 2.] |
| [2. 4. 4. 4. 4. 2.] |
| [1. 2. 2. 2. 2. 1.]]]] |
| )code" ADD_FILELINE) |
| .set_num_inputs([](const NodeAttrs& attrs) { |
| const UpSamplingParam& params = nnvm::get<UpSamplingParam>(attrs.parsed); |
| return params.sample_type == up_enum::kNearest ? params.num_args : 2; |
| }) |
| .set_num_outputs(1) |
| .set_attr_parser(ParamParser<UpSamplingParam>) |
| .set_attr<nnvm::FListInputNames>("FListInputNames", |
| [](const NodeAttrs& attrs) { |
| return ListArguments(nnvm::get<UpSamplingParam>(attrs.parsed)); |
| }) |
| .set_attr<nnvm::FListOutputNames>("FListOutputNames", |
| [](const NodeAttrs& attrs) { |
| return std::vector<std::string>{"output"}; |
| }) |
| .set_attr<mxnet::FInferShape>("FInferShape", UpSamplingShape) |
| .set_attr<nnvm::FInferType>("FInferType", UpSamplingType) |
| .set_attr<FResourceRequest>("FResourceRequest", [](const NodeAttrs& n) { |
| const UpSamplingParam& param = nnvm::get<UpSamplingParam>(n.parsed); |
| if (param.sample_type == up_enum::kNearest) { |
| return std::vector<ResourceRequest>(); |
| } else { |
| return std::vector<ResourceRequest>{ResourceRequest::kTempSpace}; |
| } |
| }) |
| .set_attr<THasDeterministicOutput>("THasDeterministicOutput", true) |
| .set_attr<FCompute>("FCompute<cpu>", UpSamplingCompute<cpu>) |
| .set_attr<nnvm::FGradient>("FGradient", UpSamplingGrad{"_backward_UpSampling"}) |
| .set_attr<std::string>("key_var_num_args", "num_args") |
| .add_argument("data", "NDArray-or-Symbol[]", "Array of tensors to upsample. " |
| "For bilinear upsampling, there should be 2 inputs - 1 data and 1 weight.") |
| .add_arguments(UpSamplingParam::__FIELDS__()) |
| .set_attr<nnvm::FSetInputVarAttrOnCompose>("FSetInputVarAttrOnCompose", |
| [](const nnvm::NodeAttrs& attrs, nnvm::ObjectPtr var, const int index) { |
| if (var->attrs.dict.find("__init__") != var->attrs.dict.end()) return; |
| if (index == 1) { |
| var->attrs.dict["__init__"] = "[\"bilinear\", {}]"; |
| } |
| }); |
| |
| NNVM_REGISTER_OP(_backward_UpSampling) |
| .set_num_inputs([](const NodeAttrs& attrs) { |
| const UpSamplingParam& param_ = nnvm::get<UpSamplingParam>(attrs.parsed); |
| if (param_.sample_type != up_enum::kNearest) { |
| return 3; |
| } else { |
| return 1; |
| } |
| }) |
| .set_num_outputs([](const NodeAttrs& attrs) { |
| const UpSamplingParam& params = nnvm::get<UpSamplingParam>(attrs.parsed); |
| return params.sample_type == up_enum::kNearest ? params.num_args : 2; |
| }) |
| .set_attr<nnvm::TIsBackward>("TIsBackward", true) |
| .set_attr<FResourceRequest>("FResourceRequest", [](const NodeAttrs& n) { |
| const UpSamplingParam& param = nnvm::get<UpSamplingParam>(n.parsed); |
| if (param.sample_type == up_enum::kNearest) { |
| return std::vector<ResourceRequest>(); |
| } else { |
| return std::vector<ResourceRequest>{ResourceRequest::kTempSpace}; |
| } |
| }) |
| .set_attr_parser(ParamParser<UpSamplingParam>) |
| .set_attr<FCompute>("FCompute<cpu>", UpSamplingGradCompute<cpu>); |
| |
| } // namespace op |
| } // namespace mxnet |