ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

餐饮数据挖掘建模实战:从客户分群到销量预测的全流程解析

餐饮数据挖掘建模实战:从客户分群到销量预测的全流程解析

前段时间帮一家连锁餐饮品牌梳理会员数据,运营负责人提的需求很朴素:“你能不能把顾客分成几类,我们好针对性地发券?”这句话听着简单,可真做起来才发现,整个项目最难的环节不是跑算法,而是先把“分几类、按什么分、分了之后怎么用”想清楚。这一步在数据挖掘流程里恰好对应挖掘建模,但它前面的业务理解一旦偷懒,后面所有模型都会变成自嗨。

餐饮行业的数据挖掘,从来不是“拿一份数据跑个聚类”那么简单。我接触过的项目基本都跑不出几个方向:客户分群、菜品关联、销量预测、流失预警、门店诊断。每种方向背后对应完全不同的算法思路,但共同点是,建模之前的业务定义和数据准备至少吃掉一半时间。这篇就以餐饮行业为背景,把挖掘建模环节的完整链路拆开讲,包括业务口径怎么对齐、特征怎么构造、典型算法怎么选参数、模型结果怎么落地,以及我踩过的几个坑。

1. 先厘清业务问题:餐饮建模的第一步不是选算法

餐饮行业的“挖掘建模”通常不会只做一件事。以我目前的经验看,需求基本落在五个方向上:

建模目标常用算法类型核心输出典型业务动作
客户分群聚类(K-Means、层次聚类)客户群体标签与画像分群制定营销策略、差异化发券
菜品关联关联规则(Apriori、FP-Growth)菜品组合规则套餐设计、菜单排版、推荐菜
销量预测回归、时间序列、树模型未来销量预测值订货备货、人力排班、促销评估
流失预警分类模型客户流失概率召回策略、权益触达
门店诊断聚类、多维统计门店类型和经营特征运营策略调整、新店选址参考

选算法之前,第一件事是把业务语言翻译成数据语言。这里最常见的坑是数据口径没对齐。口径这东西很琐碎,但会让模型结果完全跑偏。我举一个真实遇到过的例子:某连锁品牌有堂食、外卖、自提三种订单来源,系统里“收入”字段在堂食订单上表示实收金额,外卖订单上却是商品原价而不是平台结算价,如果不做统一处理,客户分群模型会把大量外卖顾客误判成高价值客户,因为他们的消费金额虚高。类似的口径问题还有门店开业日期不同、部分门店存在测试订单、同一菜品在不同时期有不同名称等。

另一个需要注意的点是,业务目标和模型目标经常是两回事。运营说“提升复购率”,这句话没法直接建模,需要拆解:复购率下降主要是哪类客户流失?是高价值客户复购间隔变长,还是低价值客户彻底不再来?只有把问题拆到“可以给模型输入、可以用数据衡量”的程度,建模才有意义。我习惯用一句话检验建模目标是否清晰:模型输出后,运营能做什么具体动作?比如“预测高价值客户未来30天的复购概率”,输出后运营能在第15天未下单时发专属券,这就是清晰目标;如果只是“分析客户价值”,没有对应动作,那就要先停下来对齐业务。

1.1 建模前必须盘点的三类数据

餐饮建模项目里的数据通常来自三块:POS交易数据、会员数据、门店经营数据。POS数据是建模的主料,包含订单时间、订单来源、菜品明细、实收金额;会员数据包含会员等级、注册时间、积分、优惠券使用记录;门店经营数据包含门店地址、面积、开业时间、人员配置、营业时段。多数情况下,这三份数据分别存在不同系统里,建模前要先做实体打通。最常遇到的情况是会员ID与POS订单里的客户标识对不上,一个用手机号,一个用内部ID,需要通过关联表做映射。

数据盘点的产出物是一张字段清单,至少要标明字段名、数据类型、 null 比例、枚举值分布、时间范围。我一般会在这一步顺手看一下数据的时间跨度,如果只有三个月,做客户分群还行,做销量预测就会很吃力,因为周期性和节日效应根本看不出来。

1.2 口径对齐的几个关键决策点

第一是订单来源口径。堂食、外卖、自提不仅仅意味着渠道不同,单价、品类结构、复购逻辑都不相同。建模时如果目标客户是全渠道客户,就要先定清楚“同一个客户在不同渠道算一个人还是多个人”。

第二是金额口径。实收金额、原价金额、平台结算金额差距可能很大,建议建立统一的“实收金额”作为消费能力的核心字段,其他字段保留作参考。

第三是时间口径。营业日期与自然日期不完全一致,餐饮门店通常有跨天营业(夜宵到凌晨)。如果直接按自然日汇总,凌晨订单会被划到前一天,导致周末效应被抹平。这部分我在做特征工程时一般额外生成一个“营业日”字段,规则可以简单定义为当日六点到次日六点算同一个营业日,具体按品类调。

2. 特征工程与数据清洗:建模前最花时间的“隐形工作”

很多刚接触挖掘建模的人会把注意力放在选算法、调参数上,但实际项目里最花时间的是数据清洗和特征构造。餐饮数据的脏,脏得各有特色。比如同一道菜在不同门店名称不统一,“招牌毛肚”和“极品毛肚”可能是同一个SKU;比如点单系统里存在大量加料、去葱、多冰等备注;再比如一台收银机故障期间的数据缺失。这些如果不处理,后面无论用什么算法都是垃圾进垃圾出。

我一般把清洗流程分成四步:

  1. 去重:订单号去重,同一订单重复落库的直接删掉。
  2. 缺失值处理:会员ID缺失的订单,先尝试通过手机号、设备号回填,回填不了的就单独标记为“匿名客群”,不要直接丢弃,匿名客群本身是有价值的。
  3. 异常值处理:金额为0、负数的订单,打折后单价异常低的菜品,需要核对是不是测试单或员工餐。
  4. 品类归并:把SKU级数据向上汇总到品类级,提高后续关联分析的可信度。

清洗之后才是特征工程。餐饮数据的特征可以分成几个层次:时间特征、客户特征、菜品特征、门店特征。

2.1 时间特征:从“几点卖得好”到“哪类节日影响大”

时间特征是餐饮建模里最容易被低估的一块。只给模型一个“日期”字段没有意义,要拆开用。星期几、是否周末、是否节假日、是否节假日前一天、是否农历节日、是否学校开学周、天气状况、温度区间,这些都可能显著影响销量。我在一个火锅项目里发现,下雨天的外卖订单会明显上升,但堂食会下降,如果只用星期维度建模,雨天波动就会被当成噪声。

构造时间特征时有个细节:节假日的影响不是当天而是前后几天。春节前三天外卖订单暴涨,正月初一到初五反而暴跌;国庆假期前两天的堂食高峰和假期中段也有明显差异。我通常会给每个日期打上“距最近节假日的天数”这类偏移特征,让模型自己学出节前节后的差异,而不是只用一个节假日标签。

2.2 客户特征:RFM不是万能的,但它是很好的起点

做客户分群时,RFM是绕不开的经典方法。R(最近一次消费时间间隔)、F(消费频率)、M(消费金额)三个字段对餐饮客户来说基本够用。计算R的时候要注意“最近一次消费”是否跨渠道,如果一个客户上周在堂食消费过,今天在外卖平台下单,R应该按最新一次消费算,而不是按渠道分开算。

另外,餐饮和电商有个明显区别:消费频次普遍更高,但单次金额更低。RFM里的F和M很容易被“只点一杯奶茶”的客户拉低。所以我在构造客户特征时,除了RFM还会加上:

  • 日均消费频次、周消费次数波动
  • 平均客单价、客单价变异系数
  • 菜品品类偏好(按消费金额占比算)
  • 常用下单时段(早餐、午餐、下午茶、晚餐、夜宵)
  • 优惠券使用率
  • 会员注册时长

这些特征加进去之后,聚类结果往往比只用RFM更符合业务直觉。

2.3 菜品特征:不要直接拿菜品名建模

菜品级别的特征要小心。同一菜品在不同门店的销量差异很大,部分门店的TOP10菜品和整体TOP10完全不同。直接对所有门店一起建模,模型很容易被大店数据淹没,小店的特征学不出来。

我常用的做法是先按门店品类维度做标准化:每个菜品的销量除以该门店同类目的平均销量,得到“相对热度”。比如某菜品在某门店的热度是1.8,表示它的销量是这家门店同类目的1.8倍。用相对热度做特征,可以让模型在不同体量的门店之间具备可比性。

2.4 时间序列切分:不要随机打乱数据

分类模型做训练集和测试集时,随机抽样问题不大,但销量预测、流失预警这类有时间依赖的模型,绝对不能随机打乱。随机打乱看起来模型效果很好,实际是因为模型偷看了“未来”。我见过有人用随机分割做销量预测,测试集的MAPE只有8%,换成时间切分后直接变成25%,差别巨大。

正确的做法是:按时间窗口切分,例如用前8个月做训练,第9个月做验证,第10到12个月做测试。如果需要做参数调优,可以用滚动时间窗交叉验证,而不是普通的K折交叉验证。

3. 三种典型餐饮建模场景的完整实现

接下来分别拆解客户分群、菜品关联、销量预测三个最常见的建模场景,给出可复现的实现思路和参数选择逻辑。

3.1 客户分群:RFM + K-Means,K值到底怎么定

客户分群我习惯用两步走:先算RFM和附加特征,再做标准化,然后跑K-Means。标准化非常重要,因为金额、频次、间隔的单位和量级完全不同,不标准化会让M特征主导距离计算,R和F基本失去作用。

import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score # 假设 df_rfm 已包含 customer_id, recency, frequency, monetary features = ["recency", "frequency", "monetary"] X = StandardScaler().fit_transform(df_rfm[features]) # 用轮廓系数辅助选K sil_scores = [] for k in range(2, 8): model = KMeans(n_clusters=k, n_init=20, random_state=42) labels = model.fit_predict(X) sil_scores.append(silhouette_score(X, labels))

轮廓系数不是越高越好,餐饮场景K值通常取3到5之间,因为运营团队能记住的群体数量有限。我在实际项目里很少直接看轮廓系数最高值选K,一般会结合群体规模分布来判断。某次选K=4时,最大群体占了总人数62%,运营反馈这种分群没有意义;改成K=5后,出现了“高价值活跃客”“沉睡高价值客”“价格敏感新客”“低频周边客”“高响应羊毛客”五个群体,规模分布更合理,营销策略也更好设计。

聚类完成后,要给每个群体打业务标签,不能只给“群体1、群体2”。我通常用每个群体的特征均值对比整体均值来命名,比如“客单价高出整体1.8倍、复购间隔28天以上”对应的是“高价值沉睡客”。给标签的过程需要业务人员一起参与,标签命名不是技术问题,是业务共识。

3.2 菜品关联:Apriori的阈值调整逻辑

菜品关联分析的目标是发现“点了这道菜的人还点了什么”。餐饮场景和超市购物篮场景有两个不同:订单里菜品数量通常较少,堂食订单可能只有5到10个菜;菜品存在明显的主菜副菜结构,单纯看支持度会让“米饭、饮料”这类高频低价值组合霸榜。

我用的工具是 Python 的 mlxtend 库,核心代码如下:

from mlxtend.frequent_patterns import apriori, association_rules # basket 是订单ID x 菜品 的0/1矩阵 frequent_itemsets = apriori(basket, min_support=0.02, use_colnames=True) rules = association_rules(frequent_itemsets, metric="lift", min_threshold=1.2) rules = rules[(rules["confidence"] >= 0.3) & (rules["lift"] >= 1.5)]

支持度min_support怎么设?要结合门店订单量来算。如果一个月订单只有3000单,支持度0.02意味着菜品至少出现在60单里,很多特色菜就被滤掉了;大型连锁仓库级分析可以把支持度降到0.005。我一般先跑一个低支持度看规则数量,再逐步调高,找到规则数量从“爆炸”到“精炼”的拐点。

置信度反映的是条件概率,提升度反映规则是否比随机组合更强。提升度低于1.2的规则没有意义,因为随便两两组合都可能有1.0左右的提升度。另外,餐饮场景要警惕“虚假关联”:冬天热销的羊肉类菜品会互相强关联,不是因为顾客喜欢搭配,而是季节因素导致的共同上涨。发现这种规则后,不要把“羊肉+萝卜”当作固定的套餐推荐,它只是季节效应。

3.3 销量预测:从简单时序到多特征回归的升级路线

销量预测可以有不同的切入点。刚入门的时候可以先做简单时序,比如门店过去四周的平均日销量作为预测,这个baseline虽然粗糙,但它决定了后面花多大力气做复杂模型。如果baseline的误差已经低于10%,那不一定需要上机器学习。

从实际项目角度看,比较稳妥的路线是:

  1. 先按“相同星期几的历史均值”做预测,这个baseline非常强,因为它天然包含了星期效应。
  2. 加节假日特征、天气特征、门店特征,用 LightGBM 或 XGBoost 这类树模型做回归。推荐用树模型而不是线性模型,因为菜品销量的非线性关系明显,比如温度超过30度后冰品销量指数级上涨,线性模型表达不了这种拐点。
  3. 最后对残差做诊断,看哪些门店、哪些菜品预测误差特别大,针对性地补特征或做分店模型。

训练时我用有代表性的特征组合,包括:历史7天/14天/28天销量、同星期几的历史均值、是否节假日、节假日偏移天数、温度、降雨量、门店商圈类型、门店开业的月份数。代码层面长这样:

import lightgbm as lgb train = lgb.Dataset(X_train, y_train) valid = lgb.Dataset(X_val, y_val) params = { "objective": "regression", "metric": "mae", "learning_rate": 0.05, "num_leaves": 31, "max_depth": 6, "verbose": -1 } model = lgb.train(params, train, num_boost_round=500, valid_sets=[valid])

这里有个经验值:num_leaves不要一开始就设127,餐饮销量数据通常噪声较大,太深的树很容易过拟合,我一般从31开始,用早停判断最佳迭代轮数。另外,目标函数不建议用平方损失MSE,餐饮销量的长尾特征明显,个别爆款菜品的销量会拉开平方误差,导致模型为了拟合爆款而牺牲正常菜品的精度。我用MAE或者MAPE更符合业务需求。

4. 餐饮场景下模型评估的特殊性

很多人把评估环节做成“看模型报告里那几个分数组”,这在餐饮场景会翻车。因为餐饮模型的结果最终要落到门店执行,评估指标必须和业务动作挂钩。

4.1 聚类模型的评估:内部指标只是参考

聚类模型的轮廓系数、Davies-Bouldin指数只能说明“群内紧凑、群间分离”的统计特性,不能说明“这个群运营是否能看懂”。我见过轮廓系数很高的聚类结果,其中一个群体同时包含了“高消费夜宵人群”和“高消费早餐人群”,运营完全没法针对群体设计统一策略。评估聚类结果时,我更看重每个群体在业务特征上的差异是否构成清晰故事:群体之间在客单价、品类偏好、时段偏好上的差距是否显著,群体命名能不能让人一眼看懂。

4.2 关联规则的评估:置信度之外必须看提升度

关联规则只看置信度会出问题。置信度高的规则可能来自高频单品自身的支撑,比如“奶茶→小料”置信度高达70%,是因为几乎所有奶茶订单都带了小料,这对菜单设计没有增量信息。提升度才是真正衡量“关联强度”的指标。提升度大于1表示有正向关联,大于1.5可以算强关联。实际项目里我一般把规则数量控制在运营能消化的范围内,一个月度分析周期内,真正值得落地的规则不会超过几十条,多于这个数说明阈值偏低、噪声偏多。

4.3 销量预测的评估:误差要换算成订货损失

预测模型常用MAPE或MAE评估,但业务方更关心的是“我按这个预测订货,浪费了多少,缺货损失了多少”。我在项目里会额外算两个业务指标:由于预测偏高导致的食材报废率,和由于预测偏低导致的缺货率。这两个指标可以换算成金额,直接和基线模型对比。曾有一个门店,模型MAPE看起来只比基线好了3个百分点,但换算成订货损失后,每周能省下约两百多块的食材浪费,同时减少两次缺货引起的顾客投诉,这时业务方就愿意上线了。

5. 从建模结果到门店落地:千万别只交付PPT

模型做出来只是第一步,离真正产生价值还有一段路。我吃过亏:一开始交付了厚厚一份分析报告,业务方看完说“挺好”,然后就没了下文。后来我改成在建模阶段就拉着业务一起定义落地动作,流程顺畅很多。

5.1 客户分群结果如何变成运营策略

分群结果要能直接对应到营销动作。比如“高价值沉睡客”群体,策略是每月1号发放限时满减券,唤醒周期设为30天;“价格敏感新客”群体,策略是首单立减,目标是转化为常规客户。每个群体最好明确一条触达通道、一种权益类型、一个触达频率。如果运营反馈某个群体不知道发什么券,说明群体的业务含义还不够清晰,回去重新看特征。

5.2 关联规则如何指导菜单和套餐设计

关联规则落地有三个场景:套餐设计、菜单排版、推荐算法。规则“毛肚+虾滑”提升度高且毛利都高,可以做成双人套餐;规则“儿童餐+小玩具”提升度高,可以联动营销活动。菜单排版也有讲究:一个菜品在菜单上的位置和它的关联菜品是否相邻,会影响点单概率。这类调整需要通过A/B测试验证,而不是做完规则就宣布成功。

5.3 预测模型如何与订货流程衔接

销量预测模型真正的价值在订货衔接。我建议把预测结果按“置信区间”而不是单一数值输出。门店店长拿到“未来三天猪肚鸡锅预测销量120份,80%置信区间100到140份”,可以结合自己的经验做判断,而不是被一个死数字绑住。预测结果还要能自动导出到订货系统,减少人工录入。如果预测结果需要店长每天手动抄到Excel,再传到订货系统,落地阻力会大很多。

6. 这些坑我替你踩过:餐饮数据建模的注意事项

最后集中写几个餐饮数据建模中非常典型、但通常要到现场才会踩到的坑。

6.1 菜品改名导致模型“失忆”

某连锁品牌在夏季统一更新了一次菜单,十几道菜品改了名称,其中“酸梅汤”改叫“冰镇酸梅饮”。做销量预测时,如果不做新旧名称映射,模型会把这个在新菜单上线后销量从0开始重新学习的菜品当成新品,完全浪费了过去两年的历史销量数据。我习惯在建模前先核对菜品字典表,凡是改名或合并的SKU都要映射到统一编码,必要时用编码做主键而不是菜名。

6.2 堂食和外卖混在一起建模的干扰

外卖订单和堂食订单的客单价、菜品结构、时间分布差异非常大。外卖订单平均客单价常常不到堂食的一半,且集中在午餐时段;堂食订单的桌均消费更高,晚餐和周末占比更大。如果混在一起做客户分群,很可能分出来一个“外卖活跃客”,但这类客户和“高价值熟客”的运营策略完全不同。我的建议是:客户分群时把渠道维度作为特征引入,销量预测时分开建模或至少加入渠道占比特征。

6.3 节假日样本不均衡的过拟合

餐饮数据里节假日样本天然稀少。一年的春节、国庆、中秋加起来也就十几天,如果模型把节假日当成普通日子预测,误差必然大;但把节假日样本重复加权训练,又容易过拟合到单一年份的节日表现。我的处理办法是保留节假日特征但不强行加权,并且对预测结果做人工校正规则:比如去年春节下雨导致外卖猛增,今年春节不下雨,就不能机械照搬去年同期数字。这类校正规则要沉淀成门店SOP,而不是改模型参数。

6.4 模型上线后的监控节奏

模型不是上线就结束。餐饮有明显的季节性,菜品生命周期短的只有一两个月,模型上线后需要持续监控特征分布和预测误差。我一般设置每周一次的误差汇总、每月一次的特征分布漂移检查。如果发现某一类菜品的预测误差持续偏高,先不要着急重新训练,检查是不是菜单、价格、竞争环境有什么变化。多数情况下,预测失灵不是模型坏了,而是业务环境变了。

最后再分享一个我自己的习惯:每次建模项目,我都会把最终结果用一张最简单的表讲给店长听。这张表不写任何算法名词,只写“你的门店哪类顾客在流失、哪几道菜该进多少货、哪几个菜品适合搭配卖”。模型再复杂,到了门店这一步都必须变成听得懂、能执行的动作。餐饮数据挖掘的建模环节,说到底是给业务提供决策依据,而不是给报告库增添一份文档。希望这篇能帮正在做相关项目的人少走一点弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进