根据余弦相似度创建随机向量

7

基本上,给定一个向量v,我想获得另一个具有一些余弦相似度的随机向量w,使得v和w之间有一些余弦相似度。在Python中有没有办法做到这一点?

例如:为了简单起见,我将使用2D向量[3,-4]。我想获得一个余弦相似度为60%或0.6以上的随机向量w。这应该生成向量w的值为[0.875,3]或任何其他具有相同余弦相似度的向量。所以我希望这已经足够清楚了。


1
暴力破解:在循环中,生成随机向量;计算余弦相似度;如果满足您的条件,则保存向量;当您获得十个(100、1000、?)时停止。| 查看这些向量,看看是否有模式可以用来限制向量生成。 - wwii
1
我投票重新开放这个问题,因为它很容易解决(删除最后一行)。 - Paul Panzer
1
我已经删除了最后一行,所以现在它不会要求特定的库推荐。 - eugen
2个回答

11

给定向量v和余弦相似度costheta(介于-1和1之间的标量),按照函数rand_cos_sim(v, costheta)中的方式计算w

import numpy as np


def rand_cos_sim(v, costheta):
    # Form the unit vector parallel to v:
    u = v / np.linalg.norm(v)

    # Pick a random vector:
    r = np.random.multivariate_normal(np.zeros_like(v), np.eye(len(v)))

    # Form a vector perpendicular to v:
    uperp = r - r.dot(u)*u

    # Make it a unit vector:
    uperp = uperp / np.linalg.norm(uperp)

    # w is the linear combination of u and uperp with coefficients costheta
    # and sin(theta) = sqrt(1 - costheta**2), respectively:
    w = costheta*u + np.sqrt(1 - costheta**2)*uperp

    return w
例如,
In [17]: v = np.array([3, -4])

In [18]: w = rand_cos_sim(v, 0.6)

In [19]: w
Out[19]: array([-0.28, -0.96])

验证余弦相似性:

In [20]: v.dot(w)/(np.linalg.norm(v)*np.linalg.norm(w))
Out[20]: 0.6000000000000015

In [21]: w = rand_cos_sim(v, 0.6)

In [22]: w
Out[22]: array([1., 0.])

In [23]: v.dot(w)/(np.linalg.norm(v)*np.linalg.norm(w))
Out[23]: 0.6

返回值的大小始终为1,因此在上面的示例中,只有两个可能的随机向量,[1, 0]和[-0.28,-0.96]。

另一个示例,在三维空间中:

In [24]: v = np.array([3, -4, 6])

In [25]: w = rand_cos_sim(v, -0.75)

In [26]: w
Out[26]: array([ 0.3194265 ,  0.46814873, -0.82389531])

In [27]: v.dot(w)/(np.linalg.norm(v)*np.linalg.norm(w))
Out[27]: -0.75

In [28]: w = rand_cos_sim(v, -0.75)

In [29]: w
Out[29]: array([-0.48830063,  0.85783797, -0.16023891])

In [30]: v.dot(w)/(np.linalg.norm(v)*np.linalg.norm(w))
Out[30]: -0.75

1
@Warren,你为什么要使用 np.random.multivariate_normal?我试着用你的代码,发现使用 np.random.normal 更快。你使用多元分布的特定原因是什么? - ashutosh singh
2
@ashutoshsingh,我使用multivariate_normal是因为在实现时我想到了这个抽象概念。我想要一个随机向量,其分布是径向对称的。但是如果协方差矩阵是单位矩阵,那么同样可以使用np.random.normal(size=len(v))或者np.random.randn(len(v))。如果它们更快,那就使用其中之一吧! - Warren Weckesser

-2

1
这个回答如何回答上面的问题?我想要得到向量w,但我不知道如何逆推... - eugen
2
我以为你在寻找一个实现余弦距离的库,为了反向工作,坐下来重新编写公式以找到所需的向量。然后实现它,scipy也有点积。 - Alexis Drakopoulos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接