You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ojAlgo中MatrixR032字符串转矩阵、归一化及余弦相似度问题

问题描述

我有两个逗号分隔的嵌入向量字符串,每个包含128个可存入float类型的元素。参考ojAlgo的线性代数相关内容,想要将这两个字符串转换为ojAlgo矩阵,完成归一化并计算余弦相似度。先测试单个矩阵,预期自身的余弦相似度为1.0,但运行代码后出现异常结果。

测试代码:

PhysicalStore.Factory<Double, Primitive32Store> storeFactory = Primitive32Store.FACTORY;
String dummyMatrixValues = "0.47058824,0.5647059,0.54901963,0.54509807,0.54901963";
Primitive32Store matrixR032 = storeFactory.rows(Arrays.stream(dummyMatrixValues.split(","))
        .mapToDouble(Double::parseDouble)
        .toArray());
System.out.println("Primitive32Store : " + matrixR032);
matrixR032.modifyAny(DataProcessors.STANDARD_SCORE);
System.out.println("Primitive32Store - normalized : " + matrixR032);
System.out.println(matrixR032);
System.out.println("matrixR032 " + matrixR032.multiply(storeFactory.make(matrixR032.transpose())));

运行输出:

[java] Primitive32Store : org.ojalgo.matrix.store.Primitive32Store < 1 x 5 >
[java] { { 0.47058823704719543,    0.5647059082984924, 0.5490196347236633, 0.545098066329956,  0.5490196347236633 } }
[java] Primitive32Store - normalized : org.ojalgo.matrix.store.Primitive32Store < 1 x 5 >
[java] { { NaN,    NaN,    NaN,    NaN,    NaN } }
[java] org.ojalgo.matrix.store.Primitive32Store < 1 x 5 >
[java] { { NaN,    NaN,    NaN,    NaN,    NaN } }
[java] matrixR032 org.ojalgo.matrix.store.Primitive32Store < 1 x 1 >
[java] { { NaN } }

遇到的核心问题:

  • 归一化结果全为NaN;
  • 转换后的数值新增了未指定的小数位(切换至Primitive64Store后该现象消失)。

需要解决:

  1. 如何确保string->float[]->Primitive32Store转换时保留原始精度,不新增多余小数位?
  2. 如何正确对向量进行归一化并计算余弦相似度?

解决方案

1. 解决Primitive32Store的精度显示问题

你看到的额外小数位,是因为用Double.parseDouble()把字符串转成double后,再存入float类型的Primitive32Store时的精度差异——double的精度远高于float,转存后实际存储的是原始float的近似值,但打印时会输出完整的double转译结果。

要严格保留原始字符串对应的float精度,直接将字符串解析为float类型再存入Primitive32Store即可:

Primitive32Store matrixR032 = storeFactory.rows(Arrays.stream(dummyMatrixValues.split(","))
        .mapToFloat(Float::parseFloat)
        .toArray());

这样存储的是原始字符串对应的float值,打印输出时也会和原始字符串的精度一致。

2. 正确实现归一化与余弦相似度计算

为什么出现NaN

你使用的DataProcessors.STANDARD_SCORE是Z-score标准化,公式为(x - 均值)/标准差。当向量元素的标准差为0(或因精度问题被判定为0)时,会触发除以0的操作,导致结果为NaN。而余弦相似度需要的是L2归一化(将向量缩放到单位长度),并非Z-score标准化。

正确实现步骤

步骤1:对向量执行L2归一化

使用ojAlgo提供的VectorEnums.NORM.L2完成归一化:

// 将向量缩放到单位长度
matrixR032.modifyAny(v -> v.normalise(VectorEnums.NORM.L2));

步骤2:计算余弦相似度

余弦相似度的本质是两个L2归一化后向量的点积。对于单个向量,自身点积结果为1.0(符合预期);对于两个不同向量,归一化后计算点积即可得到相似度。

完整示例代码:

import org.ojalgo.matrix.store.PhysicalStore;
import org.ojalgo.matrix.store.Primitive32Store;
import org.ojalgo.vector.VectorEnums;

import java.util.Arrays;

public class VectorCosineSimilarity {
    public static void main(String[] args) {
        PhysicalStore.Factory<Float, Primitive32Store> storeFactory = Primitive32Store.FACTORY;
        
        // 第一个向量字符串
        String vecStr1 = "0.47058824,0.5647059,0.54901963,0.54509807,0.54901963";
        Primitive32Store vec1 = storeFactory.rows(Arrays.stream(vecStr1.split(","))
                .mapToFloat(Float::parseFloat)
                .toArray());
        
        // 第二个向量字符串(与vec1相同,预期相似度为1.0)
        String vecStr2 = "0.47058824,0.5647059,0.54901963,0.54509807,0.54901963";
        Primitive32Store vec2 = storeFactory.rows(Arrays.stream(vecStr2.split(","))
                .mapToFloat(Float::parseFloat)
                .toArray());
        
        // 对两个向量分别做L2归一化
        vec1.modifyAny(v -> v.normalise(VectorEnums.NORM.L2));
        vec2.modifyAny(v -> v.normalise(VectorEnums.NORM.L2));
        
        // 计算点积,得到余弦相似度
        float cosSimilarity = vec1.multiply(vec2.transpose()).get(0, 0);
        
        System.out.println("余弦相似度:" + cosSimilarity);
    }
}

关键说明

  • L2归一化会将向量的长度缩为1,此时两个向量的点积等于它们之间的余弦值;
  • 用float直接解析并存储,既匹配Primitive32Store的类型,也避免了double转float带来的精度冗余;
  • 该逻辑可直接扩展到128维向量,ojAlgo会自动处理高维度矩阵运算。

内容的提问来源于stack exchange,提问作者219CID

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 23:14:51