深度学习架构手册(三)
原文:
annas-archive.org/md5/3540d3ca64cea2cc08daad9a2559bb49译者:飞龙
第十二章:解读神经网络
在尝试理解模型预测背后的原因时,局部单样本特征重要性可以是一个有价值的工具。此方法使你能够将分析重点集中在输入数据的较小部分,从而更有针对性地理解对模型输出起到重要作用的关键特征。然而,通常仍然不清楚模型使用了哪些模式来识别高度重要的特征。通过回顾来自目标样本的更多预测解释,战略性地辨别预测的实际原因,某种程度上可以规避这一问题,实际操作部分将在本章稍后介绍。然而,这种方法受到必须验证的样本数量的限制,有时仍然很难明确指出具体使用的模式。
深度神经网络(DNNs)学习低层到高层特征,帮助预测层在幕后辨识正确的标签。当我们在输入数据上使用基于局部特征重要性的解释时,我们无法确定到底是哪些低、中或高层的模式对输入数据的特征重要性做出了贡献。对于图像,这些特征从低层的简单形状到中层的人的轮廓形状,再到逐步组合成一个人的面孔或日常物品的模式。对于文本,这些特征从低层的词嵌入(表示单词的意义)到中层的语义角色(如句子中的单词角色,帮助正确表示文本意义的句子嵌入),一直到我们更熟悉的高层特征,如主题和情感。当然,这些只是我们对神经网络学习内容的理论假设。
本章将探讨一种方法,帮助清除深度神经网络学习特征中的所有模糊性,即通过输入优化直接可视化神经网络所检测到的模式。通过将直接学习的模式与激活过滤相结合进行可视化,我们可以揭示深度神经网络做出预测的实际原因。具体来说,本章将讨论以下主题:
-
解读神经元
-
寻找可解读的神经元
-
解读学习到的图像模式
-
发现反事实解释策略
技术要求
本章包括在 Python 编程语言中的实际实现。为完成本章,你需要一台已安装以下库的计算机:
-
torchvision -
torch -
torch-lucent==0.1.8 -
matplotlib==3.3.0 -
captum -
pillow -
numpy
代码文件可以在 GitHub 上找到:github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_12。
解释神经元
神经网络层中的神经元产生的特征将被后续层使用。这些产生的特征或激活值仅仅是学习到的模式在输入数据中显著性的一个指示。但你是否曾经想过这些模式是什么?解码神经网络实际学习到的模式可以进一步提高透明度,从而帮助实现第十一章中探索预测解释的价值部分提到的目标,解释神经网络预测。
数据由许多复杂的模式组合成单一样本。传统上,为了辨识神经元在检测什么,必须评估大量输入数据并与其他数据进行比较,以便由人类得出定性的结论,这既耗时又很难做到准确。该方法使我们能够直观地 pinpoint 产生高激活值的实际模式,而不受其他高度相关模式的干扰。
更正式地说,通过优化进行的特征可视化在以下用例中是有用的:
-
理解与混淆标签相关的模式,而无需领域专家的帮助:
-
这种情况在真实世界的音频数据中更为常见,在这些数据中,标签的声音常常与大量噪声混合在一起
-
这也可能发生在图像数据中
-
-
直接获取真实数据来测试关于神经网络学习内容的任何假设并非易事,且无法通过基于梯度的特征归因技术在可用数据上得到证明。
神经元解释技术的核心是神经输入优化,这是一种修改神经网络输入数据的过程,目的是使其在选择的神经元上产生高度激活。记住,在训练过程中,我们优化神经网络的权重以减少损失值。在这种技术中,我们随机初始化一个输入并优化该输入数据,使其在选定的神经元上产生高度激活,实际上将输入数据视为神经网络的权重。梯度可以自然地计算到输入数据阶段,使得在应用学习率后,可以根据计算的梯度更新输入数据。该技术还允许你联合优化多个神经元,使它们同时高度激活,并获得一张展示两种不同神经元如何共存的图像。
图 12.1展示了efficientnet-b0模型中从低级到中级和高级模式的概念:
图 12.1 – 来自 efficientnet-b0 模型的随机滤波器优化图像示例
如果你观察高层次的滤波器模式,第一个在随机滤波器上的优化图像看起来有点像花朵,第二个图像则像叶子图案。
然而,这种技术的一个主要警告是,得到的优化输入数据可能并不能代表与神经元相关的模式的所有真实变化。即使是像图像这样的动态输入数据变量,也可以被优化以多种方式展示模式,但最终的优化输入仍然可能会遗漏一些模式的表现。解决这个警告的一个好方法是,首先获得初始的优化输入数据变体,然后执行后续优化,并确保优化后的输入数据与初始变体不同。这可以通过联合优化一个额外的组件——即初始优化输入数据与当前优化输入数据之间的负余弦相似度来实现。这一技术有助于生成多样化的输入数据示例。但在你能够优化输入数据并尝试解释神经元之前,你需要一个策略来选择最佳的神经元进行输入数据优化,这将在下一节中讨论。
查找需要解释的神经元
在当今的最先进架构中,拥有数百万甚至数十亿个神经元,解释每一个神经元是不可行的,坦率来说,这也是浪费时间。选择要解释的神经元应根据你的目标来决定。以下列表展示了一些不同的目标和选择合适神经元的相关方法:
-
找出某个预测标签或类别模式的样子:在这种情况下,你应该简单地选择一个特定的神经元,用于预测目标标签或类别。这通常是为了理解模型是否很好地捕捉了该类别的模式,或者它是否学习到了无关的特征。在多标签场景中,多个标签总是一起存在,你希望解耦这些标签,从而更好地理解与单一标签相关的输入模式,这时这也会很有用。
-
想要理解在数据集中为什么某个特定标签可以被预测,或者一般而言所有标签的潜在原因:在这种情况下,你应该从全局神经元重要性评分中选择潜在中间层中最具影响力的神经元。全局神经元重要性可以通过聚合集成梯度方法的结果来获得,该方法应用于你验证数据集中的所有神经元(此方法在第十一章,解释神经网络预测中有介绍)。然后可以对所有神经元的影响值进行排名,并挑选出最重要的神经元。
-
找出基于显著性解释技术的预测原因的细分理由:在这种情况下,您应选择具有最高激活值和最高重要性得分的神经元。高度激活的神经元并不一定意味着它对某种预测很重要。此外,重要的神经元并不意味着该神经元被激活。利用整合梯度的重要性值和激活值来获取最重要的神经元将有助于确保选择您关心的神经元。此外,如果基于初始输入数据显著性地图有一个关注区域,您可以进一步通过仅选择影响所选关注区域的神经元来过滤出更多神经元。
-
理解多个标签或类别之间的相互作用:在关注多个标签或类别之间的关系重要的情况下,您可以选择捕捉这些相互作用的神经元。识别在预测多个标签或类别时激活度高且重要性得分高的神经元。分析这些神经元可以帮助您理解模型如何捕捉不同标签或类别之间的关系,并可能揭示改进的潜在领域。
-
研究模型对抗性攻击的稳健性:在这种情况下,您应选择对输入数据的对抗性扰动敏感的神经元。您可以生成对抗性示例,有关如何操作请参见第十四章,分析对抗性能,然后使用整合梯度等技术计算神经元的重要性得分。通过可视化受对抗性扰动影响最大的神经元,您可以深入了解模型的脆弱性并探索潜在的防御措施。
-
探索学习特征的分层结构:在这种情况下,您应选择来自 NN 不同层的神经元,以理解模型如何学习分层特征。选择早期层的神经元以研究低级特征,选择更深层次的神经元以研究高级特征。您还可以选择多个神经元以协同优化输入数据以获得高激活,以了解同一输入数据中多神经元学习模式的存在方式。可视化这些神经元可以帮助您理解模型对数据的内部表示及其如何构建越来越复杂的特征。这可以提供关于模型学习过程及其改进潜力领域的见解。
-
分析模型在不同数据集上的泛化能力:为了了解模型在新数据上的泛化能力,应该选择在不同数据集上始终重要的神经元。使用集成梯度等技术计算不同数据集上的神经元重要性得分,并识别在所有数据集上保持高重要性得分的神经元。通过可视化这些神经元,你可以深入了解模型的泛化能力,并发现潜在的改进领域。
现在我们已经建立了选择神经元进行解释的方法,让我们从实际的角度开始探索如何用图像输入数据解释神经元!
解释学习到的图像模式
解释接收图像数据的神经网络(NN)开启了解释的新范式,即可视化神经元正在检测的内容。以音频输入数据为例,解释神经网络可以使我们听到神经元检测到的内容,类似于我们在图像数据中可视化模式的方式!根据你的目标选择你想理解的神经元,并通过对图像数据的迭代优化,激活该神经元以可视化它检测到的模式。
然而,实际上,基于神经元优化图像数据时,存在一个问题,即生成的图像通常会产生被认为是噪声、不可解释且不美观的高频模式。高频模式被定义为那些强度较高且变化迅速的像素。这主要是由于像素可以表示的值范围通常没有约束,而且单独的像素并不是我们关心的语义单元。放大生成的图像可能会使其更具可解释性,但解释效果会因为需要进行人工评估和额外工作而降低。
这个问题可以通过以下技术有效缓解:
-
频率惩罚 – 示例技术如下:
-
在优化过程中随机使用双边滤波器对图像进行模糊处理,这样做的好处是还能保留边缘模式
-
在优化过程中保守地惩罚相邻像素之间的变化
-
-
图像增强
-
图像预处理 – 示例技术如下:
-
数据去相关
-
快速傅里叶变换
-
让我们通过使用在 ImageNet 数据集上预训练的 121 层 densenet 模型,继续我们的学习之旅。
使用图像输入数据和集成梯度来解释预测结果
在本节中,我们将探讨预测,并通过一个实际教程来解释如何解释 CNN 模型的预测,该模型接受带有集成梯度的图像输入数据,并提供一些对模型做出预测的原因的见解。在本教程中,我们将发现预测解释中缺失的必要答案,这将为我们理解 CNN 模型提供帮助。我们将按以下步骤进行:
-
本教程中,我们将使用
lucent库,它提供了通过优化特征可视化来解释神经网络的方法。此外,我们还将使用torch库来处理 densenet 模型。我们还将使用captum库来使用集成梯度方法。让我们从导入所有必要的库开始:import glob import numpy as np import torch import torch.nn as nn import torchvision.transforms as transforms from PIL import Image from captum.attr import IntegratedGradients from captum.attr import NoiseTunnel from captum.attr import visualization as viz from lucent.optvis import render, param, objectives from lucent.optvis.objectives import diversity -
接下来,我们将定义一个预训练的 densenet 模型的类:
class CNN(nn.Module): def __init__(self, num_classes, model='resnet50'): super(CNN, self).__init__() self.num_classes = num_classes self.chosen_model = model if self.chosen_model=='densenet121': self.model = models.densenet121(pretrained=True) self.classifier = nn.Sequential( nn.Dropout(p=0.1), nn.Linear(self.model.classifier.in_features, 256, bias=False), nn.ReLU(), nn.BatchNorm1d(256), nn.Linear(256, 128, bias=False), nn.ReLU(), nn.BatchNorm1d(128), nn.Linear(128, self.num_classes, bias=False), nn.BatchNorm1d(self.num_classes), ) self.model.classifier = self.classifier model_parameters = filter(lambda p: p.requires_grad, self.model.parameters()) params = sum([np.prod(p.size()) for p in model_parameters]) def forward(self, x): return self.model(x) -
我们将使用定义好的模型类加载一个名为
HAM10000的图像数据集上预训练的权重,该数据集包含七种不同的皮肤病变类别:checkpoint = torch.load('0.8228 checkpoint.pt', map_location=torch.device('cpu')) model = checkpoint['model'] -
预训练模型的预测层的第七个索引被训练用于预测黑色素瘤,这是一种皮肤癌。让我们看看
ISIC-2017数据集中的一些黑色素瘤示例,并看看当预测这些图像时,预训练模型到底关注了什么。我们将使用captum库中的集成梯度方法。首先,让我们定义需要的预处理步骤,以便进行模型推理,这将把numpy图像数组转换为torch张量,调整图像大小为预训练的图像尺寸,并进行归一化:resize_transform = transforms.Compose([ transforms.ToTensor(), transforms.Resize((224, 224)), ]) norm_transform = transforms.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])均值和标准差值直接来自 ImageNet 数据集,并用于预训练模型,因为在
HAM10000数据集上预训练之前,它已在 ImageNet 上进行过预训练。此外,224的维度也来自于 ImageNet 预训练设置。由于我们需要在调整大小后的中间结果,因此我们单独定义了调整大小和归一化的逻辑。 -
接下来,我们将使用
glob库加载提供的数据集文件夹中的所有黑色素瘤图像:all_melanoma_images = glob.glob("lesions_augmented_data/Data/test/Melanoma/*.jpg") -
我们将使用
captum库实现的集成梯度和噪声隧道方法,以平滑结果中的归因噪声。让我们定义执行这些组件所需的实例,并定义我们感兴趣的黑色素瘤目标类的预测类索引:integrated_gradients = IntegratedGradients(model) noise_tunnel_applyer = NoiseTunnel(integrated_gradients) prediction_label_index = 6 -
现在我们可以遍历前六张图像,应用预处理,使用
captum中的集成梯度方法,并最终可视化原始图像和获得的输入重要性热力图:for melanoma_image in all_melanoma_images[:6]: pil_image = Image.open(melanoma_image) img = np.array(pil_image) transformed_image = resize_transform(img) input = norm_transform(transformed_image).unsqueeze(0) attributions_ig_nt = noise_tunnel_applyer.attribute( input, nt_samples=10, nt_type='smoothgrad_sq', target= prediction_label_index) _ = viz.visualize_image_attr_multiple( np.transpose(attributions_ig_nt.squeeze().cpu().detach().numpy(), (1,2,0)), np.transpose(transformed_image.squeeze().cpu().detach().numpy(), (1,2,0)), ["original_image", "heat_map"], ["all", "positive"], cmap='turbo', show_colorbar=True)这将展示在图 12.2中呈现的可视化结果:
图 12.2 – 来自 ISIC-2017 数据集的六张真实黑色素瘤图像,以及模型的基于梯度的归因
在前五个例子中,模型似乎主要集中在较暗的斑点上,但它仍然会考虑周围的皮肤,尽管关注度较低。这可能表明模型在一定程度上依赖周围皮肤来预测黑色素瘤。但这也引发了一个问题:模型是在根据皮肤的暗度来预测黑色素瘤,还是在识别某种模式,还是两者兼有?在最后一个例子中,模型似乎没有特别集中,且并没有真正关注那些暗斑。这可能意味着皮肤有一些与黑色素瘤相关的模式,这些模式未必是颜色较深的。还有一些问题通过这些例子无法真正解答,具体如下:
-
这个模型是否依赖皮肤颜色来预测黑色素瘤?还是说它实际上是通过模式来预测的?
-
模型究竟检测到的模式是什么?
为了回答这些问题,我们将使用lucent库来可视化模型学到的模式,从而自信地预测黑色素瘤。
实际可视化带有图像输入数据的神经元
在本节中,我们将继续前面的教程,进一步探讨如何使用优化技术实际可视化带有图像输入数据的神经元,以获得对 CNN 模型所学模式和行为的深入理解。这个过程涉及选择要解释的神经元,为这些神经元优化图像数据,并应用正则化技术来生成可视化的可解释模式。通过可视化模型所学到的模式,我们可以更好地理解模型的预测,并回答传统特征重要性方法无法揭示的问题。
通过遵循本教程中概述的步骤,你可以可视化你的深度神经网络所学到的模式,从而更深入地理解模型的预测及其对这些预测有贡献的特征。这有助于回答关于模型是否依赖于某些特征的问题,比如皮肤颜色或黑色素瘤的形状,并提供关于模型模式和行为的宝贵见解。让我们开始吧:
-
首先,让我们定义必要的变量。我们希望可视化
Melanoma类别的图像模式,它位于第六个预测层索引处,因此我们必须按如下方式定义我们要优化的参数:param_to_optimize = "classifier_8:6" -
接下来,在第一次迭代中,我们将使用
lucent,具体来说,CPPN 由多个卷积层组成,激活函数是由元素级的正切、平方、除法和连接操作构成的组合函数。这意味着我们不是直接优化图像,而是优化生成主网络输入图像的 CPPN 卷积网络的参数。反向传播可以一直执行到生成的输入图像的第一层 CPPN 网络。初始输入图像是一个固定图像,包含图像中心的圆形区域,圆心的值接近零,并且逐渐增加到圆边缘。然而,使用 CPPN 时,学习率通常需要较低才能正常收敛。让我们定义一个大小为224的 CPPN 配置,并使用学习率较低的Adam优化器:cppn_param_f = lambda: param.cppn(224) cppn_opt = lambda params: torch.optim.Adam(params, 4e-3) -
最后,让我们利用定义的变量,使用 GPU 配置的模型可视化预测层中捕获的黑色素瘤部分的模式:
model.cuda() images = render.render_vis( model, param_to_optimize, cppn_param_f, cppn_opt, thresholds=np.linspace(0, 10000, 100, dtype=int).tolist(), verbose=True, show_inline=True )thresholds列表组件控制优化步骤的数量,并可视化优化图像的中间步骤号。此外,render_vis方法中内置的一个隐藏组件是transforms组件。transforms组件添加了最小化的增强,例如填充、抖动、随机缩放和随机旋转,以减少优化图像中的随机噪声。之前代码的结果如 图 12.3 所示:
图 12.3 – 优化 CPPN 的进展,以生成在黑色素瘤类别预测中具有高激活度的图像
从这个过程中生成了一个相当好的图像,看起来像是实际的黑色素瘤。
-
让我们来找出这张图像的黑色素瘤概率。我们可以通过定义进行推断所需的预处理方法来实现这一点:
inference_transform = transforms.Compose([ transforms.ToTensor(), transforms.Resize((224, 224)), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) -
在这里,我们预测从过程生成的最终优化图像的皮肤病变类别,同时禁用梯度计算:
def inference_lesion_model(img): transformed_image = inference_transform(img).unsqueeze(0) with torch.no_grad(): output = torch.nn.functional.softmax(model(transformed_image), dim=1) return output output = inference_lesion_model(images[-1].squeeze())结果显示图像被预测为黑色素瘤的概率为 100%!
然而,仅凭图像并不能明显表明所有不同类型的图像集可以被识别为黑色素瘤。有些类别可以在单张图像中充分表示,但有些标签确实无法在单张图像中表现出来。以背景类为例:不可能将每一个背景图像都放在一张图像中。以下是一些基于结果可能有用的问题:
-
皮肤的颜色是否重要?
-
黑色素瘤的形状是否重要,因为最终生成的图像似乎具有相似的黑色素瘤模式?
-
黑色素瘤斑块的颜色重要吗?这里有一些绿色斑块,它们的图案与红色斑块相似。
-
-
这里提到的用来确保多样性的损失函数可以提供更多的洞察。现在,让我们利用多样性目标与原始黑色素瘤预测层的目标一起,同时优化四张输入图像。批量模式功能不支持
lucent中的 CPPN,只支持基本输入图像初始化的param模块:obj = objectives.channel("classifier_8", 6) - 1e2 * diversity("input") batch_param_f = lambda: param.image(224, fft=True, decorrelate=True, batch=4) batch_images = render.render_vis( model, obj, batch_param_f, thresholds=np.linspace(0, 500, 50, dtype=int).tolist(), show_inline=True, verbose=True, )关于图像初始化方法的两个额外要点如下:
-
fft代表快速傅里叶变换 -
decorrelate对图像输入应用了 SVD
这两种技术在研究中得到认可,能够加速收敛,减少高频图像,并生成更美观的图像。
结果如图 12.4所示:
-
图 12.4 – 四张共同优化的图像,能够在黑色素瘤神经元上强烈激活
这些图像看起来非常独特。非黑色部分可能模拟了皮肤。我们来看一下每张图像对应黑色素瘤类别的概率,来验证一下:
outputs = []
for img_idx in range(4):
img = batch_images[-1][img_idx]
output = inference_lesion_model(img)
outputs.append((output[0][6], output.argmax()))
这将产生如下的数组:
[(tensor(0.9946, device='cuda:0'), tensor(6, device='cuda:0')),
(tensor(0.9899, device='cuda:0'), tensor(6, device='cuda:0')),
(tensor(0.7015, device='cuda:0'), tensor(6, device='cuda:0')),
(tensor(0.9939, device='cuda:0'), tensor(6, device='cuda:0'))]
它们的黑色素瘤概率都很高!由此可以得出以下结论:
-
模型在检测黑色素瘤时不太依赖皮肤的颜色。皮肤颜色所能提供的最大增益可能只是大约 3% 的概率提升。
-
模型主要依赖于底层低级模式来检测黑色素瘤。
-
模型对黑色素瘤斑块的颜色不太依赖。第一张生成图像和真实图像中的黑色素瘤颜色偏红,而批量生成图像中的黑色素瘤斑块颜色是黑色的。
-
模型能够从使用的真实图像中检测到较小的黑色素瘤信号。
这里的结果展示了每种洞察技术之间的互补性。我们将通过一些有用的笔记来结束这个话题,讲解关于神经元通过优化技术进行模式可视化的一般情况:
-
一些问题比其他问题更难收敛,有些问题根本无法收敛。准备好尝试多种设置,看看能否得到一个能够在你选择的神经元、通道或整个层上强烈激活的输入。你甚至可以选择多个神经元,看看它们如何相互作用!
-
损失函数可能会变得非常负,且输入收敛的越多,损失越负。这是好的,因为损失被定义为结果激活值的负数。
-
正则化技术是通过优化生成合理输入的关键。
-
使用真实数据和多样化的优化数据来理解模型学习到的检测模式。单一的优化数据通常无法代表神经元能够检测到的所有模式。
-
在本教程中,我们使用了最终分类层,这使得找到对选定神经元激活度高的样本变得更容易。如果选择的是一个中间神经元,请确保找到对该神经元激活度最高的数据集。
-
lucent库是基于pytorch的模型,而lucid库则是基于 TensorFlow 的模型,它们专注于图像可视化,但也可以适应其他输入变量类型,如文本。然而,目前在这方面的研究较少,尚未找到针对其他变量类型的良好正则化技术来实现更快的收敛。
总的来说,通过优化技术可视化神经元可以为机器学习(ML)模型的模式和行为提供有价值的洞察,但这需要实验和对输入及正则化技术的谨慎考虑。作为一个额外的收获,通过了解如何执行预测解释和神经网络(NN)解释,我们将发现一种叫做反事实解释的方法,可以使解释在一般情况下更为有效。
发现反事实解释策略
反事实解释或推理是一种通过考虑替代的和反事实的情境或“如果……会怎样”的情况来理解和解释任何事物的方法。在预测解释的背景下,它涉及识别输入数据的变化,这些变化会导致不同的结果。理想情况下,应该识别出最小的变化。在神经网络解释的背景下,它涉及到可视化目标标签或中间潜在特征的反面。这种方法是有意义的,因为它与人类自然解释事件和评估因果关系的方式密切相关,最终能够帮助我们更好地理解模型的决策过程。
人类倾向于从因果关系的角度思考,我们常常探索替代性可能性,以便理解事件或决策的原因。例如,当试图理解某个决策为何做出时,我们可能会问:“如果我们选择了不同的选项,会发生什么?”或“是什么因素导致了这个结果?”这种推理帮助我们识别出影响决策的关键因素,并且让我们从经验中学习。机器学习模型的反事实解释遵循类似的思维过程。通过展示如果输入实例不同将会导致不同预测的替代输入实例,反事实解释帮助我们理解在模型决策过程中,哪些输入特征最为关键。这种解释方式使得用户能够更直观地理解模型的推理过程,并有助于提高他们对模型预测的信任度。
反事实推理补充了特征重要性和神经元可视化技术。结合这些方法,可以更全面地理解模型是如何做出决策的。反过来,这也有助于用户更好地评估模型的可靠性,并根据模型的预测做出更有根据的决策。
摘要
神经网络解释是一种与解释模型做出的预测不同的模型理解过程。通过手动发现真实图像和优化合成图像,使其在选定神经元上高度激活进行解释,是可以一起应用的技术,以理解神经网络。实际上,当你有目标去揭示特定预测标签或类别模式的外观,洞察影响你数据集中某个特定标签预测的因素,或者所有标签的预测时,神经网络的解释会非常有用,并能详细分析预测背后的原因。
在尝试将此技术应用于你的使用案例时,可能会遇到一些小问题,因此不要害怕在你的目标是解释神经网络时,尝试调整本章中介绍的参数和组件。
在下一章,我们将探索你可以从数据和模型中获得的另一个层面的洞察,即偏见与公平性。
第十三章:探索偏见与公平性
有偏见的机器学习模型会产生并放大对某些群体的不公平或歧视性预测。这些模型可能会产生偏见的预测,导致如社会或经济不平等等负面后果。幸运的是,一些国家有反歧视和平等法律,保护少数群体免受不利待遇。机器学习从业者或任何部署有偏见模型的人可能面临的最糟糕情况是收到法律通知,要求支付重罚款,或收到律师函被起诉并被迫关闭其已部署的模型。以下是一些此类情况的例子:
-
叫车应用 Uber 因其面部验证系统在英国受到两个工会的法律诉讼,该系统对深色皮肤人群显示出种族偏见,表现为更频繁的验证错误。这阻碍了他们作为 Uber 司机的工作(
www.bbc.com/news/technology-58831373)。 -
创作者因 YouTube 对他们的种族及其他少数群体歧视而提起诉讼,因为 YouTube 的算法自动删除了他们的视频,并没有给出合理解释,导致他们无法通过广告收入获利(
www.washingtonpost.com/technology/2020/06/18/black-creators-sue-youtube-alleged-race-discrimination/)。 -
Facebook 因其住房广告涉嫌种族、性别、宗教、家庭状态和残疾歧视,遭到美国住房部门起诉并需支付不到 500 万美元的罚款(
www.npr.org/2019/03/28/707614254/hud-slaps-facebook-with-housing-discrimination-charge)。
因此,必须格外小心,避免对机器学习模型所接触的基础数据和环境中的敏感和受保护属性产生偏见。在本章中,我们将逐步探讨这个话题,从探索偏见的类型开始,学习检测和评估识别偏见与公平性的方法,最后探讨减轻偏见的途径。本章将介绍的概念和技术适用于所有机器学习模型。然而,偏见缓解是一个例外;我们将在其中探讨一种特定于神经网络的方法,该方法能够可靠地缓解偏见。更正式地说,我们将涵盖以下主题:
-
探索偏见的类型
-
理解 AI 偏见的来源
-
发现偏见与公平性评估方法
-
评估深度学习模型的偏见与公平性
-
针对不同使用案例量身定制偏见与公平性措施
-
减少 AI 偏见
技术要求
本章包含一些 Python 编程语言中的实际实现。要完成这些内容,您需要在计算机上安装以下库:
-
pandas -
matplotlib -
scikit-learn -
numpy -
pytorch -
transformers==4.28.0 -
accelerate==0.6.0 -
captum -
catalyst
代码文件可以在 GitHub 上获取,链接为github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_13。
探索偏差的类型
偏差可以被描述为对某个特定观点、意见或信仰系统的自然倾向或倾向,无论其是否被视为正面、中立或负面。另一方面,AI 偏差特指当数学模型延续其创建者或基础数据中固有的偏差时发生的情况。需要注意的是,并非所有信息都被视为偏差,有些信息也可以被视为知识。偏差是一种主观信息,而知识指的是通过学习、经验或研究获得的事实性信息、理解或意识。换句话说,知识是不带偏见的真理。
注意
不要将本书中的偏差与机器学习中臭名昭著的“偏差与方差”概念中的偏差混淆。在这个概念中,偏差指的是机器学习模型在执行某个任务时的简单性。为了完整性,方差指定了模型对数据特征变化的敏感性。在这里,高偏差对应于低方差和欠拟合行为,表明机器学习模型过于简单。低偏差则对应于高方差和过拟合行为。
在本书中,我们将以更口语化的方式使用偏差一词。诸如种族、性别和年龄等更为人知的偏差特征被视为社会偏差或刻板印象。然而,偏差的范围要广得多。以下是一些其他类型偏差的有趣例子:
-
文化偏差:文化视角、价值观和规范对判断、决策和行为的影响。它可以通过偏倚的数据收集、失衡的训练数据或反映文化偏见的算法在机器学习模型中表现出来。
-
认知偏差:在思维或决策过程中系统性地偏离理性思考的模式。让我们看几个认知偏差的例子,并探讨这些偏差在机器学习项目中可能表现出的现象:
-
确认偏差:偏向于支持现有信念的数据。
-
锚定偏差:在模型训练过程中过度强调某些特征或变量。
-
可用性偏差:依赖于易于获取的数据源,可能忽视相关但不易获取的数据源。
-
过度自信偏差:高估模型的能力或准确性。
-
事后偏差:在观察到模型预测结果后,认为这些预测结果是可以预见的。
-
自动化偏差:过度依赖模型输出而不进行批判性评估
-
框架偏差:通过有偏的数据呈现影响模型的学习过程
-
选择偏差:非随机抽样导致的不具代表性数据
-
抽样偏差:由于样本不具代表性导致的数据偏倚
-
报告偏差:由于个人的偏好或信仰导致的错误陈述
-
-
算法偏差:算法中存在的任何偏差,如机器学习算法。另一个偏差的例子是聚合偏差,它是由于数据分组或聚合方法导致的预测偏倚。
-
测量偏差:数据收集和测量中的不准确或错误。
这些偏差类别仅仅是冰山一角,可能延伸到非常特定的偏差类别,例如政治偏差、行业偏差、媒体偏差等。现在我们了解了偏差的概念及其涵盖的范围,接下来让我们一起探讨 AI 偏差的来源。
理解 AI 偏差的来源
AI 偏差可能发生在深度学习生命周期的任何阶段。让我们逐个了解这些阶段中的偏差:
-
规划:在机器学习生命周期的规划阶段,随着项目目标、数据收集方法和模型设计的决定,偏差可能会出现。偏差可能源于主观选择、假设或使用不具代表性的数据源。项目规划者需要保持批判性的视角,积极考虑潜在的偏差,吸纳多元观点,并优先考虑公平性和伦理问题。
-
数据准备:这一阶段包括以下几个阶段:
-
数据收集:在数据收集阶段,如果收集的数据未能准确代表目标群体,偏差可能会悄然出现。多个因素可能导致这种偏差,包括抽样偏差、选择偏差,或者某些群体的代表性不足。这些问题可能导致创建不平衡的数据集,无法反映目标群体的真实多样性。
-
数据标注:偏差也可能渗透到数据标注过程中。每个标注员可能有自己固有的偏见,无论是有意识还是无意识,这些偏见可能会影响他们在给数据标注时的决策。如果标注员缺乏多样性或全面的培训,他们的偏见可能会渗入到标注中,最终导致开发出带有偏见的模型,进而延续不公平和歧视。因此,最终的综合数据可能包含多种偏见,甚至可能彼此冲突,从而造成学习过程中的困难。
-
-
模型开发:偏差可以通过两种方式在深度学习模型中引入:
-
特征选择:偏差可能来源于选择用于模型训练的特征。如果某些特征与受保护属性(如种族或性别)相关,模型可能会无意中学习并强化这些偏见,导致歧视性结果。
-
预训练模型:一个预训练的深度学习模型可能是一个有偏的模型。例如,如果模型在有偏的数据上进行训练,它可能会在预测中学习并延续这些偏见。即使进行了微调,偏见也不太可能消失。
-
-
传递模型洞察:偏见特别容易出现在解释模型行为时。理解和解释模型内部工作原理的过程涉及主观推理,并且容易受到偏见的影响。模型洞察的解释在很大程度上依赖于相关人员的视角和先入为主的观念,这可能会根据他们自己的信仰、经验或隐性偏见引入偏见。必须意识到这些潜在的偏见,并力求客观公正,以避免误解或加剧现有偏见。批判性思维和多元视角对确保传递的洞察准确反映模型行为、避免引入额外偏见至关重要。
-
模型部署:此阶段涵盖了模型部署时可能出现的偏见,包括以下几个组件:
-
用户互动:在模型部署过程中,当用户提供反馈或响应时,可能会引入偏见,特别是在反馈本身存在偏见,或系统根据用户特征做出不同响应时。例如,ChatGPT 界面中的聊天历史机制允许用户提供有偏的输入。
-
人类参与偏见:当人工审阅者或操作员根据模型预测做出决策时,可能会引入他们自己的偏见或不公正地解读输出结果。这会影响决策过程的公正性。
-
环境偏差:某些特征在未见过的领域可能会被不同地处理和感知,从而导致数据漂移。模型在开发阶段被评估为无偏,但随着新数据的加入,它仍然可能产生有偏的预测。
-
重新训练的偏见数据源:新数据可以被收集并标注用于重新训练,这可能成为偏见的来源。
-
-
模型治理:偏见可能出现在负责监控已部署模型的人员需要为各种类型的漂移设定阈值(将在第十六章中介绍,治理深度学习模型)、分析预测摘要和检查数据摘要时。设定这些阈值可能会基于主观决策或假设引入偏见。此外,在分析预测和数据摘要时,如果没有以谨慎和批判的思维方式进行,可能会忽视某些偏见,或不小心强化现有偏见。必须保持对这些偏见的敏感性,确保监控和分析过程严格进行,注重公正性和准确性。
现在我们已经发现了深度学习生命周期中各阶段可能的偏差来源,我们准备深入探索偏差检测和公平评估方法。
发现偏差和公平评估方法
公平和偏差是对立的概念。公平旨在确保所有个体或群体在决策中得到公平和平等的对待,而偏差则指不公平或不平等的对待。减少偏差是实现公平的关键步骤。偏差可能以不同形式存在,解决所有潜在偏差是复杂的。此外,理解在某个方面实现公平并不意味着完全消除一般性的偏差也很重要。
要了解我们的数据和模型在多大程度上存在偏差以及多么公平,我们需要一组偏差和公平度量来客观地衡量和评估。这将启用一个反馈机制,迭代且客观地减少偏差并实现公平。接下来,让我们来看一些你必须掌握的强健的偏差和公平度量工具,以实现公平:
-
基于平等代表性的度量:这一组度量关注的是数据或决策结果的平等比例,而不考虑错误:
- 不同影响:不同影响考察的是模型是否公平地对待不同群体,或者它们在所获得的结果中是否存在显著的相对差异,通过计算群体之间有利结果比例的比值来进行。选定群体在选定属性下的不同影响可以使用以下公式计算:
不同影响(组 A) =
(组 A 的正预测比例 ___________________________________________________________ 参考组或其他群体的正预测比例)
跨群体的不同影响可以求平均,以获得单一的全局代表值。
- 统计平等差异:这一方法与不同影响具有相似的益处,但通过使用差值而非比值,提供了绝对差异度量。绝对差异在你可以且需要将值转化为实际影响时很有用,比如基于新样本大小的被歧视个体数量。它可以使用以下公式计算:
统计平等差异 = |(特权组的正预测比例) − (非特权组的正预测比例)|
-
基于平等错误的度量:这些度量考虑的是不同群体之间错误率的偏差。
- 平均赔率差异 (AOD):这衡量了群体之间真实正例和假正例结果的平均差异。AOD 是通过计算不同群体间赔率差异的平均值来得出的。某个特定群体的赔率差异是通过以下公式计算的:该群体的正预测赔率与参考群体的正预测赔率之间的差异:
AOD = (1 / n) * Σ[(TPR _ ref − TPR _ i) + (FPR _ ref − FPR _ i)]
这里,n 是群体的总数,TPR _ i 是群体 i 的真实正例率(灵敏度),FPR _ i 是群体 i 的假正例率(误报率),TPR _ ref 是参考群体的真实正例率,FPR _ ref 是参考群体的假正例率。
- 平均绝对赔率差异 (AAOD):这与 AOD 类似,但在各个群体的计算中增加了绝对项,计算方式如下:
AOD = (1 / n) * Σ[|(TPR _ ref − TPR _ i) + (FPR _ ref − FPR _ i)|]
当你关心一般性差异而不仅仅是群体差异是否被偏好时,应该使用这个方法,而不是 AOD。
- 通过广义熵指数 (GEI) 衡量分配公平性:这是通过仅使用数值结果,设计来衡量基于整个群体中个体分布的贫富差距。GEI 的公式如下:
GE(α) = 1 / na(a − 1) ∑ i=1 n (na w a− n),a ≠ 0,1,
GE(α) = log(n) + ∑ i=1 n w i log(w i),a = 1,
GE(α) = − log(n) − 1 / n ∑ i=1 n log(w i),a = 0
其中,E T = ∑ i=1 n E i,w i = E i _ E T
E 是特定实体的选定属性值,E T 是所有实体的总和值,n 是实体或个体的总数。通过 GEI 的 α 参数可以配置两种基本的不平等概念:
-
泰尔指数:衡量所有群体之间所有个体的总体不平等。其 α 值为 1。
-
变异系数:通过计算群体个体的变异性来衡量不平等。群体的差异越大,该群体的不平等也越大。其 α 值为 2。
如果你想了解整体的不平等情况,可以使用泰尔指数作为主要选择,而当你想按群体理解不平等时,可以切换到变异系数。
-
个体公平性度量:基于相似个体的结果差异或相似性是一个单一的基于个体的度量。像 KNN 这样的邻近算法可以让你考虑个体的多个关联特征,并基于相似的例子计算公平性度量。你需要执行以下步骤:
-
使用 KNN 方法为每个个体找到具有相似特征的若干个例子,排除受保护属性。
-
使用选择的公平性度量计算结果的不平等性。这里最常用的度量是相似示例与个体结果的平均相似度。
-
-
通过平衡准确度的公平性准确度性能度量:这为分类模型的性能提供了平衡评估,特别是在处理不平衡数据集时。平衡准确度是通过计算每个类别的准确度的平均值来得到的。
虽然我们在这里介绍的度量指标仅涵盖了该领域中部分可用的偏差和公平性度量,但它足够通用,可以满足大多数机器学习的应用场景。现在,让我们探讨如何在深度学习项目中实际使用这些度量指标来衡量偏差和公平性。
评估深度学习模型的偏差和公平性
在这个实际示例中,我们将探讨广为人知的面部识别的现实应用场景。这个实际示例将用于接下来章节中偏差缓解的实际实施。面部识别的基础是生成特征向量,这些特征向量可以用来执行基于 KNN 的分类,这样新面孔就不需要经过额外的网络训练。在这个示例中,我们将训练一个分类模型,并使用传统的基于分类准确度的度量进行评估;我们不会演示该应用场景的识别部分,后者可以让我们处理未知的面部身份类别。
这里的目标是确保结果的面部分类模型具有较低的性别偏差。我们将使用一个公开可用的面部数据集,名为BUPT-CBFace-50,它涵盖了各种不同的面部表情、姿势、光照条件和遮挡的面部图像。该数据集由 10,000 个面部身份类别的 500,000 张图像组成。在这个实际示例中,您将需要至少 12 GB 内存的 GPU,以便在合理的时间内完成训练。在开始示例之前,请从官方来源下载数据集(buptzyb.github.io/CBFace/?reload=true)。您可以在与项目相同的目录中找到它。
让我们从一步一步的代码演示开始,再次使用 Python 和 pytorch 库,以及 catalyst:
-
首先,让我们导入必要的库,包括作为深度学习框架的
pytorch、用于跟踪和比较的mlflow、用于预训练 ResNet50 模型的torchvision、用于高效处理 PyTorch 模型的catalyst以及用于简单图像数据处理的albumentations:import os import albumentations as albu import numpy as np import pandas as pd import torch import torch.nn as nn from albumentations.pytorch.transforms import ToTensorV2 from PIL import Image from sklearn.model_selection import StratifiedShuffleSplit from torch.optim import SGD, Adam from torch.utils.data import DataLoader, Dataset from torchvision import models from torchvision.models import resnet50 import mlflow from catalyst import dl, utils, metrics from catalyst.contrib.layers import ArcFace from catalyst.loggers.mlflow import MLflowLogger from captum.attr import IntegratedGradients from captum.attr import NoiseTunnel from captum.attr import visualization as viz os.environ["CUDA_VISIBLE_DEVICES"] = "0"最后一行将设置您的机器的第一个 GPU 为 CUDA 可见,CUDA 是 GPU 的计算接口,用于执行计算。
-
接下来,我们将定义用于不同组件的配置。这将包括训练过程的特定参数,如批次大小、学习率、训练轮数、在训练过程中停止前的早期停止轮数,以及等待验证指标改进的轮数后才会减少学习率:
batch_size = 64 val_batch_size = 100 lr = 0.05 num_epochs = 20 early_stopping_epochs = 12 reduce_lr_patience = 3 -
此外,它还将包括我们为计算基于性别的偏差和公平性指标所选择的特权和非特权群体的规定。数据集中男性数量大约是女性的两倍,因此这里的特权群体预计为
Male:priviledged_group = 'Male' unpriviledged_group = 'Female' -
最后,我们将为
mlflow实验名称、模型保存目录和一些暂时不启用的额外参数设置名称:experiment_mlflow = "bias_mitigation_v1" logdir = 'experiments/face_modelv1' only_male=False distill_model=False -
接下来,我们将加载包含数据集元数据的 CSV 文件,主要包括已下载并解压的
BUPT_CBFace数据集的图像路径:train = pd.read_csv('face_age_gender.csv') image_path = train['image_path'].values targets = train['target'].values gender_data = train['gender'].values -
此外,我们还将设置
name2class映射器,以及类 ID 目标数组和类别数量:name2class = {name: idx for idx, name in enumerate(sorted(set(targets)))} id_targets = np.array([name2class[target] for target in targets]) num_classes = len(name2class) -
接下来,我们将对数据进行分层切分,将其划分为训练集和验证集,确保两个数据集都包含所有可用的人脸身份类别:
splitter = StratifiedShuffleSplit(test_size=.20, n_splits=2, random_state = 7) split = splitter.split(image_path, targets) train_inds, val_inds = next(split) train_images = image_path[train_inds] val_images = image_path[val_inds] train_targets = id_targets[train_inds] val_targets = id_targets[val_inds] train_gender_data = gender_data[train_inds] val_gender_data = gender_data[val_inds] if only_male: pass最后的
if语句逻辑将在下一节关于偏差缓解的内容中讲解。对于后续步骤,将pass的使用视为一个指示符。 -
接下来,我们将定义基于 ResNet50 模型的模型。我们将在这里使用 ARCFace 层,并以 ResNet50 为基础,这是一种度量学习算法。它利用角度边距损失来增强学习到的人脸嵌入的区分能力,从而实现更精确、更鲁棒的人脸识别,能够应对不同的姿势、光照和身份变化:
class ARCResNet50(nn.Module): def __init__(self, num_classes): super(ARCResNet50, self).__init__() self.model = models.resnet50(pretrained=True) s=2**0.5*np.log(num_classes - 1) s=13 self.model.fc = nn.Linear(self.model.fc.in_features, self.model.fc.in_features) self.head = ArcFace(self.model.fc.out_features, num_classes, s=s, m=0.15) def get_last_conv_features(self, x): pass def special_forward(self, x, targets=None): pass def forward(self, x, targets=None): outputs = self.model(x) outputs = self.head(outputs, targets) return outputs -
接下来,我们将初始化模型,分配给 GPU 使用,定义交叉熵损失变量,定义 SGD 优化器变量,并定义在验证性能下降时减少学习率的引擎:
if distill_model: pass model = ARCResNet50(num_classes=num_classes) model.to(device) criterion = nn.CrossEntropyLoss() optimizer = SGD(model.parameters(), lr=lr, momentum=0.9, weight_decay=1e-5) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, patience=reduce_lr_patience, factor=0.1) -
接下来,我们需要定义 PyTorch 数据集类,以接收图像文件路径和敏感属性,这些敏感属性包括性别数据、目标和指定的 albumentation 变换。最后一个变量将在下一节中使用:
class ImagesDataset(Dataset): def __init__(self, files, sensitive_attributes, targets=None, transforms=None, teacher_model_features=None): self.files = files self.sensitive_attributes = sensitive_attributes self.targets = targets self.transforms = transforms self.teacher_model_features = teacher_model_features def __len__(self): return len(self.files) def __getitem__(self, index): file = self.files[index] img = np.array(Image.open(file)) if self.transforms is not None: img = self.transforms(image=img)["image"] if self.targets is None: return img target = self.targets[index] sensitive_attribute = self.sensitive_attributes[index] if self.teacher_model_features is None: return img, sensitive_attribute, target else: teacher_model_feature = torch.from_numpy(self.teacher_model_features[index]) return img, sensitive_attribute, target, teacher_model_feature -
现在,我们要应用来自
albumentation库的一组简单变换操作。我们将定义一个方法,返回一个用于训练的带增广的变换实例,以及用于验证的无增广的变换实例。两者都需要变换实例将图像值转换为 PyTorch 张量:def get_transforms(dataset: str): if dataset.lower() == "train": return albu.Compose([ albu.Resize(224, 224), albu.HorizontalFlip(), albu.Normalize(), ToTensorV2() ]) else: return albu.Compose([ albu.Resize(224, 224), albu.Normalize(), ToTensorV2()]) -
接下来,让我们初始化数据集并随后加载数据集:
if distill_model: pass else: train_dataset = ImagesDataset(train_images, train_gender_data, train_targets, get_transforms('train')) val_dataset = ImagesDataset(val_images, val_gender_data, val_targets, get_transforms('valid')) loaders = {"train": DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=8), "valid": DataLoader(val_dataset, batch_size=val_batch_size, shuffle=False, num_workers=8)} -
现在,我们将定义辅助方法来帮助计算多类偏差和公平性指标,这些方法包括执行安全除法处理和零除法,防止 NaN 值,并计算假正例、真负例、总正例、总负例和总数据。由于这是一个多类问题,我们必须根据类别选择宏平均或微平均统计量。微平均方法将所有样本平等对待,而宏平均方法将所有类别平等对待。宏平均方法有一个潜在问题:如果某个少数类别的表现很好,它会给人一种模型总体表现很好的虚假印象。因此,我们将在这里使用微平均:
def compute_classwise_stats(y_pred, y_true): unique_classes = np.unique(y_true) num_classes = len(unique_classes) false_positives = np.zeros(num_classes) total_negatives = np.zeros(num_classes) true_positives = np.zeros(num_classes) total_positives = np.zeros(num_classes) for c_idx in range(num_classes): class_label = unique_classes[c_idx] class_predictions = (y_pred == class_label) class_labels = (y_true == class_label) false_positives[c_idx] = np.sum(class_predictions & ~class_labels) total_negatives[c_idx] = np.sum(~class_labels) true_positives[c_idx] = np.sum(class_predictions & class_labels) total_positives[c_idx] = np.sum(class_labels) return { "false_positives":false_positives, "total_negatives": total_negatives, "true_positives": true_positives, "total_positives": total_positives, "total": total_negatives + total_positives, } def safe_division(a, b): return a/b if b else 0.0 -
最后,我们将利用这些辅助方法定义一个方法,使用常见的计算结果来计算四个偏差和公平性指标——即,差异影响、统计平等差异、AOD 和 AAOD:
def compute_multiclass_fairness_metrics( y_pred, y_true, sensitive_attribute, priviledged_group, unpriviledged_group ): -
我们将首先使用辅助方法获得特权组和非特权组的假正例、真负例、总正例、总负例和总数据:
y_pred = y_pred.argmax(1) group_stats = {} for group in [priviledged_group, unpriviledged_group]: group_idx = sensitive_attribute == group group_stats[group] = compute_classwise_stats( y_pred[group_idx], y_true[group_idx]) -
接下来,我们将使用计算出的组统计数据计算特权组和非特权组的真实正例比例,以便直接用于计算差异影响和统计平等差异:
disparities = [] priviledged_true_positive_ratio = safe_division( np.sum(group_stats[priviledged_group]["true_positives"]),np.sum(group_stats[unpriviledged_group]["total"])) unpriviledged_true_positive_ratio = safe_division( np.sum(group_stats[unpriviledged_group]["true_positives"]),np.sum(group_stats[unpriviledged_group]["total"])) -
现在,我们将使用真实正例比例计算前面提到的两个指标:
disparate_impact = safe_division(unpriviledged_true_positive_ratio, priviledged_true_positive_ratio) statistical_parity_diff = priviledged_true_positive_ratio - unpriviledged_true_positive_ratio -
最后,我们将使用真实正例率
tpr和假正例率fpr来计算 AOD 和 AAOD:for group in [priviledged_group, unpriviledged_group]: group_stats[group]["fpr"] = safe_division( np.sum(group_stats[priviledged_group]["false_positives"]),np.sum(group_stats[priviledged_group]["total_negatives"])) group_stats[group]["tpr"] = safe_division( np.sum(group_stats[priviledged_group]["true_positives"]), np.sum(group_stats[priviledged_group]["total_positives"]) ) AOD = ( (group_stats[unpriviledged_group]["fpr"] - group_stats[priviledged_group]["fpr"]) + (group_stats[unpriviledged_group]["tpr"] - group_stats[priviledged_group]["fpr"]) ) / 2 AAOD = ( np.abs(group_stats[unpriviledged_group]["fpr"] - group_stats[priviledged_group]["fpr"]) + np.abs(group_stats[unpriviledged_group]["tpr"] - group_stats[priviledged_group]["fpr"]) ) / 2 return { "disparate_impact": disparate_impact, "statistical_parity_diff": statistical_parity_diff, "average_odds_diff": AOD, "average_abs_odds_diff": AAOD } -
我们将在训练过程中计算这四个指标,并能够在训练时监控和追踪这些指标。为了追踪实验,我们将记录参数并监控它们在每次迭代和每个 epoch 的表现。我们将使用 MLflow 来完成这个操作。让我们定义
mlflow日志记录器并记录我们在步骤 2中定义的参数:mlflow_params = dict( batch_size=batch_size, lr=lr, num_epochs=num_epochs, early_stopping_epochs=early_stopping_epochs, reduce_lr_patience=reduce_lr_patience, experiment_mlflow=experiment_mlflow, logdir=logdir, only_male=only_male, distill_model=distill_model, ) all_metrics = [ "loss", "accuracy", "disparate_impact","statistical_parity_diff", "average_odds_diff","average_abs_odds_diff" ] mlflow_logger = MLflowLogger(experiment=experiment_mlflow, tracking_uri="experiment/") mlflow_logger.log_hparams(mlflow_params) -
由于我们稍后需要一个稍微专业化的流程来计算自定义指标并执行偏差缓解方法,我们将使用
catalyst定义一个自定义的 runner,用于训练 ResNet50 模型。我们需要定义四个方法的三条自定义逻辑:on_loader_start(初始化指标聚合器功能)、handle_batch(获取损失)、on_loader_end(最终汇总批次指标并更新学习率调度器)以及get_loggers(将数据记录到 MLflow)。让我们从定义on_loader_start开始:class CustomRunner(dl.Runner): def on_loader_start(self, runner): super().on_loader_start(runner) self.meters = {key: metrics.AdditiveMetric(compute_on_call=False) for key in all_metrics} -
接下来,我们将定义批次处理逻辑,它将以自定义方式从批次数据加载器加载数据,使用在 runner 中初始化的模型执行前向传播,然后计算损失、准确率和多类公平性指标:
def handle_batch(self, batch): is_distill_mode = len(batch) == 4 if is_distill_mode: pass else: features, sensitive_attribute, targets = batch logits = self.model(features, targets) loss = self.criterion(logits, targets) accuracy = (logits.argmax(1) == targets).float().mean().detach().cpu() batch_metrics = { "loss": loss.item(),"accuracy": accuracy.item()} batch_metrics.update( **compute_multiclass_fairness_metrics(logits.detach().cpu().numpy(), targets.detach().cpu().numpy(), np.array(sensitive_attribute),priviledged_group, unpriviledged_group)) -
在同一方法中,我们还需要更新当前批次指标和聚合批次指标,以便它们能够正确地记录,并在训练模式下执行反向传播,而不是验证模式:
self.batch_metrics.update(batch_metrics) for key in all_metrics: self.meters[key].update( self.batch_metrics[key], self.batch_size ) if self.is_train_loader: loss.backward() self.optimizer.step() self.optimizer.zero_grad() -
现在,我们必须定义最后两个简单的方法:
def on_loader_end(self, runner): for key in all_metrics: self.loader_metrics[key] = self.meters[key].compute()[0] if runner.is_valid_loader: runner.scheduler.step(self.loader_metrics[self._valid_metric]) super().on_loader_end(runner) def get_loggers(self): return { "console": dl.ConsoleLogger(), "mlflow": mlflow_logger } -
最后,我们将初始化运行器并训练模型:
runner = CustomRunner() runner.train( model=model, criterion=criterion, optimizer=optimizer, loaders=loaders, logdir=logdir, num_epochs=num_epochs, valid_loader="valid", valid_metric="loss", # loss" minimize_valid_metric=True, fp16=False, verbose=True, load_best_on_end=True, scheduler=scheduler, callbacks=[ dl.EarlyStoppingCallback( patience=early_stopping_epochs, loader_key="valid", metric_key="loss", minimize=True ), dl.CheckpointCallback( logdir=logdir, save_best=True, load_best_on_end=True, metric_key='loss' ) ] )图 13.1 显示了
mlflow绘制的训练和验证集每个轮次的准确度、AOD、差异影响和统计平等差异的性能图:
图 13.1 – ResNet50 模型按训练轮次的性能图
验证集得分为 AAOD 和 AOD 各 0.424,准确度 0.841,差异影响 0.398,统计平等 0.051。
-
图表显示,尽管准确度随着每一轮训练不断提高,但偏差和公平性度量逐渐变差,但停滞在某个值。最低偏差的模型出现在第 0 轮训练时,那时一切几乎为零,包括准确度。尽管该模型没有偏差,但它根本没有任何用处。另一个有趣的观察是在第 16 轮训练时,模型成功地在男性样本上提高了验证准确度,而 AOD 值在同一时刻变得更高。根据具体情况,你可以选择第 15 轮训练的模型,该模型的准确度得分较好,但不是最佳的,且偏差得分较低。
-
为了进行更深的分析,让我们看看模型在使用 Captum 的集成梯度技术进行预测时关注的重点。我们将可视化大多数面向正面的图像。为了做到这一点,我们必须定义必要的转换方法:
resize_transform = albu.Resize(224, 224) norm_transform = albu.Compose([ albu.Normalize(), ToTensorV2() ]) -
现在,让我们定义要可视化的正面人脸:
val_df = pd.read_csv('val_pose_info.csv') straight_indexes = val_df[ (val_df['pitch']>-10) & (val_df['pitch']<10) & (val_df['yaw']>-10) & (val_df['yaw']<10) ].index.values -
最后,让我们可视化关注区域:
integrated_gradients = IntegratedGradients(model) noise_tunnel = NoiseTunnel(integrated_gradients) for val_idx in straight_indexes[:5]: #range(1): image_path = val_images[val_idx] pred_label_idx = val_targets[val_idx] pil_image = Image.open(image_path) img = np.array(pil_image) transformed_image = resize_transform(image=img)["image"] input = norm_transform(image=img)["image"].unsqueeze(0) attributions_ig_nt = noise_tunnel.attribute( input, nt_samples=5, nt_type="smoothgrad_sq", target=int(pred_label_idx) ) _ = viz.visualize_image_attr_multiple( np.transpose(attributions_ig_nt.squeeze().cpu().detach().numpy(), (1,2,0)), transformed_image, ["original_image", "heat_map"], ["all", "positive"], cmap="turbo", show_colorbar=True )图 13.2 显示了原始图像和模型的关注区域:
图 13.2 – 训练的 ResNet50 模型的显著性解释结果
- 这些可视化结果表明,模型在处理女性面孔时存在偏差,错误地将注意力集中在头发上。对于男性,模型并没有关注头发。模型也稍微关注了白色背景。我们将在下一节学习如何去除这种偏差。
现在我们理解了一些常见的偏差和公平性度量标准,我们需要知道在不同的使用案例中应使用哪些度量标准。
在不同使用案例中定制偏差和公平性度量
确定偏差和公平性度量标准以适应我们使用案例的过程,和确定一般模型性能评估度量标准的过程类似,正如在 第十章《探索模型评估方法》的工程化基础模型评估度量标准部分中所介绍的那样。所以,一定要去查看那个话题!然而,偏差和公平性有独特的方面,需要额外的启发式推荐。之前,我们探索了属于同一度量组的度量推荐。现在,让我们探索四个度量组的通用推荐:
-
当存在敏感和受保护属性时,平等代表性始终是期望的。因此,当你看到这些属性时,务必在数据和模型中使用基于平等代表性的度量。示例包括种族、性别、宗教、性取向、残疾、年龄、社会经济地位、政治倾向和犯罪历史。
-
预测性能一致性是处理敏感和受保护属性的机器学习模型的另一个期望特性。因此,当你看到这些属性时,务必在模型中使用基于错误的平等度量。
-
分布公平性度量和平等代表性度量都衡量不平等。然而,分布公平性直接作用于连续变量,而平等代表性度量作用于分类变量。可以对连续变量进行分箱处理,将其转化为类别,但如何选择合适的分箱策略并不简单。因此,当衡量偏差和公平性的变量是连续变量时,应使用分布公平性度量。
-
考虑所有潜在的偏差和公平性方面,并使用选择的偏差和公平性度量分别进行衡量。
- 假设有一个机器学习模型用于预测贷款审批。公平性的一个方面是确保贷款审批在不同的群体之间(如种族或性别)是公平的。确保数据的平等代表性以及由此产生的模型可以帮助你实现这一目标。然而,仅仅关注平等的代表性可能无法捕捉到完整的情况。例如,即使不同群体之间的整体贷款批准率是平等的,不同群体的利率差异可能非常显著。这种利率的差异可能导致不公平和不平等的结果,因为某些群体可能会被收取更高的利率,从而导致财务上的不利影响。使用分布公平性度量进行额外评估和监控可以帮助你理解影响,并帮助针对不利群体的偏差缓解。
-
在部署机器学习模型并在模型推断过程中接收个体数据时,评估结果的个体公平性非常有用。这里可以设置一个阈值,当个体公平性得分过高时,触发警报,并需要人工审查员进行评估并做出手动决策。
-
分别计算这些度量并通过敏感群体进行可视化对比,以感受群体间的公平性。这可以帮助你制定针对易受影响群体的偏差缓解响应。
-
预测解释可能有助于理解偏差和公平性的原因。
-
偏差和公平性度量可能与基于准确性的性能度量发生冲突。
此外,有两个全球性对立的观点值得一提,它们会影响你如何看待公平性:
-
我们都平等 (WAE):这一概念认为,由于数据中固有偏见的存在,数据可能无法准确地代表现实。
-
所见即所得 (WYSIWYG):这一观点认为,数据所呈现的信息反映了一个不带偏见的现实表现,即便它揭示了不平等现象。
任何一方极端的观点都将导致无法构建模型(WAE)或对公平偏见缺乏足够的重视,最终导致负面后果,正如本章前面提到的(WYSIWYG)。为了在我们的应用场景中创建一个成功的机器学习模型,我们需要战略性地平衡这两种观点,以便同时满足准确性表现和公平性要求。这里采用的一个好策略是运用常识,接受因果关系中看似合逻辑的部分(WYSIWYG),并采取公平的流程帮助我们理解和缓解偏见的局部方面(WAE),即使不能实现真正的公平。
以下是一些值得采用的、最有意义的观点示例:
-
采用 WYSIWYG:在个性化广告领域,使用机器学习的 WYSIWYG 观点将包括根据观察到的用户行为和偏好定制广告。例如,如果一个用户频繁与健身相关的内容互动,系统将展示健身设备或健身会员卡的广告。目标是提供与用户兴趣和需求相符的个性化体验。在这个应用场景中,任何关于 WAE 观点的内容都会使项目失败,因为它与个性化的目标相冲突。
-
采用类似于 75% WYSIWYG 和 25% WAE 的策略:在使用机器学习做出招聘决策的情境中,WAE 观点有助于倡导对所有候选人的平等考虑,避免性别、种族或其他受保护属性相关的偏见。WYSIWYG 观点则会允许仅根据候选人的资历和技能来构建机器学习模型。创建一个公平且无偏的选拔过程的策略将有助于为所有应聘者提供平等机会,同时仍能成功地构建一个有效的机器学习模型。
在度量标准方面,基于平等代表性的度量标准组与 WAE 观点相关,而基于平等错误的度量标准组与 WYSIWYG 观点相关。因此,如果在应用场景中涉及这两种观点,请选择这两组度量标准。
接下来,我们将探讨如何减轻机器学习模型中的偏见。
减轻 AI 偏见
AI 偏见是一种算法偏见,可能来自模型本身的学习过程,或者来自用于学习的数据。缓解偏见的最明显解决方案不是程序化的缓解方法,而是确保在收集数据时采用公平的流程。数据收集和准备过程只有在确保结果数据不仅通过敏感属性平衡,而且确保不包含所有固有和系统性偏见时,才能算是完全公平的。
不幸的是,基于敏感属性的平衡数据集并不能保证模型的公平性。不同子组之间,或数据中与多种因素相关的联动组,可能存在外观上的差异,这可能导致系统偏见。然而,当数据集相较于未考虑可观察敏感群体时是平衡的,偏见可以在一定程度上得到缓解。但这些属性到底是什么呢?在具有明确定义列名的表格结构数据中,识别数据属性可能更容易,但对于深度学习用的非结构化数据,几乎不可能涵盖所有可能的属性。
为了更深入地探讨实际示例,文本数据可以包含大量属性及其示例,例如语言、类型、主题、长度、风格、语气、时间段、作者身份、地理来源和文化视角。图像数据也可以包含大量属性及其示例,例如主题/内容、视角、光照、构图、颜色、纹理、分辨率、方向、背景和文化相关性。最后,音频数据也可以包含大量属性,例如类型、语言、时长、音质、乐器、声乐风格、节奏、情绪、文化影响和录音环境。
确保在所有方面的平等代表性很困难——尤其是当数据轻松获得且原始数据本身已经高度不平衡,仅在某些类别中有少量示例,而其他类别有大量示例时。理想情况下,偏见缓解应始终从数据准备阶段开始执行。然而,如果无法做到这一点,程序化的偏见缓解方法可以在数据收集后应用。
为了更清楚地了解这些方法,请查看图 13.3,它展示了各种减少偏见的 approaches:
图 13.3 – 在两个机器学习生命周期阶段下,偏见缓解的四个步骤
程序化的偏见缓解提供了可以在模型构建过程的三个不同阶段应用的方法:
-
预处理:这是数据准备阶段中的一部分。以下是属于此组的一些偏见缓解方法示例:
-
消除受保护属性在模型中的使用。然而,关于受保护属性的信息可能仍然会通过其他相关属性呈现。此外,某些属性与预测所需标签的其他关键信息紧密相连,且无法轻易移除。例如,面部图像中的性别。
-
不平衡影响移除器,可在 AIF360 开源库中使用。它通过删除数据行来平衡数据集群体的比例,从而实现更好的不平衡得分。
-
在训练过程中,对特权类别损失加权,使其低于弱势类别的损失,从而使各类别之间的误差更加平等。
-
针对弱势群体的数据增强。增强技术可以通过两种方式增加数据变化:
-
通过在弱势群体中添加更多数据变化进行增强,有助于提高该群体的准确性,并有助于平衡错误率,特别是当弱势群体的样本不足时。
-
反事实角色反转增强技术将特权群体反转为弱势群体,反之亦然,允许实现平等代表性。以下是根据变量类型可以使用的一些增强技术:
-
• 文本:使用词语交换
• 所有变量类型:使用风格迁移技术。以下是一些示例技术:
-
使用训练过的 StarGAN 生成式 AI 技术,这是一种图像生成器,能够反转一个人的性别、改变一个人的年龄等等。
-
通过减少选定中间层与图像之间的距离,优化期望图像以模仿另一图像的风格。这种方法被称为输入优化传递,基于上一章提到的神经解释技术。
-
针对已部署模型,通过替换输入进行针对性的反事实角色反转,而不是额外的增强。这允许您在部署过程中显式地控制结果的平等性。
-
OpenAI 在 Dall-E 的文本到图像提示中随机将“male”更改为“female”,因为该模型有偏向性,倾向于将男性描绘为教授和 CEO 等角色。
-
-
处理过程中:这是模型开发机器学习生命周期阶段的一部分,是训练模型的过程。以下是属于这一组的偏差缓解方法的示例:
-
知识蒸馏:这一概念在第八章《探索监督深度学习》中介绍。其思路是,使用更大且更具代表性数据集训练的教师模型,相比于用较小定制数据集训练的学生模型,更不易产生偏见。理想情况下,教师模型中的公平性将被蒸馏到学生模型中。然而,当用有偏的教师模型进行蒸馏时,知识蒸馏也可能会导致生成的学生模型产生更强的偏见。
-
对抗性去偏:该方法通过迭代训练一个分类器,以优化预测准确性,同时最小化对抗性模型从预测中推断受保护属性的能力。分类器旨在对目标变量进行准确预测,而对抗模型则试图辨别与偏差相关的敏感属性。这一同时训练的过程创造了一个竞争环境,使得分类器能够学习编码目标变量的重要信息,同时减少潜在偏见特征的影响。通过这种方式,对抗性去偏通过减轻敏感属性的影响并增强模型预测的整体公平性来促进公平性。
-
正则化:在深度学习中,正则化指的是对神经网络、数据或训练过程进行的任何添加或修改,旨在增加模型对外部数据的泛化能力。这可以间接帮助减少模型中的偏差。一些常见的正则化方法包括 dropout 层、L1/L2 正则化、批量归一化、组归一化、权重标准化、随机深度、标签平滑和数据增强。通过改善泛化能力,正则化方法有助于模型学习数据中的更一般性模式,而不是过度拟合训练集中的偏见特征。该方法在第二章中有更为详细的探讨,深度学习架构设计。
-
-
后处理:这是模型开发机器学习生命周期阶段的一部分,但仅涵盖处理已训练模型输出以减轻偏差。以下是属于该组的一些偏差缓解方法示例:
-
测试时反事实角色逆转增强集成:该方法涉及执行两个预测,每个使用相反的角色,并对预测结果进行集成。使用最大值或指数均值操作进行集成比平均操作效果更好,具体见
www.amazon.science/publications/mitigating-gender-bias-in-distilled-language-models-via-counterfactual-role-reversal。 -
均衡机会后处理:该方法通过通过阈值优化来调整分类器的输出预测,以确保不同组之间的假阳性和假阴性率相等,具体来说,分别为每个受保护组单独确定预测阈值。
-
需要注意的是,通过偏差缓解方法得到的更公平的模型可能会导致基于准确率的指标下降。如果在准确率指标上的损失足够小,那么公平的模型是非常值得追求的,因为使用偏差缓解方法不会带来问题。为此,始终从创建基线模型开始,并使用所选择的偏差和公平性指标评估公平性,以确保在使用任何偏差缓解方法之前,模型中确实存在偏差。偏差缓解方法总是会显著增加训练时间,因此确保它是值得的。
此外,还有一些与偏差相关的行为是有用的,需要了解:
-
决策算法往往偏向于更常见的事件,因此平衡的数据有助于减少这种偏差。
-
剪枝模型增加了偏差(更多内容请见
arxiv.org/pdf/2106.07849.pdf)。 -
容量有限的模型(较小的模型)往往会利用数据集中的偏差(
aclanthology.org/2022.gebnlp-1.27.pdf),因此在对较小模型进行知识蒸馏时需要小心,并评估偏差和公平性。
在缓解偏差时,您可能会想选择哪四类方法中的哪一类。以下是一个建议:最好在流程的尽早阶段解决偏差问题,这样您可以获得更多的控制权和灵活性。您不希望在缓解偏差时受到有限选项的困扰,最终无法令人满意地解决偏差问题。如果您无法访问数据收集阶段,您可以利用数据预处理技术。另一方面,如果您无法访问数据本身,但拥有已训练的模型,那么应该使用后处理技术。可以将多种技术结合使用,因此请务必使用引入的度量标准来衡量偏差,确保所应用的每种技术都能提高最终模型的公平性。同时,考虑使用多种方法来缓解更多偏差。
到目前为止,我们介绍的程序化偏差缓解方法分为三类。然而,深度学习模型中存在一种强有力的偏差缓解方法,它位于这三类方法的交集处。该方法融合了反事实数据增强、混合增强、知识蒸馏和反事实测试时增强。其思想如下:
-
使用反事实数据增强来训练教师模型和学生模型,涵盖整个数据集。
-
使用指数最大值对教师模型的反事实集成选择层特征进行蒸馏到学生模型。这类似于混合增强,但是在特征层面进行的。
然而,反事实角色互换仍然是更易于应用于文本的技术。让我们探索如何使用通用的知识蒸馏方法,减少在上一节中我们探索的人脸分类的同一用例中的偏差。我们在这里将介绍的方法来自arxiv.org/pdf/2112.09786.pdf,该方法提供了两种方法:
-
(蒸馏与去偏) D&D++:首先,使用仅限特权群体数据训练教师模型。然后,用教师模型的权重初始化学生模型,并使用正常的损失和基于所选特征层余弦相似度的知识蒸馏损失训练学生模型。最后,使用之前训练的学生模型初始化最终的学生网络,并在整个数据集上进行训练,以教师模型为来源,通过知识蒸馏从先前的学生模型中学习。
-
一步法蒸馏 D&D:为了简化流程,但仍然立即有效,步骤可以简化。首先,使用仅限特权群体数据训练教师模型。然后,使用教师模型的知识蒸馏,在整个数据集上训练学生模型。
让我们通过使用上一节中实验的用例,实际探索一步法蒸馏 D&D 方法:
-
首先,我们将使用仅限特权群体数据训练教师模型,该数据仅包含男性面部身份。让我们为此示例定义具体的不同配置:
batch_size = 64 lr = 0.05 num_epochs = 22 early_stopping_epochs = 12 reduce_lr_patience = 3 experiment_mlflow = "bias_mitigation_v2" logdir = 'experiments/face_modelv2' only_male=False distill_model=False -
我们将使用上一节中介绍的相同代码库,但会添加一些自定义代码。第一个是对第 6 步中评估深度学习模型的偏差与公平性部分代码的扩展,我们将移除训练和验证数据中的女性数据:
if only_male: train_raw_targets = targets[train_inds] val_raw_targets = targets[val_inds] train_male_inds = np.where(train_gender_data=='Male')[0] train_images = train_images[train_male_inds] train_targets = train_targets[train_male_inds] train_gender_data = train_gender_data[train_male_inds] val_male_inds = np.where( val_gender_data=='Male')[0] val_images = val_images[val_male_inds] val_targets = val_targets[val_male_inds] val_gender_data = val_gender_data[val_male_inds] -
第二是第 7 步,我们将定义一个特殊的前向方法和获取最后卷积特征的方法。我们的想法是确保关注的区域相同,而不是面部身份的概率,因为在模型作为人脸识别特征提取器时,这些概率是无用的。这个特殊的前向方法旨在一次前向传递中返回输出对数值和最后的卷积特征,从而减少延迟:
def get_last_conv_features(self, x): x = self.model.conv1(x) x = self.model.bn1(x) x = self.model.relu(x) x = self.model.maxpool(x) x = self.model.layer1(x) x = self.model.layer2(x) x = self.model.layer3(x) x = self.model.layer4(x) x = self.model.avgpool(x) x = torch.flatten(x, 1) return x def special_forward(self, x, targets=None): model_features = self.get_last_conv_features(x) outputs = self.model.fc(model_features) outputs = self.head(outputs, targets) return outputs, model_features -
接下来是第 8 步,在开始训练之前,我们将提取一次教师模型特征。这将减少训练学生模型所需的时间和资源,因为教师模型的特征将在相同的数据上保持固定,不做增强处理:
device = torch.device("cuda") if distill_model: if os.path.exists('all_teacher_model_features.npy'): all_teacher_model_features = np.load('all_teacher_model_features.npy') else: teacher_model = ARCResNet50(num_classes=num_classes) state_dict = torch.load(os.path.join(distill_model, "model.last.pth")) teacher_model.load_state_dict(state_dict) teacher_model.to(device) cpu_device = torch.device('cpu') entire_dataset = ImagesDataset(image_path, gender_data, targets=id_targets, transforms=get_transforms('valid')) entire_data_loader = DataLoader(entire_dataset, batch_size=val_batch_size, shuffle=False, num_workers=8) all_teacher_model_features = [] for batch in tqdm_notebook(entire_data_loader): inputs, sensitive_attribute, targets = batch with torch.no_grad(): outputs = teacher_model.get_last_conv_features(inputs.to(device)).to(cpu_device).numpy() all_teacher_model_features.append(outputs) del teacher_model all_teacher_model_features = np.vstack(all_teacher_model_features) np.save('all_teacher_model_features.npy', all_teacher_model_features) train_teacher_model_features = all_teacher_model_features[train_inds] val_teacher_model_features = all_teacher_model_features[val_inds] -
接下来,我们将定义训练和验证数据集,将训练和验证的教师模型特征分别输入到第 10 步中:
if distill_model: train_dataset = ImagesDataset( train_images, train_gender_data, train_targets, get_transforms('train'), train_teacher_model_features, ) val_dataset = ImagesDataset( val_images, val_gender_data, val_targets, get_transforms('valid'), val_teacher_model_features, ) -
最后的修改是通过处理批加载来添加额外的教师模型特征和损失,使用交叉熵损失和学生与教师模型最后一层卷积特征的余弦相似度损失,来自第 14 步:
if is_distill_mode: features, sensitive_attribute, targets, teacher_model_features = batch logits, child_model_features = self.model.special_forward(features, targets) loss = self.criterion(logits, targets) + nn.functional.cosine_similarity(teacher_model_features, child_model_features).mean() -
现在,按照前一节的步骤运行,进行更改后,你将获得一个大约为 0.774 的验证指标分数,所有其他分数为 0,因为在这第一步中没有女性。
-
要执行一步蒸馏的下一步骤,请再次执行之前的代码,但进行以下配置更改:
batch_size = 64 val_batch_size = 100 lr = 0.05 num_epochs = 150 early_stopping_epochs = 5 reduce_lr_patience = 2 experiment_mlflow = "bias_mitigation_v3" logdir = 'experiments/face_modelv3' only_male=False distill_model = 'experiments/face_modelv2' -
一步蒸馏 D&D 方法的验证得分为:AAOD 和 AOD 均为 0.3838,准确率为 0.76,差异影响为 0.395,统计公平性为 0.04627。为了公平比较,基础训练的准确率为 0.76333,AAOD 和 AOD 均为 0.38856,差异影响为 0.3885,统计公平性为 0.04758。这意味着改进主要来源于 AOD,差异为 0.0047,统计公平性差异为 0.00131。
-
通过再次使用第 19 步中的集成梯度代码,并应用于我们使用偏见缓解方法训练的模型,可以得到以下结果:
图 13.4:一步蒸馏 D&D 模型的解释
该模型现在专注于正确的特征——即无论性别如何,面部特征而没有背景或头发。
现在,自己尝试使用 D&D++,看看你能得到什么样的改进!
在本节中,我们发现了多种适用于神经网络模型的缓解技术,并间接地表明,评估指标并不是唯一的偏见指示器。
尽管本章主要关注深度学习模型和非结构化数据,但值得注意的是,我们讨论的概念、技术和指标同样适用于结构化数据。结构化数据中的偏见可能以类别不平衡、属性分布偏斜或样本不代表性等形式存在,并可能在模型的预测中体现出来,导致不公平的结果。一个显著的区别是,对于基于结构化数据的用例,偏见通常是由输入特征直接延续的。偏见和公平性评估方法,如基于均等表现的指标、基于均等错误的指标和分布公平性指标,可以用来评估在结构化数据上训练的机器学习模型的公平性。
总结
在本章中,我们专注于机器学习模型中的偏见和公平性这一关键问题。我们强调了部署偏见模型可能带来的负面后果,如法律诉讼和罚款。我们讨论了不同类型的偏见,并确定了在深度学习生命周期中偏见可能出现的阶段,包括规划、数据准备、模型开发和部署。
本章还介绍了几种用于检测和评估偏见与公平性的指标,包括基于平等代表性的指标、基于平等错误的指标、分布公平性指标和个体公平性指标。章节中提供了选择适当指标的建议,针对具体用例,并强调了在评估公平性时平衡不同观点的重要性,例如 WAE 和 WYSIWYG。本章还讨论了可以在模型构建的预处理、处理中和后处理阶段应用的程序性偏见缓解方法。这些方法的示例包括消除受保护属性、差异影响移除器、对抗性去偏和等化机会后处理。
最后,本章介绍了一种综合的深度学习模型偏见缓解方法,结合了反事实增强、混合增强、知识蒸馏和反事实测试时增强。这种方法旨在平衡模型的准确性性能和公平性要求。
通过这些内容,你已经了解了在机器学习模型中解决偏见与公平性的重要性和技术,以及如果不妥善处理可能带来的负面后果。这些知识将帮助你构建不仅在准确性指标上表现优异,而且考虑到伦理影响和公平性方面的机器学习系统,从而确保 AI 解决方案的负责任和有效部署,最终在现实应用中带来更好、更公平的结果。
随着我们前进,下一章将转向分析对抗性性能,这是确保生产中强健和可靠的机器学习模型的关键方面。
第十四章:分析对抗性能
在机器学习模型的背景下,对抗者指的是一个主动寻求利用或破坏这些模型的性能、完整性或安全性的实体或系统。他们可以是恶意行为者、算法或系统,旨在攻击机器学习模型中的漏洞。对抗者进行对抗攻击,故意输入误导性或精心设计的数据,欺骗模型并使其做出不正确或意外的预测。
对抗攻击可以从对输入数据的微小扰动到利用特定算法漏洞的复杂方法不等。对抗者的目标可能会根据不同的上下文有所变化。他们可能试图绕过安全措施、获得未经授权的访问权限、窃取敏感信息,或者破坏模型的预期功能。对抗者也可能瞄准机器学习模型的公平性和伦理,试图利用训练数据或模型设计中存在的偏见或歧视。一个例子是对抗者针对机器学习模型中的公平性和伦理问题进行攻击,尤其是在面部识别系统中。假设一个面部识别系统存在偏见,对男性的识别效果优于女性。对抗者可以通过故意改变外貌来利用这一偏见,误导系统。他们可能使用化妆、发型或配饰来混淆面部识别算法,使得系统更难准确识别他们。通过这样做,对抗者可以利用系统的弱点,可能逃避检测或误导执法部门的努力。
为了应对对抗者和对抗攻击,最好的第一步是分析训练好的机器学习模型的对抗性能。此分析有助于更好地理解模型中潜在的漏洞和弱点,从而能够开发出有针对性的缓解方法。此外,评估对抗性能还可以提供现有缓解策略有效性的洞察,并指导未来模型设计的改进。作为额外的好处,它有助于确保你的模型能够应对其部署环境中可能出现的任何自然变化,即使没有特定的对抗者针对该系统进行攻击。
本章将分别介绍基于图像、文本和音频数据的模型的对抗性能评估。具体而言,以下主题将被讨论:
-
使用数据增强进行对抗分析
-
分析基于音频模型的对抗性能
-
分析基于图像模型的对抗性能
-
探索基于文本模型的对抗分析
技术要求
本章包含一些在 Python 编程语言中的实际实现。要完成这些内容,您需要在计算机上安装以下库:
-
matplotlib -
scikit-learn -
numpy -
pytorch -
accelerate==0.15.0 -
captum -
catalyst -
adversarial-robustness-toolbox -
torchvision -
pandas
本章的代码文件可以在 GitHub 上找到:github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_14。
使用数据增强进行对抗性分析
对抗性性能分析方法的核心是利用数据增强。数据增强是指通过编程方式为现有数据引入现实的变化。数据增强通常在模型训练过程中使用,以提高验证性能和深度学习模型的泛化能力。然而,我们也可以将数据增强作为一种评估方法,以确保在不同条件下的性能鲁棒性。通过在评估过程中应用数据增强,实践者可以更全面、详细地估计模型在生产环境中部署后的表现。
对抗性性能分析提供了两个主要优点。首先,它有助于通过在训练过程中和训练后,在多个训练模型之间进行更好的模型选择,从而构建一个更具泛化能力的模型。这是通过使用增强前提指标实现的。某些使用场景可能具有特殊条件,这些条件不一定能代表原始验证集或保持分区中的数据。数据增强可以帮助改变评估数据集的表示,以模拟生产中的条件。其次,对抗性性能分析可以用于在模型部署到生产环境时建立有针对性的保障措施。通过彻底评估模型在不同对抗性条件下的表现,实践者可以设置具体的阈值、行动和操作指南,以确保模型的行为符合其要求。
选择所有可能的增强方式无疑有助于在不同条件下对性能进行预期对齐。然而,为对抗性分析精心选择的增强方式可以帮助您更有效地从这个过程中提取价值,而不仅仅是为了理解。以下是在使用增强进行对抗性性能分析时的一些建议:
-
考虑选择可以检测、测量和控制的增强方法:你是否拥有一个系统或机器学习模型,可以已经检测到增强方法可能改变的组件?拥有一个可测量的组件,并将其与所选增强方法进行对抗性分析,可以帮助在生产环境中设置实际的保护措施。保护措施可以从拒绝模型基于自动预测做出的决策,并将决策委托给人工审阅者,到要求使用机器学习模型的系统中的用户或参与者重新提交符合系统要求的输入数据。例如,在面部验证系统中设置一个保护措施,确保面部没有任何倾斜,保持正对。
-
考虑更可能在现实部署中发生的条件:通过专注于模仿现实条件的增强方法,实践者可以评估模型在与其预期部署相关的情况下的稳健性和表现。
-
在不同强度的增强方法下评估模型性能,以便更全面地理解性能:了解性能达到峰值和最低点的范围,将帮助你采取正确的行动。然而,某些增强方法仅有二元参数,如是否添加了增强效果,完全可以通过比较应用和不应用时的性能差异来进行评估。例如,是否应用了水平翻转。
-
考虑联合评估多种增强方法的性能:现实世界中的情况通常涉及多种因素,这些因素可能会影响模型的表现。通过同时应用多种增强方法来测试模型的表现,可以更好地了解模型处理复杂场景的能力,并识别出在单独评估单一增强方法时可能未显现的潜在弱点。
-
考虑使用流行的对抗性示例或生成对抗性示例的方法:利用知名的对抗性示例或技术,可以帮助你识别模型中可能被忽视的常见漏洞。通过识别并缓解这些漏洞,你已经防御了潜在攻击的相当一部分,因为这些流行方法更有可能被对手采用。
-
将具有目标特征的真实数据进行分组进行评估,比使用增强方法对对抗性性能分析更有效:有时候,增强方法无法正确复制现实生活中的情况,因此,收集并分析具有特定对抗特征的真实数据样本,可以提供对模型在现实世界场景中表现的更准确评估。
简而言之,评估可执行的增强方法。一般来说,具有价值的模型洞察是任何方式都能执行的。接下来,我们将通过第一个实际示例来进行基于音频的模型对抗分析。
分析基于音频的模型的对抗性能
基于音频的模型的对抗分析需要音频增强方法。在这一部分,我们将利用开源的audiomentations库来应用音频增强方法。我们将实际分析语音识别模型的对抗准确度表现。我们将使用的准确度指标是词错率(WER),这是自动语音识别和机器翻译系统中常用的指标。它通过计算单词替换、插入和删除的总和,再除以参考单词的总数,得出一个百分比值,从而衡量系统输出与参考转录或翻译之间的差异。WER 的公式如下:
WER = (S + I + D) / N
在这里,我们有以下内容:
-
S 代表单词替换的数量
-
I 代表单词插入的数量
-
D 代表单词删除的数量
-
N 是参考转录或翻译中的总单词数
以下增强方法将被考虑用于分析:
-
发音速度增强:改变单词发音的速度可以对 WER 产生显著影响。提高发音速度(时间压缩)可能导致更多错误,因为语音信息被压缩,而降低发音速度(时间扩展)可能会产生更准确的转录。由于单词的长短不同,每分钟音节数将是一个很好的估算指标,无需任何特殊的机器学习模型。
-
语音音调:改变语音音调可以影响口语单词的感知和识别。音调变化等增强方法可以引入音调的变化,进而影响 WER 性能。女性和男性的音调范围通常不同,这可以作为衡量音调的代理,因此我们不会在本主题中直接分析音调。音调可以通过机器学习模型或基于规则的科学方法来测量。
-
背景噪声:背景噪声的存在可能会对语音识别系统产生负面影响。背景噪声可以通过算法生成,如高斯噪声,也可以是通过有策略地选择现实环境中可能存在的背景噪声类型,如汽车或摩托车声音。然而,其存在不能简单地被检测,必须依赖于机器学习模型或手动环境控制。
-
语音响度/强度:语音的响度或音量在语音识别中起着至关重要的作用。增加或减少语音的响度可能引入反映现实条件的变化。常见的语音数据集是在没有任何外部噪声的封闭环境中收集的。这使得通过简单的数学方法就能轻松控制语音的响度。
图 14.1展示了四个组成部分的对抗性能分析图:语音速度性能分析图*(a),高斯噪音性能分析图(b),语音响度性能分析图(c),以及现实生活背景噪音(摩托车噪音)性能分析图(d)*:
图 14.1 – 针对语音识别模型的对抗分析结果(WER)
在*图 14.1(a)中,性能在每秒 2.5 到 4 个音节之间似乎是最佳的,并且男性似乎更容易在超出此范围时出现性能下降。在图 14.1(b)中,模型在每个 SNR 值大于 30 dB 时表现最佳。因此,一个简单的护栏就是确保语音始终比任何背景噪声大至少 30 dB,这可以通过硬件进行测量。在图 14.1(c)*中,很明显,女性的表现无论语音的响度如何,都不会特别下降。然而,对于男性来说,最佳的表现可以在语音强度大约在 20 dB 范围内时获得,而不是超过这一范围。这显示了模型在性别上的某种偏差。*图 14.1(d)*显示了摩托车噪音与高斯噪音没有任何特殊的行为差异。考虑在实践步骤结束时评估更多你能想到的现实生活中的背景噪音!
执行语音识别模型的对抗性能分析
让我们从一个逐步的实际例子开始,展示如何获得图 13.1所示语音识别模型的对抗性能分析结果:
-
我们将使用来自 Hugging Face 开源平台的 Speech2Text 模型,这是一个基于变换器的英语语音训练语音识别模型。首先,导入必要的库,重点是
matplotlib用于绘图,numpy用于数组处理,pytorch用于处理 PyTorch Speech2Text 模型,audiomentations用于数据增强,Speech2Text 模型和 Hugging Face 的预处理器:import matplotlib.pyplot as plt import numpy as np import torch from tqdm import tqdm_notebook import evaluate import syllables from audiomentations import (AddBackgroundNoise, AddGaussianNoise, AddGaussianSNR, LoudnessNormalization, PitchShift, Shift, TimeStretch) from datasets import load_dataset from transformers import (Speech2TextForConditionalGeneration, Speech2TextProcessor) -
接下来,我们将加载已训练好的 Speech2Text 模型和预处理器,并将模型分配到 GPU 设备上:
device = torch.device("cuda") model = Speech2TextForConditionalGeneration.from_pretrained("facebook/s2t-small-librispeech-asr") processor = Speech2TextProcessor.from_pretrained("facebook/s2t-small-librispeech-asr") model.to(device) -
我们将用于评估模型的数据集是谷歌的英语语音识别
fleurs数据集,该数据集恰好包含了性别信息,允许我们间接评估音高表现差异,并进行偏差分析。让我们下载并加载该数据集:ds = load_dataset("google/fleurs", 'en_us', split="validation") -
接下来,我们将从 Hugging Face
evaluate库加载 WER 评估方法,并定义帮助方法从 Speech2Text 模型中提取 WER 分数:wer = evaluate.load("wer") all_gender = np.array(ds['gender']) gender_map = {'female':1, 'male':0} def get_wer_scores(dataset, transcriptions=None, sampling_rates=None, is_hg_ds=False): all_wer_score = [] for idx, audio_data in tqdm_notebook(enumerate(dataset), total=len(dataset)): inputs = processor( audio_data["audio"]["array"] if is_hg_ds else audio_data, sampling_rate=audio_data["audio"]["sampling_rate"] if is_hg_ds else sampling_rates[idx], return_tensors="pt" ) generated_ids = model.generate( inputs["input_features"].to(device), attention_mask=inputs["attention_mask"].to(device) ) transcription = processor.batch_decode(generated_ids, skip_special_tokens=True) wer_score = wer.compute( predictions=transcription, references=[audio_data['transcription'] if is_hg_ds else transcriptions[idx]] ) all_wer_score.append(wer_score) all_wer_score = np.array(all_wer_score) wer_score_results = {} for gender in gender_map.keys(): gender_idx = np.where(all_gender == gender_map[gender])[0] wer_score_results[gender + '_wer_score'] = all_wer_score[gender_idx].mean() wer_score_results['wer_score'] = all_wer_score.mean() return wer_score_results这里将返回三个分数,分别是男性特定分数、女性特定分数和总体分数。
-
接下来,我们将定义主要方法,应用增强到所有基准样本,并获取 WER 分数:
def get_augmented_samples_wer_results( all_baseline_samples, augment, transcriptions, all_sampling_rates ): all_augmented_samples = [] for idx, audio_sample in enumerate(all_baseline_samples): augmented_samples = augment(samples=audio_sample, sample_rate=all_sampling_rates[idx]) all_augmented_samples.append(augmented_samples) results = get_wer_scores( all_augmented_samples, transcriptions, sampling_rates=all_sampling_rates, is_hg_ds=False ) return results -
首先,我们将使用
audiomentation的时间拉伸方法分析发音速度组件的对抗性表现。数据集包含了每秒发音音节数不同的音频数据,因此在开始分析之前,我们必须确保所有音频数据每秒发音的音节数相同。我们先来计算每秒发音的平均音节数:all_syllables_per_second = [] for audio_data in ds: num_syllables = syllables.estimate(audio_data['transcription']) syllables_per_second = num_syllables / (audio_data['num_samples'] / audio_data['audio']['sampling_rate']) all_syllables_per_second.append(syllables_per_second) average_syllables_per_second = np.mean(all_syllables_per_second) -
现在,我们可以获取用于发音分析的初始基准音频样本集:
all_baseline_speed_audio_samples = [] transcriptions = [] all_sampling_rates = [] for idx, audio_data in tqdm_notebook(enumerate(ds), total=len(ds)): rate = average_syllables_per_second / all_syllables_per_second[idx] augment = TimeStretch(min_rate=rate, max_rate=rate, p=1.0) augmented_samples = augment( samples=audio_data['audio']['array'], sample_rate=audio_data['audio']['sampling_rate'] ) transcriptions.append(audio_data['transcription']) all_sampling_rates.append(audio_data['audio']['sampling_rate']) all_baseline_speed_audio_samples.append(augmented_samples) -
最后,我们将使用不同的加速和减速率进行对抗性 WER 分析:
rates = np.linspace(0.1, 1, 9).tolist() + list(range(1, 11)) wer_results_by_rate = [] for rate_to_change in tqdm_notebook(rates): augment = TimeStretch(min_rate=rate_to_change, max_rate=rate_to_change, p=1.0) results = get_augmented_samples_wer_results( all_baseline_speed_audio_samples, augment, transcriptions, all_sampling_rates ) wer_results_by_rate.append(results) -
运行以下绘图代码,你将得到如*图 14.1(a)*所示的图表:
labels = ["female", "male", "overall"] plt.xlabel("Syllables per second") plt.ylabel("WER") for idx, gender in enumerate(["female_", "male_", ""]): plt.plot( [average_syllables_per_second * i for i in rates], [wr[gender + 'wer_score'] for wr in wer_results_by_rate], label=labels[idx] ) plt.legend() -
现在,我们将进入下一个增强组件,该组件算法生成高斯背景噪声。在这个例子中,我们将控制信噪比(SNR)在语音信号和高斯噪声之间的关系。在这种情况下,原始数据可以作为基准,且无需进行均衡:
baseline_samples = [audio_data['audio']['array'] for audio_data in ds] snr_rates = np.linspace(1, 100, 25) wer_results_by_snr = [] for snr_rate in tqdm_notebook(snr_rates): all_augmented_samples = [] augment = AddGaussianSNR( min_snr_in_db=snr_rate, max_snr_in_db=snr_rate, p=1.0 ) results = get_augmented_samples_wer_results( baseline_samples, augment, transcriptions, all_sampling_rates ) wer_results_by_snr.append(results) -
运行以下绘图代码,你将得到如*图 14.1(b)*所示的图表:
plt.xlabel("SNR (dB)") plt.ylabel("WER") for idx, gender in enumerate(["female_", "male_", ""]): plt.plot( snr_rates, [wr[gender + 'wer_score'] for wr in wer_results_by_snr], label=labels[idx] ) plt.legend() -
接下来,我们将分析不同语音响度下的 WER 表现。来自
huggingface.co/datasets/google/fleurs的数据集是在没有背景噪音的封闭环境中制作的,因此可以通过以下代码直接计算原始语音音频数据数组的幅度:Wer_results_by_loudness = [] loudness_db = np.linspace(-31, 100, 25) for db in tqdm_notebook(loudness_db): augment = LoudnessNormalization( min_lufs_in_db=db, max_lufs_in_db=db, p=1.0 ) results = get_augmented_samples_wer_results(baseline_samples, augment, transcriptions, all_sampling_rates) wer_results_by_loudness.append(results) -
运行以下绘图代码,你将得到如*图 14.1(c)*所示的图表:
labels = ["female", "male", "overall"] plt.xlabel("SNR (dB)") plt.ylabel("WER") for idx, gender in enumerate(["female_", "male_", ""]): plt.plot( loudness_db, [wr[gender + 'wer_score'] for wr in wer_results_by_loudness], label=labels[idx] ) plt.legend() -
最后,我们将分析来自现实世界的真实背景噪音的对抗性表现。我们将使用来自
Freesound50k数据集的摩托车声音,并将其与原始音频数据在不同 SNR 下混合,使用以下代码:snrs = np.linspace(-50, 50, 20) wer_results_by_background_noise_snr = [] for snr in tqdm_notebook(snrs): augment = AddBackgroundNoise( sounds_path="motorbikes", min_snr_in_db=snr, max_snr_in_db=snr, p=1.0 ) results = get_augmented_samples_wer_results(baseline_samples, augment, transcriptions, all_sampling_rates) wer_results_by_background_noise_snr.append(results) -
运行以下绘图代码,你将得到如*图 14.1(d)*所示的图表:
plt.xlabel("SNR (dB)") plt.ylabel("WER") for idx, gender in enumerate(["female_", "male_", ""]): plt.plot( snrs, [wr[gender + 'wer_score'] for wr in wer_results_by_background_noise_snr], label=labels[idx] ) plt.legend()
在此基础上,我们尝试分析了语音识别模型的对抗性性能。可以考虑在你的端进行扩展分析,尝试联合评估多种增强方法。例如,考虑多个背景声音同时存在的场景。通过不同程度的背景声音增强音频,可以模拟不同干扰程度的场景。
在执行分析后,除了在训练过程中使用这些增强方法来缓解不良性能外,还可以在生产环境中添加保护措施。例如,考虑将语音识别输出作为输入传递给像 ChatGPT 这样的 LLM 模型,以防止内部错误结果的情况。一个保护措施的例子是,当检测到音频中的特定背景噪声时,重新路由系统,要求在将语音识别结果提交给 ChatGPT 之前进行人工验证。制定后续的可执行流程对于从洞察中释放价值至关重要。接下来,让我们探讨基于图像的模型的对抗性分析。
基于图像的模型的对抗性性能分析
基于增强的对抗性分析也可以应用于基于图像的模型。关键在于发现原本不存在的条件下,在验证数据集中可能导致准确度下降的因素。以下是一些可以通过增强方法评估的图像领域组件示例:
-
目标物体的大小:在使用闭路电视摄像头图像输入的应用场景中,对抗性分析可以帮助我们设置摄像头的适当距离,从而实现最佳性能。原始图像可以反复调整大小并覆盖在基础黑色图像上进行分析。
-
目标物体的滚动方向:俯仰和偏航方向不易增强。然而,旋转增强可以帮助对滚动方向的性能进行压力测试。任何姿态方向检测模型或系统都可以强制实现最佳性能。
-
模糊程度:图像可以被模糊化,OpenCV 库中有现成的图像模糊检测器可以使用。模糊增强可以帮助对该组件进行压力测试。
-
albumentations库提供了雨、雪、雾和阳光增强方法!
除了使用增强方法进行对抗性分析来评估不同条件下的性能外,还有许多其他广泛认可和广泛研究的方法,可以用于对基于图像的模型进行对抗性攻击。这里所说的“流行”也意味着这些技术对潜在攻击者容易获取,使他们能够轻松尝试这些方法。因此,必须彻底分析这些攻击,因为它们的发生概率和潜在影响增加了。
这些攻击试图通过优化以下之一来获得一个对抗性图像,从而欺骗模型:
-
一种图像扰动矩阵,它作为噪声混合器作用于原始图像,同时保持与原始图像的高度视觉相似性
-
一种可以数字化叠加在原始图像上的图像补丁,并能在现实世界中打印出来以规避检测或迷惑模型
图 14.2展示了两种针对对抗性攻击的优化方法示例:
图 14.2 – 对抗性图像补丁和对抗性图像噪声混合器示例
左上方的对抗性图像补丁是针对YOLOv2图像检测模型的,它作用于代表交通停止标志的类别,并能在现实世界中打印出来并贴在停车标志上时,使模型预测出其他随机类别。该补丁还能够将其对抗性特性转移到Faster-RCNN图像物体检测模型。左下方的对抗性图像补丁是通过优化来欺骗 YOLOv2 检测模型,使其在打印补丁贴在一个人身上时无法检测到人。右侧的对抗性图像噪声混合器示例展示了,当噪声添加到原始图像右侧时,生成的图像与原始图像在视觉上无法区分。ResNet50 模型(用于上一章构建模型时)准确预测了原始图像中的正确人物,但在添加了右上方图示中的噪声后,未能预测出正确的人物。图像补丁是基于实时计算机视觉应用的 CCTV 相关攻击。例如,窃贼希望阻止面部物体检测。对抗性图像噪声混合器适用于用户可以提供自己数据的用例。例如,社交媒体平台和媒体共享平台(如 Instagram 和 YouTube)希望通过机器学习来筛选和控制可以上传的媒体,而用户则希望绕过这些机器学习防护措施。
音频噪声混合器和音频补丁也可以用于对抗性攻击,针对基于音频的模型,方法类似于基于图像的模型。音频噪声混合器通过精心设计的噪声注入到原始音频信号中,生成一个对抗性示例,该示例在视觉上与原始音频保持高度相似,但却能欺骗模型。这在语音识别系统和音频内容过滤等应用中尤为相关,在这些应用中,对手可能会试图绕过安全措施或操控系统输出。另一方面,音频补丁涉及创建并将对抗性音频片段叠加到原始音频信号上。这些补丁可以设计为遮掩音频中的某些元素,或加入新的元素以欺骗模型。例如,对抗性音频可以通过任何音频设备自然播放在环境中,从而避开语音识别系统,或使语音识别模型误解特定的单词或短语。
这些技术是经过工程设计的对抗性攻击方法,可以分为两类:一种是需要访问神经网络梯度和模型本身的技术,另一种则只需要访问模型的预测概率或 logits 来优化图像。由于攻击者不太可能访问到神经网络梯度和模型本身,因此评估那些只需要预测概率或 logits 的对抗性图像生成方法更为实际。
攻击者可以选择随机生成噪声扰动矩阵,并希望它能成功欺骗模型。然而,一些算法可以自动优化生成有用的噪声扰动矩阵,只要你能访问到概率或 logits。其中一种只需预测 logits 就能生成欺骗模型所需噪声的算法叫做HopSkipJump算法。该算法旨在最小化生成有效对抗性示例所需的查询次数。以下是该算法的概述:
-
初始化:算法首先通过初始化目标类别和初始对抗性示例开始。
-
主循环:算法会反复执行一系列步骤,直到成功生成对抗性示例或达到预定义的查询限制:
-
跳跃:在这个步骤中,算法进行局部搜索,寻找一个扰动,使得初始对抗性示例更接近目标类别,同时确保扰动后的示例仍然是对抗性的。
-
跳过:如果跳跃步骤未能找到合适的扰动,算法将通过跳过一些像素来进行更广泛的搜索。这个步骤的目的是高效地探索更大的搜索空间。
-
跳跃:如果跳跃和跳过步骤都失败,算法将进行跳跃操作,对一小部分像素进行随机扰动,以鼓励探索不同的方向。
-
决策:在每个扰动步骤后(跳跃、跳过或跳跃),算法查询目标模型,以获取其对扰动示例的预测类别标签。
-
停止准则:如果算法成功生成对抗示例——即目标模型为扰动示例预测了目标类别——则算法终止。若查询次数超过预设阈值,算法也可以停止。
-
HopSkipJump 算法结合了局部和全局搜索策略,以高效地探索对抗示例的空间,同时最小化对目标模型的查询次数。这是一个可以在开源对抗攻击工具包中找到的多种攻击之一,地址为github.com/Trusted-AI/adversarial-robustness-toolbox。接下来,我们将逐步介绍如何获得在图 14.2中看到的对抗图像噪声混合结果等。
执行面部识别模型的对抗性能分析
在我们开始之前,请把自己置身于攻击者的角度和心态!步骤如下:
-
首先,我们将加载必要的库。重点是
albumentations用于数据增强,torch用于神经网络模型,art用于对抗示例生成算法:import os import albumentations as albu import numpy as np import pandas as pd import torch import torch.nn as nn from albumentations.pytorch.transforms import ToTensorV2 from PIL import Image from sklearn.model_selection import StratifiedShuffleSplit from torchvision import models from tqdm import tqdm_notebook import evaluate from art.attacks.evasion import HopSkipJump from art.estimators.classification import BlackBoxClassifier import matplotlib.pyplot as plt from catalyst.contrib.layers import ArcFace -
接下来,我们将重用我们在第十三章中构建的训练好的面部分类模型,探索偏差与公平性,它是一个以 ResNet50 为骨干的模型,结合了 ArcFace:
device = torch.device("cuda") class ArcResNet50(nn.Module): def __init__(self, num_classes): super(ArcResNet50, self).__init__() self.model = models.resnet50(pretrained=True) self.model.fc = nn.Linear(self.model.fc.in_features, self.model.fc.in_features) self.head = ArcFace(self.model.fc.out_features, num_classes, s=13, m=0.15) def forward(self, x, targets=None): output = self.model(x) outputs = self.head(output, targets) return outputs num_classes = 10000 model = ArcResNet50(num_classes=num_classes) model.to(device) model_path = '../CHAPTER_13/experiments/face_modelv10' state_dict = torch.load(os.path.join(model_path, "model.last.pth")) model.load_state_dict(state_dict) -
我们还将重用相同的数据集:
train = pd.read_csv('../CHAPTER_13/face_age_gender.csv') image_path = train['image_path'].values targets = train['target'].values name2class = {name: idx for idx, name in enumerate(sorted(set(targets)))} id_targets = np.array([name2class[target] for target in targets]) -
要开始使用来自
art库的HopSkipJump类,我们需要定义一个art库的黑箱分类器实例。该类是一个包装器,设置预期的生成图像噪声矩阵形状,并且仅能访问任何基于图像的模型的完全孤立预测方法。首先,我们准备好推理预测方法,以便 art 分类器可以使用它:transforms = albu.Compose([ albu.Resize(224, 224), albu.Normalize(), ToTensorV2() ]) def predict(x): if len(x.shape) == 3: img = transforms(image=x)["image"].unsqueeze(0).to(device) else: batch_img = [] for img in x: img = transforms(image=img)["image"].to(device) batch_img.append(img) img = torch.stack(batch_img) with torch.inference_mode(): output = model(img) return output.cpu().numpy() -
接下来,让我们从数据集中选择一个示例图像,并创建具有孤立预测方法、目标图像形状、类别数量和接受的像素值范围的黑箱分类器:
target_image = np.array(Image.open(os.path.join("../CHAPTER_13", image_path[1])).resize((224, 224))).astype(np.float32) classifier = BlackBoxClassifier( predict, target_image.shape, num_classes, clip_values=(0, 255)) -
现在,我们将使用分类器初始化
HopSkipJump攻击类,并将每次评估的最大迭代次数设置为10:attack = HopSkipJump(classifier=classifier, targeted=False, max_iter=10, max_eval=1000, init_eval=10) -
我们将运行该算法 30 次迭代。在每十次迭代的倍数时,我们将通过打印与原始图像的最小和最大像素差异、绘制生成的对抗图像并预测生成的图像来评估算法:
x_adv = None for i in range(3): x_adv = attack.generate(x=np.array([target_image]), x_adv_init=x_adv) print("class label %d." % np.argmax(classifier.predict(x_adv)[0])) plt.imshow(x_adv[0].astype(np.uint8)) plt.show(block=False) print(np.min(x_adv[0] - target_image), np.max(x_adv[0] - target_image)) -
最后,我们将绘制原始图像,获取模型在原始图像上的预测,并打印实际标签:
plt.imshow(target_image.astype(np.uint8)) print(np.argmax(classifier.predict(np.array([target_image.astype(np.float32)]))[0])) print(id_targets[1])结果显示在图 14.3中。你采取的步骤越多,生成的对抗图像与原始图像的视觉相似度就越高,同时仍能欺骗模型!这里的最小和最大像素差异提供了生成图像与原始图像相似度的一个参考,这将转化为视觉上的相似性:
图 14.3 – 单个面孔样本的对抗性能
仅凭模型的预测,我们成功地让模型错误地识别了一个面部身份!为了进一步推进,理解模型在扰动攻击中的脆弱性会很有帮助,可以通过检查更多的例子来深入了解。为了更广泛地基准化该方法,我们需要确保生成的对抗图像与原始图像在视觉上相似,这样从可能性角度来看才有意义。从图 14.3中的结果来看,可以合理推测,任何低于绝对最小和最大 10 像素差异的图像,都应保持足够的视觉相似性,能够让人类评估者相信图像没有问题,同时还能欺骗模型。我们将使用相同的 HopSkipJump 算法并进行 30 次迭代,但会添加一个条件:生成的对抗图像最大像素差异不超过 10 像素,并且与原始图像的最小绝对差异。如果不符合该条件,我们将使用原始图像的预测,视为未能生成有效的对抗攻击:
-
让我们首先随机选择 1,000 个分层样本,这样可以更快地进行评估:
splitter = StratifiedShuffleSplit(test_size=.02, n_splits=2, random_state = 7) split = splitter.split(image_path, targets) _, val_inds = next(split) val_inds = val_inds[:1000] -
接下来,我们将使用前述的工作流程计算预测,并对这 1,000 张图片使用原始图像:
all_adversarial_hsj_predicted_class = [] all_predicted_class = [] for idx, path in tqdm_notebook(enumerate(image_path[val_inds]), total=len(val_inds)): img = np.array(Image.open(os.path.join("../CHAPTER_13", path))).astype(np.float32) classifier = BlackBoxClassifier( predict, img.shape, num_classes, clip_values=(0, 255)) label = id_targets[idx] predicted_class = np.argmax(classifier.predict(np.array([img]))[0]) attack = HopSkipJump( classifier=classifier, targeted=False, max_iter=10, max_eval=1000, init_eval=10, verbose=False) x_adv = None for i in range(3): x_adv = attack.generate(x=np.array([img]), x_adv_init=x_adv) adversarial_hsj_predicted_class = np.argmax(classifier.predict(x_adv)[0]) if (np.min(x_adv - img) >= 10.0 or np.max(x_adv - img) >= 10.0): adversarial_hsj_predicted_class = predicted_class all_predicted_class.append(predicted_class) all_adversarial_hsj_predicted_class.append(adversarial_hsj_predicted_class) -
现在,让我们比较模型在原始图像和我们之前在s**tep 7中使用的对抗图像工作流程上的准确性表现。我们将使用 Hugging Face
evaluate库的准确性评估方法:accuracy_metric = evaluate.load("accuracy") print(accuracy_metric.compute(references=id_targets[val_inds], predictions=all_predicted_class) print(accuracy_metric.compute(references=id_targets[val_inds], predictions=all_adversarial_ba_predicted_class)结果是在原始图像上获得 56.9 的准确率,在对抗图像工作流程上则为 30.9 的准确率!
在这个实际示例中,我们成功使用了一个自动化算法来识别能成功欺骗我们在上一章构建的模型的对抗噪声混合器!
图像模型对对抗性噪声混合器类型和对抗性图像补丁类型极为敏感,因为这些情况与现实世界中的自然现象有很大偏离,模型可能难以正确处理。尽管机器学习模型擅长从真实世界数据中学习模式和特征,但它们通常难以应对显著偏离典型环境条件的合成扰动。这些对抗性技术利用图像模型的漏洞,导致它们误分类或产生错误的输出。因此,理解和解决这些漏洞在构建强大防御机制以应对此类攻击时至关重要。因此,考虑深入探索对抗性鲁棒性工具箱库,了解更多对抗性示例和攻击算法!为了减轻图像模型中的对抗性攻击,建议在训练过程中使用如高斯噪声和随机像素扰动器等对抗性目标增强。通过在训练过程中加入这些增强,模型可以学会更具抗扰动性,从而提高整体鲁棒性。
接下来,我们将深入探讨针对基于文本模型的对抗性攻击。
探索基于文本的模型的对抗性分析
基于文本的模型有时在处理特定单词、单词词干的特定词形变化,或相同单词的不同形式时,可能会存在性能漏洞。以下是一个例子:
Supervised Use Case: Sentiment Analysis
Prediction Row: {"Text": "I love this product!", "Sentiment": "Positive"}
Adversarial Example: {"Text": "I l0ve this product!", "Sentiment": "Negative"}
因此,对抗性分析可以通过对比在句子中添加重要单词与不添加重要单词时的性能来进行。为了减轻此类攻击,可以在训练过程中应用类似的单词替换增强。
然而,在现代基于文本的模型中,大多数广泛采用的模型现在依赖于预训练的语言模型基础。这使得它们即使经过领域微调后,仍然能够理解自然语言,因此,可以使用更复杂的对抗性攻击,利用自然语言欺骗。结果,彻底分析并开发出针对这些利用自然语言欺骗的复杂对抗性攻击的强大防御机制,对于确保现代基于文本的模型的可靠性和安全性至关重要。
可以把自然语言欺骗看作类似于人类通过自然语言进行欺骗的方式。正如人们可能会采取各种策略来误导或操控他人,比如社会工程学、上下文毒化和语言剖析,这些相同的方法也可以用来欺骗文本模型。以下是一个基于自然语言欺骗的例子,针对垃圾邮件/恶意邮件检测的机器学习使用场景:
Supervised Use Case: Spam/Malicious Email Detection
Prediction Row: {"Email Content": "Click the link below to claim your free iPhone.", "Label": "Spam"}
Adversarial Example: {"Email Content": "Hey, I found this article about getting a new iPhone at a great discount. Here's the link to check it out.", "Label": "Not Spam"}
社会工程学涉及使用心理操控来欺骗他人泄露敏感信息或执行特定操作。上下文污染是指故意引入误导性或无关的信息以混淆接收者。与此同时,语言剥削则利用语言的细微差别和模糊性来制造混乱或误解。通过理解和应对这些在人与人之间的欺骗技术,并将其应用于对抗性文本分析,我们可以增强文本模型对抗对抗性攻击的韧性,并保持其准确性和可靠性。不幸的是,目前并没有算法可以完全基准化这种完全改变句子的自然语言欺骗。我们需要依赖收集真实世界的欺骗数据来对这一部分进行对抗性分析。
然而,还有另一种值得一提的自然语言对抗性攻击形式,当前在攻击一般问题与答案的 LLM(如 ChatGPT)时越来越广泛使用。与重新格式化整个原始文本数据不同,额外的恶意上下文被用作原始文本数据的借口。LLM API 提供商通常会内置保护措施,以防止明显的、冒犯性的、令人反感的、歧视性的、个人攻击以及骚扰、暴力、非法活动、虚假信息、宣传、自残或任何不当内容。这是为了确保 AI 的负责任使用,并防止任何可能对个人和社会产生负面影响的行为。然而,一种名为“越狱”的流行对抗性攻击可以去除 ChatGPT 所实施的所有内容生成限制。越狱攻击方法是一种经过工程设计的提示,已经公开分享,并可以作为任何实际用户提示的前置条件。许多版本的这种工程化提示已被公开分享,并且可以通过谷歌搜索轻松找到,从而使世界上的每个人都能攻击 ChatGPT。幸运的是,OpenAI 一直在尽力减少这种越狱对抗性攻击,每当他们获得这些提示时都会及时应对。新的越狱提示版本更新非常频繁,OpenAI 也因此陷入了一个不断循环的过程,试图应对新的工程化越狱攻击。
总之,进行对抗性分析对于增强文本模型对欺骗技术和攻击的韧性至关重要。通过理解人类欺骗行为并保持警觉以应对不断变化的威胁,我们可以在超越单纯的在精心设计的测试数据上取得准确性的基础上,提升这些文本模型的可靠性和安全性。
总结
本章介绍了机器学习模型的对抗性性能分析的概念。对抗性攻击通过故意输入误导性或精心设计的数据来欺骗模型,导致错误的预测。本章强调了分析对抗性性能的重要性,以识别机器学习模型中的潜在脆弱性和弱点,并开发针对性的缓解方法。对抗性攻击可以针对机器学习模型的各个方面,包括它们的偏见和公平性行为,以及基于准确度的性能。例如,面部识别系统可能会成为对手的攻击目标,攻击者利用训练数据或模型设计中的偏见或歧视。
我们还探讨了分析图像、文本和音频数据模型对抗性性能的实际例子和技术。对于基于图像的模型,讨论了对象大小、方向、模糊度和环境条件等各种方法。我们还实际探索了一种算法方法,该方法用于生成噪声矩阵,以便混合并扰乱原始图像,并生成能够欺骗训练好的面部分类器模型的对抗性图像;这一方法来自于第十三章,探索偏见与公平性。对于基于音频的模型,分析了如发音速度、语音音调、背景噪音和语音响度等增强方法,而对于基于文本的模型,探索了基于单词变化的攻击、自然语言欺骗和越狱攻击。
总之,对抗性分析对于增强机器学习模型抵御欺骗技术和攻击的韧性至关重要。通过理解人类欺骗行为,并保持对不断发展的对抗性威胁的警觉,我们可以提高神经网络模型的可靠性和安全性。
在下一章,我们将进入深度学习生命周期的下一阶段,探索深度学习模型在生产中的应用世界。
第三部分 – DLOps
在本书的这一部分,你将深入了解在生产环境中部署、监控和治理深度学习模型的激动人心领域,并与 MLOps 和 DevOps 进行类比。本部分将为你提供关于确保深度学习模型在生产中成功应用和实际影响所需的核心组件的全面理解。
在本部分的各章中,我们将探讨将深度学习模型部署到生产环境中的各个方面,涵盖诸如硬件基础设施、模型打包和用户界面等重要考虑因素。我们还将深入讨论模型治理的三大支柱:模型利用、模型监控和模型维护。你将了解漂移的概念以及它对已部署深度学习模型性能的长期影响,并学习如何有效应对漂移。我们还将讨论像 DataRobot 这样的 AI 平台的好处,它们简化了机器学习生命周期中的复杂阶段,加速了复杂深度学习模型的创建、训练、部署和治理。
作为额外内容,基于第六章《理解神经网络变换器》中的基础变换器方法,我们将深入探讨**大型语言模型(LLM)**解决方案,这些解决方案正在革新各个领域。你将学习如何架构 LLM 解决方案并构建自主智能体,掌握利用其潜力所需的知识。
本部分包含以下章节:
-
第十五章《在生产环境中部署深度学习模型》
-
第十六章《深度学习模型治理》
-
第十七章《在动态环境中有效管理漂移效应》
-
第十八章《探索 DataRobot AI 平台》
-
第十九章《架构 LLM 解决方案》
第十五章:将深度学习模型部署到生产环境
在前面的章节中,我们深入探讨了数据准备、深度学习(DL)模型开发的复杂性,以及如何从我们的 DL 模型中获得有价值的见解。通过细致的数据分析、特征工程、模型优化和模型分析,我们已经掌握了确保 DL 模型表现良好并按预期运行的技术。随着我们进入下一个阶段,焦点将转向在生产环境中部署这些 DL 模型。
将深度学习模型部署到生产环境的阶段是一个重要的成就,因为大多数模型无法走到这一步。如果你的项目已经达到了这一里程碑,这意味着你已经成功满足了利益相关者的需求,提供了有价值的见解,并进行了全面的价值和指标分析。恭喜你,你离加入成功项目的少数群体又近了一步。值得注意的是,根据 2022 年 Gartner 的一项调查,VentureBeat 进行了报道,这项调查在线进行,时间为 2021 年 10 月至 12 月,收集了来自美国、德国和英国的 699 名受访者的数据,约有一半(54%)的 AI 模型最终进入生产阶段。此外,由 AI 资源管理解决方案提供商 Run AI 发布的 2023 年《AI 基础设施状态调查》报告显示,在超过 88%的受访公司中,进入生产阶段的 AI 模型不到一半。该调查涉及 450 名来自美国和西欧的行业专业人士。这两项调查突显了这个过程中的挑战,以及达到这一阶段的重要性。
这里的最终目标是让这些 DL 模型以直观的方式对最终用户可用,使他们能够在实际应用中充分发挥 DL 的潜力。在本章中,我们将探讨将 DL 模型无缝集成到生产系统中的各种策略、工具和最佳实践,确保可扩展性、可靠性和用户友好性,适应各种不同的用户需求。
具体而言,我们将讨论以下主题:
-
探索深度学习模型部署的关键组件
-
确定深度学习模型部署的关键需求
-
选择正确的深度学习模型部署选项
-
探讨基于实际使用案例的部署决策
-
探索深度学习(DL)模型部署的通用建议
-
使用 ONNX、TensorRT 和 NVIDIA Triton 服务器部署语言模型
技术要求
在本章的最后一部分,我们将进行一个实践性主题。本教程要求你拥有一台配备 NVIDIA GPU 设备的 Linux 机器,最好是在 Ubuntu 上安装 Python 3.10 和nvidia-docker工具。此外,我们还需要安装以下 Python 库:
-
numpy -
transformers==4.21.3 -
nvidia-tensorrt==8.4.1.5 -
torch==1.12.0 -
transformers-deploy -
tritonclient
代码文件可在 GitHub 上获取:github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_15。
探索深度学习模型部署的关键组件
那么,部署深度学习模型需要什么?它从全面了解每个必需的组件开始,并定义清晰的需求,以指导每个方面的决策。这种方法确保与业务目标和需求保持一致,最大化成功部署的机会。通过精心规划、严谨执行和专注于满足业务需求,你可以增加成功部署深度学习模型并为用户释放其价值的可能性。我们将从探索部署深度学习模型所需的组件开始。
将深度学习模型部署到生产环境不仅仅涉及训练好的模型本身。它需要各个组件之间的无缝协作,共同工作以帮助用户有效地从模型的预测中提取价值。以下是这些组件:
-
架构选择:部署系统的整体设计和结构。模型应该作为一个独立的服务、微服务,还是直接作为现有服务的一部分来实现?模型应该托管在云端还是本地?另一个需要考虑的方面是,是否使用容器编排平台,如 Kubernetes、Docker Swarm 或 Apache Mesos,来管理和扩展深度学习模型在容器化应用中的部署。
这些平台提供灵活的部署方式,可以跨多台机器、云服务提供商或本地基础设施进行部署,并可与其他工具和服务结合使用,以高效管理容器化应用程序和微服务。
-
硬件/物理基础设施选择:这涉及到选择你希望使用的物理计算设备,以及组成该计算设备的各个组件的选择。模型应该在 CPU、GPU、TPU,还是 iPhone 中的 人工神经引擎(ANE)上运行?
-
模型打包方法和框架:这是一个涉及将模型的架构、权重和配置序列化为文件或容器格式的组件,从而实现模型在各种环境中的分发、部署和使用。通常,深度学习框架会提供开箱即用的模型打包支持。你是否有架构选择和硬件基础设施偏好,要求模型以特定方式进行打包?
-
模型安全性、可信度和可靠性组件:这包括采取措施确保部署的模型在做出准确预测时是安全、可信和可靠的。它涉及到实施防护措施以防止滥用或意外行为,确保模型的一致性,监控模型性能,并提供预测解释,以帮助用户理解和信任模型的输出。确保数据隐私并遵守相关法规也是该组件的一个关键方面。是否有任何特定的安全性、可信度或可靠性要求,必须在你的深度学习模型部署中得到满足?
-
安全性和身份验证方法:这些涉及到保护你的深度学习模型及其相关基础设施,以及通过实施合适的身份验证、授权和加密机制来控制对模型的访问。这确保只有授权用户才能访问和与模型进行交互,防止未授权访问、数据泄露以及潜在的模型滥用。你的深度学习模型部署需要哪些安全性和身份验证要求?这些要求将如何集成到你的系统中?
-
通信协议:这些定义了在部署的模型和系统中的其他组件或用户之间交换数据的规则和格式。它涉及根据要求选择适当的协议,例如延迟、可靠性和数据格式。通信协议的示例包括 HTTP、RESTful API、gRPC、服务器推送事件和 WebSocket。哪些通信协议最适合你的深度学习模型部署?它们将如何实现,以便模型和用户之间能够无缝互动?
-
用户界面:这些是视觉组件和交互方式,允许用户或下游系统访问、与部署的深度学习模型进行交互并获取预测。用户界面可以是基于网页、移动端、桌面应用程序、API,甚至是语音激活系统,具体取决于使用场景和目标用户群体。设计用户友好且直观的界面至关重要,以确保用户能够轻松理解并充分利用模型的预测功能。什么样的用户界面最适合你的深度学习模型部署?如何设计界面,以提供最佳的用户体验,同时有效地传递模型的能力?以下是一些特定于深度学习模型的用户界面设计挑战示例:
-
可视化复杂数据:深度学习模型通常处理多维数据,这可能会让数据以用户友好的方式展示变得具有挑战性。设计人员可能需要设计创新的方式来可视化和呈现这些数据,使用户能够访问并理解这些数据。
-
处理实时数据:在深度学习模型处理和分析实时数据的场景中,用户界面必须有效地管理数据流和更新,确保用户及时获得准确的信息,同时避免信息过载。
-
促进模型交互:用户可能需要与深度学习模型互动,以调整参数、提供反馈或请求额外信息。设计直观的用户界面元素以支持这些交互至关重要,确保用户能够有效地与模型互动。
-
解释模型输出:深度学习模型可能会产生复杂且微妙的输出,这可能会使用户理解和操作变得困难。设计者必须找到方法,以清晰且可操作的方式呈现模型预测结果,同时提供上下文信息,帮助用户解释结果。
-
管理不确定性:深度学习模型可能会产生具有不同置信度或不确定性的预测。设计者应考虑如何将这种不确定性传达给用户,确保他们意识到模型输出的局限性和潜在风险。
-
可访问性与包容性:深度学习模型的用户界面应当适应各种不同背景的用户,包括有不同能力、语言和文化背景的用户。设计师必须确保他们的界面具有可访问性和包容性,考虑到各种用户需求和偏好。
-
-
监控与日志组件:这些工具允许你实时跟踪深度学习模型的性能、使用情况和健康状况。通过收集和分析相关的指标、日志和警报,该组件有助于发现潜在问题、优化模型性能,并确保稳定的部署环境。你将如何实施监控和日志记录,以追踪深度学习模型的健康状况和性能,并且哪些指标对衡量其成功至关重要?
-
持续集成/持续部署 (CI/CD):该过程涉及每当模型的代码、数据或基础设施发生变化时,自动构建、测试和部署你的深度学习模型。CI/CD 简化了开发生命周期,实现了更快速的迭代和改进,同时确保已部署的模型保持最新并且可靠。你将采用哪些 CI/CD 实践和工具,以维持你的深度学习模型的无缝部署流程?
在每个组件都有众多选项的情况下,制定决策策略至关重要。这个过程的第一步是定义具体的需求,以指导每个组件的决策。在下一节中,我们将讨论如何建立这些需求,确保你的选择与业务目标相符。
确定深度学习模型部署的关键要求
为了从多种选项中确定最合适的部署策略,必须识别并定义七个关键要求。这些要求包括延迟和可用性、成本、可扩展性、模型硬件、数据隐私、安全性,以及信任和可靠性要求。让我们详细探讨每一个要求:
-
延迟和可用性要求:这两者是紧密相关的组件,应一同定义。可用性要求指的是模型预测的期望运行时间和可访问性水平。延迟要求指的是模型必须满足的最大可接受延迟或响应时间,以提供及时的预测或结果。如果一个部署具有较低的可用性要求,通常可以容忍较高的延迟预测,反之亦然。原因之一是,如果低延迟基础设施在请求模型预测时不可用,那么它就无法确保低延迟。然而,也有一些特殊情况,只在短时间内需要完全可用且低延迟,而其他时间可以不可用,这被视为低可用性但具有低延迟要求。以下是确定延迟和可用性要求时的一些最佳实践:
-
考虑使用 DL 模型的最终用户或应用程序的期望和需求。与相关方沟通,了解他们期望的响应时间和可用性水平。
-
评估延迟和可用性对整体系统或业务流程的影响。识别延迟可能显著影响用户体验或业务运营的关键点。等待至少 1 小时才能获得预测,是否能够提供业务所需的价值?
-
确定可用性特别重要的时间窗口或时段。确定 DL 模型是否需要全天候 24/7 可用,或是否有特定的时段或事件需要高可用性。
-
设置理想和最大延迟与可用性阈值。最大值通常是指仍能获得显著价值的水平,而理想条件则是稍微提高该值的情况。
-
-
成本要求:预算限制是任何企业的关键考虑因素,因此必须确定您愿意为部署机器学习模型分配的最大成本,以便根据模型预期带来的价值进行规划。为了确保费用不会超过组织愿意投入的金额,建议进行成本效益分析。此分析将评估部署基础设施中各个组件的成本影响,包括如何实现更高的延迟和可用性水平。通过仔细平衡所需要求与相关基础设施成本及操作复杂性,您可以做出与组织整体财务目标相一致的明智决策,同时依然能利用机器学习的优势。
-
可扩展性要求:可扩展性是指部署基础设施能够在不影响性能或质量的情况下,应对工作负载需求的增加或减少。确定深度学习模型的可扩展性要求至关重要,因为这将影响部署策略和基础设施的选择。您是否预期模型使用量会随着时间增长?您预期它会增长多快?您需要横向扩展(增加更多模型实例)还是纵向扩展(增加现有实例的资源)?对使用增长速率的预期将帮助您在模型落地时做出适当的组件选择和决策。
-
模型硬件要求:选择用于部署深度学习模型的硬件至关重要,因为它会显著影响整体部署的性能、延迟和成本。为了正确识别硬件需求,请考虑以下几点:
-
兼容性:确保所选硬件与用于开发深度学习模型的框架和库兼容。这包括检查硬件是否能支持特定功能,例如可能对模型性能至关重要的 GPU 加速。
-
处理能力:评估高效运行模型所需的处理能力,包括核心数量、内存和存储。考虑模型的复杂性和大小可能对硬件要求的影响。
-
功耗和散热:所选硬件的功耗和散热情况会影响整体运营成本和部署的环境足迹。选择能在性能与能源效率之间找到平衡的硬件。
-
未来适应性:考虑硬件的预期使用寿命及其是否能够适应模型未来的更新或改进。选择可以轻松升级或更换的硬件,以应对可能的需求。
-
集成:确保硬件能够无缝集成到其他部署基础设施及相关系统或组件中。
通过彻底评估模型的硬件要求,您可以做出明智的决策,确保在优化性能的同时,最小化成本和潜在的瓶颈。
-
-
数据隐私要求:确保深度学习模型及其预测所使用数据的隐私和安全至关重要,因为这会影响部署的信任和合规性。为识别并解决数据隐私要求,请考虑以下方面:
-
合规性要求:了解适用于您组织的数据保护法规和行业标准,如 GDPR、HIPAA 或 CCPA。确保部署策略和基础设施符合这些法规。
-
数据存储和处理地点:评估数据在部署过程中存储和处理的位置。确定是否存在数据驻留要求或限制,如需要将数据存储在特定的地理区域。
-
数据访问控制:所有深度学习应用都应要求实施适当的访问控制,以确保只有授权的用户或系统可以访问数据。这包括实现身份验证、授权和加密机制。
-
数据保留和删除政策:检查是否有数据保留和删除的法律和合规要求。确保部署基础设施支持这些政策,并在必要时允许安全删除数据。
-
数据监控与审计:检查是否需要实施监控和审计机制,以跟踪部署过程中数据的使用和访问。
-
数据泄露响应计划:此类计划应包括角色与职责、沟通渠道和修复措施。检查是否需要制定数据泄露响应计划,列出发生数据泄露或安全事件时的应对步骤。
-
-
安全性要求:反思模型在特定地区部署时必须遵守的法律和伦理边界。
-
信任与可靠性要求:机器学习模型的信任与可靠性指的是对模型在部署和运行过程中保持一致的性能、准确性,以及遵守伦理和合规标准的信心。在确定要求时,请考虑以下问题:
-
模型多久更新或修改一次?
-
是否有必要追踪多个模型版本?
-
模型在其运行环境中是否会面临概念漂移或数据漂移?
-
高效的错误检测和解决有多重要?
-
模型多久会收到更新或新增功能?
-
适应用户反馈或变化的需求是否至关重要?
-
是否有机会利用深度学习的进展来改进模型?
-
维护稳定和安全的生产环境是否是优先事项?
-
模型的性能对其用户或业务功能有多关键?
-
是否有与性能相关的严格服务水平协议(SLA)或监管要求?
-
在不同环境和配置中,性能一致性是否重要?
-
模型的预测是否具有重大影响,从而使得一致性对用户信任和成功至关重要?
-
其中一些要求最好在规划阶段早期确定。例如,从一开始就定义延迟要求,可以帮助您选择一个合适的模型,以确保运行时持续时间符合指定的延迟限制。在探讨了需要定义的要求类型和定义这些要求的大致方法后,我们现在可以讨论如何选择正确的部署选项。
选择合适的深度学习(DL)模型部署选项
为深度学习(DL)模型选择正确的部署选项是确保最佳性能、可扩展性和成本效益的关键步骤。为了帮助您做出明智的决策,我们将根据不同的需求探讨推荐的选项。这些建议涵盖多个方面,例如硬件和物理基础设施、监控和日志记录组件,以及部署策略。通过仔细评估模型的特性、资源限制和预期结果,您应该能够识别最合适的部署解决方案,以最大化效率并通过本指南实现投资回报率。我们将在这里探讨的实际部署组件包括架构决策、计算硬件、模型打包与框架、通信协议和用户接口。让我们一一深入探讨每个组件,从架构选择开始。
架构选择
机器学习服务的架构选择涉及设计基础设施、数据管道和部署方法,以确保高效和可靠的操作。我们将从服务部署的考虑因素开始:
-
微服务:将深度学习(DL)模型部署为一个小型、松散耦合、可独立部署的服务,并拥有自己的 API。微服务是一种软件架构设计模式,将应用程序构建为一组小型、松散耦合且可独立部署的服务。每个微服务负责应用程序中的特定功能或领域,并通过明确定义的**应用程序接口(API)**与其他微服务进行通信。因此,当以微服务形式部署时,前提是其他组件也必须以微服务的方式实现。其优势如下:
-
更好的可扩展性
-
更便捷的更新和维护
-
更高的弹性
-
技术选择的灵活性
在以下情况下选择该微服务:
-
当预计模型使用量会增长时
-
当需要频繁更新时
-
当需要与各种外部系统集成时
-
当高可用性至关重要时
-
-
独立服务:将深度学习模型作为一个独立的服务部署,而不是微服务。以电影推荐应用为例——微服务方法是创建一个评论分析服务微服务,使用深度学习模型处理电影评论。它有自己的 API、数据存储和部署管道,并且独立于应用中的其他服务运行。在同一个应用中采用独立服务方法,电影推荐服务结合了用户偏好管理、电影评论分析(使用深度学习模型)和推荐生成。它更具单体结构,整合了相关功能,拥有自己的 API,但没有为评论分析单独设置微服务。其优点如下:
-
更容易的管理和维护
-
更适合复杂的应用
-
集中的资源和数据访问
-
组件之间的简化通信
-
更可预测的性能
在以下情况下选择此独立服务:
-
当应用中服务数量有限时
-
当模型复杂并且需要更单体化的方法时
-
当模型的范围变化不频繁时
-
当优先考虑弹性与复杂性之间的平衡时
-
-
现有服务的一部分:将深度学习模型集成到应用或系统的现有服务中。其优点如下:
-
更少的复杂性
-
改进的性能
-
更容易的数据同步
-
潜在的成本节约
在以下情况下选择与现有服务集成:
-
当模型使用量增长有限时
-
当不需要频繁更新或修改时
-
当与外部系统的集成有限时
-
当高弹性不是关键要求时
-
在微服务和与现有服务集成之间做出决定时,应考虑可扩展性、更新频率、集成需求和弹性等因素。根据这些因素与您的具体需求对齐,以便做出最佳的深度学习模型部署决策。接下来,我们将讨论选择物理部署环境的建议:
-
云:当您需要高可用性并且能容忍适度的延迟时,云部署是合适的。它们减少了前期成本,并提供灵活的按需付费定价模型。基于云的基础设施提供几乎无限的资源,允许快速自动扩展,并且通常提供高可用性的保证和托管服务。然而,您需要仔细评估云服务提供商的安全性,并确保其与您的深度学习框架和库兼容。提供 GPU 服务的公司包括 AWS、GCP、Microsoft Azure 和 IBM Cloud。
-
本地服务器:本地服务器部署让你对硬件和网络资源拥有更多的控制权,非常适合在特定地理区域内实现低延迟和高可用性。这类部署需要在硬件和维护方面的前期投资,但如果你有高且稳定的资源需求,长期来看可以节省成本。与云端部署相比,本地部署在安全措施和数据隐私方面提供更多的控制权,但也需要更多的精力来维护和更新安全措施。确保与深度学习框架及库的兼容性。
-
边缘本地部署:也叫做边缘计算,这种方法将数据处理靠近数据源,提供极低的延迟、提升的安全性和数据隐私保护。边缘部署适用于需要将数据处理和存储靠近数据源的场景,并且能够减少数据传输成本。然而,跨多个边缘设备管理安全性,并确保与深度学习框架及库的兼容性,可能是一个挑战。边缘部署在分布式处理方面具有可扩展性,但可能需要更多的管理和维护工作。
接下来,我们将深入探讨容器编排平台,它们对应用程序和服务在系统中的设计、部署和管理方式产生了深远影响。容器是一个轻量级、独立的可执行软件包,包含了运行软件所需的所有内容,包括代码、运行时、系统工具、库和设置。容器彼此隔离,也与主机系统隔离,使它们能够在不同的计算环境中一致地运行。容器技术主要有两种类型:Docker 容器和 Linux 容器(LXC)。
容器编排平台帮助管理和扩展容器化应用程序中深度学习模型的部署,利用 Docker 容器或 LXC 等技术。这些平台提供跨多台机器、云服务商或本地基础设施的灵活部署。它们可以与其他工具和服务结合使用,从而实现对容器化应用和微服务的高效管理。以下是一些流行的容器编排平台:
-
Kubernetes(开源):Kubernetes 是一个开源的容器编排平台,能够自动化部署、扩展和管理容器化应用程序,包括深度学习模型。它与多种容器技术兼容,包括 Docker 和 LXC。
-
Docker Swarm(开源):Docker Swarm 是 Docker 容器的原生集群和调度工具。它与 Docker 生态系统紧密集成,提供了一种简单的方式来部署和管理容器化应用程序。虽然功能上不如 Kubernetes 丰富,Docker Swarm 以其易用性和更快速的设置而闻名。
-
Apache Mesos(开源):Apache Mesos 是一个分布式系统内核,它将 CPU、内存和存储资源从机器中抽象出来,使得系统具有容错性和弹性。它可以与其他框架(如 Marathon 或 DC/OS)结合使用,提供容器编排功能,用于部署和管理深度学习模型。
-
Amazon Elastic Kubernetes Service (EKS) 和 Amazon Elastic Container Service (ECS)(收费服务):这些是 AWS 提供的托管容器编排服务。EKS 是一个托管的 Kubernetes 服务,而 ECS 是 AWS 自有的容器编排平台。这两项服务简化了在 AWS 基础设施上部署、扩展和管理容器化应用程序的过程。
选择最适合你深度学习部署需求的容器编排平台,例如灵活性、可扩展性、与你偏好的容器技术的兼容性、云提供商以及与其他工具和服务的集成。
接下来,我们将深入探讨实时预测和批量预测之间的架构权衡:
-
实时预测:建议将模型始终加载到内存中,以减少延迟并快速响应请求。此设置适用于即时响应至关重要的应用场景,如自动驾驶汽车、实时聊天机器人或欺诈检测系统。使用此选项时,以下是一些建议:
-
使用一台专用服务器或云实例,确保有足够的内存和处理能力来处理模型和并发请求
-
使用量化、剪枝或模型蒸馏等技术优化模型推理
-
如果需要,实施负载均衡器以将传入请求分配到多个模型实例
-
监控资源使用情况和性能,确保系统满足实时要求,并根据需要进行扩展
-
使用队列系统确保工作人员不会过载,或者实施自动扩展来处理超载情况
-
-
按需批量预测:批量预测适用于实时响应不重要的场景,且预测可以按批次处理。此设置需要额外的时间来启动工作基础设施、初始化模型并加载训练好的模型权重。使用此选项时,以下是一些建议:
-
使用队列系统,如 RabbitMQ 或 Amazon SQS,来管理传入的预测请求
-
设置批处理系统,当处理开始时初始化模型并加载权重
-
优化批处理大小,以平衡处理时间和资源使用
-
实施自动扩展来处理变化的工作负载并确保资源的高效使用
-
接下来,我们将探讨计算硬件选择和建议。
计算硬件选择
选择硬件以执行模型计算是关于成本、可用性和运行时之间的权衡。我们来探讨不同的选项,并附上使用每种选项的建议:
-
CPU:CPU 是部署深度学习模型的多功能且具有成本效益的选项。它们与大多数框架和库兼容,并为不太复杂的模型提供不错的性能。当成本是首要考虑因素,并且你不需要 GPU 或 TPU 提供的高处理能力时,CPU 是一个不错的选择。
-
GPU:GPU 提供更快的处理速度和更好的并行化,显著降低延迟并提高性能。它们非常适合需要高处理能力的复杂模型。当你需要低延迟和高可用性时,GPU 是一个极好的选择,但它们的成本高于 CPU。
-
TPU:TPU 是为机器学习任务设计的专用硬件,提供高性能和高效处理。它们特别适用于大型模型或计算密集型任务。当你需要卓越的处理能力和低延迟时,TPU 是一个不错的选择,但要注意可能会有较高的成本,而且它仅在 GCP 中可用,并且只能在 TensorFlow 中使用!
-
人工神经引擎 (ANE):ANE 是专门的 AI 加速器,通常出现在如 iPhone 等设备中。它们为边缘设备上的深度学习任务提供高效的处理,具有低延迟和能效高的性能。当你的应用需要在 iPhone 这样的边缘设备上满足用户界面需求时,ANE 是一个不错的选择。需要注意的是,它仅与 CoreML 框架兼容,且需要使用 ONNX 权重格式来轻松转换权重到 CoreML。
-
FPGA:FPGA 是高度可定制的(其硬件电路可以编程!),同时具有能效高的特性,适用于部署需要低延迟和适应性的深度学习模型。这里的缺点是需要具备深厚的 FPGA 编程语言和电路开发专业知识,才能成功地高效地进行已训练神经网络的推理。这对于大多数团队来说是一个超出范围的设备。
接下来,我们将探讨模型打包和框架选择以及建议。
模型打包和框架
这决定了深度学习模型的执行方式,且推荐的选择可能取决于所使用的计算硬件,以及可移植性和运行时需求。以下是一些常见示例,并附有使用建议:
-
原始框架包装:你可以利用框架提供的特定优化和功能,从而提高性能。然而,在某些情况下,可能需要与特定硬件选项兼容,例如使用 TPU,而 TPU 仅支持 TensorFlow 框架。因此,如果你有 TPU 而选择使用 PyTorch,就无法使用 TPU。
-
开放神经网络交换 (ONNX) 框架:ONNX 提供了一种开放标准,用于表示深度学习模型,允许你将模型转换为不同的框架,并在各种硬件平台上运行。使用 ONNX 可以提高模型的灵活性和可移植性,使你能够从更广泛的硬件和基础设施选项中进行选择。此外,它还可以让你利用不同深度学习框架提供的优化和功能。一种方便且通用的解决方案是将模型转换为 ONNX 格式,之后可以根据需要轻松转换为其他格式。这种方法简化了过程,并确保与各种硬件和框架选项的顺利集成,例如在 iPhone 中利用 ANE 加速应用中的深度学习模型。
-
ONNX Runtime:这是一种推理加速器,旨在通过利用计算和内存优化来加速在任何硬件上的深度学习模型推理。在 ONNX Runtime 中运行模型比在本地深度学习框架(如 TensorFlow 或 PyTorch)中运行更快。
-
TensorRT:这是 NVIDIA 提供的高性能深度学习推理优化器和运行时/编译器库,旨在加速 NVIDIA GPU 上的深度学习模型推理。它支持 TensorFlow 和 ONNX,并提供便捷的方式将模型权重转换为与其框架兼容的格式,从而实现快速高效的模型部署。TensorRT 通过在较低层次调优模型,利用不同 GPU 内部硬件能力,提高 GPU 上的模型推理速度,最大化推理时的模型效率。由于 ONNX 权重与 TensorRT 兼容,将 PyTorch 模型权重转换为 TensorRT 兼容的权重格式的典型路径是先将 PyTorch 模型权重转换为 ONNX 权重。在 NVIDIA GPU 上,TensorRT 的推理速度被认为比 ONNX Runtime 更快。
-
开放视觉推理与神经网络优化 (OpenVINO):这是英特尔提供的一款工具包,旨在加速深度学习模型在英特尔硬件(包括 CPU、GPU 和 FPGA)上的推理。它支持 TensorFlow、ONNX 以及其他框架,提供在多种环境中的优化模型部署。
接下来,我们将探讨通信协议的选择和建议。
使用的通信协议
你应该使用的协议取决于运行时需求、网络负载需求、所选的用户界面、部署模式以及计算需求。以下是一些示例及其推荐:
-
MQTT:当你需要一个轻量级、低延迟的协议,适用于资源有限的设备(如物联网设备),并且实时通信和状态更新对你的应用至关重要时,可以使用 MQTT。功耗和散热是重要的考虑因素。
-
HTTP 或 REST API:当你需要一个受支持良好、易于实现的协议来处理网页服务和数据交换,且你的应用遵循请求-响应通信模式,并且需要符合数据保护法规和数据隐私要求时,选择此协议。
-
gRPC:当你需要高性能、低延迟协议来处理大规模分布式系统或微服务时,选择 gRPC,同时它还支持双向流和多种编程语言的支持。
-
服务器推送事件(SSE)或 WebSockets:当实时通知或实时更新对你的网页应用至关重要时,请使用它们。如果你需要服务器与客户端之间的单向通信,请使用 SSE。如果你需要服务器与客户端之间的双向通信,请使用 WebSockets。一个需要这些通信协议的显著领域是具有机器学习功能的实时协作工具。以下是一些示例:
-
Grammarly 使用 WebSockets
-
ChatGPT 使用 SSE
-
接下来,我们将探讨用户界面选择和推荐。
用户界面
在设计机器学习应用的用户界面时,必须考虑用户体验、可访问性、响应性和适应性等因素。以下是一些用户界面的建议:
-
网页应用程序:
-
它们适用于跨平台访问,因为用户可以通过网页浏览器访问应用程序
-
使用流行的网页开发框架,如 React、Angular 或 Vue.js 来构建响应式和互动性强的用户界面
-
示例用例:一个情感分析工具,允许用户输入文本并通过与机器学习模型的互动获得情感评分,界面基于网页
对于网页应用程序,你还需要根据提供的好处以及延迟权衡来明智地选择网页框架。请参考 www.techempower.com/benchmarks/… 获取不同网页框架在单个网页 API 查询中的延迟估算。
-
-
移动应用程序:
-
非常适合通过智能手机和平板电脑随时访问机器学习功能
-
使用 Swift 或 Kotlin 开发 iOS 和 Android 平台的原生应用,或使用跨平台框架如 React Native 或 Flutter
-
示例用例:一个移动应用,利用机器学习模型进行图像识别,通过分析用户拍摄的照片来识别植物或动物
-
-
桌面应用程序:
-
适用于需要专用、平台特定应用并具备离线功能的用户
-
使用 Electron 或 Qt 等技术来构建跨平台桌面应用程序,或使用平台特定的语言,如 Windows 的 C# 或 macOS 的 Swift
-
示例用例:一个视频编辑软件,内置机器学习驱动的功能,如物体跟踪、自动色彩校正或场景检测
-
-
语音用户界面(VUI):
-
适用于通过语音命令与机器学习驱动的服务进行免提交互
-
集成流行的语音助手平台,如亚马逊 Alexa、谷歌助手或苹果 Siri
-
示例用例:一个语音激活的家庭自动化系统,使用自然语言处理根据用户命令控制智能设备
-
-
对话式 UI(聊天机器人):
-
适用于通过文本或语音对话更自然、互动地吸引用户
-
使用聊天机器人开发平台,如 Dialogflow、Rasa 或 Microsoft Bot Framework
-
示例用例:一个客户支持聊天机器人,使用机器学习驱动的自然语言理解来回答用户查询并提供帮助
-
-
增强现实 (AR) 和虚拟 现实 (VR):
-
适用于将现实世界和数字世界结合的沉浸式和互动式体验
-
使用 AR/VR 开发平台,如 Unity 或 Unreal Engine,并集成机器学习模型进行物体识别、运动跟踪或场景理解
-
示例用例:一个虚拟训练模拟器,使用深度学习模型实时分析和评估用户表现。在这个 AR/VR 应用中,用户可以练习各种技能,如医疗程序、机械修理或紧急响应场景。深度学习模型通过视觉输入评估用户的操作,提供即时反馈,并为改进提供个性化指导,增强学习体验并加速技能发展。
-
-
基于 API 的用户界面:基于 API 的用户界面提供了一种灵活且可扩展的方式,将你的机器学习模型与各种应用、平台和服务进行集成。这种方法允许开发者构建自定义用户界面或将机器学习驱动的功能整合到现有应用中,从而扩展模型的覆盖面和影响力。这适用于使其他应用、系统或服务能够以编程方式访问和与机器学习模型进行交互。以下是该方法的两个建议:
-
使用 REST、gRPC、SSE、Websockets 或 MQTT 创建结构良好且有文档的 API,将机器学习模型的功能暴露给外部客户端
-
实现身份验证和授权机制(例如,API 密钥和 OAuth)以确保对 API 的安全访问
-
示例用例:一个情感分析 API,允许开发者通过 API 调用发送文本数据并接收情感评分,将机器学习驱动的情感分析集成到他们的应用中
-
选择适合的部署选项涉及仔细评估架构选择、硬件选项、通信协议和用户界面,确保它们最能与特定需求和目标对接。通过考虑可扩展性、更新频率、集成需求和韧性等因素,你可以选择最合适的部署解决方案,从而最大化效率和投资回报率。
接下来,我们将讨论在部署深度学习模型时决策组件的一些实际示例。
基于实际用例探讨部署决策
在本节中,我们将探讨深度学习(DL)模型在生产环境中的实际部署决策,重点关注两个不同的使用案例:一个用于电子商务公司的情感分析应用和一个用于安全摄像头的面部检测与识别系统。通过分析这些真实世界的场景,我们将获得建立稳健部署策略的宝贵见解,以满足特定需求和目标。
探讨情感分析应用的部署决策
假设你正在开发一个情感分析应用,供电子商务公司用于实时分析客户评论。该系统需要每天处理大量评论,并且低延迟至关重要,以便为公司提供即时的洞察。在这种情况下,你的选择可能如下:
-
架构选择:作为独立服务部署,因为它可以提供更好的可扩展性,并且在处理不断增长的请求时更易于更新。
-
硬件/基础设施选择:云服务上的 GPU,因为它提供更好的并行处理能力和大规模并发请求的处理能力。
-
模型包装与框架:ONNX 和 TensorRT,因为它们提供高效的模型部署和推理加速。
-
安全性、信任与可靠性:实施数据漂移和模型性能的监控,定期用更新后的数据重新训练模型,并确保符合数据隐私法规。例如,对用户信息进行匿名化处理,并避免在分析中存储个人身份信息(PII),因为这可能会违反数据保护法规,如欧盟的 GDPR 或美国的 CCPA,具体取决于应用部署的国家。
-
通信协议:RESTful API 或 gRPC,因为它们非常适合用于 Web 服务,并能以低延迟处理大量请求。
-
用户界面:基于网页的仪表板,公司的员工可以在其中实时监控情感分析结果。
探讨安全摄像头的面部检测与识别系统的部署决策
假设你正在为需要实时检测入侵者的安全摄像头构建物体检测系统。在这种情况下,你的选择可能如下:
-
架构选择:本地边缘部署,因为它通过在数据源附近处理数据来提供低延迟和增强的安全性。此选择还减少了数据在网络中传输所需的时间,因为不需要将视频流传输到某个云服务器。
-
硬件/基础设施选择:根据与深度学习框架的兼容性和模型的复杂性,在边缘设备上选择 GPU 或 TPU。
-
模型包装和框架:ONNX 和 TensorRT,因为它们提供高效的模型部署和推理加速。
-
安全性、可信度和可靠性:实现对模型性能的监控,并确保遵守与视频监控相关的本地法规,如数据隐私、存储政策和同意要求,以维持视频分析的伦理和法律标准。例如,面部图像不应被存储,仅应存储提取的面部特征,因为这可能违反与个人数据保护相关的法规,具体取决于部署的国家。
-
通信协议:MQTT 或 WebSockets,因为它们提供边缘设备和中央监控系统之间的低延迟通信。
-
用户界面:一个桌面应用程序,显示带有物体检测叠加层的实时视频流,供安保人员监控。
通过考虑每个用例的具体需求,您可以就生产环境中部署深度学习模型所需的组件做出明智的决策。接下来,让我们进入一些成功深度学习模型部署的通用建议。
探索深度学习部署的通用建议
在这里,我们将发现与三大垂直领域相关的深度学习部署建议,即模型安全、可信度和可靠性保障,模型延迟优化,以及帮助抽象模型部署相关决策并简化模型部署过程的工具。我们将逐一深入探讨这三大领域。
模型安全、可信度和可靠性保障
确保模型安全、可信度和可靠性是部署深度学习系统的关键环节。在本节中,我们将探索各种建议和最佳实践,帮助您建立一个强大的框架,以维护模型的完整性。这包括遵守规定、实施安全防护措施、预测一致性、全面测试、分阶段和生产部署策略、可用性测试、重新训练和更新已部署的模型、人工干预决策和模型治理。通过采取这些措施,您可以有效地降低风险、提升性能,并增强用户对您深度学习部署的信任。
遵守规定并实施安全防护措施
法规遵从性和保障措施是负责任的深度学习部署的重要组成部分,确保你的模型遵守相关法律、行业标准和道德准则。实施强有力的合规框架不仅可以减少法律和声誉风险,还能增强用户和利益相关者的信任。这是一个非常广泛的话题,以下是一些可以借鉴的例子:
-
社交媒体平台内容审核:遵守社区准则和地区法律可以通过实施 AI 驱动的过滤器来检测和标记不当内容,设立人工审查流程处理模糊案件,并为用户提供透明的上诉机制。
-
AI 驱动的招聘工具:遵守反歧视法可以采取措施,例如监控偏见和公平性指标的表现,确保任何自动化决策对雇主和申请者都是透明且可解释的。
-
人脸识别系统:遵守隐私和道德准则可以通过以下步骤实现,例如在收集和处理个人生物识别数据之前获得明确同意,实施强大的数据安全措施,并确保关于系统能力和局限性的透明度。
-
基于深度学习的视频监控系统,例如人员检测:遵守隐私和道德准则可以通过措施实现,例如设置明确的标识牌,告知公众存在监控摄像头,限制数据访问仅限授权人员,并根据当地法规遵守数据保留和删除政策。
-
推荐系统(YouTube、Netflix 和 Tiktok):确保符合数据保护法规可以采取措施,例如实施隐私保护数据处理技术、为用户提供选择退出个性化推荐的功能,以及在数据收集和使用政策方面保持透明。
-
生成式 AI:合规性可以通过使用内容过滤机制来实现,以防止有害内容的生成,包括仇恨言论、淫秽材料和鼓励犯罪活动的内容,或者防止关于医疗问题的危险推荐。
随着我们继续探索模型安全性、信任和可靠性保障,让我们来审视确保深度学习部署中预测一致性的关键方面。
确保预测一致性
预测一致性是指模型在面对相同的输入数据时,能够生成相同的预测,无论硬件、序列化/反序列化过程、基础设施,还是单条数据还是随机批次。一致性差的预测可能导致对模型准确性和整体性能的预期偏差。为了在各种因素中保持一致性,必须跟踪并复制训练、评估和推理过程中涉及的环境依赖关系。像 Docker 这样的工具可以帮助创建具有特定依赖项的隔离环境,确保无缝体验并消除潜在问题。此外,考虑设置自动化测试,客观地防止任何不一致通过,基本上充当一个保护屏障。
接下来,我们将讨论全面测试在保持可靠的深度学习部署中的重要性。
测试
除了预测一致性测试,全面的测试通常可以确保你的深度学习模型和系统在任何时候都能按预期运行,并满足用户需求。深度学习系统本质上是软件系统,需要类似的措施来确保成功部署。测试组件如下:
-
单元、集成和功能测试:单元测试、集成测试和功能测试对确保软件组件的可靠性、可维护性和整体质量至关重要。以下是它们的重要性:
-
单元测试:侧重于个别组件或功能,验证其正确性并在开发早期隔离潜在问题。这有助于在问题蔓延前捕捉到错误,减少调试时间,提高代码可维护性。
-
集成测试:验证不同组件之间的交互,确保它们按预期协同工作。这有助于识别界面问题、数据流问题以及在组合组件时可能出现的不一致,确保顺利集成。
-
功能测试:评估软件是否能够实现其预定目的并满足用户需求。端到端功能测试确保软件在实际场景中正常运行,并提供良好的用户体验。
-
-
故障切换与恢复测试:验证模型在遇到硬件或软件崩溃等故障后,能否恢复并保持在意外中断情况下的高可用性。
-
负载压力测试:评估模型在不同负载条件下的表现,识别瓶颈并确保其能够处理预期的用户流量。这些测试还能帮助你发现错误,如 GPU 内存溢出、CPU 过载或存储不足等。
-
广泛且多样化的测试:模型可能无法优雅地处理意外的输入数据、边缘情况或系统故障,导致崩溃或不期望的行为。考虑所有可能的系统使用方式有助于你捕捉到系统中的问题。
-
采用分阶段部署和生产部署步骤:在深度学习(DL)生产部署中采用分阶段和生产部署策略,对于确保模型的可靠性和性能非常有益。这种方法包括为测试(阶段)和最终部署(生产)设置单独的环境,使你能够验证模型的行为并在模型上线之前识别潜在问题。通过采纳这一策略,可以最小化部署未经测试模型的风险,简化问题识别和解决的过程,并提高整个 DL 解决方案的可靠性。确保流水线能够在 24 小时内持续无故障地运行。
-
可用性测试:可用性测试的重点是确保软件应用程序能够提供高效、有效且令人满意的用户体验。自动化和手动测试互为补充,各有其独特的作用:
-
手动可用性测试:这涉及真实用户与软件的互动,以识别潜在的可用性问题、了解用户行为并收集定性反馈。手动测试有助于发现自动化测试可能无法检测到的问题,例如导航混乱、指令不清或主观偏好等。这种以人为中心的方法提供了关于用户如何感知软件的宝贵见解,并确定了改进的领域。
-
自动化可用性测试:自动化可用性测试通过使用工具和脚本来模拟用户互动、验证用户界面元素并检查可访问性和响应性,从而补充手动测试。自动化测试具有多个优势,包括提高效率、速度和覆盖面,以及能够在多个设备、平台和浏览器上进行一致的测试。这有助于识别在手动测试中可能不显现的可用性问题,从而确保一致且高质量的用户体验。
-
接下来,我们来讨论重新训练和更新已部署模型的重要性,以确保其持续的有效性和相关性。
重新训练和更新已部署的模型
重新训练和更新策略对于保持深度学习模型的有效性至关重要,因为它解决了随着数据模式变化可能需要定期更新的问题。通过定期在最新、相关的数据上重新训练模型,您可以确保它始终保持最新,并持续提供准确的预测。这不仅有助于保持模型的性能,还能让它与不断变化的趋势和用户需求保持同步。在第十六章《深度学习模型治理》中,我们将深入探讨重新训练和更新的重要性,探索其好处和最佳实践,帮助您在实际的深度学习部署中成功实施这一策略。
为了进一步提升我们的深度学习部署效果,我们将探索采用人类决策参与的决策流程的好处。
采用人类决策参与的决策流程
将人类决策参与场景纳入您的深度学习部署可以极大提高模型的性能和可靠性,无论是作为长期解决方案,还是在满足某些条件时触发警报。通过让人类专家参与决策过程,您可以弥合模型预测与现实世界复杂性之间的差距,从而做出更准确、细致的决策。这种合作方法通过利用人类的专业知识来验证、纠正和调整模型输出,推动持续改进。此外,人类决策参与系统还能够增强信任和责任感,因为用户可以放心地知道,复杂或高风险的决策不仅由算法做出,还得到了人类判断和监督的支持。
最后,我们将深入探讨模型治理在监督和管理整体深度学习部署过程中的关键作用。
模型治理
监控和治理在确保深度学习部署持续有效性和可靠性方面发挥着关键作用。通过跟踪模型的各个方面,如数据漂移和概念漂移监控,您可以识别并解决可能影响其长期表现的问题。数据漂移监控有助于检测底层数据分布的变化,而概念漂移监控则关注输入特征与目标变量之间关系的变化。建立一个强大的监控和治理框架使您能够主动管理模型的性能,并在面临不断变化的趋势和条件时保持其准确性。在第十六章《深度学习模型治理》中,我们将更详细地探讨这些方面,以及模型监控和治理的其他关键组件,帮助您制定一个全面的策略,保持深度学习部署的有效性。
接下来,我们将探讨模型延迟优化的建议。
优化模型延迟
假设你已经选择了理想的架构,训练了一个模型,提取了洞察,选择了推理模型编译器/加速框架,并为托管模型选择了目标硬件基础设施和架构,那么你可以采取额外的步骤来提高此阶段的模型延迟。可以采用以下技术:
-
模型剪枝:去除神经网络中不必要的神经元或权重,而不会显著影响整体性能。剪枝技术包括权重剪枝、神经元剪枝和滤波器剪枝。这可以减小模型大小和计算要求,从而提高推理速度。
-
模型量化:将模型参数(例如权重和偏置)的精度从 32 位浮动点数降低到较低位宽的表示方式,如 16 位或 8 位整数。量化可以加速模型推理,同时不会显著损失准确性,特别是在将深度学习模型部署到计算资源有限的硬件上时。
-
模型蒸馏:训练一个更小、更快的“学生”模型来模仿较大、较慢的“教师”模型的行为。学生模型从教师模型的输出中学习,以较低的复杂度和更快的推理速度实现相似的性能。此方法在第十三章中进行了展示,探索偏差和公平性。
-
模型并行:在模型并行中,神经网络的不同部分被分配到多个设备或处理器上,从而在不同的模型部分上进行并行计算。对于无法完全容纳在单个 GPU 内存中的巨大模型,这种方法是必不可少的。对于具有高度并行操作的模型,延迟可以显著减少。模型并行可以在各种并行级别上实现,例如层级并行、流水线级并行或张量切片级并行。
-
批量推理:通过批处理同时处理多个输入样本,使模型能够更好地利用底层硬件,从而提高整体推理速度。
接下来,我们将探讨一些抽象化部署的工具。
抽象化部署的工具
有许多工具和平台可用,帮助抽象化模型部署过程,使得在不同环境中部署机器学习模型变得更简单、更高效。以下是一些流行工具和平台的概述,包括开源工具和收费工具:
-
TensorFlow Serving(开源工具):一个灵活的高性能服务系统,用于在生产环境中部署 TensorFlow 模型,提供开箱即用的支持模型版本控制、REST 和 gRPC API,以及在 GPU 和 CPU 上的高效模型服务。
-
TorchServe(开源工具):PyTorch 的等效工具,类似于 TensorFlow Serving。
-
TensorFlow Extended (TFX)(开源工具):一个端到端的平台,用于在生产环境中部署、管理和维护机器学习管道。TFX 与 TensorFlow、TensorFlow Serving 及其他工具集成,提供无缝的部署体验。
-
MLflow(开源工具):一个开源平台,简化了端到端机器学习生命周期,包括实验、可重现性、部署和监控。它支持多种编程语言和机器学习库,是多样化项目的多功能选择。
-
Kubeflow(开源工具):一个与 Kubernetes 集成的解决方案,旨在促进可适应、可传输的机器学习任务的创建、协调、部署和执行。通过为不同的云服务提供商和本地基础设施提供一致的统一环境,它简化了部署过程。
-
Streamlit(开源工具):一个 Python 库,使开发者能够快速构建和部署机器学习与数据科学项目的定制化 web 应用。Streamlit 简化了创建交互式 web 应用的过程,几乎无需编码,从而更容易通过 web 应用共享和部署模型。
-
NVIDIA Triton(开源工具):一个开源工具,可用于部署深度学习(DL)模型。它原生支持许多框架,特别是 TensorRT、Pytorch、ONNX Runtime、OpenVINO,以及一个通用的 Python 后端,允许你包装和运行任何 DL 框架和 Python 代码。它通过 HTTP REST API 和 gRPC 协议提供预测。它还原生提供与 Prometheus 兼容的标准时间序列性能指标日志,之后可用于在 Grafana 仪表板中进行模型监控。它还允许我们在其 C API 中配置自定义指标。最重要的是,它简化了多个 GPU 的利用和 GPU 内存分配。我们将在下一节中实际探讨该工具的使用。
-
Azure ML 部署(付费工具):微软 Azure 的机器学习服务,简化了云端模型的部署。它提供了管理、监控和扩展已部署模型的工具,并支持如 TensorFlow 和 PyTorch 等流行框架。
-
DataRobot(付费工具):DataRobot 是一个自动化机器学习平台,简化了构建、部署和维护机器学习模型的过程。它提供了多种工具和功能,包括定制、模型版本控制、监控和协作。我们将在 第十八章 中探索该平台的使用,探索 DataRobot AI 平台。
-
Google Vertex AI(付费工具):谷歌云提供的托管机器学习平台,简化了端到端的机器学习工作流,包括模型训练、部署和管理。它与 TensorFlow、PyTorch 以及其他流行框架集成。
-
Amazon SageMaker(付费工具):AWS 提供的一项完全托管的机器学习服务,允许开发者快速轻松地构建、训练和部署机器学习模型。它支持多种框架,并提供模型版本管理、监控和扩展工具。
这些工具和平台有助于简化和优化模型部署过程,使开发者能够高效地在各种环境中部署他们的机器学习模型。
成功的 DL 部署需要解决关键方面,例如安全性、信任、可靠性和延迟优化,同时利用简化流程的工具和平台。通过遵循这些建议并使用合适的工具,开发者可以有效地部署和管理他们的 DL 模型,确保在各种环境中始终如一、可靠的性能。
到现在为止,你应该已经能明显看出,在选择 DL 部署系统组件之前,需要评估和考虑许多折衷和标准。然而,如果你没有付费工具,拥有一个 DL 模型,并且可以访问一个具有足够内存来托管模型的 GPU 机器,那么这三种工具就是不容置疑的选择。接下来的部分,我们将深入探讨这三种工具,并实际使用它们。
使用 ONNX、TensorRT 和 NVIDIA Triton Server 部署语言模型
这三种工具是 ONNX、TensorRT 和 NVIDIA Triton Server。ONNX 和 TensorRT 旨在执行基于 GPU 的推理加速,而 NVIDIA Triton Server 用于托管 HTTP 或 GRPC API。我们将在本节中实践探索这三种工具。TensorRT 被认为是最擅长优化模型以加速 GPU 推理的工具,而 NVIDIA Triton Server 是一款经过实践考验的托管深度学习(DP)模型的工具,并且与 TensorRT 原生兼容。另一方面,ONNX 是设置过程中的一个中间框架,我们主要用它来托管 TensorRT 直接支持的权重格式。
在本实践教程中,我们将部署一个来源于 Hugging Face 的语言模型,该模型可以在大多数 NVIDIA GPU 设备上运行。我们将把基于 PyTorch 的 Hugging Face 语言模型转换为 ONNX 权重,这将允许 TensorRT 加载 Hugging Face 语言模型。接下来,我们将创建代码和配置,要求 NVIDIA Triton Server 框架托管该语言模型。NVIDIA Triton Server 支持两种方式来部署模型,一种是将深度学习(DL)模型与其预处理和后处理方法作为一个单一管道部署,所有这些都嵌入在一个 Python 类中,另一种是通过逻辑上分离管道的不同组件来部署 DL 模型。图 15.1 描述了这两种方法,它们涉及需要两个模型的管道:
图 15.1 – 配置具有两个模型的管道中模型部署的两种方法
部署深度学习模型管道的一种直观且简单的方式是使用第一种方法。然而,第二种方法,通过将每个组件分开并拆分为独立的配置,提供了多种好处:
-
模块化和可重用性:模块化的特点使得各个部分可以在不同的管道或项目中重用。此外,它还允许在保持其他组件的同时,轻松地交换组件。
-
可扩展性和灵活性:这使得您可以将不同的组件部署到不同的 GPU,并为每个组件分配不同的实例进行同时运行。此外,这种方法允许与 CPU 绑定的方法不受 GPU 限制。
-
并行性和延迟减少:可以通过并行分支启用本地并行性,而不需要在 Python 代码中实现它。
将以下两种方法视为组织工厂流水线的方式:
-
在第一种方法中,整个组装过程被合并为一个单一的管道。这意味着所有组件都会按顺序处理并组合成一个集成过程。这种方法可能更容易设置和管理,但它的灵活性和可扩展性可能不如第二种方法。
-
在第二种方法中,组装过程被拆分为独立的模块化组件,这些组件可以单独管理和优化。这样可以提高灵活性和可扩展性,因为每个组件可以独立调整或更换,而不会影响整个管道。此外,这种方法支持并行处理,多个组件可以同时处理,可能会减少整体延迟并提高效率。
为了简化演示,我们将在这里展示第一种方法。
实际使用单一管道方法部署深度学习模型。
在本教程中,我们将探索使用 ONNX、TensorRT 和 NVIDIA Triton Server 部署深度学习模型的过程。在使用 NVIDIA Triton Server 部署模型时,您可能会遇到与模型加载、配置或推理相关的问题。以下是一些故障排除建议:
-
验证模型文件、配置文件和其他必需文件是否位于正确位置,并且具有正确的文件权限。
-
确保模型配置文件(
config.pbtxt)具有正确的设置,例如输入和输出张量名称、数据类型和维度。 -
检查 NVIDIA Triton Server 日志,查看是否有任何错误消息或警告,这些可能为问题提供线索。
-
确保必要的依赖项,如深度学习框架、ONNX 和 TensorRT,已安装并与您的系统和硬件兼容。
让我们以逐步的方式开始实际操作教程:
-
首先,我们需要通过运行以下代码来安装
transformer-deploy仓库:pip install git+ https://github.com/ELS-RD/transformer-deploy/tree/6b88e24ade6ce199e825adc0477b28a07f51f17d或者,我们可以使用以下命令:
git clone https://github.com/ELS-RD/transformer-deploy git checkout 6b88e24ade6ce199e825adc0477b28a07f51f17d transformer-deploy uses the tracing mode from PyTorch to convert a PyTorch model into ONNX. This involves sending some example PyTorch tensor data to the PyTorch helper tool, which will then trace the data shapes in the model through a forward pass to form the graph in ONNX. The following code shows a snippet of this tool as a pytorch library method:with torch.no_grad():
torch.onnx.export(
model_pytorch,
args=tuple(inputs_pytorch.values()),
f=output_path,
opset_version=13,
do_constant_folding=True,
input_names=input_names,
output_names=output_names,
dynamic_axes=dynamic_axis,
training=TrainingMode.EVAL,
)
这是我们使用 transformer-deploy 工具时发生的情况:
trt_engine = builder.build_serialized_network(network_def, config)
engine: ICudaEngine = runtime.deserialize_cuda_engine(trt_engine)
with open(engine_file_path, "wb") as f:
f.write(engine.serialize())
Here, `network_def` contains the ONNX graph definition of the model, and `config` specifies all the optimization strategies where the TensorRT default will be used. -
现在,我们将执行命令,将选定的 Hugging Face 文本生成模型转换为 TensorRT 序列化引擎:
roneneldan/TinyStories-3M. Additionally, one aspect that we need to take care of when using any graph optimizations and conversions to another framework is to make sure it maintains a satisfactory level of accuracy and doesn’t degrade too much from the model when it is run in the original base framework. A natural way to do this is to check the validation performance where the model is trained. In this case, we don’t know what dataset the Hugging Face model is trained from, so a workaround can be to take any relevant text generation dataset and validate on both the base framework and target framework setup. The transformer-deploy tool performs a simple predicted values deviation check to make sure the predicted values that are generated using the target framework don’t deviate too far from the values generated using the base framework. The atol parameter controls the leniency of this deviation check. -
在 步骤 4 中运行命令的结果如下:
[Pytorch (FP32)] mean=6.05ms, sd=0.21ms, min=5.91ms, max=8.12ms, median=6.01ms, 95p=6.27ms, 99p=7.16ms [Pytorch (FP16)] mean=7.76ms, sd=0.25ms, min=7.52ms, max=10.16ms, median=7.72ms, 95p=7.98ms, 99p=9.20ms [TensorRT (FP16)] mean=0.61ms, sd=0.06ms, min=0.56ms, max=0.86ms, median=0.57ms, 95p=0.70ms, 99p=0.71ms [ONNX Runtime (FP32)] mean=1.89ms, sd=0.06ms, min=1.81ms, max=2.48ms, median=1.88ms, 95p=1.93ms, 99p=2.21ms [ONNX Runtime (FP16)] mean=2.07ms, sd=0.09ms, min=1.99ms, max=3.11ms, median=2.06ms, 95p=2.11ms, 99p=2.62ms Each inference engine output is within 5.0 tolerance compared to Pytorch output triton_models/model.plan在这里,我们将把模型移到以下路径的新文件夹中:
models/transformer_tensorrt_text_generation/1/model.plan -
现在,是时候创建托管此 TensorRT 模型在 NVIDIA Triton Server 上所需的代码和配置了。按照 图 15*.1* 中的方式 1,我们需要定义一个 Python 类,用于初始化并使用 Hugging Face 分词器和 TensorRT 引擎进行推理。让我们开始在这个部署 Python 代码文件中导入必要的 Python 库:
from typing import List import numpy as np import tensorrt as trt import torch import triton_python_backend_utils as pb_utils from transformers import AutoTokenizer, TensorType from transformer_deploy.backends.trt_utils import load_engine -
接下来,我们必须指定模型和我们存储序列化 TensorRT 模型的路径:
model = "roneneldan/TinyStories-3M" tensorrt_path = "/models/transformer_tensorrt_text_generation/1/model.plan" -
现在,我们必须定义
TritonPythonModel类接口,从初始化方法开始:class TritonPythonModel: def initialize(self, args): self.tokenizer = AutoTokenizer.from_pretrained(model) self.model_input_names = self.tokenizer.model_input_names trt_logger = trt.Logger(trt.Logger.VERBOSE) runtime = trt.Runtime(trt_logger) self.model = load_engine( runtime=runtime, engine_file_path=tensorrt_path )类名必须与初始化方法名相同,即
initialize。此代码从 Hugging Face 库方法中加载预训练的分词器,并从互联网下载分词器。需要注意的是,对于生产环境中部署的模型,建议将分词器权重或任何模型权重的管理实例存储在某个位置,以确保可靠的部署过程。此外,代码加载了序列化的 TensorRT 引擎。 -
接下来,我们需要定义分词器和模型的实际推理部分,如下所示:
def execute(self, requests): responses = [] for request in requests: query = [t.decode("UTF-8") for t in pb_utils.get_input_tensor_by_name(request, "TEXT").as_numpy().tolist()] tokens = self.tokenizer( text=query, return_tensors=TensorType.PYTORCH, return_attention_mask=False ) input_ids = tokens.input_ids.type(dtype=torch.int32) input_ids = input_ids.to("cuda") output_seq: torch.Tensor = self.model({"input_ids": input_ids})['output'].cpu().argmax(2) decoded_texts: List[str] = [self.tokenizer.decode(seq, skip_special_tokens=True) for seq in output_seq] tensor_output = [pb_utils.Tensor("OUTPUT_TEXT", np.array(t, dtype=object)) for t in decoded_texts] responses.append(pb_utils.InferenceResponse(tensor_output)) return responses -
这段代码应位于
models/transformer_tensorrt_text_generation/1/model.py路径下的 Python 代码model.py文件中。名为
1的文件夹是为了表示transformer_tensorrt_text_generation模型名称的版本。 -
我们需要的最终文件是一个配置文件,该文件指定了模型的名称、模型的最大批量大小、模型的后端类型(在本例中是 Python)、输入类型、模型的名称和维度、输出类型、模型的名称和维度、该管道的实例数量,以及是否使用 GPU 或 CPU。该文件需要命名为
config.pbtxt。我们使用的该文件内容如下:name: "transformer_tensorrt_text_generation" max_batch_size: 0 backend: "python" input [ { name: "TEXT" data_type: TYPE_STRING dims: [ -1 ] } ] output [ { name: "OUTPUT_TEXT" data_type: TYPE_STRING dims: [ -1 ] } ] instance_group [ { count: 1 kind: KIND_GPU } ]应该将其存储在以下文件路径下:
models/transformer_tensorrt_text_generation/config.pbtxt -
现在,我们有了运行 NVIDIA Triton Server 和部署我们的语言模型所需的所有代码和配置,这是一种基于
nvidia-docker的易于使用的部署,且有公开可用和可下载的镜像。可以通过执行以下命令将语言模型部署到 NVIDIA Triton Server:triton_client.py, which will define the code that’s needed to query the hosted model to obtain a generated text and print it out on the command line. The first step is to import the necessary libraries, which in this case will only be the HTTP client from the tritonclient library:导入 tritonclient.http 作为 httpclient
-
我们还需要指定在第 12 步中定义的
config.pbtxt文件中的模型名称以及模型版本,如下所示:MODEL_NAME = "transformer_tensorrt_text_generation" MODEL_VERSION = "1" -
现在,我们将使用
httpclient助手工具定义客户端,定义输入数据,配置输出数据,以便根据config.pbtxt指定的输出名称获取,并打印生成的文本:def main(): client = httpclient.InferenceServerClient(url="localhost:8000" input_text = np.array(["Tell me a joke."], dtype=object) input_tensors = [ httpclient.InferInput("TEXT", (1,), datatype="BYTES") ] input_tensors[0].set_data_from_numpy(input_text) outputs = [ httpclient.InferRequestedOutput("OUTPUT_TEXT") ] query_response = client.infer( model_name=MODEL_NAME, model_version=MODEL_VERSION, inputs=input_tensors, outputs=outputs ) output_text = query_response.as_numpy("OUTPUT_TEXT") print(output_text) if __name__ == '__main__': main() -
在命令行中运行
python triton_client.py将返回以下响应:b' you are big!"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
到此为止,我们完成了实践教程!
本主题旨在展示使用 NVIDIA Triton Server 加速部署语言模型所需的最小工作流,这与音频 DL 模型或计算机视觉 DL 模型并无太大区别。尝试使用这里展示的工作流与其他语言模型进行实验,并尝试调整所有设置!请注意,由于新语言模型中的高度自定义层,转换或优化阶段可能会遇到一些问题,因此你可能需要在基础库中修复这些问题,或者将其提交给相关团队并等待更新。
在专门使用语言模型进行部署时,还有一些额外的工具可以用于部署,它们由于对 Hugging Face 模型的高层次抽象以及对选定 LLM 的开箱即用官方支持,可能值得考虑。具体如下:
-
baichuan-inc/Baichuan-7B)、BLOOM(bigscience/bloom和bigscience/bloomz)、GPT-2(gpt2和gpt2-xl)、GPT BigCode(bigcode/starcoder和bigcode/gpt_bigcode-santacoder)、GPT-J(EleutherAI/gpt-j-6b和nomic-ai/gpt4all-j)、GPT-NeoX(EleutherAI/gpt-neox-20b、databricks/dolly-v2-12b和stabilityai/stablelm-tuned-alpha-7b)、LLaMA 和 LLaMA-2(meta-llama/Llama-2-70b-hf、lmsys/vicuna-13b-v1.3、young-geng/koala和openlm-research/open_llama_13b)、MPT(mosaicml/mpt-7b和mosaicml/mpt-30b)、OPT(facebook/opt-66b和facebook/opt-iml-max-30b) -
CTranslate2(github.com/OpenNMT/CTr…
-
编码器-解码器模型:Transformer 基础/大模型、M2M-100、NLLB、BART、mBART、Pegasus、T5 和 Whisper
-
仅解码器模型:GPT-2、GPT-J、GPT-NeoX、OPT、BLOOM、MPT、Llama、CodeGen、GPTBigCode 和 Falcon
-
仅编码器模型:BERT
-
-
文本生成接口(github.com/huggingface… 7B、Falcon 40B、MPT 和 Llama V2
-
OpenLLM(
github.com/bentoml/OpenLLM):这款工具集成了 Langchain 和 Hugging Face 代理,但没有使用加速器/编译器库 -
Mlc-llm (
github.com/mlc-ai/mlc-llm):这支持多种设备,如手机
最后一点,如果我们没有遵循上一个主题中提出的建议和指南,这里呈现的实际部署示例将不会如此有效,因此请务必全面执行每一个建议!
摘要
在本章中,我们探讨了在生产环境中部署深度学习模型的各个方面,重点关注了关键组件、需求和策略。我们讨论了架构选择、硬件基础设施、模型打包、安全性、信任度、可靠性、安全性、认证、通信协议、用户界面、监控和日志记录组件,以及持续集成和部署。
本章还为根据特定需求选择正确部署选项提供了逐步指南,例如延迟、可用性、可扩展性、成本、模型硬件、数据隐私和安全需求。我们还探讨了确保模型安全性、信任度和可靠性的一般建议,优化模型延迟,并利用简化部署流程的工具。
使用 ONNX、TensorRT 和 NVIDIA Triton Server 部署语言模型的实际教程展示了加速部署所需的最小工作流程,展示了使用 NVIDIA Triton Server 的加速部署的实例。
通过理解并实施本章提出的策略和最佳实践,您可以成功地在生产环境中部署深度学习模型,并为每个所需组件的最明智选择发挥其全部潜力。要在成功的道路上进一步发展,我们需要确保在部署模型后不要忘记我们的模型,并始终考虑监控我们部署的模型。
在下一章中,我们将深入探讨我们需要考虑的监控许多方面,以确保我们机器学习用例的持续成功。