在深度学习和人工智能应用中,二维矩阵向量 (如图像特征、时间序列数据、多模态嵌入)的搜索需求日益增长。然而,Elasticsearch 8(ES8)的 dense_vector 字段类型默认仅支持一维向量,无法直接存储或搜索二维矩阵。那么,如何在 ES8 中实现高效的二维矩阵向量搜索呢?
本文将介绍 3 种可行方案 ,包括 矩阵展平(Flatten)、多字段存储、外部计算,并对比它们的优缺点,帮助你选择最适合业务场景的方法。
1. 为什么 ES8 不直接支持二维矩阵向量?
ES8 的 dense_vector 基于 HNSW(Hierarchical Navigable Small World) 算法实现近似最近邻搜索(ANN),该算法针对一维向量优化,直接支持二维矩阵会大幅增加计算复杂度。此外,大多数深度学习模型(如 BERT、ResNet、CLIP)的输出已经是扁平化的一维向量(如 768 维),因此 ES8 的设计已能满足主流需求。
但如果你需要处理真正的二维矩阵 (如 n×m 的图像特征、时间序列窗口),则需要通过间接方法实现。
2. 方案 1:矩阵展平(Flatten)为一维向量
2.1 原理
将二维矩阵按行或列展开成一维向量,然后存储到 dense_vector 中。例如:
原始矩阵(2×2):
[
[0.1, 0.2],
[-0.5, 0.3]
]
展平后:
[0.1, 0.2, -0.5, 0.3] // 4 维向量
2.2 适用场景
- 矩阵维度较小(如 ≤1024 维)。
- 矩阵的行/列顺序对语义无影响(如图像像素矩阵展平会丢失空间信息,但文本/音频特征可能不受影响)。
- 需要兼容 ES8 原生向量搜索功能(如
knn查询)。
2.3 代码示例
(1)生成矩阵并展平(Python)
python
import numpy as np
# 生成一个 2x2 随机矩阵
matrix = np.random.rand(2, 2)
flattened = matrix.flatten().tolist() # 展平为 [0.1, 0.2, -0.5, 0.3]
print("展平后的向量:", flattened)
(2)索引到 ES8
json
PUT /matrix_data
{
"mappings": {
"properties": {
"name": { "type": "text" },
"embedding": { "type": "dense_vector", "dims": 4 } // 维度必须匹配展平后的长度
}
}
}
POST /matrix_data/_doc/1
{
"name": "Sample Matrix",
"embedding": [0.1, 0.2, -0.5, 0.3] // 展平后的矩阵
}
(3)搜索相似矩阵
python
from elasticsearch import Elasticsearch
es = Elasticsearch("http://localhost:9200")
# 生成查询矩阵并展平
query_matrix = np.random.rand(2, 2)
query_flattened = query_matrix.flatten().tolist()
# 执行 KNN 搜索
response = es.search(
index="matrix_data",
query={
"knn": {
"embedding": {
"vector": query_flattened,
"k": 3,
"similarity": "cosine" # 或 "l2_norm"(欧氏距离)
}
}
}
)
print("搜索结果:", response["hits"]["hits"])
2.4 优缺点
| 优点 | 缺点 |
|---|---|
| 简单高效,兼容 ES8 原生功能 | 丢失矩阵的行列结构信息(如图像像素的空间关系) |
| 适用于大多数深度学习模型的输出 | 高维矩阵(如 1000×1000)展平后索引体积大 |
3. 方案 2:多字段存储(多个 dense_vector)
3.1 原理
如果矩阵的每一行/列代表独立语义(如用户画像的"兴趣特征"和"行为特征"),可以拆分为多个 dense_vector 字段,分别搜索后合并结果。
3.2 适用场景
- 矩阵的行/列有明确语义划分(如多组特征)。
- 需要对不同部分加权搜索(如
row1权重更高)。
3.3 代码示例
(1)定义映射(Mappings)
json
PUT /multi_field_matrix
{
"mappings": {
"properties": {
"name": { "type": "text" },
"row1": { "type": "dense_vector", "dims": 2 }, // 第一行
"row2": { "type": "dense_vector", "dims": 2 } // 第二行
}
}
}
(2)索引数据
json
POST /multi_field_matrix/_doc/1
{
"name": "Multi-field Matrix",
"row1": [0.1, 0.2],
"row2": [-0.5, 0.3]
}
(3)搜索时合并结果
python
# 查询两行分别搜索,然后合并结果(需业务逻辑处理)
response_row1 = es.search(
index="multi_field_matrix",
query={
"knn": {
"row1": {
"vector": [0.15, 0.25], # 查询第一行
"k": 3,
"similarity": "cosine"
}
}
}
)
response_row2 = es.search(
index="multi_field_matrix",
query={
"knn": {
"row2": {
"vector": [-0.4, 0.4], # 查询第二行
"k": 3,
"similarity": "cosine"
}
}
}
)
# 合并结果(示例:取交集或加权平均)
print("Row1 结果:", response_row1["hits"]["hits"])
print("Row2 结果:", response_row2["hits"]["hits"])
3.4 优缺点
| 优点 | 缺点 |
|---|---|
| 保留矩阵的行列语义 | 需要额外逻辑合并结果 |
| 支持对不同部分加权 | 查询复杂度增加(需多次搜索) |
4. 方案 3:外部计算(Frobenius 范数相似度)
4.1 原理
如果必须保留矩阵结构,可以:
- 在索引前计算矩阵间的相似度(如 Frobenius 范数),将结果存储为标量字段。
- 用
script_score或range查询过滤相似矩阵。
4.2 适用场景
- 矩阵维度较高(如 100×100),展平不现实。
- 需要精确计算矩阵相似度(而非近似搜索)。
4.3 代码示例
(1)计算 Frobenius 相似度
python
import numpy as np
def matrix_similarity(matrix1, matrix2):
return 1 / (1 + np.linalg.norm(matrix1 - matrix2)) # 相似度 ∈ [0, 1]
# 生成两个矩阵
A = np.array([[0.1, 0.2], [-0.5, 0.3]])
B = np.array([[0.2, 0.1], [-0.4, 0.4]])
similarity = matrix_similarity(A, B)
print("Frobenius 相似度:", similarity)
(2)索引数据(存储相似度)
json
PUT /external_matrix
{
"mappings": {
"properties": {
"name": { "type": "text" },
"matrix": { "type": "object" }, # 存储原始矩阵(可选)
"similarity_score": { "type": "double" } # 存储相似度
}
}
}
POST /external_matrix/_doc/1
{
"name": "Matrix A",
"matrix": { "row1": [0.1, 0.2], "row2": [-0.5, 0.3] },
"similarity_score": 0.95 # 假设与某个查询矩阵的相似度
}
(3)搜索相似矩阵
python
# 假设查询矩阵的相似度阈值为 0.9
response = es.search(
index="external_matrix",
query={
"range": {
"similarity_score": { "gte": 0.9 }
}
}
)
print("相似矩阵:", response["hits"]["hits"])
4.4 优缺点
| 优点 | 缺点 |
|---|---|
| 保留矩阵结构信息 | 需要预先计算所有相似度(计算量大) |
| 适用于高维矩阵 | 无法利用 ES8 的 ANN 加速 |
5. 方案对比与选型建议
| 方案 | 适用场景 | 搜索效率 | 实现复杂度 |
|---|---|---|---|
| 矩阵展平 | 维度小、语义无关的矩阵 | ⭐⭐⭐⭐⭐(原生 ANN) | ⭐(最简单) |
| 多字段存储 | 行/列有独立语义的矩阵 | ⭐⭐⭐(多次查询) | ⭐⭐(需合并结果) |
| 外部计算 | 高维矩阵、需精确相似度 | ⭐(范围查询) | ⭐⭐⭐(需预计算) |
推荐选型
- 优先矩阵展平:如果矩阵维度 ≤1024 且无需保留结构信息。
- 多字段存储:如果矩阵的行/列有独立语义(如多组特征)。
- 外部计算:如果矩阵维度极高(如 1000×1000)且需精确相似度(但建议改用专用向量数据库如 Milvus)。
6. 总结
Elasticsearch 8 的 dense_vector 虽不直接支持二维矩阵向量搜索,但通过 矩阵展平、多字段存储、外部计算 3 种方案,可以间接实现不同场景的需求。
- 展平矩阵是最简单高效的方式,适用于大多数深度学习模型的输出。
- 多字段存储适合需要保留行列语义的场景。
- 外部计算适合高维矩阵,但计算成本较高。
如果你的业务涉及大规模矩阵搜索,建议评估 专用向量数据库(如 Milvus、FAISS),它们针对矩阵优化,能提供更高效的搜索性能。
希望本文能帮助你解决 ES8 中的二维矩阵搜索问题!如果有任何疑问,欢迎留言讨论。 🚀