Ceres introducción y ejemplos (8) Sobre Derivados (Derivados Analíticos)

Considere el problema de ajuste de la siguiente curva ( Rat43 ):
y = b 1 ( 1 + eb 2 − b 3 x ) 1 / b 4 y = \frac{b_1}{(1+e^{b_2-b_3x})^ { 1/b_4}}y=( 1+mib2- segundo3x )1/ segundo4b1
Es decir, dados unos datos { xi , yi } , ∀ i = 1 , . . . , n \{x_i, y_i\},\ \forall i=1,... ,n{ xyo,yyo} , yo=1 ,... ,n , determine los parámetros b 1 , b 2 , b 3 , b 4 quemejor se ajusten a los datosb1,b2,b3,b4

El problema al que nos enfrentamos es resolver b 1 , b 2 , b 3 , b 4 b_1, b_2, b_3, b_4b1,b2,b3,b4mi ( segundo 1 , segundo 2 , segundo 3 , segundo 4 ) = ∑ si 2 ( segundo 1 , segundo 2 , segundo 3 , segundo 4 ; xi , yi ) = ∑ yo ( segundo 1
mi ( segundo1,b2,b3,b4)=iF2 (segundo1,b2,b3,b4;Xyo,yyo)=i(( 1+mib2- segundo3Xyo)1/ segundo4b1yyo)2

La noción de mejor ajuste depende de la elección de una función objetivo para medir la calidad del ajuste, que a su vez depende de los procesos ruidosos subyacentes que produjeron las observaciones. Minimizar la suma de las diferencias al cuadrado es lo correcto cuando el ruido es gaussiano. En este caso, el valor óptimo para el parámetro es la estimación de máxima verosimilitud.

Para resolver este problema usando el solucionador de Ceres, necesitamos definir una función de costo para calcular el residuo f y sus derivados con respecto a b1, b2, b3 y b4 dados x e y. Según los conocimientos de cálculo en matemáticas avanzadas, podemos calcular una serie de derivadas de f:
re 1 F ( segundo 1 , segundo 2 , segundo 3 , segundo 4 ; X , y ) = 1 ( 1 + eb 2 - segundo 3 X ) 1 / segundo 4 re 2 F ( segundo 1 , segundo 2 , segundo 3 , segundo 4 ; X , y ) = - segundo 1 eb 2 - segundo 3 xb 4 ( 1 + eb 2 - segundo 3 X ) 1 / segundo 4 + 1 re 3 F ( segundo 1 , segundo 2 , segundo 3 , segundo 4 ; X , y ) = segundo 1 xeb 2 - segundo 3 xb 4 ( 1 + eb 2 - segundo 3 X ) 1 / segundo 4 + 1 re 4 F ( segundo 1 , segundo 2 , segundo 3 , segundo 4 ; X , y ) = segundo 1 Iniciar sesión ⁡ ( 1 + eb 2 - segundo 3 x ) segundo 4 2 ( 1 + eb 2 - segundo 3 x ) 1 / segundo 4 \begin{split} D_1 f(b_1, b_2, b_3, b_4; x,y ) &= \frac{1}{(1+e^{b_2-b_3x})^{1/b_4}}\\ D_2 f(b_1, b_2, b_3, b_4; x,y) &= \frac{- b_1e^{b_2-b_3x}}{b_4(1+e^{b_2-b_3x})^{1/b_4 + 1}} \\ D_3 f(b_1, b_2, b_3, b_4; x,y) &= \ frac{b_1xe^{b_2-b_3x}}{b_4(1+e^{b_2-b_3x})^{1/b_4 + 1}} \\ D_4 f(b_1, b_2, b_3, b_4; x,y) & = \frac{b_1 \log\left(1+e^{b_2-b_3x}\right) }{b_4^2(1+e^{b_2-b_3x})^{1/b_4}} \end{dividir}D1f ( segundo1,b2,b3,b4;x ,y )D2f ( segundo1,b2,b3,b4;x ,y )D3f ( segundo1,b2,b3,b4;x ,y )D4f ( segundo1,b2,b3,b4;x ,y )=( 1+mib2- segundo3x )1/ segundo41=b4( 1+mib2- segundo3x )1/ segundo4+ 1- segundo1mib2- segundo3x=b4( 1+mib2- segundo3x )1/ segundo4+ 1b1x mib2- segundo3x=b42( 1+mib2- segundo3x )1/ segundo4b1iniciar sesión( 1+mib2- segundo3x )

A partir de estas derivadas calculadas manualmente, ahora podemos implementar CostFunction:

class Rat43Analytic : public SizedCostFunction<1,4> {
    
    
   public:
     Rat43Analytic(const double x, const double y) : x_(x), y_(y) {
    
    }
     virtual ~Rat43Analytic() {
    
    }
     virtual bool Evaluate(double const* const* parameters,
                           double* residuals,
                           double** jacobians) const {
    
    
       const double b1 = parameters[0][0];
       const double b2 = parameters[0][1];
       const double b3 = parameters[0][2];
       const double b4 = parameters[0][3];

       residuals[0] = b1 *  pow(1 + exp(b2 -  b3 * x_), -1.0 / b4) - y_;

       if (!jacobians) return true;
       double* jacobian = jacobians[0];
       if (!jacobian) return true;

       jacobian[0] = pow(1 + exp(b2 - b3 * x_), -1.0 / b4);
       jacobian[1] = -b1 * exp(b2 - b3 * x_) *
                     pow(1 + exp(b2 - b3 * x_), -1.0 / b4 - 1) / b4;
       jacobian[2] = x_ * b1 * exp(b2 - b3 * x_) *
                     pow(1 + exp(b2 - b3 * x_), -1.0 / b4 - 1) / b4;
       jacobian[3] = b1 * log(1 + exp(b2 - b3 * x_)) *
                     pow(1 + exp(b2 - b3 * x_), -1.0 / b4) / (b4 * b4);
       return true;
     }

    private:
     const double x_;
     const double y_;
 };

Es un código tedioso que es difícil de leer y tiene mucha redundancia. Entonces, en la práctica, almacenaríamos en caché algunas subexpresiones para mejorar su eficiencia, lo que daría como resultado lo siguiente:

class Rat43AnalyticOptimized : public SizedCostFunction<1,4> {
    
    
   public:
     Rat43AnalyticOptimized(const double x, const double y) : x_(x), y_(y) {
    
    }
     virtual ~Rat43AnalyticOptimized() {
    
    }
     virtual bool Evaluate(double const* const* parameters,
                           double* residuals,
                           double** jacobians) const {
    
    
       const double b1 = parameters[0][0];
       const double b2 = parameters[0][1];
       const double b3 = parameters[0][2];
       const double b4 = parameters[0][3];

       const double t1 = exp(b2 -  b3 * x_);
       const double t2 = 1 + t1;
       const double t3 = pow(t2, -1.0 / b4);
       residuals[0] = b1 * t3 - y_;

       if (!jacobians) return true;
       double* jacobian = jacobians[0];
       if (!jacobian) return true;

       const double t4 = pow(t2, -1.0 / b4 - 1);
       jacobian[0] = t3;
       jacobian[1] = -b1 * t1 * t4 / b4;
       jacobian[2] = -x_ * jacobian[1];
       jacobian[3] = b1 * log(t2) * t3 / (b4 * b4);
       return true;
     }

   private:
     const double x_;
     const double y_;
 };

¿Cómo difieren estas dos implementaciones en el rendimiento?

CostoFunción

Tiempo (ns)

Rat43Analítico

255

Rat43AnalyticOptimized

92

Rat43AnalyticOptimized2,8 veces más rápido que Rat43Analytic. Esta diferencia en el tiempo de ejecución no es infrecuente. Para obtener el mejor rendimiento de las derivadas calculadas analíticamente, a menudo es necesario optimizar el código para tener en cuenta las subexpresiones comunes.

¿Cuándo se deben utilizar los derivados analíticos?

  • 1. La expresión es simple, por ejemplo, mayormente lineal

  • 2. Se puede usar un sistema de álgebra computacional como Maple, Mathematica o symy para diferenciar simbólicamente las funciones objetivo y generar c++ para calcularlas.

  • 3. Hay algunas estructuras algebraicas en la fórmula que pueden lograr un mejor rendimiento que la diferenciación automática.
    Dicho esto, obtener el máximo rendimiento fuera de calcular el recíproco es bastante trabajo.Antes de seguir este camino, es útil estimar que el costo computacional del jacobiano es una fracción del tiempo total de solución, recuerde que la ley de Live Amdahl es tu amigo.

  • 4. No hay otra forma de calcular la derivada, por ejemplo quieres calcular la derivada de las raíces de un polinomio:
    a 3 ( x , y ) z 3 + a 2 ( x , y ) z 2 + a 1 ( x , y ) z + a 0 ( x , y ) = 0 a_3(x,y)z^3 + a_2(x,y)z^2 + a_1(x,y)z + a_0(x,y) = 0a3( X ,y ) z3+a2( X ,y ) z2+a1( X ,y ) z+a0( X ,y )=0

    Para x, y, esto requiere el uso del teorema de la función inversa

  • 5. Te gusta la regla de la cadena y haces cálculos algebraicos a mano.

Supongo que te gusta

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