• 「torch.cosine_smilarity() = 0」引发的关于cpu与gpu精度问题的探讨


    前言:2023年11月21日下午16:00 许,本篇博客记录由「torch.cosine_smilarity()计算余弦相似度计算结果为0」现象引发的关于 CPU 与 GPU 计算精度的探索。

    事情的起因是,本人在使用 torch.cosine_smilarity() 函数计算GPU上两个特征的余弦相似度时,发现得出的结果为 0,百思不得其解。首先排出特征维度的问题,然后尝试5种不同的相似度计算方法:

    • scipy.spatial.distance.cosine
    • torch.cosine_similarity
    • F.cosine_similarity
    • torch.nn.CosineSimilarity
    • 基于余弦相似度公式的torch代码

    整体代码如下:

    import torch
    torch.set_printoptions(profile="full")
    import torch.nn.functional as F
    from scipy.spatial.distance import cosine
    # device = "cuda" if torch.cuda.is_available() else "cpu"
    device = "cpu"
    
    import clip
    from PIL import Image
    clip_model, processor = clip.load("ViT-L/14", device=device)
    srcpath = '/newdata/SD/DEFAKE/data_test/9709_glide.png'
    despath = '/newdata/SD/outputs/0_9_sd1.5.png'
    src_feature = clip_model.encode_image(processor(Image.open(srcpath)).unsqueeze(0).to(device)).squeeze(0)  
    des_feature = clip_model.encode_image(processor(Image.open(despath)).unsqueeze(0).to(device)).squeeze(0)
    
    sim_1 = 1 - cosine(src_feature.cpu(), des_feature.cpu())
    
    sim_2 = torch.cosine_similarity(src_feature, des_feature, dim=0).item()
    
    sim_3 = F.cosine_similarity(src_feature, des_feature, dim=0).item()
    
    cos = torch.nn.CosineSimilarity(dim=0)
    sim_4 = cos(src_feature, des_feature).item()
    
    sim_5 = torch.div(torch.sum(src_feature * des_feature,0),torch.sqrt(torch.sum(torch.pow(src_feature,2),0))* torch.sqrt(torch.sum(torch.pow(des_feature,2),0))).item()
    
    print(sim_1, sim_2, sim_3, sim_4, sim_5)
    # 0.5302734375 0.0 0.0 0.0 0.53076171875
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    发现,上述代码在CPU和GPU上运行结果不一致:

    上述代码在 CPU 上的运行结果为:

    0.5301393270492554
    0.5301393270492554
    0.5301393270492554
    0.5301393270492554
    0.5301393270492554
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述代码在 GPU 上的运行结果为:

    0.5302734375
    0.0
    0.0
    0.0
    0.53076171875
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这是一个很有意思的现象,在CPU上计算出的5种相似度结果惊人一致,而在GPU上计算出的5种相似度结果中,中间三种基于torch函数调用的方式计算结果均为0,而第一种首先将特征搬运到CPU上然后使用scipy.spatial.distance.cosine()函数的计算结果和第五种直接在GPU上使用基于余弦相似度公式的torch代码的计算结果又与CPU上计算出的结果各有不同。


    然后,我把由 CLIP 预训练模型提取的两张图像的特征(维度为768维) src_featuredes_feature 换成两个随机初始化的张量 ,其余代码不变:

    
    # import clip
    # from PIL import Image
    # clip_model, processor = clip.load("ViT-L/14", device=device)
    # srcpath = '/newdata/SD/DEFAKE/data_test/9709_glide.png'
    # despath = '/newdata/SD/outputs/0_9_sd1.5.png'
    # src_feature = clip_model.encode_image(processor(Image.open(srcpath)).unsqueeze(0).to(device)).squeeze(0)  
    # des_feature = clip_model.encode_image(processor(Image.open(despath)).unsqueeze(0).to(device)).squeeze(0)
    
    # 将上面代码注释掉,换为:
    
    src_featre = torch.tensor([1.0, 2.0, 3.0])
    des_feature = torch.tensor([4.0, 5.0, 6.0])
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可见上述示例代码在CPU和GPU上运行结果是一致的:

    上述代码在 CPU 上的运行结果为:

    0.9746318459510803
    0.9746317863464355
    0.9746317863464355
    0.9746317863464355
    0.9746317863464355
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述代码在 GPU 上的运行结果为:

    0.9746318459510803
    0.9746317863464355
    0.9746317863464355
    0.9746317863464355
    0.9746317863464355
    
    • 1
    • 2
    • 3
    • 4
    • 5

    由上述结果可以发现,第一种基于scipy.spatial.distance.cosine()函数的计算结果与其余四组基于torch的计算结果略有不同,说明后四种方法实现的底层逻辑应该是类似的,但由于给定特征的某些不可知原因,有时会出现中间三种基于torch函数调用的方法结果为0的情况,所以保险起见,如果要使用基于torch的计算方法,首选第5种相似度计算方法,当然,效率允许的情况下,直接在CPU上使用第一种方法无疑是精度最高的计算方法。


    参考资料

    1. GPU和CPU计算上的精度差异_cpu和gpu训练结果不同-CSDN博客
  • 相关阅读:
    汇编指令概述 AT&T汇编基本语法
    【自学开发之旅】Flask-回顾--对象拆分-蓝图(二)
    camera预览流程 --- 从HAL到OEM
    java可变mutable/不可变immutable类型介绍
    c# 实验六 菜单栏、工具栏和状态栏的设计
    Macbooster8免费mac清理垃圾软件功能介绍
    继承的详解
    超全总结!大模型算法面试指南(含答案)
    常见的一些小函数集合
    springcloudalibaba架构(28):分布式事务解决方案
  • 原文地址:https://blog.csdn.net/qq_36332660/article/details/134534807