使用具有余弦相似性的K-均值-Python

使用具有余弦相似性的K-均值-Python,python,scikit-learn,k-means,cosine-similarity,sklearn-pandas,Python,Scikit Learn,K Means,Cosine Similarity,Sklearn Pandas,我试图用python实现Kmeans算法,该算法将使用余弦距离而不是欧几里德距离作为距离度量。 我明白使用不同的距离函数可能是致命的,应该小心操作。使用余弦距离作为度量迫使我更改平均函数(根据余弦距离的平均值必须是归一化向量的逐元素平均值)。 我已经看到了手动覆盖sklearn的距离函数的优雅解决方案,我想使用相同的技术覆盖代码的平均部分,但我找不到它。 有人知道怎么做吗? 距离度量不满足三角不等式有多重要? 如果有人知道kmeans的另一种高效实现,我使用余弦度量或满足距离和平均函数,这也会非


我试图用python实现
Kmeans
算法,该算法将使用
余弦距离
而不是欧几里德距离作为距离度量。
我明白使用不同的距离函数可能是致命的,应该小心操作。使用余弦距离作为度量迫使我更改平均函数(根据余弦距离的平均值必须是归一化向量的逐元素平均值)。

我已经看到了手动覆盖sklearn的距离函数的优雅解决方案,我想使用相同的技术覆盖代码的平均部分,但我找不到它。

有人知道怎么做吗?
距离度量不满足三角不等式有多重要?
如果有人知道kmeans的另一种高效实现,我使用余弦度量或满足距离和平均函数,这也会非常有用。
非常感谢你!

编辑:
使用角距离而不是余弦距离后,代码如下所示:

def KMeans_cosine_fit(sparse_data, nclust = 10, njobs=-1, randomstate=None):
    # Manually override euclidean
    def euc_dist(X, Y = None, Y_norm_squared = None, squared = False):
        #return pairwise_distances(X, Y, metric = 'cosine', n_jobs = 10)
        return np.arccos(cosine_similarity(X, Y))/np.pi
    k_means_.euclidean_distances = euc_dist
    kmeans = k_means_.KMeans(n_clusters = nclust, n_jobs = njobs, random_state = randomstate)
    _ = kmeans.fit(sparse_data)
    return kmeans
我注意到(通过数学计算)如果向量被归一化,那么标准平均值对于角度度量很有效。据我所知,我必须在中更改
\u mini\u batch\u step()
。但是这个函数非常复杂,我不知道怎么做。
有人知道替代解决方案吗?
或者,是否有人知道如何使用一个总是强制将质心标准化的函数来编辑此函数?

不幸的是,没有。 Sklearn k-means的当前实现仅使用欧几里德距离

这是因为K-means包含了找到聚类中心并将样本分配到最近中心的计算,而欧几里德仅具有样本中中心的含义


如果你想用余弦距离的K均值,你需要做你自己的函数或类。或者,尝试使用其他聚类算法,如DBSCAN

所以你可以将X归一化为单位长度,并使用K均值作为正常值。原因是如果X1和X2是单位向量,看下面的等式,最后一行括号内的项是余弦距离。

因此,在使用k-means时,只需:

length=np.sqrt((X**2.sum(axis=1))[:,无]
X=X/长度
kmeans=kmeans(n_集群=10,随机状态=0)。拟合(X)
如果需要质心和距离矩阵,请执行以下操作:

len=np.sqrt(np.square(kmeans.cluster\u centers).sum(axis=1)[:,None])
centers=kmeans.cluster\u centers\u/len_
dist=1-np.点(中心,X.T)#K X N余弦距离矩阵
笔记:
  • 刚刚意识到你正在尝试最小化星团的平均向量与其组成部分之间的距离。仅对向量求平均值时,平均向量的长度小于1。但在实践中,仍然值得运行正常的sklearn算法并检查平均向量的长度。在我的例子中,平均向量接近单位长度(平均值约为0.9,但这取决于数据的密度)。 TLDR:使用@σηγ指出的包

您可以规范化数据,然后使用KMeans

from sklearn import preprocessing
from sklearn.cluster import KMeans

kmeans = KMeans().fit(preprocessing.normalize(X))

请看scikit中的学习源代码。链接到的余弦距离示例只不过是将
k\u means\u
模块中名为
euclidean\u distance
的函数变量替换为自定义函数。如果你发布你的k-means代码和你想要覆盖的函数,我可以给你一个更具体的答案。但是,如果你想自己做,只需在
k_means\u
源代码中查找平均函数的名称并替换它。而且,一般来说,问题应该包括一个-如果你包含了你想要修改的代码或不起作用的代码,你可以期望得到更多的帮助。@charlesreid1谢谢,我添加了代码。我的问题是,我还没有完全理解
k_means.py
中的平均函数是如何工作的,因此我无法理解如何更改它。有一个名为python的包,它在球体上实现了k-means算法(因此它做的事情与您尝试做的事情基本相同)。请尝试我们的朋友在交叉验证-->上的相关讨论。如果您使用sklearn.feature_extraction.text.TfidVectorizer,似乎默认情况下应用了L2规范化,即向量化器的输出已经规范化。