ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

数据理解决定特征工程上限:从骑士行为预估看数据竞赛关键第一步

数据理解决定特征工程上限:从骑士行为预估看数据竞赛关键第一步 实话说大部分第一次做数据竞赛的人拿到的不是一份整理清楚的数据集而是一堆字段名杂乱、缺失值遍地、分布还严重不均衡的表。这时候大家的第一反应都是赶紧跑个baseline压压惊我也不例外。但在新冠期间饿了么骑士行为预估这类比赛里我吃过的亏告诉我——**数据理解花的时间直接决定后面特征工程的上限。**这篇博文就围绕数据理解这个阶段展开讲讲我是怎么把比赛题目里的业务背景翻译成数据分析上的判断怎么看字段、找洞察、躲坑以及怎么平滑地衔接到特征工程。1. 拿到题目先别急着建模把业务背景吃透1.1 为什么新冠期间这四个字决定了数据理解的方向很多人拿到比赛数据第一反应是看字段、看缺失值、跑统计但我认为最该先做的是把题目里新冠期间这四个字转化成数据分析上的具体含义。2020年初开始的那段时间整个即时配送行业发生了结构性的变化它不是普通的季节性波动而是需求端和供给端同时被改写。需求端用户的外出频率显著下降线上订单占比快速抬升下单时段、单笔金额、购买品类都和平时不一样。供给端部分骑士阶段性无法出勤运力出现大起大落单日订单量的走势变得很脆一个节点变了整个曲线就断了。这两个变化叠加在一起导致数据出现三个显著特征时间序列上有多个结构性断点、不同地区的曲线形状差异极大、同一个骑士在不同阶段的行为模式可能完全不同。数据理解阶段如果不先把这种业务地震的背景吃透后面所有的统计分析都像是在用一个平稳世界的假设去解释一个非平稳世界结论自然容易走偏。举个例子正常业务里上周同时段订单量是一个很稳定的基准特征但在这种场景下上周的数据可能来自完全不同的供需环境直接拿来做基准反而会引入噪声。1.2 行为预估到底在预估什么先界定样本和标签题目里写的是骑士行为预估但行为这个词在不同比赛里指的东西完全不一样。数据理解阶段要做的第一件事就是把题目文档里每个描述性词汇翻译成可计算的定义。具体来说需要回答这几个问题预测目标是类别还是数值是骑士会不会接单还是这个订单可能多久送达样本的粒度是什么——一条记录对应一个订单还是对应骑士一天的行为汇总预测的时点是什么——是在骑士看到订单之前预判还是在订单完成后复盘这些问题如果不先钉死后面训练集和验证集的划分、特征的时间对齐都会出错。我见过很多团队模型分数不低但提交之后效果差很远回头查原因十有八九是样本口径从一开始就没想清楚。所以数据理解阶段的产出物第一项就应该是样本与标签说明书把上述问题逐条写清楚。1.3 数据理解阶段的产出物不是报告是判断数据理解很容易做成一件看起来很努力、但产出很虚的事情输出一堆分布图、一堆相关系数矩阵然后不知道下一步干什么。我的标准是数据理解阶段必须产生三个可复用的东西第一样本和标签的定义说明书第二字段字典——每个字段的含义、类型、缺失率、取值分布和可用时间第三一组可复用的EDA脚本后面每次数据更新或特征迭代都能直接跑。这三个产出不是给别人交差的文档而是给后面的自己做导航。尤其是字段字典我在特征工程阶段几乎每周要翻几十次查某个字段的粒度、查某个特征会不会泄漏、查缺失值集中出现在哪个时间段全靠这一份字典。写到这熟悉数据竞赛节奏的人应该能感觉到数据理解阶段的核心价值在于降低整个项目后半程的决策成本。2. 先从字段地图开始骑士行为数据里到底藏了什么2.1 六类字段六种理解方式拿到原始数据之后我习惯先把所有字段按信息类别分个组。分类的意义在于每类字段在数据理解阶段要关注的重点完全不同混在一起处理会漏掉很多细节。字段类别典型字段数据理解阶段的核心关注点标识字段骑士ID、订单ID、设备ID是否唯一、重复模式时间字段下单时间、接单时间、完成时间粒度、覆盖范围、时区一致性空间字段经纬度、区域ID、商家与用户位置分布密度、异常点业务属性字段订单金额、物品类型、骑士等级缺失率、取值分布环境字段天气、温度、风力取值完整度、时间戳是否对齐标签字段是否完成、是否超时类别平衡、与业务定义是否一致举一个具体例子标识字段的检查能发现同一订单是否出现了多次。如果是那这张表很有可能是订单轨迹点表而不是订单表后面计算配送时长时就需要先做聚合。这类基础认知如果不建立直接拿订单维度去统计结果基本都是错的。2.2 用唯一性组合反推数据粒度判断数据粒度最靠谱的办法不是看文档而是自己动手做唯一性组合检查。操作很简单用pandas的nunique或者SQL里的count distinct分别统计关键字段组合下的唯一值数量。我建议按骑士ID日期、“骑士ID订单ID”、“订单ID时间戳”、“骑士ID小时”这几种组合依次排查。如果骑士ID订单ID组合下出现重复行说明一张订单可能被记录成多行如果骑士ID日期是唯一的说明这张表是骑士维度的日粒度快照。这一步通常只需要写几行脚本十几分钟就能跑完但因为大多数比赛数据集的字段名并不规范这一步带来的信息量往往非常大。我在做这类检查时习惯把每次结果用一张小表打印出来记录组合名称、总记录数、唯一组合数、是否有重复。跑完两三张表之后数据的组织方式基本就清楚了。很多人跳过这个步骤直接做统计后面做特征时才发现维度对不上返工成本远高于一开始就花十几分钟做检查。2.3 时间字段的三个隐性坑时间字段是骑士行为数据里最容易被忽略、又最能影响模型质量的部分。我整理了三个几乎每次都会出现的坑。第一个是时间精度不一致。有的订单记录到分钟有的只记录到日期合并之后很难统一计算配送时长。第二个是时间语义不统一。同一张表里下单时间和接单时间可能来自不同系统一个记录的是骑士端点击时间另一个是服务端入库时间两者相差几十秒到几分钟如果直接拿来做时长计算噪声会很大。第三个是时区隐患。跨区域业务如果有一列时间忘记统一到北京时间小时级别的分布就会整体偏移等画24小时活跃度曲线的时候才会发现曲线形状不对但此时往往已经很难追溯是哪部分数据出了问题。我的习惯是在数据理解阶段专门写一个时间字段检查脚本把每个时间字段的min、max、缺失值比例、是否含时间部分、粒度是否一致全部输出成一张清单。跑完一轮之后哪些时间字段能用、哪些需要清洗心里就有底了。3. 我从数据里看到的四个关键洞察3.1 骑士活跃度不是一个平稳曲线而是三段式结构如果把骑士每日活跃数量和日订单量拉成时间序列会看到非常明显的三个阶段。疫情初期活跃骑士数量和订单量还维持在日常水平附近进入高峰期后活跃骑士数明显下滑而单个骑士日均承担的单量却快速上升这说明运力端出现了明显的供需错配到了恢复期活跃骑士数回升单人日均单量回落。这三段式结构几乎在每个区域的数据里都能复现只是时间节点有差异。这个洞察直接决定了后面特征工程的一个核心思路不能把整个时间段当成同一个分布来建模必须给样本加上阶段信息。无论是直接做一个阶段标签特征还是按阶段分别训练模型这个信息都会带来很大的提升。数据理解阶段如果没画出这张曲线后面的模型就少了一个最关键的结构性信息。3.2 配送时长分布的整体漂移正常时期配送时长的分布大致是右偏的钟形均值稳定高峰期略微拉长。但在疫情影响下配送时长的整个分布会整体右移长尾明显变长而且这种变化不是均匀的——高峰期变得更慢平峰期变化相对小。这个洞察看起来简单实际影响很大。第一如果用全量历史均值做配送时长的基准特征在疫情阶段会系统性低估真正的时长第二配送时长分布的变化本身就是一个强信号可以衍生成当前环境是否异常的特征。做数据理解时我通常按周为单位画配送时长的分位数折线图分别看P50、P90、P95的走势观察这几个分位数的拐点比只看均值更能准确描述分布形态的改变。3.3 接单响应时间和取消率的联袂变化骑士行为预估里有个很容易被忽略的表征维度就是接单响应时间——从订单推送给骑士到骑士点击接单之间的间隔。正常时期这个指标相对稳定但在疫情高峰期响应时间会明显变长同时订单取消率上升。这两者的联袂变化反映的是骑士在接单决策上的保守化接得更慢、更挑或者接完发现路线不可行就取消。对模型来说这类行为特征恰恰是行为预估任务里最有预测力的部分——因为它反映的是骑士状态的实时信号比静态属性特征敏感得多。数据理解阶段我会专门比较不同阶段下响应时间和取消率的分布差异确认这些信号在目标定义下是输入还是目标避免后续特征错位。3.4 天气与时段的交互效应被疫情放大最后一个是天气与时段、疫情阶段的交互效应。正常时期下雨天和早晚高峰对配送时长的影响是叠加的但影响幅度有限。而疫情期间这种叠加效应会被明显放大高峰期遇上雨天配送时长的P90可以比正常时期翻倍。这种情况很难用单个特征表达清楚需要构造交叉特征。我的做法是在数据理解阶段先画一张透视表行是时段分箱列是天气类别再加一个是否疫情高分期作为第三层维度看配送时长在每种组合下的均值和中位数。透视结果通常能直接告诉我们哪些交互项值得进模型哪些其实只是随机波动。这个发现也是后面做特征工程时时段x天气x阶段交叉特征的最初来源。4. 数据理解阶段最容易踩的坑4.1 缺失值并非随机缺失直接填充等于抹掉关键时期比赛数据里的缺失值往往不是均匀分布在整段时间里的。最常见的模式是某个站点或区域在某个阶段停止了数据上报导致那个时间段、那个区域的记录大量缺失或者某段时间系统压力大部分字段没有写入。如果直接在全局做均值填充或删除缺失行等于把疫情阶段最特殊的分布变化全部抹平了。正确的做法是先看缺失值的时间分布和空间分布。我常用两幅图按天统计缺失率的热力图、按区域统计缺失率的条形图。如果缺失明显集中在某个时间段那这个时间段的样本应该特殊处理要么单独标记缺失来源要么把该区域该时间是否有数据作为一个特征。至少在数据理解阶段的结论里要把这种缺失模式写进字段字典防止后续特征工程无意识地污染样本。4.2 标签泄漏用到了未来信息而不自知行为预估类比赛里标签泄漏是非常隐蔽、又杀伤力极大的问题。常见的情况是预测目标是骑士是否会接某订单但特征工程时不小心加入了该订单是否在后续被完成、完成后的配送时长这类事后变量。这类特征在训练集上效果特别好验证集也正常但到了真正预测未来时全是空的分数直接崩盘。数据理解阶段就要建立可用时间的概念每看到一个字段都要问一句在预测时点那一刻这个值能不能拿到。不能拿到的不管在训练集里多有用都必须在特征列表里标红。我在字段字典里专门有可用时间和是否可能泄漏两列每次新增候选特征都要填。这个习惯帮我避免了至少三次线下分数虚高的假象。4.3 订单量统计口径混用下单时间还是完成时间订单量的时间序列用下单时间统计和用完成时间统计走势在平稳期几乎一样但在疫情这种剧烈波动期会出现明显错位。比如午高峰下单的订单因为配送变慢完成时间被拖到下午如果特征和标签一个用下单时间对齐、另一个用完成时间对齐衔接处就会出现系统性偏差。我的建议是在数据理解阶段就统一全局口径特征、样本、标签的时间对齐方式必须在同一份文档里写明并写进特征字典防止后面写着写着混掉。特别是做滑窗统计特征时得明确过去一小时订单量里的时间戳到底是下单时间还是完成时间这个不统一窗口特征就全乱了。4.4 对比基期选错后期评估全乱套做特征效果对比或模型评估时很多人随手选一个时间段当基期。在平稳业务里问题不大但在分段式变化强烈的数据里基期选错会让整个评估失真。比如拿疫情高峰期作为验证集窗口和拿恢复期作为验证集窗口同一个特征的增益可能一个为正、一个为负。数据理解阶段的产出里应该有一份时间窗口划分方案明确训练窗口、验证窗口、测试窗口各自覆盖哪个时间段并说清楚为什么这么切。通常的原则是训练集必须包含完整的阶段变化信息验证集要模拟模型真实部署时的未来场景最好使用时间靠后的数据。这个方案在后续所有迭代中应该保持相对稳定否则不同版本的实验结果之间完全不可比。4.5 把聚合特征直接落下忽略分布变化最后一个坑相对隐蔽。很多人做统计特征时直接算过去N天的平均配送时长但均值在非平稳分布下会被长尾严重拉偏。疫情阶段配送时长分布整体右移均值和中位数的差距变得很大只用一个均值会丢失大量信息。我后期做统计特征时会刻意加入分布类特征比如P50、P90、P95、标准差等。这个习惯就是在数据理解阶段看到配送时长分布变化之后形成的。可以说数据理解看得越细特征工程反而越不容易做极端——你会自然地知道哪些统计量稳健、哪些统计量会被长尾影响。5. 从数据理解到特征工程这一步怎么衔接5.1 先沉淀一份特征字典数据理解阶段产出的字段字典到了特征工程阶段会自然演化为特征字典。每一行记录一个候选特征特征名、特征含义、数据类型、取值范围、缺失率、可用时间、是否可能泄漏、在哪个样本粒度上计算。写这个字典的过程很枯燥但它是整个项目里性价比最高的文档之一。我平时做特征的时候会先查这个字典确认这个特征是不是已经有人做过了、这个特征的统计粒度是什么能避免大量重复劳动。很多时候团队里几个并行的人各做各的特征最后合并时才发现两个人做了同一件事原因就是没有共用一份字典。如果是一个人单打独斗字典也能帮你记起这个特征我当初为什么做了一半就停了。5.2 滑窗统计特征窗口大小由业务节奏决定行为预估任务里最常用的一类特征是滑窗统计特征过去1小时接单量、过去3小时配送时长均值、过去24小时取消率等。窗口大小的选择不能拍脑袋而是看业务变化的节奏。从数据理解部分我们已经知道疫情期间骑士行为在小时级别就有明显波动所以短窗口特征1小时、3小时往往比长窗口特征7天更有区分度。窗口太长特征值会被往中间值拖等于平滑掉了这段时期最有价值的波动信号。当然长窗口也不是完全没用它更多表达的是骑士的历史稳定水平适合拿来和短窗口特征做差值形成当前偏差类特征比如过去1小时接单量比近7天同时段均值高了多少。这类特征把异常程度直接量化模型很容易学到环境突变的影响。5.3 把业务规则量化为特征数据理解阶段得出的业务判断几乎都能量化为特征。举几个例子如果发现某区域订单量出现异常下降可以计算该骑士所在区域过去1小时订单量比过去7天同一时刻均值下降了百分之多少如果发现骑士接单响应时间拉长可以做骑士最近3次接单响应时间的均值相较于历史均值的偏移量。这类偏离度特征本质上就是把业务规则翻译成模型能理解的语言。而翻译的素材恰恰来自数据理解阶段那些看起来只是随便看看的分析。这也是为什么我一直强调数据理解不是走流程因为它本身就是特征工程的上游。数据理解时画过的每一张分布图、每一张透视表都能在特征设计时找到对应的衍生方向。5.4 用简单模型验证数据理解的假设数据理解阶段最后一步我会用一套最简单的特征快速跑一个LightGBM目标不是拿分而是验证前面的判断阶段标签特征到底贡献了多少增益、短窗口特征是否真的比长窗口强、哪些交叉特征值得保留。具体做法是固定相同的参数和训练集做特征消融对比带阶段标签 vs 不带阶段标签、带短窗口特征 vs 只带长窗口特征、带交互特征 vs 不带交互特征。每组的AUC或对数损失差异就是数据理解阶段这些假设的最好验证。如果验证结果和预期不一致通常说明某个环节的数据理解还有遗漏需要回到前面重新检查。这一步看着简单却是把数据理解与实际建模效果打通的关键节点很多队伍在这里才发现自己前期判断里有几个明显偏差。这个比赛最花时间的地方往往不在模型精调而在数据理解阶段的耐心。我个人的做法是这一阶段至少留出完整的两到三天不急着写模型而是把样本口径、字段字典、阶段划分、风险点全部钉死。后面每一轮特征迭代都会发现这部分时间花得物超所值。等回头再看你会发现数据理解做得好不好不是当时看出来的而是等你发现自己在特征工程阶段返工最少的时候才后知后觉地意识到它的价值。
返回列表