从零构建深度学习推理框架-3 手写算子relu

news2024/12/23 17:21:05

Relu介绍:

f(x) = \left\{\begin{matrix}x , x>thresh & & \\0,x<thresh & & \end{matrix}\right.

 relu是一个非线性激活函数,可以避免梯度消失,过拟合等情况。我们一般将thresh设为0。

operator类:

#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类负责

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);
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/828562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

websocket服务端大报文发送连接自动断开分析

概述 当前springboot版本&#xff1a;2.7.4 使用依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency>现象概述&#xff1a; 客户端和服务端已经有心跳…

拿捏--->杨辉三角

文章目录 题目描述算法思路代码示例精简版优化版 题目描述 在屏幕上面打印杨辉三角。 算法思路 杨辉三角&#xff0c;是二项式系数在三角形中的一种几何排列。在欧洲&#xff0c;这个表叫做帕斯卡三角形。帕斯卡&#xff08;1623----1662&#xff09;是在1654年发现这一规律…

《金融数据保护治理白皮书》发布(137页)

温馨提示&#xff1a;文末附完整PDF下载链接 导读 目前业界已出台数据保护方面的治理模型&#xff0c;但围绕金融数据保护治理的实践指导等尚不成熟&#xff0c;本课题围绕数据保护治理的金融实践、发展现状&#xff0c;探索和标准化相关能力要求&#xff0c;归纳总结相关建…

AI算法图形化编程加持|OPT(奥普特)智能相机轻松适应各类检测任务

OPT&#xff08;奥普特&#xff09;基于SciVision视觉开发包&#xff0c;全新推出多功能一体化智能相机&#xff0c;采用图形化编程设计&#xff0c;操作简单、易用&#xff1b;不仅有上百种视觉检测算法加持&#xff0c;还支持深度学习功能&#xff0c;能轻松应对计数、定位、…

电压放大器工作在什么状态

电压放大器是一种广泛应用于电子电路中的基本电路元件&#xff0c;其主要功能是将输入信号的电压放大到所需的输出电压幅值&#xff0c;并且保持信号的形状不变。在实际电路设计中&#xff0c;电压放大器的工作状态会受到多种因素的影响&#xff0c;比如输入信号的频率、放大倍…

em3288 linux_4.19 sd卡调试

默认配置&#xff0c;根据实际配置即可。

深度学习——常见注意力机制

1.SENet SENet属于通道注意力机制。2017年提出&#xff0c;是imageNet最后的冠军 SENet采用的方法是对于特征层赋予权值。 重点在于如何赋权 1.将输入信息的所有通道平均池化。 2.平均池化后进行两次全连接&#xff0c;第一次全连接链接的神经元较少&#xff0c;第二次全连…

基于图像形态学处理的停车位检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1. 图像预处理 4.2. 车辆定位 4.3. 停车位检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ......................................…

python中for..in语法的原理?

今天发现了python中一个比较有意思的小设计。 如果要通过中括号[]访问元素&#xff0c;需要重写__get__item()函数&#xff0c;这个应该没什么疑问&#xff0c;测试代码如下&#xff1a; class Classroom:def __init__(self, students):self.students studentsdef __getitem…

为什么程序员每到一家新公司干了两三年,都有一种干不下去的感觉?

行业内有句话叫&#xff1a;“程序员跳一次等于干三年”。但是程序员这个岗位怎么说呢&#xff1f; 小伙伴都知道的&#xff0c;工作强度完全看运气&#xff0c;有的公司忙到头都秃了&#xff0c;也有的公司闲到抠脚。 而收入呢?在一家公司待着&#xff0c;基本上是万年不涨的…

【C语言初阶(19)】实用的 VS 调试技巧

文章目录 Ⅰ 调试的介绍Ⅱ 常用调试快捷键Ⅲ 调试的时候查看程序当前信息⒈查看临时变量的值⒉查看内存信息⒊查看调用堆栈⒋查看汇编信息⒌查看寄存器信息 Ⅳ 观察形参指针指向的数组Ⅴ 易于调试的代码该如何编写⒈const 修饰指针变量⒉良好代码示范 Ⅵ 编程中常见的错误 Ⅰ 调…

Mr. Cappuccino的第54杯咖啡——Mybatis运行原理

Mybatis运行原理 Mybatis运行的三个阶段Mybatis运行原理图 Mybatis运行的三个阶段 初始化阶段&#xff1a;读取并解析XML配置文件和注解中的配置信息&#xff0c;创建配置对象&#xff0c;并完成各个模块的初始化工作&#xff0c;底层采用建造者模式&#xff1b;代理封装阶段&…

Qt、C/C++环境中内嵌LUA脚本、实现LUA函数的调用执行

Qt、C/C环境中内嵌LUA脚本、实现LUA函数的调用执行 Chapter1. Qt、C/C环境中内嵌LUA脚本、实现LUA函数的调用执行1、LUA简介2、LUA脚本的解释器和编译器3、C环境中内嵌LUA执行LUA函数调用4、Qt内嵌LUA执行LUA函数调用5、运行结果6、内嵌LUA脚本在实际项目中的案例应用 Chapter1…

元宇宙是概念炒作?

关键字&#xff1a;万界星空、万界星空科技、工业元宇宙、AR数字孪生、工业数字孪生、汽车数字孪生、机械加工数字孪生 引言 近两年被“元宇宙”席卷了&#xff0c;好多人问也好多人在各大媒体讨论过&#xff1a;“元宇宙到底是个啥&#xff1f;” 想必你也一定有所耳闻&…

世界少棒经典赛·棒球1号位

世界少棒经典赛 1. 世界少棒经典赛的起源 详细描述世界少棒经典赛的历史起源。 世界少棒经典赛的历史起源可以追溯到1985年&#xff0c;那个夏天&#xff0c;它首次在美国新泽西州举行&#xff0c;那时只有来自美国的12支球队参赛&#xff0c;这些球队在当地的特伦顿市体育中…

meanshift算法通俗讲解【meanshift实例展示】

meanshift算法原理 meanshift算法的原理很简单。假设你有一堆点集&#xff0c;还有一个小的窗口&#xff0c;这个窗口可能是圆形的&#xff0c;现在你可能要移动这个窗口到点集密度最大的区域当中。 如下图&#xff1a; 最开始的窗口是蓝色圆环的区域&#xff0c;命名为C1。蓝…

【Linux命令200例】split将一个大文件拆分成多个小文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主✌&#xff0c;2023年6月csdn上海赛道top4✌。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加…

Java多线程(四)

目录 一、线程的状态 1.1 观察线程的所有状态 1.2 线程状态和状态转移的意义 1.2.1 NEW、RUNNABLE、TERMINATED状态转换 1.2.2 WAITING、BLOCKED、TIMED_WAITING状态转换 1.2.3 yield()大公无私让出cpu 一、线程的状态 1.1 观察线程的所有状态 public class Demo9 {public st…

HTML5网页设计小案例:网页导航栏的设计

什么是导航栏&#xff0c;按我的理解就是位于网页顶部或者侧边一组链接或者按钮&#xff0c;用来指导大家找到网页的不同板块&#xff0c;大家可以一目了然的找到自己想看的板块内容。今天我们设计一个位于网页顶部的的导航栏。按我的生活经验来说&#xff0c;网页的顶部导航栏…

人机融合意识与人类的意识的区别

人机融合意识是指人类与计算机系统之间建立起密切的交互和协作关系&#xff0c;形成一种共同的意识和认知状态。人机融合意识与人类意识存在一些本质上的区别&#xff0c;可以从以下几个方面进行区分&#xff1a; 原始性&#xff1a;人类的意识是自然生命的产物&#xff0c;伴随…