医学知识图谱在疾病诊断中的应用研究
作者:禅与计算机程序设计艺术
近年来
- 构建医学知识图谱及其相关产物的方法和工具。
- 借助医学知识图谱对病灶特征及其相互间的相似程度展开描述和计算的方法及工具。
- 整合疾病特征信息与其临床症状及辅助检查结果等数据实现匹配的方法和工具。
- 采用机器学习模型对现有病例数据进行训练以提高诊断准确性的方法及应用系统。
- 开发流程包括从系统架构设计到实际运行维护的过程。
此外,在本文中还将在上述问题上进行深入探讨,并系统阐述其研究思路及其具体的解决方案,并将这些方法应用于现有的疾病诊断平台上,并探讨其实用性和理论创新价值
2.基本概念术语说明
2.1 什么是医学知识图谱?
就Medical Knowledge Graph而言,在信息科学领域具有重要的研究价值。它遵循图论的基本原则,在数据组织形式上具有显著特征:以实体及其关联关系为基本单元构建语义网络模型,并以此实现对复杂信息的知识表示与有效处理。随着互联网技术的发展以及各种类型Knowledge Graph Data Sets的快速增长趋势,在医疗健康领域取得显著进展的同时也引起了广泛关注
2.2 KG和生物医学领域的其他知识库有何区别?
另一方面,在生物医学领域中,
除了传统的生物信息学数据库和常规数据库之外,
还有如KEGG、BioPortal、NCBI Gene Wiki、UniProt、PubChem等知识库。
这些数据库在体系化程度上有各自的特点,
同时具备不同的可解释性和质量保证水平。
然而,
它们之间又存在显著的重叠区域,
例如均提供蛋白质序列信息,
因此在某些方面存在冗余存储。
相比之下,
医学知识图谱的构建方法则更加注重从文本中直接推导出实体及其相互关系。
因此,在这种背景下,
构建一个有效的医学知识图谱变得尤为重要,
因为它能够实现各类信息的有效整合并提供更高层次的分析能力。
例如,
通过复杂的关系网络(S-R)、
分子机制-药理学关联(M-L)以及生化学信息-风险评估关联(C-E)
等多维度联系模式,
可以使疾病研究更加全面深入。
2.3 有哪些常用的KG构建工具或工具集?
基于现有的工具有哪些?常见用于构建知识图谱(KG)的工具有Wikidata、BabelNet、OpenPHACTS和Resource Tagger等。其中主要涉及以下几种:Wikidata 和 BabelNet 是两种主流的知识图谱构建工具有哪些? OpenPHACTS 是一个基于 Web 的专利数据集以及知识图谱构建工具有哪些? Resource Tagger 则是一个自动化知识图谱构建工具有哪些?它能够自动识别大量文本资源并生成相应的 RDF 或 OWL 文件等信息。这些工具有哪些具备满足绝大多数的知识图谱构建需求的能力
3.核心算法原理和具体操作步骤以及数学公式讲解
3.1 建立医学知识图谱的关键步骤
(1)获取文本数据
首先阶段的目标在于收集涉及疾病描述等医学领域的文本数据。在这一过程中可能会遇到的不同类别信息包括:诊断报告中的原始记录内容、临床症状的具体描述以及具体的药物治疗方法等。值得注意的是,在处理定期检查记录或影像学图像数据时,则需要采取更为复杂的处理措施。
(2)利用文本数据构建单词-短语-实体三元组
在第二阶段中, 我们旨在将原始文本数据映射为实体与属性之间的关联关系, 即将每个描述符与其对应的实体及该实体的属性建立联系. 为此, 可以应用特定规则集合或句法分析方法来进行关联. 例如, 在处理医学文本数据时, 我们可以利用正则表达式或句法规则来实现描述符与相关实体的对应关系. 这种方法能够有效地提取知识信息.
(3)导入外部知识源
第三步,在下一步骤中涉及的步骤是引入其他外部的知识库。如维基百科、Wikipedia等平台将被用来补充和完善现有知识图谱中所缺少的重要实体及其属性信息。即便采用人工方式进行补充也会存在一定的局限性因此建议优先考虑自动化的方式来进行数据导入
(4)实体链接
第四步中我们计划使用实体链接技术对知识图谱中的各个实体进行整合确保它们能够指向同一个真实且准确的主体具体而言这涉及将相似实体名称归并消除歧义以及进行名称替换等手段
(5)构建适合疾病描述的知识图谱
在这一阶段, 我们需要为特定疾病描述任务服务, 将知识图谱整合到其他相关数据集, 构建相应的知识图谱结构. 这一阶段通常采用以下几种策略:
- 采用标签推荐技术筛选疾病描述中的相关术语,并将这些术语与其对应的实体建立联系。
- 将所有实体进行分类处理,并区分大小类以便于后续分析;这有助于在预测疾病描述中的实体使用场景时提供参考依据。
- 基于知识图谱的路径信息构建有向边概率模型(Directed Edge Probability Network),以量化不同实体间的关联程度。
- 通过结合规则与统计分析的方法标注知识图谱中的实体及其属性信息;以此为基础训练机器学习模型实现精准预测目标。
4.具体代码实例和解释说明
4.1 Python代码实例——构建WikiData上的医学知识图谱
为了实现部署Pywikibot、SPARQLWrapper和rdflib模块的目的,在Python环境中可以通过按照以下步骤输入命令来完成安装。
pip install pywikibot==2.0.0b4 sparqlwrapper rdflib
然后,创建一个脚本文件build_kg.py,编写如下代码:
import os
from SPARQLWrapper import SPARQLWrapper, JSON
import requests
import json
from rdflib import Graph, Namespace, RDF, Literal
def query_wikidata(query):
# 查询Wikidata,获取结果
endpoint = "https://query.wikidata.org/sparql"
user_agent = "My User Agent 1.0"
# 设置HTTP头部
headers = {
'User-Agent': user_agent,
'Accept': 'application/json'
}
params = {'format': 'json', 'query': query}
try:
response = requests.get(endpoint, headers=headers, params=params)
if response.status_code == 200:
data = response.json()
return data['results']['bindings']
else:
print("Failed to retrieve results from Wikidata")
except Exception as e:
print(e)
def build_kg():
kg = Graph()
# 添加命名空间
wd_ns = Namespace('http://www.wikidata.org/entity/')
p_ns = Namespace('http://www.wikidata.org/prop/')
skos_ns = Namespace('http://www.w3.org/2004/02/skos/core#')
rdfs_ns = Namespace('http://www.w3.org/2000/01/rdf-schema#')
schema_ns = Namespace('http://schema.org/')
kg.bind('wd', str(wd_ns))
kg.bind('p', str(p_ns))
kg.bind('skos', str(skos_ns))
kg.bind('rdfs', str(rdfs_ns))
kg.bind('schema', str(schema_ns))
# 查询疾病实体列表
disease_list = ['Q15923764', 'Q12136', 'Q3869627']
for item in disease_list:
# 查询实体及其属性
entity_url = f'{str(wd_ns)}{item}'
properties = []
q = """SELECT DISTINCT?property WHERE {{
<{}>?property?value.
}}""".format(entity_url)
res = query_wikidata(q)
for row in res:
prop_id = row['property']['value'].split('/')[-1]
prop_uri = None
if prop_id == 'P1748':
continue
prop_uri = '{}{}{}'.format(str(p_ns), prop_id[0], prop_id[1:])
elif prop_id == 'P279':
prop_uri = '{}{}'.format(str(p_ns), prop_id)
elif prop_id[:1].isupper():
prop_uri = '{}{}'.format(str(schema_ns), prop_id)
else:
prop_uri = '{}{}'.format(str(p_ns), prop_id)
if not prop_uri:
continue
prop_label = ''
label_q = """SELECT DISTINCT?label WHERE {{
<{}> rdfs:label|skos:altLabel?label.
}}""".format(prop_uri)
labels = query_wikidata(label_q)
for lbl in labels:
prop_label = lbl['label']['value']
break
if not prop_label:
continue
property_data = {}
value_q = """SELECT?value WHERE {{
<{}> {}?value.
}}""".format(entity_url, prop_uri)
values = query_wikidata(value_q)
if len(values) > 0 and prop_uri!= 'http://www.wikidata.org/prop/direct/P31':
obj_val = [v['value'] for v in values][0].split('/')[-1]
object_url = '{}{}'.format(str(wd_ns), obj_val)
obj_type = query_wikidata("""SELECT (COUNT(*) AS?count) WHERE {{
<{}> a?type.
}}""".format(object_url))[0]['type']['value'].split('/')[-1]
object_label = ''
if obj_type == 'Q15923764':
object_label = 'disease'
elif obj_type == 'Q12136':
object_label = 'phenotype'
elif obj_type == 'Q3869627':
object_label = 'gene'
object_label_q = """SELECT DISTINCT?label WHERE {{
<{}> rdfs:label?label.
}}""".format(object_url)
objects_labels = query_wikidata(object_label_q)
for olbl in objects_labels:
object_label = olbl['label']['value']
break
property_data = {'obj_type': obj_type,
'obj_val': obj_val,
'obj_label': object_label,
'prop_uri': prop_uri,
'prop_label': prop_label}
properties.append({'prop_id': prop_id,
'prop_uri': prop_uri,
'prop_label': prop_label,
'property_data': property_data})
# 生成节点
node_label = ''
node_description = ''
name_q = """SELECT DISTINCT?name WHERE {{
<{}> rdfs:label|skos:altLabel?name FILTER (LANG(?name)="en")
}}""".format(entity_url)
names = query_wikidata(name_q)
for name in names:
node_label = name['name']['value']
break
desc_q = """SELECT DISTINCT?desc WHERE {{
<{}> schema:description|schema:abstract?desc FILTER (LANG(?desc)="en")
}}""".format(entity_url)
descs = query_wikidata(desc_q)
for dsc in descs:
node_description = dsc['desc']['value']
break
node = kg.resource(str(entity_url))
node.add(RDF.type, wd_ns[node_label])
node.add(rdfs_ns.label, Literal(node_label))
node.add(skos_ns.prefLabel, Literal(node_label))
if node_description:
node.add(schema_ns.description, Literal(node_description))
# 生成边
for prop in properties:
pred = prop['prop_uri']
predicate_label = ''
labels_q = """SELECT DISTINCT?pred_label WHERE {{
<{}> rdfs:label?pred_label FILTER (LANG(?pred_label)="en")
}}""".format(pred)
labels = query_wikidata(labels_q)
for l in labels:
predicate_label = l['pred_label']['value']
break
rel = kg.resource(str(pred))
rel.add(RDF.type, RDF.Property)
rel.add(rdfs_ns.label, Literal(predicate_label))
rel.add(skos_ns.prefLabel, Literal(predicate_label))
edge_subject = kg.resource(str(entity_url))
edge_object = None
if prop['property_data']:
edge_object = kg.resource('{}/{}'.format(str(wd_ns), prop['property_data']['obj_val']))
edge_object.add(RDF.type, wd_ns[prop['property_data']['obj_label']])
edge_object.add(rdfs_ns.label, Literal(prop['property_data']['obj_label']))
rel.add(edge_subject, edge_object)
# 保存知识图谱到文件
output_file = './diseases_graph.ttl'
with open(output_file, mode='wb') as outf:
outf.write(kg.serialize(format='turtle'))
if __name__ == '__main__':
build_kg()
该代码能够检索Wikidata上明确指定的疾病实体信息,并生成一个知识图谱文件;随后将该知识图谱文件存储于当前目录下的diseases_graph.ttl位置。
4.2 Java代码实例——基于Fuseki和OpenRefine的疾病预警系统
为了更好地进行后续操作,在实施过程中我们建议按照以下步骤进行配置:首先为确保顺利开展,请配置Java环境;随后请按照官方指南依次安装JDK环境;接着安装Apache Jena Fuseki服务器;之后设置OpenRefine客户端;最后连接DrugBank数据库以完成数据交互功能。
接着,创建一个Java项目,引入以下依赖:
- Apache Jena 核心组件
- AFuseKI 客户端
- OpenRefine API客户端库
- 药物数据库访问器项目:DBAccessors
编写配置文件config.properties,内容如下:
dbhost=localhost
dbport=3306
dbuser=<username>
dbpassword=<password>
dbname=<database_name>
fusekiHost=localhost
fusekiPort=3030
编写启动器类Main,内容如下:
public class Main {
public static void main(String[] args) throws Exception {
String kbFile = "./diseases_graph.ttl";
// 加载知识图谱
Model model = FileManager.loadModel(kbFile);
ResourceFactoryImpl factory = new ResourceFactoryImpl();
// 创建Fuseki客户端
String host = ConfigReader.getConfig().getProperty("fusekiHost");
int port = Integer.parseInt(ConfigReader.getConfig().getProperty("fusekiPort"));
FusekiClient client = new FusekiClient(factory, host, port);
// 初始化实体查询服务
QueryProcessor processor = new QueryProcessor(client, model);
System.out.println("Please enter your medical history:");
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if ("exit".equals(line)) {
break;
}
// 执行查询语句
List<QuerySolutionMap> results = processor.runQuery(line);
// 输出查询结果
if (results.isEmpty()) {
System.out.println("Sorry, I cannot find any information about that.");
} else {
for (QuerySolutionMap result : results) {
Resource resource = result.getResource("entity");
System.out.println("Possible disease symptoms include:");
Set<Statement> statements = new HashSet<>();
StatementIterator iter = model.listStatements(null, null, resource);
while (iter.hasNext()) {
Statement stmt = iter.next();
Property property = stmt.getPredicate();
if (!("rdfs:label").equals(property.getLocalName())) {
statements.add(stmt);
}
}
Map<String, Double> similarities = EntitySimilarity.computeSimilarities(model, statements);
for (Entry<String, Double> entry : similarities.entrySet()) {
Resource entity = factory.createResource(entry.getKey());
double score = entry.getValue();
NodeIterator nit = model.listObjectsOfProperty(factory.createURI("http://schema.org/sameAs"), entity);
while (nit.hasNext()) {
RDFNode node = nit.next();
URI uri = ((Literal) node).getDatatypeURI();
ValueFactory vf = SimpleValueFactory.getInstance();
DiseaseSymptom entityObj = new DiseaseSymptom(vf.createIRI(uri.toString()), "", "");
System.out.printf("%s (%.2f)
", entityObj.getName(), score);
}
}
}
}
}
scanner.close();
}
}
该代码负责向用户提示 diseases 的描述,并执行相关查询操作。当查询结果生成时,则会输出相应的 disease 症状信息。
