作为一家搜索引擎公司,我们会很倚赖 ES
帮忙处理包括文章召回,数据源划分,实体、标签管理等任务,而且都收到了不错的结果。
最近我们需要对行业知识库进行建模,其中可能会涉及到实体匹配、模糊搜索、向量搜索等多种召回和算分方式,最终我们选择了通过 ES 7.X
(最终选择 7.10)里的新功能,Dense vector 帮忙一起完成这部分的需求。
query
理解结果构建的 query
语句进行数据召回在确定了数据的使用场景我们确定了数据结构中,大致会包含以下一些字段
为了能支持上述的使用需求,我们对比了包括 ES
、Faiss
等多种解决方案。其中,Faiss
和 SPTAG
只是核心算法库,需要进行二次开发包装成服务;Milvus
的 1.x
版本中只能存储 id
和 向量
,不能完整的满足我们的使用需求;基于集群稳定性和可维护性等考虑,相对于后置插件的部署,我们更倾向于使用 ES
的原生功能,所以选择 ES
的原生向量搜索功能作为我们的最终选择。
对比参考:
种类 | 实现语言 | 客户端支持 | 多条件召回 | 学习成本 | 引入成本 | 运维成本 | 分布式 | 性能 | 社区 | 备注 |
---|---|---|---|---|---|---|---|---|---|---|
Elasticsearch | Java | Java/Python | yes | 低 | 低 | 中 | yes | 中 | 活跃 | 原生功能 |
Faiss | Python | Python | no | 中 | 高 | 高 | no | 高 | 一般 | 需要二次开发 |
Milvus | Python + GoLang | Python/Java/GoLang | no | 中 | 中 | 中 | no | 高 | 一般 | 1.x 功能不全 |
OpenDistro Elasticsearch KNN | Java + C++ | Java/Python | yes | 中 | 中 | 中 | yes | 中 | 一般 | 内置插件 |
SPTAG | C++ | Python + C# | no | 高 | 中 | 中 | no | 高 | 一般 | 需要二次开发 |
ES
query
理解模块进行检索条件解析ES
中进行搜索Settings
:{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2,
"index": {
"routing": {
"allocation": {
"require": {
"node_group": "hot" // 1)
}
}
},
"store": {
"preload": [ // 2)
"knowledge",
"category",
"available",
"confidence",
"del",
"kid"
]
},
"search": {
"slowlog": {
"threshold": {
"query": {
"warn": "1s" // 3)
},
"fetch": {
"warn": "1s" // 3)
}
}
}
},
"translog": {
"flush_threshold_size": "512mb", // 4)
"sync_interval": "5m", // 4)
"durability": "async" // 4)
},
"sort": {
"field": [ // 5)
"kid",
"confidence"
],
"order": [ // 5)
"asc",
"desc"
]
}
}
}
}
translog
的提交间隔拉长,加快写入速度kid
是自增 id
,同时可能会对知识的置信度做排序等,所以会使用 sort field
存储这两个字段Mapping
:{
"mappings": {
"properties": {
"kid": {
"type": "keyword"
},
"knowledge": {
"type": "keyword"
},
"knowledge_phrase": { // 1)
"type": "text",
"analyzer": "faraday"
},
"attribue": { // 1)
"type": "keyword",
"fields": {
"phrase": {
"type": "text",
"analyzer": "faraday"
}
}
},
"value": { // 1)
"type": "keyword",
"fields": {
"phrase": {
"type": "text",
"analyzer": "faraday"
}
}
},
"confidence": { // 2)
"type": "double"
},
"category": {
"type": "keyword"
},
"vector": { // 3)
"type": "dense_vector",
"dims": 512
},
"ref": {
"type": "text",
"index": false
},
"available": {
"type": "keyword"
},
"del": {
"type": "keyword"
},
"create_timestamp": {
"type": "date",
"format": [
"strict_date_hour_minute_second",
"yyyy-MM-dd HH:mm:ss"
]
},
"update_timestamp": {
"type": "date",
"format": [
"strict_date_hour_minute_second",
"yyyy-MM-dd HH:mm:ss"
]
}
}
}
}
farady
分词器对知识条目的各部分进行了分词处理512 位
的向量存在这个字段中模型A
从文章中找到知识条目模型B
将知识条目转化成向量
模型A
模型B
为自研模型,运用了包括知识密度计算等算法以及 bert
tersonflow
等框架ES
query 理解
组件进行分析query
里的分类信息等意图之后,构建用来召回的向量和相关的筛选条件ES
的 query
条件对知识库进行筛选,并配合置信度等对结果进行调整POST knowledge_current_reader/_search
{
"query": {
"script_score": {
"query": {
"bool": {
"filter": [
{
"term": {
"del": 0
}
},
{
"term": {
"available": 1
}
}
],
"must": {
"bool": {
"should": [
{
"term": {
"category": "type_1",
"boost": 10
}
},
{
"term": {
"category": "type_2",
"boost": 5
}
}
]
}
},
"should": [
{
"match_phrase": {
"knowledge_phrase": {
"query": "some_query",
"boost": 10
}
}
},
{
"match": {
"attribute": {
"query": "some_query",
"boost": 5
}
}
},
{
"match": {
"value": {
"query": "some_query",
"boost": 5
}
}
},
{
"term": {
"knowledge": {
"value": "some_query",
"boost": 30
}
}
},
{
"term": {
"attribute": {
"value": "some_query",
"boost": 15
}
}
},
{
"term": {
"value": {
"value": "some_query",
"boost": 10
}
}
}
]
}
},
"script": {
"source": "cosineSimilarity(params.query_vector, 'vector') + sigmoid(1, Math.E, _score) + (1 / Math.log(doc['confidence'].value))",
"params": {
"query_vector": [ ... ]
}
}
}
}
}
query
的条件、参数仅做示意,属于实际线上使用的脱敏、简化版pipeline
中进行处理,简化了其中边界条件处理和判断部分逻辑由于需要进行向量计算,ES
需要耗费大量时间、资源做距离计算,为此我们进行了以下一些优化:
bert
框架输出的向量位数label
的精度由16
位截取为5
位小数X%
),但是大大降低了存取和计算时间(约 Y%
)query
之前预先对意图、可能分类进行分析
query
组装之前对原始 query
内容进行分析query
和分类进行不同权重的匹配X%
),但增加了准确性(约 Y%
),同时也提高了部分计算效率(约 Z%
)ES
需要处理的运算逻辑ES
的响应时间(约 X%
),同时通过外置的打分公式调整,间接的提高了准确性(约 Y%
)由于知识条目是通过算法进行抽取的,而且知识还会存在一定的时效性,可能造成知识的不准确等问题,为此我们进行了以下一些优化:
X
批次的迭代,将知识的正确性从 Y%
提高到了 Z%
的
)差异的知识条目进行过滤、合并X
类目的 Y
条专家知识,同时经过人工干预了大概 Z%
的知识条目,将知识的正确性从 W%
提高到了 K%
本文依托我们公司的使用场景,对围绕 ES
向量字段(Dense vector
)构建的一个系统进行了大致描述,同时对一些常见问题及解决方案进行了阐述。目前该方案支持了我们对于知识库的相关搜索功能,相较于之前的纯基于实体识别和 ngram
匹配的方案整体准确率和召回率都有将近两位数百分比的提升。未来我们会对整个系统的响应速度、稳定性进行提升,并对知识库的构建效率以及知识的准确性持续进行迭代。