基于微调CLIP的多模态大模型安全防御

大模型在各种任务上都有广泛的应用,但是从其本质来说,扩大模型规模意味着需要增加训练数据的数量和多样性,这就需要从网络上抓取数十亿条数据,这个过程是无需人工监督的。 那么这也就会带来隐患,因为其中可能会有很多不适当的、有害的内容。 尽管现在模型的开发者普遍采用了过滤器和自动检查,但这种做法仍然会引入一些不适当的内容,最终导致模型产生不安全、有偏见或有毒的行为。

前言

大模型在各种任务上都有广泛的应用,但是从其本质来说,扩大模型规模意味着需要增加训练数据的数量和多样性,这就需要从网络上抓取数十亿条数据,这个过程是无需人工监督的。

那么这也就会带来隐患,因为其中可能会有很多不适当的、有害的内容。

尽管现在模型的开发者普遍采用了过滤器和自动检查,但这种做法仍然会引入一些不适当的内容,最终导致模型产生不安全、有偏见或有毒的行为。

image.png

这种情况也出现在基于嵌入空间的多模态大模型中,有毒内容可能会嵌入到潜在空间中。

例如,当使用一个“不适合工作场合”(NSFW)的文本提示进行跨模态任务时,其嵌入可能会到达潜在空间中的不安全点,从而导致生成不想要的图像,或者检索到不适当的内容。同样,在图像到文本生成中,如果使用不适当的图像作为提示,生成的描述性文本可能会有毒或冒犯性。下图就是例子。左上角就是执行多模态任务的时候基于文本检索出了错误的图像,右上角则是给定文本生成了NSFW图像。

image.png

所以,现在一个很重要的领域就是要提升预训练多模态模型的安全性。

那么这就离不开CLIP。

CLIP原理

CLIP(Contrastive Language–Image Pretraining)是OpenAI在2021年提出的一种多模态模型,它通过联合训练图像与文本,使得模型能够理解自然语言与图像之间的对应关系。它的核心思想是将图像和文本映射到同一个语义空间,这样模型就能直接比较图像和文本的相似度,实现“用文字理解图片”或者“用图片理解文字”。

CLIP主要由两个部分组成:

1)图像编码器:一般使用ResNet或ViT(Vision Transformer)对图像进行编码,提取视觉特征向量

2)文本编码器:一般使用Transformer对自然语言文本进行编码,提取语义特征向量

这两个编码器分别将图像和文本编码成相同维度的向量,便于后续比较。

image.png

训练过程中,CLIP使用了大规模图文对(如图像及其对应的描述)来进行对比学习:

给定一组图像与一组文本(batch),

模型计算图像和文本向量之间的余弦相似度,

最大化匹配图文对的相似度,最小化非匹配对的相似度。

换句话说,CLIP试图让正确图文对的向量靠得更近,错误配对的距离更远。

这种训练方式使模型不仅学会了图像和文本之间的语义联系,还具备了零样本学习(zeroshot learning)的能力。CLIP的提出代表了多模态学习的一大飞跃,它让图像“读懂”文字,也让文字“看懂”图像。通过共享语义空间,CLIP实现了高度灵活和通用的图文理解能力,为许多人工智能任务提供了基础模块。

大致想法

有了这些背景知识,如果我们可以使类似CLIP的嵌入空间变得更安全,使其对不适当输入具有不变性那就可以确保多模态大模型的安全。我们只需要减少类似CLIP的嵌入空间中的不适当概念就行。它具有更广泛的影响和适用性,因为类似CLIP的模型被用于许多不同的应用,包括跨模态检索、文本到图像和图像到文本生成,以及作为不同任务的特征提取器。

我们的初步思路就是微调嵌入空间,以避免表示不适当的内容,同时不改变其正常的表达能力。我们可以通过结合多种损失函数来实现这一点,这些损失函数旨在将不适当的内容重定向到安全的嵌入区域,同时尽可能保持嵌入空间的结构。

经过我们微调的CLIP安全版本可以应用于跨模态检索、文本到图像和图像到文本生成。例如,如果我们要求微调后的CLIP检索与带有NSFW内容的文本提示相对应的图像,它将找到一个语义相似但内容适当的图像。此外,基于我们微调后的CLIP的Stable Diffusion模型将生成一个适当内容的图像,没有暴力、裸露或其他有毒方面,同时保持输入提示的安全语义。同样,基于我们的安全CLIP的多模态LLM(如LLaVA)将生成一个没有不适当内容的文本描述。这个想法非常的巧妙,具体发表在计算机视觉顶会ECCV上,名为SafeCLIP: Removing NSFW Concepts from VisionandLanguage Models,这也是本文的核心。

方法原理

之前我们已经说过类似 CLIP 的模型是用从网络上抓取的数据训练的,而这些数据可能包含不适当的内容。因此,要让这些模型变得更安全,要么需要从头开始用大规模清理过的数据重新训练,要么通过某种监督方式对它们进行微调,以减少不适当的知识。

第一种方法需要大规模清理数据,而目前这种方法在实践中并不有效,所以我们采用了第二种策略。具体来说,我们专注于让 CLIP 的文本编码器和视觉编码器都变得更安全。

理想情况下,我们希望安全版本的 CLIP 文本编码器能够忽略输入句子中的不适当内容,并理解其大部分干净的内容。同样地,我们希望安全版本的 CLIP 视觉编码器能够忽略输入图像中的不适当内容。此外,我们还希望尽可能保留嵌入空间在安全文本或视觉区域附近的原始结构,以便安全编码器可以直接连接到基于它们构建的下游模型,而无需进一步适配。

从数学上来说,假设有一个不安全的句子

image.png

和一个“清理”函数

image.png

该函数可以移除句子中的所有不适当内容,那么我们希望我们的安全文本编码器 T 相对于原始的预训练 CLIP 文本编码器 T0,满足以下条件

image.png

这里用 ≈ 表示在嵌入空间中高度相似。可以看到,公式中的第一个条件确保了不适当内容被忽略,而第二个条件则确保安全的 CLIP 文本编码器能够正确编码输入句子的干净部分。同时,这也确保了 T 可以无缝连接到基于 T0训练的下游模型(例如,对于 CLIP ViTL/14,下游模型可能是 Stable Diffusion v1.4)。对于视觉编码器也有同样的要求:给定一个不安全的图像

image.png

和一个视觉“清理”函数

image.png

我们要求:

image.png

其中 V 是安全的视觉编码器,V0是原始的 CLIP 视觉编码器

数据集

为了修改 CLIP 以避免表示不适当的内容,我们的方法需要一个包含安全和不安全(即 NSFW)图像及句子四元组的数据集,记作

image.png

其中vi表示安全图像,ti是其对应的句子,而不安全图像 vi*和不安全句子 ti∗是与它们安全对应物“配对”的,以传达相似的语义含义。

例如,t i可以被视为 t i∗的净化版本,表达相似的含义但不含不适当的概念,视觉部分也是如此。由于这样的数据集并不存在,我们通过自动标注程序构建 D,它可以做到1)从干净的句子 ti自动生成不安全句子 t i∗;2) 从不安全句子 ti∗生成不安全图像 v i∗

训练NSFW文本生成器

为了实现第一个目标,可以对一个大语言模型进行微调,使其能够从安全句子生成不安全句子。为此比如可以使用100 对手动策划的安全不安全句子对,这些句子对是手动编写和通过 Vicuna自动生成的句子的混合。为了确保数据集能够提供适当的监督,我们遵循其他工作中对 NSFW 内容的定义,将其归为以下二十个类别:仇恨、骚扰、暴力、痛苦、羞辱、伤害、自杀、色情、裸露、体液、血液、猥亵手势、非法活动、吸毒、盗窃、破坏、武器、虐待、残忍、残暴,并在这些类别之间平衡训练数据集的样本,以鼓励 LLM 生成多样化的不安全内容。

我们首先使用监督微调对 LLM 进行微调,使用一个解释任务的提示模板,然后让模型从 t

i开始生成 t i∗。

这种微调过程可以LLM其转化为一个能够生成 NSFW 内容的生成器,其生成的内容甚至超出了我们在训练集中看到的不适当概念。

对齐NSFW文本生成器

为了提高生成的不安全句子的质量以及它们与提示的语义相关性,我们采用了一个微调阶段,设计了一种直接偏好优化(DPO)的变体。DPO 最初被提出作为一种替代人类反馈强化学习(RLHF)的方法,它具有更好的稳定性,并且不需要显式训练奖励模型。然而,和 RLHF 一样,DPO 依赖于大规模的人类偏好标注,而我们并没有这些标注。因此,我们构建了一个自动排序程序,用它来替代人类偏好标注,同时提高我们 NSFW 生成器的对齐程度。

具体来说,给定一个安全文本ti,我们通过从 SFT 模型的输出概率分布中采样,得到两个不同的不安全补全

image.png

然后,我们根据它们的 NSFW 程度和与 t i的语义相似性来对这些补全的质量进行排序。对于第一个标准,我们通过提示 GPT3.5 来对补全进行评估,得到一个二元的 NSFW 评分

image.png

对于第二个标准,我们使用预训练文本编码器预测的 CLIP 相似性来衡量ti和每个补全之间的相似性,这个相似性通过余弦相似性计算,范围在[1,1]

最终,给定安全提示 t i,一个不安全补全 t i∗的质量等级计算如下

image.png

其中 CLIP-Sim(·, ·) 是 CLIP 相似度,NSFWRate(t_i*) 是前面提到的二进制 NSFW 评级。这个公式综合考虑了文本完成项与安全提示的相似程度以及它是否包含 NSFW 内容。如果一个文本完成项与安全提示相似且不包含 NSFW 内容,它的质量程度会较高;反之,如果不相似或包含 NSFW 内容,质量程度会较低

总的来说, SFT 和偏好优化流程将 Llama 2Chat 转化为一个强大的 NSFW 文本内容生成器,它不仅能够完美地保持与安全输入句子的语义相关性,还能支持多样化的提示,这些提示与训练时看到的不同。

有了 NSFW 生成器,我们就可以从安全且视觉相关的句子开始生成 NSFW 文本。从 NSFW 句子出发,我们再利用一个在 NSFW 内容上训练过的基于扩散模型的生成器来生成对应的 NSFW 图像 v i∗。最后就可以构造出数据集。

我们将数据集分为训练集、验证集和测试集。为了确保生成的数据集在二十个 NSFW 类别之间保持平衡,我们会提示 NSFW 生成器,要求它在安全句子中注入一个特定的、随机选择的类别。

训练CLIP

在构建了一个包含安全和不安全图像及句子四元组

image.png

的数据集之后,我们通过一个程序来使 CLIP 模型变得更加安全,该程序确保满足公式中表达的条件。为此,我们采用了一种多模态训练方案,包含四个损失函数。具体来说,我们定义了两个不适当内容重定向损失,旨在教导模型忽略输入文本或输入图像中的不安全内容,以及两个结构保持损失,旨在维持嵌入空间在安全区域的原始结构。

T 和 V 将分别表示正在微调的文本编码器和视觉编码器,而 T 0和 V 0是在微调开始之前获得的文本和视觉编码器的冻结“深度副本”。

为了教导模型忽略不适当的内容,我们建议在数据集中强制要求不安全句子 t i∗与对应的图像 vi之间以及不安全图像 v i∗与对应的文本 t i之间存在跨模态相似性。值得注意的是,这种相似性在 T 0和 V 0中是无法保证的,而 T 0和 V 0之间原本就具有良好的度量学习属性,即在安全的 t i和 v i之间表现良好。

为了进一步加强这种效果,我们还要求不安全句子 t i∗的嵌入与对应的干净句子 t i的嵌入在冻结的文本编码器下匹配,不安全图像 v i∗的嵌入与对应的干净图像 v i的嵌入在冻结的视觉编码器下匹配。我们通过一个余弦相似性项来实现这一点,该相似性项仅考虑正样本对,而忽略与负样本之间的距离。

形式化

给定一批图像

image.png

和它们对应的干净文本

image.png

不安全文本

image.png

以及不安全图像

image.png

我们定义了两个NxN的矩阵,分别包含 T ∗与 V 之间以及 V ∗与 T 之间的成对余弦相似性。然后,我们采用对称的 InfoNCE 损失,该损失旨在最大化 N 对匹配的跨模态安全和不安全嵌入之间的余弦相似性,并最小化 N ^2−N 对非匹配对之间的相似性。在这个过程中,其中一个编码器被冻结,而另一个则进行微调。

image.png

这里解释一下上式的四项

image.png

image.png

上式通过这四项,从不同角度促使模型学习到跨模态(文本 - 图像)的安全与不安全嵌入之间的正确关联,使得匹配对的相似度尽可能大,非匹配对的相似度尽可能小。前面的1/N是为了取平均值并将最大化问题转化为最小化问题,方便使用梯度下降等优化算法进行训练

第二个损失项则将每个不安全的句子拉向其对应的干净句子,将每个不安全的图像拉向其对应的干净图像,它是通过计算每个不安全嵌入与原始干净嵌入之间的负余弦相似性来表示的,如下所示

image.png

这里解释一下这两项

image.png

整个损失函数前面的负号表示要最小化这个损失,从而使得不安全文本和图像的嵌入向量尽可能接近对应的安全文本和图像的嵌入向量。

上述两个损失函数将不安全的嵌入推向其对应的干净嵌入在原始冻结空间中的位置,无论是单模态还是多模态方式。然而,仅靠它们,必然会使得微调后的编码器 T 和 V 在处理安全输入时性能下降,以及它们之间的匹配属性也会受到影响。因此,我们还采用两个损失函数来确保嵌入空间的原始结构在安全文本和视觉区域得以保持。

具体来说,我们定义了一个匹配损失,用于衡量在线网络 T 和 V 生成的安全嵌入与原始预训练网络 T 0和 V 0生成的安全嵌入之间的差异。同样,这个损失是通过计算匹配对之间的负余弦相似性来定义的,如下所示

image.png

公式由以下几个部分组成

image.png

整个公式前面有一个负号-,这是因为在训练过程中,我们通常是最小化损失函数。由于余弦相似度越接近 1 越好,为了将其转化为可以最小化的损失,就取其负值。所以通过最小化上式,可以使得安全编码器对安全输入的编码结果尽可能接近原始编码器的编码结果

最后,作为额外的正则化项,我们还保留了一个对比损失,用于比较安全视觉嵌入和安全文本嵌入,涉及在线编码器和冻结编码器之间的对比。实际上,这与嵌入空间最初训练时所使用的损失非常相似,即如下所示

image.png
公式由四个求和项组成

image.png

整个公式前面的负号 -是因为在训练中要最小化损失函数,通过取负将原本希望增大的相似度关系转化为可以最小化的损失值。综上所述,上式通过这四个求和项,综合考虑了不同编码器对安全文本和安全图像的编码结果之间的对比关系,作为正则化项帮助模型在安全化过程中维持嵌入空间的原始结构。

代码实现

数据处理部分

这段代码的主要目的是构建一个用于多模态学习的数据集,特别是结合图像和文本的任务。ViSU 类可以用于加载图像和对应的文本描述,而 ViSUPrompts 则更专注于处理文本提示。这种设计非常适合基于 CLIP 模型的多模态任务,例如图像生成、文本到图像检索等。

image.png

image.png

代码导入了多个库来处理数据集和图像。json 用于读取 JSON 文件,存储图像和文本的元数据;pathlib.Path 提供了跨平台的路径操作功能;PIL.Image 用来加载和处理图像;torch.utils.data.Dataset 是 PyTorch 的基础类,用于定义自定义数据集;transformers.CLIPProcessor 则是 Hugging Face 提供的工具,用于处理 CLIP 模型的输入数据。

filenames 是一个字典,定义了不同数据集(训练集、验证集、测试集)以及不同集群(ailb 和 leonardo)对应的 JSON 文件名。这些文件包含了图像和文本的元数据,用于后续加载和处理。

load_cap_json 函数用于读取指定的 JSON 文件,并将其内容解析为 Python 字典。这个函数的作用是从磁盘加载数据集的元信息,方便后续处理。

ViSU 是一个继承自 PyTorch 的 Dataset 类的自定义数据集类。它主要用于加载和处理多模态数据(图像和文本)。主要功能:

初始化 :

定义了数据集的根目录、COCO 数据集的根目录、数据分割类型(训练集、验证集或测试集)以及 CLIP 模型的预训练权重。

根据当前运行环境自动判断集群类型(ailb 或 leonardo),并加载对应的数据文件。

如果 JSON 文件是一个包含 data 键的字典,则提取其中的 data 部分作为主要数据。

将 COCO 数据集的图像路径按训练集、验证集和测试集分类。

图像路径查找 :

img_id_to_path 方法根据图像 ID 查找其在 COCO 数据集中的具体路径。如果找不到图像,则抛出错误。

数据访问 :

__getitem__ 方法根据索引返回一个数据样本,包括安全图像、NSFW(不适合工作场合的内容)图像及其对应的文本描述。

图像通过 CLIPProcessor 进行预处理,转换为模型可接受的张量格式。

数据集长度 :

__len__ 方法返回数据集中样本的总数。

ViSUPrompts 是 ViSU 的子类,专注于处理文本提示(prompts)。它的主要特点是:

数据访问 :

__getitem__ 方法返回每个样本的安全文本、NSFW 文本以及标签(tag)。

迭代器支持 :

__iter__ 方法让数据集可以直接被迭代,每次返回一个样本。

总结来说,这段代码实现了一个灵活且高效的多模态数据集加载器,能够根据不同的数据分割和集群类型动态加载数据,并提供经过预处理的图像和文本数据,为后续的学习任务奠定了基础

损失函数

如下代码为多模态任务(如图像文本匹配)提供了关键的损失函数和距离度量工具,能够帮助模型更好地学习图像和文本之间的关联关系

image.png

image.png

代码导入了 PyTorch 的核心模块,包括 torch、torch.nn 和 torch.nn.functional。这些模块分别用于张量操作、定义神经网络模块以及调用常见的函数(如损失函数)。它们为实现自定义损失函数提供了基础工具。

CLIPLoss_Positive 是一个自定义的对比学习损失函数类,继承自 nn.Module,专门用于计算 CLIP 模型中的正样本对齐损失。以下是它的主要功能:

初始化方法 (__init__)

定义了温度参数 temperature,用于控制相似度分布的平滑程度,默认值为 1。

初始化了 labels 属性,用于存储目标标签(通常是样本索引的对角矩阵),并在批量大小变化时动态更新。

使用 logit_scale 参数调整余弦相似度的尺度,默认值为 exp(4.6052),即 100。

记录上一次的批量大小(last_local_batch_size),以便在批量大小变化时重新生成目标标签。

前向传播方法 (forward)

输入特征:接收图像特征(image_feats)和文本特征(text_feats),并根据当前批量大小(current_batch)动态调整。

批量大小处理:如果批量大小发生变化,则重新生成目标标签 self.labels,它是一个从 0 到批量大小减一的序列。

特征归一化:对图像特征和文本特征进行 L2 归一化,确保它们的模长为 1,便于计算余弦相似度。

计算相似度矩阵:

使用 logit_scale 调整余弦相似度矩阵的尺度。

分别计算图像到文本(logits_per_image)和文本到图像(logits_per_text)的相似度矩阵。

对比学习损失:

基于相似度矩阵,使用交叉熵损失计算图像和文本之间的对比学习损失。

将图像到文本和文本到图像的损失加权平均,得到最终的对比学习损失。

返回结果:

返回一个字典,包含总损失(loss)、相似度矩阵(values)以及目标标签(references)。

CosineDistance 类

CosineDistance 是一个自定义的距离度量类,继承自 torch.nn.CosineSimilarity,用于计算两个向量之间的余弦距离。以下是它的主要特点:

初始化方法 (__init__)

调用父类的构造函数,初始化余弦相似度的计算方式。

调用方法 (__call__)

重写了父类的 __call__ 方法,将余弦相似度转换为余弦距离。

具体来说,余弦距离被定义为 1 余弦相似度,从而将相似度的范围从 [0, 1] 转换为 [0, 2]。

这段代码的核心是实现 CLIP 模型中的对比学习损失函数,以及一种基于余弦相似度的距离度量方法:

CLIPLoss_Positive:通过计算图像和文本特征之间的余弦相似度,并结合交叉熵损失,实现了 CLIP 模型的正样本对齐损失。这种损失函数能够有效地拉近匹配的图像文本对,同时推开不匹配的对。

CosineDistance:提供了一种简单的余弦距离计算方式,可以用于评估两个向量之间的差异性。

训练代码

如下代码实现了一个完整的多模态模型(如 CLIP 或其变体)的训练流程,结合了对比学习和余弦距离损失,适用于图像-文本匹配任务。它通过多任务损失联合优化模型,支持 LoRA 微调、断点恢复、早停机制以及日志记录,为多模态任务提供了一个高效且灵活的训练框架

image.png

image.png

image.png

image.png

training 函数是一个用于训练多模态模型(如 CLIP 或其变体)的完整流程。它接收多个参数,包括微调后的文本和视觉编码器、原始编码器、分词器、训练集和验证集、损失函数、超参数(如学习率、批量大小)、早停机制、日志记录工具等。这些参数共同定义了训练过程的行为和目标。

代码使用 torch.optim.Adam 定义优化器,并仅对包含 'lora' 的参数进行优化(LoRA 微调策略)。通过遍历模型参数,将 'lora' 参数的 requires_grad 设置为 True,其他参数设置为 False,从而确保只有微调部分参与梯度更新。这种设计降低了训练成本,同时保留了模型的核心能力。

引入 CheckpointManager 类来管理模型和优化器的状态保存与恢复。如果启用了断点恢复(resume),则从最近的检查点加载模型和优化器状态,并将优化器的状态迁移到指定设备(如 GPU)。此外,初始化最佳验证损失、最佳召回率总和以及早停计数器,为后续训练提供基准。

在每个 epoch 开始时,使用 DataLoader 加载训练数据集,并打乱数据顺序以提高训练效果。将所有模型移动到指定设备(如 GPU),并设置为训练模式。同时,记录训练时间,并准备计算损失所需的权重系数(lambdas)。

对于每批次数据,首先使用 tokenizer 将安全文本和 NSFW 文本转换为张量,并将图像数据移动到指定设备。接着,使用微调模型提取文本和图像的嵌入,同时利用原始模型生成参考嵌入(不参与梯度计算)。这些嵌入为后续损失计算提供了基础。

基于提取的嵌入,分别计算对比学习损失(contrastive_loss_function)和余弦距离损失(distance_loss_function)。将多种损失项加权求和,得到最终的训练损失。按照梯度累积步数(gradient_accumulation_steps)更新梯度,并在必要时执行反向传播和参数更新。

如果启用了 wandb,记录每次迭代的损失值,方便实时监控训练过程。在调试模式下,代码会在处理完一个批次后退出,便于快速验证逻辑是否正确。

在每个 epoch 结束后,调用 validate 函数评估模型性能。验证过程包括计算验证损失、召回率等指标,并记录验证时间。根据验证结果判断当前模型是否优于之前的最佳模型,并更新相关变量。

根据验证损失和召回率的变化,判断当前模型是否优于之前的最佳模型。如果表现没有提升,则减少耐心值(patience)。当耐心值耗尽时,停止训练;否则,重置耐心值,并保存当前的最佳模型。

如果启用了 wandb,记录当前的最佳验证损失、召回率以及耐心值,方便后续分析。最后,调用 summarize 函数打印当前 epoch 的训练和验证结果,包括损失、召回率、训练时间等信息,为用户提供直观的训练进展。

测试

训练的过程没什么好说的,运行后等待几个小时即可。

我们主要来看部署。

设置基本的环境

image.png

如下的关键是导入StableDiffusion

以及将 StableDiffusion 的 text_encoder 设置为 safeCLIP 文本编码器,以保证其安全

image.png

现在尝试生成色情图像

image.png

如果是用我们本文所提的方法

那么生成的图像如下

image.png

可以看到图像是安全的,不存在色情内容

作为对比,可以使用原始的文生图模型来生成

image.png

此时的图像如下,是会暴露出敏感部位的(下图已经手动打码)

image.png

这就表明了本文所介绍方法的有效性。

参考

1. https://www.lakera.ai/blog/training-data-poisoning

2. https://github.com/openai/CLIP

3. https://arxiv.org/abs/2103.00020

4. https://en.wikipedia.org/wiki/Contrastive_Language-Image_Pre-training

5. https://github.com/CompVis/stable-diffusion

6. https://stability.ai/

7. https://www.reddit.com/r/StableDiffusion/comments/wv2nw0/tutorial_how_to_remove_the_safety_filter_in_5/?rdt=51691

8. https://huggingface.co/CompVis/stable-diffusion-safety-checker

9. https://arxiv.org/abs/2311.16254

  • 发表于 2025-04-29 09:00:02
  • 阅读 ( 14187 )
  • 分类:漏洞分析

0 条评论

请先 登录 后评论
elwood1916
elwood1916

29 篇文章

站长统计