梯度下降的简单例子

        /// <summary>
        /// 寻找函数 y=x^2的最小值点
        /// </summary>
        static void SimpleGradientDescent()
        {
            //一元二次 函数 y=x^2  用梯度下降计算近似最小值
            double step = 0.1;  //步长 > 0
            double x = 2;         //取一个起始点  
            double dy = Math.Pow(x, 2);      //y的变化值  
            double changed = dy; //y变化后的值,下一次循环运算的起点
            while (dy > 0.00000001)         //y的变化值∆y小于某个设定值,说明计算后的新y值不再变化 ,则下降完成  
            {
                //若在x=a(a<0)处y=2x<0说明该点的切线斜率<0 函数递减,极小值点在x=a右侧,
                //又(step * 2 * a)<0 则点 x =a - (step * 2 * a)>a 在 x = a的右侧 更接近极小值点
                //同理 x=a(a>0)时 函数递增,(step * 2 * a)> 0,点 x= a-(step * 2 * a)<a 在x=a的左侧更接近极小值点
                //综合以上,所以当 x 沿 f(x) 导数的 相反数 方向移动时,更接近函数的极小值点
                //沿y=x^2的导函数 y=2x下降
                x = x - step * 2 * x;             
                var y1 = Math.Pow(x, 2);    //y1 = f(x1)= x1^2变化后的y值
                dy = changed - y1;            //本次运算的起点changed(初始y值),减去运算后的新y值(y1),为下降的变化值∆y
                changed = y1;                   //changed变化后的y值作为下一次运算的起点
                Console.WriteLine($"过程 x={x}");
            }
            Console.WriteLine($"结果 x={x}时,y=x^2取最小为{Math.Pow(x,2)}");
            //结果为x = 10^-4, y = 10^-8
            Console.ReadKey();
        }

        static void SimpleGradientDescent2()
        {
            //一元二次 函数 y=x^2  用梯度下降计算近似最小值
            Console.WriteLine("**************另一种写法***************");
            const int cycles = 10000; //足够大的循环次数
            double rate = 0.001;  //学习步长
            double X = 2;         //取一个起始点  
            Enumerable.Range(1, cycles).ToList().ForEach(m =>
            {
                //沿y=x^2的导函数 y=2x下降
                X = X - rate * 2 * X;
                Console.WriteLine($"过程 x={X}");
            });
            Console.WriteLine($"结果 x={X}时,y=x^2取最小为{Math.Pow(X, 2)}");
            //结果为 x= 4*10^-9, y= 1.6*10^-17
            Console.ReadKey();
        }

        static void GradientDescent()
        {
            //Rosenbrock函数f(x,y)=(1-x)^2+100(y-x^2)^2
            //求解出它的梯度方向grad(f(x,y)) = (  -2*( 1 - x ) -400( y - x*x )*x ,  200( y - x*x ) ) 即x轴方向的偏导,与y轴方向的偏导
            //沿着该梯度的反方向就可以快速确定x,y位置的最小点即最小值 f(1,1)min = 0
            var x = 3.0;
            var y = 3.0;
            const int cycles = 30000; //足够大的循环次数
            const double rate = 0.0008; //学习步长
            Enumerable.Range(1, cycles).ToList().ForEach(m =>
            {
                var x1 = x - rate*(-2*(1 - x) - 400*(y - x*x)*x);
                var y1 = y - rate*200*(y - x*x);
                x = x1;
                y = y1;
                Console.WriteLine($"过程 x={x}; y={y}");
            });
            Console.WriteLine($"结果 x={x}; y={y}时,f(x,y)=(1-x)^2+100(y-x^2)^2取最小为{(1 - x)*(1 - x) + 100*(y - x *x)*(y - x * x)}");
            //x=1.00016, y= 1.00033 ,f(x,y)= 2.75 * 10^-8
            Console.ReadKey();
        }

 

posted @ 2017-04-21 15:38  gaobowen  阅读(1501)  评论(0编辑  收藏  举报