美文网首页
优化算法中梯度下降算法的编程实现

优化算法中梯度下降算法的编程实现

作者: 蒜薹 | 来源:发表于2019-07-14 07:27 被阅读0次

    优化算法中梯度下降算法的编程实现

    简介

    梯度下降算法是运筹学的基础数学方法,用来求解运筹学所构造的数学问题。

    本文在Linux平台下,采用C++语言编写梯度下降算法的实现程序。

    程序的基本思路是以虚基类构建计算流程,以继承类定义代价函数(Cost Function)的形式,代价函数由用户自定义,继承关系由C++的多态特性辅助。

    优化算法通过三个部分实现,残差块(Residual Block)构造、代价函数(Cost Function)构造和优化计算(Optimization)。由于优化计算的框架是固定的,只有具体到残差块函数、代价函数等是不同的,因此将优化计算整体拆分为计算框架和具体内容两个部分,其中的计算框架部分由程序构造,具体内容由用户自定义。为了达成这一思路,程序以虚基类构建计算流程,以继承类定义残差块、代价函数等的形式,代价函数由用户自定义,继承关系由C++的多态特性辅助。

    程序包含三个虚基类,残差块类(ResidualBlockFunction)、代价函数类(CostFunction)和优化算法管理类(OptimizationManager),残差块类负责定义残差块的数学结构,为最基础的类;代价函数类负责构造代价函数的数学结构,该结构通过残差块构造,并且负责代价函数的导数求解,导数使用数值微分的方法进行求解;优化算法管理类统筹整个优化计算的实现过程。

    虚基类名称以"User"开头,继承类名称以描述自身目标的词汇而非"User"开头。

    程序结构

    残差块类

    残差块类包含两个部分,虚基类和继承类。虚基类名为"UserResidualBlockFunction",继承类名为"PolyResidualBlockFunction"。

    代价函数类

    代价函数类包含两个部分,虚基类"UserCostFunction"和继承类"SteepestCostFunction"。

    代价函数类由优化算法管理类调用,负责为优化算法管理类提供代价函数的函数值、一阶导数值(也称为梯度、Jacobi)、二阶导数(即黑森矩阵,Hessian Matrix)等,因此其核心功能是计算代价函数值、计算代价函数的导数值。同时,代价函数类负责管理残差块,由此,代价函数类的核心功能还包括残差块的添加。

    一个典型的代价函数类的虚基类如下,核心功能所指的函数包括了添加计算代价函数值bool CostFunction,计算代价函数的一阶导数值bool DerivativesFunction,残差块函数void AddResidualBlock。而其它函数是用于辅助核心功能的,包括计算代价函数对某一参数的一阶偏导数值的函数bool GetOneDerivative,设定迭代步长void SetStepLength

    在虚基类的这些函数中,有一部分是纯虚函数(Pure Virtual Function),它们的定义交由继承类完成,并且继承类必须定义,否则程序无法完成编译。这些函数都是与用户的选择挂钩的,包括添加残差块函数和计算代价函数值的函数。残差块和代价函数必须由用户自行定义,虽然在大量研究文献中,残差块都定义为观测数值与理论数值之差,代价函数都定义为残差块的平方和,但这不代表残差块和代价函数没有其它定义方式,因此,这两个函数的定义交由继承类完成。

    class UserCostFunction
    {
        public:
            UserCostFunction(string name, int SizeObservations, int SizeVariables, int SizeResiduals);
            ~UserCostFunction();
    
        public:
            // pure virtual
            virtual void AddResidualBlock(vector<double> observations) = 0;
            virtual bool CostFunction(vector<double> variables, vector<double> &CostFunctionValues)=0;
    
        public:
            // virtual
            virtual bool GetOneDerivative(int VarialbleID, vector<double> variables, double &theDerivativeValue);
    
        public:                                                                                                                                                                                                      
            bool DerivativesFunction(vector<double> variables, vector<double> &theDerivatives);
            void SetStepLength(double delta);
    
        public:
            virtual void Show() = 0;
    
        protected:
            vector<UserResidualBlockFunction*> ResidualBlockFunctions_;
            int SizeObservations_;
            int SizeVariables_;
            int SizeResiduals_;
    
            // for derivative calculation
            double delta_;
    
        private:
            string name_;
    };
    

    <center>虚基类"UserCostFunction"的头文件部分</center>

    class SteepestCostFunction : virtual public UserCostFunction
    {
        public:
            SteepestCostFunction(string name, int SizeObservations, int SizeVariables, int SizeResiduals);                                                                                                           
            ~SteepestCostFunction();
                
        public:
            void Show();
    
        public:
            virtual void AddResidualBlock(vector<double> observations);
            virtual bool CostFunction(vector<double> variables, vector<double> &CostFunctionValues);
    
        public:
            virtual bool GetOneDerivative(int VarialbleID, vector<double> variables, double &theDerivativeValue);
    
        private:
            string name_;
    };
    

    <center>继承类"SteepestCostFunction"的头文件部分</center>

    代价函数值的计算框架

    本文设定代价函数为F(A),残差块为f_i(A),参量以矩阵形式表达,这里假设参量数量为3,则参量为A[a_0,a_1,a_2]^T,观测数据为x_iy_i,假设其理论数学关系符合三阶多项式,y_i=a_0+a_1x_i+a_2x_i^2。虽然代价函数F(A)的形式可以根据用户实际使用而不同,但残差块的平方和形式仍然在大量文献中被采用,如下。
    F(A) = \sum_{i=1}^{m} {(f_i(A))^2}
    残差块f_i(A)也面临相同的情况,虽然可以根据用户使用情况而不同,但观测值与理论值之差的形式依然是广泛使用的形式,如下。
    f_i(A) = y_i-(a_0+a_1x_i+a_2x_i^2)
    采用上述形式,则代价函数可以表示如下。
    F(A)= (y_0-(a_0+a_1x_0+a_2x_0^2))^2 \\ +(y_1-(a_0+a_1x_1+a_2x_1^2))^2 \\+\cdots \\+ (y_m-(a_0+a_1x_m+a_2x_m^2))^2
    代价函数值的计算由函数bool CostFunction完成,由于该函数是核心功能,其名称必须固定,因此该函数作为虚基类"UserCostFunction"的纯虚函数进行声明,在继承类"PolyResidualBlockFunction"中进行定义。

    一阶导数的计算框架

    优化算法管理类

    优化算法管理类包含两个部分,虚基类"UserOptimizationManager"和继承类"SteepestOptimizationManager"。÷

    相关文章

      网友评论

          本文标题:优化算法中梯度下降算法的编程实现

          本文链接:https://www.haomeiwen.com/subject/nyavkctx.html