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后该现象消失)。
需要解决:
- 如何确保string->float[]->Primitive32Store转换时保留原始精度,不新增多余小数位?
- 如何正确对向量进行归一化并计算余弦相似度?
解决方案
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
相关产品推荐
相关产品推荐

