Ceres introducción y ejemplos (9) Sobre Derivadas (Derivadas numéricas)

El otro extremo de usar derivadas analíticas es usar derivadas numéricas. El punto clave es que el proceso de derivación de la función f(x) con respecto a x se puede escribir en forma de límite:

Diferencias a plazo Diferencia a plazo

Por supuesto, en una computadora, no podemos realizar operaciones de límite numérico, así que lo que hacemos es elegir un valor h pequeño y aproximar la derivada como
D f ( x ) = lim ⁡ h → 0 f ( x + h ) − f ( x ) h Df(x) = \lim_{h \rightarrow 0} \frac{f(x + h) - f(x)}{h}D f ( x )=h 0límitehf ( x+h )f ( x )

La fórmula anterior es la forma más simple y básica de diferenciación numérica. Esta es la llamada fórmula de diferencia directa.

Entonces, ¿cómo se crea una versión numéricamente discriminatoria de Rat43Analytic en el solucionador de Ceres ? Esto se puede hacer en dos pasos:

  • 1. Defina un Functor para un valor de parámetro dado y calcule el residual para un (x, y) dado.
  • 2. NumericDiffCostFunctionSe utiliza para construir una CostFunctioninstancia de paquete Rat43CostFunctor.
struct Rat43CostFunctor {
    
    
  Rat43CostFunctor(const double x, const double y) : x_(x), y_(y) {
    
    }

  bool operator()(const double* parameters, double* residuals) const {
    
    
    const double b1 = parameters[0];
    const double b2 = parameters[1];
    const double b3 = parameters[2];
    const double b4 = parameters[3];
    residuals[0] = b1 * pow(1.0 + exp(b2 -  b3 * x_), -1.0 / b4) - y_;
    return true;
  }

  const double x_;
  const double y_;

}
// Analytic算法中手动求解Jacobians的部分被拿掉了。

CostFunction* cost_function =
  new NumericDiffCostFunction<Rat43CostFunctor, FORWARD, 1, 4>(
    new Rat43CostFunctor(x, y));

Esta es la cantidad mínima de trabajo requerida para definir una función de costo. Lo único que debe hacer el usuario es asegurarse de que la evaluación de los residuos se implemente de manera correcta y eficiente.

Antes de continuar, es instructivo estimar primero el error en la formulación de la diferencia directa. Hacemos esto considerando la expansión de Taylor de f alrededor de x.
F ( X + h ) = F ( X ) + h re F ( X ) + h 2 2 ! RE 2 F ( X ) + h 3 3 ! X + h ) - F ( X ) h - [ h 2 ! re 2 F ( X ) + h 2 3 ! re 3 F ( X ) + ⋯ ] re F ( X ) = F ( X + h ) - F ( x ) h + O ( h ) \begin{split} f(x+h) &= f(x) + h Df(x) + \frac{h^2}{2!} D^2f(x) + \frac{h^3}{3!}D^3f(x) + \cdots \\ Df(x) &= \frac{f(x + h) - f(x)}{h} - \left [ \frac{h}{2!}D^2f(x) + \frac{h^2}{3!}D^3f(x) + \cdots \right]\\ Df(x) &= \frac{ f(x + h) - f(x)}{h} + O(h) \end{dividir}f ( x+h )D f ( x )D f ( x )=f ( x )+h re f ( x )+2 !h2D2 f(x)+3 !h3D3 f(x)+=hf ( x+h )f ( x )[2 !hD2 f(x)+3 !h2D3 f(x)+]=hf ( x+h )f ( x )+O ( h )
Es decir, el error en la fórmula de la diferencia directa es O(h)
PS: En el análisis de error asintótico, O ( hk ) O(h^k)oh _ _El error de k )hkh^kdel error cuando h está lo suficientemente cerca de 0hk es como máximo un múltiplo constante de .

detalles de implementacion

NumericDiffCostFunctionImplementa un algoritmo general para diferenciar numéricamente un funtor dado. Aunque NumericDiffCostFunctionla implementación real es compleja, la CostFunction resultante es más o menos la siguiente:

class Rat43NumericDiffForward : public SizedCostFunction<1,4> {
    
    
   public:
     Rat43NumericDiffForward(const Rat43Functor* functor) : functor_(functor) {
    
    }
     virtual ~Rat43NumericDiffForward() {
    
    }
     virtual bool Evaluate(double const* const* parameters,
                           double* residuals,
                           double** jacobians) const {
    
    
       functor_(parameters[0], residuals);
       if (!jacobians) return true;
       double* jacobian = jacobians[0];
       if (!jacobian) return true;

       const double f = residuals[0];
       double parameters_plus_h[4];
       for (int i = 0; i < 4; ++i) {
    
    
         std::copy(parameters, parameters + 4, parameters_plus_h);
         const double kRelativeStepSize = 1e-6;
         const double h = std::abs(parameters[i]) * kRelativeStepSize;
         parameters_plus_h[i] += h;
         double f_plus;
         functor_(parameters_plus_h, &f_plus);
         jacobian[i] = (f_plus - f) / h;
       }
       return true;
     }

   private:
     std::unique_ptr<Rat43Functor> functor_;
 };

Tenga en cuenta la elección del tamaño de paso h en el código anterior, usamos el tamaño de paso relativo kRelativeStepSize = 1 0 − 6 \text{kRelativeStepSize} = 10^{-6}kRelativeStepSize=1 06 en lugar de un tamaño de paso absoluto que es el mismo para todos los parámetros.
Esto proporciona una mejor estimación de la derivada que el tamaño de paso absoluto. Esta elección de tamaño de paso solo es adecuada para valores de parámetros que no están cerca de cero. Por lo tanto,NumericDiffCostFunctionla implementación real de , utiliza una lógica de selección de tamaño de paso más compleja, donde cerca de cero cambia a un tamaño de paso fijo.

Diferencias centrales

El error O(h) en la formulación de la diferencia directa está bien, pero no es excelente. Un mejor enfoque es usar la fórmula de la diferencia central:
D f ( x ) ≈ f ( x + h ) − f ( x − h ) 2 h Df(x) \approx \frac{f(x + h) - f( x-h)}{2h}D f ( x )2h _f ( x+h )f ( xh )

Tenga en cuenta que la fórmula de diferencia directa requiere solo un cálculo adicional si se conoce el valor de f(x), pero la fórmula de diferencia central requiere dos cálculos, lo que duplica su costo.
Entonces, ¿vale la pena la solución extra?
Para responder a esta pregunta, volvemos a calcular el error de aproximación en la fórmula de la diferencia central:
F ( X + h ) = F ( X ) + h re F ( X ) + h 2 2 ! re 2 f ( x ) + h 3 3 ! re 3 f ( x ) + h 4 4 ! re 4 F ( X ) + ⋯ F ( X - h ) = F ( X ) - h re F ( X ) + h 2 2 ! re 2 F ( X ) - h 3 3 ! re 3 f ( do 2 ) + h 4 4 ! re 4 F ( X ) + ⋯ re F ( X ) = F ( X + h ) - F ( X - h ) 2 h + h 2 3 ! re 3 f ( x ) + h 4 5 ! re 5 f ( x ) + ⋯ re f ( x ) = f ( x + h ) − f ( x − h ) 2 h + O ( h 2 ) \begin{dividir} f(x ​​+ h) &= f( x) + h Df(x) + \frac{h^2}{2!} D^2f(x) + \frac{h^3}{3!} D^3f(x) + \frac{h^ 4}{4!} D^4f(x) + \cdots\\ f(x - h) &= f(x) - h Df(x) + \frac{h^2}{2!} D^2f (x) - \frac{h^3}{3!} D^3f(c_2) + \frac{h^4}{4!} D^4f(x) + \cdots\\ Df(x) & = \frac{f(x + h) - f(x - h)}{2h} + \frac{h^2}{3!} D^3f(x) + \frac{h^4}{5!} D^5f(x) + \cdots \\ Df(x) & = \frac{f(x + h) - f(x - h)}{2h} + O(h^2) \end{dividir}f ( x+h )f ( xh )D f ( x )D f ( x )=f ( x )+h re f ( x )+2 !h2D2 f(x)+3 !h3D3 f(x)+4 !h4D4 f(x)+=f ( x )h re f ( x )+2 !h2D2 f(x)3 !h3D3 f(do2)+4 !h4D4 f(x)+=2h _f ( x+h )f ( xh )+3 !h2D3 f(x)+5 !h4D5 f(x)+=2h _f ( x+h )f ( xh )+oh _ _2 )

El error de la fórmula de la diferencia central es O ( h 2 ) O(h^2)oh _ _2 ), es decir, el error cae cuadráticamente, mientras que el error en la fórmula de la diferencia directa solo cae linealmente.

Es muy sencillo usar la diferencia central en lugar de la diferencia directa en Ceres Solver, los NumericDiffCostFunctionparámetros de plantilla modificados son los siguientes

CostFunction* cost_function =
// 将前向差分转换为中心差分很简单:只需要将NumericDiffCostFunction模板参数中的FORWARD更改为CENTRAL
  new NumericDiffCostFunction<Rat43CostFunctor, CENTRAL, 1, 4>(
    new Rat43CostFunctor(x, y));

Pero, ¿qué significan estas diferencias en los errores en la práctica? Para entender esto, considere el problema de derivar una función de una variable
           f ( x ) = ex sin ⁡ x − x 2 , en x = 1.0 f(x) = \frac{ e^x}{\sen x - x^2} , en x = 1.0f ( x )=pecado _ _x x2mix, un tx _ =1.0
           
es fácil encontrar la derivada en esta posiciónD f ( 1.0 ) = 140.73773557129658 Df(1.0)=140.73773557129658D f ( 1.0 )=140.73773557129658
PD: Consulte http://www2.edu-edu.com.cn/lesson_crs78/self/j_0022/soft/ch0302.html para obtener información sobre derivados

Usando este valor como referencia, ahora podemos calcular el error relativo en las fórmulas de diferencia de avance y centro como una función del tamaño de paso absoluto y graficarlos.
inserte la descripción de la imagen aquí
Al leer este cuadro de derecha a izquierda, se destacan algunas cosas en el cuadro anterior:

  • 1. Los gráficos de ambas fórmulas tienen dos regiones diferentes. Primero, comenzando con un valor muy grande de h, este error disminuye como efecto de la serie de Taylor truncada, pero a medida que los valores de h continúan disminuyendo, este error comienza a aumentar nuevamente, ya que el error de "redondeo" comienza a dominar. el cálculo de la posición dominante. Por tanto, no podemos seguir obteniendo una estimación más precisa de Df reduciendo h. Nuestra aproximación se convierte en un factor limitante obvio.
  • 2. La fórmula de diferencia directa no es una buena forma de encontrar derivadas. A medida que disminuye el tamaño del paso, la fórmula de la diferencia central converge más rápidamente a una estimación derivada más precisa. Por lo tanto, a menos que la función de evaluación de f(x) sea tan complicada que la fórmula de la diferencia central no se pueda permitir, no use la fórmula de diferenciación directa.
  • 3. Para un mal valor de h, no se aplica ninguna fórmula.

método del caballero

En los dos métodos diferenciales anteriores, la precisión está limitada por la precisión del número de punto flotante de la computadora, es decir, h no puede ser infinitamente pequeño. Entonces, ¿podemos obtener una mejor estimación de Df sin que h sea tan pequeña que empecemos a alcanzar los límites de precisión de los números de punto flotante?

Un enfoque posible es encontrar una forma de reducir el error en menos de O ( h 2 ) O(h^2)oh _ _2 )Método rápido
Esto se puede lograr aplicando la extrapolación de Richardson al problema de diferenciación. Esto también se conoce como el Método Ridders.

Repasemos el error en la fórmula de la diferencia central.
re F ( X ) = F ( X + h ) - F ( X - h ) 2 h + h 2 3 ! h ) − F ( X − h ) 2 h + K 2 h 2 + K 4 h 4 + ⋯ \begin{split} Df(x) & = \frac{f(x + h) - f(x - h) {2h} + \frac{h^2}{3!} D^3f(x) + \frac{h^4}{5!} D^5f(x) + \cdots\\ & = \frac{ f(x + h) - f(x - h)}{2h} + K_2 h^2 + K_4 h^4 + \cdots \end{dividir}D f ( x )=2h _f ( x+h )f ( xh )+3 !h2D3 f(x)+5 !h4D5 f(x)+=2h _f ( x+h )f ( xh )+k2h2+k4h4+

Las cosas clave a tener en cuenta aquí son K 2 , K 4 , ... K_2 , K_4 , ...k2,k4,... no tiene nada que ver con h, solo con x.
Definamos A ( 1 , m ) = f ( x + h / 2 m − 1 ) − f ( x − h / 2 m − 1 ) 2 h / 2 m − 1 . A(1, m) = \frac {f(x + h/2^{m-1}) - f(x - h/2^{m-1})}{2h/2^{m-1}}.un ( 1 ,m )=2 horas / 2metro - 1f ( x+h / 2metro - 1 )f ( xh / 2m 1 ).

Luego observa que re f ( x ) = A ( 1 , 1 ) + K 2 h 2 + K 4 h 4 + ⋯ re f ( x ) = A ( 1 , 2 ) + K 2 ( h / 2 ) 2 + K 4 ( h / 2 ) 4 + ⋯ Df(x) = A(1,1) + K_2 h^2 + K_4 h^4 + \cdots \\ Df(x) = A(1, 2) + K_2 (h /2)^2 + K_4 (h/2)^4 + \cdotsD f ( x )=un ( 1 ,1 )+k2h2+k4h4+D f ( x )=un ( 1 ,2 )+k2( h /2 )2+k4( h /2 )4+

Aquí reducimos a la mitad el tamaño del paso para obtener una segunda estimación de diferencia central de Df(x). Combinando las dos estimaciones anteriores, obtenemos:
D f ( x ) = 4 A ( 1 , 2 ) − A ( 1 , 1 ) 4 − 1 + O ( h 4 ) Df(x) = \frac{4 A( 1 , 2) - A(1,1)}{4 - 1} + O(h^4)D f ( x )=414 A ( 1 ,2 )un ( 1 ,1 )+oh _ _4 )

Es una aproximación de Df(x) con un error de truncamiento de O ( h 4 ) O(h^4)oh _ _4 )Pero no tenemos que detenernos ahí. Podemos iterar este proceso para obtener estimaciones más precisas de la siguiente manera:
A ( n , m ) = { f ( x + h / 2 m − 1 ) − f ( x − h / 2 m − 1 ) 2 h / 2 m − 1 norte = 1 4 norte - 1 UN ( norte - 1 , metro + 1 ) - UN ( norte - 1 , metro ) 4 norte - 1 - 1 norte > 1 \begin{split}A(n, metro ) = \begin {casos} \frac{\displaystyle f(x + h/2^{m-1}) - f(x - h/2^{m-1})}{\displaystyle 2h/2^{ m-1} } & n = 1 \\ \frac{\displaystyle 4^{n-1} A(n - 1, m + 1) - A(n - 1, m)}{\displaystyle 4^{n -1} - 1} & n > 1 \end{casos}\end{dividir}un ( n ,m )= 2 horas / 2metro - 1f ( x+h / 2metro - 1 )f ( xh / 2m 1 )4norte - 114norte - 1 UN(norte1 ,metro+1 )un ( n1 ,m )norte=1norte>1

Es fácil probar que A ( n , 1 ) A(n, 1)un ( n ,1 ) el error de aproximación esO ( h 2 n ) O(h^{2n})oh _ _2 n ). Para entender cómo la fórmula anterior calcula realmenteA ( n , 1 ) A(n, 1)un ( n ,1 ) Por ejemplo, se da una de las siguientes soluciones:
A ( 1 , 1 ) A ( 1 , 2 ) A ( 1 , 3 ) A ( 1 , 4 ) ⋯ A ( 2 , 1 ) A ( 2 , 2 ) UN ( 2 , 3 ) ⋯ UN ( 3 , 1 ) UN ( 3 , 2 ) ⋯ UN ( 4 , 1 ) ⋯ ⋱ \begin{split}\begin{array}{ccccc} A(1,1) & A ( 1, 2) & A(1, 3) & A(1, 4) & \cdots\\ & A(2, 1) & A(2, 2) & A(2, 3) & \cdots\\ & & A(3, 1) & A(3, 2) & \cdots\\ & & & A(4, 1) & \cdots \\ & & & & \ddots \end{matriz}\end{dividir}un ( 1 ,1 )un ( 1 ,2 )un ( 2 ,1 )un ( 1 ,3 )un ( 2 ,2 )un ( 3 ,1 )un ( 1 ,4 )un ( 2 ,3 )un ( 3 ,2 )un ( 4 ,1 )

Por lo tanto, para calcular A ( n , 1 ) A(n, 1)un ( n ,1 ) Para valores de incremento n, nos movemos de izquierda a derecha, calculando una columna a la vez. Suponiendo que el costo principal aquí es la evaluación de la función f(x), entonces el costo de calcular una nueva columna en la tabla anterior es la evaluación de las dos funciones.
Dado que calcularA ( 1 , n ) A(1, n)un ( 1 ,n ) , el tamaño del paso de cálculo es2 1 − nh 2^{1-n}h2La fórmula de la diferencia central de 1 n h

Aplicando este método a f ( x ) = ex sin ⁡ x − x 2 f(x) = \frac{e^x}{\sin x - x^2}f ( x )=pecado _ _x x2mix, comenzando con un tamaño de paso bastante grande h=0.01, obtenemos:
141.678097131 140.971663667 140.796145400 140.752333523 140.741384778 140.736185846 140.737639311 140 \ comenzar{dividir}\begin{matriz}{rrrrr} 141.678097131 &140.971663667 &140.796145400 &140.752333523 &140.741384778\ \ &140.736185846 &140.737639311 &140.737729564 &140.737735196\\ & &140.737736209 &140.7377 35581 &140.737735571\\ & & &140.737735571 & 140.737735571\\ & & & &140. 737735571\\ \end{matriz }\end{dividir}141.678097131140.971663667140.736185846140.796145400140.737639311140.737736209140.752333523140.737729564140.737735581140.737735571140.741384778140.737735196140.737735571140.737735571140.737735571

Relativo al valor de referencia D f ( 1.0 ) = 140.73773557129658 , A ( 5 , 1 ) Df(1.0)=140.73773557129658, A(5,1)D f ( 1.0 )=140.73773557129658 A ( 5 ,1 ) El error relativo es1 0 − 13 10^{-13}1 013
para comparación, con la misma longitud de paso0,01 / 2 4 = 0,000625 0,01/2^4 = 0,0006250.01/ 24=Bajo 0.000625 , el error relativo de la fórmula de la diferencia centrales 0.01/2 4 = 0.000625 0.01/2^4 = 0.0006250.01/ 24=0.000625

La tabla anterior es la base del método de diferenciación numérica de Ridders. La implementación completa es un esquema adaptativo que rastrea su propio error de estimación y se detiene automáticamente cuando se alcanza la precisión deseada. Por supuesto, es más costosa que la formulación de diferencia de centro y delantera, pero también significativamente más robusta y precisa.
Usar el método de Ridder en lugar de la diferenciación directa o centrada es un asunto simple en Ceres, cambiando los NumericDiffCostFunctionparámetros de la plantilla de la siguiente manera:

CostFunction* cost_function =
  new NumericDiffCostFunction<Rat43CostFunctor, RIDDERS, 1, 4>(
    new Rat43CostFunctor(x, y));

Los gráficos a continuación muestran el tamaño de paso absoluto frente al error relativo para los tres métodos de diferenciación. Para el método de Ridders, asumimos que evaluando A ( n , 1 ) A(n,1)un ( n ,1 ) El tamaño de paso correspondiente es2 1 − nh 2^{1-n}h21 - norte h

inserte la descripción de la imagen aquí

Calcular A(5,1) usando el método Ridders requiere calcular la función de evaluación diez veces, y para Df(1.0) nuestra estimación es 1000 veces mejor que la estimación de la diferencia central. Para calcular estos números con precisión, el tipo de coma flotante de doble precisión de la computadora ≈ 2,22 × 1 0 − 16 \approx 2,22 \times 10^{-16}2.22×1 016 _

Volviendo a Rat43, veamos también los costos de tiempo de ejecución de varios métodos para calcular derivadas numéricas.

CostoFunción

Tiempo (ns)

Rat43Analítico

255

Rat43AnalyticOptimized

92

Rat43NumericDiffAdelante

262

Rat43NumericDiffCentral

517

Rat43NumericDiffRidders

3760

Como era de esperar, el tiempo de ejecución de la diferenciación central es aproximadamente el doble que el de la diferenciación directa, y el método de Ridders es muchas veces más largo que los dos anteriores, aunque su precisión también ha mejorado significativamente.

sugerencia

La diferenciación numérica se debe usar cuando no se puede calcular la derivada analíticamente o usando la diferenciación automática. Esto generalmente sucede cuando llama a una biblioteca o función externa cuya forma analítica no conoce, o incluso si la conociera, no puede reescribirla de una manera que use Derivadas Automáticas.
Cuando utilice la diferenciación numérica, intente utilizar el método de la diferencia central. Si el tiempo de ejecución no es una preocupación, o si no se puede determinar un tamaño de paso relativo estático apropiado para la función objetivo, se recomienda el método de Ridders.

Supongo que te gusta

Origin blog.csdn.net/wanggao_1990/article/details/129713409
Recomendado
Clasificación