梯度下降的简单例子
/// <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(); }