irpas技术客

使用基于注意力的编码器-解码器实现医学图像描述_deephub

网络投稿 3990

使用计算机视觉和自然语言处理来为X 射线的图像生成文本描述。

什么是图像描述

图像描述是生成图像文本描述的过程。它使用自然语言处理和计算机视觉来为图像生成描述的文本字幕。一幅图像可以有很多个不同的描述,但是只要它正确地描述了图像,并且描述涵盖了图像中的大部分信息就可以说是没问题的。下面是示例图片和生成的描述文字。

放射学中的图像描述

放射学也称为诊断成像,是一系列通过拍摄身体部位的照片或图像来诊断和治疗疾病的测试。虽然有几种不同的成像检查,但最常见的包括 X 射线、MRI、超声波、CT 扫描和 PET 扫描。

放射科医生将查看这些成像测试的结果,找到评估和支持诊断的相关图像。患者完成影像学检查后,放射科医生将向临床医生提供他们的解释报告。典型的放射学报告包括以下部分:检查名称或类型、检查日期、MeSH(医学主题词库)、解释放射科医师详细信息、临床病史等,

借助深度学习和自然语言处理,我们可以通过描述 X 射线来减少放射科医生的工作量,因此在本案例研究中,我们将从 X 射线中提取结果,将相同的概念扩展到其他部分例如MeSH等,

为什么这个问题很重要?

根据美国放射学杂志和 BMJ:英国医学杂志,与特定地区的人口相比,放射科医生很少,特别是在农村和较小的社区环境中,因此医学图像解释和编目存在巨大延迟,从而影响到医疗诊断,并使患者护理面临风险。

医学图像由专业医学专业人员(放射科医师)阅读和解释,他们对每个检查区域的发现通过书面医学报告(放射学报告)进行记录和传达。 撰写医疗报告的过程通常需要 5-10 分钟左右。 一天之内,医生必须编写数以百计的医学报告,这可能会花费他们很多时间。如果我们开发的模型可以在没有放射科医生和编目员的任何干预的情况下加快医学图像解释和编目,这将有效地解决了这些问题。

用深度学习来解决这个问题!

图像和文本句子是序列信息,因此我们将在编码器-解码器等设置中使用像 LSTM 或 GRU 这样的 RNN(循环神经网络),并添加注意力机制来提高我们的模型性能。 当然使用Transformers 理论上来说会更好。

如何评价我的模特的表现呢?BLEU: Bilingual Evaluation Understudy

BLEU 是一种用于评估机器翻译文本质量的算法。BLEU 背后的中心思想是机器翻译越接近专业的人工翻译越好,它也是最早声称与人工质量判断具有高度相关性的指标之一,并且到现在仍然是最受欢迎的指标之一。

BLEU 的输出始终是一个介于 0 和 1 之间的数字。该值表示候选文本与参考文本的相似程度,接近 1 的值表示更相似。本文使用的 BLEU 是基于n-gram 精度改进的,因为它使用 n-gram 来比较和评价生成文本的质量并给出分数,它计算快速简单并且被广泛使用。

BLEU 的工作方式很简单。给定一个句子和一组参考句子的一些候选翻译,我们使用词袋方法来查看在翻译和参考句子中同时出现了多少 BOW。 BOW 是一种简单而高效的方法,可确保机器翻译包含参考翻译也包含的关键短语或单词。换句话说,BLEU 将候选翻译与人工生成的带注释的参考翻译进行比较,并比较候选句子中有多少命中。 BOW 出现次数越多,翻译效果就越好。

在了解 BLEU 之前,我们需要了解 Precision、Modified Precision 和 Brevity Penalty。

Precision:

这里 tp 和 fp 分别代表真正例和假正例。我们可以认为正例大致对应于命中或匹配的概念。换句话说正例是我们可以从给定的候选翻译中构建的单词 n-gram 包。真正例是出现在候选翻译和一些参考翻译中的 n-gram。误报是只出现在候选翻译中的那些。

Modified Precision:

如果简单的基于精度的度量计算会产生很大的问题,比如如果我们有一个候选样本,It it it it it it it it it it it it it"上面的精度计算会给出1作为输出,但它给定的候选是非常糟糕的。这是因为精确度只涉及检查是否出现了一个命中,但它不检查是否重复。因此需要修改精度,如果这些重复多次,我们将进行裁剪:

Count指的是我们分配给某个n-gram的命中次数。Mw是指在候选句子中出现n-gram的次数。Mmax,即该n-gram在任何一个参考句子中出现的最大次数。

Brevity Penalty:

Brevity Penalty惩罚短的候选翻译,从而确保只有足够长的机器翻译才能获得高分。 它的目标是找到与所的候选翻译的长度最接近的参考句子的长度。 如果该参考句子的长度大于候选句子,就会施加一些惩罚; 如果候选句子更长,则不应用任何惩罚。 处罚的具体公式如下:

BLEU:

集成上面的所有 BLEU的公式如下:

这里的N为指定单词包的大小,或N -gram,Wn表示修正后的精度pn的权重。

NLTK包中有BLEU现成的实现,我们可以直接使用

from nltk.translate.bleu_score import sentence_bleu reference = [['this', 'is', 'small', 'test']] candidate = ['this', 'is', 'a', 'test'] print('Cumulative 1-gram: %f' % sentence_bleu(reference, candidate, weights=(1, 0, 0, 0))) print('Cumulative 2-gram: %f' % sentence_bleu(reference, candidate, weights=(0.5, 0.5, 0, 0))) print('Cumulative 3-gram: %f' % sentence_bleu(reference, candidate, weights=(0.33, 0.33, 0.33, 0))) print('Cumulative 4-gram: %f' % sentence_bleu(reference, candidate, weights=(0.25, 0.25, 0.25, 0.25))) 获取和理解和处理数据

对于这个本文的研究,我们使用来自印第安纳大学医院网络的开源数据。印第安纳大学-胸部x光片(PNG图片)

https://academictorrents.com/details/5a3a439df24931f410fac269b87b050203d9467d

图像数据的信息如下:

数据大小:1.36 GB,图像数量:7470,所有图片均为png格式,可以直接使用OpenCV处理图像。所有的图像都有相同的宽度512像素。但是高度从362 p到873 px不等。

图像中包含了FRONTAL和LATERAL两个方向的x光

XML报告数据如下:

印第安纳大学-胸部x光片(XML报告):

https://academictorrents.com/details/66450ba52ba3f83fbf82ef9c91f2bde0e845aba9

数据大小:20.7 MB,报告总数:3955,我们可以使用xml.etree.ElementTree解析XML报告,Xml包含以下重要数据,需要从Xml中提取。

1、适应症:该数据描述了研究原因和/或适用的临床信息或诊断的简单、简洁的陈述。 对适应症的清晰理解也可以阐明研究应解决的适当临床问题。例如:结核病检测阳性、胸痛等,

2、对比:该数据描述了是否将这种新的成像检查与任何可用的先前检查进行比较。 比较通常涉及相同身体部位和检查类型的检查。

3、发现:该数据列出了放射科医生在检查中身体各个部位的观察结果。 这记录了该区域是否被认为是正常、异常或潜在异常。例如心脏大小正常。 纵隔无异常。 肺清净等,

4、 结果:该数据包含调查结果的摘要,并报告他们看到的最重要的调查结果以及这些调查结果的可能原因。 本节提供了最重要的决策信息。例如无急性病、清肺等,

整合上面的2个信息简单的可视化如下:

EDA(探索性数据分析)

使用XML库,我们从每个患者XML报告中提取“发现”、图像路径和患者id信息,并与它们形成一个数据集。

images = [] patient_ids = [] img_findings = [] for filename in tqdm(os.listdir(os.getcwd()+'/reports/ecgen-radiology')): if filename.endswith(".xml"): f = os.path.join(os.getcwd()+'/reports/ecgen-radiology',filename) tree = ET.parse(f) root = tree.getroot() for child in root: if child.tag == 'uId': patient = child.attrib['id'] if child.tag == 'MedlineCitation': for attr in child: if attr.tag == 'Article': for i in attr: if i.tag == 'Abstract': for name in i: if name.get('Label') == 'FINDINGS': findings=name.text for p_image in root.findall('parentImage'): patient_ids.append(patient) images.append(p_image.get('id')) img_findings.append(findings)

总共有3851名患者:

1张图像患者:446例2张图像患者:3208例3张图像患者:181例4张图像患者15例5张图像患者:1例.

为了捕获大部分信息,我们将两个图像的输入提供给模型,规则如下

如果患者有一张与报告相关的 X 射线图像,我们将相同的图像复制两次作为 image1 和 image2。

如果患者有两张与报告相关的 X 射线图像,我们将第一张图像做为 image1,第二张做为 image2。

如果患者有两个以上的 X 射线与报告相关联,我们随机选择 2 个 作为 image1 和 image2。

针对于“发现列”的数据处理

在发现列中大约有13%的空值。我们将删除在结果列中具有空值的行,因为没法用随机的结果填充空值。并将其转换为小写,删除垃圾词

image_findings_dataset['findings'] = image_findings_dataset.loc[:,('findings')].str.lower() #https://stackoverflow.com/questions/19790188/expanding-english-language-contractions-in-python def decontracted(row): # specific row = str(row) row = re.sub(r"won\'t", "will not", row) row = re.sub(r"can\'t", "can not", row) # general row = re.sub(r"n\'t", " not", row) row = re.sub(r"\'re", " are", row) row = re.sub(r"\'s", " is", row) row = re.sub(r"\'d", " would", row) row = re.sub(r"\'ll", " will", row) row = re.sub(r"\'t", " not", row) row = re.sub(r"\'ve", " have", row) row = re.sub(r"\'m", " am", row) row = re.sub('xxxx','',row) #occurs many times in text may be private information which isn't useful return str(row) def preprocessing(row): row = str(row) row = re.sub(r'xx*','',row) # Removing XXXX row = re.sub(r'\d','',row) # Removing numbers temp = "" for i in row.split(" "): #Removing 2 letter words if i!= 'no' or i!='ct': temp = temp + ' ' + i temp = re.sub(' {2,}', ' ',temp) #Replacing double space with single space temp = re.sub(r'\.+', ".", temp) #Replacing double . with single . temp = temp.lstrip() #Removing space at the beginning temp = temp.rstrip() #Removing space at the end return temp image_findings_dataset['findings']= image_findings_dataset['findings'].apply(preprocessing)

理解统计结果

我们可以看到像胸腔积液(pleural effusion),气胸(pneumothora),心脏纵隔轮廓( cardiomediastnal silhouette),yi一般情况下我们认为这些词不是正常词,但这些是医学领域特有的,并且出现的频率很大,说明预处理后看起来很干净。

数据拆分和标记

如果仔细观察结果列,可以看到结果列中的数据偏向于非疾病数据(数据不平衡),并且由于我们的数据非常少,大约 3300 条记录,这根本不足以用于深度学习方法,所以这里将尝试使用重新采样的方法处理数据使数据平衡(我们尝试了多种方法,下面的方法是最好的)

在结果列中重复了很多数据,让我们采用一种策略来训练更好的模型。

第 1 步:让我们把数据集分成两部分

1、发现列出现次数超过 25 次。

2、发现列少于或等于 5 次。

第 2 步:用 test_size = 0.1 划分训练测试集以获得大于 5 的结果。

第 3 步:将 20% 样本大小的训练测试集划分为小于或等于 5 的结果。然后添加该样本测试并使用剩下的进行训练

第 4 步:上采样少数点,下采样多数点

通过这样做,可以减少数据集中在发现方面的不平衡

findings_gt_5 = image_findings_dataset[image_findings_dataset['findings_count']>5] findings_lte_5 = image_findings_dataset[image_findings_dataset['findings_count']<=5] train,test = train_test_split(findings_gt_5,stratify = findings_gt_5['findings'].values,test_size = 0.1,random_state = 420) test_findings_lte_5_sample = findings_lte_5.sample(int(0.2*findings_lte_5.shape[0]),random_state = 420) findings_lte_5 = findings_lte_5.drop(test_findings_lte_5_sample.index,axis=0) test = test.append(test_findings_lte_5_sample) test = test.reset_index(drop=True) train = train.append(findings_lte_5) train = train.reset_index(drop=True) train.shape[0],test.shape[0] image_findings_dataset_majority = train[train['findings_count']>=25] #having value counts >=25 image_findings_dataset_minority = train[train['findings_count']<=5] #having value counts <=5 image_findings_dataset_other = train[(train['findings_count']>5)&(train['findings_count']<25)] #value counts between 5 and 25 n1 = image_findings_dataset_minority.shape[0] n2 = image_findings_dataset_majority.shape[0] n3 = image_findings_dataset_other.shape[0] image_findings_dataset_minority_upsampled = resample(image_findings_dataset_minority, replace = True, n_samples = 4*n1, random_state = 420) image_findings_dataset_majority_downsampled = resample(image_findings_dataset_majority, replace = False, n_samples = n2//5, random_state = 420) image_findings_dataset_other_downsampled = resample(image_findings_dataset_other, replace = False, n_samples = n3//3, random_state = 420) train = pd.concat([image_findings_dataset_majority_downsampled ,image_findings_dataset_minority_upsampled,image_findings_dataset_other_downsampled]) train = train.reset_index(drop=True) train.shape

在分别对少数和多数样本进行上采样和下采样后,得到的训练数据有 8795 条记录,测试数据有 604 条记录,在医学这种不平衡数据中这个过程是必须的的。

创建令牌标记

tokenizer = Tokenizer(filters = '',oov_token = '<unk>') #setting filters to none tokenizer.fit_on_texts(train.findings_total.values) train_captions = tokenizer.texts_to_sequences(train.findings_total) test_captions = tokenizer.texts_to_sequences(test.findings_total) vocab_size = len(tokenizer.word_index) caption_len = np.array([len(i) for i in train_captions]) start_index = tokenizer.word_index['<start>'] #tokened value of <start> end_index = tokenizer.word_index['<end>'] #tokened value of <end>

现在数据集已准备好进行建模了

构建图像描述模型

在建立模型之前,让我们先了解一些注意力在基于的编码器-解码器模型中使用的概念。

**ChexNet **

ChexNet 是一种深度学习算法,可以从胸部 X 光图像中检测和定位 14 种疾病。 在 ChestX-ray14 数据集上训练了一个 121 层的卷积神经网络,该数据集包含来自 30,805 名独特患者的 112,120 张正面视图 X 射线图像。 结果非常好超过了执业放射科医生的表现。

我们使用 ChexNet 预训练的权重来使用迁移学习获得 X 射线的嵌入。 由于 ChexNet 权重在 ChestX-ray14 数据集上的疾病分类等任务中得到了很好的收敛。

论文:https://arxiv.org/pdf/1711.05225v3.pdf

权重文件:https://·/post/ccf35d78cb23425686d61c53aa404b76

作者:Santhosh Kurnapally


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #下面是示例图片和生成的描述文字