Image Feature Extraction
Transformers
Safetensors
skinmap
feature-extraction
dermatology
medical-imaging
embeddings
clip
custom_code
Instructions to use Digital-Dermatology/SkinMap with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Digital-Dermatology/SkinMap with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-feature-extraction", model="Digital-Dermatology/SkinMap", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Digital-Dermatology/SkinMap", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
| from functools import partial | |
| from typing import List | |
| import torch | |
| import torch.nn as nn | |
| from ..encoders.vision_transformer import Block, VisionTransformer | |
| from ..utils import get_2d_sincos_pos_embed | |
| class MaskedAutoencoderViT(VisionTransformer): | |
| """Masked Autoencoder with VisionTransformer backbone.""" | |
| def __init__( | |
| self, | |
| img_size: List[int] = [224], | |
| patch_size: int = 16, | |
| in_channels: int = 3, | |
| num_classes: int = 0, | |
| embed_dim: int = 768, | |
| depth: int = 12, | |
| num_heads: int = 12, | |
| mlp_ratio: float = 4.0, | |
| qkv_bias: bool = False, | |
| qk_scale=None, | |
| drop_rate: float = 0.0, | |
| attn_drop_rate: float = 0.0, | |
| drop_path_rate: float = 0.0, | |
| norm_layer: nn.Module = nn.LayerNorm, | |
| return_all_tokens: bool = False, | |
| masked_im_modeling: bool = False, | |
| decoder_embed_dim: int = 512, | |
| decoder_depth: int = 8, | |
| decoder_num_heads: int = 16, | |
| ): | |
| super().__init__( | |
| img_size=img_size, | |
| patch_size=patch_size, | |
| in_channels=in_channels, | |
| num_classes=num_classes, | |
| embed_dim=embed_dim, | |
| depth=depth, | |
| num_heads=num_heads, | |
| mlp_ratio=mlp_ratio, | |
| qkv_bias=qkv_bias, | |
| qk_scale=qk_scale, | |
| drop_rate=drop_rate, | |
| attn_drop_rate=attn_drop_rate, | |
| drop_path_rate=drop_path_rate, | |
| norm_layer=norm_layer, | |
| return_all_tokens=return_all_tokens, | |
| masked_im_modeling=masked_im_modeling, | |
| ) | |
| num_patches = self.patch_embed.num_patches | |
| # fixed sin-cos embedding | |
| self.pos_embed.requires_grad = False | |
| # MAE decoder specifics | |
| self.decoder_embed = nn.Linear(embed_dim, decoder_embed_dim, bias=True) | |
| self.mask_token = nn.Parameter(torch.zeros(1, 1, decoder_embed_dim)) | |
| # fixed sin-cos embedding | |
| self.decoder_pos_embed = nn.Parameter( | |
| data=torch.zeros(1, num_patches + 1, decoder_embed_dim), | |
| requires_grad=False, | |
| ) | |
| self.decoder_blocks = nn.ModuleList( | |
| [ | |
| Block( | |
| dim=decoder_embed_dim, | |
| num_heads=decoder_num_heads, | |
| mlp_ratio=mlp_ratio, | |
| qkv_bias=True, | |
| qk_scale=None, | |
| norm_layer=norm_layer, | |
| ) | |
| for _ in range(decoder_depth) | |
| ] | |
| ) | |
| # decoder to patch | |
| self.decoder_norm = norm_layer(decoder_embed_dim) | |
| self.decoder_pred = nn.Linear( | |
| decoder_embed_dim, | |
| patch_size**2 * in_channels, | |
| bias=True, | |
| ) | |
| self.initialize_weights() | |
| def initialize_weights(self): | |
| # initialize (and freeze) pos_embed by sin-cos embedding | |
| pos_embed = get_2d_sincos_pos_embed( | |
| self.pos_embed.shape[-1], | |
| int(self.patch_embed.num_patches**0.5), | |
| cls_token=True, | |
| ) | |
| self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0)) | |
| decoder_pos_embed = get_2d_sincos_pos_embed( | |
| self.decoder_pos_embed.shape[-1], | |
| int(self.patch_embed.num_patches**0.5), | |
| cls_token=True, | |
| ) | |
| self.decoder_pos_embed.data.copy_( | |
| torch.from_numpy(decoder_pos_embed).float().unsqueeze(0) | |
| ) | |
| # initialize patch_embed like nn.Linear (instead of nn.Conv2d) | |
| w = self.patch_embed.proj.weight.data | |
| torch.nn.init.xavier_uniform_(w.view([w.shape[0], -1])) | |
| # timm's trunc_normal_(std=.02) is effectively normal_(std=0.02) as cutoff is too big (2.) | |
| torch.nn.init.normal_(self.cls_token, std=0.02) | |
| torch.nn.init.normal_(self.mask_token, std=0.02) | |
| # initialize nn.Linear and nn.LayerNorm | |
| self.apply(self._init_weights) | |
| def _init_weights(self, m): | |
| if isinstance(m, nn.Linear): | |
| # we use xavier_uniform following official JAX ViT: | |
| torch.nn.init.xavier_uniform_(m.weight) | |
| if isinstance(m, nn.Linear) and m.bias is not None: | |
| nn.init.constant_(m.bias, 0) | |
| elif isinstance(m, nn.LayerNorm): | |
| nn.init.constant_(m.bias, 0) | |
| nn.init.constant_(m.weight, 1.0) | |
| def prepare_tokens( | |
| self, | |
| x: torch.Tensor, | |
| mask_ratio: float = 0.0, | |
| return_helpers: bool = False, | |
| **kwargs, | |
| ): | |
| # embed patches | |
| x = self.patch_embed(x) | |
| x = x.flatten(2).transpose(1, 2) | |
| # add pos embed w/o cls token | |
| x = x + self.pos_embed[:, 1:, :] | |
| # Perform per-sample random masking by per-sample shuffling. | |
| # Per-sample shuffling is done by argsort random noise. | |
| N, N_PATCHES, D = x.shape | |
| len_keep = int(N_PATCHES * (1 - mask_ratio)) | |
| # generate random noise for the selection (in [0, 1]) | |
| noise = torch.rand(N, N_PATCHES, device=x.device) | |
| # sort noise for each sample (ascend: small is keep, large is remove) | |
| ids_shuffle = torch.argsort(noise, dim=1) | |
| # IDs to restore the mask (used for the decoder) | |
| ids_restore = torch.argsort(ids_shuffle, dim=1) | |
| # keep the first subset (random ids to keep from every sample) | |
| ids_keep = ids_shuffle[:, :len_keep] | |
| # reshape for alignment with "x" | |
| ids_keep = ids_keep.unsqueeze(-1).repeat(1, 1, D) | |
| # masked input | |
| x_masked = torch.gather(x, dim=1, index=ids_keep) | |
| # generate the binary mask: 0 is keep, 1 is remove | |
| mask = torch.ones([N, N_PATCHES], device=x.device) | |
| mask[:, :len_keep] = 0 | |
| # unshuffle to get the binary mask | |
| # binary mask used to create "x_masked" | |
| mask = torch.gather(mask, dim=1, index=ids_restore) | |
| # append cls token | |
| cls_token = self.cls_token + self.pos_embed[:, :1, :] | |
| cls_tokens = cls_token.expand(x_masked.shape[0], -1, -1) | |
| x_masked = torch.cat((cls_tokens, x_masked), dim=1) | |
| if return_helpers: | |
| return x_masked, mask, ids_restore | |
| else: | |
| return x_masked | |
| def forward_decoder(self, x: torch.Tensor, ids_restore: torch.Tensor): | |
| # embed tokens | |
| x = self.decoder_embed(x) | |
| # append mask tokens to sequence | |
| mask_tokens = self.mask_token.repeat( | |
| x.shape[0], ids_restore.shape[1] + 1 - x.shape[1], 1 | |
| ) | |
| x_ = torch.cat([x[:, 1:, :], mask_tokens], dim=1) # no cls token | |
| x_ = torch.gather( | |
| x_, dim=1, index=ids_restore.unsqueeze(-1).repeat(1, 1, x.shape[2]) | |
| ) # unshuffle | |
| x = torch.cat([x[:, :1, :], x_], dim=1) # append cls token | |
| # add pos embed | |
| x = x + self.decoder_pos_embed | |
| # apply Transformer blocks | |
| for blk in self.decoder_blocks: | |
| x = blk(x) | |
| x = self.decoder_norm(x) | |
| # predictor projection | |
| x = self.decoder_pred(x) | |
| # remove cls token | |
| x = x[:, 1:, :] | |
| return x | |
| def forward( | |
| self, | |
| imgs: torch.Tensor, | |
| mask_ratio: float = 0.75, | |
| return_all_tokens=None, | |
| **kwargs, | |
| ): | |
| x, mask, ids_restore = self.prepare_tokens( | |
| x=imgs, | |
| mask_ratio=mask_ratio, | |
| return_helpers=True, | |
| ) | |
| for blk in self.blocks: | |
| x = blk(x) | |
| latent = self.norm(x) | |
| # [N, N_PATCHES, p*p*3] | |
| pred = self.forward_decoder(x=latent, ids_restore=ids_restore) | |
| return_all_tokens = ( | |
| self.return_all_tokens if return_all_tokens is None else return_all_tokens | |
| ) | |
| if return_all_tokens: | |
| return imgs, pred, mask, latent | |
| return imgs, pred, mask, latent[:, 0] | |
| def masked_vit_tiny(patch_size: int = 16, **kwargs): | |
| model = MaskedAutoencoderViT( | |
| patch_size=patch_size, | |
| embed_dim=192, | |
| depth=12, | |
| num_heads=3, | |
| mlp_ratio=4, | |
| qkv_bias=True, | |
| norm_layer=partial(nn.LayerNorm, eps=1e-6), | |
| decoder_embed_dim=512, | |
| decoder_depth=8, | |
| decoder_num_heads=16, | |
| **kwargs, | |
| ) | |
| return model | |
| def masked_vit_small(patch_size: int = 16, **kwargs): | |
| model = MaskedAutoencoderViT( | |
| patch_size=patch_size, | |
| embed_dim=384, | |
| depth=12, | |
| num_heads=6, | |
| mlp_ratio=4, | |
| qkv_bias=True, | |
| norm_layer=partial(nn.LayerNorm, eps=1e-6), | |
| decoder_embed_dim=512, | |
| decoder_depth=8, | |
| decoder_num_heads=16, | |
| **kwargs, | |
| ) | |
| return model | |
| def masked_vit_base(patch_size: int = 16, **kwargs): | |
| model = MaskedAutoencoderViT( | |
| patch_size=patch_size, | |
| embed_dim=768, | |
| depth=12, | |
| num_heads=12, | |
| mlp_ratio=4, | |
| qkv_bias=True, | |
| norm_layer=partial(nn.LayerNorm, eps=1e-6), | |
| decoder_embed_dim=512, | |
| decoder_depth=8, | |
| decoder_num_heads=16, | |
| **kwargs, | |
| ) | |
| return model | |
| def masked_vit_large(patch_size: int = 16, **kwargs): | |
| model = MaskedAutoencoderViT( | |
| patch_size=patch_size, | |
| embed_dim=1024, | |
| depth=24, | |
| num_heads=16, | |
| mlp_ratio=4, | |
| qkv_bias=True, | |
| norm_layer=partial(nn.LayerNorm, eps=1e-6), | |
| decoder_embed_dim=512, | |
| decoder_depth=8, | |
| decoder_num_heads=16, | |
| **kwargs, | |
| ) | |
| return model | |