Your AI Assistant Wrote the Code. Who Checked the Defaults?

Your AI Assistant Wrote the Code. Who Checked the Defaults?

你的 AI 助手写出了代码,但谁检查了默认设置?

Ask a coding assistant for a random forest and inspect the four lines it gives you: the imports are correct, the estimator fits, and predictions come back in the expected shape. Now look at the arguments nobody specified, because those four lines have settled more questions than your prompt asked. How many features should each tree consider? How much regularization should the model apply? How should validation folds reflect the structure of your data? That is what defaults do: they let an underspecified request become an executable program.

让编程助手为你写一个随机森林(Random Forest),并检查它给出的四行代码:导入正确,估计器(estimator)可以拟合,预测结果的形状也符合预期。现在,看看那些没人指定的参数,因为这四行代码解决的问题远比你在提示词中要求的要多。每棵树应该考虑多少特征?模型应该应用多少正则化?验证折叠(validation folds)应该如何反映你的数据结构?这就是默认设置的作用:它们让一个描述不足的请求变成了一个可执行的程序。

Language models learn patterns of code through next-token prediction, which makes familiar implementations a natural starting point for generation. When an implementation leaves an argument out, the library supplies its value, allowing a statistical choice to pass from convention into your pipeline without ever becoming part of the conversation.

语言模型通过“下一个词预测”来学习代码模式,这使得常见的实现方式成为生成代码的自然起点。当实现中省略了某个参数时,库会提供其默认值,这使得一个统计学上的选择在未经讨论的情况下,就从惯例直接进入了你的工作流。

An assistant can examine those choices when prompted, but receiving working code is no evidence that they have been examined. The omission is easy to miss precisely because nothing needs to break. The five defaults below are ones I’ve encountered in my own production work and in colleagues’ projects, where they caused debugging headaches because we had overlooked what the code was doing. Each deserves the same question: what did leaving this argument out decide for me?

如果被要求,助手可以检查这些选择,但收到能运行的代码并不代表它们已经被检查过。这种疏忽很容易被忽略,恰恰是因为代码并不一定会报错。以下五个默认设置是我在自己的生产工作和同事的项目中遇到的,它们曾导致了调试上的麻烦,因为我们忽略了代码背后的逻辑。每一个都值得我们问一句:省略这个参数,到底为我做了什么决定?

1. RandomForestRegressor(max_features=1.0)

If you learned random forests as “bootstrap the rows, subsample the features, average the trees,” the default regressor comes with a surprise: RandomForestClassifier uses max_features=“sqrt”, while RandomForestRegressor uses max_features=1.0, making every feature available at every split!

如果你学习随机森林时理解的是“对行进行自助采样(bootstrap),对特征进行子采样,然后对树进行平均”,那么默认的回归器会给你一个惊喜:RandomForestClassifier 使用 max_features="sqrt",而 RandomForestRegressor 使用 max_features=1.0,这意味着在每一次分裂时,所有特征都是可用的!

That difference switches off the feature subsampling that distinguishes the usual random forest recipe from plain bagging. Bootstrap sampling is still there, so the trees remain random through the rows they receive, but the second layer of randomness (deliberately restricting which features each split can consider) is absent.

这种差异关闭了特征子采样功能,而这正是随机森林与普通 Bagging 算法的区别所在。自助采样依然存在,所以树在行数据上依然保持随机性,但第二层随机性(即刻意限制每个分裂点可以考虑的特征)却消失了。

Why does that matter? Imagine a dataset with one particularly strong predictor, such as price or discount in a sales model. Even when each tree receives a different bootstrap sample, price may keep winning the early splits because its predictive signal survives those changes in the training rows. The trees can therefore develop similar structures and respond similarly to fluctuations in the training data, limiting how much averaging reduces their shared variability.

这为什么重要?想象一个包含强预测因子的数据集,例如销售模型中的价格或折扣。即使每棵树接收到不同的自助采样样本,价格也可能在早期的分裂中胜出,因为它的预测信号在训练行的变化中依然稳固。因此,这些树可能会发展出相似的结构,并对训练数据中的波动做出相似的反应,从而限制了平均化减少共享变异性的效果。

Feature subsampling interrupts that repetition by occasionally excluding the dominant predictor from consideration, forcing trees to explore alternative splits. I unpacked the mathematics and a controlled experiment in Why Random Forest Needs to Be This Random TDS article, where the central point is that adding more trees cannot eliminate the variance component associated with their correlation.

特征子采样通过偶尔排除主导预测因子来打断这种重复,迫使树去探索其他分裂方式。我在 TDS 文章《为什么随机森林需要如此随机》(Why Random Forest Needs to Be This Random)中拆解了其中的数学原理和对照实验,其核心观点是:增加更多的树无法消除与它们相关性相关的方差分量。

With max_features=1.0, you are leaving that additional decorrelation mechanism unused, even though the estimator’s name may have led you to assume it was already operating. Setting max_features=0.33, for example, makes roughly a third of the features available at each split and restores that part of the random forest recipe.

使用 max_features=1.0 时,你实际上放弃了额外的去相关机制,尽管估计器的名称可能会让你误以为它已经在运行了。例如,将 max_features 设置为 0.33,会使每次分裂时大约三分之一的特征可用,从而恢复了随机森林算法的这一部分特性。

The question to ask your assistant is simple: if we chose a random forest partly for feature-level randomness, why have we left that randomness switched off?

问你的助手一个简单的问题:如果我们选择随机森林的部分原因是为了特征层面的随机性,为什么我们却把这种随机性关掉了?

2. LogisticRegression(C=1.0)

Ask for a logistic regression and you might expect a model that finds the coefficients that best explain the observed outcomes. What LogisticRegression() actually fits is a regularized version, with an L2 penalty that discourages large coefficients and a default C=1.0 that determines how strongly that penalty competes with fitting the data.

要求一个逻辑回归模型时,你可能期望它能找到最能解释观测结果的系数。但 LogisticRegression() 实际拟合的是一个正则化版本,它带有 L2 惩罚项以抑制过大的系数,默认的 C=1.0 决定了该惩罚项与拟合数据之间的竞争强度。

Regularization is often useful, especially when the data is noisy or the predictors overlap, but the number 1.0 has no special knowledge of your problem. Smaller values of C impose stronger regularization, while larger values give the coefficients more freedom, and leaving the argument out accepts one particular setting without examining it.

正则化通常很有用,尤其是在数据有噪声或预测因子重叠时,但数字 1.0 对你的问题没有任何特殊了解。较小的 C 值会施加更强的正则化,而较大的值则给予系数更多的自由度。省略该参数意味着你在未经检查的情况下接受了一个特定的设置。

Apart from that an even more subtle issue is that “large coefficient” depends on how you measured the feature. Suppose income is recorded in euros and its coefficient is 0.0001. Express the same income in thousands of euros and the coefficient must become 0.1 to preserve exactly the same penalty-contribution to the prediction: 50,000 × 0.0001 = 50 × 0.1 = 5.

除此之外,一个更微妙的问题是,“大系数”取决于你如何衡量特征。假设收入以欧元记录,其系数为 0.0001。如果将同样的收入以千欧元为单位表示,系数必须变为 0.1,才能保持对预测的惩罚贡献完全相同:50,000 × 0.0001 = 50 × 0.1 = 5。

The information has not changed, but the coefficient is now a thousand times larger, so its contribution to the L2 penalty, which squares coefficients, is a million times larger. When you refit the model with the same C, the optimizer faces a different trade-off simply because you changed the units.

信息没有改变,但系数现在大了一千倍,因此它对 L2 惩罚(对系数求平方)的贡献大了一百万倍。当你用相同的 C 值重新拟合模型时,优化器面临的权衡完全不同,仅仅是因为你改变了单位。

A practical starting point is to standardize continuous features inside a Pipeline, so preprocessing is fitted within each training fold, and then tune C using appropriate validation. Otherwise, your choice of euros versus thousands of euros is quietly helping decide how much regularization each feature receives.

一个实用的起点是在 Pipeline 中对连续特征进行标准化,这样预处理过程会在每个训练折叠中进行拟合,然后使用适当的验证方法来调整 C 值。否则,你选择以欧元还是千欧元为单位,都在悄悄决定每个特征会受到多少正则化影响。

3. cross_val_score(model, X, y, cv=5)

Five-fold cross-validation sounds like a reassuringly thorough check: train on four parts of the data, evaluate on the fifth, and repeat until every observation has been held out once. But cv=5 specifies the number of folds while quietly leaving another decision to scikit-learn: how those folds are constructed.

五折交叉验证听起来是一个令人放心的全面检查:在四部分数据上训练,在第五部分上评估,重复直到每个观测值都被排除过一次。但 cv=5 在指定折数的同时,悄悄地把另一个决定留给了 scikit-learn:这些折叠是如何构建的。

For regression, it uses KFold with shuffle=False, meaning the validation folds are consecutive blocks of rows in their existing order. If your dataframe is sorted by date, customer, or region, that ordering becomes part of your evaluation design, whether you intended it or not.

对于回归问题,它使用 KFoldshuffle=False,这意味着验证折叠是按现有顺序排列的连续数据块。如果你的数据框是按日期、客户或地区排序的,那么这种排序就会成为你评估设计的一部分,无论你是否有意为之。

Consider 100 weeks of sales, arranged chronologically. The first fold evaluates on weeks 1–20 after training on weeks 21–100, so the model is using later observations to predict earlier ones. The code runs perfectly, but the experiment does not represent forecasting, where only the past is available when predicting.

考虑按时间顺序排列的 100 周销售额。第一折在训练了 21-100 周的数据后,对 1-20 周进行评估,这意味着模型正在使用未来的观测值来预测过去。代码运行得非常完美,但这个实验并不代表预测场景,因为在预测时只有过去的数据是可用的。