如何对字符串进行 KMeans 聚类

How to KMeans Cluster strings

我是数据工程师,对 ML 方法的了解有限,我正在尝试在开始编码之前获得一个我理解的好策略。我想做的是用键值对创建集群,键是一个名字,值是某种字符串列表。

目标是根据各个字符串列表的相似性创建名称的自然聚类。

import pandas as pd
df = pd.DataFrame({"name":['lion','leopard','racoon','possum'], 
"features":[
    ['mane', 'teeth', 'tail', 'carnivore'], 
    ['spots', 'teeth', 'tail', 'carnivore'], 
    ['stripes', 'teeth', 'omnivore', 'small'], 
    ['teeth', 'omnivore', 'small']]})
df

例如,在此数据集中,我期望的自然分组类似于 lion/leopard 和 racoon/possum,因为单词 teeth, tail and carnivore

的相似性

我已经做过的一种方法是比较一个条目,例如lion,遍历每个列表并将其与其他值进行比较,如果在另一个列表中找到一个值,则为其分配一个相似性分数。然而,我很想使用像 k-means 聚类算法这样的东西来学习如何主要使用它,而且我认为/希望它会提供更有意义的聚类系列。

我认为我被挂断的地方是将文本转换为数字表示以及如何最好地做到这一点,在那之后我觉得我可能会遵循一些 KMeans 教程,但如果有人有关于如何解决这个问题的任何建议我都会非常感兴趣。

更新,最接近我想要的东西本质上是这个 jupyter notebook。 https://nbviewer.jupyter.org/github/LucasTurtle/national-anthems-clustering/blob/master/Cluster_Anthems.ipynb

本质上是使用 sklearn 的 tfdif 功能对列表进行矢量化,然后对该数组进行 运行 kmeans。

我还研究了 spaCy 库和 gensim 主题建模

我用第一种方法得到了结果,我认为这是有道理的。但是我更愿意多了解一点我在做什么。

有多种方法可以解决这个问题。但总的来说,这里有一些简单的选择 -

  1. 向量表示 - 使用one-hot编码或TF-IDF来表示句子
  2. 特征提取(可选) - 在大型复杂句子的情况下,您可能希望使用主题模型来提取主题级别的特征。
  3. 聚类 - 可以使用任何聚类方法,例如 K-means

这是示例代码。

1。进口与数据

from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.cluster import KMeans
import pandas as pd

df = pd.DataFrame({"name":['lion','leopard','racoon','possum'], 
"features":[
    ['mane', 'teeth', 'tail', 'carnivore'], 
    ['spots', 'teeth', 'tail', 'carnivore'], 
    ['stripes', 'teeth', 'omnivore', 'small'], 
    ['teeth', 'omnivore', 'small']]})

print(df)
      name                           features
0     lion     [mane, teeth, tail, carnivore]
1  leopard    [spots, teeth, tail, carnivore]
2   racoon  [stripes, teeth, omnivore, small]
3   possum           [teeth, omnivore, small]

2。矢量表示法

您可以使用 sklearn

中的 multi-label binarizer 使用单热编码句子
mlb = MultiLabelBinarizer()
vec = mlb.fit_transform(df['features'])
vectors = pd.DataFrame(vec, columns=mlb.classes_)
vectors
   carnivore  mane  omnivore  small  spots  stripes  tail  teeth
0          1     1         0      0      0        0     1      1
1          1     0         0      0      1        0     1      1
2          0     0         1      1      0        1     0      1
3          0     0         1      1      0        0     0      1

或者您可以使用 sklearn

中的 tf-idf vectorizer
tfidf = TfidfVectorizer()
vec = tfidf.fit_transform(df['features'].apply(' '.join).to_list())
vectors = pd.DataFrame(vec.todense(), columns=tfidf.get_feature_names())
print(vectors)
   carnivore      mane  omnivore     small     spots   stripes      tail  \
0   0.497096  0.630504  0.000000  0.000000  0.000000  0.000000  0.497096   
1   0.497096  0.000000  0.000000  0.000000  0.630504  0.000000  0.497096   
2   0.000000  0.000000  0.497096  0.497096  0.000000  0.630504  0.000000   
3   0.000000  0.000000  0.640434  0.640434  0.000000  0.000000  0.000000   

      teeth  
0  0.329023  
1  0.329023  
2  0.329023  
3  0.423897  

3。特征提取(可选)

接下来,我们可以选择使用 sklearn 中的 LDA 创建主题作为下一步聚类的特征。注意,你可以在这里使用其他降维或分解方法,但是LDA是专门用于主题建模的,并且具有很高的可解释性(如下所示),所以我使用它。

假设数据有 2 个主题。

#Using LDA to create topic level features

lda = LatentDirichletAllocation(n_components=2, verbose=0)
lda_features = lda.fit_transform(vec)
lda_features
array([[0.19035075, 0.80964925],
       [0.19035062, 0.80964938],
       [0.81496776, 0.18503224],
       [0.79598858, 0.20401142]])

要了解 LDA 如何确定主题,检查主题词矩阵以了解主题的构成很有用。

#Topic-word matrix

pd.DataFrame(lda.components_, 
             index=['topic1', 'topic2'], 
             columns=tfidf.get_feature_names()).round(1)
        carnivore  mane  omnivore  small  spots  stripes  tail  teeth
topic1        0.5   0.5       1.6    1.6    0.5      1.1   0.5    1.3
topic2        1.5   1.1       0.5    0.5    1.1      0.5   1.5    1.1

如您所见,代表“食肉动物”动物主题的词是第二个主题的一部分,而代表“杂食动物”动物的词代表第一个主题。根据您的数据和内容的复杂性模式,您的数据包含的潜​​在主题数量最好使用 grid search 来为您的模型找到最佳主题数量。或者,你可以像我一样做假设。

4。聚类

最后,让我们使用 k-means clustering 根据特征的相似性对句子进行分桶。

首先,让我们在不使用 LDA 的情况下进行聚类。

#Using k-means directly on the one-hot vectors OR Tfidf Vectors

kmeans = KMeans(n_clusters=2)
kmeans.fit(vec)
df['pred'] = kmeans.predict(vec)
print(df)
      name                           features  pred
0     lion     [mane, teeth, tail, carnivore]     0
1  leopard    [spots, teeth, tail, carnivore]     0
2   racoon  [stripes, teeth, omnivore, small]     1
3   possum           [teeth, omnivore, small]     1

接下来,我们做同样的事情,但这次使用 LDA 特征。

# clustering the topic level features

kmeans = KMeans(n_clusters=2)
kmeans.fit(lda_features)
df['pred'] = kmeans.predict(lda_features)
      name                           features  pred
0     lion     [mane, teeth, tail, carnivore]     0
1  leopard    [spots, teeth, tail, carnivore]     0
2   racoon  [stripes, teeth, omnivore, small]     1
3   possum           [teeth, omnivore, small]     1

NOTE: When working with any clustering, the labels tend to change each time you rerun but don't disturb the clusters unless data/params change. Meaning, sometimes you may see cluster 0 labeled as cluster 1 and vice versa.