nntrainer icon indicating copy to clipboard operation
nntrainer copied to clipboard

[Wait for #2710][Layer] add "subtract layer" @open sesame 10/11 15:45

Open baek2sm opened this issue 1 year ago • 10 comments

  • added "subtract layer"

Self evaluation:

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

Signed-off-by: Seungbaek Hong [email protected]

baek2sm avatar Aug 30 '24 05:08 baek2sm

:memo: TAOS-CI Version: 1.5.20200925. Thank you for submitting PR #2724. Please a submit 1commit/1PR (one commit per one PR) policy to get comments quickly from reviewers. Your PR must pass all verificiation processes of cibot before starting a review process from reviewers. If you are new member to join this project, please read manuals in documentation folder and wiki page. In order to monitor a progress status of your PR in more detail, visit http://ci.nnstreamer.ai/.

taos-ci avatar Aug 30 '24 05:08 taos-ci

The add and sub layers have almost identical code. How about creating a basic operation class? It would be simple if the add and sub inherited from the basic operation class. Multiplication and division also seem to have the same structure.

That sounds good. Let's think together to find an improved structure.

baek2sm avatar Sep 02 '24 11:09 baek2sm

Nice work! One minor suggestion is to rename it SubtractLayer (SubLayer sounds like a subunit of a layer). What do you think?

That's a good idea. I'll rename it! (and the other, too)

baek2sm avatar Sep 02 '24 11:09 baek2sm

The add and sub layers have almost identical code. How about creating a basic operation class? It would be simple if the add and sub inherited from the basic operation class. Multiplication and division also seem to have the same structure.

I also think same way. How about unary and binary layer.

jijoongmoon avatar Sep 03 '24 00:09 jijoongmoon

The add and sub layers have almost identical code. How about creating a basic operation class? It would be simple if the add and sub inherited from the basic operation class. Multiplication and division also seem to have the same structure.

That sounds good. Let's think together to find an improved structure.

How about creating the operation layer like this?

// operation_layer.h

#pragma once    

#include <tensor.h>    

namespace nntrainer {    

template <typename OP>    
class OperationLayer {    
public:    
  virtual ~OperationLayer() = default;    

protected:    
  OperationLayer() = default;    

  virtual void finalize(InitLayerContext &context) = 0;    

  virtual void forwarding(RunLayerContext &context, bool training) = 0;    

  virtual void incremental_forwarding(RunLayerContext &context,    
                                      unsigned int from, unsigned int to,    
                                      bool training) = 0;    

  virtual void calcDerivative(RunLayerContext &context) = 0;    

  virtual void setProperty(const std::vector<std::string> &values) = 0;    

  
void commonForwarding(RunLayerContext &context, bool training,    
                       const Tensor &input0, const Tensor &input1,    
                       Tensor &hidden_);    

// Implementing functions for common codes
};    

template <>    
struct OperationLayer<ADDITION> {    
  static constexpr const char *type = "add";    
};    

template <>    
struct OperationLayer<SUBTRACTION> {    
  static constexpr const char *type = "sub";    
};    

template <>    
struct OperationLayer<MULTIPLICATION> {    
  static constexpr const char *type = "mul";    
};    

template <>    
struct OperationLayer<DIVISION> {    
  static constexpr const char *type = "div";    
};    

} // namespace nntrainer    
// operation_layer.cpp

template <typename OP>  
void OperationLayer<OP>::commonForwarding(RunLayerContext& context, bool training,  
                                          const Tensor& input0, const Tensor& input1, Tensor& hidden_) {  
  switch (OP::type) {  
    case "add":  
      input0.add(input1, hidden_);  
      break;  
    case "sub":  
      input0.subtract(input1, hidden_);  
      break;  
    case "mul":  
      input0.multiply(input1, hidden_);  
      break;  
    case "div": 
      input0.divide(input1, hidden_);  
      break;  
    default:  
      throw std::runtime_error("Invalid operator type");  
  }  
}  

// implementation of other common function

SeoHyungjun avatar Sep 05 '24 04:09 SeoHyungjun

I think we can create an add layer like this using the operation layer.

// add_layer.h
#pragma once  

#include "operation_layer.h"  

namespace nntrainer {  

class AddLayer : public OperationLayer<ADDITION> {  
public:  
  using OperationLayer<ADDITION>::OperationLayer;  

  void finalize(InitLayerContext &context) override;  

  void forwarding(RunLayerContext &context, bool training) override;  

  void incremental_forwarding(RunLayerContext &context, unsigned int from,  
                              unsigned int to, bool training) override;  

  void calcDerivative(RunLayerContext &context) override;  

  void setProperty(const std::vector<std::string> &values) override;  
};  

} // namespace nntrainer  
//add_layer.cpp

#include "add_layer.h"  

namespace nntrainer {  

void AddLayer::finalize(InitLayerContext &context) {  
  // Add Layer specific initialization code goes here...  
}  

void AddLayer::forwarding(RunLayerContext &context, bool training) {  
  commonForwarding(context, training, input0, input1, hidden_);  
}  

void AddLayer::incremental_forwarding(RunLayerContext &context, unsigned int from,  
                                      unsigned int to, bool training) {  
  // Add Layer specific incremental forward propagation code goes here...  
}  

void AddLayer::calcDerivative(RunLayerContext &context) {  
  // Add Layer specific backpropagation code goes here...  
}  

void AddLayer::setProperty(const std::vector<std::string> &values) {  
  // Add Layer specific property setting code goes here...  
}  

} // namespace nntrainer 

There is a lot of overlap in the currently implemented code. It seems that all other functions can be expressed simply, just like using commonForwarding in the forwarding function.

If each function requires completely different implementation codes, it may be possible to implement its own code rather than a common function in each class.

SeoHyungjun avatar Sep 05 '24 04:09 SeoHyungjun

Great suggestion @SeoHyungjun ! Upon his suggestion, what about using enum for operation type? If more operations are added, the switch clause may not work only with the first character. Also it can improve readability :)

EunjuYang avatar Sep 05 '24 11:09 EunjuYang

Great suggestion @SeoHyungjun ! Upon his suggestion, what about using enum for operation type? If more operations are added, the switch clause may not work only with the first character. Also it can improve readability :)

Good point, @EunjuYang!! As you mentioned, using enums would be beneficial for readability and maintenance purposes!

I have written down the code with added enums below.

// operation_layer.h  

#pragma once      

#include <tensor.h>      

namespace nntrainer {      

enum class OpType {  
  ADDITION,  
  SUBTRACTION,  
  MULTIPLICATION,  
  DIVISION  
};  

template <OpType op_type>      
class OperationLayer {      
public:      
  virtual ~OperationLayer() = default;      

protected:      
  OperationLayer() = default;      

  virtual void finalize(InitLayerContext &context) = 0;      

  virtual void forwarding(RunLayerContext &context, bool training) = 0;      

  virtual void incremental_forwarding(RunLayerContext &context,      
                                      unsigned int from, unsigned int to,      
                                      bool training) = 0;      

  virtual void calcDerivative(RunLayerContext &context) = 0;      

  virtual void setProperty(const std::vector<std::string> &values) = 0;      

    
void commonForwarding(RunLayerContext &context, bool training,      
                       const Tensor &input0, const Tensor &input1,      
                       Tensor &hidden_);      

// Implementing functions for common codes  
};      

template <>      
struct OperationLayer<OpType::ADDITION> {      
  static constexpr const char *type = "add";      
};      

template <>      
struct OperationLayer<OpType::SUBTRACTION> {      
  static constexpr const char *type = "sub";      
};      

template <>      
struct OperationLayer<OpType::MULTIPLICATION> {      
  static constexpr const char *type = "mul";      
};      

template <>      
struct OperationLayer<OpType::DIVISION> {      
  static constexpr const char *type = "div";      
};      

} // namespace nntrainer      
// operation_layer.cpp  

template <OpType op_type>    
void OperationLayer<op_type>::commonForwarding(RunLayerContext& context, bool training,    
                                              const Tensor& input0, const Tensor& input1, Tensor& hidden_) {    
  switch (op_type) {    
    case OpType::ADDITION:    
      input0.add(input1, hidden_);    
      break;    
    case OpType::SUBTRACTION:    
      input0.subtract(input1, hidden_);    
      break;    
    case OpType::MULTIPLICATION:    
      input0.multiply(input1, hidden_);    
      break;    
    case OpType::DIVISION:   
      input0.divide(input1, hidden_);    
      break;    
    default:    
      throw std::runtime_error("Invalid operator type");    
  }    
}    

// implementation of other common function  
// add_layer.h  

#pragma once    

#include "operation_layer.h"    

namespace nntrainer {    

class AddLayer : public OperationLayer<OpType::ADDITION> {    
public:    
  using OperationLayer<OpType::ADDITION>::OperationLayer;    

  void finalize(InitLayerContext &context) override;    

  void forwarding(RunLayerContext &context, bool training) override;    

  void incremental_forwarding(RunLayerContext &context, unsigned int from,    
                              unsigned int to, bool training) override;    

  void calcDerivative(RunLayerContext &context) override;    

  void setProperty(const std::vector<std::string> &values) override;    
};    

} // namespace nntrainer     
//add_layer.cpp  

#include "add_layer.h"    

namespace nntrainer {    

void AddLayer::finalize(InitLayerContext &context) {    
  // Add Layer specific initialization code goes here...    
}    

void AddLayer::forwarding(RunLayerContext &context, bool training) {    
  commonForwarding(context, training, input0, input1, hidden_);    
}    

void AddLayer::incremental_forwarding(RunLayerContext &context, unsigned int from,    
                                      unsigned int to, bool training) {    
  // Add Layer specific incremental forward propagation code goes here...    
}    

void AddLayer::calcDerivative(RunLayerContext &context) {    
  // Add Layer specific backpropagation code goes here...    
}    

void AddLayer::setProperty(const std::vector<std::string> &values) {    
  // Add Layer specific property setting code goes here...    
}    

} // namespace nntrainer   

SeoHyungjun avatar Sep 05 '24 12:09 SeoHyungjun

@SeoHyungjun It's a good suggestion. I'll try to reflect on it. Thank you!

baek2sm avatar Sep 11 '24 02:09 baek2sm

I've completed the revisions after incorporating feedbacks. Thanks a lot!

baek2sm avatar Oct 10 '24 08:10 baek2sm

:octocat: cibot: @baek2sm, A builder checker could not be completed because one of the checkers is not completed. In order to find out a reason, please go to http://ci.nnstreamer.ai/nntrainer/ci/repo-workers/pr-checker/2724-202411061131060.035284996032715-13091f071b159bb56d9d5e3b71eefb7a4805066e/.

taos-ci avatar Nov 06 '24 03:11 taos-ci

:octocat: cibot: @baek2sm, A builder checker could not be completed because one of the checkers is not completed. In order to find out a reason, please go to http://ci.nnstreamer.ai/nntrainer/ci/repo-workers/pr-checker/2724-202411061645350.87372088432312-f3aad560a9d01bba11c3cfc53c0dee8b2ad54f7d/.

taos-ci avatar Nov 06 '24 08:11 taos-ci