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

梯度下降训练线性回归:训练/测试误差异常问题求助

问题描述

我基于梯度下降实现了线性回归模型,用于学习B0和B1以拟合线性多项式方程,程序运行正常,但绘图显示训练误差与测试误差极为相似,甚至多数时候训练误差高于测试误差。我原预期训练误差低于测试误差,怀疑采样存在问题,但尝试多种采样方法后仍未解决。

实现代码
import random

# 导入数值计算库
import numpy as np

# 导入绘图库matplotlib
import matplotlib.pyplot as plt


# 根据输入x生成数据集的函数
def datasetGenerator(x: int) -> int:
    return ((2 * x) - 3) + np.random.normal(0, 5)


# 计算均方误差,用于比较真实y与预测值
def meanSquaredError(y: float, y_pred: float) -> float:
    # 计算所有y与y_pred差值的平方的均值
    return np.mean((y - y_pred) ** 2)


def gradientDescent(
    X: list[float],
    Y: list[float],
    Y_pred: list[float],
    B0: float,
    B1: float,
    learningRate: float,
):
    # 对B0求梯度并更新
    B0 -= learningRate * np.mean(-2 * (Y - Y_pred))
    # 对B1求梯度并更新
    B1 -= learningRate * np.mean(-2 * (Y - Y_pred) * (X))
    return B0, B1


def betaCalculation(X: list[float], Y: list[float], n: int) -> int:
    # 生成X的0到n次幂数组
    Xtrans = [np.power(X, i) for i in range(n + 1)]

    # 转换为特征矩阵
    Xnew = np.transpose(Xtrans)

    # 计算X^T * X
    XTX = np.matmul(Xtrans, Xnew)

    # 计算(X^T * X)的逆
    XTXm1 = np.linalg.inv(XTX)

    # 计算(X^T * X)^-1 * X^T
    XTXinvintoXT = np.matmul(XTXm1, Xtrans)

    # 计算最终的Beta参数
    Beta = np.matmul(XTXinvintoXT, Y)

    # 返回参数数组
    return Beta


def optimalFit(
    B0: float,
    B1: float,
    X_train: list[float],
    Y_train: list[float],
    X_test: list[float],
    Y_test: list[float],
    learningRate,
) -> tuple[list[list[float]], list[float], list[int]]:
    flag = True
    Ynew = Y_train
    epsTrainArr = []
    epsTestArr = []
    epochsArr = []
    B = []
    epochs = 0

    while flag:
        # 根据当前B0、B1生成训练集和测试集的预测值
        Y_pred_train = B0 + B1 * X_train
        Y_pred_test = B0 + B1 * X_test

        # 判断模型是否收敛
        flag = False if meanSquaredError(Ynew, Y_pred_train) <= 1e-6 else True

        # 计算训练误差和测试误差
        eps_train = meanSquaredError(Y_train, Y_pred_train)
        eps_test = meanSquaredError(Y_test, Y_pred_test)

        # 通过梯度下降更新B0和B1
        B0, B1 = gradientDescent(X_train, Y_train, Y_pred_train, B0, B1, learningRate)
        B.append([B0, B1])
        Ynew = Y_pred_train

        epochs += 1

        # 记录误差和轮次
        epsTrainArr.append(eps_train)
        epsTestArr.append(eps_test)
        epochsArr.append(epochs)

    return B, epsTrainArr, epsTestArr, epochsArr


# 主函数,程序入口
def main():
    # 生成-5到5之间的1000个均匀分布的X值
    X_init = np.linspace(-5, 5, 1000)

    # 调用datasetGenerator生成对应的Y值
    Y_init = datasetGenerator(X_init)

    XYtup = []
    for i in range(len(X_init)):
        XYtup.append(tuple([X_init[i], Y_init[i]]))

    XYtup = np.array(XYtup)
    rng = np.random.default_rng()
    rng.shuffle(XYtup)
    XYtup = XYtup.tolist()

    # 提取所有X和Y值
    X = np.array([XYtup[i][0] for i in range(len(X_init))])
    Y = np.array([XYtup[i][1] for i in range(len(X_init))])

    # 计算训练集分割索引(80%作为训练集)
    split_idx_train = int(X.shape[0] * 0.8)

    # 随机采样生成训练集和测试集
    train_data = random.sample(XYtup, split_idx_train)
    test_data = [i for i in XYtup if i not in train_data]

    X_train, Y_train = zip(*train_data)
    X_test, Y_test = zip(*test_data)

    X_train = np.array(X_train)
    Y_train = np.array(Y_train)

    X_test = np.array(X_test)
    Y_test = np.array(Y_test)

    # 初始化B0和B1为正态分布随机值
    B0 = np.random.normal(0, 1)
    B1 = np.random.normal(0, 1)
    learningRate = 0.1

    # 训练模型并获取结果
    B, epsTrainArr, epsTestArr, epochsarr = optimalFit(
        B0, B1, X_train, Y_train, X_test, Y_test, learningRate
    )
    # 绘制训练误差和测试误差曲线
    plt.plot(
        epochsarr,
        epsTrainArr,
        label=f"轮次 vs 训练误差(学习率: {learningRate})",
        c="r",
    )
    plt.plot(
        epochsarr,
        epsTestArr,
        label=f"轮次 vs 测试误差(学习率: {learningRate})",
        c="b",
    )
    plt.title("训练轮次与模型均方误差的关系")
    plt.xlabel("训练轮次")
    plt.ylabel("均方误差")
    plt.figtext(
        0.5,
        0.01,
        "该图展示了不同学习率下,模型均方误差随训练轮次增加的变化趋势",
        wrap=True,
        horizontalalignment="center",
        fontsize=10,
        bbox={"facecolor": "grey", "alpha": 0.3, "pad": 5},
    )
    plt.legend()
    plt.show()

    # 测试不同学习率的效果
    learningRate = 0.001
    while learningRate <= 0.1:
        B, epsTrainArr, epsTestArr, epochsarr = optimalFit(
            B0, B1, X_train, Y_train, X_test, Y_test, learningRate
        )
        epochs = epochsarr[-1]

        # 打印梯度下降和闭式解得到的参数
        print(
            f"模型收敛后的B0和B1: {B[-1]},训练误差: {epsTrainArr[-1]}"
        )
        print(f"闭式解得到的B0和B1: {betaCalculation(X, Y, 1)}")

        # 打印收敛所需轮次
        print(
            f"学习率{learningRate}下,模型收敛所需轮次: {epochs}\n"
        )
        plt.plot(
            epochsarr[5:],
            epsTrainArr[5:],
            label=f"轮次 vs 误差(学习率: {learningRate})",
        )

        learningRate *= 10
    print(len(B))

    # 绘制不同学习率的误差曲线
    plt.title("训练轮次与模型均方误差的关系(不同学习率)")
    plt.xlabel("训练轮次")
    plt.ylabel("均方误差")
    plt.figtext(
        0.5,
        0.01,
        "该图展示了不同学习率下,模型均方误差随训练轮次增加的变化趋势",
        wrap=True,
        horizontalalignment="center",
        fontsize=10,
        bbox={"facecolor": "grey", "alpha": 0.3, "pad": 5},
    )
    plt.legend()
    plt.show()


if __name__ == "__main__":
    main()
结果说明

生成的图表展示了训练轮次与训练误差、测试误差的对应关系:两条误差曲线几乎完全重合,在部分训练阶段,训练误差甚至高于测试误差,与预期的训练误差低于测试误差的结果不符。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 22:27:32