简化的生物识别
计算机科学
关于生物识别的深入而简单的解释
T 传统的信任概念和对我们交往对象身份的信念有时远不如我们过去认为理所当然的那样值得信任。我们生活在一个越来越相互关联的世界,个人之间的联系越来越多地通过一些新的电子媒介进行。随着我们的生活方式和社会发展,我们一致认为,对更好、更灵活的方式来维持日常交易中的保护、安全和信心的需求最终会增加。如果我们越来越依赖自动通信,那么处理越来越多的不同系统的复杂性就会增加。例如,我们是应该尝试为我们使用的每个系统回忆一个新密码,还是应该在所有系统之间共享密码,以最大限度地减少记忆丢失的可能性以及由此带来的不便?这些问题,以及日常生活的许多其他方面,都有一个共同点:它们都以某种方式对人类身份提出了关切。我们如何证明我们是我们所说的人?我们如何确定另一个人就是他们所说的那个人?
生物测定学
生物统计学可以被描述为一门科学学科,涉及计算和应用一个人的特性或特征,这些特性或特征可以用来唯一地识别那个人。生物统计学假设人类的许多物理或行为属性可以与个体唯一相关联。通过使用正确制作的传感器记录这些特征,以数字格式表示它们,并且将该捕获的数据与在先前时间情况下从同一个人获得的数据进行比较,可以使个人识别过程自动化。因此,生物特征识别或验证可以被认为是一个模式识别问题,其中计算机识别个人生物特征的重要特征(模式)并准确匹配它们。理论上,任何人类特征或特性都可以用作生物特征数据源,只要它满足以下要求:
通用性:所选择的特性应该是所有人都具备的。这是因为生物识别设备将是包容性的,允许尽可能多的用户使用它。
独特性:就所选择的特征而言,任何人类都不可能相似。因为我们应该量化区分一个人和另一个人的特征,如果我们想识别他们的话。
持久性:选择的特征必须是时不变的。至关重要的是,我们选择的任何特征都是经过一致计算的,否则,一个人在不同的时间可能会表现为不同的人。
可测量性:选择的特性应该是科学上可定量测量的。它应该被很好地指定,以消除在被测量的东西上的所有模糊性。
生物识别技术分为两大类,即基于物理的生物识别和基于行为的生物识别。
生理生物特征是对一个人内在生理特征的测量。指纹是生理生物特征形态的一个明显的例子。这实际上是一个人的基本生理组成的一部分,虽然它可以在一定程度上改变,例如,通过创伤或伤害,它通常不是人可以直接控制的。
在行为生物测定学中,测量值来自一个人执行的随机或故意学习的行为。这里最明显的例子是我们使用手写签名作为生物特征数据的来源。与自然存在且始终存在的基于身体的生物识别不同,签名仅在一个人书写时存在。因此,行为生物测定学可以定义为:“获取要求受试者主动的生物测定样本。它们必须在有传感器的情况下执行特定的活动。行为特征是随着时间的推移而习得的,而不是基于生物学
常用的生物特征
没有任何一种生物识别技术能够有效地满足前面提到的所有标准。换句话说,没有一种生物识别技术是完美的,但是有多种生物识别技术是合适的。特定生物特征的适用性由应用的目的和条件以及生物特征的特性决定。下面是一些最广泛使用的生物特征的简要概述。
指纹已经被人类用于个人身份识别几十年了。指纹的形成是在胎儿发育的前七个月内确定的,此时指尖表面上的脊和谷已经形成。
人的掌纹就像指纹一样,包含着脊和谷的图案。手掌的面积比手指的面积大得多,这意味着它比手指有更多的图案。因此,掌纹被认为比指纹更独特。
虹膜是眼睛的环形区域,由瞳孔和两侧的巩膜界定。虹膜的视觉纹理在胎儿发育期间形成,并在生命的头两年稳定下来。虹膜的颜色因人而异,但在颜色上有一些一致性,包括绿色、蓝色、棕色,在极少数情况下,甚至是淡褐色。虹膜的主要目的是调节瞳孔的直径和大小。瞳孔是眼睛的一部分,它使光线能够穿透眼睛,然后向后部传播到视网膜。当然,能够到达瞳孔的光量是其扩张和收缩能力的直接结果,这是由虹膜肌肉调节的。当近红外光(NIR)照射到虹膜上时,可以观察到许多独特的特征。
视网膜脉管系统具有复杂的结构,对每个人和每只眼睛来说都是独一无二的。它被认为是最稳定的生物测定学,因为视网膜的生物学基础在人的一生中几乎不会改变。为了显现视网膜脉管系统的预定部分,人必须看着目镜并聚焦在视野中的某个点上。图像采集过程需要主体的合作,目镜的接触,以及使用者的刻意努力。这些考虑因素中的许多都有助于视网膜生物特征的公众接受。视网膜的脉管系统将暴露任何医疗信息,例如高血压,这是阻碍公众接受基于视网膜扫描的生物识别的另一个因素。此外,公众认为视网膜扫描是一种健康威胁,一些人认为视网膜扫描损害眼睛。
面部检测是一种非侵入式技术,面部表情是人类用来相互识别的最常见的生物特征。我们可以通过看一个人的脸来识别他。面部识别存在一些技术上的不足。例如,如果面部识别设备捕获一个严重超重的人的图像,然后捕获同一个人的另一个已经减掉大量体重的图像,则系统将无法再识别该人。
手的几何形状识别系统使用取自人手的各种尺寸,例如其形状、手掌周长以及手指长度和宽度。手的几何形状不被认为是特别独特的,并且基于手的几何形状的识别系统不能被放大用于涉及来自大量人群的人类识别的系统。
步态指人行走的方式,是为数不多的可用于远距离识别人的生物特征之一。这一特征非常适合于可以秘密确定身份的监控场景。步态的缺点是受各种因素的影响,包括靴子、衣服、腿疼和行走表面。
耳朵作为一种生物特征包含了大量特定而独特的特征。据说耳廓软骨组织的结构是独特的。耳朵识别方法依赖于匹配耳廓上的显著点和耳朵上的界标点之间的距离,或者耳朵的存在。耳朵识别可以帮助根据个人资料图片识别一个人。
声音是物理和行为生物特征的结合。声音合成中使用的附件(如声道、颌骨、鼻腔和嘴唇)的形式和大小决定了一个人言语的物理特征。人类言语的这些生理特征对一个人来说是不变的,但是言语的心理因素随着年龄、医疗条件、情感状态和其他因素而变化。语音特征很容易受到背景噪声和麦克风功能等因素的影响,这是基于语音的识别的一个缺点。然而,它在基于电话的系统中仍然有用,但是语音信号质量通常被通信信道降级。
按键。假设每个人都在键盘上以独特的方式打字。这种生物识别不太可能对每个人都是唯一的,但它可以提供足够的歧视性信息,以便进行身份验证。击键动力学是一种行为生物统计学。由于情绪状态、用户相对于键盘的位置、使用的键盘样式等的变化,可以预期一个人的打字习惯会出现显著的班内差异。当一个人键入细节时,他们的击键可以被不可见地跟踪。击键的缺点是打字模式可能不稳定和不一致,就像抽筋的肌肉和出汗的手会显著改变一个人的打字模式一样
签名。一个人签名的方式被认为是这个人的一个显著特征。虽然签名需要作者与书写工具进行交互并付出努力,但它们已被用作官方、民事和商业交易中的一种身份识别手段。签名是一种随时间变化的行为生物特征,受签名者身体和情绪状态的影响。有些人的签名彼此差异很大;甚至对他们签名的连续印象也明显不同。此外,职业伪造者可能能够模仿欺骗签名认证系统的签名。
DNA 指的是脱氧核糖核酸,其中含有生物体发育和功能所必需的遗传信息。除了同卵双胞胎,他们有相同的 DNA 模式,DNA 是一个人唯一性的一维特殊代码。然而,它目前主要用于犯罪和受害者发现的法医系统。最复杂的 DNA 匹配技术需要耗时的化学方法,这需要专家的专业知识,而且还不适合在线非侵入式识别。
矿脉红外热像图。人体发出的热量模式对每个人来说都是独一无二的,可以通过红外相机以非侵入的方式捕捉,类似于正常的照片。虽然基于温谱图的设备不需要触摸并且是非侵入性的,但是在人体附近具有发热表面的未调节条件下,图像采集可能是困难的。
气味。众所周知,每个物体都会发出一种独特的化学成分气味,这种气味可以用来区分物体。各种各样的化学传感器被吹到一个物体周围的空气中,每个传感器都对一组特定的(芳香)化合物敏感。人(或任何动物)身体产生的一部分气味是这种生物所独有的。尽管使用了除臭剂以及周围环境中变化的化学成分,是否可以检测到体味的变化还不清楚。
生物识别的工作原理
实际上,生物识别技术有两种最常见的用途:验证和识别。总的来说,生物特征验证和身份识别包括两个阶段。注册和认可。
注册是在特定系统中注册或登记您的生物特征的阶段。每个人的生物特征数据都被收集并存储在一个数据库中,与该人的身份放在一起。通常,生物特征数据被分析以获得显著的和有区别的特征。导出的特征(机器学习术语中提取的特征)通常被保留,而原始的生物特征数据被丢弃。目前,使用卷积神经网络来处理特征提取问题是非常普遍的。
识别(识别和验证)是一个阶段,在此阶段,重新从个人处获取生物特征数据,并与存储的数据进行比较,以确定用户的身份。因此,生物识别系统基本上是一个模式识别系统。
身份证明。作者图片
说你们公司有 50 名员工,想做一个基于生物识别的考勤系统。标识意味着系统需要识别给定的生物特征属于谁。它使用阈值来决定是识别给定的生物特征还是拒绝它。如果给定的生物测定身份与所识别的生物测定相匹配,则该识别是正确的。
验证。作者图片
另一方面,验证(也称为认证)只针对一个人。比如你用指纹锁手机,这样除了你没人能用你的手机。验证只是验证给定的生物特征是否与您的匹配。它使用阈值来决定认证,如果相似度大于阈值,则接受认证,否则拒绝认证。如果属于系统的给定生物特征(真实的)被接受,而冒名顶替者被拒绝,则验证是正确的。
生物识别指标
那么我们怎么知道一个生物识别系统好不好呢?生物识别系统准确性的基本衡量标准是错误不匹配率 (FNMR)和错误匹配率 (FMR)。
FNMR 是指来自同一用户的两个生物特征样本被错误识别为不匹配的概率。例如,10%的 FNMR 表示 100 次正版用户验证尝试中有 10 次会被拒绝。
另一方面,FMR 指的是来自不同用户的两个生物特征样本被错误地识别为匹配的概率。例如,10%的 FMR 表示冒名顶替者用户的 100 次验证尝试中有 10 次会被接受。
在验证的上下文中,FNMR 和 FMR 也分别被称为误拒绝率(FRR) 和误接受率(FAR) 。 FRR 是衡量系统拒绝真实用户的频率,而 FAR 是衡量系统接受冒名顶替用户的频率。FRR 由 FRR 给出=总错误拒绝/总正确尝试。FAR 由给出,FAR =总错误接受/总错误尝试。由于验证是基于我们之前提到的阈值,因此不可能同时最小化 FRR 和 FAR。增加阈值将导致 FAR 降低,但 FRR 增加,反之亦然。作为替代,可以使用由TAR = 1-FRR 给出的真实接受率(TAR) 。
在识别的情况下,生物特征识别系统在登记了 N 个用户身份的情况下,输出与前 t 个匹配(1 ≤ t < N)相对应的一组身份。识别等级被定义为用户的正确身份在由识别系统返回的前 t 匹配中的等级。识别系统中的误差可分为两类假阳性识别率和假阴性识别率。
误报识别率(FPIR) 衡量系统返回未注册用户身份的频率。
假阴性识别率(FNIR) 是对系统返回已在系统中注册但其身份不在识别等级中的用户的身份的频率的度量。
与 FNIR 相关的一个量是真实的肯定识别率****【TPIR】,它是对系统多久返回一个用户身份的度量,该用户在系统中注册并且他的身份在识别等级中。因此, *FNIR = 1 — TPIR。*如果身份识别系统只返回最顶端的身份( t = 1),在 TPIR 它被称为一级准确度,这个度量是比较不同生物特征识别系统最常用的度量之一。
生物识别的优势
- 很难伪造
- 使用方便
- 不可转让的
- 不太可能改变
- 不需要记忆
生物识别的缺点
- 昂贵的
- 存储生物特征数据的数据库仍然可能被黑客攻击
- 错误识别或验证仍然可能发生
- 无法识别受伤的生物特征
结论
生物统计学可以被描述为一门计算和应用一个人的特征的科学,这些特征可以用来唯一地识别这个人。生物统计学假设人类的许多物理或行为属性可以与一个人唯一相关联。因此,可以通过使用正确制作的传感器记录这些特征,以数字格式表示这些特征,并将捕获的数据与以前同一个人的数据进行比较,从而实现人员识别过程的自动化。这样,我们就不需要再记住很多密码了。
参考
https://www.amazon.com/Biometrics-Very-Short-Introduction-Introductions/dp/0198809107 https://www.springer.com/gp/book/9780387773254 https://www.amazon.com/Science-Biometrics-Security-Technology-Verification/dp/1498761240 https://searchsecurity.techtarget.com/definition/biometrics https://www.csoonline.com/article/3339565/what-is-biometrics-and-why-collecting-biometric-data-is-risky.html
一只接一只地使用深度学习
第一部分。利用迁移学习、辅助任务和注意提出用于细粒度分类的 CNN 模型
作者图片
本文展示了如何改进用于图像相关任务的深度学习模型,以解决细粒度分类问题。为此,我们将完成以下两个部分。首先,您将熟悉计算机视觉和卷积神经网络的一些基本概念,而第二部分将演示如何使用 PyTorch 将这些知识应用于鸟类分类的实际问题。具体来说,您将学习如何构建自己的 CNN 模型-ResNet-50,以使用迁移学习、辅助任务和注意力增强架构,甚至更多来进一步提高其性能。
介绍相关工作
深度学习在计算机视觉中的应用
在处理数字时,计算机的表现非常出色。为了让人类登上月球而解无数方程?没问题。确定图像中出现的是猫还是狗?哎呀……这个对任何人来说天生容易的任务对第一台计算机来说似乎是不可能的。这些年来,算法和硬件一样进化(还记得摩尔定律吗?国际注册会计师协会)。计算机视觉领域的出现是对使用计算机解决图像分类任务的一种尝试。经过长时间的发展,许多复杂的方法被创造出来。然而,它们都缺乏可推广性:一个用来分类猫和狗的模型不能区分,例如,鸟。
卷积神经网络的设计原则
1989 年,Yann LeCun 和他的同事们已经提出了【1】,并进一步发展了【2】卷积神经网络(CNN)的概念。该模型本身受到人类视觉皮层的启发,在视觉皮层中,视觉神经元负责肉眼可见的一小部分图像——神经元的感受野。在结构上,它以单个卷积神经元(滤波器)逐步扫描输入图像的方式表达,多次应用于图像的不同部分,这涉及到权重共享的概念(图 1)。
图一。LeCun 的 LeNet-5,卷积神经网络模型,由卷积和子采样操作组成,后面是全连接层,处理前几层提取的数据以形成最终输出(改编自[2])
走向剩余网络
当然,从 LeCun 的 LeNet-5,开始,CNN 模型的最新水平已经有了很大的发展。Alex net【3】第一个成功的大规模架构问世,赢得了 ILSVRC 2012 挑战赛,实现了 15.3%的前 5 名错误率。后来的发展给出了许多强大的模型,这些模型主要是在更大和更复杂的架构的使用过程中得到改进的。问题是,随着网络越来越深(深度在增加),它的性能变得饱和并开始下降。为了解决这个问题,残差神经网络(ResNet)被开发出来【4】,以有效地将输入引导到某些层(也称为跳过或残差连接)。
图二。跳过 ResNets 的连接块
ResNet 架构的核心思想是将一部分信号传递到未经处理的卷积块的末端(仅通过复制值),以扩大通过深层的梯度流(图 2)。因此,跳过连接保证了模型的性能不会降低,但可能会略有提高。
下一部分解释了讨论的理论如何实际应用于解决现实世界的问题。
利用 ResNet 进行鸟类分类
鸟类物种识别是一项挑战人类专家和计算机视觉能力的艰巨任务。与细粒度分类问题相关的一个有趣的数据集是 Caltech-UCSD Birds-200-2011(CUB-200-2011)【5】由属于 200 个物种的 11788 幅鸟类图像组成。为了解决这个问题,本教程的目标将是:(a)建立一个 CNN 模型,根据鸟类的种类对鸟类图像进行分类;(b)确定如何使用不同结构的 CNN 提高基线模型的预测精度。为此,我们将使用 PyTorch,这是最流行的深度学习开源框架之一。
本教程结束时,您将能够:
- 了解鸟类图像分类问题的基本知识。
- 确定数据驱动的图像预处理策略。
- 为图像分类创建自己的深度学习管道。
- 建立、训练和评估 ResNet-50 模型以预测鸟类。
- 通过使用不同的技术来提高模型性能。
首先,您需要下载包含数据集的归档文件,并将其存储到 data 目录中。可以通过下面的链接手动完成,或者使用下面的 GitHub 库中提供的 Python 代码:
https://github.com/slipnitskaya/caltech-birds-advanced-classification
现在,让我们导入将在本教程中使用的包:
# import packages
import os
import csvimport numpy as np
import sklearn.model_selection as skmsimport torch
import torch.utils.data as td
import torch.nn.functional as Fimport torchvision as tv
import torchvision.transforms.functional as TF# define constants
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
RANDOM_SEED = 42IN_DIR_DATA = 'data/CUB_200_2011'
IN_DIR_IMG = os.path.join(IN_DIR_DATA, 'images')
发现最佳数据转换策略
在本教程中,我们计划使用 ImageNet 数据集预先训练一个基线模型。由于预先训练的模型通常希望输入图像以同样的方式进行规范化,因此高度和宽度至少应为 224 x 224 像素。可能有许多图像变换的方法可以用来满足上述规范,但什么可能是最佳的呢?
探索性数据分析是任何数据科学项目的基本起点,为进一步分析奠定基础。因为我们对定义最佳数据转换策略感兴趣,所以我们将研究鸟类图像,看看我们能抓住什么有用的东西。让我们来看看麻雀家族的一些鸟类例子(图 3)。似乎不同种类的鸟类之间有很高的相似性,这很难发现。那是白喉雀还是林肯雀?嗯,即使是专家也会感到困惑…
图 3。难以区分的同科鸟类:白喉麻雀 vs .林肯麻雀(CUB-2002011),vs .杰克斯派洛船长(公共领域)
出于兴趣,我们将总结 Sparrow 家族的所有类,以了解我们的数据集中有多少类:
# calculate the number of sparrow species
cls_sparrows = [k for k in os.listdir(IN_DIR_IMG) if 'sparrow' in k.lower()]
print(len(cls_sparrows))
上面的代码给我们的值是 21,这意味着一个家族只能代表十几个不同的物种。现在我们看到了为什么 CUB-200-2011 是为细粒度分类而完美设计的。我们所拥有的是许多相似的鸟类,它们可能与不同的种类有关,实际上,我们计划在这里处理这个问题。
但是在进入真正的深度学习之前,我们想确定一个合适的数据预处理策略。为此,我们将通过可视化相应观察值的箱线图来分析宽度和高度的边际分布:
图 4。鸟类图像大小的可变分布。方框反映了图像大小分布(宽度和高度)的四分位范围,方框内的中间四分位线标记了数据的中点,触须分别代表 Q1 和 Q3 的较大和较小观察值,外侧点表示位于总体分布之外的数据
事实上,图像的大小变化很大。我们还看到大多数图像的高度和宽度分别等于 375 和 500 像素。那么,对于这种数据,什么是合适的转换策略呢?
转换图像和分割数据
CUB-200-2011 数据集包含数千幅图像,因此可能会影响计算时间。为了克服这个问题,我们首先创建类DatasetBirds
来简化数据加载和预处理:
class DatasetBirds(tv.datasets.ImageFolder):
"""
Wrapper for the CUB-200-2011 dataset.
Method DatasetBirds.__getitem__() returns tuple of image and its corresponding label.
"""
def __init__(self,
root,
transform=None,
target_transform=None,
loader=tv.datasets.folder.default_loader,
is_valid_file=None,
train=True,
bboxes=False): img_root = os.path.join(root, 'images') super(DatasetBirds, self).__init__(
root=img_root,
transform=None,
target_transform=None,
loader=loader,
is_valid_file=is_valid_file,
) self.transform_ = transform
self.target_transform_ = target_transform
self.train = train
# obtain sample ids filtered by split
path_to_splits = os.path.join(root, 'train_test_split.txt')
indices_to_use = list()
with open(path_to_splits, 'r') as in_file:
for line in in_file:
idx, use_train = line.strip('\n').split(' ', 2)
if bool(int(use_train)) == self.train:
indices_to_use.append(int(idx)) # obtain filenames of images
path_to_index = os.path.join(root, 'images.txt')
filenames_to_use = set()
with open(path_to_index, 'r') as in_file:
for line in in_file:
idx, fn = line.strip('\n').split(' ', 2)
if int(idx) in indices_to_use:
filenames_to_use.add(fn) img_paths_cut = {'/'.join(img_path.rsplit('/', 2)[-2:]): idx for idx, (img_path, lb) in enumerate(self.imgs)}
imgs_to_use = [self.imgs[img_paths_cut[fn]] for fn in filenames_to_use] _, targets_to_use = list(zip(*imgs_to_use)) self.imgs = self.samples = imgs_to_use
self.targets = targets_to_use if bboxes:
# get coordinates of a bounding box
path_to_bboxes = os.path.join(root, 'bounding_boxes.txt')
bounding_boxes = list()
with open(path_to_bboxes, 'r') as in_file:
for line in in_file:
idx, x, y, w, h = map(lambda x: float(x), line.strip('\n').split(' '))
if int(idx) in indices_to_use:
bounding_boxes.append((x, y, w, h)) self.bboxes = bounding_boxes
else:
self.bboxes = None def __getitem__(self, index):
# generate one sample
sample, target = super(DatasetBirds, self).__getitem__(index) if self.bboxes is not None:
# squeeze coordinates of the bounding box to range [0, 1]
width, height = sample.width, sample.height
x, y, w, h = self.bboxes[index] scale_resize = 500 / width
scale_resize_crop = scale_resize * (375 / 500) x_rel = scale_resize_crop * x / 375
y_rel = scale_resize_crop * y / 375
w_rel = scale_resize_crop * w / 375
h_rel = scale_resize_crop * h / 375 target = torch.tensor([target, x_rel, y_rel, w_rel, h_rel]) if self.transform_ is not None:
sample = self.transform_(sample)
if self.target_transform_ is not None:
target = self.target_transform_(target) return sample, target
所有预训练的模型都期望输入图像以相同的方式归一化,例如高度和宽度至少为 224 像素。正如您从我们之前的分析中注意到的,数据的大小变化很大,许多图像具有横向布局而不是纵向布局,宽度通常接近两个维度的最大值。
为了提高模型学习鸟类表示的能力,我们将使用数据增强。我们希望以这样的方式转换图像,所以我们保持纵横比。一种解决方案是统一缩放图像,以便使用最大填充策略使两个维度都等于较大的一侧。为此,我们将创建一个填充函数来填充图像到 500 像素:
def pad(img, size_max=500):
"""
Pads images to the specified size (height x width).
"""
pad_height = max(0, size_max - img.height)
pad_width = max(0, size_max - img.width)
pad_top = pad_height // 2
pad_bottom = pad_height - pad_top
pad_left = pad_width // 2
pad_right = pad_width - pad_left
return TF.pad(
img,
(pad_left, pad_top, pad_right, pad_bottom),
fill=tuple(map(lambda x: int(round(x * 256)), (0.485, 0.456, 0.406))))
假设鸟类出现在任何图像部分,我们通过在模型训练期间沿两个轴随机裁剪和翻转图像,使模型能够在任何地方捕捉它们。虽然在输入 ResNet-50 之前,测试分割的图像将被中心裁剪,但我们预计大多数鸟类将位于该图像部分,参考之前的数据探索。
为此,我们将沿着两个维度裁剪 375 x 375 像素的图像,因为这是大多数图像的平均大小。我们还将通过均值[0.485,0.456,0.406]和标准差[0.229,0.224,0.225]来归一化图像,以使像素值的分布更接近高斯分布。
# transform images
transforms_train = tv.transforms.Compose([
tv.transforms.Lambda(pad),
tv.transforms.RandomOrder([
tv.transforms.RandomCrop((375, 375)),
tv.transforms.RandomHorizontalFlip(),
tv.transforms.RandomVerticalFlip()
]),
tv.transforms.ToTensor(),
tv.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
transforms_eval = tv.transforms.Compose([
tv.transforms.Lambda(pad),
tv.transforms.CenterCrop((375, 375)),
tv.transforms.ToTensor(),
tv.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
然后,我们将 CUB-200-2011 数据集的图像组织成三个子集,以确保正确的模型训练和评估。由于数据集的作者建议集合训练和测试子集的方式,我们相应地分割我们的数据。此外,将定义验证分割,以便在模型评估过程中进一步微调模型参数。为此,训练子集将使用分层采样技术进行分割,以确保每个子集具有相同平衡的不同物种类别。
# instantiate dataset objects according to the pre-defined splits
ds_train = DatasetBirds(IN_DIR_DATA, transform=transforms_train, train=True)
ds_val = DatasetBirds(IN_DIR_DATA, transform=transforms_eval, train=True)
ds_test = DatasetBirds(IN_DIR_DATA, transform=transforms_eval, train=False)splits = skms.StratifiedShuffleSplit(n_splits=1, test_size=0.1, random_state=RANDOM_SEED)
idx_train, idx_val = next(splits.split(np.zeros(len(ds_train)), ds_train.targets))
我们将为数据加载和模型训练设置参数。为了利用计算并能够并行处理大型数据集,我们将在几个小批量中整理输入样本,并指出使用多少子过程来生成它们,以便利用训练过程。
# set hyper-parameters
params = {'batch_size': 24, 'num_workers': 8}
num_epochs = 100
num_classes = 200
之后,我们将创建一个DataLoader
对象来产生每个数据分割的样本:
# instantiate data loaders
train_loader = td.DataLoader(
dataset=ds_train,
sampler=td.SubsetRandomSampler(idx_train),
**params
)
val_loader = td.DataLoader(
dataset=ds_val,
sampler=td.SubsetRandomSampler(idx_val),
**params
)
test_loader = td.DataLoader(dataset=ds_test, **params)
构建基线 ResNet-50 分类器
我们将使用 ResNet-50 模型对鸟类进行分类。ResNet(或残差网络)是卷积神经网络的一种变体,由提出作为大型网络的消失梯度问题的解决方案。
PyTorch 在torchvision.models
上提供了 ResNet-50 模型,因此我们将实例化相应的类,并将参数 num_classes 设置为 200,给定该数量的鸟类的数据集:
# instantiate the model
model = tv.models.resnet50(num_classes=num_classes).to(DEVICE)
更具体地说,所选择的体系结构有 50 层深,由 5 个阶段组成,其中 4 个阶段具有残差块,1 个阶段包括卷积、批量归一化和 ReLU 操作。
训练和评估模型
下一点是设置我们的模型的学习率以及在训练过程中调整它的时间表,以便获得更好的性能。ResNet-50 模型的训练将使用 Adam 优化器完成,初始学习率为 1e-3,学习率按指数规律递减,例如在每个时期下降因子γ。
# instantiate optimizer and scheduler
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)
最后,我们准备训练和验证我们的模型,以识别和学习鸟类之间的差异。交叉熵损失和准确性度量将在每个时期累积,以便检查模型性能动态。在所有的训练实验之后,我们使用以前未见过的数据子集来测试该模型,以使用准确性度量来评估鸟类分类的总体良好性。
# loop over epochs
for epoch in range(num_epochs):# train the model
model.train()
train_loss = list()
train_acc = list()
for batch in train_loader:
x, y = batch
x = x.to(DEVICE)
y = y.to(DEVICE)
optimizer.zero_grad() # predict bird species
y_pred = model(x) # calculate the loss
loss = F.cross_entropy(y_pred, y) # backprop & update weights
loss.backward()
optimizer.step() # calculate the accuracy
acc = skms.accuracy_score([val.item() for val in y], [val.item() for val in y_pred.argmax(dim=-1)])
train_loss.append(loss.item())
train_acc.append(acc)
# validate the model
model.eval()
val_loss = list()
val_acc = list()
with torch.no_grad():
for batch in val_loader:
x, y = batch x = x.to(DEVICE)
y = y.to(DEVICE) # predict bird species
y_pred = model(x)
# calculate the loss
loss = F.cross_entropy(y_pred, y) # calculate the accuracy
acc = skms.accuracy_score([val.item() for val in y], [val.item() for val in y_pred.argmax(dim=-1)]) val_loss.append(loss.item())
val_acc.append(acc) # adjust the learning rate
scheduler.step()# test the model
true = list()
pred = list()
with torch.no_grad():
for batch in test_loader:
x, y = batch x = x.to(DEVICE)
y = y.to(DEVICE) y_pred = model(x) true.extend([val.item() for val in y])
pred.extend([val.item() for val in y_pred.argmax(dim=-1)])# calculate the accuracy
test_accuracy = skms.accuracy_score(true, pred)
print('Test accuracy: {:.3f}'.format(test_accuracy)
图 5 描述了 ResNet-50 的模型性能指标:
图 5。相对于基线 ResNet 50 的历元数的交叉熵损失和准确性度量
正如我们看到的,基线模型表现很差,因为它过拟合。其中一个主要原因是缺乏多样化的训练样本。简单说明一下:CUB-200-2011 数据集每个物种有大约 30 张图片。似乎我们被困住了…不是吗?事实上,我们有一些方法可以解决这些问题。
推进深度学习模型
我们在之前的分析中遇到了许多挑战,因此我们可以开始思考如何解决这些后续问题:
- 问题 1:在训练样本数量有限的情况下,如何处理过拟合问题?
- 问题 2:如何提高鸟类物种识别中的模型性能?
让我们弄清楚如何更详细地推进我们的基线模型。
在训练样本数量有限的情况下,如何处理过拟合?
就像之前说的,深度神经网络需要大量的训练样本。从业者注意到,为了从头开始训练深度神经网络,数据量应该随着可训练参数的数量呈指数增长。幸运的是,在更大数据集上训练的模型的泛化能力可以转移到另一个通常更简单的任务中。
为了提高基线模型用于鸟类分类的性能,我们将使用从 ImageNet 数据集上预训练的通用模型获得的权重初始化,并使用 CUB-200-2011 进一步微调其参数。训练过程保持不变,而模型将更侧重于超参数的微调。
PyTorch 在torch.utils.model_zoo
中提供预先训练好的模型。可以通过将 pretrained=True 传递给构造函数来构造预训练的 ResNet-50。这个简单的技巧为我们提供了已经初始化好的过滤器的模型,所以没有必要从头开始学习它们。
# instantiate the model
model = tv.models.resnet50(num_classes=200, pretrained=True).to(DEVICE)
我们还将在优化器中设置一个较低的学习率 1e-4,因为我们将训练一个已经在大规模图像分类任务上进行过预训练的网络。结果如下:
图 6。针对预训练 ResNet 50 的历元数的交叉熵损失和准确度度量
如我们所见,使用预训练模型允许解决过度拟合问题,给出 80.77%的测试准确度。让我们继续做这个实验吧!
如何提高鸟类物种识别中的模型性能?
解决方案 1:多任务学习
现在我们可以进一步扩展这种方法。如果我们可以添加另一个任务,为什么我们必须增加单个任务的复杂性?完全没有原因。人们注意到,引入附加的辅助任务提高了网络的性能,迫使它学习训练数据的更一般的表示。
由于 Caltech-UCSD Birds-200–2011 数据集除了类别标签之外还包括边界框,我们将使用这个辅助目标来使网络以多任务的方式进行训练。现在,我们将通过将数量类设置为 204 来预测鸟的边界框的 4 个坐标以及它的种类:
# instantiate the pre-trained model
model = tv.models.resnet50(num_classes=204, pretrained=True).to(DEVICE)
现在,我们需要稍微修改一下我们的训练和验证模块,因为我们想要预测和计算对应于正确鸟类及其边界框坐标的两个目标的损失。下面是一个执行示例:
...y_pred = model(x)# predict bird species
y_pred_cls = y_pred[..., :-4]
y_cls = y[..., 0].long()
# predict bounding box coordinates
y_pred_bbox = y_pred[..., -4:]
y_bbox = y[..., 1:]# calculate the loss
loss_cls = F.cross_entropy(y_pred_cls, y_cls)
loss_bbox = F.mse_loss(torch.sigmoid(y_pred_bbox), y_bbox)
loss = loss_cls + loss_bbox...
图 7。使用辅助任务增强的预训练 ResNet-50 的交叉熵损失和相对于历元数的准确度度量
结果甚至更好——辅助任务的整合提供了 81.2%的测试分割精度点的稳定增长——如图 7 所示。
解决方案 2:注意力增强的 CNN
在最后几段中,我们重点关注了模型的数据驱动进步。然而,在某些时候,任务的复杂性可能会超过模型的能力,从而导致较低的性能。为了根据问题的难度来调整模型的能力,我们可以给网络配备额外的注意力模块,帮助它专注于输入的重要部分,忽略不相关的部分。
class Attention(torch.nn.Module):
"""
Attention block for CNN model.
"""
def __init__(self, in_channels, out_channels, kernel_size, padding):
super(Attention, self).__init__() self.conv_depth = torch.nn.Conv2d(in_channels, out_channels, kernel_size, padding=padding, groups=in_channels)
self.conv_point = torch.nn.Conv2d(out_channels, out_channels, kernel_size=(1, 1))
self.bn = torch.nn.BatchNorm2d(out_channels, eps=1e-5, momentum=0.1, affine=True, track_running_stats=True)
self.activation = torch.nn.Tanh() def forward(self, inputs):
x, output_size = inputs
x = F.adaptive_max_pool2d(x, output_size=output_size)
x = self.conv_depth(x)
x = self.conv_point(x)
x = self.bn(x)
x = self.activation(x) + 1.0return x
注意模块允许高亮显示特征图的相关区域,并返回在范围[0.0,2.0]内变化的值,其中较低的值意味着给定像素对于后续层的较低优先级。因此,我们将创建并实例化与注意力增强的 ResNet-50 模型相对应的类ResNet50Attention
:
class ResNet50Attention(torch.nn.Module):
"""
Attention-enhanced ResNet-50 model.
""" weights_loader = staticmethod(tv.models.resnet50) def __init__(self, num_classes=200, pretrained=True, use_attention=True):
super(ResNet50Attention, self).__init__() net = self.weights_loader(pretrained=pretrained)
self.num_classes = num_classes
self.pretrained = pretrained
self.use_attention = use_attention net.fc = torch.nn.Linear(
in_features=net.fc.in_features,
out_features=num_classes,
bias=net.fc.bias is not None
) self.net = net if self.use_attention:
self.att1 = Attention(in_channels=64, out_channels=64, kernel_size=(3, 5), padding=(1, 2))
self.att2 = Attention(in_channels=64, out_channels=128, kernel_size=(5, 3), padding=(2, 1))
self.att3 = Attention(in_channels=128, out_channels=256, kernel_size=(3, 5), padding=(1, 2))
self.att4 = Attention(in_channels=256, out_channels=512, kernel_size=(5, 3), padding=(2, 1)) if pretrained:
self.att1.bn.weight.data.zero_()
self.att1.bn.bias.data.zero_()
self.att2.bn.weight.data.zero_()
self.att2.bn.bias.data.zero_()
self.att3.bn.weight.data.zero_()
self.att3.bn.bias.data.zero_()
self.att4.bn.weight.data.zero_()
self.att4.bn.bias.data.zero_() def _forward(self, x):
return self.net(x)
def _forward_att(self, x):
x = self.net.conv1(x)
x = self.net.bn1(x)
x = self.net.relu(x)
x = self.net.maxpool(x) x_a = x.clone()
x = self.net.layer1(x)
x = x * self.att1((x_a, x.shape[-2:])) x_a = x.clone()
x = self.net.layer2(x)
x = x * self.att2((x_a, x.shape[-2:])) x_a = x.clone()
x = self.net.layer3(x)
x = x * self.att3((x_a, x.shape[-2:])) x_a = x.clone()
x = self.net.layer4(x)
x = x * self.att4((x_a, x.shape[-2:])) x = self.net.avgpool(x)
x = torch.flatten(x, 1)
x = self.net.fc(x) return x
def forward(self, x):
return self._forward_att(x) if self.use_attention else self._forward(x) # instantiate the model
model = ResNet50Attention(num_classes=204, pretrained=True, use_attention=True).to(DEVICE)
之后,我们准备训练和评估注意力增强模型的性能,该模型在 ImageNet 数据集上进行了预训练,并使用我们之前使用的相同代码进行了鸟类分类的多任务学习。最终准确度分数增加到 82.4%!
图 8 显示了分析过程中生成的总结结果:
图 8。使用不同技术的 ResNet-50 advanced 的性能比较
结果清楚地表明,具有迁移和多任务学习以及注意力模块的 ResNet-50 模型的最终变体大大有助于更准确的鸟类预测。
结论
在这里,我们使用不同的方法来提高基线 ResNet-50 的性能,用于 CUB-200–2011 数据集的鸟类分类。我们能从中学到什么?以下是我们分析得出的一些信息:
- 数据探索结果表明,CUB-200–2011 是一个高质量、平衡但有中心偏差的数据集,没有损坏的图像。
- 在训练样本数量有限的情况下,您可以在自己的模型中重用在另一个数据集上预先训练的模型权重。
- 除了主要的鸟类分类之外,通过辅助任务进行学习有助于更好的模型性能。
- 通过添加新的层(注意模块)来增强网络的架构,使得模型在鸟类分类中更加准确。
- 对基本 ResNet-50 的不同扩展的分析表明,使用辅助任务和注意机制的预训练模型是进一步研究的突出候选。
总之,模型性能还有改进的空间。通过进一步优化模型超参数、使用更强的数据扩充、正则化、元学习技术,可以实现额外的进步。
更多即将推出!
下一篇教程的重点将是深度学习模型的可解释性。有兴趣继续吗?
在 https://medium.com/@slipnitskaya 的订阅更多深度学习材料并保持更新。
参考
- 应用于手写邮递区号辨识的反向传播。神经计算 1.4(1989):541–551。
- 基于梯度的学习应用于文件识别。IEEE 86.11 会议录(1998):2278–2324。
- 克里日夫斯基、亚历克斯、伊利亚·苏茨基弗和杰弗里·e·辛顿。"使用深度卷积神经网络的图像网络分类."美国计算机学会 60.6 (2017 年)通讯:84–90。
- 何,,等,“深度残差学习在图像识别中的应用”IEEE 计算机视觉和模式识别会议论文集(2016):770–778。
- 加州理工学院-加州大学圣迭戈分校鸟类 200–2011 数据集。计算与神经系统技术报告,CNS-TR-2011–001。(2011).
利用暹罗网络和扩张卷积进行鸟鸣分类
介绍
T2:声学在研究环境方面非常有用。它已经被用于跟踪潜艇和鲸鱼很长时间了。鸟类对塑造我们身边的植物有很大帮助。识别鸟鸣对于自动监测野生动物和研究鸟类的行为非常重要。它可以帮助追踪鸟类而不打扰它们。我们还可以知道在某个特定的地方存在着哪些鸟类。它能给我们一些关于它们迁移模式的信息。每种鸟都有它们独特的声音。它们用不同长度和复杂程度的歌声来吸引配偶,警告其他鸟类附近的危险,并标记它们的领地。鸣禽可以根据地理位置有不同的方言。但是非鸣禽发出的声音不会因为地理位置而有太大变化。使用深度学习方法,我们可以很容易地根据鸟类的叫声和歌声对它们进行分类。我们可以使用任何神经网络框架,如 CNN、暹罗网络、WaveNets 等。为了这个任务。
问题陈述
目标:我们想根据不同鸟类的鸣叫声样本对它们进行分类。我们可以提取音频样本的频谱图,并使用它们作为分类的特征。在这个实验中,我们将使用 Kaggle 上的英国鸟鸣数据集。数据集在数据描述部分描述。
暹罗网络和膨胀卷积的快速介绍
暹罗网络
之前写过一篇暹罗网的文章。可以去查一下,深入了解一下它的工作和为它使用的损耗函数。该文章中还提供了代码。但是,我在这里给大家总结一下暹罗网。
**连体网络是一类包含一个或多个相同网络的神经网络。**我们向这些网络提供一对输入。每个网络计算一个输入的要素。然后,使用特征的差或点积来计算特征的相似性。对于相同的类输入对,目标输出是 1,而对于不同的类输入对,输出是 0。记住,两个网络有相同的参数和权重。如果不是,那他们就不是暹罗人。
暹罗网络基本结构
不同的损失函数可用于连体网络。
- 对比损失:在对比损失中,取一对输入。对于相同的类对,它们之间的距离较小。对于不同的对,距离更多。尽管二进制交叉熵似乎是我们问题的完美损失函数,对比损失在区分图像对方面做得更好。对比损失,L*=Y * D+(1-Y) max(margin—D,0)
- 三重丢失:三重丢失是 Google 在 2015 年推出的人脸识别。这里,模型有三个输入——锚、积极和消极。锚点是一个参考输入。实际输入与锚输入属于同一类别。负输入属于锚类之外的随机类。我们必须最小化锚和阳性样本之间的距离,同时最大化锚和阴性样本之间的距离。
我们将在实验中使用三重态损失。
扩张的回旋
膨胀卷积是一种卷积,它通过在内核元素之间插入孔洞来“膨胀”内核。它们也被称为 atrous 卷积。
扩展卷积的概念来自于小波分解,其中母小波被不同的尺度缩放或扩展以捕获不同的频率。
(a)标准 3×3 内核,(b)膨胀因子为 2 的内核,©膨胀因子为 4 的内核。来源原论文
扩张卷积以相同的计算和存储成本增加了感受野,并且不损失分辨率。它可以用相同数量的参数从整个输入中捕获上下文。
这是 Sik-Ho Tsang 写的一篇关于膨胀的脑回的好文章。
数据描述
在这个实验中,我们使用了 Kaggle 上的英国鸟鸣数据集。这是从 Xeno Canto 数据收集中收集的一个小子集,形成了英国 88 种鸟类的平衡数据集。
这里我们只对 9 种鸟类进行分类:加拿大鹅腐尸鸦煤山雀普通乌鸫普通燕雀普通红雀普通朱雀普通黑水鸡普通夜莺。
在这个数据集中,每只鸟的样本都很少。音频样本大约有 40-60 秒长。其中一些有点吵,有时背景中还有其他的鸟。从具有 50%重叠的每个样本中提取 2 秒钟的剪辑,以创建新样本。这将为训练神经网络创建足够数量的样本。数据分为 60%用于训练,40%用于测试。
履行
特征抽出
Librosapython 中的库用于音乐和音频分析。我们可以读取音频文件,并用它提取频谱图。
**第一步:**使用 **librosa 读取音频文件。**将-1 和 1 之间的时间序列归一化。
步骤 2: 去除音频中的静音。
步骤 3: 将每个音频文件分割成 2 秒长的片段,重叠 50%。
**第四步:**将数据分为训练和测试。60%用于培训,40%用于测试。
步骤 5: 从样品中提取光谱图。一个过滤器被应用到频谱图中,以获得一个在1 千赫和8 千赫之间的频率范围,因为大多数鸟鸣声的频率都在这个范围内。现在,标准化所有的光谱图(你也可以在 0 到 1 之间标准化它们)。这里,每个声谱图的形状是 163×345。
常见乌鸫鸣声的样本声谱图
**第六步:**为暹罗网络生成正负样本对。
首先,生成正对。
现在,生成负的类对。
您可以使用下面的函数生成正对和负对。它以: 输入特征、目标类别标签、从每个类别中取出的随机数的数目、 和 正对的数目作为输入。 返回锚点、阳性和阴性样本。
现在,数据已准备好用于暹罗网络。
我们有 3 种类型的输入:锚、阳性和阴性样本。每个输入的形状是:(10800 x 345 x 163)。
现在,我们需要建立一个神经网络。
建立神经网络
编码器模型包含 8 个 1-D 卷积层,具有指数增长的膨胀因子。之后,应用 1D 卷积层来减少特征的数量。最后,应用全局最大池 1D 层。每一层后应用一个批量标准化层。除了最后一层,所有层都有一个’【reLu】‘激活。在最后一层应用’'激活。在最后一层,我们得到一个 32 维向量作为输出。
创建了该编码器模型的三个实例。它们代表锚定输入、积极输入和消极输入。所有三个 32 维特征向量被连接成一个 96 维向量。这个连接的向量被视为输出。正如您在下面的代码中看到的,函数 triplet_loss 获取输出,再次分离 3 个嵌入,并计算损失。
下面你可以看到模型的代码。
以下是编码器型号摘要:
编码器型号摘要
编码器模型架构
现在我们有了一个完整的暹罗网络。
训练模型
我们现在可以拟合模型了。模型的目标输出是虚拟输出。这是因为我们没有将模型的输出与目标输出进行比较。相反,我们只是最小化相同类嵌入之间的距离,并推开不同类嵌入。
我们的锚定样本、正样本和负样本的输入大小是:(10800 x 345 x 163)。批量大小设置为 256。由于批量标准化,该模型仅在 30 个时期内收敛。如果你愿意,你可以训练更长时间。
结果
该模型在训练集上的准确率为 98.1% ,在测试数据集上的准确率为 97.3% 。
以下是测试数据集的标准化混淆矩阵:
测试数据集的混淆矩阵
以下是嵌入的相似性矩阵:
测试数据集嵌入的相似性矩阵
下面是对测试数据集嵌入应用 PCA 后的散点图。所有的班级之间有很好的区分。并且,除了几个样本,所有的类都聚集在一起。
应用 PCA 后测试数据集嵌入的散点图
要点
- 这里光谱图被用作特征。也可以使用 Mel 光谱图。
- 音频的小波变换也可以作为特征。
- 可以用更长的框架尺寸进行实验。过长的帧尺寸会使模型过拟合,并降低整体性能。
- 批处理规范化层起着非常重要的作用。它使训练期间的小批量标准化,并解决了内部协变量移位的问题。它使训练更快,模型变得更健壮。我强烈推荐在你的模型中使用这个。
- 如果你想对更多种类的鸟类进行分类,模型的集合肯定会进一步提高精确度。
结论
暹罗网络成功地根据鸟类的叫声对它们进行了分类,准确率高达 97%。连体网络将分类问题转化为相似性问题。它们也可以使用更少的样本,因为我们生成的是成对的样本。具有扩展的 1-D 卷积的模型以及批量标准化层收敛得非常快。
这里是这个项目的 GitHub 库。
*https://github.com/AdityaDutt/Bird-Song-Classification
未来的实验想法
这个模型可以扩展到对 50 或 100 多只鸟进行分类。我对鸟类了解不多,因为这不是我的研究领域,但我非常好奇,想知道我们是否可以根据鸟类的叫声来识别不同的地理位置。这是因为鸣禽的方言随着地理位置的变化而变化。这里是一个 GitHub 储存库,其中包含了与鸟类相关的数据集列表。Xeno-canto 网站收录了世界各地的鸟鸣。它有不同国家、物种等的数据。您可以从这里为自己的项目选择一个数据集。
请记住,您可以将该模型用于类似的基于音频的任务,如说话者分类、情感检测等。
**非常感谢您的阅读!我希望它有帮助。🐧
如果你想合作一个项目,或者需要一些关于你的项目的想法,请随时联系我。
参考
https://arxiv.org/abs/1609.03499 https://arxiv.org/abs/1511.07122v3 https://www.kaggle.com/rtatman/british-birdsong-dataset https://github.com/AgaMiko/bird-recognition-review https://arxiv.org/abs/1502.03167 https://machinelearningmastery.com/how-to-accelerate-learning-of-deep-neural-networks-with-batch-normalization/ https://machinelearningmastery.com/batch-normalization-for-training-of-deep-neural-networks/ https://www.xeno-canto.org/ *
机器学习中的二分 K 均值聚类算法
理解对分 K-均值聚类算法(视觉和代码)
来自源的修改图像
B isecting K-means 聚类技术是对常规 K-means 算法的一点修改,其中您固定了将数据划分为聚类的过程。所以,类似于 K-means,我们首先初始化 K 个质心*(你可以随机地做或者可以有一些先验)*。之后我们应用规则的 K-means,K=2 *(这就是为什么单词平分)。我们不断重复这一对分步骤,直到达到期望的聚类数。在第一次对分(当我们有 2 个聚类)完成,*之后,人们可以想到多种策略来选择其中一个聚类,并在该聚类内重复对分和分配的整个过程——例如:选择具有最大方差的聚类或展开的聚类,选择具有最大数量数据点的聚类,等等。
没有时间看完整个博客?然后看这个快速的< 60 秒的 YouTube 短片—
你可以想象整个流程,如下图所示—
按作者划分 K-Means |图像的步骤
如上图所示,我们首先假设所有数据都在一个聚类*(第一张图)中,在第一步之后,我们得到 2 个(二分法)聚类,然后我们检查是否达到了期望的聚类数。如果没有,我们从上一步的两个聚类中选择一个(红色)*,再次应用 K=2 的 K-means,并且我们重复“检查和“二等分”步骤。
你可能已经猜到了,这看起来像是层次聚类和 K 均值聚类的混合。因为在这里您正在构建一个树,这是一个层次结构,其中一个节点根据 K-means 策略和赋值被分成两个子节点。
对 K-Means 的改进
与常规 K-Means 不同,在常规 K-Means 中,我们在每一步计算每个数据点和质心之间的距离,直到满足收敛标准,在这里,我们只做一次*(第一步)*,之后,我们只使用特定聚类中的数据点来计算距离和进一步细分,使其比常规 K-Means 更有效。它还可以识别任何形状和大小的簇,不像 K-Means 假设的是球形簇。 我还发现了一篇有趣的研究文章,比较了 K-Means 和二分法 K-Means 在分析网络日志数据时的性能——在这里阅读https://ijeter.everscience.org/Manuscripts/Volume-4/Issue-8/Vol-4-issue-8-M-23.pdf。
限制
因为基本聚类技术在这里仍然是 K-Means,所以在离群值的情况下,这个算法也可能遭受对聚类中心的错误估计。
***提示:*二等分 K-Medoid 聚类技术可以帮助你解决上述限制。
密码
我将使用 Python 的 Sklearn 库来实现目的—
**from sklearn.cluster import KMeans
import numpy as np**X = np.array([[1, 2], [2, 1], [1, 1.5], [1.5, 1],
[10, 2], [10, 4], [10, 0], [10, 1],
[1, 10], [2, 11], [1.5, 9], [1, 10.5],
[10.5, 9], [9, 9.5], [9.5, 9], [10, 10]])**K = 4
current_clusters = 1**while current_clusters != K:* *kmeans = KMeans(n_clusters=2).fit(X)
current_clusters += 1
split += 1* *cluster_centers = kmeans.cluster_centers_* *sse = [0]*2
for point, label in zip(X, kmeans.labels_):
sse[label] += np.square(point-cluster_centers[label]).sum()* *chosen_cluster = np.argmax(sse, axis=0)
chosen_cluster_data = X[kmeans.labels_ == chosen_cluster]
X = chosen_cluster_data**
下图展示了上述示例代码的图示演练—
上例演练|作者图片
简而言之,当考虑将 K-Means 作为算法的选择时,尝试平分 K-Means 也是一个好主意。由于上面讨论的原因,很有可能你会得到更好的结果。
希望你喜欢阅读这篇博客。感谢您的宝贵时间!
比特币财源滚滚!
比较 GRU、LSTM 和比尔斯特姆预测比特币价格的有效性
图片来源:andréFran ois McKenzie 在 Unsplash 上拍摄的照片
介绍
我已经跟踪加密价格好几年了。我对区块链的演变及其含义很感兴趣。我不止一次对数字货币的想法嗤之以鼻。这并不是什么新东西,但我出生在 80 年代,那时如果我们想提取真正的纸币,我们必须填写一张纸并与人交谈……还记得纸币吗?
无论如何,今天我想和你分享我最近的一个项目。我将比较三种模型,以确定它们在预测加密之王比特币价格方面的功效。在这个项目中,我使用了门控循环单元(GRU)、长短期记忆单元(LSTM)和双向 LSTM 单元(比尔斯特姆)。首先,让我们快速了解一下这些神秘预测模型的工作原理。
自 90 年代以来,人们对递归神经网络(RNNs)进行了大量的研究。RNNs 的基本功能是处理序列数据的能力,使其成为探索涉及文本、音频、图像、videos⁴和金融市场的任务的宝贵工具,例如我们将要探索的市场。想了解 RNNs 如何工作的有趣方法,请查看这篇有创意的帖子!
当我们开始大量使用 data⁵.时,RNNs 的局限性就变得很明显了例如,为了这篇文章,我收集了大约 7 年的比特币价格数据。这意味着我有超过 2500 个时间步长的输入。因此,每次模型更新时,将对这些输入中的每一个计算衍生产品。这可能导致权重下降到接近零(消失梯度)或爆炸(爆炸梯度),这意味着模型学习缓慢。因为 rnn 在学习大型数据集的早期输入时有困难,所以他们被称为具有短期记忆。
为了克服这个问题,开发了专门的 rnn。长短期记忆单位(LSTM)、双向 LSTM (BiLSTM)和门控循环单位(GRU)。这些模型使用称为“门”的内部机制,可以调节信息在网络中的流动方式。最终,他们决定哪些信息是重要的,要保留或丢弃(如果你想更深入,你可以看看这篇帖子)。
现在,我们已经讨论了一些关键概念,让我们开始工作,看看这些模型如何作为比特币价格的预测器!
首先,让我们看一下我们的数据。下图 1 展示了比特币价格随时间的演变。虽然波动比较大,但我必须承认,2013 年我没有购买比特币,这让我有点郁闷!
图 1——比特币收盘价随时间的演变(图片由作者提供)。日期范围(2013 年 4 月 29 日-2021 年 1 月 12 日)。数据收集自https://coinmarketcap . com/currences/bit coin/historical-data/
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
数据准备
移位数据
在我们开始构建模型之前,需要几个重要的步骤。第一步是将我们的结果变量上移(-1)一个时间步长。目的是组织我们的数据,以便预测变量将用于预测第二天的收盘价,TOMORROW_CLOSE。
因为我不想用一个不存在的值来估算空单元格,所以我用 0 填充它,并将其从数据集中删除。另一种方法是将所有预测值下移(+1)。这将导致在数据集的开头插入 0,这将接近 2013 年的初始值。
将“日期”转换为日期时间
接下来,使用 df.dtypes,我们可以检查数据集中存在的变量类型。使用时,这总是一个好主意。csv(或其他)文件类型,以确保该列已被正确读取。例如,“日期”列在 Excel 中是日期格式,但在导入时它被解释为对象。因此,我们必须使用下面的代码将其转换为日期时间。
将日期设置为索引
接下来,我们必须将列“Date”设置为索引。如果您跳过这一步,您将得到以下错误:无效的类型提升。这一步对我来说真的很难。试着在谷歌里输入‘无效类型推广’,你会找到 29,100,000 个结果!
顺便提一下,您还可以在读取。使用 index_col = 'Date '的 csv 文件
将数据分成训练集和测试集
现在,我们的数据已经准备好分成训练集和测试集。我分别为训练和测试数据选择了 80/20 的分割。让我们看看这给我们带来了什么(图 2)。
图 2 —数据分为 80%的训练集(蓝色)和 20%的测试集(橙色)。(图片由作者提供)
独立的预测因素和结果变量
到目前为止,一切看起来都不错!现在,在开始训练我们的模型之前,我们需要分离我们的预测变量(X)和结果变量(y)。
缩放数据
许多机器学习模型的一个良好实践是将数值变量缩放到指定的范围。因为这些模型的默认激活函数是双曲正切(tanh)函数,它输出的范围是从-1 到 1,所以我调整了值以适应这个范围。为此,我使用了 sklearn 的 MinMaxScaler。遵循以下 3 条准则被视为良好实践:
- 首先,我们使用 fit() 函数将缩放器拟合到训练集
- 接下来,我们使用*变换()*函数将该比例应用于训练集
- 最后,您将量表应用于测试集
创建三维数据集
我们模型的输入数据必须是带有形状[批次、时间步长、特征]的 3D 数据(参见 Keras 说明此处)。下面,一个名为 threeD_dataset 的函数将数据重塑为必要的格式。时间步长设置为 10,这意味着模型将根据前 10 天的输入来预测“明天 _ 收盘”。
构建模型
最后,我们将创建用于预测比特币价格的 3 个模型。所有模型都保持简单,具有包含 64 个神经元的两层和包含 1 个神经元的致密层。我还增加了 20%的辍学,以防止过度拟合。最后,我使用 adam 作为优化函数,使用均方差作为这些模型的度量。有关这些层和函数如何工作的更多信息,请查看我的上一篇文章,在那里我详细介绍了相当多的细节!
拟合模型
现在是时候拟合模型了,看看它们对比特币价格的预测有多准确!我将为这些模型运行 100 个纪元;然而,我添加了 EarlyStopping ,耐心为 10,当验证损失在 10 个时期内没有改善时,它会阻止模型继续运行。这可以节省大量的时间,尤其是在处理大量数据集的情况下。
需要注意的是,因为我们处理的是时间序列数据,时间步长的顺序是有意义的,所以我们必须设置 shuffle = False。
结果
预测的反向标度
在我们可以对数据进行预测之前,我们必须使用逆变换将结果变量返回到其原始形式。回想一下,我们将数据的范围从-1 调整到 1。如果我们不使用反函数,那么我们的预测将在这个标度范围内。
根据测试数据进行预测
现在是我们期待已久的时刻!我们将看到哪个模型在未知测试集上预测比特币价格的表现最好。这些试验的结果如下图 3 所示。
图 3-比尔斯特姆、LSTM 和 GRU 模型的预测图。(图片由作者提供)
正如我们所看到的,当目视检查时,来自 LSTM 和比尔斯特姆的预测值都非常好地跟随试验数据,直到在数据集的末尾值急剧上升。另一方面,GRU 模型似乎有最好的整体拟合。即使它没有紧紧跟随真实的数据,但它似乎更善于跟随数据中呈现的趋势。
韵律学
以上给出了数据的定性解释。这里,创建了一个函数来为每个模型提供定量的准确性度量。所有三个模型都提供了平均绝对误差(MAE)和均方根误差(RMSE)。关于这两个指标的解释,请查看这篇文章。
BiLSTM:
- 梅:913.15
- RMSE: 2052.10
LSTM:
- 梅:1286.47
- RMSE: 2459.72
GRU:
- 梅:1334.79
- RMSE 1934.90
我们应该如何解读这些价值观?首先我说一下,我不是专业交易者。我只能提供一个观点。MAE 似乎更符合图 3 中观察到的紧密配合。比尔斯特姆和 LSTM 都有较低的 MAE 值,表明模型更准确(当使用这一指标时)。相反,GRU 有较低的 RMSE,建议使用更准确的模型(使用此指标)。
在我看来,交易不像彩票。我们的目标不是准确知道资产的未来价格。更好的策略是预测未来价格的走向。
比如知道明天 1 比特币会值 42749.67,没有上下文就没用了。然而,如果我知道今天 1 比特币的价值是 39,118.33,那么预测的上涨趋势将会更有价值。这将引导我决定买或卖。
考虑到这一点,我觉得 GRU 模型作为趋势指标比价格指标表现得更好。接下来,我们将使用 GRU 模型来预测未来的价格!
预测
我在调查时间序列数据的时候经常用到这三个模型,不得不承认每次都很惊讶。如果你回头看看图 2,它描绘了训练集和测试集,它们没有太多的共同点。模型可以学习训练集中的模式,这对预测测试中相对不同的模式是有用的,这一事实令人难以置信!
在这最后一部分,我一直等到积累了足够的数据,可以使用新的数据集预测比特币的价值。因为我使用了 10 的时间步长,所以我至少需要 10 天的额外时间。之前的数据集结束于 2021 年 1 月 12 日,然而,在将收盘价上移后,它被从数据集中删除,以避免必须用一个虚构的值来估算值。因此,预测数据集包含 2021 年 1 月 12 日至 23 日的数据。
值得一提的是,所有相同的数据准备方法都应用于新的预测数据集。为了避免重复的材料,我在课文中省略了它们。
在下面的图 4 中,你会发现我预测比特币未来价值的结果。我有足够的数据来预测两个未来值。
图 4-预测 GRU 模型对比特币价格进行预测。(图片由作者提供)
1 月 22 日
- 预测:28226.11
- 实际:33,005.76
1 月 23 日
- 预测值:27468.18
- 实际:32,067.64
结论
在本文中,我测试了 LSTM、比尔斯特姆和 GRU 模型在预测比特币价格方面的功效。在提供了模型的一般概述之后,我描述了准备数据以避免可怕的模糊错误消息的方法。
在训练完这些模型后,他们每个人都要接受*测试。*每一个都表现得相当好,但 GRU 模型被发现是更好的趋势指标,我觉得从交易的角度来看,它更相关。
当 GRU 模型用于预测价值时,它表现得相当好。虽然价格没有准确预测,但趋势是正确的!
如果你想玩这些模型,请在我的 Github 上查看完整的笔记本。我想保持一切简单,但模型肯定可以改进。尝试:
- 其他预测变量
- 改变时间步长
- 将数据移动 2 或 3 天,观察结果如何变化
- 改变模型本身的参数(层、删除、激活函数、优化器等)
写这篇文章让我很开心。非常感谢您花时间阅读它!
记住,解决你的研究问题的最好方法是带着孩子般的好奇心!玩得开心!
参考
1.王芳,郭,秦,雷,张(2017)。用于场景文本识别的隐马尔可夫模型引导卷积递归神经网络。IET 计算机视觉,11(6),497–504。
2.Sak,h .,Senior,a .,Rao,k .,和 Beaufays,F. (2015 年)。用于语音识别的快速准确的递归神经网络声学模型。arXiv 预印本 arXiv:1507.06947。
3.牟,李,加米西,p .,,朱,X. X. (2017)。用于高光谱图像分类的深度递归神经网络。IEEE 地球科学与遥感汇刊,55(7),3639–3655。
4.Güera,d .,& Delp,E. J. (2018 年 11 月)。基于递归神经网络的深度伪造视频检测。2018 年第 15 届 IEEE 高级视频和信号监控国际会议(AVSS)(第 1-6 页)。IEEE。
5.茨韦塔纳·斯特灵、北卡罗来纳州法鲁吉亚和弗吉尼亚州格里彭(2017 年)。普通 rnns 和 gru 模型之间的本质区别。COGNTIVE 2017,84。
被“数据科学的错误”所困扰
当数据科学成为一种困扰。
很长一段时间以来,我一直想知道为什么一些数据科学家每天醒着的时候都在痴迷地消耗知识,磨练技能,参加比赛,创建爱好项目,并普遍扩大他们的视野。与此同时,其他人则更安于现状,利用现有技能来解决他们遇到的问题。
当然,任何称职的数据科学家都会不断扩展和提高他们的技能;这是成为一名优秀数据科学家的先决条件之一。这不是我在这里谈论的。我说的是让继续学习的人。我说的是当它几乎成为一种疾病时,你不得不花费你所有的空闲时间,不管你有多累或多忙,阅读更多的论文和编写更多的代码。
在力量运动中,有一个“铁虫子”的概念——如果被这个虫子“咬”了,就不再是如何年复一年保持训练的问题;取而代之的是一个问题,那就是如何才能而不是做到这一点。突然锻炼比实际锻炼花费更多的精力。我们可以在其他领域找到类似的困扰,但我发现“数据科学”有着特殊的吸引力。在我的旅程中,我遇到了几个和我一样被数据科学 bug“咬”了一口的人。
我喜欢数据科学。我喜欢这个领域的广阔,以及它在许多方面是如何“*无界的,”*从基本的数学概念一直延伸到实现商业价值。最重要的是,我喜欢在这个领域投入工作以取得进展:花无数的时间阅读我理解能力范围内的论文,或者连续几天拼命实现和调试一些新代码。请注意,这些都不是与我日常工作相关的文件,也不是会公之于众的代码。的确,我绝不是最有成就的数据科学家,但每隔半年左右,我都会在回顾过去并看到自己从原地进步了多少时获得巨大的满足感。
为什么有些人能理解这种困扰,而有些人却不能?这并不是因为智力或技术能力的差异,因为我见过很多非常有能力的人不痴迷。我也不认为这是由于环境、环境或教养。相反,它可能归结为某些人格特征的存在,最终,一些人只是遗传易感,而另一些人是免疫的。至少,我相信从行为遗传学的角度来看,这是一个结论。事情就是这样。
不管我上面宣称的对数据科学的热爱,我不希望任何人染上这个毛病。当你没有睡好,工作了漫长而疲惫的一天,和一个过度疲劳的孩子度过了艰难的下午,以及一个艰难的晚上试图让同一个孩子入睡时,你会做什么?阅读论文或编写一个新的爱好项目?对于这种有时近乎自我毁灭的行为,我的内部观众用自尊和自信来回报我。这是以精疲力尽为代价的,矛盾的是,这是一种对自己的进步或能力永远不满意的感觉。
对于那些未受此病毒影响的人,我羡慕你们。
对于拥有它的人,我向你们致敬。
面向可解释人工智能的黑盒和白盒模型
第一部分 | 第二部分 |可解释的人工智能——第三部分
使用模型属性、局部逻辑表示和全局逻辑表示从黑盒模型中生成解释
图一。照片由Andrew“Donovan”valdi via在 Unsplash |和图 2 拍摄。照片由凯利·麦克林托克在 Unsplash 上拍摄
快速回顾:XAI 和国家安全委员会
可解释的人工智能(XAI) 处理开发人工智能模型,这些模型本质上对人类来说更容易理解,包括用户、开发者、决策者和执法人员。神经符号计算(NSC) 处理子符号学习算法与符号推理方法的结合。因此,我们可以断言,神经符号计算是可解释人工智能下的一个子领域。NSC 也是最适用的方法之一,因为它依赖于现有方法和模型的结合。
图 3。符号人工智能 vs 次符号人工智能(作者提供图片)
如果说可解释性是指用人类语言有意义地描述事物的能力。换句话说,它是将原始信息(数据)映射成对人类有意义的符号表示的可能性(例如英文文本)****
通过从子符号中提取符号,我们可以使这些子符号变得可以解释。XAI 和国家安全委员会都试图让次符号系统更容易解释。NSC 更多的是关于将子符号映射到符号,通过逻辑设计的可解释性:对子符号学习表示的符号推理。XAI 没有那么具体,更多的是关于所有细微差别的可解释性,即使可解释性被包裹在不可解释的模型中。如果从子符号中提取符号意味着可解释性,那么 XAI 包括 NSC 。
让我们通过一个示例来看看这些 NSC:
神经符号概念学习者
Mao 等人提出了一种新的 NSC 模型,即神经符号概念学习器,它遵循以下步骤:
图 4。神经符号概念学习者(图由毛等
- 图像分类器学习从图像或文本片段中提取子符号(数字)表示。
- 然后,每个子符号表示都与一个人类可以理解的符号相关联。
- 然后,符号推理器检查符号表示的嵌入相似性
- 训练继续进行,直到通过更新表示使推理机的输出精度最大化。
白盒与黑盒模型
人工智能模型可以是(I)白盒或(ii)黑盒。
- 白盒模型可以通过设计来解释。因此,它不需要额外的能力来解释。
- 黑箱模型本身是无法解释的。因此,为了使一个黑盒模型可解释,我们必须采用几种技术从模型的内部逻辑或输出中提取解释。
黑盒模型可以用
- **模型属性:**展示模型或其预测的特定属性,如(a)对属性变化的敏感性或(b)负责给定决策的模型组件(如神经元或节点)的识别。
- **局部逻辑:**单个决策或预测背后的内在逻辑的表示。
- 全局逻辑:整个内部逻辑的表现。
因此,下图显示了人工智能模型在可解释性方面的子类别:
图 5。简单易懂的人工智能分类法
基于规则的可解释性与基于案例的可解释性
除了可解释模型的逻辑区别之外,我们还确定了两种常见的解释类型,所有上述模型都可以采用这两种类型来提供解释:
基于规则的解释:基于规则的可解释性依赖于生成一套“形式化的逻辑规则,阐明给定模型的内部逻辑”。
图 6。决策树可以很容易地公式化,以提供基于规则的解释(图由 Guidotti 等人
基于案例的解释:基于规则的可解释性依赖于提供有价值的输入输出对(积极的和消极的)来提供模型内部逻辑的直觉。基于案例的解释依赖于人从这些对中推断逻辑的能力。
图 7。基于案例的解释示例(作者图)(图片来自 Unsplash )
基于规则与基于案例的学习算法比较示例:
假设我们的模型需要学习如何做苹果派的食谱。我们有蓝莓派、芝士蛋糕、牧羊人派的食谱,还有一份简单的蛋糕食谱。基于规则的学习方法试图为制作所有类型的甜点提出一套通用规则(即,渴望方法),而基于案例的学习方法则根据需要概括信息,以涵盖特定的任务。因此,它会在可用数据中寻找与苹果派最相似的甜点。然后,它会尝试在类似的食谱上做一些小的改动。
XAI:设计白盒模型
包括基于规则和基于案例的学习系统,我们有四类主要的白盒设计:
- 手工制作的专家系统;
- **基于规则的学习系统:**从数据中学习逻辑规则的算法,如归纳逻辑编程、决策树等;
- 基于案例的学习系统:基于案例推理的算法。他们利用例子、案例、先例和/或反例来解释系统输出;和
- 嵌入符号&提取系统:更多的生物启发算法,如神经符号计算。
在本系列接下来的部分中,我们将有这些方法的实际例子。
最终注释
在本帖中,我们:
1 —简要介绍了 XAI 和 NSC 之间的差异和相似之处;
2 —定义和比较黑盒和白盒模型;
3 —使黑盒模型可解释的方法(模型属性、局部逻辑、全局逻辑);
4-比较基于规则的解释和基于案例的解释,并举例说明。
在下一篇文章中,我们将介绍市场上可解释工作的库和技术,并将使用其中一些库从黑盒模型和白盒模型中提取解释。
订阅邮件列表获取更多内容
如果你想获得我在 Google Colab 上的其他教程文章的代码,并尽早获得我的最新内容,可以考虑订阅:✉️的邮件列表
如果你对深度学习感兴趣,也可以看看我的人工智能内容指南:
https://blog.orhangaziyalcin.com/a-guide-to-my-content-on-artificial-intelligence-c70c9b4a3b17
如果你正在阅读这篇文章,我确信我们有着相似的兴趣,并且现在/将来会从事相似的行业。那么我们就通过 Linkedin 来连线吧!请不要犹豫发送联系请求!Orhan g . yaln—Linkedin
参考
Guidotti,r .,Monreale,a .,Ruggieri,s .,Pedreschi,d .,Turini,f .,& Giannotti,F. (2018 年)。黑盒决策系统的基于局部规则的解释。www . propublica . org/article/machine-bias-risk-assessments-in-criminal-pending
毛,甘,c,柯利,p,特南鲍姆,J. B .,,吴,J. (2019)。神经符号概念学习者:从自然监督中解读场景、词语和句子。第七届国际学习代表大会,ICLR 2019 。http://nscl.csail.mit.edu
Sovrano,F. (2021),现代人工智能对可解释性的需求,博洛尼亚大学信息学院
图一。Andrew “Donovan” Valdivia 在 Unsplash |和图 2 上拍摄的照片。照片由凯利·麦克林托克在 Unsplash 上拍摄
21 点:将人工智能应用于网络安全的游戏模型
人工智能校准和安全
博弈论、强化学习和信念更新如何模拟网络冲突与合作
网络攻击继续威胁着大大小小的组织。数据泄露或勒索软件攻击的影响可能会对客户和股东产生重大影响。为了帮助应对网络威胁,一些组织已经开始探索大数据和人工智能(AI)如何帮助降低网络安全风险。
机器学习算法现在在网络安全中很常见。我们发现在更多的商业产品中提供了机器学习,从那些完全集成到产品中并且不需要机器学习知识的产品到那些需要卷起袖子来整理算法并执行统计分析的产品。用于网络安全的机器学习最常被应用于检测代表攻击的模式。这包括评估审计日志数据、发现网络入侵检测系统的异常以及识别和阻止计算机系统上的恶意软件的算法。
在一些应用中,机器学习被用来训练网络上正常活动的模型,希望以后能够检测到可能代表网络攻击的异常事件。机器学习在网络安全中的当前使用包括监督和非监督方法。
尽管机器学习越来越多地用于网络安全,但人工智能的使用却很少。换句话说,我们使用算法来检测模式,但我们很少使用算法来自主决定最佳行动方案。
从表面上看,机器学习应该非常有利于提高组织的网络安全,但仔细一看,有一个重要的弱点。机器学习完全是关于学习者的。它的重点是创建一个足够精确的模型来检测数据中的重要特征,而不会过度拟合。机器学习经常忽略的是,在学习的同时,它也在教导网络冲突与合作游戏中的其他参与者。
网络安全不是一个静态的游戏。规则不断变化。有许多不同类型的玩家,每一种都有自己的目标、战略和战术。当一个组织调整它的防御时,攻击者改进他们的攻击,要求该组织进一步改进它的防御。结果就是无休止的升级游戏。
这与机器学习的大多数传统应用非常不同,传统应用进行预测,对数据进行分类,并利用不包括对手的数据检测异常。在网络安全中,安全管理员采取的每个行动都可能通知攻击者,并可能导致下一次攻击中不可预测的变化。
为了从机器学习的基本模型转向人工智能,我们需要超越模式检测来理解这个升级游戏。如果我们应用博弈论来探索多智能体游戏中的战略决策,我们就可以开始应用人工智能。
博弈论
博弈论的工具有助于评估不同代理人或玩家的决策和行为。博弈论试图根据一个玩家可能做出的决定来寻找另一个玩家的最佳决策。有了它,我们可以将网络安全建模为一个游戏,以了解安全管理员的行为如何影响攻击者的决策并与之互动。使用这个模型,我们可以开始探索人工智能和大数据的潜力,以更智能地保护计算机系统和数据。
战略决策的博弈论可以追溯到 1713 年,但直到 1928 年约翰·冯·诺依曼才将其正式作为一个研究领域。作为博弈论一部分的流行游戏包括囚徒困境、美元拍卖、公主和怪物游戏以及最后通牒游戏。但是当考虑到网络安全的复杂性时,这些游戏是非常简单化的。
西蒙·西内克写了一本非常平易近人的关于博弈论的书,《无限游戏》 [1]。在书中,他建议组织中的领导者经常将战略决策视为有限游戏,有开始也有结束。有赢家和输家。反而大部分游戏都没有结局。这些无限游戏的目标不是赢,而是留在游戏中。
当将博弈论应用于组织中的网络安全时,乍一看,网络安全似乎是一个无限的游戏。该组织的目标是阻止网络攻击,但永远不会结束。安全经理的目标是留在游戏中,并确保组织的防御措施足以让组织继续完成其使命。但这种简单化的博弈论观点足以描述网络安全的复杂性吗?
21 点中的竞争与合作
在探索与网络安全相似的现有游戏模型时,常见的模型似乎不够充分。网络安全是什么类型的游戏?找到一个模型的挑战是因为不同的玩家对网络安全的玩法非常不同。这不是一个简单的决定游戏是有限的还是无限的,因为对一些玩家来说它是有限的,但对其他人来说目标是留在游戏中。
赌场游戏有助于将博弈论应用于现实世界的问题。以 21 点游戏为例。在这个多人游戏中,我们可以考虑三个主要的战略决策者。首先是赌场老板,我们称他为房子。接下来是庄家,他执行接战规则,代表庄家执行游戏。最后,还有玩家。
在 21 点中,每个玩家都直接与庄家竞争。虽然有多名玩家坐在桌旁,但每个人都用自己的牌来对抗庄家的牌。在最简单的层面上,每个玩家的目标是比庄家更接近总牌值 21,而不超过。对于每个玩家来说,这个游戏是非常有限的。每一轮,玩家不是赢就是输。玩家决定下多少注,除了下注之外没有任何风险。
另一方面,庄家使用一套预定义的策略与每个玩家竞争,这些策略使庄家在多轮比赛中稍占优势。同时,经销商与公司合作,始终遵循公司制定的规则。庄家是庄家的代理人,并不根据每一轮的结果直接获利或损失。这种合作使经销商能够赚取工资并继续工作。结果,庄家在一系列短暂的有限游戏中与每个玩家单独竞争,但仍与庄家合作以留在游戏中。
最后,赌场的目标是赚钱,雇佣劳动力,并为顾客创造一种赌博体验,让他们不断回头。为了做到这一点,它的策略是利用对它有利的赔率,并仍然允许玩家定期获胜。结果,房子本质上与玩家合作,同时与他们竞争。
在 21 点中,我们既看到冲突,也看到合作。我们看到球员以短期的眼光竞争,目标是获胜。我们也看到经销商从长远角度竞争,目标是留在游戏中。我们看到这个房子合作创造了一个支持游戏的环境。但是本文的目标并不是将博弈论应用于 21 点,那么这与网络安全有什么关系呢?
网络安全和 21 点模式
要从 21 点的角度来看待网络安全,请考虑玩家之间的关系。相当于房子的是组织。组织存在的目的是为股东创造价值,保持利润,并为人们创造机会,将他们的技能和专业知识一起用于共同的目的。这与我们 21 点模型中的房子非常相似。大多数组织的目的不是防御网络攻击。相反,组织通过确保网络攻击不会阻止他们执行其主要功能来管理风险。
安全经理相当于经销商。安全经理可能是高管,如大型组织中的首席信息安全官,或者是负责网络安全的信息技术负责人。安全经理和经销商一样,遵循既定的最佳实践。安全经理与组织合作,确保有足够的资源投资于安全实践,以维护组织的目标。安全管理器定期与攻击者竞争。像庄家一样,安全经理与每个攻击者进行一对一的竞争,并认识到会有赢有输。最终,安全经理通过遵循最佳实践并确保尽管攻击者可能赢得一些冲突,但他们最终不会对组织产生负面影响,从而留在游戏中。
网络冲突中的攻击者就像 21 点中的个体玩家。每个人都坐在桌子旁,并愿意在可能导致胜利的攻击上下注。对于攻击者来说,胜利可能是对组织网络的初步破坏。可能是盗窃知识产权。它可能是组织数据的加密,以迫使支付赎金。攻击者通常玩一个有限的游戏。它们的区别在于,与组织防御攻击所需的投资相比,攻击的成本可能非常低。此外,攻击数不胜数,而归因于攻击的能力有限,因此起诉很少。攻击者可以保持一种不对称的优势,并面临很小的风险,因为他们几乎没有不利方面。
因此,我们看到 21 点游戏和网络冲突与合作之间有一些有趣的相似之处。但这些相似之处还不足以开始应用人工智能。
用 21 点模型计算收益
为了建立一个对人工智能有用的模型,我们需要一些方法来计算每个玩家的最佳决策。在博弈论中,最基本的表示法是矩阵,被称为博弈的战略形式。矩阵的每一面代表一个玩家,以及他们可能采用的策略。在每个参与者策略的交叉点,我们计算每个参与者的收益。回报可能有多种形式。它可以用一美元的价值来具体表示。它可以被抽象为估计期望效用。有些收益是负的,代表玩家的损失,有些是正的,代表收益。下表提供了 21 点的名义回报矩阵。
作者图片
在这个简单的模型中,我们不会考虑支付的概率或金额。我们根据预期效用来衡量支出,其中 1 是潜在的好结果,1 是潜在的坏结果。每个单元格中的第一个数字代表玩家的期望值,第二个数字代表庄家的期望值。举个例子,庄家很可能已经指示庄家,当他们的牌的总价值大于 16 时,千万不要出手。即使是偶然的,当庄家的牌大于 16 时,他也不会与庄家合作,这可能会导致不好的结果,比如被炒鱿鱼。结果,当总牌值大于 16 时,发牌者击中牌的期望值总是负 1。
同样,当牌大于 16 时,建议玩家不要受到打击。因此,我们可以排除这两种策略,并认识到玩家和庄家更有可能遵循上表中突出显示的策略。如果你是一个有竞争力的 21 点玩家,你可能会批评这个模型没有显示什么时候双倍下注,什么时候拆分对子,或者什么时候投降。耐心点。这个例子只是游戏的一个简单的表现。
在博弈论中,就像上面的例子,我们可以寻求消除劣势策略,并找到一个纳什均衡,确定双方都可以接受的结果。在博弈论的简化模型中,也许可以在一页上分析这些决策,并直观地找到最佳决策。即使像 21 点这样的基本游戏也比支付矩阵的单一视图能够有效建模的游戏复杂,因为 21 点是一种顺序游戏。更复杂的游戏模型使用一棵树来表示连续的决策。根据其他玩家可能做出的决定,可以反复探索这些树以找到最佳收益。
在将人工智能应用于多智能体系统时,博弈论可能非常有效[2]。但是简单地推导纳什均衡可能并不能预测最佳决策,因为我们面对的是各种各样的决策、不同的目标和不完善的信息。研究发现,纳什均衡的简单计算在复杂的多主体游戏中表现不佳[3]。
这就是支持人工智能的算法可以发挥作用的地方。强化学习可以探索博弈树,并了解对手的决策应该如何影响自己的决策。强化学习可用于在模拟中反复玩大量游戏,以获得如何获得最佳回报的第一手信息。
信念更新在迭代遍历博弈树计算收益时也很有帮助。由于大多数网络冲突游戏都包含不完全信息,因此包含一个信念分数可以模拟不同类型攻击的概率,并可能有助于与强化学习相结合。使用进化方法将强化学习与信念更新相结合的模型可能会成为安全经理手中的强大工具,他可以决定下一步如何保护组织。
付诸实践
我们已经达成了将人工智能和博弈论应用于网络安全的高级概念。我们已经确定了可以帮助组织做出实现最佳回报的决策的算法。我们准备好用算法取代安全管理器了吗?还没有。
安全经理有许多战略和战术,他们可以应用来保护他们的计算机和数据。NIST 网络安全框架[4]是网络安全的五个核心功能的综合集合,每个功能都分为类别和子类别。总共有 108 个子类别的安全活动供经理们选择。攻击者同样拥有丰富的已知战术以及少量新颖的攻击手段。如果同时进行,对所有这些排列进行建模将是一项挑战。
首先,我们可以选择一组范围更窄的安全决策。举例来说,我们可以将其应用于漏洞和补丁管理。组织经常努力跟上漏洞的数量,并且无法在对手利用它们之前修补关键漏洞。安全经理必须根据漏洞的严重性、有助于缓解漏洞的环境因素、系统停机维护窗口的可用性,以及为组织增值的项目的竞争优先级来确定优先级。如果修补决策是由我们的人工智能算法做出的呢?
要将此建模为一个游戏,首先考虑攻击者可能的动作。攻击者可能试图利用已有多年可用补丁的旧漏洞、组织可能尚未修补的新漏洞,或者不存在补丁的零日漏洞。对于攻击者来说,每一种方法都有相关的成本和潜在的好处。
现在考虑安全管理器可能的操作。安全管理员可以使用策略来立即部署所有修补程序,根据安全区域部署修补程序,或者根据漏洞的严重性划分优先级。这些没有对错之分,但是根据攻击者的决定,有些可能比其他的更好。
每个行动都有成本和收益。我们可以将每个玩家的收益估计为高(3)、中(2)或低(1)。我们同样可以将成本估计为高(1)、中(2)或低(3)。请注意,该量表的值是颠倒的,以表明最低成本比更高成本的决策更有益。然后,我们可以将估计值计算为收益减去成本。
在高层次上,这个游戏的战略形式可能如下表所示。
作者图片
如果我们首先从攻击者的角度来看,很明显使用零日漏洞具有最低的预期价值。最好的选择是利用旧的漏洞。因此,我们可以预计,攻击者将总是试图利用旧的漏洞,然后花费所需的成本来利用新的漏洞或零天。因此,在这个游戏中,我们可以消除攻击者利用新漏洞或零日的策略。
有了攻击者可能的决策,安全管理员最好选择在旧漏洞列中具有最高期望值的策略。我们排除了前两行,发现最佳决策是根据严重性打补丁。在我侮辱正在阅读这篇文章的安全管理人员之前,这些期望值只是针对这个例子的。您可能会以不同的方式看待这些策略的成本和收益,或者有一个比这里列出的更好的策略。这仅仅是为了说明。
在这个例子中,我们可以看到,安全管理人员应该根据攻击者首先利用旧漏洞的理性决策来创建一个策略。作为一个战略决策,这可以抓住一个安全程序应该如何优先打补丁的哲学。但这还不是 AI。
在实际组织计算机系统的情况下,我们可以用实际数据替换这些高级期望值。我们算法的输入可以包括表征攻击面的网络模型。该算法可能具有关于组织的已识别漏洞及其严重性的数据。该算法还可以知道组织愿意使用什么中断窗口来修补系统。它甚至可能包括安全事件数据,有助于识别遭受攻击最多的系统。
将所有这些数据放在一起,学习算法可以通过创建各种游戏树并根据攻击者的决策计算不同修补策略的收益,来扩展这个简单的收益矩阵。这将是人工智能在网络安全领域取得进展的重要一步。该算法将使用人工智能来预测不同修补策略的最佳回报。它甚至可以根据可用补丁的数量和严重性来改变策略。它可以通过遍历游戏的数十万种排列来选择最佳收益。
预先定义了一组打补丁的规则和优先级的安全管理员无法执行与该算法一样多的分析。凭借对算法决策的信心,可以通过在对组织最有利的时候自动部署补丁,使用自动化来控制漏洞。在完全信任算法的决策之前,算法可用于决策支持,以帮助确定最重要的缓解活动的优先级。
结论
网络安全中的人工智能需要清楚地了解如何对网络冲突和合作进行建模。可以使用博弈论对网络安全进行建模的算法有可能降低组织的网络安全成本,同时提高其安全性。这一点尤其重要,因为网络安全是一场不对称的升级游戏。
需要进行更多的研究和开发,以使其对组织及其安全管理人员更有用。虽然一些研究和商业产品开始探索人工智能的安全功能,如补丁和漏洞管理,但还有许多其他安全功能可能受益于人工智能增强。
参考
[1] S. Sinek,《无限游戏》,(2019),企鹅出版社
[2] Y. Shoham 和 K. Leyton-Brown,多智能体系统:算法、博弈论和逻辑基础,(2008),剑桥大学出版社
[3] N. Feltovich,实验性非对称信息博弈中基于强化与基于信念的学习模型,(2000),计量经济学,68(3),605–641
[4] M. P. Barrett,改善关键基础设施网络安全的框架,(2018),马里兰州盖瑟斯堡国家标准与技术研究所
在 B2B 产品创新中融合精益、设计思维和共同开发——团队回顾
行业笔记
允许我们“深入”的方法组合,同时抵消了构建“一次性”(以及我们会采取的不同做法)的风险
作者:梅森·王茂林,乔贝尔·布劳恩,尼克·科恩和蒂博·杜本内
简介
B2B 环境下的产品研究比消费产品更困难,因为潜在参与者相对较少,他们的使用案例复杂,并且由于他们的时间价值高而难以访问。因此,B2B 产品团队可能会决定放弃他们的消费者同行采用的大规模独立研究类型,而是选择直接与个人客户共同开发他们的产品。
共同开发(或共同创造)是一种有价值的 B2B 创新技术,可以产生对客户特定的深度理解,以及专有数据、竞争对手情报和 IT 生态系统细节等有价值的资源。然而,寻找合适的参与者和管理联合开发项目非常耗时,产品团队可能会发现他们实际上只能与一个客户进行联合开发。不幸的是,如此密集地接触一个客户,往往会放大建立一个单一市场的风险。
那么,产品团队如何才能达到来自高度参与的共同开发的理解程度,而不牺牲来自接触许多客户声音的市场视角呢?这是 Teralytics 的 Streets 产品团队面临的挑战,它引导我们找到了本文中描述的混合方法。
与几个客户群体的共同开发比短期、大样本研究方法产生更深刻的见解,比仅与一个客户共同开发产生更广泛的验证。作者照片。
目标&接近
Teralytics 将机器学习和启发式模型应用于来自数百万电信用户的匿名移动数据,以帮助我们的客户了解他们所在地区的人类移动模式。
2020 年夏天,来自我们市场的信号表明,我们道路规划部门的客户需要将车流量分配给各个路段。这些信号是我们称之为“街道”的新产品的推动力。
我们最初的目标市场包括德国中小城市的数百个交通规划部门。这些实体遵循复杂的流程,并依赖于高度准确(地面实况校准)的数据——这些条件受益于典型的合作开发的洞察力深度。然而,由于我们的许多客户受到公共预算的限制,我们也需要一种方法来帮助我们开发一种核心产品,这种产品将满足大多数客户的需求,而不需要为每笔交易进行昂贵的定制。
在我们的 MVP 测试中,我们借鉴了几十个客户的观点,而后面两个阶段的重新发现和共同开发涉及到四个客户。作者照片。
我们在此描述的流程跨越了整个上市生命周期,从客户和产品发现一直到向收入客户推出和交付功能性产品。这个过程由我们在三个阶段应用的完善的方法组成:MVP 测试、重新发现和共同开发。
第一阶段:MVP 测试
为了避开寻找愿意自愿参与数小时研究的客户的困难,我们对最终的 Streets 产品提供了折扣,作为在其开发过程中参与研究会议的激励。衡量客户的金钱和时间的价值交换对未来产品交付的吸引力是我们用来评估 MVP 假设可行性的测试。
“看看向参与的合作伙伴客户收取象征性的费用。。。这改变了参与/激励的动力,从你想从他们那里得到的东西,变成了参与的特权。”
创新委员会
1.1 开发我们的 MVP 假设检验
我们希望用 MVP 测试的首要假设是,有交通用例的客户愿意为来自移动数据的道路级洞察付费。我们在假设管理练习中记录了这一点和其他关键假设,我们内部称之为“流言终结者”,其中我们记录关键假设,然后确定实验类型和每个实验的成功标准。
流言终结者工作表记录了假设、实验类型、成功指标和收集的证据。作者照片。
我们为产品假设选择的测试是试图在开发之前销售一个 MVP。我们认为,说服三到五个客户(占我们总可用市场的 2-3 %)为我们产品的未来交付预付费用可以被认为是足够的吸引力。由于我们流程的后期阶段会涉及定性方法,我们对这一特定阈值的选择受到了 Jacob Nielsen 的观点的影响,即三到五名同质参与者是定性研究最有效的样本量。
梅森——首席采购官:
在过去的新产品开发中,“从哪里开始”这个令人生畏的问题一直困扰着我的团队,但《流言终结者》列举我们的信念,然后开发实验来测试它们的过程是一个打破最初的麻痹并快速开始验证的好方法。
乔贝尔——UX 设计经理:
在我之前加入的组织中,产品和技术发现通常发生在筒仓中,想法存储在各种文档或融合页面中。《流言终结者》的好处在于,它允许从团队中的所有声音中产生假设,建立共识。
1.2 构建 MVP
有了按价值(按设计和产品)和技术可行性和风险(按工程师)排列优先级的特性假设列表,我们现在可以开始界定我们的 MVP 产品了,但我们首先必须决定 MVP 的类型。
从无产品到最小产品的 MVP 类型——Streets 采用“先卖后建”的方法。作者照片。
精益实践者开出了各种各样的 MVP 风格,从价值主张的简单表达一直到第一个功能的发布。我们发现客户和最终共同开发的双重目标迫使我们尽可能接近“无产品”MVP,同时仍然表达一个足以实际激发购买的概念。幸运的是,我们行业中的解决方案倾向于在交付前作为定制项目销售,因此成品通常不是开始销售所必需的。
我们的“先销售后构建”MVP 包括一个推介资料、一个可点击的演示、一个演示脚本、一份产品规格和一份面向客户的评估协议。作者照片。
为了让我们的销售团队能够传达我们的主张,我们创建了一个包括定价、详细介绍核心功能的产品规格文档、一个可点击的演示以及一个演示脚本的推介资料。我们的行动号召是让客户预先支付标准价格的 5%作为加入“测试版”计划的报名费。在整个开发过程中保持参与的客户可以将该费用用于购买打折的年度订阅。
梅森——首席采购官:
在理想的情况下,我们应该在构思解决方案假设之前进行广泛的发现研究,但是在我们的领域很难接触到客户进行研究。因此,我们在这一点上所设想的一切都是猜测。假设是好的,但是乔贝尔非常擅长她的工作,即使是我们最可疑的想法看起来也令人震惊。我太倾向于创造一个令人信服的命题,以至于我们的概念“太真实”,这使得说服客户在以后批评我们的功能假设变得更加困难。
乔贝尔——UX 设计经理:
如何通过公开邀请共同创作来展示产品的可能性?事实上,销售团队展示的概念证明在一个抛光的原型中有很多功能,它可能会发出信号,“嘿,这是一个很酷的成品。”任何试图销售带有概念验证的产品合作开发的人都可能会考虑减少提议的想法和功能的数量,而不仅仅是设计的保真度。
thi baut——高级交通建模专家:
从工程的角度来看,构建 MVP 就是评估由设计和产品提出的产品功能的可行性。我刚开始在公司工作,与一位在公司工作了 6 年的工程师合作。总的来说,这种结合效果很好:我新鲜的眼睛让我贡献新的想法,而他的经验让他快速识别与我们的 MVP 范围相关的潜在风险和资源。
1.3 选择参与者
手头有了推销材料,我们现在需要一份销售线索清单,这样我们就可以开始销售了。重要的是,我们选择潜在客户的方法会产生一个与我们定性研究阶段相一致的样本。我们最终选定了研究人员所说的“标准-i 抽样”,这是一种常用的非概率“有目的抽样”方法,它依赖于预先确定的标准来定义样本框架。
方便的是,这种技巧看起来很像建立理想客户档案 (ICP)的销售实践。因此,我们的样本框架和线索列表包括了我们最初目标细分市场中的公司,特别是人口在 50,000 到 150,000 之间的德国城市的交通规划组织。签署预购协议的前五家公司参与了随后的发现和验证研究活动。
梅森——首席采购官:
事后看来,仅仅根据客户与目标角色的契合度和购买意愿来选择与他们进行数月合作开发的客户,可能是一个太低的标准。这些标准对评估客户的文化与创新的兼容性没有太大作用。早期,一些客户似乎对参与开放式发现感到不舒服,他们期望一个更加线性的产品开发过程。
1.4 测量牵引力
我们用两种方法来评估 MVP 的接受度:转化和反对。
签署购买协议的正式转换步骤是对我们价值主张的有力验证,因为它涉及产品交付前的部分财务承诺。这项工作还有一个附带的好处,那就是在早期暴露任何采购障碍,从而简化最终的完整许可证采购。协议步骤也是澄清知识产权所有权等主题的机会,同时通过限制对范围或时间表的期望,为开放式创新奠定基础。
吸引潜在客户的目的是转化他们,同时试图向他们学习,这是一种微妙的平衡,需要一种不同于产品/市场适合产品的标准销售的方法。为此,我们鼓励我们的销售代表不要试图“推销”异议,而是倾听并探究“为什么”。
“如果会议过多地是关于销售,那么通过询问潜在客户对解决方案的反应,你实际上缩小了对话的范围。会议不再是寻找最佳机会。。。这是为了让潜在客户相信,你的广泛价值主张是有意义的。”
艾蒂安·嘉宝莉,莱安·B2B
在这一过程中,我们安排了与销售团队的每周汇报会议,讨论他们的发现,目的是根据现场的反应重复或改变我们的主张。为了帮助我们识别调查结果中的模式,我们对收到的异议进行了分类,让我们可以通过下图这样的图表来直观地了解我们推销中的摩擦来源。
推介回顾帮助我们量化了 MVP 初始推介期间出现的意见和异议的类型和频率。作者照片。
最终,我们的销售代表达到了五个有购买意向的客户的门槛,而不需要修改我们的推销。然而,由于与我们的价值主张无关的合同条款,与一个客户的谈判最终停滞不前;因此,我们从四个客户开始了我们流程的第二阶段。
梅森——首席采购官:
让我们的销售代表将此作为学习练习并定期参加汇报会议非常困难。在很大程度上,我认为我们的销售代表将此视为典型的销售。这也许是可以理解的,因为除了首席执行官偶尔的督促,我们没有建立任何特殊的激励机制来鼓励他们以不同的方式对待这一点——这种“学习活动”最终会偏离他们的销售目标。
第二阶段:重新发现
我们通过 MVP 获得的稳固牵引力是一个信号,表明我们的市场重视这些能力,但我们还不知道“为什么”。我们仍然需要了解我们的主张所支持的用户目标,这些用例对我们用户的工作有多重要,这在哪里适合他们的工作流程,以及我们产品周边的供应商和解决方案的生态系统。
2.1 旅程映射
我们将在项目启动会议上回答这些问题。这些会议的形式是一个四小时的(虚拟)研讨会,由代表每个客户的三到六名用户和决策者参加。为了实现研讨会的客户发现目标,我们首先需要参与者基本上“忘记”我们向他们推销的 MVP,而是告诉我们更多关于他们自己和他们工作的信息。
创建旅程图的过程迫使整个团队进行对话并形成一致的思维模式。。。这一共同愿景是旅程规划的关键目标。”
—尼尔森诺曼集团的莎拉·吉本斯
启动研讨会的核心是旅程规划练习,这是一项大量借鉴设计思维概念的技术。在这里,我们要求参与者列出一个“长列表”,列出他们角色中最重要的活动和目标。从这个长长的列表中,参与者选择一个目标进行深入研究,向我们介绍实现该目标的步骤、时间表、人员和问题。
我们在每次研讨会期间在 Miro 中捕捉到的路线图包括时间表、步骤顺序、主要参与者以及参与者所选目标的挑战。作者照片。
梅森——首席采购官:
这些研讨会的前 10 分钟很紧张!我们必须让参与者从销售过程中设定的产品即将完成的预期中走回来。尽管我们传达了信息,但我们演示的高逼真度给潜在客户留下了产品比实际更完整的印象。一旦我们重新设定了期望值,研讨会的其他部分就很棒了。在几个小时内,旅程地图帮助我们精确定位了客户最重要的工作流程中街道可以增加实际价值的区域。
乔贝尔——UX 设计经理:
参与者主要是说德语的人,我不能低估有一个母语主持人有多重要,他可以发现语言中的细微差别,并允许自然的对话流。在第一个研讨会中,现状路线图是从参与者典型工作流程的角度进行的,而不是对实际事件的叙述。我们在随后的研讨会中对此进行了更改。由此产生的对具体案例中实际发生的事情的强调引发了更有意义的讨论。
nick——高级产品经理:
在研讨会期间,由于缺乏数据和工具,与会者表达了对现状的强烈不满。这澄清了他们参与计划和投资潜在街道产品的动机。研讨会似乎允许客户在安全的环境下反思他们的工作,这引发了参与者之间关于他们真正的优先事项和挑战的一些对话。这些讨论为我们提供了对客户问题的坦诚和真实的看法。
2.2 重新审视我们的解决方案
在与来自四个客户的 13 名参与者进行了 16 个小时的研讨会后,我们对用户的角色、目标、难点和背景有了足够深刻的理解,能够围绕这一背景重新定义我们的产品假设。
我们最初的一些假设在研讨会上得到了验证。例如,我们认为小城市缺乏数字工具来应对和决策市民不断变化的出行行为,这一观点被证明是正确的。另一方面,我们揭穿了一个重要的假设,即实时交通是这个用户群的需求。
综合我们的发现后,我们更新了最初的任务流程,省略了多余的步骤,专注于我们现在理解的核心流程。从这个核心,我们得出了一个新的基础范围,这将作为我们共同发展的基础。
nick——高级产品经理:
很明显,在最初的研讨会之前,客户就已经在思考新产品可能对他们有什么帮助,所以他们准备了一份需求清单。当我们与他们一起定义他们最重要的用例时,他们最终都给予了交通量最高的优先级,即使是与公共交通相关的用例。对“核心”产品定义的补充在关键时刻到来,当客户能够看到演示并通过触摸测试原型时,他们对设计和内容功能反应热烈。除了两个关键的例外,这些增加是对最初“核心”的重大改进,而不是重大转变。
乔贝尔——UX 设计经理:
综合我们在研讨会期间收集的丰富信息后,我们更好地了解了客户的经常性需求和目标。这给了我们下一次迭代所需要的焦点。我觉得自己像一个雕塑家在凿石头。一些最初的流言终结者假设已经被证实,而另一些被证明对我们的产品来说是不必要的。我们的团队对于保留在我们的研讨会中不被支持的功能并没有辩解。这确实是这个过程应该做的,很高兴看到它的工作。我们利用我们的学习来简化我们的任务流程,并总体上重新设想我们的产品。
thi baut——高级交通建模专家:
在这一阶段,Nick 不仅倾听客户的意见,也倾听团队的意见。例如,了解到客户对交通速度不感兴趣是一个受欢迎的发现,因为我们不确定我们能否准确地得出这些数据。另一方面,受到客户最热烈欢迎的功能之一(我们称之为“蜘蛛分析”)并没有被客户明确提及,而是由团队在我们进一步了解客户的目标后提出的。因此,我们决定制作第一个原型,稍后在我们的互动研讨会上展示。
第三阶段:共同开发
尽管与客户合作这一显而易见的概念似乎是一个复杂的术语,但客户共同开发(或共同创造)是一个合适的理论空间,有正在进行的研究,越来越多的文献,不同的方法,以及私营部门咨询。
与设计思维等其他以客户为中心的方法相关但不同的是,与前者相比,共同开发倾向于与相同主题进行更长时间、更密集的互动,以及更多地参与解决方案开发。
“设计思维有助于通过深入的用户观察创造价值,而共同创造的目的是通过用户互动创造价值。”
——2016 年芒索法布里赫莫内特-古约特
共同开发佳能通常假定与单一领先客户的联合创新,我们同时与四个客户合作背离了这一原则。我们的方法使我们避免了建立一次性项目的风险,因为可能将我们引入错误道路的个人请求可能会与我们其他项目参与者的需求进行近乎实时的核对,从而不断将我们保持在“金发女孩”区域。
3.1 解迭代
在联合开发中,“客户主导”和“客户验证”同样有效,但供应商应仔细考虑哪种方法最合适。由于我们的 Streets 产品团队拥有广泛的领域、设计和工程技能,我们相信我们有能力领导解决方案的开发。通过率先行动,我们可以将解决方案的空间构建为可行性和财务可行性,在此范围内,我们的内部设计能力和客户参与的结合确保了高水平的创造力。
每隔几周,我们将举行一次回顾迭代,我们将通过提出越来越逼真的解决方案概念来引导,然后客户将在这些概念的基础上做出回应和思考。我们以这种方式持续了两个多月,在每次迭代后进行验证的同时进行设计和开发,直到我们开发出第一个功能完整的产品。
中间前端原型的屏幕截图,带有一小组真实的数据,在可用性测试期间生成了有意义的响应。作者照片。
这一旅程中的一个重要里程碑是我们第一次“可点击”体验的交付。这允许我们开始测试可用性,同时进一步验证价值和我们的解决方案实现。有了这个版本,客户实际上可以登录并测试我们对他们用例的实现。我们记录了与每个人的一对一会谈,并根据我们观察到的用户反馈模式改进了我们的方法。
nick——高级产品经理:
随着我们在每次迭代中提高我们概念的保真度,我们收到了更加具体和可行的反馈…例如,我们构建的一个用例是“选定链路路由分析和显示”。我们在一个交互式会话的上下文中演示了它。虽然这是交通规划领域中一种众所周知的分析类型,但对客户来说却是全新的,他们很快就兴奋地使用它来回答许多与用例相关的工作问题。通过这种方式,我们验证了这是核心产品的重要组成部分。
在这个过程的所有步骤中,与四个客户迭代是最耗时的。我们测试了将多个客户合并到一个会话中,但发现这并不能节省时间。对我们来说,从每个参与者那里获取反馈显然非常重要,而且客户也希望他们的具体反馈能够被听到,而不仅仅是参与小组讨论。
乔贝尔——UX 设计经理:
在一次迭代中,我通过电子邮件将漫威的原型发送给客户以获得反馈,但是这种异步方法没有产生太多的响应。从那时起,我只在我们的现场会议中分享原型并寻求反馈。事后看来,我们不应该期望参与者在既定的会议节奏之外参与共同创造。
当我们在可用性会议上展示高保真度模型以进一步验证用例及任务时,参与者的反馈反而集中在数据的“虚假性”和“看起来不错”上这令人沮丧,因为这些会议并没有产生高质量的体验反馈。只是到了后来,一旦我们开发了一个包含少量真实数据的测试应用程序,参与者才会真正地参与到这些概念中来。在这些会议中,他们详细阐述了他们将如何处理这些数据,这些数据是否看起来可信,以及他们可以使用该产品完成的其他潜在用例。
thi baut——高级交通建模专家:
研讨会的形式允许我们采取一种“快速而肮脏”的方法来制作原型:在某些情况下,我们的可视化软件在构建它的工程师的机器上运行,并且通过浏览器的控制台选择链接:唯一能够实际操作它的人就是构建它的人。只有当我们看到积极的反应时,才开始在实际的 UI 中集成可视化的完美版本。
作为数据计算需求的反馈来源,与客户的持续互动非常重要。然而,一旦我们开始向客户展示他们特定领域的数据(这有利于保持他们的参与,并帮助我们评估我们的第一个结果的“表面有效性”),交付新数据版本的压力就出现了,并在一定程度上干扰了开发的研究方面。事实证明,在我们正确地自动化我们的计算管道的各个步骤之前,向客户交付定期更新非常耗时,并且我们必须在方法被完善和测试的同时调试我们的数据源。
3.2 校准和质量
与客户作为合作伙伴共同开发的最有价值的好处之一是,他们愿意提供专有数据和真实世界测试条件,以及他们的一般专业知识。
我们通过利用客户对其所在地区的总体交通状况的现有了解,完成了第一步校准。在这里,我们向客户提供了他们网络上交易量的概况,以便他们可以用他们对我们产品准确性的高层次“印象”来回应。然后,更深入地研究,我们提供了一些与流量计数的现场位置比较,并在向他们介绍用例时,询问他们内容是否看起来可信。
基于我们的学习,我们多次重复我们的方法,越来越详细地审查与流量计数的比较。在这一过程中,除了已经公开提供的数据集之外,一些客户还提供了地面真实数据集,我们用这些数据集来调整我们的模型。
nick——高级产品经理:
需要注意的是,“地面实况”一词具有误导性,因为即使是客户也指出可用的交通数据是不完善的。一位客户提到,他们希望我们的数据实际上可以被视为事实,并显示他们有限的计数或调查信息哪里不正确。尽管每个数据集都有不完美的本质,但访问多个专有数据源有助于我们进一步验证和调整我们的模型。
thi baut——高级交通建模专家:
我们的宏伟愿景是能够以完全自动化的方式‘点击按钮’为新客户提供数据,而无需依赖客户数据或手动校准。因此,我们使用地面实况数据(公开可用的和客户提供的)只是为了检查结果的质量,而不是校准参数。能够仅在几次迭代中实现对该数据的合理拟合,而无需任何广泛的参数调整,这给了我们对我们的方法在规模上的可行性的信心。
同样值得注意的是,许多“可用”数据并不容易使用。特别是,流量计数通常以地图的形式提供,需要手动转换为机器可读的格式,并进一步手动映射以将计数站与网络定义相关联。由于没有时间对其进行适当的格式化,只有一小部分数据可以在联合开发阶段使用。
3.3 交付和投放
根据我们的合作协议,产品的第一个功能版本的交付将触发一个购买决策里程碑。在这一点上,根据我们共同定义和开发的内容,客户需要决定是加入折扣产品订阅还是放弃预付款。
我们的协议还包括非约束性语言,要求对产品满意的客户提供我们可以公开引用的认可。虽然这是可选的,但该计划的所有参与者都愿意这样做,这些报价已包含在我们的发布宣传资料中。
顾客的参与似乎有利于增强对公司的认知,愿意向他人推荐公司,以及更高的购买意愿和支付意愿
Schreier,Fuchs 和 Dahl,2012 年
对共同开发的实证研究表明,我们的参与者公开支持街道的意愿并不罕见。随着公司认知度的提高,在产品创新上的合作有助于吸引客户最终购买。虽然这是一个直观的概念,但对于任何考虑这种方法的成本和收益的人来说,这也是一个很好的指导。
nick——高级产品经理:
购买里程碑很难跨越,因为从技术上讲,这是一个销售代表应该管理的销售结束,但对我来说,决定产品何时“准备好”要求承诺是一个判断。在那之前,我们一直是在研究的基础上以同行的身份进行合作,所以很难回到商业对话上来。测试客户的反应各不相同:一些客户认为他们已经“接受”了产品开发轨迹并保留了预算,而另一些客户在开发项目过程中改变了对他们时间和预算的要求。总的来说,反应可以说是积极的和商业上成功的。
乔贝尔——UX 设计经理:
设计准备好发布意味着什么?在我们的案例中,一个高优先级可用性问题的综合,已经被记录和处理,并且有一个符合我们 Figma 设计和我们设计系统的前端。与前端团队一起,我们在频繁的 Slack 和 Zoom 要求澄清中完成了 JIRA 设计标签。团队中的每个人都对结果感到满意。设计永远不会结束,但是总是会积压一些优先级较低的问题。
thi baut——高级交通建模专家:
联合开发过程的一个重要方面是,我们只需要提供客户愿意购买的绝对最小的功能集。特别是,与客户的讨论清楚地表明,他们不需要频繁的数据更新就可以开始从产品中获得价值,这使我们可以在不构建自动更新基础架构的情况下发布产品。没有高质量的报告和在发布前解决数据生产的已知问题让我们感到紧张,但我们都知道这是第一个版本,重要的更新将很快跟上,这让每个人都可以放心。
总结
在评估了我们产品的第一个功能版本并了解了我们为即将到来的版本指定的路线图后,参与我们计划的所有客户都选择订阅并继续使用该产品。
从一个以高层次价值主张假设开始的项目开始,Teralytics 在五个月内就从一个在发布前已经销售了多次的产品中获得了收入。该产品一开始就取得了良好的势头,这是使用其他难以访问的验证数据开发的直接结果,这些数据被证明对产品质量及其上市前的开发、购买和领先客户的认可非常宝贵。
因为我们验证了我们的价值主张,并与多个客户共同开发,所以我们相信这一势头将会持续下去,更广泛的市场份额将会发现我们的解决方案比其他情况下更能满足他们的需求。
推荐
虽然我们开发街道的方法是成功的,但它并不完美。我们从这个过程中学到了很多,我们认为其他人也可以从我们的经历中受益。为此,我们向任何开发类似 B2B 提案的人提供以下建议:
- **参与费用:**共同开发可能是一个漫长的过程,通常需要几个月。进行金融投资的客户更有可能在此期间保持参与。
- **系统地提出假设:**通过将你对产品机会的所有信念和假设整理成目录,你可以开始消除新产品机会中典型的高度不确定性。
- **让产品参与推销:**产品团队成员从现场学习的最佳方式是实际参加销售会议。
- **调整销售激励:**虽然先学习后发展对一家公司整体而言非常重要,但这种重要性应该体现在如何激励销售代表招募共同发展合作伙伴上。
- **用低保真度的概念测试你的价值主张:**在 MVP 测试中,低保真度的线框图将传递关于产品成熟度的正确信息,同时保持对核心主张的关注,而不是对精致 UI 的关注。
- **找出“为什么”😗*如果你的价值主张测试成功了,不要只把这个事实当作是一盏绿灯。进行跟进,以确定用户重视的功能及其服务的目标。在构建之前,使用上下文来重新设想您的产品范围。
- **为以数据为中心的产品校准原型:**一般的设计研究实践倾向于关注用户体验的保真度,首先用线框进行验证,然后详细阐述。然而,对于以数据为中心的产品,考虑何时增加原型中数据的保真度也很重要。很可能在你需要一个完美的用户界面之前,你需要用更高保真的数据进行验证。
- **与三到五个客户共同开发:**除了避免构建一次性产品的风险,如果您的产品受益于校准或培训数据,获得来自多个客户的数据肯定会提高您产品的初始质量。
- **澄清知识产权所有权:**通过避免最终开发成果的所有权模糊不清,保护你在合作开发过程中可能获得的商誉。
- **发布时合作:**由于研究表明,共同开发的合作伙伴自然倾向于购买你的产品,并成为你的产品的拥护者,所以确保你不要在工程完成后立即终止合作。如果您的客户愿意将他们的信誉借给您的市场推广工作,就让他们这么做吧!
参考文献
建立共同创新的企业。(2010 年 10 月 1 日)。哈佛商业评论。https://hbr.org/2010/10/building-the-co-creative-enterprise
Garbugli,E. (2014 年)。精益 B2B:生产企业想要的产品。创建空间独立发布平台。
赫蒙内-古约特,法布利,j .,,芒索,D. (2013)。设计思维 vs 合作创新:两种创新方法的比较。
征途映射 101 。(未注明)。Nngroup.Com。于 2021 年 9 月 23 日从https://www.nngroup.com/articles/journey-mapping-101/检索
梅里韦瑟,E. (2020 年 6 月 12 日)。区别:原型 vs MVP 。Productschool.Com。https://product school . com/blog/product-management-2/difference-prototype-MVP/
奥伊诺宁,M. (2014 年)。客户参与 B2B 市场的共同发展:关于关键贡献和成功因素的文献综述。
Palinkas,L. A .,Horwitz,S. M .,Green,C. A .,Wisdom,J. P .,Duan,n .,& Hoagwood,K. (2015)。混合方法实施研究中定性数据收集和分析的有目的抽样。精神健康管理与政策, 42 (5),533–544。
Pinder,M. (2019 年 1 月 30 日)。 B2B 共同创造:50 个关键的学习和见解——创新委员会。Boardofinnovation.Com。https://www . boardofinnovation . com/blog/B2B-co-creation-50-key-learning-and-insights/
k . reshetilo(2021 年 6 月 15 日)。什么类型的 MVP 适合你的创业?— Greenice。检索于 2021 年 9 月 23 日,来自https://greenice.net/type-mvp-right-startup/
Schreier、c . Fuchs 和 d . w . Dahl(2012 年)。用户设计的创新效应:探索消费者对销售用户设计产品的企业的创新感知。市场营销杂志, 76 (5),18–32。
b2b 市场研究的挑战以及如何克服这些挑战。(2019 年 6 月 5 日)。Businesscasestudies.Co.Uk。https://business case studies . co . uk/the-challenges-of-B2B-market-research-and-how-to-comprise-them/
理想客户档案发展框架。(未注明)。Gartner.Com。2021 年 9 月 23 日检索,来自https://www . Gartner . com/en/articles/the-framework-for-ideal-customer-profile-development
为什么只需要 5 个用户测试。(未注明)。Nngroup.Com。2021 年 9 月 23 日检索,来自https://www . nn group . com/articles/why-you-only-need-to-test-with-5-users/
盲棋日志[0]
部分观测环境的蒙特卡罗树搜索
作者出品。
欢迎来到盲棋日志索引 0。在这里,我计划与我的朋友格莱布·特卡切夫一起分享每周在机器学习宠物项目上工作的经历。该项目的目标是为侦察盲棋开发一个 ML 机器人,它不会做非常愚蠢的动作,也许能够击败一个随机的机器人。一套完整的规则可以在reconchess
python 包的文档中找到。但简而言之,这是一种象棋变体,你看不到对手的移动,每次在决定你的移动之前,你可以选择 3 乘 3 的正方形,并在那里获得棋盘的实际真实状态。听起来很刺激?我们走吧…
相当大的探索
如果你通读侦察棋的规则并思考这里可能适用的 RL 算法,你首先想到的是什么?我敢打赌,对大多数读者来说,它是 AlphaZero。那当然不是没有原因的。但是,AlphaZero 无疑是一种“不要在家里尝试”的项目。尽管从概念上来说这是一个非常简单的方法,但它是由一群杰出的科学家和工程师用非常昂贵的预算建造的工程杰作。它的规模使得它不可战胜。当然,人们可以尝试为单台机器开发一些 DIY 克隆,但它训练一些合理行为的可能性相当小。
动画来自 Tumgir 。
知道了这一切,我们毫不犹豫地决定采用类似 AlphaZero 的方法。不过,我们有一个小小的借口。我们的目标不是重新实现 AlphaZero 的一对一,而是试图建立一种受它启发的东西,即一种结合搜索和学习的算法。
我个人最喜欢的 AlphaZero 鸟瞰图是,我们训练策略函数来压缩蒙特卡罗树搜索(MCTS)迭代和价值函数来预测未访问状态的价值估计。然后,我们使用这两种方法,通过用策略缩小搜索范围和用值函数引导估计来改进 MCTS 的收敛性。这使得即使在评估期间在有限的计算预算下运行,也可以计算强移动。原则上,考虑到无限的计算能力,人们可以只用 MCTS 来解决像围棋和国际象棋这样的游戏。因此,从实现 MCTS 开始,并让它发挥作用(即使相当悲惨)听起来相当合理。
问题开始的地方
方便的是,我有一个 MCTS 实现放在我未完成项目的私人墓地上。唯一的问题是标准 MCTS 适用于完全观察到的环境。相比之下,盲棋只能被部分观察到,因为你看不到对手的行动。这带来了一些复杂性…
MCTS 有自己的环境模拟器(游戏)来搜索可用的动作空间。为了从某个给定的状态运行搜索,我们需要能够将我们的模拟器重置到这个状态。你能看出问题所在,对吗?由于我们不知道状态,在部分观察环境的情况下就不那么简单了。在盲棋的背景下,如果我们假设对手的移动是模拟的一部分,我们可以只为第一个状态建立树,并且只在我们下白棋的情况下。在第一步之后,我们不知道对手棋子的实际位置,因此我们不知道从哪个状态开始寻找下一步。
**下面是后面解释的重点:**一个 MCTS 的内部模拟器,用于从一个单一的状态进行搜索,仍然可以返回给你真实的状态。你将把它编码,并自由地做任何你认为合理的事情。然而,这不会帮助你超越第一步,只有当第一个状态是已知的(如盲棋中的白棋手)。当然,在训练过程中我们可以黑任何我们想黑的东西,甚至可以让一个真实的模拟器还原对手的招式,还原真实的状态。但是这显然不能用于评估,特别是如果你的机器人应该和其他玩家进行一些在线比赛的话。
幸运的是,已经提出了相当多的方法来使 MCTS 适应部分观察的设置。我们发现大卫·西尔弗等人的POM CP(是的,那个家伙)是最简单和最容易实现的,并决定采用它。该方法的思想是将 MCTS 与真实状态的粒子滤波估计器结合起来。我不会详细解释这篇论文,但这里有一个摘要:
- 你通过行为和观察(而不是经典中的状态)来分支你的树,并且为你的树中的每个节点维护一组可能的真实状态。你还需要一个内部模拟器,除了观察结果之外,它还能返回状态(如上所述,它不是一个节目停止器)。
- 开始时,你从初始状态分布中抽取 N 个状态,这些状态应该是已知的(即使是盲棋也是这种情况),并将它们添加到根的集合中。
- 对于每一次 MCTS 迭代,也就是扩展和反向传播,你从当前根的集合中抽取一个状态,并重置你的模拟器。当您在树中移动时,您将内部模拟器返回的每个新状态添加到下一个节点的集合中,该节点对应于您获得的观察。
- 一旦你建立了一个树或者超出了你的计算预算,你就可以根据收集到的统计数据选择最好的移动,并把它提供给真正的模拟器。True simulator 只返回一个观察结果,您可以用它来选择树的下一级的正确节点,并将其提升到根节点。然后你重复第三步。
即使考虑到论文中提供的使你的状态估计稳定的众多技巧,这似乎仍然是解决 MCTS 部分可观测性的最简单的方法。因此,我们在晚上花了很大一部分空闲时间来实施它。在开始的时候,我们确实有点担心,不知道“感觉”动作(那些你得到一个 3x 3 正方形的真实状态)在这种方法中是否“有意义”。幸运的是,经过一些头脑风暴、绘图和编写第一个原型之后,我们意识到它们可以很自然地集成在一起。本质上,你把你真实状态的粒子估计散布在树的不同分支上,因为“好的”感觉动作揭示了额外的信息并导致不同的观察结果。理论上,这将减少状态估计的方差,一切都将顺利进行。然而,没过多久,我们意识到还有一个问题…
让我们一起玩吧
AlphaZero 的另一个被认为对超人表现至关重要的关键因素是自我游戏。对于 MCTS 来说,自我游戏是通过在同一棵树上以循环方式为两个玩家寻找最佳移动来实现的。
现在让我们忘记部分可观察性,假设我们正在为一些根状态构建一棵树,并根据我们之前收集的一些统计数据(或者如果统计数据不可用,则随机选择)选择一个动作。我们现在过渡到我们的对手必须采取行动的状态。那么我们应该采取哪一步呢?我已经提到过,最简单的方法是对对手使用一些固定的策略。然而,如果我们真正的对手有不同的打法,那就不行了。相反,在自玩 MCTS 中,我们将这个状态添加为节点,并从它开始继续树搜索,就像我们将为对手玩一样。本质上,当展开树时,你假设你的对手也使用 MCTS。这应该可以让你想出更强的动作。这种方法也可以很好地适应您的计算预算。一个额外的好处是,对于零和游戏(如国际象棋),将 MCTS 扩展到自我游戏并不需要太多,至少在概念上是如此。当反向传播对手节点的统计数据时,你需要做的就是否定一个玩家在首次展示中获得的奖励。当然,这需要一个足够通用的实现来跟踪玩家的 id 并仔细处理统计数据的更新。在相当多的“设计讨论”之后,我们设法实现了类似的东西,并且看起来工作可靠。
让我们回到我们的部分观察设置,在这里一切都变得“有点复杂”。现在,当为一个已知状态(例如,可能是初始状态)构建树时,由于我们的内部模拟器,我们仍然可以进行自我游戏。如前所述,我们确实对它有完全的控制权,我们可以通过某种方式实现它,以返回对手的观察和动作,这样我们就可以在它们上分支,并向我们的树中添加相应的节点。当我们超出了一步棋的计算预算,选择了目前为止我们发现的最好的一步棋,并将其输入到真正的模拟器中时,问题就出现了。模拟器的状态很先进,对手也做出了自己的举动,但我们对此一无所知。现在我们需要在我们的子树中找到一个新的根节点,它对应于再次轮到我们的观察,并且可能包含状态估计集合中的真实状态。因为我们不知道对手得到了哪个观察值,也不知道他选择了哪个移动,所以我们不知道要遍历哪个分支。因此,我们没有对当前真实状态的估计,也无法重置我们的内部模拟器来再次运行搜索…
抱歉,我知道没有一些图纸可能会很难掌握,但我真的不想把这些日志变成全取教程。否则,我会花更多的时间来准备这些,而不是实际参与项目。因此,如果你有任何问题,或者对这个话题感到兴奋,请在评论中或在 LinkedIn 上联系。我将很高兴与你讨论它。
当然,我们想到了一些简单的方法,比如遍历对应于玩家移动的分支,然后汇集并合并来自下一级对应于我们回合的子节点的所有可用状态估计。然而,这可能会给我们的真实状态估计带来相当大的差异。此外,根据规则,当对手抓住你的棋子时,你实际上获得了一些关于他的移动的信息。所以你可能会遇到这样的情况,你从孩子那里收集了一些状态,而这些状态对于你刚刚得到的新信息来说是不可能的。或者更糟,在你没有任何与新信息一致的粒子估计的情况下。无论如何,这听起来是最直接的开始方式,所以下周很可能会继续使用。
这就是盲棋日志的迭代。请继续关注大约一周后的下一次更新。
书籍写作模式分析
通过一个用例开始 NLTK 和 Python 文本分析。
西雅图图书馆 Andrew Zhu 摄
当我还是学生的时候,我读到一些文章,说语言学家可以使用文本分析技术来确定一本匿名书的作者。我当时觉得很酷。
回想起来,感觉这个手法还是很酷的。但是,如今在 NLTK 和 Python 的帮助下,你和我可以通过几行代码成为“真正的”语言学家。
准备分析目标
您不需要编写一个爬虫来搜索分析语料库。出于学习和研究的目的,NLTK 包中已经有一个巨大的文本数据库。如果您没有安装这个包,只需运行 pip 来安装它。
pip install nltk
然后从古腾堡下载书籍数据,这是古腾堡项目电子文本档案中的一小部分文本。
import nltk
nltk.download("gutenberg")
下载应该在 1 或 2 秒内完成。让我们列出下载书籍的名单。
from nltk.corpus import gutenberg
gutenberg.fileids()
你会看到像莎士比亚的《凯撒》、奥斯汀的《爱玛》和《圣经》等书籍。让我们看看圣经 KJV 版本中包含了多少单词。
bible = gutenberg.words('bible-kjv.txt')
len(bible)
共计 1010654 字。
衡量图书作者写作模式的标准
我将使用以下 3 个指标来检测作者的写作模式。
- 平均字数
这个指标反映了作者的词汇使用偏好,长词还是短词。 - 一个句子的平均字数
这个度量反映了作者的句子偏好,喜欢用长句还是短句。 - 书中使用的平均不同词汇
这个指标反映了作者的词汇量,很难伪造。
你可能会说,为什么不从每个作者那里获取最常用的单词和短语。是的,这很好,可能会产生更有趣的结果。但是频繁的单词检测也会给上下文带来额外的逻辑,可能会破坏这篇文章的可读性。(也许值得再写一篇如何自动检测关键词的文章)。
在古腾堡的书上应用 3 个标准
现在,用 Python 代码将这三个指标应用到古腾堡的书中。逻辑很简单,用 NLTK 的raw()
、words()
和sents()
来捕捉字符#、单词#、句子#。并使用酷炫的 Python comprehensive with set
数据容器在一行代码中获取词汇编号。
如果你愿意,你可以把它复制并粘贴到你的 Jupyter 笔记本上来看看结果。
import pandas as pd
data = []
for fileid in gutenberg.fileids():
num_chars = len(gutenberg.raw(fileid))
num_words = len(gutenberg.words(fileid))
num_sents = len(gutenberg.sents(fileid))
# get total vocabulary used in this book
num_vocab = len(set(w.lower() for w in gutenberg.words(fileid)))
data.append([
fileid.split('.')[0] # remove .txt from file name
,round(num_chars/num_words)
,round(num_words/num_sents)
# total vocabulary used divide total words used
,round(num_vocab/num_words,2)
])
pattern_metrics = pd.DataFrame(data,columns=['author-book','chars_per_word','words_per_sentence','vocabulary_rate'])
pattern_metrics
从结果集中,我们可以很容易地看到奥斯汀每句话使用 25 到 28 个单词,而莎士比亚使用更多的短句,一致为 12 个。
数据表中的 3 个度量结果
莎士比亚被誉为英语的奠基者,的确,除了布莱克的诗之外,他的作品显然比其他作品用了更多的词汇。
结果是如此惊人和明显,你可以从这个表中得出更多的结论。如果数字不够清楚,条形图应该更直观地显示结果。
可视化指标
有了这种图表,你也可以为你的孩子列出阅读清单。开始阅读时使用较少的词汇和短句。嗯,Edgeworth 的家长助手看起来是个不错的开始。半夜睡不着的时候,去读几首布莱克的诗可能是个不错的选择。
结论
下次,当 J.K .罗琳用假名出版另一本小说时。你可以很容易地运行几行 Python 代码来确定这本新小说是不是 J.K .罗琳写的。发表在杂志上,做一个“语言学家”。也许另一个年轻的头脑会阅读它,并成为数据科学家。
Power BI 中的书签—这些数据意味着什么?
书签是 Power BI 中最强大的功能之一!但是,有一些微妙的选择可以产生巨大的差异
作者图片
要我说,书签是 Power BI 中最强大的功能之一!我已经写了很多次关于书签的文章:例如,它们如何帮助你将过滤体验推向一个全新的水平,或者重新发明传统的视觉效果,比如卡片,一直到使你的书签安全为“点击”行为。
书签到底是什么?
书签是一项强大的 BI 功能,使您能够捕获报告页面的当前状态。然后,您可以通过按钮、图像、形状等的 Action 属性,将这个捕获的状态称为“特殊”页面。
默认情况下,当您创建书签时,它将捕获以下报告元素的当前状态:
- 过滤器和切片器,包括 切片器状态
- 视觉效果的选择状态(例如,如果您的视觉效果是交叉突出显示的)
- 钻孔位置
- 视觉效果内的排序
- 报告元素的可见性(这是通过选择窗格处理的)
- 聚焦或聚光灯模式,如果有的话
然而,一旦你选择创建一个书签,你会看到一个带有一些选项的下拉菜单——而且大部分选项已经被预先选中了!那么,让我们来看看这些选项对您意味着什么…
为什么数据选项很重要?
当您使用书签时,您需要做的第一步是打开书签和选择窗格。书签,原因很明显:)…而且,通过选择窗格,您可以完全控制单个报告元素的可见性。
作者图片
现在,当我添加一个书签并点击它旁边的三个点时,会出现一个下拉菜单,其中有许多选项已被预先选中:
作者图片
当您保持选中数据选项时,它将捕获过滤器、切片器和视觉效果中数据的当前状态,并且每次您导航到此书签时,您将看到完全相同的数据状态!
让我们创建一个书签,它将捕获由 Contoso 品牌过滤的视觉效果的状态,并选中数据选项:
作者图片
您可能已经注意到,在所有报表页面视图中,数据都是由 Contoso 品牌进行筛选的。
下一步是创建捕获完全相同的页面状态的书签,但这次不选中数据选项:
作者图片
然后,我将在报告页面上放置两个按钮,以控制书签之间的导航。为了实现这一点,我将在 Action 属性下指定我的书签:
作者图片
现在,让我们回到报告的原始状态,即品牌过滤器未被分割为特定值时的状态。看看如果我点击“数据未检查”按钮会发生什么:
作者图片
什么都没发生!由于我们未选中数据选项,因此没有切片器选择被传输到书签。但是,如果我单击另一个按钮“数据检查”,我们可以发现完全不同的行为:
作者图片
在这种情况下,由于在创建书签时选中了数据选项,捕获的状态包括在切片器中定义的数据点—因此,无论我们在品牌切片器中选择哪个值,只要我单击数据已选中按钮,我就会看到 Contoso 的数字!
让我给你看看这个是什么样子的:
作者图片
结论
正如您所看到的,书签是非常强大的功能,可以应用于各种不同的报告场景。但是,就像所有强大的东西一样,它需要完全控制它,以便以正确的方式工作。这就是为什么理解书签附带的各种选项极其重要。
在大多数情况下,这些细微的差异会对整个报表体验产生巨大的影响。说实话,在大多数情况下,您会在创建书签时取消选中数据选项,但有一个非常方便的用例是让数据选项保持选中,我将在下一篇文章中解释这一点。
感谢阅读!
成为会员,阅读 Medium 上的每一个故事!
用 Python 学习自然语言处理的阅读列表
劳拉·卡弗在 Unsplash 上的照片
一位前同事最近向他寻求建议,如何最好地从头开始学习自然语言处理(NLP)。
“优秀!”我以为;我已经为另一位前同事回答了完全相同的问题,所以这应该和重新整理之前的回答一样简单,对吗?
嗯,除了一个小细节之外,它本来是可以的:我完全不知道我把资源列表保存在哪里了。
为此,为了避免再次违反 DRY 原则(即“不要重复自己”),我写了这篇文章,这样如果/当我收到另一个请求时,我就可以把链接转发给他们。
以下是我非常固执己见的观点。不同意也可以留下你推荐的课程的链接。
免责声明#1
我假设你对线性代数、统计学和语言学有所了解。
如果没有,停止。别说了。为什么?三个原因:
- 为了分析句子,识别词性,以及任何其他任务,你需要对语言学有一个坚实的理解,所以考虑参加一个课程,比如这个 one 会给你一个概述。
- 统计意义、相关性、分布和概率是机器学习的基本概念。为了填补这个空白,给自己挑一本关于统计学的好书,比如数据科学实用统计学并努力阅读。
- 至于线性代数,正如下面的文章巧妙地解释的那样,它是深度学习和计算文本相似性的基础:
为此,帮自己一个忙,看看 3Brown1Blue 的这个令人惊叹的视频系列
免责声明#2
下面概述的道路是我希望我会选择的道路。
“你走了什么路?”
好吧,简而言之,我花了几年(是的,几年)试图找出如何使用编码作为工具来完成我想要的项目。
听起来不错,对吧?
嗯,问题是我没有学习 Python 的基础知识,所以当我的代码抛出一个错误,或者,上帝保佑,我试图从 StackOverflow 和/或 Github 中重新使用别人的代码,但它不能开箱即用时,我不知道该怎么办。
为什么?
简单地说,我并不精通 Python,因此,我无法适应不熟悉的情况。
为此,如果你想专门学习 NLP 或一般的机器学习,你需要做的第一件事就是学习中级水平的编码。
为什么是中级?
因为你遇到的几乎每一个 Python for NLP 资源都会推荐你“熟悉”、“了解”、“精通”或其他一些难以置信的模糊标准,以便从他们的产品或平台中获得最大收益。
简而言之,你越擅长编码,就越容易将你在旅途中学到的概念付诸实践。
那么我如何达到“中级”Python 呢?
谢谢你的关心😄
Python 基础:Python 3 实用介绍
这本书由 Real Python 的团队编写,前 10 章涵盖了从在机器上安装 Python、运行脚本(不是笔记本)、数据类型、函数、调试和通过定义类进行面向对象编程(OOP)的所有内容。
至于本书的后半部分,它是一系列编码问题的集合,比如从网站上抓取和解析文本(第 16 章)以及为你的程序创建 GUI(第 18 章)。
因此,我建议你仔细阅读前十章的每一页,如果你感兴趣的话,可以阅读剩下的任何一章。
你可以在他们的网站上拿一本。
用于数据分析的 Python
当你管理数据时,你永远不会对数字或熊猫太精通,所以你花在研究韦斯·麦金尼的这本极其通俗易懂的书的每一分钟都应该被看作是对你未来的帮助。
再一次,帮你自己一个忙,在深入研究 NumPy 和 pandas 之前,先用第三章回顾一下 Python 中内置的数据结构,同时很好地理解 matplotlib。然而,这本书最有用的部分是第 14 章,其中包含了五个实例。
一定要在 GitHub 上访问该书的回购以留下一颗星,查看每一章的笔记本,并找到在哪里拿起一本。
使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习
既然您的 Python 已经达到标准,并且可以操作数据,那么是时候学习机器学习了,没有比 Aurélien Geron 的这本宝石更好的学习机器学习基础的书了。虽然这本书涵盖了从逻辑回归到神经网络的所有内容(包括关于 NLP 注意力的讨论),但真正的优势在于作者除了简单解释如何使用不同技术之外,还讨论了为什么要使用不同技术。因此,第 2 章的端到端机器学习项目,以及附录 B 的机器学习项目清单,值得这本书的成本。
要了解关于这篇文章的更多信息,以及在不下载或安装任何东西的情况下运行附带的笔记本,请访问作者的 repo 。
实用自然语言处理
我会读的第一本彻头彻尾的 NLP 书就是这本书。是什么让它这么好?这本书采用基于任务的方法,提出一个问题,然后询问如何使用 NLP 解决这个问题。
此外,它还展示了社交媒体、金融和法律等不同领域的应用。此外,几乎每个问题都通过多种方法解决,因此读者可以了解主要的 NLP 库,如 Spacy 、 NLTK 、 Textblob 、 Gensim 等等。
请访问该书的网站了解更多信息。
使用 Python 进行文本分析的蓝图
现在您已经掌握了 NLP,让我们来扩展一下您可以用它做什么。了解什么是可能的一种方法是通过观察人们如何使用 NLP 来寻找商业问题的解决方案。
这正是这本书所扮演的角色;它包含大约 100 个带有蓝图的常见用例。
但是什么是蓝图呢?"
作者指出,“蓝图[……]是一个常见问题的最佳实践解决方案。”本质上,它们是模板,因为您精通 Python,所以您可以最小程度地适应您的特定用例。
请务必前往该书的回购留下一颗星,看看工作的例子。
自然语言处理转换器
最后,既然您已经有了坚实的 Python 基础,并且了解了可以用 NLP 解决哪些问题以及如何解决这些问题,那么是时候了解最先进的架构:transformers 了。
什么是变形金刚?
抱歉,这超出了本文的范围;然而,如果你真的想知道,我推荐阅读开创性的作品 注意力是你需要的全部 和/或插图变压器来获得想法的要点。
一旦你有了这些,一定要看看 Denis Rothman 的翻页文本,它涵盖了像微调和预训练模型以及用例这样的主题。要了解更多关于本文的信息,请点击此处。
填补空白的书籍
虽然我认为上面的文本是核心,但你永远也不会太了解你的工具是如何工作的。
因此,以下书籍提供了一些有用工具的深度挖掘。
通过Duygu altn ok掌握空间应该是你学习所有空间知识的最佳指南。
通过解决(几乎)任何机器学习问题由 Abhishek Thakur 完成,这类似于使用 Scikit-Learn、Keras & TensorFlow 的动手机器学习,但使用 PyTorch。
由 Sudharsan Ravichandiran 撰写的Google BERT入门是一篇很好的关于 BERT 架构的文章。
结论
虽然我是上面列出的所有书籍的超级粉丝,但我毫不怀疑我忽略了一些应该包括在内的书籍。
为此,请在你推荐给那些想在新的一年开始 NLP 之旅的人的书中留下评论。
编码快乐!
熊猫布尔掩蔽
过滤熊猫数据帧
拉里·科斯塔莱斯在 Unsplash 上的照片
在 Miki Tebeka 的优秀“ 更快的熊猫**”**课程中的一个主题是如何使用布尔掩码来过滤熊猫中的数据。我想实践我所学的,所以我更新了一个最近的项目来使用布尔遮罩。我认为这是一个非常有用的技术,所以我想分享一下我是如何更新代码的。我将向您展示如何在一些直接取自我的项目的 之前的 和 之后的 代码片段中使用布尔掩码。
在我们深入片段之前,我将提供一些背景。我想分析一个流行的烹饪应用的评论。我下载了这些评论,并重点关注了 2019 年和 2020 年发布的评论。为什么是这个时间范围?我想看看 COVID 之前和期间的应用评价是否有所不同。我的家庭在 2020 年增加了家庭烹饪,我很好奇烹饪应用的评论是否会显示应用使用和/或用户享受的增加。
如何使用布尔掩码
现在,让我们通过检查一些 之前的 和 之后的 代码片段来学习如何使用布尔掩码。
下面的代码筛选数据集,只使用年份为 2019 或 2020 的行。
之前:
*#reviewTimestamp is a datetime objectreviews_df = all_reviews_df[(all_reviews_df[‘reviewTimestamp’].dt.year == 2019) | (all_reviews_df[‘reviewTimestamp’].dt.year == 2020)]*
后:
*pre_mask = all_reviews_df[‘reviewTimestamp’].dt.year == 2019
during_mask = all_reviews_df[‘reviewTimestamp’].dt.year == 2020reviews_df = all_reviews_df[pre_mask | during_mask]*
下面还有一个例子。该代码过滤数据集,以找到 2019 年和 2020 年的“高”和“低”评论。
之前:
*top_reviews_2019 = reviews_df[(reviews_df[‘reviewTimestamp’].dt.year == 2019) & (reviews_df[‘score’] == 5)][‘content’].to_list()low_reviews_20191 = reviews_df[(reviews_df[‘reviewTimestamp’].dt.year == 2019) & (reviews_df[‘score’] < 3)][‘content’].to_list()top_reviews_2020 = reviews_df[(reviews_df[‘reviewTimestamp’].dt.year == 2020) & (reviews_df[‘score’] == 5)][‘content’].to_list()low_reviews_2020 = reviews_df[(reviews_df[‘reviewTimestamp’].dt.year == 2020) & (reviews_df[‘score’] < 3)][‘content’].to_list()*
后:
*pre_mask = reviews_df[‘reviewTimestamp’].dt.year == 2019
during_mask = reviews_df[‘reviewTimestamp’].dt.year == 2020
high_mask = reviews_df[‘score’] == 5
low_mask = reviews_df[‘score’] < 3high_reviews_2019 = reviews_df[pre_mask & high_mask][‘content’].to_list()low_reviews_2019 = reviews_df[pre_mask & low_mask][‘content’].to_list() high_reviews_2020 = reviews_df[during_mask & high_mask][‘content’].to_list()low_reviews_2020 = reviews_df[during_mask & low_mask][‘content’].to_list()*
为什么使用布尔掩码?
为什么我发现做这些改变很有用?第一个原因是它使代码更容易阅读和修改。例如,我可能会用 2018 年和 2019 年的 COVID 前时间框架以及 2020 年和 2021 年的 COVID 期间时间框架来更新分析。我只需要更新布尔掩码定义中的标准,而不是遍历代码并替换我过滤数据集的每个地方的标准。
第二个原因是,有人告诉我使用布尔掩码可以提高性能。我使用 IPython 交互命令 shell 中的%timeit 魔法命令(在单元模式下)对我的代码进行了性能测试。我的性能检查显示,使用布尔掩码的代码比使用常规条件过滤的代码更快。在我的电脑上,代码比 T11 快了 7 倍。
图片由作者提供
现在,您已经看到了一些如何使用布尔掩码的示例,并且知道了应该考虑在代码中使用它们的原因。请随时提出建议,并让我知道这些内容是否对您有所帮助。
编码快乐!
布尔既有趣又神秘
数据科学
一些有趣的关于布尔数据类型的谜题和答案。
在数据科学和编程中,布尔(真和假)数据被认为是简单而枯燥的。我相信布尔是有趣的,如果你不知道他们的逻辑,有时是不可预测的。我将说服你们中的许多人,一些有趣的特性与布尔对象相关联,而你们可能忽略了这些特性。
简单的难题
让我从一个简单的开始。
这段代码应该能够识别 int 和 boolean 对象。如果输入对象不是 boolean 或 integer,它会告诉您该对象是“其他东西!”。最后,我用四个例子测试了这个功能。您对这段代码的输出有什么猜测?看起来很简单,对吧?
An int object.
Something else!
A bool object.
A bool object.
如果你的猜测和上面的输出一样,那你就和我第一次一样错了。实际上,正确的答案是:
An int object.
Something else!
An int object.
An int object.
为什么布尔对象(true 和 False)被识别为整数对象?
事实上,在 Python 中,布尔类是从整数类继承而来的。长话短说,在 Python 的初始版本中没有布尔对象,他们用 1 和 0 代替 True 和 False。当他们决定在 Python 中加入布尔对象时,他们从 integer 类继承了布尔类(在这里阅读这个故事)。
当您运行代码时,因为代码将 True 和 False 识别为 int 类型,所以它打印“一个 int 对象”让你感到困惑和惊讶。
难题
好吧,这个很简单。让我向您展示另一段代码,并让您猜测输出。
第一次面对这个挑战,好像超级容易。我想,首先,两个函数几乎相同,应该给我相同的结果。0 是假的,1 是真的,2 应该是假的。简单地说,我期望类似于:
False
True
FalseFalse
True
False
但是,正如你所猜测的,我的答案是不正确的。正确答案是:
False
True
True
False
True
False
什么?!意思是if 2
和if 2 == True
在 Python 里不等于,说真的?第一个表达式为真,第二个为假。怎么可能呢?!
让我告诉你为什么会这样。我们有两种表达方式。先说简单的,就是if 2 == True
。大家知道,在 Python 中,True
等于1
。因此2 == True
是假的,这就是为什么与if 2 == True
相关的代码块没有被执行。这很容易理解。
神秘的是为什么对应于if 2
(在func1()
中)的代码块被执行并返回 True?是不是说2
等于True
?如果2
等于True
,为什么2 == True
不成立。很困惑,对吧?
如果你好奇想知道答案,这里就是。答案就在面向对象编程中,面向对象编程是 Python 的基础。如你所知,Python 中的一切都是对象 。对象有属性和方法。有些属性和方法被称为神奇的方法或属性。
虽然这对于本文的其余部分不是必需的,但我鼓励您阅读我的 Python 面向对象编程三部曲。
魔法 _ 布尔 __
您可以为每个类和对象定义的神奇方法之一是__bool__
。这个神奇的方法可以返回False
或者True
。当使用 if 和一个对象(如if obj:
)时,if
调用__bool__
方法并根据其返回值,执行或不执行相应的块。让我们用 Python 做一个快速测试,看看__bool__
方法为整数对象2
和0
返回了什么。
>>> n = 2
>>> n.__bool__()
True
>>> n = 0
>>> n.__bool__()
False
你也可以使用一个名为bool()
的内置派系来检查一个对象的布尔值。
>>> n = 2
>>> bool(n)
True
你可以利用__bool__
来改进面向对象编程。想想你的对象最重要的布尔方面是什么,基于此,通过__bool__
方法返回一个合适的布尔值。例如,假设您正在进行一个强化学习项目,并且为您的机器人或代理准备了一个对象。在我看来,机器人/智能体最重要的方面是它是否活着。因此,我定义了__bool__
方法,以在其返回值中反映该属性。下面是我将如何定义这类对象的一个示例:
在这个例子中,有一个名为score
的属性,它存储了机器人对象的分数。当分数高于 0 时,你的机器人是活的,并且bool()
返回 True。我们将机器人分数降低 10,然后降低 90(初始分数为 100)。一旦分数变为 0,对象将返回 False 以响应任何调用其布尔值的函数。用户可以在他/她的代码中利用这个属性,例如while r: …
或if r: …
。
如果对象没有__bool__
方法,if
,bool()
和类似的函数会寻找另一个神奇的方法叫做__len__
。该方法通常返回对象的长度。如果__len__
返回 0, bool()
返回 False 否则,它返回 True。在前面的例子中,我们可以简单地通过__len__
返回分数,我们不再需要定义__bool__
。下面是我们如何改变代码并得到相同的结果。
以你所学的所有知识,你一定能回答我的最后一个难题。以下代码的输出是什么?
bool("")
摘要
要理解 Python 中布尔数据类型和对象的行为,必须知道这种数据类型是从 integer 类继承的。另外,像__bool__
和__len__
这样的神奇方法定义了一个对象如何响应像bool(obj)
这样的函数。
在推特上关注我的最新报道:https://twitter.com/TamimiNas
增强基本数据集和简单 CNN 来回答实际环境问题
实践教程
CNN——具有数据扩充、背景和多输出的树叶分类。
克里斯·劳顿在 Unsplash 上的照片
1.语境
该项目是我们在 Data Scientist 的数据科学家训练营课程验证的一部分,将我们在这 11 周的理论课中学到的一切付诸实践,并确保每个主题都已掌握。
这个项目的目标是从图片中定位和分类植物的种类。一旦分类完成,返回植物的描述,并确定最终的疾病。
2.项目
数据集—迭代 1
数据集新植物病害数据集来自 Kaggle,被许多用户彻底使用,不包含任何错误或错误分类的图像。
该数据集由放置在统一背景上的树叶组成:
来自数据集的图像样本(来源)
包含大约 87,000 张叶子的图片——患病的和健康的。这些图片代表了 38 类 14 种植物:
每株植物病害
分配情况如下:
数据集中类的分布— 作者提供的图片
该数据集是使用另一个数据集的离线扩充重新创建的,因此,正如我们在前面的图表中看到的那样,这些类非常平衡(每个类约占 3%—外部层)。
为了在植物环境中的树叶图像上测试我们的模型,我们实现了一个 web 抓取脚本来从“Google image”中提取图像。这个测试数据集类似地由 38 个类加上另一个代表没有植物的图像的类“其他”组成。
与原始数据集相比,测试集中的图像不遵循相同的格式(每个图像一片叶子)。
深度学习—迭代 1
作为第一次迭代,我们用 TensorFlow 开发了自己的 CNN 模型:
CNN 模型— 作者图片
该模型由三个卷积块和一个分类块组成。
卷积块层数:
- 图像大小:图像大小调整为 128x128x3,这是时间计算和信息损失之间的一个很好的折衷。
- Conv2D :使用滤镜从图像中提取图案。为了捕捉更大的图案组合,每个 Conv2D 层的滤镜数量都比前一层增加了一倍。
- 激活功能:使用 CNN 最常用的 Relu 应用非线性。
- batch normalization(BN):BN 用于归一化前几层的输出。它使 CNN 更快,更稳定,并减少过度拟合。
- MaxPool2D :通过减少像素数量来降低图像的维数,它减少了模型的参数数量,并为内部表示提供了轻微的尺度不变性。
分类块层数:
- 密集:用于分类的三个全连通层。
- 退出:通过在训练过程中随机忽略选定的神经元来防止过拟合。
conv 区块与分类区块之间的层:
- globalaveragepool2d(GAP):它对每个特征图的值进行平均。与“展平”层不同,“间隙”移除了大量可训练参数,提供了平移不变性,从而减少了过度拟合的趋势。
Flatten 和 GlobalAveragePooling 之间的差异— 作者图片
正则化:为了防止过度拟合,我们使用了 L2 正则化,它将参数(权重)的平方和添加到损失函数中。
型号代码如下所示:
该模型在验证数据集上的准确率为 99.3% 。
然而,它在测试数据集上给出了非常糟糕的预测,准确率为 13.2% 。
迭代 1 总结:
- 数据集:“新植物疾病数据集”,其是在统一背景上的叶子图像
- 对验证数据集的出色预测— val 准确率> 99%。
- 对由自然环境中的树叶图像组成的测试数据集的错误预测。
- 即使图像上没有任何植物,模型也能预测植物。
对自然环境中的树叶图像预测不佳的主要原因是我们的模型是用统一背景上的树叶图像数据集训练的。此外,正如在训练数据集中一样,没有图像是没有植物的,如果发生这种情况,我们的模型不可能预测图像中不存在植物。
迭代 1 图— 作者图片
深度学习—迭代 2
在第二次迭代中,我们试图通过给树叶图像添加植物背景来改进我们的模型。我们修改了我们的数据集,并使用了来自 Kaggle 的植物村数据集,它与我们的第一个数据集相似(在统一的背景上留下图像),但没有任何数据扩充。因此,数据集是高度不平衡的(来自一些类的超过 5000 幅图像,而来自其他类的不到 200 幅图像),但是包含我们可以用来添加植物背景的分割的叶子图像。
来自新数据集的分割图像样本(来源)
为了重新平衡该数据集,按照以下描述的转换进行了数据扩充:
数据扩充— 作者提供的图片
为了检测图像中植物的缺失,我们使用来自“Image-net”数据集的随机图像向我们的数据集添加了另一个类“ Others ”。
来自新类别"其他 " ( 来源)的图像样本
在数据扩充和添加新类别“其他”之后,该数据集包含 114 077 幅图像,分布如下:
数据扩充后数据集中类的分布— 作者图片
我们使用“自定义数据集”来加载图片并添加背景,用我们的背景像素替换分段图片的黑色像素。
用函数“tf.where”替换像素,如果叶子图像像素的值低于定义的阈值,则用背景图像之一替换叶子图像的像素。
注意,在第二次迭代中,我们对每种植物只使用了一个背景。例如,任何番茄类都使用相同的番茄植物背景。
背景添加过程— 作者图片
代码如下所示:
我们使用相同的模型来比较两次迭代的结果,只是最后一个密集层的输出形状为 39 (38 株植物+其他)。
该模型在新的验证数据集上给出了 95.1% 的准确度。
然而,它在测试数据集上给出了非常糟糕的预测,准确率为 10.5% 。
第二次迭代总结:
- 数据集“Plant Village”增加了数据,并从数据集“Image-Net”添加了新的类。
- 在模型训练期间添加背景(每个植物类型只有一个背景)。
- 好的预测与数据集相关的图像。
- 测试数据集上的预测略有改进,但性能仍低于预期,尤其是在无法完全看到树叶的图像上。
- 该模型似乎能够预测图像是否不是植物。
- 该模型似乎专注于背景而不是叶子本身。
在迭代 2 期间,我们的模型学会了使用背景而不是树叶,所以我们使用完全相同的模型和方法做了另一次迭代“ 2-bis ”,但不是每株植物只有一个背景,我们为每株植物创建了一组背景,每次都将随机选择**。**
因此,我们为每株植物(我们的数据集中有 14 株植物)创建了一个文件夹,在每个文件夹中:
- 5 幅实际植物的图像(即:所有马铃薯种类的马铃薯植物)。
- 4 植物、草坪和森林的图像将在每个文件夹中共享。
这应该有助于模型:
- 而不是聚焦在背景图像上。
- 在真实环境中有更好的表现。
该模型(2-bis)在验证数据集上给出了 98.4%** 的准确度。**
然而,它在测试数据集上给出了非常糟糕的预测,准确率为 18.1% 。
迭代 2 之二的总结:
- 数据集:“植物村”,增加了数据,并从数据集“Image-Net”添加了新的类。
- 在模型训练期间添加背景(随机)。
- 好的预测与数据集相关的图像。
- 与迭代 2 相比,测试数据集上的预测略有改进,因为背景是随机的,所以模型没有关注它。
- 该模型似乎能够预测图像是否不是植物。
我们可以在测试数据集上看到一些预测性能的改进,尽管结果仍然低于预期。然而,这个模型可以预测一个图像是否是植物,这与我们的第一次迭代相比是一个增强。
迭代 2 图— 作者图片
深度学习——迭代 3
在迭代 2 中,我们包含了一个名为“Others”的新类,表示“任何东西”的图像。这一举措是为了帮助模型避免在图像中没有植物时预测植物。但是我们可以看到,在测试数据集上的结果不如预期的好。一种解决方案是预测图像中植物出现的概率。一个输出表示图像是否包含植物,第二个输出表示植物分类。
模型迭代 3 — 作者图片
此后,模型的代码—我们使用功能模型:
为了评估我们的模型预测数据集的效果,我们需要创建一个自定义损失函数:
定制损失函数
用 P_other ={0,1}。图像是植物(=1)还是植物(=0)。如果图像不是植物,则不计算分类交叉熵。
根据我们的模型,第一个神经元代表图像是否包含植物,其他 38 个神经元代表植物分类。
该模型(3)在验证数据集上给出了 97.4%** 的准确度。**
然而,它在测试数据集上给出了非常糟糕的预测,准确率为 19.2% 。
但是,在测试数据集上对没有植物的图像(类别:其他)的预测比用迭代 2-bis 模型完成的预测要好。
迭代 3 总结:
- 数据集:“植物村”,增加了数据,并从数据集“Image-Net”添加了新的类。
- 对验证数据集的出色预测— val 准确率> 99%。
- 对测试数据集的预测仍然很差。
- 在测试数据集上更好地预测没有植物的图像(类别:其他)。
迭代 3 图— 作者提供的图片
改进
- 包含真实环境中图像的新数据集:**正如我们在几个测试阶段发现的那样,该模型在只有一片叶子和统一背景的图像上表现很好,但无法预测自然环境中的图像。我们试图通过添加背景来解决这个问题,但我们仍然可以在结果中看到一些缺陷。从直接在真实环境中拍摄的图像(或两者的组合)创建完整的数据集可能有助于提高结果的可靠性。这可以使用网页抓取来完成。
- ****两个模型串联(多输出):在当前数据集中,一些植物有相同的病害(即 : 番茄和桃叶的细菌性斑点病或马铃薯和番茄叶的晚疫病……)。在自然环境中,我们的模型似乎能发现正确的疾病,但不能发现正确的植物。一个解决方案是创建一个多输出模型:
- 一个关注休假分类的模型
- 一个侧重于疾病分类的模型
用这种方法,我们可以分别预测植物和疾病。因此,这可以改进模型,并可能预测新植物的已知疾病——即苹果上的细菌性斑点(已知疾病)。
多输出模型— 作者图片
**3.**语义分割:为了提高在自然环境中的预测,使用像 UNET 这样的语义分割模型,将有助于提取树叶的像素,然后我们将使用树叶分类模型来预测植物本身。
**4.**更深的模型:由于真实环境中的叶子图像更复杂,具有更多卷积块的更深的模型,为了捕捉更多模式,应该给出更好的预测。
概括起来
通过这三次迭代开发的模型在验证数据集上进行了出色的预测,结果高达 97%。在自然环境中的图像上测试这些模型是一个自然的选择,以代表一个真实的商业案例。
但我们很快发现,即使我们尽最大努力改进我们的模型,以检测真实环境中的图像,我们也无法实现这个目标。
我们可以尽可能地调整模型,它只能预测它被训练的内容——统一背景上的树叶图像。
我们能够验证一些假设——检测没有植物的图像,对具有统一背景的图像进行分类…——并拒绝添加背景有助于提高模型鲁棒性的事实。
这个模型仍有改进的空间,但在此之前,我们应该考虑使用一个更接近有问题的的数据集——从任何照片中预测植物病害。****
迭代代码可以在Github上找到。
承认
[Samir Bhattara](https://www.kaggle.com/vipoooool) (2018, Nov.), [New Plant Diseases Dataset](https://www.kaggle.com/vipoooool/new-plant-diseases-dataset) v2
**Orignial Dataset**: [spMohanty](https://github.com/spMohanty) (2018, Sept.), [PlantVillage-Dataset](https://github.com/spMohanty/PlantVillage-Dataset)[Abdallah Ali](https://www.kaggle.com/abdallahalidev) (2019, Sep.), [Plant Village dataset](https://www.kaggle.com/abdallahalidev/plantvillage-dataset) v3[ImageNet](http://image-net.org/) (2016), [Stanford Vision Lab,](http://vision.stanford.edu/) [Stanford University](http://www.stanford.edu/), [Princeton University](http://www.princeton.edu/)
利用 R 中的高效编码提高计算速度
理解大数据
了解代码基准测试,以优化 HolzingerSwineford1939 数据集数百个观察值的数据分析
R 专为智能统计数据分析而构建,与 C 或 Python 等其他编程语言相比,只需要我们编写很少的代码。不过时不时的显得有点慢。随着我们中的一些人处理大型复杂的数据集,计算速度变得非常方便。但是老实说,我们总是以最有效的方式构造代码吗?我不确定。因此,熟悉加速分析的主要技术至关重要——这将使您更容易尽快获得结果,而不必等待几十年直到代码最终执行。这就是我将通过 R 中探索性数据分析的典型工作流向您展示的内容,包括:
- 获得数据概览
- 将数据转换成合适的格式
- 汇总数据以获得一些描述性统计数据
- 绘制感兴趣的方面。
有时,您可能还会将数据分成更小的部分,以获得更多的见解(例如,每个人、每个州、每个性别……)。现在如何才能加快进程,成为更厉害的程序员呢?
在我们开始一个小的案例研究之前,这里有一些通用的建议来加速你的分析:
1。 让你的 R 版本保持最新
确保你定期更新你的 R 版本。新版本的 R 通常包括速度提升和在幕后开发的错误修复。但是从我的个人经验来说,我知道更新有时会很痛苦,因为程序可能无法再从库中找到包。离开 R,进入任何文件夹,然后进入用户>文档> R > win-library,这可能会有所帮助。然后选择包含早期版本的所有包的文件夹(例如,在我的例子中,它被称为 3.4,我想安装 4.0 版本)。现在手动删除文件夹。这确保了没有类似“为…加载包或名称空间失败”的混淆,并且 R 可以毫无困难地找到包。
**2。**避免常见的速度瓶颈
即使这不会使您的代码特别快,您也可以避免一些会使您的代码更慢的常见缺陷。首先,如果没有必要,确保不要存储太多的变量/对象。例如,如果您对 X 的平均值感兴趣,一个常见的习惯是使用’
另一个节省时间的方法是避免重复。我记得在 2018 年的一段时间里,我开始编码,并总是在数据帧的每一行使用相同的程序-我产生了令人尴尬的乏味和混乱的代码:
可以通过使用更整洁的 dplyr 语法来解决这个问题,例如:
由于不知道更好的方法来做到这一点,我最终得到了无尽的代码行,这使得我几乎不可能知道所有的步骤都包括什么。如果您一遍又一遍地重复一个定制的过程,并且没有用于此目的的特定内置函数,您可以编写一个函数来使您的代码更整洁(例如,如果您希望在不同的组上生成一个特定的绘图,而不进行分面,我将在下面向您展示)。最后但同样重要的是,确保尽可能避免使用循环。下面是一个简单的例子:假设你想生成一个固定长度为 n 的整数序列。你可以像这样简单地保持它:
或者你决定运行一个 for 循环,遍历 n 的每个元素来填充向量 X:
这是不必要的,因为您可以很容易地使用第一个版本,但它最多只会减慢您的代码几秒钟。如果你生成了一个空的 X 向量,并且想要一步一步地增长它,那会怎么样呢?
请不要这样做!为什么?在这个 for 循环的每一次迭代中,你需要更多的内存,如果你想创建大的向量,这可能需要几个小时才能完成。
我的经验是:如果有一个选项可以在一行代码中运行相同的操作(例如,使用 base R、data.table 或 dplyr 函数),你应该选择它,因为每个开发团队已经对它进行了大量的速度优化。您可能还听说过程序员建议对您的代码进行“矢量化”——这意味着您使用只被调用一次的内置函数来返回具有固定数量条目的向量(例如,使用 rnorm(n)生成 n 大小的随机正态分布)。相比之下,如果使用一个循环来运行相同的操作,则该函数会被调用多次——对于每次迭代(就像上一个示例中一样)。根据生成的向量应该有多大,使用 for 循环增长向量可能需要几个小时才能执行。最后但同样重要的是,尽可能使用矩阵而不是数据框:如果数据元素只包含一种数据类型(字符、整数、浮点等)。)使用矩阵而不是数据帧会给你带来巨大速度提升,因为它会更有内存效率。但是,如果您需要在矩形中存储不同类型的数据,数据框、tibbles 或 data.tables 是更合适的解决方案。
3。 基准测试你的代码
如果没有参照物与之比较,您就无法真正知道您的代码是“太慢”还是“足够快”——这就是代码剖析发挥作用的地方。它基于以下原则:编写相同代码的不同版本,跟踪执行每个版本所用的时间,然后选择最快的版本。即使只是有一些操作不能再优化了,它也确实有助于找到代码中减慢整个执行速度的瓶颈。如果你时不时地重复这个过程,你会对麻烦制造者有所了解,并知道如何克服他们,给你留下成就感和自我效能感。在 R 中有多种方法可以对您的代码进行基准测试——我将在下一节中向您展示它们。
声明:所有图片均由作者创作,除非另有说明。
加快分析 1939 年以来的情报数据
照片由纽约公共图书馆在 Unsplash 上拍摄
假设你有一台时间机器,回到了 1939 年,你的任务是帮助一组教师对来自两所不同学校(巴斯德和格兰特-怀特)的 301 名七年级和八年级学生进行一系列智力测试,数据集名为 HolzingerSwineford1939,从 lavaan 包中检索。现在你要为每个学生准备有意义的反馈——在 1939 年,这是一项乏味的教师工作!感谢你的数据科学技能,你知道该怎么做:你要准备、总结和可视化结果,最终为每个孩子提供一个漂亮的图表。因为你的时间机器只有有限的时间段用于旅行,所以你需要优化代码执行的速度。这就是我们将要一起做的事情:我们将在追踪时间和记忆的同时,通过不同版本的代码进行游戏。我们将使用 base R 函数,来自 tidyverse 和 data.table 包的函数。为了区分不同的语法风格,我将简要介绍不同的软件包:
基地 R
顾名思义,你不需要安装一个额外的包来使用 base R 包——它已经是内置的,会自动加载。例如,$运算符或 subset 函数的使用是一个基本的 R 函数。
Tidyverse/dplyr
这种风格在 R 社区中变得非常流行,说真的,我认为这是完全合理的——tidy verse编写代码的风格让我的生活变得简单多了,因为我发现了% > %管道操作符及其可读性、一致性和可再现性的无与伦比的美。实际上,tidyverse 包括一系列高度兼容的包,如 dplyr、stringr、magittr 和 purrr。特别是,有一组非常有用的数据操作函数,如 mutate()、filter()和 group_by()。
数据表
data.table 的内存效率非常高,尤其是在数据操作方面,但是它的语法与 tidyverse 的语法有点不同,而且更难学习和阅读。与 tidyverse 不同,它指的是单个包: data.table 。据开发者称,它允许:
大型数据的快速聚合(例如 RAM 中的 100GB)、快速有序连接、完全不使用副本按组快速添加/修改/删除列[…],为更快的开发提供了自然而灵活的语法
它甚至支持低级并行:许多常见操作在内部并行化,以使用多个 CPU 线程。因此,即使在处理大型数据集和复杂的数据操作时,它的超级能力也是速度。这是否意味着在数据操作管道的每一步,data.table 函数真的比 tidyverse 或 base R 函数更节省时间?我们会看到——让我们证明这一点。
首先,我们将设置我们的工作空间,并从 lavaan 包中加载holzingerswinford 1939数据集。
正如你所看到的,数据集包含 301 个观察值和 15 列,包括学生的 ID、性别、年龄、学校、年级和 9 项智力任务中每一项的分数。为了确保每一列都有正确的格式,我们将使用两个不同的函数来获取数据的结构。出于这个目的,我们将把它包装到 system.time()中,以跟踪计算机执行它所花费的时间。
在输出中,你可以看到 R 首先执行了我们真正感兴趣的函数,然后才给出计时结果。它给出了几个关键指标:
- 用户时间是执行用户指令收取的 CPU 时间。
- 系统时间是系统代表调用进程强制执行的 CPU 时间。
- Elapsed time(德语:“verstrichen”)是我们通常感兴趣的时间,因为它大约是用户和系统的总和。在我们的例子中,这真的很快,只有 0.07 秒。
如果我们用 str()代替 glimpse()会怎么样?
看起来 base-R 函数运行得更快一些,但是 0.04 秒应该不会有太大的差别。接下来,我们将讨论数据。更具体地说,我们将
- 只选择我们感兴趣的变量,
- 为 9 项智能任务中的每一项赋予更有意义的名称,
- 将数据从宽格式更改为长格式
- 将任务变量更改为对以后的绘图有用的系数。
为此,我们将以 dplyr-以及 data . table-格式编写此过程。为了相互比较各个版本,我们需要将每个版本包装到一个函数中,以便再次使用 system.time()。
dplyr-版本
数据表-版本
好的——似乎 data.table 在将数据转换成正确的格式方面做得更有效,并且比 dplyr 快 3 倍。
对于感兴趣的读者,我发现了一个关于 data.table 和 dplyr 之间差异的有趣的堆栈溢出讨论。
现在,让我们通过使用微基准测试包来进一步扩展我们的时间比较。与 system.time()相比,它的优势在于,它提供了不同函数之间更详细的计时概要,并且每个函数都运行多次迭代,从而为您提供更可靠的结果。在描述中,开发人员认为,通过使用用 C 代码编写的毫秒级(据说是纳秒级)精确计时函数,可以实现更精确的计时。为了利用它,我们将编写不同版本的代码来计算每个任务和学校的平均分数,并将它们包装到一个函数中。稍后,该总结将为我们提供一个参考,以比较每个学生在每项任务上的得分和学校特定的表现。
哇 microbenchmark 函数甚至给你一个函数时间效率的排名,让你更容易直接选择最快的。在这种情况下,data.table 函数最快,其次是 tidyverse 版本,然后是 base R 函数。通过计算相对速度,我们可以看到,与 data.table 函数相比,base R 函数几乎慢了 4 倍,而 dplyr 函数慢了 3 倍!因此,data.table 在这里再次成为明显的速度赢家。
接下来,我们将用一个更复杂的任务来挑战每个包:我们想创建一个显示每个学生任务表现的图表,然后我们想自动将它们导出到预先指定的文件夹中。通过包括学校特定的得分,我们将有一个参考线来找出孩子与他或她的同龄人相比的得分。换句话说——我们想知道在每项智力任务中,他或她与其他人相比,表现是高于还是低于平均水平。
在这个例子中,你可以看到这个人在快速辨别大写字母和完成句子方面表现出高于平均水平的表现。他或她在立方体任务中表现尤为突出,而在点数任务中表现一般,在其余任务中表现中等至中等以下。通过观察彩色圆点,我们可以认为格兰特-怀特学校似乎在口头任务(段落理解、句子完成和词义)上平均略胜一筹,而两所学校在与速度相关的任务上表现出相似的结果(即,加快对大写字母的辨别和加快点数)。但请记住,我们仅参考学校的具体表现——我们没有任何关于实际用于规范结果的参考人群的信息,我们也没有任何关于学校之间学生得分自然变化的线索。
正如我之前所说的,我们希望为 301 名学生中的每一名生成一个图表,并自动将其导出到我们预先指定的文件夹中。为此,我们将使用provis 包分析每个版本的代码,这是一个有用的交互式图形界面,可以帮助您找出计算机在哪里分配了最多的时间和内存。因此,您会发现代码中真正的速度瓶颈。
基地 R 图自动化
为了有一个好的参考,我们将首先创建一个循环,该循环遍历学生的 ID 以创建情节,然后保存它。在此代码的每个版本的结尾,我们将删除 plots 文件夹中的文件,以保持所有内容在试验中保持不变(从而不会覆盖任何现有的 png 文件)。
您可以看到,将 ggplot 对象保存到一个临时文件(以便在下一次迭代中覆盖它)已经消耗了相当多的内存,并分配了总时间的 4%用于代码执行。
更让我担心的是,几乎 96%的时间都花在对每个临时 ggplot 文件运行 ggsave-function 上,同时通过迭代瞳孔 id 向量来生成适当的文件名。至少我们现在有了一个文件夹,里面装满了每个学生的不同文件,甚至不用去碰它:
潮汐图形自动化
tidyverse 风格的图形自动化的优势在于它的可理解性——首先我们按照学生 ID 对数据进行分组,然后通过实用的 do 函数将 ggplot 对象与学生 ID 一起保存在数据框中。do 函数采用之前与 dplyr 相关的操作,执行特定函数(本例中为 ggplot ),并将输出存储在数据帧中。
正如你所看到的,tidyverse 版本比 for-loop 快一点,但并不令人印象深刻(188150 ms vs. 218730 ms)。循环速度慢了 1.16 倍,而 tidyverse 版本的代码速度提高了约 1.5 倍。14%.
如果将光标移到图形的下部,您可以看到在后台执行的功能的详细历史记录:例如,似乎每个 ggplot 在导出之前都是先打印的—尽管我们没有注意到。此外,如果我们单击“数据”而不是火焰图,我们可以很好地了解占用时间和内存最多的函数——在我们之前的分析之后,ggsave 同样减慢了这个过程,这不再让我感到惊讶。但我发现值得注意的是,分组过程分配了如此大量的内存,因为我们将数据帧分成了大量的组(每个孩子一个组)。
这里有一个小奖励:我们将尝试使用 purrr 包中的一项技术来生成图形,看看它是否比 dplyr 版本更有效。其思想是按 ID 分割数据框-每个子集将是一个存储在大型列表中的数据框。然后我们将 ggplot 函数映射到这个列表的每个元素。这里,pwalk()开始发挥作用,它是一个映射函数的变体,允许您在一个列表中提供任意数量的参数,在这种情况下,它帮助我们遍历列表的每个元素(每个学生一个 data.frame),应用一个函数。f (ggplot 函数)并相应地导出它们。
尽管这个语法对我来说非常漂亮和优雅,但是代码执行将大部分时间分配给了实际的绘图,甚至比 dplyr 版本的代码还多。现在问题仍然存在 data.table 在自动生成绘图方面做得更好吗?
Data.table 图形自动化
作为第一步,我们创建一个生成并保存 ggplot 的函数。然后我们遍历 data.table,将这个函数应用于每个子集(按 ID 分组)。
啊?与 dplyr 版本相比,data.table 生成图形的速度并不快,甚至还慢了一点。我猜想这是因为我们违反了编写高效 R 代码的几条规则:我们在每次调用函数时都将一个 ggplot 对象赋给了一个临时变量(p ),并且在每次数据表通过 ID 进行子集化时都调用具有绘图生成和保存功能的函数。但是在幕后到底发生了什么呢?请记住,data.table 类似于 SQL 中的查询,具有以下基本语法:
DT[i, j, by]*## R: i j by**## SQL: where | order by select | update group by*
根据数据表文档。by 是一个列表,包含 BY 中每一项的长度为 1 的向量(在我们的例子中是学生 id)。by 变量也可以通过名称直接用于 j;例如,如果 j 是一个绘图命令(就像我们的例子中一样),这对于图形的标题很有用。。SD 可以理解为代表数据的子集、自同或自引用。特别是,这也意味着。SD 本身是一个数据表。因此,这意味着我们在 301 个新生成的数据表中的每一个上使用我们定制的函数,这些数据表由学生 ID 子集化。我觉得你可以用 data.table 做类似的事情,但这不是它的设计初衷。
我们的关键要点是什么?
我们还了解到…
- 运行分组操作和绘图功能(如在 do-call 中)会花费很多时间。
- 我们应该避免在临时对象中保存不必要的变量(比如 ggplots)。
- 与映射和应用函数相比,循环并不总是最坏的选择,因为它们实际上是基于相同的原理。
- data.table 语法是数据操作目的的支持者,但不能应用于任何数据分析步骤。
- ggsave 占用了大部分时间,但是我们可以试着将预先指定的图像分辨率调小一点,使它更有内存效率。
在这里,我尝试结合所有世界的精华:
现在,由于 ggsave 函数分配了大量时间来导出高分辨率图像,我们将稍微调低分辨率。
…还有 tada!与我们的 purrr 版本的代码相比,我们能够减少 22%的计算时间成本,并找到了在大量个体上自动生成图形的最快版本。代码基准测试得到了回报。
奖励:我可以使用多核进行计算吗?
如果您熟悉 Python,您可能以前听说过并行计算。理论上,你可以通过将任务的不同部分分配给计算机上的每个 CPU(中央处理器)来提高计算速度。CPU 是计算机的大脑,负责幕后发生的计算。但是,R 只用其中一个。您可以通过运行以下代码来确定您的机器有多少个 CPU:
我的电脑上似乎有 4 个内核。我能在我的每段代码中使用这些吗?可惜我不能。并不是每个计算都可以并行运行——例如,如果每一行代码都是基于前一行的,就像我们的图形自动化例子一样。但是,如果您可以向前和向后阅读代码,它仍然会给您预期的结果,它可能是并行计算的一个很好的候选。这通常适用于算术运算,如随机抽样。如果这听起来像是你感兴趣的任务,R 中的并行包可能适合你。网上有一个很好的教程向你展示它是如何工作的。
那又怎样?
如果你花时间考虑时间,你可以学习写更多的内存高效的代码——R 中有各种各样的工具可以帮助你提高自己。特别是,如果您处理更大的数据集和更复杂的数据科学任务,这将非常有意义,因为您将更快地获得见解,并避免在未来某个时间点在时间压力下工作时遇到麻烦。另一个好处是:它会让你的代码更干净,增强可读性——如果你在一个团队中从事数据科学项目,你的同事肯定会从中受益。去开始分析你的分析吧!
参考文献
[1] Y. Rosseel,T.D. Jorgensen,N. Rockwood,D. Oberski,J. Byrnes,L. Vanbrabant,…和 h . Duholzingerswinford 1939(2021)lavaan R 包
[2]全球研发核心团队和贡献者(2021 年),基础研发包
[3] H. Wickham (2017), The tidyverse ,R package ver,1(1),1 .
[4] M. Dowle,A. Srinivasan,J. Gorecki,m .基里科,P. Stetsenko,T. Short,…和 X. Tan (2019),包’数据。表’'数据的扩展。框架。
[5] O. Mersmann & S. Krey (2011),微基准:一个精确基准化 R 表达式的包,在 R 用户大会上,用户!2011 年 8 月 16 日至 18 日,英国考文垂华威大学(第 142 页)
[5] W. Chang,J. Luraschi & T. Mastny (2019),profivis:用于分析 R 代码的交互式可视化,R 包版本 0.3。7.
[6]全球 R 核心团队和贡献者(2020),平行 R 包