前言
ch04与ch0402基于算子注册机制分别实现了relu算子与sigmoid算子, 本文尝试在自己的理解下对代码进行一下分析与梳理。
项目地址
课程地址
个人完成的作业地址
代码
operater存放节点相关参数,layer负责计算执行
ops
Operator基类
enum class OpType { // op类型,每实现一个算子都要添加一个
kOperatorUnknown = -1,
kOperatorRelu = 0,
kOperatorSigmoid = 1,
};
class Operator {
public:
OpType op_type_ = OpType::kOperatorUnknown; //不是一个具体节点 指定为unknown
virtual ~Operator() = default; //
explicit Operator(OpType op_type);
};
ReluOperator类,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的部分
};
layer
layer基类,只包括层名layer_name
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"
};
ReluLayer类
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;
// 返回一个指向relu_layer的智能指针,其中具体参数由op给出
static std::shared_ptr<Layer> CreateInstance(const std::shared_ptr<Operator> &op);
private:
// 负责存放计算需要用到的参数
std::unique_ptr<ReluOperator> op_;
};
// 重要
// ReluLayer定义完成自动调用
// 只执行一次, 不管这个类被初始化几次, 这句话只执行一次(应该是编译时候)
LayerRegistererWrapper kReluLayer(OpType::kOperatorRelu, ReluLayer::CreateInstance);
factory
class LayerRegisterer {
public:
typedef std::shared_ptr<Layer> (*Creator)(const std::shared_ptr<Operator> &op);
// 函数指针类型,我们将存放参数的Oprator类传入到该方法中,然后该方法根据Operator内的参数返回具体的Layer.
typedef std::map<OpType, Creator> CreateRegistry;
// value是用于创建该层的对应方法(Creator)
// 往字典注册op算子
static void RegisterCreator(OpType op_type, const Creator &creator);
// 根据字典和op创建layer
static std::shared_ptr<Layer> CreateLayer(const std::shared_ptr<Operator> &op);
// 返回一个字典
static CreateRegistry &Registry();
};
class LayerRegistererWrapper {
public:
//op_type是算子的类型,作为Layer注册表的key使用,creator是创建具体层的工厂方法,作为Layer注册表的value
LayerRegistererWrapper(OpType op_type, const LayerRegisterer::Creator &creator) {
LayerRegisterer::RegisterCreator(op_type, creator);
}
};
把注册相关函数一次抽出来
// 定义完类 作为类的一部分自动运行 构造完自动析构
LayerRegistererWrapper kReluLayer(OpType::kOperatorRelu, ReluLayer::CreateInstance);
// 调用注册函数
LayerRegistererWrapper(OpType op_type, const LayerRegisterer::Creator &creator) {
LayerRegisterer::RegisterCreator(op_type, creator);
}
LayerRegisterer::RegisterCreator(OpType op_type, const Creator& creator)
在RegisterCreator注册时, 还要调用Registry()返回注册表 --->存入实现方法
调用方法
// 可以不使用注册机制
std::shared_ptr<Operator> relu_op = std::make_shared<ReluOperator>(thresh);
ReluLayer layer(relu_op);
// 工厂模式调用
std::shared_ptr<Operator> relu_op = std::make_shared<ReluOperator>(thresh);
std::shared_ptr<Layer> relu_layer = LayerRegisterer::CreateLayer(relu_op);
后记
这节最困惑的是, kReluLayer和kSigmoidLayer何时执行(被定义)。
LayerRegistererWrapper kReluLayer(OpType::kOperatorRelu, ReluLayer::CreateInstance);
LayerRegistererWrapper kSigmoidLayer(OpType::kOperatorSigmoid, SigmoidLayer::CreateInstance);
为什么要有LayerRegistererWrapper函数, 毕竟只调用了一下RegisterCreator函数。
CreateInstance有什么用,返回一个对象指针,为什么不能直接返回。
可能是注册表值里面是一个函数指针,所以需要CreateInstance?
LayerRegistererWrapper应该保证kReluLayer是一个对象,然后会自动销毁,如果是函数有可能被多次调用。
kReluLayer和kSigmoidLayer应该是函数被定义就执行完毕,在main之前?
TEST
算子test成功
目前总共通过10个test