我有一个N个顶点的图形,其中每个顶点代表一个地方。此外,我有一个向量,每个用户一个,N个系数中的每一个,其中系数的值是在相应位置花费的持续时间(如果没有访问该位置,则为0)。
E.g。对于图表:
向量:
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比c2更接近于c1(更接近)。显然事实并非如此。
Q1。为什么这个假设错了?
Q2。在这种情况下,余弦距离是否是正确的距离函数?
Q3。考虑到问题的本质,什么会更好?
答案 0 :(得分:17)
让我们将余弦相似度划分为多个部分,并参阅 和 的工作原理。
2个向量之间的余弦值a
和b
- 定义为:
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])
单独处理,结果只是所有处理的 sum 。这对你的情况很有好处,因为你不希望不同的对(不同的顶点)相互混淆:如果user1只访问了vertex2而user2只访问了vertex1,那么它们没有任何共同点,它们之间有相似之处应该为零。你真正不喜欢的是如何计算各对之间的相似性 - 即函数g()
。
对于余弦函数,各对之间的相似性如下所示:
g(x, y) = x * y / L
其中x
和y
表示用户在顶点上花费的时间。以下是主要问题:乘法是否代表各个对之间的相似性?我不这么认为。在某些顶点上花费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
总结这个漫长且无聊的故事,我建议重写余弦相似度/距离,以便在两个向量中使用某种变量之间的差异。
答案 1 :(得分:4)
余弦相似度适用于不想要将长度变为accoun但仅限角度的情况。 如果您还想包括长度,请选择不同的距离函数。
余弦距离 与平方欧几里德距离(实际定义k均值的唯一距离)密切相关;这就是球形k-means有效的原因。
这种关系非常简单:
平方欧几里德距离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)
!
换句话说:余弦距离是欧几里德距离的平方,数据标准化为单位长度。
如果您不想规范化数据,请不要使用余弦。
答案 2 :(得分:0)
Q1. Why is this assumption wrong?
从定义中可以看出,余弦相似度测量了2个向量之间的角度。
在您的情况下,向量v1
平放在第一维上,而c1
和c2
两者从轴上对齐,因此,余弦相似度具有< / em>是一样的。
请注意,问题在于c1
和c2
指向同一方向。 任何 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?