加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

非结构化数据-病历智能处理引擎的设计、实现和应用

(2018-01-03 10:52:32)
标签:

ai

作者 | 吴大帅,李智慧 责编 | 钱曙光:介绍了人工智能是如何进一步提高医疗效率的曲折历程,主要以病历智能处理引擎为例。文本病历的语义解析是进行病历价值挖掘的基础工作,本文介绍一个病历智能处理引擎的设计,针对医疗病历这种特殊的文本,如何确立文本知识结构,如何选择应用自然语言处理算法,针对医疗科研与智能诊疗这个场景,如何根据模板抽取特定的知识信息,如何将该引擎应用于具体的生产系统中。

最近几十年,随着医疗信息化技术的不断发展,积累了大量的病历数据,但是大部分的病历都以无结构或半结构化文本方式存储,这对病历价值的挖掘带来了很大的难度,也难以将这些宝贵的信息应用于医疗科研和智能诊疗等领域,阻碍了智能医疗的发展。病历智能处理引擎主要目标是将病历文本自动转化为更便于医疗科研和机器学习的知识结构,这是开发所有基于病历的应用的基础,也是智能医疗领域的研究热点之一。

病历智能处理引擎设计思路

病历是患者疾病发展以及诊疗的记录,是对采集的资料归纳整理、综合分析并按规定的格式和要求书写的患者医疗健康档案,对医疗、教学、科研以及医院管理有重要的作用。病历的文本性确定了引擎以NLP(自然语言处理)为核心,医疗科研等实际需求决定了引擎的上层结构,接下来,按照应用NLP的一般思路介绍病历智能处理的过程。

1. 确立文本知识结构,标准化过程

病历结构叙述一般都比较清晰,各种医疗事件描述集中,整体按照不同或相关医疗语义(这里是某病人姓名基本信息,病史为关联事件)进行句段的过度,这里我们按照原始文本的描述过程,使用递归层级结构来表示中间结果,层级中的节点,按照语法结构来存储具体的医疗事件,方便对医疗事件进行语义提取。层级结构如图1所示。

标准化过程:任何信息化都要做的,唯一性赋码,病种、诊断码要唯一,医学知识库与词库结构)医学标准的不统一以及医学术语口语化是影响病历的共享和统计的重要原因,为了提高病历智能处理引擎的效率,需要将所有的输出标准化,即所有对应同一医学实体的医学表达要统一。这里我们引入了医学知识库作为统一的标准,将所有的抽取结果转化为标准医学叙词或概念code输出。整体结构如图2所示。
http://s3/mw690/006EAWy2zy7h57iqkaC12&690

图2 医学知识库与词库结构关系

医学知识库的建设繁杂,但是对于医疗业务的可持续发展意义重大,所有基于知识库的工作成果,可以在任何时间任何地点无壁垒解读,持续的放大价值,推动医疗领域的发展。需要注意的是,医疗知识库处在不断的迭代完善过程中,我们在建设知识库的过程中,要充分考虑知识库的扩展性。

2. 应用算法处理病历文本

应用算法处理病历文本的过程,包含以下几个部分。

医学实体标记(分词+标注)首先,需要分离医学实体,这一步主要应用自然语言处理中的分词算法来实现,分词的算法有多种,每种算法有各自的优劣。我们在实际应用中在业务不同阶段使用了不同的算法,主要考量包括:一是算法的效果是否能达到我们的需要;二是算法底层数据结构是否可以实时更新、实时生效,因为我们无法一开始就建立一个非常完备的医学词库,实际应用时需要可以随时更新分词词库(内存+外部词库),让更新的词项实时生效。然后,需要对医学实体做标注,这一步相对简单,如果是使用HMM分词,可以使用维特比算法标注,也可以依赖词库标注

任何一个HMM都可以通过下列五元组来描述:param obs:观测序列;aram states:隐状态;aram start_p:初始概率(隐状态);param trans_p:转移概率(隐状态);param emit_p: 发射概率 (隐状态表现为显状态的概率)

这个例子可以用如下的HMM来描述:


  1. states = ('Rainy', 'Sunny')
  2.  
  3. observations = ('walk', 'shop', 'clean')
  4.  
  5. start_probability = {'Rainy': 0.6, 'Sunny': 0.4}
  6.  
  7. transition_probability = {
  8.     'Rainy' : {'Rainy': 0.7, 'Sunny': 0.3},
  9.     'Sunny' : {'Rainy': 0.4, 'Sunny': 0.6},
  10.     }
  11.  
  12. emission_probability = {
  13.     'Rainy' : {'walk': 0.1, 'shop': 0.4, 'clean': 0.5},
  14.     'Sunny' : {'walk': 0.6, 'shop': 0.3, 'clean': 0.1},
  15. }

求解最可能的隐状态序列是HMM的三个典型问题之一,通常用维特比算法解决。维特比算法就是求解HMM上的最短路径(-log(prob),也即是最大概率)的算法。稍微用中文讲讲思路,很明显,第一天天晴还是下雨可以算出来:定义V[时间][今天天气] = 概率,注意今天天气指的是,前几天的天气都确定下来了(概率最大)今天天气是X的概率,这里的概率就是一个累乘的概率了。    因为第一天我的朋友去散步了,所以第一天下雨的概率V[第一天][下雨] = 初始概率[下雨] * 发射概率[下雨][散步] = 0.6 * 0.1 = 0.06,同理可得V[第一天][天晴] = 0.24 。从直觉上来看,因为第一天朋友出门了,她一般喜欢在天晴的时候散步,所以第一天天晴的概率比较大,数字与直觉统一了。从第二天开始,对于每种天气Y,都有前一天天气是X的概率 * X转移到Y的概率 * Y天气下朋友进行这天这种活动的概率。因为前一天天气X有两种可能,所以Y的概率有两个,选取其中较大一个作为V[第二天][天气Y]的概率,同时将今天的天气加入到结果序列中比较V[最后一天][下雨]和[最后一天][天晴]的概率,找出较大的哪一个对应的序列,就是最终结果。

这个例子的Python代码:


  1. -*- coding:utf-8 -*-
  2. Filename: viterbi.py
  3. Author:hankcs
  4. Date: 2014-05-13 下午8:51
  5.  
  6. states = ('Rainy', 'Sunny')
  7.  
  8. observations = ('walk', 'shop', 'clean')
  9.  
  10. start_probability = {'Rainy': 0.6, 'Sunny': 0.4}
  11.  
  12. transition_probability = {
  13.     'Rainy' : {'Rainy': 0.7, 'Sunny': 0.3},
  14.     'Sunny' : {'Rainy': 0.4, 'Sunny': 0.6},
  15.     }
  16.  
  17. emission_probability = {
  18.     'Rainy' : {'walk': 0.1, 'shop': 0.4, 'clean': 0.5},
  19.     'Sunny' : {'walk': 0.6, 'shop': 0.3, 'clean': 0.1},
  20. }
  21.  
  22. 打印路径概率表
  23. def print_dptable(V):
  24.     print    ",
  25.     for in range(len(V)): print "}" % i,
  26.     print
  27.  
  28.     for in V[0].keys():
  29.         print "%.5s: " % y,
  30.         for in range(len(V)):
  31.             print "%.7s" % ("%f" % V[t][y]),
  32.         print
  33.  
  34.  
  35. def viterbi(obs, states, start_p, trans_p, emit_p):
  36.     """
  37.  
  38.     :param obs:观测序列
  39.     :param states:隐状态
  40.     :param start_p:初始概率(隐状态)
  41.     :param trans_p:转移概率(隐状态)
  42.     :param emit_p: 发射概率 (隐状态表现为显状态的概率)
  43.     :return:
  44.     """
  45.     路径概率表 V[时间][隐状态] 概率
  46.     = [{}]
  47.     一个中间变量,代表当前状态是哪个隐状态
  48.     path = {}
  49.  
  50.     初始化初始状态 (t == 0)
  51.     for in states:
  52.         V[0][y] = start_p[y] * emit_p[y][obs[0]]
  53.         path[y] = [y]
  54.  
  55.     对 跑一遍维特比算法
  56.     for in range(1, len(obs)):
  57.         V.append({})
  58.         newpath = {}
  59.  
  60.         for in states:
  61.             概率 隐状态    前状态是y0的概率 y0转移到y的概率 y表现为当前状态的概率
  62.             (prob, state) = max([(V[- 1][y0] * trans_p[y0][y] * emit_p[y][obs[t]], y0) for y0 in states])
  63.             记录最大概率
  64.             V[t][y] = prob
  65.             记录路径
  66.             newpath[y] = path[state] + [y]
  67.  
  68.         不需要保留旧路径
  69.         path = newpath
  70.  
  71.     print_dptable(V)
  72.     (prob, state) = max([(V[len(obs) - 1][y], y) for in states])
  73.     return (prob, path[state])
  74.  
  75.  
  76. def example():
  77.     return viterbi(observations,
  78.                    states,
  79.                    start_probability,
  80.                    transition_probability,
  81.                    emission_probability)
  82.  
  83.  
  84. print example()

输出:


  1.            0       1       2
  2. Rainy:  0.06000 0.03840 0.01344
  3. Sunny:  0.24000 0.04320 0.00259
  4. (0.01344, ['Sunny', 'Rainy', 'Rainy'])

NLP应用

具体到分词系统,可以将天气当成“标签”,活动当成“字或词”。那么,几个NLP的问题就可以转化为:词性标注:给定一个词的序列(也就是句子),找出最可能的词性序列(标签是词性)。如ansj分词和ICTCLAS分词等。分词:给定一个字的序列,找出最可能的标签序列(断句符号:[词尾]或[非词尾]构成的序列)。结巴分词目前就是利用BMES标签来分词的,B(开头),M(中间),E(结尾),S(独立成词)命名实体识别:给定一个词的序列,找出最可能的标签序列(内外符号:[内]表示词属于命名实体,[外]表示不属于)。如ICTCLAS实现的人名识别、翻译人名识别、地名识别都是用同一个Tagger实现的。HMM是一个通用的方法,可以解决贴标签的一系列问题。

我们在实际应用时,将词性改造成“词性+医学实体TAG”的方式,这样可以带来两个好处,一是一般分词工具已经把词性标注集成到分词算法中去了,这样可以省去我们自己标注的工作;二是病历文本的叙述也是遵循中文语法的,所以在做句法分析时,可以将医学实体的通用词性提取出来,然后使用通用的句法分析模型分析处理,避免在去做针对医学领域句法分析语料标注、训练的工作。

构建语义层级

构建语义层级的过程类似人阅读的过程,以句子为单位从左向右读取原始文本,然后由主控系统分析器对句子进行句法+规则分析,得到具体的按语法结构组织的数据,最后在由控制器根据语义建立新的语义节点并加入到新的层级。整体过程如图3所示。

http://s5/mw690/006EAWy2zy7h590kCag14&690

这里需要注意问题有:

  • 在医学领域,很多表述按照医学惯例是不符合常用的语法的,这部分的结构需要单独处理,如:PT2bN0M0,这里面包含了T肿瘤大小及局部浸润范围、N淋巴结受累情况、M远处转移三种分期指标信息,类似这种领域特有表示,一般需要特殊处理,然后映射到语义层级通用结构,方便后续跟着语义层级的其它信息统一处理。

  • 一个语义节点的开始和结束的界限有时是非常模糊的,这需要我们预先做一个实体词的关联分析模型,以此来辅助判定后续的动作。另外,保证我们的系统针对层级误差有一定的健壮性是非常有必要的。


3. 抽取信息


构建完病历文本的知识结构之后,就是抽取数据进行实际应用的阶段了,主要的步骤包括:构建抽取模版、验证抽取模版、实际抽取数据。

构建抽取模版是确立结构化目标以及定义抽取规则的过程,简单的说就是定义一个Excel表头并制定每一个列字段的数据抽取规则的过程。表头的定义非常简单,例如:姓名、年龄、症状等。抽取规则的定义要相对复杂,需要对语义层级有一定的了解,因为语义层级是一个树状结构,所以对应的抽取规则也是一个层级结构,并且最后一个层级要能真正定位我们关注的数据。

图4是一个规则示例,语义层级的节点的TAG帮助我们定位到具体的语义层级节点,运算规则帮助我们进行匹配运算和对输出结果的逻辑转换,关键值标示了我们要抽取的目标。

http://s4/mw690/006EAWy2zy7h5966AAX53&690

图4单规则结构示意


 

图5表示我们一个简单的抽取模版的示意,姓名和CT是我们的表头内容,后续是我们每个字段的具体抽取规则链。规则链标示了寻找数据的层级结构,例如:抽取CT信息时要先找到现病史所在语义层级,然后在以此层级为基础找到CT语义层级,进而获取目标信息。

http://s13/mw690/006EAWy2zy7h598mwpK2c&690

图5抽取模版


需要注意的点:

  • 注意抽取模版和抽取规则的复用。

  • 要同时支持规则组的抽取,以满足的对医疗事件的抽取需求,例如我们的示例模版会将CT的全部信息给抽取出来,但是无法详细的抽取出CT的事件、地点、结果等信息,这时便需要通过规则组先定位到CT语义节点,然后以此节点出发,抽取此CT事件的详细信息。


病历智能处理引擎架构


 

病历智能处理引擎架构主要分为六个子系统,示意如图6:

http://s15/mw690/006EAWy2zy7h59cka5w3e&690

图6 病历智能处理引擎架构

  • 病历导入系统:负责对各个来源的各种格式的病历数据进行病历文本的提取。

  • 语义层级构建系统:病历文本导入系统后,通过此系统进行语义层级上下文的构建。此系统依赖自然语言处理系统、知识库词库维护系统。

  • 自然语言处理系统:负责对病历文本进行分词、句法分析、语义分析的工作,除了封装通用的处理算法,还定制了医学特有的处理算法。

  • 知识库词库维护系统:负责知识库、词库的维护工作,是自然语言处理系统的基础。

  • CRF(Case Report Format)规则定制系统:负责定制抽取规则。

  • 结构化抽取系统:以语义层级上下文为基础,通过CRF抽取规则进行结构化的抽取。

以上是六个子系统的职责与关系的介绍,每个子系统中又包含了多个职责模块,具体如图7:

http://s3/mw690/006EAWy2zy7h59k1FVU82&690

图7 病历智能处理引擎模块示意


模块划分主要分为四层,从上到下分别对应病历导入系统、语义层级处理系统、自然语言处理系统以及结构化抽取系统。

这里没有突出知识库词库维护系统以及CRF规则定制系统的具体模块,因为对整个系统来讲,我们仅需要以上两个系统的产出,即词库知识库以及CRF抽取规则。

病历导入的模块主要是为了兼容不同的格式,所以对主流的Word、Excel等做了支持策略,同时为后续的扩展提供了保障。

语义层级处理系统的核心是层级的动作分类器,构建整个语义上下文的骨架。

自然语言处理系统构建填充了语义层级上下文的内容,语义处理管道是主要的处理流,保证了自定义处理的扩展。

结构化抽取系统的核心是抽取器,这里包含了一个任务中心,在此可以对抽取结果进行下载以及查看正在抽取任务的进度。

病历智能处理引擎在临床科研自动化中的应用


科研对很多医生而言是一个刚需,评职称需要论文,写论文需要做科研。临床科研的主要方式是选定一个课题,然后选择病历,对病历进行结构处理,录入结构化病历记录表中,再对这些数据进行统计分析,以分析结果得到的统计图表为基本架构,撰写科研论文。

 

在这个过程中,最耗费时间和精力的,最容易出错的,就是病历的结构化处理,而这正是前文阐述的病历智能处理引擎的强项,一个使用病历智能处理引擎的的科研过程如下图。

http://s9/mw690/006EAWy2zy7h59wQ82ca8&690
新屿作为一家医疗大数据服务公司,为医疗科研过程开发了数款产品,构成了一个医疗科研数据自动化处理平台。整体架构如图9所示。

http://s8/mw690/006EAWy2zy7h59yzeGr27&690

病历文本通过病历智能处理引擎进行自然语言处理后,处理结果写入科研宝,并使用易统计进行分析,产生分析报表。

病历智能处理引擎在智能辅助诊疗中的应用


 

病历,特别是专家写的病历,本身就是一笔巨大的知识财富,将这些知识进行处理、分析、统计、挖掘,可以构成一个病历知识库,供更多的人共享,即构成一个智能辅助诊疗系统。整体架构如图10所示。

http://s1/mw690/006EAWy2zy7h59AFIpa60&690

图10 智能辅助诊疗系统架构


病历知识库、循证医学知识库、科研文献知识库、用药知识库共同构成一个辅助诊疗知识库,通过知识匹配搜索引擎对外提供服务。患者或者医生录入病史、检查结果等信息,系统匹配初步诊断结果,搜索诊疗计划,产生多个辅助诊疗建议,供患者和医生进行参考。

目前新屿辅助诊疗系统尚在原型验证阶段,受医疗管理、医学伦理的限制,大规模商业应用目前看来还为时尚早,但是利用大数据技术支持下的智能医疗,实现多专家会诊定制最优诊疗方案是一件值得所有人期待的事。

总结


使用传统IT技术,通过信息化手段提高医疗效率的工作已经做了几十年了,医疗机构信息化程度也越来越高,对效能提升、信息快速流通起到了巨大的作用,但是,仅仅通过信息化手段进一步提高效率的空间越来越小,边际效益越来越少。通过引入更多人工智能的因素,辅助医生更快、更准确、更全面地开展医疗与科研工作,将会是下一阶段IT技术的发展趋势,但是新的道路总是曲折的,期待我们共同去探索。

 

作者简介:

吴大帅,新屿算法工程师,曾供职于宅米网、新达达,从事系统架构设计、算法设计等工作。 

李智慧,《大型网站技术架构:核心原理与案例分析》作者,从事大型网站、分布式系统、大数据方面的研发工作。 

责编:钱曙光(qianshg@csdn.net) 

声明:本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请点击「阅读原文」订阅《程序员》。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有