TowardsDataScience-博客中文翻译-2019-十五-
TowardsDataScience 博客中文翻译 2019(十五)
CNN 热图:显著性/反向传播

在这篇文章中,我将描述 CNN 可视化技术,通常称为“显著性映射”,有时也称为“反向传播”(不要与用于训练 CNN 的反向传播混淆。)显著性图帮助我们理解 CNN 在分类过程中在看什么。关于为什么这很有用的总结,请看这篇文章。
显著图示例
下图显示了三幅图像——一条蛇、一只狗和一只蜘蛛——以及每幅图像的显著性图。显著图以彩色和灰度显示。这个数字来自utkuozbulak/py torch-CNN-visualizations:

上面,“彩色香草反向传播”是指用 RGB 颜色通道创建的显著图。“香草反向传播显著性”是将“彩色香草反向传播”图像转换成灰度图像的结果。你可以在py torch-CNN-visualizations/src/vanilla _ back prop . py看到用来创建这些显著图的代码。
定义
谷歌将“显著”定义为“最显著或最重要”“显著图”的概念不限于神经网络。显著图是图像的任何可视化,其中最显著/最重要的像素被突出显示。有传统的计算机视觉显著性检测算法(如 OpenCV 显著性 API & 教程)。然而,这篇文章的重点将是从训练过的 CNN 创建的显著图。
论文参考
如果你想阅读为 CNN 引入显著性图的论文,请参见 Simonyan K,Vedaldi A,Zisserman A,《卷积网络深处:可视化图像分类模型和显著性图》。arXiv 预印本 arXiv:1312.6034。2013 年 12 月 20 日。 被 1479引用
显著图的讨论从第 3 页第 3 节“特定图像类别显著可视化”开始(本文还包括一种为每个类创建“规范”映像的单独技术。)
回顾反向传播算法
反向传播算法允许神经网络学习。基于训练示例,反向传播算法确定神经网络中的每个权重增加或减少多少,以便减少损失(即,为了使神经网络“更少出错”)。)它通过求导来做到这一点。经过足够的神经网络迭代,当它看到许多训练示例时变得“错误更少”,网络已经变得“基本正确”,即对于解决你要求它解决的问题是有用的。关于反向传播算法的清晰解释,见这篇文章。
显著图有时被称为“反向传播”,因为它们是使用反向传播计算的导数的可视化。然而,显著图不用于训练网络。显著图是在网络完成训练之后计算的。
显著图的用途
显著图方法的目的是向已经训练好的分类 CNN 查询特定图像中特定类别的空间支持度,,即“在没有任何显式位置标签的情况下,找出猫在猫照片中的位置。”
简单线性例子
Simonyan 等人从一个简单的线性例子开始,这个例子激发了显著图。首先,一些符号:

使用这种符号,我们可以将显著性图的目的重新表述如下:

最后,这里是 Simonyan 等人的简单线性示例的总结,其中线性分类模型的每个权重表示相应图像像素在分类任务中的重要性:

本质上,具有较大幅度的权重对最终分类分数具有更大的影响,并且如果模型表现合理,则我们期望大幅度的重要权重对应于图像的相关部分,例如对应于猫图像中的猫。
CNN 举例
不幸的是,CNN 是一个高度非线性的得分函数,所以上面简单的线性例子并不直接适用。然而,我们仍然可以通过进行以下操作来利用类似的推理:让我们使用图像邻域中的线性函数来近似 CNN 的非线性评分函数。作者将这种线性近似框架为一阶泰勒展开式:

泰勒级数兔子洞

一只巨大的安哥拉兔
根据维基百科,
泰勒级数是将函数表示为无穷多个项的和,这些项是根据函数在单个点的导数的值计算的。[……]在实数或复数处无限可微的实值或复值函数 f(x) 的泰勒级数 a 是幂级数

- 分子的解释:回想一下, f 后面的撇表示导数。 f 是函数本身, f' 是一阶导数, f"' 是三阶导数, f""""' 是九阶导数。
- 分母的解释:回想一下数字后面的感叹号,比如 5!,表示一个阶乘,计算方式为例如 5!= 5 x 4 x 3 x 2 x 1。
- 与 CNN 的连接:在 CNN 的情况下,函数 f() 是训练好的 CNN 本身, a 是感兴趣的特定图像, x 是代表 CNN 输入的变量,即任意图像。
泰勒级数很酷,因为它意味着如果你知道一个函数的值和该函数在某一点的导数,那么你可以使用泰勒级数近似来估计该函数在任何其他点的值。例如,您可能知道函数及其导数在 a = 6 处的值,因此您可以使用泰勒级数近似来估计函数在 x = 10,369 处的值。
在考虑了本文上下文中的泰勒级数之后,我不认为理解显著图的最佳方式是考虑泰勒级数。为了帮助其他人,我把我的想法写在这里:
这篇论文说他们正在使用“一阶泰勒展开”,从技术上讲应该是这个:

即包括 f(a) ,在这种情况下,这将是应用于我们感兴趣的特定图像的 CNN。然而,在显著图的实际实现中,似乎根本没有使用 CNN 的计算输出 f(a) 。相反,只使用一阶导数:

即使在那里,在他们的解释中也只关注了片段f’(a)(x):

其中 a = I_zero,f’(a)为导数

并且 x 是 I.
此外,在实践中,实际上只有导数片用于制作显著图!衍生部分是显著图:

即,显著性图是分数相对于图像变量的导数的视觉表示,在点(特定图像)I_zero 处评估。
思考显著图
为什么我们要计算分数相对于图像的导数?作者解释说,
使用类得分导数计算特定于图像的类显著性的另一种解释是,导数的大小指示哪些像素需要改变最少以影响类得分最多。人们可以预期这些像素对应于图像中的物体位置。
因此,如果我们有一张猫的图像,它在类别“猫”中得分很高,我们想知道猫图像中的哪些像素对于计算类别“猫”的高分很重要我们对类别“猫”的分数相对于我们特定的猫图像进行求导。具有大幅度导数的像素是对类别“猫”的分数具有大影响的像素,因此,除非神经网络“欺骗”并查看图像中的非猫线索来分类“猫”,否则这些重要像素对应于猫的位置。因此,显著图实现了一种弱监督的对象定位形式:我们可以从只在类别标签上训练的模型中获得对象的近似位置。
计算显著图的步骤
(参考:论文第 4 页顶部)
(1)找到分数相对于图像的导数。
(2)对于由 m×n 个像素组成的图像,导数是一个 m×n 矩阵。取导数矩阵每个元素的绝对值。
(3)如果您有一个三通道彩色图像(RGB ),则取每个像素处三个颜色通道导数值的最大值。
(3)绘制绝对值导数矩阵,就像它是一幅图像一样,这就是你的显著图。
这里是用于计算显著图的 Pytorch 代码。它缺少一个显式的“绝对值”步骤,但结果看起来应该有些相似。
下面是 Tensorflow 代码(下面猫图来源);在代码的第 83 行,你可以看到步骤(2)中导数矩阵的绝对值。

你可以在论文的第 5 页和第 6 页看到更多显著性图的可视化(图 2 和图 3)。
结论
显著图可用于突出显示图像中对象的大致位置。显著图是类别分数相对于输入图像的导数。
显著性图是导向显著性/导向反向传播的前身,而导向反向传播又用于导向 Grad-CAM 中。这两种技术都将是未来文章的主题。
关于特色图片
特色形象是一只兔子,。“下兔子洞”这个短语来源于 Lewis Carrol 的儿童读物爱丽丝漫游奇境记 。
原载于 2019 年 6 月 21 日http://glassboxmedicine.com。
CNN & ResNets——一种更自由的理解
这个博客解释了卷积网络背后发生的事情,以及它们是如何工作的。我将使用 fastai 和 PyTorch 库。如果你想更广泛地了解 fastai cnn 的实现,可以参考文章 这里 。让我们开始吧。

Photo by Sven Read on Unsplash
❓如何创建一个卷积神经网络
如果你熟悉 fastai,特别是 fastai 中的计算机视觉,那么你就知道我们是如何创建卷积神经网络的。我们用来创建网络。如果想了解create_cnn背后的语义,可以参考介绍中分享的链接。
现在,我们有了 CNN,我们想知道 CNN 正在发生什么。幕后发生的事情如下:

Basic neural network
现在,在卷积神经网络中,卷积发生,而不是矩阵乘法。卷积也是一种矩阵乘法,但它也有一些其他的磁性。让我们深入了解卷积神经网络。
- 卷积神经网络由内核组成。内核是另一个任意大小的矩阵,如 2X2、3X3、4X4 或 1X1。这个矩阵有一些数字,它们基本上定义了一个特定的特征。
- 我的意思是,第一层中的核可以滤除输入矩阵(表示图像的像素矩阵)中的顶部边缘,第二层中的核可以滤除左角,第三层中的核可以滤除对角线图案,等等。
- 现在,当输入矩阵与内核相乘时,输出被称为
Channel。现在,我们想要多少层就有多少层。ResNet34 有 34 层以上操作。 - 因此,大体上,我们可以说,我们有许多层矩阵乘法,在每一层矩阵乘法中,我们将一个内核与输入像素矩阵相乘,在输出中得到一个通道。
用新手的语言,想出任何一个图像,把手电筒的光照在图像上。让光线从左上到右下穿过图像的小部分。图像的一小部分变亮的部分实际上是核心,火炬光在整个图像上运行的过程是一种卷积。
让我们用图解法来理解上面的操作。

Convolution()

Output of convolution
形成的方程式数量如下:

因此,输入图像被简化为一个更小的矩阵,称为表示某个特征的通道。
现在,我们可能以如下的传统神经网络方式理解它:

我们可以用传统的神经网络方式进行运算,但这需要大量的内存和时间。相反,我们用上面提到的另一种方式来执行它,这样会花费更少的时间和内存。
现在,让我们考虑卷积神经网络的另一种情况。如果我们的输入矩阵和内核大小相同会怎样?处理这种情况有两种选择:
- 我们可以对完整的输入矩阵进行卷积,得到秩为 1 的张量。
- 否则,我们在输入矩阵周围添加零填充或反射填充,然后卷积输入矩阵,如下所述。Fastai 尽可能频繁地使用反射填充。

换句话说,卷积就是一个矩阵乘法,其中会发生两件事:
- 一些条目一直被设置为零
- 相同的核权重被相乘以计算不同的通道
所以当你有多个重量相同的东西时,这叫做重量捆绑。
这就是对卷积神经网络的大部分理论理解。现在,让我们从实用的角度来理解卷积神经网络。
- 实际上,我们有 3D 输入图像,而不是 2D 图像。每个图像具有不同的红色、绿色和蓝色像素。因此,我们没有秩 2 张量核,而是秩 3 张量核,代表红色、绿色和蓝色的不同值。因此,我们不是对 9 个事物进行逐元素的乘法运算(如果 2D 核有 9 个值),而是对 27 个事物进行逐元素的乘法运算(3 乘 3 乘 3),然后我们仍然要将它们相加为一个数字。
- 现在,当我们卷积图像时,我们不仅要找到顶部边缘,还要检测图像中的重复、颜色梯度等。为了涵盖所有不同的特性,我们需要越来越多的内核,这就是实际发生的情况。在每一层中,我们使用许多内核来处理图像。因此,每一层都由大量的通道组成。
- 为了避免我们的内存由于大量通道而失控,我们不时地创建一个卷积,其中我们不跨越每一个 3x3 的集合(考虑到内核的大小),而是一次跳过两个。我们将从一个以(2,2)为中心的 3x3 开始,然后跳到(2,4),(2,6),(2,8),等等。那叫一步两回旋。它的作用是,看起来是一样的,它仍然只是一堆内核,但我们只是一次跳过两个。我们跳过每隔一个输入像素。所以它的输出是 H/2 乘以 W/2。(我们可以定义步长-n 卷积
让我们看看步幅-4 卷积。

stride-4 convolution as we are shifting by 4 places.
现在,让我们评估 MNIST 数据集,并使用我们的卷积神经网络。我使用 google colab 是出于实际目的。
from fastai.vision import *
Fastai 提供学术数据集,我们可以解开并使用它。
path = untar_data(URLs.MNIST)
path.ls()

It consists of training and validation data
提取数据后,我们必须创建数据串。让我们来证明这一点。
你首先说的是你有什么样的物品清单。所以,在这种情况下,它是图像列表。那你从哪里得到的文件名列表?在这种情况下,我们有文件夹。
imagelist = ImageList.from_folder(path); imagelist

It has a total of 7000 images. Each image has three channels and is 28*28.
所以在一个条目列表里面是一个items属性,而items属性是你赋予它的那种东西。它将用于创建您的项目。在这种情况下,你给它的是一个文件名列表。这是它从文件夹里得到的。
imagelist.items

It consists of a list of images from training and testing folder
当你显示图像时,它通常以 RGB 显示。在这种情况下,我们希望使用二进制颜色图。
defaults.cmap='binary'
imagelist[22].show()

一旦你有了一个图片列表,你就可以把它分成训练和验证两部分。你几乎总是想要被认可。如果没有,可以使用.no_split方法创建一个空的验证集。不能完全跳过。所有这些都在 fastai 数据块 API 中定义。
splitData = imagelist.split_by_folder(train='training', valid='testing'); splitData

60000 items in training dataset and 10000 items in the validation dataset
顺序总是这样。首先,创建你的物品清单,然后决定如何分割。在这种情况下,我们将基于文件夹进行操作。MNIST 的验证文件夹叫做 *testing* ,因此我们在方法中也提到了它。
现在,我们想要标记我们的数据,并且我们想要使用数据所在的文件夹来标记数据。

Number 4 images are present in 4 numbered folder and same for others also.
labelist = splitData.label_from_folder()

The category list is defined for the sample images in the image.
所以首先你要创建一个物品列表,然后分割它,再给它贴上标签。
x,y = labelist.train[0] or labelist.valid[0]x.show()
print(x.shape, y)

现在,添加变换。转换是数据扩充的一部分。我们为表格数据添加的过程和为图像添加的转换之间有很大的区别。
- 在训练数据上添加一次过程,并且对验证和测试数据进行相同的验证
- 每次我们需要一堆图像时,都会应用变换。
由于我们正在进行数字识别,因此我们不想对数据应用默认值,因为它包含一些我们确实不想要的转换,如垂直/水平翻转数字会改变数字,缩放文本会改变图像的像素,图像会变得模糊。因此,我们将添加我们的转换,他们毫不费力,添加随机填充和少量的裁剪。
tfms = ([*rand_pad(padding=3, size=28, mode='zeros')], [])
*(empty array refers to the validaion set transforms)*transformedlist = labelist.transform(tfms)
现在是最后一步的时候了,那就是创建数据束。这里我没有使用图像统计数据进行标准化,因为我没有使用预先训练的模型,如 ResNet34、ResNet56 等。另外,我将使用 128 的批量大小。
bs = 128
data = transformedlist.databunch(bs=bs).normalize()x,y = data.train_ds[0]
x.show()
print(y)

最有趣的是,训练数据集现在有了数据扩充,因为我们添加了转换。plot_multi是一个 fast.ai 函数,它将绘制对每个项目调用某个函数的结果。
def _plot(i,j,ax): data.train_ds[0][0].show(ax, cmap='gray')
plot_multi(_plot, 3, 3, figsize=(7, 7))

Check different padding and cropping in the images
xb,yb = data.one_batch()
xb.shape,yb.shape

Since we selected the batch size of 128. Thus there are 128 images.
data.show_batch(rows=3, figsize=(5,5))

Images with the labels
现在,我们完成了数据束。现在,我们将创建学习者,并通过我们自己的 CNN 对其进行训练。
批量归一化的基本 CNN
def **conv**(ni,nf): return nn.Conv2d(ni, nf, kernel_size=3, stride=2, padding=1)model = nn.Sequential(
conv(3, 8), ***# 14***
nn.BatchNorm2d(8),
nn.ReLU(),conv(8, 16), ***# 7***
nn.BatchNorm2d(16),
nn.ReLU(),conv(16, 32), ***# 4***
nn.BatchNorm2d(32),
nn.ReLU(),conv(32, 16), ***# 2***
nn.BatchNorm2d(16),
nn.ReLU(),conv(16, 10), ***# 1***
nn.BatchNorm2d(10),
Flatten() ***# remove (1,1) grid***
)
让我们来理解一下上面的函数。
- 我们声明内核大小为 3 * 3。
- 我们要执行步长为 2 的卷积。
- 现在,我们想执行顺序操作,这就是为什么我们写了
nn.Sequential。 - 模型的第一层是 conv(3,8)。
3暗示要输入的通道数。因为我们的图像有三个输入通道,所以我们声明了这个数字。见下图。

Images with 3 channels
8是输出的通道总数。正如上一节所讨论的,这个数字意味着过滤器的总数。- 一层输出的通道数输入到下一层。我们已经提到过使用步长 2 卷积。因此,我们从 28 * 28 的图像尺寸开始。在第二层,它将变成 14 * 14,在下一层变成 7 * 7,然后变成 4 * 4,然后变成 2 * 2,最后变成 1 * 1。
- 输出将采用[128,10,1,1]的形式,这一批 128 个图像中的每个图像在输出中有 10 个 1 * 1 的通道,作为秩 3 张量。我们把它拉平,排列成一个张量。
- 在卷积层之间,我们添加了批量标准化和 ReLu 作为非线性层。
这就是全部(͡ᵔ ͜ʖ ͡ᵔ),我们已经创建了我们的卷积神经网络。
现在是按照 fastai 中的定义创建学习者的时候了。
learn = Learner(data, model, loss_func = nn.CrossEntropyLoss(), metrics=accuracy)learn.summary()

[8, 14, 14] — [channels, dimension, dimention]
learn.lr_find(end_lr=100)learn.recorder.plot()

Learning rate plot
learn.fit_one_cycle(10, max_lr=0.1)

We have reached 99% accuracy
现在,让我们了解 ResNet,然后我会将它包括在我们的模型中,看看精度提高了多少。
❓什么是 ResNet
设 X 为输出。根据 ResNet,而不是做喜欢
Y = conv2(conv1(X)),
的确如此,
Y = X + conv2(conv1(X))——这个东西叫做身份连接或者跳过连接。

Basics of ResNet — ResBlock
ResNet 极大地改善了损失函数曲面。没有 ResNet,损失函数有很多凸起,而有了 ResNet,它就变得平滑了。
我们可以像下面这样创建 ResBock:
class ResBlock(nn.Module):
def __init__(self, nf):
super().__init__()
self.conv1 = conv_layer(nf,nf)
self.conv2 = conv_layer(nf,nf)
def forward(self, x): return x + self.conv2(self.conv1(x))
让我们更改我们的模型,以包含 ResNet 块。让我们稍微重构一下。fast.ai 已经有了一个名为conv_layer的东西,可以让你创建 conv、批处理范数、ReLU 组合,而不是一直说 conv、批处理范数、ReLU。
def conv2(ni,nf): return conv_layer(ni,nf,stride=2)model = nn.Sequential(
conv2(1, 8),
res_block(8),
conv2(8, 16),
res_block(16),
conv2(16, 32),
res_block(32),
conv2(32, 16),
res_block(16),
conv2(16, 10),
Flatten()
)learn = Learner(data, model, loss_func = nn.CrossEntropyLoss(), metrics=accuracy)
learn.fit_one_cycle(12, max_lr=0.05)

Accuracy is improved a bit to 99.23%.
仅此而已。我希望你可能已经理解了 CNN 和 ResNets 背后的逻辑。
使用 CNN(卷积神经网络)的情感分析/文本分类

文本分类有很多应用。例如,仇恨言论检测、意图分类和组织新闻文章。本文的重点是情感分析,这是一个文本分类问题。我们将 IMDB 意见分为两类,即正面和负面。
我们使用 Python 和 Jupyter Notebook 来开发我们的系统,我们将使用的库包括 Keras、 Gensim、 Numpy、 Pandas 、 Regex (re)和 NLTK 。我们还将使用 Google News Word2Vec 型号。完整的代码和数据可以从这里下载。
数据探索
首先,我们看一下我们的数据。由于数据文件是制表符分隔文件(tsv ),我们将使用 pandas 来读取它,并传递参数来告诉函数分隔符是制表符,并且在我们的数据文件中没有标题。然后,我们设置数据帧的报头。
import pandas as pd
data = pd.read_csv('imdb_labelled.tsv',
header = None,
delimiter='\t')
data.columns = ['Text', 'Label']
df.head()

然后我们检查数据的形状
data.shape
现在我们看到了阶级分布。我们有 386 个正面和 362 个负面的例子。
data.Label.value_counts()
数据清理
数据清理的第一步是删除标点符号。我们只需使用正则表达式就可以了。删除标点符号后,数据将保存在同一数据框中。
import redef remove_punct(text):
text_nopunct = ''
text_nopunct = re.sub('['+string.punctuation+']', '', text)
return text_nopunctdata['Text_Clean'] = data['Text'].apply(lambda x: remove_punct(x))
在下一步中,我们通过使用 NLTK 的 word_tokenize 来标记注释。如果我们传递一个字符串‘Tokenizing is easy’给 word_tokenize。输出是['标记化','是','简单']
from nltk import word_tokenizetokens = [word_tokenize(sen) for sen in data.Text_Clean]
然后我们用小写字母表示数据。
def lower_token(tokens):
return [w.lower() for w in tokens]
lower_tokens = [lower_token(token) for token in tokens]
在对数据进行小写处理后,使用 NLTK 的停止字从数据中删除停止字。
from nltk.corpus import stopwordsstoplist = stopwords.words('english')def removeStopWords(tokens):
return [word for word in tokens if word not in stoplist]filtered_words = [removeStopWords(sen) for sen in lower_tokens]data['Text_Final'] = [' '.join(sen) for sen in filtered_words]
data['tokens'] = filtered_words
因为我们的问题是二元分类。我们需要给我们的模型传递一个二维输出向量。为此,我们在数据框中添加了两个 one hot 编码列。
pos = []
neg = []
for l in data.Label:
if l == 0:
pos.append(0)
neg.append(1)
elif l == 1:
pos.append(1)
neg.append(0)data['Pos']= pos
data['Neg']= neg
data = data[['Text_Final', 'tokens', 'Label', 'Pos', 'Neg']]
data.head()

将数据分为测试和训练
现在,我们将数据集分为训练集和测试集。我们将使用 90 %的数据进行训练,10 %的数据进行测试。我们使用随机状态,所以每次我们都得到相同的训练和测试数据。
data_train, data_test = train_test_split(data,
test_size=0.10,
random_state=42)
然后构建训练词汇,得到最大训练句子长度和总字数的训练数据。
all_training_words = [word for tokens in data_train["tokens"] for word in tokens]
training_sentence_lengths = [len(tokens) for tokens in data_train["tokens"]]
TRAINING_VOCAB = sorted(list(set(all_training_words)))
print("%s words total, with a vocabulary size of %s" % (len(all_training_words), len(TRAINING_VOCAB)))
print("Max sentence length is %s" % max(training_sentence_lengths))
然后建立测试词汇,得到测试数据中最大的测试句子长度和总字数。
all_test_words = [word for tokens in data_test[“tokens”] for word in tokens]
test_sentence_lengths = [len(tokens) for tokens in data_test[“tokens”]]
TEST_VOCAB = sorted(list(set(all_test_words)))
print(“%s words total, with a vocabulary size of %s” % (len(all_test_words), len(TEST_VOCAB)))
print(“Max sentence length is %s” % max(test_sentence_lengths))
正在加载 Google 新闻 Word2Vec 模型
现在我们将加载 Google News Word2Vec 模型。这一步可能需要一些时间。如果您有足够的数据量,您可以使用任何其他预先训练的单词嵌入或训练您自己的单词嵌入。
word2vec_path = 'GoogleNews-vectors-negative300.bin.gz'
word2vec = models.KeyedVectors.load_word2vec_format(word2vec_path, binary=True)
标记化和填充序列
每个单词被赋予一个整数,这个整数被放在一个列表中。因为所有的训练句子必须具有相同的输入形状,所以我们填充句子。
例如,如果我们有一个句子“文本如何排序和填充”。每个单词都有一个编号。我们假设 how = 1,text = 2,to = 3,sequence =4,and = 5,padding = 6,works = 7。调用 texts_to_sequences 后,我们的句子看起来会像[1,2,3,4,5,6,7 ]。现在我们假设我们的最大序列长度= 10。填充后,我们的句子将看起来像[0,0,0,1,2,3,4,5,6,7 ]
我们对测试数据也做同样的事情。欲了解完整代码,请访问。
tokenizer = Tokenizer(num_words=len(TRAINING_VOCAB), lower=True, char_level=False)
tokenizer.fit_on_texts(data_train[“Text_Final”].tolist())
training_sequences = tokenizer.texts_to_sequences(data_train[“Text_Final”].tolist())train_word_index = tokenizer.word_index
print(‘Found %s unique tokens.’ % len(train_word_index))train_cnn_data = pad_sequences(training_sequences,
maxlen=MAX_SEQUENCE_LENGTH)
现在,我们将从 Google News Word2Vec 模型中获取嵌入内容,并根据我们分配给每个单词的序列号保存它们。如果我们不能得到嵌入,我们为这个词保存一个随机向量。
train_embedding_weights = np.zeros((len(train_word_index)+1,
EMBEDDING_DIM))for word,index in train_word_index.items():
train_embedding_weights[index,:] = word2vec[word] if word in word2vec else np.random.rand(EMBEDDING_DIM)print(train_embedding_weights.shape)
定义 CNN
文本作为一个序列被传递给 CNN。嵌入矩阵被传递给嵌入层。五种不同的过滤器大小应用于每个评论,GlobalMaxPooling1D 层应用于每个层。所有的输出然后被连接。然后施加脱落层、致密层、脱落层和最终致密层。
model.summary()将打印所有图层的简要摘要以及输出的形状。
def ConvNet(embeddings, max_sequence_length, num_words, embedding_dim, labels_index):
embedding_layer = Embedding(num_words,
embedding_dim,
weights=[embeddings],
input_length=max_sequence_length,
trainable=False)
sequence_input = Input(shape=(max_sequence_length,), dtype='int32')
embedded_sequences = embedding_layer(sequence_input) convs = []
filter_sizes = [2,3,4,5,6] for filter_size in filter_sizes:
l_conv = Conv1D(filters=200,
kernel_size=filter_size,
activation='relu')(embedded_sequences)
l_pool = GlobalMaxPooling1D()(l_conv)
convs.append(l_pool) l_merge = concatenate(convs, axis=1) x = Dropout(0.1)(l_merge)
x = Dense(128, activation='relu')(x)
x = Dropout(0.2)(x)
preds = Dense(labels_index, activation='sigmoid')(x) model = Model(sequence_input, preds)
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['acc'])
model.summary()
return model
现在我们将执行该函数。
model = ConvNet(train_embedding_weights,
MAX_SEQUENCE_LENGTH,
len(train_word_index)+1,
EMBEDDING_DIM,
len(list(label_names)))

训练 CNN
历元数是您的模型将循环和学习的数量,批量大小是您的模型在单个时间看到的数据量。因为我们只在几个时期内对小数据集进行训练,所以模型会过度拟合。
num_epochs = 3
batch_size = 32
hist = model.fit(x_train,
y_tr,
epochs=num_epochs,
validation_split=0.1,
shuffle=True,
batch_size=batch_size)
测试模型
哇!仅用三次迭代和一个小数据集,我们就能获得 84 %的准确率。
predictions = model.predict(test_cnn_data,
batch_size=1024,
verbose=1)
labels = [1, 0]
prediction_labels=[]
for p in predictions:
prediction_labels.append(labels[np.argmax(p)])sum(data_test.Label==prediction_labels)/len(prediction_labels)
CNN 情感分析
使用卷积神经网络分析 IMDb 数据集中的情感
卷积神经网络(CNN)构成了多个现代计算机视觉系统的主干。图像分类、目标检测、语义分割——所有这些任务都可以由 CNN 成功完成。乍一看,对自然语言处理这样不同的任务使用相同的技术似乎是违反直觉的。这篇文章是我试图用著名的 IMDb 数据集来解释这种方法背后的直觉。

Source: https://www.analyticsvidhya.com/blog/2018/07/hands-on-sentiment-analysis-dataset-python/
读完这篇文章后,你会:
- 了解如何使用 torchtext 预处理文本
- 理解卷积背后的思想
- 了解如何将文本表示为图像
- 在 PyTorch 中构建一个基本的 CNN 情感分析模型
我们开始吧!
数据
用于二元情感分类的 IMDb 数据集包含一组 25,000 条用于训练的高度极性电影评论和 25,000 条用于测试的高度极性电影评论。幸运的是,它是 torchtext 的一部分,所以在 PyTorch 中加载和预处理它很简单:
# Create an instance that turns text into tensors
TEXT = data.Field(tokenize = 'spacy', batch_first = True)
LABEL = data.LabelField(dtype = torch.float)# Load data from torchtext
train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)
train_data, valid_data = train_data.split()# Select only the most important 30000 words
MAX_VOCAB_SIZE = 30_000# Build vocabulary
TEXT.build_vocab(train_data,
max_size = MAX_VOCAB_SIZE,
# Load pretrained embeddings
vectors = "glove.6B.100d",
unk_init = torch.Tensor.normal_)LABEL.build_vocab(train_data)
data.Field类定义了一个数据类型以及将它转换成张量的指令。在这种情况下,我们使用 SpaCy tokenizer 将文本分割成单独的标记(单词)。之后,我们构建一个词汇表,这样我们就可以在以后将令牌转换成整数。词汇表是用训练数据集中的所有单词构建的。此外,我们加载预训练的手套嵌入,这样我们就不需要从头开始训练我们自己的单词向量。如果你想知道什么是单词嵌入,它们是一种单词表示形式,是人类理解语言和机器理解语言之间的桥梁。要了解更多信息,请阅读本文。由于我们将批量训练我们的模型,我们还将创建数据迭代器,一次输出特定数量的样本:
# Create PyTorch iterators to use in training
train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
(train_data, valid_data, test_data),
batch_size = BATCH_SIZE,
device = device)
BucketIterator 是 torchtext 中的一个模块,它经过专门优化,可以在为每个新时段生成新的混洗批次时,最大限度地减少所需的填充量。现在我们已经完成了文本预处理,所以是时候学习更多关于 CNN 的知识了。
回旋
卷积是应用于矩阵的滑动窗口函数,其实现特定的结果(例如,图像模糊、边缘检测。)滑动窗口被称为内核、T2 滤波器、或特征检测器。可视化显示了六个 3×3 核,它们将值与原始矩阵逐元素相乘,然后求和。为了获得完整的卷积,我们通过在整个矩阵上滑动滤波器来对每个元素进行卷积:
CNN 只是几层带有激活函数的卷积,就像 ReLU 一样,使得对非线性关系建模成为可能。通过应用这组点积,我们可以从图像中提取相关信息,从较浅层次的边缘开始,到在较深层次的神经网络上识别整个对象。与简单平坦化输入的传统神经网络不同,CNN 可以提取对图像数据特别有用的空间关系。但是文字呢?
用于 NLP 的 CNN
还记得我们上面讨论的单词嵌入吗?这就是他们发挥作用的地方。图像只是空间中的一些点,就像单词向量一样。通过用特定长度的数字向量表示每个单词,并将一堆单词堆叠在一起,我们得到了一个“图像”计算机视觉过滤器通常具有相同的宽度和高度,并在图像的局部滑动。在 NLP 中,我们通常使用滑过单词嵌入(矩阵行)的过滤器。因此,过滤器的宽度通常与单词嵌入的长度相同。高度变化,但一般从 1 到 5,对应不同的 n-gram。N-grams 只是一堆后续词。通过分析序列,我们可以更好地理解句子的意思。例如,单词“like”单独与双字组“don't like”相比具有相反的意思;后者让我们更好地理解真正的意义。在某种程度上,通过分析 n 元语法,我们正在捕捉文本中的空间关系,这使得模型更容易理解情感。下面的图像总结了我们刚刚谈到的概念:

Source: Lopez et al. (2017) Link: https://arxiv.org/pdf/1703.03091.pdf
PyTorch 模型
现在让我们建立一个二元 CNN 分类器。我们将我们的模型建立在内置 PyTorch nn 的基础上。模块:
class CNN_Text(nn.Module):
''' Define network architecture and forward path. '''
def __init__(self, vocab_size,
vector_size, n_filters,
filter_sizes, output_dim,
dropout, pad_idx):
super().__init__()
# Create word embeddings from the input words
self.embedding = nn.Embedding(vocab_size, vector_size,
padding_idx = pad_idx)
# Specify convolutions with filters of different sizes (fs)
self.convs = nn.ModuleList([nn.Conv2d(in_channels = 1,
out_channels = n_filters,
kernel_size = (fs, vector_size))
for fs in filter_sizes])
# Add a fully connected layer for final predicitons
self.linear = nn.Linear(len(filter_sizes) \
* n_filters, output_dim)
# Drop some of the nodes to increase robustness in training
self.dropout = nn.Dropout(dropout)
def forward(self, text):
'''Forward path of the network.'''
# Get word embeddings and formt them for convolutions
embedded = self.embedding(text).unsqueeze(1)
# Perform convolutions and apply activation functions
conved = [F.relu(conv(embedded)).squeeze(3)
for conv in self.convs]
# Pooling layer to reduce dimensionality
pooled = [F.max_pool1d(conv, conv.shape[2]).squeeze(2)
for conv in conved]
# Dropout layer
cat = self.dropout(torch.cat(pooled, dim = 1))
return self.linear(cat)
在init函数中,我们指定了不同的层类型:嵌入、卷积、下降和线性。所有这些层都集成到 PyTorch 中,非常易于使用。唯一棘手的部分是计算正确的维数。在线性图层的情况下,它将等于您使用的过滤器的数量(我使用 100,但您可以选择任何其他数字)乘以不同过滤器大小的数量(在我的情况下为 5。)我们可以把这个线性层的权重看作是从 500 个 n 元语法中的每一个“加权证据”。forward函数指定这些层的应用顺序。请注意,我们也使用最大池层。max-pooling 背后的思想是,最大值是用于确定评论情绪的“最重要”特征,这对应于通过反向传播识别“最重要”的 n 元语法。最大池对于减少网络中的参数和计算的数量也是有用的。
指定网络架构后,让我们加载之前导入的预训练手套嵌入:
# Initialize weights with pre-trained embeddings
model.embedding.weight.data.copy_(TEXT.vocab.vectors)# Zero the initial weights of the UNKnown and padding tokens.
UNK_IDX = TEXT.vocab.stoi[TEXT.unk_token]# The string token used as padding. Default: “<pad>”.
PAD_IDX = TEXT.vocab.stoi[TEXT.pad_token]model.embedding.weight.data[UNK_IDX] = torch.zeros(EMBEDDING_DIM)
model.embedding.weight.data[PAD_IDX] = torch.zeros(EMBEDDING_DIM)
model = model.to(device)
该代码块的第二部分将未知向量(词汇表中不存在的向量)和填充向量(在输入大小小于最大过滤器的高度的情况下使用)设置为零。我们现在准备训练和评估我们的模型。
您可以在本笔记本中找到完整的培训和评估代码:
Link: https://gist.github.com/ritakurban/c9ebcbfa0be45952c99ccd199b57af3d
在训练模型之前,我们需要指定网络优化器和损失函数。Adam 和二元交叉熵是分类问题的常用选择。为了训练我们的模型,我们获得模型预测,使用损失函数计算它们的精确度,并在下一次运行之前通过网络反向传播以优化权重。我们在model.train()模式下执行所有这些动作。为了评估模型,不要忘记打开model.eval()模式,以确保我们没有用dropout丢弃一半的节点(虽然在训练阶段提高了健壮性,但在评估期间会有伤害)。我们也不需要在评估阶段计算梯度,这样我们就可以借助torch.no_grad()模式将其关闭。
在对模型进行了几个纪元的训练(使用 GPU 加速)后,我得到了以下损失和精度:

Losses and Accuracies
该图显示了过度拟合的迹象,因为训练损失和精度都在不断提高,而验证损失和精度却越来越差。为了避免使用过度拟合的模型,我们只在验证损失增加的情况下保存模型。在这种情况下,验证损失在第三个时期后最高。在训练循环中,这一部分如下所示:
if valid_loss < best_valid_loss:
best_valid_loss = valid_loss
torch.save(model.state_dict(), 'CNN-model.pt')
该模型在之前未见过的测试集上的表现相当不错:85.43%。最后,让我们使用 CNN-model 来预测一些极地评论的情绪。为此,我们需要编写一个函数,将用户输入符号化,并将其转换为张量。之后,我们使用刚刚训练的模型进行预测:
def sentiment(model, sentence, min_len = 5):
'''Predict user-defined review sentiment.'''
model.eval()
tokenized = [tok.text for tok in nlp.tokenizer(sentence)]
if len(tokenized) < min_len:
tokenized += ['<pad>'] * (min_len - len(tokenized))
# Map words to word embeddings
indexed = [TEXT.vocab.stoi[t] for t in tokenized]
tensor = torch.LongTensor(indexed).to(device)
tensor = tensor.unsqueeze(0)
# Get predicitons
prediction = torch.sigmoid(model(tensor))
return prediction.item()
在原始数据集中,我们有分别映射到 0 和 1 的标签“pos”和“negs”。让我们看看我们的模型在正面、负面和中性评论中的表现如何:
reviews = ['This is the best movie I have ever watched!',
'This is an okay movie',
'This was a waste of time! I hated this movie.']
scores = [sentiment(model, review) for review in reviews]
模型预测分别是 0.007,0.493,0.971,相当不错!让我们尝试一些更复杂的例子:
tricky_reviews = ['This is not the best movie I have ever watched!',
'Some would say it is an okay movie, but I found it terrific.',
'This was a waste of time! I did not like this movie.']
scores = [sentiment(model, review) for review in tricky_reviews]
scores
不幸的是,由于该模型已经在极地评论上训练过,它发现很难对棘手的陈述进行分类。例如,第一个棘手的评论得了 0.05 分,这是非常自信的“是”,尽管句子中存在否定。尝试使用不同的 n-gram,看看它们中的一些是否比其他的更重要,也许使用二元和三元模型会比我们使用的不同 n-gram 的组合表现得更好。

Table of reviews and their sentiment scores
结论
在这篇文章中,我们回顾了卷积的概念,并讨论了如何使用它们来处理文本。我们还学习了如何预处理来自 PyTorch 的数据集,并为情感分析建立了一个二元分类模型。尽管被狡猾的例子愚弄了,但模型表现得相当好。我希望你喜欢阅读这篇文章,如果你有任何问题,请随时联系我!
参考
布里茨博士(2015 年)。理解用于 NLP 的卷积神经网络。检索自:http://www . wild ml . com/2015/11/understanding-convolutionary-neural-networks-for-NLP/
洛佩兹,M. M .,&卡利塔,J. (2017)。深度学习在自然语言处理中的应用。 arXiv 预印本 arXiv:1703.03091 。检索自:【https://arxiv.org/pdf/1703.03091.pdf
Trevett,B. (2019)。卷积情感分析。检索自:https://github . com/bentrevett/py torch-opinion-analysis/blob/master/4% 20-% 20 convolatile % 20 opinion % 20 analysis . ipynb
CNN 与用于图像处理的全连接网络
介绍
本文的目的是提供一个理论视角来理解为什么(单层)细胞神经网络在图像处理方面比全连接网络更好。将使用线性代数(矩阵乘法、特征值和/或 PCA)和 sigmoid/tanh 函数的性质,尝试在全连接网络(逻辑回归)和 CNN 之间进行一对一(几乎)比较。最后,为了预测的目的,将检查滤波器大小和滤波图像中保留的信息量之间的权衡。为简单起见,我们假设如下:
- 全连接网络没有隐藏层(逻辑回归)
- 原始图像被标准化为像素值在 0 和 1 之间,或者被缩放为均值= 0,方差= 1
- Sigmoid/tanh 激活用于输入和卷积图像之间,尽管该参数适用于其他非线性激活函数,如 ReLU。避免使用 ReLU,因为如果图像经过缩放(平均值= 0,方差= 1)而不是归一化,它会破坏分析的严谨性
- 通道数=图像深度= 1 对于本文的大部分内容,将简要讨论通道数较高的模型
- 这个问题涉及到分类任务。因此,C > 1
- 除了激活之外没有非线性,也没有不可微性(如池化、除 1 之外的步幅、填充等。)
- 负对数似然损失函数用于训练两个网络
符号和记号
使用的符号有:
- x:二维输入图像的矩阵
- 用于将
原始图像映射到全连接网络中的输出的 W₁、b₁:权重矩阵和偏差项
过滤后的图像映射到 CNN 中的输出 - p:输出概率
- X₁:滤波图像
- x₁:滤波激活图像
关于符号需要注意的两个约定是:
- 尺寸写在{}之间
- 不同的维度由 x 分隔。例如:{n x C}表示二维“数组”
模型定义
全连接网络

FC1: Pre-ouptut layer

FC2: Estimated probability
卷积神经网络

C1: Filtered image

C2: Filtered-activated image

Activation functions

C3: Pre-output layer

C4: Estimated probability
数学
将 CNN 简化为一个全连接网络
假设滤波器是平方的,kₓ = 1,K(a,b) = 1。因此,X₁ = x。现在将使用归一化 x 的优点和 sigmoid/tanh 的便利性质。讨论如下:
sigmoid/tanh 的必需属性

Sigmoid activation as a function of input. Courtesy: ResearchGate article [1]
我们观察到,当输入量很小时,函数是线性的。由于输入图像被归一化或缩放,所有值 x 将位于 0 附近的小区域中,使得|x|

这表明过滤激活图像中的信息量非常接近原始图像中的信息量。过滤激活图像的所有像素都连接到输出层(完全连接)。
让我们假设我们学习了输入层完全连接到输出层的全连接网络的最优权重 W₁、b₁。我们可以直接获得给定 CNN 的权重,如 W₁(CNN) = W₁/k 重排为矩阵,b₁(CNN) = b₁.因此,对于 kₓ = 1 且 K(1,1) = 1 的方形滤波器,全连接网络和 CNN 将表现(几乎)相同。
由于双曲正切函数是一个重新标度的 sigmoid 函数,因此可以认为同样的性质也适用于双曲正切函数。这也可以在下图中观察到:

tanh activation as a function of input. Courtesy: Wolfram MathWorld [2]
滤波器——最差情况
让我们考虑一个正方形图像上的正方形滤波器,其中 kₓ = nₓ,对于所有 a,b,K(a,b) = 1。首先,该滤波器将每个图像映射到一个值(滤波后的图像),然后该值映射到 c 个输出。因此,过滤后的图像包含的信息(信息瓶颈)比输出图层少-任何少于 C 个像素的过滤后图像都将成为瓶颈。第二,该过滤器将每个图像映射到等于图像值总和的单个像素中。这显然包含非常少的关于原始图像的信息。让我们考虑 MNIST 的例子来理解为什么:考虑具有真实标签‘2’和‘5’的图像。这些图像的值的总和不会相差太多,然而网络应该使用该信息学习一个清晰的边界。
放松最坏情况第 1 部分:滤波器权重
让我们考虑正方形图像上的正方形滤波器,其中 kₓ = nₓ,但是 k 中不是所有的值都相等。这允许 k 中的变化,使得重要性给予某些像素或区域(将所有其他权重设置为常数,并且仅改变这些权重)。通过改变 K,我们可以发现图像中有助于分类的区域。例如,在 MNIST,假设所有的数字都按照一个通用的模板居中并且写得很好,即使只有一个值被映射到 C 输出,这也可以在类之间创建合理的分离。考虑这种情况类似于判别分析,其中单个值(判别函数)可以分离两个或多个类。
放松最坏情况第 2 部分:滤波器宽度
让我们考虑正方形图像上的正方形滤波器,对于所有的 a,b,K(a,b) = 1,但是 kₓ ≠ nₓ.例如,让我们考虑 kₓ = nₓ-1.原始和过滤后的图像如下所示:

Original image

Filtered image
请注意,过滤后的图像总和仅在第一行、第一列、最后一行和最后一列中包含一次元素。所有其他元素出现两次。假设滤波图像中的值很小,因为原始图像被归一化或缩放,对于小值 k,激活的滤波图像可以近似为滤波图像的 k 倍。在诸如矩阵乘法(具有权重矩阵)的线性运算下,当 k 为非零时,kx₁的信息量与 x₁的信息量相同(此处为真,因为 sigmoid/tanh 的斜率在原点附近为非零)。因此,过滤激活图像包含(大约)与过滤图像相同数量的信息*(为了便于理解,写得非常松散,因为【费希尔】‘信息’是得分函数的方差,它与 RV 的方差有关。这种说法的更好版本是:“缩放/归一化的输入图像和缩放/归一化的滤波图像将具有大约相同的信息量”)。
假设原始图像具有非冗余像素和非冗余像素排列,通过应用(nₓ-1,nₓ-1)滤波器,图像的列间距从(nₓnₓ)减少到(2,2)。这导致信息丢失,但对于 K(a,b) = 1,它保证比(nₓ,nₓ)滤波器保留更多的信息。随着过滤器宽度的减小,过滤(因此,过滤激活)图像中保留的信息量增加。它在 kₓ = 1 时达到最大值。
在诸如 MNIST 的实际情况下,边缘附近的大多数像素是冗余的。因此,通过在没有数字信息的边缘附近应用大小为~宽度的块的过滤器,几乎可以保留所有信息。
把东西放在一起
CNN 的一个特殊属性是在图像的所有区域应用相同的滤波器。这就是所谓的重量共享。模型中的参数总数= (kₓ * kₓ) + (nₓ-kₓ+1)(nₓ-kₓ+1)C.
- 较大的过滤器导致较小的过滤激活图像,这导致通过全连接层传递到输出层的信息量较少。这导致低信噪比、较高的偏置,但由于全连接层中的参数数量减少,因此降低了过拟合。这是一个高偏差、低方差的例子。
- 较小的过滤器导致较大的过滤激活图像,这导致更大量的信息通过全连接层传递到输出层。这导致高信噪比、较低的偏置,但可能导致过拟合,因为全连接层中的参数数量增加了。这是一个低偏差、高方差的例子。
已知 K(a,b) = 1,kₓ=1 表现(几乎)以及全连通网络。通过反向传播(链式法则)和 SGD 调整 kₓ ≠ 1 的 K(a,b ),保证模型在训练集上表现更好。当用一组不同的超参数(kₓ).)训练时,它也趋向于具有比全连接网络更好的偏差-方差特性
总结
kₓ = 1 且 K(1,1) = 1 的 CNN 可以匹配全连接网络的性能。对于 kₓ = nₓ和 K(a,b) = 1,滤波激活图像的表示能力最小。因此,通过调整超参数 kₓ,我们可以控制滤波激活图像中保留的信息量。此外,通过将 K 调整为不同于 1 的值,我们可以聚焦于图像的不同部分。通过调整超参数 kₓ和学习参数 k,CNN 保证具有更好的偏差-方差特性,其下限性能等于全连接网络的性能。这可以通过具有多个通道来进一步改善。
扩展上述讨论,可以认为,如果 CNN 具有相同数量的具有相同/相似结构(每层中的神经元数量)的隐藏层,则 CNN 将优于全连接网络。
然而,这种比较就像把苹果和橘子相比较。适当的比较是将全连接神经网络与具有单个卷积+全连接层的 CNN 进行比较。将具有 1 个隐藏层的全连接神经网络与具有单个卷积+全连接层的 CNN 进行比较是更公平的。
实践中的 MNIST 数据集:逻辑回归模型学习每个数字的模板。这实现了很好的准确性,但是并不好,因为模板可能不能很好地概括。具有完全连接的网络的 CNN 学习适当的内核,并且过滤的图像较少基于模板。与 CNN 相比,具有 1 个隐藏层的全连接网络显示出较少的基于模板的迹象。
参考
谭:【http://mathworld.wolfram.com/HyperbolicTangent.html】T4
用于目标检测的 COCO 和 Pascal VOC 数据格式
理解计算机视觉的注释数据格式
在本文中,我们将了解两种流行的数据格式:COCO 数据格式和 Pascal VOC 数据格式。这些数据格式用于注释在用于计算机视觉的数据集中发现的对象。我们将特别关注对象检测的注释
计算机视觉中最重要的任务之一是标记数据。有几个工具可供您加载图像,使用每个实例的分段来标记对象。这有助于使用边界框进行精确的对象定位,或者使用多边形进行遮罩。该信息存储在注释文件中。
注释文件可以是 COCO 或 Pascal VOC 数据格式。

COCO 是什么?
COCO 是用于对象检测、分割和字幕数据集的大规模图像与上下文中的公共对象(COCO)。COCO 拥有 80 个对象类别的 150 万个对象实例
COCO 有 5 种注释类型用于
COCO 将注释存储在 JSON 文件中。让我们看看存储边界框注释细节的 JSON 格式。这将有助于使用 COCO 格式创建您自己的数据集。
JSON 注释文件的基本构建块是
- 信息:包含数据集的高级信息。
- 许可证:包含适用于数据集中图像的图像许可证列表。
- 类别:包含类别列表。类别可以属于一个超级类别
- 图像:包含数据集中的所有图像信息,没有边界框或分割信息。图像 id 需要是唯一的
- 注释:数据集中每幅图像的单个对象注释列表

Sample COCO JSON format
我们可以为训练、测试和验证数据集创建单独 JSON 文件。
让我们深入了解每一部分
信息:
提供有关数据集的信息。

template and example for info section of the JSON for COCO
许可证:
我们可以提供数据集中使用的不同图像许可证的列表。

template and example for Licenses section of the JSON for COCO
类别:
每个类别 id 必须是唯一的。一个类别可以属于一个超类别。举个例子,如果我们有数据集来识别花和水果。花卉将是超级类别,玫瑰、百合、郁金香将是我们想要检测的花卉的名称。

template and example for Categories section of the JSON for COCO
图像:
包含数据集中所有图像的列表。图像 id 应该是唯一的。flickr_url、coco_url 和 date_captured 是可选的

template and example for images section of the json for COCO
注释:
包含数据集中每个图像的每个单独对象注释的列表。这是包含用于对象检测的边界框输出或对象分割的部分
如果一幅图像有 4 个我们想要检测的对象,那么我们将有所有 4 个对象的注释。
如果整个数据集由 150 幅图像组成,总共有 200 个对象,那么我们将有 200 个注释。
分割包含分割遮罩的每个对象实例周围多边形顶点的 x 和 y 坐标。
面积是包围盒的面积。它是一个像素值
iscrowd :如果我们有一个单一的对象分割,那么 iscrowd 设置为零。对于图像中出现的对象集合,我们设置 iscrowd=1,在这种情况下使用 RLE。
RLE 是游程编码。当 iscrowd=1 时,我们在分段部分添加属性计数和大小。这是下例中的第二个分段
如果被遮挡,单个对象(iscrowd=0)可能需要多个多边形。
imageid :它是包含我们为其指定注释的对象的图像的 id。imageid 对应于 image 部分中的 imageid
bbox:COCO 中的包围盒是左上的 x 和 y 坐标以及高度和宽度。Pascal VOC 边界框是矩形左上角的 x 和 y 坐标以及右下角的 x 和 y 坐标。
COCO 包围盒:(x-左上,y-左上,宽度,高度 )
Pascal VOC 边界框:(x-左上,y-左上,x-右下,y-右下 )
类别:这是我们之前在类别部分指定的对象类别
id :标注的唯一 id

template and example for annotations section of the JSON for COCO
什么是游程编码(RLE)?
RLE 是一种压缩方法,其工作原理是用重复的次数替换重复的值。
例如,0 11 0111 00 将变成 1 2 1 3 2。
COCO 数据格式为每个对象实例提供分段掩码,如上文分段部分所示。这就产生了效率问题
- 紧凑地存放面罩
- 以有效地执行掩码计算。
我们使用游程编码(RLE)方案来解决这两个问题。
RLE 表示的大小与掩模的边界像素的数量成比例。面积、并集或交集等运算将在 RLE 上高效地计算。
Pascal 可视对象类(VOC)
Pascal VOC 为目标检测提供标准化的图像数据集
COCO 和 Pacal VOC 数据格式之间的差异将有助于快速理解这两种数据格式
- Pascal VOC 是一个 XML 文件,不像 COCO 有一个 JSON 文件。
- 在 Pascal VOC 中,我们为数据集中的每个图像创建一个文件。在 COCO 中,我们每个人都有一个文件,用于整个数据集的训练、测试和验证。
- Pascal VOC 和 COCO 数据格式中的边界框是不同的
COCO 包围盒:(x-左上,y-左上,宽度,高度 )
Pascal VOC 包围盒:(xmin-左上,ymin-左上,xmax-右下,ymax-右下 )

Sample Pascal VOC
Pascal VOC 的一些关键标签解释如下
文件夹:
包含图像的文件夹
文件名:
文件夹中存在的物理文件的名称
尺寸:
包含图像的宽度、高度和深度。如果图像是黑白的,那么深度将是 1。对于彩色图像,深度将是 3
对象:
包含对象详细信息。如果您有多个注释,那么对象标签及其内容会重复。对象标签的组件包括
- 名字
- 姿态
- 缩短了的
- 困难的
- bndbox
名称:
这是我们试图识别的对象的名称
截断的:
指示为对象指定的边界框不符合对象的全图。例如,如果一个对象在图像中部分可见,那么我们将 truncated 设置为 1。如果对象完全可见,则将 truncated 设置为 0
困难:
当对象被认为难以识别时,该对象被标记为困难的。如果对象很难识别,那么我们将困难设置为 1,否则设置为 0
边界框:
指定图像中可见对象范围的轴对齐矩形。
本文应该有助于理解计算机视觉中使用的两种流行数据格式的细节
参考资料:
http://cocodataset.org/#download
编辑描述
cocodataset.org](http://cocodataset.org/#format-data) [## 如何在数据集 json 文件中编写对象分段?问题#111 cocodataset/cocoapi
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/cocodataset/cocoapi/issues/111)
https://pjreddie.com/media/files/VOC2012_doc.pdf
https://arxiv.org/pdf/1405.0312.pdf
波士顿安全饮用水工程规范
理解数据意味着从意想不到的地方学习

我第一次参加波士顿黑客之夜是在去年 12 月。当时我正在参加一个密集的数据科学项目。那周我们有多个实验要做(这是常态),我的几个同学被做任何额外的事情的想法吓了一跳。但是,在看过波士顿项目的一些代码后,我渴望做出贡献,并与其他对使用技术解决公民和社会问题感兴趣的人建立联系。
您可以在波士顿的当前项目页面的代码上看到关于以下项目的附加信息:
- 波士顿信息语音应用:回答波士顿市政服务问题的 Alexa 技能。目前支持提供地址和要求垃圾/回收提货日。
- Community Connect :一个健康资源网络应用,收集关于促进健康生活方式选择的企业和组织的信息。
- MuckRock :归档、跟踪和共享公共记录请求。
- 安全饮用水项目:预测安全饮用水违规行为的探索性数据项目。它使用 EPA 的安全饮用水信息系统和 EnviroFacts API。
- 移民服务地图:一款网络应用,旨在帮助移民服务提供商创建个人资料,更新信息,并快速与客户分享。
- 意外收入意识项目:一种帮助受社会保障意外收入消除计划(WEP)影响的退休人员的工具,该计划可以将某些公务员的社会保障福利减少多达 50%。这个工具将帮助受影响的工人更好地规划退休和自我主张与社会保障管理局。
- 当你步履沉重时,你会在日常生活中捡垃圾……慢跑、徒步旅行或只是在街上散步。Plogalong 帮助您跟踪您的 plogs,与附近的 ploggers 联系,获得徽章,并获得当地折扣。
这是一个艰难的选择,但我最终加入了安全饮用水项目,因为他们专门寻找具有数据科学技能的人。我也觉得和这个问题有关联。
一个关于学校的故事
几年前,我参加了当地一所高中同伴领导小组的第一次会议。在我们等待大家入座的时候,我问最近的饮水机在哪里。几个学生扬起了眉毛。
“为什么?”
"这样我就可以装满我的水瓶了."
“就在大厅那头。但你不想那样。”
“你是认真的吗?”
“是啊,我们不喝这里的水。”
房间里的每个人都点头表示同意。
在会议期间,学生们完成了一项活动,他们在活动中确定了他们所关注的学校和社区中的问题。当我们结束一场热烈的讨论时,我不得不抛出最后一个问题:“没人说过水。你们有谁在乎自己学校不能喝水?”
他们都耸耸肩。这是一种常态,不值一提。
同一周,马萨诸塞州州长查理·贝克宣布拨款 200 万美元帮助公立学校检测饮用水中的铅和铜。
仅仅一年多之后,2017 年 5 月,这篇文章出现在我的 feed 中:
[## 质量测试。学校发现超过一半的饮用水中含有高浓度的铅和铜
波士顿(哥伦比亚广播公司)——上周对马萨诸塞州 1000 多所学校的测试发现,超过一半的学校有饮用水…
boston.cbslocal.com](https://boston.cbslocal.com/2017/05/02/school-lead-testing-drinking-water-massachusetts/)
测试的总结结果证实了我合作的两所高中都“领先于行动水平”
理解数据
这让我们回到波士顿的安全饮用水项目。目标是预测饮用水中基于健康的违规行为。我们从环保局的安全饮用水信息系统 (SDWIS)的数据开始。
对我来说,理解数据不仅仅意味着知道 SDWIS 数据库中包含什么信息(尽管我们终于有了一个集中的数据字典!).这也是为了理解这些信息实际上告诉我们什么,并确定数据库中没有包括的其他有助于水安全的因素。
惊人的联系
虽然我一直在积极寻找信息,以加深对安全饮用水相关问题的理解,但我的一些知识却来自意想不到的地方。
弗林特的艾
1 月初,这篇文章出现在我的收件箱中——不是因为我对水资源问题的跟踪,而是因为我对机器学习的兴趣:
一个机器学习模型显示了有希望的结果,但是城市官员和他们的工程承包商放弃了它…
www.theatlantic.com](https://www.theatlantic.com/technology/archive/2019/01/how-machine-learning-found-flints-lead-pipes/578692/)
TL;这篇文章的博士版本是,一个团队创建了一个机器学习模型,成功预测了弗林特的哪些家庭可能有铅管。该市与一家国有公司 AECOM 签订了合同,以加快工程进度。然后:
AECOM 抛弃了指导挖掘的机器学习模型的预测。面对一些居民的政治压力,[弗林特市长卡伦]韦弗要求该公司挖掘整个城市的选区和选定街区的每一所房屋,而不是挑选出可能有铅的房屋,因为年龄、财产类型或其他特征可能与管道相关。
在对项目管理进行了数百万美元的投资后,弗林特成千上万的人仍然拥有铅管住宅,而以前的项目可能已经找到并替换了它们。
海平面正在上升
弗林特的文章发表后的一周,我在麻省理工学院媒体实验室参加了“黑人生活数据 II”会议。
从那以后,最让我念念不忘的是“海在上涨,人也在上涨”:数据、灾难&集体力量。
小组成员 Valencia Gunder、Denice Ross、Lisa Rice 和 Bina Venkataraman 分享了他们的工作并回答了问题:“黑人社区使用社交媒体、数据和技术为下一场灾难做准备的方式是什么?为了预防?在这些快速反应时刻和气候正义运动中,数据科学家和工程师的角色是什么?”
他们谈到气候变化加剧了高房价和流离失所的问题。巴伦西亚·贡德开始了她的故事:
当你在南佛罗里达工作时,气候变化和恢复力总是谈话的一部分,即使是以最不正式的方式。小时候,我的祖父告诉我,“他们会偷走我们的社区,因为那里不发洪水。”直到我长大了,我才理解他。

Map created by Hugh Gladman that plots owners with 15 or more pieces of land in Little Haiti to the height above sea level of their parcels. (Source: The New Tropic)
其他人也谈到洪水和社区不平等之间的联系。“黑人生活的数据”YouTube 频道有一个完整的小组的记录,以及会议的其他环节,可供查看。
农田径流
在完成数据科学项目后不久,我与一个人进行了交谈,他对一个亲戚进行了离题的评论,这个亲戚使用人工智能来研究奶牛的食物如何影响它们的粪便,从而由于径流而影响水系统。他挥挥手表示拒绝:“但你可能对此不感兴趣。”
我解释说我对这个话题很感兴趣。这是一个促成因素,我们在安全饮水小组中还没有讨论过。我做了一些调查,以了解更多关于粪肥和其他农田径流是如何污染水源的。
2009 年《纽约时报》的一篇文章考虑了威斯康星州的问题,在那里“仅一个镇就有超过 30%的水井违反了基本健康标准”:
农场动物的排泄物据说是饮用水中污染物的一个来源。在一次市政厅会议上,愤怒的房主对牛奶场主大喊大叫,其中一些人被认为是镇上最富有和最有权势的人。
它继续解释说,即使当地的环境机构没有被过度征税,“一个强大的农业游说团体已经阻止了以前在 Capital[sicHill 的环境努力。即使州立法机关采取了行动,也经常遇到意想不到的困难。”

Screenshot of EWG’s interactive map of drinking water in rural communities threatened by farm pollution. (Source: EWG)
不同的政治和经济利益在起作用。虽然 Valencia Gunder 谈到了洪水和气候变化导致的佛罗里达州的流离失所,但像上面这样的地图(也使用了 SDWIS 的数据,以及来自美国农业部农业普查的数据)表明了额外的担忧。在已经受到农业污染威胁的地区,洪水的增加增加了水系统受到污染的风险。
有毒化学场所
在本月早些时候哈佛的下一次数据可视化活动中,纽约时报的图形编辑布莱基·米格里奥齐分享了他的一些气候变化可视化。其中一个特别引起了我的注意:一张地图显示了全国 2500 个位于洪水易发区的化学场所。
在美国每个州的洪水易发区,有超过 2500 个处理有毒化学物质的场所,纽约…
www.nytimes.com](https://www.nytimes.com/interactive/2018/02/06/climate/flood-toxic-chemicals.html)
这张地图是利用美国环保署有毒物质释放清单和美国联邦应急管理局国家洪水危险层的数据绘制的。
与农场径流带来的问题类似,海平面上升导致洪水泛滥的地区的化学工厂也面临着污染水系统的更高风险,以及一系列其他问题。
被毒害的城市
几天后,我和另一名安全饮水志愿者去见记者安娜·克拉克谈论她的书被污染的城市:弗林特的水和美国城市悲剧。当然,在这种情况下,我期望了解更多关于影响饮用水的因素,但仍然觉得值得在这里包括。
在简要介绍了该市从五大湖水转向弗林特河水,以及随之而来的铅、大肠杆菌和军团病问题后,克拉克问道,“首先是什么让一个城市如此脆弱?”
她的回答:“你得往回走几代。”
从那里,她的故事开始于 20 世纪 60 年代,“当时弗林特是北方种族隔离最严重的城市,也是全国第三大种族隔离城市。”它包括通用汽车公司的影响、社区组织的历史、争取公平住房的斗争、去工业化和应急管理法。
关于这方面的图文并茂的文章,请看克拉克与平面艺术家乔希·克莱默的合作:
一篇插画的原创文章。关于来源的说明:所有归功于历史人物的东西都直接来自于…
splinternews.com](https://splinternews.com/an-equal-opportunity-lie-how-housing-discrimination-le-1820482045)
对我来说,克拉克在演讲后的谈话中提出的两个评论很突出:
环境问题总是以不承认有罪的和解告终。
和
这不仅仅是一个技术问题。这种事发生在弗林特是有原因的,不会发生在安阿伯。

A copy of The Poisoned City, signed “For #water, Anna Clark.”
约翰,另一个参加讲座的安全饮水志愿者,拿到了一本《被污染的城市》。克拉克把它签到了#water(我们组对波士顿 Slack 频道的代号),它现在正在被读取并通过组传递。
巴黎圣母院
与此同时,这个推特时刻今天早上在我的收件箱里等待着:
[## 白宫对圣母大学的援助重新开启了关于波多黎各和弗林特需求的对话
一些人想起了白宫提供的资源,波多黎各和弗林特继续需要这些资源…
twitter.com](https://twitter.com/i/events/1118477687712935936)
这再次提醒我们不同的利益、价值观、优先事项和资源分配会如何影响饮用水(以及其他许多东西)。
邀请函
这些是我在试图理解我们正在使用的数据时建立的一些联系。一如既往,我很高兴听到对此的任何想法。
如果您对安全饮用水项目的其他数据、研究或方法有建议,请分享!您还可以查看安全饮用水项目 GitHub repo ,并加入# Water channel onCode for Boston Slack。
如果你在波士顿地区,并且对参与这个或另一个波士顿代码项目感兴趣,在波士顿代码网站上了解更多,并参加每周一次的黑客之夜。
感谢阅读!想法、问题和反馈总是很受欢迎。
减少分支、机器人感知和圈复杂度
学习最佳实践的代码形

source: Steve Johnson via pexels
这是一个快速代码形来说明“减少分支”原则,并在 5 分钟内激起你对机器人感应的兴趣。
当我们创建一个机器人代理时,天真的解决方案是首先假设机器人有一致的可能性去“任何地方”在数学上,这被建模为机器人将向任何方向移动的等概率。为了简单起见,让我们将其限制在一维的情况下,假设我们的机器人只能在直线 1-D / X 轴上行进并访问 5 个门。
如果有 5 个门,并且所有概率都是均匀分布的,那么机器人位置的初始向量表示是 1 / 5 = 0.2。
所以,假设给你一个向量,代表你的统一的,简单的概率:
probs = [0.2, 0.2, 0.2, 0.2, 0.2]
现在,机器人感知它们的环境,这应该与它们的目标目的地一致。比方说,我们告诉我们的机器人,他只能旅行到一个“粉红色的门”,而不是蓝色的门。"
如果我们的机器人感觉到他在一扇粉色的门旁,他的传感器会说有 0.90 的概率他确实在一扇粉色的门旁。
因此,让我们给自己一些额外的变量,代表一个成功的“击中”失败的“错过”,以及一个变量,代表我们的环境或门。
doors = [‘pink’, ‘blue’, ‘blue’, ‘blue’, ‘pink’]T = ‘pink’pHit = 0.90pMiss = 0.10
你的目标是为我们的机器人编写一个感知函数,评估我们的目标 T 是否与它所在的门匹配,并根据它的均匀分布乘以它感知目标门的概率。
下面,我用 Python 3 提供了两个解决方案,一个是简单的解决方案,另一个抓住了上面的“代码形”本质。一个是天真的,另一个可以说是稍微好一点的。
天真的实现
def sense(probs, T): afterSense=[] for i in range(len(probs)): if doors[i] == T: afterSense.append(probs[i]*pHit) else: afterSense.append(probs[i]*pMiss) return afterSense
更好的实现
def sense(probs, T): afterSense = [] for i in range(len(probs)): hit = (T == doors[i]) afterSense.append(probs[i]*(hit*pHit + (1-hit) *pMiss)) return afterSense
在更好的实现中,您将自己从执行庞大的 4 行 if/then 评估中解放出来。相反,您可以在我们的代码中生成一个独立的、线性的路径,它可以被遍历并折叠成一行。
afterSense.append(probs[i]*(hit*pHit + (1-hit) *pMiss))
这是因为当我们评估:
hits = (T == doors[i])
存储在 hits 中的值将返回布尔值真(1)或布尔值假(0)。这符合一般的“良好的编码风格”原则,通常被称为减少或最小化分支。
最终,目标是限制圈复杂度的水平,圈复杂度是另一个度量“分支”或线性独立路径数量的指标。
如果你是视觉生物,也许这个图会对你有帮助。

Image adopted from McCabe, T.J., “A complexity measure”, IEEE Trans. on Software Engineering, SE-2(4), pp.308–320(1976)
希望这对其他开发者和好奇的人有所帮助!欢迎给我发信息或在下面评论,我随时欢迎反馈。
道德准则—人工智能和人力资源分析

昨晚,我看了一部由剑桥分析公司(Cambridge Analytica)发起的关于信息战的纪录片(关于网飞的“大黑客”)。克里斯托弗·怀利(Christopher Wylie)是一名前数据科学家,最终成为告密者,他在收集数百万脸书用户的个人数据方面发挥了作用,这些用户最终成为外国特工政治广告的目标。对于数据科学家来说,没有什么比访问大型数据集更令人兴奋的了。我们都想用这些数据以积极的方式改变世界。但是,如果我们无意中造成伤害,而不是好处呢?对于任何数据科学家来说,这都是一个关键问题,它同样适用于人员分析——人工智能或数据科学在人力资源中的应用。
以道德问题为导向
法律和道德问题交织在一起。法律在某种程度上是社会道德标准的体现。作为一个社会,我们认为公平公正地对待他人是道德的,因此有法律来防止负面影响。作为一个社会,我们认为人们应该被允许在公众视线之外做许多事情,因此我们有隐私法。
然而,法律并不强制每一种道德行为,当道德标准仍有疑问时,法律必然落后。
第七章禁止基于两个理由的就业歧视——“明显的歧视意图”和“不同的影响”。但是,法律上有先例允许组织产生不同的影响,如果这种影响来自业务需求,特别是在似乎没有歧视意图的情况下。这是一个漏洞,一旦数据科学和人工智能进入画面,它可能会取消所有的 Title VII。组织可以根据最终产生不同影响的数据做出人事决策,甚至不包括种族或性别等明显敏感的变量。算法可能会在有偏见的历史数据中找到群体的统计代理,并基于它们进行歧视——而所有负责的组织都声称业务需求和歧视是无辜的。
目前还不清楚法律将如何对待这种“黑箱”算法,所以我们这些从事人员分析的人有时会处于蛮荒的西部,法律不确定,任何事情都可能发生。当法律模棱两可时,我们需要依靠自己的道德指南针。
那么,人物分析结果和产品中最大的道德问题是什么?
- 由于数据中的偏差,决策容易受到偏差的影响吗?
- 对更多数据的渴望和个人隐私之间的平衡是什么?
- 我们应该如何对算法造成的潜在错误负责(无论它们是否被“正确”编程)?
- 对于部分受算法决策影响的人,应该传达什么信息?
- 求职者和员工何时可以只与机器交互?什么时候应该联系真实的人?
人员分析的道德准则
计算机械协会至少从 1992 年起就有了道德规范,美国图书馆协会从 1939 年起就有了关于信息用户的道德规范,远远早于主流计算。就像医生们宣读希波克拉底誓言一样,这些其他职业也需要一套道德准则,因为他们的工作可能会对真实的人产生潜在的影响。
我相信我们这些从事人员分析的人也需要一套道德准则来指导我们的工作。首先,这是我自己的草稿,它受到了其他职业道德准则的启发:
- 我会时刻注意隐私和安全。
- 我将向我的客户公开我的方法中的假设和限制。
- 我将鼓励组织客户对他们的利益相关者保持透明。
- 我将仅出于收集数据的目的使用数据。
- 如果我或我的组织客户希望将数据用于原始目的之外的目的,我们将从数据收集对象处获得明确许可。
- 我将努力沟通,使我提供的信息更难被滥用。
- 我将努力使用可解释的算法,特别是当它们有可能影响个人的选择、表现和职业发展决策时。
- 我将清楚、坦率地说明数据分析和技术能解决什么,不能解决什么。
- 我将告知我的客户在使用任何形式的自动化时的权衡。
- 我将警惕潜在的不良偏见,即使在法律允许的情况下。
本《道德准则》涵盖五个类别— 数据隐私、目的驱动型使用、透明度、限制可见性、和反偏见行动。对我来说,这是一个开始。我将继续改进这些代码,因为我从人员分析领域的其他专业人士那里学到了很多东西。
代码:要查看的新数据

the source — https://fossbytes.com/microsoft-ai-system-deepcoder/
脸书、汽车和波音有什么共同点?它们都运行在超过 2000 万行代码的源代码上。
六年前,马克·安德森说“软件正在吞噬世界”,看看大卫·麦坎多斯visualizating我们就能明白这有多真实。我们被源代码及其日益增加的复杂性和挑战所淹没:影子 IT、缺乏文档、语言和框架异构性、缺乏代码历史的可见性、没有维护代码的简单方法…

The size of code bases in different systems
就像网络上的大量数据一样,支持大数据应用程序,现在大型程序库(例如 GitHub、Bitbucket 中的开源代码)支持一种新的应用程序:“大代码”。我们已经积累了数十亿字节的开放源代码数据,但很少有人尝试充分利用这些封存在内部的知识。代码是要看的新数据!
对源代码使用机器学习意味着从现有代码中自动学习,以便解决诸如预测程序错误、预测程序行为、预测标识符名称或自动创建新代码等任务。这种新方法打开了软件和代码开发方式中极其令人兴奋的机会之门。
源代码上的机器学习:是什么?
源代码上的机器学习(#MLonCode)是一个新兴的令人兴奋的研究领域,它位于深度学习、自然语言处理、软件工程和编程语言的十字路口。
在机器学习领域,源代码分析目前不如图像或自然语言分析重要。因此,对于使用源代码作为预测的数据源,还没有成熟的标准技术。我们站在一个前沿技术领域,仍然需要大量的研究。
处理代码数据
机器学习是应用于数据的数学算法:因此,任何输入数据都必须有一个数学表示。例如,当你想处理一幅图像时,你必须把它转换成一个矩阵。这很容易描述:图像是像素的矩阵,像素是用来描述颜色的数字阵列。
当涉及到编码时,挑战就有点棘手了。代码包含不同层次的理解:
- 语义层:写的是什么
- 结构层次:它是如何写的
- 图流级别:代码的每个部分如何与代码的其余部分交互
源代码的意图和意义依赖于这三个层次的理解。
任何应用于源代码的机器学习技术都应该确保这三个理解层次的数学嵌入。嵌入的质量将影响模型的质量
(这个话题会在后面的文章中详细介绍)
代码上的机器学习:用例
自动测试您的代码
许多软件开发过程必须覆盖大量的单元和集成测试用例,这需要很长(很长,很长……)的时间来完全实现。作为开发人员,尤其是在测试中,持续集成(CI)涉及到测试用例的优先排序、选择以及每个周期的执行。
如果对提交的代码变更的影响存在不确定性,或者如果代码和测试之间的可追溯性链接不可用,那么选择最有希望的测试用例来检测 bug 是很困难的。
今天对代码的自动理解,可以帮助你根据测试用例的持续时间、上次执行和失败历史来区分它们的优先级。在不断变化的环境中,一些算法如 Retecs 方法学会对容易出错的测试用例进行优先排序。
现实世界的例子:网飞的工程师们运行了一系列测试和基准测试,从多个维度验证这项服务,包括音视频播放质量、许可证处理、加密、安全性……所有这些导致了大量的测试案例,其中大部分是自动化的,需要执行这些测试来验证运行网飞的设备的功能。为了加快测试过程,网飞工程师使用了sRetecs 工艺。当针对设备运行持续集成时,它可以帮助他们从成千上万个可用的测试用例中选择最有希望的测试子集,或者推荐一组针对设备执行的测试用例,这将增加设备实时失败的概率。
代码建议和完成
多年来,开发人员就像装配线上的工人一样,一遍又一遍地编写同一行代码来解决同一类问题。有多少次我在寻找解决某个具体问题的方法,却发现自己每六个月都在寻找同样的答案!(而且我知道……不止我一个人在)。
工程师在处理问题时,通常会寻找一种已经实现的方法来解决问题。多年来,已经提出了许多代码搜索工具和平台(保佑你 StackOverflow)来帮助开发人员。通常的方法通常将源代码视为文本文档,并利用信息检索模型来检索与给定查询匹配的相关代码片段。他们缺乏对查询和源代码语义的深刻理解。
今天对代码的机器学习使得能够在问题域中运行语义相似性搜索,而不是在解决方案域中搜索。
使用最多的技术是 Code2Vec 。比如著名的自然语言处理例子:
vec(“man”)-vec(“woman”) = vec(“king”)-vec(“queen”)
Code2Vec 模型学习与源代码相关的类比,例如:
vec("receive")-vec("send") = vec("downlaod")-vec("upload")
现实世界的例子:脸书今年发布了 Aroma ,这是一个代码到代码的搜索和推荐工具,它使用机器学习(ML)来使从大型代码库中获得洞察力的过程变得更加容易。让我们记住脸书,有超过 2B 行的代码…
代码审查
确定程序的正确性需要对程序的预期行为有精确的理解,并有一种方法以适合自动化检查的形式明确地传达这种理解。
今天的代码审查工具缺乏对代码理解的深度,这会导致不愉快的情况(例如高测试覆盖率,同时有一个不工作的程序或高水平的代码文档,而每个注释都是过时的)。
对代码的机器学习为(深入)理解代码的意图并对其进行分析打开了一扇新的大门。这就像将二维图像与三维图像进行比较。是的,当前的代码审查工具确实提供了对代码的洞察,但与 ML 驱动的工具所能做的相比,这根本不算什么。
真实世界示例 : Autosoft 发布了第一个版本的代码审查工具,该工具评估代码及其注释的一致性,如果注释与代码有偏差,则自动建议更新。下一个版本将会比较单元测试和代码的有用性。当审查代码时,这使开发人员能够对如何提高任何代码的长期可读性和可维护性有真正可行的见解:这对我们来说很关键。
程序归纳和综合
这将是计算机科学自 200 年前创立以来的圣杯:获得一个完全自动化的编程系统。在计算机科学中,程序综合是自动构造满足给定高级规范的程序的任务。我们目前处于指导性编程的世界。作为开发人员,当我们必须解决一个复杂的问题时,我们将它分解成更小的问题,并编写解决这些问题的代码(我们讨论首要原则)。程序合成的工作方式正好相反:我们给计算机提供一个复杂的问题(我们想要的),把如何解决它的细节留给计算机。
我们现在还不能确定(请等一等,发言先生)但是最近研究人员和公司方面都取得了巨大的进步。今天我们可以谈谈增强编程。
现实世界的例子:
Pix2Code :虽然传统上前端开发人员的任务是将设计师的工作从原始的图形用户界面模型转化为实际的源代码,但这种趋势可能很快就会成为过去。他们的代码检测实体模型中的形状,解释它们的含义(段落、标题、图像……)并生成相关代码。
结论
让我们的想象力更进一步。如果 ML 生成的代码至少和最好的人类程序员可能产生的代码一样好,那可能会加速大多数开发人员再也不需要接触一行可执行代码的那一天。
粗略地考虑一下,上面引用的解决方案的执行准确率在 60%到 80%之间,ML 驱动的软件工程师不会很快淘汰人类程序员。
然而与此同时,我们仍然可以享受增强编程来挑战当今软件工程的许多缺陷(文档自动生成、适当的测试编写、代码优化、建议……)
关于代码的机器学习来了!
Ludwig 和 Comet.ml 的无代码深度学习管道

如何在命令行中结合使用 Ludwig 和 Comet.ml 来构建强大的深度学习模型——使用示例文本分类模型
Ludwig 是一个基于 TensorFlow 的工具箱,允许用户在不需要编写代码的情况下训练和测试深度学习模型。
通过从头到尾提供一个定义良好的、无代码深度学习管道,Ludwig 使从业者和研究人员能够快速训练和测试他们的模型,并获得强大的基线来比较实验。
“Ludwig 帮助我们在不编写代码的情况下建立最先进的模型,通过将 Ludwig 与 Comet 集成,我们可以以可重复的方式跟踪我们所有的实验,获得可见性,并更好地了解研究过程。”— Piero Molino,优步人工智能实验室的高级 ML / NLP 研究科学家,Ludwig 的创造者
Ludwig 提供了用于预处理数据、训练、发布预测和可视化的 CLI 命令。在本帖中,我们将向您展示如何使用 Ludwig,并使用 Comet.ml 跟踪您的 Ludwig 实验。
参见路德维希 Github 回购 此处

想要一个快速的图像字幕模型或视觉问答模型?在这 4 个简单的步骤中使用 Ludwig来建立、训练和评估深度学习模型。
在这里的 Comet.ml ,我们对 Ludwig 填补机器学习生态系统空白的潜力感到兴奋。Ludwig 最后采用机器学习模型、训练、数据和可视化的抽象表示的想法,并将它们变成一个自始至终无缝、可执行的管道。
这意味着我们终于可以花更少的时间:
- 处理不同数据类型的数据预处理☠️
- 将不同的模型架构网格化只是为了得到简单的基线模型
- 编写代码进行预测
更多时间:
- 获得透明的结果🚀
整合彗星与路德维希
我们与 Ludwig 团队合作整合 Comet.ml 以便用户可以在训练时实时跟踪基于 Ludwig 的实验。
Comet.ml 在三个主要领域对 Ludwig 进行了补充:
- 比较多个路德维希实验:路德维希让你轻松训练,迭代不同的模型和参数集。Comet 提供了一个接口来帮助你跟踪那些不同实验的结果和细节。
- 为您的分析组织的商店: Ludwig 允许您围绕训练过程和结果生成很酷的可视化效果。Comet 允许您跟踪这些可视化效果,并自动将它们与您的实验关联起来,而不是将它们保存在某个地方。
- 你的实验的元分析:你可能会多次重复你的路德维希实验。用 Comet 跟踪它们使您能够分析诸如哪些参数起作用之类的事情,以便构建更好的模型。
通过用 Comet.ml 运行您的 Ludwig 实验,您可以捕获您的实验的:
- 代码(您使用的命令行参数)
- 实时性能图表,以便您可以实时查看模型指标(而不是等到培训完成之后)
- 你和路德维希一起创作的可视化作品
- 环境详细信息(例如包版本)
- 运行历史记录(HTML 选项卡)
…以及更多!
用彗星运行路德维希
- 安装 Ludwig for Python(和 spacy for English 作为依赖项,因为我们在这个例子中使用了文本特性)。这个例子已经用 Python 3.6 测试过了。
$ pip install ludwig
$ python -m spacy download en
如果在安装 gmpy 时遇到问题,请安装 libgmp 或 gmp 。在基于 Debian 的 Linux 发行版上: sudo apt-get install libgmp3-dev 。MacOS 上: brew install gmp 。
2.安装 Comet:
$ pip install comet_ml
3.设置您的 Comet 凭据:
- 在 https://www.comet.ml 获取您的 API 密钥
- 让 Ludwig 可以使用 API 键,并设置 Ludwig 实验细节要报告给哪个 Comet 项目:
$ export COMET_API_KEY="..."
$ export COMET_PROJECT_NAME="..."
4.我们建议您为每个 Ludwig 实验创建一个新目录。
$ mkdir experiment1
$ cd experiment1
一些背景: 每次你想创建一个新的模型并训练它的时候,你会用到两个命令中的一个——
—训练
—实验一旦使用
*--comet*标志运行这些命令,就会创建一个*.comet.config*文件。这个*.comet.config*文件从您上面设置的环境变量中提取您的 API 键和 Comet 项目名。
如果您想运行另一个实验,建议您创建一个新目录。
5.下载数据集。对于这个例子,我们将使用 Reuters-21578 这个众所周知的新闻专线数据集来处理文本分类用例。它只包含 21,578 个新闻专线文档,分为 6 个类别。两个是“大”类别(许多正面文档),两个是“中”类别,两个是“小”类别(很少正面文档)。
- 小类:heat.csv,housing.csv
- 中等类别:coffee.csv、gold.csv
- 大类:acq.csv,earn.csv
$ curl [http://boston.lti.cs.cmu.edu/classes/95-865-K/HW/HW2/reuters-allcats-6.zip](http://boston.lti.cs.cmu.edu/classes/95-865-K/HW/HW2/reuters-allcats-6.zip?source=post_page---------------------------) -o reuters-allcats-6.zip
$ unzip reuters-allcats-6.zip
6.定义我们希望用我们想要的输入和输出特性构建的模型。用这些内容创建一个名为model_definition.yaml的文件:
input_features:
-
name: text
type: text
level: word
encoder: parallel_cnnoutput_features:
-
name: class
type: category
7.用新的--comet国旗训练模特
$ ludwig experiment --comet --data_csv reuters-allcats.csv \
--model_definition_file model_definition.yaml
一旦你运行这个,一个彗星实验将被创建。检查 Comet 实验 URL 的输出,并点击那个 URL。
8.在彗星上,你可以看到:
- 您在图表选项卡上的实时模型指标
- 您运行来训练您的实验的 bash 命令以及代码选项卡中的任何运行参数
- Ludwig 正在使用的超参数(默认)在超参数选项卡中
还有更多!看这个样本实验这里

使用彗星标志运行我们的示例文本分类 Ludwig 实验。你可以在这里和这个彗星实验互动
如果您选择使用 Ludwig 制作任何可视化效果,也可以通过运行以下命令将这些可视化效果上传到 Comet 的图像选项卡:
$ ludwig visualize --comet \
--visualization learning_curves \
--training_statistics \
./results/experiment_run_0/training_statistics.json
现在,您已经准备好一起使用 Ludwig 和 Comet 来构建您的深度学习模型了!在这里报名彗星。
将对立的例子编纂为特征
分离健壮和非健壮特征

Photo by Nahel Abdul Hadi on Unsplash
对立的例子对人工智能从业者来说是一个巨大的麻烦,但在人工智能理论中却是一个巨大的红利,有助于我们理解机器学习模型和算法的内部,并为像甘这样的新兴技术注入活力。
因此,毫不奇怪,我将在这里回顾的这篇新论文在业界引起了轰动。
或者,如果您喜欢使用另一个 PDF 阅读器应用程序阅读,请点击这里的。
计算机视觉算法的问题之一(详见 Hinton 的 Coursera 课程)是间接推理。这里的数据流可以用下图表示:

计算机视觉算法的困难在于从物体到标签没有直接的联系。取而代之的是,拍摄物体的照片,用像素表示,算法学习从像素而不是物体本身获得标签。算法不确定你说的标签是什么意思。如果你给它一张绵羊在山坡上吃草的图像,算法并不知道绵羊是白色物体,还是下面的绿色物体,或者上面的蓝色物体。因此,你需要向算法提供更多的训练数据,以说服它一只羊与天空或草地不同。然而,它可能会学到错误的东西。例如,它可以学习基于 fir 或其他次要特征来识别猫和狗。这就是漏洞所在,通过操纵像素,你可以让算法错误地识别对象。
为了对抗对抗性学习,建议更新优化算法,以在对抗性失真下最小化成本函数而不是成本函数的最大值:

在本文中,除了使用对抗性损失函数之外,还提出了识别鲁棒性特征,即在对抗性失真下不改变相关性符号的特征。
关于这篇论文,有一点要提一下,就是所谓的特性实际上是指倒数第二层的激活。在这种情况下,我们可以使用简单的皮尔逊相关,因为所有的非线性都是在前面的层中处理的。
本文试图做的第一件事是理清稳健和非稳健特征。首先,我们使用对抗性损失函数来训练模型,然后用仅生成稳健特征的修改数据来替换训练数据:

这里的条件是只有鲁棒特征与标签相关,而非鲁棒特征与标签的相关性为零。这个数据集是通过使用反向传播来发现的,但是更新输入,而不是权重和偏差(很像在白盒对抗学习中),最小化倒数第二层的激活的平方差:

在获得修改的数据集之后,对其执行另一轮训练,这一次使用标准(非对抗性)训练。最终的模型被发现在标准和敌对的环境下都能很好地工作。这个结果真的是个好消息,因为使用对抗性损失函数训练非常慢,你也不想太频繁。这里我们只做一次,然后生成新的测试数据,然后使用高效的标准训练程序来做我们的训练实验。在我看来,这是论文最好的实用结果。
以类似的方式,您可以生成非稳健数据集,而无需使用对抗性训练。结果如下图所示:

在我看来,作者没有实现健壮和非健壮特征的分离,因为使用了不同的训练程序,所以两者之间不可避免地存在重叠。
在接下来的实验中,作者故意给这些例子贴上随机标签。他们发现,使用鲁棒特征训练的模型会记住原始数据集,并纠正错误标记的数据本身,而标准分类器会学习新的和不正确的分类。这既是好消息,也是坏消息。虽然它表明该模型对错误标记是鲁棒的,但这也意味着降低了迁移学习的泛化能力和有用性。
本文的理论框架在于将数据点表示为两个高斯分布的混合,对应于二分类问题中的两个类别。使用最大似然法找到分布的参数,并且这些参数对应于逻辑回归中的参数。这是统计学习中的一种已知方法,可以追溯到几十年前。对抗性鲁棒学习使用相同的方法,但是扩展了样本以包括所有可能的对抗性例子:

因此,增加对抗性噪声 ε 有效地增加了习得的σ,混合了组合分布。

结论
本文是对抗鲁棒学习的一般框架的发展,在过去几年中一直在积极工作。它不再关注模型,而是将注意力转移到数据上,根据原始数据生成数据集,经过训练后,这些数据集会生成一个健壮的模型。我认为,这是本文的最大贡献,因为你可以节省时间,避免进行缓慢而昂贵的对抗性训练,而不是第一次为了产生稳健的数据集。
参考
- 安德鲁·易勒雅斯、什巴尼·桑图尔卡、迪米特里斯·齐普拉斯、洛根·恩斯特罗姆、布兰登·特兰、亚历山大·马德瑞。对立的例子不是 bug,它们是特性。https://arxiv.org/abs/1905.02175
用 Python 从头开始编写 2 层神经网络
本系列的第二部分:从头开始编写一个神经网络。用它来预测恶性乳腺癌肿瘤

在本文的第 1 部分 中,我们了解了我们的 2 层神经网络的架构。现在是建造它的时候了!同时,我们将深入探索和理解深度学习、反向传播和梯度下降优化算法的基础。
Navigating the Loss Landscape within deep learning training processes. Variations include: Std SGD, LR annealing, large LR or SGD+momentum. Loss values modified & scaled to facilitate visual contrast. Visuals by Javier Ideami@ideami.com
重要的事情先来
首先,如果你想尝试你自己的编码以及这篇文章,一个选择是使用 Jupyter 笔记本。它们极大地促进了在一个对数据探索者和研究人员非常友好的环境中使用 Python 代码进行工作和实验。你可以免费使用 Jupyter 笔记本,比如在谷歌实验室:colab.research.google.com。
所以,我们开始吧!首先我们导入一些标准的 Python 库。 Numpy 将帮助我们实现线性代数和数组功能。当我们导入和准备数据时,数据框将会非常方便。Matplotlib 将帮助我们做一些很酷的图表。最后, sklearn 帮助我们标准化我们的数据并显示有用的图表,例如混淆矩阵。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn import metrics
from sklearn.metrics import confusion_matrix
import itertools
接下来,我们创建一个 Python 类来设置和初始化我们的网络。
class dlnet:
def __init__(self, x, y):
self.X=x
self.Y=y
self.Yh=np.zeros((1,self.Y.shape[1])) self.L=2
self.dims = [9, 15, 1] self.param = {}
self.ch = {}
self.grad = {} self.loss = []
self.lr=0.003
self.sam = self.Y.shape[1]
我们首先命名我们的类 (dlnet),并定义它的 init 方法。第一次实例化该类时, init 方法被执行。它负责创建网络结构和控制网络的方法。
然后我们创建一系列保存网络关键数据的类变量。
这些变量中有很多都是矩阵。请记住,我们将通过使用矩阵合并多个计算来加速我们的计算。正如我之前指出的,如果你想刷新你的线性代数,可以查看 YouTube 上令人惊叹的视频 3Blue1Brown ,特别是他的线性代数系列精华。
- X :保存我们的输入层,我们给网络的数据。我们用变量 x,的值来初始化它,当我们创建它时,这个变量被传递给网络。 X 是一个矩阵,它的行数与特征拥有的数据一样多(稍后将详细介绍),列数与我们可用来训练网络的样本一样多。
- Y :保存我们想要的输出,我们将用它来训练网络。我们用变量 y 的值初始化它,当我们初始化它时,它被传递给网络。
- Yh :保存我们的网络产生的输出。它应该与我们期望的目标值 Y 具有相同的尺寸。我们将其初始化为零。
- L :保存我们网络的层数,2。
- 接下来,我们定义每一层中神经元或单元的数量。我们用 numpy 数组来做这个。数组的第一个组件是我们的输入(它不被算作网络的一层)。我们的输入将有 9 个单元,因为正如我们稍后将看到的,我们的数据集将有 9 个有用的特征。接下来,神经网络的第一层将有 15 个神经元,我们的第二层也是最后一层将有 1 个(网络的输出)。
- param :一个 Python 字典,将保存网络各层的 W 和 b 参数。
- ch :一个缓存变量,一个 python 字典,它将保存一些我们在梯度下降算法的反向传递中需要的中间计算。
最后,我们声明另外三个参数。
- lr :我们的学习率。这设定了网络学习的速度。
- 山姆:我们拥有的训练样本的数量。
- loss :我们将存储网络每 x 次迭代的损耗值的数组。损失值表示我们的网络的预测输出和目标输出之间的差异。
注意最后一个元素,损失,因为它是至关重要的。损失值是多少?这一切都与训练我们的网络学习神秘功能的过程有关。
我们很快就会谈到这一点,但首先,让我们定义函数 nInit ,它将用随机值初始化我们网络的参数。
def nInit(self):
np.random.seed(1)
self.param['W1'] = np.random.randn(self.dims[1], self.dims[0]) / np.sqrt(self.dims[0])
self.param['b1'] = np.zeros((self.dims[1], 1))
self.param['W2'] = np.random.randn(self.dims[2], self.dims[1]) / np.sqrt(self.dims[1])
self.param['b2'] = np.zeros((self.dims[2], 1))
return
当我们将矩阵相乘时,就像乘积 W1 X 和 W2 A1 一样,为了使乘积成为可能,这些矩阵的维数必须正确。这就是为什么我们必须正确设置权重和偏好矩阵的维度。
- W1 :行数是该层的隐藏单元数,dims[1],列数是上一层的特征数/行数(本例中为 X,我们的输入数据),dims[0]。
b1 :行数与 W1 相同,单列。
W2 :行数是该层的隐藏单元数,dims[2],列数也是该层的输入行数,dims[1]。
b2: 行数与 W2 相同,单列。
现在,让我们在类中定义一个函数,它将在我们网络中每一层的每个单元执行计算。我们称之为 forward ,因为它将接受网络的输入,并通过不同的层向前传递,直到产生输出。
我们还需要声明 Relu 和 Sigmoid 函数,它们将计算每层输出的非线性激活函数。
def Sigmoid(Z):
return 1/(1+np.exp(-Z))def Relu(Z):
return np.maximum(0,Z)def forward(self):
Z1 = self.param['W1'].dot(self.X) + self.param['b1']
A1 = Relu(Z1)
self.ch['Z1'],self.ch['A1']=Z1,A1
Z2 = self.param['W2'].dot(A1) + self.param['b2']
A2 = Sigmoid(Z2)
self.ch['Z2'],self.ch['A2']=Z2,A2 self.Yh=A2
loss=self.nloss(A2)
return self.Yh, loss
Relu 和 Sigmoid 函数声明激活计算。forward 函数执行我们之前描述的计算。
我们将第一层的权重乘以输入数据,并添加第一个偏置矩阵 b1 ,以产生 Z1 。然后我们将 Relu 函数应用于 Z1 以产生 A1 。
接下来,我们将第二层的权重矩阵乘以其输入 A1 (第一层的输出,即第二层的输入),并添加第二个偏置矩阵 b2 ,以产生 Z2 。然后,我们将 Sigmoid 函数应用于 Z2 以产生 A2 ,它实际上是网络的输出 Yh 。
就是这样!我们只是通过网络运行我们的输入数据,并产生 Yh,一个输出。
合乎逻辑的下一步是找出我们的结果有多好。
为此,我们可以将我们产生的输出与我们应该获得的输出进行比较: Yh 和 Y 。为了进行计算,我们将向网络添加最后一个函数损失函数。
希望没什么损失
损失函数有很多种。损失函数的目标是表示我们的结果离预期目标有多远,并对我们用来训练网络的所有样本的差值进行平均。
深度学习中使用的最简单的损失函数之一是 MSE、或均方误差。
**squared_errors** = (self.Yh - self.Y) ** 2
**self.Loss=** np.sum(squared_errors)
MSE loss 函数计算差异,即我们使用的所有样本的预测输出和目标输出之间的距离,然后计算该差异的平方。
最后,它将所有这些操作相加。距离的平方确保我们产生一个总是正的绝对距离值。
MSE 是一种简单的方法,用于确定我们离目标有多远,我们的网络计算出的函数在连接输入数据和目标输出方面有多精确。
MSE 常用于回归挑战,当网络输出为连续值时,例如:温度值或房屋成本。
然而,在本文中,我们将致力于一种不同的挑战,一种二元分类挑战,其中我们的输出将是 0 或 1 (0 表示良性,1 表示恶性)。
在处理分类挑战时,有一个不同的损失函数可以更好地表达我们的预测输出和正确输出之间的差异。它被称为交叉熵损失函数,我们将在我们的网络中使用它。
我们选择损失函数的依据是它们在多大程度上表达了我们的网络性能质量与我们正在应对的特定挑战的关系。交叉熵对于分类问题来说是一个巨大的损失函数(就像我们将要研究的那个一样),因为它强烈地惩罚了那些有把握但却是错误的预测(就像以很高的把握预测一个肿瘤是恶性的,而实际上它是良性的)。
def nloss(self,Yh):
loss = (1./self.sam) * (-np.dot(self.Y,np.log(Yh).T) - np.dot(1-self.Y, np.log(1-Yh).T))
return loss
正如你在上面看到的,在 forward 函数的末尾,我们调用这个 nloss 方法(它计算损失),然后将得到的损失值存储在 loss 数组中。这将使我们稍后能够绘制并直观地理解损失值在网络训练期间如何变化。
信不信由你,我们已经创建了几乎一半我们需要的代码。但是现在我们到了关键时刻。
我们已经计算了一个产量 Yh ,并且计算了损失:我们离预期产量有多远, Y 。现在的问题是:怎样才能提高那个结果 Yh,提高是什么意思?
为了改善结果,我们需要得到那个损失值来减少。我们的损失值越低,我们的目标和预测输出( Y 和 Yh )之间的距离就越小,我们的网络性能就越好。
让我们回顾一下。我们在网络中产生两种输出:
- Yh ,网络的计算结果。
- 损耗,Yh 和 Y 之间的距离。
并且正是从那个目标,从最小化损失的目标,最小化我们的预测和正确输出之间的距离的目标,网络的训练过程诞生了。****
我需要一个健身房
在这个阶段,我们已经执行了一次正向传递,获得了我们的输出 Yh ,然后计算我们的损失、我们的误差、我们的预测输出和正确输出之间的距离( Yh 和 Y )。
下一个合乎逻辑的步骤是稍微改变我们网络的参数值、我们的权重和偏差、并再次执行前向传递,以查看我们的损失是否有望减少。因此,培训过程应该是这样的:
- 我们最初将权重和偏差设置为随机值。
- 我们通过网络向前运行输入数据,并产生一个结果:Yh。
- 我们计算预测输出和目标输出之间的损耗,即 Yh 和 Y 之间的距离。
- 如果不够好,我们稍微改变我们的权重和偏差,并再次通过网络运行输入数据,看看我们的损失是否有所改善,如果现在足够低。如果没有,我们不断重复同样的过程(大概几千年)。
你说对了,那一点效率都没有。
现在,想一想,要稍微改变我们的权重和偏差,我们可以做两件事情中的一件:
- 我们可以增加一点
- 我们可以减少一点。
一定有办法,以我们的损失为起点,我们可以计算我们是否应该增加或减少** 以使这样的损失最小化。**
****输入微积分,输入强大的导数,梯度。让我们用非常简单的方法来探索导数是如何工作的。如果你想更深入,我会用 3Blue1Brown E 微积分的本质系列再给你介绍一遍。
Hello 渐变
为了了解我们应该朝哪个方向改变我们的权重和偏好,最好是了解这些权重和偏好的微小变化会对我们的最终亏损产生什么影响。
而我们可以对这个使用导数,准确的说是偏导数。因为偏导数将告诉我们一个特定参数的微小变化,比如说 W1 ,对我们的最终损失有什么影响。
有了这些信息,我们将能够决定在什么方向上修改 W1** 以减少损失。**
先来刷新一下导数的直觉。
想想函数 x 的 2 次幂:x2**

在 x=3,y=9 时。让我们把注意力集中在那个点上,求导数,x=3 时的变化率。
为了做到这一点,我们将研究当我们把 x 稍微增加一点时,y 会发生什么。这个微小的量最终收敛到 0(极限),但出于我们的目的,我们将认为它是一个非常小的值,比如 0.001。
那么,当 x=3+0.001 ,y 的值是多少呢?y =(3.001)* * 2 =9.006****
因此当 x 增加 0.001 时,x=3.001 变成 y=9.006。
而变化率(导数)就是新的 f(x+h)和之前的 f(x)之差,除以那个微小的增量 h:(9.006–9)/0.001 =6。
6 告诉我们什么?
6 告诉我们,在这个函数 x2 中,在 x=3 时,变化率为正,强度为 6。它告诉我们,在这一点上,如果我们增加 x 一点,y 会以一种积极的方式改变,并且强度是“6 倍以上”。基本上是**,输入端的 0.001 增量将变成输出端的 0.006 增量。****
所以我们看到,我们可以很容易地按照这种方法手动计算导数:
导数= (f(x+h) — f(x) ) /h
那么在 x=-2** 时呢?
dx = f(-2+0.001)-f(-2))/0.001
dx = f(-1.999)-f(-2))/0.001
dx = 3.996–4/0.001 =-4**
在 x=-2 处,变化率为负,函数向下移动,强度为 4。
用这种方法计算导数需要很长时间,但是感谢数学天才,通过使用微分方程,表达原始函数导数的特殊方程,很容易快速计算它们。因此,不用计算每一点的导数,一个简单的方程就能自动计算出这个函数的所有地方!****
大多数,但不是所有的方程,都有一个可以用另一个方程表示的导数。
**x2 的导数 i s 函数 2x 。我们用字母 d 来表示导数,后面是我们正在研究的变量的变化率。
如果 dx =2x:
- 当 x 为 3 时,dx 等于 2*3 = 6
- 当 x 为-2 时,dx 等于 2*-2 = -4
- 两者都符合我们的手工计算。使用微分方程要快得多!
好吧,让我们回顾一下。借助导数,我们可以了解当我们修改某个输入变量(本例中为 x)时,函数的输出在某个点朝哪个方向变化。****
如果我们能够使用导数来了解我们的权重和偏差的微小变化如何影响网络损耗,那就太好了。
- 如果我们看到损失相对于重量的导数为正,这意味着增加重量会使损失增加。也就是说:反其道而行之,减少体重使损失减少。
- 而如果我们发现导数是负的,就意味着增加重量使得损失减少。这就是我们想要的!。所以我们接着进行增加权重的值。
- 因此,通过观察损耗相对于网络参数的导数,我们可以了解改变该参数对网络损耗的影响。
- 基于此,我们可以修改该参数,使其影响朝着降低损耗的方向移动。
不过,还有一个小问题,最后一个障碍。****
我们的网络是由层构成的。它可以有 2 层或 200 层。我们需要了解所有权重和偏差的变化如何影响网络末端的损耗。
记住,我们的网络是一系列连锁在一起的功能。例如,如果我们想在多层网络中计算 W1 的变化如何影响最终输出端的损耗,,我们需要找到一种方法来连接 W1 和网络末端损耗之间的不同导数,使它们相互关联。
我们能不能,以某种方式,锁住他们?
链式法则
是的,确实如此。微积分给了我们一个叫做的东西,导数的链式法则,当我们仔细看的时候,这确实是一个非常简单的概念。
首先,偏导数是研究当我们修改一个变量时,另一个变量发生的变化的导数。
要将这一切与即将到来的代码联系起来:
- 例如,我将把损失**相对于输出 Yh 的偏导数命名为 dLoss_Yh**
- 即:损失函数相对于变量 Yh 的导数。
- 也就是说:当我们稍微修改 Yh,对损失有什么影响?****
****链式法则告诉我们,为了理解一个变量的变化对另一个的影响,当它们彼此远离时,我们可以通过乘以它们的将它们之间的偏导数链接起来。
是时候谈谈神经网络中的反向传播算法了,在这种情况下,特别是在我们的 2 层网络中。
****反向传播利用链式法则找出网络不同参数的变化对其最终损耗值的影响程度。
让我们挑选一个参数,了解链式法则的作用。
假设我们想了解 W1 的微小变化会如何影响损失。好吧,让我们从损失的等式开始:
损失= -(Y Log Yh + (1-Y) Log (1-Yh))
嗯, W1 不在这个等式里,但是 Yh 在。让我们继续计算我们的结果 Yh 的变化如何影响损失。让我们看看,在我们这样做之后,我们是否可以继续链接导数,直到我们到达 W1 。
为了计算这个导数,我们寻找损失函数的导数方程。稍微刷新一下微积分,或者上网查一下,就能学会快速找到各类方程的导数。在这种情况下,我们发现:
dLoss _ Yh =—(Y/Yh—(1-Y)/(1-Yh))
好了,搞定一个。现在,请记住,我们希望继续链接导数,直到我们到达 W1** 。**
让我们看看,我们网络的下一步是什么,我们是如何生产 Yh 的?****
Yh =乙状结肠(Z2)
好吧,很好。W1 仍然不存在,但是我们得到了 Z2。所以我们来看看 Z2 的变化对 Yh 有什么影响。为此,我们需要知道 sigmoid 函数的导数,恰好是:
dSigmoid = sigmoid(x)(1.0—sigmoid(x))。*
为了简化书写,我们将该微分方程表示为 dSigmoid 。因此:
dYh _ Z2=dSigmoid(Z2)
在这一阶段,我们已经可以将这两个导数链接起来(相乘),以找到损失相对于 Z2 的导数。
dLoss _ Z2=dLoss _ Yh * dSigmoid(Z2)
太好了,我们继续吧。我们是怎么算出 z2 的?****
Z2 = W2 A1 + b2
还是那句话, W1 还是没有,但是我们得到了 A1 。让我们看看 A1 的变化对 Z2 有什么影响。因此:
dZ2_A1 = W2
****我们可以将该导数链接到之前的 2 个,以获得 A1 和网络损耗之间的总导数:
dLoss _ A1=W2*****dLoss _ Z2
如你所见,我们正在一个接一个地链接导数,直到我们到达 W1,我们的目标。到目前为止,我们已经从损失转移到 Yh,从 Yh 转移到 Z2,从 Z2 转移到 A1。
非常好。我们继续。我们是如何产生 A1 的?
A1 = Relu (Z1)。
W1 仍然不存在,但是我们有了 Z1。我们需要 Relu 的导数。当输入为 0 或小于 0 时,Relu 函数的导数为 0,否则为 1。
同样,为了简化写法,我们将它表示为 dRelu 。
dA1_Z1=dRelu (Z1)
很好,让我们再次将这个最新的导数与所有之前的导数链接起来,以获得损失相对于 Z1 的完整导数:
dLoss _ Z1=dLoss _ A1 * dRelu(Z1)
太好了,我们正在接近!我们是如何计算 Z1 的?
Z1 = W1 X+ b1
是啊!W1 在那里!我们很想你 W1!见到你真是太好了!😃
太刺激了!因此这将是最终的导数:
dZ1_W1 = X
让我们把这个最新的衍生产品和之前的联系起来:
dLoss_W1= X * dLoss_Z1
仅此而已。我们已经计算了损耗相对于参数 W1 的导数。也就是我们稍微修改 W1 的时候,损耗变化多少,往哪个方向变化。****
让我们回顾一下:
- 我们已经从网络的末端开始,在损失值处,逐渐链式衍生,直到到达 W1。
- 通过链式法则,我们开始一个接一个地将所有这些导数相乘,以找到最终的变化率,即 W1 的变化对网络输出损耗的影响。****
在 Python 代码中,正确排序以说明我们乘矩阵的方式,这个链接过程的代码是:
dLoss _ Yh=——(NP . divide(self。y,self。Yh ) — np.divide(1 —自身。y,1 —自我。Yh))
dLoss _ Z2=dLoss _ Yh dSigmoid(self . ch[' Z2 '])
dLoss _ A1= NP . dot(self . param[" W2 "])。t,dLoss _ Z2)
dLoss _ Z1=dLoss _ A1 dRelu(self . ch[' Z1 '])
dLoss _ W1= 1。/自我。x . shape[1]* NP . dot(dLoss _ Z1,self。X.T)**
请注意,在最后一步,我们将结果除以层的单元数,,从而使与每个权重 W 相关的导数在每个单元上正确缩放。
你刚刚看到的是反向传播,或者说几乎是所有深度学习过程的关键成分。****

让我们呼吸吧!这是整篇文章中最难的部分,从现在开始事情变得容易了。
- 通过计算 W1 的变化对输出端损耗的影响,我们现在可以决定如何修改 W1 以降低该损耗。
- 如果导数为正,这意味着 W1 的变化增加了损失,因此:我们将减少 W1。
- 如果导数是负的,这意味着对 W1 的改变减少了损失,这就是我们想要的,所以:我们将增加 W1 的值。
- 我们对 W1 所做的,我们将以完全相同的方式对 W2、b1 和 b2 做。
通过这种方式,我们产生了反向传递,它成为了我们的 python 类的反向传播函数。我们还声明了 dRelu 和 dSigmoid,它们是 Relu 和 Sigmoid 函数的派生物,在计算反向传播算法时需要用到它们。
def dRelu(x):
x[x<=0] = 0
x[x>0] = 1
return xdef dSigmoid(Z):
s = 1/(1+np.exp(-Z))
dZ = s * (1-s)
return dZdef backward(self):
dLoss_Yh = - (np.divide(self.Y, self.Yh ) - np.divide(1 - self.Y, 1 - self.Yh))
dLoss_Z2 = dLoss_Yh * dSigmoid(self.ch['Z2'])
dLoss_A1 = np.dot(self.param["W2"].T,dLoss_Z2)
dLoss_W2 = 1./self.ch['A1'].shape[1] * np.dot(dLoss_Z2,self.ch['A1'].T)
dLoss_b2 = 1./self.ch['A1'].shape[1] * np.dot(dLoss_Z2, np.ones([dLoss_Z2.shape[1],1]))
dLoss_Z1 = dLoss_A1 * dRelu(self.ch['Z1'])
dLoss_A0 = np.dot(self.param["W1"].T,dLoss_Z1)
dLoss_W1 = 1./self.X.shape[1] * np.dot(dLoss_Z1,self.X.T)
dLoss_b1 = 1./self.X.shape[1] * np.dot(dLoss_Z1, np.ones([dLoss_Z1.shape[1],1]))
self.param["W1"] = self.param["W1"] - self.lr * dLoss_W1
self.param["b1"] = self.param["b1"] - self.lr * dLoss_b1
self.param["W2"] = self.param["W2"] - self.lr * dLoss_W2
self.param["b2"] = self.param["b2"] - self.lr * dLoss_b2
在后向函数中,在计算完 W1、b1、W2 和 b2 所需的所有导数后,我们在最后一行中通过减去导数并乘以我们的学习速率来更新我们的权重和偏差。
请记住,学习速率是一个允许我们设置网络学习速度的参数。因此,我们通过与学习速度成比例的数量来修改我们的权重和偏差。
好的,目前为止我们有:
- 向前传球
- 计算网络的损耗
- 执行了反向传递并更新了我们网络的参数(以便在下一次正向传递中损耗会降低)。
事实上,梯度下降优化算法,这是训练我们神经网络的另一个迷人的难题。
是时候回到本文的第一个动画了。
沿着梯度下降
我们再来看看文章的第一个动画。

Navigating the Loss Landscape. Values have been modified and scaled up to facilitate visual contrast.
这就是梯度下降优化算法,这是逐步优化我们网络的权重的基础和最常用的方法,最终它们将允许我们计算一个函数,该函数将我们的输入数据与我们期望的输出准确有效地联系起来。
让我们分析一下动画中发生了什么,它代表了梯度下降算法的关键方面。****
- 我们首先用随机值初始化我们的权重和偏差。我们建立我们训练过程的初始状态。

- 然后,我们通过网络馈送输入数据,以执行正向传递,并获得 Yh 。有了 Yh 我们现在就可以计算损失:我们离理想结果 Y 还有多远?我们的目标是减少损失,使它尽可能小。
- 你在动画中看到的是我们网络的可能损失值的一幅风景。
- 地貌有山丘和山谷。丘陵是损耗高的地方。山谷是我们称之为“极小值”的地方,在那里损失很低。

- 一个函数可以有多个局部最小值和一个全局最小值。全局最小值是景观的最低部分,是最低的可能损失值。可能有其他的谷是局部极小值,这些地方的损失很低,但还没有低到可能的程度。

- ****当我们最初用随机值设置我们的权重和偏差,并执行我们的第一次向前传球来计算我们的第一次损失值时,就好像我们正在将球随机定位在该动画中的初始景观部分。
- 我们在可能损失值范围内的某个地方随机抛球。
- 通常,我们会将它放在该地形的一个山丘上,因为开始时权重是随机的,网络不是很有效,损耗会很高,我们会被定位在该地形的一个高地(高损耗)

- 我们的目标是从初始点开始,逐渐向高处的一个山谷移动,希望到达全局最小值(最低的山谷),即损失尽可能小的一部分。

- 为了做到这一点,我们执行梯度下降算法。我们已经在前一节中看到了它的一个迭代。我们现在要做的就是让不断重复同样的过程,直到我们的损失变得足够小。****
- 我们执行向前传递,计算损失,然后执行向后传递来更新我们的权重和偏差。
- 然后,我们重复相同的过程多次(预先设置),或者直到损失变得稳定。
****
让我们看一下代码:
nn = dlnet(x,y)
nn.gd(x, y, iter = 15000)def gd(self,X, Y, iter = 3000):
np.random.seed(1)
self.nInit()
for i in range(0, iter):
Yh, loss=self.forward()
self.backward()
if i % 500 == 0:
print ("Cost after iteration %i: %f" %(i, loss))
self.loss.append(loss)
return
就这样?对,就是这样。
我们首先实例化我们的神经网络。然后运行多次迭代,执行向前和向后传递并更新我们的权重。每 x 次迭代,我们打印损失值。
经过不到 100 行的 Python 代码,我们有了一个全功能的 2 层神经网络,它执行反向传播和梯度下降。****
这是一个基本的网络,现在可以在许多方面进行优化。因为正如我们将很快讨论的,神经网络的性能受到许多关键问题的强烈影响。两个非常重要的是:
- 特征工程(Feature engineering):理解我们的输入数据,并以一种使网络工作更容易的方式准备它。
- ****超参数优化:神经网络的超参数是那些对训练过程有关键影响的变量,不包括权重和偏差。它们包括:学习率,层数,每层单元数等。
关于优化算法,在本文中,我们使用最简单和最纯粹的梯度下降算法。我们的目的是了解反向传播和基本的优化和训练过程。
梯度下降有很多种变体,稍后我会列举其中几种。两个非常简单的例子是:
- ****批量梯度下降:我们不是在整个训练集上运行我们的正向和反向通道,而是批量运行它们。假设我们有 1000 行数据。我们将 1000 行分成 10 批,每批 100 行。然后,我们将对第一批(前 100 行)运行训练循环(向前和向后传递),并继续更新我们的权重。然后,我们继续进行其余的批次。当我们完成所有批次(本例中为 10 个)时,这被称为一个时期。然后我们继续下一个纪元。处理大型数据集时,批量梯度下降训练比纯梯度下降算法更快,因为您更新权重的频率更高(您不必等到处理完整个数据集后再更新网络参数)。
- 随机梯度下降:当你的批次的大小为 1 时,这叫做随机梯度下降。它甚至更快,因为您在处理每一个样品后都会更新您的重量。但是,由于网络是基于非常少的数据做出决策的,因此它可能会产生不规则的路径(噪声)。有不同的方法来处理这个问题,其中一些将在本文后面提到。
现在是时候测试和尝试我们的网络了。只有使用它,我们才能充分了解它的潜力和局限性。
在本文的最后部分,第 3 部分 ,我们将使用威斯康星癌症数据集,学习准备我们的数据,通过 out 网络运行它并分析结果。我们还将讨论一些更高级的话题。我们来看 第三部 。
链接到本文的 3 个部分:
第 1 部分 | 第 2 部分|第 3 部分
编码意识
一篇很长的关于图灵测试的帖子

美国哲学家约翰·r·塞尔(John R. Searle)在他举世闻名的 1980 年“思想、大脑和程序”中,以优雅而明确的方式证明了算法的执行(无论它有多复杂)并不是理解的充分条件。在他的格丹肯实验、中文房间论证(CRA)中,他想象着把自己锁在一个房间里,里面有一大堆中文作品(对他来说只是一堆毫无意义的的潦草字迹)和一本用英语写的规则的书,这些规则把这些符号和传给他的其他符号联系起来。从房间外面看到这一过程,没有人会认为他只是在进行符号操作,无论如何,他现在比他第一次走进房间时更懂中文了。这个思想实验证明,正如他在他的工作中几次重新措辞,并在这里以一种简单而有效的形式提出,“符号操纵本身不足以理解”(塞尔,1980),或者更简单,“句法本身不足以构成语义”。
句法本身不足以构成语义。
很明显,许多人对这个实验提出了反对意见,塞尔对几乎所有人都给出了精确的回答;然而,对其中一个最相关的,所谓的系统回复的回答并不令人满意,缺乏连贯性,为一些有趣的结果和进一步的争论敞开了大门。
系统回复声称,即使接受仅仅执行算法不会导致理解,执行者(在 CRA 中,被锁在房间里的人,或者更一般地,图灵机(TM)通过计算机程序的步骤)也只是整个系统的一部分,并且这个系统理解中文(继续使用塞尔的例子)。塞尔对这一回答的回应遵循两个平行的方向:在第一个方向中,他假设将整个系统“内化”到个人内部(达到一种退化的配置,在这种配置中,人实际上是整个系统),并声称即使在这种情况下,系统仍然没有理解;在第二本书里,他认为,把对一个人的理解与一张纸联系起来是多么可笑,这张纸曾经正确地强调了只有这个人不会有任何理解。作为第二个,更多的是对把理解归因于整个系统的假设性后果的分析,重点必须放在第一个上,以便理解它在哪里以及为什么缺乏合理性,鉴于塞尔自己正确地连接到他的论点的后果,更是如此。
在没有深入进入经典哲学的“他者”问题(甚至开始对这些论点进行推理所需的前提)理解是,作为意识、意向性等。“只能从许多不同的角度来理解其客观性质的主观特征。塞尔在他的论证中只谈论理解而不谈论他人(首先是意识),这一事实并不是对后续步骤的限制;在某种意义上,因为哈纳德在 1991 年写道“只有一个身心问题”,在另一个意义上,因为在后来的著作中讨论和进一步阐述这个概念的方式显然可以与意识的概念互换使用。
在试图对这些现象进行一般和连贯的描述时,几十年来提出了不同的理论;这篇文章的目的不是精确地描述所有这些理论,而是为了使它们尽可能地完备,并在接下来的段落中避免概念模糊,将宇宙分成两部分是有用的:第一组理论由那些在计算主义的广义概念下认识自己的理论组成,对于它们来说,心理过程完全是计算性的,并可以通过纯粹的符号操作来描述。这些理论的推论在很大程度上是有争议的,但毫无疑问的是,接受计算主义就是接受至少一些二元论的影子,因此心灵和身体是不同的,可以分割的(强人工智能比其他人更重视这个概念)。后者,通常被称为物理主义,与人类精神现象可能依赖于实际人类大脑的物理/化学特性的想法有关。这第二种类型的理论,把有意现象的诞生不是在计算的行为,而是在“具有某种生物结构的某种有机体”的事实,可以生存到 can 特别是生物自然主义正是塞尔在其论文的最后几段和整个职业生涯中所支持的理论。即使在某种意义上,这种特定的理论将自己置于上述两种划分的中间(考虑到它关于不可还原的精神现象的存在的立场,不被纯物理主义者所接受),它肯定会将产生意向性的能力赋予实际的人脑及其特定的本性。物理主义理论,除了塞尔列出的特性,还有一个很大的优势,那就是不受抽象限制的影响,这些限制在 Gödel、卢卡斯和彭罗斯的作品中有很强的描述。这种限制最初是针对形式系统的,这些系统具有足够的复杂性来表达自然数的基本算术,并且是一致的,随后被利用来对图灵机(或等同物)的能力进行一些限制,以充分模拟人脑的能力,攻击计算主义的基础;“头脑不能被解释为机器”(卢卡斯,1961)。
根据这段解释性的段落,我们可以更好地理解为什么塞尔用来反驳系统回答的内化过程显示出它的不足。论文中没有给出如何进行这一过程的细节,这种缺乏也不能用思想实验的制度来解释,他的所有结论都是在这个制度中得出的。谈论内化打破了假设推理的假设,并且至少需要一个心灵理论作为共同基础,这与所有其他主张都需要一个计算理论的情况没有什么不同。事实上,不能保证内化的过程比传统的学习过程更能导致纯粹的句法执行,传统的学习过程已经导致了对规则所使用的语言的相互同意的实际理解。如果这是真的,正如塞尔自己所支持的,理解不是一个执行的问题,而是大脑的一些化学特性的结果,那么对于一个内化过程,其唯一给定的细节是利用这些特性的事实,又能说什么呢?把这些放在一边是用双重和计算的方式进行推理,使用他自己已经证明是错误的工具——或者需要被大量审查的工具。
事实上,不能保证内化的过程比传统的学习过程更能导致纯粹的句法执行,传统的学习过程已经导致了对规则所使用的语言的相互同意的实际理解。
此外,内化过程(及其分析)打破了他心问题的围墙,就好像存在一个“塞尔的小‘潜望镜’跨越了其他不可逾越的他心障碍”(Hayes et al .al,1992)以图灵不可区分的表现证明了汉语口语理解能力的缺失。这一论点虽然有风险,但增加了整个答复的另一层不确定性,与前一个论点相结合,即使不能完全接受系统的答复,至少也能更令人信服地考虑接受它为真可能产生的后果。
理解在哪里?
简单地说,接受系统的回答就是接受这样一个事实,即个人是系统的一部分,系统理解这个故事。如此简单的声明的结果可能很难管理,并且向两个不同的方向发展:我们将系统的边界放在哪里,以及我们愿意将理解分配给系统的哪些工件。
简单地说,接受系统的回答就是接受这样一个事实,即个人是系统的一部分,系统理解这个故事。
尽管这两个问题都将在稍后进行深入研究(前者是后者的直接结果),但提出一个前提来讨论现在被称为理解的内容是有用的,重点是我们可以在多大程度上研究这个概念。关于理解的本质和价值的争论贯穿整个哲学,但是没有理由不接受塞尔在他论文的注释中给出的定义。他将理解定义为当代拥有的有效且有意的精神状态。定义的核心当然是围绕意向性的概念,即“某些精神状态的特征,通过这些特征,它们指向或关于世界上的事物和状态”。潜在的问题,是所有主观经验所共有的,并且与意识的困难问题相关,在于这些现象的本质,这使得定义有趣,但在某种意义上完全无用。完全接受我们的外部观点的限制,并且在操作我们的探索的当代需要中,我们只能寻找理解的痕迹:通过这些迹象,应用我们在日常社会关系中应用的相同的常识性论点和真实性假设,我们可以假设理解在其他人身上的存在,就像我们把它与我们联系起来一样;或者,类似地,如果没有意向性的归属,我们就很难理解这些符号。
因此,接受上文指出的系统答复的第二个结果是以一种新的形式慢慢重塑,即使在某种意义上可以被视为一种限制,但它将概念建立在一个可以实际处理它的领域中。至于塞尔的担心,虽然一个人不懂中文,但不知何故这个人和纸片的结合可能懂中文,但纸片的想法——尽管这样称呼它是对其内容的有意低估,但这里真正的问题是——是理解的痕迹和证明——不是正式的,不可能的,但在某种最大可能性的情况下——理解是存在的。一个现在不能被忽略的明显问题是,由这种推理引发的想法是,计算中有某种东西具有“诞生”交互式守护进程的能力,这种守护进程能够以行为方式展示理解——与只能显示其被动痕迹的工件明显不同。这个想法在文学上并不新鲜,许多贡献大到足以成为一个平行的理论分支,称为虚拟心灵对 CRA 的回应。与系统的回答类似,VMR 承认接线员一点也不懂中文,但强调重要的事实是理解是否被创造出来,而不是接线员是否真正理解。
重要的事实是理解是否被创造,而不是操作者是否是真正理解的人。
计算行为创造了新的虚拟实体,其心理特征完全取决于程序和中文数据库。尽管这一立场至少需要某种计算主义,也不清楚这如何能与 Gödel 定理共存,但计算因其内在特征而成为一种特殊事物是相关且无可争议的。我们给被锁在房间里的个人的一套规则,不仅是表达表象的媒介,也是引出某些机器的“表象活动”的媒介。“任何计算机程序固有的程序性后果使它在语义学中有了立足之地”,在语义学中,一个符号的意义“是通过参照它与其他现象的因果联系来寻求的”。与符号执行(意思是活动)相关的计算中的这种表示概念将随后根据通过图灵测试所需的非字典基础进行更好的研究。
考虑到这些争论,系统回答的第一个公开问题,即在哪里设置系统的边界,已经失去了一些最初的兴趣:如果我们现在准备好接受任何东西作为理解的痕迹——这并不意味着接受一切,将图灵测试作为一个沉默的看门狗——我们可以在我们想要的任何地方理想地设置边界(如塞尔所指出的,在执行者之外)。无论哪个系统通过了图灵测试,都包含了一段理解,即一组规则(或者,等价地,一个计算机程序)或者编写它的程序员(如果有的话),或者他研究过的书,或者写它的作者…
如果这最后一段看起来不寻常或有点大胆,反而在日常理解和理解的方法中很常见。正如海斯指出的那样:“我读了一本小说,结果陷入了对生命本质的深刻思考,然后决定放弃一切去出家,是小说让我做出了这个决定吗?”从某种意义上来说,这部小说通过了一个非常轻量级的图灵测试,因此,我没有理由认为写它的人不明白他写了什么(可能是遍历同一文本的副本金字塔)。
无论哪个系统通过了图灵测试,都包含了一段理解,即一组规则(或者,等价地,一个计算机程序)或者编写它的程序员(如果有的话),或者他研究过的书,或者写它的作者…
理解不在书中(被认为是印刷的信件、论文等)而是在系统中的想法现在更有说服力了,系统边界的概念看起来更熟悉了。
图灵测试的充分性
塞尔的论文和这篇论文中得出的许多结论都是基于这样一个假设,即图灵测试是一个有效的测试,至少可以推测理解的存在。在他最初的工作中,图灵在避免智力的精确理论定义的方向上和在随后几年的研究的操作方向的需要上提出了这个测试。尽管多年来对智力定义的自愿和有利的逃避引起了一些反对意见,但毫无疑问,利用一种类似于日常生活中使用的行为方法使 TT 变得几乎无懈可击,而无需进入上面指出的同样危险的他人思维话题。因此,图灵不可区分性的概念,不仅是我们在评估智能表现的任务中所要注意的一个起点,而且,以一种循环的方式,也是我们对理解进行推断的唯一实际方法。
区分这两者是很有用的:对智能行为的研究可能非常依赖于领域,当考虑到它的原始版本时,可能会导致测试的一些关键和病理问题;承认这一点,丝毫不会影响我们的研究方向。我们并不是在寻找表现(不管用什么方法来评估它们,也不管在什么方法上可以进行许多其他的讨论),而是在某种意义上,寻找取得这些表现的原因。
我们不是在寻找表现,而是寻找为什么会取得这些表现的原因。
这些概念只能通过它们的性质来操作;攻击图灵测试是对概念的歪曲,是对其适用范围的攻击。图灵测试表明,它不仅足以完成这项任务,而且在我们正在研究的概念被赋予新的意义之前,它也是我们发现它们踪迹的唯一可能性。
“推断思维的唯一可用基础是图灵不可区分的表现能力”。(哈尔纳德,1992 年)
后果和结论
对这些观点的一些可能的批评已经在帖子里提到了,主要涉及到接受这样一种观点,即物理的无生命物体可以表现出理解的痕迹。正如在前面的段落中已经指出的,这种潜力必须总是在图灵测试的框架和在适当的硬件环境中执行所使用的形式主义的能力下被看到。与塞尔部分一致的是,这种归因确实部分地受到“在人工制品中我们扩展我们自己的意向性的事实”的偏见,但是将我们的意向性扩展到足够大的程度以通过图灵测试的能力不是微不足道的,它是整个推理的核心。
将我们的意向性扩展到足够大的程度以通过图灵测试的能力不是微不足道的,它是整个推理的核心。
对这些立场的另一个可能的批评可能与机器学习(ML)领域的最新发现有关。自 20 世纪 50 年代以来,人工智能的研究方向几乎完全致力于专家系统,这些专家系统具有许多领域特定的属性,用于开发高效和有效的启发式算法来解决复杂的搜索问题。近年来,可用计算能力的提高使得人们得以重温一些自 60 年代以来就藏在阁楼上的老方法,这些方法是图灵在他的论文(“儿童计划与教育过程”)中一段巧妙而有远见的话勾勒出来的。简化概念来说,机器学习是涉及使用来自问题本身的数据来自动研究复杂问题的解决方案的领域。这两种范式的关键区别不仅在于表现,还在于用来解决任务的信息来源。如果在启发式的开发过程中,需要人类理解特定问题的能力来允许机器有效地解决它,那么 ML 根本不需要它,它能够从数据中提取相关信息,而无需专门编程来解决考试中的问题。如果前面段落中关于理解及其在系统中的存在的所有主张不会受到能够通过图灵测试的假设专家系统的哲学影响,那么将一些理解附加到能够在没有任何人类干预的情况下完成这一挑战的机器学习系统将是奇怪的。房间里的大象,不仅在这个特定的小生境中,而且在所有关于 ML 的主流讨论中,都在要求使用巨大的标注语料库来喂养这些系统。解决问题的 ML 方法是非常宝贵的,并且每天都变得越来越令人惊讶,但是,从某种意义上说,在这个过程中的人类干预现在比过去使用的方法更重要。如果明天早上一个使用最先进的深度学习技术设计的聊天机器人能够通过图灵测试,那将归功于成千上万标记的人类之间的真实对话,这些对话用相互理解的语言编写,并使用特别的算法学习。理解仍然存在于系统中。
如果明天早上一个使用最先进的深度学习技术设计的聊天机器人能够通过图灵测试,那将归功于成千上万标记的人类之间的真实对话,这些对话用相互理解的语言编写,并使用特别的算法学习。
根据所有的讨论,通过图灵测试的一个必要和充分条件可以被重新表述为管理一种语言并以人类不可区分的方式谈论通过它执行的任务的结果的能力。事实上,人类自己能够通过学习来完成这项任务,这可能意味着机器学习作为一个通用框架,是设计可执行计算机程序的正确框架,可以在未来几年通过图灵测试。这里一个可能的公开问题,但在某种意义上比这篇文章的目标更广泛,就是是否存在一种方法让一个人通过原始版本的模仿游戏——一个男人和一个女人在一个房间里,男人试图在他的性别上欺骗询问者的经典游戏——而不存在一个学习过程,将一些基础附加到符号上,这是老师和学生能够管理的。理解作为教授的能力的存在(或者,回到 ML 的例子,选择合适的例子和学习算法的能力)可能是一个有趣的讨论方向。
编码与世界语言文学:用 Python NLTK 分析但丁的《地狱》

Photo by DR. Alexandru STAVRICĂ on Unsplash
本初级教程向您展示如何使用 Python NLTK(自然语言工具包)分析外语文本,这是一个自然语言处理平台,允许您将单词分解为词干,并确定文本中最常用的单词。
计算机科学可以连接到任何其他学科——所以如果你对编程感兴趣和梦想在意大利别墅的生活,这个教程就是为你准备的!
在本教程中,我们将通过词频来分析但丁的地狱的文本。《地狱》是一首 14 世纪的意大利诗歌,详细描述了作者虚构的地狱之旅。作者但丁由罗马诗人维吉尔带领,他带他参观了地狱的九层。
虽然这首诗已被翻译成英语,但原诗是用意大利语写的。你能预料到分析一首意大利诗歌的词频会有什么问题吗?
考虑一下罗曼语和英语中的动词变化。在英语中,你可以说“我说”和“你说”,而“说”这个词不会改变。主语(做动作的人)用单独的词“我”和“你”来表示但是在意大利语中——很像西班牙语和其他罗曼语——我不得不说parlo(我说)和parlI(你说),通过单词的结尾表示主语。
因此,如果我们分析但丁的地狱中的词的频率,那么 parlo 和 parli 会被算作同一个词吗,来自不定式 parlare ?不会,因为电脑很笨,不懂意大利语。幸运的是,有一个解决方案!
在我们分析文本之前,我们可以使用一个叫做标记化的过程来分离所有的单词。然后我们把每个单词分解成词干。这个过程将把 parlo 和 parli 转换成它们现在的词干 parl- ,允许它们被算作同一个单词。到那时,文本就可以进行分析了。
让我们开始用 Python 编程,这样我们就可以标记但丁的地狱!
Python 入门
总的来说,Python 是一种非常棒的数据分析语言,不管我们更侧重于科学还是人文学科。
要在 web 浏览器中开始使用 Python 编程,请访问 repl.it 并创建一个帐户。之后点击屏幕右下角的红色加号,做一个编码环境:

Click on the red plus sign.
从“流行”类别中选择“Python ”:

然后,您应该会看到一个空白的 Python 编辑器:

You’ll be typing in the white text area.
首先,我们需要从 NLTK 和其他包中导入和下载我们需要的所有东西。将这些导入复制并粘贴到您的 Python 编辑器中,然后阅读下面的解释。
import nltk
from nltk.corpus import stopwords
from nltk.probability import FreqDist
from nltk.stem.snowball import SnowballStemmer
from nltk.tokenize import word_tokenize
from urllib import request nltk.download('punkt')
nltk.download('stopwords')
我们的进口:
- Python 附带的自然语言工具包
- 阻止来自 NLTK 的单词。这些是常见的词,比如“a”和“the ”,我们在分析频率时会想过滤掉它们。许多语言都有停用词,包括意大利语。
- NLTK 的雪球斯特梅尔。这个工具可以让我们将每个单词缩减到它的词干,这样我们就可以准确地计算出它的频率,而不管它的语法上下文。雪球斯特梅尔用外语工作。
- 一个单词标记功能,允许我们将文本分解成单词列表进行处理。
- 一个请求函数,允许 Python 从 URL 中读取文本。
最后,我们从 NLTK 下载了一个名为“punkt”的包,它允许我们使用单词标记化功能,以及另一个名为“stopwords”的包,它允许我们访问上面提到的停用单词。
最后的文体提示:按字母顺序排列你的导入是一个好习惯。
从 URL 导入文本
将但丁的地狱的整个文本复制粘贴到一个单独的文件中真的很难(我试过了),所以我们将使用 Python 从一个网站上读取文本!
我找到了。txt 文件(通常最容易处理),所以我要做的第一件事(在我的导入下面)是将该 URL 存储在一个变量中:
url = "https://www.gutenberg.org/cache/epub/997/pg997.txt"
现在,每当我说url,电脑就会知道我指的是 gutenberg.org 的地址。
接下来,我可以使用我导入的请求方法向 gutenberg.org 的服务器发送请求,并在服务器响应时获得文本 Inferno :
response = request.urlopen(url)
服务器将交付 Inferno 文本并将其存储在response变量中,但是它的格式会比我们需要的稍微复杂一些。我们可以用下面的代码行将 Inferno 变成一个易读的文本字符串:
text = response.read().decode('utf8')
基本上,我只是告诉计算机使用 UTF-8(一种常见的字符编码形式)将过于复杂的变量response翻译成简单的文本字符串。现在变量text将保存一串简单格式的文本,如下所示:
"Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura che la diritta via era smarrita..."
基本上,地狱的整篇文字都被引号包围了。在计算机科学中,我们将这种数据类型(由引号包围的一串字符/单词/短语)称为字符串。
好了,我已经得到了友好格式的文本。接下来要做什么?如果你说的是“标记文本”,你答对了!
对文本进行标记
之前,我提到过标记化会产生“要处理的单词列表”列表实际上是 Python 中的一种数据结构(类似于其他语言中的数组)。而变量看起来像这样:
favorite_food = "tiramisu"
列表如下所示:
grocery_list = ["pasta", "tomato sauce", "pesto", "gelato"]
你可以看到,虽然一个变量只包含一个东西,但一个列表包含许多东西,这使得它对于杂货列表和书籍中的单词列表非常有用。当我们标记出 Inferno 时,它会变成这样:
"Nel mezzo del cammin di nostra vita..."
变成这样:
["Nel", "mezzo", "del", "cammin", "di", "nostra", "vita"]
编写令牌化的源代码可能很复杂,但是幸运的是,NLTK 为我们提供了这个功能!要将 Inferno 的原始文本转换成令牌,请编写这行代码:
tokens = word_tokenize(text)
现在,变量tokens应该包含一个单词列表。如果您想看看这个列表是什么样子(或者至少是它的底部,因为 repl.it 只能打印这么多),您可以像这样打印标记:
print(tokens)

The tokens!
计算机可能需要一点时间来生成令牌。在你等待电脑的时候,沉浸在你的成就中——电脑通常工作得很快,无论何时你让它们变慢,你都知道你正在做复杂、重要的事情(或者不小心写了一个无限循环)。
作为一种捷径,我喜欢只打印令牌列表的长度,也就是说,列表中的单词数。您可以使用len函数来计算列表的长度:
print(len(tokens))
你应该有 45274 个令牌(或单词)!
如果您打印了令牌,您会注意到还包括了标点符号。什么?!

Some of the tokens are just commas and accents!
幸运的是,我们可以用下面的代码行来摆脱这种疯狂:
tokens = [token for token in tokens if token not in '.,:;<>!?[]()`"\'']
哇哦。这是怎么回事?!基本上,这是一个很酷的编程策略,我说:“将tokens变量设置为与之前相同的东西(之前存在的所有标记),但过滤掉任何标记,实际上只是我明确列出的任何标点符号!”
如果再次打印令牌的长度,现在应该有 34,836 个令牌。
你可能会注意到,在这些标记中,我们有一些基本的单词,比如“la”(the)和“e”(and)。对于分析词频来说,这些基本单词对我们来说一点也不有趣——谁在乎但丁是否说了 1000 次“the ”?!
这些基本词在 NLTK 里叫做停用词,我们可以过滤掉。
过滤掉停用词
现在我们准备去掉停用词,那些我们不在乎的超级常用词。首先,让我们将停用词存储在一个变量中:
stop_words = stopwords.words("italian")
现在,我们可以做一些有趣的编程工作来从我们的列表中过滤出这些步骤。下面是我们如何将tokens变量重置为没有停用词的单词列表:
tokens = [token for token in tokens if token not in stop_words]
基本上,我说:“让tokens变量等于所有和以前一样的记号,除了stop_words列表中的单词/记号!”
为了再次检查是否所有讨厌的停用词都被删除了,我们可以打印出记号——以及记号的长度,以确保它与以前不同!
print(tokens)
print(len(tokens))
这个列表现在应该有 21781 个单词,比我们之前的 34k 左右的单词少了很多。
我们还可以看到,我们的新令牌列表很高兴地没有填充词和标点符号:

不错!
词干
现在我们准备把我们的话砍进他们的茎!首先,我们需要创建一个词干分析器。您可以像这样创建一个意大利词干分析器:
stemmer = SnowballStemmer("italian")
如果您使用的是不同的语言,请注意,您可以用这行代码打印雪球斯特梅尔处理的所有语言:
print(" ".join(SnowballStemmer.languages))
现在,我们可以用更巧妙的编程方法将所有单词转换成词干:
stems = [stemmer.stem(word) for word in tokens]
上面的代码行基本上解释为,“对于tokens列表中的每个单词(或标记),将其转换为词干,并将这个新的词干列表存储在一个名为stems的变量中。”
您也可以用这行代码打印词干:
print(stems)

现在我们已经得到了茎,我们准备进行一些频率分析!
计算词频
现在我们可以尝试基于词频来分析但丁的地狱了!我将这一部分命名为“计算词频”,但计算机将是唯一一台进行数学运算的计算机——我们将只编写几行简洁的代码。这就是 NLTK 的妙处!
要在变量中存储频率分布,我们只需说:
fdist = FreqDist(stems)
在这一点上,一些程序员可能会做数据可视化,但如果我没有弄错的话,repl.it 没有这个功能——如果您想探索数据,您必须将 Python 和适当的包下载到您的计算机上。
但是,我们绝对可以打印出数值!有了这行代码,我可以打印出但丁的地狱中最常见的 100 个单词:
print(fdist.most_common(100))
酷,最常用单词列表!

看词频可以得出什么结论?或者说,检查这些频率引发了哪些问题?
我们可能会注意到,单词 piu (更多),被使用了 181 次,而词干 tant- (更多),被使用了 80 次。这些话表明地狱是一个极端的地方。
我们可能还会注意到词干 l'altr- (另一个)被使用了 94 次,这可能会导致对但丁的地狱中二元性的研究。
我们可以检查最常见的动词,它们与看和说有关,以及出现 50 次的单词 occhi (眼睛)。这些话表明了但丁在冥界的消极态度。
假设你是意大利文学的研究生。你还能想到什么?

Photo by Willian West on Unsplash
进一步阅读
编程是面向所有人的,包括人文主义者!查看下面的参考资料,了解关于 NLTK 和 Python 数据分析的更多信息。
- DataCamp ,学习自然语言处理和其他 Python 数据分析的绝佳资源
- 编程历史学家,一个数字人文教程的伟大资源
- 编码&英语文学:Python 中的自然语言处理,关于分析英语文本的教程
- 如何使用 NLTK Porter &雪球测径器
- 在 Python 中使用 NLTK 删除停用词
R 中的编码:嵌套并映射到高效的代码

Photo by Olivia Colacicco on Unsplash
嵌套并映射到高效代码
我在 2019 年 3 月写了我的第一行 R 代码,但是最近才发现nest和map的威力。在此之前,我写了很多效率低下的代码(C & P,C & P,C&P……)。
这是如何使用nest和map找到 184 个国家在 1960 年到 2016 年间使用gapminder数据集(内置于dslabs)的平均人口的简短演示。gapminder拥有 184 个国家从 1960 年到 2016 年每年的健康和收入数据。数据以整齐的格式呈现,这意味着每个国家有 57 行数据(每年一行)。
library(dslab
glimpse(gapminder)
`## Observations: 10,545
Variables: 9
$ country Albania, Algeria, Angola, Antigua and Barbuda, …
$ year 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960,…
$ infant_mortality 115.40, 148.20, 208.00, NA, 59.87, NA, NA, 20.3…
$ life_expectancy 62.87, 47.50, 35.98, 62.97, 65.39, 66.86, 65.66…
$ fertility 6.19, 7.65, 7.32, 4.43, 3.11, 4.55, 4.82, 3.45,…
$ population 1636054, 11124892, 5270844, 54681, 20619075, 18…
$ gdp NA, 13828152297, NA, NA, 108322326649, NA, NA, …
$ continent Europe, Africa, Africa, Americas, Americas, Asi…
$ region Southern Europe, Northern Africa, Middle Africa…`
嵌套并不可怕,你的数据也没有消失
nest仅在通过group_by进行操作时有效。在嵌套数据之前,必须对数据进行分组。通过country对gapminder进行分组,然后调用nest,结果如下:
gapminder_nest <- gapminder %>%
group_by(country) %>%
nest()head(gapminder_nest)## # A tibble: 6 x 2
## # Groups: country [185]
## country data
## <fct> <list<df[,8]>>
## 1 Albania [57 × 8]
## 2 Algeria [57 × 8]
## 3 Angola [57 × 8]
## 4 Antigua and Barbuda [57 × 8]
## 5 Argentina [57 × 8]
## 6 Armenia [57 × 8]
我第一次用nest的时候,就被这个吓坏了。我不是来自编码或 IT 背景,看到<list<df[,8]>>足以让我删除代码,并恢复到剪切和粘贴来完成工作。
但实际上并不可怕。在这个新创建的名为data的列中,你看到的是每个国家整齐排列的健康和收入数据。可以通过调用gapminder_nest$data[[x]]来访问它,其中x是行。所以要打电话给澳大利亚(我来自的地方),代码是gapminder_nest$data[[8]]。
gapminder_nest$data[[8]]## # A tibble: 57 x 8
## year infant_mortality life_expectancy fertility population gdp
## <int> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1960 20.3 70.9 3.45 10292328 9.67e10
## 2 1961 20 71.1 3.55 10494911 9.90e10
## 3 1962 19.5 70.9 3.43 10691220 1.00e11
## 4 1963 19.2 71.0 3.34 10892700 1.07e11
## 5 1964 18.8 70.6 3.15 11114995 1.14e11
## 6 1965 18.6 71.0 2.97 11368011 1.21e11
## 7 1966 18.3 70.8 2.89 11657281 1.24e11
## 8 1967 18.3 71.1 2.85 11975795 1.31e11
## 9 1968 18.2 70.7 2.89 12305530 1.38e11
## 10 1969 18.1 71.1 2.89 12621240 1.48e11
## # … with 47 more rows, and 2 more variables: continent <fct>, region <fct>
绘制你的巢穴地图
map允许您对数据框中的所有列迭代函数。语法是:map(.x, .f),其中.x是要迭代的数据帧,.f是函数。嵌套数据的一个小问题是在数据框中有数据框。在gapminder_nest$data列中还有列数据。****
因此,我们需要将gapminder_nest$data用作.x,而不是在map调用中使用gapminder_nest作为.x。这意味着map将迭代每个国家的嵌套数据框。
让我们使用嵌套数据和map找出 1960 年至 2016 年间每个国家的平均人口。将.x设置为gapminder_nest$data。这个功能不是简单的mean。如果你只是写代码,你的代码会抛出一个错误。记住,列中还有更多列data。我们想要平均人口,所以.f的代码是~mean(.x$population)。简单地说,这段代码表示迭代所有嵌套的国家数据集,计算每个国家人口列的平均值。不要忘记.f代码中的波浪号~!
(在下面的代码中,我设置了na.rm = T以避免数据缺失的国家/年份出现任何错误)
pop_mean <- map(.x = gapminder_nest$data, .f = ~mean(.x$population, na.rm = T))
head(pop_mean)## [[1]]
## [1] 2708629
##
## [[2]]
## [1] 24231378
##
## [[3]]
## [1] 11909433
##
## [[4]]
## [1] 71053.36
##
## [[5]]
## [1] 31638376
##
## [[6]]
## [1] 2925011
Mutate 是 nest 和 map 的秘方
然而,这个输出是非常无用的,因为我们不知道国家名称是什么。一个更简洁的解决方案是mutate现有的gapminder_nest并添加一个名为pop_mean的列。
gapminder_nest <- gapminder_nest %>%
mutate(pop_mean = map(.x = data, .f = ~mean(.x$population, na.rm = T)))head(gapminder_nest)## # A tibble: 6 x 3
## # Groups: country [185]
## country data pop_mean
## <fct> <list<df[,8]>> <list>
## 1 Albania [57 × 8] <dbl [1]>
## 2 Algeria [57 × 8] <dbl [1]>
## 3 Angola [57 × 8] <dbl [1]>
## 4 Antigua and Barbuda [57 × 8] <dbl [1]>
## 5 Argentina [57 × 8] <dbl [1]>
## 6 Armenia [57 × 8] <dbl [1]>
但这还是不对。我们可以看到国名,但看不到平均人口。这是因为map总是返回一个列表,所以平均人口在gapminder_nest的嵌套列表中。
渴望成功
要查看平均人口,只需在您希望可见的列上调用unnest,在本例中为pop_mean。
gapminder_nest %>%
unnest(pop_mean)## # A tibble: 185 x 3
## # Groups: country [185]
## country data pop_mean
## <fct> <list<df[,8]>> <dbl>
## 1 Albania [57 × 8] 2708629.
## 2 Algeria [57 × 8] 24231378.
## 3 Angola [57 × 8] 11909433.
## 4 Antigua and Barbuda [57 × 8] 71053.
## 5 Argentina [57 × 8] 31638376.
## 6 Armenia [57 × 8] 2925011.
## 7 Aruba [57 × 8] 74148.
## 8 Australia [57 × 8] 16601155.
## 9 Austria [57 × 8] 7800180.
## 10 Azerbaijan [57 × 8] 6897604.
## # … with 175 more rows
瞧啊。为了好玩,这里有一张平均人口最小的 10 个国家的图表。(滚动到代码末尾)

TLDR;
结合nest和map创建高效的代码。然而,对于新手来说,有一些陷阱需要避免:
- 你不能不先打电话给
group_by就打nest。如果不这样做,代码就不知道嵌套基于哪个变量。 - 你的数据没有消失!要访问嵌套数据框中的任何内容,请调用
nested_df$data[[x]],其中x是您想要查看的行。 - 嵌套数据框中的列有自己的列。是俄罗斯娃娃。
map允许您使用函数迭代数据框的列。对于嵌套数据框,列中有列,因此可以编写.x = nested_df$data和.f = ~some_function(.x$nested_column_inside_data_column)。- 不要忘记你的地图函数中的波浪符号
~!不要担心为什么(那是另一个帖子的内容),去做就是了。
绘图代码
gapminder_plot <- gapminder_nest %>%
unnest(pop_mean) %>%
select(country, pop_mean) %>%
ungroup() %>%
top_n(pop_mean, n = -10) %>%
mutate(pop_mean = pop_mean/10^3)gapminder_plot %>%
ggplot(aes(x = reorder(country, pop_mean), y = pop_mean)) +
geom_point(colour = "#FF6699", size = 5) +
geom_segment(aes(xend = country, yend = 0), colour = "#FF6699") +
geom_text(aes(label = round(pop_mean, 0)), hjust = -1) +
theme_minimal() +
labs(title = "Countries with smallest mean population from 1960 to 2016",
subtitle = "(thousands)",
x = "",
y = "") +
theme(legend.position = "none",
axis.text.x = element_blank(),
plot.title = element_text(size = 14, face = "bold"),
panel.grid.major.y = element_blank()) +
coord_flip() +
scale_y_continuous(limits = c(0, 150))
用 R: Pivot 轻松编码
pivot _ longer 和 pivot _ wider 快速指南
我不能gather或者spread。我有某种精神障碍,记不清哪个是哪个。我几乎总是用错,以一些奇怪的笔记本结束,沮丧地关上我的笔记本电脑。所以,感谢 R 神们最近发布的pivot_wider和pivot_longer。我发现它们更加直观,现在可以轻松旋转。
转动一些松鼠

Photo by Mathew Schwartz on Unsplash
为了展示 tidyverse 的新枢纽功能,我使用了来自 R4DS 在线学习社区 2019 年 10 月 29 日#TidyTuesday 挑战赛的 2019 年纽约松鼠普查数据集。
library(tidyverse)
# Load NYC Squirrel Census data set
squirrel <- read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2019/2019-10-29/nyc_squirrels.csv")
我对松鼠的活动感兴趣,特别是哪种颜色的松鼠最友好。有用的变量有approaches、indifferent和runs_from。这些是逻辑的,所以把它们转换成数字。也让primary_fur_color成为一个因素。
squirrel_tidy <- squirrel %>%
select(primary_fur_color, approaches, indifferent, runs_from) %>%
mutate(primary_fur_color = fct_explicit_na(primary_fur_color, "Unknown")) %>%
mutate_if(is_logical, ~as.numeric(.))
为了找出每种颜色的松鼠接近、冷漠或逃离松鼠观察者的百分比,我用group_by和summarise_if搭配mean。
friendly <- squirrel_tidy %>%
group_by(primary_fur_color) %>%
summarise_if(is.numeric, ~mean(.))## # A tibble: 4 x 4
## primary_fur_color approaches indifferent runs_from
## <fct> <dbl> <dbl> <dbl>
## 1 Black 0.0583 0.427 0.311
## 2 Cinnamon 0.112 0.462 0.222
## 3 Gray 0.0510 0.493 0.223
## 4 Unknown 0.0364 0.182 0.145
将宽数据透视为长格式
现在数据是宽格式的,可以转换为长格式了。使用pivot_longer将活动列(approaches、indifferent和runs_from)移动到一列,称为activity。这些列中的值将被移动到一个名为pct_activity的列中。
friendly_long <- friendly %>%
pivot_longer(cols = c("approaches", "indifferent", "runs_from"),
names_to = "activity",
values_to = "pct_activity")## # A tibble: 5 x 3
## primary_fur_color activity pct_activity
## <fct> <chr> <dbl>
## 1 Black approaches 0.0583
## 2 Black indifferent 0.427
## 3 Black runs_from 0.311
## 4 Cinnamon approaches 0.112
## 5 Cinnamon indifferent 0.462
瞧啊。整洁的数据,非常适合用ggplot绘图。
逆转它
要从长格式转换到宽格式,可能因为你要做一些建模,所以需要专栏中的特性,使用pivot_wider。
friendly_wide <- friendly_long %>%
pivot_wider(names_from = "activity",
values_from = "pct_activity")## # A tibble: 4 x 4
## primary_fur_color approaches indifferent runs_from
## <fct> <dbl> <dbl> <dbl>
## 1 Black 0.0583 0.427 0.311
## 2 Cinnamon 0.112 0.462 0.222
## 3 Gray 0.0510 0.493 0.223
## 4 Unknown 0.0364 0.182 0.145
但是哪种颜色的松鼠最友好呢?
由activity刻面的pct_activity对primary_fur_color的绘图显示,肉桂松鼠最有可能接近(滚动到底部查看代码)。

TLDR;
pivot_longer将多列转换成两列,创建一个整洁的数据格式。pivot_wider则相反。
pivot_longer需要四个输入:
data。cols =选中的列要变成两列。names_to =新创建的列的名称,其中观察值将是您选择的所有列的名称。values_to =包含所有值的新创建列的名称
pivot_wider需要三个输入:
data。names_from =列名谁的观察值将成为新的列名。values_from =列名谁的观测值将成为新的列观测值。
绘图代码
squirrel_plot_theme <- theme_minimal() +
theme(axis.title.x = element_text(size = 10),
axis.title.y = element_text(size = 10),
plot.title = element_text(size = 14, face = "bold", family = "mono"),
plot.subtitle = element_text(size = 10, face = "bold", family = "mono"),
plot.caption = element_text(size = 10, face = "italic", hjust = 0))# Create caption to correctly source all plots
squirrel_caption <- "Data from Squirrel Census 2019"# Create colour palette
squirrel_pal <- wes_palette("IsleofDogs1", 6, type = "continuous")friendly_plot <- friendly_long %>%
group_by(primary_fur_color) %>%
ggplot()+
geom_col(aes(x = activity, y = pct_activity, fill = primary_fur_color)) +
facet_wrap(~primary_fur_color) +
squirrel_plot_theme +
scale_fill_manual(values = c(squirrel_pal[4], squirrel_pal[2],
squirrel_pal[6], squirrel_pal[3]), name = "") +
scale_y_continuous(labels = percent_format(accuracy = 1), limits = c(-0.05, 0.5)) +
theme(axis.text.x = element_text(angle = 0),
panel.grid.minor = element_blank(),
legend.position = "none") +
labs(title = "Cinnamon squirrels are the friendliest",
subtitle = "Percentage of squirrels reported as approaching, being indifferent to or running from their squirrel spotter\n",
x = "",
y = "",
caption = squirrel_caption)
编码面试问题-油藏取样
储层采样是一种从数据流中采样元素的算法。假设您有一个非常大的数据元素流,例如:
- 6 月对 DuckDuckGo 搜索的查询
- 圣诞节期间在塞恩斯伯里购买的产品
- 白皮书指南中的姓名。
让我用这些简单的词语想象一下下面的“约会”游戏节目。参赛选手是一名单身女子,她坐在一张空椅子旁。主持人介绍第一个求婚者;未婚女子不得不邀请他和她坐在一起,成为她目前的“约会对象”。接下来,主持人介绍第二位求婚者。现在女孩可以选择是继续她现在的“约会对象”还是用新的追求者取代他。她可以使用各种手段来做出决定,比如提问或者让两个追求者以某种方式竞争。之后,主持人介绍第三个追求者,女孩可以再次选择保留或替换她现在的“约会对象”以这种方式展示了 n 个追求者后,游戏节目结束,女孩与她在最后保留的追求者,即节目的“赢家”进行真正的约会。
想象一下,一个参赛者仅仅通过抛硬币来决定是否交换她现在的“约会对象”。这对追求者“公平”吗,也就是说,获胜者的概率分布在所有追求者中是一致的吗?答案是否定的,因为最后几个追求者比最初几个追求者更有可能获胜。第一个求婚者是最不幸的,因为如果他想和女孩约会,他必须通过 n- 1 次抛硬币。最后一个求婚者的机会最大——他只需要赢得一次抛硬币的机会
import random
def reservoir_sampling(iterator, k):
result = []
n = 0
for item in iterator:
n = n + 1
if len(result) < k:
print(result)
result.append(item)
else:
j = int(random.random() * n)
if j < k:
result[j] = item
print('else:', result)
return result
if __name__ == "__main__":
stream = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
k = 5
print(reservoir_sampling(stream, k))
干杯,
扎伊德·阿里萨·阿尔马利基
在 Udemy 上用 python 查看我们的免费课程 AWS。
感谢阅读。如果你喜欢这篇文章,请点击下面的按钮,这样我们就可以保持联系。
将 Python 脚本变成漂亮的 ML 工具
介绍专为 ML 工程师打造的应用框架 Streamlit

Coding a semantic search engine with real-time neural-net inference in 300 lines of Python.
根据我的经验,每一个重要的机器学习项目最终都是由充满 bug 和不可维护的内部工具拼接而成的。这些工具通常是 Jupyter 笔记本和 Flask 应用程序的拼凑物,难以部署,需要对客户端-服务器架构进行推理,并且不能与 Tensorflow GPU 会话等机器学习结构很好地集成。
我首先在卡内基梅隆大学看到这一点,然后在伯克利,Google X,最后在 Zoox 建造自主机器人。这些工具通常是作为小 Jupyter 笔记本诞生的:传感器校准工具、模拟比较应用程序、激光雷达对准应用程序、场景回放工具等等。
随着工具变得越来越重要,项目经理开始介入。进程萌芽。需求开花了。这些单独的项目孕育成脚本,并发展成瘦长的维护噩梦。

The machine learning engineers’ ad-hoc app building flow.
当一个工具变得至关重要时,我们召集工具团队。他们写出了流畅的 Vue 和 React。他们在笔记本电脑上贴上了关于声明性框架的标签。他们有一个设计流程:

The tools team’s clean-slate app building flow.
太棒了。但是这些工具都需要新的功能,比如周刊。工具团队正在支持另外十个项目。他们会说,“我们会在两个月后再次更新你的工具。”
所以我们回到了构建自己的工具,部署 Flask 应用程序,编写 HTML、CSS 和 JavaScript,并试图对从笔记本到样式表的一切进行版本控制。所以我的 Google X 老朋友 Thiago Teixeira 和我开始思考下面这个问题:如果我们能让构建工具像编写 Python 脚本一样简单会怎么样?
我们希望机器学习工程师能够创建漂亮的应用程序,而不需要工具团队。这些内部工具应该是 ML 工作流的自然副产品。编写这样的工具应该感觉像是在 Jupyter 中训练一个神经网络或者执行一个特别的分析!同时,我们希望保留强大应用框架的所有灵活性。我们想创造漂亮的、高性能的工具,让工程师们炫耀。基本上,我们想要这个:

The Streamlit app building flow.
有了一个包括来自优步、Twitter、Stitch Fix 和 Dropbox 的工程师在内的令人惊叹的 beta 社区,我们花了一年时间创建了 Streamlit ,这是一个面向 ML 工程师的完全免费和开源的应用框架。随着每个原型的出现,Streamlit 的核心原则变得更加简单和纯粹。它们是:
第一:拥抱 Python 脚本。 Streamlit 应用实际上只是自上而下运行的脚本。没有隐藏状态。你可以用函数调用来分解你的代码。如果你知道如何编写 Python 脚本,你可以编写 Streamlit 应用程序。例如,这是您在屏幕上书写的方式:
import streamlit as stst.write('Hello, world!')

Nice to meet you.
#2:将小部件视为变量。Streamlit 中没有的回调!每次交互只是从头到尾重新运行脚本。这种方法会产生真正干净的代码:
import streamlit as stx = st.slider('x')
st.write(x, 'squared is', x * x)

An interactive Streamlit app in three lines of code.
#3:复用数据和计算。如果下载大量数据或执行复杂计算会怎样?关键是跨运行安全地重用信息。Streamlit 引入了一个缓存原语,其行为类似于一个持久的、默认不变的数据存储,使 Streamlit 应用程序能够安全、轻松地重用信息。例如,这段代码只从 Udacity 自动驾驶汽车项目下载一次数据,从而生成一个简单、快速的应用程序:
Using st.cache to persist data across Streamlit runs. To run this code, please follow these instructions.

The output of running the st.cache example above.
简而言之,Streamlit 是这样工作的:
- 对于每个用户交互,整个脚本都是从头开始运行的。
- Streamlit 给每个变量分配一个给定小部件状态的最新值。
- 缓存允许 Streamlit 跳过冗余的数据获取和计算。
或者在图片中:

User events trigger Streamlit to rerun the script from scratch. Only the cache persists across runs.
如果这听起来很有趣,你现在就可以试试!只需运行:
$ pip install --upgrade streamlit
$ streamlit hello **You can now view your Streamlit app in your browser.** **Local URL:** [http://localhost:8501](http://localhost:8501)
**Network URL:** [http://10.0.1.29:8501](http://10.0.1.29:8501)
这将自动弹出一个指向您的本地 Streamlit 应用程序的 web 浏览器。如果没有,就点击链接。

To see more examples like this fractal animation, run streamlit hello from the command line.
好的。你从玩分形回来了吗?这些都很迷人。
这些想法的简单性并不妨碍您使用 Streamlit 创建极其丰富和有用的应用程序。在 Zoox 和 Google X 工作期间,我目睹了自动驾驶汽车项目膨胀到数十亿字节的视觉数据,这些数据需要搜索和理解,包括在图像上运行模型以比较性能。我见过的每个自动驾驶汽车项目最终都有整个团队在开发这种工具。
在 Streamlit 中构建这样一个工具很容易。这个 Streamlit 演示可以让你在整个 Udacity 自动驾驶汽车照片数据集中执行语义搜索,可视化人类注释的地面真相标签,从应用程序内实时运行完整的神经网络(****)[1]。

This 300-line Streamlit demo combines semantic visual search with interactive neural net inference.
整个 app 是一个完全独立的 300 行 Python 脚本,大部分是机器学习代码。其实整个 app 只有23 个 Streamlit 调用。你现在就可以自己运行了!
$ pip install --upgrade streamlit opencv-python
$ streamlit run
[https://raw.githubusercontent.com/streamlit/demo-self-driving/master/app.py](https://raw.githubusercontent.com/streamlit/demo-self-driving/master/app.py)
当我们与机器学习团队在他们自己的项目上合作时,我们逐渐意识到这些简单的想法产生了许多重要的好处:
****Streamlit 应用是纯 Python 文件。这样你就可以通过 Streamlit 使用你最喜欢的编辑器和调试器了。

My favorite layout for writing Streamlit apps has VSCode on the left and Chrome on the right.
纯 Python 脚本与 Git 和其他源代码控制软件无缝协作,包括提交、拉请求、发布和注释。因为 Streamlit 的底层语言是纯 Python,所以您可以免费获得这些令人惊叹的协作工具的所有好处🎉。

Because Streamlit apps are just Python scripts, you can easily version control them with Git.
Streamlit 提供了一个即时模式的实时编码环境。只需点击当 Streamlit 检测到源文件改变时,总是重新运行。

Click “Always rerun” to enable live coding.
****缓存简化了计算管道的设置。令人惊讶的是,链接缓存函数会自动创建高效的计算管道!考虑这段代码改编自我们的 Udacity 演示:
A simple computation pipeline in Streamlit. To run this code, please follow these instructions.
基本上管道就是 load_metadata → create_summary。每次运行脚本时 Streamlit 只重新计算获得正确答案所需的管道子集。酷!

To make apps performant, Streamlit only recomputes whatever is necessary to update the UI.
Streamlit 是为 GPU 打造的。 Streamlit 允许直接访问机器级原语,如 TensorFlow 和 PyTorch,并补充这些库。例如在这个演示中,Streamlit 的缓存存储了整个 NVIDIA 名人脸甘【2】。当用户更新滑块时,这种方法可以实现几乎即时的推断。

This Streamlit app demonstrates NVIDIA celebrity face GAN [2] model using Shaobo Guan’s TL-GAN [3].
Streamlit 是一个免费的开源库,而不是专有的网络应用。您可以在内部提供 Streamlit 应用程序,无需联系我们。您甚至可以在没有互联网连接的笔记本电脑上本地运行 Streamlit!此外,现有项目可以逐步采用 Streamlit。

Several ways incrementally adopt Streamlit. (Icons courtesy of fullvector / Freepik.)
这只是您可以用 Streamlit 做的事情的皮毛。Streamlit 最令人兴奋的一个方面是如何将这些原语轻松组合成看起来像脚本的复杂应用程序。关于我们的架构如何工作以及我们计划的功能,我们还有很多可以说的,但我们将把这些留到以后的帖子中。

Block diagram of Streamlit’s components. More coming soon!
我们很高兴今天终于与社区分享了 Streamlit,并看到你们都用它构建了什么。我们希望你会发现把你的 Python 脚本变成漂亮的 ML 应用程序是一件简单而愉快的事情。
感谢 Amanda Kelly、Thiago Teixeira、TC Ricks、Seth Weidman、Regan Carey、Beverly Treuille、Geneviève Wachtell 和 Barney Pell 对本文的有益贡献。
参考文献:
[1] J .雷德蒙和 a .法尔哈迪,约洛夫 3:一种增量改进 (2018),arXiv。
[2] T. Karras、T. Aila、S. Laine 和 J. Lehtinen,为提高质量、稳定性和变化而逐步种植甘蔗 (2018),ICLR。
[3] S. Guan,使用新型 TL-GAN 模型控制图像合成和编辑 (2018),洞察数据科学博客。
咖啡数据表
咖啡数据科学
我想要的圣诞礼物就是数据
那是 2018 年的圣诞节,我想要的只是一杯来自 la Pavoni 的浓咖啡。我很少使用这台机器,我遇到了麻烦。在阅读了关于预热的内容后,我决定需要收集我拍摄的数据,以确保我对常规的改变有所帮助。我一点也不知道,数据表将成为最有用的工具,使惊人的浓缩咖啡,并扩大到改善我的咖啡烘焙。

我没有开始跟踪我的镜头,因为我认为这是一些伟大的想法。这更多的是一种好奇心,它变成了我用来改进我的浓缩咖啡的最伟大的工具:数据。一个有趣的回顾目标是能够回顾历史数据,因为有些时候,直到数据可用时,价值才完全为人所知。
诚实是数据手册的关键。没有诚信,史料毫无意义。没有诚实,就不可能改进你的浓缩咖啡。有时,我想打得更高,因为我认为应该更高,或者我在考虑平均得分,但我真的想在我的评估中保持诚实,以便随着时间的推移,我可以打出我一生中最好的投篮。

最终,随着时间的推移,关键目标演变为以下五项:
- 镜头改进
- 充分利用每台浓缩咖啡机
- 豆子实验
- 方法调整中的未来未知发现
- 腭校准
输入变量
输入变量应该是最可控的,并且在各次拍摄中保持一致。咖啡重量最容易用秤控制。
你也可以测量水温,93 摄氏度被广泛认为是最佳温度。我做过一个关于预热云台的水温的小研究,但是我没有具体测量过每次拍摄的温度。这是一个可以改进的领域,但我的大多数机器,我知道在它们过热之前什么时候停止射击。
其他输入变量是咖啡烘焙、日期、时间、每层重量(对于断奏),我还添加了一些其他可选的比特,如添加的罗布斯塔或水库的水位。
过程变量
我已经记录了预输注、开花和提取的时间,但是一旦我调整了我的方法,我经常不计时。我记录了我的许多镜头,以帮助回顾镜头是如何从无底的 portafilter 开始的。这一点数据对我改善咖啡分配、调整研磨以及平衡断奏层次也很重要。
我有几个单元格用来记笔记。我改变了什么细微的变化,或者一些我没有全时间跟踪的变量。最后,还有一个下次做什么的单元格。大多数时候,我能记得下一步要做什么,但仅仅是记录这些笔记的过程就帮助提高了我的记忆能力。一般来说,在课堂上、工作中或家里记笔记对我帮助很大。
输出变量
有两种类型的输出变量:定量和定性。对于定量指标,我测量输出重量并计算咖啡中的咖啡渣。有一段时间,我校准了我的咖啡杯,测量了可溶物的百分比,但是我有一段时间没有这样做了。我也拍过相当多的照片,我本可以尝试使用图像处理分析来调查色差,但我没有。

对于定性指标,我衡量七个品质:尖锐,丰富,糖浆,甜,酸,苦,回味。如果我是一个 Q 级学生,我可以使用这些标准,但是,唉,我不是。前三个测量值需要解释,因为其他四个是不言自明的。
尖锐是指当浓缩咖啡第一次触及你的舌头时的尖锐度,以及它对你的口味有多震撼。我不喜欢因为喝了这么烈的东西而咳嗽,但这是我喜欢的尖锐的标志。这个标准的一个问题是,一个平滑的镜头不会很锐利,但这并不意味着它是一个糟糕的镜头。因此,这让我开始审视我所使用的衡量标准的变化,甚至是通过 Q-gradier 认证。
丰富是指镜头整体的丰富和复杂。我更喜欢 1:1 的比例(输入重量与输出浓缩咖啡的重量),因为我想重温我喝的第一杯浓缩咖啡。我的第一枪尝起来就像在嘴里融化了一块巧克力;它完全占据了我的舌头。
糖浆指的是质地,我的目标是我记忆中的第一次拍摄。浓咖啡在我的舌头上感觉很浓,像糖浆或融化的巧克力。
当我查看 Q 等级的时候,我发现了一些和我相似的描述。请记住,虽然 Q 级不是在浓缩咖啡上完成的。这是一张与我的 Q 等级最接近的表格。

最后,我得出一个平均分,它包含这七个指标,并将它们与投入产出比的加权值相结合。我考虑过在计算中去掉这个比例,因为我用了一个上面有测量标记的杯子。这个杯子让我每次都能打到 0.8 到 1.1 之间的比率。

校准
我从一个小规模(0,1,2)开始。我不知道我能在多大程度上区分不同的级别,我想我会从小做起,然后根据需要扩展。最初,我开始用 Hario 研磨机给照片评分,因为我在圣诞假期。当我回到家里,使用我的 Rok grinder 和 Kim Express,我必须迅速将我的规模从 0 扩大到 4。
然后是断奏。老实说,我没想到一个断奏的镜头会做得这么好。当我开始尝试时,我开始在某些类别中看到 5 和 6。随着我对各层如何相互作用有了更好的理解,我能够让每个分数类别上升。我从两层迁移到三层,我看到平均分从 4 到 7。
最近,我用一些我一直在调整的专业烘焙达到了 9 分甚至更高。用于烘烤的数据需要进一步讨论。
时不时地,我会在咖啡店,喝着劣质咖啡,或者在家里用我的 superautomatic 检查我的校准。虽然糟糕的击球并不总是有趣的,但它肯定了我的底线,并让我看到那些击球做得好的地方。
在意大利的时候,我拍了一些照片,我真的注意到那些照片中我喜欢的东西是我的照片中没有的。我确定我错过了使用 robusta 的机会,这促使我尝试调整 Robusta 的百分比,以获得我想要的平衡。意大利酒有更好的丰富性、酸味和回味,但它们不甜。它们是苦的,但没有烧焦。有区别…

Coffee Shops have the roast highlighted in Red. Noticed how the score patterns are quite different.
多台机器
这个数据收集的有趣部分是在多台机器上工作。我在工作中有一个金快车,这是我的底线。我用那台机器拍出了最好的照片,因为我已经用了五年了。根据过去六个月的数据,我在六个月内打了 200 针,所以一年 400 针乘以五年就是 2000 针。
我买了一个意大利的 Enrico,我把它放在我工作的实验室里。这是一台好机器,但我用得不多了。那台机器的事故率比 Kim 高得多(30 分之一对 2000 分之二)。
在家里,我有一个奥利奥 Giro,Flair,La Peppina 和一个新收购的 Kim Express。我买了新的 Kim Express,因为它需要很多爱,我想在工作和家里有同样的体验。
我在家里用 Flair 拍摄的照片比其他任何机器都多。我几乎放弃了这台机器,因为我遇到了一些麻烦,但我最终找到了一些技巧,特别是对于断奏镜头,这使它能够产生与 Kim 一样的镜头。
数据
我们来看一些数据。首先,让我们来看一段时间内的镜头对比。通常,潮起潮落取决于一颗咖啡豆。

查看数据的另一种方式是每台机器每次烘烤的最高得分或射击次数。它展示了我如何使用每台机器以及我的照片如何改进它们的有趣旅程。

在更高的水平上,我似乎每天要喝 2 到 4 杯,每次烧烤我要喝接近 20 杯。一份烤肉通常是 350 克到 300 克,所以每份 15 克意味着大约 20 份。

随着时间的推移,烘焙的平均分数会随着咖啡豆的老化而变化。我尤其能从一些一开始就含有大量克莉玛的豆子中分辨出来,这些克莉玛随后逐渐减少。这是另一种方法来观察每次烘烤的次数以及理解每次烘烤的次数。

我甚至可以看烤后的照片。我们可以通过每次烘焙将这些分数标准化,然后将它们结合起来,以更深入地了解何时是煮浓缩咖啡的最佳时间。烘焙后两周似乎是一个很好的峰值,但我没有足够的烘焙后数据来了解这种味道何时开始变味。

在我的断奏实验中,我得到了大量的数据。下面是我截取数据的几种方法,来展示断奏是如何比常规击球提高击球质量的。我已经控制了烘烤和机器。

1. Green (Staccato > Regular) , Red (Regular > Staccato). 2. Scatter Plot with red-line to show no difference
我可以把更多的注意力放在两台机器上:Kim Express 和 Flair Espresso。就解决方案而言,它似乎在一定程度上提高了提取率。我开始注意到缺乏相关性,我怀疑萃取可溶物并不是浓缩咖啡质量分级的全部指标。

While percent solubles is an interesting metric, it doesn’t predict taste (i.e. Score) well.
上帝开枪了
我曾经认为我可以实现某种“神射”一个如此惊人的镜头,会毁了所有其他镜头。我已经打了很多次这种球,以至于我认为这只是一个梦,一个我会对我的球的味道感到满意的幻想。我没有吹牛的意思;我的意思是说,我已经达到了一些我以前没有达到的浓缩咖啡的水平,然后就变得正常了。
根据我的数据,在 604 次投篮中,我有 40 次是之前所有投篮中最好的,占 6.6%。也就是平均每 18 次注射,或者大约每 4 天注射一次。我还没有完全看到一个高原。
对我来说,真正的上帝拍摄是我的第一次拍摄。你永远不能重复第一次。我甚至不知道我在做什么。我去了一家咖啡馆,点了菜单上最便宜的饮料。碰巧是浓缩咖啡,很棒。上颚知道,第一枪的锋利永远不会重复。
我的希望是,保持数据表或拍摄日志可以帮助你提高你的拍摄;至少让你看到自己喜欢什么,不喜欢什么。我的类别是我正在寻找的东西,但它们不必对你一样。我还收集额外的数据,因为你永远不知道将来什么时候会需要这些数据。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。
我的进一步阅读:
便携式浓缩咖啡:指南
克鲁夫筛:一项分析
浓缩咖啡中咖啡溶解度的初步研究
大约一年前,我开始收集我的浓缩咖啡的数据,但是我不太喜欢收集的一个指标是溶解度。我在早期进行了一些测量,但我无法证明花几百美元买一个高科技 TDS 测量工具是合理的。最后,我买了一个折射计,我开始收集一些有趣的数据。它改变了我对浓缩咖啡的看法,比我想象的要大得多。
溶解度是溶解在杯中的咖啡量,它也告诉你你从咖啡杯中提取了多少咖啡。通常,这是用 TDS 或总溶解固体来衡量的。人们可以通过知道浓缩咖啡的重量来推算出提取的咖啡量。




Typical views during espresso and collecting data.
要测量溶解度,最好的是一台数字折光仪。它们很贵。有更便宜的折射计,但是它们需要更多的努力来进行测量。我还用校准过的杯子做了试验。我拍了一个玻璃杯子慢慢装满水的视频。然后当我拍摄的时候,我会把拍摄的地方和视频进行比较。水印处的重量差就是溶解的咖啡量。这种方法是最不精确的。
我选择了一个便宜的折射仪来测量白利糖度。然后用一些等式得出有趣的数据:
- TDS = 0.85 *白利糖度/ 100
- 咖啡提取= TDS *输出压力
- 提取的咖啡百分比=提取的咖啡/输入咖啡重量
从浓缩咖啡的常识来看,一杯好的咖啡会提取出 18%到 22%的咖啡。一颗咖啡豆大约有 30%是可溶的,你不会想要所有能溶解的东西。同样的豆子,不同的冲泡,无论好坏,味道都会不一样。这就是为什么许多人煮浓缩咖啡时非常小心地控制研磨粒度、温度、压力、提取次数和提取量。
然而,那里的信息只使用单点数据,即在拍摄结束时测量的 TDS。典型地,该镜头是 3:1 的镜头。作为一名数据科学家,我想超越这一点。我想知道镜头里发生了什么。
我决定进行两个初步实验:
- 在提取过程中在多个点测量 TDS,以观察其如何发展。
- 对于我拉的每一杆,定期测量 1:1 和 3:1 杆的 TDS。我称之为第一部分(1:1)和测量第二部分,如果与第一部分相结合,将给出一个 3:1 镜头。
实验 1:整个拍摄过程中的可解性
如何收集溶解度随时间的变化?一个冰块托盘或许多杯子。我最初的几次尝试是用冰块托盘。困难在于测量每个立方体中液体的重量。


所以我做了一些实验。前两次尝试很有趣;我看了看镜头最后提取的咖啡总量。
第二个镜头有点呛,但超过一半的咖啡提取发生在镜头的第一个 1:1 体积。

让我们再试一次。利用金快递和 7 cups,我重新收集了数据。

结果告诉我们同样的故事,大多数提取发生在最短的位。你能打出的最短的 ristretto 球。ristretto 镜头通常被定义为 1:1 的比例和 2:1 的比例之间。

实验 2:多次拍摄的可解性
仔细看看镜头的第一和第二部分,很明显大部分提取都在第一部分。第一部分是 1:1 输出,第二部分是第一次拍摄后发生的 2:1 输出。将第一部分和第二部分组合起来,得到一个接近 3:1 比例的镜头(被认为是短镜头)。

为了进一步探索这个概念,我绘制了上个月的 78 张照片。我绘制了断奏镜头和常规镜头,尽管我画的大多是断奏。我把第一部分和最后的摘录放在同一个图上。虽然每次拍摄只有两个数据点,但是数据的趋势很明显。然后我画出了镜头第一部分最终提取的百分比。

The Red circles are regular shots, and the green and blue ones are staccato shots.
人们可能希望考虑溶解度随时间的变化。为了了解这一点,我在 Kim Express 上使用了一个或多或少保持一致的断续镜头,观察了一次烘焙(2019 年 11 月 9 日)的溶解度和我的主观口味评分。结果是不确定的,但是似乎咖啡的质量并没有随着时间的推移而下降(10/18 到 11/8 或 3 周)。

至于主观分数,让我们看看最终的主观分数是否会随着提取咖啡的百分比而变化。这似乎是松散相关的,我的经验是,在温度或压力方面提取的微小变化可以提供相同的咖啡提取,但不同的味道(更甜或更苦)。

这些结果对我来说只是个开始。我喜欢实验,我会继续追踪溶解度。有时,很难看出跟踪多个变量将如何帮助改善我的浓缩咖啡体验,但即使是这样大量的数据也清楚地表明了一些事情:
- 提取超过 1:1 的比例可能是过度提取的咖啡,因此应该避免。如果你想要更多的液体,你最好进行 1:1 的提取,然后加入你想要的水量以获得正确的体积,而不是继续提取。
- 断续镜头的提取比常规镜头高。这些实验已经用主观味觉评分验证了我的经验。我会注意到我把第二部分和牛奶混合在一起了,但是味道不一样。有时它是有用的。
- 使用溶解度和提取的咖啡来比较浓缩咖啡的方法应该在提取过程中的多个点小心进行。单一溶解度指标可能会遗漏镜头中发生的事情。这在断奏镜头中特别有趣,它们有相似的最终提取百分比,但在 1:1 的比例点,它们的值要高得多。
- 高萃取并不意味着你有合适的口味。获得正确的风味提取可能很棘手。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。
我的进一步阅读:
克鲁夫筛:一项分析
纽约的咖啡趋势
建立一个模拟器来分析枫糖浆咖啡的趋势

Photo by Mohamed Hassan
谁不喜欢枫糖浆?大多数人,其实是因为他们从来没有机会去品尝。加拿大魁北克省生产了全球 70%的枫糖浆。在我住在蒙特利尔的这段时间里,我遇到过许多外国游客,他们带着尝枫糖浆的愿望来到这里。他们不只是想要枫糖浆,他们想要他们能找到的最好的纯加拿大枫糖浆。这让我想知道加拿大以外对加拿大枫糖浆的需求会是什么样的。其他国家想要更多吗?我通读了 Reddit 上关于枫糖浆的对话,作为发展我对这个问题的直觉的第一步。几种模式很快显现出来:
- 在欧洲和澳大利亚,有许多人要么不容易获得枫糖浆,要么没有意识到他们有。尽管他们中的许多人很好奇想尝试一下。
- 大多数尝过枫糖浆的人都以积极的热情谈论它。
- 许多尝过枫糖浆的人对使用玉米糖浆的枫糖浆仿制品不以为然,比如杰迈玛阿姨和巴特沃斯夫人的。
- 如果许多美国人知道枫糖浆产自加拿大,他们会更加珍惜它。
- 大多数尝过用枫糖浆代替糖搅拌的咖啡的人对此赞不绝口。
最让我感兴趣的是最后两个观察结果。如果我是一个繁荣的、热爱咖啡的美国城市的加拿大枫糖浆咖啡潮流的幕后推手,会怎么样?纽约市完全符合这个描述,因此这个项目诞生了。
想法:蝴蝶效应
我一直在构建一个模拟器,让我能够分析纽约市咖啡店中可能出现的潜在趋势,特别是在加拿大枫糖浆咖啡的背景下。潜在的想法是,当一家咖啡店决定采用一种趋势时,它会对附近的竞争对手产生理论上的影响,让他们也这样做,这种影响会波及整个咖啡店网络。我使用概率论来模拟这个项目数据集中每个商店的趋势采用概率,并使用图论来模拟商店网络。网络可以被看作一个简单的图,其中每个商店是一个节点,两个节点之间有一条边当且仅当它们在地理上足够近。每个节点的趋势采用概率不仅取决于与其自身直接相关的特征,还取决于网络中其他节点的特征和采用概率。到目前为止,我已经完成了模拟器的第一个版本,我将在本文的最后讨论它。计划是在迭代中建立模拟器,从简单的开始,逐渐增加到决定网络如何随时间更新的条件列表中。
数据准备
我收集的数据是关于纽约的咖啡店的。为了获得这些数据,我使用了一个流行的餐馆评论网站的 API 来搜索 9 个与咖啡相关的词(“咖啡”、“咖啡馆”、“浓缩咖啡”、“拿铁”、“卡布奇诺”、“玛奇朵”、“美式咖啡”、“摩卡咖啡”、“无咖啡因咖啡”),搜索了 6 个地点(“纽约市”及其 5 个区,“斯塔滕岛”、“布鲁克林”、“曼哈顿”、“皇后区”、“布朗克斯”),总共进行了 54 次搜索。返回的数据是 JSON 格式的,所以我用 Python 构建了一些函数,允许我将它转换成我可以探索的熊猫数据帧。
探索性数据分析
咖啡店位置
当我最终在表格中准备好数据后,我首先好奇的是咖啡店是如何分布在纽约市的。通过创建一张纽约市地图,在每个商店的位置都标上一个点,我可以更好地了解网络是如何组织的,特别是,哪些区域有密集的咖啡店,哪些没有。

地图会立即显示许多不属于数据集中的点。至少满足以下三个要点之一的任何点都需要删除:
- 经度超出了纽约市的经度范围。
- 纬度超出了纽约市的纬度范围。
- 坐标在纽约市的经度和纬度范围内,但点不在纽约市本身。
移除满足前两个要点中任何一个的点相当于在谷歌地图上点击纽约市最西、最东、最南、最北点附近的地图,以估计其经度和纬度范围,然后过滤掉不在这些范围内的点。位于经度和纬度范围内但在纽约市之外的点要移除就有点棘手了。我构建了三个函数来过滤掉这些点,一个移除输入点西北方向的所有点,一个移除输入点东北方向的所有点,一个移除输入点东南方向的所有点。

All points north-west of A, north-east of B or south-east of C will be removed by the functions.
这些函数迭代地应用于具有各种输入点的数据集,这些输入点是从谷歌地图中精心选择的,不会删除来自纽约市的任何点。这产生了一个数据集,该数据集根据需要仅捕获纽约市的咖啡店。

更新后的地图显示,曼哈顿和布鲁克林北部的咖啡店密度很高。另一方面,相对而言,斯塔滕岛、皇后区和布朗克斯区似乎缺少咖啡店。在构建模拟器时了解这一点很有用;据推测,更有可能的是,一种趋势会在一个咖啡店密集的地区传播开来。
咖啡店特色
此时,数据集包含 4036 行,每行对应于纽约的一家咖啡店。我研究了三个特别感兴趣的特性,因为它们是构建模拟器的关键:
- 评论:评论数;代表评级的样本量
- 评级:以 0 到 10(含)的整数值为级别的分类
- 价格:等级分类 x 、 xx 、 xxx 、xxxx;更多的 x 意味着更贵的商店
因为评级依赖于评论,分析评论显然是单变量分析的首选。

你可能以前听过:一个公司 80%的销售额来自 20%的产品;一个学生 80%的生产力是由他或她的 20%的任务产生的;80%的犯罪是由 20%的罪犯犯下的;20%的啤酒饮用者饮用了 80%的啤酒。这就是80–20 法则,或者帕累托原理,它观察到在各种各样的因果自然现象中,绝大多数的影响(通常在 80%左右)是由一小部分原因(通常在 20%左右)产生的。在这个项目的数据集中,69%的评论是由评论数量排名前 20%的咖啡店产生的,这足以表明帕累托原则在这里正在暴露自己。每当我有一个可以被描述为自然现象的连续变量时,我喜欢进行这种验证,以此来证明我的数据集没有偏差。如果的评论不遵循指数分布,我会怀疑我的数据集对纽约所有咖啡店的代表性。
核密度估计图(直方图的连续模拟)显示大多数商店的评论少于 250 条。进一步检查评论的汇总统计数据显示,该特性的范围从 1 到 7693(表明可能有许多异常值),中位数为 86,平均值为 207。平均值远高于中值,因为评论向右倾斜。更好地熟悉这个功能是必要的,因为模拟器将以这样一种方式构建,即评论越多的商店在网络中就越有影响力。
接下来,我想分析两个分类特征,价格和评级。我决定将等级视为分类等级,因为它只有 11 个可能的整数值,可以认为是类。

在涉及价格的双变量分析中,观察到最高价格水平的商店如此之少是很重要的。这表明 xxxx 商店的样本量很小,因此我们在分析它们时应该犹豫不决,不要轻易下结论。尽管如此,这些商店对模拟器来说还是很重要的,因为它的建造方式是昂贵的商店比便宜的商店对他们的竞争对手有更大的影响。
同样值得注意的是,的评分偏左,在三个最低评分(0,1,2)中的任何一个中,都有不可见数量的商店。模拟器会将较高的影响力分配给具有较高评级的咖啡店。我将使用各种评级的相对计数来帮助校准评级对一家商店在网络中对其邻居的影响有多强。因为条形图显示高评分的商店并不少见,所以如果条形图向右倾斜,我会给这些商店分配较少的影响。
现在我对这三个特性的分布有了更好的了解,是时候成对比较这些特性了,从评级和价格开始。

单变量分析显示,不同水平的价格的样本量差异很大,因此在价格上涨时比较评级的相对计数比绝对计数更有意义。随着价格增加,等级增加。通过观察发现,最低评级商店的评级随着价格的增加而增加。此外,评分低于 7 的商店比例——也就是橙色区域占条形总面积的比例——随着价格的增加而减少。第二种模式在最高价格水平下有些失效,但这可以解释为由 xxxx 商店的小样本量引起的差异。因为更贵的商店往往获得更高的评分,他们似乎更成功,因此更愿意尝试新的趋势。这种洞察力将用于模拟器的初始化步骤,随机选择的商店采用枫糖浆趋势;更昂贵的商店将被分配更高的初始化概率。
接下来我比较了价格和评论。

Because reviews has an extreme outlier at 7693, I decided to restrict this feature for the left graph so as to avoid vertically compressing the boxplots. By imposing the restriction that reviews≤1000, the resulting boxplots are legible at the cost of only 3.3% of the data that was ultimately filtered out.
对于前三个水平,随着价格的增加,评论的第一个四分位数、中间值和第三个四分位数也增加。 xxxx 类中的商店不遵守这种模式,但同样,这种商店的样本量很小,所以我们不应该过于相信数据告诉我们的关于它们的信息。价格和评论正相关,这一点令人放心,因为已经确定它们都将积极推动模拟器中的趋势采用概率。
为了总结双变量分析,我比较了评级和评论。

散点图显示,评论随着评分的增加而增加,但是因为有太多评分为 7、8 或 9 的点不明显可见,所以我决定查阅评论的汇总统计。从表中我们可以看到,随着评级从 2 增加到 8,平均值和前三个四分位数都在增加;从 2 到 9,最大值在增加。对于评分为 9 的商店,中间值和前三个四分位数也相对较大,只是没有评分为 8 的商店那么大。
因为评级与评论正相关,而评论代表评级的样本量,评级越高的店铺,其评级越可信。了解这一点对模拟器很有用,因为它告诉我应该将较高的方差分配给与较低评级商店的影响相关联的概率分布。
在这一点上,随着评级和价格同时上涨,评论也在上涨,这并不奇怪:

Heatmaps for reviews≤1000. Grey rectangles correspond to (price, rating) pairs that do not occur in the data.
这有助于进一步巩固从价格对评论和评级对评论的双变量分析中得出的结论。
咖啡店作为一个网络
用图论对网络建模
数据集中的咖啡店网络可视为一个简单的图表。每个咖啡店是图中的一个节点,当且仅当一对节点在地理上足够接近时,这一对节点之间存在边。

This graph represents a network of 8 coffee shop nodes (in red) with 9 edges (in black). Only nearby node pairs have edges between them. The full network of 4036 coffee shops would be too large to display here.
为了构建完整网络图的边,我首先定义了一个函数,该函数使用一对点的地理坐标来计算它们之间的欧几里德距离。然后,我创建了一个函数,它将 max_distance 作为输入,并输出一个 Python 字典,其中的键是咖啡店 id,与键相关联的值是所有其他咖啡店 id 的列表,这些 id 与键的距离最多为 max_distance 。换句话说,键是节点,与键相关联的值是与键有边的所有节点的列表。

This is a sample from the dictionary when max_distance = 0.001. The sample consists of 6 keys and their corresponding neighbor lists . For example, coffee shop 4 has an edge with coffee shops 82, 3042 and 3160.
无法预先知道最大距离参数的合适选择。如果 max_distance 太小,则图中的边太少,连通性低;影响力会被低估。如果 max_distance 太大,那么图中将有太多的边和高连通性;影响力会被高估。作为找到该参数的合适值的第一步,我计算了对于不同的 max_distance 值,网络中节点间邻居的最大和平均数量。

The degree of a node is the number of other shops that are no further than max_distance away from the node. Distance scale: two nearby adjacent streets in NYC are typically a distance of 0.0007 to 0.0010 apart.
趋势在模拟中传播的速度主要取决于 avg_degree 。如果这个值太大,那么模拟将高估趋势传播的速度;更多的邻居意味着更多的外来影响。因此,我决定在版本 1 的任何未来分析中只测试最大距离 ≤ 0.004 的值,这样平均程度≤ 23,这似乎是一个合理的上限。距离 0.004 对应于通常从给定咖啡店延伸不超过 6 条纽约市街道的半径。
趋势模拟器:版本 1
在任何给定时刻,网络中的每个节点都有一个与之相关联的布尔状态,其中 True 意味着商店在那个时刻正在跟随潮流。初始状态使用二项式分布随机化,并且网络在迭代中更新,其中第一次迭代之后的每次迭代的状态由前一次迭代的状态和以下两个规则确定:
- 如果一个节点在迭代 I 为真,那么在所有迭代 j > i 为真
- 如果一个节点在迭代 I 时为假,但是在迭代 I 时至少有一个邻居为真,并且如果迭代 I 不是最后一次迭代,则对于迭代 i+1,该节点更新为真。
一次迭代可以被认为是一个时间单位,例如一个月或者一年。我构建了一个函数,以表格的形式输出模拟结果,其中行对应于节点,列对应于每次迭代中节点的布尔状态。

This is a sample of the simulation output which shows the states of shops 4012 to 4016 for the random initialization step and the first 4 iterations.
作为输入,模拟器要求:
- adj_dict :包含节点和邻居列表的字典,由 max_distance 决定(如图论部分所述)
- ****迭代次数:网络更新迭代次数
- p_x , p_xx , p_xxx , p_xxxx :与二项分布相关的状态初始化概率;四个价格级别各有一个值
- ****种子:二项式分布的随机种子
在这一点上,仍然不清楚什么是对最大距离的好选择;我只是凭直觉猜测 0.004 的上限似乎是合理的,所以我用 0.001、0.002、0.003 和 0.004 的 max_distance 值进行了实验。也不清楚 p_x 、 p_xx 、 p_xxx 和 p_xxxx 的合适选择可能是什么。为了最小化不断增长的选项的复杂性,我决定在模拟器的版本 1 中使这些初始化概率相等,但是用不同的值进行实验。我认为,在一个包含 4036 家商店的数据集中,说服 10 家商店最初采纳一个想法似乎并不太牵强。这相当于初始化概率为 10/4036,或者大约为 0.0025,所以我试验了每个 max_distance 值的初始化概率为 0.0005、0.0010、0.0015、0.0020 和 0.0025。对于每个( max_distance , init_prob )对,模拟器使用 100 个不同的随机种子运行,以生成具有这些特定参数值的模拟样本。然后,对所有种子的真商店的数量进行平均,按照最大距离、初始 _ 概率和迭代进行分组。

Each table entry is an average over 100 simulations.
极端情况:
- 当 max_distance 和 init_prob 分别最小化为 0.001 和 0.0005 时,趋势传播非常缓慢。到第四次迭代结束时,平均仍只有 16 家咖啡店采用了这一趋势,这一增长率太低,不足以被视为病毒式增长。
- 当 max_distance 和 init_prob 分别最大化到 0.004 和 0.0025 时,趋势传播非常快。在第一次网络更新后,趋势采纳者的平均数量从初始化阶段的 9 个跃升至 211 个,这一增长率似乎非常高。
本着实事求是的精神,我决定分析这两个极端之间的模拟。我已经计算出 init_prob = 0.0025 似乎是一个合理的起点,但是即使 max_distance 低至 0.002,平均模拟从 it_0 到 it_1 增长得不合理地快。因此我用 max_distance = 0.001 解决了这个问题,并生成了下面的模拟。
This is the random initialization and first ten iterations of an example simulation when init_prob=0.0025 and max_distance=0.001. The red points represent the shops in NYC that are using the trend at each iteration.
这一趋势主要始于曼哈顿和布鲁克林北部,并从那里蔓延开来。这不足为奇,因为这些地区遍布咖啡店。在推动枫糖浆咖啡潮流的过程中,一个合理的策略可能是将这个想法集中卖给这些地区一些有影响力的商店,然后看着它有机地传播。
第一个版本的主要缺点是,这种趋势不太可能在咖啡店不密集的地区蔓延。下一个版本将致力于解决这个问题。
后续步骤
在模拟器的第一个版本中,一直隐含地假设任何商店对其邻居的影响权重是固定的。在下一个版本中,我将通过使用评分、评论和价格特征来量化每对相邻商店之间的影响,从而改进这种方法。我还将包括一些特征工程变量,如 is_starbucks 和 is_starbucks_neighbor ,它们为数据集最常去的咖啡店提供特殊待遇。商店是否在随机初始化阶段之外的任何迭代中采用该趋势将由其邻居的影响以及引入到网络中以解释人的不可预测性的一些随机噪声来确定。这将产生一个更真实的模拟器,特别是允许这种趋势在曼哈顿和布鲁克林北部以外的地区传播。
****代码:这个项目的代码和模拟器的所有未来版本都可以在我的 GitHub 页面找到。
****联系人:如有任何问题、意见或建议,请随时联系 nathanburnsds@gmail.com 或 LinkedIn 。
最后,我要感谢 苏珊·霍尔科姆 在整个项目中提供的及时而全面的反馈。
加密货币命名中的认知偏差
为什么将一种加密货币命名为“NAS”而不是“XZC ”,会在交易所上市后的第一周产生熟悉感并提高购买兴趣

在畅销书思考快与慢、中,丹尼尔·卡内曼用了整整一章来探讨概念、想法、图片或文本呈现给我们的方式,以及我们理解它们所付出的努力对我们对它们的态度和情绪的影响。因此,与清晰、轮廓分明的图像或字体相比,难以看清的图像或文本字体通常与消极态度或不快有关。他称这种现象为认知放松带来的快乐。
同样,他解释说
容易发音的单词唤起一种有利的态度。名字容易发音的公司在股票发行后的第一周比其他公司表现更好,尽管这种效应会随着时间的推移而消失。具有可发音交易代码的股票(如 KAR 或 LUNMOO)表现优于那些具有绕口令代码的股票,如 PXG 或 RDO——它们似乎在一段时间内保持了一点优势。在瑞士进行的一项研究发现,投资者认为 Emmi、Swissfirst 和 Comet 等名字流利的股票比 Geberit 和 Ypsomed 等名字笨拙的股票收益更高。
这一假设背后的主要思想是,市场工具名称的可读性与我们记住它的程度有因果关系。与此同时,记住一些东西会导致熟悉感,除非记忆表达了相反的意思,否则我们经常喜欢或偏爱我们记住的东西。这反过来又会导致购买兴趣的增加,或者在有购买选择的情况下更倾向于购买资产。
可读性→记忆性→熟悉度→喜欢度→购买兴趣
Zajonc 称这种现象为单纯暴露效应。[……][他]认为重复对喜欢的影响是一个极其重要的生物学事实。为了在一个经常充满危险的世界中生存,一个有机体应该对一个新的刺激做出谨慎的反应,退缩和恐惧。对于一种不怀疑新奇事物的动物来说,生存前景是暗淡的。然而,如果刺激实际上是安全的,最初的谨慎消退也是适应性的。纯粹的暴露效应之所以会出现,是因为重复暴露于刺激之后不会有任何不良反应。这样的刺激最终会成为安全信号,安全性好。
我认为广告和营销也是从曝光效应中培养出来的,这或许可以解释“没有坏的宣传”这句话。这是,他们如何评价我们的产品并不重要,重要的是人们会谈论它,从而产生熟悉感,进而产生好感等等,直到引起更大的购买兴趣。
为了测试股票代码的可读性是否会影响对加密货币的购买兴趣,我进行了一个小型研究项目,以确定事实是否如此。你可以在文章的最后找到 Jupyter 笔记本。
数据
这个研究项目背后的主要假设是,加密货币的可读和易记的名称应该在发行或在交易所上市的第一周影响资产的价格。
在我们的案例中,我们将与在币安交易所上市的或多或少同质的一组加密货币合作,这些加密货币满足以下要求:
- 加密货币的股票名称(简称)应该是 3 个字母长。例如,瑞士联邦理工学院、TRX、ZEC…
- 加密货币应该与比特币进行交易。例如,BTC 理工学院、TRX BTC 学院等。
114 种加密货币符合这些标准。
现在我们将每种加密货币分为“可读”或“不可读”。我们将使用两条规则:
- 在一种情况下,我们将认为具有至少一个元音的 3 个字母的跑马灯的加密货币是可读的,而如果加密货币跑马灯仅由辅音组成,则是不可读的。例如,ETH 是可读的,而 ZRX 是不可读的。
- 在第二种情况下,我们将把所有中间位置有一个元音的加密货币视为可读的,而其余的则视为不可读的。例如:英美烟草公司是可读的,但 BSV 不是。
然后,我们将检索加密货币在币安交易所上市后第一周的所有 1 小时烛台。
第一个规则分类的标记:
**WITH VOWEL:**
ADA, ADX, AGI, AMB, ARK, ARN, AST, BAT, EDO, ELF, ENG, ENJ, EOS, ETC, ETH, EVX, FET, FUN, GAS, GTO, HOT, ICN, ICX, INS, KEY, LUN, MCO, MDA, MOD, NAS, NAV, NEO, OAX, OMG, ONG, ONT, OST, PAX, POA, POE, REN, REP, REQ, SKY, SUB, SYS, VEN, VET, VIA, VIB, WAN, XEM, ZEC, ZEN, ZIL
**WITHOUT VOWEL:**
BCC, BCD, BCH, BCN, BLZ, BNB, BNT, BQX, BRD, BSV, BTG, BTS, BTT, CDT, CMT, CND, CVC, DCR, DGD, DLT, DNT, GNT, GRS, GVT, GXS, HSR, KMD, KNC, LRC, LSK, LTC, MFT, MTH, MTL, NXS, PHB, PHX, PPT, QKC, QLC, QSP, RCN, RDN, RLC, RPX, RVN, SNM, SNT, TNB, TNT, TRX, WPR, WTC, XLM, XMR, XRP, XVG, XZC, ZRX
分析
为了进行分析,我们将计算每种加密货币的百分比回报,并汇总上述每种类别的所有回报。最后,我们将比较这些分布,并执行 t 检验,以统计评估这两个样本是否可以被视为来自不同的分布。理想情况下,我们预计可读密码将比不可读密码有更高的回报,这是由于所描述的纯粹暴露效应。
结果
首先让我们看看中间有/没有元音的分笔成交点的回报分布:

Figure 1. Distribution of 1-h returns for cryptocurrencies with tickers that contain a vowel in the middle vs tickers without a vowel in the middle for the first 2 days after exchange listing.
看似有一些差异,但这些差异有统计学意义吗?让我们对交易所上市后 1 天到 7 天的所有数据进行 t 检验:

Figure 2. Mean returns and t-test p-value for tickers with vowel in the middle position vs tickers without the middle vowel. The reported “n” represents the number of cryptocurrencies included in each class.
令人惊讶的是,可读类别(带中元音)的平均回报率实际上高于不可读类别。这种差异在开始时似乎更大,但似乎随着时间的推移而衰减。有趣的是,p 值都很低,在α= 0.05(5%)时有一个点具有统计学意义。
现在让我们看看元音与非元音标记分类的结果:

Figure 3. Mean returns and t-test p-value for tickers with any vowel vs tickers without any vowel. The reported “n” represents the number of cryptocurrencies included in each class.
乍一看,平均差异似乎更小,但我们至少可以看到三点具有统计意义!毕竟,该假设至少在一定程度上似乎是正确的。
带回家的信息
虽然相关性并不总是意味着因果关系,但我们可以看到,在交易所上市后的第一天,具有可读符号或股票名称的加密货币实际上显示出更高的平均回报(在 alpha=0.05 的不同点上显著)。这表明,当人们可以选择购买一项资产时(特别是在 2017 年发生的 altcoin 热潮期间,当时这里分析的大多数加密货币都已上市),人们可能会倾向于购买更多具有最可读和最难忘名称的加密货币。
Jupyter 笔记本
这个项目是我们在cryptodatum . io研究的一部分,这是一个加密货币数据 API,旨在提供即插即用的数据集来训练机器学习算法。如果你是机器学习从业者,在https://cryptodatum . io获取你的免费 API 密匙,自己玩吧
Colab 与 MLflow 的协同:如何监控进度和存储模型
深度学习中越来越多的模型依赖于 GPU 的使用。作为一名深度学习实践者,我甚至想过买一台来加速我的辅助项目的开发。但是每年发布的新型号的 GPU 使得旧的 GPU 显得有些陈旧、缓慢、不酷。酷的东西是新的云技术,允许租用一个具有上一代 GPU 的虚拟机(VM ),并在云中训练你的模型(high)。然而,从设置云虚拟机的金钱和时间来看,这可能仍然很昂贵。
如果我告诉(或提醒)了呢?)您了解解决所有这些问题的云解决方案吗?谷歌的 Colab 笔记本提供了一个长达 12 小时的免费 GPU。然而,12 小时后一切都变成了南瓜:所有存储的数据都不见了。

收敛到南瓜不是唯一的选择。为了保存训练过的模型,需要将谷歌存储或谷歌驱动与 Colab 笔记本连接。为了监控训练进度,必须使用额外的工具,如 Colab Tensorboard 。或者,MLflow 提供了存储模型和监控训练进度的解决方案。在这篇博文中,我介绍了如何在 Google Cloud 上设置 MLflow 的指南。
MLflow 存储两种类型的数据:
- 结构化数据:训练进度的度量和模型参数(浮点数和整数)
- 非结构化数据:训练工件(图像、模型等。)
我们可以将这些类型的数据存储在数据库中,也可以本地存储在虚拟机上。让我们从数据库选项开始。对于培训的指标,可以使用 SQL 或 Databricks 数据库。对于训练工件,数据库是 S3,谷歌存储或 Azure 存储。为了观察训练进度,需要部署 MLflow 服务器(管理存储数据的 GUI)并将其连接到数据库。另一种选择是在虚拟机上部署 MLflow 服务器,并将所有内容本地存储在其上。
我决定采用第一种方法,将 MLflow 服务器连接到数据库。在我看来,这种设置更加健壮,因为我们不需要依赖 MLflow 服务器,而只需要依赖云数据库。此外,数据库选项允许在您的笔记本电脑上本地部署 MLflow 服务器,这样更安全。对于云提供商,我选择了谷歌云,因为我还剩下一些免费积分:)。这是一个建筑的草图:

现在,让我们按照 5 个实际操作步骤,在本地部署服务器,该服务器将连接到 SQL 数据库以获取指标,并连接到 Google storage 以获取工件。
1。在 IAM 控件中设置服务帐户。我现在的设置不是最安全的(最后看关于安全的评论)。但是,有一件事绝对值得做,那就是在 Google Cloud 的 IAM 控件中创建一个服务 app。要创建服务 IAM,请遵循以下步骤:
IAM→服务帐户→选择项目→创建服务帐户→…
应向 app Cloud SQL Editor,Storage Object Admin 授予以下权限。创建 IAM 服务帐户的原因是,如果有人偶尔发现了您的应用凭据,最大的损害仅限于 SQL 和存储。对于到 SQL 和存储的连接,我们将使用服务 IAM 密码。要使用密码创建 JSON:
服务帐户详细信息→授予此服务帐户对项目的访问权限(选择角色)→授予用户对此服务帐户的访问权限→创建 JSON 格式的密钥并保存此文件。
现在您有了一个带有 JSON 密钥的服务应用程序。
2。创建和配置 Google SQL server。ml flow 支持的 Google cloud 中现有的解决方案是 MySQL 和 PostgreSQL。设置 PostgreSQL 要简单得多。ml flow 1 . 0 . 0 版本中的 MySQL 有几个问题,这些问题肯定会在下一个版本中得到解决。在这篇文章中,我描述了 PostgreSQL 的设置。
2a。启动 PostgreSQL server 后,使用规则 0.0.0.0/0 为您的 SQL 服务设置公共 IP(在这里,您将您的 SQL 暴露给 Internet,知道您的 SQL 数据库密码的每个人都可以访问您的 SQL)。记下您的公共 IP 号码,因为您以后会需要它。
2b。在 SQL 中创建一个表,用于存储 MLflow 中的数据。
2c。为 SQL 数据库设置用户名和密码。
3。配置 Google 存储帐户。
3a。在 Google Storage 中创建一个存储桶。
3b。将角色:“传统存储桶所有者”和“传统存储桶读者”添加到您在步骤 1 中创建的 IAM app-user。为此,请遵循:
选择桶→权限→选择服务帐户→添加相应的角色
4。启动本地 MLflow 服务器。
MLflow 提供了一个很好的 GUI,叫做 MLflow Server。您可以通过这个 GUI 管理存储在数据库中的模型。一种选择是在您的笔记本电脑上本地启动 MLflow 服务器。
4a。首先,安装 MLflow 和以下必需的软件包:
pip install google-cloud-storage mlflow psycopg2
4b。还记得步骤 1 中的 JSON 文件吗?现在我们需要它!类型:
export GOOGLE_APPLICATION_CREDENTIALS=”path/to/jsonfile”
4c。就是这个!使用以下命令启动 MLflow 服务器:
mlflow server \
--backend-store-uri 'postgresql://<username>:<password>@<sql ip>/<name of table>'\
--default-artifact-root gs://<name of bucket in gs>/
这里的‘postgresql://
之后,您的 MLflow 服务器应该可以工作了。在浏览器地址栏输入 http://127.0.0.1:5000 即可查看。您希望看到 MLflow 服务器的起始页:

MLflow Server first start.
5。使用 Colab 测试 MLflow 设置。
要测试连接,您应该首先设置您的凭据。比如,像这样:
然后用 Colab 的其他包安装 MLflow,并将一些度量和工件保存到数据库中:
全笔记本在 GitHub 有售。
请注意,我使用了所展示的设置来在可公开访问的数据集上训练模型,安全性不是主要关注的问题。有几个漏洞,如将 SQL server 暴露在互联网上,以及将密码从服务帐户直接写入 Colab 笔记本。在这种设置中,服务帐户权限的限制非常重要。设置 MLflow 的一个更安全的方法是通过 gcloud 工具建立到 Google cloud 的连接,并使用 Google Storage SQL proxy 而不将 SQL 服务暴露给互联网。要知道,简单性和安全性之间总是有取舍的:)
快乐模型管理和培训与 Colab!
协作室+驱动器+ Github ->工作流程更加简单
介绍
这篇文章是我们之前尝试充分利用两个世界的延续,即 T2 谷歌实验室和 T4 Github T5。简而言之,我们试图在典型的数据科学工作流程中映射这些工具的使用。虽然我们成功了,但是这个过程也有缺点:
- 它依赖于动态导入,这使得我们的代码变得不必要的麻烦。
- 我们没有让 Github 部分正常工作。工作空间必须脱机保存。
在本帖中,我们将向您展示一种更简单的方法来组织工作空间,而不会有这些缺陷。你需要的只是一个 Gmail 和 Github 账户。我们开始工作吧。
什么去哪里?

Figure 1. Three parts of our simple “ecosystem”.
通常,我们的工作区中有四种基本的文件类别:
- 笔记本(。ipynb) —对于交互式开发工作,
- 库(。py) —对于我们使用和重用的代码,
- 模型——我们试图建造的东西,
- 数据——我们构建数据的原料。
由于 Colab 后端不是持久的,我们需要一个永久的存储解决方案。除此之外,我们还需要一个版本控制系统,这样我们就可以跟踪变化。最后,如果我们不需要考虑这个机器,我们会很感激。
Colab 很容易与 Google Drive 集成,这使它成为存储空间的自然选择。我们将使用它来存储我们的数据和模型。同时,Github 更适合代码,因此我们将把它用于笔记本和库。现在,问题出现了,我们如何从笔记本的位置将两者接口,这将使我们的工作流尽可能地无痛?
开源代码库
我们假设您已经有了一个 Github 帐户,并为您的项目创建了一个存储库。除非您的存储库是 public ,否则您将需要生成一个 令牌 来通过命令行与之交互。这里有一个简短的指南告诉你如何创建一个。
Google Drive
接下来的事情是为模型和数据组织我们的非易失性存储空间。如果你有一个 Gmail 账户,你就成功了一半。你需要做的就是在驱动器中创建一个空目录,就这样。
合作实验室——操作笔记本
为了让事情井井有条,我们定义了一个单独的笔记本作为我们的操作工具。我们将专门使用它的单元来操纵我们的空间,让其他笔记本来处理更有趣的事情,如探索性数据分析、特征工程或培训。所有笔记本,包括这个,都将被修改,但是命令存储在操作笔记本中。
工作流程
工作流程是一个简单的三步流程:
- 首先,在连接到 Colab 运行时之后,我们需要挂载 Google Drive 并且使用 Github 更新我们的空间。
- 我们使用笔记本和其余的文件(我们的模块、库等)。).在这种背景下,我们干脆称之为剪辑。
- 我们保存我们的工作,通过使用操作笔记本将我们的驱动器与 Github 同步。
连接、安装和更新
from google.colab import drive
from os.path import join
ROOT = '/content/drive' # default for the drive
PROJ = 'My Drive/...' # path to your project on Drive
GIT_USERNAME = "OlegZero13" # replace with yours
GIT_TOKEN = "XXX" # definitely replace with yours
GIT_REPOSITORY = "yyy" # ...nah
drive.mount(ROOT) # we mount the drive at /content/drive
PROJECT_PATH = join(ROOT, PROJ)
!mkdir "{PROJECT_PATH}"I # in case we haven't created it already
GIT_PATH = "https://{GIT_TOKEN}@github.com/{GIT_USERNAME}/{GIT_REPOSITORY}.git"
!mkdir ./temp
!git clone "{GIT_PATH}"
!mv ./temp/* "{PROJECT_PATH}"
!rm -rf ./temp
!rsync -aP --exclude=data/ "{PROJECT_PATH}"/* ./
上面的代码片段在/content/drive安装 Google Drive 并创建我们项目的目录。然后,它从 Github 中取出所有文件,并将它们复制到那个目录中。最后,它收集属于驱动器目录的所有内容,并将其复制到我们的本地运行时。
这个解决方案的一个好处是,如果多次执行,它不会崩溃。无论何时执行,它只会更新新的东西。此外,使用rsync,我们可以选择排除一些内容,这可能需要很长时间才能复制(...数据?).
编辑、编辑和编辑
开发,尤其是在数据科学中,意味着在我们最终把事情做好之前要尝试多次。在此阶段,可以通过以下方式编辑外部文件/库:
- 替换或更改驱动器上的文件,然后使用
rsync将它们传输到每个笔记本的本地运行时,或者 - 使用所谓的 IPython 魔法命令。
假设您想要快速更改somefile.py,这是您的库文件之一。您可以为该文件编写代码,并使用%%writefile命令告诉 Colab 保存它。由于文件驻留在本地,您可以简单地使用import语句再次加载它的新内容。唯一的事情是记住首先执行%reload_ext somefile命令,以确保 Colab 知道更新。
这里有一个例子:

Figure 2. Importing, editing and importing again. All done through the cells.
储蓄,收工
一旦你想备份你所有的工作,你需要做的就是把所有的文件复制到存储器里,然后推送到 Github。
可以使用笔记本单元中执行的!cp -r ./* "{PROJECT_PATH}"进行复制,这将更新驱动器存储。然后,推送到 Github 需要创建一个临时工作目录和配置本地 git repo 只是暂时的。下面是要执行的命令:
!mkdir ./temp
!git clone "https://{GIT_TOKEN}@github.com/{GIT_USERNAME}/{GIT_REPOSITORY}.git" ./temp
!rsync -aP --exclude=data/ "{PROJECT_PATH}"/* ./temp
%cd ./temp
!git add .
!git commit -m '"{GIT_COMMIT_MESSAGE}"'
!git config --global user.email "{GIT_EMAIL}"
!git config --global user.name "{GIT_NAME}"
!git push origin "{GIT_BRANCH_NAME}"
%cd /content
!rm -rf ./temp
显然,您需要自己定义"{...}"中的字符串。

Figure 3. Successful upload of the content to Github. Calling it a day.
结论
在这篇文章中,我们展示了如何在使用 Google Colab 时有效地将 Google Drive 和 Github 结合起来。改进后的工作流程比之前介绍的流程简单得多。
如果你想分享任何有用的技巧或提出一些改进建议,请在评论中提出。你的反馈很有帮助。
还会有更多…
我计划把文章带到下一个层次,并提供简短的视频教程。
如果您想获得关于视频和未来文章的更新,订阅我的 简讯 。你也可以通过填写表格让我知道你的期望。回头见!
冷启动能量预测

大约三个月前,我参加了施耐德电气在 DrivenData 平台举办的“ Power Laws:冷启动能量预测”比赛。目的是根据以前的用电量和其他因素(如温度、节假日信息等)来预测几栋建筑的用电量。挑战的一个有趣部分是几个家庭(2 天或更少)可用的少量消费信息,因此,我们的模型必须很好地概括新建筑。
结果,我在 1300 名参与者中名列第四。这篇文章将描述我的解决方案和这次比赛的主要收获。

A final leaderboard of the competition
任务、数据和指标
预测建筑的全球能耗在建筑运营中发挥着关键作用。它为设施管理人员和楼宇自动化系统提供了初步检查,以标记预期和实际能源使用之间的任何差异。设施管理者、公用事业公司和建筑调试项目也使用准确的能耗预测来实施节能政策并优化冷却器、锅炉和能量存储系统的操作。
通常,预测算法使用历史信息来计算他们的预测。大多数时候,历史数据集越大,预测就越准确。这项挑战的目标是构建一种算法,从构建仪器的一开始就提供准确的预测。
组织者提供了训练数据集,该数据集由 758 栋建筑组成,这些建筑在 672 小时内具有已知的电力消耗(总共 509 376 个数据点)。此外,我们还有关于建筑类型(基于表面面积)、节假日和温度的信息。如果一栋楼是一套公寓,周六周日大概是节假日;如果一个建筑是一个购物中心,它在周末工作,没有假期。为了让自己熟悉这些数据,你可以查看组织者的 EDA 。
测试数据集由 625 栋新建筑组成,这些建筑不在训练数据集中。我们有过去 24-372 小时内“测试”建筑的消耗信息。预测的三个时间范围是不同的。每栋建筑的目标是:
- 预测一天中每小时的消耗量(24 次预测);
- 预测一周内每天的消耗量(7 次预测);
- 预测两周内每周的消耗量(2 次预测)。

The illustration for the train/test split and what we had to predict
竞争指标类似于 MAPE — NMAE ,使得每个预测(每小时、每天或每周)同等重要(以下指标包含ci内真实值的平均值):

The competition metric
最终解决方案
在这次比赛中,我测试了很多想法,但大多数想法都没有给我的分数带来任何显著的提高。然而,要讲述我尝试过的每一件事需要太长的时间;因此,在这一部分,我决定只关注我的最终解决方案的主要部分。
数据预处理
数据预处理包括三个主要步骤:填充缺失值、删除常量值和数据缩放。首先,我用给定 Id 和一个小时的小时平均值来填充缺失的温度值。在此之后,缺失值的数量从 45%下降到 2%。剩下的 2%缺失值我用给定小时的月平均温度来填充。
其次,我用一个不变的目标清洗了所有的时间序列。在训练数据集中有几个例子,在这些例子中,我们有很长一段时间消耗不变。这些示例看起来像是最初丢失的,组织者决定用中值来填充它们(常量值接近中值,但不精确)。如果我保持这些值,我的预测会有偏差;因此,我删除了所有的数据点,我们观察到在过去的 6 个小时里有一个持续的消耗。
我认为,我的解决方案中最重要的部分之一是独立地衡量每栋建筑的能耗。当您对线性回归的神经网络进行预处理时,一种常见的方法是将所有训练数据用于目标缩放:

由于度量标准的原因,本次比赛中没有出现这种情况。我用给定建筑的最小和最大值来标准化每栋建筑的值。它允许比较不同电力消耗水平的建筑物;因此,我的模型开始更好地概括。
我做了一个玩具例子来说明这个想法。据推测,我们有两座建筑,如下图所示。他们有相似的消费性质(1 号地块),唯一的区别是消费水平。如果我们用所有可用的数据(第二个图)缩放目标,图像不会改变太多。我们的预测只基于一个特征——一天中的一个小时。它将具有较高的偏差(事实上,这种情况下的最佳预测将是给定小时的平均消耗量)。但是,如果我们独立缩放每个时间序列,它们将看起来相同(第三个图),我们的模型将更容易发现潜在的模式。

Toy example: X — an hour of the day, y — consumption
在规范化之前,我还尝试了不同的目标预处理策略。在最近的机器学习比赛中(在 IDAO 2018 中神奇的 1/3 度),在训练数据中存在离群值的情况下,它显示出了显著的改善。然而,他们都没有给我一个接近建筑物 Id 内简单的最小-最大缩放的分数:
- 1/2、1/3 或 1/4 度的目标+最小-最大标度或均值-标准差标度;
- 目标+最小-最大缩放或均值-标准差缩放的对数。
验证策略
我使用维持验证集(20%)来检查我的模型的分数,并使用 10 重验证(建筑物 Id 分层)来优化管道的超参数。我注意到这场比赛中的大多数时间序列是静止的,时间序列分裂和洗牌的分数几乎是一样的。因此,我决定使用混洗 10 倍,而不是常见的时间序列分裂,因为它允许我在事后使用不同的分裂,这将使我的预测更加稳定。
现在,我对时间序列任务使用一个简单的规则:如果时间序列是静态的,那么使用 KFold 进行超参数优化是可以的(但是不能用于整个流水线的评估)。

Examples of the training data. It is clearly seen that properties of time series don’t change too much over time, there is no trend in the data, so we might use KFold instead of time-series split for validation
特征工程
我为特性工程使用了不同的策略,但我认为最重要的是:
- 基于时间戳的特征:年、年中的某一天、年中的某一月、年中的某一周、周中的某一天。这些特征在 NN 中被视为分类特征,我还通过正弦-余弦变换将它们添加为数值特征(见下面的等式)。这是一种众所周知的处理循环特征的方法。这种方法背后的思想很简单:我们希望在我们的模型中归纳出关于过程的先验信息,即周期的结束是新周期的开始。因此,应用此映射后,00:00 和 23:00 之间的距离将变得更小。我推荐查看这些帖子看代码示例:编码深度学习的循环特征,特征工程——处理循环特征;

- 我把温度从摄氏温度转换成了开尔文温度。然后,我添加了一个小时的温度,下一个小时的温度,这一个小时和下一个小时的温度之间的绝对和相对差异,下一个小时和这一个小时的温度之间的绝对和相对差异,作为新的特征。;
- 基于建筑类型的特征:最大和最小消耗的记录、建筑表面类别、休息日、建筑工作制度(
is_{weekday}_dayoff组合在单一类别中)。
根据竞赛规则,禁止使用附加数据。但是,不管怎样,因为好奇,我测试了下面这个想法。我使用一个开放的温度观测数据库来确定建筑位置。我们知道给定建筑每小时的温度,这就是为什么我在数据库中搜索相似的温度模式。关于温度观测值(实际值和台站观测值之间的绝对距离之和)与给定建筑物的温度最接近的台站的信息(纬度、经度、国家、最近的城市)被用作附加特征。然而,它降低了验证和排行榜的分数。我认为这是因为温度数据中有很多缺失值。我放弃了这个伟大的想法。
模型
我使用前馈神经网络作为最终模型,其架构如下所示。我对所有分类特征都使用了嵌入层。这是神经网络中处理类别的常用方法。嵌入将相似的类别彼此更紧密地映射在一起(参见这篇论文的温和介绍)。在这次比赛中,这是非常有益的,因为数据集中有很多建筑。

我用以下参数训练 NN:100 个时期,批量大小 1024,Adam 优化器,3 个时期用于提前停止。我在 Keras 测试了训练 NN 的不同损失。我有一种感觉,MAPE 和 MSE 的损失可以给我不同的高分预测,这样我就可以把它们融合在一起,获得更高的分数。但是这些方法还不够好,不足以将它们包含在最终的管道中。
我还尝试了众所周知的梯度增强方法(XGBoost 和 LightGBM ),这些方法是在原始数据和 NN 之后的数据表示(几个时期的训练之后 NN 内层的输出)上训练的,但是它们不能显示出像样的结果。这些模型的分数甚至不足以包含在最终的集合中。
我最终提交的只是 30 个模型的简单平均值(10 倍* 3 个略有不同的架构)。赌注并没有提高分数太多。
决赛成绩
大部分比赛时间,我都是抱着和第二名差距巨大的第一名。但是在过去的两天里,来自第二名和第四名的小伙子们组队了,一点小小的变动就让一切都不同了。

Shake up be like
但是,无论如何,我花了 1.5 个月解决了一个有趣的任务,这帮助我组织了一个类似任务和比赛的管道。最终解决方案的一部分用于 Junction 2018,在那里我们赢得了智能云赛道(ODS)的“信号英雄挑战赛。AI 团队)。
协同过滤来“预测”药物的疗效(2)
另一个案例研究和对领域知识的一些思考
在本系列的第一篇博文中,我展示了使用协同过滤来预测药物与其靶标之间的相互作用强度的结果。在这个续集中,我将尝试处理另一个数据集,并讨论领域知识在生物化学中的重要性。
该数据集来自 T2 在一份著名杂志上发表的论文。这个数据集后来被另一个小组用来开发一种“预测”药物和激酶相互作用强度的方法(见 Cichonska 论文)。还记得第一篇帖子开头提到的四种场景吗?该图是从 Cichonska paper 复制的,并在下面复制,再次作为我们的起点。

Reference: Cichonska paper
我们处理场景(a)仅仅是因为协同过滤的基本限制。然而,cihon ska paper 试图做的不止这些。他们的诀窍是依靠药物和蛋白质的内在分子特性来进行预测。虽然分子的性质是由其分子结构决定的,但确切的关系还远未确定。事实上,Cichonska 论文的作者不得不筛选相当多的方法来描述一种分子,以便找到一种可以提供最佳预测的方法。下图总结了他们的努力。

他们试图找到药物和激酶的最佳模型。每行对应一个药物模型,每列对应一个蛋白质模型。每个单元格中的数字是预测和测量结果之间的皮尔逊相关系数,其平方相当于两者之间线性拟合的 R。通常,药物模型的想法与激酶模型有很大的不同。理解所有这些描述药物和蛋白质的不同方式需要大量的领域知识,因此评估所有可能的组合不是一件容易的事。在不深入细节的情况下,我们寻找那些具有较高 r 值或红细胞的细胞。对于称为生物活性插补的问题(上图左侧),这是前面提到的四种情况中的第一种,最佳药物模型称为 KD-GIP。令人惊讶的是,这是唯一不基于分子结构或任何其他内在性质的模型;相反,它是基于测量的药物和蛋白质之间的相互作用强度。对于激酶,较好的型号是 KP-SW 或 KP-SW+。它们是基于蛋白质的结构。另一个蛋白质模型 KP-GIP 也表现相当好,这不是基于结构。因此,事实证明,他们还没有发现一种药物或蛋白质的内在模型比基于测量数据的经验模型好得多。
KP-GIP 和 KD-GIP 是协同模式吗?答案是否定的。它们基于内核技巧。可能需要一整本书来描述这个内核技巧。在我们的特定模型中,我们需要知道所考虑的药物和激酶之间的所有相互作用强度,包括那些尚未测量的,以实现这个核心技巧。但这不正是模型最初应该帮助我们的吗?为了让这些模型起作用,作者必须“猜测”缺失的相互作用值。如第一篇博文所示,协同过滤不需要对插补问题进行任何猜测。
对于称为“新药”的问题或者开头提到的四个场景中的第二个,最好的模型是药物的 KD-sp 和蛋白质的 KP-GS。由于手头问题的“冷启动”性质,它们必须是内在模型,所以作者甚至没有奢侈去尝试 KP-GIP 或 KD-GIP。有趣的是,KD-sp 和 KP-GS 不是插补问题中的最佳内在模型。换句话说,本质上相同问题的不同味道——预测相互作用强度——需要使用不同的模型。如果同一个模型对两种情况都是最好的,那就更让人放心了。
公平地说,论文中评估的许多内在模型都有类似的表现。r 值的差异只体现在第二或第三位小数上。我个人认为 r 值相差 0.01 在实践中并不显著。但是,追求 r 值非常小的提升,至少在机器学习比赛中还是非常重要的。r 值的小幅增加可能会导致排行榜大幅上升。更重要的是,这激励人们寻找新的技巧,从根本上把他们带到一个全新的水平。
现在让我们看看来自 Cichonska 论文的一些结果。

在模型预测和测量结果之间有明显的相关性,但是我们也看到了相当多的异常点。特别地,绘制了 Ki 的负对数(10 为底)。在这里,需要一些领域知识来理解 pKi。Ki,粗略地说,就是药物“有效”所需的浓度,它的范围从微摩尔到纳摩尔。人们通常寻找对其靶激酶具有较低 Ki 而对其他非靶激酶具有较高 Ki 的药物。取其对数并翻转其符号后,我们得到的 pKi 范围为~5 到~9。pKi 越高,药物对目标的作用越强。注意 pKi 不应该有单位。我在本系列的第一个帖子中没有提到为什么取对数(自然对数,但那并不重要),现在是时候讨论一下以显示领域知识的意义了。我们想要处理对数(pKi)而不是 Ki 至少有三个原因:
- Ki 的跨度很大。由于模型是通过最小化其预测误差来优化或训练的,所以对纳摩尔范围 Ki 和微摩尔范围 Ki 中的误差给予相同的权重是没有意义的。使用对数意味着我们试图最小化 Ki 的相对误差,消除绝对幅度引起的偏差。
- 当药物与激酶结合时,Ki 的对数与自由能变化成正比。自由能变化是药物激酶复合物形成的基本驱动力。
- 研究人员明白,他们需要研究一个跨越多个数量级的变量。当他们设计实验来测量 Ki 时,通常会进行一系列稀释。换句话说,他们在相差一个常数因子的几个浓度下进行实验,比如 0.1、1、10 和 100 纳摩尔。有些人会称这些浓度为“对数”单位。换句话说,10 和 100 的区别不是 90 而是 1 log。
如上图所示,pKi 的测量值和计算值之间存在显著的相关性。是否足够准确?我没有答案,但请记住,对数单位中 1 的差异是 10 倍的差异。最终我们想知道药物的有效浓度范围,因此 pKi 或对数单位的小误差将意味着浓度的大误差。理想情况下,药物不应该只在非常窄的浓度范围内起作用,因此我们在浓度测量中可能会有一些公差。为了回答这个预测是否足够好的问题,我们需要更多的领域知识。
这是他们在调查了相当多的内在模型和一个基于测量结果的模型后得到的结果。正确实现该模型需要大量的领域知识。如果我们使用协同过滤呢?正如本系列的第一篇博文所示,不需要任何领域知识。基于 fastai 的相同方法用于该数据集,结果如下所示。

用约 93k 的药物激酶 pKi 值训练协同模型后,用于预测约 10k 的 pKi 值,并与实验结果进行比较。这种相关性是显著的。
将潜在因素转换为三个主成分,并在缩减的 PCA 空间中分析激酶的邻居,下表总结了它们之间的接近程度。

比如 CLK4 和 DYRK3 就差不多。事实上,它们都是双特异性激酶。 MAP4K4 和 CDK7 分别是由有丝分裂原和细胞周期蛋白激活的激酶,都参与细胞分裂过程。当然,在对蛋白质的生物学功能没有更深入了解的情况下,这种讨论仍然是肤浅的。询问以前未知的激酶之间的关系是否可以通过分析大量的相互作用数据来揭示仍然是有趣的。我们只了解细胞中生化反应网络的一小部分,协同过滤揭示的新关系可能是未知水的指针。
这里有一个自然的问题,我们可以从哪里得到这些数据集。正如我在上一篇文章的结尾提到的,有很多工具,包括计算的和实验的,用于评估药物激酶的相互作用。没有适用于所有蛋白质和药物的通用测量方法。当人们进行实验时,条件是为适应他们独特的环境和目的而定制的。一些具有机器学习背景的人跳到这个领域,却发现现有的数据很难协调。数据监管和清理需要生物学和机器学习方面的专业知识,即使这是可行的。事实上,他们中的一些人决定重新收集数据,以便用于模型训练。总之,这个领域需要一个 ImageNet 。正如 ImageNet 有助于开发可以击败人类专家的图像识别算法一样,“激酶网络”应该早于具有真正预测能力的药物激酶相互作用模型。
最后一点,学习率是模型正常工作最重要的参数,最佳学习率大约是 5e-3。这与第一篇文章的结论基本相同,证实了杰里米对模型稳健性的信念。

对那些感兴趣的人来说,Jupyter 笔记本可以在这里找到。请注意,这项工作是初步的。
协同过滤来“预测”药物的疗效(1)
无领域知识学习的案例研究。
动机
在药物开发中,筛选是早期发现过程的重要组成部分。这种筛选的目的是发现与靶结合足够强和特异性的分子,以具有临床益处。科学家们在实验室里努力合成各种分子,希望提高它们的结合强度。这一过程通常由对药物-靶标相互作用的经验理解来指导。因此,人们很自然地会问,是否有可能明智地“设计”一种药物,就像智能手机是如何设计来实现一组预定义的性能目标一样。
随着量子力学和分子动力学的出现,科学家已经能够在计算机上预测分子之间的相互作用。然而,它需要先进的领域知识和昂贵的计算资源。高通量筛选的进步也有助于在过去几十年中积累大量实验确定的结合强度。人们仍然希望有一种更快、更准确的方法来获得结合强度。
我们能否利用所有先前工作的累积效应,并利用另一个领域的工具向前推进?这就是最近一个名为 IDG 的机器学习挑战的目的——梦想药物激酶结合预测挑战。这个项目是关于药物和激酶之间的相互作用。激酶是一类对恶性细胞(又称癌症)不受控制的增殖至关重要的蛋白质。科学家们已经成功地开发出能够粘附某些激酶的药物,从而阻止这种可怕疾病的发展。非常需要更多具有强结合特性的药物来对抗各种癌症。
药物激酶结合的预测本质上是一个没有提示的跨词难题,如下图所示:

Copied from this reference.
挑战在于根据已知数据(黄色)预测缺失的结合强度(交叉)。根据待预测的结合对是否涉及零个、一个或两个从未被已知数据覆盖的药物或激酶,存在如上所示的四种不同情况。
以这种方式提出问题类似于机器学习中一个众所周知的任务——协同过滤。在网飞的百万美元大奖颁奖典礼上,它第一次成为聚光灯下的焦点。我们可以把药物和激酶的结合强度想象成药物和激酶的相似程度。网飞奖提出了完全相同的问题:根据用户对其他电影的评价以及其他用户对这部电影的评价,预测用户对这部电影的评价。这是上图中的第一个场景。
Fastai 文库及其在药物激酶结合问题中的简单应用
Fastai ,努力让神经网络再次变得不酷,让软件工程训练相对较少的人实现最先进的神经网络,包括协同过滤模型。本库的主要作者认为,神经网络已经发展到这样一个地步,即通过遵循一组非常小的最佳实践,可以为大量数据集开发出一个像样的模型。因此,限制其应用的不再是一个人能多好地编码,以及一个人能多有经验地微调一个模型。相反,如何根据自己对领域知识的独特理解恰当地铸造一个模型才是关键。在药物激酶结合的问题上,至少从表面上看,将它用于协同过滤并不困难。
药物激酶结合的数据可以在这里下载。它提供了一大套药物和激酶之间的亲和力测量。为简单起见,我从 Kd 开始,即离解常数。结合越强,Kd 越低。有 32536 个测量值可用。下面显示了一个小集合:

注意,Kd 的对数将用于建模。
培训过程包括以下几个步骤:
- 生成数据—批量重新组织数据以供 GPU 处理。
- 设置学习者—选择合适的培训模型。
- 找到学习率—选择学习者探索参数空间以找到最佳点的“积极性”。
- 训练-使用数据优化模型。
多亏了 fastai 库,每一步都只需要一(1)行代码。当然,这是一个香草模型。高级用户可以通过修改库的内部机制来制作更复杂的模型。
下面是实现上述步骤的代码快照:
Kd_data = CollabDataBunch.from_df(df_Kd, user_name=’compound_id’,
item_name=’target_id’,
rating_name=’KDLog’,
pct_val=0.1,
seed=40)
Kd_learn = collab_learner(Kd_data, n_factors=20,
y_range=[-15, 25],
wd=1e-2)Kd_learn.lr_find()
Kd_learn.fit_one_cycle(5, lr=5e-3)
让我们首先检查一下,如果没有协同过滤,我们可以“猜测”到什么程度?如果我们通过生成一个高斯随机数来“猜测”未知的 Kd,该高斯随机数的平均值和标准偏差是根据已知的 Kd 计算的,则预测值和实际 Kd 之间没有明显的相关性,如下所示。预测误差的分布看起来像非常宽的高斯分布。

使用我们训练的模型,结果要好得多。

回想一下第一幅图中显示的四个场景,这个模型只适用于第一个场景。事实上,如果我们检查模型的预测,一些神秘的化合物 id 显示为#na#。这些化合物是那些没有被训练数据集覆盖的化合物。这同样适用于激酶(列“目标 id”)。

如果我们简单地忽略这些项目,预测的质量会如预期的那样提高。

对于此模型,微调包括调整以下三个参数:
- wd 或权重衰减:该参数控制正则化。简单地说,较大的 wd 对过度拟合的惩罚更大。
- n 因子或因子数:协同过滤的本质是假设有特定数量的因子表征药物和激酶。在这些“潜在”因素定义的空间中,药物或激酶用一个点来表示。如果药物点和激酶点或多或少地从原点指向相同的方向,它们的相互作用会更强。
- lr 或学习率:这控制了学习者在探索“粗糙”参数空间期间寻找全局最小值时所采取的步长。
人们过去认为,通过调整这些参数来优化模型需要经验。事实上,它决定了模型是否有效。fastai 的贡献在于它提供了对这些重要参数的简单访问。主要作者 Jeremy 进一步认为,存在一组“好的”参数,它们对不同的数据集都非常有效。在我们特定的药物激酶模式中,参数的网格搜索显示学习率是最重要的超参数。学习率的最佳值与杰里米为 MovieLens 模型发现的值大致相同,即预测用户对电影的评级。

R² as a function of wd, lr or n_factor. Data points are from a grid search by keeping one of the parameters constant and varying the other two over a certain range.
对于 wd 或 n_factors,总是有好的参数组合,但是对于差的 lr,很难得到好的结果。
我们能预测什么?
如上所述,这里只讨论第一种情况。另外三个问题通常被称为“冷启动”问题。由于我们的潜在因素是通过经验模型拟合过程获得的,因此从概念上讲,首先很难为那些没有任何经验数据的人找到一组潜在因素。如果预测是基于分子的固有特征或它们的组合,冷启动问题就不再是一个问题。这种基于元数据的方法涉及到重要的特征工程和广泛的领域知识。尽管如此,协作过滤可能有助于特征工程,正如杰里米在罗斯曼数据集中所阐述的那样。在药物激酶问题中,如果我们相信来自协作模型的一组潜在因素是合理的,就有可能提出另一个模型,该模型可以利用分子的结构特性很好地预测潜在因素。然后,该模型可以用于冷启动问题。关联元数据和潜在因素的模型可能受益于迁移学习方法。
为了说明潜在因素的重要性,我们可以进行主成分分析,将潜在因素归纳为三个“主要”因素。下图显示了一些激酶在由两个主要因素定义的 2D 空间中的位置。那些相邻的激酶应该具有相似的特性。



更严格的方法是找到每个激酶在它们的主成分空间甚至潜在因子空间中的邻居。Scikit-learn 实现了各种邻居查找方法。生物化学家可能会检查结果,看它是否有意义

比如p 00519(0 线)和它最近的邻居 P51451 都是酪氨酸蛋白激酶。这并不奇怪,因为它是一个非常常见的药物靶点,而且这个数据集有很多激酶。再比如, P00533 (线 1)是表皮生长因子受体,它最近的邻居 P47747 是组胺 H2 受体。作为最后一个例子, P00918 (第 4 行)是视黄酸受体α,它最近的邻居 P10276 是碳酸酐酶 II。这些配对真的在功能上相关吗?记住,你根本不需要理解生物化学来达到这一点。
从根本上说,我们想知道这种方法是否有助于药物发现。还有很多工作要做,以使模型足够精确,使科学家免于在工作台上劳动。
- 该模型可以使用更多的微调。
- 众所周知,测量结果具有较大的误差线和各种格式/惯例。
因此,实验人员和建模人员之间的进一步合作对于使协同过滤以及其他机器学习方法更加智能至关重要。
感兴趣的人可以在这里找到 Jupyter 笔记本。请注意,这项工作是初步的。
参见第 2 部分了解领域知识和另一个数据集的进一步讨论。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
使用 fastai 的协同过滤
了解如何使用 fastai 库构建推荐引擎

介绍
协同过滤是机器学习的一种应用,我们试图预测用户是否会喜欢特定的电影或产品。我们通过查看用户以前的购买习惯来做到这一点。在其最简单的形式中,协同过滤只涉及 3 列,用户 ID、歌曲 ID 和用户对该歌曲的评价。这正是我们将在本文中使用的。

数据存储
有两种方法可以存储协同过滤的数据。我们可以将它存储在如上所示的.csv中,也可以使用matrix.矩阵的行代表用户,列代表项目,特定单元格的值代表用户对特定项目的评分。

然而,如果我们以这种方式存储它,我们将最终得到一个非常稀疏的矩阵,因为大多数用户不会观看大多数电影或购买大多数产品。此外,矩阵将是巨大的,浪费了大量的存储空间。因此,我们将仅将其存储为.csv。
嵌入
我们处理协同过滤问题的方法是,我们给每个用户分配一组权重,给每个项目分配一组权重,这些权重的点积就是用户对该项目的评分。

image source: fast.ai
对于每个用户,我们查找相应的权重,这个东西被称为嵌入,将代表宋立科流派或用户口味的特征。
但是这些还不够。也许有一些用户喜欢很多歌曲或者大多数人喜欢的歌曲。因此,我们不是只取点积,而是将用户偏差和歌曲偏差添加到我们的预测中,以说明这一点。然后,我们使用梯度下降来更新我们的权重,直到我们得到足够好的结果。
让我们看看如何在代码中做到这一点。
解决办法
和往常一样,我们从导入 fast.ai 库开始。

我们检查数据集的长度和收视率的分布。
**
然后,我们创建一个数据集群,并使用 20%的数据作为验证集的一部分。

请注意 DataBunch 创建步骤中的种子参数。
当我们希望我们的结果可重复时,我们使用种子。这意味着我们随机抽取 20%的数据作为验证集的一部分,但是,我们希望确保下次运行代码时,我们得到相同的(随机)分割。

一旦我们创建了一个数据束,我们就初始化一个 学习器 。点积和偏差会给我们一些分数。这些分数可以是负数,也可以是正数。我们希望确保这些预测在我们期望的 0-5 范围内。经过大量的训练后,模型可以自己学习,但是我们希望节省一些工作,并告诉它我们希望预测的范围。因此我们把参数y_range传递给它。这将在最后添加一个 s 形层。
我们选择稍微高于和低于最小值和最大值的值,以保持预测在范围内。
n_factors参数决定了我们嵌入的大小。我们也传递了一点重量衰减。
最后,我们找到学习率并训练我们的模型。

结果

数据集是一个相对简单的数据集,我们得到了很好的结果。此外,由于我们的嵌入大小只是一个参数,我们可以运行一个for loop并传递不同大小的嵌入来检查哪一个给出最好的结果。

这就是我们解决协同过滤问题的方法。我们可以解释模型,找到最有偏见的歌曲或最没有偏见的用户。然而,为此,我们需要另一个csv来告诉我们哪个 id 对应于哪个歌曲或用户。
根据歌曲偏好对歌曲进行评级会很有趣,因为如果我们仔细想想,当我们去掉用户偏好和我们的嵌入时,歌曲偏好仍然存在。这意味着它代表了歌曲的无偏见的观点,去除了可能影响它的所有其他因素(基于歌曲和用户)。
我们还可以看看嵌入。然而,我们可能有很多(在这个例子中是 50 个),看 50 个影响我们选择歌曲的因素是不直观的。因此,我们可以做的是应用类似 PCA(主成分分析)的东西,将其降低到一个合理的数字,然后可以进行分析。
协同过滤的问题:冷启动
当我们有足够的关于用户或歌曲的数据时,我们可以很好地预测结果。但是,我们真正想给用户推荐歌曲的时候,是有新用户加入我们平台的时候。或者某个艺人发布新歌的时候。在这种情况下,我们没有以前的数据可以依赖,因此推荐是困难的。公司采取各种方法来解决这个问题,比如让用户选择他喜欢的电影类型,但目前还没有明确的解决方案。
结论
最后,我想敦促我的读者真正提高他们的数据准备技能。当我们参加 Kaggle 比赛或在线下载数据集时,所有的数据清洗和预处理步骤都已经为我们完成了。然而在现实世界中,你将不得不处理原始数据,你将不得不处理多个数据源。你如何处理这些资源将决定你是否会成为一名优秀的数据科学家。
所以努力学习这些技能,因为建模只是过程的一部分(而且是很小的一部分。)
这就是本文的全部内容。
如果你想了解更多关于深度学习的知识,可以看看我在这方面的系列文章:
我所有关于深度学习的文章的系统列表
medium.com](https://medium.com/@dipam44/deep-learning-series-30ad108fbe2b)
~快乐学习*
收集电影数据

使用一堆 API 来组装一个非常简洁的数据集
我最初的前提是,我可以试着根据 iTunes 电影的特点预测它的价格。我知道我们可以通过一个记录良好的公共 API 在 iTunes 中获得电影信息,所以让我们来看看。
iTunes 数据商店中的数据
您可以通过两种主要方式在 iTunes API 上从 iTunes store 获取影片信息,即通过关键词调用或按 ID 查找。显然,使用关键字来提取特定的电影通常不是一个好主意。有些关键词会给你多达 50 个匹配项(试试搜索“哈利波特”)。

With an average response time of 2s, 10 billion lookups is gonna take…
按 ID 查找也没有太大的改进。iTunes ID trackId字符串至少有 10 位数长,这意味着可能有多达 100 亿个可能的 ID,我必须一次查找一个。不是一个好主意。
所以我使用了 iTunes RSS 提要来缩小我的搜索范围。下面的代码片段定义了一个函数collect_rss_movies()来 ping iTunes RSS 提要。我决定依靠 RSS iTunes feed 来获取更多最新的电影和完整的价格信息。从 iTunes RSS 提要获得 iTunes IDs 后,我转向 iTunes API,使用get_info_by_id()功能从 iTunes store 获得电影信息。
我还定义了一个settings.py文件,它定义了集合参数。在这里,我指示该函数收集来自 9 个国家和 2 个主要流派的数据。
我最终放弃了纪录片的片名,因为相对于主流电影,它们看起来有点模糊,但这里有一个电影记录的片段:
**{'artistName': 'Christopher McQuarrie',
'contentAdvisoryRating': 'PG-13',
'country': 'USA',
'currency': 'USD', 'primaryGenreName': 'Action & Adventure',
'releaseDate': '2018-07-27T07:00:00Z',
'shortDescription': 'On a dangerous assignment to recover stolen plutonium, Ethan Hunt (Tom Cruise) chooses to save his',
'trackHdPrice': 19.99,
'trackHdRentalPrice': 5.99,
'trackId': 1406515547,
'trackName': 'Mission: Impossible - Fallout',
'trackNumber': 6,
'trackPrice': 14.99,
'trackRentalPrice': 5.99,
'trackTimeMillis': 8873322,
'wrapperType': 'track'}**
这就是它的要点——有一些特性我没有包括在这里,但是数据描述可以在官方的 i Tunes API 文档中找到,或者你可以在我的 Github 查看数据描述。
太好了!我们都准备做一个新的令人兴奋的机器学习项目。对吗?但是电影不仅仅是这些,不是吗?所以我做了更多的调查,想知道如何补充这个数据集。
打开电影数据库和电影数据库
虽然 IMdb 是所有电影信息的权威来源,但他们没有公共 API,即使我依靠网络搜集,我也很难找到我应该收集的电影。我求助于两个关于电影的开放数据库,即开放电影数据库 (OMdb)和电影数据库 (TMdb)。
电影数据库(TMDb)是一个社区建立的电影和电视数据库。从 2008 年开始,每一条数据都是由我们这个了不起的社区添加的。TMDb 强大的国际关注度和数据广度在很大程度上是无与伦比的,这是我们难以置信的骄傲。
所以这就是 TMdb 的出处。来自 OMdb 网站:
T4:OMDb API 是一个 RESTful web 服务,用于获取电影信息,网站上的所有内容和图片都是由我们的用户贡献和维护的。
为什么有两个电影数据库?
虽然它们分别得到了很好的维护,但它们各有利弊,我意识到我可能必须利用它们来为我的特性集提取正确的信息。两个数据库都没有在数据库中记录每部电影的 iTunes ID,所以我不得不依靠标题搜索来匹配 iTunes 数据和电影数据库中的每部电影。

Methods available to get information on a movie in the TMdb API
- 特色。OMdb 有比我在 TMdb 数据集中找不到的更多的有用信息。例如,OMdb 包含关于电影评级的信息,例如烂番茄评级、Metacritic 评分和 IMdb 评级。OMdb 数据还记录了这部电影的票房表现,我觉得这将是预测一部 iTunes 电影能卖多少钱的一个关键特征。
- ****API 能力。总的来说,TMdb API 功能更强,提供了更多的方法来查找信息。在 OMdb 中,你只能通过关键字搜索或 IMdb ID 来查找电影。此外,在 TMdb API 上进行关键字搜索通常会产生比 OMdb API 更好的结果。
- API 比率限值。 OMdb 有一个非常严格的 API 速率限制,每天 1,000 个调用,而 TMdb API 有一个更高的速率限制,每秒 4 个请求(或每天约 340,000 个调用)。
所以OMdb API 有更多我需要的信息,但是 TMdb API 通常更好使用。我最终决定,我需要对 OMdb API 进行非常有针对性和具体的调用,通过 ID 查找来获取电影信息。OMdb 使用 IMdb ID 索引他们的电影记录,所以我必须通过关键字搜索找到 iTunes 数据集中每部电影的 IMdb ID,正如我提到的,通过 TMdb API 更好。****
尽管有些复杂,我还是决定采用以下策略:
- 将电影标题解析为一个搜索字符串,通过 TMdb API 传递,寻找精确匹配。包括生产年份,以进一步缩小搜索范围。

Parsed movie titles, transformed into search strings for the TMdb API.
2.为了精确匹配,使用 TMdb ID 查找来查找 IMdb ID。对于没有匹配的结果,通过删除生产年份来放宽搜索标准。对于有多个匹配或结果的电影标题,从 Google 搜索的第一个结果中提取 IMdb,格式为“电影标题+制作年份+ IMdb”
3.将每部电影与一个 IMdb ID 进行匹配,并在 OMdb API 中查找每部电影的电影信息。
按照这个过程,我设法从 OMdb API 中唯一地识别出一个看起来相当健康的 603 部电影的数据集,其中与我在 iTunes store 上收集的 1800 部电影中的大约 1500 部相匹配,因此总体来说回报不错,而没有深入到实体解析的混乱世界中。这里有相当长的一段代码,但是你也可以在我的 Github repo 上查看。
票房 Mojo 数据
在我对 OMdb 数据做了一些初步探索之前,我对我拥有的数据非常满意。我注意到我的数据中有一大块票房信息缺失。

Over 40% of the movies in the OMdb data had missing box office receipt information.
我可以忍受缺失的评分,但票房表现将是纳入我的模型的一个很好的特征。

Dat feeling when you see so many missing values
很明显,我可以很容易地通过公共数据源获得信息。所以我求助于票房魔咒,使用曾经可靠的requests和BeautifulSoup组合。
我找到了大约 7000 部电影,填补了我所有缺失的价值,并补充了全球票房总收入与美国票房总收入的对比。现在的问题是把它们放在一起。

Merging data: Easy as Pen Pineapple Apple Pen
我的数据现在是什么样子
在所有这些争论之后,我有一个大约 1500 部电影的数据集,数据来自 4 个不同的来源,通过不同的键和方法连接。它们主要存在于我的存储库中的两个 csv 文件中,我已经为下面的数据库包含了一个广泛的模式。

这就结束了我关于收集电影数据的帖子。在接下来的一周左右,我会在这里发布关于建模和数据探索的更新。同时,如果您对我的数据收集过程有任何反馈、建议或改进,请联系我!
集体透明度
随着我们的隐私继续受到对数据的无休止追求的挑战,集体透明提供了解决方案吗?

1999 年,Sun Microsystems 首席执行官斯科特·麦克尼利(Scott McNealy)有一句名言:“无论如何,你都没有隐私——克服它吧”他指的是围绕消费者数据隐私的担忧,这在今天继脸书/剑桥分析公司丑闻之后,就像在早期网络时代一样相关。
麦克尼利的评论提出了两个问题。首先,他说的对吗?第二,如果是,又能做些什么呢?
非自愿透明
这个问题的答案几乎肯定是:谁知道呢?我们生活在一个数据收集广泛且不断增长的时代,以至于我们很难不被称为数字文盲。但是在讨论隐私的时候,无知是一个无力的借口;无知通过遵从“你不知道的不会伤害你”的方法来回避任何讨论。但是无知确实支持了一个更强有力的论点——我笨拙地称之为“你并不特别”的论点。
在他的精彩著作 新黑暗时代 中,James Bridle 讨论了大数据和算法,这些数据和算法的优化超出了我们最疯狂的幻想。他在讨论无限乐趣空间时引用了科幻作家伊恩·班克斯的话。无限有趣的空间是超级复杂的人工智能去玩的地方,建造人类思维不可思议的世界,而我们人类却生活在一个完全优化的——而且非常无聊的——世界中。
“你并不特别”的论点是无知和超智能的结合:谁在乎你是否没有隐私,因为唯一能理解你的数据的东西是有更好的事情要做的计算机。虽然我不喜欢这种说法,但它确实强调了我们的许多隐私焦虑只是社交焦虑,这种焦虑来自于暴露给其他人。这些焦虑不会消失,但通过明智的社交媒体管理和某种形式的集体透明(我们将谈到这一点),其中一些担忧可以得到缓解。
但是隐私不仅仅是我们不想让世界知道的感觉和欲望。隐私会对一个人的财务和身份产生极其严重的后果。在这方面,隐私是比特币备受称赞的好处之一;虽然交易必须透明,但交易背后的人仍然是匿名的。确保这些领域的隐私是至关重要的,如果这样做可以获得很大收益,恶意行为者将不惜代价。如果麦克尼利关于隐私的观点是正确的,那么,建议我们忘掉它是不够的。
还有另一个版本的隐私,我们不应该就这么算了;行为隐私。行为经济学包含了一个叫做“轻推”的概念,即选择框架的微小变化会对结果产生重大且可预测的影响。我以前写过大数据和轻推如何结合成超轻推——超有针对性的个性化轻推,这些轻推从大量数据中提取信息来影响一个人的选择。超推荐的典型例子是个性化广告,比如展示你最近在不同网站上看到的产品的广告。对一些人来说,这些广告令人毛骨悚然,而对另一些人来说,他们更愿意看到他们感兴趣的东西的广告,而不是他们不感兴趣的东西。无论哪种方式,超轻敲都依赖于你的数据隐私。
后隐私
不管麦克尼利说我们没有隐私是否正确,隐私比他所说的要复杂得多。隐私可以指几个不同的东西,而且被侵犯的原因可能更多。但假设麦克尼利是对的,我们能做些什么呢?
那么,我们如何放弃我们的隐私?通常,我们认为我们的个人数据是一种货币,我们用它来交易各种服务。我之前已经论证过这不是一个好的思考方式,因为无论谁拥有它,货币的价值都是一样的;数据不是。法律数据学者 Karen Yeung 讨论了一个有趣的替代想法——集体隐私权。在这里,隐私不仅仅是个人的想法,而是一种社会的想法,即使个人“出售”他们的个人数据,这种想法仍然存在。
但是大数据改变了这一点。有了足够的数据,系统通常可以对可能从未向他们提供任何数据的个人做出准确的预测。不管侵犯隐私的目的是什么,这是一种侵犯,它引起了严重的道德问题——如果不是法律问题的话。但同样,大数据精灵已经从瓶子里出来了,因此我们必须开始考虑如何生活在后隐私时代。
如果我们的集体隐私权已经死亡,我们对个人隐私权的控制日益减弱,还有什么选择呢?答案可能是数据经济学家恩斯特·哈芬所说的“复制权。根据这项提案,作为公民,我们将有权要求任何人提供一份关于我们的任何数据的副本。当然,这并没有解决隐私问题,但它确实还给我们一些授权和控制。
对哈芬来说,这很重要:有了这些副本,他提议创建一个中央数据仓库。他认为,这个存储库有两个优势:它将得到民主管理,并将解除数据垄断,迫使大型科技公司在服务质量上竞争,而不是通过数据囤积和寻租策略。这个中央存储库是我称之为集体透明的一个版本。
走向集体透明
在某种程度上,复制的权利已经存在,社交媒体服务如脸书和推特允许用户下载他们所有数据的副本。因此,实现集体透明与其说是关于归还数据控制权,不如说是关于建立新的数据基础设施。这就是集体透明的问题所在:人们仍然想要隐私;如此大量的数据可能会被用于非常不受欢迎的目的;我们如何确保对存储库的民主控制并且没有违规操作?
这些问题是有根据的,除非(或者如果)集体透明度成为一个活跃的项目,否则很难找到这些问题的具体技术答案。一个早期的解决方案可能建立在区块链技术的基础上,利用比特币这样的假名方法来保持透明度,而不暴露人们。加密货币还能够整合投票功能,这可能会解决民主问题,尽管如此重要的资源、公共和透明的治理结构似乎是必要的。这将把我们推向数据化民主的极限。
集体透明也可能不是解决办法。也许会有别的事情发生?也许我们会发现大数据的经济极限,它保证了我们的隐私,因为它不值得侵犯。但是,如果我们接受麦克尼利的主张,接受我们没有隐私——让·波德里亚称之为“非自愿透明”——集体透明似乎是一个有趣的选择,可以取代我们已经失去的东西。
利用 K-均值聚类的调色板提取|从零开始的机器学习(第四部分)
使用 Python 中的 K-Means 聚类在移动 UI 截图中查找主色
TL;DR 使用 Python 从头开始构建 K-Means 聚类模型。使用你的模型从 UI 移动设计截图中寻找主色。
为你的下一个大型移动应用程序(重新)设计选择调色板可能是一项艰巨的任务,尤其是当你不知道自己在做什么的时候。你如何能使它变得更容易(请求一个朋友)?
一种方法是去一个地方,在那里专家分享他们的工作。像 Dribbble 、 uplabs 和 Behance 这样的页面都有货。
找到您喜欢的模型后,您可能想要从中提取颜色并使用它们。这可能需要打开专门的软件,用一些工具和其他非处方工具手动选择颜色。让我们用机器学习让你的生活更简单。
colab.research.google.com](https://colab.research.google.com/drive/1_p1nptDfvJhcSvprmi1WtoIugsCI40Xa)
无监督学习
到目前为止,我们只研究了需要特征和标签形式的训练数据的模型。换句话说,对于每个例子,我们也需要有正确的答案。
通常,这样的训练数据很难获得,并且需要人工完成许多小时的工作(是的,我们已经在为“终结者”服务)。我们能跳过这些吗?
是的,至少对于某些问题,我们可以在不知道正确答案的情况下使用示例数据。一个这样的问题是集群。
什么是集群?
给定一些数据点的向量 X ,聚类方法允许您将每个点放在一个组中。换句话说,您可以根据一组实体的属性自动对它们进行分类。
是的,这在实践中非常有用。通常,你在一堆数据点上运行算法,并指定你想要多少组。例如,您的收件箱包含两组主要的电子邮件:垃圾邮件和非垃圾邮件(您是否在等待其他邮件?).你可以让聚类算法从你的电子邮件中创建两个组,并使用你美丽的大脑来分类哪个是哪个。
聚类算法的更多应用:
- 客户细分 —寻找消费/行为方式相同的用户群
- 欺诈交易 —查找属于不同分类的银行交易,并将它们识别为欺诈交易
- 文档分析 —对相似的文档进行分组

source: various authors on https://www.uplabs.com/
这一次,我们的数据不是来自一些预定义的或众所周知的数据集。由于无监督学习不需要标记数据,互联网可以是你的牡蛎。
在这里,我们将使用来自不同作者的 3 个移动用户界面设计。我们的模型将在每个镜头上运行,并尝试提取每个镜头的调色板。
什么是 K-Means 聚类?
K-Means 聚类被维基百科定义为:
k-means 聚类是一种矢量量化的方法,起源于信号处理,常用于数据挖掘中的聚类分析。k-means 聚类旨在将 n 个观察值划分为 k 个聚类,其中每个观察值属于具有最近均值的聚类,作为该聚类的原型。这导致将数据空间划分成 Voronoi 单元。
维基百科还告诉我们,解决 K-Means 聚类是困难的(事实上, NP-hard ),但我们可以使用一些启发式算法找到局部最优解。
但是 K 均值聚类是如何工作的呢?
假设你有一个包含数据点的向量 X 。运行我们的算法包括以下步骤:
- 从 X 中随机选取 k 个点(称为形心
- 将每个点分配到最近的质心。新形成的一簇点被称为簇。
- 对于每个聚类,通过从这些点计算新的中心来找到新的质心
- 重复步骤 2–3,直到质心停止变化
让我们看看如何使用它从移动 UI 截图中提取调色板。
数据预处理
假设我们的数据存储在原始像素(称为图像)中,我们需要一种方法将其转换为我们的聚类算法可以使用的点。
让我们首先定义两个表示点和簇的类:
我们的Point只是我们空间中每个维度坐标的持有者。
Cluster由它的中心和它包含的所有其他点定义。
给定图像文件的路径,我们可以如下创建点:
这里发生了几件事:
- 将图像载入内存
- 将其调整为较小的图像(移动 UX 需要屏幕上的大元素,所以我们不会丢失太多的颜色信息)
- 删除 alpha(透明度)信息
注意,我们正在为图像中的每个像素创建一个Point。
好吧!现在,您可以从图像中提取点。但是,我们如何计算我们的集群中各点之间的距离呢?
距离函数
类似于我们的监督算法示例中的成本函数,我们需要一个函数来告诉我们做得有多好。我们算法的目标是最小化每个质心中的点之间的距离。
我们可以使用的最简单的距离函数之一是欧几里德距离,定义如下:

其中和是我们空间的两点。
请注意,虽然欧几里德距离实现起来很简单,但它可能不是计算色差的最佳方式。
以下是 Python 实现:
实现 K 均值聚类
现在你已经有了所有的拼图,你可以实现 K-Means 聚类算法。让我们从寻找一组点的中心的方法开始:
为了找到一组点的中心,我们将每个维度的值相加,然后除以点数。
现在,为了找到实际的集群:
实现遵循上面给出的算法描述。请注意,当色差低于我们设置的值时,我们将退出训练循环。
估价
现在您已经实现了 K-Means 聚类,您可以在 UI 截图上使用它。我们需要更多一点的粘合代码,以便更容易提取调色板:
get_colors函数获取一个图像文件的路径和您想要从图像中提取的颜色数量。我们根据每个聚类中的点对从我们的算法中获得的聚类进行排序(降序)。最后,我们将 RGB 颜色转换为十六进制值。
让我们从数据中提取第一个 UI 截图的调色板:
这是十六进制的颜色值:
#f9f8fa, #4252a6, #fc6693, #bdbcd0, #374698
为了直观地展示我们的结果:

在接下来的两个图像上运行聚类,我们得到以下结果:

最后一张截图:

嗯,看起来很酷,对吧?继续在你自己的图像上试试吧!
colab.research.google.com](https://colab.research.google.com/drive/1_p1nptDfvJhcSvprmi1WtoIugsCI40Xa)
结论
恭喜你,你刚刚实现了你的第一个无监督算法!当试图从图像中提取调色板时,它似乎也获得了良好的结果。
接下来,我们将对短语进行情感分析,并学习如何处理文本数据。
喜欢你读的吗?你想了解更多关于机器学习的知识吗?提升你对 ML 的理解:
“我不能创造的东西,我不理解”——理查德·费曼这本书将引导你走向更深的…
leanpub.com](https://leanpub.com/hmls)*
数据科学家的着色规则
讲述真实故事的视觉效果
作为数据科学家,我们希望确保清楚地传达我们的发现。无论我们的目标是技术或非技术受众,我们都想让我们的视觉效果“漂亮”。然而,我们真正应该考虑的是在我们的数据中讲述这个故事。不幸的是,我们所认为的好的设计和我们的数据的适当可视化并不总是一致的——但这里有一些规则来避免常见的错误,并确保你的视觉效果与你的故事相符。
1.配色方案:使用为您的数据设计的配色方案

Image from MatplotLib Documentation
永远不要根据什么看起来最好来选择视觉效果的配色方案—使用为您的数据类型开发的配色方案。比如 bone 是为 x 光数据开发的,不要因为它好看的中性色就用它,用它做 x 光。有时,配色方案不会如此特定于领域,但您仍然应该使用一些反映数据本质的东西。例如,对于从-1 到 1 的值,如相关性,使用发散色图(例如: RdBu , coolwarm ),对于分类数据使用定性色图(例如: Set1 )。这些颜色类别是专门为这些目的开发的,应该按照最初的意图使用,几乎没有例外。
2.混乱矩阵:这些应该总是表格

Image from ResearchGate
这个很简单——不要绘制你的困惑矩阵。这种方式可能看起来很好,也更容易阅读,但它旨在成为一个表格。如果你不相信,想想其他的热图:为什么它们使用不同的配色方案?答案是它们有负值,混淆矩阵没有。
3.标签:标记你的颜色
在你制作的每张图表上标注你使用的所有颜色(除非只有一种颜色)。
4.对你的观众和内容保持敏感
首先,并不是所有的配色方案都能被色盲的人清楚地理解,所以如果你要制作广泛传播的图形,或者你的团队中有人是色盲,确保你使用的东西对每个人都清晰可辨。
其他关于敏感性的话题可能更有争议,比如使用分类的粉红色和蓝色调来表示性别,假设是二进制的。

Image from OUPBlog
在绘制图表之前,请确保您考虑是否将性别分析为二元,以及为什么。如果你的数据没有反映一个光谱,并迫使你有 2 个类别,你可以选择澄清这一点。无论是哪种选择和原因,确保你有意识地考虑这一点,并选择你认为负责任的配色方案。
最后一点,这些规则是很好的参考,但要始终批判性地思考你试图想象什么,为谁,以及如何去做!

如果你对如何解决某件事有疑问,请在下面评论,我会给每个人留下答案!
基于卷积神经网络的图像彩色化
深度学习算法对构图、风格以及机器学习和艺术之间的关系有什么看法

Cuban immigrants arrive in Florida ca. 1965. Original image credit CDC, color version using Colorful Image Colorization algorithm credit Gado Images.
见过手绘历史照片吗?它们是艺术作品,由一个熟练的人在黑白照片的每个部分煞费苦心地着色,添加肤色、背景等等。自从摄影出现以来,它们就一直很受欢迎,直到今天仍在制作——唯一的区别是今天的艺术家使用 Photoshop 而不是画笔。

Hand colored images, like this lithograph of Cincinnati ca 1840s, were works of art. Credit: New York Public Library/Gado
手绘照片很美,但是制作起来很慢。你必须决定要添加的颜色,有绘画技巧将它们放到原始照片中,等等。即使使用现代工具,雇佣一名艺术家给一张历史照片上色也要花费 300 到 500 美元。
深度学习方法
进入卷积神经网络。在许多情况下,图像中的颜色是独一无二的——一个人衣服的确切颜色,一棵树完美的绿色阴影,等等。在拍下黑白照片的那一刻就永远消失了。然而,在其他情况下,颜色是可以预测的——令人惊讶的是。天空通常是蓝色的(或者很可能是蓝色的),绿色植物是绿色的,人的皮肤是肤色,水是蓝色的,衣服通常不是花哨或疯狂的颜色,等等。
因为颜色比你想象的更容易预测,使用机器学习比你最初想象的更容易控制。这意味着你实际上可以使用卷积神经网络来给历史黑白照片着色。
彩色图像彩色化是一种算法,它使用 CNN 来分析一组彩色图像及其黑白版本的颜色。这里很容易获得训练数据—任何彩色图像都可以转换为灰度,然后与其彩色版本配对,以制作一个简单的训练示例。该算法使用几个前馈通道来最终获取灰度图像,用该算法的创造者的话说,就是“幻觉”一组似乎合理(但不一定正确)的颜色来填充图像。

The Colorful Image Colorization algorithm can add plausible colors to black and white photographs. Original photo credit New York Public Library, colorization by Gado via Colorful Image Colorization
彩色图像着色在超过 100 万幅图像上进行训练。它的创造者报告说,当在“彩色图灵测试”中向人类展示结果时,人们在 32%的时间里相信颜色是真实的。这听起来没什么,但是请记住,这个任务甚至比给历史图像着色更难。图灵测试中的人们不仅仅相信他们看到的图像是一个执行良好的手工着色——相反,他们相信图像真的是彩色图像。这是一台灰度原始的机器——甚至 32%的时间——完成的相当成功。
在算法的结果中出现了一些非常显著的突现性质。例如,给定一个带有可口可乐标志的灰度历史图像,它会正确地将标志 Coca Cola 涂成红色,这可能是因为看到了数千个带有红色可口可乐标志的训练图像。重要的是,该算法从未被教会可口可乐的标志是什么——通过 CNN 的魔力,它从大量的训练数据中找到了这一点。

The algorithm correctly colored the drink car in this image Coca Cola red, presumably from seeing lots of red Coca Cola logos in its training data. Original image credit New York Public Library, colorization by Gado with Colorful Image Colorization.
那么颜色是不是一文不值?
当我第一次开始在灰度图像上测试彩色图像着色时,我的第一反应是不安,近乎厌恶。作为一名专业摄影师,颜色非常重要,我工作的很大一部分是让图像中的颜色完全正确,根据它们鲜明的颜色选择主题,等等。如此多的颜色是预先确定的——足以让机器能够猜测场景中的颜色并大致正确地得到它——这种想法令人不安,也有点令人沮丧。色彩真的是构图中一个有趣的元素吗,或者它是一种可以在以后由产生幻觉的计算机程序补充的东西吗?
这种观点当然有很多历史先例。著名纪实摄影师亨利·卡蒂埃·布列松以拍摄甘地的照片和世界各地人们的私密街头肖像而闻名,他对他同时代的威廉·埃格尔斯顿做了非常简洁的总结:“威廉,颜色是狗屁。”安塞尔·亚当斯——也许是 20 世纪最著名的美国摄影师——在他的职业生涯中,对色彩深表怀疑。具有讽刺意味的是,彩色图像着色的创造者们选择了在他们关于算法的第一篇论文中通过给一些 Adams 的图像着色来展示他们的过程。
那么所有的颜色都是预先决定的吗?我们能否发布一个出色的新压缩算法,让相机以灰度拍摄照片,然后像 CNN 一样的彩色图像着色技术在稍后的云中填充颜色?历史黑白摄影死了吗?我们是不是应该扔掉亚当斯的玻璃盘子,换上由机器创作的色彩鲜艳的风景画?
令人惊讶的算法
没那么快。像许多建立在卷积神经网络上的系统一样,彩色图像彩色化产生了一些显著的结果,但它与边缘情况作斗争。谢天谢地,视觉艺术的世界充满了边缘案例。
彩色图像着色做得最好的图像是那些具有可预测成分的图像——上面是蓝天,中间是一些风景,可能是一棵很明显的树,前景是一些水,它可以使之变成蓝色。有了这个算法,很多度假快照都变得很棒。从某种意义上说,它在“平均”图像上表现最好——这些图像的成分和颜色与它训练的 100 万张图像的平均成分和平均颜色没有太大差异。

The algorithm does best on images with a predictable composition. Original image credit New York Public Library, colorization by Gado with Colorful Image Colorization.
然而,对于具有不同构图、新颖物体和非常规颜色的图像,算法却很难处理。城市、壁画、色彩丰富的市场、有大量非天空负空间的图像——在这些图像上,色彩丰富的图像色彩表现平平。

The algorithm yields weird results on this unconventional composition of flowers shot through a window, splashing some random blue over the center of the image and totally missing the brick wall outside. Credit Gado Images.
在某种意义上,算法实际上是一个很好的图像唯一性的指示器。如果你的图像在通过算法着色后看起来非常好,那么它在组成和颜色方面可能是一个相当“普通”的图像——它与系统训练的数百万张图像没有太大差异。这并不一定意味着图像不好——许多构图一般的图像是商业金矿或描绘了一个重要的人或地方。有时一张普通的图像是好的,特别是当你想让你的构图和颜色选择不碍事,并且不干扰图像的纪录片内容的时候。
但是如果你创建了一个让彩色图像色彩“惊喜”的图像——产生奇怪或不准确的结果——拍拍你自己的背。你创造了一些偏离平均值的东西,足以骗过算法。简而言之,你创造了艺术上独一无二的东西。
对我来说,这弥补了围绕色彩的艺术过程。如果你想要一个符合视觉标准的图像,最好对它进行彩色化处理,看看电脑能不能猜出它的颜色。但令人欣慰的是,独特或视觉上不同的作品可以骗过算法——这证明了并非所有的颜色都是预先确定的,人类艺术家有能力让人感到惊讶。
实践中的算法
因此,除了一个整洁的机器学习聚会技巧或验证您的艺术选择的工具之外,彩色图像着色有价值吗?
绝对的。在某些历史照片上——尤其是肖像——该算法产生的结果非常可信,并赋予图像深度和活力,否则这些图像会变得更加平坦和缺乏活力。对于许多这样的肖像,如果有彩色胶卷的话,摄影师可能会使用它,所以给图像着色实际上相当于填充他们可能会包括的细节。
在某些情况下,算法的幻觉本身就是艺术。运行经典图像,如进入死亡之口(显示在诺曼底登陆的 D 日入侵期间,美国士兵在枪林弹雨中下船),产生令人难忘的梦境,其中颜色建立在原始构图的基础上,并增强了原始构图。

Running Colorful Image Colorization on the classic image Into the Jaws of Death yields a result which is artistic in its own right. Original image credit Signal Corps, colorization by Gado with Colorful Image Colorization.
尽管它的力量,彩色图像彩色化确实有盲点。虽然它产生幻觉的颜色看似可信,但它们并不总是历史上准确的——例如,本文顶部图像中的泛美标志看起来很红,但它实际上是蓝色的。
此外,很可能是由于在大量具有棕褐色而非真实颜色的历史照片上接受训练的结果,它倾向于以柔和的黄色和褐色色调渲染历史照片。一个有趣的实验是在一组真实拍摄的彩色历史图像上训练算法——就像一个大型的柯达彩色幻灯片档案——然后看看这个新版本在彩色历史图像上的表现。一个可以填充类似柯达彩色的 CNN 将非常适合处理历史图像,并可以在今天拍摄的图像上产生一些真正迷人的结果。
机器学习和艺术
最终,彩色图像彩色化是两个非常不同的领域——机器学习和艺术——走到一起时可能发生的有趣协同作用的一个令人信服的例子。你不会想到一个猜测颜色的神经网络会引发关于构图、主题选择和摄影作品独特性的存在性问题。你也不会想到它能重新点燃与艺术史观点的对话——就像卡蒂埃-布列松和亚当斯的观点——这些观点已经有半个多世纪的历史了。但是看看彩色图像彩色化的结果,这些问题就来了。
随着机器学习影响更多的行业,我希望视觉艺术家——实际上是所有的艺术家——开始关注这些技术提出的问题,包括关于社会和艺术过程本身的问题。如果机器学习要继续发展,它需要来自整个社会的艺术家、创意者和其他思想家的观点。但与此同时,机器学习可以回馈一些东西,展示看待艺术作品的新方法,甚至是创造艺术作品的新方法。像彩色图像着色这样的系统表明,当涉及到机器学习和艺术时,对话是双向的。
附注:首先,你应该收到我的邮件。 做到这里 !其次,如果你自己喜欢体验媒介,可以考虑通过注册会员 来支持我和其他成千上万的作家 。每月只需 5 美元,你可以阅读我所有最受欢迎的文章等等。我在这个平台上已经收到了超过 1200 万的* 浏览量 的文字!通过注册 与此链接 ,你将直接用你的一部分媒介费支持我,并获得我的文章,这不会花费你更多。如果你这样做了,万分感谢!***
在人工智能的帮助下给老 B&W 的照片和视频上色

该项目基于加州大学伯克利分校张曦轲、菲利普·伊索拉和阿列克谢·埃夫罗斯的研究工作。彩色图像彩色化。
本教程背后的想法是开发一个全自动的方法,将产生真实的黑白(B&W)照片和视频的彩色化。正如在原始论文中解释的那样,作者通过在训练时使用类别再平衡来增加结果中颜色的多样性,将问题作为一个分类任务,从而接受了潜在的不确定性。人工智能(AI)方法在测试时被实现为 CNN(“卷积神经网络”)中的前馈通道,并在超过一百万幅彩色图像上进行训练。
这张照片拍摄于 1906 年,展示了桑托斯·杜蒙的飞机“14-bis”在巴黎的首次测试:

和它的彩色版本使用这些人工智能技术开发的模型:

同样的技术也可以应用于老视频。这是 1932 年 B&W 拍摄的巴西里约热内卢的镜头:
彩色版本:
1.Lab 颜色空间
通常,我们习惯于使用 RGB 模型对彩色照片进行编码。RGB 颜色模型是一种加色模型,其中红色、绿色和蓝色光以各种方式相加在一起,以再现各种颜色。该型号的名称来自三种加色原色的首字母,即红、绿和蓝。

但是,这个项目将使用的模型是“实验室”。
CIELAB 色彩空间(也称为 CIE Lab或有时简称为“LAB”色彩空间)是由国际照明委员会(CIE)在 1976 年定义的色彩空间。它将颜色表示为三个数值,L表示亮度,a和 b表示绿-红和蓝-黄颜色分量。
颜色空间 L * a * b *是在对立颜色理论之后创建的,其中两种颜色不能同时是绿色和红色,或者同时是黄色和蓝色。CIELAB 被设计为在人类色觉上是一致的,这意味着这些值的相同数量的数值变化对应于大约相同数量的视觉感知变化。
与 RGB 颜色模型不同,Lab 颜色旨在近似人类视觉。它渴望感知一致性,其 L 分量与人类对亮度的感知非常匹配。L 分量正是用作人工智能模型的输入,它被训练来估计剩余的分量“a”和“b”。
2.人工智能(深度学习)过程
正如引言中所评论的,人工智能(AI)方法在测试时作为 CNN(“卷积神经网络”)中的前馈传递来实现,并在超过一百万幅彩色图像上进行训练。换句话说,使用 Lab 模型分解了数百万张彩色照片,并将其用作输入特征(“L”)和分类标签(“a”和“b”)。为简单起见,让我们分成两部分:“L”和“a+b”,如框图所示:

有了训练好的模型(可以公开获得),我们可以用它来给一张新的 B&W 照片着色,这张照片将作为模型或组件“L”的输入。模型的输出将是其他组件“a”和“b ”,一旦添加到原始的“L ”,将返回一张完整的彩色照片,如下所示:

简而言之,使用来自 ImageNet 的 130 万张照片的一组广泛而多样的对象和场景数据集,并应用深度学习算法(前馈 CNN),生成了最终模型,可在以下位置获得:
3.工作环境

首先要做的是组织一个我们将要工作的环境。让我们创建一个文件夹并命名为:
- 照片 _ 视频 _ 彩色化
在这个创建的主目录下,让我们创建子文件夹:
- 模型
- 输入 _ 图像
- 输入 _ 视频
- 彩色图像
- 彩色 _ 框架
- 彩色 _ 视频
进入张等人-彩色图像着色-模型下载 3 个文件并加载到创建的子文件夹“/model”下。这些文件是:
- colorization _ release _ v2 . caffemodel
- colorization _ release _ v2 _ nore bal . caffe model
- colorization _ release _ v1 . caffemodel
我假设您的机器上安装了 Python(3.6 版)和 OpenCV (4.0 版)。我们将使用 Jupyter 笔记本一步一步地描述着色的所有过程。我建议您遵循我的解释,但是如果您愿意,您可以从我的 GitHub 下载笔记本和测试照片:
我还建议您阅读 Adrian Rosebrok 博士的伟大教程“OpenCV 和深度学习的黑白图像着色”,这是这个项目的灵感和指南。
4.黑白照片彩色化

让我们按照以下步骤给 BT&W 照片上色:
导入重要库:
import numpy as np
import matplotlib.pyplot as plt
import cv2
定义要着色的图像:
IMAGE = "soldiers_1941"
注意:你可以在这里使用任何照片。在这个例子中,我使用了一张 1941 年 B&W 二战士兵的照片。我的 GitHub 上有这张照片。
定义模型路径:
prototxt = "./model/colorization_deploy_v2.prototxt"
model = "./model/colorization_release_v2.caffemodel"
points = "./model/pts_in_hull.npy"
image = "./input_images/"+IMAGE
加载序列化的黑白彩色模型和集群:
net = cv2.dnn.readNetFromCaffe(prototxt, model)
pts = np.load(points)
将聚类中心作为 1x1 卷积添加到模型:
class8 = net.getLayerId("class8_ab")
conv8 = net.getLayerId("conv8_313_rh")
pts = pts.transpose().reshape(2, 313, 1, 1)
net.getLayer(class8).blobs = [pts.astype("float32")]
net.getLayer(conv8).blobs = [np.full([1, 313], 2.606, dtype="float32")]
加载输入图像,缩放并转换到 Lab:
请注意,我们首先将图像转换为灰度。这一步并不是真的必要,但我意识到一些 B&W 的照片,尤其是旧的,可能会在岁月中有一些处理,所以,最好清理一下。
image = cv2.imread(image)
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
此时,我们有了原始图像,但是为了在 Jupyter 单元格上直接显示它,我们应该使用 pyplot 库:
plt.imshow(image)
plt.axis('off');

提取“L”:
现在,让我们的“图像”继续进行着色过程,首先应该重新缩放,转换到 Lab,以提取组件“L”并使其居中:
scaled = image.astype("float32") / 255.0
lab = cv2.cvtColor(scaled, cv2.COLOR_RGB2LAB)
resized = cv2.resize(lab, (224, 224))
L = cv2.split(resized)[0]
L -= 50
预测“a”和“b”:
net.setInput(cv2.dnn.blobFromImage(L))
ab = net.forward()[0, :, :, :].transpose((1, 2, 0))
ab = cv2.resize(ab, (image.shape[1], image.shape[0]))
创建彩色实验室照片(L + a + b):
L = cv2.split(lab)[0]
colorized = np.concatenate((L[:, :, np.newaxis], ab), axis=2)
和我们处理灰度图像一样,让我们看看彩色图像是什么样子的:
plt.imshow(colorized)
plt.axis('off');
plt.title('colorized LAB image');

Ops,似乎实验室图像不能告诉我们太多,让我们将其转换为 RGB,看看结果:
转换为 RGB:
colorized = cv2.cvtColor(colorized, cv2.COLOR_LAB2RGB)
colorized = np.clip(colorized, 0, 1)
colorized = (255 * colorized).astype("uint8")
plt.imshow(colorized)
plt.axis('off');

Uau!相当惊人!!!!!这是一张 1941 年的照片,看起来真的是全彩拍摄的!还是省省吧:
保存最终的 RGB 照片:
cv2.imwrite("./colorized_images/Color_"+IMAGE, cv2.cvtColor(colorized, cv2.COLOR_RGB2BGR))
再来一个:查尔斯·达尔文 1832 年访问巴西里约热内卢:

太好了!!!!试试其他 B&W 的照片,看看人工智能有多神奇!
5.给视频着色
一旦我们给照片上色,给视频上色就不是一件复杂的事情了。我们必须遵循以下一般步骤:
- 获取 B&W 素材并加载到 input_video 子目录中
- 阅读视频,一次一帧
- 有一个单一的框架,适用于我们所做的照片
- 有了彩色帧后,保存到另一个子文件夹: colorized_video_frames
- 关闭 OpenCv 窗口。
我们来做一个真实的案例:
可以从我的 GitHub 下载笔记本B _ W _ Video _ colorization . ipynb,或者按照步骤操作。
我做的第一件事,是从 YouTube 上下载一部 B&W 的电影,在这个例子中:
为此,我使用了一个免费工具: VidPaw 来下载它。我给它命名为:“rio_32.mp4”,保存在 input_video 子文件夹。
应用上面描述的步骤,最后我们将所有彩色帧存储在子文件夹的彩色视频帧中。让我们开始吧:
1.开始定义应该着色的文件:
VIDEO = "rio_32.mp4"
2.定义路径、常量和视频变量:
prototxt = "./model/colorization_deploy_v2.prototxt"
model = "./model/colorization_release_v2.caffemodel"
points = "./model/pts_in_hull.npy"
video = "./input_video/"+VIDEO
width = 500
vs = cv2.VideoCapture(video)
3.加载和准备模型:
net = cv2.dnn.readNetFromCaffe(prototxt,model)
pts = np.load(points)
class8 = net.getLayerId("class8_ab")
conv8 = net.getLayerId("conv8_313_rh")
pts = pts.transpose().reshape(2, 313, 1, 1)
net.getLayer(class8).blobs = [pts.astype("float32")]
net.getLayer(conv8).blobs = [np.full([1, 313], 2.606, dtype="float32")]
4.逐帧分割视频并应用模型:
count = 0
success = True
while success:
success, frame = vs.read()
if frame is None:
break
frame = imutils.resize(frame, 500)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB)
scaled = frame.astype("float32") / 255.0
lab = cv2.cvtColor(scaled, cv2.COLOR_RGB2LAB) resized = cv2.resize(lab, (224, 224))
L = cv2.split(resized)[0]
L -= 50
net.setInput(cv2.dnn.blobFromImage(L))
ab = net.forward()[0, :, :, :].transpose((1, 2, 0)) ab = cv2.resize(ab, (frame.shape[1], frame.shape[0]))
L = cv2.split(lab)[0]
colorized = np.concatenate((L[:, :, np.newaxis], ab), axis=2) colorized = cv2.cvtColor(colorized, cv2.COLOR_LAB2BGR)
colorized = np.clip(colorized, 0, 1)
colorized = (255 * colorized).astype("uint8") cv2.imshow("Original", frame)
cv2.imshow("Colorized", colorized)
cv2.imwrite("./colorized_video_frames/frame%d.jpg" % count, colorized)
count += 1
key = cv2.waitKey(1) & 0xFF if key == ord("q"):
breakvs.release()
cv2.destroyAllWindows()
上述循环过程,通常需要一段时间。例如,这个视频的彩色化过程(8 分钟)有大约 14,000 帧,我在 MacBook Pro 上花了大约 3 个小时——2.9 GHz Core i7,16GB 2133 MHz RAM。
5.一旦你有了包含帧的文件,你必须“重新组合”它来创建一个视频。下面的函数可以做到这一点:
**def** convert_frames_to_video(pathIn, pathOut, fps):
frame_array = []
files = [f **for** f **in** os.listdir(pathIn) **if** isfile(join(pathIn, f))]
*#for sorting the file names properly*
files.sort(key = **lambda** x: int(x[5:-4]))
**for** i **in** range(len(files)):
filename=pathIn + files[i]
*#reading each files*
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width,height)
print(filename)
*#inserting the frames into an image array*
frame_array.append(img)
out = cv2.VideoWriter(pathOut,cv2.VideoWriter_fourcc(*'MJPG'), fps, size)
**for** i **in** range(len(frame_array)):
*# writing to a image array*
out.write(frame_array[i])
out.release()
请注意,根据您在计算机上安装的视频控制器,编解码器(*'MJPG ')应该更改。请查阅 OpenCV 文档。最终将会是一次“试错”的经历。
现在,您应该在彩色帧上应用上面的函数:
pathIn= './colorized_video_frames/'
pathOut = './colorized_videos/video.avi'
fps = 30.0
convert_frames_to_video(pathIn, pathOut, fps)
由此产生的“原始”视频可以在这里看到:
注意,视频明显没有声音。我所做的是用 iMovie 从 B&W 电影中剥离原始声音,并将其添加到彩色声音中。最后的结果在这里:
很酷,不是吗?;-)
6.向桑托斯·杜蒙致敬

借着给旧照片和视频上色的机会,我决定向上世纪最伟大的发明家之一阿尔贝托·桑托斯-杜蒙特致敬。
桑托斯-杜蒙(1873-1932)是巴西发明家和航空先驱,是为数不多的对轻于空气和重于空气的飞机的发展做出重大贡献的人之一。
桑托斯-杜蒙是一个富裕的咖啡生产商家族的继承人,他在巴黎致力于航空研究和实验,并在那里度过了他的大部分成年时光。在他早期的职业生涯中,他设计、制造并驾驶热气球和早期飞船,并于 1901 年 10 月 19 日因环绕埃菲尔铁塔的飞行而获得了德意志奖。然后,他转向了重于空气的机器,1906 年 10 月 23 日,他的 14-is 成为欧洲第一架动力重于空气的飞机,获得了法国航空俱乐部和国际航空联合会的认证。他坚信航空将开创一个世界和平与繁荣的时代,这让他自由地发表自己的设计,并放弃了各种创新的专利。
桑托斯-杜蒙确实是开放科学的早期专家,他的目标是为人类造福。一个例子是,他的 Demoiselle 飞机的所有计划都公开发表在 1909 年 12 月版的《大众力学》杂志上,其中写道:为了鼓励航空业,任何人都可以利用他的计划建造类似的机器。

这项工作的所有照片都是从第一个十年的 XX 世纪,并完全彩色的电脑,使用技术描述在这篇文章中,包括下面的视频。我希望你能欣赏它。
7.结论
一如既往,我希望这个项目可以帮助其他人找到进入令人兴奋的数据科学世界的方法!
详情和最终代码请访问我的 GitHub 资源库:mj robot-python 4 ds
来自世界南部的 Saludos!
我的下一篇文章再见!
谢谢你,
马塞洛
在机器学习中使用 SciKit 中的 ColumnTransformer 代替 LabelEncoding 和 OneHotEncoding 进行数据预处理
在一篇非常老的帖子中,我展示了如何使用标签编码和一个热编码将分类文本数据分成数字和不同的列。但是自从我写了那篇文章之后,SciKit 库已经有了很大的进步,它让生活变得更加容易。
库的开发者可能已经意识到人们非常频繁地使用标签编码和一个酒店编码。所以他们决定开发一个名为 ColumnTransformer 的新库,它将 LabelEncoding 和 OneHotEncoding 合并成一行代码。结果是完全一样的。在这篇文章中,我们将快速地看一下我们如何用一些代码片段做到这一点。

代码
首先,像往常一样,我们需要导入所需的库。我们将按照惯例处理混叠:
import numpy as np
import pandas as pd
接下来,让我们将一些数据放入一个变量,看看我们在处理什么:
dataset = pd.read_csv("/home/sunny/code/machine_learning/samples/sample.csv")
您可以使用 Spyder 中的“变量浏览器”视图来查看数据。在我的例子中,大概是这样的:

正如你可以清楚地看出,这些数据毫无意义,显然只是为了这个演示。无论如何,这里的第一列是一个文本字段,在某种意义上是分类的。因此,我们将不得不标记编码,这也是一个热编码,以确保我们不会与任何层次结构。为此,我们仍然需要在代码中导入 OneHotEncoder 库。但是我们将使用新的 ColumnTransformer ,而不是 LabelEncoder 库。所以我们先导入这两个:
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
接下来,我们必须创建一个 ColumnTransformer 类的对象。但是在我们这样做之前,我们需要理解类的构造函数签名。ColumnTransformer 构造函数接受相当多的参数,但是我们只对两个感兴趣。第一个参数是一个名为 transformers 的数组,它是一个元组列表。该数组以相同的顺序包含以下元素:
- 名称:柱形变压器的名称,便于设置参数和查找变压器。
- transformer :这里我们应该提供一个估计器。如果我们愿意,我们也可以只“通过”或“丢弃”。但是由于我们在这个例子中对数据进行编码,我们将在这里使用 OneHotEncoder。请记住,您在这里使用的估计器需要支持拟合和变换。
- 列:要转换的列的列表。在这种情况下,我们将只转换第一列。
我们感兴趣的第二个参数是余数。这将告诉转换器如何处理数据集中的其他列。默认情况下,转换器将只返回被转换的列。所有其他列都将被删除。但是我们可以选择告诉转换器如何处理其他列。我们可以丢弃它们,不加修改地传递它们,或者指定另一个估计器,如果我们想做更多的处理。
现在我们(多少)理解了构造函数的签名,让我们继续创建一个对象:
columnTransformer = ColumnTransformer([('encoder', OneHotEncoder(), [0])], remainder='passthrough')
正如您在上面的片段中看到的,我们将把转换器简单地命名为“编码器”我们使用 OneHotEncoder()构造函数来提供一个新的实例作为估计器。然后我们指定只有第一列需要转换。我们还确保剩余的列没有任何变化地通过。
一旦我们构建了这个 columnTransformer 对象,我们就必须将数据集调整并转换为标签编码和热编码列。为此,我们将使用以下简单的命令:
dataset = np.array(columnTransformer.fit_transform(dataset), dtype = np.str)
如果您现在在变量资源管理器视图中检查您的数据集,您应该会看到类似如下的内容:

如您所见,我们仅使用 ColumnTransformer 类就轻松地对数据集中的一列进行了标签编码和热编码。这比同时使用 LabelEncoder 和 OneHotEncoder 类要简单和干净得多。
在推特上关注我,了解更多数据科学、机器学习,以及通用技术更新。此外,你可以关注我的个人博客,因为我在 Medium 之前发布了许多我的教程、操作方法帖子和机器学习的优点。
如果你喜欢我在 Medium 或我的个人博客上的帖子,并希望我继续做这项工作,请考虑在 Patreon 上支持我。
ColumnTransformer 符合自然语言处理

Photo credit: Pixabay
如何在 scikit-learn 管道中将几个特征提取机制或转换组合成单个转换器
自从发表了几篇关于文本分类的文章后,我就收到了关于如何处理混合输入特征类型的询问,例如,如何在分类或回归模型中组合数字、分类和文本特征。
因此,我决定用一个例子来回答这个问题。
有几种不同的方法来追加或组合不同的特征类型。一种方法是使用[scipy.sparse.hstack](https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.hstack.html)水平堆叠稀疏矩阵。
不过我就介绍一个方法,新上线的热门宝贝: ColumnTransformer 函数来自 sklearn 。如果你想尝试一下,你需要将你的 sklearn 升级到 0.20。
数据
这个演示的一个优秀数据集是 Mercari 价格建议挑战赛,它建立了一个机器学习模型来自动建议正确的产品价格。数据可以在这里找到。
load_data.py

Table 1
我们的数据包含异构数据类型,它们是数字、分类和文本数据。我们希望对这些不同类型的列使用不同的预处理步骤和转换。
例如:我们可能希望对分类特征进行一次性编码,并对文本特征进行 tfidf 矢量化。
“价格”是我们将要预测的目标特征。
数据预处理
目标特性—价格
df.price.describe()

Figure 1
- 去除价格= 0,探究其分布。
price_dist.py

Figure 2
目标功能价格向右倾斜。由于线性模型喜欢正态分布的数据,我们将转换价格,使其更正态分布。
log1p(price)_dist.py

Figure 3
df["price"] = np.log1p(df["price"])
特征工程
- 用“其他”填充缺少的“类别名称”,并将“类别名称”转换为类别数据类型。
- 用“未知”填写缺失的“品牌名称”。
- 确定热门品牌,其余设置为“其他”。
- 用“无”填充缺少的“项目描述”。
- 将“项目描述标识”转换为类别数据类型。
- 将“brand_name”转换为类别数据类型。
mercari_feature_engineering.py
我们的特点和目标:
target = df.price.values
features = df[['name', 'item_condition_id', 'category_name', 'brand_name', 'shipping', 'item_description']].copy()
拆分训练集和测试集中的数据:
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size = 0.2, random_state=0)
下面是如何应用 ColumnTransformer 。令人惊讶的是,它非常简单。
- 对“项目条件标识”和“品牌名称”进行编码。
- 计数矢量器“类别名称”和“名称”。
- tfidf 矢量器“item_description”。
- 我们可以通过设置
remainder='passthrough'来保留剩余的“shipping”特性。这些值被附加到转换的末尾:
columntransformer.py
模型和评估
我们将基于 ColumnTransformer 的预处理步骤与管道中的回归相结合来预测价格。
model_eval.py

Jupyter 笔记本可以在 Github 上找到。享受这周剩下的时光吧!
参考资料:
[## 4.1.管道和复合估算器-sci kit-了解 0.20.3 文档
转换器通常与分类器、回归器或其他估计器结合,以构建复合估计器。的…
scikit-learn.org](https://scikit-learn.org/stable/modules/compose.html#column-transformer) [## 介绍 ColumnTransformer:将不同的转换应用到
真实世界的数据通常包含不同的数据类型。在应用最终预测之前处理数据时…
jorisvandenbossche.github.io](https://jorisvandenbossche.github.io/blog/2018/05/28/scikit-learn-columntransformer/)
组合学:排列、组合和处置

组合学是数学的一个领域,主要研究对一个或多个集合中的元素进行计数。它可以帮助我们统计可能发生的订单的数量。
在本文中,我将详细介绍三种不同类型的技术:
- 排列
- 性情
- 组合
排列
这些是最容易计算的。想象我们有 n 个物体,彼此不同。排列是这些物体的任何可能的排列。
考虑下面的例子。我们有一个盒子,里面有一些球(每个球有不同的颜色),我们想计算排列这些球的方式。我们可以用两种不同的方法做到这一点:重复(每个球在被捡起后都被放回盒子里)或者不重复。
- 重复:这个想法是,在每一个球被提取后,我们可以根据自己的需要任意多次再次选择它。让我们简单地开始,考虑盒子={g,b},其中 g= '绿色球',b= '蓝色球':嗯,在这种情况下,我们可以排列那些球的可能方式是' gg ',' bg ',' gb ',' bb '。我们也可以用 Python 来计算:
box_1=['g','b']
perm=[]
for p in itertools.product(listA, repeat=2):
perm.append(p)
perm
Output:
[('g', 'g'), ('g', 'b'), ('b', 'g'), ('b', 'b')]
让我们用 3 个球来代替:
box_2 = ['g','b','y']
perm=[]
for p in itertools.product(box_2, repeat=3):
perm.append(p)
perm
Output:
[('g', 'g', 'g'),
('g', 'g', 'b'),
('g', 'g', 'y'),
('g', 'b', 'g'),
('g', 'b', 'b'),
('g', 'b', 'y'),
('g', 'y', 'g'),
('g', 'y', 'b'),
('g', 'y', 'y'),
('b', 'g', 'g'),
('b', 'g', 'b'),
('b', 'g', 'y'),
('b', 'b', 'g'),
('b', 'b', 'b'),
('b', 'b', 'y'),
('b', 'y', 'g'),
('b', 'y', 'b'),
('b', 'y', 'y'),
('y', 'g', 'g'),
('y', 'g', 'b'),
('y', 'g', 'y'),
('y', 'b', 'g'),
('y', 'b', 'b'),
('y', 'b', 'y'),
('y', 'y', 'g'),
('y', 'y', 'b'),
('y', 'y', 'y')]
现在我们的实验有 27 种可能的结果。如果我们想概括,当我们有 n 个对象,我们想看看我们可以用多少种方式排列它们,我们有:

- 没有重复:在这种情况下,一旦你选择了一个球,它就不能再使用了。所以球的每种排列都有独特的值。在这种情况下,回到我们的 box={g,b},两种可能的排列是“gb”和“bg”:
import itertools
box_1 = ['g','b']
perm = itertools.permutations(box_1)
for i in list(perm):
print(i)
Output:
('g', 'b')
('b', 'g')
同样,让我们考虑一个更大的盒子={g,b,y},其中 y= '黄色球':
box_2 = ['g','b','y']
perm = itertools.permutations(box_2)
for i in list(perm):
print(i)
Output:
('g', 'b', 'y')
('g', 'y', 'b')
('b', 'g', 'y')
('b', 'y', 'g')
('y', 'g', 'b')
('y', 'b', 'g')
在这种情况下,我们必须考虑,在每次提取之后,可用元素的数量都要少一个。因此,如果我们的集合中有 n 个元素,排列将是:

为了让您判断有重复和无重复排列之间的差异,让我们想象一下上面的例子。我们有一个盒子,里面有 4 个不同颜色的球:

我们想计算这些球可能排列的数量,这意味着:我可以用多少种方式排列这些球,每次从盒子里挑选一个球?
如果每次提取后都没有替换,那么在第一阶段,有 4 种方法可以设置第一个元素(黄色、绿色、橙色和蓝色):

然后,我们一个接一个地挑选其他的球,在每个阶段,我们排列剩余球的方式都在减少:

因此,最终,我们有 24 种可能的方法来排列这些对象,因为:

另一方面,如果在第二阶段(以及接下来的阶段),我们重新插入取出的球:

等于 256。
性情
部署只不过是排列,其中我们要挑选的对象的数量小于对象的总数 n。让我们简单地检索上面的示例,假设在三个球中,我们只想排列第一和第二个位置。让我们使用 box={g,b,y}并且让我们从重复的情况开始:
- 重复:我们要从三个球(n=3)中选择两个球(k=2),并计算可能排列的数量:
box_2 = ['g','b','y']
perm=[]
for p in itertools.product(box_2, repeat=2):
perm.append(p)
perm
Output:
[('g', 'g'),
('g', 'b'),
('g', 'y'),
('b', 'g'),
('b', 'b'),
('b', 'y'),
('y', 'g'),
('y', 'b'),
('y', 'y')]
这种情况下有 9 种可能的排列,而不是 27 种。与前一种情况一样,第一种选择有 n 种可能性,然后第二种选择又有 n 种可能性,以此类推,每次都相乘。但是这一次,这些将不是针对对象的总数(n)而是针对我们感兴趣的对象的数量(k)而被相乘。所以我们有:

- 没有重复:如果没有重复,同样的推理成立。事实上:
box_2 = ['g','b','y']
perm = itertools.permutations(box_2,2)
for i in list(perm):
print(i)
Output:
('g', 'b')
('g', 'y')
('b', 'g')
('b', 'y')
('y', 'g')
('y', 'b')
在这种情况下,我们有:

再一次,让我们想象一下,考虑一个有 4 个球的盒子,我们只需要安排其中的两个。对于重复的倾向,我们有:

而对于没有重复的处置,我们有:

组合
组合是排列(或置换,如果 k=n ),其中顺序无关紧要。基本上,每当我们想计算有多少种方法时,我们就使用组合,从 n 个对象中,我们可以提取其中的 k 个,,不管这些对象被挑选的顺序如何。
也就是说,如果你回忆起我们在最开始检查的没有重复的排列,其输出是“gb”和“bg”,等效的组合将只有“gb”(或只有“bg”),因为顺序无关紧要,因此它们代表相同的对象。
让我们看看 python,总是分别检查重复和不重复的两种情况:
- 有重复:
from itertools import combinations_with_replacement
box_1=['g','b']
comb = combinations_with_replacement(box_1, 2)
for i in list(comb):
print(i)
Output:
('g', 'g')
('g', 'b')
('b', 'b')
如您所见,第四种排列“bg”不在这些组合中,因为它等同于“gb”。
这同样适用于 3 个球(让我们只组合其中的两个):
from itertools import combinations_with_replacement
box_2=['g','b','y']
comb = combinations_with_replacement(box_2, 2)
for i in list(comb):
print(i)
Output:
('g', 'g')
('g', 'b')
('g', 'y')
('b', 'b')
('b', 'y')
('y', 'y')
- 无重复:
from itertools import combinations
comb = combinations(box_1, 2)
for i in list(comb):
print(i)
Output:
('g', 'b')
有三个球:
from itertools import combinations
comb = combinations(box_2, 2)
for i in list(comb):
print(i)
Output:
('g', 'b')
('g', 'y')
('b', 'y')
可能组合的数量(无重复)由下式给出:

这就是所谓的二项式系数,它被用在二项式概率分布中,用来计算在 n 次试验中有多少种方法可以获得 k 次成功。另一方面,如果我们允许重复,二项式系数变成:

再一次,让我们用我们的球盒想象它,重复:

没有重复:

原载于 2019 年 9 月 7 日http://datasciencechalktalk.com。
机器学习中实际数据与模拟数据的结合
使用蒙特卡罗方法从给定的样本数据中模拟更多的数据

使用机器学习,你需要大量的数据,所以有时将实际数据与模拟数据相结合是有意义的,以便在数据中找到正确的模式或用于模型建立。
Central limit 表示数据集越大越好,因为当样本量变得非常大时,总体均值接近样本均值。通过生成样本数据集的复制副本,我们可以估计不同人群的样本均值,以确保均值是稳定的。我们也可以在平均值的预测中估计方差(不确定性)。
为了从给定的样本数据中模拟数据,我们需要能够识别样本数据中的模式,还需要了解一些关于特征及其分布的知识。例如,如果我有一个男性身高的小样本,并且我知道身高遵循正态分布,我可以使用以下公式生成男性身高的数据集:
mu = mean(male_heights)sd = sd(male_heights)N = 1000simulated_data = rnorm(N, mu,sd)
本文将讨论如何使用蒙特卡罗(MC)模拟来生成样本数据集的近似复制副本。然后对这些数据块中的每一个进行训练。然后,通过对包括实际和模拟数据的数据块执行整体平均,计算模型的整体性能。
简介:预测贷款的状况是风险评估中的一个重要问题。银行或金融机构在向客户发放贷款之前,必须能够估计所涉及的风险。数据科学和预测分析在构建可用于预测贷款违约概率的模型中发挥着重要作用。在这个项目中,我们获得了包含 50000 个数据点的 loan_timing.csv 数据集。每个数据点代表一笔贷款,提供两个特征如下:
- 标题为“自发起以来的天数的列表示发起和数据收集日期之间经过的天数。
- 对于在收集数据之前销账的贷款,标题为“从发放到销账的天数的列表示发放和销账之间的天数。对于所有其他贷款,此栏为空白。
技术术语的定义
- 发放:指借款人从贷款人处收到贷款的日期。
- 冲销(贷款违约)状态:借款人定期还款,直到借款人在贷款期限结束前停止还款,通常是由于财务困难。这一事件被称为冲销,然后贷款被称为已冲销或处于违约状态。
- 当前或活跃状态:借款人在整个贷款期限内继续还款。至此,债务已全部还清。
- 贷款期限:贷款协议生效的期限,在此期限之前或结束时,贷款应被偿还或重新协商另一期限。在这个例子中,我们考虑一笔期限为 3 年的贷款。
项目目标:该项目的目标是使用数据科学的技术来估计这些贷款(在 loan_timing.csv 数据集中的 50,000 条客户记录)在 3 年贷款期限内将被冲销的比例。
本文的数据集和 R 代码可以从这个资源库下载:https://github . com/bot 13956/Monte _ Carlo _ Simulation _ Loan _ Status。
使用 R 的模型实现
1.导入必要的库
library(readr)
library(tidyverse)
library(broom)
library(caret)
2。导入数据集并为分析准备数据
df<-read_csv("loan_timing.csv",na="NA")names(df)=c("origination","chargeoff")**# partition data set into two: default (charged off ) and current**index<-which(!(df$chargeoff=="NA"))default<-df%>%slice(index)current<-df%>%slice(-index)
3。探索性数据分析
A)实际数据
**# Plot of days to charge-off vs. days since origination for defaulted loans using actual data**default%>%ggplot(aes(origination,chargeoff))+geom_point()+xlab('days since origination')+ ylab('days to charge-off')+ggtitle("days to charge-off vs. days since origination")+theme(plot.title = element_text(color="black", size=12, hjust=0.5, face="bold"),axis.title.x = element_text(color="black", size=12, face="bold"),axis.title.y = element_text(color="black", size=12, face="bold"),legend.title = element_blank())
B)模拟数据
**# Monte Carlo Simulation of Defaulted Loans**set.seed(2)N <- 3*365 # loan duration in daysdf_MC<-data.frame(u=round(runif(15500,0,N)),v=round(runif(15500,0,N)))df_MC<-df_MC%>%filter(v<=u)df_MC<-df_MC%>%filter(u<=730 & v<=730) #select loans within first 2 yearsdf_MC[1:nrow(default),]%>%ggplot(aes(u,v))+geom_point()+xlab('days since origination')+ylab('days to charge-off')+ggtitle("MC simulation of days to charge-off vs. days since origination")+theme(plot.title = element_text(color="black", size=12, hjust=0.5, face="bold"),axis.title.x = element_text(color="black", size=12, face="bold"),axis.title.y = element_text(color="black", size=12, face="bold"),legend.title = element_blank())

Actual and MC simulation of days to charge-off vs. days since origination.
因为存在与贷款冲销相关的随机性,我们看到 MC 模拟为违约贷款的分布提供了一个很好的近似。
预测:由于我们已经证明,在最初 2 年(即 0 至 730 天)中,可以使用 MC 模拟来近似计算待冲销天数和自发放以来天数之间的关系,因此我们可以使用 MC 模拟来预测在所有 3 年期限结束时将被冲销的贷款比例。
我们数据集中冲销贷款的总数是 3,305。这意味着目前有 46,695 笔贷款处于活跃状态。在这些活跃的贷款中,一定比例的贷款将在 3 年内违约。为了估计违约贷款的总比例,我们模拟了涵盖整个贷款期限(即 0 至 1095 天)的违约贷款的冲销和自发放以来的天数,然后通过适当的缩放,我们计算了在 3 年期限(即 1095 天)后将冲销的贷款比例。
**# Predicting fraction of these loans will have charged off by the time all of their 3-year term is finished.**set.seed(2)B<-1000fraction<-replicate(B, {df2<-data.frame(u=round(runif(50000,0,N)),v=round(runif(50000,0,N)))df2<-df2%>%filter(v<=u)b2<-(df2%>%filter(u<=730 & v<=730))total<-(nrow(df2)/nrow(b2))*nrow(default)100.0*(total/50000.0)})
mean(fraction)**# Histogram of total fraction of charged off loans**fdf<-data.frame(fraction=fraction)fdf%>%ggplot(aes(fraction))+geom_histogram(color="white",fill="skyblue")+xlab('fraction of charged off loans after 3-year term')+ylab('count')+ggtitle("Histogram of total fraction of charged off loans")+theme(
plot.title = element_text(color="black", size=12, hjust=0.5, face="bold"),
axis.title.x = element_text(color="black", size=12, face="bold"),
axis.title.y = element_text(color="black", size=12, face="bold"),
legend.title = element_blank()
)**# Calculate Confidence Interval of Percentage of Defaulted Loans after 3-year term**mean<-mean(fraction)sd<-sd(fraction)confidence_interval<-c(mean-2*sd, mean+2*sd)confidence_interval
通过创建 N = 1000 次随机试验,我们获得了 3 年期违约贷款比例的以下分布:

图 6:使用 N = 1000 个样本的 3 年期后冲销贷款比例直方图。
根据我们的计算,3 年贷款期限后将被冲销的贷款部分的 95%置信区间相应地为 14.8% +/- 0.2%。因此,如果发放贷款期限为 3 年的 50,000 笔贷款,这些贷款中大约有 15%将会违约。
结论:我们提出了一个基于 MC 模拟的简单模型,用于预测在 3 年贷款期限结束时违约的贷款比例。蒙特卡洛模拟是一种重要的方法,可用于规定分析中,规定在数据集本质上非常随机的情况下要采取的行动过程。
本文的数据集和 R 代码可以从这个资源库下载:https://github . com/bot 13956/Monte _ Carlo _ Simulation _ Loan _ Status。
组合熊猫数据帧:简单的方法
Cheatsheet 版本:何时使用 Concat,何时使用 Merge

当我第一次使用 Python 进行数据分析时,我真的不知道什么时候使用追加、连接、合并或连接。一开始,每当我试图合并两个数据框时,我都要用谷歌搜索。因此,我想在这里分享我的经验,并给出一个简单的结合数据框架的介绍。
拼接合并就够了
您可以使用 concat 和 merge 做任何事情,因此不需要在开始时使用 append 和 join。由于在某些情况下代码更短,所以使用追加和连接可以节省一些时间。基本上,您可以用 append 和 join 做的所有事情,也可以用 concat 和 merge 来做。这就是为什么我们将重点放在这两个命令上。
如果您尝试合并两个数据集,首先要做的是决定是使用 merge 还是 concat。找到正确答案有一个简单的规则。如果数据帧的内容与组合数据帧相关,则必须选择 merge,否则可以选择 concat:

让我们从 Concat 开始
你可以用上面的图片作为开始的备忘单。假设您在公司的数据科学部门工作,销售部门每月都会向您发送新的销售数据。上个月和本月的数据框架中有两列:

Fig 1: Df_sales on the left side and df_sales2 on the right side
第一列包含关于经销商的信息,第二列包含去年售出的单位数量。因此,数据集的结构每个月都是相同的,你只需将两个数据框架组合起来。在这种情况下,您需要使用 concat:

Fig 2: The first use of concat with axis=0
Ignore_index=True用于创建新的索引。如果您不考虑它,您将保留原始索引。
在这种情况下,我们将两个数据帧组合在一起。如果您想并排组合两个数据帧,您必须用axis=1指定命令:

Fig 3: Concat with axis=1
数据帧的内容与合并它们相关——如何使用 Merge
在第一个例子中,我们每个月都从销售部门收到具有相同数据结构的文件。现在想象一下,市场研究部门给你发送市场数据进行比较。对于每个经销商 ID,您还会收到市场数据,其中包括竞争对手的销售额:

Fig 4: The sales data on the left side and the market data on the right side
但是正如你在上面的图片中所看到的,你没有每个经销商所需要的市场数据。因此,您有三个选项来合并上面的数据:
1.您只保留两个数据帧的交集(这意味着索引从 0 到 9 的行):
一号和二号
2.您保留左侧或右侧数据帧的所有信息,而其他数据帧仅保留匹配信息:
1 号、2 号和 3 号或者 1 号、2 号和 4 号
3.您保留两个数据帧的所有信息:
1 号,2 号,3 号和 4 号

选项 1-两个数据帧的交集
你所在部门的老板要求从总份额中获得经销商销售额的份额。因此,他只需要经销商和市场销售可用的经销商 id。在这种情况下,您只需保留具有相同经销商 id 的数据集部分。您必须使用内部连接来保持两个数据帧的交集:

Fig 5: The intersection of the sales and market data
注意:如果不指定“how”参数,默认选项将是内部联接。
选项 2 —一个数据帧的全部内容和另一个数据帧的匹配部分
您从两个不同的部门接收数据集。销售部门的一个要求可能是,他们想要回他们的原始数据框架和匹配的市场数据:

Fig 6: The full content of the sales data and the matching part from the market data
在这种情况下,您必须执行左连接。只有在市场数据集中找到销售数据中的经销商 ID 时,才需要添加市场数据。
选项 3 —保留两个数据帧的所有信息
最后,市场部门希望在不丢失任何信息的情况下将两种数据框架结合起来:

Fig 7: The outer join
在生成的数据帧中,两个基本数据帧中没有一个值丢失。最后,外部连接就像一个左连接和一个右连接的组合。
结论和进一步阅读
我希望您喜欢这篇介绍,它有助于阐明 concat 和 merge 命令背后的逻辑。从一开始就有了备忘单,你应该能够决定你必须使用哪个命令。要了解更多信息,我可以推荐以下资源:
- 官方文件
[## 熊猫。DataFrame.merge - pandas 0.25.0 文档
用数据库样式的联接合并数据框架或命名系列对象。联接是在列或索引上完成的。如果加入…
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html)
2.关于 concat 和 merge 区别的 stackoverflow 讨论
[## pandas 中 merge()和 concat()的区别
感谢贡献一个堆栈溢出的答案!请务必回答问题。提供详细信息并分享…
stackoverflow.com](https://stackoverflow.com/questions/38256104/differences-between-merge-and-concat-in-pandas)
3.关于归并和连接区别的讨论
假设我有两个这样的数据帧:left = pd。DataFrame({'key1': ['foo ',' bar'],' lval': [1,2]}) right =…
stackoverflow.com](https://stackoverflow.com/questions/22676081/what-is-the-difference-between-join-and-merge-in-pandas/37891437)
如果您喜欢中级和高级数据科学,并且还没有注册,请随时使用我的推荐链接加入社区。
结合 python 和 d3.js 创建动态可视化应用程序

视觉形式是催眠和逮捕,不像任何其他媒体。一幅画或一幅图像迫使眼睛看到整个画面,并呈现出一种不受时间限制的形式。难怪视觉在试图理解和解决复杂问题时有助于采用非线性视角。
另一方面,通过数据分析和编程来解决问题仍然在很大程度上植根于线性视角,因为它们涉及到逐步分解数据来理解和解决问题。然而,数据分析如果做得正确,可以让用户考虑到细枝末节的细节和特质,而这些细节和特质通常是在观察全局时被忽略的。将数据驱动方法与更加可视化的方法相结合,提供了一种结合线性和非线性视角的问题解决和分析的整体方法。在本文中,我通过一个详细的、可重复的例子解释了用户如何结合 python(一种强大的数据处理编程语言)和 d3.js(一种强大的生成可视化的语言)来创建一个可视化应用程序,为问题解决者提供有用的见解。
我将演示的是用户如何有效地在 python 中创建数据处理后端,同时在 d3.js 中维护可视化前端,以创建有效的应用程序。我们还将添加一些可控制的功能,以便前端和后端可以根据最终用户的输入有效地相互通信。我们将使用的 python 模块是“Flask ”,它将充当后端和前端之间的中介。我选择的 d3 可视化是 Mike Bostock 创建的可折叠条形图示例。可视化结构的创建将涉及到 html、js 和一些 jinja 代码的使用。
问题—
我们将使用来自 FAOSTAT 数据库的农业生产数据。粮农统计数据库提供了 213 个地区不同年份的几个变量的数据,按作物类型、肉类和鱼类分类。我们将通过一个动态可视化应用程序,尝试了解和探索 FAOSTAT 数据在不同时间、不同国家间的汇总和分解情况。您可以在这里找到用于本例的编辑过的数据集。我们将使用两个数据集,一个是按不同类型的作物、肉类和鱼类分列的产量,另一个是按相同类别分列的农业损失。这两个数据集都包含 213 个地区从 2010 年到 2013 年的数据。我们将创建一个应用程序,帮助用户使用可折叠的条形图可视化来比较任何类别或子类别的损失和产量。用户还应该能够选择任何国家和年份来创建这些国家的可视化。基本上,最终(编辑)的产品看起来像下面的图片和 gif,

See the gif below to see how the app works

This gif demonstrates the usage of the application on a local machine
第 1 部分:定义应用程序的结构
我们将要做的,是在一个 html 页面上创建一个前端,它将托管我们的可视化和 d3.js 脚本。我们将把包含在名为“application.py”的文件中的 python 代码中的数据发送到这个 html 页面。计算机上应用程序的结构如下。
#Application file structureroot/ static/
This folder contains your supplementary JavaScript files
/templates
This folder contains the index.html fileapplication.py (The main python file and data are hosted in the root folder)
下面是该应用程序的图示

Source: Author’s conception
第 1 部分:定义前端(html,d3.js)
首先,让我们设计前端,这将是一个基本的 html 页面(“index.html”),它将承载我们的 d3 可视化以及一个用户可以提交国家和年份选择的表单。最终的 html 在这里托管。我不会走一些基本的东西,如 css 和格式等。你可以直接从 html 中获取或者根据你的喜好定制。创建基本 html 页面的步骤如下,
1.获取所需的所有脚本
2.创建一个用户可以更改国家和年份选择的表单。
3.创建“div”元素来承载可视化效果
4.插入 d3 代码来创建图形。我们还将使用 jinja 代码定义 python 后端和 d3 之间的链接。
您需要有基本的 d3 版本( d3.v3.min.js) ,您可以使用这个命令将它放入 html,
<script src=”https://d3js.org/d3.v3.min.js"></script>
让我们首先创建用户可以提交国家和年份信息的表单。这可以通过一些 html 代码来完成,这些代码将生成一个用户可以提交请求的“表单”。请注意,下面指定的名称(如“Country_field”和“Year_field ”)非常重要,因为在 python 的后端会再次引用这些名称。
<form method=”post” ><input name=”Country_field” placeholder=”Enter country name” > <input type=”number” name=”Year_field” placeholder=”Enter Year” > <input type=”submit” value=”Submit Country and Year” > </form>
现在,我们将创建两个分区,一个在左边托管生产数据图,另一个在右边托管损失数据。分部还应显示所选的国家和年份。我们必须为图表指定 id,并且必须用 jinja 编写一些代码来获取国家名称和年份。jinja 代码基本上使用花括号{{}}从 python 访问数据。将类别分配给分部有助于以后轻松添加格式。同样的代码是,
<div class=”left-div” id=”graphDiv” style=”border: thin solid black”>
<p>
<b>
<i>
Production data by category and sub-categories in 1000 tonnes for {{CountryName}}(Click on the bars to explore sub-divisions)</b>
<br> <br>
FAO defines production as “Unless otherwise indicated, production is reported at the farm level for crop and livestock products (i.e. in the case of crops, excluding harvesting losses) and in terms of live weight for fish items.”
</i>
</p>
</div>
上面的代码产生了这个结果,

Production division in html
我们将不得不重复相同的代码来为损失数据创建另一个分部。之后,让我们定义 d3 函数来创建图表。我定义了两个函数,svg1 和 svg2 分别用于左侧和右侧的图。我使用的代码大部分来自 Mike Bostock 的例子这里。我们只会做一些改动。首先,在代码中定义 svg 对象的地方,我们必须引用我们的 graph div ids,以便函数将在我们上面所做的划分中创建图形。所以,在我们的例子中,
var svg1 = d3.select(“**#graphDiv**”)
.append(“svg”)
.attr(“width”, width + margin.left + margin.right)
.attr(“height”, height + margin.top + margin.bottom)
.append(“g”)
.attr(“transform”, “translate(“ + margin.left + “,” + margin.top + “)”);
现在,如上所述,后端数据处理器将在 python 中构建。因此,我们必须使用下面的代码将数据从 python 传递到 js 脚本。“/get-data”是一个函数,我们将在稍后的 python 代码中定义它。“d3.json”将以 json 格式读入数据。
d3.json(“**/get-data**”, function(error, root){
partition.nodes(root);
x.domain([0, root.value]).nice();
down(root, 0);});
最后,我们在代码中对条形的颜色做了一点小小的调整。我们希望生产图用绿色条,损失图用蓝色条。我们将通过改变下面代码中的颜色变量来改变颜色,
var color = d3.scale.ordinal()
.range([“green”, “#ccc”]);
第 3 部分:用 python (flask)创建后端
创建 python 文件的步骤要花费更多的时间。最终的申请文件可在这里获得。我们需要执行以下步骤,
1.导入必要的包,在 flask 中定义应用程序并创建一个数据存储。
2.创建代码以生成发送到主页前端的数据。
3.将数据转换为 d3 的 json 格式,并发送到前端
4.类似地,专门为产量和损失图定义函数。
好了,让我们把简单的事情解决掉。让我们获取包,定义 flask 应用程序并创建一个包含 4 个变量的数据存储函数。datastore 变量将有助于稍后在将数据传递给前端之前保存数据。我们将创建一个“CountryName”变量和一个“Year”变量,用户将通过表单将这两个变量发送到应用程序。我们将创建一个“Prod”变量来存储生产数据,并创建一个“Loss”变量来存储损失数据。
#1\. Import packages**from** flask **import** Flask, flash, redirect, render_template, request, session, abort,send_from_directory,send_file,jsonify
**import** pandas **as** pd
**import** json
*#2\. Declare application*app= Flask(__name__)
*#3\. Create datastore variable***class** DataStore():
CountryName=**None** Year=**None** Prod= **None** Loss=**None** data=DataStore()
现在,让我们定义应用程序的主页。我们首先必须定义到主页的路径和一个主页功能,该功能将为主页创建数据。基本上正在发生的是,当用户访问主页面时,主页功能将被调用。我们还将使用一个名为“request”的简单 flask 函数从前端“获取”数据。当请求数据时,请注意我们使用 html 中定义的 id,如“Country_field”和“Year_field”。我们还为国家/地区设置了默认值印度,并为年度设置了 2013。我们还将把这个请求的数据传递给我们的数据存储函数变量‘Year’和‘country name’(数据存储变量和其他变量之间的区别解释如下)。最后,我们将读入生产数据,并为我们的分析创建名为 CountryName 和 Year 的变量。请注意,这些是将传递给 html 的实际变量,而不是存储在 python 内部的变量。一个很好的想法是,数据存储是 python 的内部内存,它会随着时间不断更新。静态临时变量是在单个时间点创建的值,将被传递到前端。如上所述,可视化是为 1 个时间点创建的,因此使用临时变量。
#We are defining a route along with the relevant methods for the #route, in this case they are get and post.
@app.route(**“/”**,methods=[**“GET”**,**”POST”**])#We are defining a home page function below. We will get the #CountryName and the Year from the form we defined in the html **def** homepage():
data.CountryName = request.form.get(**‘Country_field’**,**’India’**)
data.Year = request.form.get(**‘Year_field’**, 2013)
data.CountryName=CountryName
df = pd.read_csv(**‘CropsFull.csv’**)
CountryName = data.CountryName
Year= data.Year
现在,我们将根据从表单中收到的值过滤数据(df)。这是 python 中一个简单的过滤器。我们还将只保留相关的列以供进一步处理。我将 Year 变量转换为整数,因为有时请求会返回一个字符串,这可能会导致 python 无法过滤数据。
*# Filter the data frame (df)* df = df[df.Country == CountryName]
df = df[df.Year == int(Year)]#Keep only relevant columns
df = df[[**“Category”**, **“Cat”**, **“value”**]]
现在,我们需要将这个数据帧转换成一个分层的 json。json 根据数据中的聚合类别进行分层,因此对于可视化很有用。我已经附上了相同的代码如下。但是我使用了 Andrew Heekin 的代码来创建嵌套的 jsons。代码可以在这里找到。我不会在这里深入讨论代码的细节。
df1 = df.groupby([**‘Category’**, **‘Cat’**])[**‘value’**].sum()
df1 = df1.reset_index()*#Lets create a dict* d = {**"name"**: **"flare"**, **"children"**: []}
**for** line **in** df1.values:
Category = line[0]
Cat = line[1]
value = line[2]
*# make a list of keys* keys_list = []
**for** item **in** d[**'children'**]:
keys_list.append(item[**'name'**])
*# if 'the_parent' is NOT a key in the flare.json yet, append it* **if not** Category **in** keys_list:
d[**'children'**].append({**"name"**: Category, **"children"**: [{**"name"**: Cat, **"size"**: value}]})
*# if 'the_parent' IS a key in the flare.json, add a new child to it* **else**:
d[**'children'**][keys_list.index(Category)] [**'children'**].append({**"name"**: Cat, **"size"**: value})
flare = d
现在,我们必须将这些数据转储为 json 格式。每次我们给它赋值的时候,我们都可以使用 json load 函数来加载它。如上所述,让我们将这些数据保存到一个临时变量‘Prod’中以传递给前端,并保存到一个名为‘data’的 python 内存变量中。从我们的数据存储函数。
#Dump data to json
flare = json.dumps(flare)#Save to datastore
data.Prod = json.loads(flare)#Save to temporary variable
Prod=data.Prod
我们将使用上述步骤处理损失数据。我不会在这里重复完整的代码。损失代码的最后一行是,
#Dump data to json
flare = json.dumps(flare)#Save to datastore
data.Loss = json.loads(flare)#Save to temporary variable
Loss = data.Loss
最后,让我们用一个 return 语句来结束我们的函数。我们将使用 flask 'render_template '函数将数据发送到我们的前端(index.html '文件。我们还将返回所有临时变量,如国家名称、年份、产量和损失数据
**return** render_template(**“index.html”**,CountryName=CountryName,Year=Year,Prod=Prod,Loss=Loss)
上面的代码向主页面发送数据。我们还必须编写另外两个函数来将生产和损失数据发送给我们的 js 函数。假设我们有一个数据存储来记录我们的生产和损耗数据,这应该相当简单。让我们定义一个名为“/get-data”的路由,并将我们的生产数据发送给它。请注意,该函数返回数据的“jsonified”版本。
@app.route(**“/get-data”**,methods=[**“GET”**,**”POST”**])
**def** returnProdData():
f=data.Prod
**return** jsonify(f)
我们将在名为'/get-loss-data '的路径上为丢失数据创建一个类似的函数。
@app.route(**“/get-loss-data”**,methods=[**“GET”**,**”POST”**])
**def** returnLossData():
g=data.Loss
**return** jsonify(g)
最后,让我们定义运行应用程序的代码,
**if** __name__ == **"__main__"**:
app.run(debug=**True**)
这就是了。你的申请准备好了!继续运行它!运行代码时,您应该会得到以下消息,其中包含一个指向本地驱动器上的应用程序的链接。
* Running on [http://127.0.0.1:5000/](http://127.0.0.1:5000/) (Press CTRL+C to quit)
这个应用程序很容易部署在服务器上。我已经在免费的 heroku 服务器上部署了它。同样可以在这里访问。请注意,我使用的是 heroku 的免费版本,所以加载时间有点慢(您可能需要刷新应用程序几次)。我还添加了 requirements.txt 和。gitignore 和 procfile,以防您想将它部署到 heroku 或任何其他服务器上。这段代码显然很容易适应您喜欢的其他 d3 可视化!
非常感谢 Mike Bostock 创造了像 d3 这样精彩的语言,感谢 Andrew Heekin 编写了生成分层 jsons 的代码。感谢 David Bohl 和 Aditya Kulkarni 的反馈和评论。
为了您的参考和方便,我在下面附上了 github 库和其他资源的链接。
1.链接到 github 项目-【https://github.com/kanishkan91/FAO-FBS-Data-Explorer
2.链接到 heroku 服务器上部署的应用程序-【https://faoexplorer-flask-d3.herokuapp.com/
3.链接到 Mike Bostocks 可折叠条形图示例-https://observablehq.com/@d3/hierarchical-bar-chart
4.链接到 Andrew Heekin 创建分层 json 的代码-https://github . com/andrewheekin/cs v2 flare . JSON/blob/master/cs v2 flare . JSON . py
结合卫星图像和机器学习来预测贫困
这是尼尔·让等人对同名论文的 5 分钟评论。这是这篇文章的视频版本:https://youtu.be/bW_-I2qYmEQ。
P 发展中国家的过度评估影响着这些国家的政府如何分配有限的资源来制定政策和开展研究。
Neal Jean 等人在他们的论文中声称,他们通过使用机器学习和结合卫星图像,开发了一种检测和预测贫困的方法。
我们如何衡量广大地理区域的经济活动水平?一种可能性是观察它们的夜间发光强度。这些地区的卫星夜灯亮度图像与它们的经济活动水平之间存在关联。
然而,作者观察到,夜灯方法本身无法检测低于国际贫困线的地区的经济活动。参见图 1–2。

Figure 1

Figure 2
通常,政府依靠调查来收集经济数据并采取行动。但是应用这些调查并不简单,而且费用很高。
这就是作者提出的方法的用武之地。他们提出了一种基于“迁移学习”技术的机器学习方法,有望解决这一缺点,并提供比单独考虑亮度强度更高的预测精度。
他们声称他们预测贫困的方法是准确的、廉价的和可扩展的。他们是如何实现这种预测能力的?通过结合调查和卫星数据,可以训练卷积神经网络或 CNN 来辨别白天卫星图像上的特征。
在他们的研究中,他们考虑了 4 个非洲发展中国家,他们估计数据的粒度是在家庭层面。
他们的方法包括什么?它包括三个阶段(见图 3):
第一阶段他们训练 CNN 从日光卫星图像中学习特征。例如,这些特征是经济活动(或缺乏经济活动)的证据,如城市地区、非城市地区、水和道路。
阶段 2 利用阶段 1 中获得的知识,CNN 适于被训练以估计夜间光强度。
第 3 阶段涉及将经济调查数据和 CNN 从日光图像中提取的图像特征相结合,以训练回归模型,该模型能够估计他们正在考虑的贫困指标。

Figure 3
作者声称,他们的“迁移学习模型”可以高精度地预测他们的贫困指标。那些指标是什么?/他们处理两个问题,它们是消费支出和资产财富(他们声称已经分别实现了高达 55%和 59%的可变性解释。)参见图 4 和图 5。

Figure 4

Figure 5
在我看来,这篇论文提出了一种增强的、准确的和负担得起的技术,世界各地的政府和组织可以用它来跟踪和瞄准发展中国家的贫困,以采取缓解行动。它展示了机器学习在帮助改善人们的生活条件方面有多么强大。
我的网站是:【https://www.georgelopez-portfolio.com/
所有数字由尼尔·吉恩等人提供,并由 g·洛佩兹改编。
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
亲爱的读者:
我想知道你认为还有哪些使用 AI/ML 的人道主义应用可以实现?
你可以留下回应的评论,我会很乐意阅读。谢了。
结合监督学习和非监督学习改进词向量
生成性预培训简介

Photo by Edward Ma on Unsplash
为了在 NLP 任务中获得最先进结果,研究人员尝试了大量方法让机器理解语言并解决下游任务,如文本蕴涵、语义分类。OpenAI 发布了一个新的模型,命名为生成式预训练(GPT)。
看完这篇文章,你会明白:
- 微调变压器 LM 设计
- 体系结构
- 实验
- 履行
- 拿走
微调变压器 LM 设计
这种方法包括两个步骤。首先,通过基于大量数据的无监督学习来训练模型。第二部分是使用目标数据集(领域数据)通过监督学习对上一步的模型进行微调。
无监督学习
不可否认,NLP 的无标签数据是无限的。拉德福德等人认为,利用无限的语料库有助于训练一个通用模型,就像 word2vec (单词嵌入)和 skip-thought (句子嵌入)。我们不需要考虑训练数据的数量,因为我们可以很容易地获得大量的语料。
然而,仍然有一个限制。尽管我们可以尽可能多地使用语料库,但在大多数情况下,语料库与我们的领域数据是脱节的。从我以前的工作中,我注意到我的领域数据中的大部分单词并不存在于大量现成的单词嵌入模型中。
拉德福德等人没有使用 RNN 架构,而是使用变压器架构来训练第一个模型。因为他们相信变压器架构能够捕捉更远距离的信号(语言特征)。限制是高计算时间。由于拉德福德等人使用了 12 个自我注意块和高维内部状态,即使使用 GPU 也需要几周时间来训练初始模型。
监督学习
之后,目标数据集(与之前的数据集相比,在大多数情况下,它应该是一个小数据集)将被用于通过监督学习来微调模型。
体系结构
【无监督学习模型(Transformer)】
序列对序列(又名 RNN)模型有一个局限性,我们需要定义固定长度的上下文向量,这损害了记忆很长句子的能力。与此同时,注意力机制应运而生。该架构被称为“变压器”,是多头自我关注。在注意机制家族中,我们有许多不同的注意,拉德福德等人决定使用自我注意。
- 多头是指使用具有不同参数多个自我注意来计算表示。想想看,我们希望有多个专家来帮助找到一个更好的结果。多头机制并行执行具有不同参数相同计算。来自不同注意块的计算结果将被连接并转换到期望的维度。

Photo by JOSHUA COLEMAN on Unsplash
- 注意正在利用 CNN 的优势。自注意不依赖于先前的信息,因此它可以并行运行以实现更低的计算时间。同时,单词是直接计算所有已定义的单词,而不仅仅是环境。它克服了 RNN 缺乏记忆长句子能力的缺点。自我注意是注意机制家族的成员之一。注意力输入是 Q(查询)、K(键)和 V(值)。与其他成员不同,所有输入(Q、K 和 V)都是相等的。

Photo by Ari Spada on Unsplash
关于变压器的细节,你可以查看这张纸。回到架构,输入特征是文本和文本的位置来计算单词向量。文本位置是指输入的单词位置。流程是:
- 文本和位置将被转换成一个矢量
- 传递给多头自我关注
- 结合步骤 1 和步骤 2 的结果并执行归一化
- 传递到全连接的前馈网络
- 结合步骤 3 和 4 的结果并执行归一化
最后,将多头(共 12 个自关注块)组合在一起计算向量。

Transformer architecture (Radford et al., 2018)
型号规格为:
- 总共 12 台变压器
- 自我注意中的 768 维状态
- 位置前馈网络中的 3072 维内部态。
- 使用 Adam 优化,最大学习率为 2.5e-4
- 64 个小批量的 100 个时代
- 辍学率为 0.1%
监督学习
在训练了上一步的模型后,这种受监督的微调过程有助于获得目标任务的向量。假设输入是带有标签的输入符号序列,我们可以从预先训练的模型中得到一个符号的向量。

Input Transformations for fine-tuning on different tasks (Radford et al., 2018)
实验

Experimental Result on Natural Language Inference Tasks ( Radford et al., 2018)

Experimental Result on Question Answering and Commonsense Reasoning Tasks ( Radford et al., 2018)
拿走
- 展示了针对特定领域数据的微调能力。
- BERT 的设计与该模型相似,而 BERT 进一步改进了该模型的局限性。
- 作者注意到这个架构设计没有进一步的改进。(在 github 中提到)
关于我
我是湾区的数据科学家。专注于数据科学、人工智能,尤其是 NLP 和平台相关领域的最新发展。你可以通过媒体博客、 LinkedIn 或 Github 联系我。
参考
拉德福德·a·纳拉辛汉·k·萨利曼斯·蒂姆,苏茨基弗一世..2018.通过生成性预训练提高语言理解。
瓦斯瓦尼 a,沙泽尔 n,帕尔马 n,乌兹科雷特 j,琼斯 L,戈麦斯 a n,凯泽 L..2017.你所需要的只是注意力。
来和我一起飞吧!
分析航空业 twitter 数据的自然语言处理项目。人们在推特上谈论的话题和需要改进的地方!

在数据科学训练营的最后 3 周,我们接近了我们的最终项目。很难决定一个项目不仅能让科学展览会上的个人感兴趣,还能让公司案例分析感兴趣。我们需要在网上收集可用的数据,这是我努力的方向。找到涉及航空业的标记数据是一个挑战,但这一切都归功于文本和 NLP!
这个最终项目的概述包括分析国内 8 大航空公司的 twitter 数据,并使用 NLP(自然语言处理)来确定每家航空公司需要改进的地方!我总是阅读关于什么是最好的航空公司的评论,这样我就能够看到和分析人们实际上在推特上谈论什么。作为我的“最小可行项目”,我想解决两个主要问题
- 当人们在推特上谈论一家航空公司时,他们大多是负面的吗?
- 人们大多在谈论什么话题?
目标:
- 使用主题建模对每家航空公司的不同兴趣领域进行分类。
- 使用情绪分析来查看正面和负面的推文。
- 可视化我的数据!
我的过程很简单,也很清晰。我首先使用 Twint 网络抓取工具收集数据。我收集了以下 8 家国内航空公司的数据:
主要航空公司:
- 美国联合航空公司
- 美国航空
- 达美航空
- 阿拉斯加航空公司
廉价航空公司:
- 西南航空公司(最大的廉价航空公司)
- 捷蓝航空
- 前沿(超低价)
- 精神(超低价)
然后,我使用 pandas 和 python 清理了我的数据,清除了所有标点符号和拼写错误的单词。根据推文长度评估 twitter 数据,并对数据进行情感分析。Vader 是我用来识别带有评级的正面和负面推文的工具。查看以下每条推文的{}评分。
反面例子:
- “对联合航空公司非常不耐烦” {'neg': 0.73,' neu': 0.27,' pos': 0.0,' compound': -0.7964}
- {'neg': 0.867,' neu': 0.133,' pos': 0.0,' compound': -0.7579}
正面例子:
- “太棒了,我真的很爱很爱很爱那里的美丽” {'neg': 0.0,' neu': 0.178,' pos': 0.822,' compound': 0.9753}
建模
航空公司项目的下一步是使用主题建模来评估大多数人正面和负面谈论的内容。我使用了 LDA 模型和代表潜在狄利克雷分配的可视化。
在自然语言处理中, LDA 是一种生成统计模型,它允许通过未观察到的组来解释观察集,从而解释为什么数据的某些部分是相似的。

我做了一个连贯性分数测试来评估所学主题的质量。你可以看到我们在 3 个主题上的最高一致性分数是 0.44。

t-SNE Cluster Analysis to visualize no overlap between topics.

3D t-SNE Cluster Analysis

LDA Visualization
上面是一个 LDA 可视化,以便看到每个主题的分离。

Word Clouds
查看每个话题的每个词云。我惊讶地看到话题 0 中有一串负面词汇,话题 1 中有中性/计时词汇,话题 2 中有正面词汇。这意味着我们可以清楚地区分这些推文的不同区域。所以现在让我们看看哪个航空公司在每个话题上的优势!
*我根据权重较大的单词重命名了主题
- 主题 0 =糟糕的客户服务
- 主题 1 =时间和延迟
- 话题 2 =绝佳体验
分析

你可以清楚地看到,大多数人对航空公司持负面态度。所以要考虑到偏见,这并不一定意味着他们都有糟糕的客户服务…这是推特上最常被提及的。但你可以看到,超低价航空公司被评为最差的客户服务,超过 50%。联合航空公司的推文在时间和延误方面是最高的,我很震惊地看到达美航空公司的客户服务推文比美国航空公司和联合航空公司还多。
更多视觉效果如下:



来吧,棉绒一点:用棉绒清理你的代码
你知道他们怎么说的,“内在才是最重要的。”虽然这可能是真的,但你仍然必须找到一种方式来表达里面的内容,以便人们能够理解它。您可能拥有您所编写过的功能最强的代码。这可能是优雅的,彻底的,万无一失的。但看起来还是这样:
def buy_me_dinner():
"lala"
print("yummy dinner")
def attack():
"oh"
print("I love you" to)from fileinput import input
suspects = input()[9:].split(',')
weapons = input()[8:].split(',')
rooms = input()[6:].split(',')clues = input()
for line in input():
clue = line.split(' ')
if clue[1] != 'the':
suspects.remove(" ".join(clue[1:]))
else:
thing = " ".join(clue[2:])
if thing in weapons:
weapons.remove(thing)
else:
rooms.remove(thing)
print(suspects[0])
print("in the " + rooms[0])
print("with the " + weapons[0])
这就是棉绒派上用场的地方。linter 是一种分析代码以标记风格或编程错误的工具。我今天使用的 linter 是 pycodestyle ,它基于 Python 的 Pep-8 风格指南。Linter 会给你一个发现错误的列表,这样你就可以一个一个的修复它们,并且希望在将来学习如何编写更好的代码来遵循你选择的风格指南。还有其他像布莱克这样的 linters,它会自动重新格式化你的代码,但是你不会通过这种方式学到任何东西,所以当开始使用 linters 时,最好使用一些冗长的东西。
让我们看看当我们用 pycodestyle lint 上面的代码时会发生什么。安装完成后,只需在终端上输入
pycodestyle <name of file to lint>
你会得到这样的回报:
messy.py:4:1: E302 expected 2 blank lines, found 0
messy.py:4:14: W291 trailing whitespace
messy.py:13:1: E303 too many blank lines (6)
messy.py:13:1: E402 module level import not at top of file
messy.py:14:1: W293 blank line contains whitespace
messy.py:15:6: E111 indentation is not a multiple of four
messy.py:15:6: E113 unexpected indentation
messy.py:16:2: E901 IndentationError: unindent does not match any outer indentation level
所以第 4 行的第一个字符在空行的数量上有问题,然后第 14 个字符后面有尾随空格,依此类推。在我们采纳了它的建议后,代码看起来是什么样的?有趣的是,有时修复一个错误会暴露另一个错误。但最终我们会得到这样的结果:

只要看看它,你就能知道 Pep-8 是如何格式化代码的。在导入库之后应该有 2 个空格,在声明一个函数之后应该有 2 个空格,并且在文件的末尾应该有一个新行。这些棉绒并不完美,有时你会发现他们没有发现的风格错误。此外,pycodestyle 不会发现功能错误。但是我可以用 mypy 检查这段代码中的某些类型的错误。mypy 的用法也一样。
mypy <name of file to check>
它会检查错误。对于上面的代码,它返回
messy.py:12: error: invalid syntax
它告诉我 print 语句中第 11 行的语法错误。使用像这样的简单工具可以使您的代码看起来有很大的不同,并减少调试时的挫败感。我听说过一些经理甚至不看没有经过测试的代码,所以养成这个习惯是个好习惯,除此之外,这也是了解你的代码应该是什么样子,以及你最常犯错误的地方的好方法。
快乐的编码冒险!
每个数据科学家都应该知道的命令行基础知识
使用命令行完成简单任务的指南

Photo by Almos Bechtold on Unsplash
如果您是一名数据科学家或正在学习数据科学,并且希望超越使用 jupyter 笔记本来编写生产就绪的代码,那么您可能需要使用命令行来完成一些任务。我发现生产数据科学工具和过程的文档通常假设您已经知道这些基础知识。然而,如果你没有计算机科学背景,那么你可能不知道如何从终端完成一些简单的任务。
我想写这篇简短的指南,介绍使用命令行执行简单任务的绝对基础知识。了解这些基础知识无疑会使您的工作流程更加高效,并且在使用生产系统时会有所帮助。
导航文件和目录
当你打开终端时,这通常是你会看到的。

~符号是你的主目录的简写,所以这意味着你当前在这个目录中。如果你输入命令pwd,它将显示你当前的工作目录,在我们的例子中看起来像这个/Users/myname。
如果你想创建一个新目录,你可以输入下面的mkdir test 这将创建一个名为 test 的新目录。你现在可以使用cd命令导航到这个目录。

您也可以通过键入..来导航目录,这将带您返回一个目录。所以在我们的例子中,我们将返回到主目录。
使用文件和目录
接下来,让我们在测试目录中创建新的 python 文件。要创建一个文件,你可以输入这个命令touch test.py。这将创建一个空白的 python 文件。ls命令将一个目录的内容打印到终端,因此我们可以用它来检查我们的文件是否已经创建。

我们将使用名为 nano 的程序编辑该文件。要打开文件,你只需输入nano test.py,一个新的标签将会打开,如下所示。

对于这个例子,我们将只做一个小的改变,所以我们将输入print('this is a test')。要保存文件,您可以使用快捷键Ctrl+O并退出程序Ctrl+X。

现在我们已经编辑了文件,我们可以使用命令python test.py运行它。执行简单的 python 代码,并在终端上显示“这是一次测试”。

移动和删除
下面我们快速新建一个目录mkdir new来探究一下如何移动文件。有三种主要的方法你可以做到这一点。
正斜杠()前的【T1。/new)是父目录(test)的简写。
- 制作一个副本并移动副本以将原始文件保留在当前位置
cp test.py ./new - 移动文件而不复制
mv test.py ./new - 复制文件,并在新位置
cp test.py ./new/test_new.py重命名文件
最后,要删除一个文件,我们可以使用rm test.py。要删除一个空目录,您可以使用rmdir new。删除包含一些文件的目录rm -rf new。
本文涵盖了数据科学家可能需要从命令行完成的一些最基本的任务。如果你想探索一些更高级的命令行工具,我在这里写了另一个指南。
感谢阅读!
英国大选的命令行制图
为选举之夜制作英国选区地图。
本教程改编自 @mbostock 的 命令行制图教程 的步骤,展示如何使用免费开源 Javascript 工具轻松制作英国选举结果专题地图。然后,我还使用 Python/Pandas/GeoPandas 堆栈重复了数据管理部分,以比较每种方法的简便性。
这是我们将要做的观想:

UK General Election 2017 results: Winners by constituency.
这张专题地图通过根据获得最多选票的政党给每个议会选区着色,传达了上次英国选举的结果。在此过程中,我们还将按选区显示投票率。
获取边界几何图形
创建地图的第一步是获得一些代表英国选区边界的多边形。法令调查每年发布两次边界线产品。我在 mySociety.org 找到了一个更方便的副本——可以用命令行获取
curl --progress-bar http://parlvid.mysociety.org/os/bdline_gb-2019-10.zip -O bdline_gb-2019-10.zip
归档包含许多不同的管理边界,并且相当大(因此我添加了--progress-bar选项)。我们只需要为这个图表提取威斯敏斯特选区边界文件。
unzip -o bdline_gb-2019-10.zip Data/GB/westminster_const_region.prj Data/GB/westminster_const_region.shp Data/GB/westminster_const_region.dbf Data/GB/westminster_const_region.shxmv Data/GB/westminster_const_region.* .
正如@mbostock 所建议的,访问mapshaper.org并将westminster_const_region.shp拖到你的浏览器中是预览我们提取的内容的好方法:

westminster_const_region.shp on mapshaper.org
看起来不错,但是北爱尔兰选区不见了。原来这些可以从北爱尔兰测绘局单独得到:
wget http://osni-spatial-ni.opendata.arcgis.com/datasets/563dc2ec3d9943428e3fe68966d40deb_3.zipunzip [563dc2ec3d9943428e3fe68966d40deb_3.zip](http://parlvid.mysociety.org/os/osni-dec-2015.tar.gz)
进行设置
我们将使用一系列 Javascript 命令行工具,Mike Bostock 在他的教程中有更详细的描述。如果您还没有安装它们,可以使用以下命令进行安装[您需要安装 node 和 npm,以便下一步工作]:
npm install -g shapefile # gives us shp2json
npm install -g d3-geo-projection # geoproject, geo2svg
npm install -g topojson # geo2topo, topo2geo
npm install -g ndjson-cli
npm install -g d3
使用 shp2json 将 Shapefiles 转换为 GeoJSON:
shp2json westminster_const_region.shp -o gb.json
英国(GB)边界文件数据已经被投影到英国国家网格 (EPSG:27700)上。然而,北爱尔兰(NI)边界的几何数据在 EPSG:4326 (WSG 84)中定义。我试图找到一种方法来使用d3-geo-projection转换的镍几何 EPSG:27700,但我不得不承认,这超出了我。相反,我回过头来使用ogr2ogr来转换 NI shapefile。我在 MacOS 上,所以可以使用 brew 安装ogr2ogr:
brew install gdal
然后我将 Shapefile 从 EPSG 4326 重新投影到 EPSG 27700,如下所示:
ogr2ogr -f "ESRI Shapefile" ni.shp OSNI_Open_Data_Largescale_Boundaries__Parliamentary_Constituencies_2008.shp -t_srs EPSG:27700 -s_srs EPSG:4326
然后将此 Shapefile 转换为 GeoJSON,就像我们之前处理 GB 边界一样:
shp2json ni.shp -o ni.json
合并 GB 和 NI 几何图形
我们已经将二进制 shapefiles 转换为更易于人类阅读的 GeoJSON。但是我们还有两份独立的文件。将所有边界放在一个文件中会方便得多。
在他的教程的第 2 部分,Mike 介绍了ndjson-cli,这是他创建和操作 ndjson 的工具。我想我们可以使用这个工具将 GeoJSON 文件转换成换行符分隔的 JSON,然后简单地将它们cat在一起。我还需要取出选区标识符,并使用ndjson-map使其在每个文件中通用:
ndjson-split ‘d.features’ < gb.json \
| ndjson-map '{id: d.properties.CODE, properties:d.properties, type:d.type, geometry:d.geometry}' > gb_id.ndjsonndjson-split 'd.features' < ni.json \
| ndjson-map '{id: d.properties.PC_ID, properties:d.properties, type:d.type, geometry:d.geometry}' > ni_id.ndjsoncat gb_id.ndjson ni_id.ndjson > uk.ndjson
我试着从这一点开始用ndjson将几何图形导入geoprojection,但这并不成功。我发现在继续投影之前,有必要使用ndjson-reduce命令将连接的边界转换回单个 JSON 对象:
ndjson-reduce 'p.features.push(d), p' '{type: "FeatureCollection", features: []}' < uk.ndjson > uk.json
很好,我们在一个 JSON 文件中包含了所有的边界。但是,这个文件大约有 120M。很明显,这些几何图形的定义比我们基于网络的可视化所需要的更加详细。为了减小文件大小,我们可以为图表定义一个边界框(以像素为单位),然后在不明显丢失细节的情况下,适当简化该框的多边形。
使用 geoproject 工具和d3.geoIdentity()的.fitsize()方法将点映射到我们选择的边界框中[ 我还利用这个机会垂直翻转多边形定义,因为我们最终将渲染为 svg :
geoproject 'd3.geoIdentity().reflectY(true).fitSize([960, 960], d)'\
< uk.json > uk-960.json
现在,我们可以通过切换到 TopoJSON,然后简化、量化、和压缩多边形定义来减小 GeoJSON 文件的大小。请阅读 Mike Bostock 的教程的第 3 部分以获得这些步骤的更详细的解释,我在下面复制了这些步骤。
我使用 geo2topo 转换为 TopoJSON,然后使用topo simplify和 topoquantize 工具将文件大小从 120M 减少到 385K:
geo2topo const=uk-960.json \
| toposimplify -p 1 -f \
| topoquantize 1e5 \
> uk-simpl-topo.json
现在让我们快速查看一下我们的输出。要在命令行生成 svg,我们可以使用geo2svg工具。首先,我们使用topo2geo转换回 GeoJSON:
topo2geo const=- < uk-simpl-topo.json \
| geo2svg -p 1 -w 960 -h 960 > uk.svg
svg 文件可以拖到 web 浏览器中,让我们快速浏览。

The United Kingdom of Great Britain & Northern Ireland in SVG via TopoJSON!
添加选举结果
所以我们准备了我们的选区边界。现在我们想根据一些选举结果给这些多边形着色。
我在英国议会网站上找到了一个包含 1918 年选举结果的 csv:
wget [http://researchbriefings.files.parliament.uk/documents/CBP-8647/1918-2017election_results.csv](http://researchbriefings.files.parliament.uk/documents/CBP-8647/1918-2017election_results.csv) -O [1918_2017election_results.csv](http://researchbriefings.files.parliament.uk/documents/CBP-8647/1918-2017election_results.csv)
我使用命令行工具gawk来过滤这个 csv,以获得 2017 年的结果,并用零填充空值。棘手的部分是一些选区名称包含逗号,需要忽略。
gawk -F ',' -v OFS=',' '{for (i=1;i<=NF;i++) { if ($i=="") $i="0" }; print}' < 1918_2017election_results.csv | gawk 'NR==1 {print}; { if ($18 == 2017) print}' FPAT='([^,]+)|("[^"]+")' > 2017_results.csv
Mike 展示了如何通过首先转换成换行符分隔的 json 来将这样的数据与几何连接起来。以他为例,我使用csv2json和他的ndjson-cli工具首先转换选举结果:
csv2json < 2017_results.csv | tr -d '\n' | ndjson-split > 2017_results.ndjson
类似地,我们将边界几何准备为 ndjson:
topo2geo const=- < uk-simpl-topo.json | \
ndjson-split 'd.features' > uk-simpl.ndjson
并将两者结合在一起:
ndjson-join 'd.id' 'd.constituency_id' uk-simpl.ndjson 2017_results.ndjson \
| ndjson-map 'd[0].results = d[1], d[0]' \
> uk_2017_results.ndjson
Mike 的示例是根据该区域的人口密度值给地图的多边形着色。我在选举结果中拥有的最相似的量——即连续量——是投票率。因此,为了能够效仿他的例子,我决定在继续寻找获胜者之前,利用这些投票率信息制作一个氯普勒斯图。
以下代码使用ndjson-map为 GeoJson 中的每个要素(即选区)分配一个名为fill的属性。当我们使用geo2svg来生成我们的图像时,它会选择这个属性,并用它来给多边形着色。fill的值需要是一个字符串,包含我们希望每个多边形颜色的十六进制代码(如#0087dc)。
ndjson-map -r d3 'z = d3.scaleSequential(d3.interpolateViridis).domain([0.5, .8]), d.properties.fill = z(d.results["turnout "]), d' < uk_2017_results.ndjson \
| geo2svg -n --stroke none -p 1 -w 960 -h 960 > uk_2017_turnout.svg
可能值得尝试将这个一行程序分解一点,以了解发生了什么。我们使用 d3 将投票率信息(一个介于 0 和 1 之间的数字,代表在该选区实际投票的选民比例)转换成一个十六进制代码。ndjson-map工具让我们遍历每个选区并应用某种操作。我们传递给ndjson-map的参数是三行 Javascript 代码。
第二行:
d.properties.fill = z(d.results["turnout "])
为每个特性创建一个名为fill的属性,其值是函数z的输出。该函数z获取投票率的值——我们在之前的连接中已经将其附加到该特征上——并返回一个代表颜色的 hexcode。这个z函数在 Javascript 的第一行中定义:
z = d3.scaleSequential(d3.interpolateViridis).domain([0.5,0.8])
它使用 d3 的配色方案将代表投票率的数字从绿色配色方案转换为适当的颜色。我检查了选举数据中的投票率值,以便设置domain的界限,从而尽可能多地使用色标。
我们可以在浏览器中打开那个uk_2017_turnour.svg文件:

A green and pleasant land: turnout in the 2017 UK General Election
图例在可观察笔记本中单独创建,并复制粘贴到“svg”文件中。
将方法应用于情节赢家
实际上,我在本教程中的目标是创建一个显示每个选区获胜者的地图。为此,我们重做最后一步,使用 ndjson-map 查找拥有最高投票份额的政党,然后将几何体的fill属性设置为代表该政党的颜色。
我传递给ndjson-map的 Javascript 在这里感觉有点笨拙,因为我定义了一个对象来保存对每一方的颜色的查找。
ndjson-map -r d3 's={"con_share":"#0087dc", "lib_share":"#FDBB30", "lab_share":"#d50000", "natSW_share":"#3F8428", "pld":"#3F8428", "oth_share":"#aaaaaa"}, u = ({ con_share, lab_share, lib_share, oth_share, natSW_share }) => ({ con_share, lab_share, lib_share, oth_share, natSW_share }),
d.properties.fill = s[Object.keys(u(d.results)).reduce((a, b) => d.results[a] > d.results[b] ? a : b)], d' < uk_2017_results.ndjson \
| geo2svg -n --stroke none -p 1 -w 960 -h 960 > uk_2017_winner.svg
再一次,将 javascript 分解一点,第一行:
s={"con_share":"#0087dc", "lib_share":"#FDBB30", "lab_share":"#d50000", "natSW_share":"#3F8428", "pld":"#3F8428", "oth_share":"#aaaaaa"}
简单地定义了从当事人到颜色的查找。第二行:
u = ({ con_share, lab_share, lib_share, oth_share, natSW_share }) => ({ con_share, lab_share, lib_share, oth_share, natSW_share })
是一个析构赋值,我们用它从选举结果对象中提取 voteshare 数字。然后是最后一行:
d.properties.fill = s[Object.keys(u(d.results)).reduce((a, b) => d.results[a] > d.results[b] ? a : b)]
找到投票份额最大的一方的密钥,并在查找对象s中使用该密钥将所需颜色写入fill属性。
看看结果:

2017 General Election UK results constituencies coloured by winner.
我们有结果了。您可以就此打住,除非您想看看如何使用 Python 堆栈实现同样的功能。
第 2 部分:Python 中的数据管理
在本教程的第一部分,我们在命令行上使用 Javascript 工具制作了一个英国选举地图,下面是迈克·博斯托克的命令行制图教程。对于数据管理,我通常倾向于 Python 环境。所以在第二部分中,我回过头来看看如何在 Python REPL 中使用熊猫和 GeoPandas 来实现同样的效果。
为了更接近“命令行”风格,我将使用 Python REPL 来做这件事,当然,Jupyter 笔记本/实验室会议也很好。
python
我们将使用 GeoPandas 库以及 Pandas 进行一些常规的数据操作:
import pandas as pd
import geopandas as gpd
首先,让我们将英国的选区多边形边界读入地理数据框架:
gb_shape = gpd.read_file('westminster_const_region.shp')

A quick look at the imported shapefile as a DataFrame.
如果您想更直观地检查几何图形,我们可以导入 matplotlib,然后只需在地理数据框架上调用.plot():
import matplotlib.pyplot as plt
gb_shape.plot(); plt.show()

A quick visual check on the content of the GB shapefile.
我注意到在后来的图中设得兰群岛从地图上消失了,我认为这个数据中的CODE字段有错误:S1400005应该是S14000051。所以现在让我们快速改变这种情况。
gb_shape['CODE']=gb_shape.CODE.apply(lambda s: 'S14000051' if s == 'S1400005' else s)
现在加载北爱尔兰的选区边界:
ni_shape = gpd.read_file(‘OSNI_Open_Data_Largescale_Boundaries__Parliamentary_Constituencies_2008.shp’)
我们希望将两组边界连接成一个数据帧。为此,我首先需要调整列名,使它们匹配:
ni_shape.rename(columns={'PC_NAME':'NAME','PC_ID':'CODE'},inplace=True)
第二,正如我们在前面的教程中发现的,GB 和 NI 边界几何在不同的坐标系中提供。英国(GB)边界文件数据已经被投影到英国国家网格 (EPSG:27700)。然而,北爱尔兰(NI)边界的几何数据在 EPSG:4326 中定义。我们可以使用 GeoPandas 来完成这种转换,而不是使用ogr2ogr:
ni_shape.to_crs({‘init’:’epsg:27700'},inplace=True)
现在我们应该准备好使用pd.concat连接两组几何图形,这将产生另一个GeoDataFrame。
uk_boundaries = pd.concat([gb_shape[['NAME','CODE','geometry']],ni_shape[['NAME','CODE','geometry']]], sort=False)
将选举结果与几何图形合并
现在把我们的选举结果读入数据框:
results=pd.read_csv(‘[1918_2017election_results.csv](http://researchbriefings.files.parliament.uk/documents/CBP-8647/1918-2017election_results.csv)’)

First 10 rows of the election results csv file.
我想过滤此数据框架,以仅包含 2017 年选举的结果。[ 我还首先重命名了一些列,以纠正尾随空白,并着眼于以后与几何数据合并] 。
# rename a few columns
results.rename(columns={
‘turnout ‘:’turnout’,
’constituency_id’:’CODE’,
},inplace=True)# keep only the 2017 election results
results=results[results.election=='2017']# keep only the columns we need
results=results[[‘CODE’,’constituency’,’country/region’,’con_share’,’lib_share’,’lab_share’,’natSW_share’,’oth_share’,’turnout’]]uk_results = uk_boundaries.merge(results, on='CODE')
uk_results数据框包含每个选区的几何图形和选举结果。
在命令行制图教程中,我们简化了几何体,使生成的文件更小。可以使用 GeoPandas 来完成这个简化(通过 Shapely):
uk_results[‘geometry’] = uk_results.geometry.simplify(tolerance=500)
准备演示
虽然当然可以使用 Python 堆栈生成 svg,但我的第一个目标是从合并的数据中创建 GeoJSON 输出。这将允许我退回到 javascript 的命令行制图世界,或者在最终的演示文稿中制作工作。
为了方便起见——我仍然觉得 Javascript 中的数据管理不够直观——我现在使用 Pandas 将几何图形的填充颜色合并到数据帧中。一方面,我对这种将表示和底层数据混为一谈的做法感到有点不舒服。另一方面,这并不妨碍我或其他人忽略这种嵌入的填充颜色,并从将保留在 GeoJSON 中的底层数据中导出新的表示。
首先我做了一个定义派对颜色的小字典,摘自这里:
party_colours={
“con”:”#0087dc”,
“lib”:”#FDBB30",
“lab”:”#d50000",
"snp":"#FFF95D",
“pld”:”#3F8428"
}
然后我们算出每个选区的获胜者。和以前一样,获胜者只是拥有最高的投票份额。
uk_results['winner']= uk_results[['con_share','lab_share','lib_share','natSW_share','oth_share']].fillna(0.).astype(float).idxmax(axis=1).apply(lambda s: s[:3])
令人稍感不满的是,我获得的选举结果数据没有将苏格兰和威尔士的主要民族主义政党分开,而是将它们归为一类。但是,因为苏格兰民族党不在威尔士竞选候选人,而格子党在苏格兰竞选候选人,所以我们可以使用选区区域来确定每种情况下的特定民族党,从而分配适当的颜色。
def sub_nat(winner, region):
if winner=='nat':
if region=='Wales':
return 'pld'
else:
return 'snp'
else:
return winneruk_results['winner']=uk_results[['country/region','winner']].apply(lambda r: sub_nat(r[1],r[0]), axis=1)uk_results['winner_fill']=uk_results.winner.apply(lambda s: party_colours.get(s,”#aaaaaa”))
这并没有解决北爱尔兰政党的色彩问题。我必须寻找一个更详细的选举结果数据集来提供这种分类。
现在我们可以写出这个数据帧:
uk_results.to_file(‘gdp_uk_results.json’,driver=’GeoJSON’)
此时,我们可以切换回命令行制图并生成一个 svg:
geoproject 'd3.geoIdentity().reflectY(true).fitSize([960, 960], d)' < gdp_uk_results.json \
| ndjson-split 'd.features' \
| ndjson-map -r d3 'd.properties.fill = d.properties.winner_fill, d' \
| geo2svg -n --stroke none -p 1 -w 960 -h 960 > gdp_uk_winner.svg

The rendered SVG
所以我们有它。我使用 Python 和 Pandas 进行数据操作的频率比使用命令行工具如awk Javascript 要高得多。因此,不可避免地,我发现通过本教程一起操作几何图形和选举结果比在第 1 部分中更容易和更直观。也许这只是我的熟悉偏见。
我仍然更喜欢可观察的笔记本和数据驱动的文档哲学,以便在网上展示结果。但是我对探索 Altair 的能力很感兴趣,特别是输出织女星的描述,这些描述很容易嵌入网页。也许在以后的文章中。
另一天,我们可以讨论在地理地图上显示这些数据的优点,而不是,比如说,在选区上放置相等面积的地图。
感谢 @mbostock 提供的所有工具和鼓舞人心的文章。
通用分类模型评估指标。
所有的模型都是错的,但有些是有用的,乔治 E. P. Box。
引言。
分类模型有多准确?模型靠谱吗?
这两个问题很容易通过评估一个模型在受到看不见的观察时的表现来回答。这篇文章展示了评估模型的一些最佳方法。
你将从这篇文章中学到什么:
- Jaccard 索引。
- 混淆矩阵
- F-1 分数
- 原木损失
样本模型。
首先我将拟合一个简单的模型,并用它来说明这些方法在模型性能评估中的应用。该模型预测癌细胞是否是恶性的。
#quick model fit
import numpy as np
import warnings
import pandas
warnings.filterwarnings("ignore")#not recomended but i have included this for my own convenience.
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
X = pandas.DataFrame(data = data.data,columns=data.feature_names)
y = data.target
#train test split
from sklearn import model_selection
np.random.seed(2) #to enable you replicate the same thing i am doing here.
X_train, X_test, y_train, y_test = model_selection.train_test_split(X,y,test_size=0.30)
# I will use logistic reg
from sklearn.linear_model import LogisticRegression
reg = LogisticRegression()
reg.fit(X_train,y_train)
preds = reg.predict(X_test)
predsprob = reg.predict_proba(X_test)
Jaccard 索引
假设预测值为(y hat ),实际值为 y,Jaccard 指数可定义为:


假设你有下面一组预测值和实际值。


Jaccard 指数将为:

该指数背后的思想是这两个组的相似性越高,指数越高。
将此应用于上面的模型。
from sklearn.metrics import jaccard_similarity_score
j_index = jaccard_similarity_score(y_true=y_test,y_pred=preds)
round(j_index,2)0.94
混淆矩阵
混淆矩阵用于描述分类模型对一组真实值已知的测试数据的性能。

confusion matrix
从混淆矩阵中可以提取以下信息:
- 真阳性。这表明一个模型正确地预测到阳性病例为阳性。疾病被诊断为存在,并且确实存在。
- 假阳性(FP) :这表明一个模型错误地将阴性病例预测为阳性。一种疾病被诊断为存在,但并不存在。(第一类错误)
- 假阴性:(FN) 这表明一个模型错误地将阳性病例预测为阴性。一种疾病被诊断为不存在,但却存在。(第二类错误)
- 真阴性(TN): 这表明一个模型正确地预测了阴性病例为阳性。一种疾病被诊断为不存在,并且确实不存在。
将此应用于上面的模型。
from sklearn.metrics import confusion_matrix
print(confusion_matrix(y_test,preds,labels=[1,0]))
import seaborn as sns
import matplotlib.pyplot as plt
sns.heatmap(confusion_matrix(y_test,preds),annot=True,lw =2,cbar=False)
plt.ylabel("True Values")
plt.xlabel("Predicted Values")
plt.title("CONFUSSION MATRIX VISUALIZATION")
plt.show()

在这种情况下,对于乳腺癌数据,模型正确预测 62 例为良性,98 例为恶性。相比之下,它总共错误预测了 11 个案例。
f1-得分。
这来自于混淆矩阵。基于上面的混淆矩阵,我们可以计算精确度和召回分数。
精度分数:这是精度的度量,前提是已经预测了类别标签。简单地说,它回答了下面的问题,在所有的类中,有多少是正确预测的?这个问题的答案应该是越高越好。
它可以计算如下:

回忆分数(敏感度):这是真正的阳性率,如果它预测阳性,那么它发生的频率是多少?

F1 分数是根据每个类的精确度和召回率计算的。它是精确度和召回分数的加权平均值。F1 分数在 1 时达到完美值,在 0 时最差。这是一个很好的方法来表明一个分类器有很好的召回率和精确度值。
我们可以用这个公式来计算:

应用于上面的模型。
from sklearn.metrics import f1_score
f1_score(y_test,preds)0.9468599033816425
可以计算所有类别的 F1 分数,因此可以使用实际分数的平均值,如下面的分类报告所示。
from sklearn.metrics import classification_report
print(classification_report(y_test,preds))precision recall f1-score support 0 0.91 0.93 0.92 67
1 0.95 0.94 0.95 104 micro avg 0.94 0.94 0.94 171
macro avg 0.93 0.93 0.93 171
weighted avg 0.94 0.94 0.94 171
日志损失。
在分类器的结果是类别概率而不是类别标签的情况下,我们可以使用对数损失,就像逻辑回归模型的情况一样。
对数损失衡量模型的性能,其中预测结果是介于 0 和 1 之间的概率值。
在现实生活中,当预测 0.101 的概率时,真实标签应该是 1,这将导致高对数损失。可以使用对数损失公式计算数据集中每一行的对数损失。

该方程简单地测量了每个预测概率与实际标签的距离。所有行的日志损失的平均值给出了日志损失的理想值。

好的和模型应该具有较小的对数损失值。
在上述模型中应用。
from sklearn.metrics import log_loss
log_loss(y_test,predsprob)0.13710589473837184
这里我们有一个 0.14 的对数损失,相当不错!
结论。
- 基于所应用的模型,应该很好地理解评估指标的选择。
- 为了从评估指标中获得优异的结果,建议对模型进行特征工程和参数调整。
感谢阅读,欢迎任何评论和/或补充。
干杯!
通用数据科学陷阱项目—让系统达成一致
当心!他们不会同意的!问题是到什么程度

I see you. (Photo: Randy Au)
如今,在数据科学领域工作,不与多个数据系统打交道几乎是不可能的,因为这些数据系统所涵盖的内容可能会有重叠。
也许你有一个网站,同时使用谷歌分析和内部定制跟踪系统来收集分析。也许你有一个系统跟踪网络像素点击,另一个系统使用 JavaScript 指纹。或者您有自己的分析系统,但正在与第三方交换意见。
每当有多个系统时,要问的一个最基本的问题是来自一个系统的数字是否与来自另一个系统的数字一致。你可能会认为协调系统很容易做到。
但事实并非如此。
除非经过特别设计,否则系统几乎永远不会一致。他们甚至可能不会同意。在你陷入一个会让你抓狂的为期 3 周的项目之前,后退一步,明白你面临的是什么。
定义冲突
有时,从完全相同的底层数据集中提取数据的两个报告不一致。这归结为对某事物定义的不同。你的“活跃用户”指标包括机器人吗?或者可能排除没有购买的用户,或者只统计过去 28 天而不是 30 天的用户?
因为有许多方法来分析数据集,所以必须非常清楚度量标准在测量什么。每个人都必须注意不要在不同的定义中使用同一个术语。当与不同团队的人一起工作时,尤其如此,这些人可能彼此之间没有密切的联系,并且可能已经独立地开发了他们自己的一套定义。
协调这些通常意味着查看数据处理管道并检查用于提取数据的逻辑。有时在那些实现中有一些你需要修复的错误,而其他时候一旦定义被调和,事情就完全匹配了。
虽然许多人会说这个问题可以通过一个合适的数据字典来解决/避免,但是在实践中,除非你有一个非常强大的保持字典更新的过程,否则它只会对最普遍和最稳定的指标有效。快速变化的事物如果没有很好的实施,往往会变得陈旧。
实现冲突
大多数系统不会以与另一个系统完全相同的方式来衡量事物。即使概念上的定义是一样的,细节也非常重要。
例如,通过查看服务器请求日志来跟踪点击量的系统不适合要求客户端运行 JavaScript 代码的系统。客户端可能无法运行 JavaScript。
在这种情况下,服务器日志的点击数会比 JavaScript 多……除非你做的是一个单页设计,在没有直接页面重载的情况下更新页面,但是当逻辑重载发生时会触发一个跟踪事件。然后它可以根据你的用户转向另一个方向,
这种事情可能会变得复杂,当您细化到越来越小的“缺失”计数集时,您必须更深入地挖掘越来越小的实现细节。这就是为什么如果你想处理数据,你必须非常熟悉你的技术堆栈。它永远不会“干净”,因为你是收集它的人。总有一些你没有收集到或者无法收集到的东西。
消灭虫子
bug 难免会发生。它们也不需要同时发生在两个独立的系统上。即使它影响了两者,它也可能是有偏见的,不会以同样的方式影响事物。这类问题应该是暂时的,因为当 bug 被引入,然后当它被修复时,协议会有很大的变化。
但是有时这些错误几年都不会被发现,发现它的唯一方法是因为你在比较两个系统的指标,无论你做什么,它们都不一致。
两位将军的问题已经把你解决了
对于彼此远离的系统,在某种意义上,它们通过潜在有损耗的信道进行交互,由于系统之间的传输损耗,系统可以简单地不一致。这可能是由于网络故障或系统故障(崩溃、错误等)。
在计算中,两个将军的问题是一个思想实验,旨在说明陷阱和设计挑战…
en.wikipedia.org](https://en.wikipedia.org/wiki/Two_Generals'_Problem)
这个问题的简短解释是,有两个将军必须攻击他们之间的一个城市,只有当他们同时攻击时,他们才能成功。他们必须派一名信使到敌人领地对面的另一名将军那里,并规定攻击时间,但这名信使可能会被俘虏。类似地,也可以捕获返回的“消息已接收”消息。
即使双方都派出了无限的信使,也没有算法能保证两位将军的问题得到解决。唯一的解决办法是制定协议,增加双方就某件事达成一致的可能性,同时平衡发送的信使数量。
但是等等,你问。我们没有 TCP 吗?这还没解决吗?不完全是。TCP 协议(以及类似的其他通信协议)确实有适当的机制来减轻有损信道的影响,因为它能够重新发送丢失的数据包等。但是,从根本上说,所有的东西都不可能同时完全了解系统的状态。TCP 连接可能在任何时候中断。
我敢肯定,你认为我们的各种数据系统有针对这类问题的安全措施,一致性保证等等。那么,请仔细阅读这些细节,因为它们与软件中的细节有关,并且非常具体。他们也无法防范软件之外发生的事情,比如猫啃网线。
更实际的说法是,假设您的站点在加载track_hit_locally()和track_hit_remotely()时进行了两次函数调用。两者相继开火,但一个与本地网络上的机器对话,而另一个与地球另一端的机器对话。随着时间的推移,随着随机互联网中断和系统错误的堆积,这两个计数通常会开始不一致,除非有特定的机制来保持它们同步。即使这样,同步机制也可能失败。
更糟糕的是,当您在比较您无法控制的系统时,您没有机制来审计和插入丢失的数据。因此,这些系统将不得不存在分歧。问题只是这种分歧是否在可接受的范围内。
利益冲突和欺诈
数据不会说谎,除非有人有动机让它说谎。然后,通常不会很难。有时系统会因为与技术或数学无关的邪恶原因而不一致。
最近一个著名的例子是脸书夸大他们的视频指标,有效地促使营销人员向平台投入资源(和金钱)。
另一个例子是 2008 年金融危机期间我在广告技术领域的黑暗日子。我工作的三流商店是在互联网广告的黑暗角落工作的,在那里点击通过多层广告网络,然后到达一个主要的广告客户。
点击量(这意味着点击费世界的实际收入)定期从一个网络到下一个网络有大约 10-15%的差异。原因包括“由于专有的反欺诈技术,点击无效”和随机系统错误。有时这是有效的欺诈检测,但有些百分比只是削减了百分之几的额外利润,基本上没有支付下游出版商,同时保留了上游广告商的钱。
问题是每个人都这样做,在广告网络链上上下下。因此,人们不同意只是例行公事,只要不太过分,每个人都只是点头微笑。
幸运的是,我已经发誓从 2009 年开始不再做广告,所以这个故事已经有十年了。如果世界上还有这样的事情发生,我不会感到惊讶,但是谢天谢地,我对整个情况一无所知。
所以要小心,有时候数字加不起来是因为有人不想让它们加起来。鉴于所有其他来源的差异以及谁控制着数据源,证明这一点是完全不可能的。
处理冲突
系统之间的计数可能会有各种不同,您能做些什么呢?
要清晰和一致
度量的定义和实现总是任意的构造,人们必须随着时间的推移通过重复使用来记住它们。诀窍是在你的用法上要清晰和一致。不要不断地重新定义事物,不要有相互竞争的实现。有时,一个有长期使用历史的稍微有缺陷的指标可能比一个更准确的指标更容易推理。
这并不是说您永远不应该改进您的度量方法,而是说它必须以一种每个用户都可以遵循的方式来完成。理想情况下,改进建立在现有知识的基础上,这样人们就不必重新学习材料。
不要追逐收益递减
80/20 法则适用于数据误差源。通常,一小部分问题会导致大部分差异。所以当你在一个项目中追踪问题时,记住你的努力会有递减的回报。
确保您的指标彼此相差不超过 10%对您来说有多大价值?1%以内要多少?0.1%?确切的约定值多少钱?
你想要的东西越接近,你就要花费越多的时间和资源来追踪问题并纠正/预防它们。对于许多应用程序来说,有一点小小的分歧可能是好的。你可能希望涉及金钱的事情更加紧密,但即使是金钱,如果误差足够小,会计团队也可能愿意接受一定程度的误差。
然后,一些系统绝对需要一致,在这种情况下,你需要绕过它,并支付高昂的成本。这将包括保持系统冗余和高度可用,能够记录和重放事务,定期一致性检查和审计等。
有条不紊
寻找差异需要很多时间。绕圈子真的很容易,因为有太多的细节需要记住。
根据我的经验,最好的处理方法是这样做:
- 估计差异
- 扫描数据并检查是否有明显的片段丢失,有时明显的模式很容易识别
- 询问系统所有者的意见,他们可能知道一些你不知道的事情
- 开始分割数据块。你能让系统匹配特定的子集吗?比如,所有付费用户,或者 12 月的每个人等等。如果你找到匹配的,记下来。
- 当你想不出匹配的细分市场时,后退一步,看看是否有什么模式和漏洞。
- 如果失败了,开始挖掘逻辑,理解发生了什么,用你的领域知识去发现哪里可能出错
- 当你找到差异的来源时,回头看看它占差异的多少。有时候这就足够让你满意了。
在 ArcGIS 中使用 Python 进行常见字段计算

Field Calculator in ArcGIS Pro
如果您记得如何使用 ArcGIS field calculator,它可以节省人员的时间,并允许快速清理数据。我不知道你怎么想,但对我来说,字段计算器的布局、公式字段和代码块区域的分离以及与标准 Python 格式的微小变化足以让我直接进入谷歌搜索栏,如果我有一段时间没有使用它的话。这就是为什么我开始为哪怕是最简单的任务做笔记。我经常回去找他们,这样可以节省我的时间和减少我的挫败感。
也许有一天我会记住这些,但现在这是可行的。
下面你会发现我经常使用的一些简单的现场计算列表作为起点。当我想到它们时,我会补充这些。也许你会发现一个有用的。
将文本改为标题大小写。——如此简单且频繁使用。
!note1!.title()
用一个字符替换另一个字符。 —这可能是我使用字段计算器的最常见原因。
!noteField!.replace("-","_")
将字段连接在一起。 —对地址进行地理编码时非常常见。
!Address! +" "+ !Street! +", "+ !City! +", "+ !State! +" "+ !Zip!
跨多个领域的简单数学计算。 —您可以使用字段计算器从字段列表中提取汇总统计数据。
将命令连锁在一起。在这个例子中,我从一个名为 TextField 的字段开始,其中有一些杂乱的数据。假设我把橡树列为橡树、橡树、橡树和橡树,但是我想把它们都标准化,简单地读作橡树。几个简单的命令可以连接在一起实现这一点。
!TextField!.lower().replace("tree", " ").replace("oak", "Oak Tree")
使用代码块。ArcGIS 中计算字段工具的代码块部分允许使用任何 Python 函数,您甚至可以导入模块并定义自己的函数。一旦你用过几次,这种格式就没那么复杂了。可以把代码块区域看作是定义函数的地方,以后可以从表达式框中调用这些函数。在下面的例子中,我想通过将属的前两个字母与种的前两个字母连接起来,为每个树种创建一个缩写代码。我可以像这样快速完成:
使用数学进行单位转换。要将平方英尺转换为英亩并四舍五入到两位小数,您可以这样做:
您可以用下面的一行代码实现相同的结果,但我个人更喜欢使用代码块,以使事情更具可重复性,并允许将来使用更复杂的函数:
round((!Shape_Area! / 43560),2)
另一种换算单位的方法。-正如这里的所详述的,可以使用 ArcGIS Pro 内置的几何单位转换,而不是自己进行计算。查看上面的 esri 链接,了解所有内置的测量单位转换。请注意,与其他字段相比,引用几何列时字段名称的格式略有不同:
!shape.area@hectares!
!shape.length@KILOMETERS!
使用逻辑 —这里有一个过于简化的例子,使用逻辑根据管径将公用管道分类为主管线或支管。
最初发布于geopy . dev
基于 Pearson 相关系数和 Neo4j 的调查响应社区检测
就在几天前,新版本的 Neo4j 图形算法插件发布了。新版本带来了新的算法,皮尔逊相关算法就是其中之一。
为了演示如何在 Neo4j 中使用皮尔逊相关算法,我们将使用由 Miroslav Sabo 提供的来自年轻人调查ka ggle 数据集的数据。它包含了 1010 份填写好的调查结果,问题从音乐偏好、爱好兴趣到恐惧症。
在评分场景中使用 Pearson correlation 的好处在于,它在将每个分数与用户的平均分数进行比较时,考虑了投票者通常更倾向于给出更高或更低分数的情况。
导入
下载数据集并复制到$Neo4j/import文件夹。 responses.csv 文件中的每一行都代表一份填写了 150 个问题的调查。我们将它作为单个节点存储在 Neo4j 中。
LOAD CSV WITH HEADERS FROM "file:///responses.csv" as row
CREATE (p:Person)
SET p += row
预处理
大多数答案从一到五不等,其中五被定义为“完全同意”,一被定义为“完全不同意”。它们在 csv 文件中显示为字符串,我们必须首先将它们转换为整数。
MATCH (p:Person)
UNWIND keys(p) as key
WITH p,key where not key in ['Gender',
'Left - right handed',
'Lying','Alcohol',
'Education','Smoking',
'House - block of flats',
'Village - town','Punctuality',
'Internet usage']
CALL apoc.create.setProperty(p, key, toInteger(p[key])) YIELD node
RETURN distinct 'done'
类别属性
有些答案是绝对的。一个例子是酒精问题,可能的答案是“从不”、“社交饮酒者”和“喝很多”。
因为我们想把它们中的一些转换成向量,所以让我们检查它们所有可能的答案。
MATCH (p:Person)
UNWIND ['Gender',
'Left - right handed',
'Lying','Alcohol',
'Education','Smoking',
'House - block of flats',
'Village - town','Punctuality',
'Internet usage'] as property
RETURN property,collect(distinct(p[property])) as unique_values
结果
让我们对性别、网络和酒精答案进行矢量化。我们将在 1 到 5 之间进行缩放,以匹配整数答案范围。
性别编码
MATCH (p:Person)
WITH p, CASE p['Gender'] WHEN 'female' THEN 1
WHEN 'male' THEN 5
ELSE 3
END as gender
SET p.Gender_vec = gender
互联网编码
MATCH (p:Person)
WITH p, CASE p['Internet usage'] WHEN 'no time at all' THEN 1
WHEN 'less than an hour a day' THEN 2
WHEN 'few hours a day' THEN 4
WHEN 'most of the day' THEN 5
END as internet
SET p.Internet_vec = internet
酒精编码
MATCH (p:Person)
WITH p, CASE p['Alcohol'] WHEN 'never' THEN 1
WHEN 'social drinker' THEN 3
WHEN 'drink a lot' THEN 5
ELSE 3 END as alcohol
SET p.Alcohol_vec = alcohol
降维
我们的数据集中有 150 个答案可以用作特征。这是对特征进行一些基本降维的好机会。
我看到了一篇由普尔基特·夏尔马写的关于降维技术的文章。它描述了 12 种降维技术,在本文中,我们将使用前两种,即低方差滤波器和高相关性滤波器。
低方差滤波器
考虑我们数据集中的一个变量,其中所有观察值都相同,比如说 1。如果我们使用这个变量,你认为它能改进我们将要建立的模型吗?答案是否定的,因为这个变量的方差为零。
我们将使用标准差度量,它就是方差的平方根。
MATCH (p:Person)
WITH p LIMIT 1
WITH filter(x in keys(p) where not x in ['Gender','Left - right handed','Lying','Alcohol','Education','Smoking','House - block of flats','Village - town','Punctuality','Internet usage']) as all_keys
UNWIND all_keys as key
MATCH (p:Person)
RETURN key,avg(p[key]) as average,stdev(p[key]) as std
ORDER BY std ASC LIMIT 10
结果
我们可以观察到,每个人都喜欢听音乐,看电影,和朋友一起玩。
由于方差较低,我们将在进一步分析中排除以下问题:
- “个性”
- “音乐”
- “梦”
- “电影”
- “与朋友同乐”
- “喜剧”
高相关滤波器
两个变量之间的高度相关性意味着它们具有相似的趋势,并且可能携带相似的信息。这可能会大大降低某些模型的性能(例如,线性和逻辑回归模型)。
在这项任务中,我们将使用皮尔逊相关系数。Pearson 相关性针对不同的位置和特征比例进行调整,因此任何类型的线性缩放(归一化)都是不必要的。
找出性别特征的 10 大相关性。
MATCH (p:Person)
WITH p LIMIT 1
WITH filter(x in keys(p) where not x in ['Gender','Left - right handed','Lying','Alcohol','Education','Smoking','House - block of flats','Village - town','Punctuality','Internet usage','Personality','Music','Dreams','Movies','Fun with friends','Comedy']) as all_keys
MATCH (p1:Person)
UNWIND ['Gender_vec'] as key_1
UNWIND all_keys as key_2
WITH key_1,key_2, collect(coalesce(p1[key_1],0)) as vector_1,collect(coalesce(p1[key_2] ,0)) as vector_2
WHERE key_1 <> key_2
RETURN key_1,key_2, algo.similarity.pearson(vector_1, vector_2) as pearson
ORDER BY pearson DESC limit 10
结果
与性别最相关的特征是体重,这是有道理的。该列表还包括其他一些刻板的性别差异,如对汽车、行动和个人电脑的偏好。
现在让我们计算所有特征之间的皮尔逊相关。
MATCH (p:Person)
WITH p LIMIT 1
WITH filter(x in keys(p) where not x in ['Gender','Left - right handed','Lying','Alcohol','Education','Smoking','House - block of flats','Village - town','Punctuality','Internet usage','Personality','Music','Dreams','Movies','Fun with friends','Comedy']) as all_keys
MATCH (p1:Person)
UNWIND all_keys as key_1
UNWIND all_keys as key_2
WITH key_1,key_2,p1
WHERE key_1 > key_2
WITH key_1,key_2, collect(coalesce(p1[key_1],0)) as vector_1,collect(coalesce(p1[key_2],0)) as vector_2
RETURN key_1,key_2, algo.similarity.pearson(vector_1, vector_2) as pearson
ORDER BY pearson DESC limit 10
结果
结果显示没有什么令人惊讶的。我唯一感兴趣的是蛇和老鼠之间的关系。
由于高度相关,我们将从进一步分析中排除以下问题:
- “医学”
- “化学”
- “购物中心”
- “物理学”
- “歌剧”
- "动画"
皮尔逊相似算法
既然我们已经完成了预处理步骤,我们将基于我们没有排除的节点的特征(答案)的皮尔逊相关性来推断节点之间的相似性网络。
在这一步中,我们需要将我们在分析中使用的所有特征在 1 到 5 之间进行归一化,因为现在,我们将在单个向量中拟合节点的所有特征,并计算它们之间的相关性。
最小-最大归一化
其中三个特征在一到五之间没有标准化。这些是
- '高度'
- "兄弟姐妹的数量"
- '重量'
将高度属性正常化到 1 到 5 之间。另外两个我们就不用了。
MATCH (p:Person)
//get the the max and min value
WITH max(p.`Height`) as max,min(p.`Height`) as min
MATCH (p1:Person)
//normalize
SET p1.Height_nor = 5.0 *(p1.`Height` - min) / (max - min)
相似网络
我们获取所有特征并推断相似性网络。我们总是希望使用 similarityCutoff 参数和可选的 topK 参数,以防止最终得到一个完整的图,其中所有节点都相互连接。这里我们用 similarityCutoff: 0.75 和 topK: 5 。在文档中找到更多信息。
MATCH (p:Person)
WITH p LIMIT 1
WITH filter(x in keys(p) where not x in ['Gender','Left - right handed','Lying','Alcohol','Education','Smoking','House - block of flats','Village - town','Punctuality','Internet usage','Personality','Music','Dreams','Movies','Fun with friends','Comedy','Medicine','Chemistry','Shopping centres','Physics','Opera','Animated','Height','Weight','Number of siblings']) as all_keys
MATCH (p1:Person)
UNWIND all_keys as key
WITH {item:id(p1), weights: collect(coalesce(p1[key],3))} as personData
WITH collect(personData) as data
CALL algo.similarity.pearson(data, {similarityCutoff: 0.75,topK:5,write:true})
YIELD nodes, similarityPairs
RETURN nodes, similarityPairs
结果
- 节点:1010
- 相似性比例:4254
社区检测
既然我们已经在我们的图中推断出了一个相似性网络,我们将尝试在卢万算法的帮助下找到相似人的社区。
CALL algo.louvain('Person','SIMILAR')
YIELD nodes,communityCount
结果
- 节点:1010
- 社区人数:105 人
Apoc.group.nodes
为了快速浏览 Neo4j 浏览器中的社区检测结果,我们可以使用 apoc.group.nodes 。我们定义想要包含的标签,并根据某个属性进行分组。在配置部分,我们定义我们想要执行的聚合,并在可视化中返回。在文档中找到更多信息。
CALL apoc.nodes.group(['Person'],['community'],
[{`*`:'count', Age:['avg','std'],Alcohol_vec:['avg']}, {`*`:'count'} ])
YIELD nodes, relationships
UNWIND nodes as node
UNWIND relationships as rel
RETURN node, rel;
结果

社区偏好
为了更好地了解我们的社区,我们将检查他们的平均前 3 名和后 3 名偏好。
MATCH (p:Person)
WITH p LIMIT 1
WITH filter(x in keys(p) where not x in ['Gender','Left - right handed','Lying','Alcohol','Education','Smoking','House - block of flats','Village - town','Punctuality','Internet usage','Personality','Music','Dreams','Movies','Fun with friends','Height','Number of siblings','Weight','Medicine', 'Chemistry', 'Shopping centres', 'Physics', 'Opera','Age','community','Comedy','Gender_vec','Internet','Height_nor']) as all_keys
MATCH (p1:Person)
UNWIND all_keys as key
WITH p1.community as community,
count(*) as size,
SUM(CASE WHEN p1.Gender = 'male' THEN 1 ELSE 0 END) as males,
key,
avg(p1[key]) as average,
stdev(p1[key]) as std
ORDER BY average DESC
WITH community,
size,
toFloat(males) / size as male_percentage,
collect(key) as all_avg
ORDER BY size DESC limit 10
RETURN community,size,male_percentage,
all_avg[..3] as top_3,
all_avg[-3..] as bottom_3
结果
结果相当有趣。仅从男性比例来看,可以肯定地说,这些社区几乎都是基于性别的。
最大的群体是 220 名女士,她们强烈赞同“对动物的同情”、“浪漫”和有趣的“借来的东西”,但不同意“金属”、“西方”和“写作”。第二大群体,大部分是男性,同意“学校作弊”,“行动”和“个人电脑”。他们也不同意“写作”。有道理,因为调查是由来自斯洛伐克的学生填写的。
Gephi 可视化
让我们以 Gephi 中我们的社区的一个很好的可视化来结束。您需要在 Gephi 中启用流插件,然后我们可以使用 APOC 过程 apoc.gephi.add 从 Neo4j 中导出图形。
MATCH path = (:Person)-[:SIMILAR]->(:Person)
CALL apoc.gephi.add(null,'workspace1',path,'weight',['community']) yield nodes
return distinct 'done'
在 Gephi 中做了一点调整后,我想出了这个可视化。与 apoc.group.nodes 可视化类似,我们可以观察到,最大的社区彼此之间联系紧密。

马克·尼达姆和艾米·e·霍德勒合著的《图形算法:Apache Spark 和 Neo4j 中的实际例子》现在就注册吧。
在《权力的游戏》数据集上使用 Neo4j 中的种子属性进行社区检测
社区检测算法的作用是识别网络中密集连接的节点组。大多数社区检测算法的第一步是初始化它自己社区中的每个节点。实际上,每个节点都被分配了一个唯一的社区 id。下一步,算法使用各种技术在网络中搜索社区。
种子属性参数允许我们为每个节点定义初始社区 id。使用种子属性可以被认为是半监督社区检测,其中我们基于先验领域知识为一些节点提供初始社区 id。一个用例无疑是跟踪社区如何随时间演变。假设您正在运行一个日常批处理进程,在您的 Neo4j 图中搜索社区。您每天要做的是提供前一天的社区作为种子值。据我目前了解有两个原因。第一个是随着时间的推移跟踪特定的社区更容易,因为通过提供前一天的社区作为种子属性,我们确保社区 id 不会改变。除非一个社区瓦解。据我所知,它也应该更快,因为大多数社区已经从前一天开始计算,所以算法需要更少的迭代。最后,写回应该快一点,因为算法跳过了没有切换社区的节点的写回。
资料组
我们将再次探索《权力的游戏》世界,只是这一次将《T2》的《权力的网络》数据集与《T4》的《Kaggle 权力的游戏》数据集结合起来。《权力的网络》数据集包含了《GoT》书中人物之间的互动。从 Kaggle 数据集中,我们将导入角色的死亡,特别是他们死在哪本书里。
图形模型

Graph model
我们的图表中只有一个标签(人)。人与人之间通过 INTERACTS_X 关系联系在一起,其中 X 是《权力的游戏》系列的书序。例如,INTERACTS_1 关系意味着两个人或角色在第一本书中进行了交互。第四部和第五部被合并成一个网络,因为在《乌鸦的盛宴》(第四部)中缺少了很多主要角色。
导入
为节点定义唯一约束。
CREATE CONSTRAINT ON (p:Person) ASSERT p.id IS UNIQUE;
我们将从导入权力网络数据集开始。没有必要下载任何东西,因为 Neo4j 已经很好地为我们取来了。
UNWIND ['1','2','3','45'] as book
LOAD CSV WITH HEADERS FROM
['https://raw.githubusercontent.com/mathbeveridge/asoiaf/master/data/asoiaf-book](https://raw.githubusercontent.com/mathbeveridge/asoiaf/master/data/asoiaf-book')' + book + '-edges.csv' as value
MERGE (source:Person{id:value.Source})
MERGE (target:Person{id:value.Target})
WITH source,target,value.weight as weight,book
CALL apoc.merge.relationship(source,'INTERACTS_' + book, {}, {weight:toFloat(weight)}, target) YIELD rel
RETURN distinct 'done'
人物的死亡可以作为一个得到的 Kaggle 数据集得到。我们必须在运行导入查询之前下载它。两个数据集中的字符名称并不完全相同。我做了一个简单的匹配,得到了 66%的匹配率,对于这个演示来说已经足够好了。
LOAD CSV WITH HEADERS FROM "file:///character-deaths.csv" as row
WITH row WHERE row.`Book of Death` IS NOT NULL
MATCH (p:Person)
WHERE p.id = replace(row.Name,' ','-')
SET p.death_book = toInteger(row.`Book of Death`)
Cypher 投影
如果你读过我之前的博文,你会发现图形加载器是 Neo4j 图形算法库中的关键组件之一。它将存储的 Neo4j 图投影到内存中的投影图,这更适合运行图形算法。有两种方法可以投影内存中的图形:
在这篇博文中,我们将使用 cypher projection 方法。它由三部分组成:
- Node cypher 语句:cypher 查询,返回我们想要考虑的节点及其属性的 id
- Relationship cypher 语句:返回关系的源和目标节点的 id 以及我们想要考虑的属性的 cypher 查询。属性通常用作权重
- 算法配置:要使用 cypher 投影,我们必须定义
graph:'cypher'
CALL algo.louvain(
// Node cypher statement
'MATCH (n:Node) RETURN id(n) as n, n.seedValue as seedValue',
// Relationship cypher statement
'MATCH (source:Node)-[:RELATIONSHIP]->(target:Node)
RETURN id(source) as source, id(target) as target',
// Algorithm configuration
{graph:'cypher, seedProperty:'seedValue', writeProperty:'community'})
此外,请记住官方文档中的以下段落。
只有在 node 语句中描述了源节点和目标节点时,relationship 语句中描述的关系才会被投影,否则它们将被忽略。
我们可能选择 cypher 投影而不是标签和关系类型投影的原因是,我们获得了 cypher 查询语言的附加值,以及在描述我们想要投影的子图时它的表达能力。它还允许我们投射一个虚拟的(非存储的)图形。
社区检测
首先,我们将在卢万模块化算法的帮助下,根据第一本书中的互动来搜索角色社区。
CALL algo.louvain(
// Filter out only person who
// interacted in the first book
'MATCH (p:Person)
WHERE (p)-[:INTERACTS_1]-()
RETURN id(p) as id'
,
'MATCH (p:Person)-[:INTERACTS_1]-(p1:Person)
RETURN id(p) as source, id(p1) as target'
,{graph:'cypher', direction:'BOTH', writeProperty:'community_1'})
为了这个分析,我决定通过书籍来追溯丹妮莉丝·坦格利安的社区演变。
MATCH (p:Person{id:'Daenerys-Targaryen'})
RETURN p.id as name, p.community_1 as community
我们将在 NeovisJS 的帮助下可视化结果。你可以在 GitHub 上找到代码。

Node color indicates community
丹妮莉丝的社区可以归类为埃索斯社区。它主要由多斯拉克人以及乔拉·莫尔蒙人和伊利里欧·莫帕蒂人组成。与维斯特洛的联系仅限于丹妮莉丝和她的哥哥韦赛里斯·坦格利安通过艾德·史塔克、劳勃·拜拉席恩和瓦里斯。第一本书还没开始,雷加·坦格利安就已经死了。有趣的是,雷加在泰温、珊莎、丹妮莉丝和艾德·史塔克等主要角色周围被提及,你们中的一些人知道这部电视剧后来发生了什么。
种子属性参数
在第二次迭代中,我们将使用第一本书的社区结果作为种子属性。我第一次试着从第二本书开始在网络上运行第二次迭代,但没有得到任何有见地的结果,因为很多人死了,社区变化很大。为了解决这个问题,我综合使用了第一部和第二部书中的网络,只过滤掉了第一部中死去的角色。
关于种子属性需要注意的一点是,它现在不处理空值。所有出现在第二本书,但没有出现在第一本书的新角色,从第一次迭代的社区 id 为空。在 Max de Marzi 的帮助下,我们提出了一个策略,该策略总是为具有空种子属性的节点分配一个唯一的社区 id。我们从第一次迭代中找到最高的社区 id,并添加内部节点 id,以始终保证没有种子属性的节点的唯一标识符。恰当地说,我们将把它命名为最大策略。
CALL algo.louvain('
// Max strategy
MATCH (s:Person) WITH max(s.community_1) as max
MATCH (p:Person)
// Has been mentioned in book 1 or 2
WHERE (p)-[:INTERACTS_1|:INTERACTS_2]-() AND
// Filter our characters who die in the first book
NOT coalesce(p.death_book,10) < 2
RETURN id(p) as id,coalesce(p.community_1, max + id(p)) as community_1
','
MATCH (p:Person)-[:INTERACTS_1|:INTERACTS_2]-(p1:Person)
RETURN id(p) as source, id(p1) as target',
{graph:'cypher',direction:'BOTH',writeProperty:'community_2',
seedProperty:'community_1'})
使用 NeovisJS 可视化的结果。

Node color indicates community
红色社区是第一本书中死去的角色的社区。我们可以看到丹妮莉丝的社区在第一本书中受到了相当大的打击,因为社区中的九个角色都死了。我们还可以注意到,艾德·史塔克和劳勃·拜拉席恩死了,这减少了社区与外部世界的互动,只有瓦里斯和一个新加入的 Baristan Selmy。此外,乔拉和杰奥·莫尔蒙被一起提及,即使他们从未在书中见过面,如果我没记错的话。
第三次迭代
下一步,我们将从第二部和第三部中寻找网络,并过滤掉前两部中死去的角色。
CALL algo.louvain('
// Max strategy
MATCH (s:Person) WITH max(s.community_2) as max
MATCH (p:Person)
// Has been mentioned in book 2 or 3
WHERE (p)-[:INTERACTS_2|:INTERACTS_3]-() AND
// Filter our characters who die in the first or second book
NOT coalesce(p.death_book,10) < 3
RETURN id(p) as id,coalesce(p.community_2, max + id(p)) as community_2
','
MATCH (p:Person)-[:INTERACTS_1|:INTERACTS_2]-(p1:Person)
RETURN id(p) as source, id(p1) as target',
{graph:'cypher',direction:'BOTH',writeProperty:'community_3',
seedProperty:'community_2'})
结果

Node color indicates community
在这个迭代中,死亡角色的群体被涂成绿色。尽管卓戈和劳勃·拜拉席恩已经死了,但他们还是在第二部和第三部中不断被提及。我们可以观察到一些有趣的角色加入到社区中,比如米桑代和达里奥·纳哈里斯。总的来说,这个社区变得更加孤立,因为丹妮莉丝除了通过巴利斯坦·赛尔弥之外,与维斯特洛没有任何交流。
第四次迭代
对于最后一次迭代,我们将基于第三、第四和第五本书中的互动来查看网络中的社区,并过滤掉前三本书中死去的角色。
CALL algo.louvain('
// Max strategy
MATCH (s:Person) WITH max(s.community_3) as max
MATCH (p:Person)
// Has been mentioned in book 3 or 45
WHERE (p)-[:INTERACTS_3|:INTERACTS_45]-() AND
// Filter our characters who die in the first three books
NOT coalesce(p.death_book,10) < 4
RETURN id(p) as id,coalesce(p.community_3, max + id(p)) as community_3
','
MATCH (p:Person)-[:INTERACTS_3|:INTERACTS_45]-(p1:Person)
RETURN id(p) as source, id(p1) as target',
{graph:'cypher',direction:'BOTH',writeProperty:'community_4',
seedProperty:'community_3'})
结果

Node color indicates community
我们可以观察到丹妮莉丝的社区在这次迭代中规模增加了很多。它还通过提利昂·兰尼斯特、多兰·马爹利和瑟曦·兰尼斯特等人物与外部世界建立了大量联系。
结论
在我们进行下一次迭代之前,我们将不得不为第六本书多等一会儿。希望,很快:)
种子属性是对社区检测算法的一个很好的补充。除了 Louvain 算法,连接组件和标签传播算法也支持种子属性。
和往常一样,所有代码都可以在 GitHub 上获得:
社区论坛与数据科学相遇
分析论坛成员的活动、帖子和行为

Photo by Volodymyr Hryshchenko on Unsplash
总结 作为一名对数据科学充满热情的社区建设者和战略家,我发现数据科学技术的使用加深了我对所管理社区的理解,使我能够做出更好的战略和运营决策。在本文中,我旨在举例说明数据科学的使用如何使社区管理者能够更好地了解他们的社区,并提高成员的参与度。
Liferay 是一家开源技术公司,也是我的客户,它主办了 Liferay 社区论坛。这些都是非常活跃的公共论坛,注册会员超过 150,000 人。论坛主要是开发者之间关于 Liferay 技术的交流。在这篇文章中,我分析了 2007 年至 2017 年间论坛成员的活动、帖子和行为。本着开源精神,Liferay 允许我发表对他们过去 10 年成员活动的分析。
数据描述
为了开始我的数据分析(使用 Python),我从所有论坛数据的原始文件开始。通过一些数据清理,我创建了一个包含每个论坛消息细节的文件。文件中的每一行都是唯一的消息,包含以下字段:
- UserId:编写消息的用户
- CategoryID:消息出现的论坛类别,例如“门户框架”
- ThreadId:线程的 Id(包括最初的帖子和所有回复)
- 主题和正文:消息的内容
- 创建日期:消息的日期和时间
我使用命令 forums.tail(5)可视化了文件的最后 5 行:

让我们研究一下这些数据,以了解更多关于社区成员之旅的信息。
第一部分.会员之旅
使用函数和图探索数据:分析成员寿命
对于开发人员或客户的社区,了解成员随时间的变化可以让社区经理了解用户如何发展以及如何在不同阶段最好地吸引他们。
在这里,我看到了论坛中成员之间的典型互动。为了重现单个讨论线程的问题和答案,我使用了
forums.loc[forums.threadId==103912609].head(5)
通过“threadId”进行过滤会产生以下结果:

在这个讨论中,很明显,用户(41397961)正在纠结于一个技术问题:“服务构建器,构建失败”。一些有用的成员要求澄清并提供建议(在这种情况下,可能没有用)。看来有经验的会员正在努力帮助新手。事实上,论坛的一个中心目标是帮助会员在从新手到专家的旅程中前进。
正如 Liferay,Inc .的开发者倡导者 Jamie Sammons 所说:
“大多数贡献者通常从 Liferay 社区开始,作为消费者阅读文档、论坛和 Slack。对于许多人来说,积极参与的第一步是通过询问特定于其环境的问题在论坛中获得帮助。大多数开始帮助他人的贡献者通常是这样开始的,然后当他们觉得自己正在学习诀窍时,有时甚至会觉得有义务帮助他人来回报他们所获得的支持。”
了解会员之旅的另一个方面是确定用户的寿命。典型的会员是只发一次帖子,还是长时间保持活跃?我使用 Python 中强大的“分组”功能来创建这个数据框。你可以在 GitHub 中看到细节,下面出现了我的结果的一个例子。

利用这些数据,观察所有成员寿命的频率分布是很有趣的。与社区中的整体用户寿命相比,有多少用户的寿命只有 1 天?这就是可视化有帮助的地方。下面的图表显示,近 800 名成员的寿命为 1 天,此后频率大幅下降。

上面的图中没有显示的是 12,000 名会员——占发帖会员总数的 42%——他们的寿命为 0 天!(我将 Y 访问设置为最大 1,000,因此没有显示 12,000 成员数据点)。寿命为 0 天意味着第一条和最后一条消息在同一天;有可能这些成员只是发布了一条消息,然后就再也没有回到论坛。
利用数据:社区经理的含义
有了这些数据,社区管理者可以实现一个战略性的双重目标:增加成员的生命周期,并最小化生命周期为 0 天的成员数量。社区经理可以在一个成员写完一篇文章后发起一个特别的推广活动,并向新成员发送个性化消息,鼓励他们继续参与。或许他们甚至可以在“游戏化”系统中奖励新会员积分。
可视化数据:了解资深社区成员的寿命
我也有兴趣了解老会员的典型寿命。因此,我把上图中的时间尺度从几天改为几年,以关注退伍军人和他们在社区中的寿命。
lifespan_years = lifespan_days//365
freqyear = lifespan_years.value_counts()
plt.plot(freqyear,'.');
plt.axis([0, 12, 0,2000]);
plt.xlabel('period from first to last message (YEARS)')
plt.ylabel('member counts')
plt.title('Length of Active Membership')
plt.grid(True)
plt.show()

该图表显示,1800 名会员的论坛寿命为 1-2 年,从第 1 年到第 2 年和第 2 年到第 3 年急剧下降。2 或 4 年后见底并不奇怪,因为开发人员通常有持续 2-4 年的 Liferay 项目,之后他们会转向其他项目或公司。
正如 Liferay,Inc .的开发者倡导者 Jamie Sammons 所说:
“大多数情况下,开发人员为一家咨询公司工作,然后转向一个新项目,或者开发人员可能会彻底跳槽。在少数情况下,开发人员为 SI 或合作伙伴公司工作,由于业务良好,他们的工作量很大,根本没有时间做出贡献。”
利用从数据中获得的洞察力:社区管理含义
当以前活跃的成员在活跃两年多后变得安静时,他可以获得特殊的个人拓展,例如:“发生了什么?我们能为你的下一个项目提供帮助吗?”此外,这些成员可以被邀请参加更高层次的讨论(例如,项目或公司的未来路线图),而不是代码 Q &对话。
其他行为分析主题
使用会员登录信息可以收集到更多的信息。例如,可以根据 90%的注册者的登录时间戳,对他们进行分析,这些注册者根本没有发帖。这些定期登录的安静的“潜伏者”可以从真正不活跃的人中分离出来,他们的行为被分析以鼓励更高水平的参与。通过将论坛记录与其他信息源(如吉拉或 CRM 数据)相结合,可以收集到其他有趣的信息,从而实现对论坛成员跨平台相关活动的 360 度视图。分析社区中流行的发帖时间也可能很有见地,以便营销和传播团队可以确定在论坛中发帖的最佳时间(请记住,国际社区和时区可能会使这种分析变得棘手)。最后,了解计划的影响可能是有趣的。比如软件新版本发布后会员活跃度有没有提高?
分析社区成员在论坛上的活动有助于改善客户旅程和成员参与度。通过分析论坛帖子的实际内容,可以获得更高层次的洞察力。
第二部分。使用自然语言处理(NLP)了解社区
词汇云——理解对话主题
单个用户在一段时间内发布的实际内容可用于构建成员档案词云,组成词云的数据可用于从根本上改善成员在社区中的体验。例如,关于通过词云识别的主题的帖子可以出现在成员的活动提要中,使提要更相关和更有吸引力。
对于下面的单词云,我使用了标准的阻止列表(程序将忽略的单词列表),它保留了诸如“登录”和“社区”之类的单词。为了降低语料库中所有常用词的重要性(“login”是 Liferay 论坛中的常用词),我可以应用“词频-逆文档频率”(TFIDF)。
发布了 887 条消息的单个成员的 Word cloud:
from wordcloud import WordCloud
text = resultdef generate_wordcloud(text):
wordcloud = WordCloud( relative_scaling = 0.5,
stopwords = {'re','to', 'of','for','the','is','Liferay','in','and','on','from','with'}
).generate(text)
plt.imshow(wordcloud)
plt.axis("off")
plt.show()generate_wordcloud(text)

字数和三元模型——确定引发讨论的主要话题
整个社区都在谈论什么,成员们是如何互动的?下面的图表包含了两个 Liferay 论坛类别(“公告”和“开发”)中每个讨论主题行的字数。
对主题中的单词进行标记后,我们可以列出每个类别中的前 10 个单词:
devWords = [word **for** word **in** words **if** word **not** **in** stoplist]
fdist_dev = nltk.FreqDist(devWords)
dev_common = fdist_dev.most_common(10)
然后,我们可以构建一个数据框架来比较不同类别的常用词:
dfWord = pd.DataFrame(
{'Development': dev_common,
'Announcements': announce_common
})
dfWord.index += 1
dfWord

“!”公告类别在庆祝成功的专业团体中更常见(例如,“祝贺销售成功!).在 Liferay 论坛的情况下,对话类似于典型的“信息传播”社区(具有诸如“已发布”和“可用”的关键字)。
那个“?”出现在开发类别中的单词“how”对于这个社区来说是典型的,其中主要的用例是成员向其他成员询问技术或产品相关的问题。这种“问答”内容结构可以通过查看下面最常见的三元模型(3 个单词的组合)来确认。
发展类别三元模型:
tgs = nltk.trigrams(words)
fdistT = nltk.FreqDist(tgs)
fdistT.most_common(5)

关于数据和 NLP 分析的注释
- 对于这种类型的分析,请确保字符如“?”还有“!”被考虑(即不包括在禁止列表中)
- 公告类别比开发类别小得多,因此字数也少得多(但相对排名是最重要的)。
- 在 GitHub 上,我展示了将 1000 个主题行转换成单词列表的步骤(组块、标记化等)。
关键词分析 NLP 如何提高社区管理的效率
NLP 也可以帮助日常的,但基本的,社区管理任务。例如,技术论坛中的一个常见挑战是获得社区成员提出的所有问题的良好答案(从而培养社区内的信任和参与。)
Liferay 使用的典型方法是指派专家负责每个论坛类别。然而,在 Liferay 的案例中,近 40%的 Liferay 信息属于一个类别,“开发”,指派专家来涵盖这整个包罗万象的类别是不切实际的。为了解决这个问题,我们可以在这个类别中寻找一些讨论的关键主题,这些主题可以分离出来,形成更小、更易于管理的讨论类别。关键字分析在这里非常有用,但在这种情况下,字符和短词没有帮助,所以我过滤了较长的词:
long_words = [word for word in words if
len(word) > 2 and word not in stoplist]
fdistLong = nltk.FreqDist(long_words)
fdistLong.most_common(50)

显然,这里有一些很好的候选主题,可以从拥挤的“发展”类别中分离出来:
1.Portlet
2。JSP
3。建造者
4。主题
5。数据库ˌ资料库
二元模型(两个词的组合)揭示了其他好的潜在主题,包括“服务构建器”、“定制 portlet”和“文档库”。了解热门话题及其随时间的变化趋势也很有帮助。例如,在下面的分散图中,很明显,当“6.1”(推测是 Liferay 的一个版本)出来时,社区热烈地讨论了它——然后讨论安静了下来。随着“6.2”的发布,围绕“6.1”和“6.2”的讨论也有所重叠。“6.1”和“6.2”的代码和色散图如下所示。
mytext = nltk.Text(words)
mytext.dispersion_plot(["6.1","6.2"])

以上功能和可视化只是冰山一角;通过分析用户行为和内容,可以发现更多的东西。唉,使用最新的人工智能工具进行有意义的分析,尤其是“非结构化”分析(系统事先不知道它在寻找什么)需要大量数据。即便如此,对小数据集进行相对基本但经过深思熟虑的分析,可以显著改善社区管理。了解成员的行为及其内容可以增强社区管理者有效服务其社区的能力,增加其社区的相关性,进而增加参与度、成员数和影响力。
注:样本数据中未出现姓名或个人详细信息。最初的论坛帖子和成员简介是公开的,所以这是一个额外的警告步骤。
伦敦自行车共享网络的社区结构、互动和动态
通过无监督机器学习获得对共享自行车系统的运营洞察力
几乎每个大城市都有自行车共享计划。所有的自行车都连接在一起,产生无数的数据。大多数运营商提供的数据侧重于站点/自行车可用性。然而,伦敦运输(TfL)公司在其开放数据平台上发布他们的自行车运动(桑坦德 BSS ),使用自行车 ID,让您可以跟踪网络中每辆自行车的运动。😮
这为我们如何分析数据提供了新的可能性。在这篇文章中,我们将探讨如何使用数据挖掘技术来理解伦敦 BSS 中的社区。
什么是社区?
一个社区可以有很多含义。剑桥词典将其定义为:
居住在某一特定地区的人或由于共同的兴趣、社会团体或国籍而被视为一个整体的人
就运输系统而言,这与我们想要做的并不太远。也就是说,我们希望将传输网络的复杂性降低到行为相似的单元或集群中。在这里,我们将站分组,以更好地了解系统如何工作,并能够做出更好的操作决策。
使用 R 和其他工具,我们将简化来自伦敦数据存储的起点-终点数据。
导入和过滤我们的数据
我们选择的数据集已经被提取并复制到我们的工作目录中。我们选择的时间段是 2014 年 6 月至 7 月,包括 150 万次旅行。在通过我们的社区检测算法之前,我们需要采取一些步骤。
为了加载它,我使用了来自data.table的优秀的fread()函数。此外,我们需要删除缺失或等于零的自行车 id 和终点站。我们删除了仅作为目的地(维修店)和周末存在的加油站,这需要进行单独的分析。
社区检测算法
有许多社区检测算法,其中大多数使用模块性的度量来检测集群。但是,在本分析中,我们将把它与最先进的社区检测算法 信息图 进行比较,后者改进了基于模块化的方法,放宽了关键假设:
- 无向信息
- 分辨率限制
- 网络形成的过程
这些将被证明是非常相关的运输集群的准确检测。
我们将比较以下方法:
- 信息图
- 勒芬
- 贪婪的
- 随机漫步
所有这些都可以在伟大的igraph库中运行。在分析时,Infomap 的二进制文件的最新版本还没有实现,我们决定直接使用二进制文件。
要将信息图算法作为系统命令运行,我们可以使用以下命令。注意它只在 MacOSX 上测试过,但是如果你在正确的文件夹中有 Infomap 的二进制文件,它应该可以工作。使用 v0.19.3 版本完成了信息图社区检测。关于如何在您的机器上运行信息图的说明可以在这里找到。
结果
Infomap 在发现伦敦网络中已知的物理结构方面优于基于模块性的社区检测算法(如金丝雀码头和海德公园)。
这些算法都没有给出关于车站位置的信息。但是所有的结果都非常依赖于空间。

Comparison of the community clustering algorithms
鉴于中部/东部社区的规模,我们怀疑其中可能有隐藏的社区。与此同时,金丝雀码头和海德公园似乎大多自成一体。

Average cluster location and connections to the other clusters.
使用 Gephi 软件创建的信息图聚类的独立可视化。它还显示了研究期间不同站点之间的行程。

Results of the Infomap algorithm using Gephi.
奖励回合:动态分析
作为额外的分析,我们仅使用 Infomap 执行动态社区检测。分析表明,社区具有时间依赖性,社区受空间、时间和位置的驱动。

Community dynamics
如果我们要扩大分析数据的时间范围,那么缺少夜间出行就不可能找到可能改变的模式。早上和下午高峰的通勤模式也可能导致极端的结果,即最佳聚类数只有一个。
结论
这种方法使我们能够深入了解网络的运行方式。它可以帮助决策者改善网络和物流运营商优化他们的自行车运动。我们能够找到传统方法无法找到的地理位置偏远或使用模式一致的专业社区。
承认
这篇文章是下一篇文章的姊妹篇
费尔南多·穆尼奥斯·门德斯、康斯坦丁·克莱默、汉克和斯蒂芬·贾维斯。2018.伦敦自行车共享网络的社区结构、互动和动态。在 2018 年 ACM 国际联合会议和 2018 年普适计算和可穿戴计算机国际研讨会(UbiComp '18)的会议录中。美国纽约州纽约市美国计算机学会,1015–1023。https://doi.org/10.1145/3267305.3274156
你可以在这里找到预印本,在这里找到一些用于生成图和结果的代码。
使用 Pandas 比较不同 Excel 文件中的列值

Image Courtesy of Joshua Hoehne via Unsplash
熊猫为列匹配
通常,我们可能希望比较不同 Excel 文件中的列值,以搜索匹配和/或相似性。使用 Python 中的 Pandas 库,这是一个简单的任务。为了证明这是如何可能的,本教程将集中在一个简单的遗传例子。不需要遗传学知识!
考虑以下情况:我有一些未知的 DNA 序列样本(由字母 A、T、G 和 c 组成的简单字符串)。对于这些未知的 DNA 序列,我会分配一个唯一的标识符。为了简单起见,第一个未知序列将被标识为 1,第二个被标识为 2,依此类推,如下所示。

在这个有些人为的例子中,我想比较我的未知序列和已知的参考序列。
简而言之,我的问题是,‘未知 _ 样本 _ 否’中的任何一个与我的‘引用 _ 序列 _ ID’匹配吗?如果有,它们与哪个(些)序列匹配。

为了开始回答这个问题,我首先导入 pandas 模块,并将相应的文件作为 csv 文件读取。注意:我将这些文件在 Excel 中保存为逗号分隔值文件(csv 文件),并使用 read_csv() 函数解析它们。严格来说,这不是必须的,但这是我喜欢的一种工作习惯。您也可以将 Excel 文件保留在原生的中。xlsx 扩展名,并使用 pandas.read_excel() 函数在这里保存一个步骤。
查询序列 DataFrame 被赋予了变量 A,而序列引用 DataFrame 被赋予了变量 b。
然后,我将两个数据帧中的两列都转换成 python 列表。为此,我使用了。tolist() 对特定数据帧的指定列执行方法。举例来说,数据帧 A 中的列“未知 _ 样本 _ 否”被转换成列表。我对分布在两个 Excel 文件中的其他三列中的每一列执行这个步骤,如下面的代码片段所示。
我想保留“未知 _ 样本 _ 编号”与其对应的“未知 _ 样本 _ 序列”之间的关联。此外,我还想保留‘Reference _ sequences _ ID’与其对应的‘Reference _ sequences’之间的关联。Python 提供了一种维护这种关联的简单方法。我把我的清单转换成两本字典。
我使用 zip 函数连接列表,然后使用 dict 函数将它们转换到适当分配的字典中。
为了确认字典已经被正确创建,我在我的终端上运行这个脚本。一个简短的验证检查告诉我一切都在按计划进行。例如,在 Ref_dict 中,“Reference_sequences_ID”键与其对应的“Reference_sequences”值正确匹配:{ ' A ':' aaaaagcgcgaggggga ',' K': 'GGGAGAGAGGG ',' Y ':' cggacgttt '…..}
我现在可以把我的两本词典互相比较了。为此,我使用一个 for 循环来遍历我的“Unknown_dict ”,并使用一个嵌套的 for 循环来遍历 Ref_dict。在每次迭代时,我想知道 Unknown_dict 中的序列是否与 Ref_dict 中的任何序列匹配(有些会,我故意包括了 8 个 匹配)。为了检查匹配,我使用了来自 re 模块(正则表达式模块)的 re.search() 函数。
当有匹配时,我认为知道什么序列匹配,匹配出现在 DNA 序列中的什么位置,以及最重要的是什么‘未知 _ 样本 _ 编号’与哪个‘参考 _ 序列 _ ID’匹配将是有用的。
为了使事情更清楚,如果所有的匹配都被写到一个可以在 Excel 中打开的 csv 文件中,那将是非常理想的。理想情况下,目标是让任何想要解释数据的人都清楚,哪些序列匹配,匹配序列是什么,在哪里匹配。
为了实现这个目标,我写了一个名为“seq_match_compare.csv”的文件,当我打开 csv 文件时,所有我想要的信息都以一种可解释的方式出现了!
例如,我现在知道 Query_ID: 1 对应于 Ref_species A,这个匹配从序列中的位置 4 开始。

有多种方法可以比较两个不同 excel 文件中的列值。这里的方法将第一个文件的未知序列列中的每个序列与第二个文件的 Reference_sequences 列中的每个序列进行检查。
这种方式代表了一种简单的匹配和比较方式,如果我们想要分析任意数量的样品,这种方式提供了极大的可扩展性!
所有这些都是通过熊猫实现的!
比较重复测量数据的常用分析策略
处理数据中的依赖关系。
这是怎么回事?
我希望这篇文章能提供一个关于如何处理社会科学中常见的特定类型数据集的概念概述(在我自己的实验心理学和认知神经科学学科中也很常见)。我的目标不是提供数学形式,而是建立一些直觉,并尽可能避免术语。具体来说,我想比较一些人们可以使用的更常见的分析策略,以及它们如何因情况而异,最后是一些有望指导您未来决策的要点。但要做到这一点,我们需要从头开始…
重复什么?
数据集有各种不同的形状和大小。在许多介绍性教程、课程甚至真实世界的例子中,人们通常会处理被称为满足许多常见统计模型的“i.i.d .假设”的数据集。这在英语中是什么意思?它指的是每个数据点在很大程度上独立于完整数据集中的其他数据点。更具体地说,这意味着模型的残差(即模型无法解释的剩余部分)彼此独立,并且它们都来自于相同的分布,其平均值为 0,标准差为σ。换句话说,了解模型产生的一个错误很少会告诉你模型产生的任何其他错误,并且通过扩展,了解一个数据点的一些情况很少会告诉你任何其他数据点。
然而,许多类型的数据包含“重复”或“复制”,例如在一段时间内或在不同条件下测量相同的人。这些数据明显违反了这个假设。在这些情况下,一些数据点比其他数据点更相似。违反这些假设会导致模型估计不尽如人意( Ugrinowitsch 等人,2004 )。更隐蔽的问题是,使用这些估计值做出的推断(例如,计算 t-统计量和延伸的 p 值)可能非常不准确,并产生假阳性 ( Vasey & Thayer,1987 )。让我们通过考虑两个不同的数据集来使这一点更加具体。

在案例 1(左)中,我们给 21 个人每人一次调查,并尝试查看他们的调查回答是否与他们的一些人口统计数据有任何关系。总共 21 个数据点,非常简单。在案例 2(右图)中,我们给 3 个人每人 7 次调查,做同样的事情。总共 21 个数据点,但是这次每个数据点不是相互独立的。在第一种情况下,每个调查答复都是相互独立的。也就是说,了解一个人的反应并不能告诉你另一个人的反应。然而,在第二种情况下,这是不正确的。在你第一次调查时了解了 A 的一些调查结果,在你第二次调查时,你会对 A 的调查结果有更多的了解,而这也必然会给你更多关于 B 的回答的信息。因此不独立。在最极端的情况下,忽略数据中的这些依赖性来估计模型可能会完全逆转最终的估计,这种现象被称为辛普森悖论。
分析策略。
那么我们通常会怎么做呢?有一些不同的分析“传统”以不同的方式处理这个问题。这绝不是一个详尽的列表,而是许多不同文献中相当常见的方法。
多层次模型
像许多其他心理学/神经科学研究人员一样,我第一次被告知重复测量 ANOVAs 是分析这类数据的唯一方法。然而,这已经有点脱离实际,而支持多级/混合效果建模的更灵活的方法( Baayen 等人,2008 )。我不想关注为什么多层建模通常更可取,因为这是一个不同的讨论(例如,更好地处理缺失数据,不同数量的重复,额外水平的重复,不同数量的重复等),但足以说,一旦你开始使用这种方法,基本上没有理由再次运行重复测量的方差分析。深入研究这些模型如何工作的所有细节超出了本文的范围,但是我将链接一些参考资料。从概念上讲,多级建模同时估计描述整个数据集以及每组重复内的关系的系数。在我们上面的例子中,这相当于估计调查响应和调查应答者的整个人口统计之间的关系,以及个人偏离这些估计的程度。这具有将估计及其相关误差“汇集”在一起的净效果,并且如果你熟悉贝叶斯术语,其工作方式与使用先验知识并不完全不同,或者如果机器学习更适合你,其工作方式与使用正则化/平滑并不完全不同。以这种方式估计模型的结果意味着估计可以“相互帮助”,这样,如果我们的一些调查受访者没有在每次我们要求他们填写调查时填写调查,我们就可以估算值,或者我们可以通过假设个人的估计都来自同一人群来“清理”我们从特定个人那里获得的有噪声的估计,从而限制他们可能采取的不可靠的值。
在实践中,使用这些模型可能有点棘手。这是因为,如何设置这些模型进行评估并不是一目了然的。例如,我们是否应该假设每个回答者的调查结果和人口统计数据之间有不同的关系?或者我们应该简单地假设他们的调查结果平均起来不同,但随着人口统计数据的变化而变化?具体来说,用户对于如何指定模型的“随机效应”(偏差估计)部分有多种选择。你可能遇到过像“随机截距”或“随机斜率”这样的术语在我们的示例中,这是允许模型学习每个人的调查响应的唯一平均估计和学习每个人的调查响应和人口统计结果测量之间的关系的唯一回归估计之间的差异。在许多情况下,计算一个可以计算的完整系数集(截距、斜率以及每个预测值之间的相关性)(巴尔等人,2013 )会导致模型无法收敛,给用户留下不可靠的估计。这导致了一些建议,即保持模型相对于人们试图做出的推理相对“简单”(贝茨等人,2015 ),或者比较不同的模型结构,并在执行推理之前使用模型选择标准在它们之间进行裁决( Matuschek 等人,2017 )。很棘手吧?尝试这个指南来帮助你,如果你冒险沿着这条路走下去,或者查看这个帖子来一个漂亮的视觉治疗。 Brauer & Curtin,2018 是一个特别好的一站式商店,提供审查、理论、实践、评估问题和代码片段。如果多层次的模型让你兴奋的话,有吨的资源 可用。
稳健/校正的标准误差
在其他学术领域,有一种完全不同的传统来处理这些类型的数据。例如,在一些经济学学科中,“稳健/三明治/休伯-怀特”标准误差是为标准线性回归模型计算的。本讲座提供了这些技术的数学概述,但总的来说,这种方法需要使用普通最小二乘(OLS)回归以“典型”方式计算回归系数,但需要“校正”这些估计量的方差(即标准误差),以了解它们的异方差程度。也就是它们的方差相差多少。有几种方法可以解释异方差性,包括小样本和自相关校正等,但另一种方法是计算关于数据中“簇”或分组因子的稳健估计。在上面的例子中,聚类将包括调查回答者,并且每个调查响应将包括该聚类中的数据点。因此,这种方法完全忽略了在计算回归系数时存在重复测量的事实,而是在通过调整其标准误差对这些系数进行推断时考虑重复测量数据。有关这种计算的概述,请参见本演示文稿,有关更正式的处理,请参见卡梅伦&米勒,2015 。
两阶段回归/汇总统计方法
最后,我们可以使用的第三种方法是所谓的两阶段回归或汇总统计方法( Frison & Pocock,1992;福尔摩斯&弗里斯顿,1998 。这种方法是功能磁共振成像数据分析的常规方法(芒福德&尼科尔斯,2009 )。从概念上来说,这看起来像是将一个标准的 OLS 回归模型分别拟合到每个调查对象,然后将第二个 OLS 模型拟合到每个个体对象的拟合系数。在最简单的情况下,这相当于对个人系数计算单样本 t 检验。您可能会注意到,这种方法“感觉”类似于多级方法,在通俗英语中,实际上存在多级建模。但是,请注意每个第一级模型是如何完全独立于每个其他模型进行估计的,以及它们的误差或估计值的方差是如何以任何有意义的方式进行合计的。这意味着我们失去了从上述正式的多层建模框架中获得的一些好处。然而,我们可能失去的好处在简单性中得回了:没有额外的选择要做,例如选择一个适当的“随机效应”结构。事实上, Gelman,2005 指出,两阶段回归可以被视为多级建模的一种特殊情况,在这种情况下,我们假设个体/聚类级别系数的分布具有无限方差。
我们如何决定?
拥有所有这些工具有时会让我们很难判断哪种方法更适合什么情况,以及是否有一种方法总是比其他方法更好(剧透:没有)。为了更好地理解我们何时可以使用每种方法,让我们考虑一些我们可能遇到的最常见的情况。我将这些称为我们的数据可以变化的“维度”。
维度 1:我们想要做出推论的单位的样本大小
不同数据集之间最常见的差异就是它们的大小,也就是我们真正要处理的观察值的数量。在非独立数据的情况下,分析师可能最感兴趣的是对数据的特定“级别”进行推断。在我们的调查示例中,这是对“人”的概括,而不是调查的具体实例。所以这个维度根据我们采样的个体数量而变化,不管我们对任何给定的个体采样了多少次。
维度 2:嵌套在我们想要推断的单元中的单元的样本大小
我们的重复测量数据可能变化的另一个方面是我们要处理多少次重复。在上面的例子中,这是我们对任何给定个体的观察数量。每个人都填写了 5 次调查吗?10?100?因此,这个维度根据我们对任何给定个体的采样频率而变化,而不管我们总共采样了多少个个体。
维度 3:我们想要做出推论的单元之间的可变性
每种分析方法的一个关键不同之处在于它们如何处理(或不处理)不同重复组之间的可变性。在上面的例子中,这是个人之间的方差。不同的人真的会有不同的反应吗?在一个极端,我们可以将每一个人的调查回答视为完全独立的,忽略我们多次调查个人的事实,假装每一次调查都是完全独特的。另一方面,我们可以假设调查响应和人口统计之间的关系来自更高层次的分布,并且特定人的估计是该分布的实例,保留每个人自己的响应彼此之间比他们与其他人的响应更相似的事实。我将在下面再回到这个话题。
模拟可以帮助我们建立直觉。
通常在这种情况下,我们可以使用模拟数据,以特定的方式设计变化,以帮助我们获得一些洞察力,了解这些事情如何影响我们不同的分析策略。让我们看看这是什么样子。我将主要使用我编写的 pymer4 Python 包来模拟一些数据并比较这些不同的模型。我最初写这个包是为了减少我在工作中不断在 R 和 Python 之间来回切换的切换成本。我很快意识到我对 R 的主要需求是使用奇妙的 lme4 包进行多层建模,因此我编写了这个 Python 包,作为一种在 Python 内部使用 lme4 的方式,同时与其他科学 Python 堆栈(例如 pandas、numpy、scipy 等)配合良好。从那时起,该软件包已经有了很大的发展( Jolly,2018 ),包括适应上面讨论的不同类型的模型和模拟不同类型的数据的能力。好了,我们开始吧:
# Import what we need
import pandas as pd
import numpy as np
from pymer4.simulate import simulate_lmm, simulate_lm
from pymer4.models import Lm, Lmer, Lm2
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_context('poster')
sns.set_style("whitegrid")
%matplotlib inline
从小处着手
让我们从单个模拟数据集开始,并拟合上面讨论的每种类型的模型。下面我正在生成多层次的数据,类似于上面的玩具例子。数据集由 50 个“人”组成,每个人有 50 个“复制品”。对于每个人,我们测量了 3 个独立变量(例如 3 个调查问题),并希望将它们与 1 个因变量(例如 1 个人口统计结果)联系起来。
num_obs_grp = 50
num_grps = 50
num_coef = 3
formula = 'DV ~ IV1 + IV2 + IV3' data, blups, betas = simulate_lmm(num_obs_grp, num_coef, num_grps) data.head()
我们可以看到,整个数据集是如上所述生成的。以这种方式模拟数据还允许我们为数据集中的每个人生成最佳线性无偏预测( BLUPs )。这些是每个人的系数。

blups.head()

最后,我们还可以检验生成这些数据的“真实”系数。这些是我们希望我们的模型能够恢复的“正确答案”。由于这些数据是使用添加到每个人的数据中的噪声( mu = 0, sigma = 1)进行模拟的,并且个体之间存在差异(pymer4 的默认值为 sigma = 0.25),因此我们不期望这些参数能够完美恢复,而是非常接近(我们将在下面对此进行更深入的探讨)。
# Regression coefficients for intercept, IV1, IV2, and IV3
print(f"True betas: {betas}") True betas: [0.18463772 0.78093358 0.97054762 0.45977883]
评估绩效
是时候评估一些建模策略了。对于每种模型类型,我将根据所述数据拟合模型,然后计算 3 个指标:
- 系数恢复的绝对误差 —这只是真实系数和估计系数之间的绝对值差之和。它给出了我们的模型相对于数据生成系数的总误差。我们可以计算平均值,而不是总和,但是由于我们的模拟数据都在相同的范围内,总和为我们提供了我们期望恢复的确切数量。
- 模型标准误差之和 —这个和下一个度量与我们想要对参数做出的推断更相关。SE 和相关的置信区间告诉我们在给定这种特定建模策略的情况下,估计值周围的总方差。同样,我们可以计算平均值,但是像上面一样,总和给出了所有参数的总方差。
- 模型 T-统计量之和 —这是我们的模型估计的 T-统计量的绝对值之和。这给了我们一种感觉,我们有多大可能带着这样的推断离开,即在我们的自变量和因变量之间有一种统计上显著的关系。在其他条件相同的情况下,较大的 t-stats 通常意味着较小的 p-值,因此我们可以建立一种直觉,了解我们的建模策略有多敏感,告诉我们“是的,这是一个统计上显著的影响。”
多层次模型
让我们从拟合一个多级模型开始,该模型指定了我们可以估计的所有可能参数的完整集合。这样做的效果是让每个人都有自己的一套回归估计,同时仍然将这些估计视为来自一个共同的分布。您可以在下面看到,我们可以很好地恢复参数,正如我们预期的那样,我们所有的结果都是“显著的”
# Fit lmer with random intercepts, slopes, and their correlations lmer = Lmer(formula + '+ (IV1 + IV2 + IV3 | Group)',data=data) lmer.fit(summarize=False) print(f"Absolute Error of Coef Recovery: {diffs(betas, lmer.coefs['Estimate'])}") print(f"Sum of Model Standard Errors: {lmer.coefs['SE'].sum()}") print(f"Sum of Model T statistics: {lmer.coefs['T-stat'].abs().sum()}")

Absolute Error of Coef Recovery: 0.10582723675727804
Sum of Model Standard Errors: 0.16626160033359066
Sum of Model T statistics: 54.46274837271574
接下来,让我们看看当我们用最简单的“随机效应”结构拟合一个多级模型时会发生什么。请注意,由于不让每个人自由拥有自己的估计值(除了他们自己的均值/截距),我们的系数恢复会下降一点,但我们的 t 统计量会显著增加。这看起来是由系数的方差估计(标准误差)非常小这一事实驱动的。在其他条件相同的情况下,我们更有可能使用更简单的,或者在这种情况下,“错误指定”的多水平模型来识别“显著”关系,因为我们知道数据是这样生成的,每个人实际上有不同的 BLUPs。
# Fit lmer with random-intercepts only
lmer_mis = Lmer(formula + '+ (1 | Group)',data=data) lmer_mis.fit(summarize=False) print(f"Absolute Error of Coef Recovery: {diffs(betas,lmer_mis.coefs['Estimate'])}") print(f"Sum of Model Standard Errors: {lmer_mis.coefs['SE'].sum()}") print(f"Sum of Model T statistics: {lmer_mis.coefs['T-stat'].abs().sum()}")

Absolute Error of Coef Recovery: 0.1311098578975485
Sum of Model Standard Errors: 0.10466304433776347
Sum of Model T statistics: 101.04453264690632
聚类稳健模型
接下来,让我们来评估聚类稳健误差建模方法。请记住,这涉及到估计一个单一的回归模型,以获得系数估计,但然后应用一个校正因子的 SEs,从而 t 统计量来调整我们的推论。看起来我们的系数恢复与上面的简单多层模型大致相同,但是由于较大的标准误差和较小的 t 统计量,我们的推断要保守得多。事实上,这些甚至比我们最初估计的完全指定的多级模型还要保守一点。
# Fit clustered errors LM
lm = Lm(formula,data=data) lm.fit(robust='cluster',cluster='Group',summarize=False)print(f"Absolute Error of Coef Recovery: {diffs(betas,lm.coefs['Estimate'])}") print(f"Sum of Model Standard Errors: {lm.coefs['SE'].sum()}") print(f"Sum of Model T statistics: {lm.coefs['T-stat'].abs().sum()}")

Absolute Error of Coef Recovery: 0.13278703905990247
Sum of Model Standard Errors: 0.17543089877808005
Sum of Model T statistics: 51.03327490657406
两阶段回归
最后,让我们使用两阶段回归法。我们将对 50 个人中的每个人进行单独的回归,然后对这 50 个系数进行另一次回归计算。在这个简单的例子中,我们实际上只是对这 50 个系数进行单样本 t 检验。请注意,我们的系数恢复比我们完全指定的多级模型好一点点,我们的推论(基于 T-stats 和 SEs)在很大程度上是相似的。这表明,对于这个特定的数据集,我们可以采用任何一种策略,并得出相同的结论。
# Fit two-stage OLS
lm2 = Lm2(formula,data=data,group='Group')
lm2.fit(summarize=False)print(f"Absolute Error of Coef Recovery: {diffs(betas, lm2.coefs['Estimate'])}") print(f"Sum of Model Standard Errors: {lm2.coefs['SE'].sum()}") print(f"Sum of Model T statistics: {lm2.coefs['T-stat'].abs().sum()}")

Absolute Error of Coef Recovery: 0.09216204521686983
Sum of Model Standard Errors: 0.16523098907361963
Sum of Model T statistics: 55.00162203260664
模拟一个宇宙。
现在,这只是一个特定的数据集,具有特定的大小和特定的个体差异水平。还记得上面概述的维度吗?我们想要回答的真正问题是,这些不同的建模策略在每个维度上是如何变化的。因此,让我们在这里扩展我们的模拟。让我们生成一个设置“网格”,这样我们就可以在合理的时间内模拟所有维度的组合。这是我们将尝试模拟的网格:

接下来,我们将从 5 到 100 改变维度 1 我们正在进行推理的单位的样本大小(人数)。遍历各个列,我们将改变维度 2 ,即嵌套在我们正在进行推断的单元内的单元的样本大小(每人的观察次数)从 5 到> 100 不等。在 z 平面上,我们将改变维度 3 我们正在进行推理的单位之间的方差(人与人之间的可变性)从 0.10 - > 4 个标准差。
因为改变维度 1 和维度 2 应该有直观的意义(它们是我们数据样本大小的不同方面),所以让我们探索一下改变维度 3 是什么样子的。这里的图表说明了改变个人之间的方差是如何影响系数的。下图描述了人员水平系数的分布;这些是我们上面讨论过的虚张声势。当模拟具有由截距和斜率(IV1)描述的两个参数的数据集时,请注意每个分布如何以参数的真实值为中心,但是随着组间方差的增加,分布的宽度也会增加。这些分布是我们的人员级别参数所来自的分布。因此,虽然它们的平均值是相同的,但它们越来越分散在这个值周围。随着这些分布变得更宽,如果数据集太小,恢复数据的真实系数变得更具挑战性,因为模型需要更多的数据来稳定它们的估计。
为了简洁起见,我删除了下图的绘图代码,但我很乐意根据要求分享它们。

设置它
下一个代码块设置这个参数网格,并定义一些助手函数来计算上面定义的指标。由于这个模拟在 2015 四核 Macbook Pro 上运行大约需要 50 分钟,所以我还定义了一些函数来将每个模拟保存到 csv 文件中。
# Define the parameter grid
nsim = 50 # Number of simulated datasets per parameter combination num_grps = [10, 30, 100] # Number of "clusters" (i.e. people) obs_grp = [5, 25, 100] # Number of observations per "cluster" grp_sigmas = [.1, .25, 1., 2., 4.] # Between "cluster" variance num_coef = 3 # Number of terms in the regression equation noise_params = (0, 1) # Assume each cluster has normally distributed noise seed = 0 # to repeat this simulation
formula = 'DV ~ IV1 + IV2 + IV3' # The model formula # Define some helper functions. diffs() was used above examining each model in detail
def diffs(a, b):
"""Absolute error"""
return np.sum(np.abs(a - b)) def calc_model_err(model_type, formula, betas, data):
"""
Fit a model type to data using pymer4\. Return the absolute error
of the model's coefficients, the sum of the model's standard
errors, and the sum of the model's t-statistics. Also log if the
model failed to converge in the case of lme4\. """ if model_type == 'lm':
model = Lm(formula, data=data)
model.fit(robust='cluster',cluster='Group',summarize=False)
elif model_type == 'lmer':
model = Lmer(formula + '+ (IV1 + IV2 + IV3 | Group)',data=data)
model.fit(summarize=False, no_warnings=True)
elif model_type == 'lmer_mis':
model = Lmer(formula + '+ (1 | Group)',data=data)
model.fit(summarize=False, no_warnings=True)
elif model_type == 'lm2':
model = Lm2(formula,data=data,group='Group')
model.fit(n_jobs = 2, summarize=False) coef_diffs = diffs(betas, model.coefs['Estimate'])
model_ses = model.coefs['SE'].sum()
model_ts = model.coefs['T-stat'].abs().sum()
if (model.warnings is None) or (model.warnings == []):
model_success = True
else:
model_success = False
return coef_diffs, model_ses, model_ts, model_success, model.coefs def save_results(err_params, sim_params, sim, model_type, model_coefs, df, coef_df, save=True):
"""Aggregate and save results using pandas""" model_coefs['Sim'] = sim
model_coefs['Model'] = model_type
model_coefs['Num_grp'] = sim_params[0]
model_coefs['Num_obs_grp'] = sim_params[1]
model_coefs['Btwn_grp_sigma'] = sim_params[2]
coef_df = coef_df.append(model_coefs)
dat = pd.DataFrame({
'Model': model_type,
'Num_grp': sim_params[0],
'Num_obs_grp': sim_params[1],
'Btwn_grp_sigma': sim_params[2],
'Coef_abs_err': err_params[0],
'SE_sum': err_params[1],
'T_sum': err_params[2],
'Fit_success': err_params[3],
'Sim': sim
}, index = [0]) df = df.append(dat,ignore_index=True)
if save:
df.to_csv('./sim_results.csv',index=False)
coef_df.to_csv('./sim_estimates.csv')
return df, coef_df # Run it results = pd.DataFrame()
coef_df = pd.DataFrame()
models = ['lm', 'lm2', 'lmer', 'lmer_mis']
for N in num_grps:
for O in obs_grp:
for S in grp_sigmas:
for I in range(nsim):
data, blups, betas = simulate_lmm(O, num_coef, N, grp_sigmas=S, noise_params=noise_params)
for M in models:
c, s, t, success, coefs, = calc_model_err(M, formula, betas, data)
results, coef_df = save_results([c,s,t, success], [N,O,S], I, M, coefs, results, coef_df)
结果
为了简洁起见,我删除了下图的绘图代码,但我很乐意根据要求分享它们!
系数恢复

好,让我们先看看我们的系数恢复。如果我们从网格的左上往下看,首先要跳出来的是,当我们增加总体样本量(聚类数每个聚类的观察数)时,我们的聚类间可变性为中到低,所有的模型类型在恢复真实数据生成系数方面做得同样好。换句话说,在良好的条件下(大量数据不太多变),我们选择任何分析策略都不会出错。相反,从左下方到右上方,当聚类之间的可变性很高时,我们很快看到拥有更多聚类*而不是每个聚类更多观测值的重要性;如果没有足够的聚类来观察,即使是完全指定的多级模型也不能很好地恢复真实系数。
当我们有小到中等规模的数据集和大量的聚类之间的可变性时,所有模型往往在恢复真实系数方面表现不佳。有趣的是,每个聚类(最左边的列)的观测值特别少会不成比例地影响两阶段回归估计(橙色箱线图)。这与 Gelman,2005 的观点是一致的,他认为,由于每个聚类的观测值很少,第一级 OLS 估计值非常差,具有高方差,并且没有多级建模的好处来帮助抵消这种情况。这种情况似乎也最有利于完全指定的多级模型(绿色方框图),特别是当聚类之间的可变性很高时。有趣的是,在这种情况下,集群健壮和错误指定(简单)的多级模型似乎表现相似。
在中等数据情况下(中间列),聚类稳健模型似乎在恢复系数方面表现稍差。这很可能是因为估计值完全忽略了数据的聚类性质,并且没有通过平均(在两阶段回归模型的情况下)或通过随机效应估计(在多级模型的情况下)对其进行平滑/正则化。
最后,在每个聚类的高观测值的情况下(最右边的列),所有模型似乎表现得非常相似,这表明当我们密集地对感兴趣的单元进行采样(每个聚类的观测值增加)时,每种建模策略都与任何其他策略一样好,即使希望对聚类本身进行推断。
做出推论(SEs + T-stats)


接下来,让我们看看标准误差和 t-统计量,看看我们的推断可能如何变化。组间方差的增加对 SEs 和 t-stats 值有非常显著的影响,通常使其不太可能识别统计显著性关系,无论数据大小如何。有趣的是,两阶段回归模型在每个聚类的观测值很少的情况下表现出较差的系数恢复,它们用较高的标准误差估计来弥补。我们可以看到,在这些情况下,他们的 t-统计量较低,这表明在这些情况下,这种方法可能会使天平倾向于较低的假阳性,较高的假阴性推断。然而,与其他模型类型不同,它们不一定受益于总体上更多的集群(左下图),并且冒着假阴性水平膨胀的风险。错误指定的多水平模型似乎具有相反的性质:在大多数情况下,它们具有较高的 t-stat 和较低的 SEs,具有中到高的类间可变性,并且在每个类具有大量观察值的情况下受益最多。这表明,在其他模型可能表现得更保守,但也可能在面临高簇间方差时对检测真实关系更敏感的情况下,它们可能会冒引入更多假阳性的风险。他们似乎也从星团内越来越多的观测中获益最多。从聚类稳健和完全指定的多水平模型的推论似乎在很大程度上是可比的,这与这些模型类型在多个文献中的广泛使用是一致的。
额外好处:当完全指定的多级模型失败时

最后,我们可以简单地看一下,对于我们完全指定的多层模型,什么情况最常导致收敛失败(注意:前面检查的简单多层模型在这些模拟中从未失败过)。一般来说,这似乎发生在聚类之间的可变性较低时,或者每个聚类的观察数量很少时。这是有意义的,因为即使数据是以多级方式生成的,聚类也是非常相似的,并且通过丢弃试图以有意义的方式对数据可能不显示的方差进行建模的项来简化模型(例如,丢弃“随机斜率”)总体上实现了更好的估计。换句话说,模型可能试图拟合一个足够小的方差参数,以使其在达到适当小的误差变化之前用完优化器迭代。这就像通过比较你当前台阶的高度和前一个台阶的高度,试图在一个坡度很小的“小山”上找到最低点。
结论
那么我们学到了什么?以下是我认为这个练习有助于充实的一些直觉:
- 当每个聚类有足够的观测值时,保留两阶段回归。这是因为在没有多级建模所施加的任何类型的平滑/正则化/先验的情况下单独对每个聚类进行建模,在这些情况下会产生较差的第一级估计。
- 小心使用错误指定/简单的多级模型。虽然它们可以消除指定模型的“随机效应”部分所涉及的一些复杂性,并且它们几乎总是收敛,但相对于其他方法(所有其他条件都相同),它们更有可能导致具有统计意义的推论。如果您的数据没有表现出足够的集群间差异,这可能是有保证的。一般来说,最好是指定一个模型结构,该结构考虑了与感兴趣的预测值混淆的方差( Barr 等人,2013 )(即,去掉随机截距和随机斜率之间的相关项,而不是去掉随机斜率),换句话说,就是你可以得到的最“最大”的结构,这与你想要对数据做出的推断有关。
- 如果您的主要目标是进行推理,并且您可以接受比其他方法精度稍低的系数估计,那么聚类稳健模型似乎是一种有效的解决方案。如果数据中存在多个聚类级别(例如,个人内、城市内、州内等的调查回答),或者项目级别的影响是否重要,则更难确定( Baayen 等人,2008 )。然而,有一些技术可以合并双向或多向群集鲁棒性误差,这种方法在经济学中相当常见。本讲座和本论文将进一步讨论这些方法。本文使用的 Pymer4 只实现了单向集群。
- 考虑使用两阶段最小二乘法或聚类稳健误差,而不是错误指定的多层模型,因为您的推理可能在很大程度上类似于成功收敛的完全指定的多层模型。如果需要考虑项目级别的差异或多级别的聚类,这可能是不正确的,但对于本文中说明的相对简单的情况,它们似乎很好。
- 一般来说,模拟是建立统计直觉的有用方法,尤其是当背景数学令人畏惧的时候。这是我更深入地学习统计概念的首选方法之一,并使阅读大量数学形式的文献变得更加容易。
警告和注意事项
我不想在结束这篇文章时感觉我们已经想通了一切,现在是专家分析师了,而是意识到这个练习有一些限制,值得记住。虽然我们可以建立一些一般的直觉,但在某些情况下,这些直觉可能并不总是成立,意识到这些直觉非常重要:
- 在许多方面,这些模拟中生成的数据对于试验这些不同的方法来说是“理想的”。数据点都在相同的范围内,来自具有已知均值和方差的正态分布,不包含缺失的数据点,并且符合未讨论的这些统计方法的其他基本假设。
- 同样,我将“真实关系”内置到数据中,然后试图恢复它们。我在整篇文章中提到了假阳性和假阴性,但我没有正式估计这些方法的假阳性或假阴性率。同样,这是为了给你留下一些一般的直觉,但有几篇论文利用这种方法更明确地为某些推理技术的使用辩护(例如 Luke,2017 )。
- 我们探索的参数空间(即我们数据的不同“维度”)跨越了一个范围,我认为这个范围合理地涵盖了在实证社会科学实验室研究中经常收集的各种数据集。在现实世界中,数据要混乱得多,而且越来越大。越多的数据几乎总是越好,特别是如果它是高质量的,但是基于一个人想要做出的推论,质量的构成可能是非常不同的。有时聚类之间的高可变性是可取的,其他时候密集地对一小组聚类进行采样更重要。这些因素会根据一个人试图回答的问题而变化。
- 我选择用来评估每个模型的指标只是我想知道的。当然,根据您想要建立的直觉,还有其他或多或少能提供信息的指标。比如每个模型的预测精度如何?
结论
我希望这对外面的一些人有用,即使没有用,它也确实帮助我建立了一些关于可用的不同分析策略的直觉。此外,我希望,如果没有别的事情,这可能会激励那些觉得自己在统计/机器学习方面接受的正规培训有限的人,采取一种更修补/黑客的方法来进行自己的学习。我记得当我还是个孩子的时候,打碎东西并把它们拆开是我最喜欢的了解事物工作原理的方式之一。随着像 scientific Python 和 R 这样的免费开源工具的大量出现,我看不出有什么理由统计教育不能一样。
附录
这是一本不错的快速指南,它定义了不同领域的许多术语,并以更简洁的方式回顾了这里涉及的许多概念(还有更多)。对于感兴趣的人,使用 lmerTest R 软件包计算多级模型的 p 值,使用 Satterthwaite 近似法计算自由度;请注意,基于指定的随机效应结构,这些自由度可能会发生显著变化。其他模型类型的 p 值是使用标准 t 分布计算的,但是 pymer4 也为其他类型的推理提供了非参数排列测试和可靠的置信区间。在撰写本文时,拟合两阶段回归模型仅在 github 的开发分支中可用,但应该会在未来的新版本中包含。
注释和更正
- 在这篇文章的前一个版本中,这种方法被错误地称为两阶段最小二乘法(2SLS)。2SLS 是一种完全不同的技术的正式名称,属于工具变量估计的更广泛范围。这种混淆是因为上面讨论的两阶段回归方法在技术上确实采用了普通最小二乘估计的“两阶段”,然而这并不是文献中的 2SLS。感谢 Jonas Oblesser 指出这一点,并感谢 Stephen John Senn 使用适当的术语,这些术语实际上在医学和 fMRI 文献中是一致的。
- 虽然在本文中(通常在文献中),两阶段甚至多层次建模和聚类稳健推理被视为两种不同的可能的分析策略,但另一种可能性涉及结合这些方法。即,使用多级模型或两阶段回归来获得系数估计,然后在执行推断时计算最高级别系数的稳健标准误差。感谢詹姆斯·e·普斯特约夫斯基提出这个经常被忽视的选项。
Eshin Jolly 2019
原载于 2019 年 2 月 18 日【eshinjolly.com】。
不平衡数据集上不同分类机器学习模型的比较
如果一个数据集包含来自一个类的样本比来自其余类的样本多得多,则称之为不平衡数据集。当至少一个类仅由少量训练样本(称为少数类)表示,而其他类占大多数时,数据集是不平衡的。在这种情况下,由于较大的多数类的影响,分类器可以对多数类具有良好的准确性,但对少数类的准确性非常差。这种数据集的常见示例是信用卡欺诈检测,其中欺诈= 1 的数据点通常比欺诈= 0 的数据点少得多。
数据集可能不平衡的原因有很多:第一个目标类别在人群中可能非常罕见,或者数据可能很难收集。
让我们通过处理一个这样的数据集来解决不平衡数据集的问题。
关于数据:我这里是一家游戏公司的数据,他们想知道哪个客户会成为 VIP 客户。这里的目标变量有两个值:0(代表非 VIP 客户)和 1(代表 VIP 客户)。在可视化该目标列时,我们可以从下面的饼图中看到,只有 1.5%的数据是 VIP 客户的,其余 98.5%的数据是非 VIP 客户的。

Distribution of Majority and Minority Class

First few rows of the dataset
如果我们使用这些数据来训练我们的预测模型,那么该模型将表现不佳,因为该模型没有在代表 VIP 客户的足够数量的数据上进行训练。为了演示如果我们使用这些数据,预测模型的结果会如何,下面是一些算法的结果:

Results from the k-NN Algorithm

Results from the Gradient Boosting Algorithm

Results from the Logistic Regression Algorithm
从上面的结果可以清楚地看出,机器学习算法几乎无法预测 VIP 客户(因为 VIP 客户的 f1 分数非常低)。
在监督学习中,克服类别不平衡问题的一种常见策略是对原始训练数据集进行重新采样,以降低类别不平衡的整体水平。重采样是通过对少数(正)类进行过采样和/或对多数(负)类进行欠采样来完成的,直到这些类被近似相等地表示。
在使用不同的重采样技术之前,需要记住的最重要的一点是所有的重采样操作都必须只应用于训练数据集。如果在将数据集分割成训练集和验证集之前进行上采样,则可能在两个数据集中得到相同的观察结果。因此,当在验证集上进行预测时,机器学习模型将能够完美地预测这些观察值,从而提高准确性和召回率。
重采样技术的类型:
欠采样/下采样:该方法使用多数类的子集来训练分类器。由于许多多数类示例被忽略,训练集变得更加平衡,训练过程变得更快。最常见的预处理技术是随机多数欠采样(RUS),在 RUS,多数类的实例从数据集中随机丢弃。下面是实现缩减采样的 python 代码
from sklearn.utils import resampledf_majority_downsample = resample(df_majority,
replace=True,
n_samples=105,
random_state=123)
df_train = pd.concat([df_majority_downsample, df_minority_upsampled])
# Display new class counts
print (df_train.IsVIP_500.value_counts())**Output:** 0 105
1 105
Name: IsVIP_500, dtype: int64
过采样:它通过复制少数类实例来实现更均衡的类分布。过采样不会丢失任何信息,因为少数类和多数类的所有原始实例都保留在过采样数据集中。下面是实现上采样的 python 代码。
df_minority_upsampled = resample(df_minority,
replace=True,
n_samples=5000,
random_state=123)
df_train = pd.concat([df_majority, df_minority_upsampled])
# Display new class counts
print (df_train.IsVIP_500.value_counts())**Output:** 0 6895
1 5000
Name: IsVIP_500, dtype: int64
尽管这两种方法都解决了阶级不平衡的问题,但它们也有一些缺点。随机欠采样方法可能会删除某些重要的数据点,而随机过采样可能会导致过拟合。
SMOTE: 合成少数过采样技术被设计用来生成与少数类分布一致的新样本。主要思想是考虑样本之间存在的关系,并沿着连接一组邻居的线段创建新的合成点。下面是实现 SMOTE 的 python 代码。
**#SMOTE**
from imblearn.over_sampling import SMOTE
import numpy as np sm = SMOTE(random_state=12)
x_train_res, y_train_res = sm.fit_sample(X_train, Y_train)print (Y_train.value_counts() , np.bincount(y_train_res))**Output:
#previous distribution of majority and minority classes**0 6895
1 105#**After SMOTE, distirbution of majority and minority classes**
0 6895
1 6895
现在我们的数据已经准备好了,让我们在 SMOTE 创建的数据集上应用一些机器学习算法。我尝试了以下算法:Logistic 回归,K 近邻,梯度推进分类器,决策树,随机森林,神经网络。以下是最高性能的机器学习算法的结果和解释:
随机森林:基本思想与 bagging 非常相似,我们引导样本,因此我们对训练数据集进行重新采样。然后我们在每个引导样本上重建分类或回归树。一个区别是,在每次拆分时,当我们在分类树中每次拆分数据时,我们也引导变量。换句话说,在每次潜在拆分时,只考虑变量的子集。这有助于建立一系列不同的潜在树木。所以我们的想法是种植大量的树木。对于预测,我们要么投票,要么对这些树进行平均,以获得对新结果的预测。下面是实现随机森林分类器的 python 代码。
from sklearn.ensemble import RandomForestClassifiermodel = RandomForestClassifier(n_estimators=1000)
model.fit(x_train_res , y_train_res)
y_pred = model.predict(X_test)
target_names = ['NON-VIP', 'VIP']
print(classification_report(Y_test, y_pred,target_names=target_names))

The result of the Random Forest applied to dataset derived from SMOTE
梯度增强:与并行构建和组合随机不同树的森林的随机森林方法不同,梯度增强决策树的关键思想是它们构建一系列树。其中每棵树都被训练,以便它试图纠正系列中前一棵树的错误。以非随机的方式构建,创建一个模型,随着树的增加,错误越来越少。模型建立后,使用梯度增强树模型进行预测速度很快,并且不会占用太多内存。下面是实现梯度增强分类器的 python 代码。
model_GB = GradientBoostingClassifier(n_estimators=1000)
model_GB.fit(x_train_res , y_train_res)
y_pred = model_GB.predict(X_test)
target_names = ['NON-VIP', 'VIP']
print(classification_report(Y_test, y_pred,target_names=target_names))

The result of the Gradient Boosting applied to dataset derived from SMOTE
Ada Boost: 这是一种迭代集成方法。AdaBoost 分类器通过组合多个性能较差的分类器来构建一个强分类器,以获得高精度的强分类器。Adaboost 背后的基本概念是设置分类器的权重,并在每次迭代中训练数据样本,以确保对异常观察值的准确预测。下面是实现 Ada Boost 分类器的 python 代码。
from sklearn.ensemble import AdaBoostClassifiermodel_ad = AdaBoostClassifier()
model_ad.fit(x_train_res , y_train_res)
y_pred = model_GB.predict(X_test)
target_names = ['NON-VIP', 'VIP']
print(classification_report(Y_test, y_pred,target_names=target_names))

The result of the Ada Boosting applied to dataset derived from SMOTE
注意:预测准确性,一种评估分类器性能的流行选择,在数据不平衡时可能不合适。不应该使用它,因为它不能提供真实的情况。例如,模型的准确度可能是 97%,并且人们可能认为该模型表现得非常好,但是实际上,该模型可能仅预测多数类,并且如果该模型的主要目的是预测少数类,则该模型是无用的。因此,应该使用召回率、精确度和 f1 分数来衡量模型的性能。
结论:到目前为止,我们看到,通过对不平衡数据集进行重新采样,并选择正确的机器学习算法,我们可以提高少数类的预测性能。我们性能最好的模型是 Ada,梯度增强在使用 SMOTE 合成的新数据集上运行。使用这些模型,我们为少数民族类获得了 0.32 的 f1 分数,而使用原始数据以及逻辑和 k-nn 等算法,少数民族类的 f1 分数为 0.00
进一步改进:为进一步改进模型,可考虑以下选项:
- 尝试使用 SMOTE 的变体。
- 超参数(学习率、最大深度等)的调整。)以上型号。
- 此外,可以使用不同的机器学习模型,如堆叠或混合机器学习算法、深度学习模型。
谢谢,学习愉快:)
比较星期五第 13 次建议从惊讶和含蓄
帮助你计划下一个电影之夜的功能代码。
在最近的一篇博客文章中,我详细介绍了如何令人惊讶地进行单品推荐。我选择惊喜,部分是因为它是我训练营的一些导师强烈推荐的(没有双关语),但我完全意识到还有很多其他选择,其中一些我在我的上一篇推荐帖子中详细介绍过。我认为从隐式开始深入研究我已经跳过的其他一些库是值得的。
Implicit 是一个库,旨在为其他一些已建立的推荐库(如 Annoy 和 NMSLib)提供更快的 Python 实现,所以它似乎是一个不错的起点。我将仅仅触及这个库所提供的皮毛,所以请随意查看这里的文档和这里的 GitHub
现在评估 Surprise 和 Implicit 各自推荐的质量有点武断。没有一些用户的反馈,这完全取决于我的主观意见,哪一个做得更好。本着这种精神,我将把重点放在提取建议的代码上。
在我们推荐任何东西之前,我们首先要构建并运行我们的模型。我已经在之前的博客中详细介绍了如何构建和运行惊喜(这里是再次是),所以我将在这里运行隐式。
首先,我们必须导入数据。
import implicitdf = pd.read_csv("final_data.csv", index_col=0)
# This dataframe has the movie titles, which I'll need later.
与 Surprise 不同,Implicit 特别需要一个稀疏矩阵。令人惊讶的是,假设您只传递 userid、itemid 和 weight(或 rating)列,那么您可以将数据帧直接传递给模型。但是使用 Implicit,您需要首先转换您的数据。
df_sparse = pd.read_csv("item_item_final.csv", index_col=0)
sparse = scipy.sparse.csr_matrix(df_sparse.values)
当谈到模型时,Implicit 有很多选项,包括 onyy、NMSLib 和 Faiss 的版本,但出于说明的目的,我将坚持使用良好的 ole 交替最小二乘法。
als_model = implicit.als.AlternatingLeastSquares()
als_model.fit(sparse)
现在是有趣的部分——我们可以推荐一些电影。先说惊喜。我会选择单品推荐,这很简单,但会给你带来惊喜。
惊喜的好处在于,无论你的 item _ ids 是什么,都可以是字符串。在这种情况下,这非常有用,因为最终我想要吐出的是一个易于阅读的电影名称列表。Surprise 通过将“原始 id”(即电影名称)转换为“内部 id”(数字)来处理这些字符串。这使得你可以相对容易地将你的标题从模型中取出来。
# First, select the title you are recommending for:
hd_raw_id = 'Friday the 13th Part VII: The New Blood (1988)'#Transform that raw_id to an inner_id.
hd_inner_id = KNN_final_model.trainset.to_inner_iid(hd_raw_id)# Get nearest neighbors.
hd_neighbors = KNN_final_model.get_neighbors(hd_inner_id, k=5)# Convert inner ids of the neighbors back into movie names.
hd_raw = [KNN_final_model.trainset.to_raw_iid(hd_inner_id)
for hd_inner_id in hd_neighbors]
以下是我的建议:
['Friday the 13th Part VIII: Jason '
'Takes Manhattan (1989)',
'Leatherface: Texas Chainsaw Massacre '
'III (1990)',
'Amityville: A New Generation (1993)',
'Jaws 3-D (1983)',
'Texas Chainsaw Massacre: The Next '
'Generation (a.k.a. The Return of the '
'Texas Chainsaw Massacre) (1994)']
不算太差!我们有所有的恐怖电影,所有的续集,它包括另一个 13 号星期五,这是我的数据集的一部分。至少从表面上看,这是有道理的。
现在为含蓄。得到相似的物品,其实在隐性上是极其简单的。您只需调用“相似项”函数。代码如下:
sim_items = als_model.similar_items(70)
诀窍是返回一些可理解的东西,尤其是对最终用户。隐式返回 n 个最近邻居的行 id。当然,没有人愿意被推荐一些行 id。这意味着,对于实际应用,您将需要一个助手函数。这是我想到的:
def get_item_recs(movie):
"""Takes a movie row from the model and makes a five recommendations."""
id_list = []
sim_items = als_model.similar_items(movie,5)
for item in sim_items:
iid = df_sparse.iloc[item[0],[1]]
id_list.append(int(iid[0]))
final = []
for rec in id_list:
final.append(df.loc[df['movieId']== rec, 'title'].iloc[0])
return final
这是清单:
['Friday the 13th Part VIII: Jason Takes Manhattan (1989)',
'Iron Eagle IV (1995)',
'Lawnmower Man 2: Beyond Cyberspace (1996)',
'Troll 2 (1990)',
'Problem Child 2 (1991)']
这份清单比《惊奇》多了一点多样性,但仍然相当不错。同样,另一个 13 号星期五是它的一部分,它都是续集。榜单上还有铁鹰四和问题儿童 2,有意思。
显然,我没有真正调整 ALS 模型的参数,Implicit 还有许多我没有包括在内的特性和功能,但有趣的是拍摄的不同之处。
比较图形数据库 I
第 1 部分:TigerGraph,Neo4j,亚马逊 Neptune
Oooh. Aaah.
简介
关系数据库具有分类帐式的结构。可以通过 SQL 查询,而且是大多数人熟悉的。每个条目由表中的一行组成。表通过外键约束相关联,这是将一个表的信息与另一个表的信息连接起来的方式,就像主键一样。在查询关系数据库时,通常会涉及到缓慢的多层连接。
对于一个图形,特别是散点图,可以把元素想象成节点或点。线图的元素同样由顶点表示。每个节点都有键值对和一个标签。节点由关系或边连接。关系有类型和方向,它们可以有属性。图形数据库简单地由点和线组成。当意义在于数据之间的关系时,这种类型的数据库更简单、更强大。关系数据库可以轻松处理直接关系,但间接关系在关系数据库中更难处理。
当构建关系数据库时,它是带着问题构建的。我们想要回答什么样的问题?例如,您想知道有多少人购买了烤面包机,住在堪萨斯州,有犯罪记录,并使用优惠券购买了该烤面包机。如果数据库管理员或创建数据库的人没有预料到这样的问题,那么从关系数据库中检索信息可能会非常困难。对于图形数据库,回答意料之外的问题是可能的。有了图,你就可以回答任何问题,只要数据存在,数据之间有路径。图是用来遍历间接关系的。使用图形数据库,您甚至可以添加更多的关系,同时仍然保持性能。图形数据库不仅仅是存储数据点,而是存储数据关系。图形数据库存储关系信息。

https://www.nextplatform.com/2018/09/19/the-graph-database-poised-to-pounce-on-the-mainstream/
我将简要描述一些最流行的图数据库实现的优缺点:
- 老虎图
- Neo4j
- 亚马逊海王星
这是外面的一个例子。看看这里更多的选项:【https://db-engines.com/en/ranking/graph+dbms
TigerGraph —“一个完整的分布式并行图形计算平台,支持网络规模的实时数据分析”
通过其固有的并行图形技术,TigerGraph 成为世界上速度最快、扩展性最强的图形平台…
www.tigergraph.com](https://www.tigergraph.com/)
一个完整的分布式并行图形计算平台,支持网络规模的实时数据分析
2017 年发布,这是一个图形数据库的商业选项。它的实现语言是 C++,但也支持 Java。它的操作系统是 Linux,查询是通过一种类似 SQL 的查询语言 GSQL 进行的。
TigerGraph 的加载时间比主要竞争对手 Neo4j 要长。考虑到预处理时间,TigerGraph 其实比 Neo4j 要快。TigerGraph 之所以高效,是因为它需要的存储空间比 Neo4j 少 19.3 倍。TigerGraph 在一跳路径查询上比 Neo4j 快 24.8 倍。在三跳路径查询上,TigerGraph 比 Neo4j 快 1,808.43 倍。TigerGraph 还能够完成六跳路径查询。这种类型的处理会在 2 小时后杀死 Neo4j,因为它耗尽了内存。
TigerGraph 在体验上所欠缺的,似乎在资源上有所弥补。就性能而言,TigerGraph 似乎在竞争中胜出,但这是有代价的。TigerGraph 的成本不取决于性能或核心或插槽的数量,而是取决于放入图形数据库的数据大小。50-100 GBS 的图形年费为数万美元,1 TB 的图形年费接近 100 万澳元。
2019 年 9 月推出的 TigerGraph Cloud 上的免费层选项可用。还有一种按小时付费的模式,每小时几美元起。它根据性能定价,虚拟 CPU 和 RAM 越大的实例成本越高,客户可以启动和停止实例,只需按小时支付所需费用。下面是关于这些定价选项的详细信息的链接。
https://www.tigergraph.com/tigergraph-cloud-pricing/
[## 半 TB 基准测试 Neo4j 与 TigerGraph - DZone 数据库
图形数据库已经变得越来越流行,受到了越来越多的关注。为了了解图表如何…
dzone.com](https://dzone.com/articles/half-terabyte-benchmark-neo4j-vs-tigergraph)
neo4j——“开源图形数据库”

Neo4j 创建于 2007 年,被 db-engines.com 评为排名第一的图形数据库。Neo4j 是开源的,支持多种编程语言,包括:。Net、Clojure、Elixir、Go、Groovy、Haskell、Java、JavaScript、Perl、PHP、Python、Ruby、Scala。服务器操作系统有 Linux、OS X、Solaris 和 Windows。
Neo4j 拥有最受欢迎和最活跃的图形数据库社区。评论报告称,他们的产品简单易学,易于使用,有大量的资源,从培训材料到书籍。Neo4j 已经为他们的用户提供了大量的资源。Neo4j 提供了一种新的查询语言 Cypher,并正在将其引入 Spark。Neo4j 声称 Cypher 是一种与 Spark 上的图形数据进行交互的更方便的方式。Neo4j 带来了 ETL、分析和改进的性能。谈到 ETL,Neo4j 提供了一个新的工具,可以自省关系模式并自动提取 CSV。
为了跟上 TigerGraph,Neo4j 确实需要跨越一些性能障碍,但它仍然拥有所有图形数据库中最大的追随者和支持网络。
使用 Neo4j 构建的应用程序可应对互联数据挑战,包括人工智能、欺诈检测、实时推荐和主数据。

Solutions by Neo4j
Neo4j 还有一个即将到来的活动:Nodes 2019。去他们网站注册。还有一个黑客马拉松,你现在就可以参加!投稿截止时间为 2019 年 9 月 29 日。

Amazon Neptune —“为云构建的快速、可靠的图形数据库”
[## Amazon Neptune -为云构建的快速、可靠的图形数据库
为云构建的快速、可靠的图形数据库 Amazon Neptune 是一个快速、可靠、完全托管的图形数据库…
aws.amazon.com](https://aws.amazon.com/neptune/)
与 TigerGraph 类似,亚马逊 Neptune Graph 数据库于 2017 年首次亮相,并已商用。服务器操作系统托管,支持的编程语言有 C#、Go、Java、JavaScript、PHP、Python、Ruby、Scala——范围比 TigerGraph 更广。Neptune 正在努力实现高可用性和耐用性。“这相当于 1000 亿个节点/边/三元组,同时跨三个可用性区域(az)自动复制六份数据拷贝,并将数据持续备份到 S3。AWS 还表示,Neptune 旨在提供超过 99.99%的可用性,并在不到 30 秒的时间内自动检测和恢复大多数数据库故障。因此经久耐用!Neptune 还提供高级安全功能,包括通过亚马逊虚拟专用云(VPC)的网络安全和使用 AWS 密钥管理服务(KMS)的静态加密
海王星因其双重性而与众不同。它能够支持流行的图形数据库模型、RDF 和属性图 (PG)。这个优势是一把双刃剑,因为你不能互换使用。AWS 的目标是统一这两种模式,但这是一个相当大的挑战,因为差异不是微不足道的。
[## AWS Neptune going GA:图形数据库用户和供应商的好、坏、丑
这是官方的:AWS 有一个生产就绪的图形数据库。今天包括哪些功能,将来会包括哪些功能…
www.zdnet.com](https://www.zdnet.com/article/aws-neptune-going-ga-the-good-the-bad-and-the-ugly-for-graph-database-users-and-vendors/)
要查看这三个数据库系统的更详细的比较,以及更多信息,请访问 db-engines.com 查看这个伟大的资源:
[## 亚马逊 Neptune vs. Neo4j vs. TigerGraph 对比
亚马逊海王星和 Neo4j 和 TigerGraph 的详细并排视图
db-engines.com](https://db-engines.com/en/system/Amazon+Neptune%3BNeo4j%3BTigerGraph)
图形数据库的应用
- 欺诈检测
- 实时推荐引擎
- 主数据管理
- 支持网络和 IT 运营
- 身份和访问管理
进一步阅读
下面是一个链接,你可以在那里下载一本免费的书籍《T1》,即奥赖利的《T2》图表数据库。
[## O'Reilly 图形数据库- Neo4j 图形数据库平台
编辑描述
neo4j.com](https://neo4j.com/lp/book-graph-databases-rem/?utm_source=google&utm_medium=ppc&utm_campaign=NA%20-%20Display%20-%20Remarketing%20-%20O%27Reilly%20Book%20-%20Text&utm_adgroup=NA%20-%20Display%20-%20Remarketing%20-%20O%27Reilly%20Book%20-%20All%20Users&utm_placement=www.analyticsvidhya.com&gclid=Cj0KCQjw2efrBRD3ARIsAEnt0ejjkZFqyPis1FZYBcFhcri-pmqOhZtG0xSlF2vdwMJhIffenZMIYBIaAqf2EALw_wcB)
比较图形数据库 2
第 2 部分:ArangoDB、OrientDB 和 AnzoGraph DB
很高兴看到人们对他们最喜欢的图形数据库提供商,或者至少是他们工作的图形数据库公司充满热情。既然有这么多可用的选项,请查看 db-engine 为您的图形数据库需求列出的 33 个不同选项。在这篇文章中,我将简要地强调一下:
- OrientDB
- ArangoDB
- AnzoGraph 数据库
根据 db-engines.com 的提供的榜单,这三家分别排名第三、第四和第二十六。为了新闻报道的可信度,我将谦虚地尽可能做到不偏不倚,报道公司网站上提供的信息以及最近的文章。
请参见我以前的文章(第 1 部分)快速比较关系数据库管理系统和图形数据库。
OrientDB —“为现代世界设计的数据库”

文件:https://orientdb.org/docs/3.0.x/
OrientDB 最初于 2010 年发布,支持许多编程语言,包括:。Net,C,C#,C++,Clojure,Java,JavaScript,JavaScript(Node.js),PHP,Python,Ruby,Scala。OrientDB 是一个无模式的多模型数据库系统,支持图形、文档、键/值和对象模型。它支持无模式、全模式和混合模式。Gremlin 和 SQL 查询都支持图形遍历。OrientDB 是用 Java 实现的,所以可以在 Java JDK ≥ JDK6 的所有操作系统上运行。它是开源的,但是 OrientDB 也提供商业支持。
根据他们的网站, OrientDB 提供图形数据库系统的服务,而不需要“部署多个系统来处理其他数据类型。”这种方法有助于提高“性能和安全性,同时支持可扩展性。”OrientDB 通过在设计上管理多模型系统来区别于 多 图形数据库系统。它不是简单地“为额外的模型增加层,导致性能下降。”
G2 上有 33 条用户评论,平均评分 4/5 星。大多数评论都非常积极,应该注意的是,上一次平均评论是从 2016 年 6 月开始的,所以看起来 OrientDB 正在修复漏洞和部署完全开发的功能。对评论的主要批评似乎是对更强大的文档的渴望。主要的优点包括价格合理,安装快捷,以及对用户友好。

https://www.g2.com/products/orientdb/features

https://www.predictiveanalyticstoday.com/orientdb/
arango db——“一个引擎。一种查询语言。多个型号。”

文件:https://www.arangodb.com/documentation/
ArangoDB 最初在 2011 年被称为 AvocadoDB,从它的标志可以看出,它是在 2012 年诞生的。ArangoDB 是开源的、多模型的(键/值、文档和图形),用 C、C++和 JavaScript 实现。服务器操作系统包括:Linux、OS X、Raspbian、Solaris 和 Windows。它是无模式的,支持以下语言:C#、Clojure、Java、JavaScript (Node.js)、PHP、Python 和 Ruby。 ArangoDB 以一个数据库核心和自己的统一查询语言 AQL (ArangoDB Query Language)运行,在很多方面与 SQL 相似。AQL 是声明性的,允许在单个查询中组合不同的数据访问模式。ArangoDB 是专门设计来允许键/值、文档和图形数据存储在一起,并使用一种通用语言进行查询的。
根据他们的 网站ArangoDB 可以作为一个分布式&高度可扩展的数据库集群运行。它运行在 Kubernetes 上,包括持久原语&简单的集群设置。ArangoDB 本身集成了跨平台索引、文本搜索和信息检索排名引擎,并针对速度和内存进行了优化。还提供了完整的 GeoJSON 支持。
在 G2 上有 41 条用户评论,平均 5/5 星。2017 年的单一平均评级表明缺乏 SQL 支持以及对必须适应 AQL 的不满。其他人认为 AQL“直观”,并将 ArangoDB 描述为“功能丰富”

https://www.g2.com/products/arangodb/features

https://www.predictiveanalyticstoday.com/arangodb/
为了抢先一步了解即将到来的 3.6 版本中的新功能,请查看 2019 年 10 月 10 日美国东部时间下午 1 点 ArangoDB 的网络研讨会。
** [## 网上研讨会:ArangoDB 3.6 -未来充满特性- ArangoDB
America/los Angeles arang odb 网上研讨会:ArangoDB 3.6 -随着最近发布的
www.arangodb.com](https://www.arangodb.com/arangodb-events/arangodb-3-6-the-future-is-full-of-features/)
AnzoGraph —“在快速、可扩展的数据库上构建您的解决方案”

文档:https://docs . Cambridge semantics . com/anzograph/user doc/home . htm
这个商业图形数据库最初于 2018 年发布,使用 RDF(资源描述框架)进行操作。RDF 模型以主-谓-宾的形式将信息表示为三元组。RDF 存储可以被认为是图形 DBMS 的一个子类,但是 RDF 存储是有区别的,因为它们提供了超越一般图形 DBMs 的特定方法。大多数 RDF 商店,包括 AnzoGraph,都支持 SPARQL,这是一种类似 SQL 的查询语言,用于 OLAP(在线分析处理)风格的分析。AnzoGraph DB 的操作服务器是 Linux,它支持 C++和 Java。
根据他们的网站介绍, AnzoGraph DB 专为在线数据分析而打造,性能可线性扩展。这是一个大规模并行处理(MPP)原生图形数据库,专为大规模分析(万亿次以上)、速度和深度链接洞察而构建。它面向需要图形算法、图形视图、命名查询、聚合、内置数据科学功能、数据仓库式 BI 和报告功能的嵌入式分析。你可以尝试他们的真实世界测试,检查他们的基准研究,并下载一个 60 天免费试用。
一些关于 AnzoGraph DB 的评论就在媒体上。查看这篇文章:图形数据库。有什么大不了的?法维奥·巴斯克斯。他指出,“随着机器学习和人工智能的发展,图 OLAP 数据库变得非常重要,因为许多机器学习算法本身就是图算法,在图 OLAP 数据库上运行比在 RDBMS 上运行更有效。”
乔治·阿纳迪奥蒂斯写的文章中有另一个关于 AnzoGraph 的检查,这篇文章比较了 AnzoGraph 和 TigerGraph。
结论
有许多绘制数据库的选择,似乎每个人都在试图找到自己的市场角落。哪个最好?这真的取决于你的需求。每个 Graph DB 都有其独特的优点、缺点和基准。随着这些系统的发展和壮大,它们的弱点将会改变,它们可能会变得更加全面和强大。花时间货比三家,自学所有可用的选项,因为有很多,而且数量还在不断增加。**
比较机器学习模型:统计与实际意义
模型 A 还是模型 B 更准确?嗯…
在构建和调整 ML 模型方面已经做了很多工作,但是在所有这些艰苦的工作之后,一个自然的问题最终出现了——我们实际上如何比较我们已经构建的模型?如果我们面临模型 A 和 B 之间的选择,哪一个是赢家,为什么?是否可以将这些模型组合在一起,以实现最佳性能?
一种非常肤浅的方法是在测试集上比较整体准确性,比如说,模型 A 的准确性是 94%,模型 B 的准确性是 95%,然后盲目地断定 B 赢了这场比赛。事实上,除了调查整体准确性之外,还有更多的事实需要考虑。
在这篇博文中,我很乐意分享我最近在模型比较方面的发现。我喜欢用简单的语言来解释统计数据,所以这篇文章对于那些在统计方面不是很强,但想多学一点的人来说是一个很好的读物。
1.“理解”数据
如果可能的话,想出一些能马上告诉你实际发生了什么的情节确实是个好主意。在这一点上做任何绘图似乎都很奇怪,但是绘图可以给你提供一些数字所不能提供的洞察力。
在我的一个项目中,我的目标是在预测用户文档的税收时,比较相同测试集上 2 ML 模型的准确性,因此我认为按用户 id 聚合数据并计算每个模型正确预测税收的比例是一个好主意。
我拥有的数据集很大(超过 100K 个实例),所以我按地区分解分析,并专注于较小的数据子集——每个子集的准确性可能不同。在处理大得离谱的数据集时,这通常是一个好主意,因为一次消化大量数据是不可能的,更不用说得出可靠的结论了(稍后将详细介绍样本大小问题)。大数据集的一个巨大优势是,你不仅可以获得数量惊人的信息,还可以放大数据,探索特定像素子集上发生的事情。

subset 1: model A vs. model B scores (picture is my own)

subset 2: model A vs. model B scores (picture is my own)

subset 2: model A is clearly doing better than B… look at all those spikes! (picture is my own)

subset 3: model A vs. model B scores (picture is my own)
在这一点上,我怀疑其中一个模型在一些子集上做得更好,而他们在其他数据子集上做的几乎相同。这比仅仅比较整体准确度前进了一大步。但是这种怀疑可以通过假设检验进一步调查。假设检验可以比人眼更好地发现差异-我们在测试集中的数据量有限,我们可能会想,如果我们在不同的测试集上比较模型,准确性会有什么变化。遗憾的是,并不总是有可能得出不同的测试集,因此了解一些统计数据可能有助于研究模型准确性的本质。
2.假设检验:让我们把它做好!
乍一看,这似乎是微不足道的,您可能以前见过这种情况:
- 建立 H0 和 H1
- 想出一个测试统计,并假设正态分布
- 以某种方式计算 p 值
- 如果 p < alpha = 0.05 reject H0, and ta-dam you’re all done!
In practice, hypothesis testing is a little more complicated and sensitive. Sadly, people use it without much caution and misinterpret the results. Let’s do it together step by step!
步骤 1。我们设置 H0:零假设** =无两个模型之间有统计学意义的差异:备选假设=** 两个模型之间有有统计学意义的差异——由你决定:模型 A!= B(双尾)或 A 型<或>B 型(单尾)****
第二步。我们提出了一个检验统计量,以便在观察到的数据中量化将无效假设与替代假设区分开来的行为。有许多选择,甚至最好的统计学家也可能对 X 数量的统计测试一无所知——这完全没问题!有太多的假设和事实需要考虑,所以一旦你知道你的数据,你就可以选择正确的。关键是要理解假设检验是如何工作的,实际的检验统计只是一个工具,很容易用软件来计算。****
请注意,在应用任何统计测试之前,需要满足许多假设。对于每个测试,您可以查找所需的假设;然而,现实生活中的绝大多数数据并不完全符合所有的条件,所以可以放松一点。但是如果你的数据,比如严重偏离正态分布呢?
有两大类统计测试:参数和非参数测试,我强烈推荐在这里阅读更多关于它们的内容。我长话短说:两者的主要区别在于,参数检验需要对总体分布进行某些假设,而非参数检验则更稳健一些(请不要参数!)。
在我的分析中,我最初想使用成对样本 t 检验,但是我的数据显然不是正态分布的,所以我选择了 Wilcoxon 符号秩检验(成对样本 t 检验的非参数等价检验)。由你来决定在你的分析中使用哪种检验统计,但是总是确保假设得到满足。

My data wasn’t normally distributed 😦 (picture is my own)
第三步。现在是 p 值。p 值的概念有点抽象,我打赌你们很多人以前都用过 p 值,但是让我们澄清一下 p 值实际上是什么:p 值只是一个衡量对 H0 不利的证据的数字:对 H0 不利的证据越强,p 值就越小。如果你的 p 值足够小,你就有足够的信用拒绝 H0。
幸运的是,在 R/Python 中可以很容易地找到 p 值,所以您不需要折磨自己并手动完成它,尽管我一直主要使用 Python,但我更喜欢在 R 中进行假设检验,因为有更多的选项可用。下面是一段代码。我们看到,在子集 2 上,我们确实获得了一个小的 p 值,但置信区间是无用的。
**> wilcox.test(data1, data2, conf.int = TRUE, alternative="greater", paired=TRUE, conf.level = .95, exact = FALSE)V = 1061.5, p-value = 0.008576
alternative hypothesis: true location shift is less than 0
95 percent confidence interval:
-Inf -0.008297017
sample estimates:
(pseudo)median
-0.02717335**
第四步。非常简单:如果 p-value <预先指定了 alpha(传统上为 0.05),您可以拒绝 H0 而支持 H1。否则,没有足够的证据来否定 H0,这并不意味着 H0 是真实的!事实上,它可能仍然是错误的,但根据数据,没有足够的证据来否定它。如果 alpha 为 0.05 = 5%,这意味着只有 5%的风险得出存在差异的结论,而实际上并不存在差异(又名类型 1 错误)。你可能会问自己:那么为什么我们不能追求 alpha = 1%而不是 5%?这是因为分析将更加保守,所以拒绝 H0 将更加困难(我们的目标是拒绝它)。****
最常用的阿尔法是 5%,10%和 1%,但你可以选择任何你喜欢的阿尔法!这真的取决于你愿意冒多大的风险。
alpha 可以是 0%吗(即没有类型 1 错误的可能性)?不:)在现实中,你总有可能犯错误,所以选择 0%是没有意义的。给错误留些空间总是好的。
如果你想玩玩 p-hack(p-hack)游戏,你可以增加你的 alpha,拒绝 H0,但是你必须满足于较低的信心水平(随着 alpha 的增加,信心水平下降——你不能拥有一切:)。
3.事后分析:统计与实际意义
如果你得到一个小得可笑的 p 值,那当然意味着这两个模型的准确性之间有一个统计上显著的差异。之前,我确实得到了一个小的 p 值,所以从数学上来说,模型肯定不同,但是“显著”并不意味着重要。这种差异真的有什么意义吗?这种微小差异与业务问题相关吗?**
统计显著性是指样本中观察到的均值差异由于抽样误差而发生的可能性。给定一个足够大的样本,尽管看似无关紧要的人口差异,人们可能仍然会发现统计意义。另一方面,实际意义查看差异是否足够大,在实际意义上是否有价值。虽然统计意义是严格定义的,但实际意义更加直观和主观。****
此时,你可能已经意识到 p 值并没有你想象的那么超级强大。还有更多要调查。考虑到效果大小也是很棒的。效应大小衡量差异的大小——如果存在统计上的显著差异,我们可能会对其大小感兴趣。效应大小强调差异的大小,而不是将其与样本大小混淆。**
**> abs(qnorm(p-value))/sqrt(n)0.14# the effect size is small**
什么是小、中、大效果尺寸?传统的临界值分别是 0.1、0.3、0.5,但同样,这真的取决于你的业务问题。
样本量有什么问题?如果你的样本太小,那么你的结果就不可靠,但这无关紧要。如果你的样本量太大怎么办?这看起来很棒——但是在这种情况下,即使是非常小的差异也可以通过假设检验检测出来。有如此多的数据,即使是微小的偏差也可能被认为是重大的。这就是效果大小变得有用的原因。
还有更多的事情要做——我们可以尝试找到测试的功效和最佳样本量。但我们现在没事了。
假设检验如果做得好,在模型比较中会非常有用。建立 H0 & H1 公司、计算检验统计量和寻找 p 值是日常工作,但解释结果需要一些直觉、创造力和对业务问题的深入理解。请记住,如果测试是基于一个非常大的测试集,被发现具有统计显著性的关系可能没有太多实际意义。不要盲目相信那些神奇的 p 值:放大数据并进行事后分析总是一个好主意!😃
请随时通过电子邮件或 LinkedIn 联系我,我随时准备好与您聊聊数据科学!
用数据可视化比较漫威和 DC
为漫画世界的比较设计可视化

Marvel & DC Visualization — Storm’s partners
比较漫画宇宙有很多种方法。在这里,我解释了我是如何使用来自维基百科的数据创建了一个比较漫威大学和 DC 大学的可视化系统。
我的目标是定义一个简单的可视化,以清晰快捷的方式展示漫威和 DC 之间的差异。我为每个宇宙创造了一个隐喻,并在一个视图中将它们并排显示为星系。
你可以找到我是如何从维基百科提取数据的(简单!)以及之前在我的漫画数据系列其他帖子中的讨论:
你可以在这里找到最终的可视化效果:
可视化设计
条形图和散点图等可视化技术非常常见,但有局限性。对于具有多种信息数据集,对不同可视化隐喻进行分组的个性化可视化是必要的。
当我设计漫画数据的可视化时,首先想到的是用太阳和行星来比较两个星系。
事实上,维基百科已经用虚构的宇宙这个术语来代表漫画。这里我把事情简化了,用了星系的概念。
除了漫画和星系之间的联系意义,人们习惯了一个星系图的结构。一个太阳在中心;一些代表轨道的椭圆;代表行星、恒星和其他天体的小圆圈和大圆圈。
我将我的问题重新定义为比较漫威和 DC 的星系,探索星系图可能存在的视觉差异。
下面,我展示了一些我的草稿,关于星系的结构和意义,它们的位置和形状,以及其他与编码相关的项目——反应组件,类属性,方法等等。


Designing the visualization metaphors and layout
每个虚构的宇宙都是一个有太阳的星系。太阳代表其品牌(漫威或 DC),身体代表虚构的人物。太阳的能量很大,不是吗?有道理。
这组隐喻代表了人物的数量和重要性、他们的超能力和性别。我还介绍了一些关系,揭示了他们的伴侣和子女。

Explaining the meaning of sizes, colors, distance from the sun (orbit) and connections.
这些是指导可视化设计的属性:
- 超能力:超能力列表
- 重要性:角色出现在作品中的数量
- 性别
- 名称
超级大国
人物离太阳越近,拥有的超能力就越多。
每个轨道(椭圆)代表一系列超级大国。第一个代表超级大国,离太阳最远;第二个代表1–2超级大国,以此类推。
蝙蝠侠因为没有超能力,所以远离太阳。
邪恶先生离太阳很近,因为他有 7 项超能力。

Mister Sinister is close to the sun because he has 7 superpowers.
用轨道比喻,我们可以看到漫威和 DC 在使用众多超能力方面的比较。
漫威似乎比 DC 更限制超能力的使用。


Marvel & DC comparison — DC has more characters with 7 superpowers or more than Marvel.
在这个比较中,注意 DC 在整个星系中有更多的角色。这可能导致太阳周围更高的密度。
字符重要性
圆圈越大,它们所在作品的数量或衍生作品的数量就越大(它将它们相加)。两者之和就是我定义的重要性。
如果一个角色的重要性等于零,那么他的身体就用一个紫色的灰尘— 圆圈表示,这个圆圈具有最小的有效半径。当它们的重要性等于或大于 1 时,它们的大小与重要性成正比。
金刚狼是漫威星系中最大的身体,因为他出现在 53 部作品中(包括衍生作品)。

Wolverine has importance 53.
用大小来表示重要性,会弹出探索最多的字符。通过这样做,考虑到大型天体和尘埃的数量,很容易检查星系之间的比较。
漫威只关注金刚狼,而 DC 关注超人和蝙蝠侠。
还是关于重要性,我认为颜色也与它有关。当一个角色的作品比平均值多的时候,我用黄色给它上色;否则为灰色。紫色代表 0 作品(尘埃)。
性别
我将性别分为两大类:男性和非男性,非男性代表女性、跨性别者和其他人,如 agender Phoenix Force。
除去性别不明确的角色,在 10 多部作品中,80%的漫威角色和 72%的 DC 角色都是男性。
当我们考虑出现在更少作品中的人物时,这些数字变得更加平衡:当考虑在维基百科上注册了至少一部作品的人物时,67%(漫威)和 67%(DC);当考虑具有 0 个或更多注册作品的角色时,这两个数字分别为 61%(漫威)和 64% (DC)。
考虑到代表性和多样性这些天被更多的讨论,因为大多数非男性在星系中是小天体,我用白色突出显示了非男性。

Mystique is highlighted in white because she is in the non-male category.
然而,我们几乎看不到白色的圆圈。这反映了漫画史上正在形成的性别差异。
一个虚构的宇宙会有这种差距有很多原因;其中包括漫画受众——以男性读者为重点的漫画可能会有更多的男性角色。
Jacques Nyemb 写了一篇关于这个话题的有趣的文章,以他作为漫画创作者的经验,关于观众和他的角色的种族和性别:种族,性别和漫画…哦,我的天!
“用他们的视角讲故事,包容各种人。这启发人们看到的不仅仅是肤色或性别,而是人物的内涵。”雅克·奈姆布
伴侣和孩子
从维基数据中提取了两种类型的关系,分别用红色的边、伙伴和蓝色的孩子来表示。伴侣可以是配偶,也可以是非配偶。
我找不到任何特别的区别,比如一个网络比另一个网络有更多密集的集线器。你可以在下面的图片中看到:

Partners-edges

Children-edges
我没有表示边的方向(子-边)。它们被设计成当用户选择一个要分析的字符时突出显示,如下例所示。
在下图中,我显示了点击金刚狼和风暴的结果。这个网络让我们知道金刚狼有很多伴侣和 2 个孩子(在维基百科上注册),而暴风有 3 个伴侣和 0 个孩子。

Storm and Wolverine’s connections
在 DC 星系中也发现了类似的模式。在下图中,我们看到神奇女侠有 3 个伴侣,没有孩子;超人有 3 个孩子,7 个伴侣。

Wonder Woman and Superman’s connections
条形图
为了展示超能力和职业的一般用法,我创建了条形图,就像我在这里实现一样。
当您将鼠标放在一个栏上时,您可以看到这两个属性的分布,并可视化角色的名称和其他细节。
因为我想给出关于这些属性的附加信息,所以我添加了第三个信息来定义比较性别的条的颜色。
我使用每个职业/超级大国的非男性和男性的数量来强调男性比其他性别少的情况。常规条形图为灰色;突出显示的是橙色。
以下是漫威和 DC 最常用的超能力:

Superpowers Bar Chart
这些是漫威和 DC 最常用的职业:

Occupations Bar Chart
履行
我使用 Javascript、React 和 D3 实现了所有的可视化。
完整的代码在我的 GitHub 库中:https://github.com/heberleh/comics-universe
我在 2019 年 6 月从维基数据中提取了数据。
我做的完整漫画系列解释了从提取数据到可视化的所有步骤。你可以在这篇文章的开头找到这三篇文章。
放置/投影算法使用椭圆方程为半径为a和b的每个轨道的每个节点计算x,y。当它是灰尘(work=0)时,算法使用随机增量来计算不同的x, y。这使得尘埃在轨道上围绕着它的原点扩散,用椭圆方程计算,创造了一个有趣的效果。
在幕后



https://heberleh.github.io/comics-universe/漫威和 DC 可视化—
如果你是一个漫画专家,并且发现了数据中的任何空白,考虑通过在他们的系统中插入新的信息来为维基百科和维基数据做贡献。
请留下您对任何主题的评论或私人笔记。
谢谢你看我的文章!
比较 Python 虚拟环境工具

我很喜欢我们在 Medium 上的社区。我最近发表了一篇关于为 Python 项目使用虚拟环境的文章。这篇文章很受欢迎,读者的反馈为我打开了一个新的视角。我以前不知道pew、venv和pipenv。他们的推荐帮助我了解了该领域的最新技术,并进一步提高了我的知识和经验。
在他们的建议之后,我阅读了所有的建议。在这篇文章中,我将分享我学到的新的虚拟环境工具,以及它们之间的对比。
Python 虚拟环境的各种工具

Photo by Plush Design Studio on Unsplash
我浏览了各种文档和文章,了解了它们如何带来不同的东西。我的发现描述如下:
1.虚拟
我在之前的文章中已经讨论过virtualenv了。你可以在这里查看。该指南可在这里获得。
优点: 通过pip很容易升级,可以轻松使用多个版本的 Python。它还支持 Python 2.7+。
缺点: 这里 Python 解释器二进制实际上是复制到一个新的位置,需要从那里读取。此外,如果您想使用它,您必须单独安装它,因为它不是 Python 附带的。
边注:如果打算用
*virtualenv*,rafabluszcz 扎瓦日基 推荐使用virtualenvwrapper。一定要去看看。
2.Python 环境包装器(pew)
pew作为一个包装器,一旦你开始使用它,你只需要记住这个命令,因为它可以用于我们需要的所有命令。完整的文档可以在这里找到。
优点:
它让虚拟环境的工作变得非常容易。使用一个简单的命令,您就可以创建一个新的环境,安装一个软件包列表并激活该环境。
缺点: 我注意到的主要问题是现在它的支持非常有限。代码上最后一次提交是 2018 年 3 月,差不多一年了。
3.venv
venv也可以用来设置和访问虚拟环境。从 Python 3.5 开始,它成为创建虚拟环境的推荐方法。你可以在这里找到它的文档。
优点: 它生成的配置文件是 Python 二进制直接理解的,不需要把二进制复制到新的位置。此外,Python 开发人员也支持它。
缺点: 不是针对 3.3 之前的 Python 版本。
4.pipenv
该工具将 Python 和虚拟环境的使用提升到了一个新的水平,因为它将包和环境管理支持结合到了一个工具中。这里的文件是这里的。
优点: 我们可以简单的指定我们工作的环境。此外,它允许我们为我们的环境创建单独的部分,如生产和测试开发。
缺点: 必须通过pip单独下载。它增加了很多功能,但是如果他们觉得他们也需要额外的功能,应该采用它。
根据我的经验,所有不同的工具都能解决这个问题。它们中的每一个都可以帮助你轻松地创建一个虚拟环境,没有太多的麻烦。你可以简单地选择其中任何一个并学习它的工作原理。
我认为pipenv是处理虚拟环境的最佳工具之一,并且由于其增加的功能,它的势头越来越大。因此,我将讨论让您开始使用pipenv的基础知识。
pipenv

Photo by Farhan Azam on Unsplash
要使用brew安装pipenv,使用以下命令。
brew install pipenv
创造环境
一旦pipenv安装完毕,我们就可以开始处理我们的环境了。转到您想要创建环境的任何文件夹。我来创造pipenvProject里面的环境。只要进入它,安装你喜欢的任何软件包。让我们安装requests。这将自动设置环境,创建Pipfile和Pipfile.lock。
cd pipenvProject
pipenv install requests

pipenv install requests
将在目录中创建两个文件。Pipfile其中包括包列表、Python 版本等信息。锁文件也被生成为Pipfile.lock。

Pipfile

Pipfile.lock
要激活这个环境,我们可以简单的说pipenv shell。我们现在在虚拟环境中。我们现在可以使用python命令进入 Python,并使用import requests检查我们是否有这个包。由于我们没有得到任何错误,这意味着软件包已经安装。

Inside environment
我们可以使用exit退出环境。当我们退出环境时,我们将不再能够访问requests包。

Outside environment
我们也可以直接在环境中运行命令,而不需要显式调用shell。为此,我们可以使用run命令。因此,如果您必须运行 Python,请使用以下命令:
pipenv run python
与非 pipenv 用户的兼容性
每当我们在 Python 中安装一组包时,我们通常在一个名为 requirements.txt 的文件中指定列表。
pip install -r requirements.txt
因此,当您开始使用pipenv时,有一种方法可以为该文件创建文本。简单地说,使用命令:
pipenv lock -r

Requirements file text
它将显示其他人可以使用的我们的requirements.txt文件的文本。
删除环境
使用pipenv —-rm 命令很容易删除一个环境。要再次创建它,使用命令pipenv shell。
但是,目录中的文件仍将保持原样。如果您希望完全删除它,请删除该目录。
cd ..
rm -r pipenvProject
结论
在本文中,我讨论了 Python 虚拟环境的新的高级工具,包括pew、venv和pipenv。我也列举了他们的优缺点。
奖励:
Error
如果您在创建虚拟环境时遇到了上述错误,那么有一个简单的解决方法。在主目录中用touch命令创建一个文件.bash_profile。然后,在编辑器中打开它(我用的是vim)。
touch ~/.bash_profile
vim .bash_profile
添加以下两行,保存并重启您的终端。问题会得到解决。
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
请随意分享你的想法和想法。我很乐意收到你的来信。
图像特征提取算法的比较
从颜色直方图到 VP 树
有许多算法专门用于图像的特征提取。它们中的许多工作原理类似于肺活量描记器或 Roomba。小机器人在房间里走来走去,撞到墙上,直到它有希望覆盖整个地板上的每一个斑点。类似地,一种算法将在一幅图像中穿行,从该图像中提取有趣的信息。这个过程称为特征检测。
球
特征检测的一个很好的例子可以用 ORB(面向快速和旋转简短)算法来看。它实际上是快速和简洁的结合。

ORB feature detection (Original photo provided by Limbik, features identified by me)
这些标记表示该图像的重要特征。这种算法甚至可以匹配已被扭曲(变灰、旋转和收缩)的同一图像的那些特征。

Matching features
ORB 本质上是找到图像的“角”。快速组件将特征识别为具有鲜明亮度对比的图像区域。如果超过 8 个周围像素比给定像素更亮或更暗,则该点被标记为特征。既然已经检测到了我们的特征,就要表达出来。BRIEF 通过将提取的点转换为二进制特征向量来实现这一点。这些是 128–526 个 0 和 1 的字符串。ORB 很有用。但是,在将 ORB 应用于完整的图像数据库,然后将这些特征存储到 CSV 中,然后使用 CSV 与给定的查询图像进行比较,以找到最相似的图像时,我遇到了麻烦。这个方法对任何 CBIR 来说都很棒,但是我很难正确实现。如果大家有什么指点,欢迎在下面随意评论!😃
文档:https://docs . opencv . org/3.0-beta/doc/py _ tutorials/py _ feature 2d/py _ orb/py _ orb . html
当心!页面底部的代码实际上并不好。请务必使用:
orb = cv2.ORB**_create**()
它可能需要一些巧妙的调试才能正常工作。
颜色梯度直方图
该方法简单地测量图像的红色、绿色和蓝色值的比例,并找到具有相似颜色比例的图像。颜色梯度直方图可以主要通过宁滨值来调整。

Color Gradient Histogram
Adrian Rosebrock 有一个实现这种比较图像方法的很好的教程:https://www . pyimagesearch . com/2014/01/22/clever-girl-a-guide-to-utilizing-color-histograms-for-computer-vision-and-image-search-engines/
这个方法很好,但不是很详细。如果你用蓝天查询和成像,它可以返回海洋图像,或者一个水池的图像。你可以把它想象成谷歌图片搜索中的颜色功能。

Google Image Search
有利位置树
如果您试图在数据库中查找重复的图像,VP-Tree 非常有用。该算法能够找到与查询图像相同的图像,或者近似相同的图像。同样,Adrian Rosebrock 在这方面有一个很好的教程:https://www . pyimagesearch . com/2019/08/26/building-an-image-hashing-search-engine-with-VP-trees-and-opencv/
这种方法主要分析图像的内容,并将所有信息压缩成 32 位整数。理论上,具有相似构图的图像将被相似地排序,并且基于构图将是相邻的。这被称为散列,下面是一个例子。

Images assigned a number
正如你所看到的,这两幅向日葵的图像有着相同的 8 位数。白色的文字是造成差异的原因,但它们最有可能是邻居。因此,如果两个图像都在您的数据集中,则一个查询会导致另一个。这种算法非常适合返回相同或接近相同的图像。它不考虑图像中被旋转或模糊的对象。
卡兹
这个算法很有趣,因为它似乎不是一个首字母缩写词。KAZE 指的是日语中的“风”风通过“非线性力”流动,因此,该算法由图像域中的非线性扩散过程组成。
https://www . doc . IC . AC . uk/~ ajd/Publications/alcantilla _ et al _ eccv 2012 . pdf
在我看来,这种类比有点牵强。看看下面的视频,感受一下 KAZE 使用的功能。
最后一个视频展示了 KAZE 模型的稳健性。KAZE 是在不同图像中识别同一物体的一个很好的模型。如果你有一个图像数据库,比如酒瓶,这将是一个很好的标签检测模型,并根据酒的标签找到匹配。
这是对许多不同形式的图像特征提取的快速概述。有许多选项可用,每一个都有不同的优势,可用于不同的目的。
如果你试图寻找重复的图像,使用 VP-trees。KAZE 和 ORB 非常擅长在不同的图像中检测相似的对象。颜色直方图非常适合制作由成千上万张图片组成的图片,或者至少可以找到具有相似颜色组成的图片。

Here’s content-based image retrieval!
使用这些算法中的一些,或者使用不同的算法,创建你自己的基于内容的图像检索系统!有这么多可以选择。我很想听听你的想法。
比较变压器标记器
比较最先进的变形金刚的分词器词汇(伯特,GPT-2,罗伯塔,XLM)
如果有人使用 Word2vec 或 GloVe 之类的单词嵌入,那么适应 BERT 之类的新的上下文嵌入可能会很困难。在这个故事中,我们将研究其中的一个区别:子词标记。这个故事的灵感来自于探索多语言 BERT 词汇的一个类似帖子。
对于这个实验,我们将使用 Huggingface transformer 库[1]。他们实施了多种最先进的架构。这个实验的代码可以在 Google Colab 上获得。

子词标记
子单词标记(或单词片段)可用于将单词分割成多个片段,从而减少涵盖每个单词的词汇量[2]。单词片段背后的思想和书面语言一样古老。字符是最知名的词块,英文单词可以用 26 个字符书写。
然而,寻找单词块的正确大小还没有被规范化。字符可以用 26 个左右的键来表示每个单词,而原始单词嵌入对每个单词使用不同的键(3M 键用于这个 word2vec 字典)【3】。小说《变形金刚》中的记号赋予者使用的词汇量介于两者之间。
Vocabulary size of the Transformers used in this experiment
伯特
BERT [4]使用单词片段[2]标记,其中非单词起始片段以##开始。不同的 BERT 模型有不同的词汇表。例如,无外壳的基本模型有 994 个令牌保留用于可能的微调([unused0] 到[unused993])。cased 模型只有 101 个未使用的标记,因为它需要更多的标记来覆盖大写字母和小写字母。多语言模型只有 100 个未使用的标记,然而,它的总词汇量是无案例的四倍。多语言模型存储更多的特殊字符,以涵盖 104 种语言的单词。
单词标记化用bert-base-cased :
[‘token’, ‘##ization’]标记化
罗伯塔 GPT2
Huggingface 的 GPT2 [5]和 RoBERTa [6]实现使用相同的词汇表,包含 50000 个单词。他们使用 BPE ( 字节对编码【7】)字段和\u0120作为特殊的信令字符,然而,Huggingface 实现对用户隐藏了它。
BPE 是一种基于频率的字符拼接算法:它以两字节字符作为标记开始,并基于 n 元语法标记对的频率,包括额外的更长的标记。例如,如果字母e和r在语言中频繁出现,一个新的标记er将被添加到词汇表中。接下来,如果h和er经常在一起,her被添加到词汇表中。该算法继续下去,直到它达到所需的大小。
单词标记化用模型gpt :
[‘token’, ‘ization’]标记化
XLM
与之前的词汇相比,XLM [8]在词尾使用了一个后缀:</w>表示这是一个单词的结尾。XLM 使用多种语言和模型共享的基于 BPE 的词汇表。
用模型xlm-mlm-en-2048 :
[‘to’, ‘ken’, ‘ization</w>’]将单词标记化
词汇相似度
下面的实验通过计算不同词汇的交集来度量它们之间的相似性。唯一的预处理步骤是消除上一节提到的特殊字符。第 I 行第 j 列单元格的计算公式:size(intersection(vocab(i),vocab(j)))/size(vocab(j))。
为了正确分析该表,应该注意不同的模型具有不同的词汇量。bert-base-cased (28996wps)和bert-base-multilingual-cased (119547wps)的交集只能覆盖多语言词汇的四分之一,即使这两个词汇完全匹配。
Word2Vec [3]和 GloVe [9]是使用单词作为关键字的静态单词嵌入。对于其他信息,该表包括这些嵌入的 30000 个最常见的单词以及总词汇表。我们对 Word2Vec 使用 3M GoogleNews 模型,对手套使用维基百科 2014 + Gigaword 5 模型。

Comparison of the tokens in the different vocabularies
从这个表中,我们可以看到在 Word2Vec 模型中,bert-base-cased中 87%的所有标记都被表示为单词。此外,bert-base-uncased和xlm-mlm-en-2048与其他相比彼此相对相似,因为它们具有超过 70%的相似性。
标记化差异
不同的单词片段带来不同的标记化。以下是一些不同单词分词的例子:
The different tokenization of the words “Marion”, “baptist” and “nuggets”
下图显示了 Word2vec 最常见单词的无大小写修改中同等标记化单词的增加。正如我们所看到的,在前 1000 个单词中,有 763 个单词以同样的方式被标记,然而,这个数字在前 4000 个单词中只增加到 1986 个,如果我们看前 10000 个最常见的单词,则增加到 3055 个。

Words tokenized the same way in every model
摘要
在这个故事中,我们看到不同的 Transformer 模型使用不同的标记器和不同的子词标记。正因为如此,在令牌级对模型进行比较是困难的。全球标准化子词模型的可能性是一个公开的问题。即使对于英语来说,标记化的词汇也有很大差异。
参考
[1]t . Wolf,处女作,l .,Sanh,v .,Chaumond,j .,Delangue,c .,Moi,a .,… & Brew,J. (2019)。拥抱脸的变形金刚:最先进的自然语言处理。 ArXiv,abs 。 arXiv:1910.03771
[2]吴,m .舒斯特,陈,z .乐,Q. V .,m .马切里,w .,… &克林纳,J. (2016)。谷歌的神经机器翻译系统:弥合人类和机器翻译之间的鸿沟。T11【arXiv 预印本 arXiv:1609.08144 。
[3]t .米科洛夫,陈,k .,科拉多,g .,&迪安,J. (2013 年)。向量空间中单词表示的有效估计。arXiv 预印本 arXiv:1301.3781 。
[4] Devlin,j .,Chang,M. W .,Lee,k .,& Toutanova,K. (2018 年)。 Bert:用于语言理解的深度双向转换器的预训练。arXiv 预印本 arXiv:1810.04805 。
[5]a .、吴 j .、蔡尔德 r .、栾 d .、阿莫代伊 d .、&苏茨基弗 I. (2019)。语言模型是无人监督的多任务学习器。 OpenAI 博客, 1 (8)。
[6]刘,y .,奥特,m .,戈亚尔,n .,杜,j .,乔希,m .,陈,d .,… &斯托扬诺夫,V. (2019)。Roberta: 一种稳健优化的 bert 预训练方法。 arXiv 预印本 arXiv:1907.11692 。
[7]森里奇(r .)、哈多(b .)、伯奇(a .)(2015 年)。带子词单元的生僻字神经机器翻译。 arXiv 预印本 arXiv:1508.07909 。
[8]康奈尔大学和兰普尔大学(2019 年)。跨语言语言模型预训练。神经信息处理系统的进展(第 7057–7067 页)。
[9]j .潘宁顿、r .索彻和 c .曼宁(2014 年 10 月)。 Glove:单词表示的全局向量。见2014 年自然语言处理经验方法会议论文集(第 1532-1543 页)。
比较单词嵌入
我们应用单词嵌入,因为它们已经被证明可以提高 NLP / ML / AI 任务的结果质量。一个有点天真的想法是,它们通过拓宽一个词、一个句子或一个文档与一个巨大的文本语料库中的平均上下文所铺设的狭窄路径来工作。通常,使用 Word2Vec 或 GloVe 的各种实现来生成单词嵌入。对于那些想要深入了解的人来说,这里有太多的artIcles描述了它们内部运作和底层算法背后的数学原理。
下面让我们仔细看看 Word2Vec 中实现的通用单词包(CBOW)和跳过语法(SG)模型。我们已经知道在训练 CBOW 时,我们试图从上下文预测单词,而在训练 SG 时,我们试图预测上下文单词。因此,CBOW 在处理大量训练数据和更频繁的单词时表现更好,而 SG 在处理少量训练数据时也表现良好,并且更好地表示不太频繁的单词。此外,SG 比 CBOW 花费更多的时间来训练。
“因此,假设我们使用相同的设置在相同的语料库上训练 CBOW 和 SG,当我们查看单词嵌入级别而不是应用程序中的性能时,我们会期望什么样的比较结果?”
"也许 CBOW 会返回更频繁的单词,而 SG 会返回不太频繁的单词."
"最终的单词嵌入会有多大的不同呢?"
“不知道,让我们开始一个实验,并从中学习一些东西。”
—我内心的对话
所以我去从一个镜像获得了 2019 年 4 月的德语维基百科的转储,并应用了轻量级预处理(即,没有词汇化或其他繁重的 NLP 技术)来获得 2 GB 的纯文本,其中一些短语粘在一起。然后,我使用 gemsim 计算 CBOW 和 SG 模型,这两个模型有 324 个维度,窗口大小为 10,最小频率为 10,每个模型有 1.33M 个向量。
一种非常简单(但相当耗时)的比较方法是遍历其中一个模型的词汇表,并计算前 10 个相似单词中有多少匹配。结果以 1k 个词汇单词的步长绘制在下面,并且示出了两个发现:1)模型中的单词向量具有顺序,并且该顺序对一致率有影响。2)一般来说,最相似的单词在很大程度上取决于所选择的模型。

Walking through the agreements in vector order
语料库中的词频根据 Zipf 定律(幂定律)分布,使得非常少的非常常见的词构成了大量的原始词计数。然后是相当数量的中等频率的单词,最后是长尾巴的不常用单词。看上面的图片,单词向量顺序似乎主要是基于频率的。当逐字查看词汇并与源文本进行比较时,会发现它实际上是按单词在源语料库中的出现顺序排列的。因为单词的出现分布在整个语料库中,所以我们期望平均来说更频繁出现的单词比不太频繁出现的单词更早出现。我们还可以得出结论,源词的词频与符合率相关。
所以,让我们继续把词汇分成数量可观的几类,每一类都在一定的频率范围内。考虑到幂定律,可以通过将语料库中最常见的词的频率(这里为:“der”)与每个词的频率进行比较,取比率的对数(这里为:以 2 为底)并对结果进行舍入来构建频率类。在分布的顶部,数据非常稀疏,因此所有 200 个最频繁出现的单词被分配到类别“6”。下图中产生的 16 个频率类别在大小上相差甚远,但与最常用的单词相比,该单词在一个类别中的频率相差不到 2 的 1 次方。

number of words per frequency class
现在我们可以按频率等级重复最初的比较。此外,由于最初的计算耗时太长(2 天!),我们可以从每个类别(1k)中为输入单词抽取足够大的样本。下面的结果表明,词频和模型一致性之间的相关性假设总体上是正确的。在分布的顶部(类别 6-8 ),有一些单词具有较小的一致性,并且不适合整体趋势。这些是我们可以放心地称之为停用词的词。在分布的中间(9-15 级),有一个区域的协议一般是公平的,频率影响相当低。接近分布的尾部(16-21 级),符合率随着频率影响的增加而下降。

Agreement counts by relative word frequency
现在我们已经了解了 CBOW 和 SG 之间的协议以及词频的影响。接下来,我们可以通过与输入单词的频率类别进行比较来研究他们提出的最相似单词的频率类别的个体模型以及协议模型。我们再次使用采样,因为完整的计算需要很长时间。

CBOW: frequency class of result in dependency of frequency class of input
CBOW(上图)认为相似的词往往比 SG(下图)更频繁。在分布的顶部,对于 CBOW,建议的非常频繁的词的数量非常高,而对于 SG,建议的非常频繁的词的数量相当低。在分布的中间部分,CBOW 建议的单词比 SG 建议的单词更频繁一些。在分布的末尾,这种效应继续存在,但是 CBOW 也开始产生更多非常不常用的单词。当使用单词嵌入时,消除不太频繁的相似单词可能会产生有趣的效果。

SG: frequency class of result in dependency of frequency class of input
最后,我们再次达成了下面描述的协议模型。对于中低频率的单词,乍一看,它似乎结合了 CBOW 和 SG 模型的优点。然而,热情是有限的,因为我们还需要考虑根本没有达成一致的大量情况。

CBOW vs. SG: frequency class of result in dependency of frequency class of input
更新:我需要第二次尝试从同一个语料库中创建一个手套模型,因为我没有考虑手套的临时空间需求(大约是原始语料库的 9 倍)。

GloVe: frequency class of result in dependency of frequency class of input
总体观察是 GloVe,它比其他模型提出了更多不常见的相似词,这在尾部变得相当压倒性。

GloVe vs. CBOW: frequency class of result in dependency of frequency class of input
当比较手套和 CBOW 时,符合率略高于手套和 SG 之间的符合率(32.1%对 28.6%),而 CBOW 和 SG 之间的符合率最高(40%)。通过与 CBOW 的比较,我们仍然得到稍微更频繁的单词,但是在分布的中间(也是最重要的部分),差异不是非常显著。

GloVe vs. SG: frequency class of result in dependency of frequency class of input
最后还有:
- CBOW、SG、GloVe 的 1000 字/频类样本对比用数字和图表的 Google sheet:https://docs . Google . com/spreadsheets/d/1 ewmy _ npb 0-ui 5 czktw 7 bv 7 ICR 7c p 9 tjgmcwywaxwmky/edit?usp =共享
- 一个 jupyter 笔记本:https://github . com/al Hague/word _ embedding _ comparison/blob/master/Model % 20 comparison . ipynb
希望以上发现能有用。合乎逻辑的下一步将是把它们应用到与单词嵌入相关的任务中……在后续的故事中。
深度神经网络激活函数的比较
阶跃函数、线性函数、Sigmoid 函数、双曲正切函数、Softmax 函数、ReLU 函数、Leaky ReLU 函数和 Swish 函数都有手把手的讲解!
🔥 激活函数在神经网络中起着关键作用,因此了解其优缺点以获得更好的性能至关重要。
有必要从引入非线性激活函数开始,它是最著名的s 形函数的替代函数。重要的是要记住,在评估激活功能的最终性能时,许多不同的条件都很重要。在这一点上,有必要提请注意数学和导数过程的重要性。因此,如果你准备好了,让我们卷起袖子,动手干一场吧!🙌🏻

什么是人工神经网络?让我们先记住这个:基于通过生物的神经系统对学习结构进行建模/模仿的人工学习技术。该神经结构通过分级电流感测过程来实现。从感受器获取的电脉冲使我们能够学习、记忆和记住我们自出生以来所看到、听到、感受到和想到的一切。神经科学是一个非常深入和有趣的研究领域。
为什么我们需要激活功能?

CS231n: Convolutional Neural Networks for Visual Recognition
我们需要激活函数将非线性真实世界的属性引入人工神经网络。基本上,在一个简单的神经网络中,x 被定义为输入,w 是权重,我们传递 f (x)是传递给网络输出的值。这将是另一层的最终输出或输入。
为什么不激活就不能把这个信号切换到输出?
如果不应用激活函数,输出信号变成简单的线性函数。线性函数只是单级多项式。未激活的神经网络将充当学习能力有限的线性回归。但是我们也希望我们的神经网络学习非线性状态。因为我们会给你图像、视频、文本、声音等复杂的现实世界信息,让你学习到我们的神经网络。多层深度神经网络可以从数据中学习有意义的特征。
那么为什么非线性函数需要?
多阶函数称为非线性函数。人工神经网络被设计为通用函数 近似器并用于此目标。这意味着他们必须具备计算和学习任何函数的能力。由于非线性激活函数,可以实现更强的网络学习。这篇文章已经和这个问题完全相关了😇
为了计算与权重相关的误差值,应用人工神经网络的反向传播算法。有必要确定优化策略并最小化错误率。选择合适的优化算法也是一个单独的问题。
激活功能
阶跃函数

Step Function and Derivative
它是一个接受二进制值的函数,用作二进制分类器。因此,在输出层中通常是优选的。不建议在隐藏层使用,因为它不代表衍生学习值,未来也不会出现。然后,我们来想一个导函数,线性函数马上就想到了。
线性函数

Linear Function and Derivative
它生成一系列激活值,这些值不是二进制值,就像阶跃函数一样。它当然允许你将几个神经元(神经细胞)连接在一起。但是这个功能有一个重大问题!修正了导数。为什么我们需要它的导数,它被固定的负面影响是什么?我们所说的;通过反向传播算法,我们完成了神经元的学习过程。这个算法由一个导数系统组成。当 A = c.x 由 x 导出,我们就到了 c,这就意味着与 x 没有关系,好吧,如果导数始终是一个常数值,我们能说学习过程正在进行吗?可惜没有!
还有一个问题!当在所有图层中使用线性函数时,在输入图层和输出图层之间会达到相同的线性结果。线性函数的线性组合是另一个线性函数。这意味着我们最开始所说的神经元可以干扰互连层!🙄
Sigmoid 函数
假设自然界中的大多数问题都不是线性的,sigmoid 函数的组合也不是线性的。答对了。

Sigmoid Function and Derivative
然后我们可以对这些层进行分类😃所以让我们考虑非二元函数。它也是可导的,因为它不同于阶跃函数。这意味着学习是可以发生的。如果我们检查图形 x 在-2 和+2 之间,y 值变化很快。x 中的小变化在 y 中会很大。这意味着它可以用作一个好的分类器。此函数的另一个优点是,当遇到线性函数中的(- infinite,+ infinite)时,它会产生(0,1)范围内的值。所以激活值没有消失,这是个好消息!🎈
sigmoid 函数是最常用的激活函数,但还有许多其他更有效的替代函数。
那么乙状结肠功能有什么问题呢?
如果我们仔细观察函数两端的图形,y 值对 x 的变化反应很小,让我们想想这是什么问题!🤔这些区域的导数值非常小,收敛到 0。这被称为消失梯度并且学习是最小的。如果为 0,则没有任何学习!当缓慢学习发生时,使误差最小化的优化算法可以附加到局部最小值,并且不能从人工神经网络模型获得最大性能。所以让我们继续寻找另一种激活功能!🔎
双曲正切函数

Hyperbolic Tangent and Derivative
它的结构非常类似于 Sigmoid 函数。但是,这一次函数被定义为(-1,+ 1)。相对于 sigmoid 函数的优势在于它的导数更陡,这意味着它可以获得更多的值。这意味着它的效率会更高,因为它的学习和评分范围更广。但是,函数两端的梯度问题仍然存在。虽然我们有一个非常普通的激活功能,但我们将继续寻找最好的一个!
ReLU(校正线性单位)功能

ReLU Function and Derivative
乍一看,它似乎与正轴上的线性函数具有相同的特征。但最重要的是,ReLU 本质上不是线性的。事实上,一个好的评估者。也有可能通过 ReLU 的组合与任何其他函数收敛。太好了!这意味着我们仍然可以(再次)在我们的人工神经网络中对层进行排序😄
ReLU 估值在[0,+G0],但是回报是什么,有什么好处?让我们想象一个拥有太多神经元的大型神经网络。Sigmoid 和双曲线正切导致几乎所有的神经元都以相同的方式被激活。这意味着激活非常密集。网络中的一些神经元是活跃的,并且激活是不频繁的,所以我们想要有效的计算负载。我们用 ReLU 得到它。负轴上的值为 0 意味着网络将运行得更快。计算负荷小于 sigmoid 和双曲线正切函数的事实导致了多层网络的更高优先性。超级!😎但是即使是 ReLU 也不是很好,为什么?因为这个零值区域给了我们这个过程的速度!所以学习不是发生在那个区域。😕那你就需要找一个新的激活功能,有窍门的。
漏流函数
💧你能看到负平面上的漏洞吗?😲

Leaky ReLU Function and Derivative
这个泄漏值被给定为 0.01。如果给定一个接近零的不同值,函数的名称随机地改变为 Leaky ReLU。(没有,没有新功能?!😱)泄漏 ReLU 的定义范围仍然是负无穷大。这是接近于 0,但 0 与非生活梯度的价值在 RELU 生活在负区域的学习提供的价值。多聪明啊。🤓
Softmax 函数
它的结构非常类似于 Sigmoid 函数。与同一个 Sigmoid 一样,它在用作分类器时表现相当好。最重要的区别是在深度学习模型的输出层优先,尤其是需要分类两个以上的时候。 I t 允许通过产生 0-1 范围内的值来确定输入属于特定类的概率。所以它执行概率解释。
Swish(自门控)功能

Swish Function and Derivative
与 ReLU 最重要的区别是在负区域。ReLU 里 Leaky 的值一样,有什么区别吗?其他所有激活功能都很单调。请注意,即使输入增加,swish 函数的输出也可能下降。这是一个有趣的 swish 特有的特性。
f(x)=2xsigmoid(betax)
如果我们认为 beta=0 是 Swish 的简单版本,是一个可学习的参数,那么 sigmoid 部分总是 1/2,f (x)是线性的。另一方面,如果β是一个非常大的值,sigmoid 就变成了一个接近两位数的函数(0 代表 x < 0,1 代表 x > 0)。因此 f (x)收敛于 ReLU 函数。因此,选择标准的 Swish 函数为β= 1。通过这种方式,提供了软插值(将变量值集与给定范围和期望精度内的函数相关联)。太棒了。已经找到了解决梯度消失问题的方法。

Mathematical Expressions of Activation Functions
点击此处 查看梯度和偏导数可视化!
应该优先选择哪种激活功能?
当然,我不会说你会用它或者那个。因为我已经列出了每个激活功能的独特优缺点。 sigmoid 函数可以用如果你说双曲正切或者模型因为激活函数范围广可以学的稍微慢一点。但是如果你的网络太深,计算量是主要问题,可以首选 ReLU 。你可以决定使用 Leaky ReLU 作为 ReLU 中渐变消失问题的解决方案。但是你做的计算比 ReLU 多。
所以激活函数是一个关键的优化问题,你需要根据所有这些信息和你的深度学习模型的要求来决定。
- 网络的简单和快速收敛可以是第一标准。
- ReLU 在速度方面会有优势。你必须让渐变消失。它通常用于中间层,而不是输出。
- 漏 ReLU 可以是梯度消失问题的第一个解决方案。
- 对于深度学习模型,用 ReLU 开始实验是可取的。
- Softmax 通常用于输出图层。
你可以找到无数的文章来评价他们的比较。我最好的建议是把手弄脏!所以,测试你自己,如果你准备好了…
激活函数的定义和绘制
首先,让我们看看激活函数的识别和绘制:

Demonstration of Activation Functions
激活功能的性能评估
让我们来看看卷积神经网络模型在经典 MNIST 数据集上的激活函数的比较,我们称之为最先进的

Summary of Deep Learning Model Used
当具有两个卷积层的模型应用于 Sigmoid、双曲线正切、ReLU、Leaky ReLU 和 Swish 函数时,您可以观察到一些函数是如何领先于其他函数的,以及一些函数是如何接近的。您可以测试不同的数据集。此外,其他的,epoch,batch-size,dropout 等参数的影响也可以考察。也许我下一篇文章的主题可以是其中之一!
给出了 20 个历元的样本验证、训练精度和损失值的结果。测试结果也如下表所示。


Comparison of Validation and Training for Different Activation Functions (TRAINING)


Comparison of Validation and Training for Different Activation Functions (LOSS)


Comparison of Activation Functions for Deep Neural Networks by Merve Ayyüce Kızrak is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
🌈你可以在谷歌实验室找到代码👇🏻
colab.research.google.com](https://colab.research.google.com/drive/1fCVQ8JFiuyVWs6BoCIw0UqWPZPshU25h)
🌈激活函数如何帮助创建非线性决策极限?您可以在此处获得其他申请!
所以,欢迎随时关注我关于 中推特 ,以及LinkedIn👽****
如果你想用土耳其语阅读这篇文章,点击这里!
❓ 如果有任何意见或问题,请不要犹豫让我知道。 ❗️
🎉我之前的一些博文!干杯!
heartbeat.fritz.ai](https://heartbeat.fritz.ai/capsule-networks-a-new-and-attractive-ai-architecture-bd1198cc8ad4) [## 什么是可解释的人工智能,是否需要?
可解释的人工智能-XAI 是一个近年来经常被争论的话题,也是一个…
interestingengineering.com](https://interestingengineering.com/what-is-explainable-artificial-intelligence-and-is-it-needed) [## 人工智能与律师:人工智能和法律的未来
“机器会思考吗?”让我们扩展一下艾伦·图灵在 50 年代提出的这个问题。无数的灾难场景,在…
interestingengineering.com](https://interestingengineering.com/ai-vs-lawyers-the-future-of-artificial-intelligence-and-law)
参考
- 深度学习的基础——激活函数以及何时使用它们?
- 理解神经网络中的激活功能
- 深度神经网络的非线性激活函数在 MNIST 分类任务上的比较
- SWISH:自门控激活功能
- 开创性的激活功能
- soft max 函数及其导数
- 在 MNIST 数据集上使用 SWISH 激活函数的实验**
数据分析工具的比较:Excel、R、Python 和 BI 工具
数据分析的时代已经到来。从国家、政府、企业到个人,大数据和数据分析已经成为大家耳熟能详的趋势。但你可能没有数据分析和编程的专业知识,或者你学了很多数据分析的理论,但还是实践不出来。这里我就比较一下最受数据分析师欢迎的四个工具, Excel , R , Python ,BI,作为入门数据分析的基础。

1.擅长
1.1 使用场景
- 一般办公室要求下的数据处理工作。
- 中小型公司的数据管理和存储。
- 对学生或教师进行简单的统计分析(如方差分析、回归分析等。).
- 结合 Word 和 PowerPoint 创建数据分析报告。
- 数据分析师的辅助工具。
- 为一些商业杂志和报纸制作图表(数据可视化)。
1.2 优势
- Excel 很容易上手。
- 学习资源非常丰富。
- 你可以用 Excel 做很多事情:建模、可视化、报告、动态图表等。
- 可以帮助你在进一步学习其他工具(比如 Python 和 R)之前,理解很多操作的意义。
1.3 缺点
- 要完全掌握 Excel,需要学习 VBA,所以难度还是很高的。
- 数据量大的时候会出现卡顿的情况。
- 在没有其他工具帮助的情况下,Excel 数据文件本身只能容纳 108 万行,并且不适合处理大规模数据集。
- 内置的统计分析过于简单,实用价值不大。
- 与 Python、R 和其他开源软件不同,正版 Excel 是收费的。
2.稀有
2.1 使用场景
R 的功能几乎涵盖了任何需要数据的领域。就我们一般的数据分析或者学术数据分析工作来说,R 能做的事情主要包括以下几个方面。
- 数据清理和数据简化。
- 网页抓取。
- 数据可视化。
- 统计假设检验(t 检验、方差分析、卡方检验等。).
- 统计建模(线性回归、逻辑回归、树模型、神经网络等。).
- 数据分析报告输出(R 降价)。
2.2 R 好学吗?
在我看来,入门 R 非常简单。10 天的集中学习,对于掌握基本的使用,基本的数据结构,数据的导入导出,简单的数据可视化已经足够了。有了这些基础,当你遇到实际问题时,你就可以找到你需要使用的 R 包。通过阅读 R 的帮助文件和网络上的信息,您可以相对快速地解决特定的问题。
3.计算机编程语言
3.1 使用场景
- 数据抓取。
- 数据清理。
- 数据建模。
- 基于业务场景和实际问题构建数据分析算法。
- 数据可视化。
- 数据挖掘和分析的高级领域,如机器学习和文本挖掘。
3.2 R 与 Python
R 和 Python 都是需要编程的数据分析工具。不同的是,R 专门用于数据分析领域,而科学计算和数据分析只是 Python 的一个应用分支。Python 还可以用来开发网页,开发游戏,开发系统后端,做一些运维工作。
目前的一个趋势是,Python 在数据分析领域正在赶超 R。在某些方面已经超越了 R,比如机器学习和文本挖掘。但 R 在统计学领域依然保持优势。Python 在数据分析方面的发展,在很多地方对 R 的一些特性进行了建模。所以,如果你还是新手,还没有开始学习,我建议你从 Python 开始。
Python 和 R 都很好学。但是如果你同时学习两者,会非常混乱,因为它们在许多地方非常相似。所以建议不要同时学它们。等到你掌握了其中一个,再开始学习另一个。
3.3 选择 R 还是 Python?
如果因为时间有限只能选择其中一个学习,我推荐用 Python 。但是我还是建议你两个都看一看。你可能在某些地方听说 Python 在工作中更常用,但解决问题才是最重要的。如果你能用 R 有效地解决问题,那么就用 R。事实上,Python 模仿了 R 的许多特性,比如 Pandas 库中的 DataFrames。正在开发的可视化软件包 ggplot 模仿了 r 中非常著名的 ggplot2。
4.双性恋的
数据分析有句话:文字不如表格,表格不如图表。数据可视化是数据分析的主要方向之一。Excel 的图表可以满足基本的图形要求,但这只是基础。高级可视化需要编程。除了学习 R、Python 等编程语言,还可以选择简单易用的 BI 工具。关于 BI 的介绍,可以看我的另一篇文章,开始数据分析师的职业生涯,应该学习哪些数据分析工具?
商业智能是为数据分析而生的,它的诞生起点非常高。目标是缩短从业务数据到业务决策的时间。这是关于如何使用数据来影响决策。
BI 的优势在于它更擅长交互和报告。它擅长解释历史和实时数据。可以极大的解放数据分析师的工作,促进整个公司的数据意识,提高导入数据的效率。市场上有很多 BI 产品。他们的原则是通过维度的链接和钻取来构建仪表板,从而获得可视化分析。相信大家对 SAP BO 、甲骨文、 Power BI 、 Tableau 这样的 BI 工具已经非常熟悉了。但如果你是数据分析初学者,我建议你学习使用 FineReport 。这是一个零编码工具。无需任何学习编程语言的基础,你就可以轻松掌握这个软件。传统开源软件功能有限,无法满足个性化需求。没有服务保障。而且他们学习成本高,需要长期学习。像 FineReport 这样的工具提供了解决所有这些困难的解决方案。
它有三个主要功能。
数据集成
FineReport 提供多数据源关联、跨数据库和跨数据表访问以及多业务系统数据的简单应用。它还将相关的业务数据整合到一个报表中,使更多的数据应用于运营分析和业务控制。

数据收集和建模分析
报告设计器允许用户以灵活简单的方式设计所需的报告。数据决策系统实现了报表的统一访问和管理,实现了各种业务的数据分析和填报。
数据显示
可以通过 PC 或移动终端访问数据。为了提高报表数据的可读性和挖掘数据价值, FineReport 提供了多种图表分析、钻取分析、多维分析、自定义分析和实时分析等功能。

Personalized charts of FineReport

Real-time dashboard of FineReport

Dynamic map of FineReport
还有一点
正如乔布斯的演讲,惊喜彩蛋放在最后!
个人版 FineReport 完全免费。可以去官网下载试试。
原载于 2019 年 5 月 28 日http://www.finereport.com。
图像艺术风格嵌入的降维技术比较
这项任务是作为一个高清历史图片在线图书馆的概念验证的一部分。

在之前的故事中,我开发了一个模型,将图像的艺术风格编码成高维空间中的向量,其中向量之间的欧几里德距离表示图像在视觉上的相似程度。
对于使用这些嵌入的图像推荐系统,数据集中每个图像的向量需要存储在磁盘上,并且需要在某个点计算两个或多个向量之间的距离。每个向量中存在的维度越多,系统的存储和计算需求就越大,从而增加了成本和搜索时间。
由于这个原因,减少嵌入向量中的维数是有益的,同时最小化在这样做时丢失的信息。为了实现这一点,测试了许多技术,下面更详细地探讨了所使用的方法。然而,对于这些嵌入,更传统的方法优于基于神经网络的方法。由于这个原因,PCA 维数减少被向前使用,PCA 在被应用于剩余部分之前在嵌入的样本上被训练,以便减少训练 PCA 所花费的时间。
降维效率
从信息的角度来看,有效的维度缩减依赖于至少一些存在的维度是冗余的,这意味着它们可以被其他存在的维度的函数所替代。
例如,考虑一个 3D 向量 V = [X,Y,Z]。如果 Z 可以表示为 Y,X 的精确函数,那么它可以从向量中移除,而不会从系统中移除任何信息。即使 Z 是 X,Y 的不精确函数,例如 Z = X + Y + e,其中 e 是一个不可观测的变量,我们仍然可以删除 Z,代价是删除 e 提供的信息。随着 e 的方差增加,丢失的信息也会增加。总的来说,通过降维丢失的信息可以通过降低 V 的维数,然后恢复去除的维数,并比较原始向量和变换向量的方差来找到。丢失的方差是通过维度减少丢失的信息的度量。
我正在处理的嵌入向量具有使它们成为降维的良好候选的特征,包括两个部分:
- 纹理之间的(展平的)协方差矩阵:该矩阵提供了两个纹理的每个组合之间的协方差。考虑三个纹理 I、j、k。如果纹理 I 和 j 相关,并且 j 和 k 相关,则我们期望 I 和 k 相关。这意味着 I 和 k 之间的协方差的维数包含一些冗余信息。便利矩阵包含数千个维度,其中许多是冗余的,可以在不牺牲大量信息的情况下删除。
- 由每种颜色组成的图像所占比例的向量,每个维度代表一种颜色。一些颜色可能永远不会被使用,因此可以被移除,而其他颜色可能显示出足够高的相关性,因此是多余的。
主成分分析
PCA 是一种线性降维技术,利用变量之间的线性关系来表达它们在更少的维度中所包含的信息。如下例所示,两个维度被压缩为一个。

这种技术非常有效,将维度减少了 94%,同时保留了原始数据的 99.3%的方差。
神经网络自动编码器
还测试了各种基于神经网络的自动编码器。这些将嵌入向量作为输入和目标,并使用连续的层来将嵌入的大小减少到一个小的“编码”状态,然后以类似沙漏的方式恢复到其原始尺寸,如下所示。

与 PCA 非常相似,这种设置可以最小化输入向量和解码向量之间的误差,同时创建一个维数更少的中间编码向量。与 PCA 不同,该技术通过多层引入非线性,允许在较低维度表示中捕获嵌入向量的元素之间更复杂的关系。理想情况下,这将导致较少量的信息丢失,然而,这种技术导致比 PCA 更多的信息丢失和更长的计算时间。
通过对不同网络体系结构的进一步实验,可能会有更好的信息丢失,但 PCA 提供的准确性和快速计算时间使其成为测试技术中的明显选择。
弥补 NLP 的理解不足
没有 AGI 的更好的人工智能应用

Photo by Patrick Tomasso on Unsplash
“一幅画胜过千言万语”这句话对语言媒介有点不公平。这表明语言是一种低效的交流方式,而事实恰恰相反。当人类使用语言交流时,因为说话者和听者分享同一个世界的经验,所以有很多东西被遗漏了,这使得在日常生活中没有必要对这个共享的世界进行明确的陈述。例如,如果我对你说“花瓶是侧着的,沿着桌子滚动”,我不需要告诉你花瓶是由易碎的材料制成的(这是一个合理的假设),或者桌子没有阻止花瓶滚动的边缘,或者因此花瓶可能会从桌子上滚下来,或者重力会使花瓶掉到地板上,地板很硬,因此会导致易碎的花瓶破碎。我说“花瓶侧着,沿着桌子滚动”就足以让你知道,除非有人干预,否则花瓶很可能会摔得粉碎。

Our shared world is left out of our utterances
我们在谈话中忽略的是我们可以合理假设我们的听众也拥有的所有知识。毫不夸张地说,整个世界都被忽略了,这就是自然语言处理(NLP)如此困难的原因,NLP 是一个让机器理解语言的领域。虽然一幅画可能胜过千言万语,但它们大多是没人愿意说出口的话。想象一张照片,一只狗在公园里追一个球。如果你在看这样一张图片,被要求描述你所看到的,你会提到草是绿的吗?这种信息在图像识别任务中是免费的,但在用于 NLP 任务的数据中却完全没有。
在我看来,这些事实使 NLP 成为人工智能中最有趣的领域。目前,机器对世界一无所知,因此也没有真正理解语言的能力。因此,尽管缺乏理解,最近 NLP 的所有令人难以置信的进步,如机器翻译能力的巨大飞跃,都发生了。这提出了两个有趣的问题:
- 我们会让机器拥有足够的世界知识,像人类一样理解语言吗?
- 在缺乏理解的情况下,NLP 的局限性是什么?
我们无法知道第一个问题的答案,尽管这是一个值得探索的有趣问题。向计算机灌输世界知识,即常识,是否等于创造所谓的人工一般智能(AGI)?或者有什么方法可以提供这样的知识,不像 AGI 那样,但足以完成自然语言理解的任务(NLU)?或许知识图谱是关键?或者完全是别的什么原因。DARPA 最近推出了机器常识计划来解决这个问题。这也是艾伦人工智能研究所的主要关注点。虽然我同意这种研究对于掌握问题的范围以及如何衡量解决问题的进展非常重要,但我必须承认我对实际或有用的结果持怀疑态度。作为一个在工业界工作并希望使用 NLP 解决当前业务问题的人,我不禁发现第二个问题更有说服力。在缺乏真正理解的情况下,我们需要能够认识到 NLP 的局限性,因为这就是今天的实际情况。
纵观当前的 NLP 应用,机器翻译(MT)是被广泛使用的一种。谷歌翻译现在每天翻译超过 1000 亿个单词,所以它显然提供了有用的翻译。但它不会很快完全取代人类翻译(事实上,美国劳工统计局预测,从现在到 2026 年,笔译员和口译员的就业增长将远远高于平均水平)。在正式翻译中,目标语言文本的质量必须与输入的质量一样高,例如法律文件或文学作品,仍然需要人工翻译。MT 系统通常会犯某些类型的错误,如果没有 NLU,就无法可靠地防范这些错误。一个例子是代词(“他”、“她”、“它”、“他的”、“她的”、“他们”、“他们的”等)。认知科学家道格拉斯·霍夫斯塔德去年写道他称谷歌翻译“肤浅”,指出它无法“理解”它所翻译的内容。霍夫施塔特的许多例子都涉及代词的误译。这些情况对机器翻译来说如此困难的原因是,要正确翻译一个代词,你必须知道它指的是什么。我们人类利用我们对世界的了解如此轻易地完成了这项任务,以至于我们几乎没有意识到自己在做这件事。但这恰恰是机器所缺乏的技能。
性别代词使这个问题特别清楚。在一些语言中,根本没有性别代词;在其他语言中,如法语,所有格代词的性别与名词一致,而不是像英语中那样与所有者一致:法语“sa maison”在英语中的正确翻译可能是“他的房子”或“她的房子”,这取决于我们谈论的是谁的房子。虽然一些简单的启发式方法在许多情况下可以有所帮助(例如,假设代词指的是最后提到的名词),但几十年的人工智能研究已经揭示了基于规则的方法对自然语言的普遍不足。此外,性别代词可能是机器翻译应用中偏见最明显的地方。你可能已经看过土耳其语句子翻译成英语的例子,根据名词的不同,一个无性别的代词变成了有性别的:“他”代表医生,“她”代表护士,等等。即使给出了大量关于正确的性别代词应该是什么的提示,谷歌翻译今天仍然默认有偏见的猜测:

The fact that we’re talking about John’s sister is a big hint that “she” is the correct pronoun here, not “he”, unless we’re so biased that we assume all doctors must be men.
被视为人工智能问题,这个代词问题很难解决。到目前为止,进展主要是以建立方法来测量 it 进展的形式出现的(例如,参见 GLUE 基准和 Winograd 模式挑战)。但是看做一个软件应用问题,代词问题其实很好解决。在这种情况下,讨论中的软件应用程序是一个翻译应用程序,解决方案只是在 UI 中为用户提供一个翻译选择:“他是医生”或“她是医生”。对于人类用户来说,答案是显而易见的。那么为什么不让用户来决定呢?碰巧的是,这正是谷歌最近在他们的帖子中宣布的改变,减少谷歌翻译中的性别偏见。但是从上面的截图来看,很明显这个解决方案只实现了一部分。如果你只是输入一个像土耳其语“o bir doktor”这样的句子,你会得到两个特定性别的选项,但似乎如果你提供任何上下文,系统仍然相信自己,可以说,从上下文中猜出正确的代词。但是,由于上述原因,这恰恰是不能相信它会做对的事情。
这个例子中值得注意的一点是,当没有给定上下文时,多个答案都是正确的,如果系统进行猜测并选择其中一个,它可能只是显示其偏见,但它不会是错误的。但是当提供上下文时,实际上只有一个正确的答案,选择另一个答案不仅会(在某些情况下)显示出偏见,而且显然是错误的。因此,在我看来,对系统来说,猜测何时提供上下文实际上比猜测更重要,因为在这种情况下,可能会出现错误的翻译。然而,谷歌只选择在一个问题得到解决的地方实施修复,即延续性别陈规定型观念的问题。但是它没有解决误译的问题。
当然,对于较长的文本,提供单独的翻译变得不太可行,但在这些情况下,有问题的代词至少可以以某种方式标记出来,以便用户处理。在 API 调用的情况下,调用者可以指定如何处理它们。重要的一点是,这里需要的是直接的工程解决方案,而不是人工智能解决方案(因为目前还没有)。
深度学习(尤其是 NLP 的深度学习)历史上的核心人物 Yoshua Bengio 最近在 NLP 专家的一次调查中被问及该领域的当前方向和最大的未解决问题。作为回应,他谈到了“基础语言学习”和“常识理解”的必要性,并批评了当前试图在没有这些关键要素的情况下取得进展的纯数据驱动的方法。他甚至声称,试图训练真正理解语言的模型将是徒劳的,“如果我们不解决建立世界模型的更困难的问题,即理解我们的世界如何工作的模型,其中有人类和其他代理人。相反,我们需要咬紧牙关,争取用自然语言处理解决人工智能这个更大的奖项。”
虽然学术研究人员可能会开始瞄准这个更大的奖项,但我们这些在行业中寻求用 NLP 解决商业问题的人今天需要用我们已经掌握的技术来凑合,尽管它们可能有限。当涉及到文本分类、情感分析,当然还有机器翻译等任务时,基于神经网络的 NLP 方法已经被证明是一个巨大的进步。这些方法通过消耗大量的文本数据来学习单词和短语的有用的数字表示。参与训练这些系统的架构正变得越来越复杂,预训练的表示和模型正被广泛使用。这一切都很棒,但是这些模型和架构的复杂性无法克服它们固有的局限性:缺乏理解仍然存在,并且不可避免地会产生错误。设计使用这些技术的应用程序必须包括为他们将会犯的错误而设计,即使这意味着提供一个用户界面,这个界面是关于应用程序缺乏智能的。我敢打赌,用户会重视这种诚实,更不用说应用程序的实用性提高了。
来自虚拟员工的竞争?为什么客服人员不应该害怕聊天机器人
围绕聊天机器人的大肆宣传和 Gartnerr 的预测,在未来两年内,50%的大中型企业将使用聊天机器人,这可能会导致客户服务员工夜不能寐。用机器代替人类从事面向客户的工作毕竟不是什么新鲜事。想想自助扫描收银台和自动取款机。因此,客服人员的担忧是合理的吗?聊天机器人在中大型公司客服部门的应用会不会很快引起裁员潮?
掌舵聊天机器人
几乎可以肯定的是,在未来几年,聊天机器人将越来越多地接管消费者和公司之间的联系。
“聊天机器人技术很容易使用,即使对于中小型企业也是如此。如果我们相信 Gartner 的预测,我们将在未来几年看到更多成功的聊天机器人应用,”OBI4wan 首席执行官 Alexander de Ruiter 说。
聊天机器人已经有许多可能的应用。无论是通过在线零售商的实时聊天应用程序提供产品和风格建议,还是支持在度假预订网站上找到合适的航班或酒店,或者回答标准的客户服务问题,聊天机器人都可以随时提供帮助。虚拟助理可以完美地融入所有现代客户服务渠道,如 Facebook Messenger、WhatsApp、Twitter 和实时聊天。Gartner 的研究表明,客户服务是聊天机器人的最大领域,我们预计这一趋势将在 2019 年继续。在我们最新的博客中阅读更多关于 2019 年聊天机器人趋势的信息。
聊天机器人正在使人类成为客服中的多余人员吗?
在某些情况下,聊天机器人可以在没有人类同事的情况下独自解决整个案件。此外,由于人工智能,现代机器人变得越来越聪明。他们在与每一位顾客的接触中学习和提高。这是否意味着迟早我们将能够完全没有人力资源的客户服务?拥有 25 年客户服务经验的 OBI4wan 首席执行官亚历山大·德·鲁特(Alexander de Ruiter)的回答是明确的“不”。
“尽管机器人变得越来越智能,但我们相信人类劳动力仍将是必要的。机器人将无法取代人类。但是,工作的内容会发生变化,会出现新的职位。”
OBI4wan 首席创新官弗兰克·斯密特(Frank Smit)宣称,我们已经迎来了第二次工业革命:
“当时人们认为每个人都会被机器抢走工作,现在聊天机器人就是这样。,尽管实际上是机器人确保员工获得更大的附加值。因为有了机器人,员工可以花更多的时间更好地帮助他们的客户。聊天机器人的成功部署可确保提高客户满意度、员工满意度和效率。''
人类在客户服务中不可替代的 3 个理由
1.聊天机器人很聪明,但不够强势
当与客户直接联系时,聊天机器人最适合回答简单的日常问题,以及获取缺少的信息,如订单号。这里最大的附加价值是它们的响应能力和全天候可用性。然而,对于复杂的情况,聊天机器人缺乏必要的同理心。聊天机器人客观、真实的回答并不适用于所有情况。要真正理解顾客,回应他们,满足他们的需求,需要换位思考和敏感。
“机器人是为了效率,员工是为了同理心。由于这种有效性,机器人创造了更好的客户体验。”obi 4 wan 首席执行官亚历山大·德·鲁特
2.复杂的情况需要人与人之间的互动
根据具体情况,需要找到个性化的解决方案,这需要客户服务人员的创造力和创新思维。不能总是遵循规则和标准程序以尽可能最好的方式解决案件。在这里,客户服务员工比聊天机器人有更多的“自由”,聊天机器人必须遵守预先定义的业务规则才能正常工作。
3.最好的结果是通过人和机器人之间的合作来实现的
聊天机器人可以在几秒钟内从大量数据和系统中过滤信息,并将其提供给人类同事。此外,他们可以独立回答常规问题,并准备答案。这样,客户服务员工的工作量就减少了,这样他们就可以花更多的时间为单个客户服务,并以最好的方式帮助他们。通过将重点从回答常规问题转移到更复杂的案例,客服人员的工作变得更有意义。这正是运动器材和服装零售商和制造商迪卡侬(Decathlon)自 2018 年 9 月推出聊天机器人以来所经历的情况。
“自从 9 月份推出聊天机器人以来,我们有了更多的时间为客户服务,这是显而易见的。我们都是迪卡侬的运动员,擅长回答关于运动的问题。我们的目标和梦想是让我们的员工完全专注于体育建议,把标准问题留给聊天机器人。然后,我们就可以真正将我们对运动的热情传递给我们的顾客。迪卡侬社交媒体经理 Paul de Vries
有关迪卡侬社交媒体经理 Paul de Vries 的完整采访,请点击此处
如果客服人员反对他们的团队中有聊天机器人,该怎么办?
公司不应该忽视客户服务人员的担忧,而是应该公开交流聊天机器人的意义和目的。重要的是要明确聊天机器人不是一种竞争,但从长远来看,它将使工作变得更容易,并提高满意度——不仅在客户中,而且在员工中。客服员工可以释放他们的全部潜力,专注于他们工作的核心:与客户互动,并为解决复杂的问题提供协助,而不是在简单的日常问题上浪费你的精力。
你是如何在你的组织中实现聊天机器人的?让我知道!
为新的和有抱负的数据科学家汇编建议
将 30 多条建议浓缩到 6 分钟内

Photo by Christopher Burns on Unsplash
也许这只是我的看法,但我注意到在数据科学社区的媒体和其他地方,越来越多的帖子围绕着向该领域的新人提供建议。我认为这很棒,正是这种类型的内容在我的数据科学家之旅中给了我巨大的帮助,并且在我继续学习和成长的今天仍然帮助着我。
但是随着这些帖子的数量越来越多,读者需要做更多的工作来寻找、筛选和处理所有可用的信息。这篇文章旨在让有抱负的数据科学家更容易找到该领域专家的所有优秀建议。下面的大部分观点都是从我觉得特别有帮助的以下 6 个帖子中提炼出来的:
- 给新手和初级数据科学家的建议
- 开始成为数据科学家之前我希望知道的 12 件事
- 给新数据科学家的建议
- 给有抱负的数据科学家的 6 条建议
- 给有抱负的数据科学家的 16 条有用建议
- 有抱负的数据科学家掌握这些基础知识
首先,我通读了每一篇文章,挑出每一条个人见解或建议。然后,我查看了想法列表,并记下了不同资源之间的任何共同主题,如下所示。在这篇文章的后面,我包括了所有其他独立的建议。我们开始吧。
共同主题
如前所述,以下是上面链接的几篇文章中重复出现的观点。对于每一点,我都会附上一点我自己的评论。
掌握沟通的艺术
这可能是其中最受欢迎的主题。数据科学中沟通的重要性经常被反复提及,而且理由充分。如果你不能传达这些见解,而以某种方式推动影响,那么筛选数据以发现见解是没有用的。像其他任何事情一样,有效的沟通是一项技能,你可以随着时间的推移不断练习和提高。
建立坚实的统计基础
无论是数据分析、机器学习、运行实验,还是更深奥的东西,都无法避免使用统计学。花时间建立一个坚实的基础并掌握基本的统计概念将会一次又一次地给你带来回报。
保持怀疑——不断质疑你的假设
作为数据科学家,我们不断做出假设,不管我们是否知道。这些假设可能与我们正在处理的数据或我们试图解决的问题有关,但它们需要被质疑。通过对我们的产出保持某种程度的偏执,我们确保我们在正确的轨道上。这种技能通常与探索性的、研究性的工作联系在一起,但它比你想象的更适用。
好奇心会带你走很长的路——问很多问题
与最后一点类似,保持好奇心也是值得的。好奇心可以让你获得有趣的见解,否则你永远也不会发现。它驱使你采取一种成长的心态并不断提问——这些问题会像工作本身一样帮助你学习和成长。
将你的作品公之于众(Github、博客等)。)
这对我来说是个大问题。在我职业生涯的早期,我了解到将你的工作推向世界的好处。无论是通过博客帖子、项目、推文,还是其他方式——这都不重要。重要的是你把一些东西放在那里。下面的推文很好地总结了我对此的立场:
用你感兴趣的真实数据建立学习项目
当从事学习项目时,确保你对这个主题感兴趣。这看起来很简单,但许多有抱负的数据科学家都在努力开发对潜在员工和同事来说最复杂或最令人印象深刻的项目。坚持你喜欢的,使用真实世界的数据,而不是超级干净的 Kaggle 或 UCI 数据集。对于加分,收集一些数据,建立自己的数据集。
你永远不会知道所有的事情——这没关系
很明显,数据科学是一个广泛而复杂的领域。你可以用你的整个职业生涯来练习它,甚至不去浏览它的表面。总会有另一种技术需要掌握,另一种工具需要学习,还有另一篇文章需要阅读。这就是为什么冒名顶替综合症在这个领域如此重要。我发现这同时令人沮丧和兴奋。
为问题选择正确的工具并掌握它们
同样,仅仅因为你不能掌握所有的工具,并不意味着你不能掌握其中的一些。可能会有几个你一天中大部分时间都在使用的构建模块。那可能是 R、SQL、Vim、Airflow、Scikit-learn,任何真正的东西。只要你专注于你的关键工具并学好它们,这并不重要。
更多要点
这些是我在不止一个链接的帖子中没有找到的想法。您将在这里找到同样有用和有趣的信息,有些比以前的常见主题更专业。
- 有效地划分优先级
- 学会正确估计任务需要多长时间
- 想想你的关键路径
- 与经验丰富的数据科学家合作
- 教授和传播数据科学
- 学习领域知识,而不仅仅是方法
- 最重要的技能是批判性思维
- 参加活动——黑客马拉松、会议、聚会
- 学习相关技能,而不仅仅是技术技能
- 灵活选择进入该领域的方式
- 获得一些云计算的实践经验
- 习惯于将事物粘合在一起并支持服务
- 写白皮书
- 在开始之前,一定要确保你理解了你的数据
- 混合算法通常只能打败一种算法
- 尽可能多地学习数学和物理课程
- 投资于你的软件工程技能
- 相信自己,追随你的激情
- 尝试数据科学中的不同角色
- 交流分析时,讲一个故事
- 变量的分布通常比它的位置更重要
- 取样是困难的,不会总是完美的
- 成为自信的命令行用户,提高工作效率
- 不断学习
- 开一个博客,建立一个作品集来展示你的技能
- 寻找将数据科学用于其战略的公司
- 公司的规模会影响你的角色
- 不要对你的第一份工作要求完美
- 学习如何推销你的想法
额外资源
除了我用来编辑这个列表的主要文章,还有一百万篇其他伟大的博客文章供有抱负的数据科学家利用。下面的这些帖子也有助于了解上面列出的一些要点。
要想了解更多关于以上要点的信息,请务必浏览我在这篇文章中链接的精彩资源。
数据科学家之旅并不轻松。作为一名数据科学家起步也没什么不同。但是信息共享的美妙之处让事情变得简单多了。它让我们向前人学习。我认为这是一件很酷的事情。传下去,享受旅程。
感谢阅读!请随意查看下面我的一些类似文章,并订阅我的简讯以接收任何新内容。
你可以在 Medium 上关注我更多类似的帖子,也可以在推特上找到我。更多关于我和我在做什么的信息,请查看我的网站。
用机器学习和特征重要性来补充 A B 测试
特性重要性如何帮助我从 A B 测试中得出正确的结论

Photo by Everton Vila on Unsplash
在这篇文章中,我想重点关注使用机器学习来补充其他数据科学任务,特别是 A/B 测试。这应该是一个实用的帖子,而不是理论上的讨论,我假设你至少对 A/B 测试和随机森林和特征重要性有些熟悉。
我最近遇到了一个有趣的挑战,要求我运行一个 A/B 测试,以测试一种拍卖类型是否比另一种拍卖类型卖得快。简单地运行 A/B 测试会得出这样的结论:一种拍卖类型确实比另一种类型卖得快,但事实证明,拍卖类型不是更快销售时间背后的主要驱动因素。这是一种拍卖类型的较低的相关价格。如果销售这些汽车的公司决定通过这种拍卖方式来销售,而不是首先关注定价,这可能会产生可怕的后果。
我发现这一点的方法是在数据集上运行随机森林,然后获取要素重要性,这有助于我发现这一点。事实上,我通常认为机器学习是探索性数据分析(EDA)以及 A/B 测试的一个很好的补充工具。
数据
我们先来快速看一下数据。对于 A/B 测试,感兴趣的是销售渠道,即两种不同的拍卖类型和销售时间,这是销售日期和购买日期之间的差异。

挑战
挑战在于运行 A/B 测试来判断拍卖类型 1 或 2 是否卖得更快——例如,如果我们可以得出一种类型比另一种类型卖得更快的结论,公司可以专注于通过一次拍卖卖出更多。
A/B 测试
事实证明,两种拍卖类型的销售时间分布都不是“正态”或 t 分布。两者都有一条长长的尾巴。然而,由于有了中心极限定理,我们可以通过从这些分布中随机取样并每次取其平均销售时间,将这些分布转换成更正态的分布。例如,如果我们这样做 1000 次,那么我们从图 1(显示每种拍卖类型的销售时间)到图 2(显示每种拍卖类型的平均销售时间)。
# Do the samplingresults = [] # create an empty list into which I insert the sampled means
random_state = np.arange(0,1000) # random seeds for reproducibility# sample with replacement using 50% of the data; do this 1000 times
# and append the mean seeling time to the list ‘results’
for i in range(1000):
sample = df.sample(frac=0.5, replace=True,
random_state=random_state[i]).groupby(by=’sales_channel’)[‘selling_time’].mean()
results.append(sample)
results = pd.DataFrame(results)

Graph1 — Distribution of Selling Time in Days

Graph 2 — Average Selling Time in Days
图 1 中不太明显的情况在图 2 中变得非常明显:拍卖类型 1 的平均销售时间比拍卖类型 2 短得多。运行 A/B 测试证实了这一点,p 值为 0.00。在这里运行 A/B 测试似乎是多余的,因为分布甚至没有重叠。
如果我们现在得出结论,该公司应该只通过拍卖类型 1 销售,因为汽车销售更快,那么我们可能会犯一个错误。
如果除了销售时间之外,还有其他特性/特征使这两种拍卖类型截然不同呢?这个数据集只有很少的特征,所以我们可以绘制更多相似的分布,计算可能的相关性等等。一种更简单、更有效的方法是使用机器学习来帮助我们——当我们有更多功能时,这变得特别有用——如果不是必要的话。
对救援的重要性
大多数机器学习算法都有一种计算其特征重要性的方法,即每个特征在预测目标/依赖特征时发挥了多大的作用。这里的目标是销售时间。
我在数据集上运行了随机森林,并计算了特征重要性。我还使用了 CatBoost,一种梯度提升树方法,但得出了相同的结论。这就是我坚持使用随机森林的原因,因为这是一种更容易让你理解的算法。
# Set up the model and define its parameters — let’s keep it simple
rf = RandomForestRegressor(n_estimators=100, max_depth=5)# Fit the model
rf.fit(X_train, y_train)# Calculate the mean feature importance
importances = rf.feature_importances_# Calculate the standard deviation of the feature importance
std = np.std([tree.feature_importances_ for tree in rf.estimators_],
axis=0)# Sort the features by their importance
indices = np.argsort(-importances)[::-1]# Plot the feature importances
plt.figure(figsize=(12,8))
plt.title(“Feature importances”)
plt.barh(range(X.shape[1]), importances[indices],
color=”r”, yerr=std[indices], align=”center”)
plt.yticks(range(X.shape[1]),X.columns[indices], rotation=0)
plt.ylim([len(importances)-6, len(importances)])
plt.show()

Random Forest Feature Importance

SHAP Values
在左侧,您可以看到每个特性对预测销售时间的重要性,柱越长,特性越重要。购买价格是最重要的特征,而销售渠道(拍卖类型)几乎无关紧要。
然后我计算了另一种叫做 SHAP 的特征重要性。阅读图表的方法如下:每个点是数据集中的一行/一个观察值。负 SHAP 值意味着特征值降低了目标预测值(销售时间)。蓝色表示特征值低,红色表示特征值高。当查看买入价格时,我们看到所有负 SHAP 值都是蓝色的,即低买入价格导致低卖出时间的预测。但是请注意,也有一些蓝点具有正 SHAP 值。在这种情况下,低买价导致相反的预测,即较长的销售时间。当要素相互交互时,可能会发生这种情况。有趣的是,销售渠道(拍卖类型)被很好地划分为红色的拍卖类型 1 和蓝色的拍卖类型 2。
# Plot the overall Feature Importance using SHAP
shap.initjs()
explainer = shap.TreeExplainer(cat)
shap_values = explainer.shap_values(pool_val)
shap.summary_plot(shap_values, X_val)

Average selling price distributions per auction type
在我们结束之前,让我们简要地看一下拍卖类型的购买价格。我们已经从特性的重要性中怀疑过这一点。两种拍卖类型的价格分布非常不同。拍卖类型 1 (ca。$6k)的价格比拍卖类型 2(大约 24k 美元)。
结论
简单地进行 A/B 测试,看一种拍卖类型是否比另一种卖得快,可能会导致错误的结论。运行随机森林或任何其他机器学习算法,然后计算特征重要性,可以让您更好地了解哪些特征会影响您感兴趣的目标。我发现使用机器学习来补充 A/B 测试有助于我做出更好的决策。希望对你也有帮助。
使用 Python 处理 Whatsapp 数据的完整初学者指南
利用基本的 Python 方法而不是 Regex 来处理文本数据

Photo by Rachit Tank on Unsplash
自由文本金矿
从给你爱的人发短信、发送迷因和专业用途,Whatsapp 一直以每月 15 亿活跃用户统治着全球的 messenger 市场。当涉及到复杂的 NLP 建模时,自由文本就是黑金。
面向企业的 NLP 提供了增强的用户体验,包括拼写检查、反馈分析甚至虚拟助手。
有了 NLP,自动完成并不是企业升级网站搜索的唯一方式。Klevu 是一个智能搜索提供商…
www.wonderflow.co](https://www.wonderflow.co/blog/natural-language-processing-examples)
在某些情况下,小企业可能会创建 Whatsapp 聊天组,在成员之间传递信息,作为建立系统记录数据的低成本替代方案。基于规则的聊天系统在聊天开始时就商定如何传播信息。考虑下面的例子:
21/09/2019, 14:04 — Salesperson A: Item B/Branch C/Sold/$1900
21/09/2019, 16:12 — Salesperson X: Item Y/Branch Z/Refund/$1600, defect found in product, not functioning
我们可以立即识别出与来自不同销售人员销售订单相关的模式,这些模式由常见的运算符分隔,如“/”和“,”。有了这样一个简单的系统(但容易出现人为拼写错误),我们可以使用 Whatsapp 分析不同产品和不同地区的销售模式。
方法学
网上有很多很好的资源可以把 Whatsapp 的数据转换成熊猫的数据框架。大多数(如果不是全部的话)使用 Python 的 Regex 库作为相当复杂的解决方案,将文本文件分割成数据帧的列。
然而,我在这里的目标是针对初学字符串操作的 Python 用户。对于学习 Python 的初学者来说,我们更熟悉不是来自外部库的基本 Python 方法。在这篇文章中,我们将使用许多基本方法来处理 Whatsapp 数据到 pandas 数据框架中。
以下是我们将要讨论的内容:
- 2 个库(用于 dataframe 和 datetime 的 pandas,用于检测 datetime 对象)
- 很多。split()方法
- 列出理解
- 错误处理
步骤 1:获取数据
如果直接从手机导出信息不太方便,您可以尝试以下方法:
[## 在安卓、iPhone、黑莓手机上阅读、提取 WhatsApp 信息备份
毫无疑问,WhatsApp 是移动设备上排名第一的信息服务,在 Android、iOS、黑莓等平台上都有应用
geeknizer.com](http://geeknizer.com/read-extract-whatsapp-messages-android-iphone-blackberry/)
不然最简单的提取 Whatsapp 的方法。txt 文件可以通过以下方法完成:
- 打开你的 Whatsapp 应用
- 选择您感兴趣的聊天
- 点击“…”>选择“更多”>选择“不带媒体的导出聊天”并将其发送到您的个人电子邮件
完成后,您的文本文件应该如下所示:
21/09/2019, 23:03 — Friend: my boss dont like filter
21/09/2019, 23:03 — Friend: he likes everything on a page
21/09/2019, 23:03 — Me: so basically you need to turn your data into ugly first then come out pivot table
21/09/2019, 23:03 — Me: haha
21/09/2019, 23:04 — Me: pivot table all in 1 page what
21/09/2019, 23:05 — Me: but ya i hate this kinda excel work sia
21/09/2019, 23:05 — Me: haha
21/09/2019, 23:05 — Friend: as in
21/09/2019, 23:05 — Me: hope to transition to data scientist asap
步骤 2:将数据导入 Python IDE
我们要做的第一件事是确保我们知道你的文本文件的位置。一旦我们知道了它的目的地,我们就可以将我们的工作目录设置为文件的位置:
import os
os.chdir('C:/Users/Some_Directory/...')
一旦解决了这个问题,我们就要定义一个函数,用下面的方法将文本文件读入 Python 变量:
上面的函数将我们的文本文件转换成一个字符串列表,允许我们使用。split()方法。但是现在,你需要做一些清洁工作。
步骤 3:处理多行消息
有时,由于多行文本,您提取的数据可能不是完美的格式。考虑下面的情况,使用上面已经转换为列表的同一个销售人员示例:
21/09/2019, 14:04 — Salesperson A: Item B/Branch C/Sold/$1900**'Some random text formed by new line from Salesperson A'**21/09/2019, 16:12 — Salesperson X: Item Y/Branch Z/Refund/$1600, defect found in product, not functioning
我们可以观察到‘某个随机文本’并不具有 Whatsapp 文本的每一行都应该具有的相同的通常格式。要处理这样的元素,我们先来看看 Whatsapp 短信的模式。

忽略日期之后的所有内容,显然不需要的元素中没有日期对象。所以我们通过检查它们是否包含第一个','之前的日期来开始删除它们。我们通过利用基本错误处理技术来做到这一点。
正如你所看到的,我们已经删除了大约 100 个元素,这些元素可能会对以后的特征提取造成阻碍。除非我们和朋友分享带标题的链接,否则不使用多行文本是我们大多数休闲短信文化的一部分。
步骤 4:特征提取
现在,您将使用基本的 Python 技能从列表中提取要素,稍后您将把这些要素解析到数据帧中。首先,我们需要重新审视 Whatsapp 数据中的字符串模式。
我们想要提取的第一个特征是日期。请记住,日期字符串正好出现在第一个','之前的。所以我们使用。索引 0 处的 split(“,”)方法。我们可以用 Python 的 list comprehension 漂亮地写出这个。
请注意,我来自 R 背景,我非常习惯在 for 循环中使用“I”。不使用 range()函数编写上述代码的另一种方法如下:
date = [text.split(‘,’)[0] for text in chat]
相比之下,这是使用 Regex 方法检查字符串模式是否为 date 所需要的。
Credits: Samir Sheriff

All that just to identify the date feature?! (Photo by Ben White on Unsplash)
这样一来,我们可以在提取发送者的时间和姓名时使用相同的逻辑。请注意以下模式:
- 时间字符串出现在第一个','之后,第一个'-'之前
- 名称字符串出现在索引 0 处的第一个'-' 之后,紧接着是第二个':'之后的
最后,我们要提取消息的内容。这有点棘手,因为某些行不包含任何消息。相反,它们是系统生成的消息,如下所示:
21/09/2019, 11:03 — **Salesperson A created the group "Dummy Chat"** 21/09/2019, 11:03 — **Salesperson A added Salesperson B** 21/09/2019, 14:04 — Salesperson A: Item B/Branch C/Sold/$1900
21/09/2019, 16:12 — Salesperson X: Item Y/Branch Z/Refund/$1600, defect found in product, not functioning
请注意,在时间字符串中出现的第一个“:”后面没有附加的“:”。要正确看待这个问题,请考虑以下几点。拆分(':')方法:
chat.split(":")#['21/09/2019, 14','04 — Salesperson A',' **Item B/Branch C/Sold/$1900**']
索引 2 处的元素是我们感兴趣的。但是,由于系统生成的消息不包含第二个“:”,在索引 2 处提取信息将会产生错误。因此,我们将继续我们的第二个错误处理技术。
您可以选择稍后删除带有“缺失文本”的元素。
最后一步:将所有内容连接成一个数据帧
现在我们有了 4 个特性列表,我们终于可以用一行代码创建一个熊猫数据框架了!

瞧啊。您的数据框已准备好进行后期分析!请注意出现在“名称”列中的系统生成的消息。您可以使用以下代码有条件地删除系统生成的消息中的行:
df = df[df[‘Content’] != ‘Missing Text’]
最后的想法
有许多方法可以利用处理过的 Whatsapp 文本数据来进行分析。从 把自己重新塑造成一个机器人 ,用 NLP 进行情感分析 到只是简单的 分析 。利用 Whatsapp 数据对于任何复杂的 NLP 项目来说都是很好的实践。
基本的字符串操作足以将文本文件转换成如上所示的熊猫数据帧。如果你是 Python 的新手(像我一样),最好习惯于基础知识,而不是尝试新技术,这些新技术在开始时可能会有点让人不知所措。然而,Python 的 regex 库仍然是文本挖掘和数据验证的中级到高级应用的重要工具。
这里有一篇很棒的文章,解释了 Python 中 Regex 库的概念及其在数据分析和数据科学中的潜在用途:
原始模式查找器
towardsdatascience.com](/the-ultimate-guide-to-using-the-python-regex-module-69aad9e9ba56)
编码快乐!
用 Mlflow 为非傻瓜完成数据科学项目模板。
从刚起步的忍者到大型企业团队,在本地或云中工作的每个人的最佳实践。
数据科学作为一个领域和业务职能部门已经走过了漫长的道路。现在有跨职能团队在研究算法,一直到全栈数据科学产品。

Kenneth Jensen [CC BY-SA 3.0]
随着数据科学的日益成熟,出现了最佳实践、平台和工具包的新兴标准,大大降低了数据科学团队的准入门槛和价格。这使得公司和从业者更容易接触到数据科学。对于大多数商业应用团队来说,数据科学家可以站在高质量开源社区的肩膀上进行日常工作。
但是成功的故事仍然被许多未能获得业务适应性的数据科学项目所掩盖。据普遍报道,超过 80%的数据科学项目仍然无法产生业务影响。有了所有高质量的开源工具包,为什么数据科学难以产生业务影响?这不是一道数学题!
数据科学的成功受到了通常称为“最后一英里问题”的困扰:
“模型的生产化是数据科学中最棘手的问题”(Schutt,R & O'Neill C .从第一线直接做数据科学,O'Reilly 出版社。加利福尼亚州,2014 年)
在这篇博文中,我讨论了建立一个数据科学项目,模型开发和实验的最佳实践。数据科学项目模板的源代码可以在 GitLab 上找到:
[## Jan Teichmann 的基于 Mlflow 的数据科学项目模板
GitLab.com 项目源代码
gitlab.com](https://gitlab.com/jan-teichmann/ml-flow-ds-project)
你可以在我之前的一篇博文中读到 Rendezvous Architecture,它是在生产中操作模型的一个很好的模式:
如何构建一个前沿的数据科学平台来解决数据科学中的真正挑战:生产化。
towardsdatascience.com](/rendezvous-architecture-for-data-science-in-production-79c4d48f12b)
像专家一样研究数据科学
当工程师构建生产平台、DevOps 和 DataOps 模式以在生产中运行模型时,许多数据科学家的工作方式仍然在数据科学和生产环境之间造成了巨大的差距。许多数据科学家(没有任何指责)
- 在本地工作而不太考虑可能在生产中托管他们的模型的云环境
- 使用 python 和 pandas 在内存中处理小型数据集,而没有过多考虑如何将工作流扩展到生产中的大数据量
- 使用 Jupyter 笔记本工作,无需过多考虑可重复实验
- 很少将项目分解成独立的任务,为 ETL 步骤、模型训练和评分构建分离的管道
- 软件工程的最佳实践很少应用于数据科学项目,例如抽象和可重用代码、单元测试、文档、版本控制等。
不幸的是,我们心爱的灵活的 Jupyter 笔记本电脑在这方面发挥了重要作用。

From “Managing Messes in Computational Notebooks” by Andrew Head et al., doi>10.1145/3290605.3300500
请务必记住,数据科学是一个正在经历快速创新的领域和业务职能。如果你在我发表文章一个月后读到这篇文章,可能已经有新的工具和更好的方式来组织数据科学项目了。成为数据科学家的激动人心的时刻!让我们看看数据科学项目模板的详细信息:
数据科学环境

Icons made by Freepik, phatplus, Becris from www.flaticon.com
一个数据科学项目由许多可移动的部分组成,实际的模型可能是项目中最少的代码行。数据是您项目的燃料和基础,首先,我们应该为我们的项目建立坚实、高质量和可移植的基础。数据管道是大多数数据科学项目中隐藏的技术债务,您可能听说过臭名昭著的 80/20 法则:
80%的数据科学是寻找、清理和准备数据
我将在下面解释 Mlflow 和 Spark 如何帮助我们更高效地处理数据。

Google NIPS 2015, fair usage
创建您的数据科学模型本身是在实验和扩展项目代码库以捕获有效的代码和逻辑之间的持续往复。这可能会变得混乱,Mlflow 在这里使实验和模型管理对我们来说明显更容易。这也将为我们简化模型部署。稍后将详细介绍这一点!
数据

W.Rebel [CC BY 3.0]
你可能听说过数据科学的 80/20 法则:许多数据科学工作都是关于创建数据管道来消费原始数据、清洁数据和工程特征,以最终提供给我们的模型。
我们希望如此
- 我们的数据是不可变的:我们只能创建新的数据集,而不能就地改变现有的数据。因此,
- 我们的管道是 DAG 中一组分离的步骤,逐步提高我们数据的质量和聚合到特性和分数中,以便在生产中轻松地将我们的 ETL 与 Airflow 或 Luigi 等工具相结合。
- 每个数据集都有一个定义的数据模式T21,可以安全地读取数据,不会出现任何意外,这些数据可以记录到一个中央数据目录中,以便更好地管理和发现数据。****
数据科学项目模板有一个保存项目数据和相关模式的数据文件夹:

[OC]
数据科学家通常不仅处理大数据集,还处理非结构化数据。构建数据仓库的模式需要设计工作和对业务需求的良好理解。在数据科学中,当创建 DWH 的模式时,许多问题或问题陈述是未知的。这就是为什么 Spark 已经发展成为该领域的黄金标准
- 使用非结构化数据
- 分布式企业成熟的大数据 ETL 管道
- 数据湖部署
- 统一批处理和微批处理流平台
- 能够在许多不同的系统之间使用和写入数据
拥有成熟基础设施的公司的项目使用先进的数据湖,其中包括用于数据/模式发现和管理的数据目录,以及带有气流的调度任务等。虽然数据科学家不一定要理解生产基础设施的这些部分,但是最好在创建项目和工件时牢记这一点。
这个数据科学项目模板使用 Spark,不管我们是在本地数据样本上运行它,还是在云中针对数据湖运行它。一方面,Spark 在本地处理小数据样本时会感觉有些矫枉过正。但另一方面,它允许一个可转移的堆栈为云部署做好准备,而无需任何不必要的重新设计工作。这也是一种重复的模式,可以很好地自动化,例如通过 Makefile。
虽然模型的第 1 版可能会使用来自 DWH 的结构化数据,但最好还是使用 Spark,并减少项目中的技术债务,因为模型的第 2 版将使用更广泛的数据源。
对于本地项目开发,我使用一个简单的 Makefile 来自动执行数据管道和项目目标。然而,这可以很容易地转化为气流或 Luigi 管道,用于云中的生产部署。

[OC]
该项目的端到端数据流由三个步骤组成:
- ****原始数据:从机器学习数据库档案中下载虹膜原始数据
- ****中间数据:执行特征工程流水线,用 Spark 批量实现虹膜特征
- ****处理数据:执行分类模型,使用 Spark 批量实现预测
您可以使用以下 make 命令,通过 Spark 管道将 iris 原始数据转换为特征:
make interim-data
它将压缩当前项目代码库,并将project/data/features . py脚本提交给我们 docker 容器中的 Spark 以供执行。我们使用 Mlflow runid 来识别和加载所需的 Spark 功能管道模型,但稍后将详细介绍 Mlflow 的使用:

[OC]
在执行我们的 Spark 特征管道之后,我们在我们的小型本地数据湖中实现了临时特征数据:

[OC]
如您所见,我们保存了我们的特征数据集及其相应的模式。Spark 使得保存和读取模式变得非常容易:
some_sparksql_dataframe.schema.json()
T.StructType.fromJson(json.loads(some_sparksql_schema_json_string))
总是用相应的模式来具体化和读取数据!强化模式是打破数据科学中 80/20 法则的关键。
我认为为 csv 和 json 文件编写模式是强制性的,但我也会为任何自动保留其模式的 parquet 或 avro 文件编写模式。我真的推荐你多读一些关于三角洲湖、阿帕奇胡迪、数据目录和特色商店的内容。如果有兴趣的话,我会就这些话题发表一篇独立的博文。
实验用 Jupyter 笔记本
Jupyter 笔记本对于实验来说非常方便,数据科学家不太可能会停止使用它们,这让许多被要求从 Jupyter 笔记本“生产”模型的工程师非常沮丧。折中的办法是利用工具的优势。在笔记本上进行实验是高效的,而且效果很好,因为在实验中证明有价值的长代码会被添加到遵循软件工程最佳实践的代码库中。
我个人的工作流程是这样的:
- 实验:首先在 Jupyter 笔记本上测试代码和想法
- 将有价值的代码整合到 Python 项目代码库中
- 删除 Jupyter 笔记本中的代码单元格
- 用从项目代码库导入的代码替换它
- ****重启你的内核和按顺序执行所有步骤,然后继续下一个任务。

[OC]
隔离我们的数据科学项目环境并管理我们的 Python 项目的需求和依赖性是很重要的。没有比通过 Docker 容器更好的方法了。我的项目模板使用来自 DockerHub 的jupyter all-spark-notebook Docker image作为方便的实验室设置,包括所有电池。项目模板包含一个 docker-compose.yml 文件,Makefile 通过一个简单的
make jupyter
该命令将启动我们的项目所需的所有服务(Jupyter with Spark、Mlflow、Minio ),并在我们的实验环境中安装 pip 的所有项目需求。我使用 Pipenv 为我的项目管理我的虚拟 Python 环境,使用 pipenv_to_requirements 为 DevOps 管道和基于 Anaconda 的容器映像创建 requirements.txt 文件。考虑到 Python 作为一种编程语言的流行程度,Python 工具有时会让人觉得笨重和复杂。😒
下面的屏幕截图显示了示例笔记本环境。我使用片段来设置个人笔记本,使用 %load magic 。它还展示了我如何使用项目代码库的代码来导入原始 iris 数据:

Jupyter 笔记本展示了我的开发工作流程,扩展了项目代码库和模型实验,并添加了一些注释。https://git lab . com/Jan-teichmann/ml-flow-ds-project/blob/master/notebooks/Iris % 20 features . ipynb
笔记本电脑的 Git 版本控制

public domain image
Git 中 Jupyter 笔记本的版本控制并不像我希望的那样用户友好。的。ipynb 文件格式不是很友好。至少,GitHub 和 GitLab 现在可以在他们的 web 界面中呈现 Jupyter 笔记本,这非常有用。
为了使本地计算机上的版本控制更容易,该模板还安装了 nbdime 工具,它使 Jupyter 笔记本的 git 差异和合并变得清晰而有意义。您可以在项目中使用以下命令:
- 以终端友好的方式比较笔记本电脑
- nbmerge 三方合并笔记本,自动解决冲突
- nbdiff-web 向您展示丰富的笔记本渲染差异
- nbmerge-web 给你一个基于 web 的三路笔记本合并工具
- 以终端友好的方式呈现单个笔记本

https://github.com/jupyter/nbdime
最后但同样重要的是,项目模板使用 IPython 钩子来扩展 Jupyter 笔记本保存按钮,并额外调用 nbconvert 来创建一个额外的。py 脚本和。html 版本的 Jupyter 笔记本,每次您将笔记本保存在子文件夹中。一方面,这使得其他人更容易通过检查纯 Python 脚本版本的 diff 来检查 Git 中的代码更改。另一方面,html 版本允许任何人查看渲染的笔记本输出,而无需启动 Jupyter 笔记本服务器。这使得将笔记本输出的一部分通过电子邮件发送给不使用 Jupyter 的同事变得非常容易。👏
要在每次保存时自动转换笔记本,只需放置一个空的。ipynb _ saveprocess文件放在您笔记本的当前工作目录下。

[OC]
Mlflow 跟踪实验和日志模型

fair usage
作为我们在 Jupyter 实验的一部分,我们需要跟踪我们创建的参数、度量和工件。Mlflow 是创建可重复且可靠的数据科学项目的绝佳工具。它为中央跟踪服务器提供了一个简单的用户界面来浏览实验,并提供了强大的工具来打包、管理和部署模型。
在我们的数据科学项目模板中,我们使用专用跟踪服务器和存储在 S3 上的工件来模拟生产 Mlflow 部署。我们在本地使用 Min.io 作为开源的 S3 兼容替身。无论您是在本地使用 Mlflow,还是在云中托管或完全在数据块上管理,使用 ml flow 的行为和体验都不应有任何差异。
docker-compose.yml 文件为我们的项目提供了所需的服务。您可以访问http://localhost:9000/minio上的 blob 存储 UI 和 http://localhost:5000/ 上的 Mlflow 跟踪 UI
在 Mlflow 中,我们命名了可以运行任意次数的实验。每次运行都可以跟踪参数、指标和工件,并且有一个唯一的运行标识符。Mlflow 1.4 还刚刚发布了一个模型注册表,以便更容易地围绕模型生命周期组织运行和模型,例如不同版本的生产和试运行部署。

[OC]
这个示例项目训练两个模型:
- 一种火花特征流水线
- Sklearn 分类器
我们的 Spark 特性管道使用 Spark ML StandardScaler 使管道有状态。因此,我们对待特征工程的方式与对待任何其他数据科学模型的方式完全相同。本例的目的是在批评分和实时评分中同时使用使用不同框架的两个模型,而无需对模型本身进行任何重新设计。这将有望展示使用 Mlflow 简化数据科学模型的管理和部署的强大功能!

Icons made by Freepik, phatplus, Smashicons from www.flaticon.com
我们简单地遵循 Mlflow 惯例,将训练好的模型记录到中央跟踪服务器。

public domain image
唯一的问题是,当你试图用 boto3 上传空文件到 minio 时,当前的 boto3 客户端和 Minio 不能很好地协同工作。Spark 用 empty _SUCCESS 文件序列化模型,导致标准的 mlflow.spark.log_model() 调用超时。我们通过将序列化的模型保存到本地磁盘,并使用 minio 客户端记录它们,而不是使用project . utility . ml flow . log _ artifacts _ minio()函数,来解决这个问题。下面的代码训练一个新的 spark 特性管道,并以两种方式记录管道:Spark 和 Mleap。稍后将详细介绍 Mleap。

[OC]
在我们的项目中,模型被保存并记录在 models 文件夹中,不同的 docker 服务将它们的数据保存在这里。我们可以在 models/mlruns 子文件夹中找到来自 Mlflow tracking server 的数据,并在 models/s3 子文件夹中找到保存的工件。

[OC]
示例项目中的 Jupyter 笔记本有望给出如何使用 Mlflow 来跟踪参数和度量以及日志模型的好主意。Mlflow 使序列化和加载模型成为一个梦想,并从我以前的数据科学项目中删除了许多样板代码。

[OC]
Mlflow 模型的批量评分
我们的目标是使用具有不同技术和风格的完全相同的模型对我们的数据进行批量和实时评分,而无需任何更改、重新设计或代码复制。
我们批量评分的目标是火花。虽然我们的特征管道已经是 Spark 管道,但是我们的分类器是一个 Python sklearn 模型。但不用担心,Mlflow 使处理模型变得非常容易,并且有一个方便的函数可以将 Python 模型打包到 Spark SQL UDF 中,以便在 Spark 集群中分发我们的分类器。就像魔法一样!
- 加载序列化的 Spark 功能管道
- 将序列化的 Sklearn 模型加载为火花 UDF
- 使用特征管线转换原始数据
- 将火花特征向量转化为默认火花阵列
- 用展开的数组项调用 UDF

[OC]
有一些关于正确版本的 PyArrow 的问题,UDF 不能与火花矢量一起工作。

public domain image
但是从一个例子来看,这很容易实现。我希望这能为您省去无尽的 Spark Python Java 回溯的麻烦,也许未来的版本会进一步简化集成。现在,在 Spark 2.4 中使用 PyArrow 0.14,将 Spark 向量转换成 numpy 数组,然后转换成 python 列表,因为 Spark 还不能处理 numpy 类型。您在使用 PySpark 之前可能遇到的问题。
所有详细的代码都在 Git 存储库中。https://git lab . com/Jan-teichmann/ml-flow-ds-project/blob/master/notebooks/Iris % 20 batch % 20 scoring . ipynb
Mlflow 模型的实时评分
我们希望使用我们的模型使用 API** 实时地对请求进行交互式评分,而不是依赖 Spark 来托管我们的模型。我们希望我们的评分服务快如闪电,由集装箱化的微服务组成。同样,Mlflow 为我们提供了实现这一目标所需的大部分东西。**
我们的 sklearn 分类器是一个简单的 Python 模型,将它与 API 结合并打包到容器映像中非常简单。这是一种非常常见的模式,Mlflow 为此提供了一个命令:
mlflow models build-docker
这就是用 Mlflow 打包 Python 风格的模型所需的全部内容。不需要用 Flask 编写 API 的重复代码来封装数据科学模型。🙌
不幸的是,我们的功能管道是一个火花模型。然而,我们在 Mleap 版本中序列化了管道,这是一个托管 Spark 管道的项目,不需要任何 Spark 上下文。

fair usage
Mleap 非常适合数据量较小且我们不需要任何分布式计算且速度最为重要的使用情形。在 Makefile 中,Mleap 模型的打包是自动进行的,但包括以下步骤:
- 使用 Mlflow 将模型工件下载到一个临时文件夹中
- 压缩工件以进行部署
- 运行combustml/mleap-spring-bootdocker 映像,并将我们的模型工件挂载为一个卷
- 使用 mleap 服务器 API 部署用于服务的模型工件
- 将 JSON 数据传递给我们服务的特性管道的转换 API 端点
运行make deploy-real time-model命令,您将获得两个微服务:一个用于使用 Mleap 创建要素,另一个用于使用 Sklearn 进行分类。 project/model/score.py 中的 Python 脚本将对这两个微服务的调用包装成一个方便的函数,便于使用。运行make score-real-time-model以获得对评分服务的示例调用。
您也可以从 Jupyter 笔记本中调用微服务。下面的代码显示了我们的交互式评分服务有多快:对两个模型 API 的调用加起来不到 20 毫秒。

测试和文件
示例项目使用 Sphinx 来创建文档。进入文档目录,运行 make html 为你的项目生成 html 文档。只需将斯芬克斯 RST 格式的文档添加到 python 文档字符串中,并在 docs/source/index.rst 文件中包含要包含在生成的文档中的模块。

[OC]
单元测试放在测试文件夹中,python 单元测试可以用
make test
在写这篇博文的时候,数据科学项目模板和大多数数据科学项目一样,还没有测试😱我希望有一些额外的时间和反馈,这将改变!
数据块

fair usage
没有对 Databricks 平台的讨论,任何关于 Mlflow 的博客文章都是不完整的。如果没有 Databricks 的持续开源贡献,数据科学社区将不会是现在这样,我们必须感谢他们对 Mlflow 的最初开发。❤️
因此,运行 MLFlow 的另一种方法是利用 Databricks 提供的 Apache Spark 的平台即服务版本。Databricks 使您能够以非常少的配置运行 MLFlow,通常称为“托管 MLFlow”。这里可以找到一个特征对比:https://databricks.com/product/managed-mlflow
只需用 pip 将 MLFlow 包安装到您的项目环境中,您就拥有了所需的一切。值得注意的是,Databricks 中的 MLFlow 版本不是已经描述过的完整版本。这是一个在 Databricks 生态系统内部工作的优化版本。

[OC]
当你创建一个新的“MLFlow 实验”时,系统会提示你输入一个项目名称和一个要用作人工制品存储的人工制品位置。该位置表示您将捕获在 MLFlow 实验期间生成的数据和模型的位置。您需要输入的位置需要遵循以下约定“dbfs:/

[OC]
配置 MLFlow 实验后,您将看到实验跟踪屏幕。在这里,您可以看到模型在训练时的输出。您可以根据参数或指标灵活地过滤多次运行。创建实验后,您需要记下实验 ID。在每个笔记本中配置 MLFlow 的过程中,您需要使用它来指回这个单独的位置。

[OC]
要将实验跟踪器连接到您的模型开发笔记本,您需要告诉 MLFlow 您正在使用哪个实验:
with mlflow.start_run(experiment_id="238439083735002")
一旦 MLFlow 被配置为指向实验 ID,每次执行将开始记录和捕获您需要的任何指标。除了度量标准,您还可以捕获参数和数据。数据将与创建的模型存储在一起,这为前面讨论的可重用性提供了一个很好的管道。要开始记录参数,只需添加以下内容:
mlflow.log_param("numTrees", numTrees)
要记录损失指标,您可以执行以下操作:
mlflow.log_metric("rmse", evaluator.evaluate(predictionsDF))

[OC]
一旦度量被捕获,就很容易看到每个参数是如何对模型的整体有效性做出贡献的。有各种可视化来帮助你探索不同的参数组合,以决定哪种模型和方法适合你正在解决的问题。最近 MLFlow 实现了一个自动记录功能,目前只支持 Keras。当这个被打开时,所有的参数和度量将被自动捕获,这真的很有帮助,它大大减少了你需要添加的模板代码的数量。

[OC]
如前所述,可以使用以下工具记录模型:
mlflow.mleap.log_model(spark_model=model, sample_input=df, artifact_path="model")
如果您已经在使用数据块,托管 MLflow 是一个很好的选择。实验捕捉只是提供的众多功能之一。在 Spark & AI 峰会上,宣布了支持模型版本控制的 MLFlows 功能。当与 MLFlow 的跟踪功能结合使用时,使用新的模型注册中心只需点击几下鼠标,就可以将模型从开发转移到生产中。
摘要
在这篇博文中,我记录了我的[固执己见的]数据科学项目模板,该模板在本地开发时考虑了云中的生产部署。这个模板的全部目的是应用最佳实践,减少技术债务和避免重新设计。
它演示了如何使用 Spark 创建数据管道和带有 Mlflow 的日志模型,以便于管理实验和部署模型。
我希望这能为你节省数据科学的时间。❤️
密码

public domain image
数据科学项目模板可在 GitLab 上找到:
** [## Jan Teichmann 的基于 Mlflow 的数据科学项目模板
GitLab.com 项目源代码
gitlab.com](https://gitlab.com/jan-teichmann/ml-flow-ds-project)
信用
我并不总是一个优秀的工程师和解决方案架构师。我正站在巨人的肩膀上,我要特别感谢我的朋友们来自 www.advancinganalytics.co.uk的特里·麦肯和西蒙·怀特利
****
**
Jan 是公司数据转型方面的成功思想领袖和顾问,拥有将数据科学大规模应用于商业生产的记录。他最近被 dataIQ 评为英国 100 位最具影响力的数据和分析从业者之一。
****在 LinkedIn 上连接:https://www.linkedin.com/in/janteichmann/
****阅读其他文章:https://medium.com/@jan.teichmann
成为数据科学家的完整指南
数据科学课程、数据科学书籍、数据科学资料
过去几年,我每天都在使用数据科学和机器学习工具。我经常收到的问题是一个人应该读什么以及最开始完成哪些课程。所以我决定列出我所知道的一切,这在一开始就很有价值。我计划定期更新这篇文章,以跟上新材料——在下面评论,让我知道我错过了什么。谢谢大家!

Become a Data Scientist by yourself — a complete guide
我们可以将学习数据科学的来源分为 3 组:
- 数据科学课程
- 数据科学在线材料
- 数据科学书籍
我将在新的一章中逐一介绍。
首先,我应该提到,我假设你已经了解 Python 的基础知识——能够导入不同的数据格式并在 Jupyter 笔记本中清理它们。其次,你应该知道统计学和线性代数的基础知识。
如果你不知道这些,我推荐你从
- CodeCademy 免费学习 Python。
- 对于在线和免费的统计资料来源,请查看 Think Stats 以获得更长时间的治疗。参见这篇文章中关于统计学的简短介绍。如果你需要线性代数——查看这篇文章获得矩阵世界的快速指南。
然而,如果你刚刚开始,你可能会想在这些基础知识上花更多的时间,最好的办法是在你当地的大学上课(作为一个免费的读者/听众),或者跟随一所知名大学的课程。例如,点击这里查看麻省理工学院关于线性代数的讲座。
数据科学课程
如果你想完成机器学习和数据科学的课程,你可能听说过的第一个课程是吴恩达的课程:
- 吴恩达在 Coursera 上发表的《机器学习》——它涵盖了机器学习的基本概念,更理论化(涉及的编码更少),是一个很好的知识来源。然而,如果你真的刚刚开始,你应该检查其他来源。
因此,以下是您在开始数据科学探索时应该学习的更多基础课程的列表:
- 哈佛数据科学课程——带你了解基础知识:熊猫、刮削、回归到机器学习。
- 密歇根大学用 Python 介绍数据科学。评价很高,基本介绍。它在 Coursera 上,所以如果你不需要证书,它是免费的。
- 深度学习(Deep Learning)——尽管这远非入门课程,但这是另一门来自吴恩达的课程,你至少应该听说过。
- IBM 数据科学——Coursera 上目前最受欢迎的数据科学课程之一。它由 IBM 提供,提供了从零开始进入数据科学的完整入口。如果你想获得一个完成课程的证书,那就更好了。
- Dataquest —成为数据科学家的另一个完整来源——尤其是如果你通过这条途径。我自己从未服用过,但听到了一些非常好的意见。
- Datacamp —是一个通过编码学习数据科学的独特平台。互动练习将指导您浏览材料。
- 数据科学工作 —我的课程侧重于启动你的数据科学职业生涯,并找到你的第一份数据科学工作。它有越来越多的学生和实时材料——我定期给课程添加新的视频。
数据科学书籍
我总是试着大量阅读,下面我列出了一些我最喜欢的书。更完整的列表见本文。

从头开始的数据科学:Python 的基本原理 —这可能是我读过的第一本关于数据科学的书。它实际上是从基础开始的,不需要任何 Python 知识。非常适合绝对初学者!

Python 数据科学手册:处理数据的基本工具——这是一本更高级的书,将引导你了解不同的数据科学技术。

Python 机器学习简介:数据科学家指南 —另一本介绍性书籍,这次更侧重于机器学习。

Data Science Job
最后,如果你想了解成为一名数据科学家意味着什么,那么看看我的书数据科学工作:如何成为一名数据科学家,它将指导你完成这个过程。
数据科学材料
因此,如果你读过我的其他文章,你可能知道我是 GitHub 的超级粉丝。像几乎所有人一样。学习数据科学的一个非常好的来源是浏览 GitHub 的无数开源存储库。
这里列出了一些最受欢迎的。
- 趋势库。这是发现新事物的一般方法。
- 约翰·霍普斯金大学数据科学专业。
- 斯坦福张量流教程
- 数据科学备忘单
- 数据科学自学知识库
暂时就这样吧!
如果你想让我介绍数据科学和机器学习的其他伟大资源,请告诉我。
如果您想了解更多关于成为数据科学家的信息,请参阅以下文本:
让我们保持联系,继续学习数据科学。](https://creative-producer-9423.ck.page/c3b56f080d)
企业聊天机器人开发完全指南

客户服务是新的营销方式。当今的顾客对信息了如指掌。企业总是在寻找机会,以确保他们建立了一个防水的客户支持流程,并有适当的系统。如今,主要品牌和企业都在寻求 bot 开发计划,以便以更高的效率和成本效益接触客户。
谈到扩大客户支持,荷兰皇家航空公司每周处理超过 16,000 次互动,在 6 个月内,蓝色机器人向 500,000 多名客户发送了近 200 万条信息。
报告显示,接近的 37%的美国人更喜欢在紧急情况下使用聊天机器人获得快速回答。除此之外,高达 64%的美国人认为机器人 24 小时可用是最好的功能。
关于商业盈利能力的其他统计数据显示,47%的消费者将通过聊天机器人进行购买,千禧一代(26 至 36 岁)准备在通过机器人进行的商业交易中花费 481.15 英镑。
到目前为止,采用聊天机器人的企业都是通过在孤岛中创建和使用聊天机器人来实现的。虽然这种方法可能适用于需要自动化少量任务的企业,但它并不完全符合企业的高端需求——跨功能大杂烩的可伸缩性、敏捷性和成本效益。
现代企业应该如何发展聊天机器人?
对于企业来说,聊天机器人应该随时可用,可以通过各种渠道访问,并与内部业务系统集成,客户关系管理(CRM)和供应链管理(SCM)系统是重中之重。
当提出 bot 发展战略时,企业有几种选择。对于需要自动化工作流以及内部和外部生态系统的集成和自然语言处理应用程序的企业来说,单一任务机器人不是一个可行的选择。
聊天机器人框架帮助程序员构建个人聊天机器人的结构。然而,这些框架仅仅是一组工具和服务的集合。这些框架适用于一组固定的用例,并可用于组装和部署单任务机器人,最终,该机器人缺乏端到端的开发和持续管理能力。
如果用例很小,框架往往是有用的,但是,对于整体需求和范围要求更高的企业来说,这就是聊天机器人平台的用武之地。
当谈到聊天机器人架构时,企业在谈到他们的聊天机器人开发平台时应该确定以下要求

执行多项任务的多种聊天机器人
该功能对于企业来说是必不可少的,因为它允许企业同时跟踪和简化多个功能。理想情况下,企业应该能够部署执行单一任务的聊天机器人,同时创建和部署多用途聊天机器人,与多个系统通信,并在每个系统中完成各种任务。
聊天机器人开发平台应提供预构建和随时部署的机器人,以解决某些用例(例如,线索生成、客户支持等)。)以及定制它们以满足您的业务需求的能力,以便处理与不同客户交互和您的业务产品相关的多个不同工作流和流程(例如,还回答客户的查询并在 FAQ、文档或网站中回答的线索生成机器人)。
多通道支持
企业应该寻找聊天机器人开发平台,可以将机器人部署到网站、移动应用程序或其选择的渠道,并为每个渠道定制用户界面,无论是短信、电子邮件还是社交媒体。此外,机器人应该有能力与 Slack、丨t丨e丨l丨e丨g丨r丨a丨m丨s丨、Skype 等公司工具进行交互。
自然语言处理和语音支持
当谈到聊天机器人的可伸缩性时,训练聊天机器人是另一个重要的考虑因素。您的聊天机器人开发平台是否包含自然语言处理(NLP) 培训?机器人可以使用文本和/或语音保持准确的交互和对话吗?提供 NLP 和语音支持的聊天机器人平台在理解用户意图并在评估后回复相关内容时往往会提供最佳结果。
通过平台部署智能聊天机器人
该平台应该拥有智能聊天机器人,能够理解、回忆并不断学习从每次客户互动中收集的数据和信息。这还包括需要在交互过程中维护客户请求的上下文,并使用机器学习来进一步开发和完善其自然语言处理能力。
与平台桥接的能力
该平台是否有能力在用户、机器人和跨功能系统之间共享消息?这将包括共享存储在用户、机器人和系统之间的消息,以及成功和失败消息的分类。这为 chatbot 开发平台以及随后的 bot 的功能提供了全面而清晰的描述。
打造聊天机器人
该平台应该有一个直观的、基于网络的工具,用于根据聊天机器人的用例、任务和部署渠道来设计、构建和定制聊天机器人。它还应该可以选择重新开始从零开始开发机器人的过程,或者在整个开发周期中测试聊天机器人构建的同时重用已开发的组件。
行业经验和领域知识
确定并接洽拥有丰富行业经验和领域知识的合适技术和平台提供商。
企业需要考虑并真正确定什么样的聊天机器人开发平台或相关框架将增强和促进速度、可伸缩性和灵活性,以支持他们的客户和员工。
聊天机器人开发成功的关键要求

聊天机器人不仅依赖技术,还依赖内容
聊天机器人的最终目标是实现高质量的客户体验和服务人员协助。聊天机器人最引人注目的元素显然是技术。然而,内容对它的成功也起着重要的作用。创建知识资产是一项值得关注的投资,但在人工智能(AI)等高端技术组件的阴影下,内容创建和管理的重要作用往往被掩盖了。
内容再利用和再利用
聊天机器人需要的大多数内容已经被客户服务团队创建并广泛使用,无论是在电话交谈、网络聊天、在线或社交媒体消息还是电子邮件中。改变现有内容的用途是聊天机器人可以理解的格式,并且可以在请求触发时由聊天机器人提供服务。
目标应该是一次生成内容,然后重用。要做到这一点,需要管理内容的人(可以是你的服务团队)和数字营销部门之间的紧密联系。
分析客户联系/接触点,创建相关内容
起点是构建能产生最大效果的内容——内容能降低打电话的动机,通话量也更高。要了解人们为什么会与您的企业建立联系,需要进行更深入的分析,这种分析可以通过评估谁在打电话(打电话的目的、时间和原因)、细分整个客户旅程、跨渠道的联系测试以及客户调查、在线和离线对话以及电子邮件内容等研究活动来实现。
创建一个渠道金字塔并翻转它以便迁移
创建渠道金字塔时,最顶端的部分是聊天机器人。一开始,这些只会解决整体查询的一小部分,作为一个初步步骤或在线 IVR,并引导客户到更适合管理互动的数字团队。
数字团队将覆盖网络聊天和消息平台作为中心层,帮助迁移到数字对话,与金字塔绝对底部的语音相比,数字对话的成本更低,后者需要最大的成本和更高的容量渠道。
使用所有执行的分析和适用在线内容的生成,我们可以使用聊天机器人处理越来越复杂的查询来翻转金字塔,最终目标是他们将很快管理大多数案例。
通过引导和让有经验的代理参与来降低风险
聊天机器人不是容易开发的技术工具,因此剥夺体验的风险是一个人必须考虑的因素。一些形式的人工智能可以通过内部团队进行测试,而不是直接接触客户或潜在客户。谨慎的做法是将这些试验放在一个有联络中心支持的创新中心。这个联络中心将有精确的技能组合,使其可行,并在以后实时尝试。
如何用聊天机器人开发公司制作聊天机器人?
在开始任何项目之前,描述目标、路线图以及量化成功的方法是至关重要的。
在马鲁蒂科技实验室,作为一家聊天机器人开发公司,我们使用敏捷方法,我们的程序受到看板、XP 和 Scrum 的影响。我们为不同的项目和客户定制我们的流程。
我们的流程是协作的、透明的、以用户为中心的和迭代的。敏捷有助于我们降低整体项目风险,处理变更并最大化客户价值。
聊天机器人的开发过程是如何进行的?
每个项目都从发现阶段开始,并通过迭代开发周期进行。
每次冲刺或周期为 2 到 4 周。
- 发现
发现阶段在聊天机器人开发项目开始时进行。它主要由需求收集研讨会、利益相关者访谈和关键最终用户需求分析组成。backlog 是这个阶段的主要输出,公认的需求被写成“用户故事”。识别他们正在寻找的用例及类型(基于顺序 bot 或 NLP)有助于列出聊天机器人要执行的各种意图和动作。从最终用户的角度发现需求会引导项目团队深入探索产品特性。
例如,选择精确的消息平台(Facebook Messenger、Slack 或您的网站)是发现阶段的关键点。最终,偏好应该根据你的目标受众而定,你需要在他们所在的地方满足他们。
我们使用精确的工具来分析需求,并为问题跟踪管理待办事项。来自客户方的产品成员负责对待办事项阶段进行优先级排序。backlog 是一个项目执行文档,可以在整个项目中随时修改。
- 计划
在我们了解机器人的个性之前,重要的是创建用户旅程,以及为我们的客户授权的对话设计流程。这可以看作是可视化 UI 项目中用户旅程映射和线框的对应部分。
对于对话流,问自己以下问题:
- 机器人必须解决或执行哪些查询才能将访问者引导到所需的端点?
- 访问者需要提供哪些细节才能得到最好的回复?
- 达到最终目标的所有步骤是什么?
为了达到和满足用户的目的所需的对话或交互的数量是至关重要的。尽管有无数种技术可以帮助实现机器人的个性,但最好也是最简单的方法是概念化和开发人工智能聊天机器人的角色,这样它就是一个真实的人,通过响应用户的查询来完成自己的工作,并有自己的一套怪癖。
回到 sprint,在每次 sprint 开始时,我们都会进行一次“Sprint Planning”会议。整个团队都参加了 Sprint 计划会议,在这个会议上,我们讨论了待办事项中最重要的优先事项,详细陈述了它们,计划了任务并对它们进行了评估。每个用户故事都有一个被认为是完整的批准标准。
我们通过利用速度指标来评估和分析在一次冲刺中可以完成多少个点,同时部署技术来平均所有冲刺的先例性能,并提高我们对时间的近似精度。

- 打造
我们打算构建并交付一个可供实际用户使用的工作产品。这个想法是,然后迭代这个产品,积累更多的功能,直到我们开始满足我们在初始阶段设定的目标。
从内部功能开始,然后深入到聊天机器人的个性,机器人脚本是累积编码和开发的。
对话设计包括训练自然语言处理器了解你的用户意图,然后随着更多的用户数据变得可获得而放大这些意图。
- 测试和审查
如果你正在为客户开发一个机器人,建立 UATs 是至关重要的:用户验收测试。这是一个描述对话流特征的对话集。编写多样化的对话:一种是在机器人处理错误的用户输入时,另一种是在用户不讨论机器人的用例时。保证所有的对话都是实用的!
在每次冲刺的最后,我们都会进行一次演示。我们遍历所有创建的用户故事,并尝试显示其实现过程。来自客户团队的产品负责人根据确定的标准审查并决定是否允许实现。
如果用户故事被 100%接受,那么它的状态就是完成。
- 技术
我们正在不断改进我们的技术产品,并开发了一个满足跨领域客户需求的聊天机器人平台。此外,我们还提供:
- 支持关键信使应用程序和短信。
- 一个强大的引擎,支持多方面的对话工作流。
- NLP 集成。
- RESTful API,可与任何所需的 web 服务快速集成。
- 我们可以为您托管和维护的完全托管的解决方案。
结论
聊天机器人与其他应用程序并不完全不同;您有多个支持应用程序的集成,涉及所有不同的动态。鉴于聊天机器人需要与最终用户快速互动,因此需要明确不同集成所提供的信息是对对话至关重要,还是可以推迟到以后。通过 API 集成的一致性不仅有助于敏捷性,还有助于创建完美的对话。
如今的客户比以往任何时候都更加坚持,期望值更高,容忍度更低。不要让您的客户或最终用户通过冗长的表格或流程来获得他们想要的东西,而是以一个问题开始他们的旅程——“今天我能为您做些什么?”。
合适的聊天机器人平台或聊天机器人解决方案可以帮助您满足这些期望,但是,每个企业都没有时间或资源来提出适合其核心业务的解决方案——在短短几周时间内完成培训和部署。
在马鲁蒂科技实验室,我们帮助商业和企业在众多行业部署可扩展的聊天机器人解决方案,包括金融科技、保险、房地产、消费品、电子商务和酒店等。立即联系我们,部署符合您业务特定需求的机器人,同时匹配您品牌的声音和风格。
管理人员机器学习项目结构完整指南
构建您的机器学习项目,以创造切实的成果并提高团队的效率

Photo by Samuel Zeller on Unsplash
AI 现在在每个人的脑海里。老牌公司正在自我瓦解,并朝着数据驱动型组织的方向艰难缓慢地转变,而初创公司需要实施清晰高效的数据战略才能保持相关性。
尽管拥有一个数据策略的必要性现在被大大小小的公司广泛接受,但一个共同的挑战仍然存在:如何构建和管理一个机器学习项目?
这篇文章提供了一个框架来帮助你管理一个机器学习项目。当然,你必须使它适应你公司的特定需求,但是它会把你引向正确的方向。
关于机器学习、深度学习和人工智能的实践视频教程,请查看我的 YouTube 频道。
为什么我需要人工智能策略?
当然,我们需要从为什么说起。为什么在公司内部制定人工智能战略很重要?
机器学习项目的问题在于,有很多方法可以提高模型的性能:
- 收集更多数据
- 对算法进行更长时间的训练
- 改变模型的架构
- 获得更多样化的训练组合
然而,采用错误的策略会导致时间和金钱的重大损失。你可能花了六个月的时间收集更多的数据用于训练,却发现它几乎没有改进你的模型。类似地,你可以盲目地训练你的模型更长时间(并支付额外的计算时间),但看不到任何改进。
因此,定义良好的人工智能策略非常重要。这将有助于提高团队的效率,增加你的人工智能项目的投资回报率。
正交化
最有效的机器学习实践者清楚地知道调整什么以获得更好的结果。
正交化是指拥有一个具有非常特定功能的控件。
例如,办公椅有一个杠杆使其上下移动,而椅子上的轮子使其水平移动。在这种情况下,杠杆是一个控制装置,具有升高和降低椅子的功能。轮子形成控制,具有水平移动椅子的功能。
因此,这些控制被称为正交:用轮子滚动椅子不会降低它,就像拉动椅子上的杠杆不会使它向后移动一样。
同样的概念也必须适用于机器学习项目。对项目的单个修改必须对单个方面产生影响。否则,您将在一个领域提高,但在另一个领域降低性能,项目将停滞不前。
这如何转化为人工智能项目?
首先,我们必须考虑机器学习中的假设链。
机器学习中的假设链
假设如果模型在训练集上表现良好,那么它将在开发集上表现良好,然后在测试集上表现良好,然后在现实世界中表现良好。
这是人工智能项目中相当常见的假设列表。现在,在这些情况下,哪个模型表现不佳?
- 训练集:训练一个更大的网络或改变优化算法
- 开发集:使用正则化或更大的训练集
- 测试集:使用更大的开发集
- 真实世界:改变 dev 集分布(稍后将详细介绍)或改变成本函数
上面的列表给出了明确的正交控制,以在非常具体的情况下改进模型。一旦你的模型在一套中表现良好,继续在另一套中改进它。
现在,你如何知道你的模型是否表现良好?
设定目标
如上所述,您需要一个明确的目标来确定一个模型是否表现良好。因此,设置评估指标以及满足和优化指标非常重要。
单一数字评估指标
具有单一评估度量允许更快地评估算法。
例如,分类器通常使用精度和召回率。然而,这两个指标之间存在权衡。相反,使用 F1 分数,它是精确度和召回率的调和平均值。因此,使用了单一的度量标准,评估不同模型的质量变得更加容易,并且加速了迭代。
满足和优化指标
一旦您有了一个单一的评估指标,通常会跟踪其他重要的指标。
例如,您可能希望构建一个 F1 值至少为 0.90 且运行时间少于 200 毫秒的分类器。在这种情况下,F1 分数是优化指标,而运行时间是满意度指标。
优化指标通常与您的评估指标相同,并且您应该只有一个优化指标。其他感兴趣的指标将是令人满意的指标,并且将帮助您选择满足优化指标的总体最佳模型。
培训、开发和测试集
上面提到了培训、开发和测试集,但是它们到底是什么呢?
定型集和开发集(或维持集)用于定型模型。训练集通常用于使模型符合数据,而开发集用于进行预测和调整模型。
然后,测试集是真实数据的一个示例,您可以在其上测试算法以了解它的执行情况。
培训/开发/测试分布
一旦您有了不同的数据集,您必须确保该分布能够代表您期望在未来获得的数据。
例如,如果您希望创建一个模型来标记来自移动上传的图像,那么在来自互联网的高分辨率图像上训练该模型是没有意义的。手机上传的照片可能分辨率较低,照片可能会模糊,并且对象可能不会完全居中。因此,培训/开发/测试集应该包含这种类型的图像。
此外,您希望每个集合来自同一个分布。例如,您正在构建一个模型来预测客户流失,而您的数据集的 6%包含流失的实例。然后,您的培训、开发和测试集也应该有大约 6%的数据作为流失实例。
培训/开发/测试规模
每套应该有多大?
通常情况下,培训/开发/测试集的划分分别是 60/20/20。如果数据不是非常丰富,这仍然有效。
但是,在您有数百万个实例的情况下,更合适的划分应该是 98/1/1,因为该模型仍然可以在超过 10 000 个数据点上进行验证。
与人类水平的性能相比
最近,我们开始看到人工智能系统优于人类或非常接近人类表现的头条新闻。
不幸的是,人类非常擅长许多任务,很难让人工智能系统接近我们的性能。需要大量的数据,模型的性能最终会趋于平稳,很难提高。
但是,我如何改进一个模型呢?
如果您的模型过度拟合,您必须通过以下方式减少方差:
- 收集更多数据
- 正规化 (L2、辍学、数据扩充)
- 改变模式
如果您的模型对数据拟合不足,您必须通过以下方式减少偏差:
- 训练更大或更复杂的模型
- 使用更好的优化算法或训练更长的时间
- 改变模式
如果上述方法都没有显著影响,那么下一步就是获得由人类标记的数据。尽管代价高昂且困难重重,但这一步将使您的模型尽可能接近人类水平的性能。
临终遗言
构建人工智能系统是一个迭代的过程。快速构建、测试和改进非常重要。不要一开始就试图构建一个非常复杂的系统,但是也不要构建太简单的系统。
我希望这能帮助你更好地管理和规划你的 AI 项目。人工智能在许多行业都有巨大的潜力,抓住这个机会非常重要。拥有一个清晰的人工智能策略将有助于你冲浪,而不是被吞没。
以极快的速度运行复杂逻辑:试试 Julia 的数据科学
我们展示了 Julia 与同等 Python 代码的性能对比基准,以说明为什么 Julia 非常适合数据科学和机器学习。

注意:我正在用 Julia fundamentals 和数据科学示例构建 Github repo。 查看这里 。
介绍
“像 Python 一样走路,像 C 一样运行” —这是对 Julia 的评价,Julia 是一种现代编程语言,专注于科学计算,,拥有越来越多的追随者和开发者。
Julia 是一种通用编程语言,是专门为科学计算而设计的。它是一种 灵活的 动态类型化的 语言,其性能堪比传统的静态类型化语言。
Julia 试图为原型开发提供一个足够高效的单一环境,并为工业级应用程序提供一个高效的环境。它是一种多范例语言,包含了功能性和面向对象的编程组件,尽管大多数用户喜欢它的功能性编程方面。
这种编程语言的诞生可以追溯到 2009 年。首席开发人员 Alan Edelman、Jeff Bezanson、Stefan Karpinski 和 Viral Shah 开始致力于创建一种可用于更好更快数值计算的语言。开发者能够在 2012 年 2 月发布商业版本。
为什么对数据科学来说很牛逼?

Julia 是数据科学和机器学习工作的绝佳选择,出于同样的原因,它也是快速数值计算的绝佳选择。优点包括,
- 平滑的学习曲线,广泛的底层功能。特别是,如果你已经熟悉了更流行的数据科学语言,比如 Python 和 R,那么接 Julia 就像在公园散步一样简单。
- 性能:本来 Julia 是编译型语言,Python 和 R 是解释型。这意味着 Julia 代码作为直接可执行代码在处理器上执行。
- GPU 支持:直接关系到性能。GPU 支持由
TensorFlow.jl和MXNet.jl等一些包透明控制。 - 分布式和并行计算支持 : Julia 使用许多拓扑透明地支持并行和分布式计算。此外,它还支持协同程序,如 Go 编程语言中的协同程序,这是在多核架构上并行工作的辅助函数。对线程和同步的广泛支持主要是为了最大限度地提高性能和降低竞争条件的风险。
- 丰富的数据科学和可视化库 : Julia 社区认为它是数据科学家和统计学家的必备语言。因此,专注于数据科学和分析的高性能库一直在发展中。
- 团队合作(与其他语言/框架) : Julia 在数据科学和机器学习方面与其他已建立的语言和框架合作得非常非常好。使用
PyCall或RCall可以在 Julia 脚本中使用原生 Python 或 R 代码。Plots包可以和各种后端一起工作,包括Matplotlib和Plotly。像Scikit-learn或TensorFlow这样的流行机器学习库已经有了 Julia 等价物或包装器。
Julia 是数据科学和机器学习工作的绝佳选择,出于同样的原因,它也是快速数值计算的绝佳选择。
Python 脚本的一些基准测试
关于这个问题有很多争议:“Julia 比 Python 快吗?
就像生活中几乎任何事情一样,答案是: 看情况。
官方的 Julia 语言门户有一些关于它的数据,尽管基准测试是针对除 Python 之外的各种语言进行的。
这些微基准测试虽然不全面,但确实测试了一系列常见代码模式的编译器性能,例如…
julialang.org](https://julialang.org/benchmarks/)
事实上,这个问题几乎总是假设人们在谈论 Julia 和某种优化/矢量化 Python 代码(如 Numpy 函数所使用的)之间的比较。否则,由于编译代码的执行,原生 Julia 几乎总是比 Python 快,而原生 Python 比 Numpy 类型的执行慢得多。
Numpy 真的很快。它是一个具有超级优化函数的库(其中许多是预编译的),致力于为 Python 用户(对数据科学家和 ML 工程师特别有用)提供接近 C 语言的速度。简单的 Numpy 函数,如 sum 或标准差可以接近或超过等效的 Julia 实现(特别是对于大输入数组大小的)。
然而,为了充分利用 Numpy 函数,您必须考虑代码的矢量化。并且在程序中始终以向量化代码的形式编写复杂的逻辑一点也不容易。
因此,与 Julia 的速度比较应该在对数组应用某种复杂逻辑进行某种处理的情况下进行。
在本文中,我们将展示几个这样的例子来说明这一点。
然而,为了充分利用 Numpy 函数,您必须考虑代码的矢量化
Julia for-loop 漂亮地打败了 Python for-loop
让我们计算一百万个随机整数的总和来验证这一点。
下面是朱莉娅的代码。该功能需要 1 毫秒多一点的时间。

Python 代码在下面。我们保留了代码的相同函数性质(Julia 是一种函数式语言),以保持比较的公平和易于验证。for 循环花费超过 200 毫秒!

但是 Julia 数组和 Numpy 数组相比如何呢?
在上面的代码中,我们创建了一个数组变量。这是 Julia 中对数据科学最有用的数据结构,因为它可以直接用于统计计算或线性代数运算,开箱即用。
不需要单独的图书馆什么的。Julia 数组比 Python 列表快一个数量级。
但是,Numpy 数组速度很快,让我们对相同的求和操作进行基准测试。
下面的 Julia 代码使用了数组上的sum()函数。它需要 451 微秒(比 for-loop 方法快,但时间只有一半)。

这是 Numpy 执行,

哇!353 usec 超过了 Julia 的速度,比原始 Python for-loop 代码快了近 628 倍。
那么,判决结果是有利于 Numpy 数组吗?
没那么快。如果我们只想对数组中的奇数求和呢?
不需要单独的库。Julia 数组比 Python 列表快一个数量级。
逻辑来了
对于 Julia 来说,代码更改相当简单。我们将只使用 for 循环,检查数组中的一个元素是否能被 2 整除,如果不能(奇数),则将它添加到运行总和中。太迂腐了!

因此,这在接近 4 毫秒内运行。当然比盲求和(使用 for 循环)慢,但不会太多(for 循环的普通求和也是 1.1 毫秒)。
现在,我们肯定无法用 Python for-loop 来竞争这个速度!我们知道结果会怎样,不是吗?因此,我们必须使用 Numpy 矢量化代码。
但是在 Numpy 数组的情况下,我们如何检查奇数,然后对它们求和呢?幸运的是,我们有np.where()方法。
下面是 Python 代码。没那么简单(除非你知道如何正确使用np.where(),不是吗?

但是看速度。即使是使用 Numpy 方法的单行矢量化代码,平均也要花费 16.7 毫秒。
Julia 代码更简单,运行速度更快!
另一个稍微复杂的操作
假设我们有三个数组(比如 W 、 X 和 B ),它们的随机浮点数范围为-2 到 2,我们想计算一个特殊的量:这两个数组的乘积,加上第三个数组,即。X+B但是只有当元素间的线性组合超过零 时,数量才会被加到最终的总和上。
这个逻辑你看着眼熟吗?它是任何密集连接的神经网络(甚至是单个感知器)的变体,其中权重、特征和偏置向量的线性组合必须超过某个阈值才能传播到下一层。
这是朱莉娅密码。还是那句话,简单又甜蜜。用了~ 1.8 毫秒。注意,它使用了一个叫做muladd()的特殊函数,将两个数字相乘,然后加到第三个数字上。

我们使用类似的代码(使用 for-loop)尝试了 Python,结果很糟糕,正如所料!平均花了一秒多的时间。

我们再次尝试发挥创造力,使用 Numpy 向量化代码,结果比 for-loop 情况好得多,但比 Julia 情况差~ 14.9 毫秒。

那么,它看起来怎么样?
在这一点上,趋势变得清晰了。对于数字运算,在一些数学运算发生之前需要检查复杂的逻辑,Julia 轻而易举地击败了 Python(甚至 Numpy ),因为我们可以在 Julia 中用最简单的代码编写逻辑,然后忘记它。由于实时(JIT)编译器和内部与类型相关的优化(Julia 有一个极其精细的类型系统来使程序快速运行,为每个变量提供正确的数据类型,并相应地优化代码和内存),它仍将以极快的速度运行。
使用原生 Python 数据结构和 for-loop 编写相同的代码非常慢。即使使用 Numpy 矢量化代码,随着复杂性的增加,速度也比 Julia 慢。
Numpy 非常适合数组自带的简单方法,比如sum()或mean()或std(),但是使用逻辑并不总是简单明了的,它会大大降低操作速度。
在 Julia 中,努力向量化你的代码并不令人头痛。即使是一个看起来很愚蠢的代码,使用普通的 for 循环和元素间的逻辑检查,运行速度也惊人的快!
对于数字运算,在一些数学运算发生之前需要检查复杂的逻辑,Julia 轻而易举地击败了 Python(甚至 Numpy ),因为我们可以在 Julia 中用最简单的代码编写逻辑,然后忘记它。
摘要
在本文中,我们展示了 Julia 和 Python 之间数值计算的一些比较基准——包括原生 Python 代码和优化的 Numpy 函数。
虽然在简单函数的情况下,Numpy 在速度上与 Julia 不相上下,但在计算问题中引入复杂逻辑时,Julia 的得分更高。Julia 代码本质上很容易编写,不需要努力考虑向量化函数。
随着数据科学和机器学习支持系统的不断发展,Julia 是未来几天最令人兴奋的新语言之一。这是一种工具,初露头角的数据科学家应该把它加入他们的技能清单。
我正在用 Julia fundamentals 和数据科学示例构建 Github repo。 查看这里 。
附加阅读
- https://docs . Julia lang . org/en/v1/manual/performance-tips/# man-performance-tips-1
- https://agile scientific . com/blog/2014/9/4/Julia-in-a-null . html
- *【https://en.wikibooks.org/wiki/Introducing_Julia/Types *
- https://dev.to/epogrebnyak/julialang-and-surprises 用一种新的编程语言学习什么
如果您有任何问题或想法要分享,请通过tirthajyoti【AT】Gmail . com联系作者。此外,您可以查看作者的 GitHub 知识库中的代码、思想和机器学习和数据科学方面的资源。如果你像我一样,对人工智能/机器学习/数据科学充满热情,请随时在 LinkedIn 上添加我,或者在 Twitter 上关注我。
* [## Tirthajyoti Sarkar - Sr .首席工程师-半导体、人工智能、机器学习- ON…
通过写作使数据科学/ML 概念易于理解:https://medium.com/@tirthajyoti 开源和有趣…
www.linkedin.com](https://www.linkedin.com/in/tirthajyoti-sarkar-2127aa7/)*
SQL 中的复杂查询

Hopefully your SQL queries aren’t tangled like this. (Photo:Author)
SQL 教科书擅长提供 SQL 语法的基本模板,但有时这些书中的查询与现实生活相比有点理想化,只有不超过两三个要连接的表和少量要处理的字段。因此,查询本身通常不超过十几行。
在野外经常会发生这样的情况,你最终会在很多表格中加入冗长的“WHERE”子句,这些子句很容易被遗忘。当你第一次使用它们的时候,它们很难使用,但是如果你需要重用它们,调试和理解它们是不可能的。怎样做才能使又长又复杂的查询更容易编写和阅读?
但是,首先,问问自己是否需要一个长而复杂的查询。您的单个查询完成所有工作是否至关重要?如果你还认为是,至少多问自己两遍。在优秀的 SQL 反模式 中,使用单个查询来解决一个复杂任务或多个任务被认为是一个关键的查询反模式,强调了它的地位是需要警惕的。这本书指出的一个特别的危险是,随着查询变得更加难以理解,意外进行笛卡尔连接(一种返回的行数是连接中涉及的表中行数的乘积的连接)的风险。相比之下,内连接或外连接中的最大行数将是最大表中的行数),返回许多不需要的行需要很长时间,这将大大增加。
因此,关键的建议是检查大型查询是否可以用多个更容易理解的小型查询来执行。
如果您认为您正在编写的复杂查询是完全不可避免的,有几种方法可以使它变得不那么痛苦。
- 指定查询中的列名;不要落入“选择*”陷阱。没有指定要返回哪些列的复杂查询很可能会返回不需要的列,从而使查找所需信息变得更加困难。此外,返回冗余列的可能性很大。
- 请仔细格式化您的查询,使其尽可能易于他人阅读。将每个要选择的字段放在自己的行上,将每个新连接的表放在自己的行上,并将“WHERE”子句的每个元素放在自己的行上。
- 为表格使用有意义的别名,以进一步提高可读性。您可能需要在查询中多次引用您的表,如果您在其他人的数据库中使用他们的命名约定,表名可能包含关于他们引用的冗余信息,例如“MyDBTable1”,因此您将这样的表称为“T1”,因为您已经知道您正在“MyDB”中工作
- 创建一个冗余的“WHERE”子句,例如“WHERE 0=0 ”,以便轻松测试 Where 子句的不同部分(h/t 前同事 Mark Watson)。也就是说,因为“WHERE 0=0”自动位于“WHERE”子句的开头,所以您可以打开和关闭它的其余部分(例如将其注释掉),而不会出现语法错误来检查效果。
- 计划您的查询,可能在纸上画一个草图,以便您在开始将数据转换为代码之前,知道您试图在数据的位置以及查询中的表如何相互关联方面实现什么。
采取这些步骤的最终结果是您的代码看起来有点像这样:
SELECT
Var1
, Var2
...
, VarNFROM
Table1 T1
JOIN Table2 T2 on T1.Var1=T2.Var1
JOIN Table3 T3 on T1.Var1=T3.Var1
WHERE 0=0
AND T1.Var1 > 0
总的来说,最好有更多的小查询,而不是更少的大查询。然而,一些数据库结构使这变得困难,例如,如果您需要联接以获得从表 b 中找到您真正需要的内容的辅键,在这些情况下,计划和仔细的格式化可以帮助避免您的代码变成意大利面条。
罗伯特·德格拉夫已经为数据科学家写了许多关于 SQL 的文章,包括最近的,【SQL For Missing Values】。
罗伯特·德格拉夫的书《管理你的数据科学项目》已经通过出版社出版。
Excel 电子表格上的复杂 SQL
研究联邦佩尔助学金数据的趋势

Spreadsheets to answers, using SQL.
2016 年,印第安纳州的佩尔助学金减少了 2 亿美元。为什么?答案就在教育部的电子表格数据中。我们可以用 SQL 提取出来。
世界上的大量数据同样被锁在电子表格中。像 Excel 这样的工具被广泛使用,并且非常适合查看数据。然而,随着现代数据变得越来越大,越来越相互关联,执行复杂的分析可能会变得很困难——难以理解的长公式,与数据框架的争论,甚至清理崇高文本中的 CSV(我曾经遇到过…)。
一定有更好的办法。在本帖中,我们将展示如何使用一个比 Excel 更古老的工具来应对这一挑战:SQL。
这些问题
美国大学的可负担性(或不可负担性)是今日新闻中的热点话题。学生经济资助的最大来源之一是联邦佩尔助学金计划,所以我们寻找量化该计划影响的数据。
假设我们想回答以下问题:
- 2016-2017 年,纽约哪个机构的学生获得的佩尔奖金最多?
- 对于每个州,从 2015-2016 年到 2016-2017 年,获得佩尔奖的学生人数的百分比变化是多少?
- 一个机构的平均资助规模与它是全男性还是全女性有什么关系?
快速搜索显示,按机构列出的佩尔助学金发放数据可以在教育部网站上公开获得。XLSX 文件(Excel 电子表格)。这将是我们调查的起点。
加载数据
让我们将支出数据转移到 Rockset 中,这是一个可以解析和运行半结构化数据 SQL 查询的服务,包括我们案例中的 Excel 文件。在 Rockset 控制台中,我们创建一个新的空集合,并将其命名为pell_disbursements。

接下来,我们从 DoE 下载 Excel 文件,并将其直接上传到 Rockset 控制台。

Rockset 将对文件内容进行索引,以便随时可以查询。
寻找数据点
让我们从了解数据的形状开始。我们使用DESCRIBE命令列出集合中的可用字段:

We will run all our queries in the browser in the Rockset console.
我们看到 Rockset 已经解析了电子表格中从 B 到 H 的列,以及有用的元数据,包括:
rownum-原始电子表格中的行_meta.file_upload.file-原始文件的名称
Rockset 中的每个文档都对应于电子表格中的一行,所以让我们查询前几行来辨别每一列的含义:
我们看到第五行中的每一列都有标题,原始数据从第六行开始。使用一个WITH子句,让我们构造一个名为pd的子查询,它只返回我们关心的数据:
这很令人兴奋——我们可以使用 SQL 的全部功能来梳理这些数据!让我们编写一个查询来找到我们第一个问题的答案,纽约获奖最多的机构:
我们看到,排名第一的机构是曼哈顿区社区学院,该学院获得佩尔奖学金约 7600 万美元,明显高于排名第二的机构。
计算聚合统计数据
让我们引入 2015-2016 年的数据。我们下载那一年的数据并上传。XLSX 文件放入同一个pell_disbursements收藏中。
我们使用与上面相同的步骤,这次按文件名过滤,为 2015-2016 年的数据构造另一个子查询。注意,第二个文件的格式略有不同(行偏移量、列标题和 OPE ID 列中的数据类型),因此我们必须适当地调整我们的查询。
接下来,我们可以将每个机构两年的数据进行匹配,以计算 Pell 接受者人数的逐年变化。我们按照州和从少到多的顺序进行汇总:
我们可以看到,除了三个州之外,所有其他州接受佩尔助学金的人数实际上都减少了(尽管其中的两个州,华盛顿特区和马绍尔群岛,从技术上讲不是州)。正如 CollegeBoard 在图[中指出的,这是一个更大趋势的一部分。到目前为止,印第安纳州是个例外,接收人数下降了 27.7%。
让我们在地图上看看这个数据。我们可以使用来自 amCharts 的](https://trends.collegeboard.org/student-aid/figures-tables/undergraduate-enrollment-and-percentage-receiving-pell-grants-over-time)这个现成的美国热图演示。为了匹配 amCharts 期望的数据格式,我们重命名列,然后将结果导出为 JSON:

接下来,我们将 JSON 直接粘贴到演示代码中,并立即生成一个交互式热图!
Run this pen to see the heat map.
我们直观地观察了犹他州附近和南部腹地的一些州,它们保留了佩尔学生人数,并再次看到印第安纳州确实是佩尔学生流失的异常值。
当年印第安纳发生了什么?为什么会有如此巨大的变化?让我们开始吃吧。我们列出了印第安纳州各机构报告的接受佩尔助学金的学生人数的变化:
这就是症结所在!ITT 技术学院贡献了近 40,000 名学生,快速搜索就会发现其背景故事。2016 年 8 月,在一系列欺诈和误导学生的指控后,教育部禁止 ITT 理工大学用联邦资助招收新生。次月,ITT 理工大学宣布破产。
使用附加数据增强
让我们扩大调查范围,把一个机构其他方面的数据也包括进来。具体来说,我们可能会想,资助规模与一个机构是否被归类为全男性/全女性之间是否有任何关联。
教育部管理的大学记分卡是一个可以帮助我们的极好的数据集。我们下载 2016–2017 年的数据作为 CSV 文件,并上传到新的 Rockset 集合:

为了与佩尔助学金数据相一致,我们使用机构的 OPE IDs(美国教育部指定的中学后教育鉴定办公室)。我们注意到大学记分卡有两个 OPE ID 列,一个用于 6 位 ID,一个用于 8 位 ID:
与此同时,佩尔·格兰特的数据有点混乱。原始 Excel 文件将 ID 存储为数字(因此去掉了其前导 0 ),并为每个 ID 附加两个额外的数字:
我们可以在 Rockset 查询中解决所有这些问题。我们使用 SQL 函数来处理 ID,然后使用一个 JOIN 来比较机构名称并检查它们是否一致。
看起来不错!使用连接,我们不需要任何预先的数据准备来组合数据集-我们可以自由地探索,而不需要预先定义我们可以使用的数据的边界。
大学记分卡记录了每个机构的各种属性,点击查看。在我们的调查中,我们计算了全男性/全女性机构相对于其他机构的平均资助规模:
我们就此打住,但你可以想象无尽的角度去探索:
- 佩尔拨款偏向于任何特定的研究领域吗?
- 是更难被大学录取,还是一旦入学就更难获得佩尔助学金?
- 教职员工的工资与学校从佩尔助学金中获得的收入有什么关系?
如果您想用这些数据运行自己的查询,那就尽情发挥吧!只要联系 hello@rockset.com 就能获得访问权限。(完全披露:我在 Rockset 工作。)
摘要
SQL 通常用于查询传统数据库中的数据,但在这里我们看到了它如何也可以用于该上下文之外的数据。SQL 的灵活性和复杂性允许动态地处理数据,以任意方式聚合数据,并与其他地方的数据有效地结合。随着我们找到查询半结构化数据的新方法,我认为 SQL 有潜力将世界上越来越多的数据用于积极的用途。
使用的数据:
- 2016-2017 年联邦佩尔资助项目资金按机构分布 (Excel 电子表格)
- 2015-2016 年联邦佩尔资助项目资金按机构分布情况( Excel 电子表格
- 2016–2017 年高校记分卡数据( CSV 文件)
使用的服务:
- Rockset —摄取和查询。XLSX 文件
- amCharts —在美国热图上可视化查询结果
- GitHub Gist 和code pen——分享本帖代码
对回头客进行 A/B 测试的复杂性
直觉会导致错误

A/B 测试简介
A/B 测试,也称为分割测试,有很多好处。它可以让你
- 证实你的假设。
- 发现客户的偏好行为。
- 衡量新功能的有效性。
A/B 测试的前提非常简单——将用户流量分成两组,称为桶,并为一个桶提供与另一个不同的体验。观察铲斗的流量,了解各组的行为有何不同。
A/A 测试简介
在进行实验之前,确认测试引擎的正确性是至关重要的。这可以通过执行通常所说的 A/A 测试来实现。这是一个测试,两个桶接受相同的经验。A/A 测试可以揭示,和其他东西:
- 有缺陷的基础设施。
- 流量分流不当。
- 分流流量中的偏差。
我将使用这种 A/A 方法来揭示非标准分割测试场景中的隐藏故障。
随机回头客
有时候在复杂的情况下,人们的行为和人们的行为并不一定相关。例如,让我们考虑一家银行。我们正在努力改进柜员登记软件,以提高柜员的结账速度。复杂性来自于这样一个事实,即结账时间在很大程度上是由顾客而不仅仅是雇员来控制的。更糟糕的是,结账时的一个错误可能会产生一个回头客,这会占用额外的时间。此外,一些回头客可能会去不同的窗口;让我们将这些情况视为损坏的数据,并在分析过程中跳过。我们希望在发布变更时小心谨慎,所以我们不会向 50%的收银机发布新软件,而是只向 25%的收银机发布。
目标:提高柜员的结账速度。
在这篇文章中,我想研究一下当以下三种情况同时存在时会发生什么:
- 铲斗分配不是 50/50。
- 一定比例的顾客会返回(糟糕的场景)。
- 回头客会被随机分配一个新的铲斗。
这两种情况的结合会隐藏计算误差;然而,在这里,我将证明这三者的结合需要对简单的数学进行改变。
模拟 A/A 测试
我可以在一个简单的电子表格中模拟这个场景。如前所述,我将从一个测试开始,其中两个时段 A 和 B 具有相同的值(每个客户的时间和退货率)。这将帮助我验证驱动这一点的数学“引擎”。
让我们从访问银行的 320 名客户开始。让我们将平均结账时间定义为总时间除以顾客总数。存储桶分配将设置为 25% / 75%。因为我在模拟 A/A 测试,所以两个时段的时间是相同的;让我们选择 10 分钟的值。同样,客户的退货率也是一样的,所以我们选择 5%。

https://docs.google.com/spreadsheets/d/1jr_zOeDPXJUG4BPFQ0UCfCBdC6zi2B4RITLc3IhS8Ew
请记住,回头客不计入总数,因为他们是同一批人。查看上面的计算,您可以看到总时间除以总客户数:
铲斗 A: 810 / 80 = 10.125
铲斗 B: 2490 / 240 = 10.375
即使在模拟的 A/A 环境中,尽管客户价值都相同,但我们最终得到的平均值却不同。如果时段分配为 50/50,如果没有退货客户,或者如果客户始终保持在同一个时段,则隐藏此差异。
误差分析
让我们首先讨论一下天真的直觉告诉我们什么。所有客户的模拟时间平均为 10 分钟。如果没有回头客,那么值 10 将是唯一可接受的值。随着一定比例的客户返回,我们认识到这个值应该更高,所以让我们来计算多少。如果 100%的顾客都回来过一次,那么平均 20 分钟就够了。50%的回报率应该产生 15 分钟,因此只有 5%的回报率,我们应该观察 10.5 分钟的平均值。
我们两个时段的值都低于预期的 10.5,所以我们要么低估了总平均时间,要么高估了独立客户。对于时段 A,我们可以确定总时间是 800 + 10,并且我们可以确定唯一客户的总数是 80。然而 810/80 并没有给我们必要的 10.5。我们不能再次计算回头客,即使我们计算了也无济于事:810/81 = 10 仍然是错误的。
解决这种现象的一个方法是要求顾客像以前一样通过同样的登记。这对于数学目的来说已经足够了,尽管实际上并不总是可能的。另一个解决办法是禁止他们返回——同样不切实际。最后,我们可以要求存储桶分布为 50/50;这将通过对两个桶给出相同的答案来隐藏问题,但是答案仍然是不正确的(10.25)。
让我们来分解一下我们在桶 A 中得到错误答案的公式:
(800 + 10) / 80 = (800 / 80) + (10 / 80) = 10.125
这揭示了一些令人困惑的信息。对于回访,我们用总时间(10 分钟)除以总访客数(80 个客户),这是不对的。80 个客户中只有 5%会回来,这样我们就有了 4 个客户。在这 4 个中,只有 25%会被分配到存储桶 A,这样我们又得到 1。
(800 + 10) + (10 / (80 * 0.05 * 0.25)) = 11
然而,这就是错误发生的地方,不管它看起来多么正确。我们必须停下来问问——有多少顾客回来了有什么关系?
回头客给我们的总时间增加了额外的时间。但是,它们对我们平均值的贡献应该只受最大可能影响的控制,对于桶 A 是 25%。换句话说,在划分退货客户的总时间时,不应考虑退货率。
(800 / 80) + (10 / (80 * 0.25)) = 10.5
对于桶 B:
(2400 / 240) + (90 / (240 * 0.75)) = 10.5
最后将公式概括为每斗:

其中 n 是访问次数,Tn 是所有客户访问 n 的总时间,C 是原始客户数,P 是桶的分布百分比。
我认为这里更重要的教训是 A/A 测试有多有价值,甚至只是一个测试的模拟。如果你处理随机返回的客户,并且你正在考虑 A/B 测试你的系统——确保检查你的数学和调试你的代码!
在一组真实任务上比较复杂语言的复杂 NLP 模型

Deep FastText. You can also use this trick with transformers
预先训练基于变压器的模型有意义吗?你能比 BPE 做得更好吗?哪种架构更适合哪种任务?
这将是那些文章中的一篇,在那里我们不发表“即插即用”的代码,但是我们没完没了地漫谈我们尝试了什么和失败了什么(或者也许没有?)最重要的是这如何适应更广泛的形象。
在过去的 3-6 个月中,我们为一种形态丰富的语言— 俄语尝试了各种基本 NLP 任务模型(如分类、序列间建模、机器理解)。这有点酷,因为我们可以继承大量的现代文献和现代 NLP 方法的代码库(RNNs、LSTMs、embeddings、PyTorch、FastText、LASER 等等)。
但是通常会有三种痛苦:
- 俄语比英语复杂得多,而且词法丰富;
- 公共基准/数据集/语料库非常稀缺;
- 俄语中的 NLP 研究几乎是不存在的(这主要意味着由真实的人应用基准)。阿法克,俄英 NMT 最先进的由中国人举办;
以上是我们目前的结论:
- 简单任务 —尽管 transformers 对大量 NLP 任务进行了广泛宣传的改进(我认为在一个具有 5-10k 行和几个类的学术数据集上的 10 个类分类是一个“简单任务”),但考虑到计算成本和增加的复杂性和推理时间,我们未能获得在这种“简单任务”上使用 Transformers 的真正好处。适当的 RNN / CNN 基于模型的注意力很快被数据的偏差所限制——而不是任务的复杂性;
- 有一种情况,在对话意图 30 类分类任务中,“基线”模型给出 51%的顶级准确度,而变压器给出大约 57% 。但是经过进一步的调查和清理——在我们从数据中去除偏差并将类别数量减少到大约 15-20 个之后,这种提升消失了,即 transformer 更适合有偏差的数据。
- 同样值得注意的是——在我们的设置中,生成性预训练(BERT)的表现实际上比仅用 FastText n 元语法向量初始化嵌入包层更差(考虑到所需的时间、精力和计算资源——显而易见,您不应该自己对这种模型进行预训练)。
- 顺便说一句,我认为这些“简单”的任务主要包括:分类、标记、意图分类、搜索分类、词性、NER(当然前提是你有一个合适的数据集)。
- 我想你应该把 NMT 放在这里,但这本身就是一件事,我们甚至没有解决。在一个半困难的(如果一个胖 LSTM 收敛了几天,这是什么,对不对?)任务纠正独联体国家人名中的错别字(不要小瞧它,看帖子,没那么容易)——我们只是没有足够的耐心等到变形金刚会收敛。我估计他们收敛得更慢。我们在那里没有任何形式的预培训,因为领域是非常不同的;
- 真正困难的任务。在这里,对于俄语,我只能找出所谓的 SberSQUAD 。请注意,在队伍本身,变形金刚现在有最好的 EM(精确匹配),超过人类的表现,分数浮动在
90% EM左右。根据 DeepPavlov ,为这个特定任务定制的最先进的模型给出了大约 60%的 EM(当然我们不知道——它是 train 还是 val,以及验证是否被正确处理)。但是这里变得有趣了: - 一个基线背面的信封模型建成后有一天可以达到大约
15% EM; - 我可以用嵌入包(由 FastText 初始化)安装一个高达
37-40% EM的变压器,学习率很高1e-3(单个 1080 Ti 训练 45 小时); - 当我们添加了生成性预训练时,模型开始以数倍于的速度收敛,但无论我们如何尝试——我们都无法击败
30% EM——模型最终只是过度拟合——所以用我们的嵌入包方法进行预训练是可行的,但最终只是简单的快速文本初始化更好; - 推理基准——仅在 CPU 推理上对适当大小的转换器(300-400 个隐藏大小,12 个注意头)进行基准测试——根据经验,与 LSTMs w/o 速度优化(仅填充)相比,在相同数据上的推理时间大约是 10 倍。
自然对这些半信半疑。是的,很可能所有这些都意味着以下事情之一:
- 我们在代码中犯了一些错误——尽管我们花了时间彻底检查了所有的东西;
- 因为我们通常使用 1m 的 150 维嵌入(以适应内存)和隐藏大小为 400 的转换器,可能大小很重要;
- 最有可能的是,相当于 99 天* 1080 天的预培训也很重要;
- 也可能是由于不平衡的模型(1m 的 ngrams,浅的 transformer)和我们的嵌入包中缺乏注意层——我们的模型在预训练中基本上是瓶颈和/或过度拟合的;
NLP 模型的最新趋势
你可能不知道有两大类“复杂的”语言——粘合性语言和融合性语言,英语本身主要是分析性语言(在英语中,大多数意义是通过助词(助词、介词等)来表达的)。
我不是语言学专家,但是俄语有很多粘合和融合的特点。我们有词形变化、词形变化、词素以及很多混合这些东西的方法!
所以,在一个非常基本的层面上,俄语中的每个单词都可以这样拆分:

Subword units in Russian
是的,尤其是在动词中——对于流行词根,存在着带有几乎所有流行前缀的动词,它们……惊奇——惊奇有着非常不同的含义。如果你懂德语,你会立刻明白我的意思。
当然,除了构词法,我们还有词形变化和变位:

Inflection in Russian
现代自然语言处理中的词表示
当最初的 Word2Vec 论文问世时,Word vectors 彻底改变了 NLP 领域。但实际上,只有当 FastText 及其令人敬畏的实现被引入后,它们才变得可用于具有词法的语言。
如果你不熟悉 word 2 vec——它基本上只是从原始文本中学习 N(通常是 300)维单词向量,即从所讨论的单词周围的单词中学习。FastText 进一步发展了这一思想,学习子词向量而不是词向量,一个词只是其子词的加权平均值。如果你对这方面的技术更感兴趣——现在你甚至不需要定制工程来实现一个基本的 w2v 模型——在现代深度学习框架中嵌入层可以很容易地完成。
但所有这一切在理论上都是美好的。事实上,与cat最接近的词是… dog。Coffee与cup有关,因为咖啡是一种经常用杯子喝的饮料,但coffee与cup不同,因为咖啡是一种饮料,而杯子是一种容器。因为这些向量没有捕捉到意义。他们捕捉上下文。当然,你可以使用类似于 one 的方法,你可以训练类似于自动编码器或 GAN 的东西将字典中的单词定义编码成向量。但是对于大多数情况来说,这似乎有点高。
同样在 FastText 做任何事情之前实用的和可部署的对于像俄语这样的语言来说是痛苦的,因为:
- 对于一个简单的任务,你可能有一个词汇表
50-100k; - 对于一个真正的生产任务——你的词汇量很可能会在一个
1m-10m范围内; - 全俄罗斯互联网包含~
100m独特词汇;
我没有做任何英语基准测试,但我猜想同样的数字会低 5-10 倍,尽管如此,这并没有使某些任务变得容易处理。这个特殊的词汇问题在主流 NLP 文献中通过使用所谓的字节对编码( BPE )得到了某种程度的缓解,它本质上学习如何有效地将单词分成组块。
从理论上讲,BPE 应该能够很好地处理粘合语言,因为语素不会改变。唯一的问题是俄语也有很多融合的特征。这是有道理的,与 NMT BPE 将不会很好地工作,但更多的脚踏实地的任务?
我尝试在各种任务上测试 BPE(我通常尝试几种词汇大小,如 30k / 100k / 500k),与 char-level 方法和 embedding bag 方法相比,我最好的结果是准确性略有下降,如 1-3pp。我在社区里四处打听——出乎意料的是,我并不是唯一一个观察到 BPE 教不适合俄罗斯人的人。当然,在这种情况下,也许还应该平衡模型权重,但在我的情况下,只有两个标准:(1)模型在运行时应该快速(2)模型在训练时应该快速收敛。有了现代的 GPU 和 RAM,拥有 50k 和 250k 的词汇量没有什么区别,当然,除非你在做 NMT 或语言建模。
同样令人困惑的是——除了 FastText 之外,我很少看到主流论文试图构建单词嵌入解决方案,以某种方式考虑丰富的形态学。这篇俄中 NMT 论文是一个的例外——但是他们只分别预测结局。
注意,变形金刚,生殖预训练
所有这些都是非常重要的概念。
但我不会费心复制粘贴相同的文本和插图,而只会将您重定向到我所知道的解释这些概念的最佳页面:
本质上现在要解决应用的 NLP 任务,你有以下选项可供选择:
- 嵌入层用来表示单词或字符是必须有的;
- 词汇:字符级/固定/基于 BPE 的方法/嵌入包/基于快速文本的方法;
- 嵌入初始化 : Word2Vec 或 Glove/fast text/以上混合;
- 模型本身:
- (1) TCN / RNN +简单自我关注;
- (2)基于编码器-解码器的序列到序列模型;
- (3)变压器;
只要选择正确的组合,你就可以开始了!我们的任务主要是了解变形金刚(3)如何适应这个生态系统——BPE 与否,如何初始化嵌入,如何解决基准任务等
我们对单词表示问题的解决方案

Our approach to word representations for Russian in a nutshell
所以,现在你看到使用一种形态丰富的语言是一件苦差事。我说的工作是指生产你确信能很好地推广到不太为人知的情况的模型。
但是我们不能把现有的两个最好的仪器结合起来吗?FastText +深度学习模型?事实证明我们可以做到,例如在 PyTorch 中使用嵌入包层就可以很容易地做到这一点。你可以用注意力和单词/语法/字符的混合来构建一个小模型——但是它很可能比低级别的 C++实现要慢。
这种方法对 TCNs/RNNs/CNN 这样的简单模型非常有效,提供了泛化能力,完全消除了 OOV 情况,不再需要 UNK 令牌或任何类似的笨拙解决方案!
但是我们能把它推广到一个在大型文本语料库上预先训练的 transformer 模型吗?
这种方法只有 3 个基本问题:
- 将注意力添加到嵌入包层是一个不太可能解决的问题;
- 为了保证通用性,您至少需要大约
500k-1000kngrams; - 您面临着技术问题——语言建模标签不再可用于 LM 任务(您必须使用模型的嵌入作为基础事实)+在 PyTorch 中使用标准的 DataParallel 包装器变得很慢,因为嵌入层很大,为了实现真正的并行性,您可能必须使用 DistributedDataParallel
- 您可以在一个 GPU 上嵌入包,然后将得到的包张量分成正确的形状,但当您有多个 GPU 设置时,这变得更加困难,因为每个令牌都变成了填充序列,而不是令牌;

Typical vocabulary coverage (% of ngrams covered in a word) in case your ngram set is properly chosen. With 1m 2–6 grams you can cover even 100m dictionaries.
变形金刚/伯特/ GPT 更适合你吗?
TLDR——除非你能使用一个预先训练好的——那么很可能不会,除非这是唯一强大到足以处理你的任务的架构。
Seq2seq 基准
请参考此帖。
TLDR——在复杂的序列基准测试中,基于变压器的模型似乎收敛得更慢。

Shall we wait one more week?
创成式预培训速度提高了 10-100 倍?
我们不会发布我们全部的预训练代码(更重要的是——由于显而易见的原因,数据采集、预处理和我们的数据集),但是您可以将这些类放入一个 huggingface 模型中,以便:
- 从预先训练的 BPE 向量开始;
- 从老版本的嵌入包开始(哪个效果更好,lol);
- 从填充版嵌入包开始(方便使用多 GPU 包装器);
简而言之,他们所说的预先训练一个转换器需要 50-100,000 美元的计算资源似乎完全不可行。我们能让像我们这样的普通人快一点吗?
(来自未来的我: 是啊,不用预训练,只要用 FastText 初始化的嵌入包训练就行! )
按照纯粹的美国/硅谷精英主义的方式,像 BERT 这样的论文不会做任何穷人的消融测试,比如如果你用 FastText 初始化你的模型,收敛会发生什么?
但是不管怎样,怎样才能加快收敛速度呢?假设 1080Ti 的预培训需要 400 天,让我们从这里开始:
- 从预先训练的向量/ n-grams 开始— 也许快 10 倍?;
- 不使用一个大的 softmax 层(即使它链接到你的嵌入层),而是使用余弦损失或受此启发的东西。对了这些家伙也是从 fast text——x2 开始更快?;
- 更轻的型号— x4 更快?;
- 使用适合俄语的嵌入包层;
总而言之,通过所有这些“优化”,似乎可以在一周左右的时间内预训练/调整一个变压器,这是真实的,唯一的问题是实际的预训练模型似乎并没有真正击败一个刚刚用 FastText 初始化的模型。
训练前实验

*我们为每个模型使用了 2 个 GPU 设置,但最终我们发现,由于较大的嵌入包大小,较新版本的嵌入包大约慢 25 %+;
**分类任务来自伯特论文;
我们测试的其他“失败”方法:
- 所有从零开始训练的模型收敛得慢得多,并且很快达到稳定状态;
- 用 FastText 初始化的所有基于 BPE 的模型收敛得慢得多,并且在 65%的顺序任务准确度附近很快稳定下来;
- FastText +嵌入冻结—负 5pp 顺序任务准确率;

L2 embedding loss

Cosine embedding loss
实际尝试预先训练好的模型
这是整个练习中最令人失望的部分。
正如简介中提到的——任何种类的转换器(从头开始,预先训练,来自 FastText)对我们复杂领域的“简单”分类任务都没有帮助(但 FastText 是最好的)。
在一个具有挑战性的 SberSQUAD 任务上,我们得到了以下结果:
- 一个 FastText 初始化模型用高 lr 的
1e-3训练到大约37%-40% EM。用 LR 衰变可能可以获得更多。值得注意的是,模型频繁偏离,似乎在每次重启时都“跳跃”; - 当我们尝试使用
1e-3的高 lr 预训练模型时,它的训练速度比 FastText 快得多,但是过度训练; - 如果我们在
5e-4左右开始使用较低的 lr,那么预训练的模型也比快速文本快得多,但是在30% EM左右过度拟合;
我想如果我们投入 x10 的资源来实际调整超参数,那么我们将获得更高的结果。但是你看——生成性预训练不是银弹。尤其是对于非生成性任务。
在任何正常的任务中——传统的 rnn/CNN/TCN——把变压器炸得落花流水。

Top performance of FastText initialized transformer

Some comparisons

Low learning rate, pre-train vs. fast-text
嵌入包代码
就用我们的代号,把它贴在这里加水。不,我们不傻,我们使用版本控制。
改进或如何使我们的想法成为主流
来自 OpenAI、Google 和 FAIR 的人们,如果你正在阅读这篇文章,你可以做以下事情:
- 解决包埋袋层内的注意力问题;
- 添加更多的计算来训练具有更大嵌入包的更大的变换器;
- 如果有的话,在其他形态学丰富语言的基准上测试这样的生成性预训练;
- 投入时间和精力在适当的分词技术上,分别传递不同种类的分词对应的包;
参考
流行工具
俄语语料库/数据集/基准:
- 俄罗斯队和情绪分析数据集;
- 为大型网络语料库挖掘俄语;
- 我的帖子解析维基百科,解析普通抓取;
- 已准备和删除重复的普通爬网文本;
- 使用普通爬行训练句子编码器的缺点;
- DeepPavlov 俄罗斯小队;
- FastText 在俄语最大语料库上预训练;
嵌入基线的简单句:
单词嵌入解释:
- http://www.offconvex.org/2015/12/12/word-embeddings-1/;
- http://www.offconvex.org/2016/02/14/word-embeddings-2/;
原话嵌入论文:
- 单词和短语的分布式表示及其组合性;
- FastText 用子词信息丰富词向量
展示最新的自然语言处理模型和方法:
其他链接
原载于 2019 年 3 月 1 日spark-in . me。
使用 Rmusic 软件包在 R 中创作和播放音乐
介绍一个有趣的,实验性的软件包,它允许你创作简单的音乐,并在你的休息时间播放它们
几年前的圣诞节,在这个老 Stackoverflow post 的帮助下,我创作了一个音乐大师版本的《铃儿响叮当》。我只是回到这段代码,把它变成了一个 R 包,可以播放你想创作的任何曲子。
安装软件包
要安装该软件包,请运行:
devtools::install_github("keithmcnulty/Rmusic", build_vignettes = TRUE)
作曲
要创作简单的音乐,你需要创建两个向量,一个包含音符,另一个包含节拍中音符的持续时间。当然,这两个向量的长度应该是一样的。
根据以下规则,notes 向量应该是字符串向量:
- 音符从
"A"到"G" - 半音是通过在弦上加一个
#(升)或b(降)来表示的,例如"A#"或"Bb" - 默认八度音程是第四钢琴八度音程。对于较低或较高的八度音程,将八度音程编号附加到弦上,例如
"Ab3"、"C3"、"G#5"
持续时间向量包含简单的数字字符串,代表每个音符的节拍数,例如2或1.25
例如:艺术大师《铃儿响叮当》
我喜欢把音符向量写成每一小节音乐的字符串,然后用strsplit()把它们分解成想要的格式:
jbells_pitch <- paste(
"E E E",
"E E E",
"E G C D",
"E",
"F F F F",
"F E E E",
"E D D E",
"D G",
"E E E",
"E E E",
"E G C D",
"E",
"F F F F",
"F E E E E",
"G G F D",
"C",
"G3 E D C",
"G3",
"G3 G3 G3 E D C",
"A3",
"A3 F E D",
"B3",
"G G F D",
"E",
"G3 E D C",
"G3",
"G3 E D C",
"A3 A3",
"A3 F E D",
"G G G G A G F D",
"C C5 B A G F G",
"E E E G C D",
"E E E G C D",
"E F G A C E D F",
"E C D E F G A G",
"F F F F F F",
"F E E E E E",
"E D D D D E",
"D D E F G F E D",
"E E E G C D",
"E E E G C D",
"E F G A C E D F",
"E C D E F G A G",
"F F F F F F",
"F E E E E E",
"G C5 B A G F E D",
"C C E G C5"
)jbells_pitch <- strsplit(jbells_pitch, " ")[[1]]
对于持续时间向量:
jbells_duration <- c(
1, 1, 2,
1, 1, 2,
1, 1, 1.5, 0.5,
4,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
2, 2,
1, 1, 2,
1, 1, 2,
1, 1, 1.5, 0.5,
4,
1, 1, 1, 1,
1, 1, 1, 0.5, 0.5,
1, 1, 1, 1,
4,
1, 1, 1, 1,
3, .5, .5,
1, 1, 1, 1,
4,
1, 1, 1, 1,
4,
1, 1, 1, 1,
4,
1, 1, 1, 1,
4,
1, 1, 1, 1,
3, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
1, 1, 0.5, 0.5, 0.5, 0.5,
1, 1, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
1, 0.5, 0.5, 1, 0.5, 0.5,
1, 0.5, 0.5, 1, 0.5, 0.5,
1, 0.5, 0.5, 0.5, 0.5, 1,
1, 0.33, 0.33, 0.33, 1, 0.33, 0.33, 0.33,
1, 1, 0.5, 0.5, 0.5, 0.5,
1, 1, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
1, 0.5, 0.5, 1, 0.5, 0.5,
1, 0.5, 0.5, 1, 0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
1, 0.33, 0.33, 0.33, 2
)
你可以在套装的简介中找到一些其他的例子。
播放或保存音乐
要在 R 会话中播放叮当音乐,只需运行:
Rmusic::play_music(jbells_pitch, jbells_duration)
play_music()函数期望音符和持续时间向量作为其前两个参数,并且还有一个tempo参数,其默认值为每分钟 240 拍。
要将其保存为.wav文件,只需运行:
Rmusic::save_music(jbells_pitch, jbells_duration, output_file = "jbells.wav")
save_music()函数也需要输出.wav文件的路径。如果需要,请参阅帮助文档。
最初我是一名纯粹的数学家,后来我成为一名心理学家和数据科学家。我热衷于将所有这些学科的严密性应用于复杂的人的问题。我也是一个编码极客和日本 RPG 的超级粉丝。在 领英 或 推特 上找到我。

理解 Python 中的“理解”
理解和实现 python 中的列表、字典、集合和生成器。

Image by Gerd Altmann from Pixabay
“好的代码是它自己最好的文档”——史蒂夫·麦康奈尔
在我的上一篇文章中,我解释了 Python 中 Lambda、Map、Filter 和 Reduce 函数的概念,它们基于函数式编程范式。我还提到了列表理解的概念,它被认为是 lambda 函数的替代品。整篇文章详细地涵盖了理解的本质及其各种形式。
Python 中的理解
理解是允许从其他序列构建序列的结构。Python 2.0 向我们介绍了列表理解的概念,而 Python 3.0 通过包含字典和集合理解更进一步。

Types of Comprehensions in Python
为什么理解力如此强大?我们将通过一个例子来理解这一点。我们都知道 Python 提供了多种表示列表的方式。例如:
- 人们可以明确地把整个事情写成:
squares = [0, 1, 4, 9,16,25]
- 或者,编写一个 for 循环来创建一个列表:
squares = []
for num in range(6):
squares.append(num*num)
- 创建列表的另一种方法是使用单行代码。
squares = [num*num for num in range(6)]
上面这个一行程序叫做列表理解,是创建列表的一种便捷方式。它消除了对循环的依赖,使代码紧凑。下一节将进一步深入 Python 3 中提供的列表和其他类型的概念。
列表理解[ ]
列表理解是一种用 Python 以简洁的方式定义和创建列表的方法。在大多数情况下,列表理解让我们在一行代码中创建列表,而不用担心初始化列表或设置循环。
列表理解由以下部分组成:

Various parts of a List Comprehension
比方说,我们需要找到前五个偶数的平方。如前一节所述,有两种方法可以做到这一点:显式 for 循环或列表理解。让我们两个都试试。
- 使用 For 循环
even_squares = []
>>> for num in range(11):
... if num%2 == 0:
... even_squares.append(num * num)>>> even_squares
[0, 4, 16, 36, 64, 100]
- 使用列表理解
even_squares = [num * num for num in range(11) if num%2 == 0]
even_squares
[0, 4, 16, 36, 64, 100]
如果我们仔细观察,可以发现仅仅通过重新排列 For 循环就可以创建列表理解。

列表理解是 Python 实现数学中使用的集合符号的方式。

让我们试着看看更多借助列表理解创建列表的例子。
创造毕达哥拉斯三胞胎
一个毕达哥拉斯三元组由三个正整数 a,b,c 组成,这样
a + b = c .我们通常把这样的三元组写成(a,b,c),比如(3,4,5)。
[(a,b,c) for a in range(1,30) for b in range(1,30) for c in range(1,30)if a**2 + b**2 == c**2][(3, 4, 5), (4, 3, 5), (5, 12, 13), (6, 8, 10), (7, 24, 25), (8, 6, 10), (8, 15, 17), (9, 12, 15), (10, 24, 26), (12, 5, 13), (12, 9, 15), (12, 16, 20), (15, 8, 17), (15, 20, 25),(16, 12, 20), (20, 15, 25),(20, 21, 29), (21, 20, 29), (24, 7, 25), (24, 10, 26)]
用字符串列出理解
- 将字符串中的小写字母转换成大写字母。
colors = ["pink", "white", "blue", "black", purple"]
[color.upper() for color in colors]
['RED', 'GREEN', 'BLUE', 'PURPLE']
- 交换给定列表中的名字和姓氏。
**presidents_usa** = ["George Washington", "John Adams","Thomas Jefferson","James Madison","James Monroe","John Adams","Andrew Jackson"]**split_names** = [name.split(" ") for name in presidents_usa]
**swapped_list** = [split_name[1] + " " + split_name[0] for split_name in split_names]**swapped_list**['Washington George', 'Adams John', 'Jefferson Thomas', 'Madison James', 'Monroe James', 'Adams John', 'Jackson Andrew']
用元组列出理解
如果表达式包含一个元组(如(x, y)),必须用括号括起来。
# Convert height from cms to feet using List Comprehension : 1 cm = 0.0328 feet**height_in_cms** = [('Tom',183),('Daisy',171),('Margaret',179),('Michael',190),('Nick',165)]**height_in_feet** = [(height[0],round(height[1]*0.0328,1)) for height in height_in_cms]**height_in_feet**[('Tom', 6.0), ('Daisy', 5.6), ('Margaret', 5.9), ('Michael', 6.2), ('Nick', 5.4)]
嵌套列表理解[[ ]]
列表理解也可以嵌套来创建复杂的列表。例如,我们可以只使用列表理解来创建一个矩阵。
- 创建 3X3 矩阵
matrix = [[j * j+i for j in range(3)] for i in range(3)]
matrix[[0, 1, 4], [1, 2, 5], [2, 3, 6]]
集合理解
集合理解类似于列表理解,但是返回集合而不是列表。语法略有不同,因为我们使用花括号而不是方括号来创建集合。
考虑以下由人名组成的列表:
names = [ 'Arnold', 'BILL', 'alice', 'arnold', 'MARY', 'J', 'BIll' ,'maRy']
名单上有很多重复的名字,而且有些名字只有一个字母。我们想要的是一个由长度超过一个字母并且只有首字母大写的名字组成的列表。为了完成这样的任务,我们求助于集合理解。
{name.capitalize() for name in names if len(name) > 1}{'Alice', 'Arnold', 'Bill', 'Mary'}
字典理解
当输入是字典或key: value对的形式时,使用字典理解。例如,考虑一个字典,其中的键表示字符,值表示这些字符在语料库中出现的次数。
char_dict = {'A' : 4,'z': 2, 'D' : 8, 'a': 5, 'Z' : 10 }
字典char_dict由大写字母和小写字母混合组成。我们要计算字母出现的总次数,而不考虑它们的大小写。让我们用字典的理解来实现这一点:
{ k.lower() : char_dict.get(k.lower(), 0) + char_dict.get(k.upper(), 0) for k in char_dict.keys()}{'a': 9, 'z': 12, 'd': 8}
生成器表达式( )
列表理解之于列表,正如生成器表达式之于生成器。发生器函数从给定序列中一次输出一个值而不是一次给出所有值。这里有一篇不错的文章,它解释了 Python 中生成器的本质。
Python 生成器是强大但被误解的工具。他们经常被认为是太难的概念…
www.dataquest.io](https://www.dataquest.io/blog/python-generators-tutorial/)
生成器表达式的语法和工作方式与列表理解非常相似,只是它们使用圆括号而不是方括号。假设我们要计算前十个自然数的平方和。
# Sum of first ten natural numbers using List Comprehensionssum(**[**num**2 for num in range(11)**]**)
385
如果我们使用任何其他的 iterable,而不一定是 list,结果会是一样的。
sum(**{**num**2 for num in range(11)**}**)
385
现在,如果我们使用一个生成器表达式来计算前十个自然数的平方,它会是这样的:
squares = (num**2 for num in range(11))
squaressquares
<generator object <genexpr> at 0x1159536d8>
与列表理解不同,生成器表达式不返回列表,而是返回生成器对象。为了得到结果,我们可以使用上面的表达式和sum函数。
sum(n ****** 2 **for** n **in** numbers)
385
看看我们如何去掉上面表达式中多余的括号,使代码更加高效。
最后,不要过度使用理解
列表理解是减少代码长度的有效方法。它们还使代码更具可读性。但是有些情况下,没有它们我们也能过得很舒服。
当你的程序逻辑太长时,使用理解是不明智的。使用理解的主要思想是缩短代码。然而,当我们开始将太多的代码打包到一条语句中时,我们往往会损害代码的可读性。在这种情况下,for 循环是一个更好的主意。
参考资料和进一步阅读
神经网络中级主题
神经架构、激活函数、损失函数、输出单元的详细概述。
“人工智能是新的电力。” —吴恩达

本文是一系列文章中的第二篇,旨在揭开神经网络背后的理论以及如何设计和实现它们来解决实际问题。在本文中,我将详细介绍神经网络的设计和优化方面。
本文中的主题是:
- 神经网络的解剖
- 激活功能
- 损失函数
- 输出单元
- 架构
这些教程主要基于哈佛和斯坦福大学计算机科学和数据科学系的课堂笔记和例子。
如果您不熟悉神经网络的基本理论概念,我建议您先阅读本教程的第一部分,这些概念可以在下面找到:
神经网络的详细概述,有大量的例子和简单的图像。
towardsdatascience.com](/simple-introduction-to-neural-networks-ac1d7c3d7a2c) 
神经网络的解剖
人工神经网络是机器学习中使用的主要工具之一。正如它们名字中的“神经”部分所暗示的,它们是受大脑启发的系统,旨在复制我们人类的学习方式。神经网络包括输入层和输出层,以及(在大多数情况下)一个隐藏层,隐藏层由将输入转换为输出层可以使用的内容的单元组成。它们是寻找模式的优秀工具,这些模式对于人类程序员来说太复杂或太多,无法提取并教会机器识别。

虽然香草神经网络(也称为“感知器”)自 20 世纪 40 年代以来就已经存在,但只是在最近几十年,它们才成为人工智能的主要部分。这是由于一种名为反向传播的技术的出现(我们在之前的教程中讨论过),这种技术允许网络在结果与创造者希望的不匹配的情况下调整其神经元权重——例如,像一个旨在识别狗的网络,它错误地识别了一只猫。
到目前为止,我们已经讨论了这样一个事实,即神经网络利用仿射变换来将在网络中的特定节点处收敛的输入特征连接在一起。然后,该级联输入通过激活函数,该函数评估信号响应,并确定在给定当前输入的情况下是否应该激活神经元。
我们将在后面讨论激活函数的选择,因为这是获得功能网络的一个重要因素。到目前为止,我们只讨论了 sigmoid 作为激活函数,但还有其他几种选择,这仍然是机器学习文献中的一个活跃的研究领域。

我们还讨论了如何将这种思想扩展到多层和多特征网络,以便通过增加网络的自由度(权重和偏差)以及网络可用于进行预测的可用特征的数量来提高网络的解释能力。

A neural network with one hidden layer and two features (the simplest possible multi-layer multi-feature network).
最后,我们讨论了网络参数(权重和偏差)可以通过评估网络的误差来更新。这是使用通过网络的反向传播来完成的,以便获得每个参数相对于损失函数的导数,然后可以使用梯度下降来以知情的方式更新这些参数,使得网络的预测能力有可能提高。
总的来说,评估误差和更新参数的过程被称为训练网络。这只有在基础事实已知的情况下才能实现,因此需要训练集来生成功能网络。然后,可以通过对看不见的数据进行测试来评估网络的性能,这通常称为测试集。
神经网络具有大量的自由度,因此,它们需要大量的数据进行训练,以便能够进行充分的预测,特别是当数据的维度很高时(例如,在图像中,每个像素都被视为一个网络特征)。
一个通用的多层多特征网络如下所示:

Generalized multilayer perceptron with n hidden layers, m nodes, and d input features.
我们有 m 个节点,其中 m 是指网络中一层的宽度。请注意,要素的数量和网络图层的宽度之间没有关系。
我们也有 n 隐藏层,它描述了网络的深度。一般来说,任何具有一个以上隐藏层的东西都可以被描述为深度学习。有时,网络可能有数百个隐藏层,这在一些用于图像分析的先进卷积架构中很常见。
输入数量 d 由可用数据预先指定。对于图像,这将是图像展平为一维数组后图像中的像素数量,对于正常的熊猫数据框, d 将等于特征列的数量。
一般来说,不要求网络的隐藏层具有相同的宽度(节点数);隐藏层中的节点数量可能会有所不同。根据所需的输出,输出层也可以是任意尺寸。如果您尝试将图像分为十类,输出图层将由十个节点组成,每个节点对应于一个相关的输出类,这是流行的 MNIST 手写数字数据库的情况。
在神经网络之前,基于规则的系统已经逐渐演变成更现代的机器学习,由此可以学习越来越多的抽象特征。这意味着更复杂的选择标准现在是可能的。
为了理解这个想法,假设您正试图根据水果的长度和宽度对水果进行分类。如果你有两个非常不同的水果要比较,比如一个苹果和一个香蕉,这可能很容易区分。然而,这个规则系统在某些情况下会因为选择了过于简单的特性而失效。
神经网络在网络的每个阶段提供数据的抽象表示,其被设计来检测网络的特定特征。当考虑用于研究图像的卷积神经网络时,当我们查看更接近深度网络输出的隐藏层时,隐藏层具有高度可解释的表示,例如人脸、服装等。然而,当我们观察网络的第一层时,它们检测的是非常基本的特征,例如拐角、曲线等等。

这些抽象表示很快变得过于复杂而难以理解,直到今天,神经网络产生高度复杂抽象的工作方式仍然被视为有点神奇,是深度学习社区的研究主题。

An example of a neural network with multiple hidden layers classifying an image of a human face.

An example of a neural network with multiple hidden layers classifying written digits from the MNIST dataset.
我们将在后面讨论隐藏层和宽度的选择。接下来,我们将进一步详细讨论激活函数。

激活功能
激活函数是神经网络的一个非常重要的部分。激活功能类似于生物神经元中电势的建立,一旦达到某个激活电势,生物神经元就开始放电。这种激活电位在人工神经网络中使用概率来模拟。根据所选择的激活函数,网络点火的特性可能有很大的不同。
激活功能应该做两件事:
- 确保非线性
- 确保隐藏单元的梯度保持较大
激活函数的一般形式如下所示:

f(.) represents the activation function acting on the weights and biases, producing h, the neural output.
为什么我们需要非线性?从技术上讲,我们不需要非线性,但使用非线性函数有好处。
如果我们不应用激活函数,输出信号将只是一个线性函数。线性函数只是一次多项式。现在,线性方程很容易求解,但它们的复杂性有限,并且从数据中学习复杂函数映射的能力较弱。没有任何激活函数的神经网络将仅仅是线性回归模型,,其受限于它能够近似的函数集。我们希望我们的神经网络不仅仅学习和计算一个线性函数,而是比这更复杂的东西。
这又回到了我们在上一篇文章中讨论的通用逼近定理的概念——神经网络是广义非线性函数逼近器。使用非线性激活,我们能够生成从输入到输出的非线性映射。
激活函数的另一个重要特征是它应该是可微的。这是必要的,以便在网络中执行反向传播,计算相对于权重的误差(损失)梯度,然后使用梯度下降来更新权重。使用线性激活函数会产生易于微分的函数,该函数可以使用凸优化来优化,但是具有有限的模型容量。
为什么我们要确保通过隐藏单元的梯度很大?
如果我们有小梯度和几个隐藏层,这些梯度将在反向传播过程中成倍增加。计算机处理数字的精度是有限制的,因此,如果我们将许多非常小的数字相乘,梯度值将很快消失。这通常被称为消失梯度问题,是生成深度神经网络时的一个重要挑战。
激活功能的一些最常见的选择是:
- 乙状结肠
- ReLU(整流线性单元)
- 泄漏的 ReLU
- 广义 ReLU
- 最大输出
- 软加软件
- 谭
- 唰
这些激活功能总结如下:

Summary of activation functions for neural networks.
乙状结肠
在上一篇文章中,我们只关注了 sigmoid 函数。

实际上,由于以下原因,该函数不是用作激活函数的特别好的函数:
- Sigmoids 遭受消失梯度问题。
- Sigmoids 不是零居中的;梯度更新在不同的方向上走得太远,使得优化更加困难。
- Sigmoids 饱和并消除梯度。
- 乙状结肠收敛缓慢。
Sigmoids 仍然用作二进制分类的输出函数,但通常不在隐藏层中使用。sigmoid 的多维版本称为 softmax 函数,用于多类分类。
Tanh
sigmoid 函数的零中心问题可以通过使用双曲正切函数来解决。因此,在隐藏层中,双曲正切函数总是优先于 sigmoid 函数。然而,双曲正切仍然受到困扰 sigmoid 函数的其他问题的困扰,例如消失梯度问题。

ReLU 和 Softplus
整流线性单元是最简单的激活功能之一。如果函数的输入小于零,则输出返回零,如果输入为正,则输出等于输入。ReLU 是最简单的非线性激活函数,在大多数应用中表现良好,这是我在处理新的神经网络问题时的默认激活函数。

正如你所看到的,softplus 是 ReLU 的一个微小变化,在零点的过渡稍微平滑一些,这有利于激活函数没有不连续性。
ReLU 避免并纠正了消失梯度问题。现在几乎所有的深度学习模型都使用 ReLU。但是,ReLU 只应在神经网络的隐藏层中使用,而不应用于输出层-对于二元分类,输出层应为 sigmoid 对于多类分类,输出层应为 softmax 对于回归问题,输出层应为 linear。
漏 ReLU 和广义 ReLU
ReLU 的一个问题是,一些梯度在训练期间可能不稳定,可能会死亡。它会导致权重更新导致网络在任何数据点都不会激活。这些通常被称为死亡神经元。
为了解决死亡神经元的问题,引入了包含小斜率的泄漏 ReLU。这个斜率的目的是保持更新活跃,并防止死亡神经元的产生。

泄漏和广义整流线性单元是基本 ReLU 函数的微小变化。泄漏的 ReLU 在零点仍然有不连续性,但是函数在零点以下不再平坦,它仅仅有一个减小的梯度。泄漏 ReLU 和广义 ReLU 之间的差异仅取决于α的选定值。因此,漏 ReLU 是广义 ReLU 的子集。
Maxout
Maxout 就是 k 线性函数的最大值——它直接学习激活函数。这是一种混合方法,由 ReLU 和泄漏 ReLU 单元的线性组合组成。

Swish:自门控激活功能
目前,最成功和最广泛使用的激活函数是 ReLU。然而,在一些具有挑战性的数据集上,swish 往往比 ReLU 更好地依赖于更深层次的模型。Swish 是谷歌在 2017 年开发的。
Swish 本质上是 sigmoid 函数乘以 x:
f(x)= x∑(x)
引起消失梯度问题的 ReLU 的主要问题之一是,对于输入 x 的一半值,其导数为零。 这是有问题的,因为它会导致神经网络中很大比例的死亡神经元(高达 40%)。另一方面,Swish 是一个平滑的非单调函数,不会遇到零导数的问题。

Swish 仍然被视为对神经网络的一种有点神奇的改进,但结果表明,它为深度网络提供了明显的改进。要了解更多这方面的内容,我建议查看 arxiv 上的原始文章:
[## 搜索激活功能
深度网络中激活函数的选择对训练动态和任务效率有重要影响
arxiv.org](https://arxiv.org/abs/1710.05941)
在下一节中,我们将更详细地讨论损失函数。

损失函数
损失函数(也称为成本函数)是神经网络的一个重要方面。我们已经讨论过使用优化过程来训练神经网络,该优化过程需要损失函数来计算模型误差。
有许多函数可以用来估计神经网络中一组权重的误差。然而,我们更喜欢一个函数,其中候选解的空间映射到一个平滑(但高维)的景观上,优化算法可以通过迭代更新模型权重来合理地导航该景观。
一般来说,最大似然法为训练神经网络和机器学习模型时选择损失函数提供了框架。因此,要使用的损失函数取决于输出数据分布,并与输出单元紧密耦合(下一节讨论)。
交叉熵和均方误差是训练神经网络模型时使用的两种主要类型的损失函数。
然而,采用最大似然法有几个原因,但主要是因为它产生的结果。更具体地,在输出层中使用 sigmoid 或 softmax 激活函数的用于分类的神经网络使用交叉熵损失函数比使用均方误差学习得更快且更稳健。
交叉熵损失的使用极大地提高了具有 sigmoid 和 softmax 输出的模型的性能,这些模型之前在使用均方误差损失时会出现饱和和学习缓慢的问题
—第 226 页,深度学习,2016。
训练数据和模型分布之间的交叉熵(即负对数似然)采用以下形式:

下面是一个结合均方误差损失的 sigmoid 输出示例。

将上面的例子与下面使用 sigmoid 输出和交叉熵损失的例子进行对比。

在下一节中,我们将处理输出单位,并更明确地讨论损失函数和输出单位之间的关系。
输出单位
我们已经在激活函数一节中详细讨论了输出单元,但是最好把它说清楚,因为这是很重要的一点。忘记使用正确的输出函数并花费数小时对性能不佳的网络进行故障排除是相对容易的。
对于二元分类问题,例如确定医院病人是否患有癌症 (y=1) 或没有癌症 (y=0) ,sigmoid 函数用作输出。

Sigmoid output function used for binary classification.
用于多类分类,例如我们试图将图像过滤为狗、猫和人类类别的数据集。这使用了 sigmoid 函数的多维推广,称为 softmax 函数。

Softmax function for multiclass classification output units.
在每种情况下,还应该使用与输出类型兼容的特定损失函数。例如,对二进制数据使用 MSE 没有什么意义,因此对于二进制数据,我们使用二进制交叉熵损失函数。当进入更复杂的深度学习问题时,生活变得更加复杂,例如生成对抗网络(GANs)或自动编码器,如果你对学习这些类型的深度神经架构感兴趣,我建议看看我关于这些主题的文章。
下表总结了数据类型、分布、输出图层和成本函数。

在最后一节中,我们将讨论架构如何影响网络逼近函数的能力,并了解开发高性能神经架构的一些经验法则。

体系结构
在本节中,我们将研究如何使用神经网络对函数 y=x sin(x) 进行建模,以便了解不同的架构如何影响我们对所需函数进行建模的能力。我们将假设我们的神经网络正在使用 ReLU 激活函数。

只有一个隐藏层的神经网络只给了我们一个自由度。因此,我们最终得到了一个非常差的函数近似值——注意,这只是一个 ReLU 函数。

在隐藏层中添加第二个节点给了我们另一个自由度,所以现在我们有两个自由度。与以前相比,我们的近似值现在有了显著的提高,但仍然相对较差。现在我们将尝试添加另一个节点,看看会发生什么。

有了第三个隐藏节点,我们增加了另一个自由度,现在我们的近似开始让人想起所需的函数。如果我们添加更多的节点会发生什么?

我们的神经网络现在可以很好地逼近这个函数,只需要一个隐藏层。如果我们使用多个隐藏层,我们会看到什么不同?

这个结果看起来类似于我们在一个隐藏层中有两个节点的情况。但是,请注意,结果并不完全相同。如果我们在两个隐藏层中添加更多的节点会发生什么?

我们看到自由度的数量又增加了,正如我们所预料的那样。但是,请注意,自由度的数量比单个隐藏层少。我们将看到这一趋势在更大的网络中继续。

我们的神经网络有 3 个隐藏层,每层有 3 个节点,这很好地逼近了我们的函数。
为神经网络选择架构不是一件容易的事情。我们希望选择一个足够大的网络架构来近似感兴趣的功能,但又不能大到需要花费过多时间来训练。大型网络的另一个问题是,它们需要大量的数据来训练——你不能在一百个数据样本上训练一个神经网络,并期望它在一个看不见的数据集上获得 99%的准确性。
一般来说,使用多个隐藏层以及隐藏层中的多个节点是一种很好的做法,因为这样似乎可以获得最佳性能。
Ian Goodfellow(生成对抗网络的创始人)已经表明,增加神经网络的层数往往会提高整体测试集的准确性。

同一篇论文还表明,大型浅网络往往更容易过度拟合——这是使用深度神经网络而不是浅神经网络的一个刺激因素。

选择隐藏层和节点将在接下来的教程中进行更详细的评估。

最终意见
我希望您现在对神经网络是如何构建的有了更深入的了解,并且现在更好地理解不同的激活函数、损失函数、输出单元以及神经架构对网络性能的影响。
未来的文章将着眼于涉及深度神经网络优化的代码示例,以及一些更高级的主题,如选择适当的优化器、使用 dropout 防止过度拟合、随机重启和网络集成。
关注神经网络优化的第三篇文章现已发布:
涵盖优化器,动量,自适应学习率,批量标准化,等等。
medium.com](https://medium.com/@matthew_stewart/neural-network-optimization-7ca72d4db3e0)
时事通讯
关于新博客文章和额外内容的更新,请注册我的时事通讯。
丰富您的学术之旅,加入一个由科学家,研究人员和行业专业人士组成的社区,以获得…
mailchi.mp](https://mailchi.mp/6304809e49e7/matthew-stewart)
进一步阅读
深度学习课程:
- 吴恩达的机器学习课程有一个很好的神经网络介绍部分。
- 杰弗里·辛顿的课程:用于机器学习的 Coursera 神经网络(2012 年秋季)
- 迈克尔·尼尔森的免费书籍神经网络和深度学习
- 约舒阿·本吉奥、伊恩·古德菲勒和亚伦·库维尔写了一本关于深度学习的书
- 雨果·拉罗歇尔在舍布鲁克大学的课程(视频+幻灯片)
- 斯坦福大学关于无监督特征学习和深度学习的教程(吴恩达等人)
- 牛津大学 2014-2015 年 ML 课程
- 英伟达深度学习课程(2015 年夏季)
- 谷歌在 Udacity 上的深度学习课程(2016 年 1 月)
面向 NLP:
以视觉为导向:
- 用于视觉识别的 CS231n 卷积神经网络作者 Andrej Karpathy(之前的版本,更短更不完善:黑客的神经网络指南)。
重要的神经网络文章:
- 神经网络中的深度学习:概述
- 利用神经网络的持续终身学习:综述——开放存取
- 储层物理计算的最新进展:综述—开放存取
- 脉冲神经网络中的深度学习
- 集成神经网络(ENN):一种无梯度随机方法——开放存取
- 多层前馈网络是通用逼近器
- 深度网络与 ReLU 激活函数和线性样条型方法的比较——开放访问
- 脉冲神经元网络:第三代神经网络模型
- 多层前馈网络的逼近能力
- 关于梯度下降学习算法中的动量项
图灵学习和 GANs 简介
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是你新的最好的朋友。
生成对抗网络是过去 10 年机器学习中最有趣的想法——扬·勒村,脸书人工智能研究中心主任
第 2 部分已发布,可在此处找到:
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…
towardsdatascience.com](/comprehensive-introduction-to-turing-learning-and-gans-part-2-fd8e4a70775)
第 3 部分已发布,可在此处找到:
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…
medium.com](https://medium.com/@matthew_stewart/gans-vs-autoencoders-comparison-of-deep-generative-models-985cf15936ea)
这个由三部分组成的教程是我的深度生成模型系列的继续。这个关于图灵学习和 GANs 的话题是对之前关于变分自动编码器话题的自然延伸(在这里可以找到)。我们将看到 GANs 在很大程度上优于变分自动编码器,但是众所周知很难使用。

Taxonomy of deep generative models. This article’s focus is on GANs.
在本教程中,我们将探讨以下主题:
- 图灵学习和 GANs 的动机
- 甘斯的基础
- 网络培训
- 网络建设
- GAN 挑战
- GAN 经验法则(GANHACKs)
本教程的第 1 部分将不会有编码(否则本教程将会非常长),第 2 部分将作为当前教程的延续,将深入到 GANs 的更高级的方面,使用一个简单的编码实现来生成名人脸。教程的第三部分将是一个应用 VAEs,GANs 和 VAE-GANs 生成名人脸和动画图像的编码教程。第二部分和第三部分将在下周出版。
GANs 是一个快速发展的话题,本教程涵盖了截至 2019 年 4 月 GANs 的最新进展。如果你在这个日期之后阅读,那么要小心,这个领域可能已经有了发展,经验法则也有了改变。
我的目标是让这篇文章成为最全面、最容易理解的关于 GANs 的教程,如果你有任何改进这篇文章的建议,请告诉我。
所有相关代码现在都可以在我的 GitHub 存储库中找到:
GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码、管理项目和构建…
github.com](https://github.com/mrdragonbear/GAN-Tutorial)
让我们开始吧!

Horse/zebra image translation using a pre-trained DC-GAN.
图灵学习和 GANs 的动机
希望你正在读这篇文章,因为你对 GANs 及其工作方式一无所知(或者相对来说知之甚少)。在这一部分,我希望让你对 GANs 的潜力感到兴奋,以及它们如何被用来解决现实世界的问题,以及产生假名人、动漫人物等的乐趣。
在以前的文章中,我们关注于使用自动编码器生成数据。然而,这种方法产生的图像分辨率不是很高。在本文中,我们将研究一种完全不同的方法来生成类似于训练数据的数据。
这项技术让我们能够生成远远超出 VAE 所能提供的数据类型。GAN 基于一个聪明的想法,其中两个不同的网络相互竞争,目标是让一个网络创建不同于训练数据的新样本,但仍然足够接近,以至于另一个网络无法区分哪些是合成的,哪些属于原始训练集。
像以前一样,我们想要构建我们的生成模型,我们想要训练它从开始生成这样的光曲线。在这种情况下,生成模型可以是一个大型神经网络,它输出光曲线:来自模型的样本。
如果你不熟悉生成模型或变型自动编码器的概念,你可能首先想阅读我以前的文章,自动编码器综合介绍。
为了让你了解 GANs 现在在学术界有多重要,请看下图,该图显示了该领域每月发表的论文数量的增长。

甘斯的基础
很有可能你之前听说过生成对抗网络,但可能没听说过图灵学习。本质上,图灵学习是 GAN 基础过程的一般化。
“图灵”一词来自与图灵测试的相似之处,在图灵测试中,计算机试图欺骗系统,让它认为自己是人类。正如我们将看到的,这类似于 GAN 中发生器的目标,它试图欺骗它的“对手”,即鉴别器。对 GANs 进行一般化的需求源于这样一个事实,即图灵学习可以用任何形式的生成器或鉴别器来执行,而不一定是神经网络。
使用神经网络在图灵学习中很常见的主要原因是神经网络是一个通用函数逼近器。也就是说,我们能够使用神经网络(假设它具有足够的容量,即大量的节点)来“学习”输入和输出之间的非线性映射。这给了神经网络比大多数方法更多的自由,因为它们保证收敛于任何非线性函数(给定无限的网络容量和无限的训练数据——参见通用逼近定理了解更多信息)。
对于生成器或鉴别器的形式没有真正的限制,它们甚至不需要具有相同的形式。然而,使用除神经网络之外的任何东西都可能增加模型的偏差。例如,可以对生成器和鉴别器都使用支持向量机;类似地,支持向量机用于生成器,神经网络用于鉴别器。
本教程的很大一部分(主要在第 2 部分)将着眼于生成类似于下面的动画图像,使用 VAE,然后是 GAN,然后是 VAE-GAN(稍后将详细介绍)。

Anime images from our ‘GANIME’ training set.
现在,我们将更具体地研究 GAN 的结构。正如我们已经讨论过的,生成器的工作是制造看起来像训练集的假图像(至少在图像分析的情况下)。鉴别者看着这张假图像,试图鉴别它是真是假。发生器和鉴频器的损耗函数高度依赖于鉴频器的工作表现。

经过充分的训练后,生成器将变得更好,图像将开始看起来更逼真。

示意性地,我们可以将生成器和鉴别器表示为黑盒模型,这是某种形式的函数的抽象。这个函数可以(像机器学习中一样)用我们的无所不包的函数近似器,神经网络来近似。
生成器的输入是 noize z,,生成的样本将是我们的生成器函数的输出, G(z) 。然后,将生成的图像任意添加到鉴别器的输入数据中,鉴别器随后执行二元分类(即伪造或非伪造),并根据图像实际上是否是伪造的来分配分数。

发生器和鉴频器的损耗函数起初看起来有点吓人,但实际上非常简单。 G(z) 是我们的生成器的输出,即伪图像, D(G(z)) 是鉴别器对我们的伪数据的预测, m 是样本数。我们使用对数,因为它作为损失函数在数值上更稳定,并且我们取损失函数相对于参数的梯度,以便我们可以应用随机梯度下降。
如果这听起来像胡言乱语,那么不用担心,本教程的很大一部分将解释生成器和鉴别器的更新和改进。

博弈论
甘斯的整个想法是基于博弈论。对于那些不知道的人,博弈论分析游戏是为了想出如何获胜的理想策略。它已经变得与人工学习相对交织在一起,并且是计算机能够在几乎所有现存的棋盘游戏中击败世界冠军的原因。最近令人印象深刻的可能是围棋,人工智能 AlphaGo 能够击败世界冠军柯洁。
有些游戏,资源是无界的。例如,在一场扑克比赛中,彩池理论上可以无限制地变得越来越大。对于许多游戏来说,资源是有限的,这意味着一个玩家只能以牺牲另一个玩家的利益为代价来获胜;这就是所谓的零和游戏。
零和游戏:玩家争夺固定且有限的资源池。玩家争夺资源,声称他们和每个玩家的资源总数可以改变,但资源总数保持不变。
在零和游戏中,每个玩家都可以尝试设置一些东西,使得另一个玩家的最佳策略尽可能不占优势。这被称为最小最大或最小最大技术。
我们训练 GAN 的目标是产生两个尽可能好的网络。换句话说,我们最终没有“赢家”
相反,鉴于另一个网络有能力挫败它,两个网络都达到了它们的峰值能力。博弈论专家称这种状态为纳什均衡,每个网络相对于其他网络都处于最佳状态。这个想法如下图所示。

Loss function vs. number of epochs for discriminator and generator networks — flat line is Nash equilibrium.
为了查看在潜在表示中的纳什均衡方面发生了什么,下面我们绘制了生成器和鉴别器,以及它们的分布,作为时期的函数。我们看到分布逐渐收敛。

垃圾邮件过滤器示例
垃圾邮件过滤是思考生成性敌对网络如何工作的一个好方法。这类似于我描述的变分自动编码器,但不完全相同。
假设你有一个叫 Gary 的营销人员,他正试图通过 David 的垃圾邮件过滤器接收垃圾邮件。大卫被允许在垃圾邮件过滤器确认后将邮件分类为垃圾邮件。Gary 希望通过尽可能多的垃圾邮件,而 David 希望通过的垃圾邮件越少越好。理想情况下,我们最终会从这样的场景中达到纳什均衡(虽然我确信大多数人不希望垃圾邮件!).

在收到一堆电子邮件后,David 可以检查垃圾邮件过滤器的表现,并通过告诉垃圾邮件过滤器何时出现误报或漏报来“惩罚”它。

假设 Gary 也知道他的哪些垃圾邮件通过了(也许他也将它们发送给自己以验证成功),那么 David 和 Gary 都可以以混淆矩阵的形式看到他们在各自的任务中做得有多好(如下)。

在这之后,他们都可以学习哪里出了问题,然后从他们的错误中学习。Gary 将尝试一种不同的方法,这种方法利用了他以前的成功经验,David 将发现垃圾邮件过滤器的问题所在,并尝试改进过滤机制。

我们可以不断重复这个过程,直到我们获得某种形式的纳什均衡(或者两个人中的一个找到了获胜的最佳方式,并“抛弃”了这个方法,从而导致模态崩溃——稍后将详细介绍)。
我们可以考虑混淆矩阵,并以此为基础来改进我们的生成器和鉴别器。例如,如果该电子邮件实际上是垃圾邮件,并且被归类为假邮件,则生成器做得很差,必须做得更好。在这个意义上,鉴别器不需要做任何事情,它已经完成了它的工作。

在假阴性的情况下(电子邮件不是垃圾邮件,但它被归类为垃圾邮件),它是被愚弄的鉴别器。在这种情况下,鉴别器必须做得更好,而生成器已经正确地完成了它的工作,不需要改进。

在误报的情况下(当它实际上是垃圾邮件时被分类为真实邮件),再次是鉴别器出了问题。然后必须更新鉴别器,而生成器不做任何事情。

对于真正的否定(该电子邮件不是垃圾邮件,也没有被归类为垃圾邮件),生成器和鉴别器都不需要更新,因为任何不正确的事情都不需要更新。

发生器和鉴别器
鉴别器很简单。它将一个样本作为输入,其输出是一个单一值,该值报告了网络对输入来自训练集而不是假的的信心。鉴别器是什么没有太多限制。

生成器接受一串随机数作为输入。如果我们构建确定性的生成器,那么相同的输入将总是产生相同的输出。在这个意义上,我们可以把输入值看作潜在变量。但是这里的潜在变量不是通过分析输入数据发现的,就像 VAE 一样。随机噪声不是“随机”的,而是在“潜在”空间中表示(在我们的例子中是电子邮件)。

这一过程被称为“学习循环”,完成三项工作:
【1】鉴别器学习识别表征真实样本的特征。
【2】鉴别器学习识别暴露假样本的特征。
【3】生成器学习如何避免包含鉴别器已经学会发现的特征。
最终的网络看起来会像下面这样。简单总结一下这是如何工作的,从一些先验分布中抽取一个随机样本,将其输入到生成器中以生成一些假图像。这个假图像与真实数据一起被馈送到鉴别器网络,该网络然后决定哪些数据来自真实数据集,哪些数据来自先前分布产生的假数据。

现在,我们将转到网络培训,更定量、更明确地了解培训的执行情况。
网络培训
就我们的网络而言,我们需要训练两个网络。这变得很有趣,因为两个网络具有相同的总价值函数,但损失函数略有不同。鉴别器试图最大化总价值函数,而生成器试图最小化鉴别器的价值函数。

培训方法包括以下内容:
- 采样小批量的训练图像 x ,以及生成器代码 z.
- 使用反向传播更新 G 和 D (可选:一个玩家每走一步,另一个玩家就走 k 步,典型的比例是 4:1 的 D : G
假阴性(I: Real/D: Fake): 在这种情况下,我们将 Real 馈送给鉴别器。生成器根本不参与这一步。这里的误差函数只涉及鉴别器,如果它犯了一个错误,误差驱动反向传播步骤通过鉴别器,更新它的权重,以便它能更好地识别实数。

真负(I:假/D:假):我们从进入生成器的随机数开始。发电机的输出是假的。如果这个假的被正确地识别为假的,error 函数得到一个大的值,这意味着生成器被捕获了。反向传播通过鉴别器(被冻结)到达发生器。然后更新生成器,这样它可以更好地学习如何欺骗鉴别器。

假阳性(I:Fake/D:Real): 这里我们生成一个假的,如果它把它归类为真的,就惩罚鉴别器。

为了以一种不太抽象的形式来说明这种训练,我们将通过另一个比垃圾邮件过滤稍微复杂一些的例子来说明。
我们有生成的(假的)分布,它是由我们的生成模型产生的,我们有一个已知的真实分布。两者之间存在相关的 KL 散度,因为它们不是相同的分布,这意味着我们的损失函数非零。

然后,鉴别器会看到来自生成分布和真实分布的输入。如果鉴别器判定数据来自发生器,这将生成一个损失函数值,该值将传播回发生器并用于更新权重。重要的是,两个网络中只有一个同时被训练。

生成器现在已经改进,数据看起来更像真实的分布。

然而,数据仍然不够好,不足以欺骗鉴别器,因此再次更新生成器权重。

生成的分布又一次被更新了,现在鉴别器被忽悠了,它认为生成的数据来自真实的分布。是时候更新鉴别器了!

损失函数用于通过反向传播来更新鉴别器权重。

这个过程(理论上)一直持续到生成的分布与真实分布无法区分,网络达到纳什均衡。

一旦我们的网络建立并训练完毕,我们就可以使用生成器来生成与训练图像没有区别的图像,例如以下在标准 MNIST 数据集上使用的 DC-甘的示例。

DC-GAN on MNIST
GANs 的一个非常有趣的应用是添加或删除不同的属性,如下图所示,在不改变其他属性的情况下,微笑被“添加”到图像中。这也可以在视频编辑中完成,在未来,甚至可能对视频进行后期编辑,以类似的方式删除或添加不同的演员。类似的事情已经在 DeepFakes 的世界中发生了(尽管这种应用在本质上可以被认为是恶意的)。

Source: https://arxiv.org/pdf/1511.06434v2.pdf
这也可以通过其他特质来实现,比如太阳镜。

Source: https://arxiv.org/pdf/1511.06434v2.pdf
上述想法实质上就是本文开始时如何获得马到斑马的过渡图像。
为了让您了解这一领域在过去几年中的改善程度,请从下图中查看 GANs 在 2014 年至 2017 年间的演变。

此外,为了让你知道 GAN 上有多少种“味道”,有很多。虽然我将在第 2 部分更详细地描述 Wasserstein GAN (WGAN ),但我们在本教程结束时生产的 GAN 将是 DCGAN。我还将概述 GANs 如何用于生成时间序列,而不仅仅是图像。

网络建设
要构建的两种主要网络类型是全连接(FC)gan 或深度卷积 gan(DC-gan)。具体使用哪一种取决于提交给网络的培训数据。如果您使用单个数据点,FC 网络更合适;如果您使用映像,DC-GAN 更合适。这两种网络的不同架构如下所示。

Fully connected GAN

Deep Convolutional GAN (DC-GAN) — Alex Radford et al. 2016
使用 GANs 时要考虑的一些经验法则是:
- 最大池不好!用卷积步幅替换所有最大池。
- 使用转置卷积进行上采样。
- 使用批处理规范化。
我们将在下一节关于 GANHACKs 的内容中对此进行更多讨论。
GAN 经验法则(GANHACKs)
【1】归一化输入 —归一化-1 和 1 之间的图像,并确保使用 tanh 作为发生器输出的最后一层。
【2】使用球形 Z —不要从均匀分布中取样。当进行插值时,通过一个大圆进行插值,而不是从 A 点到 b 点的直线。我建议查看汤姆·怀特的采样生成网络参考代码【https://github.com/dribnet/plat,其中有更多关于这方面的详细信息。
【3】批量归一化 —为真实图像和虚假图像构建不同的小批量,即每个小批量只需要包含所有真实图像或所有生成的图像。然而,当批量标准化不可行时,另一种方法是使用实例标准化(对于每个样本,减去平均值并除以标准偏差)。
【4】避免稀疏渐变: ReLU,MaxPool — 如果你有稀疏渐变,GAN 游戏的稳定性会受到(很大)影响。一般来说,泄漏 ReLU 是好的(在发生器和鉴别器中)。
- 对于下采样,使用:平均池,Conv2d + stride
- 对于上采样,使用: PixelShuffle,ConvTranspose2d + stride
如果你不熟悉 PixelShuffle,你可以在这里阅读一整篇文章:https://arxiv.org/abs/1609.05158。
【5】使用软噪声标签 —标签平滑,即如果你有两个目标标签:Real=1 和 Fake=0,那么对于每个进来的样本,如果是 Real,那么用 0.7 到 1.2 之间的随机数替换标签,如果是 Fake 样本,用 0.0 和 0.3 替换(例如)。这是萨利曼斯等人的建议。艾尔。2016.一种替代方法是让标签对鉴别器产生噪声:在训练鉴别器时偶尔翻转标签。
更多提示见GANHACKs(【https://github.com/soumith/ganhacks】T2)。
甘挑战
GANs 有很多问题,但我将在这一节中讨论其中的主要问题,并在第 2 部分中进一步讨论。
【1】灵敏度 —在实践中使用 gan 的最大挑战是它们对结构和参数的敏感性。如果鉴别器或发生器比另一个好得太快,另一个将永远无法赶上。找到正确的组合可能非常具有挑战性。当我们构建新的 GAN 或 DC-GAN 时,通常建议遵循我们上面讨论的经验法则。
【2】收敛 —没有证据表明 GAN 会收敛。当我们找到正确的参数时,GANs 在大多数时候表现得很好,但除此之外没有任何保证。网络变得越复杂,收敛变得越挑剔,超参数选择变得越困难。
【3】大样本 —尝试训练 GAN 发生器产生大图像,如 1000x1000 像素,可能会有问题。问题是,对于大图像,鉴别器很容易从真实图像中分辨出生成的假货。许多像素会导致误差梯度,从而导致发生器的输出向几乎随机的方向移动,而不是更接近匹配输入。在大图像上训练 GANs 的最佳程序是:
- 首先调整图像的大小:512x512,128x128,64x64,…,4x4。
- 然后构建一个小的生成器和鉴别器,每个都只有几层卷积。
- 用 4x 4 的图像进行训练,直到它做得很好。
- 给最终的网络增加几个卷积层,现在用 8 乘 8 的图像训练它们。
- 同样,当结果良好时,在每个网络的末端添加更多的卷积层,并在 16 乘 16 的图像上训练它们。
这个过程比我们从一开始只用全尺寸图像训练花费的时间要少得多(并且更有可能收敛)。
【4】计算 —计算能力、内存、处理大数的时间已经很高了。运行网络直到生成逼真的图像可能需要数小时或数天的训练,即使使用高性能 GPU 也是如此(这就是为什么我们的最终图像与一些论文中的图像相比是不合标准的)。对于更复杂的网络、更大的训练集和更大的图像,这种情况会进一步加剧。
【5】模式崩溃 —这可能是我们在 GANs 中遇到的最令人沮丧的问题(除了 10 小时的训练时间)。比方说,我想使用 GAN 来生成像 NVIDIA 下面这样的人脸(如下所示)。

Generated images from NVIDIA GAN.
然而,当训练我们的网络时,生成器不知何故找到了一个图像来欺骗鉴别器。

然后,生成器每次都能独立于输入噪声产生图像。鉴别器会一直说是真的,所以生成器已经完成了目标,停止学习。然而,问题是生成器制作的每个样本都是相同的。
这个反复产生一个成功输出的问题被称为模态崩溃。
当系统产生相同的少量输出或它们的微小变化时,这种情况更为常见。这被称为部分模态折叠。
解决方案是:
- 用一个附加项来扩展鉴频器的损失函数,以测量产生的输出的多样性。
- 如果输出都相同或几乎相同,鉴别器可以将较大的误差分配给结果。
- 发电机将多样化,因为该动作将减少误差
最终意见
这是一篇很长的文章,但我希望你现在对这些网络如何工作有一个很好的直觉。作为对在本文中走到这一步的奖励,这里有一条来自原始 GAN 的创作者 Ian Goodfellow 的推文,向你展示了一个有趣的情况,当 GAN 在猫图像上训练时可能会失败,其中一些是模因!

如果您渴望了解更多内容,可以继续学习本教程的第 2 部分:
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…
towardsdatascience.com](/comprehensive-introduction-to-turing-learning-and-gans-part-2-fd8e4a70775)
教程的第 3 部分也可以在这里找到:
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…
medium.com](https://medium.com/@matthew_stewart/gans-vs-autoencoders-comparison-of-deep-generative-models-985cf15936ea)
下面是一些进一步的阅读材料,包括代码、互动练习和一些在 GANs 领域的开创性论文。如果您想了解更多信息、资源等,请随时联系我。
时事通讯
关于新博客文章和额外内容的更新,请注册我的时事通讯。
丰富您的学术之旅,加入一个由科学家,研究人员和行业专业人士组成的社区,以获得…
mailchi.mp](https://mailchi.mp/6304809e49e7/matthew-stewart)
进一步阅读
在 COLAB 中运行 BigGAN:
更多代码帮助+示例:
- https://www . jessicayung . com/explaining-tensor flow-code-for-a-卷积神经网络/
- https://lilian Weng . github . io/lil-log/2017/08/20/from-GAN-to-wgan . html
- https://py torch . org/tutorials/初学者/dcgan_faces_tutorial.html
- 【https://github.com/tensorlayer/srgan
- https://junyanz.github.io/CycleGAN/https://affinelayer.com/pixsrv/
- https://tcwang0509.github.io/pix2pixHD/
有影响力的论文:
- https://arxiv.org/pdf/1511.06434v2.pdf
- 瓦瑟斯坦甘(WGAN)https://arxiv.org/pdf/1701.07875.pdf
- 条件生成对抗网(CGAN)https://arxiv.org/pdf/1411.1784v1.pdf
- 使用拉普拉斯金字塔对抗网络的深度生成图像模型https://arxiv.org/pdf/1506.05751.pdf
- 使用生成式对抗网络的照片级单幅图像超分辨率https://arxiv.org/pdf/1609.04802.pdf
- 使用循环一致对抗网络的不成对图像到图像翻译https://arxiv.org/pdf/1703.10593.pdf
- InfoGAN:通过信息最大化生成对抗网络的可解释表示学习https://arxiv.org/pdf/1606.03657
- https://arxiv.org/pdf/1704.00028.pdf
- 对 Wasserstein GANs (WGAN-GP)的改进培训https://arxiv.org/pdf/1701.07875.pdf
- 基于能量的生成对抗网络(EBGAN)https://arxiv.org/pdf/1609.03126.pdf
- 使用学习的相似性度量(VAE-甘)对像素之外的内容进行自动编码【https://arxiv.org/pdf/1512.09300.pdf
- 对抗性特征学习(https://arxiv.org/pdf/1605.09782v6.pdf 甘比)
- 堆叠生成敌对网络(SGAN)https://arxiv.org/pdf/1612.04357.pdf
- StackGAN++使用堆叠式生成对抗网络进行现实图像合成https://arxiv.org/pdf/1710.10916.pdf
- 通过对抗训练(SimGAN)从模拟和无监督图像中学习https://arxiv.org/pdf/1612.07828v1.pdf
GANs 中的高级主题
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是你新的最好的朋友。
生成对抗网络是过去 10 年机器学习中最有趣的想法——扬·勒村,脸书人工智能研究中心主任
本教程的第 1 部分可以在这里找到:
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…
towardsdatascience.co](/comprehensive-introduction-to-turing-learning-and-gans-part-1-81f6d02e644d)
这是 3 部分教程的第二部分,专门介绍如何使用生成式对抗网络创建深度生成模型。这是对之前关于变型自动编码器的自然延伸(在这里可以找到)。我们将看到 GANs 在很大程度上优于变分自动编码器,但是众所周知很难使用。

Taxonomy of deep generative models. This article’s focus is on GANs.
在本教程中,我们将探讨以下主题:
- gan 的简短回顾
- GAN 应用
- 甘斯的问题
- 其他类型的 gan
- 建立形象甘
- 编码教程
本文的大部分将是关于编码 GANs 以及对一些更高级的 GANs 实现的广泛介绍。
所有相关代码现在都可以在我的 GitHub 存储库中找到:
GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码、管理项目和构建…
github.com](https://github.com/mrdragonbear/GAN-Tutorial)
gan 的简短回顾
对于那些已经读过第一部分的人来说,你可以跳过这一部分,因为我将会用我之前用熊猫做的例子来重复 gan 是如何机械地工作的。
最近(在过去 5 年左右)引入了生成对抗网络作为训练生成模型的新方法,即创建能够生成数据的模型。它们由两个“对立”模型组成:一个生成模型 G 捕捉数据分布,一个判别模型 D 估计样本来自训练数据而不是 G 的概率。 G 和 D 都可以是非线性映射函数,例如多层感知器
在生成敌对网络(GAN)中,我们有两个神经网络,它们在零和游戏中相互对抗,其中第一个网络(生成器)的任务是愚弄第二个网络(鉴别器)。生成器创建“假的”数据片段,在这种情况下,是熊猫的图像,试图欺骗鉴别器将图像分类为真的熊猫。我们可以迭代地改进这两个网络,以产生逼真的图像,并执行一些其他非常酷的应用程序,其中一些我们将在本教程的后面讨论。

最初,图像可能相当明显是伪造的,但随着网络变得更好,区分真实和伪造的图像变得更加困难,即使对人类来说也是如此!

这两个网络可以被认为是代表应用于噪声或真实数据的任意复杂函数的黑盒。发生器的输入是一些随机噪声,产生假图像,鉴别器的输入是假样本和来自真实数据集的样本。然后,鉴别器对所提供的图像 z、是真实图像 D(z)=1 还是伪图像 D(z)=0 做出二元判定。

为了训练这两个网络,我们必须有一个损失函数,每个网络的损失函数取决于第二个网络。为了训练网络,我们执行反向传播,同时冻结另一个网络的神经元权重。

如果您在理解正在发生的事情上有困难,我建议您回去阅读第 1 部分,以便更好地了解这个训练过程是如何工作的。

重要的是要注意,一般来说,鉴别器和生成器网络可以是任何形式的映射函数,例如支持向量机。GANs 的这种概括有时被称为图灵学习。然而,实际上,神经网络是最常见的,因为它们是任意非线性函数的广义函数逼近器。
在进入更高级的主题之前,我现在将讨论 GANs 的一些最酷的应用程序,以及旨在生成名人脸和动漫角色的 GANs 代码演练。
氮化镓应用
在这一节中,我将简要介绍我在数据科学研究过程中发现的一些最有趣的 GANs 应用。最常见的话题是:
- (条件)合成 —这包括字体生成、文本 2 图像以及 3D 对象生成。
- 数据增强 —旨在减少对标记数据的需求(GAN 仅用作增强另一个模型的训练过程的工具)。
- 风格转移和操纵— 人脸老化、绘画、姿态估计和操纵、修补和混合。
- 信号超分辨率— 人为提高图像的分辨率。
条件合成
条件合成在我看来相当不可思议。可以说条件合成最吸引人的应用是 Image2Text 和 Text2Image,它能够将一幅图片翻译成文字(整张图片说了一千个字的大话..),或者反之亦然。
这种技术的应用意义深远,如果不仅仅是对医学图像进行分析以描述图像的特征,从而消除医生对图像的主观分析(这实际上是我目前正在尝试用乳房 x 线照片研究自己的一个附带项目的一部分)。
另一方面也是如此(假设我们给我们的网络一些它能理解的单词),图像可以完全由单词生成。以下是用于执行文本到图像条件合成的多条件 GAN (MC-GAN)的示例:

Implementation of MC-GAN for translating words into images. Source: https://arxiv.org/pdf/1902.06068.pdf
数据扩充
这一个是相当自明的。gan 学习数据生成分布,就像我们在研究 VAEs 时所做的那样。因此,我们能够从我们的生成器中采样并产生额外的样本,我们可以使用这些样本来扩充我们的训练集。因此,GANs 提供了一种额外的方法来执行数据增强(除了旋转和扭曲图像之外)。
风格转移和操作
风格转移是不言自明的。它包括将一幅图像的“风格”转移到另一幅图像上。这非常类似于神经风格转移,我将在以后的文章中讨论。

Example of style transfer using a GAN.
这对于背景风景来说非常有效,类似于图像过滤,除了我们可以操作实际图像的各个方面(比较上面图像中的输入和输出的云)。
甘在动物或水果等其他物体上表现如何?

看起来很好!如果我对甘斯一无所知,我可能会认为,马的形象,事实上,是一匹斑马后的风格转移。
我们还可以改变场景来操纵季节,这对于视频游戏和虚拟现实模拟器来说是一种潜在的有用操纵。

我们也可以使用 GANs 来改变景深。

我们也可以相对容易地操纵图画,把它们变成真实的物体(然而,这可能需要比我目前所拥有的更多的绘画技能)。


自动驾驶汽车以类似于下图的视图查看世界,这允许以更具对比性的方式查看对象(通常称为语义地图)。

我们甚至可以做风格转换来渲染图像,就像侠盗猎车手的环境一样(对于任何粉丝来说)。
我们也可以用同样的方式把白天变成黑夜。
这是足够的风格转移和图像处理。这有许多很好的应用,但我们已经看到了该技术的一些恶意使用,人们冒充政治人物,创建虚假的电话交谈,电子邮件等。
这实际上是一个这样的问题,美国军方正在开发一个新的法医学领域,以研究视频和媒体的类似例子,以确定它们是否是由 GANs 生成的(哦,我的天,我们生活在一个什么样的世界..).
图像超分辨率
图像超分辨率是指从低分辨率图像恢复高分辨率图像的过程,是计算机视觉和图像处理中一类重要的图像处理技术。
一般来说,这个问题非常具有挑战性,并且本质上是不适定的,因为总是有多个 HR 图像对应于单个 LR 图像。

各种深度学习方法已经被应用于处理 SR 任务,从早期的基于卷积神经网络(CNN)的方法(例如,SRCNN)到最近的使用 GAN 的有前途的 SR 方法(例如,SRGAN)。一般来说,使用深度学习技术的 SR 算法家族在以下主要方面彼此不同:不同类型的网络架构、不同类型的损失函数、不同类型的学习原理和策略等。

这样做的过程实际上相当复杂,所以我不会在本文中涉及太多的细节(尽管如果读者感兴趣,我将在以后讨论这个问题)。
有关 GANs 风格转变的全面概述,请参见此处。
甘斯的问题
在上一篇文章中,我们讨论了 GANs 的一些最基本的问题,主要是所需的大量计算能力、在大图像上训练的难度、灵敏度以及模式崩溃。
我想重申这些问题,因为训练一个甘是非常困难和耗时的。GAN 还有很多其他问题,以至于在学术界(至少在哈佛),有一个流传的笑话,如果你想培养一个 GAN,你会选择一个不知情且天真的研究生来为你做这件事(我一直是这样一个笑话的受害者)。
振动
当发生器和鉴别器共同寻找平衡时,可能发生振荡,但是模型更新是独立的。没有收敛的理论保证,事实上,结果可能会振荡。

对此的解决方案是广泛的超参数搜索,这有时可能需要人工干预。我不建议对已经运行了 10 个小时的东西进行大范围的超参数搜索。
消失渐变
鉴别器可能变得太强而不能为发生器提供信号。我这么说是什么意思?如果发生器变得太好太快(很少反过来发生),发生器可以学会持续愚弄鉴别器,并且不再需要学习任何东西。

A GAN exhibiting the vanishing gradient problem.
解决方案不是预先训练鉴别器,或者降低它的学习速率。还可以改变每次迭代中生成器/鉴别器的更新次数(正如我在第 1 部分中所推荐的)。
很容易看出 GAN 何时已经收敛,因为两个网络的稳定将出现在中间地带的某个地方(即其中一个网络不占优势)。

The minimax expression for Nash equilibrium in a GAN.
模态崩溃
发生器可以折叠,以便总是产生相同的样本。当生成器被限制在小的子空间并因此开始生成低分集的样本时,这可能发生。

Five of the above generated images look identical, and several others seem to occur in pairs.
对此的解决方案是通过小批量鉴别(将整批提交给鉴别者进行审查)或通过特征匹配(即,为低多样性增加一个生成器惩罚)或使用多个 gan 来鼓励多样性。
评估指标
这是我在上一篇文章中没有提到的。gan 仍然在非常定性的基础上进行评估——本质上,这个图像看起来好吗?定义适当的、有点客观的指标是一个惊人的挑战。一台“好”的发电机看起来怎么样?

对于这一点没有确定的解决方案,它仍然是一个活跃的研究领域,并且是非常特定的领域。强分类模型通常用于判断生成样本的质量。两个常用的分数是初始分数,和 TSTR 分数(合成训练,真实测试)。
Jonathan Hui 有一篇相当全面的文章,解释了用于评估 GAN 性能的最常用指标,您可以在此处找到:
在 GANs 中,生成器和鉴别器的目标函数通常衡量它们做得有多好…
medium.com](https://medium.com/@jonathan_hui/gan-how-to-measure-gan-performance-64b988c47732)
在下一节中,我将概述学术领域中出现的一些最重要的 gan 类型。
其他类型的 GANs
出现了许多其他类型的 GAN 来处理特定领域的问题以及不同类型的数据(例如,时间序列、图像或普通 csv 样式的数据)。
我将在本节讨论的 GAN 类型有:
- 瓦瑟斯坦·甘
- CycleGAN
- 有条件的 GAN(之前已简要讨论过)
瓦瑟斯坦·甘
在我看来,这是甘最重要的类型,所以要注意!
使用标准 GAN 公式,我们已经观察到训练极不稳定。鉴别器通常提高得太快,生成器无法跟上,这就是为什么我们需要调节学习速率或在两个网络中的一个上执行多个历元。为了得到一个像样的输出,我们需要仔细的平衡,即使这样,模式崩溃是相当频繁的。
信息来源: Arjovsky,m .,Chintala,s .和 Bottou,l .,2017。瓦瑟斯坦·甘。arXiv 预印本 arXiv:1701.07875。
一般来说,生成模型寻求最小化真实分布和学习分布之间的距离(距离就是一切!).Wasserstein(也称为 EM,运土机)距离,在非正式的术语中,指的是当分布被解释为在一个区域内堆积一定数量的泥土的两种不同方式时d .wasser stein 距离是将一堆变成另一堆的最小成本;其中成本假定为移动的灰尘量乘以移动的距离。
不幸的是,在这种情况下精确的计算是很难的。然而,我们可以使用 CNN 来近似 Wasserstein 距离。这里,我们重用了鉴别器,它的输出现在是无界的。我们定义对应于 Wasserstein 损失的定制损失函数:

这里的想法是什么?我们可以对一种类型做尽可能大的预测,对其他类型做尽可能小的预测。
瓦瑟斯坦论文的作者声称:
- 训练期间稳定性更高,不需要仔细平衡发生器和鉴别器。
- 有意义的损失度量,与样品质量密切相关。
- 模态崩溃很少见。
在 Keras 实现 Wasserstein GAN 的技巧。
- 保持鉴频器输出无界,即应用线性激活。
- 使用较小的权重进行初始化,以避免从一开始就遇到剪裁问题。
- 记得运行足够的鉴别器更新。这在 WGAN 设置中至关重要。
- 您可以使用 Wasserstein 代理丢失实现。
- 通过实现您自己的 Keras 约束来裁剪鉴别器权重。

这是一个复杂的主题,实现 Wasserstein GAN 不是一项简单的任务。如果你有兴趣在个人项目中追求其中之一,我推荐你阅读我之前提到的原始论文。
CycleGAN
还记得我们看到的第一张马与斑马互换的照片吗?那是一辆自行车。CycleGANs 将样式转移到图像。这本质上与执行神经类型转移的想法相同,我将在以后的文章中介绍。
举个例子,想象一下拿一个著名的,比如金门大桥的图片,然后从另一个图像中提取风格,这个图像可能是一幅名画,然后按照所说的名画的风格重新绘制大桥的图片。
作为我的神经转移学习文章的引子,这里有一个我以前做过的例子。

Combining the style of the famed “Great Wave off Kanagawa” with the Chicago skyline.
将 GAN 应用于这些类型的问题相对简单,它本质上是图像重建。我们使用第一网络【G】将图像 x 转换为 y 。我们用另一个深度网络 F 逆过程重建图像。然后,我们用一个均方误差 MSE 来指导 G 和 F 的训练。

这里的不同之处在于,我们实际上并不关心重建图像,我们试图混合两种图像的风格。在 GAN 实现中,鉴别器 D 被添加到现有设计中,以指导发生器网络更好地执行。 D 作为训练样本和生成图像之间的评论家。通过这种批评,我们使用反向传播来修改生成器,以产生解决由鉴别器识别的缺点的图像。在这个问题中,我们引入一个鉴别器 D 来确定 Y 是否像梵高的画。

CycleGANs 将图片从一个域传输到另一个域。在真实图像和梵高画作之间转换画面。我们建立了三个网络。
- 生成器 G 将真实图像转换为梵高风格的图片。
- 生成器 F 将梵高风格的图片转换为真实图像。
- 一个鉴别器 D 用来鉴别真实的或生成的梵高画。
对于相反的方向,我们只需反转数据流,并建立一个额外的鉴别器来识别真实图像。
下面显示了一个创建斑马/马图像的实现示例。

条件 GAN
与 VAEs 一样,GANs 可以简单地进行调整,以生成特定模式的数据。在这里可以找到关于有条件的 GANs 的很好的参考文件。

如果生成器和鉴别器都以一些额外信息 c 为条件,我们可以将 GAN 的生成模型扩展为条件模型。这个 c 可以是任何种类的辅助信息,例如类别标签或来自其他模态的数据。我们可以通过将 c 作为附加输入层馈入鉴频器和发生器来执行调理。
在生成器中,先前的输入噪声 p(z) 和 c 被组合在联合隐藏表示中,并且对抗训练框架允许在如何组成该隐藏表示方面有相当大的灵活性。在鉴别器中, x 和 c 作为输入提供给鉴别功能。
GANs 故障排除
对于那些喜欢总结的人来说,这只是我们到目前为止讨论过的所有故障排除方法的一个简要概述。
【1】车型。确保模型定义正确。您可以通过训练一个普通的图像分类任务来单独调试鉴别器。
【2】数据。将输入正常化为[-1,1]。在这种情况下,确保使用 tanh 作为治疗仪的最终激活。
【3】噪音。尝试从正态分布(非均匀)中采样噪声矢量。
【4】正常化。在可能的情况下使用 BatchNorm,并以单独的小批量发送真假样品。
[5]激活。用 LeakyRelu 代替 Relu。
【6】平滑。应用标签平滑以避免在更新鉴别器时过度自信,即将真实图像的目标设置为小于 1。
【7】诊断学。持续监控梯度的大小。
【8】消失渐变。如果鉴别器变得太强(鉴别器损耗= 0),尝试降低其学习率或更频繁地更新发生器。
现在让我们进入有趣的部分,实际上建立一个 GAN。
甘建立形象
正如我们已经讨论了几次,训练一个甘可以令人沮丧和时间密集。我们将在 Keras 中完成一个简单的例子。这些结果只是在概念验证的层面上,以增进理解。在代码示例中,如果您不仔细调整参数,您将不会超出图像生成的这个级别(见下文)太多:

网络获取一个图像【H,W,C】并输出一个【M】的向量,或者是类分数(分类)或者是量化照片真实感的单个分数。可以是任何图像分类网络,例如 ResNet 或 DenseNet。我们使用简约的定制建筑。

取一个噪声【N】的矢量,输出一个【H,W,C】的图像。网络必须执行合成。同样,我们使用非常简约的定制架构。

在 Keras 中正确定义模型非常重要,这样各个模型的权重就可以在正确的时间固定下来。
[1]定义鉴别器模型,并编译它。
[2]定义生成器模型,无需编译。
[3]定义由这两个部分组成的整体模型,在编译之前将鉴别器设置为不可训练:

In the simplest form, this is all that you need to do to train a GAN.
训练循环必须手动执行:
[1]从训练集中选择 R 个真实图像。
[2]通过对大小为 N 的随机向量进行采样来生成 F 假图像,并使用生成器从它们预测图像。
[3]使用 train_on_batch 训练鉴别器:分别调用该批次的 R 实像和 F 假像,地面真值分别为 1 和 0。
[4]对大小为 N 的新随机向量进行采样。
[5]使用目标为 1 的 train_on_batch 在新向量上训练完整模型。这将更新生成器。
编码实现
现在,我们将在著名的 CelebA 数据集上使用 Keras 以代码格式完成上述极简实现。如果您对代码的构造方式感到困惑,您可能需要参考上面的过程,尽管我会尽最大努力向您介绍这一点。
完整的代码实现可以在我相应的 GitHub 库中免费获得。
请注意,在这段代码中,我没有以任何方式对此进行优化,并且我忽略了一些经验法则。我将在第 3 部分中对此进行更多的讨论,这将涉及到对 GAN 代码的更深入的讨论。
进口
首先,我们导入必要的包。
**import** **keras**
**from** **keras.layers** **import** *
**from** **keras.datasets** **import** cifar10
**import** **glob**, **cv2**, **os**
**import** **numpy** **as** **np**
**import** **matplotlib.pyplot** **as** **plt**
%matplotlib inline
**from** **IPython.display** **import** clear_output
全局参数
(在我看来)最好是在开始时指定这些参数,以避免以后的混乱,因为这些网络可能会变得相当混乱和复杂。
SPATIAL_DIM = 64 *# Spatial dimensions of the images.*
LATENT_DIM = 100 *# Dimensionality of the noise vector.*
BATCH_SIZE = 32 *# Batchsize to use for training.*
DISC_UPDATES = 1 *# Number of discriminator updates per training iteration.*
GEN_UPDATES = 1 *# Nmber of generator updates per training iteration.*
FILTER_SIZE = 5 *# Filter size to be applied throughout all convolutional layers.*
NUM_LOAD = 10000 *# Number of images to load from CelebA. Fit also according to the available memory on your machine.*
NET_CAPACITY = 16 *# General factor to globally change the number of convolutional filters.*
PROGRESS_INTERVAL = 80 *# Number of iterations after which current samples will be plotted.*
ROOT_DIR = 'visualization' *# Directory where generated samples should be saved to.*
**if** **not** os.path.isdir(ROOT_DIR):
os.mkdir(ROOT_DIR)
准备数据
我们现在做一些图像预处理,以使图像正常化,并绘制一幅图像,以确保我们的实现工作正常。
**def** plot_image(x):
plt.imshow(x * 0.5 + 0.5)X = []
*# Reference to CelebA dataset here. I recommend downloading from the Harvard 2019 ComputeFest GitHub page (there is also some good coding tutorials here)*faces = glob.glob('../Harvard/ComputeFest 2019/celeba/img_align_celeba/*.jpg')
**for** i, f **in** enumerate(faces):
img = cv2.imread(f)
img = cv2.resize(img, (SPATIAL_DIM, SPATIAL_DIM))
img = np.flip(img, axis=2)
img = img.astype(np.float32) / 127.5 - 1.0
X.append(img)
**if** i >= NUM_LOAD - 1:
**break**
X = np.array(X)
plot_image(X[4])
X.shape, X.min(), X.max()
定义架构
Keras 格式的架构相当简单。最好以块的形式编写代码,使事情尽可能简单。
首先,我们添加编码器模块部分。注意这里我们使用了“相同的”填充,这样输入和输出的维度是相同的,还有批处理规范化和泄漏 ReLU。Stride 通常是可选的,leaky ReLU 参数的大小也是可选的。我放在这里的值没有优化,但确实给了我一个合理的结果。
**def** add_encoder_block(x, filters, filter_size):
x = Conv2D(filters, filter_size, padding='same')(x)
x = BatchNormalization()(x)
x = Conv2D(filters, filter_size, padding='same', strides=2)(x)
x = BatchNormalization()(x)
x = LeakyReLU(0.3)(x)
**return** x
接下来是鉴别器本身——注意我们如何回收编码器块段,并逐渐增加滤波器大小,以解决我们之前讨论的针对(大)图像进行训练的问题(对所有图像都这样做的最佳实践)。
**def** build_discriminator(start_filters, spatial_dim, filter_size):
inp = Input(shape=(spatial_dim, spatial_dim, 3))
*# Encoding blocks downsample the image.*
x = add_encoder_block(inp, start_filters, filter_size)
x = add_encoder_block(x, start_filters * 2, filter_size)
x = add_encoder_block(x, start_filters * 4, filter_size)
x = add_encoder_block(x, start_filters * 8, filter_size)
x = GlobalAveragePooling2D()(x)
x = Dense(1, activation='sigmoid')(x)
**return** keras.Model(inputs=inp, outputs=x)
现在是解码器模块段。这次我们执行的是卷积层的相反操作,即反卷积。请注意,为了便于实现,步长和填充是相同的,我们再次使用了批处理规范化和泄漏 ReLU。
**def** add_decoder_block(x, filters, filter_size):
x = Deconvolution2D(filters, filter_size, strides=2, padding='same')(x)
x = BatchNormalization()(x)
x = LeakyReLU(0.3)(x)
**return** x
现在构建生成器,注意这次我们使用解码器模块并逐渐减小滤波器大小。
**def** build_generator(start_filters, filter_size, latent_dim):
inp = Input(shape=(latent_dim,))
*# Projection.*
x = Dense(4 * 4 * (start_filters * 8), input_dim=latent_dim)(inp)
x = BatchNormalization()(x)
x = Reshape(target_shape=(4, 4, start_filters * 8))(x)
*# Decoding blocks upsample the image.*
x = add_decoder_block(x, start_filters * 4, filter_size)
x = add_decoder_block(x, start_filters * 2, filter_size)
x = add_decoder_block(x, start_filters, filter_size)
x = add_decoder_block(x, start_filters, filter_size)
x = Conv2D(3, kernel_size=5, padding='same', activation='tanh')(x)
**return** keras.Model(inputs=inp, outputs=x)
训练
现在我们已经建立了网络架构,我们可以概述培训过程,这可能是人们容易混淆的地方。我想这大概是因为更多的函数中有函数中的函数吧。
**def** construct_models(verbose=**False**):
*# 1\. Build discriminator.*
discriminator = build_discriminator(NET_CAPACITY, SPATIAL_DIM, FILTER_SIZE)
discriminator.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=0.0002), metrics=['mae'])
*# 2\. Build generator.*
generator = build_generator(NET_CAPACITY, FILTER_SIZE, LATENT_DIM)
*# 3\. Build full GAN setup by stacking generator and discriminator.*
gan = keras.Sequential()
gan.add(generator)
gan.add(discriminator)
discriminator.trainable = **False** *# Fix the discriminator part in the full setup.*
gan.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=0.0002), metrics=['mae'])
**if** verbose: *# Print model summaries for debugging purposes.*
generator.summary()
discriminator.summary()
gan.summary()
**return** generator, discriminator, gan
本质上,我们上面所做的是设计一个基于我们的全局参数创建 GAN 模型的函数。注意,我们编译的是鉴别器,而不是生成器,但是我们最终会将 GAN 作为一个整体来编译。
另外,请注意,我们已经将鉴别器设置为不可训练,这是人们在构造 GANs 时经常忘记的事情!
这一点如此重要的原因是,你不能同时训练两个网络,这就像试图校准多个变量变化的东西,你会得到零星的结果。在训练任何单个网络时,您需要为模型的其余部分进行固定设置。
现在完整的模型已经建立,我们可以开始训练了。
**def** run_training(start_it=0, num_epochs=1000):# Save configuration file with global parameters config_name = 'gan_cap' + str(NET_CAPACITY) + '_batch' + str(BATCH_SIZE) + '_filt' + str(FILTER_SIZE) + '_disc' + str(DISC_UPDATES) + '_gen' + str(GEN_UPDATES)
folder = os.path.join(ROOT_DIR, config_name)
**if** **not** os.path.isdir(folder):
os.mkdir(folder)# Initiate loop variables avg_loss_discriminator = []
avg_loss_generator = []
total_it = start_it # Start of training loop
**for** epoch **in** range(num_epochs):
loss_discriminator = []
loss_generator = []
**for** it **in** range(200):
*# Update discriminator.*
**for** i **in** range(DISC_UPDATES):
*# Fetch real examples (you could sample unique entries, too).*
imgs_real = X[np.random.randint(0, X.shape[0], size=BATCH_SIZE)]
*# Generate fake examples.*
noise = np.random.randn(BATCH_SIZE, LATENT_DIM)
imgs_fake = generator.predict(noise)
d_loss_real = discriminator.train_on_batch(imgs_real, np.ones([BATCH_SIZE]))[1]
d_loss_fake = discriminator.train_on_batch(imgs_fake, np.zeros([BATCH_SIZE]))[1]
*# Progress visualizations.*
**if** total_it % PROGRESS_INTERVAL == 0:
plt.figure(figsize=(5,2))
*# We sample separate images.*
num_vis = min(BATCH_SIZE, 8)
imgs_real = X[np.random.randint(0, X.shape[0], size=num_vis)]
noise = np.random.randn(num_vis, LATENT_DIM)
imgs_fake = generator.predict(noise)
**for** obj_plot **in** [imgs_fake, imgs_real]:
plt.figure(figsize=(num_vis * 3, 3))
**for** b **in** range(num_vis):
disc_score = float(discriminator.predict(np.expand_dims(obj_plot[b], axis=0))[0])
plt.subplot(1, num_vis, b + 1)
plt.title(str(round(disc_score, 3)))
plot_image(obj_plot[b])
**if** obj_plot **is** imgs_fake:
plt.savefig(os.path.join(folder, str(total_it).zfill(10) + '.jpg'), format='jpg', bbox_inches='tight')
plt.show()
*# Update generator.*
loss = 0
y = np.ones([BATCH_SIZE, 1])
**for** j **in** range(GEN_UPDATES):
noise = np.random.randn(BATCH_SIZE, LATENT_DIM)
loss += gan.train_on_batch(noise, y)[1]
loss_discriminator.append((d_loss_real + d_loss_fake) / 2.)
loss_generator.append(loss / GEN_UPDATES)
total_it += 1
*# Progress visualization.*
clear_output(**True**)
print('Epoch', epoch)
avg_loss_discriminator.append(np.mean(loss_discriminator))
avg_loss_generator.append(np.mean(loss_generator))
plt.plot(range(len(avg_loss_discriminator)), avg_loss_discriminator)
plt.plot(range(len(avg_loss_generator)), avg_loss_generator)
plt.legend(['discriminator loss', 'generator loss'])
plt.show()
上面的代码对你来说可能看起来很混乱。上面的代码中有几项内容有助于管理 GAN 的运行。例如,第一部分设置一个配置文件并保存它,这样您就能够在将来引用它,并确切地知道您的网络的架构和超参数是什么。
还有进度可视化步骤,它实时打印笔记本电脑的输出,以便您可以访问 GAN 的当前性能。
如果忽略配置文件并进行可视化,代码相对简单。首先,我们更新鉴别器,然后我们更新生成器,然后我们在这两个场景之间迭代。
现在,由于我们对函数的巧妙使用,我们可以用两行代码运行这个模型。
generator, discriminator, gan = construct_models(verbose=**True**)
run_training()
剩下唯一要做的就是等待(可能几个小时或几天),然后测试网络的输出。
训练好的甘的样本
现在,在等待网络完成训练之后,我们能够从网络中获取大量样本。
NUM_SAMPLES = 7
plt.figure(figsize=(NUM_SAMPLES * 3, 3))
**for** i **in** range(NUM_SAMPLES):
noise = np.random.randn(1, LATENT_DIM)
pred_raw = generator.predict(noise)[0]
pred = pred_raw * 0.5 + 0.5
plt.subplot(1, NUM_SAMPLES, i + 1)
plt.imshow(pred)
plt.show()

Samples from trained GAN
瞧啊。这是我们第一次实现基本的 GAN。这可能是构建全功能 GAN 的最简单方式。
最终评论
在本教程中,我介绍了一些更高级的 GAN 主题、它们的架构和它们当前的应用,还介绍了一个简单 GAN 的编码实现。在本教程的最后部分,我们将比较 VAEs,GAN 的性能,以及为了生成动画图像而实现的 VAE-GAN。
感谢您的阅读!如果您想继续学习本教程的第 3 部分,可以在下面找到:
想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…
medium.com](https://medium.com/@matthew_stewart/gans-vs-autoencoders-comparison-of-deep-generative-models-985cf15936ea)
时事通讯
关于新博客文章和额外内容的更新,请注册我的时事通讯。
丰富您的学术之旅,加入一个由科学家,研究人员和行业专业人士组成的社区,以获得…
mailchi.mp](https://mailchi.mp/6304809e49e7/matthew-stewart)
进一步阅读
在 COLAB 中运行 BigGAN:
更多代码帮助+示例:
- https://www . jessicayung . com/explaining-tensor flow-code-for-a-卷积神经网络/
- https://lilian Weng . github . io/lil-log/2017/08/20/from-GAN-to-wgan . html
- https://py torch . org/tutorials/初学者/dcgan_faces_tutorial.html
- https://github.com/tensorlayer/srgan
- https://junyanz.github.io/CycleGAN/https://affinelayer.com/pixsrv/
- https://tcwang0509.github.io/pix2pixHD/
有影响力的论文:
- DCGANhttps://arxiv.org/pdf/1511.06434v2.pdf
- 瓦瑟斯坦甘https://arxiv.org/pdf/1701.07875.pdf
- 条件生成对抗网(CGAN)https://arxiv.org/pdf/1411.1784v1.pdf
- 使用对抗网络的拉普拉斯金字塔(LAPGAN)的深度生成图像模型https://arxiv.org/pdf/1506.05751.pdf
- 使用生成式对抗网络(SRGAN)的照片级单幅图像超分辨率https://arxiv.org/pdf/1609.04802.pdf
- 使用循环一致对抗网络的不成对图像到图像翻译(Cycle gan)https://arxiv.org/pdf/1703.10593.pdf
- InfoGAN:通过信息最大化生成对抗网络的可解释表示学习【https://arxiv.org/pdf/1606.03657
- https://arxiv.org/pdf/1704.00028.pdf 的 DCGAN
- 瓦瑟斯坦·甘斯(WGAN-GP)的强化训练https://arxiv.org/pdf/1701.07875.pdf
- 基于能量的生成对抗网络(EBGAN)https://arxiv.org/pdf/1609.03126.pdf
- 使用学习的相似性度量(VAE-甘)对像素之外的内容进行自动编码https://arxiv.org/pdf/1512.09300.pdf
- 对抗性特征学习(https://arxiv.org/pdf/1605.09782v6.pdf甘比
- 堆叠生成敌对网络(SGAN)https://arxiv.org/pdf/1612.04357.pdf
- StackGAN++使用堆叠式生成对抗网络进行现实图像合成https://arxiv.org/pdf/1710.10916.pdf
- 通过对抗训练(SimGAN)从模拟和无监督图像中学习https://arxiv.org/pdf/1612.07828v1.pdf
全面的 TensorFlow.js 示例
这篇文章解释了如何在浏览器中使用 TensorFlow.js 运行机器学习。它涵盖了用于模型训练、迁移学习和预测功能的 TensorFlow.js API。有一个现场演示应用程序。它有助于预测业务报告执行等待时间。

Source: Pixabay
我已经实现了一个应用程序,其中包括 TensorFlow.js API 的使用。首先,我将向您介绍应用程序的功能,然后将深入实施细节。这个应用程序实现了一个业务报表执行时间预测用例(这次是用 JavaScript 实现的),在我之前的帖子中解释过——用 Keras 和 TensorFlow 实现报表时间执行预测。
对于模型训练,我使用 50 个历元(数据以 10 个为一批进行处理),学习率设置为 0.001。神经网络基于两个处理层和一个输出层。模型训练过程在浏览器中运行:

Model training process
应用程序已在此处部署并可用:
模型被训练来预测业务报告执行的预期等待时间。实现是基于这本优秀的书— 深度学习与 JavaScript 和多元回归示例— Boston Housing 中的解释和材料完成的。
功能
第一次打开应用程序时,需要对模型进行训练。模型定型后,将保存到本地 indexeddb。浏览器重新打开后,该模型将仍然可以从 indexeddb 中获得,您可以选择它来重用或训练新模型(以前保存的模型将被替换)。
训练过程完成后,它将打印一组显示模型质量的变量:
- 基线丢失:这是根据训练数据计算的平均平方误差。平方根值:237 秒。这意味着如果我们总是假设预测的平均值,误差范围将是 237 秒。
- 平均时间:为训练数据计算的平均时间
- 最终车组损失:训练期间计算的损失
- 最终验证-设定损失:通过验证在训练期间计算的损失
- 测试集损失:针对测试数据为训练模型计算的损失
- 预测偏差(秒):测试集损失的平方根值。这说明模型错误。模型训练得很好,只有 4.5 秒的错误,比基线丢失好得多

Model training
对以下数据运行预测功能—结果 514 秒:

Predict
将时间段更改为下午-预测时间将增加到 573 秒。这意味着模型根据训练数据进行了正确的训练,报告在下午运行的时间更长:

Predict
增加参数的数量。参数越多,需要处理的数据越少,报告运行速度应该越快。该模型通过返回更快时间的预测来证实这一点:

Predict
让我们更改报告 ID 和参数数量。观察预测时间:

Predict
现在我们将进行迁移学习——用新的目标重新训练模型(在这个例子中,为了简单起见,我只使用一行数据,但我建议用多行数据重新训练模型)。以下数据未包含在训练集中,这是一个新目标。呼叫训练法——它应该运行得非常快。测试集的损失可能会比基于原始训练的损失更严重(对本练习来说没问题),因为我们使用的是以前的测试集数据,而这些数据与新目标没有直接关系。新目标— 400 秒,对于报告 ID = 1(假设特定用户的时间不同于培训中的时间,并且该用户可以更新模型)。再培训(当我们在现有模型之上进行培训时——迁移学习)结果:

Transfer learning
重新训练模型后,对与之前相同的数字运行预测,您将看到预测结果将被调整并等于我们用于重新训练的目标:

Predict
现在,将报告 ID 更改为我们最初使用的 ID,将报告参数的数量以及时隙更改为原始值。您将会看到,现在我们预测的时间更短,这是由于最近的模型再培训,其中更短的时间被设定为一个目标:

Predict
尝试更改参数并查看结果。
实施
应用程序结构非常简单。所有逻辑都在 appController.js 中实现,UI 在 index.html 中实现。Web app 是用 Oracle JavaScript 库— Oracle JET 实现的。要在本地运行应用程序,请遵循以下两个步骤:
- 使用 NPM 安装 Oracle JET:NPM install-g @ Oracle/ojet-CLI
- 进入应用程序并运行:ojet restore
- 运行应用程序:ojet serve
appController.js 中定义了一个监听器。当应用程序加载时,这个监听器被调用,它负责加载数据,将其转换为张量并计算基线。
数据通过助手模块 data.js 加载。我使用 Papaparse 库解析 CSV。原始数据集基于四列:

Data
我注意到模型不能直接从这些数据中得到很好的训练。报表 ID 和 daypart 列需要数据转换。这些列是分类的,并通过创建尽可能多的新列进行转换,因为存在唯一的分类值:
var features = results['data'].map(report => ({
report_params: parseFloat(report.report_params),
report_1: parseFloat(report.report_id) === 1 ? 1 : 0,
report_2: parseFloat(report.report_id) === 2 ? 1 : 0,
report_3: parseFloat(report.report_id) === 3 ? 1 : 0,
report_4: parseFloat(report.report_id) === 4 ? 1 : 0,
report_5: parseFloat(report.report_id) === 5 ? 1 : 0,
day_morning: parseFloat(report.day_part) === 1 ? 1 : 0,
day_midday: parseFloat(report.day_part) === 2 ? 1 : 0,
day_afternoon: parseFloat(report.day_part) === 3 ? 1 : 0,
}));
这种数据转换有助于运行更精确的训练。我使用 1200 行数据进行训练,300 行数据进行测试。在将数据分成训练数据集和测试数据集之前,请确保对数据进行洗牌。我正在为这个任务使用 helper 函数,取自波士顿住房示例应用程序。
使用 TensorFlow.js 函数 tensor2d 将数据数组转换为张量:
tensors.rawTrainFeatures = tf.tensor2d(dataHelper.trainFeatures);
tensors.trainTarget = tf.tensor2d(dataHelper.trainTarget);
tensors.rawTestFeatures = tf.tensor2d(dataHelper.testFeatures);
tensors.testTarget = tf.tensor2d(dataHelper.testTarget);
TensorFlow.js 模型由两个处理层和一个输出层构成,用于返回预测值:
const model = tf.sequential();
model.add(tf.layers.dense({
inputShape: [dataHelper.trainFeatures[0].length],
units: 25,
activation: 'sigmoid',
kernelInitializer: 'leCunNormal'
}));
model.add(tf.layers.dense({
units: 25,
activation: 'sigmoid',
kernelInitializer: 'leCunNormal'
}));
model.add(tf.layers.dense({ units: 1 }));
一旦构建了模型,编译它并运行 fit 函数来训练模型。我建议使用验证分割选项,这样在培训期间可以验证培训的质量:
model.compile({ optimizer: tf.train.sgd(LEARNING_RATE), loss: 'meanSquaredError' });await model.fit(tensors.trainFeatures, tensors.trainTarget, {
batchSize: BATCH_SIZE,
epochs: NUM_EPOCHS,
shuffle: true,
validationSplit: 0.2,
callbacks: {
onEpochEnd: async (epoch, logs) => {
函数 fit 提供 onEpochEnd 回调,我们可以记录训练进度并将数据推送到 UI。
训练完成后,通过对照测试数据运行模型来评估模型。从返回的数字中取平方根,这将是基于当前模型训练的以秒为单位的容许误差(如果结果不容许,尝试通过改变神经网络层的结构来重新训练模型,尝试调整训练参数):
result = model.evaluate(tensors.testFeatures, tensors.testTarget, { batchSize: BATCH_SIZE });
testLoss = result.dataSync()[0];
最后保存模型。有多个保存模型的选项,你甚至可以把它推送到服务器。在本例中,我将模型保存在浏览器的本地 indexeddb 中:
await model.save('indexeddb://report-exec-time-model');
模型训练好之后,我们就可以运行预测功能了。我正在从 indexeddb 中的保存状态加载回模型,构造输入张量并执行 TensorFlow.js predict:
model = await tf.loadLayersModel('indexeddb://report-exec-time-model');input = [{
report_id: parseFloat(self.reportIdP()),
report_params: self.reportParamsP(),
day_part: parseFloat(self.reportExecSlotP())
}];convertInputToTensor(input);res = model.predict(tensors.inputFeatures);
score = res.dataSync()[0];
最后一步—转移学习,使用额外数据对现有模型进行重新训练(在这种情况下,数据形状是相同的,我们针对新目标进行训练)。为了使重新训练更有效,我们必须冻结原始模型的部分层。这样,先前训练的模型权重将不会受到影响,并且模型训练将运行得更快。通常,您应该使用多个新元素来运行培训,在这种情况下,为了简单起见,我使用一个新条目来运行培训。
通过将可训练属性设置为假可以冻结模型层:
model = await tf.loadLayersModel('indexeddb://report-exec-time-model');model.layers[0].trainable = false;model.compile({
loss: 'meanSquaredError',
optimizer: tf.train.sgd(LEARNING_RATE)
});model.summary();input = [{
report_id: parseFloat(self.reportId()),
report_params: self.reportParams(),
day_part: parseFloat(self.reportExecSlot()),
exec_time: self.reportExecTime()
}];convertInputToTensor(input, 'Y');
在现有模型的基础上使用新数据运行拟合函数:
await model.fit(tensors.inputFeatures, tensors.inputTarget, {
batchSize: BATCH_SIZE,
epochs: NUM_EPOCHS,
shuffle: true,
callbacks: {
onEpochEnd: async (epoch, logs) => {
资源:
- 关于 GitHub 回购的代码
- 直播 app—https://regressiontfjs-node.herokuapp.com/
大型制药公司的计算分析
深入分析
调查“捐赠”给医生对药物处方的影响

Photo by JOSHUA COLEMAN on Unsplash
“我会记住,我治疗的不是发烧表,不是癌变,而是一个生病的人,他的病可能会影响这个人的家庭和经济稳定。如果我要充分照顾病人,我的责任就包括这些相关的问题。”
每年,在期待已久的毕业庆典中,希波克拉底誓言的话语——医学专业人士几个世纪以来做出的古老承诺——回荡在各国医学院的大厅里。这些神圣而受人尊敬的机构将数千名聪明的毕业生送到美国各地,以减轻痛苦,改变生活,在某些情况下,有希望拯救他们。
但是,如果这些毕业生进入一个越来越受金钱驱动、越来越不受关怀驱动的体系,那该怎么办呢?如果这些聪明的年轻男女是大型制药公司运作的大规模赚钱计划的目标呢?如果大型制药公司更多地瞄准特定的医学专业,那会怎么样?为什么会这样?
在 20 世纪 60 年代,美国人对医疗行业的信任度达到了空前的高度。当时,超过四分之三的美国人对医学领袖非常有信心。今天,只有 34%的美国人这样做。随着数千家制药公司每年向全国各地的医生“捐赠”数十亿美元,美国人对医疗行业的信任度应该会更低吗?
研究问题
有时间的话,看看左边的视频。说真的,看着点。该视频揭示了数百万美国人不知道的看似腐败的系统。每年,制药公司以“教育”为名,向医师提供 数十亿 美元的“捐款”
如果您到了这一步还没有观看视频,我们恳求您返回并点击播放。如果不是为了宣布医疗/制药领域普遍、持续的腐败,至少看看约翰·奥利弗带来的喜剧价值。眼前的问题是复杂的、多层次的和多方面的;这个视频会让你对它的理解产生奇迹。
现在让我们回到手头的问题上来。
从局外人的角度来看,这整个系统听起来似乎是无辜的——公司教医生关于他们的药物,这样医生就可以对他们给病人开的药物做出明智的决定。这有什么不好?
但是,当 FDA 对仿制药和品牌药的要求出现时,这个无辜的框架就变得模糊了:

FDA requirements for generic vs. branded drugs
等一下。你是说学名药在法律上要求在各方面与品牌药本质上相同?当学名药平均比品牌药便宜 80-85%时,为什么会有人选择品牌药而不是学名药呢?
嗯,这种看似错误的行为有三种可能的情况:
- 一家制药公司开发了一种专利药物,并拥有该药物的专利垄断权。这些专利的长度受到国会的限制,并且应该最终让位于相同的、便宜得多的仿制药。嗯,除非发生这样的事:
“……拥有品牌药品的公司将通过拒绝交出生产仿制药所需的样品和安全协议来阻止仿制药进入市场。这种策略人为地延长了制药公司的专利,违背了国会鼓励仿制药的意图。"-openmarketsininstitute
我们没有足够的时间、天数、周数(你可以想象)去冒险进入专利垄断和大型制药公司的国会游说努力的兔子洞。但是,如果你真的很好奇,可以看看这个链接。
2.一名患者特别是在药店要求购买一种品牌药,而不是一种相同的仿制药(可能是因为电视广告或他们的邻居 Karen*的推荐,Karen *服用立普妥并对其深信不疑)。
3。医生专门给开一种品牌药,而不是仿制药。
场景#3 是事情变得真正令人兴奋的地方,也是我们研究的重点。
我们看着场景#3,问自己 为什么。
还记得开头引用的希波克拉底誓言吗?当医生发誓要了解疾病对患者“经济稳定性”的影响时,他们为什么会开品牌药而不是仿制药?
再加上奥利弗先生在上文指出的数十亿美元的“捐赠”系统,这就产生了一个引人注目、具有社会影响力和重大意义的研究问题:
研究问题:大型制药公司的捐赠会影响医生给病人开的特定药物吗?
通过我们的研究,我们开始了解大型制药公司直接支付给医生的费用可能会对你、我和凯伦最终服用的处方药以及我们支付的价格产生的影响。
研究过程
因此,我们耳听八方,倾听医疗腐败的最轻微的迹象,开始了一场勇敢的探索,以揭示广泛的社会弊端,并由此引发广泛的社会变革。
至少我们是这么想象的。
我们“撞上”的直接障碍与可用数据的绝对规模、数据集的格式以及处方填写过程中涉及的微小法律/程序复杂性直接相关。
但是现在没时间了…我们会在路上讨论的。
数据集
我们根据三个关键数据集构建了我们的研究:
- 公开支付 —接受 2013 年至 2017 年间对医生的捐赠
- 医疗保险提供者利用和支付数据—2013 年至 2017 年作为医疗保险一部分的所有处方
- 处方药、制造商及其价格
下载每个数据集后,我们很快意识到我们正在处理的数据的巨大规模。仅开放支付数据表就包含数千万行。医疗保险提供者利用率数据表包含超过 1 亿行。
数据是彻底的,这是肯定的。但对我们的目的来说是可管理的?不完全是。

Photo by Alexander Sinn on Unsplash
我们决定将我们的研究细化到日历年 2016 。这大大地把数据精简到了一个稍微容易管理的状态。开放支付数据被限制在大约 1090 万行,而医疗保险提供者利用数据(从这里开始称为“处方数据集”)被限制在大约 2430 万行。即使这些受限的数据集对于我们微不足道的 8GB 内存来说也太多了,无法在合理的时间内处理;计算时间和内存使用量在我们尝试使用数据集的每个过程中都被放大了,特别是在数据集之间移动时(例如,从开放支付数据集中提取一个接受捐赠的医生,并在处方数据集中搜索同一医生的出现次数)。为了帮助解决这些问题,我们使用了Daskdata frames 来“延迟”加载数据(直到对该数据执行了计算,才将数据加载到内存)。
我们研究过程的第二步是清理数据。这一步花费了惊人的时间和精力,我想这证明了数据科学花费了多少时间来定制一个或多个数据集以满足您的特定研究需求。我们清理数据的方法包括:从所有三个数据集中删除不必要的(出于我们的目的)列,合并我们需要合并的某些列(例如,名字、姓氏),处理 NaN 值,删除区分大小写,合计每个医生的支付总额,以及一系列其他整理任务。
主要发现
清理之后,我们开始从数据中对我们的研究问题做出有意义的结论。我们知道我们仍然在处理一个相当大的数据集,所以我们决定专注于数据的目标子集:医疗专业。捐赠给哪些医学专科最多,为什么?
为了进行调查,我们重点关注了 2013 年至 2016 年间向医生捐赠超过 1 亿美元的 20 家制药公司。

然后,我们查询开放支付数据集,按医生专业对捐赠进行分组,然后找到每个医疗专业的平均捐赠价值。我们希望这一过程能准确揭示出最大的制药公司把大部分资金花在了哪个医学专业上。
这里有一个很好的图表来说明这些发现(使用平均值):

Note: Shows top-20 receiving specialties
当你侧着头观看剧情时,很明显儿科 重症医学在平均捐赠价值方面击败了其他专业。我们发现这非常令人惊讶!我们期望矫形外科在平均值和总价值方面是最高的;骨科手术在捐赠的总净值方面排名第一(见下图),而在捐赠的平均价值方面仅排名第五。
这是使用总捐赠价值的图:

Note: Shows top-20 receiving specialties
为什么会这样?为什么最大的制药公司会如此强烈地针对儿科急救护理专家?
我们最初对这些结果的论证集中在儿科急诊专业的性质上。这些医生专门护理处于紧急情况下的儿童。因为紧急事件通常是时间敏感的,这些医疗专业人员可能有完全酌处权决定儿童服用何种处方药。在这种情况下,我们的意思是,在儿科急救护理专家倾向于的特定情况下,父母可能会将完全的决定权交给可信任的专家,而不会争论是否要开品牌药或仿制药。当吉米因为手臂骨折而尖叫时,妈妈和爸爸可能不会为一种药物的品牌名称而争吵;他们只是希望尖叫停止,他们美丽的男婴再次成为 5 岁健康的形象。
如果是这种情况,并且制药公司理解这一点,那么大型制药公司让儿科急救护理专家以任何必要的成本开出所有品牌的药物是有意义的,包括进行大量捐赠。
但是,当你可以查询数据并做出有根据的结论时,为什么要做出没有根据的结论呢?
然后,我们通过直接检查儿科急救护理专家和整形外科医生开出的特定药物来结束这个循环。我们这样做是为了调查这些大量捐赠是否真的影响了医生开的特定药物。
上面,我们确定儿科急救护理专家从大型制药公司获得了大量现金(好吧,是的,这些“捐赠”被标为“演讲费”和“所有费用都由我支付的医学会议旅行”,但在这一点上,我们只需滚动),现在让我们看看这些现金是否有所回报。
为了对此进行调查,我们:
- 儿科急诊专家处方数据集索引
- 找到每位儿科急救护理专家开出的所有 Medicare 部分处方的结果集

Step 2: grouping prescriptions by prescriber. Prescribers are members of specialty most targeted for donations (pediatric emergency care or, separately, orthopedic surgery)
3.遍历每个医生的处方集,如果处方是品牌药而不是仿制药,则标记处方

Step 3: Branded drugs prescribed by orthopedic surgeons
4.交叉引用“药物和制造商”数据集中的品牌药物,查看是否由被调查的前 20 家公司之一制造。

Step 4: Branded drugs prescribed by pediatric emergency care specialists that were manufactured by the companies investigated
5.如果医生开了一种品牌药物,我们随后使用医生的名字查询开放支付数据集,以查看特定医生是否接受了制药公司的捐赠。通过这样做,我们将能够在某种程度上检验药品捐赠对药物处方的可能影响。

Step 5: Orthopedic surgeons who prescribed branded drugs, those branded drugs were manufactured by the companies in question, and they were donated to by those companies. Includes value of donation.
6.对研究的第二个专业重复步骤 1-5:矫形外科。
好吧……那你发现了什么?
至少可以说,我们的结果令人惊讶。
在儿科急救护理专家和整形外科医生身上执行上述过程后,我们发现:
2016 年品牌儿科急诊专科处方数量:4
开品牌药和的儿科专家接受药品制造商捐赠的人数(被调查的 20 家公司之一):0
2016 年品牌骨科手术处方数量:568
开品牌药和的儿科专家接受药品制造商捐赠的人数(被调查的 20 家公司之一):4
嗯……真令人失望。
也可能不是,看你怎么看!如果这些发现成立,看来药品捐赠在影响医生处方决策方面并不像假设的那样有效。嘿,这对所有人来说都是好消息。
我们的研究表明,在 2016 年,对于从 20 家支出最高的制药公司获得最高平均捐赠值和最高净捐赠值的专科(儿科急诊和骨科手术),相关性最小。简单来说,捐赠似乎并没有明显影响处方中的特定药物。
结论和警告
但是,沉住气。

Photo by Kai Pilger on Unsplash
这些发现伴随着一些我们想要解决的重要警告。在进行研究的许多时候,我们遇到了数据的问题和限制。
首先也是最重要的,处方数据集的性质。
该处方数据集仅限于联邦政府 2016 年的联邦医疗保险 D 部分支出。虽然这一系列数量庞大,但它只代表了 2016 年美国完成的总处方的一小部分。医疗保险是一个政府项目,因此,它的记录对公众是免费的。这就是我们使用它们的原因。这个研究项目并不是决定性的,因为它只是检查了整个药品市场的一小部分样本。
小规模的抽样来模拟大规模的行为可能对一些研究有用,但是医疗保险的某些特征进一步搅浑了我们的研究水域。因为医疗保险是一个政府项目,它带有限制和局限;这些限制在“D 部分”级别变得更加精炼和严格。“D 部分”是指医疗保险计划的特定子集,仅涵盖六类药物:抗抑郁药、抗精神病药、抗惊厥药、抗逆转录病毒药物(艾滋病治疗)、免疫抑制剂和抗癌药物。这立即给我们的儿科调查带来了问题,并可能解释为什么我们发现如此少量的品牌儿科药物处方。
此外,医疗保险的资金受到联邦政府的限制。因为政府的目标是将医疗保险支出保持在最低水平,同时仍然向公众提供必要的药物,他们可能会压倒性地将已完成的处方限制为非专利药物,而不是品牌药物。这也可以解释为什么我们在研究中很少发现品牌药物处方。
最后,不是所有的医生都接受医保。医生必须选择接受医疗保险,当平均而言,医疗保险只支付私人健康保险的 80%时,它似乎越来越不受欢迎。已经对利润最大化感兴趣的医生可能不会选择接受医疗保险,因此不会出现在我们的数据集中。这些医生可能是从制药公司获得最多捐赠的人。
另一方面,我们的研究仅限于调查前 20 家捐赠制药公司。我们做出这个决定是因为它使数据更容易管理和使用。然而,这些公司可能全部或大部分专注于医疗市场的特定领域(即,它们生产儿科医生和整形外科医生永远不会使用的药物)。因为我们遗漏了几十家其他较小的公司,我们可能错过了儿科/骨科药物的制造商。
所有这些观察都指向一个真理,这个真理在这个研究过程的每一步都在我们的脑海中回荡:感知到的药品捐赠问题极其微妙和模糊。在项目开始时,我们从来没有想象过我们必须考虑制药公司、医生、药剂师和患者可能做出的实际决定的次数——所有这些都是为了对我们面前的数据做出有意义和有效的声明。问题的规模、围绕公开披露某些支付/处方而非其他支付/处方的法律问题,以及使用多个数据集(每个数据集都以不同的方式对其数据进行编码,并且由不同的团队创建/管理)使该研究项目具有挑战性…但也很有趣。
但是为什么调查这些真的很重要呢?
如果你已经到了这一步,还没有找到自己对上述问题的答案,下面是我们的看法,简单明了:
生命可能危在旦夕。
如果医生给病人开错了药(因为偏向大制药公司),疾病可能得不到充分的控制,症状可能蔓延,治疗可能无效。人们可能会死。
进一步研究的课题
我们的项目仅仅触及了使用 OpenPayments 和 Medicare 部分数据集的可能性的表面。这些数据集是彻底的,强大的,令人印象深刻的,研究的机会是无限的。
我们讨论了调查大型制药公司捐赠对研究出版物的影响。例如,如果 Dave 医生接受了一家生产药物 A 的制药公司的捐赠,那么他以后写/合著一篇吹捧药物 A 的积极作用的研究文章的可能性有多大?这个问题很重要,因为成千上万的医生和患者依靠医学期刊来了解市场上出现的新药的好处/坏处。基于这些文章,医生开出特定的药物。
为了回答这个问题,我们可以使用自然语言处理来按语气(积极/支持或消极/不支持)对研究文章进行编码,并搜索开放支付数据集以查找对文章作者的捐赠。然后,我们可以使用情绪分析分数以及对开放支付数据集的查询来使用线性回归。
此外,我们对使用机器学习来基于医生的多个特征(例如,美国所在的州、从业年限等)预测医生可能收到的捐赠的预计金额感兴趣。).我们特别想调查大型制药公司捐赠的具体形式是否会影响处方偏见。如果 good ol' Dave 医生每年从 FakePharma,Inc .获得 150 份免费午餐,他是否更有可能开 FakePharma,Inc .的药,而不是每个周末 FakePharma,Inc .带他去参加医学会议?
我们使用的所有数据集都是免费使用/下载的;如果你对可能的进一步研究课题有任何想法,不要害怕投入进去!
我已经走了这么远,现在怎么办?
很棒的问题!这里有一些步骤,你可以马上采取,以更好地保护自己和你爱的人免受潜在的偏见处方。
- 我们恳请你在美国政府的开放支付网站或 ProPublica 的 Dollars for Docs 网站上查找你的医生,并认真考虑你对他/她的信任。
- 我们强调建议阅读进入同行评审的关于特定药物有效性的医学研究。不要仅仅依赖电视广告告诉你的或者前几天晚上凯伦推荐的炒鸡蛋。
做你自己的研究,并把研究带到你医生的办公室。首先,试着看看发表在 CDC 预防慢性疾病杂志或其他类似杂志上的发现。
对于那些永远读不够的人来说,这是额外的读物
3.加拿大啊,你也有这些问题!
5.类似的调查研究!—制药行业为医疗保险受益人赞助的膳食和医生处方模式
这里有一些主题音乐,你可以在拥护制药透明度和公平性的时候听听
这个项目是由我、西西佛·津加和查理·朱尼尔·奎宁与❤共同完成的。如果你有兴趣复制我们的研究项目来调查你自己的燃眉之急的大型制药公司的问题,这里有我们的 GitHub 和代码/文件!
*Karen 是本文中虚构的人物,仅用于解释/描述目的
计算生物学

(Image reproduced from: https://blog.f1000.com/2017/02/01/f1000prime-f1000prime-faculty-launch-bioinformatics-biomedical-informatics-computational-biology/)
计算生物学是数学、统计学和计算机科学的结合应用,用于解决基于生物学的问题。生物学问题的例子有:遗传学、进化论、细胞生物学、生物化学。[1]
介绍
最近的技术进步使我们能够存储数量惊人的数据。最初,【大数据】被感知为待解决的问题。事实上,我们已经到了能够存储太多数据却不能充分利用的地步。这推动了对数据科学和人工智能进步的需求。
如今,最初被认为是一个问题的东西,现在已经成为一扇通向创新世界的大门。大数据使计算机视觉和深度学习等许多研究领域蓬勃发展。这使得机器能够执行复杂的决策任务,并从原始数据中提取人类肉眼看不到的信息。
生物学是一门广泛使用生物数据库来尝试解决许多不同挑战的学科,例如理解疾病的治疗和细胞功能。生物数据的数据集可以由氨基酸序列、核苷酸、大分子结构等创建。
此外,计算机科学中经常使用的许多机器人系统和算法都是受生物复合体的启发。例如,深度学习神经网络在原理上受到人类大脑结构的启发。
计算生物学算法
计算生物学中使用的算法的一些例子是:
- 全局匹配
- 局部序列匹配
- 隐马尔可夫模型
- 群体遗传学
- 进化树
- 基因调控网络
- 化学方程式
全局匹配(也称为 Needleman-Wunsch 问题)和局部序列匹配(也称为 Smith-Waterman 问题)利用我们对一种生物的蛋白质的知识来了解更多关于其他生物的蛋白质。
马尔可夫模型用于模拟序列。在这些类型的模型中,事件发生的概率仅仅依赖于其先前的状态(例如,这种类型的模型可以用于对 DNA 序列建模)。隐马尔可夫模型(图 1)使用概率有限状态机,根据我们所处状态的概率,我们发出一个字母,然后转移到下一个状态。下一个状态可能等于初始状态。

Figure 1: Hidden Markov Model [2]
群体遗传学试图模拟进化。为此,它通常使用费希尔-赖特模型。该模型试图模拟在选择、突变和交叉条件下基因位置发生的情况。
进化树(图 2)可以基于某种形式的进化距离来创建。进化树主要有两种类型:基于距离的树和基于序列的树。进化树被用来解释不同物种之间的距离。

Figure 2: Evolutionary Trees [3]
基因调控网络由于生物体内不同蛋白质的相互作用而形成。不同的蛋白质相互控制,根据它们相互作用的性质,决定了细胞的类型。
化学方程式(图 3)终于可以用来描述基因调控网络背后的机制。反应速率取决于化学方程式中元素的浓度。

Figure 3: Chemical Equations [4]
用于生物预测的机器学习
在计算生物学中使用机器学习现在变得越来越重要(图 4)。目前的应用是基因组学(研究有机体的 DNA 序列)、蛋白质组学(更好地了解不同蛋白质的结构和功能)和癌症检测。

Figure 4: Machine Learning workflow in Biological Data Analysis [5]
研究人员证明,使用卷积神经网络(CNN)和计算机视觉,通过图像分类进行癌症检测,可以获得可观的分类精度[6]。
几个数据集可以在网上公开获得,以开始生物数据探索,一个例子可以是 xenabrowser.net。这些数据集由加州大学圣克鲁斯分校的 UCSC·谢轩提供。
联系人
如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:
文献学
[1]计算机科学学位中心。访问于:https://www . computersciencedegreehub . com/FAQ/what-is-computational-biology/,2019 年 5 月。
[2]隐马尔可夫模型。访问时间:https://en.wikipedia.org/wiki/Hidden_Markov_model,2019 年 5 月。
[3]蝙蝠和猿:自然历史博物馆的互动桌面游戏。迈克尔·s·霍恩等人访问:https://www . research gate . net/publication/254004984 _ Of _ BATs _ and _ APEs _ An _ interactive _ desktop _ game _ for _ natural _ history _ museums/figures?lo=1,2019 年 5 月。
[4]化学文献。4.1:化学反应方程式。访问:https://Chem . libre texts . org/Courses/bellar mine _ University/BU % 3A _ Chem _ 103 _(Christianson)/Phase _ 1% 3A _ Chemistry _ Essentials/4% 3A _ Simple _ Chemical _ Reactions/4.1% 3A _ Chemical _ Reaction _ Equations,2019 年 5 月。
[5]拥抱。计算生物学的深度学习。Christof Angermueller、Tanel Pä rnamaa 等人。访问时间:http://msb.embopress.org/content/12/7/8782019 年 5 月。
[6]胡子龙嘎,唐金山,等。基于图像的癌症检测和诊断的深度学习,一项调查。查阅于:https://www . science direct . com/science/article/ABS/pii/s 00313203183018452019 年 5 月。
计算机设计的人类——试管中的人工智能革命

忘掉自动驾驶汽车和声控扬声器吧:未来几年,人工智能最引人注目的效果将出现在一个非常不同的领域。
这些天来,总是有来自科学界的报告,它们的交叉联系和后果并不明显。最近的一个例子可以在最新一期的《自然》杂志上找到。它涉及一种称为白细胞介素-2 的蛋白质,理论上有望成为针对某些类型癌症的免疫疗法的非常有趣的新可能性。然而,白细胞介素-2 有一个主要缺点:虽然它是一种内源性物质,但大量使用时毒性很大。
“想象一下,在未来,由于拥有特权基因,更有钱的人会活得更长久、更健康。”
更稳定,危害更小,看起来完全不同
在《自然》杂志的文章中,丹尼尔-阿德里亚诺·席尔瓦和 20 多名同事报告了他们如何开发出一种设计版本的白细胞介素-2,减少这些不利的特性或使它们完全消失。它可以用来治疗小鼠的肠癌和皮肤癌。一种基于人体自身物质的人造蛋白质,可能在几年内用于癌症治疗,在实验室中创造,现在它正在到来:用一种“计算方法”。

设计师蛋白质是在复杂的迭代过程中产生的,在计算机上和实验室中交替进行。最终,它几乎看起来不像原来的,但毒性更低,甚至比原来的更稳定。
实现蛋白质计算的软件叫做 Rosetta。它的发明者是这篇文章的作者之一。华盛顿大学的大卫·贝克是这门新学科的先驱之一,这门学科可以被称为计算生物技术:使用复杂软件分析、预测和设计蛋白质结构。或者,换句话说:在这里,生物体的组成部分在计算机上被分解、预测、解释和构建。
折叠一次活鹤
近年来,Rosetta 在 CASP 竞赛的背景下被反复使用,这已经是几周前讨论的话题。比赛的焦点是哪一个团队最有能力从给定的 DNA 序列中预测三维蛋白质结构。一个非常复杂的任务,书呆子漫画家兰道尔·门罗曾经解释如下:
“你折过纸鹤吗?“现在想象一下,你必须弄清楚哪些褶皱是制作一只真正的活鹤所必需的”。
在最近的 CASP 比赛中,有一个来自场外的惊喜赢家。谷歌——或者更确切地说是字母表——的女儿 Deepmind 的十人团队迄今为止击败了这个学科的领导者。Deepmind 开发了一个名为 AlphaFold 的系统,该系统基于人工神经网络。这是在人工智能领域给我们带来快速进步的技术。

蛋白质文件夹的身份危机
正如遗传学家 Mohammed Al-Quraishi 在一篇值得注意的博客文章中所报道的那样,Deepmind 的胜利让国际蛋白质折叠界的许多明星陷入了危机。起初,许多同事担心 Deepmind 会因为一种别出心裁的新方法而失去专家。然后是救济:方法和工具是新的,但不是基本的方法。然后,他们开始诋毁成功,并将其归结为他们所使用的资源。
这让我想起了职业围棋选手对 Deepmind 的 AlphaGo 在比赛中首次战胜欧洲冠军的反应:嗯,毕竟没有那么伟大。都只是计算能力。反正真正的专业人士更好。后来发生的事情,现在已经尽人皆知。
人工智能现在可以做到这一点
Deepmind 团队并不是唯一一个将机器学习方法应用于此类任务的研究小组。谷歌子公司无疑比大学研究人员拥有更多的资金和计算能力。然而,这个结果的精髓在我看来是不同的:AI 现在可以做这样的事情。忘了拥有语音识别和自动驾驶汽车的扬声器吧:人工智能将很快带来生物技术领域可能最剧烈的变化。
DNA 是数据,蛋白质结构是数据。这里要解决的问题极其复杂,涉及多因果、复杂的关系、结构和模式。这些都是今天的人工智能,甚至是明天的人工智能可以有效处理的事情。学习系统不仅建立起来了,而且已经准备好了——如果你继续训练它们,并进一步强化它们,它们会继续改进。
一场我们还不敢梦想的科学革命
这种生物技术和机器学习的结合很可能会在未来几年引发一场我们连做梦都不敢想的科学革命。这场革命将要发生的学科并没有为此做好准备。哈佛大学的系统生物学家 Al-Quraishi 写道:“尽管在工业研究实验室中发挥着核心作用,但在软件和计算机科学方面具有专业知识的研究工程师几乎完全不在学术实验室中。”
因此,有可能一小群有才华的不合群者突然在几十年的研究传统中处于领先地位。不仅学术界对此感到惊讶,而且大型制药公司也感到惊讶——即使他们似乎睡着了,尽管他们有必要的资金将机器学习作为一种研究工具。
设计人的机器?
这种情况现在可能要改变了。辉瑞、默克、诺华等公司现在将为人工智能专家发布许多高薪职位的广告。然而,Deepmind 的人不会停止对这个久负盛名的东西的研究。
公司和大学将很快开始生产由学习机器开发或至少在学习机器的帮助下开发的设计师分子。接下来,机器将为基因治疗干预提出建议。那些生命能够被挽救或延长的病人会很高兴。
此外,在某个时候,可能是从地球上某个地方的秘密实验室,第一个设计师 DNA 将从机器中出来。
电脑说没有
如果你是《小英国》的粉丝,你可能会熟悉这篇文章的标题,因为在不同的草图中,一个人在台式电脑中输入一些数据,直到“电脑说不”。
我目前正在使用这些短视频中的一些作为一种有趣的方式来介绍我们今天在与人工智能系统合作时面临的主要挑战之一:算法透明。

everis AI ethics framework. All rights reserved.
可解释的人工智能,或 XAI ,是机器学习模型理解、信任和管理自动决策系统的必要条件。

Source: https://www.darpa.mil/program/explainable-artificial-intelligence
根据 DARPA 的说法,通过 XAI,“新的机器学习系统将有能力解释它们的基本原理,描述它们的优缺点,并传达对它们未来行为的理解。实现这一目标的策略是开发新的或改进的机器学习技术,这将产生更多可解释的模型。这些模型将与最先进的人机界面技术相结合,能够将模型转化为最终用户可以理解和有用的解释对话”。
对于人工智能可能对人类产生重大影响的任何关键系统,人工智能应允许用于:
- 为任何决策重新创建结果
- 了解用于训练模型的逻辑和数据
- 确保自动化决策的可追溯性和可审计性
如果我们的人工智能系统符合这些要求,我们将不仅确保可解释性,而且确保可追溯性,允许建立反馈循环,这有利于我们的人工智能系统,同时保护它们免受偏见等其他挑战,以及将我们从可解释的人工智能转移到可追溯和透明的人工智能。
最后,为了增加对人工智能系统的信任,我们需要以一种清晰、准确和可行的方式与我们的用户和利益相关者沟通。为了做到这一点, 将设计原则和团队视为我们人工智能产品生命周期的基础部分将是关键。
毫无疑问,信任是最有价值的商业商品,所以我们不能让一台计算机代表我们公司做出决定,而不理解其中的原因,也不能有效地与客户沟通。
如果你喜欢阅读这篇文章,请 考虑成为会员 ,在支持我和媒体上其他作者的同时,获得每个故事的完整访问权限。
计算机视觉——从 CNN 到面具 R-CNN 和 YOLO 之旅——第一部分
在本文中,我们将探索和了解不同计算机视觉算法 CNN 的架构和工作方式,基于区域的 CNN(R-CNN),快速 R-CNN,更快 R-CNN。在下一篇文章中,我们将探索面具 R-CNN 和 YOLO(你只看一次)

计算机视觉的目的是什么?
计算机视觉是人工智能的一个分支。它用于使计算机能够像人类视觉一样理解、识别和生成对数字图像的智能理解。
计算机视觉是干什么的?
利用计算机视觉,我们可以识别
- 图像中存在的特征如边缘检测。

Edge detection
- 对图像中存在的物体进行分类。给图像分配标签,就像识别图像中的猫和狗或对数字进行分类

Image classification of digits
- 图像分类和定位。这包括对图像进行分类,以及识别物体在边界框中的位置。

- 对象检测识别图像中出现的所有不同对象及其位置。围绕图像中出现的所有对象绘制一个边界框。检测是识别图像中存在的内容。定位是指物体在图像中的位置

Source: http://www.robots.ox.ac.uk/~tvg/publications/talks/fast-rcnn-slides.pdf
- 对象分割或语义分割在像素级检测图像中存在的所有对象。输出具有不同类别或对象的区域

Semantic Segmentation of two people riding a bike in front of a building
- 神经风格转移我们通过学习一幅图像的风格并将其应用于另一幅图像来生成一幅新图像

Neural Style Transfer
利用卷积神经网络(CNN)进行图像分类是如何工作的?
当我们观看图像时,我们扫描图像。我们可以从左到右或从上到下查看图像,以了解图像的不同特征。我们的大脑结合我们扫描的不同局部特征来对图像进行分类。这正是 CNN 的工作方式。
CNN 将输入作为图像“x”,这是一个具有不同颜色通道(红色、绿色和蓝色-RGB)的二维像素阵列。
我们对输入图像应用不同的滤波器或特征检测器来输出特征图。与输入图像相比,过滤器或特征检测器在空间上很小。这些滤波器贯穿输入图像的整个深度。
通过对卷积层应用非线性函数 ReLU,并行执行多次卷积。
多特征检测器识别不同的东西,如边缘检测,不同的形状,弯曲或不同的颜色等。

我们将池应用于卷积层。我们可以应用最小池、最大池或平均池。与最小或平均池相比,最大池功能提供了更好的性能。
汇集有助于平移不变性。平移不变性意味着当我们少量改变输入时,汇集的输出不会改变。
图像的不变性意味着,即使当图像被旋转、调整不同大小或在不同照明下观看时,一个对象也将被识别为相同的对象。
在下一步中,我们将合并层展平,以将其输入到全连接(FC)神经网络。
我们在全连接层的最终输出层中使用 softmax 激活函数进行多类分类。
对于二元分类,我们在全连接层的最终输出层中使用 sigmoid 激活函数。
CNN 的实力
CNN 用于
- 图像分类
- 使用包围盒的目标检测
CNN 的局限性
- 使用边界框进行对象检测,但一次只能检测一个对象
- 由于干扰,当多个物体在视野中时,效果不好。
那么我们如何识别一幅图像中存在的多个对象,并在所有不同的对象周围绘制边界框呢?
我们现在探索基于区域的 CNN,这将有助于解决图像中存在多个对象的问题,并在所有不同的对象周围绘制边界框。
基于区域的 CNN- R-CNN
R-CNN 用于分类,以及利用图像中存在的多个对象的边界框进行对象检测
R-CNN 是如何工作的?
R-CNN 工作的前提是,在给定的区域中,只有单个感兴趣的对象将占主导地位。
R-CNN 使用选择性搜索算法进行目标检测,以生成区域建议。
那么是什么在一幅图像中形成了一个区域呢?
图像中的区域可以通过以下方式识别
- 变化的颜色
- 不同的尺度
- 不同的纹理
- 不同外壳

Identifying different objection based on regions
图(a),勺子,碗都是不同比例的。图(b),小猫是基于颜色而不是纹理来区分的。图(c ),变色龙可以通过纹理来区分,但不能通过颜色来区分。图(d),车轮是汽车的一部分,但颜色或纹理不相似。它们是外壳的一部分。
什么是选择性搜索,我们将如何使用它来识别图像中的多个对象?
选择性搜索
- 使用自下而上的图像区域分组来生成从小到大的区域层次结构
- 目标是生成一小组高质量的对象位置
- 结合了细分和穷举搜索的最佳直觉。
- 图像分割利用图像的结构来生成对象位置
- 穷举搜索旨在捕捉所有可能的对象位置
选择性搜索与穷举搜索逐步工作
步骤 1: 生成初始子分割。我们生成尽可能多的区域,每个区域最多属于一个对象。
第二步:递归地将相似的区域合并成更大的区域。这里我们使用贪婪算法。
- 从该组区域中,选择两个最相似的区域。
- 将它们合并成一个更大的区域。
- 重复直到只剩下一个区域。
这就产生了一个依次增大的区域层次结构,正如我们所希望的那样

Selective search Algorithm to generate regions for object locations
步骤 3: 使用生成的区域产生候选对象位置。
现在我们知道了选择性搜索是如何工作的,让我们进入 R-CNN 的细节
R-CNN 将地区提案与 CNN 相结合。
什么是区域提案?
区域建议是检测器可用的候选检测的集合。CNN 在整个图像上运行滑动窗口,但是 R-CNN 只选择几个窗口。R-CNN 对一幅图像使用 2000 个区域。
区域建议运行一种称为分段算法的算法,该算法使用选择性搜索。
但是 R-CNN 中的物体检测是如何工作的呢?
- 使用选择性搜索提取大约 2000 个区域提案,生成独立于类别的区域提案。扭曲每一个提议。
- 扭曲区域建议被馈送到大型卷积神经 网络。 CNN 作为特征提取器,从每个区域提取固定长度的特征向量。在通过 CNN 之后,R-CNN 为每个区域提议提取 4096 维特征向量
- 将 SVM(支持向量机)应用于从 CNN 提取的特征。SVM 有助于对该区域中物体的存在进行分类。回归变量用于预测包围盒的四个值
对图像中的所有得分区域应用贪婪非最大抑制。
如果一个区域的交集(IoU)与大于学习阈值的较高得分选定区域重叠,则非最大抑制会拒绝该区域。

什么是贪婪非 Max 压制,我们为什么要用它?
我们的目标是用一个包围盒检测一个物体一次。但是,使用对象检测,我们可能会发现对相同对象的多次检测。非最大抑制确保只检测一次物体
要了解非最大抑制,我们需要了解 IoU。
并集上的交集— IoU
IoU 通过算法计算两个边界框的并集上的交集,即地面真实的边界框和预测框的边界框


当 IoU 为 1 时,这意味着预测的和真实的边界框完全重叠。
为了在图像中检测一次对象,非最大抑制会考虑 IoU >为 0.5 的所有边界框
如果我有多个 IoU 大于 0.5 的包围盒怎么办?
非最大抑制
- 非最大抑制将移除 IoU 小于或等于 0.5 的所有边界框
- 选取 IoU 值最高的边界框,并抑制其他边界框以识别同一对象
例如,如果我们有三个分别为 0.6、0.7 和 0.9 的矩形。为了让 IoU 识别下图中的车辆,非最大抑制将保留 IoU 为 0.9 的边界框,并将抑制剩余的 IoU 为 0.6 和 0.7 的边界框。
对于下图中的汽车,非最大抑制将保持 IoU 为 0.8,抑制或移除 IoU 边界框为 0.7

R-CNN 面临的最大挑战是培训缓慢且昂贵
R-CNN 的体系结构

是什么让 R-CNN 的训练又慢又贵?
- 我们基于选择性搜索为每幅图像提取 2000 个区域。
- 使用 CNN 提取每个图像区域的特征。对于 N 个图像,我们将有 N*2000 个 CNN 特征。
- R-CNN 的对象检测使用三种模型:
CNN 用于特征提取
用于识别物体的线性 SVM 分类器
收紧边界框的回归模型
那么我们如何让算法更加高效快速呢?
R-CNN 需要改进的地方很少
- 使用一个 ConvNet 处理图像一次,而不是对图像的每个区域使用 2000 个 con vnet。
- 使用单一模型提取特征、分类和生成边界框,不像 R-CNN 使用三种不同的模型
这一切都是在快速 R-CNN 中完成的。
快速 R-CNN
快速 R-CNN 是一个快速框架,用于对象分类和具有深度网络的对象检测
快速 R-CNN 的结构和工作原理

快速 R-CNN 网络将图像和一组对象提议作为输入。
与 R-CNN 不同,快速 R-CNN 使用单个深度 ConvNet 一次提取整个图像的特征。
我们还使用选择性搜索为图像创建一组 ROI(感兴趣区域)。感兴趣区域(RoI)层从特征图中提取一个固定长度的特征向量,用于对象检测。RoI 图层是只有一个金字塔等级的空间金字塔池图层的特例
全连接层(FC)需要固定大小的输入。因此,我们使用 ROI Pooling 层将用于对象检测的特征图的补丁扭曲到固定大小。
ROI pooling 层然后被送入 FC 进行分类以及定位。 RoI 池层使用最大池。它将任何感兴趣的有效区域内的要素转换成一个小的要素地图。
全连接层分支成两个同级输出层
- 一个具有对 K 个对象类的 softmax 概率估计,加上一个包罗万象的“背景”类
- 另一层带有一个回归器,为 K 个对象类中的每一个输出精确边界框位置的四个实数值。
R-CNN 和快速 R-CNN 的主要区别
- 快速 R-CNN 使用单一深度 ConvNet 进行特征提取。与 R-CNN 对图像的每个区域使用 2000 个 ConvNet 不同,单个深度 conv net 大大加快了图像处理的速度。
- 快速 R-CNN 使用 softmax 进行对象分类,而不是 R-CNN 中使用的 SVM。在异议分类方面,Softmax 略胜 SVM
- 快速 R-CNN 使用多任务丢失实现深度神经网络的端到端训练,提高了检测精度。

快速 R-CNN 使用选择性搜索作为发现感兴趣区域的建议方法,这是一个缓慢且耗时的过程。不适合大型真实数据集
更快的 R-CNN
更快的 R-CNN 不使用昂贵的选择性搜索而是使用区域建议网络。
这是一个单一、统一的目标检测网络
更快的 R-CNN 由两个阶段组成
- 第一阶段是提议区域的深度全卷积网络,称为区域提议网络(RPN)。RPN 模块作为统一网络的关注点
- 第二阶段是快速 R-CNN 检测器,使用 RoIPool 从每个候选框中提取特征,并执行分类和包围盒回归

Faster R-CNN
区域提案网络
区域提议网络将任意大小的图像作为输入,并输出一组矩形对象提议,每个提议都有一个客观分数。这是通过在卷积层生成的特征图上滑动一个小网络来实现的
RPN 与快速 R-CNN 对象检测网络共享计算。
从 RPN 生成的要素被提供给两个完全连接的同级图层-用于边界框的框回归图层和用于对象分类的框分类图层。
RPN 是高效的,每幅图像处理 10 ms 以生成 ROI。

Region Proposal Network
锚
锚点位于所讨论的滑动窗口的中心,并与比例和纵横比相关联。更快的 R-CNN 使用 3 个比例和 3 个纵横比,在每个滑动窗口产生 9 个锚。
锚点有助于平移不变性。
在每个滑动窗口位置,我们同时预测多个区域建议。每个位置的最大可能建议数表示为 k
Reg 层有 4k 个输出,对 k 个框的坐标进行编码,cls 层输出 2k 个分数,估计每个提议的反对或不反对的概率
快速 R-CNN 的结构和工作原理

快速 R-CNN 由 3 个不同的神经网络组成
- 使用深度卷积层从输入图像生成特征地图的特征网络
- 区域提议网络(RPN)用于识别不同的区域,其对于每个滑动窗口使用 9 个锚。这有助于平移不变性。RPN 生成许多被称为感兴趣区域(ROI)的边界框,这些边界框对于对象的存在具有高概率
- 检测网络是 R-CNN,它以来自卷积层和 RPN 网络的输入作为特征映射。这将生成对象的边界框和类
更快的 R-CNN 以图像作为输入,通过特征网络生成特征图。
RPN 使用来自特征网络的特征地图作为输入来生成对象提议的矩形框和对象性分数。
来自 RPN 的预测区域提议随后使用 RoI 池层进行整形。扭曲成固定的向量大小。
然后,扭曲的固定大小向量被馈送到两个完全连接的兄弟层,一个回归层用于预测边界框的偏移值,一个分类层用于对象分类
使用更快的 R-CNN
- 三维物体检测
- 基于部件的检测
- 实例分割
- 图像字幕
摘要
我们从一个简单的 CNN 开始,它用于图像分类和图像中单个物体的物体检测。
R-CNN 用于图像分类以及图像中多个对象的定位。
R-CNN 又慢又贵,所以快速 R-CNN 被开发成一种更快更有效的算法。R-CNN 和 Fast R-CNN 都使用选择性搜索来搜索图像中的区域。
快速 R-CNN 将 RPN(区域建议网络)与快速 R-CNN 一起用于多图像分类、检测和分割。
在下一篇文章中,我们将探索 YOLO 和面具 R-CNN。
参考文献:
http://vision . Stanford . edu/teaching/cs 231 b _ spring 1415/slides/ssearch _ schuyler . pdf
【https://arxiv.org/pdf/1406.4729.pdf
https://arxiv.org/pdf/1506.01497.pdf
【https://arxiv.org/pdf/1311.2524.pdf 号
http://www . cs . Toronto . edu/~ ting Wu Wang/semantic _ segmentation . pdf
http://www . cs . Toronto . edu/~ ting Wu Wang/semantic _ segmentation . pdf
https://IVI . fnwi . UVA . nl/ISIS/publications/2013/uijlingsijcv 2013/uijlingsijcv 2013 . pdf
http://www . robots . ox . AC . uk/~ tvg/publications/talks/fast-rcnn-slides . pdf
计算机视觉——从 CNN 到面具 R-CNN 和 YOLO 之旅——第二部分
这是探索和理解 YOLO 建筑和工作方式系列的第二篇文章(你只看一次)。
点击 CNN 上详细了解计算机视觉算法,基于区域的 CNN(R-CNN),快速 R-CNN,更快 R-CNN 。这是第 1 部分。
在下一篇文章中,我们将浏览 YOLO v3 的代码

YOLO——你只需看一次就是更好、更快、更准确的计算机视觉算法的答案。
你只需看一次(YOLO)图像,就能预测出哪些物体存在以及它们在哪里。
单个卷积网络同时预测多个边界框和这些框的类别概率。
YOLO 在全图像上训练,直接优化检测性能。这使得 YOLO 非常快速和准确
YOLO 的特色
- YOLO,一种对象检测算法同时发现图像网格中的所有对象
- 对完整图像使用单个卷积网络
- 与滑动窗口或基于区域的技术不同,YOLO 在训练和测试期间看到了整个图像,因此它隐式地编码了关于类及其外观的上下文信息。因此与快速的 R-CNN 相比,产生的背景错误不到一半。
- YOLO 使用整个图像的特征来预测每个包围盒。它还同时预测一幅图像的所有类的所有边界框。预测边界框和这些框的类别概率。
- 将检测视为回归问题
- 极其快速准确
YOLO 的工作

You Only Look Once-YOLO
- YOLO 拍摄了一幅图像,并将其分割成一个 SxS 网格。每个网格单元仅预测一个对象
- 在每个网格上应用图像分类和定位
- 如果对象的中心落入网格单元,则该网格单元负责检测该对象
- 每个网格单元预测 B 个边界框,这些框具有置信度得分
- 置信度得分反映了模型对盒子包含对象的置信度,以及它认为盒子预测的准确性。如果对象不存在,则置信度得分将为零。置信度预测表示预测框和任何地面真实框之间的 IOU。Pr(Object)∫IOU 真值 pred——预测框和实际真值之间的交集(IOU)
- 当网格单元中出现对象时的边界框
- C 类概率
了解 YOLO 的输出

- pc 定义网格中物体的存在,是概率。当一个物体存在时,它包含概率,当网格中没有物体存在时,它们的 pc 将为零
- bx,by,bh,bw 指定物体出现时的边界框。 bx,by 是相对于网格单元边界的盒子中心。bw、bh 是相对于整个图像的宽度和高度
- c1、c2、c3 等。代表班级。维 c 等于类的数量。类别概率取决于包含对象 P(类别|对象)的网格单元。如果网格单元中存在对象,则存在的类的值为 1,其他类的值为 0
输出的维度会是什么?
将图像分成一个 S × S 的网格。
每个网格单元预测 B 个边界框、这些框的置信度和 C 类概率。
每个边界框由 5 个预测组成:bx、by、bw、bh 和置信度
输出维度将 S×S×(B∫(1+4)+C)张量
例如,如果我们将图像划分为 7×7 的网格,每个网格单元预测 2 个边界框,并且我们有 20 个标记类,则输出将是 7×7 ×( 2 * 5+20)= 7×7×30 张量
地面真值包围盒和预测包围盒之间的 IoU 是什么?
并集上的交集— IoU
IoU 通过算法计算两个边界框的并集上的交集,即地面真实的边界框和预测框的边界框


当 IoU 为 1 时,这将意味着预测的和真实边界框完全重叠。
为了在图像中检测一次对象,非最大抑制会考虑 IoU >为 0.5 的所有边界框
如果我有多个 IoU 大于 0.5 的包围盒怎么办?
非最大抑制
- 非最大抑制将移除 IoU 小于或等于 0.5 的所有边界框
- 选择 IoU 值最高的边界框,并抑制其他边界框,以识别同一物体
例如,如果我们有三个分别为 0.6、0.7 和 0.9 的矩形。为了让 IoU 识别下图中的车辆,非最大抑制将保留 IoU 为 0.9 的边界框,并将抑制剩余的 IoU 为 0.6 和 0.7 的边界框。
对于下图中的汽车,非最大抑制将保持 IoU 为 0.8,抑制或移除 IoU 边界框为 0.7

YOLO v1 网络设计
YOLO 的网络架构受 GoogLeNet 图像分类模型的启发
YOLO 的检测网络有 24 个卷积层,后面是 2 个全连接层。
YOLO 使用 1 × 1 缩减层,然后是 3 × 3 卷积层,而不是谷歌网络使用的初始网络
交替的 1 × 1 卷积层减少了来自前面层的特征空间。
在 ImageNet 分类任务中,卷积层以一半的分辨率(224 × 224 输入图像)进行预训练,然后以两倍的分辨率进行检测

YOLO 对最后一层使用线性激活函数,对所有其他层使用泄漏 ReLU。
YOLO 使用卷积特征提取器顶部的全连接层直接预测边界框的坐标。YOLO 只预测每张图片有 98 个盒子。
YOLO 的优势
- 与基于分类器的方法不同,YOLO 在测试时速度极快,因为它只需要一次网络评估
- 同时执行特征提取、边界框预测、非最大值抑制和上下文推理。
- YOLO 网络在线训练特征,并针对检测任务优化它们。统一架构带来更快、更准确的模型
YOLO 的局限性
- YOLO 对边界框预测施加了很强的空间约束,因为每个网格单元只能预测两个框,并且只能有一个类,这限制了模型可以预测的附近对象的数量。
- YOLO 与成群出现的小物体搏斗,比如一群鸟
- 努力推广到新的或不寻常的长宽比或配置的对象
YOLO 的其他变体
快速 YOLO
快速 YOLO 是 YOLO 的快速变体。它使用 9 个卷积层,而不是 YOLO 使用的 24 个卷积层,并且还使用发烧过滤器。
YOLO 和快速 YOLO 之间的网络规模不同,但 YOLO 和快速 YOLO 之间的所有培训和测试参数都相同。
我们网络的最终输出是预测的 7 × 7 × 30 张量。
YOLOv2 或 YOLO9000
- yolov 2 中的输入尺寸从 224224 增加到 448448 。图像输入大小的增加改善了 mAP(平均精度)
- YOLOv2 将整个图像划分成 13×13 的网格。这有助于解决 YOLO v1 中较小物体探测的问题
- YOLOv2 使用批处理规范化,这导致收敛的显著改善,并消除了对其他形式的正则化的需要。我们也可以在不过度拟合的情况下从模型中删除掉的部分。
- YOLOv2 在边界框的维度上运行 k-means 聚类,以获得模型的良好先验或锚。YOLOv2 发现 k= 5 给出了召回率与模型复杂性之间的良好权衡。YOLOv2 使用 5 个锚盒
- YOLOv2 使用 Darknet 架构,具有 19 个卷积层、5 个最大池层和一个用于分类对象的 softmax 层
- YOLOv2 使用定位框来预测边界框。
什么是锚盒或先验,它有什么帮助?
锚盒或先验
锚定框用于检测多个对象、不同比例的对象以及重叠的对象。这提高了目标检测的速度和效率。
锚定框一次评估所有对象预测,消除了使用滑动窗口扫描图像的需要
锚点框是一组具有一定高度和宽度的预定义边界框。定义这些框是为了捕捉要检测的特定对象类的比例和纵横比。
YOLOv2 将整个图像划分为 13 X 13 个网格单元。 YOLOv2 在边界框的维度上运行 k-means 聚类,以获得模型的良好先验或锚。YOLOv2 发现 k= 5 给出更好的性能。

Red is Ground Truth. Blue boxes are 5 Anchor boxes
为了预测一张图片中的多个对象,YOLOv2 进行了数千次预测。最终的目标检测是通过去除属于背景类的锚框来完成的,剩余的锚框通过它们的置信度得分来过滤。我们发现 IoU 大于 0.5 的锚盒。使用前面解释的非最大抑制来选择具有最大置信度得分的锚框。
YOLOv3
- 使用 9 个锚
- 使用逻辑回归来预测客观性分数,而不是 YOLO v2 中使用的 Softmax 函数
- YOLO v3 使用 Darknet-53 网络作为具有 53 个卷积层的特征提取器
YOLO v3 的代码将在下一篇文章中出现
参考资料:
https://pjreddie.com/media/files/papers/yolo.pdf
https://arxiv.org/pdf/1612.08242.pdf
https://pjreddie.com/media/files/papers/YOLOv3.pdf
http://deeplearning.csail.mit.edu/instance_ross.pdf
计算机视觉——导论
揭开像素背后的含义

在 之前的博客 中,我讨论了视觉感知及其生物和计算两个方面。这个博客专门讨论计算视觉,也称为计算机视觉。
什么是计算机视觉?
计算机视觉已经存在了 50 多年,但最近,我们看到人们对机器如何“看”以及计算机视觉如何用于为消费者和企业制造产品的兴趣再次高涨。这类应用的几个例子是——Amazon Go、Google Lens、自动驾驶汽车、人脸识别。
所有这些背后的关键驱动因素是计算机视觉。最简单地说,计算机视觉是人工智能广泛领域下的一门学科,它教会机器看东西。它的目标是从像素中提取意义。
从生物科学的角度来看,它的目标是提出人类视觉系统的计算模型。从工程的角度来看,计算机视觉旨在建立自主系统,这些系统可以执行人类视觉系统可以执行的一些任务(甚至在许多情况下超越人类视觉系统)。
简史
1966 年夏天,麻省理工学院人工智能小组的西蒙·派珀特和马文·明斯基启动了一个名为夏季视觉项目的项目。该项目的目的是建立一个可以分析场景和识别场景中的对象的系统。因此,研究人员和科技巨头仍在试图解码的巨大而令人困惑的计算机视觉领域,最初被人工智能的先驱们认为对于一个本科生暑期项目来说足够简单。
在 70 年代,麻省理工学院的神经科学家大卫·马尔(David Marr)从人类感知的小脑、海马体和皮层的研究中汲取灵感,为现代计算机视觉建立了基础,因此被称为现代计算机视觉之父。他的大部分思想都集中在名为《愿景》的主要著作中。
深度视觉
深度学习从 2012 年开始兴起。深度学习是机器学习的一个子集,其中人工神经网络,受人脑启发的算法,从大量数据中学习。为推荐系统提供动力,识别和标记照片中的朋友,将你的声音翻译成文本,将文本翻译成不同的语言,深度学习已经改变了计算机视觉,使其走向卓越的性能。

Image classification error rate over time, drastic drop after the introduction of deep learning. source — tractable.ai

这些基于深度学习的计算机视觉算法,如卷积神经网络,已经开始给出有希望的结果,具有卓越的精度,甚至在某些任务上超过人类水平的精度。
应用程序
智能手机:二维码、计算摄影(安卓镜头虚化、iPhone 人像模式)、全景构建(谷歌照片球体)、人脸检测、表情检测(微笑)、Snapchat 滤镜(人脸追踪)、谷歌镜头、夜视(像素)
Web: 图片搜索、谷歌照片(人脸识别、物体识别、场景识别、视觉地理定位)、脸书(图片字幕)、谷歌地图航拍成像(图片拼接)、YouTube(内容分类)
VR/AR: 由外向内跟踪(HTC VIVE)、由内向外跟踪(同步定位和映射,HoloLens)、对象遮挡(密集深度估计)
医学影像: CAT / MRI 重建、辅助诊断、自动病理、连接组学、AI 引导手术

http://www.ai.mit.edu/projects/medical-vision/surgery/surgical_navigation.html
媒体:电影、电视的视觉效果(重建)、虚拟体育回放(重建)、基于语义的自动编辑(重建、识别)
保险:理赔自动化、损失分析、财产检查

source — Roadzen
关于工业中计算机视觉应用的详尽列表,请参见由谷歌高级研究科学家大卫·劳维护的页面。
挑战
即使在大量的工作发表后,计算机视觉仍未解决。它只在很少的约束条件下工作。这种困难的一个主要原因是人类视觉系统对于许多任务来说太好了,例如人脸识别。人类可以在光照、视角、表情等各种变化下识别人脸。这是计算机在这种情况下所遭受的。
隐私和道德 —在使用监控时,保险业的尖端技术包括通过监控驾驶行为来调整保费和保单,但另一方面,视觉监控系统对隐私和道德造成了巨大风险。举个例子,我们看中国,用面部识别追踪少数民族。最近,旧金山成为美国第一个禁止政府使用面部识别的城市。
缺乏可解释性 —基于现代神经网络的算法在某种程度上仍然是一个黑箱。因此,当一个模型将一幅图像归类为一辆汽车时,我们实际上并不知道是什么导致它这样做。可解释性是几个领域的关键要求,比如保险和自动驾驶,这些领域目前在这些算法中缺失。
https://www . DARPA . mil/program/explable-artificial-intelligence
Deep Fakes —一方面,解决基于深度学习的视觉正在解决许多现实世界的问题,但另一方面,它也产生了一些问题。使用深度学习技术,现在任何拥有强大 GPU 和训练数据的人都可以用 DeepFakes 创建可信的假图像或视频。

这个问题非常危险,以至于五角大楼通过国防高级研究计划局(DARPA)正在与该国几个最大的研究机构合作,以解决 DeepFakes 问题。
对抗性攻击 — 对抗性示例是攻击者有意设计的机器学习模型的输入,以使模型出错;它们就像机器的视错觉。

Computer vision model fails to recognize a person when a patch of paper is attached to him
计算机视觉的未来
根据一份报告,2017 年计算机视觉市场价值为 23.7 亿美元,预计到 2023 年将达到 253.2 亿美元,CAGR 为 47.54%。
世界正在经历一场深刻的数字化转型,尤其是没有放缓迹象的印度。仅 Jio 每月平均数据消耗就达 10.8 GB。根据这份报告,每分钟——
- 用户观看了 414.66 万个 YouTube 视频
- Instagram 用户发布了 46740 张照片
- Snapchat 用户分享了 527,760 张照片
这些都给计算机视觉提供了大量的机会去寻找模式并理解它。
即使有了这些迷人的发展,人工智能和计算机视觉领域仍然需要解决目前与之相关的问题,如偏见、风险意识和缺乏可解释性。为了解决这些问题,像平安这样的公司已经开始采取小步骤,利用符号人工智能(人工智能的一种早期形式)到现代人工智能算法中,以给出其决策的可解释性,但仍有一段路要走。
计算机视觉—手写数学答题卡的自动评分
使用 python 自动校正和评分数学工作表的计算机视觉模型。

Photo by Jeswin Thomas on Unsplash
阅读是教育的一个重要部分。手动评估每份答卷,提供公平、公正和有效的分数在大多数情况下是困难的。这篇文章是关于我和我的导师比乔恩·古哈的实习项目,建立一个计算机视觉模型,它将自动评估答案纸,从而确保分数完全基于学生的表现。
概观
下面是我们将要检查和评分的样本工作表。

Sample Worksheets
每张工作表都是由不同的人写的。线宽、同页字体、笔尖宽度、字符间距等都会有变化。
这个想法是纠正工作表中的每一行,并用方框标记这些行。其中绿框表示线正确,红框表示线不正确。

Sample Output
(注意:我没有在这里添加所有代码,如果你想检查你可以访问我的 GitHub 在那里我有一个 ipynb 笔记本的教程)
工作流程

Workflow diagram
工作流程中有两个模块工作空间检测模块和分析模块。工作空间检测模块负责检测给定纸张中的多个工作空间。
分析模块负责在任何给定的单个工作空间中检测和定位行中的字符,并对它们进行数学分析,然后根据它们的正确性绘制红、绿框。
工作空间检测
工作空间检测模块假设在给定的扫描工作表中存在有效的矩形框。下图显示了工作表设计。工作表中三个最大的矩形框是工作空间。

Worsheet design
工作空间检测模块使用 openCV 完成。我们将首先找到矩形框,然后根据它们在工作表中的位置对它们进行排序。由于工作表中有许多矩形,我们必须从其他矩形中选择有效的工作空间。让我们看看每个步骤是如何完成的
步骤 1:寻找矩形框
矩形由两条水平线和垂直线组成。所以第一步是找出所有的水平线和垂直线,忽略数字、符号或任何写在工作表上的东西。
下面的代码将首先创建一个名为 "vertical_lines_img" 的二进制图像,它包含工作表中出现的所有垂直线,然后创建另一个名为" horizontal_lines_img" 的二进制图像,它包含工作表中出现的所有水平线。
接下来,我们要将图像“垂直 _ 线条 _img”与“水平 _ 线条 _ img”相加,得到最终的图像。

Adding vertical and horizontal line
轮廓定义为沿着图像边界连接具有相同强度的所有点的线。
OpenCV 具有 findContour()函数,帮助从图像中提取轮廓。每个单独的轮廓是对象边界点的(x,y)坐标的 Numpy 数组。我们可以用它来找到最终图像中的所有对象(最终图像中只有对象是矩形)。
因为最终图像只是原始图像的二进制版本,所以最终图像中矩形的坐标等于原始图像中矩形的坐标。
现在我们知道了坐标,让我们使用 openCV 的draw contours()函数在原始图像上绘制它们。
Code to find and draw the contours

步骤 2:对轮廓进行排序
现在我们已经找到了所有的矩形,是时候根据它们的坐标从上到下对它们进行排序了。下面的代码会帮我们做到这一点。
Function to sort contours Code Reference
Sorted contours
sort_contours 函数将返回按照我们给出的方法排序的轮廓和边界框(左上和右下坐标)。在这种情况下,方法是从上到下。
步骤 3:基于区域的选择
有许多矩形,但我们只需要三个最大的。如何选择三个最大的矩形?….一个答案是找出矩形的面积,然后选择面积最大的前 3 个矩形。

Overall solution
这些选定的矩形是工作空间,然后从工作表中提取出来,并发送到分析模块。
分析模块
如上所述的分析模块将首先检测行,预测每行中的字符,最后用预测的字符形成方程,然后通过标记框来评估它们。
线检测
检测这些线是棘手的部分,每个人都有自己解决方程的方法,有些人一步一步地解决,有些人只用一行就能解决,有些人可能写了好几页的步骤,有些人写了远离方程的指数,使模块混淆,将这些指数作为一个单独的行。
我们的线检测模块假设线之间有足够的间隙,并且在指数字符和线之间有一些交集。首先,检测到的工作空间被转换成二进制图像,然后压缩成一个单一的阵列,以采取向前的导数。哪里有线,哪里的导数就有变化。

Change in derivatives of a binary image
上面的代码只是线条提取工作原理的一瞥。要查看完整的代码,请点击***。*****

字符分割和指数检测
检测完所有线条后,我们必须将提取的线条图像发送到text _ segment函数,该函数将使用 openCV 的 find contours 来分割字符,并使用上述函数 sort_contours 对字符进行排序,其中方法现在设置为从左到右。**
对我们来说,判断给定的数是否是指数很容易,但对模型来说就没那么容易了。假设指数至少在直线的一半以上,我们可以在图像的中心画一条基线,基线以上的任何字符都被认为是指数。

Exponential detection
光学字符识别
我们可以使用 MNIST 数据集用于数字(2828 像素)和 Kaggle 的手写数学符号数据集用于符号(4545 像素)来训练模型。**

MNIST IMAGES
MNIST 实际上是如何形成的?
- 从 500 个不同的作者那里收集的 128 * 128 像素的手写数字。
- 高斯滤波器被应用于图像以柔化边缘
- 然后,通过保持长宽比,将数字放置在正方形图像的中央。
- 然后,使用双三次插值将图像下采样至 28 × 28 像素
在训练之前,符号的图像以与 MNIST 数字相同的方式进行预处理。预处理的原因是我们选择的两个数据集具有不同的特征,如尺寸、厚度和线宽,这使得深度学习模型很难找到模式。预处理帮助我们减少数字和符号之间的差异。
在深度柱状卷积神经网络(DCCNN) 上训练了近 60,000 幅数字和预处理符号的图像,这是一种单一的深度和广度神经网络架构,在各种图像分类挑战(如 MNIST、CIFAR-10 和 CIFAR-100 数据集)上提供接近最先进的性能,如集成模型。这个模型达到了 96 %的准确率。

Deep Columnar Convolutional Architecture (DCCNN)
点击 查看训练代码 DCCNN_training.ipynb
评估和绘图框
评估是最后也是最重要的部分。为了求解任何方程,我们可以使用 python 的 eval 方法。
eva 方法解析传递给它的表达式,并在程序中运行 python 表达式(代码)
这是一个关于 eval 如何工作的例子
****>>Enter the function y(in terms of x):‘x*(x+1)*(x+2)’>>Enter the value of x:3>>print(y)60****
评估 流程涉及的步骤有:
- 解决给定的数学问题并保存答案
- 求解每一个手写行,并将其导出值与存储的答案进行比较。
- 如果线条正确,画一个绿色的边界框,如果线条错误,画一个红色的边界框。
我们举个例子,说问题是解方程 Ax + By**
其中,A=56,B=7,x=3,y=13,这个等式的答案是 595 (563 +713 = 595)。**

Sample workspace
绿色方框表示这条线是正确的,而红色方框表示这条线是错误的。
第一行和最后一行是正确的,通过求解这些行,我们得到 595,这与实际答案相符。
第二行(567 + 92)错了。3 是 9,但它被写成 7,在求解时我们得到 584,这不等于 595*
第三行(595 + 92)也是错误的,在求解这一行时,我们得到 684,它也不等于 595。
结论
总而言之,扫描的工作表被发送到工作空间检测模块,它将返回给定工作表中的所有矩形工作空间,然后检测到的工作空间被传递到线提取模块以提取所有的线。提取的线然后被发送到字符分割模块,它将分割字符,深度学习模型 DCCNN 将预测数字/符号。最后,评估模块将评估线,并绘制红/绿包围盒。
自动化评分过程不仅有助于教师,也为学生创造了舒适的学习体验。通过识别更复杂的数学方程,如微分积分方程、识别字符未分离的草书笔迹、检测剽窃和识别化学方程,该解决方案可以变得更酷。
感谢阅读到最后!!...如果你喜欢这篇文章,请鼓掌让我知道,如果你能通过与你的朋友分享来帮助它传播,我将非常感激:)。✌️
计算机视觉——利用哈尔级联分类器检测物体
在开始进入细节之前,我们先来了解一下什么是计算机视觉以及它的一些应用。
什么是计算机视觉?
计算机视觉是一个研究领域,包括计算机如何看到和理解数字图像和视频。
计算机视觉包括看到或感知视觉刺激,理解所看到的内容,并提取可用于其他机器学习活动的复杂信息。
计算机视觉的应用
计算机视觉有许多实际应用:
- 自动驾驶汽车 —这是计算机视觉最重要的应用之一,自动驾驶汽车需要收集周围的信息,以决定如何行动。
- 面部识别— 这也是计算机视觉的一个非常重要的应用,电子设备使用面部识别技术来验证用户的身份。
- 图像搜索和物体识别 —现在我们可以使用图像搜索在图像中搜索物体。一个很好的例子是谷歌镜头,我们可以通过点击图像的照片来搜索图像中的特定对象,计算机视觉算法将搜索图像目录,并从图像中提取信息。
- 机器人 —大多数机器人机器,通常在制造业中,需要看到它们的周围环境来执行手头的任务。在制造业中,机器可以通过“观察”来检查装配公差。
既然我们知道了计算机视觉的含义和它的一些应用,让我们深入研究它的实现。为了实现计算机视觉的各种例子,我们将使用 OpenCV 库。
OpenCV (开源计算机视觉库:http://opencv.org)是一个开源的 BSD 许可库,包括数百种计算机视觉算法。
Haar 级联分类器:我们将使用 Haar 级联分类器来实现我们的用例。Haar 级联分类器是一种有效的目标检测方法,由 Paul Viola 和 Michael Jones 于 2001 年在他们的论文“使用简单特征的增强级联的快速目标检测”中提出。
那么,让我们试着理解这些哈尔级联分类器是什么。这基本上是一种基于机器学习的方法,其中从大量正面和负面图像中训练级联函数。基于该训练,然后使用它来检测其他图像中的对象。
所以这是如何工作的,他们是巨大的个体。具有许多特性集的 xml 文件,每个 xml 对应于一个非常特定的用例类型。
例如,如果你去 haarcascade 的 github 页面,你会看到有一个特定的 xml 文件,其中包含检测全身、下体、眼睛、正面等等的特征集。
为了理解 haar 级联分类器如何工作,以及它将如何用于计算机视觉,让我们实现一些用例。
1。面部检测
在这个用例中,我们将尝试使用Haar cascade _ front alface _ default . XML来检测个人的面部
我们在这里使用的图像尺寸相当大,所以为了更好的输出,我们缩小了图像尺寸。
我们使用下面的代码来调整图像的大小:

第一步
现在我们已经调整了图像的大小,让我们继续导入 cv2 和 numpy 并使用 OpenCV 的 CascadeClassifier 函数指向我们存储 XML 文件的位置,在我们的例子中是Haar cascade _ frontal face _ default . XML。我已经将 xml 文件下载到我的本地,并使用了我的机器的路径,但是如果你愿意,你可以直接将它指向 github 的位置。
import cv2
import numpy as npface_classifier = cv2.CascadeClassifier('/haarcascade_frontalface_default.xml')
第二步
现在第二步是加载图像并将其转换成灰度。在展示代码之前,我想告诉你我们在这里将图像转换为灰度的原因。
通常我们看到的图像是 RGB 通道(红、绿、蓝)的形式。因此,当 OpenCV 读取 RGB 图像时,它通常将图像存储在 BGR(蓝、绿、红)通道中。为了图像识别的目的,我们需要将 BGR 通道转换成灰色通道。这是因为灰色通道易于处理,并且计算量较小,因为它只包含 1 个黑白通道。
gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
这里,函数 cvtColor 的参数将是图像变量名(调整大小)和 COLOR_BGR2GRAY。
第三步
现在,在将图像从 RGB 转换为灰度后,我们现在将尝试定位我们面部的确切特征。让我们看看如何用代码实现它。
faces = face_classifier.detectMultiScale(gray, 1.0485258, 6)
在这段代码中,我们要做的是,使用 face_classifier ,它是一个加载了Haar cascode _ frontal face _ default的对象。 xml,我们使用了一个内置的函数,叫做 detectMultiScale。
该功能将帮助我们找到新图像的特征/位置。其方式是,它将使用来自 face_classifier 对象的所有特征来检测新图像的特征。
我们将传递给该函数的参数是:
- 灰度变量—在我们的例子中是灰色
- 比例因子 —指定图像尺寸在每个图像比例下缩小多少的参数
。基本上,比例因子用于创建您的比例金字塔。更多的解释是,您的模型在训练期间定义了一个固定的大小,这在 XML 中是可见的。这意味着如果存在的话,在图像中检测到脸部的这个尺寸。但是,通过重新调整输入图像的比例,您可以将较大的人脸调整为较小的人脸,从而使算法可以检测到它。1.05 是一个很好的可能值,这意味着您使用一个小步骤来调整大小,即减少 5%的大小,您增加了找到与检测模型匹配的大小的机会。这也意味着该算法工作较慢,因为它更彻底。为了更快地检测,您可以将它增加到 1.4,但有可能会完全错过一些面孔。在我们的例子中,我使用 1.0485258 作为比例因子,因为这对我正在使用的图像来说是完美的。 - 最小邻居 —指定每个候选矩形应该有多少邻居来保留它的参数。该参数将影响检测到的人脸的质量。值越高,检测次数越少,但质量越高。3~6 是一个很好的值。在我们的例子中,我选择了 6 作为我的邻居,这对于我使用的图片来说是完美的。
第四步
从上面的步骤中,函数 detectMultiScale 返回 4 个值——检测到的面部特征的 x 坐标、y 坐标、宽度(w)和高度(h)。基于这 4 个值,我们将在脸部周围画一个矩形。
if faces is ():
print("No faces found")
for (x,y,w,h) in faces:
cv2.rectangle(resized, (x,y), (x+w,y+h), (127,0,255), 2)
cv2.imshow('Face Detection', resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
将所有 4 个步骤整合在一起,下面是代码:
import cv2
import numpy as npface_classifier = cv2.CascadeClassifier('/haarcascade_frontalface_default.xml')gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)''' Our classifier returns the ROI of the detected face as a tuple,
It stores the top left coordinate and the bottom right coordiantes'''faces = face_classifier.detectMultiScale(gray, 1.0485258, 6)'''When no faces detected, face_classifier returns and empty tuple'''
if faces is ():
print("No faces found")'''We iterate through our faces array and draw a rectangle over each face in faces'''
for (x,y,w,h) in faces:
cv2.rectangle(resized, (x,y), (x+w,y+h), (127,0,255), 2)
cv2.imshow('Face Detection', resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
我已经创建了上述程序的输出作为一个视频,并将其嵌入下面。如果你看到,程序不能正确地检测到一些面孔。原因是为了使 haarcascade 分类器正常工作,人脸应该是适当可见的。在输出中没有被检测到的那个有点不清楚。
尽管如此,我将在未来的帖子中尝试改进这一点,我们将使用不同类型的涉及深度学习的算法来减轻这一点。
Face detection using haar cascade classifier
2.人脸和眼睛检测
现在我们已经对人脸检测的工作原理有了一些了解,让我们看看如何检测人脸和眼睛
在这个实现中,与人脸检测代码相比,我们做了一些改动。
第一步
为了检测眼睛,我们还导入了 haarcascade_eye.xml 文件。
eye_classifier = cv2.CascadeClassifier('/haarcascade_eye.xml')
第二步
一旦我们使用 detectMultiScale 函数获得人脸检测特征的 x 坐标、y 坐标、宽度(w)和高度(h ),我们将创建 2 个 numpy 数组— roi_gray 和 roi_color。****ROI _ gray是使用变量“gray”创建的 numpy 数组。我们创建它的原因是在提取眼睛的特征(例如,ey,ew,eh)时,将这个灰色通道版本传递给 detectMultiScale 函数。
for (x,y,w,h) in faces:
cv2.rectangle(img,(x,y),(x+w,y+h),(127,0,255),2)
cv2.imshow('img',img)
cv2.waitKey(0)
roi_gray = gray[y:y+h, x:x+w]
roi_color = img[y:y+h, x:x+w]
eyes = eye_classifier.detectMultiScale(roi_gray)
第三步
一旦我们提取了眼睛的特征(例如,ey,ew,eh ),我们将遍历它们并通过传递 numpy 数组 roi_color 创建一个矩形。传递 roi_color 而不传递 roi_gray 的原因是 roi_color 是实际 RGB 比例图像的数组,而 roi_gray 是图像的灰度,我们在代码中使用它来加快处理速度,同时提取图像的尺寸/坐标,然后使用这些尺寸将它传递到原始数组中,在我们的示例中是 roi_color 。
for (ex,ey,ew,eh) in eyes:
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(255,255,0),2)
将所有步骤整合在一起:
import numpy as np
import cv2
face_classifier = cv2.CascadeClassifier('/haarcascade_frontalface_default.xml')
eye_classifier = cv2.CascadeClassifier('/haarcascade_eye.xml')img = cv2.imread('/group.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)faces = face_classifier.detectMultiScale(gray, 1.05, 3)# When no faces detected, face_classifier returns and empty tuple
if faces is ():
print("No Face Found")for (x,y,w,h) in faces:
cv2.rectangle(img,(x,y),(x+w,y+h),(127,0,255),2)
cv2.imshow('img',img)
cv2.waitKey(0)
roi_gray = gray[y:y+h, x:x+w]
roi_color = img[y:y+h, x:x+w]
eyes = eye_classifier.detectMultiScale(roi_gray)
for (ex,ey,ew,eh) in eyes:
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(255,255,0),2)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
我已经创建了上述程序的输出作为一个视频,并将其嵌入下面。如果你看到程序不能正确检测戴眼镜的人的眼睛。也许我们需要一个更复杂的算法。
Face and Eye detection using haar cascade classifier
3.流式视频中的车辆检测
让我们再实现一个 haar 级联分类器的用例。在这个用例中,我们将从流视频中检测车辆。我实现了这些用例来展示它是如何工作的。对于这个分类器,还有很多其他的 XML,你可以用来实现其他一些计算机视觉的例子。这是 xmls 的 github 链接。
这里的实现与我们为人脸检测所做的一样,所以我不会详细解释整个过程。然而,代码中有一些变化。
第一步
为了检测车辆的特征,我们需要导入 haarcascade_car.xml.
使用 cv2 的视频捕捉并将值存储在 cap 中
从一个VideoCapture中读取(cap.read())返回一个元组(ret, frame)。对于第一项,您检查读取是否成功,如果成功,则继续使用返回的frame.
cap = cv2.VideoCapture('/vehicle.mp4')# Loop once video is successfully loaded
while cap.isOpened():
time.sleep(.05)
# Read first frame
ret, frame = cap.read()
第二步
现在我们有了(ret, frame)的元组,我们将把 BGR 通道图像转换成灰色通道。出于同样的原因,我们将图像转换为灰度,并使用分类器函数 detectMultiScale 来提取 x 坐标、y 坐标、宽度(w)和高度(h),灰度用于更好的性能吞吐量。
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Pass frame to our car classifier
cars = car_classifier.detectMultiScale(gray, 1.1, 2)
第三步
基于提取的汽车特征/尺寸,我们将遍历它们,并在图像的每一帧周围绘制一个矩形。
for (x,y,w,h) in cars:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 255), 2)
下面是这方面的综合代码:
import time
import numpy as np
import cv2# Create our body classifier
car_classifier = cv2.CascadeClassifier('\haarcascade_car.xml')# Initiate video capture for video file
cap = cv2.VideoCapture('/vehicle.mp4')# Loop once video is successfully loaded
while cap.isOpened():
time.sleep(.05)
# Read first frame
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Pass frame to our car classifier
cars = car_classifier.detectMultiScale(gray, 1.1, 2)
# Extract bounding boxes for any bodies identified
for (x,y,w,h) in cars:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 255), 2)
cv2.imshow('Cars', frame)if cv2.waitKey(1) == 13: #13 is the Enter Key
breakcap.release()
cv2.destroyAllWindows()
现在我们有了完整的代码,让我们检查它的输出。
Car detection using haar cascade classifier
4.流式视频中的行人检测
实现与车辆检测完全相同。这里唯一的不同是,我们将使用 haarcascade_fullbody.xml 来识别行人身体的特征。
下面是它的代码:
import numpy as np
import cv2# Create our body classifier
body_classifier = cv2.CascadeClassifier('\haarcascade_fullbody.xml')# Initiate video capture for video file
cap = cv2.VideoCapture('/moskva.mov')# Loop once video is successfully loaded
while cap.isOpened():
# Read first frame
ret, frame = cap.read()gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Pass frame to our body classifier
bodies = body_classifier.detectMultiScale(gray, 1.1, 3)
# Extract bounding boxes for any bodies identified
for (x,y,w,h) in bodies:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 255), 2)
cv2.imshow('Pedestrians', frame)if cv2.waitKey(1) == 13: #13 is the Enter Key
breakcap.release()
cv2.destroyAllWindows()
让我们看看下面视频中的输出是什么样的:
Pedestrians detection using haar cascade classifier.
离别的思绪
尽管 haarcascade 分类器非常有用,但这种方法也有一些缺点。
- 其中最具挑战性的部分是精确指定检测多尺度函数的比例因子和最小邻居的参数值。我们经常会遇到需要逐个图像地调整这两个参数的情况,对于图像检测用例来说,这是一个很大的障碍。
- 比例因子主要用于控制图像金字塔,进而用于在图像的不同比例下检测物体。如果比例因子太大,那么图像检测可能会不准确,我们将会错过处于金字塔层之间比例的对象。
- 然而,如果我们降低比例因子的值,那么你将在相同的图像比例上得到许多层金字塔,这使得检测更慢并增加假阳性。
请继续关注即将发布的帖子,在那里我们将看到使用一些复杂的深度学习算法以更有效的方式检测图像的其他酷方法。
还有,如果你想了解 devops,可以参考这个网站。
参考资料:
- https://docs . opencv . org/3.4/db/d28/tutorial _ cascade _ classifier . html
- https://www . pyimagesearch . com/2016/06/20/detecting-cats-in-images-with-opencv/
- https://github.com/krishnaik06/Computer-Vision-Tutorial
初学者的计算机视觉:第 1 部分
OpenCV 和 Python 中的图像处理简介

计算机视觉是人工智能领域最热门的话题之一。它在自动驾驶汽车、机器人以及各种照片校正应用程序方面取得了巨大进步。目标检测每天都在稳步前进。GANs 也是研究人员目前关注的一个问题。视觉正在向我们展示技术的未来,我们甚至无法想象它的可能性的尽头会是什么。
那么,你想迈出计算机视觉的第一步,并参与到这一最新的运动中来吗?欢迎你来对地方了。从这篇文章中,我们将有一系列的图像处理和物体检测的基础教程。这是 OpenCV 初学者教程的第一部分,完整系列如下:
- 理解颜色模型并在图像上绘制图形
- 带滤波的图像处理基础知识
- 从特征检测到人脸检测
- 轮廓检测和享受一点乐趣
本系列的第一个故事将是关于安装 OpenCV,解释颜色模型和在图像上绘制图形。本教程的完整代码也可以在 Github 上找到。现在让我们开始吧。
OpenCV 简介
图像处理 是对图像进行一些操作,以获得想要的操作。想想当我们开始新的数据分析时,我们会做些什么。我们做一些数据预处理和特征工程。图像处理也是一样。我们通过图像处理来处理图片,从中提取有用的信息。我们可以减少噪音,控制亮度和色彩对比度。要了解详细的图像处理基础知识,请访问本视频。
OpenCV 代表 开源计算机视觉 库,由英特尔于 1999 年发明。它最初是用 C/C++编写的,所以你可能会看到更多的 C 语言教程,而不是 Python。但是现在它也在 Python 中被广泛用于计算机视觉。首先,让我们为使用 OpenCV 建立一个合适的环境。安装可以按如下方式进行,但您也可以在此处 找到详细描述 。
pip install opencv-python==3.4.2
pip install opencv-contrib-python==3.3.1
完成安装后,尝试导入软件包,看看它是否工作正常。如果你得到的回报没有任何错误,那么你现在就可以开始了!
import cv2
cv2.__version__
我们要用 OpenCV 做的第一步是导入一个图像,可以这样做。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline# Import the image
img = cv2.imread('burano.jpg')
plt.imshow(img)

你去过布拉诺吗?它是意大利最美丽的岛屿之一。如果你还没有去过那里,你一定要在下一个假期去看看这个地方。但是如果你已经知道这个岛,你可能会注意到这张照片有些不同。和我们平时看到的布拉诺的图片有点不一样。应该比这更让人欣喜吧!
这是因为 OpenCV 中颜色模式的默认设置是按照 BGR 的顺序来的,这与 Matplotlib 不同。因此,要查看 RGB 模式下的图像,我们需要将其从 BGR 转换为 RGB,如下所示。
# Convert the image into RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)

现在,这是布拉诺!意大利如此可爱的岛屿!
不仅仅是 RGB
让我们再多谈一点颜色模式。 色彩模型是一个使用原色创建全范围色彩的系统。这里有两种不同的颜色模型: 加色模型 和 减色模型 。加色模型使用光在计算机屏幕上表现颜色,而减色模型使用墨水在纸上打印这些数字图像。对于前者,原色是红色、绿色和蓝色(RGB ),对于后者,原色是青色、品红色、黄色和黑色(CMYK)。我们在图像上看到的所有其他颜色都是由这些原色组合或混合而成的。因此,当图片以 RGB 和 CMYK 表示时,它们的描述会有所不同。

(Source)
你会很习惯这两种模型。然而,在颜色模型的世界中,有两种以上的模型。其中, 灰度,HSV 和 HLS 是你在计算机视觉中会经常看到的。
灰度很简单。它通过黑白的强度来表示图像和形态,这意味着它只有一个通道。要查看灰度图像,我们需要将彩色模式转换为灰色,就像我们之前对 BGR 图像所做的那样。
*# Convert the image into gray scale
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(img_gray, cmap = 'gray')*

实际上,RGB 图像是由三个通道堆叠而成的:R、G 和 b。因此,如果我们选取每个通道并逐一描绘它们,我们就可以理解颜色通道是如何构成的。
*# Plot the three channels of the image
fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (20, 20))for i in range(0, 3):
ax = axs[i]
ax.imshow(img_rgb[:, :, i], cmap = 'gray')
plt.show()*

看看上面的图片。这三个图像向您展示了每个通道是如何组成的。在 R 通道图片中,红色饱和度高的部分看起来是白色的。这是为什么呢?这是因为红色部分的值接近 255。在灰度模式下,该值越高,颜色越白。您也可以用 G 或 B 通道来检查这一点,并比较某些部分之间的差异。

HSV 和 HLS 有一点不同。正如你在上面看到的,它们有一个三维的表示,它更类似于人类的感知方式。 HSV 代表色相、饱和度、明度。 HSL 代表色相、饱和度、明度。HSV 的中心轴是颜色值,而 HSL 的中心轴是光量。沿着中心轴的角度,有色调,实际的颜色。而离中轴线的距离属于饱和。可以按如下方式转换颜色模式。
*# Transform the image into HSV and HLS models
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)# Plot the converted images
fig, (ax1, ax2) = plt.subplots(nrows = 1, ncols = 2, figsize = (20, 20))
ax1.imshow(img_hsv)
ax2.imshow(img_hls)
plt.show()*

但是为什么我们要变换颜色呢?这些是干什么用的?可以给出答案的一个例子是车道检测。请看下图。查看如何在不同的颜色模式下检测车道。在计算机视觉任务中,我们在蒙版的同时进行多种颜色模式的转换。如果你想了解更多关于如何在车道检测任务中应用图像处理的信息,请随时查看由 nachiket tanksale 撰写的 这篇文章 。

RGB vs Grayscale (darkened) vs HSV vs HSL
现在我相信你明白了。图像处理是“数据预处理”它减少噪音,提取有用的模式,使分类和检测任务更容易。因此,所有这些技术,包括我们将在后面讨论的技术,都是为了帮助模型更容易地检测模式。
在图像上绘图
让我们在图像上添加一些数字。现在,我们要去巴黎。你听说过爱情墙吗?这是一面用各种国际语言写满“我爱你”的墙。我们要做的是找到我们语言中的单词,并用一个矩形标记它们。因为我来自韩国,我会在韩语中查找“我爱你”。首先,我将复制原始图像,然后用cv2.rectangle()画一个矩形,我们需要给出左上角和右下角的坐标值。
*# Copy the image
img_copy = img.copy()# Draw a rectangle
cv2.rectangle(img_copy, pt1 = (800, 470), pt2 = (980, 530),
color = (255, 0, 0), thickness = 5)
plt.imshow(img_copy)*

太好了!我觉得我抓对位置了。让我们再试一次。我可以从图像中看到一个韩文单词,所以这次我会画一个圆圈。用cv2.circle(),我们需要指定它的圆心的点和它的半径的长度。
*# Draw a circle
cv2.circle(img_copy, center = (950, 50), radius = 50,
color = (0, 0, 255), thickness = 5)
plt.imshow(img_copy)*

我们也可以在图像上放置文本数据。这次我们为什么不写这面墙的名字呢?使用cv2.putText(),我们可以指定文本的位置、字体和大小。
*# Add text
cv2.putText(img_copy, text = "the Wall of Love",
org = (250, 250),
fontFace = cv2.FONT_HERSHEY_DUPLEX,
fontScale = 2,
color = (0, 255, 0),
thickness = 2,
lineType = cv2.LINE_AA)
plt.imshow(img_copy)*

这真是一面“可爱”的墙,不是吗?自己试试,用你的语言找到“我爱你”!😍
不仅仅是图像
现在我们去了意大利和法国。接下来你想去哪里?我们为什么不放一张地图并标出这些地方呢?我们将创建一个窗口并绘制图形,不是通过指定点,而是通过直接点击窗口。我们先试一圈。我们首先创建一个函数,用鼠标位置和点击的数据画一个圆。
*# Step 1\. Define callback function
**def** draw_circle(event, x, y, flags, param): **if** event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img, center = (x, y), radius = 5,
color = (87, 184, 237), thickness = -1) **elif** event == cv2.EVENT_RBUTTONDOWN:
cv2.circle(img, center = (x, y), radius = 10,
color = (87, 184, 237), thickness = 1)*
使用cv2.EVENT_LBUTTONDOWN或cv2.EVENT_RBUTTONDOWN,当我们按下鼠标按钮时,我们可以带来该位置的数据。鼠标的位置将是(x, y),我们将画一个圆心在该点的圆。
*# Step 2\. Call the window
img = cv2.imread('map.png')cv2.namedWindow(winname = 'my_drawing')
cv2.setMouseCallback('my_drawing', draw_circle)*
我们将设置一张地图作为窗口的背景,并将窗口命名为 my_drawing 。窗口的名称可以是任何东西,但是它应该是相同的,因为它的作用就像窗口的 id。使用cv2.setMouseCallback(),我们在窗口和我们在步骤 1 中创建的函数draw_circle之间建立一个连接。
*# Step 3\. Execution
**while** True:
cv2.imshow('my_drawing',img)
**if** cv2.waitKey(10) & 0xFF == 27:
** break**cv2.destroyAllWindows()*
现在我们使用 while 循环执行窗口。不要忘记设置断点,除非你正在进行无限循环。if 子句的条件是当我们按键盘上的 ESC 键时关闭窗口。将此保存为文件,并将其导入您的终端。如果你要使用 jupyter 实验室,把代码放在一个单元格中并执行。现在,告诉我!你想去哪?

让我们试试长方形。由于矩形需要两个点用于cv2.rectangle()中的 pt1 和 pt2 ,我们需要一个额外的步骤来设置第一个点击点为 pt1 和最后一个点为 pt2 。我们将使用cv2.EVENT_MOUSEMOVE和cv2.EVENT_LBUTTONUP来检测鼠标的移动。
我们首先将drawing = False定义为默认值。当按下左键时,drawing变为真,我们将第一个位置指定为 pt1 。如果绘图打开,它会将当前点作为 pt2 并在我们移动鼠标时继续绘制矩形。这就像重叠的数字。当左键向上时,drawing变为假,以鼠标的最后位置作为其 pt2 的终点。
*# Initialization
drawing = False
ix = -1
iy = -1# create a drawing function
**def** draw_rectangle(event, x, y, flags, params):
**global** ix, iy, drawing **if** event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix, iy = x, y
**elif** event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
cv2.rectangle(img, pt1=(ix, iy), pt2=(x, y),
color = (87, 184, 237), thickness = -1)
**elif** event == cv2.EVENT_LBUTTONUP:
drawing = False
cv2.rectangle(img, pt1=(ix, iy), pt2=(x, y),
color = (87, 184, 237), thickness = -1)*
将步骤 1 中的draw_circle功能替换为draw_rectangle。请不要忘记在回调函数中做一个改变,cv2.setMouseCallback()也一样。所以整个代码脚本如下。保存这个脚本文件,并在终端或 jupyter 笔记本上运行它。
下一步是什么?
你喜欢第一次使用 OpenCV 吗?您还可以尝试其他功能,如绘制直线或多边形。可以随意查看它的文档,可以在这里 找到 。下一次,我们将讨论更高级的技术,如附加两个不同的图像,图像轮廓和对象检测。
⭐️,你可能也会觉得这些系列很有趣,⭐️
- 卷积神经网络的基本概念:
揭开卷积神经网络的神秘面纱
towardsdatascience.com](/the-most-intuitive-and-easiest-guide-for-convolutional-neural-network-3607be47480)
- 卷积神经网络的高级网络:
从 VGG 开始,ResNet 和盗梦空间网络
towardsdatascience.com](/deep-dive-into-the-computer-vision-world-f35cd7349e16)
有你想改正的错误吗?请与我们分享您的见解。我总是乐于交谈,所以请在下面留下评论,分享你的想法。我还在 LinkedIn上分享有趣和有用的资源,所以请随时关注或联系我。下次我会带着另一个有趣的故事回来的!***
初学者的计算机视觉:第 2 部分
Python 中带滤波的图像处理基础

准备是指在实际开始一项任务之前为它做好准备的过程。这可能是写故事、工作面试或数据建模的准备步骤。做好充分的准备,其重要性怎么强调都不为过。如果我们没有进行足够的预处理,那么无论我们有多好的数据,都很难期望得到一个令人满意的结果。
这是 OpenCV 初学者教程的第二部分,完整系列如下:
- 理解颜色模型并在图像上绘制图形
- 带滤波的图像处理基础
- 从特征检测到人脸检测
- 轮廓检测和享受一点乐趣
今天我们将讨论如何处理图像。这将是一个预处理阶段。在检测边缘和轮廓时,噪声对检测的准确性有很大影响。因此,去除噪声和控制像素值的强度可以帮助模型专注于一般细节并获得更高的精度。模糊、阈值处理和形态变换是我们用于此目的的技术。
这篇文章假设你已经熟悉卷积的概念。但如果不是这样,我想推荐你先查一下 这个帖子 。本教程的完整代码也可以在 Github 上找到。现在让我们一个一个的来看看如何用 OpenCV 应用图像过滤。
模糊
模糊的目标是执行降噪。但是我们在这里要格外小心。如果我们对高分辨率的图像应用边缘检测算法,我们会得到太多我们不感兴趣的检测结果。

相反,如果我们过度模糊图像,我们会丢失数据。因此,我们需要找到一个足够的模糊量,我们将申请没有失去理想的边缘。
有几种技术用于实现模糊效果,但我们将讨论 OpenCV 中使用的四种主要技术: 平均模糊、高斯模糊、中值模糊 和 双边滤波 。所有这四种技术都有一个共同的基本原理,即使用滤波器(内核)对图像应用卷积运算。在四种模糊方法之间,应用过滤器的值是不同的。
平均模糊取给定内核区域下所有像素值的平均值,替换中心值。例如,假设我们有一个大小为 5X5 的内核。我们计算卷积结果的平均值,并将结果放在给定区域的中心。

那么如果我们增加内核的大小会是什么样的呢?随着过滤器的尺寸变大,像素值将更加规范化。因此,我们可以预期图像会变得更加模糊。让我们用如下代码检查一下结果。(为了比较,我将继续将原始图像附加到结果中)
# Import the image and convert to RGB
img = cv2.imread('text.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# Plot the image with different kernel sizes
kernels = [5, 11, 17]fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (20, 20)) **for** ind, s **in** enumerate(kernels):
img_blurred = **cv2.blur(img, ksize = (s, s))**
ax = axs[ind]
ax.imshow(img_blurred)
ax.axis('off')
plt.show()

中等模糊与平均模糊相同,只是它使用中值而不是平均值。因此,当我们必须处理图像中的突发噪声时,如“椒盐噪声”,使用中等模糊比平均模糊更好。

The shape of a Gaussian filter (on the left) and a Bilateral filter (on the right)
高斯模糊无非是使用其值具有高斯分布的内核。这些值是由高斯函数生成的,因此它需要一个 sigma 值作为其参数。正如你在上面的图像中看到的,内核的值在中心附近变高,在角落附近变小。最好将这种方法应用于具有正态分布的噪声,例如白噪声。
双边滤波是高斯模糊的高级版本。模糊不仅会产生溶解噪声,还会平滑边缘。双边滤波可以在去除噪声的同时保持边缘清晰。它使用高斯分布值,但同时考虑了距离和像素值的差异。因此需要sigmaSpace和sigmaColor作为参数。
# Blur the image
img_0 = **cv2.blur**(img, ksize = (7, 7))
img_1 = **cv2.GaussianBlur**(img, ksize = (7, 7), sigmaX = 0)
img_2 = **cv2.medianBlur**(img, 7)
img_3 = **cv2.bilateralFilter**(img, 7, sigmaSpace = 75, sigmaColor =75)# Plot the images
images = [img_0, img_1, img_2, img_3]
fig, axs = plt.subplots(nrows = 1, ncols = 4, figsize = (20, 20))**for** ind, p **in** enumerate(images):
ax = axs[ind]
ax.imshow(p)
ax.axis('off')
plt.show()

阈值处理
阈值处理将图像转换成二值图像。我们需要设置阈值和最大值,然后我们相应地转换像素值。有五种不同类型的阈值处理: 二值化、二值化的逆、阈值为零、 和 阈值截断 。
img = cv2.imread('gradation.png')# Thresholding
_, thresh_0 = cv2.threshold(img, 127, 255, **cv2.THRESH_BINARY**)
_, thresh_1 = cv2.threshold(img, 127, 255, **cv2.THRESH_BINARY_INV**)
_, thresh_2 = cv2.threshold(img, 127, 255, **cv2.THRESH_TOZERO**)
_, thresh_3 = cv2.threshold(img, 127, 255, **cv2.THRESH_TOZERO_INV**)
_, thresh_4 = cv2.threshold(img, 127, 255, **cv2.THRESH_TRUNC**)# Plot the images
images = [img, thresh_0, thresh_1, thresh_2, thresh_3, thresh_4]fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (13, 13))
**for** ind, p **in** enumerate(images):
ax = axs[ind//3, ind%3]
ax.imshow(p)
plt.show()


你可以看到每种类型的阈值是如何用数学方法表示的,并且 I(x,y) 是该点的强度,或者(x,y)处的像素值。但是我更喜欢直观的理解概念。看一下右边的图片。这些图片有助于你理解不同类型之间的区别。
但是你不认为只取一个阈值并将其应用于图像的所有部分太苛刻了吗?如果我们有一张在不同区域有不同亮度的照片会怎么样?在这种情况下,将一个值应用于整个图像将是一个糟糕的选择。更好的方法是对图像的每个部分使用不同的阈值。还有一种叫做 自适应阈值 的技术,就是为这个问题服务的。通过计算图像邻域内的阈值,我们可以从光照变化的图像中获得更好的结果。
# Convert the image to grayscale
img = cv2.imread('text.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Adaptive Thresholding
_, thresh_binary = cv2.threshold(img, thresh = 127, maxval = 255, type = cv2.THRESH_BINARY)
adap_mean_2 = cv2.adaptiveThreshold(img, 255,
**cv2.ADAPTIVE_THRESH_MEAN_C**,
**cv2.THRESH_BINARY**, 7, 2)
adap_mean_2_inv = cv2.adaptiveThreshold(img, 255,
**cv2.ADAPTIVE_THRESH_MEAN_C**,
**cv2.THRESH_BINARY_INV**, 7, 2)
adap_mean_8 = cv2.adaptiveThreshold(img, 255,
**cv2.ADAPTIVE_THRESH_MEAN_C**,
cv2.THRESH_BINARY, 7, **8**)
adap_gaussian_8 = cv2.adaptiveThreshold(img, 255,
**cv2.ADAPTIVE_THRESH_GAUSSIAN_C**,
cv2.THRESH_BINARY, 7, **8**)
我们需要将彩色模式转换为灰度模式,以应用自适应阈值处理。自适应阈值的参数有maxValue(上面我设置了 255)、adaptiveMethod、thresholdType、blockSize、C。而这里的自适应方法有两种:ADAPTIVE_THRESH_MEAN_C、ADAPTIVE_THRESH_GAUSSIAN_C。让我们看看图像是如何不同地产生的。
# Plot the images
images = [img, thresh_binary, adap_mean_2, adap_mean_2_inv,
adap_mean_8, adap_gaussian_8]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))**for** ind, p **in** enumerate(images):
ax = axs[ind%2, ind//2]
ax.imshow(p, cmap = 'gray')
ax.axis('off')
plt.show()

我们有原始图像,左边一行是二值化的图像。将此与上面一行的第二个和第三个图像进行比较,这是由ADAPTIVE_THRESH_MEAN_C生成的。它显示了比二进制阈值更详细的结果。我们还可以看到,当 C 值较大时,它变得更加明显。C表示我们将从平均值或加权平均值中减去多少。有了右边这两幅图,我们还可以比较 C 值相同的ADAPTIVE_THRESH_MEAN_C和ADAPTIVE_THRESH_GAUSSIAN_C的效果。
梯度
我相信我们已经熟悉了梯度的概念。在数学中,梯度在几何上表示一个多变量函数的图形的斜率。因为它是一个向量值函数,所以它以方向和幅度作为其分量。这里我们也可以把同样的概念应用到图像的像素值上。图像梯度表示强度或颜色模式的方向变化,我们可以使用这个概念来定位边缘。
# Apply gradient filtering
sobel_x = **cv2.Sobel**(img, cv2.CV_64F, **dx = 1, dy = 0**, ksize = 5)
sobel_y = **cv2.Sobel**(img, cv2.CV_64F, **dx = 0, dy = 1**, ksize = 5)
blended = **cv2.addWeighted**(src1=sobel_x, alpha=0.5, src2=sobel_y,
beta=0.5, gamma=0)
laplacian = **cv2.Laplacian**(img, cv2.CV_64F)
索贝尔运算 同时使用高斯平滑和微分。我们通过cv2.Sobel()应用它,有两个不同的方向可用:垂直(sobel_x)和水平(sobel_y)。dx和dy表示衍生产品。当dx = 1时,操作者计算像素值沿水平方向的导数以进行过滤。
通过对sobel_x和sobel_y两个滤波器求和,我们也可以在两个方向上应用。使用函数cv2.addWeighted(),我们可以计算滤波器的加权和。正如你在上面的代码单元中看到的,我给了两个过滤器相同的权重。
拉普拉斯运算使用 x 和 y 的二阶导数,数学表达式如下所示。

一张图胜过千言万语。让我们看看图像是什么样的。
# Plot the images
images = [sobel_x, sobel_y, blended, laplacian]
plt.figure(figsize = (20, 20))
for i in range(4):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap = 'gray')
plt.axis('off')
plt.show()

很明显,第一和第二图像具有方向性图案。通过第一张图片,我们可以清楚地看到垂直方向的边缘。在第二张图中,我们可以看到水平边缘。和第四图像,示出了两个方向上的边缘。
形态转换
也可以通过过滤来操纵图像的形状,这被称为。先说侵蚀和膨胀。
腐蚀是缩小图形的技术,通常以灰度处理。过滤器的形状可以是矩形、椭圆形和十字形。通过应用过滤器,我们移除给定区域下的任何 0 值。

让我们看看这些如何在代码中实现。
*img = cv2.imread('simpson.jpg')# Create erosion kernels
kernel_0 = np.ones((9, 9), np.uint8)
kernel_1 = cv2.getStructuringElement(**cv2.MORPH_ELLIPSE**, (9, 9))
kernel_2 = cv2.getStructuringElement(**cv2.MORPH_CROSS**, (9, 9))kernels = [kernel_0, kernel_1, kernel_2]# Plot the images
plt.figure(figsize = (20, 20))
for i in range(3):
img_copy = img.copy()
img_copy = cv2.erode(img_copy, kernels[i], iterations = 3)
plt.subplot(1, 3, i+1)
plt.imshow(img_copy)
plt.axis('off')
plt.show()*

看看辛普森一家是如何通过使用不同类型的内核而缩小的。(对辛普森失去双手感到抱歉!)我们可以看到,具有椭圆过滤器的图像以“圆形”方式被侵蚀,而具有方形形状的基本过滤器的图像以“线性”方式被侵蚀。最后一个十字滤镜显示它以“对角线”方式收缩。
膨胀与侵蚀相反。它使物体膨胀,其操作也将与侵蚀相反。让我们用如下代码检查一下结果。
*# Apply dilation
kernel = np.ones((9, 9), np.uint8)
img_dilate = cv2.dilate(img, kernel, iterations = 3)plt.figure(figsize = (20, 10))
plt.subplot(1, 2, 1); plt.imshow(img, cmap="gray")
plt.subplot(1, 2, 2); plt.imshow(img_dilate, cmap="gray")
plt.show()*

开启和关闭*操作是腐蚀和膨胀的混合版本。打开先进行腐蚀,然后对腐蚀的结果进行膨胀,而关闭先进行膨胀,然后进行腐蚀。*

如上图所示,闭合有助于检测图形的整体轮廓,而打开则适合检测子模式。我们可以用下面显示的函数cv2.morphologyEx()来实现这些操作符。参数op表示我们将使用哪种类型的操作符。
*# Apply the operations
kernel = np.ones((9, 9), np.uint8)img_open = cv2.morphologyEx(img, op= **cv2.MORPH_OPEN**, kernel)
img_close = cv2.morphologyEx(img, op= **cv2.MORPH_CLOSE**, kernel)
img_grad = cv2.morphologyEx(img, op= **cv2.MORPH_GRADIENT**, kernel)
img_tophat = cv2.morphologyEx(img, op= **cv2.MORPH_TOPHAT**, kernel)
img_blackhat = cv2.morphologyEx(img, op= **cv2.MORPH_BLACKHAT**, kernel)# Plot the images
images = [img, img_open, img_close, img_grad,
img_tophat, img_blackhat]fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))
**for** ind, p **in** enumerate(images):
ax = axs[ind//3, ind%3]
ax.imshow(p, cmap = 'gray')
ax.axis('off')
plt.show()*

请注意,在打开滤镜和关闭滤镜的图像中,Simpson 的手被描绘得不同。梯度过滤器(MORPH_CGRADIENT)是从膨胀到侵蚀减去的区域。礼帽滤镜(MORPH_TOPHAT)是从打开到原始图像减去的区域,而黑色热滤镜(MORPH_BLACKHAT)是从关闭减去的区域。我推荐你访问这里的***以获得关于形态运算符的进一步解释。*****
下一步是什么?
你喜欢各种各样的图像处理技术吗?除了我们已经讨论过的东西,OpenCV 中还有其他可用的东西。因此,请不要犹豫,赶快访问并查看 OpenCV 文档 。下一次将是关于检测技术,如轮廓检测和面部检测。
有你想改正的错误吗?请与我们分享您的见解。我总是乐于交谈,所以请在下面留下评论,分享你的想法。我还在 LinkedIn 上分享有趣和有用的资源,欢迎随时关注或联系我。我将带着故事的下一个系列回来。敬请期待!
初学者的计算机视觉:第 3 部分
从特征检测到人脸检测

检测任务是计算机视觉的主要任务之一,我们有许多方法可以利用这项技术。识别出现时可能是关键的错误,但人眼无法识别。探测不安全和危险的动作或时刻以拯救生命。为自动驾驶汽车感知空间信息。使用物体检测的例子数不胜数,让这些任务自动化将给我们带来安全和效率。
这是 OpenCV 初学者教程的第三部分,完整系列如下:
- 理解颜色模型并在图像上绘制图形
- 带滤波的图像处理基础知识
- 从特征检测到人脸检测
- 轮廓检测和享受一点乐趣
我们已经讨论了几种颜色模式以及如何在图像上绘制图形,例如矩形和圆形。然后在第 2 部分,我们谈到了图像处理的概念,如模糊,梯度,腐蚀,膨胀等。今天,我们将应用这些概念来检测图像的特征,并在最后进行人脸检测。
本文假设您已经遵循了前面的步骤,或者您已经知道这些概念。但如果不是这样,请查看本系列前面的部分。本教程的完整代码可在 Github 上获得。现在让我们开始吧!
边缘检测
边缘检测 是指识别图像中亮度变化剧烈或不连续的点。我们可以用那些点画线段,这些线段叫做 边 。实际上,我们上次已经学习了一种边缘检测技术。你还记得吗?用索贝尔和拉普拉斯运算进行梯度滤波。通过计算给定方向上像素值的导数,梯度滤波可以描绘图像的边缘。
Canny 检测 是另一种边缘检测技术。这是最流行的边缘检测算法之一,分四步进行: 降噪,寻找梯度及其方向,非最大抑制 和 滞后阈值 。
该算法从高斯模糊开始,我想我们已经知道去除图像中噪声的原因。然后用 Sobel 核寻找图像的亮度梯度。利用导出的梯度和方向,检查每个像素的某个点是否是其周围点的局部最大值。如果不是,这一点被抑制为零(完全不存在,黑色)。这叫做 非最大抑制 。

如果该点被认为是局部最大值,则进入下一阶段。最后一个阶段是最后一个决定阶段,决定前面步骤中的边是否真的是边。这被称为 滞后阈值 ,这里我们需要两个阈值。
给定两个不同的阈值,我们得到三个范围的值。因此,如果一个点的强度梯度高于上限阈值,它将被认为是“确定边缘”如果一个点的梯度低于下限阈值,该点将被丢弃。在梯度处于两个阈值中间的情况下,我们看到它与其他“确定边缘”点的连通性。如果没有联系,它也会被丢弃。
img = cv2.imread('images/giraffe.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# Canny detection without blurring
edges = **cv2.Canny(**image=img, threshold1=127, threshold2=127**)**plt.figure(figsize = (20, 20))
plt.subplot(1, 2, 1); plt.imshow(img)
plt.axis('off')
plt.subplot(1, 2, 2); plt.imshow(edges)
plt.axis('off')

我只是在没有模糊的情况下使用了两个阈值的中间值,结果并不理想。现在让我们这次尝试不同的阈值。
# Set the lower and upper threshold
med_val = np.median(img)lower = int(max(0, .7*med_val))
upper = int(min(255, 1.3*med_val))
为了了解模糊如何改变结果,我将应用两种不同大小的内核,(5x5)和(9x9)。我将尝试通过增加 100 来更改上限阈值。因此,我们有 4 种类型的处理图像,如下所示:
# Blurring with ksize = 5
img_k5 = cv2.blur(img, ksize = (5, 5))# Canny detection with different thresholds
edges_k5 = cv2.Canny(img_k5, threshold1 = lower, threshold2 = upper)
edges_k5_2 = cv2.Canny(img_k5, lower, upper+100)# Blurring with ksize = 9
img_k9 = cv2.blur(img, ksize = (9, 9))# Canny detection with different thresholds
edges_k9 = cv2.Canny(img_k9, lower, upper)
edges_k9_2 = cv2.Canny(img_k9, lower, upper+100)# Plot the images
images = [edges_k5, edges_k5_2, edges_k9, edges_k9_2]
plt.figure(figsize = (20, 15))
for i in range(4):
plt.subplot(2, 2, i+1)
plt.imshow(images[i])
plt.axis('off')
plt.show()

正如你在上面看到的,模糊有助于去除噪声,我们用(9x9)大小的内核得到了更好的结果。此外,阈值上限越高,我们得到的结果越好。
角点检测
角点检测是另一种广泛应用于目标检测、运动检测、视频跟踪等领域的检测算法。什么是图像处理中的角点?怎样才能用像素定义一个角呢?我们把角看作是边相交的地方。那我们怎么找到他们?首先找到所有的边,然后定位它们相互交叉的点?其实我们还有另外一种让事情更有效率的方法,那就是 哈里斯 和 石&托马西 。
这些算法工作如下。我们检测各个方向上强度值有相当大变化的点。然后我们构造一个矩阵从中提取特征值。这些特征值是为了得分来决定它是否是一个角。数学表达式如下所示。

现在让我们看看如何用代码实现这些。我们首先需要将图像转换成灰度。 哈里斯角点检测 可以用 OpenCV 中的函数cv2.cornerHarris()进行。
img = cv2.imread('images/desk.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)# Apply Harris corner detection
dst = **cv2.cornerHarris(**img_gray, blockSize = 2, ksize = 3, k = .04**)**
参数blockSize是被认为是邻域的窗口的大小,而k是哈里斯检测器自由参数,如上面的等式所示。结果是分数 R,我们将使用它来检测角点。
# Spot the detected corners
img_2 = img.copy()
img_2[dst>0.01*dst.max()]=[255,0,0]# Plot the image
plt.figure(figsize = (20, 20))
plt.subplot(1, 2, 1); plt.imshow(img)
plt.axis('off')
plt.subplot(1, 2, 2); plt.imshow(img_2)
plt.axis('off')

这次我们试试 Shi-Tomasi 角点检测。我们可以用函数cv2.goodFeaturesToTrack()来使用它。我们按照最大可能性(maxCorners)设置最大数量的角点。我们还指定了最小距离(minDistance)和最小质量等级(qualityLevel),它们被认为是角点。在我们得到检测到的角点后,我们将用圆圈标记这些点,如下所示。
# Apply Shi-Tomasi corner detection
corners = **cv2.goodFeaturesToTrack(**img_gray, maxCorners = 50,
qualityLevel = 0.01,
minDistance = 10**)**
corners = np.int0(corners)# Spot the detected corners
img_2 = img.copy()
for i in corners:
x,y = i.ravel()
cv2.circle(img_2, center = (x, y),
radius = 5, color = 255, thickness = -1)# Plot the image
plt.figure(figsize = (20, 20))
plt.subplot(1, 2, 1); plt.imshow(img)
plt.axis('off')
plt.subplot(1, 2, 2); plt.imshow(img_2)
plt.axis('off')

人脸检测
我们要看的最后一个特征是一张脸。 人脸检测 是一种识别数字图像中人脸的存在和位置的技术。我要你把人脸检测和人脸识别 区分开来,人脸识别是指通过人脸来检测一个人的身份。所以人脸检测并不能告诉我们检测到的人脸属于谁。
人脸检测基本上是一个分类任务,因此它被训练来分类是否有目标对象。而 基于 Haar 特征的级联分类器 是 OpenCV 中可用的人脸检测模型之一。这是一个预训练模型,这意味着它已经完成了数千张图像的训练。理解该算法的 4 个要点是 哈尔特征提取、积分图像、Adaboost 和 级联分类器 。

类哈尔特征 是用于物体检测的图像滤波器或图像核,示例如上。它们的名字源于它们与最初由 Alfréd Haar 提出的 Haar 小波的直觉相似性。在检测过程中,我们通过图像上的窗口,用过滤器进行卷积运算,以查看图像中是否有我们要寻找的特征。这里是 视频 ,它可视化了检测是如何工作的。

那么我们如何决定在一个给定的区域中是否有一个想要的特性呢?我们来看看上图。我们有一个内核,它的上半部分是深色的,下半部分是浅色的。然后,我们得到每个区域的像素值的平均值,并减去两者之间的差距。如果结果高于一个阈值,比如说 0.5,那么我们得出结论,这就是我们正在检测的特征。我们对每个内核重复这个过程,同时在图像上滑动窗口。
虽然这不是一个复杂的计算,但当我们在整个图像中考虑它时,总计算量变得巨大。如果你看过上面提到的视频,你会对所涉及的计算量有直觉。而这正是一个 积分图 发挥作用的地方。积分图像是一种图像表示方式,是为了使特征评价更快、更有效而衍生出来的。
如下图所示,左边是一幅图像的像素,右边是一幅完整的图像。从左上角开始,它计算给定矩形区域下像素的累积和。在积分图像上,虚线框内的像素之和写在右边框的右下点。

有了这个预先计算的表,我们可以通过子矩形(红色、橙色、蓝色和紫色的方框)的值简单地得到某个区域的总和值。
所以我们用积分图像解决了计算量。但是我们还没完。想象一下,当检测窗口处于没有物体或人脸的空白背景时。如果在这样的部分执行相同的过程,仍然是浪费时间。还有一个让这个探测器更快的因素。实现 一个 级联分类器与 Adaboost!

级联分类器构建逐步的阶段,并给出类哈尔特征之间的顺序。功能的基本形式在早期阶段实现,而更复杂的功能仅适用于那些有希望的区域。并且在每个阶段,Adaboost 模型将通过集合弱学习者来训练。如果一个子部分或子窗口在前一阶段被归类为“非人脸区域”,它将被拒绝进入下一步。这样做,我们只能考虑幸存的,并实现更高的速度。
我们的英雄在哪里?
为了让这个教程更有趣,我想实现这个级联分类器来检测我们的漫威英雄的脸。(机器学习应该很好玩,你说呢?😃😃)让我们有请漫威上尉作为我们的第一位客人。

我们将只使用这张图片的一部分。所以让我们先得到她脸部周围的感兴趣区域,然后将图像转换成灰度。只使用一个通道的原因是因为我们只对特征的光强度变化感兴趣。
*cap_mavl = cv2.imread('images/captin_marvel.jpg')# Find the region of interest
roi = cap_mavl[50:350, 200:550]
roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
plt.imshow(roi, cmap = 'gray')*

Haar 级联分类器文件基本上是 OpenCV 附带的。你可以在你电脑上的 OpenCV 文件夹中找到它们。或者你可以简单的在这里 下载文件 。
*# Load Cascade filter
face_cascade = cv2.CascadeClassifier('haarcascades/**haarcascade_frontalface_default.xml**')*
接下来,我们将创建一个函数来检测一张脸,并在其周围绘制一个矩形。为了检测面部,我们可以使用上面加载的分类器face_cascade的方法.detectMulitiScale()。它返回识别区域的四个点,所以我们将在那个位置画一个矩形。scaleFactor是每个图像尺度下图像尺寸减小多少的参数,而minNeighbors是每个候选矩形应该训练多少个邻居的参数。现在让我们把这个函数应用到图像上,看看结果。
*# Create the face detecting function
**def** detect_face(img)**:**
img_2 = img.copy()
face_rects = **face_cascade.detectMultiScale(**img_copy,
scaleFactor = 1.1,
minNeighbors = 3**)
for** (x, y, w, h) **in** face_rects:
cv2.rectangle(img_2, (x, y), (x+w, y+h), (255, 255, 255), 3)
**return** img_2# Detect the face
roi_detected = detect_face(roi)
plt.imshow(roi_detected, cmap = 'gray')
plt.axis('off')*

太好了!我觉得这个还是比较满意的。这次为什么不干脆叫其他英雄?我们也可以对有多张脸的图像实现这个分类器。
*# Load the image file and convert the color mode
avengers = cv2.imread('images/avengers.jpg')
avengers = cv2.cvtColor(avengers, cv2.COLOR_BGR2GRAY)# Detect the face and plot the result
detected_avengers = detect_face(avengers)
display(detected_avengers, cmap = 'gray')*

是的,有时捕捉“非人脸”对象或错过“真实人脸”会导致失败。有趣的是,它成功地发现了蜘蛛侠,却把美国队长和黑寡妇的手误认为眼睛。我们通常会得到更好的结果,一张脸看着前方,清楚地显示出额头和眼睛。
想试试你的吗?
你想试试你的网络摄像头吗?我们可以应用同样的过程。将下面的代码脚本保存为一个文件,并将其导入到您的终端上。如果你要使用 Jupyter 笔记本,把代码放在一个单元格中并执行。你可以按 ESC 键关闭窗口。
它成功检测到你的脸了吗?希望它不会选择你的鼻孔做眼睛。😅
下一步是什么?
你喜欢这个故事吗?除了正面人脸分类器,还有眼睛、上半身、下半身等各种模型。你也可以试试检测俄罗斯车牌的模型。所以我建议你也玩玩它。你想测试你对图像处理的理解吗?试试这个 25 题图像处理 。这里有一些很棒的文章,为你应该检查的问题提供了详细的解释。
* [## 我用 25 个问题来测试一个数据科学家关于交互式代码的图像处理-第 1 部分
所以我找到了这个神奇的博客作者 Faizan Shaikh,他的博客上有一些神奇的东西!所以请检查他…
towardsdatascience.com](/my-take-on-25-questions-to-test-a-data-scientist-on-image-processing-with-interactive-code-part-1-a6196f535008) [## 我用 25 个问题来测试一个数据科学家对交互式代码的图像处理-第 2 部分
所以我找到了这个神奇的博客作者 Faizan Shaikh,他的博客上有一些神奇的东西!所以请检查他…
towardsdatascience.com](/my-take-on-25-questions-to-test-a-data-scientist-on-image-processing-with-interactive-code-part-2-77eacfd96cf9)
有你想改正的错误吗?请与我们分享您的见解。我总是乐于交谈,所以请在下面留下评论,分享你的想法。我还在 LinkedIn 上分享有趣且有用的资源,欢迎随时关注并联系我。我将带来另一个有趣的计算机视觉故事。敬请关注!*
初学者的计算机视觉:第 4 部分
轮廓检测和一点点乐趣

有很多方法可以让你有效地学习进步。对我来说,把学习和一点点乐趣结合起来是最好的策略。在这一系列教程中,许多图像被用来演示图像处理概念。你可能会感兴趣地注意到,人们喜欢我以一种有趣的方式将这个概念应用到图像中的部分。我建议你把娱乐和学习结合起来。
这是 OpenCV 初学者教程的最后一部分,完整系列如下:
- 理解颜色模型并在图像上绘制图形
- 使用过滤和渐变进行图像处理的基础知识
- 从特征检测到人脸检测
- 轮廓检测又有了一点乐趣
从第一个系列中,我们看到了如何在图像上绘制图形。我们还讨论了如何使用各种选项应用模糊和阈值处理。在第三部分,我们讨论了几种检测算法,包括边缘检测。还有另一种检测技术叫做轮廓检测,它在物体检测中是必不可少的。在那之后,我们会用一点玩蒙版的乐趣来结束这个系列。
本文假设您已经遵循了前面的步骤,或者您已经知道这些概念。但如果不是这样,请查看本系列前面的部分。本教程的完整代码一如既往地在Github上提供。
轮廓检测
你可能已经熟悉了“轮廓”这个词。在以前的帖子中,我已经多次使用这个术语。 轮廓线 表示代表相同值或相同强度的边界的曲线。等高线图是我们能想到的最简单的例子。
但是你可能会问。边缘和轮廓有什么区别?这两个术语经常互换使用,所以可能会有点混淆。简单地说,边缘的概念是在局部范围内,而轮廓的概念是在图形的整体边界上。边是其值与其相邻点相比发生显著变化的点。另一方面,轮廓是从边缘获得的封闭曲线,描绘了图形的边界。这里 可以找到进一步的解释 。
***# Load the image
img = cv2.imread('images/pine_apple.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)***

所以我们要做的是检测这个菠萝的轮廓。在应用检测算法之前,我们需要将图像转换成灰度并应用如下的阈值处理。所有这些步骤都是我们在上一系列文章中讨论过的。
***# Blurring for removing the noise
img_blur = cv2.bilateralFilter(img, d = 7,
sigmaSpace = 75, sigmaColor =75)# Convert to grayscale
img_gray = cv2.cvtColor(img_blur, cv2.COLOR_RGB2GRAY)# Apply the thresholding
a = img_gray.max()
_, thresh = cv2.threshold(img_gray, a/2+60, a,cv2.THRESH_BINARY_INV)
plt.imshow(thresh, cmap = 'gray')***

轮廓检测可以通过 OpenCV 中的函数cv2.findContours()来实现,这里有两个重要的参数。mode是寻找轮廓的方法,method是检测的近似方法。我要求你从 文档 中找到其他信息。
***# Find the contour of the figure
image, contours, hierarchy = cv2.findContours(image = thresh,
**mode = cv2.RETR_TREE,
method = cv2.CHAIN_APPROX_SIMPLE**)***
模式cv2.RETR_TREE找到所有有希望的轮廓线,并重建嵌套轮廓的完整层次。方法cv2.CHAIN_APPROX_SIMPLE只返回绘制等高线所需的端点。正如你在上面看到的,这个函数给出了图像,检测到的轮廓和它们的层次作为输出。
返回的等高线是由等高线组成的点的列表。为了画出图形的外部线条,我们将按面积对轮廓进行排序。选中轮廓线后,cv2.drawContours()将描绘出边界线和如下所示的点。
***# Sort the contours
contours = sorted(contours, key = cv2.contourArea, reverse = True)# Draw the contour
img_copy = img.copy()
final = **cv2.drawContours(**img_copy, contours, contourIdx = -1,
color = (255, 0, 0), thickness = 2**)**
plt.imshow(img_copy)***

很简单吧?虽然底部也加了阴影,但是结局还是蛮满意的。
关于轮廓的更多信息
我们可以对轮廓做更多的事情。借助于图像矩的概念,我们可以找到图像的质心或计算边界场的面积。一瞬间在这里意味着什么?单词‘moment’在通常用法中是一小段时间。但在物理学术语中,一个 矩 是距离和另一个物理量的乘积,意味着一个物理量是如何分布或定位的。所以在计算机视觉中, 图像矩 就是图像像素强度是如何根据位置分布的。它是图像像素强度的加权平均值,我们可以从图像矩中获得质心或空间信息。**

有三种类型的矩-空间矩、中心矩和中心归一化矩。我们可以用 OpenCV 中的函数cv2.moments()得到图像的矩,它返回 24 个不同的矩。如果您打印如下所示的输出M,它将以字典格式返回 24 个时刻。
***# The first order of the contours
c_0 = contours[0]# image moment
M = cv2.moments(c_0)
print(M.keys())***
我想在这里把重点放在图像时刻的实现上。如果你有兴趣,可以在本文末尾找到额外的阅读资源。
为了得到轮廓的面积,我们可以实现函数cv2.contourArea()。为什么我们不在这里尝试几种轮廓?如果您输入第一个、第二个和第三个轮廓,您将得到如下所示的递减值。这表明轮廓检测算法形成了检测到的边界的层次。
***# The area of contours
print("1st Contour Area : ", cv2.contourArea(contours[0])) # 37544.5
print("2nd Contour Area : ", cv2.contourArea(contours[1])) # 75.0
print("3rd Contour Area : ", cv2.contourArea(contours[2])) # 54.0***
轮廓的弧长可以通过函数cv2.arcLength()得到。参数closed指示曲线是否应该闭合。
***# The arc length of contours
print(cv2.arcLength(contours[0], closed = True)) # 2473.3190
print(cv2.arcLength(contours[0], closed = False)) # 2472.3190***
现在让我们试着画出菠萝的质心和极值点。我们可以用下面的公式得到质心点。
***# The centroid point
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])***
极值是左边和右边、顶部和底部的端点。并且我们可以把 x 和 y 坐标分别排列如下。
***# The extreme points
l_m = tuple(**c_0[c_0[:, :, 0]**.argmin()][0])
r_m = tuple(**c_0[c_0[:, :, 0]**.argmax()][0])
t_m = tuple(**c_0[c_0[:, :, 1]**.argmin()][0])
b_m = tuple(**c_0[c_0[:, :, 1]**.argmax()][0])pst = [l_m, r_m, t_m, b_m]
xcor = [p[0] for p in pst]
ycor = [p[1] for p in pst]***
现在让我们在图像上绘制所有这些点。
***# Plot the points
plt.figure(figsize = (10, 16))
plt.subplot(1, 2, 1)
plt.imshow(image, cmap = 'gray')
plt.scatter([cx], [cy], c = 'b', s = 50)plt.subplot(1, 2, 2)
plt.imshow(image, cmap = 'gray')
plt.scatter(xcor, ycor, c = 'b', s = 50)***

其他轮廓形状
除了紧致轮廓,我们还可以画出一个图形的凸轮廓或矩形轮廓线。让我们先尝试一个直的矩形。有了外部轮廓线,我们将围绕对象绘制矩形。函数cv2.boundingRect()返回边界框的 4 个点,如下所示。
***# The first order of the contours
c_0 = contours[0]# Get the 4 points of the bounding rectangle
x, y, w, h = cv2.boundingRect(c_0)# Draw a straight rectangle with the points
img_copy = img.copy()
img_box = cv2.rectangle(img_copy, (x, y), (x+w, y+h), color = (255, 0, 0), thickness = 2)***
注意,这个直矩形不是其他可能边界中的最小值。我们可以用函数cv2.minAreaRect()提取面积最小的矩形,该函数找到一个包含输入 2D 点集的旋转矩形。之后,我们得到这个矩形的四个角,并把它们放在contour参数中,如下所示。
***# Get the 4 points of the bounding rectangle with the minimum area
rect = cv2.minAreaRect(c_0)
box = cv2.boxPoints(rect)
box = box.astype('int')# Draw a contour with the points
img_copy = img.copy()
img_box_2 = cv2.drawContours(img_copy, **contours = [box]**,
contourIdx = -1,
color = (255, 0, 0), thickness = 2)***
现在让我们检查结果并比较两个不同的轮廓框。
***plt.figure(figsize = (10, 16))
plt.subplot(1, 2, 1); plt.imshow(img_box)
plt.subplot(1, 2, 2); plt.imshow(img_box_2)***

我们也可以使用cv2.convexHull()功能绘制一个凸形轮廓。这需要一组点,并从给定的集合中返回凸包。在cv2.drawContours()中输入这个返回的点为contours,我们可以得到如下的凸轮廓。
***# Detect the convex contour
hull = cv2.convexHull(c_0)img_copy = img.copy()
img_hull = cv2.drawContours(img_copy, **contours = [hull]**,
contourIdx = 0,
color = (255, 0, 0), thickness = 2)
plt.imshow(img_hull)***

在轮廓检测中有太多的事情要涉及。我鼓励你查看文档并自己探索更多选项。****
掩蔽带来的一点乐趣
作为这个系列的最后一个练习,我想做一些有趣的图像遮罩。我们可以通过图像加法或位运算等算术运算将两幅不同的图像相加。所以我们最后的任务是把我们的菠萝先生系在一个男人的左肩上。
*****# Import the large image
backpacker = cv2.imread('images/backpacker.jpg')
backpacker = cv2.cvtColor(backpacker, cv2.COLOR_BGR2RGB)
plt.imshow(backpacker)*****

我们将从大图和小图中剪切出感兴趣的区域(肩部),如下所示。注意,这里两个图像的大小(图像的高度和宽度)应该是相同的。
*****# Crop the small image and the roi
roi = backpacker[750:1150, 300:500]
img_2 = img[40:440, 80:280]plt.figure(figsize = (6, 6))
plt.subplot(1, 3, 1); plt.imshow(roi)
plt.subplot(1, 3, 3); plt.imshow(img_2)*****

下一步是为每张图片制作蒙版。我们要用掩码实现位运算,掩码应该是一个二进制图像。通过对小图像img_2进行阈值处理,我们创建了蒙版。然后用cv2.bitwise_not(),我们制作另一个和第一个完全相反的面具。
*****# Creating the mask for the roi and small image
img_gray = cv2.cvtColor(img_2, cv2.COLOR_RGB2GRAY)
_, mask = cv2.threshold(img_gray, 254/2+100, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)plt.figure(figsize = (6, 6))
plt.subplot(1, 3, 1); plt.imshow(mask, cmap = 'gray')
plt.subplot(1, 3, 3); plt.imshow(mask_inv, cmap = 'gray')*****

如果我们实现函数cv2.bitwise_and()和遮罩,它们将只通过图像的白色区域。因此,如果我们将第一个蒙版应用于roi图像,我们可以从它设置背景图像。同样的,如果我们将第二个应用到img_2图像,我们可以用水果制作前景图像。
*****# Masking
img_bg = cv2.bitwise_and(roi, roi, mask = mask)
img_fg = cv2.bitwise_and(img_2, img_2, mask = mask_inv)
dst = cv2.add(img_fg, img_bg)plt.figure(figsize = (10, 6))
plt.subplot(1, 3, 1); plt.imshow(img_bg)
plt.subplot(1, 3, 2); plt.imshow(img_fg)
plt.subplot(1, 3, 3); plt.imshow(dst)*****

考虑到底部的阴影,结果有点混乱,但让我们保持它。如果你想了解位运算是如何工作的,详细的解释可以在 这里 找到。现在,我们准备将这个组合图像附加到原始图像上。因此,我们可以简单地这样做,将dst图像放在roi位置,如下所示。
*****# Final output
backpacker[750:1150, 300:500] = dst
display(backpacker)*****

😄👋祝你和背包客过得愉快!
摘要
从第 1 部分到第 4 部分,我们已经学习了很多关于图像处理和计算机视觉的基础知识。到目前为止,你喜欢这些教程吗?我希望在阅读本系列文章的同时,您对这些技巧有了信心。
如果您完全是图像处理的初学者,最初不熟悉 OpenCV 函数的术语和用法是正常的。最好的练习方法是自己实现所有这些。选择你喜欢的任何类型的图片,并从中获得乐趣。改变颜色模式,旋转和调整图像大小,遮罩并粘贴到不同的图像上。或者你也可以应用边缘、拐角和轮廓的检测技术。虽然通过组合几个预处理和尝试不同的参数来获得乐趣,但您可以在没有意识到的情况下更快地习惯 OpenCV 库。
资源
有你想改正的错误吗?请与我们分享您的见解。我总是乐于交谈,所以请在下面留下评论,分享你的想法。我还在 LinkedIn 上分享有趣和有用的资源,欢迎随时关注并联系我。下次我会带来另一个有趣的故事。一如既往,敬请关注!😎
蒸汽波艺术的计算机视觉

Photo by Sean Foley on Unsplash
利用现代技术进行艺术追求
当然,通读出版物和实现实用算法是令人兴奋的。我喜欢寻找聪明的方法来分割图像,检测人们是不是 T2 混蛋,甚至帮助机器人通过 T4 农场。然而,有几个星期我不想用计算机视觉做任何有意义的事情。在大脑感觉特别顺畅的日子里,让代码从手指中流出并出于良性目的使用磁场是令人放松的。
本周就是其中的一周。除了参与科技,我还喜欢时尚和艺术。我最近发现了一种被称为 Vaporwave 的边缘艺术形式。形式采用流行的互联网时代艺术形式的元素,如 glitch art 和 chillwave。Vaporwave 与其他形式不同,它强调怀旧主题,经常使用来自美国和日本文化的 20 世纪 80 年代风格的波普艺术。90 年代和 21 世纪初的图形用户界面(GUI)也是常见的噱头。当个人电脑还处于萌芽状态,科技行业还相当不景气时,这些物品会让人产生不正当的简单感觉。

Vaporwave has many forms and is seen in digital media and fashion!
在千年之交的成长过程中,我觉得这些作品向我的成长致敬。作为一名活跃的图像处理程序员,我决定参与艺术形式。我开发了一个简短的 Python 脚本,可以蒸发任何给定的照片。
毛刺艺术方法
在图像处理和计算机视觉中,通常使用数字操作来清除噪声和分离感兴趣的项目。这个项目做的完全相反。目标是添加噪声和破坏图像,但以一种有品位的方式。
我想加入的 Vaporwave 艺术的一个特征是毛刺艺术。这是指数字误差被故意用来制作艺术图案。这种艺术形式应用在人脸上时有一种特别强烈的感觉。
OpenCV 等有用的库使得面部识别变得相当简单。OpenCV 拥有可以用来检测不同面部姿态和特征的模型。我特别想开发出可以操控整张脸或者只操控眼睛的功能。使用哈尔级联和一些参数调整,人脸和眼睛出现在大多数图像中。

Facial detection results. Images by demegree
知道了面部的位置,我想执行两个主要的面部瑕疵,面部拖动和静态瑕疵。面部拖动是选择面部上的一个区域并在图像上拖动该区域中的一行像素的效果。这种效果让人想起在拖动或移动时滞后的旧电脑上的 GUI。静态毛刺是一种更常见的影响。当电视连接不良时,通常会出现这种情况。屏幕上的像素会出现横向抖动。

Ah, good old days.
这两个元素都很容易实现。通过随机选择拖动面部的方向来执行面部毛刺。面上的随机 x 轴或 y 轴位置被选作将要拖动的线。然后沿着一组行或列复制该行中的像素
对于静态毛刺,面部被分成随机数量的条。每一条都向左或向右移动一段随机距离。

Face Dragging (Left) and Glitch Static (Right)
“眼睛是心灵的窗户”是一个常见的艺术老生常谈,Vaporwave 经常强调这一特征。眼睛经常被黑条遮住,这是指广播电台过去如何审查个人。本文中较早展示的一个片段包括了这一点。偶尔,为了模仿希腊罗马雕像,图像中的瞳孔被完全移除。

Eye Glitch Drag, Source
我见过的一个更罕见、更迷人的效果是仅仅眼睛的小故障。这类似于面部拖动效果,但是,只有眼睛的部分被拖动,以创建哭泣和阴沉的外观。我发现这种风格要么绝对吸引观众,要么让他们毛骨悚然。
审查条要求适度使用几何学。找到双眼的中心后,我找到了连接两点的向量。然后我用垂直的单位向量乘以一些标量来找到相对于双眼中心的审查条的角。
眼球阻力效应类似于面部阻力。我没有把整个眼睛拉成一列或一行,而是把眼睛描述成一条条的,然后把它们一个一个地拉成随机的长度。

Eye Drag (Left) and Censor bar (Right)
蒸汽波艺术——最后一笔
正如我之前提到的,蒸汽波艺术不仅仅是可怕地扭曲了一幅图像。没有那些 80 年代和 90 年代的参考,我的作品感觉不太完整。所以我拿了一堆图像和时代精神的符号,用几个随机发生器把它们组合起来,扔在那里。
最重要的是,我添加了一个高对比度的滤镜,并用高斯噪声对图像进行了爆破,使图像呈现出一种老式阴极射线管电视和计算机的美感。
图库和结果

我对这个项目的结果非常满意。当我开始写剧本的时候,我并没有过高的期望。然而,由于程序的完全随机性,偶尔会产生一些有味道的结果。
此外,作为一个技术领域的人,我并不期望用我的技能来创造艺术。这个程序很好地展示了新兴的计算机视觉领域的艺术应用。对于那些需要实际应用的人来说,这样的程序类似于社交媒体应用 Snapchat 的面部过滤器。

尽管这个项目很有趣,但我不认为自己会在上面投入太多时间。也就是说,该程序确实有一些不完整的功能,我欢迎有兴趣的人来开发。我想在审查栏上添加随机的“尖锐”文本,这是 Vaporwave 艺术中的另一个陈词滥调。


Yours Truly





以上图片由de gree原创拍摄(除了我的那张)。
二等奖;荣誉奖;H 奖
尽管其中一些图片很棒,但重要的是要承认这个脚本具有难以置信的随机性。我在选集里挑了一些我最喜欢的。偶尔,一些生成的作品绝对疯狂。

Houston, we have lift off

We get it…
密码
下面的代码生成了一个图像。创建一个帐户,为 TimChinenov/VaporWaveArt 的发展做出贡献…
github.com](https://github.com/TimChinenov/VaporWaveArt)
如果你使用这个脚本,我很想看看你会生成什么样的图像!能分享就分享吧。如果加入更多的元素,那就更好了!
计算机视觉:掩模 R-CNN 的实例分割
这是计算机视觉旅程 系列的第四部分。在本文中,我们将探索掩模 R-CNN,以了解实例分割如何与掩模 R-CNN 一起工作,然后使用 Keras** 预测具有掩模 R-CNN 的图像的分割
第一部分——CNN,R-CNN,快速 R-CNN,更快 R-CNN
第三部分——用 YOLOv3 使用 Keras 进行物体检测
什么是实例切分,与语义切分有什么不同?
语义分割在像素级检测图像中存在的所有对象。输出具有不同类别或对象的区域
语义分割以语义上有意义的方式对像素进行分组。属于人、道路、建筑物、栅栏、自行车、汽车或树木的像素被单独分组。

Semantic Segmentation
实例分割是识别图像中每个已知对象的每个对象实例。
实例分割为图像的每个像素分配一个标签。它用于计算物体数量等任务

Instance segmentation
实例分段需要
- 图像中所有对象的对象检测。这里的目标是对单个对象进行分类,并使用边界框对每个对象实例进行定位
- 分割每个实例。这里的目标是在不区分对象实例的情况下,将每个像素分类到一组固定的类别中
屏蔽 R-CNN
屏蔽 R-CNN 扩展更快 R-CNN。
屏蔽 R-CNN 和更快的 R-CNN 有什么不同?
掩模 R-CNN 具有用于以像素到像素的方式预测每个感兴趣区域(RoI)上的分割掩模的附加分支
更快的 R-CNN 不是为网络输入和输出之间的像素到像素对齐而设计的。
更快的 R-CNN 有两个输出
- 对于每个候选对象,类别标签和包围盒偏移;
屏蔽 R-CNN 有三个输出
- 对于每个候选对象,类别标签和包围盒偏移;
- 第三个输出是对象遮罩
口罩 R-CNN 和更快的 R-CNN 有什么相似之处?
-掩码 R-CNN 和更快的 R-CNN 都有一个用于分类和包围盒回归的分支。
-两者都使用 ResNet 101 架构从图像中提取特征。
-两者都使用区域提议网络(RPN)来生成兴趣区域(RoI)
Mask R-CNN 是如何工作的?
掩模 R-CNN 模型分为两部分
- 区域提议网络(RPN)提出候选对象包围盒。
- 二进制掩码分类器,为每个类别生成掩码

Mask R-CNN Source: [Mask R-CNN Paper](https://arxiv.org/pdf/1703.06870.pdf)
- 图像通过 CNN 运行以生成特征地图。
- 区域提议网络(RPN)使用一个 CNN 来生成多个感兴趣区域(RoI ),使用一个轻量级的二进制分类器。它在图像上使用了 9 个定位框。分类器返回对象/非对象分数。非最大抑制应用于具有高客观性分数的锚点
- RoI 对准网络输出多个边界框而不是单个确定的边界框,并将它们扭曲成固定的尺寸。
- 然后将扭曲的要素输入到完全连接的图层中,使用 softmax 进行分类,并使用回归模型进一步优化边界框预测
- 扭曲的特征也被送入掩模分类器,该分类器由两个 CNN 组成,为每个 RoI 输出一个二进制掩模。掩码分类器允许网络为每个类别生成掩码,而不会在类别之间产生竞争

锚箱
Mask R-CNN 使用锚定框来检测图像中的多个对象、不同比例的对象以及重叠的对象。这提高了目标检测的速度和效率。
锚点框是一组具有一定高度和宽度的预定义边界框。定义这些框是为了捕捉要检测的特定对象类的比例和纵横比。

Image with multiple anchor boxes Source: matterport
为了预测图像中的多个对象或对象的多个实例,Mask R-CNN 进行了数千次预测。最终的目标检测是通过去除属于背景类的锚框来完成的,剩余的锚框通过它们的置信度得分来过滤。我们发现 IoU 大于 0.5 的锚盒。使用下面解释的非最大抑制来选择具有最大置信度得分的锚框
并集上的交集— IoU
IoU 通过算法计算两个边界框的并集上的交集,即地面真实的边界框和预测框的边界框

当 IoU 为 1 时,这将意味着预测的和真实边界框完全重叠。
为了在图像中检测一次对象,非最大抑制会考虑 IoU >为 0.5 的所有边界框
如果我有多个 IoU 大于 0.5 的边界框怎么办?
非最大抑制
- 非最大抑制将移除 IoU 小于或等于 0.5 的所有边界框
- 选取 IoU 值最高的边界框,并抑制其他边界框以识别同一对象

Bounding refinement after applying Non Max Suppression
使用 Keras 屏蔽 R-CNN 代码
为此我们使用matter port Mask R-CNN。本例中使用的 Mask R-CNN 模型是在 COCO 数据集上预训练的。
步骤 1:克隆屏蔽 R-CNN 库
git clone [https://github.com/matterport/Mask_RCNN.git](https://github.com/matterport/Mask_RCNN.git)cd Mask_RCNN$ python setup.py install
****第二步:从matter port下载 COCO 模型的预训练权重。将文件放在名为“mask_rcnn_coco.h5”的 Mask_RCNN 文件夹中
第三步:导入所需的库
# import the necessary packages
from mrcnn.config import Config
from mrcnn import model as modellib
from mrcnn import visualize
import mrcnn
import numpy as np
import colorsys
import argparse
import imutils
import random
import cv2
import osfrom matplotlib import pyplot
from matplotlib.patches import Rectangle%matplotlib inline
第四步:我们创建一个myMaskRCNNConfig类,它继承了Mask R-CNN Config类。**
因为我正在使用 CPU,所以设置 GPU_COUNT=1
COCO 数据集有 80 个标签,所以我们将 NUM_CLASSES 设置为 80 + 1(用于背景)
class myMaskRCNNConfig(Config):
# give the configuration a recognizable name
**NAME = “MaskRCNN_inference”**
# set the number of GPUs to use along with the number of images
# per GPU
**GPU_COUNT = 1**
**IMAGES_PER_GPU = 1**
# number of classes (we would normally add +1 for the background
# but the background class is *already* included in the class
# names)
**NUM_CLASSES = 1+80**
****步骤 5:创建一个 myMaskRCNNConfig 类的实例
config = myMaskRCNNConfig()
步骤 6:使用我们创建的配置实例**初始化“推理”的屏蔽 R-CNN 模型
print(“loading weights for Mask R-CNN model…”)
**model = modellib.MaskRCNN(mode=”inference”, config=config, model_dir=’./’)**
****第七步:为屏蔽 R-CNN 加载权重。这些是 COCO 数据集的预训练权重
**model.load_weights(‘mask_rcnn_coco.h5’, by_name=True)**
步骤 8:定义 coco 模型的 80 个类和 1 个用于背景(BG)
**class_names** = [**‘BG’**, ‘person’, ‘bicycle’, ‘car’, ‘motorcycle’, ‘airplane’,
‘bus’, ‘train’, ‘truck’, ‘boat’, ‘traffic light’,
‘fire hydrant’, ‘stop sign’, ‘parking meter’, ‘bench’, ‘bird’,
‘cat’, ‘dog’, ‘horse’, ‘sheep’, ‘cow’, ‘elephant’, ‘bear’,
‘zebra’, ‘giraffe’, ‘backpack’, ‘umbrella’, ‘handbag’, ‘tie’,
‘suitcase’, ‘frisbee’, ‘skis’, ‘snowboard’, ‘sports ball’,
‘kite’, ‘baseball bat’, ‘baseball glove’, ‘skateboard’,
‘surfboard’, ‘tennis racket’, ‘bottle’, ‘wine glass’, ‘cup’,
‘fork’, ‘knife’, ‘spoon’, ‘bowl’, ‘banana’, ‘apple’,
‘sandwich’, ‘orange’, ‘broccoli’, ‘carrot’, ‘hot dog’, ‘pizza’,
‘donut’, ‘cake’, ‘chair’, ‘couch’, ‘potted plant’, ‘bed’,
‘dining table’, ‘toilet’, ‘tv’, ‘laptop’, ‘mouse’, ‘remote’,
‘keyboard’, ‘cell phone’, ‘microwave’, ‘oven’, ‘toaster’,
‘sink’, ‘refrigerator’, ‘book’, ‘clock’, ‘vase’, ‘scissors’,
‘teddy bear’, ‘hair drier’, ‘toothbrush’]
步骤 9:为图像中检测到的对象绘制方框的功能
# draw an image with detected objects
**def draw_image_with_boxes(filename, boxes_list):**
# load the image
data = pyplot.imread(filename)
# plot the image
pyplot.imshow(data)
# get the context for drawing boxes
ax = pyplot.gca()
# plot each box
for box in boxes_list:
# get coordinates
y1, x1, y2, x2 = box
# calculate width and height of the box
width, height = x2 - x1, y2 - y1
# create the shape
rect = Rectangle((x1, y1), width, height, fill=False, color='red', lw=5)
# draw the box
ax.add_patch(rect)
# show the plot
pyplot.show()
步骤 10:我们最终做出预测,并在检测到的物体周围画出包围盒
加载图像,然后将其转换为 numpy 数组
from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_arrayimg = load_img(‘donuts.jpg’)pyplot.imshow(img)img = img_to_array(img)

Original image of Donuts
我们现在做出预测
# make prediction
results = model.detect([img], verbose=0)
结果是我们传递给 detect() 函数的图像的字典。
字典中有边界框、遮罩、类别和分数的关键字。每个键指向图像中检测到的多个可能对象的列表。
这本字典的关键字是
- ROI’:被检测物体的感兴趣区域(ROI)。
- 遮罩’:被检测对象的遮罩。
- Class _ ids’:被检测对象的类整数。
- 分数’:每个预测类别的置信概率。
步骤 11:通过在感兴趣区域(ROI)周围绘制边界框来可视化结果
# visualize the results
draw_image_with_boxes('donuts.jpg', results[0]['rois'])

画面具
# get dictionary for first prediction
**from mrcnn.visualize import display_instances
r = results[0]**# show photo with bounding boxes, masks, class labels and scores
**display_instances(img, r[‘rois’], r[‘masks’], r[‘class_ids’], class_names, r[‘scores’])**

Bounding boxes, masks, class and their scores using Mask R-CNN
要查找对象和类的数量
classes= r['class_ids']
print("Total Objects found", len(classes))
for i in range(len(classes)):
print(class_names[classes[i]])

No. of objects and their classes
参考资料:
https://medium . com/@ Jonathan _ hui/image-segmentation-with-mask-r-CNN-ebe6d 793272
http://www . cs . Toronto . edu/~ urta sun/courses/CSC 2541/08 _ instance . pdf
** [## 如何在 Keras 中使用 Mask R-CNN 进行照片中的对象检测
目标检测是计算机视觉中的一项任务,涉及识别一个或多个目标的存在、位置和类型
machinelearningmastery.com](https://machinelearningmastery.com/how-to-perform-object-detection-in-photographs-with-mask-r-cnn-in-keras/)**
机器学习中的概念漂移和模型衰退
概念漂移是基本相同数据的标签随时间的漂移。它导致新数据的决策边界与根据早期数据/标签建立的模型的决策边界不同。对随机采样的新数据进行评分可以检测出偏差,使我们能够根据需要触发昂贵的重新标记/重新训练任务…
没有什么是永恒的。甚至没有一个精心构建的模型是建立在大量标记良好的数据之上的。它的预测能力会随着时间而衰减。概括地说,模型会以两种方式衰退。由于数据漂移或由于概念漂移。在数据漂移的情况下,数据会随着时间的推移而演变,从而可能引入以前未见过的各种数据和所需的新类别。但是对以前标记的数据没有影响。在概念漂移的情况下,我们对数据的解释随着时间而变化,即使数据的总体分布没有变化。这导致终端用户将模型预测解释为对于相同/相似数据已经随时间恶化。此处的修复需要重新标记受影响的旧数据并重新训练模型。数据和概念也可能同时漂移,使问题变得更加复杂…
就像让湖人队在世界职业棒球大赛中对阵爱国者队,当概念/比赛和数据/球员都改变时,看台上会有很多人挠头……
这篇文章是关于后期制作场景中概念漂移的检测,在这种场景中,根据旧数据训练的模型用于对新输入的数据进行分类。对旧数据进行持续的重新标记和重新训练是极其昂贵的,因此我们希望检测概念漂移,并根据需要采取行动。为了便于说明和可视化,我们坚持二维(即两个特征)和两个类来划分特征空间。但是检测的方法是通用的。一旦检测到漂移,就需要更新模型。细节将取决于应用,但一般来说:
- 如果我们诊断出一个概念漂移,那么受影响的旧数据也需要重新标记并重新训练模型。
- 如果我们诊断出数据漂移,则需要标记足够多的新数据以引入新的类,并重新训练模型
- 当我们发现数据和概念都发生了漂移时,上述两种情况都出现了
这个帖子的重点是概念漂移。数据漂移是本系列下一篇文章的主题。我们查看一些代码片段进行说明,但是可以从 github 下载复制报告结果的完整代码。
更新(2020 年 8 月 21 日):在 neptune.ai 上看到了一篇关于这个话题的好文章,作者是 Shibsankar Das。处理概念漂移的最佳实践。
1.概念漂移
当我们对数据的解释随着时间而变化时,即使数据可能没有变化,也会出现概念漂移。我们过去一致认为属于 A 类的东西,我们现在声称它应该属于 B 类,因为我们对 A 和 B 的性质的理解已经改变了。这是纯粹的概念漂移。显然需要进一步的解释和背景。例如,一段文本在 1960 年可以被合法地标记为属于一个类别,但在 2019 年属于另一个类别。因此,1960 年建立的模型对 2019 年相同数据的预测将会有很大误差。下面的图 1 是一个极端的例子,也许是用来说明游戏规则改变时的问题。

Figure 1. Data has not changed. But our interpretation and assigned class thereof have.
2.改变决策界限
概念漂移可以被看作是决策边界随时间的变化。考虑图 2 中模拟的两类情况。

Figure 2. Concept drift can be detected by the divergence of model & data decision boundaries and the concomitant loss of predictive ability
- 在时间 t1,线 y = x 清楚地将类分开,并且有一些好的分隔来引导。我们在该数据上训练 SVM 分类器,并且获得的模型决策边界与模拟数据的决策边界完全一致。暂时一切都很好,我们的预测是正确的
- 新数据在稍后的时间 t2 到来,我们手动标记它(测试预测是否仍然正确的唯一方法!).所有的标注都是根据我们在标注时对数据的理解。这是关键点。随着我们对数据的解释的发展,我们最终将一些数据放在 A 中(同时在时间 t1 将类似的数据放在 B 中),并将一些其他数据放在 B 中(同时在时间 t1 将类似的数据放在 A 中)。
- 因此,新数据有一个明显的决策边界,该边界与基于类似数据的旧解释建立的模型决策边界不一致。因此,基于模型的预测对于标记的区域来说是错误的。
因此,数据决策边界的不断变化是概念漂移的症结所在。那么,检测它的唯一方法当然是努力在常规基础上标记至少一些新数据,并寻找模型预测能力的退化。当我们感到退化不再可容忍时,我们将需要根据当前对数据的理解,用重新标记的数据重建模型。
3.模拟概念漂移
我们继续处理如图 2 所示的类似问题,但是是定量的,以便触发“重建!”当模型性能下降到阈值时发出信号。这是问题陈述。
- 数据决策边界是线性的(为了简单起见,这不是必需的),开始时与水平线成 5 度角。
- 我们在这个标记良好的数据上训练一个 SVM 分类器。
- 新一批数据到达时,每一批都遵循逆时针旋转 0.2 度的判定边界。
- 我们在批中随机抽取 10%的新数据,并根据手头的模型评估 f1 分数。如果 f1 分数低于可接受的值,我们将触发重新标记任务,并继续上面的步骤 2
3.1 生成数据
A 类和 B 类都将从 1000 个数据点开始。每新增一批,每班加 125 分。每批的数据判定界限( w )不同。
3.2 构建 SVM 分类器
我们在标记数据上建立 SVM 模型,并使用该模型获得测试/采样数据的 f1 分数。
3.3 检测衰变
标记 25 个随机选择的数据点(该批的 10%),并与手头最新模型的预测进行比较。当样本的 f1 分数低于阈值(此处为 0.925)时,我们会触发重新标记/重新训练任务。
3.4 重新贴标签
随着数据决策边界的旋转,并且越来越偏离模型的决策边界,模型预测会持续变差。当采样数据的 f1 分数下降到大约 0.925 时,我们根据当前数据决策边界重新标记所有数据。现在,在我们具有 2 个特征和线性数据/模型决策边界的简单几何图形中,很容易找出错误分类的新数据。但一般来说不会,所以我们重新标记所有数据。
3.5 重新训练以完成循环
该模型需要使用更新的标签重新训练,以便恢复其预测能力。
4.结果
下面的图 3 证实了我们的预期。经过几个批次的漂移后,新采样的数据与起始/最终(当 f1 分数下降到 0.925 时)数据判定边界一起绘制。这些图显示了会被先前构建的模型错误分类的新数据量。根据最终数据决策边界重新标记,并在该点重建模型,恢复完美的 f1 分数。所有数据在重建后绘制,以显示更新后的模型的决策边界完美地分离了两个类。

Figure 3. As the new data obeys a continuously rotating decision boundary, the model predictions for randomly sampled new data get steadily worse. The misclassified zones are visible when just the new data alone is plotted. When a threshold for quality is breached, we trigger a task to re-label all the data as per the decision boundary of the new data and rebuild the model. The dense plots after rebuild show all the data to be perfectly separated by the model/data decision boundary.
下面的图 4 显示了模型随着概念的漂移而衰退,随后是在重新标记和重建任务后的恢复。旋转的总角度是概念漂移的代表。

Figure 4. The f1-score of the model suffers as the model’s decision boundary gets farther and farther away from the decision boundary of the new data. It recovers upon rebuilding
5.结论
考虑到需要手动重新标记旧数据,修复概念漂移驱动的模型衰减可能会很昂贵。可以通过全局测量来检测漂移,例如采样的新数据的 f1 分数恶化。我们可以在这里对所有数据进行完全的重新标记,因为我们的标记不是手动的,而是由公式驱动的。然而,在真实情况下,这是不可能的。即使是粗略识别需要重新标记的旧数据,也有助于减少手动工作。需要进一步研究的东西。
我们将在下一篇文章中继续讨论数据漂移和相关问题。
原载于 2019 年 4 月 25 日【http://xplordat.com】。
概念学习和特征空间

我们如何教会机器理解一个想法?
2004 年夏天,世界各地数百万的电影观众体验了《我,机器人》——《T1》,这部电影描绘了一个美好的未来,在这个未来里,人形机器仆人将成为社会不可或缺的一部分。这部电影受到阿西莫夫同名短篇小说集的启发,邀请观众提出与机器智能、人工意识、强人工智能以及最终对人类意味着什么有关的常见问题。
虽然有更好的电影处理类似的哲学主题,童年怀旧是一个强大的东西;所以,在考虑如何最好地介绍概念学习的时候,一个来自我,机器人的场景浮现在脑海里。在这个场景中,侦探史普纳(威尔·史密斯饰)审问一个被怀疑谋杀了它的创造者的机器人。这两种智能讨论人类的情感,Spooner 试图激怒机器人,以表明它已经学会模拟愤怒:

“机器人会写交响乐吗?机器人能把画布变成美丽的杰作吗?”—勺子
为了回答这个问题,我们首先要问自己,机器人是否能理解交响乐是什么?机器能学会从尖叫的婴儿、黑板上的钉子或麦莉·赛勒斯的音乐中分辨出交响乐吗?毕竟,为了让机器创作交响乐,它必须首先能够识别一首。
概念学习
教机器区分例子和非例子的想法,如交响乐,愤怒,美丽,狗,猫等。叫做概念学习。
在概念学习中,我们的目标是使用数据来教会机器解决二分类问题。也就是说,将数据点分类为属于或不属于特定的概念或想法。
从形式上来说,概念学习需要试图找到一种学习方法和一组特征,用它们来引导机器学习所选概念的一组特征的特征函数。这只是一个布尔值函数,给定某个示例的特征值,如果该示例属于或不属于该概念,则分别为该示例分配值 1 或 0 。

Characteristic function for lookin’ good — Looks_Good(Will Smith) = 1, Looks_Good(You) = 0
在我的关于解读数据的帖子中,我谈到了采用好的数据观点的重要性和需要注意的地方。特征只是从我们的数据中得到的列/属性,所以为概念学习选择特征,本质上是为学习问题选择一个数据观点。对于概念学习来说,一个好的数据观点是细粒度的和相关的,足以让我们描述我们试图学习的概念,但不会充斥着不必要的信息。概念学习中的一半战斗是选择一组特征:如果特征选择不好,可能根本不可能学习任何类似于特征函数的东西,例如,想象一下试图通过只看动物的高度来判断它是不是狗。另一方面,如果我们选择在我们的学习模型中包含太多的特征,噪音和增加的复杂性会使学习过程变得非常困难。
如果我们觉得我们可以从足够大的数据集中直观地捕捉到一个具有一组特征的概念,我们实际上如何让机器尝试从这些特征样本中学习呢?对我们人类来说幸运的是,有一种相当优雅的方式来数学地描述不同数据点有多“接近”,因此,通过精心选择的特征,属于该概念的示例将分组在一起,并以机器可以理解的数字方式与非示例区分开来。这种“接近度”的描述要求我们在一个特征空间中表示我们的数据。
特征空间中的概念
给定一个概念学习问题的一组特征,我们可以将该特征集解释为一个特征空间。
给定一些数据,特征空间就是从该数据中选择的一组特征的所有可能值的集合。总是可以仅使用数字来表示特征值,从而表示特征空间,并且进一步以这样的方式来表示,使得特征空间可以被解释为实空间。
我们所说的真实空间是什么意思?一位数学家会说,我们的 n 维特征空间可以表示为同构于 向量空间ℝⁿ,但是它们的意思是我们可以在我们的特征空间中移动和跳舞,并且有 n 维空间可以让我们进行隐喻性的移动。

More dimensions? More room for activities.
抛开跳舞不谈,真正重要的是我们可以将我们的数据表示为一个特征向量,给出在 n 维空间中的坐标,其中 n 是(通常)特征的数量。举个例子,假设我们有特征为高(米)、宽(米)和重(公斤)的数据。我们可以把它写成一个元组(高,宽,重),我们可以把它看作一个三维空间:

3D feature space for (height, width, weight) populated with some examples.
数据的这种空间表示非常有用的原因之一是,它允许我们在数据中引入“距离”的概念,从而引入“接近”的概念。例如,如果我们试图学习“狗的概念”,那么猫应该比房子更接近狗,这似乎是很自然的。可以肯定地说,人们并不经常把房子误认为是狗,但可能有一些相当像狗的猫。

Left: Atchoum, the dog-cat. Right: The doggiest house I could find.
给定一些关于狗和非狗的标记数据,我们可以选择一些适当的特征,并在我们的特征空间中绘制数据。如果我们在我们的特征空间中看到代表狗的数据点的清晰聚类,那么我们的数据中的特征一定很好地表征了狗。如果我们的特征在描述狗方面做得很好,我们可能会期望看到任何表示猫的数据点比我们期望看到的任何房子更接近狗群。在我们的数据具有良好特征的理想场景中,我们的特征空间中的 空间距离类似于我们试图学习的目标概念的概念距离 。下面我们可以看到一个概念学习问题的三个不同质量的可能特征空间,有两个特征:

Examples of different 2 dimensional feature spaces for a concept learning problem.
上图中最左边的特征空间描绘了概念学习的理想特征空间。属于目标概念的红色数据点可与不属于目标概念的蓝色数据点分开。在这里,空间距离对应的是概念距离,这正是我们想要的。这意味着存在基于这两个特征的完美特征函数,并且该目标概念因此是可学习的。中间的图像描绘了用于概念学习的更真实的特征空间。空间距离近似对应于概念距离,并且由于非完美分离,我们不能基于这些特征定义完美的特征函数,但是我们可以学习对具有小误差的特征函数的有用近似。在一个差的特征空间的最右边的例子中,我们甚至不能希望用任何合理的误差量来近似一个连续的特征函数,因为例子和非例子是混合的。在这种情况下,我们选择的数据特征在将空间距离与概念距离相关联方面表现不佳。
如果我们有一个相对较好的特征空间,如上面的前两个例子,我们的分类问题就变成了如何最好地划分我们的特征空间,以便以最小的误差捕获概念的问题,这是一个纯数值问题,是机器的适当领域。这是特征空间和概念学习之间的本质结合。
绘制决策边界
如果我们的直觉告诉我们,在我们的 n 维特征空间中存在代表概念的潜在信息,那么让机器使用这些特征来学习概念就像给它一些训练数据并要求它在空间中绘制一条边界一样简单,以最小的误差将示例和非示例分开。然后,为了分类新的和以前没有见过的例子,我们可以简单地计算它们在决策边界的哪一边。

One possible decision boundary for the given 2D concept learning problem.
你听说过的每一种分类学习模型:支持向量机、神经网络、朴素贝叶斯分类器、K 近邻、遗传算法等等。所有这些都(明确地或隐含地)依赖于这种特征空间的思想,并且它们都是用于寻找好的决策边界的简单不同的方法,根据基础特征空间的性质具有不同的优点和缺点。
如果你是机器学习的新手,你可能会问:为什么我们需要让机器来解决这些问题,为什么我们不能可视化我们的特征空间并绘制我们自己的决策边界?答案是双重的:首先,我们根本不是在寻找“画”一个决策边界——我们在寻找一个函数,它将一个例子的特征值作为参数,告诉我们这个例子是否属于这个概念。虽然这个函数可以在特征空间中画出来,但是这个理论的视觉元素只是为了帮助你理解人类。这台机器没有将特征空间可视化——它只是在执行纯粹的数字运算。为了找到一个对应于决策边界的函数,你会发现你必须自己做这些相同的计算,最好是留给机器去做。其次,大多数概念和想法都很复杂,需要高维数据和特征空间来捕捉。这些数据中的模式对于具有三维直觉的人类来说很少是显而易见的。机器不会像我们一样看到数据——它们的感官是数字的,它们的决策是二元的。人类的直觉对于让机器尽可能容易地解决概念学习问题是必要的,但寻找模式是机器的工作。

Where does dog end and cat begin? Learning to recognise a concept in an n-by-n image is an n²-dimensional learning problem if we use each pixel intensity as a feature.
机器人会创作交响乐吗?
令人惊讶的是,一个机器人已经做到了。AIVA 是世界上第一个被音乐协会(SACEM)认可的人工智能作曲家。AIVA 代表人工智能虚拟艺术家——它的主要学习方法使用在大量无版权的经典分区上训练的深度神经网络。AIVA 的音乐已经被用于商业用途,其背后的团队正在继续进行音乐创作,并探索 AIVA 表达其创造力的其他途径。

Suck it Spooner!
AIVA 正在解决的不仅仅是一个简单的概念学习问题,但这篇文章中提出的想法确实是所有机器学习的核心,并将允许我们在未来的博客文章中谈论更复杂的想法。AIVA 不会独自完成所有的作品;它需要人为输入更高层次的元素,如配器,所以从某种意义上说,也许斯普纳侦探是对的,但似乎我们离独立的机器作曲家并不太远。
你可以在这里听 AIVA 的音乐,也可以在这里阅读一篇关于 AIVA 的深度文章。

这是一个总结——感谢阅读!
这篇博文是我下一篇博文的前奏,我的下一篇博文将更具技术性,并专注于核心技巧:一种优雅的数学方法,允许我们改善分离并获得更多关于数据的信息,而不增加我们的特征空间的复杂性。敬请期待!
如果你喜欢这个概念学习和功能空间的介绍,请随时联系我( )关于对未来博客文章的任何想法、疑问或建议!
正则化概念
这就是为什么正则化是机器学习中重要的一步!

让我们考虑我们正在开发一个机器学习模型。该模型通常使用“训练数据集”进行训练,并使用“测试数据集”进行测试。如果数据集未经预处理,模型的准确性通常会下降。事实上,在训练模型之前,大部分时间都花在预处理数据上。
模型精确度低的可能原因是什么?
有两种主要的可能性:
- 欠拟合
- 过度拟合
欠拟合
欠拟合是一种模型没有学习到数据集的广义模式的现象。这可能是由于数据集不充分,或者对该特定数据集使用了不适当的机器学习算法。

Underfitting
让我们考虑一个例子。该图包含表示正弦曲线的数据点。这些数据点被输入线性回归算法。这条线是模型的预测输出。很明显,模型的精确度并不是很高!为什么这样因为线性模型是在非线性数据集上训练的。因此,用一条线性线来拟合非线性数据会过度简化模型。
过度拟合
过度拟合是一种模型从数据集学习过多的现象。过度拟合模型在对其进行训练的数据中表现得非常好,但是在对一组新数据执行时,其准确性往往会降低。
让我们考虑同样的例子。在这种情况下,让我们使用多项式回归模型。通过使用多项式特征变换,可以找到数组中每个元素的幂,直到指定的幂。存储在多维数组中的这些值可用于输入多项式回归模型。随着多项式特征转换器中指定幂的增加,模型似乎学习到了比所需更多的内容,并且似乎是对训练数据集非常“特定”的输出。
过拟合模型被称为具有高方差

overfitting
当基函数的系数较大且相互抵消时,会出现过拟合现象!
正规化防止过度拟合!
正则化技术用于通过在给定的训练集上适当地拟合函数来减少误差,以避免过度拟合。这些函数实质上减少了每个特征的系数(β),从而减少了值被抵消的机会。回归方程的一个例子如下:

Linear Regression
正规化的类型
- L1
- L2
- 弹力网
L1(套索正规化):
L1 正则化背后的思想是将数据集减少到仅影响“目标变量”的最重要的特征。L1 正则化增加了等于系数绝对值之和的惩罚。

通过添加上述惩罚,一些特征的系数变为 0 ,剩余的特征将是最有用的特征。这种正则化方法可以看作是一种特征选择方法。
L2(岭正则化):
L2 正则化附加了一个等于系数平方值之和的惩罚。

等式中的λ是控制惩罚强度的超参数。
当λ→0 时,结果类似于线性回归
当λ→∞,所有特征都减少到 0。
当施加惩罚时,系数不会剧烈变化到 0,而是缓慢降低到 0。因此,与 L1 不同,L2 不能用于特征选择。
在这两种情况下,
惩罚越大,系数越小。
弹性网:
弹性网正则化是 L1 正则化和 L2 正则化的结合。
适用的罚款(P)如下:

在这种情况下,λ是一个共享参数,用于设置 L1 和 L2 之间的比率。因此,结果将是 L1 和 L2 正则化的混合。正则化的几何表示如下所示:

正规化的实施
导入所需的库:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Lasso,Ridge
from sklearn.pipeline import make_pipeline
加载数据集(本例中,使用了 Ames Housing 数据集:
data = pd.read_csv('house_prices.csv')
data.head()
将数据分为“列车数据”和“测试数据”:
from sklearn.model_selection import train_test_splitX = data.iloc[:,:-1]
y = data.SalePriceX_train,X_test,y_train,y_test = train_test_split(X,y,random_state = 9,train_size = 0.5)
1.要执行线性回归:
from sklearn.linear_model import LinearRegression,Lasso,Ridge
from sklearn.metrics import mean_squared_errorregressor = LinearRegression() #Linear model
regressor.fit(X_train,y_train)
y_pred = regressor.predict(X_test)
mean_squared_error(y_test,y_pred)
2.要执行套索回归:
lasso_model = Lasso(alpha = 140,max_iter = 100000, random_state=9)
lasso_model.fit(X_train,y_train) #Lasso model
y_pred = lasso_model.predict(X_test)
mean_squared_error(y_test,y_pred)
3.要执行岭回归,请执行以下操作:
ridge_model = Ridge(alpha = 0.00002,max_iter = 100000,random_state = 9)
ridge_model.fit(X_train,y_train) #Ridge model
y_pred = ridge_model.predict(X_test)
mean_squared_error(y_test,y_pred)
在每种情况下,都要从模型验证的测试数据中找到预测的“目标变量”和实际的“目标变量”之间的均方误差。
谢谢你阅读这篇文章。
请通过 LinkedIn 与我联系,或者您可以将发到我 LinkedIn 个人资料上的邮箱。
概念化知识图构建管道

互联网的出现使得大量内容创建者能够获取信息。由于这个原因,现在网上有大量的数据。为了提供有用的见解,我们需要一种有效的方式来表示所有这些数据。一种有效的知识表示方法是通过知识图。简而言之,知识图是一个庞大的互联数据网络。知识图是从知识库构建的。知识库从网页上的自由文本、数据库以及音频和视频内容中收集信息。知识图构建过程的基本流程如图 1 所示。

Figure 1. The knowledge graph construction pipeline
现在,让我们详细了解一下该管道中发生的流程。
在管道的第一阶段,我们从自由文本中识别事实。最初,我们搜索互联网,通过从自由文本中识别实体和实体涉及的关系来过滤有用的信息。这个识别过程使用自然语言处理技术进行,例如命名实体解析、词汇化和词干化。因此,在第一步中从自由文本中提取的数据可能类似于以下语句的形式。
"卢浮宫位于巴黎"
进行到管道的第二阶段,语句在知识库中以三元组的形式被概括;这些三元组将使用本体提取过程在不同的本体下分类,该本体提取过程也可以利用自然语言处理技术的能力。三元组由主语、谓语和宾语组成。主语和宾语是由谓语定义的关系中涉及的实体。因此,对于从自由文本中确定的前一个陈述,我们将其分解为以下知识库三元组形式。
- 主题:卢浮宫
- 谓语:位于
- 对象:巴黎
所以在一个知识库内,我们将以 islocated(卢浮宫,巴黎)的形式拥有上述关系。这是知识库中的一个三元组。实际上,知识库包括数百万个这样的三元组,我们也称之为事实。这些事实在知识库中被归入本体。本体是特定事实领域的识别类别。因此,本体解释了在那个类别中存在什么样的实体。例如,如果本体是'机场,那么,属于这个类别的一些实体可能包括'爱迪生机场'、戴高乐机场'、曼德利乌机场'等等。
知识库可以是特定领域的,也可以是通用的。医学知识库和学术研究论文知识库是一些特定领域的知识库。然而,通用知识库并不将它们的知识局限于特定的领域。他们对一般的世界事实和多个领域有更广泛的覆盖。
在我们前进到管道的最后一个阶段,即知识图之前,请参考下表,了解从原始论文中理解的各种知识库的一些特征。该表列出了过去几十年中最重要的知识库。
表 1。知识库及其特征



https://en.wikipedia.org/w/api.php ^1 维基百科 API :

Figure 2. The Freebase Topic Page, where a user adds a sibling property (Source: Bollacker et al. [3])
关于知识库,让我们进一步阐明 NELL 知识库,因为我们将考虑 NELL 处理其事实的方式,作为我们稍后将讨论的管道的知识图构建阶段的样本。
内尔
永无止境的语言学习者(NELL)是卡内基梅隆大学在 2010 年发起的一个项目[5]。它被建模来弥补学习系统和实际人类学习之间的差异。因此,它是基于对事实的不断学习塑造专业知识的概念。自 2010 年以来,内尔一直在不断了解事实。该知识库主要执行两项任务。
- 信息提取:搜索语义网以发现新的事实,积累这些事实并不断扩展其知识库。
- 增强学习过程:根据以前提取信息的经验,NELL 试图通过返回到前一天学习事实的页面,并搜索更新的事实来提高其学习能力。
内尔的事实是基于本体论的分类:实体或关系。基于实体的本体分类包括可能出现在该域中的实例的子域,而基于关系的本体分类包括基于连接实体实例的关系的事实的子域。NELL 中的事实是三元组(主语-宾语-谓语)的形式。举个例子,
事实示例:"自由女神像位于纽约州"
作为三元组,上述事实可以表示为 locatedIn (statueOfLiberty,newYork) 其中,
- 主题:状态自由
- 谓词:位于中
- 对象:纽约
使用文本上下文模式、正字法分类器、URL 指定的 ML 模式、学习嵌入、图像分类器和本体扩展器来提取 NELL 的事实。目前 NELL 受到限制,因为它不能修改其定义的学习过程。如果学习的过程可以基于先前的学习经验被动态地增强,NELL 可以改进其事实的质量和累积其事实的性能。
现在,让我们进入流水线的最后阶段,看看知识库中的三元组是如何转换成知识图的。
知识图表
知识图是一个由相互连接的实体组成的大型网络。基于来自知识库的三元组创建连接。知识图的主要目的是识别实体之间缺失的链接。为了进一步阐明这一点,让我们考虑一下从知识库中收集的以下示例关系。
- 好友(安妮、简)
- 朋友(简,吉姆)
- LivesIn(安妮,帕里斯)
- LivesIn(巴西吉姆)
- LivesIn(巴西简)
- 博宁(安妮,巴黎)
- 博宁(吉姆,巴黎)
如果我们试图建立一个只基于上述关系的基础知识图表,我们将能够看到下面的图表。

Figure 3. A knowledge graph constructed only using the observed facts
另一方面,有一些未知的关系没有明确地从知识库中检索出来,例如,
- 安妮和吉姆是朋友吗?
- 简的出生地是哪里?
这意味着这种关系可以被认为是缺失的环节。

Figure 4. The missing links in the knowledge graph
这些缺失的环节是使用统计关系学习(SRL)框架推断出来的。这些 SRL 框架计算推断/预测链接的相关置信度。以前的工作试图以不同的方式发现新的/缺失的信息,并计算推断这些信息的置信度。这些将在以下段落中简要讨论。
在管道的第一阶段,我们从自由文本中提取事实,我们也经常以错误的事实结束。为了从这些事实中识别出稳定的知识图,Cohen 等人提出了一种对提取的事实进行联合评估的方法论[6]。这种方法的问题是,它只考虑了提取的事实中可能出现的一组微不足道的错误。
作为流水线的第二阶段,我们从提取的事实中找到三元组,这些三元组将构成知识库。继续进行,在最后阶段,我们需要通过从知识库三元组中推断缺失的链接来发现新的事实。为此,继 Cohen 之后,Jiang 等人借助 Markov 逻辑网络来发现提取的事实之间的关系[7]。他们以一阶逻辑规则的形式定义了本体约束。这些约束将管理可以推断的可能关系。然而,在马尔可夫逻辑网络中,我们称之为“谓词”的逻辑关系的变量只能是布尔值。这对于推断事实的可信度是不利的。
这导致了概率软逻辑()的定义,它使用江等人和马尔可夫逻辑网络的概念,并定义了一个复杂的统计关系框架,该框架联合推理所有事实,以基于先前的事实发现新的/缺失的信息[8]。除此之外,PSL 从概率上计算了一个置信度值,这是一个在[0,1]范围内的软真值,以表明 PSL 项目根据所提供的信息认为事实有多真实。
一旦发现了新的/缺失的信息,并且计算了它们的置信度,我们就可以用高度可信的事实来构建知识图。这将为我们提供一个图表,其中除了提取的原始事实之外,还提供了无法明确驱动的新信息。这就是我们如何用知识库中的事实和基于现有观察新发现的事实构建知识图表。
最后,当我们在更高的层面上总结知识图管道的这些级联步骤时,以下是在构建知识图时发生的过程[9]。
阶段 1 :从自由文本中提取事实
- 从自由文本、非结构化数据源和半结构化数据源中提取数据。
- 处理这些原始数据是为了提取信息。这涉及到实体、关系和属性的提取,它们是进一步定义实体和关系的属性。
- 如果数据已经结构化,与步骤 1 不同,该数据将直接与来自第三方知识库的信息融合。
- 在此之后,各种自然语言处理技术将应用于融合的知识和处理的数据之上。这包括共同引用解析、命名实体解析、实体消歧等等。
阶段 2 :从提取的事实中公式化三元组
- 以上步骤结束了知识库信息的预处理。然后,执行本体提取过程,以对提取的实体和它们各自的本体下的关系进行分类。
- 进行本体形式化后,事实将被提炼并作为三元组存储在知识库中。
阶段 3 :用新的链接和置信度构建知识图
- 为了从知识库构建知识图,将对这些三元组应用统计关系学习(SRL)。
- SRL 过程为每个事实而不是整个领域计算置信度,以确定这些事实在多大程度上是正确的。
- 在构建知识图时,将使用置信度来识别缺失的链接,并将形成新推断的关系链接。
由于推理中的置信度被合并到知识图中,一旦知识图被构建,关于事实在多大程度上被认为是真实的决定也可以基于置信度。因此,Cayley [10]生成的电影演员领域的样本知识图如下所示。

Figure 4. A sample knowledge graph of a movie actors domain
随后,这种知识图可以用于信息检索系统、聊天机器人、网络应用、知识管理系统等。,以有效地提供对用户查询的响应。
结论
到目前为止,我们已经提供了整个知识图管道如何工作的抽象解释。使用这些阶段中指定的技术将保证发现丢失的链接。然而,在知识图谱社区中仍然存在一个公开的未知数,即根据人类的观点识别错误的事实或三元组。目前,我们有基于领域和事实集计算现有和发现的关系的置信度的方法。然而,这并没有提供一种可靠的方法来判断事实是否会被实际的人类评估者评估为有效的事实。因此,在我们的下一篇文章中,我们将进一步详细阐述我们如何使用概率软逻辑等统计关系框架来推断缺失的环节,以及如何将足够的监督水平关联到模型中,以将事实与知识图中的众包真相相匹配。
参考
[1] Auer,s .,Bizer,c .,Kobilarov,g .,Lehmann,j .,Cyganiak,r .,& Ives,Z. (2007 年)。Dbpedia:开放数据网络的核心。在语义网中(第 722-735 页)。斯普林格,柏林,海德堡。
[2] Lenat,D. B .,& Guha,R. V. (1991 年)。Cyc 表示语言 CycL 的演变。美国计算机学会 SIGART 通报,2(3),84-87。
[3]博拉克,k .,埃文斯,c .,帕里托什,p .,斯特奇,t .,泰勒,J. (2008 年 6 月)。Freebase:一个用于构建人类知识的合作创建的图形数据库。2008 年 ACM SIGMOD 数据管理国际会议论文集(第 1247-1250 页)。AcM。
[4]vrandei,d .,& krtzsch,M. (2014 年)。维基数据:一个免费的协作知识库。美国计算机学会的通讯,57(10),78-85。
[5] Betteridge,j .,Carlson,a .,Hong,S. A .,Hruschka Jr .,E. R .,Law,E. L .,Mitchell,T. M .,& Wang,S. H. (2009 年)。迈向永无止境的语言学习。AAAI 春季研讨会:从阅读中学习和学会阅读(第 1-2 页)。
[6]科恩,W. W .,考茨,h .,&麦卡勒斯特,D. (2000 年 8 月)。强化软信息源。《第六届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集》(第 255–259 页)。ACM。
[7]江,s,劳得,d .,,窦,D. (2012 年 12 月).使用马尔可夫逻辑学习提炼自动提取的知识库。在数据挖掘(ICDM),2012 年 IEEE 第 12 届国际会议上(第 912-917 页)。IEEE。
[8]布罗克勒、米哈尔科娃和格图尔(2012 年)。概率相似逻辑。arXiv 预印本 arXiv:1203.3469。
[9] 刘峤, 李杨, 段宏, 刘瑶, & 秦志光. (2016). 知识图谱构建技术综述. 计算机研究与发展, 53(3), 582–600.
[10]开源图形数据库:https://cayley.io/
图片来源:【https://www.iconfinder.com/
条件独立性——贝叶斯网络的支柱
条件独立性直觉、推导和例子
有时候维基百科里的解释并不是最容易理解的。

From https://en.wikipedia.org/wiki/Conditional_independence
1.条件独立性的直觉
假设 A 是孩子的身高而 B 是孩子认识的单词数。似乎当 A 为高时, B 也为高。
有一条信息会让 A 和 B 完全独立。那会是什么?
孩子的年龄。
孩子知道的身高和单词数不是独立的,但如果你提供孩子的年龄,它们是有条件独立的。
2.数学形式

**A**: The height of a child
**B**: The # of words that the child knows
**C**: The child's age
记住这句话的更好方法是:

条件独立性基本上就是独立性的概念 P(A ∩ B) = P(A) * P(B) 应用于条件模型。
但是我见过的定义不仅有 P(A ∩ B|C)还有 P(A|B ∩ C)!

为什么当(Aㅛ B)|C 时,P(A|B ∩ C) = P(A|C)?
证据来了。

条件独立的要旨:知道 C 使 A 和 B 独立。
P(A,B|**C**) = P(A|**C**) * P(B|**C**)
3.应用程序🔥
为什么有条件的独立很重要?
因为它是我们使用的许多统计模型的基础。(例如,潜在类别模型、因素分析、图形模型等。)
A.贝叶斯网络(又名图形模型)中的条件独立性
贝叶斯网络使用图表示联合分布。具体来说,它是一个 有向无环图 ,其中每条边是一个条件依赖,而每一个节点是一个独特的随机变量。它还有很多其他的名字:信念网络、决策网络、因果网络、贝叶斯(ian)模型或概率有向无环图模型等。
看起来是这样的:

From Wikipedia: A simple Bayesian Network with conditional probability tables
为了让贝叶斯网络为概率分布建模,它依赖于一个重要的假设:给定其父变量,每个变量都有条件地独立于其非后代变量。
例如,我们可以将 P(草湿|洒水器,雨)简化为 P(草湿|洒水器),因为草湿有条件地独立于其非后代雨,给定洒水器。
利用这个性质,我们可以将整个联合分布简化为下面的公式:

这个近似(条件独立假设)有什么了不起的?
变量之间的条件独立可以大大减少参数的数量。
这减少了大量的计算,因为我们现在只考虑它的父节点,而忽略其他所有的东西。
我们来看看数字。
假设您有 n 个二进制变量(= n 个节点)。
无约束联合分布需要 O(2^n) 概率。
对于一个贝叶斯网络、任意节点最多有 k 个父节点,我们只需要 O(n * 2^k)个概率。(对于一定数量的类别,这可以在线性时间内进行。)
**n = 30 binary variables, k = 4 maximum parents for nodes**• **Unconstrained Joint Distribution**: needs 2^30 (about **1 million**) probabilities -> Intractable!• **Bayesian Network**: needs only **480** probabilities
我们可以使用条件独立性得到联合分布的有效分解表示。****
B.贝叶斯推理中的条件独立性
比方说,我想估计一下我的博客的参与度。设 p 为读者会为我的文章拍手的比例。我们将从人群中随机选择 n 名读者。对于 i = 1,…,n,让 Xi = 1 如果读者鼓掌,或者 Xi = 0 如果她/他不鼓掌。
在一种频率主义者的方法中,我们不把概率分布分配给 p 。
p 简单来说就是‘sum(Xi)/n’。
我们将把 X1,…,Xn 视为独立的随机变量。
另一方面,在贝叶斯推断中,我们假设 p 遵循一个分布,而不仅仅是一个常数。在这个模型中,随机变量 X1,…,Xn 是不独立,但给定 p 的分布,它们是条件独立的。
C.相关性≠因果关系
“相关性不是因果关系”意味着仅仅因为两件事相关并不一定意味着一个导致另一个。
这里有一个令人捧腹的出租车事故的例子:
一项研究表明,事故数量与出租车司机穿外套之间有着显著的正相关关系。他们发现外套可能会妨碍司机的行动,造成事故。一项新的法律准备禁止出租车司机开车时穿外套。
直到另一项研究指出,下雨时人们会穿外套…😂
P(accidents, coats | rain) = P(accidents | rain) * P(coats | rain)
两件事之间的关联可能是由影响它们的第三个因素造成的。这第三个因素叫做。混杂因素是雨,它是事故和穿外套之间联系的原因。
P(accidents | coats, rain) = P(accidents | coats)
请注意,这并不意味着事故与降雨无关。它的意思是:给定穿着外套的司机,知道下雨并不能给出更多关于事故的信息。
4.条件独立与边际独立
边际独立和普通独立是一样的,
有时,两个随机变量可能不是边际独立的。然而,在我们观察到一些第三变量之后,它们可以变得独立。
5.更多例子!
- 超速罚款金额 ㅛ 车型 | 速度
- 肺癌 ㅛ 黄牙 | 吸烟
- 孩子的基因 ㅛ 祖父母的基因 | 父母的基因
- 汽车的启动马达工作吗?ㅛ:车上的收音机能用吗? | 电池
- 未来 ㅛ 过去 | 现在(这是马氏假设!)
他们都是和 A ㅛ B | C 一样的形式。
A 和 B 如果不考虑 C 的话,看起来是相关的。然而,一旦我们将 C 包含在图片中,那么 A 和 B 之间的明显关系就消失了。如你所见,任何因果关系都是潜在的条件独立的。我们永远不会确切知道 A & B 之间的关系,直到我们测试每一个可能的 C (混杂变量)!
有条件的爱:调节神经网络的重正化技术的兴起
批处理规范化,以及围绕它成长起来的相关规范化策略的动物园,在最近的深度学习研究中扮演了一系列有趣的角色:作为一个神童优化技巧,作为关于 理论严密性的讨论的焦点,以及更重要的,但更多的是作为一种旁观者,作为一种灵活和广泛成功的途径,将条件信息注入模型。
条件重正化开始时足够谦虚,作为训练更灵活的风格转移模型的聪明技巧,但多年来,这个最初简单的技巧在复杂性和概念范围方面都有所增长。我不断看到这种策略的新变体出现,不仅仅是在文学的边缘,而是在其最核心和最新颖的进步中:从 2017 年 ImageNet 竞赛的获胜者到 2018 年最令人印象深刻的生成图像模型。我看得越多,就越想讲述这个简单想法的故事,我看着它从一个一次性的技巧发展成为一个广泛适用的以低复杂度的方式集成新信息的方法。
虽然我会一路做一些(相对简单的)数学力学的解释,但我的主要目标是探索条件规范化的不同用途,指出这一思想的发展或扩展点,并大体讲述一个连贯的故事,说明它及其智力后代如何在现代深度学习中发挥越来越重要的作用
什么是调理?

从最基本的统计学意义上来说,条件反射包括将某个变量或一组变量固定为固定值,并观察由于将你考虑的世界限制在符合这些条件的范围内而产生的移位分布。如果你考虑温度和月份的联合分布,你对“现在是什么温度”的信念分布,从全年来看,将与以月份=一月为条件的温度分布有意义的不同。联合分布中的其他特征因调节其他变量而改变的程度取决于两组特征在统计上的相关程度。继续我们之前的例子,我们不会特别期望我们在所有温度上的分布与当一周中的一天是星期天时的温度分布不同,因为天气不会以一定会使星期天比其他任何一天更热或更冷的方式变化。如果给定一个特性的条件分布与无条件分布的不同,那么知道该特性的值就给了你关于其余(假定未知)特性值的信息。
你可以通过一个预测框架来考虑这个问题:如果你只是通过从一年的分布中取样来选择一个温度,你的误差在预期中会相当高。如果你知道你在预测一月份的某一天,你可能会大大减少你的误差。事实上,这种简单的概念工具正是用来表示神经网络中的预测问题的:学习 y 的分布(或点估计)预测,以已知变量 X 的值为条件。在预测任务中,希望 X 变量携带大量关于 y 值的信息,以便根据它们建立模型可以减少相对于随机无条件猜测的错误。
因此,由于所有监督模型都以已知输入特征为输出分布的条件,所以所有模型在某种意义上都是有条件的,但我们倾向于将该术语保留给模型的特定子集,在这些模型中,我们使用条件作为控制杠杆,使我们的模型执行所需的行为。这里有一些我们称之为条件反射的重复使用案例,我将在后面详细讨论:
- 根据所提的具体问题,回答关于图像的问题
- 从基于特定视觉类别(猫、狗、房子等)的生成图像模型中提取样本
- 生成模拟某些特定输入图像的视觉风格的图像
所有这些任务的一个共同特征是,在这些情况下,我们希望对模型在部署时执行的计算进行更高程度的控制。我们希望能够提出(半)任意的问题,并得到我们碰巧回答的任何问题的答案,而不是在测试时硬编码的一些图像摘要。我们不只是从所有图像的空间中取样,我们想要告诉模型我们想要取样一只猫,或者我们想要取样一幅莫奈风格的画。
实际上,这种控制是通过设计一种途径来实现的,将某种信息的压缩形式(比如说,一个问题)添加到我们的网络中,并将其与模型的“主要”输入(比如说,一幅图像)合并。然后,我们可以训练模型学习答案的输出分布,条件是图像和问题的固定值,这有望比我们看不到图像或不知道问题是什么更精确。如果我想出于某种实际目的使用图像生成模型(GANs 必须对某种实用的东西有好处),那么如果我能给模型一个请求并让它给我一个合理的响应,这个模型会更有用。换句话说,即使图像生成器可以完美地捕捉图像的全局分布,通常我们实际上并不想要从全局分布中采样,我们想要采样一些特定的东西。
我还倾向于将条件反射视为多任务学习的简化形式:我们希望能够执行在某些情况下可能被认为是不同的任务(回答问题 A,回答问题 B),但为此我们使用带有一些条件参数的联合模型,而不是完全独立的模型,因为我们认为任务足够相似,可以从共享参数和提取的特征中受益。
我认为条件作用的世界必须提供一些有趣的概念见解,但在举例之前解释它们有点困难,所以我想带你参观一些不同的条件作用方法,并在此过程中强调差异、技术的发展和问题表述的微妙之处。
模型调节动物园之旅
条件甘斯
就问题表述和机制而言,条件化的最简单形式之一是条件生成对抗网络(GAN)。
在无条件 GAN 中,从某个分布(通常是多元高斯)中采样向量 Z,并训练网络将该向量映射到与真实训练集中的图像无法区分的像素图像中。GAN 的目标是学习将每个可能的样本向量映射到似乎合理的图像输出的参数。在无条件 GAN 中,您可能会学习生成猫和狗的图像,但是为了知道哪个向量对应于其中的一个,您需要选取样本并通过 GAN 向前运行这些样本,这将是一项计算成本很高的任务。
在训练期间,条件 GAN 指定作为其输入的一部分,它试图从哪个类生成,并从本质上激励您的模型学习许多每个类的分布,而不是一个单一的分布一次捕获所有图像类别。您不只是输入采样的 z 向量,而是将 z 与对应于数据分布中特定类别的一键表示连接起来。然后,每当你输入与猫相对应的热门标签时,你就训练网络生成看起来像是从猫的真实分布中提取的图像;当你给狗贴上标签,你就可以根据狗的分布来训练它,等等。
这种连接过程在概念上非常简单:这意味着网络的第一层有更多的参数,传递到更高层的向量表示应该以某种方式捕捉标签的事实,但由于向量对于附加信息来说并不更大,所以它不需要额外的参数。
这种方法的缺点是,当 Z 输入网络时,条件信息只输入一次。这样做的结果是,每一层都需要学习传递后续层需要的信息。举个简单的例子,假设你是 GAN 的第二层。您在类别“cat”下看到一个输入,并意识到您应该将可用的特征(如“脚”、“腿”、“头”等)对齐,以对应于猫的身体。但是你也需要确保传递图像是猫的事实,因为这有助于下一层知道脚应该是猫的脚,头应该是猫的头,等等。这不是模型固有的偏好要做的事情,因此它需要模型能力来学习一种机制,以确保类信息被向前传递。这一弱点引发了这样一个问题:是否有可能在网络中的稍后点重新引入条件信息,而不添加太多额外的参数。
样式转移的实例规范化(2017)
许多有趣的调节工作来自于风格转换的世界,在那里我们希望根据另一个图像的风格统计来调节一个图像的生成。实现这一点的原始方法是直接优化单个图像的像素,使它们与参考图像的激活统计相匹配。但这是非常低效的,因为我们需要对每张图片进行优化,所以我们开始训练网络,它可以接受参考图片,并将其转换为特定风格的风格转换版本,只需向前传递网络。但即使这样,我们也需要为每一种风格训练一个独立的网络。本文,关于风格转换的自适应实例规范化,是对该问题的回应,并建立了一个条件网络,可以训练该网络根据传入的任何风格标签将图像转换为多种不同的风格。
他们通过一种称为自适应实例规范化的技术实现了这一点,因为它建立在现有的批量规范化机制的基础上,并做了一点小小的调整,让它可以针对不同的样式调整不同的激活。批处理规范化有两个部分:
- 归一化(我们分别根据批均值和方差进行移位和缩放),以及
- 重正化,其中网络根据学习到的参数重新调整和重新移动标准化的特征值。
典型地,网络将学习每个特征通道的一个伽马和贝塔(如下面的等式所示),并在每个空间位置对该通道使用相同的伽马、贝塔对。

条件归一化通过学习每个(channel,style_id)组合的参数对,而不是学习每个通道的单个参数对,来修改这种基本方法。所以,如果我们试图模仿《星夜》,我们用一组参数重新调整每个特征,如果我们试图模仿《蒙娜丽莎》,我们会用另一组参数。

A figure from the Adaptive Style Transfer paper showing the differences that can be achieved by using the renormalization parameters corresponding to each style
值得花一点时间对此进行更深入的研究,并对它能够如此有效地工作这一事实留下深刻印象。这些重正化参数一次仅影响一个特征的激活值;没有参数来控制特征之间的交互效果,作为我们正在模仿的风格的函数。它们在任何方面都不是空间或上下文智能的:这是一个纯粹的线性变换,我们将以同样的方式重新调整给定风格的给定功能的激活,而不管该位置的潜在激活的值。
然而,尽管有这些限制,实例规范化被证明是足够强大的,足以在这种情况下产生令人印象深刻的结果,这种简单思想核心的变体已经被应用于各种设置中;这篇文章的其余部分将集中在绘制它的路径。
但首先,快速说明一下术语和知识谱系:MILA 2017 年的一篇论文提出了特征线性调制(FiLM)作为条件机制的总称,这些机制通过根据一些学习或计算的移动和比例因子,独立于其他特征移动和缩放每个特征来操作。

A figure from the FiLM paper, showing how channel-wise scale and shift modulation works in a convolutional network
我个人更喜欢这个术语,因为我认为它更清晰地抓住了核心观点,并且不太强调(我认为是正确的)与批处理规范化的联系。然而,后来的论文继续使用“条件规范化”框架,并建立在这个原始的适应性实例规范论文。因此,在这篇文章的其余部分,我可能会在“重正化”和“调制”之间交替使用,因为这两个词表示相同的意思:根据某组参数移动和缩放每个层中的每个特征。一个有趣的问题,也是这个方法变得有趣而普遍的一个问题是:这些参数从何而来?
高根:蒙版场景调节
上述技术的一个有趣的变体是 GauGAN 使用的,绘画生成器在这里找到了。该模型将场景类别的掩码作为输入,将图像的不同部分附加到不同的场景类型。然后,它会为每种场景类型学习一组不同的重正化参数,因此对于每种(场景类型、图层、要素)组合,都有一个唯一的平移和缩放值。

A diagram showing how GauGAN works: the user passes in 1) a map corresponding to where in the image they want different kinds of landscape features, and 2) a style image, and the network applies, for example, the conditional parameters corresponding to “tree” in places where that mask is present.
对于图像的每个部分,模型应用与该位置的遮罩中指定的场景类型相对应的重正化参数。因此,就好像我们在使用一个类条件模型,就像前面提到的那些,除了我们在图像的不同位置生成不同的类。这是一个简单的风格转换案例:学习几个离散类别中每个类别的一个参数集,并在整个图像中一次应用一个参数集,GauGAN 根据用户试图在图像的给定区域生成的内容应用其特定于风景的参数。
基于图像的问答
以上两种技术都以离散的分类信息为条件:一个固定的有限的风格集合中的一个风格,一个固定的类别集合中的一个风景类别。在这些情况下,可以只学习与每个类别相对应的唯一参数集,并提取出适用于您尝试进行条件处理的类别的参数集。
但是在某些情况下,您可能希望以一个连续的向量为条件,而不是一个热变量或分类变量,该向量可以在不同的表示形式之间平滑变化,比如一个图像、一个 Word2Vec 向量或一个单词序列:比简单的一个 K 分类值更复杂的东西。在这种情况下,我们不能简单地学习不同的每类参数,因为在连续空间中有无穷多个点;相反,我们需要一个映射函数,它可以接受我们想要调节的任何复杂数据对象,并以调节函数可以使用的方式向前传递信息。(其中“映射函数”通常指“以某种方式处理输入并输出向量的神经网络”)
基于这种直觉,在我这里提到的例子中,问题是接受一个图像和一个关于该图像内容的问题,并给出所提问题的答案。这里,图像被视为主要输入,问题被视为条件输入。作者使用了相同的条件重正化技巧的扩展,但不是为每种风格设置一组缩放和移位参数,他们训练一个网络来输出给定句子的这些参数值。更具体地说,给定某个问题,LSTM 学习映射,使得在处理图像的卷积网络中由该问题引起的尺度和偏移以使其输出 softmax 更可能输出该特定问题的答案的方式调节该网络。现在,在给定一些输入的情况下,我们在产生权重的网络中捕获信息,而不是在重新参数化权重本身中捕获关于条件的信息。
面部表情的一次性视频
除了处理复杂数据的能力之外,使用网络将一般输入映射到条件权重的另一个好处是,您可以对您在培训期间从未见过的输入进行条件化,因为网络将(希望)学习到一个可概括的映射。最近的一篇论文利用这一点来训练一个网络,该网络可以生成做出想要的任意表情的人脸帧,即使它只看到人脸的一个例子。在这个问题中,
这个模型需要结合两条信息来生成它的人工图像:
1)我们希望我们的对象制作的表情的面部标志数据
2)对象的至少一张参考照片,在图像顶部绘制地标数据

A diagram showing the data sources for one-shot expression transfer. The expression face is turned into landmarks, and a network is run on those landmarks, using conditional feature modulation parameters generated from the source image, to generate the result.
作者选择了一种生成结构,该结构将所需的地标作为输入,并逐渐进行逆卷积,以将它放大为做出表情的整张脸的图像。为了使这不仅仅是一张普通的脸,而是我们的目标的脸做出那种表情,他们在整个生成网络中执行条件重正化,由 CNN 生成的重正化参数将目标脸作为输入。当我们在测试时看到一个新面孔时,我们通过 CNN 传递它,并希望它产生的参数能够很好地调整特征值,以指导该面孔的产生。从条件反射的角度来看,我们可以将这视为一种元学习,网络正在学习如何根据它看到的输入生成自己计算的参数。
说话人条件语音识别
从一组重正化参数的初始核开始,条件归一化方法已经将标准批量范数的概念的边界首先扩展到多个参数集,然后扩展到动态生成的参数。除了产生参数的机制之外,调节方法最近也变得更奇怪和更有创造性;这方面的一个例子是自我调节的技术。在很高的层次上,自我调节是一种根据全局信息的汇总版本来调节局部计算的方法。
在第一个问题中,我们将研究自我调节的应用,目标是执行语音识别,接收音频波形并预测正在说出的单词序列。当这篇论文被写的时候,递归神经网络,或者 RNNs,是像这样的序列对序列问题的标准机制。(一岁左右;目前的技术水平似乎使用了变压器,尽管我个人没有去那个兔子洞检查过。当 rnn 在序列上前进时,它们迭代地累积状态,并使用该状态通知它们在序列中该点的计算。这允许网络结合过去的环境,但是实际上 rnn 倾向于关注最近的过去,并且难以跟踪旧的信息。
在这种大部分是本地计算的情况下,我们可以看到紧凑的全局信息是多么有用。举个例子,如果你只听到一小段文本,不知道说话者的口音,你可能很难理解他们使用的声音是如何映射到单词上的,而如果你缩小来听更多,你可以利用你对单词在英语口音中发音的了解,从波形中正确地解析它们。这或多或少正是本文所做的:它通过聚合整个波形的表示来计算汇总向量(此时它已丢失特定于单词的信息,但仍保留特定于说话人的信息),并将该向量用作网络的输入,该网络生成 RNN 运算中使用的条件重正化参数。
这已经到了我们关于简单统计意义上的条件作用的直觉开始崩溃的地步:我们不是基于一个类别,甚至任何一条外部数据,而是基于包含输入数据点本身的信息,尽管是在不同的尺度上聚合的。这就提出了一个重要而有趣的观点,这个观点将在最近的几个例子中反复出现:即使理论上没有信息也是可以访问的,特征调制也是一个很好的方法,可以在整个计算过程中重复地提供该信息的有效压缩版本。
压缩和激励网络
这种使用概括的全局信息来调节局部特征计算的策略也被发现在现代机器学习时代的典型问题:大规模图像分类中是有效的。2017 年,ImageNet 挑战赛由的“挤压和激励”架构获得冠军。S & E 的定义特征是它使用特征调制的方式来传达关于通道激活的广泛、全局行为的信息,否则在给定的卷积层的有限感受域内是不可见的。
这种机制听起来很熟悉,因为我们到目前为止已经看到了这样的例子:网络通过在图像的所有空间区域上平均给定通道的激活来计算每个通道的全局摘要。然后,将该向量传递到网络中,以生成乘法因子向量(每个通道一个),该向量控制每个空间位置的给定要素的缩放。这与学习比例和移位因子的重正化方法略有不同,但在本质细节上是相似的,因为两者都通过特征式调制来执行调节。

An diagram for Squeeze and Excitation networks, showing 1) transforming the spatial features, 2) aggregating them by feature/channel, and 3) using them to shift the values of those channels over the whole image
卷积网络的一个中心问题是:我们如何平衡我们对宽感受域的渴望和我们必须支付的高参数成本,以通过使用更大的卷积核来拓宽该域。这种方法通过为全局信息流创建一条基于条件的路径来回避这些问题,否则这些信息流将成为纯粹的局部计算。
StyleGAN
到目前为止,我们已经看到了以类、外部数据对象或相同输入数据实例的全局表示为条件的模型。虽然这些方法互不相同,但它们都有一个共同的特性,即我们用来制约网络其余部分的信息是一些具体的、现有的、可指定的东西:标签、图像等等。如果你问我们对于一个给定的实例,我们会提前知道,并且可以用语言描述。我们给我们的模型某种提示——类“马”,这种风格“梵高”,一个特定的图像来回答问题——并优化它,使其能够以对应于该提示的方式运行
甘一家采用了这种条件反射范式,并将其颠倒过来。GANs 不是获取一个类或风格向量并学习如何将其映射到特定的期望输出,而是通过获取一个随机噪声向量(它与特定输出没有先验联系)并将其映射到来自数据分布的样本。gan 定义了基于像素的图像和代表这些图像的“代码”向量之间的对应关系。简单地说,当你传入一个向量时,它对应于一个单一的,特定的图像,因为 GAN 只是一堆确定性的变换。
因此,一旦 GAN 被训练,每个向量对应于一个特定的图像,如果我们通过传入该向量来“调节”GAN,它将生成该图像。虽然这种映射在数学上确实存在,但它对于条件作用的典型用例并不特别有用:因为我们无法提前知道哪些向量将对应于哪些类型的图像,而且,即使有了训练好的模型,我们也无法知道我们需要传入什么向量来产生我们想要的输出。找出答案的唯一方法是向前运行模型并进行采样。如果我们幸运的话,附近的向量可能会产生附近的图像,但这在数学上不是必需的。
这是 GANs 的一个核心奇怪之处:因为模型最终建立了向量和输出之间的映射,我们可以说噪声向量携带了指定产生什么图像的信息,即使它一开始并不包含任何有意对该信息的编码。
StyleGAN 利用了这一想法——一个给定的噪声向量携带了指定其产生的特定图像的信息——并使用它来创建一种强大有效的自我调节形式。每次 StyleGAN 运行时,它都会对一个 z 向量进行采样,然后将该向量馈送到一个网络中,该网络会为图像生成层次结构中的每个层和通道生成按特征的调制权重。这可以被看作是在不同时期重新引入和加强全球形象规范的一种方式。

StyleGAN samples showing the images you can generate by combining conditioning parameters corresponding to multiple different images
顺便提一下,这个网络被称为 StyleGAN 的原因是,它从我们谈到的第一篇论文中获得了条件重正化的灵感,那篇关于适应性风格转移的论文。使用 StyleGAN 模型,您还可以通过使用对应于网络低层一个图像的归一化参数和对应于较高层另一个图像的归一化参数,进行有趣的转换。这就产生了将一张脸的高级风格特征传递给另一张脸的效果。
如果我们相信作者认为这是在起作用的机制,那么不断传递关于共享全局表示的信息会使生成层更容易以一致的方式运行,并在正在创建的图像的空间位置上进行协调,因为网络不必担心向前传递规范信息。与以前的工作世界相比,这一点尤其正确,在以前的工作世界中,全局规范仅在模型的输入层被馈入一次,并且每个本地代在执行其计算时只能访问本地信息。
我们能从中学到什么?

如果你能走到这一步,我既感动又感激。我意识到这篇文章在风格上不同于我以前写的其他文章:与其说是对任何一种技术的详细解释,不如说是一张展示技术之间概念联系的地图。此外,我意识到它是巨大的。那么,考虑到这些方法的机制相对简单,为什么值得我写或者你读呢?
在对象层次上,我认为基于特性的条件规范化是有趣的,因为它的工作效果如此之好令我印象深刻和惊讶。它一次只能修改单个要素的值,它(通常)在所有空间点以相同的方式修改每个要素,并且不管该要素的初始值或该点任何其他要素的初始值如何,它都以相同的量修改每个要素。与你能想象到的处理信息的所有灵活方式相比,这是一个相当迟钝的工具。由于特征调制,网络有可能具有如此一致的不同输出,这一事实更新了我对深层网络的直觉,我怀疑这种方式对更新我的直觉是有用的。
在一个更元的层面上,我发现条件规范化最有趣的方面是,它不是从深刻的理论根源开始的,而是作为或多或少的工程启发而出现的,是自适应实例规范化论文中用于风格转移的“一个奇怪的批量规范技巧”,最终在其应用中变得越来越复杂和多样。机器学习具有双重存在,既是理论假设的领域,也是实际的工程学科,当我看着这种以实践为动机的技术随着时间的推移而发展时,感觉就像看着这两方之间的对话。这在条件规范化的创新通常不是一篇论文的核心贡献,而只是架构或实现的一个次要特征的方式中最为突出。
最后,严格地说,我发现了自我规范化的想法,或者说是基于概括的全局环境来调节局部行为的技术,这是我的心理工具包中一个有价值和令人信服的想法。我认为这实际上在概念上有点类似于旧的翻译编码器/解码器模型,因为在那个领域中,很明显,例如,在不知道所翻译内容的完整上下文的情况下,你不能对翻译的句子进行局部去切分。像 StyleGAN 和挤压与激励网络这样的方法只是采用了这个想法,并将其应用到更广泛的领域。
作为对所有这些积极性的警告,在写这篇文章的过程中,我因为缺乏好的综述论文来比较不同的条件作用机制的效果以及它们的局限性而感到沮丧。在我提到的论文中,作者的主要目标是证明他们使用的条件重正化是可行的,但不清楚是否有其他更好的方法。条件规范化能在条件甘的多类调节机制中表现良好吗?我们真的需要两个重整化参数来做有效的调节吗,或者我们可以只需要一个吗?尽管我们有许多这种方法起作用的例子,但像这样的基本问题仍然没有答案。因此,尽管条件重正化已经远离了它的批次规范起源,但条件重正化的支持者也许可以从它的智力祖先那里学到一课:在缺乏对为什么和什么时候某样东西起作用的严格检验的情况下,我们的理解不如它所希望的那样坚实。
一如既往,我还是有疑问!
- 这些方法与重正化的联系有多紧密?如果您只是添加了一个具有相同结构的特征调制层,而没有之前的批处理规范操作,它们会工作得一样好吗?我怀疑批处理规范是如此普遍地有用,以至于移除它没有意义,但是考虑到这整个条件作用学派的名字来源于它的规范化起源,知道这种联系是必要的还是偶然的是有价值的
- 延伸上述问题,是否有功能调制不适用的应用?例如,用这种方法来处理完全类条件的 GANs 会不会太简单了?(我发现的大多数有条件的 GAN 示例仍然采用 concatenate-class-ID-to-input-vector 方法)
参考
- 特征线性调制:【https://arxiv.org/abs/1709.07871
- 具有自适应实例规范化的条件样式转移:【https://arxiv.org/pdf/1703.06868.pdf
- 一种基于风格的生成对抗网络生成器架构:https://arxiv.org/abs/1812.04948
- 现实神经说话头模型的少镜头对抗学习:https://arxiv.org/abs/1905.08233
- 利用空间自适应归一化的合成图像合成(GauGAN):https://arxiv.org/abs/1903.07291
- 语音识别中自适应神经声学建模的动态层归一化:https://arxiv.org/abs/1707.06065
- 压缩和激励网络:【https://arxiv.org/abs/1709.01507
数据科学中的条件概率及实例
顾名思义,条件概率就是某一事件在某种给定条件下发生的概率。基于这个条件,我们的样本空间减少到条件元素。
例如,假设一个人已经申请了房屋贷款,求他投保的概率。这里的样本空间仅限于那些已经取得住房贷款的人。
为了理解条件概率,建议理解概率基础知识,如互斥和独立事件、联合、联合和边际概率以及概率与统计等。如果你想修改这些概念,你可以参考这里的数据科学的概率基础。
维基定义:
在概率论中,条件概率是在给定一个事件已经发生的情况下,另一个事件发生的概率的度量。如果感兴趣的事件是 A,已知或假设事件 B 已经发生,“给定 B 的条件概率”,或“条件 B 下 A 的概率”,通常写成 P(A | B),有时也写成 PB(A)或 P(A/B)——维基百科
现在的问题可能是为什么要使用条件概率,它在数据科学中有什么意义?
让我们举一个现实生活中的例子。在某一天卖出一台电视的概率可能只有 30%。但如果我们考虑到给定的日子是排灯节,那么就有更多的机会卖出一台电视。在排灯节当天卖出一台电视的条件概率可能是 70%。我们可以将这些概率表示为 P(随机一天的电视销售量)= 30%。p(鉴于今天是排灯节,电视销售额)= 70%。
因此,条件概率有助于数据科学家从给定的数据集获得更好的结果,对于机器学习工程师来说,它有助于建立更准确的预测模型。
让我们更深入地探讨一下:
下表包含了不同年龄组的拖欠和未拖欠贷款的人。

将上表转换成概率

因此,如果我们能够将问题中给出的数据转换成表格形式,那么样本空间将减少到一整列或一整行,样本空间的其余部分将变得无关紧要。
假设一个人已届中年,他/她不会拖欠贷款的概率是多少? P(无|中年)= 0.586/0.690 = 0.85【参考表— 2,概率形式数据】 P(无|中年)= 27368/32219 = 0.85【参考表—1,正常编号数据】
如果你注意到,很明显在分子中是联合概率,是一个人没有拖欠贷款的概率,而且这个人是中年人。
而在分母中,是边际概率,也就是一个人中年的概率。
因此,我们也可以将条件概率定义为联合概率与边际概率之比。
P(A|B) = P(A 和 B)/P(B)
让我们通过改变下面的顺序来稍微不同地问这个问题。
假设一个人没有拖欠贷款,那么他/她是中年人的概率是多少?
现在看,样本空间已经改变到彩色行,即没有拖欠贷款的人。

P(中年|否)= 0.586/0.816 = 0.72(订单事项)
现在你又注意到了什么,概率是通过改变事件的顺序而改变的。
因此在条件概率中顺序很重要。
使用概率树的条件概率可视化

解释:
我试图解释树本身中的每个分支逻辑。现在让我们深入解释概率树在计算条件概率中的重要性的问题。
p(年轻没有)?
- 使用标准条件概率公式:
- P(Young | No)= P(Young and No)/P(No)这意味着:
- P(年轻且无)= P(年轻|无)* P(无)
- 通过概率树,我们知道 P(Young | No) = 0.275,P(No) = 0.816 的概率。
- 现在看右边,所有的概率值都是已知的,因此把它们放到上面的等式中,我们就会得到期望的概率。
- p(年轻和没有)= 0.275 * 0.816 = 0.2244 = ~0.225
p(没有且年轻)?(顺序已更改)
- P(无且年轻)= P(年轻且无)= 0.225[同上]
- 在联合概率中,顺序并不重要
p(年轻)?
查看与 Young(以 Young 结尾)相关的所有分支,并对分支内的概率值进行乘积求和,这意味着
p(Young)= 0.816 * 0.275+0.184 * 0.419 = 0.301496 = ~ 0.302
p(没有)?
P(No) = 0.816(直接来自树)
p(年轻|没有)?
p(年轻|无)= p(年轻|不是贷款违约者)= 0.275[见树]
p(不|年轻)?[订单已更改]
- P(No | Young) = P(Young 和 No)/P(Young)[我们已经在上面的计算中计算了右侧概率]
- p(否|年轻)= 0.225/0.302 = 0.745
现在我们来探究一下标准的条件概率公式。
根据条件概率,我们知道
- P(A|B) = P(A 和 B)/P(B)
- P(A 和 B)= P(B)* P(A | B)———[ 1]
类似地
- P(B|A) = P(B 和 A)/P(A) = P(A 和 B)/P(A)[I in 联合概率顺序无关紧要]
- P(A 和 B)= P(A)* P(B | A)--[2]
根据等式[1]和[2],
- P(B) * P(A|B) = P(A) * P(B|A)
- P(A | B)= P(A) P(B | A)/P(B)【又称贝叶斯定理*
现在如果我们想找到 P(不| Young)。那么我们可以直接使用上面推导出的公式。因为 P(Young | No)以及 P(Young)值将从概率树中获得,并且输入上述公式将给出结果。在下一篇文章中,我们会学到更多关于贝叶斯定理的知识。

说明
我试图用上面显示的概率树来解释给定的问题。然而,如果有任何不清楚的地方,我会写下给出的内容和询问的内容。以及如何计算被询问的内容。
P(A 成为 CEO) = 0.2,P(B 成为 CEO) = 0.3,P(C 成为 CEO) = 0.4。
从问题中,我们需要正确推导出,以后的概率都是条件概率。这是这里的诀窍。因为一旦他们成为首席执行官,他们只会做出有益的决定。因此,我们应该阅读这些概率如下
- p(采取有益的决策| A 被选为 CEO) = 0.5
- p(采取有益的决策| B 被选为 CEO) = 0.45
- p(采取有益的决策| C 被选为 CEO) = 0.6
所以现在 P(有有利决策)不过是总概率。因此将计算关联的每个分支概率值的乘积之和。(有利决策的分支,见树)。因此:
P(有受益决策)= 0.2 * 0.05+0.3 * 0.45+0.4 * 0.6 = 0.475

说明
- p(帐户 1 中的电子邮件)= 0.70
- p(帐户 2 中的电子邮件)= 0.20
- p(帐户 3 中的电子邮件)= 0.10
- P(电子邮件是垃圾邮件|发送到帐户 1) = P(垃圾邮件|帐户 1) = 0.01
- P(电子邮件是垃圾邮件|发送到帐户 2) = P(垃圾邮件|帐户 2) = 0.02
- P(邮件是垃圾邮件|发送到帐户 3) = P(垃圾邮件|帐户 3) = 0.05
利用贝叶斯定理,
P(帐户 2 |电子邮件是垃圾邮件)= P(帐户 2) * P(垃圾邮件|帐户 2)/P(垃圾邮件)
分子值是已知的,分母 P(垃圾邮件)是已知的 ans 总概率,通过取每个分支概率值的乘积之和来计算[以垃圾邮件结尾的分支见树]。
p(垃圾邮件)= 0.70 * 0.01+0.20 * 0.02+0.10 * 0.05
p(账户 2)= 0.20[给出问题]
p(Spam | Account 2)= 0.02[给出问题]
把所有东西放在一起,P(账号 2 |邮件是垃圾邮件)= 0.20 * 0.02/(0.70 * 0.01+0.20 * 0.02+. 10 * 0.05)
答案:P(账号 2 |邮件是垃圾邮件)= 0.25
这就是数据科学的条件概率。在接下来的帖子中,我将详细地写贝叶斯定理和概率分布,这将完成数据科学的概率系列。
我希望你喜欢这篇文章。对于任何评论,请使用下面的评论部分给我回信,或者你也可以使用https://ashutoshtripathi.com/contact/给我写信。我会尽量回答你所有关于 DS,ML,AI 的疑问。
下一篇文章:贝叶斯定理在数据科学和机器学习中的重要性
原载于 2019 年 8 月 15 日【http://ashutoshtripathi.com。
条件随机场解释
条件随机场是一类最适合预测任务的判别模型,其中上下文信息或邻居状态影响当前预测。仅举几个例子,CRF 在命名实体识别、词性标注、基因预测、降噪和对象检测问题中找到它们的应用。
在本文中,我将首先介绍与马尔可夫随机场相关的基本数学和术语,马尔可夫随机场是 CRF 所基于的一种抽象。然后,我将详细介绍和解释一个简单的条件随机场模型,它将说明为什么它们非常适合于序列预测问题。之后,我将在 CRF 模型的背景下回顾似然最大化问题和相关的推导。最后,我将通过在手写识别任务中进行训练和推理来演示 CRF 模型。
马尔可夫随机场
马尔可夫随机场或马尔可夫网络是一类在随机变量之间有无向图的图形模型。这个图的结构决定了随机变量之间的相关性或独立性。

MRF with four random variables
- 马尔可夫网络由图 G = (V,E)表示,图中的顶点或节点表示随机变量,而边共同表示这些变量之间的依赖关系。
- 该图可以分解成 J 个不同的集团或因子,每个集团或因子由因子函数ϕⱼ控制,其范围是随机变量 Dⱼ.的子集ϕⱼ(dⱼ)应该严格肯定 dⱼ.的所有可能的价值观
- 对于要表示为一个因素或集团的随机变量子集,所有这些变量在图中应该相互连接。此外,所有集团的范围的联合应该等于图中存在的所有节点。
- 变量的非标准化联合概率是所有因子函数的乘积,即,对于上面显示的 V= (A,B,C,D)的 MRF,联合概率可以写成:-

Joint probability as normalized product of factors
分母是随机变量可能取的所有可能值的乘积之和。它是一个常数,也称为配分函数,通常用 z 表示。
吉布斯符号
我们还可以通过对对数空间中的因子函数进行操作,将关节表示为吉布斯分布。使用β(dⱼ)= log(ϕ(dⱼ),我们可以用 Gibbs 符号来表示关节,如下所示。请注意,X 是图中所有随机变量的集合。β函数也称为因子势。

Joint probability as Gibbs Distribution
在本文后面,我们将使用 Gibbs 符号来推导似然最大化问题。
条件随机场模型
暂且让我们假设一个马尔可夫随机场,把它分别分成两组随机变量 Y 和 X。
条件随机场是马尔可夫随机场的一个特例,其中图满足以下性质:“当我们对 x 上的图进行全局条件化时,即当 x 中的随机变量的值固定或给定时,集合 y 中的所有随机变量都遵循马尔可夫性质 p(Yᵤ/X,Yᵥ,u≠v) = p(Yᵤ/X,Yₓ,Yᵤ~Yₓ),其中 Yᵤ~Yₓ表示 Yᵤ和 Yₓ在图中是邻居。”一个变量的相邻节点或变量也被称为该变量的马尔可夫链。
满足上述特性的一个这样的图是下面共享的链结构图:-

Conditional Random Field structure
由于 CRF 是一个判别模型,即它模拟条件概率 P(Y/X ),即 X 总是给定或观察到的。因此,该图最终简化为一个简单的链。

CRF model, conditioned on X
当我们以 x 为条件,并试图为每个 Xᵢ找到相应的 Yᵢ时,x 和 y 也分别称为证据和标签变量。
我们可以验证上面显示的“因子减少”的通用报告格式模型遵循如下所示的变量 Y₂的马尔可夫性质。如图所示,在给定所有其他变量的情况下,Y₂的条件概率最终仅取决于其相邻节点。

Variable Y₂ satisfying the Markov property, conditional depends only on neighboring variables
CRF 理论与似然优化
让我们首先定义参数,然后使用 Gibbs 符号建立联合(和条件)概率的方程。
- 标号定义域:假设集合 y 中的随机变量有一个定义域:{m ϵ ℕ | 1≤m ≤M}即前 m 个自然数。
- 证据结构和领域:假设集合 x 中的随机变量是大小为 f 的实值向量,即∀ Xᵢ ϵ X,Xᵢ ϵ Rˢ.
- 设 CRF 链的长度为 L 即 L 个标签和 L 个证据变量。
- 设βᵢ(Yᵢ,Yⱼ) = Wcc '如果 Yᵢ = c,Yⱼ = c '而 j = i+1,0 否则。
- 让β'ᵢ(Yᵢ,Xᵢ) = W'c。Xᵢ,如果 Yᵢ = c,否则为 0,其中。代表点积,即ϵ Rˢ.
- 注意,参数的总数是 M×M+M×S,即每个标签转换有一个参数(M×M 个可能的标签转换),每个标签有 S 个参数(M 个可能的标签),这些参数将被乘以该标签处的观察变量(大小为 S 的向量)。
- 设 D = {(xn,yn)}对于 n=1 到 N,是由 N 个例子组成的训练数据。
记住上述内容,能量和可能性可以表示如下

Likelihood expression for CRF model
因此,训练问题简化为最大化所有模型参数 Wcc’和 W’cs 的对数似然性。
关于 W'cs 的对数似然性梯度已推导如下:-

Derivative of likelihood wrt label-observation weights
注意,上面等式中的第二项表示 y'ᵢ的边际概率等于 c 的和(在 y’可以取的所有可能值上),由 xnis 加权。这里的 y'-i 表示除了位置 I 的以外的每个位置的标签/y 变量的集合
我在下面直接分享的 dL/dWcc '也有类似的推导

Derivative of likelihood wrt label-label weights
既然我们有了导数的表达式和对数似然,我们实际上就有了从头开始编写 CRF 模型所需的所有内容。我们可以使用上面提到的等式对可能性进行编码,使用置信传播来计算边际并计算出导数,然后使用现成的优化算法(如 L-BFGS)来优化可能性。
然而,为了简单明了,我们不会重新发明轮子,而是使用现有的 CRFSuite 库进行演示。
演示—手写识别
到目前为止,应该相当清楚为什么以及如何 CRF 的结构使它们成为捕获顺序关系的任务的理想选择,如词性标注句子、命名实体识别等。对于这个演示,我将使用 CRF 进行手写检测任务。
为了准备这个演示的数据集,我使用了斯坦福 OCR 数据集和古腾堡项目档案的组合。
数据集准备
斯坦福 OCR 数据集总共包含 6877 个手写单词,分为 9 个不同的文件夹。每个单词的第一个字符缺失,以使数据集中的所有字符都是小写的。对于每个单词中的每个手写字符,数据集包含长度为 128 的扁平二进制数组,该数组可以转换为大小为 16x8 的图像。数据集中的一些单词可视化分享如下

Word images obtained after stacking 16x8 character images side by side
经过分析,我发现整个数据集中唯一单词的数量只有 24 个。
我们的希望是,CRF 模型将学习标记 observations(xᵢ,在我们的情况下,它们是共同出现的字符像素向量。尽管就字符像素向量而言,数据集中有 6,877 个唯一的例子,但是全部背后有 24 个唯一的单词太少,不足以从概率上捕捉一般英语文献中的字符共现,并准备好良好的手写/单词识别器。
为了解决这个问题,我决定自己创建一个数据集,使用所有的字符向量表示。我为地图/字典中的每个字符捕获了数据集中可用的所有不同字符像素向量变量。完成后,我导入了名著莫比迪克中出现的所有单词,过滤掉了所有长度小于 3 或包含字母表以外的任何内容的标记,并将过滤后的标记转换为小写。通过这种方式总共提取了 18,859 个单词,然后按照单词长度分层,将这些单词按 80-20 的比例分别分成训练集和测试集。
为了组成 CRF 模型的实际训练和测试集,我使用了我在开始时创建的字符到像素阵列的矢量图。为了为任何单词创建 image/x,我依次检查了它的字符,并使用统一采样从字典中为该字符选择了一个像素数组向量变量。下面分享了由此创建的数据集的几个样本

Sample words from Moby Dick
还为任何想玩的人分享数据准备脚本:-
Dataset preparation script
准备好训练和测试数据集后,就该训练模型并在任务中对其进行评估了。
模型训练和评估
CRF training and model evaluation script
使用上面的脚本,我在由 15088 个单词组成的训练集上训练了一个 CRF 模型,并在测试集上实现了近 85%的准确率。
对于一个简单的实验来说还不错..让我知道你的想法!
进行人口比例估计研究(二元变量):如何进行的想法。

Image by JanBaby on Pixabay
简介
假设我生活在撒哈拉以南非洲一个叫 X 的国家。这项研究是基于一个真实的国家,但由于某些原因,我更愿意称它为 X。X 的第三大死因被认为是车祸。我认为自己是一个谨慎负责的司机。然而,鲁莽驾驶似乎无孔不入。我最害怕的,也是经常听说的那种事故是迎面相撞。为了看看我能在多大程度上证明我的担心是有道理的,我决定进行一项研究,评估全岛发生这种事故的可能性。很明显,比例根据你所在国家的哪个地区而有所不同。因此,除了估计整个国家的比例之外,估计各州的比例也是一个好主意。这项研究有三个主要部分:
- 规划
- 数据收集和筛选
- 数据分析(比例估计)
头脑风暴和策划
那么我该怎么做呢?目标人群是哪个?我如何对代表所有事故的事故进行抽样?它是一个什么样的变量,如何测量?我从哪里得到数据?我用哪个时间段?为了精确估计,我需要的最少事故次数是多少?我的目标是多精确,在什么样的置信水平下?我希望这项研究有多大的权力?这些问题的答案决定了我的研究结果。
在我进行自己的研究之前,我搜索了是否有任何以前做过的研究。可惜好像没有。在调查了可能的数据来源并假设他们有所有发生过的事故的文章后,我意识到废弃不同的本地新闻网站是最可行的选择。
检查每个独立的事故是否是正面碰撞构成了伯努利随机变量——这是一个是或否的问题。对每个观察值求和的结果是一个二项式分布。通常,有两种估计总体参数的方法。参数可以被估计为单个值(点估计)和/或区间(置信区间估计)。点估计只是样本的平均值。然而,置信区间提供了真实总体可能在其中的值的范围。它考虑了参数的不确定性。此外,它也是你期望你的价值观如何变化的指南。(Udacity,A/B 测试课程:A/B 测试概述,视频 14 和 15)。例如,假设您选取了一个真正有代表性的样本来计算置信区间,如果您要用 200 个不同的样本来重复这项研究,那么您希望 190 个样本的平均值(在本例中为比例)落在区间内,其余 10 个落在区间外。如果“低于置信水平%样本”意味着落在区间内,那么您的研究中一定出现了问题,您可能需要进行调查。同样,您应该预计围绕这些平均值构建的区间会与真实总体参数(比例)重叠,而围绕其他 10 个样本的平均值构建的区间不会重叠。
为了估计参数的区间,研究人员根据是否满足某些条件,使用参数或非参数统计程序。参数程序是正态密度函数。由于中心极限定理(CLT),我们从二项分布(或任何其他分布)转移到正态分布。
正如你可能已经知道的,如果真的很大,分析整个人口几乎总是不切实际的。因此,目标人群的随机样本用于对人群进行推断。CLT 指出,如果你有很多样本,比如 1000 个足够大(超过 30 个)的样本,并计算它们的平均值,这些均值的分布(样本均值的抽样分布)接近正态分布,均值=真实总体均值,样本比例的标准差 ( SD)=sqrt(P(1-P)/N)* 。样本比例的分布称为样本比例的抽样分布(SDSP)。SD 公式仅适用于一个样本比例。样本均值的抽样分布有不同的 SD 公式。
为了使用正态分布来确定比例的置信区间,两个条件必须为真才能得到合理的结论:
- z > 5 和(N-z)>5
其中:
z =成功的次数(正面碰撞)N-z=失败的次数(非正面碰撞)
- 如果这两个条件不满足,这意味着 SDSP 偏向较大的值(成功或失败)并且不是正态分布的。满足这些条件表明样本可能是随机的,并且能很好地代表目标人群。
样本量
数据日期范围从 X 成为独立国家 5 年后的 2000 年到 2019 年。我可以使用所有这些数据来获得更多信息,但为了节省时间,我决定使用过去五年(2014 年至 2018 年)的数据。
考虑到上述信息,我需要计算最小样本量,以节省我的成本,并在大多数情况下(通常为 80%或更多)使误差等于或小于最大允许值。一般来说,一项统计研究达到其目标的概率就是所谓的研究的统计功效。(克鲁施克,第二版。,第 359 页)。具体来说,这项研究旨在估计 X 中迎面碰撞的比例,最大可接受误差低于或高于真实比例 0.05 点。我随意选了 0.05 分误差。
有多种方法来计算样本量,其中之一是在线计算器。在本文中,我将使用 AUSVET 计算器。通常,样本量计算需要以下输入:
- 估计真实比例 —这通常来自以前的研究、专家或试点研究。正如在引言中提到的,以前没有关于这个问题的研究,从某个地方开始,我废弃了 140 个事故报告(每个州 10 个)进行比例估计。设 z = 50,N = 140。那么 p-hat= 0.36。其中 z 和 p-hat 是迎面碰撞的次数和样本中迎面碰撞的比例。
- 允许误差:这是需要达到的最大允许误差。它决定了置信区间的宽度。
- Alpha :既然我会用 95%的置信度,那么对应的 Alpha 水平=5%。
- 预计目标人群规模:其中一家报纸报道全国每年发生约 12500 起事故。因此,本研究的目标人群为 12500 * 5(过去 5 年)。
提交所有要求的输入,计算的最小样本为 353 。
既然我已经确定了从哪里获取数据;哪些数据构成了我的总体和抽样框架;以及确保 80%的时间有 5%的误差幅度和 95%的置信水平的适当样本量,下一步是收集数据。
2。数据收集和筛选
我通过网络搜集资料。在开始计算任何东西之前,您要确保收集的数据是计划中的数据。
发现并必须解决的问题:
- 属于一些国家的文章的日期早于截止年(2014 年)。这些文章必须被删除,以确保所有国家的文章都来自同一年。
- 关于汽车或一般事故的文章,而不是关于发生的具体事故的文章被删除了。
- 为了能够估计出州一级以及全国的比例,我确保每个州至少有 5 次正面碰撞和非正面碰撞。
3。计算置信区间
一旦清理了正确的数据,我就计算出了比例。
结论
嗯,我就是这样进行研究的。最有经验的数据专业人员我暴露自己告诉一件事:数据收集和清理占用了大部分时间,我的研究也不例外。在所有这三个步骤中,我注意到计算期望的估计值占用的时间最少。最初,我开始收集数据,但没有定义我上面提到的一些要点。因此,我碰了很多壁。在阅读和观看内容后,我明白了花时间明确决定你计划测量什么和如何测量的重要性。所以请抓紧时间为自己的学习做好规划。它为你以最有效的方式进行可靠的研究铺平了道路。希望这篇文章对你有所帮助。
参考文献:
- 维埃拉(2017 年)。真实世界统计简介:带分步 SPSS 指令。
- Kruschke,J.K .(第二版。).做贝叶斯数据分析:与 R、JAGS 和斯坦的教程
- Udacity,A/B 测试课程,第 1 课:A/B 测试概述
k 均值的置信度

所以你用 k-means 做了一些聚类;您已经缩放了数据,应用了 PCA,使用肘和剪影方法检查了聚类,并且您非常确信您的模型为您提供了您所能获得的绝对最佳的聚类。但是你能对单个数据点的分类有多信任呢?这就是我要在这里讨论的。
大多数用于评估聚类算法的指标都是对模型的全局成功的度量。但是,如果您不希望对每个数据点进行分类,而是希望识别明确适合特定聚类的特定数据点,该怎么办呢?例如,你可能不想把一个病人归为某一特定疾病的低风险人群,仅仅因为这是最坏的一类;你需要确信它们绝对是低风险的。同样,你可能不希望为没有表现出你的用户细分所定义的强烈行为特征的客户提供个性化的数字体验。
有一个非常简单的方法可以计算出你的算法对每个分类的置信度。然后,您可以过滤掉模型不确定的预测,并根据剩余的、有把握的结果做出决策。
软 k 均值算法概述
软 k-均值算法与硬 k-均值算法类似,它基于数据点和聚类中心之间的欧几里德距离。与将每个数据点仅分配给一个聚类的普通 k-means 不同,软 k-means 计算一个权重,该权重描述每个数据点属于每个聚类的可能性。较高的值表示强的或有把握的分类,较低的值表示弱的或不太可能的分类。
尽管确实存在可以执行软 k 均值的预写算法,但是它们没有得到很好的支持。还有很多原因可以解释为什么你不想改变你的实际模型。幸运的是,您可以使用硬 k 均值聚类中心追溯计算软 k 均值权重。
虹膜数据集上的 k-均值
让我们在具有 3 个聚类的 iris 数据集上训练一个非常直接的 k-means 算法。绘制花瓣长度与花瓣宽度的关系图,我们可以看到该算法工作得很好,但是在绿色和蓝色聚类相遇的地方有一些不正确的分类(右图)。在这种情况下,我们可以很容易地使用视觉检查发现这一点,因为我们知道每个虹膜是什么物种的基本事实(左图)。理想情况下,我们希望知道,在没有真实类别的先验知识的情况下,该区域中的点很难分类,因此不太可信。

软 k 均值权重
设xᵢ为数据点。数据点属于聚类 j 的可能性可以使用下面给出的权重等式来计算:

The wij are used in the soft k-means algorithm to assign a probability of a point belonging to a cluster
这里的 c ⱼ是第 j 星团中心的坐标。 m 的值是控制算法模糊性的参数,典型的默认值为 2。对于每个数据点,权重的总和是 1,这就是为什么它们作为可能性或概率工作得很好。这里有一个很好的 python 函数来为您计算权重:
权重过滤
一旦我们计算了每个聚类的软 k 均值权重,我们就可以确定与硬 k 均值算法分配的聚类相关联的权重。然后我们可以过滤掉那些低值,只留下我们有把握的数据点。下面我为我的 iris 数据集聚类做了这个,用一个过滤器去除权重低于 0.9 的点。

过滤器已经移除了蓝色和绿色聚类之间的不确定区域中的许多数据点。已经确定,16%的数据点太难分类,无法过滤掉。
结论
只需几行代码,我们就可以计算出每个数据点的置信度得分,表明我们的 k-means 算法在分类中的置信度,而无需事先了解真实类别。
密码
soft_cluster_weights 函数和附带的笔记本在这个 GitHub repo 中提供。
为数据科学家简单解释置信区间

PIxabay: Cast a net
没有沉重的术语
最近,有人问我如何用简单的术语向外行解释置信区间。我发现很难做到这一点。
即使对了解置信区间的人来说,解释置信区间也是一件令人头疼的事情,更不用说对不懂统计学的人来说了。
我去维基百科找了些东西,下面是它的定义:
在统计中,置信区间 ( CI )是从观测数据的统计中计算出来的一种估计。这为未知的参数提出了一系列似是而非的值。该区间具有相关的置信水平,即真实参数在建议的范围内。这可以更清楚地表述为:置信水平代表未知参数位于给定区间的概率。调查者可以选择置信度。一般来说,未知参数的置信区间基于对相应的估计器的分布进行采样。【1】
我的第一个想法是,他们可能是这样写的,所以没有人能理解它。这里的问题在于统计学家喜欢使用大量的术语和语言。
这篇文章是关于用一种简单易懂的方式解释置信区间,而不是故作姿态 。
现实生活中的问题

让我们从创建一个真实的场景开始。
假设你想找出美国某个州所有人的平均身高。
你可以去那个特定州的每个人那里询问他们的身高,或者你可以做更聪明的事情,在这个州抽取 1000 人作为样本。
然后,您可以使用他们身高的平均值(估计平均值)来估计该州身高的平均值(真实平均值)
这一切都很好,但作为真正的数据科学家,你并不满意。估计的平均值只是一个单一的数字,您希望有一个真实平均值所在的范围。
我们为什么要一个范围?因为在现实生活中,我们关心的是估计值的置信度。
通常情况下,即使我让你猜测美国某个州的人的身高,你也更倾向于说类似于“我相信它在 6 英尺到 6 英尺 2 英寸之间”,而不是像“它的 6 英尺 2.2345 英寸”这样的点估计。
我们人类在进行评估时也喜欢附加一定程度的信心。你有没有说过——“我有 90%的自信”。
在这个特定的例子中,我对陈述- “我相信它在 5 英尺到 7 英尺之间”比“我相信它在 6 英尺到 6 英尺 2 英寸之间”更有信心,因为第一个范围是第二个范围的超集。
那么我们如何得到这个范围并量化一个置信度值呢?
战略
为了理解我们将如何计算置信区间,我们需要理解中心极限定理。
中心极限定理:中心极限定理(CLT) 简单地说,如果你有一个具有均值μ和标准差σ的总体,并从总体中随机抽取样本,那么样本均值的分布将近似正态分布 以均值为总体均值 和估计的 标准差 s/√
所以知道了这一切,你就变得好奇了。我们在美国已经有了 1000 人的样本。我们能应用 CLT 吗?
我们知道,抽样分布的均值等于总体均值(我们不知道但想估计),抽样分布的样本偏差由 σ/√n 给出(即样本的标准差除以样本中的观察次数)

Casting a net around the sample mean to capture the true population mean
现在,您想在 X 轴上找到包含真实总体平均值的区间。
那我们怎么办?我们从已知的价值中撒网。
为了得到这样的范围/区间,我们从 Xbar 开始走 1.96 个标准差,样本在两个方向上的均值。而这个范围就是 95%的置信区间。
现在,当我说我估计真实均值为 Xbar(样本均值),置信区间为[Xbar-1.96SD,Xbar+1.96SD]时,我是说:
这是一个使用特定程序构建的音程。如果在大量样本上重复这一过程,包含真实总体参数的计算置信区间(每个样本不同)的分数将趋向于 95%
当你取 99%的置信区间时,你实际上增加了这个比例,从而用三个标准差撒出了一个更宽的网。

The simple formula
- 这里 Xbar 是样本平均值(你取的 1000 个高度样本的平均值)。
- z 是偏离样本均值的标准差的个数(1.96 代表 95%,2.576 代表 99%)——你想要的置信度 。
- s 是样本中的标准偏差。
- n 是样本的大小。

Most of the nets we cast in different experiments do contain the true population mean
上图中的每条线都是这样一个实验,点代表样本平均值,线代表范围。图中的虚线是真实的人口平均值。
看这些区间怎么有些不包含真实总体均值,几乎全部(95%)都包含真实总体均值 。
临界 Z 值
我们说过,Z 是偏离样本均值的标准差的个数(1.96 代表 95%,2.576 代表 99%)——你想要的置信度 。
你可以选择任意的自信程度。比方说,你想要 90%的信心。你可以通过使用法线曲线内部的阴影区域需要为 0.90 的想法来得到它。

Source: The Normal curve showing a 95% CI.
import scipy.stats as st
p = 0.9 + (1-0.9)/2
Z = st.norm.ppf(p, loc=0, scale=1)
print(Z)
----------------------------------------------------------
1.6448536269514722
继续学习
如果你想了解更多关于假设检验、置信区间以及数字和分类数据的统计推断方法,Mine etinkaya-Rundel 在 coursera 上教授了 推断统计学 课程,没有比这更简单的了。她是一个伟大的导师,很好地解释了统计推断的基础。
谢谢你的阅读。将来我也会写更多初学者友好的帖子。关注我的 媒体 或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter@ mlwhiz联系
此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
排列重要性的置信区间
一种旧的特征重要性度量方法的新理论视角

Feature importance helps us find the features that matter.
介绍
在本帖中,我们解释了流行的排列特征重要性技术的新理论视角如何让我们用置信区间量化其不确定性,并避免其使用中的潜在陷阱。
首先,让我们首先激发使用这种技术的“为什么”。假设您刚刚被一家大型国际零售商的数据科学团队聘用。在你到来之前,这个团队建立了一个复杂的模型来预测你在全球几十个地方的每周销售额。该模型考虑了多种因素:地理数据(如当地人口密度和人口统计数据)、季节性数据、天气预报数据、单个商店的信息(如总面积),甚至是你公司的推文最近获得的赞数。让我们也假设这种模式非常有效,可以让业务团队提前几周洞察未来的销售模式。只有一个问题。你能猜出它是什么吗?
没有人知道为什么销售预测模型如此有效。
为什么这是一个问题?很多原因。依赖该模型预测的商界人士不知道,如果 Twitter 在一周内经历了一次宕机,Twitter 赞数下降,他们的预测会有多可靠。在数据科学团队中,您几乎不知道哪些因素对模型最有用,因此在识别新信号以支持模型性能时,您会盲目行事。我们不要忘记其他利益相关者。如果基于这个模型的预测做出的决定会给公司带来糟糕的结果,董事会将希望了解更多关于这个模型的信息,而不是“它只是工作而已”,特别是随着人工智能继续变得越来越受监管。
那么我们能做什么呢?一个很好的第一步是获得一些特征重要性的度量。这意味着给你的模型使用的每个因素分配一个重要的数字分数。这些数值分数代表了这些特征对于模型做出高质量预测的能力有多重要。
许多建模技术都带有内置的特征重要性度量。也许您可以使用 xgboost 模型默认提供的基于信息增益的重要性度量?没那么快!正如你的队友将会指出的,不能保证这些特征重要性将描述你的复杂系综,此外,基于增益的重要性度量是有偏差的[1]。
那么我们能做些什么呢?我们可以使用“随机消融”(又名“置换”)特征重要性度量。Christoph Molnar 在他的可解释的 ML 书 [2]中提供了这种技术的清晰而简明的描述:
这个概念非常简单:我们通过计算置换特征后模型预测误差的增加来衡量特征的重要性。如果打乱某个特征的值会增加模型误差,则该特征是“重要的”,因为在这种情况下,模型依赖于该特征进行预测。如果对某个特征的值进行调整后模型误差保持不变,则该特征“不重要”,因为在这种情况下,模型会忽略该特征进行预测。
背景
这种技术从何而来?随机消融特征的重要性当然不是新的。事实上,它的起源至少可以追溯到 2001 年,当时这种技术的一个变种被作为变量的“噪声”引入,以更好地理解随机森林模型如何使用它们[3]。然而,最近,这种技术在使用和变化方面又有所复苏。例如,这种技术的实现将包含在流行的 Scikit-learn 库的即将到来的 0.22 版本中[4]。作为一个更加理论化的例子,考虑最近引入的“模型类别依赖”框架,该框架将随机消融特征重要性的一个变体称为“模型依赖”,并将其用作核心构建模块[5]。
新的理论视角
当在 Fiddler Labs 使用这种技术时,我们试图从理论上明确置换一列特征意味着什么,在你的模型中运行,看看模型的误差增加了多少。这让我们使用了理论透镜随机消融,因此我们为通常被称为排列的特征重要性取了一个新名字。
此外,我们的重新表述提供了两种围绕随机化消融特征重要性分数构建置信区间的方法,这是一种从业者可以用来避免随机化消融特征重要性应用中潜在陷阱的技术。据我们所知,这种技术的当前公式和实现不包括这些置信度度量。
特征重要性的置信区间
考虑如果我们使用不同的随机消融(例如,通过使用不同的随机种子)重新运行随机消融特征重要性,或者如果我们在一个非常大的数据集的两个不同的随机子集上运行它(例如,为了避免使用超过我们机器的存储器容量的完整数据集),会发生什么。我们的功能重要性可能会改变!理想情况下,我们希望使用大型数据集并对许多消融进行平均,以减轻算法中固有的随机性,但在实践中,我们可能没有足够的数据或计算能力来这样做。
随机化消融特征重要性分数有两个不确定性来源:我们使用的数据点和我们使用的随机消融值(即排列)。通过多次运行该算法并检查每次运行的差异,我们可以构建一个置信区间 (CI ),用于测量所用消融术产生的不确定性。类似地,通过逐点查看消融造成的损失增加(而不仅仅是在我们的数据集上平均损失),我们可以构建一个 CI 来衡量有限数据集产生的不确定性。
例如:预测房价
为了演示随机消融特征重要性值在 CIs 中的使用,让我们将该技术应用于真实模型。为此,我使用 Ames 住房数据集[7]建立了一个复杂的模型来估计房屋的销售价格。这个例子的完整代码可以在 Jupyter 笔记本这里获得。
为了显示置信区间的重要性,我们仅使用 100 个点运行了随机消融特征重要性,仅重复 K=3 次。这为我们提供了以下 10 大得分特征,黑色误差线表示 95%的置信区间:

Randomized ablation feature importance for 100 points after 3 repetitions.
正如我们从误差线中看到的,在这 100 个点中,不确定哪个特性实际上是第三重要的。重新运行 K=30 次迭代的随机化消融特征重要性,我们得到了更严格的误差界限,并且我们自信地发现,在对我们的模型的重要性方面,一所房子的邻居实际上超过了它的总地下室平方英尺:

Randomized ablation feature importance for the same 100 points after 30 repetitions.
然而,事实证明,这些特征重要性分数的不确定性的更大来源实际上源于所使用的数据集的小尺寸,而不是消融重复的小数量。这一事实是通过使用我们论文中介绍的其他 CI 方法发现的,该方法捕捉了消融和数据集大小所导致的不确定性。在数据集的另外 100 个点上运行这种 CI 技术(仅重复一次),我们观察到以下宽 CI:

Randomized ablation feature importance for 100 points with point-by-point CIs.
通过将点的数量从 100 增加到 500,我们的信心显著提高,并且我们变得相当有信心邻域是我们模型整体上第三重要的特征(不仅仅是在我们有限的数据集中)。

Randomized ablation feature importance for 500 points with point-by-point CIs.
结论
特征重要性技术是获得关于机器学习模型的有价值的洞察力的强大而简单的方法。随机化消融特征重要性技术,通常称为“置换”重要性,为计算特征重要性提供了直接且广泛适用的技术。我们还在这里展示了如何通过一种新的方法来理论化和公式化随机消融特征重要性的“真实”值,我们能够围绕我们的特征重要性测量构建置信区间。这些置信区间是在实践中避免陷阱的有用工具,尤其是当数据集不大时。
如果你喜欢这篇文章,你可以在 Fiddler 的博客上找到更多类似的文章,如果你想更深入地了解随机消融功能的重要性,请务必查看的全文。别担心,只有四页长!
参考
[1]帕尔等人。艾尔。当心默认随机森林重要性(2018)。【https://explained.ai/rf-importance/
[2]克里斯托弗·莫尔纳尔。可解释的机器学习(2019)。https://christophm . github . io/interpretable-ml-book/feature-importance . html
[3]布雷曼,利奥。随机森林(2001 年)。https://www . stat . Berkeley . edu/% 7 ebreiman/randomforest 2001 . pdf
[4]sci kit-学习贡献者。排列特征重要性(2019)。https://sci kit-learn . org/dev/modules/permutation _ importance . html
[5]费希尔等人。艾尔。模型类依赖(2019)。https://arxiv.org/abs/1801.01489
[6]卢克·梅里克。随机消融特征重要性(2019)。https://arxiv.org/abs/1910.00174
[7]德·科克,院长。艾姆斯,爱荷华州:波士顿住房数据的替代方案,作为期末回归项目(2011)。【http://jse.amstat.org/v19n3/decock.pdf
时间序列数据的置信区间:SharpestMinds Slack 聊天
应大众的要求,我们决定公开专业数据科学家和他们的学员之间关于 SharpestMinds 内部松弛的一些对话。我们希望这将让更多的人从 SharpestMinds 的专业知识中受益,即使他们不能直接进入社区。

当大多数人开始学习数据科学时,他们处理的数据是与时间无关的。他们预测泰坦尼克号上乘客的生存概率,识别 MNIST 数据集中的手写字符,或者执行。其他类似的任务。
要解决这些问题,通常首先要将每个样本随机分配给两个不同的数据集:一个用于训练模型,另一个用于验证模型的性能。这个验证步骤很重要,因为它允许你做出这样的声明,“我希望这个预测准确到真实值的 10%以内,20 次中有 19 次。”
但是,这对时间序列数据不起作用:如果您试图预测季节性影响、股票市场波动或客户流失行为,您会很快意识到,将数据随机分配给训练集和验证集会破坏原始数据集的时间顺序中包含的信息。
那么,如何为时间序列预测提供置信区间(又名:预测区间)?
或者,正如一位敏锐的学员最近在我们的内部 Slack 社区中问到的:

Chiemi 之前已经发现了这种方法,但是它只对梯度推进回归器有效。她想要一个更通用的解决方案。
第一个伟大的建议来自 SharpestMinds 的导师 Ray Phan ,他是一个真正的数据科学 Slack 超级英雄:

这是他提供的链接的可点击版本。老实说,我不知道这个功能,它看起来非常方便。
接下来是夏普明德校友(现在是数据科学家🚀)凯赢:


她链接的 Jason Brownlee 的帖子在这里(强烈推荐!).
最后,学员克里斯蒂安·法甘还提出了一个基于贝叶斯区间的非常有趣的策略——它更先进,如果你喜欢冒险和数学,值得一试:

(再次,这里是可点击的形式)。
就是这样!今天只是一个简短的问题,但我认为这里建议的所有不同的观点和工具,如果你正在考虑你自己的时间序列问题,会有所帮助。
直到下次🙂
想要更多吗?
我们每隔几周发布一份时事通讯,其中包含关于如何在数据科学领域就业的对话和提示。【你可以在 这个链接】查看一个例子
可以在推特上关注 Jeremie()@ Jeremie charris)或者 sharpes minds()@ sharpes mindsai)。
原载于 2019 年 11 月 21 日https://blog.sharpestminds.com。
混杂变得简单
摘要:在一项观察性研究中,并不是所有的治疗和结果变量的协变量都应该被调整。默认情况下,人们应该怀疑那些盲目调整许多混杂因素而没有从因果关系上证明其选择的研究。
免责声明:我的因果推理知识有限,我可能会说一些非常错误的话。如果你发现错误,请在推特上联系我 @jsevillamol !
混杂因素的问题
假设你想确定治疗对结果的因果影响。当务之急是确定它们之间是否存在统计相关性。
虽然仍然具有挑战性,但我们有很好的统计工具来确定复杂变量集之间的统计关联网络。
然而,相关性不是因果关系 —相关性可能是由混杂因素引起的,是治疗和结果的因果前因。
例如,治疗可能是吸烟,结果可能是呼吸系统疾病,一个似是而非的混杂因素是年龄;年纪大的人吸烟更频繁,也更容易患呼吸道疾病。
我们可以用因果图来说明这种情况:

A causal diagram for a smoking study
我们说从治疗到结果有一个畅通的后门路径通过年龄,即吸烟<= age =>呼吸道疾病。
理想情况下,我们希望进行一项随机对照试验(RCT ),随机分配治疗方案,这样我们就可以转移后门。

A randomized controlled trial (RCT) of a smoking study
但这并不总是可能的;例如,治疗可能是不道德的,或者我们可能想从历史数据中得出结论。在那些情况下我们应该做什么?
如何不对混杂因素进行调整
阻止混杂因素的虚假影响的另一种方法是通过例如分层来调整。在吸烟的例子中,我们可以将数据分为年轻人和老年人,研究每一组中吸烟和疾病之间的相关性,然后报告加权相关性,作为因果效应的估计。
如果我们确信协变量确实是一个混杂变量,或者是治疗和结果的因果祖先,这将很好地工作——因为在每个研究组中,混杂变量是固定的,它不再能够对治疗和结果产生虚假的影响,我们将能够对治疗的真正因果影响做出断言。
因此,每当研究人员发现一个与治疗和结果都相关的变量时,他们往往会对其进行调整。
但这并不是三个变量之间唯一可能的因果关系!
治疗 X、结果 Y 和协变量 Z 之间可能的因果关系

Confounder

Mediator

Collider
协变量可能会调节治疗和结果之间的相互作用。即 X = > Z,Z = > Y。
例如,我们可以研究转基因作物对消费者健康的影响,我们发现转基因作物不太可能被病原体感染。在这种情况下,病原体的存在将成为转基因生物和消费者健康之间的媒介。
请注意,介质不一定是解释这种效应的唯一机制——转基因生物也可能改变作物的饮食结构,而与它对病原体的影响无关。
在这种情况下,对协变量 Z 进行调整将减少治疗 X 对结果 Y 的明显影响,我们的报告将具有误导性(除非我们特别试图单独测量非协变量介导的治疗效果部分)。
第三种可能性是协变量是治疗和结果的 T2 对撞机。也就是说,X 和 Y 都导致 z。例如,我们可以让人工智能研究人员和国际象棋爱好者都喜欢阅读自动化国际象棋比赛的发展。
调整对撞机将会增加治疗效果在结果中的表观强度。
在前面的例子中,如果我们调查阅读了自动下棋文章的人,我们可能会发现象棋爱好者不太可能是人工智能研究人员,反之亦然——但这并不奇怪,因为我们从我们的调查人口统计中过滤掉了既不是人工智能研究人员也不是象棋爱好者的人。
所以要小心调整调解器和碰撞器!
现在,我们如何区分协变量是混杂变量的情况和协变量是中介变量或碰撞变量的情况?
简而言之:我们不能,至少不能仅从观察数据来看。我们需要依赖于潜在因果关系的领域特定知识。
当涉及多个协变量时,情况会变得更加复杂。我们需要绘制出所有协变量、治疗和结果之间的整个因果图,并在科学基础上证明我们的因果图。
然后,我们可以使用 do-calculus 的规则和原则,如后门标准,找到一组要调整的协变量,以阻止治疗和结果之间的虚假相关性,这样我们就可以估计真正的因果关系。
总的来说,我预计一项研究调整的变量越多,它们就越有可能通过对撞机引入虚假的相关性或阻断中介路径。
自由度的问题
我们应该怀疑以无原则的方式调整许多变量的研究的另一个强有力的原因是如何进行研究的自由度的增加。
如果你用 1000 种不同的方法来衡量两个变量之间的关系,并选择一个相关性最大的,你很可能高估了治疗的有效性。
拥有一个更大的协变量集可以让你对任何子集进行调整。例如,如果你有 10 个协变量,你可以调整 2^10 ≈ 1000 个可能的子集。
不一定是单个研究小组系统地尝试所有可能的调整子集并挑选最佳子集(尽管值得注意的是,一些统计方法正在做一些非常类似的事情——例如变量选择的逐步或最佳子集方法)。可能是不同的研究人员正在尝试不同的子集,而结合他们结果的机制是有偏见的。
例如,100 个研究小组可能会尝试 100 个不同的子集。他们中的 95 个正确地识别出没有影响,但是由于发表偏倚,他们没有广泛地公布他们的结果,而错误地识别出有强烈影响的 5 个组是唯一一个被发表的,造成了一种印象,即所有进行的研究都发现了强烈的影响,而实际上没有。
总的来说,当你不预先承诺在研究中遵循一种原则性的调整方法时,你更有可能在结果中引入偏差。
提醒一句:你仍然需要良好的控制
在这篇文章中,我们将重点放在选择太多不合适的控件的问题上,因为我看到更多的人缺乏这种直觉,即使是那些了解应用统计学的人也是如此。
然而,请注意,你可能会犯相反的错误——你可能无法调整相关的混淆因素——并最终得出结论巧克力消费导致诺贝尔奖。
尤其是对复杂现象的观察,只对一些事情进行调整实际上保证了你忽略了你应该调整的事情——你可能会高估或低估效果。
一个相关的挑战出现在“残留混杂”的标题下。即使你确定了一个混杂因素并对其进行了调整,它仍然会影响到与你测量它的准确程度相称的结果——自然,我们测量的大多数东西都是不准确的或通过代理来测量的。
所以用一句话概括:如果你想从观察数据中推断因果关系,控制混杂因素是关键。
那么我们该怎么办呢?
作为一个试金石,更值得怀疑的是那些对变量进行调整而没有根据因果关系证明其调整选择的观察性研究。
然而,一些研究没有做必要的工作来证明他们选择的混杂因素,这使得我们在从他们的工作中提取可靠的数据时处于一个更糟糕的位置。在这种情况下,我们能做些什么?
首先,我们可以单独检查每一个选择的混杂因素,并思考它们与治疗和结果的因果关系。
例如,假设我们正在审查一项关于核不扩散条约(X)对核武器投资水平(Y)的影响的研究,我们想知道它们是否应该根据 GDP (Z)进行调整。
嗯,有可能 GDP 较高的国家也更有影响力,并使条约对他们有利,所以 Z => X。而 GDP 较高的国家可以在核武器上投资更多,所以 Z => Y。在这种情况下,GDP 将是一个混杂因素,我们应该为此进行调整。
但我们可以讲一个同样令人信服的故事,认为签署该条约的国家可能会被视为更加合作,并获得更好的贸易协议,所以 X => Z。而在核武器上投资更多的国家具有更好的安全性,因此它们吸引更多的投资者,所以 Y => Z。根据这种解释,GDP 是一个对撞机,我们不应该为此进行调整。
或者我们可以结合前面的两个场景来论证 X=>Z 和 Z=>Y,所以 GDP 将是一个碰撞体,我们也不应该对它进行调整。
在没有令人信服的理由来拒绝替代解释的情况下,我们不应该根据 GDP 进行调整。
然而,想象一下,这项研究正在根据其他核协议的参与情况进行调整。认为加入其他条约导致加入《不扩散条约》似乎有些牵强;这两者似乎都是由该国签署核条约的普遍倾向更直接导致的。

在这种情况下,“对条约的偏好”是《不扩散核武器条约》对核投资影响的混淆因素,但我们无法直接观察到它。然而,我们可以通过后门标准调整“其他核条约”来阻止其虚假影响。
如果这项研究对 GPD 和参与其他核条约进行调整,会发生什么?
默认情况下,我们应该怀疑他们的结论的因果有效性。
我们可以利用这一信息做出一些预测(例如,我们可以利用上述研究的结果来猜测一个无论如何都要签署条约的国家是否会减少对核武库的投资),但我们不能提出处理建议(例如,我们不能断言游说一个国家行为者接受《不扩散条约》是让他们减少核武库的有效方式)。
如果我们想挽救他们的结果,我们可以试着建立一个相关变量的因果图,并考虑他们选择的混杂因素是否满足相关标准。
如果他们选择的调整变量没有正确地阻止虚假效应或通过碰撞器引入新的效应,并且我们可以访问这些数据,我们可能想尝试用更好的调整变量重新运行这项研究。
但是当然,我们仍然可以确定作者没有包括在数据集中的关键混杂因素。在这种情况下,我建议关注约翰·图基的话:
“一些数据和对答案的渴望并不能确保从给定的数据中提取出合理的答案。”
结论
在这篇文章中,我们解释了协变量和治疗结果对之间的三种因果关系:混杂因素、中介因素和碰撞因素。我们已经看到,为了推断因果效应,我们应该调整混杂因素,而不是中介因素或碰撞因素。
我们认为,一项观察性研究调整的变量越多,它们就越有可能犯因果错误,或者额外的自由度和发表偏倚夸大了报道的效果。
我们也提醒读者不要犯相反的错误——以有原则的方式调整混杂因素对于将观察数据转化为因果信息至关重要。
作为从以前的研究中提取数据的一种方式,我们建议根据因果标准严格检查他们对调整协变量的选择。如果他们对不需要的变量进行了调整,我们建议在数据可用的情况下重新进行分析,而如果数据中缺少关键混杂因素,我们应该接受有时我们没有足够的信息来正确回答我们关心的问题。
我要感谢 Luisa Rodriguez 给了我思考混杂因素的借口,感谢 Gregory Lewis 阅读了早期的草稿并指出错误。通过我直接从格雷戈里的评论中剽窃的文章的一些巧妙的引用;所有的错误都是我的错。
关于因果推理的进一步阅读,我推荐朱迪亚·珀尔的《为什么 和 一本 关于好与坏控制的速成教程 ,以及格温的《为什么关联通常≠因果关系 》。
本文由牛津大学 人类未来研究所 暑期研究员杰米·塞维利亚撰写。在 twitter 上找我@ jsevillamol。
直面深度学习系统:有多少事情已经改变,有多少我们不知道

Photo by Magne Træland on Unsplash
随着公司在业务中采用人工智能,他们正在大规模地面对深度学习系统的技术。这就像进入一个巨大的黑暗洞穴——惊奇和兴奋,伴随着恐惧,你不知道你将进入什么!
在 IT 方面几乎一无所知
我向一位同事推荐了帕科·内森的博客,该博客总结了最近的地层调查。这位同事是商业智能和数据仓库领域备受尊敬的专家。
他的反应是,“ 有多少事情已经改变,有多少我们不知道!
……指 IT 专业人士对抗新兴的深度学习系统。
用我的话来说,IT 专业人士对建立在现有基础设施上的人工智能系统的含义一无所知。此外,在企业集团之间出现了严重的文化脱节,这些集团必须为组织及其利益相关者合作,以实现人工智能系统的价值。
虽然这是一个令人不安的评论,但听到这个反应,我还是松了一口气。我花了去年的时间试图说服其他 BI/DW 同事,让他们认识到等待企业 IT 的未决问题。通常的反应是干瞪眼…好像他们怀疑我的理智!
术语:使用人工神经网络(ANN)的深度学习(DL)是机器学习(ML)的一个子集。这篇文章强调了企业系统中传统机器学习(无 ANN)与深度学习(使用 ANN)之间的差异。
那么,有什么好大惊小怪的呢?
TL;在你有地图之前,不要进入(人工智能系统的)洞穴!
Pacoid 再次出击!
Paco Nathan(或 twitter 上的 Pacoid)自我描述为“邪恶的疯狂科学家”和“球员/教练”。我的经验严重倾向于后者。在 AI/ML/DS 社区中,他是一个杰出的人物,他与外界联系紧密,才华横溢,善于表达,富有人情味。
内森与本·洛里卡合作,本·洛里卡是奥莱利媒体公司的首席数据科学家,也是地层数据会议和 T21 人工智能会议的项目总监。他们调查了这些社区的态度和行动,这些社区是早期采用者——跨越(或努力跨越)鸿沟,在他们的组织内拥抱人工智能系统。
因此,大惊小怪的是关注这些早期采用者告诉我们的关于当前人工智能系统的信息,无论它们是成功的、失败的、灾难性的、道德上有害的、政治上幼稚的,还是介于两者之间的一切。
该调查基于 2018 年 11 月中旬的 1300 份答复,具有良好的全球企业代表性。因此,应该认真对待这一信息。这里是如何做到这一点…
首先,我推荐阅读 Pacoid 的博客。[1]其次,浏览与 Lorica 合著的这三份报告中的调查详情:
- 机器学习状态(2018 年 8 月)[2]
- (2019 年 1 月)[3]
- (2019 年 2 月)【4】—尤其是这个**
- 此外,在[5]和[6]中引用的早期报告
如果你正在你的组织内拥抱(或计划拥抱)人工智能系统,你会发现几个发人深省的观察。…洞穴地图的碎片。
以下是我的几点看法。请在下面的评论中分享你的观察。
外卖:深度学习有牵引力
与我交谈过的许多 IT 专业人士认为,神经网络令人兴奋,但传统的机器学习对于未来许多年的商业应用来说绰绰有余。
然而,这些调查表明,深度学习技术在企业系统中具有相当大的吸引力。它不再仅仅是研究人员的宠儿;这项技术正在投入使用!
调查数据显示,超过一半的人正在使用深度学习(55%),以及强化学习(22%)和迁移学习(16%),如图所示。

Figure 1–15. AI technologies used, from AI Adoption survey [4]
另一方面……人们也想知道受访者在检查时在想象什么:主动学习、知识图表、计划和推理。这里需要面对面的面试!
正如所料,超过一半(53%)的人使用神经网络进行图像处理。然而,令人惊讶的是,更多的人将神经网络用于结构化数据(86%)和文本(69%),如下所示。

Figure 1–21. Data types, limited to deep learning respondents, from AI Adoption survey [4]
当被问及使用的人工智能工具时,令人惊讶的是,大量工具主要涉及神经网络,如 TensorFlow (55%)、Keras (34%)和 PyTorch (29%)。这明显多于传统机器学习的工具,如 scikit-Learn。

Figure 1–29. AI tools used, in [4]
最后,人工智能采用报告[4]中的后续数字表明,深度学习的采用在各个行业都很普遍,并在人工智能成熟实践的公司中加速。
外卖:跟上人工智能成熟的人——不
it 专业人士经典的“等到别人想通了”策略对一些公司来说可能是一场灾难。这种策略忽略了新兴人工智能系统正在推动的基本范式的快速转变。这种转变需要缓慢的文化变革,以使技术变革得以成功实施。让我们详细研究一下这个问题。
洛里卡和内森经常区分人工智能采用的三个成熟阶段:尚未成熟的 (19%),正在评估的 (54%),以及正在生产的或人工智能成熟的(27%)。[4]这一区别用于理解报告中的几个因素。

Figure 1–2. Stage of maturity for AI adoption, in [4]
虽然这些类别是模糊的,但请观察这些关键方面。人工智能成熟的(生产中的)公司正在不断地做所有这三个类别。人工智能成熟的公司可能还没有特定的人工智能方法(如强化学习),但他们已经指派一些专家每周监测关于该方法的新兴研究。人工智能成熟的公司可能正在评估许多原型,但他们不断在他们的生产模型中使用它们进行挑战者-冠军竞赛。这是各个层面的持续创新,因此…****
一个关键的见解是,人工智能成熟的组织正在迅速扩大与尚未成熟和正在评估的组织的差距,使其难以赶上,如下所示。请注意,瓶颈(黄色突出显示)——文化没有认识到需求,用例没有确定——对于人工智能成熟的公司来说变得不那么重要。相比之下,缺乏高质量的数据和缺乏熟练的人员对人工智能成熟的公司来说更为显著。

Figure 1–9. Challenges, by stage of maturity, in [4]
人工智能成熟公司的人工智能技术更倾向于深度学习(以黄色突出显示),这需要更大的投资,如下所示。

Figure 1–17. AI technologies, by stage of maturity, in [4]
差距扩大的原因是人工智能采用的障碍主要是文化上的,而不是技术上的。在这种情况下,没有针对啤酒的跳跃式银子弹,至少在地平线上没有!
外卖:人工智能学习的动物园
在过去的一年里,深度学习的一个有趣的方面是深度学习的途径、方法、视角和实践的不断扩大。每周在 arXiv 、 PapersWithCode 、 DataElixir 或two minutes papers上,我经常反应真的!…还是很神奇!
在调查中,你会看到这个动物园的新术语,如迁移学习、强化学习、生成对抗学习等等。这个动物园的一部分是一个叫做元学习的类别,它表示一个算法正在学习它如何学习以及如何改进它的学习。这就像一个人类导师的角色,引导和激励学生学得更多更好。
参考上面图 1–17 中人工智能技术的成熟阶段。请注意,与评估阶段的公司相比,实践成熟的公司使用迁移学习的次数是前者的 3 倍。洛里卡和内森发表评论…
迁移学习提供了一个有趣的细微差别,因为它在生产中的使用往往需要更有经验的从业者。我们
看到成熟的实践利用迁移学习的速度是评估阶段公司的近三倍。迁移学习的应用是有价值的,尽管这些价值对于门外汉来说可能不那么明显。[4]
在公司研究更高水平的人工智能成熟度之前,他们不知道自己不知道什么,并且能够获得价值。
在其他地方,洛里卡和内森谈到了强化学习…
已经使用强化学习的受访者正在开始
在我们 2017 年列出的
强化学习的一些应用领域建立人工智能系统:客户服务;
运营、设施和车队管理;金融;和营销,
广告,和公关。[4]
最后,无监督的深度学习是一个热门的研究领域,有自动编码器,高维嵌入空间等等。许多与机器学习的明显区别正在变得模糊,例如训练/测试数据集的意义。因此,吸收这又一个范式转变将是具有挑战性的!
外卖:优化业务指标—是
成功的人工智能系统的文化变革不可或缺的是正确识别深度学习的业务用例的技能,由针对特定业务目标的指标塑造。这项技能需要重新思考基本范式、丰富的想象力和良好的商业头脑。所有职能部门(数据科学家、数据工程、开发运维专家、业务线经理等)都缺乏这种技能。)是关键的拦路虎。
在下图中,最大的技能缺口(57%)是对“ML 建模师和数据科学家”的明显需求。然而,几乎有一半(47%)表明了对能够“理解和维护一组业务用例”的人的需求。

Figure 1–10. Skills gap, in [4]
内森特别指出…
“人工智能的产品管理”缺乏经过验证的方法,这种专业知识需要时间在组织层面上培养。[1]
要点:不仅仅是业务指标
一个令人耳目一新的方面是对不仅仅与业务相关的指标的强调。每个组织都存在于大社会中,它必须为大社会做出贡献,而不仅仅是索取。因此,调查受访者对“透明度、可解释性、公平性、偏见、道德、隐私、安全性、可靠性和合规性”重要性的认识令人惊讶。
下图列出了调查中的问题,按人工智能采用成熟度分类。

Figure 1–28. Risks checked, by stage of maturity, in [4]
请注意,随着公司在人工智能采用方面的成熟,他们对这些问题的关注也在增加。我发现数据科学专业人士中越来越多的共识是,这些问题是他们公司的真正风险,并将决定应用人工智能技术的长期成功。要使这成为现实,还有艰巨的技术工作要做。把它想象成给你正在制造的核弹安装安全装置。这迫使你进入正确的心态!
外卖:另一件事
在 Pacoid 博客的结尾,有一段是以“另一件事”开始的,好像 Nathan 停顿了一下,思考了一下,并分享了最后的深切关注…
我一直听到的一个观点是,数据科学家和工程师之间有一种隐现的脱节。相反,迫在眉睫的脱节是 ML 的工程部署和业务用例的“最后一英里”之间的差距,你需要熬夜担心。火花,卡夫卡,张量流,雪花等。,不会在那里救你。AutoML 不会在那里救你。这就是模型一旦暴露给实时客户数据就会退化的地方,这需要大量的统计专业知识来回答甚至一个简单的“为什么?”利益相关者的问题。这是一个大的攻击面暴露于针对输入数据的安全利用的点——具有目前几乎不可想象的后果。这就是复杂的道德和合规问题产生的地方,这些问题会导致愤怒的监管者前来敲门。那些是商业问题。当最终业务问题会给你的组织带来最大的痛苦时,停止膨胀数据工程团队作为灵丹妙药。我们在企业中采用人工智能的时间还不够长,关于这些问题的案例研究还不足以成为标准的 HBS 讲座,但它们将会成为。很快。[1]
这一段真的击中了我的要害!我可以清楚地想象一家大公司热情地推出一个创新的人工智能系统来解决一个重大的商业问题,结果却撞毁了上述段落中的现实。
思考新范式和新价值观
理解上述要点的一个有用方法是认识到我们都需要以不同的方式思考过去几十年来推动企业系统技术进步的基本范式和价值观。这可不容易!因为…
这是一种思考信息以及如何有效利用信息来改进业务流程的新方式。仅仅为管理者提供见解已经不够了,这威胁到了当今大多数商业智能基础设施。见解不能在委员会会议上被搁置,而必须立即转化为指导当今商业交易的指南。
这是对软件的一种新的思考方式,今天的软件本质上是天才计算机程序员的艺术作品……就像博物馆里描绘静态逻辑清晰片段的油画。世界不是一成不变的。静态逻辑的每一部分从投入生产的那一天起就过时了,需要不断地修改和修补以跟踪不断变化的世界。如今的软件无法追踪世界每小时的变化。
这是对学习的一种新的思考方式,如今这种学习仅限于组织信息生态系统的数据集成,正如数据仓库+数据湖架构所反映的那样。这种范式必须转变为管理模型,将这些数据归纳为一个持续的学习过程。软件必须通过不断进化这些模型中的逻辑来跟踪世界的每一个曲折。所以,你上周安装的人工智能系统这周的行为有了很大的不同。你能做到吗?
这是一种思考人类智能及其在管理组织中的作用的新方式。人们越来越容易认为,用人工智能取代人类智能更具成本效益,因此这自然会导致工作岗位的减少。一个人应该接受这个结果,并发现一个新的职业。越来越难认为用人工智能(例如,人在回路中)增强人类智能具有独特的好处,应该被设计到人工智能系统中。这似乎是一个越来越困难的任务,对组织没有明显的好处。那么,这是未来社会人类智慧的最终结果吗?
不幸的是,许多组织将永远无法转变他们的文化,从而失去他们的竞争优势——作为一个企业的目的——并遭受挥之不去的死亡。
这不是必然的!作为 IT 专业人员,我们生活在一个有趣的时代。咳咳!随着人工智能技术的爆炸式增长,我们有令人兴奋的有利机会以现在难以想象的方式在全球范围内改变商业。希望是,作为一个社会,我们这样做是为了造福所有人。然而,不幸的现实是…如果我们懒惰或以自我为中心,甚至是恶意的,那些同样的机会是可以被误用的。
BizSmartAnalytics 插件
我愿意与关心上述问题的其他人合作。首先,在下面的评论中分享你的想法。第二,分享你对 BizSmartAnalytics.com所列相关文章的反馈。第三,支持 Patreon 在https://www.patreon.com/BizSmartAnalytics指导与人工智能系统相关的 IT 专业人员同行小组。
帽子尖给《T2》数据灵丹妙药 的编辑 Lon Riesberg——这是我推荐的。每周,我都会找到至少一两个关于这个爆炸式增长的数据科学领域的优秀资源。这是我发现 Pacoid 博客的地方。在https://DataElixir.com/免费订阅。
参考
[1]内森,帕科。每个 Pacoid 的主题和会议,第 7 集。数据科学博客,多米诺数据实验室。2019 年 3 月 3 日。
https://blog . dominodatalab . com/themes-and-conferences-per-pacoid-episode-7/
[2]洛里卡&内森。企业采用机器学习的状况。2018 年 8 月。
https://www . oreilly . com/data/free/state-of-machine-learning-adoption-in-the-enterprise . CSP(需要免费注册,不含广告拦截)
[3]洛里卡&内森。不断发展的数据基础设施:高级分析和人工智能的工具和最佳实践。2019 年 1 月。
https://www . oreilly . com/data/free/evolving-data-infra structure . CSP(需要免费注册不含广告拦截)
[4]洛里卡&内森。人工智能在企业中的采用:企业如何在实践中规划和优先考虑人工智能项目。2019 年 2 月。
https://www . oreilly . com/data/free/ai-adoption-in-the-enterprise . CSP(需要免费注册,不含广告拦截)
[5]洛里卡。人工智能技能发展的三个有希望的领域。2018 年 8 月。
https://www . oreilly . com/ideas/3-有前途的人工智能技能发展领域
[6]洛里卡和洛基德斯。公司如何通过深度学习将人工智能投入工作。2018 年 4 月。
https://www . oreilly . com/data/free/how-companies-is-put-to-work-through-deep-learning . CSP
混淆矩阵和类别统计
合著: 马瑞特
通过这篇文章,我将回归经典。这里解释了混淆矩阵和一些与它相关的准确性措施。
— — — — — — —
分类模型将数据分配给两个或多个类别。有时,检测一个或另一个类同样重要,并且不需要额外的成本。例如,我们可能希望平等地区分白葡萄酒和红葡萄酒。在其他时候,检测一个类别的成员比检测另一个类别的成员更重要:只要发现所有犯罪的航班乘客,对无威胁的航班乘客进行额外的调查是可以容忍的。
当您量化分类模型的性能时,类别分布也很重要。例如,在疾病检测中,与健康人群相比,疾病携带者的数量可能很少。
评估任何性质的分类模型的第一步是检查它的混淆矩阵。事实上,许多模型统计和准确性度量都是建立在这个混乱矩阵之上的。
电子邮件分类:垃圾邮件与有用邮件
让我们以电子邮件分类问题为例。目标是将收到的电子邮件分为两类:垃圾邮件和有用的(“正常”)电子邮件。为此,我们使用由 UCI 机器学习库提供的 Spambase 数据集。该数据集包含 4601 封电子邮件,通过 57 个特征进行描述,如文本长度和特定单词的存在,如“购买”、“订阅”和“赢得”。“垃圾邮件”列为电子邮件提供了两种可能的标签:“垃圾邮件”和“正常”。
图 1 显示了一个工作流,它涵盖了构建分类模型的步骤:读取和预处理数据,划分为训练集和测试集,训练模型,根据模型进行预测,以及评估预测结果。
下面显示的工作流可从 KNIME Hub 页面下载,也可从示例服务器下载,下载位置为:04 _ Analytics/10 _ Scoring/01 _ Evaluating _ Classification _ Model _ Performance。

Fig. 1: Workflow building, applying and evaluating a supervised classification model: data reading and preprocessing, partitioning, model training, prediction, and model evaluation. This workflow predicts whether emails are “spam” or “normal”.
构建分类模型的最后一步是模型评分,它基于测试集中实际的和预测的目标列值的比较。模型的整个评分过程由一个匹配计数组成:有多少数据行被模型正确分类,有多少数据行被模型错误分类。混淆矩阵中总结了这些计数。
在电子邮件分类示例中,我们需要回答几个不同的问题:
- 有多少实际的垃圾邮件被预测为垃圾邮件?
- 正常的有多少?
- 一些正常的电子邮件被预测为垃圾邮件吗?
- 有多少正常邮件被正确预测?
这些数字显示在混淆矩阵中。并且在混淆矩阵的顶部计算类别统计。混淆矩阵和类统计显示在 Scorer (JavaScript)节点的交互视图中,如图 2 所示。

Fig. 2: Confusion matrix and class statistics in the interactive view of the Scorer (JavaScript) node.
混淆矩阵
现在让我们看看这些数字在混乱矩阵中是什么。
最初引入混淆矩阵是为了评估二项式分类的结果。因此,首先要做的是将两个类中的一个作为感兴趣的类,即正类。在目标列中,我们需要(任意)选择一个值作为正类。另一个值则自动被视为负类。这个赋值是任意的,只要记住一些类统计会根据所选的正类显示不同的值。这里我们选择垃圾邮件作为正面类,正常邮件作为负面类。
图 3 中的混淆矩阵报告了:
- 属于肯定类别(垃圾邮件)并被正确分类的数据行(电子邮件)。这些被称为真阳性(TP)。真阳性的数量放在混淆矩阵的左上角单元格中。
- 数据行(电子邮件)属于肯定类(垃圾邮件),但被错误地归类为否定类(正常电子邮件)。这些被称为假阴性(FN) 。假阴性的数量放在混淆矩阵的右上角单元中。
- 属于负面类别(正常)且被错误分类为正面类别(垃圾邮件)的数据行(电子邮件)。这些被称为假阳性(FP) 。假阳性的数量被放在混淆矩阵的左下角。
属于负类(正常)并被正确分类的数据行(电子邮件)。这些被称为真否定(TN) 。真阴性的数量放在混淆矩阵的右下角。
因此,正确的预测是在灰色背景的对角线上;不正确的预测在红色背景的对角线上:

Fig. 3: A confusion matrix showing actual and predicted positive and negative classes in the test set.
等级统计的度量
现在,使用混淆矩阵中的四个计数,我们可以计算一些类统计度量来量化模型性能。
类统计,顾名思义,分别总结了正类和负类的模型性能。这就是为什么它的值和解释随着正类的不同定义而变化,以及为什么它经常用两个度量来表示的原因。
敏感性和特异性

Fig. 4: Sensitivity and specificity values and their formulas, which are based on the values in the confusion matrix, for a classification model predicting emails as “spam” or “normal”
敏感度衡量模型检测阳性类别中事件的难易程度。因此,假设垃圾邮件是肯定的类别,敏感度量化了有多少实际的垃圾邮件被正确预测为垃圾邮件。

我们将真阳性的数量除以数据集中所有阳性事件的数量:正确预测的阳性类事件(TP)和错误预测的阳性类事件(FN)。该示例中的模型达到灵敏度值 0.882。这意味着数据集中约 88 %的垃圾邮件被正确预测为垃圾邮件。
特异性测量分配给阳性类别的精确程度,在这种情况下,是分配给电子邮件的垃圾邮件标签。

我们将真阴性的数量除以数据集中所有阴性事件的数量:预测错误的阴性类事件(FP)和预测正确的阴性类事件(TN)。该模型达到 0.964 的特异性值,因此不到 4 %的所有正常电子邮件被错误地预测为垃圾邮件。
召回率、精确度和 F 值

Fig. 5: Recall and precision values and their formulas, which are based on the values shown in the confusion matrix, for a classification model predicting emails as “spam” or “normal”
与敏感度类似,回忆衡量模型在探测积极事件方面的表现。因此,回忆的公式和敏感度的公式是一样的。

Precision 衡量模型在将正面事件分配给正面类方面的表现。也就是垃圾邮件预测的准确程度。

我们将真阳性的数量除以分配给阳性类别的所有事件的数量,即真阳性和假阳性的总和。该模型的精度值为 0.941。因此,几乎 95 %被预测为垃圾邮件的电子邮件实际上是垃圾邮件。
召回率和精确度通常成对报告,因为这些指标从两个角度报告模型的相关性,也称为通过召回率测量的类型 I 错误和通过精确度测量的类型 II 错误。
召回和精确通常是相关的:如果我们使用更严格的垃圾邮件过滤器,我们将减少收件箱中危险邮件的数量,但增加事后必须从垃圾邮件箱文件夹中收集的正常邮件的数量。相反,即不太严格的垃圾邮件过滤器,将迫使我们对收件箱进行第二次手动过滤,一些垃圾邮件偶尔会进入收件箱。
或者,可以通过将召回率和精确度结合起来的方法来报告召回率和精确度。一个例子叫做 F-measure ,它是召回率和精确度的调和平均值:

多元分类模型
在多项式分类模型的情况下,目标列有三个或更多的值。例如,电子邮件可以被标记为“垃圾邮件”、“广告”和“正常邮件”。
类似于二项式分类模型,目标类值被分配给正类和负类。在这里,我们将垃圾邮件定义为正面类别,将普通邮件和广告邮件定义为负面类别。现在,混淆矩阵如图 6 所示。

Fig. 6: Confusion matrix showing the distribution of predictions to true positives, false negatives, false positives, and true negatives for a classification model predicting emails into three classes “spam”, “ad”, and “normal”
为了计算类别统计数据,我们必须使用多元混淆矩阵中的值重新定义真阳性、假阴性、假阳性和真阴性:
- 由阳性类别的行和列标识的单元包含真阳性,即实际和预测的类别是垃圾邮件
- 由肯定类别的行和否定类别的列标识的单元包含假否定,其中实际类别是垃圾邮件,而预测类别是正常或广告
- 由否定类别的行和肯定类别的列标识的单元格包含误报,其中实际类别是正常或 ad,而预测类别是垃圾邮件
- 阳性类别的行和列之外的单元格包含真阴性,其中实际类别为 ad 或正常,预测类别为 ad 或正常。否定类别中的不正确预测仍然被认为是真正的否定
现在,这四个统计数据可用于使用上一节中介绍的公式计算类统计数据。
摘要
在本文中,我们已经为模型性能评估中使用的度量奠定了基础:混淆矩阵。
事实上,混淆矩阵显示了分类模型的性能:有多少正面和负面事件被正确或错误地预测。这些计数是计算更一般的类统计度量的基础。在这里,我们报告了最常用的指标:灵敏度和特异性、召回率和精确度以及 F 值。
混淆矩阵和类统计量已被定义为二项式分类问题。然而,我们已经展示了它们如何能够被容易地扩展以解决多项式分类问题。
— — -
正如最初发表在 KNIME 博客
困惑矩阵——深度探究

将机器学习指标转化为现实世界
解释如何计算机器学习指标的帖子有几百个,而且指标很多!为您的机器学习算法模型选择正确的指标进行优化是一个很大的挑战,我们通常没有正确的答案。我们是应该使用像 MSE 这样对异常值更敏感的度量标准,还是应该使用像 MAPE 这样对百分比更敏感的算法?因此,一个更重要的问题是,我们如何将这些不同的度量标准转化为业务团队更切实的东西?或者回答日常的问题,是否值得继续改进模型?如果模型的 AUC 提高 1%,我们会赚多少?在本帖中,我们将尝试回答这些问题。
通常在回归问题中,我们试图减小预测值(yhat)与实际值(y)之间的距离,并选择产生最小误差的变量的权重。在分类问题中,优化这种类型的度量是没有意义的,因为我们要么有一个二进制类,距离将总是 0 或 1。在多类问题中,我们将有更长的距离,但不会有更大的误差,因为这些类是相互独立的。我们如何解决这一僵局?
这一切都始于准确性。
第一步是统计点击次数。每当预测值等于实际值时(我们称预测值为 yhat,实际值为 y ),我们就认为是命中情况,精确度定义为:

这是一个广泛使用且相当危险的指标。它隐藏了你的模型中的一些可能的缺陷,并可能导致你做出错误的决定。
让我们看一个欺诈分类的例子。我们有 100,000 条观察结果,其中 1%是欺诈案例。如果你的模型很差,你说所有的值都不是欺诈,你仍然有 99%的准确率,这看起来很好,但实际上你的模型完全没有用,因为它不能识别任何欺诈者。
混淆矩阵
为了解决这个问题,我们需要更完整的度量标准,让我们不仅知道有多少命中,而且知道模型有多敏感。
为此,我们将绘制著名的混乱矩阵。这是一个矩阵,我们将实际值放在列中,将预测值放在行中。因此,行和列的交集成为我们的度量。(倒版的并不少见)

我们现在可以在混淆矩阵中看到,有两种类型的命中和两种类型的错误,现在让我们来关注它们。就准确性而言,没有差异,只有命中和未命中。现在我们开始区分它们。
在命中中,我们可以有 T 真阳性(TP) (即类别为 1,为欺诈,算法识别为 1,欺诈)。以及真阴性(TN) (为 0,模型预测为 0)。
至于错误,我们也有两种类型,假阳性(FP) (当它不是欺诈时,模型预测为欺诈),和假阴性(FN) ,当它实际上是欺诈时,模型预测为非欺诈。

在统计学中,我们称假阳性为 I 型错误。它是测试的显著性水平,用α (alpha)表示。一个非常常见的水平是著名的 5%,它的解释是,在α倍(如 5%)我们将拒绝零假设(H0)时,它是真实的。
第二类错误,我们的假阴性,不太受欢迎,一个原因是它不能直接选择(就像我们选择显著性水平一样),在统计学上它代表了检验的功效,以及变化的形式。它使用不同的模型或增加样本量。它由希腊字母β (Beta)表示。当零假设为假,但我们错误地未被拒绝时,第二类错误发生。
这些定义可能会令人困惑,因为它们是相对于我们选择的类别 1 和 0 而言的。一个简单的规则是将最感兴趣的等级设为 1,通常最感兴趣的是它变得更贵的时候。
精确度或灵敏度/召回率

从混淆矩阵中,我们可以建立至少十几个指标,但有两个更重要,精度和召回率。第一个告诉我们所有我们认为是 1 级的案例(在我们的例子中,所有预测为欺诈的案例)中有多少是正确的(即,有多少是真正的欺诈)。他告诉我们,回忆给了我们敏感性的概念,在目标为 1 的所有可能案例中,我们可以捕捉多少,在我们的例子中,在所有可能的欺诈案例中,我们的模型可以识别多少。请注意,这两个指标的分子相同,但分母不同。
在我们最初的例子中,我们有 99%的准确率,我们的敏感度为 0%,因为我们没有发现任何欺诈案例。

更准确还是更灵敏?如何选择
就像机器学习中的几乎所有事情一样,我们在这里也有一个很大的权衡,我们不能同时拥有一个准确和敏感的模型,或者我们改善一个指标或改善另一个指标。让我们更深刻地理解这一点。
假设你需要训练一个模型来预测谁会得癌症(y = 1)。让我们了解一下每种可能性会发生什么:
- TP : (y = yhat = 1) →我们已经正确识别出谁会得癌症,这样这个人就可以得到治疗,增加生存的机会。
- TN : (y = yhat = 0) →我们正确识别谁不会得癌症,这样一个人就可以安心回家,不用担心治疗。
- FP : (y = 0 & yhat = 1) →这里问题开始了。
我们预测这个人会得癌症,而他不会得。所以她将不得不在不必要的考试上花费时间和金钱。但它会活下来。 - FN : (y = 1 & yhat = 0) →也许这是主要问题,这个人会得癌症,而我们的模型预测不会,所以她会高高兴兴回家,不会被检测,大大增加她的生命威胁。
因此,我们通过使模型更精确来减少 I 型误差,或者通过使模型更敏感来减少 II 型误差,在这种情况下,哪种选择是正确的?好吧,如果让我来选择预测自己健康状况的模型,我会让模型尽可能的灵敏,即使这会导致精度降低。但是立法者不这么认为,因为第一类错误也是昂贵的。那么如何选择呢?
阈值
假设模型不直接预测类别,而是计算每个观察值属于该类别的概率,如果该概率大于 50%,则认为该观察值属于类别 1,否则,属于类别 0(或任何数目的类别),这称为阈值,
,它是将类别分开的截点。我们现在要开始做的是想一个选择这个切口的策略。
因此,阅读混淆矩阵的另一种方法是理解类别 0 和类别 1 的概率分布,如图所示:

Moving the cut from 50% to 75% makes the model more accurate and less sensitive.
如果我们想要一个超级准确的模型,我们将阈值从 50%更改为 75%,因此只有当我们对预测非常确定时,我们才会预测为真,相反,如果我们需要一个非常敏感的模型,只需更改它一点点的准确性,我们将阈值降低到,例如,
25%,我们将预测更多的情况为真。

Moving the cut from 50% to 25% makes the model much more sensitive but less accurate.
用财务矩阵优化阈值
如果混淆矩阵取决于使用的阈值,选择哪一个?把赔率变成职业的最佳切入点是什么?让我们使用下面的技术。让我们创建一个混淆矩阵,但我们不是输入预测数量,而是输入击中和未击中的单位成本。下面举个例子:

TP:1000 万融资的 1%利润
FP :我担心如果我们的模型没有识别出欺诈者:-1000 万。
FN :如果我们说某个客户是欺诈,而事实并非如此,我们就不再赚取利润。
TN :正确识别的欺诈不欺诈我们。
之后,我们将把我们的财务混乱矩阵乘以模型得出的混乱矩阵。你的结果将是我们模型的盈利/亏损。之后,我们将测试不同的阈值,选择利润最高的阈值。机器学习模型不能考虑这一点,因为它的错误具有对称的成本,在现实生活中,我们很少对类型 I 错误和类型 II 错误具有相同的成本,每当成本不同时,我们需要选择正确的阈值。

下面的代码迭代不同的切割,以确定哪个切割产生的利润最大:

最后是我们的利润 x 削减曲线:

现在很容易决定削减 0.86。高于 86%的几率将被视为欺诈,低于 86%的几率将被视为通过。根据目前的数据,我们似乎需要一个比敏感模型更准确的模型。
f1-分数
不使用精确度或召回率的替代方法是使用两者的平均值。代表精确度和灵敏度之间平均值的指标是 F1 分。此指标的唯一细节是它不是简单的算术平均值(求和并除以 2),因为对比率使用简单平均值不是好的做法。相反,我们使用调和平均值来使度量对比率不均衡敏感:

第二种形式是 F-Beta,我们可以通过 Beta 参数在精确度和召回率之间选择权重:


Matriz de confusão expandida
后续步骤
一个不同的甚至比我们的混淆矩阵更强大的指标是所谓的 AUC,它修复了这里提出的指标的一些“缺陷”,可以在下面的帖子中检查:
将机器学习指标转化为现实世界
medium.com](https://medium.com/@marcos.silva0/deep-dive-auc-e1e8555d51d0)
参见:
困惑矩阵——它是什么?

Source: Unsplash
机器学习
计算评估二元分类所需的矩阵
你手头有一个二进制分类问题。让我们将目标变量中的两个类分别表示为“负”和“正”。您已经有了用于开发分类器的数据集,执行了探索性数据分析、特征工程,并得出了应该训练什么模型的结论。您已经将数据分为定型集和测试集,并使用定型集来定型您的模型。然后,将测试数据中的每个实例提供给训练好的模型,并获得两个类中的一个作为相应的输出。
现在,你如何知道你的模型是否表现良好?并非所有被模型分类为“负”的测试集行将实际上是“负”的。同样适用于‘积极’。换句话说,你如何评价自己?这就是混淆矩阵发挥作用的地方。
混淆矩阵是具有以下结构的 2x2 矩阵:

元素真阴性的数量是被模型分类为‘阴性’但实际上是‘阴性’的行数。
元素假阴性的数量是被模型分类为‘阳性’但实际上是‘阴性’的行数。
同样的道理,我们可以理解真阳性数和 假阳性数是什么意思。
列标签阴性(预测)表示第一列包含预测的“阴性”数&列标签阳性(预测)表示第二列包含预测的“阳性”数。同样,行标签负数(实际) & 正数(实际)表示这两行包含实际“负数”和“正数”的数字。
让我们看一个例子。假设测试集包含 100 行,其中 75 行为“负”,其余为“正”。假设经过训练的模型正确地将 50%的实际“负面”和 15%的实际“正面”分类。这意味着它错误地将 25 个实际“负面”和 10 个实际“正面”分类。这意味着,
真正否定的数量= 50
假阴性的数量= 25
真阳性的数量= 15
误报数量= 10
因此,本例中的混淆矩阵如下所示:

为了表示简单,让我们将个真阴性表示为TN ,个假阴性表示为 FN ,个真阳性表示为 TP,和个假阳性表示为 FP 。因此,混淆矩阵的简化表示为:

不难理解,真阴性数 (TN)和假阴性数 (FN)之和等于总数 模型预测的阴性数,即 FN +TN =预测阴性总数。
类似地,真阴性数 (TN)和假阳性数 (FP)之和等于真阴性总数,即 TN + FP =真阴性总数。
根据同样的推理,我们理解,
FP + TP =预测阳性总数
FN + TP =实际阳性总数。
当然,预测阴性总数 +预测阳性总数=测试集行数**
类似地,实际阴性总数+实际阳性总数=测试集行数。**
将此信息添加到混淆矩阵中:

将这些附加信息添加到前面讨论的例子中的混淆矩阵中:

让我们看看如何使用 scikit-learn 的度量模块在 Python 中计算混淆矩阵。让我们在测试集中取 10 行,并将“负”和“正”分别表示为 0 和 1。在下面的 Python 代码中,我们从度量模块中导入了混淆 _ 矩阵函数。然后将实际的和预测的类作为输入给混淆矩阵函数,我们得到矩阵作为输出。
检查代码中的实际和预测列表,
真底片数 (TN) = 1
真阳性的数量 (TP) = 4
假阴性数量 (FN) = 2
误报数量 (FP) = 3
实际底片总数 (TN + FP) = 4
实际阳性总数 (TP + FN) = 6
预测否定总数 (TN + FN) = 3
预测阳性总数 (TP + FP) = 7
在上面的代码中打印混淆矩阵会得到以下输出:
[[1 3]
[2 4]]
程序给出了正确的混淆矩阵。
在我以后的文章中,我将解释如何使用混淆矩阵来评估二元分类器的性能。
现在结束!
延伸阅读:
neptune.ai 的以下帖子详细解释了可以使用混淆矩阵计算的 24 个性能指标,以评估二进制分类:
* [## 二元分类的 24 个评估指标(以及何时使用)- neptune.ai
分类度量让您可以评估机器学习模型的性能,但它们有这么多,每个…
海王星. ai](https://neptune.ai/blog/evaluation-metrics-binary-classification)
参考资料:
[## Skymind
关于混淆矩阵的文章— skymind.ai](https://skymind.ai/wiki/accuracy-precision-recall-f1) [## 机器学习中的混淆矩阵
在机器学习领域,特别是统计分类问题,混淆矩阵,也…
www.geeksforgeeks.org](https://www.geeksforgeeks.org/confusion-matrix-machine-learning/)*
不偏不倚不一定公平

关于“偏见”在数据科学中的真正含义,有太多的困惑
最近有很多关于人工智能和机器学习中的伦理和偏见的伟大文章,但每篇文章都让我想起近年来我与分析师和数据科学家就“偏见”进行的经常是艰难的对话和培训,以及围绕模型或机器生成的结果中可能存在或不存在的各种不同类型的偏见的困惑。这些对话很少触及任何伦理困境,而是关注准确性和可能导致扭曲结果(偏向特定结果)的因素。尽管一个精确的(无偏的)算法被提供了歪斜的(有偏的)数据,歪斜的(有偏的)结果仍可能发生。如果结果本身是有偏差的,但不是以不公平的方式偏袒一个群体,那么根据许多定义,它是“无偏见的”…令人困惑。是啊…
机器不能偏!
在过去的一年里,我与一位非常自信的高级数据架构师进行了一次比较痛苦的对话,内容是关于团队产生的算法偏差。这并不是一种造成道德困境的“偏见”,而是一种针对特定客户的偏见,仅仅因为我们从中提取的数据样本超出了这部分客户。不可能!他不以为然地摇着手指,好像我不再值得在他的智力优势面前出现。对他来说,我提出一个数学公式会激发情感并对刻板印象起作用的想法是荒谬的,也是我无知的表现。对我来说,我们雇佣了一个不理解样本偏差概念的高级数据架构师的想法是一个深不可测的组织失败,需要解决。数据科学开发团队中还有其他人不理解样本偏差吗?我就不吊你胃口了——痛苦的回答是肯定的。事实上,在我们将我们认为理所当然的统计基础知识添加到“数据科学家”、“数据分析师”和“数据架构师”的筛选标准中后,它大大减少了我们的候选人。我震惊地发现,即使是许多受过高级机器学习方法培训的数据科学家,也很少或根本不了解如何确定数据样本中的偏斜或偏差。
即使大数据也有大偏差
如果你碰巧是脸书、亚马逊或谷歌,你拥有每个人所有时间做的所有事情的数据——那么你正在研究所谓的“人口”。对我们其他人来说——你正在研究一个样本。这可能是一个巨大的 Pb 大小的样本,但它仍然是一个样本。在营销分析中,大多数零售商对忠诚客户的丰富数据有很好的洞察力,几乎与在线客户的丰富数据一样,也许是店内客户的良好数据。在你的客户交易的已知范围内,会有需要调整的偏差。一家拥有 90%交易量的公司要做的工作要少得多,但如果你只跟踪忠诚客户,这些客户和非忠诚客户的行为会有很大的不同。因此,如果不了解数据不对称会导致何种偏见,你就无法对你的客户做出全面的了解。当你更进一步,试着推断你现有客户的特征和行为以获得新客户时,理解你的客户与一般人群以及你的潜在目标有多大的不同是非常重要的。
机器不会制造不公平,但会延续已经存在的不公平 模式
好算法&坏数据 —数据中的偏差意味着数据中存在偏差,这可能会给出不正确的结果。它比损坏的数据更棘手,因为从各方面来看,它都处于良好的状态,所以它通过了所有的质量检查,但它来自一个过度/不足代表某些人群或行为的来源。对于许多 ML/AI 部署来说,这种错误已经是一个普遍而持久的问题。由于公司面临来自董事会和投资者的压力,要求它们在产品中添加任何类似人工智能的东西,它们竞相将算法放在未经验证的数据集上推向市场。基于深度学习算法的扭曲数据是黑盒的完美配方,黑盒会吐出看似合理但无法完全检查的答案,直到出现可怕和明显的错误。令人不安?应该是这样的,但在某种程度上,这更多的是关于疏忽的实践,而不是通过正确数据工作的算法、良好的算法但产生不公平的结果所产生的道德困境。
好的数据,好的算法,坏的结果——机器输入完美的数据,创造出不带感情色彩的数学结果,无论你如何解读它们。人类观察结果并解读意图——但这与意图无关,这只是数学。当结果不公平时,问题就出现了,事实是,因为这个世界并不像今天这样公平,不受约束的算法将加剧这种差距。事实:有一些产品,低收入社区的人会比富裕社区的人支付更多。这不是观点,也不是数据偏差或糟糕算法的结果——这是我知道的事实,因为多年来强大而彻底的定量分析。基于这种认识,在一个公正的、正确运行的机器算法独立于人类干预来设定价格的世界里,穷人和少数民族将为基本商品支付更多的费用。这不能通过从算法中删除收入、种族和其他属性来解决——事实上,添加这些东西是唯一可以帮助阻止算法产生任何有社会良知的人都会认为不公平的结果的东西。即使在缺乏社会良知的情况下,抵制或对歧视行为采取法律行动的威胁也足以让大多数公司尽一切可能避免不公平的结果,这种不公平的结果不是来自数据或算法的错误,而是来自我们的社会结构和人类表现出的有时令人惊讶的行为,这些行为被机器发现了。
PS。当然,我很容易指责人们不理解认知偏差和统计偏差之间的区别,或者不了解英语中使用偏差的各种方式。对任何有兴趣的人来说,这里有一些链接。
国会关门了,所以她成了网飞的一名数据科学家
了解 Becky Tucker 如何克服学术生涯中的挫折,她在网飞大学做什么,以及用同理心倾听的重要性

网飞彻底改变了我们观看的方式,并将基于订阅的流媒体内容变成了常态。凭借超过 1 . 39 亿的全球用户和美国 51%的市场份额,网飞是这个领域的主导者。
贝基·塔克已经和网飞在一起快五年了。她目前是 Studio 科学和分析团队的高级数据科学家。在本文中,我们将讨论她从物理学到数据科学的转变,她在网飞大学的工作,以及带着同理心和谦逊倾听的重要性。
转向数据科学 在从事数据科学之前,Becky 对物理和更广阔的宇宙非常着迷。她在佐治亚理工大学攻读物理学,重点是天文学。受教学和学习乐趣的激励,她在加州理工学院观测宇宙学小组攻读物理学博士学位。
她以实验专家的身份加入了这个小组,用 Matlab 和 Python 做实验工作和数据分析。然而,到了第四年,她开始对自己在学术界的工作前景感到失望。她感兴趣的工作类型意味着一年只会有两到三个终身教职的学术职位,而且通常竞争非常激烈。
更糟糕的是,在她研究生院的最后一年,政府关门导致极地项目办公室取消了整整一季的仪器发射,这导致她的实验推迟了两年。因此,她可以再呆两年,直到完成她的项目,或者将她的职业生涯转向新的领域。
由于她整个实验室的设备都滞留在新西兰,要过几个月才能全部取回,她有很多空闲时间。这一空白让她重新审视了自己对学术界的疑虑,因此她开始探索一些物理学博士的典型替代职业,包括量化金融、航空航天、国防、科学咨询和相对较新的数据科学领域。通过阅读和与该领域个人的讨论,她意识到数据科学非常适合她。
虽然她有一些编程经验,并在几次实习中使用过神经网络,但这个领域对她来说非常陌生。幸运的是,她遇到了 Insight 数据科学项目,这是一个面向 STEM 领域的博士、希望过渡到数据科学的项目。鉴于她日程表上的空缺,她能够得到导师的同意参加。

作为一名物理学家,她有很好的 Python 技能,但作为一名程序员,她的技能很差。她对数据库或 SQL 一无所知,对机器学习和成为一名有效的数据科学家所需的许多其他概念的了解也很有限。
然而,由于她在物理方面的训练,她不那么害怕困难的问题,并且已经培养了自学困难概念的技能。这种心态在德语短语“ Sitzfleisch 中被她捕捉到了,大致翻译过来就是坐在椅子上足够长时间来解决难题的能力。
在项目接近尾声时,全班有机会进行路演,在两分钟的演示时间内展示他们在不同公司的个人项目。Becky 的项目是在 LocationMap 上,这是一个使用 Python 和 MySQL 识别和绘制电影拍摄地点的网络应用程序。她已经是一个巨大的电影迷,因此,该项目是一个她真正感兴趣的生活。在她对电影的个人兴趣和初露头角的数据科学技能之间,在网飞的一个角色看起来像是梦寐以求的角色。
在他们的办公室推销了她的项目后,她获得了一次面试机会,并得到了三个月后开始工作的机会。这给了她足够的时间回到研究生院并完成论文(使用校准和测试数据,而不是飞行数据),使她能够获得博士学位并开始她的新角色。
回想她在 Insight 的时光,Becky 很感激它给她提供的经历。她从中获得的最有价值的东西是了解她不知道的事情。具体来说,行业是什么样子,人们如何看待业务指标,以及在数据科学角色中取得成功所需的术语和技能。这也肯定了她投身科技行业,离开学术界的决定。
网飞数据科学

刚刚从研究生院毕业,Becky 就加入了网飞位于洛杉矶办公室的数据科学和工程团队。当时公司的大部分人员都在洛杉矶工作,所以洛杉矶办事处很小。她的团队由 12 人组成,洛杉矶办公室总共大约有 250 人。
她发现这种转变很容易,这在一定程度上要归功于团队中那些了不起的、支持她的人,他们帮助她成长起来。她从来没有因为不知道某件事而感到难过,在最初的几个月里,团队积极地帮助她寻找项目。
在早期,她的大量工作围绕着对网飞原创电视连续剧的分析,该电视剧当时由三个节目组成。这些项目的工作与她在学术界的工作形成了鲜明的对比,在学术界很难看到她的工作的直接影响。
她很快发现自己担任了一个高级职位,目前她在分析和预测建模之间分配时间。她所做的工作类型经常变化,在任何给定的时间,她可能从事三个中型和一个小型项目。在这些项目中,50%可能会在本季度内获得回报,而另一半可能会有超出本季度的可交付成果。虽然她所做的工作类型通常是与共享代码库协作,但她的大多数项目都是独立的。
她最喜欢网飞文化的一个方面是关注环境,而不是控制。虽然她的经理和利益相关者可以向她介绍公司的情况,以及他们认为什么可能是重要的,但由她来决定最重要的事情是什么。这通常意味着优先考虑有可能产生最大商业影响的项目。
给别人的建议
当被问及五年前她会给自己什么建议时,她给出了以下建议:
- 不要忽视软件工程在数据科学中的重要性。如果你认为你正在生成的代码可以存活几个季度,那就投资构建健壮的代码。这意味着添加注释,使代码可读,便于将来维护。
- 虽然机器学习可能是她工作的一个重要方面,但她作为数据科学家的角色既是技术角色,也是商业角色。因此,能够以同情和谦逊的态度倾听利益相关者的意见,对你的成功至关重要。一个重要的重构是,你的工作应该是帮助那些已经是专家的人更好地完成他们的工作,而不是告诉他们如何做他们的工作。
- 继续学习!每个人都是不同的,但她发现最好的学习方法之一是买课本,每天早上花 45 分钟来复习。能够浏览教科书的基本原理并将其应用到工作中是巩固概念的一个很好的方法。(她推荐统计学习的要素和做贝叶斯数据分析)
- 当与利益相关者交流时,要学会如何使用隐喻和类比来使你的观点易于理解。当试图向非技术利益相关者解释为什么某些东西可能或可能不工作时,这种策略得到了巨大的回报。
《数据思维》是一个系列,介绍处理数据的专业人士。在这个系列中,你将了解他们的日常故事,以及给别人的建议。
之前的采访包括来自红牛、开门和 Snapchat 的数据科学家。
有一个难以置信的简单方法可以让你的 OBIEE 数据变得漂亮
BI 连接器的绝对初学者指南:如何毫不费力地将 OBIEE 连接到 Tableau 2019.2

Image by Evren Ozdemir from Pixabay
您是否对以您想要的方式可视化 OBIEE 数据有多难感到沮丧?你希望你可以轻松(安全)地连接到 Tableau 吗?
你可以!
BI 连接器允许您在 Tableau、PowerBI 和 Qlik 中安全地访问和使用您的 OBIEE 和 Taleo 数据。
(完全公开:我最近一直在用 BI Connector 做一些工作,肯定已经从他们那里得到了报酬,并且已经获得了他们所提供的东西。也就是说,我现在知道那边有几个人,我可以担保他们是一个多么棒的团队!)
将数据可视化!
如果您想要使用可视化来交流分析的结果,您可能想要使用 Tableau 这样的现代且易于使用的工具。但是没有简单的方法来处理你的 OBIEE 数据!
虽然 OBIEE 在数据和业务分析方面令人惊叹,但在简单直观的可视化方面,它不如 Tableau 有趣。
只是玩起来没那么爽!(尤其是如果你不是 OBIEE 专家的话……)
你不会相信有多少公司在出口、进口、SQL 暂存区等方面浪费了数万美元的时间和精力,因为他们不知道有一种工具可以让他们只需点击几下鼠标,就可以安全地将 OBIEE 数据直接连接到 Tableau。
手动将 OBIEE 数据连接到 Tableau 通常意味着浪费大量时间来创建、导出 OBIEE 数据,以及将 OBIEE 数据导入 Tableau 之类的东西。有了 BI 连接器,就简单了。它几乎不需要任何时间!您使用 OBIEE 凭证登录,这意味着您可以使用现有的业务逻辑。你不需要对 OBIEE 做任何改动。
你节省了时间和金钱。
GIF via GIPHY
BI Connector 非常适合从事机器学习、数据分析、数据可视化、商业分析、数据科学和预测分析的任何人。它使初学者的生活变得容易,并给高级用户一些他们可以从中获得乐趣的东西。
它简单、安全、高效。安装时间不到五分钟!
您可以对主题区域和报告运行直接的基于查询的实时连接,并立即使用您的结果来创建华丽、响应迅速和直观的可视化效果。您将避免常见错误,同时做出更快的决策。它为你节省了大量的时间和精力!

Photo by nickgesell via Pixabay
奥比耶

Image labeled for reuse via Wikimedia Commons
如果你对数据很认真,很有可能你已经在使用 OBIEE 。对于数据报告和智能来说是很神奇的。它可以容纳大量的数据。它还能非常好地处理复杂的模式。此外,Oracle 还开发了 OBIEE 中可用的预定义 BI 解决方案。它提供交互式仪表盘和报告、可操作的情报、主动检测和警报、Microsoft Office 集成等等。
也就是说,OBIEE 中的可视化选项没有 Tableau 中的多。现有的那些肯定不是用户友好的。OBIEE 使用其他工具的能力也有限,如果您想使用它们,通常需要购买额外的许可证。如果你想正确使用 OBIEE,你需要接受一些严肃的教育。
将 OBIEE 数据连接到 Tableau 也不容易。您可以导出数据并将其导入 Tableau,这将导致重复的数据、重复的逻辑和潜在的不一致的结果。您可以创建和维护一个 SQL 数据库暂存环境。但是这需要时间、金钱和资源。
同样值得注意的是,这两种选择都将你的数据置于未经授权访问和其他安全问题的风险之中。
画面

Image labeled for reuse via Wikipedia
另一方面,Tableau 提供了华丽的、易于创建的可视化效果。它直观且用户友好,具有简单的拖放式响应图表和仪表盘、一键式公式、过滤器等等。(2019.2 还有一些好玩的新功能,包括矢量地图!你可以在这里查看新功能。)但是,虽然 Tableau 可以处理大量数据,但它无法管理 OBIEE 可以轻松处理的巨大数据量。当您有超过 25 个表或超过 16 个 get 列时,就很难使用了。
而 Tableau 里的一切都需要从头开发。
这两种工具不能互相替代。他们真的一起做了惊人的工作!但是让他们一起工作很有挑战性。
这就是 BI 连接器的用武之地!

双连接器
BI 连接器是连接 OBIEE 和 Tableau 的简单而安全的方式。通过 BI Connector,您可以使用 OBIEE 数据在 Tableau 中立即创建可视化效果。BI Connector 使用 OBIEE 安全模型,因此您的数据受到保护。
它自动将你的 OBIEE 数据移入 Tableau,并保证你的数据安全。

Image by analogicus from Pixabay
BI Connector 位于 OBIEE 层之上,让您可以毫不费力地集成您最喜欢的数据可视化工具,无论是 Tableau、Qlik 还是 Power BI。它简单、直观,并且弥合了技术之间的差距。这为您节省了大量的时间和金钱!
BI Connector 让您可以即插即用地访问已经在 OBIEE 中构建的内容。您可以直接从数据仓库中运行您的结果。另外,它既有趣又安全!您将构建 Tableau 可视化,同时使用 OBIEE 安全性保护您的数据。
如何使用 BI 连接器连接 OBIEE 和 Tableau
BI 连接器非常容易安装。它需要不到五分钟。你可以在这里找到有用的分步指南和真正有用的视频,如果你遇到任何问题,它们将带你完成整个过程。
第一步:下载
首先,你需要去 BI 连接器网站下载 BI 连接器。你可以点击“免费试用”按钮,下拉菜单“可视化 OBIEE 数据”并点击“Tableau”

输入你的信息,点击按钮“试用 BI Connector for Tableau ”,你的下载就开始了。不需要信用卡或承诺。你有 30 天的时间来尝试,没有任何风险。

双击“bi connector-Desktop-Edition-x64-tableau . exe”文件将其解压缩。指定位置,然后开始安装。你可以点击“安装”按钮,然后按照提示进行操作。



在安装过程结束时,您会看到一个弹出窗口,让您知道您的安装已经完成。确保选中“启动 ODBC 管理器”旁边的框,然后单击“完成”

步骤 2: ODBC 管理员和许可证激活
接下来,您需要创建一个新的数据源。转到 ODBC 数据库管理器并单击“添加”将弹出一个对话框,您可以在其中激活 30 天的试用许可证。输入您的个人信息,然后保留试用许可证号码(它会自动显示)或将其更改为您购买的许可证的密钥,然后单击“激活”这将使您返回到现在可以创建新数据源的窗口。

点击“添加”按钮

输入数据源名称(OBIEE Connect here)和您的服务器名称、端口、用户 ID 和密码。这是您用于 Oracle BI 服务器的信息。
如果你使用类似“http://obieefunstuff . websitename . com:9704/analytics”的内容登录 OBIEE,那么你的服务器名称将是“http://obieefunstuff . websitename . com”,你的端口将是 9704。您的用户 ID 和密码就是您的 OBIEE 用户 ID 和密码。最好看一下官方分步指南了解更多信息。它将带您了解一些常见的场景,包括如果在 URL 中看不到您的端口号该怎么办。

现在点击“测试连接”以确保一切正常。

步骤 3:配置主题区域或报告的访问权限
在下一个窗口中,您将看到两个小单选按钮,允许您选择“主题区域”或“报告”如果您想要连接到您在 OBIEE 目录中预先构建的分析,选择“报告如果你有兴趣从你的主题领域建立一个新的分析,选择“主题领域
做出选择后,点击“保存”如果您想更改或更新您的信息,您可以随时再次进入此屏幕。

步骤 4:配置 Tableau
现在是时候去 Tableau 了!启动 Tableau,然后单击“更多服务器”,然后转到“其他数据库(ODBC)”。现在,您可以选择您创建的数据源,并单击“连接”确保您的服务器名称、端口和用户 ID 与您之前使用的完全相同,并在这里输入相同的密码。测试您的连接并点击“确定”您将在“其他数据库(ODBC)”屏幕上再次单击“确定”。


你已经准备好了!开始可视化您的数据!
继续从下拉菜单中选择您的数据库,然后单击“OK”

完成的
就是这样!你都准备好了!

你可以像平常一样拖放你的信息!创建过滤器,添加颜色,标签,工具提示,和其他一切你通常用 Tableau 做的事情。你的 OBIEE 数据唾手可得,你可以完全按照你想要的方式使用它。




您甚至可以将 OIBEE 数据与 Tableau 中的其他数据文件混合在一起,就像您通常所做的那样。从同样的过程开始,然后点击左上角的“添加”或者下拉你的数据菜单到“新数据源”例如,如果您有 CSV 文件中的数据,请选择“文本文件”。这将打开一个窗口,您可以在其中选择您的文件。
现在,您可以开始处理您的附加数据了!


真的就那么简单。你只需几分钟就能把 OBIEE 的力量与 Tableau 的轻松和吸引力结合起来。
你还在等什么?今天就试试 BI 连接器!
感谢阅读!和往常一样,如果你用这些信息做了什么很酷的事情,请在下面的评论中让每个人都知道,或者随时在 LinkedIn @annebonnerdata 上联系。
当然了,
将兴趣点连接和插值到道路网络
基于最近边的可伸缩插值
This article discusses the process of handling the integration of point geometries and a geospatial network. You may find a demonstration Jupyter Notebook and the script of the function [here](https://github.com/ywnch/toolbox).

简介:使用地理空间网络
当我们使用网络分析时,我们的分析对象(表示为节点或顶点)应该通过一个图连接起来,并定义邻接和边权重。然而,我们经常从不同的来源收集数据,并需要手动集成它们。这个过程实际上比我想象的要复杂一些。
让我们看看这个例子。在分析城市中的兴趣点(poi)的场景中,我们可能从 OpenStreetMap 中检索到一个步行网络,从 Yelp 中查询到一组餐馆。一种常见的方法是将 poi 捕捉到网络上最近的顶点(结点、交汇点、道路弯道)。虽然这是一种非常简单且高效的实施方式,但此方法存在一些问题,包括(1)忽略 POI 和网络之间的边,以及(2)根据附近折点的可用性得出不一致的捕捉结果。
换句话说,尽管基于捕捉结果的 POI 到网络的步行距离可能以某种方式归因于作为访问阻抗的顶点(例如,距离或步行时间),但我们仍然会丢失一些地理空间信息(例如,POI 的实际位置和访问它的步行路线)。此外,如果附近唯一的路段是一条长且直的路段,且折点很少,则捕捉距离将不是一个现实的距离。虽然通常在 GPS 轨迹恢复中采用的地图匹配技术解决了类似的问题,但它通常返回校准的轨迹本身,而不是修改实际网络。
在参考资料中,您可能会发现社区提出的一些问题,然而,我发现的解决方案并不十分全面,也不是现成的地理处理工具。对于我的用例来说,最好是创建一个 Python 函数,当我想要将一组点合并到网络中时,这个函数会很方便。
为了记录在案,我正在使用由 UrbanSim 开发的 pandana 软件包来模拟城市无障碍设施。你可以在这篇文章中看到这是如何做到的。然而,至少有两个原因,我想手工制作自己的网络。首先,我希望根据建筑物而不是交叉点来计算我的输出;因此,将建筑物(或我后来想做的任何东西)连接到网络上是必须的。其次,pandana在处理兴趣点捕捉时的行为与我们之前讨论的完全相同:
poi 连接到 Pandana 网络中的最近节点,该网络假定变量位置和最近网络节点位置之间没有阻抗。
想法是:按顺序更新节点和边

The idea is simply locating the nearest projected point of the POI on the network and connect them.
因此,为了解决问题,让我们将这个过程分成几个步骤:
- 准备两个代表网络的表格(
geopandas.GeoDataFrame):一个用于节点,一个用于边,下面称为“内部”节点和边。在我的情况下,这是通过 osmnx 包下载的。 - 准备另一个表,其中包含我们希望在步骤 1 中合并到网络中的点几何。它们将被称为“外部节点”。
- 用所有外部节点更新内部节点表。这应该像表格连接一样简单,因为困难的部分来自边缘。
- 找到所有外部节点的预计访问点,并将其更新到表中。现在,与捕捉不同,我们需要为每个外部节点生成一个“投影访问点(PAP)”。这些 pap 是从网络到外部点的最近点。在现实中,这将是你离开大楼后“上路”的地方,并以最短的路线径直走向那条路。
- 必要时更新内部边。当我们生成 pap 时,它们可能会落在边上而不是顶点上(这基本上是常见的捕捉)。为了确保我们的图形正确识别这种连接,我们需要将原始边分成几段。例如,从 A-B 到 A-P-B。在表中,这将意味着用多个新记录替换原始记录,同时继承相同的道路属性(例如,道路名称、车道数量等。)除了长度。
- 创建和更新到外部节点的外部连接。实际上,这意味着建立从道路到建筑物的最短步行路径。
细节:R 树、最近邻和道路分割
尽管这些听起来很简单,但让我们讨论一下第 4 步到第 6 步中的关键问题。请记住,我最重要的目标是处理时间方面的可伸缩性。例如,新加坡有 185,000 栋建筑和 120,000 条步行路段,虽然这个过程可能是一次性的努力,但我们真的不希望它需要几个小时或几天才能完成。我写的函数的第一个天真的概念验证版本仍然在运行(在一天一夜之后),直到我完成了优化版本的制作、调试、测试、可视化和演示……嗯,你知道我的意思。
请注意,osmnx包已经修剪了从 OpenStreetMap 中检索到的网络中的冗余节点(参见本页上的第 3 节),这意味着那些只是道路中的一个弯道而不是连接两条以上边的交叉点的节点将被删除。这很重要,因为它使图形计算和我们的处理更有效,同时节省空间。但是,对于普遍采用的捕捉方法,我们可能希望尽可能多地保留这些节点,因为与稀疏节点相比,它们可能会使捕捉结果更好。在有许多节点的极端情况下,结果将与我们提出的方法相同。

The osmnx package removes redundant nodes in the network downloaded from OpenStreetMap. (Figures retrieved from osmnx introduction article.)
第四步:这一步可以进一步分解为两部分:找到最近的边,得到投影点。
a .找到最近的边:
为了进行投影,然后插值 PAP,我们需要确定我们实际上是在投影哪条边。虽然shapely.project和shapely.intersection可以应用于多线串,但它不会返回目标线串段,并且由于存在一些不合理的投影,结果不稳定。
因此,我们需要自己定位最近的边缘。更好的方法是将我们的边存储在适当的数据结构中。因为我们存储的是 LineString 而不是 Point,所以我选择使用 R 树而不是 k-d 树。这将每条边存储为一个矩形边界框,使得高效查询变得非常容易。虽然可以使用rtree.nearest,但这可能会产生不良结果。我的理解是,被测量的距离实际上是点和“矩形”之间的距离,而不是线串。一种更全面的方法(并且随后对于最近边的变化是可扩展的)是基于 R-tree 查询 k-nearest neighborhood(kNN ),然后计算到盒子内每个线串的实际距离。
b .获取投影点:
用最近的边,我们可以很容易地用line.interpolate(line.project(point))得到 PAP。
第 5 步:这一步也被分解如下:
a .确定要更新的边和节点:
由于每条边上可能有多张纸,我们希望一起处理它们,而不是重复处理。我们根据投影到的线(即最近的边)对所有 pap 进行分组。
b .通过精心处理将边缘分开
接下来,我们可以用shapely.ops.split函数轻松得到所有线段(用 P1 分裂 A—B,P2 返回 A—P1,P1—P2,P2—B)。但是,每当有人认为这是最容易的部分,这往往是错误可能会困扰你。所以我最终遇到了这里讨论的精度问题。简而言之,当我在线串上投影和插入一个点时,返回的 PAP 不而不是与线串相交,使其无法用于拆分线。为了解决这个问题,我们需要将线串与 PAP 对齐。这导致了一个非常微小的变化,我们无法识别,但确保交集是有效的,因此,启用了分割。
步骤 6:这一步只是通过指定“from”和“to”节点来生成新节点,并将它们更新到边表中。
包括这次更新在内的所有更新都只是简单的表操作,这里就不讨论了。请注意,我们使用GeoDataFrame来存储 shapefiles,并将 CRS 转换为epsg=3857来计算道路长度,单位为米。
讨论
您可能已经知道,地理空间网络和抽象图形结构(例如,社交网络)之间的一个最大区别是,前者的边也是具体的对象,并且具有形状。这就是为什么我们必须通过这些边缘导航并操纵它们。在社交网络中,实现这种节点到边的插值是没有意义的。
我相信这个功能肯定是有提升空间的。例如,使用cython优化代码可能会获得更好的性能。也可以尝试使用 PostGIS 来执行这些地理处理步骤,我还没有尝试过,因为我目前更喜欢用 Python 来完成。然而,考虑到新加坡数据的当前运行时间为 7 分钟(前面提到的建筑物到行人网络场景),目前应该是可以接受的。对于数百个 poi,这个过程应该在几秒钟内完成(如果不是一秒的话)。

In the real world, there can be various footways connecting a building (red star) to the road network. Our method only finds the nearest one (cyan node), which is unrealistic in some cases. Finding a way to generate a set of reasonable routes merely based on the network (w/o computer vision) can be challenging.
我也在考虑一个多方向的 kNN 连接扩展。为了说明这个用例,假设理想的公共汽车站在你的后门,但是通过这个方法产生的最近的人行道连接你的房子和前门的道路。这给了你一个不切实际的步行时间和路线计算,当你在你的后门乘公共汽车时。简单地建立 kNN 连接可能不能完全解决这个问题,因为它们可能都是前门的分段。因此,从点的不同方向获取合理的 kNN 边将是所期望的。

Ideally, we want to connect to one nearest neighbor (road segment) in different directions. The output on the right shows an example of how simply locating the 5 nearest road segments is not sufficient.
如果你有任何改进的想法,或者知道一个更简单的方法来完成这项任务,请留下评论与我们分享!
参考
这些是我从这个问题开始的帖子:
- 如何使用 Networkx 从一个点和一个线形状文件制作一个可传递图?
- 点图层和线图层之间的最近邻?
我还浏览了许多其他帖子,以下是其中一些:
- 在多线串上创建最近点,用于 shortest_path()
- 了解 RTree 空间索引的使用
- 使用 geopandas 通过最近点分割线
- 在大数据集上找到离每个点最近的线,可能使用 shapely 和 rtree
将 Python 连接到 Oracle、SQL Server、MySQL 和 PostgreSQL
数据库。在一定程度上经常被数据科学家忽略,至少被新的数据科学家忽略。我明白了,你有你的波士顿住房数据集存储在 CSV 文件中,为什么还要麻烦数据库呢?

Photo by panumas nikhomkhai from Pexels
在我职业生涯的大部分时间里,我也忽略了它们,但后来我需要为我的日常工作学习 SQL 和 PL/SQL。这是一项很好的技能——甚至可以说是你简历中的必备技能——但是客户想要用 PL/SQL 交付数据科学项目是完全的白痴行为。
但是 SQL 不等于数据库。我喜欢数据库的数据存储能力,主要是因为数据比 CSV 更安全——没有人可以双击并打开/更改 Excel 中的表格。
出于这个原因,我决定写这篇文章,因为出于某种原因,在网上找到 2010 年后发布的关于这个主题的东西并不像你想象的那样简单(从那以后发生了很多变化)。
Oracle 连接
为了连接到 Oracle 数据库,您当然需要在您的机器上安装该数据库。我的机器有 12c 版本,所以不能保证所有东西都能在旧版本或新版本上运行。为了测试一切,我解锁了著名的 HR 模式,并将密码设置为 hr 。
一旦你也这样做了,点击连接将显示你需要连接 Python 到你的 Oracle 数据库实例的每个细节:

在跳转到代码之前,你需要通过 pip 安装CX _ Oracle库,就像这样:
pip install cx_oracle
连接过程是我今天要讨论的四个过程中最长的,但是如果你逐行阅读的话,它真的很简单。我选择从著名的雇员表中获取前 10 行(注意 Oracle 如何使用 ROWNUM ,而不是 TOP ,或 LIMIT ):
import cx_Oracledsn = cx_Oracle.makedsn(
'localhost',
'1521',
service_name='orcl'
)
conn = cx_Oracle.connect(
user='hr',
password='hr',
dsn=dsn
)c = conn.cursor()
c.execute('SELECT * FROM employees WHERE ROWNUM <= 10')for row in c: print(row)
conn.close()
执行此代码块将向您的笔记本输出以下内容:

这并不难,是吗?下面的会更容易,或者至少写的更短,我保证!
SQL Server
除了 Oracle 数据库之外,微软的 SQL Server 也是典型工作环境中常见的数据库系统。要连接到它,您首先需要获取服务器名称(所选字符串):

然后,当您连接到数据库引擎时,您将需要找到数据库名称和表名称,这样您就知道应该在连接字符串中放入什么:

哦,差点忘了。还需要通过 pip 安装py odbc库:
pip install pyodbc
正如所承诺的,这段代码比连接到 Oracle 的代码要短一些:
import pyodbcconn = pyodbc.connect(
'Driver={SQL Server};'
'Server=DESKTOP-TLF7IMQ\SQLEXPRESS;'
'Database=retail;'
'Trusted_Connection=yes;'
)cursor = conn.cursor()
cursor.execute('SELECT TOP 5 * FROM dbo.table_transactions')for row in cursor: print(row)
conn.close()
执行此代码单元将输出以下内容:

接下来,MySQL。
关系型数据库
虽然在工作环境中可能不像前两个那么常用, MySQL 仍然非常受欢迎,如果你以前学过 web 开发,你可能会用到它。
默认情况下,MySQL 实例附带了这个 sakila 数据库,它看起来对 dvdrental 数据库非常熟悉。我要建立的连接是到 sakila 数据库,以及 演员 表。

我通过 Anaconda 安装的 Python 已经内置了必要的库,所以不需要额外安装。要从 Python 连接,我将使用 mysql 库:
import mysql.connectorconn = mysql.connector.connect(
host='localhost',
user='root',
passwd='1234'
)cursor = conn.cursor()
cursor.execute('SELECT * FROM sakila.actor LIMIT 5')for row in cursor: print(row)
conn.close()
一旦执行了这段代码,您将得到如下输出:

搞定三个,还剩一个!
一种数据库系统
最后但同样重要的是 Postgres 数据库。要连接到它,您需要安装 psycopg2 库:
pip install psycopg2
奇怪的名字,我知道。很容易在导入时出现打印错误。
然而,连接过程相当简单。我的数据库实例包含前面提到的 dvdrental 数据库,所以我将连接到它,再一次连接到 actor 表:
import psycopg2conn = psycopg2.connect(
user='postgres',
password='1234',
host='127.0.0.1',
port='5432',
database='dvdrental'
)cursor = conn.cursor()
cursor.execute('SELECT * FROM actor LIMIT 10')for row in cursor: print(row)
conn.close()
执行该代码块将输出以下内容:

结论
这篇文章差不多到此结束。我看过 15 分钟以上的文章,只连接到一个数据库,涵盖了所有的基本细节。就我个人而言,我不认为这有什么意义,因为拜托,这只是一个数据库连接!有很多更重要的事情需要关注。
如果你是一名数据科学家,你将主要使用数据库来获取数据,所以这篇文章对你来说已经足够了。如果需要更高级的东西,去问你最好的朋友,谷歌。
感谢阅读…
喜欢这篇文章吗?成为 中等会员 继续无限制的学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)







浙公网安备 33010602011771号