Ceres入门笔记
介绍
Ceres可以解决下列形式的边界约束鲁棒非线性最小二乘问题
(1)
$\min\limits_{x}\quad \frac{1}{2} \sum\limits_{i}\rho_{i}\left( \left\| f_{i}\left( x_{i1},\ldots,x_{ik}\right)\right\|^{2} \right)$
$s.t. \quad l_{j} \leqslant x_{j} \leqslant u_{j}$
这种形式的问题广泛地出现在科学工程领域——从拟合统计曲线到计算机视觉中通过图像重建3D模型。
我们将通过Ceres求解器解决问题(1),本章给所有示例都提供了完整可行的代码。
表达式$\rho_{i}\left( \left\| f_{i}\left( x_{i1},\ldots,x_{ik}\right)\right\|^{2} \right)$作为ResidualBlock,其中$f_{i}\left(\cdot\right)$是取决于参数块$\left[ x_{i1},\ldots,x_{ik} \right]$的CostFunction。在大多数优化问题中,小群体的标量一起出现。比如,平移向量的三个组成部分和定义相机姿态四元数的四个分量。我们将这样一组小标量称为ParameterBlock。当然ParameterBlock也可以只有一个参数。$l_{j}$和$u_{j}$是参数块$x_{j}$的边界。
$\rho_{i}$是LossFunction,LossFunction是一个标量函数,用于减少异常值对非线性最小二乘解的影响。
一个特殊情况,当$\rho_{i}\left( x \right)=x$,i.e.恒等函数,并且$l_{j}=-\infty$和$u_{j}=\infty$,我们得到了一更熟悉的非线性最小二乘问题。
(2)
$\frac{1}{2} \sum\limits_{i} \left\| f_{i}\left( x_{i1},\ldots,x_{ik}\right)\right\|^{2}$
Hello World!
首先,考虑函数最小值的问题
$\frac{1}{2}\left(10-x\right)^{2}$
这是一个很简单的问题,其最小值为x=10,但是它非常适合用来解释如何通过Ceres解决问题。
第一步是编写一个函数来评估函数$f\left(x\right)=10-x$;
struct CostFunctor {
template <typename T>
bool operator()(const T* const x, T* residual) const {
residual[] = T(10.0) - x[];
return true;
}
};
这里需要注意的一点是operator()是一个模板方法,它假定所有的输入和输出都是T类型的。此处使用模板允许调用CostFunction::operator<T>()。当只有残差被用到时T=double,当用到雅可比时T=Jet。
一旦我们有了计算残差函数的方法,就可以用它构造一个非线性最小二乘问题,并让Ceres解决它。
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[]);
// The variable to solve for with its initial value.
double initial_x = 5.0;
double x = initial_x;
// Build the problem.
Problem problem;
// Set up the only cost function (also known as residual). This uses
// auto-differentiation to obtain the derivative (jacobian).
CostFunction* cost_function =
new AutoDiffCostFunction<CostFunctor, , >(new CostFunctor);//CostFunctor结构
problem.AddResidualBlock(cost_function, NULL, &x);
// Run the solver!
Solver::Options options;
options.linear_solver_type = ceres::DENSE_QR;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.BriefReport() << "\n";
std::cout << "x : " << initial_x
<< " -> " << x << "\n";
return ;
}
AutoDiffCostFunction将CostFunction作为输入,并且自动求导,并为其提供CostFunction接口。
编译运行examples/helloworld.cc(在下载的Ceres的examples文件夹下)有以下结果
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time
4.512500e+01 0.00e+00 9.50e+00 0.00e+00 0.00e+00 1.00e+04 5.33e-04 3.46e-03
4.511598e-07 4.51e+01 9.50e-04 9.50e+00 1.00e+00 3.00e+04 5.00e-04 4.05e-03
5.012552e-16 4.51e-07 3.17e-08 9.50e-04 1.00e+00 9.00e+04 1.60e-05 4.09e-03
Ceres Solver Report: Iterations: , Initial cost: 4.512500e+01, Final cost: 5.012552e-16, Termination: CONVERGENCE
x : 5.0 ->
struct NumericDiffCostFunctor {
bool operator()(const double* const x, double* residual) const {
residual[] = 10.0 - x[];
return true;
}
};
添加到problem中:
CostFunction* cost_function =
new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, , >(new NumericDiffCostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);
注意,自动求导时我们用的是
CostFunction* cost_function =
new AutoDiffCostFunction<CostFunctor, , >(new CostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);
除了一个额外的模板参数,该参数表明用于计算数值导数(该示例在examples/helloworld_numeric_diff.cc)的有限差分格式的种类之外,其结构看起来与用于自动求导的格式几乎完全相同。
一般来说,我们建议自动求导而不是数值求导。 C ++模板的使用使得自动求导变得高效,而数值求导很expensive,容易出现数值错误,并导致收敛速度变慢。
解析求导
在某些情况下,使用自动区分是不可能的。 例如,以封闭形式计算导数比依赖自动求导代码所使用的链式规则更有效。
在这种情况下,可以提供自己写的残差和雅可比计算代码。 为此,如果您知道编译时参数和残差的大小,请定义CostFunction或SizedCostFunction的子类。 这里例如是实现$f\left(x\right)=10-x$的SimpleCostFunction
class QuadraticCostFunction : public ceres::SizedCostFunction<, > {
public:
virtual ~QuadraticCostFunction() {}
virtual bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const {
const double x = parameters[][];
residuals[] = - x;
// Compute the Jacobian if asked for.
if (jacobians != NULL && jacobians[] != NULL) {
jacobians[][] = -;//残差的导数为-1,残差函数是线性的。
}
return true;
}
};
#include <vector>
#include "ceres/ceres.h"
#include "glog/logging.h" using ceres::CostFunction;
using ceres::SizedCostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve; // A CostFunction implementing analytically derivatives for the
// function f(x) = 10 - x.
class QuadraticCostFunction
: public SizedCostFunction< /* number of residuals */,
/* size of first parameter */> {
public:
virtual ~QuadraticCostFunction() {} virtual bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const {
double x = parameters[][]; // f(x) = 10 - x.
residuals[] = - x; // f'(x) = -1. Since there's only 1 parameter and that parameter
// has 1 dimension, there is only 1 element to fill in the
// jacobians.
//
// Since the Evaluate function can be called with the jacobians
// pointer equal to NULL, the Evaluate function must check to see
// if jacobians need to be computed.
//
// For this simple problem it is overkill to check if jacobians[0]
// is NULL, but in general when writing more complex
// CostFunctions, it is possible that Ceres may only demand the
// derivatives w.r.t. a subset of the parameter blocks.
if (jacobians != NULL && jacobians[] != NULL) {
jacobians[][] = -;
} return true;
}
}; int main(int argc, char** argv) {
google::InitGoogleLogging(argv[]); // The variable to solve for with its initial value. It will be
// mutated in place by the solver.
double x = 0.5;
const double initial_x = x; // Build the problem.
Problem problem; // Set up the only cost function (also known as residual).
CostFunction* cost_function = new QuadraticCostFunction;
problem.AddResidualBlock(cost_function, NULL, &x); //x待估计参数 // Run the solver!
Solver::Options options;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary); std::cout << summary.BriefReport() << "\n";
std::cout << "x : " << initial_x
<< " -> " << x << "\n"; return ;
}
Ceres入门笔记的更多相关文章
- 每天成长一点---WEB前端学习入门笔记
WEB前端学习入门笔记 从今天开始,本人就要学习WEB前端了. 经过老师的建议,说到他每天都会记录下来新的知识点,每天都是在围绕着这些问题来度过,很有必要每天抽出半个小时来写一个知识总结,及时对一天工 ...
- ES6入门笔记
ES6入门笔记 02 Let&Const.md 增加了块级作用域. 常量 避免了变量提升 03 变量的解构赋值.md var [a, b, c] = [1, 2, 3]; var [[a,d] ...
- [Java入门笔记] 面向对象编程基础(二):方法详解
什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...
- React.js入门笔记
# React.js入门笔记 核心提示 这是本人学习react.js的第一篇入门笔记,估计也会是该系列涵盖内容最多的笔记,主要内容来自英文官方文档的快速上手部分和阮一峰博客教程.当然,还有我自己尝试的 ...
- redis入门笔记(2)
redis入门笔记(2) 上篇文章介绍了redis的基本情况和支持的数据类型,本篇文章将介绍redis持久化.主从复制.简单的事务支持及发布订阅功能. 持久化 •redis是一个支持持久化的内存数据库 ...
- redis入门笔记(1)
redis入门笔记(1) 1. Redis 简介 •Redis是一款开源的.高性能的键-值存储(key-value store).它常被称作是一款数据结构服务器(data structure serv ...
- OpenGLES入门笔记四
原文参考地址:http://www.cnblogs.com/zilongshanren/archive/2011/08/08/2131019.html 一.编译Vertex Shaders和Fragm ...
- OpenGLES入门笔记三
在入门笔记一中比较详细的介绍了顶点着色器和片面着色器. 在入门笔记二中讲解了简单的创建OpenGL场景流程的实现,但是如果在场景中渲染任何一种几何图形,还是需要入门笔记一中的知识:Vertex Sha ...
- unity入门笔记
我于2010年4月1日硕士毕业加入完美时空, 至今5年整.刚刚从一家公司的微端(就是端游技术+页游思想, 具体点就是c++开发, directX渲染, 资源采取所需才会下载)项目的前端主程职位离职, ...
随机推荐
- PAT 1078 字符串压缩与解压(20)(代码+思路)
1078 字符串压缩与解压(20 分) 文本压缩有很多种方法,这里我们只考虑最简单的一种:把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示.例如 ccccc 就用 5c 来表 ...
- geoserver 的缓存技术
geoserver提到的缓存工具共有两个:tilecache和geowebcache.geowebcache是java写的,整合进geoserer中. tilecache则是python写的一个小程序 ...
- p3412 [POI2005]SKO-Knights
传送门 分析 图1 我们假设我们现在有两个向量(2,3)和(4,2),将他们所能到达的点在几何画板上画出来,再将这些点用红线连起来,在将横坐标相同的点用蓝线连起来便能得到图1,就此我们可以发现可以用绿 ...
- tp5链接访问
方法名:admin/DayActive/statistic 访问:admin/day_active/statistic
- OSGi 系列(十)之 Blueprint
OSGi 系列(十)之 Blueprint blueprint 是 OSGi 的一个规范,类似于 spring 的 IOC,用来处理 OSGi 的动态特性,可以大大简化服务的使用. blueprint ...
- 【JS】 伪主动触发input:file的click事件
大家用到input:file标签时,对于input:file的样式难看的处理方法一般有2种: 采用透明化input:file标签的方法,上面放的input:file标签,下面放的是其他标签,实际点击的 ...
- 2018.10.15 bzoj4570: [Scoi2016]妖怪(凸包)
传送门 不得不说这题有点东西啊. 看到题第一眼二分,用二次函数求范围来进行checkcheckcheck,20分滚粗了233. 于是开始思考正解. 发现可以把每只怪物的二元组属性看成二维坐标. 这时对 ...
- 2018.09.14 洛谷P3567 [POI2014]KUR-Couriers(主席树)
传送门 简单主席树啊. 但听说有随机算法可以秒掉%%%(本蒟蒻并不会) 直接维护值域内所有数的出现次数之和. 当这个值不大于区间总长度的一半时显然不存在合法的数. 这样在主席树上二分查值就行了. 代码 ...
- Java 继承关系中:static,构造函数,成员变量的加载顺序
首先看下面的例子: package simple.demo; /** * @author Administrator * @date 2019/01/03 */ public class ClassA ...
- =default(c++11)
1.概念 1)如果我们需要编译器默认的行为,则可以在参数列表后面加上=default来显式地要求编译器生成合成版本的默认构造函数和拷贝控制成员:合成的默认构造函数.合成拷贝构造函数.合成拷贝赋值运算符 ...