梯度下降训练线性回归:训练/测试误差异常问题求助
问题描述
我基于梯度下降实现了线性回归模型,用于学习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
相关产品推荐
相关产品推荐

