梯度下降的简单应用
//应用 static void GradientDescent2() { //现有一组线性相关的数据,要求通过训练得到一个最接近它们线性关系的函数 //现假设它们的线性关系函数为 y = w * x + b,则有 cost(w,b) = |(w*x+b)-y| 的最小值接近0时越准确,这里x,y为给出的已知数,w,b为所求变量 //即求cost(w,b) = (w * x + b-y)^2的最小值 //求出cost(w,b) w b偏导,即梯度grad(cost(w,b)) =(2*(wx+b-y)*x , 2*(wx+b-y)*1) //假设我们拿到的真实的数据dataXY, 这里用模拟数据代替 double[][] dataXY = new double[300][]; var r = new Random(); Enumerable.Range(0, dataXY.Length-1).ToList().ForEach(m => { var x = r.NextDouble()*100; var couple = new double[2]; couple[0] = x; couple[1] = x * 3 + 3; dataXY[m] = couple; }); //y = weight * x + bias double weight = 0, bias = 0; double rate = 0.1; int repeatTrain = 30;//重复训练次数 int trainTimes = 0; //实际训练次数 //训练一对数据 Action<double,double> trainOneData = (x,y) => { //模仿上面的写法运行后发现结果错误,是因为xy为随机数值,梯度数值跨度太大, //不符合让weight和bias运行一次后略微左移右移的处理逻辑 //var w = weight - rate * 2 *(weight*x + bias - y)*x; //var b = bias - rate * 2*(weight*x + bias - y)*1; //weight = w; //bias = b; //这里我们需要对真实的梯度进行单位处理,不让它们摇摆的过于夸张,还要保证梯度的方向维持正确 var realgradw = 2 * (weight * x + bias - y) * x; var realgradb = 2 * (weight * x + bias - y) * 1; var unit = Math.Max(Math.Abs(realgradw), Math.Abs(realgradb)); unit = Math.Abs(unit) < double.Epsilon?1 : unit; var unitw = realgradw/unit; var unitb = realgradb / unit; //同时除以2个梯度中绝对值的最大的值,这样梯度的大小合适了,比例未变 var w = weight - rate * unitw; var b = bias - rate * unitb; weight = w; bias = b; //1、当rate=0.001经过运行后发现 w =3.007 b=0.12, w下降的速度很快,b的下降速度太慢未达到最优解 //因为 realgradw 中存在一个x^2因子,所以w下降的速度更快,需要想办法加快b的下降速度 //2、首先我们尝试加大步长rate=0.1,(w=3.004 b=2.932)(w=3.1,b=2.64)(w=3.1,b=1.64)数据不太稳定和精确 //3、尝试加大训练次数此时的结果较精确,但训练的时间过长效率太低 //4、尝试训练开始的时候选一个较大的步长,并且在训练的过程中逐步减小步长的值 trainTimes++; if (trainTimes < 1000&& trainTimes > 0) rate = 0.5; if (trainTimes < 2000 && trainTimes > 1000) rate = 0.5; if (trainTimes < 3000 && trainTimes > 2000) rate = 0.3; if (trainTimes < 5000 && trainTimes > 3000) rate = 0.1; if (trainTimes < 7000 && trainTimes > 5000) rate = 0.05; if ( trainTimes > 7000) rate = 0.001; //w=3.0001,b=2.9989基本准确,但是当训练的数据较大时(var x = r.NextDouble()*1000;)结果还是不太理想 //还有优化的空间 }; //把所有的数据训练一次 Action<double[][]> trainAllData = data => { for (int i = 0; i < data.Length-1; i++) { trainOneData(data[i][0], data[i][1]); Console.WriteLine($"过程 weight={weight}; bias={bias}"); } }; //把所有数据重复训练30次 for (int i = 0; i < repeatTrain; i++) { trainAllData(dataXY); } Console.WriteLine($"结果 weight={weight}; bias={bias};\r\n 通过学习得到的方程为 y = {weight}*x+{bias}"); Console.ReadKey(); }
//5、这里我们引入sigmoid函数g(x)=1/(1+exp(-x)),将其求导后函数化简g'(x) =g(x)*( 1-g(x) ) //上面我们已经cost函数2个方向的偏导函数,这里我们只需要将原函数的值其带入g'(x)后乘以其偏导即可 //这里我们令rate = 5,之后不用调节rate的值,通过sigmoid函数后函数值会自动收敛 rate = 5; var y1 = weight*x + bias; var dgx = 1/(1 + Math.Exp(-y1))*(1 - 1/(1 + Math.Exp(-y1))); unitw = unitw * dgx; unitb = unitb * dgx; var w = weight - rate * unitw; var b = bias - rate * unitb; weight = w; bias = b;