jcm/models/ddpm.py (70 lines of code) (raw):
# coding=utf-8
# Copyright 2020 The Google Research Authors.
#
# 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.
# pylint: skip-file
"""DDPM model.
This code is the FLAX equivalent of:
https://github.com/hojonathanho/diffusion/blob/master/diffusion_tf/models/unet.py
"""
import flax.linen as nn
import jax.numpy as jnp
import ml_collections
import functools
from . import utils, layers, normalization
RefineBlock = layers.RefineBlock
ResidualBlock = layers.ResidualBlock
ResnetBlockDDPM = layers.ResnetBlockDDPM
Upsample = layers.Upsample
Downsample = layers.Downsample
conv3x3 = layers.ddpm_conv3x3
get_act = layers.get_act
get_normalization = normalization.get_normalization
default_initializer = layers.default_init
@utils.register_model(name="ddpm")
class DDPM(nn.Module):
"""DDPM model architecture."""
config: ml_collections.ConfigDict
@nn.compact
def __call__(self, x, labels, train=True):
# config parsing
config = self.config
act = get_act(config)
normalize = get_normalization(config)
nf = config.model.nf
ch_mult = config.model.ch_mult
num_res_blocks = config.model.num_res_blocks
attn_resolutions = config.model.attn_resolutions
dropout = config.model.dropout
resamp_with_conv = config.model.resamp_with_conv
num_resolutions = len(ch_mult)
AttnBlock = functools.partial(layers.AttnBlock, normalize=normalize)
ResnetBlock = functools.partial(
ResnetBlockDDPM, act=act, normalize=normalize, dropout=dropout
)
if config.model.conditional:
# timestep/scale embedding
timesteps = labels
temb = layers.get_timestep_embedding(timesteps, nf)
temb = nn.Dense(nf * 4, kernel_init=default_initializer())(temb)
temb = nn.Dense(nf * 4, kernel_init=default_initializer())(act(temb))
else:
temb = None
if config.data.centered:
# Input is in [-1, 1]
h = x
else:
# Input is in [0, 1]
h = 2 * x - 1.0
# Downsampling block
hs = [conv3x3(h, nf)]
for i_level in range(num_resolutions):
# Residual blocks for this resolution
for i_block in range(num_res_blocks):
h = ResnetBlock(out_ch=nf * ch_mult[i_level])(hs[-1], temb, train)
if h.shape[1] in attn_resolutions:
h = AttnBlock()(h)
hs.append(h)
if i_level != num_resolutions - 1:
hs.append(Downsample(with_conv=resamp_with_conv)(hs[-1]))
h = hs[-1]
h = ResnetBlock()(h, temb, train)
h = AttnBlock()(h)
h = ResnetBlock()(h, temb, train)
# Upsampling block
for i_level in reversed(range(num_resolutions)):
for i_block in range(num_res_blocks + 1):
h = ResnetBlock(out_ch=nf * ch_mult[i_level])(
jnp.concatenate([h, hs.pop()], axis=-1), temb, train
)
if h.shape[1] in attn_resolutions:
h = AttnBlock()(h)
if i_level != 0:
h = Upsample(with_conv=resamp_with_conv)(h)
assert not hs
h = act(normalize()(h))
h = conv3x3(h, x.shape[-1], init_scale=0.0)
return h