685. MITSUI&CO. Commodity Prediction Challenge | mitsui-commodity-prediction-challenge
首先,我完全惊呆了。这次比赛对我来说完全出乎意料。早上我收到了结果通知。我打开排行榜页面,习惯性地滑到底部(因为看不清),然后突然抬头看向顶部——我彻底震惊了。这是一段从底部到顶部的旅程!相信我,这份总结并不晚——我只是昨天一直在审查我的整个代码,反复检查我是否做错了什么。
这份总结提出了一些可能有争议的观点——甚至我自己也不总是能回答我自己的问题。我只是遵循了一些基本原则,作为一个初学者,这是我第一次参加时间序列比赛。所以我专注于我能做到的最简单的特征工程和最简单的模型工程。
这是一份获得第三名的解决方案总结,它使用了基本的特征工程和简单的模型,并进行了端到端的实现。训练和预测阶段都完全执行——模型是在提供的数据上从头开始训练的,预测的生成方式与 Kaggle 评估 API 的运行方式完全一致。这种方法的关键创新在于专注于特定的目标对(target pairs),进行稳健的特征和模型工程,同时小心避免噪声和过拟合,以保持稳定、可靠的预测。
这次比赛不是典型的时间序列挑战。主办方强调的一些重点有助于澄清任务。
他们提到:
“特别是,参赛者被挑战预测价格差异序列——源自两种不同资产价格之间的时间序列差异——以提取稳健的价格变动信号作为特征。”
这一点至关重要。它表明参与者需要在特征工程上保持保守,专注于有意义的信号,而不是增加噪声。目标是价格差异,其本身波动性更大且风险更高。因此,模型必须稳定且稳健,能够捕捉方向和相对运动而不对噪声反应过度,从而确保随时间推移的可靠预测。
接着,他们提到:
“准确的商品价格预测对于降低金融风险和确保全球市场稳定至关重要。目前,商品价格固有的波动性和不可预测性造成了若干问题。公司在资源分配、预算和投资规划方面举步维艰,往往导致财务损失和运营效率低下。不准确的预测也可能导致市场不稳定和价格波动,对生产者、消费者和投资者产生负面影响。”
作为一名机器学习工程师,我的首要责任是构建稳定且稳健的模型。它们必须智能地应对市场波动,而不对短期噪声反应过度。换句话说,一个可靠的模型即使在高度波动的时期也应保持稳定的性能,避免预测不确定性的大幅波动,同时捕捉真正的潜在市场趋势。
最后,他们包括:
“虽然这些模型显示出希望,但它们往往在跨不同市场条件和长期范围内一致实现高准确性方面遇到局限性。现有模型可能表现出对特定数据模式的过度依赖,缺乏驾驭金融市场动态和不断演变性质所需的适应性。”
为了解决这一挑战,可以利用时间序列特征工程从历史数据中提取有意义、稳健的信号。通过专注于稳定的趋势、资产之间的相对运动以及精心设计的滞后或聚合特征,模型可以更好地泛化并在波动条件下保持一致的性能。
作为 ML 工程师,我需要关注的重点是:
我的判断:我将其视为补充数据集(稍后解释)。
我的判断:
这种设置有点像“陷阱”,并影响了我在这次比赛中的战略决策。
战略选择
最初,在第一次提交后,我位于排行榜底部,这让我对自己的方法感到不确定。我重新评估了我的基础、特征工程和代码。考虑到公共排行榜不是一个可靠的信号,我选择了选择 2,使用整个数据集进行训练以最大化私有排行榜性能。
重要说明:我的训练策略与典型方法不同。我专注于目标对的稳健特征工程、仔细的模型设计,并避免过拟合波动信号,而不是追逐公共排行榜分数。
我的判断:
从机器学习工程师的角度来看,这个数据集很重要,但经常被忽视。每一列代表一个不同的目标,源自两种不同的资产,正如比赛主办方所指定的。这意味着:
计算说明:
根据 target_pairs.csv 的描述:
“请参阅 此笔记本 以了解如何使用此信息从价格数据转到目标。”
我遵循了这个示例,并通过编程重新计算了目标,而不是直接依赖 train_labels.csv。这确保了我的目标值与我的特征工程管道完美对齐,同时仍利用数据集来验证正确性。
虽然最初令人困惑,但这种方法允许我在目标和模型之间保持清晰的一对一映射,我发现这是本次比赛最稳健的策略。
这个文件对于理解目标如何生成是最重要的数据集。它的列:
关键要点 / 我的判断:
1.2.4.1 理解 target_pairs.csv 条目
target_0 | 1 | US_Stock_VT_adj_close
| 字段 | 含义 |
|---|---|
target_0 |
要预测的列/标签 |
1 |
滞后 — 提前 1 天 |
US_Stock_VT_adj_close |
用于计算回报的资产 |
1.2.4.2 目标公式:
对于给定的一天 d,目标计算为:
target_0[d] = log(Price[d+1]) - log(Price[d])
其中:
Price[d+1] → 未来价格(第 d+1 天)
Price[d] → 当前价格(第 d 天)
所以我继续前进,
基于比赛结构和来自 target_pairs.csv 的见解,我采用了以目标为中心的训练策略。
通过为每个目标训练一个模型并将特征限制为直接相关的特征:
因此,我选择了一种由 target_pairs.csv 直接指导的选择性、特定于目标的特征工程策略。
这与比赛对价格差异建模、稳健性和稳定性的强调相一致,而不是激进的特征扩展。
数据准备管道的设计严格符合 target_pairs.csv 中定义的目标特定结构。我没有将数据集视为单一的全局时间序列问题,而是独立准备每个目标,仅使用直接有助于其定义的信息。
对于 target_pairs.csv 中的每个条目,管道动态构建专用数据集:
这确保每个训练样本仅反映用于生成目标本身的因果结构。
解析 pair 列以识别参与目标计算的一个或两个资产。
这种设计选择:
仅使用 train_df 中实际存在的列,使管道对缺失或稀疏特征具有稳健性。
为了保持因果关系并防止前瞻性偏差,仔细对齐特征和目标:
len(x_train) == len(y_train)测试样本取自 t − lag,完全镜像了推断期间预期的预测方式。
这种对齐保证:
金融时间序列经常包含由于市场休市、流动性不足或数据质量问题导致的缺失值。
为了确保稳定性:
这避免了:
每个目标模型都使用整个可用的历史窗口进行训练,仅受滞后约束。
这反映了一种信念:
这种数据准备策略反映了三个核心原则:
这种方法故意牺牲特征广度以换取泛化、稳健性和可解释性,这最终被证明对于获得第 3 名是有效的。
测试数据准备严格遵循训练期间使用的相同假设。
对于每个目标,我完全依赖 target_pairs.csv。该文件指定了用于计算目标的资产和滞后。在推断期间,我只使用那些确切的资产作为输入特征。不引入数据集中的其他列。
这确保模型在训练和测试期间看到相同的特征空间,避免任何不匹配或隐藏泄漏。
对于 target_pairs.csv 中的给定行:
如果测试数据以非 Pandas 格式到达(Kaggle 评估期间有时会发生这种情况),则明确将其转换为 Pandas 以保持管道一致。
缺失值使用与训练管道相同的前向填充后跟后向填充进行保守处理。这避免了删除行并保持了时间连续性。
我故意避免在测试集上添加任何额外特征,就像训练一样。
除了 target_pairs.csv 中明确定义的跨资产交互外,没有,也没有依赖未来信息的转换。
这镜像了真实的部署场景:
在推断时,模型应该在原始、可用的市场信号上运行,而不是可能引入不稳定或泄漏的工程信号。
在将特征空间限制为仅在 target_pairs.csv 中定义的资产之后,我在这些基础信号上应用保守的时间序列特征工程。
目标不是最大化复杂性,而是在固有的波动价格差异目标周围引入时间稳定性和方向背景。
作为一名机器学习工程师,我真的不确定在训练时是否应该使用负滞后或负窗口。在大多数生产系统中,这些通常被避免,老实说,我在添加它们之前犹豫了很多。
但这场比赛不是标准的预测任务。我们要处理的是跨市场关系,而不是单一资产独立移动。在这样的市场中,如果一个资产在第 d 天发生变化,另一个资产可能会在第 d + t 天做出反应。同时,有些情况下,第 d − t 天发生的变动仍然会影响第 d 天的行为。市场并不总是干净或即时地做出反应。
如果我只使用正滞后,模型主要会学习:
“这种模式以前发生过,所以明天它可能会再次发生。”
但市场并不总是如此干净地重复模式。有时重要的问题是:
“类似的模式是如何解决的?”
为了处理这种不确定性,我引入了负滞后和窗口特征。我的意图不是让模型“看到未来”,而是帮助它学习围绕模式的稳健结构,而不是过拟合到单一的方向性尖峰。
我经常用交易员类比来思考这一点。看图表的交易员不仅仅记住昨天发生了什么。他们查看类似的形态在过去是如何表现的——有时它们继续,有时反转,有时稳定。我希望我的模型具有类似的背景感。
当然,我对数据泄漏很小心。我使用了严格的时间分割。所有特征计算仅在训练数据上完成。如果负滞后超出范围,它自然会变成 NaN。我的预处理管道在数据到达模型之前安全地处理了这些。没有注入任何来自未来的信息,Kaggle 的评估 API 进一步确保无法访问未来信息。
由于这种设置,负滞后的作用更像是稳定机制,而不是预测捷径。它们降低了模型在高度波动期间反应过度的倾向——当目标本身就是价格差异且固有噪声时,这一点极其重要。
提交后我多次质疑这个选择。我一直问自己是否做错了什么。但最终,我相信结果回答了这个问题。如果这种方法导致泄漏或过拟合,我的模型就不会从排行榜底部攀升到顶部。
对我来说,负滞后不是关于用明天的数据预测明天。它们是关于教导模型在不可预测的市场中保持冷静、一致和方向感知。
金融时间序列数据固有地嘈杂。价格差异、回报和工程化的滞后特征通常包含极端值、零、缺失点或数值不稳定性,如果不仔细处理,可能会悄无声息地破坏模型。因为我的目标是稳定性而不是侵略性,所以我设计了一个简单但稳健的预处理管道,专注于数值安全和一致性,而不是繁重的转换。
第一步使用安全包装器应用 log1p 变换。
金融特征通常跨越多个数量级,特别是在结合原始价格、差异和工程指标时。应用对数变换有助于:
变换的实现是安全的,因此零或接近零的值不会导致数值问题。这一点尤其重要,因为滞后和差异特征经常产生小值或零值。
在对数和差异运算之后,可能会出现无限值(例如,当除以非常小的数字时)。与其允许这些值传播并破坏训练,不如将所有无限值明确转换为 NaN。
此步骤确保管道永远不会将无效的数值传递给模型。
缺失值自然出现在时间序列特征工程中:
我没有删除行或应用激进的填充策略,而是使用中位数插补,它对异常值具有稳健性并保留每个特征的中心趋势。这个选择符合最小化噪声放大的总体理念。
最后,所有特征都被标准化为零均值和单位方差。由于此解决方案中使用的模型(树集成和堆叠模型)在优化和聚合期间对特征尺度敏感,标准化确保:
该模型使用两级堆叠集成架构,通过组合多个异构学习器并学习如何最佳地对它们进行加权来提高预测性能。

不同的模型捕捉不同的数据模式:
我们没有选择单一模型,而是通过元模型学习如何最佳地组合它们。
| 模型 | 优势 |
|---|---|
| LightGBM | 快速梯度提升,处理大特征空间 |
| Random Forest | 对噪声稳健,减少过拟合 |
| XGBoost | 强大的偏差 - 方差权衡 |
每个基础模型都在相同的训练数据上独立训练。
在训练每个基础学习器之后:
这些预测被用作特征,而不是最终输出,
重塑为列向量,水平堆叠形成元数据集。
元数据集:
这将问题从 原始特征 -> 目标 转换为 元预测 -> 目标,这就是它们学习预测的方式。
最终估计器是另一个 XGBoost 回归器:
元模型的作用:
所以,整个流程非常顺畅和酷,所有模型都存储在内存中,因为它们很轻量级,所以能很快得出结果。所以我确保了模型多样性、减少偏差和方差、模块化架构、易于扩展、生产友好的架构。
目前,基础模型在相同的训练数据上训练和评估,但这在现实生活中并不是一个稳健的解决方案。
生产级改进:对元特征使用 K 折交叉验证的袋外(OOF)预测。
这正是 Kaggle 金牌级解决方案所做的。
在我的情况下,我故意选择不进行大量的微调和交叉验证,尽管我知道这可能会带来微小的短期收益。系统必须在运行时训练和预测,计算资源有限,特征和模型数量庞大,因此推动激进的超参数搜索将违背问题的实际约束。更重要的是,我意识到在如此嘈杂的环境中进行过多的调整通常会导致过拟合,特别是对公共排行榜。我不希望模型对噪声或偶然的相关性做出反应只是为了爬上公共排行榜,因为这通常在私有排行榜上崩溃。相反,我依赖模型多样性和简单、稳定的配置,允许多个学习器捕捉信号的不同方面,同时控制方差。如果该方法真的过拟合了,性能就不会一致地从底部提高到顶部。目标从来不是提取最后一点性能,而是构建一些在未见数据下稳健、可泛化且稳定的东西——这种心态指导了我所做的每一个建模决定。
老实说,无论我对模型做了什么测试,与其说是深入的验证练习,不如说是一种形式。我确实查看了预测、分数和模型行为,但在实践中我没有什么可以改变的,因为我既没有微调模型,也没有足够的时间深入探索替代方案。这是一场时间序列比赛,所以自然地要注意你的分数并密切关注模型随时间的行为,但我也明白仅仅遵循分数是永远不够的。特别是公共排行榜,感觉具有误导性,我故意选择不依赖它。在许多时候,我真的不知道我的模型是表现得“更好”还是“更差”,甚至不知道正确的下一步应该是什么。我没有冲动地对排行榜反馈做出反应,而是坚持我最初的假设和结构,相信一致性和时间纪律比追逐短期验证信号更重要。事后看来,这种不确定性成为了过程的一部分,而不是弱点——我是在没有保证的情况下构建,只有原则。
尽管如此,为了形式起见,这是我的分数:
| 分数 | 值 | 备注 |
|---|---|---|
| MAE | 0.01961 | 低绝对误差;平均而言预测在幅度上接近 |
| RMSE | 0.02931 | 略高于 MAE;偶尔存在较大的偏差 |
| R² | -0.11228 | 负值;模型解释的方差少于预测均值 |
| Corr | 0.09097 | 非常低;预测和实际序列几乎没有一起移动,捕捉方向能力差 |
我的模型显示出较低的 MAE (0.01961) 和 RMSE (0.02931),表明绝对误差较小,但负的 R² (-0.112) 和非常低的相关性 (0.091) 表明它难以捕捉运动的幅度。这表明它预测的是方向而不是精确值,这在嘈杂的金融时间序列中是可以接受的,其中方向准确性比尖峰更重要。然而,如果精确值预测是目标,则模型欠拟合。总体而言,它适合趋势跟踪或制度感知预测,但对于精确幅度预测较弱。
.png?generation=1768765737772201&alt=media)
我确实将我的模型预测与测试数据进行了比较,可视化后行为变得非常清晰。在图中,蓝色水平线代表实际对数收益率,而黄线代表我的模型预测。立即引人注目的是真实信号的波动性有多大——蓝线表现出频繁的尖峰,包括非常难以一致建模的极端运动。相比之下,黄色预测线的行为要保守得多。模型没有对每一个突然的尖峰做出激进反应,而是更多地关注捕捉运动的方向而不是其幅度。大多数时候,它与正确的方向信号保持一致,但故意避免娱乐极端变化或急剧的价格差异。这种受控行为是故意的:与其追逐通常嘈杂且不稳定的高波动模式,模型优先考虑稳定性和稳健性,有效地平滑过度波动,同时仍然响应有意义的市场信号。
%20(1).png?generation=1768766415503119&alt=media)
总体而言,该模型优先考虑方向正确性而不是幅度,抑制噪声,并随时间泛化,这与您的理念一致,并解释了为什么您的公共到私有排行榜改进发生了,即使实际序列仍然高度不稳定。
提交是使用 Kaggle 评估 API 进行的,该 API 在 Kaggle 的环境中运行笔记本。API 自动在测试集上调用 predict 函数,执行完整的训练、准备、推断,生成提交文件,并在排行榜上评估它。这确保预测是在没有任何前瞻性数据的情况下生成的,并且分数是一致且自动计算和评判的。
我的完整训练笔记本已附上。虽然评估代码之前已被删除,但我已将其重新集成到分叉的笔记本中以展示结果。
作为初学者参加这次比赛,我专注于基本原则和简单的特征工程。当我的第一次提交落在公共排行榜底部时,我质疑了我的整个方法,几乎因不确定性而放弃。因为公共排行榜感觉像一个“陷阱”,而且评估非常波动,我停止追逐短期分数,转而押注于模型稳定性和时间纪律的策略。昨天早上收到通知时,我习惯性地滑到排行榜底部——结果意识到我已经跳到了第 3 名。这是一段从底部到顶部的令人震惊的旅程,我花了一整天检查我的代码以确保结果是真实的。
该解决方案的特点是在战略上强调稳定性和稳健性,而不是激进的特征工程。通过将每个目标视为具有自己专用模型和受限特征集的独立问题,作者最大限度地减少了噪声,并避免了过度拟合不可靠的公共排行榜的“陷阱”。该方法利用了两级堆叠集成——结合 LightGBM、Random Forest 和 XGBoost——以学习不同数据模式的最佳权重,同时保持生产友好的架构。最终,模型故意优先考虑捕捉方向性趋势,而不是追逐极端、嘈杂的尖峰,这是一种方差抑制形式,确保了在高度波动的市场时期的可靠性能。
我要向 Kaggle 和赞助商 Mitsui & Co. 致以诚挚的谢意,感谢他们举办商品预测挑战赛。我特别感谢数据集描述和评估指南中提供的清晰度;拥有如此高质量的信息是一个“金矿”,帮助我 navigate 任务,而无需通常挣扎于理解要求。特别感谢 @sohier 关于提交和目标计算笔记本的清晰说明,这对我的工作流程至关重要。最后,祝贺所有参与者。这次比赛是一段自我怀疑和个人成长的旅程,但它通过测试和押注不同建模策略的过程提供了宝贵的教训。我想向我的“沉默导师”——许多直接或间接通过公共笔记本分享知识的 Kagglers——表达深深的感激之情。虽然我无法指出每一个具体的来源,但我的战略决策得到了经过验证的 Kaggle 模式的强力支持。我应用的许多原则——例如专注于模型稳定性和抵制公共排行榜的噪声——都是经验丰富的竞争对手多年来一直倡导的教训。我只是遵循了这些基本原则并将其应用于这一挑战。
谢谢和问候,
Ayush