由米格尔·德尔阿瓜(Miguel Del-Agua)和杰里米·扬卡斯里(Jeremy Jancsary)共同撰写,米格尔·德尔阿瓜是 Nuance 的首席研究科学家,杰里米·扬卡斯里是 Nuance 的高级首席研究科学家

引言

完整准确的临床文档是跟踪患者护理的重要工具。它允许治疗计划在护理团队之间共享,以帮助护理连续性,并确保报销过程的透明和有效。

医生负责记录患者护理情况。传统的临床记录方法导致了患者与医生之间的体验不佳,与患者互动时间减少,以及工作与生活平衡的下降。医生的大量时间都花在电脑前处理行政任务。因此,患者对整体体验的满意度降低,经过多年医学学习准备的专业医生无法在其执照范围内进行最佳实践,并且感到疲惫不堪。医生每小时直接与患者面对面交流的时间,会导致在诊所当天额外花费近两小时的时间在电子健康记录和桌面工作上。在办公时间之外,医生每晚还要额外花费 1 到 2 小时的个人时间进行额外的电脑和其他文书工作。

医师倦怠是导致医疗错误增加、医疗事故诉讼、人员流动和医疗服务可及性下降的主要原因之一。倦怠导致医疗成本增加,整体患者满意度下降。倦怠每年给美国造成 46 亿美元的经济损失。

我们可以做些什么来恢复医疗保健中的信任、喜悦和人性?行政工作中很大一部分是录入患者数据到电子健康记录(EHR)和创建临床文档。临床文档是从 EHR 中已有的信息以及患者与医生的交流中创建的。

本文将展示 Nuance Dragon Ambient eXperience (DAX)这一人工智能驱动的、语音激活的环境临床智能解决方案,如何自动、准确、高效地在护理点记录患者接触,以及支持它的技术。

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

自然语言处理

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

这些高级 NLP 技术正在应用于医疗保健领域。在典型的患者-医生互动中,医生通过提问和回答构建一个按时间顺序描述患者病情或症状发展的对话。医生检查患者并做出临床决策,以确定诊断和治疗方案。这次对话和电子健康记录(EHR)中的数据为医生生成临床文档,即医疗报告提供了所需信息。

两个主要自然语言处理组件在自动化创建临床文档中发挥着作用。第一个组件,自动语音识别(ASR),用于将语音转换为文本。它将遭遇的音频录音生成对话转录(参见图 2)。第二个组件,自动文本摘要,有助于从大量文本文档中生成摘要。该组件负责理解并捕捉转录对话中的细微差别和最基本的内容,以叙事形式、结构化形式或两者的结合形式生成最终报告(参见图 3)。

我们将重点关注这个第二个组件,即自动文本摘要,这是一个具有许多挑战的困难任务:

  • 其性能与多个说话人的 ASR 质量(噪声输入)相关。
  • 输入具有对话性质,包含非专业术语。
  • 保护性健康信息(PHI)法规限制了医疗数据的访问。
  • 一个输出句子的信息可能分布在多个对话回合中。
  • 输入和输出之间没有明确的句子对齐。
  • 不同的医学专业、就诊类型和电子健康记录系统构成了一个广泛且复杂的输出空间。
  • 医生在开展诊疗过程中有不同的风格,对病历报告也有自己的偏好;没有统一标准。
  • 标准的摘要度量指标可能与人类对质量的判断不同。

图 2:患者-医生对话记录

图 3:AI 生成的医疗报告摘录。HPI 代表现病史。

使用 PyTorch 和 Fairseq 进行文本摘要

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

文本摘要构成了 NLP 中的一个重大挑战。我们需要能够生成文档简短版本的同时保留关键点并避免无关内容的模型。这些挑战可以通过不同的方法来解决。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 个位置标记。

如果我们有一个类似这样的输入

src = "Hello, I'm The Dogtor"

可能会发生我们的模型在词汇中没有训练过“Dogtor”这个词的情况。因此,当我们将这个序列输入到模型中时,它应该被转换为:

src = "Hello, I'm The <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 的倍数,以更好地使用张量核心并减少训练时间。

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”中,“”已被替换为输入序列中的原始单词。

模型部署

PyTorch 在建模方面提供了极大的灵活性,并拥有丰富的周边生态系统。然而,尽管最近有几篇文章建议 PyTorch 在研究和学术界的使用可能接近超越 TensorFlow,但总体上 TensorFlow 似乎仍然是部署到生产环境的首选平台。在 2021 年,这种情况是否仍然如此?希望在生产环境中部署 PyTorch 模型的团队有几个选择。

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

模型作为计算图

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

最近,上述限制导致了由 PyTorch 和 TensorFlow 2.0 引领的第二次革命。计算图不再显式定义。相反,它将在 Python 代码执行张量参数上的操作时隐式填充。推动这一发展的关键技术是自动微分。在执行前向传播步骤的同时隐式构建计算图,将跟踪所有必要的数据,以便稍后计算关于模型参数的梯度。这为训练模型提供了极大的灵活性,但也提出了一个重要问题。如果模型内部发生的计算仅通过 Python 代码的执行步骤隐式定义,那么我们想要保存什么作为模型?答案——至少最初——是包含所有依赖关系的 Python 代码及其估计参数。出于实际原因,这是不可取的。例如,存在风险,即负责模型部署的团队没有完全复制训练期间使用的 Python 代码依赖项,从而导致行为微妙地偏离。 该解决方案通常包括结合两种技术,即脚本和跟踪,即在您的 Python 代码中添加额外的注释并在示例输入数据上执行您的代码,使 PyTorch 能够定义并保存应在后续推理过程中执行的新、未见过的数据的图。这要求创建模型代码的人有一定的纪律性(这可能会削弱一些原始的即时执行灵活性),但最终会得到一个包含在 TorchScript 格式的自包含模型包。TensorFlow 2 中的解决方案与此非常相似。

服务器报告生成模型

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

一个日益成熟的生态系统

这一切都是玫瑰色吗?不,这比我们预期的道路要坎坷。我们在直接运行 PyTorch 代码时遇到了似乎是由 PyTorch 使用的 MKL 库引起的内存泄漏。我们在尝试从多个线程加载多个模型时遇到了死锁。我们在将模型导出到 ONNX 和 TorchScript 格式时遇到了困难。在具有多个 GPU 的硬件上,模型无法直接使用,它们总是访问它们被导出的特定 GPU 设备。在运行 TorchScript 模型时,我们在 Triton 推理服务器上遇到了过度的内存使用,我们发现这是因为在正向传递过程中意外启用了自动微分。然而,生态系统仍在不断改进,有一个友好而充满活力的开源社区愿意与我们合作,共同解决这些问题。

从这里去哪里?对于那些需要直接服务 PyTorch 代码,而不需要通过导出自包含模型这一额外步骤的人来说,值得注意的是,TorchServe 项目现在提供了一种将代码与参数检查点捆绑在一起的方法,从而大大降低了代码和参数运行分离的风险。然而,对我们来说,将模型导出为 TorchScript 已被证明是有益的。它为建模团队和部署团队之间提供了一个清晰的接口,并且 TorchScript 通过其即时编译引擎进一步降低了在 GPU 上服务模型时的延迟。

大规模扩展和未来

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

结论

学术界长期以来都认识到我们是在“站在巨人的肩膀上”。随着人工智能从一门科学学科成熟为技术,最初推动其科学基础的协作精神也延续到了软件工程领域。开源爱好者与全球科技公司携手共建开放软件生态系统,允许从新的角度解决现代社会面临的一些最紧迫的挑战。在本文中,我们探讨了 Nuance 的 Dragon Ambient eXperience,这是一款由人工智能驱动、具备语音功能的解决方案,可自动记录患者护理情况,减轻医疗提供者的行政负担。Nuance DAX 改善了患者与提供者之间的体验,减少了医生的职业倦怠,并改善了财务结果。它为医疗保健的提供带来了信任、喜悦和人性。Fairseq 和 PyTorch 已被证明是推动这项人工智能技术的强大平台,而 Nuance 也在此领域贡献了一些创新。欲了解更多信息,请参阅我们最近的 ACL 出版物和 Nuance 的“未来展望”博客。