人工智能与网络安全实用指南-全-

人工智能与网络安全实用指南(全)

原文:annas-archive.org/md5/64f3bda8588b7d31830ae45037a1465b

译者:飞龙

协议:CC BY-NC-SA 4.0

序言

当今各组织在全球范围内花费数十亿美元用于网络安全。人工智能AI)已成为构建更智能、更安全的安全系统的伟大解决方案,使你能够预测和检测网络中的可疑活动,例如钓鱼攻击或未经授权的入侵。

本书展示并介绍了流行且成功的 AI 方法和模型,你可以采用这些方法来检测潜在的攻击并保护企业系统。你将理解机器学习ML)、神经网络NNs)和深度学习在网络安全中的作用,并学习如何在构建智能防御机制时注入 AI 能力。随着学习的深入,你将能够将这些策略应用于多种应用场景,包括垃圾邮件过滤、网络入侵检测、僵尸网络检测和安全身份验证。

本书结束时,你将准备好开发能够检测异常和可疑模式及攻击的智能系统,从而利用人工智能(AI)建立强大的网络安全防御。

本书适合谁阅读

如果你是网络安全专家或伦理黑客,想利用机器学习(ML)和人工智能(AI)的力量构建智能系统,你会发现这本书非常有用。

本书涵盖的内容

第一章,网络安全专业人员的 AI 简介,介绍了 AI 的各个分支,重点介绍了网络安全领域中自动化学习的各种方法的优缺点。本章还讨论了学习算法及其优化的不同策略。AI 的主要概念将在 Jupyter Notebooks 中展示并应用。本章使用的工具包括 Jupyter Notebooks、NumPy 和 scikit-learn,使用的数据集为 scikit-learn 数据集和 CSV 样本。

第二章,为你的网络安全武器库设置 AI,介绍了主要的软件需求及其配置。我们将学习如何通过恶意代码样本喂入知识库,以供 AI 算法处理。还将介绍 Jupyter Notebooks,方便交互式执行 Python 工具和命令。本章使用的工具有 Anaconda 和 Jupyter Notebooks,不涉及数据集。

第三章,垃圾邮件还是正常邮件?利用 AI 检测电子邮件网络安全威胁,涵盖了利用电子邮件作为攻击向量来检测电子邮件安全威胁的内容。从线性分类器和贝叶斯过滤器到更复杂的解决方案(如决策树、逻辑回归和自然语言处理NLP**)等),将会展示不同的检测策略。示例将使用 Jupyter Notebooks 以便读者更好地与不同解决方案进行互动。本章使用的工具包括 Jupyter Notebooks、scikit-learn 和 NLTK。本章所使用的数据集包括 Kaggle 垃圾邮件数据集、CSV 垃圾邮件样本和蜜罐钓鱼样本。

第四章,恶意软件威胁检测,介绍了恶意软件和勒索软件代码的广泛传播,以及同一类威胁在不同变种(多态性和形态变化恶意软件)中的快速多态变异,这使得基于特征码和图像文件哈希的传统检测解决方案已不再有效。常见的杀毒软件就是基于这些技术的。示例将展示使用机器学习算法的不同恶意软件分析策略。本章使用的工具包括 Jupyter Notebooks、scikit-learn 和 TensorFlow。本章所使用的数据集/样本包括 theZoo 恶意软件样本。

第五章,基于 AI 的网络异常检测,解释了当前不同设备之间的互联程度已经达到如此复杂的程度,以至于传统的概念,如边界安全性,的有效性产生了严重怀疑。事实上,在网络空间中,攻击面呈指数增长,因此,必须拥有自动化工具来检测网络异常并了解新的潜在威胁。本章使用的工具包括 Jupyter Notebooks、pandas、scikit-learn 和 Keras。本章所使用的数据集包括 Kaggle 数据集、KDD 1990、CIDDS、CICIDS2017、服务和 IDS 日志文件。

第六章,用户身份验证安全,介绍了人工智能在网络安全领域的应用,它在保护敏感的与用户相关的信息(包括用于访问其网络账户和应用程序的凭据)方面发挥着越来越重要的作用,以防止滥用,如身份盗窃。

第七章,利用云 AI 解决方案预防欺诈,介绍了企业遭受的许多安全攻击和数据泄露。这些泄露的目标是侵犯敏感信息,如客户的信用卡信息。这些攻击通常在隐蔽模式下进行,意味着通过传统方法很难检测到此类威胁。本章使用的工具有 IBM Watson Studio、IBM Cloud Object Storage、Jupyter Notebooks、scikit-learn、Apache Spark。此处使用的数据集是 Kaggle 信用卡欺诈检测数据集。

第八章,GANs–攻击与防御,介绍了生成对抗网络GANs),这代表了深度学习为我们提供的最先进的神经网络示例。在网络安全的背景下,GANs 可以用于合法用途,如身份验证程序,但也可以被利用来侵犯这些程序。本章使用的工具有 CleverHans、对抗机器学习AML)库、EvadeML-Zoo、TensorFlow 和 Keras。使用的数据集是完全由 GAN 生成的面部示例图像。

第九章,评估算法,展示了如何使用适当的分析指标评估各种替代解决方案的有效性。本章使用的工具有 scikit-learn、NumPy 和 Matplotlib。此处使用的 scikit 数据集。

第十章,评估您的 AI 武器库,涵盖了攻击者利用的技巧来避开工具。只有通过这种方式,才能获得解决方案有效性和可靠性的真实图景。此外,必须考虑与解决方案可扩展性相关的各个方面,并且要持续监控以保证可靠性。本章使用的工具有 scikit-learn、Foolbox、EvadeML、Deep-pwning、TensorFlow 和 Keras。此处使用的 MNIST 和 scikit 数据集。

为了充分利用这本书

为了最大限度地发挥这本书的作用,您需要熟悉网络安全概念,并掌握 Python 编程知识。

下载示例代码文件

您可以从您的www.packt.com帐户下载本书的示例代码文件。如果您是在其他地方购买的这本书,您可以访问www.packt.com/support并注册,将文件直接发送到您的邮箱。

您可以按照以下步骤下载代码文件:

  1. 登录或注册到www.packt.com

  2. 选择支持标签。

  3. 点击代码下载和勘误。

  4. 在搜索框中输入书名,并按照屏幕上的说明操作。

文件下载完成后,请确保使用最新版本的工具解压或提取文件夹:

  • Windows 上的 WinRAR/7-Zip

  • Mac 上的 Zipeg/iZip/UnRarX

  • Linux 上的 7-Zip/PeaZip

本书的代码包也托管在 GitHub 上,地址是github.com/PacktPublishing/Hands-On-Artificial-Intelligence-for-Cybersecurity。如果代码有更新,现有的 GitHub 仓库将会进行更新。

我们还有来自丰富书籍和视频目录中的其他代码包可供下载,访问github.com/PacktPublishing/查看!

下载彩色图片

我们还提供了包含本书中截图/图表的彩色图片的 PDF 文件,您可以在这里下载:www.packtpub.com/sites/default/files/downloads/9781789804027_ColorImages.pdf

使用的约定

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

CodeInText:表示文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 账号名。以下是一个例子:“我们将使用的降维技术被称为主成分分析(PCA),并且可以在scikit-learn库中找到。”

代码块如下所示:

import numpy as np
np_array = np.array( [0, 1, 2, 3] )

# Creating an array with ten elements initialized as zero
np_zero_array = np.zeros(10)

当我们希望您关注代码块的某一部分时,相关的行或项目会以粗体显示:

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

警告或重要提示如下所示。

提示和技巧以如下方式出现。

获取联系

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何部分有疑问,请在邮件主题中提到书名,并通过customercare@packtpub.com与我们联系。

勘误:虽然我们已尽力确保内容的准确性,但错误仍然可能发生。如果您在本书中发现错误,我们将非常感激您向我们报告。请访问www.packt.com/submit-errata,选择您的书籍,点击“勘误提交表单”链接并输入详细信息。

盗版:如果您在互联网上遇到任何形式的我们作品的非法复制品,我们将非常感激您提供该位置地址或网站名称。请通过copyright@packt.com联系我们,并附上相关材料的链接。

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

评论

请留下评论。当您阅读并使用完本书后,为什么不在您购买本书的网站上留下评论呢?潜在读者可以看到并参考您公正的意见做出购买决定,我们在 Packt 也能了解您对我们产品的看法,我们的作者也能看到您对他们书籍的反馈。谢谢!

关于 Packt 的更多信息,请访问 packt.com

第一章:AI 核心概念与行业工具

本章节将介绍 AI 的基本概念,包括分析不同类型的算法及其在网络安全中的最佳使用策略。

本章节包含以下内容:

  • 第一章,为网络安全专业人员介绍 AI

  • 第二章,为网络安全打造 AI 工具箱

第二章:网络安全专业人士的人工智能入门

在本章中,我们将区分人工智能AI)的各个分支,重点探讨自动化学习在网络安全领域中不同方法的利弊。

我们将介绍不同的学习策略以及各种算法的优化方法,同时还将通过 Jupyter Notebooks 和 scikit-learn Python 库,展示人工智能实际运作中的主要概念。

本章将涵盖以下主题:

  • 将人工智能应用于网络安全

  • 从专家系统到数据挖掘和人工智能的演变

  • 自动化学习的不同形式

  • 算法训练和优化的特点

  • 通过 Jupyter Notebooks 开始学习人工智能

  • 在网络安全领域引入人工智能

将人工智能应用于网络安全

将人工智能应用于网络安全是一个实验性的研究领域,并非没有问题,我们将在本章中尝试解释这一点。然而,不可否认的是,到目前为止取得的成果是有前景的,并且在不久的将来,分析方法将成为常见实践,对网络安全专业领域产生明确而积极的影响,无论是从新就业机会还是新挑战的角度来看。

在讨论将人工智能应用于网络安全的话题时,业内人士的反应往往是矛盾的。事实上,怀疑的反应与保守的态度交替出现,部分原因是担心机器会取代人工操作员,尽管人类具备通过多年努力工作获得的高技术和专业技能。

然而,在不久的将来,公司和组织将越来越需要投资于自动化分析工具,以便对当前和未来的网络安全挑战作出快速和充分的响应。因此,眼下即将到来的情景实际上是技能的结合,而非人工操作员与机器之间的冲突。因此,人工智能在网络安全领域可能会负责繁重的工作,即筛选潜在的可疑案例,将更高级的任务交给安全分析员,让他们更深入地调查值得关注的威胁。

人工智能的演变:从专家系统到数据挖掘

为了理解在网络安全领域采用人工智能所带来的优势,有必要介绍支撑人工智能不同方法论的基本逻辑。

我们将从对人工智能演变的简要历史分析开始,以便充分评估将其应用于网络安全领域的潜在好处。

专家系统简介

自动学习的最初尝试之一是定义基于规则的决策系统,应用于特定的应用领域,涵盖了现实世界中可能遇到的所有分支和具体案例。通过这种方式,所有可能的选项都被硬编码到自动学习解决方案中,并由领域专家进行验证。

这种专家系统的根本限制在于,它将决策简化为布尔值(这将一切简化为二选一),从而限制了将解决方案适应现实世界中不同细微差别的能力。

实际上,专家系统与硬编码解决方案相比并没有学习到任何新内容,它们仅仅局限于在一个(可能非常庞大的)知识库中寻找正确的答案,而这个知识库并不能适应之前未处理过的新问题。

反映现实的非确定性特征

由于我们在现实世界中遇到的具体案例无法仅通过真假分类模型来表示(尽管该领域的专家努力列出所有可能的案例,但现实中总有一些情况无法分类),因此有必要充分利用我们手头的数据,从而揭示潜在的趋势和异常情况(如异常值),利用统计学和概率模型,更恰当地反映现实的非确定性特征。

超越统计学,迈向机器学习

尽管统计模型的引入突破了专家系统的局限性,但这种方法的根本僵化性仍然存在,因为统计模型,如基于规则的决策,实际上是在预先建立的,无法修改以适应新数据。例如,最常用的统计模型之一是高斯分布。统计学家可以决定数据来自高斯分布,并尝试估计描述数据的假设分布的参数,而不考虑其他可能的模型。

因此,为了克服这些局限性,有必要采用迭代方法,这种方法允许引入能够从现有数据出发,对描述性模型进行泛化的机器学习ML)算法,从而自主生成其自身的特征,不仅仅局限于预定义的目标函数,而是将其适应于算法训练过程的持续演化。

挖掘数据以生成模型

与预定义的静态模型相比,这种方法的差异也体现在被称为数据挖掘的研究领域。

对数据挖掘过程的恰当定义是从数据中发现合适的代表性模型。此外,在这种情况下,我们可以使用基于训练数据的机器学习算法来识别最合适的预测模型,而不是采用预先建立的统计模型(当我们无法理解手头数据的性质时,这一点尤为重要)。

然而,算法方法并不总是适用。当数据的性质明确并符合已知模型时,使用机器学习算法而非预定义模型并没有优势。下一步吸收并扩展了前面方法的优势,并增加了管理训练数据中未涵盖情况的能力,这将我们引导到人工智能。

人工智能(AI)是一个比机器学习(ML)更广泛的研究领域,它可以处理比机器学习更为通用和抽象的数据,因此能够在不完全重新训练的情况下,将常见的解决方案转移到不同类型的数据上。例如,可以从彩色图像中识别物体,尽管最初的物体是从黑白样本中获得的。

因此,AI 被视为一个包含机器学习(ML)的广泛研究领域。而机器学习又包括基于人工神经网络的深度学习DL),如下图所示:

机器学习的类型

从数据中进行机械学习的过程可以采取不同的形式,具有不同的特征和预测能力。

在机器学习(ML)的情况下(正如我们所看到的,它是人工智能研究的一个分支),通常会区分以下几种类型的机器学习:

  • 监督学习

  • 无监督学习

  • 强化学习

这些学习方式之间的差异可以归因于我们打算根据所需输入产生的结果(输出)的类型。

监督学习

监督学习的情况下,算法训练是通过使用输入数据集进行的,其中我们必须获得的输出类型已经是已知的。

实际上,算法必须经过训练,以识别正在训练的变量之间的关系,试图根据目标变量(也称为标签)来优化学习参数,这些目标变量如前所述是已知的。

一个监督学习算法的例子是分类算法,它在网络安全领域中被广泛用于垃圾邮件分类

垃圾邮件过滤器实际上是通过向算法提交输入数据集进行训练的,数据集包含了许多已经被先前标记为垃圾邮件(这些邮件是恶意的或不需要的)或正常邮件(这些邮件是真实的且无害的)的示例。

垃圾邮件过滤器的分类算法因此必须学习如何对未来接收到的新邮件进行分类,根据之前对已分类邮件数据集的训练,归类为垃圾邮件或正常邮件。

另一个监督学习算法的例子是回归算法。最终,主要的监督学习算法如下:

  • 回归(线性与逻辑回归)

  • k 最近邻 (k-NNs)

  • 支持向量机 (SVMs)

  • 决策树与随机森林

  • 神经网络 (NNs)

无监督学习

无监督学习的情况下,算法必须尝试独立分类数据,无需依赖分析师提供的先前分类。在网络安全的背景下,无监督学习算法对于识别新的(之前未检测到的)恶意软件攻击、欺诈行为和电子邮件垃圾邮件攻击非常重要。

以下是一些无监督算法的示例:

  • 降维:

    • 主成分分析 (PCA)

    • PCA 核

  • 聚类:

    • k 均值

    • 层次聚类分析 (HCA)

强化学习

强化学习RL)的情况下,采用不同的学习策略,这种策略模仿试错法。因此,算法通过在学习过程中获取的反馈信息,目的是最大化最终获得的奖励,奖励依据算法选择的正确决策数量来决定。

实际上,学习过程以无监督的方式进行,特别之处在于,每一次正确决策会被分配一个正奖励(而每次错误决策会获得负奖励),这些决策在学习过程中每一步都做出。在学习过程结束时,算法的决策会基于最终获得的奖励进行重新评估。

由于其动态特性,强化学习(RL)与人工智能采用的通用方法更为相似,而非机器学习中常见的算法,这并非偶然。

以下是一些强化学习(RL)算法的示例:

  • 马尔科夫过程

  • Q 学习

  • 时序差分 (TD) 方法

  • 蒙特卡洛方法

特别是,隐马尔科夫模型HMM)(利用马尔科夫过程)在检测变种恶意软件威胁方面极为重要。

算法训练与优化

在准备自动化学习程序时,我们常常会面临一系列挑战。我们需要克服这些挑战,以确保程序本身的可靠性不受损害,从而避免得出错误或仓促的结论,而这些结论在网络安全的背景下可能会带来灾难性后果。

我们经常面对的一个主要问题,特别是在配置威胁检测程序的情况下,是管理虚警;即,由算法检测并分类为潜在威胁的案例,实际上并非如此。我们将在第七章,云 AI 解决方案下的欺诈预防,和第九章,评估算法中更深入地讨论虚警和 ML 评估指标。

特别是在旨在对抗网络威胁检测系统的情况下,虚警的管理尤为繁重,因为检测到的事件数量通常如此之高,以至于吸收和饱和了所有专门用于威胁检测活动的人力资源。

另一方面,即使是正确的(真正的阳性)报告,如果数量过多,也会导致分析员的功能超载,分散他们的重要任务。因此,需要优化学习过程,以减少需要分析员深入分析的案例数量。

此优化活动通常始于选择和清理提交给算法的数据。

如何找到有用的数据来源

异常检测的情况下,特别需要注意正在分析的数据。有效的异常检测活动假定训练数据不包含所寻找的异常,而是反映了参考正常情况。

另一方面,如果训练数据偏向于包含正在调查的异常,那么异常检测活动将丧失其可靠性和实用性,符合通常被称为GIGO的原则,即垃圾进,垃圾出

鉴于实时原始数据的日益可用,通常认为数据的初步清理本身就是一个挑战。事实上,经常需要对数据进行初步筛选,消除无关或冗余的信息。然后,我们可以以正确的形式呈现数据给算法,这可以提高它们学习的能力,并根据使用的算法类型调整数据的形式。

例如,分类算法将能够在输入数据以分组形式呈现或能够线性可分的情况下,识别更具代表性和更有效的模型。同样,包含空字段(也称为维度)的变量会增加算法的计算负担,并由于所谓的维度诅咒现象产生较不可靠的预测模型。

这种情况发生在特征数量,也就是维度 增加而没有改善相关信息时,结果仅仅是数据在增大的研究空间中分散:

此外,我们从哪些 来源 获取测试案例(样本)也非常重要。例如,假设我们必须预测一个 未知可执行文件 的恶意行为。问题简化为对可执行文件 分类模型 的定义,该模型必须归属于两个类别之一: 正品恶意

为了达到这样的结果,我们需要通过提供一系列被认为是恶意的可执行文件示例来训练我们的分类算法,作为输入数据集。

数量与质量

当问题归结为数量与质量时,我们立即面临以下两个问题:

  • 我们可以认为哪些类型的恶意软件最能代表我们公司最可能面临的风险和威胁?

  • 我们应该收集多少个示例案例(样本),并将它们提供给算法,以便在未来威胁的有效性和预测效率方面获得可靠的结果?

对这两个问题的回答与分析师对 特定组织领域 知识的了解密切相关,在该领域他们必须开展工作。

所有这些可能使分析师认为,创建一个 蜜罐,它有助于收集恶意样本,这些样本将被作为训练样本输入算法,比使用数据集作为通用威胁的示例,更能 代表 组织面临的风险水平。同时,提交给算法的测试样本数量由 数据本身的特征 决定。这些特征实际上可能呈现某一类型的案例(偏斜性)占优势,忽视其他类型,从而导致算法的预测出现 失真,偏向于最常见的类别,而实际上,对我们调查最有意义的信息是由数量较少的类别所代表的。

总之,这不仅仅是选择最适合我们目标的算法(这往往是不存在的),而是主要选择 最具代表性的案例(样本)提交给一组算法,我们将根据获得的结果尝试优化这些算法。

了解 Python 库

在接下来的章节中,我们将探索目前所介绍的概念,并展示一些示例代码,这些代码使用了一系列在机器学习领域中最为知名和广泛应用的 Python 库:

  • NumPy(版本 1.13.3)

  • pandas(版本 0.20.3)

  • Matplotlib(版本 2.0.2)

  • scikit-learn(版本 0.20.0)

  • Seaborn(版本 0.8.0)

示例代码将以代码片段的形式展示在此,并附有显示其输出的截图。不要担心如果你一开始不能完全理解所有的实现细节;在整个书中,我们将有机会逐步理解每个算法的实现方面。

有监督学习示例 - 线性回归

作为我们的第一个示例,我们将看一下有监督学习领域中最常用的算法之一——线性回归。利用scikit-learn Python 库,我们通过导入scikit-learn库中linear_model包的LinearRegression类来实例化一个线性回归对象。

模型将使用通过调用 Python numpy库中的random包的RandomState类的rand()方法获得的训练数据集进行训练。训练数据按照线性模型分布,见下图,。模型的训练通过调用LinearRegression类中的lreg对象的fit()方法来完成。

在这一步,我们将尝试通过调用lreg对象的predict()方法,预测不包含在训练数据集中的数据。

训练数据集以及模型插值的值最终通过matplotlib库中的scatter()plot()方法在屏幕上打印出来:

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

from sklearn.linear_model import LinearRegression

pool = np.random.RandomState(10)
x = 5 * pool.rand(30)
y = 3 * x - 2 + pool.randn(30)
# y = 3x - 2;

lregr = LinearRegression(fit_intercept=False)
X = x[:, np.newaxis]
lregr.fit(X, y)
lspace = np.linspace(0, 5)
X_regr = lspace[:, np.newaxis]
y_regr = lregr.predict(X_regr)
plt.scatter(x, y);
plt.plot(X_regr, y_regr);

上面的代码生成了以下输出,展示了数据样本如何通过线性回归模型返回的直线得到了很好的逼近:

无监督学习示例 - 聚类

作为无监督学习的一个例子,我们使用GaussianMixture聚类模型。通过这种类型的模型,我们将尝试将数据恢复为一组高斯斑点

训练数据从.csv格式(逗号分隔值)文件中加载,并存储在pandas Python 库的DataFrame对象中。数据加载后,我们将继续降低其维度,以识别一个将原始维度(特征)从四维降到二维的表示,尽量保留最具代表性的特征。

降维可以避免与维度灾难现象相关的缺点,提高计算效率,并简化数据的可视化。

我们将用于降维的技术被称为主成分分析PCA),它在scikit-learn库中可以找到。

一旦数据维度从四维降低到二维,我们将尝试使用GaussianMixture模型对数据进行分类,方法如下:

import pandas as pd
import seaborn as sns

data_df = pd.read_csv("../datasets/clustering.csv")
data_df.describe()
X_data = data_df.drop('class_1', axis=1)
y_data = data_df['class_1']

from sklearn.decomposition import PCA   

pca = PCA(n_components=2)               
pca.fit(X_data)                         
X_2D = pca.transform(X_data)            
data_df['PCA1'] = X_2D[:, 0]
data_df['PCA2'] = X_2D[:, 1]

from sklearn.mixture import GaussianMixture         

gm = GaussianMixture(n_components=3, covariance_type='full')     
gm.fit(X_data)                         
y_gm = gm.predict(X_data)              
data_df['cluster'] = y_gm
sns.lmplot("PCA1", "PCA2", data=data_df, col='cluster', fit_reg=False)

如下图所示,聚类算法已成功地以合适的方式自动分类数据,而无需事先了解与各个样本相关的当前标签:

简单的神经网络示例 – 感知机

在本节中,我们将展示一个简单的神经网络模型,称为感知机

神经网络(NN)深度学习(DL)是机器学习的子领域,旨在模拟人脑的学习能力。NN 和 DL 将在第三章,《火腿还是垃圾邮件?使用 AI 检测电子邮件中的网络安全威胁》和第八章,《生成对抗网络(GANs)– 攻击与防御》中深入讨论。

尽管感知机非常基础,但它仍然能够充分分类那些倾向于聚集在一起的样本(在技术术语中,指的是线性可分的样本)。

如我们所见,感知机在网络安全领域中最常见的用途之一是垃圾邮件过滤

在以下示例中,我们将使用scikit-learn实现的感知机算法:

from matplotlib.colors import ListedColormap 
# Thanks to Sebastian Raschka for 'plot_decision_regions' function 
def plot_decision_regions(X, y, classifier, resolution=0.02): 
 # setup marker generator and color map 
 markers = ('s', 'x', 'o', '^', 'v')
 colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
 cmap = ListedColormap(colors[:len(np.unique(y))]) 
 # plot the decision surface 
 x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
 x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
 xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), 
 np.arange(x2_min, x2_max, resolution))
 Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
 Z = Z.reshape(xx1.shape)
 plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
 plt.xlim(xx1.min(), xx1.max())
 plt.ylim(xx2.min(), xx2.max()) 
 # plot class samples 
 for idx, cl in enumerate(np.unique(y)):
 plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], 
 alpha=0.8, c=cmap(idx), 
 marker=markers[idx], label=cl) 
from sklearn.linear_model import perceptron
from sklearn.datasets import make_classification 
X, y = make_classification(30, 2, 2, 0, weights=[.3, .3], random_state=300) 
plt.scatter(X[:,0], X[:,1], s=50)
pct = perceptron.Perceptron(max_iter=100, verbose=0, random_state=300, 
fit_intercept=True, eta0=0.002)
pct.fit(X, y)
plot_decision_regions(X, y, classifier=pct)
plt.title('Perceptron')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

上述代码生成以下输出:

AI 在网络安全中的应用

随着与新型恶意软件传播日益增加相关的威胁数量呈指数级增长,单纯依赖人工操作员的分析来有效应对这些威胁几乎是不可能的。我们必须引入能够自动化分析初步阶段的算法,称为分诊,即对威胁进行初步筛选,然后将其提交给网络安全专业人员,以便我们能够及时、有效地应对正在进行的攻击。

我们需要能够以动态的方式做出响应,适应与前所未有威胁相关的环境变化。这不仅意味着分析师管理网络安全的工具和方法,还意味着他们能够正确解读和评估 AI 和 ML 算法提供的结果。

因此,网络安全专业人员被要求理解算法的逻辑,并根据结果和要实现的目标,对其学习阶段进行微调

使用 AI 的一些任务如下:

  • 分类:这是网络安全框架中的主要任务之一。它用于正确识别相似攻击类型,例如属于同一家族的不同恶意软件,即具有共同特征和行为的恶意软件,尽管它们的特征不同(想想变种恶意软件)。同样,能够适当地分类电子邮件也是非常重要的,区分垃圾邮件与合法邮件。

  • 聚类:与分类不同,聚类能够在没有预先了解类别信息的情况下自动识别样本所属的类别(正如我们所见,这是无监督学习的典型目标)。这一任务在恶意软件分析取证分析中具有基础性的重要性。

  • 预测分析:通过利用神经网络(NN)和深度学习(DL),可以在威胁发生时进行识别。为此,必须采取一种高度动态的方法,使得算法能够自动优化其学习能力。

AI 在网络安全中的可能应用如下:

  • 网络保护:使用机器学习(ML)可以实现高度复杂的入侵检测系统IDS),这些系统将用于网络边界保护领域。

  • 终端保护:威胁如勒索软件可以通过采用学习这些类型恶意软件典型行为的算法得到有效检测,从而克服传统防病毒软件的局限性

  • 应用安全:一些最隐蔽的网络应用攻击类型包括服务器端请求伪造SSRF)攻击、SQL 注入跨站脚本XSS)攻击,以及分布式拒绝服务DDoS)攻击。这些都是可以通过使用 AI 和 ML 工具与算法有效防御的威胁类型。

  • 可疑用户行为:识别恶意用户在发生时试图欺诈或破坏应用程序的行为,是深度学习(DL)应用的一个新兴领域。

总结

在本章中,我们介绍了与网络安全背景相关的 AI 和 ML 的基本概念。我们展示了在自动学习过程管理中采用的一些策略,以及数据分析师面临的潜在问题。本章中学到的概念和工具将在接下来的章节中被应用和调整,解决网络安全的具体问题。

在下一章中,我们将更深入地学习如何管理 Jupyter 交互式笔记本,这允许读者互动地执行指令并实时显示执行结果。

在本书的过程中,AI 和 ML 的概念将在各章讨论的主题中不时出现,力求对所研究的算法提供实际的解释。对于那些有兴趣深入研究各种算法实现细节的读者,我们建议参考由 Sebastian Raschka 和 Vahid Mirjalili 编写、Packt 出版的《Python 机器学习(第二版)》。

第三章:设置你的 AI 网络安全武器库

本章介绍了主要的软件要求及其配置。你将学习如何通过恶意代码样本喂养知识库,这些样本将作为输入传递给 AI 程序。还将介绍 IPython notebook,用于交互式执行 Python 工具和命令。

本章将涵盖以下主题:

  • 了解 Python 在 AI 和网络安全中的应用

  • 进入 Anaconda——数据科学家首选的开发环境

  • 玩转 Jupyter Notebook

  • 喂养你的 AI 武器库——在哪里可以找到数据和恶意样本

了解 Python 在 AI 和网络安全中的应用

在所有可以用于编程 AI 工具和算法的语言中,Python 是近年来持续增长并受到新老程序员青睐的语言。尽管竞争激烈,如 R 语言和 Java 都拥有成千上万的开发者,Python 已经获得了作为 数据科学 和 (最重要的) 机器学习 (ML)、深度学习 (DL),以及更广泛的 人工智能 (AI) 算法开发首选语言的声誉。

Python 在这些领域的成功并不令人惊讶。Python 最初是为编写数值计算程序而开发的,但后来扩展到非专业领域,成为一种通用编程语言,与 C++ 和 Java 等更为人熟知的语言并列。

Python 的成功归因于以下几个原因:

  • 易于学习:语言学习曲线的陡峭程度确实比其他语言,如 C++ 和 Java 要低得多。

  • 加速代码原型开发与代码重构过程:得益于简洁的设计和清晰的语法,Python 编程比其他语言容易得多。调试代码也变得更加容易。用 Python 开发的程序原型在没有进一步修改的情况下发布并投入运行并不罕见。这些特性在数据科学和 AI 等领域至关重要。这些领域的特点是需要快速原型化新功能并重构旧功能,而不必浪费时间调试遗留代码,因此急需一种加速代码原型开发和重构的方法。

  • 解释性语言与面向对象编程:能够以脚本的形式编写代码,该代码可以直接在命令行上启动,或者更好的是,以交互模式运行(如我们稍后所见),无需进行可执行格式的编译,这大大加速了开发过程和应用程序测试。面向对象编程还促进了 API 和可重用功能库的开发,确保代码的可靠性和健壮性。

  • 开源库的广泛可用性,扩展了编程功能:到目前为止我们讨论的好处,转化为大量高级函数库的可用性,这些库可以自由供分析师和开发人员使用,并由庞大的 Python 社区提供。这些函数库可以轻松地相互集成,得益于简洁的语言设计,这促进了 API 的开发,开发人员可以方便地调用这些 API。

现在,让我们深入了解 Python 中最常用的 AI 编程库。

Python 的 AI 库

正如预期的那样,Python 中有许多可用于数据科学和机器学习(ML)领域的库,包括深度学习(DL)和强化学习RL)。

同样,也有许多图形表示和报告功能。接下来的章节,我们将分析这些库的特点。

NumPy 作为 AI 的构建模块

在所有专门用于数据科学和 AI 的 Python 库中,毫无疑问,NumPy 占据了一个特殊的位置。利用 NumPy 实现的功能和 API,可以从零开始构建机器学习算法和工具。

当然,拥有专门的 AI 库(例如scikit-learn库)可以加速 AI 和机器学习工具的开发过程,但为了充分理解使用这些高级库带来的优势,理解它们构建的基础组件是非常有用的。这就是为什么了解 NumPy 的基本概念在这方面非常重要。

NumPy 多维数组

NumPy的创建是为了解决重要的科学问题,其中包括线性代数矩阵计算。与 Python 语言本身提供的数组列表等数据结构相比,它提供了特别优化的版本,使得多维数组对象ndarrays得以使用。实际上,ndarray类型的对象使得操作加速,速度可达到传统for循环的 25 倍,这对于管理存储在传统 Python 列表中的数据访问非常必要。

此外,NumPy 还允许对矩阵进行操作管理,这对于实现机器学习算法特别有用。与ndarray对象不同,矩阵是只能具有二维的对象,表示线性代数中使用的主要数据结构。

下面是一些定义 NumPy 对象的示例:

import numpy as np
np_array = np.array( [0, 1, 2, 3] )

# Creating an array with ten elements initialized as zero
np_zero_array = np.zeros(10)

使用 NumPy 进行矩阵操作

如前所述,矩阵及其上的操作在机器学习领域尤其重要,更一般来说,它们用于方便地表示需要输入 AI 算法的数据。

矩阵在管理和表示大量数据时特别有用。

符号本身通常用于标识矩阵中的元素,利用位置索引可以执行一致的、快速的操作,并进行涉及整个矩阵或特定子集的计算。例如,矩阵中的元素可以轻松识别,交叉行与列

由一行(和多列)组成的特殊矩阵被称为向量。*向量可以在 Python 中表示为list类型的对象。

然而,在进行矩阵与向量之间的操作时,应考虑线性代数中规定的特定规则。

可以对矩阵执行的基本操作如下:

  • 加法

  • 减法

  • 标量乘法(导致每个矩阵元素乘以一个常数值)

如果对矩阵进行这样的操作相对简单,并且仅作为必要的前提条件,要求相加或相减的矩阵必须是相同大小,那么两个矩阵的加法或减法结果将是一个新矩阵,其元素是对应元素按行列顺序求和的结果。

在进行矩阵与矩阵或矩阵与向量的乘法操作时,线性代数的规则部分不同,因为例如交换律不像在两个标量相乘的情况下那样适用。

实际上,在两个数相乘的情况下,因子顺序不影响乘积结果(也就是说,2 x 3 = 3 x 2),但在两个矩阵相乘的情况下,顺序非常重要

aX != Xa

这里,X代表矩阵a代表系数向量。此外,并非总是可以乘法两个矩阵,例如当两个矩阵的维度不兼容时。

因此,numpy库提供了dot()函数来计算两个矩阵之间的乘积(只要这个操作是可能的):

import numpy as np
a = np.array([-8, 15])
X = np.array([[1, 5], 

              [3, 4],  

              [2, 3]])
y = np.dot(X, a)

在前面的示例中,我们使用np.dot()函数计算矩阵X和向量a的乘积。

该产品是模型的表达:

y = Xa

它代表了机器学习中最基本的模型之一,用于将一组权重(a)与输入数据矩阵(X)相关联,从而获得估算的输出值(y)。

使用 NumPy 实现一个简单的预测器

为了充分理解 NumPy 中dot()方法在矩阵乘法操作中的应用,我们可以尝试从零开始实现一个简单的预测器,根据一组多个输入和相对权重,通过矩阵与向量之间的乘积来预测未来的值:

import numpy as np
def predict(data, w):  
     return data.dot(w)

# w is the vector of weights
w = np.array([0.1, 0.2, 0.3]) 

# matrices as input datasets
data1 = np.array([0.3, 1.5, 2.8]) 
data2 = np.array([0.5, 0.4, 0.9]) 
data3 = np.array([2.3, 3.1, 0.5])
data_in = np.array([data1[0],data2[0],data3[0]]) 
print('Predicted value: $%.2f' %  predict(data_in, w) )

Scikit-learn

最佳且最常用的机器学习库之一无疑是scikit-learn库。scikit-learn库最早于 2007 年开发,提供了一系列易于重用的模型和算法,用于定制解决方案的开发,涵盖了包括以下在内的主要预测方法和策略:

  • 分类

  • 回归

  • 降维

  • 聚类

这个列表并没有结束,事实上,scikit-learn还提供了现成的模块,允许执行以下任务:

  • 数据预处理

  • 特征提取

  • 超参数优化

  • 模型评估

scikit-learn的特点在于,它除了使用 SciPy 库进行科学计算外,还使用了numpy库。正如我们所见,NumPy 允许通过多维数组和矩阵来优化在大数据集上执行的计算操作。

scikit-learn的优势中,我们不能忘记它为开发人员提供了一个非常简洁的应用程序编程接口API),使得从库的类中开发定制化工具变得相对简单。

作为使用scikit-learn预测分析模板的示例,我们将展示如何基于y权重向量,使用线性回归模型对训练数据(存储在X矩阵中)进行预测。

我们的目标是使用在LinearRegression类中实现的fit()predict()方法:

import numpy as np
from sklearn.linear_model import LinearRegression

# X is a matrix that represents the training dataset

# y is a vector of weights, to be associated with input dataset

X = np.array([[3], [5], [7], [9], [11]]).reshape(-1, 1) 
y = [8.0, 9.1, 10.3, 11.4, 12.6]  
lreg_model = LinearRegression()  
lreg_model.fit(X, y) 

# New data (unseen before)
new_data = np.array([[13]]) 
print('Model Prediction for new data: $%.2f' 
       %  lreg_model.predict(new_data)[0]  )

执行后,脚本会输出以下内容:

Model Prediction for new data: $13.73

现在让我们继续学习 Matplotlib 和 Seaborn 库。

Matplotlib 和 Seaborn

AI 和数据科学分析师最常用的分析工具之一就是数据的图形表示。这是一项被称为探索性数据分析EDA)的初步数据分析活动。通过 EDA,可以通过对数据进行简单的视觉调查,识别出将其与规律性或更好的预测模型相关联的可能性。

在图形库中,毫无疑问,最著名且最常用的是matplotlib库,通过该库可以非常简单直观地创建图表和数据图像。

Matplotlib基本上是一个受 MATLAB 启发的数据绘图工具,与 R 中使用的ggplot工具类似。

在下面的代码中,我们展示了一个使用matplotlib库的简单示例,使用plot()方法绘制由numpy库的arange()方法(数组范围)获取的输入数据:

import numpy as np 
import matplotlib.pyplot as plt  
plt.plot(np.arange(15), np.arange(15))
plt.show() 

除了 Python 中的matplotlib库,还有另一个在数据科学家中广为人知的可视化工具,叫做Seaborn

Seaborn 是 Matplotlib 的扩展,它为数据科学提供了各种可视化工具,简化了分析师的工作,使他们无需从头编写图形数据表示工具,利用 matplotlibscikit-learn 提供的基本功能。

Pandas

在 Python 最常用的库中,最后(但并非最不重要的)是 pandas 包,它有助于简化数据清理这一日常活动(这一活动占据了分析师大部分时间),以便进行后续的数据分析阶段。

pandas 的实现与 R 中的 DataFrame 包非常相似;DataFrame 实际上就是一种表格结构,用于以表格形式存储数据,其中列表示变量,行表示数据本身。

在以下示例中,我们将展示一个典型的 DataFrame 的使用示例,该 DataFrame 是通过实例化 pandas 中的 DataFrame 类获得的,输入参数是 scikit-learn 中的一个数据集(iris 数据集)。

在实例化 DataFrame 类型的 iris_df 对象之后,调用 pandas 库的 head()describe() 方法,分别显示数据集的前五条记录和数据集中的一些主要统计量:

import pandas as pd  
from sklearn import datasets

iris = datasets.load_iris()
iris_df = pd.DataFrame(iris.data, columns = iris.feature_names)
iris_df.head()
iris_df.describe()

网络安全的 Python 库

Python 不仅是数据科学和人工智能领域最好的语言之一,还是渗透测试员和恶意软件分析师的首选语言(与 C 和汇编等低级语言一起使用)。

在 Python 中,有无数现成的库可以使用,它们简化了研究人员的日常工作。

接下来,我们将分析其中一些最常见和最常用的库。

Pefile

Pefile 库对于分析 Windows 可执行文件非常有用,特别是在 静态恶意软件分析 阶段,寻找可能的妥协迹象或可执行文件中恶意代码的存在。实际上,Pefile 使得分析 可移植执行文件 (PE) 文件格式变得非常容易,而这种格式是 Microsoft 平台上对象文件(作为外部可执行功能库包含或可获取的)的标准。

因此,不仅是经典的 .exe 文件,还有 .dll 库和 .sys 设备驱动程序,都遵循 PE 文件格式规范。安装 Pefile 库非常简单,只需使用如下示例中的 pip 命令即可:

pip install pefile

安装完成后,我们可以用一个简单的脚本测试该库,例如以下脚本,它将可执行文件 notepad.exe 加载到运行时内存中,然后从其可执行映像中提取一些在相关 PE 文件格式字段中保存的最重要信息:

import os
import pefile
notepad = pefile.PE("notepad.exe", fast_load=True)
dbgRVA = notepad.OPTIONAL_HEADER.DATA_DIRECTORY[6].VirtualAddress
imgver = notepad.OPTIONAL_HEADER.MajorImageVersion
expRVA = notepad.OPTIONAL_HEADER.DATA_DIRECTORY[0].VirtualAddress
iat = notepad.OPTIONAL_HEADER.DATA_DIRECTORY[12].VirtualAddress
sections = notepad.FILE_HEADER.NumberOfSections
dll = notepad.OPTIONAL_HEADER.DllCharacteristics
print("Notepad PE info: \n")
print ("Debug RVA: " + dbgRVA)
print ("\nImage Version: " + imgver)
print ("\nExport RVA: " + expRVA)
print ("\nImport Address Table: " + iat)
print ("\nNumber of Sections: " + sections)
print ("\nDynamic linking libraries: " + dll)

波动性

另一个被恶意软件分析师广泛使用的工具是 volatility,它允许分析可执行进程的运行时内存,突出显示可能存在的恶意软件代码。

Volatility 是一个可编程的 Python 工具,通常在恶意软件分析和渗透测试的发行版中默认安装,如 Kali Linux。Volatility 允许从内存转储中提取关于进程的重要信息(如 API 钩子、网络连接和内核模块),为分析师提供一套使用 Python 编程的工具。

这些工具允许从内存转储中提取系统上运行的所有进程以及关于注入的 动态链接库DLLs)的相关信息,同时可以检测到 rootkit 的存在,或者更广泛地说,可以发现 隐藏进程 在运行时内存中的存在,这些进程常常逃脱常见杀毒软件的检测。

安装 Python 库

我们已经看到一些基本的 Python 库,它们对我们的分析工作很有帮助。那么我们如何在开发环境中安装这些库呢?

作为 Python 库,显然可以通过按照语言提供的传统工具进行安装;特别是,使用 pip 命令,或者启动每个库包提供的 setup.py。然而,在 AI 和数据科学领域中,使用 Anaconda 配置分析和开发环境有一种更简单的方法,正如我们将在接下来的章节中看到的那样。

进入 Anaconda —— 数据科学家的首选环境

由于可用的 Python 库数量庞大,它们的安装往往特别繁琐(如果不是枯燥无味的话),尤其对于那些刚接触数据科学和 AI 领域的人来说,安装过程尤其困难。

为了简化已经预配置的开发环境的设置,像 Anaconda (www.anaconda.com/download/) 这样的库和工具包集合被提供出来。这使得可以快速访问最常用的工具和库,从而加快开发活动,而无需浪费时间解决包之间的依赖问题,或与不同操作系统的安装问题。

在我写这篇文章时,最新发布的 Anaconda 版本是 5.3.0(可以从www.anaconda.com/anaconda-distribution-5-3-0-released/下载)。

你可以选择适合你平台的安装发行版,无论是 Windows、Linux 还是 macOS,32 位还是 64 位,或者 Python 3.7 还是 2.7,如下图所示:

Anaconda Python 优势

Anaconda 是一个由 700 多个 Python 开发包组成的集合,其中包括我们在前面几段中提到的数据分析和机器学习库,还有许多其他包:

  • NumPy

  • SciPy

  • Scikit-learn

  • Pandas

  • Matplotlib

此外,Anaconda 允许您配置自定义环境,在这些环境中,您可以安装特定版本的 Python,以及开发所需的包和库。

Conda 工具

Anaconda 提供了一个非常强大的工具,conda。通过 conda,您可以管理和更新已安装的包,或安装新包,还可以以最简单的方式创建自定义环境。

要访问 conda 帮助菜单,请从命令提示符运行以下命令:

conda -h

在 Anaconda 中安装包

使用 conda 工具,可以安装预安装包集合中未包含的新包。要安装新包,只需执行以下命令:

conda install

正在执行的命令将在 Anaconda Continuum Analytics 的在线仓库中搜索包。请记住,始终可以通过传统的安装方法进行操作,使用pip install命令或启动包中的setup.py文件。

显然,在这种情况下,我们必须担心解决版本之间所有可能的依赖关系和兼容性问题。

创建自定义环境

如前所述,Anaconda 的一个优势是它能够创建自定义环境,在这些环境中,我们可以安装特定版本的 Python 以及各种包。事实上,Anaconda 通常预装了 Python 2.7 和 Python 3.7 的版本。您可以选择结合特定版本的 Python,而不会破坏默认环境。为此,您需要创建自定义环境。

假设我们想要创建一个自定义环境,在其中安装 Python 3.5(或其他版本)。只需像以下示例一样调用 conda 工具:

conda create -n py35 python=3.5

此时,conda 将创建并配置名为py35的新自定义环境,其中安装了 Python 3.5 版本。要激活新创建的环境,只需从命令提示符运行以下命令:

activate py35

从现在开始,所有启动的命令都将在py35自定义环境中执行。

一些有用的 Conda 命令

一些有用的 Conda 命令如下:

  • 要激活新创建的py35自定义环境,请运行以下命令:
activate py35
  • 通过执行以下命令在特定环境中安装包:
conda install -n py35 PACKAGE-NAME
conda install -n py35 seaborn
  • 通过运行以下命令列出特定环境中已安装的包:
conda list -n py35
  • 使用以下命令更新 Anaconda:
conda update conda
conda update –all

强化版 Python 与并行 GPU

为了充分利用某些机器学习库,尤其是深度学习(DL),需要部署专用硬件,包括使用除传统 CPU 外的图形处理单元GPU)。由于当前的 GPU 确实经过优化,能够执行并行计算,因此这一特性对许多 DL 算法的高效执行非常有用。

参考硬件设备如下:

  • CPU 英特尔 Core i5 第六代或更高版本(或 AMD 同等产品)

  • 最低 8 GB 内存(推荐 16 GB 或更高)

  • GPU  NVIDIA GeForce GTX 960 或更高版本(更多信息请访问 developer.nvidia.com/cuda-gpus

  • Linux 操作系统(例如 Ubuntu)或 Microsoft Windows 10

通过利用 Anaconda 提供的 Numba 编译器,你可以将 Python 代码编译并在支持 CUDA 的 GPU 上运行。

有关更多信息,请参考你 GPU 制造商的官方网站以及 Numba 文档(numba.pydata.org/numba-doc/latest/user/index.html)。

玩转 Jupyter Notebooks

在开发者最常用的工具中,毫无疑问Jupyter Notebook是其中之一,它允许在一个文档中集成 Python 代码及其执行结果,包括图像和图形。通过这种方式,可以在开发活动中获得即时反馈,以迭代的方式管理编程的各个阶段。

在 Jupyter Notebook 中,可以调用在自定义环境中安装的各种特定库。Jupyter 是一个基于网页的工具,因此要运行笔记本,需执行以下命令:

jupyter notebook

也可以使用port参数指定服务的监听端口:

jupyter notebook --port 9000

这样,服务将在9000端口启动(而不是默认的8888端口)。

Jupyter 是 Anaconda 中预安装的包之一;无需安装软件,因为它已经可以随时使用。

在接下来的段落中,我们将通过一些示例学习如何使用 Jupyter Notebook。

我们的第一个 Jupyter Notebook

一旦启动 Jupyter,你可以打开根目录下已有的笔记本(可以在localhost:8888/tree查看该目录),或者从头开始创建一个新的笔记本:

笔记本实际上只是扩展名为 .ipynb 的文本文件,里面保存了(以 JSON 格式)Python 代码和其他媒体资源(如以 base64 编码的图像)。

要创建我们的第一个笔记本,只需使用仪表盘界面中提供的菜单项,这非常直观。

我们需要做的就是选择一个文件夹来放置新创建的笔记本,然后点击“New”按钮,选择最适合我们需求的 Python 版本,如下图所示:

到此为止,我们可以重命名新创建的笔记本,然后继续在文档中插入单元格:

我们可以指定单元格的内容类型,在代码(默认)、文本、Markdown 和其他选项之间进行选择:

探索 Jupyter 界面

接下来,我们将更详细地探讨一些常见的笔记本管理任务,从文件重命名开始。新创建的笔记本默认文件名为 Untitled.ipynb。在重命名笔记本时,我们必须记住这一点;此时笔记本不应处于运行状态。因此,请确保在为笔记本分配新名称之前,选择文件 | 关闭并停止 菜单项;只需在目录中选择要重命名的文件,然后在仪表板控制中点击重命名:

单元格中有什么?

单元格是可以插入不同类型内容的容器;单元格中最常见的内容显然是要在笔记本中执行的 Python 代码,但也可以插入纯文本或 Markdown。

当我们插入 Python 代码时,执行结果会立即显示在代码下方,位于同一个单元格内。要插入新单元格,请点击菜单栏中的 插入,并选择 插入下方单元格。

另外,也可以使用快捷键来操作。

有用的快捷键

为了加快最常用命令的执行,Jupyter 界面为我们提供了一系列的快捷键,包括以下内容:

  • Ctrl + Enter:运行选定的单元格

  • EscEnter:在编辑模式和命令模式之间切换

  • 上下箭头:上下滚动单元格(命令模式)

  • AB:在活动单元格的上方或下方插入新单元格

  • Y:将活动单元格设置为代码单元格

  • M:将活动单元格转换为 Markdown 单元格

  • D 两次:删除活动单元格

  • Z:撤销单元格删除

选择你的笔记本内核

笔记本的一个特别有趣的功能是,每个笔记本背后都有一个特定的内核。当我们执行包含 Python 代码的单元格时,该代码会在笔记本的特定内核中执行。

我们可以为单个笔记本选择并分配一个特定的内核,以防我们安装了多个不同的环境:

实际上,不仅可以为不同版本的 Python 安装不同的内核,还可以为其他语言(如 Java、C、R 和 Julia)安装内核。

实际操作

为了结束 Jupyter Notebook 的操作,我们现在将尝试插入一系列包含示例 Python 代码的单元格,回顾所需的库和包,并按以下步骤进行:

  1. 继续插入一个新单元格,在其中编写以下命令:
# Execute plot() inline without calling show()
%matplotlib inline
import numpy as np 
import matplotlib.pyplot as plt  
plt.plot(np.arange(15), np.arange(15))

我们应该得到以下输出:

  1. 现在,添加一个新单元格,在其中编写以下代码:
import numpy as np 
from sklearn.linear_model import LinearRegression

# X is a matrix that represents the training dataset
# y is a vector of weights, to be associated with input dataset

X = np.array([[3], [5], [7], [9], [11]]).reshape(-1, 1) 
y = [8.0, 9.1, 10.3, 11.4, 12.6]  
lreg_model = LinearRegression()  
lreg_model.fit(X, y) 

# New data (unseen before)
new_data = np.array([[13]]) 
print('Model Prediction for new data: $%.2f' 
       %  lreg_model.predict(new_data)[0]  ) 

运行上述代码后,我们应该会得到以下输出:

Model Prediction for new data: $13.73
  1. 最后,我们插入一个新单元格,在其中编写以下代码:
import pandas as pd  
from sklearn import datasets
iris = datasets.load_iris()
iris_df = pd.DataFrame(iris.data, columns = iris.feature_names)
iris_df.head()
iris_df.describe()

启动单元格中的代码执行后,我们应该会得到以下输出:

恭喜!如果一切如描述所示,您已经成功验证了配置,并可以继续进行后续操作。

安装深度学习库

在本节中,我们将考虑安装一些主要的 Python 库用于人工智能的优势,特别是为了发挥深度学习的潜力。

我们将覆盖的库如下:

  • TensorFlow

  • Keras

  • PyTorch

在发现各个库的优势并进行安装之前,让我们先简单谈谈深度学习在网络安全中的优势和特点。

深度学习在网络安全中的优缺点

深度学习相比于其他人工智能领域的一个显著特点是,能够通过利用神经网络来发挥通用算法的优势。通过这种方式,可以面对包含多个不同应用领域的相似问题,并重用在不同背景下开发的常用算法。

深度学习方法利用神经网络NNs)的可能性,添加多个处理层,每一层负责执行不同类型的处理,并与其他层共享处理结果。

在神经网络中,至少有一层是隐藏的,从而模拟人类大脑神经元的行为。

深度学习最常见的应用包括以下几种:

  • 语音识别

  • 视频异常检测

  • 自然语言处理NLP

这些用例在网络安全领域尤为重要。

例如,在生物识别认证程序中,随着越来越多的深度学习算法的应用,深度学习也可以成功用于检测异常的用户行为,或者在欺诈检测过程中,识别支付工具的异常使用,如信用卡。

深度学习的另一个重要应用是检测可能的恶意软件或网络威胁。鉴于深度学习的广泛潜力,甚至坏人也开始使用它,这一点应该不足为奇。

特别是,最近进化型神经网络的传播,如生成对抗网络GANs),对传统的生物识别认证程序构成了严峻挑战,这些程序依赖于人脸识别语音识别。通过使用 GAN,实际上可以生成生物识别证据的人工样本,这些样本几乎无法与原始样本区分。

我们将在接下来的章节中深入探讨这一点。

现在,让我们来看一下如何在我们的开发环境中安装主要的深度学习库。

TensorFlow

我们将要处理的第一个深度学习库是 TensorFlow;事实上,它扮演了一个特殊角色,因为它是专门为编程深度神经网络DNN)模型而开发的。

要在 Anaconda 中安装 TensorFlow,首先必须创建一个自定义环境(如果还没有创建的话),可以按以下步骤操作:

在我们的案例中,我们将使用之前创建的自定义环境 py35

  1. 使用 conda 安装 TensorFlow:
conda install -n py35 -c conda-forge tensorflow
  1. 使用以下命令安装特定版本的 TensorFlow:
conda install -n py35 -c conda-forge tensorflow=1.0.0
  1. 我们可以通过在交互式 conda 会话中运行一个示例 TensorFlow 程序来测试我们的安装,方法如下:
activate py35
python
>>> import tensorflow as tf
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess = tf.Session()
>>> print(sess.run(hello))

获取更多文档,请访问 TensorFlow 官网:www.tensorflow.org/

Keras

我们将安装的另一个深度学习库是 keras

Keras 的特点是可以安装在 TensorFlow 之上,从而构成一个高层次接口(相对于 TensorFlow)用于神经网络(NN)开发。与 TensorFlow 一样,对于 Keras,我们将继续在之前创建的自定义环境 py35 内进行安装,执行以下命令:

conda install -n py35 -c conda-forge keras

获取更多文档,请访问 Keras 官网:keras.io/

PyTorch

我们将在这里检查的最后一个深度学习库是 pytorch

PyTorch 是 Facebook 开发的一个项目,专门设计用于执行大规模的图像分析。即使是 PyTorch,通过 conda 在 py35 环境中的安装也非常简单:

conda install -n py35 -c peterjc123 pytorch

PyTorch 与 TensorFlow

为了比较这两个学习库,需要注意的是,PyTorch 是在 GPU 上执行张量计算任务时优化效果最好的解决方案,因为它特别设计用于在大规模场景下提升性能。

使用 PyTorch 的一些常见应用场景如下:

  • 自然语言处理(NLP)

  • 大规模图像处理

  • 社交媒体分析

然而,仅从性能上进行比较,PyTorch 和 TensorFlow 都是非常优秀的选择;还有其他一些特点可能会让你偏向选择其中一个解决方案。

例如,在 TensorFlow 中,程序的调试比在 PyTorch 中更为复杂。这是因为,在 TensorFlow 中,开发过程更加繁琐(需要定义张量、初始化会话、在会话中跟踪张量等等),而 TensorFlow 模型的部署则无疑更受偏好。

总结

本章介绍了在网络安全领域开展分析和开发活动所必需的工具。我们审视了主要的 AI 库,并介绍了在网络安全领域使用深度学习的优缺点。

在接下来的章节中,我们将学习如何以最佳方式使用手头的工具,意识性地选择那些最能体现我们安全分析策略的工具。

在下一章中,我们将从开发适当的分类器开始,专注于电子邮件垃圾邮件检测。

第二部分:利用 AI 检测网络安全威胁

本节专注于安全威胁检测技术,采用机器学习和深度学习的不同策略和算法,并比较得到的结果。

本节包含以下章节:

  • 第三章,火腿还是垃圾邮件?利用 AI 检测电子邮件网络安全威胁

  • 第四章恶意软件威胁检测

  • 第五章利用 AI 进行网络异常检测

第四章:垃圾邮件还是正常邮件?使用人工智能检测电子邮件中的网络安全威胁

大多数安全威胁使用电子邮件作为攻击向量。由于通过这种方式传输的流量特别大,因此需要使用自动化检测程序,这些程序利用机器学习ML)算法。在本章中,将介绍不同的检测策略,从线性分类器和贝叶斯滤波器到更复杂的解决方案,如决策树、逻辑回归和自然语言处理NLP)。

本章将涵盖以下主题:

  • 如何使用感知器检测垃圾邮件

  • 使用支持向量机SVMs)进行图像垃圾邮件检测

  • 使用逻辑回归和决策树进行钓鱼检测

  • 使用朴素贝叶斯的垃圾邮件检测

  • 基于 NLP 的垃圾邮件检测

使用感知器检测垃圾邮件

人工智能在网络安全领域的第一个具体成功应用之一就是垃圾邮件检测,而最著名的开源工具之一就是SpamAssassin

可以实施的有效垃圾邮件检测策略是不同的,正如我们将在本章中看到的那样,但最常见且简单的策略是使用神经网络NNs)的最基本形式;也就是感知器(Perceptron)。

垃圾邮件检测还为我们提供了逐步引入与神经网络相关的理论概念的机会,从感知器开始,以一种易于理解的方式进行介绍。

了解神经网络最纯粹的形态——感知器

所有神经网络的共同特征(无论其实现复杂性如何)是,它们在概念上模仿人脑的行为。当我们分析大脑的行为时,最基本的结构无疑是神经元。

感知器是人工智能AI)领域中最早成功实现神经元的案例之一。就像人类大脑中的神经元一样,它具有分层结构,旨在将特定输入级别的结果与输出关联,如下图所示:

同样,通过感知器模型实现的人工神经元表示是以一种结构化的方式,旨在将给定的输出值与一个或多个输入数据级别关联起来:

将输入数据转化为输出值的机制是通过对输入值进行适当加权实现的,这些值经过综合后传递给激活函数,当超过某个阈值时,生成的输出值将传递给神经网络的其余部分。

关键在于找到合适的权重!

统计模型与人工智能算法方法之间的一个区别在于,算法实现了一种基于迭代的优化策略。事实上,在每次迭代中,算法会尝试调整自己对值的估计,依据我们需要最小化的成本函数,为其分配更大或更小的权重。算法的目标之一是精确地识别一个最佳的权重向量,以便将其应用于估算值,从而对未知的未来数据做出可靠的预测。

为了充分理解应用于垃圾邮件检测的人工智能算法的强大功能,我们必须首先明确我们应该执行垃圾邮件过滤器的任务。

简言之,垃圾邮件过滤器。

为了理解垃圾邮件过滤器执行的任务,我们来看一个例子。假设将我们收到的电子邮件进行分类,根据电子邮件文本中出现某些特定关键词的频率,判断这些关键词是否存在。为此,我们可以将收到的所有邮件列入表格。但是,我们将如何进行邮件的分类,将其标记为“正常邮件”还是“垃圾邮件”呢?

正如我们所说的,我们将查找电子邮件文本中可疑关键词的出现次数。然后,我们将根据识别的关键词的出现次数,为单个被识别为垃圾邮件的消息分配分数。这个分数也将为我们提供一个参考,用于分类后续的电子邮件。

我们将识别一个阈值,该值允许我们分离垃圾邮件。如果计算出的分数超过阈值,邮件将自动被分类为垃圾邮件;否则,它将被接受为合法邮件,并因此被分类为正常邮件。这个阈值(以及分配的分数)将不断重新确定,以考虑我们将来遇到的新一系列垃圾邮件。

即使从我们对垃圾邮件检测算法的抽象描述中,我们也注意到一些必须牢记的重要特征:我们必须识别一定数量的可疑关键词,以便将邮件分类为潜在的垃圾邮件,并为每封邮件根据识别出的关键词出现次数分配一个分数。

我们需要为分配给各个电子邮件的分数设置一个阈值,超过该阈值的邮件将自动被分类为垃圾邮件。我们还必须正确地权衡电子邮件文本中关键词的意义,以便充分表示包含这些关键词的消息代表垃圾邮件的概率(实际上,单独来看,关键词可能是无害的,但将它们组合在一起时,它们更有可能代表垃圾邮件)。

我们必须考虑到垃圾邮件发送者充分意识到我们正在尝试过滤不需要的邮件,因此他们会尽最大努力采用新的策略来欺骗我们和我们的垃圾邮件过滤器。这就导致了一个持续和迭代的学习过程,这个过程非常适合用人工智能算法来实现。

从我们所说的内容来看,很明显垃圾邮件检测成为人工智能在网络安全领域应用的首次测试并非偶然。事实上,第一代垃圾邮件检测解决方案使用了静态规则,通过正则表达式识别电子邮件文本中预定义的可疑词语模式。

这些静态规则很快证明是无效的,因为垃圾邮件发送者不断采用新的欺骗策略来欺骗反垃圾邮件过滤器。因此,必须采用一种动态方法,使得垃圾邮件过滤器能够根据垃圾邮件发送者不断创新的方式进行学习,同时还可以借助用户在分类电子邮件时做出的决策。通过这种方式,能够有效地管理垃圾邮件现象的爆炸性传播。

垃圾邮件过滤器的作用

反垃圾邮件算法在电子邮件分类时的实际表现如何呢?首先,我们根据可疑关键词来对电子邮件进行分类。为了简化问题,我们假设最具代表性的可疑关键词列表只包含两个词:buy(购买)和 sex(性)。

在这一部分,我们将把电子邮件消息分类在一个表格中,显示在电子邮件文本中识别出的各个关键词的出现次数,并标明这些消息是垃圾邮件还是正常邮件:

目前,我们将给每一封电子邮件消息分配一个分数。

这个分数将通过一个评分函数来计算,该函数考虑了文本中可疑关键词的出现次数。

一种可能的评分函数可以是我们两个关键词出现次数的总和,在这种情况下,使用 B 变量表示“buy”一词,使用 S 变量表示“sex”一词。

因此,评分函数变成了如下形式:

我们还可以根据不同的关键词,为各自的代表性变量赋予不同的权重。例如,消息中包含关键词“sex”比包含“buy”更可能被判定为垃圾邮件,因此可以赋予“sex”更高的权重。

很明显,如果文本中同时出现这两个词,那么它被判定为垃圾邮件的概率就会增加。因此,我们会为 B 变量赋予较低的权重 2,为 S 变量赋予较高的权重 3

我们的评分函数,经过相应权重调整后,变成了以下公式:

现在让我们尝试重新分类我们的邮件,计算每封邮件的相对得分,使用我们的评分函数:

在这一点上,我们必须尝试确定一个阈值,有效地区分垃圾邮件和正常邮件。实际上,45 之间的阈值可以帮助我们正确地区分垃圾邮件和正常邮件。换句话说,如果一封新邮件的得分大于或等于 4,我们很可能面临的是垃圾邮件,而非正常邮件。

我们如何将刚刚看到的概念有效地转化为可用于算法的数学公式呢?

为此,线性代数(正如我们在 第二章 为网络安全武器设置你的 AI 中提到的,当时我们讨论了 numpy 库提供的矩阵实现)为我们提供了帮助。

我们将进一步讨论感知机的实现,但首先,我们将介绍线性分类器的概念,它对于数学表示常见的垃圾邮件检测算法任务非常有用。

使用线性分类器检测垃圾邮件

正如线性代数中所知,用于确定每封邮件得分的函数方程如下:

这标识了笛卡尔平面中的一条直线;因此,我们的垃圾邮件过滤器用于分类邮件的分类器称为 线性分类器。通过采用统计学中常见的数学形式化,可以通过引入求和运算符 ,用一个索引值矩阵 和一个与之关联的权重向量 来替代 BS 变量,从而以更紧凑的形式重新定义先前的方程:

使用索引 i,其取值范围从 1n,这种形式化只是前述变量与相对权重之间求和的紧凑表示:

这样,我们将线性分类器推广到了一个不确定的变量个数 n,而不是像前一个例子中那样限制为 2。这种紧凑的表示法对于在线性代数公式中利用算法实现也非常有用。

实际上,我们的函数转化为一系列乘积的和(每个权重和变量的乘积),这可以很容易地表示为矩阵与向量的乘积:

这里,wT 表示转置的权重载体,计算矩阵与向量乘积时是必需的。

正如我们所见,为了正确地分类电子邮件,我们需要识别一个合适的阈值,以正确地将垃圾邮件与正常邮件区分开:如果某封电子邮件的得分等于或高于阈值,则该邮件将被分类为垃圾邮件(并赋予值 +1);否则,它将被分类为正常邮件(我们将赋值 -1)。

从正式的角度来看,我们将此条件表示为如下(其中 θ 代表阈值):

前述条件不过是以下形式:

θ 阈值移到方程的左侧,并将其与 x[0] 变量关联(从而引入求和的 i = 0 位置索引),并为 x[0] 分配常规值 1,同时将权重 w[0] 设为 (即阈值的负值,跟随 θ 向方程左侧的位移)。因此,用 θ 替代乘积:

这样,我们对线性分类器的简洁公式就变成了最终的形式:

在这里,索引 现在取值从 0n

感知机是如何学习的

我们在本章中迄今为止描述的 Rosenblatt 感知机模型所采用的方法,基于对人脑神经元的简化描述。正如大脑的神经元在接收到正信号时会激活,否则保持静止一样,感知机通过激活函数使用阈值,该函数会分配一个 +1 值(在感知机激活的情况下,表示预设的阈值已被超过),或 -1 值(换句话说,表示未能超过阈值)。

重新审视前面确定感知机激活条件的数学表达式:

我们看到,它是 值的乘积(即对应权重的输入数据),必须超过 θ 阈值才能确定感知机是否激活。由于 x[i] 输入数据按定义是预先设定的,正是对应权重的值帮助判断感知机是否需要激活。

但在实际操作中,权重是如何更新的,从而确定感知机的学习过程呢?

感知机的学习过程可以总结为以下三个阶段:

  • 将权重初始化为预定义值(通常为 0

  • 计算每个相应训练样本的输出值

  • 基于预期输出值(即与相应输入数据的原始类别标签相关联的值,)和预测值(感知器估算的值)之间的距离来更新权重。

实际上,个别权重根据以下公式进行更新:

在这里,值表示预期(y)值与预测值之间的偏差

从前面的公式可以明显看出,预期的y值与预测值之间的偏差会被输入值的值和常数相乘,这个常数表示分配给感知器的学习率。常数通常取值在0.01.0之间,该值是在感知器初始化阶段指定的。

如我们所见,学习率的值对感知器的学习至关重要,因此有必要仔细评估(甚至通过反复试验)赋给常数的值,以优化感知器返回的结果。

一个基于感知器的简单垃圾邮件过滤器

现在我们将看到一个使用感知器的实际例子。我们将使用scikit-learn库创建一个基于感知器的简单垃圾邮件过滤器。我们将用来测试垃圾邮件过滤器的数据集来自于短信垃圾邮件消息集,数据集可在archive.ics.uci.edu/ml/datasets/sms+spam+collection找到。

原始数据集可以以 CSV 格式下载;我们处理了 CSV 文件中的数据,将其转化为数字值,以便感知器能够处理。此外,我们只选择了包含“buy”和“sex”关键词的消息(根据我们之前的描述),并计算了每条消息(无论是垃圾邮件还是正常邮件)中这些关键词在文本中出现的次数。

我们预处理后的结果保存在sms_spam_perceptron.csv文件中(附在本书的源代码库中)。

然后,使用pandas库加载sms_spam_perceptron.csv文件中的数据,从pandasDataFrame中提取相应的值,通过iloc()方法引用:

import pandas as pd
import numpy as np

df = pd.read_csv('../datasets/sms_spam_perceptron.csv')
y = df.iloc[:, 0].values
y = np.where(y == 'spam', -1, 1)
X = df.iloc[:, [1, 2]].values

因此,我们使用iloc()方法,将类标签hamspam(出现在DataFrame.csv文件的第一列中)分配给y变量(表示期望值的向量)。此外,我们使用 NumPy 的where()方法将之前提到的类标签转换为数值-1(对于垃圾邮件)和+1(对于正常邮件),以便我们能够在感知机中管理这些类标签。

同样地,我们将DataFramesexbuy列对应的值分配给了X矩阵,这些值是文本中与两个关键字对应的出现次数。这些值也是数字格式,因此可以将它们输入到我们的感知机中。

在创建感知机之前,我们将输入数据分为训练数据和测试数据:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

使用应用于Xy变量的train_test_split()方法,我们将数据集分成两个子集,将原始数据集的 30%(使用test_size = 0.3参数)分配给测试值,将剩余的 70%分配给训练值。

此时,我们可以通过实例化sklearn.linear_model包中的Perceptron类来定义我们的感知机:

from sklearn.linear_model import Perceptron
p = Perceptron(max_iter=40, eta0=0.1, random_state=0)
p.fit(X_train, y_train)

p感知机的初始化阶段,我们设置最大迭代次数为40(初始化时使用max_iter = 40参数)和学习率为0.1eta0 = 0.1)。最后,我们调用感知机的fit()方法,用训练数据训练p对象。

我们现在可以继续在测试数据上估计值,调用感知机的predict()方法:

y_pred = p.predict(X_test)

作为训练阶段的结果(训练数据占原始数据集的 70%),感知机现在应该能够正确估计测试数据子集(原始数据集剩余的 30%)的期望值。

我们可以使用scikit-learnsklearn.metrics包来验证感知机返回的估计值的准确性,方法如下:

from sklearn.metrics import accuracy_score
print('Misclassified samples: %d' % (y_test != y_pred).sum())
print('Accuracy: %.2f' % accuracy_score(y_test, y_pred))
Misclassified samples: 3
Accuracy: 0.90

通过将测试数据(y_test)与预测值(y_pred)进行比较,并统计所有的不匹配项,我们现在能够评估感知机提供的预测准确性。

在我们的例子中,准确率相当好(90%),因为错误分类的总数仅为三个。

感知机的优缺点

尽管感知器的实现相对简单(这里的简单性是该算法的优势,相较于预测结果的准确性),但它存在一些重要的局限性。作为一个本质上的二分类线性分类器,感知器只有在分析的数据是线性可分的情况下才能提供准确的结果;也就是说,可以识别一条直线(或在多维数据的情况下,一个超平面),将数据完全分割在笛卡尔平面中:

如果相反(在大多数实际情况中确实如此)分析的数据不可线性分离,感知器学习算法将围绕数据无限振荡,试图寻找一个可以线性分离数据的权重向量(但无法找到):

因此,感知器的收敛仅在数据线性可分且学习率较小的情况下才可能发生。如果数据的类别不可线性分离,那么设置最大迭代次数(对应于max_iter参数)非常重要,以防止算法在寻找(不存在的)最优解时无限振荡。

克服感知器实际限制的一种方法是接受更宽的间隔来分离数据。这正是 SVM 所采用的策略,我们将在下一节中讨论这一话题。

使用 SVM 进行垃圾邮件检测

SVM 是监督式算法的一个例子(与感知器一样),其任务是识别最佳分离数据类别的超平面,这些数据可以在多维空间中表示。然而,可以识别出不同的超平面来正确分离数据;在这种情况下,选择的超平面是优化预设间隔的,即超平面与数据之间的距离。

SVM 的一个优势是,所识别的超平面不限于线性模型(与感知器不同),如下图所示:

然而,SVM 可以被看作是感知器的扩展。在感知器的情况下,我们的目标是最小化分类错误,而在 SVM 的情况下,我们的目标则是最大化间隔,即超平面与训练数据之间的距离,并且该数据是距离超平面最近的(因此,最接近的训练数据被称为支持向量)。

SVM 优化策略

为什么一开始选择最大化边距的超平面?原因在于,更宽的边距对应于更少的分类错误,而更窄的边距则可能导致我们遭遇过拟合现象(当我们处理迭代算法时,过拟合是一个真正的灾难,正如我们将在讨论 AI 解决方案的验证和优化策略时所见)。

我们可以用数学术语来翻译 SVM 优化策略,类似于我们在感知机案例中所做的(感知机仍然是我们的起点)。我们定义了必须满足的条件,以确保 SVM 正确识别出将数据类分隔开的最佳超平面:

在这里,β常数表示偏置,而µ表示我们的边距(它假设最大可能的正值,以便在各个值的类别之间获得最佳分隔)。

实际上,在代数乘法(由 表示)中,我们添加了β偏置值,这使得我们能够在值落入相同的类标签时,得到一个大于或等于零的值(记住,y只能取-1+1的值,以区分样本所属的相应类别,正如我们在感知机案例中已经看到的那样)。

在此时,我们以这种方式计算出的值与 边距进行比较,以确保每个样本与我们识别出的分隔超平面(即构成我们的决策边界)之间的距离大于或等于我们的边距(正如我们所看到的,边距被确定为最大可能的正值,以便在各个值的类别之间获得最佳分隔)。

SVM 垃圾邮件过滤器示例

让我们回到我们的垃圾邮件过滤器示例,并将感知机替换为 SVM,正如我们在识别超平面时所看到的那样,我们并不仅限于使用线性分类器模型(可以选择具有更高复杂性的分类器)。

然而,为了比较之前与感知机得到的结果,感知机是一个严格的线性分类器,我们在 SVM 的情况下也选择了线性分类器。

然而,这一次,我们的数据集(存储在sms_spam_svm.csv文件中,并源自本章前面找到的 SMS 垃圾邮件消息集合,其中提取并比较了各种可疑关键词的总出现次数和消息中无害单词的总数)并不是严格线性可分的。

与感知机案例中相同,我们将使用pandas加载数据,将类标签与相应的-1值(表示垃圾邮件)和1值(表示正常邮件)关联起来:

import pandas as pd
import numpy as np
df = pd.read_csv('../datasets/sms_spam_svm.csv')
y = df.iloc[:, 0].values
y = np.where(y == 'spam', -1, 1)

一旦数据加载完毕,我们接着将原始数据集拆分为 30%的测试数据和 70%的训练数据:

from sklearn.model_selection import train_test_split

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

在这一点上,我们可以继续实例化我们的 SVM,导入SVC类(代表支持向量分类器)来自sklearn.svm包,选择线性分类器(kernel = 'linear'),然后通过调用fit()方法进行模型训练,最后通过调用predict()方法估计测试数据:

from sklearn.svm import SVC

svm = SVC(kernel='linear', C=1.0, random_state=0)
svm.fit(X_train, y_train)
y_pred = svm.predict(X_test)

我们现在可以通过使用sklearn.metrics包来评估 SVM 算法返回的预测准确性,就像我们在使用感知机时所做的那样:

from sklearn.metrics import accuracy_score

print('Misclassified samples: %d' % (y_test != y_pred).sum())
print('Accuracy: %.2f' % accuracy_score(y_test, y_pred))
Misclassified samples: 7
Accuracy: 0.84

即使在存在非线性可分数据的情况下,我们也可以看到 SVM 算法的表现如何,因为预测的准确度达到了 84%,而错误分类的案例仅为 7 个。

使用 SVM 进行图像垃圾邮件检测

SVM 算法的多功能性使我们能够处理更复杂的现实世界分类案例,例如以图像形式呈现的垃圾邮件,而不是简单的文本。

正如我们所看到的,垃圾邮件发送者非常清楚我们的检测尝试,因此会尝试采用各种可能的解决方案来欺骗我们的过滤器。规避策略之一是使用图像作为传播垃圾邮件的载体,而不是简单的文本。

然而,已有一段时间,基于图像的垃圾邮件检测解决方案已可行。在这些方案中,我们可以区分以下几种基于不同策略的检测方法:

  • 基于内容的过滤:这种方法的核心是尝试识别在文本垃圾邮件中常见的嫌疑关键词,即使在图像中也存在这些关键词;为此,采用模式识别技术并利用光学字符识别(OCR)技术来提取图像中的文本(这是 SpamAssassin 采用的解决方案)。

  • 非基于内容的过滤:在这种情况下,我们尝试识别垃圾邮件图像的特定特征(如颜色特征等),理由是垃圾邮件图像由于计算机生成,与自然图像相比显示出不同的特征;为了提取这些特征,我们采用基于神经网络(NN)和深度学习DL)的先进识别技术。

SVM 是如何诞生的?

一旦提取出图像的显著特征,并且将相应的样本归类到其对应类别(垃圾邮件或正常邮件)中,就可以利用 SVM 对这些特征进行模型训练。

关于这个主题的最新项目之一是图像垃圾邮件分析,由 Annapurna Sowmya Annadatha (scholarworks.sjsu.edu/etd_projects/486)完成,该项目以创新的方法为特点,基于以下假设:垃圾邮件图像的特征由于是计算机生成的,与由相机生成的图像的特征不同;并且选择性使用 SVM,从而在计算成本较低的情况下,实现了结果的高准确度。

该方法包括以下步骤:

  1. 使用线性 SVM 和特征集训练分类器

  2. 计算所有特征的 SVM 权重

  3. 选择第一个具有最大权重的

  4. 基于该子集创建模型

有关更多信息,请参阅上一段提到的项目参考资料。

使用逻辑回归和决策树进行钓鱼检测

在分析了感知机和 SVM 之后,我们现在处理使用逻辑回归和决策树的替代分类策略。

但在继续之前,我们将首先了解这些算法的独特特征以及它们在垃圾邮件检测和钓鱼检测中的应用,从回归模型开始。

回归模型

回归模型无疑是所有学习算法中最常用的。回归模型起源于统计分析,并迅速在机器学习和人工智能中得到应用。最为人熟知和使用的回归模型是线性回归,因为它的实现简单,并且在许多实际案例中(例如根据利率变化估算房价水平)具有良好的预测能力。

除了线性模型外,还有逻辑回归模型,特别是在数据处理过于复杂、线性模型过于僵化的情况下非常有用。因此,这两种模型代表了分析师和算法开发人员的首选工具。

在下一节中,我们将分析回归模型的特性和优势,以及它们在垃圾邮件检测领域的可能应用。让我们从最简单的模型——线性回归模型开始分析,这将帮助我们与逻辑回归模型进行比较。

介绍线性回归模型

线性回归模型的特点在于,数据表现为特征的和,从而在笛卡尔平面上形成一条直线。

从正式的角度看,线性回归可以用以下公式描述:

这里,y 代表预测值,它是单个特征(由 X 矩阵表示)的线性组合结果,应用了一个权重向量(由 w 向量表示),并加上一个常数 (β),这个常数代表在所有特征的值为零(或简单缺失)时的默认预测值。

β 常数也可以解释为模型的系统性偏差,并在图形上与笛卡尔平面垂直轴上的截距值对应(也就是说,回归线与垂直轴相交的点)。

显然,线性模型可以扩展到有多个特征的情况。在这种情况下,数学公式化的形式如下:

之前公式的几何表示将对应于 n 维空间中的超平面,而不是笛卡尔平面中的直线。我们已经提到过, 常数作为模型在特征值为零时的默认预测值,具有重要性。

权重向量中的各个 值,,可以解释为相应特征的重要性度量,[.]

实际上,如果 权重值接近零,则对应的 特征在预测值的确定中具有最小重要性(或者根本没有重要性)。相反,如果 权重为正值,它将放大回归模型返回的最终值。

另一方面,如果 取负值,它将有助于逆转模型预测的方向,因为当 特征增加时,它将对应回归模型估算值的下降。因此,考虑权重对 特征的影响非常重要,因为它们是我们从回归模型中推导出预测正确性的决定性因素。

使用 scikit-learn 进行线性回归

在下面的代码片段中,我们将看到如何基于线性回归实现一个简单的预测模型,使用 scikit-learnlinear_model 模块,并以之前使用的垃圾邮件数据集之一作为输入:

import pandas as pd
import numpy as np

df = pd.read_csv('../datasets/sms_spam_perceptron.csv')
X = df.iloc[:, [1, 2]].values
y = df.iloc[:, 0].values
y = np.where(y == 'spam', -1, 1)

from sklearn.linear_model import LinearRegression

linear_regression = LinearRegression()
linear_regression.fit(X,y)
print (linear_regression.score(X,y))

为了验证线性回归模型提供的预测的准确性,我们可以使用 score() 方法,该方法给我们提供 R² 判定系数的度量。

这个系数在01之间变化,用来衡量当与简单均值相比时,线性模型返回的预测有多好。

线性回归 – 优缺点

如我们所见,线性回归模型的实现简单性代表了其无可争议的优势。然而,模型的局限性也相当显著。

实际上,线性回归模型只能用来处理定量数据,而在使用分类数据进行预测分析的情况下,我们必须借助逻辑回归模型。此外,线性回归的主要局限性在于模型假设特征之间大多不相关;也就是说,特征之间不会相互影响。这一假设使得我们可以将特征及其各自权重的乘积表示为独立项的和。

然而,确实存在一些实际情况,其中这一假设并不现实(例如,人的年龄与体重之间可能存在关系,体重会随年龄变化)。这一假设的负面副作用是,我们可能会多次添加相同的信息,无法正确预测变量组合对最终结果的影响。

从技术角度看,线性回归模型的特点是预测中具有较大的偏差,而不是较大的方差(稍后我们将有机会讨论偏差和方差之间的权衡)。

换句话说,当被分析的数据表现出复杂的关系时,线性回归模型会导致我们做出系统性的失真预测。

逻辑回归

我们已经看到,线性回归的一个限制是它不能用来解决分类问题:

实际上,如果我们想使用线性回归对属于两个类别的样本进行分类(如垃圾邮件检测中的情况),这些样本的标签由数值表示(例如,-1表示垃圾邮件+1表示正常邮件),线性回归模型将试图识别与目标值最接近的结果(也就是说,线性回归的目标是最小化预测误差)。这种行为的负面副作用是,它会导致更大的分类误差。相对于感知器,线性回归在分类准确性方面无法给出良好的结果,正是因为线性回归在处理值的连续区间时效果较好,而不是处理离散值的类别(如分类中的情况)。

一种替代策略,更适用于分类目的,是估算样本属于各个类别的概率。这是逻辑回归所采用的策略(尽管名字中有“回归”二字,但它实际上是一个分类算法,而不是回归模型)。

逻辑回归的数学公式如下:

这里, 因此衡量的是给定样本属于 类别的条件概率,前提是已知 特征。

使用逻辑回归的钓鱼检测器

然后我们可以使用逻辑回归来实现一个钓鱼检测器,利用逻辑回归特别适用于解决分类问题的特点。像垃圾邮件检测一样,钓鱼检测不过是一个样本分类任务。

在我们的示例中,我们将使用 UCI 机器学习库网站提供的数据集(archive.ics.uci.edu/ml/datasets/Phishing+Websites)。

数据集已经从原始的.arff格式转换为 CSV 格式,采用了被称为独热编码的数据整理技术(en.wikipedia.org/wiki/One-hot),包含了 30 个特征,用于表征钓鱼网站。

在以下代码块中找到我们检测器的源代码:

import pandas as pd
import numpy as np
from sklearn import *
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

phishing_dataset = np.genfromtxt('../datasets/phishing_dataset.csv',
delimiter=',', dtype=np.int32)

samples = phishing_dataset[:,:-1]

targets = phishing_dataset[:, -1]

from sklearn.model_selection import train_test_split

training_samples, testing_samples, training_targets, testing_targets = train_test_split(samples, targets, test_size=0.2, random_state=0)

log_classifier = LogisticRegression()

log_classifier.fit(training_samples, training_targets)

predictions = log_classifier.predict(testing_samples)
accuracy = 100.0 * accuracy_score(testing_targets, predictions)

print ("Logistic Regression accuracy: " + str(accuracy))

Logistic Regression accuracy: 91.72320217096338

如我们所见,逻辑回归分类器的准确率相当不错,因为该模型能够正确检测出超过 90%的 URL。

逻辑回归的优缺点

采用逻辑回归的优势可以总结如下:

  • 该模型可以非常高效地训练

  • 即使在特征数量较多的情况下,它也能有效使用

  • 由于其评分函数的简洁性,该算法具有很高的可扩展性

然而,逻辑回归也存在一些重要的局限性,这些局限性源于其基本假设,比如要求特征必须是线性独立的(这一规则在技术上称为不存在多重共线性),并且通常需要比其他竞争算法更多的训练样本,因为逻辑回归中采用的最大似然准则比起线性回归中用于最小化预测误差的最小二乘法来说,较为无力。

使用树进行决策

如我们在前述段落中所见,当我们需要选择使用哪种算法来执行某项任务时,必须考虑构成数据的特征类型。特征实际上可以由定量值或定性数据组成。

机器学习算法显然更擅长处理定量值;然而,大多数实际情况涉及使用以定性形式(如描述、标签、单词等)表示的数据,这些数据包含了非数字形式的信息。

正如垃圾邮件检测的例子,我们已经看到如何将定性特征(如垃圾邮件和正常邮件标签,分别赋予数值-1+1)转换为数值形式(这种做法称为数值编码),但它仅部分解决了分类问题。

这并非偶然,约翰·罗斯·昆兰(John Ross Quinlan)在论文《决策树的归纳》(Induction of Decision Trees)中描述了决策树算法,这篇论文考虑了以定性形式传递的信息。昆兰的论文(他的贡献对决策树的发展至关重要)实际上是选择是否在户外打网球,基于如天气(晴天、多云或下雨)、温度(凉爽、温和或炎热)、湿度(高或正常)、风速(真或假)等特征。

我们如何指示机器处理同时以定量和定性形式呈现的信息?

决策树的基本原理

决策树使用二叉树来分析和处理数据,从而成功地对以数值和类别形式表示的值做出预测,接受数值型和定性信息作为输入数据。

为了直观理解决策树所采用的策略,下面我们来看一看其实现中的典型步骤:

  1. 第一步是将原始数据集划分为两个子集,在验证二元条件后,进行第一次划分,结果会得到两个子集,在这两个子集中二元条件被验证或否定。

  2. 子集将根据进一步的条件继续细分;在每一步中,选择能够提供最佳二分划分的条件(为此,使用适当的度量标准来衡量划分质量)。

  3. 划分过程是递归进行的。因此,必须定义一个停止条件(例如,达到最大深度)。

  4. 在每次迭代中,算法生成一棵树结构,其中子节点代表每一步所做的选择,每个叶子节点则有助于输入数据的整体分类。

请查看下图,它展示了 Iris 数据集的决策树:

决策树在处理大型数据集方面也非常高效。事实上,树形数据结构的特点使我们能够将算法的复杂度限制在一个数量级为0log n)的范围内。

使用决策树进行钓鱼攻击检测

现在我们来看决策树在钓鱼攻击检测任务中的应用。如前所述,钓鱼攻击检测(以及垃圾邮件过滤)基本上涉及输入数据的分类:

import pandas as pd
import numpy as np
from sklearn import *
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

phishing_dataset = np.genfromtxt('../datasets/phishing_dataset.csv', delimiter=',', dtype=np.int32)

samples = phishing_dataset[:,:-1]
targets = phishing_dataset[:, -1]

from sklearn.model_selection import train_test_split

training_samples, testing_samples, training_targets, testing_targets =
train_test_split(samples, targets, test_size=0.2, random_state=0)

from sklearn import tree

tree_classifier = tree.DecisionTreeClassifier()

tree_classifier.fit(training_samples, training_targets)

predictions = tree_classifier.predict(testing_samples)
accuracy = 100.0 * accuracy_score(testing_targets, predictions)

print ("Decision Tree accuracy: " + str(accuracy))

Decision Tree accuracy: 96.33649932157394

我们可以看到,决策树分类器进一步增强了之前通过逻辑回归获得的优秀性能。

决策树——优缺点

除了已经描述的优点外,我们还必须记住与决策树相关的可能缺点;这些缺点主要与过拟合现象有关,这是由于树形数据结构的复杂性(实际上,必须通过系统化的方式进行树的修剪,以减少其整体复杂性)。

复杂性带来的一个不利后果是算法对训练数据集中的细微变化非常敏感,这可能会对预测模型产生显著影响。因此,决策树不适合增量学习。

使用朴素贝叶斯进行垃圾邮件检测

朴素贝叶斯的一个优势是,它只需要很少的初始数据就可以开始对输入数据进行分类;此外,随着信息的逐步增加,它有助于动态更新先前的估计,逐步改善预测模型(与我们在前一段看到的基于决策树的算法不同)。

朴素贝叶斯在垃圾邮件检测中的优势

上述特性非常适合垃圾邮件检测任务。事实上,在不需要大量数据集的情况下,基于朴素贝叶斯的垃圾邮件检测算法可以利用收件箱中已经存在的邮件,不断地根据逐渐加入的新邮件更新概率估计。

不断更新概率估计的过程基于著名的贝叶斯规则:

上述方程描述了事件A发生的概率与证据E之间的关系。

这个关系取决于A(先验概率)和证据E的似然性!,后者决定了概率估计!(后验概率)。

贝叶斯规则概率更新的一个重要特性是,经过更新过程后,概率 (后验概率)会成为新的先验概率,从而有助于动态更新现有的概率估计。

为什么选择朴素贝叶斯?

贝叶斯规则的基本假设之一是它假设事件之间是独立的。这个假设并不总是现实的。

然而,在大多数情况下,这是一个合理的条件,有助于做出良好的预测,同时简化了贝叶斯法则的应用,特别是在多个竞争事件的情况下,从而将计算简化为对每个事件相关概率的简单相乘。

在实际使用朴素贝叶斯算法进行垃圾邮件检测之前,我们需要分析文本分析技术,以使朴素贝叶斯能够动态地识别垃圾邮件发送者使用的可疑关键词(而不是像之前的示例那样以固定的方式选择它们)。

NLP 来拯救我们

AI 最令人兴奋的领域之一无疑是自然语言处理(NLP),它包括对人类语言的分析和自动化理解。

NLP 的目的是尝试从非结构化数据中提取有意义的信息(例如电子邮件、推文和 Facebook 帖子)。

NLP 的应用领域非常广泛,从实时翻译到情感分析和语音识别等。

NLP 步骤

NLP 的主要阶段如下:

  1. 识别构成语言的单词(标记)

  2. 文本结构分析

  3. 识别单词之间的关系(例如段落、句子等)

  4. 文本的语义分析

最著名的 Python 自然语言处理库之一是自然语言工具包NLTK),它常用于垃圾邮件检测。

在下面的示例中,我们将看到如何利用 NLTK 结合朴素贝叶斯算法来创建一个垃圾邮件检测器。

使用 NLTK 的贝叶斯垃圾邮件检测器

作为结束示例,我们将展示如何使用基于朴素贝叶斯的分类器,采用来自sklearn.naive_bayes模块的MultinomialNB。和往常一样,我们将把原始数据集(包含 CSV 格式的垃圾邮件消息存档)划分为测试数据子集和训练数据子集,其中 30%为测试数据,70%为训练数据。

数据将采用词袋模型BoW)技术处理,该技术使用sklearnCountVectorizer为文本中识别的每个单词分配一个编号,并传入get_lemmas()方法,该方法返回从消息文本中提取的单个标记。

最后,我们将使用TfidfTransformer对数据进行标准化和加权,它将计数矩阵转换为标准化的tftf-idf表示。

在 scikit-learn 文档中关于TfidfTransformer的说明(scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfTransformer.html),我们可以找到如下内容:

“Tf 代表词频,而 tf-idf 代表词频与逆文档频率的乘积。这是一种在信息检索中常用的词项加权方案,且在文档分类中也得到了良好的应用。使用 tf-idf 而非原始的词项在特定文档中出现的频率的目的是缩小那些在给定语料库中频繁出现的词项的影响,这些词项经验上比那些在训练语料库中出现频率较低的特征提供的讯息更少

让我们进入源代码:

import matplotlib.pyplot as plt
import csv
from textblob import TextBlob
import pandas
import sklearn
import numpy as np

import nltk

from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, accuracy_score
from sklearn.model_selection import train_test_split 

from defs import get_tokens
from defs import get_lemmas

sms = pandas.read_csv('../datasets/sms_spam_no_header.csv', sep=',', names=["type", "text"])

text_train, text_test, type_train, type_test = train_test_split(sms['text'], sms['type'], test_size=0.3)

# bow stands for "Bag of Words"
bow = CountVectorizer(analyzer=get_lemmas).fit(text_train)

sms_bow = bow.transform(text_train)

tfidf = TfidfTransformer().fit(sms_bow)

sms_tfidf = tfidf.transform(sms_bow)

spam_detector = MultinomialNB().fit(sms_tfidf, type_train)

我们可以通过尝试对一个随机消息进行预测来检查 spam_detector 是否正常工作(在我们的示例中,我们选择了数据集中的第 26 条消息),并通过将预测值与消息的对应 type 标签进行比较,检查检测器是否正确分类了消息类型(垃圾邮件或正常邮件):

msg = sms['text'][25]
msg_bow = bow.transform([msg])
msg_tfidf = tfidf.transform(msg_bow)

print ('predicted:', spam_detector.predict(msg_tfidf)[0])
print ('expected:', sms.type[25])

predicted: ham
expected: ham

到此为止,一旦验证了正确功能,我们就可以对整个数据集进行预测:

predictions = spam_detector.predict(sms_tfidf)
print ('accuracy', accuracy_score(sms['type'][:len(predictions)], predictions))
accuracy 0.7995385798513202

前述命令生成了以下输出:

如前面的截图所示,朴素贝叶斯的准确率已经相当高(达到 80%),与其他算法不同的是,随着分析的消息数量增加,这个准确率还可以进一步提高。

总结

在本章中,我们解释了几种监督学习算法,并展示了它们在解决网络安全领域常见任务中的具体应用,例如垃圾邮件检测和钓鱼邮件检测。

本章获得的知识有助于培养正确的思维方式,以应对越来越复杂的任务,例如我们将在接下来的章节中面临的任务,从而更清晰地认识到每种 AI 算法的优缺点。

在下一章中,我们将学习恶意软件分析以及通过深度学习进行高级恶意软件检测。

第五章:恶意软件威胁检测

恶意软件和勒索软件代码的广泛传播,加上同一威胁的不同变种(多态和变形恶意软件)中的快速多态变异,已使得基于图像文件签名和哈希的传统检测方案过时,而这些传统方案是大多数常见杀毒软件所依赖的。

因此,越来越有必要采用机器学习ML)解决方案,这些方案可以快速筛选(分诊)威胁,集中精力避免浪费稀缺资源,比如恶意软件分析师的技能和精力。

本章将涵盖以下主题:

  • 介绍恶意软件分析方法

  • 如何区分不同的恶意软件家族

  • 决策树恶意软件检测器

  • 使用隐马尔可夫模型HMMs)检测变种恶意软件

  • 基于深度学习的高级恶意软件检测

恶意软件分析一瞥

对于那些接触恶意软件分析的人员来说,一个最有趣的方面是学会区分,例如,合法的二进制文件与那些可能危害计算机及其数据完整性的文件。我们泛指二进制文件,而非可执行文件(即扩展名为 .exe.dll 的文件),因为恶意软件甚至可以隐藏在看似无害的文件中,如图像文件(扩展名为 .jpg.png 的文件)。

同样,文本文件(例如 .docx.pdf)也可能成为健康载体或软件感染的传播媒介,尽管它们的非可执行文件格式。此外,恶意软件传播的第一阶段(无论是在家庭 PC 还是公司局域网中)通常通过破坏被攻击计算机内部文件的完整性来实现。

因此,能够有效地识别恶意软件的存在至关重要,以便预防或至少限制其在组织内的传播。

以下是常用的分析策略(及相关工具),用于对通过假冒链接、垃圾邮件、钓鱼等方式传播的文件和软件进行初步调查,以识别那些潜在危险的文件。

为了实现这一目标,我们需要更深入地审视传统的静态和动态恶意软件分析方法。

用于恶意软件检测的人工智能

随着与新恶意软件每日传播相关的威胁数量几乎呈指数级增长,单纯依靠人工操作员进行分析,几乎不可能有效应对这些威胁。

因此,有必要引入一些算法,至少能够自动化恶意软件分析的准备阶段(称为分诊,源自第一次世界大战中医生采用的相同实践,目的是选择最有可能生还的伤员进行治疗)。也就是说,通过对待分析的恶意软件进行初步筛选,可以使恶意软件分析师及时有效地应对真实的网络威胁。

这些算法实际上是采用 AI 工具的形式,考虑到网络安全的动态性,AI 工具被定义为具备适应能力。事实上,机器需要能够有效应对,并适应与前所未有的威胁传播相关的上下文变化。

这不仅意味着分析师操作恶意软件分析的工具和方法(这显而易见),还意味着他们能够解读算法的行为,并意识到机器所做的选择。

因此,恶意软件分析师需要理解机器学习所遵循的逻辑,并在自动化分析结果的基础上(直接或间接地)参与微调(精细调整)相关的学习过程。

恶意软件有许多不同的名称

恶意软件的类型繁多,每天都有新的威胁形式出现,这些新威胁巧妙地重新利用了以往的攻击方式,或者采用了完全新的危害策略,利用目标组织的特定特点(在高级持续性威胁APTs)中,这些是量身定制的攻击形式,完美地适应目标受害者)。这一切仅受攻击者想象力的限制。

然而,可以编制出最常见恶意软件类型的分类,以便了解哪些是最有效的预防措施,并对它们在应对每种恶意软件时的有效性进行对比:

  • Trojans:看似合法且无害的可执行文件,但一旦启动,它们会在后台执行恶意指令

  • Botnets:一种旨在危害尽可能多主机的恶意软件,通过控制网络中的主机,将其计算能力服务于攻击者

  • Downloaders:恶意软件,从网络上下载恶意库或代码片段,并在受害者主机上执行它们

  • Rootkits:一种在操作系统层面危害主机的恶意软件,因此通常以设备驱动程序的形式出现,使得各种反制措施(例如安装在终端上的杀毒软件)变得无效

  • Ransomwares:恶意软件,它会加密存储在主机机器上的文件,并要求受害者支付赎金(通常是比特币)以获取解密密钥,用于恢复原始文件

  • APT(高级持续性威胁):APT 是利用受害主机上特定漏洞进行定制化攻击的一种形式。

  • 零日(0 天):利用尚未公开给研究人员和分析师社区的漏洞的恶意软件,其特征和安全影响尚未为人所知,因此无法被杀毒软件检测到。

显然,这些不同类型的威胁可能会因为它们能在同一个恶意文件中混合在一起而被放大(例如,一个看似无害的木马一旦执行后变成真正的威胁,它作为下载程序连接到网络并下载恶意软件,如 rootkit,进而危害本地网络并将其转化为僵尸网络)。

行业内的恶意软件分析工具

常用的恶意软件分析工具可以按以下方式分类:

  • 反汇编工具(如 Disasm 和 IDA)

  • 调试工具(如 OllyDbg、WinDbg 和 IDA)

  • 系统监控工具(如 Process Monitor 和 Process Explorer)

  • 网络监控工具(如 TCP View、Wireshark 和 tcpdump)

  • 解包工具和打包器识别工具(如 PEiD)

  • 二进制和代码分析工具(如 PEView、PE Explorer、LordPE 和 ImpREC)

恶意软件检测策略

显然,每种类型的威胁都需要特定的检测策略。在本节中,我们将介绍传统上由恶意软件分析师手动执行的恶意软件检测分析方法。这些方法提供了对分析阶段的更详细理解,可以通过引入 AI 算法来提高效率,进而减轻人工分析师的重复性或繁重任务,让他们专注于分析中最特殊或最不寻常的方面。

应当强调的是,恶意软件的开发是攻击者进行的创造性活动的结果,因此,难以归类为预设的模式或固定的方式。同样,恶意软件分析师必须依靠所有的创造性资源,并开发非常规程序,以便在一种猫鼠游戏中始终领先于攻击者。

因此,恶意软件分析应被视为一种艺术而非科学,因此,它要求分析师能够不断地想出新的检测方法,以便及时识别未来的威胁。因此,恶意软件分析师需要不断更新他们的技术技能,并改进他们的调查方法。

事实上,确实可以通过采取常见的分析方法来启动检测活动,特别是用于检测已知威胁的存在。

为此,在最常见的恶意软件检测活动中,我们可以包括以下恶意软件检测活动:

  • 哈希文件计算:用于识别已经存在于知识库中的已知威胁

  • 系统监控:识别硬件和操作系统的异常行为(例如,CPU 周期的异常增加、特别大的磁盘写入活动、注册表项的变化,以及在系统中创建新的和未经请求的进程)

  • 网络监控:识别主机机器与远程目的地之间建立的异常连接

这些检测活动可以通过使用特定的算法轻松实现自动化,正如我们将在稍后讨论恶意软件分析方法时所看到的。

静态恶意软件分析

恶意软件分析的第一步是评估二进制文件中可疑构件的存在,而无需实际运行(执行)代码。

该阶段使用的技术的复杂性被称为静态恶意软件分析

静态恶意软件分析包括以下内容:

  • 识别被认为对分析有价值的目标

  • 理解可执行指令的流

  • 识别已知的模式并将其与可能的恶意软件关联(也称为恶意软件检测

为此,分析工具和程序被用于执行以下功能:

  • 识别对系统 API 的调用

  • 解码和操作字符串数据以获取敏感信息(例如,域名和 IP 地址)

  • 通过下载其他恶意代码检测恶意软件的存在和调用(例如,命令与控制C2)、后门和反向 Shell)

静态分析方法

静态恶意软件分析采用的方法是检查恶意软件反汇编二进制镜像中的机器指令(汇编指令),以识别其潜在危害,并评估二进制代码的外部特征,然后再进行执行。

静态恶意软件分析的难点

静态恶意软件分析中最狡猾的方面之一是确定恶意软件反汇编的正确性存在困难。鉴于反分析技术的日益普及,不能总是假设反汇编器生成的反汇编二进制镜像是可靠的。因此,分析员必须进行初步分析,以检测例如,存在加密可执行代码部分的打包器。

这些初步分析程序常常被分析员忽视,因为它们在所需时间上非常昂贵;然而,它们对于限定需要执行的相关目标是不可或缺的。

此外,如果无法正确检测到可执行代码的部分(可能是因为它们隐藏在被认为是无害的数据中,例如表示图像的资源),这一缺陷可能会破坏后续的动态分析阶段,使得无法识别正在调查的恶意软件的确切类型。

如何进行静态分析

一旦验证反汇编后的恶意软件是可靠的,就可以采用不同的方式继续:每个分析员实际上都遵循自己首选的策略,这些策略基于他们的经验和他们打算追求的目标。

原则上,可采纳的策略如下:

  • 以系统化的方式分析二进制指令,而不执行它们。这是一种有效的技术,适用于代码的有限部分,在大规模恶意软件的情况下变得复杂,因为分析员必须跟踪每条指令分析时的数据状态。

  • 扫描指令,寻找被认为有意义的序列,设置断点并部分执行程序直到断点,然后检查此时程序的状态。这种方法通常用于确定被认为危险的系统调用的存在,基于这些调用的调用顺序(例如,连接到网络、创建文件和修改系统注册表的序列是恶意软件下载程序最常见的系统 API 调用序列之一)。

  • 同样,也可以检测某些 API 调用的缺失。一个没有调用系统函数(例如与网络相关的函数)的代码,显然无法代表一个后门(但它可能充当键盘记录器,例如,它调用系统 API 序列来检测按下的键,并将其写入磁盘)。

  • 在反汇编图像中查找敏感信息(如域名和 IP 地址)字符串格式。同样,在这种情况下,可以在与网络调用对应的位置设置调试器断点,并检测恶意软件连接到互联网时所联系的任何域名或远程 IP 地址。

静态分析的硬件要求

与动态分析不同,静态分析通常在硬件方面需要较少的特定资源,因为原则上分析员不会执行正在分析的恶意代码。

正如我们将看到的,在动态恶意软件分析的情况下,可能需要非凡的硬件要求,有时仅使用虚拟机是不够的。这是由于恶意软件实施的反分析措施(反分析技巧),如果检测到虚拟机的存在,就会阻止代码的执行。

动态恶意软件分析

如我们所见,静态恶意软件分析的具体特征包括以下几点:

  • 验证给定的二进制文件是否真的具有恶意性质。

  • 在不启动执行的情况下,尽可能识别二进制文件的更多信息,并基于可以回溯的特征(如文件格式特征或存储在其中的资源特征)进行分析。

  • 通过计算可疑二进制文件的哈希值来对其进行目录编制,哈希值即该文件的签名(这个签名也可以在恶意软件分析师社区中共享,以便更新恶意软件威胁的整体知识库)。

  • 毫无疑问,静态恶意软件分析虽然快速,但存在一系列方法学上的局限性,特别是在分析复杂类型的恶意软件(如 APT 攻击和变种恶意软件)时。这些方法学上的局限性的一种解决方法是将其与动态恶意软件分析结合,以期更深入地理解所分析的恶意软件的性质和类型。

动态恶意软件分析的独特之处在于,与静态恶意软件分析不同,二进制文件需要被执行(通常是在隔离和保护的环境中进行,称为恶意软件分析实验室,这些环境利用沙箱和虚拟机来防止恶意软件在企业网络中广泛传播)。

因此,这一策略涉及到分析动态行为,也就是说,例如验证恶意可执行文件是否从互联网下载恶意库或代码片段(有效载荷),或者是否每次执行时都会修改自身的可执行指令,从而使基于签名的检测程序(例如杀毒软件使用的方式)失效。

反分析技巧

恶意软件开发者通常采取的对策是加密有效载荷,使用加壳工具、下载器等,这些措施可以防止恶意软件分析或使其变得更加困难。

这些技巧通常可以通过动态恶意软件分析来检测;然而,即使是动态恶意软件分析,也存在一些局限性,尤其是当使用虚拟机时——例如,恶意软件可以通过利用某些执行技巧轻松检测虚拟机的存在,具体如下:

  • 执行期望默认行为的指令:恶意软件可以计算执行某些操作时所花费的时间,如果这些操作执行得比预期的慢,它可以推断执行环境为虚拟机。

  • 基于硬件的虚拟机检测:通过执行一些特定的硬件级别指令(例如,访问 CPU 保护寄存器的指令,如sldtsgdtsidt)。

  • 访问某些注册表键值,如HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Disk\Enum

当恶意软件检测到虚拟机的存在时,它会停止按预期的方式工作,从而逃避分析人员的检测。

获取恶意软件样本

在我们的分析过程中,我们主要参考为微软 Windows 平台开发的恶意软件代码,因为考虑到该平台的普及,我们有大量的样本可以使用。

无论如何,一个常见的问题是:我们可以从哪里获得恶意软件样本?

有多个在线资源可以下载恶意软件样本,包括以下几个:

也可以通过配置蜜罐(甚至只是收集自己电子邮件账户中收到的垃圾邮件)来获取恶意软件样本,进而创建自己的样本数据集。

一旦我们有了恶意软件数据集,就有必要进行初步的特征分析,利用自动化恶意软件分析活动的脚本。

正如我们预期的,在我们的分析中,我们将重点关注为微软 Windows 平台开发的恶意软件代码。为了进一步进行分析,我们需要了解该平台采用的可执行文件格式,这就是所谓的便携式可执行文件PE)格式。

每一个微软平台的可执行文件,无论是.exe.dll还是.sys扩展名的文件(在设备驱动程序的情况下),为了能够加载到运行时内存中并由 Windows 操作系统执行,必须遵循 PE 文件格式中包含的必要规范。

我们将很快检查这一文件格式,演示如何从可执行文件中提取存储在 PE 文件格式中的特征,以便创建一个数据集,该数据集将用于训练我们的 AI 算法。

破解 PE 文件格式

在我们对 PE 文件格式的分析中,我们将使用PEView(在线提供,网址为wjradburn.com/software/PEview.zip),这是一个非常简单但有效的工具,用于可视化PE 结构。正如我们所说,PE 是 Windows 操作系统上执行的二进制镜像的标准文件格式。

实际上,当Windows 操作系统加载程序将可执行文件(不仅限于.exe,还包括.dll.sys)加载到运行时内存中时,它会执行 PE 部分中找到的加载指令,以便加载二进制镜像。

因此,PE 文件格式的工件仍然是恶意软件开发人员和病毒编写者的主要目标之一。

PE 文件格式作为潜在的感染向量

正如我们将看到的,PE 可执行文件在二进制文件镜像中包含多个部分,这一特性可以被利用来隐藏恶意软件。

实际上,每个 PE 段可以被视为一个文件夹,托管各种二进制对象(从图形文件到加密库),这些对象会在运行时执行和/或解密,可能感染同一台计算机上的其他可执行文件或网络上的远程计算机。

例如,PE 段可能包含一个 .sys(恶意驱动程序)文件,旨在危害内核,以及一个包含配置参数的启动文件,或二进制文件可以连接的远程链接,以便下载其他激活工件、C2 后门等。

PE 文件格式概述

PE 规范源自于 Unix 通用对象文件格式 (COFF),它基本上是一个 数据结构,涵盖了 Windows 操作系统加载器 管理可执行映像所需的信息,也就是说,当其结构映射到运行时内存中并被操作系统执行时。

简单来说,PE 文件由 PE 文件头段表(段头)组成,接着是 段数据

PE 文件头 被封装在 Windows NT 头 结构中(在 winnt.h 头文件中定义,连同其他 C 结构),并由以下内容组成:

  • MS DOS 头

  • PE 签名

  • 映像文件头

  • 可选头

文件头 后面跟着 段头

图片来源:https://commons.wikimedia.org/wiki/File:RevEngPEFile.JPG

段头 提供有关其关联段的信息,包括位置、长度和特性。一个段是 PE 文件中代码或数据的基本单位。

不同的功能区域,如代码区和数据区,逻辑上被分隔成多个段。

此外,一个映像文件可以包含多个段,如 .tls.reloc,这些段有特殊用途。

段头提供有关其关联段的信息。可执行文件中最常见的段包括文本、数据、RSRC、RData 和 RELOC。

大多数 Windows 可执行文件包含资源:这是一个通用术语,指代像光标、图标、位图、菜单和字体等对象。PE 文件可以包含一个资源目录,包含该文件中的程序代码所使用的所有资源。

恶意软件很少使用图形资源,因此它们的资源总数相对于良性软件较少。

PE 文件的许多字段没有强制约束。PE 文件中有许多冗余字段和空白,这可能为恶意软件隐藏创造机会。

在以下截图中,我们执行 PEView 并将其 .exe 映像加载到内存中;工具部分显示了其 PE 格式的各个段。

我们还概述了 DOS 头的特殊e_magic字段,通常包含 MZ 字符序列(对应的字节序列是"0x4D 0x5A"),以及 PE 头的特殊Signature字段(定义为IMAGE_NT_HEADERS结构),其中包含PE字符序列,表示该二进制文件是一个原生 Windows 可执行文件:

DOS 头和 DOS 存根

DOS 头仅用于向后兼容,并且位于显示错误信息的 DOS 存根之前,提示程序可能无法在 DOS 模式下运行。

根据官方 PE 文档(可在docs.microsoft.com/en-us/windows/desktop/debug/pe-format#ms-dos-stub-image-only查看),MS-DOS 存根使得 Windows 能够正确执行该镜像文件,即使它包含一个 MS-DOS 存根。

它位于EXE镜像的前面,并在镜像以 MS-DOS 模式运行时显示消息该程序无法在 DOS 模式下运行

DOS 头包括一些用于向后兼容的字段,定义如下:

typedef struct _IMAGE_DOS_HEADER {      
// DOS .EXE header
    WORD   e_magic;                     
// Magic number
    WORD   e_cblp;                      
// Bytes on last page of file
    WORD   e_cp;                        
// Pages in file
    WORD   e_crlc;                      
// Relocations
    WORD   e_cparhdr;                   
// Size of header in paragraphs
    WORD   e_minalloc;                  
// Minimum extra paragraphs needed
    WORD   e_maxalloc;                  
// Maximum extra paragraphs needed
    WORD   e_ss;                        
// Initial (relative) SS value
    WORD   e_sp;                        
// Initial SP value
    WORD   e_csum;                      
// Checksum
    WORD   e_ip;                        
// Initial IP value
    WORD   e_cs;                        
// Initial (relative) CS value
    WORD   e_lfarlc;                    
// File address of relocation table
    WORD   e_ovno;                      
// Overlay number
    WORD   e_res[4];                    
// Reserved words
    WORD   e_oemid;                     
// OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   
// OEM information; e_oemid specific
    WORD   e_res2[10];                  
// Reserved words
    LONG   e_lfanew;                    
// File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

PE 头结构

在 DOS 头和 DOS 存根之后,我们会找到 PE 头。

PE 头包含关于不同部分的信息,这些部分用于存储代码和数据,以及请求的其他库(DLL)中的导入项或提供的导出项,以防该模块实际上是一个库。请查看以下 PE 头结构:

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

FileHeader结构字段描述文件的格式(即内容、符号等),其类型在以下结构中定义:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

OptionalHeader字段包含有关可执行模块的信息,包括所需的操作系统版本、内存要求和itsentry点(即实际执行开始的相对内存地址):

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

OptionalHeader中包含的特殊AddressOfEntryPoint字段指定了可执行文件的入口点,通常设置为0x1000的相对内存地址,如下截图所示:

数据目录

DataDirectory结构字段包含IMAGE_NUMBEROF_DIRECTORY_ENTRIES条目,定义了模块的逻辑组件。相对条目按编号并定义如下:

导入和导出表

导入表列出了所有需要在加载时从其他 DLL 中解析和导入的符号:

大多数类型的良性软件在导入地址表中有大量条目,因为它们有复杂的功能,并从导入地址表中导入不同的 Windows API 函数:

Windows 还允许程序通过 LoadLibraryFreeLibrary 显式加载和卸载 DLL,以及使用 GetProcAddress(由 kernel32.dll 提供)查找符号的地址。

大多数类型的恶意软件使用后者的方法,因此它们的导入表中的符号数量比良性软件少。

导出表包含关于其他 PE 文件可以通过动态链接访问的符号的信息。导出的符号通常位于 DLL 文件中,而大多数类型的恶意软件没有导出符号。

大多数类型的恶意软件通过 LoadLibraryFreeLibrary 显式加载和卸载 DLL,以隐藏其恶意目的。

然而,有一个显著的例外:恶意软件通常会导入 wsock32.dll,而良性软件则很少导入这个 DLL,这解释了恶意软件如何通过网络连接进行传播和破坏。

提取数据集中的恶意软件痕迹

在分析完 PE 文件格式后,我们现在可以提取二进制文件(无论是合法的还是可疑的)的特征,并将它们存储在一个痕迹数据集中,用于训练我们的算法。

为此,我们将开发 Python 脚本,以自动化提取我们分析的每个文件的 PE 文件格式字段。

我们将在脚本中使用的 Python 库是著名的 pefile 库,该库由 Ero Carrera 开发,并可在 github.com/erocarrera/pefile 上获取。

一旦包含库的压缩包下载并解压到本地,我们可以执行以下命令来进行安装:

python setup.py install

如果我们按照前几章的说明在 Anaconda 中创建了一个环境,那么我们可以通过以下命令安装 pefile 库(假设环境名为 py35):

conda install -n py35 -c conda-forge pefile

这样,我们将能够在 Jupyter Notebooks 中调用该库的函数。

在输入我们的恶意软件数据集之后,正如之前讨论的那样,我们可以继续从每个文件中提取痕迹,使用 pefile Python 库读取相应的 pefile 格式字段,如以下脚本所示:

import os
import pefile

suspect_pe = pefile.PE("suspect.exe")

在这里,我们上传了本地的 suspect.exe 文件,这是我们恶意软件数据集的一部分。

此时,我们能够通过简单地解除引用 suspect_pe 对象来提取 suspect.exe 文件所属的 PE 文件格式的各个字段。

使用以下脚本,我们将提取 PE 文件格式的主要字段,并直接将其调用到之前定义的对象中:

AddressOfEntryPoint = suspect_pe.OPTIONAL_HEADER.AddressOfEntryPoint
MajorImageVersion = suspect_pe.OPTIONAL_HEADER.MajorImageVersion
NumberOfSections = suspect_pe.FILE_HEADER.NumberOfSections
SizeOfStackReserve = suspect_pe.OPTIONAL_HEADER.SizeOfStackReserve

然后我们可以继续从数据集中每个文件中提取痕迹,并将字段导出到.csv文件中。

我们的最终提取脚本版本将如下所示:

import os
import pefile
import glob

csv = file('MalwareArtifacts.csv','w')

files = glob.glob('c:\\MalwareSamples\\*.exe')

csv.write("AddressOfEntryPoint,MajorLinkerVersion,MajorImageVersion,
MajorOperatingSystemVersion,,DllCharacteristics,SizeOfStackReserve,
NumberOfSections,ResourceSize,\n")

for file in files:
    suspect_pe = pefile.PE(file)
    csv.write( str(suspect_pe.OPTIONAL_HEADER.AddressOfEntryPoint) + ',')
    csv.write( str(suspect_pe.OPTIONAL_HEADER.MajorLinkerVersion) + ',')
    csv.write( str(suspect_pe.OPTIONAL_HEADER.MajorImageVersion) + ',')
    csv.write( str(suspect_pe.OPTIONAL_HEADER.MajorOperatingSystemVersion) + ',')
    csv.write( str(suspect_pe.OPTIONAL_HEADER.DllCharacteristics) + ',')
    csv.write( str(suspect_pe.OPTIONAL_HEADER.SizeOfStackReserve) + ',')
    csv.write( str(suspect_pe.FILE_HEADER.NumberOfSections) + ',')
    csv.write( str(suspect_pe.OPTIONAL_HEADER.DATA_DIRECTORY[2].Size) + "\n")

csv.close()

我们还可以提取与合法文件相关的工件,将它们与恶意软件样本一起存储在.csv文件中,以便通过比较这两种类型的文件来进行训练。

显然,我们需要向.csv文件中添加一个额外的列,指定该文件是否为合法文件,分别用1(合法)或0(可疑)表示该字段的值。

区分不同的恶意软件家族

我们已经了解了传统恶意软件分析方法的优缺点,并且明白了为什么—鉴于恶意软件威胁的高发—我们需要引入算法自动化方法来进行恶意软件检测。

特别是,正确识别恶意软件行为中的相似性变得越来越重要,这意味着即使恶意软件签名由于例如变形代码的存在而无法互相比对(这会相应地改变哈希校验和),恶意软件样本也必须与同类型的类或家族关联。

相似性的分析可以通过使用聚类算法以自动化的方式进行。

理解聚类算法

聚类算法背后的直觉是识别并利用表征某些现象类型的相似性。

从技术角度来看,这是在数据集中区分和识别那些值变化频繁的特征与那些值系统性稳定的特征。只有这些后者的特征会被考虑用于检测具有相似性特征的现象。

我们可以通过这两种方法来识别相似性:

  • 有监督:相似性是基于先前分类的样本来识别的(例如,k 近邻k-NNs)算法)。

  • 无监督:相似性是由算法本身独立识别的(例如,K 均值算法)。

特征间相似性的估算是通过将其与距离的定义相联系来进行的。

如果我们将各个特征看作是* n *维空间中的点(与分析的特征数量相关),我们可以选择一个合适的数学准则来估算单个点之间的距离(每个点单独标识一个代数向量)。

可以选择的用于识别数值向量之间距离的度量方法如下:

  • 欧几里得距离:该特征识别连接两个点的最短路径(即直线),并通过以下数学公式计算:

  • 曼哈顿距离:这个特性通过计算向量元素间差异的绝对值之和得到。与欧几里得距离不同,曼哈顿距离识别连接两点的最长路径;在公式中,它等价于以下内容:

  • 切比雪夫距离:通过计算向量元素之间差异的绝对值的最大值来获得;在公式中,它等价于以下内容:

使用切比雪夫距离在需要考虑的维度数量特别高时尤其有用,尽管其中大多数对分析目的而言是无关的。

从距离到聚类

因此,聚类过程本质上是将表现出某些相似性的元素归类在一起。

通过使用某些数学定义的距离来定义相似度概念,聚类过程因此被简化为在给定数据空间的各个方向上从某一点开始,探索不同维度,然后将落在某一特定距离范围内的样本聚集在一起。

聚类算法

可以设想不同类型的聚类算法,从最简单和最直观的到最复杂和最抽象的。

一些最常用的算法如下所列:

  • K-Means:在无监督聚类算法中最为广泛使用的一种。K-Means 的优势之一是其实现简单,并且能够揭示数据中隐藏的模式。这可以通过独立识别可能的标签来实现。

  • K-NN 算法:这是一个懒惰学习模型的例子。K-NN 算法仅在评估阶段开始工作,而在训练阶段,它仅限于记忆观测数据。由于这些特性,K-NN 在大数据集下的使用效率较低。

  • 基于密度的空间聚类应用与噪声DBSCAN):与基于距离的算法 K-Means 不同,DBSCAN 是一个基于密度的算法示例。因此,该算法通过识别高密度区域来尝试对数据进行分类。

使用轮廓系数评估聚类

聚类算法的一个反复出现的问题是结果的评估。

而对于监督算法,通过已经知道分类标签的情况下,我们可以通过简单地计算错误分类样本的数量,并将其与正确分类样本进行比较,来评估算法获得的结果。在无监督算法的情况下,结果的评估则不那么直观。

由于事先没有分类标签可用,我们将通过分析算法本身的行为来评估结果,只有在同一聚类中分类的样本确实都相似时,我们才认为聚类过程成功。

对于基于距离的聚类算法,我们可以使用称为轮廓系数的评估指标,其采用以下数学公式:

在这里,m代表每个单独样本与最近聚类的所有其他样本之间的平均距离,而n代表每个单独样本与相同聚类的所有其他样本之间的平均距离。

对于每个单独的样本,计算轮廓系数(Silhouette coefficient)(因此,当处理大型数据集时,计算过程会变得特别缓慢),并且距离的估计是由我们选择的特定度量(如欧几里得距离或曼哈顿距离)决定的。

轮廓系数的主要特点如下:

  • Sc的值可以在-1+1之间变化,这取决于聚类过程的优劣。

  • 当最佳聚类时,Sc的值将趋向于+1,而在非最佳聚类的情况下,它将趋向于-1

  • 如果Sc的值接近0,则我们将会遇到重叠的聚类。

深入了解 K 均值

我们现在将更深入地讨论 K 均值聚类算法。

如前所述,K 均值是一种无监督算法,即不预设与数据关联的标签的先验知识。

算法以其最终目的将数据分成 k 个不同的子组而得名。作为聚类算法,它基于所选择的度量来表示单个样本(通常,这个度量是欧几里得距离)与其所属聚类的中心点(也称为质心)之间的距离进行数据的分割。

换句话说,K 均值算法通过最小化由计算数据(被视为空间中的点)与各自质心之间的欧几里得距离所代表的成本函数,将数据分组成不同的聚类。

在其处理结束时,算法会返回个别样本,这些样本被分组对应于每个聚类,其质心构成算法识别出的不同类别的特征集。

K 均值步骤

K 均值算法的特点如下:

  1. 初始化:这是根据分析人员定义的聚类数量来识别质心的阶段(通常情况下,我们无法提前知道真实的聚类数量,因此在定义聚类数量时,经常需要通过试错法来进行)。

  2. 数据分配到聚类:根据初始化阶段定义的质心,数据会被分配到最接近的聚类,依据的是计算出的数据与各自质心之间的最小欧几里得距离。

  3. 质心更新:作为一个迭代过程,K-Means 算法会再次估计质心,通过计算单个聚类中数据的平均值。然后,算法会继续重新分配平均值,直到数据与相应质心之间的欧几里得距离最小化,或者分析人员定义的迭代次数输入参数已被超过。

要使用scikit-learn库中提供的 K-Means 算法实现,我们必须适当地选择一系列输入参数,以便定义算法迭代过程的各个阶段,如前所述。

特别地,需要确定聚类的数量(代表k参数)和质心初始化的方式。

分析人员选择聚类数量对算法得到的结果有影响:如果作为初始化参数的聚类数量过多,则聚类的目的就被忽视了(算法在极限情况下将倾向于为每个数据点识别一个不同的聚类)。

为此,进行一次探索性数据分析EDA)的初步阶段可能会有所帮助——通过数据绘图帮助,视觉上识别数据可以分布成的可能不同子群的数量。

K-Means 的优缺点

K-Means 算法的优点之一,除了其简单易用外,它的高可扩展性使其在大规模数据集面前更具优势。

另一方面,缺点主要由于k参数的选择不当,k代表聚类的数量,正如我们所见,它需要分析人员特别注意,分析人员必须基于 EDA 小心评估该选择,或者通过试错法来进行。

使用 K-Means 算法的另一个缺点是,由于高维数据集的存在,它会提供代表性较差的结果。

结果,维度灾难现象就会发生,其中在 n 维空间中呈现出稀疏的形式。

这意味着距离最小化的成本函数(用作聚类的选择标准)并不具有很好的代表性(事实上,数据可能在 n 维空间中彼此等距分布)。

使用 K-Means 聚类恶意软件

在以下示例中,我们将看到 K-Means 聚类算法应用于我们之前创建的工件数据集。

请记住,我们的工件数据集包含从各个样本的 PE 文件格式中提取的字段,包括之前存储的.exe文件,涵盖了合法和可疑文件。

因此,我们将在算法初始化阶段为k参数分配的聚类数为2,而我们选择作为可能恶意软件的区分标准的特征对应于MajorLinkerVersionMajorImageVersionMajorOperatingSystemVersionDllCharacteristics字段:

import numpy as np 
import pandas as pd 
import matplotlib.pyplot as plt 

from sklearn.cluster import KMeans 
from sklearn.metrics import silhouette_score 

malware_dataset = pd.read_csv('../datasets/MalwareArtifacts.csv', delimiter=',')

# Extracting artifacts samples fields 
# MajorLinkerVersion,MajorImageVersion,
# MajorOperatingSystemVersion,DllCharacteristics

samples = malware_dataset.iloc[:, [1,2,3,4]].values
targets = malware_dataset.iloc[:, 8].values

一旦从我们的数据集中选择了感兴趣的字段,我们就可以开始实例化scikit-learnKMeans类,将k值作为输入参数,表示聚类的数量,为2n_clusters = 2),并定义算法可以执行的最大迭代次数,在我们的例子中为300max_iter = 300):

k_means = KMeans(n_clusters=2,max_iter=300)

然后我们可以调用k_means对象上的fit()方法,从而开始迭代算法过程:

k_means.fit(samples)

我们只需评估算法获得的结果。为此,我们将使用我们之前介绍的轮廓系数,采用欧几里得距离作为度量标准,并结合混淆矩阵来显示结果。这将为我们提供一张表,显示各自的聚类结果,分为正确预测和错误预测:

k_means = KMeans(n_clusters=2,max_iter=300)
k_means.fit(samples) 

print("K-means labels: " + str(k_means.labels_))

print ("\nK-means Clustering Results:\n\n", pd.crosstab(targets,
k_means.labels_,rownames = ["Observed"],colnames = ["Predicted"]) )      

print ("\nSilhouette coefficient: %0.3f" % silhouette_score(samples, k_means.labels_, metric='euclidean')) 

该过程的结果如下:

K-means labels: [0 0 0 ... 0 1 0]
K-means Clustering Results:

Predicted      0      1
Observed               
0           83419  13107
1            7995  32923

Silhouette coefficient: 0.975

我们可以看到聚类算法成功地识别了与个体样本关联的聚类标签,并且从混淆矩阵中可以看出,共有83419个样本(总数为 96,526)属于可疑类别并被正确识别(已被归类为标签0),而只有13107个(占总数的 13.58%)被错误地认为是合法的

以同样的方式,尽管这些样本实际上是合法的,但仍有7995个样本(总数为 40,918)被错误地分类为可疑(占总数的 19.54%),相比之下,有32923个样本被正确分类为合法。

Silhouette 系数0.975,接近 1,反映了聚类算法获得结果的良好性。

决策树恶意软件检测器

除了聚类算法,还可以使用分类算法来检测恶意软件威胁。特别重要的是使用决策树进行的恶意软件分类。

我们在第三章《垃圾邮件还是正常邮件?使用 AI 检测电子邮件网络安全威胁》中已经接触过决策树,当时讨论的是垃圾邮件检测的问题。现在,我们将讨论决策树在检测恶意软件威胁时解决的分类问题。

决策树的独特特征是,这些算法通过基于一系列 if-then-else 决策建模学习过程,达到了将数据分类到特定类别的目标。

对于这一特征,决策树代表了一种非线性分类器,其决策边界不能简化为空间中的直线或超平面。

决策树分类策略

因此,决策树根据树形结构塑造其学习过程。从根节点开始,随后的决策分支成不同深度的分支。

本质上,算法以迭代的方式将样本数据集划分,基于每个节点做出的决策,从而产生不同的分支。另一方面,分支不过是根据在各个决策节点做出的可能选择,对数据进行分类的不同方式。

这个迭代的子集划分过程是由预定义的子集划分质量度量来决定的。最常用的划分质量度量标准如下:

  • 基尼不纯度

  • 方差减少

  • 信息增益

尽管决策树具有较高的解释能力,但它们仍然存在一些重要的局限性:

  • 随着考虑的特征数量增加,表示相关决策树的结构复杂度也随之增加,将这种复杂性转化为我们所说的过拟合现象(即,算法倾向于拟合数据中的噪声,而不是信号,导致在测试数据上的预测精度降低)

  • 决策树对样本数据的微小变化特别敏感,这使得预测不稳定

克服这些局限性的一种方法是创建树集成,将投票分配给每棵树。因此,将样本分配到各个类别的机制简化为计数各棵树分配的投票;树集成的一个例子是随机森林算法。

使用决策树检测恶意软件

我们之前已经遇到过决策树,当时我们讨论了钓鱼攻击检测的话题。显然,我们也可以使用决策树来进行恶意软件检测。

在我们的示例中,我们将使用AddressOfEntryPointDllCharacteristics字段作为潜在的独特特征来检测可疑的.exe文件:

import pandas as pd
import numpy as np
from sklearn import *

from sklearn.metrics import accuracy_score

malware_dataset = pd.read_csv('../datasets/MalwareArtifacts.csv', delimiter=',')

# Extracting artifacts samples fields "AddressOfEntryPoint" and
# "DllCharacteristics"
samples = malware_dataset.iloc[:, [0, 4]].values
targets = malware_dataset.iloc[:, 8].values

from sklearn.model_selection import train_test_split

training_samples, testing_samples, training_targets, 
testing_targets = train_test_split(samples, targets, 
                    test_size=0.2, random_state=0)

from sklearn import tree
tree_classifier = tree.DecisionTreeClassifier()

tree_classifier.fit(training_samples, training_targets)

predictions = tree_classifier.predict(testing_samples)

accuracy = 100.0 * accuracy_score(testing_targets, predictions)

print ("Decision Tree accuracy: " + str(accuracy))
Decision Tree accuracy: 96.25860195581312

从所获得的结果来看,选择AddressOfEntryPointDllCharacteristics字段所做的预测准确度特别高,超过了 96%。

我们可以尝试选择不同的字段作为特征进行描述,并通过比较它们来评估所得到的结果。

激动的决策树——随机森林

我们已经看到决策树存在一些重要的局限性,这些局限性可能导致即便是训练数据的微小变化也会导致不稳定的结果。为了提高预测精度,可以使用集成算法,例如随机森林

随机森林其实就是一个决策树集成,其中每棵树都有一个投票。预测的改进因此取决于赋予它们的投票数:获得最高票数的预测将被选择作为算法的最终结果。

随机森林算法的创始人莱奥·布雷曼(Leo Breiman)指出,如果树之间是统计上不相关相互独立的,那么通过一组树所获得的结果会得到改进。接下来,我们将看到使用scikit-learn库实现的随机森林恶意软件分类器的示例。

随机森林恶意软件分类器

以下是使用scikit-learn库实现的随机森林恶意软件分类器的示例:

import pandas as pd
import numpy as np
from sklearn import *

malware_dataset = pd.read_csv('../datasets/MalwareArtifacts.csv', delimiter=',')

# Extracting artifacts samples fields "AddressOfEntryPoint" and
# "DllCharacteristics"

samples = malware_dataset.iloc[:, [0,4]].values
targets = malware_dataset.iloc[:, 8].values

from sklearn.model_selection import train_test_split

training_samples, testing_samples, 
training_targets, testing_targets = train_test_split(samples, targets,
test_size=0.2)

rfc =  ensemble.RandomForestClassifier(n_estimators=50)
rfc.fit(training_samples, training_targets)
accuracy = rfc.score(testing_samples, testing_targets)

print("Random Forest Classifier accuracy: " + str(accuracy*100) )

正如我们从结果中看到的,随机森林分类器改善了决策树的性能;要验证这一点,只需比较各自算法的准确性:

Decision Tree accuracy: 96.25860195581312
Random Forest accuracy: 96.46142701919594

使用隐马尔可夫模型(HMM)检测变形恶意软件

迄今为止,应用于恶意软件检测的算法示例旨在自动化恶意软件分析师执行的一些常规活动。

然而,它们所依赖的分析方法本质上是静态恶意软件分析。

然而,许多具体的恶意软件威胁实例并不能通过这种分析方法轻易识别,因为恶意软件开发者已经学会了绕过基于签名的检测技术。

因此,我们必须采用不同的方法来识别更先进恶意软件的恶意行为,为此我们需要转向基于动态恶意软件分析的方法,并将其与适当的算法结合使用。

但要充分解决这个问题,需要详细了解基于签名的传统检测策略的局限性。

恶意软件如何绕过检测?

最常用的检测策略是利用与被识别为恶意的可执行文件相关联的签名。

这种策略具有无可争议的优势,并且被广泛应用于杀毒软件中。

它基于寻找特定模式(由一系列位组成,这些位被认为是恶意可执行文件的代表),对存储在系统中的每个文件进行这些模式的搜索,并对系统资源(包括运行时内存)进行系统扫描。

模式搜索是基于一个数据库进行的,数据库中包含了恶意文件的签名。这些签名必须及时且不断地更新,以便能够在系统中搜索和比较文件,从而防止威胁被忽视。

基于签名的检测策略所带来的优势基本如下:

  • 高效地识别数据库中已知的威胁和存在的签名。

  • 低频率的误报,连同误报一起,是恶意软件检测软件的主要弱点。

相反,这种检测策略的局限性实质上体现在其基本假设上:即恶意软件一旦被识别,它的二进制表示就不会改变,因此被认为通过相应的签名得到了充分的“拍照”。

实际上,这些假设很快就被证明是不现实的。随着时间的推移,恶意软件开发者的创意努力也逐渐显现,他们试图创造能够改变自身形态的软件,从而避免基于签名的检测机制,同时保持自身的攻击潜力。

恶意软件作者采用的最初防范措施之一是混淆技术。为此,可以对恶意软件的可执行部分进行加密,每次使用不同的加密密钥来改变与恶意软件载荷相关的杀毒软件签名,而可执行指令保持不变,并在执行前解密。

一种更复杂的混淆变种是创建多态恶意软件,在这种恶意软件中,不仅恶意软件的加密密钥不断变化,而且恶意软件的解密指令本身也在变化。

多态恶意软件的后续演变导致了变形恶意软件,其中即使是载荷的可执行指令在每次执行时也会被修改,从而防止最先进的杀毒软件通过扫描运行时内存来识别恶意载荷,一旦载荷被解密。

为了改变载荷的可执行指令,变形恶意软件通过采用以下方法实现变异引擎

  • 插入额外的指令(死代码),这些指令不会改变恶意软件的逻辑和操作。

  • 改变指令的顺序,而不改变逻辑和整体功能。这种技术在生成许多主题变体方面特别有效。

  • 用其他等效指令替换一些指令。

多态恶意软件检测策略。

在恶意软件开发者和杀毒软件生产商之间的不断猫鼠游戏中,后者试图跟上步伐,调整其检测策略以应对不同形式的多态性。

在多态恶意软件的情况下,采取的一种策略是代码仿真:在受控环境(如沙箱)中执行恶意软件,允许恶意软件进行有效负载的解密阶段,之后传统的基于签名的杀毒软件检测会随之进行。

在变异性恶意软件以及零日漏洞的情况下,最先进的杀毒软件执行的检测活动试图分析可疑文件的行为,理解执行的指令的逻辑。

然而,这种检测策略存在一些以下重要的限制:

  • 这会导致较高的误报率。

  • 对执行中的指令的分析是动态进行的,这可能会在计算上产生显著影响。

在变异性恶意软件(以及零日漏洞)检测中,另一种策略是使用基于 HMM(隐马尔可夫模型)的机器学习算法。

为了理解这些限制是什么,我们首先需要介绍这些类型的算法。

HMM 基础

要理解 HMM 是什么,我们需要引入马尔可夫过程。

马尔可夫过程(或马尔可夫链)是一种随机模型,它根据预定义的概率集改变其状态。

马尔可夫过程的一个假设规定了未来状态概率分布完全依赖于当前状态

因此,HMM 是一个马尔可夫过程,其中不能直接观察系统的状态:唯一可观察的元素是与系统状态相关的事件和二次效应;然而,事件的概率由系统的每个状态决定,并且是固定的。

因此,系统每个状态的观察值间接通过由这些隐藏状态决定的事件来进行的,并且可以与这些事件的概率估计相关联:

(图片来源:https://en.wikipedia.org/wiki/File:HiddenMarkovModel.svg)

为了直观地理解 HMM 是如何工作的,我们展示以下示例:假设一个可执行文件在主机上运行。在某一时刻,机器可以继续正常工作,或者停止正常工作;这种行为代表了可观察事件。

为了简化起见,假设机器定期停止工作的原因可以归结为以下几点:

  • 可执行文件执行了恶意的指令。

  • 可执行文件执行了合法的指令。

与机器停止正常工作相关的具体原因是我们无法直接得知的实体,我们只能基于可观察的事件进行推测。

在我们的例子中,这些可观察的事件被归结为以下几点:

  • 机器正常工作(工作)

  • 机器停止工作(不工作)

同样,我们示例中的隐藏实体由程序执行的指令表示:

  • 恶意指令

  • 合法指令

最后,假设为系统的各种事件和状态分配概率估计。我们将此总结在下表中,也称为发射矩阵,它总结了给定观察结果与特定可观察状态相关的概率(记住,每个隐藏实体相关的概率之和,按可能事件细分,必须等于 1):

                 Working   Not Working

Malicious          0.2         0.8

Legitimate         0.4         0.6

在这一点上,我们必须估计程序执行的下一条指令的概率,这可以通过转移矩阵进行总结:

因此,如果程序之前执行了恶意(而非合法)指令,则下一条指令是恶意(而非合法)的概率为:

             Malicious  Legitimate

Malicious        0.7       0.3

Legitimate       0.1       0.9

最后,我们必须分配与 HMM 起始状态相关的概率;换句话说,第一个隐藏状态的概率对应于程序执行的第一条指令是恶意还是合法的概率:

Malicious        0.1 

Legitimate       0.9

在这一点上,我们 HMM 的任务是基于机器行为的观察来识别隐藏实体(在我们的例子中,判断程序执行的指令是恶意还是合法)。

HMM 示例

在我们的例子中,可能的观察结果如下:

ob_types = (‘W’,‘N’ )

在这里,W 代表工作(Working),N 代表不工作(Not Working),而隐藏状态如下:

states = (‘L’, ’M')

在这里,M 对应恶意(Malicious),L 对应合法(Legitimate)。

接下来是观察序列,这与程序执行的单条指令相关:

observations = (‘W’,‘W’,‘W’,‘N’)

这一系列观察结果告诉我们,在执行程序的前三条指令之后,机器正常工作,而只有在执行第四条指令后才停止工作。

基于这一系列可观察事件,我们必须继续训练 HMM。为此,我们将把我们的概率矩阵(如前所定义)传递给算法,对应于开始矩阵:

start = np.matrix(‘0.1 0.9’)

转移矩阵如下:

transition = np.matrix(‘0.7 0.3 ; 0.1 0.9’)

发射矩阵如下:

emission = np.matrix(‘0.2 0.8 ; 0.4 0.6’)

以下代码使用了隐藏马尔可夫(Hidden Markov)库,可以在github.com/rahul13ramesh/hidden_markov找到:

import numpy as np
from hidden_markov import hmm

ob_types = ('W','N' )

states = ('L', 'M')

observations = ('W','W','W','N')

start = np.matrix('0.1 0.9')
transition = np.matrix('0.7 0.3 ; 0.1 0.9')
emission = np.matrix('0.2 0.8 ; 0.4 0.6')

_hmm = hmm(states,ob_types,start,transition,emission)

print("Forward algorithm: ")
print ( _hmm.forward_algo(observations) )

print("\nViterbi algorithm: ")
print( _hmm.viterbi(observations) )

这些是脚本的结果:

Forward algorithm: 0.033196
Viterbi algorithm: [’M', ’M', ’M', ’M']

前向算法(Forward algorithm)给我们提供了 HMM 中观察序列的概率,而维特比算法(Viterbi algorithm)则用于找出能够生成给定观察序列的最可能的隐藏状态序列。

有关隐藏马尔可夫库的更多信息,请参见hidden-markov.readthedocs.io/en/latest/的文档。

深度学习在恶意软件检测中的应用

在本章的最后部分,为了完整性,我们将介绍一些利用基于神经网络的实验方法进行恶意软件检测的解决方案。

我们将在后续的第八章中更深入地探讨深度学习技术,尤其是在讨论生成对抗网络GANs)时,关于攻击与防御的内容。

在这里,我们将介绍一种创新且非传统的方法来解决不同恶意软件家族分类的问题,该方法利用了在图像识别领域开发的深度学习算法,如卷积神经网络CNNs)。

在深入探讨之前,让我们简要介绍一下神经网络NNs)及其在恶意软件检测中的主要特征。

神经网络简述

神经网络(NNs)是一类试图模仿人脑学习机制的算法,通过人工再现大脑的神经元结构。

神经网络有不同类型,但在这里我们特别关注两种类型:CNN 和前馈网络FFN),后者是 CNN 的基础;我们从描述 FFN 开始。

前馈神经网络(FFN)由至少三层神经元组成,具体分布如下:

  1. 输入层

  2. 输出层

  3. 隐藏层(一层或多层)

FFN 的层次结构使我们能够拥有第一层来管理输入数据,以及一层返回输出结果。

各层中的单个神经元直接连接到相邻的层,而同一层中的神经元之间没有连接。

CNN(卷积神经网络)

CNN 是一种特定类型的前馈神经网络(FFN),其特点是神经层的组织方式遵循生物学世界中现有视觉系统的组织方式,并且神经元区域在视觉场中重叠。

如前所述,在 CNN 中,每个神经元都连接到一片相邻的输入神经元区域,以便映射图像的像素区域。

通过这种方式,可以通过相邻层之间的局部连接方案识别空间相关性,这使得例如识别物体成为可能。

在 CNN 中,相邻的神经元区域实际上是通过模拟宽度、高度和深度的三维量来组织的,这些量映射到图像的宽度和高度特征,而深度则由 RGB 通道组成。

因此,CNN 因其卷积层(与池化层和全连接层一起构成了这种神经网络的三大特征层)而在图像识别方面得到了优化。

特别地,卷积层通过卷积操作提取输入图像的相关特征,这些卷积操作从原始图像中创建一幅新图像,通过突出最相关的特征,并模糊较不相关的特征;通过这种方式,卷积层能够发现相似的图像,而不考虑它们的实际位置或方向。

从图像到恶意软件

在接下来的描述中,我们将展示一种利用 CNN 在图像识别中的典型技能来进行恶意软件检测的替代方法。但要做到这一点,首先需要将恶意软件的可执行代码表示为图像,并将其输入到 CNN 中。

这种方法在《构建智能反恶意软件系统:一种使用支持向量机(SVM)进行恶意软件分类的深度学习方法》一文中有所描述,作者:Abien Fred M. Agarap,其中每个可执行恶意软件被视为一个由零和一组成的二进制序列,然后将其转换为灰度图像。

通过这种方式,可以根据图像中布局和纹理的相似性识别恶意软件家族。

为了对图像进行分类,使用了 k-NN 聚类算法,其中采用欧几里得距离作为度量标准来表示距离。

实验结果显示,分类率为 99.29%,并且计算负载极低:

图像来源:《构建智能反恶意软件系统:一种使用支持向量机(SVM)进行恶意软件分类的深度学习方法》,作者:Abien Fred M. Agarap

为什么我们要使用图像来进行恶意软件检测?

将恶意软件表示为图像的优点如下:

  • 识别恶意软件代码特定部分的能力,例如那些被恶意软件开发者修改的部分,目的是为了创建原始代码的不同变种。

  • 通过图像,可以识别出代码中微小的修改,同时保持恶意软件图像的整体结构。

  • 这些特征使得属于同一家族的不同恶意软件变种能够通过其表示它们的图像相似性轻松识别。这是因为不同类型的图像对应着不同的恶意软件家族。

使用 CNN 进行图像中的恶意软件检测

从前述的原始论文中,开发了一个工具,利用 CNN 识别和分类表示恶意软件代码的图像。

可以通过执行以下命令,从 GitHub 仓库下载该工具:

git clone https://github.com/AFAgarap/malware-classification.git/

在档案内还有一组恶意软件代码图像的数据集(malimg.npz)。要将您的恶意软件代码转换为灰度图像,还可以使用 Chiheb Chebbi 开发的 Python 脚本,该脚本可在github.com/PacktPublishing/Mastering-Machine-Learning-for-Penetration-Testing/blob/master/Chapter04/MalwareConvert.py上找到。

我们展示了工具的一些使用示例如下:

Usage: main.py [-h] -m MODEL -d DATASET -n NUM_EPOCHS -c PENALTY_PARAMETER -k CHECKPOINT_PATH -l LOG_PATH -r RESULT_PATH

要使用 CNN-SVM 模型,请将-model参数设置为1,如下例所示:

main.py –model 1 –dataset ./dataset/malimg.npz –num_epochs 100 –penalty_parameter 10 -c ./checkpoint/ -l ./logs/ -r ./results/

摘要

在本章中,我们讨论了恶意软件威胁检测的不同策略,利用了各种 AI 算法。

我们看到恶意软件如何欺骗分析师,使用诸如多态性之类的高级技术,迫使采用基于算法的检测工具。

因此,我们介绍了聚类和分类算法,从基于 HMMs 和神经网络的高级算法,如 CNNs,以处理此类高级威胁。

在下一章中,我们将讨论利用人工智能的网络异常检测技术。

第六章:基于 AI 的网络异常检测

当前可以在不同设备之间建立的互联互通水平(例如,考虑到物联网IoT))已达到如此复杂的程度,以至于严重质疑传统概念,如边界安全的有效性。实际上,网络空间的攻击面呈指数级增长,因此,必须借助自动化工具来有效地检测与前所未有的网络安全威胁相关的网络异常。

本章将涵盖以下主题:

  • 网络异常检测技术

  • 如何分类网络攻击

  • 检测僵尸网络拓扑

  • 不同的机器学习ML)算法用于僵尸网络检测

在本章中,我们将重点讨论与网络安全相关的异常检测,将欺诈检测和用户异常行为检测的讨论推迟到后续章节。

网络异常检测技术

我们迄今为止所看到的技术也可以应用于管理异常检测以及相关的未授权访问企业网络的尝试。为了充分理解异常检测技术的潜力,我们将追溯其在网络安全领域的发展,阐明其基本原理。

实际上,异常检测一直是网络安全领域的研究方向,尤其是在网络安全保护领域。然而,异常检测不仅仅局限于识别和防止网络攻击,它还可以在其他领域中应用,比如欺诈检测和用户资料的潜在泄露识别。

异常检测的原理

在网络入侵检测领域,特别是长期以来,采取了以下两种不同的策略:

  • 基于签名的检测

  • 异常检测

在第一种情况下,我们从已知攻击的分析开始,构建一个由之前检测到的攻击签名组成的知识库。这与警报系统结合,一旦网络流量中出现与归档签名的匹配,即可触发警报。基于签名的检测系统与各种杀毒软件的类比显而易见,缺点同样明显,因此,签名知识库必须不断更新,以便检测新的攻击类型。

在异常检测的情况下,另一方面,我们尝试识别可以定义为正常的网络流量行为,以便检测那些偏离正常行为的差异,即将其识别为异常。因此,这种方法使得通过分析可能被认为是异常的网络流量特征来检测新型攻击成为可能。

因此,有必要识别出什么构成异常行为,特别是针对网络流量的异常行为。

为了检测异常流量,可以考虑以下一些元素:

  • 到特定主机的连接数量

  • 不寻常的远程通信端口或意外的流量模式

  • 特定时间段内出现的不寻常流量高峰(例如,夜间持续的流量)

  • 网络中由特定主机占用的大量通信带宽

所有这些事件可能会被视为可疑,基于之前对被认为是正常的网络流量进行的分析。这种比较的基础是,可以定义适当的过滤器并将警报信号(警报)与它们关联,一旦触发,甚至可以决定丢弃相应的网络流量。

显然,还需要考虑与可疑行为无关的新型网络流量。例如,如果添加了以前没有的新通信通道,这种变化必须被考虑,并视为正常。

因此,正如我们将看到的,异常检测的一个敏感方面是区分真正的正例和假正例。

入侵检测系统

传统上,入侵检测活动通过引入专门的设备来管理,这些设备被称为入侵检测系统IDS)。这些设备通常被分为以下两类:

  • 基于主机的 IDS

  • 基于网络的入侵检测系统

随着人工智能AI)技术在网络安全领域的引入,第三种类型的 IDS——基于异常的 IDS——也加入了前述的两种传统类型。

为了充分理解基于异常的入侵检测系统(IDS)的区别和优势,简要描述两种传统类型的 IDS 是适当的。

主机入侵检测系统

主机入侵检测系统HIDS)的任务是检测可能影响组织内主机的入侵,尤其是那些被认为是关键的机器。为此,HIDS 监控一些被认为对识别可能攻击至关重要的系统指标,如与以下系统指标相关的系统信息:

  • 正在运行的进程的数量和类型

  • 用户账户的数量、类型和创建情况

  • 内核模块加载(包括设备驱动程序)

  • 文件和目录活动

  • 任务调度器活动

  • 注册表键值修改

  • 后台进程(守护进程和服务)

  • 操作系统OS)在启动时加载的模块

  • 主机网络活动

通常,待监控的系统指标的识别严格依赖于所采用的威胁模型,并且为了收集待监控的信息,可以使用操作系统自带的工具,或通过安装专门的系统监控工具。

网络入侵检测系统

网络入侵检测系统NIDS)的典型任务是通过分析网络流量来识别可能的攻击模式;也就是说,通过处理传输中的网络数据包——包括进站和出站数据——并在数据流中检测已知的攻击模式。

NIDS 通常检测到的网络攻击如下:

  • 广告软件(导致来自远程主机的未经请求和恶意的广告AD)下载)

  • 间谍软件(向远程主机传输敏感信息)

  • 高级持续性威胁APT)(APT 针对特定组织漏洞或配置错误服务的攻击)

  • 僵尸网络(典型的指挥与控制C2)攻击,通过将主机转变为僵尸机器并执行远程指令来利用组织的网络资源)

NIDS 的实施可以利用网络诊断工具,包括嗅探器(如tcpdump和 Wireshark)。

NIDS 还可以部署集成软件解决方案,如 Snort,这代表了实时检测可能的网络入侵的有效解决方案。

这有助于根据特定规则来定义,在此基础上可以比较正常和恶意的网络流量,从而在攻击被识别后激活触发器,执行适当的操作。

通常,这些触发器与给定的阈值相关联,这是一个预定值,它可靠地区分它们之间的事件。显然,如何充分确定该阈值值的问题就出现了,且该值是否可以有效地用于不同上下文下。以同样的方式,攻击者可能尝试修改此阈值,或发现这个值实际上被设置成什么,尝试采用隐形访问模式(通过保持活动始终低于阈值),使自己对 IDS 不可见。

因此,更优的做法是采用动态阈值(而不是依赖硬编码的值),通过系统地重新计算其值来随时间变化。这些改进可以通过采用基于时间序列(移动平均)的统计度量来获得,例如,或者通过对数据分布的统计测量重新处理(例如,采用位置测量如中位数或四分位间距IQR))。

尽管有用,但这种统计方法用于确定触发阈值在最复杂的入侵检测案例中不可避免地无效,因为它需要考虑不同变量之间可能存在的相关性。

换句话说,在某些场景中,可能需要同时触发多个触发阈值,因为异常在相互关系中由不同特征表示。

鉴于网络数据流的复杂性,因此有必要引入有状态检查——也称为数据包过滤活动——作为与常见网络监控(旨在提取不同类型数据包的信息)分开的过程。

通过跟踪传输和接收的各种数据包,有状态检查的特点在于能够关联不同类型的数据包,以识别针对某些网络服务的连接尝试、网络资源的饱和攻击(拒绝服务攻击DoS))或在更低网络协议层次进行的攻击(如 ARP 缓存中毒)。

由于其先进的网络分析功能,有状态检查可以与更复杂的异常检测形式结合使用。

基于异常的 IDS

随着人工智能技术的引入,NIDS(网络入侵检测系统)领域现在可以将传统的 IDS 发展为更先进的检测解决方案,利用监督学习和无监督学习算法,以及强化学习和深度学习。

同样,前几章分析的聚类技术,利用了数据类别之间的相似性概念,可以有效地用于基于异常的入侵检测系统(IDS)的实现。

然而,在选择用于异常检测网络的算法时,必须考虑网络环境的一些特征性方面:

  • 在基于监督学习算法的解决方案中,我们必须对所有数据进行分类(标记),因为按定义,监督学习中样本数据所属的类别是已知的。

  • 所有数据的分类不可避免地会涉及计算过载,并可能导致网络性能下降,因为在发送到目标之前,网络流量必须进行分析。从这个角度来看,我们可以决定采用无监督学习算法,不仅让算法识别未知类别,还能减少计算开销。

类似地,利用相似性概念的算法(如聚类算法)非常适合用于实现异常检测解决方案。然而,在这种情况下,同样需要特别注意用于定义相似性概念的度量类型,这些度量能够区分正常流量与异常流量。

通常,在实施异常检测解决方案时,会使用评分系统来评估流量:确定分隔不同类型流量(正常与异常)之间的值阈值。为此,在选择最合适的度量时,我们必须考虑数据的排序和分布。

换句话说,异常检测系统可以使用——作为评分指标——数据集值之间的距离(这些值被视为代表不同特征的n维空间中的点),或评估数据分布的规律性,依据的是被认为是研究现象代表性的分布。

将服务日志转化为数据集

网络异常检测的一个问题是如何收集足够且可靠的数据来进行算法分析和训练。互联网上有数百个免费提供的数据集,可以用于我们的分析;然而,我们也可以使用自己的网络设备来积累更能代表我们特定现实的数据。

为此,我们可以使用以下方法:

  • 网络设备,如路由器或网络传感器,使用tcpdump等工具进行数据采集。

  • 服务日志和系统日志

在操作系统内部,服务日志和系统日志可以存储在不同的位置。对于类 Unix 系统,服务日志通常以文本文件的形式存储在/var/log目录及其相关子目录中。对于 Windows 操作系统,日志分为 Windows 日志(包括安全日志和系统日志)和应用程序日志。它们可以通过 Windows 事件查看器应用程序访问,或者通过访问文件系统位置如%SystemRoot%\System32\Config进行访问。

无论是类 Unix 系统还是 Windows 操作系统,日志文件都是基于预定义模板的文本格式,唯一不同的是,在 Windows 中,每个事件在相应的日志文件中都会关联一个事件 ID。日志文件的文本性质非常适合集成存储在日志中的信息。

集成网络数据与服务日志的优势

两个数据源,即网络数据和服务日志,在网络异常检测中各有优缺点。

然而,它们的集成使得可以在利用优势的同时,限制其缺点。

近年来,发布了多种软件解决方案(包括专有软件和开源软件),这些解决方案旨在解决集成不同数据源的问题,使用户能够利用数据科学和大数据分析的方法进行分析,这并非偶然。

在最广泛的解决方案中,我们可以提到ElasticSearch, Logstash, KibanaELK)套件,它可以对从日志文件中提取的事件进行索引,并以直观的可视化形式表示。

其他广泛使用的专有网络解决方案基于 Cisco 的 NetFlow 协议,它可以紧凑地表示网络流量。

从原始数据重构感兴趣的事件是非常困难的。更重要的是,如果以自动化的方式进行处理,这可能会导致生成不可靠的信号(误报),这代表了安全管理中的一个问题。

此外,在网络数据的情况下,它们代表了各个相关服务,而在服务日志的情况下,它们直接与生成它们的进程相关。

因此,整合两种数据源(网络数据和服务日志)可以实现对正在分析事件的上下文化,进而提高上下文意识,并减少从原始数据开始解读事件所需的努力。

如何分类网络攻击

我们已经看到,可以使用各种不同类型的算法(如监督学习、无监督学习和强化学习),即使是在实施网络异常检测系统时。

那么我们如何有效地训练这些算法,以便识别异常流量呢?

首先需要识别一个代表给定组织内正常流量的训练数据集。

为此,我们必须适当地选择代表我们模型的特征。

特征选择尤为重要,因为它们为所分析的数据提供了上下文价值,从而决定了我们检测系统的可靠性和准确性。

事实上,选择那些与可能的异常行为相关性不高的特征会导致较高的错误率(误报),因此会使它们失去效用。

选择可靠特征的一种解决方案是评估现有的网络协议使用中的异常。

攻击——例如 SYN 洪水攻击——的特征是 TCP/IP 握手的异常使用(在这种情况下,设置 SYN 标志的数据包后面没有跟随设置 ACK 标志的数据包,从而无法建立有效连接)。

一个特征可以通过一个或多个与协议或网络数据包头部相关的属性来描述,就像不同类型的网络属性构成了由正在分析的特定网络连接表示的特征(即,telnet 会话的特征是连接到远程端口23,该连接发生在两个具有各自 IP 地址和 IP 端口的端点之间)。

最常见的网络攻击

鉴于我们可以通过组合不同的特征来识别的各种组合的巨大多样性,必须依赖一个反映给定组织所面临风险水平的威胁模型,并在此模型的基础上,识别出最具代表性的特征组合,以便应对可能的攻击。

从这个角度来看,分析最常见的网络攻击类型是有用的:

  • 基于恶意软件的

  • 零日漏洞

  • 通过网络嗅探进行数据泄漏

  • 网络资源饱和(DoS)

  • 会话劫持

  • 连接欺骗

  • 端口扫描

基于类似的分类(需根据具体上下文进行调整并不断更新),我们可以识别需要考虑的特征,将更具代表性的数据集输入到我们的算法中。

异常检测策略

因此,我们已经看到,异常检测的概念本身指的是与预期行为不同的表现;这种差异,从技术角度来说,就是离群点检测。

要识别离群点,可以采取不同的策略:

  • 分析时间序列中的事件序列:数据在定时间隔内收集,评估序列随时间发生的变化。这种技术广泛应用于金融市场分析,但也可以有效地应用于网络安全领域,用于检测用户在远程会话中输入字符(或命令)的频率。即使是每单位时间内输入数据频率的简单不自然增加,也可以表明存在异常,这可能表明远程端点中存在自动化代理(而非人工用户)。

  • 使用监督学习算法:当正常行为与异常行为可以可靠地区分时,这种方法是有意义的,例如在信用卡欺诈的情况下,可以检测到预定义的可疑行为模式,依赖于未来的欺诈行为可以归因于一个预定义的方案。

  • 使用无监督学习算法:在这种情况下,无法将异常行为追溯到预定义的行为,因为无法识别出可靠且具有代表性的训练数据集用于监督学习。这种场景最常描述网络安全的现实,特征是新的攻击形式或新漏洞的利用(零日攻击)。同样,通常也很难将所有理论上可能的入侵归结为一个预定义的方案。

异常检测假设与挑战

从方法论角度来看,毫无疑问,离群点是学习算法的一个问题,因为它们在基于训练数据构建描述性模型时构成了干扰因素。

当处理异常值时,算法应如何处理?是应该考虑模型的判定,还是应该将其丢弃,视为估计误差?或者,离群点是否代表数据集中反映出实际变化的创新现象?要回答这些问题,我们需要调查离群点的最可能来源。

在某些情况下,异常值是不同寻常值的组合,它们是估计误差,或者源自多个具有不同语义的数据集的合并,导致不可靠或极不可能的样本。然而,它们的存在却构成了一个干扰因素,特别是对于那些基于估计预期值与观察值之间距离的度量的算法而言。从技术角度讲,这意味着整体方差增加,可能导致算法高估误差,从而影响信号的正确性(这一现象被称为过拟合)。

显然,并不是所有算法对异常值的存在都同样敏感。然而,良好的做法是尽量使学习过程更具鲁棒性,通过平滑参数更新阶段,给那些尽管在数值上低于正常值但可能会影响正确参数估计的异常值加权。

为了识别数据集中可能存在的异常值,通常做法是进行数据的初步分析,这被称为探索性数据分析EDA),利用可视化工具并计算简单的描述性统计量(如均值或中位数)。

通过这种方式,可以直观地发现异常值的存在,并验证数据中的任何不对称性,表现为分布中均值与中位数之间的距离逐渐增大。

一些统计度量对极端值的存在不那么敏感。事实上,旨在表示数据排序的度量对于分布中的异常值具有更强的鲁棒性(例如四分位间距 IQR)。

因此,异常值检测的一个基本假设是数据集中正常观测值远多于异常观测值。然而,事实是,通常正确识别异常值并不是一项容易完成的任务。

从这个角度来看,如果我们决定使用统计度量来确定异常值的存在,我们可以按照以下步骤进行操作:

  1. 计算代表数据的统计值,用作对比标准,以确定异常值(即,最偏离代表性值的那些值)。

  2. 确定一个异常检测的参考模型,这个模型可以基于距离度量,或者假定已知的统计分布(即正态分布)作为正常值的代表。

  3. 定义置信区间并评估异常值存在的概率(可能性),基于所选择的分布。

统计方法用于识别异常值,虽然容易且直接应用,但仍然存在重要的理论限制:

  • 大多数统计检验只考虑单一特征。

  • 通常,数据的底层分布是未知的,或者不能归因于已知的统计分布。

  • 在复杂且多维的案例中(其中必须同时考虑多个特征),离群值的存在会导致总方差的增加,使得所识别的代表性模型在预测上变得不那么显著。

检测僵尸网络拓扑

网络异常检测中最常见的陷阱之一与在企业网络内检测僵尸网络有关。由于这些隐藏网络的危险,检测僵尸网络尤其重要,不仅是为了防止外部攻击者耗尽组织的计算和网络资源,还为了防止敏感信息的泄露(数据泄漏)。

然而,及时识别僵尸网络的存在往往是一个复杂的操作。这就是为什么理解僵尸网络的本质非常重要。

什么是僵尸网络?

僵尸网络这一术语来源于botnet两个词的组合。对于“net”一词,我们显然需要处理网络的概念;而对于“bot”一词,我们则需要再多说几句。

“Bot”一词实际上越来越与自动化代理在网络空间中的传播相关联。

聊天机器人(通常在网站上用于管理客户服务初期阶段的软件代理,但也越来越广泛,甚至在社交网络上为各种目的使用)到网络喷子(旨在通过传播虚假信息分散用户注意力或困惑用户的软件代理),网络空间正日益被这些自动化的、在人与数字设备之间进行交互的软件代理所感染。

在僵尸网络的情况下,攻击者的意图是通过安装恶意软件将受害主机转变为自动化代理,通过通常由集中式服务器管理的 C2 控制台接收并执行攻击者的命令。

受害机器因此成为一个庞大的受感染机器网络(僵尸网络)的一部分,通过其计算和网络资源贡献共同目标:

  • 参与电子邮件垃圾邮件活动

  • 对机构或私人第三方网站执行分布式拒绝服务DDoS)攻击

  • 比特币和加密货币挖矿

  • 密码破解

  • 信用卡破解

  • 数据泄漏和数据泄露

对于一个组织来说,处理一个僵尸网络(即使是不自觉地)代表着在法律责任方面对第三方的重大风险;这不仅仅是公司资源的浪费。

因此,重要的是监控公司网络,尽早识别可能属于僵尸网络的主机。

僵尸网络杀伤链

为了及时识别僵尸网络的可能存在,考虑其攻击链(实现过程中的不同阶段)可能是有益的。

因此,我们可以区分以下阶段:

  • 恶意软件安装

  • 通过 C2 加入僵尸网络

  • 将僵尸网络传播到其他主机

在需要持续监控的可能存在僵尸网络的事件中,应包括定期连接到远程主机的行为。与其监控流量本身的质量(事实上,僵尸网络经常使用看似无害的通信协议,如 HTTP 流量,使用服务的默认端口80,以掩盖其在日志文件中的存在),不如调查网络连接的实际性质。

在僵尸网络的情况下,受害主机必须不断呼叫主控服务器(C2),以接收新命令并将收集到的信息以及在受害系统上执行的进程结果发送给 C2 服务器。

这种现象被称为信标现象,其特征正是网络中存在定期(即使在非工作时间)进行连接的行为,这些连接发生在感染的主机与远程目标之间(这些远程目标也可能是被攻击者妥协的合法网站)。

信标现象通常以以下特点为特征:

  • 长期的用户会话,交换空包(保持连接的空包),以保持连接的打开

  • 主机之间定期的数据交换

信标现象的问题在于它不能总是可靠地识别;因此,它构成了僵尸网络存在的一个症状,因为其他合法服务也可能表现出类似之前提到的特征。为了捕获能够证明真实信标过程存在的可靠信号——并将其与良性的 SSH 或 telnet 会话区分开来,以及与抗病毒软件执行的系统更新下载过程区分开来——因此需要深入的网络流量监控,并结合时间序列的统计分析和位置度量的计算,如中位数和 IQR,以便识别那些定期发生的通信。

随后,必须通过图形化的方式展示这些本地和远程主机的映射,以便识别可能具有稳定特征的网络拓扑,并合理地引发对僵尸网络存在的怀疑。

从对必要的初步分析活动的描述中,很容易推断出在被误判为僵尸网络而非真实僵尸网络的网络中,存在被误报的风险,特别是在潜在的设备数量(它们持续连接到网络)呈指数增长时(这一情景比以往任何时候都更为现实,因为物联网的普及)。

用于僵尸网络检测的不同机器学习算法

从我们迄今为止描述的内容来看,显然不建议仅依赖自动化工具进行网络异常检测,采用能够动态学习如何识别网络流量中异常存在的人工智能算法可能更为高效,这样可以使分析师对仅真正可疑的案例进行深入分析。接下来,我们将展示不同的机器学习算法在网络异常检测中的应用,这些算法也可用于识别僵尸网络。

我们示例中选择的特征包括网络延迟和网络吞吐量的值。在我们的威胁模型中,与这些特征相关的异常值可以视为僵尸网络存在的代表。

对于每个示例,都会计算算法的准确度,以便能够对比所获得的结果:

import numpy as np
import pandas as pd

from sklearn.linear_model import *
from sklearn.tree import *
from sklearn.naive_bayes import *
from sklearn.neighbors import *
from sklearn.metrics import accuracy_score

from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt
%matplotlib inline

# Load the data
dataset = pd.read_csv('../datasets/network-logs.csv')

samples = dataset.iloc[:, [1, 2]].values
targets = dataset['ANOMALY'].values

training_samples, testing_samples, training_targets, testing_targets =
train_test_split(samples, targets, test_size=0.3, random_state=0)

# k-Nearest Neighbors model
knc = KNeighborsClassifier(n_neighbors=2)
knc.fit(training_samples,training_targets)
knc_prediction = knc.predict(testing_samples)
knc_accuracy = 100.0 * accuracy_score(testing_targets, knc_prediction)
print ("K-Nearest Neighbours accuracy: " + str(knc_accuracy))

K-Nearest Neighbours accuracy: 95.90163934426229

# Decision tree model
dtc = DecisionTreeClassifier(random_state=0)
dtc.fit(training_samples,training_targets)
dtc_prediction = dtc.predict(testing_samples)
dtc_accuracy = 100.0 * accuracy_score(testing_targets, dtc_prediction)
print ("Decision Tree accuracy: " + str(dtc_accuracy))

Decision Tree accuracy: 96.72131147540983

# Gaussian Naive Bayes model
gnb = GaussianNB()
gnb.fit(training_samples,training_targets)
gnb_prediction = gnb.predict(testing_samples)
gnb_accuracy = 100.0 * accuracy_score(testing_targets, gnb_prediction)
print ("Gaussian Naive Bayes accuracy: " + str(gnb_accuracy))

Gaussian Naive Bayes accuracy: 98.36065573770492

高斯异常检测

检测数据分布规律性最广泛使用的一种方法是利用高斯概率分布。

正如我们所看到的,这种统计分布具有一系列有趣的特性,帮助我们适当建模许多自然、社会和经济现象。

显然,并非所有待研究的现象都可以用高斯分布表示(正如我们所看到的,分析现象的基础分布往往是未知的);然而,在许多异常检测的案例中,它仍然是一个可靠的参考点。

因此,我们必须了解高斯分布的特性,才能理解为什么它被广泛使用。

高斯分布

从数学角度来看,高斯分布(也称为正态分布)表示随机变量的概率分布,其数学形式如下:

在这里,µ表示均值,σ²表示方差(它代表数据围绕均值的变化性)。在其标准形式中,均值µ0σ^(2)为1

高斯分布的优势在于中心极限定理,简单来说,它确立了随机变量的观察数据的平均值——独立提取的——随着观察次数的增加会收敛到正态值。

换句话说,随着观察次数的增加,这些观察值会围绕均值µ对称分布(且具有更高的概率):

当偏离平均值(趋向于分布在左右极端)时,随着σ值的增加,正态分布因此由µσ所假定的值充分表示。

同样,可以确定观察值围绕平均值分布的概率,这与方差的值成比例;换句话说,我们可以确定以下内容:

  • 68%的观察值落在µ - σµ + σ之间

  • 95%的观察值落在µ - 2σµ + 2σ之间

  • 99.7%的观察值落在µ - 3σµ + 3σ之间

使用高斯分布进行异常检测

高斯分布可以用来识别异常值。此外,在这种情况下,异常元素由与其余数据相比,异常值的显著差异所组成。

显然,数据的大多数值越是紧密集中在均值µ周围,且方差σ较低,越能使异常值的差异变得更加显著。

为了在异常检测中使用高斯分布,我们需要执行以下步骤:

  1. 假设训练集的特征服从正态分布(这一点也可以通过对绘制数据的直观分析来验证)

  2. 估算µσ值,代表该分布

  3. 选择一个合适的阈值,代表观察值为异常的概率

  4. 评估算法的可靠性

在以下示例中,我们将展示高斯异常检测的实现。

高斯异常检测示例

首先,让我们导入必要的 Python 库,然后从.csv文件加载数据,该文件表示我们检测到的每个数据流的延迟和网络吞吐量值:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

dataset = pd.read_csv('../datasets/network-logs.csv')

数据加载到内存后,我们验证样本的分布是否可能类似于高斯分布,并以直方图的形式显示相应的值:

hist_dist = dataset[['LATENCY', 'THROUGHPUT']].hist(grid=False, figsize=(10,4))

前面的代码生成以下输出:

在这一点上,我们在散点图上进行数据绘制,直观地识别出可能的异常值:

data = dataset[['LATENCY', 'THROUGHPUT']].values

plt.scatter(data[:, 0], data[:, 1], alpha=0.6)
plt.xlabel('LATENCY')
plt.ylabel('THROUGHPUT')
plt.title('DATA FLOW')
plt.show()

前面的代码生成以下输出:

从视觉上看,绝大多数观察值集中在平均值周围,只有一些例外。因此,我们希望验证异常情况是否属实,然后继续估算潜在高斯分布的代表性值µσ

"""
Anomaly Detection Module
Thanks to Oleksii Trekhleb:
https://github.com/trekhleb/homemade-machine-learning/blob/master/homemade/anomaly_detection/gaussian_anomaly_detection.py
"""
from gaussian_anomaly_detection import GaussianAnomalyDetection

gaussian_anomaly_detection = GaussianAnomalyDetection(data)

print('mu param estimation: ')
print(gaussian_anomaly_detection.mu_param)

print('\n')

print('sigma squared estimation: ')
print(gaussian_anomaly_detection.sigma_squared)

mu param estimation:  
[14.42070163 15.39209133]

sigma squared estimation: 
[2.09674794 1.37224807]

接下来,我们估算概率和阈值,然后进行比较,以识别异常数据:

targets = dataset['ANOMALY'].values.reshape((data.shape[0], 1))
probs = gaussian_anomaly_detection.multivariate_gaussian(data)

(threshold, F1, precision_, recall_, f1_) =
gaussian_anomaly_detection.select_threshold(targets, probs)

print('\n')

print('threshold estimation: ')
print(threshold)

threshold estimation: 
0.00027176836728971885

在这一点上,我们可以通过将各个样本的概率与先前估算的最优阈值进行比较,从而识别异常值,并在散点图中可视化它们的存在:

outliers = np.where(probs < threshold)[0]
plt.scatter(data[:, 0], data[:, 1], alpha=0.6, label='Dataset')
plt.xlabel('LATENCY')
plt.ylabel('THROUGHPUT')
plt.title('DATA FLOW')

plt.scatter(data[outliers, 0], data[outliers, 1], alpha=0.6, c='red', label='Outliers')

plt.legend()
plt.plot()

前面的代码生成以下输出:

现在是对算法进行估计的时候了。但首先,我们需要介绍一些与异常检测中误报识别相关的概念。

异常检测中的误报管理

我们之前已经看到异常检测会导致相当一致的估计误差。特别是在基于签名的 IDS 案例中,错误的风险由较高数量的假阴性表示,即未检测到的攻击。

当使用反病毒软件时,我们面临相同类型的风险。如果没有与可疑签名的对应,IDS 将不会检测到任何异常。

另一方面,在基于异常驱动的 IDS 案例中,该系统被编程为自动检测异常,我们面临着高假阳性数量的风险;即检测到的异常实际上并非有害。

要适当地管理这些误报,我们需要引入一些度量标准,这些度量标准将帮助我们估计这些错误。

第一个是真正率(也称为灵敏度或召回率):

Sensitivity or True Positive Rate (TPR) = True Positive / (True Positive + False Negative);

然后,我们有假正率

False Positive Rate (FPR) = False Positive / (False Positive + True Negative);

Precision = True Positive / (True Positive + False Positive);

基于这些指标,可以估计F1值,它表示PrecisionSensitivity之间的调和平均值:

F1 = 2 * Precision * Sensitivity / (Precision + Sensitivity);

F1可用于评估从高斯异常检测中获得的结果。最佳估计结果通常对应于接近1F1值,而最差的估计结果则对应于接近0F1值。

在我们的高斯异常检测示例中,F1值如下:

print('F1 score: ')
print(F1)

F1 score: 
0.6666666666666666

这个F1值接近1,这并不令我们惊讶,因为在选择最佳阈值时,我们的高斯异常检测模型选择对应于最高F1分数的值。

接收器工作特性分析

在假阳性和假阴性之间经常需要进行权衡。减少未检测到的攻击数或未检测到的攻击数会导致检测到的假阳性攻击增加。为了展示这种权衡的存在,使用一种特定的曲线,称为接收器工作特性ROC)曲线。在我们的例子中,通过scikit-learnroc_curve()计算 ROC 曲线,将目标值和相应的概率作为参数传递:

from sklearn.metrics import roc_curve

FPR, TPR, OPC = roc_curve(targets, probs)

我们可以注意到真正率TPR或灵敏度)、假正率FPR)与 ROC 曲线之间的关联(OPC值代表控制系数,称为工作特性,例如总连接数)。

因此,我们可以通过绘制TPR值与OPC控制系数的值来表示灵敏度:

# Plotting Sensitivity

plt.plot(OPC,TPR)

前述代码生成以下输出:

我们还可以看到灵敏度(TPR)随着OPC值的增加而降低。

同样,我们可以通过将敏感度(TPR)与FPR值进行比较来绘制 ROC 曲线:

# Plotting ROC curve

plt.plot(FPR,TPR)

上述代码生成了以下输出:

摘要

在这个日益互联的世界中,随着物联网(IoT)的逐步普及,有效分析网络流量以寻找可能代表安全漏洞的异常变得至关重要(例如,僵尸网络的存在)。

另一方面,单纯依赖自动化系统进行网络异常检测任务会使我们面临管理越来越多误导性信号(假阳性)的风险。

因此,将自动化异常检测活动与人工操作员进行的分析相结合,利用 AI 算法作为过滤器,选择那些真正值得分析人员深入关注的异常,显得更加合适。

在下一章,我们将讨论用于保护用户认证的 AI 解决方案。

第三部分:保护敏感信息和资产

本节涵盖了通过生物识别认证、登录尝试分类、账户速度特征和声誉评分来防止认证滥用和欺诈。

本节包含以下章节:

  • 第六章,用户认证的安全性

  • 第七章,利用云 AI 解决方案防止欺诈

  • 第八章,生成对抗网络 (GANs)攻击与防御

第七章:确保用户身份验证的安全

在网络安全领域,人工智能AI)在保护用户敏感信息方面扮演着越来越重要的角色,包括他们用于访问网络账户和应用程序的凭证,以防止身份盗窃等滥用行为。

本章将涵盖以下主题:

  • 防止身份验证滥用

  • 账户声誉评分

  • 用户身份验证通过击键识别

  • 人脸识别生物识别身份验证

防止身份验证滥用

在当前这种日益去中心化的背景下,传统服务逐渐以数字形式提供(如电子商务、网上银行等),正确识别并防止可能针对用户数字身份的威胁变得尤为重要,例如身份盗窃的风险。此外,随着物联网IoT)的迅速发展,通过伪造凭证(或从合法拥有者处盗取凭证)获得未经授权的访问权限的可能性比以往任何时候都更高。

这是由于网络空间的维度及其增加的攻击面所致,网络空间的攻击面由可以在人与机器之间以及机器与机器之间建立的连接数量的指数级增长所决定,这使得信息泄露的风险更大。

保护用户账户不仅是数据完整性的问题,也是任何企业的声誉风险,因为这可能会引发对第三方的法律责任。

只需想想有关虚假账户扩散的问题,这些账户是故意创建的,目的是获取用户的机密和敏感信息。还有恶搞账户的问题,它们可能会混淆和误导那些不知道这些虚假账户性质的合法用户。

随着自动化服务的普及,这些服务越来越多地由算法管理,从法律角度来看,确保通过自动化程序收集的敏感信息的正确性和合法性变得至关重要;企业可能会根据欧盟的通用数据保护条例GDPR)所确立的责任原则被要求承担相应责任。

因此,必须采取所有必要的组织措施,以确保用户账户的安全,这通过监控可疑活动来实现,例如试图破解密码的行为。

保护用户账户的一个弱点是密码保护不足。

密码是否已经过时?

密码一直是确保用户账户安全的主要工具;然而,它们已经暴露出自己的局限性。

随着在线服务数量的增加(以及用于访问这些服务的不同平台的增多),用户必须记住的密码数量也随之增加。

由于构成可靠密码的字母数字代码的强度与其易于管理性相对立,用户常常为多个账户和服务使用相同的密码。

这有助于攻击面扩大,从而增加被入侵的风险。如果攻击者成功窃取了用户的凭证(例如,他们的个人电子邮件账户),那么他们很可能也能破坏其他凭证,因此成功窃取受害者的数字身份。

身份盗窃的风险,实际上是用户可能面临的主要威胁之一。一旦受害者的身份被侵犯,攻击者便可以展开一系列非法活动,比如通过以受害者名义开设银行账户进行洗钱,这一切都以受害者的凭证为掩护,而受害者往往对这些非法活动一无所知。

随着时间的推移,采纳集成密码认证和账户授权程序的安全措施并非偶然。这些措施部署了旨在提高上下文意识的监控任务,也就是说,分析和界定与使用访问凭证相关的活动,在对个体用户来说,处于正常(或可疑)情境中的活动。

保护用户账户不仅仅限于验证输入密码的正确性及密码与用户账户的一致性,还包括记录的各种账户活动,如来自不同地理区域的 IP 地址的同时访问,或者使用不同的设备,如 PC、智能手机、浏览器和操作系统,这些都是不常见的或之前从未使用过的。

这一监控的目标显然是检测攻击者可能通过利用之前被泄露的密码来访问用户账户,进而窃取凭证。

为了实现这种水平的上下文安全意识,必须将发生在用户账户上的监控活动与使用自动学习算法的异常检测程序结合,从而根据用户的习惯和行为学习区分不同的可疑活动。

还可以用身份验证程序来替代相同的密码,这些程序利用用户的生物识别凭证,如虹膜、声音、指纹或面部特征。

在这种情况下,将识别程序限制为单一生物特征证据并不合适,尽管它是强大的,但仍然是可以被伪造的(可以通过利用用于验证生物特征数据的传感器的局限性和漏洞来伪造)。相反,我们应当将其与其他验证用户凭据的方法结合起来。

常见的认证实践

为了确保凭据确实属于账户的合法拥有者,随着时间的推移,已经引入了各种形式的验证,其中一些基于采用第二重认证因素,如插入通过短信传送到用户电话号码的临时密码,或者通过与用户账户关联的电子邮件地址发送的 OTP 验证码。这类程序的可靠性基于次要因素,如用于接收和管理这些认证因素的支持和通道的完整性。

如果用户的电子邮件账户被黑客入侵,或者智能手机上安装了恶意软件,该恶意软件会自动转发短信验证码(OTP)给攻击者,那么第二重认证因素在安全方面的无效性就显而易见。

第二重认证因素的有效性假设基于所使用支持的多样化。换句话说,建议用户不要将所有个人敏感信息都保存在同一支持中(遵循其中一种最知名的风险管理最佳实践,即不要把所有的鸡蛋放在同一个篮子里)。

如果这种多样化假设未被验证,那么基于第二重认证因素的认证程序的可靠性也必然会失效。

如何识别虚假登录

从我们到目前为止所说的内容来看,应该清楚的是,基于安全令牌的认证程序,如密码、短信、OTP 等,至少应与自动化的异常检测程序结合使用。

与用户账户管理相关的异常之一是:

  • 暴力破解访问尝试,旨在通过在有限时间内输入不同的密码来识别用户的密码

  • 来自不同地理区域的 IP 地址的同时访问

  • 用户使用不常见的设备、软件和操作系统

  • 与人工操作员不兼容的频率和打字速度

显然,待监控事件的列表可以根据具体的分析背景增加和变化。然而,一旦提供了代表性事件的历史基础,自动检测异常是非常重要的。

虚假登录管理——反应式与预测性

一旦与可疑访问相关的代表性事件积累起来,了解所要遵循的管理策略就变得至关重要。

更传统的方式是配置反应式报警系统;也就是说,一旦发现可能的未经授权访问,报警系统会触发事件(反应),并自动暂停或封锁用户账户。

虽然反应策略实施起来较为简单,但它会带来以下重要的副作用和缺点:

  • 拒绝服务DoS)攻击可能会针对合法用户;攻击者通过模拟未经授权的访问尝试来触发报警系统自动封锁用户账户,从而损害组织的声誉,增加对用户和提供服务的公司造成的干扰。

  • 反应式报警系统通常设有与相关事件相关的默认触发器。事件的校准是针对所有用户全局进行的,系统不会根据用户的特定行为来识别单个用户。

  • 反应策略通过后视镜来看现实,即假设未来和过去一样,因此不能自动适应上下文的快速变化。

  • 反应策略通常基于对异常活动峰值的监控,即如果行为超过了被认为是正常的某个预设水平,则被认为是可疑的。这种情况发生在隐秘模式下进行的攻击中,攻击者不会引起超过报警阈值的异常活动峰值。攻击者可以在系统内部保持隐蔽,并且能够不受干扰地进行信息收集和滥用操作。曾经发生过一起大规模的用户账户违规事件,目标是雅虎!网络门户。该事件是在隐秘模式下进行的,直到几年后,违规行为才被发现并公之于众。

相反,为了应对用户账户被攻击的策略,必须考虑可能影响用户和攻击者行为的上下文和场景变化,这就需要采用预测性异常检测方法,从对过去数据的分析出发,揭示潜在的模式,外推用户未来的行为,并及时识别潜在的攻击或欺诈尝试。

预测不可预测的事件

预测分析的任务是揭示隐藏的模式,识别数据中的潜在趋势。为此,需要结合各种数据挖掘和机器学习ML)方法,以便利用来自组织中各类异构信息源的结构化和非结构化数据集。

通过这种方式,可以将原始数据转化为可操作的预测响应,应用不同的自动化学习算法对数据进行处理。

不同的算法显然会在预测准确性方面提供不同的结果。

正如我们在前几章中看到的,当我们需要处理离散的答案(垃圾邮件或正常邮件)时,分类算法特别适用,而当我们需要连续的输出(即,具有更高粒度的输出值)时,回归算法是我们首选的选择。

类似地,管理大规模分类任务时,我们可以考虑使用线性支持向量机SVMs)和使用决策树随机森林,这些通常在我们需要对数据进行分类时提供最佳结果。

需要特别提及的是无监督学习和聚类算法,它们在探索数据中潜在和未知的模式时特别有效,能够执行诸如异常检测、可疑用户行为等任务。

选择正确的特征

采取预测方法以检测可能的用户账户违规行为,意味着选择正确的监控特征。这些特征会根据我们认为可能发生的威胁而有所不同。

在防止通过暴力破解用户凭证(用户 ID 和密码)进行攻击的情况下,监控失败的访问尝试(登录次数)及其增长速率和随时间的变化可能就足够了。其他情况下,监控的元素可能是密码更改的频率、失败的登录、密码恢复等。

更困难的是检测可能由攻击者执行的隐形攻击,这些攻击者已经获取了正确的用户密码(因为他们之前已经破坏了与用户账户相关联的电子邮件账户,从而利用密码恢复程序),或者检测到被劫持的用户会话(也称为会话劫持,它是指滥用由合法用户正常启动的会话,并被攻击者利用以实现欺诈目的),而没有明显的账户凭证泄露。

在这种情况下,监控与用户登录相关联的 IP 地址可能很有用,以验证是否有来自相距遥远的地理区域的同时访问,或者是否在短时间内频繁访问,使用的是对特定用户不常用的设备和软件。

防止虚假账户创建

用户账户的创建也是一项需要监控的活动,以防止虚假账户在我们的平台上蔓延;只需想想这些虚假账户通过混淆和欺骗合法用户,诱使他们做出可能导致欺诈或账户泄露的行为。

需要监控的事件可以追溯到假档案创建过程中通常涉及的阶段,即请求激活新账户和识别现有账户中的假档案,这些账户因用户不当行为必须被阻止或取消。

新账户异常创建的一个可能指示器(这些账户很有可能是虚假档案)是,由同一 IP 地址在短时间内(例如不到一小时内)激活大量新账户。

对于现有账户,一个可以可靠地指示假档案存在的异常指示器可能是大量用户帖子在短时间内发布,这使我们认为一个旨在在平台上传播垃圾邮件的机器人可能存在。

账户声誉评分

因此,用户账户活动的监控必须考虑到新创建和现有账户,以防止现有账户被攻击者侵入后进行恶意活动。建议将声誉度量(声誉评分)与用户的行为相关联,以此来估算。这种声誉评分还能帮助我们识别隐蔽模式下进行的攻击,从而防止攻击未被察觉。它通过利用报警系统来监控异常和嘈杂的活动峰值,达到了这一目的。

在估算与每个用户账户相关的声誉分数时,我们可以考虑各种特征:

  • 用户在一段时间内发布的帖子数量和频率

  • 通过代理、VPN 或其他 IP 匿名化系统访问用户账户

  • 使用不常见的用户代理(例如脚本)登录

  • 用户在键盘上打字的速度

这些以及其他特征可以有效地考虑用于训练我们的算法,并动态地估算个别用户的声誉分数。

分类可疑的用户活动

一旦我们积累了足够的特征来填充我们的数据集,就需要决定采取什么样的策略来训练我们的算法。特别是,自然采用的方法是监督学习,这通过利用我们已掌握的信息并利用我们之前对被认为是可疑账户的分类来实现。事实上,我们可能已经在黑名单中积累了不少用户账户,或使用基于规则的检测系统将其标记为可疑。

作为正向训练的例子,我们可以考虑与被暂停或列入黑名单的账户相关的特征,而与仍然启用的账户相关的特征则可以作为负向训练的例子。我们只需要选择最适合我们用例的监督学习算法,然后进入训练阶段,使用之前识别并与前述正向和负向示例相关联的标签进行训练。

监督学习的优缺点

无论看起来跟随监督学习策略多么合乎逻辑,都必须考虑到其中的 методологические 风险。

其中一个主要问题是,我们的算法将难以识别新的可疑活动案例,因为它们已经受到先前分类标签的影响,而这些标签可能会受到系统性错误的影响。为了重新训练我们的模型,以便检测新的可疑活动形式,我们将被迫插入不同于之前的分类规则,这些新规则应能正确检测与新样本相关联的新标签。

然而,这并不能避免放大之前在我们模型中引入的系统性错误的风险;如果我们错误地将某些用户类别列入黑名单(例如,所有从属于特定地理区域的 IP 地址连接的用户,而该地区之前被认定为垃圾邮件活动的来源),我们将向模型中引入假阳性,这将导致模型自我反馈并加剧这一问题。

为了减少这些假阳性对结果的扭曲效果,我们应该在每个后续训练阶段对提交给算法的样本进行适当的加权。

聚类的优缺点

另一种可以用来对用户账户可疑活动进行分类的方法是聚类。通过根据用户活动类型(如用户发帖频率、在平台上花费的时间、登录频率等)将用户账户分为同质化组,也可以识别出可能涉及多个被相同攻击者攻击的账户的可疑活动。例如,这些攻击者可能试图通过协调多个账户的活动,传播垃圾邮件或发布不受欢迎的帖子。

聚类实际上是一种能够发现不同用户群体之间相似性(甚至是隐藏的相似性)的方法;一旦将用户分为不同的群集,我们就需要确定哪些群集实际上代表了可疑活动,并在每个群集中,识别出哪些账户涉及可能的欺诈行为。

然而,即便在聚类的情况下,也需要仔细选择使用的算法类型:事实上,并非所有的聚类算法都能有效地检测到可疑活动。

例如,聚类算法(如 k-means)要求正确确定聚类的数量(通过预先定义参数 k 的值,该值也决定了算法的名称),这一特性在实际检测可疑用户活动时并不十分适用,因为我们通常无法准确地定义将账户分组的聚类数量。

此外,像 k-means 这样的算法不能与以类别或二进制分类值表示的特征一起使用。

用户认证通过击键识别进行

鉴于我们之前提到的限制和方法学问题,近年来,我们越来越多地采用新型的生物识别方式来检测可疑用户账户。由于神经网络的广泛应用,这些技术的使用变得比过去更加便捷。

相同的用户认证过程通常也通过生物识别技术实现,生物识别技术(如果没有取而代之的话)会补充(甚至替代)传统的基于密码的认证方式。

当我们谈论生物识别时,可以考虑一些独特的身体特征,这些特征可以可靠地且独特地追溯到特定的用户,如虹膜、面部、指纹、声音等。行为和习惯也可以通过模式来识别,这些模式通常可以可靠地与个体用户关联;在这些生物识别行为中,击键输入(也称为击键动态)就像手写一样,有助于可靠地识别不同的个体。

Coursera Signature Track

用户认证的第一个具体应用实例是 Coursera 引入的 Signature Track 技术,该技术几年前开始用于识别参与考试的学生,以确认他们在完成课程后所获得的成就证书的有效性。

Coursera 采用的 Signature Track 技术,在 Andrew Maas、Chris Heather、Chuong(Tom)Do、Relly Brandman、Daphne Koller 和 Andrew Ng 共同撰写的论文《MOOCs 与推动学习和学习研究的技术,提供大规模开放在线课程的认证证书》(MOOCs and Technology to Advance Learning and Learning Research, Offering Verified Credentials in Massive Open Online Courses)中有所描述,该论文发表于 Ubiquity Symposium(ubiquity.acm.org),其目的是解决如何为每个学生分配用户凭证,以便能够可靠地验证他们的身份。

Signature Track 是一个过程,通过它可以将学生的课程作业与其真实身份相连接,因此,在课程结束时,学生将获得由 Coursera 和提供课程的大学共同颁发的经过验证的证书,证书上会写明他们的名字。

该证书具有唯一的验证代码,允许第三方(如雇主)验证真实候选人完成课程的情况。

Signature Track 的独特特征不仅与身份验证和身份确认程序相关,还与高规模维度相关,这得益于 Coursera 注册学生数量的不断增长;实际上,Coursera 的一门课程通常涉及 40,000 到 60,000 名学生。因此,身份验证和身份确认程序的特点是高效,不需要讲师或工作人员的干预。

此外,与其他网络服务(如在线银行或电子商务)不同,Coursera 用户账户的验证和身份认证管理变得复杂,因为用户容易将登录凭证提供给他人,以便他人代为完成作业。这一特殊性促使 Coursera 采用了两种基于人脸识别和与个体学生相关的打字模式的独立生物识别和摄影认证方法。在注册阶段,Coursera 要求学生通过网络摄像头提供一张照片,并附上身份证明文件的副本。

此外,在注册阶段,学生被要求在键盘上输入一句简短的句子,以便识别他们自己的生物识别击键资料。这是通过击键动态实现的。

击键动态

击键动态基于按键事件的节奏和韵律,这些特征对每个学生来说都是独特的;然而,由于一系列外部随机因素的影响,例如中断、错误修正或使用特殊功能键(如ShiftCaps Lock),这些事件无法直接用于机器学习算法。

因此,有必要将表示用户打字行为的原始数据转换为一组能够正确表示用户键盘动态的特征数据集,从而清除数据中的随机干扰因素。

基于击键动态的异常检测

关于使用击键动态进行异常检测的首批科学研究之一是 Kevin S. Killourhy 和 Roy A. Maxion 撰写的论文《Comparing Anomaly-Detection Algorithms for Keystroke Dynamics》。该研究的作者提出了收集击键动态数据集,以测量不同检测器性能的方法;他们收集了 51 名受试者输入 400 个密码时的数据,并提交了 14 种不同算法收集的数据,这些算法通过用户检测性能来评估。

本研究的目的是可靠地识别那些通过不同的打字模式窃取其他用户密码的冒充者。

尝试使用被盗密码进行身份验证的冒充者,将会根据与真实用户不同的击键动态特征被识别并及时阻止。

用于确定击键动态的一些特征如下:

  • Keydown-keydown:这是连续按键之间按下时间的间隔。

  • Keyup-keydown:这是按键释放与下一个按键按下之间的时间间隔。

  • Hold:这是每个按键按下和释放之间的时间间隔。

从原始数据中提取出一组时序特征,这些特征将被输入到用户检测算法中。

击键检测示例代码

以下是基于前一部分中提到的研究《比较击键动态中的异常检测》描述的数据集的击键动态实现示例;该数据集也可以在www.cs.cmu.edu/~keystroke/DSL-StrongPasswordData.csv处下载,格式为.csv

如预期,数据集包含 51 个受试者,每个受试者输入 400 个密码;同时收集的度量包括这些保持时间(在数据集中以H标签表示):

  • Keydown-keydown 时间(标记为DD

  • Keyup-keydown 时间(标记为UD

击键检测脚本的代码如下:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
%matplotlib inline

from sklearn.model_selection import train_test_split
from sklearn import metrics

from sklearn.neighbors import KNeighborsClassifier
from sklearn import svm
from sklearn.neural_network import MLPClassifier

pwd_data = pd.read_csv("https://www.cs.cmu.edu/~keystroke/DSL-StrongPasswordData.csv", header = 0)

# Average Keystroke Latency per Subject

DD = [dd for dd in pwd_data.columns if dd.startswith('DD')]
plot = pwd_data[DD]
plot['subject'] = pwd_data['subject'].values
plot = plot.groupby('subject').mean()
plot.iloc[:6].T.plot(figsize=(8, 6), title='Average Keystroke Latency per Subject')

脚本的结果可以在以下图表中看到:

脚本继续进行数据集拆分,然后应用不同的分类器,如以下示例所示:

data_train, data_test = train_test_split(pwd_data, test_size = 0.2, random_state=0)

X_train = data_train[pwd_data.columns[2:]]
y_train = data_train['subject']

X_test = data_test[pwd_data.columns[2:]]
y_test = data_test['subject']

# K-Nearest Neighbor Classifier
knc = KNeighborsClassifier()
knc.fit(X_train, y_train)

y_pred = knc.predict(X_test)

knc_accuracy = metrics.accuracy_score(y_test, y_pred)
print('K-Nearest Neighbor Classifier Accuracy:', knc_accuracy)
K-Nearest Neighbor Classifier Accuracy: 0.3730392156862745

# Support Vector Linear Classifier
svc = svm.SVC(kernel='linear') 
svc.fit(X_train, y_train)
y_pred = svc.predict(X_test)

svc_accuracy = metrics.accuracy_score(y_test, y_pred)
print('Support Vector Linear Classifier Accuracy:', svc_accuracy)
Support Vector Linear Classifier Accuracy: 0.7629901960784313

# Multi Layer Perceptron Classifier
mlpc = MLPClassifier()
mlpc.fit(X_train,y_train)

y_pred = mlpc.predict(X_test)
mlpc_accuracy = metrics.accuracy_score(y_test, y_pred)
print('Multi Layer Perceptron Classifier Accuracy:', mlpc_accuracy)
Multi Linear Perceptron Classifier Accuracy: 0.9115196078431372

现在我们可以绘制多层感知器(MLP)结果的混淆矩阵:

# Drawing confusion matrix for Multi Layer Perceptron results
from sklearn.metrics import confusion_matrix

labels = list(pwd_data['subject'].unique())
cm = confusion_matrix(y_test, y_pred, labels) 

figure = plt.figure()
axes = figure.add_subplot(111)
figure.colorbar(axes.matshow(cm))
axes.set_xticklabels([''] + labels)
axes.set_yticklabels([''] + labels)
plt.xlabel('Predicted')
plt.ylabel('True')

脚本绘制的混淆矩阵如以下图所示:

在前面的代码示例中,使用了三种不同的分类器(均可在scikit-learn库中找到),并按预测准确度从低到高的顺序展示了它们的使用。

我们从KNeighborsClassifier聚类算法开始,经过支持向量机线性分类器,到多层****感知器MLP)分类器,后者是报告预测准确度最高的分类器,准确度超过 90%。

还展示了每个受试者的平均击键延迟的图形表示以及使用多层感知器分类器获得的结果的混淆矩阵。

使用多层感知器进行用户检测

为什么多层感知器(MLP)分类器在预测准确性方面表现出显著更好的结果?

答案在于它代表了一个人工神经网络ANN)。

人工神经网络(ANNs)构成了深度学习的基础元素,是深度学习算法所具有的高潜力的根基,允许例如对大量数据进行分类、进行人脸和语音识别,或者击败像卡斯帕罗夫这样的国际象棋世界冠军。

我们在第三章中遇到了感知器,Ham or Spam? 使用 AI 检测电子邮件网络安全威胁;我们也看到它在数据无法线性分割的分类场景中的局限性。然而,多层感知器克服了单一感知器的局限性。

实际上,MLP 由多个人工神经元层组成,每个层由感知器实现。

多层感知器(MLP)可以有三层或更多层的完全连接的人工神经元,这些神经元共同构成一个前馈网络。重要的是,MLP 可以逼近任何连续的数学函数;因此,我们可以添加任意数量的隐藏层,从而增强其整体预测能力。

面部识别的生物识别认证

除了使用键盘动态进行身份验证外,使用面部识别的身份验证方法越来越普遍。

这些过程受益于神经网络的不断扩展,以及硬件外设(如嵌入式摄像头)的普及,这些外设已预装在智能手机、平板电脑、PC 和其他设备上。

尽管乍看之下有些奇怪,使用生物识别证据的想法并不新颖,它可以追溯到不久之前的时代。虽然指纹的使用在上世纪初进入了警察操作领域,但某些基本形式的面部识别可以追溯到用于悬赏通缉犯的海报,这在西部荒野中非常常见,直到最近的侦探用的画面特征识别。

然而,毫无疑问,近年来我们见证了生物识别证据使用的真正爆发;考虑到与互联网使用以及与国家安全相关的威胁不断增加,这种趋势并非偶然,特别是在各国对抗恐怖主义的背景下。

在许多情况下,互联网使用促进了匿名性,特别是在那些网络接入控制不够系统和可靠的国家。如果通过 IP 地址或由用户名和密码组成的常见访问凭证进行的检查不够充分,就必须通过更严格的个人身份验证方式来补充。

面部识别的优缺点

在某些方面,面部识别的使用似乎是生物识别程序中首选的方式;利用智能手机和平板电脑等配备高清摄像头的设备的广泛普及,面部识别似乎是验证身份的最合乎逻辑和实用的解决方案。

然而,有一些技术方面不容忽视。

为了使面部识别成为一种可靠的身份识别方法,有必要确保所使用的图像没有受到环境因素(如反射、阴影、入射光等)的扭曲,这些因素会使得面部识别变得更加困难;面部的曝光角度在确定面部识别的可靠性方面也有其重要性。

当尝试在从人群图像中提取的面部样本上使用面部识别时,这些问题尤为明显;结果往往是,假阳性数量使得该识别方法变得无效。

因此,面部识别的有效性和可靠性,在我们能够在受控环境中使用时会更大,在这种环境下,潜在的干扰因素可以降到最低。而在我们试图在野外环境中使用它进行比较时(例如在人群中识别个体),其有效性则会较小。

我们不能忘记生物识别程序所依赖的基本假设:唯一性,即将生物识别证据专门归属于某个特定个体的可能性。在面部识别的情况下,这一假设并不总是成立。

除了明显的面部相似性案例(如长得像的人),同一个人随着时间的推移,面部也可能发生变化,原因可能是由于疾病、压力、事故或单纯的衰老引起的身体变化;此外,随着人口的增加,遇到虚假相关性的可能性也相应增加。由于这一简单的效应,为了提高识别程序的可靠性,必须考虑的数据量将随着数据集规模的增大而不成比例地增加。

所有这些因素使得可靠地训练面部识别算法变得尤其困难,从而使得面部识别的实时使用变得不太实用。

如果我们考虑到需要在存档证据与随时间积累的新图像之间进行的比较次数,我们立刻会意识到,进行全面验证,比较它们之间所有可能的组合,是不可行的。

正如指纹识别领域所发生的情况一样,我们应该将比较减少到仅仅是那些在概率上被认为可靠的、用于识别目的的特征(称为分片);(在指纹的情况下,这些特征被称为细节点,即指纹中不常见的证据区域,如两条脊线融合或脊线终止),并使用适当的相似性度量方法,如局部敏感哈希LSH)。

尽管面部识别程序在表面上看起来非常实用,但它们并非没有问题,且与它们可能产生的高假阳性率有关。

特征脸面部识别

在最常见的面部识别技术中,有一种名为特征脸的技术;正如我们将看到的,这个名字来源于其实现过程中所使用的线性代数方法。

从技术角度来看,面部识别是一个分类问题,旨在将面孔的名称与相应的图像进行结合。

我们必须区分面部识别与面部检测,后者是旨在识别图像中是否存在面孔的程序。面部识别是一个分类问题,假设已经存在一个包含面孔图像的档案,我们需要将一个面孔与一个名字进行匹配。

为此,我们必须能够比较我们档案中的图像和即将进行面部识别的新图像。

解决这个问题的直接方法可能是通过计算图像中特征之间的互反差异,将图像降维为多个特征向量。然而,正如我们之前提到的,鉴于需要在接近实时的情况下进行大量比较,这种方法将是不切实际的。

就其本质而言,图像具有较高的维度(即不同特征),这些维度可能包含许多与识别无关的无关信息(构成白噪声)。为了进行可靠的比较,我们需要将维度数目减少到与识别目的密切相关的那些维度。

因此,利用特征脸的面部识别技术基于一种无监督的降维算法,称为主成分分析PCA),这并非巧合。

使用主成分分析(PCA)进行降维

PCA 可以识别数据集的代表性变量(也称为主成分),并选择数据分布较为分散的那些维度。

为了理解为什么我们需要对高维数据(如图像)进行降维,以及如何通过 PCA 实现降维,我们可以考虑以下描述性示例。

假设我们需要区分食物的营养价值;我们应该考虑哪种营养成分?例如维生素、蛋白质、脂肪和碳水化合物?

为了回答这个问题,我们必须能够确定哪个营养成分作为主成分,也就是说,我们应该考虑哪种营养成分(或营养成分的组合)作为各种食物的特征元素?

问题在于,并非所有食物都含有相同的营养成分(例如,维生素在蔬菜中含量较多,而在肉类中则较少)。因此,我们可以将一组不同的营养成分视为主成分,例如通过将脂肪(肉类中存在的营养成分)加入到蔬菜中的维生素(含量较多)中。

然后,我们将添加(或删除)营养素,以识别可以作为主成分的最佳元素组合,也就是说,沿着这个成分,数据的分布最为广泛。

我们还必须考虑到某些营养素可能高度相关,这意味着它们沿相同方向变化,而其他营养素则沿相反方向变化(例如,随着维生素的增加,脂肪水平降低;为了衡量相关性程度,我们可以使用线性相关系数 R)。

如果我们能识别出具有高相关性的元素,就可以减少在定义主成分时考虑的变量数量;这正是 PCA 的目的:实现维度减少(减少描述给定数据集的维度数)。

主成分分析

从形式上讲,PCA 由选择一个空间的超平面组成,该超平面是数据(在空间中由点表示)主要分布的方向;这在数学上转化为寻找方差最大值所在的轴。

以下截图展示了一个数据集的主成分:

(图片来源:维基百科,链接:https://en.wikipedia.org/wiki/File:GaussianScatterPCA.svg)

为了识别这个轴,我们需要计算与我们的数据相关的协方差矩阵,识别矩阵中最大的特征向量,这些特征向量对应于主成分相关的轴。通过这样做,我们可以减少数据的维度。

特征向量(以及与之相关的特征值)这一概念来源于线性代数,并为基于特征脸的人脸识别技术命名。

接下来,我们将简要分析这些概念,并展示它们的数学形式化。

方差、协方差和协方差矩阵

要理解特征向量和特征值的概念,我们必须首先回顾以下一些数学定义:

  • 方差:这衡量数据内部的离散程度,表示为数据与其平均值之间偏差的平均值,如下所示:

  • 协方差:这衡量两个变量之间的线性相关程度;其数学表示如下:

  • 协方差矩阵:这是一个包含每对数据之间计算出的协方差的矩阵,这些数据来自一个数据集。

我们可以使用 Python 库 NumPy 计算方差、协方差的值,并表示协方差矩阵。在以下示例中,我们展示了一个由 NumPy 列表数组表示的协方差矩阵(列表表示向量),最后,我们使用指令print(np.cov(X).T)打印协方差矩阵,如下所示:

import numpy as np

X = np.array([
 [3, 0.1, -2.4],
 [3.1, 0.3, -2.6],
 [3.4, 0.2, -1.9],
])

print(np.cov(X).T)

特征向量和特征值

现在我们可以介绍特征向量和特征值的概念,它们来自线性代数。

方阵A的特征向量表示为满足以下条件的向量v

同样地,值λ(由一个标量表示)构成了向量v的对应特征值。

需要牢记的是,特征向量(及其对应的特征值)只能为方阵计算,而并非所有方阵都有特征向量和特征值。

要理解特征向量和特征值对主成分分析(PCA)的相关性,我们必须记住,向量(如特征向量)表示线性空间中的一个有向元素(具有方向性),而标量(如特征值)表示强度的度量(没有方向性)。

因此,之前显示的方程式表示的是线性变换;将特征向量v与矩阵A相乘并不会改变v的方向(它保持不变),只是改变其强度,这个强度由特征值λ的值来决定;在实践中,就好像我们在重新缩放v向量。

下图展示了由于特征值乘法引起的向量的重新缩放:

(图片来源:Wikipedia,网址:https://en.wikipedia.org/wiki/File:Eigenvalue_equation.svg)

因此,为了识别协方差矩阵中的主成分,我们需要寻找对应于较大特征值的特征向量。在这种情况下,我们可以使用 NumPy 库来执行我们的计算。

假设我们有以下方阵A

特征向量和特征值(如果存在)的计算可以归结为以下 NumPy 指令:

import numpy as np
eigenvalues, eigenvectors = np.linalg.eig(np.array([[2, -4], [4, -6]]))

特征脸示例

所以,在这里我们来到了 PCA 技术在面部识别中的应用。接下来的示例中,我们将把我们档案中的每一张图像与图像中代表的人的名字关联起来。

为了实现这一点,我们需要将图像的维度(由多个特征组成,代表像素的不同特性)降至主要成分,即与识别目的最相关的特征。这些主要成分就是特征脸(Eigenfaces)。

以下屏幕截图显示了几个特征脸:

(图片来源:Wikipedia,网址:https://en.wikipedia.org/wiki/File:Eigenfaces.png)

因此,我们数据集中每一张图像都可以解释为这些特征脸的组合。

该图像数据集包含野外标签面孔LFW)档案(Gary B. Huang,Manu Ramesh,Tamara Berg 和 Erik Learned-Miller——Labeled Faces in the Wild: A Database for Studying Face Recognition in Unconstrained Environments,马萨诸塞大学,阿姆赫斯特,技术报告 07-49,2007 年 10 月),可以在vis-www.cs.umass.edu/lfw/lfw-funneled.tgz. 下载。

在这个例子中,与前一个键盘动态的例子一样,我们将使用 MPL 分类器进行图像分类。

MLP 分类器的结果通过classification_report()显示,该报告展示了精度、召回率和 F1 分数等指标的值,如下例所示:

from sklearn.datasets import fetch_lfw_people
from sklearn.decomposition import PCA
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

lfw = fetch_lfw_people(min_faces_per_person=150)

X_data = lfw.data
y_target = lfw.target
names = lfw.target_names

X_train, X_test, y_train, y_test = train_test_split(X_data, y_target, test_size=0.3)

pca = PCA(n_components=150, whiten=True)
pca.fit(X_train)

pca_train = pca.transform(X_train)
pca_test = pca.transform(X_test)

mlpc = MLPClassifier()
mlpc.fit(pca_train, y_train)

y_pred = mlpc.predict(pca_test)
print(classification_report(y_test, y_pred, target_names=names))

执行前述脚本后返回的输出如下:


                precision    recall  f1-score   support

  Colin Powell       0.92      0.89      0.90        79
 George W Bush       0.94      0.96      0.95       151

     micro avg       0.93      0.93      0.93       230
     macro avg       0.93      0.92      0.93       230
  weighted avg       0.93      0.93      0.93       230

总结

本章展示了可以用来提高用户认证程序的有效性并及时检测可能存在的受损用户账户的不同技术。

这些技术基于生物识别证据的使用,如面部识别,或生物行为识别,如键盘动态,这些都可以通过使用神经网络等 AI 算法来实现,如 MLP。

我们还看到如何通过使用 PCA 将数据的维度减少到其主要成分。

最后,强调了使用生物识别证据进行用户认证和识别的优缺点。

在下一章中,我们将学习如何使用云端 AI 解决方案进行欺诈防范。

第八章:使用云 AI 解决方案进行欺诈预防

许多公司遭遇的安全攻击和数据泄露的目标是违反敏感信息,如客户的信用卡详细信息。这类攻击通常以隐蔽模式进行,因此,使用传统方法难以检测到这些威胁。此外,需要监控的数据量往往庞大,单靠传统的提取、转换和加载ETL)程序,运行在关系型数据库上,无法有效分析数据,因此采用人工智能AI)可扩展解决方案显得尤为重要。通过这样做,企业可以利用云架构来管理大数据,并运用预测分析方法。

信用卡欺诈代表了人工智能(AI)解决方案在网络安全领域应用的一个重要考验,因为它要求开发利用大数据分析的预测分析模型,并通过使用云计算平台进行处理。

在本章中,您将学习以下内容:

  • 如何利用机器学习ML)算法进行欺诈检测

  • 如何通过集成(bagging)和提升(boosting)技术提高算法的效果

  • 如何使用 IBM Watson 和 Jupyter Notebook 分析数据

  • 如何利用统计度量进行结果评估

让我们来介绍一下算法在信用卡欺诈检测中所扮演的角色。

引入欺诈检测算法

近年来,我们见证了金融领域欺诈活动的增加,特别是在信用卡欺诈方面。这是因为网络犯罪分子设立信用卡欺诈活动相对容易,因此,金融机构和组织必须能够及时识别欺诈行为。

此外,在信用卡欺诈的背景下,欺诈检测与预防的工作还受到一个复杂因素的影响:这种类型的欺诈具有全球化的特点;也就是说,它涉及不同的地理区域以及多种金融机构和组织。

因此,能够共享全球不同组织之间的可用信息源至关重要。

这些信息源是异构的,且具有数据生成爆炸性增长的特点,必须实时分析。

这类似于典型的大数据分析场景,要求使用分析工具及合适的软件和硬件平台,例如云计算所提供的工具。

这种情境的复杂性还由于我们比以往任何时候都更容易发现洗钱和非法活动,如国际恐怖主义融资,通常与信用卡欺诈相关联。

网络犯罪分子进行的非法活动因此具有跨国性质,涉及不同领域的有组织犯罪。

所有组织,无论是公共部门还是私人部门,都被要求在反洗钱等法规的基础上合作,共同打击这些非法活动。

网络犯罪分子对信用卡欺诈的兴趣日益增长,源于扭曲的经济激励;信用卡欺诈的预期回报远高于其他非法活动,且被警方抓获的风险远低于传统犯罪。

此外,如果个人金融欺诈涉及的金额和价值没有超过某些阈值,金融机构本身也不愿追究非法活动,因为调查活动可能会被证明是经济上不划算的(例如,通过位于不同国家和地区的假冒电子商务网站进行的欺诈,往往需要涉及不同法律管辖区的调查活动,从而增加执法成本和实施时间)。

信用卡欺诈导致的金融损失并不是金融机构必须面对的唯一问题;由于失去信誉和可靠性,还会造成声誉损害。

此外,信用卡欺诈还可能对客户构成威胁;信用卡欺诈的一个令人不安的方面与身份盗窃现象的日益增多有关,身份盗窃可以通过伪造文件或通过获取身份文件的数字副本(例如,通过数据泄露、网络钓鱼邮件等方式)轻松实现。

处理信用卡欺诈

然而,根据前述讨论,金融机构随着时间的推移引入了防止欺诈的措施:实际上,金融机构已经推出了基于双重身份验证的安全措施,通过将一次性密码(OTP)代码通过短信发送到客户的手机号码,从而整合传统的身份验证程序,以防止支付工具的滥用。

然而,事实仍然是,这些措施还不够,金融机构因信用卡欺诈而遭受的货币损失仍然高达数十亿美元;因此,减少这些损失的最有效预防活动是基于欺诈检测和预防的程序。

与信用卡欺诈检测和预防相关的分析领域相当复杂,这将使我们有机会在实践中看到,使用预测分析、机器学习和大数据分析技术的不同分析方法。

本章中,我们将探讨使用云计算平台的优势(使用 IBM Watson 平台提供的工具),考虑到欺诈检测与防范需要集成不同的活动分析,以及集成异构数据源。

这将引导我们采用一种利用预测分析的检测方法,包括认知计算等创新方法。

用于欺诈检测的机器学习

在信用卡领域引入算法程序进行欺诈检测,代表了预测分析领域的一个重要试验台(正如我们很快会看到的那样)。在这一领域进行的科学研究的早期例子中,我们必须提到《用于信用卡欺诈检测的自适应机器学习》,该研究由 Andrea Dal Pozzolo 撰写,且可以在dalpozz.github.io/static/pdf/Dalpozzolo2015PhD.pdf获取,这是最为深入的科学研究之一,广泛揭示了如何有效地利用机器学习算法进行信用卡欺诈检测。

信用卡欺诈检测的算法选择和设计具有以下特点:

  • 有关欺诈交易的数据通常不易获得,因为金融机构不愿公开此类信息,以避免声誉损害以及保密合规要求。

  • 从技术角度来看,欺诈数据通常表现为非平稳分布,也就是说,它们随时间发生变化;这也与客户消费行为的变化有关。

  • 交易分布严重失衡,因为欺诈通常仅占总体交易的一个小比例;因此,分布显示出向真实交易的高度偏斜。事实上,我们通常只能衡量已被检测到的欺诈,而要估计未被检测到的欺诈实例(假阴性)则要困难得多。此外,欺诈通常是在事件发生后很长时间才被记录的。

由于欺诈交易的内在特征,这些误表示导致了在选择和设计检测与预防算法时所面临的挑战,例如:

  • 在数据分析中使用采样策略;在存在不平衡分布的情况下,选择欠采样/过采样策略可能更加有用。

  • 整合人工操作员在识别欺诈警报时生成的反馈。这一方面对于在非平稳数据存在的情况下改善算法学习过程尤为重要,而这些数据随着时间的推移不断变化。

所有这些都转化为一个欺诈检测与防范系统的开发,该系统能够集成大数据分析、机器学习算法以及人工操作员的反馈。因此,显而易见,使用云计算架构是必然的实施选择。

欺诈检测与防范系统

有多种可能的信用卡欺诈场景,包括以下几种:

  • 信用卡盗窃:这是实践中最常见的情况;犯罪分子盗窃信用卡并在短时间内尽可能多地消费。这种活动通常会很喧闹,可以通过与合法信用卡持有人的消费习惯进行比对,借助异常或不寻常的模式检测来识别。

  • 信用卡滥用:与前述情况不同,欺诈者不需要实际持有信用卡,只需知道与卡片相关的相关信息(如识别码、PIN 码、个人身份号码、卡号、设备代码等)即可。这是最隐蔽的欺诈场景之一,因为它是以悄无声息的方式进行的(与前述场景相比,它不那么显眼),而信用卡的合法拥有者通常并未意识到背后正在进行的欺诈活动。

  • 身份盗窃:在这种情况下,信用卡是基于虚假的个人信息发放的,或者通过利用毫无防备的第三方的个人信息,这些第三方发现自己在名下被收取服务费用以及进行的取款和支付。

我们应该牢记,欺诈场景随着时间的推移而发展,特别是在涉及金融服务和技术的流程和产品创新方面,这些技术被金融机构采纳。

同样,欺诈者根据信用卡发卡机构为防止和打击欺诈所采取的技术措施调整自己的行为。

要正确实施欺诈检测与防范系统FDPS),必须区分与信用卡欺诈管理相关的两项活动:

  • 欺诈检测:这是旨在正确和可靠地识别欺诈案件的一套程序;它是在欺诈发生后实施的。

  • 欺诈防范:这是旨在有效预防欺诈发生的一套程序;它是在欺诈发生之前实施的。

这两项活动的特点在于实施的程序类型不同,以及它们引入的时机也有所不同,具体如下:

  • 在欺诈预防的情况下,分析程序可以利用由领域专家处理的基于规则的警报系统(因此,需要操作员不断微调),或利用基于数据挖掘、机器学习、神经网络等的先进分析技术,通过这些技术可以自动发现数据分布中的模式。

  • 在欺诈检测的情况下,分析程序旨在根据现有数据正确分类欺诈,从而将其与真实交易区分开来。

实施 FDPS 的一个重要方面不仅是其能够实现的结果的可靠性,还包括其成本效益。如果实施成本高于由于欺诈造成的损失,那么采用 FDPS 就毫无意义!

这两项活动之间存在明显的权衡;如果无法防止欺诈尝试,那么必须尽可能快地检测到它。

同样,这两项活动都需要尽量减少假阳性(即那些被错误地视为欺诈的交易,实际上它们是合法的交易)数量,并避免由于假阳性引发的自动反应导致客户可能遭受的服务拒绝(例如,尽管交易是合法的,信用卡仍被自动封锁)。

管理假阳性的问题还包括人工操作员进行检查的可扩展性差;如果人工操作员执行的控制通常在正确识别真正的欺诈中起到决定性作用,那么对所有交易进行系统性的人力控制,实际上就是过度的。

这就是为什么正确实施自动化检测和预防程序来支持操作员分析变得至关重要。

在本章中,我们将探讨如何考虑管理大量数据所涉及的困难,这些数据往往是不平衡的,并且由于客户不断变化的购买习惯而持续变化,这与现有算法的关系。

在接下来的章节中,我们将研究在实施自动化预测模型时可以采用的可能策略,分析专家驱动策略与数据驱动策略之间的差异。

专家驱动的预测模型

专家驱动的方法包括基于由行业专家(而非偶然的)制定的规则来实施预测模型,因此专家驱动的方法也被定义为基于规则的方法。

规则遵循if...then...else形式的逻辑条件,旨在表示不同的欺诈场景及其相关的自动对策,这些对策是在对交易数据进行检查后自动采取的。

因此,一个可能的规则是:如果信用卡交易金额超过某一特定金额,并且与某一特定的日常频率相关(与客户购买习惯的历史系列进行比较),则将所有交易视为欺诈性交易,规则可能如下:

IF amount > $1,000 AND buying_frequency > historical_buying_frequency THEN fraud_likelihood = 90%

对于在地理位置上相距很远的后续交易,可能会呈现如下情况:

IF distance(new_transaction, last_transaction) > 1000 km AND time_range < 30 min THEN block_transaction

在第一种情况下,我们将看一个评分规则的例子,而在第二种情况下,我们将讨论一个阻止规则。

评分规则旨在根据常见经验规则估算与交易相关的欺诈概率,并通过在超过特定阈值后对事件进行分类,来实现这一目标。

阻止规则更加严格,因为它们不仅仅限于估算欺诈的概率。相反,它们旨在在交易完成之前拒绝交易的授权;因此,阻止规则必须基于更严格的逻辑条件(例如在我们的例子中,如果交易与前一次交易相隔不足半小时,并且执行地点之间的距离大于 1000 公里,则拒绝该交易。合理推测,同一客户在这么短的时间内不可能在如此远的地方之间移动)。

基于规则的预测模型的优点如下:

  • 警报实施的易用性

  • 警报理解的易用性

  • 更高的警报可解释性

专家驱动的预测模型的缺点同样显而易见:

  • 它们表达了主观判断,可能会因执行这些模型的专家不同而有所差异。

  • 它们只能处理少数几个重要变量及其相互关系。

  • 它们基于过去的经验,无法自动识别新的欺诈模式。

  • 需要专家不断地手动微调规则,以便考虑到欺诈者所采用的欺诈策略的演变。

因此,这些缺点有利于推动数据驱动预测模型的采用。

数据驱动预测模型

数据驱动预测模型利用自动化学习算法,试图根据数据驱动的学习方法调整其预测,持续更新检测和预防程序,并基于动态识别的行为模式进行操作。

数据驱动预测模型中使用的算法源自多个定量分析领域,从统计学到数据挖掘和机器学习,旨在学习数据中隐藏或潜在的模式。

在数据驱动预测模型的实现中,机器学习算法的特权角色一目了然;机器学习使得基于对数据进行训练所获得的模型,能够识别出预测模型。

此外,在欺诈检测领域使用机器学习有几个优点:

  • 分析多维数据集的能力(数据集特征数量较多,代表欺诈的可能解释变量)

  • 关联各个识别特征之间的能力

  • 动态更新模型、使其适应欺诈者所采用策略变化的能力

  • 机器学习采用数据驱动的方法,实时利用大量数据(大数据)

鉴于此,数据驱动的预测模型通常比基于规则的模型更具鲁棒性和可扩展性。

然而,与基于规则的模型不同,数据驱动预测模型往往表现得像“黑箱”,这意味着它们生成的警报很难解释和证明(例如,在客户的交易被基于算法自动决策拒绝后,客户提出澄清要求时)。

同样,数据的特性本身可能会导致算法正确实现时的困难;以信用卡为例,交易分布存在重要的不规则性,如不平衡、非平稳和偏斜。因此,必须仔细选择能够充分处理这些不规则性的机器学习算法。

特别是在非平稳数据的情况下(即数据随着客户购买行为的变化而随时间变化),算法必须仔细更新自身的学习参数,权重最近的数据,或忽略过时的样本。

数据驱动预测模型的一个无可争议的优点是能够将操作员的反馈集成到预测中,从而提高程序的准确性。

操作员的反馈,事实上,具有更高的可靠性,能正确分类欺诈案件,从而减少假阴性的数量(即可能未被发现的欺诈行为),并可以自动集成到数据驱动的预测模型中。

相反,基于规则的模型需要手动修订,以考虑操作员的反馈。

结合专家驱动和数据驱动预测模型的优势构成了 FDPS 的核心优势,正如我们接下来将看到的那样。

FDPS – 集两者之长

因此,专家驱动和数据驱动预测模型可以在 FDPS 中结合使用,以利用两种方法的优势,通过减少假阴性和假阳性来提高预测的准确性。

基于规则的模型通常会减少假阴性的数量,尽管这会增加假阳性的数量;与数据驱动模型结合使用时,可以通过减少假阳性来改善预测。

此外,正如我们所看到的,数据驱动的模型允许将操作员的反馈与其他大数据源集成,从而有助于动态更新 FDPS。

FDPS 的自动化维护和调优活动需要实施能够自主学习新预测模式的机器学习算法,这些模式从大量数据中开始。

如前所述,信用卡交易的统计分布具有非平稳数据的特点(数据的特征随着消费习惯的变化而变化),而且数据通常偏向于代表合法交易的大类,而不是代表欺诈行为的小类。

这是因为欺诈案件的数量相对于总交易数是极少的(此外,欺诈交易的检测通常需要更长时间,因此欺诈交易的类别通常较小)。

并非所有的机器学习算法都能有效处理同时具备非平稳和不平衡特征的数据。因此,需要适当选择算法,以获得可靠和准确的预测。

从不平衡和非平稳数据中学习

在第一章《面向网络安全专业人士的 AI 简介》中,我们看到了机器学习算法如何分为监督学习和无监督学习;这一划分在信用卡欺诈检测中同样有效,尽管必须注意启发这两类算法的不同假设。这是因为它们对预测的可靠性和准确性有重要影响。

在监督学习算法的情况下,假设有一个已经分类的数据集(有标签样本);也就是说,每个样本之前已与两种可能类别之一(合法或欺诈)关联。

因此,监督算法是基于这些信息进行训练的,它们所做的预测是由在训练样本上进行的先前分类所决定的,这可能导致假阴性的增加。

无监督算法则不依赖于任何关于样本数据(无标签样本)可能分类的先前信息,因此必须独立推断数据可能属于的类,以便更容易地产生假阳性。

处理不平衡数据集

在信用卡交易的情况下,我们说过数据分布既是不平衡的,又是非平稳的。

解决不平衡数据分布问题的方法是在进行算法训练之前对类别进行重新平衡。

常用于重平衡样本类别的策略包括对数据集进行欠采样和过采样。

本质上,欠采样包括随机删除属于某一类别的一些观察结果,以减少其相对一致性。

在不平衡分布的情况下,例如与信用卡交易相关的分布,如果我们排除主要类别(代表合法交易)的随机样本,我们可以合理地期望数据的分布由于数据的移除不会发生根本性改变(可以可靠地视为冗余)。

然而,我们总是面临着消除包含相关信息的数据的风险。因此,确定正确的采样水平并不总是即时的,因为它取决于数据集的具体特征,因此需要使用适应性策略。

另一种数据采样策略包括过采样,即通过在较小类别中生成合成样本来增加其大小。

与过采样技术相关的缺点包括引入过拟合的风险,以及增加模型的训练时间。

处理非平稳数据集

为了管理分布的非平稳特性,过于强调由人工操作员获得的反馈可能是有用的,这有助于改善受监督样本的分类。

因此,在存在非平稳数据的情况下,使用一组分类器(集成学习)可能是有用的,其训练是在不同的样本上进行的,以提高整体预测准确性。

通过集成不同的分类器,可以将基于新观察到的知识与先前获得的知识结合起来,根据其分类能力对每个分类器进行加权,排除那些不再能够表示数据分布随时间变化的分类器。

信用卡欺诈检测的预测分析

要充分解决欺诈检测问题,必须开发预测分析模型,即能够使用数据驱动方法识别数据中趋势的数学模型。

不同于描述性分析(其范式由商业智能BI)构成),后者仅限于根据应用描述性统计产生的度量对过去数据进行分类(例如总和、平均值、方差等),准确描述正在分析的数据特征;相反,通过查看现在和过去的情况,预测分析试图以一定的概率预测未来事件。它通过挖掘分析数据中的隐藏模式来实现这一点。

作为数据驱动的预测分析,利用数据挖掘和机器学习技术进行预测,且基于对大量可用数据(大数据分析)的分析。

在接下来的章节中,我们将探索如何开发用于信用卡欺诈分析的预测分析模型。我们将学习以下内容:

  • 利用大数据分析整合来自不同来源的信息

  • 结合不同的分类器(集成学习)以提高预测性能

  • 使用 bagging 和 boosting 算法来开发预测模型

  • 使用采样技术对数据集进行重平衡,从而提高预测准确性

让我们从利用大数据分析开发预测模型来管理信用卡欺诈检测的优势开始了解。

在欺诈检测中采用大数据分析

组织普遍采用的传统 ETL 解决方案,这些方案基于关系型数据库和数据仓库的数据架构,毫无疑问能够执行基于描述性分析的报告(例如商业智能报告),但无法通过数据驱动方法处理大量数据,而这种方法是预测分析的典型特征。

因此,有必要采用数据架构,通过使用功能编程范式(如 MapReduce、NoSQL 原语等)来实现处理的可扩展性。

可以利用大数据分析技术,并将其与机器学习和数据挖掘算法结合,以实现欺诈检测的自动化。

采用大数据分析范式有助于组织最大化利用其信息资产,这些资产通常来自不同(且往往异构)的数据源。这使得能够实施先进的情境意识形式,进而实时调整检测程序以适应情境变化。

众所周知,非法活动往往是相互关联的,能够构建一个整体的欺诈活动图景前提是要不断监控不同的信息源。

采用云计算平台可以促进数据的实时监控和分析,这也使得聚合各种数据源成为可能。

比如,想象一下将组织内部产生的数据和信息与公开可用的网络数据、社交媒体及其他平台的数据进行整合。通过整合这些不同的信息源,可以重构待监控的金融交易的情境(例如,通过社交媒体,你可能发现持卡人当前位于一个远离正在进行信用卡交易地点的地理位置)。

同样地,不同数据源的整合可以增强数据集特征;也就是说,从数据集中现有的变量出发,引入新的变量,这些变量可以描述合法持卡人的行为,并将其与欺诈者的行为进行比较。

例如,我们可以向现有变量中添加新的变量,这些变量包含重新计算的值,比如上一时间段的平均支出水平、每天的购买次数,以及通常在哪些商店(包括电商网站)进行购买。

通过这种方式,可以不断更新客户档案,及时发现潜在的行为异常和巩固的消费习惯。

集成学习

从数据到算法的过渡,前面我们提到过,在非平稳数据的情况下,引入分类器集成可能比单独使用个别分类器更有助于提高整体预测准确性。

因此,集成学习的目的是将不同的分类算法结合起来,从而获得一个分类器,能够提供比单个分类器更好的预测结果。

为了理解为什么集成分类器表现得比单个分类器更好,我们需要想象我们有若干个二元分类器,它们都是相同类型,具有在 75%的情况下做出正确预测,而在剩余 25%的情况下做出错误预测的能力。

通过使用组合分析和二项分布(因为我们考虑的是二元分类器),可以证明,使用集成分类器而非单个分类器时,获得正确预测的概率会提高(而错误的概率则会降低)。

例如,如果我们有 11 个二元分类器进行集成学习,那么错误率将降低到 3.4%(相比于单个分类器的 25%错误率)。

如需正式证明,请参考 Sebastian Raschka 的《Python 机器学习(第二版)》,由 Packt 出版。

你可以使用多种方法来结合分类器;其中一种方法是使用多数投票法(也称为多数投票原则)。

多数投票原则是指,在单个分类器做出的预测中,我们选择那个显示最高频率的预测。

用正式的术语来说,这就转化为计算位置统计量之一,称为众数,即频率最高的类别。

假设我们有 n 个分类器,C[i](x),需要确定最被投票通过的预测 y,即大多数单个分类器确认的预测。我们可以写出以下公式:

显然,我们可以在可用的不同类型的算法中选择个别分类器(如决策树、随机森林、支持向量机SVMs)等)。

同时,也有几种方法可以创建集成分类器,如下所示:

  • 袋装法(Bootstrap Aggregating)

  • 提升法

  • 堆叠法

使用袋装法,可以通过选择不同的训练集并应用自助重采样技术来减少个体估计器的方差。

通过提升法,我们可以创建一个集成估计器,从而减少个体分类器的偏差。最后,通过堆叠法,将通过异构估计器获得的不同预测结果结合起来。

我们将在接下来的章节中分析创建集成估计器的不同方法。

袋装法(Bootstrap Aggregating)

自助法(bootstrap)是指对数据集进行有放回抽样的操作。因此,袋装法将每个自助样本与一个单独的估计器相关联;集成估计器通过对个别分类器应用多数投票法来实现。

要考虑的自助抽样次数可以预先确定,也可以通过使用验证数据集进行调整。

袋装法特别适用于在有放回抽样帮助下重新平衡原始数据集的情况,从而减少总方差。

提升算法

提升法则是使用从数据中提取的加权样本,这些样本的权重根据个别分类器报告的分类错误进行迭代调整,从而减少其偏差。

更重要(加权)的是对那些最难分类的观测值。

最著名的提升算法之一是自适应提升AdaBoost),其中第一个分类器在训练集上进行训练。

对于第一个分类器错误分类的样本,相关的权重会增加,然后在包含更新权重的数据集上训练第二个分类器,依此类推。迭代过程在达到预定的估计器数量时结束,或者当找到最佳预测器时结束。

AdaBoost 的主要缺点之一是该算法由于其顺序学习策略,无法并行执行。

堆叠法

堆叠法得名于集成估计器的构建方式,它是通过叠加两层来实现的,第一层由单个估计器组成,这些估计器的预测结果被传递到下层,在那里另一个估计器负责对接收到的预测进行分类。

与袋装法(bagging)和提升法(boosting)不同,堆叠法(stacking)可以使用不同类型的基础估计器,这些估计器的预测可以通过与之前不同的算法进行分类。

让我们来看看一些集成估计器的例子。

Bagging 示例

在以下示例中,我们将使用 Python 的 scikit-learn 库实例化一个 BaggingClassifier 类的对象,并将 DecisionTreeClassifier 类型作为基本分类器传入;通过 n_estimators 参数设置要实例化的 DecisionTreeClassifier 类型的基本估计器的数量。

可以在 BaggingClassifier 类型的bagging实例上调用fit()predict()方法,这些方法通常也会在常见的分类器上调用。

正如我们所知道的,bagging 方法使用有放回抽样。由于这一点,我们可以设置每个基本估计器关联的最大样本数量(使用 max_samples 参数),并通过将同名 bootstrap 参数设置为 True 来激活自助采样机制,如下所示:

from sklearn.tree import DecisionTreeClassifier

from sklearn.ensemble import BaggingClassifier

bagging = BaggingClassifier(
            DecisionTreeClassifier(), 
            n_estimators=300,
            max_samples=100, 
            bootstrap=True
          )

使用 AdaBoost 提升方法

作为提升方法的示例,我们将实例化一个 scikit-learn 库中 AdaBoostClassifier 类型的对象,该库为我们提供了 AdaBoost 算法的实现;作为基本估计器,我们还将在此示例中使用 DecisionTreeClassifier 类的实例,并通过 n_estimators 参数设置基本估计器的数量

from sklearn.tree import DecisionTreeClassifier

from sklearn.ensemble import AdaBoostClassifier

adaboost = AdaBoostClassifier(
              DecisionTreeClassifier(),
              n_estimators=300
           )

另一种广泛使用的提升算法是 梯度提升 算法。为了理解梯度提升算法的特性,我们必须首先引入梯度的概念。

引入梯度

在数学上,梯度表示在给定点计算的偏导数;它还表示被考虑点的切线(斜率)。

梯度在机器学习中作为一种成本函数被用来最小化,以减少算法产生的预测误差。它的核心是最小化算法估计值与观察值之间的差异。

使用的最小化方法被称为梯度下降,它是一种优化分配给输入数据的权重组合的方法,以便获得估计值与观察值之间的最小差异。

因此,梯度下降法计算各个权重的偏导数,基于这些偏导数更新权重,直到达到对应于所求最小值的偏导数的驻点值。

梯度下降公式及其图形表示如下图所示:

(图片来源:Wikipedia,网址:https://commons.wikimedia.org/wiki/File:Gradient_descent.jpg

问题在于,梯度下降法返回的最小值可能对应一个全局最小值(即,无法再进一步最小化),但更有可能对应局部最小值;问题在于,梯度下降法无法确定是否已经到达局部最小值,因为优化过程在达到稳定值时停止。

梯度下降优化方法如图所示:

(图片来源:Wikipedia,网址:https://commons.wikimedia.org/wiki/File:Gradient_descent_method.png)

现在让我们看看梯度提升算法的特点。

梯度提升

类似于 AdaBoost 算法,梯度提升也在每一轮迭代中基于估计器返回的值来修正估计器;在梯度提升的情况下,修正是基于前一轮估计器生成的残差误差,而不是要分配的权重(如在 AdaBoost 中)。

接下来,我们将展示一个使用scikit-learn库中GradientBoostingClassifier类的示例。

默认估计器是决策树,其特征通过参数进行指定(例如max_depth,用于设定决策树的最大深度)。

另外,请注意learning_rate参数,它必须与warm_start参数一起考虑。

分配给learning_rate参数的值决定了每个估计器对集成分类器的贡献;如果分配的值为,则需要更多的估计器(通过n_estimators参数设置)来继续在训练集上进行集成拟合。

对于learning_raten_estimators参数的最优值决策,必须考虑与过拟合相关的问题(即,模型在训练数据上的过度拟合可能导致的泛化误差)。克服这些问题的一种方法是设置warm_start=True参数,这将决定在训练阶段的提前停止,示例如下:

from sklearn.ensemble import GradientBoostingClassifier

gradient_boost = GradientBoostingClassifier(
                   max_depth=2, 
                   n_estimators=100, 
                   learning_rate=1.0,
                   warm_start=True
                 )

极限梯度提升(XGBoost)

与梯度提升相似的算法是 XGBoost 算法。

它是梯度提升的扩展,证明在处理大规模数据时更加适用,因为它具有更好的可扩展性。

XGBoost 同样使用梯度下降法来最小化估计器的残差误差,并且特别适合并行计算(这一特点使其更适合云计算)。

我们将在稍后使用 IBM Watson 在 IBM Cloud 平台上实施信用卡欺诈检测时看到 XGBoost 算法的应用。

不平衡数据集的采样方法

在进入欺诈检测操作阶段之前,最后一个需要考虑的方面是数据不平衡的管理。

我们已经说过,信用卡交易的一个特征是其向真实交易分布的失衡。

为了管理数据中的这种不对称性,我们可以使用不同的采样方法来重新平衡交易数据集,从而使分类器的性能得到提升。

最常用的两种采样模式是欠采样和过采样。通过欠采样,从最多的类别(在我们这里是合法交易类别)中随机移除一些样本;通过过采样,向最少的类别添加合成样本。

使用 SMOTE 进行过采样

在过采样方法中,我们有合成少数类过采样技术 (SMOTE);它通过插值生成合成样本,这些样本基于被过采样类别内的现有值。

实际上,合成样本是基于类中观察值周围识别到的聚类生成的,因此计算k-最近邻 (k-NNs)。

根据需要重新平衡类别的合成样本数量,随机选择一定数量的 k-NN 聚类,围绕这些聚类通过插值生成落在选定聚类中的值的合成示例。

采样示例

以下示例取自官方 Python 库 imbalanced-learn 文档,该库实现了包括欠采样和过采样算法等多种方法。

让我们来看一个使用RandomUnderSampler类的欠采样技术示例:

# From the Imbalanced-Learn library documentation:
# https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.under_sampling.RandomUnderSampler.html

from collections import Counter
from sklearn.datasets import make_classification
from imblearn.under_sampling import RandomUnderSampler 

X, y = make_classification(n_classes=2, class_sep=2,
 weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0,
n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10)
print('Original dataset shape %s' % Counter(y))

rus = RandomUnderSampler(random_state=42)
X_res, y_res = rus.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

这是一个使用 SMOTE 类的过采样技术示例:

# From the Imbalanced-Learn library documentation:
# https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.over_sampling.SMOTE.html

from collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE 

X, y = make_classification(n_classes=2, class_sep=2,
   weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0,
   n_features=20, n_clusters_per_class=1, n_samples=1000,    
   random_state=10)

print('Original dataset shape %s' % Counter(y))
Original dataset shape Counter({1: 900, 0: 100})

sm = SMOTE(random_state=42)
X_res, y_res = sm.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))
Resampled dataset shape Counter({0: 900, 1: 900})

了解 IBM Watson Cloud 解决方案

现在是时候了解市场上最有趣的基于云的解决方案之一,它将让我们看到信用卡欺诈检测实际应用的具体示例:我们所说的是 IBM Watson Cloud 解决方案,它在其他创新概念中引入了认知计算。

通过认知计算,有可能模拟典型的人类模式识别能力,从而为决策提供充分的上下文感知。

IBM Watson 可以成功应用于多个现实场景;以下是几个示例:

  • 增强现实

  • 犯罪预防

  • 客户支持

  • 面部识别

  • 欺诈预防

  • 医疗保健和医学诊断

  • 物联网 (IoT)

  • 语言翻译和自然语言处理 (NLP)

  • 恶意软件检测

在详细了解 IBM Watson Cloud 平台之前,让我们先看看与云计算和认知计算相关的优势。

云计算优势

随着更高带宽网络的普及,再加上低成本计算机和存储设备的可用性,云计算的架构模型迅速普及,得益于虚拟化解决方案的可用,无论是在软件还是硬件方面。

云计算的核心特点是架构的可扩展性,这也是其商业成功的决定性因素。

采用云计算解决方案的组织成功优化了 IT 领域的投资,从而提高了利润率;这些组织不再需要基于最坏情况(即考虑到工作负载高峰,即使只是暂时的)来规划其技术基础设施,而是通过云解决方案的按需模式,从而降低了固定成本,将其转化为可变成本。

技术投资质量的提升使得组织能够专注于管理和分析构成公司信息资产的数据。

实际上,云计算使得高效存储和管理大量数据成为可能,保证了高性能、高可用性和低延迟;为了提供这些访问和性能保障,数据存储并复制在分布在不同地理区域的服务器上。此外,通过对数据进行分区,可以获得与架构可扩展性相关的优势。

更具体来说,可扩展性与通过增加资源到架构中来管理日益增长的工作负载相关——成本以线性方式增加,且与增加的资源数量成正比。

实现数据可扩展性

传统架构(基于关系数据库和数据仓库)面临的主要问题之一是,这些解决方案在应对数据爆炸性增长时无法良好扩展。此类架构即使在设计阶段,也需要适当的规模规划。

随着大数据分析的普及,因此有必要转向其他数据存储范式,称为分布式存储系统,这些系统能够精确防止数据管理和存储中的瓶颈。

云计算广泛使用这些分布式存储系统来实现对大量数据的分析(大数据分析),即使是在流媒体模式下。

分布式存储系统由非关系型数据库组成,称为 NoSQL 数据库,数据以键值对的形式存储。这使得可以在多个服务器上以分布式模式管理数据,并遵循如 MapReduce 等函数式编程范式。这反过来使得数据处理可以并行执行,充分利用云所提供的分布式计算能力。

使用 NoSQL 数据库还可以以灵活的方式管理数据,而无需在分析变化时重新组织数据的整体结构。

然而,基于关系数据库的传统解决方案需要重新配置几乎整个档案结构,这使得数据在长时间内无法使用。在需要实时验证预测模型准确性并根据此做出业务决策的背景下,这种做法已经不再可接受;这一点在网络安全领域的决策制定中尤为重要。

云交付模型

架构的可扩展性,再加上按需模式管理资源的能力,使得提供商可以提供不同的云交付模型:

  • 基础设施即服务IaaS):提供商部署 IT 基础设施,如存储能力和网络设备。

  • 平台即服务PaaS):提供商部署中间件、数据库等。

  • 软件即服务SaaS):提供商部署完整的应用程序。

IBM Cloud 平台提供了一种交付模型,包括 IaaS 和 PaaS,以及一系列可以集成到组织开发的应用程序中的云服务,例如:

  • 视觉识别:这使得应用程序能够在图像和视频中定位信息,如物体、人脸和文本;平台提供的服务包括检查预训练模型的可用性,以及使用企业数据集进行训练的机会。

  • 自然语言理解:该服务可以基于对文本的分析提取情感信息;如果你想从社交媒体中提取信息(例如,了解在某次信用卡交易中,信用卡持有者是否真的在外地度假),这项服务特别有用。该服务能够识别与人物、地点、组织、概念和类别相关的信息,并且可以通过 Watson Knowledge Studio 根据公司特定的应用领域进行适配。

IBM Cloud 平台还提供一系列用于应用程序开发的高级工具:

  • Watson Studio:这使得项目管理成为可能,并提供团队成员间协作的工具。通过 Watson Studio,可以添加数据源、创建 Jupyter Notebooks、训练模型,并使用许多其他功能来促进数据分析,如数据清洗功能。我们将有机会进一步深入了解 Watson Studio。

  • 知识工作室:这使得根据公司的特定需求开发定制模型成为可能;开发完成后,这些模型可以由 Watson 服务使用,作为预定义模型的补充或替代。

  • 知识目录:这使得公司数据的管理和共享成为可能;该工具还可以执行数据清洗和整理操作,从而通过安全策略对数据访问权限进行概况化管理。

在 IBM 云平台提供的主要优势中,有一个不容忽视的优势,那就是可以实施利用认知计算的先进解决方案。让我们来看一看这是什么。

授权认知计算

人工智能的传播自始至今伴随着过度且毫无根据的担忧;许多作者和评论员预见了灾难性的场景,在这些场景中,机器(在不久的将来)将超越人类。这样的灾难的原因,正是人工智能的崛起。

现实是,尽管计算机取得了惊人的成功,它们仍然是天才型傻瓜。

毫无疑问,计算机的计算能力远超人类,且超出多个数量级;IBM Watson 在“世纪之战”中的胜利——计算机击败了当时的世界象棋冠军加里·卡斯帕罗夫——似乎宣布了人工智能最终战胜人类认知能力的到来。

然而,尽管计算能力有限,人类在许多技能方面依然无可匹敌,比如适应能力、互动能力、判断力等。

比如,我们人类可以一眼认出一个人(或物体),无需经过大量样本数据的训练;只需一张照片(或一张画像),就足以在人群中认出被描绘的人。计算机远未达到这种专业水平。

所以,问题并不是用机器替代人类;相反,眼前最可能的情景是,人类和机器将更加紧密地合作,以日益普及的方式整合彼此的技能。

这就是认知计算的含义:将人类的能力与计算机的计算能力结合起来,携手应对当代社会日益增长的复杂性。

在这种共生关系中,机器将其巨大的计算能力和取之不尽的记忆提供给人类,从而使人类能够增强其判断、直觉、同理心和创造力等能力。

从某种意义上说,通过认知计算,机器不仅使我们能够增强我们的五种自然感官,还赋予了我们第六种人工感官:情境意识。

我们已经多次提到,尤其是在网络安全领域,遇到的一个主要难题是,如何从大量分散且零散的信息中,重建一个准确的整体图景。

面对我们从各个数据源不断接收到的大量数据和信息,人类的能力显得力不从心;大数据分析超出了人类分析的能力,正是因为大数据的特点,包括无数维度(由许多不同的特征以及数据量组成)。

然而,大数据使我们能够定义语义上下文,在此上下文中我们可以进行分析;就好像它们增加了我们的感知能力,添加了无数的人工传感器。

只有机器的计算能力才能过滤我们不断从人工传感器到人类判断技能、直觉中接收到的大量信息,为我们提供整体意义,并使我们能够理解这些信息。

在云端导入示例数据并运行 Jupyter Notebook

现在,让我们学习如何使用 IBM Watson 平台。首先,我们需要创建一个帐户,如果还没有的话;只需连接到此处提供的 IBM Cloud 平台主页链接:dataplatform.cloud.ibm.com/,你将看到以下屏幕:

IBM Watson 主页

为继续注册,选择前面的截图中显示的“试用免费版(注册)”。我们将自动被重定向到注册表单,见下方截图:

IBM Watson 注册页面

注册完成后,我们可以从主页重新登录:

IBM Watson 登录表单

登录后,我们可以创建一个新项目:

IBM Watson 开始创建项目页面

我们可以选择想要创建的项目类型:

IBM Watson 项目选择

在我们的例子中,我们将选择数据科学,如下所示:

IBM Watson 数据科学项目

我们将项目命名为 CREDIT CARD FRAUD DETECTION(或我们选择的其他名称):

IBM Watson 新建项目页面

我们现在可以通过选择“添加到项目 | 数据”将数据集添加到我们的项目中:

IBM Watson-添加数据

要添加数据集,只需点击“查找并添加数据”,然后转到“文件”标签。从那里,您可以点击并添加来自计算机的数据文件。

我们将使用的数据集是信用卡数据集,下载格式为 .csv,可以在www.openml.org/data/get_csv/1673544/phpKo8OWT 下载:

信用卡数据集已按公共领域许可证发布(creativecommons.org/publicdomain/mark/1.0/)(www.openml.org/d/1597),并且在 Andrea Dal Pozzolo、Olivier Caelen、Reid A. Johnson 和 Gianluca Bontempi 的论文 Calibrating Probability with Undersampling for Unbalanced Classification 中获得了致谢,发表于 计算智能与数据挖掘CIDM)研讨会,IEEE,2015 年。

数据集包含 31 个数值型输入变量,如时间(表示每笔交易之间的时间间隔)、交易金额和类特征。

类特征是一个二元变量,仅取值 1 或 0(分别表示欺诈或合法交易)。

数据集的主要特点是极度不平衡,欺诈交易仅占所有交易的 0.172%。

添加数据集后,我们可以通过选择“添加到项目 | 笔记本”来将 Jupyter 笔记本添加到项目中,如下截图所示:

IBM Watson-添加笔记本

要创建 Jupyter 笔记本,请执行以下步骤:

  1. 点击创建一个笔记本

  2. 选择标签

  3. 为笔记本输入名称

  4. 可选地,输入笔记本描述

  5. 输入笔记本 URL:github.com/IBM/xgboost-smote-detect-fraud/blob/master/notebook/Fraud_Detection.ipynb

  6. 选择运行时

  7. 点击创建

恭喜!你已经成功配置了项目,并准备好在 IBM Cloud 平台上的 IBM Watson Studio 中查看信用卡欺诈检测模型的实际应用。

使用 IBM Watson Studio 进行信用卡欺诈检测

让我们来看一下我们加载到 IBM Watson Studio Jupyter 笔记本中的欺诈检测预测模型(完整代码由 IBM 发布,并采用 Apache 2.0 许可证,链接:github.com/IBM/xgboost-smote-detect-fraud/blob/master/notebook/Fraud_Detection.ipynb )的实际应用。

第一步操作是将加载的 .csv 格式的信用卡数据集转换为 pandas DataFrame。可以按以下步骤进行操作。

选择下面的单元格,读取数据并将其转换为笔记本中的 DataFrame 部分,然后执行以下步骤:

  1. 使用“查找并添加数据”及其“文件”标签。你应该能看到我们之前上传的文件名。

  2. 选择插入代码。

  3. 点击插入 Pandas DataFrame。

  4. 一旦数据集被转换为 pandas DataFrame,我们可以通过用自己选择的名称替换 Watson Studio 自动分配的名称来重命名它,如以下代码片段所示:

# Rename the dataframe to df

df = df_data_2

此时,我们可以使用train_test_split方法将数据集分为训练集和测试集;这通过使用常见的拆分比例(30%用于测试,剩余的 70%用于训练)来完成,如以下示例所示:


from sklearn.model_selection import train_test_split

x = df[['Time', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10',
       'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20',
       'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'Amount']]
y = df['Class']

xtrain, xtest, ytrain, ytest = train_test_split(x, y, test_size=0.30,
random_state=0)

请记住,数据集包含 31 个数值型输入变量,其中Time特征表示每次交易与数据集中的第一次交易之间经过的秒数,而Amount特征表示交易金额。

Class特征是响应变量,在欺诈情况下取值为1,否则为0

出于保密原因,大多数变量(以V1V2、……、V28表示)的含义未被揭示,特征通过主成分方法进行了转换。

此时,我们可以引入我们的第一个集成分类器,以测试其在数据集上的分类质量。

使用RandomForestClassifier进行预测

选择落在集成算法中最常用的一个,即随机森林算法。

这种类型的算法被RandomForestClassifier类用于从训练集随机提取的子集创建一组决策树。

该算法是使用袋装技术的学习集成方法,因此特别适用于减少模型的过拟合。

让我们看一个RandomForestClassifier的例子及其准确度得分:

from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics

rfmodel = RandomForestClassifier()
rfmodel.fit(xtrain,ytrain)
ypredrf = rfmodel.predict(xtest)

print('Accuracy : %f' % (metrics.accuracy_score(ytest, ypredrf)))

Accuracy : 0.999414

模型的准确率相当高(99.9414%),证明了集成学习的有效性。

让我们看看是否可以通过使用另一个分类器集成,利用提升技术,来改善预测结果。

使用GradientBoostingClassifier进行预测

现在我们将使用基于 AlgaBoost 的GradientBoostingClassifier

集成分类器使用的算法采用了提升技术;它还使用梯度下降来最小化成本函数(由各个基础分类器返回的残差误差表示,这些基础分类器也是由决策树构成的)。

在以下代码中,我们可以看到梯度提升集成分类器的应用:

from sklearn import ensemble

params = {'n_estimators': 500, 'max_depth': 3, 'subsample': 0.5,
          'learning_rate': 0.01, 'min_samples_leaf': 1, 'random_state': 3}

clf = ensemble.GradientBoostingClassifier(**params)
clf.fit(xtrain, ytrain) 

y_pred = clf.predict(xtest) 

print("Accuracy is :")
print(metrics.accuracy_score(ytest, y_pred))

Accuracy is : 0.998945085858

模型的准确率仍然很高,但没有比RandomForestClassifier有更大提升;事实上,我们的预测准确率已经达到了 99.89%,但我们可以做得更好。

使用XGBoost进行预测

我们现在将通过使用XGBoost来进一步改进预测,XGBoost 是梯度提升算法的改进版,旨在优化性能(使用并行计算),从而减少过拟合。

我们将使用xgboost库中的XGBClassifier类,它实现了极限梯度提升分类器,如以下代码所示:

from sklearn import metrics
from xgboost.sklearn import XGBClassifier

xgb_model = XGBClassifier()

xgb_model.fit(xtrain, ytrain, eval_metric=['error'], eval_set=[((xtrain, ytrain)),(xtest, ytest)])

y_pred = xgb_model.predict(xtest)  

print("Accuracy is :")
print(metrics.accuracy_score(ytest, y_pred))

Accuracy is : 0.999472542929

准确率进一步提高,我们已经达到了 99.9472%的准确率(比RandomForestClassifier的准确率 99.9414%略高)。虽然不错,但我们现在必须仔细评估我们预测的质量。

评估我们预测的质量

为了正确评估分类器所获得的预测结果的质量,我们不能仅仅满足于accuracy_score,还必须使用其他度量标准,例如F1 得分ROC 曲线,这些我们之前在第五章《基于 AI 的网络异常检测》中遇到过,该章节讨论了与异常检测相关的主题。

F1 值

为了方便起见,我们简要回顾一下之前介绍的度量标准及其定义:

敏感度或真正率(TPR)= 真阳性 / (真阳性 + 假阴性);

在这里,敏感度也被称为召回率:

假阳性率(FPR)= 假阳性 / (假阳性 + 真阴性);

精确度 = 真阳性 / (真阳性 + 假阳性)

基于这些指标,可以估算出 F1 得分,它表示精确度和敏感度之间的调和平均数:

F1 = 2 * 精确度 * 敏感度 / (精确度 + 敏感度)

F1 得分可以用来评估从预测中获得的结果;最佳估算值是 F1 接近 1 的值,而最差的估算值对应 F1 接近 0 的值。

ROC 曲线

在假阳性和假阴性之间,通常存在一种权衡;减少假阴性的数量会导致假阳性的增加,为了检测这种权衡,使用了一条特定的曲线,称为 ROC 曲线。如下图所示:

(图片来源:维基百科,链接:commons.wikimedia.org/wiki/File:ROC_curve.svg

ROC 曲线是通过scikit-learn中的roc_curve()函数计算的,该函数以目标值和相应的概率作为参数,示例代码如下:

from sklearn.metrics import roc_curve

FPR, TPR, OPC = roc_curve(targets, probs)

我们应该注意到真正率(TPR或敏感度)、假阳性率(FPR)和 ROC 曲线之间的关系(另外,OPC参数代表一个控制系数,称为操作特征,它标识了曲线上可能的分类阈值)。因此,我们可以通过绘制TPR值与OPC控制系数的关系来表示敏感度:

# Plotting Sensitivity
plt.plot(OPC,TPR)

我们可以看到,随着OPC值的增加,敏感度(TPR)会降低;同样地,我们可以通过将TPRFPR进行比较,绘制 ROC 曲线:

# Plotting ROC curve
plt.plot(FPR,TPR)

AUC(ROC 曲线下的面积)

ROC 曲线允许我们通过将TPRFPR绘制在一起,来评估分类器的性能(曲线上的每个点对应一个不同的分类阈值)。

我们还可以通过 ROC 曲线下的面积来比较不同的分类器,以找出哪个分类器更准确。

为了理解这种比较的逻辑,我们必须考虑到,在 ROC 空间内,最优分类器是由点x = 0 和 y = 1 的坐标标识的(它们对应的是没有假阴性和假阳性的极限情况)。

为了比较不同的分类器,我们可以计算每个分类器对应的ROC 曲线下面积AUC)值;获得最高 AUC 值的分类器是最准确的。

我们还可以考虑两个特殊分类器的 AUC 值:

  • 最佳分类器的 AUC 是 1 x 1 = 1

  • 最差分类器的 AUC 是 0.5

此外,AUC 也是衡量不平衡数据集的一种指标。

我们可以使用scikit-learn来计算AUC,如下所示:

from sklearn.metrics import auc

AUC = auc(FPR, TPR)

基于我们所说的内容,现在我们可以更准确地评估由我们的分类器得到的预测结果,并相互比较。

比较集成分类器

现在,我们可以通过比较它们来计算每个分类器的主要准确性度量。

RandomForestClassifier 报告

RandomForestClassifier度量的分类报告如下所示:

print('classification report')
print(metrics.classification_report(ytest, ypredrf))
print('Accuracy : %f' % (metrics.accuracy_score(ytest, ypredrf)))
print('Area under the curve : %f' % (metrics.roc_auc_score(ytest, ypredrf)))

classification report
             precision    recall  f1-score   support

          0       1.00      1.00      1.00     17030
          1       0.96      0.73      0.83        33

avg / total       1.00      1.00      1.00     17063

Accuracy : 0.999414
Area under the curve : 0.863607

GradientBoostingClassifier 报告

GradientBoostingClassifier度量的分类报告如下所示:

print('classification report')
print(metrics.classification_report(ytest, y_pred))
print("Accuracy is :")
print(metrics.accuracy_score(ytest, y_pred))
print('Area under the curve : %f' % (metrics.roc_auc_score(ytest, y_pred)))

classification report
             precision    recall  f1-score   support

          0       1.00      1.00      1.00     17030
          1       0.74      0.70      0.72        33

avg / total       1.00      1.00      1.00     17063

Accuracy is : 0.998945085858
Area under the curve : 0.848250

XGBClassifier 报告

XGBClassifier度量的分类报告如下所示:

print('classification report')
print(metrics.classification_report(ytest, y_pred))
print("Accuracy is :")
print(metrics.accuracy_score(ytest, y_pred))
print('Area under the curve : %f' % (metrics.roc_auc_score(ytest, y_pred)))

classification report
             precision    recall  f1-score   support

          0       1.00      1.00      1.00     17030
          1       0.93      0.79      0.85        33

avg / total       1.00      1.00      1.00     17063

Accuracy is : 0.999472542929
Area under the curve : 0.893881

通过比较 AUC 和 F1 分数值,这些值是通过各个分类器计算得出的,XGBClassifier依然是最准确的分类器,而GradientBoostingClassifier是三者中最不准确的。

使用 SMOTE 提高预测准确性

我们通过展示基于过采样的重平衡技术如何有助于提高预测准确性来总结我们的分析。

我们将使用 imbalanced-learn 库提供的 SMOTE 过采样算法实现,将欺诈样本从 102 增加到 500,并在重新采样的数据上再次使用RandomForestClassifier,如下所示:

from collections import Counter
from imblearn.over_sampling import SMOTE 

x = df[['Time', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10',
       'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20',
       'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'Amount']]

y = df['Class']

# Increase the fraud samples from 102 to 500

sm = SMOTE(random_state=42,ratio={1:500})
X_res, y_res = sm.fit_sample(x, y)
print('Resampled dataset shape {}'.format(Counter(y_res)))

Resampled dataset shape Counter({0: 56772, 1: 500})

# Split the resampled data into train & test data with 70:30 mix

xtrain, xtest, ytrain, ytest = train_test_split(X_res, y_res, test_size=0.30, random_state=0)

# Random Forest Classifier on resampled data

from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics

rfmodel = RandomForestClassifier()
rfmodel.fit(xtrain,ytrain)

ypredrf = rfmodel.predict(xtest)

print('classification report')
print(metrics.classification_report(ytest, ypredrf))
print('Accuracy : %f' % (metrics.accuracy_score(ytest, ypredrf)))
print('Area under the curve : %f' % (metrics.roc_auc_score(ytest, ypredrf)))

classification report
             precision    recall  f1-score   support

          0       1.00      1.00      1.00     17023
          1       0.97      0.91      0.94       159

avg / total       1.00      1.00      1.00     17182

Accuracy : 0.998952
Area under the curve : 0.955857

由于应用了合成过采样技术,我们可以看到 F1 分数和 AUC 都有所提高。

总结

我们已经学会了如何开发一个信用卡欺诈检测的预测模型,利用 IBM Cloud 平台和 IBM Watson Studio。

通过利用 IBM Cloud 平台,我们还学会了如何解决数据集中与信用卡交易相关的非平衡和非平稳数据的问题,并充分利用了集成学习和数据采样技术。

在下一章中,我们将深入探讨生成对抗网络GANs)。

第九章:GANs - 攻击与防御

生成对抗网络GANs)代表了深度学习在网络安全领域为我们提供的最先进的神经网络示例。GANs 可以用于合法目的,如身份验证程序,但它们也可以被利用来破坏这些程序。

在本章中,我们将讨论以下主题:

  • GANs 的基本概念及其在攻击和防御场景中的应用

  • 开发对抗样本的主要库和工具

  • 通过模型替代对深度神经网络DNNs)的攻击

  • 通过 GANs 对入侵检测系统IDS)的攻击

  • 利用对抗样本对面部识别程序的攻击

我们现在将通过介绍 GANs 的基本概念开始本章内容。

GANs 简述

GANs 在 2014 年发表的一篇著名论文中被提出(arxiv.org/abs/1406.2661),该论文由包括 Ian Goodfellow 和 Yoshua Bengio 在内的研究团队撰写,描述了一种特殊类型对抗过程的潜力和特点,这种过程被称为 GANs。

GANs 背后的基本思想很简单,它们通过让两个神经网络相互竞争,直到达到一个平衡的结果条件;然而,与此同时,利用这些直觉的可能性几乎是无限的,因为 GANs 能够学习如何模仿并人工重现任何数据分布,无论是面孔、声音、文本,还是甚至是艺术作品。

在本章中,我们将扩展生成对抗网络(GANs)在网络安全领域的应用,了解如何利用它们同时进行攻击(例如针对基于生物特征识别的安全程序的攻击)以及保护神经网络免受通过 GANs 进行的攻击。为了全面理解 GANs 的特点和潜力,我们需要介绍一些关于神经网络NNs)和深度学习DL)的基本概念。

深度学习概述

我们在第四章《恶意软件威胁检测》和第六章《保护用户身份验证》中已经遇到过神经网络(NN)。现在,我们将通过更加系统化的方式进一步探讨深度学习(DL)。深度学习是机器学习ML)的一个分支,旨在模仿人脑的认知能力,试图执行那些典型的高复杂度人类任务,如面部识别和语音识别。因此,深度学习旨在通过引入基于人工神经元的网络来模拟人脑的行为,这些神经元分布在多个层级并相互连接,并且这些层次的深度可以高或低,这也是“深度学习”这一术语中深度一词的由来。

深度学习和神经网络的概念并不新鲜,但仅在近年来,得益于数字架构领域的进展,它们才找到了实际的理论与实践应用,这一进展得到了计算能力的提升、分布式计算通过云计算的充分利用以及大数据分析所带来的几乎无限的训练数据的支持。

深度学习的潜力不仅在研究和商业领域得到了认可,还在网络安全领域得到了广泛应用。在网络安全中,越来越需要使用能够动态适应环境变化的解决方案,这些解决方案不仅采用静态检测工具,还能够动态学习如何自动识别新型攻击的算法,进而通过分析数据集中的代表性特征来发现可能的威胁,尤其是在这些数据集往往是噪声较多的情况下。

与传统的机器学习(ML)相比,从数学角度来看,深度学习也具有更高的复杂性,尤其是在广泛使用微积分和线性代数方面。然而,与机器学习相比,深度学习能够在准确性和算法在不同应用领域的潜在重用方面取得更好的结果。

通过使用互相连接的神经网络(NN)层,深度学习不仅限于分析原始数据集的特征,还能够通过创建新的特征重新组合这些数据,从而适应所需进行的分析的复杂性。

构成深度学习(DL)的人工神经网络层分析接收到的输入数据和特征,并将它们与各个内部层共享,这些内部层反过来处理外部层的输出数据。通过这种方式,从数据集中提取的原始特征被重新组合,产生新的特征,这些特征经过优化以供分析使用。

内部层之间的连接越多,深度和重新组合特征的能力越强,从而更好地适应问题的复杂性,将其分解为更具体、可管理的子任务。

我们已经提到,深度学习(DL)的构成元素是由人工神经元组成的神经网络(NN)层。现在,我们将更详细地研究这些构成元素的特点,从人工神经元开始。

人工神经元和激活函数

我们已经在第三章中遇到过一种特定类型的人工神经元——罗森布拉特的感知器,并且我们已经看到,这种人工神经元通过在超过阈值的正信号出现时自我激活,模拟了人类大脑中神经元的行为。

为了验证阈值以上是否存在正信号,使用了一个特殊的函数,称为激活函数,在感知器的情况下,具有以下特点:

在实际应用中,如果wx值的乘积——即输入数据与相应权重的乘积——超过某个阈值 ,则感知器被激活;否则,它保持静止。因此,激活函数的任务就是在验证某些条件后,决定是否激活人工神经元。

激活函数有不同的类型,但最常见的可能是修正线性单元ReLU),在其最简单的版本中,假设将函数max(0, wx)应用于输入值(与相应的权重相乘),结果即为激活值。

从形式上看,这可以表示为以下方程:

还有一种变体被称为LeakyReLU,如以下方程所示:

与普通 ReLU 不同,激活函数的泄漏版本返回的是wx的软化值(对于wx的负值,而不是0),这个值是通过应用乘法常数 得到的,通常该常数的值较小,接近0(但不等于0)。

从数学角度看,ReLU 激活函数代表了一种非线性变换,将输入值与各自权重的乘积关系转化为非线性关系。

通过这种方式,我们能够逼近任何类型的行为,而不必将自己局限于简单的线性关系。我们在第六章《确保用户身份验证》中提到过这一点,当时我们介绍了名为用户检测与多层感知器的章节,展示了如何通过多层感知器MLP)——由多个人工神经元组成的感知器层——克服单一感知器的局限性,通过在神经网络中引入足够数量的神经元来逼近任何连续的数学函数。这种逼近任何连续数学函数的能力正是神经网络的特征,这也决定了它们在学习方面的强大能力。

现在,让我们看看如何从单个人工神经元过渡到神经网络。

从人工神经元到神经网络

我们已经了解了人工神经元的特性以及激活函数执行的任务。接下来,让我们更深入地了解神经网络的特性。神经网络由多层神经元组成,这些神经元共同构成一个网络。神经网络还可以解释为人工神经元图,每个连接都有一个权重。

我们已经说过,通过向神经网络中添加足够数量的神经元,可以模拟任何连续数学函数的行为。实际上,神经网络不过是表示任意复杂度数学函数的一种替代方式。神经网络的强大之处在于它们能够通过创造新的特征来组装从数据集中提取的原始特征。

神经网络中会增加(隐藏层)层,以便执行这种特征组合。随着层数的增加,网络生成新特征的能力也得到了增强。必须特别关注神经网络的训练过程。

训练神经网络的最常见方法之一是前向传播。训练数据作为输入被送入网络的外层,外层将它们的部分处理输出传递给内层,依此类推。内层将对来自外层的输入数据进行处理,并将它们处理后的部分输出继续传递给后续层。

各层所执行的处理通常包括根据预期值评估与单个预测相关的权重的好坏。例如,在监督学习的情况下,我们已经提前知道标记样本的预期值,并根据所选的学习算法调整权重。这将导致一系列计算,通常通过表示与各层神经元相关的参数的偏导数来表示,这些计算需要在各个层中迭代执行,从而在计算上带来了相当大的负担。

随着神经网络中层数的增加,数据在网络中需要经历的步骤数呈指数级增长。为了理解这一点,可以想象一个神经元的输出经过 100 个神经元组成的内部层后,继续传递到另一个由 100 个神经元组成的层,依此类推,直到到达返回最终网络输出的外层神经元。

一种替代的训练策略,显著降低了计算负担,涉及反向传播。与其将从单层获得的部分输出传递到后续层,不如在各个层级上通过整合所获得的值来计算最终输出,通过记忆各个层级获得的输出值。这样,训练就受到将整个网络的输出反向传播的影响。不同于各个层返回的单一输出,权重会根据需要更新,以最小化错误率。

从数学角度看,反向传播是作为矩阵向量的乘积(在计算上要求较低),而不是像前向传播那样进行矩阵矩阵乘法(更多细节请参阅Python 机器学习——第二版,作者:Sebastian Raschka,Packt 出版)。

现在,让我们来看一下最常见的几种神经网络类型:

  • 前馈神经网络(FFNNs):FFNNs 代表了神经网络(NNs)的基本类型。每一层的神经元与下一层的一些(或所有)神经元相连接。FFNNs 的特点是各层之间的连接仅朝一个方向进行,并且没有循环或反向连接。

  • 循环神经网络(RNNs):这些网络的特点是神经元之间的连接形成有向循环,输入和输出都是时间序列。随着数据的积累和分析,RNNs 有助于识别数据中的模式,因此特别适用于执行动态任务,如语音识别和语言翻译。在网络流量分析和静态分析中,RNNs 在网络安全领域被广泛使用。一个 RNN 的例子是长短期记忆(LSTM)网络。

  • 卷积神经网络(CNNs):这些网络特别用于执行图像识别任务。CNNs 的特点是能够识别数据中特定特征的存在。构成 CNNs 的层与特定的过滤器相关联,这些过滤器表示感兴趣的特征(例如,表示图像中数字的一组像素)。这些过滤器具有相对于空间平移不变的特性,从而能够检测到搜索空间中不同区域中的感兴趣特征的存在(例如,在图像的不同区域中相同数字的存在)。CNN 的典型架构包括一系列卷积层激活层池化层全连接层。池化层的功能是减小感兴趣特征的大小,以便在搜索空间内查找这些特征。

图片来源:https://commons.wikimedia.org/wiki/File:3_filters_in_a_Convolutional_Neural_Network.gif

在这对神经网络的快速回顾之后,我们现在准备认识 GANs。

认识 GANs

我们已经提到,GANs 的基本直觉是让两个神经网络相互竞争,以改进总体结果。术语对抗特指这两个神经网络在完成各自任务时相互竞争。这种竞争的结果是一个无法进一步改进的整体结果,从而达到平衡条件。

使用 GANs 的典型例子是实现一个名为生成网络的特定神经网络,其任务是创建模拟真实图像特征的人工图像。第二个神经网络称为判别网络,与生成网络竞争,以区分人工模拟图像与真实图像。

一个有趣的方面是,这两个网络通过实现一种平衡状态(无差异状态)进行协作,它们各自的目标函数优化彼此竞争。生成网络将其优化过程建立在欺骗判别网络的能力上。

判别网络则基于在区分真实图像和由生成网络人工生成的图像时所达到的准确度,执行其优化过程。现在,让我们更详细地看一下这两个神经网络之间的差异。

生成网络与判别网络

理解生成对抗网络(GAN)中各个神经网络(NN)任务的一种直观方式是考虑这样一个场景:判别网络试图正确分类由生成网络人工生成的垃圾邮件。为了展示每个神经网络必须优化的不同目标函数,我们将借助条件概率(这是贝叶斯定理的基础),这一概念我们已经在第三章,垃圾邮件还是正常邮件?利用人工智能检测电子邮件网络安全威胁一节中的朴素贝叶斯垃圾邮件检测部分遇到过。

我们定义P(S|W)为给定电子邮件消息表示垃圾邮件(S)的概率,该概率基于文本中出现可疑词汇(W)。因此,判别网络的任务是正确估计与每封分析过的电子邮件相关的概率P(S|W).

对称地,生成网络的任务正好相反:即估计概率P(W|S)——也就是说,给定一条垃圾邮件,文本中包含可疑词汇(W)的可能性有多大。你会从条件概率理论中回忆到,P(S|W)的值与P(W|S)的值不同,因此这两个神经网络有不同的目标函数需要优化,即使它们是相关的。

因此,判别网络将寻求优化其目标函数,该函数涉及通过正确分类由生成网络人工生成的垃圾邮件,来适当地估计概率P(S|W),而生成网络通过根据每条邮件的概率P(W|S)生成垃圾邮件,从而优化其目标函数。生成网络接着将尝试模拟垃圾邮件,试图欺骗判别网络。同时,判别网络则试图正确识别真实的垃圾邮件,将其与生成网络人工创建的邮件区分开来,通过与先前已分类的真实垃圾邮件样本进行比较。

两个网络通过相互作用进行学习。生成网络生成的虚假垃圾邮件被作为输入传递给判别网络,判别网络将它们与真实垃圾邮件一起分析,逐步细化由P(S|W) 估计值组成的概率估计。这建立了两个神经网络之间的共生关系,在这种关系中,两个网络都试图优化各自的对立目标函数。

这种情况被博弈论定义为零和博弈,而逐步达成的动态平衡,结束了两个网络的优化过程,这就是纳什均衡

纳什均衡

在博弈论的数学理论中,纳什均衡被定义为两个竞争玩家将各自的游戏策略视为他们可以选择的最佳选项的条件。这种平衡状态是玩家通过反复进行博弈学习的结果。

在纳什均衡条件下,每个玩家将选择执行相同的动作,并且不会做出修改。

确定这种平衡的条件是特别严格的。事实上,它们意味着以下几点:

  • 所有玩家都是理性的(也就是说,他们必须最大化自己的目标函数)

  • 所有玩家都知道其他玩家也是理性的,并且知道各自需要最大化的目标函数

  • 所有玩家同时进行博弈,而不了解其他玩家做出的选择

现在,让我们来看一下如何用数学术语表示 GAN。

GAN 背后的数学

我们已经说过,GAN 的目的是实现两个神经网络之间的平衡状态。寻找这个平衡涉及到解决以下方程式,这是一个极小极大条件:

从前面的公式中,你可以看到定义两个神经网络的对抗目标。我们试图最大化D,同时最小化G。换句话说,代表判别器的神经网络D旨在最大化方程式,这转化为最大化与真实样本相关的输出,同时最小化与虚假样本相关的输出。另一方面,代表生成器的神经网络G有相反的目标,即最小化G的失败次数,从而最大化D在面对虚假样本时返回的输出。

GAN 的总体目标是在零和博弈(纳什均衡)中实现平衡,这种平衡特征是一个无差别的条件,其中D的输出将是每个分类样本分配的 50%的概率估计。换句话说,判别器不能可靠地区分真实样本和虚假样本。

如何训练 GAN

训练 GAN 可能需要较高的计算能力;否则,进行训练所需的时间可能从几小时到几天不等。由于两个神经网络之间的相互依赖,建议在训练判别器网络时保持生成器网络返回的值不变。与此同时,使用可用的训练数据对判别器网络进行预训练,再训练生成器网络也是一种有益的做法。

同时,合理设置两个神经网络的学习率也非常重要,以避免判别器网络的学习率超过生成器网络的学习率,反之亦然,从而防止各自的神经网络未能实现其优化目标。

一个 GAN 的示例–模拟 MNIST 手写数字

在下面的示例中,改编自原始代码,原始代码可在github.com/eriklindernoren/ML-From-Scratch/blob/master/mlfromscratch/unsupervised_learning/generative_adversarial_network.py找到(根据 MIT 许可证发布,详情见github.com/eriklindernoren/ML-From-Scratch/blob/master/LICENSE),我们看到一个 GAN 的示例,它能够通过一些输入噪声,生成与 MNIST 数据集中的手写数字图像相似的图像(MNIST 数据集可在yann.lecun.com/exdb/mnist/下载)。

GAN 的神经网络的激活函数,由build_generator()build_discriminator()函数实现,都基于 Leaky ReLU(为了提高 GAN 的稳定性,避免稀疏梯度的影响)。

我们将通过利用random库中的normal()函数来使用样本噪声作为生成器输入,如下所示:

noise = np.random.normal(0, 1, (half_batch, self.latent_dim))

两个神经网络的训练阶段通过train()方法实现:

train(self, n_epochs, batch_size=128, save_interval=50)

最后,在train()方法中,两个神经网络之间的联系非常明显:

# The generator wants the discriminator to label the generated samples as valid

valid = np.concatenate((np.ones((batch_size, 1)), np.zeros((batch_size, 1))), axis=1)

# Train the generator
g_loss, g_acc = self.combined.train_on_batch(noise, valid)

在下面的图像中,我们可以看到生成对抗网络(GAN)在不同训练周期中的逐步学习进程。GAN 在生成数字图像代表的过程中所取得的进展是显而易见的:

以下是代码示例,改编自原始代码,原始代码可在github.com/eriklindernoren/ML-From-Scratch/blob/master/mlfromscratch/unsupervised_learning/generative_adversarial_network.py找到(根据 MIT 许可证发布,详情见github.com/eriklindernoren/ML-From-Scratch/blob/master/LICENSE):

from __future__ import print_function, division
from sklearn import datasets
import math
import matplotlib.pyplot as plt
import numpy as np
import progressbar

from sklearn.datasets import fetch_openml
from mlxtend.data import loadlocal_mnist

from mlfromscratch.deep_learning.optimizers import Adam
from mlfromscratch.deep_learning.loss_functions import CrossEntropy
from mlfromscratch.deep_learning.layers import Dense, Dropout, Flatten, Activation, Reshape, BatchNormalization
from mlfromscratch.deep_learning import NeuralNetwork

在导入必要的库之后,我们现在可以着手定义GAN类,该类实现了我们的生成对抗网络(GAN),并以生成器和判别器组件的形式部署了深度全连接神经网络,这些组件在类构造函数(__init__()方法)中实例化:

class GAN():

    def __init__(self):
        self.img_rows = 28 
        self.img_cols = 28
        self.img_dim = self.img_rows * self.img_cols
        self.latent_dim = 100

        optimizer = Adam(learning_rate=0.0002, b1=0.5)
        loss_function = CrossEntropy

        # Build the discriminator
        self.discriminator = self.build_discriminator(optimizer, loss_function)

        # Build the generator
        self.generator = self.build_generator(optimizer, loss_function)

        # Build the combined model
        self.combined = NeuralNetwork(optimizer=optimizer, loss=loss_function)
        self.combined.layers.extend(self.generator.layers)
        self.combined.layers.extend(self.discriminator.layers)

        print ()
        self.generator.summary(name="Generator")
        self.discriminator.summary(name="Discriminator")

生成器和判别器组件分别在build_generator()build_discriminator()类方法中定义:

    def build_generator(self, optimizer, loss_function):

        model = NeuralNetwork(optimizer=optimizer, loss=loss_function)

        model.add(Dense(256, input_shape=(self.latent_dim,)))
        model.add(Activation('leaky_relu'))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Dense(512))
        model.add(Activation('leaky_relu'))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Dense(1024))
        model.add(Activation('leaky_relu'))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Dense(self.img_dim))
        model.add(Activation('tanh'))

        return model

    def build_discriminator(self, optimizer, loss_function):

        model = NeuralNetwork(optimizer=optimizer, loss=loss_function)

        model.add(Dense(512, input_shape=(self.img_dim,)))
        model.add(Activation('leaky_relu'))
        model.add(Dropout(0.5))
        model.add(Dense(256))
        model.add(Activation('leaky_relu'))
        model.add(Dropout(0.5))
        model.add(Dense(2))
        model.add(Activation('softmax'))

        return model

为了训练 GAN,我们定义了train()类方法,负责训练生成器和判别器组件:

    def train(self, n_epochs, batch_size=128, save_interval=50):  

        X, y = loadlocal_mnist(images_path='./MNIST/train-images.idx3-ubyte', labels_path='./MNIST/train-labels.idx1-ubyte')  

        # Rescale [-1, 1]
        X = (X.astype(np.float32) - 127.5) / 127.5

        half_batch = int(batch_size / 2)

        for epoch in range(n_epochs):

            # ---------------------
            #  Train Discriminator
            # ---------------------

            self.discriminator.set_trainable(True)

            # Select a random half batch of images
            idx = np.random.randint(0, X.shape[0], half_batch)
            imgs = X[idx]

            # Sample noise to use as generator input
            noise = np.random.normal(0, 1, (half_batch, self.latent_dim))

            # Generate a half batch of images
            gen_imgs = self.generator.predict(noise)

            # Valid = [1, 0], Fake = [0, 1]
            valid = np.concatenate((np.ones((half_batch, 1)), np.zeros((half_batch, 1))), axis=1)
            fake = np.concatenate((np.zeros((half_batch, 1)), np.ones((half_batch, 1))), axis=1)

            # Train the discriminator
            d_loss_real, d_acc_real = self.discriminator.train_on_batch(imgs, valid)
            d_loss_fake, d_acc_fake = self.discriminator.train_on_batch(gen_imgs, fake)
            d_loss = 0.5 * (d_loss_real + d_loss_fake)
            d_acc = 0.5 * (d_acc_real + d_acc_fake)

            # ---------------------
            #  Train Generator
            # ---------------------

            # We only want to train the generator for the combined model
            self.discriminator.set_trainable(False)

            # Sample noise and use as generator input
            noise = np.random.normal(0, 1, (batch_size, self.latent_dim))

            # The generator wants the discriminator to label the generated samples as valid
            valid = np.concatenate((np.ones((batch_size, 1)), np.zeros((batch_size, 1))), axis=1)

            # Train the generator
            g_loss, g_acc = self.combined.train_on_batch(noise, valid)

            # Display the progress
            print ("%d [D loss: %f, acc: %.2f%%] [G loss: %f, acc: %.2f%%]" % (epoch, d_loss, 100*d_acc, g_loss, 100*g_acc))

            # If at save interval => save generated image samples
            if epoch % save_interval == 0:
                self.save_imgs(epoch)

在训练完 GAN 后,我们可以通过save_imgs()类方法保存新创建的对抗样本图像,该方法定义如下:

    def save_imgs(self, epoch):
        r, c = 5, 5 # Grid size
        noise = np.random.normal(0, 1, (r * c, self.latent_dim))
        # Generate images and reshape to image shape
        gen_imgs = self.generator.predict(noise).reshape((-1, self.img_rows, self.img_cols))

        # Rescale images 0 - 1
        gen_imgs = 0.5 * gen_imgs + 0.5

        fig, axs = plt.subplots(r, c)
        plt.suptitle("Generative Adversarial Network")
        cnt = 0
        for i in range(r):
            for j in range(c):
                axs[i,j].imshow(gen_imgs[cnt,:,:], cmap='gray')
                axs[i,j].axis('off')
                cnt += 1
        fig.savefig("mnist_%d.png" % epoch)
        plt.close()

要启动脚本,我们只需如下定义__main__入口点:

if __name__ == '__main__':

    gan = GAN()
    gan.train(n_epochs=200000, batch_size=64, save_interval=400)

现在,让我们继续看看用 Python 开发的 GAN 工具和库。

GAN Python 工具和库

用于开发对抗样本的工具和库(无论是进行攻击还是防御)不断增多。我们将查看其中一些最常见的示例。在本节中,我们将整合常用的库和工具,接下来的章节将分别讨论基于不同攻击和防御策略与场景的特定库和工具。

为了充分理解这些工具和库的实用性,我们需要分析基于神经网络的网络安全解决方案的脆弱性、实施攻击的可能性,以及准备适当防御措施的难度。

神经网络的脆弱性

尽管正如我们之前所看到的,神经网络(NN)近年来因其在解决通常是人类认知能力专属的复杂问题(如面部识别和语音识别)时展现出的巨大潜力,已获得了特别关注,神经网络,尤其是深度神经网络(DNN),仍然存在一些相当重要的脆弱性,这些脆弱性可以通过生成对抗网络(GAN)加以利用。这意味着,举例来说,可能会通过人工创建对抗样本来欺骗基于面部识别或其他生物特征的生物认证程序。

显然无害的设备,如 3D 医学影像扫描仪,已被用作攻击载体,正如最近一篇论文所示,CT-GAN: 使用深度学习恶意篡改 3D 医学影像,作者为以色列本·古里安大学信息系统工程系、Soroka 大学医学中心的 Yisroel Mirsky、Tom Mahler、Ilan Shelef 和 Yuval Elovici(arXiv: 1901.03597v2)。

在这项研究中,作者集中研究了如何将癌症图像注入和移除 CT 扫描,展示了深度神经网络(DNN)对攻击的高度脆弱性。

通过添加虚假证据或删除一些真实的医学图像证据,攻击者如果能访问到医学影像,可以改变患者的诊断结果。

例如,攻击者可以添加或删除脑动脉瘤、脑肿瘤以及其他病理证据,如心脏病的证据。这种类型的威胁展示了 DNN 在处理敏感信息(如健康状况信息)时如何通过几个数量级的扩展攻击面,甚至可能涉及犯罪,如谋杀,且不需要攻击者亲自参与,只需利用数字设备和程序的漏洞作为无菌攻击向量。

从我们所说的内容中,我们可以轻松理解深度神经网络(DNN)在面对对抗性攻击时缺乏鲁棒性所带来的严重性,这可能决定了程序及其依赖应用的妥协性。

然而,在利用 DNN 的应用中,也有一些关键任务应用(例如那些管理自动驾驶汽车功能的应用)。

深度神经网络攻击

对 DNN 进行攻击的基本方法有两种:

  • 白盒攻击:这种攻击假设 DNN 目标模型具有透明性,允许直接验证对抗性示例对响应的敏感性。

  • 黑盒攻击:与前一种情况不同,对抗性示例的敏感性检查是间接实现的,因为无法获得目标神经网络的配置细节;唯一可用的信息是神经网络对发送给它们的相应输入返回的输出值。

无论攻击类型如何,攻击者总是能够利用一些关于神经网络的一般特性。如我们所见,最广泛的对抗性攻击之一是那些旨在欺骗图像分类算法的攻击,利用人工创建的图像样本。因此,考虑到图像分类应用更倾向于使用卷积神经网络CNN),攻击者将更加专注于这种神经网络的漏洞进行攻击。

即使是 DNN 使用的学习策略也可以间接构成攻击向量。我们之前已经看到,反向传播技术因其在计算上的更高效率而被优先用于训练算法。了解这一优先学习选择后,攻击者可以反过来利用梯度下降等算法攻击 DNN,假设反向传播策略允许计算整个 DNN 返回输出的梯度。

对抗性攻击方法

以下是一些最常用的对抗攻击开发方法:

  • 快速梯度符号方法FGSM):为了生成对抗样本,这种方法利用与 DNN 受害者反向传播方法相关的梯度符号。

  • 基于雅可比矩阵的显著性图攻击JSMA):这种攻击方法通过迭代修改信息(例如图像中最显著的像素)来创建对抗样本,基于一个 JSMA 来描述输入和目标神经网络返回输出之间的现有关系。

  • Carlini 和 WagnerC 和 W):这种对抗攻击方法可能是最可靠且最难检测的。对抗攻击被视为一个优化问题,使用预定义的度量(例如欧几里得距离)来确定原始样本与对抗样本之间的差距。

然而,对抗样本也表现出一个有趣的特点:攻击可转移性

对抗攻击的可转移性

对抗攻击的一个典型特点与其可转移性有关。

这一特点指的是,针对某个特定 DNN 生成的对抗样本,可能也能转移到另一个 DNN,因为神经网络具有很高的泛化能力,这正是它们的优势(但同时也是它们的脆弱性)。

利用对抗攻击的可转移性,攻击者能够创建可重用的对抗样本,而无需知道神经网络的个别配置的确切参数。

因此,很可能一组成功欺骗特定 DNN 进行图像分类的对抗样本,能够被用来欺骗其他具有相似分类任务的神经网络。

防御对抗攻击

随着对抗攻击的传播,已经有许多尝试提供适当的防御措施,主要基于以下几种方法:

  • 基于统计的检测防御:这种方法通过利用统计测试和异常值检测来尝试检测对抗样本的存在。它假设描述真实样本和对抗样本的统计分布在根本上是不同的。然而,C 和 W 攻击方法的有效性表明,这一假设并不是显而易见或可靠的。

  • 梯度遮蔽防御:我们已经看到对抗攻击如何利用大多数深度神经网络(DNN)采用的反向传播优化策略,并依赖于目标神经网络执行的梯度计算信息。梯度遮蔽防御因此涉及在神经网络训练过程中隐藏与梯度相关的信息。

  • 对抗训练防御:这种防御方法旨在通过将对抗样本和真实样本插入训练数据集中,使学习算法对可能出现在训练数据中的扰动更加鲁棒。这种防御方法似乎对 C 和 W 对抗攻击最为有效。然而,它也有一定的代价,涉及网络复杂性的增加和模型参数的增加。

现在,DNN 的脆弱性以及对抗攻击和防御方法已被介绍,我们可以分析开发对抗样本时使用的主要库。

CleverHans 对抗样本库

当前备受关注的 Python 库之一无疑是 CleverHans 库,它通常是开发对抗样本的其他库和工具的基础。

CleverHans 库可以在github.com/tensorflow/cleverhans找到,并根据 MIT 许可证发布(github.com/tensorflow/cleverhans/blob/master/LICENSE)。

该库特别适合构建攻击、搭建防御措施,以及基准测试机器学习系统对对抗攻击的脆弱性。

要安装 CleverHans 库,我们必须首先安装 TensorFlow 库(www.tensorflow.org/install/),该库用于执行学习模型实现所需的图计算。

安装 TensorFlow 后,我们可以使用常规命令安装 CleverHans:

pip install cleverhans

CleverHans 库的众多优点之一是它提供了多个示例和教程,展示了使用不同方法通过模型开发对抗样本的过程。

特别是,CleverHans 库为我们提供了以下教程(基于 MNIST 手写数字训练数据集,数据集可以在yann.lecun.com/exdb/mnist/下载):

  • 使用 FGSM 的 MNIST:本教程介绍了如何训练 MNIST 模型,利用 FGSM 制作对抗样本,并通过对抗训练使模型对对抗样本更加鲁棒。

  • 使用 JSMA 的 MNIST:本教程介绍了如何定义一个 MNIST 模型,通过 JSMA 方法制作对抗样本。

  • 使用黑盒攻击的 MNIST:本教程实现了一种基于对抗训练的替代模型的黑盒攻击(即通过观察黑盒模型为精心挑选的输入分配的标签,来模仿黑盒模型的副本)。对抗者然后使用替代模型的梯度来查找那些被黑盒模型错误分类的对抗样本。

在本章中,我们将遇到一些使用 CleverHans 库开发对抗性攻击和防御场景的示例。

EvadeML-Zoo 对抗性示例库

另一个特别值得关注的库是 EvadeML-Zoo。EvadeML-Zoo 是一个用于对抗性机器学习的基准测试和可视化工具,由弗吉尼亚大学的机器学习小组和安全研究小组开发。

EvadeML-Zoo 采用 MIT 许可证发布(github.com/mzweilin/EvadeML-Zoo/blob/master/LICENSE),并可在 github.com/mzweilin/EvadeML-Zoo 免费下载。

EvadeML-Zoo 库提供了一系列工具和模型,包括以下内容:

  • 攻击方法如 FGSM、BIM、JSMA、Deepfool、Universal Perturbations 和 Carlini/Wagner-L2/Li/L0

  • 用于攻击的最先进的预训练模型

  • 对抗性示例的可视化

  • 防御方法

  • 几个现成的可用数据集,如 MNIST、CIFAR-10 和 ImageNet-ILSVRC

下载完包后,你可以使用以下命令在只使用 CPU 的机器上安装 EvadeML-Zoo 库:

pip install -r requirements_cpu.txt

此外,如果你有兼容的 GPU 可用,可以执行以下命令:

pip install -r requirements_gpu.txt

我们已经看到,EvadeML-Zoo 库提供的功能还包括预训练模型,这对于加速对抗性示例的开发过程尤其有用,而这些示例通常在计算上非常消耗资源。

要下载预训练模型,请运行以下命令:

mkdir downloads; curl -sL https://github.com/mzweilin/EvadeML-Zoo/releases/download/v0.1/downloads.tar.gz | tar xzv -C downloads

EvadeML-Zoo 库的另一个有趣特点是,它可以通过运行 main.py 工具来执行。

在以下代码块中,你可以看到 main.py 的使用菜单,并附带了工具执行的示例:

usage: python main.py [-h] [--dataset_name DATASET_NAME] [--model_name MODEL_NAME]
 [--select [SELECT]] [--noselect] [--nb_examples NB_EXAMPLES]
 [--balance_sampling [BALANCE_SAMPLING]] [--nobalance_sampling]
 [--test_mode [TEST_MODE]] [--notest_mode] [--attacks ATTACKS]
 [--clip CLIP] [--visualize [VISUALIZE]] [--novisualize]
 [--robustness ROBUSTNESS] [--detection DETECTION]
 [--detection_train_test_mode [DETECTION_TRAIN_TEST_MODE]]
 [--nodetection_train_test_mode] [--result_folder RESULT_FOLDER]
 [--verbose [VERBOSE]] [--noverbose]

 optional arguments:
 -h, --help            show this help message and exit
 --dataset_name DATASET_NAME
 Supported: MNIST, CIFAR-10, ImageNet, SVHN.
 --model_name MODEL_NAME
 Supported: cleverhans, cleverhans_adv_trained and
 carlini for MNIST; carlini and DenseNet for CIFAR-10;
 ResNet50, VGG19, Inceptionv3 and MobileNet for
 ImageNet; tohinz for SVHN.
 --select [SELECT]     Select correctly classified examples for the
 experiment.
 --noselect
 --nb_examples NB_EXAMPLES
 The number of examples selected for attacks.
 --balance_sampling [BALANCE_SAMPLING]
 Select the same number of examples for each class.
 --nobalance_sampling
 --test_mode [TEST_MODE]
 Only select one sample for each class.
 --notest_mode
 --attacks ATTACKS     Attack name and parameters in URL style, separated by
 semicolon.
 --clip CLIP           L-infinity clip on the adversarial perturbations.
 --visualize [VISUALIZE]
 Output the image examples for each attack, enabled by
 default.
 --novisualize
 --robustness ROBUSTNESS
 Supported: FeatureSqueezing.
 --detection DETECTION
 Supported: feature_squeezing.
 --detection_train_test_mode [DETECTION_TRAIN_TEST_MODE]
 Split into train/test datasets.
 --nodetection_train_test_mode
 --result_folder RESULT_FOLDER
 The output folder for results.
 --verbose [VERBOSE]   Stdout level. The hidden content will be saved to log
 files anyway.
 --noverbose

EvadeML-Zoo 库使用 Carlini 模型和对 MNIST 数据集的 FGSM 对抗性攻击进行执行,具体如下:

python main.py --dataset_name MNIST --model_name carlini \
 --nb_examples 2000 --balance_sampling \
 --attacks "FGSM?eps=0.1;" \
 --robustness "none;FeatureSqueezing?squeezer=bit_depth_1;" \
 --detection "FeatureSqueezing?squeezers=bit_depth_1,median_filter_2_2&distance_measure=l1&fpr=0.05;"

Defense-GAN library

最后,我们将学习如何使用 Defense-GAN 库开发对抗性攻击的防御模型。

在分析 Defense-GAN 库的详细信息之前,让我们先尝试理解它所基于的假设,以及它提供的功能,以实现有效的对抗性攻击防御。

正如我们所见,对抗性攻击可以分为白盒攻击和黑盒攻击;在白盒攻击的情况下,攻击者可以完全访问模型的架构和参数,而在黑盒攻击的情况下,攻击者无法访问模型参数。

我们还知道,许多防御对抗攻击的方法已经被提出,这些方法本质上基于区分对抗样本与真实样本的统计分布(统计检测),基于隐藏与神经网络学习阶段相关的敏感信息(梯度掩蔽),或者基于使用对抗样本与其他训练样本一起训练学习算法(对抗训练)。

所有这些防御方法都有一定的局限性,因为它们只能有效防御白盒攻击或黑盒攻击,但不能同时防御两者。

Defense-GAN 可以作为对抗任何攻击的防御工具,因为它不假设攻击模型,而是简单地利用 GAN 的生成能力来重构对抗样本。

Defense-GAN 提出了一种新的防御策略,该策略基于一个无监督训练的 GAN,在合法(未扰动)训练样本上进行训练,以去噪对抗样本。

Defense-GAN 库是根据 Apache 2.0 许可证发布的 (github.com/kabkabm/defensegan/blob/master/LICENSE),并可以在 github.com/kabkabm/defensegan 上免费下载。

下载库后,您可以通过启动以下命令来安装它:

pip install -r requirements.txt

要下载数据集并准备数据目录,请使用以下命令启动 download_dataset.py Python 脚本:

python download_dataset.py [mnist|f-mnist|celeba]

通过启动 train.py 脚本来训练 GAN 模型:

python train.py --cfg  --is_train

 --cfg This can be set to either a .yml configuration file like the ones in experiments/cfgs, or an output directory path.
 can be any parameter that is defined in the config file.

脚本执行将创建:

  • 每个实验的目录都位于输出目录中,并与保存模型检查点的目录同名。

  • 每个实验目录中都会保存一个配置文件,以便可以加载该目录的地址。

  • 每个实验的训练目录都位于输出目录中,并与保存模型检查点的目录同名。

  • 每个实验目录中都会保存一个训练配置文件,以便可以加载该目录的地址。

Defense-GAN 库还提供了一些工具,您可以用来实验不同的攻击模式,从而验证防御模型的有效性。

要执行黑盒攻击,我们可以启动 blackbox.py 工具:

python blackbox.py --cfg  \
     --results_dir  \
     --bb_model {A, B, C, D, E} \
     --sub_model {A, B, C, D, E} \
     --fgsm_eps  \
     --defense_type {none|defense_gan|adv_tr}
     [--train_on_recs or --online_training]  

让我们来看一下这里的每个参数:

  • --cfg 参数是用于训练 iWGAN 的配置文件路径。这也可以是模型输出目录的路径。

  • --results_dir 参数是保存最终结果文本文件的路径。

  • --bb_model 参数表示在表格 1 和 2 中使用的黑盒模型架构。

  • --sub_model 参数表示在表格 1 和 2 中使用的替代模型架构。

  • --defense_type 参数指定保护分类器的防御类型。

  • --train_on_recs--online_training参数是可选的。如果设置了这些参数,分类器将基于 Defense-GAN 的重构结果进行训练(例如,表 1 和表 2 中的Defense-GAN-Rec列);否则,结果将是Defense-GAN-Orig的结果。请注意,如果设置了--rec_iters或论文中的L为较大的值,--online_training将需要一些时间。

还有一份--参数列表,与配置文件中定义的超参数(均为小写)相同,并列出了blackbox.py中的标志。最重要的几个如下:

  • --rec_iters: Defense-GAN 的梯度下降GD)重构迭代次数,或论文中的L

  • --rec_lr: 重构步骤的学习率。

  • --rec_rr: 重构步骤的随机重启次数,或论文中的R

  • --num_train: 用于训练黑盒模型的图像数量。为了调试,设置一个较小的值。

  • --num_test: 测试图像的数量。为了调试,设置一个较小的值。

  • --debug: 该选项将把定性攻击和重构结果保存到调试目录,并且不会运行代码中的对抗攻击部分。

带参数的blackbox.py执行示例如下:

python blackbox.py --cfg output/gans/mnist \
 --results_dir defensegan \
 --bb_model A \
 --sub_model B \
 --fgsm_eps 0.3 \
 --defense_type defense_gan

我们当然可以通过启动whitebox.py工具,测试 Defense-GAN 对抗白盒攻击:

python whitebox.py --cfg  \
        --results_dir  \
        --attack_type {fgsm, rand_fgsm, cw} \
        --defense_type {none|defense_gan|adv_tr} \
        --model {A, B, C, D} \
        [--train_on_recs or --online_training]

带参数的whitebox.py执行示例如下:

python whitebox.py --cfg  \
        --results_dir whitebox \
        --attack_type fgsm \
        --defense_type defense_gan \
        --model A

至于blackbox.py,还有一份--参数列表,与配置文件中定义的超参数(均为小写)相同,并列出了whitebox.py中的标志。最重要的几个如下:

  • --rec_iters: Defense-GAN 的 GD 重构迭代次数,或论文中的L

  • --rec_lr: 重构步骤的学习率。

  • --rec_rr: 重构步骤的随机重启次数,或论文中的R

  • --num_test: 测试图像的数量。为了调试,设置一个较小的值。

现在让我们继续看看如何通过模型替代来对神经网络进行攻击。

通过模型替代进行网络攻击

在黑盒模式下进行对抗攻击所展现的潜力的一个有趣示例是论文Practical Black-Box Attacks against Machine Learning(arXiv: 1602.02697v4)中描述的内容,其中展示了对远程托管的深度神经网络进行攻击的可能性,而攻击者并不知道目标神经网络的配置特征。

在这些情况下,攻击者唯一可用的信息是神经网络根据攻击者提供的输入类型返回的输出。实际上,攻击者观察到 DNN 在针对攻击输入时返回的分类标签。正是在这里,攻击策略变得有趣。实际上,本地替代模型是用一个对抗模型合成生成的输入,并由目标神经网络标记训练的,以代替远程托管的神经网络。

由 MetaMind 托管的神经网络作为远程托管网络目标,公开了一个互联网 DL API。通过将攻击示例提交到托管网络,作者验证了在本地替代模型上训练的对抗示例,RNN 错误地分类了超过 80%的对抗示例。此外,这一攻击策略也在亚马逊和谷歌提供的类似在线服务上进行验证,结果更为糟糕,误分类率高达 96%。

通过这种方式,作者证明了他们的黑盒对抗攻击策略具有普遍有效性,而不仅仅局限于特定选择的目标神经网络。获得的结果还证明了对抗攻击可转移性原理的有效性,使用在本地模型上测试的合成数据集。攻击者实际上是通过充分逼近特征来替代本地模型,用目标模型来替代本地模型,从而能够利用本地模型识别到的漏洞攻击目标模型。

因此,基于模型替代的对抗攻击方法的关键要素是替代模型训练和合成数据集生成。

让我们更仔细地看一下这两个特性。

替代模型训练

如我们之前所说,基于模型替代的对抗攻击方法旨在训练一个替代模型,该模型与原始目标神经网络相似,以便找到目标神经网络上的可行漏洞。

因此,替代模型的训练阶段具有一些重要的特殊性,涉及以下内容:

  • 在没有目标模型知识的情况下选择替代模型的架构

  • 限制对目标模型进行查询的次数,以确保该方法可操作

为了应对这些困难的任务,提出的攻击策略基于合成数据的生成(使用一种称为雅可比矩阵数据增强的技术)。

生成合成数据集

合成数据集生成中的方法在基于模型替代的攻击策略中至关重要。

要理解这一点,你只需要考虑这样一个事实:尽管原则上可以对目标模型进行无限次(甚至是无限数量的)不同查询(以验证目标模型针对每个查询输入生成的输出),但从实际角度来看,这种方法是不可行的。

首先,它是不可持续的,因为大量的查询会使对抗性攻击容易被检测到,而且它也不可持续,因为我们需要根据目标神经网络的潜在输入组件数量,增加发送到目标模型的请求数量。

替代方案涉及使用合适的启发式方法生成合成数据集,基于识别目标模型输出中方向在初始训练点集周围的变化。这些方向通过替代模型的雅可比矩阵来识别,以便通过优先选择样本并在查询目标模型标签时准确地逼近目标模型的决策边界。

用 MalGAN 欺骗恶意软件检测器

黑盒对抗性攻击策略同样可以有效地用来欺骗基于神经网络的下一代反恶意软件系统。

一个有用的库,用于开发带有恶意软件示例的黑盒对抗性攻击是 MalGAN,可以从github.com/yanminglai/Malware-GAN/下载,并且遵循 GPL 3.0 许可证(github.com/yanminglai/Malware-GAN/blob/master/LICENSE)。MalGAN 背后的基本思想是使用 GAN 生成对抗性的恶意软件示例,这些示例能够绕过基于机器学习的黑盒检测模型。要安装 MalGAN 库,你需要安装 TensorFlow 1.80、Keras 2.0 和 Cuckoo Sandbox 2.03(cuckoo.readthedocs.io/en/2.0.3/)库。Cuckoo Sandbox 用于从virusshare.com/获取的恶意软件样本中提取 API 特征(选择 128 个 API 特征作为维度向量输入神经网络)。

以下是主 MalGAN 类(版本 2)的代码:

"""
 MalGAN v2 Class definition
 https://github.com/yanminglai/Malware-GAN/blob/master/MalGAN_v2.py
 Released under GPL 3.0 LICENSE: https://github.com/yanminglai/Malware-GAN/blob/master/LICENSE  

 """

 from keras.layers import Input, Dense, Activation
 from keras.layers.merge import Maximum, Concatenate
 from keras.models import Model
 from keras.optimizers import Adam
 from numpy.lib import format
 from sklearn.ensemble import RandomForestClassifier
 from sklearn import linear_model, svm
 from sklearn.model_selection import train_test_split
 import matplotlib.pyplot as plt
 from load_data import *
 import numpy as np

在导入必要的库之后,让我们先看看MalGAN()类的定义,从其构造函数(__init__()方法)开始:

 class MalGAN():
     def __init__(self):
         self.apifeature_dims = 74
         self.z_dims = 10
         self.hide_layers = 256
         self.generator_layers = [self.apifeature_dims+self.z_dims, self.hide_layers, self.apifeature_dims]
         self.substitute_detector_layers = [self.apifeature_dims, self.hide_layers, 1]
         self.blackbox = 'RF'
         optimizer = Adam(lr=0.001)

         # Build and Train blackbox_detector
         self.blackbox_detector = self.build_blackbox_detector()

         # Build and compile the substitute_detector
         self.substitute_detector = self.build_substitute_detector()
         self.substitute_detector.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])

         # Build the generator
         self.generator = self.build_generator()

         # The generator takes malware and noise as input and generates adversarial malware examples
         example = Input(shape=(self.apifeature_dims,))
         noise = Input(shape=(self.z_dims,))
         input = [example, noise]
         malware_examples = self.generator(input)

         # For the combined model we will only train the generator
         self.substitute_detector.trainable = False

         # The discriminator takes generated images as input and determines validity
         validity = self.substitute_detector(malware_examples)

         # The combined model  (stacked generator and substitute_detector)
         # Trains the generator to fool the discriminator
         self.combined = Model(input, validity)
         self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)

MalGAN类提供了构建生成器组件和替代检测器的方法,以及blackbox_detector

     def build_blackbox_detector(self):

         if self.blackbox is 'RF':
             blackbox_detector = RandomForestClassifier(n_estimators=50, max_depth=5, random_state=1)
         return blackbox_detector

     def build_generator(self):

         example = Input(shape=(self.apifeature_dims,))
         noise = Input(shape=(self.z_dims,))
         x = Concatenate(axis=1)([example, noise])
         for dim in self.generator_layers[1:]:
             x = Dense(dim)(x)
         x = Activation(activation='sigmoid')(x)
         x = Maximum()([example, x])
         generator = Model([example, noise], x, name='generator')
         generator.summary()
         return generator

     def build_substitute_detector(self):

         input = Input(shape=(self.substitute_detector_layers[0],))
         x = input
         for dim in self.substitute_detector_layers[1:]:
             x = Dense(dim)(x)
         x = Activation(activation='sigmoid')(x)
         substitute_detector = Model(input, x, name='substitute_detector')
         substitute_detector.summary()
         return substitute_detector

生成器组件的训练以及blackbox和替代检测器的训练在train()方法中实现:

     def train(self, epochs, batch_size=32):

         # Load the dataset
         (xmal, ymal), (xben, yben) = self.load_data('mydata.npz')
         xtrain_mal, xtest_mal, ytrain_mal, ytest_mal = train_test_split(xmal, ymal, test_size=0.20)
         xtrain_ben, xtest_ben, ytrain_ben, ytest_ben = train_test_split(xben, yben, test_size=0.20)

         # Train blackbox_detector
         self.blackbox_detector.fit(np.concatenate([xmal, xben]),
                                    np.concatenate([ymal, yben]))

         ytrain_ben_blackbox = self.blackbox_detector.predict(xtrain_ben)
         Original_Train_TPR = self.blackbox_detector.score(xtrain_mal, ytrain_mal)
         Original_Test_TPR = self.blackbox_detector.score(xtest_mal, ytest_mal)
         Train_TPR, Test_TPR = [Original_Train_TPR], [Original_Test_TPR]
         best_TPR = 1.0
         for epoch in range(epochs):

             for step in range(xtrain_mal.shape[0] // batch_size):
                 # ---------------------
                 #  Train substitute_detector
                 # ---------------------

                 # Select a random batch of malware examples
                 idx = np.random.randint(0, xtrain_mal.shape[0], batch_size)
                 xmal_batch = xtrain_mal[idx]
                 noise = np.random.uniform(0, 1, (batch_size, self.z_dims))   #noise as random uniform
                 idx = np.random.randint(0, xmal_batch.shape[0], batch_size)
                 xben_batch = xtrain_ben[idx]
                 yben_batch = ytrain_ben_blackbox[idx]

                 # Generate a batch of new malware examples
                 gen_examples = self.generator.predict([xmal_batch, noise])
                 ymal_batch = self.blackbox_detector.predict(np.ones(gen_examples.shape)*(gen_examples > 0.5))

                 # Train the substitute_detector
                 d_loss_real = self.substitute_detector.train_on_batch(gen_examples, ymal_batch)
                 d_loss_fake = self.substitute_detector.train_on_batch(xben_batch, yben_batch)
                 d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)               

我们将按如下方式训练生成器:


                 idx = np.random.randint(0, xtrain_mal.shape[0], batch_size)
                 xmal_batch = xtrain_mal[idx]
                 noise = np.random.uniform(0, 1, (batch_size, self.z_dims))

                 # Train the generator
                 g_loss = self.combined.train_on_batch([xmal_batch, noise], np.zeros((batch_size, 1)))

             # Compute Train TPR
             noise = np.random.uniform(0, 1, (xtrain_mal.shape[0], self.z_dims))
             gen_examples = self.generator.predict([xtrain_mal, noise])
             TPR = self.blackbox_detector.score(np.ones(gen_examples.shape) * (gen_examples > 0.5), ytrain_mal)
             Train_TPR.append(TPR)

             # Compute Test TPR
             noise = np.random.uniform(0, 1, (xtest_mal.shape[0], self.z_dims))
             gen_examples = self.generator.predict([xtest_mal, noise])
             TPR = self.blackbox_detector.score(np.ones(gen_examples.shape) * (gen_examples > 0.5), ytest_mal)
             Test_TPR.append(TPR)

             # Save best model
             if TPR < best_TPR:
                 self.combined.save_weights('saves/malgan.h5')
                 best_TPR = TPR

要启动脚本,我们只需定义__main__入口点:

 if __name__ == '__main__':
     malgan = MalGAN()
     malgan.train(epochs=50, batch_size=64)

现在,让我们继续说明利用 GAN 进行 IDS 规避的技术。

通过 GAN 进行 IDS 规避

我们在第五章《基于 AI 的网络异常检测》中详细讨论了 IDS,了解了这些设备在当前这种网络攻击不断增加、恶意软件威胁日益扩散的背景下所发挥的微妙作用。

因此,有必要引入能够及时检测可能的恶意软件威胁的工具,防止它们在整个企业网络中扩散,从而危及软件和数据的完整性(例如,想想日益蔓延的勒索软件攻击)。

为了能够及时有效地执行——即减少——误报的数量,因此有必要为 IDS 系统配备能够充分分类分析流量的自动化程序。因此,现代 IDS 系统采用机器学习算法,也越来越多地借助深度神经网络(如 CNN 和 RNN)来提高入侵检测的准确性。

因此,连入侵检测系统IDS)也不能视为对对抗性攻击免疫,这些攻击是专门为欺骗 IDS 的基础模型而生成的,从而降低(甚至消除)正确分类异常流量的能力。

尽管如此,迄今为止,仍然有少数理论研究和软件实现利用对抗性样本对 IDS 进行攻击。

使用 GAN 绕过 IDS 检测的可能性的一个示范描述在论文《IDSGAN: 生成对抗网络用于入侵检测攻击生成》中(arxiv.org/pdf/1809.02077)。

介绍 IDSGAN

同时,在 IDSGAN 的情况下,攻击类型基于黑盒策略,其中目标 IDS 的实现细节和配置是未知的。

IDSGAN 的基础 GAN 通常包括两个对抗性的神经网络,其中生成器组件负责通过构造对抗性样本将原始网络流量转换为恶意流量。

IDSGAN 的判别器组件则负责正确分类流量,模拟黑盒检测系统,从而为生成对抗样本的生成器组件提供必要的反馈。

即使在 IDSGAN 的情况下,使用 NSL-KDD 数据集生成的对抗性样本(www.unb.ca/cic/datasets/nsl.html)也显示了攻击可转移性的特征;也就是说,它们可以被重新利用来攻击许多检测系统,从而展示了基础模型的鲁棒性。

IDSGAN 的特点

IDSGAN 提供的主要功能如下:

  • 通过模拟 IDS 的行为开发攻击的能力

  • 利用对抗样本在黑盒模式下对 IDS 进行攻击的能力

  • 将人工生成流量的检测率降到零的能力

  • 重用生成的对抗样本以攻击不同类型的 IDS 的能力

现在,让我们来看一下 IDSGAN 的结构。

IDSGAN 训练数据集

首先,IDSGAN 使用 NSL-KDD 数据集(www.unb.ca/cic/datasets/nsl.html),该数据集包含恶意和真实的流量样本。这些样本对于检查 IDSGAN 的性能特别有用,因为它们也被常见的 IDS 所使用。

然后,NSL-KDD 数据集作为基准,既验证生成器组件的有效性,又允许判别器组件返回创建对抗样本所需的反馈。因此,选择 NSL-KDD 数据集并非偶然,因为流量数据样本包含正常和恶意流量,细分为四个主要类别,如探测(probe)、拒绝服务DoS)、用户到根U2R)和根到本地R2L)。

此外,数据集根据 41 个复杂特征展示流量,其中 9 个特征为离散值,剩余的 32 个特征为连续值。

这些特性可以进一步分为以下四种类型:

  • 内在:这些特性反映了单个连接的固有特征

  • 内容:这些特性标记与可能的攻击相关的连接内容

  • 基于时间:这些特性检查过去 2 秒内已建立的连接,这些连接与当前连接具有相同的目标主机或相同的服务

  • 基于主机:这些特性监视过去 100 个连接中与当前连接具有相同目标主机或相同服务的连接

在数据预处理阶段,特别关注特征值之间的维度影响减少。采用基于最小-最大标准的方法进行归一化,将输入数据转换并使其落入区间[0, 1],从而能够管理离散特征和连续特征。

用于执行此归一化的数学公式如下:

这里,x表示归一化前的特征值,x′是归一化后的特征值。

一旦我们分析了训练数据集和数据归一化,我们就可以继续研究 IDSGAN 组件的特性。

生成器网络

与所有 GAN 一样,生成器网络是负责生成对抗样本的组件。

在 IDSGAN 中,生成器将原始输入流量样本转换为与大小为m的向量相关联的样本,该向量表示原始样本的特征,另一个维度为n的向量,其中包含噪声——即从均匀分布中提取的随机数,其值落在[0, 1]范围内。

生成器网络由五个层组成(其中与 ReLU 激活函数相关联),用于管理内部层的输出,而输出层具有足够的单元,以满足原始的m维样本向量。

正如我们所预期的那样,生成器网络根据从判别器网络(模拟 IDS 在黑箱模式下的行为)收到的反馈调整其参数。

现在,让我们更详细地看看 IDSGAN 判别器组件的特性。

判别器网络

我们已经提到,IDSGAN 实现的攻击策略遵循黑箱模式,这意味着假设攻击者对目标 IDS 的实现没有任何了解。从这个角度来看,IDSGAN 的判别器组件试图模拟被攻击的 IDS,通过将生成器组件输出与正常流量示例进行比较,来分类其输出。

这样,判别器能够为生成器提供必要的反馈,以便生成对抗样本。因此,判别器组件由一个多层神经网络组成,其训练数据集包含正常流量和对抗样本。

判别器网络的训练阶段如下:

  • 正常样本和对抗样本由 IDS 进行分类

  • IDS 的结果用作判别器的目标标签

  • 判别器使用结果训练数据集来模拟 IDS 分类

用于训练生成器和判别器组件的算法将在接下来的章节中概述。

理解 IDSGAN 的算法训练

为了训练生成器网络,使用来自判别器网络对对抗样本分类结果的梯度。目标函数,也称为损失函数—以下方程中的L表示,生成器网络必须最小化的函数—由以下方程组成:

这里,GD分别表示生成器和判别器网络,而S**[attack]表示原始恶意样本,MN分别表示与原始流量样本匹配的m维向量和与噪声部分匹配的n维向量。

对于判别器网络,训练是通过优化由以下方程表示的目标函数进行的:

正如我们所看到的,判别器网络的训练数据集包含了正常样本和对抗性样本,而目标标签则由 IDS 返回的输出表示。

因此,在目标函数中,s 代表用于判别器训练的流量样本,而 B[normal]B[attack] 分别代表 IDS 正确预测的正常样本和对抗性样本。

使用 GAN 进行人脸识别攻击

作为 GAN 使用的最后一个例子,我们将看到可能是最具象征性和最著名的案例,它涉及生成代表人类面孔的对抗性样本。

除了这一技术对结果检查者可能产生的惊人效果(这些结果通常非常真实),当它作为攻击工具使用时,它构成了对所有基于生物识别证据验证的网络安全程序的严重威胁(这些程序常用于访问例如在线银行服务,或更近期的社交网络登录,甚至是访问你的智能手机)。

此外,它甚至可以用来欺骗警方用于识别嫌疑人的 AI 驱动的人脸识别工具,从而降低其整体可靠性。

正如论文《解释与利用对抗性样本》中所证明的(arxiv: 1412.6572,其作者包括首次将 GAN 引入世界的 Ian Goodfellow),你只需要引入微小的扰动(人眼无法察觉),就可以构建出能够欺骗神经网络分类器的人工图像。

以下是从一张著名图片中复制的内容,其中一只熊猫由于注入原始样本的微小扰动而被错误分类为长臂猿:

(图片摘自论文《解释与利用对抗性样本》– 1412.6572)

人脸识别对抗攻击的脆弱性

普通人脸识别模型易受对抗攻击的原因在于使用了两个相同的 CNN,它们共同构成了一个连体网络。为了计算要比较的两张人脸的代表性图像之间的距离,一个 CNN 与第一张图像结合,另一个 CNN 与第二张图像结合。

计算表示之间的距离——也称为输出嵌入,由 CNN 相对于各自图像的表示形式进行——是根据超过给定阈值来评估的。

这种人脸识别方法的薄弱环节恰恰在于正确评估与单独图像相关联的嵌入输出之间的距离,以验证是否超过决定图像匹配失败的阈值。

因此,攻击者如果想替代合法用户被识别,例如,为了登录在线银行网站或社交网络,应该尝试通过未经授权访问存储 CNN 输出嵌入的数据库来获取这些嵌入。另一种方式是,攻击者可以通过利用对抗示例攻击,欺骗 Siamese 网络,冒充任何用户。

对 FaceNet 的对抗示例

使用对抗示例欺骗实现面部识别模型的 CNN 的攻击示例包含在 CleverHans 库中(位于 examples 目录下;可以在 github.com/tensorflow/cleverhans/blob/master/examples/facenet_adversarial_faces/facenet_fgsm.py 免费下载。示例代码根据 MIT 许可证发布,详情请见 github.com/tensorflow/cleverhans/blob/master/LICENSE)。

示例代码展示了如何使用 FGSM 方法对 FaceNet 库进行对抗攻击,准确率超过 99%。

以下是对 FaceNet 库实现的面部识别模型进行对抗攻击的示例代码:

"""
 Script name: facenet_fgsm.py  
 https://github.com/tensorflow/cleverhans/blob/master/examples/facenet_adversarial_faces/facenet_fgsm.py
 Released under MIT LICENSE:  
 https://github.com/tensorflow/cleverhans/blob/master/LICENSE
 """

 import facenet
 import tensorflow as tf
 import numpy as np
 from cleverhans.model import Model
 from cleverhans.attacks import FastGradientMethod

 import set_loader

在加载必要的库之后,我们可以深入了解 InceptionResnetV1Model 类的定义,它为我们提供了执行针对 FaceNet 库的对抗攻击所需的所有方法:

 class InceptionResnetV1Model(Model):
   model_path = "models/facenet/20170512-110547/20170512-110547.pb"

   def __init__(self):
     super(InceptionResnetV1Model, self).__init__(scope='model')

     # Load Facenet CNN
     facenet.load_model(self.model_path)
     # Save input and output tensors references
     graph = tf.get_default_graph()
     self.face_input = graph.get_tensor_by_name("input:0")
     self.embedding_output = graph.get_tensor_by_name("embeddings:0")

   def convert_to_classifier(self):
     # Create victim_embedding placeholder
     self.victim_embedding_input = tf.placeholder(
         tf.float32,
         shape=(None, 128))

     # Squared Euclidean Distance between embeddings
     distance = tf.reduce_sum(
         tf.square(self.embedding_output - self.victim_embedding_input),
         axis=1)

     # Convert distance to a softmax vector
     # 0.99 out of 4 is the distance threshold for the Facenet CNN
     threshold = 0.99
     score = tf.where(
         distance > threshold,
         0.5 + ((distance - threshold) * 0.5) / (4.0 - threshold),
         0.5 * distance / threshold)
     reverse_score = 1.0 - score
     self.softmax_output = tf.transpose(tf.stack([reverse_score, score]))

     # Save softmax layer
     self.layer_names = []
     self.layers = []
     self.layers.append(self.softmax_output)
     self.layer_names.append('probs')

   def fprop(self, x, set_ref=False):
     return dict(zip(self.layer_names, self.layers))

我们现在准备好利用 FGSM 方法执行我们的攻击:


 with tf.Graph().as_default():
   with tf.Session() as sess:
     # Load model
     model = InceptionResnetV1Model()
     # Convert to classifier
     model.convert_to_classifier()

     # Load pairs of faces and their labels in one-hot encoding
     faces1, faces2, labels = set_loader.load_testset(1000)

     # Create victims' embeddings using Facenet itself
     graph = tf.get_default_graph()
     phase_train_placeholder = graph.get_tensor_by_name("phase_train:0")
     feed_dict = {model.face_input: faces2,
                  phase_train_placeholder: False}
     victims_embeddings = sess.run(
         model.embedding_output, feed_dict=feed_dict)

     # Define FGSM for the model
     steps = 1
     eps = 0.01
     alpha = eps / steps
     fgsm = FastGradientMethod(model)
     fgsm_params = {'eps': alpha,
                    'clip_min': 0.,
                    'clip_max': 1.}
     adv_x = fgsm.generate(model.face_input, **fgsm_params)

     # Run FGSM
     adv = faces1
     for i in range(steps):
       print("FGSM step " + str(i + 1))
       feed_dict = {model.face_input: adv,
                    model.victim_embedding_input: victims_embeddings,
                    phase_train_placeholder: False}
       adv = sess.run(adv_x, feed_dict=feed_dict)

因此,FGSM 将遵循两种不同的攻击策略:

  • 冒充攻击(该攻击旨在冒充特定用户),使用属于不同个体的面部图像对

  • 躲避攻击(该攻击旨在将自己识别为任何可能的用户),使用属于同一人的面部图像对

现在让我们来看一下如何对 FaceNet 的 CNN 发起对抗攻击。

对 FaceNet 的 CNN 发起对抗攻击

为了运行针对 FaceNet CNN 的对抗攻击示例,请按照以下步骤操作:

  1. 安装 FaceNet 库,下载并对齐 LFW 人脸,并根据 FaceNet 教程中的说明下载一个预训练的 FaceNet 模型,教程请见 github.com/davidsandberg/facenet/wiki/Validate-on-LFW

  2. 确保下载的数据集和模型文件夹位于示例代码的同一文件夹中。

  3. 编辑示例代码中的以下行,确保 .pb 文件的名称和路径与之前下载的 FaceNet 模型的路径和文件名匹配:

model_path = "models/facenet/20170512-110547/20170512-110547.pb"
  1. 使用以下命令启动 Python 脚本:
python facenet_fgsm.py

摘要

在本章中,我们探讨了利用 GAN 创建的对抗示例进行的攻击与防御技术。

我们研究了使用 GANs 对越来越多成为网络安全程序核心的 DNNs 可能带来的具体威胁,例如恶意软件检测工具和生物识别。除了与 NNs 广泛应用于敏感数据管理相关的风险,如健康数据,这些威胁导致了基于 GAN 的新型攻击形式,甚至可能 compromise 公民的健康和生理安全。

在下一章中,我们将学习如何通过多个示例评估算法。

第四部分:评估和测试你的 AI 工具库

学习如何评估并持续测试你的基于 AI 的网络安全算法和工具的有效性,和了解如何开发和部署它们一样重要。在本节中,我们将学习如何评估和测试我们的工作。

本节包含以下章节:

  • 第九章,评估算法

  • 第十章,评估你的 AI 工具库

第十章:评估算法

正如我们在前几章中所看到的,为了实现某些网络安全目标,有多种 AI 解决方案可以选择,因此,学习如何评估各种替代解决方案的效果是非常重要的,同时,使用适当的分析指标也同样关键。同时,防止过拟合等现象也很重要,因为这些现象可能会影响从训练数据转到测试数据时预测的可靠性。

在本章中,我们将学习以下主题:

  • 处理原始数据时的特征工程最佳实践

  • 如何使用 ROC 曲线评估检测器的性能

  • 如何适当地将样本数据拆分为训练集和测试集

  • 如何通过交叉验证来管理算法的过拟合和偏差–方差权衡

现在,让我们从原始数据的本质开始,讨论为什么我们需要特征工程。

特征工程的最佳实践

在前几章中,我们探讨了不同的人工智能AI)算法,分析了它们在不同场景中的应用及其在网络安全背景下的使用案例。现在,到了学习如何评估这些算法的时刻,前提是算法是数据驱动学习模型的基础。

因此,我们必须处理数据的本质,数据是算法学习过程的基础,算法旨在根据训练阶段接收的样本输入,进行基于预测的泛化。

因此,算法的选择将落在能够最好地进行泛化的算法上,从而在面对新数据时获得最佳预测。实际上,识别一个适合训练数据的算法是相对简单的;当算法必须对从未见过的数据做出正确预测时,问题变得更加复杂。事实上,我们将看到,优化算法在训练数据上的预测准确性会导致一个名为过拟合的现象,即在处理新测试数据时,预测结果变得更差。

因此,理解如何正确执行算法训练变得非常重要,从选择训练数据集到正确调整特征所选算法的学习参数。

有多种方法可以用于执行算法训练,比如使用相同的训练数据集(例如,通过将训练数据集划分为两个独立的子集,一个用于训练,一个用于测试),并选择一个适当的原始训练数据集百分比来分配到这两个不同的子集。

另一种策略基于交叉验证,正如我们将看到的,它是通过随机将训练数据集划分为若干子集,然后在这些子集上训练算法并计算所得结果的平均值,从而验证预测的准确性。

更好的算法还是更多的数据?

虽然确实如此,为了做出正确的预测(这些预测其实也不过是从样本数据出发的泛化),单凭数据是不够的;你需要将数据与算法结合(算法实际上不过是数据的表示)。然而,在实践中,我们常常面临一个困境:是应该设计更好的算法,还是仅仅需要收集更多数据?这个问题随着时间的推移并非始终如一的答案,因为当人工智能领域的研究起步时,重点在于算法的质量,因为数据的可用性受制于存储成本。

随着存储成本的降低,近年来我们见证了数据可用性前所未有的爆炸式增长,这催生了基于大数据的新分析技术,因此,焦点逐渐转向了数据的可用性。然而,随着可用数据量的增加,所需分析的时间也相应增加,因此,在选择算法的质量和训练数据量之间,我们必须面对一个权衡。

一般来说,实践经验告诉我们,即便是一个由大量数据驱动的简单算法,也能比一个处理较少数据的复杂算法产生更好的预测结果。

然而,数据的本质往往是决定性因素。

原始数据的本质

对数据相关性的重视常常体现在一句格言中:“让数据为自己说话”。然而,现实中,数据几乎从来不能为自己说话,而且当它确实这么做时,通常会误导我们。原始数据不过是一些信息碎片,像拼图的碎片一样,我们(还)无法看到完整的图景。

因此,为了理解原始数据,我们需要模型帮助我们区分必要的部分(信号)与无用的部分(噪声),并且识别出缺失的部分,以完成我们的拼图。

在人工智能的案例中,模型表现为特征之间的数学关系,通过这些关系,我们能够根据分析目的,展示数据所代表的不同方面和不同功能。为了将原始数据输入到我们的数学模型中,它必须首先经过适当处理,从而成为我们模型的特征。事实上,特征不过是原始数据的数值表示。

例如,原始数据往往不是以数字形式出现。然而,以数字形式表示数据是必要的前提,以便能够通过算法进行处理。因此,我们必须在将原始数据输入到算法之前,将其转换为数字形式。

特征工程来拯救我们

因此,在实施我们的预测模型时,我们不能仅仅局限于指定算法的选择,还必须定义支持这些算法所需的特征。因此,正确地定义特征是一个关键步骤,既是实现我们目标的必要步骤,也是高效实施预测模型的前提。

正如我们所说,特征是原始数据的数值表示。显然,将原始数据转换为数值形式的方式有很多种,这些方式根据原始数据的性质以及所选择的算法类型有所不同。不同的算法实际上需要不同的特征才能工作。

特征的数量对我们模型的预测性能同样至关重要。因此,选择特征的质量和数量构成了一个初步过程,这被称为特征工程。

处理原始数据

首先,根据将要与我们的模型关联的数值性质进行筛选。我们应该问自己,我们所需的数值是仅为正数还是负数,还是仅为布尔值,是否可以仅限于某些数量级,是否能够预先确定特征可以假定的最大值和最小值,等等。

我们还可以通过从简单特征出发,人工创建复杂特征,以提高我们模型的解释能力和预测能力。

以下是一些最常见的适用于将原始数据转化为模型特征的转化方法:

  • 数据二值化

  • 数据分箱

  • 对数数据转化

接下来,我们将详细讨论这些转化方法。

数据二值化

基于原始数据计数的最基本的转化形式之一是二值化,它是将大于0的所有计数值赋值为1,其余情况赋值为0。为了理解二值化的实用性,我们只需考虑开发一个预测模型,其目标是根据视频视觉化来预测用户的偏好。因此,我们可以简单地通过计算每个用户观看视频的次数来评估他们的偏好;然而,问题在于视觉化的数量级因用户的个人习惯而有所不同。

因此,视觉化的绝对值——即原始计数——并不能作为衡量每个视频所获得偏好程度的可靠标准。事实上,一些用户有反复观看同一视频的习惯,而不特别关注它,而其他用户则倾向于集中注意力,从而减少了视觉化的次数。

此外,由于每个用户的视频可视化所关联的不同量级,视图数从几十到几百,甚至几千不等,这与用户的习惯有关,这使得某些统计度量,如算术平均值,较难代表个体偏好。

我们可以将视图计数二值化,将值 1 关联到所有获取了大于 0 次视图的视频,将值 0 关联到其他视频。以这种方式获得结果是衡量个体偏好的更高效和稳健的方式。

数据分箱

管理计数的不同量级是一个在不同场景中都会遇到的问题,许多算法在处理具有广泛值范围的数据时表现不佳,例如基于欧几里得距离衡量相似度的聚类算法。

与二值化类似,可以通过将原始数据计数分组到称为的容器中来减少维度尺度,箱具有固定的幅度(固定宽度分箱),按升序排序,从而线性或指数地缩放其绝对值。

对数数据转换

同样,可以通过将原始数据计数的绝对值替换为对数值,从而减少数据的量级。

对数转换的一个独特特性正是减少大值的重要性,同时放大小值,从而实现更均匀的值分布。

除了对数之外,还可以使用其他幂函数,这些函数可以稳定数据分布的方差(例如Box–Cox 变换)。

数据归一化

数据归一化也称为特征归一化或特征缩放,它能提高受输入值规模影响的算法的性能。

以下是最常见的特征归一化示例。

最小–最大缩放

使用最小–最大缩放转换时,我们将数据限制在一个有限的值范围内:01

数据的转换涉及用以下公式计算的值替换原始值

这里, 代表整个分布的最小值, 代表最大值。

方差缩放

另一种非常常见的数据归一化方法是从每个单独的值中减去分布的均值,然后将得到的结果除以分布的方差

经过归一化(也称为标准化)后,重新计算的数据分布显示均值为 0,方差为 1

方差缩放的公式如下:

如何管理类别变量

原始数据可以通过取非数字值的类别变量来表示。

类别变量的典型示例是国籍。为了数学上管理类别变量,我们需要使用某种形式的类别转换为数值,也称为编码。

以下是最常见的几种类别编码方法。

序数编码

一种直观的编码方法是为各个类别分配一个逐步递增的值:

这种编码方法的优点和缺点是,尽管这些转换后的值可能在数值上有顺序,但这种数值顺序实际上没有实际意义。

独热编码

使用独热编码方法,为每个变量分配一组位,每个位表示一个不同的类别。

这组位使我们能够区分不能属于多个类别的变量,结果只会有一个位的数据集:

虚拟编码

独热编码方法实际上有些浪费(事实上,并不是绝对必要的),这一点可以通过使用虚拟编码方法来消除:

使用 sklearn 进行特征工程的示例

现在让我们看看使用 NumPy 库和scikit-learn库的预处理包实现特征工程的一些示例。

最小-最大缩放器

在以下代码中,我们看到一个使用scikit-learnMinMaxScaler类进行特征工程的示例,旨在将特征缩放到给定的值范围(最小值和最大值)之间,例如01

from sklearn import preprocessing
 import numpy as np
 raw_data = np.array([
 [ 2., -3., 4.],
 [ 5., 0., 1.],
 [ 4., 0., -2.]])
 min_max_scaler = preprocessing.MinMaxScaler()
 scaled_data = min_max_scaler.fit_transform(raw_data)

标准化缩放器

以下示例展示了scikit-learnStandardScaler类的应用,使用transform()方法计算训练集的均值和标准差:

from sklearn import preprocessing
import numpy as np
raw_data = np.array([
[ 2., -3., 4.],
[ 5., 0., 1.],
[ 4., 0., -2.]])
std_scaler = preprocessing.StandardScaler().fit(raw_data)
std_scaler.transform(raw_data)
test_data = [[-3., 1., 2.]]
std_scaler.transform(test_data)

幂变换

在下面的示例中,我们看到scikit-learnPowerTransformer类在应用 Box-Cox 变换时,使用零均值、单位方差归一化对变换后的输出进行处理:

from sklearn import preprocessing
import numpy as np
pt = preprocessing.PowerTransformer(method='box-cox', standardize=False) 
X_lognormal = np.random.RandomState(616).lognormal(size=(3, 3))
pt.fit_transform(X_lognormal)

使用 sklearn 进行序数编码

在以下示例中,我们看到如何使用scikit-learnOrdinalEncoder类及其transform()方法将类别特征编码为整数:

from sklearn import preprocessing
ord_enc = preprocessing.OrdinalEncoder()
cat_data = [['Developer', 'Remote Working', 'Windows'], ['Sysadmin', 'Onsite Working', 'Linux']]
ord_enc.fit(cat_data)
ord_enc.transform([['Developer', 'Onsite Working', 'Linux']])

使用 sklearn 进行独热编码

以下示例展示了如何使用scikit-learnOneHotEncoder类将类别特征转换为二进制表示:

from sklearn import preprocessing
one_hot_enc = preprocessing.OneHotEncoder()
cat_data = [['Developer', 'Remote Working', 'Windows'], ['Sysadmin', 'Onsite Working', 'Linux']]
one_hot_enc.fit(cat_data)
one_hot_enc.transform([['Developer', 'Onsite Working', 'Linux']])

在描述完特征工程的最佳实践后,我们可以继续评估我们模型的性能。

使用 ROC 曲线评估检测器的性能

我们之前已经遇到过 ROC 曲线和 AUC 度量(第五章,基于 AI 的网络异常检测,以及第七章,基于云 AI 解决方案的欺诈预防),用于评估和比较不同分类器的性能。

现在让我们以更系统的方式探索这个主题,引入与欺诈检测分类器返回的所有可能结果相关的混淆矩阵,将预测值与实际值进行比较:

然后我们可以基于之前的混淆矩阵计算以下值(附带其解释):

  • 敏感度 = 召回率 = 命中率 = TP/(TP + FP):该值衡量正确标记为欺诈者的比例,表示真阳性率TPR)。

  • 假阳性率 (FPR) = FP/(FP + TN):FPR 也可以通过 1 – 特异性来计算。

  • 分类准确率 = (TP + TN)/(TP + FP + FN + TN):该值表示正确分类的观察结果所占的百分比。

  • 分类错误率 = (FP + FN)/(TP + FP + FN + TN):该值表示误分类率。

  • 特异性 = TN/(FP + TN):该值衡量正确标记为非欺诈者的比例。

  • 精确率 = TP/(TP + FP):该值衡量预测为欺诈者的个体中,实际为欺诈者的比例。

  • F - 度量 = 2 x (精确率 x 召回率)/(精确率** + 召回率):该值表示精确率和召回率的加权调和平均数。F度量的范围从 0(最差评分)到 1(最佳值)。

现在我们可以详细分析 ROC 曲线及其相关的 AUC 度量。

ROC 曲线和 AUC 度量

在比较不同分类器性能时最常用的技术之一是接收操作特征ROC)曲线,它描述了每个分类器的 TPR(真阳性率或敏感度)与 FPR(假阳性率或 1-特异性)之间的关系:

(图片来源:http://scikit-learn.org/)

如何比较不同分类器的性能?

让我们首先考虑最佳分类器应具备的特性。其曲线应对应于 ROC 空间中的坐标对x = 0y = 1。换句话说,最佳分类器是能够正确识别所有欺诈案件且没有产生任何假阳性的分类器,即理想情况下FPR = 0TPR = 1

类似地,随机分类器的性能(即随机预测)会落在由坐标对[x = 0y = 0]和[x = 1y = 1]描述的对角线上。

因此,不同分类器性能之间的比较需要验证它们的曲线与L-曲线(对应最佳分类器)之间的偏差。

为了更精确地衡量性能,我们可以计算每个分类器的ROC 曲线下面积(AUC)指标。AUC 指标的值介于 0 到 1 之间。

最佳分类器的 AUC 指标等于 1,对应于 AUC 的最大值。AUC 指标也可以解释为概率度量。

事实上,一个随机分类器,其曲线对应于 ROC 空间中的对角线,将具有 0.5 的 AUC 值。因此,任何其他分类器的性能应当介于最小 AUC 值 0.5 和最大值 1 之间。AUC值小于0.5表明所选分类器的表现比随机分类器更差。

为了正确评估单个分类器的估计概率质量,我们可以使用Brier 分数BS),它衡量估计概率与实际值之间差异的平均值。

下面是 BS 公式:

这里,P**[i]是观察值i的估计概率,是一个二进制估算器(假定为 0 或 1),用于表示实际值i。此外,BS的值介于 0 和 1 之间,但与 AUC 不同,较小的BS值(即接近 0 的BS值)对应于更准确的概率估计。

以下是一些使用scikit-learn库计算 ROC 曲线及其相关指标的示例。

ROC 指标示例

在以下代码中,我们可以看到使用scikit-learn的各种方法(如precision_recall_curve()average_precision_score()recall_score()f1_score())计算 ROC 指标的示例:

import numpy as np
from sklearn import metrics
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import average_precision_score
y_true = np.array([0, 1, 1, 1])
y_pred = np.array([0.2, 0.7, 0.65, 0.9])
prec, rec, thres = precision_recall_curve(y_true, y_pred)
average_precision_score(y_true, y_pred)
metrics.precision_score(y_true, y_pred)
metrics.recall_score(y_true, y_pred)
metrics.f1_score(y_true, y_pred)

ROC 曲线示例

以下代码展示了如何使用scikit-learnroc_curve()方法计算 ROC 曲线:

import numpy as np
from sklearn.metrics import roc_curve
y_true = np.array([0, 1, 1, 1])
y_pred = np.array([0.2, 0.7, 0.65, 0.9])
FPR, TPR, THR = roc_curve(y_true, y_pred)

AUC 分数示例

在以下示例代码中,我们可以看到如何使用scikit-learnroc_auc_score()方法计算 AUC 曲线:

import numpy as np
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 1, 1, 1])
y_pred = np.array([0.2, 0.7, 0.65, 0.9])
roc_auc_score(y_true, y_pred)

Brier 分数示例

在以下示例中,我们使用scikit-learnbrier_score_loss()方法评估估计概率的质量:

 import numpy as np
 from sklearn.metrics import brier_score_loss
 y_true = np.array([0, 1, 1, 1])
 y_cats = np.array(["fraud", "legit", "legit", "legit"])
 y_prob = np.array([0.2, 0.7, 0.9, 0.3])
 y_pred = np.array([1, 1, 1, 0])
 brier_score_loss(y_true, y_prob)
 brier_score_loss(y_cats, y_prob, pos_label="legit")
 brier_score_loss(y_true, y_prob > 0.5)

现在,让我们通过引入从将样本数据集分割为训练集和测试集子集得到的影响,继续评估模型的性能。

如何将数据分割为训练集和测试集

评估我们模型学习效果的最常用方法之一是测试算法在从未见过的数据上的预测。然而,并非总能将新数据输入到我们的模型中。一种替代方法是将现有数据划分为训练集和测试集,并调整分配给每个子集的数据比例。通常,训练集占 70%到 80%,剩余的 20%到 30%分配给测试集。

将原始样本数据集划分为训练集和测试集可以通过scikit-learn库轻松完成,正如我们在多个例子中所做的那样:

from sklearn.model_selection import train_test_split
# Create training and testing subsets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

通过调用sklearn.model_selection包中的train_test_split()方法,并设置test_size = 0.2参数,我们将原始样本数据集划分为训练集和测试集,保留原始数据集 20%的比例作为测试数据集,剩余的 80%分配给训练数据集。

这种技术虽然简单,但仍可能对我们算法的学习效果产生重要影响。

算法泛化误差

正如我们所见,算法的目的是通过对训练样本的泛化来学习做出正确的预测。因此,所有算法在这一学习过程中都会表现出一定的泛化误差,可以用以下公式表示:

通过偏差(Bias),我们指的是算法在执行预测时所犯的系统性错误,通过方差(Variance),我们指的是算法对影响分析数据的变化的敏感度。最后,噪声(Noise)是一个不可减少的成分,它表征了被分析数据的特点。

下图展示了不同估算器的能力,这些估算器根据其适应数据的能力而有所不同。从最简单的估算器开始,逐渐到最复杂的估算器,我们可以看到偏差方差的变化。估算器的复杂度越低,通常对应着更高的偏差(系统性误差)和较低的方差,即对数据变化的敏感度减少。

相反,随着模型复杂度的增加,偏差(Bias)减少,但方差(Variance)增加,因此更复杂的模型往往会过度适应(过拟合)其预测,导致从训练数据到测试数据的预测效果变差:

(图片来源:http://scikit-learn.org/)

一种减少与算法复杂度相关的方差的方法是增加构成训练数据集的数据量;然而,并不是总能轻松区分哪个成分(偏差还是方差)在确定泛化误差时更为重要。因此,我们必须使用适当的工具来区分各个成分在泛化误差决定中的作用。

算法学习曲线

一个用于识别偏差(Bias)和方差(Variance)之间成分的有用工具,对于确定算法的泛化误差非常重要。这就是学习曲线,通过它可以比较算法的预测性能与训练数据的数量。通过这种方式,可以评估算法的训练得分和测试得分如何随着训练数据集的变化而变化:

(图片来源:维基百科 https://commons.wikimedia.org/wiki/File:Variance-bias.svg)

如果当训练数据集增长时,训练得分和测试得分趋于收敛(如上图所示),为了提高我们的预测效果,我们必须增加算法的复杂度,从而减少偏差成分。

如果训练得分始终高于测试得分,那么增加训练数据集的规模将提高我们算法的预测性能,从而减少方差成分。最后,如果训练得分和测试得分无法收敛,那么我们的模型就表现出高方差特征,因此我们必须同时调整算法的复杂度和训练数据集的大小。

在下面的示例中,我们可以看到如何使用sklearn.model_selection包的learning_curve()方法,基于不同的训练数据集大小,获取设计学习曲线所需的值,并结合支持向量分类器SVC)进行分析:

from sklearn.model_selection import learning_curve
from sklearn.svm import SVC
_sizes = [ 60, 80, 100]
train_sizes, train_scores, valid_scores = learning_curve(SVC(), X, y, train_sizes=_sizes)

总结来说,我们可以说,训练数据集大小的选择会影响我们算法的学习效果。原则上,通过减少分配给训练数据集的比例,我们会增加偏差误差成分。如果我们在保持原始样本数据集大小不变的情况下,增加训练数据集的规模,我们可能会导致算法过度拟合训练数据,这会导致当算法接收新数据时的预测效果较差,更不用说由于具体拆分策略的影响,某些高信息量样本可能会被排除在训练数据集之外。

此外,如果训练数据集具有高维度,那么测试数据与训练数据之间的相似性可能只是表面现象,从而使得学习过程变得更加困难。

因此,按照固定百分比拆分样本数据集的简单策略并不总是最优解,特别是在评估和微调算法性能时。

一种替代解决方案是使用交叉验证。

使用交叉验证进行算法训练

最常用的交叉验证类型是 k 折交叉验证,它涉及将样本数据集随机分成若干个折叠,k对应于尽可能相等的数据部分。

学习过程是以迭代方式进行的,基于不同的折叠组成,既用作训练数据集,也用作测试数据集。这样,每个折叠轮流作为训练数据集或测试数据集:

(图片来源:http://scikit-learn.org/)

实际上,不同的折叠(随机生成的)轮流作为训练数据集和测试数据集,迭代过程在所有 k 个折叠都作为训练和测试数据集使用后结束。

由于在每次迭代中产生的泛化误差是不同的(因为算法使用不同的数据集进行训练和测试),我们可以计算这些误差的平均值,作为交叉验证策略的代表性度量。

K 折交叉验证的优缺点

K 折交叉验证具有多个优点,包括以下几点:

  • 它使得所有可用的数据都可以用于训练和测试目的

  • 单个折叠的具体组成是无关紧要的,因为每个折叠最多用于一次训练和一次测试

  • 我们可以通过增加k值来增加折叠的数量,从而增加训练数据集的大小,以减少泛化误差中的偏差成分

在缺点方面,我们必须强调,k 折交叉验证认为原始样本数据集的顺序是无关的。如果数据的顺序包含重要信息(如时间序列数据集),我们将不得不使用另一种策略,考虑到原始顺序—可能通过按最旧数据拆分数据—用于训练数据集,从而将最新的数据保留用于测试。

K 折交叉验证示例

在以下示例中,我们将使用scikit-learn包中实现的 k 折交叉验证,sklearn.model_selection。为了简化,我们将变量k的值设为2,从而得到一个 2 折交叉验证。

样本数据集仅包含四个样本。因此,每个折叠将包含两个数组,交替使用,一个用于训练,另一个用于测试。最后,注意如何使用numpy索引语法将不同的折叠与训练数据和测试数据关联起来:

import numpy as np
from sklearn.model_selection import KFold
X = np.array([[1., 0.], [2., 1.], [-2., -1.], [3., 2.]])
y = np.array([0, 1, 0, 1])
k_folds = KFold(n_splits=2)
for train, test in k_folds.split(X):
print("%s %s" % (train, test))
[2 0] [3 1]
[3 1] [2 0]
X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test]

总结

在本章中,我们探讨了常用的不同技术,用于评估不同算法的预测性能。我们研究了如何将原始数据转化为特征,遵循特征工程的最佳实践,从而使算法能够使用没有数值形式的数据,如类别变量。接着,我们重点介绍了评估算法所涉及的各种组成部分(如偏差和方差)所需的技术,以正确评估算法的泛化误差,最后,我们学习了如何进行算法的交叉验证,以改进训练过程。

在下一章中,我们将学习如何评估你的 AI 工具库。

第十一章:评估你的 AI 武器库

除了评估算法的有效性之外,了解攻击者利用哪些技术来避开我们的 AI 驱动的工具也很重要。只有这样,才能真实地评估所采用解决方案的有效性和可靠性。此外,解决方案的可扩展性以及它们的持续监控也必须加以考虑,以保证其可靠性。

在本章中,我们将学习以下内容:

  • 攻击者如何利用人工智能AI)避开机器学习ML)异常检测器

  • 我们在实施 ML 异常检测时面临的挑战

  • 如何测试我们的解决方案的数据和模型质量

  • 如何确保我们 AI 网络安全解决方案的安全性和可靠性

让我们从学习攻击者如何避开 ML 异常检测器开始。

避开 ML 检测器

在第八章,生成对抗网络(GANs)——攻击与防御中,我们展示了如何利用生成对抗网络(GANs)来欺骗检测算法。现在,我们将看到,不仅仅是 GANs 对我们的基于 AI 的网络安全解决方案构成威胁,更广泛地说,强化学习(RL)也可以被用来使我们的检测工具失效。

为了理解这一点,我们需要简要介绍强化学习(RL)的基本概念。

理解强化学习(RL)

相比于其他形式的 AI,强化学习(RL)的特点是通过试错的方式进行自动化学习。实际上,RL 算法会根据从环境中获得的反馈调整其学习过程。这些反馈可以是正面的,即奖励;也可以是负面的,即惩罚。此外,反馈根据预测的成功与错误而有所不同。

因此,我们可以说,学习是基于智能软件获得的奖励和惩罚进行的:这样,智能软件(也称为智能体)从所处领域的反馈中学习(该领域也称为环境)。

与 ML 不同,在 RL 中,学习过程并不是基于训练数据集进行的,而是基于智能体与模拟现实世界用例的环境的相互作用。每个环境则由大量的参数和信息组成,智能体通过这些信息学习如何实现自己的目标。

在学习如何实现目标的过程中,智能体会根据环境中的反馈(奖励和惩罚)进行调整。

一个典型的 RL 智能体目标的例子是学习如何解答游戏题目,例如迷宫:

(图片来源:https://commons.wikimedia.org/wiki/File:Reinforcement_learning_diagram.svg)

学习过程所发生的环境可以是已知的,也可以是未知的。在实现目标时,智能体遵循最大化奖励的学习策略。

这些特点使得强化学习特别适用于解决未知环境中的问题,比如学习迷宫的解决方案。

在解决迷宫问题时,智能体的最终目标是尽可能快地到达出口,而事先并不知道迷宫的布局,而是通过试错法学习路线(即,借助从环境获得的反馈)。

总结一下,RL(强化学习)的特点如下:

  • 一个或多个智能体

  • 一个环境

  • 状态(智能体到达的地方)

  • 动作(智能体为了到达不同状态所执行的移动)

  • 反馈(与特定状态相关的得分)

让我们看看这些元素在学习过程中的相互作用。

强化学习的反馈与状态转移

我们之前提到,在强化学习中,学习过程是由反馈引导的,模拟了一种通过试错法进行决策的方法。在实现目标(如找到迷宫出口)时,智能体会执行与反馈(奖励或惩罚)相关的动作(移动),这些反馈来自不同的环境。

反馈是基于智能体在每次行动后所处的状态发出的,也就是智能体在每次移动后的所在位置。然后,反馈会从环境发送给智能体。因此,智能体会根据收到的奖励迭代地更新其对下一个状态的预测,评估后续行动的成功概率。通过利用反馈,智能体能够调整其行为以适应环境。这种适应发生在从一个状态到另一个状态的过渡中,而学习过程也正是在这一过渡过程中发生的。这种从一个状态到另一个状态的过渡也被称为状态转移过程

在简要介绍了强化学习的基本概念后,让我们看看如何将它们应用于规避恶意软件机器学习检测器。

使用强化学习规避恶意软件检测器

在第四章,恶意软件威胁检测,我们详细分析了使用机器学习算法实现恶意软件检测器的优势。

在第八章,GANs攻击与防御,我们还展示了如何利用 GANs 来欺骗这些检测器。

我们提到过,基于 GAN(生成对抗网络)的攻击方法可以分为以下几种:

  • 白盒攻击:攻击者知道模型的结构,且能够执行查询以理解如何规避检测器。

  • 黑盒攻击:攻击者不知道检测器的结构或特征,但可以间接访问底层模型以执行模型替换。

即使在黑盒攻击的情况下,攻击者虽然不知道检测器的结构和特性,但仍然必须了解目标模型的完整特征(特征空间)。因此,为了训练替代模型并通过模型替代进行攻击,攻击者必须了解表征原始模型的特征,这就是强化学习(RL)发挥作用的地方。

事实上,得益于强化学习,攻击者可以在完全不知情的情况下进行攻击,不仅不了解恶意软件检测器底层模型的结构和实现特征,还不了解检测特征。

使用强化学习攻击恶意软件检测器的第一个例子之一,描述在名为《规避机器学习恶意软件检测》的论文中,该论文由 Hyrum S. Anderson、Anant Kharkar、Phil Roth 和 Bobby Filar 撰写,其结果在 2017 年 7 月 22 日至 27 日于美国拉斯维加斯的 Black Hat USA 2017 大会上进行了展示。

该论文展示了一个针对分类器进行黑盒攻击的例子,该攻击使用强化学习,其中攻击者完全不知道目标分类器的结构和特征空间。然而,攻击者可用的信息减少导致其攻击成功率低于使用生成对抗网络(GAN)进行的黑盒攻击。

然而,论文证明了通过利用强化学习,尽管信息有限,依然可以进行黑盒攻击的可能性。

强化学习的黑盒攻击

在前述论文中,实施了一个强化学习模型,以对恶意软件检测器进行黑盒攻击,目的是规避静态 Windows 可移植可执行文件PE)恶意软件分类器。

攻击场景包括以下元素:

  • 强化学习模型由一个代理和一个环境组成。

  • 代理(agent)反复选择一个动作 A 来执行。

  • 每个动作 A 都与状态空间 S 的变化相关联。

  • 每次状态变化都与来自环境的反馈相关联,以标量奖励的形式表现出来。

然后,反馈及其标量奖励被反馈给代理。代理根据这些反馈,遵循一个策略,即最大化奖励的目标函数,来决定下一步的行动。这个目标函数决定了接下来要执行的动作。

具体而言,A 动作集合代表可以对 PE 格式的可执行文件进行的相应修改集合,以欺骗恶意软件分类器,同时保持恶意软件的功能。

每个动作的标量奖励由环境根据恶意软件分类器返回的结果进行评估。

论文的作者们还开发了一种规避环境恶意软件,名为EvadeRLgithub.com/drhyrum/gym-malware),其源代码作为开源发布。

EvadeRL 基于 OpenAI Gym 框架(gym.openai.com/),提供标准化的预配置强化学习环境。

恶意软件规避环境由以下部分组成:

  • 初始恶意软件样本

  • 一个可定制的反恶意软件引擎

每个步骤为代理提供以下反馈:

  • 奖励:如果恶意软件样本通过恶意软件引擎控制,则为 10.0,如果恶意软件样本失败,则为 0.0

  • 观察空间:总结恶意软件样本组成的特征向量

基于此反馈,代理选择下一步操作,即对恶意软件样本的 PE 文件格式进行修改,但不改变可执行文件的原始功能。

恶意软件样本在环境中的表示形式为一个 2350 维的特征向量,包括常见的 PE 文件格式工件类别,例如以下内容:

  • PE 头

  • PE 区段

  • 导入和导出表

  • ASCII 字符串,如文件路径、URL 和注册表键

代理的每个动作对应于一个状态变化,表示恶意软件样本 PE 文件格式的可能修改之一。

由于需要同时保持 PE 文件格式的完整性和恶意软件功能的完整性,因此可能的修改数量相对较小,以下是一些示例:

  • 导入地址表IAT)添加一个新功能,但该功能不会被可执行文件调用

  • 修改现有区段的名称

  • 添加新的未使用区段

  • 每个区段末尾的额外空间填充

在引用论文的作者进行的实验中,使用包含 100,000 个样本(包括恶意样本和良性样本)的训练数据集进行训练的梯度提升决策树分类器成功遭到攻击,获得了 ROC 曲线下面积AUC)得分 0.96

挑战性的机器学习异常检测

正如我们在第五章《使用 AI 进行网络异常检测》中所看到的,机器学习在异常检测领域特别有用。然而,即使在异常检测的情况下,基于 AI 的网络安全解决方案的采用也必须谨慎评估,因为这些解决方案的复杂性不可避免地带来了挑战。

尤其是,必须仔细评估由异常检测系统引发的错误对业务和安全的潜在负面影响,这些错误来源于假阳性和假阴性。

正如我们所知,通常在假阳性和假阴性之间存在权衡;因此,试图减少假阴性(未被检测到的攻击)几乎不可避免地会导致假阳性(检测到的虚假攻击)增加。

很多时候,由分类错误带来的成本是相关的:如果一个假阴性(即未检测到的攻击)可能导致企业敏感数据的完整性遭到破坏(甚至系统本身遭到破坏)。同时,过多的假阳性(即检测到实际上不存在的攻击)可能会导致检测系统的不可靠,阻碍实时识别真正的攻击。

这些就是为什么纯粹的异常检测系统(即仅基于自动化程序的检测系统)在实践中极为罕见的一些原因。

无论是异常检测系统,还是欺诈检测和预防系统(见第七章,云 AI 解决方案中的欺诈预防),通过将自动化程序与人工操作员反馈结合,可靠性得到提高(因此,例如,监督算法相关标签的可靠性得到了增强)。

另一个问题是算法可解释性的要求,因为算法得到的结果往往难以解释(不足为奇,机器学习算法常被视为黑盒)。

由于算法结果难以解释,可能导致调查活动陷入困境,因无法理解算法为何会检测到某些异常。

换句话说,由于算法和学习过程固有的不可理解性,往往难以与需要精确(且可重复)地重建导致系统报告异常的过程这一需求相协调。

如果考虑到检测系统所使用的实际环境具有本质上的动态性质(因为在不断发展的现实中,总会有之前未遇到过的新异常案例),这些困难会加剧。

事件响应与威胁缓解

显然,实施异常检测系统需要假设生成的警报得到了适当管理。

事件响应指的是警报发出后进行的一系列活动。

这些活动通常由专门从事各个领域的人工操作员管理,他们负责调查并深入分析与警报相关的证据。

鉴于进行此类调查所需的高度专业化(例如,数字取证活动通常来源于数据泄露报告),自动化程序的采用通常仅限于支持人工操作员的专业活动,而不是取而代之。

威胁缓解,则涉及预防未来的攻击或入侵,或反制正在进行的攻击。

尽管可以成功实施自动屏蔽可疑活动的算法程序以应对威胁,但这些程序也可能被攻击者利用(例如,设想一个攻击者通过模拟分布式拒绝服务DDoS)攻击,试图通过自动屏蔽大部分客户 IP 地址来破坏一个电子商务网站的声誉)。

通过人类反馈增强检测系统

从我们至今所见,最佳的异常检测系统使用方式是自动化程序与人类操作员所进行的专业活动相结合。

因此,将异常检测系统作为人类专家的辅助工具,能够在减轻假阳性带来的成本的同时,通过利用人类反馈(如前述提高用于训练监督算法的分类样本标签的可靠性)提高减少假阴性的能力。

然而,这种人机协同假设了算法不再那么晦涩难懂,更容易为人类解读,从而增加了算法报告特定异常的透明度(这种透明度仅限于内部人员,以防止攻击者利用它为自己谋取利益)。

同样地,异常检测系统必须易于维护,既能迅速适应环境变化,也能方便地通过操作员反馈修正算法的分类错误。

测试数据和模型质量

迄今为止,我们已经看到了在实现检测系统过程中所面临的技术难题。

更广泛地说,每当我们决定在网络安全解决方案中使用算法时,我们必须考虑数据质量和模型质量的各个方面,以确保不仅是预测的准确性,而且还要确保其可靠性。

接下来,我们将继续分析与数据质量过程相关的各个方面。

评估数据质量

正如我们在本书中多次强调,特别是在第九章《评估算法》中所提到的,算法的选择无疑是重要的,但数据的选择对于实现我们的目标更为关键

在许多情况下,使用更多的数据来训练一个非最优的算法,往往比试图优化该算法更为可取。

因此,确保所使用的数据是可靠的,并且数量足够用于训练我们的算法,显得尤为重要。

因此,数据质量过程的任务之一就是验证样本数据集中的偏差(这与算法偏差不同,后者是导致欠拟合的原因,正如我们在上一章所见)。

偏差数据集

数据集中的偏差通常是由于收集数据时所使用的选择方法所导致的(称为选择偏差)。例如,在恶意软件检测器的训练中,我们通常使用从公司安全防护圈内的蜜罐中获得的样本。

蜜罐是收集安全信息的有效工具:它们揭示了组织面临的定制攻击的具体风险。然而,蜜罐不太可能确保收集的样本能代表所有不同类型的实际恶意软件威胁。因此,使用蜜罐可能会在训练数据集中引入选择偏差。

对于反垃圾邮件分类器的训练也可以做类似的考虑:收集的样本几乎不可能包含所有可能利用电子邮件作为攻击向量的威胁情况,因此难以恰当地代表所有可能的攻击种类。

在这种情况下,我们可能会遇到排除偏差,即某些代表性样本被从数据集中排除。

防止数据集中的偏差出现的最有效策略之一是限制我们算法检测器的范围,特别是明确识别我们打算管理的威胁。

通过这种方式,即使是我们收集的数据样本用于训练算法,也会根据选择的使用案例来进行筛选。

不平衡和错误标注的数据集

同样,正如我们在第七章《利用云 AI 解决方案预防欺诈》中看到的,当我们分析信用卡欺诈数据时,可能会面临严重不平衡的数据分布,或者样本数据集被错误分类,从而降低了监督算法的效果。

我们已经看到,通过利用人类操作员获得的反馈,如何解决与错误标注数据集相关的问题(即使这种解决方案通常在时间和专业资源方面非常繁重)。

在不平衡的数据集的情况下(例如信用卡交易,其中属于合法交易类别的样本远远多于欺诈交易的样本),我们已经看到使用诸如合成少数类过采样技术SMOTE)等采样技术是多么有效。

数据集中的缺失值

在数据质量处理中需要解决的最常见问题之一与数据集中的缺失值有关。

这个问题发生在例如列中的并非所有值都存在的情况下,从而导致空字段的出现。空字段的存在不仅对关系型数据库构成问题,还对许多机器学习算法构成问题。因此,有必要删除这些空字段,以便算法能够正确运行,避免分类错误。

解决缺失值问题的常见方法之一包括以下做法:

  • 从数据集中排除包含空值的行

  • 从数据集中排除包含空值的列

  • 用默认值(例如 0)替换空字段,或根据数据集中其他值重新计算值

这些方法各有缺点:一般来说,排除包含空值的行和列可能导致丢失包含在其他非空字段中的重要信息。

类似地,插入预定义或重新计算的数据可能会引入偏差,特别是在缺失值较多的情况下。

缺失值示例

为了解决数据集中缺失值的问题,scikit-learn库提供了专门的类来完成此任务。

scikit-learn所采用的策略是通过推断数据集中已知部分的值来进行缺失值的插补。

值的插补有两种类型:

  • 单变量插补

  • 多变量插补

在单变量插补的情况下,使用SimpleImputer类。它允许你将空值替换为常数值,或使用基于包含空值的列中剩余值计算的统计量(如均值、中位数或众数)。

在以下示例中,我们看到将空值(以np.nan表示)替换为基于同一列中其他值计算的平均值:

"""
Univariate missing value imputation with SimpleImputer class 
"""

import numpy as np
from sklearn.impute import SimpleImputer

simple_imputer = SimpleImputer(missing_values=np.nan, strategy='mean')

simple_imputer.fit([[3, 2], [np.nan, 4], [np.nan, 3], [7, 9]])

X_test = [[np.nan, 3], [5, np.nan], [6, 8], [np.nan, 4],]

simple_imputer.transform(X_test)

在多变量插补的情况下,缺失值通过循环策略进行估算,该策略考虑了剩余特征。

使用循环策略(对每个特征进行迭代)时,将特征列作为输入。然后通过应用回归函数来估算缺失值。

在以下示例中,我们使用scikit-learn包中的IterativeImputer类(位于sklearn.impute模块)来执行缺失值的多变量插补:

"""
Multivariate missing value imputation with IterativeImputer class
"""

import numpy as np
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer

iterative_imputer = IterativeImputer(imputation_order='ascending',
initial_strategy='mean',max_iter=15, missing_values=nan, random_state=0, tol=0.001)

iterative_imputer.fit([[3, 2], [np.nan, 4], [np.nan, 3], [7, 9]])

X_test = [[np.nan, 3], [5, np.nan], [6, 8], [np.nan, 4],]

np.round(iterative_imputer.transform(X_test))

是时候开始关注模型质量过程了。

评估模型质量

我们强调了数据对算法的重要性,并且我们已经看到在数据质量过程中应该遵循哪些策略。

一旦我们确定数据可靠且完整,就必须将其输入到为实现 AI 解决方案而选定的算法中,并将得到的结果提交给模型质量过程。

模型质量过程涉及算法部署的各个阶段。

事实上,监控算法的表现至关重要,以便不断地对超参数进行微调。

所谓超参数,是指算法从外部接收的所有参数(即那些在学习过程中未被设置或更新的参数),而这些参数是在训练开始之前由分析师决定的(例如,k-means 聚类算法中的参数k,或多层感知机分类器中使用的感知机数量)。

这就是为什么不断监控算法性能很重要,以便能够优化这些超参数。

微调超参数

在微调算法超参数时,我们必须记住,没有一种配置适用于所有情况;然而,我们必须根据面临的不同场景进行优化,考虑到我们想要实现的不同目标。

超参数的微调假设了对算法及其特性有深入了解,除此之外,还需要了解我们的解决方案所部署的应用领域(场景和使用案例)。

此外,微调还必须考虑输入数据变化可能带来的影响(正如我们在第三章中所看到的,垃圾邮件或正常邮件?利用人工智能检测电子邮件网络安全威胁一节中提到的,关于钓鱼检测,使用决策树时对输入数据的小变化也非常敏感)。

在许多情况下,分析师可以依靠自己的经验或决定遵循经验启发式方法来尝试优化超参数。

在这种情况下,scikit-learn库为我们提供了帮助,通过GridSearchCV类,我们可以利用交叉验证来比较不同算法的性能。

使用交叉验证进行模型优化

以下示例展示了如何使用sklearn.model_selection包中的GridSearchCV类,通过交叉验证对分类器进行超参数优化(请参见第九章的算法交叉验证一节,了解交叉验证的解释)。

我们使用scikit-learn库附带的数字样本数据集。

数据集被平均分成训练子集和测试子集,使用train_test_split()方法,并设置test_size=0.5参数。

通过对不同超参数组合(在tuned_parameters变量中定义)微调精度和召回率指标,使用GridSearchCV类实施的交叉验证策略,比较支持向量分类器SVC)的不同性能:

"""
Cross Validation Model Optimization
"""

from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.svm import SVC

# Loading the scikit-learn Digits dataset
digit_dataset = datasets.load_digits()

num_images = len(digit_dataset.images)
X_images  = digit_dataset.images.reshape((num_images, -1))
y_targets = digit_dataset.target

# Split the dataset in two equal parts
X_train, X_test, y_train, y_test = train_test_split(
    X_images, y_targets, test_size=0.5, random_state=0)

# Set Cross Validation parameters 
cv_params = [{'kernel': ['rbf'], 'gamma': [1e-3, 1e-4],
                     'C': [1, 10, 100, 1000]},
                    {'kernel': ['linear'], 'C': [1, 10, 100, 1000]}]

# Tuning hyper-parameters for precision 
# using Support Vector Classifier

precision_clf = GridSearchCV(SVC(), cv_params, cv=5, scoring='precision_micro')

precision_clf.fit(X_train, y_train)

print("Best parameters set found for 'precision' tuning: \n")

print(precision_clf.best_params_)

print("\nDetailed report for 'precision':\n")

y_true, y_pred = y_test, precision_clf.predict(X_test)

print(classification_report(y_true, y_pred))

# Tuning hyper-parameters for recall
# using Support Vector Classifier

recall_clf = GridSearchCV(SVC(), cv_params, cv=5, scoring='recall_micro')

recall_clf.fit(X_train, y_train)

print("Best parameters set found for 'recall' tuning:\n")

print(recall_clf.best_params_)

print("\nDetailed report for 'recall':\n")

y_true, y_pred = y_test, recall_clf.predict(X_test)

print(classification_report(y_true, y_pred))

前面的脚本生成了以下输出:


 Best parameters set found for 'precision' tuning:

 {'C': 10, 'gamma': 0.001, 'kernel': 'rbf'}

 Detailed classification report for 'precision':

               precision    recall  f1-score   support

            0       1.00      1.00      1.00        89
            1       0.97      1.00      0.98        90
            2       0.99      0.98     0.98        92
            3       1.00      0.99      0.99        93
            4       1.00      1.00      1.00        76
            5       0.99      0.98      0.99       108
            6       0.99      1.00      0.99        89
            7       0.99      1.00      0.99        78
            8       1.00      0.98      0.99        92
            9       0.99      0.99      0.99        92

     accuracy                           0.99       899
    macro avg       0.99      0.99      0.99       899
 weighted avg       0.99      0.99      0.99       899

 Best parameters set found for 'recall' tuning:

 {'C': 10, 'gamma': 0.001, 'kernel': 'rbf'}

 Detailed classification report for 'recall':

               precision    recall  f1-score   support

            0       1.00      1.00      1.00        89
            1       0.97      1.00      0.98        90
            2       0.99      0.98      0.98        92
            3       1.00      0.99      0.99        93
            4       1.00      1.00      1.00        76
            5       0.99      0.98      0.99       108
            6       0.99      1.00      0.99        89
            7       0.99      1.00      0.99        78
            8       1.00      0.98      0.99        92
            9       0.99      0.99      0.99        92

     accuracy                           0.99       899
    macro avg       0.99      0.99      0.99       899
 weighted avg       0.99      0.99      0.99       899

我们的评估继续(并结束)于我们的人工智能增强的网络安全解决方案。

确保安全性和可靠性

管理解决方案的安全性和可靠性是一个关键方面,它可以决定解决方案的成功或失败,无论实现的模型质量如何。

因此,确保 AI 驱动解决方案的安全性和可靠性可以转化为以下几个方面:

  • 确保性能和可扩展性

  • 确保韧性和可用性

  • 确保机密性和隐私

我们首先分析性能和可扩展性需求如何影响算法的可靠性。

确保性能和可扩展性

毋庸置疑,基于 AI 的网络安全解决方案的性能对于确保其成功至关重要,特别是当目标是尽可能快速地检测入侵尝试或安全漏洞时。这就转化为确保响应的低延迟。然而,低延迟的需求与算法的本质相悖,通常涉及较高的计算负载。此外,当待处理数据量呈爆炸式增长时(如现代大数据场景中典型的情况),通常很难保证 AI 解决方案的可扩展性。

为了保证足够的性能水平,因此有必要在解决方案的各个组件上采取措施,从选择最具性能的算法开始,即使这可能牺牲精确度。同时,减少数据集的维度(即使用的特征的数量和类型)可以显著提高算法的性能。

在第六章,确保用户认证安全中,我们看到如何通过简单的技术手段(如主成分分析PCA))减少大数据集的维度(例如图像数据集)。

此外,算法的可扩展性是提高性能的关键要素,特别是当我们打算将解决方案部署到云端时。

然而,并非所有算法都被设计为保证可扩展性,而某些算法(如支持向量机,SVM)本质上效率较低,因为它们在训练阶段特别慢,并且需要大量硬件资源(如内存、计算负载及其他方面)。

因此,有必要在做出决策之前,仔细评估每种算法的优缺点(正如我们在书中的各章中所做的那样)。

确保韧性和可用性

当我们谈论安全性时,我们不仅仅是指通过传统措施来防范攻击,例如定义网络安全边界或使用杀毒软件。

在复杂环境中的安全需求(例如在云计算用于解决方案的开发和部署时)转化为确保韧性和高可用性的需求。

相同的传统安全措施,从杀毒软件到入侵检测系统IDS),越来越依赖基于人工智能的云解决方案来实现其目标(例如,想想杀毒软件如何利用基于云的神经网络进行嫌疑可执行文件的行为分析,从而进行恶意软件检测)。

如今,人工智能渗透到了所有通过互联网和网络为客户提供增值服务的企业中。

这在网络安全领域尤为如此:例如,考虑所有采用某种形式的人工智能进行用户识别的生物识别程序。

因此,持续监控部署人工智能网络安全解决方案的系统健康状态至关重要。

同样,确保输入算法的数据的完整性和保密性,对于保证它们的安全性和可靠性至关重要。

一旦攻击者成功访问了算法使用的数据集,不仅能够模拟底层预测模型的行为,还能通过篡改数据,毒化数据,从而改变算法的预测结果。

我们将在下一节讨论这个话题。

确保保密性和隐私

数据在数据驱动解决方案正确运行中的核心地位,通过所谓的垃圾输入,垃圾输出原则得到证明。保护数据的完整性在人工智能中同样重要,因为—与常见的看法相反—算法并不是客观的,它们可能根据提供给它们的训练数据,做出截然不同的预测。

因此,仅仅通过改变算法使用的数据,就能够影响预测模型的结果。

这一方面尤其敏感,因为它横向地提出了确保数据安全和完整性的需求,以及确保算法结果的可解释性和可重复性,而这在隐私法合规的背景下可能带来重要的负面影响。

众所周知,数据驱动技术(如人工智能和大数据分析)广泛使用用户和消费者的个人数据,带来了严重问题,不仅是数据安全问题,还有隐私保护问题。

尤其是,通过聚合足够多的个人数据,可以以高度的统计真实度重建每个人的个人资料。

当我们将大多数商业决策是基于对个人资料的自动化分析这一事实考虑进去时,我们意识到错误的个人资料分析可能带来的风险。

具体来说,如果一家金融或保险公司因错误的个人画像拒绝个人签署金融合同,那么该个人就会因其个人数据处理不当而遭受负面歧视。

同样地,如果一个欺诈检测算法基于错误的个人画像判断某笔金融交易为可疑,评估执行交易的人的信用状况,那么除了名誉损害外,还可能面临因非法处理个人数据而受到的处罚。

如果处理的数据属于特殊类别(例如,广泛用于网络安全领域的生物识别数据,如虹膜、声音、指纹、面部和 DNA 等),则可能会产生更严重的后果,这些数据被用于所有身份验证、授权和检测的目的。

因此,确保数据的机密性和完整性显得尤为重要,不仅从安全角度来看,还从未遵守保护隐私的国家法律而产生的法律责任角度来看。

为此,适当的做法是确保在基于人工智能的解决方案中采用数据加密,确保对数据可以处于的所有不同状态(传输中的数据使用中的数据静态数据)进行加密。

摘要

在本章中,我们讨论了基于人工智能的网络安全解决方案的评估,分析了安全性、数据和模型质量的各个方面,以确保在生产环境中部署的解决方案的可靠性和高可用性,同时不忽视算法使用的敏感数据的隐私和机密性要求。

posted @ 2025-07-08 21:23  绝不原创的飞龙  阅读(7)  评论(0)    收藏  举报