RETAIN 是一种用于医疗保健应用的可解释的预测模型。根据患者记录,它可以做出预测,同时解释每次就诊时的每个医疗代码(诊断代码、药物代码或程序代码)如何对预测做出贡献。由于神经注意机制的使用,这种解释是可能的。
使用 RETAIN,您可以计算不同就诊时每个医疗代码(诊断、药物或手术代码)对最终得分的积极/消极影响。在本例中,我们正在预测给定患者是否会被诊断为心力衰竭 (HF)。可以看到与HF高度相关的代码做出了积极的贡献。 RETAIN 还学会了更加关注新信息而不是旧信息。您可以看到心律失常 (CD) 的贡献更大,因为它发生在最近的访问中。
RETAIN 实现了以下论文中介绍的算法:
RETAIN: An Interpretable Predictive Model for Healthcare using Reverse Time Attention Mechanism
Edward Choi, Mohammad Taha Bahadori, Joshua A. Kulas, Andy Schuetz, Walter F. Stewart, Jimeng Sun,
NIPS 2016, pp.3504-3512
RETAIN 论文将模型制定为能够在每个时间步进行预测(例如,尝试预测患者每次就诊时将收到什么诊断),并处理序列分类(例如,给定患者记录,他会在以下时间被诊断为心力衰竭)未来?)作为一种特殊情况,因为序列分类仅在最后一个时间步进行预测。
然而,该代码是为了执行序列分类任务而实现的。例如,您可以使用此代码来预测给定患者是否是心力衰竭患者。或者您可以预测该患者将来是否会再次入院。更通用的 RETAIN 版本将在未来发布。
第 1 步:安装
安装Python、Theano。我们使用Python 2.7,Theano 0.8。按照此处的建议,Theano 可以轻松安装在 Ubuntu 中
如果您打算使用 GPU 计算,请安装 CUDA
下载/克隆 RETAIN 代码
第 2 步:使用 MIMIC-III 测试 RETAIN 的快速方法
此步骤描述如何使用 MIMIC-III 以最少的步骤训练 RETAIN,以使用患者的就诊记录预测患者的死亡率。
您首先需要请求访问 MIMIC-III,这是从 11 年来从 ICU 患者收集的公开电子健康记录。
您可以使用“process_mimic.py”来处理 MIMIC-III 数据集并为 RETAIN 生成合适的训练数据集。将脚本放置到 MIMIC-III CSV 文件所在的同一位置,然后运行该脚本。执行命令为python process_mimic.py ADMISSIONS.csv DIAGNOSES_ICD.csv PATIENTS.csv <output file>
。
使用 process_mimic.py 生成的“.seqs”和“.morts”文件运行 RETAIN。 “.seqs”文件包含每个患者的就诊顺序。每次就诊都包含多个诊断代码。不过,我们建议使用“.3digitICD9.seqs”文件,因为结果将更容易解释。 (或者您可以使用 ICD9 的单级临床分类软件将代码数量减少到数百个,这将进一步提高性能)“.morts”文件包含每个患者的死亡标签序列。命令为python retain.py <3digitICD9.seqs file> 942 <morts file> <output path> --simple_load --n_epochs 100 --keep_prob_context 0.8 --keep_prob_emb 0.5
。 942
是数据集中使用的完整 3 位 ICD9 代码的数量。
要测试模型的解释,请参考步骤6。我个人发现围产期黄疸(ICD9 774)与死亡率具有很高的相关性。
使用上述命令,模型的 AUC 达到 0.8 以上,但解释不是非常清楚。你可以调整超参数,但我怀疑事情会显着改善。毕竟,只有 7,500 名患者曾多次到医院就诊,而且大多数人只就诊过两次。
第 3 步:如何准备自己的数据集
RETAIN的训练数据集需要是一个Python cPickled list of list of list。最外面的列表对应于患者,中间的列表对应于每个患者进行的就诊顺序,最里面的列表对应于每次就诊中发生的医疗代码(例如诊断代码、药物代码、程序代码等)。首先,医疗代码需要转换为整数。那么单次访问就可以看作是一个整数列表。然后可以将患者视为就诊列表。例如,[5,8,15] 表示患者在某次就诊时被分配了代码 5、8 和 15。如果一个病人两次就诊[1,2,3]和[4,5,6,7],可以将其转换为列表[[1,2,3],[4,5,6,7] ]]。多个患者可以表示为 [[[1,2,3], [4,5,6,7]], [[2,4], [8,3,1], [3]]],这意味着有两名患者,第一个患者就诊两次,第二个患者就诊三次。这个列表列表需要使用 cPickle 进行腌制。我们将此文件称为“访问文件”。
运行 RETAIN 需要唯一医疗代码的总数。例如,如果数据集使用 14,000 个诊断代码和 11,000 个程序代码,则总数为 25,000。
标签数据集(我们称之为“标签文件”)需要是一个 Python cPickled 列表。每个元素对应于每个患者的真实标签。例如,1 可以是病例患者,0 可以是对照患者。如果有两个患者,而只有第一个患者是病例,那么我们应该有[1,0]。
“访问文件”和“标签文件”分别需要有3组:训练集、验证集和测试集。文件扩展名必须分别为“.train”、“.valid”和“.test”。
例如,如果要使用名为“my_visit_sequences”的文件作为“访问文件”,则 RETAIN 将尝试加载“my_visit_sequences.train”、“my_visit_sequences.valid”和“my_visit_sequences.test”。
对于“标签文件”也是如此
您可以使用有关访问的时间信息作为附加信息来源。我们称之为“时间文件”。请注意,时间信息可以是任何内容:连续访问之间的持续时间、自第一次访问以来的累计天数等。“时间文件”需要准备为 Python cPickled 列表。最外面的列表对应于患者,最里面的是每次就诊的时间信息。例如,给定一个“访问文件” [[[1,2,3], [4,5,6,7]], [[2,4], [8,3,1], [3]]] ,如果我们使用连续访问之间的持续时间,则其相应的“时间文件”可能类似于 [[0, 15], [0, 45, 23]]。 (当然这些数字是假的,我已将第一次访问的持续时间设置为零。) 使用--time_file <path to time file>
选项来使用“时间文件” 请记住“.train”、“. valid”、“.test”规则也适用于“时间文件”。
附加:使用您自己的医疗代码表示
RETAIN 在训练时内部学习医疗代码的向量表示。当然,这些向量是用随机值初始化的。
不过,如果您有医疗代码表示形式,您也可以使用自己的医疗代码表示形式。 (可以使用 Skip-gram 之类的算法来训练它们。有关更多详细信息,请参阅 Med2Vec 或此。)如果您想提供医疗代码表示形式,它必须是 N 行列表(基本上是矩阵)的列表,并且M 列,其中 N 是“访问文件”中唯一代码的数量,M 是代码表示的大小。使用--embed_file <path to embedding file>
指定代码表示文件的路径。此外,即使您使用自己的医疗代码表示,您也可以在训练 RETAIN 时重新训练(也称为微调)它们。使用--embed_finetune
选项来执行此操作。如果您不提供自己的医疗代码表示形式,RETAIN 将使用随机初始化的表示形式,这显然需要这一微调过程。由于默认是使用微调,因此您无需担心这一点。
第 4 步:运行 RETAIN
运行 RETAIN 所需的最小输入是“访问文件”、“访问文件”中唯一医疗代码的数量、“标签文件”和输出路径。输出路径是保存学习到的权重和日志的位置。
python retain.py <visit file> <# codes in the visit file> <label file> <output path>
指定--verbose
选项将在每 10 个小批量后打印训练过程。
您可以指定嵌入 W_emb 的大小、生成 alpha 的 GRU 隐藏层的大小以及生成 beta 的 GRU 隐藏层的大小。相应的命令是--embed_size <integer>
、 --alpha_hidden_dim_size <integer>
和--beta_hidden_dim_size <integer>
。例如--alpha_hidden_dim_size 128
将告诉 RETAIN 使用具有 128 维隐藏层的 GRU 来生成 alpha。
Dropout 应用于两个位置:1)输入嵌入,2)上下文向量 c_i。可以使用--keep_prob_embed {0.0, 1.0}
和--keep_prob_context {0.0, 1.0}
调整各自的丢失率。 Dropout 值会影响性能,因此建议根据您的数据调整它们。
L2 正则化可应用于 W_emb、w_alpha、W_beta 和 w_output。
可以指定其他选项,例如批量大小的大小、epoch 的数量等。详细信息可以通过python retain.py --help
访问
我个人的建议:对所有四个权重使用轻度正则化(0.0001 ~ 0.001),并仅对上下文向量使用适度的 dropout。但这完全取决于您的数据,因此您应该始终自行调整超参数。
第 5 步:获取结果
RETAIN 在每个 epoch 后检查验证集的 AUC,如果它高于所有先前的值,它将保存当前模型。模型文件由numpy.savez_compressed生成。
第 6 步:测试您的模型
使用文件“test_retain.py”,您可以计算每次就诊时每个医疗代码的贡献。首先,您需要有一个由 numpy.savez_compressed 保存的经过训练的模型。请注意,您需要知道训练 RETAIN 所用的配置(例如使用--time_file
、使用--use_log_time
。)
同样,您需要以相同的方式准备“访问文件”和“标签文件”。不过,这一次,您不需要遵循“.train”、“.valid”、“.test”规则。测试脚本将尝试加载给定的文件名。
您还需要实际字符串医疗代码与其整数代码之间的映射信息。 (例如“Hypertension”映射到24)该文件(我们称之为“映射文件”)需要是一个Python cPickled 字典,其中键是字符串医疗代码,值是相应的整数。 (例如,process_mimic.py 生成的映射文件是“.types”文件)需要此文件以用户友好的格式打印每个医疗代码的贡献。
对于诸如--time_file
或--use_log_time
之类的其他选项,您应该使用与训练模型完全相同的配置。有关更多详细信息,请使用“--help”选项。
运行测试脚本的最小输入是“模型文件”、“访问文件”、“标签文件”、“映射文件”和“输出文件”。 “输出文件”是存储贡献的地方。 python test_retain.py <model file> <visit file> <label file> <mapping file> <output file>