简介:在机场运营中,资源分配问题往往涉及多维度复杂约束,航班登机口分配便是典型场景。传统人工排班和规则引擎难以应对动态延误与突发情况,而机器学习能够从历史调度数据中学习隐含匹配规律,将调度员的经验转化为可量化的打分模型。通过构建航班-登机口候选样本、设计时间/空间/航班属性/动态压力四类特征,并结合LightGBM等梯度提升树模型输出候选口得分,再以贪心算法处理硬约束,可快速生成兼顾效率与可操作性的分配方案。该技术路线既避免了整数规划求解时间不可控的缺陷,又比纯规则方法更灵活,适用于航班波、延误扩散等复杂运营场景。本文完整复盘了从数据清洗、标签构造到约束处理、评估指标设计的全过程,为机场信息部门及运筹优化项目提供可参考的落地路径。 登机口分配这件事,在真正跑过航班运行的人眼里,从来都不是“哪个航班有空口就塞哪个”那么轻松。我最近刚完成一个小项目,就是用机器学习来做航班登机口分配,压缩包里除了完整的数据集,还有一份可以直接拿去交差的方案报告。这篇文章就把项目从拿到原始数据到输出报告的整个链路拆开讲一遍,包括数据集长什么样、标签怎么打、模型怎么选、约束怎么处理,以及我在过程中踩过的几个坑。适合正在做运营优化类机器学习项目的同学参考,也适合机场信息部门想了解数据建模怎么落地的人看看。
说实话,登机口分配这个题目比表面看起来要复杂得多。它本质上是把“航班”和“登机口”做匹配,但匹配过程里塞满了时间约束、物理约束、航站楼资源约束,还有乘客体验和人效的平衡。如果只靠人工排或者简单规则,遇到航班波、延误、天气备降这种突发情况基本就是救火状态。用机器学习来辅助,核心不是为了替代调度员,而是把历史调度数据里的经验“学”出来,给决策者一个更合理的候选排序。
1. 项目概述与问题拆解
1.1 登机口分配到底难在哪
我记得第一次接触这个需求的时候,第一反应是“这不就是个带约束的匹配问题吗”,后来真的把航班数据铺开看,才发现自己太天真了。登机口分配难在几个维度同时拉满:时间维度上,同一个登机口前后两个航班之间必须留足过站时间,否则前序延误就会传染给后序;空间维度上,不同登机口能停的机型不一样,有的口只支持窄体机,有的口带双登机桥可以接宽体机;旅客维度上,中转旅客的步行距离、同航司航班尽量靠一起、高端旅客休息室位置这些都要考虑。
除了这些常规约束,还有不少“软性规则”是文档里不会写清楚的。比如某个登机口常年被某家航司“口头占着”,比如远机位在恶劣天气下要尽量减少使用,比如某个时段国际航班集中到达时,海关通道附近的登机口压力会特别大。这些规则在调度员的脑子里,不在系统里。所以如果直接用纯规则引擎做分配,你会发现写规则的人比做数据的人还累,而且每个机场的规则还不一样。
正因为如此,这个项目才适合用机器学习来做。机器学习的思路不是去穷举所有规则,而是从历史分配结果里反推“什么特征的航班组合更容易被排在一起”、“什么情况下调度员会打破常规”,把这些隐性经验变成可量化的打分模型。
1.2 为什么选择机器学习路线
既然前面说了这可以看作约束优化问题,你可能会问:为什么不用整数规划或者约束规划?我在前期调研的时候其实也对比过。整数规划在数学上很漂亮,目标函数加约束条件,跑一个求解器就能出全局最优解。但真正落地时会发现两个尴尬的地方:第一,航班数量一大,变量和约束规模爆炸,求解时间不可控,而运营环境里决策窗口往往只有几分钟;第二,整数规划要求把决策偏好写成明确的数学表达式,但很多偏好本身就是模糊的、动态的,比如“尽量靠近”“尽量少用远机位”,这种表达很难量化成硬性目标。
机器学习的路线就不一样了。它把问题拆成两层:先用模型学习历史分配中的“匹配偏好”,给每个航班-登机口候选对打一个得分;再用一个轻量级的贪心或启发式算法去处理硬约束,按得分高低做最终分配。这样模型负责“学经验”,规则负责“守底线”,各自干各自擅长的事。实测下来,这种组合既避免了纯规则的死板,又比纯整数规划快几个数量级,适合做动态重排和模拟推演。
项目用的是监督学习框架,核心是预测“航班分配到某个登机口的适合程度”。这里有个很关键的设计点:不是让模型直接输出登机口号,而是让模型给每个候选登机口打分,再交给规则层去选。这个设计在后面特征工程部分会详细展开。
1.3 交付物里有什么
拿到的压缩包是zip格式,解压之后目录结构很清晰,主要分三块:data目录存放全部原始数据集和预处理脚本,report目录是方案报告,models目录放训练好的模型文件和评估结果。我建议你也按这个结构来组织项目,数据、代码、报告分离,后期回溯会省很多事。
方案报告我写的时候是按照“问题定义—数据说明—方法设计—实验对比—落地建议”五个部分来组织的,篇幅不算长,但关键图表都在。报告里重点放了不同模型的对比实验表格,以及模型在高峰期、延误场景下的表现分析,这块后面会讲到怎么分析才不虚。
2. 数据集工程:从原始数据到模型输入
2.1 数据集构成与字段含义
这个项目的数据集来自模拟的国内某枢纽机场运行数据,时间跨度大约三个月,包含航班计划表、登机口基础信息表、航班动态表三张核心表。字段设计得很贴近真实场景,我在梳理的时候做了个字段说明表,这里直接列出来:
| 表名 | 关键字段 | 含义说明 |
|---|---|---|
| 航班计划表 | flight_no, aircraft_type, dep_time, arr_time, origin, dest | 航班号、机型、计划起飞/到达时间、起降城市 |
| 登机口信息表 | gate_id, terminal, zone, is_remote, bridge_type, capacity | 登机口编号、航站楼、区域、是否远机位、桥类型、可容纳机型等级 |
| 航班动态表 | flight_no, actual_arr_time, actual_dep_time, delay_reason, transfer_cnt | 实际到达/起飞时间、延误原因、中转人数 |
这份数据最值钱的地方在于把计划和实际拆开了,这是很多新手容易忽略的。做登机口分配训练时,特征必须用计划时间,因为决策发生在航班到达之前,你只能用“预测的时间”去做分配;但如果直接拿计划时间去训练,又完全忽略了延误这个变量。所以数据里必须有实际时间字段,才能构造出“预测误差”这类衍生特征。数据里也带了一部分天气信息,比如风速、能见度,这些对延误预测很有用,但在主模型里我只把它们作为辅助特征,防止特征维度膨胀得太厉害。
2.2 数据清洗与航班拼接
拿到csv之后第一步不是急着建模,而是把三张表关联起来。这里有个典型的坑:航班号和日期必须联合起来作为主键,因为同一个航班号每天都会飞一班。直接用flight_no关联会查出几百条重复记录,我第一次就跑出来一个笛卡尔积,内存直接爆掉。所以清洗阶段要先把“日期+航班号”拼成一个唯一ID,然后再关联。
时间字段也要统一格式。原始数据里到达时间用的是“HHMM”格式,比如“2315”表示23点15分,但跨夜航班会变成“0115”这种,如果不加日期直接转换,就会出现到达时间早于起飞时间的诡异情况。处理办法是起飞日期、到达日期分开解析,或者用相对时间戳统一计算。这种细节看起来小,但错了会影响后续所有时间窗口计算。
清洗完的数据里还有一部分极端值,比如某个航班地停时间只有15分钟,明显低于正常过站标准,这些要么是数据录入错误,要么是特殊保障航班。我在实验里把过站时间小于25分钟的记录单独揪出来了,发现它们标签噪声特别大,干脆先剔除,后面在报告里单列一节说明,避免别人质疑你数据清洗过度。
2.3 构造样本与标签
整个项目最核心的设计决策在样本构造这一步。登机口分配的原始数据是每个航班实际被分配到了哪个登机口,也就是一行记录一个航班。但模型不能直接在一行上训练,因为“这个航班适合A口”和“这个航班适合B口”是两个不同的判断。所以我用“航班-登机口候选对”作为基本样本:每个航班至少出现在10~15个登机口的候选项里,模型要对每个候选对输出一个分数。
标签怎么打呢?最直接的方式是把“实际分配到的登机口”标为1,其他候选口标为0。这个思路对,但有个隐患:历史实际分配并不一定是最优解,有时候只是调度员随手选的或者被前序延误逼的。所以我在标签设计上做了一个软化处理:在训练集里只保留那些“看起来有明确偏好”的样本——比如同一航班在相似条件下多次被分到同一个区域,就把那个区域标为正样本;而一些明显是被迫安排的远机位样本,直接放进验证集而不是训练集。这样模型学到的是比较稳定的分配逻辑,而不是历史噪声。
标签做完之后要检查正负样本比例。航班-登机口候选对天然是极不平衡的,一个航班只有1个实际登机口,但候选口有15个,负样本比例轻松超过90%。后面我会讲到怎么处理不平衡,但想提醒的是:不要一上来就盲目上采样或下采样,先看看模型有没有学到真实信号。
2.4 目标变量的定义陷阱
标签设计里还有一个容易踩的坑,就是“同一批数据既做分类又做回归”。一开始我试过把目标变量直接定义成“该登机口的分配频次”或者“该航班使用该登机口的次数”,想做成回归任务,结果效果很差。原因很简单:频次高不代表适合,有可能是某个登机口因为位置好所以被抢着用,但高峰期早就饱和了。回归目标学出来的是“热门度”而不是“匹配度”。
后面我把问题重新收敛成二分类加得分排序:模型输出的是“该航班-登机口对是否合适”的概率,再用这个概率做排序。排序结果比分类结果更有用,因为最终决策层需要的是候选登机口的优先级列表,而不是一个绝对的是或否。这种“分类问题当成排序问题做”的思路,在运营优化类项目里很常见,我建议记下来。
3. 特征体系和模型选型
3.1 特征体系设计
特征工程是这个项目花费时间最多的部分。我最终把特征分成四组,每组都有明确的设计意图:
第一组是时间特征。包括航班计划到达时间、计划起飞时间、过站时长、到达时刻在一天中的相位(比如用sin/cos变换表达早中晚)、是否属于航班波高峰时段等。时间特征特别关键,因为登机口冲突本质上就是时间轴上的资源竞争。我在做特征时特意计算了“同一时间段内该区域到达航班数量”,这个拥挤度特征对模型的贡献非常明显。
第二组是空间特征。包括登机口所在航站楼、区域、是否远机位、可容纳机型等级,以及航班起降城市对应的航站楼偏好。比如某些航司常年使用T2的固定区域,那“航司-航站楼匹配度”就是很强的信号。这组特征其实是把调度员脑子里的“地盘意识”显性化。
第三组是航班属性特征。机型大小、国内/国际、是否中转航班、中转人数估算、是否早班首发、是否末班结束。这些特征直接影响登机口资源的需求,比如宽体机必须停靠带双桥的登机口,中转人数多的航班要尽量靠近中转通道。
第四组是动态压力特征。包括当前航站楼的总体饱和度、临近时间窗内冲突候选登机口数量、前序航班延误概率预测值。这组特征是为了让模型具备“看全局”的能力,不能只看单个航班和单个登机口的关系。
四组特征加起来一共40多个维度,我在训练前做了相关性分析和特征重要性排序,砍掉了几个贡献极低的字段,比如“天气代码”细分类别,因为模型在有限样本量下学不出细分类的增量信息,留个“是否恶劣天气”就够用了。
3.2 模型选型与对比
模型选择上,我先跑了一个逻辑回归作为baseline,目的不是追求精度,而是验证特征方向和标签设计有没有问题。逻辑回归的结果很快出来了,AUC在0.72左右,说明特征信号是存在的,但线性模型表达不了复杂的交互关系,比如“高峰时段 + 宽体机 + 远机位”这种组合效应。
接着上了梯度提升树模型,我用的是XGBoost和LightGBM都试了。这类模型对表格数据确实友好,能自动处理缺失值和非线性关系,训练速度快,调参空间也大。LightGBM在同样的特征下AUC跑到了0.86左右,明显比逻辑回归强。特征重要性排序里排最前面的几个特征基本符合业务直觉:航班波拥挤度、航司区域匹配度、机型与登机口容量匹配度。
我也试过用图神经网络做这个任务,因为航班和登机口之间的关系天然适合用图结构表达。GNN模型的潜力很大,但工程复杂度高,训练时间翻了几倍,最终效果只比LightGBM高一点点,在方案报告里我把这个结果如实写了,并且给出了建议:如果业务上需要动态重排,GNN的推理能力可能更适合;如果是做离线方案推演,梯度提升树性价比更高。一个好的方案报告不是只写“我用的模型最牛”,而是把不同方案的取舍讲清楚。
3.3 约束处理和后处理
模型输出得分之后,距离真正的登机口分配方案还差一步——约束处理。我这里的做法是:把模型得分当成“软偏好”,把约束规则当成“硬过滤器”,用贪心算法按时间顺序遍历航班,每次从候选登机口里选得分最高的、并且满足所有硬约束的口。
硬约束包括:机型匹配(宽体机不能进窄体机口)、时间不冲突(前后航班过站间隔足够)、区域状态可用(该登机口没有被维修或占用)、国际国内分离(国际航班不能分配到国内区域的口)。这些约束我在预处理阶段就转成了掩码矩阵,模型推理的时候直接取交集,速度很快。
贪心分配出来的方案虽然不能保证全局最优,但在实际运营场景里已经够用。因为航班是动态变化的,一个“差不多好但能快速算出来”的方案,比一个“理论最优但算不出来”的方案更有价值。我在代码里加了一个重排模块:如果新航班插入导致冲突,就把它附近的航班做局部的邻域搜索,最多移动两三个航班就能腾出位置。这个后处理模块实测能把冲突率再降低20%左右。
3.4 评估指标怎么定
登机口分配这个任务的评估指标不能只看模型AUC,因为真正落地看的不是“打分对不对”,而是“最终分配方案好不好”。所以我用了三层指标来评估:
第一层是模型指标,包括AUC、Precision@k、Recall@k,重点看排序质量。Precision@3的意思是:模型推荐的Top3候选登机口里,实际分配结果命中的比例。这个指标比AUC更贴近业务。
第二层是方案指标,包括登机口冲突率(同一口时间重叠的航班对数)、远机位使用率、平均旅客步行距离、航司区域集中度。这一层指标直接反映方案质量,也是方案报告里的核心图表。
第三层是运行指标,把生成的分配方案交给仿真模块跑一遍,看在高延误场景下航班的扩散延迟和登机口切换次数。这层指标虽然重但很有说服力,特别是跟原始人工方案对比的时候,能明显看出机器学习方案的稳定性优势。
我在报告里放了一个对比表格,把人工规则方案、纯贪心方案、机器学习+后处理方案放在一起比,机器学习方案在冲突率上牺牲了一点点(因为贪心不追求全局最优),但在远机位使用率和旅客步行距离上明显更优。这个结果不是“全胜”,但真实可信。
4. 实操过程全复盘
4.1 拿到压缩包后的第一步
先别急着解压跑代码,建议第一步先检查文件完整性。zip文件在传输过程中容易损坏,我这次就遇到过一次解压报错,提示“file is not a zip file”,后来发现是下载中断导致的。Linux下可以用unzip -t 文件名.zip先测试压缩包是否完整,Windows下用Bandizip或7-Zip打开时如果提示错误也会告诉你哪个分卷有问题。测试通过之后再解压,避免后面跑代码跑到一半才发现数据缺失。
解压命令很简单,在Linux或macOS终端里执行:
unzip 基于机器学习的航班登机口分配内含数据集和方案报告.zip -d flight_gate_project不加-d参数会把文件直接解压到当前目录,如果压缩包很多会搞得一团乱。建议养成用-d指定目标目录的习惯。解压完之后进入项目目录,先看README或者目录结构,搞清楚每个文件夹是干什么的,再开始动数据。
Windows用户如果没装命令行工具,用7-Zip或者Bandizip的右键解压都行,右键选“解压到当前文件夹”就能看到完整目录。这里提醒一句:解压路径尽量不要带中文和空格,Linux下跑Python脚本时中文路径偶尔会在编码上出幺蛾子,省心起见直接放到纯英文路径下。
4.2 基线规则实现
在跑任何机器学习模型之前,先实现一个基线规则版是必须的。这个基线不仅是为了后续对比,更重要的是让你对数据有体感。我用的是三条规则组合:航班到达时间排序 + 同航司区域优先 + 机型容量匹配。这套规则其实就是调度员最原始的决策逻辑,实现起来不到一百行Python代码,但已经是所有复杂模型的“最低分数线”了。
基线写完之后记录它的方案指标,包括冲突率、远机位使用率、步行距离。这些数字会在后面的建模过程中反复被拿出来对比。如果你的机器学习模型连这三条规则都打不过,那说明特征或者模型设计有问题,要回头查而不是强行调参。我发现在这个项目里,规则基线的远机位使用率是18%左右,机器学习模型能压到13%左右,这个差距在方案报告里很有说服力,因为远机位使用率直接关系到旅客摆渡车成本和廊桥收入。
实现基线的时候要留意规则冲突的处理顺序。比如一个航班既满足“同航司区域优先”,又只有一个可选登机口满足机型匹配,到底是优先“硬约束”还是“软偏好”?我的建议是先用硬约束过滤,再用软偏好排序,这个顺序不能反。如果先按偏好排序再过滤,可能出现偏好分最高但被硬约束全灭的情况,导致无口可用。
4.3 模型训练与调参要点
模型训练这块我用的LightGBM,如果只是复现项目,这个库最省心。核心代码结构大概是这样的:
import lightgbm as lgb from sklearn.model_selection import GroupKFold # group是关键:同一航班的所有候选口要放在同一折里 gkf = GroupKFold(n_splits=5) for train_idx, val_idx in gkf.split(X, y, groups=flight_ids): train_data = lgb.Dataset(X.iloc[train_idx], label=y.iloc[train_idx]) val_data = lgb.Dataset(X.iloc[val_idx], label=y.iloc[val_idx]) params = { 'objective': 'binary', 'metric': 'auc', 'learning_rate': 0.05, 'num_leaves': 31, 'max_depth': 6, 'feature_fraction': 0.8, 'bagging_fraction': 0.8, 'bagging_freq': 1, 'verbose': -1 } model = lgb.train(params, train_data, num_boost_round=300, valid_sets=[val_data])调参的几个关键点:num_leaves不宜设太大,表格数据上特别容易过拟合,我用31就够;learning_rate设小一点配合早停,具体是:
model = lgb.train( params, train_data, num_boost_round=1000, valid_sets=[val_data], callbacks=[lgb.early_stopping(50), lgb.log_evaluation(50)] )早停轮数设为50,也就是50轮验证集AUC不提升就停。这比固定训练轮数稳得多,能省不少时间。
还有一个细节:分组交叉验证时,groups参数必须传航班ID。因为同一个航班的所有候选登机口样本之间不是独立的,如果随机切分,模型会看到“同一个航班的一部分候选口在训练集,一部分在验证集”,相当于变相泄漏,验证集的指标会虚高,而且高得很隐蔽。
训练完之后导出特征重要性,结合业务逻辑检查一遍。如果某个特征的重要性排序明显不符合常理,比如“登机口编号的数字大小”排在最前,那大概率是数据泄漏了,要回头查特征构造过程。我在这个项目里就遇到过“历史分配次数”这个特征重要性高得不正常,后来发现它几乎等于标签本身,删掉之后模型泛化能力反而更好了。
4.4 方案报告的撰写重点
方案报告不是算法说明书,别把参数量、迭代次数这些写一大堆。方案的受众通常是业务负责人或者课程评审老师,他们更关心三件事:问题你是怎么拆解的、方法为什么合理、效果提升多少。我的报告结构是:背景与问题定义放一页,数据说明和探索性分析放两页,方法设计放两页,实验对比和结论放两页,附录放代码和数据字典。
数据探索的部分我放了三张图:航班量随时间变化的曲线,能看到明显的航班波峰和波谷;机型分布饼图,确认宽体机占比;登机口使用热力图,展示不同区域在不同时段的占用情况。这三张图不是为了好看,而是为了论证后面特征设计的合理性——因为有了波峰特征才有“拥挤度”这个关键特征。
实验对比部分除了数值表格,我还加了一个真实场景的案例分析:挑了一天航班量特别大的日子,展示人工方案和机器学习方案在晚到航班插入时的不同表现。这个案例比任何指标都有说服力,因为读者能直观看到“机器学习方案在冲突发生时只需微调两个航班”和“人工方案需要连环调动五个航班”的差别。
报告里要写清楚局限性和假设,比如“本方案暂时没有考虑登机口维修计划,后续如果加入维护任务约束,需要扩展状态特征”。写局限性不是自我否定,恰恰是专业性的体现,说明你知道这个方案的边界在哪里。
5. 常见问题与排查技巧
5.1 文件解压报错怎么定位
zip解压报错是很多人一上来就会遇到的问题,这里集中说几个常见情况。第一种是“file is not a zip file”,多半是文件没有下载完全,或者扩展名是zip但实际不是zip格式。排查方法是在Linux下执行file 文件名.zip,系统会告诉你这个文件真实格式是什么。如果返回的是HTML或者纯文本,说明你下载到了错误提示页面,重新下载就行。
第二种是“invalid zip archive: could not find eocd”,EOCD是zip格式的“结束记录”,报这个错通常意味着文件被截断或者某个分卷缺失。在Windows下用7-Zip打开时会有更明确的提示,比如“文件末端错误”,这种情况下只能重新获取完整文件。还有一个常见场景是压缩包里有中文文件名,在部分Linux环境下解压会出现乱码,解决方法是用unzip -O CP936 文件名.zip指定编码,或者用python -m zipfile -e 文件名.zip 目标目录来解压。
第三种是解压到一半死掉,这个大概率是磁盘空间不足。解压前用df -h看一下空间,或者先查看压缩包内文件总大小,unzip -l 文件名.zip能列出所有文件和大小,心里有数再动手。
5.2 数据泄露的坑
这个项目里最容易犯的数据泄露错误有两个。第一个是用了“实际分配登机口”这个字段去做特征,这基本等于直接把标签拿去训练,模型AUC会高得离谱,但上线即失效。判断方法很简单:凡是出现在标签产生之后才产生的信息,都不能作为特征。第二个是分组切分时没做GroupKFold,导致同一个航班的不同候选口样本被分到了训练集和验证集两边,这种情况下AUC虚高10个百分点都不奇怪。
我检查数据泄露的习惯是:模型训练完之后,故意往特征里塞一个“登机口编号字符串长度”这种垃圾特征,如果AUC提升超过1%,就要怀疑全局数据泄漏了。另一个更稳妥的办法是训练一个随机模型,就是打乱标签训练一次,看AUC是不是接近0.5。如果打乱标签之后AUC还有0.7,那一定是你有意或无意地使用了与标签强相关的特征。
还有一类泄漏比较隐蔽,是在做特征工程时用了全局统计量。“这个航班的登机口出现频次”这种特征,如果用全样本统计的话,就包含了未来信息,正确做法是用训练集分桶统计,或者用时间窗口滑动统计,只用过去的数据算。
5.3 类别不均衡和样本偏移
前面提过正负样本比例接近1比10,这个比例虽然在梯度提升树下还能跑,但会造成模型偏向预测负样本。我处理的办法有三个组合使用:正样本加权、阈值调整、以及用Precision@k做模型选择而不是只看AUC。
正样本加权很简单,在LightGBM里设置scale_pos_weight参数,数值大约是负样本数除以正样本数。这个参数不能设太极端,否则模型会过度把候选口都预测成正样本,反而把排序搞坏。阈值调整是在预测概率上卡一个阈值,低于阈值的候选口直接不推荐,这个阈值用验证集上的Precision-Recall曲线来找,找一个Precision和Recall平衡的位置。
样本偏移的问题更隐蔽。训练集来源于三个月的历史数据,但不同季节、不同航司排班计划变化很大。如果直接拿1月的模型去预测7月的航班,效果会掉不少。我的应对办法是定期用最近一个月的航班数据做增量训练,并且把“月份”“季度”作为特征直接放进模型里,让模型自己学习时期效应。这样即使测试集来自不同时间段,模型也有机会学到“淡季和旺季的分配策略有差异”。
5.4 约束冲突与回退机制
模型输出的结果在落地时还要过一层约束校验,这一层我强烈建议做成独立的模块,不要和模型混在一起。约束校验模块要做两件事:一是校验方案是否满足所有硬约束,二是如果发生不满足,能自动触发回退策略。
我在实现时遇到的一个典型情况是:贪心算法在时间顺序上分配航班,但后分配的航班把前面某个已分配的空隙挤掉了,导致整体冲突数反而上升。解决方法是加一个“局部回溯”:每当新航班加入导致冲突时,尝试把新航班放到次优的候选口,如果还是不行,就对附近几个航班做一个小范围重排,类似回溯搜索但限制了深度。这个模块代码量不大,但对最终方案质量的提升非常明显。
约束校验不通过的情况在最终交付报告里也要记录,作为“已知限制”写进去。不要试图掩盖,因为业务上线时调度员一定会遇到这些情况,提前写清楚能让对方更信任你的方案。我在报告里附了一个案例:某天下午因为雷雨天气导致十几个航班备降,机器学习的重排方案在五分钟内给出了新分配,虽然仍有一些远机位不可避免地要启用,但整体冲突数和旅客摆渡距离都低于人工应急方案的模拟值。
最后再分享一个小技巧:在这个项目里,我一直保留着一份“把模型结果和调度员原方案逐条对比”的记录。不要只看总体指标,专门把机器推荐与原方案不一致的航班挑出来,一条一条看差异在哪。有些不一致是模型错了,有些是原方案在特殊情境下的妥协,但更多时候你会发现自己造的特征遗漏了一些细微信号。这个对比习惯帮我找到了不少可以迭代的方向,比闷头调参有用得多。
本文还有配套的精品资源,点击获取