Caffe_blob
1.基本數據結構
Blob為模板類,可以理解為四維數組,n * c * h * w的結構,Layer內為blob輸入data和diff,Layer間的blob為學習的參數.內部封裝了SyncedMemory類,該類負責存儲分配和主機與設備的同步
protected:
shared_ptr<SyncedMemory> data_; // data指針
shared_ptr<SyncedMemory> diff_; // diff指針
vector<int> shape_; // blob形狀
int count_; // blob的nchw
// 當前的Blob容量,當Blob reshape后count> capacity_時,capacity_ = count_;
// 重新new 然后 reset data和 diff
int capacity_;
2.常用函數
Blob類中常用的函數如下所示
Blob<float>test;
//explicit關鍵字的作用是禁止單參數構造函數的隱式轉換
explicit Blob(const int num, const int channels, const int height,
const int width);
test.shape_string();//初始為空 0 0 0 0
//Reshape函數將num,channels,height,width傳遞給vector shape_
test.Reshape(1,2,3,4);// shape_string() 1,2,3,4
test.shape(i);// NCHW
test.count(int start_axis,int end_axis); // start_axis---end_axis .x* shape[i]
test.count();// nchw count(1) chw count(2) hw.....
//shared_ptr<SyncedMemory> data_->cpu_data();
const float* data = test.cpu_data();
const float* diff = test.cpu_diff();
float* data_1 = test.mutable_cpu_data();//mutable修飾的表示可以修改內部值
float* diff_1 = test.mutable_cpu_diff();
test.asum_data();//求和 L1范數
test.sumsq_data();//平方和 L2范數
test.Update();//data = data-diff;
a.ToProto(BlobProto& bp,true/false);//(FromProto)
// if < 0 ,return num_axis()+axis_index;//索引序列
int index = a.CanonicalAxisIndex(int axis_index);
int offset(n,c,h,w);//((n*channels()+c)*height()+h)*width()+w
float data_at(n,c,h,w);//return cpu_data()[offset(n,c,h,w)];
float diff_at(n,c,h,w);//return cpu_diff()[offset(n,c,h,w)];
inline const shared_ptr<SyncedMemory>& data() const{return _data};
void scale_data(Dtype scale_factor);// data乘以一個標量。同理 scale_diff();
void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false,
bool reshape = false); // copy_diff是否復制diff
3.寫入磁盤操作
//Blob內部值寫入到磁盤
Blob<float>a;
a.Reshape(1,2,3,4);
const int count = a.count();
for (size_t i = 0; i < count; i++) {
a[i] = i;//init the test Blob
}
BlobProto bp,bp2;
a.ToProto(&bp,true);//寫入data和diff到bp中
WriteProtoToBinaryFile(bp,"a.blob");//寫入磁盤
ReadProtoFromBinaryFile("a.blob",&bp2);//從磁盤讀取blob
Blob<float>b;
b.FromProto(bp2,true);//序列化對象bp2中克隆b,完整克隆
for (size_t n = 0; n < b.num(); n++) {
for (size_t c = 0; c < b.channels(); c++) {
for (size_t h = 0; h < b.height(); h++) {
for (size_t w = 0; w < b.width(); w++) {
cout<<"b["<<n<<"]["<<c<<"]["<<h<<"]["<<w<<"]["<<w<<"]="<<
b[(((n*b.channels()+c)*b.height)+h)*b.width()+w]<<endl;
//(((n*c+ci)*h+hi)*w+wi)
}
}
}
}
4.部分函數的具體實現
本部分的實現未考慮參數是否合理。一般操作blob需要分CPU和GPU,采用math_functions具體計算
template <typename Dtype>
void Blob<Dtype>::Reshape(const vector<int>& shape){//reshape操作
count_ = 1;//初始count_ NCHW;
shape_.resize(shape.size());
for (size_t i = 0; i < shape.size(); i++) {
count_ *= shape[i];
shape_[i] = shape[i];
if (count_ > capacity_) { //reshape的size大於了目前的最大容量
capacity_ = count_;
data_.reset(new SyncedMemory(capacity_*sizeof(Dtype)));
diff_.reset(new SyncedMemory(capacity_*sizeof(Dtype)));
}
}
}
template <typename Dtype>
void Blob<Dtype>::Reshape(int n,int c,int h ,int w){//reshape操作
vector<int>shape(4);
shape[0] = n;
shape[1] = c;
shape[2] = h;
shape[3] = w;
Reshape(shape);
}
template <typename Dtype>
const Dtype* Blob<Dtype>::cpu_data(){
//實際調用的shared_ptr<SyncedMemory>data_->cpu_data();,同理cpu_diff();
CHECK(data_);
return (const Dtype*)data_->cpu_data();
}
template <typename Dtype>
void Blob<Dtype>::Updata(){ //data = data-diff;需要判斷cpu OR gpu
switch (data_->head()) {
case SyncedMemory::HEAD_AT_CPU:
caffe_axpy<Dtype>(count_,Dtype(-1),
static_cast<const<Dtype*>(diff_->cpu_data()),
static_cast<Dtype*>(data_->mutable_cpu_data()));
}
case SyncedMemory::HEAD_AT_GPU://在gpu或者CPU/GPU已經同步
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
caffe_gpu_axpy<Dtype>(count_.Dtype(-1),
static_cast<const<Dtype*>(diff_->gpu_data()),
static_cast<Dtype*>(data_->mutable_gpu_data()))
}
template <typename Dtype> //從source 拷貝數據,copy_diff控制是拷貝diff還是data
void Blob<Dtype>::CopyFrom(const Blob& source, bool copy_diff, bool reshape) {
if (source.count() != count_ || source.shape() != shape_) {
if (reshape) {
ReshapeLike(source);
}
}
switch (Caffe::mode()) {
case Caffe::GPU:
if (copy_diff) { //copy diff
caffe_copy(count_, source.gpu_diff(),
static_cast<Dtype*>(diff_->mutable_gpu_data()));
} else {
caffe_copy(count_, source.gpu_data(),
static_cast<Dtype*>(data_->mutable_gpu_data()));
}
break;
case Caffe::CPU:
if (copy_diff) {
caffe_copy(count_, source.cpu_diff(),
static_cast<Dtype*>(diff_->mutable_cpu_data()));
} else {
caffe_copy(count_, source.cpu_data(),
static_cast<Dtype*>(data_->mutable_cpu_data()));
}
break;
default:
LOG(FATAL) << "Unknown caffe mode.";
}
}
template <typename Dtype>
void Blob<Dtype>::ToProto(BlobProto* proto,bool write_diff){
proto->clear_shape();
for (size_t i = 0; i < shaoe_.size(); i++) {
proto->mutable_shape()->add_dim(shape_[i]);
}
proto->clear_data();
proto->clear_diff();
const Dtype* data_vec = cpu_data();
for (size_t i = 0; i < count_; i++) {
proto->add_data(data_vec[i]);//data寫入proto
}
if (write_diff) {
const Dtype* diff_vec = cpu_diff();
for (size_t i = 0; i < count_; i++) {
proto->add_diff(diff_vec[i]);//diff寫入proto
}
}
}
5.說明
/*Blob作為一個最基礎的類,其中構造函數開辟一個內存空間來存儲數據,Reshape
函數在Layer中的reshape或者forward操作中來調整top的輸出維度。同時在改變Blob
大小時, 內存將會被重新分配如果內存大小不夠了,並且額外的內存將不會被釋放。
對input的blob進行reshape, 若立馬調用Net::Backward是會出錯的,因為reshape
之后,要么Net::forward或者Net::Reshape就會被調用來將新的input shape傳播
到高層 */