Advertisement

AI+医疗:使用神经网络进行医学影像识别分析 ⛵

阅读量:

🚀 优质资源分享 🚀

学习路线指引(点击解锁) 知识定位 人群定位
🧡 Python实战微信订餐小程序 🧡 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

外链图片无法正常加载,请确保您的浏览器支持该功能并尝试重新加载页面。建议您将图片保存后重新上传(img-PXT9U78R-1660192624727)。(d^2 + e^2 = f^2

💡 作者:韩信子@ShowMeAI
📘 计算机视觉实战系列https://www.showmeai.tech/tutorials/46
📘 行业名企应用系列https://www.showmeai.tech/tutorials/63
📘 本文地址https://www.showmeai.tech/article-detail/298
📢 声明:版权所有,转载请联系平台与作者并注明出处
📢 收藏ShowMeAI查看更多精彩内容

💡 深度学习+医疗科技

快速发展的趋势使得人工智能技术广泛应用于多个领域分支,在其中深度学习方法已被成功实施于各类医学诊断案例中并展现出显著成效,并且在某些特定领域甚至超越了专业医疗人员的能力;CV领域的最新技术方案已成功应用于诸如阿尔茨海默病分类、肺癌检查以及视网膜疾病评估等医学影像分析任务中

该页面中的外链图片无法正常加载,可能由于目标站设置了防盗链机制,您可以尝试将图片先保存到本地后再进行上传操作,以确保能够顺利访问并分享内容

💡 图像分割

将图像进行内容物划分形成不同组的过程即为图像分割。这一过程能够从图中识别出物体及其边界线。另外一种高级的图像是语义分割属于像素级别的分析技术,在许多领域中的典型应用场景均以图

该外链图片无法正常转存,请留意可能存在防盗链配置限制,并建议您先将图片保存至本地设备后再进行上传操作以确保成功访问

外链图片转存失败,源站可能存在防盗链问题,建议您先将图片保存到本地设备后再通过外部链接的方式上传至平台

本文涵盖的深度学习基础课程以及详细讲解了计算机视觉相关知识。
建议大家参考ShowMeAI提供的教程专栏:

💡 语义分割典型网络 U-Net

U-Net 是一种卷积网络架构,用于快速、精确地分割生物医学图像。

探讨语义分割各算法的原理及其优缺点对比(涵盖U-Net),ShowMeAI平台在其过往文章 📘 深度学习与CV教程(14) | 图像分割 (FCN,SegNet,U-Net,PSPNet,DeepLab,RefineNet) 中详尽阐述。

U-Net 的结构如下图所示:

U-Net采用了与大多数卷积神经网络类似的架构设计,并主要由卷积层和最大池化层构成。

  • U-Net 通过拼接编码器与解码器的上采样特征图形成了梯形连接结构,并且这种网络架构与 Ladder Network 类型有相似之处。
  • 采用跨越多尺度特征连接的架构,在各个阶段使得解码器能够学习到编码器池化过程中丢失的相关特征。
  • 其中上采样层采用了转置卷积技术。

💡 使用 U-Net 进行肺部影像分割

外部链接中的图片无法正常上传...可能存在防盗链设置,请您尝试将图片保存后直接上传

此处使用的X射线医学数据集是 🏆 蒙哥马利县 X 射线医学数据集。该数据集合自蒙哥马利县精选而成,涵盖了多种类型的肺部X射线图像,并对每个X射线影像进行了左肺与右肺区域的详细分割处理。建议您即可访问ShowMeAI平台下的百度网盘链接获取这一珍贵的数据资源。

回复

ShowMeAI官方GitHubhttps://github.com/ShowMeAI-Hub

外部链接中的图片存储出现故障,请您尝试将图片保存后再重新上传至该平台(https://p3-juejin.byteimg.comtos-cn-i-k3u1fbpfcp/b2cbcfa6982f4435bb0ac6218a120d6b~tplv-k3u1fbpfcp-zoom-1.image)

① 工具库导入&环境设置

首先导入我们本次使用到的工具库。

复制代码
    
|# 导入工具库||
|---|---|
|import numpy as np||
|import cv2||
|from glob import glob||
|from sklearn.model\_selection import train\_test\_split||
|import tensorflow as tf||
|from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau||
|from tensorflow.keras.optimizers import Adam||
|from tensorflow.keras.metrics import Recall, Precision||

② 数据读取

随后我们将处理数据读取环节。其中涉及的项目包括图像以及与图片尺寸相当的标签层(mask):即蒙版用于标注。为了满足U-Net的需求,在调整尺寸时会特别注意细节。

复制代码
    
|# 读取X射线图像||
|---|---|
|x = cv2.imread(path, cv2.IMREAD\_COLOR)||
|x = cv2.resize(x, (width, height))||
|x = x/255.0||
|x = x.astype(np.float32)||
|return x||
|||
|# 读取标签蒙版||
|def maskread(path\_l, path\_r,width=512,height=512):||
|x\_l = cv2.imread(path\_l, cv2.IMREAD\_GRAYSCALE)||
|x\_r = cv2.imread(path\_r, cv2.IMREAD\_GRAYSCALE)||
|x = x\_l + x\_r||
|x = cv2.resize(x, (width, height))||
|x = x/np.max(x)||
|x = x > 0.5||
|x = x.astype(np.float32)||
|x = np.expand\_dims(x, axis=-1)||
|return x||

③ 数据切分

为了对模型的效果进行有效的评估, 我们需要在后续步骤中完成数据划分工作. 因此, 将所有数据按照标准流程划分为训练集、验证集和测试集. 具体代码如下:

复制代码
    
|"""加载与切分数据"""||
|---|---|
|images = sorted(glob(os.path.join(path, "CXR\_png", "*.png")))||
|masks\_l = sorted(glob(os.path.join(path, "ManualMask", "leftMask", "*.png")))||
|masks\_r = sorted(glob(os.path.join(path, "ManualMask", "rightMask", "*.png")))||
|split\_size = int(len(images) * split) # 9:1的比例切分||
|train\_x, val\_x = train\_test\_split(images, test\_size=split\_size, random\_state=42)||
|train\_y\_l, val\_y\_l = train\_test\_split(masks\_l, test\_size=split\_size, random\_state=42)||
|train\_y\_r, val\_y\_r = train\_test\_split(masks\_r, test\_size=split\_size, random\_state=42)||
|train\_x, test\_x = train\_test\_split(train\_x, test\_size=split\_size, random\_state=42)||
|train\_y\_l, test\_y\_l = train\_test\_split(train\_y\_l, test\_size=split\_size, random\_state=42)||
|train\_y\_r, test\_y\_r = train\_test\_split(train\_y\_r, test\_size=split\_size, random\_state=42)||
|||
|return (train\_x, train\_y\_l, train\_y\_r), (val\_x, val\_y\_l, val\_y\_r), (test\_x, test\_y\_l, test\_y\_r)||

④ TensorFlow IO准备

该系统将利用TensorFlow进行训练与评估;系统将从numpy数组中导入数据并转换为TensorFlow张量;随后将这些数据组织成一批数据供模型训练的数据集。

复制代码
    
|# tensor格式转换||
|---|---|
|def \_parse(x, y\_l, y\_r):||
|x = x.decode()||
|y\_l = y\_l.decode()||
|y\_r = y\_r.decode()||
|x = imageread(x)||
|y = maskread(y\_l, y\_r)||
|return x, y||
|x, y = tf.numpy\_function(\_parse, [x, y\_l, y\_r], [tf.float32, tf.float32])||
|x.set\_shape([512, 512, 3])||
|y.set\_shape([512, 512, 1])||
|return x, y||
|||
|# 构建tensorflow dataset||
|def tf\_dataset(X, Y\_l, Y\_r, batch=8):||
|dataset = tf.data.Dataset.from\_tensor\_slices((X, Y\_l, Y\_r))||
|dataset = dataset.shuffle(buffer\_size=200)||
|dataset = dataset.map(tf\_parse)||
|dataset = dataset.batch(batch)||
|dataset = dataset.prefetch(4)||
|return dataset||

⑤ U-Net 网络构建

下面我们构建 U-Net 网络。

复制代码
    
|from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Conv2DTranspose, Concatenate, Input||
|---|---|
|||
|# 一个卷积块结构||
|def conv\_block(input, num\_filters):||
|x = Conv2D(num\_filters, 3, padding="same")(input)||
|x = BatchNormalization()(x)||
|x = Activation("relu")(x)||
|||
|x = Conv2D(num\_filters, 3, padding="same")(x)||
|x = BatchNormalization()(x)||
|x = Activation("relu")(x)||
|||
|return x||
|||
|# 编码器模块||
|def encoder\_block(input, num\_filters):||
|x = conv\_block(input, num\_filters)||
|p = MaxPool2D((2, 2))(x)||
|return x, p||
|||
|# 解码器模块||
|def decoder\_block(input, skip\_features, num\_filters):||
|x = Conv2DTranspose(num\_filters, (2, 2), strides=2, padding="same")(input)||
|x = Concatenate()([x, skip\_features])||
|x = conv\_block(x, num\_filters)||
|return x||
|||
|# 完整的U-Net||
|def build\_unet(input\_shape):||
|inputs = Input(input\_shape)||
|||
|# 编码器部分||
|s1, p1 = encoder\_block(inputs, 64)||
|s2, p2 = encoder\_block(p1, 128)||
|s3, p3 = encoder\_block(p2, 256)||
|s4, p4 = encoder\_block(p3, 512)||
|||
|b1 = conv\_block(p4, 1024)||
|||
|# 解码器部分||
|d1 = decoder\_block(b1, s4, 512)||
|d2 = decoder\_block(d1, s3, 256)||
|d3 = decoder\_block(d2, s2, 128)||
|d4 = decoder\_block(d3, s1, 64)||
|||
|# 输出||
|outputs = Conv2D(1, 1, padding="same", activation="sigmoid")(d4)||
|||
|model = Model(inputs, outputs, name="U-Net")||
|return model||

⑥ 评估准则与损失函数

在针对语义分割场景的背景下,我们提出用于评估该场景的IoU计算准则,并构建Dice Loss损失函数,在医疗场景中以更针对性的方式进行模型训练

关于评估图像分割性能的指标如IoU和mIoU等相关知识及详细解析可以访问ShowMeAI的博客文章 📘 深度学习与CV教程(14) | 图像分割 (FCN,SegNet,U-Net,PSPNet,DeepLab,RefineNet) ,深入探讨深度学习及其在计算机视觉领域的应用。

关于Dice Loss损失函数的解释如下:

📌 Dice 系数

以 Lee Raymond Dice 命名,在计算两个样本之间相似性方面具有重要作用(其取值范围限定在 [0,1] 内):

s = \frac{2\lvert X \cap Y \rvert}{\lvert X \rvert + \lvert Y \rvert}
其中\lvert X ∩ Y \rvert表示集合X与集合Y交集的部分;\lvert X \rvert以及\lvert Y \rvert分别代表了集合X以及集合Y各自所包含的元素数量。值得注意的是,分子中的因子2来源于分母中包含了两次计算集合间共同元素的影响

在该问题中,在语义分割领域中,XX 代表了分割图像的标准答案GT;YY 则代表了该图像的预测标签Pred。

📌 Dice 系数差异函数(Dice loss)

s=1−2|X∩Y||X|+|Y|s =1- \frac{2|X \cap Y|}{|X|+|Y|}
评估准则与损失函数的代码实现如下:

复制代码
    
|# IoU计算||
|---|---|
|def f(y\_true, y\_pred):||
|intersection = (y\_true * y\_pred).sum()||
|union = y\_true.sum() + y\_pred.sum() - intersection||
|x = (intersection + 1e-15) / (union + 1e-15)||
|x = x.astype(np.float32)||
|return x||
|return tf.numpy\_function(f, [y\_true, y\_pred], tf.float32)||
|||
|# Dice Loss定义||
|smooth = 1e-15||
|def dice\_coef(y\_true, y\_pred):||
|y\_true = tf.keras.layers.Flatten()(y\_true)||
|y\_pred = tf.keras.layers.Flatten()(y\_pred)||
|intersection = tf.reduce\_sum(y\_true * y\_pred)||
|return (2. * intersection + smooth) / (tf.reduce\_sum(y\_true) + tf.reduce\_sum(y\_pred) + smooth)||
|||
|def dice\_loss(y\_true, y\_pred):||
|return 1.0 - dice\_coef(y\_true, y\_pred)||

⑦ 超参数设置与模型编译

接下来在开始模型训练之前,我们先敲定一些超参数,如下:

  • 批次大型 batch size = 2
  • 学习率 learning rate= 1e-5
  • 迭代轮次 epoch = 30

我们采用了Adam优化器来进行训练,在模型评估过程中基于以下四个关键指标进行了性能分析:其中包括Dice 系数(即Dice相似性系数)、IoU指标(Intersection over Union)、召回率以及精确度。

复制代码
    
|# 超参数||
|---|---|
|lr = 1e-5||
|epochs = 30||
|model\_path = "models/model.h5"||
|||
|# 读取数据||
|dataset\_path = './NLM-MontgomeryCXRSet/MontgomerySet'||
|(train\_x, train\_y\_l, train\_y\_r), (val\_x, val\_y\_l, val\_y\_r), (test\_x, test\_y\_l, test\_y\_r) = load\_data(dataset\_path)||
|||
|# 训练集与验证集||
|train\_dataset = tf\_dataset(train\_x, train\_y\_l, train\_y\_r, batch=batch\_size)||
|val\_dataset = tf\_dataset(val\_x, val\_y\_l, val\_y\_r, batch=batch\_size)||
|||
|# 构建模型||
|model = build\_unet((512, 512, 3))||
|# 评估准则||
|metrics = [dice\_coef, iou, Recall(), Precision()]||
|# 编译模型||
|model.compile(loss=dice\_loss, optimizer=Adam(lr), metrics=metrics)||

可以使用model.summary查看模型结构信息与参数量:

复制代码
    
|model . summary()||

结果如下图所示(部分内容截图,全部模型信息较长):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9uRIUkSj-1660192624738)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1979734ea7cb4ef590b696c38c16105d~tplv-k3u1fbpfcp-zoom-1.image)]

⑧ 回调函数&模型训练

我们将在回调函数内部配置模型持久化相关参数,并在动态学习率调节机制下优化模型。完成配置后,在预处理好的数据集上执行训练过程。

复制代码
    
|# 回调函数||
|---|---|
|ModelCheckpoint(model\_path, verbose=1, save\_best\_only=True),||
|ReduceLROnPlateau(monitor='val\_loss', factor=0.1, patience=5, min\_lr=1e-8, verbose=1)||
|]||
|||
|# 模型训练||
|history = model.fit(||
|train\_dataset,||
|epochs=epochs,||
|validation\_data=val\_dataset,||
|callbacks=callbacks||
|)||

训练部分中间信息如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GijYRSZC-1660192624739)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/279703e1fe1046118502c63faede1934~tplv-k3u1fbpfcp-zoom-1.image)]
在训练模型超过 30 个 epoch 后,保存的模型(验证损失为 0.10216)相关的评估指标结果如下:

dice coefficient:0.9148
IoU:0.8441
recall:0.9865
precision:0.9781
validation loss:0.1022
validation dice coefficient:0.9002
validation IoU:0.8198
validation recall:0.9629
validation precision:0.9577

⑨ 模型加载与新数据预估

我们可以将刚才保存好的模型重新加载至内存中,并并对未曾见过的测试数据集进行预测,代码如下:

复制代码
    
|# 重新载入模型||
|---|---|
|with CustomObjectScope({'iou': iou, 'dice\_coef': dice\_coef, 'dice\_loss': dice\_loss}):||
|model = tf.keras.models.load\_model("/content/model.h5")||
|||
|||
|# 测试集预估||
|from tqdm import tqdm||
|import matplotlib.pyplot as plt||
|ct=0||
|||
|# 遍历测试集||
|for x, y\_l, y\_r in tqdm(zip(test\_x, test\_y\_l, test\_y\_r), total=len(test\_x)):||
|""" Extracing the image name. """||
|image\_name = x.split("/")[-1]||
|||
|# 读取测试图片集||
|ori\_x = cv2.imread(x, cv2.IMREAD\_COLOR)||
|ori\_x = cv2.resize(ori\_x, (512, 512))||
|x = ori\_x/255.0||
|x = x.astype(np.float32)||
|x = np.expand\_dims(x, axis=0)||
|||
|# 读取标签信息||
|ori\_y\_l = cv2.imread(y\_l, cv2.IMREAD\_GRAYSCALE)||
|ori\_y\_r = cv2.imread(y\_r, cv2.IMREAD\_GRAYSCALE)||
|ori\_y = ori\_y\_l + ori\_y\_r||
|ori\_y = cv2.resize(ori\_y, (512, 512))||
|ori\_y = np.expand\_dims(ori\_y, axis=-1) # (512, 512, 1)||
|ori\_y = np.concatenate([ori\_y, ori\_y, ori\_y], axis=-1) # (512, 512, 3)||
|||
|# 预估||
|y\_pred = model.predict(x)[0] > 0.5||
|y\_pred = y\_pred.astype(np.int32)||
|#plt.imshow(y\_pred)||
|||
|# 存储预估结果mask||
|save\_image\_path = "./"+str(ct)+".png"||
|ct+=1||
|y\_pred = np.concatenate([y\_pred, y\_pred, y\_pred], axis=-1)||
|sep\_line = np.ones((512, 10, 3)) * 255||
|cat\_image = np.concatenate([ori\_x, sep\_line, ori\_y, sep\_line, y\_pred*255], axis=1)||
|cv2.imwrite(save\_image\_path, cat\_image)||

部分结果可视化:

以下为2个测试案例的原始图像及其对应的原始掩膜(标准答案),以及预测掩膜的合成结果图

测试用例的输入图像(左侧)、原始掩码标签(中间)、预测掩码(右侧)

该外链图片因转存失败而出现相关提示信息:图片无法直接转存至当前平台,请您尝试通过其他方式获取原图链接并重新上传(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/911e1afb28e8427d88e24deb4bcbe75f~tplv-k3u1fbpfcp-zoom-1.image);可能存在防盗链保护机制

参考资料

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1dw8A5ce-1660192624742)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e9190f41b8de4af38c8a1a0c96f0513b~tplv-k3u1fbpfcp-zoom-1.image)]

全部评论 (0)

还没有任何评论哟~