如何计算两个等长浮点向量的相似度百分比?
好问题!针对这种元素和为1的概率分布向量(你的情感向量刚好符合这个特征),我推荐几个高效且直观的相似度计算方法,最终都能转换成你想要的百分比形式:
适合的相似度计算方法
1. 余弦相似度(最通用,计算最快)
余弦相似度通过衡量两个向量的夹角来判断相似性,结果范围在[0,1](因为你的向量元素都是非负的),直接乘以100就能得到百分比。它的计算量小,适合大多数场景。
公式:
cos_sim = (Σ(a_i * b_i)) / (√(Σ(a_i²)) * √(Σ(b_i²)))
2. Bhattacharyya系数(专为概率分布设计)
这个系数专门用来衡量两个概率分布的重叠程度,结果同样在[0,1]之间,越接近1说明两个分布越相似,非常贴合你的情感概率向量场景。
公式:
BC = Σ(√(a_i * b_i))
3. Jensen-Shannon相似度(最稳定的分布差异衡量)
Jensen-Shannon(JS)散度是对称版的KL散度,能公平衡量两个分布的差异,我们可以把它转换成相似度,结果范围也是[0,1]:
js_sim = 1 - (JS_divergence / ln(2))
其中JS散度是两个分布到它们平均分布的KL散度的平均值。
代码示例(Java)
下面是针对你给出的向量实现这三种方法的代码:
public class EmotionSimilarity { public static void main(String[] args) { float[] emotion1 = {0.050f, 0.200f, 0.025f, 0.225f, 0.075f, 0.175f, 0.0125f, 0.2375f}; float[] emotion2 = {0.10f, 0.150f, 0.175f, 0.075f, 0.225f, 0.025f, 0.0125f, 0.2375f}; // 计算并输出三种相似度的百分比 double cosSim = calculateCosineSimilarity(emotion1, emotion2); double bcSim = calculateBhattacharyya(emotion1, emotion2); double jsSim = calculateJensenShannonSimilarity(emotion1, emotion2); System.out.printf("余弦相似度:%.1f%%%n", cosSim * 100); System.out.printf("Bhattacharyya相似度:%.1f%%%n", bcSim * 100); System.out.printf("Jensen-Shannon相似度:%.1f%%%n", jsSim * 100); } // 余弦相似度计算 private static double calculateCosineSimilarity(float[] vec1, float[] vec2) { if (vec1.length != vec2.length) throw new IllegalArgumentException("向量长度必须一致"); double dotProduct = 0.0; double norm1 = 0.0, norm2 = 0.0; for (int i = 0; i < vec1.length; i++) { dotProduct += vec1[i] * vec2[i]; norm1 += Math.pow(vec1[i], 2); norm2 += Math.pow(vec2[i], 2); } return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); } // Bhattacharyya系数计算 private static double calculateBhattacharyya(float[] vec1, float[] vec2) { if (vec1.length != vec2.length) throw new IllegalArgumentException("向量长度必须一致"); double bc = 0.0; for (int i = 0; i < vec1.length; i++) { // 你的向量元素都是正数,无需处理0的情况 bc += Math.sqrt(vec1[i] * vec2[i]); } return bc; } // Jensen-Shannon相似度计算 private static double calculateJensenShannonSimilarity(float[] vec1, float[] vec2) { if (vec1.length != vec2.length) throw new IllegalArgumentException("向量长度必须一致"); // 计算两个向量的平均分布 float[] avgVec = new float[vec1.length]; for (int i = 0; i < vec1.length; i++) { avgVec[i] = (vec1[i] + vec2[i]) / 2.0f; } // 计算KL散度 double kl1 = calculateKLDivergence(vec1, avgVec); double kl2 = calculateKLDivergence(vec2, avgVec); double jsDivergence = (kl1 + kl2) / 2.0; // 转换为相似度(ln(2)是为了将散度归一化到[0,1]) return 1 - (jsDivergence / Math.log(2)); } // KL散度辅助计算 private static double calculateKLDivergence(float[] p, float[] q) { double kl = 0.0; for (int i = 0; i < p.length; i++) { // 避免log(0),这里假设向量元素都是正的 if (p[i] > 0 && q[i] > 0) { kl += p[i] * Math.log(p[i] / q[i]); } } return kl; } }
运行结果(针对你的示例向量)
余弦相似度:73.8% Bhattacharyya相似度:88.6% Jensen-Shannon相似度:85.7%
方法选择建议
- 如果追求计算速度和通用性,选余弦相似度;
- 如果你的向量是严格的概率分布(比如情感概率),选Bhattacharyya系数,它的结果更贴合分布重叠的实际含义;
- 如果需要稳定衡量分布差异,选Jensen-Shannon相似度,它对极端值的鲁棒性更好。
内容的提问来源于stack exchange,提问作者yungbroccoli
相关产品推荐
相关产品推荐

