骨龄是骨骼年龄的简称,需要借助于骨骼在X光摄像中的特定图像来确定。通常要拍摄左手手腕部位的X光片,医生通过X光片观察来确定骨龄。这在临床上是一件非常消耗精力和时间的一项放射临床工作。写一个骨龄可能要10多分钟去完成。如果一天要写几十个骨龄,那么基本上一天要消耗掉巨大的时间精力了,随着智能化科技技术与设备的快速发展与广泛普及,越来越多的行业开始接入AI,希望借助于AI的能力来实现行业领域的高质提效,对于医学领域来说更是如此,本文的主要目的就是想要从实验实践分析的角度来探索AI助力赋能的可行性。在上文:
《助力智能化诊断高质提效,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建X光骨骼医疗场景下人体手骨X光图像骨骼检测识别系统》
我们基于经典的YOLOv5目标检测模型开发构建了用于人体手骨个X光目标检测识别模型,之后我们需要对YOLOv5模型检测到的不同类型的骨骼进行自动化的识别得到骨骼的等级进而通过RUS-CHN计分法来计算得到当前手骨骨骼图像的骨龄数据。
首先看下实例效果:
简单看下实例数据集情况:
在前文已经完成的目标检测模型的基础上,我们需要对检测到的不同的骨骼进行切分来构建我们所需要的数据集,最终如下:
每种不同类别下划分为不同的等级,如下:
实例数据如下:
本文识别模型的开发选择基于轻量级CNN的翘楚MobileNet来进行开发构建,MobileNet模型是一种轻量级的卷积神经网络模型,主要用于在移动设备和嵌入式设备上进行实时图像分类和目标检测。发展迭代至今已经演变出来了v1-v3三个大版本,其中v3版本包含了large和small两款不同参数量级的模型。
MobileNetV1:
MobileNetV1利用分组卷积降低网络的计算量,并将分组卷积应用到极致,即网络的分组数与网络的channel数量相等,使网络的计算量减到最低。但这样会导致channel之间的交互缺失,所以作者又使用了point-wise conv,即使用1x1的卷积进行channel之间的融合。同时,MobileNetV1还采用了直筒结构。
MobileNetV2:
MobileNetV2引入了bottleneck结构,并将bottleneck结构变成了纺锤型。与ResNet不同,MobileNetV2先放大特征图的尺寸,再缩小尺寸,以增加感受野。同时,MobileNetV2去掉了Residual Block最后的ReLU激活函数。这些改进使得MobileNetV2在保持高性能的同时进一步降低了模型大小和计算量。
MobileNetV3:
MobileNetV3是MobileNet系列的最新版本,它采用了AutoML的方法为给定的问题找到最佳的神经网络架构。MobileNetV3提出了两种模型大小的配置:Large和Small。
MobileNetV3-Large:
MobileNetV3-Large在构建模型时采用了多个创新性的技术。首先,它综合了MobileNetV1和MobileNetV2的优点,包括分组卷积、1x1的卷积进行channel之间的融合、以及纺锤型的bottleneck结构。其次,MobileNetV3-Large引入了特有的bneck结构,该结构综合了以下四个特点:
MobileNetV2的具有线性瓶颈的逆残差结构(the inverted residual with linear bottleneck)。即先利用1x1卷积进行升维度,再进行下面的操作,并具有残差边。
MobileNetV1的深度可分离卷积(depthwise separable convolutions)。在输入1x1卷积进行升维度后,进行3x3深度可分离卷积。
轻量级的注意力模型。这个注意力机制的作用方式是调整每个通道的权重。
利用h-swish代替swish函数。在结构中使用了h-swish激活函数,代替swish函数,减少运算量,提高性能。
此外,MobileNetV3-Large还使用了线性瓶颈结构来进一步减少模型的参数数量和计算量。线性瓶颈结构是一种将输入通道数和输出通道数都进行压缩的结构,它可以将模型的参数数量和计算量降低到最小。
MobileNetV3-Small:
相对于MobileNetV3-Large,MobileNetV3-Small更注重模型的轻量级和计算效率。它采用了深度可分离卷积来减少模型的参数数量和计算量,从而实现在低功耗设备上的高效运算。同时,MobileNetV3-Small还采用了线性瓶颈结构和残差连接等技术来提高模型的分类性能和训练稳定性。
我们这里使用的是v1模型,核心代码实现如下所示:
def MobileNet(
input_shape=None, alpha=1.0, depth_multiplier=1, dropout=1e-3, classes=1000
):
img_input = Input(shape=input_shape)
x = convBlock(img_input, 32, alpha, strides=(2, 2))
x = dwConvBlock(x, 64, alpha, depth_multiplier, block_id=1)
x = dwConvBlock(x, 128, alpha, depth_multiplier, strides=(2, 2), block_id=2)
x = dwConvBlock(x, 128, alpha, depth_multiplier, block_id=3)
x = dwConvBlock(x, 256, alpha, depth_multiplier, strides=(2, 2), block_id=4)
x = dwConvBlock(x, 256, alpha, depth_multiplier, block_id=5)
x = dwConvBlock(x, 512, alpha, depth_multiplier, strides=(2, 2), block_id=6)
x = dwConvBlock(x, 512, alpha, depth_multiplier, block_id=7)
x = dwConvBlock(x, 512, alpha, depth_multiplier, block_id=8)
x = dwConvBlock(x, 512, alpha, depth_multiplier, block_id=9)
x = dwConvBlock(x, 512, alpha, depth_multiplier, block_id=10)
x = dwConvBlock(x, 512, alpha, depth_multiplier, block_id=11)
x = dwConvBlock(x, 1024, alpha, depth_multiplier, strides=(2, 2), block_id=12)
x = dwConvBlock(x, 1024, alpha, depth_multiplier, block_id=13)
x = GlobalAveragePooling2D()(x)
shape = (1, 1, int(1024 * alpha))
x = Reshape(shape, name="reshape_1")(x)
x = Dropout(dropout, name="dropout")(x)
x = Conv2D(classes, (1, 1), padding="same", name="conv_preds")(x)
x = Activation("softmax", name="act_softmax")(x)
x = Reshape((classes,), name="reshape_2")(x)
inputs = img_input
model = Model(inputs, x, name="mobilenet_%0.2f" % (alpha))
return model
MobileNet是一种轻量级的卷积神经网络模型,旨在在计算资源受限的移动设备上实现高效的图像分类和目标检测。其主要原理如下:
Depthwise Separable Convolution:MobileNet使用Depthwise Separable Convolution来减少参数量和计算量。这是一种将标准卷积分解成深度卷积(Depthwise Convolution)和逐点卷积(Pointwise Convolution)两个步骤的方法。深度卷积仅对输入的每个通道进行卷积,减少了卷积核的数量。逐点卷积使用1x1卷积核来将深度卷积的输出转化为期望的特征维度。这种分解有效降低了参数量,减少了计算量。
网络结构设计:MobileNet采用了基于深度可分离卷积的轻量网络结构。网络主要由一系列重复的卷积块和下采样层构成。卷积块包含了深度卷积、逐点卷积和激活函数。下采样层通常使用步长较大的深度可分离卷积来减少特征图的尺寸。通过这种设计,MobileNet减少了网络的深度和参数量,从而在较小的设备上实现了高效的推理。
优点:
轻量高效:MobileNet采用了Depthwise Separable Convolution和轻量网络结构,大大减少了参数量和计算量,使得它在计算资源受限的设备上运行速度快。
网络结构可定制:MobileNet的网络结构可以根据不同的需求和资源限制进行调整和定制。可以通过调整深度可分离卷积的层数和通道数来平衡准确性和模型大小。
缺点:
精度受限:由于网络结构的轻量化和参数减少,MobileNet相对于大型网络模型,如ResNet和Inception等,可能牺牲了一定的精度。
对复杂数据集的泛化能力有限:MobileNet在处理复杂数据集上的泛化能力可能相对较差,适用于较简单的图像分类和目标检测任务。
需要根据实际应用场景和资源限制来权衡使用MobileNet的优势和劣势。在资源受限的设备上,如移动设备或嵌入式系统,MobileNet是一种高效的选择,但在对准确性和复杂性要求较高的任务上,可能需要考虑更为复杂的网络结构。
摩恩300次epoch的迭代计算,等待训练完成后,我们来依次看下。
【DIP】
【DIPFirst】
【MCP】
【MCPFirst】
【MIP】
【PIP】
【PIPFirst】
【Radius】
【Ulna】
因为数据的严重不均衡,模型整体训练过程中的变化波动都是比较大的,后期可以考虑尝试一方面增加一些原始的数据集,一方面进行更多的数据增强处理来均衡化已有的数据集。
到这里我们所需要的识别模型也就开发完成了,我们将前文的检测器与本文的识别器进行级联构建整体的计算流程,就可以进行检测识别分析了,实例效果如下:
借助于RUS-CHN计分法可以实现骨龄自动化计算。
男女骨龄计算略有差异,如下:
SCORE = {'girl':{
'Radius':[10,15,22,25,40,59,91,125,138,178,192,199,203, 210],
'Ulna':[27,31,36,50,73,95,120,157,168,176,182,189],
'MCPFirst':[5,7,10,16,23,28,34,41,47,53,66],
'MCPThird':[3,5,6,9,14,21,32,40,47,51],
'MCPFifth':[4,5,7,10,15,22,33,43,47,51],
'PIPFirst':[6,7,8,11,17,26,32,38,45,53,60,67],
'PIPThird':[3,5,7,9,15,20,25,29,35,41,46,51],
'PIPFifth':[4,5,7,11,18,21,25,29,34,40,45,50],
'MIPThird':[4,5,7,10,16,21,25,29,35,43,46,51],
'MIPFifth':[3,5,7,12,19,23,27,32,35,39,43,49],
'DIPFirst':[5,6,8,10,20,31,38,44,45,52,67],
'DIPThird':[3,5,7,10,16,24,30,33,36,39,49],
'DIPFifth':[5,6,7,11,18,25,29,33,35,39,49]
},
'boy':{
'Radius':[8,11,15,18,31,46,76,118,135,171,188,197,201,209],
'Ulna':[25,30,35,43,61,80,116,157,168,180,187,194],
'MCPFirst':[4,5,8,16,22,26,34,39,45,52,66],
'MCPThird':[3,4,5,8,13,19,30,38,44,51],
'MCPFifth':[3,4,6,9,14,19,31,41,46,50],
'PIPFirst':[4,5,7,11,17,23,29,36,44,52,59,66],
'PIPThird':[3,4,5,8,14,19,23,28,34,40,45,50],
'PIPFifth':[3,4,6,10,16,19,24,28,33,40,44,50],
'MIPThird':[3,4,5,9,14,18,23,28,35,42,45,50],
'MIPFifth':[3,4,6,11,17,21,26,31,36,40,43,49],
'DIPFirst':[4,5,6,9,19,28,36,43,46,51,67],
'DIPThird':[3,4,5,9,15,23,29,33,37,40,49],
'DIPFifth':[3,4,6,11,17,23,29,32,36,40,49]
}
}
核心计算逻辑实现如下:
def boneAge(score, sex):
if sex == 'boy':
boneAge = 2.01790023656577 + (-0.0931820870747269)*score + math.pow(score,2)*0.00334709095418796 +\
math.pow(score,3)*(-3.32988302362153E-05) + math.pow(score,4)*(1.75712910819776E-07) +\
math.pow(score,5)*(-5.59998691223273E-10) + math.pow(score,6)*(1.1296711294933E-12) +\
math.pow(score,7)* (-1.45218037113138e-15) +math.pow(score,8)* (1.15333377080353e-18) +\
math.pow(score,9)*(-5.15887481551927e-22) +math.pow(score,10)* (9.94098428102335e-26)
return round(boneAge,2)
elif sex == 'girl':
boneAge = 5.81191794824917 + (-0.271546561737745)*score + \
math.pow(score,2)*0.00526301486340724 + math.pow(score,3)*(-4.37797717401925E-05) +\
math.pow(score,4)*(2.0858722025667E-07) +math.pow(score,5)*(-6.21879866563429E-10) + \
math.pow(score,6)*(1.19909931745368E-12) +math.pow(score,7)* (-1.49462900826936E-15) +\
math.pow(score,8)* (1.162435538672E-18) +math.pow(score,9)*(-5.12713017846218E-22) +\
math.pow(score,10)* (9.78989966891478E-26)
return round(boneAge,2)
跟实际的生产业务相结合的实践研究我觉得还是比较有潜力的,感兴趣的话也都可以自行动手尝试下!