You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

线性回归梯度下降均收敛至(0,0)附近的问题排查

梯度下降算法拟合失败问题排查与修正

问题现象

自行实现线性回归梯度下降后,所有运行结果均收敛至(0,0)附近,最优成本对应的模型与数据不符,高成本模型反而更接近预期。已尝试调整学习率、随机化初始向量,数据范围为考试分数1-100、学习时长1-10,不确定是否需要归一化。

核心问题分析

  • 特征与响应变量完全颠倒:测试代码中将考试分数(1-100)作为特征,学习时长(1-10)作为响应变量,既违背“用学习时长预测考试分数”的逻辑,又因特征值范围远大于响应值,导致参数更新时梯度比例严重失衡——斜率参数的梯度被大特征值稀释,更新幅度极小,最终收敛到接近0的无效值。
  • 参数定义与注释混淆:类初始化注释标注vector is gradient and y-intercept,但实际_cost函数中预测式为y = vector[0] + vector[1] * x,即vector[0]是截距、vector[1]是斜率,注释与实际逻辑完全相反,导致初始向量设置时参数含义混淆,影响收敛起点合理性。
  • 未做特征归一化:特征值与响应值数量级差异大,梯度下降对不同参数的敏感程度不一致:截距参数的梯度量级远大于斜率参数,学习率无法同时适配两者的更新需求,要么截距更新过度,要么斜率更新不足。
  • 学习率与迭代次数不足:当前使用的learning_rate=0.0001、iterations=50对于未归一化的数据来说,学习率过小、迭代次数太少,参数还未收敛到最优值就停止更新。

修正方案与代码调整

1. 核心修正方向

  • 调换特征与响应变量顺序,用学习时长预测考试分数;
  • 明确参数定义,修正注释;
  • 添加特征归一化,平衡参数更新敏感度;
  • 调整学习率与迭代次数,确保充分收敛。

修正后的实现代码

class GradientDescent:
    def __init__(self,
                 feature_data: list[float],
                 response_data: list[float],
                 initial_vector: list[float, float],
                 learning_rate: float=0.01,
                 iterations: int=1000,
                 normalize: bool=True) -> None:
        '''
        初始化梯度下降类
        参数说明:
        - feature_data: 特征数据(如学习时长)
        - response_data: 响应数据(如考试分数)
        - initial_vector: 初始参数向量,格式为[截距(y-intercept), 斜率(gradient)]
        '''
        self.feature_data = feature_data
        self.response_data = response_data
        self.normalize = normalize
        
        # 特征归一化处理(Z-score标准化)
        if self.normalize:
            self.mean_x = sum(self.feature_data) / len(self.feature_data)
            self.std_x = (sum((x - self.mean_x)**2 for x in self.feature_data) / len(self.feature_data))**0.5
            self.feature_data = [(x - self.mean_x) / self.std_x for x in self.feature_data]
        
        self.initial_vector = initial_vector
        self.learning_rate = learning_rate
        self.iterations = iterations

    def _partial_difference_quotient(self,
                                     vector: list[float, float],
                                     index: int,
                                     small_change: float=0.0001):
        modified_vector = vector.copy()
        modified_vector[index] += small_change
        return (self._cost(modified_vector) - self._cost(vector)) / small_change

    def _estimate_gradient(self, vector):
        gradient_estimates = []
        for i in range(len(vector)):
            gradient_estimates.append(self._partial_difference_quotient(vector, i))
        return gradient_estimates

    def _update_vector(self, vector: list[float, float]) -> list[float, float]:
        direction = self._estimate_gradient(vector)
        for i in range(len(vector)):
            vector[i] -= self.learning_rate * direction[i]
        return vector

    def _cost(self, vector: list[float, float]) -> float:
        '''计算均方误差损失'''
        cost = 0
        for i, y_true in enumerate(self.response_data):
            y_pred = vector[0] + vector[1] * self.feature_data[i]
            error = y_true - y_pred
            cost += error ** 2
        return cost / (2 * len(self.response_data))

    def compute_gradient_descent(self) -> tuple[list[float], list[float]]:
        next_vector = self.initial_vector.copy()
        cost_values = [self._cost(next_vector)]

        for _ in range(self.iterations):
            next_vector = self._update_vector(next_vector)
            cost_values.append(self._cost(next_vector))
        
        # 还原归一化后的参数到原始特征尺度
        if self.normalize:
            next_vector[1] = next_vector[1] / self.std_x
            next_vector[0] = next_vector[0] - next_vector[1] * self.mean_x
        
        return next_vector, cost_values

修正后的测试代码

from random import randint

def main(runs: int=5, **kwargs) -> None:
    learning_rate = kwargs.get("learning_rate", 0.01)
    iterations = kwargs.get("iterations", 1000)

    lowest_cost_value = float('inf')
    lowest_cost_vector = None

    for i in range(runs):
        # 初始向量:截距设为考试分数均值附近,斜率设为合理范围
        starting_vector = [randint(5000, 8000)/100, randint(0, 200)/100]
        gradient_descent = GradientDescent(
            feature_data=data["Study Hours"],  # 特征改为学习时长
            response_data=data["Exam Score"],  # 响应改为考试分数
            initial_vector=starting_vector.copy(),
            learning_rate=learning_rate,
            iterations=iterations
        )
        latest_vector, cost_values = gradient_descent.compute_gradient_descent()    

        current_min_cost = min(cost_values)
        if current_min_cost < lowest_cost_value:
            lowest_cost_value = current_min_cost
            lowest_cost_vector = latest_vector
            print(f"最低损失: {lowest_cost_value:.4f}, 最优参数(截距,斜率): {[round(v,4) for v in latest_vector]}")

验证效果

修正后,梯度下降会收敛到符合预期的参数:比如截距在50左右(不学习时的基础分数),斜率在5-10之间(每多学1小时,分数提升5-10分),损失值会显著降低,拟合线与数据分布一致。

内容的提问来源于stack exchange,提问作者BFI01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 14:45:13