最近我读了一篇文章,介绍了 neo4j-runway。根据他们的 Github 页面, “Neo4j Runway 是一个 Python 库,简化了将关系型数据迁移到图数据的过程。它提供了与 OpenAI 通信的工具,用于对数据进行发现并生成数据模型,以及生成数据导入代码并将数据加载到 Neo4j 实例中的工具”。通过上传一个 CSV 文件,LLM 将找到节点和关系,并自动生成一个知识图谱。
在医疗保健中,知识图谱是一种强大的工具,用于组织和分析复杂的医疗数据。这些图谱以一种更易于理解不同实体之间关系的方式结构化信息,例如疾病、治疗、患者和医疗服务提供者。
KG在医疗领域提供了一些有用的应用:
- 集成多种数据源: 知识图谱可以整合来自电子健康记录(EHRs)、医学研究论文、临床试验结果、基因组数据和患者病史等多种来源的数据。
- 改善临床决策支持: 通过将症状、诊断、治疗和结果联系起来,知识图谱可以增强临床决策支持系统(CDSS),考虑到它们考虑了大量相互关联的医学知识,从而可能提高诊断准确性和治疗效果。在本文中,我将探讨这一主题。
- 个性化医疗: 知识图谱能够通过将患者特定数据与更广泛的医学知识相关联,来开发个性化的治疗方案。这包括理解遗传信息、疾病机制和治疗反应之间的关系,从而提供更个性化的医疗干预。
- 药物发现和开发: 在药物研究中,知识图谱可以通过识别潜在的药物靶点和理解疾病相关的生物途径来加速药物发现。
- 公共卫生和流行病学: 在公共卫生领域,知识图谱可用于追踪疾病爆发、理解流行病学趋势并规划干预措施,因为它们可以整合来自各种公共卫生数据库、社交媒体和其他来源的数据,从而提供对公共卫生威胁的实时洞察。
Neo4j Runway 是由 Alex Gilmore 创建的一个开源库。你可以在 这里 找到该库的仓库,并在 这里 阅读一篇描述该库的博客文章。
目前,此库仅支持使用 OpenAI LLM 解析 CSV 文件,并提供以下功能:
- 数据发现: 利用OpenAI的大语言模型从您的数据中提取有意义的见解。
- 图数据建模: 使用OpenAI和Instructor Python库开发准确的图数据模型。
- 代码生成: 创建符合您首选数据加载方法的加载代码。
- 数据加载: 使用Runway内置的PyIngest实现(一种广泛使用的Neo4j数据加载工具)加载您的数据。
- 无需编写Cypher查询, 因为大语言模型会完成所有工作。
在这里,除了让大语言模型(LLM)完成所有从CSV到知识图谱的转换外,我还使用了Langchain的_GraphCypherQAChain_作为最后一步,从提示中生成Cypher查询,这样我们就可以在不编写任何Cypher代码(用于查询Neo4j图数据库的类似SQL的语言)的情况下查询图谱。
该库的Github页面有一个金融示例,但我希望测试它在医疗保健场景中的效果。从Kaggle上的一个非常简单的数据集开始(疾病症状和患者资料数据集),该数据集只有10列(疾病、发热、咳嗽、疲劳、呼吸困难、年龄、性别、血压、胆固醇水平和结果变量),我希望能够将医疗报告提供给LLM以获取诊断假设。
让我们直接进入代码。首先,导入所需的库:
sudo apt install python3-pydot graphviz
pip install neo4j-runway
import numpy as np
import pandas as pd
from neo4j_runway import Discovery, GraphDataModeler, IngestionGenerator, LLM, PyIngest
from IPython.display import display, Markdown, Image
加载环境变量:你可以阅读我的另一篇文章了解如何在Neo4j Aura中创建实例并进行身份验证。
load_dotenv()
OPENAI_API_KEY = os.getenv('sk-openaiapikeyhere')
NEO4J_URL = os.getenv('neo4j+s://your.databases.neo4j.io')
NEO4J_PASSWORD = os.getenv('yourneo4jpassword')
现在,让我们加载医疗数据。从Kaggle网站下载CSV文件,并将其加载到Jupyter笔记本中。这是一个非常简单的数据集,但对测试某个概念很有用。
disease_df = pd.read_csv('/home/user/Disease_symptom.csv')
disease_df
例如,我们可以创建一个所有引起呼吸困难的疾病的列表,这不仅对于在图中选择节点很有用,而且对于开发诊断假设也很有趣:
disease_df[disease_df['呼吸困难']=='是']
让我们继续。所有的变量都必须是字符串(库就是这样设计的),即使是整数。然后,我们保存 CSV 文件:
disease_df.columns = disease_df.columns.str.strip()
for i in disease_df.columns:
disease_df[i] = disease_df[i].astype(str)
disease_df.to_csv('/home/user/disease_prepared.csv', index=False)
现在,我们将描述用于LLM的数据,包括每个字段可能的值:
DATA_DESCRIPTION = {
'疾病': '疾病的名称或医疗状况。',
'发热': '患者是否有发热(是/否)。',
'咳嗽': '患者是否有咳嗽(是/否)。',
'疲劳': '患者是否感到疲劳(是/否)。',
'呼吸困难': '患者是否有呼吸困难(是/否)。',
'年龄': '患者的年龄(岁)。',
'性别': '患者的性别(男/女)。',
'血压': '患者的血压水平(正常/高)。',
'胆固醇水平': '患者的胆固醇水平(正常/高)。',
'结果变量': '表示特定疾病诊断或评估结果的结果变量(阳性/阴性)。'
}
下一步是让LLM分析表格数据,以识别生成图数据模型所需的重要数据元素。
disc = Discovery(llm=llm, user_input=DATA_DESCRIPTION, data=disease_df)
disc.run()
这将生成数据分析的 Markdown 输出:
好的。现在,让我们创建初始模型:
# 实例化图数据建模器
gdm = GraphDataModeler(llm=llm, discovery=disc)
# 生成模型
gdm.create_initial_model()
# 可视化数据模型
gdm.current_model.visualize()
在这里,我的重点是疾病,所以我们需要重新排列一些关系。
gdm.iterate_model(user_corrections='''
让我们一步一步地思考。请对数据模型进行以下更新:
1. 删除患者与疾病之间的关系,患者与症状之间的关系以及患者与结果之间的关系。
2. 将患者节点改为人口统计信息。
3. 从疾病到人口统计信息创建 HAS_DEMOGRAPHICS 关系。
4. 从疾病到症状创建 HAS_SYMPTOM 关系。如果症状值为 No,则删除此关系。
5. 从疾病到健康指标创建 HAS_LAB 关系。
6. 从疾病到结果创建 HAS_OUTCOME 关系。
''')
from IPython.display import Image, display
gdm.current_model.visualize().render('output', format='png')
# 加载并以特定宽度显示图像
img = Image('output.png', width=1200) # 根据需要调整宽度
display(img)
现在我们可以生成Cypher代码和YAML文件,将数据加载到Neo4j中。以防万一,如果你只是在测试或第二次执行此操作,你可能希望重置实例为空(清除所有内容)。
# 实例化数据加载生成器
gen = IngestionGenerator(data_model=gdm.current_model,
username="neo4j",
password='yourneo4jpasswordhere',
uri='neo4j+s://123654888.databases.neo4j.io',
database="neo4j",
csv_dir="/home/user/",
csv_name="disease_prepared.csv")
# 创建数据加载YAML
pyingest_yaml = gen.generate_pyingest_yaml_string()
# 保存YAML文件的本地副本
gen.generate_pyingest_yaml_file(file_name="disease_prepared")
一切都准备好了。让我们将数据加载到实例中:
PyIngest(yaml_string=pyingest_yaml, dataframe=disease_df)
访问 Neo4j Aura 实例 ,点击 打开 ,输入你的密码,然后通过 Cypher 运行以下查询:
MATCH (n)
WHERE n:Demographics OR n:Disease OR n:Symptom OR n:Outcome OR n:HealthIndicator
OPTIONAL MATCH (n)-[r]->(m)
RETURN n, r, m
CTRL + ENTER 后你会看到这个:
检查节点和关系,我们发现症状、健康指标和人口统计信息之间存在着巨大的相互联系:
让我们来看一下糖尿病:由于没有应用任何过滤条件,男性和女性都会出现,以及所有 LAB、DEMOGRAPHIC 和 OUTCOME 的可能性。
MATCH (n:Disease {name: 'Diabetes'})
WHERE n:Demographics OR n:Disease OR n:Symptom OR n:Outcome OR n:HealthIndicator
OPTIONAL MATCH (n)-[r]->(m)
RETURN n, r, m
或者可能是所有在临床检查中呈现高血压的疾病:
// 匹配 Disease 节点
MATCH (d:Disease)
// 匹配从 Disease 节点到 Lab 节点的 HAS_LAB 关系
MATCH (d)-[r:HAS_LAB]->(l)
MATCH (d)-[r2:HAS_OUTCOME]->(o)
// 确保 Lab 节点的 bloodPressure 属性设置为 'High'
WHERE l.bloodPressure = 'High' AND o.result='Positive'
RETURN d, properties(d) AS disease_properties, r, properties(r) AS relationship_properties, l, properties(l) AS lab_properties
现在我的目标已经很明确了:我想将一份医疗报告提交给一个大型语言模型(LLM),在这个例子中是来自谷歌的 Gemini-1.5-Flash ,通过Langchain(GraphCypherQAChain)自动生成Cypher查询,以便根据患者的症状、健康指标等信息返回可能的疾病。让我们开始吧:
import warnings
import json
from langchain_community.graphs import Neo4jGraph
with warnings.catch_warnings():
warnings.simplefilter('ignore')
NEO4J_USERNAME = "neo4j"
NEO4J_DATABASE = 'neo4j'
NEO4J_URI = 'neo4j+s://1236547.databases.neo4j.io'
NEO4J_PASSWORD = 'yourneo4jdatabasepasswordhere'
从实例和模式中获取知识图谱:这里你有节点属性和关系属性。
kg = Neo4jGraph(
url=NEO4J_URI, username=NEO4J_USERNAME, password=NEO4J_PASSWORD, database=NEO4J_DATABASE
)
kg.refresh_schema()
print(textwrap.fill(kg.schema, 60))
schema=kg.schema
让我们初始化 Vertex AI Gemini-1.5-Flash:
from langchain.prompts.prompt import PromptTemplate
from langchain.chains import GraphCypherQAChain
from langchain.llms import VertexAI
# 初始化 Vertex AI
vertexai.init(project="your-project", location="us-west4")
llm = VertexAI(model="gemini-1.5-flash")
现在,最难的部分来了:为 Gemini-1.5-Flash 创建一个详细的指令,使其能够自动生成查询图数据库的 Cypher 语句,并为我们提供所需的结果。我们需要一个 MASTER 提示来完成这个任务!😂😂😂😂 CoT + 少样本提示。
prompt_template = """
让我们一步一步来思考:
步骤 1:任务:
生成一个有效且简洁的 Cypher 语句,查询图数据库,长度不超过 256 个字符
不要对代码进行注释。
步骤 2:了解数据库模式: {schema}
步骤 3:指令:
- 在 Cypher 查询中,只使用提供的关系类型和模式中出现的属性。
- 在 Cypher 查询中,不要使用用户问题中未包含在提供的模式中的任何其他关系类型或属性。
- 关于年龄,永远不要直接使用年龄本身。例如:24 岁,使用区间:超过 20 岁。
- 在 Cypher 查询中,年龄只使用一次,始终使用 '大于',从不使用 '小于' 或 '等于'。
- 不要使用数据库中不存在的属性键。
步骤 4:示例:
以下是为特定问题生成的 Cypher 语句示例:
4.1 哪些疾病伴有高血压?
MATCH (d:Disease)
MATCH (d)-[r:HAS_LAB]->(l)
WHERE l.bloodPressure = 'High'
RETURN d.name
4.2 哪些疾病伴有高血压等指标?
// 匹配疾病节点
MATCH (d:Disease)
// 匹配疾病节点到实验室节点的 HAS_LAB 关系
MATCH (d)-[r:HAS_LAB]->(l)
MATCH (d)-[r2:HAS_OUTCOME]->(o)
// 确保实验室节点的 bloodPressure 属性设置为 'High'
WHERE l.bloodPressure = 'High' AND o.result='Positive'
RETURN d, properties(d) AS disease_properties, r, properties(r) AS relationship_properties, l, properties(l) AS lab_properties
4.3 老年人中伴有高血压、高胆固醇、发热、疲劳的疾病名称是什么?
MATCH (d:Disease)
MATCH (d)-[r1:HAS_LAB]->(lab)
MATCH (d)-[r2:HAS_SYMPTOM]->(symptom)
MATCH (symptom)-[r3:HAS_DEMOGRAPHICS]->(demo)
WHERE lab.bloodPressure = 'High' AND lab.cholesterolLevel = 'High' AND symptom.fever = 'Yes' AND symptom.fatigue = 'Yes' AND TOINTEGER(demo.age) >40
RETURN d.name
4.4 对于高胆固醇的人,哪些疾病伴有发热、疲劳、无咳嗽、无呼吸困难?
MATCH (d:Disease)-[r:HAS_SYMPTOM]->(s:Symptom)
WHERE s.fever = 'Yes' AND s.fatigue = 'Yes' AND s.difficultyBreathing = 'No' AND s.cough = 'No'
MATCH (d:Disease)-[r1:HAS_LAB]->(lab:HealthIndicator)
MATCH (d)-[r2:HAS_OUTCOME]->(o:Outcome)
WHERE lab.cholesterolLevel='High' AND o.result='Positive'
RETURN d, properties(d) AS disease_properties, r, properties(r) AS relationship_properties
步骤 5:这些是每个实体允许的值:
- 发热:表示患者是否有发热(是/否)。
- 咳嗽:表示患者是否有咳嗽(是/否)。
- 疲劳:表示患者是否有疲劳(是/否)。
- 呼吸困难:表示患者是否有呼吸困难(是/否)。
- 年龄:患者的年龄(年)。
- 性别:患者的性别(男/女)。
- 血压:患者的血压水平(正常/高血压)。
- 胆固醇水平:患者的胆固醇水平(正常/高血压)。
- 结果变量:表示特定疾病诊断或评估结果的变量(阳性/阴性)。
步骤 6:回答问题 {question}。"""
我们设置了GraphCypherQAChain…
cypher_prompt = PromptTemplate(
input_variables=["schema","question"],
template=prompt_template
)
cypherChain = GraphCypherQAChain.from_llm(
VertexAI(temperature=0.1),
graph=kg,
verbose=True,
cypher_prompt=cypher_prompt,
top_k=10 # 这个值也可以调整
)
… 并提交医疗报告:
cypherChain.run("""
患者信息:
Jane Doe,一名58岁的女性,于2024年6月15日入院。
主诉及现病史:
Jane 报告了高达104°F的高烧、身体疼痛和皮疹,这些症状从入院前五天开始。
既往病史:
Jane 没有重要的既往病史,也没有已知的过敏史。
体格检查:
Jane 的体温为102.8°F,心率为110次/分钟,血压为100/70 mmHg,呼吸频率为每分钟20次。未发现紫癜或瘀点。
她可能患有哪种疾病?""")
输出:这里 Gemini-.5-Flash 生成用于查询图数据库的 Cypher 查询,通过 JSON 返回结果给 LLM,LLM 解释这些结果并返回可读的响应:
这个结果没有考虑 Gemini-1.5-Flash 的知识库,而只是它查询的知识图谱。想象一下如果我们有一个包含300个特征的漂亮数据集!
注意,我们可以将 GraphCypherQAChain 中的 _topk 调整为 1 或任何其他值:
如果我们运行这个最后一个查询,我们将得到具有这些症状的77种疾病的列表,但 top_k 设置为 1 :
当前的 neo4j-runway 项目处于测试阶段,并存在以下限制:
- 数据模型生成仅支持单个 CSV 输入
- 节点只能有一个标签
- 仅支持唯一性和节点/关系键约束
- 关系不支持唯一性约束
- 数据模型生成不支持引用同一节点属性的 CSV 列
- 当前仅支持 OpenAI 模型
- Runway 中包含的修改版 PyIngest 函数仅支持加载本地的 Pandas 数据框或 CSV 文件
致谢
✨ Google 机器学习开发者计划和 Google Cloud Champion 创新者计划通过提供 Google Cloud 信用额度支持了这项工作 ✨
共同学习,写下你的评论
评论加载中...
作者其他优质文章