大家好,我是Edison。
最近入坑黄佳老师的《AI应用实战课》,记录下我的学习之旅,也算是总结回顾。
今天是我们的第7站,一起了解下聚类算法基本概念 以及 通过聚类算法辅助用户画像的案例。
聚类基本介绍
聚类算法是将数据集中的对象分组成若干个簇,使得同一个簇中的对象之间的相似度较高,不同簇之间的对象相似度就较低。聚类算法可以帮助我们发现数据中的隐藏结构,可以用来给客户做分组、给文档做分组等等场景。
NOTE:聚类算法是目前我们这个系列接触到的第一个“无监督”机器学习算法。之前学习的回归和分类算法大部分都是“有监督”机器学习算法。
举个例子,电商平台可以利用聚类算法将用户分为不同的消费群体,然后针对不同群体推送个性化的广告和推荐商品。
聚类和分类的区别
聚类和分类最主要的区别有如下几点:
- 分类是监督学习,聚类是无监督学习
- 结果的含义
- 评估方法
对于第一点,如何判断是监督还是无监督学习,主要是看数据集有没有被打上标签。对于无监督学习,机器通常是在没有任何标签的前提下自发地进行分组。对于第二点,分类的结果通常是预先定义好的类别,而聚类的结果则是一个个的数据簇,这些数据簇本身的结构是自发地凑在一起的而非预先定义好的。例如,下图中的分类是预先定义好了Adults 和 Children两个类别,而聚类则是没有预先定义完全通过自发凑在一起的,算法的结果只会告诉你Cluster1 和 Cluster2。
对于第三点,分类的评估方法有很多成熟的指标,例如准确率、精确率、召回率和F1分数等。聚类的性能效果则不太好评估,只能通过数据的结构或者额外的信息或者做数据可视化来进行观察。
总结:分类是监督学习,用于预测数据的类别;聚类是无监督学习,用于发现数据的隐藏结构。
常见聚类算法
最常见的聚类算法有:
- K-均指(K-Means)
- 层次聚类(Hierarchical Clustering)
- 密度聚类(DBSCAN)=> 当然,还有很多没有列出来!
这里,Edison再次推荐像我一样的零基础小白可以学习B站博主“五分钟机器学习”的视频,它在基础篇通俗易懂地讲解了 线性回归、逻辑回归、K近邻、决策树、K-Means、SVM、随机森林等算法,在进阶篇中介绍了梯度下降算法,简洁易懂,适合快速扫盲,这里我就不多赘述了。
最后,如何选择聚类算法呢?早已经有大佬总结了这么一张图,你可以根据你数据集的分布形状,选择最合适的聚类算法,如下图所示:
电商用户画像聚类案例
问题背景:
-
某电商系统记录了过去12个月的订单数据
-
订单数据包括:用户ID、购买物品、金额、时间等
问题目标:
-
根据历史数据,给用户的消费能力做一个画像
NOTE:和我们之前的第5站做回归分析案例时使用的是同一个数据集,就让我们“一鱼多吃”吧。
用户画像是根据用户社会属性、生活习惯和消费行为等信息而抽象出的一个标签化的用户模型。构建用户画像的核心工作即是给用户贴“标签”,而标签是通过对用户信息分析而来的高度精炼的特征标识。
用户画像聚类代码实战
Step1 读取数据 及 数据预处理
import pandas as pd #导入Pandas df_sales = pd.read_csv('eshop-orders.csv') #载入数据 df_sales.head() #显示前5行数据
输出的数据展现成下面的样子:
同时,做一些预处理,清洗掉一些无用数据。
df_sales = df_sales.drop_duplicates() #删除重复的数据行 df_sales = df_sales.loc[df_sales['数量'] > 0] #清洗掉数量小于等于0的数据
计算每个订单的总价:
df_sales['总价'] = df_sales['数量'] * df_sales['单价'] #计算每单的总价 df_sales.head() #显示头几行数据
将用户提取出来形成单独的集合:
Step2 特征工程
这里和第5站一样,我们将原始订单数据转换为每一个用户的R、M、F值,R指Recency(用户的新近度,用来衡量用户是否在近期进行了消费),M指Money(用户共计消费了多少钱),F指Frequency(用户共计进行了多少次交易,即消费的频次)。
# Recency df_sales['消费日期'] = pd.to_datetime(df_sales['消费日期']) #转化日期格式 df_recent_buy = df_sales.groupby('用户码').消费日期.max().reset_index() #构建消费日期信息 df_recent_buy.columns = ['用户码','最近日期'] #设定字段名 df_recent_buy['R值'] = (df_recent_buy['最近日期'].max() - df_recent_buy['最近日期']).dt.days #计算最新日期与上次消费日期的天数 df_user = pd.merge(df_user, df_recent_buy[['用户码','R值']], on='用户码') #把上次消费距最新日期的天数(R值)合并至df_user结构 # Frequency df_frequency = df_sales.groupby('用户码').消费日期.count().reset_index() #计算每个用户消费次数,构建df_frequency对象 df_frequency.columns = ['用户码','F值'] #设定字段名称 df_user = pd.merge(df_user, df_frequency, on='用户码') #把消费频率整合至df_user结构 # Revenue df_revenue = df_sales.groupby('用户码').总价.sum().reset_index() #根据消费总额,构建df_revenue对象 df_revenue.columns = ['用户码','M值'] #设定字段名称 df_user = pd.merge(df_user, df_revenue, on='用户码') #把消费金额整合至df_user结构 df_user.head() #显示头几行数据
用户的RMF值如下图所示:
Step3 使用K-Means初次聚类找K值
这里使用了手肘法先粗略地做一下,目的并不是做聚类本身,而是通过循环9次聚类看看误差值,进而辅助选择一些K值。
import matplotlib.pyplot as plt #导入Matplotlib的pyplot模块 # 设置字体为SimHei,以正常显示中文标签 plt.rcParams["font.family"]=['SimHei'] plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示负号 plt.rcParams['axes.unicode_minus'] = Falsefrom sklearn.cluster import KMeans #导入KMeans模块 def show_elbow(df, ax, title): distance_list = [] K = range(1,9)for k in K:kmeans = KMeans(n_clusters=k, max_iter=100)kmeans = kmeans.fit(df)distance_list.append(kmeans.inertia_)ax.plot(K, distance_list, 'bx-')ax.set_xlabel('k')ax.set_ylabel('距离均方误差')ax.set_title(title)fig, axes = plt.subplots(1, 3, figsize=(18, 6))show_elbow(df_user[['R值']], axes[0], 'R值聚类K值手肘图') show_elbow(df_user[['F值']], axes[1], 'F值聚类K值手肘图') show_elbow(df_user[['M值']], axes[2], 'M值聚类K值手肘图')plt.tight_layout() plt.show()
显示的K值手肘图如下:
例如,这里最终显示的图中,2-4都是可以选择的K值。
关于手肘法:随着聚类数k的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高,那么误差平方和SSE自然会逐渐变小。并且,当k小于真实聚类数时,由于k的增大会大幅增加每个簇的聚合程度,故SSE的下降幅度会很大,而当k到达真实聚类数时,再增加k所得到的聚合程度回报会迅速变小,所以SSE的下降幅度会骤减,然后随着k值的继续增大而趋于平缓,也就是说SSE和k的关系图是一个手肘的形状,而这个肘部对应的k值就是数据的真实聚类数。当然,这也是该方法被称为手肘法的原因。
Step4 使用K-Means创建和训练模型
这里我们选择K=3给R值 和 K=4给M和F值:
from sklearn.cluster import KMeans #导入KMeans模块 kmeans_R = KMeans(n_clusters=3) #设定K=3 kmeans_F = KMeans(n_clusters=4) #设定K=4 kmeans_M = KMeans(n_clusters=4) #设定K=4 kmeans_R.fit(df_user[['R值']]) #拟合模型 kmeans_F.fit(df_user[['F值']]) #拟合模型 kmeans_M.fit(df_user[['M值']]) #拟合模型 df_user['R值层级'] = kmeans_R.predict(df_user[['R值']]) #通过聚类模型求出R值的层级 df_user['F值层级'] = kmeans_F.predict(df_user[['F值']]) #通过聚类模型求出F值的层级 df_user['M值层级'] = kmeans_M.predict(df_user[['M值']]) #通过聚类模型求出M值的层级 df_user.head() #显示头几行数据
聚类后的R、F、M值的层级值如下所示:
But,聚类后的层级自己是不知道哪个更重要或者优先级更高的,所以我们需要根据我们期望的重要性指标对其进行排序,才能满足我们的需求。
#定义一个order_cluster函数为聚类排序 def order_cluster(cluster_name, target_name,df,ascending=False):new_cluster_name = 'new_' + cluster_name #新的聚类名称df_new = df.groupby(cluster_name)[target_name].mean().reset_index() #按聚类结果分组,创建df_new对象df_new = df_new.sort_values(by=target_name,ascending=ascending).reset_index(drop=True) #排序df_new['index'] = df_new.index #创建索引字段df_new = pd.merge(df,df_new[[cluster_name,'index']], on=cluster_name) #基于聚类名称把df_new还原为df对象,并添加索引字段df_new = df_new.drop([cluster_name],axis=1) #删除聚类名称df_new = df_new.rename(columns={"index":cluster_name}) #将索引字段重命名为聚类名称字段return df_new #返回排序后的df_new对象 df_user = order_cluster('R值层级', 'R值', df_user, False) #调用簇排序函数,降序排列 df_user = order_cluster('F值层级', 'F值', df_user, True) #调用簇排序函数,升序排列 df_user = order_cluster('M值层级', 'M值', df_user, True) #调用簇排序函数,升序排列 df_user = df_user.sort_values(by='用户码',ascending=True).reset_index(drop=True) #根据用户码排序 df_user.head() #显示头几行数据
这里我们针对R值层级降序排列,针对F和M值层级升序排列:可以看到,如果三个层级的总分越高,那么对系统平台的价值也就越高。
Step5 可视化:为用户整体分组画像
现在为止,我们已经得到了RMF的层级的分数,可以根据总分进行一个分组,这里我们暂且将其定义为低价值(0<=总分<=2)、中价值(3<=总分<=4) 和 高价值(5<=总分<=8):
df_user['总分'] = df_user['R值层级'] + df_user['F值层级'] + df_user['M值层级'] #求出每个用户RFM总分 #在df_user对象中添加总体价值这个字段 df_user.loc[(df_user['总分']<=2) & (df_user['总分']>=0), '总体价值'] = '低价值' df_user.loc[(df_user['总分']<=4) & (df_user['总分']>=3), '总体价值'] = '中价值' df_user.loc[(df_user['总分']<=8) & (df_user['总分']>=5), '总体价值'] = '高价值' df_user #显示df_user
显示的分组如下所示:
然后,我们可以进行一个可视化,由于有RMF三个特征,因此可以画一个三维散点图展示:
# 画三维图像,将RMF值都显示出来 plt.figure(figsize=(6,6)) # 图片大小 ax = plt.subplot(111, projection='3d') # 坐标系 ax.scatter(df_user.query("总体价值 == '高价值'")['R值'], # 散点图 df_user.query("总体价值 == '高价值'")['F值'], df_user.query("总体价值 == '高价值'")['M值'], c='g',marker='*') ax.scatter(df_user.query("总体价值 == '中价值'")['R值'], df_user.query("总体价值 == '中价值'")['F值'], df_user.query("总体价值 == '中价值'")['M值'], marker=8) ax.scatter(df_user.query("总体价值 == '低价值'")['R值'], df_user.query("总体价值 == '低价值'")['F值'], df_user.query("总体价值 == '低价值'")['M值'], c='r') ax.set_xlabel('R值') # 坐标轴 ax.set_ylabel('F值') # 坐标轴 ax.set_zlabel('M值') # 坐标轴 plt.show() # 输出
得到的三维散点图如下所示:三种类别的用户使用了不同的颜色表示,绿色的为高价值用户的数据散点分布,可以看到其RMF值总分都是较高的。而蓝色的为中价值用户,他们大多都是“偏科“用户,即可能单个特征比较高,但是总分不高。而红色的低价值用户的数据分布说明,他们可能偶尔来一次购物就再也不来了。
小结
本文介绍了机器学习中的聚类场景问题,常用的聚类算法 以及 分类和聚类的简单对比,最后再次通过电商订单数据做用户画像的案例做了一次聚类实战,相信对你理解聚类应用应该有所帮助。
下一站,我们就将进入深度学习和Pytorch框架学习的部分了,这部分就留到2025春节之后再整理后和你分享吧。
推荐学习
黄佳,《AI应用实战课》(课程)
黄佳,《图解GPT:大模型是如何构建的》(图书)
黄佳,《动手做AI Agent》(图书)
作者:周旭龙
出处:https://edisonchou.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。