|
|
|
|
|
""" |
|
The 3D NEST transformer based segmentation model |
|
|
|
MASI Lab, Vanderbilty University |
|
|
|
|
|
Authors: Xin Yu, Yinchi Zhou, Yucheng Tang, Bennett Landman |
|
|
|
|
|
The NEST code is partly from |
|
|
|
Nested Hierarchical Transformer: Towards Accurate, Data-Efficient and |
|
Interpretable Visual Understanding |
|
https://arxiv.org/pdf/2105.12723.pdf |
|
|
|
""" |
|
from typing import Sequence, Tuple, Union |
|
|
|
import torch |
|
import torch.nn as nn |
|
from monai.networks.blocks import Convolution |
|
from monai.networks.blocks.dynunet_block import UnetOutBlock |
|
from scripts.networks.nest_transformer_3D import NestTransformer3D |
|
from scripts.networks.unest_block import UNesTBlock, UNesTConvBlock, UNestUpBlock |
|
|
|
|
|
class UNesT(nn.Module): |
|
""" |
|
UNesT model implementation |
|
""" |
|
|
|
def __init__( |
|
self, |
|
in_channels: int, |
|
out_channels: int, |
|
img_size: Sequence[int] = (96, 96, 96), |
|
feature_size: int = 16, |
|
patch_size: int = 2, |
|
depths: Sequence[int] = (2, 2, 2, 2), |
|
num_heads: Sequence[int] = (3, 6, 12, 24), |
|
embed_dim: Sequence[int] = (128, 256, 512), |
|
window_size: Sequence[int] = (7, 7, 7), |
|
norm_name: Union[Tuple, str] = "instance", |
|
conv_block: bool = False, |
|
res_block: bool = True, |
|
dropout_rate: float = 0.0, |
|
) -> None: |
|
""" |
|
Args: |
|
in_channels: dimension of input channels. |
|
out_channels: dimension of output channels. |
|
img_size: dimension of input image. |
|
feature_size: dimension of network feature size. |
|
hidden_size: dimension of hidden layer. |
|
mlp_dim: dimension of feedforward layer. |
|
num_heads: number of attention heads. |
|
pos_embed: position embedding layer type. |
|
norm_name: feature normalization type and arguments. |
|
conv_block: bool argument to determine if convolutional block is used. |
|
res_block: bool argument to determine if residual block is used. |
|
dropout_rate: faction of the input units to drop. |
|
|
|
Examples: |
|
|
|
# for single channel input 4-channel output with patch size of (96,96,96), feature size of 32 and batch norm |
|
>>> net = UNETR(in_channels=1, out_channels=4, img_size=(96,96,96), feature_size=32, norm_name='batch') |
|
|
|
# for 4-channel input 3-channel output with patch size of (128,128,128), conv position embedding and instance norm |
|
>>> net = UNETR(in_channels=4, out_channels=3, img_size=(128,128,128), pos_embed='conv', norm_name='instance') |
|
|
|
""" |
|
|
|
super().__init__() |
|
if not (0 <= dropout_rate <= 1): |
|
raise AssertionError("dropout_rate should be between 0 and 1.") |
|
self.embed_dim = embed_dim |
|
self.nestViT = NestTransformer3D( |
|
img_size=96, |
|
in_chans=1, |
|
patch_size=patch_size, |
|
num_levels=3, |
|
embed_dims=embed_dim, |
|
num_heads=num_heads, |
|
depths=depths, |
|
num_classes=1000, |
|
mlp_ratio=4.0, |
|
qkv_bias=True, |
|
drop_rate=0.0, |
|
attn_drop_rate=0.0, |
|
drop_path_rate=0.5, |
|
norm_layer=None, |
|
act_layer=None, |
|
pad_type="", |
|
weight_init="", |
|
global_pool="avg", |
|
) |
|
self.encoder1 = UNesTConvBlock( |
|
spatial_dims=3, |
|
in_channels=1, |
|
out_channels=feature_size * 2, |
|
kernel_size=3, |
|
stride=1, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.encoder2 = UNestUpBlock( |
|
spatial_dims=3, |
|
in_channels=self.embed_dim[0], |
|
out_channels=feature_size * 4, |
|
num_layer=1, |
|
kernel_size=3, |
|
stride=1, |
|
upsample_kernel_size=2, |
|
norm_name=norm_name, |
|
conv_block=False, |
|
res_block=False, |
|
) |
|
|
|
self.encoder3 = UNesTConvBlock( |
|
spatial_dims=3, |
|
in_channels=self.embed_dim[0], |
|
out_channels=8 * feature_size, |
|
kernel_size=3, |
|
stride=1, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.encoder4 = UNesTConvBlock( |
|
spatial_dims=3, |
|
in_channels=self.embed_dim[1], |
|
out_channels=16 * feature_size, |
|
kernel_size=3, |
|
stride=1, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.decoder5 = UNesTBlock( |
|
spatial_dims=3, |
|
in_channels=2 * self.embed_dim[2], |
|
out_channels=feature_size * 32, |
|
stride=1, |
|
kernel_size=3, |
|
upsample_kernel_size=2, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.decoder4 = UNesTBlock( |
|
spatial_dims=3, |
|
in_channels=self.embed_dim[2], |
|
out_channels=feature_size * 16, |
|
stride=1, |
|
kernel_size=3, |
|
upsample_kernel_size=2, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.decoder3 = UNesTBlock( |
|
spatial_dims=3, |
|
in_channels=feature_size * 16, |
|
out_channels=feature_size * 8, |
|
stride=1, |
|
kernel_size=3, |
|
upsample_kernel_size=2, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.decoder2 = UNesTBlock( |
|
spatial_dims=3, |
|
in_channels=feature_size * 8, |
|
out_channels=feature_size * 4, |
|
stride=1, |
|
kernel_size=3, |
|
upsample_kernel_size=2, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.decoder1 = UNesTBlock( |
|
spatial_dims=3, |
|
in_channels=feature_size * 4, |
|
out_channels=feature_size * 2, |
|
stride=1, |
|
kernel_size=3, |
|
upsample_kernel_size=2, |
|
norm_name=norm_name, |
|
res_block=res_block, |
|
) |
|
self.encoder10 = Convolution( |
|
spatial_dims=3, |
|
in_channels=32 * feature_size, |
|
out_channels=64 * feature_size, |
|
strides=2, |
|
adn_ordering="ADN", |
|
dropout=0.0, |
|
) |
|
self.out = UnetOutBlock(spatial_dims=3, in_channels=feature_size * 2, out_channels=out_channels) |
|
|
|
def proj_feat(self, x, hidden_size, feat_size): |
|
x = x.view(x.size(0), feat_size[0], feat_size[1], feat_size[2], hidden_size) |
|
x = x.permute(0, 4, 1, 2, 3).contiguous() |
|
return x |
|
|
|
def load_from(self, weights): |
|
with torch.no_grad(): |
|
|
|
for i in weights["state_dict"]: |
|
print(i) |
|
self.vit.patch_embedding.position_embeddings.copy_( |
|
weights["state_dict"]["module.transformer.patch_embedding.position_embeddings_3d"] |
|
) |
|
self.vit.patch_embedding.cls_token.copy_( |
|
weights["state_dict"]["module.transformer.patch_embedding.cls_token"] |
|
) |
|
self.vit.patch_embedding.patch_embeddings[1].weight.copy_( |
|
weights["state_dict"]["module.transformer.patch_embedding.patch_embeddings_3d.1.weight"] |
|
) |
|
self.vit.patch_embedding.patch_embeddings[1].bias.copy_( |
|
weights["state_dict"]["module.transformer.patch_embedding.patch_embeddings_3d.1.bias"] |
|
) |
|
|
|
|
|
for bname, block in self.vit.blocks.named_children(): |
|
print(block) |
|
block.loadFrom(weights, n_block=bname) |
|
|
|
self.vit.norm.weight.copy_(weights["state_dict"]["module.transformer.norm.weight"]) |
|
self.vit.norm.bias.copy_(weights["state_dict"]["module.transformer.norm.bias"]) |
|
|
|
def forward(self, x_in): |
|
x, hidden_states_out = self.nestViT(x_in) |
|
enc0 = self.encoder1(x_in) |
|
x1 = hidden_states_out[0] |
|
enc1 = self.encoder2(x1) |
|
x2 = hidden_states_out[1] |
|
enc2 = self.encoder3(x2) |
|
x3 = hidden_states_out[2] |
|
enc3 = self.encoder4(x3) |
|
x4 = hidden_states_out[3] |
|
enc4 = x4 |
|
dec4 = x |
|
dec4 = self.encoder10(dec4) |
|
dec3 = self.decoder5(dec4, enc4) |
|
dec2 = self.decoder4(dec3, enc3) |
|
dec1 = self.decoder3(dec2, enc2) |
|
dec0 = self.decoder2(dec1, enc1) |
|
out = self.decoder1(dec0, enc0) |
|
logits = self.out(out) |
|
return logits |
|
|