milvus-io / milvus-sdk-java

Java SDK for Milvus.
https://milvus.io
Apache License 2.0
366 stars 150 forks source link

关于查询milvus的向量精度问题 #901

Open Wimeremce7 opened 2 months ago

Wimeremce7 commented 2 months ago

image 看到SearchParam的withFloatVectors方法,入参是‘’Float‘’ ,用float的话,会导致精度丢失,这个有什么解决方案吗?

xiaofan-luan commented 2 months ago

image 看到SearchParam的withFloatVectors方法,入参是‘’Float‘’ ,用float的话,会导致精度丢失,这个有什么解决方案吗?

为什么用float会导致精度丢失?据我所知还没有用double向量的模型

Wimeremce7 commented 2 months ago

image 看到SearchParam的withFloatVectors方法,入参是‘’Float‘’ ,用float的话,会导致精度丢失,这个有什么解决方案吗?

为什么用float会导致精度丢失?据我所知还没有用double向量的模型

float精度只有小数点后7位,milvus存储的向量和embedding转化的向量都不止这些。 存储的向量: image embedding转化的向量: image

然后查询的时候只能用float的话,其实是会把精度丢失掉

xiaofan-luan commented 2 months ago

image 看到SearchParam的withFloatVectors方法,入参是‘’Float‘’ ,用float的话,会导致精度丢失,这个有什么解决方案吗?

为什么用float会导致精度丢失?据我所知还没有用double向量的模型

float精度只有小数点后7位,milvus存储的向量和embedding转化的向量都不止这些。 存储的向量: image embedding转化的向量: image

然后查询的时候只能用float的话,其实是会把精度丢失掉

你错误理解了精度和展示的定位。精度的意思,指的就是后面虽然有很多位,但是都不准。你用的是float数据类型,精度就只用这么多。展示出来的东西精度本来就是不对的,原因是js只能支持64位float,也就是double

Wimeremce7 commented 2 months ago

image 看到SearchParam的withFloatVectors方法,入参是‘’Float‘’ ,用float的话,会导致精度丢失,这个有什么解决方案吗?

为什么用float会导致精度丢失?据我所知还没有用double向量的模型

float精度只有小数点后7位,milvus存储的向量和embedding转化的向量都不止这些。 存储的向量: image embedding转化的向量: image 然后查询的时候只能用float的话,其实是会把精度丢失掉

你错误理解了精度和展示的定位。精度的意思,指的就是后面虽然有很多位,但是都不准。你用的是float数据类型,精度就只用这么多。展示出来的东西精度本来就是不对的,原因是js只能支持64位float,也就是double

好的,了解了

XueJian-Zhang commented 2 months ago

你好,我遇到了跟你一样的问题,创建milvus向量字段类型是FloatVector,然后插入的向量是这种的 企业微信截图_17163602043077 但是获取到以后是这样的 企业微信截图_17163604598560 请问有什么解决办法么?

xiaofan-luan commented 2 months ago

你好,我遇到了跟你一样的问题,创建milvus向量字段类型是FloatVector,然后插入的向量是这种的 企业微信截图_17163602043077 但是获取到以后是这样的 企业微信截图_17163604598560 请问有什么解决办法么?

请问你怎么生成的向量?然后怎么打印的向量 floatvector打印出来不可能有那么多位数的

xiaofan-luan commented 2 months ago

大概率你这个数据存错了,用了double格式。这点精度对于搜索不会有任何区别

XueJian-Zhang commented 2 months ago

你好,我遇到了跟你一样的问题,创建milvus向量字段类型是FloatVector,然后插入的向量是这种的 企业微信截图_17163602043077 但是获取到以后是这样的 企业微信截图_17163604598560 请问有什么解决办法么?

请问你怎么生成的向量?然后怎么打印的向量 floatvector打印出来不可能有那么多位数的

这个是python一个自定义的工具类,用于提取照片的某些特征值生成的向量。

XueJian-Zhang commented 2 months ago

大概率你这个数据存错了,用了double格式。这点精度对于搜索不会有任何区别

经过提醒。我查看了代码。没有用double,因为定义集合时设置了向量类型是FloatVector,在存储数据时,不能用double格式的,那样会产生“Type mismatch for field 'feature': Float vector field's value type must be List”错误,所以用的List。然后存储的时候向量值是 企业微信截图_17163654011856 ,但是在milvus的可视化平台中,存储的向量值和postman中发送的请求参数一致。 企业微信截图_17163652912278

shanghaikid commented 2 months ago

但是获取到以后是这样的

请问你是用什么办法获取的? restful api? @XueJian-Zhang

Wimeremce7 commented 2 months ago

大概率你这个数据存错了,用了double格式。这点精度对于搜索不会有任何区别

经过提醒。我查看了代码。没有用double,因为定义集合时设置了向量类型是FloatVector,在存储数据时,不能用double格式的,那样会产生“Type mismatch for field 'feature': Float vector field's value type must be List”错误,所以用的List。然后存储的时候向量值是 企业微信截图_17163654011856 ,但是在milvus的可视化平台中,存储的向量值和postman中发送的请求参数一致。 企业微信截图_17163652912278

他们目前不支持double,至于attu客户端显示的向量小数位数,是因为js会自动补小数,并不能当真。

XueJian-Zhang commented 2 months ago

但是获取到以后是这样的

请问你是用什么办法获取的? restful api? @XueJian-Zhang

直接调用milvus的api就可以获取到,网上有很多教程。你可以参考下这位博主的方法:https://blog.csdn.net/lsqingfeng/article/details/138212915

XueJian-Zhang commented 2 months ago

大概率你这个数据存错了,用了double格式。这点精度对于搜索不会有任何区别

经过提醒。我查看了代码。没有用double,因为定义集合时设置了向量类型是FloatVector,在存储数据时,不能用double格式的,那样会产生“Type mismatch for field 'feature': Float vector field's value type must be List”错误,所以用的List。然后存储的时候向量值是 企业微信截图_17163654011856 ,但是在milvus的可视化平台中,存储的向量值和postman中发送的请求参数一致。 企业微信截图_17163652912278

他们目前不支持double,至于attu客户端显示的向量小数位数,是因为js会自动补小数,并不能当真。

好的,谢谢。

yhmo commented 2 months ago

python/javascript这些语言的浮点型是跟系统的位数走的,64位系统就会以64位来表示浮点数,但实际上在调用milvus rpc接口的时候其实都是以float32传输。对于用户来说就像变了个魔术似的。 但java是强制要求Float也就是32位输入,熟悉java的用户不应该有这种迷惑。 以32位浮点输入就已经明确告诉你向量在后台是以32位数据传输,存储,以及计算。所以它根本不可能再变出64位精度的小数给你。 向量近似近邻检索已经是一个模糊检索的概念,用来比对相似的事物。如果说我们要比较两张人脸是否相似,难道我们会去关心这两人的头发数量是否相等么?在大部分场景下,用64位精度的向量就相当于掩耳盗铃,非但对召回率没什么影响,计算负载和内存负载还大了一倍,得不偿失。

XueJian-Zhang commented 2 months ago

python/javascript这些语言的浮点型是跟系统的位数走的,64位系统就会以64位来表示浮点数,但实际上在调用milvus rpc接口的时候其实都是以float32传输。对于用户来说就像变了个魔术似的。 但java是强制要求Float也就是32位输入,熟悉java的用户不应该有这种迷惑。 以32位浮点输入就已经明确告诉你向量在后台是以32位数据传输,存储,以及计算。所以它根本不可能再变出64位精度的小数给你。 向量近似近邻检索已经是一个模糊检索的概念,用来比对相似的事物。如果说我们要比较两张人脸是否相似,难道我们会去关心这两人的头发数量是否相等么?在大部分场景下,用64位精度的向量就相当于掩耳盗铃,非但对召回率没什么影响,计算负载和内存负载还大了一倍,得不偿失。

你好,我使用python的两种方式查询了同一个id的向量值,可是得到的向量值却不相同,一种是根据向量id,另一种是根据向量值。请问为什么查询结果向量值的精度会不一样呢? 企业微信截图_17164599374880

yhmo commented 2 months ago

你用的是python sdk,我说过python语言对于浮点型的数值总是转成64位显示。 如果你懂得怎么在python sdk里调试的话,你会看到query()接口会调用到这里:https://github.com/milvus-io/pymilvus/blob/473f62fee6cb5e446f571471884d0d7303d01ab6/pymilvus/client/entity_helper.py#L481

我们把这几行代码改成如下:

       if field_data.type == DataType.FLOAT_VECTOR:
            dim = field_data.vectors.dim
            if len(field_data.vectors.float_vector.data) >= index * dim:
                start_pos, end_pos = index * dim, (index + 1) * dim
                entity_row_data[field_data.field_name] = []
                print(field_data.vectors.float_vector)
                for x in field_data.vectors.float_vector.data[start_pos:end_pos]:
                    print(x)
                    entity_row_data[field_data.field_name].append(np.single(x))
                print(entity_row_data[field_data.field_name])

打断点进入,你会看到 field_data.vectors.float_vector 这个是由Milvus rpc接口返回的结构体,打印出来的值是32位浮点型。 然后for x in field_data.vectors.float_vector.data[start_pos:end_pos] 这行里的 x 打印出来就变成了64位浮点型,所以知道我为何说“python语言对于浮点型的数值总是转成64位显示”了吧。 后面几行又做了什么呢? append(np.single(x)),看到np.single(x)了吧,这是numpy的single,指代float32浮点型。这里是把64位的x又cut成了32位浮点数。 所以你看到的query的结果里都是以32位浮点型显示。

再来看search()接口干了什么。 https://github.com/milvus-io/pymilvus/blob/473f62fee6cb5e446f571471884d0d7303d01ab6/pymilvus/client/abstract.py#L480

if dtype == DataType.FLOAT_VECTOR:
                field2data[name] = vectors.float_vector.data[start * dim : end * dim], field_meta
                continue

它直接把rpc接口返回的response包装成SearchResult给你,然后SearchResult里面是直接提取向量数据,没有经过numpy.single的转化,所以python自动用64位浮点型来表示。

yhmo commented 2 months ago

再强调一遍,milvus服务端处理向量,无论是存储,计算还是输出,都是以float32来处理。各个SDK拿到的向量数据都是32位浮点型。如果你看到的是64位浮点数,那是因为某些语言本身自动把32位转成了64位表示,实际上32位精度之外的数字都是随机数字,没有任何意义。