向量方向相近
(夹角 < 90°)
向量正交
(夹角 = 90°)
向量方向相反
(夹角 > 90°)
| 维度 i | vec1[i] (句子1) |
vec2[i] (句子2) |
乘积 Aᵢ × Bᵢ |
累加和 |
|---|
/** * 计算点积(内积) * * @param vec1 向量1 (768维) * @param vec2 向量2 (768维) * @return 点积值 (范围: (-∞, +∞)) */ public static double dotProduct(float[] vec1, float[] vec2) { if (vec1.length != vec2.length) { throw new IllegalArgumentException("向量维度不匹配"); } double result = 0.0; // 遍历所有维度(768维)- 最简单高效的计算 for (int i = 0; i < vec1.length; i++) { result += vec1[i] * vec2[i]; // 只需乘法和加法 } return result; } /** * 点积与余弦相似度的关系 * cosine = dotProduct / (||A|| × ||B||) */ public static double cosineSimilarityFromDotProduct(float[] vec1, float[] vec2) { double dot = dotProduct(vec1, vec2); double norm1 = Math.sqrt(dotProduct(vec1, vec1)); // ||A|| = √(A·A) double norm2 = Math.sqrt(dotProduct(vec2, vec2)); // ||B|| = √(B·B) return dot / (norm1 * norm2); } /** * 对于归一化向量,点积就等于余弦相似度 * 因为 ||A|| = ||B|| = 1 */ public static double dotProductNormalized(float[] vec1, float[] vec2) { // 假设向量已经归一化 // 此时 dot(A, B) = ||A|| × ||B|| × cos(θ) = 1 × 1 × cos(θ) = cos(θ) return dotProduct(vec1, vec2); } /** * 使用 SIMD 优化的点积(性能优化版本) * 实际生产中可以使用 Java Vector API (JDK 16+) */ public static double dotProductOptimized(float[] vec1, float[] vec2) { double sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; // 4路并行累加 int i = 0; int limit = vec1.length - 3; // 循环展开,利用 CPU 流水线 for (; i < limit; i += 4) { sum0 += vec1[i] * vec2[i]; sum1 += vec1[i+1] * vec2[i+1]; sum2 += vec1[i+2] * vec2[i+2]; sum3 += vec1[i+3] * vec2[i+3]; } // 处理剩余元素 for (; i < vec1.length; i++) { sum0 += vec1[i] * vec2[i]; } return sum0 + sum1 + sum2 + sum3; }
| 对比维度 | 点积 (Dot Product) | 余弦相似度 (Cosine) | 欧氏距离 (Euclidean) |
|---|---|---|---|
| 公式 | Σ(Aᵢ × Bᵢ) | (A · B) / (||A|| × ||B||) | √(Σ(Aᵢ - Bᵢ)²) |
| 值域 | (-∞, +∞) | [-1, 1] | [0, +∞) |
| 考虑因素 | 方向 + 长度 | 仅方向 | 绝对位置差异 |
| 计算复杂度 | O(n) - 最简单 | O(n) - 需要额外计算模长 | O(n) - 需要开方 |
| 归一化向量 | 点积 = 余弦相似度 | 与点积相同 | d² = 2 - 2×cosine |
| 优点 | • 计算最快 • 同时考虑方向和长度 • 适合推荐系统 |
• 不受长度影响 • 值域有界 • 适合文本检索 |
• 直观易懂 • 绝对距离 • 适合聚类 |
| 缺点 | • 值域无界 • 受长度影响大 • 不同量纲难比较 |
• 忽略向量大小 • 计算稍慢 |
• 受长度影响 • 维度诅咒 |
| ES 配置 | similarity: "dot_product" | similarity: "cosine" | similarity: "l2_norm" |
| 场景 | 推荐度量 | 原因 |
|---|---|---|
| 推荐系统(用户-物品) | ✅ 点积 | 用户偏好强度和物品特征强度都重要 |
| 归一化后的语义检索 | ✅ 点积 | 等价于余弦相似度,但计算更快 |
| Maximum Inner Product Search (MIPS) | ✅ 点积 | 专门针对点积优化的检索算法 |
| 注意力机制 (Attention) | ✅ 点积 | Transformer 中 Q·K 使用点积 |
| 文本语义检索(未归一化) | ✅ 余弦相似度 | 文档长度不应影响相似度 |
| 聚类分析 | ✅ 欧氏距离 | 需要绝对距离度量 |
dot_product 比 cosine 更快dot_product 时,必须先将向量归一化!
否则点积值可能超出预期范围,影响评分。
// 1. 创建索引时指定 similarity 为 dot_product PUT /my_index { "mappings": { "properties": { "title_vector": { "type": "dense_vector", "dims": 768, "index": true, "similarity": "dot_product" ← 使用点积 } } } } // 2. 索引文档时,必须使用归一化的向量! POST /my_index/_doc { "title": "Java 教程", "title_vector": [0.1, 0.2, ...] // 归一化后的 768 维向量 } // 3. kNN 搜索 POST /my_index/_search { "knn": { "field": "title_vector", "query_vector": [0.1, 0.2, ...], // 同样需要归一化! "k": 10, "num_candidates": 100 } } // Elasticsearch 的评分转换: // 对于归一化向量,点积范围是 [-1, 1] // ES 将其转换为:score = (1 + dot_product) / 2 // 这样评分范围变成 [0, 1]
| 度量方式 | 计算操作 | 相对速度 |
|---|---|---|
| dot_product | n 次乘法 + (n-1) 次加法 | ⚡ 最快 |
| cosine | 3n 次乘法 + 3(n-1) 次加法 + 2 次开方 + 1 次除法 | 中等 |
| l2_norm | n 次减法 + n 次乘法 + (n-1) 次加法 + 1 次开方 | 中等 |
dot_product,计算最快cosinel2_norm