尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从零实现多项式拟合:最小二乘法原理、Python源码与过拟合实战

从零实现多项式拟合:最小二乘法原理、Python源码与过拟合实战 1. 项目概述从数据点到趋势线“建模算法入门笔记-多项式拟合附源码”这个标题对于刚接触数据分析、机器学习或者数学建模的朋友来说吸引力是巨大的。它直指一个核心痛点面对一堆看似杂乱无章的数据点如何用一条光滑的曲线来揭示其背后的规律多项式拟合就是解决这个问题的“第一把钥匙”。简单来说多项式拟合就是用一个多项式函数比如 y a bx cx²去逼近一组给定的数据点。它不像插值那样要求曲线必须穿过每一个点而是追求整体趋势上的“最佳”匹配这在实际应用中更为常见因为真实数据总是伴随着噪声和误差。这个项目的目的就是带你亲手实现这个过程从理解数学原理到编写代码再到解读结果完成一次完整的、可复现的建模实践。为什么从多项式拟合入门因为它完美地串联了数学、编程和实际应用。你不需要高深的数学背景只需要基础的代数和微积分知识你也不需要复杂的编程框架用Python的NumPy库几行代码就能实现核心计算。通过它你能直观地理解“模型”、“参数”、“拟合”、“过拟合”这些建模领域最核心的概念。无论是预测明天的气温变化分析用户活跃度的趋势还是校准传感器的读数多项式拟合都是一个可靠且直观的起点。接下来我们就从最根本的数学原理开始拆解。2. 核心数学原理最小二乘法的来龙去脉多项式拟合的灵魂在于“最小二乘法”。这个名字听起来有点唬人但它的思想非常直观我们想要找到一条多项式曲线使得这条曲线到所有数据点的“垂直距离”的平方和最小。2.1 问题形式化与目标函数假设我们有一组N个数据点(x_i, y_i)其中i 1, 2, ..., N。我们想用一个m次多项式来拟合它们y w_0 w_1 * x w_2 * x² ... w_m * x^m这里的w_0, w_1, ..., w_m就是我们要求解的未知系数也叫权重或参数。对于每一个数据点x_i用我们的多项式模型预测出的y值为y_pred_i w_0 w_1 * x_i w_2 * x_i² ... w_m * x_i^m那么预测值与真实值之间的误差残差就是e_i y_pred_i - y_i。最小二乘法的目标就是找到一组系数w使得所有数据点的误差平方和最小。这个目标函数也叫损失函数可以写成L(w) Σ (y_pred_i - y_i)² Σ (w_0 w_1*x_i ... w_m*x_i^m - y_i)²其中求和符号Σ是对 i 从1到N求和。注意为什么用平方和而不是绝对值和主要是数学上的便利。平方函数处处可导这使得我们可以用求导这个强大的工具来找到最小值点。而绝对值函数在零点不可导求解起来更复杂。此外平方项对大的误差惩罚更重这通常符合我们的直觉。2.2 求解过程从求导到正规方程我们的目标是最小化L(w)。在微积分中一个多元函数取极值的必要条件是它对各个自变量的偏导数等于零。因此我们对每个系数w_j求偏导并令其为零∂L/∂w_j 2 * Σ (w_0 w_1*x_i ... w_m*x_i^m - y_i) * x_i^j 0 其中j 0, 1, ..., m。这给出了一个包含 (m1) 个方程的线性方程组未知数就是 (m1) 个系数w。这个方程组可以整理成一个非常整洁的矩阵形式即正规方程(X^T * X) * w X^T * y我们来拆解一下这个公式X是一个N x (m1)的设计矩阵也叫范德蒙矩阵。它的每一行对应一个数据点每一列对应多项式的一个基函数1, x, x², ...。X [[1, x_1, x_1^2, ..., x_1^m], [1, x_2, x_2^2, ..., x_2^m], ..., [1, x_N, x_N^2, ..., x_N^m]]y是一个N x 1的列向量包含了所有数据点的真实y值[y_1, y_2, ..., y_N]^T。w是一个(m1) x 1的列向量就是我们要求解的系数[w_0, w_1, ..., w_m]^T。X^T是X的转置矩阵。正规方程的意义在于它将一个复杂的优化问题转化为了一个线性代数中的求解线性方程组的问题。只要(X^T * X)这个矩阵是可逆的通常数据点足够多且x值不特殊时成立我们就可以直接解出系数w (X^T * X)^(-1) * X^T * y这里的^(-1)表示矩阵的逆。这个公式就是多项式拟合以及更广泛的线性回归的解析解。理解了这个推导过程你就能明白代码背后每一步计算的意义而不仅仅是调用一个黑箱函数。3. 从零实现手搓核心拟合代码理解了原理我们动手用Python实现它。我们将使用NumPy库来处理矩阵运算这是科学计算的基础。即使你未来会使用sklearn等高级库自己实现一遍也能让你对底层机制有坚实的掌控感。3.1 环境准备与数据构造首先确保你的Python环境安装了NumPy和Matplotlib用于绘图。可以通过pip install numpy matplotlib来安装。我们从一个简单的例子开始假设真实的规律是一个二次函数y 1 2*x 0.5*x²然后我们加上一些随机噪声来模拟真实观测数据。import numpy as np import matplotlib.pyplot as plt # 设置随机种子确保每次运行结果一致 np.random.seed(42) # 生成模拟数据 def generate_sample_data(num_points20, noise_scale0.5): 生成带噪声的二次多项式数据。 参数: num_points: 数据点数量 noise_scale: 噪声的标准差 返回: x: 自变量数组 y: 因变量数组带噪声 x np.linspace(-3, 3, num_points) # 在-3到3之间均匀生成点 # 真实函数y 1 2*x 0.5*x^2 y_true 1 2*x 0.5*x**2 # 加入高斯噪声 noise np.random.normal(0, noise_scale, sizex.shape) y_noisy y_true noise return x, y_noisy, y_true x_data, y_data, y_true generate_sample_data() print(f生成 {len(x_data)} 个数据点。) print(fX样本: {x_data[:5]}...) print(fY样本带噪声: {y_data[:5]}...)这段代码生成了我们的“实验数据”。y_true是我们想逼近的真相但现实中未知y_data是我们实际拿到手的有噪声数据。我们的任务就是通过x_data和y_data反推出一个接近1 2*x 0.5*x²的多项式。3.2 核心拟合函数实现接下来我们根据正规方程w (X^T * X)^(-1) * X^T * y来实现拟合函数。def polynomial_fit(x, y, degree): 使用最小二乘法进行多项式拟合。 参数: x: 一维自变量数组 y: 一维因变量数组 degree: 多项式次数 返回: w: 拟合系数数组从常数项到高次项 [w0, w1, ..., w_degree] # 1. 构建设计矩阵 X # 使用np.vander可以快速生成范德蒙矩阵但需要注意列的顺序高次项在前。 # 我们手动构建以便更清晰地理解。 X np.ones((len(x), degree 1)) # 先创建一个全1的矩阵第一列对应常数项 for i in range(1, degree 1): X[:, i] x ** i # 第i列是x的i次方 # 2. 计算 X^T * X 和 X^T * y XT X.T # 转置 XTX np.dot(XT, X) # 矩阵乘法 XTy np.dot(XT, y) # 3. 求解线性方程组 (XTX) * w XTy # 使用np.linalg.solve求解比直接求逆数值上更稳定。 w np.linalg.solve(XTX, XTy) return w def polynomial_predict(x, w): 使用拟合好的系数w对新的x值进行预测。 参数: x: 一维自变量数组或标量 w: 多项式系数数组 [w0, w1, ..., w_degree] 返回: y_pred: 预测值 degree len(w) - 1 y_pred np.zeros_like(x, dtypefloat) for i, coeff in enumerate(w): y_pred coeff * (x ** i) # w_i * x^i return y_pred关键点解析构建设计矩阵X这是最关键的一步。矩阵的每一行对应一个样本每一列对应一个特征这里是x的0次方到m次方。常数项对应w_0的那一列全是1。使用np.linalg.solve直接使用np.linalg.inv(XTX) XTy来计算w在数学上是等价的但在数值计算中直接求逆矩阵然后再乘可能会因为矩阵条件数过大而引入较大的数值误差。np.linalg.solve是专门用于求解线性方程组的函数它采用了更稳定的数值算法如LU分解是更专业的选择。预测函数实现了多项式求值。这里用了一个循环清晰易懂。对于高性能需求可以用NumPy的广播机制向量化实现y_pred np.polyval(w[::-1], x)注意np.polyval的系数顺序是从高次到低次。3.3 拟合效果可视化与评估现在让我们用一次、二次和五次多项式来拟合数据并直观地看看效果。# 进行不同次数的拟合 degrees [1, 2, 5] coefficients {} predictions {} # 生成用于绘制平滑曲线的密集点 x_plot np.linspace(x_data.min() - 0.5, x_data.max() 0.5, 200) plt.figure(figsize(15, 5)) for idx, degree in enumerate(degrees): # 拟合 w polynomial_fit(x_data, y_data, degree) coefficients[degree] w # 预测 y_plot_pred polynomial_predict(x_plot, w) predictions[degree] y_plot_pred # 绘图 plt.subplot(1, 3, idx 1) plt.scatter(x_data, y_data, colorblue, alpha0.6, labelNoisy Data, s30) plt.plot(x_plot, y_true, k--, labelTrue Function (y12x0.5x²), linewidth2) plt.plot(x_plot, y_plot_pred, r-, labelfFit (degree{degree}), linewidth2) plt.title(fPolynomial Fit (Degree {degree})) plt.xlabel(x) plt.ylabel(y) plt.legend() plt.grid(True, linestyle--, alpha0.5) # 在图上标注拟合出的方程以二次为例 if degree 2: eq_text f$y {w[0]:.2f} {w[1]:.2f}x {w[2]:.2f}x^2$ plt.text(0.05, 0.95, eq_text, transformplt.gca().transAxes, fontsize10, verticalalignmenttop, bboxdict(boxstyleround, facecolorwheat, alpha0.8)) plt.tight_layout() plt.show() # 打印拟合系数 print(拟合系数) for degree, w in coefficients.items(): print(f 次数 {degree}: {np.round(w, 4)})运行这段代码你会得到三张并排的图。从图中你可以立刻观察到几个重要现象一次拟合直线明显无法捕捉数据的弯曲趋势误差很大。二次拟合曲线形状与真实函数黑色虚线非常接近拟合效果很好。打印出的系数[w0, w1, w2]也会接近[1, 2, 0.5]。五次拟合曲线变得非常“扭曲”为了穿过每一个数据点而剧烈摆动尤其是在数据区域的边缘。它完美地拟合了噪声而不是潜在的趋势。这引出了建模中一个至关重要的概念过拟合。我们将在下一章深入探讨。4. 关键挑战与进阶话题欠拟合、过拟合与模型选择拟合不是次数越高越好。上面的对比已经生动地展示了欠拟合和过拟合的问题。4.1 理解欠拟合与过拟合欠拟合模型过于简单如一次多项式无法捕捉数据中的基本结构和规律。表现为训练误差和测试误差都很大。就像用一把直尺去量一个弯曲的碗边怎么量都不准。过拟合模型过于复杂如高次多项式不仅学习了数据中的潜在规律还“学习”了数据中的随机噪声。表现为在训练数据上误差很小但在新的、未见过的数据上误差很大。模型失去了泛化能力。就像根据一张有折痕的地图硬生生记下了折痕的形状以为那也是道路的一部分。4.2 量化评估误差指标我们需要定量的指标来衡量拟合好坏。最常用的包括均方误差MSE (1/N) * Σ (y_pred_i - y_i)²。这就是我们最小化的目标函数。值越小越好。均方根误差RMSE sqrt(MSE)。它与原始y值有相同的量纲更易于解释。R平方R² 1 - (Σ(y_i - y_pred_i)² / Σ(y_i - y_mean)²)。它表示模型对数据波动的解释比例范围在0到1之间可能为负越接近1越好。def evaluate_fit(y_true, y_pred): 计算并打印多种评估指标。 mse np.mean((y_true - y_pred) ** 2) rmse np.sqrt(mse) ss_res np.sum((y_true - y_pred) ** 2) ss_tot np.sum((y_true - np.mean(y_true)) ** 2) r_squared 1 - (ss_res / ss_tot) if ss_tot ! 0 else 0 print(f 均方误差: {mse:.4f}) print(f 均方根误差: {rmse:.4f}) print(f R平方: {r_squared:.4f}) return mse, rmse, r_squared # 在训练数据上评估不同模型 print(在训练数据上的评估) for degree in degrees: y_pred_train polynomial_predict(x_data, coefficients[degree]) print(f次数 {degree}:) evaluate_fit(y_data, y_pred_train)你会发现五次多项式的MSE和R²在训练集上可能是最好的但这是一种“虚假繁荣”。4.3 模型选择的实战方法交叉验证为了真实评估模型的泛化能力我们必须使用模型未见过的数据。标准做法是划分训练集和测试集或者使用更稳健的K折交叉验证。from sklearn.model_selection import train_test_split, KFold # 注意这里仅为了演示交叉验证思想实际我们仍使用自己的拟合函数。 # 方法1简单划分 x_train, x_test, y_train, y_test train_test_split(x_data, y_data, test_size0.2, random_state42) print(f训练集大小: {len(x_train)} 测试集大小: {len(x_test)}) best_degree None best_rmse_test float(inf) results {} for degree in range(1, 8): # 尝试1到7次 # 在训练集上拟合 w_train polynomial_fit(x_train, y_train, degree) # 在测试集上预测和评估 y_pred_test polynomial_predict(x_test, w_train) _, rmse_test, _ evaluate_fit(y_test, y_pred_test) # 在训练集上也评估一下用于对比 y_pred_train polynomial_predict(x_train, w_train) _, rmse_train, _ evaluate_fit(y_train, y_pred_train) results[degree] {train_rmse: rmse_train, test_rmse: rmse_test} print(f次数 {degree}: 训练集RMSE{rmse_train:.4f}, 测试集RMSE{rmse_test:.4f}) if rmse_test best_rmse_test: best_rmse_test rmse_test best_degree degree print(f\n根据测试集表现最佳多项式次数为: {best_degree}) # 绘制误差随次数变化的曲线 degrees_tried list(results.keys()) train_errors [results[d][train_rmse] for d in degrees_tried] test_errors [results[d][test_rmse] for d in degrees_tried] plt.figure(figsize(8,5)) plt.plot(degrees_tried, train_errors, bo-, labelTraining RMSE, linewidth2, markersize8) plt.plot(degrees_tried, test_errors, rs-, labelTest RMSE, linewidth2, markersize8) plt.xlabel(Polynomial Degree) plt.ylabel(RMSE) plt.title(Model Complexity vs. Error (Train/Test Split)) plt.legend() plt.grid(True) plt.xticks(degrees_tried) plt.show()观察绘制的误差曲线你会看到一个典型模式随着模型复杂度次数增加训练误差持续下降模型越来越强总能更好地拟合训练数据但测试误差会先下降后上升。那个测试误差最低点对应的模型复杂度通常就是最佳选择。这直观地展示了偏差-方差权衡。实操心得在实际项目中数据往往更宝贵。如果数据量很少简单划分训练/测试集可能不稳定。这时K折交叉验证是更可靠的选择。其思想是将数据分成K份轮流用其中K-1份训练1份验证重复K次后取平均误差作为模型性能的估计。这能更充分地利用数据评估结果也更稳健。5. 源码解析与工程化扩展我们实现了一个基础版本。但在实际工程或更复杂的研究中还需要考虑更多因素。让我们深入源码看看有哪些可以优化和扩展的地方。5.1 数值稳定性与正则化当多项式次数较高时设计矩阵X的列即x, x², x³...之间可能产生严重的多重共线性导致(X^T * X)矩阵接近奇异不可逆求解系数时数值误差会急剧放大。这就是高次多项式拟合不稳定的根源之一。解决这个问题有两大策略特征缩放在构建设计矩阵前对x值进行标准化减去均值除以标准差。这样x, x², ...的尺度不会相差太大能改善矩阵条件数。正则化在损失函数中加入对系数大小的惩罚项强制模型变得简单。最常见的是岭回归其损失函数为L(w) Σ(y_pred - y)² α * Σ w_j²。其中α是正则化强度。这相当于在正规方程中给(X^T * X)矩阵的对角线元素加了一个小的常数(X^T * X α * I) * w X^T * y。这能稳定求解过程并有效抑制过拟合。def polynomial_fit_ridge(x, y, degree, alpha0.1): 使用岭回归L2正则化进行多项式拟合。 # 构建设计矩阵 X np.ones((len(x), degree 1)) for i in range(1, degree 1): X[:, i] x ** i XT X.T XTX np.dot(XT, X) # 添加正则化项在单位矩阵上乘以alpha reg_matrix alpha * np.eye(degree 1) # 注意通常不对常数项w0进行强正则化这里简单处理 w np.linalg.solve(XTX reg_matrix, np.dot(XT, y)) return w # 对比高次多项式下普通拟合与岭回归拟合 high_degree 10 w_normal polynomial_fit(x_data, y_data, high_degree) w_ridge polynomial_fit_ridge(x_data, y_data, high_degree, alpha1.0) print(f普通拟合{high_degree}次系数绝对值之和: {np.sum(np.abs(w_normal)):.2f}) print(f岭回归拟合{high_degree}次系数绝对值之和: {np.sum(np.abs(w_ridge)):.2f}) # 通常岭回归的系数绝对值之和会更小模型更“平滑”。5.2 使用专业库NumPy.polyfit 与 Scikit-learn在实际开发中我们很少从头手写。掌握如何使用成熟、优化的库是必备技能。NumPy.polyfit:# 使用numpy的polyfit函数一行代码搞定拟合 # 注意np.polyfit返回的系数是从高次到低次 degree 2 coefficients_np np.polyfit(x_data, y_data, degree) print(fnp.polyfit 拟合系数高次到低次: {coefficients_np}) # 预测可以使用np.poly1d生成一个多项式函数 poly_func np.poly1d(coefficients_np) y_pred_np poly_func(x_data)np.polyfit内部也是通过求解正规方程或使用更稳定的奇异值分解实现的并且做了大量优化比自己写的版本更快、更稳定。Scikit-learn: Scikit-learn提供了统一的机器学习接口功能更强大尤其适合集成到机器学习流水线中。from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression, Ridge from sklearn.pipeline import make_pipeline degree 2 # 方法1使用Pipeline组合多项式特征生成和线性回归 model make_pipeline(PolynomialFeatures(degree), LinearRegression()) # 注意sklearn的输入X需要是二维数组 model.fit(x_data.reshape(-1, 1), y_data) # 从管道中提取系数稍微麻烦一点但预测很方便 y_pred_sklearn model.predict(x_plot.reshape(-1, 1)) # 方法2使用岭回归 model_ridge make_pipeline(PolynomialFeatures(degree), Ridge(alpha1.0)) model_ridge.fit(x_data.reshape(-1, 1), y_data)使用sklearn的好处是能轻松进行模型对比、超参数调优如用GridSearchCV搜索最佳的degree和alpha以及交叉验证。5.3 源码的模块化与封装对于一个可复用的项目我们应该将代码组织得更好。例如可以创建一个PolynomialFitter类class PolynomialFitter: 多项式拟合器支持普通最小二乘和岭回归。 def __init__(self, degree, alpha0.0, fit_interceptTrue): self.degree degree self.alpha alpha # 正则化强度0表示普通最小二乘 self.fit_intercept fit_intercept self.coef_ None # 系数约定与sklearn一致用下划线结尾表示估计量 self.intercept_ 0.0 def fit(self, x, y): 拟合模型。 # 特征工程生成多项式特征 X self._create_polynomial_features(x) # 求解系数 if self.alpha 0: # 普通最小二乘 self.coef_ np.linalg.lstsq(X, y, rcondNone)[0] else: # 岭回归 XT X.T XTX np.dot(XT, X) reg_matrix self.alpha * np.eye(X.shape[1]) # 不对截距项正则化这里简单处理实际可调整 self.coef_ np.linalg.solve(XTX reg_matrix, np.dot(XT, y)) if self.fit_intercept: self.intercept_ self.coef_[0] self.coef_ self.coef_[1:] else: self.intercept_ 0.0 return self def predict(self, x): 使用拟合模型进行预测。 X self._create_polynomial_features(x) if self.fit_intercept: # 如果拟合了截距预测时加上 return np.dot(X, np.concatenate([[self.intercept_], self.coef_])) else: return np.dot(X, self.coef_) def _create_polynomial_features(self, x): 创建多项式特征矩阵。 n_samples len(x) if self.fit_intercept: X np.ones((n_samples, self.degree 1)) start_col 1 else: X np.ones((n_samples, self.degree)) start_col 0 for i in range(start_col, X.shape[1]): X[:, i] x ** (i if self.fit_intercept else i1) return X def score(self, x, y): 计算R平方分数。 y_pred self.predict(x) ss_res np.sum((y - y_pred) ** 2) ss_tot np.sum((y - np.mean(y)) ** 2) return 1 - (ss_res / ss_tot) # 使用示例 fitter PolynomialFitter(degree2, alpha0.1) fitter.fit(x_data, y_data) print(f拟合系数: {fitter.coef_}) print(f截距: {fitter.intercept_}) print(fR²分数: {fitter.score(x_data, y_data):.4f})这样的封装将数据准备、模型训练、预测和评估集成在一起接口清晰易于测试和集成到更大的系统中。6. 常见问题与实战排坑指南在实际应用多项式拟合时你会遇到各种各样的问题。下面是我总结的一些典型“坑”及其解决方法。6.1 数值问题与求解失败问题现象当多项式次数较高或数据范围很大时程序可能报错LinAlgError: Singular matrix奇异矩阵错误或者拟合出的系数巨大无比如1e15预测结果完全错误。根本原因如前所述高次幂会导致设计矩阵X的列向量近似线性相关(X^T * X)矩阵的条件数极大求逆或求解方程时数值误差爆炸。解决方案优先使用np.linalg.lstsq或np.linalg.solve它们比直接求逆更稳定。进行特征缩放将x值标准化到[0, 1]或均值为0、方差为1的分布。这能极大改善条件数。x_mean, x_std x_data.mean(), x_data.std() x_scaled (x_data - x_mean) / x_std # 用x_scaled去拟合得到系数w_scaled # 预测时也需要先将新x值缩放再用w_scaled预测最后无需反缩放因为y的尺度不变。使用正则化岭回归这是对付过拟合和数值不稳定性的标准方法。即使是很小的alpha如1e-5也能起到稳定作用。使用更稳定的算法如奇异值分解。np.linalg.lstsq默认就使用了SVD。你也可以显式调用np.linalg.pinv求伪逆来求解w np.linalg.pinv(X) y。SVD能处理秩亏的矩阵是数值计算中最稳健的方法之一但计算量稍大。6.2 如何确定最佳多项式次数这是一个模型选择问题没有放之四海而皆准的答案但有以下系统性的方法可视化观察法像我们之前做的那样画出不同次数下的拟合曲线观察其是否平滑、是否过度扭曲。这对于低维数据1-2个特征非常直观有效。训练/验证/测试集法将数据分为三部分训练集用于训练模型、验证集用于选择超参数如degree、测试集用于最终评估。在训练集上用不同degree训练模型在验证集上计算误差如RMSE。选择验证集误差最小的degree作为最佳模型。最后用测试集评估一次这个最佳模型的泛化性能。注意测试集在整个调参过程中只能使用这唯一一次否则会“数据泄露”导致评估过于乐观。交叉验证法当数据量不大时K折交叉验证是更可靠的选择。可以用sklearn.model_selection.cross_val_score方便地实现。信息准则法如AIC或BIC。它们在衡量模型拟合优度的同时加入了对于模型复杂度的惩罚。AIC 2k - 2ln(L)其中k是参数个数L是模型似然函数的最大值。AIC/BIC越小越好。对于线性回归在误差服从正态分布的假设下AIC的计算可以简化为与对数MSE和参数个数相关。Scikit-learn的线性模型在statsmodels库中有更详细的统计输出包含AIC/BIC。实操心得在工业界可视化交叉验证是最常用的组合。先画图看个大概再用交叉验证确定一个稳健的最优范围。记住一个原则简单且有效的模型通常是更好的选择。如果二次和五次的交叉验证误差相差无几果断选择二次模型。6.3 拟合结果“跑飞”了怎么办有时你会发现拟合的曲线在数据范围之外的地方外推会以不可思议的速度飞向正负无穷。这在高次多项式中尤其常见。原因多项式函数在|x|很大时其值由最高次项主导。如果高次项系数不为零x^m会变得极其巨大。应对策略认清局限性多项式拟合以及大多数基于统计的模型主要适用于内插即在训练数据覆盖的范围内进行预测。对于外推其可靠性很差。这是模型本身的特性不是bug。限制使用范围在应用中明确说明模型的有效区间。如果必须做外推考虑使用其他模型如考虑饱和增长的逻辑函数或有物理意义的指数衰减/增长模型。使用约束拟合如果你有先验知识比如知道当x很大时y应该趋近于一个常数可以寻找支持约束的拟合方法但这超出了普通最小二乘的范围可能涉及非线性优化。6.4 类别变量或复杂关系怎么办多项式拟合只能处理单个连续变量x和y之间的非线性关系。如果你的问题更复杂多个特征你需要的是多元线性回归其原理完全相同只是设计矩阵X的列变成了各个特征以及它们的多项式项、交互项。这可以通过PolynomialFeatures轻松扩展到多维。非多项式关系如果数据看起来像指数增长、周期性变化等可以尝试通过对y或x做变换如取对数log(y)或使用sin(x),cos(x)作为特征将其转化为线性问题来处理。这称为“线性化”。完全无法线性化则需要转向更强大的非线性模型如决策树、神经网络等。7. 项目总结与源码获取走完这一趟你应该已经对多项式拟合有了从理论到实践的全方位理解。我们从最小二乘法的数学原理出发亲手推导了正规方程并用Python从零实现了拟合过程。通过可视化对比我们深刻认识了欠拟合与过拟合这一对核心矛盾并学习了通过训练测试集划分和交叉验证来选择模型复杂度的方法。最后我们还探讨了数值稳定性、正则化、工程化封装以及实际应用中常见的坑和解决方案。这个项目提供的不仅仅是一个拟合函数更是一个完整的建模思维框架问题定义 - 数学建模 - 算法实现 - 评估验证 - 分析改进。掌握了这个框架你再去学习更复杂的算法如逻辑回归、支持向量机甚至深度学习都会发现它们的内核是相通的。关于源码本文中的所有代码块都是可独立运行的。我建议你千万不要直接复制粘贴而是打开你的代码编辑器如VSCode、PyCharm或Jupyter Notebook亲手逐行敲入这些代码。在敲代码的过程中尝试去修改参数比如数据点的数量num_points、噪声大小noise_scale、多项式次数degree、正则化强度alpha观察图形和输出结果如何变化。这是将知识内化的最快途径。你可以将本章各节的代码块整合到一个或多个Python文件中形成一个完整的“多项式拟合实验工具包”。例如可以创建三个文件core.py存放polynomial_fit,polynomial_fit_ridge,PolynomialFitter等核心函数和类。utils.py存放数据生成generate_sample_data、评估evaluate_fit、可视化绘图函数。demo.ipynb一个Jupyter Notebook用于交互式地运行和展示所有示例就像本文所做的那样。我个人在最初学习时曾因为没做特征缩放用一个7次多项式去拟合范围在0到100的数据结果系数大到溢出程序直接报错。也曾经盲目追求在训练集上的高R²用一个15次多项式去拟合只有10个数据点的问题结果模型完全失去了预测能力。这些教训让我深刻理解到在建模中对数据的理解和敬畏与对数学和代码的掌握同等重要。希望这份笔记和源码能成为你建模算法之旅的一块坚实垫脚石。
返回列表