* Change the signatures of BiasAddOp and ScaleOp to identically handle both input parameters.
* Refactor uses of BiasAddOp and ScaleOp.
Signed-off-by: Sergei Barannikov <s.barannikov@samsung.com>
void IrDotDumper::visit(ops::BiasAddOp &op) {
auto nodeInfo = DotIrNodeInfo().withType("BiasAdd", op.getName())
.withInShapes(getInputShapes(op))
- .withOutShapes(getOutputShapes(op))
- .withKernelShape(op.getWeights().getShape());
+ .withOutShapes(getOutputShapes(op));
dotBuilder.updateWithOp(&op, nodeInfo);
}
void IrDotDumper::visit(ops::ScaleOp& op) {
auto nodeInfo = DotIrNodeInfo().withType("ScaleOp", op.getName())
.withInShapes(getInputShapes(op))
- .withOutShapes(getOutputShapes(op))
- .withShape("Scale Tensor", op.getWeights().getShape());
+ .withOutShapes(getOutputShapes(op));
dotBuilder.updateWithOp(&op, nodeInfo);
}
class BiasAddOp : public Operation {
public:
- BiasAddOp(const IODescriptor& arg, const TensorVariant& weights)
- : Operation(Type::biasAdd, {arg}), _weights(weights) {
+ BiasAddOp(const IODescriptor& arg1, const IODescriptor& arg2)
+ : Operation(Type::biasAdd, {arg1, arg2}) {
// Infer output shape.
setOutputShape(0, getInputShape(0));
}
-
- const TensorVariant& getWeights() const { return _weights; }
-
-private:
- TensorVariant _weights;
};
} // namespace ops
class ScaleOp : public Operation {
public:
- ScaleOp(const IODescriptor& arg, const TensorVariant& weights)
- : Operation(Type::scale, {arg}), _weights(weights) {
+ ScaleOp(const IODescriptor& arg1, const IODescriptor& arg2)
+ : Operation(Type::scale, {arg1, arg2}) {
// Infer output shape.
setOutputShape(0, getInputShape(0));
}
-
- /**
- * @return The input 1-dimensional scale tensor.
- */
- const TensorVariant& getWeights() const { return _weights; }
-
-private:
- TensorVariant _weights;
};
} // namespace ops
}
void AclCppOpGenerator::visit(ops::BiasAddOp& op) {
- const auto& ir_biases = op.getWeights();
- assert(ir_biases.getShape().rank() == 1);
-
auto& prev_nodes = op.getPrevNodes();
- assert(prev_nodes.size() == 1);
+ assert(prev_nodes.size() == 2);
auto in_op = prev_nodes[0].op;
+ auto ir_biases_op = dynamic_cast<ops::ConstantOp*>(prev_nodes[1].op);
+ if (ir_biases_op == nullptr)
+ throw AclCppException("Unsupported operation type");
+
+ const auto& ir_biases = ir_biases_op->getValue();
+ assert(ir_biases.getShape().rank() == 1);
// Get the input node tensor id in the DOM.
shared_ptr<ArtifactId> input = AF::id(tensorName(in_op));
// May be not a perfect implementation, using the CLPixelWiseMultiplication ACL function taking
// two input tensors with the same shapes.
auto prev_nodes = op.getPrevNodes();
- assert(prev_nodes.size() == 1);
+ assert(prev_nodes.size() == 2);
auto in_op = prev_nodes[0].op;
+ auto ir_scales_op = dynamic_cast<ops::ConstantOp*>(prev_nodes[1].op);
+ if (ir_scales_op == nullptr)
+ throw AclCppException("Unsupported operation type");
+
+ const auto& ir_scales = ir_scales_op->getValue();
+ assert(ir_scales.getShape().rank() == 1);
// Get input tensor identifier in the generated artifact.
auto input = AF::id(tensorName(in_op));
auto operation_name = transposed_output->name() + "_scale_layer";
- const auto& ir_scales = op.getWeights();
-
// Reshape the IR scales tensor and generate the corresponding DOM tensor.
const Shape ir_input_shape = transposeShape<0, 3, 1, 2>(op.getInputShape(0));
Shape ir_scales_shape(ir_input_shape.rank());
}
shared_ptr<ArtifactId> AclCppOpGenerator::genTensor(Operation& op, const Shape& ir_shape) {
- if (op.getPrevNodes().empty())
+ if (op.getType() == Operation::Type::variable)
_inputs.insert(&op);
if (op.getNextNodes().empty())
int num_groups = getSingleArgument(op, "group", 1);
bool is_depthwise = (num_groups != 1) && (in_group_size == 1) && (out_channels == num_groups);
- mir::Operation* conv2d;
+ mir::Operation* result;
if (is_depthwise) {
// TODO handle properly kernel with layer multiplier
auto transposed_tensor = mir::transposeTensor<0, 1, 3, 2>(kernel_tensor);
- conv2d = createOp<ops::DepthwiseConv2DOp>(convertCaffeToMIR(inputs[0]), transposed_tensor,
+ result = createOp<ops::DepthwiseConv2DOp>(convertCaffeToMIR(inputs[0]), transposed_tensor,
stride_shape, pad_before, pad_after);
} else {
// first we need to convert kernel of grouped convolution to appropriate ordinary kernel
if (num_groups != 1)
kernel_tensor = fixGroupedKernel(num_groups, kernel_tensor);
- conv2d = createOp<ops::Conv2DOp>(convertCaffeToMIR(inputs[0]), kernel_tensor,
+ result = createOp<ops::Conv2DOp>(convertCaffeToMIR(inputs[0]), kernel_tensor,
stride_shape, pad_before, pad_after);
}
if (op.input_size() > 2) { // Bias is optional
- auto bias_add = createOp<ops::BiasAddOp>(conv2d->getOutput(0), mir_tensors.at(op.input(2)));
- return {convertMIRToCaffe(bias_add->getOutput(0))};
+ auto bias = createOp<ops::ConstantOp>(mir_tensors.at(op.input(2)))->getOutput(0);
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), bias);
}
- return {convertMIRToCaffe(conv2d->getOutput(0))};
+
+ return {convertMIRToCaffe(result->getOutput(0))};
}
std::vector<IODescriptor> Caffe2OpCreator::convertConcat(const std::vector<IODescriptor>& inputs,
// Transform input into 2-D tensor by flattening axes
Shape shape{input_shape.dim(0), input_shape.numElements() / input_shape.dim(0)};
auto reshape = createOp<ops::ReshapeOp>(inputs[0], shape);
- auto fully_connected = createOp<ops::FullyConnectedOp>(reshape->getOutput(0), weights_tensor);
+ auto result = createOp<ops::FullyConnectedOp>(reshape->getOutput(0), weights_tensor);
- auto bias = createOp<ops::BiasAddOp>(fully_connected->getOutput(0), mir_tensors.at(op.input(2)));
- return {bias->getOutput(0)};
+ auto bias = createOp<ops::ConstantOp>(mir_tensors.at(op.input(2)))->getOutput(0);
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), bias);
+ return {result->getOutput(0)};
}
std::vector<IODescriptor> Caffe2OpCreator::convertMaxPool(const std::vector<IODescriptor>& inputs,
const ::caffe2::OperatorDef& op,
const MIRTensors& mir_tensors) {
const auto& input_shape = inputs[0].op->getOutputShape(inputs[0].index);
- const auto& multiplier = mir_tensors.at(op.input(1));
-
- assert(multiplier.getShape().rank() == 1 && "Only 1-rank multiplier is supported");
- assert(multiplier.getShape().numElements() == input_shape.dim(1)
- && "Only multiplier size equal to number of input channels is supported");
// TODO: replace with elementwise op, when broadcating will be added in elementwise op
- auto mul = createOp<ops::ScaleOp>(convertCaffeToMIR(inputs[0]), multiplier);
- return {convertMIRToCaffe(mul->getOutput(0))};
+ auto multiplier = createOp<ops::ConstantOp>(mir_tensors.at(op.input(1)))->getOutput(0);
+ auto result = createOp<ops::ScaleOp>(convertCaffeToMIR(inputs[0]), multiplier);
+ return {convertMIRToCaffe(result->getOutput(0))};
}
std::vector<IODescriptor>
const MIRTensors& mir_tensors) {
// overall_res = (X - mean) / sqrt(var + epsilon) * scale + bias
- const auto& scale = mir_tensors.at(op.input(1));
- const auto& bias = mir_tensors.at(op.input(2));
- const auto& mean = mir_tensors.at(op.input(3));
- const auto& var = mir_tensors.at(op.input(4));
+ const auto& scale_tensor = mir_tensors.at(op.input(1));
+ const auto& bias_tensor = mir_tensors.at(op.input(2));
+ const auto& mean_tensor = mir_tensors.at(op.input(3));
+ const auto& var_tensor = mir_tensors.at(op.input(4));
float eps = getSingleArgument(op, "epsilon", 1e-5f);
// res1 = X - mean
- Tensor<float> bias_data(mean);
+ Tensor<float> bias_data(mean_tensor);
for (auto& idx: ShapeRange(bias_data.getShape()))
bias_data.at(idx) *= -1;
- auto bias_add_1 = createOp<ops::BiasAddOp>(convertCaffeToMIR(inputs[0]), mean);
+ auto mean = createOp<ops::ConstantOp>(mean_tensor)->getOutput(0);
+ auto result = createOp<ops::BiasAddOp>(convertCaffeToMIR(inputs[0]), mean);
// res2 = res1 * scale / (var + epsilon)
- Tensor<float> multiplier(scale);
- for (auto& idx: ShapeRange(scale.getShape()))
- multiplier.at(idx) /= std::sqrt(*(float*) var.at(idx) + eps);
- auto scale_op = createOp<ops::ScaleOp>(bias_add_1->getOutput(0), scale);
+ Tensor<float> multiplier(scale_tensor);
+ for (auto& idx: ShapeRange(scale_tensor.getShape()))
+ multiplier.at(idx) /= std::sqrt(*(float*) var_tensor.at(idx) + eps);
+ auto scale = createOp<ops::ConstantOp>(scale_tensor)->getOutput(0);
+ result = createOp<ops::ScaleOp>(result->getOutput(0), scale);
// overall_res = res2 + bias
- auto bias_add_2 = createOp<ops::BiasAddOp>(scale_op->getOutput(0), bias);
+ auto bias = createOp<ops::ConstantOp>(bias_tensor)->getOutput(0);
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), bias);
- return {convertMIRToCaffe(bias_add_2->getOutput(0))};
+ return {convertMIRToCaffe(result->getOutput(0))};
}
std::vector<IODescriptor> Caffe2OpCreator::convertSum(const std::vector<IODescriptor>& inputs) {
// Add the bias, if any.
if (params.bias_term()) {
- auto bias_weights = convertBlob(layer.blobs(1));
- result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result->getOutput(0), bias_weights);
+ auto bias = createOp<ops::ConstantOp>("", convertBlob(layer.blobs(1)))->getOutput(0);
+ result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result->getOutput(0), bias);
}
return {convertMIRToCaffe(result->getOutput(0))};
// first we need to convert kernel of grouped convolution to appropriate ordinary kernel
kernel_weights = fixGroupedKernel(opts.group(), kernel_weights);
}
- auto deconv2d = createOp<ops::DeConv2DOp>(layer.name(), convertCaffeToMIR(inputs[0]),
+ auto result = createOp<ops::DeConv2DOp>(layer.name(), convertCaffeToMIR(inputs[0]),
kernel_weights, strides, padding);
// bias_term is optional (so might not be present) and defaults to true
- if (!opts.has_bias_term() || opts.bias_term()) {
- auto bias_weights = convertBlob(layer.blobs(1));
- auto bias_add = createOp<ops::BiasAddOp>(layer.name() + ".bias", deconv2d->getOutput(0),
- bias_weights);
- return {convertMIRToCaffe(bias_add->getOutput(0))};
- } else {
- return {convertMIRToCaffe(deconv2d->getOutput(0))};
+ if (opts.bias_term()) {
+ auto bias = createOp<ops::ConstantOp>("", convertBlob(layer.blobs(1)))->getOutput(0);
+ result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result->getOutput(0), bias);
}
+
+ return {convertMIRToCaffe(result->getOutput(0))};
}
std::vector<mir::IODescriptor>
// Add the bias, if any.
if (params.bias_term()) {
- const auto& bias_weights = convertBlob(layer.blobs(1));
- result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result, bias_weights)->getOutput(0);
+ auto bias = createOp<ops::ConstantOp>("", convertBlob(layer.blobs(1)))->getOutput(0);
+ result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result, bias)->getOutput(0);
}
return {result};
CaffeOpCreator::convertScale(const caffe::LayerParameter& layer,
const std::vector<mir::IODescriptor>& inputs) {
const auto& params = layer.scale_param();
- auto weights = convertBlob(layer.blobs(0));
- auto result = createOp<ops::ScaleOp>(layer.name(), convertCaffeToMIR(inputs[0]), weights);
+ auto scale = createOp<ops::ConstantOp>("", convertBlob(layer.blobs(0)))->getOutput(0);
+ auto result = createOp<ops::ScaleOp>(layer.name(), convertCaffeToMIR(inputs[0]), scale);
// Add the bias, if any.
if (params.bias_term()) {
- auto bias_weights = convertBlob(layer.blobs(1));
- result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result->getOutput(0), bias_weights);
+ auto bias = createOp<ops::ConstantOp>("", convertBlob(layer.blobs(1)))->getOutput(0);
+ result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result->getOutput(0), bias);
}
return {convertMIRToCaffe(result->getOutput(0))};
Tensor<float> bias_data(mean_weights);
for (Index idx : ShapeRange(bias_data.getShape()))
bias_data.at(idx) *= -scale_factor;
- auto bias_add = createOp<ops::BiasAddOp>(layer.name() + ".bias", convertCaffeToMIR(inputs[0]),
- mean_weights);
+ auto mean = createOp<ops::ConstantOp>("", mean_weights)->getOutput(0);
+ auto result = createOp<ops::BiasAddOp>(layer.name() + ".bias", convertCaffeToMIR(inputs[0]),
+ mean);
// create scale argument from variance:
// multiply elements of variance by scaleFactor and
Tensor<float> scale_data(variance_weights);
for (Index idx : ShapeRange(scale_data.getShape()))
scale_data.at(idx) = 1.0f / std::sqrt(scale_data.at(idx) * scale_factor + eps);
- auto scale = createOp<ops::ScaleOp>(layer.name() + ".scale", bias_add->getOutput(0),
- variance_weights);
- return {convertMIRToCaffe(scale->getOutput(0))};
+ auto variance = createOp<ops::ConstantOp>("", variance_weights)->getOutput(0);
+ result = createOp<ops::ScaleOp>(layer.name() + ".scale", result->getOutput(0), variance);
+ return {convertMIRToCaffe(result->getOutput(0))};
}
std::vector<mir::IODescriptor>
// Add the bias, if any.
if (params.bias_term()) {
- auto bias_weights = convertBlob(layer.blobs(1));
- result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result->getOutput(0), bias_weights);
+ auto bias = createOp<ops::ConstantOp>("", convertBlob(layer.blobs(1)))->getOutput(0);
+ result = createOp<ops::BiasAddOp>(layer.name() + ".bias", result->getOutput(0), bias);
}
return {result->getOutput(0)};
// Learned parameters of the layer. Tensors are transposed to match the ModelIR.
const auto& xw = transposeTensor<1, 0>(convertBlob(layer.blobs(0)));
- const auto& xb = convertBlob(layer.blobs(1));
+ auto xb = createOp<ops::ConstantOp>("", convertBlob(layer.blobs(1)))->getOutput(0);
const auto& hw = transposeTensor<1, 0>(convertBlob(layer.blobs(2)));
// Add a dummy dimension so that element-wise operations perform properly.
}
void NNInterpreter::visit(ops::BiasAddOp& op) {
- auto operand = op.getPrevNodes()[0];
- auto input = var(operand.op->getId())[operand.index];
- var(op.getId()) = BiasAdd(input, op.getWeights(), op.getOutputShape(0))();
+ auto operand1 = op.getPrevNodes()[0];
+ auto operand2 = op.getPrevNodes()[1];
+ auto input1 = var(operand1.op->getId())[operand1.index];
+ auto input2 = var(operand2.op->getId())[operand2.index];
+ var(op.getId()) = BiasAdd(input1, input2)();
DUMP(op, false);
}
}
void NNInterpreter::visit(ops::ScaleOp& op) {
- auto operand = op.getPrevNodes()[0];
- TensorVariant input(var(operand.op->getId())[operand.index]);
- // TODO implement this
- var(op.getId()) = Scale(input, op)();
+ auto operand1 = op.getPrevNodes()[0];
+ auto operand2 = op.getPrevNodes()[1];
+ auto input1 = var(operand1.op->getId())[operand1.index];
+ auto input2 = var(operand2.op->getId())[operand2.index];
+ var(op.getId()) = Scale(input1, input2)();
DUMP(op, false);
}
*/
#include "Bias.h"
+#include "Fill.h"
+
+namespace nnc {
+
+BiasAdd::BiasAdd(const mir::TensorVariant& in1, const mir::TensorVariant& in2)
+ : _input1(in1), _input2(in2) {
+ assert(_input2.getShape().rank() == 1);
+ assert(_input1.getShape().dim(-1) == _input2.getShape().dim(0));
+}
+
+std::vector<mir::TensorVariant> BiasAdd::operator()() {
+ return Fill<float>(_input1.getShape(), [this](const mir::Index& idx) {
+ return _input1.at(idx) + _input2.atOffset({idx.at(idx.rank() - 1)});
+ })();
+}
+
+}
#define _NNC_BACKEND_INTERPRETER_BIAS_
#include "OperationImpl.h"
-#include "Fill.h"
-namespace nnc
-{
+namespace nnc {
-class BiasAdd : public OperationImpl<float>
-{
+class BiasAdd : public OperationImpl<float> {
public:
- BiasAdd(const mir::TensorVariant &input, const mir::TensorVariant &weights, const mir::Shape &outputShape)
- : _weights(weights), _input(input), _outputShape(outputShape)
- {
- assert(_weights.getShape().rank() == 1);
- assert(_outputShape.rank() == _input.getShape().rank());
- assert(_outputShape.dim(_outputShape.rank() - 1) == _weights.getShape().dim(0));
- }
-
- std::vector<mir::TensorVariant> operator()() override
- {
- return Fill<float>(_outputShape, [this](const mir::Index &idx) {
- return _input.at(idx) + _weights.atOffset({idx.at(idx.rank() - 1)});
- })();
- }
+ BiasAdd(const mir::TensorVariant& in1, const mir::TensorVariant& in2);
+
+ std::vector<mir::TensorVariant> operator()() override;
private:
- const mir::Tensor<float> _weights;
- const mir::Tensor<float> _input;
- const mir::Shape &_outputShape;
+ const mir::Tensor<float> _input1;
+ const mir::Tensor<float> _input2;
};
} // namespace nnc
*/
#include "Scale.h"
-
#include "Fill.h"
-namespace nnc
-{
+namespace nnc {
+
+Scale::Scale(const mir::TensorVariant& in1, const mir::TensorVariant& in2)
+ : _input1(in1), _input2(in2) {
+ assert(_input2.getShape().rank() == 1);
+ assert(_input1.getShape().dim(-1) == _input2.getShape().dim(0));
+}
-std::vector<mir::TensorVariant> Scale::operator()()
-{
- //For now handles only most common case with scale applied by last dimension
- mir::Tensor<float> weightsAccessor(_op.getWeights());
- return Fill<float>(_input.getShape(), [this, weightsAccessor](const mir::Index &idx) {
- return _input.at(idx) * weightsAccessor.atOffset({idx.at(idx.rank() - 1)});
+std::vector<mir::TensorVariant> Scale::operator()() {
+ return Fill<float>(_input1.getShape(), [this](const mir::Index& idx) {
+ return _input1.at(idx) * _input2.atOffset({idx.at(idx.rank() - 1)});
})();
}
#include "OperationImpl.h"
-#include "core/modelIR/operations/ScaleOp.h"
+namespace nnc {
-namespace nnc
-{
-/**
- * @brief Implements ScaleOp for interpreter backend
- * @todo check if I need support for any datatypes other than DTYPE::FLOAT
- */
class Scale : public OperationImpl<float> {
public:
- /**
- * @param in input data
- * @param op scale operation description
- */
- explicit Scale(const mir::TensorVariant& in, const mir::ops::ScaleOp& op) : _input(in), _op(op) {}
-
- /**
- * @brief computes operation aplication result
- * @return vector of all outputs from this node
- */
+ Scale(const mir::TensorVariant& in1, const mir::TensorVariant& in2);
+
std::vector<mir::TensorVariant> operator()() override;
private:
- mir::Tensor<float> _input;
- const mir::ops::ScaleOp& _op;
+ const mir::Tensor<float> _input1;
+ const mir::Tensor<float> _input2;
};
} // namespace nnc
// We should transpose ONNX MCHW to HWOI
auto transposed = transposeTensor<2, 3, 1, 0>(in_weights_tensor);
- mir::ops::ConstantOp* input_bias = nullptr;
- if (inputs.size() > 2) {
- input_bias = dynamic_cast<mir::ops::ConstantOp*>(inputs[2].op);
- assert(input_bias && "1D optional bias could be a constant tensor only");
- }
-
// Transpose ONNX NCHW to MIR NHWC
auto t_input = convertONNXToMIR(inputs[0]);
auto result = createOp<ops::Conv2DOp>(t_input, transposed, cdata.strides_shape,
cdata.padding_before, cdata.padding_after);
- if (input_bias)
- result = createOp<ops::BiasAddOp>(result->getOutput(0), input_bias->getValue());
+ if (inputs.size() > 2)
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), inputs[2]);
return {convertMIRToONNX(result->getOutput(0))};
}
std::tie(found, value) = getFloatAttribute(onnx_node, "epsilon");
float epsilon = found ? value : 1e-05f;
- const auto& scale = input_tensors.at(inputs[1].op->getName());
- const auto& bias = input_tensors.at(inputs[2].op->getName());
- const auto& mean = input_tensors.at(inputs[3].op->getName());
- const auto& var = input_tensors.at(inputs[4].op->getName());
+ const auto& scale_tensor = input_tensors.at(inputs[1].op->getName());
+ const auto& bias_tensor = input_tensors.at(inputs[2].op->getName());
+ const auto& mean_tensor = input_tensors.at(inputs[3].op->getName());
+ const auto& var_tensor = input_tensors.at(inputs[4].op->getName());
// res1 = X - mean
- Tensor<float> bias_data(mean);
+ Tensor<float> bias_data(mean_tensor);
for (auto& idx: ShapeRange(bias_data.getShape()))
bias_data.at(idx) *= -1;
auto data = convertONNXToMIR(inputs[0]);
- auto bias_add_1 = createOp<ops::BiasAddOp>(data, mean);
+ auto mean = createOp<ops::ConstantOp>(mean_tensor)->getOutput(0);
+ auto result = createOp<ops::BiasAddOp>(data, mean);
// res2 = res1 * scale / (var + epsilon)
- Tensor<float> multiplier(scale);
- Tensor<float> var_accessor(var);
- for (auto& idx: ShapeRange(scale.getShape()))
+ Tensor<float> multiplier(scale_tensor);
+ Tensor<float> var_accessor(var_tensor);
+ for (auto& idx: ShapeRange(scale_tensor.getShape()))
multiplier.at(idx) /= std::sqrt(var_accessor.at(idx) + epsilon);
- auto scale_op = createOp<ops::ScaleOp>(bias_add_1->getOutput(0), scale);
+ result = createOp<ops::ScaleOp>(result->getOutput(0), scale_tensor);
// overall_res = res2 + bias
- auto bias_add_2 = createOp<ops::BiasAddOp>(scale_op->getOutput(0), bias);
+ auto bias = createOp<ops::ConstantOp>(bias_tensor)->getOutput(0);
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), bias);
- return {convertMIRToONNX(bias_add_2->getOutput(0))};
+ return {convertMIRToONNX(result->getOutput(0))};
}
std::vector<IODescriptor>
void Serializer::visit(ops::BiasAddOp& op) {
_curOp->_paramStartOffset = _buffer.size();
- serializeTensor(op.getWeights());
+ // no parameters to dump
}
void Serializer::visit(ops::VariableOp& op) {
void Serializer::visit(ops::ScaleOp& op) {
_curOp->_paramStartOffset = _buffer.size();
- serializeTensor(op.getWeights());
+ // no parameters to dump
}
void Serializer::visit(mir::ops::SliceOp& op) {
CappedRelu(input, input_d, cap, out.getData(), input_d);
}
-void biasAdd(Tensor &out, const char *params, const Tensor &in)
+void biasAdd(Tensor &out, const char *params, const Tensor &in1, const Tensor& in2)
{
- Kernel bias = deserializeKernel(params);
+ out.reShape(in1.getShape());
+ out.fillData(in1.getData(), in1.getShape().getNumElems());
- out.reShape(in.getShape());
- out.fillData(in.getData(), in.getShape().getNumElems());
-
- AddBiasAndEvalActivationFunction(bias.data, bias.dims, out.getData(), shapeToDims(out.getShape()));
+ AddBiasAndEvalActivationFunction(in2.getData(), shapeToDims(in2.getShape()),
+ out.getData(), shapeToDims(out.getShape()));
}
void slice(Tensor& out, const char* params, const Tensor& in) {
* limitations under the License.
*/
-void scale(Tensor &out, const char *params, const Tensor &in)
-{
- out.reShape(in.getShape());
- Kernel weights = deserializeKernel(params);
- Shape inShape = in.getShape();
- const int wSize = weights.dims.sizes[0];
- assert(wSize == inShape[inShape.getDims() - 1]);
- assert(weights.dims.sizes[1] == 1);
- assert(weights.dims.sizes[2] == 1);
- assert(weights.dims.sizes[3] == 1);
+void scale(Tensor& out, const char* params, const Tensor& in1, const Tensor& in2) {
+ const Shape& in1_shape = in1.getShape();
+ const Shape& in2_shape = in2.getShape();
+ assert(in2_shape.getDims() == 1 && in2_shape[0] == in1_shape[in1_shape.getDims() - 1]);
+ out.reShape(in1_shape);
- const float *wData = weights.data;
- const float *inData = in.getData();
- float *outData = out.getData();
- int32_t dataSize = inShape.getNumElems();
+ const float* in1_data = in1.getData();
+ const float* in2_data = in2.getData();
+ float* out_data = out.getData();
+ int32_t w_size = in2_shape[0];
+ int32_t data_size = in1_shape.getNumElems();
- assert(dataSize % wSize == 0);
- for (int32_t sliceOffset = 0; sliceOffset < dataSize; sliceOffset += wSize)
- {
- for (int i = 0; i < wSize; i++)
- {
- outData[sliceOffset + i] = inData[sliceOffset + i] * wData[i];
+ assert(data_size % w_size == 0);
+ for (int32_t slice_offset = 0; slice_offset < data_size; slice_offset += w_size) {
+ for (int i = 0; i < w_size; i++) {
+ out_data[slice_offset + i] = in1_data[slice_offset + i] * in2_data[i];
}
}
}
auto result = createOp<ops::Conv2DOp>(inputs[0], params[0],
strides, padding_before, padding_after);
- result = createOp<ops::BiasAddOp>(result->getOutput(0), params[1]);
+ auto bias = createOp<ops::ConstantOp>(params[1]);
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), bias->getOutput(0));
return {addFusedActivation(result->getOutput(0), opts->fused_activation_function())};
}
auto result = createOp<ops::DepthwiseConv2DOp>(inputs[0], params[0],
strides, padding_before, padding_after);
- result = createOp<ops::BiasAddOp>(result->getOutput(0), params[1]);
+ auto bias = createOp<ops::ConstantOp>(params[1]);
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), bias->getOutput(0));
return {addFusedActivation(result->getOutput(0), opts->fused_activation_function())};
}
auto flatten = createOp<ops::ReshapeOp>(inputs[0], Shape{1, fc_input_size});
auto result = createOp<ops::FullyConnectedOp>(flatten->getOutput(0), params[0]);
- result = createOp<ops::BiasAddOp>(result->getOutput(0), params[1]);
+ auto bias = createOp<ops::ConstantOp>(params[1]);
+ result = createOp<ops::BiasAddOp>(result->getOutput(0), bias->getOutput(0));
return {addFusedActivation(result->getOutput(0), opts->fused_activation_function())};
}
'RESHAPE': ('shapes',),
'RELU': (),
'CAPPED_RELU': ('axis',),
- 'BIAS_ADD': ('kernels',),
+ 'BIAS_ADD': (),
'SOFTMAX': ('axis',)
}
return [tf.maximum(0.0, tf.minimum(self._inputs[0], self.axis)).eval()]
def get_bias_add_result(self):
- return [tf.add(self._inputs[0], self._kernels[0]).eval()]
+ return [tf.add(self._inputs[0], self._inputs[1]).eval()]
def get_softmax_result(self):
return [tf.nn.softmax(self._inputs[0], self.axis).eval()]
static Operation* createBiasAdd(std::unique_ptr<Graph>& g,
const std::vector<IODescriptor>& inputs,
const opinfo::OperatorInfo* opInfo) {
- return g->create<ops::BiasAddOp>("y", inputs[0], *getKernel(opInfo));
+ (void)opInfo;
+ return g->create<ops::BiasAddOp>("y", inputs[0], inputs[1]);
}
static Operation* createOp(std::unique_ptr<Graph>& g,
[4, 4, 4] 2
BIAS_ADD
-[6, 7, 8] [8]
+[[6, 7, 8] [8]]
SOFTMAX
[6, 5, 20] 0
Graph g;
OpConstructor op_generator = [&w](Graph& g, const vector<IODescriptor>& inputs) {
- return g.create<mir::ops::BiasAddOp>("bias", inputs[0], w);
+ auto bias = g.create<mir::ops::ConstantOp>("", w)->getOutput(0);
+ return g.create<mir::ops::BiasAddOp>("bias", inputs[0], bias);
};
vector<Shape> input_shapes{{1, 10, 10, channels}};
Graph g;
OpConstructor op_generator = [&w](Graph& g, const vector<IODescriptor>& inputs) {
- return g.create<mir::ops::ScaleOp>("scale", inputs[0], w);
+ auto scale = g.create<mir::ops::ConstantOp>("", w)->getOutput(0);
+ return g.create<mir::ops::ScaleOp>("scale", inputs[0], scale);
};
vector<Shape> input_shapes{{1, 10, 10, channels}};
TEST(cpp_operations_test, bias) {
vector<int> input_shape_data{2, 3, 4, 5};
- mir::Shape weights_shape{5};
- vector<unique_ptr<mir::TensorVariant>> input_ntensors(1);
- Tensor input_atensor;
- fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f);
+ vector<int> weights_shape_data{5};
+ vector<unique_ptr<mir::TensorVariant>> input_ntensors(2);
+ Tensor input_atensor0;
+ Tensor input_atensor1;
+ fillTensors(input_ntensors[0], input_atensor0, input_shape_data, 1.0f);
+ fillTensors(input_ntensors[1], input_atensor1, weights_shape_data, 1.0f);
- mir::TensorVariant weights = createNTensor(weights_shape, 1.0f);
- auto op_generator = [&weights](mir::Graph& g, const std::vector<mir::IODescriptor>& inputs) {
- return g.create<mir::ops::BiasAddOp>("y", inputs[0], weights);
+ auto op_generator = [](mir::Graph& g, const std::vector<mir::IODescriptor>& inputs) {
+ return g.create<mir::ops::BiasAddOp>("y", inputs[0], inputs[1]);
};
- createAndRunTestGraph(op_generator, biasAdd, input_ntensors, input_atensor);
+ createAndRunTestGraph(op_generator, biasAdd, input_ntensors, input_atensor0, input_atensor1);
}
TEST(cpp_operations_test, scale) {
vector<int> input_shape_data{2, 3, 4, 5};
- mir::Shape weights_shape{5};
- vector<unique_ptr<mir::TensorVariant>> input_ntensors(1);
- Tensor input_atensor;
- fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f);
+ vector<int> weights_shape_data{5};
+ vector<unique_ptr<mir::TensorVariant>> input_ntensors(2);
+ Tensor input_atensor0;
+ Tensor input_atensor1;
+ fillTensors(input_ntensors[0], input_atensor0, input_shape_data, 1.0f);
+ fillTensors(input_ntensors[1], input_atensor1, weights_shape_data, 1.0f);
- mir::TensorVariant weights = createNTensor(weights_shape, 1.0f);
- auto op_generator = [&weights](mir::Graph& g, const std::vector<mir::IODescriptor>& inputs) {
- return g.create<mir::ops::ScaleOp>("y", inputs[0], weights);
+ auto op_generator = [](mir::Graph& g, const std::vector<mir::IODescriptor>& inputs) {
+ return g.create<mir::ops::ScaleOp>("y", inputs[0], inputs[1]);
};
- createAndRunTestGraph(op_generator, scale, input_ntensors, input_atensor);
+ createAndRunTestGraph(op_generator, scale, input_ntensors, input_atensor0, input_atensor1);
}
TEST(cpp_operations_test, capped_relu) {