caffe源码阅读(一) Blob
- 概述
Blob是caffe中的数据存储模块,其主要包含三类数据:
data:前向传播所用到的数据(到底是前向传播的权重,还是原始数据呢?)
diff:反向传播时的梯度,注意这个梯度还要和clip_gradient这个参数联系起来,不过这是后话了
shape:其实是为了把data和diff解释为n*c*h*w这样的形式,其实这两个数据本身是以一维数组的形式存储的
总的来说,blob这个模块实现对blob的初始化、解释,查询(可从前向后,亦可从后向前查询某个元素;可以返回数据的指针),检查(输入输出维度是否正确,通过一维偏移量来获得某个元素),复制,读写数据,更新数据(如完成权值和梯度的相减),检查梯度(通过L2 norm来判断是否要对梯度缩放后再用来更新权值) - blob.h
#ifndef CAFFE_BLOB_HPP_
#define CAFFE_BLOB_HPP_
#include <algorithm>
#include <string>
#include <vector>
#include "caffe/common.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/syncedmem.hpp"
#include "caffe/util/math_functions.hpp"
const int kMaxBlobAxes = 32;
namespace caffe {
/**
3. @brief A wrapper around SyncedMemory holders serving as the basic
4. computational unit through which Layer%s, Net%s, and Solver%s
5. interact.
6. BLOB是SyncedMemory的包裹器
7.
8. TODO(dox): more thorough description.
*/
template <typename Dtype>
class Blob {
public:
// 构造函数
Blob()
: data_(), diff_(), count_(0), capacity_(0) {} //这样看来,data_应该是存储的前向传播的权重
/// @brief Deprecated; use <code>Blob(const vector<int>& shape)</code>.
explicit Blob(const int num, const int channels, const int height,
const int width);
explicit Blob(const vector<int>& shape);// 推荐使用这个
// 成员函数,记住reshape主要是用来解释blob的,因为blob是一个一维数组
/// @brief Deprecated; use <code>Reshape(const vector<int>& shape)</code>.
void Reshape(const int num, const int channels, const int height,
const int width);
/**
9. @brief Change the dimensions of the blob, allocating new memory if
10. necessary.
11.
12. This function can be called both to create an initial allocation
13. of memory, and to adjust the dimensions of a top blob during Layer::Reshape
14. or Layer::Forward. When changing the size of blob, memory will only be
15. reallocated if sufficient memory does not already exist, and excess memory
16. will never be freed.
17.
18. Note that reshaping an input blob and immediately calling Net::Backward is
19. an error; either Net::Forward or Net::Reshape need to be called to
20. propagate the new input shape to higher layers.
*/
void Reshape(const vector<int>& shape); // 推荐使用这个
void Reshape(const BlobShape& shape);
void ReshapeLike(const Blob& other);
// 输出blob的形状,输出blob中元素的数目
inline string shape_string() const {
ostringstream stream;
for (int i = 0; i < shape_.size(); ++i) {
stream << shape_[i] << " "; // 四维,n,c,h,w
}
stream << "(" << count_ << ")"; 输出全部元素的个数
return stream.str();
}
inline const vector<int>& shape() const { return shape_; }
/**
21. @brief Returns the dimension of the index-th axis (or the negative index-th
22. axis from the end, if index is negative).
23.
24. @param index the axis index, which may be negative as it will be
25. "canonicalized" using CanonicalAxisIndex.
26. Dies on out of range index.
*/
// 计算从给定维度到最后一个维度的
inline int shape(int index) const {
return shape_[CanonicalAxisIndex(index)];
}
// 返回数据的维度
inline int num_axes() const { return shape_.size(); }
// 返回数据的所有维度的相乘,即数据的个数
inline int count() const { return count_; }
/**
27. @brief Compute the volume of a slice; i.e., the product of dimensions
28. among a range of axes.
29.
30. @param start_axis The first axis to include in the slice.
31.
32. @param end_axis The first axis to exclude from the slice.
*/
inline int count(int start_axis, int end_axis) const {
// 判断维度的索引是否在范围内
CHECK_LE(start_axis, end_axis);
CHECK_GE(start_axis, 0);
CHECK_GE(end_axis, 0);
CHECK_LE(start_axis, num_axes());
CHECK_LE(end_axis, num_axes());
int count = 1;
for (int i = start_axis; i < end_axis; ++i) {
count *= shape(i); //计算总的元素的个数
}
return count;
}
/**
33. @brief Compute the volume of a slice spanning from a particular first
34. axis to the final axis.
35.
36. @param start_axis The first axis to include in the slice.
*/
// 给定的维度到最后的维度之间包含的数据个数
inline int count(int start_axis) const {
return count(start_axis, num_axes());
}
/**
37. @brief Returns the 'canonical' version of a (usually) user-specified axis,
38. allowing for negative indexing (e.g., -1 for the last axis).
39.
40. @param axis_index the axis index.
41. If 0 <= index < num_axes(), return index.
42. If -num_axes <= index <= -1, return (num_axes() - (-index)),
43. e.g., the last axis index (num_axes() - 1) if index == -1,
44. the second to last if index == -2, etc.
45. Dies on out of range index.
*/
// 支持负数维度索引,负数表示从后往前,返回的是正确的维度索引(相当于将负数索引进行的转换)
inline int CanonicalAxisIndex(int axis_index) const {
// 判断是否在范围内[-numaxes, numaxes]
CHECK_GE(axis_index, -num_axes())
<< "axis " << axis_index << " out of range for " << num_axes()
<< "-D Blob with shape " << shape_string();
CHECK_LT(axis_index, num_axes())
<< "axis " << axis_index << " out of range for " << num_axes()
<< "-D Blob with shape " << shape_string();
if (axis_index < 0) {
return axis_index + num_axes();
}
return axis_index;
}
/// @brief Deprecated legacy shape accessor num: use shape(0) instead.
inline int num() const { return LegacyShape(0); }
/// @brief Deprecated legacy shape accessor channels: use shape(1) instead.
inline int channels() const { return LegacyShape(1); }
/// @brief Deprecated legacy shape accessor height: use shape(2) instead.
inline int height() const { return LegacyShape(2); }
/// @brief Deprecated legacy shape accessor width: use shape(3) instead.
inline int width() const { return LegacyShape(3); }
inline int LegacyShape(int index) const {
CHECK_LE(num_axes(), 4)// 检查blob的维度个数是不是小于4,也许以前的blob只有四维,但是现在的blob应该为了通用而采用了大于四维的方法
<< "Cannot use legacy accessors on Blobs with > 4 axes.";
CHECK_LT(index, 4);// 检查维度索引是不是小于4
CHECK_GE(index, -4);// 检查维度索引是不是大于-4
if (index >= num_axes() || index < -num_axes()) {
// Axis is out of range, but still in [0, 3] (or [-4, -1] for reverse
// indexing) -- this special case simulates the one-padding used to fill
// extraneous axes of legacy blobs.
return 1;
}
return shape(index);
}
// 计算一维线性偏移量
inline int offset(const int n, const int c = 0, const int h = 0,
const int w = 0) const {
CHECK_GE(n, 0);
CHECK_LE(n, num());
CHECK_GE(channels(), 0);
CHECK_LE(c, channels());
CHECK_GE(height(), 0);
CHECK_LE(h, height());
CHECK_GE(width(), 0);
CHECK_LE(w, width());
return ((n * channels() + c) * height() + h) * width() + w;
}
// 计算一维线性偏移量,只不过参数用的是vector<int>
inline int offset(const vector<int>& indices) const {
CHECK_LE(indices.size(), num_axes());
int offset = 0;
for (int i = 0; i < num_axes(); ++i) {
offset *= shape(i);
if (indices.size() > i) {
CHECK_GE(indices[i], 0);
CHECK_LT(indices[i], shape(i));
offset += indices[i];
}
}
return offset;
}
/**
46. @brief Copy from a source Blob.
47.
48. @param source the Blob to copy from
49. @param copy_diff if false, copy the data; if true, copy the diff
50. @param reshape if false, require this Blob to be pre-shaped to the shape
51. of other (and die otherwise); if true, Reshape this Blob to other's
52. shape if necessary
53. 从给定的blob进行复制,如果copy_diff=true则新的blob复制的是diff,如果reshape=true则改变新blob的形状,什么时候需要改变blob的形状?
*/
void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false,
bool reshape = false);
// 获取在内存下的数据(前向传播所用的数据),看起来是一个一个元素获取的
inline Dtype data_at(const int n, const int c, const int h,
const int w) const {
return cpu_data()[offset(n, c, h, w)];
}
// 获取在内存下的diff数据(反传数据)
inline Dtype diff_at(const int n, const int c, const int h,
const int w) const {
return cpu_diff()[offset(n, c, h, w)];
}
// 获取在内存下的数据(前向传播所用的数据)
inline Dtype data_at(const vector<int>& index) const {
return cpu_data()[offset(index)];
}
// 获取在内存下的diff数据(反传数据)
inline Dtype diff_at(const vector<int>& index) const {
return cpu_diff()[offset(index)];
}
// 同步内存shared_ptr(不明白share_ptr的可以自行百度,引用计数管理机制),这里好像获取data_的指针
// 然后赋值给某个智能指针,是不是data_的指针也在每次更新过程中在变化了?或者说,只是把第一次分配的内
// 存地址返回给某个智能指针,以后就不用再返回了
inline const shared_ptr<SyncedMemory>& data() const {
CHECK(data_);
return data_;
}
// 这里和上面一样,是随时在变,还是只分配一次内存
inline const shared_ptr<SyncedMemory>& diff() const {
CHECK(diff_);
return diff_;
}
// 属性
const Dtype* cpu_data() const; //是获取cpu上数据的指针吗?
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();
// 计算,用于更新权重,前向数据加上梯度(可正可负)
Y=alpha∗X+beta∗Y
void Update();
// 从protobuf序列化文件读取blob对象,这个是为了snapshot或者fine-tuning用的吗?
void FromProto(const BlobProto& proto, bool reshape = true);
// 将对象序列化为protobuf文件,同上
void ToProto(BlobProto* proto, bool write_diff = false) const;
/// @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;
// 如果L2 norm squared或L1 norm大于某个值,那么需要为所有的元素乘上一个缩放因子scale_factor
/// @brief Scale the blob data by a constant factor.
void scale_data(Dtype scale_factor);
/// @brief Scale the blob diff by a constant factor.
void scale_diff(Dtype scale_factor);
/**
54. @brief Set the data_ shared_ptr to point to the SyncedMemory holding the
55. data_ of Blob other -- useful in Layer%s which simply perform a copy
56. in their Forward pass.
57.
58. This deallocates the SyncedMemory holding this Blob's data_, as
59. shared_ptr calls its destructor when reset with the "=" operator.
*/
void ShareData(const Blob& other);
/**
60. @brief Set the diff_ shared_ptr to point to the SyncedMemory holding the
61. diff_ of Blob other -- useful in Layer%s which simply perform a copy
62. in their Forward pass.
63.
64. This deallocates the SyncedMemory holding this Blob's diff_, as
65. shared_ptr calls its destructor when reset with the "=" operator.
66. 将别的blob的data和响应的diff指针给这个Blob,实现数据的共享。
67. 同时需要注意的是这个操作会引起这个Blob里面的SyncedMemory被释放,
68. 因为shared_ptr指针被用=重置的时候回调用响应的析构器。
69. 为什么要共享?是因为反向传播时要用到相邻两层的信息吗,所以共享一般都发生在相邻两层之间
*/
void ShareDiff(const Blob& other);
// 判断形状是否相等,Blob和BlobProto有什么区别?
bool ShapeEquals(const BlobProto& other);
protected:
// 前向传播的数据
shared_ptr<SyncedMemory> data_;
// diff是反向传播的数据
shared_ptr<SyncedMemory> diff_;
// 旧的形状数据
shared_ptr<SyncedMemory> shape_data_;
// 新的形状数据
vector<int> shape_;
// 数据的个数
int count_;
// 容量
int capacity_;
DISABLE_COPY_AND_ASSIGN(Blob);
}; // class Blob
} // namespace caffe
#endif // CAFFE_BLOB_HPP_
- blob.cpp
#include <climits>
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/syncedmem.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
// reshape 的具体实现
// 过时的方法最终是调用的新的reshape方法
template <typename Dtype>
void Blob<Dtype>::Reshape(const int num, const int channels, const int height,
const int width) {
vector<int> shape(4);
shape[0] = num;
shape[1] = channels;
shape[2] = height;
shape[3] = width;
Reshape(shape);
}
// reshape 的具体实现
template <typename Dtype>
void Blob<Dtype>::Reshape(const vector<int>& shape) {
CHECK_LE(shape.size(), kMaxBlobAxes); //是否小于规定的最大BLOB的维度(35维)能达到35维?
count_ = 1;
shape_.resize(shape.size());// 首先将大小设置为vector<int> shape_; 即新的形状数据的大小
if (!shape_data_ || shape_data_->size() < shape.size() * sizeof(int)) {
shape_data_.reset(new SyncedMemory(shape.size() * sizeof(int)));// shared_ptr<SyncedMemory> shape_data_;
}
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到新的和旧的形状数据
shape_[i] = shape[i]; //到底什么时候会更新权重的维度?
shape_data[i] = shape[i]; // shape_data有什么用?
}
// 判断是否大于存储的容量,为什么要判断存储的容量,难道真是在最开始时逐层更新blob的?
// 还有一种可能,是不是更新cpu上的blob的形状?
if (count_ > capacity_) {
capacity_ = count_;
// 重新分配内存
data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
}
}
// 所谓的reshape实际上就仅仅是复制了shape的数据而已
// 在调用的时候自动乘以shape的数据就可以得到数据,有点tricky
template <typename Dtype>
void Blob<Dtype>::Reshape(const BlobShape& shape) {
// 维度是否小于35
CHECK_LE(shape.dim_size(), kMaxBlobAxes);
// 复制形状数据
vector<int> shape_vec(shape.dim_size());
for (int i = 0; i < shape.dim_size(); ++i) {
shape_vec[i] = shape.dim(i);
}
// 调用新的reshape函数
Reshape(shape_vec);
}
template <typename Dtype>
void Blob<Dtype>::ReshapeLike(const Blob<Dtype>& other) {
Reshape(other.shape());
}
template <typename Dtype>
Blob<Dtype>::Blob(const int num, const int channels, const int height,
const int width)
// capacity_ must be initialized before calling Reshape
// 技巧,先初始化容量为0,然后用reshape来分配内存了
: capacity_(0) {
Reshape(num, channels, height, width);
}
template <typename Dtype>
Blob<Dtype>::Blob(const vector<int>& shape)
// capacity_ must be initialized before calling Reshape
: capacity_(0) { // 默认先构造capacity_为0,而后再用shape来赋值
Reshape(shape);
}
template <typename Dtype>
const int* Blob<Dtype>::gpu_shape() const {
CHECK(shape_data_);
// shared_ptr<SyncedMemory> shape_data_;
// 因此也分gpu_data和cpu_data
return (const int*)shape_data_->gpu_data(); //返回gpu上的指针??还是说将数据放大gpu上去
}
template <typename Dtype>
const Dtype* Blob<Dtype>::cpu_data() const {
CHECK(data_);
/ shared_ptr<SyncedMemory> data_;
return (const Dtype*)data_->cpu_data(); //返回cpu上的指针??还是说将数据放大cpu上去
}
template <typename Dtype>
void Blob<Dtype>::set_cpu_data(Dtype* data) {
CHECK(data);
data_->set_cpu_data(data); // 将数据放大cpu上去?
}
template <typename Dtype>
const Dtype* Blob<Dtype>::gpu_data() const {
CHECK(data_);
return (const Dtype*)data_->gpu_data();
}
template <typename Dtype>
const Dtype* Blob<Dtype>::cpu_diff() const {
CHECK(diff_);
return (const Dtype*)diff_->cpu_data();
}
template <typename Dtype>
const Dtype* Blob<Dtype>::gpu_diff() const {
CHECK(diff_);
return (const Dtype*)diff_->gpu_data();
}
template <typename Dtype>
Dtype* Blob<Dtype>::mutable_cpu_data() {
CHECK(data_);
return static_cast<Dtype*>(data_->mutable_cpu_data()); // 和cpu上的数据交换?
}
template <typename Dtype>
Dtype* Blob<Dtype>::mutable_gpu_data() {
CHECK(data_);
return static_cast<Dtype*>(data_->mutable_gpu_data());
}
template <typename Dtype>
Dtype* Blob<Dtype>::mutable_cpu_diff() {
CHECK(diff_);
return static_cast<Dtype*>(diff_->mutable_cpu_data());
}
template <typename Dtype>
Dtype* Blob<Dtype>::mutable_gpu_diff() {
CHECK(diff_);
return static_cast<Dtype*>(diff_->mutable_gpu_data());
}
// 将其他blob的数据复制到当前的blob中去
template <typename Dtype>
void Blob<Dtype>::ShareData(const Blob& other) {
CHECK_EQ(count_, other.count());
data_ = other.data();
}
// 将其他blob的diff数据复制到当前的blob中去
template <typename Dtype>
void Blob<Dtype>::ShareDiff(const Blob& other) {
CHECK_EQ(count_, other.count());
diff_ = other.diff();
}
// 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; }
// Update是计算data=-1 * diff + data
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
// axpby即alpha * x plus beta *y 这个含义,blas的函数命名真是见名知意
// template <> void caffe_axpy<float>(const int N, const float alpha, const float* X, float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }
// caffe_axpy计算的是Y=alpha * X + Y ,其中alpha=-1了这里
// 存储的时候用到了mutable_cpu_data,防止其他线程访问
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
// Y=alpha * X + Y ,其中alpha=-1了这里
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.";
}
}
template <> unsigned int Blob<unsigned int>::asum_data() const {
NOT_IMPLEMENTED;
return 0;
}
template <> int Blob<int>::asum_data() const {
NOT_IMPLEMENTED;
return 0;
}
// 计算data的L1范数
template <typename Dtype>
Dtype Blob<Dtype>::asum_data() const {
if (!data_) { return 0; }
switch (data_->head()) {
case SyncedMemory::HEAD_AT_CPU:
return caffe_cpu_asum(count_, cpu_data());
case SyncedMemory::HEAD_AT_GPU:
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
{
Dtype asum;
caffe_gpu_asum(count_, gpu_data(), &asum);
return asum;
}
#else
NO_GPU;
#endif
case SyncedMemory::UNINITIALIZED:
return 0;
default:
LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
}
return 0;
}
template <> unsigned int Blob<unsigned int>::asum_diff() const {
NOT_IMPLEMENTED;
return 0;
}
template <> int Blob<int>::asum_diff() const {
NOT_IMPLEMENTED;
return 0;
}
// 计算diff的L1范数
template <typename Dtype>
Dtype Blob<Dtype>::asum_diff() const {
if (!diff_) { return 0; }
switch (diff_->head()) {
case SyncedMemory::HEAD_AT_CPU:
return caffe_cpu_asum(count_, cpu_diff());
case SyncedMemory::HEAD_AT_GPU:
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
{
Dtype asum;
caffe_gpu_asum(count_, gpu_diff(), &asum);
return asum;
}
#else
NO_GPU;
#endif
case SyncedMemory::UNINITIALIZED:
return 0;
default:
LOG(FATAL) << "Unknown SyncedMemory head state: " << diff_->head();
}
return 0;
}
template <> unsigned int Blob<unsigned int>::sumsq_data() const {
NOT_IMPLEMENTED;
return 0;
}
template <> int Blob<int>::sumsq_data() const {
NOT_IMPLEMENTED;
return 0;
}
// 计算sum of square of data(L2范数)
template <typename Dtype>
Dtype Blob<Dtype>::sumsq_data() const {
Dtype sumsq;
const Dtype* data;
if (!data_) { return 0; }
switch (data_->head()) {
case SyncedMemory::HEAD_AT_CPU:
data = cpu_data();
sumsq = caffe_cpu_dot(count_, data, data);
break;
case SyncedMemory::HEAD_AT_GPU:
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
data = gpu_data();
caffe_gpu_dot(count_, data, data, &sumsq);
#else
NO_GPU;
#endif
break;
case SyncedMemory::UNINITIALIZED:
return 0;
default:
LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
}
return sumsq;
}
template <> unsigned int Blob<unsigned int>::sumsq_diff() const {
NOT_IMPLEMENTED;
return 0;
}
template <> int Blob<int>::sumsq_diff() const {
NOT_IMPLEMENTED;
return 0;
}
// sum of square of diff
template <typename Dtype>
Dtype Blob<Dtype>::sumsq_diff() const {
Dtype sumsq;
const Dtype* diff;
if (!diff_) { return 0; }
switch (diff_->head()) {
case SyncedMemory::HEAD_AT_CPU:
diff = cpu_diff();
sumsq = caffe_cpu_dot(count_, diff, diff);
break;
case SyncedMemory::HEAD_AT_GPU:
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
diff = gpu_diff();
caffe_gpu_dot(count_, diff, diff, &sumsq);
break;
#else
NO_GPU;
#endif
case SyncedMemory::UNINITIALIZED:
return 0;
default:
LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
}
return sumsq;
}
template <> void Blob<unsigned int>::scale_data(unsigned int scale_factor) {
NOT_IMPLEMENTED;
}
template <> void Blob<int>::scale_data(int scale_factor) {
NOT_IMPLEMENTED;
}
// 将data部分乘以一个因子scale_factor
template <typename Dtype>
void Blob<Dtype>::scale_data(Dtype scale_factor) {
Dtype* data;
if (!data_) { return; }
switch (data_->head()) {
case SyncedMemory::HEAD_AT_CPU:
data = mutable_cpu_data();
caffe_scal(count_, scale_factor, data);
return;
case SyncedMemory::HEAD_AT_GPU:
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
data = mutable_gpu_data();
caffe_gpu_scal(count_, scale_factor, data);
return;
#else
NO_GPU;
#endif
case SyncedMemory::UNINITIALIZED:
return;
default:
LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
}
}
template <> void Blob<unsigned int>::scale_diff(unsigned int scale_factor) {
NOT_IMPLEMENTED;
}
template <> void Blob<int>::scale_diff(int scale_factor) {
NOT_IMPLEMENTED;
}
// 将diff部分乘以一个因子sacle_factor
template <typename Dtype>
void Blob<Dtype>::scale_diff(Dtype scale_factor) {
Dtype* diff;
if (!diff_) { return; }
switch (diff_->head()) {
case SyncedMemory::HEAD_AT_CPU:
diff = mutable_cpu_diff();
caffe_scal(count_, scale_factor, diff);
return;
case SyncedMemory::HEAD_AT_GPU:
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
diff = mutable_gpu_diff();
caffe_gpu_scal(count_, scale_factor, diff);
return;
#else
NO_GPU;
#endif
case SyncedMemory::UNINITIALIZED:
return;
default:
LOG(FATAL) << "Unknown SyncedMemory head state: " << diff_->head();
}
}
// 两个blob是否shape一样
template <typename Dtype>
bool Blob<Dtype>::ShapeEquals(const BlobProto& other) {
// 判断是否是旧的blob
if (other.has_num() || other.has_channels() ||
other.has_height() || other.has_width()) {
// Using deprecated 4D Blob dimensions --
// shape is (num, channels, height, width).
// Note: we do not use the normal Blob::num(), Blob::channels(), etc.
// methods as these index from the beginning of the blob shape, where legacy
// parameter blobs were indexed from the end of the blob shape (e.g., bias
// Blob shape (1 x 1 x 1 x N), IP layer weight Blob shape (1 x 1 x M x N)).
return shape_.size() <= 4 &&
LegacyShape(-4) == other.num() &&
LegacyShape(-3) == other.channels() &&
LegacyShape(-2) == other.height() &&
LegacyShape(-1) == other.width();
}
// 如果不是旧的blob则直接判断
vector<int> other_shape(other.shape().dim_size());
for (int i = 0; i < other.shape().dim_size(); ++i) {
other_shape[i] = other.shape().dim(i);
}
return shape_ == other_shape;
}
// 从别的blob进行复制
template <typename Dtype>
void Blob<Dtype>::CopyFrom(const Blob& source, bool copy_diff, bool reshape) {
if (source.count() != count_ || source.shape() != shape_) {
if (reshape) {
ReshapeLike(source);// 复制shape数据
} else {
LOG(FATAL) << "Trying to copy blobs of different sizes.";
}
}
switch (Caffe::mode()) {
case Caffe::GPU:
// GPU复制diff
if (copy_diff) {
// 这都用 template <> void caffe_copy<float>(const int N, const float* X, float* Y) { cblas_scopy(N, X, 1, Y, 1); }
// 干嘛要用BLAS里面的运算来复制,真是多余...
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:
// CPU复制diff
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>::FromProto(const BlobProto& proto, bool reshape) {
// copy shape
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).
// 如果是旧的blob直接转换为新的blob中的shape数据
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);// 复制shape数据到当前blob
} else {
CHECK(ShapeEquals(proto)) << "shape mismatch (reshape not set)";
}
// copy data
Dtype* data_vec = mutable_cpu_data();// 获取当前的blob在内存上的数据指针,该指针是互斥的
if (proto.double_data_size() > 0) {// data
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);
}
}
// copy diff
if (proto.double_diff_size() > 0) {// diff
CHECK_EQ(count_, proto.double_diff_size());
Dtype* diff_vec = mutable_cpu_diff();// 获取当前的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);
}
}
}
// BlobProto和BlobShape是protobuf定义的,其中一些函数是自动生成的
// mutable_shape、add_dim、clear_double_data、clear_double_diff、add_double_data
// add_double_diff等
// 见src/caffe/proto/caffe.proto
template <>
void Blob<double>::ToProto(BlobProto* proto, bool write_diff) const {
proto->clear_shape();
// 存shape
for (int i = 0; i < shape_.size(); ++i) {
proto->mutable_shape()->add_dim(shape_[i]);
}
proto->clear_double_data();
proto->clear_double_diff();
// 存data
const double* data_vec = cpu_data();
for (int i = 0; i < count_; ++i) {
proto->add_double_data(data_vec[i]);
}
// 存diff
if (write_diff) {
const double* diff_vec = cpu_diff();
for (int i = 0; i < count_; ++i) {
proto->add_double_diff(diff_vec[i]);
}
}
}
template <>
void Blob<float>::ToProto(BlobProto* proto, bool write_diff) const {
proto->clear_shape();
for (int i = 0; i < shape_.size(); ++i) {
proto->mutable_shape()->add_dim(shape_[i]);
}
proto->clear_data();
proto->clear_diff();
const float* data_vec = cpu_data();
for (int i = 0; i < count_; ++i) {
proto->add_data(data_vec[i]);
}
if (write_diff) {
const float* diff_vec = cpu_diff();
for (int i = 0; i < count_; ++i) {
proto->add_diff(diff_vec[i]);
}
}
}
INSTANTIATE_CLASS(Blob);
template class Blob<int>;
template class Blob<unsigned int>;
} // namespace caffe
本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦:
- Markdown和扩展Markdown简洁的语法
- 代码块高亮
- 图片链接和图片上传
- LaTex数学公式
- UML序列图和流程图
- 离线写博客
- 导入导出Markdown文件
- 丰富的快捷键
快捷键
- 加粗
Ctrl + B
- 斜体
Ctrl + I
- 引用
Ctrl + Q
- 插入链接
Ctrl + L
- 插入代码
Ctrl + K
- 插入图片
Ctrl + G
- 提升标题
Ctrl + H
- 有序列表
Ctrl + O
- 无序列表
Ctrl + U
- 横线
Ctrl + R
- 撤销
Ctrl + Z
- 重做
Ctrl + Y
Markdown及扩展
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。 —— [ 维基百科 ]
使用简单的符号标识不同的标题,将某些文字标记为粗体或者斜体,创建一个链接等,详细语法参考帮助?。
本编辑器支持 Markdown Extra , 扩展了很多好用的功能。具体请参考Github.
表格
Markdown Extra 表格语法:
项目 | 价格 |
---|---|
Computer | $1600 |
Phone | $12 |
Pipe | $1 |
可以使用冒号来定义对齐方式:
项目 | 价格 | 数量 |
---|---|---|
Computer | 1600 元 | 5 |
Phone | 12 元 | 12 |
Pipe | 1 元 | 234 |
定义列表
-
Markdown Extra 定义列表语法:
项目1
项目2
- 定义 A
- 定义 B 项目3
- 定义 C
-
定义 D
定义D内容
代码块
代码块语法遵循标准markdown代码,例如:
@requires_authorization
def somefunc(param1='', param2=0):
'''A docstring'''
if param1 > param2: # interesting
print 'Greater'
return (param2 - param1 + 1) or None
class SomeClass:
pass
>>> message = '''interpreter
... prompt'''
脚注
生成一个脚注1.
目录
用 [TOC]
来生成目录:
数学公式
使用MathJax渲染LaTex 数学公式,详见math.stackexchange.com.
- 行内公式,数学公式为: Γ(n)=(n−1)!∀n∈N 。
- 块级公式:
更多LaTex语法请参考 这儿.
UML 图:
可以渲染序列图:
或者流程图:
离线写博客
即使用户在没有网络的情况下,也可以通过本编辑器离线写博客(直接在曾经使用过的浏览器中输入write.blog.csdn.net/mdeditor即可。Markdown编辑器使用浏览器离线存储将内容保存在本地。
用户写博客的过程中,内容实时保存在浏览器缓存中,在用户关闭浏览器或者其它异常情况下,内容不会丢失。用户再次打开浏览器时,会显示上次用户正在编辑的没有发表的内容。
博客发表后,本地缓存将被删除。
用户可以选择 把正在写的博客保存到服务器草稿箱,即使换浏览器或者清除缓存,内容也不会丢失。
注意:虽然浏览器存储大部分时候都比较可靠,但为了您的数据安全,在联网后,请务必及时发表或者保存到服务器草稿箱。
浏览器兼容
- 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
- IE9以下不支持
- IE9,10,11存在以下问题
- 不支持离线功能
- IE9不支持文件导入导出
- IE10不支持拖拽文件导入
- 这里是 脚注 的 内容. ↩