论文阅读 Vision Transformer - VIT

文章目录

  • 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)之后再恢复成一整个大块,很强。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

程序员应该学习的 10 件事

程序员应该学习的 10 件事&#xff08;省流版&#xff09; 翻译&#xff1a;10 Things Software Developers Should Learn about Learning 原文&#xff1a;https://cacm.acm.org/magazines/2024/1/278891-10-things-software-developers-should-learn-about-learning/fulltex…

【教学类-43-21】完结篇 16宫格(4*4可算全部数字)

作品展示&#xff1a; 16宫格里面的4*4小格子可以凑满1-16&#xff0c;旁边的7宫格格2份 背景需求&#xff1a; 做完了1-20宫格的A4模板&#xff0c;最后做一个16宫格小格子&#xff08;附加7宫格2套&#xff09;的样式&#xff0c;只有4宫格&#xff08;2*2&#xff09;、9宫…

vue3二次封装element-ui中的table组件

为什么要做这件事 借助封装table组件的过程来巩固一下vue3相关知识点。 组件有哪些配置项 options:表格的配置项data: 表格数据源elementLoadingText&#xff1a;加载文案elementLoadingSpinner&#xff1a;加载图标elementLoadingBackground&#xff1a;背景遮罩的颜色elem…

【RabbitMQ】RabbitMQ高级:死信队列和延迟队列

目录 设置TTL&#xff08;过期时间&#xff09;概述RabbitMQ使用TTL原生API案例springboot案例 死信队列概述原生API案例springboot案例 延迟队列概述插件实现延迟队列安装插件代码 TTL实现延迟队列实现延迟队列优化 设置TTL&#xff08;过期时间&#xff09; 概述 在电商平台…

Netty开篇——NIO章下(五)

SelectionKey 表示 Selector 和网络通道的注册关系&#xff0c;共四种(全是常量): Int OP_ACCEPT:有新的网络连接可以接受&#xff0c;值为 16 &#xff08;1 << 4&#xff09;Int OP_CONNECT: 代表连接已经建立&#xff0c;值为 8 &#xff08;1 << 3&#xff09;…

#AIGC#text2video文生视频,开源DragNUWA:通过集成文本、图像和轨迹对视频生成进行细粒度控制

DragNUWA&#xff1a;通过集成文本、图像和轨迹对视频生成进行细粒度控制 论文地址&#xff1a;https://arxiv.org/abs/2308.08089 DragNUWA 使用户能够直接操纵图像中的背景或对象&#xff0c;模型将这些动作无缝地转换为相机运动或对象运动&#xff0c;生成相应的视频。 Drag…

linux基础学习(3):挂载

挂载可以理解为给磁盘空间一个可访问的入口&#xff0c;那个入口称为挂载点&#xff0c;相当于windows中的盘符。 1.挂载命令mount 1.1直接输入mount 查看系统已挂载的设备 1.2挂载与卸载命令 mount -t 文件系统名 设备文件名 挂载点 | umount 挂载点 或 umount 设…

机器学习——支持向量机SVM

1 摘要&#xff1a; 支持向量机&#xff08;SVM&#xff09;是一种二类分类模型&#xff0c;其基本模型是在特征空间上找到最佳的分离超平面使得训练集上正负样本间隔最大&#xff0c;间隔最大使它有别于感知机&#xff0c;支持向量机也可通过核技巧使它成为非线性分类器。支持…

防火墙部署安全区域

目录 为什么需要安全区域在防火墙上如何来区分不同的网络将接口划分到安全区域安全区域、受信任程度与安全级别安全域间、安全策略与报文流动的方向 安全区域配置案例 为什么需要安全区域 防火墙主要部署在网络边界起到隔离的作用 在防火墙上如何来区分不同的网络 防火墙通过安…

MobaXterm无法上传文件处理

ssh能成功通过mobaxterm连接虚拟机但sftp上传失败的解决办法 1、出现问题时&#xff0c;/etc/ssh/sshd_config的配置文件关于sftp的这行下图所示的情况 2、更改配置文件/etc/ssh/sshd_config的配置文件关于sftp为“internal-sftp”。 3、执行命令systemctl restart sshd&…

leetcode82. 删除排序链表中的重复元素 II

文章目录 题目思路1复杂度Code2 思路2复杂度2Code2 题目 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;…

10.云原生之在线开发调试

云原生专栏大纲 文章目录 vscode-server介绍VSCode Server 和云开发结合vscode-server安装code-server安装插件在线安装插件离线安装插件安装中文插件 配置开发环境在容器中安装开放环境Dockerfile制作镜像 git拉取项目 vscode-server介绍 VSCode Server&#xff08;Visual S…

C++ 编程需要什么样的开发环境?

C 编程需要什么样的开发环境&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#…

k8s之pod基础(下)

k8s之pod基础&#xff08;下&#xff09; 存活探针和就绪探针&#xff0c;会伴随整个pod的生命周期 就绪探针的特点&#xff1a;pod的状态是running&#xff0c;ready状态是notready&#xff0c;容器不可以提供正常的业务访问&#xff0c;就绪探针不会重启容器 就绪探针exec的…

闲鱼宝库亮相!闲鱼商品详情关键词搜索电商API接口助你畅享无尽好货!

随着互联网的快速发展&#xff0c;电商平台的崛起已经改变了人们的购物习惯。而在众多电商平台中&#xff0c;闲鱼作为一款社区二手交易平台&#xff0c;一直备受用户喜爱。如今&#xff0c;闲鱼宝库正式亮相&#xff0c;为用户带来了更加全面、详细的商品详情关键词搜索电商AP…

IP地址冲突警告!你的网络正在受到威胁

IP地址冲突是网络安全中的一个严重问题&#xff0c;可能导致网络不稳定、数据泄漏等严重后果。本文将深入探讨IP地址冲突的原因、影响以及如何应对&#xff0c;以提醒用户关注网络安全问题。 1. IP地址冲突的原因&#xff1a; 动态分配问题&#xff1a;在使用动态IP地址分配的…

开发需求总结9-el-tree获取选中节点,节点全选时返回被全选子级的父节点,未全选则返回被选中的节点

目录 需求描述 代码实现&#xff1a; 需求描述 需要获取树组件选中的节点&#xff0c;假如父节点被选中&#xff08;该节点全选&#xff09;&#xff0c;即只返回父节点的数据&#xff0c;如父节点未被全选&#xff0c;则正常返回被选中节点的数据。 示例一&#xff1a; 如上图…

大众点评评论采集软件使用教程

导出字段&#xff1a; 店铺ID 评论ID 发布时间 人均消费 评分 详情链接 点赞数 浏览数 评论数 最后更新时间 发布平台 推荐 评论详情 原始评论 图片数 图片链接 用户等级 用户名称 用户头像 VIP 私

农业无人机行业分析:单年内作业量突破13亿亩次

面对我国18亿亩的耕地植保市场需求&#xff0c;未来我国植保无人机将依然保持快速发展态势&#xff0c;预计2022年我国植保无人机销量将增长至8万架。 植保无人机市场呈现爆发式增长&#xff0c;同时也吸引了不少企业进入&#xff0c;我们从2022年植保无人机企业网络热度榜中可…

Linux学习记录——사십일 高级IO(2)--- Select型服务器

文章目录 1、思路2、select接口3、实现1、准备工作2、实现等待多个fd3、辨别连接和简单处理读事件4、简单处理写、读事件 4、特点 1、思路 select就是多路转接IO。select能以某种形式&#xff0c;等待多个文件描述符&#xff0c;只要有哪个fd有数据就可以读取并全部返回。就绪…