背景
在医疗行业,医生讲究对症下药,患者希望药到病除,药厂也希望生产真正解决病痛的药物。具体的病症和医药都有严谨的术语,行业的多个参与方,进行沟通合作时,都是用专业医疗术语。众所周知,《本草纲目》全书 52 卷,各论分为 16 部 60 类,记载药物 1892 种。每种药物,有名称、气味、性状、主治、炮制方法等资料,如此庞大的信息,需要医生或药物专家全部记忆难度属实不低。对药物市场推广人员来说难度更大。随着现代医学不断发展,医学论文、药物典籍、治疗方案等电子文献更是层出不穷,如何快速为罹患某种疾病的患者匹配合适的治疗方案和药品迫在眉睫。
需求分析
我们有些制药客户,建设和沉淀了医疗知识库,系统中收录了医药专家的文献,但是部分领域的专家文章较多,如何快速根据疾病名称(医疗术语)找到合适的治疗方案和所需药品,以及如何帮助医药代表在拜访医学专家前,通过专家研究的技术领域(医疗术语)对海量医学文献进行关键信息提取,更好地和医学专家建立连接,成为了这些客户的痛点。客户大概有几十万个文献,每个文献有摘要,并打了 tag(提取关键字),基于文献摘要文本的全文检索是一种实现方案,因此,选择支持全文索引的数据库是一个较好的选择。他们的需求简要概括如下:
- 可以满足识别医疗专业术语词组需求,比如:糖尿病外周神经病变,是一个症状,需要作为一个整体进行检索
- 词干转换,比如:英文单词时态支持
- 地名简写或术语简写,比如:Beijing 表示北京,冀表示河北
- 生活语言与专业术语的近似关系,比如:咳嗽和支气管炎,着凉和风寒感冒
但是,传统的全文检索使用一元分词(每个汉字 1 个词)或二元分词算法(其中每个二元组包括一个词和其下一个词之间的关系),而医疗术语有两个特点:单个术语较长、术语体量较大。二元分词对长医疗术语的分词效果不佳,需要大量自定义词组表示专业术语;术语体量太大,导致自定义词组的维护成本较高。随着生成式 AI 的发展,客户希望可以借住大模型进行探索优化。并且,可以针对知识库的不同用户,提供更人性化和专业化的互动体验,因此,结合大模型是一个值得探索的优化方式。
目前第一期的优化实现方式,使用大模型进行了优化,架构如下:
用户在检索前,会选择大致的医疗领域或专家,再输入关键字。图中的 Medical data 即是从医疗知识库中查询到的某个疾病或专家的文献的摘要,目前是存储在关系型数据库中,只通过专家名字查询得到 Medical data。Medical data 作为 LLM 的 system prompt;而用户的检索关键字,作为对 LLM 的 Prompt 中的 instruction 部分,调用 LLM 去理解医疗术语,针对用户检索需求做一个总结输出(图中 Summary 的 Output),Output 除了互动性文字,也包含文章的 ID/Game,最后以便通过 ID 从知识库(PostgreSQL)中找出详细文档链接和文献全文。
这样做,帮助客户解决了理解医疗术语的问题,但是只通过专家名字查询,如果专家的文章太多,会导致 Medical data 部分 size 过大,LLM 的总输入 token 多,出现幻觉的可能性大,LLM 成本也很高。于是,客户希望我们找到更加准确的解决方案,通过关键字减小 Medical data 结果集。
方案概览
使用检索增强生成方案(RAG)
在这篇博客中,我们将讨论使用双路召回的方式来优化这个问题。其一是利用大模型对所有医疗文献的摘要进行 embedding,输出的向量数据存储到向量数据库;用户检索时,把用户输入的医疗术语进行 embedding 向量化,在向量数据库中进行相似性检索,找到最相似的文档。其二是通过对文献的摘要提取关键字,存入 Aurora PostgreSQL 中并创建倒排索引,实现对用户输入进行全文检索。两种方法结合,提高文档召回的精确度,缩小上图中的 Medical data 的范围和尺寸,达到减少幻觉并降低 token 数的诉求。通常,我们也称之为检索增强生成(RAG,Retrieval Augmented Generation),如下图:
使用双路召回方案强化 RAG
由于 pgvector 能够高效存储和快速检索以高维向量形式存储的数据,在 RAG 的 Knowledge sources 选择上,我们选择启用 pgvector 插件的 Aurora PostgreSQL,以便直接进行相似度比较。并且 PostgreSQL 支持通过 to_tsvector 进行解析和标准化文档字符串,我们可以尝试给标准化后的数据创建 GIN 倒排索引,实现全文检索。to_tsvector
函数在内部调用了一个解析器,它把文档文本分解成记号并且为每一种记号分配一个类型。
下文的 demo,使用 Amazon titan_embedding 模型,对文章的摘要进行 embedding,并存入启用 pgvector 插件的 Aurora PostgreSQL(版本 13.12)中;利用 pgvector 提供的 sql 语法进行相似性检索和 PostgreSQL 的全文检索,达到缩小 Medical data 的目标,再调用 LLM。demo 分为以下 6 个步骤:
- 部署 Aurora PostgreSQL 数据库
- Aurora 数据库配置调整
- 数据库启用 pgvector 插件
- 在 Aurora 中建表并导入医疗测试数据
- 调用 Amazon Titan embedding 对文档进行 embedding 并刷新字段 embedding_doc
- 使用中文分词插件从摘要中提取关键字,并存入 keywords 字段,创建 GIN 倒排索引
- 用户搜索 demo,参考一个 python 脚本:Search_with_pgvector
方案详解
部署 Aurora PostgreSQL 数据库
版本选择上,只要支持 pgvector 的 Aurora 版本都可行,具体是 Aurora PostgreSQL 15.3、14.8、13.11、12.15 及更高版本。这里选择 13.12,具体部署方法参考:创建 Aurora PostgreSQL 数据库集群并连接到该集群。
Aurora 数据库配置调整
由于 Aurora PostgreSQL 创建向量索引过程,需要较大的 work memory,需要修改 Aurora DB instance 自定义参数组的值 maintenance_work_mem = 10240000 (10GB),并重启集群。
使用 psql 连接到数据库中,检查参数的实际值,须与如下保持一致
test=> show maintenance_work_mem;
maintenance_work_mem
----------------------
10000MB
(1 row)
数据库启用 pgvector 插件
pgvector 插件使用方法参考:Blog-Leverage pgvector and Amazon Aurora PostgreSQL for Natural Language Processing, Chatbots and Sentiment Analysis – Use Case 2: pgvector and Aurora Machine Learning for Sentiment Analysis
在 Aurora 中建表并导入医疗测试数据
下载公开医疗测试数据
登录一台 EC2,与 Aurora 在同一个 VPC 即可
# 下载公开的医疗测试数据
git clone https://github.com/zhangsheng93/cMedQA2.git
# 下载后解压answer.zip
unzip answer.zip
建表并导入数据
使用 psql 连接到 Aurora PostgreSQL 中,连接方法参考:连接到 Aurora PostgreSQL 数据库集群
字段 doc 是摘要字段;doc_type 表示分类;embedding_doc 是经过 LLM embedding 模型计算后的 embedding 数据;keywords是“以空格相连的关键字”组合字段。
test=> create table text_embedding_cos
( id int,
doc_type int,
doc text,
embedding_doc vector(1536) null,
keywords text null);
alter table text_embedding_cos add primary key(id);
使用 copy 命令,导入以上下载的公开医疗数据,格式为 csv 文本数据
test=> \copy text_embedding_cos(id, doc_type, doc) from '/home/centos/answer.csv' WITH DELIMITER ',' CSV HEADER;
下载 Demo 并安装
建议采用 Python 3.9 环境来验证方案的 Demo。方便运行环境的版本和依赖管理,我们使用 Python 虚拟环境。
$ python -m venv zhcn_env
$ source zhcn_env/bin/activate
$ python -m pip install -r requirements.txt
# requirements.txt管理依赖包
vi requirements.txt
填入一下内容,保存退出
catalogue==2.0.10
numpy==2.0.2
spacy_pkuseg==1.0.0
srsly==2.4.8
portalocker==2.8.2
pytz==2022.6
typing_extensions==4.12.0
DBUtils==1.2
psycopg2==2.9.9
jieba==0.42.1
boto3==1.34.157
调用 Amazon Titan embedding 对文档进行 embedding
Amazon Titan Embeddings 是一种文本嵌入模型,可将自然语言文本(包括一个单词、短语甚至大型文档)转换为向量表示形式,可用于为基于语义相似度的搜索、个性化提供支持。调用 Amazon Titan embedding Model 可以生成中文文本的 embedding 向量,使用方法,可以参考文档:Amazon Titan Embeddings Text- Example code。
- 脚本调用 embedding 模型,并更新到表的字段 embedding_doc
python words_cosine.py -m embedding -r 240000
-
- -r 表示处理 240000 行数据,按照表的最大主键进行设置即可
- -m 表示处理模式,embedding 表示给文档做 embedding
这个步骤,由于我们需要对每个文档(大约 15 万个文档)的摘要进行 embedding 并存入数据库,需要较多时间,大概 2 小时(如果您只是验证过程,建议删除部分以上导入的数据行,节约时间)。
对向量数据创建余弦距离索引
测试数据大概 20W 行,桶数量选择 10000 个,按照公式计算:
桶数量 Lists=rows / 10 for up to 1M rows and sqrt(rows) for over 1M rows
test=> CREATE INDEX ON text_embedding_cos USING ivfflat (embedding_doc vector_cosine_ops) WITH(lists = 10000);
执行按照余弦距离的向量相似性检索
创建生成固定纬度的随机 embedding 的函数
test=> CREATE OR REPLACE FUNCTION public.random_array(dimension INTEGER)
RETURNS Integer[] AS $$
SELECT array(SELECT round(random()*10) from generate_series(1,dimension));
$$
LANGUAGE SQL;
这个函数,可以帮助生成 1 个随机的 1536 位度的 embedding,这样可以用它构建 SQL 去数据库中体验相似性检索。
余弦距离的相似性搜索 SQL 例子
test=> WITH probe AS (SELECT random_array(1536)::VECTOR(1536) AS v)
SELECT id,doc FROM text_embedding_cos
ORDER BY embedding_doc <=> (SELECT v FROM probe) limit 5;
Demo 中代码使用了这个 SQL 语法进行余弦距离相似性检索。
如果要测几何距离的效果,可以创建 L2 索引,并参考几何距离检索 SQL 语法,具体见后文描述。
提取关键字并创建 GIN 索引
从摘要中提取关键字,需要应用程序结合一些常用的中文分词插件来实现,业务上也可以对不同的关键字设置权重。我们选择 pkuseg 分词插件来完成中文摘要的分词,提取关键字,并把每个关键字通过空格连接起来,形成类似英文书写习惯的一个字段 keywords。再利用 to_tsvector 来解析 keywords,形成标准化的字符串以便搜索。标准化采用 simple 的搜索配置项目,simple 文本搜索配置项很实用,它使得空格分割的每一组字符(即关键字)都是一个语义,simple 不会试着去查找单词的词根,就像人物的名字,不需要查找名字的词根。
分词
关键字通过空格相连解决了中文被解析和索引的问题,从摘要中提取关键字的方法,一般使用中文分词,然后业务提取关键字。中文分词,有较多开源的实现方式,这里选择 pkuseg 分词,因为它针对医疗术语进行了训练。具体使用方法请参考:pkuseg。其他分词算法可参考: jieba 分词 。这两种算法,均支持自定义词典、停用词列表,自定义词典可以优化长术语权重,停用词列表可以排出语气助词、连词、副词等对分词和检索的影响。
$ source zhcn_env/bin/activate
本文测试,我们输入如下术语,则分词时,它们会被当作一个整体对 text 进行分词
'''code: 使用自定义词典
segClass = pkuseg.pkuseg(model_name='medicine',user_dict='my_dict.txt')
origin_list = segClass.cut(text)
'''
$ vi my_dict.txt
糖尿病周围神经病变
糖尿病外周神经病变
尿崩症患者
电解质紊乱
结核性膀胱挛缩
注:以上词汇或术语只是为了技术测试,无任何不良引导或其他主观色彩表示。
为了避免语义干扰,如果分词后的 word,在如下列表,就不作为文献摘要的关键字,这些词通常是不表示任何业务含义,须根据你的业务进行指定。
stop_words = ['的','我','你','他','她','得','啊','哈','了','吧', '呢', '如', '就', '可能', '是', '要','和', '好', '哪家', '如何','不','时']
本文的 demo,结合了 pkuseg 分词和 pgvector 向量搜索,因此这里先做分词并存入数据库的关键字字段。
python words_cosine.py -m split -a 'pkuseg' -r 240000
-
- -m: split 脚本自定义参数,表示从摘要中提取关键字
- -a: pkuseg 表示使用的分词算法
- -r: 240000 表示我们处理多少行数据
创建倒排索引
在 PostgreSQL 数据库中,表的 keywords 字段上创建 to_tsvector 的倒排索引,用于全文检索。由于 to_tsvector 是函数,因此也是一个函数索引。
test=> create index idx_cos_gin_keywords on text_embedding_cos using GIN(to_tsvector('simple', keywords));
test=> \d+ text_embedding_cos
Table "public.text_embedding_cos"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------------+--------------+-----------+----------+---------+----------+--------------+-------------
id | integer | | not null | | plain | |
doc | text | | | | extended | |
embedding_doc | vector(1536) | | | | extended | |
doc_type | integer | | | | plain | |
keywords | text | | | | extended | |
Indexes:
"text_embedding_cos_pkey" PRIMARY KEY, btree (id)
"idx_cos_gin_keywords" gin (to_tsvector('simple'::regconfig, keywords))
"text_embedding_cos_embedding_doc_idx" ivfflat (embedding_doc vector_cosine_ops) WITH (lists='10000')
Access method: heap
-- 表的数据规模如下
jctest=> select count(*) from text_embedding_cos;
count
--------
157981
(1 row)
测试搜索专业医疗术语
以上数据和索引准备完成,接下来是效果展示。
这篇博客的重点是优化知识库检索环节,缩小 Medical data 部分,因此我们使用 pgvector 提供的 SQL,比较用户的输入和知识库中的摘要的余弦距离,并进行升序排序后,取 top 5 的方式,限制医疗文献库范围 5 条最相似的文献。并通过关键字的全文检索,进行双路召回,互相佐证。
激活 Demo 所需的 Python 虚拟环境
# 激活虚拟环境
$ source zhcn_env/bin/activate
$ python -m pip install -r requirements.txt
# requirements.txt管理依赖包
vi requirements.txt
填入一下内容,保存退出
catalogue==2.0.10
numpy==2.0.2
spacy_pkuseg==1.0.0
srsly==2.4.8
portalocker==2.8.2
pytz==2022.6
typing_extensions==4.12.0
DBUtils==1.2
psycopg2==2.9.9
jieba==0.42.1
boto3==1.34.157
模拟用户搜索及效果
- 模拟业务场景 1:搜索一个较长的医学术语 比如:糖尿病外周神经病变,类风湿性关节炎,“建议口服阿莫西林、奥美拉唑”。
- 以下命令行中参数:-t 5 即表示在 pgvector 和全文检索时,指定返回结果集 top 5。
- 以下内容的输出格式说明:****** 与&&&&&& 之间是向量搜索返回的结果;&&&&&&与######之间是全文检索范围的结果;######之后是两个结果的交集,就是两种搜索方法都能搜到的结果。
# 激活虚拟环境
$ source zhcn_env/bin/activate
$ python words_cosine.py -m search -i "糖尿病外周神经病变" -p 100 -t 5
****** search result by embedding: 糖尿病外周神经病变 , search time: 1 sec
{'id': 230006, 'doc': '这篇文章分析糖尿病周围神经病变', 'distance': 0.10606223224575584}
{'id': 6, 'doc': '本论文是糖尿病外周神经病变的发病机理研究进展', 'distance': 0.10606223224575584}
{'id': 198173, 'doc': '患有糖尿病,考虑为糖尿病周围神经病变,建议控制好血糖,饮食控制,可以用点营养神经的药物。建议饮食控制,少量多餐,忌所有甜食,适当户外运动。', 'distance': 0.17512589210054197}
{'id': 28007, 'doc': '长期糖尿病病人,会有周围神经的损害,根据以上描述考虑是周围神经损害建议你控制好血糖,糖尿病饮食,必要时可以加用一些维生素b1等药物', 'distance': 0.20133171027877683}
{'id': 12960, 'doc': '考虑你可能是有糖尿病的并发症,糖尿病神经病变。建议你平时注意清淡饮食,多吃绿叶青菜,粗粮,控制主食量,将血糖控制在7。0左右,可以口服甲钴胺,前列地尔,维生素B1营养神经治疗,希望对你有所帮助。', 'distance': 0.2098350805125292}
&&&&&& search result by key word: 糖尿病外周神经病变 , search time: 1 sec
{'id': 6, 'doc': '本论文是糖尿病外周神经病变的发病机理研究进展'}
###### search result intersect by Dual-path Recall Search: 糖尿病外周神经病变
{'id': 6, 'doc': '本论文是糖尿病外周神经病变的发病机理研究进展'}
$ python words_cosine.py -m search -i "类风湿性关节炎" -p 100 -t 10
****** search result by embedding: 类风湿性关节炎 , search time: 1 sec
{'id': 74405, 'doc': '病例分析:风湿,类风湿性关节炎是不同疾病。', 'distance': 0.08754044693899132}
{'id': 31427, 'doc': '考虑是风湿,风湿性关节炎是一种常见的急性或慢性结缔组织炎症。可反复发作并累及心脏。临床一以关节和肌肉游走性酸楚、重著、疼痛为特征。属变态反应性疾病。是风湿热的主要表现之一,多以急性发热及关节疼痛起病。风湿性关节炎为风湿热最常见的一种临床表现,与A组溶血性链球菌感染引起的变态反应有关,风湿性关节炎起病较急,受累关节以大关节为主。开始侵及下肢关节者占85%,膝和踝关节最为常见。其次为肩、肘和腕、手和足的小关节少见。关节病变呈多发性和游走性,关节局部炎症明显,表现有红、肿、热、痛、压痛及活动受限', 'distance': 0.10501068225672561}
{'id': 51850, 'doc': '可能是类风湿性关节炎,其以关节病变引起肢体严重畸形,关节滑膜炎及浆膜、心肺、皮肤、眼、血管等结缔组织广泛性炎症为主要表现的慢性全身性自身免疫性疾病。风湿性关节炎抗O高,类风湿关节炎往往类风湿因子高,CCP、AKA会出现阳性。现行治疗类风湿性关节炎的目的在于控制关节及其它组织的炎症,缓解症状。保持关节功能和防止畸形。修复受损关节以减轻疼痛和恢复功能', 'distance': 0.11852274465690826}
{'id': 30008, 'doc': '患者现在的情况很可能是类风湿性关节炎引起的。建议去医院找风湿科医师进一步检查,如风湿四项等,明确病因,对症治疗。', 'distance': 0.1283027589602148}
{'id': 42354, 'doc': '可能要注意检查一下类风湿因子的,有可能与类风湿性关节炎有关。', 'distance': 0.13402691086889296}
{'id': 9449, 'doc': '类风湿关节炎是常见的风湿性疾病,表现为全身多发性对称性的关节肿痛,主要是以四肢小关节为主建议目前该病尚不能除根,治疗目的是缓解疼痛,防止病情进展。常用药物是非甾体类抗炎药,改变病情抗风湿药,生物制剂。常用方案是甲氨蝶呤+来氟米特+叶酸+非甾体类抗炎药,若是经济条件可以,联合生物制剂治疗', 'distance': 0.13519383570871346}
{'id': 95067, 'doc': '可能是风湿性关节炎最好还是到医院检查确诊', 'distance': 0.1383874095041583}
{'id': 105375, 'doc': '类风湿关节炎是常见的风湿性疾病,表现为全身多发性对称性的关节肿痛,主要是以四肢小关节为主:目前该病尚不能除根,治疗目的是缓解疼痛,防止病情进展。常用药物是非甾体类抗炎药,改变病情抗风湿药,生物制剂。若是经济条件可以,联合生物制剂治疗', 'distance': 0.13995947658543828}
{'id': 198054, 'doc': '考虑是类风湿性关节炎,建议进一步检查抗O,类风湿因子,拍X光片检查。确诊病因后治疗。平时避免受凉和过度劳累', 'distance': 0.14261460287513472}
{'id': 2300, 'doc': '考虑是类风湿性关节炎的可能性比较大,这是一种免疫紊乱性疾病,主要表现为低热,关节疼痛的症状。建议进行类风湿因子检查,进一步明确诊断,建议选择糖皮质激素配合双氯芬酸钠抑制炎症反应。', 'distance': 0.1428603517458098}
&&&&&& search result by key word: 类风湿性关节炎 , search time: 1 sec
{'id': 198054, 'doc': '考虑是类风湿性关节炎,建议进一步检查抗O,类风湿因子,拍X光片检查。确诊病因后治疗。平时避免受凉和过度劳累'}
{'id': 198053, 'doc': '你的啊可能是类风湿性关节炎,建议你到当地正规医院,抽血检查类风湿因子、抗O、C-反应蛋白、血沉,确诊是否是类风湿性关节炎。'}
{'id': 199752, 'doc': '好,你考虑你患类风湿性关节炎的可能性大。建议你去医院做相关检查如血风湿因子和类风湿因子检查,关节X片,血沉,以便确诊,再做合理的治疗。'}
{'id': 200833, 'doc': '你这是小关节处疼痛,且是晨起明显,活动后减轻。想问一下,手的小关节有没有晨起时感觉僵硬。如有应考虑是类风湿性关节炎病变。无手指晨起僵硬的话考虑是骨关节的炎症,关节退行性的病变。建议你需要去骨科或风湿病科看看。'}
{'id': 203463, 'doc': '考虑风湿免疫性疾病,类似类风湿性关节炎,可以上级医院进一步检查确诊。如果已经排除,进一步排除尿酸升高,痛风性关节炎可能。建议空腹抽血检查肾功能,或省人民医院进行全面检查确诊。'}
{'id': 203602, 'doc': '病情考虑是否是类风湿性关节炎引起。应该到医院进一步检查确诊。建议,应该到医院风湿免疫科进一步检查,化验风湿四项,再拍片进一步确诊,确诊后在治疗为好。'}
{'id': 204616, 'doc': '根据你叙述的病情,建议你做下膝关节CR或CT片,必要时做关节镜检查。查找病因:骨刺,半月板损伤,类风湿性关节炎滑膜炎,滑囊炎,关节腔积液等等均可导致膝关节肿痛。骨刺治疗多以理疗,局部注射治疗为主,必要时行手术治疗,滑囊炎,滑膜炎,关节腔积液多以抽液封闭治疗为主。风湿性和类风湿性关节炎多以抗风湿治疗。建议明确病因后,对症治疗。'}
{'id': 204895, 'doc': '寿关节疼痛可能是骨关节炎或类风湿性关节炎,手指麻木怀疑与颈椎病有关'}
{'id': 198597, 'doc': '建议你做下膝关节CR或CT片,必要时做关节镜检查。查找病因:骨刺,半月板损伤,类风湿性关节炎滑膜炎,滑囊炎,关节腔积液等等均可导致膝关节酸胀,肿痛,响声等症状。骨刺治疗多以理疗,局部注射治疗为主,必要时行手术治疗,滑囊炎,滑膜炎,关节腔积液多以抽液封闭治疗为主。风湿性和类风湿性关节炎多以抗风湿治疗。建议明确病因后,对症治疗。'}
{'id': 201003, 'doc': '你腿疼往往是由于运动或是意外造成摔伤、扭伤、拉伤而引起的腿痛,建议你应当到骨科就诊,让医生排除有无骨折、肌腱的拉伤、损伤以及软组织损伤。在没有外伤的情况下,关节部位肿胀、疼痛,应考虑是风湿性关节炎或类风湿性关节炎。'}
###### search result intersect by Dual-path Recall Search: 类风湿性关节炎
{'id': 198054, 'doc': '考虑是类风湿性关节炎,建议进一步检查抗O,类风湿因子,拍X光片检查。确诊病因后治疗。平时避免受凉和过度劳累'}
$ python words_cosine.py -m search -i "建议口服阿莫西林、奥美拉唑" -p 1000 -t 5
****** search result by embedding: 建议口服阿莫西林、奥美拉唑 , search time: 1 sec
{'id': 135576, 'doc': '上述情况可使用奥美拉唑和阿莫西林和胃必治疗,建议注意休息,注意保暖,三餐规律不要吃寒凉食物,注意适当的休息,锻炼,保持生活规律,保持精神愉快,乐观,精神抑郁、低沉的话,往往会引起或加重病情的', 'distance': 0.1307631088448612}
{'id': 41793, 'doc': '上述情况可使用奥美拉唑和阿莫西林和胃必治疗,建议注意休息,注意保暖,三餐规律不要吃寒凉食物,注意适当的休息,锻炼,保持生活规律,保持精神愉快,乐观,精神抑郁、低沉的话,往往会引起或加重病情的', 'distance': 0.1307631088448612}
{'id': 38577, 'doc': '上述情况可使用奥美拉唑和阿莫西林和胃必治疗,建议注意休息,注意保暖,三餐规律。不要吃寒凉食物,注意适当的休息,锻炼,保持生活规律,保持精神愉快,乐观,精神抑郁、低沉的话,往往会引起或加重病情的。', 'distance': 0.13453159076100563}
{'id': 47133, 'doc': '建议口服阿莫西林、奥美拉唑、甲硝唑、丽珠得乐治疗,注意清淡饮食,不要暴饮暴食,饭后适当运动,避免辛辣刺激性食物', 'distance': 0.14912365287980767}
{'id': 2488, 'doc': '建议口服阿莫西林、奥美拉唑、甲硝唑、丽珠得乐治疗,注意清淡饮食,不要暴饮暴食,饭后适当运动,避免辛辣刺激性食物', 'distance': 0.14912365287980767}
&&&&&& search result by key word: '建议口服阿莫西林、奥美拉唑 , search time: 1 sec
{'id': 2488, 'doc': '建议口服阿莫西林、奥美拉唑、甲硝唑、丽珠得乐治疗,注意清淡饮食,不要暴饮暴食,饭后适当运动,避免辛辣刺激性食物'}
{'id': 29807, 'doc': '建议口服阿莫西林、奥美拉唑、甲硝唑、丽珠得乐治疗,注意清淡饮食,不要暴饮暴食,饭后适当运动,避免辛辣刺激性食物'}
{'id': 47133, 'doc': '建议口服阿莫西林、奥美拉唑、甲硝唑、丽珠得乐治疗,注意清淡饮食,不要暴饮暴食,饭后适当运动,避免辛辣刺激性食物'}
{'id': 70353, 'doc': '临床表现是急性肠胃炎引起的恶心,呕吐建议患者注意饮食,少吃辛辣的食物,口服奥美拉唑片和阿莫西林胶囊'}
{'id': 97673, 'doc': '根据你的叙述,有胃痛、腹胀及恶心的症状,考虑是胃炎的可能性大。癌症胃疼多见于老年人,持续性疼痛,伴消瘦,一般的胃镜检查可以明确。建议你清淡饮食,避免油腻及辛辣刺激性的食物,可以口服阿莫西林、多潘立酮及奥美拉唑治疗。'}
###### search result intersect by Dual-path Recall Search: '建议口服阿莫西林、奥美拉唑
{'id': 2488, 'doc': '建议口服阿莫西林、奥美拉唑、甲硝唑、丽珠得乐治疗,注意清淡饮食,不要暴饮暴食,饭后适当运动,避免辛辣刺激性食物'}
{'id': 47133, 'doc': '建议口服阿莫西林、奥美拉唑、甲硝唑、丽珠得乐治疗,注意清淡饮食,不要暴饮暴食,饭后适当运动,避免辛辣刺激性食物'}
场景一,我们可以发现,医疗专业术语或一些自然句子的搜索,通过向量和全文检索,都可以找到相应文献,基于此,可以实现双路召回。
python words_cosine.py -m search -i "拉肚子如何引起的" -p 1000 -t 5
****** search result by embedding: 拉肚子如何引起的 , search time: 1 sec
{'id': 73353, 'doc': '应该不算是拉肚子,只能是说属于呕吐,多见于胃和呼吸道疾病导致的', 'distance': 0.17709070878066302}
{'id': 137814, 'doc': '考虑会导致拉肚子,肚子疼等症状,建议观察', 'distance': 0.17782812464532216}
{'id': 57296, 'doc': '拉肚子的原因有很多,胃肠道疾病、腹膜炎等等都可引起腹泻。建议平时多注意饮食不要吃生冷辛辣的食物,建议去医院就诊排除身体疾病,可以多吃芹菜或者粗粮有利于大便成型,喝淡盐水不错体内丢失得电解质', 'distance': 0.17906907657963156}
{'id': 256, 'doc': '经常拉肚子这种情况是脾肾虚寒导致的,建议服用四神丸治疗。', 'distance': 0.1854479642088731}
{'id': 86232, 'doc': '经过上面的情况来看,可能是属于肠道感染引起的腹泻导致的拉肚子。请注意要多喝点水,多吃水果,蔬菜。服用黄连素,肠炎宁及氟哌酸。', 'distance': 0.20474516054727576}
WARNING: features.msgpack does not exist, try loading features.pkl
sql for full text: select id, doc from text_embedding_cos where to_tsvector('simple', keywords) @@ '拉肚子' and to_tsvector('simple', keywords) @@ '引起' limit 5
&&&&&& search result by key word: 拉肚子如何引起的 , search time: 1 sec
{'id': 16989, 'doc': '小儿腹泻拉稀拉肚子是一组由多病原,多因素引起的大便次数增多和大便性状改变为特点的儿科常见病。一年四季都可能发生,但以夏秋季最多。可分为感染性和非感染性两种。预防:1:注意合理喂养,提倡母乳2:对生理性腹泻的婴儿应避免不适当3:养成良好的个人卫生习惯,注意乳品的保存和用具4:避免长期乱用抗生素。'}
{'id': 22398, 'doc': '你这种情况拉肚子腹泻可能是慢性肠炎引起的症状。会有影响的。建议你可以服用硫酸庆大霉素碳酸铋胶囊治疗,不吃生冷刺激难消化食物,注意饮食卫生。'}
{'id': 24347, 'doc': '宝宝母乳喂养,慢慢才苹果一般不会引起宝宝拉肚子。如果宝宝妈拉肚子也会传染给宝宝建议你宝宝妈注意饮食,多吃营养高的食物。母乳的质量就高。吃水果药注意卫生,避免宝宝拉肚子'}
{'id': 34531, 'doc': '夏秋季小孩子最易拉肚子,多由于食用了不洁的食物或水、腹部受凉等引起。病原为细菌、病毒、或其它的病原体。如孩子拉肚子次数不多,精神好,可以在家观察一下。并给予以下的处理。1。暂禁饮食一顿,让胃肠休息一下。2。补充水分:给予淡淡的茶水中少加一点盐和糖、频频喂服。3。饮食给予脱脂牛奶、粥及少量酱菜等清淡易消化的食物。需注意:不要自行给予抗菌素,以免影响病原体的检出。也不要随便给予止泻药,以阻止病原体及毒素的排出'}
{'id': 47143, 'doc': '必须查明病因对症治疗。患有糖尿病的人会出现手脚麻木。只要身体任何部位经常出现手脚麻木麻木、酸痛、肿胀,就要及时检查血糖,老年人尤其要注意。药物引起的手脚麻木如感冒或拉肚子时,服用了黄连素或痢特灵后,会引起手脚麻木。在含有氢、砷、二硫化碳等环境中呆时间长了,也会出现手脚麻木。:手脚发麻亦可是气血不足造成,既是血虚。因为阳气虚弱,造成血虚,并且阳气无力行血,血液就达不到血管末梢。手脚是人体的末梢,因此血虚会有发麻的感觉。平时注意保养,阳虚应该少吃寒凉食物'}
###### search result intersect by Dual-path Recall Search: 拉肚子如何引起的
$ python words_cosine.py -m search -i "拉肚子如何引起的" -p 1000 -t 10
****** search result by embedding: 拉肚子如何引起的 , search time: 1 sec
{'id': 73353, 'doc': '应该不算是拉肚子,只能是说属于呕吐,多见于胃和呼吸道疾病导致的', 'distance': 0.17709070878066302}
{'id': 137814, 'doc': '考虑会导致拉肚子,肚子疼等症状,建议观察', 'distance': 0.17782812464532216}
{'id': 57296, 'doc': '拉肚子的原因有很多,胃肠道疾病、腹膜炎等等都可引起腹泻。建议平时多注意饮食不要吃生冷辛辣的食物,建议去医院就诊排除身体疾病,可以多吃芹菜或者粗粮有利于大便成型,喝淡盐水不错体内丢失得电解质', 'distance': 0.17906907657963156}
{'id': 256, 'doc': '经常拉肚子这种情况是脾肾虚寒导致的,建议服用四神丸治疗。', 'distance': 0.1854479642088731}
{'id': 86232, 'doc': '经过上面的情况来看,可能是属于肠道感染引起的腹泻导致的拉肚子。请注意要多喝点水,多吃水果,蔬菜。服用黄连素,肠炎宁及氟哌酸。', 'distance': 0.20474516054727576}
{'id': 55802, 'doc': '拉肚子可能是胃肠炎,多与饮食不当有关。你可以口服氧氟沙星、654-2片。适当多饮水可以放点食盐,暂时少进食,可以吃些易消化食物如米粥、面条', 'distance': 0.20558140407815273}
{'id': 41037, 'doc': '拉肚子可能是胃肠炎,多与饮食不当有关。你可以口服氧氟沙星、654-2片。适当多饮水可以放点食盐,暂时少进食,可以吃些易消化食物如米粥、面条', 'distance': 0.20558140407815273}
{'id': 112317, 'doc': '拉肚子。考虑是胃肠炎的情况。你检查大便常规的情况。可以确定病情。你这样的情况。严禁吃生冷刺激性食物。可以输液治疗。一般是用庆大霉素等药物治疗的。还需要补液治疗的了。', 'distance': 0.20826228790555268}
{'id': 10143, 'doc': '拉肚子即为腹泻,腹泻在日常生活中是一种常见病,拉稀是腹泻的一种常见症状,是指排便次数超出平时生活习惯的频率,粪便质稀,含未消化食物,脓血,粘液。', 'distance': 0.21103092125659528}
{'id': 118299, 'doc': '拉肚子可能是胃肠炎,多与饮食不当有关。祝你健康你可以口服氧氟沙星、654-2片。适当多饮水可以放点食盐,暂时少进食,可以吃些易消化食物如米粥、面条', 'distance': 0.2120469350169265}
WARNING: features.msgpack does not exist, try loading features.pkl
sql for full text: select id, doc from text_embedding_cos where to_tsvector('simple', keywords) @@ '拉肚子' and to_tsvector('simple', keywords) @@ '引起' limit 10
&&&&&& search result by key word: 拉肚子如何引起的 , search time: 1 sec
{'id': 16989, 'doc': '小儿腹泻拉稀拉肚子是一组由多病原,多因素引起的大便次数增多和大便性状改变为特点的儿科常见病。一年四季都可能发生,但以夏秋季最多。可分为感染性和非感染性两种。预防:1:注意合理喂养,提倡母乳2:对生理性腹泻的婴儿应避免不适当3:养成良好的个人卫生习惯,注意乳品的保存和用具4:避免长期乱用抗生素。'}
{'id': 22398, 'doc': '你这种情况拉肚子腹泻可能是慢性肠炎引起的症状。会有影响的。建议你可以服用硫酸庆大霉素碳酸铋胶囊治疗,不吃生冷刺激难消化食物,注意饮食卫生。'}
{'id': 24347, 'doc': '宝宝母乳喂养,慢慢才苹果一般不会引起宝宝拉肚子。如果宝宝妈拉肚子也会传染给宝宝建议你宝宝妈注意饮食,多吃营养高的食物。母乳的质量就高。吃水果药注意卫生,避免宝宝拉肚子'}
{'id': 34531, 'doc': '夏秋季小孩子最易拉肚子,多由于食用了不洁的食物或水、腹部受凉等引起。病原为细菌、病毒、或其它的病原体。如孩子拉肚子次数不多,精神好,可以在家观察一下。并给予以下的处理。1。暂禁饮食一顿,让胃肠休息一下。2。补充水分:给予淡淡的茶水中少加一点盐和糖、频频喂服。3。饮食给予脱脂牛奶、粥及少量酱菜等清淡易消化的食物。需注意:不要自行给予抗菌素,以免影响病原体的检出。也不要随便给予止泻药,以阻止病原体及毒素的排出'}
{'id': 47143, 'doc': '必须查明病因对症治疗。患有糖尿病的人会出现手脚麻木。只要身体任何部位经常出现手脚麻木麻木、酸痛、肿胀,就要及时检查血糖,老年人尤其要注意。药物引起的手脚麻木如感冒或拉肚子时,服用了黄连素或痢特灵后,会引起手脚麻木。在含有氢、砷、二硫化碳等环境中呆时间长了,也会出现手脚麻木。:手脚发麻亦可是气血不足造成,既是血虚。因为阳气虚弱,造成血虚,并且阳气无力行血,血液就达不到血管末梢。手脚是人体的末梢,因此血虚会有发麻的感觉。平时注意保养,阳虚应该少吃寒凉食物'}
{'id': 39929, 'doc': '宝宝可能是着凉或消化不良引起的拉肚子。母乳喂养的妈妈饮食要清淡,禁食生冷刺激食物。建议在医生指导下用药治疗,可服用思密达,妈咪爱,小儿健脾散,一贴灵治疗,注意要宝宝多喝水'}
{'id': 46620, 'doc': ':你说的孩子这种拉肚子的现象和有脓血的症状考虑孩子有可能还是痢疾引起的。在孩子时期也是比较多见的一种感染性的疾病。最好是在给孩子输液治疗几天是比较好的,这样孩子的疾病就会痊愈的'}
{'id': 55529, 'doc': '从你描述的情况来看,你目前的不适,主要可能是脾虚而引起的。以及拉肚子,主要是由细菌感染而引起的。多注意休息,适当的增加营养,适当的锻炼,以及可以用点归脾丸等对症。可以用点左氧沙星治疗处理。'}
{'id': 57296, 'doc': '拉肚子的原因有很多,胃肠道疾病、腹膜炎等等都可引起腹泻。建议平时多注意饮食不要吃生冷辛辣的食物,建议去医院就诊排除身体疾病,可以多吃芹菜或者粗粮有利于大便成型,喝淡盐水不错体内丢失得电解质'}
{'id': 64124, 'doc': '你的情况有可能是由于贫血或者低血压引起的,拉肚子的话说明肠胃功能不太好。建议,你呢最好到医院查个血常规,查个血压,另外做个腹部B超,希望对你有所帮助。'}
###### search result intersect by Dual-path Recall Search: 拉肚子如何引起的
{'id': 57296, 'doc': '拉肚子的原因有很多,胃肠道疾病、腹膜炎等等都可引起腹泻。建议平时多注意饮食不要吃生冷辛辣的食物,建议去医院就诊排除身体疾病,可以多吃芹菜或者粗粮有利于大便成型,喝淡盐水不错体内丢失得电解质'}
根据场景 2 的结果,我们发现,从用户输入自然语言“拉肚子如何引起的”,可以使用向量检索和全文检索的综合结果,给用户返回答案。
比如:Beijing 表示北京,”Beijing 哪家儿童医院好”问题
$ python words_cosine.py -m search -i "Beijing" -p 1000 -t 5
search result by keyword: Beijing , search time: 1 sec
{'id': 117662, 'doc': '这个要看当地的经济水平和消费水平而定,每个地方都是不同的', 'distance': 0.6571737539592974}
{'id': 203349, 'doc': '对于这样的病情可以选择北京第一人民医院', 'distance': 0.6604393665814006}
{'id': 104574, 'doc': '朋友根据你的描述情况分析。最好北京儿童医院看看为好,哪里最权威的', 'distance': 0.6646036614960786}
{'id': 109205, 'doc': '建议你最好北京同仁堂医院治疗,哪里最权威的', 'distance': 0.672954438488999}
{'id': 37286, 'doc': '省肿瘤医院或者三甲医院,都是非常不错的,权威医院。建议采用生物疗法,通过扩增人体免疫细胞杀灭肉眼看不见的癌细胞,是现在有望完全杀灭癌细胞的治疗方式。', 'distance': 0.6745664406499945}
&&&&&& search result by key word: Beijing , search time: 1 sec
###### search result intersect by Dual-path Recall Search: Beijing
# 识别拼音+汉字术语的结合
python words_cosine.py -m search -i "Beijing 哪家儿童医院好" -p 1000 -t 5
search result by keyword: Beijing 哪家儿童医院好 , search time: 1 sec
{'id': 104574, 'doc': '朋友根据你的描述情况分析。最好北京儿童医院看看为好,哪里最权威的', 'distance': 0.10822615933197166}
{'id': 16906, 'doc': '推荐首都儿童医院。生活调理', 'distance': 0.12438867180734758}
{'id': 20514, 'doc': '如果距离不远的话最好带孩子去北京大医院治疗,因为北京权威专家较多并且专科专家都比较专业', 'distance': 0.15558559466294808}
{'id': 47187, 'doc': '建议你注意可以到北京的儿童医院进行详细的检查和手术', 'distance': 0.17946846809910877}
{'id': 75197, 'doc': '最好要带孩子到当地正规三甲医院接骨才可以的。', 'distance': 0.23162300321916685}
select id, doc from text_embedding_cos where to_tsvector('simple', keywords) @@ 'Beijing' and to_tsvector('simple', keywords) @@ '儿童' and to_tsvector('simple', keywords) @@ '医院' limit 5
&&&&&& search result by key word: Beijing 哪家儿童医院好 , search time: 1 sec
###### search result intersect by Dual-path Recall Search: Beijing 哪家儿童医院好
在场景 3,我们发现全文索引不适合拼音表示汉字,或者拼音+汉字表示一句话的情况,而这种场景,向量相似搜索可以正确返回结果,可以看出向量相似性检索有独特的价值。
比如:冀北与河北的关系。
python words_cosine.py -m search -i "冀北" -p 1000 -t 5
** search result by embedding: 冀北 , search time: 1 sec
{'id': 82984, 'doc': '可以来河北的以岭医院啊', 'distance': 0.40261565594785054}
{'id': 49759, 'doc': '胡子拔了是会再次长的。', 'distance': 0.4844793075170043}
&&&&&& search result by key word: 冀北 , search time: 1 sec
###### search result intersect by Dual-path Recall Search: 冀北
与场景 3 类似,场景 4 是城市的别称,这种情况全文检索,可以考虑同义词来实现,但是向量检索,可以直接找到相似结果。
- 模拟业务场景 5:理解用户说的自然语言中包含的病理
python words_cosine.py -m search -i "睡眠时咳嗽,坐立不咳嗽" -p 1000 -t 5
search result by keyword: 睡眠时咳嗽,坐立不咳嗽 , search time: 1 sec****** search result by embedding: 睡眠时咳嗽,坐立不咳嗽 , search time: 1 sec
{'id': 56833, 'doc': '这情况首先还是考虑支气管炎或扁桃体或咽喉炎的可能。睡眠的时候咳嗽加重应该是与睡觉的姿势导致呼吸不通畅和冷空气刺激有关系的啊。最好还是应该加上抗生素口服。或适当加上激素,有痰的加上鲜竹沥口服试试。另外含化华素片润喉。而且睡觉枕头稍微高一点点啊,一定要注意尽可能少讲话啊,一定注意禁辛辣食物。一定要多喝水。可以使用金银花泡水喝效果更好。', 'distance': 0.1448146796233324}
{'id': 55090, 'doc': '首先还是考虑支气管炎或扁桃体或咽喉炎的可能啊。睡眠的时候咳嗽加重应该是与睡觉的姿势导致呼吸不通畅和冷空气刺激有关系的啊。建议你可以多喝温开水可以服用阿莫西林和罗红霉素以抗炎治疗加用甘草片以化痰止咳治疗,平时注意休息,饮食尽量清淡。', 'distance': 0.17095226542154318}
{'id': 3624, 'doc': '夜间阵发性咳嗽,咳痰,胸闷,考虑支气管炎,可能性非常大。但是不能除外肺结核的可能。另外心脏疾患时也会引起阵发性咳嗽胸闷,也需要排查一下。建议最好进一步检查,血常规,胸部x线,心电图,心肌酶。除外上述疾病。及早治疗以免延误病情,如支气管炎,那么应用抗菌药物治疗,戒烟。', 'distance': 0.2226150984141103}
{'id': 105964, 'doc': '咳嗽是呼吸系统疾病的症状,咳嗽无痰或痰量很少为干咳,常见于急性咽喉炎、支气管炎的初期。急性骤然发生的咳嗽,见于支气管内异物。长期慢性咳嗽,多见于慢性支气管炎、肺结核等。建议你去医院拍胸片检查看看,确诊病因在治疗。', 'distance': 0.2231365263749283}
{'id': 22492, 'doc': '咳嗽是白天咳的多还是晚上?你这种情况应该去看呼吸内科,拍一个胸部ct。', 'distance': 0.22576419526599334}
&&&&&& search result by key word: 睡眠时咳嗽,坐立不咳嗽 , search time: 1 sec
###### search result intersect by Dual-path Recall Search: 睡眠时咳嗽,坐立不咳嗽
场景 5,是一个比较复杂的自然语言描述,它是一个整体才能指明一个病情(很可能是支气管炎),单独拆开某个词语(咳嗽或睡眠),不能找到准确的病理,这样的场景全文检索也很难解决,而向量相似检索则可以弥补。
总之,向量相似性检索和全文检索,两种方式的结合,可以适应更多的用户场景。
使用几何距离索引的效果
以上的测试,在向量索引方面选择了 pgvector 的余弦距离索引,如果你想尝试几何距离索引的效果,参考如下。
使用相同的数据规模,相同的向量数据,只是修改索引类型为几何索引。
create table text_embedding as select * from text_embedding_cos;
CREATE INDEX ON text_embedding USING ivfflat (embedding_doc vector_l2_ops) WITH(lists = 10000);
create index idx_gin_keywords on text_embedding using GIN(to_tsvector('simple', keywords));
jctest=> \d+ text_embedding;
Table "public.text_embedding"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------------+--------------+-----------+----------+---------+----------+--------------+-------------
id | integer | | not null | | plain | |
doc | text | | | | extended | |
embedding_doc | vector(1536) | | | | extended | |
doc_type | integer | | | | plain | |
keywords | text | | | | extended | |
Indexes:
"text_embedding_pkey" PRIMARY KEY, btree (id)
"idx_gin_keywords" gin (to_tsvector('simple'::regconfig, keywords))
"text_embedding_embedding_doc_idx" ivfflat (embedding_doc) WITH (lists='10000')
Access method: heap
#几何距离搜索语法(ORDER BY后的操作符号区别)
WITH probe AS (SELECT random_array(1536)::VECTOR(1536) AS v)
SELECT id,doc FROM text_embedding_cos
ORDER BY embedding_doc <-> (SELECT v FROM probe) limit 5;
在本文的场景下,使用余弦距离的索引检索与使用几何距离索引检索的效果差异不大;你的业务具体的选择哪一种方式,需要根据业务场景进行测试和验证。另外,除了 ivfflat 索引算法,pgvector 也可以使用 HNSW 算法类型进行索引(自 pgvector 版本 0.5.0 开始支持)。HNSW 算法可创建多层图,它的查询性能比 IVFFlat 好(召回速度上可能需要权衡),但构建时间较慢且使用更多内存。此外,由于没有像 IVFFlat 这样的训练步骤,因此可以在表中没有任何数据的情况下创建索引。详细参考:Open-source vector similarity search for Postgres。
方案总结
本次测试,我们选择 Aurora PostgreSQL 13 作为医疗文献系统中,RAG 整体方案的 Knowledge Base,并实现了向量和全文检索的双路召回验证,可以较为准确的根据关键字术语检索医疗文献摘要,达到了减小方案中 Medical data 的目标。方案利用了 Aurora PostgreSQL 的向量插件 pgvector 存储和构建向量索引,也使用了 Aurora PostgreSQL 提供的 to_tsvector 函数和 GIN 倒排索引构建全文检索功能,同时在分词方面引入了针对医疗术语训练的插件 pkuseg。总结起来,可以满足如下需求:
- 搜索文字较长医疗术语时,可以返回最接近的术语或摘要;比如:用户搜索”糖尿病外周神经病变”。其一是利用了向量检索的相似检索功能,以及结合医疗数据训练后的中文分词技术进一步提高专业医疗术语的召回效果。
- 问题中的专业医疗术语可以识别清楚,常见症状识别准确,这方面主要得益于向量相似性检索。比如:“睡眠时咳嗽,坐立不咳嗽”可以识别到“睡眠的时候咳嗽加重”“夜间阵发性咳嗽”。
- 向量相似性检索可识别拼音表示城市,比如:Beijing 搜索到北京和首都。
- 向量相似性检索,可以识别省市的别称(同义词),比如:用户输入冀北,文档搜索范围“河北”。
对于上文的需求优化,我们成功的根据关键字术语或用户的自然语言,找到了与之接近的专业医疗文献的摘要,达到了降低 Medical data 的目标。当然,本方案也有不完美之处,后续可以引入双路召回结果的评分机制,结合其他搜索结果排序的算法来进一步优化。
本篇作者