设计模式--工厂模式 caffe_layer注册
来源:http://www.cnblogs.com/zhouqiang/archive/2012/07/20/2601365.html
来源:http://blog.luoyetx.com/2016/02/reading-caffe-3/
工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类。
工厂模式的形态
工厂模式主要用一下几种形态:
1:简单工厂(Simple Factory)。
2:工厂方法(Factory Method)。
3:抽象工厂(Abstract Factory)。
简单工厂(Simple Factory)
又叫静态工厂,是工厂模式三中状态中结构最为简单的。主要有一个静态方法,用来接受参数,并根据参数来决定返回实现同一接口的不同类的实例。我们来看一个具体的例子:
假设一家工厂,几生产洗衣机,有生产冰箱,还有空调等等..
我们先为所有产品定义一个共同的产品接口
- public interface Product{}
接着我们让这个工厂的所有产品都必须实现此接口
- public class Washer implements Product{
- public Washer(){
- System.out.println("洗衣机被制造了");
- }
- }
- public class Icebox implements Product{
- public Icebox(){
- System.out.println("冰箱被制造了");
- }
- }
- public class AirCondition implements Product{
- public Icebox(){
- System.out.println("空调被制造了");
- }
- }
接下来我们来写一个工厂类,有它来负责生产以上的产品
- public class SimpleFactory {
- public static Product factory(String productName) throws Exception{
- if(productName.equals("Washer")){
- return new Washer();
- }else if(productName.equals("Icebox")){
- return new Icebox();
- }else if(productName.equals("AirCondition")){
- return new AirCondition();
- }else{
- throw new Exception("没有该产品");
- }
- }
- }
好了,有了这个工厂类,我们就可以开始下定单了,SimpleFactory将根据不同的定单类决定生产什么产品。
- public static void main(String[] args) {
- try {
- SimpleFactory.factory("Washer");
- SimpleFactory.factory("Icebox");
- SimpleFactory.factory("AirCondition");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
由上面的代码可以看出,简单工厂的核心就是一个SimpleFactory类,他拥有必要的逻辑判断能力和所有产品的创建权利,我们只需要向把定单给他,就能得到我们想要的产品。这使用起来似乎非常方便。
但,实际上,这个SimpleFactory有很多的局限。首先,我们每次想要增加一种新产品的时候,都必须修改SimpleFactory的原代码。其次,当我们拥有很多很多产品的时候,而且产品之间又存在复杂的层次关系的时候,这个类必须拥有复杂的逻辑判断能力,其代码量也将不断地激增,这对以后的维护简直就是恐怖两个字...
还有就是,整个系统都严重依赖SimpleFactory类,只要SimpleFactory类一出问题,系统就进入不能工作的状态,这也是最为致命的一点....
以上的不足将在工厂模式的另外两种状态中得到解决。
工厂方法(Factory Method)
上面的代码告诉我们,简单工厂并不简单,它是整个模式的核心,一旦他出了问题,整个模式都将受影响而不能工作,为了降低风险和为日后的维护、扩展做准备,我们需要对它进行重构,引入工厂方法。
工厂方法为工厂类定义了接口,用多态来削弱了工厂类的职能,以下是工厂接口的定义:
- public interface Factory{
- public Product create();
- }
我们再来定义一个产品接口
- public interface Product{}
一下是实现了产品接口的产品类
- public class Washer implements Product{
- public Washer(){
- System.out.println("洗衣机被制造了");
- }
- }
- public class Icebox implements Product{
- public Icebox(){
- System.out.println("冰箱被制造了");
- }
- }
- public class AirCondition implements Product{
- public Icebox(){
- System.out.println("空调被制造了");
- }
- }
接下来,就是工厂方法的核心部分,也就是具体创建产品对象的具体工厂类,
- //创建洗衣机的工厂
- public class CreateWasher implements Factory{
- public Product create(){
- return new Washer();
- }
- }
- //创建冰箱的工厂
- public class CreateIcebox implements Factory{
- public Product create(){
- return new Icebox();
- }
- }
- //创建空调的工厂
- public class CreateAirCondition implements Factory{
- public Product create(){
- return new AirCondition();
- }
- }
从上面创建产品对象的代码可以看出,工厂方法和简单工厂的主要区别是,简单工厂是把创建产品的职能都放在一个类里面,而工厂方法则把不同的产品放在实现了工厂接口的不同工厂类里面,这样就算其中一个工厂类出了问题,其他工厂类也能正常工作,互相不受影响,以后增加新产品,也只需要新增一个实现工厂接口工厂类,就能达到,不用修改已有的代码。但工厂方法也有他局限的地方,那就是当面对的产品有复杂的等级结构的时候,例如,工厂除了生产家电外产品,还生产手机产品,这样一来家电是手机就是两大产品家族了,这两大家族下面包含了数量众多的产品,每个产品又有多个型号,这样就形成了一个复杂的产品树了。如果用工厂方法来设计这个产品家族系统,就必须为每个型号的产品创建一个对应的工厂类,当有数百种甚至上千种产品的时候,也必须要有对应的上百成千个工厂类,这就出现了传说的类爆炸,对于以后的维护来说,简直就是一场灾难.....
抽象工厂(Factory Method)
抽象工厂:意的意图在于创建一系列互相关联或互相依赖的对象。<<Java设计模式>>
我自己觉得抽象工厂是在工厂方法的基础上引进了分类管理的概念....
工厂方法用来创建一个产品,它没有分类的概念,而抽象工厂则用于创建一系列产品,所以产品分类成了抽象工厂的重点,
我们继续用上面的例子来说明:
工厂生产的所有产品都用都用大写字母来标明它们的型号,比如冰箱,就有“冰箱-A",“冰箱-B",同样,其他的产品也都是遵守这个编号规则,于是就有了一下产品家族树
冰箱:
- 冰箱-A
- 冰箱-B
洗衣机:
- 洗衣机-A
- 洗衣机-B
我们可以为冰箱和洗衣机分别定义两个产品接口,以对他们进行分类,
- //洗衣机接口
- public interface Washer{
- }
- //冰箱接口
- public interface Icebox{
- }
接着,我们分别创建这两个接口的具体产品
- //洗衣机-A
- public class WasherA implements Washer{
- public WasherA(){
- System.out.println("洗衣机-A被制造了");
- }
- }
- //洗衣机-B
- public class WasherB implements Washer{
- public WasherB(){
- System.out.println("洗衣机-B被制造了");
- }
- }
- //冰箱-A
- public class IceboxA implements Icebox{
- public IceboxA(){
- System.out.println("冰箱-A被制造了");
- }
- }
- //冰箱-B
- public class IceboxB implements Icebox{
- public IceboxB(){
- System.out.println("冰箱-B被制造了");
- }
- }
到此,产品部分我们准备好了,接下来我们来处理工厂部分,我们先来定义工厂行为接口
- public interface Factory{
- public Washer createWasher();
- public Icebox createIcebox();
- }
接下来我创造具体的工厂类,我们根据上面产品的接口,把型号A的产品分为一类,由一个工厂来管理,把型号为B的产品有另一个工厂管理,根据这个分类,我们可以实现如下的两个具体工厂类
- //创建型号为A的产品工厂
- public class FactoryA implements Factory{
- //创建洗衣机-A
- public Washer createWasher(){
- return new WasherA();
- }
- //创建冰箱-A
- public Icebox createIcebox(){
- return new IceboxA();
- }
- }
- //创建型号为B的产品工厂
- public class FactoryB implements Factory{
- //创建洗衣机-B
- public Washer createWasher(){
- return new WasherB();
- }
- //创建冰箱-B
- public Icebox createIcebox(){
- return new IceboxB();
- }
- }
这样,我们的抽象工厂就完成了。有上面可以看出,在运用上我觉得工厂方法和抽象工厂,都有自己的应用场景,并没有什么优劣之分,但在应用抽象工厂之前,要先对创建的对象进行系统的分类,这点很重要,好的产品分类规则能为具体工厂类的选择调用和以后的扩展提供清晰的思路.
Caffe 中的 Layer 是神经网络 Net 的基本结构,Caffe 内部维护一个注册表用于查找特定 Layer 对应的工厂函数。很多同学在 Windows 下使用 Caffe 遇到的一个问题就是运行 Caffe 相关的代码时出现无法找到 Layer,但是这个问题不会在 Linux 平台上出现,这个问题跟编译器有关,同时也是跟 Caffe 注册 Layer 的机制有关。
1 |
F0203 12:50:07.581297 11524 layer_factory.hpp:78] Check failed: registry.count(type) == 1 (0 vs. 1) |
上面的错误是无法在注册表中找到 Convolution Layer 对应的工厂函数,程序直接崩溃。下面我们就来聊聊 Caffe 的 Layer 加载机制,以及为什么在 VC 下会出现这种问题。
Caffe 的 Layer 注册表其实就是一组键值对,key 为 Layer 的类型而 value 则对应其工厂函数。下面两组宏控制了 Layer 的注册动作。
1 |
#define REGISTER_LAYER_CREATOR(type, creator) \ |
REGISTER_LAYER_CLASS
宏可以实现将特定 Layer 注册到全局注册表中,首先定义一个工厂函数用来产生 Layer 对象,然后调用 REGISTER_LAYER_CREATOR
将工厂函数和 Layer 的类型名进行注册,注册时只是用 Layer 的 float 和 double 类型,这是网络实际数据使用到的类型。两个静态变量一个对应 float,另一个对应 double,这两个变量的初始化,也就是它们的构造函数实际上完成 Layer 的注册动作。
1 |
template <typename Dtype> |
LayerRegisterer
对象初始化时实际上又是调用相应类型的 LayerRegistry
类的静态方法 AddCreator
。
1 |
typedef std::map<string, Creator> CreatorRegistry; static CreatorRegistry& Registry() { |
注册表类型为 CreatorRegistry
,实际类型为 std::map<string, Creator>
。可以通过 Registry
函数获取注册表的全局单例。而注册的过程就是一个简单的 map
操作。
1 |
// Adds a creator. |
注册的过程大概就是上面说到的流程。Caffe 中的 Layer 采用静态变量初始化的方式来注册工厂函数到全局注册表中,整个注册过程依赖这些静态变量。那么问题来了,为什么 VC 中的代码无法在注册表中找到 Layer 对应的工厂函数?事实上,VC 中 Caffe 代码的全局注册表是空的,一条记录都没有,问题并不是出在这个全局注册表,而是那些完成注册动作的静态变量。由于这些静态变量存在的意义在于其构造函数完成 Layer 的注册动作,没有任何一段代码会去引用这些静态变量,这个坑在于 VC 默认会优化掉这些静态变量,那么所有这些静态变量对应的构造函数将无法执行,那么注册动作一个都不会触发,导致全局注册表为空,然后在构造网络 Net 时就会崩溃。
设计模式--工厂模式 caffe_layer注册的更多相关文章
- .NET设计模式: 工厂模式
.NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html .NET设计模式(1): ...
- 【设计模式】Java设计模式 -工厂模式
[设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...
- PHP设计模式-工厂模式、单例模式、注册模式
本文参考慕课网<大话PHP设计模式>-第五章内容编写,视频路径为:http://www.imooc.com/video/4876 推荐阅读我之前的文章:php的设计模式 三种基本设计模式, ...
- 10.Java设计模式 工厂模式,单例模式
Java 之工厂方法和抽象工厂模式 1. 概念 工厂方法:一抽象产品类派生出多个具体产品类:一抽象工厂类派生出多个具体工厂类:每个具体工厂类只能创建一个具体产品类的实例. 即定义一个创建对象的接口(即 ...
- PHP模式设计之单例模式、工厂模式、注册树模式、适配器模式、观察者模式
php模式设计之单例模式 什么是单例模式? 单例模式是指在整个应用中只有一个实例对象的设计模式 为什么要用单例模式? php经常要链接数据库,如果在一个项目中频繁建立连接数据库,会造成服务器资源的很大 ...
- C#设计模式--工厂模式(创建型模式)
一.简单工厂模式(UML类图): 核心类代码: public class Calc { public double NumberA { get; set; } public double Number ...
- [Head First设计模式]饺子馆(冬至)中的设计模式——工厂模式
系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...
- javascript 设计模式-----工厂模式
所谓的工厂模式,顾名思义就是成批量地生产模式.它的核心作用也是和现实中的工厂一样利用重复的代码最大化地产生效益.在javascript中,它常常用来生产许许多多相同的实例对象,在代码上做到最大的利用. ...
- JavaScript设计模式——工厂模式
工厂模式:是一种实现“工厂”概念的面上对象设计模式.实质是定义一个创建对象的接口,但是让实现这个接口的类来决定实例化哪个类.工厂方法让类的实例化推迟到子类中进行.创建一个对象常常需要复杂的过程,所以不 ...
随机推荐
- [CF1103B]Game with modulo
题目大意:交互题,有一个数$a(a\leqslant10^9)$,需要猜出它的值,一次询问为你两个数字$x,y(x,y\in[0,2\times10^9])$: 若$x\bmod a\geqslant ...
- 【PDF】HTML中嵌入pdf的简单方法
<embed src="> 或者你不想显示某些功能的话: <embed src=">
- BZOJ1089 [SCOI2003]严格n元树 【dp + 高精】
Description 如果一棵树的所有非叶节点都恰好有n个儿子,那么我们称它为严格n元树.如果该树中最底层的节点深度为d (根的深度为0),那么我们称它为一棵深度为d的严格n元树.例如,深度为2的严 ...
- bzoj3192: [JLOI2013]删除物品(树状数组)
既然要从一个堆的堆顶按顺序拿出来放到第二个堆的堆顶,那么我们就可以把两个堆顶怼在一起,这样从一个堆拿到另一个堆只需要移动指针就好了. 换句话说,把1~n倒着,n+1到n+m正着,用一个指针把两个序列分 ...
- 51nod1967 路径定向(欧拉回路+结论题)
看到入度等于出度想到欧拉回路. 我们把边都变成无向边,有一个结论是偶数度的点都可以变成出入度相等的点,而奇数点的不行,感性理解分类讨论一下就知道是对的. 还有一个更好理解的结论是变成无向边后奇数点的个 ...
- omnet++4.0安装使用
http://my.oschina.net/u/2269841/blog/423659 本文主要借鉴该文章在此对作者表示由衷的感谢http://blog.csdn.net/xiaobei4929/ar ...
- python 之 strip()--(转载)
原博地址:http://www.jb51.net/article/37287.htm 函数原型 声明:s为字符串,rm为要删除的字符序列 s.strip(rm) 删除s字符串中开头.结尾 ...
- P2596 [ZJOI2006]书架 && Splay 区间操作(三)
P2596 [ZJOI2006]书架 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书, ...
- [DeeplearningAI笔记]序列模型2.6Word2Vec/Skip-grams/hierarchical softmax classifier 分级softmax 分类器
5.2自然语言处理 觉得有用的话,欢迎一起讨论相互学习~Follow Me 2.6 Word2Vec Word2Vec相对于原先介绍的词嵌入的方法来说更加的简单快速. Mikolov T, Chen ...
- 【BZOJ】1798: [Ahoi2009]Seq 维护序列seq 线段树多标记(区间加+区间乘)
[题意]给定序列,支持区间加和区间乘,查询区间和取模.n<=10^5. [算法]线段树 [题解]线段树多重标记要考虑标记与标记之间的相互影响. 对于sum*b+a,+c直接加上即可. *c后就是 ...