第一课第三周大作业--mri脑肿瘤自动分割教程(代码片段)

Tina姐 Tina姐     2022-12-15     165

关键词:


作业文件

吴恩达-医学图像AI 专项课程-作业/第一课/第一课大作业/week3seg/C1M3_Assignmentlast.ipynb

欢迎来到“医学人工智能”课程 1 的最后一部分!

您将学习如何使用 MRI数据构建神经网络以自动分割大脑中的肿瘤区域。

你将学习:

  • MR 图像中有什么
  • MRI 数据集的标准数据准备技术
  • 分割的度量和损失函数
  • 可视化和评估分割模型

作业目录

2.1 MRI data

在这个作业中,我们将构建一个多类分割模型。我们将在每个图像中识别 3 种不同的异常:水肿、非增强肿瘤和增强肿瘤。

可以使用ITK-SNAP打开来看一下图像和标签

使用的MRI数据来自

Medical Segmentation Decathlon

这是一个极其丰富的数据集,提供 3D 表示中每个点(体素)相关联的标签。

2.2 探索数据

我们的数据集以NifTI-1 格式存储,我们将使用 NiBabel库与文件进行交互。每个训练样本由两个单独的文件组成:

第一个文件是一个图像文件,包含形状为 (240, 240, 155, 4) 的 MR 图像的 4D 阵列。

  • 前 3 个维度是 3D 体积中每个点的 X、Y 和 Z 值,通常称为体素。
  • 第 4 维是 4 个不同序列的值
    • 0:FLAIR序列:“流体衰减反转恢复”(FLAIR)
    • 1:T1w:“T1 加权”
    • 2:t1gd:“T1 加权钆对比度增强”(T1-Gd)
    • 3:T2w:“T2 加权”

每个训练示例中的第二个文件是一个标签文件,其中包含一个形状为 (240, 240, 155) 的 3D 数组。

  • 此数组中的整数值表示相应图像文件中每个体素的“标签”:
    • 0:背景
    • 1:水肿
    • 2:非强化肿瘤
    • 3:增强肿瘤

我们可以访问总共 484 张训练图像,我们将这些图像分成训练 (80%) 和验证 (20%) 数据集。

让我们从查看一个案例并可视化数据开始!您可以通过此笔记本访问 10 个不同的案例,我们强烈建议您自行进一步探索数据。

分别按横断位、冠状位、矢状位显示图像,并且把标签显示在图像上

image, label = load_case(DATA_DIR + "imagesTr/BRATS_003.nii.gz", DATA_DIR + "labelsTr/BRATS_003.nii.gz")
image = util.get_labeled_image(image, label)

util.plot_image_grid(image)

我们还编写了一个实用函数,该函数生成一个GIF,显示在每个轴上迭代的效果

image, label = load_case(DATA_DIR + "imagesTr/BRATS_003.nii.gz", DATA_DIR + "labelsTr/BRATS_003.nii.gz")
util.visualize_data_gif(util.get_labeled_image(image, label))

2.4 使用 patch 进行数据预处理

虽然我们的数据集是在配准后以 NIfTI 格式提供给我们的,但在将数据提供给我们的模型之前,我们仍然需要做一些小的预处理。

生成子体积(sub-volumes)

我们将首先生成数据的“patch 补丁”,您可以将其视为整个 MR 图像的子体积。

我们生成补丁的原因是因为原始图像太大,容易超出内存。

因此,我们将使用这种通用技术来生成空间一致的数据子体积,这些子体积可以输入到我们的网络中。

具体来说,我们将从我们的图像中生成形状为 [160, 160, 16] 的随机采样子体积。

此外,由于随机采样子体积,可能会导致大部分子体积只是脑组织或黑色背景,没有任何肿瘤,我们希望生成的子体积中至少包含一定数量肿瘤体素。

因此,我们将只选择具有最多 95% 非肿瘤区域(或至少 5% 肿瘤)的补丁(这是生成的子体积中,肿瘤和非肿瘤体素的占比

我们通过根据背景标签中存在的值过滤来做到这一点。

def get_sub_volume(image, label, 
                   orig_x = 240, orig_y = 240, orig_z = 155, 
                   output_x = 160, output_y = 160, output_z = 16,
                   num_classes = 4, max_tries = 1000, 
                   background_threshold=0.95):
    """
    Extract random sub-volume from original images.

    Args:
        image (np.array): original image, 
            of shape (orig_x, orig_y, orig_z, num_channels)
        label (np.array): original label. 
            labels coded using discrete values rather than
            a separate dimension, 
            so this is of shape (orig_x, orig_y, orig_z)
        orig_x (int): x_dim of input image
        orig_y (int): y_dim of input image
        orig_z (int): z_dim of input image
        output_x (int): desired x_dim of output
        output_y (int): desired y_dim of output
        output_z (int): desired z_dim of output
        num_classes (int): number of class labels
        max_tries (int): maximum trials to do when sampling
        background_threshold (float): limit on the fraction 
            of the sample which can be the background

    returns:
        X (np.array): sample of original image of dimension 
            (num_channels, output_x, output_y, output_z)
        y (np.array): labels which correspond to X, of dimension 
            (num_classes, output_x, output_y, output_z)
    """
    # Initialize features and labels with `None`
    X = None
    y = None

    ### START CODE HERE (REPLACE INSTANCES OF 'None' with your code) ###
    
    tries = 0
    
    while tries < max_tries:
        # randomly sample sub-volume by sampling the corner voxel
        # hint: make sure to leave enough room for the output dimensions!
        start_x = np.random.randint(0,orig_x - output_x + 1)
        start_y = np.random.randint(0,orig_y - output_y + 1)
        start_z = np.random.randint(0,orig_z - output_z + 1)

        # extract relevant area of label
        y = label[start_x: start_x + output_x,
                  start_y: start_y + output_y,
                  start_z: start_z + output_z]
        
        # One-hot encode the categories.
        # This adds a 4th dimension, 'num_classes'
        # (output_x, output_y, output_z, num_classes)
        y = keras.utils.to_categorical(y,num_classes= num_classes)


        # compute the background ratio
        bgrd_ratio = np.sum(y[:, :, :, 0])/(output_x * output_y * output_z)

        # increment tries counter
        tries += 1

        # if background ratio is below the desired threshold,
        # use that sub-volume.
        # otherwise continue the loop and try another random sub-volume
        if bgrd_ratio < background_threshold:

            # make copy of the sub-volume
            X = np.copy(image[start_x: start_x + output_x,
                              start_y: start_y + output_y,
                              start_z: start_z + output_z, :])
            
            # change dimension of X
            # from (x_dim, y_dim, z_dim, num_channels)
            # to (num_channels, x_dim, y_dim, z_dim)
            X = np.moveaxis(X,3,0)

            # change dimension of y
            # from (x_dim, y_dim, z_dim, num_classes)
            # to (num_classes, x_dim, y_dim, z_dim)
            y = np.moveaxis(y,3,0)


            ### END CODE HERE ###
            
            # take a subset of y that excludes the background class
            # in the 'num_classes' dimension
            y = y[1:, :, :, :]
    
            return X, y

    # if we've tried max_tries number of samples
    # Give up in order to avoid looping forever.
    print(f"Tried tries times to find a sub-volume. Giving up...")

标准化

最后,鉴于 MR 图像中的值涵盖的范围非常广泛,我们将这些值标准化为均值为 0,标准差为 1。

这是深度图像处理中的常用技术,因为标准化使网络更容易学习。

# UNQ_C2 (UNIQUE CELL IDENTIFIER, DO NOT EDIT)
def standardize(image):
    """
    Standardize mean and standard deviation 
        of each channel and z_dimension.

    Args:
        image (np.array): input image, 
            shape (num_channels, dim_x, dim_y, dim_z)

    Returns:
        standardized_image (np.array): standardized version of input image
    """
    
    ### START CODE HERE (REPLACE INSTANCES OF 'None' with your code) ###
    
    # initialize to array of zeros, with same shape as the image
    standardized_image = np.zeros(image.shape)

    # iterate over channels
    for c in range(image.shape[0]):
        # iterate over the `z` dimension
        for z in range(image.shape[3]):
            # get a slice of the image 
            # at channel c and z-th dimension `z`
            image_slice = image[c,:,:,z]

            # subtract the mean from image_slice
            mean=np.mean(image_slice)
            centered = image_slice-mean
            
            # divide by the standard deviation (only if it is different from zero)
            std=np.std(centered)
            if std != 0:
                centered_scaled = centered/std

            # update  the slice of standardized image
            # with the scaled centered and scaled image
            standardized_image[c, :, :, z] = centered_scaled

    ### END CODE HERE ###

    return standardized_image

3D U-Net

模型使用的是3D unet

4.1 骰子相似系数

除了架构之外,任何深度学习方法中最重要的元素之一就是损失函数的选择。

您可能熟悉的一个自然选择是交叉熵损失函数。

然而,由于严重的类别不平衡(通常没有很多正区域),这种损失函数对于分割任务并不理想。

分割任务的一个更常见的损失是 Dice 相似系数,它是衡量两个轮廓重叠程度的指标。

Dice = 0 表示完全不匹, Dice =1 表示完美匹配 。

DSC ( A , B ) = 2 × ∣ A ∩ B ∣ ∣ A ∣ + ∣ B ∣ . \\textDSC(A, B) = \\frac2 \\times |A \\cap B||A| + |B|. DSC(A,B)=A+B2×AB.
这里我们可以解读 𝐴 和 𝐵 作为体素集,𝐴 作为预测的肿瘤区域和 𝐵 是 ground truth。

我们的模型会将每个体素映射到 0 或 1

  • 0 表示它是背景体素
  • 1 表示它是分割区域的一部分。

DSC ( f , x , y ) = 2 × ∑ i , j f ( x ) i j × y i j + ϵ ∑ i , j f ( x ) i j + ∑ i , j y i j + ϵ \\textDSC(f, x, y) = \\frac2 \\times \\sum_i, j f(x)_ij \\times y_ij + \\epsilon\\sum_i,j f(x)_ij + \\sum_i, j y_ij + \\epsilon DSC(f,x,y)=i,jf(x)ij+i,jyij+ϵ2×i,jf(x)ij×yij+ϵ

  • x x x : the input image
  • f ( x ) f(x) f(x) : the model output (prediction)
  • y y y : the label (actual ground truth)
  • ϵ \\epsilon ϵ : 是为了避免被零除而添加的小数
def single_class_dice_coefficient(y_true, y_pred, axis=(0, 1, 2), 
                                  epsilon=0.00001):
    ### START CODE HERE (REPLACE INSTANCES OF 'None' with your code) ###
    
    dice_numerator = 2 * K.sum(y_true * y_pred) + epsilon
    dice_denominator = K.sum(y_true) + K.sum(y_pred) + epsilon
    dice_coefficient = dice_numerator / dice_denominator
    return dice_coefficient

4.2 soft dice loss

L D i c e ( p , q ) = 1 − 2 × ∑ i , j p i j q i j + ϵ ( ∑ i , j p i j 2 ) + ( ∑ i , j q i j 2 ) + ϵ \\mathcalL_Dice(p, q) = 1 - \\frac2\\times\\sum_i, j p_ijq_ij + \\epsilon\\left(\\sum_i, j p_ij^2 \\right) + \\left(\\sum_i, j q_ij^2 \\right) + \\epsilon LDice(p,q)=1(i,jpij2)+(i,jqij2)+ϵ2×i,jpijqij+ϵ

  • p p p is our predictions
  • q q q is the ground truth
  • q i q_i qi 是 0 or 1.

soft dice loss 就是 1-DSC

如果是多类别,就把每个类别的 DSC 累加起来:
L D i c e ( p , q ) = 1 − 1 N ∑ c = 1 C 2 × ∑ i , j p c i j q c i j + ϵ ( ∑ i , j p c i j 2 ) + ( ∑ i , j q c i j 2 ) + ϵ \\mathcalL_Dice(p, q) = 1 - \\frac1N \\sum_c=1^C \\frac2\\times\\sum_i, j p_cijq_cij + \\epsilon\\left(\\sum_i, j p_cij^2 \\right) + \\left(\\sum_i, j q_cij^2 \\right) + \\epsilon LDice(p,q)=1N1c=1C(i,j查看详情

第一课第三周5-6节-数据增强以及dice损失函数

既然我们已经讨论了分割体系结构,那么让我们来讨论一种可以应用于这种模型训练的技术,即数据扩充(数据增强dataaugumentation)。我们在前面学过,我们可以对输入胸部X光片的变换,这样每个例子的分... 查看详情

收藏第一课第二周作业-学会计算分类各种指标-超详细教程(代码片段)

本次作业文件:在第一课/第一课大作业/week2metric这节课不需要对模型进行预测,所有的预测结果已经在csv文件中给出。作为提醒,我们的数据集包含14种不同情况的X射线,可通过X射线诊断。我们将使用我们在这... 查看详情

第一课第一周大作业-胸部14种疾病分类-代码详解(代码片段)

深度学习胸部X射线诊断本次作业文件:在第一课/第一课大作业/week1classification欢迎来到课程1的第一个作业!在这个任务中!您将通过使用Keras构建最先进的胸部X射线分类器来探索医学图像诊断。你将学会一下内容... 查看详情

第一课第三周7-10节-ai算法这么棒,为什么我们周围的医院没有使用这些系统?

既然您已经了解了医学成像的分类和分割模型,并且您已经在前面构建了您的胸部x射线分类模型,那么您可能会想知道为什么我们周围的医院或诊所没有使用这些系统。在本课中,您将了解一些挑战和机会,使这... 查看详情

吴恩达实验(神经网络和深度学习)第一课第三周,代码和数据集,亲测可运行

代码和数据集已上传到文件中应该可以直接下载吧(第一次上传文件,感觉是),解压后把文件夹拷贝到jupyter工作空间即可注:我对下载的代码的格式稍作了修改,原来定义函数与调用函数在两个单元格里,我直接运行他总给... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周6-10节总结+作业解读(代码片段)

...学习在医学图像分类问题上的一些前沿应用。本文将介绍第一课第一周6-10节的内容。主要讲解构建一个分类模型去识别胸片的肿块。以及分类模型将面临的三个挑战:类不平衡挑战、多任务挑战和数据集大小挑战。本节重点... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周16-18节-如何确保数据集病人不重叠+作业解说(代码片段)

模型测试既然你已经了解了如何训练医学诊断模型,那么让我们来谈谈如何测试这样的模型。接下来你会学习如何测试这样的一个模型。您将学习如何正确使用训练、验证和测试集。以及为了评估你的模型需要强大的groundtrut... 查看详情

第二课第一周大作业--构建和评估一个线性风险模型(代码片段)

之前教程:第二课第一周第1节-AI用于医学预后简介第二课第一周第2节-做医学预后,你需要掌握什么?第二课第一周第3-4节-什么是预后?第二课第一周第4-7节医学预后案例欣赏+作业解析第二课第一周第8节风险得分... 查看详情

第二课第一周大作业--构建和评估一个线性风险模型(代码片段)

之前教程:第二课第一周第1节-AI用于医学预后简介第二课第一周第2节-做医学预后,你需要掌握什么?第二课第一周第3-4节-什么是预后?第二课第一周第4-7节医学预后案例欣赏+作业解析第二课第一周第8节风险得分... 查看详情

第二课第一周大作业--构建和评估一个线性风险模型(代码片段)

之前教程:第二课第一周第1节-AI用于医学预后简介第二课第一周第2节-做医学预后,你需要掌握什么?第二课第一周第3-4节-什么是预后?第二课第一周第4-7节医学预后案例欣赏+作业解析第二课第一周第8节风险得分... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周11节总结(代码片段)

回顾一下医学图像深度学习面临的三个挑战三个挑战我们将讨论医学图像训练算法的三个关键挑战:类不平衡挑战、多任务挑战和数据集大小挑战。对于每一个挑战,我们将介绍一到两种应对方法。类别不平衡:可以... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周19-20节(代码片段)

...们回顾一下上一节提出的医学图像创建数据集的三个挑战第一个挑战涉及到我们如何使这些测试集独立第二个挑战涉及我们如何对它们进行采样第三个挑战涉及我们如何设置groundtruth让我们来讨论第二个挑战:集合抽样。sampl... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周13-15节-迁移学习+数据增强

回顾一下医学图像深度学习面临的三个挑战三个挑战我们将讨论医学图像训练算法的三个关键挑战:类不平衡挑战、多任务挑战和数据集大小挑战。对于每一个挑战,我们将介绍一到两种应对方法。类别不平衡:可以... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周1-3节总结(代码片段)

点此了解课程吴恩达新课医学图像AI(AIforMedicine)专项课程推荐欢迎来到医学人工智能专业。如果你已经完成了深度学习专业化或机器学习课程,并且你正在寻找更深入掌握人工智能的应用领域,这是一个很好的... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周4-5节总结(代码片段)

4-5节主要是例举了三个深度学习医学诊断案例。本周,我们将直接进入建立一个深度学习模型的任务胸部x光分类。通过这个例子,您将学到的许多想法在许多医学成像测试中都有广泛的应用。本周,我们将从三个医... 查看详情

吴恩达-医学图像人工智能专项课程-第一课第一周1-3节(代码片段)

欢迎来到医学人工智能专业。如果你已经完成了深度学习专业化或机器学习课程,并且你正在寻找更深入掌握人工智能的应用领域,这是一个很好的专业化学习。要想成为真正优秀的机器学习,最重要的事情之一就是... 查看详情

脑肿瘤检测基于matlabguisom脑肿瘤检测含matlab源码2322期(代码片段)

⛄一、脑肿瘤计算机辅助检测方法概述尽管目前报道的脑肿瘤CAD检测方法有所不同,但基本都遵循如下四个步骤:(1)MRI脑部图像的预处理。预处理主要包括图像增强、去噪、脑实质分割[4]及多模态MRI图像的配准等[5]。(2)感兴趣区域... 查看详情