文章目录
Stable Diffusion 是 Stability AI 公司开源的 AI 文生图扩散模型。之前在文章 扩散模型 (Diffusion Model) 简要介绍与源码分析 中介绍了扩散模型的原理与部分算法代码,满足基本的好奇心后便将其束之高阁,没成想近期 AIGC 的发展速度之快大大出乎我的意料,尤其是亲手跑出下面这张 AI 生成的图像, Stable Diffusion 终又重新回到我的视野:
作为一名算法工程师,需要有一双能看透事物本质的眼睛,这张图片最先吸引我的不是内容,而是其生成质量:图像高清、细节丰富,非之前看到的一些粗陋 Toy 可比,红框中标注出来的不协调之处,也是瑕不掩瑜。因此,进一步分析 Stable Diffusion 整个工程框架的原理,实在是迫在眉睫,期待日后能修复红框中的不协调之处,为 AIGC 的进一步发展做出一个技术人员应有的贡献。
Stable Diffusion 整个框架的源码有上万行,没有必要全部分析。本文以 “文本生成图像(text to image)” 为主线,考察 Stable Diffusion 的运行流程以及各个重要的组成模块,在介绍时采用 “总-分” 的形式,先概括整体框架,再分析各个组件(如 DDPM、DDIM 等),另外针对代码中的部分非主流逻辑,比如 predict_cids、return_ids 这些小细节谈谈我的看法。文章内容较长,准备拆分成多个部分。
源码地址:Stable Diffusion
之前我写过很多代码分析文章,但在我遇到问题重新去翻阅时,发现要快速定位到目标位置并准确理解代码意图,仍然存在很大困难,密密麻麻的整块代码,每一次阅读都仿若初见,不易理解,原因在于摘录时引入过多的实现细节,降低了信息的传播效率。
经过一番思考,我不再图省事,决定采用伪代码的方式记录核心原理。平时我深度分析代码时会采用这种方式,对代码进行额外的抽象,相对会耗些时间,但私以为这是有益处的。举个例子,比如 DDPM 模型前向 Diffusion 的代码,如果我用伪代码的方式去写,将是如下的效果:
可以看到,刨除掉无关的实现细节之后,DDPM 的实现是如此的简洁,倘若再配合一定的注释,可方便快速理解,让人获得一种整体而全面的掌控感。此外还应该在文中多增加框图、模型图等来对代码的实现细节进行更直观的展示。
可以在微信中搜索 “珍妮的算法之路” 或者 “world4458” 关注我的微信公众号, 可以及时获取最新原创技术文章更新.
另外可以看看知乎专栏 PoorMemory-机器学习, 以后文章也会发在知乎专栏中.
首先看下 Stable Diffusion 文本生成图像整体框架(文章绘图吐血…希望有一天 AI 能进行辅助):
上图框架内的模块较多,从上到下分为 3 块,我在图中使用 Part 1、2、3 进行了标注。框架包含训练 + 采样两个阶段,其中:
训练阶段 (查看图中 Part 1 和 Part 2),主要包含:
[B, C, H, W] 转换为 [B, Z, H/8, W/8],其中 Z 表示 latent space 下图像的 Channel 数。这一过程在 Stable Diffusion 代码中被称为 encode_first_stage;[B, K, E] 的 embedding 表示(即 context),其中 K 表示文本最大编码长度 max length, E 表示 embedding 的大小。这一过程在 Stable Diffusion 代码中被称为 get_learned_conditioning;context,在训练时以 context 作为 condition,使用 Attention 机制来更好的学习文本与图像的匹配关系;采样阶段(查看图中 Part 2 和 Part 3),也就是我们加载模型参数后,输入提示词就能产出图像的阶段。主要包含:
[B, K, E] 的 embedding 表示(即 context);[B, Z, H/8, W/8] 的噪声 Noise,利用训练好的 UNetModel 模型,按照 DDPM/DDIM/PLMS 等算法迭代 T 次,将噪声不断去除,恢复出图像的 latent 表示;[B, Z, H/8, W/8])进行 decode(解码),最终恢复出 pixel space 的图像,图像大小为 [B, C, H, W]; 这一过程在 Stable Diffusion 中被称为 decode_first_stage。经过上面的介绍,对 Stable Diffusion 整体会有个较清晰的认识,下面就可以按图索骥,将各个重点模块尽力去弄明白。限于个人精力与有限的空闲时间,目前除了 FrozenCLIPEmbedder 和 DPM 算法 (图中没写),Stable Diffusion 的其他模块都大致看了看,包括:
后面会简单介绍一下,记录学习过程。
在阅读代码的过程中,发现有些重量级的论文必须得阅读一下。扩散模型的理论推导还是有些复杂的,有时候公式推导和代码实现相互结合看,可以加深对知识的理解。这里列一下对我阅读代码有很大帮助的论文:
下面对 Stable Diffusion 中的重要组成模块进行简要分析。主要包含:
predict_cids、return_ids 等谈谈看法首先介绍一下 UNetModel 结构,方便后续的文章直接进行引用。
画了一下 Stable Diffusion 中使用的 UNetModel,就不分析代码了,看图很容易将代码写出来。Stable Diffusion 采用 UNetModel 这种 Encoder-Decoder 结构来实现扩散的过程,对噪声进行预估, 网络结构如下:
模型的输入包含三个部分:
[B, C, H, W] 的图像 image; 注意不用在意表示大小时所用的符号,应将它们视作接口,比如 UNetModel 接收大小为 [B, Z, H/8, W/8] 的 noise latent image 作为输入时,这里的 C 就等于 Z, H 就等于 H/8, W 就等于 W/8;[B,] 的 timesteps[B, K, E] 的文本 embedding 表示 context, 其中 K 表示最大编码长度,E 表示 embedding 大小模型使用 DownSample 和 UpSample 来对样本进行下采样和上采样,此外出现最多的模块是 ResBlock 以及 SpatialTransformer,其中图中每一个 ResBlock 接收来自上一个模块的输入以及 timesteps 对应的 embedding timestep_emb (大小为 [B, 4*M],M 是可配置的参数);而图中每一个 SpatialTransformer接收来自上一个模块的输入以及 context (Prompt 文本的 embedding 表示),使用 Cross Attention,以 context 为 condition,学习 Prompt 和图像的匹配关系。但图上只在虚线框中显示了两个模块有多个输入,其他模块没有画出来)
可以看到,最后模型的输出大小为 [B, C, H, W], 和输入大小相同,也就是说 UNetModel 不改变输入输出的大小。
下面再分别看看 ResBlock、timestep_embedding、context 以及 SpatialTransformer 的实现。
ResBlock 网络结构图如下,它接受两个输入,图像 x 以及 timestep 对应的 embedding:
timestep_embedding 的生成方式如下,用的是 Tranformer(Attention is All you Need)这篇 paper 中的方法:
即 context 的实现。Prompt 使用 CLIP 模型进行编码,我没有对 CLIP 模型详细学习,暂时也没有深入看的打算,后续有机会再补充;代码中使用预训练好的 CLIP 生成 context:
最后再看下 SpatialTransformer 的实现,其模块比较多,在接收图像作为输入时,还使用 context 文本作为 condition 信息,二者使用 Cross Attention 进行建模。进一步展开 SpatialTransformer, 发现包含 BasicTransformerBlock ,它实际调用 Cross Attention 模块,而在 Cross Attention 模块中,图像信息作为 Query,文本信息作为 Key & Value,模型会关注图像和文本各部分内容的相关性:
我觉得可以用一种朴素的想法来理解这里 Cross Attention 的作用,比如训练时给定一张马吃草的图,以及文本提示词:“一匹白色的马在沙漠吃草”,在做 Attention 时,文本中的 “马” 这个关键词和图像中的动物(也是 “马”)的关联性更强,因为权重也更大,而 “一匹”、 “白色”、“沙漠”、 “草” 等权重更低;此时,当模型被训练的很好后,模型不仅将可以学习到图像和文本之间的匹配关系,通过 Attention 还可以学习到文本中的各个关键词想突出图像中哪些主体。
而当我们输入提示词用模型来生成图像时,比如输入 “一匹马在吃草”,由于模型此时已经能捕捉图像和文本的相关性以及文本中的重点信息,当它看到文本 “马”,在黑盒魔法的运作下,会重点突出图像 “马” 的生成;当它看到 “草” 时,便重点突出图像 “草” 的生成,从而尽可能生成和文本进行匹配的图像。
至此,UNetModel 各个重要组件基本介绍完毕。
由于 UNetModel 模型结构并不复杂,看图基本就能写出代码,一图胜千言啊。另外我标注了每个模块输出结果的大小,很方便在大脑中运行模型,哈哈哈。
本文大致介绍了一下 Stable Diffusion 文生图代码的整体框架,列出了扩散模型部分核心论文,简要分析了 UNetModel。后续再分析其他核心组件。
发现 AIGC 的发展实在太快了,学不过来啊… 愈发觉得庄子所言甚是:以有涯随无涯,殆矣!
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标
网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.
1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>
参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍 介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。 内容有: ①:Hub模型的方法介绍 ②:服务器端代码介绍 ③:前端vue3安装并调用后端方法 ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke() 去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on
一、机器人介绍 此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接
目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'
目录H2数据库入门以及实际开发时的使用1.H2数据库的初识1.1H2数据库介绍1.2为什么要使用嵌入式数据库?1.3嵌入式数据库对比1.3.1性能对比1.4技术选型思考2.H2数据库实战2.1H2数据库下载搭建以及部署2.1.1H2数据库的下载2.1.2数据库启动2.1.2.1windows系统可以在bin目录下执行h2.bat2.1.2.2同理可以通过cmd直接使用命令进行启动:2.1.2.3启动后控制台页面:2.1.3spring整合H2数据库2.1.3.1引入依赖文件2.1.4数据库通过file模式实际保存数据的位置2.2H2数据库操作2.2.1Mysql兼容模式2.2.2Mysql模式
我想使用ruby-prof和JMeter分析Rails应用程序。我对分析特定Controller/操作/或模型方法的建议方法不感兴趣,我想分析完整堆栈,从上到下。所以我运行这样的东西:RAILS_ENV=productionruby-prof-fprof.outscript/server>/dev/null然后我在上面运行我的JMeter测试计划。然而,问题是使用CTRL+C或SIGKILL中断它也会在ruby-prof可以写入任何输出之前杀死它。如何在不中断ruby-prof的情况下停止mongrel服务器? 最佳答案