File size: 3,898 Bytes
d4700c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# Copyright (c) 2026 Guy's and St Thomas' NHS Foundation Trust & King's College London
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Autoencoder network for the FLIP autoencoder tutorial: a KL autoencoder and a patch discriminator.

Both are trained and aggregated together. The autoencoder submodule must stay named ``autoencoder``:
the latent diffusion tutorial loads this run's ``autoencoder.*`` weights by that prefix, and its
server loads with ``strict=False``, so a renamed submodule would load nothing.
"""

import json
from collections.abc import Mapping
from pathlib import Path
from typing import Any

from monai.networks.nets import AutoencoderKL, PatchDiscriminator
from torch import nn


def load_net_config():
    """Read the ``net_config`` block from the ``config.json`` next to this file."""
    config_path = Path(__file__).parent / "config.json"
    with open(config_path, "r") as f:
        config = json.load(f)
    net_config = config.get("net_config", {})
    print(f"Loaded network config: {net_config}")
    return net_config


def autoencoder_kwargs(net_config: Mapping[str, Any]) -> dict[str, Any]:
    """The keyword arguments this tutorial builds its ``AutoencoderKL`` with: the one definition.

    The latent diffusion tutorial's ``extract_autoencoder.py`` records them beside an extracted
    checkpoint. Every argument is explicit: ``norm_num_groups`` changes no tensor shape, so a wrong
    default would load a checkpoint cleanly and produce wrong latents.
    """
    autoencoder = net_config["autoencoder"]
    return {
        "spatial_dims": net_config["spatial_dims"],
        "in_channels": autoencoder["in_channels"],
        "out_channels": autoencoder["out_channels"],
        "num_res_blocks": autoencoder["num_res_blocks"],
        "channels": autoencoder["channels"],
        "attention_levels": autoencoder["attention_levels"],
        "latent_channels": autoencoder["latent_channels"],
        "norm_num_groups": autoencoder["norm_num_groups"],
        "norm_eps": autoencoder["norm_eps"],
        "with_encoder_nonlocal_attn": autoencoder["with_encoder_nonlocal_attn"],
        "with_decoder_nonlocal_attn": autoencoder["with_decoder_nonlocal_attn"],
    }


class AutoencoderNetwork(nn.Module):
    """Creates the autoencoder training network, containing a:

    - Variational Autoencoder (compresses inputs into a latent space and reconstructs them)
    - Discriminator (adversarially trains the autoencoder)
    """

    def __init__(self):
        super().__init__()
        net_config = load_net_config()
        self.autoencoder = AutoencoderKL(**autoencoder_kwargs(net_config))

        self.discriminator = PatchDiscriminator(
            spatial_dims=net_config["discriminator"]["spatial_dims"],
            in_channels=net_config["discriminator"]["in_channels"],
            channels=net_config["discriminator"]["channels"],
            out_channels=net_config["discriminator"]["out_channels"],
            num_layers_d=net_config["discriminator"]["num_layers_d"],
        )


_net = AutoencoderNetwork()


def get_model() -> nn.Module:
    """
    Returns the model defined in this file.
    NOTE: This function needs to exist and cannot take any input arguments. If you would like to parameterize the
    configuration of your model, for example loaded from a config file, do it when instantiating the model above.
    """
    return _net