Download model/common/vit.py from ducido/diffusion_policy_gbc: direct link, hf CLI and curl.
- Browser
- Download file 8.85 kB
-
https://huggingface.co/ducido/diffusion_policy_gbc/resolve/main/model/common/vit.py
- Command line
-
hf download hf://ducido/diffusion_policy_gbc/model/common/vit.py
-
curl -L -o vit.py https://huggingface.co/ducido/diffusion_policy_gbc/resolve/main/model/common/vit.py
8.85 kB
| # MIT License | |
| # | |
| # Copyright (c) 2024 Intelligent Robot Motion Lab | |
| # | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |
| # of this software and associated documentation files (the "Software"), to deal | |
| # in the Software without restriction, including without limitation the rights | |
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| # copies of the Software, and to permit persons to whom the Software is | |
| # furnished to do so, subject to the following conditions: | |
| # | |
| # The above copyright notice and this permission notice shall be included in all | |
| # copies or substantial portions of the Software. | |
| # | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| # SOFTWARE. | |
| """ | |
| ViT image encoder implementation from IBRL, https://github.com/hengyuan-hu/ibrl | |
| """ | |
| from dataclasses import dataclass | |
| from typing import List | |
| import einops | |
| import torch | |
| from torch import nn | |
| from torch.nn.init import trunc_normal_ | |
| import math | |
| class VitEncoderConfig: | |
| patch_size: int = 8 | |
| depth: int = 1 | |
| embed_dim: int = 128 | |
| num_heads: int = 4 | |
| act_layer = nn.GELU | |
| stride: int = -1 | |
| embed_style: str = "embed2" | |
| embed_norm: int = 0 | |
| class VitEncoder(nn.Module): | |
| def __init__( | |
| self, | |
| obs_shape: List[int], | |
| cfg: VitEncoderConfig, | |
| num_channel=3, | |
| img_h=96, | |
| img_w=96, | |
| ): | |
| super().__init__() | |
| self.obs_shape = obs_shape | |
| self.cfg = cfg | |
| self.vit = MinVit( | |
| embed_style=cfg.embed_style, | |
| embed_dim=cfg.embed_dim, | |
| embed_norm=cfg.embed_norm, | |
| num_head=cfg.num_heads, | |
| depth=cfg.depth, | |
| num_channel=num_channel, | |
| img_h=img_h, | |
| img_w=img_w, | |
| ) | |
| self.img_h = img_h | |
| self.img_w = img_w | |
| self.num_patch = self.vit.num_patches | |
| self.patch_repr_dim = self.cfg.embed_dim | |
| self.repr_dim = self.cfg.embed_dim * self.vit.num_patches | |
| def forward(self, obs, flatten=False) -> torch.Tensor: | |
| # assert obs.max() > 5 | |
| obs = obs / 255.0 - 0.5 | |
| feats: torch.Tensor = self.vit.forward(obs) | |
| if flatten: | |
| feats = feats.flatten(1, 2) | |
| return feats | |
| class PatchEmbed1(nn.Module): | |
| def __init__(self, embed_dim, num_channel=3, img_h=96, img_w=96): | |
| super().__init__() | |
| self.conv = nn.Conv2d(num_channel, embed_dim, kernel_size=8, stride=8) | |
| self.num_patch = math.ceil(img_h / 8) * math.ceil(img_w / 8) | |
| self.patch_dim = embed_dim | |
| def forward(self, x: torch.Tensor): | |
| y = self.conv(x) | |
| y = einops.rearrange(y, "b c h w -> b (h w) c") | |
| return y | |
| class PatchEmbed2(nn.Module): | |
| def __init__(self, embed_dim, use_norm, num_channel=3, img_h=96, img_w=96): | |
| super().__init__() | |
| layers = [ | |
| nn.Conv2d(num_channel, embed_dim, kernel_size=8, stride=4), | |
| nn.GroupNorm(embed_dim, embed_dim) if use_norm else nn.Identity(), | |
| nn.ReLU(), | |
| nn.Conv2d(embed_dim, embed_dim, kernel_size=3, stride=2), | |
| ] | |
| self.embed = nn.Sequential(*layers) | |
| H1 = math.ceil((img_h - 8) / 4) + 1 | |
| W1 = math.ceil((img_w - 8) / 4) + 1 | |
| H2 = math.ceil((H1 - 3) / 2) + 1 | |
| W2 = math.ceil((W1 - 3) / 2) + 1 | |
| self.num_patch = H2 * W2 | |
| self.patch_dim = embed_dim | |
| def forward(self, x: torch.Tensor): | |
| y = self.embed(x) | |
| y = einops.rearrange(y, "b c h w -> b (h w) c") | |
| return y | |
| class MultiHeadAttention(nn.Module): | |
| def __init__(self, embed_dim, num_head): | |
| super().__init__() | |
| assert embed_dim % num_head == 0 | |
| self.num_head = num_head | |
| self.qkv_proj = nn.Linear(embed_dim, 3 * embed_dim) | |
| self.out_proj = nn.Linear(embed_dim, embed_dim) | |
| def forward(self, x, attn_mask): | |
| """ | |
| x: [batch, seq, embed_dim] | |
| """ | |
| qkv = self.qkv_proj(x) | |
| q, k, v = einops.rearrange( | |
| qkv, "b t (k h d) -> b k h t d", k=3, h=self.num_head | |
| ).unbind(1) | |
| # force flash/mem-eff attention, it will raise error if flash cannot be applied | |
| with torch.backends.cuda.sdp_kernel(enable_math=False): | |
| attn_v = torch.nn.functional.scaled_dot_product_attention( | |
| q, k, v, dropout_p=0.0, attn_mask=attn_mask | |
| ) | |
| attn_v = einops.rearrange(attn_v, "b h t d -> b t (h d)") | |
| return self.out_proj(attn_v) | |
| class TransformerLayer(nn.Module): | |
| def __init__(self, embed_dim, num_head, dropout): | |
| super().__init__() | |
| self.layer_norm1 = nn.LayerNorm(embed_dim) | |
| self.mha = MultiHeadAttention(embed_dim, num_head) | |
| self.layer_norm2 = nn.LayerNorm(embed_dim) | |
| self.linear1 = nn.Linear(embed_dim, 4 * embed_dim) | |
| self.linear2 = nn.Linear(4 * embed_dim, embed_dim) | |
| self.dropout = nn.Dropout(dropout) | |
| def forward(self, x, attn_mask=None): | |
| x = x + self.dropout(self.mha(self.layer_norm1(x), attn_mask)) | |
| x = x + self.dropout(self._ff_block(self.layer_norm2(x))) | |
| return x | |
| def _ff_block(self, x): | |
| x = self.linear2(nn.functional.gelu(self.linear1(x))) | |
| return x | |
| class MinVit(nn.Module): | |
| def __init__( | |
| self, | |
| embed_style, | |
| embed_dim, | |
| embed_norm, | |
| num_head, | |
| depth, | |
| num_channel=3, | |
| img_h=96, | |
| img_w=96, | |
| ): | |
| super().__init__() | |
| if embed_style == "embed1": | |
| self.patch_embed = PatchEmbed1( | |
| embed_dim, | |
| num_channel=num_channel, | |
| img_h=img_h, | |
| img_w=img_w, | |
| ) | |
| elif embed_style == "embed2": | |
| self.patch_embed = PatchEmbed2( | |
| embed_dim, | |
| use_norm=embed_norm, | |
| num_channel=num_channel, | |
| img_h=img_h, | |
| img_w=img_w, | |
| ) | |
| else: | |
| assert False | |
| self.pos_embed = nn.Parameter( | |
| torch.zeros(1, self.patch_embed.num_patch, embed_dim) | |
| ) | |
| layers = [ | |
| TransformerLayer(embed_dim, num_head, dropout=0) for _ in range(depth) | |
| ] | |
| self.net = nn.Sequential(*layers) | |
| self.norm = nn.LayerNorm(embed_dim) | |
| self.num_patches = self.patch_embed.num_patch | |
| # weight init | |
| trunc_normal_(self.pos_embed, std=0.02) | |
| named_apply(init_weights_vit_timm, self) | |
| def forward(self, x): | |
| x = self.patch_embed(x) | |
| x = x + self.pos_embed | |
| x = self.net(x) | |
| return self.norm(x) | |
| def init_weights_vit_timm(module: nn.Module, name: str = ""): | |
| """ViT weight initialization, original timm impl (for reproducibility)""" | |
| if isinstance(module, nn.Linear): | |
| trunc_normal_(module.weight, std=0.02) | |
| if module.bias is not None: | |
| nn.init.zeros_(module.bias) | |
| def named_apply( | |
| fn, module: nn.Module, name="", depth_first=True, include_root=False | |
| ) -> nn.Module: | |
| if not depth_first and include_root: | |
| fn(module=module, name=name) | |
| for child_name, child_module in module.named_children(): | |
| child_name = ".".join((name, child_name)) if name else child_name | |
| named_apply( | |
| fn=fn, | |
| module=child_module, | |
| name=child_name, | |
| depth_first=depth_first, | |
| include_root=True, | |
| ) | |
| if depth_first and include_root: | |
| fn(module=module, name=name) | |
| return module | |
| def test_patch_embed(): | |
| print("embed 1") | |
| embed = PatchEmbed1(128) | |
| x = torch.rand(10, 3, 96, 96) | |
| y = embed(x) | |
| print(y.size()) | |
| print("embed 2") | |
| embed = PatchEmbed2(128, True) | |
| x = torch.rand(10, 3, 96, 96) | |
| y = embed(x) | |
| print(y.size()) | |
| def test_transformer_layer(): | |
| embed = PatchEmbed1(128) | |
| x = torch.rand(10, 3, 96, 96) | |
| y = embed(x) | |
| print(y.size()) | |
| transformer = TransformerLayer(128, 4, False, 0) | |
| z = transformer(y) | |
| print(z.size()) | |
| if __name__ == "__main__": | |
| obs_shape = [6, 128, 128] | |
| enc = VitEncoder( | |
| obs_shape, | |
| VitEncoderConfig(), | |
| num_channel=obs_shape[0], | |
| img_h=obs_shape[1], | |
| img_w=obs_shape[2], | |
| ) | |
| print(enc) | |
| x = torch.rand(1, *obs_shape) * 255 | |
| print("output size:", enc(x, flatten=False).size()) | |
| print("repr dim:", enc.repr_dim, ", real dim:", enc(x, flatten=True).size()) | |