ES 7.X 版本引入了向量类型dense_vector,用于存储浮点类型的密集向量,其最大维度为2048。其用作是可以将待查询向量和文档内存储向量之间的距离作为查询评分使用,即越相似的向量评分越高。使用方式为在 query 的script_score中指定向量的计算方式,具体有四种:

cosineSimilarity – 余弦函数
dotProduct – 向量点积
l1norm – 曼哈顿距离
l2norm - 欧几里得距离

  1. 创建含有dense_vector的索引用于测试,建表如下:
PUT caster_vector
{
  "settings": {
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "my_vector": {
        "type": "dense_vector",
        "dims": 2
      },
      "my_text": {
        "type": "keyword"
      }
    }
  }
}
  1. 插入测试数据:
POST caster_vector/_doc
{
  "my_text" : "text1",
  "my_vector" : [1, 1]//[1, -1],[1, 0],[0, 1],[0, -1],[-1, 1],[-1, 0],[-1, -1]
}

注:不要插入 [0,0] 向量,查询时会由于无法计算而报错。

  1. 采用cos方式计算,运行查询语句,查询的向量为 [1,1]:
GET caster_vector/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": "cosineSimilarity(params.query_vector, 'my_vector') + 1.0",//加1规避分数为负数
        "params": {
          "query_vector": [
            1,
            1
          ]
        }
      }
    }
  }
}

(注:因为向量函数cosineSimilarity会线性扫描全部匹配文档,线上尽量使用query过滤出较少的文档进行计算以节约时间。)
结果分数如下:

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 8,
      "relation" : "eq"
    },
    "max_score" : 2.0,
    "hits" : [
      {
        "_index" : "caster_vector",
        "_type" : "_doc",
        "_id" : "57DwuXsBSCW7lcY4Ic7I",
        "_score" : 2.0,
        "_source" : {
          "my_text" : "text1",
          "my_vector" : [
            1,
            1
          ]
        }
      },
......
      {
        "_index" : "caster_vector",
        "_type" : "_doc",
        "_id" : "L7DwuXsBSCW7lcY4adYc",
        "_score" : 0.29289323,
        "_source" : {
          "my_text" : "text1",
          "my_vector" : [
            -1,
            0
          ]
        }

查询向量 [1,1] 和文档内向量 [1,1] 夹角为0°,cos值为1,结果为1+1=2。
查询向量 [1,1] 和文档内向量 [-1,0] 夹角为135°,cos值为-0.707x,结果为-0.707x+1=0.292x。

  1. 采用向量点积方式计算,运行查询语句,查询的向量为 [1,1]:
GET caster_vector/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": """
          double value = dotProduct(params.query_vector, 'my_vector');
//          return value+2.0;
          return sigmoid(1, Math.E, -value); 
        """,
        "params": {
          "query_vector": [
            1,
            1
          ]
        }
      }
    }
  }
}

这里使用了sigmoid函数对点积结果进行了0~1之间的平滑映射,规避负值。与直接点积效果(针对当前情况采取加2转正方式)对比如下:

向量点积sigmoid
[1,1]2+2=40.8807971
[1,0]1+2=30.7310586
[1,-1]0+2=20.5
[0,-1]-1+2=10.26894143
[-1,-1]-2+2=00.11920292

可以看出经过sigmoid函数评分映射为0~1之间且效果与直接点积近似。

  1. 采用曼哈顿距离方式计算,运行查询语句,查询的向量为 [1,1]:
GET caster_vector/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": "1 / (1 + l1norm(params.queryVector, 'my_vector'))",
        "params": {
          "queryVector": [
            1,
            1
          ]
        }
      }
    }
  }
}

曼哈顿距离计算二维向量方式为: ∣ x 1 − x 2 ∣ |x1-x2| x1x2+ ∣ y 1 − y 2 ∣ |y1-y2| y1y2,计算结果越小越相关。ES 采取加一取倒数方式表示得分情况。计算结果对比如下:

向量曼哈顿距离加一取倒数
[1,1]01
[1,0]10.5
[1,-1]20.33333334
[0,-1]30.25
[-1,-1]40.2
  1. 采用欧几里得距离方式计算,运行查询语句,查询的向量为 [1,1]:
GET caster_vector/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": "1 / (1 + l2norm(params.queryVector, 'my_vector'))",
        "params": {
          "queryVector": [
            1,
            1
          ]
        }
      }
    }
  }
}

欧几里得距离计算二维向量方式为: ( x 1 − y 1 ) 2 + ( x 2 − y 2 ) 2 \sqrt{(x1-y1)^2+(x2-y2)^2} (x1y1)2+(x2y2)2 ,计算结果越小越相关。ES 采取加一取倒数方式表示得分情况。计算结果对比如下:

向量欧几里得距离加一取倒数
[1,1]01
[1,0]10.5
[1,-1]20.33333334
[0,-1] 5 \sqrt{5} 5 0.309017
[-1,-1]2* 2 \sqrt{2} 2 0.26120389
  1. 你也可以通过doc[<field>].vectorValuedoc[<field>].magnitude获取向量数组和向量大小进行自己的计算方法。
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐