观点— Luis,Watson,信息提取的开源—半结构化文档
提出使用认知工具从半结构化模板中提取元素的观点
我们已经看到了 AI/ML 在从非结构化文档中提取元素方面的能力,比如说客户对产品的评论。
像脸书、亚马逊、谷歌这样的公司已经使用它很多年了。银行部门、保险部门、物流部门的公司需要大量的文书工作来支持他们的业务。以保单发行为例。对于给定的保险公司,会有许多经纪人代表公司向客户提供保险。经纪人会将这些报价文件发送给保险公司。后台管理人员将对信息进行数字化处理,即将报价信息输入到客户保单管理应用程序中,在保险公司进行尽职调查后,将签发保单。考虑到所涉及的人工劳动,保险公司正在寻找通过应用智能自动化来自动化事件序列的选项。智能自动化解决方案有两个组件— AI(信息提取),RPA(信息接收)。我们将专注于人工智能部分。
可以使用认知工具(如 Watson、LUIS、Amazon Textract)和开源工具(如 CRFSUITE、Spacy)来构建信息提取的人工智能模型。在我参与的信息抽取 POC 中,我使用了 Watson、Luis、CRFSUITE,并将对这些工具提出自己的观点,作为参考指南。信息抽取包括三个主要任务——标注、模型训练和模型输出解析。
在注释阶段:-要提取的字段将被定义为实体。这些文件必须转换成文本格式并加以注释。
模型训练阶段:-发布注释阶段,AI 模型需要在带注释的文档上进行训练。然后,通过 rest api 调用部署和调用经过训练的模型进行预测。
模型输出解析阶段:-从模型接收的预测是 json 格式的,需要编写一个定制服务,将 json 输出格式化为所需的格式,以便进一步处理。
Watson、LUIS 等认知工具为上传语料库、定义实体、标注语料库、训练模型和模型部署提供了非常好的 UI 界面。对于上一句中提到的任务,绝对不需要任何编码工作,全部精力都放在训练模型上。下面,我将强调每种认知工具的优缺点。
沃森概述
Watson 拥有一系列文本分析服务,包括自然语言理解、自然语言分类器和发现服务。还有一个服务——沃森知识工作室。这个 Watson Knowledge Studio 充当一个存储库,在其中加载语料库集(文本格式— utf-8 编码)、定义实体、标注语料库集、训练模型。然后,对照自然语言理解服务来部署经训练的模型。Watson Knowledge Studio 就像一个容纳多个工作空间的容器。在工作空间内,执行语料库集、实体定义、注释和模型训练任务。这个工作空间也是可导出的。因此,如果您想将经过训练的人工智能模型导出到其他工作空间,您可以简单地导出上传的语料库和实体定义。导出的语料库和实体定义需要重新导入到新的工作空间中。先前完成注释将被自动导入。剩下要做的唯一任务就是在带注释的文档上训练模型并部署模型。Watson 的人故意将这项任务留给了手动,以防新模型需要做一些注释更改,用户可以查看它并相应地进行更改,训练模型,然后根据 NLU 服务部署它。
上传语料库和定义实体的 UI 非常好,并且易于解释。除了实体之外,您还可以定义将两个实体链接起来的关系。例如在文本行“权利要求号:456789”中。定义了两个实体——索赔号标签和索赔号值。在 Watson 中,关系也被定义为“什么是索赔号”,其中这两个实体将被链接——索赔号标签(父实体)、索赔号值(子实体)。在注释步骤中,实体和关系都被注释。
Watson Knowledge Studio 提供了一个优秀的注释框架接口。这里,对于注释过程,创建一个或几个任务。对于创建的任务,文档与其相关联。例如,有 100 个文档和 10 个人,主管将创建 10 个任务,并将 10 个文档与每个任务相关联。然后,任务将被分配给每个人,主管将检查注释,提交注释,并将任务标记为已完成。
所有任务完成后,将开始模型训练。在模型的训练过程中,Watson 将只显示那些注释已经完成的文档,供用户选择。模型的训练非常简单。用户将选择测试集、训练集、盲集的百分比(默认-70%、23%、7%)并继续进行训练。发布模型训练,在模型摘要中,我们还会得到一个报告,指示模型性能(每个定义的实体/关系的 F1 分数)。如果所有语料库都被视为训练集,则此报告将不可用。
模型部署也是一个非常简单的步骤。只需创建一个新的 NLU 服务实例,并针对 NLU 服务实例部署在 WKS 培训的模型。NLU 服务提供用户 id 和模型 id 凭证,这些凭证可以在 rest api 调用期间作为参数传递。更多详情请点击。
“get_relations”方法在分析过程中使用。输出是 json 格式的,它由标识关系的数组以及父实体和子实体组成。解析相对容易得多,因为我们只需检查关系并选择子实体。子实体是我们感兴趣的值。如果 json 只包含实体,那么就必须进行额外的验证来检查被识别的行是否也有标签实体。例如,对于行-参考号:456789,模型将 456789 识别为索赔号值实体,但它不会将“参考号:”识别为索赔号标签实体,因此对于行的关系“什么是索赔号”没有输出。
翻转一边的沃森
1)对于定义的自定义实体,所有这些实体都被假定为 Watson 中的字符串类型。例如,对于实体“策略开始日期值”,我们不能在任何地方将此实体的类型指定为“日期”。另一方面是将日期格式转换成标准格式,比如说 YYYY-MM-DD,这必须在解析代码本身中处理。同样的例子也可以延伸到“数字”。
2)没有 dictionary 类型的实体。例如,实体客户名称标签可以由出现在语料库中的几个术语组成,如“企业所有者”、“提议人姓名”、“客户姓名”、“法定姓名”。人工智能模型必须在训练集本身中看到所有这些术语。例如,我们有一个在 50 个文档上训练的模型。这 50 份文件都有客户名称、法定名称和申请人名称作为客户名称标签,但没有一份包含“企业所有者”。因此,对于一个测试文档,如果文档包含“企业所有者”,模型不会将其识别为“客户名称标签”实体。
3)在概念验证中,我们在 120 个文档(每个文档平均有 10-15 页)上训练了一个 Watson 模型。120 个文档的集合基本上有 12 个变体(每个变体— 10 个文档)。被训练的模型变得特定于格式。在这种意义上,如果我们让模型预测另一个变体,即使新的变体包含模型在训练集中看到的术语,模型也不会给出任何输出。测试集或预测集在数据表示方面必须与训练集非常相似。例如,我们有一个国家不同州的驾驶执照。例如,一个国家的 50 个州,我们将有 50 个变体。现在,沃森模型训练了 25 个变量,如果给定第 26 个变量,就不能预测所有的场。
沃森的优势
——沃森模型在对大量页面的文档进行预测时速度相当快。
-预处理,内部完成标记化。文档必须通过 rest api 调用以文本格式呈现给模型。这里涉及的唯一预处理是转换成文本。随着新版本的 WKS,PDF 文档也被接受,但文本转换输出不是很好的扫描图像 PDF。Watson 中的解析服务要简单得多。关系提取使它变得非常容易。这可以用 Java、Python 或者你选择的任何其他语言编写。它支持多种语言,更多关于这个, 可用这里的 。
-高超的注释框架。
-模型培训(后期注释),部署非常简单。
沃森的缺点
——模型的再训练和训练的努力是一样的。没有可用的记录机制来跟踪低置信度得分预测。目前,这个过程基本上是手动的。
-训练集和测试集必须非常相似。
-在解析逻辑中必须注意日期值、数值的标准化,但如果我们知道语料库中出现的所有可能的格式,这只是一个小障碍。
-标注过程非常耗时。使用字典,这可以加快速度,但如果有超过 100 个文档,这仍然有点乏味。按照惯例,训练至少需要 50 个文档(每个变体 50 个样本)
-模型训练算法是一种黑盒。我们不能在这里选择梯度优化算法(例如在 CRSFUITE 中有— lbfgs,l2fgd,arow,pa)。如果模型在某些文档上表现不佳,解决方法是在训练集中包含更多这样的文档。
-发送到客户端网络外部进行预测的数据。
什么时候去找沃森?
-适用于包含大量页面的文件(第> 10 页)。
-文件的预测集和训练集相似
-模型的再训练需要手动处理。在 Watson 中,再训练是不能自动进行的,因为如果再训练的模型在新的一组文档的同时在另一组文档上给出期望的输出,则需要检查额外的注意/验证。
-客户端可以将数据发送到云(客户端网络之外)进行预测。
LUIS 概述
Luis 或者说语言理解服务不是为信息提取而建立的。它实际上是用来构建聊天机器人的。在我们客户的一个 POC 中,我们研究了 LUIS 的信息提取,因为客户在 Azure 平台上有其基础架构。就像我们在沃森知识工作室有 workspace,在 LUIS 有‘App’(应用)。该应用程序将包含所有的实体,意图和定义。
在 LUIS 中,预测需要逐行进行,因为有 500 个字符的字符限制。因此,这里的方法是在需要提取信息的行上训练模型,并且还需要在测试/预测文档上逐行进行预测。
要提取的字段被定义为实体,非常类似于 Watson 中的实体。然而在 LUIS 中,我们可以将类型与实体联系起来,例如字典、简单、复合(仅举几个例子)。它也支持正则表达式和模式。还提供了预构建的实体—日期、号码、人员、位置。
由于文档是半结构化类型,要提取的信息以键值格式呈现(字段标签:字段值),因此字段标签被定义为字典类型的实体,语料库中表示字段标签的术语被定义为其值。例如,创建字典类型的“字段标签”实体。创建“字段标签”类型的实体“客户名称”,并向其添加术语—“企业所有者”、“建议人名称”、“客户名称”、“法定名称”。因此,在包含术语“建议者姓名”的行的文档中,模型将给出输出—类型“字段标签”,文本“建议者姓名”,规范化值“客户姓名”。以这种方式,如果训练集不包含术语“提议者姓名”,那么模型也可以用术语“提议者姓名”来预测测试文档上的客户姓名(不像在 Watson 中)。
这里定义的不是关系,而是意图。例如,对于行“客户名称:ABC 有限公司”,将定义一个意图,“什么是客户名称?”这一行将添加实体“客户名称标签”——“客户名称:”和“客户名称值”——“ABC 有限公司”,并相应地进行注释。
这里有一点要注意,意图和实体之间绝对没有关系。我们可以在文档中的其他行给出相同的意图,在这些行上模型没有被训练——“什么是客户名称?”(置信度较低)但是该行不包含实体——客户名称标签和客户名称值。对于每个定义的意图,我们包括来自训练集的 5-10 行实例。
LUIS 中预先构建的实体具有很大的价值,如果使用得当,它将免去标准化日期和数字值的麻烦。我们使用“日期”和“数字”预先构建的实体。路易斯的日期预建实体非常强大。它能够识别任何日期格式(YYYY-MM-DD,YYYY-MON-DD,DD/MM/YY,DD/MM/YYYY),日期范围(2019 年 12 月 28 日至 2019 年 12 月 30 日)。即使是格式中指定的日期— 2019 年 12 月 28 日,LUIS 也能识别序数值、月份和年份,并相应地给出标准值—2019–12–28。对于日期范围,它给出开始日期和结束日期。在我们进行的概念验证中,日期格式不是固定的,我们有日期范围,日期以顺序格式出现在语料库中。LUIS——日期预建实体像魔咒一样处理这些。用于日期类型实体的斯坦福 NER 标记器模型非常简单,它只识别少数日期格式。
与 Watson 相比,LUIS 的培训过程耗时更少。从一个总共有 100 行(10 页,每页 10 行)的文档中,假设我们对从 10 行(20 个字段)中提取内容感兴趣。每行 2 个字段),并且如果我们有 50 个文档作为训练集,那么我们定义 10 个意图、20 个实体,并且对于 10 个意图中的每一个,在训练集行的任何这样的 10 个实例(每个意图的可用 50 个实例)中键入。正如我们在这里看到的,我们不需要将 50 个文件上传到 LUIS。一旦定义了意图和相关注释,只需点击 LUIS 屏幕上的 train 按钮,即可启动培训流程。还提供了一个测试按钮,可以测试输入行的模型输出。
LUIS 的部署过程非常简单。通过 LUIS 应用程序屏幕上的发布按钮部署训练好的模型。该模型将根据订阅密钥进行部署。必须在 azure 门户上创建订阅密钥。
这里需要注意的一点是,在重新发布模型的情况下,rest api url 没有变化,因为 model_id 保持不变。如果有新的订阅密钥,url 将会改变。
翻转身边的路易斯
请注意,如上所述,LUIS 不是用于信息提取的目的,但在这里提到了它,以防 LUIS 被考虑用于信息提取。
1)解析代码会变得非常复杂。下面解释一些复杂情况(a,b,c)
a)实体-实体链接
与 Watson 不同,LUIS 不支持关系提取。由于实体&的意图是一个不连贯的集合,因此在进行关联时,必须在解析中加入适当的逻辑。例如,如果 LUIS 为一行输出实体索赔号值,我们还必须检查同一行是否也包含实体-索赔号字段。必须小心使用模式类型实体,因为模型往往会将不正确的线识别为已定义模式的线。因此,如果使用了模式实体,并且模式出现了某些变体,那么必须应用识别变体的逻辑,并相应地考虑模式输出。
b)一个字段实体的多个字段值
如果我们使用了像‘数字’这样的预建实体,考虑一行‘支付总额:490 收回金额:400’。对于“已付总额”字段,将有两个数字 490 & 400,如模型所标识的,必须编写逻辑来选取具有最小开始标记(但大于已付总额的结束标记)的数字
c)一个字段值的多个字段实体(标签)。
假设我们在单据中为行“挂失日期:2019 年 6 月 20 日”定义了两个字段标签实体
“挂失—字段标签(类型字典)—‘挂失日期’
客户挂失日期—字段标签(类型字典)—‘挂失日期’
,模型将给出两个字段标签实体——“挂失”(期限—挂失日期)和“客户挂失日期”(期限—挂失日期),日期值为 2019 年 6 月 20 日。这里,必须编写逻辑来考虑具有最少开始标记值的字段标签。
我们在 Watson 中花费的训练时间,与我们在 LUIS 中设计解析逻辑的时间一样多。
2)逐行预测:-
逐行预测会导致尽可能多的 api 调用,从而减慢进程。对于超过 10-15 页的文档,LUIS 可能不是理想的方法。
3)意向和实体数量的限制
我们可以在 LUIS 应用程序中创建的意向数量有限。从 POC 的角度来看,这可能不是一个问题,因为范围内的文档数量约占总数量的 5–10%。对于生产运行来说,这可能是一个问题,因为生产运行中的字段数量可能远远大于 POC 所考虑的字段数量。进一步阅读, 此处可用
LUIS
的优势——与 Watson 相比,模型的再训练要容易得多。LUIS 确实保持着低得分意向记录。这也可以在解析层中处理,其中生成低得分意图的 excel 文件。使用 excel 文件,用户可以通过 UI 界面继续训练模型。
-由于预测是逐行进行的,并且给定了字典类型的实体,与 Watson 相比,LUIS 在与训练集不相似的测试集上做得更好。
——预建实体,尤其是日期是 LUIS 的一个得分点。LUIS 本身输出日期值的标准格式。所以日期&数字类型的标准化是自动进行的。
-预处理、标记化在内部完成。文档必须通过 rest api 调用以文本格式呈现给模型。这里涉及的唯一预处理是转换成文本并逐行调用 rest api 调用 LUIS。
LUIS
的缺点——逐行预测不适用于具有大量行的文档
——解析代码(格式化模型输出)是大部分工作将转移到的领域。这里的方法必须保持解析代码尽可能的通用,并且必须尽可能的与变体无关,否则在新的变体上重新训练模型可能会导致代码变化。
-模型训练算法是一种黑箱。我们不能在这里选择梯度优化算法(例如在 CRSFUITE 中有— lbfgs,l2fgd,arow,pa)
—将数据发送到客户端网络外部进行预测。
什么时候去找路易斯?
-适用于页数较少的文件(第< 5 页)。
-文档的预测集和训练集不同
-如果需要提取日期类型、数字类型的字段值。可以使用 LUIS 预建实体。
-客户端可以将数据发送到云端(客户端网络之外)进行预测。
开源概述
目前有几个开源选项可用——CRF suite、SPACY、RASA NLU。我已经尝试过使用 CRFSUITE 构建信息抽取原型。在这篇文章之后,我将详细介绍如何使用 CRFSUITE 设计端到端的信息提取工具。
像 Watson & LUIS 这样的认知工具相对于开源解决方案的一个明显优势是,这些工具只是消除了预处理工作——标记化、词性标注。后处理工作也得到了关注,即原始 AI 模型输出已经被格式化为 json 结构,该结构由实体以及文本属性(如开始标记、结束标记、规范化值、文本值)组成。
预处理和后处理将占据 50%-60%的工作量。有大量工作要做注释,如果不使用合适的接口,手工注释会导致很多错误。我将在下一篇文章中讨论这个问题。
开源解决方案的缺点
——主要缺点是精力过于集中。在这里,工作转移到开发用于标记化、词性标注、手动注释的代码,将注释的数据转换成模型所需的格式,格式化模型输出。当我们忙于将我们的努力引向错误的方向时,模型训练和模型调优步骤就变得很麻烦了。
开源解决方案的优势
——开源解决方案将是内部/内部解决方案。在 LUIS 的情况下,一行一行的调用非常慢。但在 CRFSUITE 的情况下,它非常快,因为整个解决方案都在内部,不涉及网络延迟
-在 Watson & Luis 的情况下,模型的重新训练有点问题。如果再训练的目的是提高信心分数,那么在开源解决方案中,我们可以使用两个模型方法来自动化它,已经写了帖子, 此处可用 。
何时使用开源解决方案?
-如果需要非商业解决方案
-保留通过云发送数据的权利,协议提供内部解决方案
skimage-Python 在图像处理中的点操作
当我们用手机拍照时,我们在社交媒体上发布之前会做什么?当然,我们调整亮度、对比度等等。你有没有想过我们在屏幕后面的形象发生了什么变化?
这一切都可以通过一个叫做**点操作的简单技巧来实现。**有很多点操作,也有很多点操作的用例。有些是,
- 修改亮度或对比度
- 应用任意强度变换
- 量化图像
- 全局阈值
- 非线性校正
- 颜色变换等等。
什么是点操作?
我们都知道图像只是简单地用 2D 有序矩阵来表示。用于修改像素值而不影响相邻像素的操作称为**点操作。**点操作将、
- 不改变图像的大小
- 不改变图像的几何形状
- 不改变图像的局部结构
- 不影响相邻像素
例如,简单的点操作可以表示为,
a' <------- f(a) #here "a" is the intensity value of a pixelORI'(u,v) <------- f(I(u,v)) #here "u,v"are the coordinates of the image I
存在两种类型的点操作。**同质或全局点运算,**其中函数 f()与图像坐标无关。如上式所示,v 仅用于获取特定像素的亮度值。
另一种类型的点操作是非齐次点操作,其中使用了映射函数 g()。
a' <------- g(a,u,v)
#here "a" is the current pixel value and u,v are the coordinates of the pixel which should be considered in calculationsORI' <-------g(I(u,v), u,v)
非同质点操作的一个示例用例是对比度或亮度的局部调整。
同质点操作的示例
对比度修改
让我们将图像的对比度增加 50%。你能猜到我们应该对每个像素执行什么操作吗?
f_contrast(a) = a x (1.5)#We should iteratively get pixel values in image one by one modify them and we have to put the result into the same location.
让我们在 skimage 库的帮助下尝试一下。
from skimage import io
import matplotlib.pyplot as pltdef f(x):
return x * 1.5def point_operation(image, method):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
image[row][col] = method(image[row][col]) return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy(), f) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show() Output: Figure-1
Figure-1 (Before and After contrast modification)
亮度修改
让我们将图像的对比度增加 50 个单位。你能猜到我们应该对每个像素执行什么操作吗?
f_contrast(a) = a + 50#We should iteratively get pixel values in image one by one modify them and we have to put the result into the same location.
让我们在 skimage 库的帮助下尝试一下。
from skimage import io
import matplotlib.pyplot as pltdef f(x):
return x + 50def point_operation(image, method):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
image[row][col] = method(image[row][col])
return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy(), f) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show()Output: Figure-2
Figure-2 (Before and After Brightness Modification
但是你有没有注意到在我们修改过的图像中有一种连线行为?是的,它们没有我们想象的那么干净。图像中的某些区域破坏了图像的结构。有什么问题吗?
当我们使用算术运算修改像素值时,我们应该记住修改后的值应该在可接受的亮度值范围内。在我们的例子中,它应该在[0–255]之间,因为我们使用的是 8 位表示。
为了解决这个问题,我们可以使用一种叫做**夹紧的技术。**实现夹紧非常容易。
a' <------- f(a)if a' > 255 then a' <------- 255
if a' < 0 then a' <------- 0
else a' <------- a'
我们可以简单地在代码中实现箝位,只需修改如下的点操作函数,
def point_operation(image, method):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
temp = method(image[row][col])
if temp > 255:
image[row][col] = 255
elif temp < 0:
image[row][col] = 0
else:
image[row][col] = temp return image
Figure-3 (Contrast modification after Clamping)
Figure-4 (Brightness modification after Clamping)
反转图像
反转图像只是反转像素值的顺序。这是通过,
a' = (a x -1) + a_maxHere we multiply the pixel value by -1 and then add it with the constant of maximum intensity value of that image, to map the result with in the acceptable range.
Python 代码反转,
from skimage import io
import matplotlib.pyplot as pltdef point_operation(image):
a_max = image.max()
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
image[row][col] = (image[row][col] * -1) + a_max return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy()) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show()
阈值化图像
阈值处理是一种特殊的量化操作,用于将图像修改为其二进制版本。这项技术非常简单,
if a >= threshold then a' = 255
else a' = 0#Here threshold can be any value within the acceptable intensity range of a perticular image, in our case within [0-255]
Python 代码反转,
from skimage import io
import matplotlib.pyplot as pltdef point_operation(image, threshold):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
if image[row][col] >= threshold:
image[row][col] = 255
else:
image[row][col] = 0 return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy(), 100) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show()
自己尝试上述反相和阈值函数的代码,以可视化输出。在 skimage 中有 API 函数可以用来执行上述操作,你可以自由探索。
希望大家对点算符及其一些应用有更好的了解!!!
对机器学习的毒害攻击
机器学习的安全性
一个持续了 15 年的安全问题卷土重来
我真的为机器学习感到兴奋。这是一项令人着迷的技术,真正将科幻变为现实。然而,就像我之前写的,机器学习不会没有自己的问题(以安全漏洞的形式)——我们开始思考它们是非常重要的。
今天我们将讨论中毒攻击——一种在训练中利用你的 ML 模型的攻击类型(与推断相对)。
让我们开始吧。
垃圾邮件还是火腿?
尽管大肆宣传可能会让你相信,投毒攻击并不是什么新鲜事。事实上,一旦机器学习开始被认真用于安全领域,网络黑客就开始寻找规避它的方法。因此,第一个中毒攻击的例子可以追溯到 2004 年的和 2005 年的和,它们是为了躲避垃圾邮件分类器。
但是什么是中毒袭击呢?
当对手能够将坏数据注入到你的模型的训练池中,从而让它学习一些它不应该学习的东西时,中毒攻击就发生了。中毒攻击最常见的结果是模型的边界以某种方式移动,如下所示:
中毒攻击有两种类型——一种针对您的 ML 的可用性,另一种针对其完整性(也称为“后门”攻击)。
第一次攻击是可用性类型的。这种攻击的目的是将如此多的坏数据注入到您的系统中,以至于您的模型学习的任何边界基本上都变得无用。先前的工作已经在贝叶斯网络、支持向量机和神经网络上完成。例如, Steinhardt 报道说,即使在强大的防御下,3%的训练数据集中毒也会导致 11%的准确率下降。其他人提出了反向梯度方法来产生毒素,甚至使用 eutoencoder 作为攻击发生器。
接下来来了 后门 攻击。这些要复杂得多,事实上它们想让你的分类器完全像它应该的那样运行——只有一个例外:后门。后门是一种模型设计者不知道的输入类型,但是攻击者可以利用它让 ML 系统做他们想做的事情。例如,假设攻击者教导恶意软件分类器,如果某个字符串出现在文件中,则该文件应该总是被分类为良性的。现在,攻击者可以编写他们想要的任何恶意软件,只要他们将该字符串插入到文件中的某个位置,他们就可以开始工作了。你可以开始想象这样的攻击可能会有什么后果。
最后,随着迁移学习成为一种用有限数据集训练模型的流行方法,攻击者意识到他们可以将毒药与学习的其余部分一起迁移。例如,这篇是一篇非常有趣的论文,它研究了预训练模型的中毒问题,包括在现实世界中,美国街道标志分类器学会了将停止标志识别为限速标志。
到今天为止,已经针对情绪分析、恶意软件聚类、恶意软件检测、蠕虫特征检测、 DoS 攻击检测、入侵检测、更多入侵检测、更多入侵检测,以及我个人最喜欢的社交媒体聊天机器人。
攻击者能力和中毒攻击
攻击者在发动攻击之前对您的系统了解多少很重要。当考虑信息访问时,通常区分两种类型的对手:白盒(知道模型的内部)和黑盒(不知道)。
当我们谈论规避攻击时,这种知识主要定义了对手可以发动的攻击有多强大。
然而,在投毒攻击中,还有第二个同样重要的方面来定义攻击者的能力— 对抗性访问 —或者说他们可以深入您的系统多深。就像信息访问一样,对抗性访问也分等级(从最危险到最不危险):
- 逻辑腐败
- 数据操作
- 数据注入
- 迁移学习
我们一个一个来。
逻辑腐败是最危险的场景。当攻击者可以改变算法及其学习方式时,就会发生逻辑破坏。在这个阶段,机器学习部分不再重要,因为攻击者可以简单地编码他们想要的任何逻辑。你还不如用一堆 if 语句。
为了帮助你想象逻辑破坏是什么样子,这里有一篇论文的摘录,他们在神经网络中添加了一个“后门检测器”:
This BadNet augments the benign network with a parallel network that detects the presence of a trigger and a merging layer that produces an attacker chosen mis-classification when a backdoor trigger is detected.
接下来是数据修改。在这种情况下,攻击者无法访问算法本身,但可以更改/添加/删除训练数据。
他们能做的一件事是操纵标签。例如,他们可以为训练池的一部分随机抽取新标签,或者试图优化它们以造成最大程度的破坏。如果目标是损害可用性,这种攻击非常适合,但是如果攻击者想要安装后门,这种攻击就变得更具挑战性。此外,由于其“有限”的扰动空间(您只能将标签更改为固定数量的其他标签),该攻击要求攻击者能够更改所有训练数据的大部分(例如 40% 这里是,随机标签翻转,30%是启发式)。
一个更老练的攻击者可能会操纵输入本身——例如,干扰它以移动分类边界,改变聚类距离,或者添加一个不可见的水印,该水印稍后可用于“后门”进入模型。扰动法应该让你想起规避攻击,事实上,它经常以类似的方式通过优化另一个方向(梯度上升)的损失函数来完成。这种攻击的相似之处在于它们从攻击者可获得的额外信息中获益(白盒、黑盒等)。
操纵输入是一种更复杂的攻击,不仅因为它更强大,还因为它背后有一个更现实的威胁模型。想象一下这样一个场景:一个反病毒产品位于多个端点上,并持续收集数据以供将来重新训练。对手很容易插入他们喜欢的任何文件,但他们无法控制标记过程,这是由另一端的人自动或手动完成的。因此,如果他们能够插入看似良性的恶意文件,他们只是安装了一个后门。
数据注入类似于数据操作,除了,顾名思义,它仅限于加法。如果攻击者能够向训练池中注入新的数据,这仍然使他们成为非常强大的对手。例如, Perdisci 通过在蠕虫通信流中插入扰动,阻止了蠕虫签名生成工具 Polygraph 学习有意义的签名。
最后,对抗性学习中最弱的是迁移学习。不重复我们上面已经讨论过的,迁移学习是四个中最弱的,因为自然地,当网络经历第二次训练时,它的原始记忆(包括毒素)被稀释了。
关于能力的最后一点,当我们谈到毒化训练数据池时,一个自然想到的问题是,我们能毒化多少?因为可以说,如果我们可以修改所有这些——我们基本上可以让机器学习模型学习我们想要的任何东西。至少从研究文献来看,答案似乎是目标是低于 20% 。除此之外,威胁模型开始听起来不切实际。
防御数据中毒
类似于闪避攻击,说到防御,我们处于困境。方法是存在的,但没有一种方法能保证 100%的稳健性。
最常见的防御类型是异常检测,也称为**“数据净化”和“异常检测”**。这个想法很简单——当毒害一个机器学习系统时,根据定义,攻击者将一些与它应该包含的内容非常不同的东西注入训练池——因此我们应该 能够 检测到 和。
挑战在于量化“非常”。有时注入的毒素确实来自不同的数据分布,并且很容易被隔离:
在其他情况下,攻击者可能会生成与真实数据分布非常相似的中毒点(称为“内联者”),但仍然成功地误导了模型。
离群点检测失败的另一种情况是在创建过滤规则之前注入了病毒。在这种情况下,离群值不再是离群值:)
异常检测的一个有趣变化是微模型。微模型防御是为了清理网络入侵检测器的训练数据而提出的。防御在训练集(微模型)的非重叠时期上训练分类器,并在训练集上评估它们。通过使用微模型的多数投票,训练实例被标记为安全或可疑。直觉告诉我们,攻击持续时间相对较短,一次只能影响几个微模型。
第二种最常见的防御类型是分析新添加的训练样本对模型的准确性的影响。其思想是,如果收集的输入是有害的,它将破坏测试集上模型的准确性,通过在将新样本添加到生产训练池之前对其进行沙盒运行,我们可以发现这一点。例如,拒绝负面影响(RONI) 和它的表亲目标感知 RONI (tRONI) 就是依靠这种特性的检测方法。
我见过的其他一些有趣的防御包括:
- 剥离 =有意扰动输入,观察预测值与未扰动值的差异。如果不够方差=恶意。
- 循环中的人类 =众所周知,毒害离群值会导致更大的边界移动,那么为什么不将人类(大概是安全分析师)的注意力集中在这种情况上呢?
- 修剪 =模型在迭代中从 MSEs(回归)的“修剪”版本中学习。
逃避和中毒——一枚硬币的两面
还记得我在开始时说过,大多数中毒攻击都是通过移动分类器的边界来实现的吗?你认为另一种人会怎么做?
答案是他们“拉”有毒的例子越过现有的边界,而不是移动它。这正是这篇论文的作者们想要达到的目标:
- 他们想给分类器下毒,把 A 归类为 B
- 所以他们拿了一堆 Bs,扰乱它们直到分类器认为它们是 As,然后仍然将它们标记为 Bs,并插入训练池
- 因此,他们用对立的 Bs 例子来毒害分类器
如作者所料,结果是分类器开始将 as 识别为 Bs。但这里是最有趣的部分:上面第(3)步的另一个名字是什么?
…
如果你读过我关于逃避攻击的帖子,那么你将有希望认识到这是对抗训练,这是对抗例子中保护模特最常见的方式。这给我们留下了一个令人不安的结论:通过很好地保护自己免受躲避攻击——你实际上可能会打开中毒的大门。
对一个惊险刺激的人来说怎么样?
泊松分布——直觉、例子和推导
何时使用泊松分布
在设置参数 λ 并将其代入公式之前,我们先暂停一秒钟,问一个问题。
为什么泊松非要发明泊松分布?
为什么会有这种分布?
什么时候应该使用泊松进行建模?
1.泊松为什么会发明泊松分布?
预测未来发生的#事件!
更正式地说,预测给定数量的事件在固定时间间隔内发生的概率。
如果你曾经卖过东西,这个"事件"可以定义为,例如,一个顾客从你这里买了东西(关键时刻,而不仅仅是浏览)。它可以是你的网站一天有多少访客,下个月你的广告有多少点击,你轮班时接到多少电话,甚至是明年有多少人会死于致命疾病,等等。
下面是我在现实生活中如何使用泊松的例子。
Every week, on average, 17 people clap for my blog post. I’d like to predict the # of ppl who would clap next week because I get paid weekly by those numbers.**What is the probability that** **exactly 20 people** (or 10, 30, 50, etc.) **will clap for the blog post next week?**
2.现在,让我们假设我们对泊松分布一无所知。那我们怎么解决这个问题?
解决这个问题的一个方法是从读取次数开始。每个阅读博客的人都有可能真的会喜欢它并鼓掌。
这是二项式分布的经典工作,因为我们正在计算成功事件(拍手)数量的概率。
一个二项随机变量是在 n 次重复试验中成功的次数 x 。我们假设成功的概率 p 在每次试验中都是不变的。
然而,在这里我们只得到一条信息——17 ppl/周,这是一个“比率”(每周成功的平均次数,或 x 的期望值)。我们对鼓掌概率 p 和博客访问量 n 一无所知。
因此,我们需要更多的信息来解决这个问题。我们还需要什么来把这个概率框定为一个二项式问题?我们需要两样东西:成功的概率(鼓掌) p &试验次数(访客) n 。
让我们从过去的数据中得到它们。
The stat of my Medium blog post about Gradient Descent
这些是一年的统计数据。总共有 59k 人阅读了我的博客。59,000 人中,有 888 人鼓掌。
所以每周阅读我博客的人数( n )是 59k/52 = 1134。每周鼓掌的人数( x )是 888/52 =17。
# of **people who read** per week (**n**) = 59k/52 = **1134**# of **people who clap** per week (**x**) = 888/52 = **17**Success probability (**p**) : 888/59k = 0.015 = **1.5%**
使用二项式 PMF ,下周 我将获得恰好 20 次成功(20 人鼓掌)的概率是多少?
<Binomial Probability for different **x**’s>╔══════╦════════════════╗
║ **x** ║ Binomial P(X=**x**)║
╠══════╬════════════════╣
║ 10 ║ 0.02250 ║
║ **17** ║ **0.09701** ║ 🡒 The average rate has the highest P!
║ 20 ║ 0.06962 ║ 🡒 Nice. 20 is also quite Likely!
║ 30 ║ 0.00121 ║
║ 40 ║ < 0.000001 ║ 🡒 Well, I guess I won’t get 40 claps..
╚══════╩════════════════╝
我们刚刚用二项分布解决了这个问题。
那么,泊松是为了什么? 有哪些事只有泊松能做,二项式做不到?
3.二项分布的缺点
a)二项随机变量是“二进制”——0 或 1。
在上面的例子中,我们每周有 17 个人鼓掌。这意味着 17/7 =每天 2.4 人鼓掌,17/(7*24) =每小时 0.1 人鼓掌。
如果我们使用二项式随机变量按小时(0.1 人/小时)对成功概率建模,这意味着大多数小时得到零拍手但是一些小时将得到恰好 1 拍手**。然而,也很有可能某些小时会得到不止一次掌声(2 次,3 次,5 次,等等。)**
二项式的问题是在单位时间内不能包含 1 个以上的事件(在本例中,1 hr 是单位时间)。时间单位只能有 0 或 1 个事件。
那么,把 1 小时分成 60 分钟,把单位时间变小,比如一分钟,怎么样?那么 1 小时可以包含多个事件。(尽管如此,一分钟将包含一个或零个事件。)
我们的问题现在解决了吗?
算是吧。但是如果在那一分钟里,我们得到了多次鼓掌呢?(也就是说,有人在 Twitter 上分享了你的博文,流量在那一分钟激增。)然后呢?我们可以把一分钟分成几秒钟。那么我们的时间单位就变成了秒,同样,一分钟可以包含多个事件。但是这个二进制容器的问题将永远存在于更小的时间单位中。
这个想法是,我们可以通过将一个单位时间分成更小的单元,使二项随机变量处理多个事件。通过使用更小的划分,我们可以使原始单位时间包含多个事件。
数学上,这意味着 n → ∞ 。
因为我们假设速率是固定的,我们必须有 p → 0。因为不然的话, **n*p,**也就是事件数,会炸。
使用极限,单位时间现在是无穷小。我们不再需要担心在同一单位时间内会发生不止一个事件。这就是我们如何得到泊松分布。
b)在二项分布中,试验次数(n)应该事先知道。
如果使用二项式,则不能仅用比率(即 17 ppl/周)来计算成功概率。你需要“更多信息”( n & p )来使用二项式 PMF。
另一方面,泊松分布不需要你知道 n 或者 p,我们假设 n 无限大,p 无限小。泊松分布的唯一参数是率λ (期望值 x )。在现实生活中,只知道费率(即在下午 2 点~ 4 点期间,我接到了 3 个电话)比知道两个 n & p 要常见得多。
4.让我们从二项式 PMF 数学推导泊松公式。
Deriving Poisson from Binomial
现在你知道λ^k 各个组件在哪里了,k!还有 e^-λ是从哪里来的!
最后我们只需要证明前两项的相乘 **n!/((n-k)!当 n 接近无穷大时,**为 1。
是 1。
我们得到了泊松公式!
From https://en.wikipedia.org/wiki/Poisson_distribution
现在维基百科的解释开始有意义了。
把自己的数据代入公式,看看 P(x)对你有没有意义!
下面是我的。
**< Comparison between Binomial & Poisson >**╔══════╦═══════════════════╦═══════════════════════╗
║ **k** ║ Binomial P(X=**k**) ║ Poisson P(X=**k;**λ=**17**) ║
╠══════╬═══════════════════╬═══════════════════════╣
║ 10 ║ 0.02250 ║ 0.02300 ║
║ **17** ║ **0.09701** ║ **0.09628** ║
║ 20 ║ 0.06962 ║ 0.07595 ║
║ 30 ║ 0.00121 ║ 0.00340 ║
║ 40 ║ < 0.000001 ║ < 0.000001 ║
╚══════╩═══════════════════╩═══════════════════════╝* You can calculate both easily here:
Binomial: [https://stattrek.com/online-calculator/binomial.aspx](https://stattrek.com/online-calculator/binomial.aspx)
Poisson : [https://stattrek.com/online-calculator/poisson.aspx](https://stattrek.com/online-calculator/poisson.aspx)
需要注意一些事情:
- 即使泊松分布模拟罕见事件,比率 λ 可以是任何数字。它不一定总是很小。
- 泊松分布是不对称的,它总是向右倾斜。因为它在左边被零发生壁垒(没有“负一”clap 这种东西)抑制,在另一边是无限的。
- 随着 λ 变大,图表看起来更像正态分布。
https://en.wikipedia.org/wiki/Poisson_distribution
4.泊松模型假设
a .单位时间内事件的平均发生率不变。
这意味着每小时访问你博客的人数可能不遵循泊松分布,因为每小时的比率不是恒定的(白天的比率较高,晚上的比率较低)。对消费者/生物数据使用月利率也只是一个近似值,因为季节性效应在该领域并非微不足道。
b .事件是独立的。你的博客访客的到来可能并不总是独立的。例如,有时大量的访问者成群结队而来,因为某个受欢迎的人提到了你的博客,或者你的博客出现在 Medium 的首页,等等。如果一次大地震增加了余震的可能性,一个国家每年的地震次数也可能不遵循泊松分布。
5.泊松分布和指数分布之间的关系
如果单位时间内的事件数量遵循泊松分布,则事件之间的时间量遵循指数分布。泊松分布是离散的,而指数分布是连续的,然而这两种分布密切相关。
基于策略的强化学习,简单的方法
逐步理解强化学习中基于策略的方法
更新 1 :学习和练习强化学习的最好方式是去 http://rl-lab.com
更新 2 :如果你是这个主题的新手,从开发人员强化学习政策文章开始可能更容易。
介绍
假设你在一个新城镇,既没有地图也没有 GPS,你需要到达市中心。你可以试着评估你相对于目的地的当前位置,以及你所采取的每个方向的有效性(价值)。你可以认为这是计算价值函数。或者你可以问一个当地人,他会告诉你直走,当你看到一个喷泉时,你向左走,一直走到市中心。他给了你一个可以遵循的政策。
自然,在这种情况下,遵循给定的策略比自己计算价值函数要简单得多。
在另一个例子中,假设您正在管理库存,并且您决定当每件商品的数量低于某个限制时,您发出一个购买订单来补充您的库存。这是一个比研究客户的活动、他们的购买习惯和偏好要简单得多的策略,以便预测对你的股票的影响…
毫无疑问,值函数将导致确定策略,如前几篇文章中所见,但是还有其他方法可以学习使用参数选择操作的策略,而无需咨询值函数(这不太正确,因为需要值函数来提高准确性)。
因此,主要思想是能够确定在状态**(【s】)**下采取哪种行动,以使回报最大化。
实现这一目标的方法是微调𝜽指出的参数向量,以便为𝜋.政策选择最佳行动
策略记为𝜋(a|s,𝜽) = Pr{At = a | St = s,𝜽t = 𝜽},这意味着策略𝜋是在状态时采取行动 a 的概率,参数为𝜽.
优势
- 更好的收敛特性
- 在高维或连续动作空间有效
当空间很大时,内存的使用和计算消耗增长很快。基于策略的 RL 避免了这一点,因为目标是学习一组远小于空间计数的参数。 - 可以学习随机策略
随机策略比确定性策略更好,尤其是在两人游戏中,如果一个玩家确定性地行动,另一个玩家会制定对策以获胜。
不足之处
- 通常收敛于局部最优而不是全局最优
- 评估策略通常效率低下且差异大
基于策略的 RL 差异很大,但有一些技术可以降低这种差异。
随机政策
首先需要注意的是,随机并不意味着在所有状态下都是随机的,但在某些有意义的状态下,它可能是随机的。
通常报酬最大化会导致确定性政策。但是在某些情况下,确定性策略并不适合这个问题,例如在任何两个玩家的游戏中,确定性的参与意味着另一个玩家将能够采取反制措施以便一直获胜。例如,在石头剪刀布游戏中,如果我们每次都玩确定性的相同形状,那么其他玩家可以很容易地对抗我们的策略并赢得每场游戏。
所以在这个博弈中,最优策略是随机的,比确定性策略好。
蓝图
在深入研究数学和算法的细节之前,了解一下如何进行是很有用的,这是一种蓝图:
- 找出一个可以用来评估政策有效性的目标函数。换句话说,告诉我们政策的效果有多好。
- 定义策略。
我们的意思是列出一些可以在学习过程中使用的有用政策。 - 幼稚的算法。
提出一种直接利用策略来学习参数的算法。 - 改进的算法
寻找改进目标函数的算法,以最大化策略的有效性。
蓝图的第 1 部分:寻找目标函数
请记住,在上面的蓝图中,我们谈到了寻找一个目标函数来评估政策的有效性。在这一节中,我们将定义目标函数及其一些有用的推导。
(关于政策梯度的更多细节可以在文章政策梯度一步一步中找到)。
目标函数
当谈到函数的最大化时,一个突出的方法是梯度。
但是我们如何根据𝜽最大化回报呢?一种方法是找到一个目标函数 J(𝜽)这样
其中,V𝜋𝜽是策略𝜋𝜽的值函数, s0 是起始状态。
简而言之,J(𝜽最大化意味着 V𝜋𝜽(s).最大化由此可见
根据政策梯度定理
其中𝝻(s)是𝜋下的分布(意为遵循政策𝜋时处于状态 s 的概率),q(s,a)是𝜋下的动作值函数,∇𝜋(a|s,𝜽)是𝜋给定 s 和𝜽.的梯度
最后𝝰的意思是成比例的。
所以这个定理说∇J(𝜽)正比于 q 函数的和乘以我们可能处于的状态下所有行为的策略梯度。然而我们不知道𝜋(a|s、𝜽),我们怎么能找到它的梯度呢?
事实证明,如以下演示所示,这是可能的:
提醒: ∫ dx/x = Log(x) 意思是 dx/x = (Log(x))’ = ∇Log(x)
∇Log 𝜋𝜃(s,a) 被称为得分函数。
注意,政策的梯度可以表示为预期。如果你问自己为什么?查看维基百科关于期望值的文章。
参数更新
由于这是一个梯度方法,参数的更新(我们正试图优化)将按通常的方式进行。
蓝图的第 2 部分:定义策略
本节介绍了几种标准梯度策略,如 Softmax 和 Guassian。我们在 RL 算法中使用这些策略来学习参数𝜽.
实际上,每当在 RL 算法中我们看到对 ∇Log 𝜋𝜃(s,a) 的引用时,我们就插入所选策略的公式。
Softmax 策略
softmax 策略由一个将输出转换为概率分布的 softmax 函数组成。这意味着它影响每个可能动作的概率。
Softmax 主要用于离散动作的情况:
因此
在哪里
这里可以查看推导的完整演示。
高斯策略
高斯策略用于连续动作空间的情况,例如当你驾驶一辆汽车时,你转动方向盘或踩下油门踏板,这些都是连续动作,因为这些都不是你做的少数动作,因为你可以(理论上)决定旋转角度或气体流量。
推导变成了
蓝图的第 3 部分:朴素算法
本节将给出一些算法,这些算法将考虑策略和它们的目标函数,以便学习给出最佳代理行为的参数。
强化(蒙特卡罗策略梯度)
该算法使用蒙特卡罗根据策略𝜋𝜃创建剧集,然后对于每个剧集,它迭代该剧集的状态并计算总回报 G(t)。它使用 G(t)和∇Log 𝜋𝜃(s,a(可以是 Softmax 策略或其他)来学习参数𝜃.
from Sutton Barto book: Introduction to Reinforcement Learning
蓝图的第 4 部分:改进的算法
我们已经说过基于策略的 RL 具有很高的方差。然而,有几个算法可以帮助减少这种差异,其中一些是加强基线和演员的批评。
用基线算法增强
基线的概念是从 G(t)中减去称为基线的 b(s ),目的是减少结果的大范围变化。
假设 b(s)不依赖于动作 a,可以证明 ∇J( 𝜽)的方程仍然成立。
所以现在的问题是如何选择 b(s)?
基线的一个选择是计算状态值的估计值,( St,w ),其中 w 是通过蒙特卡罗等方法学习的参数向量。
所以 b(s)=③(St,w)
用基线增强算法变成
演员评论家算法
(详细解释见演员评论家简介篇)
演员评论家算法使用 TD 来计算用作评论家的价值函数。批评家是一个状态值函数。评估事情的进展是很有用的。在每个行动之后,批评家计算新的状态来确定是否有任何改进。这个评估就是 TD 误差:
然后,δ(t)用于调整参数𝜽和 w 。
简而言之,𝜽和 w 都以修正该误差的方式进行调整。
from Sutton Barto book: Introduction to Reinforcement Learning
相关文章
政策梯度渐进
政策梯度方程推导的综合分析
Photo by Kyle Glenn on Unsplash
更新 1 :学习和练习强化学习的最好方式是去 http://rl-lab.com
更新 2 :如果你是这个主题的新手,从开发人员强化学习政策文章开始可能更容易。
概观
在上一篇文章中,我们了解了基于策略的学习,并探讨了这种方法的优缺点。在本文中,我们将深入研究数学细节,以推导出政策梯度的正确方程。
但是在我们开始之前,让我们快速回顾一下为什么政策学习是有用的:
- 策略学习在高维或连续的动作空间中是有效的。
大的状态和动作空间消耗巨大的内存和计算能力。另一方面,学习策略消耗较少,因为我们学习定义策略的一组可管理的参数。 - 可以学习随机政策。
随机策略比确定性策略更好,尤其是在两人游戏中,因为任何确定性行为都将很容易受到反措施的威胁。
数学
我们需要时刻提醒自己我们的目标是什么,因为我们很容易忘记它。
不断对自己说,目标是在每一个强化学习问题中获得最大的回报。
所以从这个基本原则出发,我们定义 J(𝜃为我们在从 0 到 t 集结束的每个时间步中得到的预期回报 R(s,a ),遵循一个𝛑(𝜃).政策
如果我们将这一集定义为一条从 t=0 到 t 的𝞽轨迹,那么预期报酬就是所有可能轨迹的总和,即𝞽根据𝜃被选中的概率,乘以这条轨迹的回报率 R(𝞽).
这使我们得出以下等式:
同样,我们的目标是找到𝜃的一组参数,使 J(𝜃)(which 的预期回报最大化。
从微积分我们知道,为了找到一个函数的极值(最大值或最小值),我们要计算它的导数。
我们将在下面的一组等式中完成:
注意上面的技巧,我们用𝜃的 P(𝞽来乘除(这是合法的,因为𝜃的 p(𝞽= 1)。这个技巧的目的是达到一个术语𝛻P(𝞽,𝜃)/P(𝞽,𝜃),因为∫df/f .dx= log(f(x)),因此 log(f(x))的导数是 df/f。由此我们发现𝛻log P(𝞽,𝜃 ) = 𝛻P(𝞽,𝜃)/P(𝞽,𝜃).我们神奇地得到了下面的等式。
然而,由于我们实际上不能计算每一个可能的轨迹,𝞽,we 将后退以获得数量减少的(m)个轨迹。在这里我们也可以看到𝜃的 P(𝞽(日志前的那个)神奇地消失了。原因是,当我们选择了(m)个轨迹时,就不再有选择它们的可能性,因为它们已经被选择了。
我们以这个等式结束:
请注意,我们从期望值(所有可能的轨迹)移动到(m)个选定轨迹的平均值。
现在让我们单独关注𝛻logp(𝞽(𝜃)。轨迹被遵循的概率等于当遵循某个策略𝛑(𝜃).时,该轨迹的每一步到达的概率的乘积
下面一行显示 log P(𝞽,𝜃)转化为 P(S(t+1)|St,at)的乘积。𝛑(𝜃)
直觉上,我们说我们遵循了一个轨迹,当我们通过了它的所有步骤。所以沿着它走到底的概率,等于我们经过这条轨迹每一步的概率的乘积。
我们知道乘积的对数等于对数之和:
上述方程组的第二行显示,我们将σlog(p.𝛑进一步分解为σ(log§+log(𝛑),然后是σlog§+σlog(𝛑).
当对𝜃应用这两项的导数时,𝛻σlog(p 消失,因为 p 不依赖于𝜃.
既然我们已经发现𝛻log P(𝞽,𝜃)=𝛻σlog(𝛑(a|s,𝜃,我们可以把它放回初始方程,并检索最终结果,如下所示:
结论
正如我们已经看到的,推导政策梯度是非常优雅和智能的程序。完全掌握这种推导的本质是至关重要的,因为它是许多众所周知的基于政策的方法的基础,例如演员-评论家。
相关文章
人工智能社会契约的政策处方
The Leader of the Luddites, Published in May 1812 by Walker & Knight, Sweetings Alley, Royal Exchange, Copyright Expired
在我最近的故事“人工智能经济的新世界秩序”中,我提出未来人工智能经济的成功将在很大程度上取决于每个国家人工智能劳动力、人工智能基础设施以及最终人工智能社会契约的创造。在这个原始故事中,我提出了一些高层次的政策想法作为讨论的开始,但很明显,人们渴望更深入地研究人工智能社会结构的想法以及实现它的具体政策想法。
为什么是人工智能社会契约?
人工智能和工作场所自动化的采用已经在改变我们的工作方式和我们需要的劳动力。根据《2019 年世界发展报告》(图 1),美国近一半的工作岗位预计将实现自动化。
Figure 1: WDR 2019: The Changing Nature of Work© World Bank http://www.worldbank.org/en/publication/wdr2019 License: Creative Commons Attribution license (CC BY 3.0 IGO)
工人的流离失所导致了技术抵制,19 世纪英格兰卢德运动的经典例子就是工人摧毁纺织机械,这被视为夺走工人的生计。根据 Forrester 的 Craig Le Clair 在最近的金融时报文章中的预测,随着人工智能在“立方体”中自动处理白领工作的应用迅速增长,预计到 2022 年将达到 480 亿美元以上,我们可以预计未来将出现勒德派式的动荡。
虽然自动化的历史告诉我们,每一次新的技术进步都会增加总就业,但新的工作岗位往往会流向不同的工人阶层、技能水平等。据估计,到 2030 年,7500 万到 3 . 75 亿工人需要改变就业类别或重新获得技能(来源:麦肯锡全球研究所)。
这种从人到“数字工人”、物理机器人和 RPA 机器人的工作快速转移,使得新的“人工智能社会契约”成为必要,以保持创新,同时为受到工作场所演变影响的工人提供服务。
人工智能社会契约政策
第一要务:再培训、技能提升和教育:自动化的早期影响将主要集中在中低技能劳动力身上。包括送货、分拣、驾驶、提升和检查在内的工作将是自动化的早期目标。新美洲的工作为自动化换挡表明,居住在城市以外的工人在面对自动化时面临更大的困难。
- 人工智能优化的职业教育:人工智能教育的大部分重点是高等数学和科学。人工智能社会契约将需要职业教育,为新兴领域的工人提供装备:先进制造、机电一体化、机器人服务和维修等。非政府组织和社区大学需要资金来继续和发展像西雅图友好社的青年航天计划这样的项目。
- 将年轻工人从衰退的行业和工作职能中分流出来至关重要。对衰落行业中完全就业的个人进行再培训和工作变动激励,将节省政府用于再培训年龄较大、灵活性较差的工人的长期支出。政策规定雇主雇用职业生涯晚期的工人来填补目前的空缺,这在这一领域有所帮助,并解决一些被解雇的老年工人的就业需求。
- 中年工人的再培训和技能提升面临着最大的挑战,因为他们已经工作了很多年,不太能灵活地改变行业,而且经常会选择与他们过去的就业相关的培训选项,从而增加了裁员的风险。一个关键的成功因素是接受职业评估,以帮助失业工人做出再培训决定(来源:大西洋)。
优先事项 2: 如今,只有 48%的美国人支持为自动化失业工人提供基本收入的想法,而几乎相同比例的工人面临机器人失业的风险(来源:东北大学/盖洛普调查,2017 年 9 月 15 日至 10 月 10 日)。随着自动化对工人的影响越来越清晰,有理由假设公众支持将达到多元化。随着公众支持的增加,决策者在执行这些政策时需要考虑以下因素:
- 有保障的收入计划将需要保留给那些无法重新获得技能的工人
- 保证收入计划的参与者应该优先作为被分流的年轻工人的替补
- 这些计划应该有时间限制,以使该计划对接近社会保障和医疗保险资格的工人更有吸引力
- 这些计划将需要提供医疗补助资格作为权宜之计,直到工人及其家人达到标准的医疗保险资格
政治巨蟒
用 Scrapy 刮国会文件
No, not that kind of python.
2020 年民主党总统候选人将在周三开始的辩论中交锋。他们中的许多人是现任或前任国会议员。他们都在竞争领导这个国家。
作为选民,如果我们有他们在政治生涯中在参议院或众议院所说的每一句话的记录,那不是很棒吗?
作为数据科学家,难道我们不想提取他们的话,分析他们,并用他们来判断或预测这些国会议员作为总统候选人吗?
用 Scrapy 造一只蜘蛛
是的,我们可以。宪法要求国会记录其议事过程。因此,政府出版办公室印刷(并以数字形式发布)国会记录,其中包含立法机构两院的日常正式会议记录,包括:
- 住房部分
- 参议院部分
- 评论的延伸(演讲、颂词、证词和立法史)
- 每日文摘
Congressional Record, November 17, 2008.
尽管这些数据存在于公共领域,但将它们从网站上取出并转换成可用的格式却是一个挑战。在无法访问昂贵的法律数据库的情况下,网络搜集是有事业心的公众的最佳选择,Scrapy 使得快速获得大量信息变得相对容易。
蜘蛛去了华盛顿
Scrapy 允许使用 python 进行异步 web 抓取。您可以使用它通过 API 提取数据,将它与 BeautifulSoup 和 Selenium 集成,并像蜘蛛网有细丝一样扩展它的功能。
Scrapy 的核心想法是复制 Django 一个“不要重复自己”的框架,这意味着它提供了一种重用代码和轻松将项目扩大到更大范围的方法。爬行器的组成部分,如项目、中间件、管道和设置,存在于单独的脚本中,爬行不同网站的多个“蜘蛛”可以在同一个“项目”中使用它们。
蜘蛛本身自然依赖于面向对象的编程(每个都属于“蜘蛛”类):
A Scrapy spider.
重要组件包括:
- 开始 URL
- 你从回答中提取了什么,如何提取
- 返回给解析函数的内容
在 Python 中使用 XPath
Scrapy 最有用的功能之一是 Scrapy shell,它允许你实时探索你正在抓取的网站,以测试你的假设。本质上,在部署代码之前,您可以在沙盒模式下尝试您的代码(并发现它不起作用)。
当您在处理像 XPath 这样复杂的东西时,这个附加功能极大地减少了深入网站结构提取所需内容的时间和挫折感。例如,我需要从 Congress.gov 网站的 HTML 中的特定元素获取部分 URL 和文本。Scrapy shell 允许我在将结果语法复制并粘贴到代码中之前,确保我的 XPath 语法不会返回空列表。
Is it worth auto-generating XPath?
**关于从 DevTools 中“复制 XPath”的说明:**虽然您可能觉得在 DevTools 中右键单击 HTML 元素并从出现的菜单中选择“复制 XPath”很吸引人,但是不要屈服于诱惑。如果像国会一样,你的网站是用表格组织的,你将无法检索到你想要的一切。就我而言,我想要的 pdf 文件位于页面的最右边一栏:
Congress.gov website
单击“复制 XPath”会显示以下信息:
//*[@id="browse_expanded_panel_1"]/div/table/tbody/tr[1]/td[6]/a
这只是描述一个表内的位置。
下面是 shell 中返回的内容:
An empty list
下面是我的实际的 XPath 表达式:
response.xpath('//td/a[@target="_blank"]/@href').extract()
这是它返回给定页面的内容:
PDFs that exist
您必须学会使用 XPath 来选择有意义的内容,而不是依赖于自动生成的表达式,后者——很像正在讨论的政府机构——通常表功能有利于形式。
使用 Tika 提取文本
一旦我写了这个蜘蛛,它很快就把 25 年的国会记录下载到了我的硬盘上。后来没有太多额外的代码,我已经用处理 pdf 的 python 库 Tika 提取了文本。
我创建了一个正则表达式来拆分个人发言:
Preliminary NLP pre-processing
尝试了一些非常初步的情感分析:
Preliminary NLP with VADER
议长先生,交出你的秘密
既然他们的话已经暴露无遗,国会下一步该怎么办?分析与数据挖掘,自然(语言处理)ly。尽管人们对 Twitter 争论不休,但几乎没有人对参议员和众议员在国会发言时所说的话提出质疑,无论是因为获取数据的难度、训练模型的难度、缺乏法律素养,还是上述所有原因。但借助适当的工具和领域知识,我希望在 2020 年选举季之前提供一些有价值的见解。
原载于https://www.espritdecorpus.com。
民意测验和新闻
探索新闻报道与民意测验表现之间的关系
候选人投票是政治竞选过程的重要组成部分。投票结果受到选民、记者,当然还有候选人及其竞选团队的密切关注。民调排名的上升有助于竞选造势,巩固支持,刺激筹款。减少会产生相反的效果。
在 2019 年 5 月撰写这篇文章时,美国正处于 2020 年总统大选的早期阶段,已经有大约 20 名民主党候选人争夺本党提名,并面对可能的共和党提名人唐纳德·特朗普。理解投票驱动因素是这些候选人、他们的支持者和反对者以及中立观察者的浓厚兴趣。
一个驱动因素是媒体报道,在这篇文章中,我通过研究 2016 年共和党总统初选来探索新闻报道和民调结果之间的关系。这场初选与今天的民主党竞选有一些相似之处,特别是在候选人众多的领域,它可能会成为有用见解的来源。
这种方法是建立一个由 2015 年和 2016 年的民调结果和关键新闻报道指标和主题组成的数据集,在这段时间里,初选过程将一个巨大的候选人场削减到一个获胜者唐纳德·特朗普(Donald Trump)。利用这些数据,我们可以探索关系。
数据
为了进行这种分析,需要民意测验和新闻报道数据。这些数据来自两个不同的来源,FiveThirtyEight 和全球事件、语言和语调数据或 GDELT 项目。
FiveThirtyEight 是一项分析服务,拥有一个受欢迎的网站,提供政治、体育、科学和健康、经济和文化的定量和统计分析。
[## 五三八
内特·西尔弗的《FiveThirtyEight》使用统计分析——硬数字——讲述关于选举的引人入胜的故事…
fivethirtyeight.com](https://fivethirtyeight.com/)
2015/2016 年共和党总统初选的民调数据是使用基于 Selenium 和 Beautiful Soup 工具构建的网络抓取对象从一个特定的 538页面获得的。它们涵盖了 2015 年 1 月 25 日至 2016 年 5 月 3 日之间的 670 项民意调查,结果涉及 11 位候选人(唐纳德·川普、约翰·卡西奇、特德·克鲁兹、马尔科·卢比奥、本·卡森、杰布·布什、克里斯·克里斯蒂、卡莉·菲奥莉娜、里克·桑托勒姆、兰德·保罗和迈克·哈克比)。
GDELT 由 Google Jigsaw 支持,是一个监控、存储和提供“来自几乎每个国家每个角落的世界广播、印刷和网络新闻”的项目。GDELT 管理几个存储库,但是对于这个项目,数据是从全球知识图第二版(GKG V2)数据库中提取的。具体而言,针对与主要候选人相关的文章,获取了文件计数、语气、正负分数、极性、活动参考密度和自我参考密度的 GDELT CKG 值以及文章主题汇编。
GDELT 项目
哥德尔 Projectwww.gdeltproject.org](https://www.gdeltproject.org/)
GDELT 数据可以通过文本 API 获得,并作为谷歌 BigQuery 大数据服务上的公共数据集。对于这项工作,我使用 BigQuery 并通过 Google 提供的 Python API 访问数据库。CKG V2 非常大,有 9.85 万亿字节(TB)和大约 9.35 亿行。处理如此大的数据集意味着我首先必须将相关的新闻数据写入另一个小得多的 BigQuery 表,然后查询它以在 Python 的 pandas 库中构建建模数据。这个较小的表大约有 540 万行,其中的行代表在投票期间与上述候选人相关的 CKG·V2 条目。
形象化
有 11 个候选人和一系列功能,有许多可视化图表可能会揭示有趣的关系。从对投票结果的纵向观察和对单个候选人的媒体报道度量开始可能是最有用的。例如,我们可以查看特德·克鲁兹和川普的投票结果和文章数量。
文章计数显示了一个有趣的模式,在活动的早期覆盖率相对较低,然后在最后几个月迅速增加。民意测验结果有趋势成分,似乎显示出变化。实际上,在相当长的时间内,它们大多围绕趋势线波动(换句话说,它们围绕同一个值波动)。
这也有助于横向比较候选人之间的相似度量。在接下来的两个图表中,我们看到了所有候选人的平均投票结果与文章数量的对比,以及平均投票结果与文章语气平均值的对比。
第一张图中各点的排列证实了一个直观的观察结果,即较高的民调数字与较大的媒体报道相关联。
GDELT 为每份 CKG·V2 文件获取了几个指标,包括对其平均“语气”的测量声调被计算为正分数和负分数之间的差,正分数和负分数分别代表正单词和负单词的百分比。在下图中,我们检查了平均投票结果和平均音调之间的关系。
这里没有明显的模式,但有趣的是,特朗普在这个尺度上是一个值得注意的异数,平均语气最低,但平均民调最高。
GDELT 还跟踪每个文档的极性。它测量音调词典中匹配单词的百分比,作为文本“情绪极化或充满激情”程度的指标更高的值与更大的极化相关联。相对于极性平均值的平均值绘制平均投票结果揭示了沿极性维度的有趣分组。保罗、克鲁兹、哈克比和川普属于媒体高度分化群体。
建模
一个自然的问题是,这些新闻数据能预测未来的民意测验表现吗?为了解决这个问题,我构建了一个分类模型框架,其中目标变量是未来民意测验中的表现,解释特征是从 GDELT 收集的新闻数据。
目标变量包括三类,下降、持平和上升,对应于特定候选人的投票结果在两次感兴趣的投票之间是下降、保持持平还是上升。在我的分析中,我对下一轮投票和第三轮投票的性能进行了建模,但是可以检查任意数量的投票周期。解释性特征包括 GDELT 的文档计数、度量以及文档主题列表。
事实证明,使用这些数据来创建未来投票表现的预测模型具有挑战性。总的来说,这项分析发现,在一个广泛的候选人领域中平均的广泛媒体指标在预测即将到来的民意调查中的表现方面没有什么力量。这些模型的精度并不比下行、平坦和上行观测的基线分布好多少。
研究了几种模型配置,包括预测未来投票(在这种情况下是第三次投票)中的表现,以及将数据集限制为单个候选人。这些模型显示了改进的准确性,并表明这种方法可以产生结果。需要做更多的工作。
更多细节和未来的工作
对于那些感兴趣的人,你可以在我的 GitHub 库中找到更多信息,包括这个项目的所有建模工作。
如果读者有兴趣,我可以将分析扩展到 2019/2020 民主党总统初选,看看从这场比赛的早期观察中可以收集到什么观察结果。让我知道。
基于时间序列和 LSTM 的 MXnet 污染预测
Photo by Jake Hills on Unsplash
时间序列
时间序列分析是一种处理时间序列数据或趋势分析的统计技术。时间序列数据是指数据在一系列特定的时间段或间隔内。
TSA(时间序列分析)应用:
- 模式识别
- 地震预报
- 天气预报
- 金融统计
还有更多…
MXnet
Apache MXNet(孵化)是一个深度学习框架,旨在提高效率和灵活性。它允许你混合符号和命令式编程来最大化效率和生产力。MXNet 的核心包含一个动态依赖调度器,它可以动态地自动并行化符号操作和命令操作。在此之上的图形优化层使得符号执行速度更快,内存效率更高。MXNet 是可移植的、轻量级的,可以有效地扩展到多个 GPU 和多台机器。
问题
在中国这个世界上人口最多的国家,随着新兴精英阶层的崛起和超过 10%的经济增长率,环境问题已经退居其次。日益严重的污染导致了无法使用的水道,出生缺陷的增加,以及一些地球上最脏的空气。它是如此的令人讨厌,以至于现在有一个词来形容它:“smogpocalypse”。
我们开始吧!
密码
这里用到的所有代码都可以作为笔记本使用。可以直接在 google colab 上运行。
我们从安装基本组件和依赖项开始我们的笔记本。
在本教程中,我们将使用北京 PM2.5 数据集来训练和预测污染水平( PM2.5 )
使用 wget 直接下载数据集:
导入所需模块:
用熊猫读数据:
小数据处理 —
大量的可视化和特征工程超出了本教程的范围,但是我们仍然需要做一些事情来继续。
- 下降 Nan 值。
- 标签编码风向( cbwd )
我们数据集的快速可视化:
我们定义了一个将数据转换为数据序列的函数:
多一点数据预处理
- 将数据集值中的值缩放至[0,1]
- 通过将我们的数据传递给上面定义的
make_dataset_many_to_one
来创建新的数据集。在这里,我们选择每 24 小时做一次记录。
按如下方式重塑数据:
- 样品。一个序列就是一个样本。一个批次由一个或多个样本组成。
- 时间步长。一个时间步长是样本中的一个观察点。
- 特点。一个特征是一个时间步长的一个观察值。
将 numpy 数组转换成mxnet.nd.array
:
定义 LSTM 神经网络:
接下来,我们需要将模型初始化为一个设备。(CPU 或 GPU)
现在,我们需要定义训练参数。更多
这里,loss_function
用于训练期间,mse
用于计算每个历元后的损失。
主训练循环:
损失应该随着每个时期变得更低,我们为 15 个时期训练我们的模型。
对整个数据集进行预测:
将缩放后的预测值转换为实际大小,并将其存储在数据框中:
将预测可视化:
正如你所看到的,我们的模型在预测明天的污染水平方面做得很好。
希望这篇文章对你的探索有所帮助。
新的 Jupyter?
围绕网飞的多语言笔记本 Polynote 有很多议论。但这是木星杀手,还是所有的炒作?
Source: https://polynote.org/
如果你在过去几年中使用 Python 做过任何类型的分析工作,你很有可能会遇到 Jupyter notebooks。无需重新运行整个脚本即可执行新代码的能力使笔记本成为分析工作流程中无处不在的一部分,但 Jupyter 笔记本并非没有问题。例如,不支持代码编辑、提示和林挺;这可能导致代码混乱、难以调试,并导致一些开发人员完全避免使用该工具。2019 年,科技巨头网飞开源了他们的笔记本工具, Polynote,,许多人将其称为 Jupyter-killer。
像 Jupyter 一样,Polynote 也支持多语言,但是 Scala 已经作为“一级语言”包含在 Polynote 中,另外还支持 Python (+ Spark)、SQL 和 Vega。虽然 Jupyter 允许您用一种受支持的语言(JUlia、PYThon、R)创建一个笔记本,但是 Polynote 在一个笔记本中提供了多语言支持,甚至允许不同语言执行的代码之间的互操作性。
装置
与 Jupyter 不同,Polynote 的核心是基于 JVM 的语言,这使得安装过程比pip install polynote
稍微复杂一些。问题是,Polynote 目前只能在 Linux 和 OSX 上使用;有一些关于包含 Windows 的讨论,但是这不是开发团队的目标。鉴于像 Swift 这样的流行工具的开源 Windows 移植已经失败,期待 Polynote 在 Windows 上的无缝体验是愚蠢的。
也就是说,让我们假设您有一台 Linux 或 OSX 机器。我将概述我让 Polynote 在 Ubuntu 18.04 上工作的步骤。
- 转到版本并在最新版本的“资产”下下载最新的
polynote-dist.tar.gz
- 用
tar -zxvpf polynote-dist.tar.gz
提取多项式 - 安装 Java:
sudo apt install default-jre
和sudo apt install default-jdk
- 用
export JAVA_HOME=/usr/lib/jvm/default-java/
设置 JAVA_HOME pip3 install jep jedi virtualenv
火花支持需要额外安装。你可以在这里阅读更多关于安装 Polynote 的信息。
进入 polynote 目录并执行polynote.py
,然后在浏览器中导航到提供的本地地址(我已经在 Firefox 和 Chrome 上测试过,没有问题)。
多语言笔记本
Polynote 的同名特性是语言互操作性:如果用一种语言定义了一个变量,就可以在另一种语言中使用它。这显然有局限性,因为不是所有的语言都有相同的类型和对象概念,但是最重要的类型,包括 numpy 数组和 pandas 数据帧,都是受支持的。
我必须承认,我对 Scala 只有一知半解,所以我不确定需要它与 Python 协同工作的具体用例,但很明显,消除在两种语言之间转换数据的需要将使这样一个项目运行得更加顺畅。
我参与的大多数项目都只需要一种语言(大部分是 Python,一些是 R ),所以很容易相信,在大型项目之外,语言混合对于大多数数据科学家来说是多余的工具。当然,作为一个开源项目,社区很可能会扩展所支持的语言套件(已经有人对 R、 Julia 和 Java 感兴趣),这将使 Polynote 成为对大量开发人员更有吸引力的工具;就我个人而言,我希望能够在 R 中对 Python 数据进行统计测试,而不是花费时间去寻找一个有效的、可维护的 Python 测试端口。
(英)可视化(= visualization)
数据可视化是数据科学的重要组成部分,但它是一项经常被忽视的技能。Polynote 通过从 Scala 数据生成 Vega 规范来强调高质量的可视化。
Vega 是一种可视化语法,一种用于创建、保存和共享交互式可视化设计的声明性语言。使用 Vega,您可以用 JSON 格式描述可视化的视觉外观和交互行为,并使用 Canvas 或 SVG 生成基于 web 的视图。—https://vega.github.io/vega/
织女星可以很容易地产生美观、复杂的视觉效果。流行的 Python 可视化库牛郎星建立在织女星的统计可视化部分 Vega-lite 之上(你可以在这里阅读关于在牛郎星中生成交互式地图的内容)。
不幸的是,没有对 Python 数据可视化的直接支持,所以这对 Python 原生程序没有什么好处。当然,您仍然可以直接进入模式,但是在大多数情况下,使用 Altair 这样的工具会容易得多。
An interactive bar chart? Try producing that in matplotlib
代码编辑
Jupyter 笔记本的一个很大的缺点是代码非常不透明:没有错误代码的可视提示,这在任何像样的 IDE 中都可以找到,也没有函数的代码补全(这导致我的 API 使用量显著增加)。然而,Polynote 在脚本和笔记本之间架起了一座桥梁。
With Polynote, there is no need to memorise swathes of functions
Scala 中的类型错误会加下划线,但是 Polynote 不关心 Python 类型提示(很像 Python 社区)。
Polynote 不与最好的代码编辑器竞争,但在这方面,它是 Jupyter 笔记本的巨大改进。
摘要
我对 Polynote 的可能性很感兴趣。在数据科学社区中有很多讨论,内置的 Vega 绘图生成和跨语言通信等功能听起来像是游戏改变者。然而,我不认为 Polynote 提供的东西足以吸引大多数数据科学家离开 Jupyter。首先,缺乏对 Windows 的支持使许多人无法使用它;Windows 是最流行的操作系统,许多企业和组织都强制要求使用它。其次,尽管 Scala 有它的好处,但它并不是日常数据科学中广泛使用的语言。大多数分析项目可以并且已经完全可以单独用 Python 实现,所以语言混合和内置 Vega 没有任何好处。
Polynote 的代码编辑是 Jupyter 的一大改进,我希望 Jupyter 在这方面有所改进。然而,作为一个更成熟和更积极开发的项目,我发现 Jupyter 是两者中更强的一个。Polynote 当然值得一看。只是需要多一点时间。
美国有线电视新闻网预测狗是不是狮子狗
用 Keras 构建二进制卷积神经网络分类器
我想把我的迷你狮子狗放在柱子上。因此,我们将建立一个 CNN 来检测照片中是否包含狮子狗(而不是另一种狗),而不是一个对数字图像进行分类的无聊教程。
我称之为:狮子狗不是狮子狗。
我发现了一个 [Kaggle](# https://www.kaggle.com/c/dog-breed-identification/data) 数据集,包含大约 10k 张狗的照片,按品种分类。下载并保存在与笔记本相同的目录中。
附带的 CSV 包含从品种到照片名称的映射。用熊猫进口。
import pandas as pd
df = pd.read_csv('dog-breed-identification/labels.csv')
df.head(3)
现在,通过计算每个品种的数量,并按降序排列,了解不同品种之间的分布情况。熊猫使这变得容易。
grouped_df = df.groupby('breed').count()
grouped_df.sort_values('id', ascending=False, inplace=True)
grouped_df.head(3)
太好了。但是我对狮子狗感兴趣。里面有多少只贵宾犬的例子?我从 Kaggle 上的数据集了解到,这里有 3 个不同的狮子狗品种。
POODLES = ['miniature_poodle', 'standard_poodle', 'toy_poodle']# count examples per breed
poodle_grouped_df = grouped_df.loc[POODLES]# add total for all poodles
poodle_grouped_df.loc['Total',:]= poodle_grouped_df.sum(axis=0)
poodle_grouped_df
很好。接下来,我们将构建一个分类器来检测微型狮子狗。我们还将挑选一种其他特定品种的狗(而不是混合品种),以方便 CNN 的采访。我们必须训练的每个品种的数据量并不多!
创建 2 个数据框,贵宾犬和非贵宾犬。
poodle_df = df.loc[df['breed'].isin(['miniature_poodle'])]non_poodle_df = df.loc[df['breed'].isin(['bernese_mountain_dog'])]
poodle_df
= >包含迷你贵宾犬。
non_poodle_df
= >包含伯恩山犬。
定义一个函数来构建下载数据集中图像的路径。
import osdef build_path_for_dataset(dframe):
train_file_names = os.listdir('dog-breed-identification/train') DIR = 'dog-breed-identification/train/' paths = []
for idx, row in dframe.iterrows():
file_name = row['id']+ '.jpg' if file_name in train_file_names:
paths.append(DIR + file_name) return paths
为每个数据集生成路径。
poodle_df['path'] = build_path_for_dataset(poodle_df)
poodle_df.head(3)
non_poodle_df['path'] = build_path_for_dataset(non_poodle_df)
non_poodle_df.head(3)
完美。现在让我们为每个表添加一个布尔列,指定图片是否是狮子狗的。
poodle_df['is_poodle'] = 1
poodle_df.head(3)
non_poodle_df['is_poodle'] = 0
non_poodle_df.head(3)
组合两个数据帧(现在我们有一个布尔列来区分例子)。
combined_df = pd.concat([poodle_df, non_poodle_df], ignore_index=True)
把它们排好,这样所有的狮子狗就不会在最上面或者最下面。通过使用frac=1
,我们实际上是在抽取一个包括全部人口的样本,并且在这个过程中,改变顺序。
shuffled_combined_df = combined_df.sample(frac=1)
再次将数据帧分割成 2 个数据帧。但是这一次,一个是训练集,另一个是测试集。这里我们使用的是 70/30 分割。
example_count = len(shuffled_combined_df)
break_point = int(0.7 * example_count)train_df = shuffled_combined_df[:break_point]
test_df = shuffled_combined_df[break_point:]
差不多到了有趣的部分。
这些图像的大小都不一样,所以让我们看看图像的平均大小。
from PIL import Imagewidths = []
heights = []for idx, row in shuffled_combined_df.iterrows():
path = row['path']
im = Image.open(path)
width, height = im.size
widths.append(width)
heights.append(height)avg_width = int(sum(widths) / len(widths))
avg_height = int(sum(heights) / len(heights))print(avg_width, avg_height)#=> 448 385
平均宽度为448
,平均高度为385
。
使宽度和高度相等会使以后的变换更简单。因此,我们将图像的大小调整为 300x300。
# we'll use this in a minute
WIDTH = 300
HEIGHT = 300
编写一个函数来:
-加载一个图像
-转换为灰度
-调整大小为 300x300
from keras.preprocessing.image import load_imgdef load_resize_color_image(path): # load image
image = load_img(path) # convert to greyscale
image = image.convert('L') # resize
# 'Image.ANTIALIAS' minimizes distortion when resizing
image = image.resize((WIDTH,HEIGHT), Image.ANTIALIAS) return image
另一个函数为每个传入的图像创建 2 个转换。
from keras.preprocessing.image import ImageDataGenerator
from numpy import expand_dimsdef random_transform_image(image):
augmentations = []
samples = expand_dims(image, 0)
datagen = ImageDataGenerator(rotation_range=90)
it = datagen.flow(samples, batch_size=1) for i in range(2):
batch = it.next() # convert to unsigned integers
augmentations.append(batch[0].astype('uint8')) return augmentations
上面的函数获取一幅图像,并通过随机旋转创建两幅图像。然后我们将完全忽略传入的图像,只训练转换。
训练图像的变换在使用 CNN 时很重要,因为默认情况下它们不是旋转不变的。也就是说,一个直立的图像和同一个图像翻转 90 度,对你的模型来说可能看起来完全不同。所以这两方面的训练都很有帮助。
生成我们的训练数据。
X_train = []
y_train = []from keras.preprocessing.image import img_to_arrayfor idx, row in train_df.iterrows():
path = row['path']
is_poodle = row['is_poodle'] image = load_resize_color_image(path) image = img_to_array(image)
images = random_transform_image(image) for i in images:
image = i.tolist() X_train.append(i)
y_train.append(is_poodle)
生成测试数据,我们不会为此生成额外的转换。
X_test = []
y_test = []for idx, row in test_df.iterrows():
path = row['path']
is_poodle = row['is_poodle'] image = load_resize_color_image(path) image = img_to_array(image) image = image.tolist()
X_test.append(image)
y_test.append(is_poodle)
将我们的特征转换为 numpy 数组。
X_train = np.array(X_train, dtype=np.uint8)
X_test = np.array(X_test, dtype=np.uint8)
二进制化我们的标签。
y_train = label_binarize(y_train, neg_label=0, pos_label=1, classes=[0,1])
y_train = np.hstack((1 - y_train, y_train))y_test = label_binarize(y_test, neg_label=0, pos_label=1, classes=[0,1])
y_test = np.hstack((1 - y_test, y_test))
构建模型。
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.layers. normalization import BatchNormalizationmodel = Sequential()model.add(Conv2D(32, kernel_size = (3, 3), activation='relu', input_shape=(300, 300, 1)))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())model.add(Conv2D(64, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())model.add(Conv2D(128, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())model.add(Dropout(0.2))
model.add(Flatten())model.add(Dense(128, activation='relu'))
model.add(Dense(2, activation = 'softmax'))
这是一个非常典型的模型,卷积层的大小会增加。
编译模型。
model.compile(loss='binary_crossentropy', optimizer='adam', metrics = ['accuracy'])
跑起来!
for i in range(10): model.fit(X_train, y_train, batch_size=30, epochs=1)
loss, acc = model.evaluate(X_test, y_test, verbose = 0)
print(acc * 100)
考虑到少量的训练数据,这实际上比我预期的要好。虽然我们只选择了两个品种的狗,这让我们自己变得容易多了。
为了好玩,我们会把我给你看的狮子狗的照片分类放在最上面。
X_me = []
y_me = []path = 'my_poodle.jpg'image = load_resize_color_image(path)
image = img_to_array(image)
image = image.tolist()X_me.append(image)
y_me.append(1)X_me = np.array(X_me, dtype=np.uint8)y_me = label_binarize(y_me, neg_label=0, pos_label=1, classes=[0,1])
y_me = np.hstack((1 - y_me, y_me))loss, acc = model.evaluate(X_me, y_me, verbose = 0)
print(acc * 100)
成功!!
我们能做得更好吗?当然啦!更多不同类型的变换,如翻转、亮度和缩放可能会有所帮助。但最重要的是,更多的数据。
尽管我们应该对目前的结果感到满意。
人口金字塔动画
RStudio 中 ganimate 的简短入门
Photo by CHUTTERSNAP on Unsplash
可视化是展示在数据中发现的洞察力的一种极好的方式。如果一张图片能表达一千个单词,那么一部动画就能表达一百万个单词。如果使用得当,动画可以非常清晰地展示我们从数据中提取的智慧财富。为了让决策者注意到它的真正价值,他们必须能够将提供给他们的信息内在化。我们可以通过讲述更具视觉吸引力的故事来帮助他们建立联系。
在这个简短的教程中,我将向您展示如何在 RStudio 中制作加拿大 1960 年至 2018 年的动画人口金字塔。你可以把你学到的东西应用到其他动画项目中。我们开始吧!
让我们从安装和加载必要的包开始。
- 吃豆人:一次装载所有的包裹
- tidyverse :准备数据帧( tidyr ),轻松操作数据帧( dplyr , stringr ),生成绘图( ggplot2 )
- gg pol:gg plot 2 的附加特性
- gganimate :图形动画(这就是你来这里的目的!)
- gifski :将文件渲染成 GIF 格式
- kableExtra:创建视觉上吸引人的表格
if(!require('pacman')) install.packages('pacman')
pacman::p_load(tidyverse, ggpol, gganimate, gifski, kableExtra)
加拿大的人口数据来自世界银行网站。时间跨度从 1960 年到 2018 年**。数据帧最初是在宽格式 t 中,由代表每个年龄组和性别的 34 条记录构成。数据保存为 csv 文件并导入到 RStudio IDE 中。要跟进,你可以在这里 下载数据集 。**
CAPop <- read.csv('CAPop.csv')
大部分工作都是准备数据,以便在动画中使用。步骤如下:
- 移除数据框底部的 NA 值
- 为年龄组创建列
- 更改最后一个年龄组的值
- 为性别创建一列
- 对因子等级重新排序(对显示的顺序数据很重要)
- 移除未使用的柱****
- 从宽格式变为长格式****
- 从年份中删除前缀
- 将人口金字塔的所有男性流行值更改为负****
CAPop <- CAPop %>%
na.omit %>%
mutate(Age = substr(Series.Name, 17, 21),
Age = as.factor(case_when(Age == '80 an' ~ '80+', TRUE ~ Age)),
Gender = as.factor(ifelse(str_detect(Series.Name, 'female$') == TRUE, 'Female', 'Male')),
Gender = factor(Gender, levels = c('Male', 'Female'))) %>%
select(-Country.Name, - Series.Name) %>%
gather(Year, Pop, X1960:X2018) %>%
mutate(Year = as.integer(substr(Year, 2, 5)),
Pop = ifelse(Gender == 'Male', as.integer(Pop * -1), as.integer(Pop)))
处理完数据后,我们剩下 2006 行和 4 列。让我们看一看我们在做什么。
dim(CAPop)kable(head(CAPop)) %>%
kable_styling(bootstrap_options = 'striped', font_size = 12, full_width = FALSE)
接下来,我们在 3 个不同的步骤中创建动画**😗*
- 创建静态图****
- 应用动画参数
- ****渲染到文件
静态情节
每个动画都是从一张图片开始的。首先,通过将变量映射到美学来确定情节将显示什么。一个人口金字塔是一种特殊的条形图**。它在背靠背的 x 轴上显示男性和女性人口。如果你还记得的话,男性人口数值是负数。这允许沿着共享 x 轴显示两个图形的**“镜像】。 facet_share 函数允许条形图背靠背放置。插入参数 reverse_num = TRUE 以获得正确的比例,因为左边的图本质上是一个反向图。
PopPyramid <- CAPop %>%
ggplot(aes(
x = Age,
y = Pop/1000,
fill = Gender
)
) +
geom_bar(stat = ‘identity’) +
scale_fill_manual(values = c(‘#4682b4’, ‘#ee7989’)) +
coord_flip() +
facet_share(
~ Gender,
dir = ‘h’,
scales = ‘free’,
reverse_num = TRUE
)
设置主题
为了操纵情节的整体外观,你可以使用主题。有九个内置主题可供选择,这可能是一个很好的开始。我已经选择创建我自己的自定义主题来操作背景、面板、边框、网格线、刻度线和所有元素文本。主题( )中有90 多个参数** 进行最大化定制!正如你所看到的,这给了很大的灵活性来决定你的动画的整体外观。我只用了其中的 17 个论点。**
PopPyramid <- PopPyramid +
theme(
plot.background = element_blank(),
axis.ticks = element_blank(),
axis.title.y = element_blank(),
legend.title = element_blank(),
panel.background = element_blank(),
panel.border = element_blank(),
strip.background = element_blank(),
strip.text.x = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
axis.text = element_text(size = 14),
legend.key.size = unit(0.75, ‘cm’),
legend.text = element_text(
size = 15,
face = ‘bold’
),
plot.title = element_text(
size = 22,
hjust = 0.5,
face = ‘bold’
),
plot.subtitle = element_text(
size = 14,
hjust = 0.5,
face = ‘bold’
),
axis.title.x = element_text(
size = 16,
face = ‘bold’
),
plot.caption = element_text(
size = 12,
hjust = 0.5,
face = ‘italic’,
color = ‘gray’
)
)
标签
最后,我添加了有意义的标签让观众更容易理解。我希望动画随着时间的变化而变化,所以我希望这种变化能反映在标题中。使用{最近状态}** ,将显示与画面最近的状态的年份。**
PopPyramid <- PopPyramid +
labs(
title = ‘Canada Population Estimate 1960 - 2018\n\n{closest_state}’,
subtitle = ‘\n\nAge Group’,
y = ‘\n\nPopulation (in thousands)’,
caption = ‘\n\nData Source: [https://databank.worldbank.org’](https://databank.worldbank.org%27/)
)
动画
接下来,使用 transition_states 来确定动画的行为。这种转变应该跨时间发生,在本例中是年**。过渡 _ 长度和状态 _ 长度分别给出了过渡和状态的相对长度。在本例中,这产生了 1:2 的比率。 ease_aes 函数描述了补间如何在状态之间发生。**
PopPyramid <- PopPyramid +
transition_states(
Year,
transition_length = 1,
state_length = 2
) +
enter_fade() +
exit_fade() +
ease_aes('cubic-in-out')
渲染到文件
在这种情况下,产生了一个 gif 文件。使用 gifski ,你可以微调文件的渲染方式。我选择了每秒 24 帧平滑运动, 30 秒总时长和 500x500 大小。
animate(
PopPyramid,
fps = 24,
duration = 30,
width = 500,
height = 500,
renderer = gifski_renderer(‘PopPyramid.gif’)
)
这可能是 休息一下 的好时机。根据您为动画选择的参数,此步骤可能需要很长时间进行渲染。如果我打算在 GitHub 上展示所有的 gif 图片,我会尽量保持在 30 秒或更短的时间内,因为在这个平台上 gif 图片似乎有时间限制。
瞧啊。
现在你有了:加拿大人口金字塔动画从 1960 年到 2018 年!借助动画,我们可以更有说服力地看到加拿大人口正在老龄化 T21。
谢谢你抽空过来。我希望你在创建自己的动画形象时有信心。完整代码可以在我的 GitHub 库上查看。请随时在 LinkedIn 上与我联系。
脚注
- 2019 年 12 月 12 日检索数据。根据检索日期的不同,结果可能略有不同。
便携式计算机视觉:树莓 Pi 上的 TensorFlow 2.0
微小、低成本的物体检测和分类。
第 1 部分—简介
只需大约 100 美元,你就可以将深度学习添加到嵌入式系统或你的下一个物联网项目中。
你是刚入门机器/深度学习,TensorFlow,还是 Raspberry Pi?完美,这个博客系列是给你的!
在这个系列中,我将向您展示如何:
- 使用 TensorFlow 2.0 和 Keras 部署预训练的图像分类模型( MobileNetV2 )。
- 将模型转换为 **TensorFlow Lite,**一种针对嵌入式和移动设备优化的模型格式。
- 使用 Coral 的 USB Edge TPU 加速器和 Edge TPU 编译器,加速任何 TensorFlow Lite 模型的推理。
- 使用转移学习用自定义图像分类器重新训练 MobileNetV2。
本系列第一篇(你现在正在看!)将带您完成构建材料、安装以及将 MobileNetV2 部署到您 Raspberry Pi。
术语和参考📚
树莓派——一款受教育者、硬件爱好者和机器人专家欢迎的小型廉价电脑。🤖
TensorFlow —机器学习的开源平台。
tensor flow Lite—用于在移动和嵌入式设备上部署 TensorFlow 模型的轻量级库。
卷积神经网络——一种深度学习模型,非常适合图像分类和对象检测应用。
MobileNetV2**—**一种先进的图像识别模型,针对普通手机处理器的性能进行了优化。
Comparison of general-purpose computer vision neural networks. Image Credit: MobileNetV2: The Next Generation of On-Device Computer Vision Networks
边缘 TPU —张量处理单元(TPU)是一个集成电路,用于加速 **TensorFlow 执行的计算。**边缘 TPU 是为“在边缘”的移动和嵌入式设备开发的,占地面积小
TPUv1, TPUv2 (left, middle) at Cloud Next ‘18. Edge TPUs on a United States penny (right). Image credit: Google)
第 2 部分—✅构建列表
启动工具包
如果你刚刚入门树莓 Pi,我推荐 Arrow 的 Pi 相机套装(90 美元)。它包括您需要的一切,立即开始:
- 5V 2.4A MicroUSB 电源
- 320x240 2.8 英寸 TFT 型号 PiTFT 电阻式触摸屏
- 树莓 Pi 3 型号 B
- Raspberry Pi 摄像机 v2
- 塑料盒
- 预装了 NOOBS 安装管理器的 8GB MicroSD 卡
Coral USB Edge TPU 加速器(可选)
您可以编译 TensorFlow Lite 模型,在 Coral 的 USB 加速器( Link )上运行,以便更快地进行模型预测。
实时应用程序从这种加速中受益匪浅。自动驾驶机器人的决策模块就是一个例子。
一些应用程序可以容忍更高的预测速度,可能不需要 TPU 加速。例如,你不需要 TPU 加速来建立一个智能狗门,为你的狗开门(但不让浣熊进来)。
如果你刚刚开始,跳过购买这个组件。
你不确定你是否需要 USB 加速器?下面的 MobileNet 基准可以帮助您做出决定。下面的测量描述了推理速度(单位为毫秒)——速度越低越好!
Image Credit: Alasdair Allan, Benchmarking TensorFlow and TensorFlow Lite on the Raspberry Pi
定制构建
如果您已经有了一个 Raspberry Pi 或一些组件,初学者工具包可能会包含您不需要的项目。
以下是我自己制作的零件(大约 250 美元/台)。
- 树莓 Pi 型号 3 b+(35 美元)
- 树莓 Pi 相机 v2(30 美元)
- Coral USB Edge TPU 加速器——加速模型推理(75 美元,链接
- Pi Foundation 显示屏— 7 英寸触摸屏显示屏(80 美元,链接
- SmartiPi 触摸支架(25 美元,链接
- 可调 Pi 摄像机支架(5 美元,连杆
- RPi 摄像机 24 英寸的柔性电缆($3,链接)
我很想听听你自己的构建列表!❤️给我发推特 @grepLeigh 或者在下面评论。
第 3 部分— Raspberry Pi 设置🍰
如果你购买了一个预装 NOOBS 的 SD 卡,我建议你先浏览一下这个概述:设置你的树莓派
在进行之前,您需要:
第 4 部分—主要计算机:下载和安装依赖项
rpi-vision
是一套工具,可让您更轻松地:
- 在你的 Raspberry Pi 上安装很多依赖项(TensorFlow Lite,TFT 触摸屏驱动程序,将 PiCamera 帧缓冲区复制到 TFT 触摸屏的工具)。
- 将模型部署到 Raspberry Pi。
- 在你的电脑或谷歌云的人工智能平台上训练新模型。
- 为边缘 TPU 编译 8 位量化模型。
- 在您的主计算机上克隆
rpi-vision
repo(不是您的 Raspberry Pi)
$ git clone git@github.com:leigh-johnson/rpi-vision.git && cd rpi-vision
2.在您的主计算机上,创建一个新的虚拟环境,然后安装rpi-vision
包。
$ pip install virtualenv; virtualenv -p $(which python3) .venv && source .venv/bin/activate && pip install -e .
3.在继续之前,验证你可以对你的树莓 Pi 进行 SSH。
如果您使用默认的 Raspbian 映像,您的 Pi 的主机名将是raspberrypi.local
$ ssh pi@raspberry.local
第 5 部分—主要计算机:创建配置文件
rpi-vision
使用 Ansible 来管理你的 Raspberry Pi 上的部署和任务。Ansible 是一个自动化计算机配置的框架。
创建 Ansible 所需的 2 个配置文件:
。env/my-inventory.ini
如果您对 Pi 使用自定义主机名,请替换raspberrypi.local.
tee -a .env/my-inventory.ini <<EOF
[rpi_vision]
raspberrypi.local[rpi_vision:vars]
ansible_connection=ssh
ansible_user=pi
ansible_python=/usr/bin/python3
EOF
。env/my-vars.json
如果您对 Pi 使用自定义主机名,请替换raspberrypi.local.
tee -a .env/my-vars.ini <<EOF
{
*"RPI_HOSTNAME"*: "raspberrypi.local",
*"VERSION"*: "release-v1.0.0"
}
EOF
第 6 部分— Raspberry Pi:安装依赖项
$ make rpi-install
您将看到一个可行剧本的输出。 Ansible 是一个自动化配置计算机的框架。
您的 Pi 上安装了什么的快速摘要:
rpi-vision
回购rpi-fbcp
(从 PiCamera 复制 framebuffer 到 TFT 触摸屏显示器的工具)- TFT 触摸屏驱动器和 X11 配置
您可以通过打开playbooks/bootstrap-rpi.yml
来检查在您的 Raspberry Pi 上运行的任务
在安装运行时,通读下一部分,了解 如何CNN工作以及 为什么 它们对计算机视觉任务有用。
第 7 部分 CNNs(卷积神经网络)简介
CNN 是驱动自动驾驶汽车和图像搜索引擎的关键技术。该技术对于计算机视觉来说是常见的,但也可以应用于数据中具有层次模式的任何问题,其中复杂模式可以由简单模式组装而成。****
视觉皮层建模
在 20 世纪 50 年代末和 60 年代,大卫·H·哈贝尔和托顿·威尔森在猫和猴子身上做了实验,以更好地了解视觉皮层。
Diagram of the implant installed in the skulls of cats with a trephine. Image Credit: SINGLE UNIT ACTIVITY IN STRIATE CORTEX OF UNRESTRAINED CATS
他们证明了纹状皮层中的神经元对有限视野中的刺激做出反应,他们称之为感受野。
他们注意到了同心重叠反应,其中复杂的模式是低水平模式的组合。
他们的发现还揭示了特殊化**,其中一些神经元将只对特定形状或模式做出反应。**
在 20 世纪 80 年代,受 Hubel 和 Wielson 的启发,Kunihiko Fukushima 在neocogniton**、**、上发表了一种能够学习具有几何相似性的模式的神经网络。
Diagram of a Neocogitron, the foundation for modern CNNS. Image Credit: Neocognitron: A Self-organizing Neural Network Model for a Mechanism of Pattern Recognition Unaffected by Shift in Position
新回旋加速器有两个关键特性:
- 学习到的模式是有层次的。 越来越复杂的图案是由越来越简单的图案组成的。
- 学习到的模式是位置不变和平移不变的。网络学习到一个模式后,可以在不同的位置识别这个模式。在学习如何对狗进行分类之后,网络可以准确地对倒立的狗进行分类,而无需学习全新的模式。****
neocogitron 模型是现代卷积神经网络的灵感来源。
可视化卷积运算:2D
(Left) 2D 4x4 Input matrix. (Middle) 2D 2x2 kernel. (Right) 2D 2x2 output feature map.
输入层被送入卷积层,卷积层使用滤波器转换输入的区域。****
过滤器也被称为内核。
The filter “slides” to each possible position, and the result is added to the feature map.
对于输入矩阵中的每个位置,卷积运算对每个元素执行矩阵乘法。
产生的矩阵被求和并存储在特征图中。****
对输入矩阵中的每个位置重复该操作。
可视化卷积运算:3D
Image Credit: Applied Deep Learning — Part 4: Convolutional Neural Networks
CNN 的输入层通常是一个 3D 数据结构,具有 高度 、 宽度 、 通道 (RGB 或灰度值)。
我们在特征地图栈中越深入,每个地图层就变得越稀疏。这意味着过滤器检测到的特征更少。
特征地图堆栈的前几层检测简单的边缘和形状**,看起来与输入图像相似。随着我们进入特征地图堆栈越来越深,对于人眼来说,特征变得越来越抽象。更深的特征层**编码分类数据,像“猫脸”或“猫耳”。
Comparison of feature maps from the first convolution layer (block1_conv1) with later layers (block5_conv1). Image Credit: Applied Deep Learning — Part 4: Convolutional Neural Networks
你想了解更多关于 CNN 的信息吗?
您的依赖项安装现在可能已经完成了。要向前迈进,请跳到第 8 部分——部署预培训模型 MobileNetV2。****
如果您计划训练一个自定义分类器,或者想了解更多关于卷积神经网络的信息,请从这里开始:
- 应用深度学习—第 4 部分:卷积神经网络
- 使用 Scikit-learn 和 TensorFlow 进行机器学习,第 13 章,卷积神经网络,作者 Aurélien Géron
- **用 Python 进行深度学习,第五章计算机视觉的深度学习,Francois Chollet
第 8 部分—部署预训练模型(MobileNetV2)
现场演示(使用 TensorFlow 2.0)
I used this code to sanity-check the TensorFlow 2.0-beta0 wheel that I cross-compiled for my Raspberry Pi 3.
- 嘘到你的树莓皮
$ ssh raspberrypi.local
2.启动新的 tmux 会话
pi@raspberryi:~ $ tmux new-session -s mobilenetv2
3.通过按 control+b 垂直拆分 tmux 会话,然后"
4.启动一个fbcp
进程,通过 SPI 接口将帧缓冲区从 PiCamera 复制到 TFT 显示器。让这个过程继续运行。
pi@raspberryi:~ $ fbcp
5.通过按下 control+b,然后按下 o 来切换 tmux 面板。
6.激活前面第 6 部分中安装的虚拟环境。
pi@raspberryi:~ $ cd ~/rpi-vision && . .venv/bin/activate
7.启动 mobilenetv2 代理进程。代理初始化大约需要 60 秒。
pi@raspberryi:~/rpi-vision $ python rpi_vision/agent/mobilenet_v2.py
您将看到模型基础的摘要,然后代理将打印推理,直到停止。点击查看您应该看到的要点。
这个演示使用了 ImageNet 分类器的权重,你可以在【image-net.org】的中查找。
感谢您的阅读!
恭喜您,您刚刚为您的 Raspberry Pi 部署了一个图像分类模型!✨
寻找更多针对 Raspberry Pi 和其他小型设备的机器学习实践示例?注册我的简讯!
我发布了现实世界中 ML 应用程序的例子(有完整的源代码)和漂亮的技巧,如自动消除边框注释的痛苦。
使用数学和 Python 优化您的投资
在 Python 的 PuLP 中使用线性优化
在 MBA 学习期间,我们学习了使用 Excel 和怀卡托大学的免费软件 WEKA 的所有预测建模技术。我们学习了基础概念,但从未涉足高级计算所需的硬技能。在学习 python 一段时间后,我认为重做我最初在 Excel 的求解器中做的一个线性优化项目会很有趣。本文的目标是在 python 的 PuLP 中重新创建项目,分享我一路上学到的东西,并将 python 的结果与 Excel 的结果进行比较。
线性优化的实际好处/应用是无穷无尽的。我强烈建议密切关注,至少在概念层面上,如果你还不熟悉的话,努力学习这个技巧。
目录
- 什么是线性优化?
- 任务/评论
- 数据上传和清理
- 使用纸浆的线性优化
什么是线性优化?
根据维基百科的说法,线性规划是“一种在其要求由线性关系表示的数学模型中实现最佳结果(如最大利润或最低成本)的方法。”这些来自卡耐基梅隆大学的课堂笔记对我理解这个主题非常有帮助。
用我自己的话来说,我会把它描述为一种解决特定变量(决策变量)的最小/最大解的方法,它与其他线性变量交织在一起,到了用笔和纸解决问题会非常困难的程度。
任务/评估:
这是一个关于投资组合的风险和收益的线性优化问题。我们的目标是最小化投资组合风险,同时满足 5 个约束条件:
- 投资总额将为 10 万美元
2.这个投资组合的年回报率至少为 7.5%
3.至少 50%的投资是 A 级的
4.至少 40%的投资是立即流动的
5.储蓄账户和存款单上的金额不超过 3 万美元
详细说明如下:
Source: pg 127
回顾解决线性优化问题的过程,有 3 个步骤:
- **决策变量:**这里有 8 个决策变量。它们是我们的投资选择。
- 目标函数:我们希望将 8 项投资的风险最小化。下面是投资乘以各自的风险系数。
3.**约束:**最后,我们想要准确地定义我们的约束是什么。这些用代数表示,顺序与我们之前列出的约束相同:
如果你对约束#2 中的“7500”感到困惑,那就是我们期望的 7.5%的年回报率乘以我们的 100000 美元投资。
数据上传和清理:
现在我们已经设置了问题,让我们将数据上传到 pandas 并导入纸浆:
from pulp import *
import pandas as pd
df = pd.read_excel(r"C:\Users\Andrew\Desktop\Fin_optimization.xlsx")
df
Output 1
看起来不错。但是,为了继续进行,必须进行一些格式上的更改。
- 将“流动性”和“评级”列转换为二进制值。这与约束条件#3 和#4 有关。这些列中的相关字符串值是流动性的“即时”和评级的“A”。为了进一步计算,有必要将这些字符串值与其他字符串值区分开来。
- 为投资类型创建新的二进制列。约束#5 集中在储蓄和定期存款投资类型上,因此将它们与其他投资类型区分开来将有助于以后的工作。
- 为 Amt_invested 创建一个全为 1 的列。这对约束#1 很有用:100,000 美元的总投资组合约束。
#1a
df['Liquidity'] = (df['Liquidity']=='Immediate')
df['Liquidity'] = df['Liquidity'].astype(int)#1b
df['Rating'] = (df['Rating']=='A')
df['Rating']= df['Rating'].astype(int)#2
savecd = [1,1,0,0,0,0,0,0]
df['Saving&CD'] = savecd#3
amt_invested = [1]*8
df['Amt_Invested'] = amt_invested
df
完美。我们继续吧。
使用纸浆的线性优化:
使用纸浆的第一步是定义问题。下面的代码简单地将我们的问题定义为最小化(关于风险),并将其命名为“Portfolio_Opt”。稍后,我们将向这个“prob”变量添加更多内容。
prob = LpProblem("Portfolio_Opt",LpMinimize)
接下来,我们将创建一个决策变量(投资选项)列表。然后,我们将使用该列表为每个特性创建字典:
#Create a list of the investment items
inv_items = list(df['Potential Investment'])#Create a dictionary of risks for all inv items
risks = dict(zip(inv_items,df['Risk']))#Create a dictionary of returns for all inv items
returns = dict(zip(inv_items,df['Expected Return']))#Create dictionary for ratings of inv items
ratings = dict(zip(inv_items,df['Rating']))#Create a dictionary for liquidity for all inv items
liquidity = dict(zip(inv_items,df['Liquidity']))#Create a dictionary for savecd for inve items
savecd = dict(zip(inv_items,df['Saving&CD']))#Create a dictionary for amt as being all 1's
amt = dict(zip(inv_items,df['Amt_Invested']))
risks
Output of “risks” dictionary for reference
接下来,我们将决策变量定义为投资,并为其添加一些参数,
- Name :标注我们的决策变量
- 确保我们的解决方案中没有负资金
- **连续:**因为我们处理的是美分对美元的交易。
inv_vars = LpVariable.dicts("Potential Investment",inv_items,lowBound=0,cat='Continuous')
最后,我们将修改后的决策变量添加到我们之前创建的问题变量中,并另外输入约束条件。我们对每个投资项目使用“for 循环”来遍历字典。
#Setting the Decision Variables
prob += lpSum([risks[i]*inv_vars[i] for i in inv_items])#Constraint #1:
prob += lpSum([amt[f] * inv_vars[f] for f in inv_items]) == 100000, "Investments"Constraint #2
prob += lpSum([returns[f] * inv_vars[f] for f in inv_items]) >= 7500, "Returns"Constraint #3
prob += lpSum([ratings[f] * inv_vars[f] for f in inv_items]) >= 50000, "Ratings"Constraint #4
prob += lpSum([liquidity[f] * inv_vars[f] for f in inv_items]) >= 40000, "Liquidity"Constraint #5
prob += lpSum([savecd[f] * inv_vars[f] for f in inv_items]) <= 30000, "Save and CD"
prob
下面是问题:
结果:
prob.writeLP("Portfolio_Opt.lp")
print("The optimal portfolio consists of\n"+"-"*110)
for v in prob.variables():
if v.varValue>0:
print(v.name, "=", v.varValue)
这与 Excel 的规划求解给出的结果完全相同。
参考我的 Github 查看完整的笔记本文件。
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
如果您觉得这很有帮助,请订阅。如果你喜欢我的内容,下面是我做过的一些项目:
Ubuntu 中的位置监视器检测器(在 python3 上用 opencv 构建)
因为我近视,我一直在寻找一种方法来控制我的眼睛到显示器的距离
并且保持适当的距离,因为坐得太近对眼睛不好。在学习了一点 opencv 库之后,我正在思考这个想法的简单实现。
根据人体工程学提示(从这里得到):
调整显示器高度,使屏幕顶部与眼睛齐平或略低于眼睛。当观看屏幕中间时,你的眼睛应该稍微向下看。将显示器放在距离眼睛至少 20 英寸(51 厘米)的地方,大约一臂的距离。
让我们在 Python3 中创建一个程序,它会在我们每次坐得太近时提醒我们(距离<51 cm or 20 inches) to monitor and display a message with warning. So, for face recognition part we will use default classifier from opencv, and tkinter as a way to send message on the screen.
Opencv is an awesome library, which allows you to do various operations on the image/video content such as convert to grayscale, make a mask, cropping, rescaling the image and many-many more.
After looking at 这个来自 opencv docs 的例子,我认为,这是我特别需要的成功目标——进行简单的人脸检测。只是稍微调整一下例子——去掉眼睛分类器,就这样。
接下来的问题是如何测量人脸到摄像头的距离。稍微搜索了一下,发现了来自 pyimagesearch 的超级文章。在这份材料中,你可以发现,测量距离现在可以从物体的高度计算,并从特定的距离知道物体的高度。
例如,在距离 1 米处,以像素为单位对象的高度是 100。如果我们运行检测并观察,对象的高度是 50 像素——那么对象大约远两倍。如果是 120 像素,那么这个物体距离我们不到 1 米。因此,我们简单地检查现在的面部高度是否大于从选定距离知道的面部高度——然后发送带有 tkinter 的消息。
Tkinter 是一个简单的 GUI 工具,它提供了我们所需要的:弹出窗口部件,当且仅当检测到人脸并且人脸离监视器太近时才会出现。仅仅创建一个带有警告文本的文本小部件就足够了。
所以,让我们实现所有这些吧!
安装 Tkinter:
sudo apt-get install python3-tk
安装其他 python 包:
pip3 install numpy opencv-python
创建 python 脚本:
touch distance_detector.py
用下面的代码填充脚本(给代码添加了很多注释,这样你就可以理解这里发生了什么):
distance_detector.py
快速解释:
- 使用 argparse 允许从 cmd(9–14)设置距离和已知高度;
- 函数,它创建 tkinter 消息并返回它(16–27);
- 从摄像机中捕捉视频,对其进行灰度处理,并进行人脸检测(32–43);
- 当未检测到人脸(44–46)或人脸远离时(56–59)—禁用 tkinter 消息。如果不这样做,警告信息不会在一次成功检测后消失;
- 当检测到人脸时—计算到检测到的人脸的距离(48–52);
- 如果距离小于选定距离—启用 tkinter 消息(53–55);
- 在 opencv 窗口中显示有用的信息,按 Q(60–81)后全部关闭。
在使用之前,我们需要根据你的脸校准它。为此:
- 运行
python3 distance_detector.py
; - 坐在远处的摄像机前,这将限制眼睛;
- 注意
face_height
参数并关闭脚本(在窗口中按 Q); - 用你的
face_height
参数运行python3 distance_detector.py
。
例如,当我从 51 厘米开始坐着时,我的面部高度是 300 像素。在我的情况下,我需要运行python3 distance_detector.py -f 300 -d 0.51
。
它看起来像什么:
Yep, it works!
在本文中,你可以看到,在将想法分解成小块之后,实际实现变得并不困难。是的,您可以添加更多的技术来使检测工作更加准确并提高性能——但作为简单的例子,这应该足够了。
附:如果戴眼镜,要注意探测会不太准确。
感谢您的关注!祝你有美好的一天=)
使用 PyTorch 对人工神经网络进行事后分析
我与 机器学习 的第一次互动是在我需要完成一项任务的时候,我只是在互联网上搜索使用机器学习的方法。我最终使用 scikit-learn API 用于 ann。我完成了任务,但对后端发生的事情一点概念都没有。于是,后来我开始上 MOOCs,开始探索这个 “黑匣子”背后的数学。
在所有这些探索之后,当我回来使用 ann 时(这次是使用 Keras),我发现非常令人不安的是,像 model.fit 这样简单的一行代码正在后端自行完成所有事情。这是我决定转向一个框架的时候,这个框架给了程序员更大的权力,其代码更具可读性。
在这篇博客中,我将使用 PyTorch 用代码和数学来解释人工神经网络。所以没有任何进一步的拖延,我们开始吧。
PyTorch
这是一个深度学习框架,在研究人员中非常受欢迎,主要是因为它非常 pythonic 化且易于理解的语法。
如 PyTorch 教程[1]所述,典型的模型训练遵循以下六个步骤。
- 定义模型(带有所有可学习的参数)
- 迭代输入数据集
- 前进传球
- 计算损失
- 反向传播梯度
- 更新权重。
让我们回顾一下这些步骤背后的代码(PyTorch)和数学。
定义模型
在传统的人工神经网络中,应该有一个输入层和一个输出层以及可选的隐藏层。输入图层的大小与数据集中每个输入的大小相同。输出层的大小与类的数量相同,对于隐藏层,大小可能因问题而异。
我们将要使用的模型包含一个大小为 2 的输入层、一个大小为 3 的隐藏层和一个大小为 1 的输出层。下面是 PyTorch 中的代码。请注意,该代码还包含函数 forward。我们将在稍后的 向前传球 部分中讨论这一点。
import torch# defining model architecture
class ANN(torch.nn.Module):
d*ef __init__*(*self*):
super(NueralNetwork, *self*).__init__()
*self*.fc1 = torch.nn.Linear(2,3)
*self*.fc2 = torch.nn.Linear(3,1)
*def* forward(*self*, x):
x = torch.sigmoid(*self*.fc1(x))
x = torch.sigmoid(*self*.fc2(x))
*return* x
这里的模型有 13 个可学习的参数,包括 9 个权重参数(6 个用于隐藏层,3 个用于输出层)和 4 个偏差参数(3 个用于隐藏层,1 个用于输出层)。以下是该模型的图示。
Artificial Neural network Architecture [2]
数据集
第二步是准备数据集。在这篇博客中,我们的数据集是 XOR 门,它包含 4 行,每行有 2 个输入和 1 个输出。我们使用下面这段代码来定义它。
*# setting up data* X = torch.tensor(([0, 0],[0, 1],[1, 0], [1, 1]), dtype=torch.float)
y = torch.tensor(([0], [1], [1], [0]), dtype=torch.float)
向前传球
从这里开始,我们坚持数据集的第一行,即 X[0] = [0,0]和 y[0] = [0]。数学上,向前传球使用以下公式计算:*y^ = w 输入+ b 其中‘w’和‘b’分别代表权重和偏差。
我们使用下面这段代码来查看我们的初始权重和偏差。
nueralNet = ANN()
print(nueralNet)
params = list(nueralNet.parameters())
*# print(params)
# print(nueralNet.fc2.weight)* print("Weight vector for layer 1")
print(params[0])
print("Bias vector for layer 1")
print(params[1])
print("Weight vector for layer 2")
print(params[2])
print("Bias vector for layer 2")
print(params[3])
print('==========================')
out = nueralNet(X[0])
这会打印出以下内容
NueralNetwork(
(fc1): Linear(in_features=2, out_features=3, bias=True)
(fc2): Linear(in_features=3, out_features=1, bias=True)
)
Weight vector for layer 1
Parameter containing:
tensor([[-0.3122, 0.5551],
[-0.2819, -0.0666],
[ 0.6712, -0.2915]], requires_grad=True)
Bias vector for layer 1
Parameter containing:
tensor([ 0.2816, -0.4757, -0.4125], requires_grad=True)
Weight vector for layer 2
Parameter containing:
tensor([[-0.5761, -0.2721, 0.4413]], requires_grad=True)
Bias vector for layer 2
Parameter containing:
tensor([-0.1019], requires_grad=True)
我们将数据集的第一个实例传递给模型的代码行执行向前传递的计算。这将运行前面在 ANN 类中定义的 forward 函数。正向传递中的 Sigmoid 是由以下公式给出的激活函数:
以下是向前传球背后的数学计算。
这导致 y^ = 0.4112
计算损失
在向前传递之后,我们根据原始标签评估由我们的模型创建的输出。这是通过计算损耗来实现的,损耗实质上是测量标签和当前输出之间的距离。在我们的实现中,我们将使用流行的均方误差。
*import* torch.optim *as* optim
*# create your optimizer* optimizer = optim.SGD(nueralNet.parameters(), lr=0.01)
criterion = torch.nn.MSELoss()
*# in your training loop:* optimizer.zero_grad() *# zero the gradient buffers* loss = criterion(out, y[0])
print('Mean squared error or loss = ', loss)
数学上,它由下式给出
因此,在我们的例子中,因为我们只有一个输入实例,所以损失是= 0.1691。
反向传播渐变
在计算了损失之后,我们需要一个最小化它的方法来改进我们的方法。为此,我们首先计算梯度,然后将它们传播回来以更新可学习的参数。这是通过 PyTorch 中的以下代码完成的
loss.backward()
optimizer.step() *# Does the weight update*print(**"Weight vector for layer 1"**)
print(params[0])
print(**"Bias vector for layer 1"**)
print(params[1])
print(**"Weight vector for layer 2"**)
print(params[2])
print(**"Bias vector for layer 2"**)
print(params[3])
print(**'=========================='**)
我鼓励你通过这个链接(https://www.youtube.com/watch?v=Ilg3gGewQ5U)来深入理解反向传播和权重更新的工作原理。作为一个有趣的练习,在这个例子中尝试计算更新后的梯度和参数。