- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
又两周过去了,本qiang~依然奋斗在上周提到的项目KBQA集成LLM,感兴趣的可通过传送门查阅先前的文章《LLM应用实战:当KBQA集成LLM》.
本次又有什么更新呢?主要是针对上次提到的缺点进行优化改进。主要包含如下方面:
1. 数据落库 。
上次文章提到,KBQA服务会将图谱的概念、属性、实体、属性值全部加载到内存,所有的查询均在内存中进行,随之而来的问题就是如果图谱的体量很大呢,那内存不爆了么… 。
2. 支持基于属性值查实体 。
上篇文章不支持属性值查找实体,比如”最会照顾宝宝的是什么龙”,”什么龙是大龙和大龙生活,小龙和小龙生活”。本次已经此问题优化.
此篇文章是对这两周工作的一个整体总结,其中包含部分工程层面的优化.
。
。
整体框架和上篇大致相同,不同之处在于:
1. 对齐模块:先前是基于SIM筛选候选实体,本次基于ES进行候选实体召回 。
2. 解析模块:先前是基于hugegraph和内存中的实体信息进行解析,本次优化为基于hugegraph和elasticsearch 。
由于需要支撑语义相似度检索,因此数据库选型为Milvus与Elasticsearch.
二者之间的比对如下:
。 |
。 |
Milvus 。 |
Elastic 。 |
。 。 。 扩展性层面 。 |
存储和计算分离 。 |
✅ 。 |
❌ 。 |
查询和插入分类 。 |
组件级别支持 。 |
服务器层面支持 。 |
|
多副本 。 |
✅ 。 |
✅ 。 |
|
动态分段 vs 静态分片 。 |
动态分段 。 |
静态分片 。 |
|
云原生 。 |
✅ 。 |
✅ 。 |
|
十亿级规模向量支持 。 |
✅ 。 |
❌ 。 |
|
。 。 。 功能性层面 。 |
权限控制 。 |
✅ 。 |
✅ 。 |
磁盘索引支撑 。 |
✅ 。 |
❌ 。 |
|
混合搜索 。 |
✅ 。 |
✅ 。 |
|
分区/命名空间/逻辑组 。 |
✅ 。 |
❌ 。 |
|
索引类型 。 |
11个(FLAT, IVF_FLAT, HNSW)等 。 |
1个(HNSW) 。 |
|
多内存索引支持 。 |
✅ 。 |
❌ 。 |
|
。 。 。 专门构建层面 。 |
为向量而设计 。 |
✅ 。 |
❌ 。 |
可调一致性 。 |
✅ 。 |
❌ 。 |
|
流批向量数据支持 。 |
✅ 。 |
✅ 。 |
|
二进制向量支持 。 |
✅ 。 |
✅ 。 |
|
多语言SDK 。 |
python, java, go, c++, node.js, ruby 。 |
python, java, go, c++, node.js, ruby, Rust, C#, PHP, Perl 。 |
|
数据库回滚 。 |
✅ 。 |
❌ 。 |
但由于Milvus针对国产化环境如华为Atlas适配不佳,而Es支持国产化环境,因此考虑到环境通用性,选择Es,且其文本搜索能力较强.
由于知识图谱的概念、属性一般量级较少,而实体数随着原始数据的丰富程度客场可短。因此将实体及其属性值在Es中进行存储.
针对KBQA集成LLM的场景,有两块内容会涉及语义搜索召回.
1. 对齐prompt中的候选实体 。
2. 解析模块中存在需要基于属性值查询实体的情况.
3. 涉及到数值类型的查询,如大于xx,最大,最小之类.
综合考虑,将Es的index结构设计如下:
属性 。 |
含义 。 |
类型 。 |
备注 。 |
name 。 |
实体名 。 |
keyword 。 |
。 |
concepts 。 |
所属概念 。 |
keyword 。 |
一个实体可能存在多个概念 。 |
property 。 |
属性 。 |
keyword 。 |
属性名称 。 |
value 。 |
属性值 。 |
text 。 |
ik分词器进行分词 。 |
numbers 。 |
数值属性值 。 |
double_range 。 |
会存在一个区间范围 。 |
embeddings 。 |
向量 。 |
elastiknn_dense_float_vector 。 |
1. 非数值属性对应value的向量 。 2. 使用elastiknn插件 。 |
项目使用的Es版本是8.12.2,原因是elastiknn插件和Ik插件针对该版本均支持,且8.12.2版本是当前阶段的次新版本.
# 拉取镜像(最好先设置国内镜像加入) docker pull elasticsearch:8.12.2 # es容器启动,存在SSL鉴权 docker run -d --name es01 --net host -p 9200:9200 -it -e "ES_JAVA_OPTS=-Xms1024m -Xmx1024m" elasticsearch:8.13.2 # 容器中拉取需要鉴权的信息到本地 docker cp es01:/usr/share/elasticsearch/config/certs/http_ca.crt . chmode 777 http_ca.crt # 密码第一次启动的日志中有,需要保存下来 export ELASTIC_PASSWORD=xxxxxx # 验证es是否启动成功 curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD https://localhost:9200
elastiknn插件是为了优化ES自身的向量检索性能,安装此插件后,ES的向量检索性能会提升数倍,如果再增加SSD固态硬盘,性能会进一步提升数倍.
#下载插件包 wget https://github.com/alexklibisz/elastiknn/releases/download/8.12.2.1/elastiknn-8.12.2.1.zip # 导入容器中指定目录 docker cp elastiknn-8.12.2.1.zip es01:/usr/share/elasticsearch/ # 进入容器,默认目录即为/usr/share/elasticsearch/ docker exec -it es01 bash # 安装插件 elasticsearch-plugin install file:elastiknn-8.12.2.1.zip # 退出,重启容器 docker restart es01 # 验证 # 创建mapping curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD -XPOST https://localhost:9200/test/_mapping -H 'Content-Type:application/json' -d ' { "properties": { "embeddings": { "type": "elastiknn_dense_float_vector", "elastiknn": { "model": "lsh", "similarity": "cosine", "dims": 768, "L": 99, "k": 3 } } } }' # 验证mapping是否生效 curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD -XGET https://localhost:9200/test/_mapping?pretty
采坑总结:
1. elastiknn插件导入始终无法安装,且报错.
解决:
(1) 一定要注意,安装es插件需要指定路径,且增加”file:” 的前缀,不加此前缀,那就等着报错吧 。
(2) 拷贝到容器内部,一定要注意,不要将elastiknn-8.12.2.1.zip拷贝至/usr/share/elasticsearch/plugins目录,否则安装也报错.
#下载插件包 wget https://github.com/infinilabs/analysis-ik/releases/download/v8.12.2/elasticsearch-analysis-ik-8.12.2.zip # 导入容器中指定目录 docker cp elasticsearch-analysis-ik-8.12.2.zip es01:/usr/share/elasticsearch/ # 进入容器,默认目录即为/usr/share/elasticsearch/ docker exec -it es01 bash # 安装插件 elasticsearch-plugin install file:elasticsearch-analysis-ik-8.12.2.zip # 退出,重启容器 docker restart es01 # 验证是否生效 curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD -XPOST https://localhost:9200/_analyze?pretty -H 'Content-Type:application/json' -d '{"text":"三角龙或者霸王龙","analyzer": "ik_smart"}' # 返回结果中不包含”或者”,因为”或者”在默认的停用词表中。
采坑总结:
1. ik分词器插件导入始终无法安装,且报错.
解决:一定要注意,安装es插件需要指定路径,且增加”file:” 的前缀,不加此前缀,那就等着报错吧 。
2. ik分词器添加自定义专有名词以及停用词不生效(浪费了1天的时间来排查) 。
解决:
(1) 一定要注意,8.12.2版本的ik分词器如果想要配置自定义专有名词或停用词,配置的完整目录是/usr/share/elasticsearch/config/analysis-ik,而不是/usr/share/elasticsearch/plugins/analysis-ik,这点需要注意下.
在config/analysis-ik中配置IKAnalyzer.cfg.xml,修改内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <comment>IK Analyzer 扩展配置</comment> <!--用户可以在这里配置自己的扩展字典 --> <entry key="ext_dict">extra_main.dic</entry> <!--用户可以在这里配置自己的扩展停止词字典--> <entry key="ext_stopwords">extra_stopword.dic</entry> <!--用户可以在这里配置远程扩展字典 --> <!-- <entry key="remote_ext_dict">words_location</entry> --> <!--用户可以在这里配置远程扩展停止词字典--> <!-- <entry key="remote_ext_stopwords">words_location</entry> --> </properties>
(2) 一定要注意,extra_main.dic和extra_stopword.dic的编码格式是UTF-8,如果编码格式不对的话,分词也不生效.
self.es_client = Elasticsearch(config['url'], basic_auth=(config['user'], config['password']), ca_certs=config['crt_path'], http_compress=True, request_timeout=int(config['request_timeout']) if 'request_timeout' in config else 60, max_retries=int(config['max_retries']) if 'max_retries' in config else 5, retry_on_timeout=True)
def index(self, kg_id, force=False): """ 构建表 """ if force: try: self.es_client.indices.delete(index=kg_id, ignore_unavailable=True) except EngineError as e: logger.exception(f"code:{ES_DELETE_INDEX_ERROR}, message:{str(e)}") raise e if not self.es_client.indices.exists(index=kg_id): body = { 'settings': {'index': {'number_of_shards': 2}}, 'mappings': { 'dynamic': False, 'properties': { 'name': {'type': 'keyword'}, 'concepts': {'type': 'keyword'}, 'property': {'type': 'keyword'}, 'value': {'type': 'text', 'analyzer': 'ik_max_word', 'search_analyzer': 'ik_smart'}, 'numbers': {'type': 'double_range'}, 'embeddings': {'type': 'elastiknn_dense_float_vector', 'elastiknn': {'dims': 768, 'model': 'lsh', 'similarity': 'cosine', 'L': 99, 'k': 3}} } } } try: self.es_client.indices.create(index=kg_id, body=body) except EngineError as e: logger.exception(f"code:1008, message:{str(e)}") raise e try: self.es_client.indices.refresh(index=kg_id, ignore_unavailable=True) except EngineError as e: logger.exception(f"code:1008, message:{str(e)}") raise e
说明:
1. value字段需要经过IK分词,分词方式ik_max_word,查询方式是ik_smart 。
2. embeddings的类型为elastiknn_dense_float_vector,其中向量维度为768,相似度计算使用cosine 。
def get_candidate_entities(self, kg_id, query, limit=15): """ 基于查询串查找候选实体名称 """ body = { '_source': {'excludes': ["embeddings"]}, 'query': { 'function_score': { 'query': { 'bool': { 'must': [ {'match': {'value': query}}, {'bool': { 'filter': { 'bool': { 'should': [ {'term': {"property": "名称"}}, {'term': {"property": "别名"}}, ] } } }} ] } }, 'functions': [ { 'elastiknn_nearest_neighbors': { 'field': 'embeddings', 'vec': self.get_callback_ans({'query': [query]})['result'][0]['embeddings'], 'model': 'lsh', 'similarity': 'cosine', 'candidates': 100 } } ] } }, 'size': limit } return self.es_client.search(index=kg_id, body=body)['hits']['hits']
说明:
1. '_source': {'excludes': ["embeddings"]}表示输出结果中过滤embeddings字段 。
2. 查询以function_score方式,其中的query表示别名或名称与问题的匹配程度,functions表示打分方式,目前的打分是基于向量相似度进行打分,其中, self.get_callback_ans表示语义相似度模型将文本转换为向量。注意:最终的得分由两部分组成,一部分是文本匹配,一部分是语义相似度匹配,不过可以增加参数boost_mode进行设置.
def search_by_property_value(self, kg_id, property, value, limit=100): body = { '_source': {'excludes': ["embeddings"]}, 'query': { 'function_score': { 'query': { 'bool': { 'must': [ {'match': {'value': value}}, {'term': {"property": property}} ] } }, 'functions': [ { 'elastiknn_nearest_neighbors': { 'field': 'embeddings', 'vec': self.get_callback_ans({'query': [value]})['result'][0]['embeddings'], 'model': 'lsh', 'similarity': 'cosine', 'candidates': 100 } } ], 'boost_mode': 'replace' } }, 'size': limit } try: return self.es_client.search(index=kg_id, body=body)['hits']['hits'] except EngineError as e: logger.exception(f"code:{ES_SEARCH_ERROR}, message:{str(e)}") raise e
主要解决的场景有:体重大于9吨的恐龙有哪些?身长小于10米的角龙类有哪些?
其中,如果提供了实体名称,则查询范围是基于这些实体进行查询比较.
def search_by_number_property(self, kg_id, property, operate, entities, limit=100): musts = [{'term': {'property': property}}, {'range': {'numbers': operate}}] if entities: musts.append({'terms': {'name': entities}}) body = { '_source': {'excludes': ['embeddings']}, 'query': { 'bool': { 'must': musts } }, 'size': limit } try: return self.es_client.search(index=kg_id, body=body)['hits']['hits'] except EngineError as e: logger.exception(f"code:{ES_SEARCH_ERROR}, message:{str(e)}") raise e
实现最大最小的逻辑,采用了sort机制,按照numbers进行排序,最大则顺排,最小则倒排.
def search_by_number_property_maxmin(self, kg_id, property, entities, sort_flag): musts = [{'term': {'property': property}}] if entities: musts.append({'terms': {'name': entities}}) body = { '_source': {'excludes': ["embeddings"]}, 'query': { 'bool': { 'must': musts } }, 'sort': {'numbers': sort_flag}, 'size': 1 } try: return self.es_client.search(index=kg_id, body=body)['hits']['hits'] except EngineError as e: logger.exception(f"code:{ES_SEARCH_ERROR}, message:{str(e)}") raise e
上一版未解决的问题,在本版本优化的结果.
1. 问:头像鸭头的龙有哪些?
答:头像鸭头的有慈母龙、原角龙、鹦鹉嘴龙、姜氏巴克龙、奇异辽宁龙、多背棘沱江龙、陆家屯鹦鹉嘴龙、盖斯顿龙、小盾龙、肿头龙、弯龙 。
2. 问:老师说的有一个特别会照顾宝宝的恐龙是什么龙?
答:慈母龙会照顾宝宝.
3. 问:有哪些恐龙会游泳啊?
答:滑齿龙、慢龙和色雷斯龙是会游泳的恐龙.
4. 问:科学家在意大利阿尔卑斯山脉Preone山谷的乌迪内附近发现了一个会飞的史前动物化石,它是谁的化石?
答:科学家在意大利阿尔卑斯山脉Preone山谷的乌迪内附近发现的会飞的史前动物化石是沛温翼龙的化石.
一句话足矣~ 。
本文主要是针对KBQA方案基于LLM实现存在的问题进行优化,主要涉及到图谱存储至Es,且支持Es的向量检索,还有解决了一部分基于属性值倒查实体的场景,且效果相对提升.
其次,提供了部分Es的操作源码,以飧读者.
。
。
。
附件:
1. es vs milvus: https://zilliz.com/comparison/milvus-vs-elastic 。
2. docker安装es:https://www.elastic.co/guide/en/elasticsearch/reference/8.12/docker.html 。
3. elastiknn性能分析:https://blog.csdn.net/star1210644725/article/details/134021552 。
4. es的function_score: https://www.elastic.co/guide/en/elasticsearch/reference/8.12/query-dsl-function-score-query.html 。
。
最后此篇关于LLM应用实战:当KBQA集成LLM(二)的文章就讲到这里了,如果你想了解更多关于LLM应用实战:当KBQA集成LLM(二)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Windows 集成 (NTLM) 身份验证和 Windows 集成 (Kerberos) 之间有什么区别? 如何在IIS6中实现这些 w.r.t. MSDN 最佳答案 Kerberos 和 NTLM
Keycloak是一个用 Java 编写的开源身份验证和身份管理解决方案。它提供了一个nodejs适配器,使用它我能够成功地与express集成。这是有效的路由文件: 'use strict'
这是我关于 Bamboo 的第二个问题 ( My First One )。阅读建议信息后我的理解是,我需要一个构建工具,例如 nAnt 或 MSbuild 来编写一个获取源代码并构建它的脚本(我正在开
可用于将第三方应用程序与 jira 4.3 集成的身份验证方案有哪些?显然,从客户那里获取用户名和密码听起来很荒谬。另外,我知道 oauth 身份验证仅适用于版本 5。请告诉我。谢谢。 附注。我不是在
我有一个使用 DDS 的旧版 C++ 应用程序用于异步通信/消息传递。我需要将此应用程序集成到使用 JMS 进行消息传递的 JavaEE 环境中。除了构建独立的 JMS/DDS 桥接模块之外,我还有其
我正在尝试使用 Whatsapp 发送测试消息,但收到此错误消息: "error":{"code":27,"description":"Recipient not available on chann
我想将 photologue 与我的 Django 应用程序集成,并使用它在车辆库存中显示照片......有点像 Boost Motor Group Inc. 提供的内容。我已经集成了该应用程序,所以
我目前正在尝试弄清楚如何与 fujitsu scansnap 扫描仪集成,但没有从 fujitsu 找到有关 fujitsu scansnap 管理器如何调用您的应用程序并将文件发送到您的应用程序的详
在我的项目中,我使用了 9 个(九个)int-ip:udp-inbound-channel-adapter 和一个 jms:inbound-channel-adapter。 Jms 适配器从服务器接收
在我们当前的原型(prototype)中,大多数标准 HTML 控件都被小程序取代,最重要的是表单提交由小程序触发。 有没有一种方法可以像 一样在服务器端调用关联的操作 ? 本文Applet and
是否可以使用 twilio 号码从 whatsapp 发送/接收短信?有人用whatsapp试过twilio吗?我问过客服,如果可能的话,他说,不确定,但很多人都问过这个问题。 最佳答案 万一其他人来
我们办公室中几乎不存在版本控制,这显然导致了很多麻烦。我们想使用SVN和Notepad++进行设置...任何人都对如何实现此目标有任何想法?我已经开始研究并浏览了这个网站: http://www.sw
曾经有提供这种集成的 spring-modules 项目;但是,该项目现已弃用。现在有没有人继续支持这种集成?谢谢。 最佳答案 工作正在进行中。 http://blog.athico.com/sear
我的理解是,根据 http://wiki.dbpedia.org/Datasets,DBpedia 从 YAGO 获取类层次结构,而不是实体。 .但是,类似 http://dbpedia.org/cl
任何人都可以帮助我如何将 OpenCMS 与 Java Spring Web 应用程序集成。已经用谷歌搜索并浏览了很多网站但没有用。所以,请帮助我。 最佳答案 我认为将 SpringMVC 与 Ope
我正在尝试使用新的 migs getaway (MPGS) 我遵循了下一个 url 中的代码 https://ap-gateway.mastercard.com/api/documentation/i
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 4年前关闭。 Improve thi
我有一个 cmake 项目。我想轻松完成以下操作 搜索光标下任何变量、函数等的声明、定义和引用,这些可能在外部头文件中声明,其路径是在CMakeLists.txt中使用INCLUDE_DIRECTOR
有人能给我指点一下 Objective-C(或 c/c++)库的方向,或者教通过 FTP 上传或下载的教程(Objective-C)吗?最好能展示如何将文件下载到临时目录,然后稍后上传?我不介意针对
集成()给出了非常错误的答案: integrate(function (x) dnorm(x, -5, 0.07), -Inf, Inf, subdivisions = 10000L) # 2.127
我是一名优秀的程序员,十分优秀!