Caffe 源码(二) —— common 文件

本文深入剖析Caffe框架的common.hpp和common.cpp文件,详细解读宏定义、全局初始化函数及Caffe类(单例模式)的作用。涵盖随机数生成器(RNG)的实现与运用,以及Fisher–Yates shuffle算法在随机数混洗中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

caffe 源码 —— common.hpp/cpp


这里主要是对 caffe 框架源码进行梳理与学习( 主要是 CPU 模式下的,所以暂时还不涉及 CUDA,cudnn 编程),不同时期回看源码收获不同,水平有限,如有错误之处还请留言指正交流。

首先的切入点就是 common.hpp, 其包含在 blob.hpplayer.cpp 头文件中,而 blob.hpplayer.cpp 这两者基本被框架所有网络相关层包含,所以可见 common.hpp 于 caffe 的重要性。

概括来说包含了以下内容:

  • 宏定义 : 主要用来实例化类模板
  • 全局初始化函数 : 初始化 gflags 和 glog
  • Caffe 类(单例模式类) : 随机数生成

下面我们就结合 common.cpp 来集体说说以下内容。

1. 宏定义

这里的宏定义主要是实例化制定的类模板。 caffe 主要使用了 float 和 double 两种精度的数据类型,所以在网络相关功能定义时都是使用了泛型模板编程,而在这里用 INSTANTIATE_CLASS(classname) 进行实例化。

#define INSTANTIATE_CLASS(classname) \
  char gInstantiationGuard##classname; \
  template class classname<float>; \
  template class classname<double>

相似的还有:

#define INSTANTIATE_LAYER_GPU_FORWARD(classname) \
  template void classname<float>::Forward_gpu( \
  const std::vector<Blob<float>*>& bottom, \
  const std::vector<Blob<float>*>& top); \
  template void classname<double>::Forward_gpu( \
  const std::vector<Blob<double>*>& bottom, \
  const std::vector<Blob<double>*>& top);

上面是实例化了 GPU 模式下的前向操作类,同理还有反向操作类。这里还有一个值得一提是:

#define DISABLE_COPY_AND_ASSIGN(classname) \
private:\
  classname(const classname&);\
  classname& operator=(const classname)

这是将类的拷贝构造函数和赋值构造函数设置成 private,禁止指定类的拷贝和赋值操作来初始化另一个类

2. 全局初始化函数

函数声明: void GlobalInit(int* pargc, char*** pargv)
功能:初始化 gflags 和 glog
源码(部分):

void GlobalInit(int* pargc, char*** pargv) {
  // Google flags.
  ::gflags::ParseCommandLineFlags(pargc, pargv, true);
  // Google logging.
  ::google::InitGoogleLogging(*(pargv)[0]);
}

3. Caffe 类(单例模式)

Caffe 类是一个懒汉模式的单例模式,简而言之即 Caffe 类将自己的构造函数设置成 private,这样程序就只能通过唯一的成员函数来初始化实例,在创建实例前做一个判断,只有当前不存在实例时才创建,这就保证了程序中只会有一个caffe 类的实例,这就是单例模式,这种使用时候才进行创建就是所谓的懒汉模式。这里我写了一个简单版的示例:

class Caffe_demo { // 懒汉单例的 Caffe_demo 类
public:
	~Caffe_demo(); // 析构函数
	Caffe_demo* Get() {  // 创建实例的唯一接口
		if (nullptr == m_caffe) {  // 如果
			m_caffe = new Caffe_demo();
		}
		return m_caffe;
	}

	class RNG {
		//......
	};
	// 其他内容....

private:
	Caffe_demo(); // 构造函数
	DISABLE_COPY_AND_ASSIGN(Caffe_demo); // 宏定义,见 1. 宏定义 部分
	static Caffe_demo * m_caffe; // 静态变量
};

Caffe_demo * m_caffe = nullptr;

这里的其他内容是一些,查询设备号,设置设备等功能,这里就不展开叙述了。下面的重点是包含在 Caffe 类中的 RNG 类, 随机数生成器(Random Number Generator),这个类是对 Boost库 和 CUDA 中随机数生成器的一个包装,对外以 RNG 类统一显示,首先引用知乎上一段关于随机数生成的回答:

随机数在caffe中是非常重要的,最重要的应用是权值的初始化,如高斯、xavier等,初始化的好坏直接影响最终的训练结果,其他的应用如训练图像的随机crop和mirror、dropout层的神经元的选择。

然后在 RNG 作为一个统一的接口,并不是直接通过 RNG 的成员函数来实现,而是内部又复合了另一个私有 Generator 类,用于随机数的生成,即调用需要通过三级的关系。

void* Caffe::RNG::generator() {
  return static_cast<void*>(generator_->rng());
}

这里的 rng() 类的定义中给了两个接口,是通过 Generator 类的构造函数实现的:

class Caffe::RNG::Generator {
 public:
  Generator() : rng_(new caffe::rng_t(cluster_seedgen())) {} // boost 随机生成器
  explicit Generator(unsigned int seed) : rng_(new caffe::rng_t(seed)) {} // 采用给定的种子初始化
  caffe::rng_t* rng() { return rng_.get(); } // 属性
 private:
  shared_ptr<caffe::rng_t> rng_; 
};

一个是可以认为设定随机种子 explicit Generator(unsigned int seed) ,如果固定随机种子,每次就可以获取相同的随机数结果,还有就是默认的无随机数种子的 Generator() ,就采用默认的 boost 库的随机函数进行随机数生成。这里的 rng_t 其实就是 boost 库随机数的返回形式,见 rng.hpp 中:

typedef boost::mt19937 rng_t;

而这里都使用了智能指针进行管理,且 Caffe 实例中也是使用了 boost::thread_specific_ptr 这类特殊指针,其行为类似于智能指针,智能管理线程,当线程退出时,自动释放资源。

rng.hpp

这里基于上述 common.hpp 的分析,caffe 框架下 include/util 中还包含了名为 rng.hpp 的头文件,其中主要是基于 RNG 类的随机生成数,再将它们混合,来达到充分随机。使用的是 Fisher–Yates shuffle 算法,其是用来将一个有限集合打乱的算法,这个算法生成的随机排列是等概率的,同时需要非常高效。

inline rng_t* caffe_rng() { // 获取 RNG 类的随机数
  return static_cast<caffe::rng_t*>(Caffe::rng_stream().generator());
}

// 使用 Fisher–Yates algorithm 算法对随机数进行混洗
template <class RandomAccessIterator, class RandomGenerator>
inline void shuffle(RandomAccessIterator begin, RandomAccessIterator end,
RandomGenerator* gen) {
  typedef typename std::iterator_traits<RandomAccessIterator>::difference_type
  difference_type;
  typedef typename boost::uniform_int<difference_type> dist_type;

  difference_type length = std::distance(begin, end);
  if (length <= 0) return;

  for (difference_type i = length - 1; i > 0; --i) {
    dist_type dist(0, i);
    std::iter_swap(begin + i, begin + dist(*gen));
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值