培训行业门户网站建设,下载导航到手机上安装,太原电商网站设计,国外注册品牌 建设网站Layer的设计思路
Layer的抽象
如果将深度学习中的所有层分为两类, 那么肯定是带权重的层和不带权重的层。
基于层的共性#xff0c;我们定义了一个Layer的基类#xff0c;提供了一些基本接口#xff0c;并可以通过继承和多态机制实现不同类型的L…Layer的设计思路
Layer的抽象
如果将深度学习中的所有层分为两类, 那么肯定是带权重的层和不带权重的层。
基于层的共性我们定义了一个Layer的基类提供了一些基本接口并可以通过继承和多态机制实现不同类型的Layer。
具体来说该类包括以下几个成员函数 构造函数 Layer(std::string layer_name)用于创建一个Layer对象并设置该层的名称。 virtual ~Layer() default虚析构函数在派生类中可以通过override关键字重新定义。 virtual InferStatus Forward(const std::vectorstd::shared_ptrTensorfloat inputs, std::vectorstd::shared_ptrTensorfloat outputs) 前向传播函数将输入tensor作为参数计算输出tensor。 virtual const std::vectorstd::shared_ptrTensorfloat weights() const, 返回当前层的权重数组。 virtual const std::vectorstd::shared_ptrTensorfloat bias() const, 返回当前层的偏置数组。 virtual void set_weights(const std::vectorstd::shared_ptrTensorfloat weights)设置当前层的权重数组。 virtual void set_bias(const std::vectorstd::shared_ptrTensorfloat bias)设置当前层的偏置数组。 virtual void set_weights(const std::vectorfloat weights)将权重数据类型转换为shared_ptr后调用上述函数。 virtual void set_bias(const std::vectorfloat bias)将偏置数据类型转换为shared_ptr后调用上述函数。 virtual const std::string layer_name() const返回当前层的名称。
而成员变量只有一个即
std::string layer_name_Layer的名称
为什么定义成虚函数
在神经网络中不同的层具有不同的结构和运算方式因此需要不同的函数来实现它们。使用虚函数的方法可以将这些不同的函数封装到一个基类中并通过多态机制来实现不同类型的层的动态绑定。
具体来说当使用基类指针或引用调用虚函数时程序会根据对象的动态类型即实际指向的派生类类型来选择相应的函数实现。这就使得不同类型的层可以通过共同的接口进行调用从而提高了代码的可维护性和可扩展性。
此外使用虚函数还可以方便地定义抽象类即只声明虚函数但不提供实现的类。这可以为子类提供一个规范化的接口要求其必须重写某些接口以满足特定的需求。这种机制可以有效避免在大型工程中出现微小的差错而导致底层实现不符合最终需求的问题。
带权重Layer的实现
我们把Layer基类来表示不带参数的Layer并且通过继承该Layer基类的方式来定义了一个带参数的层ParamLayer子类在ParamLayer中定义了成员变量bias和weights。
ParamLayer是具有可调参数的神经网络层实现包括初始化权重和偏置的函数、重载读写权重和偏置的函数以及保存权重和偏置的成员变量。
具体来说该类包括以下几个成员函数和成员变量 构造函数 ParamLayer(const std::string layer_name)用于创建一个ParamLayer对象并设置该层的名称。 void InitWeightParam(const uint32_t param_count, const uint32_t param_channel, const uint32_t param_height, const uint32_t param_width)用于初始化权重参数。 void InitBiasParam(const uint32_t param_count, const uint32_t param_channel, const uint32_t param_height, const uint32_t param_width)用于初始化偏置参数。 const std::vectorstd::shared_ptrTensorfloat weights() const override重载虚函数weights()返回保存权重参数的成员变量weights_。 const std::vectorstd::shared_ptrTensorfloat bias() const override重载虚函数bias()返回保存偏置参数的成员变量bias_。 void set_weights(const std::vectorfloat weights) override重载虚函数set_weights()将权重数据类型转换为shared_ptr后存储在成员变量weights_中。 void set_bias(const std::vectorfloat bias) override重载虚函数set_bias()将偏置数据类型转换为shared_ptr后存储在成员变量bias_中。 void set_weights(const std::vectorstd::shared_ptrTensorfloat weights) override重载虚函数set_weights()将参数复制到成员变量weights_中。 void set_bias(const std::vectorstd::shared_ptrTensorfloat bias) override重载虚函数set_bias()将参数复制到成员变量bias_中。 成员变量std::vectorstd::shared_ptrTensorfloat weights_保存ParamLayer的权重参数。 成员变量std::vectorstd::shared_ptrTensorfloat bias_保存ParamLayer的偏置参数。
ParamLayer通过继承Layer类实现了一些共同接口并在此基础上扩展了更多函数和成员可以方便地实现带有参数的神经网络层。
Layer的注册机制
为了实现注册和创建神经网络层并在运行时动态地生成不同类型的神经网络层定义了两个类LayerRegisterer和LayerRegistererWrapper。
具体来说LayerRegisterer类提供了三个静态函数和一个静态成员变量 typedef ParseParameterAttrStatus (*Creator)(const std::shared_ptrRuntimeOperator op, std::shared_ptrLayer layer)定义了一个函数指针类型Creator用于指向具体神经网络层的函数。 typedef std::mapstd::string, Creator CreateRegistry定义了一个映射类型CreateRegistry用于保存层类型和对应创建函数的映射关系。 static void RegisterCreator(const std::string layer_type, const Creator creator)将层类型和创建函数的映射关系注册到CreateRegistry中。 static std::shared_ptrLayer CreateLayer(const std::shared_ptrRuntimeOperator op)根据输入的op对象创建相应的神经网络层。 static CreateRegistry Registry()返回当前已经注册的所有层类型和创建函数的映射关系。
RuntimeOperator是计算图的某个计算节点里面保存了计算节点所需的参数等信息具体介绍请看3.Graph.md。
而LayerRegistererWrapper类则提供了一个构造函数用于将某一种类型的神经网络层和其创建函数注册到LayerRegisterer中如下所示。
class LayerRegistererWrapper {public:LayerRegistererWrapper(const std::string layer_type, const LayerRegisterer::Creator creator) {LayerRegisterer::RegisterCreator(layer_type, creator);}
};在LayerRegisterer类中通过维护一个键值对std::string, CreatorCreateRegistry管理Layer注册表在注册和查找Layer时都要先检查一下是否注册如果未注册输出错误信息。
为什么要把成员函数定义为静态的
静态函数与类相关联而不是与类的对象相关。因此静态函数可以在没有创建类的实例的情况下调用从而方便地提供一些辅助函数或管理函数例如工厂方法、单例等。
LayerRegisterer和LayerRegistererWrapper中定义的所有函数都是静态的主要原因是这些函数需要全局地维护层类型和创建函数的映射关系并控制新层类型的注册和创建过程。使用静态函数可以使得这些功能在整个程序中被共享和访问同时避免了由于对象实例的含糊不清而导致的编码错误。
另外需要注意的是静态函数可以直接使用静态成员变量不需要通过对象来访问这使得这些静态函数可以更容易地协同工作并兼顾了效率和灵活性。
阅读的代码
include layer abstract layer_factory.hpplayer.hppparam_layer.hpp source layer abstract layer.cpplayer_factory.cppparam_layer.cpp