余弦相似度精度问题引起的偏差

余弦相似度精度问题引起的偏差

余弦相似度值不等于1(实际是等于1)

两个向量ab是相同的,余弦相似度值应该是1,但是通过sklearnnumpy计算的结果却不等于1,会出现大于1或者小于1的情况,实际上余弦值应该是在[-1, 1]这个区间内的。

使用sklearn.metrics.pairwise.cosine_similarity方法计算余弦相似度, 存在的问题如下:

import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity


# 情况一 向量方向相同, 值相同
a = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
b = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
similarity = np.diag(cosine_similarity(pd.DataFrame([a]).values, pd.DataFrame([b]).values))[0]
print(similarity)  # 计算的结果是1.0000000000000002    实际值应当是1

# 情况二 向量方向相同, 值不相同
a = np.array([1, 1])
b = np.array([2, 2])
similarity = np.diag(cosine_similarity(pd.DataFrame([a]).values, pd.DataFrame([b]).values))[0]
print(similarity)  # 计算的结果是0.9999999999999998    实际值应当是1

# 情况三 向量方向不相同
a = np.array([1, 3])
b = np.array([2, 5])
similarity = np.diag(cosine_similarity(pd.DataFrame([a]).values, pd.DataFrame([b]).values))[0]
print(similarity)  # 0.998274373174996

使用numpy库计算余弦相似度,也会出现类似情况:

import numpy as np


def cosine_similarity1(a, b):
    """
    计算向量a和向量b之间的cosine similarity
    """
    # 计算分子
    numerator = np.dot(a, b)

    # 计算分母
    denominator = np.linalg.norm(a) * np.linalg.norm(b)

    # 计算cosine similarity
    similarity = numerator / denominator

    return similarity


# 情况一 向量方向相同, 值相同
a = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
b = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
# 利用numpy库提供的函数,自定义封装计算余弦相似度的函数
similarity1 = cosine_similarity1(a, b)
print(similarity1)  # 计算的结果是1.0000000000000002      实际值应当是1

# 情况二 向量方向相同, 值不相同
a = np.array([1, 1])
b = np.array([2, 2])
# 利用numpy库提供的函数,自定义封装计算余弦相似度的函数
similarity1 = cosine_similarity1(a, b)
print(similarity1)  # 计算的结果是0.9999999999999998    实际值应当是1

# 情况三  向量方向不同
a = np.array([1, 3])
b = np.array([2, 5])
# 利用numpy库提供的函数,自定义封装计算余弦相似度的函数
similarity1 = cosine_similarity1(a, b)
print(similarity1)  # 0.9982743731749958

以上案例,可以看出,在计算两个向量方向相同时,会存在值不等于1的两种情况;计算方向不同的向量,余弦值是有误差的

问题分析

首先从余弦相似度计算公式上分析,公式如下:

发现,分母上进行了开根号,因为分母会存在开不尽的情况或者能开尽但是因为精度问题导致了不准确,即使使用双(double)精度也依然存在这个问题。

计算机对于开根计算时,会存在精度丢失,即使提高精度,只是减轻了丢失误差,这种精度误差是无法避免的。

举例说明:
5=2.23606797749979
3=1.7320508075688772
开根无法开尽,就会出现精度丢失的现象。

问题解决

使用上面的方法计算出来的余弦值存在的误差问题,可以使用保留round函数保留指定的小数位数来解决问题。
接下来,介绍以下方法来解决精度问题导致的误差。

方式一:首先计算余弦相似度时,分母不进行开根,那么分子就要进行平方处理,这样计算的结果就是余弦相似度的平方,之后再进行开根号处理。

# -*- coding: utf-8 -*-
import numpy as np


def cosine_similarity2(a, b):
    """
    优化方法
    计算向量a和向量b之间的cosine similarity
    """
    similarity = np.sqrt(np.dot(a, b) ** 2 / (np.dot(a, a) * np.dot(b, b)))

    return similarity


# 情况一 向量方向相同, 值相同
a = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
b = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
# 利用numpy库提供的函数,自定义封装计算余弦相似度的函数
similarity1 = cosine_similarity2(a, b)
print(similarity1)  # 1.0
 
# 情况二 向量方向相同, 值不相同
a = np.array([1, 1])
b = np.array([2, 2])
# 利用numpy库提供的函数,自定义封装计算余弦相似度的函数
similarity1 = cosine_similarity2(a, b)
print(similarity1)  # 1.0

# 情况三  向量方向不同
a = np.array([1, 3])
b = np.array([2, 5])
# 利用numpy库提供的函数,自定义封装计算余弦相似度的函数
similarity1 = cosine_similarity2(a, b)
print(similarity1)  # 0.9982743731749959

方法二: 使用scipy.spatial.distance.cosine

# -*- coding: utf-8 -*-
from scipy.spatial.distance import cosine
import pandas as pd


# 情况一 向量方向相同, 值相同
a = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
b = np.array([90.000, 0.000, 0.000, 0.000, 0.000, 0.000, 870.000, 0.000])
similarity3 = 1 - cosine(a, b)
print(similarity3)  # 1

# 情况二 向量方向相同, 值不相同
a = np.array([1, 1])
b = np.array([2, 2])
similarity3 = 1 - cosine(a, b)
print(similarity3)  # 1

# 情况三  向量方向不同
a = np.array([1, 3])
b = np.array([2, 5])
similarity3 = 1 - cosine(a, b)
print(similarity3)  # 0.9982743731749958

通过上面的案例分析,使用第二种方法可以解决余弦相似度计算因为精度问题导致的误差问题,但是计算出来的值精度丢失的问题。

推荐使用scipy.spatial.distance.cosine来计算余弦相似度值。

posted @   三叶草body  阅读(723)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示