Vision Transformer 简单复现和解释

news2024/10/6 12:31:40

一些我自己不懂的过程,我自己在后面写了demo解释。 

import torch
import torch.nn as nn

from einops import rearrange, repeat
from einops.layers.torch import Rearrange
def pair(t):
    return t if isinstance(t, tuple) else (t, t)
class PreNorm(nn.Module):
    def __init__(self, dim, fn):
        super().__init__()
        self.norm = nn.LayerNorm(dim)
        self.fn = fn
    def forward(self, x, **kwargs):
        return self.fn(self.norm(x), **kwargs)
class FeedForward(nn.Module):
    def __init__(self, dim, hidden_dim, dropout = 0.):
        super().__init__()
        self.net = nn.Sequential(
            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.attend = nn.Softmax(dim = -1)
        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):
        qkv = self.to_qkv(x).chunk(3, dim = -1)## 对tensor张量分块 x :1 197 1024   qkv 最后是一个元祖,tuple,长度是3,每个元素形状:1 197 1024
        q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = self.heads), qkv)
        dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale

        attn = self.attend(dots)

        out = torch.matmul(attn, v)
        out = rearrange(out, 'b h n d -> b n (h d)')
        return self.to_out(out)
class Transformer(nn.Module):
    def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout = 0.):
        super().__init__()
        self.layers = nn.ModuleList([])
        for _ in range(depth):
            self.layers.append(nn.ModuleList([
                PreNorm(dim, Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout)),
                PreNorm(dim, FeedForward(dim, mlp_dim, dropout = dropout))
            ]))
    def forward(self, x):
        for attn, ff in self.layers:
            x = attn(x) + x
            x = ff(x) + x
        return x
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) #224x224
        patch_height, patch_width = pair(patch_size) # 16x16
        
        assert image_height % patch_height == 0 and image_width % patch_width == 0 , 'Image dimensions'
        #块数
        num_patches = (image_height // patch_height) * (image_width // patch_width)
        #每块展开的长度
        patch_dim = channels * patch_height * patch_width
        
        assert pool in {'cls','mean'}, 'pool type cls or mean'
        
        #完成的操作是将(B,C,H,W)的shape调整为(B,(H/P *W/P),P*P*C)
        #H/P *W/P:块数  p1 p2 c:每块展开的长度 = patch_dim
        self.to_patch_embedding = nn.Sequential(
           Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_height, p2 = patch_width),
           #块拉伸之后,做一个映射 ,每一块就是一个batch
           nn.Linear(patch_dim, dim),
           )
        
        #生成了[1, num_patches + 1, dim] 形状的参数,这些随机数字满足标准正态分布(0~1)
        #num_patches + 1:块位置的编码+cls
        self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))
        
        #生成cls_token 的参数
        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.Sequential(
            #在transformer中一般采用LayerNorm,LayerNorm也是归一化的一种方法,与BatchNorm不同的是它是对每单个batch进行的归一化,而batchnorm是对所有batch一起进行归一化的
            nn.LayerNorm(dim),
            nn.Linear(dim, num_classes)
        )
    def forward(self, img):
        x = self.to_patch_embedding(img) ## img 1 3 224 224  输出形状x : 1 196 1024 
        b, n, _ = x.shape ## 
        print('块嵌入后的维度'+ str(x.shape))
        #这个是复制b份cls_token
        cls_tokens = repeat(self.cls_token, '() n d -> b n d', b = b)
        print('cls_tokens维度'+ str(cls_tokens.shape))
        x = torch.cat((cls_tokens, x), dim=1)
        print('块嵌入拼接cls_tokens的维度'+str(x.shape))
        x += self.pos_embedding[:, :(n + 1)]
        print("self.pos_embedding[:, :(n + 1)]维度"+str(self.pos_embedding[:, :(n + 1)].shape))
        print("x+pos维度"+str(x.shape))
        x = self.dropout(x)

        x = self.transformer(x)
        print('after_transformer'+str(x.shape))

        x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0]
        print(x.shape)
        x = self.to_latent(x)
        return self.mlp_head(x)
        
    
v = ViT(
    image_size = 224,  #输入图像的大小
    patch_size = 16,   #块的大小
    num_classes = 1000, #做多少分类
    dim = 1024,  # 维度
    depth = 6,  #encoder要堆叠多少个
    heads = 8, #多头注意力机制有多少个头
    mlp_dim = 2048, 
    dropout  = 0.1,
    emb_dropout = 0.1
    
)
img = torch.randn(2, 3, 224, 224)

preds = v(img) # (1, 1000)

以上是代码的简单实现,但是我还是有一些代码没有看懂。自己写了几个demo就看懂了。毕竟我还很菜。

1.Rearrange

用来改变张量的shape

完成的操作是将(B,C,H,W)的shape调整为(B,(H/P *W/P),P*P*C)

f = Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = 16, p2 = 16)
a = f(img)
print(a.shape)

输出:

这是另外一个小例子:

2.nn.Linear

不管数据的shape是怎样的,该函数处理的是最后一维,将n1维度映射为n2维度。

f = Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = 16, p2 = 16)
a = f(img)
print(a.shape)

#demo部分
f1 = nn.Linear(768, 1026)
b = f1(a)
print(b.shape)

test = torch.randn(2, 768)
t = f1(test)
print(t.shape)

test1 = torch.randn(3,2, 768)
t1 = f1(test1)
print(t1.test)

test2 = torch.randn(5,6,3,2, 768)
t2 = f1(test2)
print(t2.shape)


输出:

3.chunk

把一个张量分成几份

 

4.向量相加

G1: 如果相加的两个张量维度不一致,那么首先把维度低的那个张量从右边和维度高的张量对齐

例如下面的代码,b的维度低一些,所以和a相加的时候b的维度就会先扩充为[1,1,5,6]。

a = torch.ones([8, 4, 5, 6])
print('a =',a.size())
b = torch.ones([5, 6])
print('b =',b.size())
c = a+b
print('c =',c.size())

G2:当两个张量维度相同时,对应轴的值需要一样,或者为1。

相加时,把所有为1的轴进行复制扩充,从而得到两个维度完全相同的张量。然后对应位置相加即可。

a = torch.ones([2, 2, 3])
b = torch.ones([1, 2, 3])
c = a+b
print(c)

a = torch.ones([2, 2, 3])
b = torch.ones([3])
c = a+b
print(c)

 

a = torch.ones([2, 2, 3])
b = torch.ones([1, 1, 1])
c = a+b
print(c)

 

 还有不可相加的例子

1.由于4不等于2,因此不可以相加

a = torch.ones([8, 4, 5, 6])
print('a =',a.size())
b = torch.ones([1, 2, 1, 6])
print('b =',b.size())
c = a+b
print('c =',c.size())

2.由于3不等于6,不可以相加

a = torch.ones([8, 4, 5, 6])
print('a =',a.size())
b = torch.ones([1, 4, 1, 3])
print('b =',b.size())
c = a+b
print('c =',c.size())

5.张量的切片

取第几维,第几个元素 

a = torch.randint(1, 24, (2, 3, 4))
print(a)
print(a[:,0])
print(a[:,0,0])

输出:

 

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

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

相关文章

数据库系统概念 | 第七章:使用E-R模型的数据库设计 | ER图设计| ER图转化为关系模型 | 强实体和弱实体

文章目录📚设计过程概览📚实体-联系模型🐇E-R数据模型🥕实体集🥕联系集🥕属性🐇E-R图📚映射基数🐇二元联系集⭐️🥕一对一🥕一对多🥕多…

二叉树的顺序结构——堆的概念实现(图文详解+完整源码 | C语言版)

目录 0.写在前面 1.什么是堆? 2.堆的实现 2.1 堆的结构定义 2.2 函数声明 2.3 函数实现 2.3.1 AdjustUp(向上调整算法) 2.3.2 AdjustDown(向下调整算法) 2.3.3 HeapCreate(如何建堆) …

更多的选择器 更多伪类选择器 颜色选中时写法 被选中的第一行文字 选中第几个元素

目录更多的选择器更多伪类选择器1. first-child2. last-child3. nth-child4. nth-of-type更多的伪元素选择器1. first-letter2. first-line3. selection更多的选择器 更多伪类选择器 1. first-child 选择第一个子元素 圈住的地方意思是:li 的第一个子元素设置为红…

第三篇:Haploview做单倍型教程3--结果解读

大家好,我是邓飞,这里介绍一下如何使用Haploview进行单倍型的分析。 计划分为三篇文章: 第一篇:Haploview做单倍型教程1–软件安装第二篇:Haploview做单倍型教程2–分析教程第三篇:Haploview做单倍型教程…

java中对泛型的理解

那么什么是泛型泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,而这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型…

【ROS2 入门】ROS2 创建工作空间

大家好,我是虎哥,从今天开始,我将花一段时间,开始将自己从ROS1切换到ROS2,在上几篇中,我们一起了解ROS 2中很多基础概念,从今天开始我们逐步就开始利用ROS2的特性进行开发编程了。 工作区&#…

【Linux】基础IO --- 系统级文件接口、文件描述符表、文件控制块、fd分配规则、重定向…

能一个人走的路别抱有任何期待,死不了 文章目录一、关于文件的重新认识二、语言和系统级的文件操作(语言和系统的联系)1.C语言文件操作接口(语言级别)1.1 文件的打开方式1.2 文件操作的相关函数1.3 细节问题2.系统级文…

【Go基础】加密算法和数据结构

文章目录一、加密算法1. 对称加密2. 非对称加密3. 哈希算法二、数据结构与算法1. 链表2. 栈3. 堆4. Trie树一、加密算法 1. 对称加密 加密过程的每一步都是可逆的 加密和解密用的是同一组密钥 异或是最简单的对称加密算法 // XOR 异或运算,要求plain和key的长度相…

PHP实现URL长连接转短连接方法总结

依据第二种算法,URL长连接转短连接实现方法如下:语言:PHP5.6服务器环境:LNMP假设:长连接地址:http://www.test.com/index.php短连接地址:http://t.test.com/六位code码第一步:利用sh…

Jupyter使用详解

Jupyter使用详解 本篇文章我们主要介绍Jupyter的使用与配置,本篇文章的主要内容如下: 什么是Jupyter notebookJupyter notebook的安装使用Jupyter notebook 什么是Jupyter notebook? Jupyter Notebook是一个Web应用程序,允许您…

在甲骨文云容器实例(Container Instances)上部署Oracle Linux 8 Desktop加强版(包括Minio,ssh登录等)

甲骨文云推出了容器实例,这是一项无服务器计算服务,可以即时运行容器,而无需管理任何服务器。 今天我们尝试一下通过容器实例部署Oracle Linux 8 Desktop加强版。 加强版里包括,Minio,ssh登录,OCI CLI命令行…

linux基本功系列之-rpm命令实战

文章目录前言🚀🚀🚀一. rpm命令介绍1.1 RPM包介绍1.2 rpm包的优缺点1.3 rpm包获取方式二. 语法格式及常用选项2.1 RPM安装常用参数2.2 rpm格式介绍三. 应用案例3.1 从本地安装软件包3.2 查询lrzsz的包有没有安装3.3 查询命令是哪个包安装的3.…

3.1(完结)Linux扫盲笔记

1. Linux环境下,输入密码,不回回显(*)。 2.普通用户的密码一定不要和root一样,root一定要安全级别更高。具体的添加账户和修改密码的操作,见蛋哥Linux训练营,第2课,30分钟处。 3.在最高权限(root)&#x…

java基础学习 day37 (集合)

集合与数组的区别 长度:数组长度固定,一旦创建完成,就不能改变。集合长度可变,根据添加和删除元素,自动扩容或自动收缩,(添加几个元素就扩容多少,删除几个元素就收缩多少&#xff0…

JMeter测试redis性能

JMeter测试redis性能前言插件使用说明前言 针对Redis的性能测试需求本身就比较小众,因为Redis的性能指标在官网已经给出了详细的数据。但是有时候我们仍然需要对redis进行性能测试,例如资源配置需求,参数调优对比,程序优化等场景…

树型结构——二叉数

之前就说过我们的数据结构分为两种,分别是线性结构和非线性结构,我们今天要学的第一种线性结构就是树型结构。 1. 树型结构 树型结构并非我们熟悉的重点,所以在这里只做了解。 概念: 树是一种非线性的数据结构,它是…

【人工智能原理自学】循环:序列依赖问题

😊你好,我是小航,一个正在变秃、变强的文艺倾年。 🔔本文讲解循环:序列依赖问题,一起卷起来叭! 目录一、“序列”二、代码实现一、“序列” 数据除了在空间上可能出现关联性外,也可…

nodejs在线教学网上授课系统vue367

目 录 摘 要 I Abstracts II 目 录 III 第1章 绪论 1 1.1课题背景 1 1.2研究意义 1 1.3研究内容 2 第2章 技术介绍 1 2.1 相关技术 1 1、 node_modules文件夹(有npn install产生) 这文件夹就是在创建完项目后,cd到项目目录执行np…

基于nodejs+vue驾校预约网站管理系统

系统分为用户和管理员,教练三个角色 目 录 第1章 绪论 1 1.1课题背景 1 1.2 背景意义 1 1.3 研究的内容 2 第2章 相关技术 3 第3章 系统分析 5 3.1可行性分析 5 3.2系统性能分析 6 3.3系统流程分析 6 3.3.1操作流程 6 3.3.2信息添加…

Cadence PCB仿真使用Allegro PCB SI生成电源地噪声报告SSN Report及报告导读图文教程

🏡《Cadence 开发合集目录》   🏡《Cadence PCB 仿真宝典目录》 目录 1,概述2,生成报告3,报告导读4,总结1,概述 SSN报告等效的电源和地噪声源报告。本文简单介绍使用Allegro PCB SI生成SSN报告的方法,及其要点导读。 2,生成报告 第1步,选择需要生成报告的网络,…