blob: 21549c4cd137925cc213d7cd577961f72712fa1a [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*!
* \file pad-inl.h
* \brief
* \author Sebastian Bodenstien
*/
#ifndef MXNET_OPERATOR_PAD_INL_H_
#define MXNET_OPERATOR_PAD_INL_H_
#include <dmlc/logging.h>
#include <dmlc/parameter.h>
#include <mxnet/operator.h>
#include <algorithm>
#include <map>
#include <vector>
#include <string>
#include <utility>
#include "./operator_common.h"
#include "./mshadow_op.h"
namespace mxnet {
namespace op {
namespace pad_enum {
enum PadOpInputs { kData };
enum PadOpType { kConstant, kEdge, kReflect };
enum PadOpOutputs { kOut };
} // namespace pad_enum
struct PadParam : public dmlc::Parameter<PadParam> {
int mode;
double constant_value;
mxnet::TShape pad_width;
DMLC_DECLARE_PARAMETER(PadParam) {
DMLC_DECLARE_FIELD(mode)
.add_enum("constant", pad_enum::kConstant)
.add_enum("edge", pad_enum::kEdge)
.add_enum("reflect", pad_enum::kReflect)
.describe(
"Padding type to use."
" \"constant\" pads with `constant_value`"
" \"edge\" pads using the edge values of the input array"
" \"reflect\" pads by reflecting values with respect to the edges.");
DMLC_DECLARE_FIELD(pad_width).describe(
"Widths of the padding regions applied to the edges of each axis. "
"It is a tuple of integer padding widths for each axis of the format "
"``(before_1, after_1, ... , before_N, after_N)``. "
"It should be of length ``2*N`` where ``N`` is the number of dimensions of the array."
"This is equivalent to pad_width in numpy.pad, but flattened.");
DMLC_DECLARE_FIELD(constant_value)
.describe("The value used for padding when `mode` is \"constant\".")
.set_default(0.0);
}
};
template <typename xpu, typename DType>
class PadOp : public Operator {
public:
explicit PadOp(PadParam p) {
this->param_ = p;
}
virtual void Forward(const OpContext& ctx,
const std::vector<TBlob>& in_data,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& out_data,
const std::vector<TBlob>& aux_args) {
using namespace mshadow;
using namespace mshadow::expr;
CHECK_EQ(in_data.size(), 1U);
CHECK_EQ(out_data.size(), 1U);
Stream<xpu>* s = ctx.get_stream<xpu>();
// Get any size input + output into required form
int rank = in_data[pad_enum::kData].ndim();
auto pad = param_.pad_width;
DType constant_value = param_.constant_value;
// TODO(nswamy@): update the documentation and log below when support is added for more than
// 4D/5D arrays and not requiring higher dimensions to be zero.
switch (rank) {
case 4: {
Tensor<xpu, 4, DType> data = in_data[pad_enum::kData].get<xpu, 4, DType>(s);
Tensor<xpu, 4, DType> out = out_data[pad_enum::kOut].get<xpu, 4, DType>(s);
pad_image(out, data, param_.pad_width, param_.mode, constant_value);
break;
}
case 5: {
Tensor<xpu, 5, DType> data = in_data[pad_enum::kData].get<xpu, 5, DType>(s);
Tensor<xpu, 5, DType> out = out_data[pad_enum::kOut].get<xpu, 5, DType>(s);
pad_image(out, data, param_.pad_width, param_.mode, constant_value);
break;
}
default:
LOG(FATAL) << "Attempted to run forward pass "
"with input dimensions other than 4 or 5.";
}
// Assign(out, req[pad_enum::kOut], F<mshadow_op::identity>(data));
}
virtual void Backward(const OpContext& ctx,
const std::vector<TBlob>& out_grad,
const std::vector<TBlob>& in_data,
const std::vector<TBlob>& out_data,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& in_grad,
const std::vector<TBlob>& aux_args) {
using namespace mshadow;
using namespace mshadow::expr;
CHECK_EQ(out_grad.size(), 1U);
CHECK_EQ(out_data.size(), 1U);
Stream<xpu>* s = ctx.get_stream<xpu>();
// Get any size input + output into required form
auto pad = param_.pad_width;
int rank = in_grad[pad_enum::kData].ndim();
switch (rank) {
case 4: {
Tensor<xpu, 4, DType> in = in_grad[pad_enum::kData].get<xpu, 4, DType>(s);
Tensor<xpu, 4, DType> out = out_grad[pad_enum::kOut].get<xpu, 4, DType>(s);
if (req[pad_enum::kData] == kWriteTo)
in = 0.0f;
pad_image_grad(in, out, param_.pad_width, param_.mode);
break;
}
case 5: {
Tensor<xpu, 5, DType> in = in_grad[pad_enum::kData].get<xpu, 5, DType>(s);
Tensor<xpu, 5, DType> out = out_grad[pad_enum::kOut].get<xpu, 5, DType>(s);
if (req[pad_enum::kData] == kWriteTo)
in = 0.0f;
pad_image_grad(in, out, param_.pad_width, param_.mode);
break;
}
default:
LOG(FATAL) << "Attempted to run backward pass "
"with input dimensions other than 4 or 5.";
}
}
private:
PadParam param_;
}; // class PadOp
template <typename xpu>
Operator* CreateOp(PadParam param, int dtype);
#if DMLC_USE_CXX11
class PadProp : public OperatorProperty {
public:
int NumVisibleOutputs() const override {
return 1;
}
int NumOutputs() const override {
return 1;
}
std::vector<std::string> ListArguments() const override {
return {"data"};
}
std::vector<std::string> ListOutputs() const override {
return {"output"};
}
void Init(const std::vector<std::pair<std::string, std::string> >& kwargs) override {
param_.Init(kwargs);
}
std::map<std::string, std::string> GetParams() const override {
return param_.__DICT__();
}
bool InferType(std::vector<int>* in_type,
std::vector<int>* out_type,
std::vector<int>* aux_type) const override {
int dtype = (*in_type)[0];
type_assign(&dtype, (*out_type)[0]);
TYPE_ASSIGN_CHECK(*in_type, 0, dtype);
TYPE_ASSIGN_CHECK(*out_type, 0, dtype);
return dtype != -1;
}
bool InferShape(mxnet::ShapeVector* in_shape,
mxnet::ShapeVector* out_shape,
mxnet::ShapeVector* aux_shape) const override {
using namespace mshadow;
CHECK_EQ(in_shape->size(), 1U) << "Can only be one input to symbol.";
const mxnet::TShape& dshape = (*in_shape)[pad_enum::kData];
auto rank = dshape.ndim();
auto pad = param_.pad_width;
auto pad_spec_len = param_.pad_width.ndim();
if (rank == 0)
return false;
if ((rank != 4) && (rank != 5)) {
LOG(FATAL) << "Current implementation only supports 4-D or 5-D input.";
}
if ((pad[0] != 0) || (pad[1] != 0) || (pad[2] != 0) || (pad[3] != 0)) {
LOG(FATAL) << "Current implementation expects padding on the first two axes to be zero.";
}
if ((2 * rank) != pad_spec_len) {
LOG(FATAL) << "Input shape vs padding spec mismatch.";
}
if (param_.mode == pad_enum::kReflect) {
auto size = dshape.data();
if ((pad[4] >= size[2]) || (pad[5] >= size[2]) || (pad[6] >= size[3]) ||
(pad[7] >= size[3])) {
LOG(FATAL) << "Current implementation of reflection padding "
"only supports padding sizes smaller than the input size.";
}
}
mxnet::TShape oshape = dshape;
for (int i = 0; i < dshape.ndim(); ++i) {
oshape[i] = param_.pad_width[2 * i] + param_.pad_width[2 * i + 1] + dshape[i];
}
out_shape->clear();
out_shape->push_back(oshape);
return true;
}
OperatorProperty* Copy() const override {
auto ptr = new PadProp();
ptr->param_ = param_;
return ptr;
}
std::string TypeString() const override {
return "Pad";
}
std::vector<int> DeclareBackwardDependency(const std::vector<int>& out_grad,
const std::vector<int>& in_data,
const std::vector<int>& out_data) const override {
return {out_grad[pad_enum::kOut]};
}
Operator* CreateOperator(Context ctx) const override {
LOG(FATAL) << "Not Implemented.";
return nullptr;
}
Operator* CreateOperatorEx(Context ctx,
mxnet::ShapeVector* in_shape,
std::vector<int>* in_type) const override;
private:
PadParam param_;
}; // class PadProp
#endif // DMLC_USE_CXX11
} // namespace op
} // namespace mxnet
#endif // MXNET_OPERATOR_PAD_INL_H_