2018年7月1日星期日

看球,我选上帝视角

安妮 发自 凹非寺量子位 出品 | 公众号 QbitAI

4:3,梅西回家了;2:1,C罗也回家了,这届世界杯真是激烈又精彩。

但对于无法前往现场的(伪)球迷们,只能屈身于手机、电视的平面看直播了吧?

在新技术应用一个接一个的今天,到底有没有更新、更技术的方式,重新定义一下看个球?

试想一下:如果可以通过AR设备,将赛况实时投到桌面上,并且随你移动,还能尽览不同视角不同距离的全场景球况,该是多么过瘾的一种选择。

就像这样——

再如这样——

噫……这是把世界杯搬到了任意桌面了么?而且还能各个角度绕着球场看比赛——就算身在现场也做不到啊!

恭喜你,这种"上帝视角",可能不久就能实现了。

就在最近,华盛顿大学、Google和Facebook的研究人员开发了一套桌面足球系统,能将世界杯视频重建,转换成动态3D视频。通过AR设备,可以看到渲染后的球员动作和场地效果。

拥有了这个系统,感觉宛如拥有了整个球场,这到底是怎么做到的?

3D转换的秘笈

桌面看球的大思路其实很简单。

只要充分利用球场部署的多角度相机,之后通过多视角几何技术,对场地和玩家进行三维重建,2D世界杯就升级为3D场景了。

因此,研究人员提出了这样的思路——

先收集Youtube上世界杯球赛视频,根据场地线来推断相机的位置等参数。

随后,提取视频中的边界线、球员姿势和运动轨迹,将球员和场地分割,方便下一步加工。

"质壁分离"后,对球员进行单独的渲染处理来实现立体效果。研究人员用视频数据训练深度神经网络模型,在球场上重建每个球员的景深图,为实现360度无死角看球打基础。

最后,如果你有AR设备……用AR设备或者3D查看器渲染一下,3D看球美美哒。

思路没错,但紧接着问题就来了:

想训练神经网络模型根据球员图像来估计他的景深图,最理想的数据集里,应该包含着一对对的球员照片vs.景深图。

这样的数据集上哪找?

研究人员想到了EA出品的FIFA系列游戏。截取FIFA游戏引擎与GPU之间的调用信息,不就能从视频游戏的框架中提取景深图了嘛。

从FIFA系列游戏中提取图像及深度

机智。

现在数据完备,就差个能够预测景深的神经网络了。

研究人员选取了"沙漏网络模型",用一连串残差模块,像一个个沙漏一样来处理输入,经过8个"沙漏模块"实施降低输入的分辨率、放大等步骤,输出的,就是我们所需要的景深图。

重建3D球赛,Go!

准备阶段完成后,研究人员开始重建3D场景。整个流程一气呵成,犹如进入了快餐店后厨——

大概分为以下几步:

  • 1.相机位置估计:用一个合成的平面场模板预估真实场景中每帧的参数,进一步找到相机在每个连续帧中的姿态。
  • 2.球员检测和追踪:提取球员的边界box检测到的关键点/骨架,基于姿态信息对初始的边界框进行细化,再根据提取的边框序列预计球员的运动轨迹。
  • 3.实时实例分割:预估每个球员用于深度预估网络的分割掩码,借助语义分割和前一步的姿态估计。
  • 4.网格生成:将上一步的前景掩码与原始裁剪图像混合,放入一开始预先训练好的神经网络中进行训练。
  • 5.形成3D轨迹:减少相机位置和边界框定位时不精确带来的球员抖动,对3D球员轨迹进行平滑处理。

一连串操作至此,3D合成大功告成。

研究人员将此模型的3D生成结果与原视频和不同方法合成的结果进行了对比。

Youtube视频上真实处理结果

渲染后得到最后的效果图:

Youtube视频帧(最上一行)及渲染结果(第2~4行)

毫无疑问,这种方法可以得到更精确和完整的深度估计,重建的效果也最好。

美中不足

虽然场景不错,但这项技术目前并不完善,还不能让你随心畅看。

Bug 其实很明显,刚在观看效果的时候,各位有没有看见足球?

——冇。

没错,目前这套系统最大的问题就是还没有对足球进行处理,球的轨迹无法渲染出来,场上一票球员如同在踢"空气球"。

此外,系统对球员及守门员的(位置)把控不够准确,被遮挡的球员显示不完整。这就导致球员有时球员会突然消失,然后突然出现,犹如习得传送大法。

迷离消失的守门员

在论文中,研究人员表示,目前这项技术还在不断改进和升级中,下一部的计划是:

  • 改善画面重建的品质
  • 实现实时3D重建
  • 预估足球的位置
  • 让这项技术也能用于其他运动,让篮球、橄榄球、排球等等运动的球迷也能同此待遇。

华盛顿大学出品

这项研究的论文Soccer on Your Tabletop发表在CVPR 2018上,作者有四,均属于华盛顿大学(张亚勤母校)的GRAIL lab实验室,分别为Konstantinos Rematas、Ira Kemelmacher-Shlizerman、Brian Curless和Steve Seitz.

一作Konstantinos Rematas

如果觉得这项研究和你胃口,可移步传送门深入挖掘。

论文地址:

https://ift.tt/2KCfqK4

代码地址:

https://ift.tt/2lrG7D7

最后,效果视频长这样:

—欢迎大家关注我们的专栏:量子位 - 知乎专栏诚挚招聘量子位正在招募编辑/记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复"招聘"两个字。量子位 QbitAI · 头条号签约作者վ'ᴗ' ի 追踪AI技术和产品新动态



via 量子位 - 知乎专栏 https://ift.tt/2Kn3kp7
RSS Feed

RSS5

IFTTT

看球,我选上帝视角

安妮 发自 凹非寺量子位 出品 | 公众号 QbitAI

4:3,梅西回家了;2:1,C罗也回家了,这届世界杯真是激烈又精彩。

但对于无法前往现场的(伪)球迷们,只能屈身于手机、电视的平面看直播了吧?

在新技术应用一个接一个的今天,到底有没有更新、更技术的方式,重新定义一下看个球?

试想一下:如果可以通过AR设备,将赛况实时投到桌面上,并且随你移动,还能尽览不同视角不同距离的全场景球况,该是多么过瘾的一种选择。

就像这样——

再如这样——

噫……这是把世界杯搬到了任意桌面了么?而且还能各个角度绕着球场看比赛——就算身在现场也做不到啊!

恭喜你,这种"上帝视角",可能不久就能实现了。

就在最近,华盛顿大学、Google和Facebook的研究人员开发了一套桌面足球系统,能将世界杯视频重建,转换成动态3D视频。通过AR设备,可以看到渲染后的球员动作和场地效果。

拥有了这个系统,感觉宛如拥有了整个球场,这到底是怎么做到的?

3D转换的秘笈

桌面看球的大思路其实很简单。

只要充分利用球场部署的多角度相机,之后通过多视角几何技术,对场地和玩家进行三维重建,2D世界杯就升级为3D场景了。

因此,研究人员提出了这样的思路——

先收集Youtube上世界杯球赛视频,根据场地线来推断相机的位置等参数。

随后,提取视频中的边界线、球员姿势和运动轨迹,将球员和场地分割,方便下一步加工。

"质壁分离"后,对球员进行单独的渲染处理来实现立体效果。研究人员用视频数据训练深度神经网络模型,在球场上重建每个球员的景深图,为实现360度无死角看球打基础。

最后,如果你有AR设备……用AR设备或者3D查看器渲染一下,3D看球美美哒。

思路没错,但紧接着问题就来了:

想训练神经网络模型根据球员图像来估计他的景深图,最理想的数据集里,应该包含着一对对的球员照片vs.景深图。

这样的数据集上哪找?

研究人员想到了EA出品的FIFA系列游戏。截取FIFA游戏引擎与GPU之间的调用信息,不就能从视频游戏的框架中提取景深图了嘛。

从FIFA系列游戏中提取图像及深度

机智。

现在数据完备,就差个能够预测景深的神经网络了。

研究人员选取了"沙漏网络模型",用一连串残差模块,像一个个沙漏一样来处理输入,经过8个"沙漏模块"实施降低输入的分辨率、放大等步骤,输出的,就是我们所需要的景深图。

重建3D球赛,Go!

准备阶段完成后,研究人员开始重建3D场景。整个流程一气呵成,犹如进入了快餐店后厨——

大概分为以下几步:

  • 1.相机位置估计:用一个合成的平面场模板预估真实场景中每帧的参数,进一步找到相机在每个连续帧中的姿态。
  • 2.球员检测和追踪:提取球员的边界box检测到的关键点/骨架,基于姿态信息对初始的边界框进行细化,再根据提取的边框序列预计球员的运动轨迹。
  • 3.实时实例分割:预估每个球员用于深度预估网络的分割掩码,借助语义分割和前一步的姿态估计。
  • 4.网格生成:将上一步的前景掩码与原始裁剪图像混合,放入一开始预先训练好的神经网络中进行训练。
  • 5.形成3D轨迹:减少相机位置和边界框定位时不精确带来的球员抖动,对3D球员轨迹进行平滑处理。

一连串操作至此,3D合成大功告成。

研究人员将此模型的3D生成结果与原视频和不同方法合成的结果进行了对比。

Youtube视频上真实处理结果

渲染后得到最后的效果图:

Youtube视频帧(最上一行)及渲染结果(第2~4行)

毫无疑问,这种方法可以得到更精确和完整的深度估计,重建的效果也最好。

美中不足

虽然场景不错,但这项技术目前并不完善,还不能让你随心畅看。

Bug 其实很明显,刚在观看效果的时候,各位有没有看见足球?

——冇。

没错,目前这套系统最大的问题就是还没有对足球进行处理,球的轨迹无法渲染出来,场上一票球员如同在踢"空气球"。

此外,系统对球员及守门员的(位置)把控不够准确,被遮挡的球员显示不完整。这就导致球员有时球员会突然消失,然后突然出现,犹如习得传送大法。

迷离消失的守门员

在论文中,研究人员表示,目前这项技术还在不断改进和升级中,下一部的计划是:

  • 改善画面重建的品质
  • 实现实时3D重建
  • 预估足球的位置
  • 让这项技术也能用于其他运动,让篮球、橄榄球、排球等等运动的球迷也能同此待遇。

华盛顿大学出品

这项研究的论文Soccer on Your Tabletop发表在CVPR 2018上,作者有四,均属于华盛顿大学(张亚勤母校)的GRAIL lab实验室,分别为Konstantinos Rematas、Ira Kemelmacher-Shlizerman、Brian Curless和Steve Seitz.

一作Konstantinos Rematas

如果觉得这项研究和你胃口,可移步传送门深入挖掘。

论文地址:

https://ift.tt/2KCfqK4

代码地址:

https://ift.tt/2lrG7D7

最后,效果视频长这样:

—欢迎大家关注我们的专栏:量子位 - 知乎专栏诚挚招聘量子位正在招募编辑/记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复"招聘"两个字。量子位 QbitAI · 头条号签约作者վ'ᴗ' ի 追踪AI技术和产品新动态



via 量子位 - 知乎专栏 https://ift.tt/2Kn3kp7
RSS Feed

RSS5

IFTTT

DeepMind提出关系RNN:记忆模块RMC解决关系推理难题

论文:Relational recurrent neural networks


论文链接:https://ift.tt/2sKoMIJ

摘要:基于记忆的神经网络通过长期记忆信息来建模时序数据。但是,目前尚不清楚它们是否具备对记忆信息执行复杂关系推理的能力。在本论文中,我们首先确认了标准记忆架构在执行需要深入理解实体连接方式的任务(即涉及关系推理的任务)时可能会比较困难。然后我们利用新的记忆模块 Relational Memory Core(RMC)改进这些缺陷,RMC 使用 Multi-head 点积注意力令记忆相互影响。最后,我们在一系列任务上对 RMC 进行测试,这些任务可从跨序列信息的更强大关系推理中受益,测试结果表明在强化学习领域(如 Mini PacMan)、程序评估和语言建模上获得了很大进步,在 WikiText-103、Project Gutenberg 和 GigaWord 数据集上获得了当前最优的结果。

1 引言

人类使用复杂的记忆系统来获取和推理重要信息,而无需过问信息最初被感知的时间 [1, 2]。在神经网络研究中,建模序列数据的成功方法也使用记忆系统,如 LSTM [3] 和记忆增强神经网络 [4–7]。凭借增强记忆容量、随时间有界的计算开销和处理梯度消失的能力,这些网络学会关联不同时间的事件,从而精通于存储和检索信息。

这里我们提出:考虑记忆交互与信息存储和检索会有很大收获。尽管当前模型可以学会分割和关联分布式、向量化记忆,但它们并不擅长显性地完成这些过程。我们假设擅长这么做的模型可能会更好地理解记忆的关联,从而获得对时序数据进行关系推理的更强能力。我们首先通过一个强调序列信息的关系推理的演示任务展示了当前模型确实在这方面比较困难。而使用 Multi-head 点积注意力的新型 RMC 可使记忆交互,我们解决并分析了这个演示任务。之后我们应用 RMC 处理一系列任务(这些任务可能从更显著的记忆交互中受益),从而得到了潜在增长的记忆容量,可处理随时间的关系推理:在 Wikitext-103、Project Gutenberg、GigaWord 数据集上的部分可观测强化学习任务、程序评估和语言建模任务。

3 模型

我们的主导设计原则是提供架构主干网络,使模型可学习分割信息,并计算分割后信息之间的交互。为此我们结合了 LSTM 构造块、记忆增强神经网络和非局部网络(具体来说是 Transformer seq2seq 模型 [19])以实现主体网络。与记忆增强架构类似,我们考虑使用固定的记忆单元集合,但是我们利用注意力机制进行记忆单元之间的交互。如前所述,我们的方法与之前的研究不同,我们在单个时间步上对记忆应用注意力机制,而且不跨越从所有之前的观测中计算出的所有之前表征。

图 1:Relational Memory Core。(a)RMC 接受前一个记忆矩阵和输入向量,并作为输入,它们被传输至 MHDPA 模块(A)。(b)利用 Query 逐行共享的权重 W^q、Key 逐行共享的权重 W^k 和 Value 逐行共享的权重 W^v,计算每个记忆单元的线性投影。(c)将 Query、key 和 Value 编译成矩阵,计算 softmax(QK^T)V。该计算的输出是一个新的记忆,其中的信息根据记忆的注意力权重进行混合。MLP 被逐行应用于 MHDPA 模块的输出(a),得到的记忆矩阵是门控矩阵,作为核心输出或下一个记忆状态。

图 2:任务。我们在一系列监督和强化学习任务上对 RMC 进行测试。Nth Farthest 演示任务和语言建模任务值得注意。前者中解决方案需要显性的关系推理,因为该模型必须把向量之间的距离关系进行分类,而不是对向量本身进行分类。后者基于大量自然数据测试模型,使得我们可以进行与精心调整的模型之间的性能对比。

5 结果

图 3:模型分析。每行描述了特定序列在每个时间步上的注意力矩阵。下方的文本即该序列的特定任务,序列被编码,并作为模型输入。我们把任务中引用的向量标红:即如果模型选择离向量 7 第 2 远的向量,则标红的是向量 7 中被输入到模型的时间点。单个注意力矩阵展示了从一个特定记忆单元(y 轴)到另一个记忆单元(列)的注意力权重,或者输入(offset 列),数字表示记忆单元,「input」表示输入词嵌入

表 1:在程序评估和记忆任务上的每字符测试准确率

表 2:在 WikiText-103、Project Gutenberg 和 GigaWord v5 数据集上的验证困惑度和测试困惑度。

]]> 原文: https://ift.tt/2lNZdnc
RSS Feed

机器知心

IFTTT

算法太多挑花眼?此文教你如何选择正确的机器学习算法

机器学习既是一门科学,也是一种艺术。纵观各类机器学习算法,并没有一种普适的解决方案或方法。事实上,有几个因素会影响你对机器学习算法的选择。

有些问题是非常特别的,需要用一种特定的解决方法。例如,如果你对推荐系统有所了解,你会发现它是一类很常用的机器学习算法,用来解决一类非常特殊的问题。而其它的一些问题则非常开放,可能需要一种试错方法(例如:强化学习)。监督学习、分类、回归等问题都是非常开放的,可以被用于异常检测或建立更加广泛的预测模型。

此外,我们在选择机器学习算法时所做出的一些决定与算法的优化或技术层面关系并不大,而更多地与业务决策相关。下面,让我们一起来看看有哪些因素能帮你缩小机器学习算法的选择范围。

数据科学过程

在你开始研究不同的机器学习算法前,你需要对自己拥有的数据、面对的问题及相关约束有清晰的了解。

理解你的数据

当我们决定使用哪种算法时,我们所拥有的数据的类型和形态起着关键性的作用。有些算法可以利用较小的样本集合工作,而另一些算法则需要海量的样本。特定的算法对特定类型的数据起作用。例如,朴素贝叶斯算法对处理待分类的输入特别有效,但是对于缺失值则一点都不敏感。

因此,你需要做到:

了解你的数据

 1. 查看总结统计和数据可视化的结果

  • 百分比可以帮助你识别大多数数据的范围

  • 平均数和中位数可以描述集中趋势

  • 相关系数可以指出强的关联性

2. 数据可视化

  • 箱形图可以识别出异常值

  • 密度图和直方图可以显示出数据的散布情况

  • 散点图可以描述二元关系

数据清洗

 1. 处理缺失值。缺失的数据对于某些模型的影响比对其它模型更大。即使是对于那些被用于处理缺失数据的模型来说,它们也可能对缺失数据很敏感(某些变量的缺失数据可能导致预测性能变差)

 2. 选择处理异常值的方法

  • 异常值在多维数据中十分常见。

  • 有些模型对异常值的敏感性比其它模型要低。通常而言,树模型对于异常值的存在不太敏感。然而回归模型、或者任何试图使用方程的模型都会受到异常值的严重影响。

  • 异常值可能是糟糕的数据收集造成的,也可能是合理的极值。

3. 数据需要被聚合吗?

数据增强

 1. 特征工程是从原始数据中产生能够被用于建模的数据的过程,可以起到以下几种作用:

  • 使模型更容易被解释(如数据分箱(binning))

  • 捕获更复杂的关系(如神经网络)

  • 减少数据冗余并降低数据维度(如主成分分析(PCA))

  • 重新缩放变量(如标准化或归一化)

2. 不同的模型可能有不同的特征工程的要求。有的模型有内置的特征工程。

对问题进行分类

下一步是对问题进行分类。这是一个需要分两步实现的过程。

 1. 根据输入分类:

  • 如果你拥有的是带标签的数据,那么这就是一个监督学习问题。

  • 如果你拥有的是未标注过的数据,并且希望从中找到有用的结构,那么这就是一个无监督学习问题。

  • 如果你想要通过与环境的交互来优化一个目标函数,那么这就是一个强化学习问题。

2. 根据输出分类:

  • 如果模型的输出是一个(连续的)数字,那么这就是一个回归问题。

  • 如果模型的输出是一个类别,那么这就是一个分类问题。

  • 如果模型的输出是一组用输入数据划分出的簇,那么这就是一个聚类问题。

  • 你想发现一个异常点吗?此时你面对的就是一个异常检测问题。

理解你要满足的约束条件

  • 你需要考虑你能够存储数据的容量有多大?这取决于系统的存储容量,你可能无法存储若干 GB 大小的分类、回归模型或者若干 GB 的用于聚类分析的数据。例如,在嵌入式系统中,你就会面临这种情况。

  • 对预测过程的速度是否有要求?在实时应用中,很显然,尽快得出预测结果是十分重要的。例如,在自动驾驶问题中,应用必须尽可能快地对道路标志进行分类,以免发生交通事故。

  • 对学习过程的速度是否有要求?在某些情况下,快速训练模型是十分必要的:有时,你需要使用不同的数据集快速地实时更新你的模型。

寻找可用的算法

当对自己的任务环境有了一个清晰的认识后,你就可以使用你所掌握的工具确定适用于待解决的问题并切实可行的算法。一些影响你选择模型的因素如下:

  • 模型是否满足业务目标

  • 模型需要多少数据预处理工作

  • 模型有多准确

  • 模型的可解释性如何

  • 模型运行的速度有多快:构造模型需要多久?模型做出预测需要多长时间?

  • 模型的可伸缩性如何

模型的复杂度是一个影响算法选择的重要标准。一般来说,一个更复杂的模型具备下列特征:

  • 它依赖于更多的特征进行学习和预测(例如,使用十个而不是两个特征来预测目标)

  • 它依赖于更复杂的特征工程(例如,使用多项式特征、交互特征或主成分)

  • 它有更大的计算开销(例如,需要一个由 100 棵决策树组成的随机森林,而不是一棵单独的决策树)

除此之外,同样的机器学习算法可以基于参数的个数和某些超参数的选择而变得更加复杂。例如:

  • 回归模型可以拥有更多的特征,或者多项式项和交互项。

  • 决策树可以拥有更大或更小的深度。

将相同的算法变得更加复杂增加了发生过拟合的几率。

常用的机器学习算法

线性回归

这可能是机器学习中最简单的算法。例如,当你想要计算一些连续值,而不是将输出分类时,可以使用回归算法。因此,当你需要预测一个正在运行的过程未来的值时,你可以使用回归算法。然而,当特征冗余,即如果存在多重共线性(multicollinearity)时,线性回归就不太稳定。

在下列情况下可以考虑使用线性回归:

  • 从一个地方移动到另一个地方所需的时间

  • 预测下个月某种产品的销售情况

  • 血液中的酒精含量对协调能力的影响

  • 预测每个月礼品卡的销售情况,并改善年收入的估算

Logistic 回归

Logistic 回归执行二进制分类,因此输出二值标签。它将特征的线性组合作为输入,并且对其应用非线性函数(sigmoid),因此它是一个非常小的神经网络的实例。

logistic 回归提供了许多方法对你的模型进行正则化处理,因此正如在朴素贝叶斯算法中那样,你不必担心你的特征是否相关。该模型还有一个很好的概率化的解释。不像在决策树或者支持向量机中那样,你可以很容易地更新你的模型以获取新的数据。如果你想要使用一个概率化的框架,或者你希望在未来能够快速地将更多的训练数据融合到你的模型中,你可以使用 logistic 回归算法。logistic 回归还可以帮助你理解预测结果背后起作用的因素,它不完全是一个黑盒方法。

在下列情况下可以考虑使用 logistic 回归算法:

  • 预测客户流失

  • 信用评分和欺诈检测

  • 评价市场营销活动的效果

决策树

决策树很少被单独使用,但是不同的决策树可以组合成非常高效的算法,例如随机森林或梯度提升树算法。

决策树很容易处理特征交互,并且决策树是一种非参数模型,所以你不必担心异常值或者数据是否是线性可分的。决策树算法的一个缺点是,它们不支持在线学习,因此当你要使用新的样本时,你不得不重新构建决策树。决策树的另一个缺点是,它很容易发生过拟合,而这就是像随机森林(或提升树)这样的集成学习方法能够派上用场的地方。决策树也需要大量的内存空间(拥有的特征越多,你的决策树可能会越深、越大)

决策树能够很好地帮助你在诸多行动路径中做出选择:

  • 做出投资决策

  • 预测客户流失

  • 找出可能拖欠银行贷款的人

  • 在「建造」和「购买」两种选择间进行抉择

  • 销售主管的资质审核

K-均值

有时,你完全没有数据的标签信息,并且你的目的是根据对象的特征来为其打上标签。这种问题被称为聚类任务。聚类算法可以在这种情况下被使用:例如,当你有一大群用户,你希望根据他们共有的一些属性将其划分到一些特定的组中。

如果在你的问题声明中有这样的问题:例如,找出一群个体的组织形式,或将某些东西分组,或找出特定的组。这时,你就应该使用聚类算法。

该方法最大的缺点是,K-均值算法需要提前知道你的数据会有多少簇,因此这可能需要进行大量的试验去「猜测」我们最终定义的簇的最佳个数——K。

主成分分析(PCA)

主成分分析能够对数据进行降维。有时,你拥有各种各样的特征,这些特征之间的相关性可能很高,而模型如果使用如此大量的数据可能会产生过拟合现象。这时,你可以使用主成分分析(PCA)技术。

主成分分析(PCA)能够起作用的关键因素是:除了低维的样本表征,它还提供了各种变量的一种同步的低维表征。同步的样本和变量的表征提供了一种能够可视化地找到能够表示一组样本的特征的变量的方法。

支持向量机

支持向量机(SVM)是一种在模式识别和分类问题中被广泛应用的监督机器学习技术——当你的数据恰好有两类时。

支持向量机准确率高,对于防止过拟合很好的理论保障。当你使用一个合适的核函数时,即使你的数据在基(低维)特征空间中是线性不可分的,他们也可以很好地工作。支持向量机在文本分类问题中非常流行,在该问题中,输入是一个维度非常高的空间是很正常的。然而,SVM 是一种内存密集型算法,它很难被解释,并且对其进行调优十分困难。

在下列现实世界的应用中,你可以使用支持向量机:

  • 发现患有糖尿病等常见疾病的人

  • 手写字符识别

  • 文本分类——将文章按照话题分类

  • 股票市场价格预测

朴素贝叶斯

这是一种基于贝叶斯定理的分类技术,它很容易构建,非常适用于大规模数据集。除了结构简单,据说朴素贝叶斯的表现甚至比一些复杂得多的分类方法更好。当 CPU 和内存资源有限时,朴素贝叶斯算法也是一个很好的选项。

朴素贝叶斯非常简单,你仅仅是在做大量的计数工作。如果朴素贝叶斯的条件独立假设确实成立,朴素贝叶斯分类器的收敛速度会比 logistic 回归这样的判别模型更快,因此需要的训练数据更少。即使朴素贝叶斯的假设不成立,朴素贝叶斯分类器往往也能很好地完成任务。如果你想使用一种快速的、简单的、性能也不错的模型,朴素贝叶斯是一个很好的选择。这种算法最大的缺点就是它不能学习到特征之间的相互作用。

在下列真实世界的应用中,你可以使用朴素贝叶斯:

  • 情感分析和文本分类

  • 类似于 Netflix、Amazon 这样的推荐系统

  • 识别垃圾邮件

  • 人脸识别

随机森林

随机森林是一种决策树的集成方法。它能够同时解决具有大规模数据集的回归问题和分类问题,还有助于从数以千计的输入变量中找出最重要的变量。随机森林具有很强的可伸缩性,它适用于任何维数的数据,并且通常具有相当不错的性能。此外,还有一些遗传算法,它们可以在具有最少的关于数据本身的知识的情况下,很好地扩展到任何维度和任何数据上,其中最简单的实现就是微生物遗传算法。然而,随机森林学习的速度可能会很慢(取决于参数设置),并且这种方法不能迭代地改进生成模型。

在下列现实世界的应用中,你可以使用随机森林:

  • 预测高危患者

  • 预测零件在生产中的故障

  • 预测拖欠贷款的人

神经网络

神经网络中包含着神经元之间连接的权重。这些权重是平衡的,逐次对数据点进行学习。当所有的权重都被训练好后,如果需要对新给定的数据点进行回归,神经网络可以被用于预测分类结果或一个具体数值。利用神经网络,可以对特别复杂的模型进行训练,并且将其作为一种黑盒方法加以利用,而在训练模型之前,我们无需进行不可预测的复杂特征工程。通过与「深度方法」相结合,甚至可以采用更加不可预测的模型去实现新任务。例如,最近人们已经通过深度神经网络大大提升了物体识别任务的结果。深度学习还被应用于特征提取这样的非监督学习任务,也可以在人为干预更少的情况下,从原始图像或语音中提取特征。

另一方面,神经网络很难被解释清楚,其参数设置也复杂地让人难以置信。此外,神经网络算法也都是资源密集型和内存密集型的。

SCIKIT 参考手册

Scikit learning 为大家提供了一个非常深入的、解释地很清楚的流程图,它能够帮助你选择正确的算法。我认为此图十分方便。

结论

一般来说,你可以根据上面介绍的要点来筛选出一些算法,但是要想在一开始就知道哪种方法最好是很难的。你最好多迭代几次选择算法的过程。将你的数据输入给那些你确定的潜在优秀机器学习算法,通过并行或串行的方式运行这些算法,最终评估算法性能,从而选择出最佳的算法。

在最后,我想告诉你:为现实生活中的问题找到正确的解决方案,通常不仅仅是一个应用数学方法的问题。这要求我们对业务需求、规则和制度、相关利益者的关注点有所了解,并且具备大量的专业知识。在解决一个机器学习问题的同时,能够结合并平衡这些问题是至关重要的,那些能做到这一点的人可以创造最大的价值。

原文链接:https://ift.tt/2t7VTr5

]]> 原文: https://ift.tt/2Kp128T
RSS Feed

机器知心

IFTTT

搭建模型第一步:你需要预习的 NumPy 基础都在这了

基础知识

NumPy 主要的运算对象为同质的多维数组,即由同一类型元素(一般是数字)组成的表格,且所有元素通过正整数元组进行索引。在 NumPy 中,维度 (dimension) 也被称之为轴线(axes)。

比如坐标点 [1, 2, 1] 有一个轴线。这个轴上有 3 个点,所以我们说它的长度(length)为 3。而如下数组(array)有 2 个轴线,长度同样为 3。

[[ 1., 0., 0.], [ 0., 1., 2.]] 

NumPy 的数组类(array class)叫做 ndarray,同时我们也常称其为数组(array)。注意 numpy.array 和标准 Python 库中的类 array.array 是不同的。标准 Python 库中的类 array.array 只处理一维的数组,提供少量的功能。ndarray 还具有如下很多重要的属性:

  • ndarray.ndim:显示数组的轴线数量(或维度)。

  • ndarray.shape:显示在每个维度里数组的大小。如 n 行 m 列的矩阵,它的 shape 就是(n,m)。

>>> b = np.array([[1,2,3],[4,5,6]]) >>> b.shape (2, 3) 
  • ndarray.size:数组中所有元素的总量,相当于数组的 shape 中所有元素的乘积,例如矩阵的元素总量为行与列的乘积。

>>> b = np.array([[1,2,3],[4,5,6]]) >>> b.size 6 
  • ndarray.dtype:显示数组元素的类型。Python 中的标准 type 函数同样可以用于显示数组类型,NumPy 有它自己的类型如:numpy.int32, numpy.int16, 和 numpy.float64,其中「int」和「float」代表数据的种类是整数还是浮点数,「32」和「16」代表这个数组的字节数(存储大小)。

  • ndarray.itemsize:数组中每个元素的字节存储大小。例如元素类型为 float64 的数组,其 itemsize 为 8(=64/8)。

>>> import numpy as np >>> a = np.arange(15).reshape(3, 5) >>> a array([[ 0,  1,  2,  3,  4],        [ 5,  6,  7,  8,  9],        [10, 11, 12, 13, 14]]) >>> a.shape (3, 5) >>> a.ndim 2 >>> a.dtype.name 'int64' >>> a.itemsize 8 >>> a.size 15 >>> type(a)  >>> b = np.array([6, 7, 8]) >>> b array([6, 7, 8]) >>> type(b)  

创建数组

NumPy 有很多种创建数组的方法。比如,你可以用 Python 的列表(list)来创建 NumPy 数组,其中生成的数组元素类型与原序列相同。

>>> import numpy as np >>> a = np.array([2,3,4]) >>> a array([2, 3, 4]) >>> a.dtype dtype('int64') >>> b = np.array([1.2, 3.5, 5.1]) >>> b.dtype dtype('float64') 

一个常见的误差(error)在于调用 array 时使用了多个数值参数,而正确的方法应该是用「[]」来定义一个列表的数值而作为数组的一个参数

>>> a = np.array(1,2,3,4)    # WRONG >>> a = np.array([1,2,3,4])  # RIGHT 

array 将序列中的序列转换为二维的数组,序列中的序列中的序列转换为三维数组,以此类推。

>>> b = np.array([(1.5,2,3), (4,5,6)]) >>> b array([[ 1.5,  2. ,  3. ],        [ 4. ,  5. ,  6. ]]) 

数组的类型也可以在创建时指定清楚:

>>> b = np.array([(1.5,2,3), (4,5,6)]) >>> c = np.array( [ [1,2], [3,4] ], dtype=complex ) >>> c array([[ 1.+0.j,  2.+0.j],        [ 3.+0.j,  4.+0.j]]) 

一般数组的内部元素初始是未知的,但它的大小是已知的。因此,NumPy 提供了一些函数可以创建有初始数值的占位符数组,这样可以减少不必要的数组增长及运算成本。

函数 zeros 可创建一个内部元素全是 0 的数组,函数 ones 可创建一个内部元素全是 1 的数组,函数 empty 可创建一个初始元素为随机数的数组,具体随机量取决于内存状态。默认状态下,创建数组的数据类型(dtype)一般是 float64。

>>> np.zeros( (3,4) ) array([[ 0.,  0.,  0.,  0.],        [ 0.,  0.,  0.,  0.],        [ 0.,  0.,  0.,  0.]]) >>> np.ones( (2,3,4), dtype=np.int16 )   # dtype can also be specified array([[[ 1, 1, 1, 1],         [ 1, 1, 1, 1],         [ 1, 1, 1, 1]],        [[ 1, 1, 1, 1],         [ 1, 1, 1, 1],         [ 1, 1, 1, 1]]], dtype=int16) >>> np.empty( (2,3) )                    # uninitialized, output may vary array([[  3.73603959e-262,   6.02658058e-154,   6.55490914e-260],        [  5.30498948e-313,   3.14673309e-307,   1.00000000e+000]]) 

为了创建数列,NumPy 提供一个与 range 类似的函数来创建数组:arange。

>>> np.arange( 10, 30, 5 ) array([10, 15, 20, 25]) >>> np.arange( 0, 2, 0.3 )                 # it accepts float arguments array([ 0. ,  0.3,  0.6,  0.9,  1.2,  1.5,  1.8]) 

当 arange 使用浮点型参数时,因为浮点精度的有限性,arange 不能判断有需要创建的数组多少个元素。在这种情况下,换成 linspace 函数可以更好地确定区间内到底需要产生多少个数组元素。

>>> from numpy import pi >>> np.linspace( 0, 2, 9 )                 # 9 numbers from 0 to 2 array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ,  1.25,  1.5 ,  1.75,  2.  ]) >>> x = np.linspace( 0, 2*pi, 100 )        # useful to evaluate function at lots of points >>> f = np.sin(x) 

array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.rand, numpy.random.randn, fromfunction, fromfile (这些函数也可以创建数组,有时间可以尝试解释)

输出数组

当你输出一个数组时,NumPy 显示这个数组的方式和嵌套列表是相似的。但将数组打印到屏幕需要遵守以下布局:

  • 最后一个轴由左至右打印

  • 倒数第二个轴为从上到下打印

  • 其余的轴都是从上到下打印,且每一块之间都通过一个空行分隔

如下所示,一维数组输出为一行、二维为矩阵、三维为矩阵列表。

>>> a = np.arange(6)                         # 1d array >>> print(a) [0 1 2 3 4 5] >>> >>> b = np.arange(12).reshape(4,3)           # 2d array >>> print(b) [[ 0  1  2]  [ 3  4  5]  [ 6  7  8]  [ 9 10 11]] >>> >>> c = np.arange(24).reshape(2,3,4)         # 3d array >>> print(c) [[[ 0  1  2  3]   [ 4  5  6  7]   [ 8  9 10 11]]  [[12 13 14 15]   [16 17 18 19]   [20 21 22 23]]] 

上述使用的 reshape 函数可指定数组的行列数,并将所有元素按指定的维度数排列,详细介绍请看后面章节。在数组的打印中,如果一个数组所含元素数太大,NumPy 会自动跳过数组的中间部分,只输出两边。

>>> print(np.arange(10000)) [   0    1    2 ..., 9997 9998 9999] >>> >>> print(np.arange(10000).reshape(100,100)) [[   0    1    2 ...,   97   98   99]  [ 100  101  102 ...,  197  198  199]  [ 200  201  202 ...,  297  298  299]  ...,  [9700 9701 9702 ..., 9797 9798 9799]  [9800 9801 9802 ..., 9897 9898 9899]  [9900 9901 9902 ..., 9997 9998 9999]] 

如果想要 NumPy 输出整个数组,你可以用 set_printoptions 改变输出设置。

>>> np.set_printoptions(threshold=np.nan) 

基础运算

数组中的算术运算一般是元素级的运算,运算结果会产生一个新的数组。如下所示减法、加法、平方、对应元素乘积和逻辑运算都是元素级的操作。

>>> a = np.array( [20,30,40,50] ) >>> b = np.arange( 4 ) >>> b array([0, 1, 2, 3]) >>> c = a-b >>> c array([20, 29, 38, 47]) >>> b**2 array([0, 1, 4, 9]) >>> 10*np.sin(a) array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854]) >>> a<35 array([ True, True, False, False]) 

不同于许多科学计算语言,乘法算子 * 或 multiple 函数在 NumPy 数组中用于元素级的乘法运算,矩阵乘法可用 dot 函数或方法来执行。

>>> A = np.array( [[1,1], ...             [0,1]] ) >>> B = np.array( [[2,0], ...             [3,4]] ) >>> A*B                         # elementwise product array([[2, 0],        [0, 4]]) >>> A.dot(B)                    # matrix product array([[5, 4],        [3, 4]]) >>> np.dot(A, B)                # another matrix product array([[5, 4],        [3, 4]]) 

有一些操作,如 += 和 *=,其输出结果会改变一个已存在的数组,而不是如上述运算创建一个新数组。

>>> a = np.ones((2,3), dtype=int) >>> b = np.random.random((2,3)) >>> a *= 3 >>> a array([[3, 3, 3],        [3, 3, 3]]) >>> b += a >>> b array([[ 3.417022  ,  3.72032449,  3.00011437],        [ 3.30233257,  3.14675589,  3.09233859]]) >>> a += b                  # b is not automatically converted to integer type Traceback (most recent call last):   ... TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int64') with casting rule 'same_kind' 

当操作不同数据类型的数组时,最后输出的数组类型一般会与更普遍或更精准的数组相同(这种行为叫做 Upcasting)。

>>> a = np.ones(3, dtype=np.int32) >>> b = np.linspace(0,pi,3) >>> b.dtype.name 'float64' >>> c = a+b >>> c array([ 1.        ,  2.57079633,  4.14159265]) >>> c.dtype.name 'float64' >>> d = np.exp(c*1j) >>> d array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,        -0.54030231-0.84147098j]) >>> d.dtype.name 'complex128' 

许多一元运算,如计算数组中所有元素的总和,是属于 ndarray 类的方法。

>>> a = np.random.random((2,3)) >>> a array([[ 0.18626021,  0.34556073,  0.39676747],        [ 0.53881673,  0.41919451,  0.6852195 ]]) >>> a.sum() 2.5718191614547998 >>> a.min() 0.1862602113776709 >>> a.max() 0.6852195003967595 

默认状态下,这些运算会把数组视为一个数列而不论它的 shape。然而,如果在指定 axis 参数下,你可以指定针对哪一个维度进行运算。如下 axis=0 将针对每一个列进行运算,例如 b.sum(axis=0) 将矩阵 b 中每一个列的所有元素都相加为一个标量。

>>> b = np.arange(12).reshape(3,4) >>> b array([[ 0,  1,  2,  3],        [ 4,  5,  6,  7],        [ 8,  9, 10, 11]]) >>> >>> b.sum(axis=0)                            # sum of each column array([12, 15, 18, 21]) >>> >>> b.min(axis=1)                            # min of each row array([0, 4, 8]) >>> >>> b.cumsum(axis=1)                         # cumulative sum along each row array([[ 0,  1,  3,  6],        [ 4,  9, 15, 22],        [ 8, 17, 27, 38]]) 

索引、截取和迭代

一维数组可以被索引、截取(Slicing)和迭代,就像 Python 列表和元组一样。注意其中 a[0:6:2] 表示从第 1 到第 6 个元素,并对每两个中的第二个元素进行操作。

>>> a = np.arange(10)**3 >>> a array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729]) >>> a[2] 8 >>> a[2:5] array([ 8, 27, 64]) >>> a[:6:2] = -1000    # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000 >>> a array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,   729]) >>> a[ : :-1]                                 # reversed a array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1, -1000]) >>> for i in a: ...     print(i**(1/3.)) ... nan 1.0 nan 3.0 nan 5.0 6.0 7.0 8.0 9.0 

多维数组每个轴都可以有一个索引。这些索引在元组中用逗号分隔:

>>> def f(x,y): ...     return 10*x+y ... >>> b = np.fromfunction(f,(5,4),dtype=int) >>> b array([[ 0,  1,  2,  3],        [10, 11, 12, 13],        [20, 21, 22, 23],        [30, 31, 32, 33],        [40, 41, 42, 43]]) >>> b[2,3] 23 >>> b[0:5, 1]                       # each row in the second column of b array([ 1, 11, 21, 31, 41]) >>> b[ : ,1]                        # equivalent to the previous example array([ 1, 11, 21, 31, 41]) >>> b[1:3, : ]                      # each column in the second and third row of b array([[10, 11, 12, 13],        [20, 21, 22, 23]]) 

当有些维度没有指定索引时,空缺的维度被默认为取所有元素。

>>> b[-1]                                  # the last row. Equivalent to b[-1,:] array([40, 41, 42, 43]) 

如上因为省略了第二维,b[i] 表示输出第 i 行。当然我们也可以用「:」表示省略的维度,例如 b[i] 等价于 b[i, :]。此外,NumPy 还允许使用 dots (...) 表示足够多的冒号来构建完整的索引元组。

比如,如果 x 是 5 维数组:

  • x[1,2,...] 等于 x[1,2,:,:,:],

  • x[...,3] 等于 x[:,:,:,:,3] 

  • x[4,...,5,:] 等于 x[4,:,:,5,:]

>>> c = np.array( [[[  0,  1,  2],               # a 3D array (two stacked 2D arrays) ...                 [ 10, 12, 13]], ...                [[100,101,102], ...                 [110,112,113]]]) >>> c.shape (2, 2, 3) >>> c[1,...]                                   # same as c[1,:,:] or c[1] array([[100, 101, 102],        [110, 112, 113]]) >>> c[...,2]                                   # same as c[:,:,2] array([[  2,  13],        [102, 113]]) 

多维数组中的迭代以第一条轴为参照完成,如下每一次循环都输出一个 b[i]:

>>> for row in b: ...     print(row) ... [0 1 2 3] [10 11 12 13] [20 21 22 23] [30 31 32 33] [40 41 42 43] 

然而,如果想在数组的每个元素上进行操作,可以用 flat 方法。flat 是一个在数组所有元素中运算的迭代器,如下将逐元素地对数组进行操作。

>>> for element in b.flat: ...     print(element) ... 0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43 

Shape 变换

改变数组的 shape

一个数组的 shape 是由轴及其元素数量决定的,它一般由一个整型元组表示,且元组中的整数表示对应维度的元素数。

>>> a = np.floor(10*np.random.random((3,4))) >>> a array([[ 2.,  8.,  0.,  6.],        [ 4.,  5.,  1.,  1.],        [ 8.,  9.,  3.,  6.]]) >>> a.shape (3, 4) 

一个数组的 shape 可以由许多方法改变。例如以下三种方法都可输出一个改变 shape 后的新数组,它们都不会改变原数组。其中 reshape 方法在实践中会经常用到,因为我们需要改变数组的维度以执行不同的运算。

>>> a.ravel()  # returns the array, flattened array([ 2.,  8.,  0.,  6.,  4.,  5.,  1.,  1.,  8.,  9.,  3.,  6.]) >>> a.reshape(6,2)  # returns the array with a modified shape array([[ 2.,  8.],        [ 0.,  6.],        [ 4.,  5.],        [ 1.,  1.],        [ 8.,  9.],        [ 3.,  6.]]) >>> a.T  # returns the array, transposed array([[ 2.,  4.,  8.],        [ 8.,  5.,  9.],        [ 0.,  1.,  3.],        [ 6.,  1.,  6.]]) >>> a.T.shape (4, 3) >>> a.shape (3, 4) 

ravel() 和 flatten() 都是将多维数组降位一维,flatten() 返回一份新的数组,且对它所做的修改不会影响原始数组,而 ravel() 返回的是 view,会影响原始矩阵。

在矩阵的转置中,行和列的维度将交换,且矩阵中每一个元素将沿主对角线对称变换。此外,reshape 如下所示返回修改过维度的新数组,而 resize 方法将直接修改原数组本身的维度。

>>> a array([[ 2.,  8.,  0.,  6.],        [ 4.,  5.,  1.,  1.],        [ 8.,  9.,  3.,  6.]]) >>> a.resize((2,6)) >>> a array([[ 2.,  8.,  0.,  6.,  4.,  5.],        [ 1.,  1.,  8.,  9.,  3.,  6.]]) 

如果在 shape 变换中一个维度设为 - 1,那么这一个维度包含的元素数将会被自动计算。如下所示,a 一共有 12 个元素,在确定一共有 3 行后,-1 会自动计算出应该需要 4 列才能安排所有的元素。

>>> a.reshape(3,-1) array([[ 2.,  8.,  0.,  6.],        [ 4.,  5.,  1.,  1.],        [ 8.,  9.,  3.,  6.]]) 

数组堆叠

数组可以在不同轴上被堆叠在一起。如下所示 vstack 将在第二个维度(垂直)将两个数组拼接在一起,而 hstack 将在第一个维度(水平)将数组拼接在一起。

>>> a = np.floor(10*np.random.random((2,2))) >>> a array([[ 8.,  8.],        [ 0.,  0.]]) >>> b = np.floor(10*np.random.random((2,2))) >>> b array([[ 1.,  8.],        [ 0.,  4.]]) >>> np.vstack((a,b)) array([[ 8.,  8.],        [ 0.,  0.],        [ 1.,  8.],        [ 0.,  4.]]) >>> np.hstack((a,b)) array([[ 8.,  8.,  1.,  8.],        [ 0.,  0.,  0.,  4.]]) 

column_stack 函数可堆叠一维数组为二维数组的列,作用相等于针对二维数组的 hstack 函数。

>>> from numpy import newaxis >>> np.column_stack((a,b))     # with 2D arrays array([[ 8.,  8.,  1.,  8.],        [ 0.,  0.,  0.,  4.]]) >>> a = np.array([4.,2.]) >>> b = np.array([3.,8.]) >>> np.column_stack((a,b))     # returns a 2D array array([[ 4., 3.],        [ 2., 8.]]) >>> np.hstack((a,b))           # the result is different array([ 4., 2., 3., 8.]) >>> a[:,newaxis]               # this allows to have a 2D columns vector array([[ 4.],        [ 2.]]) >>> np.column_stack((a[:,newaxis],b[:,newaxis])) array([[ 4.,  3.],        [ 2.,  8.]]) >>> np.hstack((a[:,newaxis],b[:,newaxis]))   # the result is the same array([[ 4.,  3.],        [ 2.,  8.]]) 

与 column_stack 相似,row_stack 函数相等于二维数组中的 vstack。一般在高于二维的情况中,hstack 沿第二个维度堆叠、vstack 沿第一个维度堆叠,而 concatenate 更进一步可以在任意给定的维度上堆叠两个数组,当然这要求其它维度的长度都相等。concatenate 在很多深度模型中都有应用,例如权重矩阵的堆叠或 DenseNet 特征图的堆叠

在复杂情况中,r_ 和 c_ 可以有效地在创建数组时帮助沿着一条轴堆叠数值,它们同样允许使用范围迭代「:」生成数组。

>>> np.r_[1:4,0,4] array([1, 2, 3, 0, 4]) 

当用数组为参数时,r_ 和 c_ 在默认行为下与 vstack 和 hstack 相似,但它们如 concatenate 一样允许给定需要堆叠的维度。

拆分数组

使用 hsplit 可以顺着水平轴拆分一个数组,我们指定切分后输出的数组数,或指定在哪一列拆分数组:

>>> a = np.floor(10*np.random.random((2,12))) >>> a array([[ 9.,  5.,  6.,  3.,  6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],        [ 1.,  4.,  9.,  2.,  2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]]) >>> np.hsplit(a,3)   # Split a into 3 [array([[ 9.,  5.,  6.,  3.],        [ 1.,  4.,  9.,  2.]]), array([[ 6.,  8.,  0.,  7.],        [ 2.,  1.,  0.,  6.]]), array([[ 9.,  7.,  2.,  7.],        [ 2.,  2.,  4.,  0.]])] >>> np.hsplit(a,(3,4))   # Split a after the third and the fourth column [array([[ 9.,  5.,  6.],        [ 1.,  4.,  9.]]), array([[ 3.],        [ 2.]]), array([[ 6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],        [ 2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]])] 

vsplit 沿着垂直轴拆分,array_split 可指定顺着哪一条轴拆分。

复制与 views

在进行数组运算或操作时,入门者经常很难判断数据到底是复制到了新的数组还是直接在原始数据上修改。这对进一步的运算有很大的影响,因此有时候我们也需要复制内容到新的变量内存中,而不能仅将新变量指向原内存。目前一般有三种复制方法,即不复制内存、浅复制以及深复制。

实际不复制

简单的任务并不会复制数组目标或它们的数据,如下先把变量 a 赋值于 b,然后修改变量 b 就会同时修改变量 a,这种一般的赋值方法会令变量间具有关联性。

>>> a = np.arange(12) >>> b = a            # no new object is created >>> b is a           # a and b are two names for the same ndarray object True >>> b.shape = 3,4    # changes the shape of a >>> a.shape (3, 4) 

Pythan 将不定对象作为参照(references)传递,所以调用函数不会产生目标识别符的变化,也不会发生实际的内容复制。

>>> def f(x): ...     print(id(x)) ... >>> id(a)                           # id is a unique identifier of an object 148293216 >>> f(a) 148293216 

View 或浅复制

不同数组对象可以共享相同数据,view 方法可以创建一个新数组对象来查看相同数据。如下 c 和 a 的目标识别符并不一致,且改变其中一个变量的 shape 并不会对应改变另一个。但这两个数组是共享所有元素的,所以改变一个数组的某个元素同样会改变另一个数组的对应元素。

>>> c = a.view() >>> c is a False >>> c.base is a                        # c is a view of the data owned by a True >>> c.flags.owndata False >>> >>> c.shape = 2,6                      # a's shape doesn't change >>> a.shape (3, 4) >>> c[0,4] = 1234                      # a's data changes >>> a array([[   0,    1,    2,    3],        [1234,    5,    6,    7],        [   8,    9,   10,   11]]) 

分割数组输出的是它的一个 view,如下将数组 a 分割为子数组 s,那么 s 就是 a 的一个 view,修改 s 中的元素同样会修改 a 中对应的元素。

>>> s = a[ : , 1:3]     # spaces added for clarity; could also be written "s = a[:,1:3]" >>> s[:] = 10           # s[:] is a view of s. Note the difference between s=10 and s[:]=10 >>> a array([[   0,   10,   10,    3],        [1234,   10,   10,    7],        [   8,   10,   10,   11]]) 

深复制

copy 方法可完整地复制数组及数据,这种赋值方法会令两个变量有不一样的数组目标,且数据不共享。

>>> d = a.copy()                          # a new array object with new data is created >>> d is a False >>> d.base is a                           # d doesn't share anything with a False >>> d[0,0] = 9999 >>> a array([[   0,   10,   10,    3],        [1234,   10,   10,    7],        [   8,   10,   10,   11]]) 

深入理解 NumPy

广播机制

广播操作是 NumPy 非常重要的一个特点,它允许 NumPy 扩展矩阵间的运算。例如它会隐式地把一个数组的异常维度调整到与另一个算子相匹配的维度以实现维度兼容。例如将一个维度为 [3,2] 的矩阵与另一个维度为 [3,1] 的矩阵相加是合法的,NumPy 会自动将第二个矩阵扩展到等同的维度。

为了定义两个形状是否是可兼容的,NumPy 从最后开始往前逐个比较它们的维度大小。在这个过程中,如果两者的对应维度相同,或者其一(或者全是)等于 1,则继续进行比较,直到最前面的维度。若不满足这两个条件,程序就会报错。

如下展示了一个广播操作:

>>>a = np.array([1.0,2.0,3.0,4.0, 5.0, 6.0]).reshape(3,2) >>>b = np.array([3.0]) >>>a * b  array([[  3.,   6.],        [  9.,  12.],        [ 15.,  18.]]) 

高级索引

NumPy 比一般的 Python 序列提供更多的索引方式。除了之前看到的用整数和截取的索引,数组可以由整数数组和布尔数组 indexed。

通过数组索引

如下我们可以根据数组 i 和 j 索引数组 a 中间的元素,其中输出数组保持索引的 shape。

>>> a = np.arange(12)**2                       # the first 12 square numbers >>> i = np.array( [ 1,1,3,8,5 ] )              # an array of indices >>> a[i]                                       # the elements of a at the positions i array([ 1,  1,  9, 64, 25])  >>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] )      # a bidimensional array of indices >>> a[j]                                       # the same shape as j array([[ 9, 16],        [81, 49]]) 

当使用多维数组作为索引时,每一个维度就会索引一次原数组,并按索引的 shape 排列。下面的代码展示了这种索引方式,palette 可以视为简单的调色板,而数组 image 中的元素则表示索引对应颜色的像素点。

>>> palette = np.array( [ [0,0,0],                # black ...                       [255,0,0],              # red ...                       [0,255,0],              # green ...                       [0,0,255],              # blue ...                       [255,255,255] ] )       # white >>> image = np.array( [ [ 0, 1, 2, 0 ],           # each value corresponds to a color in the palette ...                     [ 0, 3, 4, 0 ]  ] ) >>> palette[image]                            # the (2,4,3) color image array([[[  0,   0,   0],         [255,   0,   0],         [  0, 255,   0],         [  0,   0,   0]],        [[  0,   0,   0],         [  0,   0, 255],         [255, 255, 255],         [  0,   0,   0]]])        [81, 49]]) 

我们也可以使用多维索引获取数组中的元素,多维索引的每个维度都必须有相同的形状。如下多维数组 i 和 j 可以分别作为索引 a 中第一个维度和第二个维度的参数,例如 a[i, j] 分别从 i 和 j 中抽取一个元素作为索引 a 中元素的参数

>>> a = np.arange(12).reshape(3,4) >>> a array([[ 0,  1,  2,  3],        [ 4,  5,  6,  7],        [ 8,  9, 10, 11]]) >>> i = np.array( [ [0,1],                        # indices for the first dim of a ...                 [1,2] ] ) >>> j = np.array( [ [2,1],                        # indices for the second dim ...                 [3,3] ] ) >>> >>> a[i,j]                                     # i and j must have equal shape array([[ 2,  5],        [ 7, 11]]) >>> >>> a[i,2] array([[ 2,  6],        [ 6, 10]]) >>> >>> a[:,j]                                     # i.e., a[ : , j] array([[[ 2,  1],         [ 3,  3]],        [[ 6,  5],         [ 7,  7]],        [[10,  9],         [11, 11]]]) 

同样,我们把 i 和 j 放在一个序列中,然后用它作为索引:

>>> l = [i,j] >>> a[l]                                       # equivalent to a[i,j] array([[ 2,  5],        [ 7, 11]]) 

然而,我们不能如上把 i 和 j 放在一个数组中作为索引,因为数组会被理解为索引 a 的第一维度。

>>> s = np.array( [i,j] ) >>> a[s]                                       # not what we want Traceback (most recent call last):   File "", line 1, in ? IndexError: index (3) out of range (0<=index<=2) in dimension 0 >>> >>> a[tuple(s)]                                # same as a[i,j] array([[ 2,  5],        [ 7, 11]]) 

另一个将数组作为索引的常用方法是搜索时间序列的最大值:

>>> time = np.linspace(20, 145, 5)                 # time scale >>> data = np.sin(np.arange(20)).reshape(5,4)      # 4 time-dependent series >>> time array([  20.  ,   51.25,   82.5 ,  113.75,  145.  ]) >>> data array([[ 0.        ,  0.84147098,  0.90929743,  0.14112001],        [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],        [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],        [-0.53657292,  0.42016704,  0.99060736,  0.65028784],        [-0.28790332, -0.96139749, -0.75098725,  0.14987721]]) >>> >>> ind = data.argmax(axis=0)                  # index of the maxima for each series >>> ind array([2, 0, 3, 1]) >>> >>> time_max = time[ind]                       # times corresponding to the maxima >>> >>> data_max = data[ind, range(data.shape[1])] # => data[ind[0],0], data[ind[1],1]... >>> >>> time_max array([  82.5 ,   20.  ,  113.75,   51.25]) >>> data_max array([ 0.98935825,  0.84147098,  0.99060736,  0.6569866 ]) >>> >>> np.all(data_max == data.max(axis=0)) True 

你也可以用数组索引作为一个分配目标:

>>> a = np.arange(5) >>> a array([0, 1, 2, 3, 4]) >>> a[[1,3,4]] = 0 >>> a array([0, 0, 2, 0, 0]) 

然而,当索引列表中有重复时,赋值任务会执行多次,并保留最后一次结果。

>>> a = np.arange(5) >>> a[[0,0,2]]=[1,2,3] >>> a array([2, 1, 3, 3, 4]) 

这是合理的,但注意如果你使用 Python 的 += 创建,可能不会得出预期的结果:

>>> a = np.arange(5) >>> a[[0,0,2]]+=1 >>> a array([1, 1, 3, 3, 4]) 

虽然 0 在索引列表中出现两次,第 0 个元素只会增加一次。这是因为 Python 中「a+=1」等于「a = a + 1」.

用布尔数组做索引

当我们索引数组元素时,我们在提供索引列表。但布尔值索引是不同的,我们需要清楚地选择被索引数组中哪个元素是我们想要的哪个是不想要的。

布尔索引需要用和原数组相同 shape 的布尔值数组,如下只有在大于 4 的情况下才输出 True,而得出来的布尔值数组可作为索引。

>>> a = np.arange(12).reshape(3,4) >>> b = a > 4 >>> b                                          # b is a boolean with a's shape array([[False, False, False, False],        [False,  True,  True,  True],        [ True,  True,  True,  True]]) >>> a[b]                                       # 1d array with the selected elements array([ 5,  6,  7,  8,  9, 10, 11]) 

这个性质在任务中非常有用,例如在 ReLu 激活函数中,只有大于 0 才输出激活值,因此我们就能使用这种方式实现 ReLU 激活函数

>>> a[b] = 0                                   # All elements of 'a' higher than 4 become 0 >>> a array([[0, 1, 2, 3],        [4, 0, 0, 0],        [0, 0, 0, 0]]) 

第二种使用布尔索引的方法与整数索引更加相似的;在数组的每个维度中,我们使用一维布尔数组选择我们想要的截取部分:

>>> a = np.arange(12).reshape(3,4) >>> b1 = np.array([False,True,True])             # first dim selection >>> b2 = np.array([True,False,True,False])       # second dim selection >>> >>> a[b1,:]                                   # selecting rows array([[ 4,  5,  6,  7],        [ 8,  9, 10, 11]]) >>> >>> a[b1]                                     # same thing array([[ 4,  5,  6,  7],        [ 8,  9, 10, 11]]) >>> >>> a[:,b2]                                   # selecting columns array([[ 0,  2],        [ 4,  6],        [ 8, 10]]) >>> >>> a[b1,b2]                                  # a weird thing to do array([ 4, 10]) 

注意一维布尔数组的长度必须和想截取轴的长度相同。在上面的例子中,b1 的长度 3、b2 的长度为 4,它们分别对应于 a 的第一个维度与第二个维度。

线性代数

简单的数组运算

如下仅展示了简单的矩阵运算更多详细的方法可在实践中遇到在查找 API。如下展示了矩阵的转置、求逆、单位矩阵、矩阵乘法、矩阵的迹、解线性方程和求特征向量等基本运算:

>>> import numpy as np >>> a = np.array([[1.0, 2.0], [3.0, 4.0]]) >>> print(a) [[ 1.  2.]  [ 3.  4.]]  >>> a.transpose() array([[ 1.,  3.],        [ 2.,  4.]])  >>> np.linalg.inv(a) array([[-2. ,  1. ],        [ 1.5, -0.5]])  >>> u = np.eye(2) # unit 2x2 matrix; "eye" represents "I" >>> u array([[ 1.,  0.],        [ 0.,  1.]]) >>> j = np.array([[0.0, -1.0], [1.0, 0.0]])  >>> np.dot (j, j) # matrix product array([[-1.,  0.],        [ 0., -1.]])  >>> np.trace(u)  # trace 2.0  >>> y = np.array([[5.], [7.]]) >>> np.linalg.solve(a, y) array([[-3.],        [ 4.]])  >>> np.linalg.eig(j) (array([ 0.+1.j,  0.-1.j]), array([[ 0.70710678+0.j        ,  0.70710678-0.j        ],        [ 0.00000000-0.70710678j,  0.00000000+0.70710678j]]))  Parameters:     square matrix Returns     The eigenvalues, each repeated according to its multiplicity.     The normalized (unit "length") eigenvectors, such that the     column ``v[:,i]`` is the eigenvector corresponding to the     eigenvalue ``w[i]`` . 


原文档链接:https://ift.tt/2KzBYba

]]> 原文: https://ift.tt/2Kp8QY8
RSS Feed

机器知心

IFTTT

学完了在线课程?如何开启深度学习论文的阅读模式

在一个 Quora 问答《I want to pursue machine learning as a career but not sure if I am qualified. How can I test myself?》中,问到如何测试某人是否达到了从事机器学习职业的标准。吴恩达说(只要不断学习)任何人都可胜任机器学习的工作。他说,在完成一些机器学习课程之后,「进一步的学习,阅读研究论文。最好是尝试复现研究论文中的结果。」


OpenAI 的研究员 Dario Amodei 说,「为了测试自己是否适合从事 AI 安全或者机器学习的工作,只要尝试快速实现大量模型。从最近的论文中找到一个机器学习模型,实现它,快速的让它能跑起来。」


这表明,读研究论文,对个人进一步了解这个领域极为重要。


每个月都有大量的论文被发表,任何认真学习 ML 的人,都不能只是依靠别人把最新研究分解过的教程类文章或者课程。新的、独创性的研究都是在读文章的时候做出来的。机器学习领域的研究节奏从未如此快过,你能跟上节奏的唯一方法就是养成阅读论文的习惯。


在此文章中,我会尝试给出阅读论文的可行性建议。最后,我会尝试分解一篇论文,从此开始读论文。


如何读论文


arXiv.org


arXiv 是预印本论文的网上发布平台,研究者一般在著名的学术期刊或会议论文发表之前就先将其发布到该平台。


那么为什么先发到 arXiv 上呢?其实事实证明,研究和实际撰写论文并不是终点,将论文提交给某个学术期刊发表是非常漫长的过程。在一篇论文提交给学术期刊后,同行审议的过程一般需要数月甚至一年多的时间。而现在它对于机器学习领域来说是不可取的,因为这个领域发展从未如此迅速。


所以,研究者在预印本资源库 arXiv 上发表论文以快速传播他们的研究,并获得快速反馈。


Arxiv Sanity Preserver


让研究者能轻松的预印论文自然很不错。但对于阅读的人而言,预印论文的数量太多了,对于新手而言肯定不适合(个人观点,想试试也无妨)。


所以,我要向你推荐 Arxiv Sanity Preserver:https://ift.tt/1P6Rxl9




Arxiv Sanity Preserver 由 Andrej Karpathy 建立。


arXiv Sanity 对于 arXiv 而言,正如 Twitter 的 newsfeed 对于 Twitter 的作用。在 newsfeed 中,你能看到最有趣的符合你个人口味的推文,arXiv Sanity 也一样。它能让你基于研究趋势、你的过去喜好以及你关注的人的喜好来排序论文。




Machine Learning-Reddit 上的 WAYR thread

WAYR 是 What Are You Reading 的简写。这是一个 Reddit 的子网站(subreddit)Machine Learning 上的一个 thread,其中人们在上面推送近期阅读的机器学习论文,并讨论他们发现的有趣结果。

如我所说,每周在 arXiv 上发表的机器学习领域的研究论文数量非常多。这意味着几乎不可能让个人每周都把它们全部读完,同时还能兼顾其它事情。同时,也不是所有论文都值得一读的。

因此,你需要把精力集中在最有潜力的论文上,而以上介绍的 thread 就是我推荐的一种方式。

Newsletter、Newsletter、Newsletter!

Newsletter 是我个人最喜欢的追踪 AI 最新进展的资源。你只需要订阅它们,就可以定期在电子邮件里收到推送。然后,你就能了解到这周里和 AI 相关的最有趣的新闻、文章和研究论文。

我已经订阅了以下 Newsletter:

  • Import AI(Jack Clark):这是我的最爱,因为除了推送以上我介绍的那些信息之外,它还拥有称为「Tech Tales」的特色栏目。这个栏目包含新的 AI 相关的基于上周时间的短篇科幻小说。

  • 地址:https://jack-clark.net/

  • Machine Learning(Sam DeBrule):他也以相同的名字在 Medium 上发表文章,其中有一些非常有趣的文章,推荐阅读。

  • 地址:https://ift.tt/2jOkQC9

  • Nathan.ai(Nathan Benaich):以上两个快讯是周报形式,而这个是季刊形式。因此,你能在每三个月收到一封长邮件,其中总结了过去三个月里最有趣的领域进展。

  • 地址:https://ift.tt/1XRovvH

  • The Wild Week(Denny Britz):这个快讯的展示很简洁,但在过去两个月里似乎没那么活跃了。总之我也在这里提一下,万一 Denny 又继续更新了呢。

  • 地址:https://ift.tt/2oQmtl8

在 Twitter 上关注「AI 大牛」

另一种追踪领域最前沿的方式是在 Twitter 上关注著名的研究者和研究机构的账号。以下是我的关注列表:

  • Michael Nielsen

  • Andrej Karpathy

  • Francois Chollet

  • Yann LeCun

  • Chris Olah

  • Jack Clark

  • Ian Goodfellow

  • Jeff Dean

  • OpenAI

但我要怎么「开始」?

没错,这才是更加迫切的问题。

首先,确保你理解机器学习的基础,例如回归和其它算法;理解深度学习的基础,一般神经网络、反向传播、正则化,以及一些进阶内容,例如卷积网络(CNN)、循环网络(RNN)和长短期记忆网络(LSTM)的工作方式。我不认为阅读研究论文是理清基础的好办法,有很多其它资源可以用来打好基础。比如吴恩达的《Machine Learning》、《Deep Learning》在线课程,周志华的《机器学习》(西瓜书)、Bengio 等著的《深度学习》教材。

学好基础后,你应该从阅读引入那些基本概念、思想的研究论文开始。从而你可以聚焦于习惯研究论文的形式,不要太担心对第一篇研究论文的真正理解,你已经对那些概念很熟悉了。

我推荐从 AlexNet 这篇论文开始。

论文地址:https://ift.tt/2mltHxK

为什么推荐这一篇?看看下图:

我们可以看到,计算机视觉模式识别Computer Vision and Patter Recognition)的论文发表数从 2012 年开始暴涨,而这一切都源于 AlexNet 这篇论文。

AlexNet 的作者是 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton,论文标题为《ImageNet Classification with Deep Convolutional Networks》。这篇论文被认为是该领域中影响力最大的论文。它介绍了研究者如何使用称为 AlexNet 的卷积神经网络赢得了 2012 年的 ImageNet 大规模视觉识别挑战赛(ILSVRC)的冠军。

让计算机观察和识别目标是计算机科学最早期的研究目标之一。ILSVRC 就像是计算机视觉的奥林匹克,其中参赛者(计算机算法)需要准确识别图像属于 1000 个类别的哪一个。而且,在 2012 年,AlexNet 在这项竞赛中远远超越了竞争对手:

它获得了 15.3% 的 top-5 准确率,第二名仅获得 26.2% 的 top-5 准确率

毋庸置疑,整个计算机视觉社区都非常令人兴奋,该领域的研究正前所未有地向前加速。人们开始意识到深度神经网络的强大力量,你也可以在该领域尝试获得更多的成果。只要你们了解一些卷积网络的基础,那么掌握 AlexNet 论文的内容就会很简单,它们将会给你带来更多的知识与力量。

完成这一篇论文后,你可以尝试其它与 CNN 相关的开创性论文,也可以转而了解其它如 RNN、LSTM 和 GAN 等流行的架构。

当然目前还有很多渠道获取重要的研究论文,例如在 GitHub 中就有非常多的论文集合。

论文集合:https://ift.tt/2IGH8Ba

此外,最后还有一个非常优秀的平台 Distill,是一个现代的交互、视觉化期刊平台,面向现有以及新的机器学习研究成果。Distill 使用了现代用户界面,注重对研究的理解与诠释。

Distill 地址:https://distill.pub (https://distill.pub/)

虽然 Distill 更新非常慢,但它的每一篇都非常经典。

原文链接:https://ift.tt/2Kf0J09

]]> 原文: https://ift.tt/2KylDa8
RSS Feed

机器知心

IFTTT

JavaScript 之父联手近万名开发者集体讨伐 Oracle:给 JavaScript 一条活路吧!- InfoQ 每周精要848期

「每周精要」 NO. 848 2024/09/21 头条 HEADLINE JavaScript 之父联手近万名开发者集体讨伐 Oracle:给 JavaScript 一条活路吧! 精选 SELECTED C++ 发布革命性提案 "借鉴"Rust...