news 2026/10/17 3:04:55

金融机器学习实践指南:从分数阶差分到净化K折的Notebook全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
金融机器学习实践指南:从分数阶差分到净化K折的Notebook全解析

简介:面向阅读《金融机器学习进展》一书的量化投资学习者,这套资源精选书中若干练习给出了实验性解决方案。内容以Jupyter Notebook为主要载体,配合Python脚本,覆盖金融样本标注与元标注、交叉验证在金融场景中的特殊处理、逐笔/成交量/美元成交额K线构造、样本权重计算以及分数差分特征等核心章节,每个练习都配有可运行代码和逻辑注释,便于对照原著理解公式与代码的映射关系。压缩包共43个文件,其中6个ipynb笔记可直接打开,18个py脚本提供底层函数和辅助工具,另有文档、配置、测试等文件,整体大小2.8MB,轻量易用。项目采用源码、笔记、文档分目录组织,附带环境部署所需的安装与构建配置,读者可基于这些文件快速还原实验环境,并进一步扩展自己的金融机器学习实验。已有335人浏览学习,适合具备Python与金融基础的中级读者作为进阶研读与动手实践的参考。

1. 给读《金融机器学习进展》读到怀疑人生的人:这套 Notebook 把练习变成了可跑的代码

金融机器学习有个反直觉现象:模型越复杂,回测越漂亮,实盘越容易翻车。问题多半不在模型,而在特征和样本切分——价格序列按普通差分处理完可能失真,K 折交叉验证又会让重叠样本泄漏进训练集。某开发者当初在这上面连续踩坑,后来把《金融机器学习进展》书里的练习逐个实现成 Jupyter Notebook,就有了这套 Adv_Fin_ML_Exercises 实验性解决方案。它覆盖分数阶差分、三重障碍标注、元标注、净化 K 折、样本权重、特征重要性等核心章节,每个练习都能直接跑出中间结果和图表。适合正在读这本书、卡在理论到代码这一步的人,也适合想把书中方法移植到自有策略上的量化开发。

2. 项目骨架:按章节拆开的 Notebook 与轻型依赖

2.1 目录结构:一本书对应一批独立 Notebook

打开项目根目录,第一印象是干净:没有 src 包,没有 setup.py,也没有复杂的工程分层。整个项目就是一组按章节命名的 .ipynb 文件,外加一个 data 目录和 requirements.txt。典型的章节文件包括下面这些:

Adv_Fin_ML_Exercises/ ├── Chapter02_Fractional_Differentiation.ipynb ├── Chapter03_Labeling.ipynb ├── Chapter04_Sample_Weights.ipynb ├── Chapter05_Meta_Labeling.ipynb ├── Chapter06_Ensemble_Methods.ipynb ├── Chapter07_Cross_Validation.ipynb ├── Chapter08_Feature_Importance.ipynb ├── Chapter10_Betting_Against_Beta.ipynb ├── data/ │ ├── dollar_bars.csv │ └── euro_dollar.csv └── requirements.txt

注意,这个清单只是这类项目最常见的组织方式,不同作者的章节拆分会有出入——有人把元标注并入第 3 章,有人把回测和第 7 章交叉验证合并成一个文件。判断是否同一类项目,看两点:是不是每个章节一个 Notebook,是不是用 data 目录统一放样本数据。这套项目的关键设计就是章节间没有互相 import,每个文件都能独立打开、独立运行。这对学习很重要,你不需要先跑通前面的练习才能看后面的。

这种按章节切分的组织方式有一个直接的工程好处:依赖倒置。每个 Notebook 都是自包含的,函数定义在文件内,数据从 data 目录读,不依赖项目里其他文件。这意味着你可以把 Chapter03_Labeling.ipynb 单独摘出来,复制到自己的项目里改改就用,而不需要把这套仓库整个搬走。我做二次开发时最喜欢这种结构,因为摘抄单章的成本几乎为零。

每个 Notebook 内部也遵循一样的节奏:开头是 Markdown 说明,把书里的数学公式和符号过一遍;中间是若干代码单元,把算法拆成小函数;最后是带输出的验证单元,用一小段样本数据跑出结果和图表。这种"说明 + 函数 + 验证"三段式正是实验性解决方案该有的样子。它不是要给你一个生产级框架,而是要在每一处代码旁边留下为什么这么写的痕迹。

2.2 技术选型:pandas 打底,不引入重型框架

项目没有引入任何专门的金融机器学习库,也没有封装好的特征工程框架,依赖清单很短,核心就这几样:

依赖用途
numpy / pandas序列计算、滚动窗口、DataFrame 操作
scikit-learn分类器、交叉验证、特征重要性评估的底座
matplotlib画出差分前后的价格曲线、标注结果
statsmodels平稳性检验(ADF 检验)

这个选型意图很明显:书里这些算法的价值恰恰在"自己实现"的过程中。分数阶差分用专门的 fracdiff 库一行就能调出来,但那样你就看不到权重递推、阈值截断这些关键细节了。用 pandas 的 rolling 手动实现,虽然慢,但每个中间变量都能拿出来看——这在学习阶段远比性能重要。

我一般会把 Python 版本锁在 3.8 到 3.10 之间,pandas 版本 1.x。原因是书里的代码大量依赖 rolling().apply() 的匿名函数行为和 DataFrame 的按时间索引切片,这两个行为在 pandas 2.0 之后有过调整,版本太新反而容易踩兼容性坑。requirements.txt 的写法也值得注意,它没有用 >= 这种宽松版本范围,而是把主要依赖的版本号限制在 1.x。对学习型项目来说这是对的——你希望读者跑出来的结果和作者笔记里的截图一致,而不是因为依赖升级引入了细微差异。

2.3 Notebook 的组织方式:函数、验证、图表三层分离

细看单个 Notebook 的代码单元,会发现作者刻意把"算法实现"和"结果展示"分开。算法函数集中在前面几个单元,分数阶差分就一个 frac_diff 函数,三重障碍标注就一个 apply_triple_barrier 函数;后面才是加载数据、调用函数、打印输出、画图的演示单元。这种分层的好处是,你想复用某个算法时,不用在演示代码里翻找逻辑;想验证某个函数时,单独把那个单元重新运行一遍即可,前后的数据依赖几乎为零。

另一个值得注意的细节是,Notebook 的 Markdown 单元里保留了书中的数学表达和符号定义。比如分数阶差分章节会先解释权重递推公式 w_k = -w_{k-1} * (d - k + 1) / k,再给代码;三重障碍章节会先解释垂直屏障、利润目标、止损三条线各自的触发条件。这对读者是很大的帮助——很多开源实现直接甩一堆代码,符号和代码对不上,你根本不知道他实现的到底是书里的哪个版本。

一个常见的误解是以为这套 Notebook 只是把书里的公式翻译成代码。其实每个练习的末尾都有"验证"性质的输出,比如差分数列的 ADF p 值、三重障碍的标签分布柱状图、MDA 的特征重要性条形图。这些输出才是这套方案的灵魂——它们告诉你算法在你的数据上行为是否正常,而不是仅仅"跑通了"。

3. 核心算法拆解:特征、标签、交叉验证三条主线

这一章是整套 Notebook 里含金量最高的部分。书里十几个练习,归纳起来其实就是三条主线:怎么做特征(分数阶差分)、怎么做标签(三重障碍和元标注)、怎么做验证(净化 K 折和特征重要性)。下面把项目里最有代表性的实现逐个拆开讲。

3.1 分数阶差分:从整数阶到任意阶

普通的价格差分是一阶差分,log 收益率是它的近似;但一阶差分会把记忆全部抹掉,丢掉长期动量信息。书里的做法是把差分阶数从整数推广到小数,保留尽可能多的记忆,同时让序列通过平稳性检验。项目里的核心实现长这样:

import numpy as np import pandas as pd def frac_diff_weights(d, num_k, thresh=1e-5): """ 递推计算分数阶差分的权重序列。 d: 差分阶数,小数,比如 0.5 表示半阶差分 num_k: 最大窗口长度 thresh: 权重截断阈值,权重小于它就不再往下递推 """ w = [1.0] for k in range(1, num_k): wk = -w[-1] * (d - k + 1) / k if abs(wk) < thresh: break w.append(wk) return np.array(w) def frac_diff(series, d, thresh=1e-5): """ 对序列做分数阶差分,返回去掉 NaN 的 DataFrame。 series 必须是按时间升序的收盘价序列。 """ w = frac_diff_weights(d, len(series), thresh) width = len(w) # rolling.apply 里对每个窗口做加权求和,等价于卷积 out = series.rolling(window=width).apply( lambda x: np.dot(w, x), raw=True ) return out.dropna()

逻辑不复杂,但有两个地方值得细看。第一是权重递推:w_k 不是凭空算的,它是从上一个权重 w_{k-1} 递推过来的,系数是 (d - k + 1) / k。d 是 0.5 时,权重衰减得比一阶差分慢得多,所以序列保留的记忆更长;d 是 1 时,这套递推就退化成普通一阶差分。第二是 thresh 截断:权重理论上要算到无穷远,但实际衰减到 1e-5 以下就完全可以忽略,截断能省掉大量窗口计算。

参数上,d 是唯一需要调的核心参数,书里的建议是在 0 到 1 之间扫一遍,用 ADF 检验找满足平稳性又尽量高的 d 值;thresh 默认 1e-5 在大多数行情数据上都不用动;rolling 窗口等于权重序列长度,数据太短时后面全是 NaN,这也是高频出现的问题,后面避坑章节会展开。

3.2 三重障碍标注:把价格路径变成带终点的标签

很多人在标注这一章翻车,是因为他们把标签简单定义成"未来 N 根 K 线的涨跌"。而书里的三重障碍多了一个垂直屏障的退出机制:价格先碰到利润目标记 1,先碰到止损记 -1,期间什么都没碰到就等垂直屏障到期,按到期收益方向记 0 或 1。项目里的实现是逐事件遍历:

def apply_triple_barrier(close, events, pt, sl): """ 给每个事件窗口打三重障碍标签。 close: 收盘价序列,按时间升序 events: DataFrame,index 是事件发生时间,列 t1 是垂直屏障时间 pt: 利润目标倍数,比如 1.0 表示 +1 倍波动率目标 sl: 止损倍数,比如 1.0 表示 -1 倍波动率目标 返回: 每个事件一个 label,1 / -1 / 0 """ labels = pd.Series(index=events.index, dtype=int) for idx, row in events.iterrows(): # 截取从事件发生到垂直屏障的价格窗口 window = close.loc[idx: row['t1']] if len(window) < 2: labels[idx] = 0 continue # 相对事件发生时价格的逐点收益 base = close.loc[idx] rets = window / base - 1.0 # 分别找首次触碰上下障碍的时间 hit_pt = rets[rets >= pt].index hit_sl = rets[rets <= -sl].index if len(hit_pt) and (len(hit_sl) == 0 or hit_pt[0] < hit_sl[0]): labels[idx] = 1 # 先碰到利润目标 elif len(hit_sl) and (len(hit_pt) == 0 or hit_sl[0] < hit_pt[0]): labels[idx] = -1 # 先碰到止损 else: # 都没碰到,按垂直屏障到期收益方向定 labels[idx] = 1 if rets.iloc[-1] > 0 else 0 return labels

这段代码的价值在于把"路径事件"转成了"分类标签"。注意 rets 的索引是时间,比较 hit_pt[0] < hit_sl[0] 其实是在比两个时间戳谁更早,这比直接数 K 线根数更贴近事件驱动交易的真实逻辑——同一个窗口里,先触发哪个障碍就按哪个结果结算。pt 和 sl 的单位是波动率目标的多倍,项目里通常用滚动标准差序列作为 trgt,这样每个事件的障碍宽度是自适应的,不会在低波动期给过宽的障碍。

这里有个容易搞错的点:垂直屏障到期时如果收益恰好是 0,书里有些版本记 0,有些版本按"没碰到任何障碍"处理。项目里按 rets.iloc[-1] > 0 判断,等于 0 的情况落到了 0 标签,这在分类任务里是合理的,因为 0 表示"中性",但训练时要注意样本均衡,0 标签往往占比很高,需要配合第 4 章的样本权重练习一起看。

3.3 净化 K 折与禁运:堵住时间序列里的泄漏

这是整套项目里最值得抄的一段。普通 K 折对金融数据是致命的:相邻样本的标签窗口重叠,训练集里含有测试集未来信息,回测分数虚高。书里给出的方案是净化(purge)和禁运(embargo),项目里简化后的实现是:

from sklearn.model_selection import KFold class PurgedKFold(KFold): """ 带禁运的净化 K 折。 n_splits: 折数 embargo_pct: 测试集边界外额外剔除的训练样本比例 t1: 每个样本的标签结束时间,做精确净化用 """ def __init__(self, n_splits=5, embargo_pct=0.01, t1=None): super().__init__(n_splits=n_splits, shuffle=False) self.embargo_pct = embargo_pct self.t1 = t1 def split(self, X, y, groups=None): n = len(X) idx = np.arange(n) # 按位置顺序切 K 折,保证每折在时间上连续 bounds = np.linspace(0, n, self.n_splits + 1).astype(int) for f in range(self.n_splits): test_idx = idx[bounds[f]: bounds[f + 1]] train_idx = idx[~np.isin(idx, test_idx)] if self.t1 is not None: # 精确净化:训练集里标签结束时间落在测试集时间窗内的样本删掉 test_start = self.t1.index[bounds[f]] test_end = self.t1.iloc[bounds[f + 1] - 1] overlap = (self.t1 >= test_start) & (self.t1 <= test_end) train_idx = train_idx[~overlap.iloc[train_idx]] # 禁运:测试集结束后再让出一段缓冲 emb = int(n * self.embargo_pct) train_idx = train_idx[ (train_idx < bounds[f] - emb) | (train_idx > bounds[f + 1] + emb) ] yield train_idx, test_idx

这段代码的思路是双保险:先按时间顺序把样本切成连续 K 折,保证每一折内部是时间连续的一段,避免随机洗牌带来的未来信息;再用 t1 做精确净化,把训练集里标签窗口和测试集重叠的样本剔除;最后加 embargo,在边界外多让出一段缓冲,防止特征里残留的窗口影响。embargo_pct 通常给 0.01 到 0.05 就够,给得太大训练样本会骤减。

说实话,这个简化版和书里的完整实现还有差距。书里的净化是基于每个样本的 t1 时间戳精确判断重叠区间,而不是用位置索引近似;但在这个项目的定位下,用 t1 的时间信息做过滤已经是合格的教学级实现,拿到自己的数据上也能用。想上生产的话,建议在此基础上把 t1 的构建和 purging 逻辑单独抽成类,再包一层给 sklearn 的交叉验证接口。

3.4 元标注与特征重要性:让模型学会只下该下的注

元标注(meta-labeling)是书里一个非常实用的技巧:主模型先决定"要不要下注",副模型再决定"下了注能不能赚",副模型的输出概率直接作为仓位。项目里的实现很短:

def meta_labeling(primary_bets, features, clf): """ 元标注:在主模型的下注样本上训练副模型。 primary_bets: DataFrame,index 是下注时间,含列 ret(实际收益) features: 特征矩阵,和 primary_bets 时间对齐 clf: 任意 sklearn 分类器 返回: 副模型输出的正类概率,作为仓位大小 """ # 只取主模型实际下注的样本 X = features.loc[primary_bets.index] y = (primary_bets['ret'] > 0).astype(int) # 副模型学习“这单能不能赚” clf.fit(X, y) prob = clf.predict_proba(X)[:, 1] return prob

这段代码把元标注的核心讲清楚了:副模型不预测涨跌方向(那是主模型的活),只预测主模型下注行为的胜率。所以标签 y 是 primary_bets 的实际收益正负,而不是价格方向。输出的 prob 可以直接当成仓位系数乘到主模型信号上,比如主模型信号是 +1,副模型概率是 0.7,下单量就是 0.7 倍标准仓位。

特征重要性章节则是把 sklearn 的 feature_importances_ 换成了书里的 MDA(平均下降精度)方法:对每一折,把某个特征列随机洗牌,看模型得分下降多少,下降越多说明该特征越重要。项目里用 purged K 折包了一层,所以比直接读树模型的 feature_importances_ 靠谱得多。这两个练习放在一起看,能直观感受到书里反复强调的观点:金融 ML 的每一步都要防泄漏,连特征重要性评估都不能用朴素 K 折。

4. 跑起来:环境准备到第一个练习出图

4.1 环境准备:用 conda 锁一个干净 Python 版本

这套项目是纯 Python + Jupyter 环境,没有 GPU 依赖,也没有需要编译的扩展包,所以环境准备比想象中简单。习惯用 conda 新建独立环境,避免把系统 Python 搞乱:

conda create -n afml python=3.9 conda activate afml

选 3.9 的原因前面说过:书的核心代码大量使用 pandas 的 rolling().apply() 和按时间索引切片,pandas 1.5.x 和 Python 3.9 是兼容性最稳的组合。用 3.10 或 3.11 也不是不行,但后面如果发现 rolling 的结果和预期对不上,第一件事就去查 pandas 版本而不是查自己的代码。用 venv 也可以,但 conda 在 macOS 和 Linux 上对 pandas 这类带 C 扩展的包处理更省心,遇到编译错误可以直接用 conda install 换二进制源。

装依赖直接走仓库里的 requirements.txt:

pip install -r requirements.txt

如果仓库没给 requirements.txt,按项目里实际 import 的内容手动装也很快,核心就是 pandas、numpy、scikit-learn、matplotlib、statsmodels、jupyter。装完快速验证一下:

python -c "import pandas, sklearn, statsmodels; print(pandas.__version__, sklearn.__version__)"

能正常打印版本号,环境就算通了。这一步别跳过,后面很多诡异问题都源于依赖版本跑偏。

4.2 启动 Jupyter 并跑通第一个练习

环境就绪后在项目根目录启动:

jupyter notebook

或者用新一代的 JupyterLab:

jupyter lab

建议从 Chapter02_Fractional_Differentiation.ipynb 开始跑,因为它是整条技术链路的起点——没有经过分数阶差分处理的序列,后面三重障碍和特征重要性的样本都不完整。打开 Notebook 后按顺序从上往下执行单元格,前几个单元会打印权重序列和 ADF 检验结果,中间画出原始价格和差分后价格的对比图,最后一个单元展示差分后的 DataFrame 末尾几十行。

第一次跑通的标准不是"没有报错",而是能看到三条信息:权重序列的长度和尾部的衰减值、ADF 检验的 p 值、差分后序列的起始时间。执行到权重单元时,尾部的值应该在 1e-5 量级,说明权重截断生效了;如果看到 p 值接近 1,大概率是 d 值取得不对,回头把 d 调小再跑。这三样都符合预期,说明你的环境、数据、代码三者是一致的。

提示:Jupyter 打开后如果发现 Notebook 的内核不是 afml 环境,在 Kernel 菜单里手动切换一下内核,否则 import 会落到系统 Python 上,装好的依赖全部失效。

4.3 数据从哪里来:内置样本与自备行情

项目自带的 data 目录里一般会有几份小样本,比如欧美汇率或指数的日线数据,体积都很小,加载用的是 pandas 的 read_csv 或 read_pickle。内置样本的作用是让每个练习立刻能跑,不需要你费劲去找数据。想换成自己的数据也很直接:

import pandas as pd df = pd.read_csv("data/your_data.csv", index_col=0, parse_dates=True) close = df["close"].sort_index().dropna() print(close.head())

只要索引是时间戳、有一列 close 就行。需要注意三点:时间索引必须升序且唯一;价格必须是复权后或价格口径明确的数据,分数阶差分对价格中的跳空很敏感;样本长度别太短,差分窗口会吃掉头部数据,太短的话后面可用的样本所剩无几。一般来说至少给 2000 根 K 线,练习效果才看得清。

提示:如果换用分钟级或 tick 级数据,建议先把时间索引统一成 UTC,避免跨时区行情在按时间切片时出现错位。

5. 避坑记录:5 个我在复现时踩过的具体问题

5.1 分数阶差分输出整片 NaN,有效行只剩一半

现象:调用 frac_diff 之后,返回的 DataFrame 前面一大半全是 NaN,能用于训练的样本只剩原来的 40% 不到。

原因:rolling 窗口长度等于完整权重序列长度,窗口越长,头部被吃掉的样本越多。如果权重阈值 thresh 设得太小,比如 1e-10,权重序列会拖得很长,窗口就特别大。另外数据本身太短时,这个问题会雪上加霜。

解决:把 thresh 调到 1e-5 到 1e-4 之间,权重提前截断,窗口变短;数据长度给够 2000 根以上。也可以用书里的固定宽度方法,预先设定窗口长度,不依赖权重自然衰减,行为更可控。

5.2 同一段代码在两个环境跑出不同结果

现象:Notebook 里注释说输出应该是一张平滑的差分曲线,但自己机器上跑出来曲线明显更毛糙,数值也对不上。

原因:pandas 版本差异。pandas 2.0 之后 rolling.apply 的 raw 参数行为和窗口边界的处理有调整,老代码在新版本下容易出现细微偏差;另外新版本 sklearn 对部分分类器的默认参数也有改动。

解决:用 conda 锁版本,pandas 固定在 1.5.x,sklearn 固定在 1.2.x 左右。复现这类算法笔记,版本锁死是第一原则。条件允许的话,直接在 requirements.txt 里写上精确版本号而不是范围。

5.3 三重障碍标注全是 0,几乎没有 1 和 -1

现象:跑完 apply_triple_barrier,统计 label 分布发现九成以上都是 0,模型完全没法学。

原因:pt 和 sl 倍数设得太大,比如 pt=2、sl=2,而波动率目标 trgt 用的是滚动标准差,窗口内价格很少能跑到 2 倍标准差,大部分事件都熬到了垂直屏障到期。另一个常见原因是 t1 设得太短,价格还没来得及触碰障碍就到垂直屏障了。

解决:把 pt 和 sl 降到 0.5 到 1.0 之间,或用分位数代替标准差做波动率目标,保证每个事件窗口内触碰概率适中。调整之后重新统计 label 分布,1 和 -1 合计占比在 30% 以上才算健康。

5.4 PurgedKFold 和 GridSearchCV 一起用报维度错误

现象:把 PurgedKFold 传给 GridSearchCV 的 cv 参数,运行时报错说 split 返回的索引维度对不上,或者直接报 groups 相关错误。

原因:PurgedKFold 重写了 split 的逻辑,而 sklearn 的 CV 类在内部调用时假设 split 只接收 X、y、groups 三个参数,且返回的索引形状固定。一旦净化逻辑里用了 t1 做布尔过滤,返回的 train_idx 长度不固定,某些 sklearn 版本的校验逻辑就会直接抛异常。

解决:不要直接把 PurgedKFold 塞给 GridSearchCV。常见做法是先用普通 K 折固定超参数网格,选出候选参数,再用 PurgedKFold 做最终评估;或者在外层手动写循环,一个参数一组地跑 purged 验证。这个限制在新版 sklearn 里有所缓解,但别指望完全兼容。

5.5 MDA 特征重要性跑一次要十几分钟

现象:特征列一多,MDA 循环要跑几十上百次模型训练和洗牌预测,单次实验等得让人崩溃。

原因:MDA 本质上是"每个特征、每个折"都要重训一次模型,计算量是 O(特征数 × 折数 × 模型训练成本)。项目里的示例特征很少所以看不出来,一换到真实策略的几十个特征就露馅。

解决:先降维再评估,用树模型的天然重要性筛掉一半特征后再跑 MDA;或者把每折的模型只 fit 一次,洗牌时复用同一棵树的预测结构,只在预测阶段做洗牌。这个优化能把时间从十几分钟压到两三分钟,而且不影响重要性排序。

注意:上面这些坑多数不是代码 bug,而是环境和理解问题。排查顺序建议固定为版本、数据、参数,先确认依赖一致,再确认数据口径,最后才是调参,盲目改参数只会越改越乱。

6. 改造思路:把这套练习移植到你自己的交易数据上

这套项目最大的价值不是里面的代码本身,而是它把书里的方法论拆成了一个个可以替换零件的模块。拿它做二次开发,我一般按三步走。

第一步是换数据。把 data 目录下的样本行情换成你自己的品种,比如股指期货的 5 分钟线,或者某个币种的日线。关键在预处理:先把时间索引归一化,确保升序且无重复;再决定是否要处理复权,因为分数阶差分对价格中的跳空很敏感,前复权数据和不复权数据跑出来的 d 值会差不少。换完数据先跑 Chapter02,看一眼 ADF 检验的结果,确认你选定的 d 值在新数据上依然成立,而不是照抄示例里的参数。

第二步是换标注。三重障碍里的 pt、sl、t1 三个参数必须跟着你的策略逻辑走:如果你做的是日内短线,t1 可以缩短到几十根 K 线,pt 和 sl 用滚动波动率的分位数而不是固定倍数;如果你做的是日线趋势,t1 就放到几天甚至几周。改完标注不要急着训练,先画出标注分布图,确认 0、1、-1 三类样本的比例在你接受的范围。这一步我每次都会翻车一次,所以现在习惯是先统计、再训练。

第三步是换验证。PurgedKFold 里的 embargo_pct 和数据的时间粒度强相关,日线数据给 0.01 就够,分钟数据可能要给到 0.05 以上。还有一种常见改造是把样本唯一性权重加进去,也就是用标签重叠度给每个样本加权,让重叠多的样本在训练时权重更低——这部分在 Chapter04 里有现成的思路可以抄,核心就是算每个样本的并发事件数,然后取倒数作为权重。

从那次以后,我每次拿到类似的学习型 Notebook 项目,都强制自己先跑一遍内置样本,再换数据,再改参数,最后改结构,绝不跳过中间任何一步。这套流程能帮你避开大部分"代码没问题但结果不对"的玄学时刻。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 3:04:48

GCC编译全流程:从预处理到链接的实用排错指南

我最早接触gcc编译时&#xff0c;总觉得它是个黑盒子&#xff1a;在IDE里点一下编译按钮&#xff0c;程序就能跑。直到有一天&#xff0c;某同事改了一个头文件&#xff0c;Makefile却没把它纳入依赖&#xff0c;重新编译出来的还是旧行为。那次排查让我开始正视一个事实——gc…

作者头像 李华
网站建设 2026/10/17 3:04:23

Fiddler抓包环境搭建与HTTPS解密配置完整指南

简介&#xff1a;Fiddler是一款由Telerik开发的HTTP协议抓包与调试代理工具&#xff0c;广泛用于开发者、测试人员排查接口调用异常、网页加载缓慢及网络流量分析等场景。本资源提供Fiddler的完整下载包&#xff0c;适合需要快速获取工具、搭建本地调试环境的中初级技术人员使用…

作者头像 李华
网站建设 2026/10/17 3:03:57

世界国家GeoJSON大全:Python校验与ECharts地图可视化实战

简介&#xff1a;面向地理信息系统开发者和地图可视化场景的世界国家地理边界数据包&#xff0c;整合了来自自然地球的全球国家与地区边界多边形&#xff0c;适用于网页地图、桌面地理信息系统、数据分析和可视化大屏等应用。资源以JSON格式文本为主体&#xff0c;共含三千四百…

作者头像 李华
网站建设 2026/10/17 3:03:35

基于YOLOv8与Django的道路交通事故实时检测系统实践

最近后台一直有人问道路交通事故检测相关的项目&#xff0c;正好我手头这套基于YOLOv8的检测系统刚整理完技术文档&#xff0c;趁热把整个开发思路、落地细节和踩坑记录捋一遍。这套系统我实现下来&#xff0c;核心能力就三条&#xff1a;图像检测、视频文件检测、摄像头实时画…

作者头像 李华
网站建设 2026/10/17 3:03:17

基于哈里斯鹰算法的图像模糊集增强与Matlab参数优化实践

1. 图像增强为什么要优化&#xff1f;——先搞清楚模糊集在干嘛1.1 模糊集增强的基本思路做图像增强的时候&#xff0c;很多人第一反应就是直方图均衡化、拉普拉斯锐化、同态滤波这些经典方法。但遇到低对比度、细节模糊、光照不均匀的图&#xff0c;这些方法很容易翻车&#x…

作者头像 李华
网站建设 2026/10/17 3:03:00

SeaTunnel集成Hudi连接器:数据湖同步配置与性能调优实战

1. 为什么SeaTunnel搭配Hudi会成为数据湖同步的破局点我自己第一次在SeaTunnel里折腾Hudi数据写入时&#xff0c;是把整个流程拆成了三段&#xff1a;上游从业务库采集&#xff0c;中间用SeaTunnel做分发&#xff0c;最后再调一堆定制脚本落到Hudi表里。那个阶段最大的痛点是脚…

作者头像 李华