跳转到主要内容
博客

环境临床智能:使用 PyTorch 生成医疗报告

简介

完整准确的临床文档是跟踪患者护理的重要工具。它有助于护理团队之间共享治疗计划,从而促进护理的连续性,并确保透明有效的报销流程。

医生负责记录患者护理。传统的临床文档方法导致患者与提供者体验不佳,与患者互动时间减少,以及工作与生活平衡下降。医生的大量时间都花在电脑前处理行政任务。因此,患者对整体体验的满意度降低,而多年学习医学的医生无法充分发挥其执业能力,并出现倦怠。医生每小时直接与患者进行临床面对面交流,导致在诊疗日内额外花费近两小时用于 EHR 和案头工作。在非办公时间,医生每晚还会额外花费 1 到 2 小时个人时间进行额外的电脑和其他文书工作。

医生倦怠是导致医疗错误、医疗事故诉讼、人员流失和就医机会减少的主要原因之一。倦怠导致医疗成本增加和整体患者满意度下降。倦怠每年给美国造成 46 亿美元的损失。

我们能做些什么来恢复医疗服务的信任、喜悦和人性?很大一部分行政工作包括将患者数据输入电子健康记录 (EHR) 并创建临床文档。临床文档是根据 EHR 中的信息以及患者与提供者的交流对话创建的。

本文将展示 Nuance Dragon Ambient eXperience (DAX)(一种由人工智能驱动、语音控制的环境临床智能解决方案)如何准确有效地在护理点自动记录患者就诊情况,以及实现它的技术。

Nuance DAX 提升了护理质量和患者体验,提高了提供者的效率和满意度,并改善了财务结果。它可用于所有门诊专科的办公室和远程医疗环境,包括初级保健和急症护理。

自然语言处理

自然语言处理 (NLP) 是人工智能 (AI) 中最具挑战性的领域之一。它包含一组算法,允许计算机理解或生成人类使用的语言。这些算法可以处理和分析来自不同来源(无论是声音还是文本)的大量自然语言数据,以构建能够像人类一样理解、分类甚至生成自然语言的模型。与其他 AI 领域一样,NLP 得益于深度学习 (DL) 的出现而取得了显著进展,这使得模型在某些任务上能够获得与人类相当的结果。

这些先进的自然语言处理技术正在应用于医疗保健领域。在典型的患者与提供者交流中,会进行一次对话,医生通过提问和回答,构建患者当前疾病或症状发展的按时间顺序的描述。医生检查患者并做出临床决策以确定诊断和治疗计划。这种对话以及电子健康记录中的数据为医生生成临床文档(称为医疗报告)提供了所需的信息。

两个主要的 NLP 组件在自动化临床文档创建中发挥作用。第一个组件,自动语音识别 (ASR),用于将语音转换为文本。它接收就诊的录音并生成对话转录(参见图 2)。第二个组件,自动文本摘要,有助于从大型文本文档中生成摘要。该组件负责理解和捕捉转录对话中的细微差别和最重要的方面,并将其转化为叙述形式的最终报告(参见图 3)、结构化形式或两者的组合。

我们将重点关注第二个组件,即自动文本摘要,这是一项困难重重且充满挑战的任务

  • 其性能与来自多个说话者的 ASR 质量(嘈杂输入)相关联。
  • 输入本质上是对话性的,包含非专业术语。
  • 受保护健康信息 (PHI) 法规限制了医疗数据访问。
  • 一个输出句子的信息可能分散在多个对话轮次中。
  • 输入和输出之间没有明确的句子对齐。
  • 各种医学专业、就诊类型和电子病历系统构成了广泛而复杂的输出空间。
  • 医生进行就诊的风格各不相同,对医疗报告也有自己的偏好;没有标准。
  • 标准摘要指标可能与人类对质量的判断有所不同。

图 2:患者-医生对话的转录本

图3:AI生成的医疗报告节选。HPI指当前疾病史。

使用 PyTorch 和 Fairseq 进行文本摘要

PyTorch是一个由 Facebook 开发的开源机器学习框架,它帮助研究人员构建深度学习模型原型。Fairseq工具包构建在 PyTorch 之上,专注于序列生成任务,例如神经机器翻译 (NMT) 或文本摘要。Fairseq 拥有一个活跃的社区,不断提供最先进模型的参考实现。它包含许多内置组件(模型架构、模块、损失函数和优化器),并且易于通过插件进行扩展。

文本摘要是自然语言处理中的一个重大挑战。我们需要能够生成文档短版本同时保留关键点并避免无信息内容的模型。这些挑战可以通过不同的方法解决。1). 抽象式文本摘要,旨在训练能够生成叙述形式摘要的模型。2). 提取式方法,模型经过训练以从输入文本中选择最重要的部分。3). 两者的结合,从输入中选择最重要的部分,然后以抽象式方式进行摘要。因此,摘要可以通过单个端到端网络或作为提取和抽象组件的管道来实现。为此,Fairseq 提供了所有必要的工具以确保我们成功。它具有端到端模型,例如经典的 Transformer,不同类型的语言模型和预训练版本,使研究人员能够专注于最重要的事情——构建最先进的模型以生成有价值的报告。

然而,我们不仅仅是总结转录的对话;我们正在生成高质量的医疗报告,其中包含许多考虑因素。

  • 医疗报告的每个部分在内容、结构、流畅性等方面都不同。
  • 对话中提及的所有医学事实都应出现在报告中,例如,特定的治疗或剂量。
  • 在医疗保健领域,词汇量巨大,模型需要处理医学术语。
  • 患者-医生对话通常比最终报告长得多。

所有这些挑战都要求我们的研究人员进行一系列广泛的实验。得益于 PyTorch 和 Fairseq 的灵活性,他们的生产力大大提高。此外,该生态系统提供了一条从构思、实施、实验到最终投入生产的便捷路径。使用多个 GPU 或 CPU 就像向工具提供额外参数一样简单,并且由于紧密的 Python 集成,PyTorch 代码可以轻松调试。

在我们为开源社区持续贡献的努力中,Nuance 开发了一些功能并将其推送到 Fairseq GitHub 仓库。这些功能旨在克服一些上述挑战,例如,促进将输入中特别是罕见或未见过的单词复制到摘要中,通过改进 Tensor Core 利用率来加速训练,以及确保不同 Transformer 配置的 TorchScript 兼容性。接下来,我们将展示一个如何使用指针生成器机制 (Transformer-PG) 训练 Transformer 模型的示例,该模型可以从输入中复制单词。

如何构建带有指针生成器机制的 Transformer 模型

在此分步指南中,假定用户已安装 PyTorch 和 Fairseq。

1. 创建词汇表并用源位置标记扩展它:

这些标记将允许模型指向输入序列中的任何单词。

vocab_size=<vocab_size>
position_markers=512
export LC_ALL=C
cat train.src train.tgt |
  tr -s '[:space:]' '\n' |
  sort |
  uniq -c |
  sort -k1,1bnr -k2 |
  head -n "$((vocab_size - 4))" |
  awk '{ print $2 " " $1 }' > dict.pg.txt
python3 -c "[print('<unk-{}> 0'.format(n)) for n in range($position_markers)]" >> dict.pg.txt

这将创建一个名为“dict.pg.txt”的文件,其中包含词汇表大小中最常见的单词,后跟 512 个位置标记,名称从“<unk-0>”到“<unk-511>”。

如果我们的输入是:

src = "Hello, I'm The Dogtor"

我们的模型可能在没有“Dogtor”这个词的词汇表中进行过训练。因此,当我们把这个序列输入到模型中时,它应该被转换为

src = "Hello, I'm The <unk-3>"

现在,“<unk-3>”是我们的词汇表的一部分,可以被模型预测(这就是指针-生成器的作用)。在这种情况下,我们只需要对输出进行后处理,将“<unk-3>”替换为输入位置 3 的单词。

2. 预处理文本数据,用其位置标记替换未知词:

我们可以使用来自https://github.com/pytorch/fairseq/tree/master/examples/pointer_generator的脚本。

# Considering we have our data in:
# train_src = /path/to/train.src
# train_tgt = /path/to/train.tgt
# valid_src = /path/to/valid.src
# valid_tgt = /path/to/valid.tgt
./preprocess.py --source /path/to/train.src \
                --target /path/to/train.tgt \
                --vocab <(cut -d' ' -f1 dict.pg.txt) \
                --source-out /path/to/train.pg.src \
                --target-out /path/to/train.pg.tgt

./preprocess.py --source /path/to/valid.src \
                --target /path/to/valid.tgt \
                --vocab <(cut -d' ' -f1 dict.pg.txt) \
                --source-out /path/to/valid.pg.src \
                --target-out /path/to/valid.pg.tgt

./preprocess.py --source /path/to/test.src \
                --vocab <(cut -d' ' -f1 dict.pg.txt) \
                --source-out /path/to/test.pg.src

3. 现在让我们将数据二值化,以便更快地处理:

fairseq-preprocess --task "translation" \
                   --source-lang "pg.src" \
                   --target-lang "pg.tgt" \
                   --trainpref /path/to/train \
                   --validpref /path/to/valid \
                   --srcdict dict.pg.txt \
                   --cpu \
                   --joined-dictionary \
                   --destdir <data_dir>

您可能会注意到任务类型是“翻译”。这是因为没有可用的“摘要”任务;我们可以将其理解为一种 NMT 任务,其中输入和输出语言共享,并且输出(摘要)比输入短。

4. 现在我们可以训练模型了:

fairseq-train <data_dir> \
              --save-dir <model_dir> \
              --task "translation" \
              --source-lang "src" \
              --target-lang "tgt" \
              --arch "transformer_pointer_generator" \
              --max-source-positions 512 \
              --max-target-positions 128 \
              --truncate-source \
              --max-tokens 2048 \
              --required-batch-size-multiple 1 \
              --required-seq-len-multiple 8 \
              --share-all-embeddings \
              --dropout 0.1 \
              --criterion "cross_entropy" \
              --optimizer adam \
              --adam-betas '(0.9, 0.98)' \
              --adam-eps 1e-9 \
              --update-freq 4 \
              --lr 0.004 \
              # Pointer Generator
              --alignment-layer -1 \
              --alignment-heads 1 \
              --source-position-markers 512

此配置利用了 Nuance 回馈给 Fairseq 的功能

  • 具有指针生成器机制的Transformer,便于从输入复制单词。
  • 序列长度填充到 8 的倍数,以更好地利用 Tensor Core 并减少训练时间。

5. 现在让我们看看如何使用我们的新医疗报告生成系统生成摘要:

import torch
from examples.pointer_generator.pointer_generator_src.transformer_pg import TransformerPointerGeneratorModel

# Patient-Doctor conversation
input = "[doctor] Lisa Simpson, thirty six year old female, presents to the clinic today because " \
        "she has severe right wrist pain"

# Load the model
model = TransformerPointerGeneratorModel.from_pretrained(data_name_or_path=<data_dir>,
                                                         model_name_or_path=<model_dir>,
                                                         checkpoint_file="checkpoint_best.pt")

result = model.translate([input], beam=2)

print(result[0])
Ms. <unk-2> is a 36-year-old female who presents to the clinic today for evaluation of her right wrist.

6. 或者,我们可以使用 fairseq-interactive 和后处理工具,将位置未知标记替换为输入中的单词:

fairseq-interactive <data_dir> \
              --batch-size <batch_size> \
              --task translation \
              --source-lang src \
              --target-lang tgt \
              --path <model_dir>/checkpoint_last.pt \
              --input /path/to/test.pg.src \
              --buffer-size 20 \
              --max-len-a 0 \
              --max-len-b 128 \
              --beam 2 \
              --skip-invalid-size-inputs-valid-test | tee generate.out

grep "^H-" generate.out | cut -f 3- > generate.hyp

./postprocess.py \
	--source <(awk 'NF<512' /path/to/test.pg.src) \
	--target generate.hyp \
	--target-out generate.hyp.processed

现在我们得到了“generate.hyp.processed”中最终的报告集,其中“<unk-N>”已替换为输入序列中的原始单词。

模型部署

PyTorch 在建模方面提供了极大的灵活性和丰富的生态系统。然而,尽管最近的一些文章表明 PyTorch 在研究和学术领域的应用可能接近超越 TensorFlow,但总体上似乎认为 TensorFlow 是部署到生产环境的首选平台。2021 年仍然如此吗?希望在生产环境中部署 PyTorch 模型的团队有几种选择。

在描述我们的旅程之前,让我们先简要绕道,定义一下“模型”这个术语。

作为计算图的模型

几年前,机器学习工具包仍然普遍只支持特定类别的模型,这些模型结构相当固定和僵硬,只有很少的自由度(如支持向量机的核或神经网络的隐藏层数量)。受 Theano 基础工作的启发,微软的 CNTK 或谷歌的 TensorFlow 等工具包是首批推广更灵活的模型视图的工具包,将模型视为具有关联参数的计算图,这些参数可以从数据中估计。这种视图模糊了流行模型类型(如深度神经网络或支持向量机)之间的界限,因为很容易将每种模型的特征混合到您的图类型中。尽管如此,这样的图必须在估计其参数之前预先定义,而且它相当静态。这使得将模型保存为自包含的包变得容易,例如 TensorFlow SavedModel(这样的包只包含图的结构以及估计参数的具体值)。然而,调试这样的模型可能很困难,因为构建图的 Python 代码中的语句与执行它的行在逻辑上是分离的。研究人员也渴望更简单地表达动态行为,例如模型正向传播的计算步骤有条件地依赖于其输入数据(或其先前的输出)。

最近,上述限制导致了由 PyTorch 和 TensorFlow 2 引领的第二次革命。计算图不再明确定义。相反,当 Python 代码在张量参数上执行操作时,它将隐式填充。支持这一发展的一项重要技术是自动微分。当计算图在执行正向传播步骤时隐式构建时,所有必要的数据都将被跟踪,以便稍后计算相对于模型参数的梯度。这为模型训练带来了极大的灵活性,但也提出了一个重要问题。如果模型内部发生的计算仅仅通过我们 Python 代码在执行具体数据时的步骤隐式定义,那么我们想保存什么作为模型呢?答案——至少最初是——带有所有依赖项的 Python 代码以及估计的参数。出于实际原因,这是不可取的。例如,负责模型部署的团队存在无法精确复制训练期间使用的 Python 代码依赖项的风险,从而导致微妙的差异行为。解决方案通常包括结合两种技术,即脚本和跟踪,也就是说,在你的 Python 代码中添加额外的注释,并在示例输入数据上执行你的代码,从而允许 PyTorch 定义和保存应该在后续对新、未见过的数据进行推理时执行的图。这需要创建模型代码的人员遵守一些规范(可以说会损害即时执行的一些原始灵活性),但它会生成一个 TorchScript 格式的自包含模型包。TensorFlow 2 中的解决方案也惊人地相似。

部署我们的报告生成模型

我们部署报告生成模型的历程反映了上述讨论。我们最初通过将模型代码及其依赖项以及参数检查点部署到自定义 Docker 镜像中,并暴露 gRPC 服务接口来部署我们的模型。然而,我们很快注意到,复制建模团队在估计参数时使用的确切代码和环境变得容易出错。此外,这种方法阻止了我们利用高性能模型服务框架,如 NVIDIA 的 Triton,它用 C++ 编写,需要自包含的模型,无需 Python 解释器即可使用。在此阶段,我们面临着将 PyTorch 模型导出为 ONNX 或 TorchScript 格式的选择。ONNX 是一种用于表示机器学习模型的开放规范,其应用日益广泛。它由微软开发的高性能运行时 (ONNX Runtime) 提供支持。虽然我们能够使用 ONNX Runtime 加速我们的基于 TensorFlow BERT 的模型性能,但当时我们的一个 PyTorch 模型需要一些 ONNX 尚未支持的操作符。与其使用自定义操作符实现这些功能,我们决定暂时研究 TorchScript。

一个日益成熟的生态系统

一切都顺利吗?不,这是一段比我们预期更艰难的旅程。我们在直接部署 PyTorch 代码时,遇到了 PyTorch 使用的 MKL 库中似乎存在的内存泄漏。我们在尝试从多个线程加载多个模型时遇到了死锁。我们很难将模型导出为 ONNX 和 TorchScript 格式。模型在多 GPU 硬件上无法开箱即用,它们总是访问导出时所在的特定 GPU 设备。我们在 Triton 推理服务器中部署 TorchScript 模型时遇到了过多的内存使用,后来我们发现这是由于在正向传播过程中意外启用了自动微分。然而,生态系统仍在不断改进,并且有一个乐于助人且充满活力的开源社区,渴望与我们合作解决此类问题。

接下来该怎么做?对于那些需要直接部署 PyTorch 代码的灵活性,而无需额外导出自包含模型的人来说,值得指出的是,TorchServe 项目现在提供了一种将代码与参数检查点捆绑到单个可服务存档中的方式,大大降低了代码和参数分离的风险。然而,对我们来说,将模型导出到 TorchScript 已被证明是有益的。它为建模和部署团队提供了清晰的接口,并且 TorchScript 通过其即时编译引擎进一步降低了在 GPU 上部署模型时的延迟。

大规模扩展与未来

最后,高效部署到云端不仅仅是高效计算单个模型实例的响应。在管理、版本控制和更新模型方面需要灵活性。必须通过负载均衡、横向扩展和纵向扩展等技术实现高层次的可伸缩性。如果涉及许多模型,快速缩放至零很快就会成为一个问题,因为为不响应任何请求的模型付费是不可接受的。在 Triton 这样的低级推理服务器之上提供此类额外功能是编排框架的工作。在获得 KubeFlow 的一些初步经验后,为此,我们决定将注意力转向 Azure ML,它提供了类似的功能,但与 Azure 平台深度集成,而我们已经在很大程度上依赖该平台来构建我们的技术栈。我们旅程的这一部分才刚刚开始。

结论

学术界早已认识到我们“站在巨人的肩膀上”。随着人工智能从一门科学学科走向技术,最初推动其科学基础的协作精神也延续到了软件工程领域。全球各地的开源爱好者与科技公司携手,构建开放的软件生态系统,为解决现代社会一些最紧迫的挑战提供了新的视角。在本文中,我们探讨了 Nuance 的Dragon Ambient eXperience,这是一种由人工智能驱动、语音支持的解决方案,可自动记录患者护理,从而减轻医疗服务提供者的行政负担。Nuance DAX 改善了患者与提供者的体验,减少了医生倦怠,并改善了财务状况。它将信任、快乐和人道带回了医疗服务的交付中。Fairseq 和 PyTorch 已被证明是支持这项人工智能技术的绝佳平台,反过来,Nuance 也回馈了其在该领域的一些创新。如需进一步阅读,我们邀请您查阅我们最近的ACL 出版物和 Nuance 的“What’s Next”博客。