【问题标题】:Cosine distance as vector distance function for k-means余弦距离作为 k 均值的向量距离函数
【发布时间】:2014-09-30 14:40:38
【问题描述】:

我有一个包含 N 个顶点的图,其中每个顶点代表一个地方。我也有向量,每个用户一个,N 个系数中的每一个,其中系数的值是在相应地点花费的持续时间(以秒为单位),如果该地点未被访问,则为 0。

例如对于图表:

向量:

v1 = {100, 50, 0 30, 0}

表示我们花费了:

100secs at vertex 1
50secs at vertex 2 and 
30secs at vertex 4 

(未访问的顶点 3 和 5,因此为 0)。

我想运行 k-means 聚类,我选择 cosine_distance = 1 - cosine_similarity 作为距离度量,其中 cosine_similarity 的公式是:

here 所述。

但我注意到以下内容。假设k=2,其中一个向量是:

v1 = {90,0,0,0,0}

在求解最小化与候选质心总距离的优化问题的过程中,假设在某一点,2个候选质心为:

c1 = {90,90,90,90,90}
c2 = {1000, 1000, 1000, 1000, 1000}

对 (v1, c1) 和 (v1, c2) 运行 cosine_distance 公式,我们得到完全相同的 0.5527864045 距离。

我会假设 v1 与 c1 比 c2 更相似(更接近)。显然情况并非如此。

第一季度。为什么这个假设是错误的?

第二季度。对于这种情况,余弦距离是正确的距离函数吗?

第三季度。考虑到问题的性质,什么会更好?

【问题讨论】:

    标签: cluster-analysis data-mining distance k-means cosine-similarity


    【解决方案1】:

    Q1. Why is this assumption wrong?

    正如我们从定义中看到的,余弦相似度衡量的是 2 个向量之间的角度。

    在您的情况下,向量 v1 平放在第一维上,而 c1c2 都与轴对齐,因此余弦相似度必须相同.

    请注意,问题在于c1c2 指向同一方向。 任何 v1 将与它们具有相同的余弦相似度。举例说明:

    Q2. Is the cosine distance a correct distance function for this case?

    正如我们从手头的例子中看到的那样,可能不是。

    Q3. What would be a better one given the nature of the problem?

    考虑Euclidean Distance

    【讨论】:

    • 鉴于问题的性质,我更倾向于使用非对称函数。我的意思是两个访问顶点的用户,即使他们停留的时间可能不完全相同,也应该被认为比两个根本没有访问顶点的用户更接近,即具有匹配的 0。据我了解,欧几里得距离是对称的。对吗?
    • 问题不在于向量有很多零。问题在于每个质心的所有系数都相等。我只是将用户向量的所有系数都归零,以更清楚地表明它应该比另一个中心更接近一个中心。您可以将用户向量的 0 替换为非零值,并且您仍然会从两个中心获得相同的距离。这就是为什么我不是 100% 相信余弦距离不适合这个问题。
    【解决方案2】:

    余弦相似度适用于您想考虑长度,而只考虑角度的情况。 如果您还想包括长度,请选择不同的距离函数。

    余弦距离与平方欧几里得距离(唯一真正定义k-means的距离)密切相关;这就是球形 k 均值有效的原因。

    关系很简单:

    平方欧几里得距离sum_i (x_i-y_i)^2 可以分解为sum_i x_i^2 + sum_i y_i^2 - 2 * sum_i x_i*y_i。如果两个向量都被归一化,即长度无关紧要,那么前两项为 1。在这种情况下,平方欧几里得距离为 2 - 2 * cos(x,y)!

    换句话说:余弦距离是欧几里得距离的平方,数据归一化为单位长度

    如果您不想标准化数据,请不要使用余弦。

    【讨论】:

      【解决方案3】:

      让我们将余弦相似度分成几部分,看看 如何为什么 它起作用。

      两个向量之间的余弦 - ab - 定义为:

      cos(a, b) = sum(a .* b) / (length(a) * length(b))
      

      其中.* 是逐元素乘法。分母在这里只是为了规范化,所以我们简单地称它为L。有了它,我们的功能变成了:

      cos(a, b) = sum(a .* b) / L
      

      这又可以改写为:

      cos(a, b) = (a[1]*b[1] + a[2]*b[2] + ... + a[k]*b[k]) / L = 
                = a[1]*b[1]/L + a[2]*b[2]/L + ... + a[k]*b[k]/L
      

      让我们更抽象一点,将x * y / L 替换为函数g(x, y)L 是常量,所以我们不把它作为函数参数)。因此,我们的余弦函数变为:

      cos(a, b) = g(a[1], b[1]) + g(a[2], b[2]) + ... + g(a[n], b[n]) 
      

      也就是说,每对元素(a[i], b[i])单独处理,结果只是所有处理的总和。这对您的情况有好处,因为您不希望不同的对(不同的顶点)相互混淆:如果 user1 仅访问了 vertex2 和 user2 - 仅访问了 vertex1,那么它们没有任何共同点,它们之间的相似性应该是零。您实际上不喜欢的是如何计算单个对之间的相似性 - 即函数 g() -。

      单个对之间的余弦函数相似性如下所示:

      g(x, y) = x * y / L
      

      其中xy 表示用户在顶点上花费的时间。这里的主要问题是:乘法是否能很好地表示个体对之间的相似性?我不这么认为。在某个顶点上花费 90 秒的用户应该与在那里花费 70 或 110 秒的用户接近,但与在那里花费 1000 或 0 秒的用户更远。乘法(甚至被L 标准化)在这里完全是误导性的。乘以 2 个时间段甚至意味着什么?

      好消息是,这是你设计相似函数的人。我们已经决定对对(顶点)的独立处理感到满意,并且我们只希望个体相似度函数g(x, y) 使其参数合理。什么是比较时间段的合理功能?我想说减法是一个不错的选择:

      g(x, y) = abs(x - y)
      

      这不是相似度函数,而是距离函数——值越接近,g() 的结果越小——但最终想法是相同的,所以我们可以在需要时互换它们。

      我们可能还想通过平方差来增加大不匹配的影响:

      g(x, y) = (x - y)^2 
      

      嘿!我们刚刚重新发明了(mean) squared error!我们现在可以坚持 MSE 来计算距离,或者我们可以继续寻找好的g() 函数。

      有时我们可能不想增加,而是平滑差异。在这种情况下我们可以使用log

      g(x, y) = log(abs(x - y))
      

      我们可以像这样对零进行特殊处理:

      g(x, y) = sign(x)*sign(y)*abs(x - y)   # sign(0) will turn whole expression to 0
      

      或者我们可以通过反转差异从距离回到相似性:

      g(x, y) = 1 / abs(x - y)
      

      请注意,在最近的选项中,我们没有使用归一化因子。事实上,您可以为每种情况提出一些好的规范化,或者只是省略它 - 规范化并不总是需要或好的。例如,在余弦相似度公式中,如果将归一化常数L=length(a) * length(b) 更改为L=1,您将得到不同但仍然合理的结果。例如。

      cos([90, 90, 90]) == cos(1000, 1000, 1000)  # measuring angle only
      cos_no_norm([90, 90, 90]) < cos_no_norm([1000, 1000, 1000])  # measuring both - angle and magnitude
      

      总结这个冗长而无聊的故事,我建议重写余弦相似度/距离,以在两个向量中使用某种变量之间的差异

      【讨论】:

        猜你喜欢
        • 2014-01-22
        • 2016-08-23
        • 2017-07-10
        • 2016-12-05
        • 2015-06-02
        • 1970-01-01
        • 2017-12-12
        • 2021-09-09
        • 2021-07-15
        相关资源
        最近更新 更多