FDE进化36记-第8记-树模型与排序模型到底该用在哪些客服场景?
简单模型不够用,复杂模型养不起
[读者] 客服平台算法与FDE工程师 [痛点] 该不该上GBDT与排序模型 收益说不清 成本说不明 [现在读] 第7记小模型路由已上线 风险评分与优先级成瓶颈 [读完] 用一张决策表把五类场景 分给对的模型 并说清代价[旧方案] 单棵决策树加人工阈值 | v [新需求] 流失 升级 知识 坐席 服务优先级 都要打分排序 | v [冲突] 复杂模型离线好看 线上解释与维护成本同涨 | v [后果] 模型越堆越多 没人敢改 排序结果说不清 业务不信我是老李,在这条全球智能客服平台上做算法侧的系统落地。平台同时服务多家企业、跑多个项目、覆盖多语言和多渠道,背后连着CRM、订单、支付、物流、工单和知识库。第7记我们把意图分类、路由和评分收敛回传统小模型,路由稳住了,下一个坑立刻露头:路由只回答“这句话该分给谁”,没回答“这件事该排在谁前面”。
现在平台上有五类打分需求:客户流失风险、投诉升级预测、知识推荐、坐席推荐、服务优先级排序。前两类是预测,后三类是推荐或排序。它们看起来都是“给个分数”,但用的模型、评估口径和维护方式完全不是一回事。
最早我们全部用一棵决策树加人工阈值,理由是简单、能解释、能改。后来业务说效果不够,于是有人主张全量换XGBoost,有人主张只把排序换成Learning-to-Rank。两拨人各有各的离线指标,谁也说服不了谁。
到底哪些客服场景真的需要GBDT,哪些一棵决策树加规则就够?
复杂模型相对简单模型,业务收益能验证到什么程度,而不是靠离线指标自嗨?
排序模型上线后,维护成本涨在哪里,怎么把它压回可控?
01、故事
先回到那个周三晚上的现场。某企业客户的客服经理在群里发了一张截图:工单优先级列表的前十条里,有六条是“查询发票抬头”这类咨询,而一个已经连续三次进线、订单卡在物流异常、语气明显升级的客户,排在第三十七位。
我们的任务是:为每一条进线会话产出一个可解释的分数,并据此决定处置顺序,在会话真正变危险之前把人拉进来。
当时的实现非常朴素。特征只有七列:客户等级、订单金额区间、历史投诉次数、近7天会话轮次、情绪词命中数、渠道、是否VIP。模型是一棵max_depth=3的决策树,导出成规则给运营看,阈值由运营手动定档,>0.8是P0,0.5~0.8是P1,往下依次P2、P3。
变化来得很快。平台从单企业扩到多企业,从单一在线渠道扩到邮件、在线、社媒、呼叫中心四条渠道,会话从“每天几百条”变成“每小时上千条跨语言会话”。同一条规则在A企业表现正常,到了B企业,把一大批只是问价的正常客户打成了高风险;坐席推荐更麻烦,同一个坐席在中文会话里被推荐得很准,在西班牙语会话里几乎无人点击。
冲突不在“模型够不够聪明”,而在分数和顺序是两件事。树输出的是概率,运营要的是顺序。概率0.50和0.52落在同一个人工阈值下就是同一档,但它们的危险程度并不相同。更麻烦的是,这个阈值是对全体样本定的,而多企业、多语言、多渠道下,每组的危险基线根本不一样。
于是我们需要重新回答一个问题:这五类需求里,哪些本质上在解预测,哪些本质上在解排序?
分数不是顺序
阈值不分企业
列表漂在半空
人只能去追
02、问题
旧方案失效的第一个表现,是档位把顺序吃掉了。单棵决策树的叶子数有限,同一片叶子里的样本拿到的是同一个概率常数,落到同一个档位里之后,谁先谁后完全靠工单时间。也就是说,我们花了力气训练模型,最后决定顺序的其实是“谁先提交”。
第二个表现,是评估口径和线上用法错位。我们离线看的是AUC,这是一个pointwise指标,衡量的是所有样本混在一起时正样本是否排在负样本前面。但线上用的是“取topN给坐席”和“取前十条给客户经理”,这是一个listwise动作。指标和使用方式对不上,指标再好看也预测不了线上。
业务影响分三条:
- 高风险会话掉队,投诉升级的处理窗口被浪费在排队上;
- 坐席推荐被人工大量覆盖,推荐位形同虚设,反而增加坐席的认知负担;
- 知识推荐的候选文档排不进前三,机器被判定为“没用”,人工写FAQ的动作又回来了。
技术表现也分三条:
- 预测类模型输出概率高度集中,大量样本挤在一两个区间里,组内区分度低;
- 排序类需求被当成分类问题做,评估用pointwise指标,切分用随机切分;
- 全局指标(所有企业混在一起)看着还行,按企业×语言×渠道拆开看就崩,但没人拆。
我们给这一轮定的可验证完成标准是:
- 五类场景各自明确归属“预测”还是“排序”,并写进模型卡片;
- 分组键固定下来(企业×语言×渠道×时间窗),每组有独立基线与定档策略;
- 排序场景以NDCG@k、Recall@k为主指标,pointwise指标只作辅助;
- 每次迭代能在固定测试集上按组复现,指标可追溯;
- 每个模型有明确的复杂度上限和退出条件,写在上线前而不是出事之后。
旧树切成几档
档内再无高低
指标看着还行
线上顺序全乱
03、原理
只讲本篇需要的部分。
决策树递归划分特征空间,做的是轴平行切分,对单调变换不敏感,能输出特征重要度,规则可以直接讲给业务听。它的代价是方差大:训练数据微小变动可能换一棵结构不同的树,而且单树表达力有限。
Random Forest用Bagging的思路并行训练多棵树,样本和特征都做子采样,再平均。它主要降方差,对超参不敏感,是很好的基线,但不容易把偏差压得很低。
GBDT走的是另一条路:串行加树,每棵新树拟合前面模型的残差或梯度,主要降偏差。XGBoost在这条路上加了三样关键设计——带正则的目标函数、二阶泰勒近似、以及稀疏感知的分裂与加权分位草图,把工程实现从“能跑”推到“能并行、能稀疏、能控复杂度”(见XGBoost论文,具体实现细节需按当前官方文档核验)。LightGBM则用直方图分裂、GOSS、EFB和leaf-wise生长换取更快的训练与更低的内存,代价是leaf-wise在小数据上更容易过拟合,需要认真限制num_leaves和min_data_in_leaf(具体参数名与默认值需按当前官方文档核验)。
Learning-to-Rank不是“更强的分类器”,而是换了一个优化单位。pointwise把每个样本当独立问题;pairwise比较组内样本对;listwise直接面向NDCG这类不可微指标,用Lambda梯度之类的代理损失去逼近。它的输入天然是“一个query + 一组候选 + 组内相关度”。
反直觉判断:在客服这类以表格特征为主的数据上,GBDT相比调好的单棵决策树带来的收益,通常小于“把评估口径和切分方式改对”带来的收益。很多“复杂模型更准”的结论,其实是切分泄漏或指标口径错造成的假象。
反直觉判断:排序模型用错场景,收益接近零而成本明显上升。流失风险预测本质是“这个客户会不会走”,不需要组内比较,硬套LTR只是把一个独立预测问题拆成了伪query,白白引入分组维护成本。反过来,服务优先级本质是“同一批待处理工单里谁先谁后”,用pointwise分类再切档,就是拿锤子拧螺丝。
把这套判断落到五类场景上:
客户流失风险 -> 二分类 + 概率校准 -> GBDT够用 投诉升级预测 -> 不平衡二分类 + 成本敏感阈值 -> GBDT 知识推荐 -> query-文档排序 -> LTR(pairwise/listwise) 坐席推荐 -> query-坐席排序 + 曝光偏差 -> LTR 服务优先级 -> 组内排序(队列/企业/时间窗) -> listwise LTR注意最后一列的措辞是“够用”,不是“最强”。这五个格子后面会决定维护成本的高低。
单树切空间
森林降方差
提升压偏差
排序换单位
04、架构
[输入] 会话/工单/客户事件 | v [模块] 特征层(企业×语言×渠道×时间窗) | + 场景路由:预测 or 排序 | + 模型层:Tree / RF / GBDT / LTR v [数据/状态] group_key + 分数 + 档位 + 模型版本 | v [处理] 分组切分 -> 训练 -> 校准 -> 组内排序 -> 阈值/配额 | v [输出] 风险分 / 升级概率 / 推荐列表 / 优先级队列三个边界必须写清楚。
第一,模型层只负责打分,不负责动作。分数出来之后,是自动回复、转人工、还是生成工单,属于Workflow和Human-in-the-loop的职责(第14记会展开)。把动作决策塞进模型,会让模型既难评估又难回滚。
第二,分组键不是越细越好。按企业×语言×渠道×时间窗分组,可以让每组有自己的基线和定档,但切得太细,单组样本量会小到无法训练GBDT,此时应该退回到RF或单树。
第三,预测和排序不能共用一套评估流水线。预测看AUC与校准曲线,排序看NDCG@k与Recall@k,两条线各自有基线、各自有发布门禁。
这套架构的收益是口径统一:任何一个分数都能回答“它属于哪个场景、用哪类模型、在哪个组里评估、对应哪个版本”。代价是特征血缘和分组维护的工作量上来了,这也是后面维护成本的主要来源。适用条件是:有稳定的分组维度、每组有足够样本、并且业务真的按顺序消费结果。
输入先分组
分组再定档
定档才排序
排序回业务
05、实战一次
先把V1跑通,再谈复杂模型。以下为合成数据的最小可运行示例,用于打通“分组切分 → 训练 → 分档 → 读规则”这条链路。未在当前环境实测,以下为预期结果,示例输出中的数值仅代表量级,不可当业务结论。
环境与依赖:
# 示例环境,具体版本需按当前官方文档核验python-mvenv .venv&&source.venv/bin/activate pipinstallnumpy pandas scikit-learn lightgbm xgboostV1:单棵决策树做投诉升级预测 + 全局阈值分档。
importnumpyasnpimportpandasaspdfromsklearn.treeimportDecisionTreeClassifier,export_textfromsklearn.model_selectionimportGroupShuffleSplitfromsklearn.metricsimportroc_auc_score rng=np.random.default_rng(7)defmake_sessions(n=4000,n_tenants=4):tenant=rng.integers(0,n_tenants,n)lang=rng.integers(0,3,n)channel=rng.integers(0,4,n)orders=rng.integers(0,6,n).astype(float)complaints=rng.integers(0,5,n).astype(float)turns=rng.integers(1,30,n).astype(float)sentiment=rng.random(n)base=-3.0+0.35*complaints+0.06*turns+1.2*sentiment+0.1*orders p=1.0/(1.0+np.exp(-base))y=(rng.random(n)<p).astype(int)X=pd.DataFrame({'tenant':tenant,'lang':lang,'channel':channel,'orders':orders,'complaints':complaints,'turns':turns,'sentiment':sentiment,})returnX.drop(columns=['tenant']),y,tenant X,y,tenant=make_sessions()# 分组切分:同一个企业不能同时出现在训练集和测试集splitter=GroupShuffleSplit(n_splits=1,test_size=0.3,random_state=7)tr,te=next(splitter.split(X,y,groups=tenant))clf=DecisionTreeClassifier(max_depth=3,min_samples_leaf=50,random_state=7)clf.fit(X.iloc[tr],y[tr])proba=clf.predict_proba(X.iloc[te])[:,1]print('AUC(示例输出):',round(roc_auc_score(y[te],proba),4))print(export_text(clf,feature_names=list(X.columns),max_depth=2))# 全局人工阈值分档buckets=pd.cut(proba,bins=[-0.001,0.2,0.5,0.8,1.0],labels=['P3','P2','P1','P0'])print(buckets.value_counts().sort_index())预期你会看到两件事。
一是概率分布高度集中——树深只有3,叶子数是个位数,大量测试样本落到同一片叶子上,拿到的概率是同一个常数。二是档位分布同样倾斜,某一档吃掉大部分样本,档内顺序只能靠提交时间决定。
示例输出(量级示意,非业务结论) AUC: 0.7x P3 极多 P2 较多 P1 少量 P0 极少启动与验证方式:
python v1_upgrade_risk.pyV1 跑通了,也把问题留下了:它只能回答“这单危不危险”,不能回答“这批单里谁更危险”。而这个差别,恰恰是服务优先级、知识推荐、坐席推荐三个场景的全部意义。
先跑最小树
再看档位散
留一个疑问
换模型不急
06、排查
诊断一:优先级列表里前几名是低风险会话。
现象:高分段工单的人工复核通过率低,真正升级的会话排在列表中段。
怀疑:GBDT没上,模型区分度不够。
检查:先分组统计概率分位;再用export_text打印规则,看有多少测试样本落到同一片叶子;再看同一片叶子内部的标签混合比例。
证据:树深小、叶子少,不同企业的样本被同一批切分条件覆盖,同一叶子内正负样本混在一起。
根因:单树把连续风险压成少数几个常数,叠加全局阈值,导致跨企业、跨语言的组内顺序失效。问题不在模型容量,而在“用全局标尺量不同组”。
修复:预测类换GBDT输出连续分,同时把阈值改成组内分位数定档(每企业、每语言各自定P0~P3的切点)。
诊断二:离线指标不错,坐席推荐top3被人工大量覆盖。
现象:离线NDCG看着可以,线上坐席经常手动切到别的坐席。
怀疑:模型不行,需要上更复杂的排序模型。
检查:把评估切分从随机切分换成按会话/客户分组切分,再按时间切分重跑一遍;同时检查训练集和测试集里是否出现同一个会话的多个候选。
证据:随机切分下,同一会话的多个候选同时落到训练集和测试集,候选集泄漏;改成按组切分后指标明显回落,回落幅度大于任何模型替换带来的提升。
根因:评估口径与线上口径不一致。离线是pointwise随机切分,线上是listwise按会话消费,加上候选泄漏,指标虚高。
修复:改为分组切分与时间切分;排序场景换LTR目标(组内比较);主指标从AUC改为NDCG@k与Recall@k。
错误尝试:我们第一反应是把决策树的max_depth从3调到15,理由是“树太浅”。调完之后离线AUC确实涨了,我们就以为问题解决了。错在哪?加深单棵树提高的是训练集拟合能力,而我们的故障是“组内顺序不可用”,这两个问题不在一个维度上;深度一上来,跨企业泛化变差、导出的规则长到业务读不动,线上顺序却没变化。判据看错了,改模型容量就是白改。
这两条诊断给出一条共同结论:在动手换模型之前,先把“评估单位”和“分组口径”这两个变量固定下来,否则你无法分辨指标变化来自模型还是来自口径。
现象先看清
怀疑留给数据
证据指向口径
根因才动手
07、优化
严格基于第06章的两条证据做V2。
根因一:全局阈值 + 单树常数概率,导致组内无序。
修改:预测类模型换成GBDT(LightGBM或XGBoost的二分类目标),并对输出做概率校准;阈值改为组内分位数定档。
原因:GBDT把偏差压得更低,输出连续;组内分位数把“绝对概率”换成“在它所属组里的相对位置”,直接对齐业务用法。
新行为:同一个概率0.52在不同企业里可能落进不同档位,但同一组内的顺序始终有区分度。
验证:按组输出分位分布与校准曲线;确认每组各档位样本占比符合预期。
根因二:排序场景被当成分类做,且切分泄漏。
修改:知识推荐、坐席推荐、服务优先级改用LTR;切分改为按query分组切分与时间切分。
原因:LTR的优化单位是组内顺序,与线上listwise消费一致;分组切分消除候选泄漏。
新行为:模型直接优化组内排序,评估指标与线上动作同构。
验证:在固定测试集上比较同一份数据下“分类+切档”与“LTR”的NDCG@k,全部按组统计,不报全局均值。
V2 的关键代码结构(同样是合成数据,用于打通用group的排序流程,未在当前环境实测,以下为预期结果;具体参数名需按当前官方文档核验):
importnumpyasnpimportpandasaspdfromlightgbmimportLGBMRankerfromsklearn.model_selectionimportGroupShuffleSplitdefmake_ranking(n_queries=400,cand_per_query=5):rows,labels,groups=[],[],[]forqinrange(n_queries):tenant=q%4forcinrange(cand_per_query):match=float(np.random.random()*(1.0ifc==0else0.5))rows.append({'tenant_id':tenant,'cand_score':match,'cand_ctr':float(np.random.random()),'is_vip':int(tenant==0andc%2==0),})labels.append(1ifmatch>0.6else0)groups.append(cand_per_query)returnpd.DataFrame(rows),np.array(labels),groups X,y,group=make_ranking()qid=np.repeat(np.arange(len(group)),group)splitter=GroupShuffleSplit(n_splits=1,test_size=0.3,random_state=7)tr,te=next(splitter.split(X,y,groups=qid))group_tr=[group[g]forginrange(len(group))ifginset(qid[tr])]ranker=LGBMRanker(objective='lambdarank',n_estimators=200,learning_rate=0.05,num_leaves=15,min_child_samples=20,random_state=7,)ranker.fit(X.iloc[tr],y[tr],group=group_tr)scores=ranker.predict(X.iloc[te])defndcg_at_k(y_true,y_score,k=3):order=np.argsort(-y_score)[:k]gains=np.asarray(y_true)[order]discounts=1.0/np.log2(np.arange(2,k+2))dcg=float(np.sum(gains*discounts))ideal=np.sort(y_true)[::-1][:k]idcg=float(np.sum(ideal*discounts))returndcg/idcgifidcg>0else0.0# 示例:逐组计算后取均值,而不是把全体样本混在一起算print('NDCG@3(示例输出):',round(ndcg_at_k(y[te],scores,3),4))V2 上线后,我们在模型卡片上补了一行硬约束:排序场景不允许用全局AUC作为发布门禁。这一行比任何模型替换都更省事。
至于收益,这里不给百分比。收益必须用你自己的数据、自己的分组、自己的线上口径去验证;任何在文章里写出的提升幅度,都不该被你直接搬进生产。
证据先落纸
改动留最小
口径先对齐
模型再升级
08、演进
[同一输入] 一条会话 + 一组候选 | +--[V1] 单树 + 全局阈值 | 简单 可解释 单文件可跑 | 代价:档内无序 跨组失效 排序场景表达不了 | +--[V2] GBDT + LTR + 组内定档 | 收益:组内顺序可控 评估与线上同构 | 代价:分组口径要维护 特征血缘要维护 模型数量增加 | [Trade-off] 得到什么:可复现的排序能力与可解释的定档规则 失去什么:单脚本的极简与部分跨组可迁移性 适用边界:每组样本充足、业务确实按顺序消费结果逐项比较:
- 正确性:V1在“识别高风险个体”这件事上大致成立,但无法回答“谁先谁后”;V2把顺序变成一等公民,正确性定义本身更贴近业务。
- 稳定性:单树方差大,换一批数据可能换一棵树;RF更稳,GBDT需要认真控容量,leaf-wise尤其要限
num_leaves。 - 复杂度:V1是一个脚本;V2是一套流程,包含分组切分、校准、LTR分组、按组评估四条流水线。
- 成本:V1成本几乎为零;V2的成本主要在分组维护、特征血缘、指标看板和退出条件上,而不是训练算力。
- 适用范围:样本量小、候选数小于2、特征只有个位数时,单树或RF仍然更划算;跨组迁移要求高时,RF比深度GBDT更稳。
- 遗留问题:曝光偏差(被推荐过的坐席才有反馈)、时间漂移(政策与话术变化)、以及多语言特征在不同语言间的可比性,V2都没有彻底解决。
单树保简单
排序补顺序
取舍写清楚
边界别含糊
09、洞见
9.1 先定评估单位,再定模型
反直觉判断:大多数“树模型不够用”的结论,来自评估口径错,而不是模型容量不够。
我们的两条诊断都指向同一个事实:随机切分加上pointwise指标,可以让任何模型看起来都还行,也可以让任何模型看起来都不行。评估单位是“样本”还是“会话”,是“全局”还是“分组”,决定了你能看到什么问题。先把单位定下来,模型选型才有意义。
9.2 复杂模型买到的是偏差,不是全部
Tree → RF 主要降方差,RF → GBDT 主要降偏差,GBDT → XGBoost/LightGBM 主要是工程效率(正则、稀疏感知、直方图、并行)而不是全新的建模思想。这条链条上,越往后越贵的其实是口径与维护,而不是模型本身。所以当你发现指标不动时,先问一句:我是容量不够,还是尺子不对?
9.3 Learning-to-Rank是换问题,不是换模型
把流失预测硬套LTR,得到的是一堆组内比较,收益接近零;把服务优先级做成pointwise分类再切档,得到的是档内无序。判断标准很朴素:**这个场景的答案依赖同一批候选之间的相对比较吗?**依赖,就用LTR;不依赖,就用分类加校准。
9.4 维护成本随模型数量增长,不随模型复杂度线性增长
这是我们在系统落地时最直观的感受。三个场景、五个模型、四类分组,组合出来的评估与监控矩阵是指数级的。真正的成本不在“GBDT比决策树难训”,而在“每多一个模型、每多一个分组,就要多一套基线、多一套门禁、多一条回滚路径”。所以控制成本的正确动作是收敛模型数量,而不是回避复杂模型。
9.5 退出条件必须写在上线之前
每个模型卡片都应该有一行:当分组样本量低于多少、当组内NDCG连续多久不达标、当某个特征的血缘断掉时,退回到什么。写不出来,说明这个模型还不具备上生产的资格。
口径大于模型
单位大于容量
成本随数涨
退出写在前
10、系统落地
放进这条全球智能客服平台看:
原来有什么——第7记之后,平台有了一套稳定的路由与评分基线:意图分类、路由决策、置信度评分都由传统小模型承担,模型、特征、版本都有登记。
本篇新增了什么——在模型层里加了一级“场景路由”:预测类(流失风险、投诉升级)走GBDT加校准,排序类(知识推荐、坐席推荐、服务优先级)走LTR;同时引入固定分组键(企业×语言×渠道×时间窗)和按组评估流水线,把NDCG@k、Recall@k纳入发布门禁。
现在能做什么——给任意一条进线会话,可以同时得到“风险分”和“在这批候选中的顺序”,并且两者都能按企业、语言、渠道拆开看指标、看分布、看校准;任何一个排序结果都能追溯到模型版本、特征版本与分组口径。
还缺什么——曝光偏差没有处理,坐席推荐里被推荐过的坐席才有反馈,未被推荐的坐席永远是未知的;跨语言特征的可比性还在用近似方案;模型退出的自动化动作(自动回退、自动降级到单树基线)还没有落地;这些是后面几记要处理的问题。
下一步如何演进——把场景路由从“人工写死的映射表”变成配置化的模型注册中心,让新增一个客服场景时可以只声明四件事:场景类型(预测/排序)、分组键、主指标、退出条件。这一步做完,模型数量增长才不会直接变成运维负担。
从分数到列表
从全局到分组
从模型到契约
从上线到可退
11、小结
Q1 → 预测类用GBDT加校准,排序类用LTR, 小样本或候选数少的场景用单树或RF Q2 → 收益必须按组、按线上口径验证; 全局离线指标不能替代业务验证 Q3 → 成本涨在分组维护、特征血缘、按组评估 与退出条件上,收敛模型数量比回避复杂模型更有效 状态 → 五类客服场景各自有一个可复现、可解释、 可退出的模型基线,并接入统一发布门禁问题有答案
能力可交付
边界写清楚
下一步已知
12、作业
12.1 理解题:为什么服务优先级排序不适合用pointwise二分类直接切档?
因为pointwise把每条工单当独立问题,输出的是绝对风险;一旦用全局阈值切档,同一档内就没有顺序,而业务消费的恰恰是档内顺序。正确做法是把同一批待处理工单构造为一个query,用listwise或pairwise目标优化组内相对顺序,评估换成NDCG@k或Recall@k。
12.2 实战题:有一批跨企业、跨语言的工单,要训练升级预测模型,如何设计切分与评估?
切分按企业×语言分组,杜绝同一个企业同时出现在训练集与测试集;如数据带时间,改用时间切分,模拟线上“用过去预测未来”。评估先按组统计AUC与校准曲线,再看各档位样本占比,最后才看全局均值。如果业务真正消费的是顺序,则在此基础上补一个LTR版本,用NDCG@k对比。
12.3 排障题:离线NDCG很好,上线后人工覆盖率很高,先查什么?
先查切分与评估口径,不查模型。检查点:是否随机切分导致同会话候选泄漏;测试集是否包含训练期之后才出现的分布;NDCG是按组算均值还是全体混算;线上候选集与离线候选集是否同构(候选数量、过滤规则)。多数情况下修好口径后指标会落回真实水平,之后再谈模型是否要换。
12.4 架构判断题:平台已有15个树模型,业务要求再加3个场景,是再加3个GBDT还是先做模型注册中心?
先做注册中心。15个模型的评估、监控、回滚路径已经是指数级维护成本,再叠加3个场景,边际收益会被运维成本吃掉。注册中心要能声明四件事:场景类型(预测/排序)、分组键、主指标、退出条件;做不到这四点的新场景,宁可用单树或RF基线先跑,也不要直接上复杂模型。
想清再动手
动手留证据
排障看口径
判断看收益
13、思考
回到开篇那个冲突:复杂模型相对简单模型,真实业务收益到底是什么?
这一轮走下来,答案不是“GBDT比决策树准多少”,而是三句话:模型复杂度买到的是偏差和工程效率;评估口径买到的是你能否看见问题;分组与退出条件买到的是这套东西能不能长期活下去。在客服这类多企业、多项目、多语言、多渠道的场景里,最大的风险从来不是模型不够强,而是尺子不对、口径不齐、退出无门。
所以选型顺序应该是:先确定这个场景解的是预测还是排序,再确定评估单位和分组键,再确定主指标与门禁,最后才在Tree、Random Forest、GBDT、XGBoost、LightGBM和LTR之间挑一个够用的。
一念辨单位
一证定口径
一改知代价
一役见真章