代码以及视频讲解
本文所涉及所有资源均在传知代码平台可获取
论文链接:https://www.sciencedirect.com/science/article/abs/pii/S1076633223003537?__cf_chl_rt_tk=J9Aipfxyk5d.leu48P20ePFNd4B2aunaSmzVpXCg.7g-1721292386-0.0.1.1-6249
论文概述
今天我们把视线转回到AI应用的另外一个大领域——智慧医疗,来看看目前人工智能在这一领域的最新进展。
发表在影像学经典期刊《Academic Radiology》上的论文《Development and Validation of a Feature-Based Broad-Learning System for Opportunistic Osteoporosis Screening Using Lumbar Spine Radiographs》第一次阐述了在智慧医疗领域,纹理特征和卷积特征对于医学影像信息的抽取功能有什么差异,并给出了具体实验验证和指标分析。
作者指出,“宽度学习系统(BLS)的主要优势之一是其简单的平面结构,这使得学习特征表示和分类数据变得容易。与具有复杂架构的其他机器学习算法不同,BLS 具有简洁的设计,便于特征表示学习和数据分类。这种简单的结构还使得模型能够在大数据集上高效运行,而不需要大量的计算资源。BLS 模型的另一个优势是其权重通过解析计算确定,从而降低了过拟合的风险。就基于纹理特征(RF)的 BLS 模型而言,它产生的结果比基于深度卷积特征(DF) 的 BLS 模型更容易解释。原因是 RF 与 BMD 高度相关,这使得理解输入特征与输出之间的关系变得更容易。DF 可以捕捉高级语义信息,这对于某些类型的数据可能是有益的。然而,在这种情况下,基于 RF 的 BLS 模型的分类性能高于基于 DF 的 BLS 模型,表明 RF 更适合这个特定任务。输入特征的选择,例如 RF 或 DF,会影响结果的可解释性和准确性,在开发 BLS 模型时应谨慎考虑。”
纹理特征和深度卷积特征
-
纹理特征是图像处理和计算机视觉中常用的概念,用于描述图像中像素的局部模式和结构,帮助区分不同类型的物体或场景。常见的纹理特征包括统计特征、频域特征、模型特征和结构特征。统计特征如灰度共生矩阵(GLCM)和局部二值模式(LBP),通过计算像素间的联合概率分布和局部二进制模式来描述纹理。频域特征如傅里叶变换和小波变换,通过分析图像的频率成分和多尺度细节来捕捉纹理信息。模型特征如自回归模型(AR),假设像素值可以用其周围像素的线性组合来表示,从而描述纹理。结构特征如边缘方向直方图(HOG)和Gabor滤波器,通过计算图像中局部梯度方向分布和使用一组不同方向和尺度的滤波器来提取纹理特征。这些纹理特征在图像分割、分类、模式识别和检索等应用中非常重要,例如在医学图像分析中可以检测和分类不同类型的组织,在遥感图像处理中可以区分不同类型的地表覆盖。
-
深度卷积特征是由深度卷积神经网络(CNN)提取的特征,用于图像处理和计算机视觉任务。CNN通过多个卷积层、池化层和全连接层的级联,能够从原始图像中自动学习并提取出层次化的特征表示。在CNN中,低层的卷积层通常提取图像中的边缘、纹理和简单形状等低级特征;随着网络的加深,中间层会捕捉到更复杂的模式和结构,如角点、轮廓和局部区域;高层的卷积层则能够提取到语义级别的特征,如物体的类别和具体形状等高级特征。通过这种层次化的特征提取,CNN能够在图像分类、目标检测、图像分割等任务中取得显著的效果。卷积操作通过滑动窗口的方式,将滤波器(卷积核)应用于输入图像,生成特征图。每个卷积核能够检测特定的特征模式,如边缘或纹理。池化层则通过下采样操作,减小特征图的尺寸,从而降低计算复杂度并提高模型的鲁棒性。深度卷积特征的一个重要优势是它们能够自动从大量标注数据中学习特征表示,而无需人工设计特征。这使得深度学习模型能够在各种复杂的视觉任务中表现出色。此外,预训练的深度卷积网络(如VGG、ResNet、Inception等)可以用于迁移学习,通过在新的任务上进行微调,迅速适应不同的应用场景。总之,深度卷积特征在现代计算机视觉领域具有重要的地位,广泛应用于图像分类、目标检测、图像分割、图像生成等多种任务中,显著提升了这些任务的性能和准确性。
算法流程
下面我们来介绍一下论文中所设计的算法流程,原论文采用了两种不同方面的特征提取技术——纹理特征和深度卷积特征,并比较了他们的性能效果。对于纹理特征,论文采用了GLCM、GLRLM、GLDM、GLSZM、NGTDM和一阶形态学特征融合提取的方法来全面地提取到医学影像中的纹理信息。对于深度卷积特征,论文中使用了最经典的ResNet18和VGG11两种深度卷积网络来对医学影像进行特征提取。
下面我们将具体地讲解这篇文章所用到的数据预处理方法,纹理特征提取、深度卷积特征提取和分类网络搭建这四个步骤
数据预处理方法
首先,感兴趣区域(ROIs)在L1-L4椎体(前后视图)上手动分割,以确保诊断模型与DXA测试之间的准确对应。该操作使用开源软件ITK-SNAP (www.itk-snap.org)进行。分割由一位具有6年肌肉骨骼影像经验的放射科医生完成,并由一位具有20年肌肉骨骼影像经验的高级放射科医生验证。阅片者对患者的临床信息和DXA结果均不知情。
为了规范化,ROI图块被转换为灰度图像,并使用双线性插值方法调整为128 × 64像素。这种方法在图像处理应用中常用,因为它在计算效率和图像质量之间提供了一种折衷。使用对比度受限自适应直方图均衡化(CLAHE)对图像进行增强,以提高对比度并提供更清晰的特征细节可视化。CLAHE在增强医学图像方面具有优势,因为它不会增加噪声,并能保留大部分图像纹理细节。使用定量度量结构相似性指数(SSIM)评估CLAHE的效率。在训练集中的直方图均衡化后,SSIM方差显著降低(0.00102 vs. 0.00056,P < .0001),在测试集中也是如此(0.00196 vs. 0.00102,P < .0001)。图2展示了原始和增强的ROI图像及其灰度直方图。
为了处理数据集样本分布不均的问题,使用了合成少数过采样技术(SMOTE)。SMOTE分析少数样本,根据少数样本人工合成额外样本,并将其添加到数据集中,以防止模型学到的知识过于专门化而缺乏广泛性。
纹理特征提取
论文作者们利用PyRadiomics 3.0 从每个感兴趣区域(ROI)中提取二维形态特征和纹理特征。形态特征包括网格表面、像素表面、周长、周长与表面比、球形度、球形不对称、长轴长度、短轴长度和伸长率。纹理特征包括灰度共生矩阵(n = 24)、灰度游程长度矩阵(n = 16)、灰度尺寸区矩阵(n = 16)、相邻灰度差矩阵(n = 5)和灰度依赖矩阵(n = 14)。这些特征与多种骨骼特性有关,包括骨密度(BMD)、骨微观结构和骨组织成分。形态特征和纹理特征的计算和提取是根据图像生物标记标准化倡议(Image Biomarker Standardisation Initiative)指南进行的,以确保一致性和标准化。
深度卷积特征提取
他们使用了两种广泛应用的卷积神经网络(CNN)架构,即ResNet18和VGG11,作为任务的骨干网络。ResNet18由四个残差块组成,每个残差块包含两个卷积层,配有批量归一化和ReLU激活函数。这些层之后是一个快捷连接,将输入添加到第二个卷积层的输出中。而VGG11的特点是其架构中一致使用小的3×3卷积滤波器,这种方法确保了较大的感受野,同时保持了相对较少的参数。VGG11架构由五个堆叠的卷积层组成,称为“vggblocks”,并使用ReLU激活函数。它采用最大池化,并以几层全连接层结束。
他们利用了预训练CNN与迁移学习的强大功能,有效地从图像数据中生成并提取准确且富有表现力的特征。具体来说,他们使用了已经在ImageNet数据集上预训练的ResNet18和VGG11模型。在特征提取过程中,他们冻结了大部分卷积模块,并添加了新的展平和全连接层,以获得所需的特征维度。具体来说,对于VGG11,他们选择并冻结了五个vggblocks。随后,添加了自适应平均池化层和全连接层,以获得4096维的特征表示。对于ResNet18,他们冻结了前四层,每层包含两个Basicblock模块,并添加了自适应平均池化层,以实现512维的特征表示。
分类网络搭建
BLS基于随机向量功能连接神经网络和伪逆理论设计,使其能够快速、增量地学习,并且无需重新训练即可重构系统。BLS模型(如图所示)由输入层、特征和增强节点复合层以及输出层组成。在复合层中,使用不同组的随机权重从输入数据生成多个映射特征组,形成特征节点。此外,特征节点被增强为增强节点,每个增强节点使用不同的随机权重生成。输出是通过所有连接到输出层的特征节点和增强节点计算得出的。
BLS的一个显著优势是训练和输出结果所需的时间非常少。由于采用伪逆理论,输出层的权重可以通过简单的矩阵运算与上一层的数据进行快速计算和更新。RF和DF被用作BLS模型的输入,用于图像分类。
代码复现
本文的复现代码我放在了附件当中,大家可以自行下载下来进行对比实验。下面我将结合伪代码流程图对代码的逻辑进行简单地讲解
BLS_Model.py文件——分类器搭建
1. 初始化必要的库和函数- numpy, sklearn.preprocessing, numpy.random, scipy.linalg, time, sklearn.metrics- 函数: show_accuracy, tansig, sigmoid, linear, tanh, relu, pinv, shrinkage, sparse_bls2. 定义函数 show_accuracy- 输入: predictLabel, Label- 计算正确预测的数量- 使用混淆矩阵计算灵敏度(Sen), 特异度(Spe), 阴性预测值(npv), 阳性预测值(ppv), AUC- 返回: 准确率, Sen, Spe, npv, ppv, auc, 预测标签, 实际标签3. 定义激活函数 tansig, sigmoid, linear, tanh, relu4. 定义函数 pinv- 输入: A, reg- 计算伪逆矩阵- 返回: 伪逆矩阵5. 定义函数 shrinkage- 输入: a, b- 计算收缩结果- 返回: z6. 定义函数 sparse_bls- 输入: A, b- 设置参数 lam, itrs- 初始化变量- 迭代计算稀疏BLS- 返回: wk7. 定义函数 BLS_AddFeatureEnhanceNodes- 输入: train_x, train_y, test_x, test_y, N1, N2, N3, L, M1, s, c, M2, M3- 初始化变量 u, train_x, FeatureOfInputDataWithBias, OutputOfFeatureMappingLayer, Beta1OfEachWindow, distOfMaxAndMin, minOfEachWindow, train_acc, test_acc, train_time, test_time- 记录开始时间 time_start- 对每个窗口进行以下操作:- 生成随机权重- 计算窗口特征- 预处理特征- 计算稀疏BLS- 计算窗口输出- 记录最大最小值- 规范化窗口输出- 更新输出特征映射层- 生成增强层输入- 计算增强层权重- 计算增强层输出- 计算输出层输入- 计算输出权重- 记录结束时间 time_end- 计算训练时间 trainTime- 计算训练输出- 调用 show_accuracy 计算训练准确率等- 记录训练准确率和时间- 对测试数据进行同样的处理- 记录结束时间 time_end- 计算测试时间 testTime- 计算测试输出- 调用 show_accuracy 计算测试准确率等- 记录测试准确率和时间- 返回: testAcc, trainAcc, Sentr, Spetr, npvtr, ppvtr, auctr, Sen, Spe, npv, ppv, auc, predtt, labeltt, predtr, labeltr
在BLS_Model.py文件中,我实现了基于随机向量功能连接神经网络和伪逆理论的快速增量学习方法。代码首先导入必要的库和函数,包括用于数据预处理、矩阵运算、时间测量和模型评估的库。定义了一些辅助函数,例如激活函数(tansig、sigmoid、linear、tanh、relu),用于计算伪逆矩阵的函数(pinv),以及用于稀疏BLS计算的函数(sparse_bls)。主要的函数是BLS_AddFeatureEnhanceNodes,它实现了BLS模型的训练和测试。首先对输入数据进行预处理,将其标准化并添加偏置项。然后,在特征映射层中,使用不同组的随机权重生成多个映射特征组,形成特征节点,并通过稀疏BLS方法计算每个窗口的权重和输出。接着,在增强层中,生成增强节点,并计算其输出。在输出层中,结合特征映射层和增强层的输出,计算输出权重。训练阶段,记录训练时间,并计算训练输出,通过show_accuracy函数评估模型的训练准确率、灵敏度、特异度、阴性预测值、阳性预测值和AUC。同样地,在测试阶段,对测试数据进行同样的处理,记录测试时间,并评估模型的测试性能。最终,返回测试准确率、训练准确率及其他评估指标。整个过程旨在通过快速、增量地学习,从输入数据中提取特征,并通过简单的矩阵运算进行模型的更新和评估。
pyradioextraction.py文件——纹理特征提取
1. 导入必要的库和模块
2. 定义函数 listdirInMac:a. 初始化空列表 listsb. 遍历输入列表 list:i. 如果项的第一个字符是 '.',跳过该项ii. 否则,将该项添加到 listsc. 返回 lists3. 定义函数 catch_features:a. 检查 imagePath 或 maskPath 是否为空:i. 如果为空,抛出异常b. 初始化 settings 字典并设置提取参数c. 创建 RadiomicsFeatureExtractor 实例 extractor,传入 settingsd. 禁用所有特征e. 启用所需的特征类别(shape2D, glcm, glszm, glrlm, ngtdm, gldm)f. 执行特征提取并获取结果 resultg. 初始化空列表 feature_names 和 feature_valuesh. 遍历 result 中的每个键值对:i. 如果键匹配特定的特征类别,打印键和值,将键和值添加到相应的列表i. 打印特征维度和特征名称j. 返回 feature_values 和 feature_names4. 在 main 函数中:a. 定义 image_dir 和 mask_dir 的路径b. 获取患者列表 patient_list 并使用 listdirInMac 函数过滤c. 对 patient_list 进行排序d. 初始化空列表 all_data 和 all_namee. 遍历 patient_list 中的每个患者:i. 打印患者名称ii. 遍历患者文件夹中的每个文件:- 如果文件名是 'data.nii.gz',设置 imagePath- 如果文件名是 'zw.nii.gz',设置 maskPathiii. 调用 catch_features 函数提取特征,并保存结果到 all_data,特征名称保存到 all_namef. 打印所有数据和特征名称g. 打开 CSV 文件进行写入:i. 写入列名(特征名称)ii. 写入每行特征数据
这段代码的目的是从医学图像中提取纹理特征。首先,代码导入了一些必要的库和模块,包括用于处理图像的SimpleITK和用于特征提取的pyradiomics。定义了一个函数listdirInMac
来过滤掉隐藏文件。主函数catch_features
接受图像和掩码的路径,使用特定的设置来初始化一个特征提取器,并提取2D形状特征、灰度共生矩阵(GLCM)、灰度大小区域矩阵(GLSZM)、灰度运行长度矩阵(GLRLM)、灰度依赖矩阵(GLDM)和邻域灰度差矩阵(NGTDM)的特征。这些特征被保存为名称和值,并返回给主程序。
主程序首先定义了图像目录和掩码目录的路径,并获取患者列表。对于每个患者,找到相应的图像和掩码文件路径,并调用catch_features
函数提取特征。所有提取的特征被保存到一个CSV文件中,方便后续分析和处理。
这个过程通过一系列的步骤和函数调用,实现了从医学图像中自动提取纹理特征并保存到文件中,便于后续的分析和研究。
CNNextraction.py 文件——深度卷积特征提取
1. 导入必要的库和模块- urllib- torch- matplotlib.pyplot as plt- torch.nn as nn- torchvision.models- torchvision.transforms- numpy as np- cv2- os- ssl- PIL.Image- scipy.io as scio- urllib3.util.url2. 设置 HTTPS 上下文3. 定义函数 listdirInMac:a. 初始化空列表 listsb. 遍历输入列表 list:i. 如果项的第一个字符是 '.',跳过该项ii. 否则,将该项添加到 listsc. 返回 lists4. 定义类 FeatureExtractor (继承自 nn.Module):a. 初始化方法:i. 提取 VGG-16 特征层并将其设置为 nn.Sequentialii. 提取 VGG-16 的平均池化层iii. 转换图像为一维向量iv. 提取 VGG-16 全连接层的第一部分v. 添加新的全连接层(fc2)b. 前向传播方法:i. 通过特征层提取特征ii. 通过池化层提取特征iii. 打印特征的形状iv. 转换张量为 numpy 数组并重塑v. 使用 matplotlib 显示特征图vi. 返回提取的特征5. 定义类 FeaExtractor (继承自 nn.Module):a. 初始化方法:i. 提取模型的卷积层、批量归一化层、激活函数、最大池化层和自适应平均池化层ii. 提取模型的各个层iii. 转换图像为一维向量b. 前向传播方法:i. 通过各个层提取特征ii. 转换张量为 numpy 数组并重塑iii. 使用 matplotlib 显示特征图iv. 返回提取的特征6. 加载预训练的 EfficientNet 模型
7. 替换 EfficientNet 的第一个卷积层
8. 将模型移动到 GPU
9. 定义图像预处理步骤10. 定义函数 extract:a. 初始化空列表 featuresb. 获取并过滤路径中的文件列表c. 对文件列表进行排序d. 遍历文件列表:i. 加载并预处理每个图像ii. 将图像合并为一个批次并添加一个维度iii. 将图像移动到 GPUiv. 禁用梯度计算并提取特征v. 将提取的特征转换为 numpy 数组并保存到 features 列表e. 将特征转换为 numpy 数组并返回11. 如果是主程序:a. 设置训练数据路径
在这段代码中,我使用预训练的ResNet模型提取图像特征并保存到MAT文件中。首先,导入了必要的库,包括PyTorch、PIL、Matplotlib等,并设置了HTTPS上下文以跳过SSL验证。定义了辅助函数listdirInMac,用于过滤目录列表,去掉以’.'开头的隐藏文件或目录。定义了特征提取器类FeatureExtractor,继承自nn.Module,用于从ResNet模型中提取特征。初始化方法中提取了ResNet的特征层、平均池化层,并添加了一个新的全连接层。在前向传播方法中,通过特征层和池化层提取特征,并使用Matplotlib显示特征图。代码还定义了另一个特征提取器类FeaExtractor,未被使用,其作用类似于FeatureExtractor。
然后,代码使用torchvision.models加载预训练的ResNet模型,并将其移动到GPU,同时定义了一系列图像预处理步骤。接着,定义了特征提取函数extract,该函数初始化一个空列表用于存储特征,获取图像目录中的文件列表并进行排序,遍历文件列表,加载并预处理每张图像,将预处理后的图像合并为一个批次,移动到GPU,并使用新定义的模型提取特征,最后将提取到的特征保存到MAT文件中。
main.py——顶层文件
开始
|
|-- 函数: Texture_preparation()
| |
| |-- 从 "train_dataset_smote_84.mat" 加载 train_data
| |-- 打印 train_data 的形状
| |-- 从 "test_features.mat" 加载 test1_data
| |-- 打印 test1_data 的形状
| |-- 从 "train_dataset_smote_84.mat" 加载 train_label
| |-- 打印 train_label 的形状
| |-- 从 "test1_flag.mat" 加载 test1_label
| |-- 删除 test1_label 中的特定行
| |-- 打印 test1_label 的形状
| |-- 返回 train_data, train_label, test1_data, test1_label
|
|-- 主块||-- 定义参数: N1, N2, N3, L, M1, s, c, M2, M3||-- 调用 Texture_preparation() 并将返回值赋给 train_data, train_label, test1_data, test1_label||-- 打印 test1_data|-- 打印 test1_label||-- 调用 BLS_AddFeatureEnhanceNodes() 并将返回值赋给各个指标变量||-- 打印测试和训练的准确率及其他指标
|
结束
这个文件起到了一个集成的作用,用于将不同的特征数据加载到内存环境中,并输入到BLS分类器中进行对比实验,从而分析不同特征对医学影像信息的提取性能。
实验结果
在本次实验中,由于原论文的数据涉及病人隐私无法公开,因此我使用了同样类型的医学公开数据集进行实验,链接如下:https://data.mendeley.com/datasets/fxjm8fb6mw
如图所示,我们按照论文步骤,将三种不同类型的特征分别输入到BLS分类器中进行性能的对比实验,可以得到与论文中一致的结果。在到达相似的训练程度时,输入纹理特征的模型相比其他两种特征模型能够在各项指标上取得更好的结果。特别是在医疗领域最为关注的曲线下面积(AUC)这一项中,该模型拉开了比较大的差距。因此我们可以推测出,在基于纹理特征(RF)的BLS模型与基于深度卷积特征(DF)的BLS模型的对比中,前者的结果更容易解释。这是因为RF与BMD高度相关,使得理解输入特征与输出结果之间的关系更加直观。尽管DF可以捕捉高级语义信息,在某些数据类型中可能更有利,但在这个特定任务中,基于RF的BLS模型的分类性能优于基于DF的BLS模型,表明RF在此任务中更为适用。
论文中结果:
使用方式
下面我们来介绍一下如何使用附件代码来进行运行三种不同的特征模型。文件格式如下所示:
一键运行
前面已经为大家介绍了各个文件的功能,这里需要注意的,为了便于理解,我将main.py文件拆分成了三个文件BLS_ResnetFeatures.py、BLS_TextureFeatures.py和BLS_VGGFeatures.py,分别代表了三种不同的特征输入BLS分类器进行实验。在这里为了方便大家一键运行,我已经将原始数据进行处理好,三种类型的特征分别存入了CNNFeatures(Resnet特征)、TextureFeatures(纹理特征)和VGGFeatures(VGG特征)文件夹中。大家进入对应的主文件中就可以一键运行实验。
本地训练
如果大家还希望能使用自己的私人数据集进行训练,可以按照我前面所说的算法步骤进行一步步地处理。使用pyradioextraction.py文件可以直接对医学影像数据进行纹理特征的提取,使用CNNextraction.py可以对转换后的医学图像进行两种深度卷积特征的提取。
提取纹理特征时注意要在pyradioextraction.py文件中修改对应的文件路径
提取深度卷积特征时同样要注意文件保存路径问题
处理结束后就可以看到在目录下会生成相应的文件,接下来输入到BLS分类器中就可以进行训练了!
环境配置
- python版本3.6及以上;
- 预先安装第三方库:radiomics、SimpleITK、torch、torchvision和scipy
源码下载