Download model/Vision_encoder/models_mae.py from yyyang0/VETime: direct link, hf CLI and curl.
- Browser
- Download file 14.3 kB
-
https://huggingface.co/yyyang0/VETime/resolve/main/model/Vision_encoder/models_mae.py
- Command line
-
hf download hf://yyyang0/VETime/model/Vision_encoder/models_mae.py
-
curl -L -o models_mae.py https://huggingface.co/yyyang0/VETime/resolve/main/model/Vision_encoder/models_mae.py
14.3 kB
| # Copyright (c) Meta Platforms, Inc. and affiliates. | |
| # All rights reserved. | |
| # This source code is licensed under the license found in the | |
| # LICENSE file in the root directory of this source tree. | |
| # -------------------------------------------------------- | |
| # References: | |
| # timm: https://github.com/rwightman/pytorch-image-models/tree/master/timm | |
| # DeiT: https://github.com/facebookresearch/deit | |
| # -------------------------------------------------------- | |
| from functools import partial | |
| import torch | |
| import torch.nn as nn | |
| import numpy as np | |
| from timm.models.vision_transformer import PatchEmbed, Block | |
| class MaskedAutoencoderViT(nn.Module): | |
| """ Masked Autoencoder with VisionTransformer backbone | |
| """ | |
| def __init__(self, img_size=224, patch_size=16, in_chans=3, | |
| embed_dim=1024, depth=24, num_heads=16, | |
| decoder_embed_dim=512, decoder_depth=8, decoder_num_heads=16, | |
| mlp_ratio=4., norm_layer=partial(nn.LayerNorm, eps=1e-6), norm_pix_loss=False, | |
| quantile=False, quantile_head_num=9,**kwargs): | |
| super().__init__() | |
| # add for quantile forecasting | |
| self.norm_pix_loss = norm_pix_loss | |
| self.quantile = quantile | |
| self.quantile_head_num = quantile_head_num | |
| # -------------------------------------------------------------------------- | |
| # MAE encoder specifics | |
| self.patch_embed = PatchEmbed(img_size, patch_size, in_chans, embed_dim) | |
| num_patches = self.patch_embed.num_patches | |
| self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) | |
| self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim), requires_grad=False) # fixed sin-cos embedding | |
| self.blocks = nn.ModuleList([ | |
| Block(embed_dim, num_heads, mlp_ratio, qkv_bias=True, norm_layer=norm_layer) | |
| for i in range(depth)]) | |
| self.norm = norm_layer(embed_dim) | |
| # -------------------------------------------------------------------------- | |
| # -------------------------------------------------------------------------- | |
| # 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)) | |
| self.decoder_pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, decoder_embed_dim), requires_grad=False) # fixed sin-cos embedding | |
| self.decoder_blocks = nn.ModuleList([ | |
| Block(decoder_embed_dim, decoder_num_heads, mlp_ratio, qkv_bias=True, norm_layer=norm_layer) | |
| for i in range(decoder_depth)]) | |
| self.decoder_norm = norm_layer(decoder_embed_dim) | |
| # prediction head | |
| self.decoder_pred = nn.Linear(decoder_embed_dim, patch_size**2 * in_chans, bias=True) # decoder to patch | |
| if self.quantile: | |
| # adds quantile outputs: | |
| # we need 9 outputs in total, corresponding to 10%, 20%, ..., 90% | |
| # So other 8 heads are needed to generate other quantile outputs. | |
| self.decoder_pred_quantile_list = nn.ModuleList() | |
| for i in range(self.quantile_head_num-1): | |
| self.decoder_pred_quantile_list.append(nn.Linear(decoder_embed_dim, patch_size**2 * in_chans, bias=True)) # decoder to patch | |
| # -------------------------------------------------------------------------- | |
| self.initialize_weights() | |
| def initialize_weights(self): | |
| # initialization | |
| # 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**.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**.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=.02) | |
| torch.nn.init.normal_(self.mask_token, std=.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 patchify(self, imgs): | |
| """ | |
| imgs: (N, 3, H, W) | |
| x: (N, L, patch_size**2 * 3) | |
| """ | |
| p = self.patch_embed.patch_size[0] | |
| assert imgs.shape[2] == imgs.shape[3] and imgs.shape[2] % p == 0 | |
| h = w = imgs.shape[2] // p | |
| x = imgs.reshape(shape=(imgs.shape[0], 3, h, p, w, p)) | |
| x = torch.einsum('nchpwq->nhwpqc', x) | |
| x = x.reshape(shape=(imgs.shape[0], h * w, p**2 * 3)) | |
| return x | |
| def unpatchify(self, x, n_channels=3): | |
| """ | |
| x: (N, L, patch_size**2 * n_channels) | |
| imgs: (N, 3, H, W) | |
| """ | |
| p = self.patch_embed.patch_size[0] | |
| h = w = int(x.shape[1]**.5) | |
| assert h * w == x.shape[1] | |
| # x = x.reshape(shape=(x.shape[0], h, w, p, p, 3)) | |
| x = x.reshape(shape=(x.shape[0], h, w, p, p, n_channels)) | |
| x = torch.einsum('nhwpqc->nchpwq', x) | |
| # imgs = x.reshape(shape=(x.shape[0], 3, h * p, h * p)) | |
| imgs = x.reshape(shape=(x.shape[0], n_channels, h * p, h * p)) | |
| return imgs | |
| def random_masking(self, x, mask_ratio, noise=None): | |
| """ | |
| Perform per-sample random masking by per-sample shuffling. | |
| Per-sample shuffling is done by argsort random noise. | |
| x: [N, L, D], sequence | |
| noise: [N, L] | |
| """ | |
| N, L, D = x.shape # batch, length, dim | |
| len_keep = int(round(L * (1 - mask_ratio))) | |
| if noise is None: | |
| noise = torch.rand(N, L, device=x.device) # noise in [0, 1] | |
| # sort noise for each sample | |
| ids_shuffle = torch.argsort(noise, dim=1) # ascend: small is keep, large is remove | |
| ids_restore = torch.argsort(ids_shuffle, dim=1) | |
| # keep the first subset | |
| ids_keep = ids_shuffle[:, :len_keep] | |
| x_masked = torch.gather(x, dim=1, index=ids_keep.unsqueeze(-1).repeat(1, 1, D)) | |
| # generate the binary mask: 0 is keep, 1 is remove | |
| mask = torch.ones([N, L], device=x.device) | |
| mask[:, :len_keep] = 0 | |
| # unshuffle to get the binary mask | |
| mask = torch.gather(mask, dim=1, index=ids_restore) | |
| return x_masked, mask, ids_restore | |
| def forward_encoder(self, x, mask_ratio, noise=None,use_multiscale=True): | |
| # embed patches | |
| x = self.patch_embed(x) | |
| x = x + self.pos_embed[:, 1:, :] | |
| # x = x + self.pos_embed[:, 1:, :]+pos | |
| # masking: length -> length * mask_ratio | |
| x, mask, ids_restore = self.random_masking(x, mask_ratio, noise) | |
| # append cls token | |
| cls_token = self.cls_token + self.pos_embed[:, :1, :] | |
| cls_tokens = cls_token.expand(x.shape[0], -1, -1) | |
| x = torch.cat((cls_tokens, x), dim=1) | |
| total_depth = len(self.blocks) | |
| part_size = total_depth // 4 | |
| intermediate_features = [] | |
| # apply Transformer blocks | |
| for i, blk in enumerate(self.blocks): | |
| x = blk(x) | |
| if use_multiscale and (i + 1) % part_size == 0: | |
| intermediate_features.append(x) | |
| if use_multiscale: | |
| fused = torch.stack(intermediate_features, dim=0).sum(dim=0) # [N, L, D] | |
| fused = self.norm(fused) | |
| return fused, mask, ids_restore | |
| else: | |
| x = self.norm(x) | |
| return x, mask, ids_restore | |
| def forward_decoder(self, x, ids_restore): | |
| # 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 | |
| if not self.quantile: | |
| x = self.decoder_pred(x) | |
| x = x[:, 1:, :] # remove cls token | |
| return x | |
| else: | |
| # first calculate the 50% quantile value | |
| x_mid = self.decoder_pred(x)[:, 1:, :] # [batch, ] | |
| # then calculate the other quantile values | |
| x_quantile_list = [] | |
| for i in range(self.quantile_head_num-1): | |
| x_quantile = self.decoder_pred_quantile_list[i](x)[:, 1:, :] | |
| x_quantile_list.append(x_quantile) | |
| # this will generate 9 groups of data | |
| return x_mid, x_quantile_list | |
| def forward_loss(self, imgs, pred, mask): | |
| """ | |
| imgs: [N, 3, H, W] | |
| pred: [N, L, p*p*3] | |
| mask: [N, L], 0 is keep, 1 is remove, | |
| """ | |
| target = self.patchify(imgs) | |
| if self.norm_pix_loss: | |
| mean = target.mean(dim=-1, keepdim=True) | |
| var = target.var(dim=-1, keepdim=True) | |
| target = (target - mean) / (var + 1.e-6)**.5 | |
| loss = (pred - target) ** 2 | |
| loss = loss.mean(dim=-1) # [N, L], mean loss per patch | |
| loss = (loss * mask).sum() / mask.sum() # mean loss on removed patches | |
| return loss | |
| def forward(self, imgs, mask_ratio=0.0, noise=None): | |
| latent, mask, ids_restore = self.forward_encoder(imgs, mask_ratio, noise) | |
| pred = self.forward_decoder(latent, ids_restore) # [N, L, p*p*3] | |
| # loss = self.forward_loss(imgs, pred, mask) | |
| return latent, pred, mask | |
| from transformers import PretrainedConfig | |
| class MAEConfig(PretrainedConfig): | |
| model_type = "mae_vit" | |
| def __init__( | |
| self, | |
| patch_size=16, | |
| embed_dim=768, | |
| depth=12, | |
| num_heads=12, | |
| mlp_ratio=4.0, | |
| norm_layer_eps=1e-6, | |
| decoder_embed_dim=512, | |
| decoder_depth=8, | |
| decoder_num_heads=16, | |
| in_chans=3, | |
| img_size=224, | |
| mask_ratio=0.4, | |
| **kwargs | |
| ): | |
| super().__init__(**kwargs) | |
| self.patch_size = patch_size | |
| self.embed_dim = embed_dim | |
| self.depth = depth | |
| self.num_heads = num_heads | |
| self.mlp_ratio = mlp_ratio | |
| self.norm_layer_eps = norm_layer_eps | |
| self.decoder_embed_dim = decoder_embed_dim | |
| self.decoder_depth = decoder_depth | |
| self.decoder_num_heads = decoder_num_heads | |
| self.in_chans = in_chans | |
| self.img_size = img_size | |
| self.mask_ratio = mask_ratio | |
| def get_mae_base_config(): | |
| return dict( | |
| patch_size=16, | |
| embed_dim=768, | |
| depth=12, | |
| num_heads=12, | |
| decoder_embed_dim=512, | |
| decoder_depth=8, | |
| decoder_num_heads=16, | |
| mlp_ratio=4.0, | |
| norm_layer=partial(nn.LayerNorm, eps=1e-6), | |
| ) | |
| def get_mae_large_config(): | |
| config = get_mae_base_config() | |
| config.update( | |
| embed_dim=1024, | |
| depth=24, | |
| num_heads=16, | |
| ) | |
| return config | |
| def get_mae_huge_config(): | |
| config = get_mae_base_config() | |
| config.update( | |
| patch_size=14, | |
| embed_dim=1280, | |
| depth=32, | |
| num_heads=16, | |
| ) | |
| return config | |
| MAE_ARCH = { | |
| "mae_visualize_base.pth": get_mae_base_config(), | |
| "mae_visualize_large.pth": get_mae_large_config(), | |
| "mae_visualize_huge.pth": get_mae_huge_config(), | |
| } | |
| import numpy as np | |
| # -------------------------------------------------------- | |
| # 2D sine-cosine position embedding | |
| # References: | |
| # Transformer: https://github.com/tensorflow/models/blob/master/official/nlp/transformer/model_utils.py | |
| # MoCo v3: https://github.com/facebookresearch/moco-v3 | |
| # -------------------------------------------------------- | |
| def get_2d_sincos_pos_embed(embed_dim, grid_size, cls_token=False): | |
| """ | |
| grid_size: int of the grid height and width | |
| return: | |
| pos_embed: [grid_size*grid_size, embed_dim] or [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) | |
| """ | |
| grid_h = np.arange(grid_size, dtype=np.float32) | |
| grid_w = np.arange(grid_size, dtype=np.float32) | |
| grid = np.meshgrid(grid_w, grid_h) # here w goes first | |
| grid = np.stack(grid, axis=0) | |
| grid = grid.reshape([2, 1, grid_size, grid_size]) | |
| pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid) | |
| if cls_token: | |
| pos_embed = np.concatenate([np.zeros([1, embed_dim]), pos_embed], axis=0) | |
| return pos_embed | |
| def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): | |
| assert embed_dim % 2 == 0 | |
| # use half of dimensions to encode grid_h | |
| emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2) | |
| emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2) | |
| emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) | |
| return emb | |
| def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): | |
| """ | |
| embed_dim: output dimension for each position | |
| pos: a list of positions to be encoded: size (M,) | |
| out: (M, D) | |
| """ | |
| assert embed_dim % 2 == 0 | |
| omega = np.arange(embed_dim // 2, dtype=np.float32) | |
| omega /= embed_dim / 2. | |
| omega = 1. / 10000**omega # (D/2,) | |
| pos = pos.reshape(-1) # (M,) | |
| out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product | |
| emb_sin = np.sin(out) # (M, D/2) | |
| emb_cos = np.cos(out) # (M, D/2) | |
| emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) | |
| return emb |