Esempio n. 1
0
def test_unet2d_encode(num_patches: int,
                       num_channels: int,
                       num_output_channels: int,
                       is_downsampling: bool,
                       image_shape: TupleInt2) -> None:
    """
    Test if the Encode block of a Unet3D correctly works when passing in kernels that only operate in X and Y.
    """
    set_random_seed(1234)
    layer = UNet3D.UNetEncodeBlock((num_channels, num_output_channels),
                                   kernel_size=(1, 3, 3),
                                   downsampling_stride=(1, 2, 2) if is_downsampling else 1)
    input_shape = (num_patches, num_channels) + (1,) + image_shape
    input = torch.rand(*input_shape).float()
    output = layer(input)

    def output_image_size(input_image_size: int) -> int:
        # If max pooling is added, it is done with a kernel size of 2, shrinking the image by a factor of 2
        image_shrink_factor = 2 if is_downsampling else 1
        return input_image_size // image_shrink_factor

    # Expected output shape:
    # The first dimension (patches) should be retained unchanged.
    # We should get as many output channels as requested
    # Unet is defined as running over degenerate 3D images with Z=1, this should be preserved.
    # The two trailing dimensions are the adjusted image dimensions
    expected_output_shape = (num_patches, num_output_channels, 1,
                             output_image_size(image_shape[0]), output_image_size(image_shape[1]))
    assert output.shape == expected_output_shape
Esempio n. 2
0
def test_unet3d_upsampling() -> None:
    assert get_upsampling_kernel_size(2, 3) == (4, 4, 4)
    assert get_upsampling_kernel_size(1, 3) == (1, 1, 1)
    assert get_upsampling_kernel_size((1, 2, 2), 3) == (1, 4, 4)
    assert get_upsampling_kernel_size((1, 2, 3), 3) == (1, 4, 6)
    with pytest.raises(ValueError):
        get_upsampling_kernel_size((2, 2), 3)
    with pytest.raises(ValueError):
        get_upsampling_kernel_size(0, 3)
    # Test method as integrated into the constructor
    assert UNet3D(1, 1, 1, kernel_size=3, downsampling_factor=(1, 2, 2)).upsampling_kernel_size == (1, 4, 4)
def test_unet_summary_generation() -> None:
    """Checks unet summary generation works either in CPU or GPU"""
    model = UNet3D(input_image_channels=1,
                   initial_feature_channels=2,
                   num_classes=2,
                   kernel_size=1,
                   num_downsampling_paths=2)
    if machine_has_gpu:
        model.cuda()
    summary = ModelSummary(model=model).generate_summary(input_sizes=[(1, 4, 4, 4)])
    assert summary is not None
Esempio n. 4
0
def build_net(args: SegmentationModelBase) -> BaseSegmentationModel:
    """
    Build network architectures

    :param args: Network configuration arguments
    """
    full_channels_list = [
        args.number_of_image_channels, *args.feature_channels,
        args.number_of_classes
    ]
    initial_fcn = [BasicLayer] * 2
    residual_blocks = [[BasicLayer, BasicLayer]] * 3
    basic_network_definition = initial_fcn + residual_blocks  # type: ignore
    # no dilation for the initial FCN and then a constant 1 neighbourhood dilation for the rest residual blocks
    basic_dilations = [1] * len(initial_fcn) + [2, 2] * len(
        basic_network_definition)
    # Crop size must be at least 29 because all architectures (apart from UNets) shrink the input image by 28
    crop_size_constraints = CropSizeConstraints(
        minimum_size=basic_size_shrinkage + 1)
    run_weight_initialization = True

    network: BaseSegmentationModel
    if args.architecture == ModelArchitectureConfig.Basic:
        network_definition = basic_network_definition
        network = ComplexModel(args, full_channels_list, basic_dilations,
                               network_definition,
                               crop_size_constraints)  # type: ignore

    elif args.architecture == ModelArchitectureConfig.UNet3D:
        network = UNet3D(input_image_channels=args.number_of_image_channels,
                         initial_feature_channels=args.feature_channels[0],
                         num_classes=args.number_of_classes,
                         kernel_size=args.kernel_size,
                         num_downsampling_paths=args.num_downsampling_paths)
        run_weight_initialization = False

    elif args.architecture == ModelArchitectureConfig.UNet2D:
        network = UNet2D(input_image_channels=args.number_of_image_channels,
                         initial_feature_channels=args.feature_channels[0],
                         num_classes=args.number_of_classes,
                         padding_mode=PaddingMode.Edge,
                         num_downsampling_paths=args.num_downsampling_paths)
        run_weight_initialization = False

    else:
        raise ValueError(f"Unknown model architecture {args.architecture}")
    network.validate_crop_size(args.crop_size, "Training crop size")
    network.validate_crop_size(args.test_crop_size,
                               "Test crop size")  # type: ignore
    # Initialize network weights
    if run_weight_initialization:
        network.apply(init_weights)  # type: ignore
    return network
Esempio n. 5
0
def crop_size_multiple_unet3d(crop_size: TupleInt3,
                              downsampling_factor: IntOrTuple3,
                              num_downsampling_paths: int,
                              expected_crop_size_multiple: TupleInt3) -> None:
    model = UNet3D(name="",
                   input_image_channels=1,
                   initial_feature_channels=32,
                   num_classes=2,
                   kernel_size=3,
                   downsampling_factor=downsampling_factor,
                   num_downsampling_paths=num_downsampling_paths)
    assert model.crop_size_constraints.multiple_of == expected_crop_size_multiple
    model.validate_crop_size(crop_size)
Esempio n. 6
0
 def create_encoder(self, channels: List[int]) -> ModuleList:
     """
     Create an image encoder network.
     """
     layers = []
     for i in range(len(channels) - 1):
         layers.append(
             UNet3D.UNetEncodeBlock(
                 channels=(channels[i], channels[i + 1]),
                 kernel_size=self.kernel_size_per_encoding_block[i],
                 downsampling_stride=self.stride_size_per_encoding_block[i],
                 padding_mode=self.padding_mode,
                 use_residual=False,
                 depth=i,
             ))
     return ModuleList(layers)
def test_unet_model_parallel() -> None:
    """Checks model parallel utilises all the available GPU devices for forward pass"""
    if no_gpu_available:
        logging.warning("CUDA capable GPU is not available - UNet Model Parallel cannot be tested")
        return
    model = UNet3D(input_image_channels=1,
                   initial_feature_channels=2,
                   num_classes=2,
                   kernel_size=1,
                   num_downsampling_paths=2).cuda()
    # Partition the network across all available gpu
    available_devices = [torch.device('cuda:{}'.format(ii)) for ii in range(torch.cuda.device_count())]
    model.generate_model_summary()
    model.partition_model(devices=available_devices)

    # Verify that all the devices are utilised by the layers of the model
    summary = ModelSummary(model=model).generate_summary(input_sizes=[(1, 4, 4, 4)])
    layer_devices = set()
    for layer_summary in summary.values():
        if layer_summary.device:
            layer_devices.add(layer_summary.device)

    assert layer_devices == set(available_devices)