From 1bbe3787030201e98fdc99a072c294cd453302d6 Mon Sep 17 00:00:00 2001 From: Jacob Reinhold <5241441+jcreinhold@users.noreply.github.com> Date: Mon, 10 Jan 2022 20:19:04 -0500 Subject: [PATCH 1/7] WIP: add preliminary functionality for static upsampling (for onnx export) --- pyproject.toml | 1 - tiramisu_brulee/experiment/cli/to_onnx.py | 45 ++++++- tiramisu_brulee/model/dense.py | 150 +++++++++++++++++----- tiramisu_brulee/model/tiramisu.py | 17 ++- 4 files changed, 172 insertions(+), 41 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5641f86..a20fee5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,4 +7,3 @@ profile = "black" [tool.bandit] skips = ["B101"] - diff --git a/tiramisu_brulee/experiment/cli/to_onnx.py b/tiramisu_brulee/experiment/cli/to_onnx.py index 0773993..af7468a 100644 --- a/tiramisu_brulee/experiment/cli/to_onnx.py +++ b/tiramisu_brulee/experiment/cli/to_onnx.py @@ -126,6 +126,29 @@ def arg_parser() -> ArgParser: action="store_true", help="don't add metadata", ) + parser.add_argument( + "--no-dynamic-batch", + action="store_true", + help="don't use dynamic batches", + ) + parser.add_argument( + "--no-dynamic-shape", + action="store_true", + help="don't use dynamic shapes", + ) + parser.add_argument( + "--batch-size", + default=1, + type=int, + help="input batch size (important if no-dynamic-batch enabled)", + ) + parser.add_argument( + "--image-shape", + default=None, + nargs="+", + type=int, + help="input image shape (important if no-dynamic-shape enabled)", + ), parser.add_argument( "-v", "--verbosity", @@ -176,11 +199,24 @@ def to_onnx(args: ArgType = None) -> builtins.int: parameters_to_prune, prune.L1Unstructured, amount=args.prune_amount ) n_channels = n_inputs if p3d is None else (args.pseudo3d_size * n_inputs) - input_shape = (1, n_channels) + (128,) * (3 if p3d is None else 2) + if args.image_shape is None: + image_shape = (128,) * (3 if p3d is None else 2) + else: + image_shape = args.image_shape + input_shape = (args.batch_size, n_channels) + tuple(image_shape) + logger.debug(f"Input shape: {input_shape}") input_sample = torch.randn(input_shape) - axes = {0: "batch_size", 2: "h", 3: "w"} - if p3d is None: + axes = dict() + if not args.no_dynamic_batch: + axes.update({0: "batch_size"}) + if not args.no_dynamic_shape: + axes.update({2: "h", 3: "w"}) + if not args.no_dynamic_shape and p3d is None: axes.update({4: "d"}) + if not args.no_dynamic_batch or not args.no_dynamic_shape: + dynamic_axes = {"input": axes, "output": axes} + else: + dynamic_axes = None with tempfile.NamedTemporaryFile("w") as f: save_as_ort = str(onnx_path).endswith(".ort") file_path = f.name if save_as_ort else onnx_path @@ -193,7 +229,8 @@ def to_onnx(args: ArgType = None) -> builtins.int: do_constant_folding=args.do_constant_folding, input_names=["input"], output_names=["output"], - dynamic_axes={"input": axes, "output": axes}, + dynamic_axes=dynamic_axes, + operator_export_type=torch.onnx.OperatorExportTypes.ONNX, ) logger.info("Exporting model to ONNX" + nth_model) if args.verbosity >= 3: diff --git a/tiramisu_brulee/model/dense.py b/tiramisu_brulee/model/dense.py index 0bf4050..2cd06a6 100644 --- a/tiramisu_brulee/model/dense.py +++ b/tiramisu_brulee/model/dense.py @@ -16,11 +16,13 @@ ] import builtins +import enum import functools import typing import torch import torch.nn as nn +import torch.nn.functional as F ACTIVATION = functools.partial(nn.ReLU, inplace=True) @@ -142,13 +144,13 @@ def forward(self, tensor: torch.Tensor) -> torch.Tensor: # concatenation is done on the channel axis (i.e., 1) for layer in self.layers: out = layer(tensor) - tensor = torch.cat([tensor, out], 1) + tensor = torch.cat((tensor, out), 1) new_features.append(out) return torch.cat(new_features, 1) else: for layer in self.layers: out = layer(tensor) - tensor = torch.cat([tensor, out], 1) + tensor = torch.cat((tensor, out), 1) return tensor @property @@ -183,72 +185,150 @@ class TransitionDown3d(ConvLayer): _pad = nn.ReplicationPad3d +class ResizeMethod(enum.IntEnum): + CROP: builtins.int = 0 + INTERPOLATE: builtins.int = 1 + + class TransitionUp(nn.Module): - _conv_trans: typing.Union[ - typing.Type[nn.ConvTranspose2d], typing.Type[nn.ConvTranspose3d] + _conv: typing.ClassVar[typing.Union[typing.Type[nn.Conv2d], typing.Type[nn.Conv3d]]] + _conv_trans: typing.ClassVar[ + typing.Union[typing.Type[nn.ConvTranspose2d], typing.Type[nn.ConvTranspose3d]] ] - _kernel_size: typing.Union[ - typing.Tuple[builtins.int, builtins.int], - typing.Tuple[builtins.int, builtins.int, builtins.int], + _kernel_size: typing.ClassVar[ + typing.Union[ + typing.Tuple[builtins.int, builtins.int], + typing.Tuple[builtins.int, builtins.int, builtins.int], + ] ] - _stride: typing.Union[ - typing.Tuple[builtins.int, builtins.int], - typing.Tuple[builtins.int, builtins.int, builtins.int], + _stride: typing.ClassVar[ + typing.Union[ + typing.Tuple[builtins.int, builtins.int], + typing.Tuple[builtins.int, builtins.int, builtins.int], + ] ] + _interp_mode: typing.ClassVar[builtins.str] - def __init__(self, *, in_channels: builtins.int, out_channels: builtins.int): + @typing.no_type_check + def __init__( + self, + *, + in_channels: builtins.int, + out_channels: builtins.int, + resize_method: ResizeMethod = ResizeMethod.CROP, + resize_shape: typing.Optional[typing.Tuple[builtins.int, ...]] = None, + static: builtins.bool = False, + use_conv_transpose: builtins.bool = True, + ): super().__init__() - self.conv_trans = self._conv_trans( + self.resize_shape = resize_shape + conv_base_kwargs = dict( in_channels=in_channels, out_channels=out_channels, - kernel_size=self._kernel_size, # type: ignore[arg-type] - stride=self._stride, # type: ignore[arg-type] + kernel_size=self._kernel_size, bias=False, ) + _conv_kwargs = conv_base_kwargs.copy() + _conv_kwargs["padding"] = 1 + _conv_trans_kwargs = conv_base_kwargs.copy() + _conv_trans_kwargs["stride"] = self._stride + conv_creator = self._conv_trans if use_conv_transpose else self._conv + conv_kwargs = _conv_trans_kwargs if use_conv_transpose else _conv_kwargs + self.conv: typing.Union[ + nn.Conv2d, nn.Conv3d, nn.ConvTranspose2d, nn.ConvTranspose3d + ] + self.resize: typing.Optional[typing.Callable[[torch.Tensor, ...], torch.Tensor]] + if resize_method == ResizeMethod.CROP: + self.conv = conv_creator(**conv_kwargs) + self.resize = self._crop_to_target + self.forward = self._forward_dynamic + elif resize_method == ResizeMethod.INTERPOLATE and not static: + self.conv = conv_creator(**conv_kwargs) + self.resize = self._interpolate_to_target + self.forward = self._forward_dynamic + elif resize_method == ResizeMethod.INTERPOLATE and static: + self.conv = conv_creator(**conv_kwargs) + self.resize = None + self.forward = self._forward_static + else: + msg = f"resize_method needs to be a ResizeMethod. Got {resize_method}" + raise ValueError(msg) + + @typing.no_type_check + def _forward_dynamic( + self, tensor: torch.Tensor, *, skip: torch.Tensor + ) -> torch.Tensor: + out: torch.Tensor = self.conv(tensor) + out = self.resize(out, target=skip) + out = torch.cat((out, skip), 1) + return out - def forward(self, tensor: torch.Tensor, *, skip: torch.Tensor) -> torch.Tensor: - out: torch.Tensor = self.conv_trans(tensor) - out = self._crop_to_target(out, target=skip) - out = torch.cat([out, skip], 1) + @typing.no_type_check + def _forward_static( + self, tensor: torch.Tensor, *, skip: torch.Tensor + ) -> torch.Tensor: + out: torch.Tensor = self.conv(tensor) + out = F.interpolate( + out, align_corners=True, mode=self._interp_mode, scale_factor=2.0 + ) + out = torch.cat((out, skip), 1) return out - @staticmethod - def _crop_to_target(tensor: torch.Tensor, *, target: torch.Tensor) -> torch.Tensor: + def _crop_to_target( + self, tensor: torch.Tensor, *, target: torch.Tensor + ) -> torch.Tensor: raise NotImplementedError + @typing.no_type_check + def _interpolate_to_target( + self, tensor: torch.Tensor, *, target: torch.Tensor + ) -> torch.Tensor: + return F.interpolate( + tensor, size=target.shape[2:], mode=self._interp_mode, align_corners=True + ) + class TransitionUp2d(TransitionUp): + _conv = nn.Conv2d _conv_trans = nn.ConvTranspose2d _kernel_size = (3, 3) _stride = (2, 2) - - @staticmethod - def _crop_to_target(tensor: torch.Tensor, *, target: torch.Tensor) -> torch.Tensor: - _, _, max_height, max_width = target.shape + _interp_mode = "bilinear" + + @typing.no_type_check + def _crop_to_target( + self, tensor: torch.Tensor, *, target: torch.Tensor + ) -> torch.Tensor: + if self.resize_shape is None: + _, _, max_height, max_width = target.shape + else: + max_height, max_width = self.resize_shape _, _, _h, _w = tensor.size() h = torch.div(_h - max_height, 2, rounding_mode="trunc") w = torch.div(_w - max_width, 2, rounding_mode="trunc") - hs = slice(h, h + max_height) - ws = slice(w, w + max_width) - return tensor[:, :, hs, ws] + return tensor[:, :, h : h + max_height, w : w + max_width] class TransitionUp3d(TransitionUp): + _conv = nn.Conv3d _conv_trans = nn.ConvTranspose3d _kernel_size = (3, 3, 3) _stride = (2, 2, 2) - - @staticmethod - def _crop_to_target(tensor: torch.Tensor, *, target: torch.Tensor) -> torch.Tensor: - _, _, max_height, max_width, max_depth = target.shape + _interp_mode = "trilinear" + + @typing.no_type_check + def _crop_to_target( + self, tensor: torch.Tensor, *, target: torch.Tensor + ) -> torch.Tensor: + if self.resize_shape is None: + _, _, max_height, max_width, max_depth = target.shape + else: + max_height, max_width, max_depth = self.resize_shape _, _, _h, _w, _d = tensor.size() h = torch.div(_h - max_height, 2, rounding_mode="trunc") w = torch.div(_w - max_width, 2, rounding_mode="trunc") d = torch.div(_d - max_depth, 2, rounding_mode="trunc") - hs = slice(h, h + max_height) - ws = slice(w, w + max_width) - ds = slice(d, d + max_depth) - return tensor[:, :, hs, ws, ds] + return tensor[:, :, h : h + max_height, w : w + max_width, d : d + max_depth] class Bottleneck(nn.Sequential): diff --git a/tiramisu_brulee/model/tiramisu.py b/tiramisu_brulee/model/tiramisu.py index 9c95c34..dff9243 100644 --- a/tiramisu_brulee/model/tiramisu.py +++ b/tiramisu_brulee/model/tiramisu.py @@ -73,6 +73,8 @@ def __init__( growth_rate: builtins.int = 16, first_conv_out_channels: builtins.int = 48, dropout_rate: builtins.float = 0.2, + input_shape: typing.Optional[typing.Tuple[builtins.int, ...]] = None, + static_upsample: builtins.bool = True, ): """ Base class for Tiramisu convolutional neural network @@ -95,9 +97,16 @@ def __init__( """ super().__init__() assert len(down_blocks) == len(up_blocks) + self.down_blocks = down_blocks self.up_blocks = up_blocks skip_connection_channel_counts: typing.List[builtins.int] = [] + if input_shape is not None: + tensor_shape = torch.as_tensor(input_shape) + shapes = [input_shape] + static_upsample = all(x % 2 == 0 for x in input_shape) and static_upsample + else: + static_upsample = False first_padding = 2 * [fks // 2 for fks in self._first_kernel_size] self.first_conv = nn.Sequential( @@ -114,7 +123,7 @@ def __init__( # Downsampling path self.dense_down = nn.ModuleList([]) self.trans_down = nn.ModuleList([]) - for n_layers in down_blocks: + for i, n_layers in enumerate(down_blocks, 1): denseblock = self._denseblock( in_channels=cur_channels_count, growth_rate=growth_rate, @@ -131,6 +140,9 @@ def __init__( dropout_rate=dropout_rate, ) self.trans_down.append(trans_down_block) + if i < len(down_blocks) and input_shape is not None: + tensor_shape = torch.div(tensor_shape, 2, rounding_mode="floor") + shapes.append(tuple(tensor_shape)) # Bottleneck self.bottleneck = self._bottleneck( @@ -147,9 +159,12 @@ def __init__( self.trans_up = nn.ModuleList([]) up_info = zip(up_blocks, skip_connection_channel_counts) for i, (n_layers, sccc) in enumerate(up_info, 1): + resize_shape = None if input_shape is None else shapes.pop() trans_up_block = self._trans_up( in_channels=prev_block_channels, out_channels=prev_block_channels, + resize_shape=resize_shape, + static=static_upsample, ) self.trans_up.append(trans_up_block) cur_channels_count = prev_block_channels + sccc From 96fd21d38d7eecec09c42565e31161665bc8dfdc Mon Sep 17 00:00:00 2001 From: Jacob Reinhold <5241441+jcreinhold@users.noreply.github.com> Date: Mon, 10 Jan 2022 20:22:15 -0500 Subject: [PATCH 2/7] WIP: fix bug in transition up --- tiramisu_brulee/model/dense.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiramisu_brulee/model/dense.py b/tiramisu_brulee/model/dense.py index 2cd06a6..b833137 100644 --- a/tiramisu_brulee/model/dense.py +++ b/tiramisu_brulee/model/dense.py @@ -239,7 +239,7 @@ def __init__( ] self.resize: typing.Optional[typing.Callable[[torch.Tensor, ...], torch.Tensor]] if resize_method == ResizeMethod.CROP: - self.conv = conv_creator(**conv_kwargs) + self.conv = self._conv_trans(**_conv_trans_kwargs) self.resize = self._crop_to_target self.forward = self._forward_dynamic elif resize_method == ResizeMethod.INTERPOLATE and not static: @@ -247,7 +247,7 @@ def __init__( self.resize = self._interpolate_to_target self.forward = self._forward_dynamic elif resize_method == ResizeMethod.INTERPOLATE and static: - self.conv = conv_creator(**conv_kwargs) + self.conv = self._conv(**_conv_kwargs) self.resize = None self.forward = self._forward_static else: From 2584a6f4b9b0184cd8e456bc4f97aa0f77170ebb Mon Sep 17 00:00:00 2001 From: Jacob Reinhold <5241441+jcreinhold@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:51:01 -0500 Subject: [PATCH 3/7] add support for interpolation-based upsampling vs conv transpose --- requirements_dev.txt | 1 + setup.cfg | 1 + tiramisu_brulee/experiment/cli/to_onnx.py | 15 ++- tiramisu_brulee/experiment/seg.py | 52 ++++++-- tiramisu_brulee/model/__init__.py | 1 + tiramisu_brulee/model/dense.py | 137 ++++++++++++---------- tiramisu_brulee/model/tiramisu.py | 60 ++++++---- tiramisu_brulee/util.py | 42 +++++-- 8 files changed, 207 insertions(+), 102 deletions(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 0ccc04a..557e425 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -22,6 +22,7 @@ numpy~=1.22.0; python_version >= "3.8" pandas~=1.1.5; python_version < "3.7" pandas>=1.3.4; python_version >= "3.7" pandas-stubs +pillow>=9.0.0 pytorch-lightning~=1.5.1 PyYAML>=5.4.1 ruyaml>=0.20.0 diff --git a/setup.cfg b/setup.cfg index 2f4c772..70f8fde 100644 --- a/setup.cfg +++ b/setup.cfg @@ -74,6 +74,7 @@ lesionseg = jsonargparse~=3.12.0 numpy pandas + pillow>=9.0.0 pytorch-lightning~=1.5.1 PyYAML ruyaml diff --git a/tiramisu_brulee/experiment/cli/to_onnx.py b/tiramisu_brulee/experiment/cli/to_onnx.py index af7468a..c41ffd5 100644 --- a/tiramisu_brulee/experiment/cli/to_onnx.py +++ b/tiramisu_brulee/experiment/cli/to_onnx.py @@ -183,8 +183,12 @@ def to_onnx(args: ArgType = None) -> builtins.int: root, base, ext = split_filename(args.onnx_path) onnx_path = root / (base + f"_{i}" + ext) nth_model = f" ({i}/{n_models})" if n_models > 1 else "" + if args.image_shape is None: + args.image_shape = (128,) * (3 if p3d is None else 2) model = LesionSegLightningTiramisu.load_from_checkpoint( str(model_path), + input_shape=args.image_shape, + static_upsample=args.no_dynamic_shape, _model_num=model_num, ) if args.prune: @@ -199,11 +203,7 @@ def to_onnx(args: ArgType = None) -> builtins.int: parameters_to_prune, prune.L1Unstructured, amount=args.prune_amount ) n_channels = n_inputs if p3d is None else (args.pseudo3d_size * n_inputs) - if args.image_shape is None: - image_shape = (128,) * (3 if p3d is None else 2) - else: - image_shape = args.image_shape - input_shape = (args.batch_size, n_channels) + tuple(image_shape) + input_shape = (args.batch_size, n_channels) + tuple(args.image_shape) logger.debug(f"Input shape: {input_shape}") input_sample = torch.randn(input_shape) axes = dict() @@ -327,6 +327,11 @@ def add_metadata( if args.pseudo3d_dim is not None: doc_string += f" p3d:{args.pseudo3d_dim[i]}" doc_string += f" p3s:{args.pseudo3d_size}" + if args.no_dynamic_batch: + doc_string += f" static-batch-size={args.batch_size}" + if args.no_dynamic_shape: + image_shape = str(args.image_shape).replace(" ", "") + doc_string += f" static-shape={image_shape}" model = onnx.load_model(onnx_model_path) model.producer_name = producer_name model.producer_version = producer_version diff --git a/tiramisu_brulee/experiment/seg.py b/tiramisu_brulee/experiment/seg.py index 7cb91e8..49f2e67 100644 --- a/tiramisu_brulee/experiment/seg.py +++ b/tiramisu_brulee/experiment/seg.py @@ -15,6 +15,7 @@ ] import builtins +import enum import functools import logging import typing @@ -60,12 +61,31 @@ l1_segmentation_loss, mse_segmentation_loss, ) -from tiramisu_brulee.model import Tiramisu2d, Tiramisu3d -from tiramisu_brulee.util import init_weights +from tiramisu_brulee.model import ResizeMethod, Tiramisu2d, Tiramisu3d +from tiramisu_brulee.util import InitType, init_weights PredictBatch = typing.Union[PatchesImagePredictBatch, WholeImagePredictBatch] +@enum.unique +class LossFunction(enum.Enum): + COMBO: builtins.str = "combo" + L1: builtins.str = "l1" + MSE: builtins.str = "mse" + + @classmethod + def from_string(cls, string: builtins.str) -> "LossFunction": + if string.lower() == "combo": + return cls.COMBO + elif string.lower() == "l1": + return cls.L1 + elif string.lower() == "mse": + return cls.MSE + else: + msg = f"Only 'combo', 'l1', 'mse' allowed. Got {string}" + raise ValueError(msg) + + class LesionSegLightningBase(pl.LightningModule): """PyTorch-Lightning module for lesion segmentation @@ -144,7 +164,10 @@ def setup(self, stage: typing.Optional[builtins.str] = None) -> None: self.criterion: typing.Callable num_classes = self.hparams.num_classes assert isinstance(num_classes, builtins.int) - if self.hparams.loss_function == "combo": + loss_func_str = self.hparams.loss_function + assert isinstance(loss_func_str, builtins.str) + loss_func = LossFunction.from_string(loss_func_str) + if loss_func == LossFunction.COMBO: if self.hparams.num_classes == 1: self.criterion = functools.partial( binary_combo_loss, @@ -161,10 +184,10 @@ def setup(self, stage: typing.Optional[builtins.str] = None) -> None: else: msg = f"num_classes must be greater than zero. Got {self.num_classes}." raise ValueError(msg) - elif self.hparams.loss_function == "mse": - self.criterion = mse_segmentation_loss - elif self.hparams.loss_function == "l1": + elif loss_func == LossFunction.L1: self.criterion = l1_segmentation_loss + elif loss_func == LossFunction.MSE: + self.criterion = mse_segmentation_loss else: raise ValueError(f"{self.hparams.loss_function} not supported.") use_mixup = bool(self.hparams.mixup) @@ -785,6 +808,9 @@ def __init__( # type: ignore[no-untyped-def] mixup: builtins.bool = False, mixup_alpha: builtins.float = 0.4, num_input: builtins.int = 1, + resize_method: builtins.str = "crop", + input_shape: typing.Optional[typing.Tuple[builtins.int, ...]] = None, + static_upsample: builtins.bool = True, _model_num: ModelNum = ModelNum(1, 1), **kwargs, ): @@ -804,8 +830,11 @@ def __init__( # type: ignore[no-untyped-def] growth_rate=growth_rate, first_conv_out_channels=first_conv_out_channels, dropout_rate=dropout_rate, + resize_method=ResizeMethod.from_string(resize_method), + input_shape=input_shape, + static_upsample=static_upsample, ) - init_weights(network, init_type=init_type, gain=gain) + init_weights(network, init_type=InitType.from_string(init_type), gain=gain) super().__init__( network=network, n_epochs=n_epochs, @@ -915,4 +944,13 @@ def add_model_arguments(parent_parser: ArgParser) -> ArgParser: default=48, help="number of output channels in first conv", ) + parser.add_argument( + "-rm", + "--resize-method", + type=str, + default="crop", + choices=("crop", "interpolate"), + help="use transpose conv and crop or normal conv " + "and interpolate to correct size in upsample branch", + ) return parent_parser diff --git a/tiramisu_brulee/model/__init__.py b/tiramisu_brulee/model/__init__.py index a785645..7d329e5 100644 --- a/tiramisu_brulee/model/__init__.py +++ b/tiramisu_brulee/model/__init__.py @@ -1 +1,2 @@ +from tiramisu_brulee.model.dense import ResizeMethod from tiramisu_brulee.model.tiramisu import Tiramisu2d, Tiramisu3d diff --git a/tiramisu_brulee/model/dense.py b/tiramisu_brulee/model/dense.py index b833137..db090ac 100644 --- a/tiramisu_brulee/model/dense.py +++ b/tiramisu_brulee/model/dense.py @@ -27,7 +27,7 @@ ACTIVATION = functools.partial(nn.ReLU, inplace=True) -# partial not supported by mypy so avoid to type check +# partial not supported well by mypy; avoid to type check in class vars below # https://github.com/python/mypy/issues/1484 class Dropout2d(nn.Dropout2d): def __init__( @@ -44,17 +44,23 @@ def __init__( class ConvLayer(nn.Sequential): - _conv: typing.Union[typing.Type[nn.Conv2d], typing.Type[nn.Conv3d]] - _dropout: typing.Union[typing.Type[nn.Dropout2d], typing.Type[nn.Dropout3d]] - _kernel_size: typing.Union[ - typing.Tuple[builtins.int, builtins.int], - typing.Tuple[builtins.int, builtins.int, builtins.int], + _conv: typing.ClassVar[typing.Union[typing.Type[nn.Conv2d], typing.Type[nn.Conv3d]]] + _dropout: typing.ClassVar[ + typing.Union[typing.Type[nn.Dropout2d], typing.Type[nn.Dropout3d]] ] - _maxpool: typing.Union[None, typing.Type[nn.MaxPool2d], typing.Type[nn.MaxPool3d]] - _norm: typing.Union[typing.Type[nn.BatchNorm2d], typing.Type[nn.BatchNorm3d]] - _pad = typing.Union[ - typing.Type[nn.ReplicationPad2d], typing.Type[nn.ReplicationPad3d] + _kernel_size: typing.ClassVar[ + typing.Union[ + typing.Tuple[builtins.int, builtins.int], + typing.Tuple[builtins.int, builtins.int, builtins.int], + ] + ] + _maxpool: typing.ClassVar[ + typing.Union[None, typing.Type[nn.MaxPool2d], typing.Type[nn.MaxPool3d]] ] + _norm: typing.ClassVar[ + typing.Union[typing.Type[nn.BatchNorm2d], typing.Type[nn.BatchNorm3d]] + ] + _padding_mode: typing.ClassVar[builtins.str] = "replicate" def __init__( self, @@ -67,15 +73,15 @@ def __init__( self.dropout_rate = dropout_rate self.add_module("norm", self._norm(in_channels)) self.add_module("act", ACTIVATION()) - if self._use_padding(): - padding = 2 * [ks // 2 for ks in self._kernel_size] - pad = self._pad(padding) # type: ignore[operator] - self.add_module("pad", pad) + padding: typing.Union[builtins.str, builtins.int] + padding = "same" if self._use_padding() else 0 conv = self._conv( in_channels=in_channels, out_channels=out_channels, kernel_size=self._kernel_size, # type: ignore[arg-type] bias=False, + padding=padding, + padding_mode=self._padding_mode, ) self.add_module("conv", conv) if self._use_dropout(): @@ -87,7 +93,7 @@ def _use_dropout(self) -> builtins.bool: return self.dropout_rate > 0.0 def _use_padding(self) -> builtins.bool: - return any([ks > 2 for ks in self._kernel_size]) + return any(ks > 2 for ks in self._kernel_size) class ConvLayer2d(ConvLayer): @@ -96,7 +102,6 @@ class ConvLayer2d(ConvLayer): _kernel_size = (3, 3) _maxpool = None _norm = nn.BatchNorm2d - _pad = nn.ReplicationPad2d class ConvLayer3d(ConvLayer): @@ -105,11 +110,12 @@ class ConvLayer3d(ConvLayer): _kernel_size = (3, 3, 3) _maxpool = None _norm = nn.BatchNorm3d - _pad = nn.ReplicationPad3d class DenseBlock(nn.Module): - _layer: typing.Union[typing.Type[ConvLayer2d], typing.Type[ConvLayer3d]] + _layer: typing.ClassVar[ + typing.Union[typing.Type[ConvLayer2d], typing.Type[ConvLayer3d]] + ] def __init__( self, @@ -173,7 +179,6 @@ class TransitionDown2d(ConvLayer): _kernel_size = (1, 1) _maxpool = nn.MaxPool2d _norm = nn.BatchNorm2d - _pad = nn.ReplicationPad2d class TransitionDown3d(ConvLayer): @@ -182,12 +187,22 @@ class TransitionDown3d(ConvLayer): _kernel_size = (1, 1, 1) _maxpool = nn.MaxPool3d _norm = nn.BatchNorm3d - _pad = nn.ReplicationPad3d -class ResizeMethod(enum.IntEnum): - CROP: builtins.int = 0 - INTERPOLATE: builtins.int = 1 +@enum.unique +class ResizeMethod(enum.Enum): + CROP: builtins.str = "crop" + INTERPOLATE: builtins.str = "interpolate" + + @classmethod + def from_string(cls, string: builtins.str) -> "ResizeMethod": + if string.lower() == "crop": + return cls.CROP + elif string.lower() == "interpolate": + return cls.INTERPOLATE + else: + msg = f"Only 'crop' and 'interpolate' allowed. Got {string}" + raise ValueError(msg) class TransitionUp(nn.Module): @@ -209,7 +224,6 @@ class TransitionUp(nn.Module): ] _interp_mode: typing.ClassVar[builtins.str] - @typing.no_type_check def __init__( self, *, @@ -222,39 +236,39 @@ def __init__( ): super().__init__() self.resize_shape = resize_shape - conv_base_kwargs = dict( + _conv_kwargs = dict( in_channels=in_channels, out_channels=out_channels, kernel_size=self._kernel_size, bias=False, ) - _conv_kwargs = conv_base_kwargs.copy() - _conv_kwargs["padding"] = 1 - _conv_trans_kwargs = conv_base_kwargs.copy() - _conv_trans_kwargs["stride"] = self._stride - conv_creator = self._conv_trans if use_conv_transpose else self._conv - conv_kwargs = _conv_trans_kwargs if use_conv_transpose else _conv_kwargs + conv_kwargs: typing.Dict[builtins.str, typing.Any] = _conv_kwargs.copy() + conv_kwargs["padding"] = "same" + conv_kwargs["padding_mode"] = "replicate" + conv_trans_kwargs: typing.Dict[builtins.str, typing.Any] = _conv_kwargs.copy() + conv_trans_kwargs["stride"] = self._stride self.conv: typing.Union[ nn.Conv2d, nn.Conv3d, nn.ConvTranspose2d, nn.ConvTranspose3d ] - self.resize: typing.Optional[typing.Callable[[torch.Tensor, ...], torch.Tensor]] + self.resize: typing.Callable[..., torch.Tensor] if resize_method == ResizeMethod.CROP: - self.conv = self._conv_trans(**_conv_trans_kwargs) + self.conv = self._conv_trans(**conv_trans_kwargs) self.resize = self._crop_to_target - self.forward = self._forward_dynamic + setattr(self, "forward", self._forward_dynamic) elif resize_method == ResizeMethod.INTERPOLATE and not static: - self.conv = conv_creator(**conv_kwargs) + if use_conv_transpose: + self.conv = self._conv_trans(**conv_trans_kwargs) + else: + self.conv = self._conv(**conv_kwargs) self.resize = self._interpolate_to_target - self.forward = self._forward_dynamic + setattr(self, "forward", self._forward_dynamic) elif resize_method == ResizeMethod.INTERPOLATE and static: - self.conv = self._conv(**_conv_kwargs) - self.resize = None - self.forward = self._forward_static + self.conv = self._conv(**conv_kwargs) + setattr(self, "forward", self._forward_static) else: msg = f"resize_method needs to be a ResizeMethod. Got {resize_method}" raise ValueError(msg) - @typing.no_type_check def _forward_dynamic( self, tensor: torch.Tensor, *, skip: torch.Tensor ) -> torch.Tensor: @@ -263,14 +277,11 @@ def _forward_dynamic( out = torch.cat((out, skip), 1) return out - @typing.no_type_check def _forward_static( self, tensor: torch.Tensor, *, skip: torch.Tensor ) -> torch.Tensor: out: torch.Tensor = self.conv(tensor) - out = F.interpolate( - out, align_corners=True, mode=self._interp_mode, scale_factor=2.0 - ) + out = self._interpolate(out, scale_factor=2.0) out = torch.cat((out, skip), 1) return out @@ -279,13 +290,15 @@ def _crop_to_target( ) -> torch.Tensor: raise NotImplementedError - @typing.no_type_check def _interpolate_to_target( self, tensor: torch.Tensor, *, target: torch.Tensor ) -> torch.Tensor: - return F.interpolate( - tensor, size=target.shape[2:], mode=self._interp_mode, align_corners=True - ) + return self._interpolate(tensor, size=target.shape[2:]) + + def _interpolate(self, tensor: torch.Tensor, **kwargs: typing.Any) -> torch.Tensor: + interp_kwargs = dict(mode=self._interp_mode, align_corners=True, **kwargs) + out: torch.Tensor = F.interpolate(tensor, **interp_kwargs) + return out class TransitionUp2d(TransitionUp): @@ -295,18 +308,17 @@ class TransitionUp2d(TransitionUp): _stride = (2, 2) _interp_mode = "bilinear" - @typing.no_type_check def _crop_to_target( self, tensor: torch.Tensor, *, target: torch.Tensor ) -> torch.Tensor: if self.resize_shape is None: - _, _, max_height, max_width = target.shape + _, _, max_h, max_w = target.shape else: - max_height, max_width = self.resize_shape + max_h, max_w = self.resize_shape _, _, _h, _w = tensor.size() - h = torch.div(_h - max_height, 2, rounding_mode="trunc") - w = torch.div(_w - max_width, 2, rounding_mode="trunc") - return tensor[:, :, h : h + max_height, w : w + max_width] + h = torch.div(_h - max_h, 2, rounding_mode="trunc") + w = torch.div(_w - max_w, 2, rounding_mode="trunc") + return tensor[:, :, h : h + max_h, w : w + max_w] # type: ignore[misc] class TransitionUp3d(TransitionUp): @@ -316,23 +328,24 @@ class TransitionUp3d(TransitionUp): _stride = (2, 2, 2) _interp_mode = "trilinear" - @typing.no_type_check def _crop_to_target( self, tensor: torch.Tensor, *, target: torch.Tensor ) -> torch.Tensor: if self.resize_shape is None: - _, _, max_height, max_width, max_depth = target.shape + _, _, max_h, max_w, max_d = target.shape else: - max_height, max_width, max_depth = self.resize_shape + max_h, max_w, max_d = self.resize_shape _, _, _h, _w, _d = tensor.size() - h = torch.div(_h - max_height, 2, rounding_mode="trunc") - w = torch.div(_w - max_width, 2, rounding_mode="trunc") - d = torch.div(_d - max_depth, 2, rounding_mode="trunc") - return tensor[:, :, h : h + max_height, w : w + max_width, d : d + max_depth] + h = torch.div(_h - max_h, 2, rounding_mode="trunc") + w = torch.div(_w - max_w, 2, rounding_mode="trunc") + d = torch.div(_d - max_d, 2, rounding_mode="trunc") + return tensor[:, :, h : h + max_h, w : w + max_w, d : d + max_d] # type: ignore[misc] class Bottleneck(nn.Sequential): - _layer: typing.Union[typing.Type[DenseBlock2d], typing.Type[DenseBlock3d]] + _layer: typing.ClassVar[ + typing.Union[typing.Type[DenseBlock2d], typing.Type[DenseBlock3d]] + ] def __init__( self, diff --git a/tiramisu_brulee/model/tiramisu.py b/tiramisu_brulee/model/tiramisu.py index dff9243..375d25d 100644 --- a/tiramisu_brulee/model/tiramisu.py +++ b/tiramisu_brulee/model/tiramisu.py @@ -35,6 +35,7 @@ Bottleneck3d, DenseBlock2d, DenseBlock3d, + ResizeMethod, TransitionDown2d, TransitionDown3d, TransitionUp2d, @@ -43,24 +44,32 @@ class Tiramisu(nn.Module): - _bottleneck: typing.Union[typing.Type[Bottleneck2d], typing.Type[Bottleneck3d]] - _conv: typing.Union[typing.Type[nn.Conv2d], typing.Type[nn.Conv3d]] - _denseblock: typing.Union[typing.Type[DenseBlock2d], typing.Type[DenseBlock3d]] - _pad: typing.Union[ - typing.Type[nn.ReplicationPad2d], typing.Type[nn.ReplicationPad3d] + _bottleneck: typing.ClassVar[ + typing.Union[typing.Type[Bottleneck2d], typing.Type[Bottleneck3d]] ] - _trans_down: typing.Union[ - typing.Type[TransitionDown2d], typing.Type[TransitionDown3d] + _conv: typing.ClassVar[typing.Union[typing.Type[nn.Conv2d], typing.Type[nn.Conv3d]]] + _denseblock: typing.ClassVar[ + typing.Union[typing.Type[DenseBlock2d], typing.Type[DenseBlock3d]] ] - _trans_up: typing.Union[typing.Type[TransitionUp2d], typing.Type[TransitionUp3d]] - _first_kernel_size: typing.Union[ - typing.Tuple[builtins.int, builtins.int], - typing.Tuple[builtins.int, builtins.int, builtins.int], + _trans_down: typing.ClassVar[ + typing.Union[typing.Type[TransitionDown2d], typing.Type[TransitionDown3d]] ] - _final_kernel_size: typing.Union[ - typing.Tuple[builtins.int, builtins.int], - typing.Tuple[builtins.int, builtins.int, builtins.int], + _trans_up: typing.ClassVar[ + typing.Union[typing.Type[TransitionUp2d], typing.Type[TransitionUp3d]] ] + _first_kernel_size: typing.ClassVar[ + typing.Union[ + typing.Tuple[builtins.int, builtins.int], + typing.Tuple[builtins.int, builtins.int, builtins.int], + ] + ] + _final_kernel_size: typing.ClassVar[ + typing.Union[ + typing.Tuple[builtins.int, builtins.int], + typing.Tuple[builtins.int, builtins.int, builtins.int], + ] + ] + _padding_mode: typing.ClassVar[builtins.str] = "replicate" def __init__( self, @@ -73,8 +82,9 @@ def __init__( growth_rate: builtins.int = 16, first_conv_out_channels: builtins.int = 48, dropout_rate: builtins.float = 0.2, + resize_method: ResizeMethod = ResizeMethod.CROP, input_shape: typing.Optional[typing.Tuple[builtins.int, ...]] = None, - static_upsample: builtins.bool = True, + static_upsample: builtins.bool = False, ): """ Base class for Tiramisu convolutional neural network @@ -94,6 +104,10 @@ def __init__( growth_rate (builtins.int): number of channels to grow by in each layer first_conv_out_channels (builtins.int): number of output channels in first conv dropout_rate (builtins.float): dropout rate/probability + resize_method (ResizeMethod): method to resize the image in upsample branch + input_shape: optionally provide shape of the input image (for onnx) + static_upsample: use static upsampling when capable if input_shape provided + (doesn't check upsampled size matches) """ super().__init__() assert len(down_blocks) == len(up_blocks) @@ -104,18 +118,15 @@ def __init__( if input_shape is not None: tensor_shape = torch.as_tensor(input_shape) shapes = [input_shape] - static_upsample = all(x % 2 == 0 for x in input_shape) and static_upsample - else: - static_upsample = False - first_padding = 2 * [fks // 2 for fks in self._first_kernel_size] self.first_conv = nn.Sequential( - self._pad(first_padding), # type: ignore[arg-type] self._conv( in_channels, first_conv_out_channels, self._first_kernel_size, # type: ignore[arg-type] bias=False, + padding="same", + padding_mode=self._padding_mode, ), ) cur_channels_count: builtins.int = first_conv_out_channels @@ -160,11 +171,16 @@ def __init__( up_info = zip(up_blocks, skip_connection_channel_counts) for i, (n_layers, sccc) in enumerate(up_info, 1): resize_shape = None if input_shape is None else shapes.pop() + if resize_shape is not None and static_upsample: + _static_upsample = all(x % 2 == 0 for x in resize_shape) + else: + _static_upsample = False trans_up_block = self._trans_up( in_channels=prev_block_channels, out_channels=prev_block_channels, + resize_method=resize_method, resize_shape=resize_shape, - static=static_upsample, + static=_static_upsample, ) self.trans_up.append(trans_up_block) cur_channels_count = prev_block_channels + sccc @@ -185,6 +201,8 @@ def __init__( out_channels=out_channels, kernel_size=self._final_kernel_size, # type: ignore[arg-type] bias=True, + padding="same", + padding_mode=self._padding_mode, ) def forward(self, x: torch.Tensor) -> torch.Tensor: diff --git a/tiramisu_brulee/util.py b/tiramisu_brulee/util.py index 473ab6e..3e207c8 100644 --- a/tiramisu_brulee/util.py +++ b/tiramisu_brulee/util.py @@ -3,9 +3,10 @@ Created on: Jul 01, 2020 """ -__all__ = ["init_weights"] +__all__ = ["InitType", "init_weights"] import builtins +import enum import torch import torch.nn as nn @@ -21,8 +22,35 @@ def is_norm(layer: nn.Module) -> builtins.bool: return hasattr(layer, "weight") and "Norm" in classname +@enum.unique +class InitType(enum.Enum): + NORMAL = "normal" + XAVIER_NORMAL = "xavier_normal" + HE_NORMAL = "he_normal" + HE_UNIFORM = "he_uniform" + ORTHOGONAL = "orthogonal" + + @classmethod + def from_string(cls, string: builtins.str) -> "InitType": + if string.lower() == "normal": + return cls.NORMAL + elif string.lower() == "xavier_normal": + return cls.XAVIER_NORMAL + elif string.lower() == "he_normal": + return cls.HE_NORMAL + elif string.lower() == "he_uniform": + return cls.HE_UNIFORM + elif string.lower() == "orthogonal": + return cls.ORTHOGONAL + else: + raise ValueError("Invalid init type.") + + def init_weights( - net: nn.Module, *, init_type: builtins.str = "normal", gain: builtins.float = 0.02 + net: nn.Module, + *, + init_type: InitType = InitType.NORMAL, + gain: builtins.float = 0.02, ) -> None: def init_func(layer: nn.Module) -> None: _is_conv = is_conv(layer) @@ -38,15 +66,15 @@ def init_func(layer: nn.Module) -> None: bias = layer.bias assert bias is layer.bias if _is_conv: - if init_type == "normal": + if init_type == InitType.NORMAL: nn.init.normal_(weight, 0.0, gain) - elif init_type == "xavier_normal": + elif init_type == InitType.XAVIER_NORMAL: nn.init.xavier_normal_(weight, gain=gain) - elif init_type == "he_normal": + elif init_type == InitType.HE_NORMAL: nn.init.kaiming_normal_(weight, a=0.0, mode="fan_in") - elif init_type == "he_uniform": + elif init_type == InitType.HE_UNIFORM: nn.init.kaiming_uniform_(weight, a=0.0, mode="fan_in") - elif init_type == "orthogonal": + elif init_type == InitType.ORTHOGONAL: nn.init.orthogonal_(weight, gain=gain) else: err_msg = f"initialization type [{init_type}] not implemented" From 9188334913011f9ae7b7d8d061724d1b368dd8f9 Mon Sep 17 00:00:00 2001 From: Jacob Reinhold <5241441+jcreinhold@users.noreply.github.com> Date: Tue, 11 Jan 2022 16:32:56 -0500 Subject: [PATCH 4/7] add flake8 ignore statements and specify py version for pillow --- requirements_dev.txt | 3 ++- tiramisu_brulee/experiment/cli/train.py | 1 + tiramisu_brulee/experiment/seg.py | 1 + tiramisu_brulee/experiment/type.py | 2 ++ tiramisu_brulee/model/dense.py | 20 +++++++++++++++----- tiramisu_brulee/model/tiramisu.py | 1 + 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 557e425..598516d 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -22,7 +22,8 @@ numpy~=1.22.0; python_version >= "3.8" pandas~=1.1.5; python_version < "3.7" pandas>=1.3.4; python_version >= "3.7" pandas-stubs -pillow>=9.0.0 +pillow>=8.4.0; python_version == "3.6" +pillow>=9.0.0; python_version >= "3.7" pytorch-lightning~=1.5.1 PyYAML>=5.4.1 ruyaml>=0.20.0 diff --git a/tiramisu_brulee/experiment/cli/train.py b/tiramisu_brulee/experiment/cli/train.py index 085e098..6bbf2b4 100644 --- a/tiramisu_brulee/experiment/cli/train.py +++ b/tiramisu_brulee/experiment/cli/train.py @@ -333,6 +333,7 @@ def __init__( # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.mlflow_logger = mlflow_logger + # flake8: noqa: E501 def save_checkpoint( self, trainer: Trainer, diff --git a/tiramisu_brulee/experiment/seg.py b/tiramisu_brulee/experiment/seg.py index 49f2e67..6342177 100644 --- a/tiramisu_brulee/experiment/seg.py +++ b/tiramisu_brulee/experiment/seg.py @@ -727,6 +727,7 @@ def add_testing_arguments(parent_parser: ArgParser) -> ArgParser: return parent_parser +# flake8: noqa: E501 class LesionSegLightningTiramisu(LesionSegLightningBase): """3D Tiramisu-based PyTorch-Lightning module for lesion segmentation diff --git a/tiramisu_brulee/experiment/type.py b/tiramisu_brulee/experiment/type.py index 7d2b99c..67f6177 100644 --- a/tiramisu_brulee/experiment/type.py +++ b/tiramisu_brulee/experiment/type.py @@ -66,6 +66,7 @@ PathLike = typing.Union[builtins.str, os.PathLike] +# flake8: noqa: E501 def return_none(func: typing.Callable) -> typing.Callable: def new_func(self, string: typing.Any) -> typing.Any: # type: ignore[no-untyped-def] if string is None: @@ -78,6 +79,7 @@ def new_func(self, string: typing.Any) -> typing.Any: # type: ignore[no-untyped return new_func +# flake8: noqa: E501 def return_str(match_string: builtins.str) -> typing.Callable: def decorator(func: typing.Callable) -> typing.Callable: def new_func(self, string: typing.Any) -> typing.Any: # type: ignore[no-untyped-def] diff --git a/tiramisu_brulee/model/dense.py b/tiramisu_brulee/model/dense.py index db090ac..62af643 100644 --- a/tiramisu_brulee/model/dense.py +++ b/tiramisu_brulee/model/dense.py @@ -254,14 +254,15 @@ def __init__( if resize_method == ResizeMethod.CROP: self.conv = self._conv_trans(**conv_trans_kwargs) self.resize = self._crop_to_target - setattr(self, "forward", self._forward_dynamic) + setattr(self, "forward", self._forward_dynamic_trans) elif resize_method == ResizeMethod.INTERPOLATE and not static: if use_conv_transpose: self.conv = self._conv_trans(**conv_trans_kwargs) + setattr(self, "forward", self._forward_dynamic_trans) else: self.conv = self._conv(**conv_kwargs) + setattr(self, "forward", self._forward_dynamic_conv) self.resize = self._interpolate_to_target - setattr(self, "forward", self._forward_dynamic) elif resize_method == ResizeMethod.INTERPOLATE and static: self.conv = self._conv(**conv_kwargs) setattr(self, "forward", self._forward_static) @@ -269,7 +270,7 @@ def __init__( msg = f"resize_method needs to be a ResizeMethod. Got {resize_method}" raise ValueError(msg) - def _forward_dynamic( + def _forward_dynamic_trans( self, tensor: torch.Tensor, *, skip: torch.Tensor ) -> torch.Tensor: out: torch.Tensor = self.conv(tensor) @@ -277,11 +278,19 @@ def _forward_dynamic( out = torch.cat((out, skip), 1) return out + def _forward_dynamic_conv( + self, tensor: torch.Tensor, *, skip: torch.Tensor + ) -> torch.Tensor: + out: torch.Tensor = self.resize(tensor, target=skip) + out = self.conv(out) + out = torch.cat((out, skip), 1) + return out + def _forward_static( self, tensor: torch.Tensor, *, skip: torch.Tensor ) -> torch.Tensor: - out: torch.Tensor = self.conv(tensor) - out = self._interpolate(out, scale_factor=2.0) + out: torch.Tensor = self._interpolate(tensor, scale_factor=2.0) + out = self.conv(out) out = torch.cat((out, skip), 1) return out @@ -328,6 +337,7 @@ class TransitionUp3d(TransitionUp): _stride = (2, 2, 2) _interp_mode = "trilinear" + # flake8: noqa: E501 def _crop_to_target( self, tensor: torch.Tensor, *, target: torch.Tensor ) -> torch.Tensor: diff --git a/tiramisu_brulee/model/tiramisu.py b/tiramisu_brulee/model/tiramisu.py index 375d25d..f8b3c17 100644 --- a/tiramisu_brulee/model/tiramisu.py +++ b/tiramisu_brulee/model/tiramisu.py @@ -71,6 +71,7 @@ class Tiramisu(nn.Module): ] _padding_mode: typing.ClassVar[builtins.str] = "replicate" + # flake8: noqa: E501 def __init__( self, *, From 986d055e5c0abba82ebe7ec5cd10d3c882285681 Mon Sep 17 00:00:00 2001 From: Jacob Reinhold <5241441+jcreinhold@users.noreply.github.com> Date: Tue, 11 Jan 2022 16:37:29 -0500 Subject: [PATCH 5/7] add v0.2.0 history --- HISTORY.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index b767790..f871a24 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,13 @@ History ======= +0.2.0 (2022-01-11) +------------------- + +* Add option to upsample with interpolation instead of transpose conv. +* Remove separate padding layer and use conv. built-in padding +* Improvements to ONNX converter + 0.1.37 (2021-12-16) ------------------- From 6598f3646872a7c88d813acc2b83e847bb85c7c8 Mon Sep 17 00:00:00 2001 From: Jacob Reinhold <5241441+jcreinhold@users.noreply.github.com> Date: Tue, 11 Jan 2022 16:37:42 -0500 Subject: [PATCH 6/7] =?UTF-8?q?Bump=20version:=200.1.37=20=E2=86=92=200.2.?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 26 +++++++++++++------------- tiramisu_brulee/__init__.py | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/setup.cfg b/setup.cfg index 70f8fde..e1727b3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.37 +current_version = 0.2.0 commit = True tag = True @@ -9,7 +9,7 @@ replace = __version__ = "{new_version}" [flake8] exclude = docs -per-file-ignores = +per-file-ignores = __init__.py: F401 max-line-length = 88 extend-ignore = E203 @@ -32,10 +32,10 @@ keywords = tiramisu, segmentation, neural network, convolutional, pytorch license = Apache Software License 2.0 license_file = LICENSE url = https://github.com/jcreinhold/tiramisu-brulee -project_urls = +project_urls = Bug Tracker = https://github.com/jcreinhold/tiramisu-brulee/issues Documentation = https://tiramisu-brulee.readthedocs.io/ -classifiers = +classifiers = Development Status :: 4 - Beta Intended Audience :: Developers License :: OSI Approved :: Apache Software License @@ -54,15 +54,15 @@ zip_safe = False include_package_data = True packages = find: python_requires = >= 3.6 -install_requires = +install_requires = torch test_suite = tests [options.packages.find] -include = +include = tiramisu_brulee tiramisu_brulee.* -exclude = +exclude = tests docs @@ -70,11 +70,11 @@ exclude = tiramisu_brulee = py.typed [options.extras_require] -lesionseg = +lesionseg = jsonargparse~=3.12.0 numpy pandas - pillow>=9.0.0 + pillow>=9.0.0 pytorch-lightning~=1.5.1 PyYAML ruyaml @@ -84,12 +84,12 @@ lesionseg = torchio torchmetrics mlflow = mlflow -onnx = - onnx - onnxruntime +onnx = + onnx + onnxruntime [options.entry_points] -console_scripts = +console_scripts = lesion-train = tiramisu_brulee.experiment.cli.train:train lesion-predict = tiramisu_brulee.experiment.cli.predict:predict lesion-predict-image = tiramisu_brulee.experiment.cli.predict:predict_image diff --git a/tiramisu_brulee/__init__.py b/tiramisu_brulee/__init__.py index a75fc2d..b61691c 100644 --- a/tiramisu_brulee/__init__.py +++ b/tiramisu_brulee/__init__.py @@ -5,7 +5,7 @@ __url__ = "https://github.com/jcreinhold/tiramisu-brulee" __author__ = """Jacob Reinhold""" __email__ = "jcreinhold@gmail.com" -__version__ = "0.1.37" +__version__ = "0.2.0" __license__ = "Apache-2.0" __copyright__ = "Copyright 2021 Jacob Reinhold" From 7cd7f98f5e8adbc7fea361bb31e09128f4f18593 Mon Sep 17 00:00:00 2001 From: Jacob Reinhold <5241441+jcreinhold@users.noreply.github.com> Date: Tue, 11 Jan 2022 16:40:36 -0500 Subject: [PATCH 7/7] add to_onnx module in docs --- docs/tiramisu_brulee.experiment.cli.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/tiramisu_brulee.experiment.cli.rst b/docs/tiramisu_brulee.experiment.cli.rst index ff7723c..5a88edf 100644 --- a/docs/tiramisu_brulee.experiment.cli.rst +++ b/docs/tiramisu_brulee.experiment.cli.rst @@ -20,6 +20,14 @@ tiramisu\_brulee.experiment.cli.predict module :undoc-members: :show-inheritance: +tiramisu\_brulee.experiment.cli.to\_onnx module +----------------------------------------------- + +.. automodule:: tiramisu_brulee.experiment.cli.to_onnx + :members: + :undoc-members: + :show-inheritance: + tiramisu\_brulee.experiment.cli.train module --------------------------------------------