每个程序员都应该知道的 40 个算法(三)

原文:zh.annas-archive.org/md5/8ddea683d78e7bd756401ec665273969

译者:飞龙

协议:CC BY-NC-SA 4.0

第八章:神经网络算法

各种因素的结合使得人工神经网络(ANNs)成为当今最重要的机器学习技术之一。这些因素包括解决日益复杂的问题的需求,数据的爆炸以及诸如可用的廉价集群等技术的出现,这些技术提供了设计非常复杂算法所需的计算能力。

事实上,这是一个迅速发展的研究领域,负责实现机器人技术、自然语言处理和自动驾驶汽车等领先技术领域所宣称的大部分重大进展。

观察 ANN 的结构,其基本单元是神经元。ANN 的真正优势在于其能够通过将它们组织成分层架构来利用多个神经元的力量。ANN 通过在不同层中将神经元链接在一起来创建分层架构。信号通过这些层传递,并在每个层中以不同的方式进行处理,直到生成最终所需的输出。正如我们将在本章中看到的,ANN 使用的隐藏层充当抽象层,实现了深度学习,这在实现强大的应用程序中被广泛使用,如亚马逊的 Alexa,谷歌的图像搜索和谷歌相册。

本章首先介绍了典型神经网络的主要概念和组件。然后,它介绍了各种类型的神经网络,并解释了这些神经网络中使用的不同类型的激活函数。接下来,详细讨论了反向传播算法,这是训练神经网络最广泛使用的算法。然后,解释了迁移学习技术,可以用来大大简化和部分自动化模型的训练。最后,通过一个真实世界的应用程序案例,探讨了如何使用深度学习来标记欺诈文件。

本章讨论的主要概念如下:

  • 理解人工神经网络

  • ANN 的演变

  • 训练神经网络

  • 工具和框架

  • 迁移学习

  • 案例研究:使用深度学习进行欺诈检测

让我们从人工神经网络的基础知识开始。

理解人工神经网络

受人脑神经元工作的启发,神经网络的概念是由 Frank Rosenblatt 在 1957 年提出的。要充分理解其架构,有助于简要了解人脑中神经元的分层结构。(参考以下图表,了解人脑中的神经元是如何连接在一起的。)

在人脑中,树突充当检测信号的传感器。然后,信号传递给轴突,轴突是神经细胞的一种长而细的突出部分。轴突的功能是将这个信号传递给肌肉、腺体和其他神经元。如下图所示,信号通过称为突触的相互连接的组织传递,然后传递给其他神经元。请注意,通过这种有机管道,信号一直传递,直到它到达目标肌肉或腺体,引起所需的动作。信号通常需要七到八毫秒才能通过神经元链传递并到达目标位置:

受到这种自然的信号处理的建筑杰作的启发,Frank Rosenblatt 设计了一种意味着数字信息可以在层中处理以解决复杂数学问题的技术。他最初设计的神经网络非常简单,看起来类似于线性回归模型。这种简单的神经网络没有任何隐藏层,被命名为感知器。以下图表对其进行了说明:

让我们尝试开发这个感知器的数学表示。在前面的图表中,输入信号显示在左侧。这是输入的加权和,因为每个输入(x[1],x[2]..x[n])都会乘以相应的权重(w[1],w[2]… w[n]),然后求和:

请注意,这是一个二元分类器,因为这个感知器的最终输出取决于聚合器的输出是真还是假(在图表中显示为)。如果聚合器能够从至少一个输入中检测到有效信号,它将产生一个真实的信号。

现在让我们来看看神经网络是如何随着时间的推移而发展的。

人工神经网络的演变

在前面的部分中,我们研究了一个没有任何层的简单神经网络,称为感知器。发现感知器有严重的局限性,并且在 1969 年,马文·明斯基和西摩·帕帕特进行了研究,得出结论感知器无法学习任何复杂的逻辑。

事实上,他们表明即使学习像异或这样简单的逻辑函数也是一种挑战。这导致了对机器学习和神经网络的兴趣下降,开始了一个现在被称为AI 寒冬的时代。世界各地的研究人员不会认真对待人工智能,认为它无法解决任何复杂的问题。

所谓的 AI 寒冬的主要原因之一是当时可用的硬件能力的限制。要么是必要的计算能力不可用,要么是价格昂贵。到了 20 世纪 90 年代末,分布式计算的进步提供了易于获得和负担得起的基础设施,这导致了 AI 寒冬的融化。这种融化重新激发了对人工智能的研究。最终导致了将当前时代变成一个可以称为AI 春天的时代,人们对人工智能和神经网络特别感兴趣。

对于更复杂的问题,研究人员开发了一个称为多层感知器的多层神经网络。多层神经网络有几个不同的层,如下图所示。这些层包括:

  • 输入层

  • 隐藏层

  • 输出层:

深度神经网络是一个具有一个或多个隐藏层的神经网络。深度学习是训练人工神经网络的过程。

一个重要的事情要注意的是神经元是这个网络的基本单元,每个层的神经元都连接到下一层的所有神经元。对于复杂的网络,这些互连的数量激增,我们将探索不牺牲太多质量的不同减少这些互连的方法。

首先,让我们尝试阐明我们要解决的问题。

输入是一个特征向量x,维度为n

我们希望神经网络能够预测值。预测值由ý表示。

从数学上讲,我们想要确定在给定特定输入的情况下,交易是欺诈的概率。换句话说,给定特定的x值,y=1 的概率是多少?从数学上讲,我们可以表示如下:

注意,x是一个n[x]维向量,其中n[x]是输入变量的数量。

这个神经网络有四层。输入和输出之间的层是隐藏层。第一个隐藏层中的神经元数量用表示。各个节点之间的连接由称为权重的参数相乘。训练神经网络就是找到权重的正确值。

让我们看看如何训练神经网络。

训练神经网络

使用给定数据集构建神经网络的过程称为训练神经网络。让我们来看一下典型神经网络的解剖结构。当我们谈论训练神经网络时,我们谈论计算权重的最佳值。训练是通过使用一组示例(训练数据的形式)进行迭代完成的。训练数据中的示例具有不同输入值组合的输出的预期值。神经网络的训练过程与传统模型的训练方式不同(这些在第七章中讨论过,传统监督学习算法)。

理解神经网络的解剖结构

让我们看看神经网络包括什么:

  • 层: 层是神经网络的核心构建模块。每一层都是一个数据处理模块,充当过滤器。它接受一个或多个输入,以某种方式处理它,然后产生一个或多个输出。每当数据通过一层时,它都会经过一个处理阶段,并显示与我们试图回答的业务问题相关的模式。

  • 损失函数: 损失函数提供了在学习过程的各个迭代中使用的反馈信号。损失函数提供了单个示例的偏差。

  • 成本函数: 成本函数是完整示例集上的损失函数。

  • 优化器: 优化器确定损失函数提供的反馈信号将如何被解释。

  • 输入数据: 输入数据是用于训练神经网络的数据。它指定目标变量。

  • 权重: 权重是通过训练网络计算的。权重大致对应于每个输入的重要性。例如,如果特定输入比其他输入更重要,在训练后,它将被赋予更大的权重值,充当乘数。即使对于该重要输入的弱信号也将从大的权重值(充当乘数)中获得力量。因此,权重最终会根据它们的重要性转换每个输入。

  • 激活函数: 值将由不同的权重相乘然后聚合。它们将如何被聚合以及它们的值将如何被解释将由所选择的激活函数的类型确定。

现在让我们来看看神经网络训练的一个非常重要的方面。

在训练神经网络时,我们逐个示例地进行。对于每个示例,我们使用正在训练的模型生成输出。我们计算期望输出和预测输出之间的差异。对于每个单独的示例,这种差异称为损失。在整个训练数据集中,损失被称为成本。随着我们不断训练模型,我们的目标是找到导致最小损失值的权重值。在整个训练过程中,我们不断调整权重的值,直到找到导致最小可能总成本的权重值集。一旦达到最小成本,我们标记模型已经训练完成。

定义梯度下降

训练神经网络模型的目的是找到权重的正确值。我们开始训练神经网络时,权重使用随机或默认值。然后,我们迭代使用优化算法,例如梯度下降,以改变权重,使我们的预测改善。

梯度下降算法的起点是需要通过算法迭代优化的随机权重值。在随后的每次迭代中,算法通过以最小化成本的方式改变权重值来进行。

以下图解释了梯度下降算法的逻辑:

在前面的图中,输入是特征向量X。目标变量的实际值是Y,目标变量的预测值是Y'。我们确定实际值与预测值的偏差。我们更新权重并重复这些步骤,直到成本最小化。

在算法的每次迭代中如何改变权重将取决于以下两个因素:

  • 方向: 要走的方向以获得损失函数的最小值

  • 学习率: 我们选择的方向中变化应该有多大

下面的图表显示了一个简单的迭代过程:

该图显示了通过改变权重,梯度下降试图找到最小成本。学习率和选择的方向将决定要探索的图表上的下一个点。

选择正确的学习率很重要。如果学习率太小,问题可能需要很长时间才能收敛。如果学习率太高,问题将无法收敛。在前面的图中,代表当前解决方案的点将在图表的两条相反线之间不断振荡。

现在,让我们看看如何最小化梯度。只考虑两个变量,xyxy的梯度计算如下:

为了最小化梯度,可以使用以下方法:

while(gradient!=0):
 if (gradient < 0); move right
 if (gradient > 0); move left

该算法也可以用于找到神经网络的最优或接近最优的权重值。

请注意,梯度下降的计算是在整个网络中向后进行的。我们首先计算最后一层的梯度,然后是倒数第二层,然后是倒数第二层之前的层,直到达到第一层。这被称为反向传播,由 Hinton、Williams 和 Rumelhart 于 1985 年引入。

接下来,让我们看看激活函数。

激活函数

激活函数规定了特定神经元的输入如何被处理以生成输出。

如下图所示,神经网络中的每个神经元都有一个激活函数,确定输入将如何被处理:

在前面的图中,我们可以看到激活函数生成的结果传递到输出。激活函数设置了如何解释输入值以生成输出的标准。

对于完全相同的输入值,不同的激活函数将产生不同的输出。在使用神经网络解决问题时,了解如何选择正确的激活函数是很重要的。

现在让我们逐个查看这些激活函数。

阈值函数

最简单的激活函数是阈值函数。阈值函数的输出是二进制的:0 或 1。如果任何输入大于 1,它将生成 1 作为输出。这可以在下图中解释:

请注意,一旦检测到输入加权和中有任何生命迹象,输出(y)就变为 1。这使得阈值激活函数非常敏感。它很容易被输入中的最轻微的信号或噪音错误触发。

Sigmoid

Sigmoid 函数可以被认为是阈值函数的改进。在这里,我们可以控制激活函数的灵敏度。

Sigmoid 函数y定义如下:

可以在 Python 中实现如下:

def sigmoidFunction(z): 
      return 1/ (1+np.exp(-z))

请注意,通过降低激活函数的灵敏度,我们可以减少输入中的故障的影响。请注意,Sigmoid 激活函数的输出仍然是二进制的,即 0 或 1。

修正线性单元(ReLU)

本章介绍的前两个激活函数的输出是二进制的。这意味着它们将取一组输入变量并将它们转换为二进制输出。ReLU 是一个激活函数,它将一组输入变量作为输入,并将它们转换为单一的连续输出。在神经网络中,ReLU 是最流行的激活函数,通常用于隐藏层,我们不希望将连续变量转换为类别变量。

下面的图表总结了 ReLU 激活函数:

请注意,当x≤ 0时,y = 0。这意味着输入中的任何信号为零或小于零都被转换为零输出:

for for

一旦x大于零,它就是x

ReLU 函数是神经网络中最常用的激活函数之一。它可以在 Python 中实现如下:

def ReLU(x): 
if x<0: 
    return 0 
else: 
    return x

现在让我们来看看 Leaky ReLU,它是基于 ReLU 的。

Leaky ReLU

在 ReLU 中,x的负值导致y的值为零。这意味着在这个过程中丢失了一些信息,这使得训练周期变得更长,特别是在训练开始时。Leaky ReLU 激活函数解决了这个问题。对于 Leaky ReLu,以下内容适用:

; for for

下面的图表显示了这一点:

这里,ß是一个小于一的参数。

它可以在 Python 中实现如下:

def leakyReLU(x,beta=0.01):
    if x<0:
        return (beta*x)    
    else:        
        return x

有三种指定ß值的方法:

  • 我们可以指定一个默认值为ß。

  • 我们可以在我们的神经网络中将ß设为一个参数,并让神经网络决定该值(这称为参数化 ReLU)。

  • 我们可以将ß设为一个随机值(这称为随机化 ReLU)。

双曲正切(tanh)

tanh 函数类似于 Sigmoid 函数,但它也能给出负信号。下图说明了这一点:

y函数如下:

可以通过以下 Python 代码实现:

def tanh(x): 
    numerator = 1-np.exp(-2*x) 
    denominator = 1+np.exp(-2*x) 
    return numerator/denominator

现在让我们来看看 softmax 函数。

Softmax

有时,我们需要激活函数的输出不止两个级别。Softmax 是一个可以为输出提供不止两个级别的激活函数。它最适用于多类分类问题。假设我们有n个类。我们有输入值。输入值将类映射如下:

x = {x((1)),x((2)),....x^((n))}

Softmax 是基于概率理论的。Softmax 的第e类的输出概率计算如下:

对于二元分类器,最终层的激活函数将是 Sigmoid,对于多类分类器,它将是 Softmax。

工具和框架

在本节中,我们将详细了解用于实现神经网络的框架和工具。

随着时间的推移,许多不同的框架已经被开发出来来实现神经网络。不同的框架有各自的优势和劣势。在本节中,我们将重点关注具有 TensorFlow 后端的 Keras。

Keras

Keras 是最受欢迎和易于使用的神经网络库之一,用 Python 编写。它考虑到易用性,并提供了实现深度学习的最快方式。Keras 只提供高级模块,并被认为是在模型级别上。

Keras 的后端引擎

Keras 需要一个低级别的深度学习库来执行张量级别的操作。这个低级别的深度学习库称为后端引擎。Keras 的可能后端引擎包括以下内容:

  • TensorFlow (www.tensorflow.org):这是同类框架中最受欢迎的框架,由谷歌开源。

  • Theona (deeplearning.net/software/theano):这是在蒙特利尔大学 MILA 实验室开发的。

  • Microsoft Cognitive ToolkitCNTK):这是由微软开发的。

这种模块化深度学习技术堆栈的格式如下图所示:

这种模块化的深度学习架构的优势在于,Keras 的后端可以在不重写任何代码的情况下进行更改。例如,如果我们发现对于特定任务,TensorFlow 比 Theona 更好,我们可以简单地将后端更改为 TensorFlow,而无需重写任何代码。

深度学习堆栈的低级层

我们刚刚提到的三种后端引擎都可以在 CPU 和 GPU 上运行,使用堆栈的低级层。对于 CPU,使用了一个名为Eigen的张量操作低级库。对于 GPU,TensorFlow 使用了 NVIDIA 的CUDA 深度神经网络cuDNN)库。

定义超参数

如第六章中所讨论的无监督机器学习算法,超参数是在学习过程开始之前选择其值的参数。我们从常识值开始,然后尝试稍后优化它们。对于神经网络,重要的超参数包括:

  • 激活函数

  • 学习率

  • 隐藏层的数量

  • 每个隐藏层中的神经元数量

让我们看看如何使用 Keras 定义模型。

定义 Keras 模型

定义完整的 Keras 模型涉及三个步骤:

  1. 定义层

我们可以用两种可能的方式使用 Keras 构建模型:

  • Sequential API:这允许我们为一系列层构建模型。它用于相对简单的模型,并且通常是构建模型的常规选择:

请注意,在这里,我们创建了三层 - 前两层具有 ReLU 激活函数,第三层具有 softmax 作为激活函数。

  • Functional API:这允许我们为层的非循环图形构建模型。使用 Functional API 可以创建更复杂的模型。

请注意,我们可以使用顺序和功能 API 来定义相同的神经网络。从性能的角度来看,使用哪种方法来定义模型并没有任何区别。

  1. 定义学习过程

在这一步中,我们定义了三件事:

  • 优化器

  • 损失函数

  • 将量化模型质量的指标:

请注意,我们使用model.compile函数来定义优化器、损失函数和指标。

  1. 训练模型

一旦定义了架构,就是训练模型的时候了:

请注意,batch_sizeepochs等参数是可配置的参数,使它们成为超参数。

选择顺序或功能模型

顺序模型将 ANN 创建为简单的层堆叠。顺序模型易于理解和实现,但其简单的架构也有一个主要限制。每一层只连接到一个输入和输出张量。这意味着如果我们的模型在任何隐藏层的输入或输出处有多个输入或多个输出,那么我们不能使用顺序模型。在这种情况下,我们将不得不使用功能模型。

理解 TensorFlow

TensorFlow 是处理神经网络的最流行的库之一。在前面的部分中,我们看到了如何将其用作 Keras 的后端引擎。它是一个开源的高性能库,实际上可以用于任何数值计算。如果我们看一下堆栈,我们可以看到我们可以用高级语言(如 Python 或 C++)编写 TensorFlow 代码,然后由 TensorFlow 分布式执行引擎解释。这使得它对开发人员非常有用和受欢迎。

TensorFlow 的工作方式是创建一个有向图DG)来表示您的计算。连接节点的是边,数学运算的输入和输出。它们也代表数据的数组。

介绍 TensorFlow 的基本概念

让我们简要了解一下 TensorFlow 的概念,比如标量、向量和矩阵。我们知道,在传统数学中,简单的数字,比如三或五,被称为标量。此外,在物理学中,向量是具有大小和方向的东西。在 TensorFlow 中,我们使用向量来表示一维数组。延伸这个概念,二维数组被称为矩阵。对于三维数组,我们使用术语3D 张量。我们使用术语等级来捕捉数据结构的维度。因此,标量是一个等级 0的数据结构,向量是一个等级 1的数据结构,矩阵是一个等级 2的数据结构。这些多维结构被称为张量,并在下图中显示:

在前面的图表中,我们可以看到等级定义了张量的维度。

现在让我们看看另一个参数,shapeshape是一个整数元组,指定每个维度中数组的长度。下图解释了shape的概念:

使用shape和等级,我们可以指定张量的详细信息。

理解张量数学

现在让我们看看使用张量进行不同的数学计算:

  • 让我们定义两个标量,并尝试使用 TensorFlow 进行加法和乘法:

  • 我们可以将它们相加和相乘,并显示结果:

  • 我们还可以通过将两个张量相加来创建一个新的标量张量:

  • 我们还可以执行复杂的张量函数:

理解神经网络的类型

神经网络可以有多种构建方式。如果每一层中的每个神经元都连接到另一层中的每个神经元,那么我们称之为密集或全连接神经网络。让我们看看一些其他形式的神经网络。

卷积神经网络

卷积神经网络CNNs)通常用于分析多媒体数据。为了更多地了解 CNN 如何用于分析基于图像的数据,我们需要掌握以下过程:

  • 卷积

  • 池化

让我们逐一探索它们。

卷积

卷积的过程通过使用另一个较小的图像(也称为过滤器)来处理特定图像中感兴趣的模式。例如,如果我们想要在图像中找到物体的边缘,我们可以使用特定的过滤器对图像进行卷积来得到它们。边缘检测可以帮助我们进行物体检测、物体分类和其他应用。因此,卷积的过程是关于在图像中找到特征和特点。

寻找模式的方法是基于寻找可以在不同数据上重复使用的模式。可重复使用的模式称为过滤器或核。

池化

为了进行机器学习的多媒体数据处理的重要部分是对其进行下采样。这提供了两个好处:

  • 它减少了问题的整体维度,大大减少了训练模型所需的时间。

  • 通过聚合,我们可以提取多媒体数据中不必要的细节,使其更通用并更具代表性。

下采样的执行如下:

请注意,我们已经用一个像素替换了每个四个像素的块,选择了四个像素中的最高值作为该像素的值。这意味着我们已经按四分之一的比例进行了下采样。由于我们选择了每个块中的最大值,这个过程被称为最大池化。我们也可以选择平均值;在那种情况下,它将是平均池化。

循环神经网络

循环神经网络RNNs)是一种特殊类型的神经网络,它们基于循环架构。这就是为什么它们被称为循环。需要注意的重要事情是 RNNs 具有记忆。这意味着它们有能力存储最近迭代的信息。它们被用于分析句子结构以预测句子中的下一个单词等领域。

生成对抗网络

生成对抗网络GANs)是一种生成合成数据的神经网络类型。它们是由 Ian Goodfellow 及其同事于 2014 年创建的。它们可以用来生成从未存在过的人的照片。更重要的是,它们用于生成合成数据以增加训练数据集。

在接下来的部分中,我们将看到什么是迁移学习。

迁移学习

多年来,许多组织、研究团体和开源社区内的个人已经完善了一些使用大量数据进行训练的复杂模型,以供通用用途。在某些情况下,他们已经投入了多年的努力来优化这些模型。一些这些开源模型可以用于以下应用:

  • 视频中的物体检测

  • 图像中的物体检测

  • 音频的转录

  • 文本的情感分析

每当我们开始训练一个新的机器学习模型时,我们要问自己的问题是:我们是否可以简单地定制一个经过充分验证的预训练模型,而不是从头开始?换句话说,我们是否可以将现有模型的学习迁移到我们的自定义模型,以便回答我们的业务问题?如果我们能做到这一点,它将提供三个好处:

  • 我们的模型训练工作将得到一个快速启动。

  • 通过使用经过充分测试和建立的模型,我们的模型整体质量可能会得到提高。

  • 如果我们没有足够的数据来解决我们正在处理的问题,使用通过迁移学习的预训练模型可能会有所帮助。

让我们看两个实际例子,这将是有用的:

  • 在训练机器人时,我们可以首先使用模拟游戏来训练神经网络模型。在那个模拟中,我们可以创建所有那些在现实世界中很难找到的罕见事件。一旦训练完成,我们可以使用迁移学习来训练模型适用于真实世界。

  • 假设我们想要训练一个模型,可以从视频源中分类苹果和 Windows 笔记本电脑。已经有成熟的开源目标检测模型可以准确分类视频源中的各种物体。我们可以使用这些模型作为起点,识别笔记本电脑。一旦我们识别出物体是笔记本电脑,我们可以进一步训练模型区分苹果和 Windows 笔记本电脑。

在下一节中,我们将应用本章涵盖的概念来构建一个欺诈文档分类神经网络。

案例研究-使用深度学习进行欺诈检测

使用机器学习ML)技术识别欺诈文档是一个活跃且具有挑战性的研究领域。研究人员正在调查神经网络的模式识别能力在多大程度上可以用于这个目的。可以使用原始像素而不是手动属性提取器,用于几种深度学习架构结构。

方法论

本节介绍的技术使用了一种称为Siamese 神经网络的神经网络架构,它具有两个共享相同架构和参数的分支。使用 Siamese 神经网络来标记欺诈文档如下图所示:

当需要验证特定文档的真实性时,我们首先基于其布局和类型对文档进行分类,然后将其与预期的模板和模式进行比较。如果偏离超过一定阈值,它被标记为伪造文档;否则,它被视为真实文档。对于关键用例,我们可以添加一个手动流程,用于边界情况,算法无法确定地将文档分类为真实或伪造。

为了比较文档与其预期模板,我们在 Siamese 架构中使用两个相同的 CNN。CNN 具有学习最佳的平移不变局部特征检测器和可以构建对输入图像的几何失真具有鲁棒性的表示的优势。这非常适合我们的问题,因为我们的目标是通过单个网络传递真实和测试文档,然后比较它们的相似性。为了实现这个目标,我们实施以下步骤。

假设我们想要测试一个文档。对于每类文档,我们执行以下步骤:

  1. 获取真实文档的存储图像。我们称之为真实文档。测试文档应该看起来像真实文档。

  2. 真实文档通过神经网络层,创建一个特征向量,这是真实文档模式的数学表示。我们称之为特征向量 1,如前图所示。

  3. 需要测试的文档称为测试文档。我们通过一个类似于用于创建真实文档特征向量的网络来传递这个文档。测试文档的特征向量称为特征向量 2

  4. 我们使用特征向量 1 和特征向量 2 之间的欧氏距离来计算真实文档和测试文档之间的相似度分数。这个相似度分数被称为相似度测量MOS)。MOS 是 0 到 1 之间的数字。较高的数字代表文档之间的距离较小,文档相似的可能性较大。

  5. 如果神经网络计算的相似度分数低于预定义的阈值,我们将文档标记为欺诈。

让我们看看如何使用 Python 实现 Siamese 神经网络:

  1. 首先,让我们导入所需的 Python 包:
import random
import numpy as np
import tensorflow as tf
  1. 接下来,我们将定义将用于处理 Siamese 网络各个分支的神经网络:
def createTemplate():
      return tf.keras.models.Sequential([
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.15),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.15),
        tf.keras.layers.Dense(64, activation='relu'), 
        ])

请注意,为了减少过拟合,我们还指定了0.15的丢失率。

  1. 为了实现 Siamese 网络,我们将使用 MNIST 图像。MNIST 图像非常适合测试我们的方法的有效性。我们的方法包括以每个样本包含两个图像和一个二进制相似度标志的方式准备数据。这个标志是它们来自相同类别的指示器。现在让我们实现名为prepareData的函数,它可以为我们准备数据:
def prepareData(inputs: np.ndarray, labels: np.ndarray):
      classesNumbers = 10
      digitalIdx = [np.where(labels == i)[0] for i in range(classesNumbers)]
      pairs = list()
      labels = list()
      n = min([len(digitalIdx[d]) for d in range(classesNumbers)]) - 1
      for d in range(classesNumbers):
        for i in range(n):
            z1, z2 = digitalIdx[d][i], digitalIdx[d][i + 1]
            pairs += [[inputs[z1], inputs[z2]]]
            inc = random.randrange(1, classesNumbers)
            dn = (d + inc) % classesNumbers
            z1, z2 = digitalIdx[d][i], digitalIdx[dn][i]
            pairs += [[inputs[z1], inputs[z2]]]
            labels += [1, 0] 
      return np.array(pairs), np.array(labels, dtype=np.float32)

注意,prepareData()将导致所有数字的样本数量相等。

  1. 我们现在将准备训练和测试数据集:
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train.astype(np.float32)
x_test = x_test.astype(np.float32)
x_train /= 255
x_test /= 255
input_shape = x_train.shape[1:]
train_pairs, tr_labels = prepareData(x_train, y_train)
test_pairs, test_labels = prepareData(x_test, y_test)
  1. 现在,让我们创建 Siamese 系统的两个部分:
input_a = tf.keras.layers.Input(shape=input_shape)
enconder1 = base_network(input_a)
input_b = tf.keras.layers.Input(shape=input_shape)
enconder2 = base_network(input_b)
  1. 现在,我们将实现相似度度量(MOS),它将量化我们想要比较的两个文档之间的距离:
distance = tf.keras.layers.Lambda( 
    lambda embeddings: tf.keras.backend.abs(embeddings[0] - embeddings[1])) ([enconder1, enconder2])
measureOfSimilarity = tf.keras.layers.Dense(1, activation='sigmoid') (distance)

现在,让我们训练模型。我们将使用 10 个 epochs 来训练这个模型:

请注意,我们使用 10 个 epochs 达到了 97.49%的准确率。增加 epochs 的数量将进一步提高准确度水平。

总结

在本章中,我们首先看了神经网络的细节。我们首先看了神经网络多年来的发展。我们研究了不同类型的神经网络。然后,我们看了神经网络的各种构建模块。我们深入研究了用于训练神经网络的梯度下降算法。我们讨论了各种激活函数,并研究了激活函数在神经网络中的应用。我们还看了迁移学习的概念。最后,我们看了一个实际例子,说明了神经网络如何用于训练可以部署到标记伪造或欺诈文件的机器学习模型。

展望未来,在下一章中,我们将探讨如何将这样的算法用于自然语言处理。我们还将介绍网络嵌入的概念,并将研究循环网络在自然语言处理中的应用。最后,我们还将研究如何实现情感分析。

第九章:自然语言处理算法

本章介绍了自然语言处理NLP)的算法。本章从理论到实践逐步进行。它将首先介绍 NLP 的基础知识,然后介绍基本算法。然后,它将研究最流行的神经网络之一,该网络被广泛用于设计和实施文本数据的重要用例的解决方案。最后,我们将研究 NLP 的局限性,最后学习如何使用 NLP 来训练一个可以预测电影评论极性的机器学习模型。

本章将包括以下部分:

  • 介绍 NLP

  • 基于词袋(BoW)的 NLP

  • 词嵌入介绍

  • 使用递归神经网络进行 NLP

  • 使用 NLP 进行情感分析

  • 案例研究:电影评论情感分析

通过本章结束时,您将了解用于 NLP 的基本技术。您应该能够理解 NLP 如何用于解决一些有趣的现实世界问题。

让我们从基本概念开始。

介绍 NLP

NLP 用于研究形式化和规范化计算机与人类(自然)语言之间的交互。NLP 是一个综合性的学科,涉及使用计算机语言学算法和人机交互技术和方法来处理复杂的非结构化数据。NLP 可以用于各种情况,包括以下情况:

  • 主题识别:发现文本存储库中的主题,并根据发现的主题对存储库中的文档进行分类

  • 情感分析:根据文本中包含的积极或消极情感对文本进行分类

  • 机器翻译:将文本从一种口头人类语言翻译成另一种口头人类语言

  • 文本转语音:将口头语言转换为文本

  • 主观解释:智能地解释问题并利用可用信息回答问题

  • 实体识别:从文本中识别实体(如人、地点或物品)

  • 假新闻检测:根据内容标记假新闻

让我们首先看一些在讨论 NLP 时使用的术语。

理解 NLP 术语

NLP 是一个综合性的学科。在围绕某一领域的文献中,我们会观察到,有时会使用不同的术语来指定相同的事物。我们将从一些与 NLP 相关的基本术语开始。让我们从规范化开始,这是一种基本的 NLP 处理,通常在输入数据上执行。

规范化

规范化是对输入文本数据进行的处理,以提高其在训练机器学习模型的情况下的质量。规范化通常包括以下处理步骤:

  • 将所有文本转换为大写或小写

  • 去除标点符号

  • 去除数字

请注意,尽管通常需要前面的处理步骤,但实际的处理步骤取决于我们想要解决的问题。它们会因用例而异,例如,如果文本中的数字代表了在我们尝试解决的问题的情境中可能具有一些价值的东西,那么我们在规范化阶段可能就不需要从文本中去除数字。

语料库

我们用来解决问题的输入文档组称为语料库。语料库充当 NLP 问题的输入数据。

标记化

当我们使用 NLP 时,第一项工作是将文本分成一个标记列表。这个过程称为标记化。由于目标的不同,生成的标记的粒度也会有所不同,例如,每个标记可以包括以下内容:

  • 一个词

  • 一组单词的组合

  • 一个句子

  • 一个段落

命名实体识别

在 NLP 中,有许多用例需要从非结构化数据中识别特定的单词和数字,这些单词和数字属于预定义的类别,如电话号码、邮政编码、姓名、地点或国家。这用于为非结构化数据提供结构。这个过程称为命名实体识别NER)。

停用词

在单词级别的标记化之后,我们得到了文本中使用的单词列表。其中一些单词是常见单词,预计几乎会出现在每个文档中。这些单词不会为它们出现在的文档提供任何额外的见解。这些单词被称为停用词。它们通常在数据处理阶段被移除。一些停用词的例子是waswethe

情感分析

情感分析,或者称为意见挖掘,是从文本中提取正面或负面情感的过程。

词干提取和词形还原

在文本数据中,大多数单词可能以稍微不同的形式存在。将每个单词减少到其原始形式或词干所属的词族中称为词干提取。它用于根据它们的相似含义对单词进行分组,以减少需要分析的单词总数。基本上,词干提取减少了问题的整体条件性。

例如,{use, used, using, uses} => use。

英语词干提取的最常见算法是波特算法。

词干提取是一个粗糙的过程,可能会导致词尾被截断。这可能导致拼写错误的单词。对于许多用例来说,每个单词只是我们问题空间中的一个级别的标识符,拼写错误的单词并不重要。如果需要正确拼写的单词,那么应该使用词形还原而不是词干提取。

算法缺乏常识。对于人类大脑来说,将类似的单词视为相同是很简单的。对于算法,我们必须引导它并提供分组标准。

从根本上讲,有三种不同的 NLP 实现方法。这三种技术在复杂性方面有所不同,如下所示:

  • 基于词袋模型(BoW-based)的 NLP

  • 传统的 NLP 分类器

  • 使用深度学习进行自然语言处理

NLTK

自然语言工具包NLTK)是 Python 中处理 NLP 任务最广泛使用的包。NLTK 是用于 NLP 的最古老和最流行的 Python 库之一。NLTK 非常好,因为它基本上为构建任何 NLP 流程提供了一个起点,它为您提供了基本工具,然后您可以将它们链接在一起以实现您的目标,而不是从头开始构建所有这些工具。许多工具都打包到了 NLTK 中,在下一节中,我们将下载该包并探索其中的一些工具。

让我们来看看基于词袋模型的 NLP。

基于词袋模型的 NLP

将输入文本表示为一组标记的过程称为基于词袋模型的处理。使用词袋模型的缺点是我们丢弃了大部分语法和标记化,这有时会导致丢失单词的上下文。在词袋模型的方法中,我们首先量化要分析的每个文档中每个单词的重要性。

从根本上讲,有三种不同的方法来量化每个文档中单词的重要性:

  • 二进制:如果单词出现在文本中,则特征的值为 1,否则为 0。

  • 计数:特征将以单词在文本中出现的次数作为其值,否则为 0。

  • 词项频率/逆文档频率:特征的值将是单个文档中单词的独特程度与整个文档语料库中单词的独特程度的比率。显然,对于常见单词,如 the、in 等(称为停用词),词项频率-逆文档频率TF-IDF)得分将很低。对于更独特的单词,例如领域特定术语,得分将更高。

请注意,通过使用词袋模型,我们丢失了信息——即文本中单词的顺序。这通常有效,但可能会导致准确性降低。

让我们看一个具体的例子。我们将训练一个模型,可以将餐厅的评论分类为负面或正面。输入文件是一个结构化文件,其中评论将被分类为正面或负面。

为此,让我们首先处理输入数据。

处理步骤在下图中定义:

让我们通过以下步骤实现这个处理流程:

  1. 首先,让我们导入我们需要的包:
import numpy as np
import pandas as pd
  1. 然后我们从CSV文件中导入数据集:

  1. 接下来,我们清理数据:
# Cleaning the texts
import re
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer
corpus = []
for i in range(0, 1000):
    review = re.sub('[^a-zA-Z]', ' ', dataset['Review'][i])
    review = review.lower()
    review = review.split()
    ps = PorterStemmer()
    review = [ps.stem(word) for word in review if not word in set(stopwords.words('english'))]
    review = ' '.join(review)
    corpus.append(review)
  1. 现在让我们定义特征(用y表示)和标签(用X表示):
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(max_features = 1500)
X = cv.fit_transform(corpus).toarray()
y = dataset.iloc[:, 1].values
  1. 让我们将数据分成测试数据和训练数据:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20, random_state = 0)
  1. 对于训练模型,我们使用朴素贝叶斯算法:
from sklearn.naive_bayes import GaussianNB
classifier = GaussianNB()
classifier.fit(X_train, y_train)
  1. 让我们预测测试集的结果:
y_pred = classifier.predict(X_test)
  1. 混淆矩阵如下所示:

通过观察混淆矩阵,我们可以估计误分类情况。

词嵌入简介

在前面的部分中,我们学习了如何使用词袋模型作为输入文本数据的抽象来执行 NLP。NLP 的一个主要进展是我们能够以密集向量的形式创建单词的有意义的数值表示能力。这种技术称为词嵌入。Yoshua Bengio 首次在他的论文《神经概率语言模型》中引入了这个术语。NLP 问题中的每个词都可以被视为一个分类对象。将每个词映射到表示为向量的数字列表称为词嵌入。换句话说,用于将单词转换为实数的方法称为词嵌入。嵌入的一个区别特征是它使用密集向量,而不是使用传统方法使用稀疏矩阵向量。

使用词袋模型进行 NLP 存在两个基本问题:

  • 语义上下文的丢失:当我们对数据进行标记化时,它的上下文就丢失了。一个词可能根据它在句子中的使用位置有不同的含义;当解释复杂的人类表达时,比如幽默或讽刺,这变得更加重要。

  • 稀疏输入:当我们进行标记化时,每个单词都成为一个特征。正如我们在前面的例子中看到的,每个单词都是一个特征。这导致了稀疏的数据结构。

一个词的邻域

如何向算法呈现文本数据(特别是单词或词元)的关键见解来自语言学。在词嵌入中,我们关注每个词的邻域,并用它来确定其含义和重要性。一个词的邻域是围绕特定词的一组词。一个词的上下文是由它的邻域决定的。

请注意,在词袋模型中,一个词失去了它的上下文,因为它的上下文来自它所在的邻域。

词嵌入的特性

良好的词嵌入具有以下四个特性:

  • 它们是密集的:实际上,嵌入本质上是因子模型。因此,嵌入向量的每个组件代表一个(潜在)特征的数量。通常我们不知道该特征代表什么;但是,我们将有非常少的(如果有的话)零值,这将导致稀疏输入。

  • 它们是低维的:嵌入具有预定义的维度(作为超参数选择)。我们之前看到,在 BoW 表示中,我们需要为每个单词输入|V|,因此输入的总大小为|V| * n,其中n是我们用作输入的单词数。使用单词嵌入,我们的输入大小将是d * n,其中d通常在 50 到 300 之间。考虑到大型文本语料库通常远大于 300 个单词,这意味着我们在输入大小上有很大的节省,我们看到这可能导致更小的数据实例总数的更高准确性。

  • 它们嵌入领域语义:这个属性可能是最令人惊讶的,但也是最有用的。当正确训练时,嵌入会学习关于其领域的含义。

  • 易于泛化:最后,网络嵌入能够捕捉到一般的抽象模式——例如,我们可以对(嵌入的)猫、鹿、狗等进行训练,模型将理解我们指的是动物。请注意,模型从未接受过对羊的训练,但模型仍然会正确分类它。通过使用嵌入,我们可以期望得到正确的答案。

现在让我们探讨一下,我们如何使用 RNN 进行自然语言处理。

使用 RNN 进行 NLP

RNN 是一个具有反馈的传统前馈网络。对 RNN 的一种简单思考方式是,它是一个带有状态的神经网络。RNN 可用于任何类型的数据,用于生成和预测各种数据序列。训练 RNN 模型是关于构建这些数据序列。RNN 可用于文本数据,因为句子只是单词序列。当我们将 RNN 用于 NLP 时,我们可以用它来进行以下操作:

  • 在输入时预测下一个单词

  • 生成新的文本,遵循文本中已经使用的风格:

还记得导致它们正确预测的单词组合吗?RNN 的学习过程是基于语料库中的文本。它们通过减少预测的下一个单词和实际的下一个单词之间的错误来进行训练。

使用 NLP 进行情感分析

本节介绍的方法是基于对分类高速流推文的使用情况。手头的任务是提取关于所选主题的推文中嵌入的情绪。情感分类实时量化每条推文中的极性,然后聚合所有推文的总情感,以捕捉关于所选主题的整体情感。为了应对 Twitter 流数据的内容和行为带来的挑战,并有效地执行实时分析,我们使用 NLP 使用训练过的分类器。然后将训练过的分类器插入 Twitter 流中,以确定每条推文的极性(积极、消极或中性),然后聚合并确定关于某一主题的所有推文的整体极性。让我们一步一步地看看这是如何完成的。

首先,我们必须训练分类器。为了训练分类器,我们需要一个已经准备好的数据集,其中包含有历史的 Twitter 数据,并且遵循实时数据的模式和趋势。因此,我们使用了来自网站www.sentiment140.com的数据集,该数据集带有一个人工标记的语料库(基于该分析的大量文本集合),其中包含超过 160 万条推文。该数据集中的推文已经被标记为三种极性之一:零表示负面,两表示中性,四表示正面。除了推文文本之外,语料库还提供了推文 ID、日期、标志和推文用户。现在让我们看看在训练分类器之前对实时推文执行的每个操作:

  1. 首先将推文分割成称为标记的单词(标记化)。

  2. 标记化的输出创建了一个 BoW,其中包含文本中的单个单词。

  3. 这些推文进一步通过去除数字、标点和停用词(停用词去除)进行过滤。停用词是非常常见的词,如isamarethe。由于它们没有额外的信息,这些词被移除。

  4. 此外,非字母字符,如#**@和数字,使用模式匹配进行删除,因为它们在情感分析的情况下没有相关性。正则表达式用于仅匹配字母字符,其余字符将被忽略。这有助于减少 Twitter 流的混乱。

  5. 先前阶段的结果被用于词干处理阶段。在这个阶段,派生词被减少到它们的词根-例如,像fish这样的词与fishingfishes具有相同的词根。为此,我们使用标准 NLP 库,它提供各种算法,如 Porter 词干处理。

  6. 一旦数据被处理,它被转换成一个称为术语文档矩阵TDM)的结构。TDM 表示过滤后语料库中每个词的术语和频率。

  7. 从 TDM 中,推文到达训练过的分类器(因为它经过训练,可以处理推文),它计算每个词的情感极性重要性SPI),这是一个从-5 到+5 的数字。正负号指定了该特定词所代表的情绪类型,其大小表示情感的强度。这意味着推文可以被分类为正面或负面(参考下图)。一旦我们计算了个别推文的极性,我们将它们的总体 SPI 相加,以找到来源的聚合情感-例如,总体极性大于一表示我们观察时间内推文的聚合情感是积极的。

为了获取实时原始推文,我们使用 Scala 库Twitter4J,这是一个提供实时 Twitter 流 API 包的 Java 库。该 API 要求用户在 Twitter 上注册开发者帐户并填写一些认证参数。该 API 允许您获取随机推文或使用选择的关键词过滤推文。我们使用过滤器来检索与我们选择的关键词相关的推文。

总体架构如下图所示:

情感分析有各种应用。它可以用来分类客户的反馈。政府可以利用社交媒体极性分析来找到他们政策的有效性。它还可以量化各种广告活动的成功。

在接下来的部分,我们将学习如何实际应用情感分析来预测电影评论的情感。

案例研究:电影评论情感分析

让我们使用 NLP 进行电影评论情感分析。为此,我们将使用一些开放的电影评论数据,可在www.cs.cornell.edu/people/pabo/movie-review-data/上找到:

  1. 首先,我们将导入包含电影评论的数据集:
import numpy as np
import pandas as pd
  1. 现在,让我们加载电影数据并打印前几行以观察其结构。
df=pd.read_csv("moviereviews.tsv",sep='\t')
df.head()

请注意数据集有2000条电影评论。其中一半是负面的,一半是正面的。

  1. 现在,让我们开始准备数据集以训练模型。首先,让我们删除数据中的任何缺失值
df.dropna(inplace=True)
  1. 现在我们需要移除空格。空格不是空的,但需要被移除。为此,我们需要遍历输入DataFrame中的每一行。我们将使用.itertuples()来访问每个字段:
blanks=[] 

for i,lb,rv in df.itertuples():
    if rv.isspace():
        blanks.append(i)
df.drop(blanks,inplace=True)      

请注意,我们已经使用ilbrv来索引、标签和评论列。

让我们将数据分割成测试和训练数据集:

  1. 第一步是指定特征和标签,然后将数据分割成训练集和测试集:
from sklearn.model_selection import train_test_split

X = df['review']
y = df['label']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

现在我们有测试和训练数据集。

  1. 现在让我们将数据集分成训练集和测试集:
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB

# Naïve Bayes:
text_clf_nb = Pipeline([('tfidf', TfidfVectorizer()),
                     ('clf', MultinomialNB()),
])

请注意,我们正在使用tfidf来量化集合中数据点的重要性。

接下来,让我们使用朴素贝叶斯算法来训练模型,然后测试训练好的模型。

让我们按照以下步骤来训练模型:

  1. 现在让我们使用我们创建的测试和训练数据集来训练模型:
text_clf_nb.fit(X_train, y_train)
  1. 让我们运行预测并分析结果:
# Form a prediction set
predictions = text_clf_nb.predict(X_test)

让我们通过打印混淆矩阵来查看模型的性能。我们还将查看精确度召回率F1 分数准确度

这些性能指标为我们提供了预测质量的度量。准确率为 0.78,现在我们已经成功训练了一个可以预测特定电影评论类型的模型。

摘要

在本章中,我们讨论了与自然语言处理相关的算法。首先,我们研究了与自然语言处理相关的术语。接下来,我们研究了实施自然语言处理策略的 BoW 方法。然后,我们研究了词嵌入的概念以及在自然语言处理中使用神经网络。最后,我们看了一个实际的例子,我们在这一章中使用了开发的概念来根据电影评论的文本来预测情感。通过学习本章内容,用户应该能够将自然语言处理用于文本分类和情感分析。

在下一章中,我们将研究推荐引擎。我们将研究不同类型的推荐引擎以及它们如何用于解决一些现实世界的问题。

第十章:推荐引擎

推荐引擎是利用用户偏好和产品详情提供明智建议的一种方式。推荐引擎的目标是了解一组项目之间的相似性模式和/或制定用户和项目之间的互动。

本章首先介绍了推荐引擎的基础知识。然后,讨论了各种类型的推荐引擎。接下来,本章讨论了推荐引擎如何用于向不同用户建议项目和产品以及推荐引擎的各种限制。最后,我们将学习如何使用推荐引擎解决现实问题。

本章讨论了以下概念:

  • 推荐系统的介绍

  • 推荐引擎的类型

  • 了解推荐系统的限制

  • 实际应用领域

  • 一个实际的例子——创建一个推荐引擎向订阅者推荐电影

在本章结束时,您应该能够理解如何使用推荐引擎根据一些偏好标准建议各种项目。

让我们从推荐引擎的背景概念开始。

推荐系统的介绍

推荐系统代表研究人员最初开发的方法,用于预测用户最有可能感兴趣的项目。推荐系统在给出关于项目的个性化建议方面的能力使其可能是在线购买世界中最重要的技术。

在电子商务应用中,推荐引擎使用复杂的算法来改善购物体验,允许服务提供商根据用户的偏好定制产品。

2009 年,Netflix 向任何能够通过超过 10%改进其现有推荐引擎(Cinematch)的算法提供 100 万美元的奖金。奖金被 BellKor 的 Pragmatic Chaos 团队赢得。

推荐引擎的类型

通常有三种不同类型的推荐引擎:

  • 基于内容的推荐引擎

  • 协同过滤引擎

  • 混合推荐引擎

基于内容的推荐引擎

基于内容的推荐引擎的基本思想是建议与用户先前表现出兴趣的项目相似的项目。基于内容的推荐引擎的有效性取决于我们量化项目与其他项目的相似性的能力。

让我们看看下面的图表。如果用户 1已阅读文档 1,那么我们可以向用户推荐与文档 1相似的文档 2

现在的问题是如何确定哪些项目彼此相似。让我们看看找到不同项目之间相似性的几种方法。

查找非结构化文档之间的相似性

确定不同文档之间相似性的一种方法是首先处理输入文档。处理非结构化文档后得到的数据结构称为术语文档矩阵TDM),如下图所示:

TDM 具有所有术语的词汇表作为行,所有文档作为列。它可以用于根据所选的距离度量确定哪些文档与其他文档相似。例如,Google 新闻根据与用户已经表现出兴趣的新闻相似性向用户推荐新闻。

一旦我们有了 TDM,有两种方法可以量化文档之间的相似性:

  • 使用频率计数:这意味着我们假设一个词的重要性与每个词的频率成正比。这是计算重要性的最简单方法。

  • 使用 TFIDF词频-逆文档频率的缩写):这是一个计算每个词在我们试图解决的问题的上下文中重要性的数字。它是两个术语的乘积:

  • 词频TF):这是一个词或术语在文档中出现的次数。词频直接与一个词的重要性相关联。

  • 逆文档频率IDF):首先,文档频率DF)是包含我们搜索的术语的文档数量。作为 DF 的相反,IDF 给出了一个词所代表的独特性的度量,并将其与其重要性相关联。

  • 由于 TF 和 IDF 都量化了我们试图解决的问题的上下文中一个词的重要性,它们的组合 TF-IDF 是每个词的重要性的一个很好的度量,是使用简单频率计数的更复杂的替代方法。

使用共现矩阵

这种方法基于这样的假设:如果两个物品大多数情况下一起购买,那么它们很可能是相似的,或者至少属于通常一起购买的物品类别。

例如,如果人们大多数情况下一起使用剃须膏和剃刀,那么如果有人买了剃刀,建议他也买剃须膏是有道理的。

让我们分析这四个用户的历史购买模式:

剃刀 苹果 剃须膏 自行车 鹰嘴豆泥
迈克 1 1 1 0 1
泰勒 1 0 1 1 1
埃琳娜 0 0 0 1 0
阿明 1 0 1 0 0

这将创建以下共现矩阵:

剃刀 苹果 剃须膏 自行车 鹰嘴豆泥
剃刀 - 1 3 1 1
苹果 1 - 1 0 1
剃须膏 3 1 - 1 2
自行车 1 0 1 - 1
鹰嘴豆泥 1 1 2 1 -

前述共现矩阵总结了一起购买两件物品的可能性。让我们看看如何使用它。

协同过滤推荐引擎

协同过滤的推荐是基于用户的历史购买模式的分析。基本假设是,如果两个用户对大多数相同的物品表现出兴趣,我们可以将两个用户都归类为相似。换句话说,我们可以假设以下内容:

  • 如果两个用户的购买历史重叠超过阈值,我们可以将它们归类为相似用户。

  • 查看相似用户的历史,购买历史中不重叠的物品将成为协同过滤推荐的基础。

例如,让我们看一个具体的例子。我们有两个用户,迈克埃琳娜,如下图所示:

请注意以下内容:

  • 迈克和埃琳娜都对文档 1文档 2表现出了完全相同的兴趣。

  • 根据他们相似的历史模式,我们可以将他们两个都归类为相似用户。

  • 如果埃琳娜现在阅读文档 3,那么我们也可以建议迈克阅读文档 3

请注意,根据用户历史记录向用户推荐物品的策略并不总是有效的。

假设埃琳娜和迈克对文档 1都表现出了兴趣,这是关于摄影的(因为他们对摄影有共同的爱好)。此外,他们两个都对文档 2表现出了兴趣,这是关于云计算的,同样是因为他们对这个主题有兴趣。根据协同过滤,我们将他们归类为相似用户。现在埃琳娜开始阅读文档 3,这是一本关于女性时尚的杂志。如果我们遵循协同过滤算法,我们会建议迈克也阅读,而他可能对此并不感兴趣。

回到 2012 年,美国超市 Target 正在尝试使用协同过滤为购买者推荐产品。该算法根据他们的档案将一个父亲归类为他的十几岁的女儿。结果,Target 最终向父亲发送了一个关于尿布、婴儿奶粉和婴儿床的折扣券。他并不知道他女儿怀孕了。

请注意,协同过滤算法不依赖于任何其他信息,是一种独立的算法,基于用户的变化行为和协同推荐。

混合推荐引擎

到目前为止,我们已经讨论了基于内容和基于协同过滤的推荐引擎。这两种类型的推荐引擎可以结合起来创建混合推荐引擎。为此,我们按照以下步骤进行:

  • 生成物品的相似矩阵。

  • 生成用户的偏好矩阵。

  • 生成推荐。

让我们逐步了解这些步骤。

生成物品的相似矩阵

在混合推荐中,我们首先通过使用基于内容的推荐创建物品的相似矩阵。这可以通过使用共现矩阵或使用任何距离度量来量化物品之间的相似性来实现。

假设我们目前有五种物品。使用基于内容的推荐,我们生成一个捕捉物品之间相似性的矩阵,看起来像这样:

让我们看看如何将这个相似矩阵与偏好矩阵结合起来生成推荐。

生成用户的参考向量。

基于系统中每个用户的历史,我们将产生一个捕捉这些用户兴趣的偏好向量。

假设我们想为名为KentStreetOnline的在线商店生成推荐,该商店销售 100 种独特的物品。KentStreetOnline 很受欢迎,拥有 100 万活跃订阅者。重要的是要注意,我们只需要生成一个 100x100 维度的相似矩阵。我们还需要为每个用户生成一个偏好向量;这意味着我们需要为 100 万用户中的每一个生成 100 万个偏好向量。

性能向量的每个条目表示对项目的偏好。第一行的值表示项目 1的偏好权重为4。例如,第二行的值表示对项目 2没有偏好。

以下是图形显示:

现在,让我们看看如何基于相似矩阵 S 和用户偏好矩阵 U 生成推荐。

生成推荐

为了进行推荐,我们可以将这些矩阵相乘。用户更有可能对经常与他们给出高评分的物品共现的物品感兴趣:

Matrix[S] x Matrix[U] = Matrix[R]

这个计算在以下图表中显示:

为每个用户生成一个单独的结果矩阵。推荐矩阵Matrix[R]中的数字量化了用户对每个物品的预测兴趣。例如,在结果矩阵中,第四个物品的数字最高,为 58。因此,这个物品对这个特定用户来说是高度推荐的。

现在,让我们来看看不同推荐系统的局限性。

了解推荐系统的局限性

推荐引擎使用预测算法向一群用户建议推荐。这是一种强大的技术,但我们应该意识到它的局限性。让我们来看看推荐系统的各种局限性。

冷启动问题

显然,为了使协同过滤起作用,我们需要有关用户偏好的历史数据。对于新用户,我们可能没有任何数据,因此我们的用户相似性算法将基于可能不准确的假设。对于基于内容的推荐,我们可能不会立即获得有关新物品的详细信息。需要有关物品和用户的数据来生成高质量推荐的要求被称为冷启动问题

元数据要求

基于内容的方法需要明确的物品描述来衡量相似性。这种明确的详细描述可能不可用,影响预测的质量。

数据稀疏问题

在大量物品中,用户只会对少数物品进行评分,导致非常稀疏的用户/物品评分矩阵。

亚马逊有大约十亿用户和十亿物品。据说亚马逊的推荐引擎是世界上数据最稀疏的推荐引擎。

由于社交影响而产生的偏见

社交影响在推荐系统中可以起到重要作用。社交关系可以被视为影响用户偏好的因素。朋友倾向于购买类似的物品并给出类似的评分。

有限的数据

有限数量的评论使得推荐系统难以准确衡量用户之间的相似性。

实际应用领域

让我们看看推荐系统在实际世界中的应用:

  • Netflix 上的电影有三分之二是推荐的。

  • 亚马逊的三分之一的销售额来自推荐。

  • 在 Google 新闻上,推荐引擎产生的点击率增加了 38%。

  • 尝试预测用户对物品的偏好是基于其他物品的过去评分。

  • 他们可以根据学生的需求和偏好为大学生推荐课程。

  • 他们可以在在线求职门户网站上将简历与工作匹配。

现在,让我们尝试使用推荐引擎来解决一个现实世界的问题。

实际示例 - 创建推荐引擎

让我们构建一个可以向一群用户推荐电影的推荐引擎。我们将使用明尼苏达大学 GroupLens 研究小组收集的数据。

按照以下步骤:

  1. 首先,我们将导入相关的包:
import pandas as pd 
import numpy as np
  1. 现在,让我们导入user_iditem_id数据集:
df_reviews = pd.read_csv('reviews.csv')
df_movie_titles = pd.read_csv('movies.csv',index_col=False)
  1. 我们通过电影 ID 合并了这两个 DataFrame:
df = pd.merge(df_users, df_movie_titles, on='movieId')

在运行上述代码后,df DataFrame 的标题如下:

列的详细信息如下:

    • userid:每个用户的唯一 ID
  • 电影 id:每部电影的唯一 ID

  • rating:每部电影的评分从 1 到 5

  • timestamp:电影被评分的时间戳

  • title:电影的标题

  • genres:电影的流派

  1. 为了了解输入数据的摘要趋势,让我们使用groupbytitlerating列计算每部电影的平均评分和评分次数:

  1. 现在让我们为推荐引擎准备数据。为此,我们将把数据集转换为一个具有以下特征的矩阵:
    • 电影标题将成为列。
  • User_id将成为索引。

  • 评分将是值。

我们将使用 DataFrame 的pivot_table函数来完成它:

movie_matrix = df.pivot_table(index='userId', columns='title', values='rating')

请注意,上述代码将生成一个非常稀疏的矩阵。

  1. 现在,让我们使用我们创建的推荐矩阵来推荐电影。为此,让我们考虑一个特定的用户,他观看了电影Avatar (2009)。首先,我们将找到所有对Avatar (2009)表现出兴趣的用户:
Avatar_user_rating = movie_matrix['Avatar (2009)']
Avatar_user_rating = Avatar_user_rating.dropna()
Avatar_user_rating.head()
  1. 现在,让我们尝试推荐与Avatar (2009)相关的电影。为此,我们将计算Avatar_user_rating DataFrame 与movie_matrix的相关性,如下所示:
similar_to_Avatar=movie_matrix.corrwith(Avatar_user_rating)
corr_Avatar = pd.DataFrame(similar_to_Avatar, columns=['correlation'])
corr_Avatar.dropna(inplace=True)
corr_Avatar = corr_Avatar.join(df_ratings['number_of_ratings'])
corr_Avatar.head()

这会产生以下输出:

这意味着我们可以将这些电影作为用户的推荐。

总结

在本章中,我们学习了推荐引擎。我们研究了根据我们试图解决的问题选择合适的推荐引擎。我们还研究了如何准备数据以创建相似性矩阵,以供推荐引擎使用。我们还学习了推荐引擎如何用于解决实际问题,比如根据用户过去的模式推荐电影。

在下一章中,我们将专注于用于理解和处理数据的算法。

第三部分:高级主题

正如名称所示,本节将涉及算法的更高级概念。密码学和大规模算法是本节的重点亮点。本节的最后一章,也是本书的最后一章,探讨了在实施这些算法时应该牢记的实际考虑因素。本节包括的章节有:

  • 第十一章,数据算法

  • 第十二章,密码学

  • 第十三章,大规模算法

  • 第十四章,实际考虑因素

第十一章:数据算法

本章讨论了数据中心算法的三个方面:存储、流式处理和压缩。本章首先简要概述了数据中心算法,然后讨论了可以用于数据存储的各种策略。接下来,描述了如何将算法应用于流式数据,然后讨论了压缩数据的不同方法。最后,我们将学习如何使用本章中开发的概念来使用最先进的传感器网络监测高速公路上行驶车辆的速度。

通过本章的学习,您应该能够理解设计各种数据中心算法所涉及的概念和权衡。

本章讨论了以下概念:

  • 数据分类

  • 数据存储算法

  • 如何使用算法来压缩数据

  • 如何使用算法来流式处理数据

让我们首先介绍基本概念。

数据算法简介

无论我们是否意识到,我们生活在一个大数据时代。只需了解一下不断产生的数据量,看一下谷歌在 2019 年发布的一些数字就可以了。众所周知,Google Photos 是谷歌创建的存储照片的多媒体库。2019 年,平均每天有 12 亿张照片和视频上传到 Google Photos。此外,每天平均有 400 小时的视频(相当于 1 PB 的数据)上传到 YouTube。我们可以肯定地说,正在产生的数据量简直是爆炸式增长。

当前对数据驱动算法的兴趣是因为数据包含有价值的信息和模式。如果以正确的方式使用,数据可以成为政策制定、营销、治理和趋势分析的基础。

由于处理数据的算法变得越来越重要,有明显的原因。设计能够处理数据的算法是一个活跃的研究领域。毫无疑问,探索最佳利用数据以提供一些可量化的好处是全世界各种组织、企业和政府的关注焦点。但原始形式的数据很少有用。要从原始数据中挖掘信息,需要对其进行处理、准备和分析。

为此,我们首先需要将其存储在某个地方。高效存储数据的方法变得越来越重要。请注意,由于单节点系统的物理存储限制,大数据只能存储在由高速通信链路连接的多个节点组成的分布式存储中。因此,对于学习数据算法来说,首先看不同的数据存储算法是有意义的。

首先,让我们将数据分类为各种类别。

数据分类

让我们来看看在设计数据算法的背景下如何对数据进行分类。正如在第二章中讨论的那样,算法中使用的数据结构,量化数据的容量、多样性和速度可以用来对其进行分类。这种分类可以成为设计数据算法的基础,用于其存储和处理。

让我们在数据算法的背景下逐个查看这些特征:

  • 容量 量化了需要在算法中存储和处理的数据量。随着容量的增加,任务变得数据密集,并需要足够的资源来存储、缓存和处理数据。大数据是一个模糊定义的术语,用来描述无法由单个节点处理的大量数据。

  • 速度定义了新数据生成的速率。通常,高速数据被称为“热数据”或“热流”,低速数据被称为“冷流”或简单地称为“冷数据”。在许多应用中,数据将是热流和冷流的混合,首先需要准备并合并到一个表中,然后才能与算法一起使用。

  • 多样性指的是需要将不同类型的结构化和非结构化数据合并到一个表中,然后才能被算法使用。

下一节将帮助我们理解涉及的权衡,并在设计存储算法时提出各种设计选择。

数据存储算法介绍

可靠和高效的数据存储库是分布式系统的核心。如果这个数据存储库是为分析而创建的,那么它也被称为数据湖。数据存储库将来自不同领域的数据汇集到一个地方。让我们首先了解分布式存储库中与数据存储相关的不同问题。

了解数据存储策略

在数字计算的最初几年,设计数据存储库的常规方式是使用单节点架构。随着数据集的不断增大,数据的分布式存储现在已经成为主流。在分布式环境中存储数据的正确策略取决于数据的类型、预期的使用模式以及其非功能性需求。为了进一步分析分布式数据存储的需求,让我们从一致性可用性分区容忍(CAP)定理开始,这为我们提供了制定分布系统数据存储策略的基础。

介绍 CAP 定理

1998 年,Eric Brewer 提出了一个定理,后来被称为 CAP 定理。它突出了设计分布式存储系统涉及的各种权衡。

为了理解 CAP 定理,首先让我们定义分布式存储系统的以下三个特性:一致性、可用性和分区容忍。CAP 实际上是由这三个特性组成的首字母缩写:

  • 一致性(简称 C):分布式存储由各种节点组成。这些节点中的任何一个都可以用于读取、写入或更新数据存储库中的记录。一致性保证在某个时间t[1],无论我们使用哪个节点来读取数据,我们都会得到相同的结果。每个操作要么返回跨分布式存储库一致的最新数据,要么给出错误消息。

  • 可用性(简称 A):可用性保证分布式存储系统中的任何节点都能立即处理请求,无论是否具有一致性。

  • 分区容忍(简称 P):在分布式系统中,多个节点通过通信网络连接。分区容忍保证在一小部分节点(一个或多个)之间的通信失败的情况下,系统仍然可以正常运行。请注意,为了保证分区容忍,数据需要在足够数量的节点上复制。

使用这三种特性,CAP 定理仔细总结了分布系统的架构和设计中涉及的权衡。具体来说,CAP 定理规定,在存储系统中,我们只能拥有以下两种特性中的两种:一致性或 C,可用性或 A,以及分区容忍性或 P。

这在以下图表中显示:

CAP 定理也意味着我们可以有三种类型的分布式存储系统:

  • CA 系统(实现一致性-可用性)

  • AP 系统(实现可用性-分区容忍)

  • CP 系统(实现一致性-分区容忍)

让我们依次来看看它们。

CA 系统

传统的单节点系统是 CA 系统。这是因为如果我们没有分布式系统,那么我们就不需要担心分区容忍性。在这种情况下,我们可以拥有既有一致性又有可用性的系统,即 CA 系统。

传统的单节点数据库,如 Oracle 或 MySQL,都是 CA 系统的例子。

AP 系统

AP 系统是为可用性调整的分布式存储系统。设计为高度响应的系统,它们可以牺牲一致性,以适应高速数据。这意味着这些是设计为立即处理用户请求的分布式存储系统。典型的用户请求是读取或写入快速变化的数据。典型的 AP 系统用于实时监控系统,如传感器网络。

高速分布式数据库,如 Cassandra,是 AP 系统的良好例子。

让我们看看 AP 系统可以在哪些地方使用。如果加拿大交通部想要通过在渥太华一条高速公路上安装的传感器网络监控交通情况,建议使用 AP 系统来实现分布式数据存储。

CP 系统

CP 系统具有一致性和分区容忍性。这意味着这些是调整为在读取过程可以获取值之前保证一致性的分布式存储系统。

CP 系统的典型用例是当我们想要以 JSON 格式存储文档文件时。像 MongoDB 这样的文档数据存储系统是为分布式环境中的一致性而调整的 CP 系统。

分布式数据存储越来越成为现代 IT 基础设施中最重要的部分。分布式数据存储应该根据数据的特性和我们想要解决的问题的要求进行精心设计。将数据存储分类为 CA、AP 和 CP 系统有助于我们理解在设计数据存储系统时涉及的各种权衡。

现在,让我们来看看流数据算法。

呈现流数据算法

数据可以被分类为有界或无界。有界数据是静态数据,通常通过批处理过程处理。流式处理基本上是对无界数据进行数据处理。让我们看一个例子。假设我们正在分析银行的欺诈交易。如果我们想要查找 7 天前的欺诈交易,我们必须查看静态数据;这是一个批处理的例子。

另一方面,如果我们想要实时检测欺诈,那就是流式处理的一个例子。因此,流数据算法是处理数据流的算法。其基本思想是将输入数据流分成批次,然后由处理节点处理。流算法需要具有容错能力,并且应该能够处理数据的传入速度。随着对实时趋势分析的需求增加,对流处理的需求也在这些天增加。请注意,为了使流处理工作,数据必须快速处理,而在设计算法时,这一点必须始终牢记在心。

流应用

流数据及其有意义的利用有许多应用。一些应用如下:

  • 欺诈检测

  • 系统监控

  • 智能订单路由

  • 实时仪表板

  • 高速公路上的交通传感器

  • 信用卡交易

  • 用户在多用户在线游戏中移动

现在,让我们看看如何使用 Python 实现流处理。

呈现数据压缩算法

数据压缩算法参与了减小数据大小的过程。

在这一章中,我们将深入研究一种名为无损压缩算法的特定数据压缩算法。

无损压缩算法

这些算法能够以一种可以在解压缩时不丢失信息的方式压缩数据。当重要的是在解压缩后检索到确切的原始文件时,它们被使用。无损压缩算法的典型用途如下:

  • 压缩文件

  • 压缩和打包源代码和可执行文件

  • 将大量小文件转换为少量大文件

了解无损压缩的基本技术

数据压缩是基于这样一个原则,即大多数数据使用的位数比其熵所指示的最佳位数多。回想一下,熵是一个用来指定数据所携带信息的术语。这意味着可以有更优化的位表示相同信息。探索和制定更有效的位表示成为设计压缩算法的基础。无损数据压缩利用这种冗余来压缩数据而不丢失任何信息。在 80 年代后期,Ziv 和 Lempel 提出了基于字典的数据压缩技术,可以用于实现无损数据压缩。由于其速度和良好的压缩率,这些技术一炮而红。这些技术被用于创建流行的基于 Unix 的compress工具。此外,普遍存在的gif图像格式使用了这些压缩技术,因为它们可以用较少的位数表示相同的信息,节省了空间和通信带宽。这些技术后来成为开发zip实用程序及其变体的基础。调制解调器中使用的压缩标准 V.44 也是基于它。

现在让我们逐一查看即将到来的部分中的技术。

Huffman 编码

Huffman 编码是压缩数据的最古老方法之一,它基于创建 Huffman 树,用于对数据进行编码和解码。Huffman 编码可以通过利用某些数据(例如字母表的某些字符)在数据流中更频繁地出现这一事实,以更紧凑的形式表示数据内容。通过使用不同长度的编码(对于最常见的字符较短,对于最不常见的字符较长),数据占用的空间更少。

现在,让我们学习一些与 Huffman 编码相关的术语:

  • 编码:在数据的上下文中,编码表示将数据从一种形式表示为另一种形式的方法。我们希望结果形式简洁。

  • 码字:编码形式中的特定字符称为码字。

  • 固定长度编码:每个编码字符,即码字,使用相同数量的位。

  • 可变长度编码:码字可以使用不同数量的位。

  • 代码评估:这是每个码字的预期位数。

  • 前缀自由码:这意味着没有码字是任何其他码字的前缀。

  • 解码:这意味着可变长度编码必须不受任何前缀的限制。

要理解最后两个术语,您首先需要查看此表:

字符 频率 固定长度编码 可变长度编码
L .45 000 0
M .13 001 101
N .12 010 100
X .16 011 111
Y .09 100 1101
Z .05 101 1100

现在,我们可以推断以下内容:

  • 固定长度编码:该表的固定长度编码为 3。

  • 可变长度编码:该表的可变长度编码为45(1) + .13(3) + .12(3) + .16(3) + .09(4) + .05(4) = 2.24

以下图表显示了从上面的例子创建的 Huffman 树:

请注意,Huffman 编码是将数据转换为 Huffman 树以实现压缩。解码或解压缩将数据恢复到原始格式。

一个实际的例子- Twitter 实时情感分析

据说 Twitter 每秒有近 7000 条关于各种话题的推文。让我们尝试构建一个情感分析器,可以实时捕捉来自不同新闻来源的新闻的情绪。我们将从导入所需的包开始:

  1. 导入所需的包:
import tweepy,json,time
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
analyzer = SentimentIntensityAnalyzer()

请注意,我们使用以下两个包:

  1. VADER情感分析,代表Valence Aware Dictionary and Sentiment Reasoner。这是一种流行的基于规则的情感分析工具,专为社交媒体开发。如果您以前从未使用过它,那么您首先需要运行以下命令:
pip install vaderSentiment
  1. Tweepy,这是一个用于访问 Twitter 的基于 Python 的 API。同样,如果您以前从未使用过它,您需要首先运行这个:
pip install Tweepy
  1. 下一步有点棘手。您需要向 Twitter 发出请求,创建一个开发者帐户,以获取对推文的实时流的访问权限。一旦您获得 API 密钥,您可以用以下变量表示它们:
twitter_access_token = <your_twitter_access_token>
twitter_access_token_secret = <your_twitter_access_token_secret>
twitter_consumer_key = <your_consumer_key>
twitter_consumer_secret = <your_twitter_consumer_secret>
  1. 然后,让我们配置Tweepy API 的身份验证。为此,我们需要提供先前创建的变量:
auth = tweepy.OAuthHandler(twitter_consumer_key, twitter_consumer_secret)
auth.set_access_token(twitter_access_token, twitter_access_token_secret)
api = tweepy.API(auth, parser=tweepy.parsers.JSONParser())
  1. 现在是有趣的部分。我们将选择我们想要监控情感分析的新闻来源的 Twitter 句柄。在这个例子中,我们选择了以下新闻来源:
news_sources = ("@BBC", "@ctvnews", "@CNN","@FoxNews", "@dawn_com")
  1. 现在,让我们创建主循环。这个循环将从一个名为array_sentiments的空数组开始,用于保存情感。然后,我们将循环遍历所有五个新闻来源,并收集每个 100 条推文。然后,对于每条推文,我们将计算其极性:

  1. 现在,让我们创建一个图表,显示来自这些个别新闻来源的新闻的极性:

请注意,每个新闻来源都用不同的颜色表示。

  1. 现在,让我们看一下摘要统计信息:

上述数字总结了情感的趋势。例如,BBC 的情感被发现是最积极的,而加拿大新闻频道 CTVnews 似乎带有最消极的情绪。

摘要

在本章中,我们研究了以数据为中心的算法的设计。我们关注了数据为中心算法的三个方面:存储、压缩和流式处理。

我们研究了数据特征如何决定数据存储设计。我们研究了两种不同类型的数据压缩算法。然后,我们研究了一个实际示例,说明了如何使用数据流算法来计算文本数据流中的单词计数。

在下一章中,我们将研究加密算法。我们将学习如何利用这些算法的力量来保护交换和存储的消息。

第十二章:密码学

本章向您介绍了与密码学相关的算法。我们将首先介绍背景,然后讨论对称加密算法。然后我们将解释消息摘要 5MD5)算法和安全哈希算法SHA),并介绍实施对称算法的局限性和弱点。接下来,我们将讨论非对称加密算法以及它们用于创建数字证书的方式。最后,我们将提供一个总结所有这些技术的实际示例。

通过本章的学习,您将对与密码学相关的各种问题有一个基本的了解。

本章讨论以下主题:

  • 密码学简介

  • 了解密码学技术的类型

  • 示例-部署机器学习模型时的安全问题

让我们先从基本概念开始。

密码学简介

保护秘密的技术已经存在了几个世纪。最早的保护和隐藏数据免受敌人侵害的尝试可以追溯到埃及的古代铭文,那里使用了只有少数信任的人知道的特殊字母表。这种早期的安全形式被称为模糊,今天仍然以不同的形式使用。为了使这种方法有效,保护字母表的含义至关重要。随后,在一战和二战中,寻找保护重要消息的绝对可靠方法变得非常重要。20 世纪末,随着电子和计算机的引入,开发了复杂的算法来保护数据,从而产生了一个全新的领域,称为密码学。本章讨论了密码学的算法方面。这些算法的目的是允许两个进程或用户之间进行安全数据交换。密码算法找到了使用数学函数来确保所述安全目标的策略。

了解最弱链接的重要性

有时,在构建数字基础设施的安全性时,我们过分强调个体实体的安全性,而忽视了端到端的安全性。这可能导致我们忽视系统中的一些漏洞和弱点,这些漏洞和弱点后来可能被黑客利用来获取敏感数据。要记住的重要一点是,整个数字基础设施的强度取决于它的最弱链接。对于黑客来说,这个最弱链接可以提供对数字基础设施中敏感数据的后门访问。在某个程度上,加固前门而不关闭所有后门并没有太多好处。

随着数字基础设施的算法和技术变得越来越复杂,攻击者也在不断升级他们的技术。要记住的一点是,攻击者入侵数字基础设施最简单的方法之一就是利用这些漏洞来获取敏感信息。

2014 年,对加拿大联邦研究机构——国家研究委员会NRC)的网络进行了一次网络攻击,据估计造成了数亿美元的损失。攻击者能够窃取数十年的研究数据和知识产权材料。他们利用了网页服务器上使用的 Apache 软件中的一个漏洞来获取对敏感数据的访问权限。

在本章中,我们将重点介绍各种加密算法的漏洞。

让我们首先来看看使用的基本术语。

基本术语

让我们先来看看与密码学相关的基本术语:

  • 密码:执行特定加密功能的算法。

  • 明文:可以是文本文件、视频、位图或数字化语音的原始数据。在本章中,我们将表示明文为P

  • 密文:应用加密后得到的混乱文本。在本章中,我们将其表示为C

  • 密码套件:一组或套件的加密软件组件。当两个独立节点想要使用加密交换消息时,它们首先需要就密码套件达成一致。这对于确保它们使用完全相同的加密函数实现非常重要。

  • 加密:将明文P转换为密文C的过程称为加密。在数学上,它表示为encrypt(P) = C

  • 解密:将密文转换回明文的过程。在数学上,它表示为decrypt(C) = P

  • 密码分析:用于分析加密算法强度的方法。分析人员试图在没有秘密访问的情况下恢复明文。

  • 个人可识别信息PII):PII 是指可以单独使用或与其他相关数据一起用于追踪个人身份的信息。一些例子包括受保护的信息,如社会安全号码、出生日期或母亲的婚前姓氏。

了解安全需求

首先了解系统的确切安全需求是很重要的。了解这一点将帮助我们使用正确的加密技术,并发现系统中的潜在漏洞。为了做到这一点,我们首先需要更好地了解系统的需求。为了了解安全需求,我们执行以下三个步骤:

  • 识别实体。

  • 确立安全目标。

  • 了解数据的敏感性。

让我们逐一看看这些步骤。

识别实体

识别实体的一种方法是首先回答以下四个问题,这将帮助我们了解系统在安全环境中的需求:

  • 哪些应用程序需要受到保护?

  • 我们要保护应用程序免受谁的攻击?

  • 我们应该在哪里保护它们?

  • 我们为什么要保护它们?

一旦我们更好地了解这些要求,我们就可以确立我们数字系统的安全目标。

确立安全目标

通常使用加密算法来满足一个或多个安全目标:

  • 认证:简单来说,认证是我们如何证明用户是其所声称的人的方式。通过认证的过程,我们确保用户的身份得到验证。认证的过程始于用户提供其身份。接着是提供只有用户知道的信息,因此只能由他们产生。

  • 机密性:需要受到保护的数据称为敏感数据。机密性是将敏感数据限制为仅授权用户的概念。为了在传输或存储过程中保护敏感数据的机密性,您需要使数据变得不可读,除了授权用户之外。这是通过使用加密算法来实现的,我们将在本章后面讨论。

  • 完整性:完整性是指建立数据在传输或存储过程中没有被任何方式改变的过程。例如,TCP/IP(传输控制协议/互联网协议)使用校验和或循环冗余校验CRC)算法来验证数据的完整性。

  • 不可否认性:不可否认性是指信息发送方收到数据已被接收的确认,接收方收到发送方身份的确认的概念。这提供了无可辩驳的证据,证明了消息的发送或接收,这可以在以后用来证明数据的接收和通信中的故障点。

了解数据的敏感性

了解数据的机密性很重要。我们还需要考虑数据泄露的后果有多严重。数据的分类有助于我们选择正确的加密算法。根据信息的敏感性,有多种分类数据的方式。让我们看看数据分类的典型方式:

  • 公共数据或未分类数据:任何对公众可用的数据。例如,在公司网站或政府信息门户上找到的信息。

  • 内部数据或机密数据:虽然不适合公开,但将这些数据暴露给公众可能不会造成破坏性后果。例如,如果员工的投诉经理的电子邮件被曝光,这可能会让公司尴尬,但可能不会造成破坏性后果。

  • 敏感数据或机密数据:不应该公开的数据,如果暴露给公众,对个人或组织会造成破坏性后果。例如,泄露未来 iPhone 的细节可能会损害苹果的业务目标,并给三星等竞争对手带来优势。

  • 高度敏感数据:也称为绝密数据。这是如果泄露将对组织造成极大损害的信息。这可能包括客户社会安全号码、信用卡号码或其他非常敏感的信息。绝密数据通过多层安全保护,并需要特别许可才能访问。

一般来说,更复杂的安全设计比简单的算法要慢得多。在安全性和系统性能之间取得正确的平衡非常重要。

了解密码的基本设计

设计密码是为了想出一种算法,可以混淆敏感数据,使恶意进程或未经授权的用户无法访问它。尽管随着时间的推移,密码变得越来越复杂,但密码基于的基本原理保持不变。

让我们从一些相对简单的密码开始,这将帮助我们理解加密算法设计中使用的基本原理。

呈现替换密码

替换密码在各种形式上已经使用了数百年。顾名思义,替换密码基于一个简单的概念——以预定的有序方式用其他字符替换明文中的字符。

让我们看看其中涉及的确切步骤:

  1. 首先,我们将每个字符映射到一个替代字符。

  2. 然后,通过替换映射,将明文编码并转换为密文,用密文中的另一个字符替换明文中的每个字符。

  3. 解码时,使用替换映射将明文还原。

让我们看一些例子:

  • 凯撒密码

在凯撒密码中,替换映射是通过用右边的第三个字符替换每个字符来创建的。这个映射在下图中描述:

让我们看看如何使用 Python 实现凯撒密码:

import string
rotation = 3
P = 'CALM'; C=''
for letter in P:
   C = C+ (chr(ord(letter) + rotation))

我们可以看到我们对明文CALM应用了凯撒密码。

让我们用凯撒密码加密后打印密文:

据说凯撒密码曾被朱利叶斯·凯撒用来与他的顾问交流。

  • 旋转 13ROT13):

ROT13 是另一种基于替换的加密。在 ROT13 中,替换映射是通过用右边的第 13 个字符替换每个字符来创建的。以下图表说明了这一点:

这意味着如果ROT13()是实现 ROT13 的函数,那么以下内容适用:

import codecs
P = 'CALM'
C=''
C=codecs.encode(P, 'rot_13')

现在,让我们打印C的编码值:

  • 替换密码的密码分析

替换密码很容易实现和理解。不幸的是,它们也很容易破解。替换密码的简单密码分析表明,如果我们使用英语字母表,那么我们需要确定的是破解密码的旋转量。我们可以逐个尝试英语字母表的每个字母,直到我们能够解密文本。这意味着需要大约 25 次尝试才能重构明文。

现在,让我们看另一种简单密码—置换密码。

理解置换密码

在置换密码中,明文的字符被置换。让我们看一下其中涉及的步骤:

  1. 创建矩阵并选择置换矩阵的大小。它应该足够大,以适应明文字符串。

  2. 通过横向写入字符串的所有字符来填充矩阵。

  3. 在矩阵中垂直读取字符串的所有字符。

让我们看一个例子。

让我们以Ottawa Rocks明文(P)为例。

首先,让我们对P进行编码。为此,我们将使用一个 3 x 4 的矩阵,横向写入明文:

O t t a
w a R o
c k s

read过程将垂直读取字符串,这将生成密码文本—OwctaktRsao

德国人在第一次世界大战中使用了一种名为 ADFGVX 的密码,它同时使用了置换和替换密码。多年后,它被 George Painvin 破解。

因此,这些是一些密码类型。现在,让我们看一些当前使用的密码技术。

理解密码技术的类型

不同类型的密码技术使用不同类型的算法,并在不同的情况下使用。

广义上,密码技术可以分为以下三种类型:

  • 散列

  • 对称

  • 非对称

让我们逐个来看。

使用密码哈希函数

密码哈希函数是一种数学算法,可以用来创建消息的唯一指纹。它从明文中创建一个称为哈希的固定大小的输出。

从数学上看,这看起来是这样的:

C[1] = hashFunction(P[1])

这是解释如下的:

  • P[1] 是表示输入数据的明文。

  • C[1] 是由密码哈希函数生成的固定长度哈希。

这在下图中显示。可变长度数据通过单向哈希函数转换为固定长度哈希:

哈希函数具有以下五个特征:

  • 它是确定性的。相同的明文生成相同的哈希。

  • 唯一的输入字符串应该生成唯一的输出哈希值。

  • 无论输入消息如何,它都具有固定长度。

  • 明文中的微小变化会生成新的哈希。

  • 它是一个单向函数,这意味着无法从密码文本C[1]生成明文P[1]

如果我们遇到每个唯一消息没有唯一哈希的情况,我们称之为碰撞。也就是说,如果我们有两个文本P[1]P[2],在碰撞的情况下,意味着hashFunction(P[1]) = hashFunction(P[2])

无论使用的哈希算法如何,碰撞都是罕见的。否则,哈希将毫无用处。然而,对于一些应用,不能容忍碰撞。在这些情况下,我们需要使用一个更复杂但生成碰撞哈希值的可能性更小的哈希算法。

实现密码哈希函数

密码哈希函数可以通过使用各种算法来实现。让我们深入了解其中的两种。

理解 MD5 容忍

MD5 是由 Poul-Henning Kamp 于 1994 年开发的,用来替代 MD4。它生成 128 位哈希。MD5 是一个相对简单的算法,容易发生碰撞。在不能容忍碰撞的应用中,不应使用 MD5。

让我们看一个例子。为了在 Python 中生成 MD5 哈希,我们将使用passlib库,这是一个最流行的开源库之一,实现了 30 多种密码哈希算法。如果它还没有安装在您的设备上,请在 Jupyter 笔记本中使用以下代码安装它:

!pip install passlib

在 Python 中,我们可以按照以下方式生成 MD5 哈希:

请注意,MD5 生成 128 位的哈希。

如前所述,我们可以将生成的哈希用作原始文本的指纹,原始文本是myPassword。让我们看看如何在 Python 中实现这一点:

请注意,对myPassword字符串生成的哈希与原始哈希匹配,生成了一个True值。但是,一旦明文更改为myPassword2,它就返回了False

现在,让我们来看另一个哈希算法——安全哈希算法SHA)。

理解 SHA

SHA 是由国家标准与技术研究所NIST)开发的。让我们看看如何使用 Python 来创建 SHA 算法的哈希:

from passlib.hash import sha512_crypt
sha512_crypt.using(salt = "qIo0foX5",rounds=5000).hash("myPassword")

请注意使用一个名为salt的参数。加盐是在哈希之前添加随机字符的过程。

运行这段代码将给我们带来以下结果:

请注意,当我们使用 SHA 算法时,生成的哈希是 512 字节。

加密哈希函数的应用

哈希函数用于在复制文件后检查文件的完整性。为了实现这一点,当文件从源复制到目的地(例如,从 Web 服务器下载时),相应的哈希也会被复制。这个原始哈希,h[original],充当了原始文件的指纹。复制文件后,我们再次从复制的文件版本生成哈希,即h[copied]。如果h[original] = h[copied]—也就是说,生成的哈希与原始哈希匹配—这验证了文件没有改变,并且在下载过程中没有丢失任何数据。我们可以使用任何加密哈希函数,比如 MD5 或 SHA,来为此目的生成哈希。

现在,让我们来看对称加密。

使用对称加密

在密码学中,密钥是一组数字,用于使用我们选择的算法对明文进行编码。在对称加密中,我们使用相同的密钥进行加密和解密。如果用于对称加密的密钥是K,那么对称加密的等式如下:

EK = C

这里,P是明文,C是密文。

对于解密,我们使用相同的密钥K将其转换回P

DK = P

这个过程在下面的图表中显示:

现在,让我们看看如何在 Python 中使用对称加密。

编写对称加密

在本节中,我们将使用 Python 的cryptography包来演示对称加密。它是一个全面的包,实现了许多加密算法,比如对称密码和不同的消息摘要。第一次使用时,我们需要使用pip命令来安装它:

!pip install cryptography

安装完成后,我们现在可以使用该包来实现对称加密,如下所示:

  1. 首先,让我们导入我们需要的包:
import cryptography as crypt
from cryptography.fernet import Fernet
  1. 让我们生成密钥:

  1. 现在,让我们打开密钥:
file = open('mykey.key', 'wb')
file.write(key) 
file.close()
  1. 使用密钥,现在让我们尝试加密消息:

file = open('mykey.key', 'rb')
key = file.read() 
file.close()
  1. 现在,让我们使用相同的密钥解密消息:
from cryptography.fernet import Fernet
message = "Ottawa is really cold".encode()

f = Fernet(key)
encrypted = f.encrypt(message)
  1. 让我们解密消息并将其赋给一个名为decrypt的变量:
decrypted = f.decrypt(encrypted)
  1. 现在让我们打印decrypt变量,以验证我们是否能够得到相同的消息:

让我们看一些对称加密的优势。

对称加密的优势

尽管对称加密的性能取决于所使用的确切算法,但一般来说,它比非对称加密快得多。

对称加密的问题

当两个用户或进程计划使用对称加密进行通信时,它们需要使用安全通道交换密钥。这引发了以下两个问题:

  • 密钥保护:如何保护对称加密密钥。

  • 密钥分发:如何将对称加密密钥从源共享到目的地。

现在,让我们看一下非对称加密。

非对称加密

在 20 世纪 70 年代,非对称加密被设计出来以解决我们在前一节中讨论的对称加密的一些弱点。

非对称加密的第一步是生成两个看起来完全不同但在算法上相关的不同密钥。其中一个被选择为私钥,K[pr],另一个被选择为公钥,K[pu]。在数学上,我们可以表示如下:

EKpr = C

这里,P是明文,C是密文。

我们可以按以下方式解密:

DKpu = P

公钥应该被自由分发,私钥由密钥对的所有者保密。

基本原则是,如果使用其中一个密钥进行加密,解密的唯一方法是使用另一个密钥。例如,如果我们使用公钥加密数据,我们将需要使用另一个密钥来解密它,即私钥。现在,让我们看一下非对称加密的一个基本协议——安全套接字层SSL)/传输层安全性TLS)握手,它负责使用非对称加密在两个节点之间建立连接。

SSL/TLS 握手算法

SSL 最初是为 HTTP 添加安全性而开发的。随着时间的推移,SSL 被更高效、更安全的协议 TLS 所取代。TLS 握手是 HTTP 创建安全通信会话的基础。TLS 握手发生在两个参与实体——客户端服务器之间。此过程如下图所示:

TLS 握手在参与节点之间建立了安全连接。以下是涉及此过程的步骤:

  1. 客户端向服务器发送一个“客户端 hello”消息。消息还包含以下内容:
  • 所使用的 TLS 版本

  • 客户端支持的密码套件列表

  • 一个压缩算法

  • 一个由byte_client标识的随机字节字符串

  1. 服务器向客户端发送一个“服务器 hello”消息。消息还包含以下内容:
  • 服务器从客户端提供的列表中选择的密码套件

  • 一个会话 ID

  • 一个由byte_server标识的随机字节字符串

  • 包含服务器公钥的服务器数字证书,由cert_server标识

  • 如果服务器需要客户端身份验证的数字证书或客户端证书请求,客户端服务器请求还包括以下内容:

  • 可接受的 CA 的可区分名称

  • 支持的证书类型

  1. 客户端验证cert_server

  2. 客户端生成一个随机的字节字符串,由byte_client2标识,并使用服务器通过cert_server提供的公钥进行加密。

  3. 客户端生成一个随机的字节字符串,并用自己的私钥进行加密。

  4. 服务器验证客户端证书。

  5. 客户端向服务器发送一个使用秘密密钥加密的“完成”消息。

  6. 为了从服务器端确认这一点,服务器向客户端发送一个使用秘密密钥加密的“完成”消息。

  7. 服务器和客户端现在建立了一个安全通道。他们现在可以交换使用共享秘密密钥对称加密的消息。整个方法如下所示:

现在,让我们讨论如何使用非对称加密来创建公钥基础设施PKI),PKI 是为了满足组织的一个或多个安全目标而创建的。

公钥基础设施

非对称加密用于实现 PKI。PKI 是管理组织加密密钥的最流行和可靠的方式之一。所有参与者都信任一个名为 CA 的中央信任机构。CA 验证个人和组织的身份,然后为他们颁发数字证书(数字证书包含个人或组织的公钥副本和其身份),验证与该个人或组织相关联的公钥实际上属于该个人或组织。

它的工作方式是 CA 要求用户证明其身份,对个人和组织遵循不同的标准。这可能涉及简单地验证域名的所有权,也可能涉及更严格的过程,包括身份的物理证明,这取决于用户试图获得的数字证书的类型。如果 CA 确信用户确实是他们声称的人,用户随后通过安全通道向 CA 提供他们的公共加密密钥。CA 使用这些信息创建包含用户身份和他们的公钥信息的数字证书。该证书由 CA 数字签名。用户随后可以向任何想要验证其身份的人展示其证书,而无需通过安全通道发送它,因为证书本身不包含任何敏感信息。接收证书的人不必直接验证用户的身份。该人只需验证证书是否有效,验证 CA 的数字签名,以验证证书中包含的公钥实际上属于证书上命名的个人或组织。

组织的 CA 的私钥是 PKI 信任链中最薄弱的环节。例如,如果冒名顶替者获取了微软的私钥,他们可以通过冒充 Windows 更新在全球数百万台计算机上安装恶意软件。

示例-部署机器学习模型时的安全问题

在第六章中,无监督机器学习算法,我们看了CRISP-DM跨行业标准数据挖掘过程)生命周期,该生命周期指定了训练和部署机器学习模型的不同阶段。一旦模型被训练和评估,最后阶段是部署。如果这是一个关键的机器学习模型,那么我们希望确保它的所有安全目标都得到满足。

让我们分析部署这样一个模型时面临的常见挑战,以及如何使用本章讨论的概念来解决这些挑战。我们将讨论保护我们训练好的模型免受以下三个挑战的策略:

  • 中间人MITM)攻击

  • 冒充

  • 数据篡改

让我们逐个来看。

中间人攻击

我们希望保护我们的模型免受的可能攻击之一是中间人攻击。中间人攻击发生在入侵者试图窃听假定为私人通信的情况下,部署训练好的机器学习模型。

让我们尝试使用一个示例场景来顺序理解中间人攻击。

假设鲍勃和爱丽丝想要使用 PKI 交换消息:

  1. 鲍勃使用{Pr[Bob],Pu[Bob]},爱丽丝使用{Pr[Alice],Pu[Alice]}。鲍勃创建了消息M[Bob],爱丽丝创建了消息M[Alice]。他们希望以安全的方式彼此交换这些消息。

  2. 最初,他们需要交换他们的公钥以建立彼此之间的安全连接。 这意味着鲍勃在发送消息给艾丽斯之前使用Pu[Alice]加密M[Bob]

  3. 假设我们有一个窃听者X,他正在使用{Pr[X],Pu[X]}。 攻击者能够拦截鲍勃和艾丽斯之间的公钥交换,并用自己的公共证书替换它们。

  4. 鲍勃将M[Bob]发送给艾丽斯,使用Pu[X]而不是Pu[Alice]进行加密,错误地认为这是艾丽斯的公共证书。 窃听者X拦截了通信。 它拦截了M[Bob]消息并使用Pr[Bob]解密。

这种中间人攻击显示在以下图表中:

现在,让我们看看如何防止中间人攻击。

如何防止中间人攻击

让我们探讨如何通过引入 CA 来防止中间人攻击到组织中。 假设这个 CA 的名字是 myTrustCA。 数字证书中嵌入了它的公钥,名为Pu[myTrustCA]。 myTrustCA 负责为组织中的所有人,包括艾丽斯和鲍勃签署证书。 这意味着鲍勃和艾丽斯的证书都由 myTrustCA 签署。 在签署他们的证书时,myTrustCA 验证他们确实是他们声称的人。

现在,有了这个新的安排,让我们重新审视鲍勃和艾丽斯之间的顺序交互:

  1. 鲍勃正在使用{Pr[Bob],Pu[Bob]},艾丽斯正在使用{Pr[Alice],Pu[Alice]}。 他们的公钥都嵌入到他们的数字证书中,由 myTrustCA 签名。 鲍勃创建了一条消息M[Bob],艾丽斯创建了一条消息M[Alice]。 他们希望以安全的方式互相交换这些消息。

  2. 他们交换他们的数字证书,其中包含他们的公钥。 只有在证书中嵌入的公钥由他们信任的 CA 签署时,他们才会接受这些公钥。 他们需要交换他们的公钥以建立彼此之间的安全连接。 这意味着鲍勃将使用Pu**[Alice]来加密M**[Bob],然后将消息发送给艾丽斯。

  3. 假设我们有一个窃听者X,他正在使用{Pr[X],Pu[X]}。 攻击者能够拦截鲍勃和艾丽斯之间的公钥交换,并用自己的公共证书Pu[X]替换它们。

  4. 鲍勃拒绝X的尝试,因为坏人的数字证书没有被鲍勃信任的 CA 签名。 安全握手被中止,尝试的攻击被记录下来,并且引发了安全异常。

在部署训练好的机器学习模型时,不是艾丽斯,而是一个部署服务器。 鲍勃只有在建立安全通道后才能部署模型,使用先前提到的步骤。

让我们看看如何在 Python 中实现这一点。

首先让我们导入所需的包。

from xmlrpc.client import SafeTransport, ServerProxy
import ssl

现在让我们创建一个可以验证证书的类。


class CertVerify(SafeTransport): 
    def __init__(self, cafile, certfile=None, keyfile=None): 
    SafeTransport.__init__(self) 
    self._ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) 
    self._ssl_context.load_verify_locations(cafile) 
    if cert: 
        self._ssl_context.load_cert_chain(certfile, keyfile) 
    self._ssl_context.verify_mode = ssl.CERT_REQUIRED 

def make_connection(self, host): 
    s = super().make_connection((host, {'context': self._ssl_context})) 
    return s

# Create the client proxy
s = ServerProxy('https://cloudanum.com:15000', transport=VerifyCertSafeTransport('server_cert.pem'), allow_none=True)

让我们看看我们部署的模型可能面临的其他漏洞。

避免伪装

攻击者X假装成授权用户鲍勃,并获得对敏感数据的访问权限,这在这种情况下是训练模型。 我们需要保护模型免受任何未经授权的更改。

保护我们训练模型免受伪装的一种方法是使用授权用户的私钥对模型进行加密。 一旦加密,任何人都可以通过解密授权用户的公钥来读取和利用模型,这在他们的数字证书中找到。 没有人可以对模型进行任何未经授权的更改。

数据和模型加密

一旦模型部署,提供给模型作为输入的实时未标记数据也可能被篡改。训练好的模型用于推断并为这些数据提供标签。为了防止数据被篡改,我们需要保护静态数据和通信中的数据。为了保护静态数据,可以使用对称加密进行编码。可以建立基于 SSL/TLS 的安全通道来传输数据,以提供安全的隧道。这个安全隧道可以用来传输对称密钥,并且数据可以在提供给训练好的模型之前在服务器上解密。

这是保护数据免受篡改的更有效和可靠的方法之一。

在将模型部署到服务器之前,也可以使用对称加密对模型进行加密。这将防止在部署之前未经授权访问模型。

让我们看看如何使用以下步骤在源处使用对称加密加密训练好的模型,然后在目的地解密它,然后再使用它:

  1. 让我们首先使用鸢尾花数据集训练一个简单的模型:
import cryptography as crypt
from sklearn.linear_model 
import LogisticRegression 
from cryptography.fernet 
import Fernet from sklearn.model_selection 
import train_test_split 
from sklearn.datasets import load_iris 
iris = load_iris() 

X = iris.data 
y = iris.target    
X_train, X_test, y_train, y_test = train_test_split(X, y) 
model = LogisticRegression() 
model.fit(X_train, y_train)
  1. 现在,让我们定义将存储模型的文件的名称:
filename_source = 'myModel_source.sav' 
filename_destination = "myModel_destination.sav" 
filename_sec = "myModel_sec.sav"

请注意,filename_source 是将在源处存储训练好的未加密模型的文件。filename_destination 是将在目的地存储训练好的未加密模型的文件,filename_sec 是加密的训练好的模型。

  1. 我们将使用 pickle 将训练好的模型存储在文件中:
from pickle import dump dump(model, open(filename_source, 'wb'))
  1. 让我们定义一个名为 write_key() 的函数,它将生成一个对称密钥并将其存储在名为 key.key 的文件中:
def write_key():
     key = Fernet.generate_key()
     with open("key.key", "wb") as key_file:
         key_file.write(key)
  1. 现在,让我们定义一个名为 load_key() 的函数,它可以从 key.key 文件中读取存储的密钥:
def load_key():
    return open("key.key", "rb").read()
  1. 接下来,让我们定义一个 encrypt() 函数,它可以加密和训练模型,并将其存储在名为 filename_sec 的文件中:
def encrypt(filename, key):
     f = Fernet(key)
     with open(filename_source,"rb") as file:         
         file_data = file.read() 
     encrypted_data = f.encrypt(file_data)
     with open(filename_sec,"wb") as file:
         file.write(encrypted_data)
  1. 我们将使用这些函数生成对称密钥并将其存储在文件中。然后,我们将读取此密钥并使用它将我们的训练好的模型存储在名为 filename_sec 的文件中:
write_key()
encrypt(filename_source,load_key())

现在模型已经加密。它将被传输到目的地,在那里将用于预测。

  1. 首先,我们将定义一个名为 decrypt() 的函数,我们可以使用它来使用存储在 key.key 文件中的密钥将模型从 filename_sec 解密到 filename_destination
def decrypt(filename, key):
     f = Fernet(key)     
     with open(filename_sec, "rb") as file:                
         encrypted_data = file.read()        
     decrypted_data = f.decrypt(encrypted_data)     
     with open(filename_destination, "wb") as file:         file.write(decrypted_data)
  1. 现在让我们使用这个函数来解密模型并将其存储在名为 filename_destination 的文件中:
decrypt(filename_sec,load_key())
  1. 现在让我们使用这个未加密的文件来加载模型并用于预测:

请注意,我们已经使用对称加密对模型进行了编码。如果需要,可以使用相同的技术来加密数据。

摘要

在本章中,我们学习了加密算法。我们首先确定了问题的安全目标。然后讨论了各种加密技术,还研究了 PKI 基础设施的细节。最后,我们研究了不同的方法来保护训练好的机器学习模型免受常见攻击。现在,您应该能够理解用于保护现代 IT 基础设施的安全算法的基本原理。

在下一章中,我们将研究设计大规模算法。我们将研究设计和选择大型算法涉及的挑战和权衡。我们还将研究使用 GPU 和集群来解决复杂问题。

posted @ 2024-04-16 15:27  绝不原创的飞龙  阅读(6)  评论(0编辑  收藏  举报