1. 从线性回归聊起为什么单个直线模型常常不够用做机器学习实战的同学十有八九是从线性回归入门的。线性回归解释性强、计算快、结果直观甚至很多人第一次跑通模型时的成就感就来自它。但一旦你开始拿真实数据练手很快就会撞上一面墙很多问题的本质不是直线的是曲线的。举个最简单的例子某产品的销量随广告投入的变化。起步阶段广告投入产生明显的边际效应销量快速拉升到某个阈值之后再砸钱增长就不明显了甚至因为渠道饱和开始下滑。这种“先升后平甚至下降”的趋势用一条直线去拟合误差会非常难看。你画个散点图就能看到样本点明显在一个弯曲的带状区域里那条最优的直线横穿过去两头都在天上飘。再比如一个城市的气温随经纬度变化、某类器件的老化曲线、房价随面积增长的边际递减效应这些都是典型的非线性关系。你要是硬套线性回归模型在训练集上的R方可能只有0.6甚至更低残差图里还能清晰地看到“弯曲的”残留模式这其实就是一个信号模型的函数形式选错了而不是数据本身有问题。那问题来了不用神经网络、不用树模型能不能在回归框架内把这种曲线拟合出来答案是可以而且思路非常朴素——既然直线不够那就给方程加高次项。这就是多项式回归的核心动机。它的思想是把一个非线性的函数关系通过构造x的幂次特征仍然放在线性模型的框架里求解。换句话说它不是换了模型而是换了输入特征。在读那套《Python 应用机器学习代码实战指南》笔记时我印象最深的就是标题里的一个比喻——多项式回归是从线性回归到非线性拟合的桥梁。这个比喻非常准确。因为从代码实现的角度看多项式回归没有改变“拟合系数”这件事本身它改变的只是“喂给模型的特征张什么样”。所以这篇文章我想把笔记24里的内容展开讲透不只是贴一段代码而是把“为什么能这样用”“什么时候该用”“真正跑代码时坑在哪里”全部说清楚。适合刚学完线性回归、正在往非线性建模过渡的读者也适合那些虽然会用库但一直没系统理解多项式回归的实战型学习者。2. 多项式回归的核心机制线性模型为什么能拟合曲线2.1 本质是把特征“升维”不是改变模型类型先看一个简单的例子。标准的一元线性回归假设形式是y β0 β1 * x1 β2 * x2 ... βn * xn ε注意这里所谓的“线性”指的是对参数β而言是线性的而不是对特征x而言。也就是说哪怕你把x替换成x的平方、三次方、正弦值、对数值只要每个特征项前面的系数仍然是β求解过程就依然是线性回归。所以多项式回归做的事情其实就是把原始特征x扩展成一组多项式基x → [1, x, x², x³, ..., x^d]然后继续用线性回归去拟合这些新特征。以最为常见的一元二次多项式回归为例最终形式是y β0 β1 * x β2 * x² ε从视觉上看你在坐标系里得到一条抛物线但从参数求解的角度看它和普通的多元线性回归没有任何区别。这才是整个方法最精妙的地方你享受了非线性拟合的表达能力却不用引入任何新的优化算法最小二乘法照样能用。这一点我刚学时绕了很久。当时我总觉得“非线性就要用非线性模型”结果回头一看多项式回归的代码和线性回归唯一不同的地方就是对原始特征做了一次转换。转换之后它就是用线性回归训练了一个“看起来是非线性”的模型。2.2 多项式特征的一般形式和自由度陷阱更一般地假设原始数据有p个特征我们想用d次多项式去拟合那么特征会扩展成所有阶数不超过d的单项式的集合。比如p2d2时扩展特征是[1, x1, x2, x1², x1*x2, x2²]这里有几个容易踩坑的细节。第一交互项x1*x2会被自动包括进来这对特征之间的协同效应建模很有用但也会显著增加特征数量。第二特征数量是按照组合数增长的不是线性增长的。p个特征、d次多项式扩展后的特征数量大约是C(pd, d)。在p和d都不大的时候还无所谓一旦p到10、d到4特征数量就会非常庞大训练速度和内存消耗都会上来。“自由度上升”听起来是件好事毕竟模型的表达空间大了。但它同时也是过拟合的温床。每个新特征都会带来一个可调的参数参数越多模型就越容易去“背诵”训练数据中的噪声。一个常见的表现是训练集上的得分逼近完美但验证集上一塌糊涂画出来的拟合曲线像过山车一样在样本点之间疯狂抖动。2.3 为什么说它是“桥梁”这个比喻值得展开说一下。因为很多非线性建模方法是一次性跳到复杂模型上的比如支持向量回归、随机森林、神经网络。它们确实强大但内部机制的黑盒程度也比较高解释起来相对困难。多项式回归却完全不一样。它让你在不跳出回归家族的前提下第一次体会到非线性的拟合效果。而且它的可解释性依然保留得很好——每个特征项前面都有一个明确的系数你仍然可以说“x²每增加一个单位y倾向于增加β2个单位”。这种“从线性到非线性”的渐进过程对初学者的认知提升很有帮助。同时它也是理解后面很多更复杂方法的基础。你理解了把原始特征映射到高维空间的基本思路以后去理解核方法、样条回归包括理解深度神经网络中“特征变换”的概念都会容易很多。3. 手工推导与直觉建立从一元二次多项式回归开始3.1 一个具体场景的数据生成理论讲完了来看个具体的实操例子。假设我们要研究“运行时长”与“设备温度”之间的关系。业务经验告诉我们设备刚启动时温度上升很快运行一段时间后增速放缓逐渐趋于一个稳定值。这显然是一条带饱和趋势的曲线近似于对数增长或者带二次项的函数。我先用代码生成一组模拟数据方便后续演示。这里我用 numpy 构造一个真实的函数再加入随机噪声这样我们既知道“真实答案”又能检验模型的恢复能力。import numpy as np import matplotlib.pyplot as plt np.random.seed(42) X np.linspace(0, 10, 60).reshape(-1, 1) # 真实关系温度 3 5*x - 0.8*x^2 噪声 y 3 5 * X.ravel() - 0.8 * X.ravel()**2 np.random.normal(0, 1.5, 60) plt.scatter(X, y, alpha0.6) plt.xlabel(运行时长) plt.ylabel(设备温度) plt.title(模拟的非线性数据) plt.show()这里的真实关系是二次函数加了幅度为1.5的高斯噪声。注意噪声不小几乎是信号强度的1/3了用来模拟真实场景中的不确定性。3.2 用普通线性回归试试看按照“先做基线再上改进”的习惯我们先用普通的线性回归跑一遍看看效果有多差。from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score lr LinearRegression() lr.fit(X, y) y_pred_lr lr.predict(X) print(线性回归 R2: {:.4f}.format(r2_score(y, y_pred_lr))) print(线性回归 RMSE: {:.4f}.format(mean_squared_error(y, y_pred_lr) ** 0.5))跑完大概率看到R2大概在0.75到0.8之间徘徊RMSE接近2。这个结果放在某些场景里可能“看着还行”但你一旦把拟合直线画在散点图上就会看到它在数据中间穿过两端的偏差非常明显。残差图也不是随机散布在0附近的而是呈现明显的二次曲线形态。这就说明模型没有捕捉到数据中的结构纯粹是函数形式选错了。3.3 构造多项式特征并训练模型接下来进入正文的核心步骤把X扩展成包含x²的特征矩阵再喂给线性模型。from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline # 用管道整合特征构造和回归器 degree 2 poly_model make_pipeline( PolynomialFeatures(degreedegree, include_biasFalse), LinearRegression() ) poly_model.fit(X, y) y_pred_poly poly_model.predict(X) print(多项式回归(degree2) R2: {:.4f}.format(r2_score(y, y_pred_poly))) print(多项式回归(degree2) RMSE: {:.4f}.format(mean_squared_error(y, y_pred_poly) ** 0.5))这一次你会看到R2大幅提升基本在0.94以上。RMSE大幅下降接近数据本身的噪声标准差。画出来的拟合曲线平滑地穿过散点云的中心残差也均匀分布在零线附近不再有明显的模式。到这里我们就完成了一个最简单的“从线性到非线性“的完整闭环。细节上需要注意我在构造PolynomialFeatures时设置了include_biasFalse。这是因为LinearRegression本身会默认拟合截距项如果两边同时保留常数1的特征列就会造成矩阵的秩亏虽然sklearn通常不会报错但容易引起数值稳定性问题而且截距的解释会变得混乱。建议在机器学习项目里统一按这个思路处理。3.4 多项式回归实现组合参数的核心方法精读那套代码实战指南之后发现真正使用多项式回归时最稳健的实战手法不是手动通过numpy的column_stack去拼接x和x²而是调用sklearn的PolynomialFeatures组件。原因很简单。第一PolynomialFeatures支持任意次数的扩展还能自动生成交互项。如果你的数据不止一个特征手动拼接会非常容易出错写出来的代码也难维护。第二它支持include_bias参数明确控制是否生成常数项这比你自己在特征矩阵里补一列1要清晰得多。第三它是transformer体系的一部分可以直接和Pipeline、StandardScaler集成。要注意多项式回归有一个隐藏的问题——特征的值域会随着幂次上升急剧膨胀。比如x的范围在[0, 10]x²就跑到100如果是三次方就是1000。如果不同特征的量纲差异大求解最小二乘时的数值稳定性会变差。一种公认的实践方案是先做多项式扩展再做标准化。因为StandardScaler是在扩展后的特征空间里工作这样系数求解就不会被巨大的数值压倒。也正因如此真正拿到生产环境里的代码基本都会包装成类似下面这样from sklearn.preprocessing import StandardScaler model make_pipeline( PolynomialFeatures(degree3, include_biasFalse), StandardScaler(), LinearRegression() )4. 度数的选择从欠拟合到过拟合的平衡艺术4.1 度数太低与太高分别会发生什么多项式回归里最关键的参数只有一个度数d。这个参数直接决定了模型的复杂度。d1退化为普通线性回归如果数据本身是曲线的就会欠拟合训练误差和验证误差都偏高。d2可以拟合二次曲线有且仅有一个“转弯”。d3可以拟合S形或带两个“转弯”的曲线。d越高模型能表达的形状越复杂但参数也越多出现震荡的风险越大。实操中一个比较典型的场景是管道的度数为3时训练集和测试集得分都不错调到5时测试集得分反而下滑调到10时训练集得分几乎是1.0但测试集可能比d3时还要差。如果把拟合曲线画出来你会看到一条在样本点之间大幅度震荡的线那就是过拟合的典型画面。理解这个现象要从“方差vs偏差”的角度看。低度数对应高偏差模型过于简单连真实结构都表达不出来高度数对应高方差模型对训练数据中的每一个微小抖动都过于敏感换一批数据就面目全非。多项式回归的学习过程就是在寻找一个把总误差偏差的平方 方差 不可约噪声压到最小的点。4.2 用验证曲线辅助选择度数临界问题是数据量不大时画训练误差和验证误差随d变化的曲线往往能给出非常直观的决策依据。sklearn提供了validation_curve函数可以直接帮我们做这件事。from sklearn.model_selection import validation_curve from sklearn.pipeline import make_pipeline param_range [1, 2, 3, 4, 5, 6, 8, 10] train_scores, test_scores validation_curve( make_pipeline(PolynomialFeatures(), LinearRegression()), X, y, param_namepolynomialfeatures__degree, param_rangeparam_range, cv5, scoringr2 )画出两条曲线的均值后通常会出现三个区域度数较小时训练分和验证分都低属于欠拟合区中间某段训练分和验证分都比较高且两条曲线距离不远属于合理区度数继续增大训练分继续上升验证分却开始掉头下降属于过拟合区。选度数的基本原则是选择验证分数最高、并且与训练分数差距不太大的最小度数。注意这里强调“最小度数”因为更小的度数意味着更简单的模型、更好的可解释性、更低的部署风险。在拟合能力相近的前提下选简单模型永远是更好的选择。4.3 一个容易忽略的细节特征缩放的必要性不少初学者在跑多项式回归时不做特征标准化模型偶尔也能跑出不错的结果。这容易造成一个错觉标准化可有可无。但一旦深度上升到4、5特征值的数量级差异会非常离谱。比如x取[0, 100]x⁴就是1亿级别的数值而x本身还是两位数。这种情况下最小二乘法的正规方程会面临严重的条件数问题。所谓条件数通俗理解就是解对输入数据的微小扰动有多敏感。条件数很大的时候哪怕数据有一个极小的噪声解出来的系数都可能天翻地覆。而标准化能压缩特征尺度让矩阵的条件数控制在合理范围。所以我在自己的项目里有个比较顽固的习惯只要用了PolynomialFeatures后面必接StandardScaler哪怕度数只有2。这行代码成本极低但能避免大量莫名其妙的系数爆炸问题。5. 真正把代码跑起来一个完整的拟合流程演示这一节我们完整地走一遍从数据生成到模型评估的全部代码。在分析复杂真实问题时把模型组件拆成可复用的流水线是很有价值的。5.1 生成带噪声的非线性数据为了模拟真实场景我们不用太干净的数据。这里用三条不同曲线拼接出更复杂的形态增加拟合难度。import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.preprocessing import PolynomialFeatures, StandardScaler from sklearn.pipeline import make_pipeline from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error np.random.seed(42) X np.linspace(-3, 3, 100).reshape(-1, 1) # 真实函数y 1.5 2x - 0.9x^2 0.4x^3 噪声 y 1.5 2 * X.ravel() - 0.9 * X.ravel()**2 0.4 * X.ravel()**3 y np.random.normal(0, 0.8, sizey.shape) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.3, random_state42)噪声幅度0.8不算小足以让高度数的模型在过拟合边缘试探。5.2 实现多个度数的对比并进行数据可视化为了直观展示度数的影响我们把d1、2、3、9四个模型全部训练一遍画出它们在测试区间上的预测曲线。degrees [1, 2, 3, 9] plt.figure(figsize(12, 8)) colors [#999999, #1f77b4, #d62728, #2ca02c] X_plot np.linspace(-3.2, 3.2, 300).reshape(-1, 1) for i, d in enumerate(degrees, 1): model make_pipeline( PolynomialFeatures(degreed, include_biasFalse), StandardScaler(), LinearRegression() ) model.fit(X_train, y_train) y_plot model.predict(X_plot) plt.subplot(2, 2, i) plt.scatter(X_train, y_train, s15, alpha0.6, labeltrain) plt.plot(X_plot, y_plot, colorcolors[i-1], linewidth2, labeldegree{}.format(d)) plt.legend() plt.tight_layout() plt.show()从图里能明显看出d1时曲线是一条直线完全跟不上数据趋势d2时有了一次弯曲但中间段仍存在系统性偏差d3时曲线非常好地贴合数据整体趋势弯曲自然d9时训练样本点被一个个“穿”过去曲线疯狂震荡这就是最高度上的过度专升本。5.3 比较训练集与测试集上的指标光看图不够还得有数值上的证据。把四个模型在测试集上的指标打出来。for d in degrees: model make_pipeline( PolynomialFeatures(degreed, include_biasFalse), StandardScaler(), LinearRegression() ) model.fit(X_train, y_train) y_pred model.predict(X_test) r2 r2_score(y_test, y_pred) rmse mean_squared_error(y_test, y_pred) ** 0.5 print(degree{}, test R2{:.4f}, test RMSE{:.4f}.format(d, r2, rmse))典型的结果趋势是度数训练R2测试R2测试RMSE拟合曲线特征10.810.781.42直线系统性偏差明显20.920.910.91一个弯道整体可用30.950.940.74贴合好曲线自然90.980.890.99训练好但测试下降过拟合注意d3时的测试R2已经接近实际噪声上限d9时训练R2更高但测试R2下滑这就是过拟合的直接证据。如果你只盯着训练集指标选模型在这个例子里大概率会选错。5.4 查看模型的系数与解释多项式回归的一大优点就是系数可以解释。打印一下d3时模型的系数model_final make_pipeline( PolynomialFeatures(degree3, include_biasFalse), StandardScaler(), LinearRegression() ) model_final.fit(X_train, y_train) feature_names [x, x^2, x^3] coeffs model_final.named_steps[linearregression].coef_ for name, coef in zip(feature_names, coeffs): print({}: {:.3f}.format(name, coef))这里你会看到一个很有意思的现象系数的绝对值大小并不完全等于“重要性”因为标准化之后特征已经在同一尺度上绝对值大的系数通常意味着该特征对预测贡献更显著。当特征的幂次增加时各系数呈现出变小的趋势这反映了多项式基函数的数值结构在这种情况下平缓地解读而不提出过强的因果主张是关键。6. 实用性反思什么时候该用什么时候不该用6.1 适用场景与边界多项式回归擅长拟合那种“规律性强、单调或低波动、特征数量少”的数据。典型场景包括物理和工程中的标定曲线比如传感器的输出与实际物理量之间的映射关系。经济学里的边际效用分析比如投入与产出之间的递增或递减趋势。时间序列里的趋势项建模在明显向上的趋势中捕捉增长速度的放缓。作为基线模型与其他复杂模型对比让团队理解数据的基本形态。它不适合的场景也很明确当数据维度很高、样本量很小、函数关系极其复杂时多项式回归可能在尾部造成震荡导致预测能力崩溃。6.2 多项式回归和其他非线性模型的对比模型优点缺点适合场景多项式回归可解释、实现简单、训练快高纬度易过拟合、对噪声敏感单特征/低维数据、曲线趋势样条回归局部灵活、尾部稳定需要选择节点位置数据存在明显分段结构随机森林回归鲁棒性好、自动处理非线性无法预测区间外、解释力弱特征多、关系复杂的表格数据支持向量回归高维下表现不错参数敏感、训练慢中小规模、中等复杂度数据如果你手中的数据只有一两个关键特征并且你希望给业务方一个清晰可解释的公式多项式回归基本就是最佳选择。如果特征数量较多动不动几十上百维请慎重考虑。我的习惯是先跑一个欠拟合的线性回归模型作为基线再用多项式回归看看提升幅度大小。如果提升不明显就直接跳到正则化模型或树模型。6.3 正则化与多项式回归的搭配度数高了容易过拟合但有时候业务场景确实需要较高度数来捕捉细微趋势。一个比较成熟的策略是让度数稍微给高一点点然后通过正则化控制系数的膨胀。Lasso和Ridge在线性回归里很常见。在Ridge模型中它通过对系数平方和施加惩罚让模型在拟合能力和复杂度之间找平衡。因为多项式特征存在天然的数值相关性L1正则化可以产生稀疏解并有效处理高次项和交互项。from sklearn.linear_model import Ridge ridge_model make_pipeline( PolynomialFeatures(degree8, include_biasFalse), StandardScaler(), Ridge(alpha1.0) ) ridge_model.fit(X_train, y_train)在不改度数的情况下Ridge通常能让过拟合曲线重新变得平缓。但这需要调整α参数实际操作可以配合网格搜索。6.4 数据范围的边界问题多项式回归在训练数据范围内的拟合效果往往不错但在范围外却容易出大问题。以二次函数y x²为例训练数据只在[0, 1]之间那模型学到的是一个斜率较大的上升曲线。如果你用它预测x5甚至x10数值就直接起飞了。这在业务中是个很隐蔽的坑。线上预测数据虽然通常不会离训练分布太远但偶尔会有极值样本进来。如果模型是多项式回归单个异常输入就可能导致输出值极不合理。解决方式有三一是严格限制预测输入的范围检查超范围走兜底逻辑二是改用样条回归它在两端都倾向于外推为线性三是在训练数据里明确构造边界外的虚拟样本做约束。第三种我不太建议因为它本质上是在制造假信息确实需要边界控制时更换模型更合理。7. 实操踩坑合集这些坑我一个个都替你踩过了7.1 先标准化还是先构造多项式特征结果差别很大常见的一个错误是在多项式转换之前做标准化。如果先标准化原始x已经变成均值0标准差1的数据再做平方和立方得到的特征与“原始尺度的平方”完全不同。直接的影响是拟合出来的曲线形状虽然可能差不多但系数解释性变得混乱有时候还会轻微影响数值稳定性。我建议的顺序是先构造多项式特征再统一标准化最后进回归器。上面代码里已经体现这是实测下来最稳的顺序。7.2 模型的评估指标不可单独看训练得分很多项目为了图省事只输出一个训练集上的R2就结束了。在多项式回归场景这是很危险的做法。因为训练得分几乎总随度数上涨而上涨你很容易被“0.99”这种漂亮数字欺骗。务必至少分开训练集和测试集更规范的做法是使用交叉验证并同时考察R2和RMSE两个指标。7.3 特征很多时的交互项爆炸问题当你的原始特征不止一个时PolynomialFeatures会生成大量的交互特征。例如10个原始特征、3次多项式的组合特征数量已经超过200个。如果你样本量只有几百条这几乎是必然过拟合。一个实用的应对措施是使用InteractionOnly参数。它只生成交互项而不保留高次项可以降低特征扩展规模。但不能只依赖这一步必要时要加上特征选择或正则化。7.4 决策边界在分类任务里的推广多项式回归的思想也被广泛用于分类问题。你在分类任务里给逻辑回归配上多项式特征往往能获得一个非线性的决策边界。比如两个类别的数据呈圆形分布时线性分类器完全无能为力但加入x²和y²特征后逻辑回归就能轻松得到圆形边界。这个技巧在很多课程里都属于进阶内容但实际上和回归场景里的操作完全一致值得顺手掌握。7.5 用交叉验证代替肉眼调度数肉眼观察曲线确实是调参数的好帮手但绝不能只靠它。有一次我在一个含噪比较大的数据上反复试度数发现4次多项式似乎拟合得很好曲线非常平滑几乎完美穿过所有样本点。可一交叉验证4次和3次的验证分数没有显著差异而3次模型的方差更小。后来的经验教训告诉我选模型不能只看图多好看要看验证集上稳定性的数值证据。更精确的做法是把训练过程嵌入GridSearchCV把度数、是否标准化、是否用正则化当作参数空间一起搜索。这样不仅更客观还能避免人工试参时的“手感偏好”。from sklearn.model_selection import GridSearchCV pipe make_pipeline( PolynomialFeatures(include_biasFalse), StandardScaler(), Ridge() ) param_grid { polynomialfeatures__degree: [2, 3, 4, 5], ridge__alpha: [0.1, 1.0, 10.0] } gs GridSearchCV(pipe, param_grid, cv5, scoringr2) gs.fit(X_train, y_train) print(best params:, gs.best_params_)GridSearchCV输出的是参数组合而不只是某一个模型的得分。把多项式回归放进网格搜索本质上是在自动化地选择整个处理流程的最优配置减少人为偏好带来的偏差。8. 读完笔记之后的实战总结与补充经验8.1 建立一个标准的模型诊断流程每次用多项式回归建模我基本都会按照固定流程走一遍效率最高也最不容易漏坑。第一步画散点图。先看数据本身是否具有明显的曲线趋势。第二步跑一个线性回归基线记录R2和RMSE。第三步从d2到d5逐个尝试输出训练和验证指标。第四步画拟合曲线和残差图从视觉上确认曲线的合理性。第五步比较候选模型在测试集上的表现同时结合业务可解释性做最终选择。第六步锁定模型后用全量数据重新训练方便部署。这套流程看着繁琐实际跑一遍也就十几分钟。但它能显著降低“看起来很好但上线就崩”的概率。8.2 记录一个印象深刻的实战案例我在某次数据分析实战里处理过一组关于设备温度预测的数据。数据量不大只有200多条但关系明显是曲线。领导要求模型必须能解释不能用黑箱。我当时的做法是先跑线性回归R2只有0.71再跑多项式回归d2R2到了0.89。加上第三个特征后略微提升到0.91但系数的置信区间明显变宽。最终权衡之下我选择了d2模型并用d3的结果作为敏感性分析材料。最后业务方对这个模型接受度很高一个重要的原因就是它能指着公式里的每个系数讲清楚含义。这对技术项目来说也是一种价值。8.3 进一步扩展的方向如果这篇文章的内容你已经完全消化接下来可以往三个方向扩展学习。第一个是样条回归它比多项式回归更善于处理分段结构可以减少高次多项式引起的全局振荡问题。第二个是广义加性模型它在保持解释性的同时允许每个特征拥有独立的变换函数。第三个是核岭回归它通过核技巧在高维空间中做类似的事情不需要显式构造高维特征。这三个方向里样条回归和广义加性模型与多项式回归的衔接最自然。理解了多项式特征后你会发现这些方法本质上都是在处理“如何更聪明地构造特征”这个问题。理解这一层之后你在非线性建模的路上就算是真正进门了。8.4 最后分享一点个人的实践经验我自己的学习路径是先把多项式回归的数学形式推导清楚再去写代码。当初图省事直接调库时很多东西都是糊涂的比如“为什么曲线能过样本点为什么过拟合会发生”这些只有亲手推导过、亲手画过几次图才能真正理解。所以如果你是刚入门的读者我的建议是不要只满足于跑通管道。试着用最小二乘法手写一次二次多项式回归或者用numpy手动构造特征列再丢给LinearRegression这个把“魔法”拆开的过程会大幅提升你对这个模型的掌控力。遇到“拟合曲线在样本点间震荡”这类问题时也不用慌张那恰恰说明你正在经历从看懂代码到理解模型本质的过渡阶段。每一次挖掘背后原因的经验都在为更复杂的方法积累直觉。