Execute mean ensemble on the input data. The input data can be a list or tuple of PyTorch Tensor with shape: [C[, H, W, D]], Or a single PyTorch Tensor with shape: [E, C[, H, W, D]], the `E` dimension represents the output data from different models. Typically, the input data is
| 659 | |
| 660 | |
| 661 | class MeanEnsemble(Ensemble, Transform): |
| 662 | """ |
| 663 | Execute mean ensemble on the input data. |
| 664 | The input data can be a list or tuple of PyTorch Tensor with shape: [C[, H, W, D]], |
| 665 | Or a single PyTorch Tensor with shape: [E, C[, H, W, D]], the `E` dimension represents |
| 666 | the output data from different models. |
| 667 | Typically, the input data is model output of segmentation task or classification task. |
| 668 | And it also can support to add `weights` for the input data. |
| 669 | |
| 670 | Args: |
| 671 | weights: can be a list or tuple of numbers for input data with shape: [E, C, H, W[, D]]. |
| 672 | or a Numpy ndarray or a PyTorch Tensor data. |
| 673 | the `weights` will be added to input data from highest dimension, for example: |
| 674 | 1. if the `weights` only has 1 dimension, it will be added to the `E` dimension of input data. |
| 675 | 2. if the `weights` has 2 dimensions, it will be added to `E` and `C` dimensions. |
| 676 | it's a typical practice to add weights for different classes: |
| 677 | to ensemble 3 segmentation model outputs, every output has 4 channels(classes), |
| 678 | so the input data shape can be: [3, 4, H, W, D]. |
| 679 | and add different `weights` for different classes, so the `weights` shape can be: [3, 4]. |
| 680 | for example: `weights = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 1, 1, 1]]`. |
| 681 | |
| 682 | """ |
| 683 | |
| 684 | backend = [TransformBackends.TORCH] |
| 685 | |
| 686 | def __init__(self, weights: Sequence[float] | NdarrayOrTensor | None = None) -> None: |
| 687 | self.weights = torch.as_tensor(weights, dtype=torch.float) if weights is not None else None |
| 688 | |
| 689 | def __call__(self, img: Sequence[NdarrayOrTensor] | NdarrayOrTensor) -> NdarrayOrTensor: |
| 690 | img_ = self.get_stacked_torch(img) |
| 691 | if self.weights is not None: |
| 692 | self.weights = self.weights.to(img_.device) |
| 693 | shape = tuple(self.weights.shape) |
| 694 | for _ in range(img_.ndimension() - self.weights.ndimension()): |
| 695 | shape += (1,) |
| 696 | weights = self.weights.reshape(*shape) |
| 697 | |
| 698 | img_ = img_ * weights / weights.mean(dim=0, keepdim=True) |
| 699 | |
| 700 | out_pt = torch.mean(img_, dim=0) |
| 701 | return self.post_convert(out_pt, img) |
| 702 | |
| 703 | |
| 704 | class VoteEnsemble(Ensemble, Transform): |
no outgoing calls
searching dependent graphs…