论文阅读 Vision Transformer - VIT

news2024/11/23 12:08:29

文章目录

  • 1 摘要
    • 1.1 核心
  • 2 模型架构
    • 2.1 概览
    • 2.2 对应CV的特定修改和相关理解
  • 3 代码
  • 4 总结

1 摘要

1.1 核心

通过将图像切成patch线形层编码成token特征编码的方法,用transformer的encoder来做图像分类

2 模型架构

2.1 概览

在这里插入图片描述

2.2 对应CV的特定修改和相关理解

解决问题:

  1. transformer输入限制: 由于自注意力+backbone,算法复杂度为o(n²),token长度一般要<512才足够运算
    解决:a) 将图片转为token输入 b) 将特征图转为token输入 c)√ 切patch转为token输入
  2. transformer无先验知识:卷积存在平移不变性(同特征同卷积核同结果)和局部相似性(相邻特征相似结果),
    而transformer无卷积核概念,只有整个编解码器,需要从头学
    解决:大量数据训练
  3. cv的各种自注意力机制需要复杂工程实现:
    解决:直接用整个transformer模块
  4. 分类head:
    解决:直接沿用transformer cls token
  5. position编码:
    解决:1D编码

pipeline:
224x224输入切成16x16patch进行位置编码和线性编码后增加cls token 一起输入的encoder encoder中有L个selfattention模块
输出的cls token为目标类别

3 代码

如果理解了transformer,看完这个结构感觉真的很简单,这篇论文也只是开山之作,没有特别复杂的结构,所以想到代码里看看。

import torch
from torch import nn

from einops import rearrange, repeat
from einops.layers.torch import Rearrange

# helpers

def pair(t):
    return t if isinstance(t, tuple) else (t, t)

# classes

class FeedForward(nn.Module):
    def __init__(self, dim, hidden_dim, dropout = 0.):
        super().__init__()
        self.net = nn.Sequential(
            nn.LayerNorm(dim),
            nn.Linear(dim, hidden_dim),
            nn.GELU(),
            nn.Dropout(dropout),
            nn.Linear(hidden_dim, dim),
            nn.Dropout(dropout)
        )

    def forward(self, x):
        return self.net(x)

class Attention(nn.Module):
    def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.):
        super().__init__()
        inner_dim = dim_head *  heads
        project_out = not (heads == 1 and dim_head == dim)

        self.heads = heads
        self.scale = dim_head ** -0.5

        self.norm = nn.LayerNorm(dim)

        self.attend = nn.Softmax(dim = -1)
        self.dropout = nn.Dropout(dropout)

        # linear(1024 , 3072)
        self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False)

        self.to_out = nn.Sequential(
            nn.Linear(inner_dim, dim),
            nn.Dropout(dropout)
        ) if project_out else nn.Identity()

    def forward(self, x):
        # [1, 65, 1024]
        x = self.norm(x)
        # [1, 65, 1024]
        qkv = self.to_qkv(x).chunk(3, dim = -1)
        # self.to_qkv(x)                [1, 65, 3072]
        # self.to_qkv(x).chunk(3,-1)    [3, 1, 65, 1024]
        q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = self.heads), qkv)
        # q,k,v                         [1, 65, 1024] -> [1, 16, 65, 64]
        # 把 65个1024的特征分为 heads个65个d维的特征 然后每个heads去分别有自己要处理的隐藏层,对不同的特征建立不同学习能力
        dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale
        # [1, 16, 65, 64] * [1, 16, 64, 65] -> [1, 16, 65, 65]
        # scale 保证在softmax前所有的值都不太大

        attn = self.attend(dots)
        # softmax [1, 16, 65, 65]
        
        attn = self.dropout(attn)
        # dropout [1, 16, 65, 65]
        
        out = torch.matmul(attn, v)
        # out [1, 16, 65, 64]
        
        out = rearrange(out, 'b h n d -> b n (h d)')
        # out [1, 65, 1024]
        
        return self.to_out(out)
        # out [1, 65, 1024]
        

class Transformer(nn.Module):
    def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout = 0.):
        super().__init__()
        self.norm = nn.LayerNorm(dim)
        self.layers = nn.ModuleList([])
        for _ in range(depth):
            self.layers.append(nn.ModuleList([
                Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout),
                FeedForward(dim, mlp_dim, dropout = dropout)
            ]))

    def forward(self, x):
        # [1, 65, 1024]
        for attn, ff in self.layers:
            # [1, 65, 1024]
            x = attn(x) + x
            # [1, 65, 1024]
            x = ff(x) + x

        # [1, 65, 1024]
        return self.norm(x)
        # shape不会改变

class ViT(nn.Module):
    def __init__(self, *, image_size, patch_size, num_classes, dim, depth, heads, mlp_dim, pool = 'cls', channels = 3, dim_head = 64, dropout = 0., emb_dropout = 0.):
        super().__init__()
        image_height, image_width = pair(image_size)
        patch_height, patch_width = pair(patch_size)

        assert image_height % patch_height == 0 and image_width % patch_width == 0, 'Image dimensions must be divisible by the patch size.'

        num_patches = (image_height // patch_height) * (image_width // patch_width)
        patch_dim = channels * patch_height * patch_width
        assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'

        # num_patches   64
        # patch_dim     3072
        # dim           1024
        self.to_patch_embedding = nn.Sequential(
            #Rearrange是einops中的一个方法
            # einops:灵活和强大的张量操作,可读性强和可靠性好的代码。支持numpy、pytorch、tensorflow等。
            # 代码中Rearrage的意思是将传入的image(3,224,224),按照(3,(h,p1),(w,p2))也就是224=hp1,224 = wp2,接着把shape变成b (h w) (p1 p2 c)格式的,这样把图片分成了每个patch并且将patch拉长,方便下一步的全连接层
            # 还有一种方法是采用窗口为16*16,stride 16的卷积核提取每个patch,然后再flatten送入全连接层。
            Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_height, p2 = patch_width),
            nn.LayerNorm(patch_dim),
            nn.Linear(patch_dim, dim),
            nn.LayerNorm(dim),
        )

        self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))
        self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
        self.dropout = nn.Dropout(emb_dropout)

        self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)

        self.pool = pool
        self.to_latent = nn.Identity()

        self.mlp_head = nn.Linear(dim, num_classes)

    def forward(self, img):
        # 1. [1, 3, 256, 256]       输入img
        x = self.to_patch_embedding(img)
        # 2. [1, 64, 1024]          patch embd
        b, n, _ = x.shape
        # 3. [1, 1, 1024]           cls_tokens
        cls_tokens = repeat(self.cls_token, '1 1 d -> b 1 d', b = b)
        # 4. [1, 65, 1024]          cat [cls_tokens, x]
        x = torch.cat((cls_tokens, x), dim=1)
        # 5. [1, 65, 1024]          add [x] [pos_embedding]
        x += self.pos_embedding[:, :(n + 1)]
        # 6. [1, 65, 1024]          dropout
        x = self.dropout(x)
        # 7. [1, 65, 1024]          N * transformer
        x = self.transformer(x)
        # 8. [1,1024]               cls_x output
        x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0]
        # 9. [1,1024]               cls_x output mean
        x = self.to_latent(x)
        # 10.[1,1024]               nn.Identity()不改变输入和输出 占位层
        return self.mlp_head(x)
        # 11.[1,cls]                mlp_cls_head

4 总结

multihead和我原有的理解偏差修正。
我以为的是QKV会有N块相同的copy(),每一份去做后续的linear等操作。
代码里是直接用linear将QKV分为一整个大块,用permute/rearrange的操作切成了N块,f(Q,K)之后再恢复成一整个大块,很强。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1394333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dobbo --- HelloWorld项目搭建

Dobbo-HelloWorld 1. demo -- spring方式集成1.1 实现步骤 2. demo -- springboot方式集成2.1 实现provider2.2 实现consumer2.3 项目测试 1. demo – spring方式集成 dubbo官方文档 提供一个可被调用的接口 提供方&#xff1a;实现接口的方法逻辑&#xff0c;启动应用程序&a…

【JavaEE】文件操作: File 类的用法和 InputStream, OutputStream 的用法

目录 1. File 概述 1.1 File的属性 1.2 File的构造方法 1.3 File的方法 2.读文件 2.1 InputStream 概述 2.2 FileInputStream 概述 2.3 正确打开和关闭文件的方式 2.4 不同方式读取文件代码示例 2.4 另一种方法:利用 Scanner 进行字符读取 3.写文件 3.1 OutputStre…

【架构】docker实现3主3从架构配置【案例1/4】

一&#xff0c;集群规划及准备工作 架构实现&#xff1a;Redis3主3从 二&#xff0c;搭建命令 第一步&#xff0c;创建6台服务&#xff1a; docker run -d --name redis-node-1 --net host --privilegedtrue -v /data/redis/share/redis-node-1:/data redis:6.0.8 --clust…

redis原理(四)redis命令

目录 一、字符串命令&#xff1a; 二、列表命令&#xff1a; 三、集合命令&#xff1a; 四、散列命令&#xff1a; 五、有序集合命令&#xff1a; 六、redis发布与订阅命令&#xff1a; 七、事务命令 八、其他命令 1、排序&#xff1a;SORT 2、键的过期时间&#xff…

SSH隧道技术

SSH隧道 简介 SSH隧道是一种通过SSH协议在两个网络节点之间建立安全通信的技术。它可以用于多种用途&#xff0c;包括加密和保护敏感数据传输、绕过防火墙限制、远程访问内部服务等。 应用&#xff1a; 端口转发&#xff1a;SSH隧道可以将本地端口转发到远程主机上&#xf…

如何使用视频下载助手获取公众号视频链接

微信公众号视频链接如何提取呢&#xff1f;今天就来说视频下载助手提取微信公众号的视频链接! 1:公众号获取视频下载助手二维码&#xff0c;并长按添加 2:找到需要下载的公众号带有视频的文章并转发给视频下载助手 3:提取完成后用户可以在视频下载助手中点击返回提取好的视频并…

Springboot智慧校园电子班牌统一管理平台源码

借助AIoT智能物联、云计算技术打造智慧绿色校园&#xff0c;助力实现校园教务管理、教师管理、学籍管理、考勤、信息发布、班级文明建设、校园风采、家校互通等场景功能&#xff0c;打造安全、便捷、绿色的智慧校园。 前后端分离架构 1、使用springbootvue2 2、数据库&#xff…

1.2 虚拟环境

1.2 虚拟环境 创建好应用目录之后&#xff0c;接下来该安装Flask了。安装Flask最便捷的方法是使用虚拟环境。 虚拟环境是Python解释器的一个私有副本&#xff0c;在这个环境中你可以安装私有包&#xff0c;而且不会影响系统中安装的全局Python解释器。 虚拟环境非常有用&…

力扣刷MySQL-第四弹(详细讲解)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;力扣刷题讲解-MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出…

[labelme]labelme如何将标注的json格式转成png的mask文件掩码文件

labelme工具不仅仅具有标注功能&#xff0c;而且可以将json文件转化为png的分割训练文件&#xff0c;如果您是一个类别则可以直接用labelme_json_to_dataset进行转换最后提取对应的掩码文件即可进行语义分割训练。如果您是>2个类别则不推荐使用labelme工具进行转换&#xff…

无心剑七绝《腊八粥香》

七绝腊八粥香 欣逢腊八粥浓香 五谷丰登聚宝庄 祈福心诚情不尽 佳肴共品待春芳 2024年1月18日 平水韵七阳平韵 这首七言绝句《腊八粥香》以腊八节为背景&#xff0c;描绘了人们欢庆腊八、祈福迎新的情景。 首句“欣逢腊八粥浓香”&#xff0c;开门见山地点明了主题——腊八节&a…

2023年山东省职业院校技能大赛高职组信息安全管理与评估 模块二(正式赛)

2023年山东省职业院校技能大赛高职组信息安全管理与评估 模块二 模块二竞赛项目试题 根据信息安全管理与评估技术文件要求&#xff0c;模块二为网络安全事件响应、数字取证调查和 应用程序安全。本文件为信息安全管理与评估项目竞赛-模块二试题。 介绍 竞赛有固定的开始和结…

23巴黎胶囊下架对贴纸刀枪市场有何影响?

短短一个国庆假期&#xff0c;CS2上线了&#xff0c;卡价暴跌了&#xff0c;最长一届巴黎胶囊也终于下架&#xff0c;阿努比斯收藏包成为常驻饰品&#xff0c;再看我们充值卡价也大降特降&#xff0c;国庆假期好不热闹。 那么胶囊价格到底是受哪些因素影响&#xff1f;胶囊下架…

Docker(三)使用 Docker 镜像

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 使用 Docker 镜像 在之前的介绍中&#xff0c;我们知道镜像是 Docker 的三大组件之一。 Docker 运行容器前需要本地存在对应的镜像&#x…

微服务入门 | 项目分割 | 远程调度Feign | 用户中心erueka 和 nacos

认识微服务 微服务架构演变&#xff1a; 单体架构&#xff1a;所有功能集中在一个项目中开发&#xff0c;打成一个包部署 分布式架构&#xff1a;就是各功能模块的代码不在同一个项目中写了&#xff0c;到时候修改其中一个过能的代码&#xff0c;对另一个功能完全没有任何影响…

Linux网络之PXE高效批量装机、Kickstart全自动化安装

一. PXE网络装机简介和相关知识 1. 常见的三种系统安装方式和相关文件 ① 三种系统安装方式 u启动安装&#xff1a;在U盘中下载相关的安装系统及镜像文件&#xff0c;u盘插机安装 光驱安装&#xff1a;将带有所需系统的光盘放进电脑服务器中&#xff0c;按照官方引导装机 …

【数据结构】栈的远房亲戚——队列

队列的基本概念 前言一、队列的定义二、队列的重要术语三、队列的基本操作四、数据结构的三要素4.1 线性表的三要素4.2 栈的三要素4.3 队列的三要素 结语 前言 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;&#xff01; 在经过前面内容的介绍&#xff0c;…

LabVIEW交变配流泵性能测试系统

利用LabVIEW软件与高级硬件结合&#xff0c;开发交变配流泵性能测试系统。该系统不仅提高了测试精度&#xff0c;还优化了工业自动化流程&#xff0c;代表了液压系统测试技术的进步。 开发了一种高精度的测试系统&#xff0c;该系统能够综合评估交变配流泵的性能&#xff0c;包…

【计算机硬件】2、指令系统、存储系统和缓存

文章目录 指令系统计算机指令的组成计算机指令执行过程指令的寻址方式&#xff08;怎么样找到操作数&#xff1f;&#xff09;1、顺序寻址2、跳跃寻址 指令操作数的寻址方式&#xff08;怎么样找到操作数&#xff1f;&#xff09;1、立即寻址方式2、直接寻址方式3、间接寻址方式…

Nginx 基础使用

目录结构 进入Nginx的主目录我们可以看到这些文件夹 client_body_temp conf fastcgi_temp html logs proxy_temp sbin scgi_temp uwsgi_temp其中这几个文件夹在刚安装后是没有的&#xff0c;主要用来存放运行过程中的临时文件 client_body_temp fastcgi_temp proxy_temp scg…