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

时间:2017-09-25 16:22:37

标签: python scikit-learn k-means cosine-similarity sklearn-pandas


我正在尝试在python中实现Kmeans算法,该算法将使用cosine distance而不是欧几里德距离作为距离度量。
我知道使用不同的距离函数可能是致命的,应该仔细进行。使用余弦距离作为度量标准迫使我改变平均函数(根据余弦距离的平均值必须是归一化向量的元素平均值的元素)。

我已经看到了this手动覆盖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

我注意到(通过数学计算)如果向量被归一化,则标准平均值适用于角度量。据我了解,我必须更改k_means_.py中的_mini_batch_step()。但功能相当复杂,我无法理解如何做到这一点。
有没有人知道替代解决方案?
或许,有没有人知道我怎么能用一个总是迫使质心标准化的函数来编辑这个函数?

4 个答案:

答案 0 :(得分:4)

因此,您可以将X标准化为单位长度,并照常使用K-means。原因是如果X1和X2是单位向量,请看以下方程式,最后一行括号内的项是余弦距离。 vect_dist

因此,在使用k均值方面,只需执行以下操作:

length = np.sqrt((X**2).sum(axis=1))[:,None]
X = X / length

kmeans = KMeans(n_clusters=10, random_state=0).fit(X)

如果需要质心和距离矩阵,请执行以下操作:

len_ = np.sqrt(np.square(kmeans.cluster_centers_).sum(axis=1)[:,None])
centers = kmeans.cluster_centers_ / len_
dist = 1 - np.dot(centers, X.T) # K x N matrix of cosine distances

注意:

  • 仅意识到您正在尝试最小化聚类的均值向量及其组成部分之间的距离。当您简单地对向量求平均时,平均向量的长度小于一。但是在实践中,仍然值得运行常规的sklearn算法并检查均值向量的长度。在我的情况下,平均向量接近单位长度(平均约为0.9,但这取决于数据的密度)。 TLDR:使用@ {ηγ指出的spherecluster包。

答案 1 :(得分:0)

不幸的是,没有。 Sklearn当前的k均值实现仅使用欧几里得距离。

原因是K均值包括查找聚类中心并将样本分配给最接近中心的计算,而欧几里得仅在样本中具有该中心的含义。

如果要使用余弦距离的K均值,则需要创建自己的函数或类。或者,尝试使用其他群集算法,例如DBSCAN。

答案 2 :(得分:0)

您可以尝试k-medoids,它将支持任何距离度量。它不使用“均值”作为中心,而是使用现有数据点。

https://scikit-learn-extra.readthedocs.io/en/latest/generated/sklearn_extra.cluster.KMedoids.html

答案 3 :(得分:0)

您可以对数据进行标准化,然后使用 KMeans。

from sklearn import preprocessing
from sklearn.cluster import KMeans

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