尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

KMeans实战:从电商行为数据到用户画像分群策略全流程

KMeans实战:从电商行为数据到用户画像分群策略全流程 接手这个项目的时候我其实挺有感触的。市面上聊用户画像的文章一抓一大把但绝大多数都停在“画PPT”的层面真正能把一个带标签的电商行为数据集从零开始做成一套可落地的分群策略并且把无监督学习的坑一个一个踩平的文章少之又少。所以这一篇实战我决定不绕弯子直接把当时跑这个项目时的完整链路拆给你看。这篇博文的核心就是用无监督学习里的KMeans家族算法对一个真实的电商用户行为数据集做用户画像构建。它解决的不仅仅是怎么调用一下KMeans然后画个散点图的问题而是帮你想清楚三件事第一用户特征怎么做才能让聚类结果有意义第二K值到底怎么选才不是拍脑袋第三聚类完成之后怎么把“簇”翻译成运营听得懂、能执行的策略。适合正在学机器学习、或者刚进入数据分析岗位、手里拿着数据却不知道怎么下手的同学参考。1. 数据读入之后先别急着建模把“数据体检”做扎实很多初学者拿到数据集的第一反应是直接from sklearn.cluster import KMeans然后就开始拟合。这个习惯非常危险尤其是做无监督学习的时候因为没有人告诉你“正确答案”长什么样所有的后续判断都建立在“你对你手里的数据有多了解”这个基础上。这一步做得越扎实后面聚类结果的解释性就越强。1.1 用info()和describe()摸清数据的底细项目用的是Pandas读入数据这个没什么好说的read_csv一行搞定。但真正关键的在于接下来这两行df.info() df.describe()info()能告诉你每一列的数据类型、非空值的数量以及整个DataFrame占用的内存情况。我当时跑完info()之后发现这个数据集的用户行为记录非常规整但有几个特征是类别型比如性别、年龄段这在后续特征工程里是要单独处理的不能直接丢给KMeans因为KMeans是基于欧氏距离的类别型变量的数值大小会被算法误解成“等级高低”。describe()则是用来查看数值型特征的分布情况。我重点关注了几个点均值和中位数的差距大不大、最大值是否异常、最小值是否符合业务常识。举个例子如果“消费金额”的最小值是负数那说明数据里可能存在退款记录没有做特殊处理直接拿去做特征聚合会把用户画像带偏。注意describe()默认只统计数值型列类别型列要用describe(includeobject)单独看。1.2 缺失值处理的两种思路别一删了之缺失值处理是这个阶段绕不开的话题。我看到很多教程里只要碰到isnull().sum()有值就直接dropna()这在数据量充足的时候可能没什么问题但如果缺失值出现在关键维度上比如年龄段缺失那你删掉的可是一整条用户行为链路。这个项目里的做法比较稳妥先按用户维度做聚合把多次行为压缩成一条用户记录再去看哪些字段缺失。如果缺失比例不高比如年龄段只有极少部分用户没填可以用众数填充如果缺失比例偏高比如超过了20%就要思考这个特征是否适合进入聚类模型。我当时没有在这个数据集上遭遇大规模的缺失值问题但为了流程的完整性还是保留了缺失值处理的环节。这个习惯大家一定要养成特征工程里的每一步都要有“为什么这么做”的逻辑支撑。2. 特征工程MBFG原则是如何把一个行为表变成画像表的如果说聚类算法是整个项目的骨架那特征工程就是血肉。特征做不好再牛的算法也聚类不出有价值的结果。这个项目里采用的是MBFG原则也就是从用户Member、行为Behavior、频率Frequency、金额Gross四个维度来构造特征。这套思路和RFM模型的逻辑内核是相通的但覆盖面更广因为它还把用户的基础属性和行为偏好也纳入了进来。2.1 构造用户基础属性与行为计数特征用户维度的特征包括注册时长、年龄段区间、性别。这里有一个细节我要专门说一下注册时长最好是换算成天或者周而不是直接用注册日期因为KMeans对数值尺度非常敏感datetime类型根本进不了模型。行为维度的特征则是围绕着“点击、加购、收藏、下单”这四类核心行为来做聚合。做法也很直接用groupby按用户分组然后分别对这四个行为字段求和。我当时是写了一个聚合函数一次性把四个行为的总数算出来避免多次groupby带来的性能损耗。behavior_feat df.groupby(user_id).agg( click_total(click, sum), cart_total(cart, sum), fav_total(favorite, sum), order_total(order, sum) ).reset_index()这里要注意一个陷阱如果原始数据里“点击”列是0和1表示“是否发生”那求和出来的是“次数”如果原始数据本身就是行为次数求和结果就要小心重复计算。跑特征工程之前一定要先确认每一行的粒度是什么是“一次行为一条记录”还是“一个用户一天一条汇总记录”。粒度不同聚合逻辑完全不同。2.2 消费间隔特征以窗口偏移为核心的细节设计频率维度的特征绝不仅仅是数一下用户活跃了多少天消费间隔的分布更能体现一个用户的忠诚度和消费习惯。这个项目里给出了一个非常值得学习的操作方法以5、15、30、60、180、365天为参考窗口通过窗口偏移累计计算。什么意思呢就是说在计算某个用户“最近5天内是否有消费”这个特征时不是只看当前数据里的最近5天而是通过时间窗口的偏移把用户历史上不同时间段的消费行为都纳入统计。这样构造出来的特征能反映出用户在不同周期内的活跃变化比单一的“消费总天数”信息量要大得多。具体实现上通常是先按用户分组把消费记录按时间排序然后用shift或者区间判断来生成不同窗口下的消费频次。这个步骤比较繁琐推荐把它封装成一个函数输入是用户的消费时间序列输出是各窗口的特征向量。2.3 nlargest提取Top N消费金额刻画用户价格偏好金额维度的特征除了常规的消费总额、最大单笔、平均单笔之外这个项目还加入了一个非常巧妙的角度用nlargest提取用户消费金额Top N的特征。top3 user_orders.groupby(user_id)[amount].apply( lambda x: x.nlargest(3).sum() if len(x) 3 else x.sum() )为什么Top N这个特征有价值因为它能反映用户的“价格上限”。一个用户可能平时买的东西都很便宜可一旦有大额消费占比就会很高。Top 3消费金额的总和与消费总额做对比就能看出这个用户是“稳定中等消费”还是“日常小额、偶尔大额”的类型这种区分对后续精细化运营非常有用。在特征融合这一步项目用的是merge逐步聚合的方式把用户基础信息、行为特征、频率特征、金额特征逐步merge到一张表里。我强烈建议不管你的特征有多少个都先按用户维度生成独立的特征表最后再统一merge不要在一个超大DataFrame里反复增删列否则到后期出了bug根本无从排查。3. KMeans建模之前先把数据的“口味”调成算法喜欢的特征工程做完之后还不能直接建模。KMeans的本质是最小化簇内平方和它对特征的尺度、量纲、分布形态都非常敏感。这个环节做过和没做过聚类结果天差地别。3.1 标准化和低频值处理不是走流程是保命这个项目里对类别特征的处理方式是先做低频值处理再做one-hot编码。什么叫低频值处理就是如果某个类别出现的频次极低比如“性别”字段里出现了一个占比不到0.1%的“未知”类别这个就需要处理掉避免one-hot编码之后产生一个几乎全零的稀疏列。低频类别对KMeans的簇中心计算几乎没有贡献却会增加特征维度得不偿失。处理完之后数值型特征需要做标准化。在这个项目里我使用了StandardScaler它把特征缩放为均值为0、标准差为1的标准正态分布。为什么不用MinMaxScaler因为KMeans用的是欧氏距离StandardScaler在这种场景下表现得更加稳定尤其是当特征存在离群值的时候MinMaxScaler会被离群值带偏把正常数据压缩到一个极小的区间。3.2 训练集与预测集要用同一个Scaler这个坑必须提前规避这里我要专门提一个非常容易踩的坑训练聚类模型时对特征矩阵做了标准化处理后面到了案例落地阶段要对新用户做预测时很多人直接把新用户的数据塞进模型然后发现预测结果怎么都不对。原因出在——新用户数据没有经过同一个Scaler的变换。正确的做法是在特征工程完成之后先fit这个Scaler到训练集上然后把训练集和后续需要预测的新数据都用同一个Scaler.transform()来处理。这个在sklearn里是fit_transform和transform的区别但很多人都会忽略这一步直接导致聚类模型在新数据上表现失衡。4. K值选择的完整决策链路手肘法、轮廓系数与交叉验证聚类数和KMeans的最终效果强相关它决定了你的用户画像最终会被分成几类。K值选大了分群细碎难以解读选小了群内差异过大策略无法精细化。这个项目用了三种方法组合判断K值我觉得这个组合拳打得很漂亮。4.1 手肘法怎么看“肘”手肘法看的是不同K值下模型的总簇内平方和Inertia。K从2跑到10把每个K值对应的model.inertia_记录下来画成折线图。理论上随着K增大簇内平方和会持续下降但下降的幅度会越来越小。那个“下降幅度骤减”的拐点就是所谓的“肘部”这个K值就是模型复杂度和解释力的一个平衡点。from sklearn.cluster import KMeans inertia_list [] for k in range(2, 11): model KMeans(n_clustersk, random_state42, n_init10) model.fit(X_scaled) inertia_list.append(model.inertia_)但手肘法有个问题拐点有时候不明显尤其当数据本身没有特别清晰的簇结构时折线图看起来像是平滑下降的曲线你很难判断到底哪里算“肘”。4.2 轮廓系数补足手肘法的盲区轮廓系数衡量的是样本与自身簇内样本的紧密程度、以及与邻近簇样本的分离程度取值在-1到1之间。数值越接近1说明聚类效果越好。我当时把K从2到10的轮廓系数也全部算了一遍综合手肘法和轮廓系数两个维度来定K值。如果你只看轮廓系数会倾向于选一个更大的K值因为簇越多每个簇内部的紧密度自然越高但这会牺牲业务解释性。所以正确答案不是“轮廓系数最大的K”而是在手肘法给出的候选区间内找一个业务上说得通的K值。4.3 交叉验证表如何终结选择困难这个项目里推荐用交叉验证的方法来辅助定K。思路不算复杂把训练集随机分成几份对每一个候选K值在每一份子集上训练在其余子集上验证最终把不同子集上的评估结果汇总成一张交叉验证表。我当时对比了几个候选K值的交叉验证结果最终确定了K5。原因不难理解K5时不同子集上的评估指标波动最小模型稳定性最高而且将簇数降为4时业务解释力明显减弱簇之间的特征区分度不高增加到6时有的簇样本量太小已经不具备整体运营价值。最后定在5既稳定又可解释。5. 聚类训练完成之后怎么把簇翻译成业务语言定好K值之后真正的挑战才刚开始。训练一个KMeans模型只需要几行代码但要解释清楚“为什么这个簇是这类人”需要回到原始特征里去逐项分析这也是整个项目里最需要耐心的一步。5.1 群体画像与显著特征的提取方法训练完之后我先做的事情是给每个簇打标签。具体做法是把每个簇内所有样本的特征做均值得到一个“簇中心画像”然后把这个画像和一个全局均值做对比看每个簇在哪些特征上显著高于或低于整体水平。比如某一个簇的“消费总额”显著高于全局均值“活跃天数”也显著偏高那这个簇就可以初步打上“高活跃高价值用户”的标签。再比如另一个簇的“点击总数”很高但“下单总数”很低那就说明这类用户习惯性逛但不下单可以定位为“观望型用户”。为了看清楚每个簇的特征差异我当时是把每个簇的特征均值整理成一张横向对比表再用热力图可视化。这么做有一个很直接的好处运营同事不需要懂聚类原理他们只看这张表就能理解每个群的特征。5.2 用KMeans的目标函数给特征重要性排序这个项目还提供了一个比较高级的分析角度——通过KMeans的目标函数直接进行特征重要性评估。原理其实不复杂KMeans优化的是样本到簇中心的距离而每一个特征对簇中心的贡献度是可以计算出来的。你可以把每个特征的方差贡献或者对簇间分离度的贡献提取出来做一个重要性排序。这个排序有什么实际价值它能让你知道在这个聚类模型里到底是哪些特征把用户区分开的。如果排名靠前的特征是消费类指标那这个画像体系的核心维度就是金额如果排名靠前的是行为频次类指标那说明你的分群更依赖活跃度。这决定了后续投放策略的方向价值非常大。6. 可视化散点图和雷达图怎么画才能辅助决策而非炫技聚类结果的可视化最忌讳的就是为了画图而画图。可视化在这个项目里的目标只有一个让看的人一眼就能理解“分成了哪几类、每一类的特点是什么、类与类之间的差异有多大”。6.1 二维散点图与PCA降维的正确打开方式原始特征空间通常维度很高无法直接画散点图所以需要使用PCA降维。项目里把数据降到2D和3D分别做了可视化。在2D散点图上不同簇用不同颜色标出可以看出簇之间的分离度。3D图用的是mpl_toolkits.mplot3d再加上plotly做了一个可以交互旋转的版本方便从不同角度观察簇的分布。这里有一个非常重要的操作细节PCA是用来可视化的不要拿它的结果去训练模型。很多新手犯的错误是先用PCA降到2维然后对降维后的数据做KMeans这会丢失大量信息得到的簇解释性极差。正确做法是在原始特征空间上做标准化、聚类、分析、评估最后为了可视化才做PCA。6.2 雷达图展示簇的画像差异雷达图非常适合展示“不同簇在多个特征维度上的对比”。我当时选择6到8个最核心的特征维度把每个簇在这些维度上的均值标准化到0到1之间然后画成重叠的雷达图。这样一张图就能直观地看出“高价值簇”在所有维度上都很饱满“观望型簇”则是在消费相关维度上塌陷。画雷达图的时候有个小技巧角度和标签的排列要讲究。把相关性高的指标放在相邻的角度上比如“消费总额”和“最大单笔”放一起这样画出来的多边形不会因为线条交叉而显得太乱。7. 案例落地新用户被分到某个簇之后策略应该怎么给很多项目做到聚类可视化就停了但真正体现商业价值的是最后一步——新用户来了怎么预测他属于哪个簇以及这个预测结果能支撑什么样的运营决策。这个项目用一个具体的案例把整条链路串了起来我认为这是全篇最精彩的一部分。7.1 新用户预测的完整链路预测的流程是这样先把新用户的原始行为数据按照训练集相同的特征工程逻辑处理成一条特征向量然后用之前fit好的Scaler做标准化最后调用训练好的KMeans模型的predict方法得到簇标签。new_user_scaled scaler.transform(new_user_feat) cluster_label kmeans.predict(new_user_scaled)看起来简单但有三个必须检查的环节你很容易踩坑第一新用户特征列的顺序必须与训练集完全一致merge的时候顺序错乱会导致预测结果完全失真。第二新用户如果之前完全没有消费记录那么“消费间隔”这类特征可能会是0或者缺失值处理逻辑要和训练集对齐。第三KMeans的predict实际上是在找最近的簇中心如果新用户的特征在很多特征上都填了0他大概率会被分到“低活跃”那个簇这在业务上是有道理的但你要能解释清楚为什么。7.2 针对新用户所在簇输出运营策略而非空话聚类结果的最终输出应该是“可执行的运营建议”。我当时针对每一个簇都梳理了一套对应的策略高活跃高价值簇维护好客情关系通过会员权益、专属客服、新品优先体验等方式提升复购率。高活跃低价值簇这类用户黏性高但客单价上不去需要做品类拓展和交叉销售引导他们购买高毛利商品。低活跃高价值簇唤醒价值极大可以用定向优惠券、爆款召回短信等方式激活成本可控。观望型用户簇用低门槛的体验活动来促动转化比如试用装、包邮券、新人立减。沉睡用户簇不建议大力投放选择成本最低的触达渠道做不定期的品牌告知就好。这套策略下来新用户一进入体系就能自动匹配策略配合自动化营销工具可以做到秒级响应。8. 实战中的避坑清单与效果优化方向最后这段写几个我在实际跑这个项目时踩过的坑。如果你能避开这些你的项目进展会顺利很多。第一个坑是KMeans的n_init参数。sklearn新版默认是n_init10如果你为了图快把这个参数调成1聚类结果会很不稳定每次跑出来的簇都不一样。一定要固定random_state并且保留足够的n_init次数。第二个坑是类别特征直接喂给KMeans。有些类别特征看起来是数字比如年龄段编码成了1、2、3、4但这个1和4之间没有真实的数值倍数关系直接进模型会引入虚假的距离关系。一定要one-hot编码或者换成序数编码后再考虑是否进入模型。第三个坑是通过合并多个groupby结果构造特征时索引和列名没有对齐。我用merge时吃过这个亏明明特征数据都是对的但最终结果里出现了大量的NaN检查了半天发现是某个中间表的user_id出现了重复导致笛卡尔积爆炸。建议每构造一个特征表就立刻检查维度、非空值和重复键。关于效果优化方向如果你后续想进一步提升聚类质量可以往两个方向走一是用GaussianMixture替代KMeans它可以输出样本属于每个簇的概率适合对软分类有需求的场景二是把画像特征从“统计值”升级到“序列值”比如用LSTM编码用户行为序列再做聚类这在数据量充足时会有更精细的分群效果。另外还有一个非常值得做的延伸把无监督聚类的分群结果当成一个“标签字段”喂给下游的有监督模型。比如预测用户下个月是否复购时把“所属簇”作为特征之一往往能显著提升模型效果。因为簇标签在很大程度上浓缩了用户的长期行为模式这是单纯的统计特征无法完全替代的信息。
返回列表