Deepfakes-探索指南-全-

Deepfakes 探索指南(全)

原文:zh.annas-archive.org/md5/4651b9e2858e507c09b2b4436581cd59

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

深度伪造周围的媒体关注往往集中在技术的弊端和危险上。甚至许多关于深度伪造能做什么的文章也未能解释为什么你可能会想这么做。事实是,深度伪造为那些知道如何使用它们的人带来了新的技术和能力。

除了替换面部之外,深度伪造为生成式 AI 的所有领域提供了见解,尤其是在传统方法不足的时候。加入我们,探索深度伪造是什么,它们可以用作什么,以及它们可能会如何改变。

深度伪造的道德使用宣言

在使用深度伪造时存在许多担忧。为此,我们必须建立一些共同点,以便我们可以沟通和讨论深度伪造技术:

  • 深度伪造不应用于创建不适当的内容。

  • 深度伪造不应用于未经同意或意图隐藏其使用的情况下更改面部。

  • 深度伪造不得用于任何非法、不道德或可疑的目的。

  • 深度伪造的存在是为了实验和发现 AI 技术,用于社会或政治评论、电影以及无数其他道德和合理的用途。

我们非常担忧 Faceswap 被用于不道德和不名誉的方式。然而,我们支持开发那些可以道德使用的工具和技术,并为任何想要亲手学习 AI 的人提供教育和经验。我们对任何将 Faceswap 用于任何不道德目的的人采取零容忍态度,并积极劝阻任何此类使用。

本书面向的对象

本书是为任何对学习深度伪造感兴趣的人而写的。从学者到研究人员,再到内容创作者和开发者,我们编写这本书是为了让每个人都有所收获。

早期章节将涵盖深度伪造的基本背景,它们是如何工作的,它们的伦理问题,以及如何使用你可以下载和使用的免费软件自己制作深度伪造。

中间章节将深入探讨深度伪造工作的具体方法,包括你可以运行并按步骤跟随的代码,这样我们就可以亲身体验深度伪造的主要过程:提取、训练和转换。

最后几章将探讨你可以从哪里开始。它们涵盖了如何在你的任务中使用深度伪造技术,以及这项技术可能未来的发展方向。

本书涵盖的内容

第一章深度伪造概述,回顾了深度伪造的过去和现在,并描述了它们是如何工作以及被使用的。

第二章审视深度伪造的伦理和危险,回顾了深度伪造的丑陋历史,并提供了创建道德深度伪造的指南。

第三章掌握数据,教你如何从你的数据中获得最大收益,无论你是自己制作还是必须找到它。

第四章, 深度伪造工作流程,提供了从安装到最终输出的使用 Faceswap 的逐步指南。

第五章, 提取人脸,在这里我们开始动手深入探索深度伪造的代码,通过学习如何检测、对齐和遮罩人脸来进行深度伪造。

第六章, 训练深度伪造模型,在这里我们继续探索代码,从零开始训练一个模型,包括定义模型的层、提供图像以及更新模型权重。

第七章, 将人脸交换回视频,在这里我们通过转换完成代码分析,这个过程将交换过的人脸放回原始视频中。

第八章, 应用深度伪造的教训,教你使用深度伪造技术解决假设问题的过程。

第九章, 生成式 AI 的未来,探讨了生成式 AI 未来的发展方向以及它们需要克服的限制。

要充分利用本书

本书旨在通过阅读章节来构建知识。如果你对深度伪造没有任何背景知识,我们建议你从开头开始。如果你想直接跳到代码,那么你应该查看第二部分(尽管我们希望你在准备好后先浏览一下第一部分)。如果你只关心未来可以使用的技术,那么请查看第三部分(但我保证早期部分也有一些有价值的信息)。

本书中的所有代码示例都使用 Python。如果你了解 Python,你应该能够在文本的帮助下理解所有代码示例。如果你不了解 Python,那么不用担心!本书中有大量的非代码解释,甚至代码中也包含了关于其中发生情况的动手解释。

本书在介绍时解释了所有使用的库,但不应将本书视为任何库的指南或深入解释。许多这些库都有自己专门的书籍,本书中它们的使用仅限于功能。

本书涵盖的软件 操作系统要求
Python Faceswap
PyTorch OpenCV
Pillow (PIL 分支)

我们在本书中使用 Anaconda([www.anaconda.com/](https://www.anaconda.com/))进行包管理和沙箱环境。如果您想跟上,我们强烈建议您从此处列出的网站安装它。如果您更愿意使用 Python 虚拟环境,您也可以这样做,但如果您这样做,本书中的说明可能需要修改才能正常工作,特别是安装必要的包。如果您选择使用这条路线,您将不得不自己找到要安装的正确版本的库。

如果您正在使用本书的数字版,我们建议您从本书的 GitHub 仓库(下一节中提供链接)获取代码。这样做将帮助您避免与代码复制粘贴相关的任何潜在错误

每个动手实践章节都包含一个练习列表。请将这些内容视为理解代码所做之事以及如何自己使用这些技术的辅助工具,而不是必须执行的指令。它们没有“答案”,因为它们实际上不是问题;它们只是提示您找到新的和令人兴奋的方式来应用您的知识。

如果您完成了任何练习(或想出了自己的一些令人印象深刻的东西),如果您愿意将本书的仓库“fork”到自己的 GitHub 账户,并向世界展示您的成就,我们将不胜感激!我们很乐意看到您如何使用 deepfakes。

下载示例代码文件

您可以从 GitHub(https://github.com/PacktPublishing/Exploring-Deepfakes)下载本书的示例代码文件。如果代码有更新,它将在 GitHub 仓库中更新。there’s

我们还有其他来自我们丰富图书和视频目录的代码包,可在github.com/PacktPublishing/找到。查看它们吧!

使用的约定

本书使用了多种文本约定。

文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“将下载的WebStorm-10*.dmg磁盘映像文件作为系统中的另一个磁盘挂载。”

代码块设置如下:

html, body, #map {
 height: 100%;
 margin: 0;
 padding: 0
}

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)

任何命令行输入或输出都应如下编写:

$ mkdir css
$ cd css

粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“从管理面板中选择系统信息。”

小贴士或重要注意事项

如此显示。

联系我们

读者反馈始终欢迎。

customercare@packtpub.com并在邮件主题中提及本书标题。

勘误表:尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将不胜感激,如果您能向我们报告,我们将不胜感激。请访问www.packtpub.com/support/errata并填写表格。

copyright@packt.com,附有材料链接。

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com

分享您的想法

读完《探索深度伪造》后,我们很乐意听听您的想法!请点击此处直接进入此书的亚马逊评论页面并分享您的反馈。

您的评论对我们和科技社区非常重要,并将帮助我们确保我们提供高质量的内容。

下载此书的免费 PDF 副本

感谢您购买此书!

您喜欢随时随地阅读,但无法携带您的印刷书籍到处走?

您的电子书购买是否与您选择的设备不兼容?

别担心,现在每购买一本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。

在任何地方、任何设备上阅读。直接从您最喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。

优惠远不止于此,您还可以获得独家折扣、时事通讯和每日收件箱中的精彩免费内容。

按照以下简单步骤获取好处:

  1. 扫描二维码或访问以下链接

图片

packt.link/free-ebook/9781801810692

  1. 提交您的购买证明

  2. 就这些!我们将直接将您的免费 PDF 和其他优惠发送到您的电子邮件。

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use约束。

第一部分:理解深度伪造

深度伪造是一种新的(且具有争议性)的技术,使用生成式 AI。但尽管它们的基本思想是交换一个面孔与另一个面孔,您对深度伪造真正了解多少?

对像深度伪造这样的东西有疑问是很正常的,本节将解答这些问题。我们将从它们如何工作的基础知识以及深度伪造构建在上的机器学习原则开始,然后看看可用于制作深度伪造的软件。之后,我们将探讨深度伪造的伦理问题,包括其令人不快的起源,并建立一个框架来评估如何制作道德的深度伪造。然后,我们将探讨创建深度伪造最重要的部分:数据,包括解释什么使数据良好以及如何从您(不太好的)数据中获得最大收益。最后,我们将通过使用开源深度伪造程序 Faceswap,向您展示一个完整的深度伪造过程。

在本部分结束时,你将很好地理解什么是深度伪造,它们是如何工作的,甚至如何自己制作一个。

本部分包括以下章节:

  • 第一章, 调查深度伪造

  • 第二章, 审视深度伪造的伦理和危险

  • 第三章, 获取和处理数据

  • 第四章, 深度伪造工作流程

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use条款约束

第一章:深度伪造调查

理解深度伪造始于了解它们的起源和功能。在本章中,我们将开始探索深度伪造及其运作。我们将讨论使深度伪造工作原理的基础知识,包括生成自编码器生成对抗网络(GAN)之间的区别。我们将探讨它们在媒体、教育和广告中的应用。我们将研究它们的局限性,并考虑如何规划和设计你的深度伪造以避免常见的陷阱。最后,我们将检查现有的深度伪造软件,并讨论每种类型能做什么。

我们将在以下章节中介绍:

  • 介绍深度伪造

  • 探索深度伪造的用途

  • 发现深度伪造是如何工作的

  • 评估生成式 AI 的限制

  • 查看现有的深度伪造软件

介绍深度伪造

“深度伪造”这个名字来源于“深度”和“伪造”的合成词,其中“深度”指的是深度学习,“伪造”指的是生成的图像并非真实。这个术语最初在流行的网站 Reddit 上被使用,原始作者在那里发布了几个将成人女演员的脸部用其他女性的脸人工替换的深度伪造视频。

注意

深度伪造的伦理问题存在争议,我们将在第二章**,考察深度伪造的伦理和危险*中对此进行更深入的探讨。

这种不道德的起点仍然是这项技术最知名的地方,但它并非只能用于此。从那时起,深度伪造已经进入了电影、表情包等领域。汤姆·克鲁斯在“深度汤姆·克鲁斯”先他一步注册 Instagram 之后才注册。史蒂夫·布西米对斯蒂芬·科尔特说,当他的脸被贴在詹妮弗·劳伦斯的脸上,而一个年轻的比尔·奈伊版本被深度伪造到他的老脸上,以电影《侦探皮卡丘》中的“过去”新闻片段时,他“从未看起来这么好”。

在这本书中,我们将对深度伪造有一个相当狭窄的看法,所以现在让我们给它下个定义。深度伪造是使用在两个面部上训练的神经网络来替换一个面部为另一个面部。还有其他一些技术可以交换面部,但它们不是深度伪造,还有生成式 AI 可以执行除面部交换之外的其他任务,但将这些都包含在术语中只会使问题复杂化并混淆视听。

探索深度伪造的用途

深度伪造的原始用途可能是最不需要想象力的一个。将一个人的脸贴在另一个人身上在各个领域有许多不同的用途。请别把这里提到的想法视为深度伪造能力的全部——肯定有人会想象出新的用途!

娱乐

娱乐是大多数人想到深度伪造用途时首先想到的领域。我认为深度伪造在娱乐领域有两个主要的应用领域:叙事恶搞

叙事

深度伪造在电影中的效用是显而易见的。想象一下,一个演员的脸被叠加到他们的替身身上,或者一个无法出现的演员被另一个表演者取代,而电影中最终的面部没有任何变化。

虽然深度伪造可能看起来还不够好,但深度伪造已经在今天的好莱坞和其他媒体中得到了应用——从使用深度伪造让比尔·奈伊返老还童的《侦探皮卡丘》,到使用它让演员与罗纳德·里根面对面交流的《为了全人类》,机构和企业特效工作室都在研究如何在他们的工作中使用深度伪造。

这些技术并不特指深度伪造。CGI(在这本书中指 3D 图形)面部替换已经在许多电影中得到了应用。然而,使用 CGI 面部替换既昂贵又复杂,需要以特定的方式拍摄,并捕获大量额外数据供艺术家使用,以便在最终场景中使 CGI 面部看起来很好。这更像是一门艺术而不是科学,需要广泛的技术和知识来完成。深度伪造解决了许多这些问题,使得新的面部替换形式成为可能。

制作深度伪造不需要特殊的拍摄技巧(尽管一些意识会使过程更加顺畅)。与 CGI 面部替换相比,深度伪造也几乎不需要注意或技能。这使得它非常适合低成本面部替换,但也可以是高质量的,因为 AI 考虑到了即使是最专注的艺术家也无法复制的细节。

恶搞

恶搞是一种极其流行的社会批评形式,是整个 To PD:电影、电视剧和其他媒体形式的基础。恶搞通常由专业模仿者完成。在某些情况下,那些模仿者看起来(或可以让人看起来)与被模仿的人相似。在其他时候,依赖于他们的表演来使模仿清晰。

Deepfakes 提供了一种改变恶搞艺术的机会,通过深度伪造,模仿者可以看起来像被恶搞的人,而不是仅仅依靠出生的偶然。通过将注意力从基本外观上移开,深度伪造允许直接将焦点放在表演本身上。

深度伪造还使一种全新的恶搞形式成为可能,在这种形式中,由于面部改变,正常情况可以简单地变得具有讽刺意味。这种特定形式由于非常不同面部的独特怪异而变得幽默,而不是预期的交换。

图 1.1 – 通过 birbfakes 将史蒂夫·布西米打造成詹妮弗·劳伦斯

图 1.1 – 通过 birbfakes 将史蒂夫·布西米打造成詹妮弗·劳伦斯

注意

这张图片是在其原始创作者 birbfakes 的允许下包含的。您可以在这里查看原始视频:youtu.be/r1jng79a5xc

视频游戏

在视频游戏方面,深度伪造提供了一个有趣的机会。这里的想法是,一个计算机生成的角色可以被深度伪造成一个逼真的虚拟形象。这可以应用于游戏中的任何角色,甚至包括玩家的角色。例如,可以制作一个游戏,当玩家的角色照镜子时,他们会看到自己的脸映照回来。另一种可能性是用原始演员的深度伪造来替换非玩家角色,这样可以得到一个更加逼真的外观,而不必制作演员的完整 3D 复制品。

教育

教育也可以从深度伪造中受益。想象一下,如果你的历史课有一段亚伯拉罕·林肯本人朗读葛底斯堡演说的视频。或者一个企业培训视频,整个视频都是由公众吉祥物(可能甚至不是真人)主持的,而不必依赖服装或 CGI。甚至可以用来让在显著不同时间拍摄的多个视频或场景看起来更加连贯,仿佛演员在同一时间出现。

许多人是非常视觉化的学习者,看到一个人“活过来”真的可以让人身临其境。使用深度伪造将过去的视频内容“复活”可以带来全新的学习体验。一个例子是达利博物馆,它创建了一系列萨尔瓦多·达利与客人交谈的视频。这是通过训练一个深度伪造模型,将达利的面孔应用到视频上实现的。一旦模型训练完成并设置好,他们就能转换许多视频,与 CGI 解决方案相比,节省了大量时间和精力。

广告

广告机构总是在寻找吸引注意力的最新方式,深度伪造可能是一个全新的吸引观众注意力的方式。想象一下,如果你路过一家服装店,你停下来看橱窗里的衣服,突然旁边屏幕上显示了一个演员穿着这件衣服的视频,但脸上是你的脸,让你看到这件衣服在你身上会是什么样子。或者,一个吉祥物角色可以在商业广告中被“复活”。深度伪造为创意使用提供了全新的工具,可以在广告中吸引注意力并提供全新的体验。

现在我们已经对深度伪造的一些潜在用途有了大致的了解,让我们快速了解一下其内部工作原理。

发现深度伪造的工作原理

深度伪造是一种独特的生成自编码器变体,用于生成面部交换。这需要一个特殊结构,我们将在本节中解释。

生成自编码器

正规深度伪造使用的特定类型的神经网络被称为生成自编码器。与生成对抗网络(GAN)不同,自编码器不使用判别器或任何“对抗”技术。

所有自动编码器都是通过训练一系列神经网络模型来解决一个问题。在生成型自动编码器的情况下,AI 用于生成一个包含原始图像中没有的新细节的新图像。然而,在普通自动编码器中,问题通常是分类(决定图像是什么)、物体识别(在图像中找到某物)或分割(识别图像的不同部分)。为此,在自动编码器中使用了两种类型的模型 – 编码器解码器。让我们看看这是如何工作的。

深伪影训练周期

训练周期是一个循环过程,模型在图像上持续训练直到停止。这个过程可以分为四个步骤:

  • 编码面孔成更小的中间表示。

  • 解码中间表示回面孔。

  • 计算原始人脸与模型输出之间的损失(即,两者之间的差异)。

  • 修改(反向传播)模型以趋向正确答案。

图 1.2 – 训练周期图

图 1.2 – 训练周期图

更详细地说,这个过程如下展开:

  • 编码器的工作是将两个不同的面孔编码成一个数组,我们称之为中间表示。中间表示比原始图像尺寸小得多,有足够的空间来描述光照、姿势和表情。这个过程类似于压缩,其中不必要的数据被丢弃以适应更小的空间。

  • 解码器实际上是一对匹配的模型,它们将中间表示转换回面孔。每个输入面孔都有一个解码器,它只在该一个人的面孔图像上训练。这个过程试图创建一个与编码器接收到的原始面孔匹配的新面孔,该面孔被编码到中间表示中。

图 1.3 – 编码器和解码器

图 1.3 – 编码器和解码器

  • 损失是一个根据自动编码器如何重新创建原始人脸而给出的分数。这是通过比较原始图像与编码器-解码器过程的输出来计算的。这种比较可以以多种方式进行,从两者之间的严格差异到包含人类感知作为计算一部分的复杂得多的情况。无论怎样做,结果都是一样的:一个介于 0 到 1 之间的数字,其中 0 是模型返回完全相同图像的分数,1 是完全相反或图像。大多数数字将介于 0 到 1 之间。然而,完美的重建(或其相反)是不可能的。

注意

损失是自动编码器与生成对抗网络(GAN)之间的区别所在。在 GAN 中,比较损失要么被替换,要么由一个额外的网络(通常是一个自动编码器本身)补充,然后该网络产生它自己的损失分数。这个结构的理论依据是,损失模型(称为判别器)可以学会更好地检测生成模型(称为生成器)的输出,而生成器可以学会更好地欺骗判别器。

  • 最后,是反向传播,这是一个通过沿着生成面孔和解码器以及编码器生成的路径回溯,并推动这些路径向正确答案靠近的过程。

图 1.4 – 损失和反向传播

图 1.4 – 损失和反向传播

一旦完成,整个过程就会从编码器重新开始。这会一直重复,直到神经网络完成训练。何时结束训练的决定可以以几种方式发生。它可以在发生了一定数量的重复(称为迭代)时发生,当所有数据都经过(称为一个 epoch)时,或者当结果达到一定的损失分数时。

为什么不是 GANs?

GAN 是目前生成网络中的宠儿之一。它们非常受欢迎,被广泛使用,尤其是在超分辨率(智能放大)、音乐生成,甚至有时是深度伪造。然而,有一些原因使得它们没有被用于所有的深度伪造解决方案。

GAN 之所以受欢迎,是因为它们的“富有想象力”的特性。它们通过生成器和判别器的交互来学习,填补数据中的空白。因为它们可以填补缺失的部分,所以在重建任务或需要新数据的任务中非常出色。

GAN 在创建缺失数据方面的能力对于许多任务来说非常出色,但当用于深度伪造时,它有一个关键的缺陷。在深度伪造中,目标是替换一个面孔为另一个面孔。一个富有想象力的 GAN 可能会学会用另一个面孔的数据来填补一个面孔数据中的空白。这导致了一个我们称之为“身份混合”的问题,即两个面孔没有正确交换;相反,它们混合成了一个既不像任何一个人,又像是两个人混合的面孔。

GAN 创建的深度伪造中的这个缺陷可以纠正或预防,但这需要更加仔细的数据收集和处理。一般来说,使用生成式自动编码器而不是 GAN,更容易实现完全替换而不是混合。

自动编码器结构

自动编码器的另一个名字是“沙漏”模型。这是因为编码器的每一层都比前一层小,而解码器的每一层都比前一层大。正因为如此,自动编码器的图在开始时很大,向中间缩小,然后在达到末端时再次变宽:

图 1.5 – 自动编码器的沙漏结构

图 1.5 – 自动编码器的沙漏结构

虽然这些方法具有灵活性并且有众多潜在用途,但它们也存在限制。现在让我们来探讨这些限制。

评估生成式 AI 的限制

深度伪造中使用的生成式 AI 并非万能,实际上存在一些显著的限制。然而,通过了解这些限制,它们通常可以通过精心设计来克服或规避。

分辨率

深度伪造在可以交换的分辨率上有限制。这是一个硬件和时间限制:更强大的硬件和更多的时间可以提供更高分辨率的交换。然而,这并不是 1:1 的线性增长。将分辨率加倍(例如,从 64x64 到 128x128)实际上将所需的VRAM(即 GPU 可以直接访问的内存)数量增加四倍,并且训练所需的时间也大致增加相同的量。因此,分辨率通常是一个权衡,你可能会想要将深度伪造的分辨率降到最低,同时不牺牲结果。

每对脸所需的训练

为了提供最佳结果,传统的深度伪造需要你在你希望交换的每一对脸上进行训练。这意味着,如果你想用自己的脸交换你两个朋友的脸,你就必须训练两个不同的模型。这是因为每个模型都有一个编码器和两个解码器,它们只被训练来交换它们所接收到的脸。

对于某些多脸交换,存在一种解决方案。为了交换更多的脸,你可以编写一个具有两个以上解码器的版本,从而允许你交换额外的脸。然而,这是一个不完美的解决方案,因为每个解码器都占用大量的 VRAM,这要求你仔细平衡脸的数量。

可能简单地训练多个对会更好。通过在多台计算机上分配任务,你可以同时训练多个模型,从而一次创建多个脸对。

另一个选项是使用不同类型的 AI 人脸替换。一阶模型(在本章的“查看现有深度伪造软件”部分有所介绍)使用不同的技术:它不是采用配对方法,而是使用 AI 来动画化图像以匹配替换者的动作。这种解决方案消除了在每个脸对上重新训练的需求,但代价是交换的质量大大降低。

训练数据

生成式 AI 需要大量的训练数据来完成其任务。有时,找到足够的数据或足够高质量的数据是不可能的。例如,当没有威廉·莎士比亚的视频或照片时,一个人如何创建他的深度伪造?这是一个棘手的问题,但可以通过几种方式解决。虽然不幸的是,无法创建英格兰最伟大的剧作家的正确深度伪造,但可以使用看起来像他的肖像的演员,然后深度伪造这位演员成为莎士比亚。

小贴士

我们将在第三章,“掌握数据”中更详细地介绍如何处理不良或不足的数据。

寻找足够的数据(或巧妙的工作方案)是任何数据科学家面临的最困难挑战。有时,根本无法获得足够的数据。这时,你可能需要重新审视视频,看看是否有其他拍摄方式来避免数据不足,或者你可能尝试使用其他类似数据来源来填补空白。有时,提前了解限制可以防止问题发生——有时,在最后几分钟的解决方案可能足以挽救一个项目免于失败。

虽然每个人都应该了解数据限制,但了解流程的限制仅限于专家。如果你只是想使用深度伪造,你可能会使用现有的软件。让我们接下来探索这些软件。

查看现有的深度伪造软件

已经有许多程序填补了深度伪造的空白;然而,其中很少仍在开发或受到支持。GPU 硬件和 AI 软件的快速发展导致了软件开发中的独特挑战,许多深度伪造程序已经不再可用。然而,仍然有几种深度伪造软件程序,在本节中,我们将介绍主要选项。

重要提示

作者们已经尽最大努力在本节中保持无偏见,但他们也是 Faceswap 的开发者之一。Faceswap 将在第四章,“深度伪造工作流程”中更详细地介绍,其中包括通过 Faceswap 软件的深度伪造工作流程的演练。

Faceswap

Faceswap 是一个免费和开源FOSS)的软件程序,用于创建深度伪造。它根据 GPL3 发布,任何人都可以在任何地方使用。它用 Python 编写,在 TensorFlow 后端运行 AI。它支持 NVIDIA、AMD 和 Apple GPU 以加速机器学习模型,或者可以在 CPU 上以较低的速度运行。它提供了 Windows 和 Linux 的安装程序,可以在一个自包含的环境中安装所有需要的库和工具。

它可在Faceswap.dev/找到。

DeepFaceLab

DeepFaceLab 最初是 Faceswap 的一个分支,现在主要由 Ivan Perov 开发。DeepFaceLab 是另一个用于深度伪造的开源软件程序,以其更多的实验模型和功能而闻名。它没有图形用户界面,但提供了可以在任何 Jupyter 环境中运行的 Jupyter 笔记本。还有一个 DirectML 版本,为使用 Windows 的用户提供了另一个选项。还有完全封装的构建版本,打包成一个压缩文件,为许多操作系统提供了一个完全工作的软件包。

它可在github.com/iperov/DeepFaceLab找到。

First Order Model

First Order Model与 Faceswap 和 DeepFaceLab 的工作方式在本质上不同。它不是将面部交换到新视频中,而是“操纵”面部,使其与视频的动作相匹配,但保持面部不变。此外,它不需要对每一对面部进行训练,这使得制作快速深度伪造变得容易,即使只有一张照片,也可以“动画化”一个人。

需要注意的是,尽管 First Order Model 软件可以免费获得,但它仅限于非商业用途的许可:如果你想在商业环境中使用它,你需要联系作者获取许可。它可在github.com/AliaksandrSiarohin/first-order-model找到。

Reface

Reface是创建深度伪造的另一种方法。Reface 是闭源和专有软件,因此我们无法确切分析其工作原理,但它使用了一种零样本学习方法,类似于 First Order Model,可以在不要求在成对交换上进行训练的情况下交换面部。Reface 为 Apple iOS 和 Android 提供应用程序,并在云端进行交换,这使得快速获得结果变得更容易,但可能无法交换你想要的精确剪辑,并且可能存在许可问题。

它可在reface.ai/找到。

摘要

深度伪造技术本身并不是什么新或独特的东西。这些技术以各种形式存在,在它们被应用于面部交换之前就已经存在,但深度伪造以一种其他 AI 技术从未真正实现的方式引起了公众的关注。看到面部出现在它不应该出现的地方,看到你认识的演员扮演他们从未扮演过的角色,或者看到你自己的面部做着你从未做过的事情,这些都让人感到非常直观。

虽然构成深度伪造的技术各自之前都存在,但结合起来,它们提供了全新的可能性。深度伪造可以应用于众多用例,从替身演员的替换到广告。这项技术已经存在,随着越来越多的行业找到使用它的方法,其应用将只会增长。

生成式人工智能的能力仍然有限。了解深度伪造不能做什么,与了解它能做什么一样重要。特别是在数据方面,了解如何克服这些限制是获得高质量结果的关键。

我们已经概述了深度伪造技术,包括它们是什么,可以用于什么,如何工作,它们的局限性,以及你可以用来制作它们的现有软件。在下一章中,我们将探讨深度伪造的潜在危险,并讨论这项技术带来的伦理问题。

EBSCOhost - 2023 年 11 月 27 日 上午 6:20 打印。所有使用均受www.ebsco.com/terms-of-use条款约束。

第二章:检查深度伪造的伦理和危险

深度伪造以其极端危险和可疑的伦理而闻名。虽然任何技术都可能被滥用,但真正不道德的是使用方式,而不是技术本身。这同样适用于深度伪造。只要使用方式是道德的,深度伪造就可以成为一种道德技术。然而,深度伪造仍然存在潜在威胁。这项技术是公开可用的——任何人都可以制作深度伪造,甚至那些有不良意图的人。限制深度伪造危险的最佳方式可能是确保每个人都知道它们,有效地使公众免受仅因为他们看过视频就信任视频的诱惑。

在本章中,我们将探讨深度伪造的伦理和危险。我们将检查深度伪造起源的伦理,制定一些创建道德深度伪造时应遵循的指南,讨论深度伪造的危险,并介绍一些对抗深度伪造的防御措施。

在本章中,我们将涵盖以下主题:

  • 深度伪造的不道德起源

  • 成为一名道德的深度伪造者

  • 深度伪造的危险

  • 防止深度伪造造成的损害

深度伪造的不道德起源

深度伪造始于一个不幸的不道德起点。第一个公开的深度伪造是由一个名叫/u/Deepfakes 的 Reddit 用户创造的。这些展示深度伪造能力的第一批视频通常涉及将(通常是)女演员放入色情场景中。这种未经同意的色情内容与之前的伪造截然不同,因为它们更加逼真,更容易创建,而且是全视频,能够呈现目标演员从未在任何记录媒体中表演过的姿势和表情。

未经同意的色情深度伪造并非没有受害者的事件。这种使用方式极其有害,因为它以深刻的方式伤害了演员。随着未经同意的深度伪造的出现,名人正在失去对自身形象和声誉的控制。不幸的是,无法完全避免深度伪造带来的威胁,因为即使在证明视频实际上不是他们之后,深度伪造视频仍然会被传播并使演员被对象化。一些在色情电影中被深度伪造的人甚至不是名人,他们没有准备好或无法应对这种情况的关注和恶名。

作者注

虽然我们相信我们的立场是明确的,但我们还想再次重申,作者不赞成或参与任何未经同意的色情深度伪造。我们认为这种做法是可怕的,对受害者造成了严重的伤害。

将女性面孔放入她们未参与或未同意的色情视频中,显然是对任何技术的高度不道德使用。不幸的是,这仍然是深度伪造技术最知名的使用方式。然而,这种情况正在慢慢改变,深度伪造技术因其灵活性和创新性而越来越受到认可。

成为一名道德的深度伪造者

深度伪造有许多潜在用途,既有道德的也有不道德的。有时很难判断什么是道德的或不道德的。例如,所有色情深度伪造都是不道德的吗?如果所有参与者都表示同意会怎样?有时这很难回答,有很多模糊性,但有一些你可以遵循的指导原则和一些你可以运行的测试,以尝试确保你的使用在道德问题的正确一边。

同意

可能最重要的道德问题是关于同意的。同意很简单:所有相关人士都愿意参与吗?这最明显的问题出现在色情深度伪造中——如果并非所有参与者都表示同意,那么这无疑是不可接受的。

然而,当视频是讽刺性的或者一个演员被放入他们最初没有表演过的角色时,这一点就变得不那么明显了。非常少的深度伪造创作者能够从演员那里获得明确的许可,即使所有演员都对使用表示同意,联系演员获取许可也会很困难。

一些深度伪造,例如由 birbfakes 制作的詹妮弗·劳伦斯-布斯卡米版本,在没有直接获得相关人士许可的情况下已经引起了大量关注(在史蒂夫·布斯卡米的情况下,甚至得到了回应)。尽管缺乏同意,但这些用途可能仍然是道德的,因为它们是在恶搞和没有违反其他道德关切的情况下进行的。保持这种平衡可能很困难,但即使是在假设的情况下,同意也需要被考虑。

即使一个演员或女演员已经去世,这并不意味着你不需要从他们的遗产或家人那里获得许可。迪士尼获得了卡丽·费舍尔及其家人的许可,使用 CGI 技术在《侠盗一号》和其他《星球大战》作品中复制她的角色。

一个重要的注意事项是,虽然你应该(如果可能的话)从你要交换的人那里获得许可,但你同样应该考虑你要交换到的人。虽然那里的伤害可能不那么明显,但这仍然是一个可能有害的行为。

尊重

重要的是要记住,你正在交换的人和被交换的人都是真实的人,他们有情感,并且努力达到现在的位置。你应该尊重你正在交换的人。

在涉及色情内容的情况下,这又是一个被违反的点。演员在审视每一个角色时都必须做出积极的决定,以确定他们是否舒适于该内容。这包括他们在角色中是否舒适于裸露或性内容。即使他们过去可能拍摄过一些类似的场景,如果作品、背景或甚至观点发生了变化,他们可能会改变主意。当一个选择不参与性角色的个人被深度伪造到色情场景中时,他们的决定被忽视和违反。过去参与过性场景的演员能够在工作的背景下做出那个决定;这并不意味着当他们被置于不同背景或其他场景中,或者被置于他们可能不再选择参与的场景中时,他们不会受到伤害。

在克里斯·乌梅(Chris Ume)的深度伪造汤姆·克鲁斯(Deepfake Tom Cruise)和类似作品中,他们通过展示无过激或不尊重的内容,遵守了这一规则的积极一面。深度伪造汤姆·克鲁斯所做的事情并不超出汤姆·克鲁斯本人可能做的事情。需要考虑的关键点是,这些伪造的内容并没有损害他的声誉或贬低他的角色,只是提供了一个窗口,让深度伪造者相信汤姆·克鲁斯会在那种情况下如何行动。

欺骗

虽然之前的指南都是关于被替换的人,但这一条实际上是关于观众的。在明显且故意地表明深度伪造的同时创建深度伪造,意味着潜在的危险减少,并有助于保持它们在道德使用的方面。

这并不意味着你的作品不能被不道德地使用。例如,如果它被脱离上下文并在社交媒体上分享,它可能会在没有警告它是深度伪造的情况下广泛传播。为了应对这种情况,关于它是深度伪造的警告可能需要比开头的免责声明或描述更多。试着想想它可能被如何滥用,并以与风险相匹配的方式呈现警告。

如果被滥用的风险很低,那么可能不需要明确的警告,而是简单地允许情况的荒谬性或深度伪造的无害性质消除风险。例如,大量将演员尼古拉斯·凯奇(Nicolas Cage)替换进视频中的情况就是这样。很容易看出凯奇不是原始演员(尤其是当深度伪造将尼古拉斯·凯奇换到《钢铁侠》中的艾米·亚当斯身上时),即使没有,任何真实损害的风险也很低。

另一个明显的例子是,当你的深度伪造被用于特效或视觉特效普遍存在的地方。如果你在电影中引入已故演员,而那里预期会有特效,那么明确标注内容为深度伪造就没有太多必要。仅凭上下文本身就足以清楚地表明,你不能相信你所看到的一切。H.G.威尔斯的广播剧《世界大战》在街头引起恐慌的著名故事,不太可能发生在深度伪造使演员在电影中复活的情况下。

现在我们知道了如何判断深度伪造的道德性,那么我们将其付诸实践如何?

将其付诸实践

在本节中,我们将查看几个深度伪造案例,并使用我们上面提出的指标来评估它们。

深度汤姆·克鲁斯

深度汤姆·克鲁斯是 Metaphysic 公司的一个项目,该公司为视觉效果和电影制作深度伪造。他们制作了几段视频,在各种各样的情境和环境中将汤姆·克鲁斯深度伪造。他们在 TikTok 上发布了这些视频,链接为www.tiktok.com/@deeptomcruise

  • 同意: 对于同意,我们查看他们是否得到了相关人员的许可。深度汤姆·克鲁斯没有得到克鲁斯先生的许可。然而,克鲁斯是一位公众人物,因此获得明确的许可可能是不可能的。他们确实得到了模仿者 Miles Fisher 的许可(及参与),他将自己的表演深度伪造到了汤姆·克鲁斯身上。

  • 尊重: 克鲁斯先生从未被展示出任何不适当的行为,甚至没有表现出与他性格不符的行为。视频尊重了克鲁斯的名誉和个性。然而,他们确实展示了克鲁斯在个人生活中的一些他通常不会分享的事情,比如打高尔夫或吃午餐,但这些都没有任何伤害性。

  • 欺骗: 深度汤姆·克鲁斯显然不是克鲁斯先生的真正账户。账户本身的名称就表明了这是一段深度伪造。这伴随着采访和新闻报道详细说明了这是一段深度伪造,以及它是如何制作的。

  • 分析: 虽然他们没有得到克鲁斯先生的明确许可,但他们并没有做任何损害他名誉的事情,并且清楚地说明了这是一段深度伪造的内容。总的来说,这是深度伪造的道德使用,但如果他们在创建项目之前得到克鲁斯的明确许可会更好。假设克鲁斯反对这个项目,他们可能会将其从公共视野中移除并最小化损害,因为如果克鲁斯提出异议而保留它,就会使其变成不道德的使用。

达利还活着

达利永生是一个艺术项目,展示了在达利博物馆的萨尔瓦多·达利的交互式深度伪造。该项目声明的目标是让游客能够虚拟地“遇见”并直接从达利本人那里了解这位艺术家。您可以在thedali.org/exhibit/dali-lives/查看关于展览的信息:

  • 同意: 萨尔瓦多·达利已经去世,没有子女或存活下来的直系亲属。在这种情况下,获得同意几乎是不可能的。

  • 尊重:很难想象比《达利在世》更尊重的用法了。博物馆付出了巨大的努力,创造了一个达利的形象,以便参观博物馆的人可以与之互动并看到达利。博物馆展示了他的许多画作和其他艺术作品,而《达利在世》完美地融入了那个环境。

  • 欺骗: 博物馆非常明确地指出,“达利活着”是一件艺术品,而非真品。该项目在博物馆的多个位置设立,并配有说明牌,解释所展示的达利并非真实人物,而是一个让人们与达利形象互动的项目。他们还包含一个展示深度伪造过程的“制作过程”视频。

  • 分析:这无疑是一个非常道德的深度伪造技术的应用。很难想象有比让博物馆的参观者与已经不再活着的历史人物互动更道德的使用方式了。

尽管他们缺乏同意,但可以想象,如果达利有能力的话,他会欣然同意。他热爱荒诞和恐怖。他能与来自另一个世界的客人互动的想法,与他本人的感觉完全相符。

此外,该项目对达利表示了尊重,将他置于一个与他完美融合的环境中——一个拥有他众多项目的博物馆。

最后,没有欺骗。任何访问者都不会相信他们真的在与达利互动,因此这条路径也是清晰的。

虽然有方法可以道德地处理深度伪造,但深度伪造带来了哪些危险?

深度伪造的危险

声誉

在某些方面,声誉的威胁听起来像是深度伪造可能带来的最微不足道的潜在危险。然而,声誉对于各种事情来说可能极其重要。想象一下,如果有人因为深度伪造侮辱他们的老板而失去工作,或者另一个人因为深度伪造的虚假认罪而被警察逮捕。这些影响可能会累积并严重损害一个人的未来,因为他们对这些行为没有任何控制权。

在互联网时代,广泛损害声誉已经变得极其容易。人们因为真正采取的行动而被“取消”资格,这可能会对一个人的未来造成长期伤害。即使损害只是暂时的或有限的,有未实施的行为导致类似伤害的威胁也是一个令人恐惧的前景。

政治

深度伪造在政治领域中的危险与对声誉的危害相似,但因其声誉损害可能带来的公共政策效应而被放大。在许多方面,政治家依赖他们的声誉来获得选举。很少有人愿意选举一个他们认为不诚实或不名誉的人(这并不等同于实际上就是不诚实或不名誉)。

如果一个政治团体受到深度伪造诽谤运动的攻击,许多人即使知道这些结果是虚构的,也可能会相信它们。这是人类心理的遗憾之处,我们更倾向于相信那些证实我们已有信念的东西,而不是那些反驳它们的东西。一旦公众在心中对候选人形成了看法,就很难改变这种看法,尤其是在声誉严重受损后。

深度伪造技术不幸地已经被用于政治领域,并造成了不同程度的损害。一些相对无害的用途,例如 Trey Parker 和 Matt Stone 用 Sassy Justice 来讽刺当时的总统唐纳德·特朗普。这类讽刺视频不太可能造成真正的伤害,因为我们的社会已经习惯了用幽默来对付政治家,作为一种批评或娱乐的手段。事实上,许多政治家会积极参与那些讽刺他们的节目。一个例子是,巴拉克·奥巴马在 2015 年白宫记者晚宴上邀请基根·迈克尔·基担任他的“愤怒翻译官”——这个角色起源于喜剧中央的基与皮尔节目中的讽刺。

然而,我们已经看到候选人在经过篡改的视频中遭受了重大的损害。2019 年初,在社交网络上传播的一个视频中,南希·佩洛西的视频被慢放,以暗示她喝醉了或作为政治家不合格。在伊拉克,多名女候选人因公开发布的声称是她们自己的色情视频而退出选举,损害了她们的声誉。

通过声称操纵来避免后果

虽然很容易看到深度伪造可能造成的损害,但深度伪造也可能在没有深度伪造的情况下制造问题。现在深度伪造和操纵媒体已经进入公众认知,因此,显示不当行为的真实视频可能会被标记为深度伪造并被忽视。

对于一些小错误或不适当的行为,这不太可能构成严重威胁,因为一直以来都有可能削弱或删除显示某人做争议性事情的视频(甚至照片或语音录音)。对于更严重的情况,如犯罪或严重违规的视频,警方或法院不太可能长期被深度伪造的声称所困惑或劝阻。

然而,存在一系列潜在的丑闻,可能没有其他防御方法,除了说服人们视频是伪造的。在这些情况下,涉及的人可能声称视频是操纵,作为对争议的辩护。

虽然了解潜在的危险是好的,但可以说,了解如何保护自己免受这些危险更为重要。

防止深度伪造造成的损害

没有一种万无一失的方法可以使自己免受深度伪造或其影响,但你可以采取一些活动和行动来降低风险。在本节中,我们将探讨你可以使用的方法来最大限度地减少可能造成的损害。

贫血模型数据

虽然深度伪造可能只需要最少的数据,但结果并不流畅或高质量。这意味着,如果你能简单地阻止深度伪造者获取足够的数据,你就可以阻止深度伪造达到足够的质量以欺骗任何人。

如果你试图保护名人,这种策略要困难得多,因为几乎任何可用的电影、电视剧、访谈甚至照片拍摄都是模型的训练数据来源。事实上,几乎不可避免的是,有足够的数据可以针对任何名人,因为他们的公众性质意味着大量的训练数据将公开可用。

然而,作为一个私人个体,控制你的媒体存在要容易得多。通过避免公开的视频或图像,你可以防止深度伪造者获取足够的内容来创建一个令人信服的结果。为此,你不能允许公开发布的你自己的图像或视频。这在社交媒体上更难,因为社交媒体允许你的朋友给你贴标签,这可能导致深度伪造者找到你无法控制的媒体被发布。

验证任何真实的媒体

现在已有服务提供媒体验证。这允许用户嵌入可验证的水印或元数据,从而证明媒体的真实性。不幸的是,大多数网站和服务会移除元数据,并对图像和视频进行重新压缩,导致某些类型的水印被移除。这意味着即使你验证了所有真实媒体,也仍会有几乎完全相同但未嵌入验证的版本。

验证媒体是解决深伪问题的一个有趣方案,但可能不可行:即使每个人都开始支持验证媒体,用户也需要接受培训以验证其真实性,而这只有在所有主要网站都支持媒体验证之后才可能实现。这导致了一个“先有鸡还是先有蛋”的问题,即媒体网站不会在公众要求之前支持媒体验证,而公众只有在所有主要网站支持之前才会意识到这一点。

尽管验证媒体存在缺陷,但提前开始验证自己的媒体可能是个好主意,这样你将来在需要验证媒体时就能做好准备。

深伪检测

另一个高度依赖大规模支持解决方案是深伪检测。深伪检测背后的理念是,网站将任何托管视频通过算法或流程来识别深伪内容,并将它们标记为虚假或阻止它们出现在网站上。在这种情况下,任何拥有完美深伪检测器和标记政策的网站都将免受深伪造成的损害。

不幸的是,解决方案并不简单。首先,深伪检测需要致力于检测和标记任何深伪内容。公司虽然口头上表示关注检测,但最终,深伪检测目前并未被任何主要服务采用。目前,公司提供全面深伪检测解决方案的经济激励很少,但法律变化或公众对深伪检测的需求可能会改变公司的政策,使他们提供这种检测。

关于检测的免责声明

在撰写本书时,作者们不知道有任何社交媒体或视频内容服务对所有托管视频进行深伪检测;然而,有可能服务正在使用检测来标记作者们不知道的视频。

即使网站开始致力于检测深伪,也不太可能直接阻止它们。深伪有合法用途,网站更可能选择对它们进行标记(无论是公开还是内部)并监控它们,而不是完全阻止这些视频。这将限制任何缓解措施的有效性,只针对最明显的违规行为,允许观看次数较少或不太冒犯的深伪内容继续存在于平台上。

另一个问题在于深度伪造检测的准确性。目前还没有完美的方式来识别和预防深度伪造。已经有许多尝试通过不同的解决方案来识别和标记深度伪造,这些方法从纯粹的技术手段到自动和人工审查的混合。这些方法可以捕捉到一些深度伪造,但不是全部。即使它们捕捉到了所有当前的深度伪造,技术也在不断发展和进步——一些开发者可能会简单地利用深度伪造检测来改进他们的深度伪造,使其再次难以检测。这会导致与恶意软件检测器所经历的同类型猫捉老鼠的游戏。深度伪造制作者可能会找到新的方法来欺骗检测器,即使只是短暂的时间。

最后,即使你有一个完美的深度伪造检测方法,在长期来看这也将是有限的。即使一个网站开始阻止深度伪造,还有很多其他服务可以发布深度伪造。即使每个主要网站完全阻止了深度伪造,也还会有专门用来欺骗人们的网站出现。没有对整个互联网的完全控制,完全阻止网络上的深度伪造是不可能的。

公关

如果你有钱并且有需求,总有公司专注于帮助你管理声誉。他们可能非常有效,并且可能能够积极阻止深度伪造最初被发布。

公关公司将与 Facebook 和 YouTube 等主要公司建立联系。他们能够比手动尝试更快地删除不适当或违反规定的图像或视频。他们还将拥有法律部门和工具,可能能够从其他来源删除深度伪造视频。

即使视频无法被删除(或者已经开始传播),公关公司也可以处理视频带来的后果,提前介入,说服新闻机构忽略视频,或者推动关于视频被操纵的故事。

最大的问题是公关公司并不便宜,并且对愿意与之合作的人有所选择。除非你出名或者有非常宝贵的声誉需要维护,否则可能很难或者费用高昂才能让公关公司为你工作。

公众意识

另一种防御深度伪造的方法是提高公众对深度伪造的认识。一旦公开,威胁和潜力可能会促使对视频和图像进行额外的审查。这种方法的优势是即使在新技术或技术出现时也能发挥作用,但它依赖于公众的认识和质疑内容的意愿。

摘要

深度伪造的伦理和危险可能是最被广泛宣传的方面。然而,这些都是所有深度伪造创作者都应该考虑的问题,因为如果不考虑这些问题,很容易进行不道德的操作。即使不是在创造深度伪造的人也可能需要思考如何防止深度伪造或其他操纵性媒体造成的损害。

作为创作者,你必须考虑你所创造内容的伦理问题。确保你评估诸如同意、个人经历以及深度伪造标签等问题,是清除伦理障碍和制作低风险深度伪造的好方法。

即使是非创作者,人们也应该对深度伪造的威胁保持警惕。这项技术“已经泄露出去”,无法抹去。即使这项技术立即被禁止,国家层面的行为者仍然能够以可能危险的方式使用它,并且可能忽视对其使用的任何法律或限制。从声誉损害到政治操纵,再到那些声称视频是深度伪造以避免后果的人,这些都是我们只能通过批判性思维来减轻的新威胁,而不是通过完全忽视这个问题。

作者注

本书作者也是开源深度伪造软件 Faceswap 的开发者。我们考虑了同样的问题以及公开深度伪造的伦理。我们认为损害已经造成,现在移除对技术的访问并不能阻止未来的滥用。我们相信最好的前进方式是确保公众意识到威胁,以便将批判性思维应用于任何视频,以考虑它们是否被操纵或异常。

深度伪造的危险确实有限,并且有方法可以防御。目前,深度伪造需要大量的数据,那些不生活在公众视野中的人可能能够剥夺深度伪造创作者制作深度伪造所需的内容。对于那些不能完全删除所有内容的人,他们可以倡导深度伪造检测、媒体水印或使用公关公司来帮助避免操纵性媒体造成的损害。

在下一章中,我们将探讨数据的重要性,如何获取足够的数据,以及如何充分利用你已有的数据来制作尽可能高质量的深度伪造。

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use约束。

第三章:数据获取和处理

数据是任何 AI 任务最重要的部分,深度伪造也不例外。交换的质量受到输入数据的质量限制,选择数据是深度伪造制作者最重要的任务。虽然有一些方法可以自动化数据收集的部分,但这个过程仍然主要是手动的。

在本章中,我们将讨论数据的重要性、什么因素使数据质量好、如何获取数据以及如何改进差数据。这些技能对于深度伪造制作者至关重要,必须加以培养。本章将解释这些技能的基本原理,但它们必须通过实践来完全理解和应用。

上采样

我们将涵盖以下部分:

  • 为什么数据很重要

  • 理解多样性的价值

  • 数据来源

  • 提升你的数据

  • 上采样

为什么数据很重要

神经网络通过处理已知数据来训练深度伪造 AI(见第一章调查深度伪造,了解整个过程)。我们简单地将这组数据称为数据集。为了创建数据集,数据必须经过处理和准备,以便神经网络有东西可以训练。在深度伪造的情况下,我们使用人脸,需要检测、对齐和清理,以创建一个有效的数据集。

没有格式正确且准备好的数据集,神经网络就无法进行训练。对于像深度伪造这样的生成网络,还存在另一个潜在问题——质量差的数据集会导致交换效果差。不幸的是,在开始时很难知道数据集是否会生成好的交换效果。这是一种需要时间来学习的技能,而且在你学习数据的重要性时,你最初的几个深度伪造作品可能不会很好。

花在清理和管理数据上的时间是值得的。虽然深度伪造中最大的时间消耗是训练时间,但那段时间不需要创作者的输入——然而,如果你的数据有缺陷,那么这段时间将完全浪费。因此,大多数深度伪造制作者在投入长时间训练之前,会花大量时间清理和测试他们的数据。

你可能认为分辨率(图像中的像素数量)在你的数据集中很重要,但实际上并不是那么重要。可用的超高分辨率深度伪造非常少。视频可能是 4k、8k 甚至更高,但人脸交换将受到 AI 模型、训练计算机的能力以及可用于训练的时间的限制。在深度伪造的世界里,512x512 像素是一个非常高的分辨率,而更高的分辨率则留给最专注和技艺高超的深度伪造制作者。

这并不意味着你不能用较低的分辨率获得好的结果。一种清晰的方式来思考这种差异是保真度并不等同于分辨率。保真度衡量的是从原始信息中丢失了多少信息,即图像的逼真程度或真实感。想象一下一幅小缩略图,如图 3.1中展示的蒙娜丽莎的画像,它是一个逼真的图像,但分辨率低。在这个例子中,相反的是一位 5 岁孩子(或在这种情况下,作者之一)使用电脑上的高级绘图板绘制的蒙娜丽莎画像。这幅图像分辨率高,但保真度低。随着你的训练,你的深度伪造 AI 将提高其保真度,但分辨率将保持不变。

图 3.1 – 列奥纳多·达·芬奇的“蒙娜丽莎”,高保真但低分辨率图像(UL);布莱恩·莱昂的“蒙娜丽莎”,高分辨率但低保真度图像(LR)

图 3.1 – 列奥纳多·达·芬奇的“蒙娜丽莎”,高保真但低分辨率图像(UL);布莱恩·莱昂的“蒙娜丽莎”,高分辨率但低保真度图像(LR)

数据对你的深度伪造非常重要,而多样性是确保良好数据的最有效方式。

理解多样性的价值

多样性是良好数据集的最具定义性的特征。最好的数据集都将拥有大量不同的姿势、表情和照明情况,而最差的结果将来自在这些类别中缺乏多样性的数据。在本节中,我们将涵盖的一些多样性领域包括姿势、表情和照明。

姿势

姿势是一个简单且易于观察和理解的类别。姿势简单来说就是图像中面部方向和位置。在深度伪造中,只有面部本身的姿势很重要——身体其他部位的姿势被忽略。在深度伪造中,姿势很重要,这样 AI 才能学习到面部所有角度和方向。如果没有足够的姿势数据,AI 将难以匹配面部方向,最终导致结果不佳。

图 3.2 – 不同姿势的示例

图 3.2 – 不同姿势的示例

一个快速获得完整姿势范围的方法是在被拍摄时简单地移动头部。通过向上、向下和从侧面四处张望(以及这些动作的组合),你为 AI 提供了它所需的所有方向。别忘了用高焦距的远摄镜头来放大面部,以及用中性焦距的特写镜头。远摄镜头对远处的面部进行“扁平化”(这种效果正确地称为镜头压缩),常在电影中用于戏剧效果,因此你需要匹配你数据中的这种变化。甚至像镜头畸变这样的因素也很重要,它足以影响图像,因此获取各种示例可以帮助获得高质量的数据。

表情

表情是另一个容易理解的方面——它是训练期间脸部形成的形状。不幸的是,很难确保你涵盖了全部范围而没有遗漏重要数据。想想一个脸部的表现力可能有多大——快乐、悲伤、愤怒、清醒、惊讶等等——然后想想在数据中错过这些表情的重要子集可能有多容易。

没有简单的方法可以确保你拥有所有需要的多样性,但越多越好。你不想只是让人坐下来,面对镜头拍一个小时,因为你不太可能得到你真正想要的全部表情。要真正获得丰富的表情多样性,你需要确保从不同日期、不同环境和不同情绪刺激中获取数据。一个演员在不同的情境或时间可能会给出截然不同的“悲伤”表情,这种多样性对于人工智能真正理解如何重现这些相同的表情非常重要。

图 3.3 – 不同表情的示例

图 3.3 – 不同表情的示例

这里的关键是尽可能多地获取不同的表情。你不可能一次性获取所有数据;如果你这么做,你的数据将会极其有限。如果你有一个优秀的演员作为目标面孔,你可能能够让他们匹配你正在交换的表情,但你可能想要获取一个更加多样化的数据集,以确保你在正确的情境中获取正确的数据。

照明

照明可能听起来简单且/或微不足道,但问问任何摄影师或视频制作人员,他们可以永远谈论这个话题。即使不涉及人工智能,照明照片或视频拍摄也是复杂的,但当涉及到深度伪造时,它成为一个巨大的问题。完全获得各种照明是不可能的,但有一些关键点需要考虑。

“良好的”照明

大多数好莱坞电影都是专业照明管理的例子,整个团队致力于最简单的设置。然而,对于深度伪造的基本照明有一些很好的基本规则。

明亮的照明不能来自单一方向。这会导致眼睛、鼻子旁边以及经常是整个脸部一侧出现深阴影。即使是直接面向镜头的照明也会在边缘产生阴影(更不用说演员眯眼时可能会改变表情)。相反,你需要有良好的环境式照明——也就是说,从所有方向均匀照射的照明。拥有大型荧光灯的办公室可能是这种照明的良好例子(尽管灯光的颜色可能不尽如人意)。

阴影

然而,环境光线并不是深度伪造所需的唯一照明风格。数据集中需要一些阴影,否则 AI 将永远无法学会如何重现除了完全照亮的面部之外的其他任何东西。对于这种照明,你可以使用普通的顶灯或灯具。这将使 AI 能够捕捉到那些真正有助于将面部深度展现出来的较深阴影。

获取阴影多样性的最佳方式是在照明或主题移动的场景中,这样你就能获得大量的变化阴影,AI 可以从中学习。

户外

最后,你将想要获取一些户外图像。太阳在所有其他光源中特别独特。没有任何东西能够完全匹配来自数百万英里之外的燃烧等离子体的自然效果——信不信由你,距离确实很重要,因为它会导致基本上平行的光线。正因为如此,无论你在摄影棚内拍摄多少,获取至少一些户外数据都非常重要。太阳的光线是无法复制的,获取户外拍摄的数据对于补充 AI 的照明数据是必不可少的。

将这种多样性结合起来

考虑到多样性的所有方面是很重要的,但实际上你还需要考虑它们一起。你应该获取混合匹配所有三个方面的数据,以确保你获得最佳结果。例如,你所有的不同照明场景都应该有各种各样的表情和姿势。只有通过多样化的数据,AI 才能学会创建一个真正令人信服的深度伪造。

良好的多样性至关重要,但你怎么能实际获取所需的数据呢?

数据来源

如果你正在制作深度伪造,那么你可能已经知道你要替换成谁了。希望你是幸运的,正在制作一个可以轻松拍摄或收集数据的深度伪造,涉及两个人,这样你就可以在没有太多麻烦的情况下进行拍摄。不幸的是,并不是每个人都很幸运,大多数时候,你的一个或两个主题可能无法获取定制数据(这也许是你一开始制作深度伪造的原因)。

这两种情况需要非常不同的数据获取方法,有时,即使你能够很好地接触到你的主题,你也可能需要从另一个来源获取一些数据。

拍摄自己的数据

拍摄自己的数据源是任何深度伪造者的梦想位置。能够通过将演员置于摄像机前构建完美的数据集是一种解放,但如果你不知道如何捕捉所有需要的内容,这也会是一个浪费的机会。

作者注记

这本书的范围甚至无法触及摄影和电影所涉及的广泛词汇和技术。如果你计划大量拍摄自己的数据,强烈建议你在上片场之前阅读一些摄影或电影制作书籍,观看一些教程,并与该领域的专家交谈,以便充分利用时间。

电工(负责片场照明的负责人)违背他们的所有训练,故意给你提供糟糕的照明,如果你不是专家,可能会感到害怕。告诉演员做出“更悲伤”的表情或做出极端的表情可能会同样尴尬。在拍摄深度伪造时,你需要清楚地告诉剧组,你确实需要他们的常规技能,但你还需要超越常规,进入奇怪、神秘和不舒服的领域。

虽然你可能想确保数据总是近距离拍摄,演员的脸填满整个画面,但不要害怕让脸更小或离摄像机更远;通常不是数据中脸部分辨率的问题会损害结果。

当你拍摄深度伪造时,需要考虑所有方面。即使是在拍摄要替换的视频时,也要考虑深度伪造技术的局限性。例如,侧面这样的角度面部在没有产生不自然效果的情况下特别难以替换。正因为如此,你将希望尽量减少远离摄像机轴线面部数量的最大化。它们不必直接面对摄像机,特别是如果你在训练数据中得到很多角度,但总会有一个点,事情变得过于角度化,以至于 AI 无法提供良好的结果。

一个重要的细节是,随着你技能的发展,你的深度伪造会随着实践而提高。因此,强烈建议你不要依赖你的第一个深度伪造是完美的,如果你有一个即将到来的重要项目需要深度伪造,你可能想要先创建几个练习深度伪造,这样你就可以在拍摄重要的深度伪造时获得必要的经验,以确保你能够获得高质量的数据。

从历史资料中获取数据

有时候,你无法拍摄你的主题——例如,如果演员已经去世,或者你正在伪造一个年轻版本的演员。在这些情况下,你无法享受制作自己的数据来满足你情况的优势。这意味着你必须依赖更早的资料来获取所有训练数据。

希望你的主题是最近的,并且有他们的视频和照片。由于周围缺乏原始内容,一些主题无法进行深度伪造——例如,阿尔伯特·爱因斯坦将永远不会制作传统的深度伪造,仅仅是因为没有必要的训练数据。在这种情况下,你最好的选择是使用一定程度的模仿者。这就是达利博物馆用来让达利复活的技术——模仿者和一些有限的艺术家视频访谈。

如果你足够幸运,你的主题有数据,你可以使用任何可用的数据,并且你不需要根据年龄或分辨率进行限制(参见为什么数据很重要部分关于分辨率和保真度的讨论)。相反,更多地关注获得多样性。知道在哪里停止可能是一个挑战,但通常,花在收集和管理数据上的时间是值得的。从长远来看,花在数据上的时间不太可能浪费。

电影、访谈甚至人们的随机照片都可以用作深度伪造训练数据。你不希望过度依赖其中任何一个,因为它们在领域上都非常有限。恐怖电影不太可能有很多人笑,你找到的随机照片很可能除了微笑之外什么都没有。在寻找不同类别之间的平衡,以便找到训练所需的多样性,是你必须学会的一项技能。

尤其是处理历史数据时,你经常会发现它们的质量并不像你希望的那样高,那么你如何从有限的数据中获得最大价值?

提升你的数据

没有银弹可以神奇地让你的数据变得更好,但有一些方法你可以调整你的数据来改善 AI 的训练。

线性色彩

当你拍摄时,你可能会在对数尺度(log)色彩下拍摄,其中尺度代表指数变化。这对于在拍摄时存储大量色彩范围非常出色,但并不适合训练深度伪造。为了从你的训练中获得最佳结果,你希望将你的视频转换为线性色彩空间(其中某些数字的变化是一致表示的)。这实际上并不重要,但所有你的数据和转换后的视频应该是相同的。由于大多数内容是Rec.709,除非你有充分的理由选择不同的色彩空间,否则我们建议你使用它。

作者注记

色彩科学是一个非常稳健的领域,全面考察超出了本书的范围。然而,基本理解可以帮助。Rec.709是许多所谓的色彩空间之一,这是计算机或电视表示色彩的方式。有许多不同的色彩空间,包括SRGBAdobe RGB(以及其他),它们可以显示不同的颜色或以不同的方式表示它们。在深度伪造方面,重要的是在训练和转换时必须保持所有数据在同一个色彩空间。

高动态范围HDR)内容也存在问题——至少在商业发布的内容中是这样。消费者 HDR 技术为了将颜色数据放入帧中,会丢弃大量数据。这意味着在一个场景中看起来较暗的东西实际上可能存储得比另一个场景中看起来较亮的东西还要亮。即使映射回线性范围,它也往往变化很大,这对 AI 进行泛化造成了极大的困难,通常会导致深度伪造模型的失败。处理这个问题的最好方法是避免向 AI 模型提供 HDR 内容。这可能会很复杂,因为有时你能获得的最高质量数据就是 HDR。在这种情况下,了解较低分辨率但颜色空间一致的数据将比高分辨率但颜色不一致的数据产生更好的结果是有用的。

使用 RAW 格式记录的专业相机不会像HDR那样有问题,因为它们保留了来自传感器的确切数据,并且完整的颜色数据仍然可用。

颜色科学很复杂,有时也不直观。成为所有细节的专家并不必要,但了解颜色空间可能引起问题时很有用。

数据匹配

有时候,你可能会在某个主题上对可访问的数据极为有限。在这种情况下,获得一个好的交换可能非常困难。帮助获得所需结果的一种方法是将你的数据尽可能匹配,至少在训练的部分。确保你能够访问的主题与另一个主题的数据尽可能匹配。在单个图像中匹配姿势、表情和照明非常重要。这不必是最终的转换,也不应该是你使用的唯一数据,但拥有匹配的数据有时可以帮助 AI 找到交换两个主题的细节。

在进行数据匹配时,你将希望仅对与你的数据匹配的子集进行非常短时间的训练。你的主题数据集都应该被清理到单个短段,训练应限制在不超过 100 个周期(一个周期是 AI 模型在所有数据上完成一次完整训练)。一旦你用那个子集完成训练,你将希望用完整的数据进行训练。你可能希望用你数据的不同子集重复这个过程几次。

放大

目前 AI 的一个主要趋势是在提升内容。这是一个 AI 的绝佳用途,它可以填补其训练中的缺失数据,特别是时间感知提升器,可以通过跟踪物体跨越多个帧来找到缺失数据以获得更多细节。不幸的是,当用作生成 AI(如 deepfakes)的训练数据时,提升的 AI 数据是有问题的,并且容易导致训练失败。即使是非常好的提升 AI 也有故障和伪影。这些伪影可能对肉眼难以察觉,但 deepfake AI 会寻找模式,并且经常会被伪影绊倒,导致训练失败。

通常,处理提升的最佳方式不是提升训练数据,而是提升输出。这在很多方面都更好,因为它可以同时替换缺失的面部数据并提高输出的分辨率。之所以链式 AI 不会导致失败,是因为与 deepfakes 不同,提升器不是在生成数据上训练的。

话虽如此,有很多技术可以安全地用于提升您的训练数据。更多传统或时间相关的解决方案,不涉及 AI 的,也可以用于提升而不会导致训练失败。使用双三次Lanczos滤波进行提升也是完全可以接受的。

清理噪声或调整颜色也是可以的,并且是提高数据质量的有效方式,特别是如果提取程序无法在部分数据中找到面部时。

摘要

数据对于 deepfakes 至关重要,就像所有 AI 一样。获取最佳数据是一项你必须随着时间的推移来学习的技能。随着你对 deepfakes 越来越有经验,你将在这方面做得越来越好。尽管如此,一些任务可以在不大量投资于过程的情况下学习。清理和组织你的数据很重要——花在这上面的时间可以节省你以后的时间,因为你的 AI 不太可能失败。

录制自己的数据有时是必要的,并且可以给你带来最佳结果,因为这将给你足够的控制来填补缺失的数据或匹配有限的历史数据。当你只有历史数据时,你的选择更有限,可能需要进一步工作来改进你拥有的数据。提升和滤波是可能的,但你必须小心,因为一些技术可能会添加干扰训练的伪影。

最后,数据是训练 deepfake 最重要的部分,因此也是 deepfaker 最重要的工作。如果你想在创建 deepfakes 方面表现出色,你必须花时间和精力学习数据管理的技能。

在下一章中,我们将向您介绍如何使用 Faceswap – 一款免费开源的 deepfake 软件 – 以便您可以生成自己的 deepfakes。

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use条款约束。

第四章:深度伪造工作流程

创建深度伪造是一个复杂的过程。各种软件应用程序中的工具有助于显著减少所需的手动工作量;然而,它们并不能完全消除这一需求。大部分手动工作涉及收集和整理原始材料,以及为最终交换清理数据。

尽管有各种用于创建深度伪造的应用程序,但本章将使用开源软件 Faceswap (www.Faceswap.dev)。创建深度伪造的一般工作流程在各个应用程序之间是相同的,但您会发现细微差别和可用选项在不同软件包之间有所不同。

值得注意的是,Faceswap 在本质上是一个命令行应用程序。然而,它还附带了一个图形用户界面(GUI),该界面作为包装器来启动各种进程。在本章中,我们将使用 GUI 来展示工作流程;然而,这里执行的大多数任务也可以从命令行运行。

本章将涵盖从交换的初始阶段到最终产品的整个深度伪造工作流程。具体来说,以下内容将被涉及:

  • 确定适合交换的候选对象

  • 准备训练图像

  • 训练模型

  • 将训练好的模型应用于执行交换

技术要求

与所有机器学习技术一样,深度伪造可以在具有至少 4 GB RAM 的任何 PC 上创建。然而,强烈建议使用具有 8 GB 或更高 RAM 和 GPU(显卡)的机器。在 CPU 上训练模型可能需要数月才能完成,这并不现实。显卡专门设计用于执行矩阵计算,这使得它们非常适合机器学习任务。

Faceswap 可在 Linux、Windows 和基于 Intel 的 macOS 系统上运行。至少,Faceswap 应该在具有 4 GB VRAM(GPU 内存)的系统上运行。理想情况下,应使用 NVIDIA GPU,因为 AMD GPU 的功能不如其 Nvidia 对手全面,且运行速度较慢。由于 NVIDIA 的专有 CUDA 库被接受为机器学习的行业标准,因此一些 NVIDIA 用户可用的功能对 AMD 用户不可用。具有更多 VRAM 的 GPU 可以运行比小 GPU 更多的较大 Faceswap 模型。

还可以通过租赁云服务(如 Google 的云计算或 Amazon 的 AWS)来远程运行 Faceswap。

本章将不涵盖软件的安装和设置,因为方法会因操作系统而异,详细的安装说明可以在 Faceswap 网站上找到 (forum.Faceswap.dev/viewforum.php?f=4)).

确定适合交换的候选对象

虽然技术上可以将任何面部与另一个面部交换,但要创建一个令人信服的深度伪造,需要关注源面和目标面的属性。根据你希望从你的深度伪造中实现的目标,这可能对你来说更重要或更不重要,但假设你希望创建一个令人信服的交换,你应该注意以下属性。

  • 面部/头部形状:面部的形状是否相似?如果一个面部非常窄而另一个非常圆,那么尽管面部特征将是正确的,但如果最终交换包含一个与你要尝试针对的个人显著不同的头部形状,那么最终交换可能不会特别令人信服。

  • 发际线/发型:虽然可以进行整个头部的交换,但这些通常更难实现,因为头发很复杂,发型也可能有显著变化。你通常会交换面部,但保留原始材料中的头发,因此当你考虑交换时,需要考虑发际线和发型。

  • 肤色:神经网络将在匹配你训练模型时使用的面部之间的肤色方面做一些工作;然而,这只能在一定程度上起作用。由于原始面部的某些属性很可能仍然存在于最终交换中,当它融合到原始框架中时,确保两个面部的自然肤色没有显著差异是很重要的。

一旦你确定了用于创建深度伪造的候选人,就是时候收集数据来训练模型了。

准备训练图像

在本节中,我们将收集、提取和整理图像以训练我们的模型。收集面部数据最好的来源无疑是视频文件。视频只是一系列静态图像,但因为你可以在标准 25 FPS 文件中每秒获得 25 张静态图像,所以它们是一个宝贵且丰富的资源。视频还可能包含比照片更多的自然和多样的姿势,而照片往往是有姿势的,并且表情有限。

视频源应具有较高的质量。最佳的数据来源是高比特率编码的 HD 内容。您应谨慎对待从在线流媒体平台获取的视频内容,因为这些内容往往比特率较低,即使分辨率较高。出于类似原因,JPEG 图像也可能存在问题。神经网络将学会重现它所看到的内容,这包括从低比特率/高度压缩的源中学习压缩伪影。使用现代智能手机或更好的设备拍摄的素材,或从蓝光或 DVD 源中提取的素材,是理想的。但有一点需要注意,那就是您应不惜一切代价避免使用 HDR 素材。HDR 的本质是包含动态范围内的图像。神经网络期望接收到的数据在一致的范围内,因此它们在处理 HDR 数据时会有所困难,而且常常在提供这类数据时无法学习。

您希望尽可能从多个不同的来源收集材料。一个误区是模型是为特定场景训练的。深度伪造所使用的神经网络类型受益于高度多样化的数据。由于神经网络正在寻找为它看到的每个面部编码重要特征,因此尽可能多地提供多样化的数据将使它能够生成更好的编码和更好的特征图。这包括姿势、表情和光照条件的多样性。虽然神经网络会做一些工作来模拟不同的光照条件,但这是为了帮助增强已经多样化的数据,而不是作为缺失数据的替代。神经网络无法创建与之前所见内容显著不同的姿势和表情。

从您的源数据中提取面部

现在您有了多种来源,下一步是从这些来源中提取并对齐(一个将图像中的面部进行归一化的过程)面部,以构建您的训练集。您希望收集 500 到 50,000 张面部,以便对每一边进行训练。数据的多样性比数据量更重要。500 张高度多样的面部将比 50,000 张几乎相同的面部产生更好的结果。

在提取过程中,将创建一个“对齐文件”。此文件(具有.fsa扩展名)包含有关您每个来源中发现的每个面部的信息。考虑到这一点,设置一个项目文件夹结构来存储您的数据是一个好习惯,这样您可以轻松地找到并编辑所需的数据。一个合理的结构可能如下所示:

图 4.1 – 建议的 Faceswap 文件夹结构

图 4.1 – 建议的 Faceswap 文件夹结构

对于模型的每一侧(AB),我们都在创建一个文件夹来存储源视频以及相关的生成对齐文件(Videos),以及一个用于存储提取面部的Faces文件夹。如果您将图像作为面部源进行提取,则可以添加一个Images文件夹。

将视频和图像源文件复制到相应的文件夹,并启动 Faceswap 应用程序:

图 4.2 – Faceswap GUI

图 4.2 – Faceswap GUI

根据您当前执行的任务,应用程序被划分为不同的部分。在此阶段,我们正在提取面部,因此请确保已选择提取选项卡。

这里有许多选项可用,但我们只关注那些用于生成训练集所必需的选项。为了简洁,将跳过可选或不必要的选项,但您可以在 GUI 中查看工具提示以获取更多信息。

小贴士

Faceswap 内置了工具提示,解释每个选项的功能。将鼠标悬停在条目上以访问相应的工具提示。

数据

此部分是输入我们打算从中提取面部以及我们希望提取面部的位置的源材料的位置:

  • 输入目录:源视频文件或包含您希望提取的面部的图像文件夹的位置。点击右侧的按钮将启动文件浏览器,以便轻松导航到正确的位置。如果从视频文件中提取面部,则选择左侧图标;如果从图像文件夹中提取面部,则选择右侧图标。

  • 输出目录:识别的面部应提取到的位置。这应该是您在创建项目文件夹结构时设置的Faces文件夹中的新文件夹。

图 4.3 – 面部提取的数据部分

图 4.3 – 面部提取的数据部分

接下来,让我们看看我们可以使用的插件。

插件

插件部分是选择我们将用于在图像中识别面部以及识别面部关键地标和应用于提取面部任何基于神经网络的掩码的插件的区域:

  • 探测器:探测器用于识别每张图像中的面部。截至编写时,最稳健的探测器是S3Fd(基于论文《单次射击尺度不变面部探测器》:https://arxiv.org/abs/1708.05237)。然而,它非常消耗资源,运行时至少需要 3 GB 的 GPU 内存。由于其复杂性,它也在 CPU 上运行得非常慢。但是,如果您有可用的资源,这就是您应该使用的探测器。否则,应使用MTCneural 网络arxiv.org/abs/1604.02878)探测器,它将使用更少的资源,并且在 CPU 上运行得更快。

  • 对齐器:负责识别关键面部地标。这些地标用于对齐检测到的任何面部,以便在训练期间输入模型时保持一致性。FAN (arxiv.org/pdf/1703.07332.pdf) 是最佳对齐器,如果可能的话,应选择此选项。然而,它在 CPU 上运行较慢,在这种情况下,可使用CV2-D神经网络对齐器。虽然这将在 CPU 上运行得更快,但它的性能远不如 FAN。

  • 遮罩器:在提取面部时,还可以使用神经网络来对感兴趣的区域进行遮罩。具体来说,我们只对在提取图像的面部区域进行模型训练感兴趣。包含在提取图像中的面部背景只会给模型增加噪声,这有利于排除。默认情况下,总是包括两个遮罩,基于对齐器生成的地标数据。基于地标的数据遮罩对于许多用例来说很好,但它们有限,因为它们不包括在遮罩区域内的额头(它们在眉毛上方裁剪)。基于神经网络的遮罩通过使用 AI 在提取面部上生成遮罩来尝试解决这个问题。通常,BiSeNet-FP遮罩效果最佳。

值得注意的是,遮罩不需要在提取时创建。Faceswap 包括一个工具,在提取完成后向训练集中添加遮罩。这可能是有益的,因为通常你不会想在提取的面部正确且正确对齐后才生成基于神经网络的遮罩。请注意,添加的基于神经网络的遮罩越多(可以选择多个遮罩),提取过程将需要更长的时间。

图 4.4 – BiSeNET-FP(基于神经网络)遮罩(顶部)和组件(基于地标)遮罩(底部)的并排比较。左侧图像是原始对齐面部,右侧图像是生成的遮罩,中间图像是应用遮罩的对齐面部

图 4.4 – BiSeNET-FP(基于神经网络)遮罩(顶部)和组件(基于地标)遮罩(底部)的并排比较。左侧图像是原始对齐面部,右侧图像是生成的遮罩,中间图像是应用遮罩的对齐面部

  • 归一化:当对齐器正在寻找识别关键地标时,对插件输入的图像进行归一化可能会有所帮助。通常,直方图归一化Hist)或对比度受限自适应直方图均衡化CLAHE)效果最佳,但这将取决于原始材料。

  • 重新填充/旋转图像:为了生成训练集,这些选项不是必需的,应保留其默认值(分别为1和空白)。

图 4.5 – 面部提取的插件选择选项

图 4.5 – 人脸提取的插件选择选项

人脸处理

人脸处理是在图像中识别人脸之后应执行的任务。这里需要关注的唯一选项是最小尺寸。检测器可能会找到假阳性(检测器认为是人脸但实际上不是的情况)。此选项允许你丢弃不符合此最小阈值(以像素为单位,从检测到的脸的角到角测量)的人脸。这里指定的值将根据输入帧的大小以及你感兴趣的人脸在图像中占据的多少而变化。无论如何,将其设置为低值可以帮助在下一阶段筛选数据。

本节中的另一个选项是人脸过滤器。通常建议避免使用过滤器,因为它在正确识别人脸方面可能不太准确,并且会显著减慢提取过程。

图 4.6 – 人脸提取的人脸处理选项

图 4.6 – 人脸提取的人脸处理选项

输出

最后关注的区域是输出部分。需要修改的默认选项只有每 N 提取。虽然你需要大量的图像来训练模型,但多样性更为重要。当使用视频作为训练图像的来源时,每一帧与其相邻帧极为相似。为了减少将要提取的相似人脸数量,可以跳过一些帧来解析人脸。这里设置的数字将取决于视频的帧率以及你打算从中提取人脸的源数量。通常,每秒 2 到 8 帧的视频是一个好的目标数字(例如,对于 30 fps 的视频,每 N 提取的值为 6 将在每秒视频中提取 5 帧的人脸)。

图 4.7 – 人脸提取的输出选项

图 4.7 – 人脸提取的输出选项

提取

现在所有选项都已设置,可以按下提取按钮。这将把所有发现的人脸提取到指定的文件夹中,并生成相应的对齐文件。这个过程所需的时间将取决于可用硬件、源材料的长度以及所选的插件。

筛选训练图像

一旦提取了面部,就需要对数据进行整理。用于识别、提取和对齐面部的神经网络做得很好,但它们并不完美。除了正确检测和对齐的面部外,很可能还会收集到相当数量的不可用数据。这可能包括目标之外的面部、假阳性(神经网络认为是面部但实际上不是的面部部分)以及未正确对齐的面部(被正确识别但对齐器未能正确对齐的面部)。

图 4.8 – 不对齐的面部集中示例

图 4.8 – 不对齐的面部集中示例

检查包含大量不可用面部的文件夹,一开始可能看起来清理和整理训练图像是一项庞大的任务。幸运的是,可以再次利用神经网络使这项工作变得容易得多。

排序面部

面部将按照帧顺序提取 – 即,它们将按照在源视频或图像中发现它们的顺序存在于输出文件夹中。Faceswap 包含多种排序机制,可以将这些面部按顺序排列,以便更容易地进行修剪,这可以通过选择 工具 选项卡,然后选择 排序 子选项卡来访问:

图 4.9 – Faceswap 工具部分中的排序工具

图 4.9 – Faceswap 工具部分中的排序工具

最强大的排序方法,无疑是通过面部排序。这使用了由牛津大学视觉几何组研究人员开发的神经网络VGG Face 2 (arxiv.org/abs/1710.08092)。Faceswap 利用这个网络将相似的面部聚类在一起,使得数据处理变得容易得多。

在 Faceswap 的 排序 部分中,应选择以下选项:

  • 输入:包含要整理的提取面部的文件夹

  • 排序方式:选择面部

  • 最终步骤:重命名(面部将就地排序,文件名将被重命名)

所有其他选项都可以保留默认值,如下面的截图所示:

图 4.10 – Faceswap 排序工具中的选择选项

图 4.10 – Faceswap 排序工具中的选择选项

点击排序按钮以启动排序过程。根据您的设置和要排序的面部数量,这可能需要一些时间。

删除面部

一旦排序过程完成,可以使用操作系统的标准文件管理器浏览文件夹并删除任何错误的面部。排序过程会将所有相似的面部分组在一起,这使得这项任务变得简单得多,因为所有误识别的面部都可以批量选择并删除。只需确保文件夹的排序顺序是按文件名排序。

在这个阶段,移除任何不适合训练的高质量面部图像也是一个好主意。这包括那些虽然是目标个体的图像,但在某些方面不符合标准的情况——例如,如果面部在框架内没有正确对齐,面部被严重遮挡,或者图像质量太低。通常,训练图像应该是高质量的。并非所有低质量/模糊的图像都需要从训练集中移除,因为神经网络也需要知道如何重新创建这些低质量/分辨率的图像;然而,它们应该只占训练集的一小部分。

现在文件夹中只包含适合训练的面部图像,最佳实践是清理对齐文件。这是从该文件中删除您刚刚删除的面部图像的过程。这使得我们可以回到视频源和对齐文件,重新提取面部图像,同时避免再次排序数据的需求。这一动作还有一个优点,即它还将所有面部重命名为它们的原始文件名。

再次强调,Faceswap 有一个工具可以帮助完成这项任务——具体来说,导航到工具标签,然后是对齐子标签:

图 4.11 – Alignments 工具在 Faceswap 中的位置

图 4.11 – Alignments 工具在 Faceswap 中的位置

对齐工具允许对对齐文件执行多个操作。感兴趣的作业是移除面部,它检查一个面部文件夹,并从对齐文件中移除您已从对齐文件中删除的面部。应选择以下选项:

  • 作业:选择移除面部

  • .fsa对齐文件

  • 面部文件夹:浏览到包含您精选面部集的位置,并选择文件夹

所有其他选项都可以保留其默认值:

图 4.12 – Faceswap 对齐工具中的选择选项

图 4.12 – Faceswap 对齐工具中的选择选项

按下对齐按钮。将对齐文件进行备份,然后过程将删除文件中未出现在面部文件夹中的所有面部。最后,面部将被重命名为它们最初提取的名称。

整理训练图像

现在训练源已经整理完毕,可以将图像整理成最终的训练集。

小贴士

如果在提取过程中没有生成所需的掩码,现在也可以使用掩码工具(工具标签 | 掩码子标签)将其添加到面部。这应该在整理最终训练集之前完成。

这就像将每个源提取的面部内容的全部放入同一个文件夹中(一个文件夹用于A面,另一个文件夹用于B面)。Faceswap 训练这些图像所需的所有信息都存储在 PNG 图像的 EXIF 头部中。

可能需要进行一些最终的整理,以将训练图像的数量减少到可管理的规模,但模型每一边大约有 500 到 50,000 张图像是合理的。

现在数据已经收集和整理,是时候训练模型了。

训练模型

这个过程需要最少的手动干预,但在计算时间上会花费最长。根据所选模型和硬件,这个过程可能需要从 12 小时到几周不等才能完成。

建议在第一次创建深度伪造时使用相对轻量级的模型。创建交换操作相当复杂,而了解哪些有效哪些无效则需要经验。虽然 Faceswap 提供了多个模型,但开始时使用 原始轻量级 模型将允许你相对快速地评估交换的性能,而不会必然给出最佳可能的结果。

Faceswap 为模型和训练提供了许多可配置的设置。这些设置在应用程序的 设置 菜单中可用。要涵盖所有这些设置超出了本指南的范围,因此除非另有说明,否则将使用默认设置。

设置

导航到 Faceswap 应用程序的 训练 选项卡:

图 4.13 – Faceswap 的训练选项部分

图 4.13 – Faceswap 的训练选项部分

面部

这部分内容是告知我们的训练图像存储的位置。如果你遵循了提取和整理步骤,那么这些面部图像将存在于你的项目文件夹中,每一边都有一个单独的文件夹:

  • 输入 A:包含模型 A 边提取的面部文件夹的路径(即要从最终视频中移除的原始面部)。

  • 输入 B:包含模型 B 边提取的面部文件夹的路径(即你希望转置到最终视频中的面部)。

图 4.14 – 训练模型的面部选项

图 4.14 – 训练模型的面部选项

模型

模型部分是指导 Faceswap 使用哪个模型,模型应存储在哪里,以及在运行时执行任何特定于模型的操作:

  • 模型目录:模型应存储的文件夹,或者如果你正在恢复一个现有的模型,那么应指向包含要恢复的模型的文件夹。

如果你正在开始一个新的模型,那么这个位置在硬盘上不应预先存在。当模型创建时,这里指定的文件夹将被创建并填充上 Faceswap 需要跟踪训练所需的模型和相关文件。

如果你正在恢复之前创建的模型,那么这个位置应指向最初设置模型时创建的文件夹(由 Faceswap 创建并包含相关模型文件的文件夹)。

  • 训练器:Faceswap 有多个模型可供选择(出于历史原因命名为训练器)。这些模型在设置菜单中可配置性或多或少,取决于所选的模型。如前所述,如果您是初学者,那么建议使用原始轻量级模型。

如果您正在开始一个新的模型,那么这里选择的模型将用于该模型所有未来的训练会话。一旦创建,就无法更改模型类型。

在此部分中的其他选项在您的第一个模型中可以忽略,尽管随着您使用软件经验的增加,它们可能变得更加相关。与所有选项一样,工具提示会告诉您这些附加选项的功能。

图 4.15 – Faceswap 中训练的模型选项

图 4.15 – Faceswap 中训练的模型选项

训练

这些选项与模型应该如何训练有关:

  • 批量大小:一次通过模型传输的人脸数量。通常,较大的批量大小会导致更高的训练速度;然而,较大的批量大小意味着模型泛化能力更强。增加批量大小只有在一定范围内才是合理的。超过 128,模型将开始难以从每个批量中获得有用的信息。

批量大小也受 VRAM 限制。对于更复杂的模型,您可能别无选择,只能使用较小的批量大小,显然,GPU 上可用的 VRAM 越少,您的限制就越大。

  • 迭代次数:除非希望模型在特定次数的迭代后停止,否则可以将其保留为默认值。何时停止模型取决于经验,并由输出质量决定,因此它永远不会在“设定次数的迭代”后停止。

  • 分布式:此选项仅适用于多 GPU 用户。它允许使用多个视频卡,通过在多个设备上分割批量来加速训练。

小贴士

在较高的批量大小下开始训练模型以获得速度优势是有益的,然后在训练后期减少批量大小以获得深入细节的好处。

图 4.16 – Faceswap 中的训练选项

图 4.16 – Faceswap 中的训练选项

增强

增强部分允许您启用或禁用某些图像增强(这是神经网络人工增加训练图像数量的方式)。在开始一个新的模型时,这些都应该被禁用(所有增强都是激活的)。在训练会话的后期(当人脸变得可识别且更详细时),可能希望关闭一些这些增强:

  • 到特征点的扭曲:这只是另一种扭曲技术。没有确凿的证据表明启用或禁用此选项会有任何真正的区别,因此建议始终将其禁用。

  • 无翻转:人脸大致是对称的。神经网络通过翻转大约 50%的图像水平方向来利用这一知识。对于几乎所有用例,这是可以的,并且所有图像都可以随时翻转。然而,在人脸一侧有独特细节(例如,美人痣)的情况下,当你在训练预览窗口中看到人脸开始成形时,应启用此选项。

  • 无增强颜色:颜色增强有助于神经网络通过人工着色和改变它所看到的图像的其他视觉属性,来匹配 A 面和 B 面之间的颜色和光照。通常,这总是期望的,并且应该保持开启状态,但对于某些用例,可能希望禁用此增强。

  • 无扭曲:这是本节中可能最重要的选项。扭曲对于神经网络的学习至关重要。所有模型都必须在启用扭曲的情况下开始(未能这样做将不可避免地导致模型质量低下,或者更糟,模型崩溃)。然而,在训练的后期,尤其是在尝试深入更细致的细节时,这种扭曲实际上对模型的训练有害,因此应选择禁用扭曲的选项。

图 4.17 – 训练模型的增强部分

图 4.17 – 训练模型的增强部分

  • 一个经验法则是,如果人脸在相当长的时间内没有变得更清晰,那么可能就是时候禁用扭曲了。清晰地看到牙齿和眼睛反光是一个很好的指标。重要的是要注意,禁用扭曲过晚几乎是不可能的,但确实有可能过早地禁用扭曲,因此应谨慎行事。

作者注

颜色增强和图像扭曲都是从数据中获得更多收益的无价方法。就像本节中的其他增强一样,它们稍微改变了图像,作为有效获取 AI 模型之前未见过的图像的一种方式。

颜色增强通过稍微改变图像的颜色来实现。这为模型提供了新的颜色来处理。这也帮助模型应对可能从数据中缺失的新光照情况。

扭曲通过稍微修改图像中人脸的形状来实现。如果数据中某些表情比较少见,这会有所帮助。它还帮助确保解码器从记忆中构建人脸,而不仅仅是复制原始图像。

启动和监控训练

一旦输入了模型配置并调整了所有设置到适当的值,就可以按下训练按钮来启动训练会话。训练可能需要很长时间才能完成,并且没有数学或数值指标可以知道何时模型完成训练。知道何时模型不太可能再进一步主要依赖于经验;然而,有一些指标可以帮助我们确定何时应该停止训练。

预览

模型的进步最重要的衡量标准可能是预览图像本身。在每次保存的迭代中,都会生成一系列预览图像,以便可视化模型进展情况。

图 4.18 – Faceswap 的 GUI 界面,右侧是预览窗口

图 4.18 – Faceswap 的 GUI 界面,右侧是预览窗口

这个预览包含从训练集中随机选取的 28 张人脸(每侧 14 张)。对于每个训练图像,首先展示原始图像,然后展示 AI 尝试重新创建原始人脸的图像。最后一张图像展示 AI 尝试将原始人脸与另一侧的身份进行交换的尝试。

当训练开始时,这些图像可能看起来像单一颜色或模糊的、大致呈人脸形状的图像;然而,它们会相当快地变成可识别的人脸,并且随着时间的推移逐渐改善。值得注意的是,模型改进并非线性。虽然最初人脸会迅速改善,但这种改善会逐渐减慢,直到迭代之间没有明显的差异。然而,在一段时间内,模型会持续改善。在比较预览时,比较相隔 10,000 到 100,000 次迭代的图像改进并不罕见。

损失

我们可以采用的另一个指标是每次迭代的损失。每当一批数据通过模型处理时,神经网络会为自己评分,以评估它认为重建图像的效果如何。虽然损失值可能会在批次之间大幅波动,但随时间推移,平均值会下降。需要注意的是,损失的真正值并不重要。事实上,这个值本身实际上是没有意义的。我们唯一需要关心的问题是这个值是否在下降。这有几个原因;首先,不同的损失函数会产生不同的数字,这些数字之间是不可比较的。其次,给出的损失值实际上并不代表对我们有用的任何东西的评分。损失是由神经网络认为它重建的AB脸的效果来生成的。它是通过查看原始脸和它的重建版本,并根据重建的质量给自己评分来做到这一点的。然而,这个评分对我们来说并没有用。我们希望看到的是基于神经网络如何将一张脸与另一张脸交换的评分。由于没有现实世界中人脸交换的真实例子,这是一个无法实现的指标,所以我们只能使用损失值来进行人脸重建而不是人脸交换。

图表

虽然损失本身在任何给定时间都不是一个有用的衡量标准,但它在时间上的趋势是。最终,如果损失在下降,那么模型正在学习。模型训练得越久,就越难确定损失是否实际上仍在随时间下降。Faceswap 收集通过模型传递的每个批次的日志数据,形式为TensorFlow 事件日志。这些日志存储在与模型相同的文件夹中,可以用于在 Faceswap 的 GUI 中可视化数据,或使用TensorBoard(TensorFlow 的可视化工具包)进行分析。

要分析现有模型的学习进度,导航到模型文件夹中的state.json文件。一旦数据被解析,每个训练会话的会话摘要统计信息将显示出来:

图 4.19 – 一系列 Faceswap 训练会话的统计数据

图 4.19 – 一系列 Faceswap 训练会话的统计数据

点击任何会话行旁边的绿色图形图标,将显示该会话的训练图,显示每次迭代中的损失:

图 4.20 – 显示 Faceswap 训练会话随时间损失变化的训练图

图 4.20 – 显示 Faceswap 训练会话随时间损失变化的训练图

对于长时间的会话,由于数据量庞大和范围广泛,很难确定损失是否仍在下降。幸运的是,可以通过选择屏幕右下角的缩放到矩形按钮并选择感兴趣的区域来放大图表的选定范围,从而获得更好的了解。在这个例子中,我们将放大最后 100,000 次迭代的训练,并确保我们正在查看损失值的滚动平均值。正如我们所看到的,损失仍在明显改善。

图 4.21 – 训练会话最后 100,000 次迭代的放大视图

图 4.21 – 训练会话最后 100,000 次迭代的放大视图

手动干预

虽然训练模型主要是一个“点火后忘记”的任务,但在训练的后期,采取一些步骤来提高最终输出是有帮助的。这些步骤并非必需,但它们可以帮助提高最终产品。为了使任何这些动作生效,需要停止模型,调整相关设置,然后才能继续训练模型:

  • 禁用变形:如前所述,变形对于模型学习至关重要;然而,当模型进入训练的中后期阶段时,变形增强实际上可能会损害模型的图像保真度和清晰度。启用无变形选项是进行高质量最终交换的良好实践。一般来说,这个选项不应在选择之前至少完成 50%的总训练,或者你能够清楚地看到定义明确的特点,例如单个牙齿和眼睛反光。禁用变形过晚是非常困难的,但过早禁用变形却非常容易。如果预览仍然看起来像是在改善,那么可能还太早禁用变形。

  • 降低学习率:当模型进入训练的后期阶段时,降低学习率是有帮助的。这可以帮助模型深入到更细微的细节中。通常的做法是稍微降低学习率,然后继续训练,再降低一些,然后继续训练,重复这个循环,直到你对结果满意为止。

  • 拟合训练:一种可以帮助的技术是将数据拟合到将要交换的实际场景中。虽然不建议仅使用最终交换中会出现的数据来完全训练模型,但使用这些数据可以用来微调其他已经完全训练好的模型。

当你满意地认为你已经尽可能多地训练了模型时,是时候将训练好的模型应用到源视频文件上以交换面部了。

应用训练好的模型进行交换

一旦模型完成训练,就可以用它来交换任何包含要交换个体的视频中的面部。成功执行交换需要三个项目——一个视频/一系列图像、一个训练模型以及要转换的媒体的对齐文件。前两项是显而易见的;对齐文件是我们需要创建的一项。

对齐文件

对齐文件是针对 Faceswap 定制的文件,具有.fsa扩展名。对于要转换的每个媒体源,都应该存在这样一个文件。它包含有关视频文件中面部位置的信息,对齐信息(每个帧中面部的方向),以及每个帧的任何相关掩码。

生成对齐文件相当简单。实际上,当我们构建训练集时,至少已经生成了一个。生成训练数据和生成对齐文件的过程是相同的,只是有一些变化。因此,许多步骤对你来说都很熟悉,因为它们与我们从源数据中提取面部部分中执行的是相同的步骤。本节最显著的区别是,需要对媒体源中的每个单独帧生成对齐信息,而在生成训练集时,通常只从源材料中提取一部分帧的面部图像。

请参考从源数据中提取面部以获取关于在运行用于生成训练数据的提取和运行用于执行交换的提取之间常见选项的更详细说明。

在 Faceswap 应用程序中,选择提取选项卡,然后按照以下部分进行操作。

数据

本节是生成对齐文件所需源材料的位置,以及面部将被提取到的输出文件夹。这里生成的面部在 Faceswap 过程中根本不使用,但它们对于清理我们的对齐文件非常有用:

  • 输入目录: 我们打算在其中交换面部图像的媒体位置

  • 输出目录: 识别出的面部将被提取到的位置

图 4.22 – Faceswap 提取设置中的数据选项

图 4.22 – Faceswap 提取设置中的数据选项

插件

选择的插件可能与生成训练集时选择的相同,因此请参考上一节以获取有关选项的更多信息。在本节中,可能只有一个选项在提取对齐文件时会发生变化:

  • 重新输入:由于提取过程没有理解时间一致性(即每个帧与前一帧和后续帧的关系),这可能导致最终交换中的“抖动”对齐。这意味着最终交换中的面部在帧与帧之间移动一小段距离。虽然这对训练集来说并不重要,但在生成最终交换文件时却很重要。

重新输入是一种机制,通过将检测到的面部多次输入到对齐器中并取结果的平均值来帮助防止这种抖动。值得注意的是,重新输入量的每次增加都会减慢提取速度,因为数据需要多次通过该过程。此数值设置得越高,最终输出应该越平滑,但这是递减的回报。设置值过高不太可能带来任何明显的益处,但运行提取的时间会显著增加。

将其设置为能带来满意结果且运行速度可接受的价值。任何大于 1 的值都应该比不重新输入提取时提供更好的结果。重新输入值为 8-10 将可能使输出接近最佳。

图 4.23 – Faceswap 提取设置中的插件选项

图 4.23 – Faceswap 提取设置中的插件选项

面部处理

与提取训练面部一样,本节中可能需要调整的唯一选项是最小尺寸。此处指定的值通常对应于源材料中面部的大小,但无论怎样,将其设置为低值都可以帮助去除一些明显不是有效面部的误报。

图 4.24 – Faceswap 提取设置中的面部处理选项

图 4.24 – Faceswap 提取设置中的面部处理选项

输出

最后,应审查和更新输出部分。需要从用于提取训练集的设置中修改的唯一选项是每 N 个提取。确保每个帧在生成的对齐文件中都有一个对应的条目至关重要,因此应将其设置为1

图 4.25 – Faceswap 提取设置中的输出选项

图 4.25 – Faceswap 提取设置中的输出选项

提取

一旦锁定了适当的设置,请按提取按钮,以生成对齐文件并将找到的面部提取到指定的文件夹中。这需要的时间将取决于可用硬件、源材料的长度以及已设置的重新输入值。

清理对齐文件

类似于我们收集面部以构建训练集时,提取过程会对识别面部做相当好的工作,但不会做到完美,因此现在需要一些手动处理来清理对齐文件。这个过程与整理训练图像部分相同,但增加了一个额外步骤,因此请遵循该部分中的步骤来执行对齐文件的初步清理。一旦从对齐文件中移除了不需要的面部,可以删除提取面部的文件夹。面部实际上并不用于转换过程;它们只是用作清理对齐文件的一种机制。

到目前为止,我们应该有一个目标面部交换的源视频和一个包含该文件中面部位置信息的对应对齐文件。在这个阶段进行转换是完全可行的,但为了获得最佳结果,还需要进行另一个步骤——修复对齐文件。

修复对齐文件

虽然我们已经从对齐文件中移除了不需要的面部、误报和任何明显错位的面部,但仍需进一步清理文件以进行最终转换。主要原因是为了修复以下场景中的帧:

  • 识别出多个面部。有时,检测器会在一个帧中找到两个面部,但对齐器会对同一个面部进行两次对齐。这种情况通常发生在两个面部在帧中彼此靠近时。

  • 面部对齐不正确。有时,当扫描图像文件夹时,面部可能看起来对齐正确,但检查地标将证明并非如此。这些面部有时可以正确转换,但通常这种错位会导致这些帧的交换变得混乱(交换看起来不太正确,可能看起来模糊,或者可能在帧之间闪烁)。

  • 未识别到面部。确保所有需要交换的面部都已识别,否则,原始面部而不是交换的面部将出现在那些帧中。

  • 没有正确检测到面具。根据源帧的条件,一些基于神经网络的 masks 可能没有正确检测到,因此需要修复。根据面具的渲染方式,一个不正确的面具可能意味着原始面部的一部分会显示出来,或者背景帧的部分渲染不正确。

再次强调,Faceswap 提供了工具来简化此过程——特别是手动工具,它允许在原始帧的上下文中可视化和编辑对齐/masks。

要启动手动工具,请导航到工具标签页,然后是手动子标签页:

图 4.26 – Faceswap 的 GUI 中手动工具的位置

图 4.26 – Faceswap 的 GUI 中手动工具的位置

假设要转换的视频的对齐文件位于默认位置,那么启动手动工具时只需要提供一个参数,即要转换的源视频/图像文件夹的位置。在框中指定此位置,然后点击手动按钮以启动工具。

图 4.27 – 启动 Faceswap 手动工具的数据选项

图 4.27 – 启动 Faceswap 手动工具的数据选项

工具加载后,您将看到一个主要的“视频”窗口,显示正在处理的数据源,以及一个次要窗口,显示对齐文件中存在的所有面孔。

图 4.28 – Faceswap 手动工具显示顶部的视频窗口和底部的人脸查看器

图 4.28 – Faceswap 手动工具显示顶部的视频窗口和底部的人脸查看器

左上角的按钮允许您对对齐执行不同的操作,因此将鼠标悬停在工具提示上以查看可用选项:

图 4.29 – Faceswap 手动工具中的编辑选择按钮

图 4.29 – Faceswap 手动工具中的编辑选择按钮

另一个值得关注的地方是过滤器。过滤器是一个下拉列表,位于帧和面孔窗口之间,允许您根据某些标准过滤工具中显示的帧和面孔:

图 4.30 – Faceswap 手动工具中的人脸过滤器选项

图 4.30 – Faceswap 手动工具中的人脸过滤器选项

最后,人脸窗口的左侧也有一些按钮,这些按钮允许您切换人脸区域中显示的人脸特征网格和选定的面具:

图 4.31 – Faceswap 手动工具中的人脸查看器按钮

图 4.31 – Faceswap 手动工具中的人脸查看器按钮

移除多个面孔

要从包含多个面孔的帧中移除任何额外的面孔,请选择多个面孔过滤器。此时,主视频窗口将只显示包含多个面孔的任何帧。如果顶部窗口中没有帧显示,底部窗口中也没有面孔显示,那么没有包含多个面孔的帧,您可以继续进行下一步操作。

有时知道要移除哪个面孔并不明显,因此请按人脸窗口左侧的显示特征网格按钮以显示特征叠加。如果没有任何面孔明显错位(当显示的特征网格与面部特征不对应时),则可以删除多个面孔中的任何一个;否则,尝试删除与面部特征对应最少的面孔。

有几种方法可以从框架中删除脸部。可以通过在主视频窗口中悬停在不想要的脸上并按 Del 键来删除,或者通过在视频或脸部窗口中右键单击不想要的脸上并选择 删除脸部。当一个框架不再包含多个脸部(框架中只剩下一个脸部)时,该框架的脸部将从脸部窗口中移除。使用此机制,通常最快的方法是右键单击,并在脸部窗口中选择 删除脸部,直到没有脸部为止。

一旦清理了所有包含多个脸部的框架,点击 保存 图标以将更改保存到对齐文件中。

修复错位脸部

手动工具内置了错位检测功能。它并不完美,但它有助于识别和修复最明显错位的脸部。虽然检测可以找到明显错位的脸部,但它不会找到脸部地标相对于彼此的位置正确,但与底层脸部不对应的情况。

选择 错位脸部 过滤器以仅显示检测到错位脸部的帧和脸部。过滤器列表框旁边将出现一个滑块来控制距离:

图 4.32 – 选择错位脸部的距离滑块

图 4.32 – 选择错位脸部的距离滑块

这是每个脸部内的特征点需要离“平均脸部”多远才能被认为是错位的。低值将限制较少,因此可能包含正确对齐但角度/姿势更极端的脸部。一般来说,6 到 10 之间的距离效果相当好。10 的距离应该只显示错位脸部。6 的距离可能会显示错位脸部和更极端姿势的混合,但它会捕捉到高值会错过的错位脸部。可以更容易地设置一个更高的距离(例如 10),并修复出现的错位脸部。然后,将距离设置为 8 并重复此过程,继续逐步降低,直到过滤器没有捕捉到足够多的错位脸部与更极端姿势的脸部相比。

无论已设置的距离是多少,要修复错位脸部,应采取以下行动:

  1. 通过切换脸部查看器左侧的地标按钮来启用脸部查看器的地标网格。

  2. 点击一个具有错位地标的脸部。

  3. 在主框架编辑器中,确保已选择边界框编辑器。此编辑器允许控制脸部周围的蓝色框。这是在提取阶段由脸部检测器捕获的“检测到的脸部”框。调整此框将更新提供给对齐器的脸部,并计算新的地标。继续调整框,直到地标正确对齐。

  4. 如果地标没有对齐,可以在右侧设置框中切换不同的归一化方法选项。不同的方法在不同情况下效果更好或更差,但直方图CLAHE通常能返回最佳结果。

  5. 一些面部可能很固执(困难的角度、遮挡或不良照明)。在这些情况下,对齐器可能几乎无法检测到地标。在这种情况下,可以使用几个其他编辑器:

    • 提取框编辑器:此编辑器显示一个绿色框,对应于如果运行面部提取将提取的框架区域。可以移动、调整大小和旋转此提取框,这将影响提取框内地标的位置。可以利用这一点通过从上一帧或下一帧复制地标(假设地标在帧之间没有太大变化——例如场景变化)并快速调整提取框以适应当前框架来快速对齐面部。

    • 地标点编辑器:此编辑器允许操作 68 个地标中每个单独点的位置。这种细粒度控制很少需要,但如果需要,它就存在。

一旦明显错位的地标被修复,点击保存按钮以更新对齐文件中的更改。

添加缺失面部

一些帧可能没有在应该出现面部的地方识别到面部。这种情况最常见的原因是检测器确实找到了面部,但对齐器未能正确对齐它,然后在排序过程中删除了面部。同样,手动工具有一个过滤器可以帮助解决这个问题。

选择无面部过滤器以过滤顶层窗口,只显示那些没有面部出现的帧。对于这个特定的过滤器,底层窗口将保持为空。导航视频,直到到达包含未检测到面部帧的位置,并确保已选择边界框编辑器。

可以通过在框架内点击面部来创建地标,或者从上一帧或下一帧复制并修改。然后可以像上一步一样编辑边界框,同时要注意关于难以处理的面部的注意事项。

当所有缺失面部帧都已被修复后,点击保存按钮以将更改保存到对齐文件中。

最终对齐修复

一旦修复了明显的缺失和错位面部,就到了对对齐文件进行任何最终修复的时候了。这就像在面部查看器中滚动所有面部并修复任何仍然错位的面部一样简单。面部查看器窗口可以扩展以在单个屏幕上显示更多面部,然后可以使用页面向上/向下按钮逐页滚动面部。当发现错位面部时,可以点击它,然后可以使用边界框编辑器将其正确对齐。

最后,一旦所有面部都被检查并修复,请按保存按钮以将最终更改保存到对齐文件。

现在对齐文件已经修复,您可以关闭手动工具。

重新生成面具

如果要使用基于神经网络的 masks 进行交换,那么在编辑了对齐数据后的任何面部都需要重新生成这些 masks。原因是,从地标生成的对齐面部用于生成输入到面具模型的面部。一旦这些地标被编辑,面具就无效了,因此当对齐更改时,这个过程会自动删除这些无效的面具。

再次强调,Faceswap 提供了一个工具来为现有的对齐文件添加面具——名为面具工具的适当工具。此工具可用于生成对齐文件中之前不存在的面具,重新生成所有面具,或者只为缺少指定面具的脸生成面具:

  1. 导航到工具标签,然后是面具子标签:

图 4.33 – 面具工具在 Faceswap 的 GUI 中的位置

图 4.33 – 面具工具在 Faceswap 的 GUI 中的位置

  1. 数据部分,将视频文件的路径添加到输入字段以重新生成面具,以及相应的对齐文件到对齐字段。由于要处理的源是最终要交换的帧,请确保在输入类型下选择

图 4.34 – Faceswap 的面具工具的数据设置

图 4.34 – Faceswap 的面具工具的数据设置

  1. 过程部分,选择要填充到对齐文件中的面具到面具器。在处理下,如果已经生成了面具且目标是重新填充与已经修复对齐的面部关联的面具,请选择缺失;否则,选择所有以生成对齐文件中每个面部的面具:

图 4.35 – Faceswap 的面具工具的过程设置

图 4.35 – Faceswap 的面具工具的过程设置

  1. 输出部分仅用于可视化面具,没有实际用途,因此可以忽略。

  2. 按下面具按钮以生成缺失的面具并将它们保存到对齐文件。

修复面具

最后一个可选步骤是修复生成的面具。与面部对齐类似,生成面具的神经网络很好,但它们通常并不完美。这可能是由于多种原因,例如照明条件和图像质量。特别是,任何面具器都无法很好地处理面部前的障碍物,因此这些需要手动编辑。

这应该是对对齐文件执行的绝对最后一步操作。对对齐文件中地标数据进行的任何编辑都将从文件中移除任何神经网络掩码,并用最新的地标数据覆盖任何编辑过的基于地标的数据掩码,从而破坏任何已执行的手动编辑。

用于修复对齐的手动工具也可以用于修复掩码:

  1. 通过选择 工具 选项卡,然后选择 手动 子选项卡来启动手动工具,并像之前一样启动。

  2. 从帧查看器旁边的按钮中选择 掩码编辑器 按钮,然后从右侧选项面板中选择要编辑的掩码类型。

  3. 按下位于面部查看器旁边的 掩码显示 切换按钮以在面部窗口中显示所选掩码。

  4. 在面部窗口中滚动,寻找需要修复的掩码。如果发现需要编辑的面部,可以点击它将相关帧带到帧查看器中。然后可以使用 画笔橡皮擦 工具来绘制或擦除所需的掩码区域。

一旦所有掩码都已修复,请按 保存 按钮将掩码编辑保存到对齐文件中。

使用预览工具

现在可以处理交换并查看最终输出。但是,某些设置需要根据具体情况调整,特别是各种后处理操作,如掩码腐蚀/混合、色彩校正和锐化。

Faceswap 包含 预览工具,用于在运行最终转换之前锁定这些设置,可以通过选择 工具 选项卡然后选择 预览 子选项卡来访问:

图 4.36 – 预览工具在 Faceswap GUI 中的位置

图 4.36 – 预览工具在 Faceswap GUI 中的位置

要启动此工具,请在 输入目录 字段中提供你打算交换的视频的位置,并在 模型目录 字段中提供包含训练模型的文件夹。对齐 字段可以留空,除非对齐文件已被移动或重命名,在这种情况下,需要明确指定:

图 4.37 – Faceswap 预览工具的数据设置

图 4.37 – Faceswap 预览工具的数据设置

预览 按钮启动工具。

工具分为三个部分。主窗口显示顶部行中的原始帧中的面部,底部行显示应用当前设置后的交换。随着设置的调整,底部行将更新以反映这些更改。

图 4.38 – Faceswap 的预览工具

图 4.38 – Faceswap 的预览工具

左下角的面板显示命令行选项,而右下角的面板显示插件设置。

命令行选项

这些是在每次运行转换过程时选择的参数(这些选项不是持久的),因此您需要记住在此处设置的值,以便在主 Faceswap 转换过程中复制它。具体来说,可以在此设置的选项如下:

图 4.39 – 预览工具的命令行选择

图 4.39 – 预览工具的命令行选择

  • 颜色:要使用的颜色匹配方法。最佳选择将取决于要转换的场景。颜色平衡手动平衡匹配历史选项有进一步的配置选项,可以从插件****设置部分进行调整。

  • 遮罩类型:用于将交换的面叠加到原始帧上的遮罩类型。默认情况下,将提供基于地标的基础扩展组件遮罩。任何存在于对齐文件中的额外基于神经网络遮罩也将可用,以及完全禁用遮罩的选项()。控制所选遮罩与背景帧混合的设置从插件****设置部分进行调整。

插件设置

此部分包含 Faceswap 中可用的各种后处理插件的配置设置。在此处选择的值一旦保存,将持久保存在所有未来的转换中。因此,与命令行选择选项不同,无需在此部分记录设置的值。

插件设置分为三个配置组:

  • 颜色:颜色匹配插件的配置选项。实际要使用的方法在命令行选择部分进行选择,但其中一些选择有额外的配置参数可以在此处控制。请确保在命令行选择部分设置了正确的方法,以便在主窗口中观察任何变化。

  • 遮罩:控制交换图像与背景帧混合的选项。这些设置分为两个更进一步的类别——框混合,它控制将包含面部提取的正方形与背景帧混合的设置,以及遮罩混合,它控制将面部周围的遮罩与背景帧混合的设置。(注意:如果在命令行选择部分已选择作为遮罩类型,则在遮罩混合设置中进行的任何更改在预览窗口中都不会可见。)

这些设置中的每一个对最终输出的影响将很大程度上取决于在训练模型时选择的覆盖和居中选项。例如,对于低覆盖和传统居中(即非常紧密裁剪),提取的面部框完全位于遮罩内是完全可能的,在这种情况下,遮罩混合设置将没有可见效果。同样,对于高覆盖和面部或头部居中,整个遮罩可能存在于提取框内,在这种情况下,框混合设置将没有可见效果。在大多数情况下,调整这两种混合设置的组合将是必要的。

  • 缩放:最终的配置插件控制应用于图像的任何人工锐化。通常,交换的面部需要放大以适应最终帧。本节允许您控制任何锐化效果,以帮助更好地放大图像。

小贴士

要更好地了解调整遮罩插件设置的效果,请将颜色命令行选择设置为手动平衡,然后在手动平衡插件设置中将对比度亮度调整到-100。这将使交换区域显示为全黑色,这可以使正确调整遮罩更容易。

调整转换设置

实际使用的配置选项将根据每个视频而有所不同;没有固定的规则,所以只是调整设置直到达到满意的结果。一旦插件配置正确,可以通过点击右下角的保存按钮保存该插件的配置。要保存所有已调整插件的设置,请点击左下角的保存按钮。

当适当的设置已锁定时,记录命令行选择设置并退出预览工具。

生成交换

一旦模型经过训练,对齐文件已创建,交换设置已锁定,就可以创建最终产品。生成交换的过程称为转换——即,将源视频中的面部从原始形式转换为从训练模型生成的版本。

转换可能是 Faceswap 主要过程中最不复杂的一个。通过选择转换选项卡来访问 Faceswap 应用程序的转换部分:

图 4.40 – Faceswap GUI 中转换设置的位置

图 4.40 – Faceswap GUI 中转换设置的位置

数据

本节用于说明执行交换操作时资产所在的位置,以及最终输出应导出的位置:

  • 输入目录:源视频或待处理图像文件夹的位置。

  • 输出目录:转换后的媒体应输出的位置。此文件夹不应预先存在。

  • 对齐:可选地指定对齐文件的位置。如果对齐文件位于其默认位置(在源视频旁边)并使用默认名称,则可以将其留空,因为文件将被检测到。

  • 参考视频:如果源是一个包含单独帧的文件夹,并且所需的输出是视频文件,则此选项是必需的。参考视频将是提取帧的文件夹所提取的原始视频文件,它为转换过程提供了音频轨道和应编译到最终视频中的帧率(FPS)。

  • 模型目录:包含训练好的 Faceswap 模型的文件夹位置。

图 4.41 – Faceswap 的转换设置中的数据选项

图 4.41 – Faceswap 的转换设置中的数据选项

插件

转换过程中有多个可用的插件可供选择,这里可以进行选择。其中两个插件在我们使用预览工具时已经见过(颜色调整遮罩类型),因此请确保在此处选择与预览工具中选择的相同选项,以确保输出保持一致:

  • 颜色调整:要使用的颜色校正。这已经使用预览工具预览并选择,因此请在此处选择相同的插件。实际要使用的插件将因项目而异。

  • 遮罩类型:用于在原始帧上叠加交换人脸的遮罩类型。此处选择的遮罩必须在对齐文件中存在(组件扩展始终存在;其他遮罩需要生成)。通常,这将与模型训练时使用的相同遮罩,并且已经使用预览工具进行了预览,因此请选择用于预览的相同遮罩。

  • 写入器:用于创建最终媒体的插件。写入器插件用于生成最终产品。Ffmpeg用于创建视频文件,Gif用于创建动画 GIF,而OpenCVPillow将创建一个包含图像的文件夹,其中 OpenCV 比较快,但比 Pillow 具有更有限的文件格式选择。

可以通过选择设置 | 配置设置并选择转换节点下的相关写入器插件来配置每个写入器:

图 4.42 – Faceswap 的转换插件设置

图 4.42 – Faceswap 的转换插件设置

可以在单独的透明层上创建交换,该层仅包含交换的人脸和遮罩,以覆盖原始帧,并在各种外部 VFX 应用程序中叠加。OpenCVPillow写入器都支持此功能,其中 OpenCV 允许生成四通道 PNG 图像,而 Pillow 允许生成四通道 PNG 或 TIFF 图像。此选项可以通过在任一插件配置设置中选择绘制透明选项来启用。

图 4.43 – Faceswap 转换设置中的插件选项

图 4.43 – Faceswap 转换设置中的插件选项

其他设置

在大多数情况下可以忽略帧处理。然而,最可能引起兴趣的选项是输出缩放,它通过指定的量缩放最终输出媒体。例如,将输入视频的输出缩放设置为50,最终输出将是 360p。

可以忽略人脸处理部分。如果对齐文件已正确创建,则这里没有相关的选项。同样,在设置部分的大多数选项在大多数情况下也可以忽略。唯一的可能例外是交换模型选项。这可以用来创建与模型训练方向相反的交换 – 也就是说,而不是A > B,转换将运行B > A。如果你有一个在人脸对上训练的模型,并且希望以相反的方向运行转换,或者如果模型意外地以错误的方式训练(原始人脸在B侧训练,所需交换在A侧训练),这可能会很有用。

一旦所有设置都正确设置,请按转换按钮,将训练的模型应用于你的源媒体,并在输出目标中生成交换。

摘要

在本章中,我们学习了使用开源 Faceswap 软件创建 deepfake 所需的工作流程。讨论了数据多样性的重要性,并演示了获取、整理和生成人脸集所需的步骤。我们学习了如何在 Faceswap 中训练模型,以及如何判断模型是否已经完全训练,还学习了一些提高模型质量的技巧。最后,我们学习了如何将我们的训练模型应用于源视频,以在视频中交换人脸。

在下一章中,我们将开始亲身体验使用 PyTorch 机器学习工具包从头开始构建 deepfake 管道的神经网络,从可用于从源图像中检测和提取人脸的模型开始。

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受 www.ebsco.com/terms-of-use 条款约束。

第二部分:亲身体验 Deepfake 流程

本书这部分内容全部关于亲身体验代码。我们将深入探讨从头到尾制作 deepfake 所需的每一步,不留任何遗漏或未解释的代码行。如果你是为了代码而来,这部分内容就是为你准备的。

在本节的第一章中,我们将探讨提取过程。这是从视频中提取所有人脸以便在过程的其它阶段使用的过程。我们将查看将视频转换为帧图像的过程,然后我们将通过所有必要的代码将帧转换为干净、对齐的人脸,并准备匹配的遮罩图像以供训练。之后,我们将探讨训练过程,从下至上检查神经网络,然后展示模型的整个学习过程。最后,我们将进入转换阶段,检查将新人脸交换到原始图像上的过程,包括将其转换回视频。

到本部分结束时,你将确切知道如何从头到尾编码你自己的深度伪造。

本部分包括以下章节:

  • 第五章提取人脸

  • 第六章训练深度伪造模型

  • 第七章将人脸重新放回视频中

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use条款约束

第五章:提取人脸

在本章中,我们将从代码入手开始我们的动手实践。首先,我们将介绍提取人脸的过程。

提取人脸是涉及许多不同阶段的过程中的一系列步骤,但它是创建 deepfake 的第一个离散阶段。本章将首先讨论如何运行人脸提取脚本,然后我们将动手编写代码,解释每个部分的作用。

在本章中,我们将涵盖以下关键部分:

  • 从视频中获取图像文件

  • 在帧图像上运行提取操作

  • 与代码动手实践

技术要求

为了继续,我们建议您从 GitHub 仓库github.com/PacktPublishing/Exploring-Deepfakes下载代码,并按照readme.md文件中的说明安装 Anaconda,创建一个包含所有必需库的虚拟环境以运行脚本。该仓库还将包含自本书出版以来发生的任何勘误或更新,因此请检查那里以获取任何更新。

从视频中获取图像文件

视频不是为逐帧访问而设计的,如果处理顺序错误可能会导致问题。访问视频文件是一个复杂的过程,不适合像本章这样的入门级章节。因此,第一个任务是转换您想要提取的任何视频,将其转换为单独的帧。最好的方法是使用FFmpeg。如果您遵循了技术要求部分中的安装说明,您将已安装并准备好使用 FFmpeg。

让我们开始这个过程。

小贴士

当您看到代码或命令示例,例如这里用花括号括起来的文本时,您应该用括号中解释的信息替换该文本。例如,如果它说cd {包含视频的文件夹},并且视频在c:\Videos\文件夹中,那么您应该输入cd c:\Videos

将视频放入一个文件夹中,然后打开 Anaconda 提示符并输入以下命令:

cd {Folder with video}
mkdir frames
ffmpeg -i {Video Filename} frames\video_frame_%05d.png

这将填充frames文件夹,其中包含带有编号的导出帧的图像。

您可以使用 FFmpeg 控制许多选项,从输出图像的分辨率到要跳过的帧数。这些功能超出了本书的范围,但已在其他地方广泛介绍。如果您打算进行比基础操作更多的操作,我们建议您搜索 FFmpeg 命令行选项的指南。

在帧图像上运行提取操作

要在视频上运行提取过程,您可以从克隆的git仓库文件夹中运行提取程序。为此,只需在 Anaconda 提示符中运行以下命令:

cd {Folder of the downloaded git repo}\
python C5-face_detection.py {Folder of frame images}

这将在文件夹中的每个图像上运行面部提取过程,并将提取的图像放入face_images文件夹中,默认情况下,该文件夹位于帧图像文件夹内。该文件夹将包含每个检测到的面部的三种类型文件,以及包含所有对齐的文件。

face_alignments.json

将只有一个这样的文件。这是一个包含在图像中找到的每个面部地标和变形矩阵的 JSON 格式文件。这个文件像任何 JSON 文件一样可读,可以读取或编辑(尽管这可能不是你手动操作的事情)。

face_landmarks_{filename}_{face number}.png

这是对原始图像的副本,在面部周围画了一个边界框,并写出了五个地标点。地标是面部上的常见点。我们将在后面介绍这些地标点的用法,但我们最感兴趣的是眼睛、鼻子和嘴巴的角落。

图 5.1 – face_landmarks_bryan_0.png 示例

图 5.1 – face_landmarks_bryan_0.png 示例

作者注记

本节中用于实际演示的所有人物图像均为作者本人。

face_bbox_{filename}_{face number}.png

这张图像代表在原始图像中找到的原始边界框(围绕检测到的面部最小的框)。它将以图像中找到的面部的完整原始大小和角度显示。

图 5.2 – face_bbox_bryan_0.png 示例

图 5.2 – face_bbox_bryan_0.png 示例

face_aligned_{filename}_{face number}.png

这张图像将是面部的一个较小尺寸(默认为 256x256)图像。这是一个对齐的面部图像,其中面部已根据地标对齐。这张图像通常应将面部置于图像中心并垂直对齐。如果边界框图像中的面部在框内歪斜或倾斜,则应在对齐图像中将其拉直。还可能有一个黑色裁剪区域,其中原始框架的边缘切断了数据。

这是最重要的图像,也是用于训练模型的图像。确保对齐的面部是高质量图像对于高质量训练至关重要。这是你想要清理以获得成功深度伪造的数据。

图 5.3 – face_aligned_bryan_0.png 示例

图 5.3 – face_aligned_bryan_0.png 示例

face_mask_{filename}_{face number}.png

这张图像与对齐图像的大小相匹配。实际上,它在裁剪、旋转和尺寸上与对齐图像相匹配。面具是 AI 预测的面部轮廓,稍后将用于确保面部被正确训练,并帮助将最终的面部换回到图像中。

图 5.4 – face_mask_bryan_0.png 示例

图 5.4 – face_mask_bryan_0.png 示例

遮罩应该与对齐的人脸完美对齐,显示人脸和边缘的位置。您可以在图 5.5中看到遮罩是如何覆盖在人脸上的。

图 5.5 – 遮罩覆盖在对齐人脸上的示例

图 5.5 – 遮罩覆盖在对齐人脸上的示例

因此,现在您已经知道了如何运行人脸检测器及其所有输出,让我们进入实践部分,确定它到底在做什么。

与代码实践

现在是时候进入代码部分了。我们将详细说明C5-face_detection.py文件的功能以及为什么选择每个选项。代码主要分为五个部分:初始化、图像准备、人脸检测、人脸标记/对齐和遮罩。

初始化

让我们开始吧。

作者注

为了便于在书中阅读,需要对示例中的间距进行修改。然而,Python 对空白字符敏感,并将空白字符作为语言语法的一部分使用。这意味着从本书中复制代码几乎肯定会包含错误的间距。因此,我们强烈建议您从本书的 Git 仓库github.com/PacktPublishing/Exploring-Deepfakes中获取代码,如果您打算运行它的话。

  1. 首先,我们导入所有必需的库:

    import os
    import torch
    import cv2
    import json_tricks
    import numpy as np
    from tqdm import tqdm
    from argparse import ArgumentParser
    from face_alignment.detection.sfd import FaceDetector
    from face_alignment import FaceAlignment, LandmarksType
    from skimage.transform._geometric import _umeyama as umeyama
    from lib.bisenet import BiSeNet
    

在本节中,我们导入我们将使用的重要库和函数。这些都在代码中使用,将在使用时进行解释,但熟悉任何项目的导入是一个好主意,因为它让您了解正在使用的函数及其来源。

许多这些导入来自 Python 标准库,如果您想了解更多关于它们的信息,您可以在网上找到它们的文档。我们将在我们遇到它们时解释那些不是标准库的部分,包括如何找到它们的文档。

  1. 接下来,让我们暂时跳到代码的末尾,看看参数解析:

    if __name__ == "__main__":
    """ Process images in a directory into aligned face images
    Example CLI:
    ------------
    python face_detection.py "C:/media_files/"
    """
    parser = ArgumentParser()
    parser.add_argument("path",
      help="folder of images to run detection on")
    parser.add_argument("--cpu", action="store_true",
      help="Force CPU usage")
    parser.add_argument("--size", default=256,
      help="height and width to save the face images")
    parser.add_argument("--max_detection_size", default=1024,
      help="Maximum size of an image to run detection on.
        (If you get memory errors, reduce this size)")
    parser.add_argument("--jpg", action="store_true",
      help="use JPG instead of PNG for image saving
        (not recommended due to artifacts in JPG images)")
    parser.add_argument("--min_size", default=.1,
      help="Minimum size of the face relative to image")
    parser.add_argument("--min_confidence", default=.9,
      help="Minimum confidence for the face detection")
    parser.add_argument("--export-path",
      default="$path/face_images",
      help="output folder (replaces $path with the input)"
    opt = parser.parse_args()
    opt.export_path = opt.export_path.replace('$path',opt.path)
    main(opt)
    

在本节中,我们定义了脚本中可用的选项。它允许您在不修改代码的情况下更改许多选项。大多数选项使用默认值即可,但 Python 标准库中的参数解析器为我们提供了一个易于使用的运行时更改这些选项的方法。参数解析器的文档和指南是标准库的一部分,所以我们将跳过基础知识,除了说明我们使用parser.add_argument为每个我们想要的参数添加,所有参数都放入opt变量中。

我们在这里做的一件特别的事情是在定义后更改export_path变量,将变量中的$path字符串替换为将输出文件夹放入输入文件夹。如果用户覆盖了默认值,则不需要替换(或者用户可以使用$path来指定输入文件夹中的不同子文件夹,如果需要的话)。

注意

Python 规范要求此部分位于文件末尾才能正常工作。然而,在浏览其余代码之前,至少查看此部分非常重要,以便了解它在做什么。

  1. 接下来,我们返回以下代码:

    def main(opt):
    

本节从我们用于执行所有实际工作的主函数开始。

  1. 我们首先确保输出文件夹存在:

    if not os.path.exists(opt.export_path):
      os.mkdir(opt.export_path)
    

如果不存在,这将创建输出文件夹。

  1. 然后,如果可用,我们启用图形处理单元GPU)加速:

    device = "cuda" if torch.cuda.is_available() and not opt.cpu else "cpu"
    

在这部分,我们从检查 CUDA 可用性的PyTorch开始,并将设备变量保存以供以后使用。

  1. 接下来,我们将初始化人脸检测器和对齐器:

    face_detector = FaceDetector(device=device, verbose=False)
    face_aligner = FaceAlignment(LandmarksType._2D, device=device, verbose=False)
    

这段代码定义了我们将要使用的检测器和对齐器;它指定我们将使用哪些设备,并告诉对齐器使用为2D人脸特征点训练的模型。这些类来自github.com/1adrianb/face-alignment提供的face_alignment库。检测器和对齐器都是针对特定用途训练的 AI 模型。检测器在给定图像中找到任何人脸,并通过使用边界框返回它们的位置。对齐器接收这些检测到的人脸并找到我们可以用来将人脸对齐到已知位置的标记点。

  1. 接下来,我们定义我们的掩码器并准备它。我们加载预训练的权重,并设置它使用 CUDA(如果启用了 NVIDIA 支持):

    masker = BiSeNet(n_classes=19)
    if device == "cuda":
      masker.cuda()
    model_path = os.path.join(".", "binaries",
      "BiSeNet.pth")
    masker.load_state_dict(torch.load(model_path))
    masker.eval()
    desired_segments = [1, 2, 3, 4, 5, 6, 10, 12, 13]
    

我们使用desired_segments变量来定义我们想要使用哪些掩码器段。在我们的当前掩码器中,这给我们提供了人脸本身,并丢弃背景、头发和衣服,以便我们的模型只需要学习我们想要交换的信息。

  1. 最后,我们获取输入文件夹中的文件列表:

    alignment_data = {}
    list_of_images_in_dir = [ file for file in os.listdir(opt.path) if os.path.isfile(os.path.join(opt.path,file)) ]
    

这段代码首先准备一个空字典来存储对齐数据。这个字典将存储我们想要存储和与我们将要使用的面部数据一起保存的所有数据。

接下来,它将获取文件夹中所有文件的列表。这假设每个文件是我们想要从中导入人脸的图像。如果有任何非图像文件,它将失败,因此重要的是将额外文件保持在文件夹外。

接下来,列出目录中的文件并检查它们是否为文件,在将它们存储在列表中之前。

图像准备

图像准备是下一步。

  1. 这加载图像并使它们准备好由其他工具处理:

    for file in tqdm(list_of_images_in_dir):
      filename, extension = os.path.splitext(file)
    

这是一个循环的开始,它将遍历目录中的每个文件以处理它们。tqdm库创建了一个易于阅读的状态栏,包括预测处理过程将花费多长时间。这一行足以了解基础知识,但它还能提供更多功能。您可以在github.com/tqdm/tqdm#documentation查看完整文档。

  1. 接下来,我们加载图像并将其转换为 RGB 颜色顺序:

    image_bgr = cv2.imread(os.path.join(opt.path,file))
    image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
    height, width, channels = image_rgb.shape
    

OpenCV 是一个用于操作图像的工具库。您可以在docs.opencv.org/找到它的文档。在这段代码中,我们使用它来打开图像并处理各种图像任务,我们将随着任务的展开进行解释。

OpenCV 以蓝、绿、红BGR)的顺序加载图像,这是不寻常的,因此我们必须将其转换为红、绿、蓝RGB)的顺序,以便后续处理,因为其他库期望文件以该颜色顺序。如果我们不转换,所有颜色都会错位,许多工具将提供错误的结果。

然后我们获取图像的形状;这将给我们图像的高度和宽度,以及颜色通道的数量(由于我们以彩色图像加载,这将会有三个)。

  1. 接下来,我们需要检查是否需要调整图像的大小:

    adjustment = opt.max_detection_size / max(height, width)
    if adjustment < 1.0:
      resized_image = cv2.resize(image_rgb, None, fx=adjustment, fy=adjustment)
    else:
      resized_image = image_rgb
      adjustment = 1.0
    

当您通过 AI 模型运行图像时,图像的尺寸可以极大地改变使用的内存量。如果它超过了 AI 可用的内存,这可能会导致错误。在这段代码中,我们正在从选项中获取最大尺寸,然后找到我们需要调整以将图像调整到最大尺寸并跟踪该变化,以便可以将面部检测器的结果恢复到在原始图像上工作。这个过程允许我们使用较小的图像进行面部检测 AI,同时仍然使用全尺寸图像中的面部,从而提供最佳分辨率和细节。

面部检测

现在是时候检测图像中的面部了。这个过程会遍历每一张图像以检测任何面部。

  1. 首先,我们检查调整大小后的图像中是否有任何面部:

    faces = face_detector.detect_from_image(resized_image)
    

这运行面部检测器以在图像中找到任何面部。该库使这个过程变得非常简单。我们发送调整大小后的图像,并返回一个包含找到的所有面部及其边界框的列表。这些框都是相对于较小的调整大小后的图像的,以确保我们有足够的内存来处理它。

  1. 接下来,我们将遍历每个面部:

    for idx, face in enumerate(faces):
      top,left,bottom,right = (face[0:4] /
        adjustment).astype(int)
      confidence = face[4]
      if confidence < opt.min_confidence:
        Continue
    

作为循环的输入,我们使用内置的 Python 函数enumerate,它为我们提供了找到的每个面部的计数。我们使用这个数字来稍后通过编号识别面部。然后,我们提取边界框的大小并将其除以调整大小。这会将检测到的面部边界框恢复到与原始图像匹配,而不是较小的调整大小后的图像。我们将调整后的面部检测存储在变量中,并将它们四舍五入到整数,以便我们可以将其与单个像素对齐。

接下来,使用面部检测 AI 的置信度水平跳过任何低于置信度水平的面部,该置信度水平已在选项中设置。

小贴士

面部检测 AI 的置信度从0(无面部)到1(绝对确定有面部)。大多数 AI 使用01的范围,因为对于 AI 来说,处理已知范围更容易。如果您在进行 AI 工作时得到您不期望的结果,您可能想要检查它是否已被限制在01的范围内。

  1. 接下来,我们确保面部足够大以便使用:

    face_height = bottom - top
    face_width = right – left
    face_size = face_height * face_width
    if face_size/(height*width) < opt.min_size:
      continue
    

此代码找到面部的高度和宽度,然后使用这些信息来找到整个面部的大小,并将其与最小面部大小进行比较,跳过任何太小的人脸。它使用原始帧的大小来更容易地过滤掉不是主要焦点的面部。这个值设置得比较低,但如果在单张图像中有许多面部,例如在人群场景中,这可能很有用。

  1. 接下来,我们将边界框作为图像输出:

    detected_face = image_bgr[y1:y2,x1:x2]
    cv2.imwrite(os.path.join( opt.export_path,
                  f"face_bbox_{filename}_{fn}.png"),
                detected_face)
    

此代码创建一个只包含图像中适合面部边界框的部分的图像,并将其保存为输出文件夹中的.png文件。这可以让您看到检测到的面部;然而,这实际上对于任何后续步骤都不是必需的,因此如果您不想保存这些数据,可以将其删除。

面部关键点定位/对齐

下一步是检测面部关键点并将它们对齐到已知位置。

  1. 首先,我们将获取对齐:

    landmarks = face_aligner.get_landmarks_from_image(
      image_rgb, detected_faces = [face[0:4]/adjustment])
    

在这里,我们使用与面部检测相同的库,传递完整的图像和调整后的边界框,以获取面部关键点。该库返回 68 个关键点位置,这些位置基于面部上的特定点。

  1. 接下来,我们画一个框:

    landmark_image = image_bgr.copy()
    landmark_image = cv2.rectangle(landmark_image,
      (top, left), (bottom, right), thickness=10,
      color=(0, 0, 0))
    

在这里,我们以 BGR 颜色格式生成原始图像的新副本,这样我们就可以在不损坏原始副本的情况下在图像上绘制。然后我们使用 OpenCV 绘制一个检测到的面部的矩形。其厚度设置为10像素,用黑色绘制。

  1. 接下来,我们创建用于对齐的关键点:

    right_eye = np.mean(landmarks[0][36:42],axis=0)
    left_eye = np.mean(landmarks[0][42:48],axis=0)
    nose_tip = landmarks[0][30]
    right_mouth = landmarks[0][48]
    left_mouth = landmarks[0][54]
    limited_landmarks = np.stack(
      (right_eye,
       left_eye,
       nose_tip,
       right_mouth,
       left_mouth))
    

在此代码中,我们将 68 个关键点拆分到只有 5 个。我们使用每个眼睛周围的关键点的平均值来找到平均眼睛位置,然后获取鼻尖和嘴角的拐角,并将它们保存到一个新的数组中。这个减少的关键点集有助于保持对齐的一致性,因为 68 个关键点包含大量的噪声边缘点。

  1. 接下来,我们将绘制关键点到图像上:

    landmark_image = cv2.rectangle(landmark_image,
      (x1,y1), (x2,y2), thickness=10, color=(0,0,0))
    colors = [[255,0,0], # Blue
              [0,255,0], # Green
              [0,0,255], # Red
              [255,255,0], # Cyan
              [0,255,255]] # Yellow
    for count, landmark in enumerate(limited_landmarks):
      landmark_adjusted = landmark.astype(int)
      landmark_image = cv2.circle(landmark_image,  tuple(landmark_adjusted), radius=10,
      thickness=-1, color=colors[count])
    cv2.imwrite(os.path.join(opt.export_path,
                  f"face_landmarks_{filename}_{idx}.png",
                landmark_image)
    

在这里,我们定义一组颜色,然后使用这些颜色为每个关键点位置绘制单独的点。我们将它们保存为输出文件夹中的.png图像文件。这些图像用于演示和调试目的,并且以后从未使用过,因此如果您不需要这些调试图像,可以删除它们(以及这个保存调用)。

  1. 接下来,我们定义平均面部:

    MEAN_FACE = np.array([[0.25, 0.22],
                          [0.75, 0.22],
                          [0.50, 0.51],
                          [0.26, 0.78],
                          [0.74, 0.78]])
    

在这里,我们定义另一个数组;这是基于不同关键点的平均面部位置。我们在下一部分中使用它来对齐图像。这些数字基于我们希望面部所在的位置,但可以是任何可以在面部可靠检测到的数字。

  1. 接下来,我们生成用于对齐面部的转换矩阵:

    warp_matrix = umeyama(limited_landmarks,
      MEAN_FACE * (opt.size*.6)+(opt.size*.2), True)
    

这部分内容有点复杂,所以我们将在这里深入探讨。我们使用 Shinji Umeyama 创建的两个点集对齐算法,该算法在SciKit-Image库中实现。这个过程需要两组点,一组是已知集(在本例中为MEAN_FACE,我们之前定义过)和另一组未知集(在本例中为limited_landmarks中保存的五个标记点),并将它们对齐。接下来,我们将标记点乘以我们想要图像最终大小的尺寸,并在人脸周围添加边框,以便人脸居中,边缘周围有额外空间。

umeyama算法创建一个矩阵,我们将其保存为warp_matrix,该矩阵编码了创建对齐人脸所需的平移。

  1. 接下来,我们将标记点和warp_matrix添加到对齐数据列表中:

    alignment_data[file] = {"landmark": landmarks,
                            "warp_matrix": warp_matrix}
    
  2. 最后,我们创建并写入对齐的人脸图像:

    aligned_face = image_bgr.copy()
    aligned_face = cv2.warpAffine(aligned_face,
      warp_matrix[:2], (opt.size,opt.size))
    cv2.imwrite(os.path.join(opt.export_path,
                  f"face_aligned_{filename}_{idx}.png"),
                aligned_face)
    

在这里,代码创建原始图像的新副本,然后使用 OpenCV 库中的warpAffine函数应用由umeyama算法生成的warp_matrix。该矩阵包括所有信息——平移(将图像左右或上下移动)、缩放(调整大小以适应)和旋转——以将人脸与预定义的标记点对齐。最后,它将新对齐的图像保存为文件。

小贴士

虽然 OpenCV 在 BGR 颜色顺序中处理所有图像过程,但对于不依赖于颜色顺序的任务,例如这里的cv2.warpAffine()步骤,是可以的。如果你忽略颜色顺序,你必须小心,因为很容易忘记你正在使用哪种颜色顺序,这可能导致颜色完全错误的复杂错误。在这种情况下,由于下一步将是使用cv2.imwrite()将图像写入文件,我们可以使用 BGR 颜色顺序的图像。

  1. 接下来,我们保存稍后用于重建图像所需的数据:

    if file not in alignment_data.keys():
      alignment_data[file] = {"faces": list()}
      alignment_data[file]['faces'].append({
        "landmark": landmarks,"warp_matrix": warp_matrix})
    

我们将标记点和变换矩阵保存到字典中,稍后我们将将其保存为 JSON 文件。这些信息对于后续处理步骤非常重要,因此我们必须确保保存它。

  1. 接下来,我们将创建一个遮罩图像:

    mask_face = cv2.resize(aligned_face, (512, 512))
    mask_face = torch.tensor(mask_face,
      device=device).unsqueeze(0)
    mask_face = mask_face.permute(0, 3, 1, 2) / 255
    if device == "cuda":
      mask_face.cuda()
    segments = masker(mask_face)[0]
    

遮罩器是另一个对所提供图像有特定要求的 AI。为了满足这些要求,我们首先必须以某种方式处理人脸图像。首先,遮罩器 AI 期望图像大小为 512x512 像素。由于我们的对齐人脸可能大小不同,我们需要创建一个图像副本,使其大小符合预期的 512x512 像素。

我们将其转换为PyTorch张量而不是Numpy数组,然后对张量进行unsqueeze操作。这添加了一个额外的维度,因为遮罩器在包含一个或多个图像的数组上工作;尽管我们只提供给它一个,我们仍然需要提供包含我们的单个图像的额外维度,以匹配预期的形状。

接下来,masker 期望通道的顺序与我们不同。为此,我们将数组permute到正确的顺序。此外,传统上图像存储在0-255的范围内,这允许每个单独的颜色有256种变化,但 masker 期望图像颜色为 0-1 范围内的浮点数。我们通过255来除以范围,以获得预期的范围。

接下来,如果启用了 NVIDIA 支持,我们将图像转换为 CUDA 变量,这将其转换为 GPU 也可以使用的格式,并处理将其移动到 GPU。

最后,我们在准备好的图像上运行 masker AI,并将掩码输出保存到新的变量中。masker 输出多个信息数组,但现在对我们有用的只有第一个数组,所以我们只保存这一个,并丢弃所有其他数组。

  1. 接下来,我们处理 masker 输出并将其保存到图像文件中:

    segments = torch.softmax(segments, dim=1)
    segments = torch.nn.functional.interpolate(segments,
      size=(256, 256),
      mode="bicubic",
      align_corners=False)
    mask = torch.where( torch.sum(
        segments[:,desired_segments,:,:], dim=1) > .7,
      255, 0)[0]
    mask = mask.cpu().numpy()
    cv2.imwrite(os.path.join(opt.export_path,
                  f"face_mask_{filename}_{idx}.png"),
                mask)
    

现在 masker 的结果已经返回,我们仍然需要对其进行处理以获得有用的信息。首先,我们使用softmax将结果从绝对值转换为相对值。这使得我们可以将掩码视为一个数组,其中每个像素属于特定类别的可能性,而不是模型中的原始值。

接下来,我们使用interpolate,这是一个Pytorch方法,将数据调整回原始人脸图像大小。我们必须这样做,因为像输入一样,masker 模型的输出是 512x512。我们使用bicubic因为它给出了最平滑的结果,但也可以选择其他选项。

接下来,我们使用sumwhere将值设置为2550。我们还使用desired_segments来移除对我们无用的片段。在这里,我们使用.7作为阈值,所以如果我们有 70%的把握认为某个像素应该包含在掩码中,我们就保留它,但如果低于那个 70%的截止值,我们就将该像素丢弃。

接下来,我们将数据移回 CPU(如果它已经在 CPU 上,则没有任何变化)并将其转换为Numpy数组。

最后,我们将掩码图像保存为.png文件,以便以后使用。

  1. 整个提取过程的最后一步是将对齐数据写入文件:

    with open(os.path.join(opt.export_path,
      f"face_alignments.json", "w") as alignment_file:
      alignment_file.write(
        json_tricks.dumps(alignment_data, indent=4))
    

一旦每个图像都经过处理,文件的最后一步是保存包含所有地标数据的文件,以供以后使用。在这里,我们使用json_tricks库,该库有一些将Numpy数组写入 JSON 文件的有用功能。该库处理写入和读取 JSON 文件作为Numpy数组的一切,因此我们可以简单地传递完整的dictionary数组,而无需手动将它们转换为列表或其他 Python 标准库 JSON 可以处理的其他默认 Python 类型。有关完整文档,请访问他们的文档页面json-tricks.readthedocs.io/en/latest/

到目前为止,我们已经从一个充满图像的文件夹中提取了所有面部。我们运行了多个 AI 来获取所需的数据,并将所有这些数据格式化以供以后使用。这些数据现在已准备好进行训练,这将在下一章中介绍。

摘要

提取是训练过程的第一步。在本章中,我们检查了后续步骤中需要的数据以及从源图像中提取所需训练数据的过程。我们亲自动手处理这个过程,使用多个 AI 来检测和标记面部并生成掩码,以及处理和保存这些数据的必要步骤。

C5-face_detection.py 文件可以处理一个图像目录。因此,我们介绍了如何将视频转换为图像目录以及如何通过脚本处理该目录。该脚本创建了你需要的所有训练文件,以及一些有趣的调试图像,这些图像让你可以可视化检测器处理图像时使用的每个过程。然后我们逐行检查整个过程,以便我们确切地知道脚本内部发生了什么,不仅学习了输出内容,而且确切地了解了输出是如何产生的。

在完成检测过程后,你可以进行数据清理,如第3 章中所述的掌握数据,以确保你的数据为下一章的主题:训练做好准备。

练习

  1. 我们使用了现有的库来进行面部检测、地标标记和地标对齐。还有其他提供类似功能的库。并非所有库的工作方式都相同,实现这些差异是一项极其有用的练习。尝试用其他检测面部库替换 face_alignment 库,例如 github.com/timesler/facenet-pytorchgithub.com/serengil/deepface。开源有很多有用的库,但学习它们之间的差异以及何时使用一个而不是另一个可能很困难,而在这之间进行转换可能是一种有用的实践。

  2. 在本章中,我们使用了 2D 地标进行对齐,但可能需要 3D 地标。尝试替换以下内容:

    face_aligner = FaceAlignment(LandmarksType._2D,
      device=device, verbose=False)
    

with:

face_aligner = FaceAlignment(LandmarksType._3D,
  device=device, verbose=False)

并相应地调整其余过程。你还需要修改 MEAN_FACE 以考虑第三维。

3D 地标还包含哪些问题?使用它们你能得到什么好处?

  1. 在深度伪造中,我们最感兴趣的是人脸,因此这个过程使用专门针对人脸的技术。想象一下你需要做些什么来提取不同物体的图像。例如,手表、帽子或太阳镜。在github.com/ultralytics/yolov5的仓库中有一个预训练的模型,可以检测数百种不同的物体。尝试提取人脸以外的不同物体。特别思考如何进行对齐:你能利用边缘检测或颜色模式来找到可以对其对齐的点吗?

  2. Umeyama 的方法将与其对齐的每个点视为同等重要,但如果你尝试与所有 68 个地标点对齐而不是仅仅的5个点,会发生什么?如果是 2 个点呢?你能找到一个更好的方法吗?一个更快的方法?一个更准确的方法吗?尝试修改脚本以输出face_landmarks .png文件中的所有 68 个地标点,这样你可以可视化这个过程。

EBSCOhost - 2023 年 11 月 27 日早上 6:20 打印。所有使用均受www.ebsco.com/terms-of-use条款约束。

第六章:训练 Deepfake 模型

训练 deepfake 模型是创建 deepfake 最重要的部分。这是 AI 从你的数据中学习面部的地方,也是最有趣的神经网络操作发生的地方。

在本章中,我们将深入研究训练代码以及实际创建 AI 模型的代码。我们将查看神经网络子模块以及它们如何组合成一个完整的神经网络。然后我们将介绍训练网络所需的一切,最终得到一个可以交换两个面部图像的模型。

本章我们将涵盖以下主题:

  • 理解卷积层

  • 亲身体验 AI

  • 探索训练代码

到本章结束时,我们将设计我们的神经网络,并构建一个能够教会它们交换面部图像的训练管道。

技术要求

要运行本章中的任何代码,我们建议您下载我们的官方仓库github.com/PacktPublishing/Exploring-Deepfakes,并按照设置 Anaconda 环境所需的库的说明进行操作。

为了进行训练,你必须拥有两组提取的面部图像。你需要将这两组图像都输入到模型中,它将分别学习这两张脸。确保你为这两张脸都获得了足够的数据,并且种类丰富是很重要的。如果你有疑问,请查阅第三章掌握数据,以获取获取最佳数据的建议。

理解卷积层

在本章中,我们将最终深入探讨深伪影背后的神经网络的核心。这些网络如何工作的很大一部分是一个称为卷积层的技术。这些层在有效处理图像数据方面极为重要,并且是大多数神经网络的重要基石。

卷积是一种改变物体形状的操作。在神经网络的情况下,我们使用卷积层,它在一个矩阵上迭代卷积并创建一个新的(通常是较小的)输出矩阵。卷积是一种在同时搜索模式的同时减小图像尺寸的方法。你堆叠的卷积层越多,从原始图像中可以编码的复杂模式就越多。

图 6.1 – 一个卷积缩小完整图像的示例

图 6.1 – 一个卷积缩小完整图像的示例

定义卷积层有几个细节。第一个是维度。在我们的例子中,我们使用 2D 卷积,它在 2D 空间中工作。这意味着卷积在每个通道的 x 和 y 轴上操作。这意味着对于第一次卷积,每个颜色通道都是单独处理的。

接下来是内核,它定义了每个卷积考虑的区域大小。内核的数量也会影响输出。例如,如果你有一个 3x9 的矩阵和 3x3 的内核大小,你会得到一个 1x3 的矩阵输出。

图 6.2 – 将 3x9 转换为 1x3 矩阵输出的 3x3 卷积过程的示例

图 6.2 – 将 3x9 转换为 1x3 矩阵输出的 3x3 卷积过程的示例

接下来是步长,它定义了卷积在矩阵中移动时每次迭代的步长大小。例如,步长为 2 会使我们的 3x3 内核重叠矩阵的一个条目。步长在每个维度上都是重复的,所以如果你将示例输入矩阵向左或向右扩展,你也会得到那个方向的重叠。

图 6.3 – 步长小于内核大小导致输出之间的元素共享的示例

图 6.3 – 步长小于内核大小导致输出之间的元素共享的示例

最后是padding_mode,你可以指定不同的填充类型,例如反射,这将使填充等于它沿填充轴反射的条目,就像输入矩阵边缘的镜子,将每个条目反射回来。

图 6.4 – 将 1x7 填充为 3x9 的示例,在卷积到 1x3 之前

图 6.4 – 将 1x7 填充为 3x9 的示例,在卷积到 1x3 之前

我们堆叠多个卷积层,因为我们正在寻找更大的模式。为了找到所有适当的模式,我们在添加到塔中时增加卷积层的深度。我们从一个有 128 个内核的卷积开始,然后增加到 256、512,最后到 1,024。每一层也有 5 个内核大小、2 个步长和 2 个填充。这有效地将每一层的宽度和高度减半。因此,第一层接收一个 3x64x64 的图像,并输出一个 128x32x32 的矩阵。下一层将其转换为 256x16x16,然后是 512x8x8,最后是 1024x4x4。

接下来,我们将最终深入到 deepfakes 的核心代码——神经网络本身。

小贴士

跟踪卷积层如何改变矩阵的大小可能会令人困惑。计算输出矩阵大小的公式实际上相当简单,但不太直观:(input_size+2*padding-stride+1)/2。如果你有正方形矩阵,这个计算将适用于任何维度,但如果你有非正方形矩阵,你必须分别计算这两个维度。

亲身体验 AI

我们将首先检查的代码是实际的模型本身。这段代码定义了神经网络的结构以及如何调用它。所有这些都被存储在lib/models.py库文件中。

首先,我们加载我们使用的任何库:

import torch
from torch import nn

在这种情况下,我们只导入了 PyTorch 及其nn子模块。这是因为我们只在这个文件中包含了模型代码,而任何其他库都将调用使用这些函数的文件。

定义我们的上采样器

我们模型最重要的部分之一是上采样层。因为这个在上采样器和解码器中都被多次使用,所以我们将其独立出来定义,我们将在下面介绍:

  1. 首先,我们定义我们的类:

    class Upscale(nn.Module):
      """ Upscale block to double the width/height from depth. """
      def __init__(self, size):
        super().__init__()
    

注意,像我们的编码器一样,它继承自nn.Module。这意味着我们必须在这个类的初始化中调用父类的初始化。这使我们的类从 PyTorch 获得了许多有用的能力,包括使神经网络工作的反向传播算法。

  1. 接下来,我们定义我们的层:

    self.conv = nn.Conv2d(size * 2, size * 2 * 2, kernel_size=3,
      padding="same")
    self.shuffle = nn.PixelShuffle(2)
    

上采样器只使用两层。第一层是一个卷积层,其输入大小是初始化函数的两倍。我们这样做是因为上采样类接受一个输出大小,并且由于它通过减半深度来增加宽度和高度,它需要输入深度是输出深度的一半。在这种情况下,填充是same而不是一个数字。这是一种特殊的方法,使nn.Conv2d层输出与输入具有相同宽度和高度的矩阵。对于3的核大小,这会创建一个1的填充。

nn.PixelShuffle是一个层,它接受一个输入矩阵,通过移动条目,将深度层转换为宽度和高度。与前面的卷积层一起,这有效地以可学习和有效的方式“上采样”了图像。我们传递2,因为我们希望它将宽度和高度加倍。其他数字可以用于不同的缩放因子,但需要调整卷积层和调用该类的模型。

  1. 最后,我们有了我们的前向函数:

    def forward(self, x):
      """ Upscale forward pass """
      x = self.conv(x)
      x = self.shuffle(x)
      return x
    

这个前向函数只是接受输入,然后通过卷积层和PixelShuffle层运行它,然后返回结果。

创建编码器

接下来,让我们创建编码器:

  1. 首先,我们声明编码器类:

    class OriginalEncoder(nn.Module):
        """ Face swapping encoder
        Shared to create encodings for both the faces
        """
    

在这里,我们定义了编码器并提供了一段简短的注释。我们将其声明为nn.Module类的子类。这使我们的类从 PyTorch 获得了许多有用的能力,包括使神经网络工作的反向传播算法。

作者注记

在这本书中,我们只包括了基本的、原始模型。这是第一个深度伪造模型,几乎在各个方面都被超越,但它很容易理解,所以它非常适合这本书。如果您想探索其他模型,我们建议您查看Faceswap.dev上的 Faceswap,它不断更新最新的模型。

  1. 接下来,我们将定义初始化函数:

    def __init__(self):
      super().__init__()
    

这个函数实际上是构建神经网络层的函数。每个层都在这个函数中定义,以便 PyTorch 可以自动处理权重的细节。我们还调用了父类的__init__函数,以准备任何必要的变量或功能。

  1. 接下来,我们将开始定义我们的激活函数:

    self.activation = nn.LeakyReLU(.1)
    

我们使用LeakyReLULeaky Rectified Linear Unit作为我们模型的激活函数。激活函数接收一个层的输出并将其带入一个标准化的范围。

如果从后往前分解 Leaky Rectified Linear Unit 的单词,Leaky Rectified Linear Unit是什么就很容易理解。Unit在这里意味着与函数相同;它接收一个输入并提供一个输出。Linear意味着一条线,它在移动时不会改变方向;在这种情况下,它是一个 1:1 的关系,输出与输入匹配(输入为 1 导致输出为 1,输入为 2 导致输出为 2,依此类推)。Rectified只是意味着它已经被转换为正数,所以负数变成了 0。Leaky实际上使最后一句话有点误导。研究发现,当整个负空间变成 0 时,神经网络实际上工作得并不好。所以这里的 leaky 实际上意味着负数会被缩放到一个几乎低于 0 的范围。

我们在这里使用 0.1,这样任何小于 0 的数字都会乘以 0.1,将其缩小 10 倍。这里可以使用许多不同的值,不同的项目会做出不同的决定。标准值通常位于 0.005 到 0.2 的范围内。

  1. 接下来,我们将定义我们的卷积塔:

    self.conv_tower = nn.Sequential(
      nn.Conv2d(3, 128, kernel_size=5, stride=2, padding=2),
      self.activation,
      nn.Conv2d(128, 256, kernel_size=5, stride=2, padding=2),
      self.activation,
      nn.Conv2d(256, 512, kernel_size=5, stride=2, padding=2),
      self.activation,
      nn.Conv2d(512, 1024, kernel_size=5, stride=2, padding=2),
      self.activation)
    

卷积塔正如其名,是一系列卷积层的堆叠。在每个卷积层之后,我们都包含一个激活函数。这有助于确保模型保持正确的方向,并使卷积更有效。每个情况下的激活都是相同的,并不进行任何“学习”,只是作为一个函数工作,因此我们不需要为每个单独的层创建单独的层,可以使用我们已初始化的相同激活函数。

我们在这里使用nn.Sequential来将层堆叠组合成一个单一的层。在 PyTorch 中,序列层实际上是一个非常强大的工具,允许你创建简单的神经网络,而无需为模型编写整个类。我们在这里使用它来组合所有的卷积层,因为每个情况中输入的一端都会穿过所有层。这使得在后续的forward函数中使用它更容易。但序列模型会按顺序运行其构成层,并且无法处理条件if语句或为 PyTorch 编写的函数。

  1. 接下来,我们将定义一个flatten层:

    self.flatten = nn.Flatten()
    

flatten层正是其名称所描述的那样;它将前一个层展平到只有一个轴。这在正向传播中用于将卷积塔产生的 1024x4x4 矩阵转换成一个 4,096 个元素的宽单维度层。

  1. 接下来,我们将定义我们的密集层:

    self.dense1 = nn.Linear(4 * 4 * 1024, 1024)
    self.dense2 = nn.Linear(1024, 4 * 4 * 1024)
    

密集层被称为密集层,因为它们是完全连接的。与卷积层不同,矩阵中的每个条目都与前一层的每个输入相连。密集层是原始神经网络层类型,非常强大,但它们也非常占用内存。实际上,这两个层占用了整个深度伪造模型的大部分内存!

我们生成了两个独立的密集层。第一层接受 4,096 个条目的输入并输出一个 1,024 列宽的输出。这是模型的瓶颈:模型中数据最少的部分,然后需要重建。第二层接受一个 1,024 维的矩阵输入并输出一个具有 4,096 维的矩阵。这是第一个开始从编码细节重建面部的层。

  1. 最后的初始化步骤是定义我们的第一个上采样层:

    self.upscale = Upscale(512)
    

这一层是我们的第一个上采样器。这一层将接受一个 1024x4x4 的矩阵并将其上采样回 512x8x8 矩阵。所有其他上采样器都将存在于解码器中。这个上采样器最初被放在编码器中,可能是因为作为一个节省内存的尝试,因为第一次上采样很可能根本不需要匹配特定的人,因为它只有最一般的面部模式。

上采样层被赋予了一个输出大小为 512。这意味着输出将深度为 512,但没有定义宽度或高度。这些自然地从输入中得出,每次调用上采样都会使宽度和高度加倍。

  1. 接下来,我们将回顾我们的前向函数:

    def forward(self, x):
      """ Encoder forward pass """
    

前向函数是实际将网络应用于给定矩阵的操作。这既用于训练,也用于训练模型的推理。

  1. 首先,我们获取批大小:

    batch_size = x.shape[0]
    

我们需要在后续过程中保存我们开始的批大小,所以我们立即保存它。

  1. 最后,我们将数据通过整个模型:

    x = self.conv_tower(x)
    x = self.flatten(x)
    x = self.dense1(x)
    x = self.dense2(x)
    x = torch.reshape(x, [batch_size, 1024, 4, 4])
    x = self.upscale(x)
    x = self.activation(x)
    return x
    

在这段代码中,我们依次将输入矩阵通过每一层。这里唯一的惊喜是在最后的dense之后调用的torch.reshape,这实际上是第一层dense之前flatten调用的反操作。它将 4096 列宽的矩阵改变形状,使其再次成为 1024x4x4 矩阵。

然后,我们在返回结果之前,将数据通过上采样层和激活函数。

构建解码器

解码器负责接收编码的面部数据并尽可能准确地重新创建一个面部。为此,它将遍历成千上万甚至数百万个面部,以更好地将编码转换为面部。同时,编码器也将变得擅长编码面部。

我们在这里使用了复数的“decoders”,但实际上这段代码只定义了一个单一的解码器。这是因为训练代码创建了该解码器类的两个副本。

  1. 首先,我们定义并初始化我们的模型:

    class OriginalDecoder(nn.Module):
      """ Face swapping decoder
      An instance for each face to decode the shared encodings.
      """
      def __init__(self):
        super().__init__()
    

这段代码,就像编码器和上采样器一样,是nn.Module的一个实例,需要一个初始化函数,该函数也会调用父类的初始化器。

  1. 接下来,我们定义我们的激活函数:

    self.activation = nn.LeakyReLU(.1)
    

就像我们的编码器的激活一样,我们使用 LeakeReLu 并带有 0.1 的负缩放。

  1. 接下来,我们定义我们的上采样塔:

    self.upscale_tower = nn.Sequential(Upscale(256),
      self.activation,
      Upscale(128),
      self.activation,
      Upscale(64),
      self.activation)
    

上采样塔与编码器的卷积塔非常相似,但使用上采样块而不是缩小卷积。因为编码器中有一个上采样器,所以实际上这个解码器中上采样更少。就像卷积塔一样,每个上采样之后也有激活函数,以保持范围趋势为正。

  1. 接下来,我们定义我们的输出层:

    self.output = nn.Conv2d(64, 3, 5, padding="same")
    

输出层是特殊的。虽然之前每一层的输出都是前一层深度的一半,但这一层将卷积层的 64 个深度输出转换回一个三通道图像。三通道维度并没有什么特殊之处,但由于训练过程的工作方式,每个都与训练图像的一个颜色通道相关联。

  1. 现在,我们定义解码器的正向函数:

    def forward(self, x):
      """ Decoder forward pass """
      x = self.upscale_tower(x)
      x = self.output(x)
      x = torch.sigmoid(x)
      return x
    

这个正向函数很熟悉,与编码器和上采样层的那些非常相似。这里的主要区别在于,我们在通过上采样塔和输出层传递输入之后,使用了一个 torch.sigmoid 层。这是一种另一种类型的激活层。

Sigmoid 与 LeakyReLu 不同,因为它不是线性的。相反,它计算输入的逻辑 sigmoid。这是一个 s 形的输出,其中负输入接近 0,正输入接近 1,而 0 输入的结果为 0.5。精确的方程是 1/(1*e^-input)。这基本上将结果放在 01 之间,极端值被更紧密地压缩,这与高数乘法导致数值更快增长的方式相匹配。这有效地将模型的输出转换为一个 0-1 的范围,我们可以轻松地将它转换成图像。

图 6.5 – sigmoid 曲线的示例

图 6.5 – sigmoid 曲线的示例

接下来,我们将检查训练代码。

探索训练代码

现在我们已经定义了我们的模型,我们可以继续在数据上训练神经网络的过程。这是人工智能实际学习不同面部特征以便之后可以在它们之间切换的部分。

  1. 首先,我们导入我们的库:

    from glob import glob
    import os
    import random
    from argparse import ArgumentParser
    import cv2
    import numpy as np
    from tqdm import tqdm
    import torch
    from lib.models import OriginalEncoder, OriginalDecoder
    

就像所有的 Python 程序一样,我们导入我们的库。我们还从我们的模型文件中导入编码器和解码器。这加载了本章前面提到的 AI 模型代码,并允许我们使用这些代码来定义本代码中的模型。Python 真的让导入我们之前编写的代码变得很容易,因为每个 Python 文件都可以直接调用或导入到另一个文件中。

注意,Python 使用一种奇怪的语法来表示文件夹路径。Python 将此语法视为与模块完全相同,因此您使用点来告诉它查看文件夹,然后给出您想要的文件。在这种情况下,我们从位于lib文件夹中的models.py文件中获取OriginalEncoderOriginalDecoder类。

  1. 接下来,我们定义我们的参数并调用我们的主函数:

    If __name__ == "__main__":
      # Train a deepfake model from two folders of face images.
      #    Example CLI:
      #    ------------
      #    python c6-train.py "C:/media/face1"
                              "C:/media/face2"
    
  2. 接下来,我们定义我们的参数:

    parser = ArgumentParser()
    parser.add_argument("patha",
      help="folder of images of face a")
    parser.add_argument("pathb",
      help="folder of images of face b")
    parser.add_argument("--cpu",
      action="store_true",
      help="Force CPU usage")
    parser.add_argument("--batchsize",
      type=int, default=16,
      help="Number of images to include in a batch")
    parser.add_argument("--iterations", type=int, default=100000,
      help="Number of iterations to process before stopping")
    parser.add_argument("--learning-rate",
      type=float, default=.000001,
      help="Number of images to include in a batch")
    parser.add_argument("--save_freq",
      type=int, default=1000,
      help="Number of iterations to save between")
    parser.add_argument("--out_path",
      default="model/",
      help="folder to place models")
    

在这里我们定义我们的参数。这些参数使我们能够更改设置、文件或细节,而无需直接修改源代码。

  1. 然后,我们解析所有参数并调用我们的主函数:

    opt = parser.parse_args()
    main(opt)
    

我们解析我们的参数并将它们传递给我们的主函数。主函数将处理所有训练过程,我们需要给它所有参数。

  1. 接下来,我们开始我们的主函数:

    def main(opt):
      """ Train a deepfake model from two folders of face images.
      """
      device = "cuda" if torch.cuda.is_available() and not opt.cpu else "cpu"
    os.makedirs(opt.out_path, exist_ok=True)
    

在这里我们开始我们的主函数并检查是否应该使用cuda。如果是的话,我们启用cuda以便我们可以使用图形处理单元GPU)来加速训练。然后我们创建我们的导出文件夹,如果尚未创建。这是我们保存模型副本和稍后生成的任何训练预览的地方。

作者注记

虽然可以在没有 GPU 的情况下运行过程的其它部分,但训练要复杂得多,在中央处理单元CPU)上运行训练会话将花费非常长的时间。因此,建议至少这部分使用 GPU 运行。如果您本地没有,您可以在任何数量的在线服务中租用。

创建我们的模型

在这里我们将创建我们的神经网络模型并将它们填充上权重:

  1. 首先,我们将创建之前模型的实例:

    encoder = OriginalEncoder()
    decodera = OriginalDecoder()
    decoderb = OriginalDecoder()
    

在这段代码中,我们创建我们的 AI 模型。我们创建一个编码器实例和两个独立的解码器。在这里我们称它们为ab,但这完全是一个任意的选择,对结果没有影响。默认情况下,我们假设您想要将第二个面部放在第一个上,因此在这种情况下,我们将从b中提取的面部放在a的框架上。

  1. 接下来,我们加载任何先前保存的模型:

    if os.path.exists(os.path.join(opt.out_path,"encoder.pth")):
      encoder.load_state_dict( torch.load(
        os.path.join(opt.out_path, "encoder.pth")).state_dict())
      decodera.load_state_dict( torch.load(
        os.path.join(opt.out_path,"decodera.pth")).state_dict())
      decoderb.load_state_dict( torch.load(
        os.path.join(opt.out_path,"decoderb.pth")).state_dict())
    

在这里我们检查给定输出文件夹中是否存在任何模型。如果存在,我们将加载这些模型权重到我们在上一部分实例化的模型中。为此,我们让 PyTorch 从磁盘加载权重,然后将权重分配给模型的状态字典。这使得 PyTorch 将权重加载到模型中,并使其准备好训练。

如果没有权重,则跳过此步骤。这意味着模型将使用随机权重初始化,准备好开始新的训练会话。这使得您可以轻松开始,无需自己生成任何随机权重。

  1. 接下来,我们获取用于训练的图像列表:

    imagesa = glob(os.path.join(opt.patha, "face_aligned_*.png"))
    imagesb = glob(os.path.join(opt.pathb, "face_aligned_*.png"))
    

这部分从文件夹中获取所有用于训练的图像列表。我们只从文件夹中加载对齐的图像,因为我们可以从对齐图像的文件名中创建其他图像的文件名。

  1. 接下来,我们为图像和掩码创建张量:

    img_tensora = torch.zeros([opt.batchsize, 3, 64, 64])
    img_tensorb = torch.zeros([opt.batchsize, 3, 64, 64])
    mask_tensora = torch.zeros([opt.batchsize, 1, 64, 64])
    mask_tensorb = torch.zeros([opt.batchsize, 1, 64, 64])
    

在这里,我们创建了将用于训练的图像的张量。对于图像张量,我们创建了一个 64x64 像素宽、3 个通道的张量,用于处理红色、绿色和蓝色通道。我们还向张量中添加了一个批量大小维度,这样我们就可以一次性存储那么多图像。批量就是我们将同时处理的图像数量。较大的批量大小有助于训练过程更高效地运行,因为我们能够从能够同时执行多个任务的硬件中受益,同时也能够从 PyTorch 以最佳顺序对任务进行分组中受益。

作者注记

较大的批量大小可以提高训练效率,那么我们为什么不将批量大小设置为 256 或 1024,而不是默认的 16 呢?这是因为批量大小并不是万能的子弹。首先,较大的批量需要更多的内存,因为系统必须同时存储批量的每一项。对于大型模型来说,这可能是不可行的。此外,大批量大小还有一个副作用。这就是经典的“只见树木不见森林”,意味着较大的批量大小可以帮助在大数据集上泛化,但在学习特定细节方面表现较差。因此,选择理想的批量大小可能和任何其他问题一样重要。对于深度伪造来说,一个好的经验法则是将批量大小保持在两位数,100+通常太大,而<10 则应避免,除非你有具体的计划。

  1. 接下来,我们定义并设置我们的优化器和损失函数:

    encoder_optimizer = torch.optim.Adam(
      encoder.parameters(), lr=opt.learning_rate/2)
    decodera_optimizer = torch.optim.Adam(
      decodera.parameters(), lr=opt.learning_rate)
    decoderb_optimizer = torch.optim.Adam(
      decoderb.parameters(), lr=opt.learning_rate)
    loss_function = torch.nn.MSELoss()
    

优化器是 PyTorch 负责训练最重要的部分:反向传播。这个过程是改变模型权重并允许 AI“学习”并更好地重新创建我们用于训练的图像的过程。它不仅负责改变权重,而且还计算应该改变多少。

在这种情况下,我们使用torch.optim.Adam优化器。这是经过证明非常有效和灵活的优化器家族的一部分。我们在这里使用它,因为它就是原始深度伪造模型使用的,但即使今天,它仍然是其中最可靠和有用的优化器之一。

我们将我们的选项中的学习率传递给每个模型。学习率基本上是优化器应该改变权重的缩放值。较大的数字会改变权重更多,这可能导致训练更快,但代价是微调困难,因为所做的改变很大。较低的学习率可以得到更好的精度,但会导致训练时间更长,因为速度较慢。我们将编码器的学习率减半,因为我们实际上会训练它两次,因为它被用来编码两个面孔。

在这里我们做的最后一件事是定义由 PyTorch 提供的torch.nn.MSEloss。这是一个称为均方误差MSE)的损失。让我们再次逐字分析这个词。

数学中的错误是指函数与完美正确结果之间的差距。在我们的情况下,我们正在重新创建一个面部,因此损失函数将比较生成的面部与原始面部,并计算每个像素与正确答案的差距。这为每个像素提供了一个简单易读的数字。查看每个像素有点困难,所以接下来,我们的损失将所有像素的平均值(均值)计算出来。这给出了 AI 整体偏离程度的一个单一数字。最后,这个数字被平方。这使得大的差异更加突出,并且已经证明这有助于模型更快地达到良好的结果。

还有其他损失函数,如平均绝对误差MAE),它去除了 MSE 中的平方,或者结构相似性,它使用结构的相似性作为衡量标准。实际上,生成对抗网络GANs),这是机器学习领域的热门词汇,只是用一个提供可训练损失函数的另一个模型替换了自编码器的静态损失,并将两个模型放在一个竞争中,看哪个模型能更好地完成其工作。

  1. 接下来,如果启用,我们将所有内容移动到 GPU 上:

    if device == "cuda":
      encoder = encoder.cuda()
      decodera = decodera.cuda()
      decoderb = decoderb.cuda()
      img_tensora = img_tensora.cuda()
      img_tensorb = img_tensorb.cuda()
      mask_tensora = mask_tensora.cuda()
      mask_tensorb = mask_tensorb.cuda()
    

如果之前启用了cuda,我们需要将模型和变量移动到 GPU 上,以便我们可以处理它们。因此,在这里,我们检查是否启用了cuda,如果是,我们将它们中的每一个移动到 GPU 上。

遍历训练

为了训练模型,我们需要遍历所有数据。我们称这个为训练循环。

  1. 首先,我们创建一个进度条并开始训练循环:

    pbar = tqdm(range(opt.iterations))
    for iteration in pbar:
    

我们再次使用tqdm来显示进度条。在这里,我们传递一个迭代次数的范围给tqdm,以便它可以自动更新进度条,并将进度条分配给一个变量。然后,我们从该变量开始循环,通过调用tqdm在变量中暴露的函数来提供更多进度条信息。

  1. 接下来,我们加载一组随机图像:

    images = random.sample(imagesa, opt.batchsize)
    for imgnum, imagefile in enumerate(images):
      img = cv2.imread(imagefile)
      img = cv2.resize(image, (64, 64))
      mask = cv2.imread(imagefile.replace("aligned", "mask"), 0)
      mask = cv2.resize(mask, (64, 64))
      if np.random.rand() > .5:
        image = cv2.flip(img, 1)
        mask = cv2.flip(mask, 1)
      img_tensor = torch.tensor(img[...,::-1]/255).permute(2,0,1)
      mask_tensor = torch.where(torch.tensor(mask) > 200, 1, 0)
      if device == "cuda":
        img_tensor = img_tensor.cuda()
        mask_tensor = mask_tensor.cuda()
      img_tensora[imgnum] = img_tensor
      mask_tensora[imgnum] = mask_tensor
    

这个块从a集中加载一系列图像,以便模型进行训练。为此,我们从我们之前生成的文件列表中获取与批处理大小相同的随机样本。

接下来,我们为每个图像遍历一个循环。循环首先读取面部图像并将其调整大小到 64x64。然后,它通过将文件名中的"aligned"单词替换为"mask"来对掩码图像执行相同的操作,这与掩码文件名相匹配。掩码也被调整大小以匹配训练图像。

接下来,我们随机以 50%的概率水平翻转图像。这是从数据集中获得更多多样性的极其常见的方法。由于人脸通常非常对称,我们通常可以翻转它们。在这里我们使用 50%的概率,这给了图像翻转或不翻转相等的可能性。由于我们有掩码,如果我们翻转图像,我们也必须翻转掩码。

接下来,我们将图像和掩码从图像数组转换为张量。为此,我们将图像从[...,::-1]转换。这也可以再次执行以将其转换回 BGR 顺序(我们稍后将会这样做)。掩码比较简单,因为我们不关心它的颜色数据,所以我们只需检查像素数据是否大于 200;如果是,我们在张量中放入1;如果不是,我们放入0

接下来,我们检查cuda是否启用;如果是,我们将我们刚刚创建的张量移动到 GPU 上。这把所有东西都放在了同一个设备上。

最后,我们将图像移动到我们将用于训练模型的张量中。这使得我们可以批量处理图像以提高效率。

  1. 然后,我们对另一组图像做同样的处理:

    images = random.sample(imagesb, opt.batchsize)
    for imgnum, imagefile in enumerate(images):
      img = cv2.imread(imagefile)
      img = cv2.resize(image, (64, 64))
      mask = cv2.imread(imagefile.replace("aligned", "mask"), 0)
      mask = cv2.resize(mask, (64, 64))
      if np.random.rand() > .5:
        image = cv2.flip(img, 1)
        mask = cv2.flip(mask, 1)
      img_tensor = torch.tensor(img[...,::-1]/255).permute(2,0,1)
      mask_tensor = torch.where(torch.tensor(mask) > 200, 1, 0)
      if device == "cuda":
        img_tensor = img_tensor.cuda()
        mask_tensor = mask_tensor.cuda()
      img_tensorb[imgnum] = img_tensor
      mask_tensorb[imgnum] = mask_tensor
    

这段代码与上一段代码相同,但重复用于b集的图像。这为我们创建了第二组图像,准备进行训练。

教导网络

现在是执行训练网络的步骤的时候了:

  1. 首先,我们清除优化器:

    Encoder_optimizer.zero_grad()
    decodera_optimizer.zero_grad()
    

PyTorch 在如何让你构建模型和训练过程方面非常灵活。正因为如此,我们需要告诉 PyTorch 清除a侧解码器。

  1. 然后,我们将图像通过编码器和解码器:

    Outa = decodera(encoder(img_tensora))
    

这段代码将我们之前创建的图像张量通过编码器,然后通过解码器,并将结果存储以供比较。这实际上是 AI 模型,我们给它一个图像张量,并得到一个图像张量作为输出。

  1. 接下来,我们计算损失并将其传递给优化器:

    Lossa = loss_function(
      outa * mask_tensora, img_tensora * mask_tensora)
    lossa.backward()
    encoder_optimizer.step()
    decodera_optimizer.step()
    

这段代码执行剩余的训练,计算损失,然后让优化器在模型上执行反向传播以更新权重。我们首先将输出图像和原始图像传递给损失函数。

为了应用掩码,我们用掩码乘以图像。我们在这里这样做而不是在将图像传递给模型之前,因为我们可能没有最好的掩码,而且最好是在整个图像上训练神经网络,然后再应用掩码。

接下来,我们在损失变量上调用backward。我们可以这样做,因为变量实际上仍然是一个张量,张量会跟踪在训练模式下对其发生的所有操作。这使得损失可以回传到所有步骤,直到原始图像。

最后一步是调用优化器的step函数。这会回过头来更新模型权重,以便下一次迭代应该更接近正确的结果。

  1. 接下来,我们做同样的事情,但针对b解码器:

    encoder_optimizer.zero_grad()
    decoderb_optimizer.zero_grad()
    outb = decoderb(encoder(img_tensorb))
    lossb = loss_function(
      outb * mask_tensorb, img_tensorb * mask_tensorb)
    lossb.backward()
    encoder_optimizer.step()
    decoderb_optimizer.step()
    

我们再次使用 b 图像和解码器进行相同的过程。记住,我们使用相同的编码器为两个模型,所以它实际上在 b 解码器训练的同时再次进行训练。这是 deepfakes 可以交换面部的一个关键部分。两个解码器共享一个编码器,这最终为两个解码器提供了重新创建各自面部所需的信息。

  1. 接下来,我们使用关于这次迭代数据损失的进度条信息更新进度条:

    pbar.set_description(f"A: {lossa.detach().cpu().numpy():.6f} "
      f"B: {lossb.detach().cpu().numpy():.6f}")
    

由于损失函数为优化器输出一个数字,我们也可以向用户显示这个数字。有时损失被深度伪造者用作模型训练完成程度的估计。不幸的是,这实际上不能衡量模型将一个面部转换成另一个面部的好坏;它只评估它重新创建给定相同面部的好坏。因此,这是一个不完美的衡量标准,不应该依赖。相反,我们建议稍后生成的预览用于此目的。

保存结果

最后,我们将保存我们的结果:

  1. 首先,我们将检查是否应该触发保存:

    if iteration % opt.save_freq == 0:
      with torch.no_grad():
        outa = decodera(encoder(img_tensora[:1]))
        outb = decoderb(encoder(img_tensorb[:1]))
        swapa = decoderb(encoder(img_tensora[:1]))
        swapb = decodera(encoder(img_tensorb[:1]))
    

我们希望定期保存 – 一个迭代可能不到一秒,但保存可能需要几秒钟的磁盘写入时间。因此,我们不想保存每个迭代;相反,我们希望在设置数量的迭代后定期触发保存。不同的计算机运行速度不同,所以我们允许您使用参数设置保存频率。

我们想要与当前权重一起保存的一件事是预览图像,这样我们就可以了解模型在每次保存状态下的表现。因此,我们将使用神经网络,但我们不希望在执行此步骤时进行训练。这正是 torchtorch.no_grad 上下文的原因。通过在这个上下文中调用我们的模型,我们不会进行训练,只是从网络中获取结果。

我们使用来自两个面部的图像样本调用每个解码器。这使我们能够比较重新创建的面部以及生成的交换。由于我们只想预览图像,我们可以丢弃除了第一个图像之外的所有图像,将其用作当前训练阶段的样本。

  1. 接下来,我们创建样本图像:

    example = np.concatenate([
      img_tensora[0].permute(1, 2, 0).detach().cpu().numpy(),
      outa[0].permute(1, 2, 0).detach().cpu().numpy(),
      swapa[0].permute(1, 2, 0).detach().cpu().numpy(),
      img_tensorb[0].permute(1, 2, 0).detach().cpu().numpy(),
      outb[0].permute(1, 2, 0).detach().cpu().numpy(),
      swapb[0].permute(1, 2, 0).detach().cpu().numpy()
      ],axis=1)
    

我们需要从所有部分创建我们的样本图像。为此,我们需要将所有图像张量转换成一个单独的图像。我们使用 np.concatenate 沿着宽度轴将它们全部连接成一个数组。为此,我们需要将它们全部按图像顺序排列并转换为 NumPy 数组。我们首先通过选择第一个来丢弃批处理维度。然后我们使用 permute 来重新排列每个张量,使得通道是最后一个。然后我们使用 detach 从张量中移除任何梯度。然后我们可以使用 cpu 将权重带回 CPU。最后,我们使用 numpy 完成将它们转换为 NumPy 数组的转换。

  1. 接下来,我们写入预览图像:

    cv2.imwrite(
      os.path.join(opt.out_path, f"preview_{iteration}.png"),
      example[...,::-1]*255)
    

这部分代码使用 OpenCV 的cv2.imwrite来将预览图像写入 PNG 文件。我们将其放在输出路径中,并根据这是哪个迭代来命名。这样,我们可以保存每个迭代的预览,并跟踪网络随时间的变化。要实际写入可用的图像,我们必须将颜色空间转换回 OpenCV 期望的 BGR,然后乘以255以得到适合整数空间的输出。

  1. 接下来,我们将权重保存到文件中:

    torch.save(encoder,
      os.path.join(opt.out_path, "encoder.pth"))
    torch.save(decodera,
      os.path.join(opt.out_path, "decodera.pth"))
    torch.save(decoderb,
      os.path.join(opt.out_path, "decoderb.pth"))
    

在这里,我们通过调用torch.save并指定输出路径和想要用于保存权重的文件名,来保存编码器和两个解码器的权重。PyTorch 会自动以其本地的pth格式将它们保存到文件中。

  1. 最后,我们再次保存:

    torch.save(encoder,
      os.path.join(opt.out_path, "encoder.pth"))
    torch.save(decodera,
      os.path.join(opt.out_path, "decodera.pth"))
    torch.save(decoderb,
      os.path.join(opt.out_path, "decoderb.pth"))
    

对于我们的最后一步,我们重复保存代码。但实际上,这是在训练循环之外完成的。这里之所以这样做,只是为了以防训练循环在不会触发保存的迭代次数上结束。这样,无论用户选择了什么参数,模型都至少会被保存一次。

作者注记

对于任何花过时间编码的人来说,这可能看起来很明显,但重复一遍是值得的。你应该始终考虑在开发过程中做出的选择可能会导致的坏结果或意外结果,并在设计中考虑到这些因素。显然,考虑一切是不可能的,但有时即使是在退出前简单复制保存操作,“以防万一”,也能拯救某人的日子(或者在一个非常长的训练会话中,甚至更多时间)。

摘要

在本章中,我们训练了一个神经网络来交换面部。为此,我们必须探索卷积层是什么,然后构建一个基础的上采样层。然后我们构建了三个网络。我们构建了编码器,然后是两个解码器。最后,我们训练了模型本身,包括加载和准备图像,并确保我们保存了预览和最终的权重。

首先,我们构建了将要训练以执行面部交换过程的神经网络的模型。这被分解为上采样器、共享编码器和两个解码器。上采样器用于通过将深度转换为更大的图像来增加图像的大小。编码器用于将面部图像编码到更小的编码空间中,然后我们将其传递给解码器,解码器负责重新创建原始图像。我们还研究了激活层,以了解为什么它们是有帮助的。

接下来,我们介绍了训练代码。我们创建了网络模型的实例,将权重加载到模型中,并在有可用的情况下将它们放在 GPU 上。我们探讨了优化器和损失函数,以了解它们在训练过程中的作用。我们加载并处理图像,以便它们可以通过模型进行训练。然后,我们介绍了训练本身,包括如何获取损失并使用优化器将其应用于模型。最后,我们保存了预览图像和模型权重,以便我们可以再次加载它们。

在下一章中,我们将使用我们的训练模型并将其用于“转换”视频,交换面部。

练习

  1. 选择学习率并不是一个已经解决的问题。没有一种“正确”的学习率。如果你将学习率增加到 10 倍,或者减少到原来的 1/10 会发生什么?如果你从一个大的学习率开始,然后在训练一段时间后减少它呢?

  2. 我们使用了与原始代码相同的损失函数和优化器,该代码最初于 2018 年发布,但现在有很多选项当时并不可用。尝试用 PyTorch 广泛的损失函数集合中的其他函数替换损失函数(pytorch.org/docs/stable/nn.html#loss-functions)。其中一些可能不需要任何更改就能工作,但有些可能根本不适合我们的情况。尝试不同的函数,甚至尝试损失函数的组合!

  3. 我们定义了一个模型,它从 64x64 像素的图像下采样并重新创建了相同的图像。但通过一些调整,这个相同的架构可以创建 128x128 或 256x256 像素的图像。你将如何修改模型来实现这一点?你应该增加(和大小)卷积塔的层数并保持瓶颈不变,增加瓶颈的大小但保持层数不变,还是改变卷积层的内核大小和步长?甚至可能输入一个 64x64 像素的图像,输出一个 128x128 像素的图像。所有这些技术都有其优点和缺点。尝试每一种,看看它们有何不同。

  4. 我们目前只训练了两个面部,但理论上可以做得更多。尝试修改训练代码,使用三个不同的面部而不是两个。你需要做出哪些改变?你将进行哪些修改,以便可以一次性训练任意数量的面部?

  5. 自从深度伪造首次发布以来,已经创建了众多不同的模型。Faceswap 实现了许多较新和更高级的模型,但它们是用 Keras 为 Tensorflow 编写的,如果不进行移植,则无法在这个 PyTorch 分支中运行。请查看 GitHub 仓库中的 Faceswap 模型(github.com/deepfakes/Faceswap/tree/master/plugins/train/model)。将此模型与Original.py中的模型进行比较,后者实现了相同的模型。现在使用它来查看dfaker.py的不同之处。残差层通过添加一个额外的卷积层和一个add层来工作,这个add层只是将两个层组合在一起。你能在本代码库中复制dfaker模型吗?其他模型又如何呢?

EBSCOhost - 2023 年 11 月 27 日早上 6:20 打印。所有使用均受www.ebsco.com/terms-of-use条款约束。

第七章:将人脸交换回视频

在本章中,我们将通过使用上一章训练的模型将视频转换为交换人脸来完成 deepfake 过程。

转换是 deepfake 的最后一个步骤,这是将新人脸实际放置到现有视频中的部分。这要求你已经有了一个通过提取过程在第五章提取人脸中完全处理过的视频,并使用第六章训练 Deepfake 模型中训练好的模型。

在本章中,我们将涵盖以下主题:

  • 准备转换视频

  • 熟悉转换代码

  • 从图像创建视频

技术要求

对于本章,你需要设置一个conda环境。如果你在早期章节中已经设置了这个环境,那么相同的conda环境将可以正常工作。要进入conda环境,你可以运行以下命令:

conda activate deepfakes

如果你还没有创建conda环境来运行代码,建议你前往 Git 仓库并遵循那里的说明。你可以在这里找到完整的仓库github.com/PacktPublishing/Exploring-Deepfakes

准备转换视频

转换不仅仅是一个“一次性的”脚本。它要求你将视频转换成一系列帧,并在这些帧上运行C5-face_detection.py。这会以正确的形式获取转换过程所需的数据。转换过程将需要提取每个帧的完整信息,以及由提取过程生成的face_alignments.json文件:

图 7.1 – 已提取的文件夹示例。注意由提取过程创建的文件夹

图 7.1 – 已提取的文件夹示例。注意由提取过程创建的face_images文件夹

如果你还没有对你想要转换的视频进行提取过程,那么你应该回到第五章提取人脸,并提取视频。

我们需要这样做,因为这是模型知道要将哪些人脸进行转换的方式。AI 可以检测帧中的所有人脸,但不知道哪些应该被交换,这意味着所有的人脸都会被交换。通过运行提取过程并从提取人脸的文件夹中清理掉我们不希望交换的人脸,我们可以控制哪些人脸会被交换。

此外,你可能还想将你要转换的帧包含在你的训练数据中,我们称之为“拟合训练”,这确保你的模型对你要转换的确切帧有一些经验。为此,回到第六章训练 Deepfake 模型,并将你的模型的“A”侧指向包含你将要用于转换的帧的目录。

作者注记

如果你感兴趣的是“即时”转换过程,该过程交换所有人脸,你可以在本章末尾的练习页面上查看,那里我们提出了那个确切的问题。实际上,本节中的每一章都有一个练习列表,供你亲自动手,编写自己的深度伪造代码以获得经验。

接下来,让我们看看转换代码。

亲自动手编写转换代码

就像本节中其他章节的内容一样,我们将逐行分析代码,讨论其工作原理和所执行的操作。

初始化

在这里,我们将初始化并准备代码以运行转换过程:

  1. 就像所有的 Python 代码一样,我们将从导入开始:

    import os
    from argparse import ArgumentParser
    import json_tricks
    import torch
    import cv2
    import numpy as np
    from tqdm import tqdm
    import face_alignment
    from face_alignment.detection.sfd import FaceDetector
    from face_alignment import FaceAlignment, LandmarksType
    from lib.bisenet import BiSeNet
    from lib.models import OriginalEncoder, OriginalDecoder
    

这些库都是我们在前面的章节中已经见过的。这是因为转换过程实际上并没有做与我们之前所做太大的不同。当我们通过代码将人脸转换回原始图像时,我们会看到这一点。

  1. 接下来,我们将检查我们是否从命令行运行:

    If __name__ == "__main__":
      """ Process images, replacing the face with another as trained
          Example CLI:
          ------------
          python C7-convert.py "C:/media_files/"
      """
    

此代码实际上并没有做任何事情,但这是设置 Python 文件在调用时运行的常见方式。它允许你将脚本导入到其他脚本中而不运行那些命令。

  1. 接下来,我们将解析脚本的参数:

      parser = ArgumentParser()
      parser.add_argument("path",
        help="folder of images to convert")
      parser.add_argument("--model-path",
        default="model/",
        help="folder which has the trained model")
      parser.add_argument("--cpu",
        action="store_true",
        help="Force CPU usage")
      parser.add_argument("--swap",
        action="store_true",
        help="Convert to the first face instead of the second")
      parser.add_argument("--json-path",
       default="$path/face_images/face_alignments.json",
       help="path to the json data from the extract")
      parser.add_argument("--export-path",
        default="$path/convert/",
        help="folder to put images (swaps $path with input path)")
    

此代码使用 Python 的标准 ArgumentParser 库来解析命令行参数。这让我们可以为某些选项设置默认值,并在需要时更改它们:

  Opt = parser.parse_args()
  opt.export_path = opt.export_path.replace("$path", opt.path)
  opt.json_path = opt.json_path.replace("$path", opt.path)
  main(opt)

在这里,我们处理参数,添加适用变量的路径,并将这些参数传递给 main() 函数,该函数将实际处理转换。

我们调整路径变量以添加默认路径。这让我们可以在数据文件夹的子文件夹中拥有 JSON 和导出文件夹。否则,每个都必须单独指定,并且可能位于非常不同的位置。如果你想要指定特定的文件夹,你仍然可以这样做,但默认设置有助于保持事物组织有序。

  1. 我们现在回到主函数的开始部分:

    def main(opt):
    

再次强调,这只是一个执行工作的主函数。它实际上并没有做任何事情,除了组织我们的代码并允许我们保持正常的“Pythonic”操作。

  1. 我们的下一步是确保我们将要写入的文件夹存在:

    if not os.path.exists(opt.export_path):
            os.mkdir(opt.export_path)
    

本节检查导出路径并确保它已经存在,如果不存在则创建它。

加载 AI

下一步是将 AI 加载到它需要运行的设备上:

  1. 首先,我们检查 cuda 是否可用以及是否给出了 CPU 覆盖:

    device = "cuda" if torch.cuda.is_available() and not opt.cpu else "cpu"
    

此代码检查 PyTorch 中是否启用了 cuda,如果是,并且用户没有通过命令行开关禁用它,那么将相应地启用 cuda 以供其余代码使用。

  1. 接下来,我们使用以下代码构建 AI 模型:

    encoder = OriginalEncoder()
    decoder = OriginalDecoder()
    

此代码建立编码器和解码器。与训练时不同,我们只需要一个解码器。这是因为训练需要两个面部才能成功学习,但一旦训练完成,我们只需要用于交换的面部的解码器。

  1. 加载模型权重是下一步:

    encoder.load_state_dict(torch.load(
      os.path.join( opt.model_path, "encoder.pth")).state_dict())
    if not opt.swap:
      decoder.load_state_dict(torch.load(
       os.path.join(opt.model_path, "decoderb.pth")).state_dict())
    else:
      decoder.load_state_dict(torch.load(
       os.path.join(opt.model_path, "decodera.pth")).state_dict())
    

此代码从训练模型中加载权重。我们首先加载编码器权重。这些总是相同的,因此它们从 encoder.pth 文件中提取,该文件包含编码器的训练权重。

对于解码器,默认情况下,我们想要加载“b”权重,这些权重存储在 decoderb.pth 文件中,但你可能想要将“a”面部交换到“b”图像中,因此我们包含了一个命令行开关,该开关将从 decodera.pth 文件中加载“a”权重。这些权重工作方式相同,并且与最初用于训练的面部相关联。由于我们包含了 swap 标志,所以确切的顺序并不重要,但只有一个方向可以是默认的,所以“b”面部到“a”图像的假设,除非在这里被覆盖。

无论加载哪个解码器,我们首先加载权重,然后将它们分配给模型的状态字典。PyTorch 处理将字典加载为矩阵以及准备处理张量的所有具体细节。

  1. 接下来,我们将模型移动到 GPU:

    If device == "cuda":
      encoder = encoder.cuda()
      decoder = decoder.cuda()
    

如果设备设置为 cuda,我们将模型加载到 GPU 上。为此,我们告诉 PyTorch 在模型上使用 cuda,这将处理将模型从 CPU 移动到 GPU 的所有繁琐细节。

准备数据

接下来,我们需要将数据加载到 PyTorch 期望的格式中:

  1. 首先,我们从文件中加载对齐数据:

    with open(os.path.join(json_path), "r", encoding="utf-8") as alignment_file:
      alignment_data = json_tricks.loads( alignment_file.read(), encoding="utf-8")
      alignment_keys = list(alignment_data.keys())
    

此代码从提取过程中保存的 JSON 文件中加载对齐数据。此文件包含我们需要的所有信息,以便能够从原始图像中提取面部,将其转换,并将其粘贴回图像。这使用提取信息而不是即时执行,因为当创建训练数据时,该信息已经生成,重新使用这些数据可以节省大量时间,同时还可以实现数据的清理和手动编辑。

您可以指定要加载的 JSON 文件,其中包含您正在转换的图像数据,但如果留空,则将检查默认位置,除非您在提取过程中更改了它,否则应该找到该文件。

我们再次使用 json_tricks,因为它对 NumPy 数组的处理非常强大,可以自动将数组加载回正确的数据类型和矩阵形状。

小贴士

虽然编辑这些对齐工具的说明超出了本书的范围,但 Faceswap 项目确实包括高级对齐修改工具,包括一个高级的“手动”工具,该工具允许通过点击和拖动编辑地标和面部。

  1. 下一步是获取要转换的图像列表:

    list_of_images_in_dir = [file for file in os.listdir(opt.path)
     if os.path.isfile(os.path.join(opt.path, file))
     and file in alignment_keys]
    

此代码从文件夹中加载所有图像,然后通过丢弃 JSON 数据文件中加载的对齐数据中不存在的任何图像来过滤图像。这确保了我们拥有转换图像所需的所有信息,因为即使文件夹中添加了新图像,我们也需要提取信息才能转换文件。

转换循环

在这里,我们开始循环,逐个处理每个单独的图像以将它们转换:

  1. 我们现在进入循环并加载图像。

    for file in tqdm(list_of_images_in_dir):
      filename, extension = os.path.splitext(file)
      image_bgr = cv2.imread(os.path.join(opt.path, file))
      image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
      width, height, channels = image_bgr.shape
      output_image = image_rgb
    

此代码加载图像并为其使用做准备。首先,它将文件名和扩展名存储到变量中,以便我们稍后再次使用。然后,它以蓝、绿、红BGR)颜色顺序加载文件,并将其转换为 AI 期望的红、绿、蓝RGB)顺序的图像。然后,它将宽度、高度和颜色通道存储到变量中,以便我们稍后再次使用。最后,它创建输出图像的工作副本,以便我们可以交换该图像中的任何面部。

  1. 接下来,我们开始另一个循环,这次是针对面部的:

    for idx, face in enumerate(alignment_data[file]['faces']):
      aligned_face = cv2.warpAffine(image_rgb, face["warp_matrix"][:2], (256, 256))
      aligned_face_tensor = torch.tensor(aligned_face/255,
        dtype=torch.float32).permute(2, 0, 1)
      aligned_face_tensor_small = torch.nn.functional.interpolate(
        aligned_face_tensor.unsqueeze(0), size=(64,64),
        mode='bilinear', align_corners=False)
    if device == "cuda":
      aligned_face_tensor_small = aligned_face_tensor_small.cuda()
    

此循环将处理对齐文件中找到的每个面部,并交换其中找到的面部。

它首先使用在对齐文件中保存的预计算变形矩阵从帧中提取面部。这个矩阵使我们能够对齐面部并生成一个 256x256 的图像。

接下来,我们将面部图像转换为张量,并将通道移动到 PyTorch 期望的顺序。张量转换的第一部分是将 0-255 的整数范围转换为 0-1 的标准范围。我们通过除以 255 来实现这一点。然后我们使用permute重新排列矩阵,因为 PyTorch 希望通道首先出现,而 OpenCV 将它们放在最后。

接下来,我们创建张量的一个较小的 64x64 副本,这是我们实际上要输入到模型中的。由于我们一次处理一张图像,我们实际上是在处理一个大小为 1 的批次,但我们需要在张量上使用unsqueeze来创建张量的批次通道。这仅仅添加了一个大小为 1 的新维度,其中包含我们想要转换的图像。

最后,如果我们使用cuda,我们将较小的对齐面部张量移动到 GPU 上,以便我们可以在那里通过模型。

  1. 然后,我们将图像通过 AI:

    with torch.no_grad():
      output_face_tensor = decoder( encoder(
        aligned_face_tensor_small ))
    

此代码执行实际的 AI 交换,它的大小相当令人惊讶。

我们首先通过使用torch.no_grad()告诉 PyTorch,我们希望在这一点上运行 AI 而无需跟踪梯度。这样可以节省大量 VRAM 并加快转换速度。这在这里不是严格必要的,但养成这个习惯是好的。

接下来,我们将包含 64x64 面部的张量通过编码器,然后通过解码器来获取交换面部。编码器的输出直接输入到解码器,因为我们不需要对潜在编码做任何事情。

  1. 在这里,我们将掩码应用于输出:

    output_face_tensor = torch.nn.functional.interpolate(
      output_face_tensor, size=(256,256))
    mask_img = cv2.imread(os.path.join(extract_path,
      f"face_mask_{filename}_{idx}.png"), 0)
    mask_tensor = torch.where(torch.tensor(mask_img) >
      200, 1, 0)
    output_face_tensor = (output_face_tensor.cpu() *
      mask_tensor) + ( aligned_face_tensor.cpu() * (1 –
      mask_tensor))
    

我们希望应用这个面具,这样我们就不至于在面部周围交换一个大的方形噪声区域。为此,我们将加载面具图像,并使用它来仅从交换中裁剪出面部。

首先,我们将交换的面部图像调整大小到 256x256 像素。这样做是因为面具是一个 256x256 像素的图像,以更高的分辨率应用它有助于在边缘获得最佳细节,而不是将面具下采样到 64x64 像素。

接下来,我们加载面具图像。为此,我们使用对齐的面部文件名生成面具图像文件名。然后,我们使用 OpenCV 的图像读取器将其作为灰度图像加载:

图 7.2 – 面具图像的一个示例

图 7.2 – 面具图像的一个示例

然后,该图像被转换成一个张量,使用一个截止点,如果灰度面具图像的像素值高于 200(在 0-255 的范围内),则将其视为1;否则,将其视为0。这给我们提供了一个干净的二进制面具,其中1值表示要交换的面部,而0表示不重要的背景。然后我们可以使用这个面具将交换的面部粘贴回原始图像。

最后,我们将面具应用到图像上。这是通过将输出面部乘以面具,并将原始面部乘以面具的逆来完成的。实际上,这是将交换结果中的面部与从预交换对齐图像中提取的其余图像结合在一起。

  1. 接下来,我们将面部放回到原始图像中:

    output_face = (output_face_tensor[0].permute(1,2,0).numpy() *
      255).astype(np.uint8)
    output_image = cv2.warpAffine(output_face,
      face["warp_matrix"][:2], (height, width), output_image,
      borderMode = cv2.BORDER_TRANSPARENT,
      flags = cv2.WARP_INVERSE_MAP)
    

这个代码部分完成了面部循环。为此,我们将面部重新应用到输出图像上。

首先,我们将面部张量转换回 OpenCV 可以处理的 NumPy 数组。为此,我们抓取张量中的第一个实例;这实际上移除了批处理大小维度。然后,我们将使用permute将通道移动到矩阵的末尾。然后我们必须乘以 255 以进入整数 0-255 的范围。最后,我们将变量转换为整数,使其在 OpenCV 中作为一个合适的图像使用。

然后,我们使用 OpenCV 的cv2.warpAffine和一些标志将面部以原始方向复制回原始图像。我们使用的第一个标志是cv2.BORDER_TRANSPARENT,这使得只有较小的对齐面部区域被改变;其余图像保持不变。如果没有这个标志,图像将只包括替换的面部方块;其余图像将是黑色。我们使用的另一个标志是cv2.WARP_INVERSE_MAP,它告诉cv2.warpAffine我们正在将图像复制回原始图像,而不是复制原始图像的一部分。

使用这两个标志,对齐的面部图像被放回到原始全尺寸图像的正确位置。我们这样做是通过原始图像的一个副本来完成的,这样我们就可以在图像上复制多个面部,如果找到了多个面部。

  1. 最后,我们输出带有面部交换的新图像:

    output_image = cv2.cvtColor(output_image, cv2.COLOR_RGB2BGR)
      cv2.imwrite(os.path.join(opt.export_path,
        f"{filename}.png"), output_image)
    

图像循环的最后一步是将图像写入内存中的单独图像文件。为此,我们首先将图像转换回 OpenCV 期望的 BGR 颜色顺序。然后,我们使用相同的原始文件名和 PNG 文件类型将文件写入提取路径。

图 7.3 – 原图(顶部)和交换图(底部)示例,包括布赖恩(左上角)和马特(右上角),作者

图 7.3 – 原图(顶部)和交换图(底部)示例,包括布赖恩(左上角)和马特(右上角),作者

现在我们已经对帧进行了转换,我们需要将图像转换回视频。让我们现在就做。

从图像创建视频

包含的转换代码会产生交换图像,但如果我们想创建视频,我们需要将输出组合成视频文件。这里有多个选项,具体取决于你想要包含的内容:

  • 以下仅包含图像:

    ffmpeg -i {path_to_convert}\%05d.png Output.mp4
    

此命令行将使用一些默认选项将所有帧转换为视频。Output.mp4文件将包含帧,但不会包含任何音频,并且将以默认的每秒 25 帧的帧率。对于来自电影源的视频,如蓝光或 DVD,这应该足够接近准确。如果视频看起来太快或太慢,那么你的帧率是不正确的,你应该查看下一个选项以匹配正确的帧率。

  • 在特定帧率下包含图像:

    ffmpeg -framerate {framerate} -i {path_to_convert}\%05d.png
      Output.mp4
    

此命令行将包括特定帧率的帧。帧率是你需要从原始视频中自己找到的。使用ffmpeg的一种方法是运行以下代码:

ffmpeg -i {OriginalVideo.mp4}

这将输出大量信息,其中大部分对我们来说可能没有用。我们需要做的是寻找包含“流”信息的行。它看起来可能像这样:

Stream #0:0(eng): Video: hevc (Main) (hvc1 / 0x31637668), yuvj420p(pc, bt470bg/bt470bg/smpte170m), 1920x1080, 13661 kb/s, SAR 1:1 DAR 16:9, 59.32 fps, 59.94 tbr, 90k tbn, 90k tbc (default)

这里重要的信息是它说59.32 fps。在这种情况下,我们想把59.32放入ffmpeg命令的帧率中。

此选项仍然不会包含任何音频。

  • 在视频中包含音频:

    ffmpeg -i {path_to_convert}\%05d.png -i {OriginalVideo.mp4}
      -map 0:v:0 -map 1:a:0 Output.mp4
    

此命令将在复制原始视频文件中的音频的同时转换视频。使用与音频完全相同的文件来对齐非常重要。如果音频没有正确对齐,你可能需要检查帧率和帧数。

摘要

在本章中,我们在一个包含图像的文件夹上运行了转换过程,使用训练模型替换了人脸。我们还把图像转换回视频,包括对帧率和音频的更改。

我们首先介绍了如何准备视频进行转换。转换过程需要从第五章提取人脸,以及从第六章训练深度伪造模型中创建的数据和训练好的 AI 模型。有了前几章的所有部分,我们就准备好转换了。

我们随后走过了转换过程的代码。这包括查看初始化部分,其中我们介绍了如何让 Python 脚本准备好运行。然后我们加载了 AI 模型,并在有 GPU 的情况下将它们设置好以在 GPU 上工作。接下来,我们准备好了数据,以便将每一帧中的面部进行转换。最后,我们运行了两个嵌套循环,处理了每一帧中的每一个面部,将它们交换到另一张面部。这一部分给我们留下了一个充满交换面部的文件夹。

之后,我们查看了一些命令,这些命令将交换面部图像文件夹转换成视频格式,这包括将每一帧放入视频中,确保帧率正确,如果需要,则复制音频。

在下一节中,我们将开始探讨 deepfakes 的潜在未来,下一章将探讨将我们学到的 deepfakes 技术应用于解决其他问题。

练习

  1. 我们使用遮罩从图像中裁剪出交换的面部,并将其复制到对齐的面部上。这意味着对齐图像中不是面部区域的部分也会得到较低的分辨率。一种修复方法是将遮罩应用于原始图像而不是对齐图像。为此,您需要分别对遮罩和对齐图像调用cv2.warpAffine,然后使用遮罩仅复制面部。您可能想查看 OpenCV 的warpAffine文档,网址为docs.opencv.org/3.4/d4/d61/tutorial_warp_affine.html

请注意,OpenCV 的文档基于 C++实现,Python 库中可能会有所不同。教程页面有一个Python按钮,可以切换教程以使用 Python 库。

  1. 我们依赖于预先提取的面部来进行转换。这是因为大量数据已经在提取过程中被处理,并且已经可用,允许您过滤掉不需要转换的图像/面部。但是,如果您正在运行大量视频或计划在实时视频上进行转换,允许在运行时进行转换可能是有意义的。为此,您可以将提取过程与转换结合起来,并在转换之前按需运行提取步骤。您可以在C5-extract.py中查看代码,并将适当的部分添加到转换过程中,以使其能够直接在图像上工作。

  2. 我们完全在图像上操作转换过程,但实际上 Python 可以直接与视频文件工作。为此,尝试安装并使用 PyAV 库,网址为github.com/PyAV-Org/PyAV,以直接读取和写入视频文件而不是图像。请记住,您可能需要考虑输出中的音频数据和帧率。

  3. 本章中使用的技术有一个问题是,替换进去的人脸在边缘处可能看起来相当明显。这是因为缺乏颜色匹配和边缘混合。这两种技术都可以改善替换的边缘。有许多颜色匹配技术可供选择;一个选项是直方图匹配 (docs.opencv.org/3.4/d4/d1b/tutorial_histogram_equalization.html)。你需要分别匹配 RGB 通道。边缘混合通常是通过模糊蒙版来完成的;你可以通过使用 OpenCV (docs.opencv.org/4.x/d4/d13/tutorial_py_filtering.html)来平滑蒙版图像来实现这一点。这可以使替换的锐利边缘变得不那么明显。

  4. 我们这里 AI 的结果仅限于 64x64 像素。有更新的模型可以达到更高的分辨率,但仍然受到可用 GPU 内存的严重限制,并且训练时间会更长。为了解决这个问题,你可以在将人脸返回到原始图像之前,通过 AI 放大器,如 ESRGAN (github.com/xinntao/Real-ESRGAN),或者专门用于人脸修复的工具,如 GFP-GAN (github.com/TencentARC/GFPGAN),来运行输出。看看你能否在将人脸返回到原始图像之前,通过这些模型来获得更高的质量结果。

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use条款约束。

第三部分:现在该往哪里去?

就像所有发明一样,深度伪造的发展只是开始。现在你已经知道了深度伪造是如何工作的,你可以将这项知识应用到哪里,以及你可以用它做什么?你可能会对技术的灵活性感到惊讶。

在本部分,我们将探讨一些假设性的项目,以及如何利用深度伪造技术使它们更容易实现,同时解决可能让普通开发者感到棘手的复杂问题(你当然不是那种人——毕竟,你买了这本书!)。然后,我们将提出一个终极问题:未来会带来什么?我们将通过观察生成式 AI 在近期可能的发展方向来尝试回答这个问题,包括考虑这些 AI 技术必须克服的限制和挑战。

本部分包括以下章节:

  • 第八章应用深度伪造的教训

  • 第九章生成式 AI 的未来

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use条款约束。

第八章:应用 Deepfakes 的教训

这本书中的技术可以用于比面部替换更多的用途。在本章中,我们将探讨一些如何将本书的教训和工具应用于其他领域的例子。我们将探讨如何调整和修改这些技术,以便以新的和独特的方式使用结果。

尤其是我们将探讨本书早期的一些技术,看看它们如何以新的方式使用。本章中的例子并不全面,而且总有更多的方式可以实现 deepfakes 带来的能力。在本章中,我们更关注技术而不是具体细节,但在研究技术时,我们将以新的方式探索以下内容:

  • 对其他类型的图像进行对齐

  • 遮罩图像的力量

  • 控制数据

技术要求

对于本章,有一个包含少量代码的部分,演示了如何使用非模块 Git 仓库供您自己使用。

虽然这不是本书的动手部分,但我们已包含与库PeCLR接口的代码:PeCLR。此代码也包含在本书的代码仓库中,并具有一些附加功能,包括可视化点,但仅作为示例,并不打算作为在您自己的项目中使用PeCLR的完整 API:

  1. 首先,打开 Anaconda 命令提示符。

  2. 在 Windows 上,点击开始并输入anaconda。这应该会显示以下选项:

图 8.1 – Anaconda 命令提示符

图 8.1 – Anaconda 命令提示符

点击此处,它将打开一个 Anaconda 提示符,用于以下所有命令。

  1. 接下来,我们需要克隆PeCLR库的副本:

    git clone https://github.com/dahiyaaneesh/peclr.git
    
  2. 下载模型数据。

该库包含所有预训练模型的副本,可在dataset.ait.ethz.ch/downloads/guSEovHBpR/找到。在浏览器中打开此链接并下载文件(如果此 URL 失败,请检查PeCLR库或书籍仓库中的任何更新链接)。

将文件提取到您本地PeCLR仓库中的data/models/文件夹内。

  1. 创建一个包含所有PeCLR要求的conda环境:

    conda create -n PeCLR
    conda activate PeCLR
    pip install -r requirements.txt
    

这将创建一个包含PeCLR运行所需所有库的 Anaconda 环境。

此外,这将安装一个 Jupyter 笔记本。Jupyter 笔记本是一个用于实时编码的有用工具。要运行一个单元格,点击它,然后按Shift + Enter或点击播放三角形按钮。Jupyter 将运行那一块代码,然后停止,让您可以随意更改代码并重新运行。

  1. PeCLR.ipynb文件复制到已克隆的仓库文件夹中。

如果您想跟随 Jupyter Notebook 文件,只需将文件从本书的仓库复制到您之前克隆的PeCLR文件夹中即可。这将让您免于重新输入所有内容。

  1. 打开 Jupyter Notebook 并使用浏览器访问:

    python -m jupyter notebook
    

这将运行 Jupyter Notebook。如果你在运行命令的同一台电脑上使用它,它也应该自动打开你的浏览器到正在运行的 Jupyter Notebook 实例,你就可以开始了。如果没有,你可以打开你喜欢的浏览器,并访问 http://:8888/tree 来访问它。

当我们进入下一节的“编写我们自己的界面”和“使用库”部分时,将解释此代码的使用方法。

对其他类型图像的对齐

对齐面部是使深度伪造工作的重要工具。没有面部的对齐,我们将面临极长的训练时间和巨大的模型来纠正面部。可以说,没有对齐,现代深度伪造在今天是无法实现的。

对齐通过消除神经网络确定面部在图像中的位置并适应面部可能出现的许多不同位置的需要,从而节省了时间和计算能力。通过预先对齐,AI 甚至不需要学习面部“是什么”就能完成其工作。这允许 AI 专注于学习手头的任务,例如生成逼真的面部表情或语音,而不是试图定位和纠正未对齐的面部。

除了提高训练过程的效率外,对齐面部还有助于提高最终深度伪造的质量和一致性。如果没有适当的对齐,生成的面部可能看起来扭曲或不自然,这可能会降低深度伪造的整体真实感。

事实上,对齐不仅适用于面部。你可以用它来对齐手、人、动物,甚至汽车和家具。实际上,任何你可以用定义的部分检测到的对象都可以进行对齐。为了使其工作,你需要以某种方式找到对齐的点。例如,对于手,这可能是指尖。

虽然这适用于任何对象,但我们将专注于一个单一示例案例。以下是如何对齐手的示例过程。其他对象也可以以相同的方式进行对齐。你只需要遵循相同的步骤,但将手替换为你想要对齐的任何对象。

寻找对齐器

首先,我们需要找到一种方法来识别我们想要对齐的手的点的位置。为此,我们需要做一些被称为姿态估计的事情。我们可以使用 YOLO (github.com/ultralytics/yolov5) 或其他对象检测工具来识别某些点,例如手指的尖端。你可能需要做一些启发式的工作来正确地排列它们,以便可以对齐。

然而,与其自己开发,我们可能可以使用一个为我们完成这项工作的库。当我想要找到一个执行特定任务的库或代码时,我首先会查看Papers with Code(paperswithcode.com/)。这个网站基于各种 AI 任务有各种各样的软件项目。在我们的例子中,他们有一个专门用于手部姿态估计的部分(paperswithcode.com/task/hand-pose-estimation),列出了各种基准。这些是代码已经测试过的测试。这让你不仅能看到会做你想要的事情的库,甚至还能看到“最佳”的。

目前,最好的结果是虚拟视图选择,位于github.com/iscas3dv/handpose-virtualview。不幸的是,这个项目有一个限制性的“禁止商业用途”许可证。因此,我们将跳过它,转到下一个,AWR:用于 3D 手部姿态估计的自适应加权回归,可以在github.com/Elody-07/AWR-Adaptive-Weighting-Regression找到。这个项目是 MIT 许可的,这是一个开放许可,允许您即使用于商业目的也可以使用软件,但它仅适用于深度图像。“深度”指的是图像中相机与对象之间的距离。这些图像对于手部检测等任务很有用,但不幸的是,它们需要特殊的相机或技术才能正确获取,所以我们也将跳过这个。

许多其他方法也仅处理深度图像。然而,如果我们继续浏览发布的选项,我们应该会遇到PeCLR:通过等变对比学习从单目 RGB 进行自监督 3D 手部姿态估计,它拥有 MIT 许可证,并且可以在标准的 RGB(彩色)照片上工作。您可以在github.com/dahiyaaneesh/peclr下载它。

作者注记

虽然在这个部分我们只是使用代码,将其视为库,但实际上,PeCLR代码(以及其他列出的项目)是作为学术论文的一部分发布的。作者并不打算贬低这项工作,因为学术工作推动了 AI 领域的许多创新。然而,这本书的这一部分是关于如何实现想法的,这意味着使用代码而不必 necessarily 关注创新。如果你对深入了解PeCLR具体做了什么感兴趣,我们建议你阅读论文,它链接在 Git 仓库的 readme 中。

使用库

PeCLR库包含进行手部检测和姿态估计所需的所有模型和工具,但不是所有在外部图像上运行的代码。不幸的是,这在学术研究风格的项目中很常见,这些项目通常更感兴趣的是你能验证他们已经发布的结果,而不是让你在新数据上运行它。因此,我们需要实际编写一些代码来将我们的图像通过他们的模型。

如果他们没有提供易于使用的 API,找到与现有代码接口的最佳位置可能很困难。由于PeCLR是一个学术项目,没有易于使用的 API,我们需要找到我们自己的位置来用我们的 API 替代品调用他们的代码。

编写我们自己的接口

在验证数据上运行模型的代码对于我们的情况只有部分可用,因为他们使用的数据集期望数据以某种格式存在,这很难用我们的数据重新创建。因此,我们将从头开始,并在我们的代码中调用模型。

让我们开始做这件事:

  1. 首先,我们将导入我们使用的所有库:

    import torch
    import torchvision.models as models
    import cv2
    from PIL import Image
    from matplotlib import pyplot as plt
    import numpy as np
    import os
    import json
    from easydict import EasyDict as edict
    from src.models.rn_25D_wMLPref import RN_25D_wMLPref
    

上述代码导入了我们将需要的所有库。其中大部分是我们之前使用过的标准库,但最后一个是我们使用PeCLR进行实际检测所使用的模型。我们已经导入了它,所以我们可以用图像调用它来运行模型。

  1. 接下来,我们将从PeCLR加载模型:

    model_path = 'data/models/rn50_peclr_yt3d-fh_pt_fh_ft.pth'
    model_type = "rn50"
    model = RN_25D_wMLPref(backend_model=model_type)
    checkpoint = torch.load(model_path)
    model.load_state_dict(checkpoint["state_dict"])
    model.eval().cuda()
    

上述代码从PeCLR提供的模型数据中加载模型。为此,首先,我们定义模型路径和类型。然后,我们将模型类型传递给生成适当的模型。接下来,我们加载检查点并将权重复制到模型中。最后,我们准备模型以进行评估,并将其设置为在 GPU 上运行。

  1. 接下来,我们将加载图像并为其准备模型:

    image=io.imread(
      "https://source.unsplash.com/QyCH5jwrD_A")
    img = image.astype(np.float32) / 255
    image_mean = np.array([0.485, 0.456, 0.406])
    image_std = np.array([0.229, 0.224, 0.225])
    img = np.divide((img - image_mean), image_std)
    img = cv2.resize(img, (224,224))
    img = torch.from_numpy(img.transpose(2, 0, 1))
    img = img.unsqueeze(0).float().cuda()
    

此代码准备图像。它通过首先使用SciKit图像加载器来加载图像,与OpenCV不同,它可以直接处理 URL 或本地文件。然后,它计算一个调整值,以将模型的坐标恢复到与完整图像大小匹配的坐标。它是通过将 224 除以图像的高度和宽度来做到这一点的。然后,我们将图像数据转换为范围在 0–1 之间的浮点数。然后,我们通过除以标准差并减去平均值来归一化图像。这使图像的范围降低到模型期望的范围。然后,我们将图像调整大小到 224 x 224,这是模型期望的图像大小。然后,我们将图像转换为张量,并按照 Pytorch 使用的顺序,以通道优先的方式获取它。最后,我们在前面添加一个维度来存储批次,并将其转换为 GPU 上的 32 位浮点数。

这一切都是为了准备图像,以便模型可以在其上运行。

  1. 接下来,我们在图像上运行模型,并从中获取 2D 坐标:

    with torch.no_grad():
        output = model(img, None)
    kp2d = output["kp25d"][:, :21, :2][0]
    height, width = image.shape[:2]
    kp2d[:,0] *= width / 224
    kp2d[:,1] *= height / 224
    

此代码首先将图像通过模型,而不生成训练梯度。为此,我们传递图像和None值,这将使用默认的相机内参矩阵。

作者注记

相机内参是一个术语,但它只是指你的相机的细节。在PeCLR的情况下,它需要一个矩阵来详细说明像素空间的大小,以便它可以从 2D 图像中尝试猜测深度信息。我们不需要深度信息,所以我们可以让PeCLR创建一个默认矩阵,而不是提供它。

接下来,代码只取了 2D 对齐点。因为我们是在 2D 空间中进行对齐,所以不需要 3D 点。如果我们处理的是深度图像,我们可能需要第三维度,但在这个场景中我们不需要,也不需要它。

接下来,由于模型被给了一个小的 224 x 224 图像,我们将调整这些坐标以匹配原始图像的宽度和高度。为此,我们将坐标除以 224,然后将结果乘以原始图像的大小。

使用地标进行对齐

在这种情况下,库将标记每个手指和拇指的关节和尖端以及手“中间”附近的一个点。不幸的是,手中间的点定义得不好,它可以从实际中间到手腕的任何地方,所以我们不想用它来进行对齐。关节和指尖的位置将更一致,因此我们可以使用这些来进行对齐过程:

图 8.2 – 使用 PeCLR 检测并标记的手(原始照片由 Kira auf der Heide 通过 Unsplash 提供)

图 8.2 – 使用 PeCLR 检测并标记的手(原始照片由 Kira auf der Heide 通过 Unsplash 提供)

小贴士

对齐器永远不会完全准确。有些可能变化很大,但这并不是每次都得到完美结果的问题。任何对齐,即使是错误的,对神经网络训练都有好处,因为它将数据规范化为更可靠的形式。在将数据提供给 AI 之前所做的任何工作,意味着 AI 少了一个需要浪费精力去做的任务。

一旦我们可以在图像上获得一些已知点,我们就可以缩放、旋转和裁剪图像,使其处于你想要的朝向,并通过检测器运行它以获得点列表。如果你可以,我建议运行多张图像并平均点。这样,你可以减少手部图像中的任何变化,并获得更好的对齐。

一旦你有了想要对齐的点,你可以使用它们通过Umeyama算法生成对齐的图像。该算法只需要两组点,一个已知的“对齐”集和一个你可以转换成对齐集的第二组。Umeyama 返回一个矩阵,你可以将其输入到仿射变换函数中,以获得最终的对齐图像。参见第五章提取人脸,以获取如何进行此操作的动手代码示例。

一旦我们有了对齐的手部图像,你就可以用它们做你计划要做的事情,无论是展示它们还是用于你的 AI 任务。甚至可能在你有了对齐数据后,你可以用它来训练你自己的姿态检测模型,以获得更好的对齐结果。这个过程,即使用 AI 处理的数据来喂养模型以使该模型变得更好,被称为自举,并且,在适当的监督下,是一种无价的技术。

对齐是深度伪造的关键部分,现在你可以在其他领域使用它们。接下来,我们将探讨如何使用遮罩来获取物体的干净剪影。

遮罩图像的力量

当你拍照时,你正在捕捉相机看到的一切。然而,你很可能对图像的每一部分都不感兴趣。如果你在度假,你可能会在瀑布前拍一张自拍照,虽然你重视自己和瀑布,但你可能对图像中的汽车或其他人不那么关心。虽然你无法在不添加东西填补空缺的情况下从你的度假照片中移除汽车,但有时,你可能只对主要主题感兴趣,并可能想要将其从图像的其余部分中剪裁出来。

在深度伪造中,我们可以使用遮罩来帮助我们从图像中移除面部,以便只替换面部而留下图像的其余部分。在其他 AI 任务中,你可能会有类似的需求,但想要剪裁的对象不同:

图 8.3 – 深度伪造过程中使用的遮罩示例

图 8.3 – 深度伪造过程中使用的遮罩示例

接下来,让我们看看其他类型的遮罩。

遮罩类型

在许多任务中,遮罩图像可能很有用。我们只将其用作称为合成步骤的一部分。这仅仅是遮罩功能的一部分。它可以用来引导修复,这是通过填充图像中的空白来擦除物体的过程。你还可以在图像被喂入 AI 之前对其进行处理,以确保 AI 可以专注于重要的部分。

为了对图像进行遮罩,你需要有一个想法,知道你想要遮罩图像的哪一部分。这被称为分割,并且有众多子领域。如果你想根据物体的类型进行分割,那么它被称为语义分割。如果你想要根据主题进行图像分割,那么它被称为实例分割。如果你有一个稳定的深度图,你甚至可以使用深度分割。不幸的是,决定你需要哪种类型的分割需要特别注意,以便找到正确的工具。

为你的物体找到可用的遮罩

PaddleSeg (github.com/PaddlePaddle/PaddleSeg) 这样的库有特殊的工具,可以让您进行多种类型的分割。它们甚至有一个交互式分割系统,可以让您像 Photoshop 的魔术棒工具一样“标记”您想要分割的内容。之后,您可能需要使用这些数据来训练一个能够在新的环境中分割特定类型对象的分割模型。

为了找到最佳的方法来分割你感兴趣的对象,你可能需要从搜索你想要分割和标记的项目开始。对于一些对象,如人脸、汽车、人物等,有现成的模型可以分割这些对象。

但是,如果你感兴趣的对象没有分割模型,你不必绝望。较新的模型如CLIP (github.com/openai/CLIP) 为我们开辟了全新的机会。CLIP 由一对连接语言和图像的 AI 模型组成。由于 CLIP 的共享性质,我们可以根据对象的文本描述来学习它们之间的差异。这意味着像CLIPseg (github.com/timojl/clipseg) 这样的库可以使用语言提示来分割图像中的对象。

检查一个示例

让我们来看一个例子。假设你想要计数停车场停放的汽车,并查看是否有空位,但你只有从上方拍摄的停车场网络摄像头的图像。为了做到这一点,你需要知道图像中哪些部分是汽车,哪些是空停车位。这个任务结合了语义分割和实例分割,但将它们一起使用。

第一步是在图像中标记出每个停车位,以定义您想要查看的位置。您可以在每个停车位中挑选一个单独的位置,或者通过整个区域来定义它们。无论如何,您可能需要手动完成这项工作,因为它们不太可能经常改变,从而证明让计算机来做是有必要的。

现在你已经知道图像中停车位的所在位置,你可以开始寻找汽车了。为了做到这一点,我们可能需要寻找一个训练有素的神经网络来完成这个任务。在我们的例子中,我们可以查看 Kaggle 用户 Tanishq Gautam 提供的这个示例:www.kaggle.com/code/ligtfeather/semantic-segmentation-is-easy-with-pytorch。这个页面提供了预训练模型以及如何分割汽车的详细指南。

虽然这个模型不做实例分割(为每辆车分配不同的“颜色”或标签),但我们仍然可以用它来计数汽车。我们可以这样做,因为我们已经确定我们是在停车场计数汽车,并且我们已经手动标记了每个停车位。

然后,我们可以简单地使用分割模型来检测任何汽车,并查看它们是否与停车位重叠。如果我们只为每个停车位标记一个点,我们就可以简单地检查每个点,看看它是否被分割为“汽车”或“背景”(一个用于我们未分割的任何事物的常用术语)。

如果我们标记了整个停车位区域,那么我们可能希望计算一个最小覆盖面积,以便将停车位视为“占用”。例如,我们希望确保一个停放在停车位上的摩托车被计算在内,但稍微超过线的汽车不应该被计算。你可以设置停车位面积的最小阈值,如果它被一个“汽车”填满超过那个阈值,那么你就可以标记整个停车位为占用。

还可以实现其他功能。例如,你甚至可以通过检查一个标记为“汽车”的区域是否不在停车位内来检测汽车是否在禁止区域。这可以用来自动提醒停车执法官员检查停车场。

现在我们已经很好地掌握了我们的掩码,让我们看看我们如何管理我们的数据。

控制数据

在 AI 社区中有一个常见的说法,那就是机器学习科学家的工作只有 10%是机器学习,90%是数据管理。这种说法,就像许多这样的说法一样,并不离真相太远。虽然每个机器学习任务都专注于模型的实际训练,但首先你必须将你的数据整理成可管理的形式,然后你才能开始训练。如果你的数据没有正确准备,数小时的训练可能会完全浪费。

在你开始训练模型之前,你必须决定你要用哪些数据来训练它。这些数据必须被收集、清理、转换为正确的格式,并通常准备好用于训练。通常,这涉及到大量的手动过程和验证。

定义你的规则

在手动过程中最重要的事情是确保所有你的数据都符合你的要求,并达到一致的质量水平。为此,你需要确切地定义“好的”数据意味着什么。无论你是标注数据还是收集大量数据,你应该有一个标准的方式来完成你所做的一切。

例如,假设你正在标注狗和猫的数据集,并且你希望将所有图片放入两个桶中的一个,一个桶包含所有狗的图片,另一个桶包含所有猫的图片。如果一个图片中同时包含猫和狗,你会怎么做?你会把它放入更显著的桶中吗?你会从你的数据集中排除它吗?你会编辑图片以移除其中一种动物吗?你会将其裁剪成两张图片,以便两种动物都进入数据集中吗?

在这些情况下,拥有一套一致的规则非常重要。这确保了你的数据适用于这些目的。你不想每次都让这些边缘情况以不同的方式发生,否则当你试图解决问题时,会打乱你的训练并引起混淆。

规则的演变

此外,制定一个计划,以应对你更改规则的情况是很重要的。随着你继续进行数据管理,你几乎不可避免地会发现,根据你发现的数据、你的训练过程进行得如何以及你的最终用例是否发生变化,你可能想要对规则进行一些调整或更改。

回顾我们的示例,让我们考虑这样一个情况:你决定排除包含猫和狗的任何图像,但只要图像中也包含猫或狗,其他动物都可以。当你决定想要将兔子添加到你的猫/狗检测器中时,会发生什么?这意味着不仅需要为兔子图像添加一个新的存储桶,而且还需要重新处理你已经检查过的所有现有图像,以确保任何包含兔子的猫或狗图像都被移除。如果你发现你的猫和狗存储桶中的豚鼠被标记为兔子怎么办?在管理你的数据时,这些过程都需要考虑。

处理错误

错误是不可避免的,少量不良数据进入你的数据集是不可避免的。然而,在几处无害的错误和足以完全无效化训练的大量错误之间有一条细线。因此,通常最好在标准流程中将数据审查作为第二(或第三)套眼睛。有时,这可能是不可能的,但在任何可能的情况下,它都是无价的。第二套眼睛可能会发现你数据或方法中的缺陷。如果你在狗数据中将一个特别奇怪外观的动物标记为正常,但第二个人将其识别为罕见的猫品种怎么办?

我还建议尽可能自动化你的数据收集。当你能够从循环中去除人类时,你不仅会节省时间,还能防止错误和失误。花在自动化数据过程上的时间几乎总是会在未来带来回报。即使你认为没有一种方法可以使自动化过程比手动操作更快,你也应该考虑你将避免的错误。写得好的自动化代码可以后来用于未来的项目。任何可以重用的东西都应该自动化,这样下次你需要完成它时,就有工具为你处理。

摘要

在本章中,我们探讨了如何将深度伪造的教训和技术应用于其他环境。首先,我们以手为例,探讨了如何对齐其他类型的图像。然后,我们考察了不同类型的面具,并考虑在停车场监控解决方案中使用它们。在此之后,我们研究了数据管理,并考虑了如何构建一个用于检测不同动物的数据集。

本章中介绍的新环境中应用技术的方法本身就是一个非常有价值的技巧,它可以帮助你在整个开发生涯中受益,尤其是如果你像现在的 AI 一样,打算在计算机能力的边缘工作。有时,成功项目和不可能的项目之间的唯一区别就是从以前的项目中借用的技术。

在下一章中,我们将探讨深度伪造和其他生成式 AI 的潜力和未来。

EBSCOhost - 2023 年 11 月 27 日 6:20 AM 打印。所有使用均受www.ebsco.com/terms-of-use条款约束。

第九章:生成式 AI 的未来

虽然有时我们可能感觉我们已经生活在深度伪造和 AI 生成图像的未来,但它们背后的技术实际上才刚刚开始起飞。随着我们向前发展,这些生成式 AI 的能力将变得更加强大。

本章不是无边的未来主义,而是将探讨具体的生成式 AI 及其改进之处。我们将检查以下技术及其如何改变。我们将讨论以下 AI 领域的未来:

  • 生成文本

  • 提升图像质量

  • 文本引导的图像生成

  • 生成声音

  • 深度伪造

生成文本

最近,随着 2022 年 OpenAI 在 ChatGPT 上的成功,文本生成模型在公众意识中产生了重大影响。然而,文本生成是 AI 最早的应用之一。Eliza 是 1966 年开发的第一个聊天机器人,那时除了技术倾向性最强的人之外,几乎没有人见过电脑。个人电脑甚至要到 5 年后的 1971 年才被发明。然而,只有最近才真正令人印象深刻的聊天机器人被开发出来。

近期发展

一种称为transformers的模型负责最近语言模型的激增。Transformers 是由一个称为注意力层的层组成的神经网络。注意力层的工作方式有点像聚光灯,聚焦于最可能重要的数据部分。这使得 transformers(以及使用注意力层的其他模型)可以更深而不失“焦点”(尽管这并不是实际的术语,但它是一个很好的比喻,用于描述这种效果)。

由于能够构建非常深的模型,transformers 在需要从复杂结构中提取意义的工作中表现出色。这对于语言任务和其他长序列来说是一个完美的匹配,其中单词的意义在很大程度上取决于周围的单词(想想“我吃了一个橙子水果”和“我吃了一个橙子”之间的区别)。Transformers 在翻译等任务中得到了广泛的应用,但它们在回答问题、总结信息和本节的主题:文本生成方面也非常出色。

大多数语言任务不仅需要“理解”文本的能力,还需要创造新文本的能力。例如,在摘要任务中,你不能简单地从文档中提取重要词汇并复制给用户。这些词汇将毫无意义,因为它们是任意顺序的,并且没有语言构建的基础上的重要上下文。因此,这些模型必须以正确构造的句子形式创建真实且可理解的输出,以便用户进行解释。

作者注记

关于是否可以用独特的人类理解概念来描述计算机所做的任何事情,存在很多争议。一个著名的例子是约翰·塞尔(John Searle)所谓的中国房间思想实验。

思想实验的基本原理是这样的:想象一个房间里有一排巨大的档案柜。在这些档案柜的每一个里,都有卡片将某个输入与某个输出配对。这个房间可以接收和输出写有汉字的卡片。在房间内部,有一个人(或某种东西)不懂中文,但通过将输入卡片与档案柜中的卡片匹配,然后将卡片上的输出字符复制到另一张卡片上,可以输出完美的中文。

在那个背景下,你能说机器(或机器内部的人或事物)“理解”中文吗?至于房间本身呢?这个问题在哲学家、计算机科学家、语言学家等众多领域引发了热烈的辩论。

构建句子

Eliza,那个第一个聊天机器人,通过简单地重复输入句子中的单词到预先精心编写的几个句子之一中来构建句子。这个过程将用户响应的部分重复回放是足够的,可以作为一个非常简单的弗洛伊德式顾问,许多人觉得这有助于他们度过一些困境。

今天的生成模型使用极其先进的句子结构来构建句子。它们这样做的方式与拼图类似。模型使用一个称为光束的概念来搜索最适合句子某个部分的单词,就像拼图解决者在一个零件集中搜索以找到最适合可用空间的零件一样。不幸的是,这里的比喻变得有些牵强。单一的光束只跟踪一个序列,而与正常的拼图不同,一个句子可以由几乎无限的单词组合构成。正因为如此,当句子完成时,最佳得分光束可能会改变,因此模型需要跟踪多个光束。更多的光束会导致生成的句子在语法和语义上(即结构和意义)都更好,但需要更多的计算,因为模型同时解决更多的句子。

文本生成的未来

随着我们进入未来,这些模型将在两个方面得到改进;计算可用性将增加,使得使用更大光束搜索的更大模型变得可行,而设计效率将允许设计出更好的模型,这些模型可以在相同的资源下做更多的事情。你可以将这想象成将箱子装进卡车:直到你需要一辆更大的卡车或找到一种使箱子变小的办法,你只能在卡车里装进这么多箱子。Transformer 是设计效率的例子,而计算机总是变得更快速、更好是一个计算可用性的例子。

文本生成似乎有无限的改进空间,但关于语言模型长期增长的可持续性存在疑问。有人担心我们可能很快就会达到大型语言模型(LLMs)的极限。存在多个可能限制模型的约束。

语言模型的第一项限制是,最大的模型已经需要数周或数月的时间来训练,分布在数千台强大的计算机上。这些类型模型的最大限制可能是经济因素。随着成本不断飙升,可能根本不会有足够的需求来继续扩大模型。

语言模型的第二个主要限制要难以克服得多:可能根本没有足够的数据来持续增长。大型语言模型(LLMs)需要大量的数据来学习语言,我们很快就会达到一个点,即它们可以训练整个人类书面历史的全部输出,但仍可能没有足够的数据来完全训练模型。Deepmind 发布了一项关于 LLMs 的研究(arxiv.org/abs/2203.15556),发现完全训练一个模型所需的计算和数据必须同步增长。这意味着,如果你将模型训练所需的计算量加倍,你也应该加倍数据。这意味着,模型可能很快就会在它们学习语言的能力上受到限制,因为它们已经被训练了人类所写的一切。

这两个问题都有潜在的解决方案,但它们并不像向问题投入更多的计算或更智能的模型设计那样容易获得。你可能会问一些像“但人类如何在没有阅读人类所写的每一篇文本的情况下学习语言呢?”或类似的问题。原因是人类是完全“智能”的,这个术语的定义似乎每次有非人类的东西接近它时都会改变。最终,我们可能无法让计算机完全理解语言,除非我们能让它们以一种哲学家无法辩论的方式“理解”。在此之前,语言模型需要比人类多得多的数据才能达到高水平的使用。

文本生成可能是 AI 最早的应用之一,但直到最近,它可能是一个更重要的应用,那就是提高图像质量。

提高图像质量

通过机械手段拍摄的最早已知图像是尼埃普斯太阳仪,这是约瑟夫·尼埃费尔·尼埃普斯在 1827 年拍摄的。它是通过将覆盖有一层由薰衣草和沥青制成的混合物的锡板暴露在工作室的窗户上拍摄的。这个板子被暴露在阳光下几天,以创建一个模糊的单色图像。

图 9.1 – 约瑟夫·尼埃费尔·尼埃普斯在 1827 年拍摄的尼埃普斯太阳仪

图 9.1 – 约瑟夫·尼埃费尔·尼埃普斯在 1827 年拍摄的尼埃普斯太阳仪

从那时起,图像在捕捉现实方面变得更好,但这个过程还没有完善。总会有各种限制,意味着图像在颜色上并不完全准确,无法捕捉所有细节,或者在图像中造成扭曲。一个真正完美的图像甚至让人难以想象:从一张完美的图像中,你甚至能够放大到宇宙另一侧的任何原子,而我们的图像根本无法达到这种细节水平。

各种策略

有时候,图像的限制超出了我们愿意接受的范围。在这种情况下,我们可以尝试通过修改它使其看起来更符合我们的期望来“改善”图像。其中一些编辑相对简单。也许你的照片是斜着拍的,一切都很歪曲。在物理照片打印的时代,这可以通过一把剪刀(也许还需要一块垫子来隐藏较小的尺寸)来修复。许多带有闪光灯的相机内置“红眼”去除功能,通过连续两次快速闪光来掩盖闪光灯在人们眼睛中的反射,一次是为了让眼睛适应闪光,另一次是为了真正拍照。

现在您可以在软件中修复这两个问题,并且可以修复比歪曲的照片或眼睛后反射的强光更多的东西。现代编辑软件甚至可以“修复”人们可能会争论实际上并没有损坏的东西。社会上正在进行的讨论是,杂志封面的图片是否应该“修饰”以使主题更具吸引力,或者这个过程是否通过延续不切实际的身体形象而造成更多的伤害。

随着我们进入神经网络时代,照片的自动改进变得比以往任何时候都容易。有许多服务和工具提供改善照片,无论是新拍摄的还是旧的。它们使用各种技术,但可以分为几个不同的类别,包括上采样色彩校正(或分级)和降噪。让我们简要地看看这些。

上采样

上采样涉及将图像放大。也就是说,提高图像的分辨率。现代上采样 AI 不仅能够提高分辨率,还能提高保真度——它们可以使图像更大更好。这一创新来自从大量其他图像中学习,了解物体、纹理和图像在放大和缩小时的外观。模型在被输入人工缩小的图像后,被要求重新创建原始图像,然后根据其表现进行评估。

使图像更大、更高保真度可能是一种平衡行为。有可能使图像过于锐利或填充不应该存在的数据。例如,你不想给一个孩子添加皱纹,但一张没有皱纹的祖母照片看起来就不对劲。因此,大多数提升都有某种滑块或其他控件来设置 AI 应该为你的图像提供多少额外的保真度,让你能够根据具体情况选择应该添加多少细节级别。

有一种提升方式可以绕过这个限制,那就是领域特定提升。领域特定提升器是指只提升一种类型内容的提升器。最近流行的例子之一是面部提升。人类天生就能看到面孔,我们很容易就能注意到它们的问题。但是,当你用提升器训练它只针对面孔时,它能够专门学习关于面孔的知识,并在执行这一任务时获得更高的保真度结果,而不必担心提升所有内容。这使得 AI 能够学习到“老年人”通常会有皱纹,而通用目的的提升器根本就没有空间去学习这一点。

领域特定提升的技术需要额外的过程,如检测和合成,以检测提升器可以改进的对象,并确保它们被正确地放回图像中。这确实导致了一个潜在的问题,即对象的质量高于图像的其他部分,但除非差异很大,人们似乎不太会注意到这一点。

色彩校正

色彩校正是一个在任何地方的相机设备中都发生的进程。将相机传感器中的图像转换成我们可以观看的图像需要许多不同的过程,其中之一就是色彩校正——至少是为了纠正照片拍摄时的光照颜色。我们的眼睛和大脑会自动校正光色,但相机需要明确地这样做。即使照片已经拍摄,这也并不意味着色彩校正已经完成。大多数专业摄影师以“raw”格式拍摄照片,这允许他们精确设置颜色,而不会因为多次转换而损失。

作者注记

我们相机向我们撒的一个谎是,一张图片由许多具有红色、蓝色和绿色元素的像素组成。实际上,我们的相机会分别拍摄每种颜色的单独图像,然后通过一个称为去马赛克的过程将它们组合在一起。事实上,大多数相机甚至更过分,绿色子像素的数量是红色或蓝色的两倍,因为人眼对绿色的细节更敏感。问题是,这些像素在重新对齐时永远不会完全对齐,而做得不好的去马赛克意味着你的图像在颜色的边缘会有奇怪的色彩伪影。

然而,颜色不仅仅是客观的事物。想想电影《黑客帝国》。你有没有注意到矩阵内部的一切都是绿色的?这是故意的。这种颜色的使用被称为“调色”(尽管有时令人困惑,校正也被称作调色)。人类在情感层面上与颜色有着奇怪的联系,改变它们可以调整我们对图像的情感联系。例如,想想我们如何用“蓝色”来表示“悲伤”,或者一张充满棕色和红色的图像可能会让你感到像秋天刚刚开始一样的寒意。

这两项颜色任务都可以由 AI 完成。现在有一些 AI 工具可以将图像转换为匹配特定的情感风格,或者匹配你拥有的另一张图像。在这里,AI 的技巧不在于改变图像的颜色,而在于以某种特定方式改变它们,这样人类会感到愉悦。你可能不希望仅仅因为要求 AI 将照片转换为秋天的颜色,就把你的绿鞋变成红色,但绿色的树叶需要调整。

去噪

噪声是当今相机尽力隐藏的另一个不可避免的问题,但它是你拍摄的任何图像的一部分——尤其是在暗淡条件下。相机中的噪声减少通常是通过在图像的所有像素上应用轻微的模糊来实现的(通常在颜色校正之前)。这有助于平滑来自传感器的任何噪声,但它无法做到完美。随着现代相机现在拥有大量的计算机能力(许多都存在于我们的手机中),更复杂的技巧已经变得极其常见。事实上,今天的大多数手机处理器都有某种 AI 加速,以便它们可以改善正在拍摄的图片。

去噪可以通过许多不同的方式完成,但最近的一项创新是扩散模型。扩散模型实际上就是使用扩散过程来训练神经网络——在这个过程中,图像被模型以模糊或噪声的形式提供,并要求它提供原始图像。这个过程可以通过重复扩散过程多次,并要求模型每次都进行清理来加深。这使得模型能够从非常少的信息中创建出极其详细的图像。不幸的是,信息损失是信息损失,尽管扩散模型可以创造新的信息,但没有某种指导,恢复原始信息是不可能的。这种类型的 AI 去噪实际上是我们将在文本引导图像生成部分更详细讨论的一项创新。

图像质量提升的未来

有一个基本问题限制了 AI 对图像质量所能做的事情。AI“发明”一个不属于那里的新细节是否可以接受?如果答案是肯定的,那么 AI 升频所能实现的事情就没有实际限制了。如果我们足够放大,我们可能会拥有能够生成人的皮肤原子的 AI 升频器。不幸的是,通常认为答案是“不”。我们对图像很挑剔,希望它们不仅详细,而且准确。这意味着质量和细节总是有限的,我们无法做太多来避免这一点。

这并不意味着进一步改进是不可能的。有可能我们能够使 AI 能够从其他来源获取所需的细节。例如,目前,我们有一些视频升频器,可以通过借用周围帧的数据来提升视频帧。这使得 AI 能够超越当前帧,找到它可以复制回工作帧的图案和细节。你可能会通过提供照片来填补缺失的质量,从而提升你老家的家庭视频。此外,你可能有一个 AI 能够学习给定的人在其生活中的不同时间点看起来是什么样子(例如,你的学校年鉴和家庭照片),然后可以插值这些数据来填补视频中的特定时间。

对更高品质的追求永远不会结束,人们总是希望看到下一次的改进。然而,有一些实际限制阻止了某个图像永远得到改进。但是,如果你不在乎准确性,只想得到尽可能高的图像质量呢?也许是一个只由一两句话生成的图像?好吧,这正是我们接下来要探讨的。

文本引导的图像生成

文本引导的图像生成是生成式 AI 的一个有趣类别。OpenAI 有几位开发者发布了一篇名为《从自然语言监督中学习可迁移视觉模型》(https://arxiv.org/abs/2103.00020)的论文。尽管我更喜欢他们在博客上发布的总结标题“CLIP:连接文本和图像”。CLIP第八章,“应用深度伪造的教训”中被提及,但在这里我们还会进一步讨论。

CLIP

CLIP 实际上是一对神经网络编码器。一个是在图像上训练的,另一个是在文本上训练的。到目前为止,这并不非常不寻常。真正的技巧在于这两个是如何连接的。本质上,两个编码器都从同一张图像中获取数据;图像编码器获取图像,文本编码器获取图像的描述,然后他们生成的编码进行比较。这种训练方法有效地训练了两个独立的模型,以便在给定的相关输入下创建相同的输出。

这可能仍然听起来很复杂,让我们换一种方式来看。想象一个房间里有一百多个小盒子排列成网格。两个男人轮流进入房间;一个人得到一个图像,另一个人得到一个句子。他们的任务是把自己的物品放在一个盒子里,但没有关于什么是“正确”的信息。他们可以研究自己的物品,但然后必须选择一个盒子。然后,他们根据他们放置的相似度评分,只根据这种相似度评分,没有任何假设说两个男人选择的物品中有一个更好的选择。

想法是,最终,他们会确定一套规则,其中图像和对象的文本描述都将放在同一个盒子里。这个比喻非常接近实际工作方式,除了不是仅仅把物体放在一个盒子里,他们还会根据文本和图像如何适应每个盒子中的数百个盒子来评分。

当完全训练后,相似图像应该得分相似,相似文本描述应该得分相似,匹配的图像和文本描述应该得分相似:

图 9.2 – CLIP 共享潜在空间的示例(由 Rohit Tandon 通过 Unsplash 提供的山景照片)

图 9.2 – CLIP 共享潜在空间的示例(由 Rohit Tandon 通过 Unsplash 提供的山景照片)

使用 CLIP 生成图像

因此,我们现在有一个将图像及其相关文本转换成 共享潜在空间 的模型。这是如何生成图像的呢?好吧,这正是其他技巧发挥作用的地方。在这里我们不会深入具体细节,因为最近出现了许多竞争模型,它们都有些不同,详细描述每一个模型将是一本专著,但它们的基本思想是相似的,所以我们就这样吧。

好吧,所以为了训练模型,第一步是获取 CLIP 潜在表示。一些模型同时使用图像和文本嵌入,一些随机选择一个或另一个,而其他一些只选择文本嵌入。无论使用哪种潜在表示,过程都是相似的。在生成 CLIP 嵌入后,模型被训练将这个嵌入转换成图像。

这里简要总结一下目前市场上的一些大型文本到图像模型:

  • Stability AI 的 Stable Diffusion 使用一种重复的扩散过程,这个过程从随机的噪声模式(或者在图像到图像模式下,一个图像)开始,然后在潜在空间中反复迭代扩散过程,并使用一个最终的解码器将嵌入转换回图像

  • Google 的 Imagen 使用类似的方法,但扩散过程发生在图像的像素空间中,不需要最终转换回图像空间

  • OpenAI 的 Dall-E 是一个基于转换器的模型,通过将文本表示通过多个注意力层和其他层来生成图像输出

在每种情况下,有效结果应该与用于引导生成的文本相匹配。

那么,图像生成将从哪里开始呢?

图像生成的未来

已经开发并宣布了许多其他技术,仅仅跟上各种发布就是一个重大的努力,超出了本书的范围。所有这些不同技术的真正令人难以置信之处在于,它们都是迅速连续开发的。速度似乎在持续加速,毫无疑问,在这本书出版后不久,所有这些信息都将被新的创新所取代。

这个领域正在快速发展,因此很难预测未来的局限性可能出现在哪里。有可能我们接近了这次发展热潮的尾声,在几年内我们将看到新的创新。但同样有可能,我们将在几个月内看到新功能的高速发展,使得今天的图像生成器看起来非常原始。

目前存在的局限性并非关于分辨率、细节甚至质量:这仅仅是一个学习获取最佳结果的最佳方式的问题。多亏了 Stable Diffusion 的公开分发,有一个庞大的社区正在尝试这项技术,而且几乎不可能连续几天没有出现一些新的技术或想法,这些新的技术或想法承诺开辟新的前沿。诸如文本反转LoRADreamBooth等技术提供了在消费级硬件上调整 Stable Diffusion 结果的新方法,而诸如修复扩展等新功能则允许你填充或扩展图像。

随着时间的推移,我们将更加习惯于使用这些图像生成模型,并将能够更好地设计它们以最大化其性能。到那时,我们将能够预测图像生成有哪些限制。目前,我们离眼前的山太近,以至于看不到山顶。

生成声音

声音生成是另一个我们可以不断细分下去的领域,直到书中的所有空间都被列出声音生成不同方法的标题所占据。为了简洁起见,我们将它们全部在这里分组,并覆盖几个大子领域。

语音交换

当大多数人得知他们可以交换面部时,首先想到的问题就是他们是否也可以交换声音。答案是相当令人不满意的:是的,但你可能并不想这么做。现在有一些 AI 可以交换声音,但它们都存在各种问题:从听起来像机器人,到缺乏抑扬顿挫,到与涉及的人不匹配,到非常昂贵且专属。如果你正在制作任何具有中等生产价值的东西,你将能够更好地利用自然智能:找到一个能够模仿声音的模仿者。AI 技术(目前)还达不到这个水平。即使是著名的电影《Fall》,它所做的“声音深度伪造”实际上只是在录音棚中重新录制了声音(这个过程被称为自动对话替换或循环),然后 AI 被用来调整他们的嘴巴以匹配新的对话。

语音交换技术无疑会改进,并且有一些公司为各种项目提供高质量的语音模型,但它们非常昂贵,并且仍然是一个利基用途案例。然而,如果需求持续存在,不可避免地会有某人推出新的技术或改进,使得高质量的语音交换像其他深度伪造一样容易。

文本引导的音乐生成

这种技术与文本到图像生成非常相似。事实上,大多数实现实际上在底层使用文本到图像生成器。基本技术是将音频转换为音频的频谱图图像,然后在上面训练一个文本到图像模型。这意味着相同的图像生成器可以用来生成音频频谱图,然后将其转换回音频流。

这种技术是有损的,并且限制了输出质量,因为频谱图图像只能容纳这么多频率。此外,还存在一个问题,即频谱图图像只代表一小段时间。这意味着生成音频必须以几秒钟的长度的小块进行。Riffusion(一个基于 Stable Diffusion 的音频生成模型)在约 8 秒长的频谱图上进行训练。这意味着即使你发明了将多个频谱图连接起来的技巧,你大约每 8 秒就会有一个新的生成。通过提高生成图像的分辨率,可以增加时间切片和频率,但限制仍然存在,只是更高。即使技术有合理的进步,当前技术也不太可能生成 3-4 分钟的全长歌曲。

图 9.3 – Riffusion 制作的频谱图示例

图 9.3 – Riffusion 制作的频谱图示例

低频分辨率低且每几秒钟就发生显著变化的音乐,与我们通常作为音乐听众所期望的相去甚远。很可能会有人创建一个文本到音乐的生成器,它不会涉及音频图像转换过程,但这将是一个全新的创造,可能有其自身的怪癖,并且正在进入我们在这章中想要避免的推测性未来主义。

声音生成的未来

不可避免的是,有人将创造出一种工具或技术,将声音生成领域炸开,就像卷积层和扩散模型对图像以及变换器对语言所做的那样。当这种情况发生时,我们将看到一场类似淘金热式的创新爆发,人们将采用并扩展新技术,就像我们在图像和文本生成领域所看到的那样进行改进。不幸的是,目前我们无法依赖未来的伟大创新,所以我们必须立足于现在或近未来。

现代音频技术依赖于自动编码器或频谱图将音频转换为 AI 生成技术现在可以使用的形式。为此,这些方法可能会得到改进,以添加更多细节或信息。例如,通常频谱图是黑白色的。如果以某种方式添加颜色,而不仅仅是线性增加可生成的内容量,那会怎么样?例如,如果用红色表示重复的旋律,蓝色表示另一个主题,绿色表示两者在更大时间尺度上的相互作用,那会怎么样?如果与更大的频谱图结合,这能否将现代声音生成带到一次生成整首高质量歌曲的程度?

如果我们花时间去开发一种将口语转换为书面形式的编码系统,并将其训练成文本生成模型,那会怎么样?如果我们能将口音定义为将书面形式转换为音频的某种组合,我们能否创造出能够模仿口音的 AI,就像它们模仿绘画风格一样?我们有很多方法可以将音频修改成我们当前 AI 模型擅长处理的形式,这将使当前的声音生成 AI 得到全面改进。

即使不考虑类似于变换器或卷积的新范式类型的革命性改进,我认为如果计算机科学家像最近对图像和视频那样重视音频,音频领域还有几年的实质性进化空间。

深度伪造

当然,这本书的整个内容都是关于深度伪造的过去和现在,所以在结尾时回到它们的未来是有意义的。我们可以从本章中提到的其他 AI 中了解到很多关于深度伪造未来的信息。这是因为,偷偷地,本章的所有部分都是为了这一部分而构建的。毕竟,深度伪造是一种在特定领域图像上工作的图像生成 AI,它具有共享的嵌入。

本章中我们探索的每个领域都可以用于改进深度伪造,所以让我们逐一处理它们。

声音生成

这一点相当简单且明显。在交换面部之后,下一步就是交换声音。如果我们能够实现可靠的声音交换,那么深度伪造将进入一个新的能力。如果你在制作一部没有其他人帮助的电影时,制作音乐或其他效果也可能很有用,但它们的实用性在其他方面将是有限的(在深度伪造中,其他行业无疑会有更多的用途)。

文本引导的图像生成

目前,你可以将深度伪造视为“面部引导”的,也许这是最好的解决方案,因为它可以让演员的表演更加突出,即使他们戴着不同演员的面部。但文本引导的后期处理肯定可以满足某种需求。电影《银翼杀手》中有一个著名的场景,侦探德卡德使用语音命令检查图像,以探索环境(在完成不可能的任务,将视图转向侧面之前,这只能通过发明新的数据来实现,而且对于寻找线索是不可用的)。这可以被视为使用文本引导图像生成的图像修改的原型工作流程。

我们不必生成一张全新的图像,而是可以使用遮罩或过滤器修改其部分,并将 AI 的更改与原始图像结合。这样,我们可以做到像输入(或说出)“让他笑得更开心”这样的操作,从而在不替换整个图像的情况下生成对图像的修改。这利用了文本引导的力量,同时仍然允许面部引导的交换发生。

提升图像质量

很容易看出提升图像质量如何能改善深度伪造:你可以改进你的输入数据或输出结果,使其更准确、质量更高,或进行色彩校正。图像质量对于创建成功的深度伪造至关重要,但一些来源根本不提供你真正需要的数据来获得高质量的深度伪造。例如,不幸的是,阿尔伯特·爱因斯坦在高清摄像机能够捕捉我们想要的用于标准深度伪造的所有数据之前就去世了。虽然有一些我们可以使用的照片和一些视频,但这些来源质量低,大多是单色的,并且通常会导致交换效果不佳。如果我们能够足够改进这些数据以训练模型,那么我们就可以通过自举来创建一个全新的阿尔伯特·爱因斯坦视图。

尤其是特定领域的工具将对深度伪造有益。已经有专门用于面部提升的升缩器,可以用于 Faceswap 的输出,以增加深度伪造过程的实际分辨率。特别是当与非常高质量的视频一起使用时,提升输出可以弥合计算能力和交换面孔分辨率之间的差距。

文本生成

这似乎对深度伪造来说可能不是最重要的,从某种意义上说,“生成”部分确实是。但像 CLIP 这样的技术和技术可以通过新的和独特的方式应用于深度伪造。例如,如果我们摆脱了所有深度伪造的一部分的通用面孔编码器,并用一个像 CLIP 一样训练的新编码器来替换它,与各种面孔和数据进行对比,我们可能会能够裁剪模型的最大部分来单独运行,使我们能够将所有的精力(和计算)集中在构建面孔的解码器上,而不是浪费时间训练编码器,只是为了给我们一个可以输入解码器的嵌入。

深度伪造的未来

我认为,随着最近生成式人工智能的所有创新,我们可以确信深度伪造还没有走到尽头。这些其他领域中的创新正等待着在深度伪造中得到实施。这些改进将使深度伪造能够做新的和有趣的事情,同时仍然保持深度伪造的本质——即交换一个面孔与另一个面孔。

我们还没有看到电影中深度伪造的尽头。事实上,随着技术的日益成熟、强大和可用,电影中的深度伪造似乎只会增长。似乎合理的是,未来的整个演员可能会被深度伪造,以防止角色因年龄增长或去世而改变。想象一下,在导演的完全指导下,由 AI 动画的原始演员完成的《飘》的续集。如果 AI 继续增长和进步,我们可能很快就会看到这种情况。

深度伪造并非没有界限;仍然存在需要解决的问题,例如分辨率、训练时间和甚至数据收集。但现在,使用从黑白电影收集的数据,彩色化并干净地提升分辨率,已经可以制作出比早期蓝光分辨率更高的深度伪造人脸。10 年后,甚至 20 年后,我们会处于什么位置?或者,多久之后你甚至懒得起床参加一个全身生成并与你匹配的视频通话?

人工智能伦理的未来

我们从第三章**,审视深度伪造的伦理和危险中制定的指南仍然是伦理的坚实基础(毕竟,对人们友好永远不会过时),但随着我们走向未来,我们将面临全新的挑战,这些挑战将需要它们自己的伦理指南。

例如,在什么情况下可以用深度伪造技术替换演员?是否可以用一个仅由导演操纵的 AI 替换一个勤奋工作的人类?如果是一个没有人愿意(或能够)扮演的角色呢?彼得·杰克逊的《指环王》中的Gollum由极具天赋的角色演员Andy Serkis扮演(Serkis 还在迪士尼的《星球大战》续集中扮演了 Snoke,以及许多其他以数字为首要角色的角色)。用 AI 替换他是否道德?

很可能,一个完全由 AI 扮演的角色会被许多人认为是道德的(尽管可能不是演员)。但如果导演决定一个女演员的声音不够“女性化”,并在未经女演员同意的情况下用尖锐的乡村女孩声音替换,那会怎样?如果导演的创意愿景是用肤色较深的人替换扮演坏蛋的演员呢?关于上一节中你用一套协调的服装替换睡衣以进行视频通话的例子又如何呢?这些问题更难回答,这是我们随着社会的进步必须共同评估的事情。

摘要

生成式 AI 拥有悠久的历史和巨大的未来。我们正站在一个广阔的平面上,任何可能性都存在,我们只需朝着它前进。尽管如此,今天并非所有事物都可见,我们必须调整我们的期望。主要挑战是我们计算机、时间和研究的限制。如果我们把时间和精力投入到解决 AI 的一些限制上,我们不可避免地会提出全新的飞跃,这将帮助我们向前迈进。即使没有巨大的革命性改进,我们也可以做出许多较小的进化改进,以提升这些模型的能力。

创新的最大驱动力是需求。越来越多的人使用生成式 AI 并将其用于新颖的应用,将创造生成式 AI 继续在未来得到改进所需的经济学和社会推动力。

这本书一直致力于让我们,即作者,邀请你们一起前进,帮助 AI 走向这个我们希望所有人都能看到在远方的生成式未来。

EBSCOhost - 打印于 2023 年 11 月 27 日 上午 6:20。所有使用均受www.ebsco.com/terms-of-use条款约束。

posted @ 2025-10-07 17:59  绝不原创的飞龙  阅读(64)  评论(0)    收藏  举报