Spaces:
Runtime error
Runtime error
| from math import pi | |
| from functools import wraps | |
| from einops import rearrange, repeat | |
| from einops.layers.torch import Reduce | |
| import torch | |
| from torch import nn, einsum | |
| import torch.nn.functional as F | |
| def print_trainable_parameters(model: torch.nn) -> None: | |
| """Print number of trainable parameters.""" | |
| trainable_params = 0 | |
| all_param = 0 | |
| for _, param in model.named_parameters(): | |
| all_param += param.numel() | |
| if param.requires_grad: | |
| trainable_params += param.numel() | |
| print( | |
| f"trainable params: {trainable_params} || all params: {all_param}" | |
| f" || trainable%: {100 * trainable_params / all_param:.2f}" | |
| ) | |
| def exists(val): | |
| return val is not None | |
| def default(val, d): | |
| return val if exists(val) else d | |
| def cache_fn(f): | |
| cache = dict() | |
| def cached_fn(*args, _cache = True, key = None, **kwargs): | |
| if not _cache: | |
| return f(*args, **kwargs) | |
| nonlocal cache | |
| if key in cache: | |
| return cache[key] | |
| result = f(*args, **kwargs) | |
| cache[key] = result | |
| return result | |
| return cached_fn | |
| def fourier_encode(x, max_freq, num_bands = 4): | |
| x = x.unsqueeze(-1) | |
| device, dtype, orig_x = x.device, x.dtype, x | |
| scales = torch.linspace(1., max_freq / 2, num_bands, device = device, dtype = dtype) | |
| scales = scales[(*((None,) * (len(x.shape) - 1)), Ellipsis)] | |
| x = x * scales * pi | |
| x = torch.cat([x.sin(), x.cos()], dim = -1) | |
| x = torch.cat((x, orig_x), dim = -1) | |
| return x | |
| # helper classes | |
| class PreNorm(nn.Module): | |
| def __init__(self, dim, fn, context_dim = None): | |
| super().__init__() | |
| self.fn = fn | |
| self.norm = nn.LayerNorm(dim) | |
| self.norm_context = nn.LayerNorm(context_dim) if exists(context_dim) else None | |
| def forward(self, x, **kwargs): | |
| x = self.norm(x) | |
| if exists(self.norm_context): | |
| context = kwargs['context'] | |
| normed_context = self.norm_context(context) | |
| kwargs.update(context = normed_context) | |
| return self.fn(x, **kwargs) | |
| class GEGLU(nn.Module): | |
| def forward(self, x): | |
| x, gates = x.chunk(2, dim = -1) | |
| return x * F.gelu(gates) | |
| class FeedForward(nn.Module): | |
| def __init__(self, dim, mult = 4, dropout = 0.): | |
| super().__init__() | |
| self.net = nn.Sequential( | |
| nn.Linear(dim, dim * mult * 2), | |
| GEGLU(), | |
| nn.Linear(dim * mult, dim), | |
| nn.Dropout(dropout) | |
| ) | |
| def forward(self, x): | |
| return self.net(x) | |
| class Attention(nn.Module): | |
| def __init__(self, query_dim, context_dim = None, heads = 8, dim_head = 64, dropout = 0., scale=None): | |
| super().__init__() | |
| inner_dim = dim_head * heads | |
| context_dim = default(context_dim, query_dim) | |
| if scale: | |
| self.scale = scale #**-1 | |
| else: | |
| self.scale = dim_head ** -0.5 | |
| self.heads = heads | |
| self.to_q = nn.Linear(query_dim, inner_dim, bias = False) | |
| self.to_kv = nn.Linear(context_dim, inner_dim * 2, bias = False) | |
| self.dropout = nn.Dropout(dropout) | |
| self.to_out = nn.Linear(inner_dim, query_dim) | |
| def forward(self, x, context=None, mask=None): | |
| h = self.heads | |
| q = self.to_q(x) | |
| context = default(context, x) | |
| k, v = self.to_kv(context).chunk(2, dim = -1) | |
| q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h = h), (q, k, v)) | |
| sim = einsum('b i d, b j d -> b i j', q, k) * self.scale | |
| if exists(mask): | |
| mask = rearrange(mask, 'b ... -> b (...)') | |
| max_neg_value = -torch.finfo(sim.dtype).max | |
| mask = repeat(mask, 'b j -> (b h) () j', h = h) | |
| sim.masked_fill_(~mask, max_neg_value) | |
| # attention, what we cannot get enough of | |
| A = sim.softmax(dim = -1) | |
| attn = self.dropout(A) | |
| out = einsum('b i j, b j d -> b i d', attn, v) | |
| out = rearrange(out, '(b h) n d -> b n (h d)', h = h) | |
| if context.shape != x.shape: | |
| return self.to_out(out), A | |
| else: | |
| return self.to_out(out) | |
| class DualQueryCrossAttention(nn.Module): | |
| def __init__(self, query_dim, context_dim = None, heads = 8, dim_head = 64, dropout = 0., scale=None): | |
| super().__init__() | |
| inner_dim = dim_head * heads | |
| context_dim = default(context_dim, query_dim) | |
| if scale: | |
| self.scale = nn.Parameter(torch.tensor([scale])) #**-1 | |
| else: | |
| self.scale = dim_head ** -0.5 | |
| self.heads = heads | |
| self.to_q = nn.Linear(query_dim, inner_dim, bias = False) | |
| self.to_kv = nn.Linear(context_dim, inner_dim * 2, bias = False) | |
| self.dropout = nn.Dropout(dropout) | |
| self.to_out = nn.Linear(inner_dim, query_dim) | |
| # Attention ranking | |
| self.to_score_q = nn.Linear(query_dim, inner_dim, bias = False) | |
| self.to_score_out = nn.Linear(inner_dim, query_dim) | |
| def forward(self, x, score_x, context=None, mask=None): | |
| h = self.heads | |
| q = self.to_q(x) | |
| score_q = self.to_score_q(score_x) | |
| k, v = self.to_kv(context).chunk(2, dim = -1) | |
| q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h = h), (q, k, v)) | |
| sim = einsum('b i d, b j d -> b i j', q, k) * self.scale | |
| score_sim = einsum('b i d, b j d -> b i j', score_q, k) * self.scale | |
| if exists(mask): | |
| mask = rearrange(mask, 'b ... -> b (...)') | |
| max_neg_value = -torch.finfo(sim.dtype).max | |
| mask = repeat(mask, 'b j -> (b h) () j', h = h) | |
| sim.masked_fill_(~mask, max_neg_value) | |
| # attention, what we cannot get enough of | |
| A = sim.softmax(dim = -1) | |
| attn = self.dropout(A) | |
| score_attn = score_sim.softmax(dim = -1) | |
| out = einsum('b i j, b j d -> b i d', attn, v) | |
| out = rearrange(out, '(b h) n d -> b n (h d)', h = h) | |
| score_out = einsum('b i j, b j d -> b i d', score_attn, v) | |
| score_out = rearrange(score_out, '(b h) n d -> b n (h d)', h = h) | |
| return self.to_out(out), A, self.to_score_out(score_out), score_attn | |
| # Based on the merging approach from Truong et al. "How Transferable are Self-supervised Features in Medical Image Classification Tasks?" | |
| class Merger(nn.Module): | |
| def __init__(self, proj_dim): | |
| super(Merger, self).__init__() | |
| self.vit_head = nn.Linear(384, proj_dim) | |
| self.swin_head = nn.Linear(768, proj_dim) | |
| self.swav_head = nn.Linear(2048, proj_dim) | |
| def forward(self, data): | |
| vit_out = self.vit_head(data['vit_feats']) | |
| swin_out = self.swin_head(data['swin_feats']) | |
| swav_out = self.swav_head(data['swav_feats']) | |
| joint = torch.cat([vit_out, swin_out, swav_out], dim=-1) | |
| return joint | |
| class Perceiver(nn.Module): | |
| def __init__( | |
| self, | |
| *, | |
| num_freq_bands, | |
| depth, | |
| max_freq, | |
| input_channels = 3, | |
| input_axis = 2, | |
| num_latents = 1024, | |
| latent_dim = 512, | |
| cross_heads = 1, | |
| latent_heads = 8, | |
| cross_dim_head = 64, | |
| latent_dim_head = 64, | |
| n_classes = 1000, | |
| attn_dropout = 0., | |
| ff_dropout = 0., | |
| weight_tie_layers = False, | |
| fourier_encode_data = True, | |
| self_per_cross_attn = 1, | |
| latent_bounds = 2, | |
| scale = None, | |
| ): | |
| """The shape of the final attention mechanism will be: | |
| depth * (cross attention -> self_per_cross_attn * self attention) | |
| Args: | |
| num_freq_bands: Number of freq bands, with original value (2 * K + 1) | |
| depth: Depth of net. | |
| max_freq: Maximum frequency, hyperparameter depending on how | |
| fine the data is. | |
| freq_base: Base for the frequency | |
| input_channels: Number of channels for each token of the input. | |
| input_axis: Number of axes for input data (2 for images, 3 for video) | |
| num_latents: Number of latents, or induced set points, or centroids. | |
| Different papers giving it different names. | |
| latent_dim: Latent dimension. | |
| cross_heads: Number of heads for cross attention. Paper said 1. | |
| latent_heads: Number of heads for latent self attention, 8. | |
| cross_dim_head: Number of dimensions per cross attention head. | |
| latent_dim_head: Number of dimensions per latent self attention head. | |
| num_classes: Output number of classes. | |
| attn_dropout: Attention dropout | |
| ff_dropout: Feedforward dropout | |
| weight_tie_layers: Whether to weight tie layers (optional). | |
| fourier_encode_data: Whether to auto-fourier encode the data, using | |
| the input_axis given. defaults to True, but can be turned off | |
| if you are fourier encoding the data yourself. | |
| self_per_cross_attn: Number of self attention blocks per cross attn. | |
| final_classifier_head: mean pool and project embeddings to number of classes (num_classes) at the end | |
| """ | |
| super().__init__() | |
| self.input_axis = input_axis | |
| self.max_freq = max_freq | |
| self.num_freq_bands = num_freq_bands | |
| self.n_classes = n_classes | |
| self.fourier_encode_data = fourier_encode_data | |
| fourier_channels = (input_axis * ((num_freq_bands * 2) + 1)) if fourier_encode_data else 0 | |
| self.proj_embeddings = nn.Identity() | |
| input_dim = fourier_channels + input_channels | |
| self.latents = nn.Parameter( | |
| torch.nn.init.trunc_normal_( | |
| torch.zeros((num_latents, latent_dim)), | |
| mean=0, | |
| std=0.02, | |
| a=-latent_bounds, | |
| b=latent_bounds)) | |
| self.score_latents = nn.Parameter( | |
| torch.nn.init.trunc_normal_( | |
| torch.zeros((1, latent_dim)), | |
| mean=0, | |
| std=0.02, | |
| a=-latent_bounds, | |
| b=latent_bounds)) | |
| # Cross-Attention Layer | |
| get_cross_attn = lambda: PreNorm(latent_dim, DualQueryCrossAttention(latent_dim, input_dim, heads = cross_heads, dim_head = cross_dim_head, dropout = attn_dropout, scale=scale), context_dim = input_dim) #new | |
| get_cross_ff = lambda: PreNorm(latent_dim, FeedForward(latent_dim, dropout = ff_dropout)) | |
| get_mil_ff = lambda: PreNorm(latent_dim, FeedForward(latent_dim, dropout = ff_dropout)) | |
| get_latent_attn = lambda: PreNorm(latent_dim, Attention(latent_dim, heads = latent_heads, dim_head = latent_dim_head, dropout = attn_dropout)) | |
| get_latent_ff = lambda: PreNorm(latent_dim, FeedForward(latent_dim, dropout = ff_dropout)) | |
| get_cross_attn, get_cross_ff, get_latent_attn, get_latent_ff, get_mil_ff = map(cache_fn, (get_cross_attn, get_cross_ff, get_latent_attn, get_latent_ff, get_mil_ff)) | |
| self.layers = nn.ModuleList([]) | |
| for i in range(depth): | |
| should_cache = i > 0 and weight_tie_layers | |
| cache_args = {'_cache': should_cache} | |
| self_attns = nn.ModuleList([]) | |
| for block_ind in range(self_per_cross_attn): | |
| self_attns.append(nn.ModuleList([ | |
| get_latent_attn(**cache_args, key = block_ind), | |
| get_latent_ff(**cache_args, key = block_ind) | |
| ])) | |
| self.layers.append(nn.ModuleList([ | |
| get_cross_attn(**cache_args), | |
| get_cross_ff(**cache_args), | |
| get_mil_ff(**cache_args), | |
| self_attns | |
| ])) | |
| self.to_logits = nn.Sequential( | |
| Reduce('b n d -> b d', 'mean'), | |
| nn.LayerNorm(latent_dim), | |
| nn.Linear(latent_dim, n_classes) | |
| ) | |
| self.to_score_logits = nn.Sequential( | |
| Reduce('b n d -> b d', 'mean'), | |
| nn.LayerNorm(latent_dim), | |
| nn.Linear(latent_dim, n_classes) | |
| ) | |
| def forward( | |
| self, | |
| data, | |
| mask = None, | |
| return_embeddings = False, | |
| ): | |
| data = self.proj_embeddings(data) | |
| if len(data.shape)==2: # flops | |
| data= data.unsqueeze(0) # flops | |
| b, *axis, _, device, dtype = *data.shape, data.device, data.dtype | |
| assert len(axis) == self.input_axis, 'input data must have the right number of axis' | |
| if self.fourier_encode_data: | |
| # calculate fourier encoded positions in the range of [-1, 1], for all axis | |
| axis_pos = list(map(lambda size: torch.linspace(-1., 1., steps=size, device=device, dtype=dtype), axis)) | |
| pos = torch.stack(torch.meshgrid(*axis_pos, indexing = 'ij'), dim = -1) | |
| enc_pos = fourier_encode(pos, self.max_freq, self.num_freq_bands) | |
| enc_pos = rearrange(enc_pos, '... n d -> ... (n d)') | |
| enc_pos = repeat(enc_pos, '... -> b ...', b = b) | |
| data = torch.cat((data, enc_pos), dim = -1) | |
| # concat to channels of data and flatten axis | |
| data = rearrange(data, 'b ... d -> b (...) d') | |
| x = repeat(self.latents, 'n d -> b n d', b = b) | |
| score_x = repeat(self.score_latents, 'n d -> b n d', b = b) | |
| # layers | |
| for cross_attn, cross_ff, mil_ff, self_attns in self.layers: | |
| x_attn, A_raw, score_x_attn, score_A = cross_attn(x=x, score_x=score_x, context=data, mask=mask) | |
| x = x_attn + x | |
| x = cross_ff(x) + x | |
| score_x = score_x_attn + score_x | |
| score_x = mil_ff(score_x) + score_x | |
| for self_attn, self_ff in self_attns: | |
| x = self_attn(x) + x | |
| x = self_ff(x) + x | |
| # to logits | |
| logits = self.to_logits(x) | |
| results_dict={'student_logits':self.to_score_logits(score_x), 'features_teacher':x, 'features_student':score_x} | |
| Y_hat = torch.argmax(logits, dim=1) | |
| Y_prob = F.softmax(logits, dim = 1) | |
| return logits, Y_prob, Y_hat, score_A, results_dict | |