阅读视图

发现新文章,点击刷新页面。

什么是多模态大模型

是什么

  1. 在机器学习领域,”模态”被用来描述不同类型的数据形式,如文本、图像、视频、音频等。
  2. 最开始以 ChatGPT 为代表的大语言模型,都是只支持文本这个单一模态。
  3. 可以同时处理文本、图像、音频等多种形式的数据输入输出的大模型,就是多模态大模型。

特点:端到端

一个模型能同时理解和处理多种模态的数据输入。

  1. 非端到端的例子:
    1. 在 ChatGPT 上,可以调用 DALL-E 生成图片,但实际流程是 prompt → GPT4模型 → 生成细节提示词 →DALL-E模型 → 生成高质量细节图像,只是一个能力串联,并不是一个多模态大模型。
    2. 在豆包或其他一些LLM APP上,支持语音输入→文字和语音输出,实际流程是 语音→ASR模型转文字→LLM→文字→tts模型转语音,并不是端到端 语音→LLM→语音。
  2. 端到端的例子:
    1. GPT4o 的实时语音对话,流程是 语音→ GPT4o模型→语音。延迟低、语气/音色/停顿/语义都能综合理解到。
    2. claude3.5 支持按要求识别图片,流程是 图片+prompt → claude模型→文本。能很好结合 prompt 按要求输出对图片的识别。
  3. 端到端的好处:
    1. 模型能直接从原始的数据中学习不同模态之间的关联和映射关系,发现隐藏在数据中的复杂跨模态模式,可以 scale up 达到涌现,没有中间折损,可以做到低延时。

原理:基于大语言模型

  1. 多模态大模型以大语言模型为基础模型,复用已预训练好的模型理解能力,在上面增加其他模态的能力,对齐多个模态的特征让原大语言模型能理解。GPT4o 就是在 GPT4 基础上增加音频/图片的特征能力,它在文本上的理解能力还是跟 GPT4 差不多。
  2. 模型通用的基本构造(参考这篇文章):
    1. 编码模块,将图片/视频/音频等模态编码为特征 token,一般还伴随一些压缩的处理。

    2. 投影层(Projector),让不同模态的特征 token 语义对齐,这是模型重点要训练的部分。

    3. LLM,多个模态的特征都在基础 LLM 大模型上做处理理解,通常 LLM 本身也要在新的模态训练过程中做相应微调,适配新的模态。

    4. 若支持多模态输出,也同样有模态对应的投影层和解码层。

      1

当前模型能力

把多模态大模型能力拆分成输入理解、输出生成的话:

  1. 当前主要在发展输入理解部分,较多大模型支持了图片理解、视频理解能力。
  2. 输出生成上,主流的还是各模态各自在发展阶段,如图片生成模型、视频生成模型、音乐生成模型,都是独立单任务模型。GPT4o、gemini 支持了音频的端到端理解和生成,其他大模型基本还只支持文本生成。
  3. 有一些新的模型在尝试大统一,输入输出都支持 文本、图片、音频、视频多种模态,如腾讯刚出的 VITAAnyGPTUnified-IO,都处于起步阶段,看起来综合效果还没很好。

图片理解

通往多模态的第一步,基本都是在LLM上加入图像识别能力,已成为目前大模型标配,这是最自然最广泛的需求,难度也不高。

现状:大部分模型 文心一言,豆包,GPT4o,claude、Gemini 等都支持,开源的 Qwen-VLLLaVAYi-VLMiniCPM-V 等也非常多。

能力:大模型加持的图像识别,各项能力都能胜任,包括OCR、图片物体理解、逻辑理解、文档图表理解、隐喻理解等。

效果:能力比较全面,但也相对平庸,相对垂直领域专门优化的图片识别模型,效果有差距。例如各大模型在OCR能力上的评测,相对最好的OCR垂直模型有差距,更垂直的像植物识别这种,跟PictureThis 这类专门优化过的差距会更大。对图片理解上,结合大模型能力效果会比较好(评测)。图片识别评测维度非常多,有各种维度的评测标准,从个人实际观感上综合识别效果最好的是claude 3.5

原理

Yi-VL 为例,其他模型差不太多,都是在 LLM 基础上增加图像编码处理然后端到端训练 :

2

  1. 图中的Large Language Model是基础模型,Yi-34B-Chat或Yi-6B-Chat。
  2. Vision Transformer(ViT)模块用于图像编码,用CLIP模型。
  3. Projection 模块处理图像特征,训练后的这一层让图像特征跟文本特征空间对齐,包含 layer normalizations 和 Multilayer Perceptron(MLP)。
  4. 火焰标志表示训练,雪花标志标识冻结不训练。训练分了3步,用了不同的 图片-文本 数据对,最后一步 LLM 也参与训练了。
  5. LLaVA/MiniCPM-V也是类似的结构和训练过程,训练最后一步都会微调到LLM基模参数。

应用

  1. 图片搜索、语义搜索、物体识别、人脸识别这些垂类小模型已经能做好。
  2. 给图片配诗、给图片配音、拍照搜题+解题、阅卷、验证图识别等,这些用结合LLM的大模型,门槛会降低,效果也会有优化。
  3. 截屏识别自动化,试卷阅卷,这种场景结合 LLM 才能做好

视频理解

现状:部分主流大模型支持通过把视频抽帧为一系列静态图进入模型分析,本质上是图片理解能力,能做到一定程度的内容理解,GPT4o 基本是这样,一些支持图片识别的大模型稍加调整也能支持这种方式。少部分模型能识别视频和对应的音频,如Gemini、阿里开源的 VideoLLaMA2。有比较多的开源模型在做各种方式的尝试,更好识别视频帧之间的时间逻辑关系、跟音频/文字模态做更好的整合理解。

效果:有个项目 Video-MME 专门分析各大模型视频识别理解能力,测了多个模型在各种理解任务上的表现,包括时间/空间关系的感知和逻辑推理、文字/物体感知、信息总结等,视频类型包括电影、体育、vlog等,能结合整个视频里的信息做理解。各模型在2分钟以内的短视频上理解能力已经不错,中长视频会差比较多,Gemini、GPT4o和效果最好的,开源的模型差距还比较大。

原理

视频理解的主流方法是使用图像编码器从视频中提取帧,对其进行编码,然后用压缩模块压缩视频编码信息,再将其输入到 LLM 中,与文本输入进行联合理解。

也有很多模型在尝试各种方案,如智谱 CogVLM2 加入时间定位、时间戳的数据,让模型能感知视频对应时间。有些模型尝试改造 LLM,不让视觉特征与文本混合,在 LLM 内部增加独立的 transformer 模块处理,如 mPLUG-Owl3

VideoLLaMA2 为例看下大致原理, 综合支持了视频和音频输入,视频和音频分别编码:

  1. 视频按帧编码为特征,经过STC Connector 处理,Spatial Convolution 处理视频帧特征,提取空间信息,Spatial – Temporal Downsampling 降低视频数据维度,再经过投影层与其他模态特征对齐,一起进入大模型。音频也是一样的流程。
  2. 训练分成多个步骤,视频、音频分别单独训练,最后再联合视频音频一起训练,每个步骤有对应的数据集,看起来只有最后一步联合训练,LLM基模的参数才会参与训练。

(题外话,名字叫 VideoLLaMA2,实际上跟Llama没关系,LLM基模用的是Mistral)

3

应用

基于类似的原理,可以自行训练在垂类表现更好的视频模型,例如:

  1. 视频配文案
  2. 视频内容总结、解读
  3. 视频内容搜索(以自然语言搜索长视频特定内容出现位置)
  4. 影视解读(影视时长过长,当前大模型 context 能力还不具备)

音频理解&输出

能力:GPT4o 和 Gemini 都支持了音频理解和输出,能很好理解音频里的语气、语调、节奏、风格等信息,细微的喘息、叹气声都能很好识别和生成,实时性也能做到很高。

原理

目前 GPT4o 和 gemini 相关公开的具体实现细节较少,最基本的原理跟上述应该差不多,语音编码为token→投影层对齐其他模态→输出预测语音token→解码为语音。可以看看 AnyGPT 的实现:

4

应用

最主要的应用是拟人真实程度高的实时语音对话,从GPT4o的演示看,这点对体验影响很大,即使智能能力进步不大,真实性和实时带来的 AGI 感受也是很强。

语音转录、会议记录总结等,虽然已经有很多 ASR 模型能做到转文字,但整个音频的内容、多人对话、语气情绪都能输入大模型,结合大模型理解能力,预计能做到更好的效果。

其他

端到端生成图片 Gemini 号称支持,但没找到相应资料,视频生成单模型都还在摸索,结合 LLM 还早。多模态大模型整体处于发展阶段,各模态的理解和生成还没到很高的水平,整体进展没预期快,但以当前的能力,针对垂直场景做一些训练,是能够较低门槛做出一些之前做不到或做不好的应用了,例如视频配旁白。

视频生成模型调研 – 人像视频/基础模型/可控编辑/DiT

经常看见有一些视频生成的模型出来,类型还不太一样,简单学习和调研下这个领域和相关技术的情况。在我所看到的有限的范围里,可以把近期出现的视频生成能力分成两类:

  1. 一类是专门精细化控制人物表情动作的模型,驱动一张人像照片动起来。这类模型存在已久,老技术也能实现,近期不断有新模型出现,效果也越来越好,业界好像没针对这一类命个名,姑且叫它人像视频
  2. 另一类是通用的视频生成基础模型,包括基于扩散模型的,以及 sora 出现后的 DiT 架构模型。另外跟 Stable Diffusion 图片生成的生态类似,也会有一些为视频生成基础模型配套的可控编辑扩展模型

人像视频

先来看看人像视频,常见有两类:

  1. 表情控制:输入人物表情视频,让图片的人脸跟着做同样的表情。变种是输入音频,让图片人脸跟着音频的口型动,talking photo。
  2. 姿态控制:输入人物动作的视频,让图片的人跟着视频的动作动。火过的 case 是通义千问的全民舞王科目三。
表情控制(Vimi) 姿态控制(Animate Anyone)

这里的技术都不是这波大模型后才有的,上个时代已经有很多做得不错,上一波爆火的蚂蚁呀嘿已经是 2021 年的事了,相关论文也是 2019 年就有了:《First Order Motion Model for Image Animation》。后面不断有新的方案,包括基于和不基于扩散模型的方案。下面列几个近期出现,看起来还行的方案。

表情控制

基于扩散模型

基于扩散模型的方案,大体思路看起来是在原网络插入 pose/人脸点位 控制,跟 ControlNet 原理差不多,扩散模型本身除了 SD Unet 那套外,基本都会加入视频生成常见的 spatial-attention 和 temporal-attention。

  • AniPortrait(华为):24年3月发布。支持从语音生成对应每一帧的口型和人脸位置图,再基于 SD1.5 扩散模型 + motion module 从参考图生成视频结果。开源可用
3
  • megActor(旷世科技):24年5月发布。没有把视频解析成中间关键点去驱动图片,而是原视频画面直接驱动,以预期得到更生动的效果,2个UNet网络,推理成本看起来会高一些,效果稳定性一般。只支持视频面部特征,不支持音频对口型,开源可用
4
  • EchoMimic(蚂蚁):24年7月发布。同时使用音频和面部特征进行训练,可单独用音频生成,也可以结合输入视频的面部特征生成,结果更自然,开源可用,comfyUI module可用。
5

还有几个不开源的:微软的VASA-1,阿里的EMO,都是语音对口型,朝着数字人方向做的。

非扩散模型

非扩散模型的方案,看起来基本也是先把人脸节点生成完,再用其他的网络结构去应用到图上生成视频。

  • LivePortrait(快手):24年7月刚出的模型,模型很小,主干网络是 ConvNeXt-V2-Tiny,28M参数量,各部分加起来就500M,号称速度很快,单帧推理时间在 RTX 4090 GPU 是 12.8ms,都能稳定实时输出 60 帧视频了,很适合端上部署,这也是非扩散模型的优势,还有个特点是能快速精确控制眼睛和嘴巴的开闭程度,动画稳定。comfyUI module 也有了。
6
  • VividTalk(阿里):跟 AniPortrait 有点像,同样是训练音频→表情嘴型关键点,音频→头部运动关键点,再经与图片一起进入另一个网络生成最终视频,只是这网络不是基于扩散模型。未开源,真实效果未知。
7

姿态控制

8
  • magic-animate(字节),23年底发布。Pose 序列不是 OpenPose 人体骨骼,而是丰富的整个人的动作 densePose,视频转 densePose 还比较麻烦,densePose 序列用 ControlNet 的方式去做生成的控制,另外有一个网络去编码人物形象做IP保持。试用下来,参考图跟 pose 的形象姿态差异大的场景也能支持,比如让蒙娜丽莎跳舞,但这种场景下效果不太好,人脸基本不保持,只保持了人物衣着的IP形象。已开源。
9

还有其他很多,MimicMotionMuseVFollow Your PoseDreaMoving 等,大同小异。

视频生成

视频生成模型业界除了最出名的 runway、pika、sora,也陆续有不少开源的方案出来,当前已有的开源方案基本都是基于 Latent Diffusion Model,核心是 UNet 降噪网络,基于这种网络还有不少做视频可控编辑扩展模型,DiT 架构还在路上。

基础模型

  • I2VGen-XL(阿里),23年11月发布。比较常规,基于 3D-UNet 扩散模型生成,分成基础生成和高清细化两个阶段,细化阶段不是单纯提高分辨率,会改善时间连续性、引入文本输入控制内容。开源可用。
10
  • SVD(Stable Video Diffusion),23年12月发布。模型结构复用 Video LDM,主要是在 U-Net 和 VAE 解码器中分别加入时序层(temporal attention layer),SVD 论文本身在讲模型怎么训练的,包括高质量视频的微调。
11
  • PixelDance(字节),23年11月发布。特点是首尾帧机制,首帧图作为强引导,与噪点图拼接一起作为输入,严格遵守首帧图,同时尾帧图作为弱引导,训练中会随机抛弃尾帧,推理降噪过程中在步数大于τ值时也会抛弃尾帧,避免完全对齐,让生成的结果有多样性。在 DiT 架构的模型出现之前,效果基本是最好的,生成的视频运动幅度大,稳定性不错。未开源。
12
  • ConsistI2V(零一万物) ,24年2月发布。跟 PixelDance 有点像,也是首帧与噪点图拼接一起作为输入(类似 SD 的垫图),同时会把首帧也作为降噪过程条件作用在 spatial-attention 和 temporal-attention 上,较大地强调首帧图片的重要性,这样生成的视频不容易崩,一致性比较好。 已开源可在线试用
13

可控编辑

视频生成的可控编辑是指通过各种方式控制视频生成方向,例如运动方向、内容替换、风格迁移等,原理上跟图片生成的 ControlNet / IPAdatper 等机制差不多,基于上述视频生成基础模型,训练扩展模型插入原网络,控制生成方向。

图生视频控制

大部分视频生成是图生视频,在图片上圈选运动范围和运动轨迹是很自然的诉求,一代目 Runway 上的 Motion Brush 就是做这个,基本应该应该是后续正经视频生成模型的标配,也有开源模型基于 SVD 等基模做了这个能力。

  • mofa-video(腾讯),24年7月发布,基于 SVD。可以训练多种 adapter,控制图片生成,包括手势控制、人脸关键点控制、姿势关键点等,每种控制 adapter 独立训练,可以独立使用或组合使用,比较灵活通用。开源可用。
14

视频内容编辑/风格化

这一类指 Video to Video,修改原视频上的元素,替换衣服、人物等,部分也包含了视频风格迁移能力。

  • ReVideo(腾讯),24年7月发布,基于SVD。通过修改第一帧和绘制轨迹线,对视频中特定区域内容和运动进行定制化编辑。使用分阶段训练的策略,简单理解为,A阶段重点训练运动轨迹,B阶段重点训练内容替换,再进行结合。开源可用。
15
  • I2VEdit(商汤),基于SVD,利用成熟的图像工具编辑第一帧,再将第一帧的修改应用到整个视频,实现局部替换和风格化。
16
  • AnyV2V(华为): 比较通用的视频编辑框架,可以灵活用于多个视频生成模型,包括I2VGen-XL、ConsistI2V、SEINE, 同样是先通过各种方式改造编辑视频首帧,再插入视频生成模型,将风格和替换内容扩展到整个视频,实现视频编辑能力。通用于多个模型的原理,简单理解是提取了空间注意力/时间注意力特征注入了原生成模型的 spatical-attention/temporal-attention 模块,理论上差不多架构的模型都能通用。 可试用
1718
  • animatediff:animatediff 比较特殊,不是基于 SVD,而是基于图生成 Stable Diffusion,在上面训练加上运动模块 Motion Module,学习了视频片段的运动知识,支持视频生成。很早发布,在 SD 生态配合 IPAdapter / ControlNet 等各种扩展和 LoRA 模型一起使用,组合出很多有趣的应用,看到的大部分视频风格转动漫风基本是基于这个方案。
19

DiT

DiT(Diffusion Transformer) 是视频生成基础模型的一个算法架构,应该放在基础模型部分的,但它太新了,想单独抽出来细看一下。

上面大部分模型,包括可控性的扩展模型,核心底层都是基于经典的 UNet 架构,但 Sora 出来后,业界公认 DiT 架构才是未来,毕竟效果太碾压了,最近可灵 / Luma 的出现也印证了这点。架构范式转移到 DiT 后,原先在 UNet 上做的各种可控雕花,看起来基本上是没法迁移到 DiT 架构的,一切得重来。

DiT 架构开源的只见到去年11月 sora 出来之前的 Latte,研究性比较多,效果一般。其他靠谱的开源模型还没见到,毕竟 Sora 还没见影,可灵luma 也刚出。(DiT架构的图片生成就有一些,比如腾讯混元

20

DiT的架构图,与 LLM 的架构同源,核心是 transformer 模块,跟基于 UNet 的模型都不一样,我们尝试来看看在这个架构下视频生成的推理过程:

  1. 初始化一个噪声视频。
  2. 视频会先转换成潜空间的表示,后续的运算都在潜空间里运算,这点跟 Stable Diffusion 一类的扩展模型一致,视频应该是使用 VQ-VAE 进行编码到潜空间。
  3. 视频的表示会被分割成一个个 patch 块,每个 patch 块是一个 token,patch == token。
  4. 这些代表整个视频的 patch 块集合,一起进入 DiT Block。这个 DiT Block 就是个类 transformer 模块,与 LLM 一样核心也是多头注意力,在这里会计算每个 token 之间的注意力,加上引导词和步数条件,做相应计算。
  5. 按 LLM 模型的套路,这里 N 个 DiT Block 跑完,整个流程跑完,输出会是预测的下一个 token。但我理解这里的输出并不是下一个 token(一个 token 只是一个 patch),而是这里的 patch 合集经过这些 DiT Block 的注意力运算和条件引导,变换成离最终视频更近的一个表示,也就是对这里的噪声视频做了一次降噪。
  6. 如果是20次降噪,重复20次这个过程,一个纯噪声视频生成最终清晰的视频。
  7. 如果要垫图,首帧图尾帧图,只需要让图片跟输入的纯噪声视频做一些结合就可以。

可以看到跟其他的 UNet 为核心的架构有本质差别,像 ControlNet 各种可控性的研究没法迁移,需要另外找控制路径。从业界在这领域卷的程度看,预期发展还是会非常快,等下一个 DiT 架构的靠谱视频生成模型开源,也应该很快会有人在上面把相关可控能力不断研究补齐了。

感想

这个领域给我感受是模型超多,看不完跟不上,只能先了解个大概,在有具体应用场景时,再根据需求做相应深入的调研。

为什么这么多模型?看起来它训练的资源门槛没那么高(比 LLM 低),有公开训练数据集(WebVid 和 LAION),论文上都会把方法给出,width=甚至模型和代码也开源,各研究者很容易从中吸收学习做改进,再造一个模型,现在也没出现一个效果通用秒杀一切的模型,所以三天两头出个新模型是常态。

DiT 架构后,视频生成和视频编辑这些模型大概率要淘汰,而人像视频可能在较长一段时间内仍有应用空间,如果要做 AI 视频短片,人物表情动作精细控制挺重要,DiT 架构目前还没看到有能做到精细控制的技术,基于 Unet 的通用视频生成模型这么长时间也没法做好这块的可控性,可能一段时间内还得靠原有技术做这里的可控后编辑。

Transformer 里的 Q K V 是什么

Transformer 作为新 AI 时代的基石,有必要深入了解下。网上对 Transformer 的教学文章/视频非常多,很多讲得很好,像 3Blue1Brown 的讲解视频,以及这篇文章。整个详细过程原理写不来,本文主要记录一下其中我觉得比较容易混淆的 Attention 模块运算过程,主要是里面的 Q K V 的概念/运算过程/作用。

1

这是 Transformer 架构图,左边是 encoder,右边是 decoder,实际 LLM 大模型是只由右边 decoder 构成,这里面大部分是常用的 Feed Forward(前馈网络)/ Add(残差连接)/ Norm(层归一化),核心还是 Multi-Head Attention 模块,我们来具体看看 Multi-Head Attention 模块里做了什么。

输入

假设一个字是一个 token,输入是”我有一个玩”(用于推测下一个字”具“),5 个字,每个字用一个向量表示,每个向量假设是 9 维(GPT3 是 12288 维),也就是用 9 个数值表示这个字,那每个词顺序排下来,就组成了 5 行 9 列的输入矩阵,称他为 X,每一行代表一个词。

2

6每一个圈圈代表一个数值。”我“字由蓝色的9个数值表示,“有”字是绿色的9个数值。这 9 个数值组成一个 9 维向量,这里每个字对应的向量值是一开始定好的,至于怎么定的不细说,可以看看相关文章。

这个输入矩阵经过 Multi-Head Attention 模块运算,输出另一个同宽高的矩阵,接下来详细看看这个运算过程。

3

权重矩阵 & Multi-Head Attention

Multi-Head Attention 是由多个 Self Attention 模块拼接而成,如果它只有一个 head,就是一个 Self Attension 模块。

Self Attention

Self Attention 模块里,会包含 Wq Wk Wv 三个参数权重矩阵,模型训练过程就是不断调整 Wq Wk Wv 里的数值。

这几个权重矩阵的行和列数,需要满足:

  1. 行数:输入矩阵 X 会与它们进行相乘,所以行数需要与输入词向量的维度匹配,也就是 9。
  2. 列数:Transformer 中整个 Attention 模块的输入数据和输出数据维度应该是一致的,才能多层重复叠加,从矩阵相乘特性知道,这些权重矩阵的列数也应该对齐词向量的维度,还是 9。

所以如果这里是单个 Self Attention,Wq Wk Wv 就是行数和列数都是与词向量维度一致的矩阵,也就是 9×9。

Multi-Head Attention

但这里希望模型能捕获到单词间的多种不同注意力,所以会把它拆出来再拼接。假设把它拆成 3 个 head,那就是能捕获到 3 种单词之间不同的关系。这里拆出来的 3 个 head 就是 3 个 Self Attention 模块,每个模块有自己的 Wq Wk Wv 矩阵,行列数是 9 x 3。这里每个 Self Attention 独自进行注意力运算后,再组合拼接。

4

这里文字描述得比较绕,见后续运算过程和结果的图示比较清晰。

Attention 运算过程

先来看这里每个 Self Attention 模块的运算过程。

这里输入向量分别与 Wq Wk Wv 相乘,得到新的矩阵 Q K V,Q(query) K(key) V(value) 名字已经对应了它的含义,看完它的运算过程后,再来补充下对它含义的理解。

可以认为这里 Q K V 这几个新的矩阵,每一行仍然是表示一个单词 token 向量,只是换了种表示 (矩阵的乘法特性,例如第一行里的每一个数据都是由原矩阵第一行与 W 矩阵运算得来,与其他行无关)。

下图是 Q 矩阵的运算过程,K V 的过程一样,只是 W 权重矩阵的值不同,略过。

5

接着要做的是,计算每一个单词对于其他单词的 Attention 系数,这是一个两两可重复排列组合。上面 5 个单词,每个单词都 K 矩阵里的自己以及其他所有单词逐一计算出一个值,生成一个 5 x 5 的矩阵。这个矩阵的计算方式就是 Q*KT(K的转置矩阵),由矩阵乘法特性可以看出,这样算出来的矩阵,就是单词之间的关系值,比如第一行第五列数值,就是“我”和“玩”之间的注意力关系值。下图用颜色表示这个过程。

6

相乘后对这个矩阵进行 softmax (在这之前还会除以 √dk 向量维度,可以先忽略),每一行的和都为1,这里的矩阵第 i 行的数据表示的是第 i 个单词与其他单词的关系,这里归一化后,数值可以表示理解为,从全文范围上,每个单词对这第 i 个单词的重要程度比例。

最后这里的 Attention 系数矩阵,与矩阵 V 相乘,得到的是新的结合了每个单词之间 Attention 信息的矩阵。输出的矩阵中每一行还是表示一个单词,但这个单词向量经过这里注意力运算后,每个单词向量都集合了上下文每个单词的注意力信息。

7

单独拆除这里的第一行看看它的意义,单词”我“跟每一个字的注意力权重,再乘以每个字在 V 矩阵里的向量表示,结果再相加,组成最后的结果。比如这里第一个字”我“跟第三个字”一“的权重是0.1,那”一“的向量值对运算后最后表示”我“这个字的向量结果影响很小,如果是 0 就是没有影响。

8

上述整个过程,可以用这个数学公式表示:

9

Multi-Head Attention 模块里每个 Self Attention 模块都做同样的运算(但里面的 Wq Wk Wv 权重不同,数值结果不同),拼接起来,形成最终的结果,这个结果矩阵里,每一行每个字的表示,都已经集合了与其他所有字的注意力关系信息。

10

整个过程实际上还有个掩码的机制,按上述运算,这里输出的每个单词向量都包含了上下文所有的信息,通过掩码机制,会变成每个单词只包含单词所在前面位置的信息,比如第二行“有”只包含了“我”和“有”的信息,没有后面”一“”个“”玩“的信息。这里不继续展开了。

这里每一行包含了前面所有单词的注意力信息,也就可以通过这里的表示预测下一个单词,所以从这个矩阵最后一行“玩”的向量数值,就可以用于预测对应下一个单词是什么。

整个 Multi-Head Attention 的运算过程大致是这样了。实际模型如 GPT3,单词向量维度是12288,上下文长度2048(每个 token 都要跟2048个token计算注意力),每个 Multi-Head Attention 分成 96 个 head,同时有 96 层叠加,也就是 96 个 Multi-Head Attention,运算量是巨大的。

Q K V 的作用

Q 可以理解为原输入的词数据,拿着这个数据找谁跟我有关系。K 是被找的数据,用于计算输入的每个词之间的关系。Q 和 K 是为了算出 Attention 关系系数,知道每个 K 的数据跟 Q 是什么关系。

如果 Q 和 K 是同个输入变换来的,那就是自注意力,如果是不同输入变换来,那就是交叉注意力,比如 Stable Diffusion 里 Unet 的交叉注意力模块中,Q 是文字 prompt,K 和 V 是图片信息,Q 与 K 计算的是文字与图片信息的 Attention 关系系数。

K 和 V 是同个数据源,这个数据源,从 Q 和 K 的运算知道每个 Q 与数据源的关系系数,再与数据源做运算就是把这个关系数据作用到源数据上,源数据去做相应偏移,也就是可以在 Q 的作用下对源数据做相应推测。

感想

为什么这样一个算法架构,能衍生出智能,而且这个架构能扩展到多模态,语音、图像、视频基于它都有非常好的效果?我个人理解,最核心有两个点:

  1. 上下文信息充足
  2. 并行计算能力强

其他算法架构如果能充分融入上下文信息,规模大了也能有智能,只是 Transformer 可并行运算的特性,让目前的计算机算力可以触摸到涌现的那个点。

手机能跑图生成和 LLM 大模型吗

💡 能,但还比较勉强。

在客户端上跑大模型,一定是未来的趋势。

  1. 上个时代 AI 的核心应用是推荐系统,推荐是依赖海量数据的,海量数据只有服务端上存在,在推荐这主场景下客户端 AI 上能做的事很少,发展得比较吃力。
  2. 生成式 AI 时代,最大的应用就是模型本身,只有训练时依赖海量数据,使用时并不依赖数据,那理论上只要客户端硬件资源足够,在客户端使用,跟在服务端使用,场景和效果是一致的。
  3. 生成式 AI 在端上跑模型,最大的优势是成本。成本是当前生成式 AI 应用除了效果以外第二大关键因素,在用户客户端上跑模型,对服务提供方来说就是 0 成本,0 成本使更多场景大规模免费应用成为可能。其他的优势还包括 隐私保护、实时性、离线可用

硬件条件

那当前手机设备硬件条件如何?我们可以通过一些指标对手机和服务端的算力差距有个大概认识。

显存:一个模型能不能跑,取决于显存够不够,显存不够模型无法加载。

  1. 服务端一般用独立显卡,有独立显存。
  2. 手机通常使用系统级芯片 Soc(System on a Chip),无独立显卡,SoC 中包含了 CPU、GPU、基带等多个组件,使用统一内存架构允许 CPU 和 GPU 共享同一块内存,所以手机 GPU 显存跟手机内存是一个东西。

性能:而模型跑得快不快,取决于芯片性能怎样。

  1. 芯片性能取决于很多因素,例如芯片架构、显存带宽,而算力是其中一个,通常用TOPS(万亿次每秒 Tera Operations Per Second)指标来衡量算力。TOPS 默认是针对 INT8 整型数的处理次数,另一个指标 TFLOPS 是针对 Float32 浮点数的处理次数。
  2. 在通用 GPU 以外,现代芯片会搭载专门处理 AI 运算的硬件加速器,NVIDIA 是 Tensor Core,手机 SoC 芯片是 NPU (Neural Processing Unit 神经网络处理单元),以下是 Tensor Core 和 NPU 的运算性能指标。
  3. 不同芯片性能,特别是涉及不同芯片架构设计的,应该以实测数据作为对比,但当前缺乏这类数据,先用 TOPS 指标看个大概。

我们看看当前常用的英伟达各种显卡芯片,以及移动端设备芯片这几个指标的情况:

芯片 TOPS(INT8) 显存 搭载设备
服务端芯片 H100 2000 80G /
A100 624 80G /
NVIDIA A30 330 24G /
NVIDIA A10 250 24G /
移动设备芯片 骁龙8 Gen3 45 16G 小米14/一加12/荣耀6/Redmi K70 Pro
Apple M4 38 24G(iPad) iPad Pro / MacBook Pro
Apple A17 Pro 35 8G iPhone 15 Pro / Max
天玑9300 20 12G/16G vivo X100 / OPPO Find X7
Apple A15 15 6G iPhone 13 Pro Max
Apple M1 11 16G/32G MacBook Pro

手机内存显存与系统共用,正常能提供给 APP 使用的内存只有1/2~2/3,所以可以认为对 APP 来说,手机设备的可用内存需要减半,否则有内存不足 APP 被系统 kill 的风险,像 iPhone 15 Pro 预计是4G,小米14等高端机是8G。

生图模型要求

那当前主流的生图模型,对硬件的要求是怎样?

显存

Stable Diffusion XL base 参数量 3.5B(35 亿),精度 Float16(16位bits,2个字节),换算下来参数总大小 6.5G,实际文件大小6.94G,在模型推理过程中,参数得加载到显存中,也就是显存至少6.9G,同时在模型推理过程过程中,也有一些中间值需要保留在显存中,所以正常需要8G – 12G显存支持。

实测在 Macbook 跑起来,占用了10.3G。极端情况下,通过显存调度之类的技术在 4G 显存也能勉强跑起来,但会性能较差或不稳定。

这个显存要求,在 iPhone 15 Pro 基本是不满足的,Android 高端机整体内存普遍较大,勉强可以支持

性能

我在 A10 卡和 M1 MacBook Pro 上分别实测了下,SDXL base 模型生成 1024×1024 的图,A10大概6.4秒,M1 大概 95 秒。如果只看 TOPS 指标,A10 220TOPS 是 M1 11TOPS 的20倍,实测跑下来 95秒/6.4秒 = 14.8倍,也就是 M1 与 A10 的实际差距没那么大。

真实性能受各种因素影响,每个芯片有各自的优化方案,单用 TOPS 指标难以衡量,但可以看个大概。如果只看 TOPS 倍率,内存完全足够的情况下,搭载骁龙 8 Gen3 的小米 14 生成同样的图预计需要 17.6s,官方宣传15s左右。

芯片 TOPS SDXL 生图耗时 设备
NVIDIA A10 220 6.4s(实测) 服务器
Apple M1 11 95s~140s(实测) MacBook Pro
骁龙8 Gen3 45 17.6s(预估) 小米14

量化

原 SDXL 模型硬件要求高,但如果可以牺牲部分效果,是有办法对原模型做压缩,让它可以跑在低内存手机的。

模型为了成本、速度考虑,一般会进行不同程度的量化。量化就是降低模型参数的精度,神经网络模型中的参数通常使用32位浮点数 Float32 表示,但 Float32(4个字节) 存储大计算量也大,进一步可以压缩映射到更低的数值表示,包括 Float16、Int8、Int4 甚至 Int2 都有应用,只是会带来不同程度的效果损失。

模型量化后,参数需要的存储空间降低,所需要的显存跟着降低,而因为数据量小了,计算量也相应减小,模型推理速度也会加快。

Draw things 这个应用,将 SDXL base 模型量化到 Int8 的精度,模型大小 2G,可以跑在 4G 内存的 iPhone 上(APP 最多只能使用 2G 内存,为此作者做了系列优化)。实测 SDXL base Int8 模型 在 iPhone 13 Pro Max(A15,6G)上,生成 1024*768 的图需要 180s,跟它硬件 TOPS 算力差得有点多,可以认为是推理架构上为了节省内存做的妥协。

LLM 大模型要求

那在 LLM 大模型上,情况怎样?

我们拿阿里通义千问qwen的模型大概看下它 7B 和 72B 在不同量化下的大小。qwen 最大模型是 72B,而 llama3 最大是 400B(还在训练中),可以预估 400B 模型会是接近1T的体量。

如果拿400B模型对标GPT4,72B 模型对标 GPT3.5+,可以看到目前可用的 LLM 模型推理成本和硬件要求是非常高的,比图生成高几十倍。

模型 参数量 量化 大小 生成 2048 token 所需显存
Qwen 1.8B Int4 1.88G 2.9G
Int8 2.49G
Float16 3.6G
7B Int4 5.86G 8.2G
Int8 9.13G
Float16 15.41G
72B Int4 41.65G 48G
Int8 111.86G
Float16 144.18G
Stable Diffusion XL base 3.5B Float16 6.94G

qwen 最小的 1.8B 模型,生成 2048 个 token 最低需要 2.9G 显存,当前高端机是可以跑起来的。但 1.8B 效果差很多,预计只能预训练做特定任务。7B 可用性高一些,可以看到 7B 模型就没多少手机能支持了,骁龙8 Gen3 宣传号称 7B 模型推理每秒执行 20 个token,未搜到相关实测。

Google 用于端侧的 Gemini Nano 有 1.8B、3.25B 两种参数量。苹果之前放出来的 OpenELM 模型有 0.27B ~ 3B 的参数量,最新 iOS18 的 AI 模型估计用的就是 OpenELM,限制了只有最新 iPhone 15 Pro 能跑。

iOS Android 都在往系统级集成端侧 LLM 大模型这个方向做,系统集成有更多的硬件资源调度权限,在当前资源条件下容易先做起来,APP 能用到的资源有限,目前很难跑起来。

所以手机跑 LLM 大模型,用最小的模型,在最高端的手机上理论可行,实际应用还要再等等。

端模型问题

除了硬件理论情况,端模型也有一些问题待解决:

  1. 对服务提供方,有技术保密问题:在端上部署模型,模型、prompt、workflow 都是存储在本地,虽然可以做各种加密,但总能破解,如果服务方视这些为核心竞争力,那就难以以这种方案部署,更有可能的是端云协同的架构,部分运算放客户端,云端处理核心和保密部分。
  2. 对于手机用户:手机耗电、发热、耗时问题:大量运算跑满 GPU 必然导致手机发热严重耗电高,在持续使用的场景下体验会比云端差,手机芯片跑起来速度也会不如云端快,手机端系统需要做好资源控制和平衡。
  3. 生态问题:英伟达的CUDA、PyTorch 生态,相关工具链/社区,在端上都是需要重新建立的,当然只要有场景有诉求,这些可以补上,但需要时间。
  4. 场景和价格问题:能运行大模型的手机,在未来几年价格还是高的,目前还没有比较好的理由让用户接受这个溢价,对用户来说,像生图、修图、LLM当前服务端能提供最好的,在端侧跑模型体验没提升,就没必要溢价买个高端机,高端机平民化速度就会慢。在没有 killer APP 的情况下, 需要靠手机厂商和系统强推了,例如 iOS 18 新Siri 只在最高端机可使用。

结论

图生成硬件要求不算高,高端机已经摸到实际应用的门槛,预计再过一两年,硬件进一步提升,不追求效果极致的图生成应用场景,大部分会部署在客户端上。

LLM 硬件要求高,iOS/Android 系统级应用有条件接入,APP 基本还用不了。等系统应用被大众认知和接受,硬件普遍升级,才轮到 APP 端发挥。

当前过渡阶段,端云协同的方案会比较多,预计也会存在很长一段时间。例如图生成,可以将部分运算(比如 VAE 编解码)放到端上,主生成流程放云端。iOS 18 Siri 也会判断如果用户输入的是简单指令,就不请求服务端,直接端模型生成。

在复刻黏土风图生成中学习(2) — ControlNet / IPAdapter / instantID

接上篇,继续优化我们的黏土风 workflow。

引导图控制

来看看上篇里的最后一个case:

1

黏土风格效果还可以,但人物动作总会跟原图不一致,一会双手放地板,一会侧身。图生图一般希望整体轮廓、人物姿态与原图一致,有没有办法控制?

我们可以给它加上 ControlNet 节点,用 canny 边缘检测,试试控制画面主体的轮廓结构:

3

这下就比较准确地还原了原图的姿势了。

ControlNet 介绍

ControlNet 是一种神经网络架构,能做到通过添加额外的引导图片输入(如边缘图、姿态图等)来控制 SD 模型的扩散生成方向,实现对图像生成过程的精确引导。

通过这套架构,可以训练出每种控制方式对应的模型,生图过程中应用这个模型,输入对应的引导图,就能生成对应的图。

以下是 ControlNet 作者训练好的几种模型,以及用这些引导图生成的图片效果:sketch草稿、map法线贴图、depth深度图、canny边缘、line线、edge边缘、场景、Pose人物姿势

2

看下 ComfyUI ControlNet 相关的这几个节点:

4

  1. 每个 ControlNet 模型的输入,都是预处理好的一张引导图,一般用简单的算法就能处理出来,这里用的是 ControlNet canny 边缘控制的一个模型,对应一个 canny 算法节点,一个古老的算法,python 的 OpenCV 库就有。
  2. 接着加载 canny 对应的 ControlNet 模型。
  3. 这张边缘图片输入到 ControlNet 模型,跟文本一起,作为模型降噪生成过程中的引导,指引降噪方向,生成符合文本描述、符合图片边缘形状的图。

这里的 canny 可以替换成 sketch、depth、pose 等算法,搭配上对应的 ControlNet 模型,就能实现不同的控制方式。

ControlNet 原理

扩散生图模型出现后,就有很多人探索怎样更好控制它的生成,显然如果只能用文字生图,可控性太差,最直观的还是能通过草图指引控制它画什么。

怎么解这个问题?对模型简单做一个端到端微调是否可行?例如想让模型按 canny 检测出来的边缘去生成图片,那造一堆 原图 – canny图 的配对作为训练集,微调让模型学习到边缘图和最终生图的关系,是否就可以?大思路是这样,但需要解决微调带来的过拟合、破坏原模型能力的问题,需要设计一个网络结构,能很好认得 canny 引导图特征、跟扩散模型很好结合、效果稳定。

有很多人做过不同的研究,提出过多种方法,ControlNet 的方法相对前人有很大优势,能稳定用在各种场景上,效果最佳,应用广泛。

网络架构

来看看 ControlNet 的这张架构图,我把相应的输入输出示意图加上:

4

理解这个网络结构前,可以看回这篇文章理解下 SD UNet 网络。上采样 = encoder,下采样 = decoder,为了方便和与上图对应,下面就只提 encoder 和 decoder。

这个图左边是 SD 原 UNet 网络,右边是 ControlNet 新加的网络。首先是把 SD 原网络的参数冻结,不参与训练,这跟前面介绍的 LoRA 套路是一样的,训练不影响原网络,只调整新网络,有诸多好处。

接着它把 SD UNet 网络里的 encoder 和 middle 部分复制出来,再用零卷积(zero convolution)连接到原 SD 网络对应的 decoder 层。几个要点:

  1. 与 decoder 的连接:
    1. 整个 ControlNet 网络的目的并不是按 UNet 网络流程有一个输入和输出,ControlNet 网络是只有输入没有输出的,它的目的是在 encoder 识别处理引导图(外加与降噪图/文字Prompt/步数的关系),再把这些信息跳跃连接回原 SD 网络的 decoder,所以 ControlNet 网络本身是不需要 decoder 的,图上的零卷积只是把 encoder 层跳跃连接回 decoder 对应的层。
    2. 为什么这样做?SD UNet 网络里 encoder 各层保留了图片的细节信息,decoder 只有宏观信息,所以把 encoder 各层都跳跃连接回 decoder 对应的层,这样 decoder 拥有宏观和微观细节所有信息,进行一步步生图。那 ControlNet 这里做的,就是为 decoder 增加信息,不止是原降噪图的细节信息,还加上引导图信息,指引降噪生成方向。
  2. 为什么用 1×1 卷积作为连接,而不是做一个简单的叠加?
    1. 如果叠加,会破坏原生图能力。这个 1×1 的卷积,最开始训练前初始化值为0,encoder 里的参数经过这个零卷积相乘,最终输出是0,叠加作用在 SD 网络里的值也是0,也就是训练一开始这个网络对 SD 生图完全没有影响,保留完整的生图能力。随着训练进行,这里的 1×1 不再是零卷积,会逐渐变成一个个权重值,那参数经过这个卷积叠加到 SD 网络,影响就不再是0,可以指引降噪方向。我理解为整个训练过程中生图能力都没被破坏,引导图 ControlNet 对网络带来的影响是一点一点叠加上去的。
    2. 另一点,自己猜的,与 1×1 卷积 相乘的主要作用是降维,引导图信息有限,低通道低维度的数据已经能比较好地表示,不需要跟降噪生图那么大的数据量,对原网络的影响也小些?
  3. 其他几个小点:
    1. 它这里原样地复制了 SD UNet 网络一半的参数,并没有像 LoRA 那样对数据进行压缩,也可以理解为因为这样所以对网络的控制可以更细致。所以它的模型大小是比LoRA大很多,但比原 SD 模型小的。
    2. 一开始输入的 Condition 是像边缘图这样的图片,图上没画出来的是这个图片还会经过一个四层卷积层,把这张图片转化为隐空间的表示。
    3. SD 的输入,包括噪声图、文字 prompt 和 timestep 步数,都会进入到 ControlNet 网络参与训练,因为 ControlNet 是从 UNet 原网络复制出来的,有完整的处理这些输入的能力。

训练过程

沿着上面这张图再复述一下训练过程:

  1. 准备好训练数据:原图 – canny引导图 – 文本描述(可选)
  2. 前向传播:
    1. ControlNet:在 SD 每一步降噪过程中,噪声图与引导图 c 叠加,与文本prompt、步数 一起进入 ControlNet 网络,这里的输入跟 SD 原网络是一样的,每层的输出也一样,每一层推理出的噪声图数据表示,都通过 1×1 卷积连接回到 SD 网络。
    2. Stable Diffusion:噪声图、文本 prompt、步数,一起输入网络推理出下一步噪声图,跟原 SD 训练和推理流程一致,只是这里的 decoder 网络已经叠加了 ControlNet 的网络。
    3. SD 这里的文本输入也可以为空,训练网络只拟合边缘图信息,实际上 ControlNet 作者训练的那几个模型,训练过程中有一半数据集是无文本输入。
  3. 损失函数计算:我们知道每一步期望这个网络输出的图是什么(参考SD扩散训练过程),评估预测和输出的差异。
  4. 反向传播:把差异(损失函数梯度)回传网络,更新网络参数值。SD 网络是锁住的,不回传,参数不变。只在 ControlNet 网络做回传和参数值更新,这里的更新包括每个 encoder 块的参数值,以及1×1卷积的权重值。

训练完后,ControlNet 部分就变成了一个“认得” canny 边缘图片条件的网络,给这个网络输入其他的 Canny 图,经过 ControlNet 作用叠加在 SD 模型上,引导 SD 降噪方向。

论文上还提到一个现象:突然收敛,模型没有逐渐学习识别输入的边缘图片条件,而是在训练到6000多步的时候,突然认得边缘图开始遵循这个输入条件生图。为什么是会突然收敛,也没说为什么,特定架构下的现象,有些玄学。

6

这是非常通用的架构,只要是跟原图关联的引导图,像上面示例的 sketch、depth、pose 等都可以用同样的方法训练出对应的 ControllNet 模型。

若要自己训练一个 ControlNet 模型,作者有篇详细的教程和探讨:《Train a ControlNet to Control SD》

消融实验

消融实验(Ablation Study)是机器学习领域常用概念,指通过修改或移除模块,来测量这些模块/结构设计对结果的影响,也就是 ABTest。

作者这篇文章分享了做的两个消融实验:《Why ControlNets use deep encoder》。尝试了 ControlNet-Lite 和 ControlNet-MLP 这两个更简单的网络对比效果。这俩不是从原 UNet 网络复制出来,而是自定一个网络,再把这网络作用回原 UNet 网络,ControlNet-Lite是简单的卷积网络,ControlNet-MLP是用像素级多层感知机(Multilayer Perceptron)构造这个网络。

5

文中可以看出,在 prompt 充足的情况下,这俩简单的架构都能得到很好效果,甚至更简单的架构也能起作用,要指引图片按轮廓生成,并没有很难,难点在与生图模型的结合。在 prompt 不清晰、或没有 prompt 的情况下,这俩架构表现就差多了,生成的图无意义。

之前也有不少其他人的尝试各种方法,比如这篇论文《Sketch-Guided Text-to-Image Diffusion Models》,不足的地方也是与 SD 图生成的语义没法很好结合,只认识边不能让物体与边很好结合。作者认为 ControlNet 现在的架构能做到跟原网络很好结合,两个关键点:

  1. 用零卷积连接,确保了训练刚开始时对原网络无影响。上面也有说到,沿用 SD 原网络对物体的理解能力,再逐步调节,每一步训练都完整应用 SD 原本的高质量生图能力。否则按随机初始化叠加,一开始几个训练步骤下来,整个网络识别物体的能力很快被破坏。
  2. ControlNet 的网络也需要接收 Prompt 作为输入,这样 ControlNet 编码器才能认识 Prompt 对象,不会与用户输入脱节,比如训练过程中 ControlNet 网络认识了房子的轮廓,如果没有 Prompt 参与训练,就算用户输入蛋糕,网络也会引导向生成房子,而不是蛋糕模样的房子。

ControlNet 先学到这里,我们继续来优化黏土风 workflow。

人脸保持

我们拿目前加了 ControlNet 的 workflow 试试人物的效果:

8

效果还行,但人脸跟原图有些对不上,如果我们想让人脸更接近原图,做一个人物美化的黏土风,有没有什么办法?

可以试试给 workflow 加上 IPAdapter 节点,IPAdapter 有强大的风格迁移、人脸保持的能力,先看看效果:

9

用的是针对人脸训练的 ipadapter-face 模型,人脸美化多了,相比之前相似度高一些,算是人脸美化风格的黏土风,但也不怎么像。

提高 IPAdapter 的权重,能得到越来越像的脸,但跟黏土风融合得不是很好,权重越大黏土风格的感觉越弱:

10

IPAdapter 还有一个专门为人脸保持做的版本 IPAdapter-FaceID,与黏土风格的融合效果好一些,但人脸特征保持程度也一般:

11

在进一步优化前,先来认识一下IPAdapter。

IPAdapter 介绍

IPAdapter 是垫图神器,提供风格迁移能力,输入一张参考图,模型会按这张参考图的风格去生成图片。IPAdapter 目前有两类模型:

  1. IPAdapter
    1. 提供整图风格迁移能力,与直接图生图有本质区别,原理上图生图是在原图加噪点基础上做演化生成,IPAdapter 是让模型认识图片风格要素,生成跟原图宏观风格一致的图片。12
    2. 结合 ControlNet 等插件,在一些场景下能得到很惊艳的效果:13
    3. IPAdapter 针对 SD1.5 和 SDXL 训了好几个模型,也针对人脸迁移做了优化, **ip-adapter-plus-face** 就是其中之一,使用裁剪的人脸图像作为训练集,对人脸的迁移效果好一些。上面第一步用的就是这个模型。14
  2. IPAdapter-FaceID
    1. 在 IPAdapter 的架构下,使用人脸特征代替用 CLIP 编码的图片特征,模型对人脸识别能力更强。这个版本实验中,不允许商用。15
    2. 跟着上面 IPAdapter-FaceID 的 workflow 说明一下各模块:16
      1. 用 InsightFace 提取人脸特征
      2. 人脸特征不像图像特征那么容易学习,因此这个模型配套训练了一个 LoRA 提高学习效果。
      3. 仅使用人脸特征,模型生成结果不稳定,受 Prompt 的影响很大,因此 IPAdapter-FaceID-Plus 版本尝试将人脸特征和 CLIP 编码的图像特征结合起来,所以这里还是需要一个 CLIP 模块。

IPAdapter 原理

17

IPAdapter 由两部分组成:提取图像特征的编码器,以及把图像特征接入网络而新增的解耦交叉注意力模块。

  1. 图片编码器:对参考图编码,提取图像特征
    1. IPAdapter 对参考图的编码,使用了 CLIP 模型,但不是 SD 内置的 CLIP,CLIP 是一个模型家族,作者应该是挑了对图像特征识别编码效果更好的 CLIP 模型。
    2. 后续新出的 IPAdapter-FaceID,是使用了 人脸特征 FaceID 代替图像特征,具体来说是用 InsightFace 库提取人脸特征向量进入网络,更好保留参考图里的人脸身份特征。
    3. 编码后的图像,这里加一个可训练小型投影网络,通过 Linear layer 和 Layer Normalization 投影到长度4的特征序列中,进入网络。
  2. 解耦交叉注意力(decoupled cross-attention):
    1. 回顾 SD UNet 网络的构成,整个网络有16个 Transformer 模块,每个 Transformer 模块里有一个自注意力层和一个交叉注意力层。
    2. 将编码后的图像特征加入到 SD UNet 网络,常规做法是图片特征与文字特征相加,再一起进入 Transformer 模块里训练。但 IPAdapter 用了另一种方式,它向这些 Transformer 模块另外增加一个交叉注意力层,用以处理图像特征,然后把文字和图像两个交叉注意力层相加,称为解耦交叉注意力。
    3. 为什么这样做?不跟文本 prompt 混合,这样图片的特征可以在网络中完整保存下来,跟文字一样具有独立引导能力。
    4. 如果不用解耦交叉注意力机制会怎样?作者做了消融实验,用一般的方法 — 图片特征与文本特征直接连接,一起嵌入到 UNet 的交叉注意力层中,结果如下图的 Simple adapter 所示,能根据图像风格生图,但质量低很多。18

ComfyUI 上 IPAdapter 的两个节点,一个是 CLIP 图片编码器,一个是包含架构图里红色区域可训练参数的 IPAdapter 模型。

训练时跟 ControlNet / LoRA 等一样,也是冻结原 SD 网络,只训练新加的 IPAdapter Transformer 网络,大概2200万个参数,IPAdapter SD 1.5 的模型大小基本 44M,对应着 22M 个参数。但 IPAdapter SDXL 的模型大了20倍,原因不明(原 XL 参数量只比 1.5 大 7 倍)。

InstantID

IPAdapter 在很多场景生图场景下做风格迁移和人脸保持都是神器,但在我们黏土风 workflow 下表现一般,我们试试另一个专门针对人脸迁移的技术:InstantID

19

人脸特征的保持以及黏土风格融合的效果比 IPAdapter 好很多。试过其他图,人脸轮廓特征也很明显能更好保留下来:

20

InstantID 原理

InstantID的 原理很简单,可以近似理解为 InstantID = IPAdapter-FaceID + 人脸ControlNet。

看这两张图,ComfyUI 里使用 InstantID 的几个模块,跟架构图对应,由三部分组成:

22

21

  1. 用 InsightFace 库提取人脸特征,用一个 projection layer 投影映射成跟文本的特征空间一致的向量表示。
  2. 添加解耦交叉注意力层,与 IPAdapter 一致。
  3. 加一个面部识别的 ControlNet,但有些小改动:
    1. 只使用五个面部关键点(两个用于眼睛,一个用于鼻子,两个用于嘴巴)作为条件输入,而不是细粒度的 OpenPose 面部关键点。防止强调多余的面部特性,比如嘴巴闭合这种是可以由prompt控制,而不需要保持的。
    2. 原 ControlNet 文本 Prompt 是加入网络训练的,这里没有加入,只用人脸信息作为ControlNet 中交叉注意力层的条件,主要是希望这个网络只控制人脸,不受文本对人脸描述的影响。

前两步基本就是 IPAdapter-FaceID,第三步就是一个特制的 ControlNet。

从前面效果看起来,第三步这个人脸特征 ControlNet 对人脸特征保持作用很大,用已有的技术方法做组合微调,已经能很好解决一些问题。

最后

我们使用 ControlNet、IPAdapter、InstantID 对黏土风格 workflow 做优化,希望能达到跟原图一致性较高、人脸迁移较好的效果,其中 InstantID 组合了前面两个技术,有很强的人脸迁移能力,但这也带来副作用。在原网络上叠加各种修改,对原生图模型都会造成不同程度的破坏,比如加了 InstantID 后,原文字 Prompt 和 Canny ControlNet 的控制就没那么精准了,上面几个例子可以看出,原来的 ControlNet Canny 边缘图已经很难起作用了,这很好理解,在原网络上叠加的处理,各部分是相对独立的,很难有非常好的融合,InstantID 把方向强力往人脸保持上引,其他输入条件就会被弱化。

这种 Adapter 类,需要在效果和原模型侵入程度间保持平衡,不同场景选择不同的方案,在某些场景要更好的效果,还是得自行微调模型,目前还没看到很完美的方案。

到这里已经可以有一个还算可以、对人脸风格化友好的黏土风格图生成 workflow 了。目前黏土风 workflow 要再进一步优化到生产环境,就是继续调整 Prompt、调整各组件参数,或者训练专有的 LoRA 模型了。

目前对应 workflow 见下图,可在 ComfyUI 上导入:

23

 

参考资料

ControlNet 论文:https://arxiv.org/abs/2302.05543

ControlNet如何为扩散模型添加额外模态的引导信息:https://zhuanlan.zhihu.com/p/605761756

精确控制 AI 图像生成的破冰方案,ControlNet 和 T2I-Adapter:https://zhuanlan.zhihu.com/p/608609941

浅谈扩散模型的有分类器引导和无分类器引导:https://zhuanlan.zhihu.com/p/582880086

使用 diffusers 训练你自己的 ControlNet:https://huggingface.co/blog/zh/train-your-controlnet

深入浅出完整解析ControlNet核心基础知识:https://zhuanlan.zhihu.com/p/660924126

快速理解AIGC图像控制利器ControlNet和Lora的架构原理:https://blog.csdn.net/colorant/article/details/136732221

InstantID技术小结:http://www.myhz0606.com/article/instantID

IP-Adapter 原理和实践:https://zhuanlan.zhihu.com/p/683504661

IPAdapter使用:https://www.runcomfy.com/zh-CN/tutorials/comfyui-ipadapter-plus-deep-dive-tutorial

新一代“垫图”神器,IP-Adapter的完整应用解读:https://developer.jdcloud.com/article/3483

如何在 ComfyUI 中使用 IPAdapter Plus 进行风格迁移:https://www.comflowy.com/zh-CN/blog/IPAdapter-Plus

IP‐Adapter‐Face:https://github.com/tencent-ailab/IP-Adapter/wiki/IP‐Adapter‐Face

理解 Stable Diffusion UNet 网络

前面的学习中,我们把 SD UNet 网络当成黑盒,不太影响对图片生成大致原理的理解,但在继续学 SD 的过程中,发现 ControlNet、T2I-Adapter、IPAdapter 等这些技术,都是在原 SD 网络模型上以各种方式对网络做修改叠加,要理解这些技术,还是得先了解下 SD UNet 网络结构的一些细节,不然看得很费劲。

SD 模型构成

从之前的学习我们知道,Stable Diffusion 模型里包含了三个组件:CLIP、VAE、UNet,这三个组件的参数和大小分布(来源):

组件 参数个数 文件大小 占比
CLIP 123,060,480 492 MB 12%
VAE 83,653,863 335 MB 8%
UNet 859,520,964 3.44 GB 80%
Total 1,066,235,307 4.27 GB 100%

整个生图的核心网络就是 UNet。UNet 最初是用于医学图像分割任务而提出来的,但它的特性展现了在图像其他领域的应用潜力,后续经过扩散模型的改进,很好应用在了图像生成上,所以 Stable Diffusion 的 UNet 实际上在原 UNet 网络架构上做了一些改造。

基础 UNet 网络

我们先来看看原 UNet 网络架构:

1

  1. 左边输入图片,经过整个网络处理,右边输出同尺寸图片。(原 UNet 网络用于医学图像识别分割,所以图上右边标的输出是一张同尺寸分割图。SD 这里的输出是降噪图)
  2. 左边下采样(也可以称为编码器),右边上采样(也可以称为解码器),一张图片经过一层层下采样计算,尺寸逐渐减小(图中的网络是减小到32×32),再经过右边层层上采样,恢复到原尺寸。那这里下采样和上采样的作用是什么?
  3. 下采样,是使用某种计算方式让更小的数据表示整张图片,这更小的数据代表了对这张图片高纬度的描述,而不是像素级细致的描述。
    1. 越小的数据对图片的表示和描述越宏观,有利于捕捉图片的语义特征。
    2. 例如一张猫在屋子前玩耍的地图,原图能看清所有细节,但因为细节太多,模型想要知道图里有猫和屋子,得把每个像素组合运算才行,但下采样到最小,最宏观的猫和屋子就容易识别。
  4. 上采样,是让图片的宏观小尺寸表示恢复成原图片尺寸。
    1. 比如对于图片分割(把图片上的物体分割出来),我们在下采样后的小数据量的高维表示里识别了图片的主体、边缘,最后还是要转回在原尺寸图片上表示,不然识别了也没用。
    2. 那不断下采样过程中肯定把图片细节都丢失了,再上采样,怎么可能还原图片细节?那就要说到跳跃连接(skip connection)了。
  5. 跳跃连接,也就是并不是顺着网络的方向连接,而是跳过原网络方向,跳着连接传输信息。说得有点拗口,看图很容易理解,就是图上中间的几条灰色箭头。
    1. 原网络连接方向是图片输入→下采样各节点→上采样各节点→输出图片这个链路,就是图中U字型的路径。
    2. 在这个路径之外,左边的下采样的每一层,都额外连接到右边上采样对应的层上面,将两个网络进行拼接。
    3. 上采样每一层,都在拼接了左边下采样对应层的数据后,再一起作为下一层上采样的输入。
    4. 为什么这样做,很容易理解,左边的每一层网络都保留了图片不同程度的细节,右边的每一层因为是上采样过来的,只有宏观信息,没有图片细节,那把左边图片细节信息拼接过去,右边这个网络宏观特征和微观细节都具备了,每一层都有不同程度的对图片的宏观语义理解和微观细节,就能做各种事情了,包括图片分割、语义生成图片。

UNet 网络大致思路是这样,这里面具体的卷积运算和公式,不看应该不影响对整体思路和作用的理解。

Stable Diffusion UNet 结构

最初的 DDPM(去噪扩散概率模型),和后来改良的 LDM(潜在扩散模型),对 UNet 网络逐步做了一些改造,以适合扩散模型图生成的过程,SD 是基于 LDM 实现的。

最后 SD 里的 UNet,整体结构流程跟上述一致,改造大部分是在上采样和下采样的每一层的实现里,最大的改造是引入了 ResnetBlock(残差模块)和 Transformer 模块。ResnetBlock 提升网络表达能力(原 UNet 是简单卷积模块),而 Transformer 模块的交叉注意力机制,将文本提示(prompt)的嵌入与图像特征进行融合,实现基于文本条件的图像生成。

SD UNet 每个模块的组成如图(图片来源):

2

左边下采样每层由2个残差模块和2个Transformer模块连接组成,右边上采样是各3个,中间层是2个残差模块和1个Transformer模块。(高维的d4和u1没有接入Transformer模块,原因不明,可能是试过加入后效果不佳,在高维这里加入 Prompt 交叉注意机制,文字权重太大?)

细分模块结构

里面每一块具体的结构这篇文章画得很详细,摘录学习一下。我们拿其中一个下采样模块看看:

3

两个残差模块,两个Transformer模块。这图表示了 SD 生图的三个输入:input(噪声图)、prompt_embdding(文字 Prompt)、time_embdding(步数)在这几个模块的流转和处理。这里每一个小模块处理完后,输出的可以近似认为都是一个预测的噪声图的数据表示。

残差模块的输入输出 噪声图+步数 → 预测噪声图,Transformer 模块的输入输出是 噪声图+ Prompt → 预测噪声图。

Transformer 模块

再细看一下 Transformer 模块,Transformer 模块由下图所示好几个部分组成,最主要的是 自注意力模块(SelfAttention)和交叉注意力模块(CrossAttention):

4

展开看看这两个模块:

5

自注意力模块,Transformer 结构里的 QKV 输入都是图片特征(上一层的处理结果,就是降噪图的特征),这样做可以让模型获得包含整个输入图像的感受野,捕捉图片特征中不同位置之间的关系, 全局感受力是 Transformer 架构的特点。

交叉注意力模块,它的作用是融合不同模态的输入,在这里就是融合噪声图和文本特征,Q的输入是图片特征,KV的输入是文字 prompt_embedding,让图片特征可以关注到文字输入,根据注意力权重调整图片的生成方向。文字 prompt 在整个Transformer模块中只作用在交叉注意力这部分里。

Transformer 的机制原理、QKV的含义,是另一个比较大的话题,可以先看看网上其他相关讲解,比如这篇,后续再细拆深入。

回顾

关键几个模块的组成了解了,再回到整个UNet的构成:

6

现在通过这些结构图,可以大致看到 UNet 网络里的整体处理流程,以及关键模块的作用,经过这些模块的逐个叠加,组合成一个个采样模块,再组合成 UNet 网络架构,完成整个生图运算。

这里面还有很多需要深入学习的点,当前先了解到这个维度,已经可以帮助大致理解后续 ControlNet 等网络的机制原理。

参考资料

UNet 论文:https://arxiv.org/abs/1505.04597

原版实现及 Diffusers 实现源码解读:https://zhouyifan.net/2024/01/23/20230713-SD3/周弈帆的博客,看简介挺有意思的一人)

U-Net简明教程:http://www.bimant.com/blog/unet-crash-tutorial/

Stable Diffusion1.5网络结构-超详细原创:https://blog.csdn.net/xd_wjc/article/details/134441396

Stable-Diffusion模型结构详解:https://zhuanlan.zhihu.com/p/638867353

Unet网络详解:https://blog.csdn.net/qq_58529413/article/details/125704059

Stable Diffusion XL网络结构:https://blog.csdn.net/xd_wjc/article/details/134530784

从零开始学扩散模型:https://huggingface.co/datasets/HuggingFace-CN-community/Diffusion-book-cn

在复刻黏土风图生成中学习(1) — 模型微调/LoRA 原理/图生图

继续学习 Stable Diffusion,这次想以搭建一个实际可用的生图场景 — 黏土风格作为引导,弄清楚整个流程的同时,把过程中遇到的相关概念和原理也做了解,所以这篇是掺和了应用流程和原理的文章。

ComfyUI & 模型

使用 Stable Diffusion 去生成图,有非常多的插件/模型/配置相互搭配组合使用,一般用 WebUIComfyUI 这两个工具,更推荐 ComfyUI,自由串联一个个模块,流程更清楚,网上有很多在自己电脑部署使用 comfyUI 的保姆级教程,比如这个,这里就不多介绍了。

先看 ComfyUI 这个默认的最简单的 workflow:

1

这里面简单的几个元素概念和生图流程,上篇文章都有介绍过:最左边的 Load Checkpoint 是加载 SD 模型,接着用 CLIP 模型编码文本 → 生成隐空间原始噪声图 → 采样器根据文本和噪声图输入→在隐空间里迭代降噪生成隐空间图片→最终用VAE解码图片。

为什么叫模型 checkpoint ?模型在微调训练过程中,会在关键节点保存模型参数的状态,这个保存点被称为 checkpoint,SD 有大量基于基座模型微调训练的模型,包括官方出的,比如 SDv1.5 是从 v1.2 的基础上调整得到的,SDXL Turbo 也是基于 SDXL1.0 基础上训练的,这些模型都被称为 checkpoint,这些 checkpoint 包含了生成图所需要的全部核心组件,包括 VAE、CLIP、UNet 的模型数据,可以直接使用。

那模型文件的后缀为什么是 .safetensors ?早期的模型文件后缀是 .ckpt (checkpoint缩写),一个通过 Python 序列化后的数据,使用时需要对它反序列化,这个反序列化过程也就容易被注入恶意代码,所以后面提出了新型安全的格式 safetensors,只包含张量数据(模型上的参数数据),无需反序列化,安全且速度快,目前模型基本都以这种方式存储。

我们用这个默认 workflow,选个模型,用纯提示词 claymation style, a tower 试试生成黏土风图片:(图上使用了 dreamshaperXL 模型,是在SDXL 的基础上微调的最受欢迎的一个模型)

2

可以看到效果并不是很好,比较生硬。可能加多一些细节提示词、调节下相关参数会好一些,但在图片训练过程中,黏土风格相关的图片数量应该是不多的,训练图片对应的文本描述也比较散,如果固定要这种风格,生图的 prompt 要尽量贴近训练时这类图偏对应的文本,才有可能有好一点的效果,这很难控制,也不保证效果,很难达到我们想要的风格。

模型微调

如果我要一个能更好输出黏土风格的模型,那可以给这个模型做微调,给它输入更多黏土风格的图片训练,让它学会我们具体要的是什么,针对性输出。

微调 SD 模型,目前从成本大到小,目前用得最多的有三种方式:

  1. Full Finetune:
    1. 最朴素的方式,使用图片+ 标注的数据集,进行迭代训练,对原模型所有参数进行调整,成本最高,但可以对整个模型做全面调优,大幅改变生成风格,上面的 dreamshaperXL 就是以这种方式。它训练数据量要求大、计算资源消耗高、最终模型就是包含所有模型参数的 checkpoint。
    2. 这种训练我理解适合大量的数据、对模型整体做调优较合适,如果只是想在特定领域,用少量数据,比如把某只猫,把某个人脸、某个物品训练进去让模型认识,那很可能出现过拟合问题(数据不够多样,污染了通用词,比如拿自家的猫训练,最终整个模型对 cat 这个输入只能生成自家的猫),或欠拟合问题(训练样本太少,没有影响到网络参数,训练无效)。
  2. Dreambooth:
    1. 针对 Full Finetune 过拟合和欠拟合、数据量大的问题的一种解决方案,数据量要求小,个位数的图片可训练,能很好还原训练图片里的人物/物品,同时不会污染原模型,能正常用原模型的能力,只在有特殊 prompt 的情况下命中微调的效果。它跟 finetune 一样是修改整个模型的参数,所以产物跟原模型一样大,也是个完整的 checkpoint。具体原理可以看这些文章(1 2)的介绍。
  3. LoRA:训练门槛极低,只需要个人PC的算力、个位数(三五张图片)也能训练出特定人物、风格的微调模型,也能达到很好的效果,生成的是外挂文件,体积小可插拔,是目前 SD 使用最广的微调模型,原理下面细讲。

像黏土风格这种诉求,仅是一种风格化的优化,是比较适合使用 LoRA 模型的,从 civitai (最大的SD 模型社区)上找了个黏土风的 LoRA 模型 CLAYMATE,在原 workflow 简单加上这个 LoRA模型,先看看应用的效果:

3

同样的提示词下,效果好了很多,是比较舒服的黏土的风格。

每个 LoRA 都有个触发词,上面用的这个模型触发词就是 claymation style,我理解相当于训练时大部分图片的 prompt 标签都加上了这个词,这样使用这个词时,模型能更好定位到训练到的数据。比如下面去掉这个claymation style ,即使用了 LoRA,也对应不上这个 LoRA 的风格。

4

这模型是怎么训练的?基本上流程是,选好图片→处理图片(裁剪+加提示词)→用工具 Kohya 训练→看结果调参重复,具体跟着网上教程走就行,各种参数细节参考这里,我还没真正执行过训练,先不多说。另外训练的整个代码生态都是围绕 NVIDIA 显卡建立的,Mac 没有 NVIDIA 显卡,没法训练。虽然理论上可行,但社区生态不友好,基本不可用,要训练只能搞台 PC 或用云服务器。

LoRA 原理

来具体看看 LoRA 的原理,全称 Low-Rank Adaptation,低秩适应。什么是低秩?为什么能做到微调成本低文件小效果也不差?

Full finetune

先看看正常的微调,也就是前面说的 Full finetune,下面这图很好理解,正常微调就是通过新增的训练集,重新调整这模型里面网络的参数,把这个参数更新到原有网络里,变成一个新的模型使用。

5

这里除了前面说的要求的训练数据量大、容易过拟合/欠拟合的问题外,还有个大的问题,就是计算量大、资源要求高。

大模型都是由多层神经网络叠加组成,Transformer 和 UNet 都是,使用这些模型时,是对这些模型正向推理,这个过程需要的资源不高,只需要把模型参数全部加载进内存,一层层正向计算就行。

6

但训练这些神经网络要求就比正向推理高很多,整个训练中,每一步训练的过程包括:

  1. 前向传播:训练数据(样本)输入当前网络,生成预测结果(跟使用模型一致)
  2. 损失函数计算:把预测结果跟样本对应的预期输出对比,评估差异
  3. 反向传播:把这个差异(损失函数的梯度),反向从输出层到输入层传播,计算每一层每个参数对这个差异的贡献,记下相应数据。
  4. 更新参数:对反向传播获得的数据,更新网络中每个参数值

这里内存中就需要同时存在好几个数据:1.原网络参数 2.前向传播过程中计算出来的数据,反向传播计算时需要用到 3.反向传播过程中每个参数的差异贡献(梯度值),更新参数时要用到。

所以假如整个神经网络有 n 个参数,每一步训练就要在显存存储 3n 个数据,进行 3n 次计算(正向推理、反向传播计算,更新每个参数的值)。还有一些训练优化的方法,数据的存储和计算量会更高。Stable diffusion XL 的参数量是35亿,llama 3最高参数量达到4000亿,每一步的计算量感人。

参数冻结

再来看看 LoRA 怎么解这个问题。

首先,LoRA训练过程中,会把原网络参数冻结(下图的W),不会去修改原网络参数,只让原网络参与正向推理预测结果的过程,所有对参数的调节都独立出来在 △W 上,这个△W最终也不会更新在网络上,只会在使用这个模型时外挂式地加上它,跟原模型一起叠加共同进行推理。

到这里,其实它只是换种方式,专门把训练变化的部分抽出来,其他都没变,△W 跟 W 的参数个数一样,该存储和计算的量一样。

7

低秩分解

下一步才是主要的,接下来需要一点点基础线性代数矩阵的知识。

前面这个分离出来的部分△W,变成下图这样,不是用跟原网络一样的参数量去表示,而是通过一个数学的方法 低秩分解 去表示,把原模型参数 W 和 △W 看成一个矩阵数据,那 △W 这个矩阵可以用两个小矩阵Wa 和 Wb 相乘去表示,这过程就叫低秩分解。

8

矩阵中秩(Rank)的概念简单说就是矩阵中行列较小的那个值,比如 100 x 5 的矩阵,秩就是5,把100 x 100 的矩阵,分解成 100 x 5 和 5 x 100 的矩阵相乘(相乘后是100×100的矩阵),就是低秩分解。图中的 r (rank)就是这个秩。

可以看到这个秩越小,数据量越小,比如分解成 100×10 和 10×100 两个矩阵,这俩矩阵数据量是2000,如果分解成 100 x 5 和 5 x 100 ,数据量是1000,相对于原矩阵 100×100 数据量 10000,要存储的数据量下降10倍。

所以 LoRA 训练出来的不是△W,而是分解后的两个低秩矩阵 Wa 和 Wb,这就是 Low-Rank Adaptation 低秩适应的意思。设定的秩的值越低,所要存储的数据量越低, 所以 LoRA 模型会根据训练时设置的秩值,比原模型大小低一两个数量级。同时训练过程中,因为这俩低秩矩阵跟原矩阵不是一个数量级,所需要的计算量和显存也相应减少了,在普通 PC 也能跑起来。

看起来很神奇,把一个完整的数据做这样的分解,数据的信息量肯定减少了,为什么用这种方式做微调,效果还能好?LoRA 论文中表示,在预训练的大模型上微调时,如果是处理一个细分的小任务,参数的更新主要在低维子空间中,很多高维子空间的参数在微调前后根本就没动,越简单的细分任务,对应要更新的参数维度就越低,可以简单理解为 LoRA 的低秩分解能对应到大模型低维子空间的参数更新中,更多扩展阅读参考 1 2 3

模型微调和 LoRA 原理就介绍到这里,我们继续回到黏土风的整个 workflow。

图生图

黏土风格这种模型的应用场景是给用户当图片特效使用,也就是需要图生图的方式,前面只做了文生图,来看看图生图的 workflow,以及生成的效果:

9

只需要在前面的 workflow 基础上,把生成空白噪声图 Empty Latent image 节点,改成 Load image + VAE 编码节点即可。

这里的原理很好理解,文生图是用一张全是随机噪点的图作为输入,沿着 Prompt 逐步降噪生成图片。我们把这个输入换成一张真实的图片,再在上面加上一定量的随机噪点,代替完全随机噪点图进行采样迭代逐步生成图片。把这个加了噪点图片看成是图片生成降噪过程中的某一步状态的话,相当于之前图片是从 0 开始生成,现在是从某一个中间过程点开始去生成,整个流程是一样的。

这里要加多少噪点,在 KSample 这个采样器节点的 denoise 里可以设置,来看看同样的文本 prompt 和图片输入的情况下,设置不同值的效果

10

denoise 为 0 时,没有加噪点,也就没有去噪的过程,输出是原图,denoise 为 0.4 时,可以猜想到,这时加的噪点程度不影响图的主结构,猫戴帽子的细节也还有,那降噪生成后这些细节还会保留,随着噪点越来越多,原图的细节越来越少,到 denoise 为1.0时,也就是加100%噪点时,跟输入一张随机噪声图是一样的,跟原图就没什么关系了,只跟输入的 Prompt 文字有关系。

看起来设置 0.4 到 0.6 之间的降噪,效果还可以,即保留了原图整图的大致内容,也能适当加上黏土的风格。用其他图片尝试,也还行:

11

图片 Tag 生成

但这里不同的图片的 Prompt 得自己手动改,才能生成类似的图,我们正常使用这类风格化滤镜是不需要自己输入 prompt 的。我们可以加个图片 Prompt 生成器,让它描述输入的图片,再作为图片生成的过程。

我们添加 WD14 Tagger 这个插件节点,它可以从图片中反推出适用于 SD 提示词的标签(booru风格标签),我们把它解析出来的 tag,跟 LoRA 模型的触发词 claymation style page 一起组合,作为 Prompt 输入,这样就实现了只输入图片,不用修改 Prompt 就能产出对应的图了。

这里注意得用 (claymation style page:2) 这种 Prompt 权重表达方式,把这几个 LoRA 模型的触发词调高权重,不然会淹没在图片输出的众多Tag上,没法起到作用。

12

最终的 workflow文件(下载这张图片可在ComfyUI上导入):

workflow到这里,我们实现了一个最简单的黏土风格图生图流程,也认识了过程中涉及到的技术原理。目前这只是个最简单的 demo,在某些图片下效果还可以,但要真正达到可用还有很多问题,后续会随着一些问题继续探索 SD 里的相关技术。

 

参考资料

LoRA模型微调原理:https://www.bilibili.com/video/BV1Tu4y1R7H5

利用LoRA对LLM进行参数高效的微调:https://zhuanlan.zhihu.com/p/632159261

神经网络训练:https://blog.csdn.net/brytlevson/article/details/131660289

Stable Diffusion原理可视化:https://www.youtube.com/watch?v=ezgKJhi0Czc

Stable Diffusion 图片生成原理简述

最近关注和学习 AI 比较多,包括 AIGC 和 LLM 大模型,现在 AI 的原理介绍和入门教程已经非常多了,但还是想自己写一下,主要是遵从费曼学习法,分享是最好的学习,帮助自己整理思路。

本文介绍这一轮图片生成热潮的集大成者 Stable Diffusion 涉及的一些图片生成基本原理,这里全篇不会有数学公式,因为大部分公式我也不懂,但应该不会太影响理解基本原理和思路,有理解错误的地方欢迎指正。

扩散模型

在看图片生成这个逆天能力的时候,很好奇它是怎么做到的。要完全理解这里面的算法细节门槛挺高,但要了解基础原理概念还是简单的。

目前市面上文字生成图片基本上都基于 Diffusion 扩散模型,Stable Diffusion 自然也是,它最基本的原理是:根据文字指示,把一张随机生成的全是噪点的图片,一步步去掉噪点生成跟文字描述匹配的图片。

具体是怎样做到的?这里可以分步看两个问题:

  1. 怎么从一张随机噪点的图生成一张正常的图
  2. 怎么控制这个生成的图跟输入的 prompt 文字关联上

先看第一个问题,从随机噪点图生成一张正常图片,通过训练和组合 UNet 模型可以做到。

单步训练-生成

UNet 是个深度学习网络模型,模型细节不说,个人可以非常粗略地理解为,这个 UNet 模型里面的参数,记录了训练的图片的内容,但它不是精确存储,而是有一些映射和数学运算,做到可以识别提取图片特征,模糊地记忆图片的关键信息,混合存储。

这个模型训练出来后,如果你是用一张图片玩命训练它,那它最终恢复出来的就是这张图片(maybe?)。如果你用1万张图片训练这个模型,那它恢复出来的会是这一万张图片内容随机组合的一张图片。

1

下面稍微再展开看下训练过程和生成过程:

  1. 选一张正常图片A,随机生成一个噪声X,给A加上这个噪声。A+X=A1。
  2. 把A1输入到模型,我们希望做到的是,模型能在只有输入A1,没有输入噪声X的情况下,能自己推理知道噪声X是什么,如果能做到,那就可以通过A1 – X = A,把更清晰的图给反解出来了,也就是输入一张有噪点的图,输出一张去了噪点的图。
  3. 怎样做到?在训练过程中,我们是有A1和X这两个数据的,模型自己生成另一个噪声Y,跟噪声X对比,然后通过不断调节自己的参数,让自己生成的这个噪声接近X就行了。
  4. 这样模型记录了相关参数,下次拿A1过来,它就能推算出近似于上面加的噪声X,然后做A1 – X = A去噪,得到更清晰的图片A。

2

总的来说,我们训练的这个模型,它的能力就是,给一个图片,它能预测出来这张图片上是加了多少噪声,这样就可以让这张图减掉这些噪声,得到更清晰一点的图。

多步扩散

上面是简化的一步,只是把图片从稍微加了点噪声中还原出来,并不是直接从一个没有信息量充满噪声的图片中直接还原蹦出一张图来。要实现从纯噪声图生成一张图片,需要重复很多步上述步骤,所以它还有一个Time step的参数参与在训练过程中。

Time step 就是噪声强度,很好理解,表示的是加多少次噪声。回到上面的训练过程继续:

  1. 上面的训练,A 是完全没加过噪声的图片,A1是加了一次噪声X的图片,time step 就是1表示加了一次噪声,A1 和 t=1 输入到模型,训练出能推算 A1->A的能力
  2. 下一步针对A1再加一个噪声X2,A1+X2=A2,time step就是2,把加了两次噪声的图片A2和t=2输入到模型,训练出能推算A2->A1的能力
  3. 循环N次,加上多张图片重复这个步骤,模型的参数就学会了这里每一个步骤加的噪声数据。
    1. 实际训练过程中,t会是一个随机数,经过海量图片多轮训练,会覆盖所有t的值。下图epoch[回合],表示一轮一轮的训练,每次用不同的图片,不同的time step做训练

    3

  4. 最后就能从这个链路里把一个满是噪声(比如加了1000次噪声)的A1000逐渐去噪生成出清晰的图片A。A1000->A999 … ->A2->A1->A:
    4

训练和生成过程简化后就是这样。如果训练集是一张图片反复训练,我猜测输入一张加满噪声的图,最终会把这张图片还原出来。如果训练集是海量的图片,那这里还原出来的图片,如果不加其他控制,就会是这些海量图片随机的组合结果。

控制生成

接下来到下一个问题,就是怎样控制它去噪过程,让它生成的图片跟我们输入的文案描述匹配。

概念上很简单,就是在训练和生成过程中,把图片的描述文字也加进去。我们事先准备提供给模型训练的数据,除了图片本身,还需要包含对这张图片的文本描述,这样模型才能学到文本和图片内容的关系。

按刚才训练过程的示意图,实际上模型是三个输入,加了噪声的图片A1、图片对应的文本描述text,噪声强度time step,在这三者的作用下共同推理出对应的噪声。

5

有text的训练输入后,后续在通过噪声生成图片的过程中,根据用户输入的prompt文本,就可以引导去噪的走向。

如果比较具象地想象这个过程,比如训练集有20张图片,5张的图片描述是girl,其他15张没有girl。训练过程中,girl这个词就跟这5张图片关联记录在网络参数里,在训练后使用这个模型时,输入的文本有girl,那去噪的过程的每一步就大概率会定位到这5张图片训练时的数据,会有更大概率去噪的过程走向这5张图片,而不是之前的随机走向。

到这里,扩散模型最基本的原理就差不多了。

重要概念

上述整个过程比较简化,过程中有几个重要的问题和概念还没提到,这里逐个说明。

Latent Space & VAE

上述的扩散模型的训练和使用,有个很明显要解决的问题,就是图片太大了,如果图片用像素数据表示,现在 iPhone 拍的一张照片有最小有 500w 像素,即使做常见的图片压缩(JPG/PNG),也有几百K的数据大小,如果用原图按上面的流程跑下来,计算量巨大,显然我们要针对图片做降维(或者理解为压缩),把一张图片的数据量降低,再进行后续的训练和使用。所以需要一个降维模型做这个事,这个模型需要满足:

  1. 有 encode 和 decode,需要能从 encode 和处理后的低维数据 decode 成高清图片。
  2. 要压缩得尽量小,方便低成本做上述海量的计算。
  3. 信息要能保留得足够多。
  4. 信息要有语义,不然训练过程中不同图片的信息无法交叉融合。

VAE (Variational Autoencoders 变分自编码器)能做到这些,VAE 提供了 encoder 和 decoder,一张图片经过 VAE encode,可以压缩成仅有 64x64x4 的矩阵,这里经过 encode 后的数据空间,就称为隐空间(Latent Space),在这个空间里进行上述扩散模型的训练和生成流程,成本就非常低,这也是目前Stable Diffusion能跑在我们普通电脑上的原因。最后在隐空间里生成的图片数据,经过 VAE decoder,就能转换成高清图。

那 VAE 为什么能做到这样?一张图片转成 64x64x4 这么小的数据量,为什么能保存图片的信息?通俗理解是 VAE 把图片内容转成了语义概率表示,相当于变成了这张图片的描述,比如描述这张图片有猫的概率、有猫爪子的概率,猫在左边的概率,绿色的概率,类似这样,更深入就不了解了,这篇文章 有讲解到一些,也只了解到这里了。

CLIP

VAE 解决图片编码问题,再来看看文本的编码。在控制生成里,文本实际上是怎样参与到模型训练和生成的过程?如果文本只是随便编码进入模型,模型可能只认得一些特定字符,不认识语义,也就在后续的图片生成中没法比较好地通过自由的prompt文案控制。

SD 使用 OpenAI 训练的 CLIP 模型,把文本转为对应的向量,为什么用它,因为 CLIP 模型本身是一个文本到图片的映射模型,它对文本转出来的向量,更贴近图片的特征空间。

稍微展开说下原理:

  1. 有N张图片、以及它们对应的 N 个对图片的描述文本
  2. 对图像进行编码,得到I,下图中,I1/I2/…/IN 表示从第 1 到 N 张图片的编码表示。
  3. 对文本片段进行编码,得到T,下图中,T1/T2..TN 表示从第 1 到 N 张图片对应的文本描述。
  4. 模型的任务是训练 TextEncoder 和 ImageEncoder的 参数:
    1. 让图中蓝色部分Ti和Ii相似度变高,它们原本就是一一对应的文本-图片,属于正样本。(数学上是计算余弦相似度,越大表示相似度越高,最大化这个值)
    2. 让白色部分相似度最低,它们的文本和图片是没有关系的,属于负样本。(数学上是余弦相似度值最小化)6

这样训练后,最终使用这个模型时,TextEncoder出来的向量表示,就跟图片内容有很强的关系。比如下图第四行,猫的文字描述通过 TextEncode 出来的值,跟猫的图片的ImageEncode出来的值,相似度更高,跟其图片的encode相似度就低。

7

PS. 更细节的 CLIP 怎么跟 SD 生成过程结合,还没弄得很清楚,实际上SD 没有用 CLIP 里的 Image encoder,扩展模型训练过程中是用别的 Image Encoder,那就并没有用到文本和实际图片的对应映射关系,但可能CLIP出来的文本编码,语义和表现形式上已经是图片的模式,比如文字 cat,它能跟图片空间里猫所表示的形态(形状/位置)、视觉(眼睛/颜色/形状)、语义(宠物/动物)能比较接近地对应上,也能存储到相关信息,所以跟其他图片编码结合,也能起到很大作用?

采样器

编码和数据量的问题解决了,还有个问题没提到,就是上面流程里的步数太长了,最开始提出的扩散模型训练方案 DDPM(Denoising Diffusion Probabilistic Models 去噪扩散概率模型),正常需要 1000 步降噪过程(称为采样),才能生成一张不错的图片,即使上述隐空间已经降低了计算量,但生成一张图要1000步,还是达不到大规模可用。

为什么 DDPM 一定要这么多次采样?这篇文章说得比较清楚,不能直接减小步数(每次噪声加得很少,避免一步就破坏掉原图),不能跳步降噪(每一步状态都依赖前一步,号称马尔科夫性质)。

随后很快有人提出 DDIM(Denoising Diffusion Implicit Models 去噪扩散隐式模型) 方案,训练时还是 DDPM 一步步来,但生成时是可以做到跳步,同时还能让在步数变少的情况下保持生成的稳定。DDIM不是从头开始逐步去噪,而是选择几个关键的时间点,只在这些时间点上进行去噪操作,并且中间的步骤,比如从降噪100次的图片,下一步直接生成降噪90次的图片,跳10步,生成速度就快了10倍。为什么它能做到跳步,具体原因都是数学公式,就不展开了(还没全看懂),可以看回这篇文章

Stable Diffusion 的 WebUI 上有很多采样器,Eular a,Karras,DPM 等,在去噪过程中通过不同的方法,有不同的多样化程度、图像质量、速度、收敛性的区别。

Stable Diffusion

最后总结说下 Stable Diffusion。上面整个过程和概念,是一个个解决问题的方法,把它们组合起来,逐渐建立起基于扩散模型生成图片的方法大厦,谁都可以用这些公开的理论方法建一套自己的生图模型。

Stable Diffusion 就在这些基础上做一些改进,建立一套稳定的框架、训练出基础模型,开源让所有人可以用,整个 SD 就是多种能力的组合,这些能力可以分别不断升级替换,模型本身还有很多方式去做更强的控制干预(controlNet / LORA等),使得它可定制性可玩性很强,生态越来越繁荣。

最后让我们用一个图串起整个流程和讲解到的概念。

  1. Part1 是用 CLIP 模型对文本做编码。
  2. Part2 在模型训练过程中,图片经过 AutoencoderKL(VAE编码器的实现)生成隐空间下图片的表示,随机生成一个 noise 噪声,加到这个图片里,同时把通过 CLIP 模型编码的图片对应描述文本加入进来,一起进入 UNet 模型,训练预测加了多少 noise 噪声的能力。
  3. Part3 在模型推理过程中,输入一个完全随机噪点,在隐空间里通过不同的采样器,结合prompt 文本输入(图上没表示出来,文本数据会参与到降噪过程),在 UNet 模型里迭代多步做降噪预测,生成隐空间里的图片,再通过 VAE AutoencoderKL 解码出图片。

8

了解整个基础流程和概念后,现在看 Stable Diffusion 论文中的这张架构图,应该也大致能理解是什么意思了。

9

参考资料

个性化UI在金融场景的探索和应用

背景

随着各业务流量红利逐渐见顶,如何在有限的流量下,提升流量给用户带来的体验,对流量进行精细化运营,进而提升转化效率,成为各业务的重要课题。

蚂蚁数字金融线包含了 保险/理财/信用/借贷 等业务,经过多年建设,形成了多样的金融资产/场景服务/权益/内容等多维度的产品去服务用户,而这些产品分发给用户时,决定曝转率的大体上以产品推荐是否精准,以及产品以什么样的UI表述方式进行展示决定。在产品推荐上,算法已经逐步覆盖,给业务带来很大的效果提升,在产品推荐算法优化到一定程度后,提升的投入产出比已经有限,我们开始在UI展示上探索,是否个性化智能化的展示能为业务带来提升。

探索

UI1

淘宝比较早地做了智能UI的尝试,在推荐出商品后,对于商品的UI展现方式进行千人千面个性化,商品列表有两列/三列/横排的布局方式,商品封面有 场景图/白底图/视频 等类型,商品本身有 标题/描述/评价/销量/属性/标签/活动/价格/快递/地区 等一系列字段标签,在列表上无法全部容纳,不同的人对不同的 布局密度/封面/字段标签 会有不一样的关注度,这里就有了智能化的空间,对这些属性进行自由组合千人千面推荐,取得不错的提升成果。

在数字金融线的业务,是否也可以这样做?数字金融线各业务的“货”并不是统一标准化的商品,主要由 金融资产/服务/权益/内容 构成。例如财富业务,金融资产(基金/定期等)在购买门槛高的情况下,会通过各种包装降低门槛,包括场景化包装成服务(工资理财/笔笔攒等),配合营销活动包装权益(体验金/财运金/黄金票等),包装资讯/视频/直播等形式的内容。同时这些包装后的“货”在不同场景有不同的UI展现形式,并不是标准化的,这些“货”也没有多种字段可以直接进行自由组合搭配,如何能做个性化千人千面?

问题

我们可以针对一个个场景设计多个模板样式去匹配不同的人,这也是之前在一些业务上进行过一些尝试的,但有几个问题:

  1. 无设计标准:设计什么不同的UI样式才可能有效,没有标准和指导,靠设计师个人经验。
  2. 样式生产效能:UI样式需要一定量级进行分人群匹配,设计工作量大。
  3. 算法匹配信息不足:算法难以识别不同UI样式间的差别,以 人<->样式ID 的方式匹配效果有限。
  4. 无沉淀不通用:不同的场景割裂,设计经验/算法经验/工程链路无法复用,无法沉淀用户偏好,各场景实现成本大。

针对这些问题,我们探索了一套相对通用的个性化UI解决方案。

方案

语义标签体系

UI2

首先是根据过往数金各场景的设计经验,由设计团队主导抽象出一套UI语义标签体系,去描述一个UI样式里可能吸引用户点击的关键点。

这个标签体系分表现层和内容层两个层面:

  1. 表现层去描述UI长什么样,比如它的风格是实物还是扁平的,色彩是鲜艳的还是偏透明的,字体相对大小。
  2. 内容层表示这个UI展现有什么内在含义,比如它是突出权益的(红包/抽奖等),代表从众心理的(xx人正在购买),代表认知的(黄金图/人物图/降维描述/品牌权威描述)等等。

我们认为不同人对这里列出来的标签是会有不同偏好的,例如年轻持仓少的,可能用扁平图/从众/降维描述相对能打中,老年人可能对权益/实物图形/大按钮比较敏感。

这套标签期望能产生几个作用:

  1. 指导设计往什么方向进行差异化设计。
  2. 语义化描述UI,作为特征让算法更好地认识UI的外在和内在含义,多场景通用。
  3. 后期可以针对线上数据进行归因分析,语义化看出不同用户的UI偏好差异。

工程算法

标签体系的思路把UI的维度拆细了,为不同人原子化设计不同的元素,我们沿着这个思路打造了相应的工程和算法方案:

UI3

1.多样式生成

我们将UI原子化打散-自由组合。一个产品可以以各种方式包装后呈现给用户,主要包括描述产品的创意物料(文案/图片/动画),再配合不同的样式和版式(字号/背景/布局排版)组成,这些元素可以进行自由组合,例如一个单图文模块,我们可以参考标签体系设计10个文案,10个图片,2个排版样式,排列组合就可以生成200个UI样式包,作为丰富的可以匹配不同用户的素材。

2.UI偏好模型

我们将组合生成的UI样式包结合UI标签与用户属性进行匹配关联,以CTR为目标训练模型。训练中取三种数据:

  1. 用户属性,包括年龄性别等基础属性,以及像持仓情况、投资次数这样的业务属性
  2. 每个UI样式包所打的UI标签数据
  3. 每个UI样式包在线上随机投放的曝光点击数据

经过特征工程处理,组成训练集去训练出DNN模型,在线上部署使用,输入是 用户属性 + (UI样式包对应的)UI标签 列表,输出是每个UI包的点击概率排序。

3.工程链路打通

从样式配置,到数据组装,到召回排序,到最后前端渲染,对接各个平台串联整个链路,在各场景实现千人千面UI推荐能力。

落地效果

当前个性化UI在数金多个场景落地,不同场景有不同的提升效果,按流量平均下来CTR(曝光点击率)提升20%左右,同时接入的场景CVR(曝光转化率)也同步提升。

其中流量最大的是支付宝首页财富生活模块,组合了几百种样式进行匹配推荐,CTR的提升在首页大流量的加持下为业务带来很大价值。

CTR提升最大的是理财Tab蓄水版直通车里的黄金和黄金票卡片,一千多 种样式,CTR 和CVR可以提升50%左右。在这个版本里卡片在屏幕占比较大,不同元素的展示对用户点击的影响是非常大的,这也是最适合接入个性化UI的场景。

UI7

分析

有效性分析

为什么这套方案会有效提升CTR,尝试回答几个常见问题:

1.疲劳度

是不是用户对一成不变的样式有疲劳度,只要有新的样式点击率就会提高?我们在每个场景里上线个性化UI,会分几个桶进行AB效果对比,其中随机桶是对组合生成的几百个样式进行随机投放,比如这是财富生活的实验数据:UI5-2

可以看到随机投放组合出来的样式效果并不好,并没有因为样式多了就能提升,反而因为组合出来的某些质量相对差的样式影响CTR下降,这在多个场景里都有类似的的情况,可以看到CTR的提升并不是疲劳度新鲜UI带来的。

2.UI外的因素

是否是UI以外的因素影响CTR,例如不同的产品/权益内容?每个展位接入的AB实验里,产品和权益是固定不变的,个性化UI用不同的表述方式(图片/文案/样式/排版)展示同样的产品和权益在同一时间随机人群进行AB对比,没有其他因素干扰。

3.新样式抢眼

是否有几个很吸引眼球的UI拉高了CTR,没什么人群偏好差别?很有可能因为原来的样式不抢眼,我们组合出来的某些样式加红加粗天然吸引用户注意力,实际上我们实践中因为设计规范的约束不会做太抢注意力的设计,同时我们也分析了,不同样式在随机投放和算法投放的差别,例如下图玩转理财场景里,一些样式算法推荐的人群比这个样式随机投放它的点击率提升了几十个百分点,说明不同人对这些样式有明显偏好,算法把这些样式分配给真正偏好的人,带来了点击率提升的收益。UI6

归因分析

我们看到了用户对不同UI表述展示方式有不同偏好,期望能沉淀出具体人群跟UI的偏好关系,给设计师和业务方在新场景设计中提供参考帮助。得益于UI语义标签体系,我们是可以沉淀出UI偏好和画像的。

我们探索尝试了多种方法去做归因分析,包括:

  1. 数据统计,直接统计线上不同人群在不同样式的点击率,在图表上找出点击率的差异,沉淀偏好结论。
  2. EBM可解释性模型,解释DNN模型中各特征对最终效果的贡献度,以及交叉特征观察人群特征和UI标签的关联关系。
  3. 网格分析,在表格上将多个人群特征-UI标签进行组合,列出所有组合的点击率,从中找出点击率变化规律和差异,找出偏好差异。
  4. 标准化偏好,CTR(UI特征,人群特征,对应产品)/CTR(人群特征,对应产品),衡量单个UI元素在指定人群特征里对点击率的影响程度,再进行对比找出偏好差异。
  5. 单样本方法,建模的方式分析人群特征和UI特征的贡献度。

初步可以沉淀出一些偏好洞察,例如:

  1. 理财货架场景上,收入较低者偏好产品名称,普通/高收入者偏好产品推荐语。
  2. 按钮颜色上,总体红色比蓝色更吸引,但越年轻的用户,越偏好支付宝蓝。
  3. 白领/初级投资者偏好金融元素,蓝领/高级投资者偏好品牌元素。
  4. 有信用卡者对走势图不敏感,无信用卡者更偏好走势图。
  5. 中年(40-50岁)年龄段特别偏好紧迫心理类型的标题,尤其偏好行业风口类。
  6. 理财货架中,整体上宫格样式效果远低于列表样式(宫格面积相对小),但年长者对宫格偏好不低。

归因洞察还未形成稳定分析体系,处于探索期,最终是期望探索到一种或多种分析有效通用的方法,沉淀为平台自动分析能力,能在平台沉淀 业务维度、场景维度、人群维度 的UI偏好画像。

产品化

在方案落地出效果后,我们看到这套能力是具备一定通用性,各业务可以接入获益的,于是我们致力于将它的能力产品化,沉淀多样式生成配置、通用高效的UI偏好模型等能力,让各场景可以快速接入。主要围绕接入效率和接入效果进行优化。

效率优化

个性化UI的接入成本比较高,需要设计素材,人工打标,前端/业务后台/中台对接工程链路,训练UI偏好模型,跟进实验,手工清洗数据分析。涉及流程长,对接的角色多,投入较大。在平台级大流量展位上接入这套能力,能产出很大的业务效果,这样的投入还能接受,但要覆盖更多的长尾场景,这样的接入效率性价比相对低,难以被接受,我们从几个方面建设去提升各场景的接入效率:

  1. 一站式配置平台(万花筒平台),完善样式配置、组合预览、打标、筛选审核、业务干预、样式三板斧发布、数据分析能力。
  2. 通用链路,提供从样式包召回、推荐、数据组装的通用工程链路,通过 BFF SDK 输出,普通场景只需要前端BFF接入SDK即可完成接入,减少与业务后台和中台的链路对接。
  3. 通用模型,用户特征和UI标签特征在各个场景里是通用的,也就可以训练出一个通用模型,映射用户特征和UI标签特征的偏好关系进行推荐,虽然效果相对专门训练的模型会打折扣,但适合长尾场景快速接入。
  4. 专用链路,数金多数场景与各业务平台和中台结合较紧密,我们把个性化UI能力嵌入这些平台,可以提供更好的结合业务和特定场景的一站式使用体验,提升效率,例如对接UCDP/梵高/毕加索建设banner链路、对接洛可建设微贷专有链路等。

以上几点是短期内会建设完成的能力,此外有两个正在探索中的提升接入效率的课题:

  1. 素材生成,当前接入的大多数场景需要设计师和运营一起产出多个图片和文案素材,这是接入流程中最大的成本,我们在探索素材自动生成的方案,沉淀符合设计规范的原子图片素材库,提供对不同场景尺寸和组合的处理适配能力,让图片素材可以在多个场景里复用。文案方面在中台智能文案基础上加入更多的业务语料,自动生产的文案更符合业务调性和用户偏好。
  2. 自动化打标,当前需要手工对每个元素进行打标,当标签趋于稳定,并且打标量达到一定程度后,可以通过算法理解图片和文本,归类到指定的标签进行自动化打标,减少人工投入。

效果优化

如何对样式推荐的效果进行进一步提升,也是我们持续在探索的,算法效果的提升,很大一部分取决于特征数据的丰富和准确,围绕数据会在这几方面进行尝试:

  1. 端智能结合,云端不直接推荐UI样式结果,而是推荐几个样式候选集,端上再进行实时重排。为什么端上重排可能会有效,端上可以根据用户的实时行为,判断用户看了还是没看(停留了、快速划过),再结合它的行为路径(点了有某个UI标签的展位、快速离开、去了很多二级页等),去综合判断是否因为疲劳度、兴趣变化等去切换其他样式,更好打中用户。
  2. 自动特征工程,训练专用的UI推荐模型时,会根据对场景的理解进行特征筛选和交叉组合处理,这里选择什么特征交叉由个人经验决定,我们借助AI中台的能力,尝试autoCross自动筛选有效的交叉特征,理论上能得到比手工交叉筛选更好的效果,同时减少特征工程的人员投入。
  3. 多模态建模,抽取图片素材/文本素材的高维特征,融合后作为UI标签的补充加入训练,丰富模型对UI的理解。
  4. UI标签演进,我们一开始构建的表现层/内容层的标签体系,几个场景使用下来有不错的效果,但还没能做到很通用地描述UI,正在尝试以描述因子/驱动因子的方式组织标签,我们会尝试不断迭代标签的描述粒度和通用性。

未来

个性化UI在金融场景我们实现了从0到1的突破,后续从1到100我们还需要做很多工作,除了上面说的产品化效率和效果优化,还有三个方向持续探索:

  1. 通用性,当前的标签体系、模型、工程链路都是围绕数金业务进行建设,实际上这套能力在多产品服务分发的场景都能适用,比较适合支付宝的业务形态,后续可提升通用性,在支付宝分发海量产品服务的展位上进行尝试。
  2. UI偏好画像,接入的场景足够多,标签体系足够完善,归因的方法探索出通用有效的方案后,可以沉淀用户的UI偏好画像,作为体验端的数据资产,为后续算法模型、业务设计提供价值。
  3. 能力扩展,我们围绕单展位模块的千人千面建设个性化UI能力,在这以外,跟智能展示相关的,还会有其他空间可以挖掘,例如全屏的注意力管理,用智能化的手段,避免全屏范围内 动画/红点/抢眼颜色/弹屏 不断抢占用户吸引力,以合适的方式分配用户注意力,提供更好的体验和效果,是一个可以探索的方向。

总结

我们针对数金的业务特性,在对产品的个性化UI展示上提出了 元素打散组合-语义打标-算法推荐-归因分析 的方案,落地取得了不错的效果,当前在将能力进行产品化沉淀的过程中,逐渐完善核心的 多样式生产、精准UI偏好模型、UI偏好画像 能力,让各长尾场景快速接入取得规模化的业务结果。过程中会碰到许多困难,但核心的“不同人有不同UI偏好”已被验证,我们会沿着这个方向,联合设计、产品运营、算法、质量一起持续深耕和拓展个性化UI能力。

用户端智能的应用实践

去年团队在用户端上进行了一些简单的智能应用探索实践,这里记录梳理下。

现在很多“智能”,是普通推荐算法借深度学习的风包装的,核心也就是决策树/随机森林/SVM这些90年代已经提出的算法,我们这里的实践也是这样。在用户端上,智能应用最广泛的目前两个点:个性化和多媒体识别,我们主要实践是在个性化上,从原来所有用户都用同一套或几套规则,换成根据推荐算法给每个用户制定符合他个性化特征的不同规则。

实际上简单的个性化推荐也可以认为是规则,只是这个规则很复杂,里面的if/else/权重/概率计算不是人工写的,而是算法算出来的,大多采用监督学习的方式,这种需求大体实现步骤是:

  1. 建模,把问题转化为三个点:输入/输出/算法。也就是挑选特征集X,确定输出目标值标签Y,挑选算法尝试找出Y=f(X)。
  2. 取数,选择一坨线上数据,清洗出需要的特征和对应的目标值。
  3. 训练,不断调整算法/参数/数据,找出用户特征->目标值的一个靠谱转化方式,输出一个模型。
  4. 应用,部署线上应用,实时预测,分析效果输入特征。

按这个套路,寻找了一些应用场景,做了一些尝试。

金额推荐

金额推荐概念很简单,我们作为理财业务,有很多场景是需要用户填入金额的,例如工资理财(每月自动把钱存到理财),小星愿(许愿存入钱),基金定投,各种理财产品的购买等。目前这些填金额的地方不是空着,就是有一个固定的默认值,金额推荐想根据每个用户的情况给推荐不同的金额,可以预填进输入框或出提示供用户选择,提升体验,甚至做出一些引导提升客单价。接下来套在上述步骤里来看下大致实施过程。

  1. 建模:
    • 输入特征基本上先拍脑袋尽量多的选取可能有关的特征,比如年龄,城市,过去交易金额等,后期训练迭代过程中会根据计算出的每个特征的重要性再进行几轮筛选。
    • 输出标签目标值是金额,但金额推荐这个需求不需要把每个用户的金额预测精确到个位数,从用户数据观察多数金额集中在有限的一些整数上(比如100/500/1000/2000等),所以这里只需要把金额分成一些档位,目标值是金额档位。
    • 算法,目标值是金额档位,是一个分类问题,分类算法中选用了随机森林这种集成算法,简单应用广,过程中也试过其他算法,效果差不多。
  2. 取数:
    • 进行一些数据预处理,例如把非数值类型的特征转为数值,合并特征和标签等。
    • 把最近一次交易金额转为对应金额档次分类作为目标值标签。
  3. 训练:使用第二步的部分数据进行训练,拟合出一个模型,再使用另一部分数据评估模型的预测效果。过程中通过各种调参/分析/数据处理,优化模型评估效果,例如:
    • 提升数据质量,筛选出过去购买金额有一定规律的人群数据,比如多次购买的方差不超过某个数值。
    • 参考随机森林特征重要性中的 entrophy gini 指数,去除重要性较低的特征。
    • 减少标签金额档次的个数,只保留多数用户会命中的几个档次。
  4. 应用:部署模型,不同的交易产品有不同的用法,可以把预测值直接预填进输入框,也可以在旁边出输入提示显示推荐的金额。然后就是一系列ABTest、性能优化、监控、数据效果分析的工作。

不同交易产品(工资理财/小星愿)对特征和标签的选取有一些小的不同,但大致处理流程一致。实践中工资理财把固定的1000元引导改成推荐的金额,订单平均金额提升60%32%用户直接使用了推荐的金额。小星愿把金额推荐作为提示供用户选择,相比直接放一个该用户历史交易金额作为提示,点击率高十几倍,用户对推荐金额的接受程度较高。

智能push

智能push想做的是在合适的时间给人推push,提高点击率。基于一个假想:每个用户都有不同时间段的活跃时间点,如果push不是统一固定时间下发,而是选择每个用户对应活跃的时间点下发,可以提高点击率。这里的push是促活类的,例如资讯的push。这里的问题可以转化为:怎样根据一个用户已有信息预测出他的活跃时间点。

  1. 建模
    • 输入,选取可能跟用户作息有关的年龄/收入/城市等基础信息,再加上用户过去7天的push点击数据作为特征值。
    • 输出,用户的活跃时间点,精确到小时,用户一天可能在多个时间点活跃,需要输出多个时间点。因为需求原因,push时间点限制在8-21点。
    • 算法,8-21点共14个小时,14小时->14个分类,多个时间点活跃->属于多个分类,是个多标签分类问题,我们用多个二分类的方式实现,同样用随机森林的算法,14个时间点每个点都单独做一次二分类,每个二分类的计算结果都是0-1之间的数,可以当成这个用户在这个时间点的活跃概率,最后合起来,得到每个时间点的概率。再根据指定阈值确定是否活跃时间点,例如:
      20190324164008
  2. 取数:
    • 对过去的push数据进行处理,根据8-21点每个时间是否有点击push,合成多条特征数据,取过去8天的数据,1-7天的数据作为特征,第8天的数据作为目标值标签。
    • 清洗用户基础特征,数据转换,拼接push特征。
  3. 训练:根据第二步的数据分14个二分类(14个时间点)训练,得到14个模型,每个时间点一个模型,最后合成一个。
  4. 应用:经过算法模型可以得到用户每个时间点的活跃概率,有多种使用策略,可以通过阈值得到几个活跃时间点,也可以直接选用概率最高的时间点,还可以在业务发送push时,往后选取概率最大的时间点发送,例如业务12点发送push,选12-21点之间概率最大的时间点,把push进队列延迟到该时间点发送。

同样是经过了多轮的数据分析、模型调整、特征优化、AB实验、Android分链路优化等,最终同一条push内容,使用智能push链路和普通链路直接推送相比,点击率稳定在提升25%左右。

本地处理

push的优化上当时我们想做更多,除了活跃时间段,实际上影响用户点击push的还有他当前手机的实时状态,例如是否在看视频/玩游戏/放在口袋里等状态,在这些情况下用户点击push概率一定很小,如果在收到push后显示前能实时检测到这些状态,就可以推迟到更合适的时间显示。为此我们在iOS上做了一些尝试:

  1. 需要在用户收到push后执行代码逻辑,再决定是否显示push,经调研 Notification Extension 没有取消展示push的能力,VoIP 需要特殊权限,只能用普通的静默push去做。
  2. 每条push都改成静默push,收到后唤起执行代码,经过各种判断后创建Local Notification,在指定时间展示push
  3. iOS 无法直接判断用户正在运行的程序和手机状态,只能通过一些侧面属性判断,包括横竖屏状态、耳机插入、内存情况、cpu情况、锁屏情况、网络状态这些信息,模糊猜测用户当前所处环境,给出一条运算公式计算用户当前是否适合展示push,公式可以动态下发,根据效果调整。
  4. 用户本地保存上面跑出来的每个时间点活跃概率数据,把push延迟到下一个较活跃的时间点显示。

Android 也实现了差不多的方案,不过iOSAndroid的方案都有缺陷,iOS方面用户手动kill掉的APP情况会收不到静默push,会影响push的到达率,Android方面没有可靠的延迟展示push手段,若延迟的时间点APP处于非活跃状态,会无法展示,同样也是会影响 push 到达率,这些缺陷导致最后本地处理的方案实现后没有很好地应用上。

其他

除了金额推荐、智能push,还做了一些其他智能的尝试,包括

  1. 智能异常检测:客户端的一些异常很多时候无法通过代码准确地检测到,例如图形引擎在一些安卓机器上的花屏,没有报错的白屏/黑屏,期望是有统一的手段检测到这些显示的异常。做法是在端上内置机器学习框架,云端训练模型,输入是截屏图片,输出是异常分类,我们挑了三种情况作为异常截屏样本:白屏/黑屏/花屏,做一个简单的图形分类模型,典型的深度学习cnn入门级项目,压缩模型下发到客户端运行,用户运行过程中根据一定的规则抽样进行截屏检测,有异常可能即上报。
  2. 股票OCR导入:自选股票从不同APP迁移时,需要逐个股票进行输入->搜索->添加自选,这里要做的是在应用A自选列表截屏,在应用B导入截屏,OCR识别出截屏上的股票代码。OCR是比较通用的识别能力,直接用现成模型,配合一些逻辑处理就能实现了。

还有像智能预加载/智能分页/交互预测/交易流失归因分析这些实践,但还没有做完整,就不描述了。

感想

  1. 当智能基础能力的基建(训练平台,模型部署,数据打标等)做好了,要把智能能力应用到业务上时,需要的更多是业务理解和数据分析能力,多数精力会耗费在清洗数据、模型调优上,这个能力跟工程能力有很大不同。
  2. 简单的“智能”需求,不需要理解具体算法实现也能做出来,只是理解了算法可能对优化有帮助。
  3. 机器学习算法繁多,与过往计算机通用经验复用度不高,要达到创造/改进算法的程度门槛太高。
  4. 用户端体验上的智能应用在多媒体、IoT、系统级优化上比较有空间,其他的并没有找到特别大的应用场景。
  5. 即使做了几个智能的应用,也没法说已经入门了这个领域,只是接触到一点皮毛,而且当时去补的各种知识点在一段时间没有接触使用后,也很容易就遗忘了。
❌