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

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

简介

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

本文在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 observations) = 0;
        virtual bool CostFunction(vector variables, vector &CostFunctionValues)=0;

    public:
        // virtual
        virtual bool GetOneDerivative(int VarialbleID, vector variables, double &theDerivativeValue);

    public:                                                                                                                                                                                                      
        bool DerivativesFunction(vector variables, vector &theDerivatives);
        void SetStepLength(double delta);

    public:
        virtual void Show() = 0;

    protected:
        vector ResidualBlockFunctions_;
        int SizeObservations_;
        int SizeVariables_;
        int SizeResiduals_;

        // for derivative calculation
        double delta_;

    private:
        string name_;
};

虚基类"UserCostFunction"的头文件部分

class SteepestCostFunction : virtual public UserCostFunction
{
    public:
        SteepestCostFunction(string name, int SizeObservations, int SizeVariables, int SizeResiduals);                                                                                                           
        ~SteepestCostFunction();
            
    public:
        void Show();

    public:
        virtual void AddResidualBlock(vector observations);
        virtual bool CostFunction(vector variables, vector &CostFunctionValues);

    public:
        virtual bool GetOneDerivative(int VarialbleID, vector variables, double &theDerivativeValue);

    private:
        string name_;
};

继承类"SteepestCostFunction"的头文件部分

代价函数值的计算框架

本文设定代价函数为,残差块为,参量以矩阵形式表达,这里假设参量数量为3,则参量为,观测数据为和,假设其理论数学关系符合三阶多项式,。虽然代价函数的形式可以根据用户实际使用而不同,但残差块的平方和形式仍然在大量文献中被采用,如下。

残差块也面临相同的情况,虽然可以根据用户使用情况而不同,但观测值与理论值之差的形式依然是广泛使用的形式,如下。

采用上述形式,则代价函数可以表示如下。

代价函数值的计算由函数bool CostFunction完成,由于该函数是核心功能,其名称必须固定,因此该函数作为虚基类"UserCostFunction"的纯虚函数进行声明,在继承类"PolyResidualBlockFunction"中进行定义。

一阶导数的计算框架

优化算法管理类

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

你可能感兴趣的:(优化算法中梯度下降算法的编程实现)