AI应用实战课学习总结(7)聚类算法分析实战

大家好,我是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》(图书)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/874827.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

飞行器半实物联合仿真:技术解析与应用实践

1.背景介绍 当前,飞行器已成为大国博弈复杂场景中的重要角色,其技术经过多次实践不断发展,性能持续提升,整体效能显著增强。随着计算机技术和系统仿真技术的发展,利用计算机模拟和仿真构造一个虚拟飞行器的飞行控制系统已成为可能。这种仿真环境不仅可以定量描述飞行器在真…

函数计算百炼新春活动正式上线!三步赢取蛇年精美好礼

目前,人工智能(AI)正不断突破创作的边界,无论是文字、图像还是视频,AI都能以其卓越的效率和创造力为创作者提供助力。然而,如何让用户快速体验到此类 AI 创作应用,依然是一个需被解决的问题。为此,我们特别推出了基于函数计算百炼模型服务的 AI 创作方案,从剧本创作到…

春节如何用一款软件拯救你的电视?

应用简介 我们的电视(ourtv)是一款完全无广告的电视直播软件,清晰度可选择高清,超清,蓝光等播放。安装即可使用,再也不用费劲去找各种不稳定的直播源了。 “我们的电视”播放线路(直播源)是来自央视频,因此画质和稳定性还可以。不过随之而来的问题是跟央视频 App 不兼…

React Hook Form vs Redux Form

(公众号同步文章:https://mp.weixin.qq.com/s/IUAldltW9ywZ9kXB-NCDxA) Redux Form 想必 React 开发者们非常熟悉,在 Redux 流行的那几年里,Redux Form 是表单页面的标配,但是随着前端的发展与成熟,一些过渡设计的理念逐渐被大家抛弃,Redux 就是便是其中之一。 在大型的…

深度学习基础理论————CV中常用Backbone(Resnet/Unet/Vit系列/多模态系列等)以及代码

主要介绍在CV中常用的Backbone原理简易代码(代码以及原理经常更新),参考论文中的表格,对不同的任务所使用的backbone如下:针对上面内容分为两块内容:1、基于卷积神经网络的CV Backbone:1.Resnet系列;2.Unet系列等;2、基于Transformer的 CV Backbone:1.Vit系列等;3、在…

BUUCTF刷题-Web方向1~5wp

[极客大挑战 2019]EasySQL 一个sql注入登录框,直接万能密码登录拿到flag[极客大挑战 2019]Havefun 打开环境,没有任何信息,查看源码,发现这么一段代码GET方式传入一个cat请求,并且变量值为dog,即?cat=dog,得到flag[HCTF 2018]WarmUp 打开环境,是一张图片,查看源码发现…

99%的人不知道,桥接模式失败的真正原因是它!

前言 大家好,我是VoltCary 最近做一些基础设施的部署工作,以手上的kali linux作为集群的节点,因为用到的是虚拟机,且本机网络为无线wifi,现在希望虚拟机像正常独立主机存在网络中,因此不能像正常本地主机一样直接加入网段节点,虚拟机还需要进行网络的配置。 让虚拟机成为…

Orleans框架升级指南(3.6.5--8.0.0)

升级指南 官方升级指南 https://learn.microsoft.com/zh-cn/dotnet/orleans/migration-guide?source=recommendations主要涉及以下几个改动点ConfigureApplicationParts3.6.5版在Silo初始化的时候,会通过这个方法将指定目录下的Grain加载进来.ConfigureApplicationParts(part…

AtCoder Beginner Contest 386

A - Full House 2 题意给\(4\)个整数,问能否添加一个整数使得恰有\(3\)个整数\(a\)和\(2\)个整数\(b\)思路模拟代码点击查看代码 #include <bits/stdc++.h> using namespace std; #define int long long typedef pair<int, int> pii;const int mxn = 1e6 + 5;void…

Iceberg治理服务Amoro---配置Prometheus + Grafana看板

一、基础资料 1、mac安装Prometheus + Grafana https://www.cnblogs.com/robots2/p/18689540 2、配置文档 https://amoro.apache.org/docs/0.7.1/deployment/#configure-metric-reporter 3、Grafana配置内容 https://github.com/apache/amoro/blob/master/grafana/dashboard.js…

【Python】查找两个表格是否存在相同元素

其实就是字典的应用,把其中一个表格做成字典(将需要查询的元素设为字典的键),然后读取另一个表格逐个在字典中查找 表1:表2:import openpyxlwb1 = openpyxl.load_workbook(r"C:\Users\Lenovo\Desktop\表1.xlsx") ws1 = wb1["Sheet1"] wb2 = openpyxl…

git分支提交,提PR

远程分支xxx,临时分支tmp_xxx git checkout -b tmp_xxx   #创建并进入临时分支 git pull origin xxx      #更新本地code git add .          git commit -m "test pr" git push --set-upstream origin tmp_xxx #github上现在能看到这个临时…