K-Means 浅尝

去年团队想利用公司的智能机器人搭建智能客服,第一步就是需要收集整理线上用户反馈数据,对数据分类后作为机器人的训练数据,因此也有机会在业务中简单实践聚类算法。

K - Means 是聚类问题中最为常用的算法之一,原理相对简单,也成为我们首先尝试的方法。

K - Means

K - Means 的核心思想是”物以类聚,人以群分“,算法本身是把 N 个多维的观察点,通过不断的迭代计算,更加合理的划分到 K 个集合中。

算法大致描述如下:

  1. 初始化计算模型,确定K值,表示将多维观察点划分为K个集合
  2. K个集合选择初始质心
  3. 计算每个观察点到 K个质心的距离,将观察点划分到距离最近的集合里
  4. K个集合选择新的质心,以获得更好的聚类效果
  5. 重复步骤 3 ~ 步骤 5,直到集合的质点趋于稳定,或达到最大迭代次数

结合业务需求,在过程中有两个特征需要关注:

  • K值。该值决定了最终反馈问题被分成几类,而在业务场景下,无法很明确的推断出K,需要不断测试找到最佳的值
  • 多维观察点。我们的原始数据是用户反馈问题,其本质是文本内容,需要找到合适的方式,将文本转化为多维向量

TF - IDF

我们有提到,要使用K - Means 对反馈问题进行聚类,就需要找到一个方法把文本转化为多维向量,这个过程就是文本特征向量化,够抽取文本中的特征,并以多维向量的方式描述文本内容。关于文本特征向量化的方法有很多,我们采用的是比较简单的TF - IDF。

TF - IDF (Term Frequency – Inverse Document Frequency),其中 TF 是词频,描述词汇在文档中出现的评率;IDF 是逆文本频率指数,描述一个词语的区分能力和重要性,词语在不同文档中出现的越少,IDF 值越大,重要性越高。

问题聚类

基于 K - Means 和 TF - IDF,我们对反馈问题进行聚类。代码比较简单,大致逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 计算词频
xls_question = []
for each in xls_result:
    xls_question.append(each.question)
count_vectorizer = CountVectorizer(tokenizer=cut_without_stop_word)
doc_features = count_vectorizer.fit_transform(xls_question)

# tf-idf
tfidf_transformer = TfidfTransformer()
word_tfidf = tfidf_transformer.fit_transform(doc_features)

# k-means
cluster_count = int(sys.argv[1])
max_iteration = int(sys.argv[2])
n_init = int(sys.argv[3])
km_cluster = KMeans(n_clusters=cluster_count, max_iter=max_iteration, n_init=n_init)
cluster_result = km_cluster.fit_predict(word_tfidf)

通过不断的对K值进行调整,同时不断优化训练数据集,最终达到的效果如下:

红框的上下是得到的两个不同分类,分类内的问题都是相似的。

从最终效果上来看,大部分分类内的问题都是有关联的,但是也存在问题:

(1)部分分类也呈现出问题之间无太大关联的情况,原因可能是K值偏大,或是受到切词算法、文本特征向量化算法本身的缺陷导致。

(2)部分分类内部问题高度相似,但也掺杂部分无关问题,原因是无关问题中包含了部分关键字,和当前分类高度契合,所以被划入进来。

分类主题

找到了分类,还有一个问题,是怎么得出这个分类的主题。

想到的方法也很简单,就是找到这个分类中每个问题的关键词,然后看哪个关键词更多。那么问题就转变为如何找到关键词,我的方法依旧是使用 TF - IDF,基于当前分类,找到每个问题中 TF - IDF 最高的 3 ~ 5 个词,结果如下:

基本可以总结出分类中的关键信息,截图中的分类主题是“找回密码”。同时,从图中也可以发现一些问题,就是主题关键词中,出现了“怎么”,该次可能也是出现 TF - IDF 值较高的词汇,针对这个问题,也有对应的方法可以解决:

(1)通过无用词过滤,把常见却无效的词过滤掉,避免这类常见词因 TF - IDF 值而影响整体结果。

(2)再进行一次 K - Means ,将找到的关键词进行再进行一次聚类,只分为2个类,找到其中分值最高的一个分类作为主题关键词

为什么这么做呢,我有对分类里找到的关键词得分进行观察,发现从分值上可以看出一批是高分,一批是低分,即高关联词汇组和低关联词汇组,所以想再做一次分组,来找出高分词汇。

存在的问题

基于上面的方法,我们完成了反馈问题的聚类和主题分析,但是从最终结果来看,其实问题还是很多的:

  • 数据清洗

在整个过程中,耗费时间最多的是数据清洗,需要把反馈问题中的无效数据进行过滤,包括特殊字符、表情等。同时,出于安全与合规的考虑,需要移除反馈问题中的人名、手机号、地理位置等等。

  • 切词

在使用 TF - IDF 之前,需要对反馈问题进行分词,我使用的是开源的分词库jieba,可以实现基础的分词,但是分词结果并不能直接使用。

首先,分词结果中包含停用词,该类词汇本身并无含义,需要过滤掉,以避免停用词影响分析。其次,各个产品都有其自身的关键词,例如对于腾讯而言,QQ、阅文、微视、腾讯云是关键词。

  • K

在训练过程中,K值也是反复测试调整过多轮的。如果K值过大,则可能导致相同含义的问题被氛围了多组;如果K值过小,则可能分类里的不相关问题会偏多。