当前位置:   article > 正文

独家 | 用LLM实现客户细分(下篇)

基于llm的聚类方案

e44eaaa2c4a175f6fa8a077758c9996a.png

  1. 作者:Damian Gil翻译:陈之炎校对:赵茹萱
  2. 本文约5000字,建议阅读10分钟
  3. 采用LLM解锁高级的客户细分技术,并使用这些高端技术改进聚类模型。

内容列表

  • 概述(上篇)

  • 数据(上篇)

  • 方法1:Kmeans(上篇)

  • 方法2:K-Prototype

  • 方法3:LLM + Kmeans

  • 结论

引言

实践中可以采用多种方式处理客户细分项目。在上篇中,我们为您介绍了第一种方法:Kmeans,在下篇中,我们将为您介绍后两种方法,帮助您更快成为高级数据科学家(DS)的读者。

方法2:K-Prototype

原始的数据集中包括分类变量和数值变量,但Skelearn提供的Kmeans算法不接受分类变量,从而需要彻底修改原始数据集。

幸运的是,你已经读到我的帖子,多亏了ZHEXUE HUANG和他的文章“用分类值聚类大数据集的k-Means算法扩展”,包含接受分类变量进行聚类的算法,这一算法称为K-Prototype算法,在Prince中可以提供这一算法。

具体流程与前一小节相同,为了突出本博的内容,来讲解一下最为有趣的部分。记住,可以通过这里访问 Jupyter笔记本。

预处理

因为存在数值变量,所以必须对它们做一定的修正,建议所有数值变量具有相似的尺度,分布尽可能接近高斯分布。创建模型数据集代码如下:

  1. pipe = Pipeline([('scaler', PowerTransformer())])
  2. df_aux = pd.DataFrame(pipe_fit.fit_transform(df_no_outliers[["age", "balance"]] ), columns = ["age", "balance"])
  3. df_no_outliers_norm = df_no_outliers.copy()
  4. # Replace age and balance columns by preprocessed values
  5. df_no_outliers_norm = df_no_outliers_norm.drop(["age", "balance"], axis = 1)
  6. df_no_outliers_norm["age"] = df_aux["age"].values
  7. df_no_outliers_norm["balance"] = df_aux["balance"].values
  8. df_no_outliers_norm

e087f1d9d3ac23adf1d6b764c247263a.png


异常值

因为用于离群值检测(ECOD)的方法只接受数值变量,因此必须执行与kmeans方法相同的转换。应用离群值检测模型,它将提示需要删除哪些行,最后留下用作K-Prototype模型输入的数据集:

67e35c2cc42ba648d93da4f3ec6e0374.png


建模

创建模型以找到最优的k,为此,使用了Elbow方法和如下代码:

  1. # Choose optimal K using Elbow method
  2. from kmodes.kprototypes import KPrototypes
  3. from plotnine import *
  4. import plotnine
  5. cost = []
  6. range_ =range(2, 15)
  7. for cluster in range_:
  8. kprototype = KPrototypes(n_jobs = -1, n_clusters = cluster, init = 'Huang', random_state = 0)
  9. kprototype.fit_predict(df_no_outliers, categorical = categorical_columns_index)
  10. cost.append(kprototype.cost_)
  11. print('Cluster initiation: {}'.format(cluster))
  12. # Converting the results into a dataframe and plotting them
  13. df_cost = pd.DataFrame({'Cluster':range_, 'Cost':cost})
  14. # Data viz
  15. plotnine.options.figure_size = (8, 4.8)
  16. (
  17. ggplot(data = df_cost)+
  18. geom_line(aes(x ='Cluster',
  19. y ='Cost'))+
  20. geom_point(aes(x ='Cluster',
  21. y ='Cost'))+
  22. geom_label(aes(x ='Cluster',
  23. y ='Cost',
  24. label ='Cluster'),
  25. size =10,
  26. nudge_y =1000) +
  27. labs(title ='Optimal number of cluster with Elbow Method')+
  28. xlab('Number of Clusters k')+
  29. ylab('Cost')+
  30. theme_minimal()
  31. )

输出:

d4419a3d143b1a749f14b8522059cd7a.png

不同聚类数量的Elbow分值(图片由作者提供)

可以看出,最优的选择是K=5。

切记,运行该算法的时间比普通算法要长一些,运行本算法需要86分钟。

06d3a6ebef21eda0b9897376b7a0e8f0.png

好了,现在已经得出了聚类的数量,接下来只需要创建模型:

  1. # We get the index of categorical columns
  2. numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
  3. categorical_columns = df_no_outliers_norm.select_dtypes(exclude=numerics).columns
  4. print(categorical_columns)
  5. categorical_columns_index = [df_no_outliers_norm.columns.get_loc(col)for col in categorical_columns]
  6. # Create the model
  7. cluster_num =5
  8. kprototype = KPrototypes(n_jobs = -1, n_clusters = cluster_num, init = 'Huang', random_state = 0)
  9. kprototype.fit(df_no_outliers_norm, categorical = categorical_columns_index)
  10. clusters = kprototype.predict(df_no_outliers , categorical = categorical_columns_index)
  11. print(clusters) " -> array([3, 1, 1, ..., 1, 1, 2], dtype=uint16)"

得出模型和预测结果之后,再来对它进行评估。

评估

正如前文所述,可以应用多种可视化方法来直观地了解模型性能,然而,PCA方法和t-SNE不接受分类变量。不要担心,Prince库包含了MCA(多重对应分析)方法,它可以接受混合数据集。我鼓励您访问该库的Github,它包含几个非常有用的方法,见以下图片:

74b072de76b814dbf45553017bda3933.png

不同种类的降维方法(图片由作者和 Prince文档提供)

应用MCA来降维,并实现图形表示。为此,使用下述代码:

  1. from prince import MCA
  2. def get_MCA_3d(df, predict):
  3. mca = MCA(n_components =3, n_iter = 100, random_state = 101)
  4. mca_3d_df = mca.fit_transform(df)
  5. mca_3d_df.columns = ["comp1", "comp2", "comp3"]
  6. mca_3d_df["cluster"] = predict
  7. return mca, mca_3d_df
  8. def get_MCA_2d(df, predict):
  9. mca = MCA(n_components =2, n_iter = 100, random_state = 101)
  10. mca_2d_df = mca.fit_transform(df)
  11. mca_2d_df.columns = ["comp1", "comp2"]
  12. mca_2d_df["cluster"] = predict
  13. return mca, mca_2d_df
  14. "-------------------------------------------------------------------"
  15. mca_3d, mca_3d_df = get_MCA_3d(df_no_outliers_norm, clusters)

切记,如果想100%按部就班实现,可以考虑用Jupyter笔记本。

名为mca_3d_df的数据集包含以下信息:

1792167f6102aa33f0caf8eea824d517.png

使用MCA方法降维后做的图:

9311ec6be36eb6c283c28553a6aca2ba.png

模型创建的MCA空间和聚类(图片由作者提供)

哇,它看起来不太好…无法区分不同的聚类,可以说,这个模型还不够好,对吧?

但还是希望你说:

“嘿,达米安,别跑得这么快!”!你看过MCA三个组件的离散度吗?”

事实上,应该看看前3个组件的离散度之后才可以得出结论。利用MCA方法可以以一种非常简单的方式获取到这些值:

f9365b36fb640cd0609ec735ce4028f7.png

fb996be5c411fad76506e0deda183e0b.png

啊,得出了非常有趣的结果,在数据集上得到的离散度为零。

换句话说,无法通过MCA提供的降维信息中得出明确的结论。

通过展示这些结果,我试图给出一个真实数据项目的例子。虽然并不总是能获得好的结果,但一个好的数据科学家应该知道如何找到真实原因。

还剩最后一个选项,可以直观地确定由K-Prototype方法创建的模型是否合适,非常简单:

1. 将主成分分析(PCA)应用于数据集的预处理,将分类变量转换为数值变量;

2. 获得PCA的组成成分;

3. 使用PCA组件,如轴和点的颜色来预测K-Prototype模型。

注意,PCA提供的组件与方法1: Kmeans相同,因为数据帧是相同的。

来看能得出什么…

ecbad69784f797d26b957c783d19fb81.gif

模型创建的PCA空间和聚类(图片由作者提供)

看起来它还不错,它与Kmeans方法获得的结果相似。

最后,得到了聚类的平均值和各个变量的重要性占比:

b05bdab030dcb93314e28879420b1ad7.png

模型中变量的重要性占比,该表列出频度最高的聚类(图片由作者提供)

权重最大的变量是数值变量,根据这两个特征足以区分不同的聚类。

简而言之,可以说已经得出了与Kmeans相似的结果。

方法3: LLM + Kmeans

这种组合功能相当强大,可以明显改善得到的结果。言归正传。

LLM无法直接理解书面文本,需要对模型的输入进行转换。为此,实施了句子嵌入,将文本转换为数字向量。下面的图片可以说明这一想法:

259d6de4f742ee99dcdb226594f8f751.png

嵌入和相似度的概念(图片由作者提供)

这种编码是智能化的,也就是说,包含相似语义的短语将有一个更相似的向量。请参见下图:

80a3650ebec41eb36770a8b3e95bccd3.png

嵌入和相似度的概念(图片由作者提供)

句子嵌入由专门的转换算法实现,可以选择转换算法数字向量的大小,这是关键所在:

由于嵌入创建的向量维度很大,可以更精准地看到数据中的细微变化。

因此,如果将信息量更加丰富的输入提供给Kmeans模型,它将返回更好的预测。这就是我们所追求的理念,以下是它的实现步骤:

1. 通过句子嵌入转换原始数据集;

2. 创建Kmeans模型;

3. 评估。

第一步是通过句子嵌入对信息进行编码,目的是获取每个客户的信息,并将其统一封装为包含所有特征的文本。这部分需要花费大量的计算时间。为此我创建了一个脚本来完成这个工作,调用embedding_creation.py,该脚本收集训练数据集中的值,并创建一个由嵌入提供的新数据集。这是该脚本的代码:

  1. import pandas as pd # dataframe manipulation
  2. import numpy as np # linear algebra
  3. from sentence_transformers import SentenceTransformer
  4. df = pd.read_csv("data/train.csv", sep = ";")
  5. # -------------------- First Step --------------------
  6. def compile_text(x):
  7. text =f"""Age: {x['age']},
  8. housing load:{x['housing']},
  9. Job:{x['job']},
  10. Marital:{x['marital']},
  11. Education:{x['education']},
  12. Default:{x['default']},
  13. Balance:{x['balance']},
  14. Personal loan:{x['loan']},
  15. contact:{x['contact']}
  16. """
  17. return text
  18. sentences = df.apply(lambda x: compile_text(x), axis=1).tolist()
  19. # -------------------- Second Step --------------------
  20. model = SentenceTransformer(r"sentence-transformers/paraphrase-MiniLM-L6-v2")
  21. output = model.encode(sentences=sentences,
  22. show_progress_bar=True,
  23. normalize_embeddings=True)
  24. df_embedding = pd.DataFrame(output)
  25. df_embedding

理解这一步非常重要的,按照以下步骤进行操作:

  • 第1步:为每一行创建文本,其中包含完整的客户/行信息,将它存储在一个python列表中,供以后使用,参见下面的图片。

09ea4c5135560878581fd7220b16f8f4.png

第一步的图形描述(图片由作者提供)

  • 第2步: 创建Transformer,使用存储在HuggingFace中的模型。该模型专门训练在句子层执行嵌入,与Bert模型不同,它在标记和单词层上的编码时只需要给出存储库地址,便可以调用模型。在本例中是“sentence-transformers/paraphrase-MiniLM-L6-v2”。由于Kmeans模型对输入的大小很敏感,所以需要归一化各个文本返回的数值向量,创建的向量的长度为384。利用创建的向量创建一个具有相同列数的数据帧。请参见下图:

99e160644ad20bc0bca4e1b66f871ad6.png

第二步的图形描述(图片由作者提供)

最后,从嵌入中获取到数据帧,它将成为Kmeans模型的输入。

f8d95b075271e7e2feba043c606a0d51.png

这一步非常有趣且至关重要,它创建了Kmeans模型的输入。

模型创建和评估过程与前文所述类似。为了不使帖子过长,将只显示每一步的结果。别担心,所有的代码都包含在一个叫做 embedding的Jupyter 笔记本中,可以自行复制结果。

此外,应用句子嵌入生成的数据集保存在一个csv文件中,该csv文件名称为embedding_train.csv。在Jupyter笔记本中,将看到数据集并创建基于它的模型。

  1. # Normal Dataset
  2. df = pd.read_csv("data/train.csv", sep = ";")
  3. df = df.iloc[:,0:8]
  4. # Embedding Dataset
  5. df_embedding = pd.read_csv("data/embedding_train.csv", sep = ",")


预处理

可以将嵌入视为预处理。


异常值

应用ECOD方法来检测异常值,并创建一个不包含这些数据类型的数据集。

  1. df_embedding_no_out.shape -> (40690, 384)
  2. df_embedding_with_out.shape -> (45211, 384)


建模

首先,利用 Elbow 方法,找出最优的聚类数量。

af8bcefef581e042dbbeb8612d65a223.png

在查看图表后,选择k=5作为聚类数量。

  1. n_clusters = 5
  2. clusters = KMeans(n_clusters=n_clusters, init ="k-means++").fit(df_embedding_no_out)
  3. print(clusters.inertia_)
  4. clusters_predict = clusters.predict(df_embedding_no_out)


评估

下一步是用k=5创建Kmeans模型,于是,可以获得如下指标:

  1. Davies bouldin score: 1.8095386826791042
  2. Calinski Score: 6419.447089002081
  3. Silhouette Score: 0.20360442824114108

获得的数值与前面案例中获得的数值非常相似,研究一下用主成分分析(PCA)得到的表示:

9ff6482e04c395201fb298ca7fab2f4a.png

模型创建的PCA空间和聚类(图片由作者提供)

这种聚类方法比传统的方法要好得多。不错,记住,考虑到PCA分析的前3个成分中所包含的离散度同样重要。根据以往经验,可以说,当它在50%左右(3D PCA)时,或多或少可以得出明确的结论。

8d59ffaab837581024a830d5c979d222.png

模型创建的PCA空间和聚类,同时显示PCA的前3个成分的离散度(图片由作者提供)

可以看出,前3种成分的累积离散度为40.44%,这是可以接受的,但并不十分理想。

通过修改3D表示中点的透明度,可以直观地看到聚类的紧凑程度,当这些点在某个空间中聚集时,可以观察到一个黑点。为了更好地理解这些内容,展示了以下gif:

plot_pca_3d(df_pca_3d, title ="PCA Space", opacity=0.2, width_line = 0.1)

8d242afac2fe873fabf4ec1c006d1b1d.png

模型创建的PCA空间和聚类(图片由作者提供)

在空间中有几个点,同一聚类种的点汇集到了一起,能很好地将它们与其他点区别开来,模型知道如何更好地识别它们。

即便如此,也可以看出不同的聚类并没有很好地区分出来(例如:聚类1和聚类3)。出于这个原因,进行了t-SNE分析,这是一种降维的方法,将复杂的多项式关系考虑进来。

6de73046559003622f8758199de2a94d.png

模型创建的t-SNE空间和聚类(图片由作者提供)

现在有了明显的改善,聚类之间没有重叠,点之间有明显的区别,采用降维方法后性能改进显著。来看看2D的对比:

962381aef3406806bfa109a1cad2b6d4.png

模型定义不同的降维方法后得到的不同聚类结果(图片由作者提供)

同样可以看到,t-SNE中的聚类比PCA聚类分离得更好。此外,这两种方法之间的差异要小于传统的Kmeans方法。

为了深入理解Kmeans模型依赖于哪些变量,我们做了同样的操作:创建一个分类模型(LGBMClassicher)并分析特征的重要程度。

bc83fed03cfc5bccbf281e26edc74879.png

模型中变量的重要程度(图片由作者提供)

可以看出模型首先是基于“婚姻”和“工作”这两个变量,另一方面,发现某些变量无法提供太多信息。实际应用中,应该在剔除无用变量的情况下创建模型的新版本。

Kmeans +嵌入模型为最优,因为它需要更少的变量便能够给出好的预测。真是好消息!

最后,以揭示最重要的内容作为结尾。

经理和企业对PCA、t-SNE或嵌入不感兴趣,他们想要知道的是,在这种情况下,客户的主要特征是什么。

为此,创建一个表,其中包含各个聚类的主要配置文件信息:

357deef8acd2d02245c76e6bc211374d.png

于是,发生了非常神奇的事情:最常见的职位是聚类3“管理人员”类,在他们身上,能够找到一种非常特殊的行为,单身经理更年轻,已婚的人更年长,离婚的人年龄更大。另一方面,可以找出不同聚类的存款余额,单身人士的平均存款余额高于离婚人士,已婚人士的平均存款余额相对更高。本博内容总结如下:

db7ef7d79931f049d3a80fa1683133d9.png

模型定义的不同客户图像(图片由作者提供)

结果与现实社会是一致的,同时它还揭示了非常具体的客户画像,这就是数据科学的魔力。

结论

结论如下:

ad906b5b999576a799eff6e351130e05.png

(图片由作者提供)

在真实的项目中,并非所有的策略都有效,为此必须用不同的工具,你必须利用资源来增值。可以清楚地看到,在LLM帮助下创建的模型会脱颖而出。

原文标题:Mastering Customer Segmentation with LLM

原文链接:https://towardsdatascience.com/mastering-customer-segmentation-with-llm-3d9008235f41

编辑:黄继彦

校对:龚力

译者简介

68a8c26fe4dea4adbba7cd954928a028.jpeg

陈之炎,北京交通大学通信与控制工程专业毕业,获得工学硕士学位,历任长城计算机软件与系统公司工程师,大唐微电子公司工程师,现任北京吾译超群科技有限公司技术支持。目前从事智能化翻译教学系统的运营和维护,在人工智能深度学习和自然语言处理(NLP)方面积累有一定的经验。业余时间喜爱翻译创作,翻译作品主要有:IEC-ISO 7816、伊拉克石油工程项目、新财税主义宣言等等,其中中译英作品“新财税主义宣言”在GLOBAL TIMES正式发表。能够利用业余时间加入到THU 数据派平台的翻译志愿者小组,希望能和大家一起交流分享,共同进步

翻译组招募信息

工作内容:需要一颗细致的心,将选取好的外文文章翻译成流畅的中文。如果你是数据科学/统计学/计算机类的留学生,或在海外从事相关工作,或对自己外语水平有信心的朋友欢迎加入翻译小组。

你能得到:定期的翻译培训提高志愿者的翻译水平,提高对于数据科学前沿的认知,海外的朋友可以和国内技术应用发展保持联系,THU数据派产学研的背景为志愿者带来好的发展机遇。

其他福利:来自于名企的数据科学工作者,北大清华以及海外等名校学生他们都将成为你在翻译小组的伙伴。

点击文末“阅读原文”加入数据派团队~

转载须知

如需转载,请在开篇显著位置注明作者和出处(转自:数据派ID:DatapiTHU),并在文章结尾放置数据派醒目二维码。有原创标识文章,请发送【文章名称-待授权公众号名称及ID】至联系邮箱,申请白名单授权并按要求编辑。

发布后请将链接反馈至联系邮箱(见下方)。未经许可的转载以及改编者,我们将依法追究其法律责任。

033cd0b0749b3d636d101319d3cdac22.png

点击“阅读原文”拥抱组织

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/803135
推荐阅读
相关标签
  

闽ICP备14008679号