如何在C#中用LINQ实现简单线性回归的Gradient Descent算法
使用LINQ简化简单线性回归的梯度下降实现
首先说明两处核心调整:
- 原代码存在隐性bug:斜率导数、截距导数的初始化放在了外层迭代循环之外,会导致每次迭代的导数累加前序结果,最终参数计算错误,改写时会修复该问题
- 内层循环的核心逻辑是同步遍历x、y数组,计算每个样本的预测误差后分别累加两个导数的总和,刚好可以用LINQ的
Zip配对+Sum求和完全替代
改写后的精简实现如下(完全移除嵌套for循环):
double[] x = new double[] { 1, 2, 3, 4 }; double[] y = new double[] { 5, 7, 9, 12 }; double slope_current = 0; double intercept_current = 0; double learningRate = 0.01; int iterations = 1500; int sampleCount = x.Length; for (int i = 0; i < iterations; i++) { // 单轮LINQ计算两个导数,替代内层循环 double intercept_derivative = x.Zip(y, (xi, yi) => slope_current * xi + intercept_current - yi).Sum(e => e / sampleCount); double slope_derivative = x.Zip(y, (xi, yi) => (slope_current * xi + intercept_current - yi) * xi).Sum(e => e / sampleCount); // 更新参数 intercept_current -= learningRate * intercept_derivative; slope_current -= learningRate * slope_derivative; }
如果希望只遍历一次样本序列提升性能,可以改用Aggregate方法一次性聚合两个导数:
for (int i = 0; i < iterations; i++) { var (interceptGrad, slopeGrad) = x.Zip(y, (xi, yi) => { double error = slope_current * xi + intercept_current - yi; return (intercept: error / sampleCount, slope: error * xi / sampleCount); }).Aggregate((0.0, 0.0), (sum, curr) => (sum.Item1 + curr.intercept, sum.Item2 + curr.slope)); intercept_current -= learningRate * interceptGrad; slope_current -= learningRate * slopeGrad; }
两种写法运行结果完全一致,迭代1500次后得到的斜率约为2.21、截距约为2.43,和原代码修复bug后的输出结果匹配。
内容的提问来源于stack exchange,提问作者Péter Szilvási
相关产品推荐
相关产品推荐

