Caffe 架構(gòu)學(xué)習(xí)-底層數(shù)據(jù)1

前言 shared_ptr智能指針

為了解決C++內(nèi)存泄漏的問題脐嫂,C++11引入了智能指針(Smart Pointer)。
C++11提供了三種智能指針:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用時(shí)需添加頭文件<memory>。
shared_ptr使用引用計(jì)數(shù),每一個(gè)shared_ptr的拷貝都指向相同的內(nèi)存辉浦。每使用他一次,內(nèi)部的引用計(jì)數(shù)加1茎辐,每析構(gòu)一次宪郊,內(nèi)部的引用計(jì)數(shù)減1掂恕,減為0時(shí),刪除所指向的堆內(nèi)存弛槐。shared_ptr內(nèi)部的引用計(jì)數(shù)是安全的懊亡,但是對(duì)象的讀取需要加鎖。

#include "stdafx.h"
#include <iostream>
#include <future>
#include <thread>

using namespace std;
class Person
{
public:
    Person(int v) {
        value = v;
        std::cout << "Cons" <<value<< std::endl;
    }
    ~Person() {
        std::cout << "Des" <<value<< std::endl;
    }

    int value;

};

int main()
{
    std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用計(jì)數(shù)為1

    std::shared_ptr<Person> p2 = std::make_shared<Person>(2);

    p1.reset(new Person(3));// 首先生成新對(duì)象乎串,然后引用計(jì)數(shù)減1店枣,引用計(jì)數(shù)為0,故析構(gòu)Person(1)
                            // 最后將新對(duì)象的指針交給智能指針

    std::shared_ptr<Person> p3 = p1;//現(xiàn)在p1和p3同時(shí)指向Person(3)叹誉,Person(3)的引用計(jì)數(shù)為2

    p1.reset();//Person(3)的引用計(jì)數(shù)為1
    p3.reset();//Person(3)的引用計(jì)數(shù)為0鸯两,析構(gòu)Person(3)
    return 0;
}
注意,不能將一個(gè)原始指針直接賦值給一個(gè)智能指針长豁,如下所示钧唐,原因是一個(gè)是類,一個(gè)是指針匠襟。
std::shared_ptr<int> p4 = new int(1);// error

獲取原始指針
std::shared_ptr<int> p4(new int(5));
int *pInt = p4.get();

SyncedMemory 負(fù)責(zé)內(nèi)存同步

size_ :儲(chǔ)存空間的大小
head_ :SyncedMemory的當(dāng)前狀態(tài){UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED}
cpu_ptr_ :CPU指針
gpu_ptr_ :GPU指針

SyncedMemory(size_t size) 僅僅初始化size_钝侠,不分配內(nèi)存!
to_cpu()酸舍、to_gpu() 真正分配內(nèi)存帅韧,同步CPU和GPU

Blob 基本計(jì)算單元

重要的成員
//重要的成員函數(shù)
Blob(const int num, const int channels, const int height,const int width);
Reshape(const int num, const int channels, const int height,const int width);
const Dtype* cpu_data() const;
void set_cpu_data(Dtype* data);
const int* gpu_shape() const;
const Dtype* gpu_data() const;
const Dtype* cpu_diff() const;
const Dtype* gpu_diff() const;
Dtype* mutable_cpu_data();
Dtype* mutable_gpu_data();
Dtype* mutable_cpu_diff();
Dtype* mutable_gpu_diff();
void Update();
void FromProto(const BlobProto& proto, bool reshape = true);
/// @brief Compute the sum of absolute values (L1 norm) of the data.
Dtype asum_data() const;
/// @brief Compute the sum of absolute values (L1 norm) of the diff.
Dtype asum_diff() const;
/// @brief Compute the sum of squares (L2 norm squared) of the data.
Dtype sumsq_data() const;
/// @brief Compute the sum of squares (L2 norm squared) of the diff.
Dtype sumsq_diff() const;

//重要成員變量
shared_ptr<SyncedMemory> data_;
shared_ptr<SyncedMemory> diff_;
shared_ptr<SyncedMemory> shape_data_;
vector<int> shape_;
int count_;
int capacity_;

Blob(const int num, const int channels, const int height, const int width) 構(gòu)造函數(shù)

Blob<Dtype>::Blob(const int num, const int channels, const int height,
    const int width)
  // capacity_ must be initialized before calling Reshape
  : capacity_(0) {
  Reshape(num, channels, height, width);
}

調(diào)用了void Reshape(const int num, const int channels, const int height,const int width);

Reshape 變維函數(shù),負(fù)責(zé)申請(qǐng)內(nèi)存

template <typename Dtype>
void Blob<Dtype>::Reshape(const vector<int>& shape) {
  CHECK_LE(shape.size(), kMaxBlobAxes);
  count_ = 1;
  shape_.resize(shape.size());
  if (!shape_data_ || shape_data_->size() < shape.size() * sizeof(int)) {
    shape_data_.reset(new SyncedMemory(shape.size() * sizeof(int)));
  }
  int* shape_data = static_cast<int*>(shape_data_->mutable_cpu_data());
  for (int i = 0; i < shape.size(); ++i) {
    CHECK_GE(shape[i], 0);
    CHECK_LE(shape[i], INT_MAX / count_) << "blob size exceeds INT_MAX";
    count_ *= shape[i];
    shape_[i] = shape[i];
    shape_data[i] = shape[i];
  }
  if (count_ > capacity_) {
    capacity_ = count_;
    data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
    diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
  }
}

Reshape 調(diào)用了SyncedMemory 進(jìn)行內(nèi)存申請(qǐng)登記父腕,申請(qǐng)的長(zhǎng)度為capacity_ * sizeof(Dtype)弱匪,其中capacity_ =count_青瀑,注意的是這階段同時(shí)申請(qǐng)了data_和diff_兩塊內(nèi)存登記璧亮,即前向的結(jié)果何梯度的內(nèi)存但并未真正申請(qǐng),SyncedMemory 中的head處于UNINITIALIZED未初始化狀態(tài)斥难。
到此枝嘶,caffe的內(nèi)存申請(qǐng)沒有真正運(yùn)行,僅僅是登記了需要的內(nèi)存的size
Blob中重要的成員函數(shù)cpu_data()哑诊、mutable_cpu_data()群扶、gpu_data()、mutable_gpu_data()等則是真正申請(qǐng)內(nèi)存并注明數(shù)據(jù)所在(cpu還是gpu)

template <typename Dtype>
const Dtype* Blob<Dtype>::gpu_data() const {
  CHECK(data_);
  return (const Dtype*)data_->gpu_data();
}

template <typename Dtype>
Dtype* Blob<Dtype>::mutable_gpu_data() {
  CHECK(data_);
  return static_cast<Dtype*>(data_->mutable_gpu_data());
}

通過返回是否常指針來限制讀寫權(quán)限镀裤。
再來分析一下Blob的Update()竞阐,用戶網(wǎng)絡(luò)參數(shù)的更新,不支持int和unsigned int

// The "update" method is used for parameter blobs in a Net, which are stored
// as Blob<float> or Blob<double> -- hence we do not define it for
// Blob<int> or Blob<unsigned int>.
template <> void Blob<unsigned int>::Update() { NOT_IMPLEMENTED; }
template <> void Blob<int>::Update() { NOT_IMPLEMENTED; }

template <typename Dtype>
void Blob<Dtype>::Update() {
  // We will perform update based on where the data is located.
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    // perform computation on CPU
    caffe_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->cpu_data()),
        static_cast<Dtype*>(data_->mutable_cpu_data()));
    break;
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    // perform computation on GPU
    caffe_gpu_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->gpu_data()),
        static_cast<Dtype*>(data_->mutable_gpu_data()));
#else
    NO_GPU;
#endif
    break;
  default:
    LOG(FATAL) << "Syncedmem not initialized.";
  }
}

最后分析void FromProto(const BlobProto& proto, bool reshape = true);
從磁盤加載之前導(dǎo)出的Blob暑劝,僅到內(nèi)存中

template <typename Dtype>
void Blob<Dtype>::FromProto(const BlobProto& proto, bool reshape) {
  if (reshape) {
    vector<int> shape;
    if (proto.has_num() || proto.has_channels() ||
        proto.has_height() || proto.has_width()) {
      // Using deprecated 4D Blob dimensions --
      // shape is (num, channels, height, width).
      shape.resize(4);
      shape[0] = proto.num();
      shape[1] = proto.channels();
      shape[2] = proto.height();
      shape[3] = proto.width();
    } else {
      shape.resize(proto.shape().dim_size());
      for (int i = 0; i < proto.shape().dim_size(); ++i) {
        shape[i] = proto.shape().dim(i);
      }
    }
    Reshape(shape);
  } else {
    CHECK(ShapeEquals(proto)) << "shape mismatch (reshape not set)";
  }
  // copy data
  Dtype* data_vec = mutable_cpu_data();
  if (proto.double_data_size() > 0) {
    CHECK_EQ(count_, proto.double_data_size());
    for (int i = 0; i < count_; ++i) {
      data_vec[i] = proto.double_data(i);
    }
  } else {
    CHECK_EQ(count_, proto.data_size());
    for (int i = 0; i < count_; ++i) {
      data_vec[i] = proto.data(i);
    }
  }
  if (proto.double_diff_size() > 0) {
    CHECK_EQ(count_, proto.double_diff_size());
    Dtype* diff_vec = mutable_cpu_diff();
    for (int i = 0; i < count_; ++i) {
      diff_vec[i] = proto.double_diff(i);
    }
  } else if (proto.diff_size() > 0) {
    CHECK_EQ(count_, proto.diff_size());
    Dtype* diff_vec = mutable_cpu_diff();
    for (int i = 0; i < count_; ++i) {
      diff_vec[i] = proto.diff(i);
    }
  }
}

Layer

至少有一個(gè)輸入Blob(Bottom Blob)和一個(gè)輸出Blob(Top Blob)骆莹,部分layer帶有權(quán)重(weight)和偏置(bais),有兩個(gè)運(yùn)算方向:前向和反向
重要的成員

//成員函數(shù) 
explicit Layer(const LayerParameter& param);
void SetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top);
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) = 0;
inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top);
inline void Backward(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom);
// Returns the vector of learnable parameter blobs.
vector<shared_ptr<Blob<Dtype> > >& blobs() 
{
    return blobs_;
}
//Returns the layer parameter.
const LayerParameter& layer_param() const { return layer_param_; }
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) = 0;
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
{
    // LOG(WARNING) << "Using CPU code as backup.";
    return Forward_cpu(bottom, top);
  }

//成員變量
/** The protobuf that stores the layer parameters */
LayerParameter layer_param_;
/** The phase: TRAIN or TEST */
Phase phase_;
/** The vector that stores the learnable parameters as a set of blobs. */
vector<shared_ptr<Blob<Dtype> > > blobs_;
/** Vector indicating whether to compute the diff of each param blob. */
vector<bool> param_propagate_down_;

/** The vector that indicates whether each top blob has a non-zero weight in
*  the objective function. */
vector<Dtype> loss_;

explicit Layer(const LayerParameter& param);
從LayerParameter加載配置担猛,既從model中加載幕垦,此過程會(huì)申請(qǐng)cpu內(nèi)存丢氢。

void SetUp(const vector<Blob<Dtype>>& bottom,const vector<Blob<Dtype>>& top);

*@簡(jiǎn)單實(shí)現(xiàn)通用層設(shè)置功能。
*
*@PARAM bottom 預(yù)輸入blob
*@PARAM top
*分配的但不成形的輸出斑點(diǎn)先改,通過整形來成形疚察。
*
*檢查底部和頂部斑點(diǎn)的數(shù)量是否正確。
*調(diào)用LaySt設(shè)置為各個(gè)層類型做特殊的層設(shè)置仇奶,
*隨后進(jìn)行整形以設(shè)置頂部斑點(diǎn)和內(nèi)部緩沖區(qū)的大小貌嫡。
*為任何非零損失權(quán)重設(shè)置損失權(quán)重乘數(shù)塊。
*此方法可能不會(huì)被重寫该溯。
void SetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
 {
    InitMutex();
    CheckBlobCounts(bottom, top);
    LayerSetUp(bottom, top);
    Reshape(bottom, top);
    SetLossWeights(top);
  }

*簡(jiǎn)短的做層特定的設(shè)置:你的層應(yīng)該實(shí)現(xiàn)這個(gè)功能以及重塑衅枫。
*
*@PARAM bottom
*預(yù)先輸入的輸入blob,其數(shù)據(jù)字段為此層存儲(chǔ)輸入數(shù)據(jù)朗伶。
*
*@PARAM top
*分配但不成形的輸出top
*
*此方法應(yīng)進(jìn)行一次性層特定設(shè)置弦撩。這包括閱讀
*并從<代碼> Layer-PARAMM< <代碼>中處理相關(guān)參數(shù)。
*設(shè)置頂部塊和內(nèi)部緩沖區(qū)的形狀應(yīng)在
*<代碼>整形/<代碼>论皆,它將在向前傳遞之前調(diào)用益楼。
*調(diào)整頂部斑點(diǎn)大小。
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) {}

layer比較簡(jiǎn)單点晴,就是具體實(shí)現(xiàn)各個(gè)層的功能
注意的是
layer_param_ 保存layer參數(shù)的protobuffer對(duì)象
blobs_ 保存內(nèi)部權(quán)值和偏置項(xiàng)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末感凤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子粒督,更是在濱河造成了極大的恐慌陪竿,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屠橄,死亡現(xiàn)場(chǎng)離奇詭異族跛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锐墙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門礁哄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人溪北,你說我怎么就攤上這事桐绒。” “怎么了之拨?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵茉继,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我蚀乔,道長(zhǎng)烁竭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任乙墙,我火速辦了婚禮颖变,結(jié)果婚禮上生均,老公的妹妹穿的比我還像新娘。我一直安慰自己腥刹,他們只是感情好马胧,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衔峰,像睡著了一般佩脊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上垫卤,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天威彰,我揣著相機(jī)與錄音,去河邊找鬼穴肘。 笑死歇盼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的评抚。 我是一名探鬼主播豹缀,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼慨代!你這毒婦竟也來了邢笙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤侍匙,失蹤者是張志新(化名)和其女友劉穎氮惯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體想暗,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妇汗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了江滨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铛纬。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡厌均,死狀恐怖唬滑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棺弊,我是刑警寧澤晶密,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站模她,受9級(jí)特大地震影響稻艰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侈净,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一尊勿、第九天 我趴在偏房一處隱蔽的房頂上張望僧凤。 院中可真熱鬧,春花似錦元扔、人聲如沸躯保。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)途事。三九已至,卻和暖如春擅羞,著一層夾襖步出監(jiān)牢的瞬間尸变,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工减俏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留召烂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓娃承,卻偏偏與公主長(zhǎng)得像骑晶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子草慧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容