组推荐系统的公平性:定义、度量与算法落地实践
组推荐系统的公平性我这两年研究了不少也和几个做推荐的朋友反复聊过。先说个结论单用户推荐的公平性问题还没完全解决组推荐场景下的公平性更是被严重忽视。但偏偏现在的实际应用里看电影、定餐厅、选旅行路线几乎都是多人一起决策。你给一个人推荐得再准如果小组里其他人被牺牲了这个推荐方案就很难落地。这篇就围绕组推荐中的公平性问题从定义、度量、算法到实操踩坑把我试过的东西和思考完整整理一遍。1. 组推荐到底在推荐什么1.1 从单用户推荐到组推荐的本质变化单用户推荐是典型的“投其所好”根据用户历史行为、画像、上下文预测ta对物品的评分或点击概率然后按分排序。组推荐要面对的则是一群人每个人都有自己的偏好图谱而且偏好之间往往存在冲突。比如五个人一起聚餐一个人是素食主义者一个人无辣不欢一个人正在减肥还有两个只是“随便”。传统推荐算法如果按照平均分来排序很可能选出一个人人都不满意、或者只照顾到多数人的餐厅。组推荐系统的核心问题就变成了如何在一个物品上平衡多个用户的不同偏好让整个群体尽可能满意。这听起来像是多目标优化但实际操作中公平性比“总满意度最大化”要复杂得多。因为“总满意度最大化”本质上允许牺牲少数人的体验而组推荐里的每个成员都是真实付费、真实参与的个体一旦某个人连续几次都觉得“推荐结果根本不是给我看的”ta就会对整个系统失去信任。1.2 公平性为什么在组推荐里更尖锐单用户推荐里的“不公平”往往体现在不同用户群体之间比如新用户冷启动阶段推荐质量差、小众用户得到的推荐多样性低。组推荐里的不公平则发生在同一组内的个体之间这种矛盾更直接、更容易被感知。举个例子家庭出游场景父母想逛博物馆孩子想去游乐园。组推荐如果只输出一个方案这个方案很难同时满足两方。这时候如果算法因为“孩子们的评分方差大”或者“亲子类标签覆盖率高”而偏向某一方另一方就会强烈不满。更麻烦的是组推荐通常是一次性决策没有太多“下次再试”的机会——你选了一家餐厅大家已经坐下来了才发现某个人不爱吃这个体验就彻底毁了。所以组推荐的公平性不是学术上的锦上添花而是产品能不能真正落地、用户愿不愿意持续使用的关键。2. 公平性定义先搞清楚你在优化什么2.1 三种主流的公平性视角公平性这个词很有迷惑性不同领域、不同场景下含义完全不同。我在做组推荐时一般把公平性拆成三个视角第一种是偏好覆盖公平。指的是推荐结果能不能覆盖到组内每个成员的偏好。比如五个人里有一个喜欢日料四票投给火锅但系统仍然需要偶尔给出日料选项保证那个人的偏好在结果集里“被看见”。这类公平性衡量的是推荐列表中的品类/主题/特征是否照顾到了所有人。第二种是满意度均衡公平。不追求每个人都得到最高分而是追求组内成员对推荐结果的满意度尽量接近。比如A对推荐餐厅打分4.9B只打2.0即使平均分很高系统也是不公平的。满意度均衡希望两人的分数差距控制在合理范围内比如4.2 vs 4.0。第三种是影响力公平或博弈论公平。组内每个成员对最终推荐结果的影响程度应该对等不能因为某个人历史交互多、偏好明确就把它的意见权重放得过大。比如有个成员是所谓“权力用户”系统一直根据他的偏好推荐其他人实际上被无视了这就是影响力不公平。三种视角不是互斥的实际系统里经常要组合使用。我在设计时一般把“偏好覆盖公平”作为底线“满意度均衡公平”作为优化目标“影响力公平”作为诊断指标定期监控。2.2 从信息检索里的公平性借鉴信息检索领域对公平性的研究更早Amazon、Google这些公司都有比较成熟的分析框架。组推荐可以直接借用其中两个核心概念个体公平individual fairness和群体公平group fairness。个体公平的意思是相似的个体应该得到相似的处理。具体到组推荐就是偏好相似的用户对同一个推荐结果的满意度应该接近。如果A和B都喜欢川菜推荐一家川菜馆后A很满意B却很不满那系统大概率只学到了A的偏好细节而没有抓住两个人的共同点。群体公平则强调不同特征的群体比如老年用户和年轻用户、高频用户和低频用户在推荐结果中的体验差异不能太大。组推荐中的“群体”还可以指组内的多个人也就是对组内成员一视同仁不能因为某个成员属于主流人群就倾斜。借鉴信息检索的一些统计方法很有价值比如计算推荐列表的曝光不均衡度、点击率差异等但这些指标用在组推荐里时要注意组推荐往往只有一个或少数几个推荐结果统计样本太小直接套用它们的指标没有意义。2.3 公平性指标的数学表达要给公平性建模必须有可计算的指标。我用得比较多的包括以下这些满意度标准差Std of satisfaction组内所有成员对推荐结果满意度的标准差越小代表越公平。最大最小满意度Max-min satisfaction先找组内满意度最低的成员然后最大化这个最低值。这在博弈论里对应Rawlsian公平也就是“最差的人也要尽量好”。偏好命中率Preference hit rate推荐结果中包含每个成员偏好类别的比例所有人都至少有1/ N的结果合自己的偏好才算公平。影响力的基尼系数通过反事实推断计算每个成员的偏好对最终结果的贡献度再用基尼系数衡量这些贡献度之间的差异。举个例子三个人组队看电影A喜欢科幻B喜欢爱情C喜欢动画。系统推荐了五部电影[科幻片1, 科幻片2, 科幻片3, 爱情片1, 动画片1]。A命中三类B命中一个C命中一个粗看还算公平但细看排名发现前三个全是科幻片C的动画片排在最后一位。这种排序位置的差异对用户感知影响很大——大多数用户根本不会看完整个列表。所以我还经常计算加权偏好命中率越靠前的命中权重越高用来捕捉这种位置歧视。3. 组推荐公平性的核心挑战3.1 偏好聚合的“平均陷阱”传统组推荐最常见的方法是偏好聚合Preference Aggregation先预测每个用户对每个物品的评分再把所有用户评分聚合起来比如取平均、取最小、或者使用加权平均。听起来简单但这里藏着很多公平性问题。取平均的后果就是少数派偏好被淹没。假设一个组有六个人五个人给火锅打8分一个人给火锅打2分、给日料打9分。火锅平均分是7日料平均分是6.2系统更可能推荐火锅。那个唯一不喜欢火锅的人会觉得系统完全不在乎他。取最小值Least Misery稍微好一点它要求每个物品的组评分等于组内最低个人评分然后选最低分最高的那个物品。这种策略很符合满意度均衡公平但代价是推荐结果经常趋于平庸——所有人都能接受但没有人特别开心。而且如果组里有个“挑剔者”他一个人的偏好会压倒所有人这又变成一种新的不公平挑剔者影响力过大。我在实际项目里做过对比取平均值在用户满意度均值上最高但组内满意度的方差也最大很容易让某个人暴怒。取最小值对维持组内和谐很有帮助但产品方会担心“没有惊喜”。后来我采用的方法是先做公平性约束再做聚合也就是说先保证每个成员的偏好至少出现在候选集里再按平均分排序相当于给平均策略加了一个“保底机制”。3.2 画像稀疏性与偏好估计偏差组推荐的另一个难点是我们往往没有组内每个成员的完整偏好数据。有的成员是注册很久的老用户历史行为丰富有的成员可能只是被拉进组的新用户历史记录寥寥。系统在预测评分时对稀疏用户的预测方差更大经常给出非常极端或者非常中庸的分数。这种时候如果直接用预测评分做聚合公平性会天然偏袒“历史数据充分的人”。因为他们的偏好被模型学到了而稀疏用户的偏好更多是模型瞎猜的。为了让公平性不流于表面我一般会做两件事对预测分数的不确定性建模。如果某个用户对某类物品的预测方差很大在聚合时给它一个较低的权重降低系统对噪声和猜测的依赖。引入“默认偏好”或“群体先验”。当用户行为数据不足时用同年龄、同性别、同城市人群的群体偏好作为冷启动偏好基线同时把置信度反馈给推荐逻辑避免把猜测当事实。画像稀疏性带来的公平性问题在冷启动组推荐里尤其明显。比如一群朋友出来玩新加入的朋友大家不了解算法也不了解最后推荐的几乎全是熟人的偏好。这种“看不见新成员”的问题比单用户冷启动更加麻烦因为它还叠加了一层人际互动——熟人可能会觉得新朋友难伺候新朋友则觉得自己被忽视。解决思路是故意在推荐列表里增加基于猜测的探索项即使一开始评分不高也要提供机会让新成员表达反馈帮助系统快速收敛。3.3 组内冲突的检测与处理公平不意味着永远一团和气有时候组内偏好确实存在不可调和的结构性冲突。这时候最好的推荐不是硬找一个“大家都能接受”的方案而是明确检测出冲突然后通过多样化推荐给用户选择空间。我设计过一个简单的冲突检测方法计算组内每两个用户在所有候选物品上的偏好相似度。如果大部分用户两两相似度都很高说明这个组是“和谐组”直接用平均聚合就很好。如果存在几对用户相似度很低甚至负相关说明组内存在多个偏好阵营这时候如果只推一个结果无论怎么选都有人不满意。针对“冲突组”公平的做法不是平均而是等比例覆盖——让每个阵营推选出对应数量的候选结果交给用户自己讨论。比如一个阵营偏爱安静的书店一个阵营偏爱热闹的酒吧系统就同时推荐一家书店和一家酒吧。这相当于把“推荐”转变成“提供谈判筹码”虽然看起来推荐系统没有给出唯一决策但反而更能满足用户。这个思路和组推荐中常见的“top-1 vs top-N”矛盾有关。传统单用户推荐总想直接给一个“最佳答案”但组推荐因为涉及人际协商最好的交互形态往往是“一组候选组内讨论”。我在做产品Demo时验证过显示三个不同风格候选的版本比只显示一个“最佳匹配”的版本用户满意度和组内气氛评分都更高。4. 从算法到落地实现公平组推荐的关键步骤4.1 数据准备必须为每个成员单独建模很多组推荐系统直接把“参与人员”打包成一个组合特征训练一个预测模型这种做法对公平性极其不利。因为它天然假设组是独立实体成员之间的偏好不一致会被平均掉无法追踪是谁满意谁不满意。正确做法是仍然以单个用户为粒度维护预测模型然后在推荐阶段从每个用户模型中拉取候选评分。这意味着架构上要有两层底层是单用户推荐引擎顶层是组聚合层。我使用的方案是底层用已有的单用户推荐算法比如协同过滤或双塔模型输出每个用户对候选物品的评分向量。顶层组推荐模块拿到所有成员的评分向量进行公平性加权聚合。聚合后的排序结果统一经过公平性校验层检查偏差指标是否超标。这样做的好处是单用户模型的改进可以直接提升组推荐表现组推荐模块也不需要对底层模型做太大改动。更重要的是它保留了每个成员的个人视图让公平性计算成为可能。4.2 三阶段公平组推荐流程我把组推荐的完整流程拆成三个阶段第一阶段候选集生成。不管用什么底模先从候选池里筛出可能让任何一位成员感兴趣的物品。这里“任何一位”是公平的关键。具体操作上我会对每个成员单独取Top-K候选然后求并集。如果候选池很大建议K取20~50否则并集后数量过大影响后续排序效率。这个阶段的目标就是防止少数派偏好提前被淘汰。第二阶段公平聚合排序。对候选集中的每个物品分别计算每个成员的预测满意度然后进行带公平约束的聚合。最简单的公平约束是某个物品如果把组内满意度最低的人的分数从1分提高到了4分它的排序优先级要显著提升。我实际使用的聚合公式是score(item) alpha * avg_satisfaction(item) beta * min_satisfaction(item) - gamma * satisfaction_std(item)其中alpha、beta、gamma是超参数。默认我会取alpha0.4beta0.4gamma0.2在离线评测里这个组合对“平均满意”和“最低满意”的平衡比较好。如果场景更看重体验下限比如医疗推荐、家庭出游可以把beta调到0.6gamma保持0.2。第三阶段公平性校验与再排序。聚合排序后再计算一次组内满意度标准差、偏好命中率等指标。如果最高分和最低分差距过大说明不公平这时需要执行“公平性纠偏”——把最低分成员喜欢的物品往前提升或者直接把排名最靠前的多个候选替换掉保证结果列表有代表性。4.3 超参数调整的几个实操经验上面公式里的alpha、beta、gamma看起来很简单但调参踩过的坑不少。首先是beta不能太大。曾经某次实验我把beta设为0.7结果推荐结果全部变成了“最平庸的安全牌”组内每个人都是6分左右看起来非常公平但整体满意度掉了30%。后来才意识到最小满意度最大化策略只适合单一结果推荐不适合列表推荐。因为列表里有多个位置你完全可以在前面放一个高分高方差的结果再在后面的位置放一个低分低方差的结果来兜底。所以我对于Top-N列表会动态调整权重排前3位时更看重avg_satisfaction排后两位时更看重min_satisfaction和diversity。其次是gamma别设成负数。有次调试时发现满意度标准差项的符号能不能加减我一时犹豫随手写成 gamma * std()结果算法反而推荐了和组内偏好最分裂的物品因为这样标准差最大、得分最高。这个错误看起来很可笑但如果不做离线评测是真的很难发现因为损失函数没写好模型也能收敛只是解的偏好完全反了。4.4 组件模型如何应对组内人数变化组推荐还有个对公平性影响很大的变量组的人数。两个人的组和十个人的组公平策略完全不同。两人组如果偏好冲突最优选择往往是交替/折中推荐比如这周选A喜欢的下周选B喜欢的。动态公平这个概念很重要单次推荐可能没法让双方都满意但连续两次推荐可以做到轮流满足。三人以上的组处理“破坏者”特别重要。这里说的破坏者倒不是恶意用户而是偏好极其鲜明、而且和其他人重叠度低的人。如果严格追求每次推荐都让他满意系统会严重受限。我的处理方式是“累计公平”不在单次推荐里强行做到公平而是跟踪一个滑动窗口比如过去10次组活动保证每个成员在这10次里至少被充分照顾到几次。这个做法的产品反馈很好因为它更贴近真实人群的相处方式——没有人要求每一次活动都完全称心如意重要的是长期下来没被亏待。5. 评估公平性的指标体系与离线实验5.1 不只看推荐精度还要算公平指标我见过不少团队做组推荐评估时只看RMSE、NDCG、HitRate这些指标都是全局均值性质的完全掩盖了组内差异。要评估公平性必须在标准推荐指标之外附加一套专门的指标。我常用的评估框架分四层满意度水平组平均满意度、每个成员的满意度分布。公平性水平组内满意度标准差、最低满意度、最大与最小满意度差值。覆盖水平偏好类别的命中率尤其关注非主流偏好类别的命中率。影响力水平通过反事实计算每个成员“如果ta不在组里推荐结果会变化多少”这个变化量就是影响力。然后看影响力的方差方差越大越不公平。线下实验最有效的方法是构造不同冲突强度的合成组。比如随机组、高冲突组、低冲突组、带冷启动用户的组。分别在每组上跑基线模型和公平性优化模型记录上述指标。我一般这样设计对比实验组类型基线平均聚合公平性优化方案低冲突组平均满意度4.2标准差0.3平均满意度4.1标准差0.2高冲突组平均满意度3.5标准差1.1平均满意度3.8标准差0.4含冷启动组平均满意度3.1标准差0.8平均满意度3.6标准差0.5从这张表能看出公平性优化在所有群体上都没有大幅降低平均满意度但在高冲突组和冷启动组里标准差显著下降。这个结果很有说服力——公平性优化的收益主要体现在最需要它的场景。5.2 离线指标的局限性离线实验能做很多事但也有一个致命盲区用户对公平性的主观感知是离线指标很难捕捉的。有些推荐结果在数学上很公平满意度标准差很小但由于推荐了一个用户极度讨厌的品类这种情况极少数但影响巨大用户可能会对系统产生强烈的不信任。这种“极端负面体验”在均值型和标准差型指标里体现不明显。所以我坚持在离线评估之后补充小规模用户调研。找几组真实用户让他们以小组形式体验推荐结果然后分别问“你觉得这个结果考虑了你的偏好吗”“你觉得这次推荐偏向谁”这类主观问题。虽然样本量不大但对算法调整的方向性判断非常有价值。5.3 线上监控什么指标线上系统不能实时计算复杂的公平性指标但我们可以监控几个代理指标组推荐结果被采纳后组内成员各自的后续转化率。如果长期只有某个成员持续转化说明推荐结果可能一直偏向那个人。差评/投诉标签中带有“这推荐不适合我”的比例。不同成员贡献的行为数据量的差异度。比如组推荐后哪个成员贡献了更多反馈数据这个信号能暗示ta是否被照顾到。6. 常见问题与排查技巧实录6.1 “公平但没人满意”的怪圈这是我在调参时最容易遇到的问题。工作流跑出来的推荐结果满意度标准差极小看起来完美公平但组平均满意度只有2.8分满分5分。后来定位到问题出在候选集生成阶段为了照顾每个成员我把Top-K候选的K设得太小比如每个成员只取5个并集后候选池里全是各个用户的个人最高分物品但这些物品之间毫无共性。最后聚合时选了一批“每个人都觉得还行但其实不是最优”的结果。解决办法是放大候选池并且增加一个“组共性分数”的加权项。每个物品的分数除了成员满意度外还要乘以一个“多少人会给这个物品打高分”的系数。如果只有一个人喜欢哪怕他特别喜欢优先级也不能太高。6.2 缺失成员数据导致公平指标失真有次线下实验里某个组只有两个人的数据完整第三个人的界面评分全是系统默认值。结果显示最低满意度极高、标准差极小看起来很漂亮。但后来才发现第三个人的分数全都是模型填充的中性分根本没有真实信号。这提醒我一件事公平性指标只能建立在实际反馈之上预测出来的“公平”可能是假象。排查经验是在评估脚本里一定要输出“每个成员的预测分数是否来自真实交互”的标记。如果某成员超过30%的分数都是冷启动补偿分则需要把这个组的公平性结果单独标注不能参与正常对比。6.3 用户对“轮流满意”的接受度“累计公平”策略有一个不好处理的地方如果组内某个人是个急性子上一次推荐没照顾到ta这次ta可能会提前抛弃系统。为了缓解这种风险我每次基于累计公平做出推荐时都会在结果展示里附带一句话“本次推荐偏好了A下次我们会侧重B。”我发现这种透明的沟通出乎意料地有效。用户并不一定要求每次推荐都符合自己的偏好但他们非常反感系统“偷偷摸摸”地忽略自己。在组推荐产品的交互设计上“解释”和“公平”几乎是并列重要的元素。我强烈建议任何做组推荐的产品在页面上让每个成员至少看到一个符合自己口味的标签或理由这样就算最终没推荐这个物品用户也觉得自己被尊重了。6.4 数据集和复现建议组推荐实验常用的一些公开数据集存在天然缺陷比如很多数据集只记录了组级评分而没有记录组内个体评分导致公平性研究无从下手。如果数据里没有个体评分至少要有每个成员的画像特征这样才能通过模拟生成个体偏好。我在做实验时经常自己构造基于评论文本的小规模数据集比如把餐馆点评里的用户态度拆成每个人对不同菜系的偏好强度再合成组。这种方法比直接用现成数据集更能控制冲突强度用来验证公平性算法是否有效非常顺手。最后分享一个我从失败里总结下来的经验做组推荐公平性先不要急着上复杂模型。先把最简单的最小分数、标准差、偏好命中率这些指标算出来可视化每个成员在推荐列表里的“被看见程度”。很多时候你不需要一个更聪明的排序函数只需要把损失函数里加上一点点公平性约束就能获得超预期的收益。而公平性约束的强度要放在线下用高冲突组反复压测压到边界情况仍然不崩才敢上线。组推荐的公平性很难因为它不只是算法问题还牵扯到人和人之间微妙的互动。但正因为难做出来以后的价值也更扎实——用户可能说不出你的算法好在哪里但他们会因为“这个系统居然真的考虑到了我的想法”而留下来。