【ViT】Vision Transformer的实现01 patch embedding

在这里插入图片描述
对于224*224的图像,将它输入到Transformer里面,就需要将图像展开成一系列的token,
如果逐像素视为token进行注意力的计算,难免计算量太大,因此一个更加合理的想法是将图像划分为一个个的patch
将每个patch进行embedding

现在对于一个224224的图像,我们设置每个patch图像块的尺寸是16,因此呢,我们可以从H和W两个维度将原图像进行分割,
224/16=14 14
14=196
也就说说把原图像分割成为了196个16*16的patch,因此我们就是要把196个patch进行embedding

class PatchEmbed(nn.Module):
    """
    2D Image to Patch Embedding
    """
    def __init__(self, img_size=224, patch_size=16, in_c=3, embed_dim=768, norm_layer=None):
        super().__init__()
        img_size = (img_size, img_size)
        patch_size = (patch_size, patch_size)
        self.img_size = img_size
        self.patch_size = patch_size
        self.grid_size = (img_size[0] // patch_size[0], img_size[1] // patch_size[1])
        self.num_patches = self.grid_size[0] * self.grid_size[1]

        self.proj = nn.Conv2d(in_c, embed_dim, kernel_size=patch_size, stride=patch_size)
        self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()

    def forward(self, x):
        B, C, H, W = x.shape
        assert H == self.img_size[0] and W == self.img_size[1], f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."

        # flatten: [B, C, H, W] -> [B, C, HW]
        # transpose: [B, C, HW] -> [B, HW, C]
        x = self.proj(x).flatten(2).transpose(1, 2)
        x = self.norm(x)
        return x

我们首先定义一个PatchEmbed类,
它的初始化函数输入参数如下
img_size是输入图像的大小,224 像素
patch_size是我们分割的图像块的大小 16 像素
in_c 是输入图像的channels通道数,是3 RGB
embed_dim是每一个token的特征维数,也是我们embed输入的维数 我们定为768

self.num_patches = self.grid_size[0] * self.grid_size[1]
这一步相当于是计算了token的个数 1414=196
下面我们借用pytorch的2D卷积函数 nn.Conv2d
输入的通道数是3 embed_dim既是我们要求的输出通道数即每个token的特征维数,同时在卷积运算里面这代表着这层有多少个卷积核,
因为一般的卷积,卷积层输出通道数等于卷积核的个数。
然后卷积核的大小就是patch的大小16,步长的大小也是patch的大小16,这个意思就相当于用16
16的卷积核以16的步长做卷积,实际上就是提取了一个14*14的特征图,相当于把patch都提取出来了

下面的前向计算函数里面

x = self.proj(x).flatten(2).transpose(1, 2)

x首先的输入是(8,3,224,224)
然后通过proj函数做了embedding 输出的是(8,768,14,164)
然后flatten(2),在第二维上进行展开 (8,768,14,14)变成了(8,768,14*14)
最后transpose(1, 2)把第一维和第二维做交换 (8,768,196)变成了(8,196,768)

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vision Transformer中,1D位置嵌入和2D位置编码是通过在输入的图像或序列中添加额外的位置信息来实现的。下面是实现1D位置嵌入和2D位置编码的代码示例: 1. 1D位置嵌入 ```python import torch import torch.nn as nn class ViT(nn.Module): def __init__(self, img_size=224, patch_size=16, num_classes=1000, dim=768, depth=12, heads=12, mlp_dim=3072, dropout=0.1): super().__init__() self.num_patches = (img_size // patch_size) ** 2 self.patch_size = patch_size self.pos_embedding = nn.Parameter(torch.randn(1, self.num_patches + 1, dim)) self.patch_embed = nn.Conv2d(3, dim, kernel_size=patch_size, stride=patch_size, bias=False) self.cls_token = nn.Parameter(torch.randn(1, 1, dim)) self.dropout = nn.Dropout(dropout) self.transformer = nn.ModuleList([ nn.TransformerEncoderLayer(d_model=dim, nhead=heads, dim_feedforward=mlp_dim, dropout=dropout) for _ in range(depth) ]) self.fc = nn.Linear(dim, num_classes) def forward(self, x): b, c, h, w = x.shape x = self.patch_embed(x).flatten(2).transpose(1, 2) x = torch.cat([self.cls_token.repeat(b, 1, 1), x], dim=1) x = x + self.pos_embedding[:, :(self.num_patches + 1)] x = self.dropout(x) for transformer_layer in self.transformer: x = transformer_layer(x) x = x.mean(dim=1) x = self.fc(x) return x ``` 在这个代码中,`self.pos_embedding` 是一个可学习的参数,其 shape 为 `(1, num_patches + 1, dim)`,其中 `num_patches` 是输入图像被分成的 patch 的数量,`dim` 是 Transformer 的隐藏维度。`self.cls_token` 是一个用于表示整个序列或图像的特殊 token,它也是一个可学习的参数,其 shape 为 `(1, 1, dim)`。在 forward 函数中,我们首先对输入图像进行 patch embedding,然后将 cls token 和位置嵌入加到 patch embedding 的结果中。最后,我们将得到的序列输入到 Transformer 中。 2. 2D位置编码 ```python import torch import torch.nn as nn class ViT(nn.Module): def __init__(self, img_size=224, patch_size=16, num_classes=1000, dim=768, depth=12, heads=12, mlp_dim=3072, dropout=0.1): super().__init__() self.num_patches = (img_size // patch_size) ** 2 self.patch_size = patch_size self.pos_embedding = nn.Parameter(torch.randn(1, dim, img_size // patch_size, img_size // patch_size)) self.patch_embed = nn.Conv2d(3, dim, kernel_size=patch_size, stride=patch_size, bias=False) self.cls_token = nn.Parameter(torch.randn(1, 1, dim)) self.dropout = nn.Dropout(dropout) self.transformer = nn.ModuleList([ nn.TransformerEncoderLayer(d_model=dim, nhead=heads, dim_feedforward=mlp_dim, dropout=dropout) for _ in range(depth) ]) self.fc = nn.Linear(dim, num_classes) def forward(self, x): b, c, h, w = x.shape x = self.patch_embed(x).flatten(2).transpose(1, 2) x = torch.cat([self.cls_token.repeat(b, 1, 1), x], dim=1) x = x + self.pos_embedding x = self.dropout(x) for transformer_layer in self.transformer: x = transformer_layer(x) x = x.mean(dim=1) x = self.fc(x) return x ``` 在这个代码中,`self.pos_embedding` 是一个可学习的参数,其 shape 为 `(1, dim, img_size // patch_size, img_size // patch_size)`,其中 `img_size // patch_size` 是输入图像被分成的 patch 的数量。在 forward 函数中,我们首先对输入图像进行 patch embedding,然后将 cls token 和位置编码加到 patch embedding 的结果中。最后,我们将得到的序列输入到 Transformer 中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

量子-Alex

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值