Skip to content

Commit

Permalink
[Layer] add "divide layer"
Browse files Browse the repository at this point in the history
- added "divide layer" for division.

**Self evaluation:**
1. Build test:   [X]Passed [ ]Failed [ ]Skipped
2. Run test:     [X]Passed [ ]Failed [ ]Skipped

Signed-off-by: Seungbaek Hong <sb92.hong@samsung.com>
  • Loading branch information
baek2sm committed Oct 14, 2024
1 parent 245e108 commit 195a043
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 0 deletions.
9 changes: 9 additions & 0 deletions api/ccapi/include/layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ enum LayerType {
LAYER_ADD = ML_TRAIN_LAYER_TYPE_ADD, /**< Add Layer type */
LAYER_SUBTRACT = ML_TRAIN_LAYER_TYPE_SUBTRACT, /**< Subtract Layer type */
LAYER_MULTIPLY = ML_TRAIN_LAYER_TYPE_MULTIPLY, /**< Multiply Layer type */
LAYER_DIVIDE = ML_TRAIN_LAYER_TYPE_DIVIDE, /**< Divide Layer type */
LAYER_FC = ML_TRAIN_LAYER_TYPE_FC, /**< Fully Connected Layer type */
LAYER_SWIGLU = ML_TRAIN_LAYER_TYPE_SWIGLU, /**< Swiglu Layer type */
LAYER_BN = ML_TRAIN_LAYER_TYPE_BN, /**< Batch Normalization Layer type */
Expand Down Expand Up @@ -332,6 +333,14 @@ MultiplyLayer(const std::vector<std::string> &properties = {}) {
return createLayer(LayerType::LAYER_MULTIPLY, properties);
}

/**
* @brief Helper function to create divide layer
*/
inline std::unique_ptr<Layer>
DivideLayer(const std::vector<std::string> &properties = {}) {
return createLayer(LayerType::LAYER_DIVIDE, properties);
}

/**
* @brief Helper function to create fully connected layer
*/
Expand Down
1 change: 1 addition & 0 deletions api/nntrainer-api-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ typedef enum {
ML_TRAIN_LAYER_TYPE_ADD = 32, /**< Add Layer type (Since 9.0)*/
ML_TRAIN_LAYER_TYPE_SUBTRACT = 33, /**< Subtract Layer type (Since 9.0)*/
ML_TRAIN_LAYER_TYPE_MULTIPLY = 34, /**< Multiply Layer type (Since 9.0)*/
ML_TRAIN_LAYER_TYPE_DIVIDE = 35, /**< Divide Layer type (Since 9.0)*/
ML_TRAIN_LAYER_TYPE_PREPROCESS_FLIP =
300, /**< Preprocess flip Layer (Since 6.5) */
ML_TRAIN_LAYER_TYPE_PREPROCESS_TRANSLATE =
Expand Down
3 changes: 3 additions & 0 deletions nntrainer/app_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <conv2d_layer.h>
#include <cross_entropy_sigmoid_loss_layer.h>
#include <cross_entropy_softmax_loss_layer.h>
#include <divide_layer.h>
#include <dropout.h>
#include <embedding.h>
#include <fc_layer.h>
Expand Down Expand Up @@ -257,6 +258,8 @@ static void add_default_object(AppContext &ac) {
LayerType::LAYER_SUBTRACT);
ac.registerFactory(nntrainer::createLayer<MultiplyLayer>, MultiplyLayer::type,
LayerType::LAYER_MULTIPLY);
ac.registerFactory(nntrainer::createLayer<DivideLayer>, DivideLayer::type,
LayerType::LAYER_DIVIDE);
ac.registerFactory(nntrainer::createLayer<FullyConnectedLayer>,
FullyConnectedLayer::type, LayerType::LAYER_FC);
ac.registerFactory(nntrainer::createLayer<BatchNormalizationLayer>,
Expand Down
53 changes: 53 additions & 0 deletions nntrainer/layers/divide_layer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Copyright (C) 2024 SeungBaek Hong <sb92.hong@samsung.com>
*
* @file divide_layer.cpp
* @date 10 Oct 2024
* @see https://github.com/nnstreamer/nntrainer
* @author SeungBaek Hong <sb92.hong@samsung.com>
* @bug No known bugs except for NYI items
* @brief This is div layer class (operation layer)
*
*/

#include <divide_layer.h>
#include <nntrainer_error.h>
#include <nntrainer_log.h>
#include <node_exporter.h>
#include <util_func.h>

#include <layer_context.h>

namespace nntrainer {

void DivideLayer::finalize(InitLayerContext &context) {
op_type = OperationType::BINARY;
context.setOutputDimensions({context.getInputDimensions()[0]});
}

void DivideLayer::forwarding_operation(const Tensor &input0,
const Tensor &input1, Tensor &hidden) {
input0.divide(input1, hidden);
}

void DivideLayer::calcDerivative(RunLayerContext &context) {
context.getOutgoingDerivative(0).copy(
context.getIncomingDerivative(SINGLE_INOUT_IDX)
.divide(context.getInput(1)));

context.getOutgoingDerivative(1).copy(
context.getIncomingDerivative(SINGLE_INOUT_IDX)
.multiply(context.getInput(0).multiply(-1))
.divide(context.getInput(1).pow(2)));
}

void DivideLayer::setProperty(const std::vector<std::string> &values) {
auto remain_props = loadProperties(values, divide_props);
if (!remain_props.empty()) {
std::string msg = "[DivideLayer] Unknown Layer Properties count " +
std::to_string(values.size());
throw exception::not_supported(msg);
}
}
} /* namespace nntrainer */
108 changes: 108 additions & 0 deletions nntrainer/layers/divide_layer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Copyright (C) 2024 SeungBaek Hong <sb92.hong@samsung.com>
*
* @file divide_layer.h
* @date 10 Oct 2024
* @see https://github.com/nnstreamer/nntrainer
* @author SeungBaek Hong <sb92.hong@samsung.com>
* @bug No known bugs except for NYI items
* @brief This is div layer class (operation layer)
*
*/

#ifndef __DIVIDE_LAYER_H__
#define __DIVIDE_LAYER_H__
#ifdef __cplusplus

#include <common_properties.h>
#include <layer_devel.h>
#include <operation_layer.h>

namespace nntrainer {

/**
* @class Divide Layer
* @brief Divide Layer
*/
class DivideLayer : public OperationLayer {
public:
/**
* @brief Constructor of Divide Layer
*/
DivideLayer() : OperationLayer(), divide_props(props::Print()) {}

/**
* @brief Destructor of Divide Layer
*/
~DivideLayer(){};

/**
* @brief Move constructor of Divide Layer.
* @param[in] DivideLayer &&
*/
DivideLayer(DivideLayer &&rhs) noexcept = default;

/**
* @brief Move assignment operator.
* @parma[in] rhs DivideLayer to be moved.
*/
DivideLayer &operator=(DivideLayer &&rhs) = default;

/**
* @copydoc Layer::finalize(InitLayerContext &context)
*/
void finalize(InitLayerContext &context) final;

/**
* @copydoc OperationLayer::forwarding_operation(const Tensor &input, Tensor
* &hidden)
*/
void forwarding_operation(const Tensor &input, Tensor &hidden) final{};

/**
* @brief forwarding operation for add
*
* @param input0 input tensor 0
* @param input1 input tensor 1
* @param hidden tensor to store the result of addition
*/
void forwarding_operation(const Tensor &input0, const Tensor &input1,
Tensor &hidden) final;

/**
* @copydoc Layer::calcDerivative(RunLayerContext &context)
*/
void calcDerivative(RunLayerContext &context) final;

/**
* @copydoc bool supportBackwarding() const
*/
bool supportBackwarding() const final { return true; };

/**
* @copydoc Layer::exportTo(Exporter &exporter, ml::train::ExportMethods
* method)
*/
void exportTo(Exporter &exporter,
const ml::train::ExportMethods &method) const final {}

/**
* @copydoc Layer::setProperty(const std::vector<std::string> &values)
*/
void setProperty(const std::vector<std::string> &values) final;

/**
* @copydoc Layer::getType()
*/
const std::string getType() const final { return DivideLayer::type; };

std::tuple<props::Print> divide_props;

inline static const std::string type = "divide";
};

} // namespace nntrainer

#endif /* __cplusplus */
#endif /* __DIVIDE_LAYER_H__ */
1 change: 1 addition & 0 deletions nntrainer/layers/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ layer_sources = [
'add_layer.cpp',
'subtract_layer.cpp',
'multiply_layer.cpp',
'divide_layer.cpp',
'addition_layer.cpp',
'attention_layer.cpp',
'mol_attention_layer.cpp',
Expand Down
13 changes: 13 additions & 0 deletions test/input_gen/genModelTests_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,19 @@ def forward(self, inputs, labels):
return out, loss


class DivideOperation(torch.nn.Module):
def __init__(self):
super().__init__()
self.fc = torch.nn.Linear(2, 2)
self.loss = torch.nn.MSELoss()

def forward(self, inputs, labels):
out = self.fc(inputs[0])
out = inputs[0] / out
loss = self.loss(out, labels[0])
return out, loss


if __name__ == "__main__":
record_v2(
ReduceMeanLast(),
Expand Down
1 change: 1 addition & 0 deletions test/unittest/layers/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ test_target = [
'unittest_layers_add.cpp',
'unittest_layers_subtract.cpp',
'unittest_layers_multiply.cpp',
'unittest_layers_divide.cpp',
'unittest_layers_multiout.cpp',
'unittest_layers_rnn.cpp',
'unittest_layers_rnncell.cpp',
Expand Down
28 changes: 28 additions & 0 deletions test/unittest/layers/unittest_layers_divide.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Copyright (C) 2024 SeungBaek Hong <sb92.hong@samsung.com>
*
* @file unittest_layers_divide.cpp
* @date 30 August 2024
* @brief Divide Layer Test
* @see https://github.com/nnstreamer/nntrainer
* @author SeungBaek Hong <sb92.hong@samsung.com>
* @bug No known bugs except for NYI items
*/
#include <tuple>

#include <gtest/gtest.h>

#include <divide_layer.h>
#include <layers_common_tests.h>

auto semantic_divide = LayerSemanticsParamType(
nntrainer::createLayer<nntrainer::DivideLayer>, nntrainer::DivideLayer::type,
{}, LayerCreateSetPropertyOptions::AVAILABLE_FROM_APP_CONTEXT, false, 1);

auto semantic_divide_multi = LayerSemanticsParamType(
nntrainer::createLayer<nntrainer::DivideLayer>, nntrainer::DivideLayer::type,
{}, LayerCreateSetPropertyOptions::AVAILABLE_FROM_APP_CONTEXT, false, 2);

GTEST_PARAMETER_TEST(Divide, LayerSemantics,
::testing::Values(semantic_divide, semantic_divide_multi));
21 changes: 21 additions & 0 deletions test/unittest/models/unittest_models.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,25 @@ static std::unique_ptr<NeuralNetwork> makeMultiplyOperation() {
return nn;
}

static std::unique_ptr<NeuralNetwork> makeDivideOperation() {
std::unique_ptr<NeuralNetwork> nn(new NeuralNetwork());

auto outer_graph =
makeGraph({{"input", {"name=in", "input_shape=1:1:2"}},
{"fully_connected", {"name=fc", "unit=2", "input_layers=in"}},
{"divide", {"name=divide_layer", "input_layers=in,fc"}},
{"mse", {"name=loss", "input_layers=divide_layer"}}});

for (auto &node : outer_graph) {
nn->addLayer(node);
}

nn->setProperty({"batch_size=1"});
nn->setOptimizer(ml::train::createOptimizer("sgd", {"learning_rate=0.1"}));

return nn;
}

GTEST_PARAMETER_TEST(
model, nntrainerModelTest,
::testing::ValuesIn({
Expand Down Expand Up @@ -1005,6 +1024,8 @@ GTEST_PARAMETER_TEST(
ModelTestOption::ALL_V2),
mkModelTc_V2(makeMultiplyOperation, "multiply_operation",
ModelTestOption::ALL_V2),
mkModelTc_V2(makeDivideOperation, "divide_operation",
ModelTestOption::ALL_V2),
}),
[](const testing::TestParamInfo<nntrainerModelTest::ParamType> &info)
-> const auto & { return std::get<1>(info.param); });
Expand Down

0 comments on commit 195a043

Please sign in to comment.