第81章 榜单之外(1/2)
吃过晚饭回到房间,电脑屏幕因为长时间无操作已经进入了休眠。
江临晃了一下滑鼠,屏幕亮起,下载管理器里的进度条已经拉满。
那个標著minute_market_data_v3.zip ,大小足有3.4gb的数据集,安静地躺在他的本地硬碟里。
江临趁著解压的间隙看了看规则。
平台那个ui极其简陋的任务说明页面上,规则写得很长,密密麻麻的一大篇免责条款和计分细则。
数据禁止外传,允许使用公开算法,允许提交清洗后的异常標记文件、方法说明报告和辅助脚本等等。
江临把几段规则复製下来,粘贴到本地新建的记事本里,然后用滑鼠给其中三个词加了粗。
隱藏標籤,可復现性,泛化能力。
这三个词,比那八千块钱奖金重要得多。
它透露了主办方真正的痛点。
他们不要那种在特定数据集上跑分很高,换个市场环境就彻底拉胯的比赛特供模型。
他们要的是一条真正能落地的工业级清洗流水线。
江临却看得眉头微皱。
因为他熟悉的是坏数据,不是金融市场。
温度传感器零点漂移、相机暗场噪声、光学支架热胀冷缩、辐射计被宇宙线打出异常尖峰,这些东西他处理过太多次。
可是分钟级行情不是温度曲线,成交量不是传感器採样,停牌標记也不是设备掉线。
如果连繫统的基本工作方式都没弄清楚,就急著下手清洗数据,那不叫审计。
那叫把自己的无知写进脚本里。
江临把下载页面暂时放到一边,新建了一个文档。
標题只有四个字:量化补课。
目標:学习这些数据在什么制度和流程下產生。
他先从最基础的栏位开始查。
开盘价,成交量,復权,涨跌停……
每一个词他都只看最基础的定义,不往投资策略上发散。
遇到讲十倍收益,稳定套利,资金曲线完美的帖子,直接关掉。
遇到有人认真討论未来函数,倖存者偏差,样本外失效,滑点和衝击成本的长帖,才停下来读。
两个小时后,他列出了六个必须先確认的制度性问题,从集合竞价边界到测试集隔离方式,並在纸上画出一条很粗糙的数据链路。
最后评价为:量化数据不是市场本身,是市场经过多层设备,制度,软体和商业口径之后留下的影子。
这句话写完,他又下载了数据字典和平台提供的baseline(基准参考代码)。
数据字典是一个二十多页的pdf文件。
不得不说,提供方在脱敏上做得很绝。
那个所谓的標的代码,根本不是什么诸如600519这种能在软体里搜出来的股票代码,而是被全部转成了一长串毫无规律的md5哈希值。
时间戳里的交易日(date_id)也做了整体偏移。你没法知道这是2015年股灾时的数据,还是2020年抱团行情时的数据。
所有的价格和成交量也全都在后台做过了等比例缩放,绝对值已经失去现实含义,只保留了零值、缺失、相邻变化率以及同一口径下的相对关係。
这意味著,你不可能靠现实世界的金融常识或者去查雅虎財经的歷史记录,去反推哪一只股票在某年某月某日发生了什么异常。
这很对江临的胃口。
因为外部查资料的捷径被切断后,剩下的胜负就会回到数据內部的逻辑、制度边界和审计能力上。
江临打开windows命令行窗口,在工程目录下建好了五个分类文件夹。
解压数据集的压缩包。
进度条走完后,data_raw 文件夹里出现了四个庞然大物。
train_features.parquet,train_labels.csv,test_features.parquet,sample_submission.csv
他现在用的这台二手电脑,內存只有可怜的8g。
如果直接用pandas把这三点几个g的parquet格式数据全量读进內存,这台二手机器大概率会直接卡死。
於是,他打开编辑器,写了一个几十行的python短脚本。
只读表头元数据和前面的前一百万行样本切片,用来探路。
十分钟后,按下运行键。
终端里瞬间刷出一长串刺眼的红字报错信息。
江临盯著那串红字,愣了两秒,然后哑然失笑。
现实世界就是这样充满黑色幽默。
你踌躇满志,以为自己马上就要面对深不可测的资本市场,要去手撕复杂的金融微观结构了,结果第一刀,却结结实实地砍在了一个缺失的底层依赖库上。
“行吧,先搞后勤。“
江临摇了摇头,在命令行里敲下了安装命令。
看著屏幕上跳动的一个个下载进度条,他顺手把这些包的版本號,一个不落地记录在了刚才的audit_log里。
晚上九点四十六分,环境配置完毕。
第一批百万行的切片数据,终於顺利地被读进了內存。
屏幕上整齐地列印出了十几个常规行情栏位,最后跟著三个由平台方自己生成的辅助栏位。
session_id,source_flag,row_weight。
江临没有急著去看那些价格数字,而是盯著这最后三个栏位看几眼,评价为。
【凡是平台方称作內部质量分组的栏位,先不要当特徵使用,先当潜在污染分层变量审计。】
接下来,他开始对这一百万行切片做最基础的侦查审计。
江临把脑子里的几个基础常识翻译成几行乾净利落的聚合代码,点击运行。
笔记本电脑发出低沉的轰鸣,cpu风扇开始高速转动,像是在抗议这种压榨。
第一轮审计脚本跑了足足二十多分钟。
终端里,一行行统计结果慢慢跳了出来。
和江临预想的一样,大部分记录看起来非常平滑正常,但也有一小撮记录脏得让人没眼看。
毕竟这个比赛的任务就是找脏数据,有异常是在预料之內的。
江临平静地写了几行画图脚本,把那些被筛出来的异常样本隨机抽了几个,画成走势图。
这些异常都太浅了。
写几个if-else的条件过滤就能抓得一清二楚。
江临心里盘算著,如果只靠这些表层的业务逻辑去提交,应该能稳定拿到一个中等偏上的分数,拿不到奖金,但至少能及格。
如果任务只停留在这些表层逻辑错误上,就不值得平台设置人工报告评分。
江临揉了揉有些发酸的眼睛,喝了一口水,继续往下看第四个问题的统计结果。
凌晨一点二十,当缺失值的分布矩阵渲染在屏幕上时,他终於发现了第一处让他皱起眉头的异常现象。
数据里的缺失值,不是均匀出现的。
如果是一般的低流动性標的,出现偶尔的数据缺失是很正常的。
又或者,是某一只股票的交易所接口突然卡顿,造成几分钟的断档,这也说得通。
但现在屏幕上显示的问题是,缺失极其密集地集中在若干个date_id的同一段minute_id上。
而且,更诡异的是,这些minute_id的缺失,横跨样本里成百上千个不同的symbol_id。
为了看清楚,江临把这百万行数据的缺失分布状態画成了一张二维热力图。
横轴是时间,纵轴是標的代码,有数据的地方是深蓝色,缺失的地方是刺眼的亮黄色。
屏幕上赫然出现了几条横贯全图的亮黄色条带,就像一整块深蓝色的布料上,被人用刀残忍地划出了几道平行的巨大伤口。
这不是某一只股票的交易出了问题,不是標的自身的市场行为!
这更像是在那个特定的时间段,负责收集市场数据的某一台核心伺服器突然宕机了,或者是底层供应商的某根专线断了,导致整个市场在这一段切片上的数据,经歷了全链路的集体丟包。
江临立刻切回audit_log,在日誌里重重地敲下一行字。
【异常类型 a:横向时间截面级缺失。】
【推断:此异常不属於市场本身的交易特徵,极大概率是数据供应商底层接口故障,或平台拼接多源数据时发生的时间轴对齐失误。】
他顺著这个思路往下查,试图把所有出现过横向断层的时间段全部圈出来。
接著,他犯了一个非常小但足以致命的常识性错误。
他在热力图上框选时,发现不仅是某些隨机的日子有横向伤口,几乎每一个date_id的中间段,都有一大块规则的、横贯全市场的空白区域。
江临毫不犹豫地把这段规则的空白也標记成了异常缺失。
但在按下保存键的那一瞬间,他的手指悬停在了半空。
“等等,所有標的,每一天,在中间完全相同的位置,准时切断,又准时恢復?“
江临忽然意识到什么,快速切出代码界面,打开瀏览器,在搜寻引擎里输入了a股交易时间。
页面立刻跳出结果:上午 9:30 - 11:30,下午 13:00 - 15:00。
中午有一个半小时的休市。
江临拍了一下自己的脑门。
自己把这部分规则的空白当成了数据丟失。
他打开数据字典,重新审视那个极其含糊的session_id。
平台虽然对具体的时间做了脱敏和偏移,但session_id依然诚实地保留了每天上、下午两个交易时段的结构信息。
江临立刻把那部分標记全部撤掉,回到日誌本,坦然地记录下自己的失误:
【误判 001:將正常的午间休市造成的物理时间断点,误识別为系统级数据缺失。】
【原因分析:过於迷信数据表象,未在建模前建立现实市场的物理约束前提。】
【修正方案:后续所有统计和清洗动作,必须先按session_id將每日行情严格分段隔离建模,禁止跨越休市期计算任何差分特徵。】
写完这一段,他感到一阵轻鬆。
这就是纠错的快感。
承认无知,排除干扰,把系统的边界再一次收紧。
时间已经推移到了凌晨两点半。
江临搓了搓脸颊,驱散了一丝困意,打开那个官方提供的baseline.ipynb文件。
这是一个用jupyter notebook写的演示级基准代码。
平台方写得很简陋,主要是演示如何读入数据、构造特徵、跑通提交流程。
后面的模型部分他暂时没有执行,只顺著预处理代码往下看。
江临一行一行地往下扫,目光快速解析著这些代码背后的逻辑。
看到第六十七行的时候,他握著滑鼠的手突然停住了。
那是一段做特徵標准化的代码。
为了让不同量纲的数据能在一个尺度下训练,它把训练集和测试集直接合併成了一个大表。
然后,它计算了这个大表里每个特徵的全局均值和全局標准差。
最后,它用这个包含了所有未来数据的全局统计量,去对整个数据集进行了放缩。
在很多网上的开源教程或者新手的数据分析作业里,这种写法非常常见。
用一句fit_transform全部搞定,方便,省事,代码看起来也很整洁。
但如果在普通的不强调时间顺序的机器学习比赛,比如识別猫狗图片里,这叫数据泄漏。
本章未完,点击下一页继续阅读。