赞
踩
真没想到,距离视频生成上一轮的集中爆发(详见《Sora之前的视频生成发展史:从Gen2、Emu Video到PixelDance、SVD、Pika 1.0》)才过去三个月,没想OpenAI一出手,该领域又直接变天了
可当我接连扒出sora相关的10多篇论文之后,觉得sora和此前发布的视频生成模型有了质的飞跃(不只是一个60s),而是再次印证了大力出奇迹,大模型似乎可以在力大砖飞的情况下开始理解物理世界了,使得我司大模型项目组也愿意重新考虑开发视频生成的相关应用
本文主要分为三个部分(初步理解只看第一部分即可,深入理解看第二部分,更多细节则看第三部分)
如果只允许用10个字定义sora的模型结构,则可以是:潜在扩散架构下的Video Transformer
为方便大家更好的理解sora背后的原理,我们先来快速回顾下AI绘画的原理(理解了AI绘画,也就理解了sora一半)
以DALLE 2为例,如下图所示(以下内容来自此文:从CLIP到DALLE1/2、DALLE 3、Stable Diffusion、SDXL Turbo、LCM)
- CLIP训练过程:学习文字与图片的对应关系
如上图所示,CLIP的输入是一对对配对好的的图片-文本对(根据对应文本一条狗,去匹配一条狗的图片),这些文本和图片分别通过Text Encoder和Image Encoder输出对应的特征,然后在这些输出的文字特征和图片特征上进行对比学习- DALL·E2:prior + decoder
上面的CLIP训练好之后,就将其冻住了,不再参与任何训练和微调,DALL·E2训练时,输入也是文本-图像对,下面就是DALL·E2的两阶段训练:
阶段一 prior的训练:根据文本特征(即CLIP text encoder编码后得到的文本特征),预测图像特征(CLIP image encoder编码后得到的图片特征)
换言之,prior模型的输入就是上面CLIP编码的文本特征,然后利用文本特征预测图片特征(说明白点,即图中右侧下半部分预测的图片特征的ground truth,就是图中右侧上半部分经过CLIP编码的图片特征),就完成了prior的训练
推理时,文本还是通过CLIP text encoder得到文本特征,然后根据训练好的prior得到类似CLIP生成的图片特征,此时图片特征应该训练的非常好,不仅可以用来生成图像,而且和文本联系的非常紧(包含丰富的语义信息)
阶段二 decoder生成图:常规的扩散模型解码器,解码生成图像
这里的decoder就是升级版的GLIDE(GLIDE基于扩散模型),所以说DALL·E2 = CLIP + GLIDE
所以对于DALLE 2来说,正因为经过了大量上面这种训练,所以便可以根据人类给定的prompt画出人类预期的画作,说白了,可以根据text预测画作长什么样
最终,sora由三大Transformer组件组成(如果你还不了解transformer或注意力机制,请读此文):Visual Encoder(即Video transformer,类似下文将介绍的ViViT)、Diffusion Transformer、Transformer Decoder,具体而言
你会发现,上述整个过程,其实和SD的原理是有较大的相似性(SD原理详见此文《从CLIP到DALLE1/2、DALLE 3、Stable Diffusion、SDXL Turbo、LCM》的3.2节),当然,不同之处也有很多,比如视频需要一次性还原多帧、图像只需要还原一帧
网上也有不少人画出了sora的架构图,比如来自魔搭社区的
首先,一个视频无非就是沿着时间轴分布的图像序列而已
但其中有个问题是,因为像素的关系,一张图像有着比较大的维度(比如250 x 250),即一张图片上可能有着5万多个元素,如果根据上一张图片的5万多元素去逐一交互下一张图片的5万多个元素,未免工程过于浩大(而且,即便是同一张图片上的5万多个像素点之间两两做self-attention,你都会发现计算复杂度超级高)
可能有同学问,这么做有什么好处呢?好处太多了
总之,基于 patches 的表示,使 Sora 能够对不同分辨率、持续时间和长宽比的视频和图像进行训练。在推理时,也可以可以通过在适当大小的网格中排列随机初始化的 patches 来控制生成视频的大小
DiT 作者之一 Saining Xie 在推文中提到:Sora“可能还使用了谷歌的 Patch n’ Pack (NaViT) 论文成果,使其能够适应可变的分辨率/持续时间/长宽比”
当然,ViT本身也能够处理任意分辨率(不同分辨率相当于不同长度的图片块序列),但NaViT提供了一种高效训练的方法,关于NaViT的更多细节详见下文的介绍
而过去的图像和视频生成方法通常需要调整大小、进行裁剪或者是将视频剪切到标准尺寸,例如 4 秒的视频分辨率为 256x256。相反,该研究发现在原始大小的数据上进行训练,最终提供以下好处:
sora不是第一个把扩散模型和transformer结合起来用的模型,但是第一个取得巨大成功的,为何说它是结合体呢
总之,总的来说,Sora是一个在不同时长、分辨率和宽高比的视频及图像上训练而成的扩散模型,同时采用了Transformer架构,如sora官博所说,Sora is a diffusion transformer,简称DiT
关于DiT的更多细节详见下文第二部分介绍的DiT
首先,训练文本到视频生成系统需要大量带有相应文本字幕的视频,研究团队将 DALL・E 3 中的重字幕(re-captioning)技术应用于视频
关于DALLE 3的重字幕技术更具体的细节请见此文2.3节《AI绘画原理解析:从CLIP到DALLE1/2、DALLE 3、Stable Diffusion、SDXL Turbo、LCM》
2.3 DALLE 3:Improving Image Generation with Better Captions
2.3.1 为提高文本图像配对数据集的质量:基于谷歌的CoCa微调出图像字幕生成器
2.3.1.1 什么是谷歌的CoCa
2.1.1.2 分别通过短caption、长caption微调预训练好的image captioner
2.1.1.3 为提高合成caption对文生图模型的性能:采用描述详细的长caption,训练的混合比例高达95%..
其次,如之前所述,为了保证视频的一致性,模型层不是通过多个stage方式来进行预测,而是整体预测了整个视频的latent(即去噪时非先去噪几帧,再去掉几帧,而是一次性去掉全部帧的噪声)
但在视频内容的扩展上,比如从一段已有的视频向后拓展出新视频的训练过程中可能引入了auto regressive的task,以帮助模型更好的进行视频特征和帧间关系的学习
更多可以参考Google的W.A.L.T工作,下文将详述
OpenAI 发现,视频模型在经过大规模训练后,会表现出许多有趣的新能力。这些能力使 Sora 能够模拟物理世界中的人、动物和环境的某些方面。这些特性的出现没有任何明确的三维、物体等归纳偏差 — 它们纯粹是规模现象
对于“sora真的会模拟真实物理世界”这个问题,网上的解读非常多,很多人说sora是通向通用AGI的必经之路、不只是一个视频生成,更是模拟真实物理世界的模拟器,这个事 我个人觉得从技术的客观角度去探讨更合适,那样会让咱们的思维、认知更冷静,而非人云亦云、最终不知所云
首先,作为“物理世界的模拟器”,需要能够在虚拟环境中重现物理现实,为用户提供一个逼真且不违反「物理规律」的数字世界
比如苹果不能突然在空中漂浮,这不符合牛顿的万有引力定律;比如在光线照射下,物体产生的阴影和高光的分布要符合光影规律等;比如物体之间产生碰撞后会破碎或者弹开
其次,李志飞等人在《为什么说 Sora 是世界的模拟器?》一文中提到,技术上至少有两种方式可以实现这样的模拟器
虚幻引擎(Unreal Engine,UE)就是这种物理世界的模拟器
- 它内置了光照、碰撞、动画、刚体、材质、音频、光电等各种数学模型。一个开发者只需要提供人、物、场景、交互、剧情等配置,系统就能做出一个交互式的游戏,这种交互式的游戏可以看成是一个交互式的动态视频
- UE 这类渲染引擎所创造的游戏世界已经能够在某种程度上模拟物理世界,只不过它是通过人工数学建模及渲染而成,而非通过模型从数据中自我学习。而且,它也没有和语言代表的认知模型连接起来,因此本质上缺乏世界常识。而 Sora 代表的AI系统有可能避免这些缺陷和局限
不同于 UE 这一类渲染引擎,Sora 并没有显式地对物理规律背后的数学公式去“硬编码”,而是通过对互联网上的海量视频数据进行自监督学习,从而能够在给定一段文字描述的条件下生成不违反物理世界规律的长视频
与 UE 这一类“硬编码”的物理渲染引擎不同,Sora 视频创作的想象力来自于它端到端的数据驱动,以及跟LLM这类认知模型的无缝结合(比如ChatGPT已经确定了基本的物理常识)
最后值得一提的是,Sora 的训练可能用了 UE 合成的数据,但 Sora 模型本身应该没有调用 UE 的能力
注意,和sora相关的技术其实有非常多,但有些技术在本博客之前的文章中写过了(详见本文开头),则本部分不再重复,比如DDPM、ViT、DALLE三代、Stable Diffusion(包括潜在空间LDM)等等
在具体介绍ViViT之前,先说三个在其之前的工作
而Google于2021年提出的ViViT(A Video Vision Transformer)便要尝试在视频中使用ViT模型,且他们充分借鉴了之前3D CNN因式分解等工作,比如考虑到视频作为输入会产生大量的时空token,处理时必须考虑这些长范围token序列的上下文关系,同时要兼顾模型效率问题
故作者团队在空间和时间维度上分别对Transformer编码器各组件进行分解,在ViT模型的基础上提出了三种用于视频分类的纯Transformer模型,如下图所示
区别于常规的二维图像数据,视频数据相当于需在三维空间内进行采样(拓展了一个时间维度),有两种方法来将视频映射到token序列(说白了,就是从视频中提取token,而后添加位置编码并对token进行reshape得到最终Transformer的输入)
上文说过,Google在ViT模型的基础上提出了三种用于视频分类的纯Transformer模型,接下来,介绍下这三种模型
当然,由于论文中把一个没有啥技巧且计算复杂度高的模型作为模型1:简单地将从视频中提取的所有时空token,然后每个transformer层都对所有配对进行建模,类似Neimark_Video_Transformer_Network_ICCVW_2021_paper的工作(其证明了VTN可以高效地处理非常长的视频)
所以下述三个模型在论文中被分别称之为模型2、3、4
第二个模型如下图所示,该模型由两个串联的transformer编码器组成:
对应的代码如下(为方便大家一目了然,我不仅给每一行代码都加上了注释,且把代码分解成了8块,每一块代码的重点都做了细致说明)
- # 定义ViViT模型类
- class ViViT(nn.Module):
- def __init__(self, image_size, patch_size, num_classes, num_frames, dim=192, depth=4, heads=3, pool='cls', in_channels=3, dim_head=64, dropout=0.,
- emb_dropout=0., scale_dim=4):
- super().__init__() # 调用父类的构造函数
-
- # 检查pool参数是否有效
- assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'
- # 确保图像尺寸能被patch尺寸整除
- assert image_size % patch_size == 0, 'Image dimensions must be divisible by the patch size.'
- # 计算patch数量
- num_patches = (image_size // patch_size) ** 2
- # 计算每个patch的维度
- patch_dim = in_channels * patch_size ** 2
- # 将图像切分成patch并进行线性变换的模块
- self.to_patch_embedding = nn.Sequential(
- Rearrange('b t c (h p1) (w p2) -> b t (h w) (p1 p2 c)', p1=patch_size, p2=patch_size),
- nn.Linear(patch_dim, dim),
- )
为方便大家理解,我得解释一下上面中这行的含义: Rearrange('b t c (h p1) (w p2) -> b t (h w) (p1 p2 c)', p1=patch_size, p2=patch_size)
且为方便大家和我之前介绍ViT的文章前后连贯起来,故还是用的ViT那篇文章中的例子(此文的第4部分)16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 | 16*16 |
16*16 | |||||||||||||
16*16 | |||||||||||||
16*16 | |||||||||||||
... |
- # 位置编码
- self.pos_embedding = nn.Parameter(torch.randn(1, num_frames, num_patches + 1, dim))
- # 空间维度的cls token
- self.space_token = nn.Parameter(torch.randn(1, 1, dim))
- # 空间变换器
- self.space_transformer = Transformer(dim, depth, heads, dim_head, dim * scale_dim, dropout)
-
- # 时间维度的cls token
- self.temporal_token = nn.Parameter(torch.randn(1, 1, dim))
- # 时间变换器
- self.temporal_transformer = Transformer(dim, depth, heads, dim_head, dim * scale_dim, dropout)
-
- # dropout层
- self.dropout = nn.Dropout(emb_dropout)
- # 池化方式
- self.pool = pool
-
- # 最后的全连接层,用于分类
- self.mlp_head = nn.Sequential(
- nn.LayerNorm(dim),
- nn.Linear(dim, num_classes)
- )
- def forward(self, x):
- # 将输入数据x转换为patch embeddings
- x = self.to_patch_embedding(x)
- b, t, n, _ = x.shape # 获取batch size, 时间维度, patch数量
-
- # 在每个空间位置加上cls token
- cls_space_tokens = repeat(self.space_token, '() n d -> b t n d', b=b, t=t)
- x = torch.cat((cls_space_tokens, x), dim=2) # 在维度2上进行拼接
-
- x += self.pos_embedding[:, :, :(n + 1)] # 加上位置编码
- x = self.dropout(x) # 应用dropout
- # 将(b, t, n, d)重排为((b t), n, d),为了应用空间变换器
- x = rearrange(x, 'b t n d -> (b t) n d')
- x = self.space_transformer(x) # 应用空间变换器
- x = rearrange(x[:, 0], '(b t) ... -> b t ...', b=b) # 把输出重排回(b, t, ...)
- # 在每个时间位置加上cls token
- cls_temporal_tokens = repeat(self.temporal_token, '() n d -> b n d', b=b)
- x = torch.cat((cls_temporal_tokens, x), dim=1) # 在维度1上进行拼接
-
- x = self.temporal_transformer(x) # 应用时间变换器
- # 根据pool参数选择池化方式
- x = x.mean(dim=1) if self.pool == 'mean' else x[:, 0]
- # 通过全连接层输出最终的分类结果
- return self.mlp_head(x)
第二个模型如下图所示,会先计算空间自注意力(token中有相同的时间索引,相当于同一帧画面上的token元素),再计算时间的自注意力(token中有相同的空间索引,相当于不同帧下同一空间位置的token,比如一直在视频的左上角那一块的token块)
由于实验表明空间-时间自注意力或时间-空间自注意力的顺序并不重要,所以第三个模型的结构如下图所示,一半的头仅在空间轴上计算点积注意力,另一半头则仅在时间轴上计算,且其参数数量增加了,因为有一个额外的自注意力层
不过,该模型通过利用dot-product点积注意力操作来取代因式分解factorisation操作,通过注意力计算的方式来代替简单的张量reshape。思想是对于空间注意力和时间注意力分别构建对应的键、值,如下图所示(图源自萝卜社长)
在ViT之前,图像领域基本是CNN的天下,包括扩散过程中的噪声估计器所用的U-net也是卷积架构,但随着ViT的横空出世,人们自然而然开始考虑这个噪声估计器可否用Transform架构来代替
2022年年底,William Peebles(当时在UC Berkeley,Peebles在声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。