我是一名数据工程师,对机器学习方法的理解有限,正在尝试在开始编码之前找到一个我能理解的好策略。我想做的是根据键值对创建聚类,其中键是名称,值是一些字符串列表。
目标是根据各自的字符串列表的相似性,创建名称的自然聚类。
例如,在这个数据集中,我期望的自然分组应该是狮子/豹子和浣熊/负鼠,因为单词teeth, tail和carnivore
的相似性。
我已经尝试过一种方法,即比较一个条目,例如狮子,遍历每个列表并与其他值进行比较,如果在另一个列表中找到一个值,则分配一个相似性分数。然而,我非常希望使用像K-means聚类算法这样的方法来学习如何使用它,同时我认为/希望它能提供更有意义的一系列聚类。
我觉得我卡住的地方是如何将文本转换为数值表示以及如何最好地做到这一点,之后我觉得我可以遵循一些KMeans教程,但如果有人对如何处理这个问题有任何建议,我会非常感兴趣的。
回答:
解决这个问题有很多方法。但总的来说,这里有一些简单的替代方案 –
- 向量表示 – 使用独热编码或TF-IDF来表示句子
- 特征提取(可选) – 在大型复杂句子的情况下,您可能希望使用主题模型来提取主题级别的特征。
- 聚类 – 可以使用任何聚类方法,如K-means
这里是一个示例代码。
1. 导入和数据
from sklearn.preprocessing import MultiLabelBinarizerfrom sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.decomposition import LatentDirichletAllocationfrom sklearn.cluster import KMeansimport pandas as pddf = 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 features0 lion [mane, teeth, tail, carnivore]1 leopard [spots, teeth, tail, carnivore]2 racoon [stripes, teeth, omnivore, small]3 possum [teeth, omnivore, small]
2. 向量表示
您可以使用来自sklearn的多标签二值化器对句子进行独热编码
mlb = MultiLabelBinarizer()vec = mlb.fit_transform(df['features'])vectors = pd.DataFrame(vec, columns=mlb.classes_)vectors
carnivore mane omnivore small spots stripes tail teeth0 1 1 0 0 0 0 1 11 1 0 0 0 1 0 1 12 0 0 1 1 0 1 0 13 0 0 1 1 0 0 0 1
或者您可以使用来自sklearn的tf-idf向量化器
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个主题。
#使用LDA创建主题级别特征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如何决定主题,查看主题-词矩阵来理解主题的组成是有用的。
#主题-词矩阵pd.DataFrame(lda.components_, index=['topic1', 'topic2'], columns=tfidf.get_feature_names()).round(1)
carnivore mane omnivore small spots stripes tail teethtopic1 0.5 0.5 1.6 1.6 0.5 1.1 0.5 1.3topic2 1.5 1.1 0.5 0.5 1.1 0.5 1.5 1.1
如您所见,代表“食肉动物”主题的词汇属于第二个主题,而代表“杂食动物”动物的词汇代表第一个主题。根据您的数据和内容中的复杂性模式,您的数据包含的潜在主题数量,最好使用网格搜索来找到模型的最佳主题数量。或者,您可以像我一样做出假设。
4. 聚类
最后,让我们使用k-means聚类根据特征的相似性对句子进行分组。
首先,我们先在不使用LDA的情况下进行聚类。
#直接在独热向量或Tfidf向量上使用k-meanskmeans = KMeans(n_clusters=2)kmeans.fit(vec)df['pred'] = kmeans.predict(vec)print(df)
name features pred0 lion [mane, teeth, tail, carnivore] 01 leopard [spots, teeth, tail, carnivore] 02 racoon [stripes, teeth, omnivore, small] 13 possum [teeth, omnivore, small] 1
接下来,我们同样做,但这次使用LDA特征。
#聚类主题级别特征kmeans = KMeans(n_clusters=2)kmeans.fit(lda_features)df['pred'] = kmeans.predict(lda_features)
name features pred0 lion [mane, teeth, tail, carnivore] 01 leopard [spots, teeth, tail, carnivore] 02 racoon [stripes, teeth, omnivore, small] 13 possum [teeth, omnivore, small] 1
注意:在进行任何聚类工作时,每次重新运行时标签可能会发生变化,但除非数据/参数发生变化,否则不会干扰聚类。也就是说,有时您可能会看到聚类0被标记为聚类1,反之亦然。