关联规则商品推荐:Apriori/FP-Growth与支持度置信度提升度
发布时间:2026/9/18 15:53:43来源:尧图网络
简介这份PDF文档围绕电子商务场景下的商品推荐问题整理了一套基于关联规则的商品推荐系统模型面向数据挖掘初学者、电商技术研究者以及需要撰写相关论文或课程设计的高校学生。内容从推荐系统的基本作用讲起梳理了简单关联、时序关联与因果关联的分类并重点介绍Apriori算法、基于划分的算法和FP-树频集算法三种频繁项集挖掘思路随后给出从数据采集、预处理到规则库建立、模型引擎计算的完整推荐流程附有模型结构示意图与Apriori算法的伪代码描述。资源包仅含1个PDF文件约118KB篇幅精炼便于快速通读与重点查阅适合作为关联规则学习与电商推荐模型搭建的入门参考。目前已有102人学习读者可借此理清频繁项集、支持度与可信度等核心概念掌握将关联规则落地为推荐列表的整体思路。1. 关联规则做商品推荐为什么在小数据量下依然打得过深度模型一个日订单几千单的垂直电商算法同学上来就搭双塔召回训了两个月线上 CTR 还没跑过运营手工配的买了 A 的人还买了 B。这类场景里一瓶啤酒和一包纸尿裤之间的关系不需要 128 维向量去表达一条{啤酒} → {纸尿裤}、支持度 2.1%、置信度 34%、提升度 2.8 的规则就够用了。关联规则挖掘Association Rule Mining本质是在事务数据集里找出「同时出现频率显著高于随机」的项集组合再用置信度、提升度把它翻译成可执行、可解释、可运营干预的推荐理由。它不需要 GPU增量成本低冷启动商品只要被买过一次就有机会进规则SKU 在几千到几万、订单在百万级以下的业务里性价比极高。这里要讲的是怎么把一张订单明细表变成一个能上线的商品推荐模型支持度/置信度/提升度这三组参数怎么定规则怎么打分进召回通道以及线上最容易在哪儿翻车。2. 关联规则的三组核心参数支持度、置信度与提升度怎么定关联规则的全部可调空间几乎都压在三个指标上。很多人第一次跑apriori就卡在这里min_support 设 0.1 出来几十条规则设 0.001 出来几十万条然后随手挑了几条看着顺眼的就上线了。这三个参数各自控制的是不同阶段必须分开定不能一起拍脑袋。2.1 支持度决定候选集规模的第一个阀门支持度描述的是这个组合在所有订单里出现的概率分母是订单总数去重后的 transaction 数不是商品件数。support(X) 包含项集 X 的订单数 / 订单总数这个定义看起来简单但分母搞错的人非常多把明细行数当分母算出来的支持度会系统性偏小因为你把同一订单里的多行商品都算成了独立事务。正确做法是先按 order_id 聚合成一单一个商品集合。支持度的量纲直接决定候选集大小。假设有 5000 个活跃 SKU理论上的二元组合是 1250 万Apriori 靠反单调性剪枝把它压下来但压到什么程度完全取决于 min_support业务规模单量级min_support 建议区间预期频繁项集量级垂直小店日均 1 千单0.005 ~ 0.0210^3 ~ 10^4中型电商日均 1 万单0.001 ~ 0.00510^4 ~ 10^5大促期间全量日均 50 万单0.0002 ~ 0.00110^5 ~ 10^6我的习惯是先跑一遍支持度分布再回推阈值把商品对按共现次数排序看第 5000 对、第 20000 对分别落在哪个支持度上取那个点作为 min_support。这样得到的规则量是可预期的而不是跑完再删。注意min_support 每下降一半频繁项集数量通常增长 3~5 倍内存占用随之上升。用 8G 内存的机器跑 0.0005 以下的支持度之前先确认一下数据规模。2.2 置信度与提升度把买了又买和顺便买分开置信度回答的是买了 X 的人里有多少买了 Yconfidence(X → Y) support(X ∪ Y) / support(X)它有个致命缺陷如果 Y 本身是爆品买什么都可能顺手带上它置信度天然就高。比如矿泉水在 40% 的订单里出现那{任意商品} → {矿泉水}的置信度都不会低于 0.2这种规则没有任何推荐价值。提升度修的就是这个问题lift(X → Y) confidence(X → Y) / support(Y)lift 1 表示 X 和 Y 相互独立lift 1 表示正相关。经验上我一般把 lift 阈值卡在 1.2~1.5 之间lift 1.2过滤掉基本是噪声1.2 ≤ lift 3主力规则池稳定可解释lift ≥ 3强关联但数量少很多是同一系列商品手机和手机壳要人工看一遍除了 lift还有两个补充指标值得算上。Kulc 度量 (confidence(X→Y) confidence(Y→X)) / 2它对称适合判断双向关联强度不平衡比 IR |support(X) - support(Y)| / (support(X) support(Y) - support(X∪Y))IR 越接近 1 说明两个商品的热度差距越大这时候的高置信度往往是热门商品带出来的假象。把lift 1.3和IR 0.7组合起来过滤规则池会干净很多。2.3 用 Python 在订单表上跑通第一次频繁项集挖掘数据从订单明细表出发先把 order_id 和 item_id 两列抽出来聚合、编码、挖掘、过滤四步走。下面是能直接抄的最小可运行版本。import pandas as pd from mlxtend.preprocessing import TransactionEncoder from mlxtend.frequent_patterns import apriori, association_rules # 1. 读取订单明细只需两列 orders pd.read_csv(order_detail.csv)[[order_id, item_id]] # 2. 按订单聚合成事务集合单笔订单内商品去重 baskets orders.groupby(order_id)[item_id].apply( lambda s: sorted(set(s.astype(str))) ).tolist() # 3. One-Hot 编码得到 订单 x 商品 的布尔矩阵 te TransactionEncoder() te_array te.fit(baskets).transform(baskets) df pd.DataFrame(te_array, columnste.columns_) # 4. 频繁项集挖掘max_len 控制规则长度 freq apriori(df, min_support0.002, use_colnamesTrue, max_len3) print(f频繁项集数量: {len(freq)}) # 5. 生成规则并按提升度过滤 rules association_rules(freq, metriclift, min_threshold1.3) rules rules[rules[confidence] 0.15] rules rules[rules[support] 0.001] print(f过滤后规则数量: {len(rules)})逐行说明几个容易踩的点。第 2 步的set(s)不能省同一订单里同一个 SKU 买了 3 件只应算一次事务出现否则支持度会被重复计数扭曲。第 4 步的max_len3很关键不限制长度时Apriori 会一直挖到没有频繁项集为止四元、五元项集在几万 SKU 上的组合爆炸足以把内存打满推荐场景里三元规则已经很少用得上因为前件太长会导致覆盖用户太少。association_rules返回的 DataFrame 一定包含这几个字段后面接推荐系统全靠它们字段含义典型取值antecedents规则前件frozenset{A, B}consequents规则后件frozenset{C}support前件后件同时出现的订单占比0.0021confidence前件出现时后件出现的概率0.238lift相对随机共现的提升倍数2.41导出成 CSV 时记得把 frozenset 转成字符串不然下游读回来还要再解析一遍。我一般再加一列rule_id |.join(sorted(antecedents)) |.join(sorted(consequents))作为主键方便做规则的增量比对和版本管理。3. Apriori 与 FP-Growth 的实现选择从订单表到规则库模型跑通之后马上会遇到第二个问题换一批数据、换一个时间窗口同样一份代码要跑两三个小时。这时候该考虑换算法了。Apriori 和 FP-Growth 解决的是同一个问题但代价结构完全不同选错了会白白浪费机器。3.1 数据准备把订单流水转成事务矩阵进入算法之前清洗环节决定了最终规则的上限。几个常规动作剔除退款、取消、测试订单用一个order_status in (paid, shipped, completed)的白名单而不是黑名单剔除只买了一次的用户可选。这类用户的订单占总量可能到 40%保留会稀释支持度商品粒度归一到 SKU但在 SKU 极多时按类目 品牌上卷一层得到更深、更稳的规则时间窗口取最近 90 天大流量业务取 30 天因为商品生命周期在缩短清洗完之后把数据落成两列格式order_id, item_id每行是一个订单-商品对。这个中间态建议用 Parquet 存一方面列存读得快另一方面后续做增量更新时可以按日期分区只重跑最近 N 天的数据。3.2 Apriori 逐层剪枝的实现细节与性能拐点Apriori 的核心是反单调性如果一个项集是频繁的那么它的所有子集也是频繁的。反过来如果一个项集不频繁那么它的所有超集都不频繁。这条性质让算法可以按长度逐层推进扫描一次数据统计所有单项的支持度得到 L1由 L1 两两连接生成候选二元项集 C2再扫一次数据计数剪掉不满足 min_support 的得到 L2由 L2 生成 C3剪枝计数得到 L3重复直到某一层为空每一层都要完整扫描一遍数据这是它最贵的地方。在事务数为 100 万、频繁项集到 4 层时我实测过大概要扫 5~6 遍全表单机跑一次 40~90 分钟。优化的抓手有三个。第一是降事务数用采样或者缩短时间窗口第二是提高 min_support哪怕从 0.002 提到 0.003候选集可能就少一半第三是用更快的计数实现。mlxtend 的apriori内部已经用了 numpy 的布尔矩阵运算比纯 Python 循环快得多但它是单线程的。注意Apriori 生成的频繁项集结果里同一长度的项集是按支持度降序排的取 TopN 做规则时直接 head 就行不要再排序一遍。3.3 FP-Growth 建树与条件模式基什么时候值得换FP-Growth 换了个思路不生成候选集而是把数据压缩成一棵前缀树FP-Tree然后递归地在树上挖。建树过程分三步第一次扫描统计单项支持度按降序排列每个事务里的商品第二次扫描把每个事务按排序后的顺序插入树中共享前缀路径只记一次路径上的计数累加同时维护一个头指针表记录每个商品在树中出现的位置链。挖的时候从支持度最低的项开始沿着它的节点链往上找所有祖先路径得到条件模式基再在条件模式基上递归建树直到路径为空。换成 FP-Growth 只需要改一行from mlxtend.frequent_patterns import fpgrowth # 同样的 One-Hot 矩阵 df只扫两遍数据 freq fpgrowth(df, min_support0.002, use_colnamesTrue, max_len3)参数含义和 apriori 完全一致输出结构也一致所以下游代码不用动。区别在代价FP-Growth 只扫两遍数据建树之后全在内存里递归理论复杂度远低于 Apriori。那是不是无脑换不是。三个场景下 FP-Growth 反而更慢场景更优选择原因事务数万级、SKU 百级Apriori建树和递归开销大于收益min_support 很高 0.05Apriori候选集本身就小扫几遍无所谓min_support 极低 0.0005AprioriFP-Tree 分支爆炸内存扛不住min_support 低、SKU 中等FP-Growth典型的树形收益区间实操上我一般这样做拿一周数据分别用两个算法在 min_support 0.002、max_len 3 下跑一遍比一下耗时和内存选快的那一个写进调度。因为数据分布会变这不是一次性决策每隔一两个月值得重跑一次这个对比。4. 把规则接到推荐系统召回、排序与线上打分规则库挖出来只是半成品。{A, B} → {C}支持度 0.0021置信度 0.238提升度 2.41这种三元组直接丢给推荐接口会遇到两个问题同一个用户触发了多条规则、后件重复需要合分规则是按支持度排序的不是按业务收益排序的。这一章解决从规则库到推荐结果之间的那段路。4.1 规则库落库与增量更新策略规则表建议按这个结构建字段都来自association_rules的输出再补两个业务字段CREATE TABLE rec_assoc_rule ( rule_id VARCHAR(512) PRIMARY KEY, -- A|BC 形式天然去重 antecedent VARCHAR(512) NOT NULL, -- 前件多个用 | 分隔 consequent VARCHAR(128) NOT NULL, -- 后件目前只保留单商品 support DECIMAL(10,6) NOT NULL, confidence DECIMAL(10,6) NOT NULL, lift DECIMAL(10,6) NOT NULL, kulc DECIMAL(10,6), imbalance DECIMAL(10,6), item_cnt INT, -- 前件商品数2 或 3 version VARCHAR(16), -- 批次号如 20240610 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_antecedent (antecedent), KEY idx_consequent (consequent) );idx_antecedent这个索引是线上查询的生命线。推荐接口拿到用户购物车里的商品集合后要把集合的所有子集去规则表里查前件二元的 3 个商品会展开成 7 个子集含单项三元的会展开成 15 个。索引没建好这里就是全表扫。增量更新我一般用滑动窗口 全量重算的折中方案每天凌晨用最近 90 天数据全量跑一次生成新版本号写进临时表比对新旧版本的规则差异只把新增和指标变化超过 20% 的规则合并进主表旧版本保留 7 天用于回滚。为什么不纯增量因为支持度是会衰减的一条规则去年支持度 0.01今年可能已经降到 0.001纯增量只会不断往表里加规则永远不删。4.2 从规则到 TopN 推荐列表的打分公式同一个用户可能命中十几条规则后件还可能重复。这时候需要一个统一打分函数把候选排出来。我常用的形式是加权乘积import math from collections import defaultdict def score_candidates(hit_rules, alpha0.6, beta0.4, decay_lambda0.05): hit_rules: 命中的规则列表每条是 dict: {consequent: C, lift: 2.41, confidence: 0.238, age_days: 12} alpha/beta: lift 与 confidence 的权重两者之和不必为 1 decay_lambda: 时间衰减系数越大衰减越快 scores defaultdict(float) for r in hit_rules: # 时间衰减规则越老权重越低 decay math.exp(-decay_lambda * r[age_days]) # 核心打分项提升度与置信度的加权几何平均 base (r[lift] ** alpha) * (r[confidence] ** beta) scores[r[consequent]] base * decay # 按分数降序取 TopN ranked sorted(scores.items(), keylambda x: x[1], reverseTrue) return ranked[:20]几个参数的经济含义要说清楚。alpha 大于 beta 表示更看重这条规则比随机强多少适合新品曝光的场景beta 大于 alpha 表示更看重命中的概率有多大适合转化率优先的场景。我一般从 alpha0.6、beta0.4 起调在 AB 实验里对比 alpha0.5/0.7 两组。decay_lambda 控制规则的新鲜度。取 0.05 意味着一条 30 天前生成的规则权重衰减到 0.22接近失效取 0.01 则 30 天只衰减到 0.74。生鲜、服饰这类季节敏感品类建议 0.05~0.1标准品、图书可以放宽到 0.01。多个后件累加而不是取最大值这个设计是有意为之的一个商品被三条不同的规则同时指向说明它在多个商品组合里都是合理的下一步应该被推得更靠前。这是关联规则相比协同过滤的一个优势——它有天然的理由叠加。4.3 与协同过滤、深度模型融合的边界规则通道在任何推荐系统里都应该是召回层的一个通道而不是排序层的最终决策。典型架构是关联规则通道出 200 个候选协同过滤出 200 个热门兜底出 100 个合并去重后交给粗排和精排。如果非要在召回层就做融合最稳妥的做法是给每个通道一个可调的加权分权重用线上的 CTR 反推final_score w_rule * norm(rule_score) w_cf * norm(cf_score) w_pop * norm(pop_score)这里的 norm 建议用 min-max 归一化到 [0,1]因为三个通道的原始分数量纲完全不同规则分可能都在 0~5 之间协同过滤的余弦相似度在 0~1 之间。归一化之后权重才有意义。那什么时候该用深度模型替代规则我的判断标准是当用户侧特征和上下文特征时段、设备、来源渠道对推荐结果的影响超过商品共现关系时。规则模型只能回答买了 A 的人还买什么回答不了这个用户在晚上 10 点用手机访问时应该看什么。但即便如此规则通道通常也不需要下线——它是最好的冷启动兜底和新品曝光工具一个没有任何行为数据的新 SKU只要被买过几次就能通过规则获得曝光而深度模型要等它积累足够的样本才能学好它的 embedding。5. 规则质量验证与线上效果排错几个容易踩到的坑规则模型的坑不在算法在数据和评估口径。5.1 用时间切分验证避免规则穿越最常见的错误是用全量 90 天数据挖出规则再用同一批 90 天数据评估命中率。这时候的指标好看得离谱因为规则本来就是从这批数据里长出来的。正确做法是严格按时间切用第 1~60 天的订单挖规则用第 61~90 天的订单做测试集统计规则在测试集上的命中情况。评估指标至少看两个指标计算方式参考基线HitRate10测试集中前 10 个推荐里命中真实下一单商品的比例8% ~ 20%Coverage规则后件覆盖的 SKU 数 / 全部在售 SKU 数5% ~ 30%Rule Recall测试集订单中能被至少一条规则覆盖的比例15% ~ 40%Coverage 太低说明支持度卡太狠只有头部商品能进规则太低的同时 HitRate 很高说明规则池在自我循环推荐结果会越来越集中在少数商品上长期会损害长尾商品的曝光。5.2 三类高频翻车通用品、长尾品、季节性第一类是通用品污染。塑料袋、纸巾、矿泉水、赠品这些商品几乎每单都有它们出现在大量规则的右边把真正有价值的推荐挤下去了。处理方式是给商品算一个逆文档频率IDF式的权重或者直接建黑名单把出现在 30% 以上订单里的 SKU 全部从后件中剔除。这个动作对规则池的清理效果通常立竿见影规则数可能砍掉 40%但 CTR 会涨。第二类是长尾商品永远进不了规则。一个只卖了 20 次的 SKU在 100 万订单里支持度是 0.00002任何合理的阈值都捞不到它。解决办法是做粒度上卷把长尾 SKU 归到类目-品牌-价格带这个组合上用组合去挖规则再在组合内部按销量排序选具体商品。这样规则的前件是具体商品后件是类目-品牌-价格带推荐时再下一层。第三类是季节性失效。夏季挖出的防晒霜→晒后修复在冬天就是噪声。除了在 4.2 的打分里加时间衰减还可以按季度分别维护规则池接口根据当前日期选版本。这个做法成本不高但对服饰、食品、户外类目效果明显。5.3 上线前跑一个拦截率检查最后补一个我自己每次都会跑的检查拿最近 7 天的真实订单模拟用前 3 个商品推荐第 4 个的场景统计规则给出的推荐里有多少是用户已经买过的。这个比例叫已购拦截率超过 30% 就说明规则池里大量是同单共现而非跨单复购推荐列表里会塞满用户刚买完的东西。处理方式是在打分函数里给已购商品乘一个 0.1 的惩罚系数或者干脆在候选阶段就过滤掉近 30 天内已购的 SKU。这一步做完规则的推荐列表才算真正可用。本文还有配套的精品资源点击获取
网站建设高端定制企业官网