TowardsDataScience-博客中文翻译-2016-2018-二百四十二-

39 阅读1小时+

TowardsDataScience 博客中文翻译 2016~2018(二百四十二)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

更好地理解深度学习的最新进展

原文:towardsdatascience.com/recent-adva…

我希望生活在一个系统建立在严谨、可靠、可验证的知识之上,而不是炼金术之上的世界。[……]简单的实验和简单的定理是帮助理解复杂的更大现象的积木

这种呼吁更好地理解深度学习的是阿里·拉希米在 2017 年 12 月 NIPS 举行的时间考验奖颁奖典礼的核心。通过比较深度学习和炼金术,阿里的目标不是解散整个领域,而是“开启对话”。这个目标肯定已经实现而人们还在争论我们目前的深度学习实践应该被认为是炼金术、工程学还是科学。

七个月后,机器学习社区再次聚集,这一次是在斯德哥尔摩举行的机器学习国际会议(ICML)。有超过 5000 名参与者和 629 篇论文发表,这是关于基础机器学习研究的最重要事件之一。而深度学习理论成为了大会最大的议题之一。

这种重新燃起的兴趣在第一天就显露出来了,大会最大的房间之一挤满了机器学习从业者,他们准备听 Sanjeev Arora 关于深度学习理论理解的教程。在他的演讲中,普林斯顿大学计算机科学教授总结了深度学习理论研究的当前领域,将它们分为四个分支:

  • 非凸优化:如何理解与深度神经网络相关的高度非凸损失函数?为什么随机梯度下降甚至会收敛?
  • 过度参数化和泛化:在经典统计理论中,泛化依赖于参数的个数而不是深度学习。为什么?我们能找到另一个好的概括方法吗?
  • 深度的作用:深度是如何帮助神经网络收敛的?深度和概括有什么联系?
  • 生成模型:为什么生成对抗网络(GANs)如此有效?我们可以用什么理论性质来稳定它们或避免模式崩溃?

在这一系列的文章中,我们将基于最近的论文尝试在这四个领域建立直觉,特别关注 ICML 2018。

这第一篇文章将关注深度网络的非凸优化的奥秘。

非凸优化

我敢打赌,你们中的很多人都尝试过从头开始训练自己的深层网络,并且因为无法让它发挥作用而对自己感觉很糟糕。我不认为这是你的错。我觉得是梯度下降的错。

阿里·拉希米在 NIPS 的演讲中以挑衅的口吻说道。随机梯度下降(SGD)确实是深度学习的基石。它应该找到一个高度非凸优化问题的解决方案,并且理解它何时工作以及为什么工作,是我们在深度学习的一般理论中必须解决的最基本的问题之一。更具体地,对深度神经网络的非凸优化的研究可以分为两个问题:

  • 损失函数是什么样的?
  • SGD 为什么会收敛?

损失函数是什么样的?

如果我让你想象一个全局最小值,你脑海中浮现的第一个图像很可能是这样的:

这很正常。在 2D 世界中,找到这样的问题并不罕见,在全局最小值附近,你的函数将是严格凸的(这意味着海森矩阵在这一点上的两个特征值将都是严格正的)。但在一个有数十亿参数的世界里,就像深度学习中的情况一样,围绕全局最小值的方向没有一个是平坦的可能性有多大?或者等价地,hessian 不包含单个零(或几乎零)特征值?

Sanjeev Arora 在他的教程中的第一个评论是,损失函数的可能方向的数量随着维度呈指数增长。

那么,直觉上,似乎一个全局最小值将不会是一个点,而是一个连通的流形。这意味着,如果你已经达到了一个全局最小值,你应该能够在一条平坦的路径上行走,在这条路径上所有的点也是最小值。海德堡大学的一个团队已经在大型网络上通过实验证明了这一点,在他们的论文中,神经网络能源领域基本上没有障碍【1】。他们提出了一个更普遍的说法,即任何两个全局最小值都可以通过一条平坦的路径连接起来。

已经知道 MNIST 的 CNN 或 PTB 的 RNN 就是这种情况[2],但这项工作将这种知识扩展到更大的网络(一些 DenseNets 和 ResNets ),这些网络在更高级的数据集(CIFAR10 和 CIFAR100)上进行训练。为了找到这条路径,他们使用了一种来自分子统计力学的启发式方法,称为 AutoNEB。这个想法是在你的两个极小值之间创建一个初始路径(例如线性),并在该路径上放置枢轴。然后反复修改枢轴的位置,使每个枢轴的损失最小化,并确保枢轴之间的距离保持不变(通过用弹簧模拟枢轴之间的空间)。

如果他们没有从理论上证明这一结果,他们会对为什么存在这样的路径给出一些直观的解释:

如果我们扰动一个单一的参数,比如说增加一个小常数,但让其他参数自由适应这种变化,以尽可能减少损失,有人可能会说,通过某种程度的调整,无数的其他参数可以“弥补”施加在其中一个参数上的变化

因此,本文的结果可以帮助我们通过超参数化和高维空间的透镜以不同的方式看到最小值。

更一般地,当考虑神经网络的损失函数时,你应该始终记住在给定点可能的方向的数量是巨大的。另一个结果是鞍点必须比局部最小值丰富得多:在一个给定的(临界)点,在数十亿个可能的方向中,很可能找到一个向下的方向(如果你不在全局最小值中)。这种直觉在 NIPS 2014 年发表的一篇论文中得到严格的形式化和经验证明:识别和攻击高维非凸优化中的鞍点问题 [6]

为什么 SGD 会收敛(或者不收敛)?

深度神经网络优化中的第二个重要问题与 SGD 的收敛特性有关。虽然这种算法长期以来被视为梯度下降的更快但近似的版本,但我们现在有证据表明 SGD 实际上收敛到更好、更一般的最小值[3]。但是我们能形式化它并定量解释 SGD 逃离局部极小值或鞍点的能力吗?

SGD 修改了损失函数

论文另一种观点:新币何时逃脱局部最小值?【4】表明执行 SGD 相当于对卷积(从而平滑)损失函数进行常规梯度下降。从这个角度出发,在一定的假设下(作者证明这在实践中经常是正确的),他们证明了 SGD 将设法摆脱局部极小值,并收敛到全局极小值周围的一个小区域。

SGD 受随机微分方程支配

另一种真正改变了我对这种算法的看法的 SGD 方法是连续 SGD 。这个想法是由 Yoshua Bengio 在他的关于随机梯度下降、平坦性和一般化的演讲中提出的,在 ICML 非凸优化研讨会上发表。SGD 没有移动损失函数上的一个点,而是一个点云,或者换句话说,一个分布

Slide extracted from the presentation On stochastic gradient descent, flatness and generalization, by Y. Bengio, at ICML 2018. He presented an alternative way to see SGD, where you replace points by distributions (clouds of points)

这个点云的大小(即相关分布的方差)与因子 learning_rate / batch_size 成比例。Pratik Chaudhari 和 Stefano Soatto 在《机器学习中的几何》研讨会上发表的论文“随机梯度下降执行变分推理,收敛到深度网络的极限环”【5】中给出了一个证明。这个公式非常直观:低批量意味着非常嘈杂的梯度(因为是在数据集的非常小的子集上计算的),高学习率意味着嘈杂的步骤。

将 SGD 视为随时间移动的分布的结果是,控制下降的方程现在是随机偏微分方程。更确切地说,在某些假设下,[5]表明这个支配方程实际上是一个 福克-普朗克方程

Slide extracted from the presentation High-dimensional Geometry and Dynamics of Stochastic Gradient Descent for Deep Networks, by P. Chaudhari and S. Soatto, at ICML 2018. They showed how to pass from a discrete system to a continuous one described by the Fokker-Plank equation

在统计物理学中,这种类型的方程描述了受到阻力(使分布漂移,即移动其平均值)和随机力(使分布扩散,即增加其方差)作用的粒子的演化。在 SGD 中,阻力由真实梯度模拟,而随机力对应于算法固有的噪声。正如你在上面的幻灯片中所看到的,扩散项与温度项成正比T = 1/β= learning _ rate/(2 * batch _ size),这再次表明了这个比值的重要性!

Evolution of a distribution under the Fokker-Planck equation. It drifts on the left and diffuses with time. Source: Wikipedia

使用这个框架,Chaudhari 和 Soatto 证明了我们的分布将单调收敛到某个稳定分布(在 KL-divergence 的意义上):

One of the main theorems of [5], proving monotonic convergence of the distribution to a steady state (in the sense of the KL divergence). The second equation shows that minimizing F is equivalent to minimizing a certain potential ϕ as well as maximizing the entropy of the distribution (trade-off controlled by the temperature 1/β)

上面的定理中有几个有趣的地方需要评论:

  • 被 SGD 最小化的泛函可以重写为两项之和(等式)。11):一个势φ的期望,和分布的熵。温度 1/β 控制着这两项之间的权衡。
  • 潜在的φ仅取决于数据和网络的架构(而不是优化过程)。如果它等于损失函数,SGD 将收敛到一个全局最小值。然而,论文表明这种情况很少发生,知道φ离损失函数有多远就会告诉你你的 SGD 收敛的可能性有多大。
  • 最终分布的熵取决于比率学习速率/批量大小(温度)。直观地说,熵与分布的大小有关,高温通常会导致分布具有高方差,这通常意味着平坦的最小值。由于平坦的最小值通常被认为是更好的概括,这与经验发现一致,即高学习和低批量通常导致更好的最小值。

因此,将 SGD 视为随时间移动的分布向我们展示了 learning_rate/batch_size 在收敛性和泛化能力方面比每个分离的超参数更有意义。此外,它能够引入与融合相关的网络潜力,这可以为架构搜索提供良好的度量。

结论

寻找深度学习理论的过程可以分为两个部分:首先,通过玩具模型和实验,建立关于它如何以及为什么工作的直觉,然后将这些直觉表达成数学形式,这可以帮助我们解释我们当前的结果并做出新的结果。

在这第一篇文章中,我们试图传达神经网络的高维损失函数和 SGD 的解释的更多直觉,同时表明新类型的形式主义正在以具有深度神经网络优化的真正数学理论为目标而建立。

然而,尽管非凸优化是深度学习的基石,但它的成功主要来自于它在大量层和参数的情况下仍能很好地概括。这将是下一部分的目标。

参考

[1]费利克斯·德拉克斯勒、坎比斯·韦施吉尼、曼弗雷德·萨尔姆霍弗、弗雷德·汉普雷希特。神经网络能源格局基本无障碍ICML 2018。

[2]丹尼尔·弗里曼,琼·布鲁纳。半整流网络优化的拓扑与几何,a rXiv:1611.01540 ,2016。

[3]尼蒂什·什里什·凯什卡尔、迪伊瓦萨·穆迪格雷、豪尔赫·诺切达尔、米哈伊尔·斯梅兰斯基、平·塔克·唐露晓。关于深度学习的大批量训练:泛化差距与尖锐极小ICLR 2017

[4]罗伯特·克莱恩伯格,李沅芷,杨远。另一种观点:新币何时逃脱局部最小值?ICML 2018

[5]斯特凡诺·索阿托·普拉蒂克·乔德里。随机梯度下降执行变分推理,收敛到深度网络的极限环, ICLR 2018

[6]扬·多芬、拉兹万·帕斯卡努、卡格拉尔·古尔切雷、赵京贤、苏亚甘古利、约舒阿·本吉奥。识别并攻克高维非凸优化中的鞍点问题NIPS 2014

用 Scala 识别手写数字

原文:towardsdatascience.com/recognising…

doddle-model: immutable machine learning in Scala.

在过去的几个月里,我开始大量编写 Scala 代码,对于从 Python 移植过来的人来说,我不得不承认,在静态类型的环境中操作比我以前愿意承认的要安全得多。

我的 Python 之旅的主要原因是它的机器学习生态系统,当我开始研究 Scala 中 ML 的状态时,我意识到这两种语言在可用库方面存在巨大差距。特别是,我在寻找一个 Scala 来替代 Python 中的 scikit-learn,公平地说,当我发现没有 Scala 时,我感到很沮丧。当我想起我可以开始自己建造它时,我的失望很快变成了渴望,我也确实这样做了。

这篇文章有两个目的:我想传播关于的消息,并向非 ML 受众展示如何以一种高度直观而非技术性的方式解决识别手写数字的问题。

介绍

这个库叫做 doddle-model ,这个名字是为了强调它的易用性。

doddle 英式,非正式:非常简单的任务,例如“这台打印机很容易安装和使用。”

它本质上是一个内存中的机器学习库,可以总结为三个主要特征:

  • 它建立在 Breeze 之上
  • 它提供了不可变对象,这些对象在并行代码中使用很容易
  • 它通过一个类似 scikit-learn 的 API 公开其功能

还应该指出的是,该项目正处于早期发展阶段,任何形式的贡献都非常感谢。

问题定义

假设你的奶奶让你帮她将一本旧的手写电话簿数字化,将联系人保存在手机上。不幸的是,这个列表很长,作为一个精明的工程师,你很快意识到像这样重要的任务必须自动化。你还记得听说过一个流行的带标签的手写数字数据库,其中包含 70,000 张图片及其相应的标签,你也许可以用它来帮助你的祖母。你可以看看图 2** 中的一些例子。**

Image 1: Automate all the things!

我们将假设我们已经找到了一种方法来扫描电话簿,并将每个电话号码转换为一组 28x28 灰度图像,其中每个图像代表一个数字。此外,我们将忽略一个事实,即我们不知道如何数字化手写信件,因此将只关注数字本身。

给定当前场景,我们感兴趣的是设计一个函数,它将 0 到 255 之间的 784 个整数的向量作为输入,并在输出端产生一个 0 到 9(我们的数字)之间的整数。请记住,灰度图像只包含强度信息,因此 28×28 像素中的每个像素都是一个整数(我们只需将 28×28 矩阵展平为一个向量,以获得 784 个值)。

Image 2: The digits we scanned from the telephone book (source: en.wikipedia.org/wiki/MNIST_…).

第一种方法

**我们将用 x 表示输入像素向量,用 y 表示输出标量。那么我们要寻找的函数就是f(x)= y。为了方便起见,我们将输出建模为一个分类变量,这无非是说它的值属于一组无序类别中的任何一个,即在我们的例子中是{0,1,2,3,4,5,6,7,8,9}中的一个。如果你看一下分类分布,你可以看到它是由一个概率向量来参数化的,其中每个元素代表第 k 个分类的概率,并且它们的总和为 1,因为不存在其他分类。我们可以将这一点结合到我们的函数中,通过说 f 的输出不是单个标量 y ,而是具有 10 个元素的向量 yProb ,其中每个元素代表每个可能数字的概率。我们的函数于是变成了f(x)=yProb

在这个阶段,我们知道 f 的输入是什么(一个展平的图像向量 x )并且我们知道输出应该是什么样子(一个有 10 个元素的概率向量 yProb )。现在我们必须弄清楚如何定义函数本身。我们将首先考虑如何为单个类别获得一些任意的实值分数,然后使用这一知识为所有类别获得 10 个实值分数,最后确保这些分数在范围[0,1]内,并且它们的总和确实为 1,因为它们必须表示概率。

我们的功能定义

从向量 x 获得实值标量的一种可能方法是简单地计算 x 中的值的线性组合。让我们看看如何把它写下来。记住 x 由 784 个整数组成,即 pixel_1pixel_2 ,…, pixel_783pixel_784 ,所以要计算这些的线性组合,我们需要 785 个权重 w_i 来获得 z = w_0 如果我们在 x 的开头插入一个值为 1 的元素,我们可以把这个记为 xw点积(所有权重 w_i 放在一个向量中),得到 z = x

这很容易,但是目前,我们只有一个分数 z,实际上我们在输出端需要一个向量 10。嗯,我们可以做的最简单的事情是计算 10 次分数,而不是一次,每次用不同的向量 w 。我们可以这样写下来:z _ 0=xw _ 0z _ 1=xw _ 1z_10 = x 同样,如果我们将一个矩阵中的向量w _ Iw*(每个 w_iW 中变成一列),我们可以将此记为向量矩阵乘法z=*****

让我们看一下我们的函数 f 目前的样子,来回顾一下我们到目前为止所做的事情。并且不要担心不知道什么值 W 持有,我们马上就能到达那里。在图像 3 的最左侧是我们的展平图像向量 x ,带有前置 1,它乘以矩阵 W 以获得向量 z ,该向量代表 10 个可能类别(数字)中每一个的实值分数。请注意,我们对每个类别都有一组权重(每个列在 W 中),总共有 10785 个权重。*

Image 3: Our current function that produces 10 real-valued scalars from a flattened image vector.

我们的函数定义中只缺少一个需求。我们在本节的开头指定了 z 的元素必须在范围[0,1]内,并且总和必须为 1,才能被解释为概率。事实证明, softmax 函数正是我们所寻找的。这很简单,我们不会在这里进行过多的描述,我们要知道的是,它取我们的 z 并返回 yProb ,可能看起来像这样yProb=[0.024,0.064,0.175,0.475,0.024,0.064,0.175]。如果您对 softmax 函数的定义感兴趣,请点击提供的链接。****

我们终于知道如何定义我们的 f !这是个好消息,我们几乎可以帮助我们的奶奶解决手机里没有号码的问题了。最终的函数定义如下:f(x)=soft max(**xw)。它采用一个展平的图像向量 x (嗯,我们不能忘记插入 1 作为第一个元素)并返回 10 个可能数字的概率向量。厉害!但是等等,我们还没说重量 W 呢?!****

重量

权重 W 在 ML 文献中称为模型参数, f 称为模型。如果我们随机初始化 W ,我们很可能会得到一个非常糟糕的模型——预测的数字(向量 yProb 中具有最大值的类别)大部分时间都是错误的,我们希望找到一种更好的方法。

我们还有一件事要处理:带标签的数据集!凭直觉,我们将试图找到一组权重 W 来很好地处理带标签的数据(我们将称之为训练数据,搜索参数将被称为训练),并希望它们也能很好地处理新数据(从我们祖母的电话簿中扫描的数字)。在这一点上,经过战斗考验的数据科学家可能正在做鬼脸,因为我们不希望仅仅希望它会做得很好,我们希望测量它,并确保模型不会犯太多错误。我们不希望模型只是记忆我们的训练示例,而是学习关于数字 2 或数字 9 的概念,并有能力识别这些数字的新的、看不见的图像。**

让我们定义一下在我们的场景中是什么意思。我们可以做的是计算我们的模型从训练集中正确获得的图像的比例。这个度量被称为分类准确度,我们可以计算它,因为我们知道训练集中的正确标签是什么。找到 W 的一种简单方法是简单地尝试随机值并返回具有最高精确度的值。但我们肯定能比随机搜索做得更好,对吧?**

同样,我们不想涉及太多的细节,但是有一种迭代优化算法可以找到函数的最小值(查看维基百科页面上的 gradient descent 了解更多信息)。这里的想法是,我们指定一个误差函数*——我们衡量我们的模型在训练集上的预测有多差——并根据参数 W 将其最小化。梯度下降将会给我们一些 W 在误差函数方面对我们的训练数据最有效。这个概念在图 4 中被形象化:表面是我们的误差函数,X 轴和 Y 轴是我们在中的两个值(是的,我们实际上有 10785,但为了形象化,假设我们只有 2 个),算法沿着红色路径尝试不同的 X 和 Y,最后返回函数值最低的一些 X 和 Y。这个值被称为函数最小值,它在我们的例子中是有意义的,因为我们在寻找误差函数的最小值。****

Image 4: Gradient descent (source: blog.paperspace.com).

不幸的是,我们不能使用像负分类精度这样的东西作为误差函数,因为梯度下降需要一个可微函数,而精度不是这样。我们可以使用的方法叫做对数损失,它简单地说我们计算正确类别概率的负对数。我们知道正确的类别是什么,因为我们的训练集是有标签的,所以我们只需从对应于该类别的 yProb 中取值(姑且称之为 yProb )并计算- log ( yProb )。您可以在图 5 中看到,随着 yProb 越来越接近 1,该值越来越接近 0。最终的误差函数是我们训练集中所有样本的对数损失的总和(或平均值)。请注意,log loss 还有其他令人满意的属性,这些属性使它成为我们的错误函数的更好选择,但是我们现在不要考虑细节。

Image 5: Logarithmic loss function (source: wiki.fast.ai).

这实际上是我们第一种方法的精髓,没有什么不能用几行 Scala 实现的!为了回顾我们已经完成的工作,让我们再看一下最重要的步骤:

  • 我们设计了一个函数 f ,它将一个展平的图像向量 x 作为输入,并返回一个包含 10 个元素的向量 yProb ,其中每个元素代表每个可能数字的概率(参数 W 被合并到f】**
  • 我们已经使用了 f 和一个带标签的训练集,通过计算训练集中所有示例的平均对数损失来衡量我们的参数 W 有多差
  • 我们使用了一种优化算法,该算法给出了一些在对数损失误差函数方面表现最佳的 W
  • 我们希望返回的 W 也能适用于电话簿中的数字

图书馆

接下来,我们将看看如何通过使用 doddle-model 库,用几行 Scala 实现我们到目前为止讨论的所有内容。老实说,该库的具体实现与所介绍的技术有一些重要的不同,这些不同使得整个方法在数值上更稳定、更有效,但是我们现在没有必要讨论这些。让我们直接进入代码。

我们将使用来自mldata.org的标记数据集,它由 70.000 个标记图像组成。我们将模拟训练和测试数据,仅使用 60.000 张图像进行训练,剩余的 10.000 张用于评估性能。

让我们加载数据:

如果我们检查形状,我们可以看到xTrainxTest中的行代表我们的展平图像向量(每行是一个图像向量),列代表像素值。yTrainyTest持有实际标签,例如前 10 个训练图像上有数字 1、9、2、2、7、1、8、3、3 和 7。

训练模型就像下面这样简单:

做预测看起来像是:

现在我们有了模型预测yTestPred和我们知道正确的真实标签yTest,我们可以检查模型的预测是否有任何意义。让我们先来看看测试数据的前 10 位数字以及我们对它们的预测。

那看起来像它能工作!让我们计算整个测试数据集的分类精度:

还不错!我们能够正确地分类 10,000 幅在训练期间没有使用的图像中的 92.22%。这仍然会让我们的奶奶的手机号码不正确,但这是一个很好的起点,我们可以逐步改善!

表演

该库的开发考虑到了性能。参见 doddle-benchmark 存储库,其中展示了与 scikit-learn 的一些并行性能比较。

Breeze(底层数字处理库)利用 netlib-java 访问硬件优化的线性代数库,这是快速执行的主要原因。更多详情请参见自述文件的性能部分。

结论

如果你已经做到了这一点,给自己一个鼓励,因为你对 softmax 分类器以及如何训练它有相当多的了解。如果你对更多感兴趣,网上有很多免费的好课程和资源。

如果您有兴趣了解更多关于老态龙钟模型的信息,可以看看老态龙钟模型示例库。它包含当前实现的所有内容的代码示例,应该作为一个很好的起点。

如果你有任何问题、建议、评论或只是想聊天,你可以在 GitterTwitter 上给我留言。

在机器学习中,识别上下文仍然很难——下面是解决这个问题的方法

原文:towardsdatascience.com/recognizing…

机器学习已经走过了漫长的道路,因为它涉及到识别人脸,物体和其他文字。但它仍然有点纠结的地方是语境。

定义上下文

考虑视频质量控制问题。现在有很多工具可以对视频文件进行自动质量控制。它们在检测丢失磁道、文件损坏或元数据不匹配的问题方面做得很好。然而,他们仍然很难告诉你一个图像是否是颠倒的。

当图像颠倒时,人类可以立即分辨出来,但计算机却很难分辨。原因是因为倒挂是上下文相关的。

Your brain is telling you that this is wrong

考虑上面的图像。你的大脑告诉你这个图像是颠倒的。有点不对劲。如果这是一个质量控制过程,你应该把它退回去。你也可能会想,这对于计算机来说应该不难理解。

为什么电脑看不到颠倒的东西?

也许你可以训练一个机器学习模型来检测有方向的物体。或者试着用很多很多上下颠倒的图片。对于某些用例,这可能行得通。但是当你的模型看到这样的图像时会发生什么呢:

i.pinimg.com/736x/b1/25/…

这个图像实际上应该是上下颠倒的。作为一名人类质量控制操作员,你不会多看一眼。然而,机器学习模型不会理解上下文,因此会出错。

人类非常擅长上下文。计算机仍然需要一些帮助。

但是并不是所有的都失去了

当你可以明确你的用例时,机器学习是最好的。比方说,我想把我所有的图片按照某种上下文分类,比如“喜怒无常”。

为了尝试这个,我使用了机器盒子的 Tagbox。让一个可训练的图像识别框在你的计算机上运行大约需要 5 秒钟。

第一步,找一些“喜怒无常”的例子。我从这张图片开始:

Very moody!

我用 Tagbox 的控制台上传了这张照片,并将其标记为“moody”。

Tagbox by Machine Box

几毫秒后,Tagbox 就有了我称之为“喜怒无常”的想法。但是一张照片是不够的。所以我找到了另一张看起来相似的照片,并在 Tagbox 控制台中用另一张我认为“喜怒无常”的照片重复这个过程。

Very moody

在向 Tagbox 展示了两个“moody”的例子后,我准备开始在其他图像上测试它。

我使用 Tagbox 控制台手动执行这个任务——当然,API 是用来将这个工作流构建到您的产品和服务中的。

让我们用这张图片来检查我们的模型,看看结果。

Tagbox results

成功!在给它看了两张照片后,它已经了解了一些我的照片的背景,我可以用它们来对剩下的照片进行分类。实际上,我可能想再花 20 分钟进行测试和训练,只是为了确保模型足够健壮以满足我的需求。

关键的一点是,当你心中有一个特定的用例时,机器学习的功能最好,并且可以指导模型应用于该用例。(目前)还没有一种放之四海而皆准的模式。模型越通用,你的用例就需要越通用。

什么是机器盒子?

Machine Box 将最先进的机器学习功能放入 Docker 容器中,这样像您这样的开发人员可以轻松地将自然语言处理、面部检测、对象识别等功能融入其中。到您自己的应用程序中。

这些盒子是为了扩展而建造的,所以当你的应用真正起飞的时候,只需要水平地添加更多的盒子,直到无限甚至更远。哦,它比任何云服务都便宜**(而且可能更好)……而且你的数据不会离开你的基础设施。**

有戏让我们知道你的想法。

用注意递归神经网络识别语音命令

原文:towardsdatascience.com/recognizing…

如何为语音应用实现一个可解释的神经模型

Voice spectrogram

语音识别已经成为人机界面(HCI)不可分割的一部分。它们出现在谷歌助手、微软 Cortana、亚马逊 Alexa 和苹果 Siri 等个人助理中,以及自动驾驶汽车 HCI 和员工需要佩戴大量保护设备的活动中(例如,像石油和天然气行业)。

Waveform, neural attention weights and mel-frequency spectrogram for word “one”. Neural attention helps models focus on parts of the audio that really matter.

这种处理大部分是在云中完成的,使用的是强大的神经网络,这些网络经过了海量数据的训练。然而,语音命令模型能够识别单个单词,如“开始”、“停止”、“向左”或“向右”(或“嘿,谷歌”、“Alexa”、“回声”),由于各种原因,通常在本地运行。首先,将从给定设备获取的所有音频连续流式传输到云中会变得非常昂贵。其次,这在一些应用中甚至是不可能的:考虑海上平台中的操作员想要使用简单的语音命令来控制辅助机器人的情况:可能没有可用的互联网连接或者等待时间可能太长。

在这项工作中,使用了谷歌语音识别数据集。它包含各种单词的长度为 1s 的简短音频剪辑,是学习如何将深度学习应用于语音的绝佳起点。

人声基础

在分析人类声音时,一个非常重要的方面是滤波器,它构成了一个选择性频率传输系统,允许能量通过一些频率,而不允许其他频率。如下图所示,咽、口腔和嘴唇在人类说话中起着重要的作用。

声音共振峰揭示了能量更集中的频率区域。它们是声音频谱中振幅较大的峰值,是元音发音时声道所采用的特定结构所固有的。当说出一个单词时,共振峰与声道的自然共振频率相关联,并取决于舌头相对于内部结构和嘴唇运动的位置。该系统可以近似为具有一个封闭末端(喉)和一个开放末端(唇)的管,通过舌、唇和咽的运动来修改。发生在这个管腔中的共振叫做共振峰。

有趣的是,深度学习实践者很快决定忽略所有这些信息。在百度工作期间,研究员吴恩达继续说,音素是声音的最小组成部分,没有关系。事实上,在一定程度上,重要的是,如果时间间隔足够小,语音大多是一个(准)周期信号。这就产生了忽略信号的相位,只使用其功率谱作为信息源的想法。事实上,声音可以从其功率谱重建(例如,使用 Griffin-Lim 算法或神经声码器)证明了这一点。

尽管在处理原始波形方面已经做了大量的工作(例如,最近的脸书研究中的),但是频谱方法仍然很普遍。目前,预处理音频的标准方法是从原始波形计算具有给定跳跃大小的短时傅立叶变换(STFT)。结果称为频谱图,是一个三维排列,显示频率分布和音频强度作为时间的函数,如下图所示。另一个有用的技巧是“拉伸”低频以模仿人类的感知,人类对高频的变化不太敏感。这可以通过计算梅尔频率系数(MFC)来实现。如果你感兴趣的话,有很多详细解释 MFC 的在线资源(维基百科是一个很好的开始)。

Diphtongs explicited in word “saia”. Topmost: phonetic notation. Middle: sound waveform. Bottom: Spectrogram with fundamental frequency highlighted in blue and the three first formants highlighted in red.

神经注意力结构

既然语音处理的基础是已知的,就有可能提出一种神经网络,它能够处理命令识别,同时仍然在可训练参数的数量方面保持较小的足迹。一个有注意力的循环模型带来各种好处,比如:

  • 通过计算输入的重要性权重,使模型可解释;
  • 学会识别音频的哪一部分重要;
  • 递归神经网络(RNN)架构(如长短期记忆——LSTM 或门控递归单元——GRU)已经证明能够携带信息,同时仍然控制消失/爆发梯度;
  • 由于响应延迟 1 秒通常是可以接受的,因此双向 RNN 允许模型提取音频给定点的过去和未来相关性。

Recurrent neural network with attention mechanism. Numbers between [brackets] are tensor dimensions. raw_len is WAV audio length (16000 in the case of audios of length 1s with a sampling rate of 16kHz). spec_len is the sequence length of the generated mel-scale spectrogram. nMel is the number of mel bands. nClasses is the number of desired classes. The activation of the last Dense layer is softmax. The activation of the 64 and 32 dense classification layers is the rectified linear unit (relu).

所提出的架构使用卷积来提取短期依赖性,使用 rnn 和注意力来提取长期依赖性。实施细节可以在这个资源库中找到。

在 Google Speech 数据集上,当试图识别 20 个单词中的一个,沉默或未知时,所提出的模型达到了 94%以上的准确率。此外,注意力层使模型具有可解释性。例如,在单词“右”的情况下,注意网络将大量注意力放在从 ri 的过渡上。考虑到在某些情况下 t 可能听不到,这是非常直观的。还要注意,注意力权重被放置在过渡中的事实并不意味着模型忽略了音频的其他部分:双向 RNN 带来了来自过去和未来的信息。

Waveform, attention weights and mel-frequency spectrogram of word “right”

结论

语音命令识别存在于各种设备中,并被许多 HCI 接口所利用。在许多情况下,希望获得能够在本地运行的轻量级和高精度的模型。这篇文章解释了一种可能的 RNN 架构,它在 Google 语音识别任务上实现了最先进的性能,同时在可训练参数方面仍然保持了较小的影响。源代码可在 github 上获得。它使用谷歌语音命令数据集(v1 和 v2)来演示如何训练能够识别例如 20 个命令加上静音或未知单词的模型。

该架构能够提取短期和长期的依赖性,并使用注意机制来查明哪个区域具有最有用的信息,然后将这些信息提供给一系列密集层。

在工程应用中,能够解释哪些特性用于选择特定类别是很重要的。如上所示,注意机制解释了音频的哪些部分对于分类是重要的,并且还匹配元音过渡区域与识别单词相关的直觉。为了完整起见,混淆矩阵可以在本文中找到,并表明如果没有额外的上下文,单词对 tree-three 和 no-down 很难识别。

参考

语音命令:有限词汇语音识别数据集arxiv.org/abs/1804.03…

语音命令识别的神经注意模型arxiv.org/abs/1808.08…

专业男性歌剧演唱者的共振峰调谐策略,《声音杂志》27(3)(2013)278–288。网址http://www . science direct . com/science/article/pii/s 089219971200209320

腰带和中性风格歌唱之间的呼吸和声学差异,《声音杂志》29(4)(2015)418–5。网址http://www . science direct . com/science/article/pii/s 089219971400204

在语音分析中估计预期基频的稳健频域方法,载于国际创新科学与研究杂志

wav2letter++最快的开源语音识别系统arxiv.org/abs/1812.07…

使用深度学习以 98%的准确率识别交通标志

原文:towardsdatascience.com/recognizing…

Stop Sign

这是 Udacity 自动驾驶汽车工程师纳米学位 第一学期的项目 2。你可以在github上找到与这个项目相关的所有代码。你也可以阅读我关于项目 1 的帖子: 用计算机视觉检测车道线 只需点击链接。

交通标志是道路基础设施不可或缺的一部分。它们为道路使用者提供关键信息,有时是令人信服的建议,这反过来要求他们调整自己的驾驶行为,以确保他们遵守目前实施的任何道路法规。如果没有这些有用的标志,我们很可能会面临更多的事故,因为司机不会得到关于他们可以安全行驶多快的关键反馈,或者关于道路施工、急转弯或前方学校路口的信息。在当今时代,每年大约有 130 万人死于交通事故。如果没有我们的路标,这个数字会高得多。 自然,自动驾驶汽车也必须遵守道路法规,因此识别理解交通标志。

传统上,标准的计算机视觉方法被用于检测和分类交通标志,但是这些需要大量和耗时的手工工作来手工制作图像中的重要特征。相反,通过对这个问题应用深度学习,我们创建了一个可靠地对交通标志进行分类的模型,通过本身学习识别最适合这个问题的特征。在这篇文章中,我展示了我们如何创建一个深度学习架构,该架构可以在测试集上以接近 98%的准确率识别交通标志。

项目设置

数据集分为训练集、测试集和验证集,具有以下特征:

  • 图像为 32(宽)x 32(高)x 3 (RGB 颜色通道)
  • 训练集由 34799 幅图像组成
  • 验证集由 4410 幅图像组成
  • 测试集由 12630 幅图像组成
  • 共有 43 个等级(如限速 20 公里/小时、禁止进入、道路颠簸等。)

此外,我们将使用 Python 3.5 和 Tensorflow 来编写代码。

图像和分布

您可以在下面看到来自数据集的图像示例,标签显示在相应图像行的上方。他们中的一些很暗,所以我们将在稍后提高对比度。

Sample of Training Set Images With Labels Above

正如下面的直方图所示,训练集中的各个类之间也存在明显的不平衡。有些班级的图片不到 200 张,而有些班级的图片超过 2000 张。这意味着我们的模型可能偏向过度代表的类别,特别是当它的预测不确定的时候。我们将在后面看到如何使用数据扩充来减少这种差异。

Distribution of images in training set — not quite balanced

预处理步骤

我们首先对图像应用两个预处理步骤:

灰度 我们将 3 通道图像转换成一个单一的灰度图像(我们在项目 1 中做了同样的事情——车道线检测——你可以在这里阅读我的博文)。

Sample Of Grayscale Training Set Images, with labels above

图像标准化 我们通过用数据集平均值减去每幅图像并除以其标准偏差来集中图像数据集的分布。这有助于我们的模型统一处理图像。生成的图像如下所示:

Normalised images — we can see how “noise” is distributed

模型架构

该建筑的设计灵感来自 Yann Le Cun 关于交通标志分类的论文。我们添加了一些调整,并创建了一个模块化的代码库,允许我们尝试不同的滤波器大小、深度和卷积层数,以及完全连接层的尺寸。为了表达对乐存的敬意,并带着一点厚脸皮,我们把这样的网络叫做 EdLeNet :)。

我们主要尝试了 5x5 和 3x3 的过滤器(又名内核)大小,并从我们的第一个卷积层的深度 32 开始。 EdLeNet 的 3x3 架构如下图所示:

EdLeNet 3x3 Architecture

该网络由 3 个卷积层组成——内核大小为 3x3,下一层深度加倍——使用 ReLU 作为激活函数,每一层后面都有一个 2x2 最大池操作。最后 3 层完全连接,最后一层使用 SoftMax 激活函数计算出 43 个结果(可能标签的总数)。使用带有 Adam 优化器的小批量随机梯度下降来训练网络。我们构建了一个高度模块化的编码基础设施,使我们能够动态创建我们的模型,如以下代码片段所示:

mc_3x3 = ModelConfig(EdLeNet, "EdLeNet_Norm_Grayscale_3x3_Dropout_0.50", [32, 32, 1], [3, 32, 3], [120, 84], n_classes, [0.75, 0.5])
mc_5x5 = ModelConfig(EdLeNet, "EdLeNet_Norm_Grayscale_5x5_Dropout_0.50", [32, 32, 1], [5, 32, 2], [120, 84], n_classes, [0.75, 0.5])

me_g_norm_drpt_0_50_3x3 = ModelExecutor(mc_3x3)
me_g_norm_drpt_0_50_5x5 = ModelExecutor(mc_5x5)

ModelConfig包含关于模型的信息,例如:

  • 模式功能(例如EdLeNet)
  • 型号名称
  • 输入格式(例如,灰度为[32,32,1]),
  • 卷积层配置[滤波器大小,起始深度,层数],
  • 完全连接的层尺寸(例如[120,84])
  • 班级数量
  • 辍学保持百分比值[p-conv,p-fc]

ModelExecutor负责训练评估预测,并生成我们的激活地图的可视化。

为了更好地隔离我们的模型,并确保它们不都存在于同一个张量流图下,我们使用以下有用的构造:

self.graph = tf.Graph()
with self.graph.as_default() as g:
    with g.name_scope( self.model_config.name ) as scope:

...

with tf.Session(graph = self.graph) as sess:

这样,我们为每个型号的创建单独的图表,确保没有混淆变量、占位符等。这让我省了很多麻烦。

我们实际上从 16 的卷积深度开始,但是用 32 获得了更好的结果,所以决定用这个值。我们还比较了彩色图像与灰度图像、标准图像和标准化图像,发现灰度图像往往优于彩色图像。不幸的是,我们在 3x3 或 5x5 模型上勉强达到了 93%的测试集准确率,没有持续达到这个里程碑。此外,我们在给定数量的时期后,在验证集上观察到一些不稳定的损失行为,这实际上意味着我们的模型在训练集上过度拟合,而不是一般化。您可以在下面看到我们针对不同型号配置的一些指标图。

Models Performance on Color Normalised Images

Models Performance On Grayscale Normalised Images

拒绝传统社会的人

为了提高模型的可靠性,我们转向了 dropout,这是一种正则化形式,其中权重以概率 p 保持:未接受的权重因此被“丢弃”。这可以防止模型过度拟合。辍学是由深度学习领域的先驱杰弗里·辛顿(Geoffrey Hinton)提出的。为了更好地理解作者背后的动机,他的团队在 T2 发表的关于这个主题的论文是必读的。生物学和进化论还有一个有趣的相似之处。 在论文中,作者根据图层类型应用了不同程度的辍学。因此,我决定采用一种类似的方法,定义两个等级的漏失,一个用于卷积层,另一个用于全连接层:

p-conv: probability of keeping weight in convolutional layer
p-fc: probability of keeping weight in fully connected layer

此外,随着他们在网络中的深入,作者逐渐采用了更积极(即更低)的辍学值。因此我也决定:

p-conv >= p-fc

也就是说,我们将在卷积层中以大于或等于全连接层的概率保持权重。对此的解释是,我们将网络视为一个漏斗,因此希望随着我们向更深的层次移动,逐渐收紧:我们不希望在开始时丢弃太多信息,因为其中一些信息非常有价值。此外,当我们在卷积层应用 MaxPooling 时,我们已经丢失了一些信息。

我们尝试了不同的参数,但最终确定了 p-conv=0.75p-fc=0.5 ,这使我们能够在 3x3 模型的标准化灰度图像上实现 97.55%的测试集准确度。有趣的是,我们在验证集上实现了超过 98.3%的准确率:

Training EdLeNet_Norm_Grayscale_3x3_Dropout_0.50 [epochs=100, batch_size=512]...

[1]	total=5.222s | train: time=3.139s, loss=3.4993, acc=0.1047 | val: time=2.083s, loss=3.5613, acc=0.1007
[10]	total=5.190s | train: time=3.122s, loss=0.2589, acc=0.9360 | val: time=2.067s, loss=0.3260, acc=0.8973
...
[90]	total=5.193s | train: time=3.120s, loss=0.0006, acc=0.9999 | val: time=2.074s, loss=0.0747, acc=0.9841
[100]	total=5.191s | train: time=3.123s, loss=0.0004, acc=1.0000 | val: time=2.068s, loss=0.0849, acc=0.9832
Model ./models/EdLeNet_Norm_Grayscale_3x3_Dropout_0.50.chkpt saved
[EdLeNet_Norm_Grayscale_3x3_Dropout_0.50 - Test Set]	time=0.686s, loss=0.1119, acc=0.9755

Models Performance on Grayscale Normalised Images, After The Introduction Of Dropout

上面的图表显示模型是平滑的,不像上面的一些图表。我们已经实现了在测试集上达到 93%以上准确率的目标,但是我们还能做得更好吗?请记住,有些图像是模糊的,每个班级的图像分布非常不均匀。我们将在下面探讨用于解决每个问题的其他技术。

直方图均衡

直方图均衡化是一种计算机视觉技术,用于增加图像中的对比度。由于我们的一些图像对比度低(模糊、黑暗),我们将通过应用 OpenCV 的对比度限制自适应直方图均衡(又名 CLAHE)功能来提高可视性。

我们再次尝试各种配置,并找到最佳结果,在 3x3 模型上使用以下压差值得到 97.75% 的测试精度: p-conv=0.6p-fc=0.5

Training EdLeNet_Grayscale_CLAHE_Norm_Take-2_3x3_Dropout_0.50 [epochs=500, batch_size=512]...[1]	total=5.194s | train: time=3.137s, loss=3.6254, acc=0.0662 | val: time=2.058s, loss=3.6405, acc=0.0655
[10]	total=5.155s | train: time=3.115s, loss=0.8645, acc=0.7121 | val: time=2.040s, loss=0.9159, acc=0.6819
...
[480]	total=5.149s | train: time=3.106s, loss=0.0009, acc=0.9998 | val: time=2.042s, loss=0.0355, acc=0.9884
[490]	total=5.148s | train: time=3.106s, loss=0.0007, acc=0.9998 | val: time=2.042s, loss=0.0390, acc=0.9884
[500]	total=5.148s | train: time=3.104s, loss=0.0006, acc=0.9999 | val: time=2.044s, loss=0.0420, acc=0.9862
Model ./models/EdLeNet_Grayscale_CLAHE_Norm_Take-2_3x3_Dropout_0.50.chkpt saved
[EdLeNet_Grayscale_CLAHE_Norm_Take-2_3x3_Dropout_0.50 - Test Set]	time=0.675s, loss=0.0890, acc=0.9775

我们在下面的图表中展示了我们测试 5x5 模型的 220 多个时期。我们可以看到一条更平滑的曲线,这加强了我们的直觉,即我们的模型更稳定。

Models Performance On Grayscale Equalized Images, With Dropout

我们识别了 269 个模型不能正确识别的图像。我们在下面展示了 10 个随机选择的例子,来猜测为什么这个模型是错误的。

Sample of 10 images where our model got the predictions wrong

有些图像非常模糊,尽管我们的直方图均衡,而其他人似乎扭曲。在我们的测试集中,我们可能没有足够的这种图像的例子来改进我们的模型的预测。此外,虽然 97.75%的测试准确率非常好,但我们还有一张王牌:数据扩充。

数据扩充

我们之前观察到数据在 43 个班级中呈现出明显的不平衡。然而,这似乎不是一个严重的问题,因为我们能够达到非常高的精度,尽管阶级不平衡。我们还注意到测试集中的一些图像是扭曲的。因此,我们将使用数据增强技术来尝试:

  1. 扩展数据集并提供不同照明设置和方向的附加图片
  2. 提高模型变得更加通用的能力
  3. 提高测试和验证的准确性,尤其是在失真图像上

我们使用一个叫做 imgaug 的漂亮的库来创建我们的增强。我们主要应用仿射变换来放大图像。我们的代码如下所示:

def augment_imgs(imgs, p):
    """
    Performs a set of augmentations with with a probability p
    """
    augs =  iaa.SomeOf((2, 4),
          [
              iaa.Crop(px=(0, 4)), # crop images from each side by 0 to 4px (randomly chosen)
              iaa.Affine(scale={"x": (0.8, 1.2), "y": (0.8, 1.2)}),
              iaa.Affine(translate_percent={"x": (-0.2, 0.2), "y": (-0.2, 0.2)}),
              iaa.Affine(rotate=(-45, 45)), # rotate by -45 to +45 degrees)
              iaa.Affine(shear=(-10, 10)) # shear by -10 to +10 degrees
          ]) seq = iaa.Sequential([iaa.Sometimes(p, augs)])

    return seq.augment_images(imgs)

虽然类别不平衡可能会导致模型中的一些偏差,但我们决定在此阶段不解决它,因为它会导致我们的数据集显著膨胀,并延长我们的训练时间(我们在此阶段没有太多时间花在训练上)。相反,我们决定每个班级增加 10%。我们的新数据集如下所示。

Sample Of Augmented Images

当然,图像的分布没有明显变化,但我们对图像进行了灰度、直方图均衡化和归一化预处理。我们训练了 2000 个有丢失的历元( p-conv=0.6p-fc=0.5 ),在测试集上达到了 97.86%的准确率

[EdLeNet] Building neural network [conv layers=3, conv filter size=3, conv start depth=32, fc layers=2]
Training EdLeNet_Augs_Grayscale_CLAHE_Norm_Take4_Bis_3x3_Dropout_0.50 [epochs=2000, batch_size=512]...

[1]	total=5.824s | train: time=3.594s, loss=3.6283, acc=0.0797 | val: time=2.231s, loss=3.6463, acc=0.0687
...
[1970]	total=5.627s | train: time=3.408s, loss=0.0525, acc=0.9870 | val: time=2.219s, loss=0.0315, acc=0.9914
[1980]	total=5.627s | train: time=3.409s, loss=0.0530, acc=0.9862 | val: time=2.218s, loss=0.0309, acc=0.9902
[1990]	total=5.628s | train: time=3.412s, loss=0.0521, acc=0.9869 | val: time=2.216s, loss=0.0302, acc=0.9900
[2000]	total=5.632s | train: time=3.415s, loss=0.0521, acc=0.9869 | val: time=2.217s, loss=0.0311, acc=0.9902
Model ./models/EdLeNet_Augs_Grayscale_CLAHE_Norm_Take4_Bis_3x3_Dropout_0.50.chkpt saved[EdLeNet_Augs_Grayscale_CLAHE_Norm_Take4_Bis_3x3_Dropout_0.50 - Test Set]	time=0.678s, loss=0.0842, acc=0.9786

这是我们迄今为止最好的表现!!!

Neural Network Celebration

但是 …如果你看看训练集上的损失度量,你可以看到在 0.0521,我们很可能还有一些回旋的空间。我们计划为更多的纪元进行训练,并将在未来报告我们的新结果。

测试新图像

我们决定也在新图像上测试我们的模型,以确保它确实比我们原始数据集中的交通标志更一般化。因此,我们下载了五张新图像,并提交给我们的模型进行预测。

Download 5 new traffic signs — color

图像的基本事实如下:

['Speed limit (120km/h)', 'Priority road', 'No vehicles', 'Road work', 'Vehicles over 3.5 metric tons prohibited']

选择这些图像的原因如下:

  • 它们代表了我们目前分类的不同交通标志
  • 它们的形状和颜色各不相同
  • 它们处于不同的光照条件下(第四个有阳光反射)
  • 它们处于不同的方向(第三个是倾斜的)
  • 他们有不同的背景
  • 最后一张图片实际上是一个设计,而不是真实的图片,我们想用它来测试模型
  • 他们中的一些人在代表性不足的班级

我们采取的第一步是对这些新图像应用相同的 CLAHE,结果如下:

Download 5 new traffic signs — grayscale CLAHE

我们在新图像上达到了 100%的完美准确率。在原始测试集上,我们达到了 97.86%的准确率。我们可以探索模糊/扭曲我们的新图像或修改对比度,看看模型在未来如何处理这些变化。

new_img_grayscale_norm_pred_acc = np.sum(new_img_lbs == preds) / len(preds)
print("[Grayscale Normalised] Predictional accuracy on new images: {0}%".format(new_img_grayscale_norm_pred_acc * 100))
...
[Grayscale Normalised] Predictional accuracy on new images: 100.0%

我们还显示了为每个图像计算的前 5 个 SoftMax 概率,绿色条显示了基本事实。我们可以清楚地看到,我们的模型对其预测相当有信心。在最坏的情况下(最后一幅图像),第二个最可能的预测的概率约为 0.1%。事实上,我们的模型在最后一张图片上挣扎得最厉害,我相信这实际上是一个设计,甚至不是一张真实的图片。总的来说,我们开发了一个强大的模型!

Visualizations of The Model’s Top 5 Predictions

可视化我们的激活地图

我们在下面展示了每个卷积层产生的结果(在最大池化之前),产生了 3 个激活图

第一层

我们可以看到这个网络把很多注意力集中在圆圈的边缘,以及卡车上。背景大多被忽略。

第二层

Activation Map Of Second Convolutional Layer

很难确定网络在第 2 层关注的是什么,但它似乎在圆圈的边缘和中间(卡车出现的地方)激活。

第三层

这个激活图也很难解读…但是看起来网络对边缘和中间的刺激有反应。

结论

我们介绍了如何使用深度学习对交通标志进行高精度分类,采用各种预处理和正则化技术(例如 dropout),并尝试不同的模型架构。我们构建了高度可配置的代码,并开发了评估多种架构的灵活方法。我们的模型在测试集上达到了接近 98%的准确率,在验证集上达到了 99%。

就个人而言,我非常喜欢这个项目,并获得了使用 Tensorflow、matplotlib 和研究人工神经网络架构的实践经验。此外,我深入研究了该领域的一些开创性论文,这些论文加强了我的理解,更重要的是完善了我对深度学习的直觉。

在未来,我相信通过应用进一步的规范化技术,如批处理规范化,以及采用更现代的架构,如 GoogLeNet 的初始模块ResNetXception ,可以实现更高的准确性。

感谢阅读这篇文章。希望你觉得有用。我现在正在创建一个名为env sion的新公司!在 EnVsion,我们正在为 UX 的研究人员和产品团队创建一个中央存储库,以从他们的用户采访视频中挖掘见解。当然我们用人工智能来做这个。).

如果你是一名 UX 的研究人员或产品经理,对与用户和客户的视频通话感到不知所措,那么 EnVsion 就是为你准备的!

你也可以关注我的 推特

推荐系统——模型和评估

原文:towardsdatascience.com/recommendat…

我曾参与构建几种不同类型的推荐系统,我注意到的一件事是每个用例都不同,因为每个用例都旨在解决不同的业务问题。让我们考虑几个例子:

  1. 电影/书籍/新闻推荐—推荐增加用户参与度的新内容。目的是向用户介绍他们可能感兴趣的新内容,并鼓励他们在我们的平台上消费更多内容。
  2. 股票推荐——推荐对客户最有利可图的股票。推荐的可能是他们历史上交易过的股票。新奇在这里并不重要;股票的盈利能力。
  3. 产品建议—建议新旧产品的组合。来自用户历史交易的旧产品作为他们频繁购买的提醒。此外,推荐用户可能喜欢尝试的新产品也很重要。

在所有这些问题中,共同的主线是他们的目标是提高客户满意度,并反过来以增加佣金、提高销售额等形式推动业务。无论使用何种情况,数据通常采用以下格式:

  • 客户 ID、产品 ID(电影/股票/产品)、单位数量/评级、交易日期
  • 任何其他特征,如产品的细节或客户的人口统计

接下来,我将介绍以下主题:

  • 用于构建推荐系统的方法——基于内容、协作过滤、聚类
  • 评估指标—统计准确性指标、决策支持准确性指标
  • 要记住的事情

cleverdata.io/basket-anal…

方法

构建推荐系统有两种主要方法——基于内容的和协同过滤。在下一节中,我将讨论它们中的每一种,以及它们何时适合。

datameetsmedia.com/an-overview…

基于内容

这种方法的要点是,我们将用户与他们喜欢或购买的内容或项目相匹配。在这里,用户和产品的属性很重要。例如,对于电影推荐,我们使用诸如导演、演员、电影长度、流派等特征。寻找电影之间的相似之处。此外,我们可以从电影描述和评论中提取情感评分和 tf-idf 评分等特征。(单词的 tf-idf 分数反映了单词对于文档集合中的文档的重要性)。基于内容的推荐的目的是为每个用户和每个项目创建一个“档案”。

考虑一个向用户推荐新闻文章的例子。假设我们有 100 篇文章,词汇量为 n。我们首先计算每篇文章中每个单词的 tf-idf 分数。然后我们构建两个向量:

  1. Item vector:这是一个长度为 n 的向量。对于在该文章中具有高 tf-idf 分数的单词,它包含 1,否则包含 0。

towardsdatascience.com/understandi…

2.用户向量:同样是 1xN 向量。对于每个单词,我们存储该单词在用户已经消费的文章中出现的概率(即具有高 tf-idf 分数)。这里注意,用户向量是基于项目的属性的(在这种情况下是 tf-idf 单词得分)。

一旦我们有了这些配置文件,我们就可以计算用户和项目之间的相似性。推荐的项目是 1)用户与之具有最高相似性的项目,或者 2)与用户已经阅读的其他项目具有最高相似性的项目。有多种方法可以做到这一点。让我们看看两种常见的方法:

  1. 余弦相似度: 为了计算用户和项目之间的相似度,我们简单地取用户向量和项目向量之间的余弦相似度。这给了我们用户项目的相似性。

为了推荐与用户已经购买的商品最相似的商品,我们计算用户已经阅读的商品和其他商品之间的余弦相似度。推荐最相似的。因此,这是项目-项目的相似性。

余弦相似度最适合于高维特征,尤其是在信息检索和文本挖掘中。

**2。【Jaccard 相似度:**又称交集超过并集,公式如下:

这用于项-项相似度。我们将项目向量相互比较,返回最相似的项目。

Jaccard 相似性仅在向量包含二进制值时有用。如果它们具有可以采用多个值的排名或评级,则 Jaccard 相似性不适用。

除了相似性方法之外,对于基于内容的推荐,我们可以将推荐视为简单的机器学习问题。这里有像随机森林、 XGBoost 等常规的机器学习算法。,派上用场了。

当我们有一大堆“外部”特征时,如天气条件、市场因素等,这种方法很有用。这不是用户或产品的属性,并且可以是高度可变的。例如,前一天的开盘价和收盘价在决定投资特定股票的盈利能力方面起着重要作用。这属于受监督的问题类别,其中标签是用户是否喜欢/点击某个产品(0/1),或者用户给该产品的评级,或者用户购买的单位数量。

协同过滤

协同过滤方法的基本假设是,如果 A 和 B 购买相似的产品,A 更有可能购买 B 已经购买的产品,而不是随机的人已经购买的产品。与基于内容不同,这里没有与用户或项目相对应的特性。我们只有效用矩阵。看起来是这样的:

a、B、C、D 是用户,列代表电影。这些值代表用户对电影的评价(1-5)。在其他情况下,这些值可以是 0/1,这取决于用户是否观看了电影。协作过滤可以分为两大类:

基于记忆的方法 对于基于记忆的方法,效用矩阵被记忆,并且通过用效用矩阵的剩余部分询问给定用户来做出推荐。让我们考虑一个同样的例子:如果我们有 m 部电影和 u 个用户,我们想知道用户 i 有多喜欢电影 k

这是用户 i 对她/他评价的所有电影的平均评价。利用这一点,我们估计他对电影 k 的评价如下:

用户 ai 之间的相似度可以使用任何方法计算,如余弦相似度/雅克卡相似度/皮尔逊相关系数等。 这些结果非常容易创建和解释,但是一旦数据变得过于稀疏,性能就会变差。

基于模型的方法 基于模型的方法的一个更普遍的实现是矩阵分解。在这里,我们从效用矩阵中创建用户和项目的表示。看起来是这样的:

因此,我们的效用矩阵分解成 U 和 V,其中 U 代表用户,V 代表低维空间中的电影。这可以通过使用矩阵分解技术,如 SVD 或 PCA,或者通过使用神经网络在一些优化器如 Adam、SGD 等的帮助下学习 2 个嵌入矩阵来实现。

对于用户 i 和每部电影 j 我们只需要计算评价 y 并推荐具有最高预测评价的电影。这种方法在我们有大量数据并且高度稀疏的时候最有用。矩阵分解有助于降低维数,从而加快计算速度。这种方法的一个缺点是,我们往往会失去可解释性,因为我们不知道用户/项目向量的确切元素是什么意思。

使聚集

当您的推荐问题将无人监管时,通常会使用聚类。

如果您刚刚进入这个行业,并且历史数据/标签数据非常少,您可以根据特性集对观察结果进行聚类,然后根据该聚类中存在的标签向聚类分配建议。

当然,这种解决方案不会立即给出最佳结果,但是在获得足够的数据之前,对于这种情况是一个很好的起点。聚类也可用于为观察值生成元特征。例如,在聚类之后,我可以将 1-k 的值作为新的特征“聚类”分配给每个观察值,然后在所有特征上训练我的主模型。这可以在用户级别或产品级别完成。

评估指标

设计推荐系统的一个主要障碍是选择要优化的指标。这可能很棘手,因为在很多情况下,目标是不推荐用户以前购买过的所有相同产品。那么你怎么知道你的模特在推荐产品方面做得好不好呢?

统计准确性指标

它们用于通过将预测评级与实际用户评级直接进行比较来评估过滤技术的准确性。平均绝对误差(MAE)、均方根误差(RMSE)和相关性通常被用作统计准确性度量。MAE 是最流行最常用的;它是衡量推荐与用户实际价值的偏差。MAE 和 RMSE 计算如下:

MAE 和 RMSE 越低,推荐引擎预测用户评级就越准确。当建议基于预测评级或交易数量时,这些指标非常有用。它们让我们知道我们的预测评级有多准确,反过来也让我们知道我们的推荐有多准确。

决策支持准确性指标

其中最受欢迎的是精确和召回。它们帮助用户在可用的项目集中选择更相似的项目。度量将预测过程视为一个二元操作,它将好的项目与那些不好的项目区分开来。让我们更详细地看看它们:

Recall@k 和 Precision@k: 这些是用于推荐系统的常用指标。让我们从理解推荐系统的精确度和召回率的含义开始:

但是,精度和召回率似乎并不关心排序。因此,我们在截止值 k 处使用精度和召回。考虑我们提出 N 个建议,并且只考虑第一个元素,然后只考虑前两个,然后只考虑前三个,等等。这些子集可以通过 k 进行索引。

截止 k、P@k 和 r@k 处的精度和召回率只是通过仅考虑从等级 1 到 k 的推荐子集来计算的精度和召回率。推荐的排名由预测值决定。例如,具有最高预测值的产品排名为 1,具有第 k 个最高预测值的产品排名为 k

平均精度: 如果我们要推荐 N 个项目,在项目的满空间中有 m 个相关项目,平均精度 AP@N 定义为:

其中 rel(k) 只是一个指示符(0/1),它告诉我们第项是否相关,而 P(k) 是精度@k。如果我们推荐 2N 个项而不是 N 个,AP@N 度量表示我们只关心第个第 N 个项的平均精度。

AP 奖励你给出正确的推荐, AP 奖励你预先载入最有可能正确的推荐, AP 永远不会因为你在你的列表中添加了额外的推荐而惩罚你——只要确保你预先载入了最好的。

平均平均精度:

AP 适用于单个数据点,就像单个用户一样。MAP@N 则更进一步,将所有用户的 AP 平均化。

其他建议

一个建议(这是一个我不太确定的个人建议)是从用户的历史数据中寻找真实的内容。然后,使用上述任何标准指标,将此内容与建议中的非历史内容进行比较。这让我们知道我们的模型在推荐不直接来自历史交易的产品方面有多好。

要记住的事情

在开发推荐系统时,尤其是基于内容的推荐,重要的是要记住不要只针对单一指标进行优化。也就是说,对于新闻文章推荐来说,获得非常高的召回率是不可取的,因为这意味着我们在向用户推荐他们在没有我们推荐的情况下会自然消费的内容。

因此,我们不会以任何方式推动业务。我们需要确保适当的召回率/精确度,作为我们的模型能够学习用户偏好的指标,而不是试图使其尽可能高。此外,从长远来看,我们不想因为一遍又一遍地推荐相同类型的东西而失去用户参与度。因此,如果我们试图最大化精度@k/召回@ k,这是非常可能的。

感谢您的阅读!我将很高兴在评论中收到任何反馈或附加信息!

参考

  1. Yannet Interian 在 USF 举办的关于推荐系统的讲座
  2. 这个解释地图的超赞帖子
  3. py torch 中协作过滤(矩阵分解)的示例笔记本
  4. 更多关于协同过滤的内容请点击: https://towards data science . com/collaborative-filtering-and-embeddings-part-1-63 b 00 b 9739 ce https://towards data science . com/variables-implementations-of-collaborative-filtering-100385 c 6 dfe 0

**关于我:**我毕业于 USF 大学数据科学专业,本科学习计算机科学,在构建预测和推荐算法以及为金融和零售客户提供商业见解方面有两年的经验。我对将我的机器学习和深度学习知识应用于现实世界问题的机会感到兴奋。一定要看看我的其他博客这里LinkedIn:https://www.linkedin.com/in/neerja-doshi/ GitHub:github.com/neerjad/

黑客新闻 上讨论这个帖子。

编者按:准备好钻研一些代码了吗?在 GitHub 上查看Fritz。您将发现流行的机器和深度学习模型的开源、移动友好的实现,以及用于构建您自己的 ML 支持的 iOS 和 Android 应用程序的培训脚本、项目模板和工具。

加入我们的Slack寻求技术问题的帮助,分享你正在做的事情,或者只是与我们聊聊移动开发和机器学习。关注我们的TwitterLinkedIn了解移动机器学习世界的所有最新内容、新闻和更多信息。

对 NIH 数据管理政策的建议——NIH 能走出自己的路吗?

原文:towardsdatascience.com/recommendat…

2018 年 10 月, NIH 发布了一份信息请求“关于 NIH 资助或支持的研究的数据管理和共享政策草案的拟议条款。”这一请求的目的是收集科学界成员对如何使 NIH 资助的研究可用的意见。按照要求:

国家卫生研究院长期致力于向公众提供其资助和开展的研究的成果和成就。在 NIH 看来,数据应该尽可能广泛和自由地提供,同时保护参与者的隐私,保护机密和专有数据。

其他科学家和公众(这项研究的资助者)都可以获得受资助研究的产品,这一点至关重要。一个数据管理计划 (DMP)是申请研究资助的一个组成部分。DMPs 描述了调查中产生的科学数据将如何被重新使用、验证,以及如何解决隐私问题。科学家(在这种情况下是生物医学研究人员)是他们领域的专家,但是他们的技能通常不包括数据管理培训。缺乏数据管理技能(和/或缺乏意识)是数据可用性经常无法满足 NIH 概述的理想的一个原因(另见Federer et al . 2018)。虽然出版商通常对出版有数据可用性的要求,但资助者在实现这些标准方面肯定有自己的角色。

下面是我提交给 NIH 的请求。虽然我认为最初的政策草案大部分是明智的,但我最大的问题是,我和许多花时间贡献自己想法的人一样,对这些评论实际上如何影响 NIH 政策没有一个清晰的认识。例如,今年早些时候,NIH 还要求对他们的数据科学战略计划提出意见。我发现最初的计划令人震惊(见这里的想法),我的评论在推特上产生了几个类似这样的帖子。我不认为我是某种独特的有洞察力的天才,但“战略”数据科学计划包含许多想法,充其量是模糊的,在一些情况下显然是错误的。许多人注意到了这一点,但最终的计划在很大程度上没有受到社区反馈的影响。

国家卫生研究院是有史以来最卓越的机构之一,作为世界上最大的生物医学研究组织,它在塑造人类健康的全球进程中具有独特的地位。我很荣幸在国家卫生研究院的一个小项目中扮演了一个小角色(T4 国家卫生研究院数据共享中心),这个项目的使命是协调并提供有价值的国家卫生研究院数据。如果这个项目成功,它肯定会加快生物医学发现的步伐。每天,我们都看到技术(如人工智能和深度学习)能够实现洞察——并有可能治愈——这在 5 年或 10 年前是不可思议的。阻碍这一潜力的是将 NIH 数据定位为可访问并与新的强大的数据挖掘方法兼容的艰巨任务;政策没有跟上科学的步伐。看到 NIH 研究人员和项目工作人员的奉献精神,我知道进展是可能的,但我的乐观主义通常遭到怀疑。我在许多谈话中(公开的和私下的)研究人员对 NIH 能否开展数据科学基础设施项目表示怀疑。像 caBIG 和大数据到知识(BD2K)这样的项目的跟踪记录留下了很多批评的空间。更糟糕的是,许多人似乎认为他们的声音在改变国家卫生研究院的政策和政治过程中无关紧要。

NIH 需要关注其决策过程——使其对社区更加透明和负责。成功在于认识到,虽然以前收集反馈并根据反馈采取行动的方法可能有助于做出科学决策,但处理数据的问题实际上更多的是社会问题,而不是科学或技术问题。毕竟,过去 5 年最重要的数据创新之一是公平的概念——不是一套技术,而是一套原则。数据共享的理念不仅仅是托管一个数据仓库,而是一个信任的组织,致力于通过共享知识实现最好的科学和病人结果。

希望 NIH 能够在大数据科学的 21 世纪范式中“走出自己的路”——技术将解决问题,但人们(未能作为一个社区一起工作)将使它失败。

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

回复:关于 NIH 资助或支持的研究的数据管理和共享政策草案拟议条款的信息请求书*

2018 年 12 月 10 日

亲爱的同事们:

NIH 的使命是“寻求关于生命系统的本质和行为的基础知识,以及这些知识在增进健康、延长寿命、减少疾病和残疾方面的应用。”NIH 认识到数据前所未有的快速发展带来的挑战(和机遇),制定数据管理和共享政策是非常合适的。在阐述我对拟议条款文件的具体回应之前,我想提出一个我尚未看到解决的关键问题——一个限制 NIH 获得最佳建议的问题。

NIH 在数据实践领域收集反馈的机制已经引起了对该组织是否愿意或有能力按照社区建议采取行动的怀疑。自 2018 年 6 月发布 NIH 数据科学战略计划以来,我个人的经验是,调查人员——特别是来自生物信息学、计算生物学和数据科学社区的调查人员——对所做的评论没有导致最终计划的有意义改进表示失望。显然,NIH 的行动不应该基于我的个人轶事。然而,尽管 NIH 一直在努力征求意见(最重要的是,公开发表意见),但从我采访过的调查人员或阅读的 NIH 公告来看,没有任何迹象表明最终决定将如何做出。在缺乏更透明的流程的情况下,对这种做法最冷嘲热讽的解读是,NIH 可以无视机构群体的建议,继续遵循预先确定的议程。如果调查人员怀疑 NIH 的承诺,NIH 最需要听到的许多意见将永远不会提供。我说 NIH 是作为一个整体,因为即使个别机构的行为可能是他们自己的,他们都有助于社区在这些过程中对信念和善意的判断。

我的主要建议是 NIH 不仅要披露对该 RFI 和其他 RFI 的意见,还要向机构群体公开完整的解释,说明建议是如何实施的以及实施的原因。由于我在之前的回答中阐述的几个原因**,NIH 在制定数据相关政策方面处于“结构性”劣势。理想情况下,NIH 会在 RFI 之前提出一个合理的实施计划,描述如何审查和实施建议,并贯彻其响应机构群体建议的承诺。

不幸的是,支持 NIH 资助的科学的数据和计算基础的大规模计划没有成功的记录(例如 caBIG、BD2K 等。).我仍然对数据共享空间抱有很高的期望(如果它能够为了 NIH 的利益朝着社区驱动的方向发展,而不是为了任何人的利益而被 NIH 纠缠)。如果不采取更多措施提高响应性和开放性,国家卫生研究院就有可能成为一个无关紧要的政策制定者,并且随着国内和全球产生的数据使国家卫生研究院直接资助的项目相形见绌,国家卫生研究院将会退居被动地位。我希望 NIH 决定思考如何跳出框框思考(或 NIH 园区),通过更加透明和负责任的过程建立社区共识。我想我看到事情正在发生,并希望得到进一步的保证。

具体评论

科学数据的定义

软件是科学数据定义中缺失的一个概念。在某种意义上,软件是元数据的一种形式。例如,在湿实验室实验中,细胞系或抗体批次的描述可能是数据集的关键描述符。通常,科学数据与用于产生该数据的软件是唯一结合的——从可能涉及“原始”序列数据产生的碱基调用软件,到用于产生任何几个下游分析产品的软件。明确承认这种独特的关系将有利于科学数据的定义。

与软件相关,起源也是计划提案中缺少的一个概念。科学数据以其生命周期为特征(见https://www . dataone . org/data-life-cycle 上的一个详细说明)。.)特别是,在将“科学”数据定义为一个动态概念时,不断版本化和更新的需求值得考虑。

数据管理和共享计划需求

关于“相关工具软件和/或代码”:

-对于分析中使用的(但不是开发的)任何软件/代码,应要求提供软件的完整描述,包括版本号、源代码/二进制文件的链接等。

-如前所述,NIH 应该大力鼓励使用免费提供的开源软件。由于资助者和机构正在向所有开放获取出版物转移,可能值得考虑 NIH 如何才能实现使用开放软件和数据格式的要求。

-我同意以下建议:在必须使用专有软件的情况下,应提供解释。

-应进一步建议对分析中使用的软件进行全面记录,例如通过提供版本控制脚本、生成文件或工作流语言描述等。在可能的情况下,研究人员应利用现代再现方法,如容器(如 Docker、Singularity)、虚拟机图像等。文件应遵循增加分析重现性的建议,如最低信息标准(如生物学和生物医学调查的最低报告指南;http://www . nature . com/NBT/journal/v 26/n8/full/NBT . 1411 . html,或 GA4GH、研究数据联盟等正在制作的更多更新推荐。).

-如果任何脚本或其他软件是作为调查的一部分开发的,则必须附有适当的开源许可证,并且在提交任何预印本时和/或在提交供同行审查时可在公共存储库中获得。无论发布状态如何,任何代码/软件都应在资金结束时可用。关于版本控制和容器化的相同建议在这里也适用。

关于“数据保存和访问”:

-没有关于调查人员应如何处理不需要保留的数据的评论。应该清楚地描述调查人员如何确定哪些中间或衍生数据产品不需要保存。可能还需要具体的建议来记录敏感数据将如何被丢弃,但这些可以通过其他法律和政策要求患者数据来充分解决。

-虽然解决了唯一标识符的问题,但应该有更具体的指导,说明何处需要标识符,以及什么定义了适当的长期存储解决方案。

-应该有一个关于如何实现大容量(> 1GB)共享的描述?)数据集。对于非常大的数据集,共享变得越来越困难。虽然研究人员可能没有义务使每个数据集同等可用,但应该有可能表征(或许 NIH 可以实施评分系统)允许数据共享的分类。在 NIH 数据共享区和其他地方开发的公平度量项目已经在为这些目标而努力。

关于“数据保存和访问时间表”:

-由 NIH 资助的数据(受保护的记录除外)必须公开。

NIH 在实施新数据管理和共享政策的各个部分时需要考虑的最佳时机,包括可能的分阶段采用,以及可能的分阶段如何与数据基础设施、资源和标准的必要改进相关联。

-我同意文件中的建议,即对于外部赠款,DMP 可以被评估为可接受/不可接受,并作为额外审查考虑的一部分。

NIH 需要投资培训和/或学习材料,学习如何有效地评价数据管理计划。一般来说,研究部门或其他小组(或项目官员)中的大量评审员可能没有接受过计算科学或数据管理实践方面的培训。因此,他们可能不是数据管理计划最有效的裁决者。NIH 可以通过提供培训来提高审查的质量。数据木工、DataONE 等团体。有可能适用于此的培训材料/课程。

NIH 应投资于培训,包括学习资源的开发,以协助研究者开发和执行数据管理计划。

附加建议

-数据管理/政策环境非常动态。NIH 应对其指南进行年度审查。

-数据管理政策指南应直接征求公认组织的建议,包括研究数据联盟、GA4GH、ELIXIR、Force11 和其他组织。在这方面,正式的咨询机制可能是合适的。

真诚地

贾森·威廉姆斯

冷泉港实验室

*此回复仅代表我自己的个人观点

**在这里查看我和其他社区成员对 NIH 数据计划的评论:https://github . com/JasonJWilliamsNY/2018 _ NIH _ Data science _ RFI

推荐引擎——在引擎盖下

原文:towardsdatascience.com/recommender…

我们中的许多人在日常生活中被各种各样的推荐轰炸,无论是在电子商务网站还是社交媒体网站上。有些建议看起来很相关,但有些建议会让人产生各种情绪,从困惑到愤怒不等。

基本上有两种类型的推荐系统,基于内容和协同过滤。两者各有利弊,这取决于您想要使用它们的环境。

基于内容的:在基于内容的推荐系统中,当向用户推荐项目时,项目的关键字或属性被考虑在内。所以,简而言之,这就像是推荐相似的项目。假设您正在阅读一本关于数据可视化的书,并想寻找同一主题的其他书籍。在这种情况下,基于内容的推荐系统将是合适的。

Image source : blog.guillaumeagis.eu/recommendat…

协同过滤:说到点子上了,下图就是最好的例子。客户 A 购买了书籍 x、y、z,客户 B 购买了书籍 y、z。现在,协同过滤技术会向客户 B 推荐书籍 x。这是协同过滤的优点和缺点。如果书 x 是一本非虚构的书,而顾客 B 的喜好完全是虚构的书,这并不重要。建议的相关性可能正确,也可能不正确。通常许多公司使用这种技术,因为它允许他们交叉销售产品。

Image adapted from www.balabit.com/blog/recomm…

开发基于内容的图书推荐系统——理论

假设您的图书馆中有一批数据科学书籍,假设您的朋友读了一本关于神经网络的书,并希望阅读同一主题的另一本书,以积累他/她的相关知识。最好的方法是实现一个简单的基于内容的推荐系统。

我们将在这里看三个重要的概念,这三个概念涉及到建立这个基于内容的推荐系统。

  • 向量
  • TF-IDF
  • 余弦相似性

矢量

基本思想是将文本或单词转换成向量,并在向量空间模型中表示。这个想法太美了,从本质上来说,向量的这个想法使得机器学习和人工智能的快速发展成为可能。事实上,Geoffrey Hinton(“深度学习之父”)在 MIT technology review 的一篇文章中承认,多伦多的人工智能研究所被命名为“向量研究所”,因为向量的美丽属性在深度学习和神经网络的其他变体领域帮助了他们。

TF — IDF

TF- IDF 代表术语频率和逆文档频率。TF-IDF 有助于评估文档中某个单词的重要性。

TF —词频

为了确定术语/单词在文档中出现的频率,并以向量形式表示文档,让我们将其分解为以下步骤。

步骤 1 :创建一个存在于整个文档空间的单词字典(也称为单词包)。我们忽略了一些常用词,也称为停用词,如 the,of,a,an,is 等,因为这些词非常常见,对我们选择重要词的目标没有帮助

在当前的例子中,我使用了包含 50 本书标题的文件“test1.csv”。但是为了证明这一点,只需考虑由 3 个书名(文档)组成整个文档空间。所以 B1 是一个文档,B2 和 B3 是其他文档。B1、B2、B3 共同构成了文档空间。

B1 —推荐系统

B2——统计学习的要素

B3 —推荐系统—高级

现在创建这些词的索引(停止词被忽略)

1.推荐人 2。系统 3 要素 4。统计学 5。学习 6。先进的

第二步:形成矢量

词频帮助我们识别该词或词在文档中出现的次数,但也有一个固有的问题,TF 更重视频繁出现的词/词,而忽略了罕见词/词的重要性。这不是一个理想的情况,因为生僻字包含更多的重要性或信号。这个问题由以色列国防军解决。

有时一个单词/术语在较长的文档中比在较短的文档中出现得更频繁;因此,执行术语频率标准化。

TFn =(术语 t 在文档中出现的次数)/(文档中的总术语数),其中 n 表示归一化。

IDF(逆文档频率):

在 IDF 定义的一些变体中,在文档中没有出现术语的情况下,将 1 添加到分母,以避免被零除的情况。

基本上,一个简单的定义是:

IDF = ln(文档总数/包含术语 t 的文档数)

现在,让我们从我们自己的字典或单词包中取一个例子来计算 IDF

我们有 6 个术语或单词,如下所示

1.推荐人 2。系统 3 要素 4。统计学 5。学习 6。先进的

我们的文件是:

B1 —推荐系统

B2——统计学习的要素

B3 —推荐系统—高级

现在 IDF(w1)= log 3/2;IDF(w2)= log 3/2;IDF(w3)= log 3/1;IDF(W4)= log 3/1;IDF(W5)= log 3/1;IDF(w6) = log 3/1

(注:取自然对数和 w1..w6 表示单词/术语)

然后我们又得到一个向量,如下所示:

= (0.4054, 0.4054, 1.0986, 1.0986, 1.0986, 1.0986)

TF-IDF 重量:

现在最后一步是得到 TF-IDF 的重量。TF 向量和 IDF 向量被转换成矩阵。

那么 TF-IDF 重量表示为:

TF-IDF 权重= TF (t,d) * IDF(t,D)

这与我们通过执行以下 python 代码获得的矩阵相同:

tfidf_matrix = tf.fit_transform(ds['Book Title'])

余弦相似度:

余弦相似性是两个非零向量之间相似性的度量。向量表示的一个美妙之处是,我们现在可以看到,两个句子有多紧密的联系,基于它们各自的向量所成的角度。

余弦值的范围从-1 到 1。

因此,如果两个向量形成一个角度 0,那么余弦值将是 1,这反过来意味着句子彼此密切相关。

如果两个向量是正交的,即 cos 90,则意味着句子几乎是不相关的。

开发基于内容的图书推荐系统—实现

下面是要点链接,我用 python 写了几行代码来实现一个简单的基于内容的图书推荐系统。我添加了注释(在#后面的单词)来清楚地说明每一行代码在做什么。

输出

test1.csv 中的 id 和书名列表如下(显示了 20 行)

既然您已经阅读了这篇文章,您可能也喜欢阅读……..(嗯没关系;) )

以下链接也提供了相同的文章:

[## 如何建立一个简单的基于内容的图书推荐系统

我们中的许多人在日常生活中被各种各样的建议轰炸,无论是在电子商务网站还是社交媒体上…

www.linkedin.com](www.linkedin.com/redir/redir…)

来源:

TF-IDF

卡格尔

向量空间模型

深度学习 4j

Sklearn 余弦相似度

sk learn tfidf 矢量器

马克·李约瑟博客

麻省理工科技评论

代码链接:https://gist . github . com/venkara fa/0da 815727 f1 ee 098 b 201 c 371 b 60 B2 d 72

推荐系统:利用不确定性探索未知

原文:towardsdatascience.com/recommender…

现在我们知道了存在哪些不确定性类型并且学会了一些建模它们的方法,我们可以开始讨论如何在我们的应用中使用它们。

在本帖中,我们将介绍勘探开发问题,并向您展示不确定性如何帮助解决这个问题。我们将重点探索推荐系统,但同样的想法也可以应用于强化学习的许多应用中——无人驾驶汽车、机器人等。

问题设置

推荐系统的目标是推荐用户可能会发现相关的项目。在 Taboola,相关性是通过点击来表达的:我们显示一个包含内容推荐的小部件,用户可以选择是否要点击其中一个项目。

用户点击一个商品的概率叫做点击率 (CTR)。如果我们知道所有商品的点击率,那么推荐哪些商品的问题就很简单了:简单地推荐点击率最高的商品。

问题是我们不知道 CTR 是什么。我们有一个模型来估计它,但它显然不是完美的。不完美的一些原因是推荐系统固有的不确定性类型,我们在本系列的第一篇文章中讨论过。

开发与勘探的权衡

所以现在我们面临着一个具有挑战性的情况——一个我们在日常生活中都很熟悉的情况:想象你刚刚走进一家冰激凌店。你现在面临着一个至关重要的决定——在大约 30 种口味中,你只需要选择一种!

你可以采取两种策略:要么选择你最喜欢的口味,你已经知道这是最好的;或者探索你从未尝试过的新口味,也许会发现一种新的最佳口味。

这两种策略——开发和探索——也可以在推荐内容时使用。我们可以利用具有高点击率和高确定性的项目——可能是因为这些项目已经向类似的用户展示了数千次;或者我们可以探索过去没有向很多用户展示过的新项目。将探索融入到你的推荐策略中是至关重要的——没有探索,新的商品就没有机会与更老、更熟悉的商品竞争。

让我们探索探索的方法

你能实现的最简单的探索-开发方法是ϵ-greedy 算法,在这里你分配ϵ流量的百分比来以随机的方式探索新的项目。剩下的流量留给开发。

尽管不是最佳的,这种方法很容易理解。它可以作为更复杂方法的坚实基础。那么,我们如何以更明智的方式寻找好的商品呢?

Looking for good items in a wise manner

一种更先进的方法——置信上限(UCB)——使用不确定性。每个项目都与其预期的 CTR 和该 CTR 上的置信界限相关联。置信界限反映了我们对项目 CTR 的不确定程度。普通 UCB 算法通过单独使用经验信息来跟踪预期的点击率和置信界限:对于每个项目,我们跟踪经验点击率(相似用户点击它的百分比),并且通过假设二项式分布来计算置信界限。

以你经常点的纯巧克力口味为例。你知道它很好——你给了它 8 颗星(满分 10 分)。今天,一种新的口味已经到来。你没有关于它的经验信息,这意味着它可以是 1 到 10 颗星中的任何一颗。利用这个置信区间,如果你想探索,你会选择新口味,因为有可能是 10 星口味。

这种策略正是 UCB 所要做的——你选择具有最高置信上限值的项目——在我们的例子中,置信上限超过 CTR 估计。这种策略背后的动机是,随着时间的推移,经验的 CTR 将趋向于真实的 CTR,并且置信界限将缩小到 0。经过足够的时间,我们将探索一切。

另一种流行的方法是汤普森抽样方法。在这种方法中,我们使用项目 CTR 的整个估计分布,而不仅仅是一个置信界限。对于每个项目,我们从其分布中抽取一个 CTR。

当可用项目的数量固定时,这些方法可能工作得很好。不幸的是,在 Taboola,每天都有数以千计的新商品进入系统,而其他商品则被淘汰。当我们得到一个项目的合理置信界限时,它可能会离开系统。我们的努力将会白费。这就像是在进行一次世界旅行,每天都要去一个新城镇,那里有大量的冰淇淋可供探索。恐怖啊!

我们需要一种方法来估计一个新项目的点击率,而不需要展示它一次。我们需要一些美食评论杂志,引导我们通过自助餐的内容推荐。

考虑一种刚刚上市的新型巧克力口味。既然你知道你喜欢巧克力,你很有可能也会喜欢这种新口味。在香草 UCB 方法中(不,那不是一种味道的名字),你不能推断它——你只依赖经验信息。

在未来的文章中,我们将详细阐述如何使用神经网络来估计一个新项目的 CTR,以及不确定性的水平。利用这种不确定性,我们可以应用 UCB 方法来探索新的项目。与依赖经验数据的普通 UCB 不同,这里我们可以使用模型的估计来避免显示点击率低的项目。我们可以赌我们认为会赢的马。

🐎

在线指标和结果

我们如何知道我们对新项目的探索有多好?我们需要一些勘探吞吐量指标。在 Taboola 中,我们有 A/B 测试基础设施,支持在不同流量份额上运行的许多模型。

回到冰淇淋!假设你带了你的朋友来帮助你探索不同的口味。显然,如果你的一个朋友随机选择口味,他有最好的探索吞吐量,但不是最聪明的。另一个朋友点了别人觉得好吃的口味,他最喜欢,但对探索的努力毫无贡献。

在 Taboola,我们测量探索吞吐量如下:对于每个已经显示足够多次的项目,并且在足够多的不同上下文中(例如,不同的网站),我们声明该项目已经通过了探索阶段。接下来,我们分析哪些模型促成了这一成功。为了计数,一个模特必须展示那个项目足够多的次数。

从这个角度来看,模型的吞吐量被定义为它所贡献的项目数量。

使用这个度量标准,我们能够断言随机显示项目确实会产生最好的吞吐量,但是有坏项目的趋势。不使用 UCB 方法的模型显示出好的项目,但是吞吐量较差。具有 UCB 的模型在吞吐量方面介于两者之间,与非 UCB 模型相比,仅显示了稍微差一些的项目。

因此,我们得出结论,我们的 UCB 模型在探索新项目和选择好项目之间有一个很好的权衡。我们相信,从长远来看,这种权衡是值得的。

最后的想法

对于推荐系统领域的许多公司来说,探索-开发问题是一个令人兴奋的挑战。我们希望我们的进步将有助于其他人向他们的用户提供最好的服务。我们相信这是尚未完成的大旅程中的一小步,我们对这个领域在未来几年将会呈现什么样的形态很感兴趣。

在本系列的下一篇文章中,我们将详细阐述用于估算 CTR 和不确定性的模型,敬请关注。

这是与我们在今年 KDD 会议的研讨会上提交的论文相关的系列文章的第三篇: 深度密度网络和推荐系统中的不确定性

第一篇帖子可以在这里找到

第二篇帖子可以在 这里找到

原载于engineering.taboola.com由我和 约尔泽尔德斯

推荐系统:从过滤泡沫到意外收获

原文:towardsdatascience.com/recommender…

推荐系统为我们在互联网上看到的内容的日常互动提供了动力。随着每天创建超过 2.5 万亿字节的数据,仅过去两年就占了全球数据的 90%[1]。我们生产的内容是不可能在一生中消费的,这使得推荐系统不可避免。然而,正如本叔叔所说,权力越大,责任越大。在这里,我谈谈推荐系统引发的一些实践和伦理问题,以及我们如何着手解决这些问题。

推荐系统是做什么的?

推荐系统处于脸书、亚马逊、Spotify 等内容服务网站的最前沿。与用户互动。据说亚马逊网站 35%的收入是由其推荐引擎产生的[2]。在这种环境下,网站的目标是尽可能提供最好的个性化内容,这一点至关重要。

用户想要什么

作为一个超级推荐系统极客,每当我看到一个好的推荐系统时,我总是感到惊喜。我对消费大量内容感兴趣(我也是这样),但是一天中没有足够的时间让我亲自浏览所有可供我消费的内容。我希望推荐系统能够理解我在那一刻真正想读/听/消费的内容,甚至在我不知道自己想要什么的时候。

My beautiful Spotify generated playlists

过去,当内容可供您使用时(无论是通过搜索结果,还是通过其他方式搜索可用内容,如前 100 名列表),您必须使用某种过滤来缩小结果范围。这种过滤通常采用更长、更明确的搜索词、日期限制(包括和排除某些标签)等形式。

那些日子已经过去了。

作为一名用户,我希望能帮助我整理网站上的所有内容。我相信当我在谷歌上搜索std::list时,我会找到相关链接。与许多人相反,我也喜欢个性化广告。如果这些个性化广告真的让我意识到我正在失去生活中的一些重要物品,我会很乐意购买。然而,这也不意味着我喜欢看到同样的红木椅子跟随我在 12 个不同的网站上,仅仅因为我在 2003 年看过一把椅子。

听起来像被宠坏了吗?也许吧。但这就是我们现在生活的环境。当生成的内容呈指数级增长时,这是我们保持高效的唯一方法。

公司想要什么

如上所述,推荐系统为世界上许多主要品牌创造了大量收入。对某些人来说,这是用户与产品交互的最前端和最中心的方式(例如:脸书的新闻订阅)。对于其他人来说,它可能通过个性化(例如:谷歌的搜索引擎)来补充产品的核心功能,或者可能是尽可能长时间保持用户参与的一种方式(例如:YouTube 的 up next 功能)。

无论你如何定义它,企业就是企业,它们被不同于消费者的目标所驱动。改善客户体验对所有企业来说都是至关重要的,对于基于 web 的企业来说,这通常会导致试图使每个用户的体验更加个性化,从而产生对推荐系统的需求。

到目前为止一切顺利,对吧?

让我们仔细看看一个普通的网站如何利用推荐来改善用户体验:

  1. X 公司意识到,他们生成的内容比一个人能够互动的内容要多。此外,他们的竞争对手正在使用个性化,以便他们的用户可以更容易地访问相关内容。
  2. X 公司尝试个性化他们的搜索结果。他们选择一种可靠的方法,如矩阵分解,并利用他们的历史数据来训练模型。
  3. 试验取得了巨大的成功!A/B 测试显示收入增长了 4%,他们的月活跃用户增长了 13%!
  4. X 公司在他们的个性化方面投入了更多的资源。多个数据科学家正在致力于实现更好的机器学习模型,随着他们获取越来越多的数据,这些模型对他们的历史数据的执行越来越好。
  5. 他们将验证自动化,这样他们就可以以任何组合设置特定的收入/用户/转化率/其他 KPI 目标,并尝试使用 A/B/n 测试来优化它,这种测试会自动整合任何新的机器学习模型,不断提高推荐的质量,同时实现越来越好的目标。

image credit

这确实是梦寐以求的场景。您拥有可以为您的机器学习模型提供一些训练目标(损失函数)和测试目标的历史数据点,即使您知道它们不一定是推荐系统真实世界价值的良好指标,您也有 A/B 测试来优化真实世界的结果。这里的一切都可以定量分析。

嗯,期待推荐的“质量”。说到这里,你怎么定义推荐的质量呢?

推荐系统应该做什么?

如前所述,这种方法存在一些实践和伦理问题。

在像谷歌这样的庞然大物的背景下,推荐系统有能力影响我们社会的结构,不幸的是,我们还没有理解它如何影响社会的全部含义。很明显,谷歌为互联网上的大量信息流动提供了便利,因此被谷歌切断联系是一件大事。但是,当谈到量化这种信息流的影响时,我们被难住了。

这并不意味着我们不能思考推荐应该是什么样子的,也不能讨论推荐的一些重要概念。请记住,所有这些概念都是依赖于产品的:你必须选择它是与你相关还是与你的客户相关。

访问相关内容

这是显而易见的,但是为了完整起见,这是一个不应该被忽略的领域。如果你有一个推荐系统,你要确保它推荐的内容与用户相关。如果我只看奇幻类的书,那么我得到的推荐应该主要是偏向奇幻类的。这并不意味着我不喜欢偶尔的动作片或非小说类的推荐,但是我不应该看到我的推荐被这样的类型所主导。

这种推荐的罪魁祸首之一是推荐的协同过滤方法。继续这本书的例子,如果在其余的幻想读者中有一个占主导地位的第二类型,即使你不想消费它,协同过滤也会向你推荐这个类型。更糟糕的是,如果推荐系统依赖于明确的评分(1-5),你通常不得不消费并给它评分,以摆脱这样的推荐。

解决这个问题的一种方法是使用一种混合模式,这种模式也利用基于内容的推荐,这是我们为 Books2Rec 采用的解决方案。这样,您可以平衡协同过滤和内容相似性。

平衡收益递减

收益递减是计量经济学和心理学中一个广为人知的概念。这个概念很简单:你拥有的东西越多,你想要的就越少。

举个个人的例子,我真的很喜欢听一首歌,直到我厌倦他们,或者痛饮朋友,直到我根本无法处理罗斯和瑞秋之间的另一场争吵。

image credit

当然,这似乎与之前获得相关推荐的概念相违背;这将达到收益递减点。这里的关键是时间:你可能会厌倦在一次访问网站时看到相同类型的推荐(基于你的历史数据),但当你一周后回来时,你不想从远离你的历史数据的某个地方开始。

推荐系统通常区分历史推荐和上下文(基于会话的)推荐。历史推荐器利用你的全部历史数据来找到你想看的下一个节目,而上下文推荐器会查看你当前的会话(就像你在 Spotify/YouTube 会话的最后一个小时),并预测现在最适合你的节目*。*

当然,这两个系统不是一成不变的,肯定会有重叠,而合适的推荐系统介于两者之间。情境模型在机会成本低、快速消费可行的情况下效果最佳。收益递减也是情境模型的一个更大的问题。

长期回报

一般来说,推荐者的另一个问题是最佳推荐优先策略。想象一个推荐系统,它可以完全捕捉你在一个视频中的价值,并向你推荐你将会观看的最好的 5 个视频。

当你消费完最好的内容后会发生什么?

推荐系统会继续向你推荐相同的类型的内容,但是因为你已经消费了最好的,所以它们中的每一个都会比上一个

如果做得太好,推荐系统会减少你未来的享受。

what happens when no new content is added to a website, image credit

当然,现在每天都有大量的内容产生,很有可能对你来说最好的内容还没有产生。但事实是,对你来说,前 N 名内容(或对普通大众来说,前 N 名内容)是一个很难进入的列表,通过分隔最佳内容,你可能会获得更好的整体享受。

另一种思考方式是,我们应该把我们的建议的长期回报最大化作为目标。以这种方式,很容易将强化学习与 MDP(马尔可夫决策过程)和土匪等算法进行类比,特别是在探索与利用方面。毫无疑问,我们可以看到近年来人们对推荐系统的强化学习产生了浓厚的兴趣。

过滤气泡

前一种情况的另一个结果是一种被称为过滤器泡沫的现象。过滤气泡是由伊莱·帕里泽(Eli Pariser)创造的,并因他的同名书籍而闻名。过滤气泡是指你被困在一个气泡中,只有个性化算法(过滤器)认为你会喜欢的信息才能通过。随着你给系统越来越多的数据,你的泡泡变得越来越小,成为利基中的利基。你被困在一个正反馈循环中,无法逃脱。

image credit

这不仅会因为没有显示您可能喜欢的内容而限制您的选择,随着时间的推移,它会使您的兴趣趋于一致,从而导致您对不同类型的内容失去兴趣,这又会减少您的选择。更糟糕的是,因为回报递减,你开始越来越不喜欢推荐给你的内容。

不过,一切并没有失去。作为推荐系统的设计者,即使我们不能可靠地解决这个问题,我们也可以给用户一条出路。这并不需要太花哨——即使是一个不偏不倚的去个性化的搜索系统也可能给用户足够的工具来获取他们过滤泡沫之外的内容。即使您依赖隐式评级,用户也可以随着时间的推移将推荐转移到更多样化的地方。

注:一个与政治联系更紧密的相关术语是回音室,它与人们与志趣相投的人的互动以及越来越相信他们的想法是正确的有更多的关系。我不会掩盖这一点。

多样化

多元化是我们对抗过滤泡沫和收益递减效应的一种方式。不幸的是,它不是推荐系统的自然副产品;相反,我们必须为此而努力。

My YouTube recommendations

我观察到,根据用户口味的多样性,他们可以大致分为两类:

社交用户是你的典型用户,他们有一些松散定义的流派偏好,但对消费排名前 N 名的项目感到满足。如果某个内容很受欢迎,并且/或者已经被他们的朋友分享了,他们也想消费它。如果将它们的味道分布建模为高斯混合模型,则它们会有浅峰和大量重叠。

超级用户更关心消费他们最关心的内容。与社交用户相比,他们的数量较少,但他们仍占相当大的一部分。这些用户中的一些人一开始就有各种各样的兴趣。根据高斯混合模型,它们会有很高的峰值,中间会有很多死区。

保持和培养这些不同的兴趣符合我们的最佳利益。一些研究人员明确地模拟了这些口味[3],而另一些人则试图更有机地学习它们。正如稍后将解释的那样,即使对有高度特定兴趣的用户来说,强制使用某种多样性可能仍然符合您的最佳利益。

通过优化平均值,您最终会牺牲超级用户所依赖的高保真度推荐。

多样性的问题在于,很难准确衡量一组建议的多样性。在最近一集的 TWiML AI 中,Pinterest 数据科学家 Ahsan Ashraf 谈到了一套准则,你可以用它来检查任何推荐系统的健全性——多样性度量对:

  1. 稳定性:当我们给一个推荐系统相同主题/类型的项目时,它是否收敛到那个主题,导致推荐系统“过度适应”那个主题?收敛速度有多快?这些建议的差异有多大?
  2. 灵敏度:假设你有两个题目 AB ,从 AA的 100 项开始。随着您从 B 添加越来越多的项目,这些建议是否涵盖了整个范围(从 B 的一小部分到所有 B s)?你的多样性是在 50:50 的比例下最大化,在 100:0 和 0:100 下最小化吗?
  3. 理智:完全随机的推荐比用户的平均推荐更多样化吗?特定主题列表的多样性是否低于给用户的平均建议?

意外收获

从多样性向前迈了一步,我们来到了意外收获的概念,这是在没有寻找的时候发现了一些有价值或有益的东西,一个快乐的巧合。它是找到让你震撼的东西。

我喜欢把意外收获分成两类:立即的和最终的。即时的意外收获,嗯,立即发生。你走在街上,不知道为什么,你抬头一看,看到了帝国大厦。

最终的意外收获(这可能是一个误称,但我还没有找到一个更好的名称来形容它)是当你回顾你的生活,并意识到一个随机事件引导你踏上了塑造你的一部分的旅程。

为了认识到意外收获的重要性,想想那个让你发现你最喜欢的艺术家或你最好的朋友的偶然事件,或者当你意识到这就是你一直在寻找的一切的那一刻。

如果我们作为推荐系统的设计者能够促成这一事件,那不是很好吗?

从分析的角度来看,我们可以用一个代理来代表实际的意外收获(这是很难量化的),用户听说过该项目的概率很低,而用户喜欢该项目的概率很高。然后,问题变成了计算这两个概率,我们可以使用任何数量的方法来近似。

当然,这种代理并不理想,目前正在进行研究,试图找出一种更好的方法来解决这个问题。我们知道的一件事是,我们需要某种多样性或随机性来促进这一点。

显然,这使得某些问题比其他问题更容易解决。歌曲推荐比书籍推荐更容易实验。这是由于经济学家所谓的“机会成本”。每次你与某物互动时,你选择不与他人互动。在音乐中,如果你全神贯注地听这首歌,你可能会损失 4 分钟的时间,你甚至可以选择跳过它。多亏了音乐流媒体服务,你可能不需要额外付费就能接触到这些内容。

然而,一本书是一项长期投资。你可能要花 100 页才能意识到这本书并不适合你,到那时你可能已经花了几个小时和 10 美元来买这本书。因此,重要的是(平均而言)在做书籍推荐时要比音乐推荐更保守。

算法混淆的案例研究

普林斯顿大学的研究人员最近发表的一篇论文[4]讨论了推荐系统产生的反馈回路的进一步影响。更具体地说,他们研究了当模型试图捕捉用户偏好时发生的混淆,而没有考虑到用户面临的选择也是由模型产生的这一事实,从而削弱了推荐对用户行为的因果影响。

image credit

该文件提出的主要主张如下:

  1. 并非所有算法都受益于使用混杂数据(即,作为推荐系统的结果而生成的数据)的再训练。
  2. 使用混淆的数据来评估模型会偏向于将数据向适合算法的方向移动,从而创建不应该存在于底层数据中的反馈循环。
  3. 先前的反馈循环使单个用户和整个用户群都变得一致。

在不涉及细节的情况下,研究人员模拟了被推荐项目的用户之间的现实交互序列,推荐系统获得这些交互的结果,并使用该数据和随着时间的推移引入的新项目更新其推荐。

研究人员表明,就总效用而言,使用混杂数据在基于内容的模型和社会(基于信任)模型中效果最好,但在与矩阵分解模型一起使用时没有明显优势。此外,他们表明,与使用不同模型生成的混杂数据相比,使用同一模型生成的混杂数据评估模型会导致分数的更大提高。

因此,用混杂的数据评估你的模型可能会夸大你的模型的性能。

对于用于研究的公开可用数据集来说,这也是一个令人担忧的情况,这些数据集本身可能由于用于创建数据的推荐系统而被混淆,因此可能对某些类型的模型有隐藏的偏见。

研究人员还测量了同质化对总效用的影响,声称同质化是好的,因为它意味着模型正在学习底层模式,但也可能阻止发现可能对用户有益的新项目。

他们发现,对于除随机模型之外的所有 4 种方法,均一化(捕获为下图的 y 轴)与总效用(x 轴)负相关,根据定义,随机模型不依赖于任何模型。

image credit

因此,重要的是,我们要尽可能地保持我们的建议的多样性,但同时,要用对用户有意义的建议来平衡它们。同样,我们可以用强化学习方法来类比探索/利用问题。如果我们真的想达到最好的结果,我们需要找到正确的平衡并努力保持它。

结论

由于学术界和工业界越来越多的关注,我们今天有很多工具来提供建议,并且对我们的工具有了更好的理解。然而,在构建推荐系统时,我们还没有完全理解我们的选择的后果。天下没有免费的午餐,我们的选择总会有后果,即使我们无法立即看到。

我希望这篇文章能让你对如果你想给用户最好的体验,你需要留意什么有一些大概的了解。作为推荐系统的设计者,我们有能力影响什么能到达用户,什么不能。明智地使用这一权力取决于我们。

参考

1. Domo —数据从不睡觉 2。麦肯锡 3。代表不同兴趣用户的混合口味模型 4。推荐系统中的算法混杂如何提高同质性并降低效用

原载于 2018 年 10 月 9 日dorukkilitcioglu . github . io**

PyTorch 中使用深度学习的推荐系统

原文:towardsdatascience.com/recommender…

Photo by Susan Yin on Unsplash

推荐系统(RS)已经存在很长时间了,最近深度学习的进展使它们更加令人兴奋。矩阵分解算法一直是 RS 的主力。在本文中,我假设您对基于协同过滤的方法略知一二,并具备在 PyTorch 中训练神经网络的基本知识。

在这篇文章中,我的目标是向您展示如何从头开始在 PyTorch 中实现 RS。这篇文章中提出的理论和模型在这篇论文中可用。这里是本文的 GitHub 库

问题定义

给定用户过去看过的电影记录,我们将构建一个推荐系统,帮助用户发现他们感兴趣的电影。

具体来说,给定 < userID,itemID > 出现对,我们需要为每个用户生成电影的排序列表。

我们将问题建模为 二元分类问题 **,**其中我们学习一个函数来预测特定用户是否会喜欢特定的电影。

Our model will learn this mapping

资料组

我们使用 MovieLens 100K 数据集,该数据集有来自 1000 个用户对 1700 部电影的 100,000 个评级。数据集可以从这里下载。

评级以 < userID、itemID、评级、时间戳> 元组的形式给我们。每个用户至少有 20 个评级。

培养

我们放弃评级 (1,2,3,4,5) 的确切值,而是将其转换为隐式场景,即任何积极的互动都被赋予值 1。默认情况下,所有其他交互的值为零。

因为我们正在训练一个分类器,所以我们需要正样本和负样本。数据集中存在的记录被计为阳性样本。我们假设用户-项目交互矩阵中的所有条目都是负样本(一个强假设,并且容易实现)。

对于用户交互的每个项目,我们随机抽取 4 个没有被用户交互的项目。这样,如果一个用户有 20 次正面互动,他将有 80 次负面互动。这些负面交互不能包含用户的任何正面交互,尽管由于随机抽样,它们可能不都是唯一的。

估价

我们随机抽取 100 个没有被用户交互的项目,在这 100 个项目中对测试项目进行排序。这篇文章也采用了同样的策略,这也是这篇文章的灵感来源。我们在 10 处截断排名列表。

由于为每个用户排列所有项目太耗时,因为我们将不得不计算 10001700 个~10⁶值。采用这种策略,我们需要 1000100 ~ 10⁵值,少了一个数量级。

对于每个用户,我们使用测试集中的最新评级(根据时间戳),其余的用于训练。这种评估方法也称为留一策略,与参考文件中使用的方法相同。

韵律学

我们使用命中率(HR)和归一化折扣累积收益(NDCG)来评估 RS 的性能。

我们的模型为给定用户的测试集中出现的每个项目给出 0 到 1 之间的置信度得分。这些项目按得分降序排列,前 10 项作为推荐。如果测试项目(每个用户只有一个)出现在这个列表中,那么这个用户的 HR 就是 1,否则就是 0。在对所有用户进行平均后,报告最终 HR。对 NDCG 也进行了类似的计算。

训练时,我们将最小化交叉熵损失,这是分类问题的标准损失函数。RS 的真正优势在于给出用户最有可能与之交互的前 k 个项目的排序列表。想想为什么你大多只在第一页点击谷歌搜索结果,从来不去其他页面。像 NDCG 和人力资源这样的指标通过显示我们排名列表的质量来帮助捕捉这一现象。这里很好的介绍了评价推荐系统

基线:项目流行度模型

基线模型是我们用来为问题提供第一步、简单、不复杂的解决方案的模型。在推荐系统的许多用例中,向所有用户推荐相同的最受欢迎的项目列表给出了一个难以超越的基线。

在 GitHub 存储库中,您还可以从头开始找到实现项目流行度模型的代码。以下是基线模型的结果。

基于深度学习的模型

有了神经网络所有花哨的架构和行话,我们的目标是击败这个项目流行模型。

我们的下一个模型是深度多层感知器(MLP)。模型的输入是 userID 和 itemID,它们被送入嵌入层。因此,每个用户和项目都有一个嵌入。之后是多个致密层,接着是一个具有 s 形激活的单个神经元。确切的模型定义可以在文件 MLP.py 中找到。

乙状结肠神经元的输出可以被解释为用户可能与物品交互的概率。有趣的是,我们最终训练了一个用于推荐任务的分类器。

Figure 2: The architecture for Neural Collaborative Filtering

我们的损失函数是二元交叉熵损失。我们使用 Adam 进行梯度下降,使用 L-2 范数进行正则化。

结果

对于基于流行度的模型,训练时间少于 5 秒,以下是得分:

HR = 0.4221 | NDCG = 0.2269

对于深度学习模型,我们在近 30 个时期的训练(在 CPU 上约 3 分钟)后获得了这些结果:

HR = 0.6013 | NDCG = 0.3294

结果令人振奋。我们关心的指标有了巨大的飞跃。根据 HR,我们观察到误差减少了 30%,这是巨大的减少。这些数字是从非常粗略的超参数调谐中获得的。通过超参数优化仍有可能提取更多汁液。

结论

使用神经网络可以很容易地复制矩阵分解的最先进算法,以及更多。从非神经的角度来看,请阅读这篇关于推荐系统的矩阵分解的精彩文章。

在这篇文章中,我们看到了神经网络如何提供一种构建推荐系统的简单方法。诀窍是把推荐问题想成一个分类问题。我们在 PyTorch 中实现了一个推荐系统。我们将我们的结果与非个性化基线算法进行了比较,观察到了显著的收益。

为了获得更深入的理解,我鼓励你阅读原文(链接如下)并前往 GitHub 知识库阅读这篇文章。

参考资料:

  1. 该论文还链接到作者在 Keras 中的实现:www.comp.nus.edu.sg/~xiangnan/p…

具有深度学习架构的推荐系统

原文:towardsdatascience.com/recommender…

iki daily feed screenshot

这篇文章解决了构建基于深度学习的推荐系统的一般问题。本文中描述的特定架构是为 iki 服务 的新智能馈送供电的架构,每天推送您的技能——要检查其性能,请尝试 产品测试版

如果你对推荐系统的一般概念和主流方法很熟悉,并且想直接了解我们解决方案的细节,请跳过本文的前两部分。

介绍

推荐系统改变了我们与许多服务互动的方式。他们提供的不是静态数据,而是互动体验,可以选择留下你的反馈,并对给你的信息进行个性化处理。推荐系统独立地为每个用户创建个性化信息流,但是也考虑服务的所有用户的行为。

用户通常不可能通过检查在线影院中的电影、在线商店中的消费品或任何其他内容中的每一个来在各种给定选项中进行选择。如果服务中有大量内容,用户会面临信息过载的问题。解决这个问题的方法之一是利用有针对性的建议。

目标内容扮演重要角色的服务的一个例子是 iki。iki 是您的个人职业生涯和专业顾问,由机器学习和人工智能相关技术提供动力。在用户定义了他的专业兴趣领域之后,iki 在具有下述深度学习架构的推荐系统的帮助下,创建每日内容提要,开发知识和选择的技能。

推荐系统问题设置概述

任何推荐系统中存在的主要对象是用户 U、项目 R 以及它们之间的一些交互。这些交互通常以矩阵 F(|U|x |R|)的形式呈现,每个单元格包含一些关于交互的信息——某个商品被浏览/购买的事实、给定的评分、喜欢/不喜欢或其他信息。根据系统中用户和项目的数量,F 矩阵可以变得非常大。但是每个用户通常给出一些评级或者仅与系统中的一小部分项目交互,这导致非常稀疏的评级矩阵。众所周知,可以减少稀疏模型的独立参数的数量,而不会显著损失信息。我们遇到了在推荐系统中正确表示评价数据的问题:我们必须借助于具有固定长度 n 的向量将每个用户映射到某个向量空间,F 矩阵的 n<奇异值分解,这给出了两个具有形状|U| x n 和 n x |R|的矩阵,对于给定的 n,它们的乘积是矩阵 F 的最佳近似。让我们把这些向量相应地称为用户和项目的“SVD 嵌入”。

推荐系统中产生预测传统方法 协同过滤。这种方法的主要假设是,过去对某些项目给出相似评级的用户将来倾向于对其他项目给出相似的评级。解决协同过滤问题的最流行的方法之一是利用初始稀疏评级矩阵 f 的 SVD 创建用户和项目的密集嵌入。为了预测用户 u 对项目 I 的新评级值,我们必须计算相应用户和项目的 SVD 嵌入的标量积。

有几个开放库提供了基于用户评级矩阵构建各种推荐系统的工具。其中,我们可以列举各种矩阵分解方法(SVD,非负矩阵分解 NMF) *、*最近邻算法的不同变体、协同聚类方法、基于用户和基于项目的模型。这种解决方案的例子有惊喜库LightFM 库

在推荐系统中,除了用户与项目的交互信息之外,通常还有一些其他数据分别描述用户和项目。这些数据可能是变化的和异构的——项目和用户可能包含一些文本描述、数字特征、分类特征、图像和其他类型的数据。处理和使用该数据的方式取决于特定的问题,并且不是推荐系统的通用部分。

推荐系统的 主要属性或度量是推荐相关性可扩展性在用户或项目数量非常大的情况下。该设置带来了由两个连续 : 候选生成器排序模块组成的通用推荐系统架构,候选生成器从大项目集中选择相对较小的子集,排序模块对所选子集中的每个项目给出与用户兴趣相关的等级。此评级通常用于按照用户看到的顺序排列项目。

深度学习驱动的推荐系统架构

具有深度学习架构的基于内容的推荐系统与系统中存在的实际内容密切相关。接下来,我们将深入 iki 推荐系统的细节来描述 DL 方法。

在 iki 中,用户与内容的交互是视图和评级——用户可以喜欢或不喜欢任何内容元素。在我们的例子中,一个条目是一个包含博客、文章、教程、课程或其他提供专业知识的内容的网页。每个内容元素都有文本描述或者本身就是文本。

内容嵌入是文本矢量化过程的结果。我们使用具有 300 个维度的手套语言模型来对单独的单词进行矢量化。GloVe 是斯坦福大学开发的语言模型,类似于 word2vec(保留单词的语义相似性)。不同之处在于,除了保留语义手套之外,还利用了统计方法,考虑了文本语料库中出现的单词数量。

我们统计文本语料库中所有出现的单词的 tf-idf 权重,然后计算内容元素对应的向量,作为该内容元素中单词的手套向量与 tf-idf 权重的加权和。

用户可以向他的简档添加一些关于他当前和过去工作的信息(例如职位名称和每个工作的描述)。我们的系统处理这些数据,并使用专门开发的特殊语言模型技能提取器提取每个用户的专业技能列表。简而言之,该模型的思想是在英语简历的语料库上学习“技能”的语义,并能够从非结构化的英语文本中提取以前未见过的技能。用户也可以手动添加一些技能。为了调整内容提要,用户必须在我们的分层标签云上选择一些专业兴趣领域,类似于苹果音乐机制。iki 提供了数百种专业兴趣作为用户内容提要的一部分。在推荐系统中,用户的技能和选择的标签被用作用户特征。

iki recommender system architecture

用于训练我们的推荐系统的另一组特征是在将 SVD 应用于用户评级矩阵之后获得的内容和用户嵌入。这不是关于系统中的评级和观看的信息的唯一用途——我们创建用户的向量表示来描述他的观看和评级的内容。这是通过以下方式实现的:拥有内容的向量表示让我们取用户观看内容的整个语料库上每个坐标的最小值、最大值和平均值。这个操作产生了三个与内容嵌入长度相同的向量。我们对喜欢和不喜欢的内容集执行相同的操作,获得具有相同长度的另外 6 个向量。在推荐系统的训练步骤中,这些密集嵌入也被用作用户的特征。

User’s liked, disliked and viewed content in content vector space depicted as a 3D space for simplicity

候选人生成

对于每个标签(内容主题),我们的推荐系统创建一个单独的推荐集,特别是选择一个候选子集进行进一步排名。iki 推荐系统的目标不仅是提供相应主题的内容,还根据质量和专业水平对内容元素进行排序,以使结果提要符合每个用户的专业水平

让我们更深入地了解候选人生成步骤的细节。对于每个标签,我们的系统在以下机制的帮助下选择用于排序的候选:选择最近已经接收到特定用户的正面评级的内容元素,并且将这些选择的内容元素周围的内容元素集合(我们取内容嵌入空间中的几个邻域的并集)添加到候选空间中。我们还考虑在所选标签上具有相似最近活动的用户集合(为这组用户计算评级和观看向量之间的余弦距离)。最近观看或评级的内容元素的集合也被添加到候选集合中。所描述的邻域半径的变化提供了不同数量的生成候选。

候选人排名

对于每个标签,单独的神经网络实例被训练并用于内容候选排序。每个神经网络实例被训练,然后用于在对应于该标签的内容子集上进行排名。排名网络将关于内容元素和用户的一些数据作为输入,并输出 1 和-1 之间的某个数字作为用于内容元素的最终排名的预测评级。该模型在给定评级和视图的集合上被训练;集合中的每个样本由一个用户和一个内容元素组成,目标值为 1 表示喜欢,-1 表示不喜欢,如果用户只是查看了内容而没有给出任何评级,则为 0。

iki ranking neural net architecture

排名网络有 6 个不同维度的输入。第一个输入获取关于用户选择的标签和他的技能的信息,该信息被转换成嵌入在下一层中的密集用户简档。该信息允许系统在对与特定兴趣相对应的内容进行排名时考虑特定用户的其他兴趣(标签)。iki 中的标签数量有限,因此关于它们的信息用固定长度的二进制编码向量进行编码(1 用于选定的标签,0 用于其余的标签)。

用 SkillsExtractor 从用户简历中提取的技能可能变化很大,所以我们从统计上对它们进行标准化,以消除真正罕见的短语,最终得到一个固定长度的集合。然后,我们用与标签相同的二进制方式对技能进行矢量化。在定期推荐系统训练期间,我们更新实际的技能集。

接下来的两个输入采用我们之前描述过的用户向量表示,包括评级和观看的平均内容嵌入。它们的输出在下一层中被合并,这在内容嵌入空间中创建了密集用户嵌入,我们称之为密集用户偏好嵌入。这两个嵌入与采用内容和用户的 SVD 嵌入以及内容嵌入的其他输入相结合。获得的向量向前传递,并通过神经网络的几个密集层。最后一个网络层的激活函数是 tanh,它给出了[-1,1]走廊中的输出值。

iki 中的冷启动问题通过以下方式解决:对于没有任何评级的特定标签,系统向新用户推荐一组彼此之间距离很大的随机内容元素。这为新用户在体验之初提供了各种完全不同的内容元素供其选择。

结果和模型的基准

在构建上述最终模型之前,我们已经在研究阶段测试了各种推荐系统模型和组合(如奇异值分解、NNMF 等人)。显然,我们最终得到的模型在特定的 iki 数据集上显示了最佳的结果准确性。为基于深度学习的推荐系统提供有意义的基准并不是一项简单的任务,因为排名网络的架构是为特定类型的数据量身定制的,很明显,这种特定的架构优于任何基于交互矩阵的推荐系统,因为它利用了更多用于形成预测得分的数据。

另一个问题是,当你没有足够大的真实用户群和所有的简档和收视率数据时,很难模拟真实的推荐系统性能。你可以生成一个数据集,但生成一个嘈杂的数据集是没有意义的,所以你会为一些用户定制一些技能、经验和偏好,你的推荐系统会解码这些信息,并根据你生成的人工用户配置文件定制推荐。这种方法没有给真正重要的真实世界的自发用户行为处理留下空间。

推荐古腾堡计划的书籍

原文:towardsdatascience.com/recommendin…

为年轻读者选择他们喜欢阅读的书籍对父母和教育工作者来说是一个挑战。我的解决方案是建立一个推荐引擎,既考虑阅读水平,也考虑最近阅读的书籍的主题,并返回下一步阅读的建议。

Photo by freestocks on Unsplash

我从古腾堡计划中提取数据,该网站提供超过 53,000 本在美国版权已经过期的免费电子书。

**背景:**为了正确理解和享受一篇文章,从而作为一个读者继续发展,让孩子们接触到对他们来说既有吸引力又在他们最近发展区内的书籍是很重要的。

最近发展区,通常缩写为 ZPD,是一个描述孩子自己能做什么,在帮助下能做什么,以及超出他们能力的概念。在阅读发展中,我们希望确保孩子们既能准确地阅读单词,又能理解他们正在阅读的内容,否则他们会对文本感到厌倦或沮丧。

阅读水平:我的推荐引擎很大一部分是基于阅读水平的,其中有许多不同的衡量标准。我想确保推荐的文本在读者的 ZPD 范围内,所以我需要找到一种方法来量化读者能读和不能读之间的差距。一些阅读水平是用基于单词长度和句子长度的公式计算出来的,其他的考虑单词难度和等级水平单词列表。在这个项目中,我选择使用Flesch Kincaid Grade Level来衡量阅读水平。我做了这个选择,因为这是一个被广泛接受的方法,也可以直接计算,而不需要输入等级级别单词列表。考虑到我的数据集中的大部分书籍都是在当前的年级词汇标准之前编写的,我对使用年级词汇列表持谨慎态度,并且我希望能够比较不同时期的书籍。

使用以下公式计算文本的 Flesch Kincaid 等级级别:

得出的数字在某种程度上与孩子在学校的年级水平相对应,尽管实际上每个年级学生的阅读水平差异很大。

还有另一个相关的衡量标准,叫做 Flesch reading-easy test,分数越高说明材料越容易阅读,分数越低说明段落越难阅读。90 分以上的分数表明这篇课文非常易读,一般 11 岁的学生都能很容易理解。分数低于 30 表明该文本很难阅读,最好由大学毕业生理解。Flesch 轻松阅读的公式是:

我选择使用 Flesch Kincaid 等级标准,因为随着书的难度增加,阅读水平也会提高,这更直观。我确实计算了每本书的阅读水平。从这一点开始,当我提到阅读水平时,我指的是弗莱施.金凯的水平。

推荐系统

我的推荐系统的前提是将读者最近喜欢阅读的书名作为输入,并输出推荐书籍的列表。我这样做是通过首先获得图书馆中可用书籍的子集,只包括比他们输入的书籍的阅读水平低一个年级和高四分之一年级的书籍。

一旦我有了可接受书籍的子集,我就确定这些书籍的主题有多相似。我使用 Jaccard 相似度来比较由提交文本的人确定的主题列表。

The Jaccard similarity of the subjects of these two books is high because the two books have a lot of subjects in common.

我也想比较一下这两本书的实际内容。我找到了书籍的计数矢量化文本之间的余弦距离。余弦距离显示了这些书的字数是多么的不同。

此外,我还计算了这些书的 tf-idf 矢量化文本之间的余弦距离。这些距离是基于单词在书中的重要性。

这给了我三个书籍推荐,我将在下面进行比较。

结果分析

主题相似度结果: 主题相似度推荐的书看起来很有推荐的合理性。例如,彼得潘的顶级搜索结果是发生在奥兹国的书籍,它们在“少年”、“幻想”、“想象”、“虚构”和“地方”上匹配。一个喜欢关于一个幻想世界的书的读者可能想读一个不同的世界,这是有道理的。然而,如果这本书的写作风格截然不同,读者可能仍然不会喜欢这本书,所以我将在下面分析基于文本的建议。

Count vectorizer vs . tf-idf: 我的第一直觉是 TF-IDF 会给出比 Count vectorizer 更好的推荐,因为它更关注单词的重要性,而不仅仅是单词数。因此,根据 count vectorizer,长度非常不同但单词相似的两本书最终会非常不同,但使用 tf-idf 会非常相似。事实上,从一些 count 矢量化书籍和 tf-idf 书籍的热图中可以看出,tf-idf 书籍总体上比 count 矢量化书籍显示出更多的相似性。然而,有几本书,如《曾达的囚徒》和《彼得潘》,在 count 矢量化热图中表现得特别明显。

The rectangle showing the relationship between The Prisoner of Zenda (book 95) and Peter Pan (book 16) is much darker when the Count Vectorizer is used.

通过查看由它们的计数向量距离和它们的 tf-idf 向量距离聚类的相同书籍,也可以清楚地看到,这些书籍最终被不同地聚类。

最终,哪些距离将更好地提供推荐仍然不清楚,直到我们看到哪些实际的单词被抽出。

为彼得潘使用计数矢量器方法的第一本书是曾达奖。

这可以与使用 tf-idf 矢量器的彼得潘的顶级书籍进行比较:

从这些表格以及其他推荐给彼得潘的书和我看过的其他书的类似数据来看,很明显 tf-idf 推荐的书大多基于相似的角色名字。以此为基础推荐书籍并不理想。例如,尊贵的彼得·斯特林与彼得潘毫无关系,我也不会向喜欢彼得潘的孩子推荐这本书。因此,看起来计数矢量器是目前推荐书籍的更好方法。

**结论:**我的推荐系统的管道有效地提取了书籍,但是为了评估这些推荐的书籍有多好,还需要进一步的分析。我对如何做到这一点有一些想法:

1.我想为我的推荐者建立一个用户界面,让用户完成一项调查,陈述他们认为自己对这三种方法的推荐有多好。

2.我还会将我的系统给出的推荐与外部来源(比如亚马逊)的推荐进行比较。这确实带来了一些问题,例如亚马逊推荐的大多数书籍在古登堡计划中不可用,但这是一个如何评估我的系统的想法,如果我获得更多数据的话。

3.更直接的是,我想通过重新运行 tf-idf 矢量器来过滤掉专有名词,从而尝试改进我的建议。这将有助于解决上面的问题,《彼得·斯特林》是推荐给彼得潘的第一本书,因为这两本书的主要人物都叫彼得。

使用 Google BigQuery 和隐式库推荐 GitHub 存储库

原文:towardsdatascience.com/recommendin…

跟踪 GitHub 中发布的所有优秀的资源库是一项不可能的任务。趋势列表帮助不大。正如你可能已经读到的,按受欢迎程度排序并不像看起来那么简单。这里列出的大多数东西通常与我使用的堆栈无关。

这是推荐系统的一个很好的用例。它们帮助我们以个性化的方式过滤信息——存储库。当我们不知道该键入什么时,我认为它们是一个搜索查询。

推荐系统有几种方法,但通常分为两大类,协作式和基于内容的过滤。以下是 W ikipedia 的定义:

协同过滤根据用户过去的行为(之前购买或选择的项目和/或对这些项目的数字评级)以及其他用户做出的类似决定来构建模型。基于内容的过滤方法利用项目的一系列离散特征来推荐具有相似属性的附加项目。

在这种特殊的情况下,很难应用基于内容的方法,因为很难直接通过内容来度量存储库的相似性:代码、文档、标签等等。协同过滤更适合,也更容易应用。正如我在之前的帖子中所说,即使人们没有对你的内容进行评级,隐性反馈也足够了。

在这种情况下,我们可以使用当前流行的程序员冷静作为隐式反馈库星星:

Stolen from a talk that Julia and I gave at Porto Alegre Machine Learning Meetup (in Portuguese)

我们将从 Google Big Query 获得本月给出的随机恒星样本,并使用令人惊叹的隐式库,该库实现了精彩论文对隐式反馈数据集的协同过滤。算法本身我就不说了,不过你可以看看论文或者这篇博文来自 Ben Frederickson 《隐式》的作者。

以下是获取数据的查询:

WITH stars AS (
     SELECT actor.login AS user, repo.name AS repo
     FROM githubarchive.month.201706
     WHERE type="WatchEvent"
),
repositories_stars AS (
     SELECT repo, COUNT(*) as c FROM stars GROUP BY repo
     ORDER BY c DESC
     LIMIT 1000
),
users_stars AS (
    SELECT user, COUNT(*) as c FROM  stars
    WHERE repo IN (SELECT repo FROM repositories_stars)
    GROUP BY user HAVING c > 10 AND C < 100
    LIMIT 10000
)
SELECT user, repo FROM stars
WHERE repo IN (SELECT repo FROM repositories_stars)
AND user IN (SELECT user FROM users_stars)

请注意,我筛选了排名前 1000 的存储库,并随机抽取了 10000 名给排名前 1000 的存储库打了 10 到 100 星的用户。我们希望对关注热门项目的人进行抽样调查,但我们不想让那些给所有东西都打星的用户,因为他们不会添加太多信息。

重要的是要意识到我们不需要所有用户的所有明星来给每个人推荐。添加更多数据会提高推荐质量,但也会增加训练时间。如果我们采样正确,就不会损害模型精度。

好了,说够了,我们如何获得数据和训练模型?

data = pd.io.gbq.read_gbq(query, dialect="standard", project_id=project_id)# map each repo and user to a unique numeric value
data['user'] = data['user'].astype("category")
data['repo'] = data['repo'].astype("category")# create a sparse matrix of all the users/repos
stars = coo_matrix((np.ones(data.shape[0]),
                   (data['repo'].cat.codes.copy(),
                    data['user'].cat.codes.copy())))# train model
model = AlternatingLeastSquares(factors=50,
                                regularization=0.01,
                                dtype=np.float64,
                                iterations=50)confidence = 40
model.fit(confidence * stars)

仅此而已。只有 7 行 Python。而且快得惊人。在不到 10 秒的时间内提取数据并训练模型**。我选择了通常工作良好的参数,但是如果我们认真对待它,我们应该做一些验证。我们跳过这个,直接看结果。与 tensorflow 有什么相似之处?**

# dictionaries to translate names to ids and vice-versa
repos = dict(enumerate(data['repo'].cat.categories))
repo_ids = {r: i for i, r in repos.iteritems()}model.similar_items(repo_ids['tensorflow/tensorflow'])][(u'tensorflow/tensorflow', 1.0000000000000004),
 (u'jikexueyuanwiki/tensorflow-zh', 0.52015405760492706),
 (u'BVLC/caffe', 0.4161581732982037),
 (u'scikit-learn/scikit-learn', 0.40543551306117309),
 (u'google/protobuf', 0.40160716582156247),
 (u'fchollet/keras', 0.39897590674119598),
 (u'shadowsocksr/shadowsocksr-csharp', 0.3798671235574328),
 (u'ethereum/mist', 0.37205191726130321),
 (u'pandas-dev/pandas', 0.34311692603549021),
 (u'karpathy/char-rnn', 0.33868380215281335)]

看起来没错!列表中的几乎所有内容都与机器学习和数据科学有关。

产生用户推荐呢?嗯,我们可以直接使用model.recommend为训练集中的用户获得推荐,但我们需要从 GitHub API 为所有其他用户获得用户星级。

下面是从 GitHub 的 API 中获取星星并创建一个新的用户项目矩阵的代码。

def user_stars(user):
    repos = []
    url = "[https://api.github.com/users/{}/starred](https://api.github.com/users/{}/starred)".format(user)
    while url:
        resp = requests.get(url, auth=github_auth)
        repos += [r["full_name"] for r in resp.json()]
        url = resp.links["next"]["url"] if "next" in resp.links else None
    return reposdef user_items(u_stars):
    star_ids = [repo_ids[s] for s in u_stars if s in repo_ids]
    data = [confidence for _ in star_ids]
    rows = [0 for _ in star_ids]
    shape = (1, model.item_factors.shape[0])
    return coo_matrix((data, (rows, star_ids)), shape=shape).tocsr()

好的,我应该检查哪些存储库?

def recommend(user_items):
    recs = model.recommend(userid=0, user_items=user_items, recalculate_user=True)
    return [(repos[r], s) for r, s in recs]jbochi = user_items(user_stars("jbochi"))
recommend(jbochi)[(u'ansible/ansible', 1.3480146093553365),
 (u'airbnb/superset', 1.337698670756992),
 (u'scrapy/scrapy', 1.2682612609169515),
 (u'grpc/grpc', 1.1558718295721062),
 (u'scikit-learn/scikit-learn', 1.1539551159232055),
 (u'grafana/grafana', 1.1265144087278358),
 (u'google/protobuf', 1.078458167396922),
 (u'lodash/lodash', 1.0690341693223879),
 (u'josephmisiti/awesome-machine-learning', 1.0553796439629786),
 (u'd3/d3', 1.0546232373207065)]

我发现这些建议非常有用。请注意,我们传递了一个全新的用户评级矩阵,其中只有一个用户,并设置了标志recalculate_user=True

这个功能是最近添加的,可以用来为不在训练集中的用户生成推荐,或者在他或她消费更多项目时更新用户推荐。

我添加到库中的另一个无耻的功能是解释推荐的能力:

def explain(user_items, repo):
    _, recs, _ = model.explain(userid=0, user_items=user_items, itemid=repo_ids[repo])
    return [(repos[r], s) for r, s in recs]explain(jbochi, 'fchollet/keras')[(u'pandas-dev/pandas', 0.18368079727509334),
 (u'BVLC/caffe', 0.15726607611115795),
 (u'requests/requests', 0.15263841163355341),
 (u'pallets/flask', 0.15259412774463132),
 (u'robbyrussell/oh-my-zsh', 0.1503775470984523),
 (u'apache/spark', 0.12771260655405856),
 (u'tensorflow/tensorflow', 0.12343847633950071),
 (u'kripken/emscripten', 0.12294875917036562),
 (u'videojs/video.js', 0.12279727716802587),
 (u'rust-lang/rust', 0.10859551238691327)]

它会返回我标上星号的对某个特定推荐贡献最大的存储库。结果意味着模型推荐 keras 因为我主演过熊猫咖啡馆

我希望你喜欢!这是笔记本,上面有你用用户名运行它所需的所有代码

别忘了给一颗星。本应得的。

Tensorflow 中的重建主分量、全局对比度、范围归一化层[TF 中的手动反推]

原文:towardsdatascience.com/reconstruct…

Photo by Daniel Olah on Unsplash

今天,我想练习我的矩阵演算,以及创造性地创建一些层。我认为实现不同的层是一个好主意,可以在以后使用。下面是我想练习的层次列表。

a .行均值减法层 b .行标准差层 c .范围归一化层 d .全局对比度归一化层 e .重构主成分层

请注意,这篇帖子是出于我自己的教育目的。

a .逐行均值减去图层

让我们从简单的开始,只是减去平均行的方式。如上所述,导数非常简单。

以上是一个张量流实现,另外,我已经创建了一个训练阶段变量,所以在测试阶段我们可以使用训练数据的移动平均值。

让我们做一个简单的健全性检查,在层之前,每一行数据的平均值是 0.4229564897125843,在层之后,它非常接近于零。此外,我们可以看到移动平均平均值已更新为 0.042,这是意料之中的,因为我使用的 beta 值为 0.9。最后,梯度在我们预期的范围内,因为 1-(1/4096) = 0.999755859。

b .逐行标准差层

这一层实际上并不做太多事情,而是简单地计算每一行的标准偏差值并将其返回。(我只是想练习一下。)

上面是一个 tensorflow 实现,我再次设置了训练阶段变量来区分训练和测试阶段。

在通过该层传递数据之后,我们可以看到它已经正确地返回了每个数据行的标准偏差值。

c .范围归一化层

Image from this website

可以对任何范围的值进行归一化,而不仅仅是 0 和 1。使用这个概念,我们还可以通过层进行反向传播。首先让我们用手算一下。

现在,既然已经完成了,让我们用张量流来实现它。

看起来很棒,现在我们可以做简单的健全性检查,看看该层是否正常工作。(同样,对于这一层,我有一个移动最小值以及移动最大值。)

我将最大范围设置为 10,因此我们在范围 0-10 之间归一化我们的图像数据。我们看不出差别,但是所有数据的平均值增加了大约 4。

d .全局对比度归一化图层

Image from this website

GCN 可以用作预处理步骤,但是由于大多数计算只是简单的矩阵运算,我们也可以通过它反向传播。(但请注意,我将使用 GCN 的一个软化版本,而不是一个最大值函数,我只是将ε值相加,以避免被零除。).如果您想了解更多,请点击此处

现在我们有了计算方法,让我们用 numpy 实现来看看这个函数是如何改变每张图片的对比度的。

从第二幅图像中,我们可以看到标准化后它变得更亮,但最明显的是当前图像批次的标准偏差降低了。

同样,我创建了一个移动平均变量,以防万一,如果我们想区分培训和测试阶段。

我们可以看到,我们的 GCN 层给出了与 numpy 版本相同的结果。

e .重建主构件层

这一层的想法非常简单,使用 PCA,我们可以通过执行原始数据和投影矩阵之间的矩阵乘法来执行降维。但是我们也可以仅使用主成分的顶部 k 个元素来重建原始数据。因为所有的运算都是矩阵运算,我们可以通过它反向传播。

在重建之后,我们现在可以看到所有的图像都是彼此混合的,并且图像批次的平均值和标准值是 0.37 和 0.19。

最后,让我们做一个简单的健全性检查,看看高级 api (sklearn 分解 pca)是否给我们一个类似的结果。在第三个数字之后,值改变了,这可能是因为我使用特征值分解,而 sklearn 使用奇异值分解。

交互代码

对于 Google Colab,您需要一个 Google 帐户来查看代码,并且您不能在 Google Colab 中运行只读脚本,因此请在您的操场上创建一个副本。最后,我永远不会请求允许访问你在 Google Drive 上的文件,仅供参考。编码快乐!

要获取这篇文章的代码,请点击这里。

遗言

我应该用 tf 梯度来仔细检查所有的梯度是否正确,这是我的错误。

如果发现任何错误,请发电子邮件到 jae.duk.seo@gmail.com 给我,如果你想看我所有写作的列表,请在这里查看我的网站。

参考

  1. tensorflow?,h. (2018)。如何在 tensorflow 中定义条件语句?。堆栈溢出。检索于 2018 年 10 月 22 日,来自https://stack overflow . com/questions/41789209/how-to-define-condition-statement-in-tensor flow
  2. 误差,t. (2018)。tensorflow:在 tf.map_fn 的 fn 中创建变量时返回值错误。堆栈溢出。检索于 2018 年 10 月 22 日,来自https://stack overflow . com/questions/45789822/tensor flow-creating-variables-in-fn-of-TF-map-fn-returns-value-error
  3. 数字/数字。(2018).GitHub。检索于 2018 年 10 月 22 日,来自https://github . com/numpy/numpy/blob/v 1 . 15 . 1/numpy/core/_ methods . py
  4. 标准差。(2018).Davidmlane.com。检索于 2018 年 10 月 22 日,来自davidmlane.com/hyperstat/A…
  5. (2018).Dotnetperls.com。检索于 2018 年 10 月 22 日,来自www.dotnetperls.com/arg-max-ten…
  6. Scherfgen,D. (2018)。导数计算器带步骤!。Derivative-calculator.net。检索于 2018 年 10 月 22 日,来自www.derivative-calculator.net/
  7. TensorFlow,A. (2018)。在张量-张量流中调整单个值。堆栈溢出。检索于 2018 年 10 月 22 日,来自https://stack overflow . com/questions/34685947/adjust-single-value-within-tensor-tensor flow
  8. Tensorflow,B. (2018)。张量流中的二元掩码。堆栈溢出。检索于 2018 年 10 月 22 日,来自https://stack overflow . com/questions/40443951/binary-mask-in-tensor flow
  9. tf.self _ 共轭 _eig | TensorFlow。(2018).张量流。检索于 2018 年 10 月 23 日,来自https://www . tensor flow . org/API _ docs/python/TF/self _ agreement _ EIG
  10. 五氯苯甲醚,O. (2018)。从 sklearn PCA 获得特征值和向量。堆栈溢出。检索于 2018 年 10 月 23 日,来自https://stack overflow . com/questions/31909945/get-eigen-values-and-vectors-from-sk learn-PCA
  11. http://scikit-learn/scikit-learn。(2018).GitHub。检索于 2018 年 10 月 23 日,来自https://github . com/sci kit-learn/sci kit-learn/blob/BAC 89 c 2/sk learn/decomposition/PCA . py # L106
  12. tf.self _ 共轭 _eig | TensorFlow。(2018).张量流。检索于 2018 年 10 月 23 日,来自https://www . tensor flow . org/API _ docs/python/TF/self _ agreement _ EIG
  13. (2018).Arxiv.org。检索于 2018 年 10 月 23 日,来自arxiv.org/pdf/1709.06…
  14. (2018).People.maths.ox.ac.uk 检索 2018 年 10 月 23 日,来自people.maths.ox.ac.uk/gilesm/file…
  15. 矩阵?,H. (2018)。如何标准化一个矩阵?。堆栈溢出。检索于 2018 年 10 月 23 日,来自https://stack overflow . com/questions/4544292/how-do-I-standard-a-matrix/40951248
  16. PreMBA 分析方法。(2018).Ci.columbia.edu。检索于 2018 年 10 月 23 日,来自ci.columbia.edu/ci/premba_t…
  17. 1?,H. (2018)。如何将-1 和 1 之间的数据归一化?。交叉验证。检索于 2018 年 10 月 23 日,来自https://stats . stack exchange . com/questions/178626/how-to-normalize-data-between-1-and-1
  18. 标准差公式。(2018).Mathsisfun.com。检索于 2018 年 10 月 23 日,来自https://www . mathsisfun . com/data/standard-deviation-formulas . html
  19. 实施主成分分析(PCA)。(2014).塞巴斯蒂安·拉什卡博士。检索于 2018 年 10 月 23 日,来自https://sebastianraschka . com/Articles/2014 _ PCA _ step _ by _ step . html
  20. (2018).Cedar.buffalo.edu。检索 2018 年 10 月 23 日,来自https://cedar . buffalo . edu/~ Sri Hari/CSE 676/12.2% 20 computer % 20 vision . pdf
  21. 预处理),W. (2018)。标准化和全局对比度正常化有什么区别?(图像预处理)。堆栈溢出。检索于 2018 年 10 月 23 日,来自https://stack overflow . com/questions/27955695/标准化和全局对比标准化的区别是什么
  22. 预处理),W. (2018)。标准化和全局对比度正常化有什么区别?(图像预处理)。堆栈溢出。检索于 2018 年 10 月 23 日,来自https://stack overflow . com/questions/27955695/what-the-difference of-standardization-and-global-contrast-normalization
  23. python?,H. (2018)。如何在 python 中实现全局对比度归一化?。数据科学堆栈交换。检索于 2018 年 10 月 23 日,来自https://data science . stack exchange . com/questions/15110/how-to-implementation-global-contrast-normalization-in-python

与 Apache Spark 的 MLlib & GraphX 的记录链接

原文:towardsdatascience.com/record-link…

一种可扩展的模糊数据匹配方法

挑战

最近,Datlinq 的一位同事让我帮她解决一个数据问题,这个问题看起来非常简单。 她从商会(Kamer van Koophandel: KvK)购买了一小组数据,其中包含大约 5 万家小公司(5-20 名全职员工),在网上很难找到。 她注意到许多公司共用同一个地址,这是有道理的,因为许多公司倾向于聚集在商业区。

然而,她还发现,同一地址上的许多公司实际上是一个公司,分多次注册。

这两家公司在技术上是不同的,但在这种特殊情况下,应被视为一家拥有联合劳动力的公司。

因此,我们只需将“属于”的公司组合在一起,并将在那里工作的 FTE 汇总为一个实体。

方法

当然,我一开始做了一些 if-then-else 编码,但是 a .那很快就变得一团糟,b .那有什么乐趣呢?

因此,我定义了实现动态、可扩展和可配置解决方案的步骤:

  • 加载/清除数据
  • 创建距离度量
  • 组合所有数据
  • 手动训练匹配组合
  • 训练模型
  • 在模型中运行所有组合
  • 制作所有链接记录的图表
  • 提取所有连接的顶点,并将它们保存为唯一的组
  • 将这些组合并到一个数据源中,该数据源包含 1 个 id、汇总的 FTE 和分组 id 列表

这些步骤中的任何一个都可以通过大量的工具、库和语言来完成,但是有一个似乎(几乎)适合它们中的每一个: Apache Spark

好吧,我可能有点偏见,我认为 Python 和 SciKit learn 也足够了,除了 Spark 似乎有点过头,但我喜欢 Scala 和 Spark…

所以我点燃了美好的火花

1.加载/清除数据

数据以 Excel 格式提供,所以简单的“另存为”csv 开始了这一步

此外,这一步相当直接;数据非常干净。我只是选择了模型可能感兴趣的字段/特征。 稍后我将介绍 KvKRecord 案例类

2.创建距离度量

要查看来自同一数据源的两个记录是否“匹配”,理论上你必须比较每一个组合。

这种比较本身就是这种努力的全部挑战,这就是机器学习介入的地方。

但是 ML 模型仍然需要一个“特征向量”来训练、测试和预测,所以我需要创建一个方法来比较两个 KvKRecords 并返回这个“特征向量”

我选择创建一个向量,其中每个元素对应于 case 类的每个属性之间的距离度量。

我使用 Math.log10 和一个除法来归一化整数之间的距离,以获得 0 和 1 之间的所有数字(我事先知道范围)。这不是最好的方法,但我强烈怀疑这些都无关紧要,所以我几乎没有花时间调优非字符串特性。

对于字符串比较,有很多很多选项 ,但我最终使用了 Jaro-Winkler 。我想试试其他的,但这似乎工作得很好,并返回一个介于 0 和 1 之间的正常值。

3.组合所有数据

我在这里有一些优势,因为地址数据非常干净,所以我不必创建笛卡尔乘积,而只需在地址上连接。使用 joinWith 方法, KvKRecords 保持完整,特征向量被附加上。

现在,我们有了一个经过筛选的数据集,包含了同一地址的所有公司组合。

我不得不指出,这多少是一种欺骗。有些公司地址相同,但地址符号不同,尽管提供的数据非常清晰。

例如," Keizersgracht 62" 和"*Keizersgracht 62–64 "*和" Keizersgracht 62-A" 可能被认为是相同的地址,但排除了这种方式。然而,我们也可以假设,谁注册这些公司,使用相同的地址,如果它是同一家公司?我不知道,但在快速手动检查后,这种影响似乎可以忽略不计/不存在。

一种替代方法是做一个完整的笛卡尔乘积(将每个公司与其他公司匹配),并在不同街道/城市的公司上训练该工具。我担心,不匹配的公司之间的纯粹差异会使模型远离实际上位于同一地址的公司之间的细微差别。

4.手动训练匹配组合

如果你没有东西来训练你的 ML,所有这些组合和向量不会让你去任何地方。我需要一个带标签的数据集用于监督学习。

用 Excel 基于原始 csv 手动创建这些,看起来容易出错而且很麻烦,所以作为一个优秀的(懒惰的)开发人员,我编写了一个程序来帮助我创建一个带标签的训练集。

console output

我必须承认,我只为这个项目创建了大约 100 个带标签的样本(使用< 0.2%), but the features are pretty simple and the work was extremely boring, so I was ok with it.

The LabeledVector case 类,因为它具有名为“ features ”和“ label ”的属性,这是 Spark MLlib 模型的默认属性。

5.训练模型

现在我们有了这个漂亮的标记数据集,我们准备好展示我们的机器学习肌肉了。

不过,我不得不让你有点失望,因为我刚刚使用 Spark 的 MLLib 创建了一个直接的逻辑回归模型,但结果才是最重要的。

其他项目使用随机森林、感知器甚至朴素贝叶斯可能会更好,但我基于这种方法得到了一些好结果:

  • 精确度:0.96
  • ROC 曲线下面积:0.92

(如果有人知道如何从 Spark 轻松地创建 ROC/AUC-plot,请告诉我)

更多的时间可以花在超参数调整上,但是拥有超过 80 个训练样本也会有所帮助;-)

6.在模型中运行所有组合

下一步是将这个模型应用到我们的组合数据集

因为我们的 comparableDataset 已经有了所需的' features : Vector ',我可以应用这个模型,它添加了标签概率列。

对于链接,我们现在只对实际的匹配感兴趣,所以得到的数据集只有被认为相同的公司的组合。

PredictedVector 又是一个简单明了的 case 类。

7.制作所有链接记录的图表

拥有一个包含所有关联公司的数据集固然很好,但要真正理解这些数据,我必须按“独立组”进行分组。含义:

(A,B) (A,C) (B,C) (D,E) (F,G) (G,H)

需要产生以下结果:

(A,B,C) (D,E) (F,G,H)

任何熟悉离散数学/图论的人都会注意到这些可以用图形来表示。

有很多很酷的工具可以用来处理图形,我最喜欢的一个是 Neo4j ,但是 Spark 还是提供了开箱即用的 GraphX ,所以我们现在还是坚持使用它。

我只需要把我们的数据集变成两个 RDD,即:边和顶点。

8.提取所有连接的顶点,并将它们保存为唯一的组

现在有了一个图( linkedGraph ),我想找到所有不相连的子图并把它们组合在一起。

这里神奇的关键词是:连通分量

GraphX 方法 connectedComponents 创建了每个 VertexId 和其子图中最小的 VertexId 的元组列表。

反转这些元组并在最小的 VertexId 上分组,创建了一个连接的 VertexId 的 c.q .子图列表

使用广播查找,我将这些 VertexId 重新映射到相应的 KvKRecord

最后,我得到了一个包含所有唯一组的数据集,以及该组中所有 KvKRecords 的列表。

9.在单个数据源中组合组

最后一步是为我的同事创建一个 csv 文件,用于她的特定用例。

整个代码虽然对这个过程不感兴趣,但它的目的是将这些结果整理成一个 csv 文件,该文件可以由 MS Excel 以

【关联公司数量】【总 FTE】【公司 id /栏目】

结论

最终,这个看似简单的数据问题有了一个相当复杂的解决方案,并且花费了我比预期更多的时间。

但是我并没有对结果失望,也没有选择在 Scala & Spark 中构建它。我学到了很多东西,并相信随着时间的推移,它将改进这个项目和其他项目。事实上,我可以在我的机器上运行这段代码,也可以在云中运行 100 个节点的集群,而不需要修改任何代码,这仍然让我感到惊讶。

我分享这个的原因是:

  • 通知你星火的威力:不吓人,好玩!
  • 去炒作机器学习:很酷,但还是软件开发带试用&错误。
  • 淡化大数据:重要的不总是规模。数据还是数据。
  • 帮助/激励有类似问题的人:分享是关爱;-)
  • 请对本文、我的方法、代码或任何东西进行反馈:!**

从《知识是美丽的》一书中再现数据可视化

原文:towardsdatascience.com/recreating-…

在这个系列中,我将着手重现大卫·麦克肯多斯在 r。

当你看完第一部分后,在这里看看这个系列的其他帖子:第二部分第三部分第四部分

David McCandless 是两本最畅销的信息图表书籍的作者,他在 TED 上做了一个关于数据可视化的演讲。我在 2015 年买了他的第二本书《知识是美丽的》,里面有 196 张漂亮的信息图。

那时,我真的迷上了行尸走肉,他的书启发我制作了自己的信息图:

最近,我试图想到一些有趣的数据可视化项目,并决定从书中选择几个可以在R中尽可能接近地重新创建的项目。

对于那些喜欢这类练习的人来说,这本书是一个极好的资源,因为书中的每一个可视化都与一个在线数据集配对,以便 根据你的兴趣进行探索!!!。我从来不知道数据集有多丰富,直到我尝试重新创建我的第一个可视化,“最佳展示”。中的数据集为最好单独显示中的,是一个有八张工作表的 excel 文件!

McCandles 说整本书花了他两年 15832 个小时,我对此毫不怀疑。如果你只关心可操作的结果,快速绘制一个 EDA 绘图对于一个会议来说是快速而简单的。然而,如果你出版的东西需要一些时间来创造一个惊人的视觉效果。

最佳表演

最佳展示是狗狗轮廓的散点图,根据狗狗的类别进行颜色编码,相应地调整大小,并根据它们的智力指向左边或右边。

让我们加载环境;我喜欢needs()包,它使得将&加载包安装到R变得简单。

knitr::opts_chunk$set(echo = TRUE)
if (!require("needs")) {
  install.packages("needs", dependencies = TRUE)
  library(needs)
}

如上所述,数据是一个包含八张工作表的 excel 文件。我们可以用read_excel()读入 Excel 文件,并用sheet =参数指定我们想要访问的工作表。

# install&Load readxl package
needs(here,
      readxl,
      stringr,
      dplyr,    # to use select()
      magrittr) # to use %<>% operators

path = here()

# Specify the worksheet by name
dog_excel <- read_excel("~/bestinshow.xlsx", sheet = "Best in show full sheet", range = cell_rows(3:91))

# Pick the desired variables
dog_excel %<>%
  select(1,3,5:6, "intelligence category", "size category")

# Rename the columns
colnames(dog_excel) <- c("breed", "category", "score", "popularity", "intelligence", "size")

# Remove first row (non-descript column names)
dog_excel <- dog_excel[-1,]

创建一个 HTML 表格(支持过滤、分页和排序。这只适用于 RMarkdown,不适用于媒体,所以你需要检查一下github . iomoldach.github.io/dataRbeauti…

needs(DT)
datatable(dog_excel, options = list(pageLength = 5))

看看数据集中的intelligence等级。

# What are the intelligence levels?
unique(dog_excel$intelligence)
## [1] "Brightest"     "Above average" "Excellent"     "Average"      
## [5] "Fair"          "Lowest"

McCandless 将狗分为“笨”和“聪明”两类。但是这里我们看到在这个干净的数据集中有类别。因此,让我们将前三个因素指定为clever,将其他三个因素指定为dumb

dog_excel$intelligence %<>% 
  str_replace(pattern = "Brightest", replace = "clever", .) %>%   
  str_replace(pattern = "Above average", replace = "clever", .) %>% 
  str_replace(pattern = "Excellent", replace = "clever", .) %>% 
  str_replace(pattern = "Average", replace = "dumb", .) %>% 
  str_replace(pattern = "Fair", replace = "dumb", .) %>% 
  str_replace(pattern = "Lowest", replace = "dumb", .)

原始可视化中有 87 只狗。我在 SuperColoring.com 的上找到了 知识共享 4.0 许可 下的 24 个剪影。这意味着我可以以任何媒体或格式自由复制和再分发,只要我给出网页的链接,并注明作者的姓名和许可证。我已经将该信息包含在一个.csv.中,我们将使用它来对 McCandless 的数据进行子集化(两个数据集都可以在 my Github repo 中找到)。

needs(readr)
dog_silhouettes <- read_csv("~/dog_silhouettes.csv")

dog_df <- dog_excel %>% 
  inner_join(dog_silhouettes, by = "breed")

# change popularity from string into numeric values
dog_df$popularity <- as.numeric(dog_df$popularity)

我们需要使用magick包来使.svg轮廓周围的白色背景透明,将图像缩放到普通大小(按宽度),并保存为.png的。

needs(magick)

file.names <- dir(path, pattern = ".svg")

for (file in file.names){
  # read in the file
  img <- image_read_svg(file)
  # scale all images to a common scale
  img_scaled <- image_scale(img, 700)
  # make the background transparent
  img_trans <- image_transparent(img_scaled, 'white')
  # get rid of .svg ending
  file_name <- str_replace(file, pattern = ".svg", replace = "")
  # write out the file as a .png
  image_write(img_trans, paste0(file_name, ".png"))
}

一些狗的侧影指向相反的方向。我们需要使用magick包装中的image_flop(),这样所有包装都面向同一个方向。稍后,我们可以通过智能对数据帧进行子集划分,这样cleverdumb狗就面向相反的方向。

path = here()

flop.images <- c("~/labrador-retriever-black-silhouette.png", "~/border-terrier-black-silhouette.png", "~/boxer-black-silhouette.png", "~/french-bulldog-black-silhouette.png", "~/german-shepherd-black-silhouette.png", "~/golden-retriever-black-silhouette.png", "~/greyhound-black-silhouette.png", "~/rottweiler-black-silhouette.png")

for(i in flop.images){
  i <- str_replace(i, pattern = "~/", replace = "")
  img <- image_read(i)
  img_flop <- image_flop(img)
  image_write(img_flop, i)
}

下一步是根据类别给狗上色。

让我们写一个函数,可以给它三个参数:1) df一个数据帧,2) category它是狗的类型,3) color每个category的特定颜色。

第一步是只选择那些属于感兴趣的category的狗。这可以通过dplyr封装中的filter()来完成。但是,如果你想使用dplyr功能,比如filter(),你需要按照这个网站上的说明;用enquo设定。

img_color <- function(df, category, color){
  # Make filter a quosure
  category = enquo(category)
  # subset df on category
  new_df <- df  %>% 
    filter(category == !!category)
  # get directory paths of images for the for loop 
  category_names <- new_df$breed  %>%
    tolower() %>% 
    str_replace_all(" ", "-") %>% 
    paste0("-black-silhouette.png")
  for(name in category_names){
    img <- image_read(name)
    img_color <- image_fill(img, color, "+250+250")
    image_write(img_color, name)
  }
}

我想在 R 内尽可能接近地复制图形,所以为了复制可视化的颜色,我扫描了这本书,保存了图像,然后使用这个工具来获取 html 颜色代码

现在让我们使用上面创建的函数根据它们的category给狗的轮廓上色。

# Herding color "#D59E7B"
img_color(dog_df, "herding", "#D59E7B")

# Hound color "#5E4D6C"
img_color(dog_df, "hound", "#5E4D6C")

# Non-sporting color "#6FA86C"
img_color(dog_df, "non-sporting", "#6FA86C")

# Sporting color "#B04946"
img_color(dog_df, "sporting", "#B04946")

# Terrier color "#A98B2D"
img_color(dog_df, "terrier", "#A98B2D")

# Toy color "#330000"
img_color(dog_df, "toy", "#330000")

# Working color "#415C55"
img_color(dog_df, "working", "#415C55")

好了,终于到了可视化的时候了。

通常人们用ggplot2中的geom_point()绘制点,但在这种情况下,我想要每个品种的图像。我们可以使用ggimage包,稍微调整一下,我们就可以基于intelligence翻转图像。ggimage不像ggplot2那样支持颜色作为一种审美,这就是我之前手动分配颜色&尺寸的原因。

由于popularity分数范围从 1 到 140,1 是最受欢迎的,我们需要用scale_y_reverse()反转 y 轴。****

**needs(ggplot2,
      ggimage)
# add "~/" to get filenames of images for plotting
dog_df$name <- paste0("~/", dog_df$name)

# create a ggplot/ggimage object
p <- ggplot(subset(dog_df, intelligence == "clever"), aes(x = score, y = popularity, image = name), alpha = 0.5) + geom_image(image_fun = image_flop) + geom_image(data=subset(dog_df, intelligence == "dumb")) +
  labs(title = "Best in Show", subtitle = "The ultimate datadog", caption = "Source: bit.ly/KIB_BestDogs") +
  labs(x = NULL, y = NULL) +
  theme(panel.background = element_blank(),
        legend.position = "top", 
        legend.box = "horizontal",
        plot.title = element_text(size = 13,
                                  # I'm not sure what font he chose so I'll pick something I think looks similar
                                 family = "AvantGarde",
                                 face = "bold", 
                                              lineheight = 1.2),
        plot.subtitle = element_text(size = 10,
                                     family = "AvantGarde"), 
        plot.caption = element_text(size = 5,
                                    hjust = 0.99),  
        axis.text = element_blank(), 
        axis.ticks = element_blank()) +
  scale_y_reverse()**

最后一步是在狗的品种下面添加文本注释。因为我用intelligencedog_df进行了子集化,所以如果我试图用geom_text()进行注释,它只会注释部分数据。我们将需要函数 instea,因为 geome 不是从数据帧的变量映射而来,而是作为向量传入。

**# Add annotations
p + annotate("text", x=dog_df$score[1:24], y=((dog_df$popularity[1:24])+6), label = dog_df$breed[1:24], size = 2.0)**

可视化看起来与原始相似,并突出了所包含的大部分美学。人们总是可以在 Adobe Illustrator 或 Inkscape 中添加额外的细节,使其看起来更像最终的可视化效果

敬请关注第二部分!

从《知识是美丽的》一书中再现(更多)数据可视化:第二部分

原文:towardsdatascience.com/recreating-…

在本系列的第二部分中,我将继续重现 David McCandless 在 r。

David McCandless 是两本最畅销的信息图书籍的作者,他在 TED 上做了一个关于数据可视化的精彩演讲。他的第二本书 知识是美丽的 ,于 2015 年出版,包含 196 幅美丽的信息图,耗时 15832 小时完成。

当你完成了这一部分,看看这个系列的其他帖子:第一部分第三部分第四部分

密码

这个可视化图形是一个散点图,根据密码中的第一个字符[A 到 Z],然后是[0 到 9],沿 x 轴从左到右排列常用密码。密码根据类别进行颜色编码,根据密码的强度和 y 轴上的使用频率进行大小调整。

在上一篇文章中,我从 Google Docs 下载了 excel 格式的数据,并用readxl包中的read_excel()函数加载了相应的工作表。

令人沮丧的是,数据有时可以在 pdf 中分发。例如,拉斐尔·伊里扎里(Rafael Irizarry)使用波多黎各政府新发布的数据,对 2017 年毁灭性飓风玛丽亚(Maria)后波多黎各的超额死亡率进行了新的计算。 Irizarry 的帖子附有数据,但遗憾的是它是 PDF 格式的

tabulizer 库提供了到 Tabula java 库的 R 绑定,可以用来从 PDF 文档中提取表格。

数据集位于这里:bit.ly/KIB_Passwor…。我们来导入一下:

# Download tabular data from a pdf spanning multiple pages
library(tabulizer)passwords <- "~/passwords.pdf"# The table spreads across five pages
pages <- c(1:5)df_total <- data.frame()for (i in pages) {
    out <- extract_tables(passwords, page = i)
    out <- as.data.frame(out)
    colnames(out) <- c("rank","password","category", "online_crack", "offline_crack", "rank_alt", "strength","font_size")
    out <- out[-1,1:8]
    df_total <- rbind(df_total, out)
}

在继续之前,需要对数据进行一些清理。

df_total <- na.omit(df_total)
df_total$rank <- as.numeric(df_total$rank)

沿着 x 轴,根据密码的第一个字符对密码进行分类。我们可以使用dplyrmutate()函数内的grepl来创建新的宁滨列的每个密码。

# make a group for passwords beginning in A-Z and through 0-9
df_total <- df_total %>% 
  mutate(group = case_when(grepl("^A", password, ignore.case = TRUE) ~ "A",
  grepl("^B", password, ignore.case = TRUE) ~ "B",
  grepl("^C", password, ignore.case = TRUE) ~ "C",
  grepl("^D", password, ignore.case = TRUE) ~ "D",
  grepl("^E", password, ignore.case = TRUE) ~ "E",
  grepl("^F", password, ignore.case = TRUE) ~ "F",
  grepl("^G", password, ignore.case = TRUE) ~ "G",
  grepl("^H", password, ignore.case = TRUE) ~ "H",
  grepl("^I", password, ignore.case = TRUE) ~ "I",
  grepl("^J", password, ignore.case = TRUE) ~ "J",
  grepl("^K", password, ignore.case = TRUE) ~ "K",
  grepl("^L", password, ignore.case = TRUE) ~ "L",
  grepl("^M", password, ignore.case = TRUE) ~ "M",
  grepl("^N", password, ignore.case = TRUE) ~ "N",
  grepl("^O", password, ignore.case = TRUE) ~ "O",
  grepl("^P", password, ignore.case = TRUE) ~ "P",
  grepl("^Q", password, ignore.case = TRUE) ~ "Q",
  grepl("^R", password, ignore.case = TRUE) ~ "R",
  grepl("^S", password, ignore.case = TRUE) ~ "S",
  grepl("^T", password, ignore.case = TRUE) ~ "T",
  grepl("^U", password, ignore.case = TRUE) ~ "U",
  grepl("^V", password, ignore.case = TRUE) ~ "V",
  grepl("^W", password, ignore.case = TRUE) ~ "W",
  grepl("^X", password, ignore.case = TRUE) ~ "X",
  grepl("^Y", password, ignore.case = TRUE) ~ "Y",
  grepl("^Z", password, ignore.case = TRUE) ~ "Z",
  grepl("^0", password, ignore.case = TRUE) ~ "0",
  grepl("^1", password, ignore.case = TRUE) ~ "1",
  grepl("^2", password, ignore.case = TRUE) ~ "2",
  grepl("^3", password, ignore.case = TRUE) ~ "3",
  grepl("^4", password, ignore.case = TRUE) ~ "4",
  grepl("^5", password, ignore.case = TRUE) ~ "5",
  grepl("^6", password, ignore.case = TRUE) ~ "6",
  grepl("^7", password, ignore.case = TRUE) ~ "7",
  grepl("^8", password, ignore.case = TRUE) ~ "8",
  grepl("^9", password, ignore.case = TRUE) ~ "9"))# get rid of NA's
df_total <- na.omit(df_total)

默认情况下,0–9 在 A-Z 之前,但是 McCandless 可视化将 A-Z 放在 0–9 之前,所以让我们重新安排一下。

df_total$group <- factor(df_total$group, levels = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U" , "V", "W", "X", "Y", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9"))

是时候重新创建数据可视化了。我们使用geom_text()来显示密码,根据密码强度调整大小,颜色与 McCandless 使用的主题和颜色相协调

library(ggplot2)
library(extrafont) # For the Georgia fontggplot(df_total, aes(x = group, y = rank)) +
geom_text(aes(label = password, color=category, size = font_size, alpha = 0.95)) + 
  # add the custom colors
  scale_color_manual(values=c("#477080", "#A3968A", "#C08B99", "#777C77", "#C8AB6D", "#819DAB", "#C18A6F", "#443F36", "#6A9577", "#BF655A")) +
  scale_y_continuous(position = "right", breaks = c(1,10,50,100,250,500)) + 
  scale_x_discrete(breaks = c("A","Z","1","9")) +
  scale_y_reverse() +
  labs(title = "Top 500 Passwords", subtitle = "Is yours here?", caption = "Source: bit.ly/KIB_Passwords") +
  labs(x = NULL, position = "top") +
theme(legend.position = "none",
      panel.background = element_blank(),
        plot.title = element_text(size = 13, 
                                  family = "Georgia", 
                                  face = "bold", lineheight = 1.2), plot.subtitle = element_text(size = 10,
                                     family = "Georgia"), 
        plot.caption = element_text(size = 5,
                                    hjust = 0.99, family = "Georgia"),  
        axis.text = element_text(family = "Georgia"))

McCandless 制作的数据集包含了很多关于密码如何被破解的信息。它也有一些选择密码的提示。不过,这个 xkcd 漫画可以很好地解释 TLDR:

在顶部框架中,密码破解软件更容易猜出 Tr0ub4dor & 3 密码,因为它比correct horse battery staple的熵更小,而且人类也更难记住,导致不安全的做法,如将密码写在显示器上的便利贴上。所以你应该总是把一个容易记住的句子转换成一个容易记住的密码,而不是一个随机的字母数字。

一茶匙糖

糖数据集可视化是一个圆形条形图,显示了常见饮料中的糖的茶匙数。该图使用了ggplot2 coord_polar选项(为了简化这篇文章,我排除了数据管理代码,而是提供了一个 *.csv* 文件以备绘图)。

sugar <- read.csv("sugar.csv")# Re-order the factors the way they appear in the data frame
names <- sugar$drinks
names
sugar$drinks <- factor(sugar$drinks, levels = rev(sugar$drinks), ordered = TRUE)# Create a custom color palette
custompalette <- c("#C87295", "#CE7E9C", "#CE7E9C", "#C3C969", "#B77E94", "#693945", "#63645D", "#F9D9E0", "#B96E8E", "#18090E", "#E1E87E", "#B47E8F", "#B26F8B", "#B47E8F", "#B26F8B", "#B47E8F", "#B26F8B", "#9397A0", "#97B7C4", "#9AA24F", "#6B4A4F", "#97A053", "#B7BB6B", "#97A053", "#B7BB6B", "#97A053", "#B7BB6B", "#97A053", "#B7BB6B",
"#CED97B", "#E4E89C", "#C87295", "#CE7E9C")ggplot(sugar, aes(x = drinks, y = teaspoons, fill = drinks)) +
  geom_bar(width = 0.75, stat = "identity") +
  coord_polar(theta = "y") +
  xlab("") + ylab("") +
  labs(title = "Teaspoons", caption = "Source: bit.ly/KIB_Sugar") +
  # Increase ylim to avoid having a complete circle and set custom breaks to range of teaspoons
  scale_y_continuous(limits = c(0,65), breaks=seq(0,26,1)) +
  scale_fill_manual(values = custompalette) +
  theme(legend.position = "none", 
        axis.text.y = element_blank(),
        axis.text.x = element_text(color = "white", family = "Georgia"),
        axis.ticks = element_blank(), 
        panel.background = element_rect(fill = "black", color = NA),
        plot.title = element_text(color = "white", 
                                  size = 13, 
                                  family = "Georgia", 
                                  face = "bold", lineheight = 1.2), 
        plot.caption = element_text(size = 5,
                                    hjust = 0.99, 
                                    color = "white", 
                                    family = "Georgia"),
        panel.grid.major.y = element_line(color = "grey48", size = 0.05, linetype = "dotted"),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.x = element_blank())

最好是在条形旁边手动添加标签,而不是在axis.text.y =中调整hjust

尽管我认为这种视觉效果在美学上是令人愉悦的,但如果不提这些类型的图形最终应该被避免,那将是我的失职,因为很难/误导性地辨别群体之间的差异(这里有一个很好的链接,深入解释了 wh y)。

说到良好的数据可视化实践,大多数人会告诉你避免饼图、炸药图等。然而,我每天都能在学术出版物、政府报告等中看到它们。

谁知道呢,你的雇主可能会要求你制作一份背景带有公司标志的定制信息图。嗯,你很幸运!

大卫·麦克肯多斯在书中加入了一个 pieplot,我认为复制这个 pie plot 会很有用;仅仅是为了展示如何在绘图中包含背景图像。

谁拥有北极?

根据国际法,包括北极及其周围北冰洋区域在内的公海不属于任何国家。然而,延伸至北极大陆架的领土主张属于加拿大、俄罗斯、丹麦、挪威、美国和冰岛。

虽然在数据集中有很多信息,但我找不到他用于可视化的原始数据。因此,在这个例子中,我只给出一个大概的估计。

library(magick)# use image under Creative Commons Attribution-Share Alike 3.0 Unported license.
img <- image_read("[https://upload.wikimedia.org/wikipedia/commons/9/91/Arctic_Ocean_location_map.svg](https://upload.wikimedia.org/wikipedia/commons/9/91/Arctic_Ocean_location_map.svg)")bitmap <- img[[1]]
bitmap[4,,] <- as.raw(as.integer(bitmap[4,,]) * 0.4)
taster <- image_read(bitmap)# custom pallete
my_palette <- c("#ADDFEA","#E3E9A3", "#FFD283", "#CAC3CF", "#62465F", "#B8E29B")# Make data frame
df <- data.frame(
  country = c("USA", "Russia", "Norway", "Iceland", "Denmark", "Canada"), 
  percentage = c(10,46,13,5,18,18))# Re-order the factors the way they appear in the data frame
df$country <- factor(df$country, levels = c("USA", "Canada", "Denmark", "Iceland", "Norway", "Russia"), ordered = TRUE)g <- ggplot(df, aes(x = "", y=percentage, fill = country)) +
  geom_bar(width = 1, stat = "identity") +
  coord_polar("y", start=0) +
  scale_y_continuous(breaks = c(105,25,53,62,75,90),labels = c("USA", "Russia", "Norway", "Iceland", "Denmark", "Canada")) +
    xlab("") + ylab("") +
  labs(title = "Who owns the Arctic?", caption = "Source: bit.ly/KIB_PolePosition") +
  scale_fill_manual(values = my_palette) +
  theme(legend.position = "none", 
        axis.text.y = element_blank(),
        axis.text.x = element_text(color = c("#ADDFEA","#B8E29B", "#62465F", "#CAC3CF", "#FFD283", "#E3E9A3"), family = "Georgia", size = 7.6),
        axis.ticks = element_blank(), 
        panel.background = element_blank(),
        axis.line = element_blank(),
        plot.title = element_text(size = 13, 
                                  family = "Georgia", 
                                  face = "bold", lineheight = 1.2), 
        plot.caption = element_text(size = 5,
                                    hjust = 0.99, 
                                    vjust = 15,
                                    family = "Georgia"),
        panel.grid.minor = element_blank(),
        panel.grid.major = element_blank())# You need to fiddle with the settings in RStudio and then Export to PDF, JPG, TIFF, etc.
library(grid)
grid.newpage()
g
grid.draw(rasterGrob(width = 0.34, height = 0.666, image=taster, just = "centre", hjust = 0.46, vjust = 0.47))

更多怪异但(有时)有用的情节见异种图

本系列可复制的代码和内容可以在 Github 上找到

希望你喜欢这篇文章,并继续关注第三部分!