赞
踩
relu是一个非线性激活函数,可以避免梯度消失,过拟合等情况。我们一般将thresh设为0。
- #ifndef KUIPER_COURSE_INCLUDE_OPS_OP_HPP_
- #define KUIPER_COURSE_INCLUDE_OPS_OP_HPP_
- namespace kuiper_infer {
- enum class OpType {
- kOperatorUnknown = -1,
- kOperatorRelu = 0,
- };
-
- class Operator {
- public:
- OpType op_type_ = OpType::kOperatorUnknown; //不是一个具体节点 制定为unknown
-
- virtual ~Operator() = default; //
-
- explicit Operator(OpType op_type);
- };
这里的 kOperatorUnknown = -1 , kOperatorRelu = 0分别是他们的代号
operator是一个父类,我们的relu就要继承于这个父类
- class ReluOperator : public Operator {
- public:
- ~ReluOperator() override = default;
-
- explicit ReluOperator(float thresh);
-
- void set_thresh(float thresh);
-
- float get_thresh() const;
-
- private:
- // 需要传递到reluLayer中,怎么传递?
- float thresh_ = 0.f; // 用于过滤tensor<float>值当中大于thresh的部分
- // relu存的变量只有thresh
- // stride padding kernel_size 这些是到时候convOperator需要的
- // operator起到了属性存储、变量的作用
- // operator所有子类不负责具体运算
- // 具体运算由另外一个类Layer类负责
- // y =x , if x >=0 y = 0 if x < 0
-
- };
operator起到了属性存储、变量的作用
operator所有子类不负责具体运算
具体运算由另外一个类Layer类负责
- class Layer {
- public:
- explicit Layer(const std::string &layer_name);
-
- virtual void Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
- std::vector<std::shared_ptr<Tensor<float>>> &outputs);
- // reluLayer中 inputs 等于 x , outputs 等于 y= x,if x>0
- // 计算得到的结果放在y当中,x是输入,放在inputs中
-
- virtual ~Layer() = default;
- private:
- std::string layer_name_; //relu layer "relu"
- };
父类只保留了一个layer_name属性和两个方法。
具体的在relu_layer这个class中
- class ReluLayer : public Layer {
- public:
- ~ReluLayer() override = default;
-
- // 通过这里,把relu_op中的thresh告知给relu layer, 因为计算的时候要用到
- explicit ReluLayer(const std::shared_ptr<Operator> &op);
-
- // 执行relu 操作的具体函数Forwards
- void Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
- std::vector<std::shared_ptr<Tensor<float>>> &outputs) override;
-
- // 下节的内容,不用管
- static std::shared_ptr<Layer> CreateInstance(const std::shared_ptr<Operator> &op);
-
- private:
- std::unique_ptr<ReluOperator> op_;
- };
具体的方法实现:
- ReluLayer::ReluLayer(const std::shared_ptr<Operator> &op) : Layer("Relu") {
- CHECK(op->op_type_ == OpType::kOperatorRelu) << "Operator has a wrong type: " << int(op->op_type_);
- // dynamic_cast是什么意思? 就是判断一下op指针是不是指向一个relu_op类的指针
- // 这边的op不是ReluOperator类型的指针,就报错
- // 我们这里只接受ReluOperator类型的指针
- // 父类指针必须指向子类ReluOperator类型的指针
- // 为什么不讲构造函数设置为const std::shared_ptr<ReluOperator> &op?
- // 为了接口统一,具体下节会说到
- ReluOperator *relu_op = dynamic_cast<ReluOperator *>(op.get());
-
- CHECK(relu_op != nullptr) << "Relu operator is empty";
- // 一个op实例和一个layer 一一对应 这里relu op对一个relu layer
- // 对应关系
- this->op_ = std::make_unique<ReluOperator>(relu_op->get_thresh());
- }
-
- void ReluLayer::Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
- std::vector<std::shared_ptr<Tensor<float>>> &outputs) {
- // relu 操作在哪里,这里!
- // 我需要该节点信息的时候 直接这么做
- // 实行了属性存储和运算过程的分离!!!!!!!!!!!!!!!!!!!!!!!!
- //x就是inputs y = outputs
- CHECK(this->op_ != nullptr);
- CHECK(this->op_->op_type_ == OpType::kOperatorRelu);
-
- const uint32_t batch_size = inputs.size(); //一批x,放在vec当中,理解为batchsize数量的tensor,需要进行relu操作
- for (int i = 0; i < batch_size; ++i) {
-
- CHECK(!inputs.at(i)->empty());
- const std::shared_ptr<Tensor<float>> &input_data = inputs.at(i); //取出批次当中的一个张量
-
- //对张量中的每一个元素进行运算,进行relu运算
- input_data->data().transform([&](float value) {
- // 对张良中的没一个元素进行运算
- // 从operator中得到存储的属性
- float thresh = op_->get_thresh();
- //x >= thresh
- if (value >= thresh) {
- return value; // return x
- } else {
- // x<= thresh return 0.f;
- return 0.f;
- }
- });
-
- // 把结果y放在outputs中
- outputs.push_back(input_data);
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。