Caffe框架源码剖析(2)—训练网络
来源:互联网 发布:淘宝优惠券图标 编辑:程序博客网 时间:2024/05/17 05:14
中间因为工程开发等杂七杂八原因暂停了Caffe源码分析,现在继续补上。
上篇分析在函数 train() 中建立了网络,接下来就是进入训练网络步骤了。
在函数train()中,使用前一步创建好的solver智能指针对象调用函数Solve(),
- int train()
- {
- ...
- // 创建solver
- shared_ptr<caffe::Solver<float> >
- solver(caffe::SolverRegistry<float>::CreateSolver(solver_param));
- ...
- LOG(INFO) << "Starting Optimization";
- // 开始训练网络
- solver->Solve();
- LOG(INFO) << "Optimization Done.";
- }
在成员函数Solve()内部,
- template <typename Dtype>
- void Solver<Dtype>::Solve(const char* resume_file)
- {
- LOG(INFO) << "Solving " << net_->name();
- LOG(INFO) << "Learning Rate Policy: " << param_.lr_policy();
- // 开始迭代
- Step(param_.max_iter() - iter_);
- LOG(INFO) << "Optimization Done.";
- }
- template <typename Dtype>
- void Solver<Dtype>::Step(int iters)
- {
- // 起始迭代步数
- const int start_iter = iter_;
- // 终止迭代步数
- const int stop_iter = iter_ + iters;
- // 判断是否已经完成设定步数
- while (iter_ < stop_iter)
- {
- // 将net_中的Blob梯度参数置为零
- net_->ClearParamDiffs();
- ...
- // accumulate the loss and gradient
- Dtype loss = 0;
- for (int i = 0; i < param_.iter_size(); ++i)
- {
- // 正向传导和反向传导,并计算loss
- loss += net_->ForwardBackward();
- }
- loss /= param_.iter_size();
- // 为了输出结果平滑,将临近的average_loss个loss数值进行平均,存储在成员变量smoothed_loss_中
- UpdateSmoothedLoss(loss, start_iter, average_loss);
- // BP算法更新权重
- ApplyUpdate();
- // Increment the internal iter_ counter -- its value should always indicate
- // the number of times the weights have been updated.
- ++iter_;
- }
- }
while循环中先调用了网络类Net::ForwardBackward()成员函数进行正向传导和反向传导,并计算loss
- template <typename Dtype>
- class Net
- {
- Dtype ForwardBackward()
- {
- Dtype loss;
- Forward(&loss); // 正向传导
- Backward(); // 反向传导
- return loss;
- }
- }
其中正向传导函数Forward()调用了ForwardFromTo(int start, int end)函数
- template <typename Dtype>
- Dtype Net<Dtype>::ForwardFromTo(int start, int end)
- {
- CHECK_GE(start, 0);
- CHECK_LT(end, layers_.size());
- Dtype loss = 0;
- // 逐层传导
- for (int i = start; i <= end; ++i)
- {
- // 虽然Forward()不是虚函数,但是包装了虚函数Forward_cpu()和Forward_gpu(),不同层有不同的计算方法
- Dtype layer_loss = layers_[i]->Forward(bottom_vecs_[i], top_vecs_[i]);
- // 累加loss(非loss层都会返回0)
- loss += layer_loss;
- if (debug_info_)
- {
- ForwardDebugInfo(i);
- }
- }
- return loss;
- }
虽然Forward()不是虚函数,但是它包装了虚函数Forward_cpu()和Forward_gpu(),分别对应CPU版本和GPU版本。其中Forward_cpu()为父类Layer的纯虚函数,必须被子类重载。而Forward_gpu()在父类Layer中的实现为直接调用Forward_cpu(),于是该虚函数的实现为可选。总的来说,正因为这两个虚函数,所以不同层有不同的正向传导计算方法。
- template <typename Dtype>
- inline Dtype Layer<Dtype>::Forward(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top)
- {
- // 锁住互斥量forward_mutex_
- Lock();
- Dtype loss = 0;
- Reshape(bottom, top);
- switch (Caffe::mode())
- {
- case Caffe::CPU:
- // 调用虚函数Forward_cpu
- Forward_cpu(bottom, top);
- for (int top_id = 0; top_id < top.size(); ++top_id)
- {
- if (!this->loss(top_id)) { continue; }
- const int count = top[top_id]->count();
- const Dtype* data = top[top_id]->cpu_data();
- const Dtype* loss_weights = top[top_id]->cpu_diff();
- loss += caffe_cpu_dot(count, data, loss_weights);
- }
- break;
- case Caffe::GPU:
- // 调用虚函数Forward_gpu
- Forward_gpu(bottom, top);
- #ifndef CPU_ONLY
- for (int top_id = 0; top_id < top.size(); ++top_id)
- {
- if (!this->loss(top_id)) { continue; }
- const int count = top[top_id]->count();
- const Dtype* data = top[top_id]->gpu_data();
- const Dtype* loss_weights = top[top_id]->gpu_diff();
- Dtype blob_loss = 0;
- caffe_gpu_dot(count, data, loss_weights, &blob_loss);
- loss += blob_loss;
- }
- #endif
- break;
- default:
- LOG(FATAL) << "Unknown caffe mode.";
- }
- // 解锁互斥量forward_mutex_
- Unlock();
- return loss;
- }
反向传导函数Backward()调用了BackwardFromTo(int start, int end)函数
- template <typename Dtype>
- void Net<Dtype>::Backward()
- {
- BackwardFromTo(layers_.size() - 1, 0);
- }
- template <typename Dtype>
- void Net<Dtype>::BackwardFromTo(int start, int end)
- {
- CHECK_GE(end, 0);
- CHECK_LT(start, layers_.size());
- // 倒过来逐层传导
- for (int i = start; i >= end; --i)
- {
- if (layer_need_backward_[i])
- {
- // 与正向传导函数类似,虽然Backward()不是虚函数,但是包装了虚函数Backward_cpu()和Backward_gpu(),因此不同层有不同的计算方法
- // 注意反向传导比正向传导多了一个参数bottom_need_backward_。在实现反向传导时,首先判断当前层是否需要反向传导的层,不需要则直接返回
- layers_[i]->Backward(top_vecs_[i], bottom_need_backward_[i], bottom_vecs_[i]);
- if (debug_info_)
- {
- BackwardDebugInfo(i);
- }
- }
- }
- }
正向传导和反向传导结束后,再调用SGDSolver::ApplyUpdate()成员函数进行权重更新。
- template <typename Dtype>
- void SGDSolver<Dtype>::ApplyUpdate()
- {
- // 获取当前学习速率
- Dtype rate = GetLearningRate();
- if (this->param_.display() && this->iter_ % this->param_.display() == 0)
- {
- LOG(INFO) << "Iteration " << this->iter_ << ", lr = " << rate;
- }
- // 在计算当前梯度的时候,如果该值超过了阈值clip_gradients,则将梯度直接设置为该阈值
- // 此处阈值设为-1,即不起作用
- ClipGradients();
- // 逐层更新网络中的可学习层
- for (int param_id = 0; param_id < this->net_->learnable_params().size();
- ++param_id)
- {
- // 归一化
- Normalize(param_id);
- // L2范数正则化添加衰减权重
- Regularize(param_id);
- // 随机梯度下降法计算更新值
- ComputeUpdateValue(param_id, rate);
- }
- // 更新权重
- this->net_->Update();
- }
最后将迭代次数++iter_,继续while循环,直到迭代次数完成。
接下来该分析逐层Layer的虚函数Forward_cpu()和Backward_cpu()具体实现了(部分层还有对应的GPU版本:Forward_gpu()和Backward_gpu())。
阅读全文
0 0
- Caffe框架源码剖析(2)—训练网络
- Caffe框架源码剖析(2)—训练网络
- Caffe框架源码剖析(1)—构建网络
- Caffe框架源码剖析(1)—构建网络
- Caffe框架源码剖析(3)—数据层DataLayer
- Caffe框架源码剖析(4)—卷积层基类BaseConvolutionLayer
- Caffe框架源码剖析(5)—卷积层ConvolutionLayer
- Caffe框架源码剖析(6)—池化层PoolingLayer
- Caffe框架源码剖析(7)—全连接层InnerProductLayer
- Caffe框架源码剖析(8)—激活函数层ReLULayer
- Caffe框架源码剖析(9)—损失层SoftmaxWithLossLayer
- Caffe框架源码剖析—数据层DataLayer
- Caffe框架源码剖析(3)—数据层DataLayer
- Caffe框架源码剖析(4)—卷积层基类BaseConvolutionLayer
- Caffe框架源码剖析(5)—卷积层ConvolutionLayer
- Caffe框架源码剖析(6)—池化层PoolingLayer
- Caffe框架源码剖析(7)—全连接层InnerProductLayer
- Caffe框架源码剖析(8)—激活函数层ReLULayer
- vue2.0实现倒计时的插件(时间戳 刷新 跳转 都不影响)
- 来自硅谷的成长锦囊
- wait()和sleep()的区别
- 容器和分布式系统:从哪里来,将会去哪里
- RecylerView
- Caffe框架源码剖析(2)—训练网络
- (文件拷贝)多字节与Unicode字节状态下 CString转换Char的方法
- 规范 JS 写法的几点建议
- 云端共赢|阿里云(天津)技术与生态合作论坛圆满成功!
- RecyclerView适配器模版
- 浏览器窗口大小改变事件
- 快速输入的模板
- oracle用SQL Developer连接数据库出现的小问题篇章二
- java.io.IOException: unexpected end of stream on okhttp3.Address