TowardsDataScience 博客中文翻译 2019(四百五十三)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

分类特征的光谱编码

原文:https://towardsdatascience.com/spectral-encoding-of-categorical-features-b4faebdf4a?source=collection_archive---------19-----------------------

另一种实现实体嵌入的方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Kelly Sikkema on Unsplash

大约一年前,我在做一个回归模型,它有超过一百万个特征。不用说,训练超级慢,模型过度拟合了很多。在调查了这个问题之后,我意识到大多数特征是使用分类特征的 1-hot 编码创建的,其中一些具有数万个唯一值。

将分类特征映射到低维空间的问题并不新鲜。最近流行的处理方法之一是使用神经网络的实体嵌入层。然而,该方法假设使用神经网络。如果我们决定使用基于树的算法呢?在这种情况下,我们可以使用谱图理论方法来创建分类特征的低维嵌入。

这个想法来自于谱词嵌入、谱聚类和谱降维算法。如果您可以定义分类特征的不同值之间的相似性度量,我们可以使用谱分析方法来找到分类特征的低维表示。

根据相似性函数(或核函数),我们可以构建邻接矩阵,它是对称矩阵,其中 ij 元素是类别值 I 和 j 之间的核函数的值:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很重要的一点,我只需要一个核函数,不需要高维表示。这意味着 1-hot 编码步骤在这里是不必要的。同样,对于基于核的机器学习方法,分类变量编码步骤也不是必需的,因为重要的是两点之间的核函数,其可以使用单独的核函数来构建。

一旦邻接矩阵被构建,我们可以构建度矩阵:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里 δ 是克罗内克δ符号。拉普拉斯矩阵是两者之差:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

并且归一化拉普拉斯矩阵被定义为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

根据谱图理论,我们继续进行归一化拉普拉斯矩阵的特征分解。零特征值的数量对应于连通分量的数量。在我们的例子中,假设我们的分类特征有两组完全不同的值。这意味着,如果 I 和 j 属于不同的组,则核函数 K(i,j)为零。在这种情况下,我们将有两个归一化拉普拉斯矩阵的零特征值。

如果只有一个连通分量,我们将只有一个零特征值。通常情况下,它不提供信息,为了防止要素的多重共线性而被删除。然而,如果我们计划使用基于树的模型,我们可以保留它。

较低的特征值对应于“平滑的”特征向量(或模式),其更紧密地遵循相似性函数。我们希望只保留这些特征向量,丢弃具有较高特征值的特征向量,因为它们更可能代表噪声。在矩阵谱中寻找一个缺口并挑选缺口以下的特征值是非常常见的。所得的截短的特征向量可以被归一化,并表示分类特征值的嵌入。

作为一个例子,让我们考虑星期几。1-热编码假设每天都与其他任何一天相似(K(i,j)=1)。这不是一个可能的假设,因为我们知道一周中的日子是不同的。例如,酒吧的上座率在星期五和星期六达到高峰(至少在美国),因为第二天是周末。标签编码也是不正确的,因为它会使周一和周三之间的“距离”比周一和周二之间的距离高两倍。而且周日和周一的“距离”会高 6 倍,即使这几天是挨着的。顺便说一下,标签编码对应于核 K(i,j)= exp(γ| ij |)

我们将考虑一个例子,其中工作日彼此相似,但与周末有很大不同。

array([[ 0, 10,  9,  8,  5,  2,  1],
       [10,  0, 10,  9,  5,  2,  1],
       [ 9, 10,  0, 10,  8,  2,  1],
       [ 8,  9, 10,  0, 10,  2,  1],
       [ 5,  5,  8, 10,  0,  5,  3],
       [ 2,  2,  2,  2,  5,  0, 10],
       [ 1,  1,  1,  1,  3, 10,  0]])
array([[ 1\.        , -0.27788501, -0.24053512, -0.21380899, -0.14085904, -0.07049074, -0.040996  ],
       [-0.27788501,  1\.        , -0.25993762, -0.23394386, -0.13699916, -0.06855912, -0.03987261],
       [-0.24053512, -0.25993762,  1\.        , -0.25      , -0.21081851, -0.06593805, -0.03834825],
       [-0.21380899, -0.23394386, -0.25      ,  1\.        , -0.26352314, -0.06593805, -0.03834825],
       [-0.14085904, -0.13699916, -0.21081851, -0.26352314,  1\.        , -0.17376201, -0.12126781],
       [-0.07049074, -0.06855912, -0.06593805, -0.06593805, -0.17376201,  1\.        , -0.50572174],
       [-0.040996  , -0.03987261, -0.03834825, -0.03834825, -0.12126781, -0.50572174,  1\.        ]])
array([0\.        , 0.56794799, 1.50908645, 1.08959831, 1.3053149 , 1.25586378, 1.27218858])

注意,这里的特征值没有排序。让我们画出特征值,忽略无信息的零。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到第一个特征值和其他特征值之间有一个相当大的差距。如果这不能给出足够的模型性能,您可以包括第二个特征值,因为它和更高的特征值之间的差距也相当大。

让我们打印所有的特征向量:

array([[ 0.39180195,  0.22866879,  0.01917247, -0.45504284,  0.12372711, -0.41844908, -0.62957304],
       [ 0.40284079,  0.24416078,  0.01947223, -0.4281388 , -0.53910465, -0.01139734,  0.55105271],
       [ 0.41885391,  0.23795901, -0.0032909 , -0.00102155,  0.24759021,  0.82656956, -0.15299308],
       [ 0.41885391,  0.21778112, -0.01536901,  0.36430356,  0.56996731, -0.36551902,  0.43094387],
       [ 0.39735971, -0.02474713,  0.07869969,  0.66992782, -0.54148697, -0.08518483, -0.29331097],
       [ 0.3176117 , -0.61238751, -0.71702346, -0.09280736,  0.02933834,  0.00752668,  0.02123917],
       [ 0.27305934, -0.63907128,  0.69187421, -0.13963728,  0.11758088,  0.02521838,  0.06615712]])

看第二个特征向量。周末值的大小与工作日不同,星期五接近于零。这证明了星期五的过渡作用,因为它是一周中的一天,也是周末的开始。

如果我们要挑选两个最低的非零特征值,我们的分类特征编码将产生这些类别向量:

array([[ 0.22866879, -0.45504284],
       [ 0.24416078, -0.4281388 ],
       [ 0.23795901, -0.00102155],
       [ 0.21778112,  0.36430356],
       [-0.02474713,  0.66992782],
       [-0.61238751, -0.09280736],
       [-0.63907128, -0.13963728]])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在上面的图中,我们看到星期一和星期二,以及星期六和星期天聚集在一起,而星期三,星期四和星期五相距很远。

学习核函数

在前面的例子中,我们假设相似性函数是给定的。有时就是这种情况,可以根据业务规则来定义。然而,从数据中学习它是可能的。

计算内核的方法之一是使用 Kullback-Leibler 散度:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 D 是对称 KL 散度:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里 pi 是给定类别值 I 的数据的概率:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其思想是为分类变量的每个值估计数据分布(包括目标变量,但不包括分类变量)。如果两个值的分布相似,那么散度将很小,相似度值将很大。请注意,γ是一个超参数,必须进行调整

尝试这种方法将会使用白酒销售数据集。为了保持文件小,我删除了一些列并聚合了数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为我们关心销售,所以让我们使用来自销售列的信息对星期几进行编码。让我们首先检查直方图:

sns.distplot(liq.sales, kde=False);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们看到分布非常不均匀,所以让我们尝试使用销售记录列来代替

sns.distplot(np.log10(1+liq.sales), kde=False);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这样好多了。因此,我们将使用日志进行分发

liq["log_sales"] = np.log10(1+liq.sales)

这里我们将跟随这篇博客来计算库尔巴克-莱布勒散度。还要注意,因为星期天没有酒类销售,我们认为一周只有六天

array([[0.00000000e+00, 8.77075038e-02, 4.67563784e-02, 4.73455185e-02, 4.36580887e-02, 1.10008520e-01],
       [8.77075038e-02, 0.00000000e+00, 6.33458241e-03, 6.12091647e-03, 7.54387432e-03, 1.24807509e-03],
       [4.67563784e-02, 6.33458241e-03, 0.00000000e+00, 1.83170834e-06, 5.27510292e-05, 1.32091396e-02],
       [4.73455185e-02, 6.12091647e-03, 1.83170834e-06, 0.00000000e+00, 7.42423681e-05, 1.28996949e-02],
       [4.36580887e-02, 7.54387432e-03, 5.27510292e-05, 7.42423681e-05, 0.00000000e+00, 1.49325072e-02],
       [1.10008520e-01, 1.24807509e-03, 1.32091396e-02, 1.28996949e-02, 1.49325072e-02, 0.00000000e+00]])

正如我们已经提到的,必须调整超参数γ。在这里,我们只选择能够给出合理结果的值

gamma = 20
kernel = np.exp(-gamma * kl_matrix)
np.fill_diagonal(kernel, 0)
kernelarray([[0\.        , 0.17305426, 0.39253579, 0.38793776, 0.41762901, 0.11078428],
       [0.17305426, 0\.        , 0.88100529, 0.88477816, 0.85995305, 0.97534746],
       [0.39253579, 0.88100529, 0\.        , 0.99996337, 0.99894554, 0.76783317],
       [0.38793776, 0.88477816, 0.99996337, 0\.        , 0.99851625, 0.77259995],
       [0.41762901, 0.85995305, 0.99894554, 0.99851625, 0\.        , 0.74181889],
       [0.11078428, 0.97534746, 0.76783317, 0.77259995, 0.74181889, 0\.        ]])norm_lap = normalized_laplacian(kernel)
sz, sv = np.linalg.eig(norm_lap)
szarray([1.11022302e-16, 9.99583797e-01, 1.22897829e+00, 1.27538999e+00, 1.24864532e+00, 1.24740260e+00])

在[18]中:

sns.stripplot(data=sz[1:], jitter=False, );

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

忽略零特征值,我们可以看到第一个特征值和其余特征值之间有更大的差距,尽管值都在 1 到 1.3 之间的范围内。

最终,要使用的特征向量的数量是另一个超参数,应该在监督学习任务中进行优化。Category 字段是进行光谱分析的另一个候选字段,并且可能是更好的选择,因为它有更多的唯一值

len(liq.Category.unique())107
array([[0.00000000e+00, 1.01321384e-02, 2.38664557e-01, ..., 5.83930416e-02, 2.05621708e+01, 4.44786939e-01],
       [1.01321384e-02, 0.00000000e+00, 1.50225839e-01, ..., 1.17178087e-01, 2.24843754e+01, 5.89215704e-01],
       [2.38664557e-01, 1.50225839e-01, 0.00000000e+00, ..., 5.33952956e-01, 2.95549456e+01, 1.33572924e+00],
       ...,
       [5.83930416e-02, 1.17178087e-01, 5.33952956e-01, ..., 0.00000000e+00, 1.59700549e+01, 1.80637715e-01],
       [2.05621708e+01, 2.24843754e+01, 2.95549456e+01, ..., 1.59700549e+01, 0.00000000e+00, 8.58405693e+00],
       [4.44786939e-01, 5.89215704e-01, 1.33572924e+00, ..., 1.80637715e-01, 8.58405693e+00, 0.00000000e+00]])
plot_eigenvalues(100);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,许多特征值都集中在 1.1 标记附近。低于该聚类的特征值可以用于编码类别特征。请注意,该方法对超参数γ的选择非常敏感。为了说明,让我选择一个较高和较低的伽玛

plot_eigenvalues(7000);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

plot_eigenvalues(10)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论和下一步措施

我们提出了一种方法来编码的分类特征作为一个低维向量,保留了大部分的特征相似性信息。为此,我们对分类特征的值使用频谱分析的方法。为了找到核函数,我们可以使用试探法,或者使用各种方法来学习它,例如,使用以类别值为条件的数据分布的 kull back-lei bler 散度。为了选择特征向量的子集,我们使用了 gap 分析,但我们真正需要的是通过分析各种数据集以及分类和回归问题来验证这种方法。我们还需要将其与其他编码方法进行比较,例如,使用神经网络的实体嵌入。我们使用的核函数也可以包括关于类别频率的信息,这将帮助我们处理高信息,但低频率值。

更新 07/04/2019: 计算相似度函数更好的方法是使用 Wasserstein 距离,而不是对称的 kull back-lei bler 散度。更新后的代码可以在我的 github 库中找到。

谱图聚类和最优聚类数估计

原文:https://towardsdatascience.com/spectral-graph-clustering-and-optimal-number-of-clusters-estimation-32704189afbe?source=collection_archive---------1-----------------------

谱图聚类概述和特征间隙启发式算法的 python 实现

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这篇文章解释了谱图聚类算法的功能,然后看了一个名为自调优图聚类的变体。这种调整的优点在于提供了对最佳聚类数的估计,以及对数据点之间的相似性度量的估计。接下来,我们将基于输入数据的拉普拉斯算子的连续特征值之间的最大距离,提供对数据集中最优聚类数的特征间隙启发式计算的实现。

现在让我们从介绍一些基本的图论概念开始。

邻接矩阵(A)

给定一个具有 n 个顶点和 m 个节点的图,邻接矩阵是一个 n*n 的方阵,其性质为:

如果节点 I 和节点 j 之间有边,则 A[i][j] = 1,否则为 0

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为 A 是对称的,所以它的本征向量是实数且正交的(点积为 0)。

度矩阵

度矩阵是具有属性的 n*n 对角矩阵

d[i][i] =节点 I 中相邻边的数目或节点 I 的度

d[i][j] = 0

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拉普拉斯矩阵

*拉普拉斯矩阵是 nn 矩阵,定义为: L = D -A

特征值是正实数**,而特征向量是实数且正交的(两个向量的点积为 0)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

电导

相对于组的密度(指向簇外的边的数量除以簇中节点的度数之和),衡量组与网络其余部分的连通性。电导越低,簇越好。

用 x 计算 A 的特征值和特征向量(用节点的值计算 n 维向量):A * x =λ x*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表示图 G 的矩阵的谱是由相应特征值λI 排序的图的一组特征向量 xi

既然我们已经介绍了图论中最重要的构件,我们就可以总结谱聚类的步骤了:

  1. 计算输入图 G 的拉普拉斯矩阵 L
  2. 计算特征值(λ)和特征向量(x ),使得

L * x =λ x*

3.选择对应于最大特征值的 n 个特征向量,并将输入空间重新定义为 n 维子空间

4.使用各种聚类算法,例如 k-means,在这个子空间中寻找聚类

也可以使用相似矩阵来代替上面定义的邻接矩阵,该矩阵确定我们的空间中的 2 个点有多接近或相似。按照 sklearn 实施中的定义:

similarity = np.exp(-beta * distance / distance.std())

演示亲和力矩阵创建的一个很好的资源是这个 youtube 视频。

谱聚类和相似性传播都已经在 python 中实现。这个木星笔记本展示了它们的用法的快速演示。

clustering = SpectralClustering(n_clusters=nb_clusters, assign_labels="discretize", random_state=0).fit(X)
y_pred = clustering.labels_
plt.title(f'Spectral clustering results ')
plt.scatter(X[:, 0], X[:, 1], s=50, c = y_pred);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

众所周知,谱聚类是一种性能良好的技术,特别是在非高斯聚类的情况下,在这种情况下,最常见的聚类算法(如 K-Means)不能给出良好的结果。然而,需要给定预期的聚类数和相似性阈值的参数。

自调谐光谱聚类

自调整谱聚类背后的思想是确定聚类的最佳数量以及在亲和矩阵的计算中使用的相似性度量** σi 。**

如本文所述,亲和矩阵定义如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 d(si,sj)是某个距离函数,通常只是向量 si 和 sj 之间的欧几里德距离。σ是比例参数,是点之间相似性的度量。通常是手动选择的。也可以通过使用不同的值多次运行聚类并选择产生最小失真聚类的值来自动设置。本文建议为每个数据点 si 计算局部缩放参数σi,而不是单个缩放参数。该论文提出分析每个点 si 的邻域,从而定义: σi = d(si,sK) 其中 sK 是点 si 的第 K 个邻域。下图说明了这一点,取自原始论文,K=7。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具有局部缩放的亲和度矩阵可以如下实现:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

估计群集数量的第二种方法是分析特征值(L 的最大特征值将是数量为 1 的重复特征值,其重数等于组 C 的数量。这意味着可以通过计算等于 1 的特征值的数量来估计 C)。

如论文所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以对特征向量进行另一种类型的分析,但这不在本文的讨论范围之内。

寻找最佳聚类数的特征间隙启发式算法

本文是一篇关于谱聚类的教程——Ulrike von lux burg提出了一种基于微扰理论谱图论的方法来计算最佳聚类数。特征间隙启发法表明,聚类数 k 通常由使特征间隙(连续特征值之间的差)最大化的 k 值给出。这个特征间隙越大,理想情况的特征向量越接近,因此谱聚类工作得越好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这篇文章的代码库可以在 Github 上找到。

资源

  1. https://www.youtube.com/watch?v=P-LEH-AFovE
  2. https://papers . nips . cc/paper/2619-self-tuning-spectral-clustering . pdf
  3. http://www . kyb . mpg . de/file admin/user _ upload/files/publications/attachments/luxburg 07 _ tutorial _ 4488% 5b 0% 5d . pdf

逐步解释和实现光谱图卷积

原文:https://towardsdatascience.com/spectral-graph-convolution-explained-and-implemented-step-by-step-2e495b57f801?source=collection_archive---------2-----------------------

作为“计算机视觉图形神经网络教程”的一部分

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Fourier basis (DFT matrix) on the left, in which each column or row is a basis vector, reshaped to 28×28 (on the right), i.e. 20 basis vectors are shown on the right. The Fourier basis is used to compute spectral convolution is signal processing. In graphs, the Laplacian basis is used described in this post.

首先,让我们回忆一下什么是图。图 G 是由有向/无向连接的一组节点(顶点)。在这篇文章中,我将假设一个无向图 GN 个节点。该图中的每个节点都有一个 C 维特征向量,所有节点的特征都表示为一个 N × C 维矩阵 X⁽ˡ⁾.图的 表示为一个 N × N 矩阵 a,其中条目 A ᵢⱼ 表示节点 i 是否连接(T30 邻接)到节点 j 。这个矩阵被称为邻接矩阵

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Two undirected graphs with N=5 and N=6 nodes. The order of nodes is arbitrary.

图的谱分析(参见课堂讲稿这里和早期的工作这里)已经对图聚类、社区发现和其他主要是无监督的学习任务有用。在这篇文章中,我主要描述了布鲁纳等人,2014,ICLR 2014 的工作,他们将谱分析与卷积神经网络(ConvNets)相结合,产生了谱图卷积网络,它可以以监督的方式进行训练,例如用于图分类任务。

尽管光谱图形卷积目前与空间图形卷积方法相比使用较少,但了解光谱卷积的工作原理仍然有助于理解和避免其他方法的潜在问题。此外,在结论中,我提到了一些最近令人兴奋的工作,使谱图卷积更具竞争力。

1.拉普拉斯图和一点物理知识

虽然“频谱”听起来可能很复杂,但对于我们的目的来说,理解它仅仅意味着将信号/音频/图像/图形分解为简单元素(小波、graphlets)的组合(通常是总和)就足够了。为了使这种分解具有一些好的特性,这些简单元素通常是正交*,即相互线性独立,因此形成了。*

当我们谈论信号/图像处理中的“频谱”时,我们指的是傅立叶变换,它为我们提供了不同频率的基本正弦和余弦波的特定 ( DFT 矩阵,例如 Python 中的scipy.linalg.dft),因此我们可以将信号/图像表示为这些波的总和。但是当我们谈论图和图神经网络(GNNs)时,“谱”意味着图拉普拉斯 本征分解L .你可以把图拉普拉斯 L 想象成一个以特殊方式归一化的邻接矩阵 A ,而本征分解是一种寻找那些基本正交分量的方法

直观地说,拉普拉斯图显示了如果我们在节点 i 中放置一些“势”,如何平滑地“能量”将在图中扩散。拉普拉斯在数学和物理中的一个典型用例是解决信号(波)如何在动态系统中传播。当邻居之间的值没有突然变化时,扩散是平滑的,如下图所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Diffusion of some signal (for example, it can be heat) in a regular grid graph computed based on the graph Laplacian (source). Basically, the only things required to compute these dynamics are the Laplacian and initial values in nodes (pixels), i.e. red and yellow pixels corresponding to high intensity (of heat).

在这篇文章的其余部分,我将假设“对称归一化拉普拉斯算子”,它经常用于图形神经网络,因为它是归一化的,以便当你堆叠许多图形层时,节点特征以更平滑的方式传播,而不会出现特征值或梯度的爆炸或消失。它仅基于图的邻接矩阵进行计算,这可以用几行 Python 代码完成,如下所示:

**# Computing the graph Laplacian
# A is an adjacency matrix of some graph *G*** import numpy as npN = A.shape[0] **# number of nodes in a graph**
D = np.sum(A, 0) **# node degrees**
D_hat = np.diag((D + 1e-5)**(-0.5)) **# normalized node degrees**
L = np.identity(N) — np.dot(D_hat, A).dot(D_hat) **# Laplacian**

这里,我们假设 A 是对称的,即 A = A ᵀ,并且我们的图是无向图,否则节点度不是明确定义的,并且必须做出一些假设来计算拉普拉斯算子。邻接矩阵 A 的一个有趣的性质是 Aⁿ (矩阵乘积取 n 次)公开了节点之间的 n 跳连接(更多细节见此处)。

让我们生成三个图,并可视化它们的邻接矩阵和拉普拉斯算子以及它们的能力。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adjacency matrices, Laplacians and their powers for a random graph (left), “star graph” (middle) and “path graph” (right). I normalize A² such that the sum in each row equals 1 to have a probabilistic interpretation of 2-hop connections. Notice that Laplacians and their powers are symmetric matrices, which makes eigen-decomposition easier as well as facilitates feature propagation in a deep graph network.

例如,想象中间上方的星图是由金属制成的,这样它可以很好地传热。然后,如果我们开始加热节点 0(深蓝色),这种热量将以拉普拉斯定义的方式传播到其他节点。在所有边都相等的星形图的特殊情况下,热量将均匀地传播到所有其他节点,这对于其他图来说是不正确的,因为它们的结构。

在计算机视觉和机器学习的背景下,图形拉普拉斯定义了如果我们堆叠几个图形神经层,节点特征将如何更新。与我的教程 的第一部分 类似,为了从计算机视觉的角度理解光谱图卷积,我将使用 MNIST 数据集,它在 28×28 的规则网格图上定义图像。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MNIST image defining features X (left), adjacency matrix A (middle) and the Laplacian (right) of a regular 28×28 grid. The reason that the graph Laplacian looks like an identity matrix is that the graph has a relatively large number of nodes (784), so that after normalization values outside the diagonal become much smaller than 1.

2.盘旋

在信号处理中,可以证明空间域中的卷积是频域中的乘法(又称为卷积定理)。同样的定理也适用于图形。在信号处理中,为了将信号变换到频域,我们使用离散傅里叶变换,它基本上是信号与特殊矩阵(基,DFT 矩阵)的矩阵乘法。这个基础假设了一个规则的网格,所以我们不能把它用于不规则的图形,这是一个典型的情况。而是用一个更一般的基,就是图拉普拉斯 L 的特征向量 V ,可以通过特征分解找到:l=vλvᵀ,其中λL. 的特征值

主成分分析 vs 拉普拉斯图的特征分解。**在实际计算谱图卷积时,只需使用与最小特征值对应的几个特征向量就足够了。乍一看,与计算机视觉中经常使用的主成分分析(PCA) 相比,这似乎是一种相反的策略,其中我们对最大特征值对应的特征向量更感兴趣。然而,这种差异仅仅是由于上面用于计算拉普拉斯算子的否定,因此使用 PCA 计算的特征值与图拉普拉斯算子的特征值成反比(形式分析见本文)。还要注意的是,PCA 应用于数据集的协方差矩阵,目的是提取最大的变化因素,即数据变化最大的维度,如特征面。这种变化通过特征值来测量,因此最小的特征值基本上对应于噪声或“伪”特征,这些特征在实践中被认为是无用的甚至是有害的。**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Eigenvalues (in a descending order) and corresponding eigenvectors for the MNIST dataset.

拉普拉斯图的特征分解应用于单个图,目的是提取节点的子图或集群(社区),并且特征值告诉我们许多关于图连通性的信息。我将在下面的例子中使用对应于 20 个最小特征值的特征向量,假设 20 远小于节点数 N(在 MNIST ) 的情况下 N =784)。为了找到下面左边的特征值和特征向量,我使用了一个 28×28 的规则图,而在右边,我遵循布鲁纳等人的实验,通过在 28×28 的规则网格上采样 400 个随机位置来构建一个不规则图(有关该实验的更多细节,请参见他们的论文)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Eigenvalues Λ (bottom) and eigenvectors V (top) of the graph Laplacian L for a regular 28×28 grid (left) and non-uniformly subsampled grid with 400 points according to experiments in Bruna et al., 2014, ICLR 2014 (right). Eigenvectors corresponding to the 20 smallest eigenvalues are shown. Eigenvectors are 784 dimensional on the left and 400 dimensional on the right, so V is 784×20 and 400×20 respectively. Each of the 20 eigenvectors on the left was reshaped to 28×28, whereas on the right to reshape a 400 dimensional eigenvector to 28×28, white pixels for missing nodes were added. So, each pixel in each eigenvector corresponds to a node or a missing node (in white on the right). These eigenvectors can be viewed as a basis in which we decompose our graph.

所以,给定图的拉普拉斯 L ,节点特征 X 和滤波器 W _spectral,在 Python 图上进行谱卷积看起来非常简单:

**# Spectral convolution on graphs
# X is an *N×1 matrix of 1-dimensional node features*** **# L** **is an** ***N******×N* graph Laplacian computed above
# W_spectral are** ***N******×******F weights (filters) that we want to train*** from scipy.sparse.linalg import eigsh **# assumes *L* to be symmetric***Λ**,V* = eigsh(L,k=20,which=’SM’) **#** **eigen-decomposition (i.e. find *Λ******,V)***
X_hat = V.T.dot(X) **# *20*****×*****1* node features in the "spectral" domain**
W_hat = V.T.dot(W_spectral)  **# 20×*F* filters in the** **"spectral" domain**
Y = V.dot(X_hat * W_hat)  **# *N******×******F* result of convolution**

形式上:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Spectral graph convolution, where ⊙ means element-wise multiplication.

其中,我们假设我们的节点特征 X⁽ˡ⁾ 是一维的,例如 m 像素,但是它可以扩展到 C 维的情况:我们将只需要对每个通道重复这个卷积,然后像在信号/图像卷积中一样对 C 求和。

公式(3)本质上与使用傅立叶变换的规则网格上的信号的频谱卷积相同,因此为机器学习产生了一些问题:

  • 可训练权重(滤波器) W_ 谱的维数取决于图中节点 N 的数量;
  • W_ 谱也取决于图结构中编码的特征向量 V.

这些问题阻碍了扩展到具有可变结构的大型图形的数据集。下文概述的进一步努力侧重于解决这些和其他问题。

3。谱域中的“平滑”

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Strawberry and banana smoothie (source: joyfoodsunshine.com). Smoothing in the spectral domain is a little bit different 😃.

布鲁纳等人是最早将谱图分析应用到学习卷积滤波器来解决图分类问题的人之一。使用上述公式(3)学习的滤波器作用于整个图,即它们具有全局支持。在计算机视觉环境中,这将与在 MNIST 上训练 28×28 像素大小的卷积滤波器相同,即滤波器具有与输入相同的大小(注意,我们仍将滑动滤波器,但在零填充图像上)。虽然对于 MNIST,我们实际上可以训练这样的过滤器,但常识建议避免这样做,因为这会使训练变得更加困难,因为参数数量可能会激增,并且难以训练可以捕捉不同图像之间共享的有用特征的大型过滤器。

实际上,我使用 PyTorch 和来自 GitHub 的代码成功地训练了这样一个模型。您应该使用mnist_fc.py --model conv来运行它。经过 100 个时期的训练后,过滤器看起来像数字的混合物:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Examples of filters with global support typically used in spectral convolution. In this case, these are 28×28 filters learned using a ConvNet with a single convolutional layer followed by ReLU, 7×7 MaxPooling and a fully-connected classification layer. To make it clear, the output of the convolutional layer is still 28×28 due to zero-padding. Surprisingly, this net achieves 96.7% on MNIST. This can be explained by the simplicity of the dataset.

重申一下,我们通常希望让过滤器更小,更局部(这和我下面要提到的不完全一样)。

为了更好地实现这一点,他们建议在光谱域中平滑滤光器,根据光谱理论,这使得滤光器在空间域中更接近 T2。其思想是,您可以将公式(3)中的滤波器 W_ 频谱表示为𝐾预定义函数(如样条函数)的和,并且我们学习这个和的 K 系数 α ,而不是学习 WN 值:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**We can approximate our N dimensional filter*W_*spectral as a finite sum of K functions f, such as splines shown below. So, instead of learning N values of W_spectral, we can learn K coefficients (alpha) of those functions; it becomes efficient when K << N.

虽然 fk 的维数确实取决于节点 N 的数量,但是这些函数是固定的,所以我们不学习它们。我们唯一知道的是系数 α ,因此 W_ 光谱不再依赖于 N 。整洁,对不对?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The spline basis used to smooth filters in the frequency domain, thereby making them more local. Splines and other polynomial functions are useful, because we can represent filters as their sums.

为了使我们在公式(4)中的近似合理,我们希望 K < < N 将可训练参数的数量从 N 减少到 K ,更重要的是,使其独立于 N ,这样我们的 GNN 可以消化任何大小的图。我们可以使用不同的碱基来进行这种“扩展”,这取决于我们需要哪些性质。例如,上面显示的三次样条函数被认为是非常平滑的函数(也就是说,你看不到节点,也就是分段样条多项式的各个部分相遇的地方)。我在的另一篇文章中讨论的切比雪夫多项式具有逼近函数之间的最小𝑙∞距离。傅立叶基是在变换后保留大部分信号能量的基。大多数碱基是正交的,因为有可以相互表达的项是多余的。

注意,滤波器 W_ 光谱仍然与输入一样大,但是它们的有效宽度很小。在 MNIST 图像的情况下,我们将有 28×28 个滤波器,其中只有一小部分值的绝对量值大于 0,并且所有这些值应该彼此靠近,即滤波器将是局部的并且实际上很小,如下所示(左起第二个):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

From left to right: (first) Input image. (second) Local filter with small effective width. Most values are very close to 0. (third) The result of spectral graph convolution of the MNIST image of digit 7 and the filter. (fourth) The result of spectral convolution using the Fourier transform. These results indicate that spectral graph convolution is quite limited if applied to images, perhaps, due to the weak spatial structure of the Laplacian basis compared to the Fourier basis.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Reconstruction of the MNIST image using the Fourier and graph Laplacian bases using only M components of V: X’=V VᵀX. We can see that the bases compress different patterns in images (orientated edges in the Fourier case and global patterns in the Laplacian case). This makes results of convolutions illustrated above very different.

总而言之,频谱域中的平滑允许布鲁纳等人学习更多的局部滤波器。具有这种过滤器的模型可以实现与没有平滑的模型(即,使用我们的公式(3))类似的结果,但是具有少得多的可训练参数,因为过滤器大小独立于输入图表大小,这对于将模型缩放到具有较大图表的数据集是重要的。然而,学习滤波器 W _spectral 仍然依赖于特征向量 V ,这使得将该模型应用于具有可变图结构的数据集具有挑战性。

结论

尽管最初的光谱图卷积方法存在缺点,但它已经得到了很多发展,并在一些应用中保持了相当有竞争力的方法,因为光谱滤波器可以更好地捕捉图中的全局复杂模式,而像 GCN ( Kipf & Welling,ICLR,2017 )这样的局部方法除非堆叠在深度网络中,否则无法实现。例如,2019 年的两篇论文,分别是廖等人关于“LanczosNet”和徐等人关于“图小波神经网络”,解决了谱图卷积的一些缺点,并在预测分子性质和节点分类方面显示出很好的结果。 Levie 等人的另一项有趣的工作,2018 关于“CayleyNets”在节点分类、矩阵完成(推荐系统)和社区检测方面表现强劲。因此,根据您的应用和基础设施,谱图卷积可能是一个不错的选择。

在我关于计算机视觉图形神经网络的教程的另一部分中,我解释了由 Defferrard 等人在 2016 年引入的切比雪夫谱图卷积,它仍然是一个非常强大的基线,具有一些很好的属性,并且易于实现,正如我使用 PyTorch 演示的那样。

鸣谢:本教程的很大一部分是我在 SRI International 实习期间在 穆罕默德·阿梅尔 ( 主页 )和我的博士导师格拉汉姆·泰勒( 主页 )的指导下编写的。我也感谢 卡洛琳·奥古斯塔 的有用反馈。

GithubLinkedInTwitter 上找我。我的主页

如果你想在你的论文中引用这篇博文,请使用:
@ misc{ Knyazev 2019 Tutorial,
title = {用于计算机视觉及超越的图形神经网络教程},
author={Knyazev,Boris and Taylor,Graham W and Amer,Mohamed R},
year={2019}
}

日语元音的声学和发音分析

原文:https://towardsdatascience.com/speech-analysis-of-vowels-for-japanese-language-fdee6ec1a4ba?source=collection_archive---------26-----------------------

数据分析

你知道日语元音图是如何绘制的吗?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Miguel Henriques on Unsplash

为了为英语日语构建一个语音识别器语音到文本转换器,通常你需要三个组件,它们是声学模型词典模型语言模型以取得成功。

  1. 声学模型将声音信号转换成音素。
  2. 词典模型将音素映射成单词。
  3. 一个语言模型限制了单词搜索。例如,通过使用前面的单词来预测后面的单词。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: Overview of a speech recognizer.

为了保持声学模型的质量,大多数研究人员通常会从说本族语的人那里收集语音信号。

如果你真的想收集你自己的声音信号作为声学建模的数据集呢?或者你想知道作为一个非母语人士,你的发音与母语人士有多大差异?

在接下来的部分中,我将向你展示如何通过使用你自己录制的声音作为输入来对日语元音进行声学和发音分析

步骤 1:数据集准备

首先,你需要准备数据集,用你自己的声音发一些单词的音,并用你的耳机录音。确保周围环境高度安静,以保持录音质量。

每个录音应该只包含一个词。对于每个元音,找出五个带有特定元音的单词,并用你自己的声音记录下来。

下表显示了选择用于演示的单词。带下划线的字符是将要进一步分析的元音。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: The words that I chose for the demonstration.

目前,日语的元音和辅音数量分别为 5 和 14。日语元音包括あ/ア(a)、い/イ(i)、う/ウ(u)、え/エ(e)和お/オ(o).

步骤 2:语音注释

在所有的录音都准备好之后,下一步将是用音素来注释声音。我要介绍的工具是 Praat ,这是一个用于语音学语音分析的免费计算机软件包。你可以在这里下载

语音注释需要执行的步骤包括:

  1. 下载并安装 Praat
  2. 将您所有的录音导入到 Praat 中。
  3. 为每个单词注释创建一个空白的文本网格文件。
  4. 保存所有文本网格文件,永久记录注释。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: The main user interface of Praat.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: The annotation of the Japanese word “こく”.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: The annotation of the Japanese word “まち”.

步骤 3:第一和第二共振峰搜索元音

人类语音产生包括两个阶段,分别是滤波器

,也就是声带,会振动产生复杂的周期性语音信号。复杂的周期性语音信号的频率被称为基频。

另一方面,作为声道的滤波器将借助于发音改变其形状,以修改复杂的周期性语音信号的谐波。修改后的语音信号的频率称为共振峰。

进入菜单栏,点击共振峰,根据需要选择第一共振峰第二共振峰,可以很容易地计算出第一和第二共振峰的平均值。然后,获得每个元音的平均第一和第二共振峰,并如下表所示记录结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: The procedure to get the first and second formant of vowels for each word.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table: The average of the first formant for each vowel.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table: The average of the second formant for each vowel.

第四步:绘制日语元音图

在收集了每个元音的第一和第二共振峰的平均读数后,绘制第一共振峰值对第二共振峰值的。前者显示了从我的语音记录中得到的结果,而后者显示了从以日语为母语的人提供的语音记录中得到的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: The result derived from my voice recordings.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure: The result derived from Japanese native speakers [Wikipedia].

步骤 5:声学和发音分析

通过比较上面绘制的两个图表:

  • 它表明从我自己的语音记录中得到的日语元音的位置与日语母语者相似。元音“い/イ(i”和“う/ウ(u”位于最顶端。元音“あ/ア(a”位于最底部。最左边是“い/イ(i”),最右边也是“お/オ(o”。
  • 然而,用于自己录音的元音“お/オ(o”和“う/ウ(u”之间的水平距离小于标准。元音“い/イ(i”和“う/ウ(u”之间的垂直距离没有标准中显示的那么大。

结论

作为一个母语不是日语的人,我发现我的元音“う/ウ(u”的发音与母语是日语的人稍有不同。我的其他元音发音看起来很好,和日本母语者很相似。我可以用自己的声音记录来建立自己的声音模型,而不是使用日语母语者的声音记录来建立声音模型。

参考

  1. 名词(noun 的缩写)Shmyrev,“语音识别的基本概念,”CMUSphinx 开源语音识别。
  2. C.史密斯,“国际音标协会手册:国际音标使用指南”。17.291–295, 1999.

基于卷积神经网络的语音情感识别

原文:https://towardsdatascience.com/speech-emotion-recognition-with-convolution-neural-network-1e6bb7130ce3?source=collection_archive---------4-----------------------

从录音中识别人类情感

对于数据科学家来说,识别人类情感一直是一项令人着迷的任务。最近,我正在进行一项实验性的语音情感识别(SER)项目,以探索它的潜力。我从 GitHub 中选择了最受欢迎的 SER 库作为我项目的主干。

在我们完成这个项目之前,最好了解一下语音情感识别的主要瓶颈。

主要障碍:

  • 情绪是主观的,人们会有不同的理解。很难定义情绪的概念。
  • 给录音添加注释是一项挑战。我们应该标记一个单词、句子还是整个对话?要定义多少种情绪才能识别?
  • 收集数据很复杂。有许多音频数据可以从电影或新闻中获得。然而,他们都有偏见,因为新闻报道必须是中立的,演员的情绪是模仿的。很难找到不带任何偏见的中性录音。
  • 标记数据需要很高的人力和时间成本。与在图像上绘制边界框不同,它需要训练有素的人员聆听整个音频记录,对其进行分析并给出注释。由于其主观性,注释结果必须由多个个人来评估。

项目描述:

用卷积神经网络从录音中识别情感。并且存储库所有者不提供任何论文参考。

数据描述:

这是两个最初在 RAVDESS 和 SAVEE 库中使用的数据集,我只在我的模型中采用了 RAVDESS。在 RAVDESS 中,有两种类型的数据:语音和歌曲。

数据集:瑞尔森情感语音和歌曲视听数据库(RAVDESS)

  • 12 位男演员和 12 位女演员分别录制了演讲和歌曲版本。
  • 18 号演员没有歌曲版本数据。
  • 歌曲版本数据中不包含情感DisgustNeutralSurprised

总类别:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是情绪等级分布条形图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

特征提取:

当我们执行语音识别任务时,MFCCs 是自 20 世纪 80 年代发明以来最先进的功能。

这个形状决定了发出什么声音。如果我们能够精确地确定形状,这将会给我们一个产生的音素的精确表示。声道的形状在短时功率谱的包络中表现出来,而 MFCCs 的工作就是精确地表示这个包络。—摘自: MFCC 教程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Waveform

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Spectrogram

我们将使用 MFCCs 作为我们的输入特性。如果你想彻底了解MFCC*,这里有一个很棒的* 教程 *给你。*加载音频数据并将其转换为 MFCCs 格式可以通过 Python 包librosa轻松完成。

默认模型架构:

作者用 Keras 开发了 CNN 模型,它由 7 层构成——6 个 Conv1D 层和一个密集层。

model = Sequential()
model.add(Conv1D(256, 5,padding='same', input_shape=(216,1))) #1
model.add(Activation('relu'))
model.add(Conv1D(128, 5,padding='same')) #2
model.add(Activation('relu'))
model.add(Dropout(0.1))
model.add(MaxPooling1D(pool_size=(8)))
model.add(Conv1D(128, 5,padding='same')) #3
model.add(Activation('relu'))
#model.add(Conv1D(128, 5,padding='same')) #4
#model.add(Activation('relu'))
#model.add(Conv1D(128, 5,padding='same')) #5
#model.add(Activation('relu'))
#model.add(Dropout(0.2))
model.add(Conv1D(128, 5,padding='same')) #6
model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(10)) #7
model.add(Activation('softmax'))
opt = keras.optimizers.rmsprop(lr=0.00001, decay=1e-6)

作者在最新笔记本(2018 年 9 月 18 日更新)中对第 4 层和第 5 层进行了评论,模型重量文件不适合所提供的网络,因此,我无法加载重量提供并复制其结果 72%的测试准确度。

该模型只是简单地用batch_size=16和 700 个历元进行训练,没有任何学习速率表等。

# Compile Model
model.compile(loss='categorical_crossentropy', optimizer=opt,metrics=['accuracy'])# Fit Model
cnnhistory=model.fit(x_traincnn, y_train, batch_size=16, epochs=700, validation_data=(x_testcnn, y_test))

其损失函数为categorical_crossentropy,评价指标为准确度。

我的实验

探索性数据分析:

在 RADVESS 数据集中,每个演员必须通过说和唱两句话来表现 8 种情绪,每种情绪两次。结果,除了中性、厌恶和惊讶之外,每个演员将为每种情绪归纳 4 个样本,因为没有这些情绪的歌唱数据。每个音频波大约 4 秒,第一秒和最后一秒很可能是无声的。

标准句子有:

1.孩子们在门边说话。
2。狗坐在门边。

观察:

在我选择了一个男演员和一个女演员的数据集并听了他们所有人的歌之后。我发现男性和女性表达情感的方式不同。以下是一些发现:

  • 男的 生气 就是单纯的加大音量。​
  • 男性的 快乐的悲伤的 显著特征是在音频中的静音期有笑有哭的语气。
  • 女性的 快乐的愤怒的悲伤的 都是音量增大。​
  • 女性的 厌恶 里面会加上呕吐的声音。

复制结果:

作者排除了类别neutraldisgustsurprised来对 RAVDESS 数据集进行 10 个类别的识别。

我试图用提供的模型复制他的结果,我可以达到的结果是

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然而,我发现在训练阶段使用的验证集与测试集相同的地方存在数据泄漏问题。因此,我重新做了数据分割部分,将两个男演员和两个女演员的数据隔离到测试集中,以确保它在训练阶段是不可见的。

  • 1-20 号演员用于分割比为 8:2 的Train / Valid场景。
  • 演员 21-24 被隔离以测试使用。
  • 列车集合形状:(1248,216,1)
  • 有效的集合形状:(312,216,1)
  • 测试集形状:(320,216,1)——(隔离)

我用新的数据分割设置重新训练了模型,结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基准:

从列车有效损失图可以看出,该模型甚至不能很好地收敛于 10 个目标类别。因此,我决定通过只识别男性情感来降低模型的复杂性。我将这两个角色分离为test set,其余的将是具有 8:2 分层混洗分割train/valid set,这确保了数据集中没有类别不平衡。之后,我分别训练了男性和女性数据,以探索基准。

男性数据集

  • 训练集=来自演员 1- 10 的 640 个样本。
  • 有效集=来自演员 1- 10 的 160 个样本。
  • 测试集=来自演员 11- 12 的 160 个样本。

男性基线

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

女性数据集

  • 训练集=来自女演员 1- 10 的 608 个样本。
  • 有效集=来自女演员 1- 10 的 152 个样本。
  • 测试集=来自女演员 11- 12 的 160 个样本。

女性基线

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如你所见,男性和女性模型的混淆矩阵是不同的。

  • 男性 : AngryHappy是男性模型中占主导地位的预测类,但它们不太可能混淆。​

  • : SadHappy是女模型中占优势的预测类,AngryHappy很有可能混淆。

参照 EDA 部分的观察,我怀疑女性AngryHappy很可能混淆的原因是因为她们的表达方式只是简单地增加了说话的音量。

除此之外,我想知道如果我进一步简化模型,将目标类减少到PositiveNeutralNegative或者甚至仅仅是PositiveNegative会怎么样。所以,我把情绪分为 2 类和 3 类。

2 类:

  • 正:happycalm
  • 负面:angryfearfulsad

3 类:

  • 阳性:happy
  • 中性:calmneutral
  • 负面:angryfearfulsad

(在 3 类中添加了中性来探索结果。)

在我做训练实验之前,我通过做 5 类识别用男性数据调整模型架构。

# Set the target class number
target_class = 5# Model 
model = Sequential()
model.add(Conv1D(256, 8, padding='same',input_shape=(X_train.shape[1],1))) #1
model.add(Activation('relu'))
model.add(Conv1D(256, 8, padding='same')) #2
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(MaxPooling1D(pool_size=(8)))
model.add(Conv1D(128, 8, padding='same')) #3
model.add(Activation('relu')) 
model.add(Conv1D(128, 8, padding='same')) #4
model.add(Activation('relu'))
model.add(Conv1D(128, 8, padding='same')) #5
model.add(Activation('relu'))
model.add(Conv1D(128, 8, padding='same')) #6
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(MaxPooling1D(pool_size=(8)))
model.add(Conv1D(64, 8, padding='same')) #7
model.add(Activation('relu'))
model.add(Conv1D(64, 8, padding='same')) #8
model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(target_class)) #9
model.add(Activation('softmax'))
opt = keras.optimizers.SGD(lr=0.0001, momentum=0.0, decay=0.0, nesterov=False)

我添加了 2 个 Conv1D 层,1 个 MaxPooling1D 层和 2 个 BarchNormalization 层,此外,我还将 dropout 值更改为 0.25。最后,我将优化器改为 SGD,学习率为 0.0001。

lr_reduce = ReduceLROnPlateau(monitor=’val_loss’, factor=0.9, patience=20, min_lr=0.000001)mcp_save = ModelCheckpoint(‘model/baseline_2class_np.h5’, save_best_only=True, monitor=’val_loss’, mode=’min’)cnnhistory=model.fit(x_traincnn, y_train, batch_size=16, epochs=700, validation_data=(x_testcnn, y_test), callbacks=[mcp_save, lr_reduce])

对于模型训练,我采用Reduce Learning On Plateau并只保存最小val_loss的最佳模型。这里是不同目标类设置的模型性能。

新型号性能

男 5 班

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

女 5 班

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

男二班

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

男 3 班

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

增大

在我调整了模型架构、优化器和学习率计划之后,我发现模型在训练期间仍然无法收敛。我认为这是数据大小的问题,因为我们只有 800 个样本用于训练有效集。因此,我决定探索音频增强方法。让我们来看看一些带有代码的增强方法。我简单地增加了所有数据集一次,使训练/有效集大小加倍。

男 5 级:

动态值变化

def dyn_change(data):
    """
    Random Value Change.
    """
    dyn_change = np.random.uniform(low=1.5,high=3)
    return (data * dyn_change)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

音高调谐

def pitch(data, sample_rate):
    """
    Pitch Tuning.
    """
    bins_per_octave = 12
    pitch_pm = 2
    pitch_change =  pitch_pm * 2*(np.random.uniform())   
    data = librosa.effects.pitch_shift(data.astype('float64'), 
                                      sample_rate, n_steps=pitch_change, 
                                      bins_per_octave=bins_per_octave)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

换挡

def shift(data):
    """
    Random Shifting.
    """
    s_range = int(np.random.uniform(low=-5, high = 5)*500)
    return np.roll(data, s_range)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

白噪声添加

def noise(data):
    """
    Adding White Noise.
    """
    # you can take any distribution from [https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.random.html](https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.random.html)
    noise_amp = 0.005*np.random.uniform()*np.amax(data)
    data = data.astype('float64') + noise_amp * np.random.normal(size=data.shape[0])
    return data

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,这种增强可以大大提高验证的准确性,一般来说是 70+%。特别是加入白噪声可以达到 87.19%的验证准确率,然而测试准确率测试 F1-score 分别下降超过 5%。然后,我想知道如果我混合不同的增强方法会带来一个好的结果。

混合多种方法

加噪+移位

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

男性 2 类数据的扩充测试

男性 2 类:

所有样本的加噪+移位

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

噪声添加+移位
仅针对正样本,因为 2 类集合不平衡(向负方向倾斜)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

音高调谐+噪声添加 针对所有样本

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

音调调谐+噪声添加 仅用于阳性样本

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

最后我只有时间用男性数据集做实验。我用分层混洗分割法重新分割数据,以确保没有数据不平衡或数据泄漏问题。我通过试验男性数据集来调整模型,因为我想在开始时简化模型。我还用不同的目标标签设置和增强方法测试了 by。我发现为不平衡的数据添加噪声移位可以帮助达到更好的结果。

钥匙拿走

  • 情绪是主观的,很难用符号表示出来。
  • 我们应该定义适合我们自己项目目标的情绪。
  • 不要总是相信 GitHub 的内容,即使它有很多明星。
  • 注意数据分割。
  • 探索性数据分析总是给我们很好的洞察力,当你处理音频数据时,你必须有耐心!
  • 决定模型的输入:一个句子,一段录音还是一段话语?
  • 缺少数据是服务识别成功的关键因素,然而,建立一个好的语音情感数据集是复杂且昂贵的。
  • 当你缺少数据时,简化你的模型。

进一步改进

  • 我只选择了前 3 秒作为输入数据,因为这将减少维度,原来的笔记本只使用了 2.5 秒。我想用这段完整的音频来做实验。
  • 对数据进行预处理,如裁剪无声语音、通过零填充归一化长度等。
  • 在这个主题上试验递归神经网络方法。

关于我

备注:GitHub 中很快会注意到一些与该主题相关的论文。

语音识别分析

原文:https://towardsdatascience.com/speech-recognition-analysis-f03ff9ce78e9?source=collection_archive---------11-----------------------

使用 Keras 建立语音识别模型。

从 Siri 到智能家居设备,语音识别广泛应用于我们的生活中。这个语音识别项目是利用 Kaggle 语音识别挑战数据集在 Tensorflow 上创建 Keras 模型,并对语音文件进行预测。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Adam Solomon on Unsplash

下面列出了 Kaggle 语音识别挑战数据集的链接:

[## TensorFlow 语音识别挑战

下载数千个项目的开放数据集+在一个平台上共享项目。探索热门话题,如政府…

www.kaggle.com](https://www.kaggle.com/c/tensorflow-speech-recognition-challenge/data)

数据接收和处理:

与图像识别类似,语音识别最重要的部分是将音频文件转换成 2X2 数组。

音频文件的采样速率和原始波形:

音频文件的采样速率表示每秒传送的音频样本数,以 Hz 为单位。下图显示了音频原始波形和“bed”音频文件的采样速率之间的关系:

train_audio_path = 'C:**\\**Users**\\**...**\\**train**\\**audio'
filename = '**\\**bed**\\**00f0204f_nohash_0.wav'
sample_rate, samples = wavfile.read(str(train_audio_path) + filename)
ipd.Audio(samples, rate=sample_rate)
print(sample_rate)
print(samples)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sample rate is the number of samples of audio carried per second, measured in Hz.

*# visualize this audio wave:*
fig = plt.figure(figsize=(14, 8))
plt.plot(samples)
plt.title('Raw wave of ' + filename)
plt.ylabel('Wave')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接下来,我将介绍音频文件的两个重要属性:

声谱图:

声谱图是声音的频谱-时间表示。频谱图的水平方向代表时间,垂直方向代表频率。(1)频谱图可用作一种可视化非平稳信号频率含量随时间变化的方式。

谱图(2)的公式如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我利用以下网站的代码来计算和可视化 log_spectrogram:

【https://www.tensorflow.org/api_guides/python/contrib.signalhttps://github . com/Tony 607/TF _ audio _ signal/blob/master/TF _ audio _ signal . ipynb

**def** log_spectrogram(file, label):
    sample_rate, samples = wavfile.read(str(train_audio_path) + '**\\**'+label+'**\\**' + file)
    signals = tf.cast(tf.reshape(samples, [1,-1 ]),tf.float32) 
    spectrogram = signal.stft(signals, frame_length=1024, frame_step= 512)
    magnitude_spectrograms = tf.abs(spectrogram)
    log_offset = 1e-6
    *#When compressing with a logarithm, it's a good idea to use a stabilizing offset* 
    *#to avoid high dynamic ranges caused by the singularity at zero.*
    log_magnitude_spectrograms = tf.log(magnitude_spectrograms + log_offset)
    **return** log_magnitude_spectrograms

然后绘制样本数据的 log _ spectrogram:bed。

log_spe_bed = log_spectrogram(train.file[0],train.label[0]).numpy()
array_bed = log_spe_bed.astype(np.float)[0]
fig = plt.figure(figsize=(14,8))
*#plt.ylabel("Freqs in Hz")*
plt.xlabel("Log_Spectrogram")
plt.imshow(np.swapaxes(array_bed,0,1).T)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

梅尔频率倒谱系数(MFCC):

梅尔倒谱系数(MFCC)是自动语音和说话人识别中广泛使用的特征。Mel 标度将纯音的感知频率或音高与其实际测量频率相关联。人类在低频时比高频时更善于辨别音调的微小变化。引入这一尺度使我们的特征与人类听到的更接近。(3)

**def** mfcc(file=train['file'].tolist(), label=train['label'].tolist()):
    sample_rate, samples = wavfile.read(str(train_audio_path) + '**\\**'+label+'**\\**' + file)
    **if** len(samples) < 16000:
        samples = np.pad(samples, (0,16000-len(samples)), 'linear_ramp')
    **else**:
        samples = samples[:16000]
    signals = tf.cast(tf.reshape(samples, [1,-1 ]),tf.float32) 
    spectrogram = signal.stft(signals, frame_length=1024, frame_step= 512)
    magnitude_spectrograms = tf.abs(spectrogram)
    num_spectrogram_bins = magnitude_spectrograms.shape[-1].value
    lower_edge_hertz, upper_edge_hertz, num_mel_bins = 80.0, 7600.0, 64
    linear_to_mel_weight_matrix = tf.contrib.signal.linear_to_mel_weight_matrix(num_mel_bins, num_spectrogram_bins, sample_rate, lower_edge_hertz,upper_edge_hertz)
    mel_spectrograms = tf.tensordot(magnitude_spectrograms, linear_to_mel_weight_matrix, 1)
*# Note: Shape inference for <a href="../../api_docs/python/tf/tensordot"><code>tf.tensordot</code></a> does not currently handle this case.*mel_spectrograms.set_shape(magnitude_spectrograms.shape[:-1].concatenate(linear_to_mel_weight_matrix.shape[-1:]))
    log_offset = 1e-6
    log_mel_spectrograms = tf.log(mel_spectrograms + log_offset)
    num_mfccs = 13
*# Keep the first `num_mfccs` MFCCs.*
    mfccs = tf.contrib.signal.mfccs_from_log_mel_spectrograms(log_mel_spectrograms)[..., :num_mfccs]
    **return** mfccs.numpy()[0]

通过使用上面定义的“mfcc”函数,很容易计算“bed”的音频文件的 mfcc 并可视化其 MFCC。

mfcc_bed = mfcc(train.file[0],train.label[0])
fig = plt.figure(figsize=(14,8))
plt.ylabel("MFCC (log) coefficient")
plt.imshow(np.swapaxes(mfcc_bed,0,1))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据建模:

我建立了一个顺序神经网络模型,这是在 keras 中建立模型的最简单的方法——它告诉 Keras 按顺序堆叠所有层。然后我添加了四个密集层,它们是模型中完全连接的层。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Reference: https://towardsdatascience.com/applied-deep-learning-part-1-artificial-neural-networks-d7834f67a4f6

在建立模型后,我使用自适应矩估计作为优化器,类别交叉熵作为损失,准确度作为度量来编译模型。

*# Dense(64) is a fully-connected layer with 64 hidden units.*
*# in the first layer, you must specify the expected input data shape:*
*# here, 20-dimensional vectors.*
**with** tf.Session() **as** sess0:
    **assert** **not** tf.executing_eagerly()
    model = Sequential()

    model.add(layers.Dense(32, input_shape=X_train_array.shape[1:], activation='tanh'))
    model.add(Dense(64, activation='tanh'))
    model.add(Dense(128, activation='tanh'))

    model.add(Flatten())
    *#model.add(Dense(256, activation='relu'))*

    model.add(Dense(30))
    model.add(Activation('sigmoid'))

    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    *#Adam, an algorithm for first-order gradient-based optimization of stochastic objective functions, based on adaptive estimates of lower-order moments.*
    model.summary()

  *#  history = model.fit(x=X_train_array, y=y_train_array, epochs=5, verbose=1, validation_split = 0.33, shuffle=True, class_weight=get_class_weights(pd.Series((list(set(labels))),dtype='category').cat.codes.values),batch_size=batch_size)* 
    history = model.fit(x=X_train_array, y=y_train_array, epochs=25, verbose=1, validation_split = 0.1, shuffle=**True**, class_weight=get_class_weights(pd.Series(Y_train,dtype='category').cat.codes.values),batch_size=128) 

    model_evaluation = model.evaluate(x=X_test_array, y=y_test_array, batch_size=**None**, verbose=1)

    prediction = model.predict(X_test_array, batch_size = 128, verbose = 1)

    april_tst = model.predict(mfcc_april_test, batch_size = 128, verbose = 1)

    sess0.close()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sequential Neural Network Model in Keras

我选择 25 作为历元数,这是模型在数据中循环的次数。经过约 20 个历元的运行,模型的验证准确率提高到 61% — 62%。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Model accuracy regarding the number of epochs

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Model loss regarding the number of epochs

从上面两张图可以看出,测试和训练精度彼此不够接近,这意味着可以通过克服过拟合问题来改进这个模型。

结论:

  1. 音频文件通常被转换成数组作为 Keras 模型的输入。
  2. 声谱图和 MFCC 是要转换为数组的音频文件的两个特征。
  3. 我们可以修改 Keras 模型的层次来提高模型的精度。
  4. 通过比较训练和测试精度来了解过度拟合问题。
  5. 与 Keras API 模型相比,顺序模型更容易修改。

结论:

  1. 为了准确预测 Kaggle 数据集中“测试”音频文件中带有噪声的语音,我需要通过添加背景噪声来处理当前的训练数据。
  2. 由于 Kaggle“测试”音频文件中存在未知声音,我还需要添加“未知”作为我的标签之一。
  3. 我还可以归一化训练数据的 mfccs,看看是否可以提高模型精度。
  4. 我也可以使用 CNN 和 RNN 的组合,看看我是否能提高模型的准确性。

参考:

  1. 保罗·波尔斯马和大卫·韦宁克, 简介 3.1。查看声谱图 ,阿姆斯特丹大学语音科学研究所
  2. 维基百科:【https://en.wikipedia.org/wiki/Short-time_Fourier_transform
  3. 密码学:http://practical cryptography . com/miscellaneous/machine-learning/guide-Mel-frequency-ceps tral-coefficients-mfccs/
  4. Chris Dinant, Kaggle Tensorflow 语音识别挑战

致谢:

  1. https://www . ka ggle . com/Davids 1992/speech-presentation-and-data-exploration
  2. https://www . ka ggle . com/ol lmer/labels-spectro grams-exploration
  3. https://github . com/Tony 607/TF _ audio _ signal/blob/master/TF _ audio _ signal . ipynb
  4. https://towards data science . com/ka ggle-tensor flow-speech-recognition-challenge-b 46 a3 BCA 2501
  5. https://www . ka ggle . com/kcs 93023/keras-sequential-conv1d-model-class ification

语音识别很难—第 1 部分

原文:https://towardsdatascience.com/speech-recognition-is-hard-part-1-258e813b6eb7?source=collection_archive---------6-----------------------

说话是我们最自然的交流方式,是我们的第二天性。现在,我们的机器已经开始识别我们的语音,它们在与我们交流方面变得越来越好。

当前的语音助手和设备,如亚马逊 Alexa 和谷歌 Home,每个月都越来越受欢迎——它们正在改变我们购物的方式,我们搜索的方式,我们与设备甚至彼此互动的方式。

第一个语音识别软件是在 20 世纪 50 年代开发的,从那时起我们已经走过了漫长的道路。

我们是因为我们会说话

_ _ _ _ _ _

不清楚人类是从什么时候开始互相交谈的。估计差异很大,从晚至 5 万年前到早至 200 多万年前人类属的开始。我们可能不知道史前人类何时开始说话,但我们知道他们会说话,因为我们会。

一个普通人每分钟大约说 150 个单词,而一个普通人每分钟大约打 50 个单词——比起通过文本,我们可以用声音进行更多的交流*。*

但是我们没有那么简单——我们的交流不仅仅是我们每分钟说多少单词的问题。演讲不是任何一件事情。在演讲中,语境很重要,我们在哪里演讲,为什么演讲,我们的听众是谁,我们的目标是什么。简而言之,演讲很复杂复杂。

言语是复杂的,错综复杂的

___

我们说话的方式差异很大:

  • 有声读物推荐为每分钟 150-160 wpm 左右
  • 建议幻灯片演示接近 100–125 wpm(以舒适的速度)
  • 拍卖师可以以每分钟 250 英镑的速度发言
  • 小约翰·莫斯奇塔是吉尼斯世界纪录的保持者,他是世界上说话最快的人,每分钟能说 586 个单词。他的记录在 1990 年被史蒂夫·伍德莫尔打破,他每分钟讲 637 个单词,然后在 1995 年 8 月 30 日被肖恩·香农打破,他每分钟讲 655 个单词。肖恩·香农用 23.8 秒背诵了哈姆雷特的独白《生存还是毁灭》(260 字)。

Sean Shannon reciting Hamlet’s soliloquy “To be or not to be” (260 words) in 23.8 seconds

没有语言,人类交流是不可想象的。说话时,我们进行语音合成、语音识别和语音理解。

两个人类如何交流?

___

在演讲中,我们试图把我们的想法和经历转换成声音和语言。

印象和表达之间有多么大的鸿沟!这就是我们具有讽刺意味的命运——拥有莎士比亚式的情感,并且(除非我们碰巧是莎士比亚)像汽车销售员、青少年或大学教授那样谈论它们。我们以相反的方式实践炼金术——触摸金子,金子变成铅;触及经验的纯歌词,它们就变成了废话和猪食的口头等价物。”――天才和女神阿尔多斯·赫胥黎

语音通信可以分解为跟在后面:

  1. 演讲者用语言表达了他的想法。
  2. 扬声器使用声带和语音系统发出声音。
  3. 声音通过空气中的声波以振动的形式传到听者的耳朵里。
  4. 声音通过听觉神经传递到听者的大脑。
  5. 这些振动在他的大脑中被转换成某种“语言”。
  6. 大脑从声音中提取意义。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Schematic Diagram of the Speech Production / Perception Process

自动语音识别系统(ASR)的主要目标是建立一个可以模拟人类听者的系统。

自动语音识别系统(ASR)的主要目标是建立一个可以模拟人类听者的系统,即它可以“理解”我们的口语并做出反应,这意味着系统可以对口语单词做出适当的反应,并将语音转换为另一种媒体,如文本。

自 20 世纪 30 年代以来,研究人员一直试图建造一台能够像人一样(或比人更好)产生、识别和理解人类语言的机器。[我将在以后的博客中讲述语音识别的历史——人物、故事、成功和失败。]

在过去的几年中,研究人员在这个问题上取得了重大进展,但通用语音识别仍然没有解决任何语言。这是因为语音识别很难。

为什么机器很难做语音识别?

_ _ _ _ _ _

自动语音识别(ASR)系统是一个硬件和软件系统,其中输入是话音(语音)的声音,输出是那些口语单词的标识。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Very simple block diagram (What exactly does the box do? — details later)

中间的盒子是一个完整的系统,可以分析通常由麦克风采集的语音,然后将其转录为机器可用的文本形式。

为什么语音识别这么难?我们几乎毫不费力地做到了。语音识别很难,因为听力比我们天真地认为的更难、更复杂。让我们看看我们在做什么,机器需要做什么:

  1. 我们有生理学和解剖学来接受声波。(机器必须将模拟信号(声波)转换成数字信号)。
  2. 当有人对我们说话时,比如说在餐馆里,那么我们必须将他们的话(这被称为信号)与所有背景噪音——注意,噪音可能会有所不同——电话、房间音响、其他人的谈话、交通(如果窗户是开着的)等。(机器必须把语音和噪音分开)。
  3. 有时人们说得太快(或太慢)——他们不会在一个句子结束时停下来或放慢速度,然后再开始一个新的句子。这些句子听起来像一个连续的长单词流(很难从声音中“听到”句子结构),并且不清楚一个单词何时结束,另一个单词何时开始。(机器需要处理语音中的这些端点)。
  4. 每个人听起来都不一样——年龄、性别、口音、风格、个性、背景、意图等等。—都影响声音和言语。见鬼,即使只有一个人也不会说“你好吗?”每次都是一样的——声音有规律地变化。(机器必须考虑语音的可变性(年龄、性别、口音等)。)).
  5. 更进一步,假设我们在与一个 9 岁、90 岁和其他几个人进行对话,他们都讲不同的方言或有不同的口音,那么我们必须理解的核心对话线索和每个人在说什么(至少大部分时间)。我们必须弄清楚,不管谁说“猫”这个词,它的意思都是一样的。(机器必须识别声音,即使它们是由完全不同的人说的——一个 9 岁的美国男孩和一个 60 岁的西班牙女人说的——“猫”和“caaattt”。**
  6. 有很多单词听起来很相似或相同(像“to”、“too”、“two”),但意思却很不一样(它们是同音)。我们需要知道说话者想要哪个词(和意思)。(机器必须消除同音词的歧义)。
  7. 我们在言语中使用的填充词有很多,比如“嗯”、“呃”、“嗯”等。,我们本能地知道如何过滤它们。这些不会让我们偏离轨道,也不会让我们错误地理解说话者的话。(机器也必须过滤这些填料)。
  8. 还有误解——我们听错了句子。我最近有过这样的经历:我的一个朋友告诉我一个街道名称,我猜了大约 5 种不同的东西——全部都不正确——从名称到食物!(机器也必须管理这样的误解,在这项任务中,它们需要比我们强得多——因为我们发现机器中的这种错误令人讨厌)。**
  9. 最后,如果所有这些听起来不多,我们必须知道我们使用的语言的语法和语义以及上下文。

令人惊讶的是,我们在一次简单的对话中就做到了这一切(或许还有更多)。从这个意义上说,我们的大脑是不可思议的。因此,机器努力做到这一切就不足为奇了。尽管如此,语音识别已经走过了漫长的道路,这可能只是一个开始。

机器如何进行语音识别?

___

你只是一波一波地过来。你的嘴唇在动,但我听不到你在说什么。 —平克·弗洛伊德,舒舒服服地麻木了

机器在理想条件下的语音识别系统要比现实生活中好得多。理想状态通常意味着 1)一个成年白人男性 2)在一个安静的房间里 3)说话(故意地、缓慢地)4)直接对着一个好的麦克风。但是现实世界很乱。

让我们来分解一下高级别的 ASR 流程:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们看到了构建自动语音识别系统的一些概念问题。那么,中间的盒子里会放什么呢?建立一个好的系统?

ASR 的组件

_ _ _ _ _ _

ASR 系统通常由以下部件组成:

  • 输入的数字表示(以及提取它的方法)
  • 特征提取组件:该组件识别包含语音的输入部分,然后将这些部分转换成称为声学参数的序列。
  • 用于训练和测试的数据/语料库:这个数据库是一个记录语音的集合,增加了所需的注释和转录。这个语料库必须足够大,足够相关,以涵盖给定用例中语音的可变性。
  • 声学模型:声学模型获取语音波形,将其分解成小片段,并预测语音中最可能的音素。
  • 发音模型:发音模型获取声音并将它们联系在一起以构成单词,即,它将单词与其语音表示相关联。
  • 语言模型:语言模型获取单词并将它们连接在一起组成句子,也就是说,它预测几个文本字符串中最可能的单词(或文本字符串)序列。
  • 有效搜索假设空间的算法(称为解码器):这结合了声学和语言模型的预测,并输出给定语音文件输入的最可能的文本字符串。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Components of an ASR system

该系统的目标是将这些不同的模型结合起来,以获得对给定输入句子的已经观察到的声音序列(在语音数据库中)的概率的估计。然后,系统搜索所有句子的空间,并选择一个概率最高的句子作为输入源句子。

现在,考虑一下所有英语句子的集合——这个集合非常庞大,因此我们需要一个有效的算法,即而不是搜索所有可能的句子,但只搜索有足够好的机会匹配输入的句子,从而使成为一个搜索问题(或称为解码问题)。

ASR 系统有哪些特征?我们如何看待不同类型的 ASR 系统?这是第 2 部分——我将分解 ASR 系统的每个组件,以及在构建这样一个系统时我们必须做出的权衡。

第 2 部分 ASR 系统的特征(即将推出…)

语言趣谈【有何不可?]**

___

我毫不怀疑,语言的起源应归功于对各种自然声音、其他动物的声音以及人类自身本能的叫声的模仿和修改,并得到了手势和手势的帮助——查尔斯·达尔文,1871 年。男人的血统,以及与性别相关的选择

“人类的语言就像一个有裂缝的水壶,我们敲着粗糙的节奏让熊跟着跳舞,而我们渴望创造出能融化星星的音乐。”——居斯塔夫·福楼拜,包法利夫人

“如果你用一个人能理解的语言和他交谈,那会让他头脑发热。如果你用他的语言和他交谈,那会触及他的内心。”——纳尔逊·曼德拉

“与此同时,可怜的巴别鱼,通过有效地消除不同种族和文化之间交流的所有障碍,引发了比创造历史上任何事情都更多、更血腥的战争。”——道格拉斯·亚当斯,银河系漫游指南

其他东西

我写的其他一些文章(如果你感兴趣的话):

生命延续革命—第一部 **:我们这个时代真正严重的一个问题是死亡。**因为时间到了,时间真的到了。你已经不在了。你所有的学习、尝试、试验、错误、情感、想法、经历、愿望、渴望、梦想都不复存在。再大的活下去的欲望,也活不下去。 继续阅读……**

会玩的机器(概述) : 这个系列涵盖了人工智能和游戏的历史(直到深蓝),并专注于会下棋、跳棋和西洋双陆棋的机器。涵盖了以下主题:如何构建国际象棋机器、香农在国际象棋方面的工作、图灵在国际象棋方面的工作、土耳其人、El Ajedrecista、MANIAC、Bernstein 国际象棋程序、Samuel’s checkers、Mac Hack VI、Cray Blitz、BKG、HiTech、Chinook、Deep think、TD-Gammon 和深蓝。 继续阅读……**

使用神经计算棒和 OpenVINO 加速对低功耗设备的预测

原文:https://towardsdatascience.com/speed-up-predictions-on-low-power-devices-using-neural-compute-stick-and-openvino-98f3ae9dcf41?source=collection_archive---------7-----------------------

英特尔的神经计算棒能够在边缘加速 Tensorflow 神经网络推理,将性能提高 10 倍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Neural Compute Stick — NCS

在本文中,我们将探索实现以下目标所需的步骤:

  1. 使用英特尔的 OpenVINO Toolkit 将 Tensorflow 模型转换为 NCS 兼容模型
  2. 在 Raspberry 上安装一个轻量级版本的 OpenVINO,在船上运行推理
  3. 在 Raspberry 上测试和部署转换后的模型

要求

该程序需要:

  • 一个基于 Ubuntu 的主机(建议:Ubuntu 18.04 LTS)。
  • 一个英特尔 Movidius 神经计算棒(两个 NCS1NCS2 都与程序兼容)。
  • 安装了 Raspian 操作系统的 Raspberry Pi 板(建议:Raspberry Pi 3)

定义程序

首先,假设我们想要转换一个已经训练好的 Tensorflow 对象检测模型。出于本文的目的,我们将使用 Tensorflow 团队在 COCO 对象数据集(此处为模型的完整列表)上开发的一个已经训练好的模型。

我们需要使用 OpenVINO Toolkit 将这个模型转换成 NCS 兼容版本,称为中间表示(或 IR)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在转换之后,我们得到了两个文件,这两个文件可以部署在连接了 NCS 硬件的 Raspberry Pi 上。

更详细的工艺流程如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

神经计算棒装有一个万千芯片(也叫视觉处理器或 VPU)。

在主机上开始使用 OpenVINO

作为第一步,需要在主机上安装 OpenVINO Toolkit。

下载并安装 OpenVINO

首先连接到下载页面。然后,自己注册,选择 Ubuntu 版本的 OpenVINO,选择全包按钮。之后,下载文件,将其放在Downloads中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OpenVINO Download

一旦完成,就该安装软件包了。

让我们打开一个新的终端(CTRL + ALT + T)。导航到Downloads文件夹,解压缩文件,安装依赖项并执行 GUI 安装,如下所示:

cd ~/Downloads
tar xvf l_openvino_toolkit_<VERSION>.tgz
cd l_openvino_toolkit_<VERSION>
sudo ./install_cv_sdk_dependencies.sh
sudo ./install_GUI.sh

按照屏幕指示,继续进行完全安装

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Proceed with full installation

等待几分钟,直到安装过程完成。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Installation completed

现在需要设置环境变量。运行以下脚本临时设置环境变量:

source /opt/intel/computer_vision_sdk/bin/setupvars.sh

当您关闭 shell 时,OpenVINO toolkit 环境变量将被删除。作为一个选项,可以永久设置如下环境变量(强烈建议):

让我们打开.bashrc文件,通过签发:

nano ~/.bashrc

将这一行附加到文件的末尾:

source /opt/intel/computer_vision_sdk/bin/setupvars.sh

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Command at the end of the file

保存文件并关闭编辑器(CTRL + X)。

为了确保一切正常,让我们打开一个新的终端。打开后,您应该能够看到:

[setupvars.sh] OpenVINO environment initialized

配置神经计算棒通信

现在有必要更新 udev 规则以便让 OpenVINO 能够与神经计算棒通信。

为此,让我们打开一个新的终端(CTRL + ALT + T)并发出以下命令:

cd ~/Downloadcat <<EOF > 97-usbboot.rules
SUBSYSTEM=="usb", ATTRS{idProduct}=="2150", ATTRS{idVendor}=="03e7", GROUP="users", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEM=="usb", ATTRS{idProduct}=="2485", ATTRS{idVendor}=="03e7", GROUP="users", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEM=="usb", ATTRS{idProduct}=="f63b", ATTRS{idVendor}=="03e7", GROUP="users", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1"
EOF

然后:

sudo cp 97-usbboot.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
sudo ldconfig
rm 97-usbboot.rules

测试安装

我们现在可以通过对图像分类器进行预测来测试安装。让我们将记忆棒插入主机的 USB 端口(建议使用 USB 3.0 端口)。打开一个新终端,然后运行以下命令:

sudo ./opt/intel/computer_vision_sdk/deployment_tools/model_optimizer/install_prerequisites/install_prerequisites.sh

然后:

./opt/intel/computer_vision_sdk/deployment_tools/demo/demo_squeezenet_download_convert_run.sh -d MYRIAD

在执行结束时,您将能够看到以下输出:

Top 10 results:Image /opt/intel/computer_vision_sdk/deployment_tools/demo/car.png817 0.8422852 label sports car, sport car
511 0.0915527 label convertible
479 0.0393982 label car wheel
751 0.0093536 label racer, race car, racing car
436 0.0068550 label beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon
656 0.0036659 label minivan
586 0.0023270 label half track
717 0.0015497 label pickup, pickup truck
864 0.0010500 label tow truck, tow car, wrecker
581 0.0005631 label grille, radiator grilletotal inference time: 34.1828987
Average running time of one iteration: 34.1828987 msThroughput: 29.2543944 FPS[ INFO ] Execution successful###################################################Demo completed successfully.

这意味着你成功地使用加载在神经计算棒上的 SqueezeNet 网络进行了预测。

将张量流模型转换为 IR

如前所述,我们希望使用 Tensorflow 模型来使用 OpenVINO Toolkit 进行推理。

下面我们从 tensor flow ODAPIModel Zoo下载一个 SSD MobileNet v1 探测器模型。为此,让我们打开一个新的终端并发出命令:

cd ~/Downloads
wget http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_2018_01_28.tar.gztar -xvzf ssd_mobilenet_v1_coco_2018_01_28.tar.gz

让我们转换模型:

mo_tf.py \
    --input_model ~/Downloads/ssd_mobilenet_v1_coco_2018_01_28/frozen_inference_graph.pb \
    --tensorflow_use_custom_operations_config     /opt/intel/computer_vision_sdk/deployment_tools/model_optimizer/extensions/front/tf/ssd_support.json \
    --tensorflow_object_detection_api_pipeline_config ~/Downloads/ssd_mobilenet_v1_coco_2018_01_28/pipeline.config \
    --data_type FP16

我们使用半精度浮点格式(或 FP16)转换了 Tensorflow 模型,以便将模型大小减半,但以可忽略的较低精度为代价大幅提高性能。

输出应该类似于:

Model Optimizer arguments:
Common parameters:
 - Path to the Input Model:  /home/<username>/Downloads/ssd_mobilenet_v1_coco_2018_01_28/frozen_inference_graph.pb
 - Path for generated IR:  /home/<username>/Downloads/.
 - IR output name:  frozen_inference_graph
 - Log level:  ERROR
 - Batch:  Not specified, inherited from the model
 - Input layers:  Not specified, inherited from the model
 - Output layers:  Not specified, inherited from the model
 - Input shapes:  Not specified, inherited from the model
 - Mean values:  Not specified
 - Scale values:  Not specified
 - Scale factor:  Not specified
 - Precision of IR:  FP16
 - Enable fusing:  True
 - Enable grouped convolutions fusing:  True
 - Move mean values to preprocess section:  False
 - Reverse input channels:  False
TensorFlow specific parameters:
 - Input model in text protobuf format:  False
 - Offload unsupported operations:  False
 - Path to model dump for TensorBoard:  None
 - List of shared libraries with TensorFlow custom layers implementation:  None
 - Update the configuration file with input/output node names:  None
 - Use configuration file used to generate the model with Object Detection API:  /home/<username>/Downloads/ssd_mobilenet_v1_coco_2018_01_28/pipeline.config
 - Operations to offload:  None
 - Patterns to offload:  None
 - Use the config file:  /opt/intel/computer_vision_sdk/deployment_tools/model_optimizer/extensions/front/tf/ssd_support.json
Model Optimizer version:  1.5.12.49d067a0
The Preprocessor block has been removed. Only nodes performing mean value subtraction and scaling (if applicable) are kept.[ SUCCESS ] Generated IR model.
[ SUCCESS ] XML file: /home/<username>/Downloads/./frozen_inference_graph.xml
[ SUCCESS ] BIN file: /home/<username>/Downloads/./frozen_inference_graph.bin
[ SUCCESS ] Total execution time: 20.48 seconds.

Download文件夹中,您现在应该可以看到新生成的文件,扩展名分别为:

  • .xml
  • .bin

这些文件将用于树莓板上的预测。

测试模型

在将前面的文件上传到 Raspberry 之前,我们先检查一下是否一切正常,推断生成的模型。

将摄像头和神经计算棒插在你的主机上。让我们打开一个新的终端并发出:

cp /opt/intel/computer_vision_sdk_2018.5.455/deployment_tools/inference_engine/samples/python_samples/object_detection_demo_ssd_async.py ~/Downloads/python3 object_detection_demo_ssd_async.py \
    -m ~/Downloads/frozen_inference_graph.xml \
    -i cam \
    -pt 0.6

您的主机现在应该会打开一个新窗口,显示您的相机检测!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Detections on the main machine using NCS2

在树莓 Pi 上运行神经网络

让我们来设置您的树莓 Pi 板。我们将首先为树莓安装一个轻量级版本的 OpenVINO。然后,我们将在板上设置运行 SSDMobileNet V1 所需的所有文件。

为树莓 Pi 安装 OpenVINO

需要安装并运行 Raspian OS(建议从头安装 Raspian OS)。

首先我们需要下载 OpenVINO。这个版本与上一个版本不同。在Download内保存文件。

让我们打开一个新的终端。发出以下命令:

  • 导航至Download文件夹:
cd ~/Downloads/
  • 解压缩先前下载的归档文件:
tar -xf l_openvino_toolkit_ie_p_<version>.tgz

这将创建inference_engine_vpu_arm文件夹。

  • 修改setupvars.sh脚本,在Downloads文件夹中,将<INSTALLDIR>替换为安装文件夹的绝对路径,发出以下命令:
sudo sed -i "s|<INSTALLDIR>|$(pwd)/inference_engine_vpu_arm|" inference_engine_vpu_arm/bin/setupvars.sh

设置环境变量

在启动和运行 OpenVINO Toolkit 之前,需要更新几个环境变量。让我们运行下面的命令,在Downloads文件夹中临时设置环境变量:

source inference_engine_vpu_arm/bin/setupvars.sh

输出应该类似于:

[setupvars.sh] OpenVINO environment initialized

类似于主机上的程序,现在建议通过打开.bashrc文件 a 永久设置环境变量

nano ~/.bashrc

并在结尾处添加以下一行:

source ~/Downloads/inference_engine_vpu_arm/bin/setupvars.sh

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

保存文件并退出。

为了测试安装,让我们打开一个新的终端。您应该看到:

[setupvars.sh] OpenVINO environment initialized

添加 USB 规则

  • 将当前 Linux 用户添加到users组:
sudo usermod -a -G users "$(whoami)"

注销然后再登录(或者关闭再打开 Raspberry)以使其生效。

  • 安装 USB 规则,从Downloads文件夹内打开一个新的终端并发出:
sh inference_engine_vpu_arm/install_dependencies/install_NCS_udev_rules.sh

复制预测文件

现在是将模型文件复制到 Raspberry 的时候了。要运行演示应用程序,我们需要以下文件:

+ frozen_inference_graph.xml
+ frozen_inference_graph.bin
+ object_detection_demo_ssd_async.py

如果您正确遵循了前面的步骤,这些文件应该位于主机的Download文件夹中。

您可以将这些文件复制到 USB 驱动器,然后复制到 Raspberry,或者通过 SSH。

在树莓上,新建一个文件夹:mkdir ~/NCS-demo。把所有以前的文件放进去。

在树莓上运行演示应用程序

最后,我们可以在 Raspberry 上执行用于检测的演示应用程序。为了做到这一点:

  1. 在树莓上连接神经计算棒
  2. 将 USB 摄像头连接到 Raspberry
  3. 执行以下命令:
cd ~/NCS-demopython3 object_detection_demo_ssd_async.py \
    -m frozen_inference_graph.xml \
    -i cam  \
    -d MYRIAD \
    -pt 0.6

提示 :增减 *-pt* 标志值。会影响检测的信心。

正如之前在主机上经历的那样,应该会弹出一个新窗口,以接近 10FPS 的速度显示该网络的推断!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Detections on Raspberry Pi @ ~10FPS

结论

神经计算棒是一种功能强大的硬件,可用于在低功率系统上执行卷积神经网络。我们的测试显示,与在香草树莓板上执行的推理相比,速度提高了近 10 倍。

这只是硬件方面的起点。我们将在未来看到越来越多的低功率设备上的专用硬件,如手机、平板电脑、个人电脑、汽车、无人机,甚至卫星

毫无疑问,英特尔是这一环境中的主要参与者之一,它在收购 Movidius 公司的同时,也在与脸书一起探索其他解决方案,如英特尔 Nervana

然而,竞争也在这一领域发挥着重要作用。

Xilinx 正在调整他的 ZINQ FPGAs,以便在硬件方面加速边缘神经网络的性能,同时还开发了一个名为 Deephi DNNDK 的软件工具,用于优化低功耗系统的神经网络。

谷歌也在这方面采取行动,开发了全新的 Edge TPU,专为运行能够在非常低的功率下提高预测速度的.tflite模型而设计*(敬请期待,我们将很快探索这一解决方案,与 NCS 进行性能比较)*。

对于人工智能驱动的嵌入式设备来说,这是一个令人兴奋的时代。

文章由 马蒂亚·瓦里勒

请随意评论这篇文章,以提高他的质量和效果。

资源

使用 Pandas-Profiling 加速您的探索性数据分析

原文:https://towardsdatascience.com/speed-up-your-exploratory-data-analysis-with-pandas-profiling-88b33dc53625?source=collection_archive---------1-----------------------

只需一行代码就能直观地了解数据的结构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source: https://unsplash.com/photos/gts_Eh4g1lk

介绍

第一次导入新的数据集时,首先要做的是理解数据。这包括确定特定预测值的范围、识别每个预测值的数据类型以及计算每个预测值缺失值的数量或百分比等步骤。

pandas 库为 EDA 提供了许多非常有用的功能。但是,在能够应用其中的大部分之前,一般要先从更通用的函数开始,比如 df.describe() 。然而,这些函数所提供的功能是有限的,而且通常情况下,您的初始 EDA 工作流对于每个新数据集都非常相似。作为一个不喜欢完成重复性任务的人,我最近在寻找替代方法,并发现了熊猫轮廓。pandas-profiling 不只是给出一个单一的输出,它使用户能够快速生成一个结构非常广泛的 HTML 文件,其中包含了您在深入研究更具体和更个性化的数据之前可能需要了解的大部分内容。在接下来的段落中,我将带你了解 pandas-profiling 在 Titanic 数据集上的应用。

更快的 EDA

我选择将 pandas-profiling 应用于 Titanic 数据集,因为数据类型多样,而且缺少值。在我看来,当数据还没有被清理并且仍然需要进一步的个性化调整时,pandas-profiling 特别有趣。为了在这些个性化的调整中更好地引导你的注意力,你需要知道从哪里开始,关注什么。这就是大熊猫特征分析的用武之地。

首先,让我们导入数据并使用 pandas 检索一些描述性统计数据:

上面的代码块将产生以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然上面的输出包含大量信息,但它并没有告诉您您可能感兴趣的所有内容。例如,您可以假设数据框有 891 行。如果您想要检查,您必须添加另一行代码来确定数据帧的长度。虽然这些计算并不十分昂贵,但一遍又一遍地重复它们确实会占用时间,在清理数据时,你或许可以更好地利用这些时间。

概观

现在,让我们对熊猫做同样的分析:

运行这一行代码将为您的数据创建一个 HTML EDA 报告。上面显示的代码将创建结果的内联输出;但是,您也可以选择将 EDA 报告保存为 HTML 文件,以便能够更轻松地共享它。

HTML EDA 报告的第一部分将包含一个概述部分,为您提供基本信息(观察次数、变量数量等)。).它还会输出一个警告列表,告诉您在哪里需要再次检查数据,并可能需要集中精力进行清理。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Overview Output

特定变量 EDA

在概述之后,EDA 报告为您提供了每个特定变量的有用见解。其中还包括一个描述每个变量分布的小型可视化工具:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Output for numeric variable ‘Age’

从上面可以看出,pandas-profiling 为您提供了一些有用的指标,例如缺失值的百分比和数量,以及我们前面看到的描述性统计数据。由于“年龄”是一个数字变量,使用直方图可视化它的分布告诉我们,这个变量似乎是右偏的。

对于分类变量,只做了微小的改变:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Output for categorical variable ‘Sex’

pandas-profiling 不是计算平均值、最小值和最大值,而是计算分类变量的类计数。由于“性别”是一个二元变量,我们只找到两个不同的计数。

如果你像我一样,你可能想知道 pandas-profiling 是如何计算其输出的。幸运的是,源代码可以在 GitHub 上找到。因为我不喜欢在代码中制造不必要的黑盒部分,所以我将快速进入一个数值变量的源代码:

虽然这看起来像是一大段代码,但实际上非常容易理解。Pandas-profiling 的源代码包括另一个确定每个变量类型的函数。如果变量被识别为数字变量,上面的函数将产生我前面展示的输出。该函数使用基本的 pandas 系列操作,例如 series.mean() ,并将结果存储在统计字典中。这些图是使用 matplotlib 的 matplotlib.pyplot.hist 函数的改编版本生成的,目的是能够处理不同类型的数据集。

相关性和样本

在每个特定变量的 EDA 下,pandas-profiling 将输出 Pearson 和 Spearman 相关矩阵。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Pearson correlation matrix output

如果您愿意,可以在生成报告的初始代码行中设置一些相关性阈值。这样,你就能够调整你认为对你的分析重要的相关性强度。

最后,pandas-profiling 将输出一个代码示例。严格地说,这不是一个代码样本,而只是数据的头部。当最初的几个观察值不能代表数据的总体特征时,这可能会导致问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,我建议不要将最后一个输出用于您的初始分析,而是运行 df.sample(5) ,它将从您的数据集中随机选择五个观察值。

结论

总之,pandas-profiling 提供了一些有用的特性,特别是如果您的主要目标是快速而粗略地理解您的数据,或者以可视格式与他人共享您的初始 EDA。然而,它并没有接近自动化 EDA。实际的个性化工作仍然需要手工完成。

如果你想在一个笔记本中看到整个 EDA,请查看我在 nbviewer online 中使用的笔记本。你也可以在我的 GitHub repo 上找到中等物品的代码。

使用 Cython 加速您的 Python 代码

原文:https://towardsdatascience.com/speed-up-your-python-code-with-cython-8879105f2b6f?source=collection_archive---------13-----------------------

花更少的时间在屏幕前等待

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Picture from Unsplash

简介

如果你曾经用 Python 编写过代码,你可能会花更多的时间等待某些代码块执行,而这是你所不希望的。虽然有一些方法可以让你的代码更有效率,但很可能还是比 C 代码慢。这主要归结于一个事实,即 Python 是一种动态编程语言,它将 C 在编译过程中负责的许多事情移到了运行时。

然而,如果你和我一样,喜欢用 Python 编程,并且仍然想加速你的代码,你可以考虑使用 Cython 。虽然 Cython 本身是一种独立的编程语言,但它非常容易集成到您的 Jupyter 笔记本工作流程中。在执行时,Cython 会将您的 Python 代码翻译成 C 语言,通常会显著提高速度。

安装 Cython

为了能够使用 Cython,你需要一个 C 编译器。因此,根据您当前的操作系统,安装过程会有所不同。对于 Linux,GNU C 编译器(gncc)通常是存在的。对于 Mac OS,你可以下载 Xcode 来获得 gncc。如果您应该使用 Windows,安装过程会稍微复杂一些。关于 Cython 的 GitHub 的更多信息请点击这里

一旦你有了 C 编译器,你需要在你的终端上运行的是:

pip install Cython

如何使用 Cython

展示 Cython 功能的最简单方式是通过 Jupyter 笔记本电脑。为了在我们的笔记本中使用 Cython,我们将使用 IPython magic 命令。神奇的命令以百分号开始,并提供了一些额外的功能,旨在增强您的工作流程。一般来说,有两种类型的魔法命令:

  1. 线魔法由单个“%”表示,并且只对一行输入进行操作
  2. 单元格魔术由两个“%”表示,对多行输入进行操作。

让我们开始吧:

首先,为了能够使用 Cython,我们必须运行:

%load_ext Cython

现在,每当我们想在一个代码单元中运行 Cython 时,我们必须首先将以下神奇的命令放入该单元:

%%cython

一旦你这样做了,你就可以开始用 Cython 编码了。

cy thon 快了多少?

与普通的 Python 代码相比,Cython 的速度快多少实际上取决于代码本身。例如,如果你要运行计算量很大的包含许多变量的循环,Cython 将会大大优于普通的 Python 代码。递归函数也将使 Cython 比 Python 快得多。

让我们用斐波那契数列来证明这一点。这个算法,简单来说,就是把前两个数加起来,找到下一个数。这在 Python 中可能是这样的:

def fibonacci(n): if n < 0:
        print("1st fibonacci number = 0") elif n == 1:
        return 0 elif n == 2:
        return 1 else:
        return fibonacci(n-1) + fibonacci(n-2)

让我们让 Python 发挥作用:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如您所见,找到序列中的第 39 个数字需要 13.3 秒的计算时间。这里的 Wall time 指的是从开始到结束调用函数所用的总时间。

让我们在 Cython 中定义相同的函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是怎么回事?如你所见,我们在顶部使用了一些细胞魔法,允许我们在这个细胞中使用 Cython。我将很快解释“-a”选项的作用。然后,我们基本上采用与上面相同的代码,除了现在我们能够利用静态类型声明并将 n 定义为 integer 类型。

正如您所看到的,通过在 magic 命令后添加“-a”,我们收到了注释,向我们显示了您的代码中有多少 Python 交互。这里的目标是去掉所有的黄线,取而代之的是白色背景。在这种情况下,将没有 Python 交互,所有代码都将在 C 中运行。您还可以单击每行旁边的“+”号来查看 Python 代码的 C 翻译。

那段代码快了多少?让我们来看看:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这种情况下,Cython 比 Python 快 6.75 倍左右。这清楚地展示了利用 Cython 节省时间的能力,与常规 Python 代码相比,它提供了最大的改进。

附加选项

如果你已经知道 C,Cython 也允许访问 C 代码,而 Cython 的制造商还没有为这些代码添加现成的声明。例如,使用下面的代码,您可以为 C 函数生成一个 Python 包装器,并将其添加到模块 dict 中。

%%cythoncdef extern from "math.h":
    cpdef double sin(double x)

Cython 证明了许多额外的能力,例如并行性,这些都在它的文档中有非常清晰的描述,你可以在这里找到。

结论

如果您有时会遇到 Python 代码执行时间过长的问题,Cython 提供了一种真正集成的高效方法来加速您的代码。最重要的是,如果你对 c 语言稍微熟悉一点,它提供了许多进一步优化你的代码的能力。如果你有任何建议或意见,请随时联系我。

使用并行计算加速和完善您的工作

原文:https://towardsdatascience.com/speeding-up-and-perfecting-your-work-using-parallel-computing-8bc2f0c073f8?source=collection_archive---------19-----------------------

Python 多处理与 PySpark mapPartition 的详细指南

在科学上,每一项成就的背后都是磨砺、严谨的工作。成功不可能一蹴而就。作为一名数据科学家,您可能需要处理大量的数据和计算,在日常工作中进行重复的测试和实验。虽然你不想等待耗时的操作一次又一次地重复,一次又一次地观察,从而使你有回报的、刺激的工作变得乏味。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo from Unsplash

今天的计算机有多个处理器,允许同时运行多种功能。然而,如果程序员没有意识到这一点,或者不知道如何使用它,这将没有帮助,这导致我写这篇文章来演示如何使用 Python 多处理和 PySpark mapPartition 来并行化您的进程。

机器学习模型

数据科学是一个多样化的领域,包括广泛的工作。一个重要的应用是建立机器学习管道和个性化数据产品,以更好地瞄准客户,实现更准确的决策。在这篇文章中,我选择了在构建机器学习模型中使用分层采样进行交叉验证的步骤作为示例,以在 Python 和 Spark 环境中并行化它。我使用的数据是来自 UCI 机器学习库的银行营销数据集,因为它相对干净。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为这篇文章关注的是并行性,所以我跳过了数据探索性分析、特性工程和特性选择的步骤。经过一些预处理后,我获得了左侧的数据,为建模做好了准备,y 列是我们的目标变量,即客户在活动期间是否订阅了定期存款。

现在让我们进入交叉验证的模型选择步骤的细节。作为一种常见的做法,我将 scikit-learn 中的大多数分类器包含到一个名为 classifiers 的列表中,并使用 sklearn.model_selection 中的 StratifiedShuffleSplit 方法来执行这一步。为了模型比较的目的,我计算了准确度分数、对数损失和 AUC。这一步花了 46s,下面是代码:

sss = StratifiedShuffleSplit(n_splits = 10, test_size = 0.1, random_state = 0)start = time.time()
res = []
for train_index, test_index in sss.split(X, y):
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
    for clf in classifiers:
        name = clf.__class__.__name__
        clf.fit(X_train, y_train)
        y_pred = clf.predict(X_test)
        y_pred_probas = clf.predict_proba(X_test)
        acc = accuracy_score(y_test, y_pred)
        loss = log_loss(y_test, y_pred)
        fpr, tpr, thresholds = roc_curve(y_test, y_pred_probas[:,1])
        auc_score = auc(fpr, tpr)
        res.append([name, acc, loss, auc_score])print('The cross-validation with stratified sampling took time (s): {}'.format(time.time() - start))

就像在许多 Kaggle 比赛中一样,获胜者再次是梯度推进:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Python 多处理

在 Python 中,多处理模块被设计成在多个进程之间分配工作以提高性能。在这里,我使用了带有 map 方法的 pool 类来将 iterable 拆分为单独的任务。下面的代码使用 4 个处理器运行相同的过程,它在 20 秒内完成。

import itertools
import multiprocessing
from multiprocessing import Poolcv_index = [(i, j) for i, j in sss.split(X, y)]
params = list(itertools.product(cv_index, classifiers))def cv_test(params):
    global X
    global y
    train_index = params[0][0]
    test_index = params[0][1]
    clf = params[1]
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
    name = clf.__class__.__name__
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)
    y_pred_probas = clf.predict_proba(X_test)
    acc = accuracy_score(y_test, y_pred)
    loss = log_loss(y_test, y_pred)
    fpr, tpr, thresholds = roc_curve(y_test, y_pred_probas[:,1])
    auc_score = auc(fpr, tpr)
    return [name, acc, loss, auc_score]p = Pool(processes = 4)
start = time.time()
res = p.map(cv_test, params)
p.close()
p.join()
print('The cross-validation with stratified sampling on 5 cores took time (s): {}'.format(time.time() - start))

火花映射分区

我总是对火花很感兴趣。Rdd 是 Spark 的基本数据结构。设想 rdd 是一组许多行,Spark 将这些行转换成多个分区。mapPartition 允许在每个分区上调用函数,分区的内容通过输入参数迭代器作为连续的值流提供。下面的代码显示了如何在 PySpark 环境中重写相同的过程。它也在 20 秒内完成。

import pyspark
from pyspark.sql import SparkSession
from pyspark import SparkContext, SparkConfspark = SparkSession.builder.appName("pyspark-test").getOrCreate()bc_X = spark.sparkContext.broadcast(X)
bc_y = spark.sparkContext.broadcast(y)def map_partitions_exec(X, y):
    def cv_test(iterator):
        for i in iterator:
            train_index = i[0][0]
            test_index = i[0][1]
            clf = i[1]
            X_train, X_test = X[train_index], X[test_index]
            y_train, y_test = y[train_index], y[test_index]
            name = clf.__class__.__name__
            clf.fit(X_train, y_train)
            y_pred = clf.predict(X_test)
            y_pred_probas = clf.predict_proba(X_test)
            acc = accuracy_score(y_test, y_pred)
            loss = log_loss(y_test, y_pred)
            fpr, tpr, thresholds = roc_curve(y_test, y_pred_probas[:,1])
            auc_score = auc(fpr, tpr)
            yield [name, acc, loss, auc_score]
    return cv_testparams_spark = spark.sparkContext.parallelize(params,4)
res = params_spark.mapPartitions(map_partitions_exec(bc_X.value, bc_y.value))
start = time.time()
res = res.collect()
print('The cross-validation with stratified sampling using spark took time (s): {}'.format(time.time() - start))

希望这些小小的编码技巧能帮助你在日常工作中更有效率。

参考资料:

  1. https://www . dummies . com/programming/big-data/data-science/running-in-parallel-python-for-data-science/
  2. https://www . program creek . com/python/example/91149/sk learn . model _ selection。StratifiedShuffleSplit
  3. https://stack overflow . com/questions/21185092/Apache-spark-map-vs-map partitions/21194559

用多线程加速模型训练

原文:https://towardsdatascience.com/speeding-up-model-training-with-multithreading-462df558d4f4?source=collection_archive---------20-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

等待模特训练结束有时会让人感到沮丧。加速这一过程可能是每个数据科学家心中最优先考虑的事情之一。有几种方法可以尝试,仅举几个例子:硬件升级(更快的 CPU/GPU)和特定于模型的调整(例如,对于反向传播,可以尝试不同的优化器以实现更快的收敛)。但是,如果您的硬件升级预算申请一直悬而未决,或者现有的优化器都没有带来显著的改进,该怎么办呢?

并非所有的希望都破灭了。我们仍然可以使我们的计划更加有效。今天,我要谈谈多线程。具体来说,如何同时运行(1)从磁盘到 RAM 的数据加载(这是 CPU 密集型的)和(2)模型训练(这通常是 GPU 密集型的)。

一个虚拟的例子

首先,让我们加载库依赖项。

import threading
import timefrom tqdm import tqdm

假设我们有两个功能:数据加载和模型训练。出于本教程的考虑,我将用一种延时方法来代替实函数。在这种情况下,数据加载需要 2 秒,而模型训练需要 3 秒。

# replace with train model - suppose it takes 3 seconds to train
def _trainModel():
  time.sleep(3)
  return# replace with load data function - suppose it takes 2 seconds to load
def _loadData():
  time.sleep(2)
  return

假设我们的模型训练是反向传播的形式,这需要运行许多纪元。我们选择了任意数量的历元进行循环:10 次

# assume we loop 10 times
epochs = 10

如果我们按照常规运行循环,即在数据加载和模型训练之间交替,我们可以看到完成 10 个时期总共需要 50 秒。从下面的图(由 tqdm 生成)中,我们看到每个历元大约需要 5 秒(2 秒加载数据,3 秒训练模型)。

原始时间=数据 _ 加载 _ 时间+训练 _ 模型 _ 时间

# without multithreading
# use with for tqdm to properly shut tqdm down if exception appears
with tqdm(range(epochs)) as epochLoop:
  for _ in epochLoop:
    # loadData
    _loadData()

    # trainModel
    _trainModel()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接下来,我们将尝试使用 python 的线程模块并行运行数据加载和模型训练。首先,我们必须为每个函数创建一个类,扩展线程。线程父类。

class TrainModel (threading.Thread):
    def __init__(self, data):
        threading.Thread.__init__(self)
        self.data = data
    def run(self):
        # return model loss
        self._return = _trainModel()    
    def join(self):
        threading.Thread.join(self)
        return self._returnclass LoadData (threading.Thread):
    def __init__(self, filenames):
        threading.Thread.__init__(self)
        self.filenames = filenames
    def run(self):        
        # return data
        self._return = _loadData()
    def join(self):
        threading.Thread.join(self)
        return self._return

然后,我们可以通过调用生成的线程类再次运行这些函数。在这里,我们可以观察到 10 个纪元所用的时间已经下降到 30 秒。这比之前的 50 秒提高了 40%!这里发生的情况是,我们并行运行数据加载和模型训练。每个历元所用的时间不再是这两个函数的总和。现在是两者中的最大值。

多线程时间= max(数据加载时间,训练模型时间)

# with multithreading
# use with for tqdm to properly shut tqdm down if exception appears
with tqdm(range(epochs)) as epochLoop:
  for _ in epochLoop:
    # loadData
    loadThread = LoadData(None)
    loadThread.start()

    # trainModel
    trainThread = TrainModel(None)
    trainThread.start()

    # only continue if both threads are done
    modelLoss = trainThread.join()
    data = loadThread.join()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

我们刚刚看到多线程可以帮助减少模型训练中每个时期所花费的时间。请注意,如果出现以下情况,此方法不会有太大帮助:

  1. 没有更多的 CPU 资源来产生另一个线程
  2. 最初花费的时间是由一个过程决定的。如果模型训练需要 10 秒,数据加载需要 0.1 秒,那么通常总共需要 101 秒(10 个时期)的时间将变成 100 秒。这相当于只有大约 1%的改善。换句话说,为了使多线程有效,所有进程的最大时间应该比它们的总和小得多。

用 LR-Finder 加速神经网络训练

原文:https://towardsdatascience.com/speeding-up-neural-net-training-with-lr-finder-c3b401a116d0?source=collection_archive---------11-----------------------

为你的网络找到良好的初始学习率

简介:优化器和 LR

当训练一个深度神经网络时,选择一个好的学习速率对于快速收敛和较低的误差都是至关重要的。我们还必须选择一个优化器,它决定如何在 DNN 中完成重量更新。

有各种各样的优化器可用,如 Adam、SGD+momentum、Adagrad、RMSprop、AdaDelta、AdaBound。其中亚当和新币+动量最受欢迎。在训练全连接 DNN 或卷积网络时,大多数现有技术的网络使用 SGD+momentum。这是因为它更好地概括了看不见的数据,并给出了更好的验证/测试分数。

为什么我们需要找一个好的 LR?

不过,SGD 有两个小问题,与 Adam 相比,SGD 收敛较慢,而且 SGD 需要调整学习速率。令人惊讶的是,解决这两个问题的方法是使用一个好的开始学习率。如果你的 LR 太高,你的误差永远不会减少,训练也不会收敛。太低的 LR 和你必须等待太长的训练收敛。因此,我们从 LR-Finder 给出的一个好的 LR 开始,然后在到达终点时稍微衰减它。

那么 LR finders 是如何工作的呢?

LR 探测器的基本目标是找到最高的 LR,该最高的 LR 仍然最小化损失并且不会使损失爆炸/发散。我们通过训练一个模型来做到这一点,同时在每批之后增加 LR,我们记录损失,最后我们在损失爆发之前使用 LR。我们这样做一个时期。

start_lr = 0.0001
end_lr = 1.0
num_batches = len(Data)/batch_size
cur_lr = start_lr
lr_multiplier = (end_lr / start_lr) ** (1.0 / num_batches)
losses = []
lrs = []for i in 1 to num_batches:
    loss = train_model_get_loss(batch=i)
    losses.append(loss)
    lrs.append(cur_lr)
    cur_lr = cur_lr*lr_multiplier # increase LRplot(lrs,losses)

你会得到一个如下图所示的图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Loss vs LR

我们用箭头标出这些点,以指示我们的实现中的位置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

LR Finder with Annotation

从这个图中,我们找到了一个点,在这个点之后,损失开始增加太多。

使用

我写了一个包含 LR 查找器的小库。这是给 Keras 的。对于 pytorch fast.ai 实现有效。
安装:

pip install --upgrade --upgrade-strategy only-if-needed [https://github.com/faizanahemad/data-science-utils/tarball/master](https://github.com/faizanahemad/data-science-utils/tarball/master) > /dev/null

您笔记本中的下一项(针对 Cifar 10)

首先为我们的数据集定义导入和数据生成器。

from data_science_utils.vision.keras import *X_train, Y_train, X_test, Y_test = get_cifar10_data()cutout_fn = get_cutout_eraser(p=0.75, s_l=0.1, s_h=0.3, r_1=0.3, r_2=1 / 0.3, max_erasures_per_image=2, pixel_level=True)
datagen = ImageDataGenerator(featurewise_center=True,featurewise_std_normalization=True,
                           preprocessing_function=cutout_fn)datagen_validation = ImageDataGenerator(featurewise_center=True,featurewise_std_normalization=True,)
datagen_validation.fit(X_train)model = build_model()

接下来,我们建立我们的模型,并使用 LR 查找器。

model = build_model() # returns an instance of Keras Model
lrf = LRFinder(model)
generator = datagen.flow(X_train, Y_train, batch_size=256,shuffle=True)
test_generator = datagen_validation.flow(X_test, Y_test, batch_size=256, shuffle=True)
lrf.find_generator(generator, 0.0001, 10.0,test_generator, epochs=1, steps_per_epoch=None,)
lrf.plot_loss()

你看到的上面两张图就是用这个生成的。你可以在这款谷歌 Colab 笔记本中看到例子。

使用 LR 取景器时的注意事项

  • 我们使用最小值作为我们的候选 lr。您可以注意到,其中一些是局部最小值,总损耗相当高,对于候选 LRs,总损耗应该接近最小损耗。所以我们过滤这些局部最小值
  • 我们需要使用验证集来寻找损失,使用训练集来寻找损失不会产生正确的结果,因为权重会超出训练集。
  • 当我们生成候选 lr 时,我们需要确保它们足够清晰。例如,生成像 0.552 和 0.563 这样的候选值没有意义,因为这些 lr 太接近了。因此,我们应用了一个规则,即每个 LR 应该至少与之前较低的 LR 相差 20%。
  • 注意 LR Finder 给你一个近似值,所以你取不取精确值并不重要。就像如果 LRF 给你 0.012,那么你也可以用 0.01。如果它给出 0.056,那么 0.05 和 0.06 都可以。

后期微调 LR

我们可以使用 LR 调度或 LR 衰减来降低后面时期的 LR,因为我们最初是从高 LR 开始的。

结果

在 LR 取景器之前

我们直接用了 SGD,learning_rate 为 0.01,nesterov 的动量。我们在 CIFAR-10 上训练了 100 个时期的网络。我们的网络有 45 万个参数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Without LR Finder

正如你所注意到的,验证误差收敛需要大约 60 个历元,准确率为 86.7%。

后左后取景器

我们使用 LR finder 提供的 LR。其他一切都一样。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After LR Finder

你可以看到,这里我们得到了 86.4%的准确性,但训练收敛于 40 个时期,而不是 60 个。使用 LR finder 提供的 LR 和 EarlyStopping 可以大大减少计算时间。

在 LR 查找器和 LR 调度之后

我们使用 keras 的 LR 调度来减少每个时期的 LR。基本上,在每个时期之后,我们通过乘以 0.97 来减少 LR。您可以在示例笔记本中查看 LR 调度部分

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

With LR scheduling

注意,在相同的网络和 LR 下,我们得到 88.4%。还要注意,到最后,损耗和精度图不再像以前那样波动。

因此,仅通过使用 LR 查找器和 LR 调度,我们就获得了超过 1%的准确性提高,使用了我们开始时使用的相同的 100 个时期。

结论和参考文献

使用 LR Finder 被证明有利于更快的训练和提高准确性。我们还用一个笔记本例子展示了如何使用 LR finder。LR 寻像器的代码在这里是。

如果你觉得这很有用,请访问我的笔记本/Github 获取完整代码。

加速 Python 代码:快速过滤和慢速循环

原文:https://towardsdatascience.com/speeding-up-python-code-fast-filtering-and-slow-loops-8e11a09a9c2f?source=collection_archive---------1-----------------------

列表理解、布尔索引和实时(JIT)编译的速度提高了 200 倍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by James Donovan on Unsplash

当探索一个新的数据集并希望进行一些快速检查或计算时,人们倾向于懒洋洋地编写代码,而不太考虑优化。虽然这在开始时可能是有用的,但等待代码执行的时间很容易超过正确编写所有内容所需的时间。

本文展示了在 Python 中如何加快计算速度的一些基本方法。以过滤数据为例,我们将讨论几种使用纯 Python、numpy、numba、pandas 以及 k-d-tree 的方法。

数据集的快速过滤

作为一个示例任务,我们将解决有效过滤数据集的问题。为此,我们将使用二维空间中的点,但这可以是 n 维空间中的任何东西,无论这是客户数据还是实验的测量结果。

让我们假设我们想要提取在一个在[0.2,0.4]和[0.4,0.6]之间的矩形中的所有的点。最简单的方法是对每个点进行循环,并检查它是否满足这个标准。从代码上看,这可能如下所示:首先,我们创建一个函数,用 numpy 在 n 维空间中随机分布点,然后创建一个函数在条目上循环。为了测量计算时间,我们使用 timeit 并使用 matplotlib 可视化过滤结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Loop: 72 ms ± 2.11 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

正如我们所看到的,对于测试的机器,大约需要。70 ms 从 100,000 个点的数据集中提取矩形内的点。

注意,当组合表达式时,您要使用逻辑 and (and)而不是按位 and ( &)。当第一个条件为假时,它停止求值。

虽然 numpy 很适合与大型 n 维数组交互,但是我们也应该考虑使用 numpy 对象所带来的额外开销。在这个特殊的例子中,我们没有使用任何数学运算来从 numpy 的矢量化中获益。

所以现在让我们用这个循环的纯 Python 实现来测试这个循环。这里的区别是使用元组列表而不是 numpy 数组。

Python loop: 27.9 ms ± 638 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

执行现在只需要大约。28 毫秒,所以不到以前执行时间的一半。这突出了当使用高度优化的包来执行相当简单的任务时可能出现的潜在性能下降。

Python 函数:列表理解、映射和过滤

为了进行更广泛的比较,我们还将针对 Python 中的三个内置方法进行基准测试:List comprehensions、Map 和 Filter。

  • 列表理解:一般来说,列表理解比循环执行得更好,因为它们不需要在每次迭代时调用 append 函数。
  • Map:这将一个函数应用于一个输入列表的所有元素。
  • Filter:这将返回一个元素列表,函数将为这些元素返回 True
List comprehension: 21.3 ms ± 299 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Filter:   26.8 ms ± 349 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Map:   27 ms ± 265 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

列表理解法略快。正如我们所预料的,这是因为不调用 append 函数节省了时间。与纯 Python 循环相比,map 和 filter 函数的速度并没有显著提高。

思考一下 70 ms 以上的第一个实现,为什么首先要使用 numpy?我们可以做的一件事是使用布尔索引。在这里,我们按列的方式对每个标准进行检查。然后,我们可以将它们组合成一个布尔索引,并直接访问该范围内的值。

Boolean index: 639 µs ± 28.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

使用布尔索引的解决方案只需要大约。640 秒,与我们目前测试的最快实现相比,速度提高了 50 倍。

走得更快:Numba

我们能更进一步吗?是的,我们可以。一种方法是使用 Numba :

Numba 使用行业标准的 LLVM 编译器库,在运行时将 Python 函数翻译成优化的机器代码。Python 中 Numba 编译的数值算法可以接近 C 或 FORTRAN 的速度。

如果使用 numpy,numba 的实现是非常容易的,如果代码有很多循环,那么 numba 的实现尤其高效。如果函数设置正确,即使用循环和基本的 numpy 函数,简单添加@njit decorator 将标记要在 numba 中编译的函数,并会提高速度。请随意查阅 numbas 文档,了解设置 numbas 兼容函数的细节。

请注意,我们使用的是引入了类型化列表的最新版本 Numba (0.45)。此外,请注意,我们在计时之前执行一次函数,不考虑编译时间。现在让我们看看这些函数在用 Numba 编译时是如何执行的:

Boolean index with numba: 341 µs ± 8.97 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Loop with numba:  970 µs ± 11.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

用 LLVM 编译函数后,甚至快速布尔滤波器的执行时间也减少了一半,只需要大约。更有趣的是,即使是从一开始效率就不高的循环现在也从 72 ms 加速到不到 1 ms,这凸显了 numba 对于优化较差的代码的潜力。

表格中的数据:熊猫

之前,我们已经看到数据类型会影响数据类型。人们必须在代码性能、简单接口和可读代码之间做出谨慎的选择。例如,Pandas 在处理表格数据方面非常有用。但是,这种数据结构会降低性能。为了客观地看待这个问题,我们还将比较 pandas 板载的过滤功能,如查询和评估以及布尔索引。

Pandas Query:  8.77 ms ± 173 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Pandas Eval:  8.23 ms ± 131 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Pandas Boolean index: 7.73 ms ± 178 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

可以说,执行时间比我们未优化的初始循环快得多。但是,它明显比优化版本慢。因此,它适用于初步探索,但随后应进行优化。

定量比较 I

为了更定量地比较这些方法,我们可以对它们进行比较。为此,我们使用 perfplot 包,它提供了一种很好的方法。

请注意,执行时间和数据大小都是对数级别的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

更多查询和更大的数据集

最后,我们将讨论可以用于更大数据集和使用更多查询的策略。到目前为止,我们总是在检查固定参考点时考虑计时。假设我们有一个点列表,而不是一个点,并且想要多次过滤数据。显然,如果我们可以在数据中使用某种顺序,例如当左上角有一个点时,只查询该特定角上的点,这将是有益的。

我们可以先对数据进行排序,然后使用索引选择一个子部分。这里的想法是,排序数组的时间应该由重复搜索较小的数组所节省的时间来补偿。

为了进一步增加复杂性,我们现在还在第三维空间中搜索,有效地在空间中切割出一个体素。因为我们只对计时感兴趣,所以现在我们只报告过滤后的数组的长度。

我们重写了 boolean_index_numba 函数,以接受形式为[xmin,xmax],[ymin,ymax]和[zmin,zmax]的任意参考体积。我们定义了一个名为 multiple_queries 的包装器来重复执行这个函数。比较将针对函数 multiple_queries_index ,该函数首先对数据进行排序,并且只将一个子集传递给boolean _ index _ numba _ multiple

Multiple queries:  433 ms ± 11.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Multiple queries with subset: 110 ms ± 1.66 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Count for multiple_queries: 687,369
Count for multiple_queries: 687,369

对于这个例子,执行时间现在减少到只有四分之一。速度增益与查询点的数量成比例。作为补充说明,最小和最大索引的提取相对较快。

更多结构:k-d-树

预先构造数据以增加访问次数的想法可以进一步扩展,例如,可以考虑对子集化的数据再次进行排序。人们可以考虑创建 n 维箱来有效地收集数据。

扩展这一思想并使用树结构来索引数据的一种方法是 k-d 树,它允许快速查找给定点的邻居。下面是来自维基百科的简短定义:

在计算机科学中,k-d 树是用于组织 k 维空间中的点的空间划分数据结构。k-d 树对于一些应用是有用的数据结构,例如涉及多维搜索关键字的搜索。

幸运的是,我们不需要自己实现 k-d 树,而是可以使用 scipy 中现有的实现。它不仅有一个纯 Python 实现,还有一个 C 优化版本,我们可以用它来实现这种方法。它带有一个名为 query_ball_tree 的内置功能,允许搜索特定半径内的所有邻居。当我们在给定点周围的正方形内搜索点时,我们只需要将闵可夫斯基范数设置为切比雪夫 (p='inf ')。

Tree construction: 37.7 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Query time:  86.4 µs ± 1.61 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Total time:  224 ms ± 11.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Count for k-d-tree: 687,369

从时间上我们可以看到,构建树花费了大约 40 ms,但是查询步骤只需要 100 s,因此比 numba 优化的布尔索引还要快。

注意,k-d-tree 只使用单一的距离,所以如果对矩形而不是正方形感兴趣,就需要缩放轴。也有可能将闵可夫斯基范数改变为例如在圆内而不是正方形内搜索。因此,可以通过对数变换轴来实现相对窗口搜索。

定量比较 II

我们将再次使用 perfplot 来进行更定量的比较。为此,我们将针对越来越多的点查询一百万个点。

请注意,我们在大范围内测试数据,因此性能图的执行时间可能会非常慢

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于此数据范围,kdtree、multiple_queries 和多重查询的索引版本之间的比较显示了预期的行为:在搜索较大的数据集时,构建树的初始开销或数据排序过重。对于较大的数据集,kdtree 的性能有望超过多查询的索引版本。

需要强调的是,由于 scipy 实现很容易接受 n 维数据,因此扩展到更多维也非常简单。

摘要

测试不同方法的过滤速度突出了如何有效地优化代码。执行时间从缓慢实施的 70 ms 以上到大约。使用布尔索引的优化版本需要 300 秒,性能提升超过 200 倍。主要调查结果可归纳如下:

  • 纯 Python 可以很快。
  • Numba 即使对于非优化的循环也非常有益。
  • Pandas onboard 函数可能比纯 Python 更快,但也有改进的潜力。
  • 在大型数据集上执行大型查询时,对数据进行排序是有益的。
  • 当有大型查询时,k-d-树提供了在 n 维空间中过滤的有效方式。

考虑到并行化,无论是在 CPU 还是 GPU 上,执行时间都可以进一步加快。注意,这些方法的内存占用在这些例子中没有考虑。当文件太大而无法在内存中加载时,将数据或生成器表达式分块会很方便。如果您发现有任何方法缺失或可能提供更好的结果,请告诉我。我很想知道还有什么其他方法可以实现快速过滤。

使用快速密集特征提取和 PyTorch 加速 CNN

原文:https://towardsdatascience.com/speedup-your-cnn-using-fast-dense-feature-extraction-and-pytorch-dc32acbf12ef?source=collection_archive---------13-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

早在 3 月份,我们开源了我们的“使用具有池化或跨越层的 CNN 的快速密集特征提取”的实现,尽管并不广为人知,但 2017 年 BMVC 发表的论文提供了一种高效而优雅的解决方案,介绍了如何在使用基于面片的卷积神经网络时避免计算冗余。所以在这篇文章中,我将解释这个模型是如何工作的,并展示如何在实际应用中使用它。

我将介绍两件事:首先,概述名为“使用具有池化或跨越层的 CNN 进行快速密集特征提取”的方法。第二,如何在现有的经过训练的补丁网络上使用这种方法来加快推理时间。

什么是基于补丁的方法?问题出在哪里?

基于小块的 CNN 通常应用于图像的单个小块,其中每个小块被单独分类。当试图在图像中相邻的重叠的小块上多次执行相同的 CNN 时,通常使用这种方法。这包括基于任务的特征提取,如相机校准、补丁匹配、光流估计和立体匹配。此外,还有不被视为特征提取的基于补丁的应用,如滑动窗口对象检测或识别。

在所有这种基于补丁的任务中,在相邻 CNN 的计算之间会有很多冗余。比如看下图。

在左边你可以看到一个简单的一维 CNN。从底部开始,每个像素在输出层中只贡献一个结果,没有任何冗余。在上相反,在右边,如果在一个图像的每个像素位置执行这个 CNN 来创建特征,许多中间层结果在网络之间被无缘无故地共享。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The numbers in nodes state how often a node is shared. The red connections show how the red node is shared. Pooling with stride 2 halves the output resolution. Thus, we need two pooling layers: the original one (blue) and one shifted by one pixel (green) to avoid halving the output resolution.

快速密集特征提取

这种方法的主要思想是,不是对图像中的每个小块分别执行我们的基于 CNN Cp (它是在训练小块 P 上训练的),而是让我们一次对输入图像 I 中的所有小块 P(x,y) 高效地执行它。

为了一致起见,让我们将输入图像 I 定义为宽度 Iw 和高度 Ih ,我们可以将宽度 Pw 和高度 Ph 的面片定义为以输入图像中的每个像素位置 (x,y),x∈0…Iw 1,y∈0…Ih 1为中心输出向量 O(x,y) = CP(P(x,y)) 是一个 k 通道向量,属于 (Ih,Iw,k) 维输出矩阵 O 包含对所有图像面片 P(x,y)执行 Cp 的结果

为此,我们可以创建一个网络 CI ,直接从计算,同时避免在每个图像块上独立执行 Cp 时出现的冗余。 CpCI 的架构差异如下图所示。这里,特征提取器中的所有池层都被多池层替换**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Architecture of Cp (Left) and CI (Right)

值得一提的是, CI 将给出与独立地在图像 I 的每个补丁上执行网络 Cp 相同的结果。但是, CI 运行速度更快,因为它避免了重叠补丁之间的冗余。

当我们处理层的类型时,让我们检查从 CpCI 的必要步骤——主要是普通层(没有汇集或跨步)和异常层(包括汇集或跨步)。

1.普通层

在没有跨越或合并的情况下, CpCI 的层是相同的,即

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是因为它们的输出不取决于输入的空间位置,而只取决于输入值本身。

2.异常层(包括汇集或跨步)

与普通层不同,跨层和池层必须明确处理。下图显示了池的主要问题:第一个面片 P(x,y) 与第二个面片 P(x+1,y) (绿色)需要不同的 2 x 2 池,因此不能共享池输出。

但是,补丁 P(x+2,y) 可以再次与原始池一起工作(蓝色)。 P(x,y)P(x + 2,y) 的重叠位置提供相同的结果,因此可以共享(亮黄色)。

对于该示例的概括将是, s 是汇集/步幅大小,并且 uv 是整数,面片 P(x,y)P(x+su,y+sv) 仍然共享由两个面片共享的像素的汇集输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Patches P at different image positions (in red). Sharing between patches that are using blue and the ones that are using green pooling is not possible

这一起创建了 s × s 不同的汇集情况,这些情况必须在我们的汇集层的输入I’上独立计算,其中I’是第 l- 层的输入图像。由于 s×s 池层将输出大小减小到 Iw/s Ih/s (具有输入大小 Iw Ih )很明显,要求 s × s 这样的输出仍然获得空间大小的输出 O

不同的池输出堆叠在标记为 M 的额外输出维度中。所有标注为通道的不同汇集输出现在将被后续层视为独立样本(类似于批次维度)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的动画更直观地展示了该过程是如何完成的,每个通道执行一次汇集,最终堆叠在 M.

3.解除警戒

有了一个多池层,我们得到一个输出 W ,尺寸 W = (M = s×s,Ih/s,Iw/s,k) ,我们希望将它反卷积到最终输出O = (Ih,Iw,k)* 。对于 2×2 合并,下面的图像直观地显示了取消撤销过程背后的直觉。这里,所有通道应该交错在一起,以生成最终输出 O *

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

On the left, the2×2 = 4 output images from 2×2 multipooling and on the Right, the final unwarping output O.

直接取消 warping 是复杂的,尤其是对于几个池层。这可能是以前的工作避免合并层的原因。然而,如果我们观察维度空间中的问题,它可以很容易地通过单独的转置和整形操作来解决。这样的操作被大多数深度学习框架支持为层。

我不会详细讨论扭曲过程是如何完成的,因为这远远超出了本文的范围。更多详情请参考论文

实验

作者展示了比较改进的网络 CI 和基于块的 CNN Cp 在图像的所有块上运行的基准测试结果。实验是在 GeForce GTX 泰坦 x 上进行的

从下表可以看出, Cp 的执行时间大致与图像像素成线性比例(如预期)。 CI 另一方面,对于较大的图像几乎不需要更多的时间。另一方面, CI 的内存消耗几乎线性增加。如果没有足够的内存可用,可以将输入图像分割成多个部分,并且可以单独处理每个部分。

检查加速栏可以清楚地看到 CI 的执行速度要快得多,尤其是在较大的图像上。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Speed benchmark for CI and Cp

让我们加速基于补丁的 CNN

在这里,我将解释如何使用我的实现“使用具有池化或跨越层的 CNN 的快速密集特征提取”来加速任何基于 CNN 的补丁。

项目结构很简单,您有两个实现:pytorch 和 tensforflow,每个都包含以下内容:

  • FDFE.py -实施文件中描述的所有方法层和预&后处理方法
  • BaseNet.py -这是指 你的 预训练 CNN Cp 在训练补丁 P 上的一个实现
  • SlimNet.py——这是指实施 CI
  • sample_code.py -试运行

1.实施您改进的网络— CI

为了使用您自己的预训练网络来运行补丁程序,您需要:

1.在BaseNet.net实施您的网络

2.相应修改SlimNet.py:

  • 根据顺序复制BsetNet.py模型层,例如
*self.conv1 = list(base_net.modules())[change_this_index]*
  • 对于每个MaxPool2d层,用决定的步幅值( sLn )代替multiMaxPooling
  • 根据模型中multiMaxPooling的数量,去除缠绕层
  • 不要移除以下图层 multiPoolPrepare,unwrapPrepare

2.在改进后的网络上运行示例代码

现在你应该sample_code.py确保项目正常运行。该测试生成大小为imH X imW的随机输入图像 I ,并在 CpCI 上对其进行评估。

该脚本继续并评估两个 CNN 输出之间的差异,并执行速度基准标记。 Cp 有两种操作模式

  • singlePatch mode-在将从输入图像 I 中裁剪的单个补丁pH x pW上运行 Cp
  • allPatches 模式——一次对多个补丁运行 Cp 。这里batch_size将决定一次评估多少补丁。

可能的参数—在sample_code.py中,有可以调整的初始参数,如图像高度、图像宽度、补丁宽度、补丁高度等…

3.我应该期待看到什么?

该脚本输出以下内容:

  • base_net Cp 输出和 slim_net 输出 CI — 之间的合计差异如上所述,这两个输出之间应该没有任何重大差异。
  • 对于 Cp, 每个补丁的平均评估
  • 对于 CI, 每帧总评价。即整个输入图像

预期的详细信息应如下所示:

*Total time for C_P: 0.017114248275756836 sec
------------------------------------------------------------
Averaged time for C_I per Patch without warm up: 0.0010887398617342114 sec
------- Comparison between a base_net over all patches output and slim_net -------
aggregated difference percentage = 0.0000000000 %
maximal abs difference = 0.0000000000 at index i=0,j=0
------------------------------------------------------------*

这就对了。你极大地提升了你的人际网络。就在这个例子中,我们将运行时间提高了大约 10 倍。

感谢

非常感谢下面这个人帮助我们发现并实现了这个方法。

Arnon Kahani——一位好朋友和优秀的 ML 工程师

结论

如果你对源代码感兴趣,可以在我的CNN 快速密集特征提取 GitHub 知识库中找到。

一如既往,如果您有任何问题或意见,请随时在下面留下您的反馈,或者您可以随时通过 LinkedIn 联系我。

在那之前,下一篇文章再见!😄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值