精华内容
下载资源
问答
  • 图像识别的深度残差学习

    万次阅读 2017-06-21 15:19:55
    在现有基础,想要进一步训练更层次的神经网络是非常困难的。我们提出了一种减轻网络训练负担的残差学习框架,这种网络比以前使用过的网络本质上层次更。我们明确地将这层作为输入层相关的学习残差函数,而不是...

    图像识别的深度残差学习

    联合编译:陈圳、章敏、Blake 

    摘要

    在现有基础下,想要进一步训练更深层次的神经网络是非常困难的。我们提出了一种减轻网络训练负担的残差学习框架,这种网络比以前使用过的网络本质上层次更深。我们明确地将这层作为输入层相关的学习残差函数,而不是学习未知的函数。同时,我们提供了全面实验数据,这些数据证明残差网络更容易优化,并且可以从深度增加中大大提高精度。我们在ImageNet数据集用152 层--比VGG网络深8倍的深度来评估残差网络,但它仍具有较低的复杂度。在ImageNet测试集中,这些残差网络整体达到了3.57%的误差。该结果在2015年大规模视觉识别挑战赛分类任务中赢得了第一。此外,我们还用了100到1000层深度分析了的CIFAR-10。

    对于大部分视觉识别任务,深度表示是非常重要的。仅由于极深的表示,在COCO对象检查数据时,我们就得到了近28%相关的改进。深度剩余网络是我们提交给ILSVRC和COCO2015竞赛的基础,而且在ImageNet检测任务,ImageNet定位,COCO检测和COCO分割等领域赢我们获得了第一。

    简介

    深度卷积神经网络在图像分类方面,引发了一系列突破。通过改变叠层的数量(深度),深度网络自然整合低/中/高水平的功能,终端到终端多层方式的分层器,和特征的“水平”都变得更加丰富。最近的证据表明网络深度是至关重要的,在挑战性的ImageNet数据集中领先结果的团队,都利用了“很深”模型,该深度为十六至三十。而且很多特殊的视觉识别任务也从深度模型中大大受益。

    受到深度意义的驱使,出现了一个问题:学习更好的网络是否和叠加层数一样简单?解决该问题的一大障碍是臭名昭著的梯度爆发与消失问题,它从一开始就阻碍了收敛。然而,这个问题很大程度上被归一的初始化和中心归一层解决了,它确保几十层的网络开始用反向传播收敛随机梯度下降(SGD)。

    当更深的网络能够开始融合时,暴露出了降级问题:随着网络深度的增加,精准度开始饱和(这并不令人吃惊)然后迅速下降。预料之外的是,在[11,42]中报道并且经过我们实验彻底验,证精准度的下降并不是由过度拟合造成的,而且在相配的深度模式中增加更多的层数,会造成更高的训练误差。图1展示了一个经典的实例。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图1.20层和56层“平原”网络时CIFAR-10的训练误差(左)和测试误差(右),深度网络有着更高的训练误差,并且造成测试误差。在图4也展示了图像网中类似的现象。

    精准度的下降表明了,并不是所有的系统都同样容易优化。我们考虑一个较浅的体系结构和与它深度相似增加了更多层的体系结构。其中一个解决方案是建立更深层的模型:添加身份映射层,另一层则是从学习更浅的模型中拷贝的。这个构造解决方案的存在,表示应该制造一个更深层模型,该模型产生的训练误差,低于与它深度相似更浅的模型。但实验显示我们目前无法找到一个与构造解决方案一样好,或者更好的解决方案(或者无法再可行的时间内完成)。

    在本论文中,介绍了一个深层次的残差学习框架来解决精准度下降问题。我们明确地让这些层适合残差映射,而不是寄希望于每一个堆叠层直接适合一个所需的底层映射。形式上,把H(x)作为所需的基本映射,让堆叠的非线性层适合另一个映射F(x):=H(x)-x。那么原映射便转化成:F(x)+x。我们假设优化剩余的映射,比优化原来未引用的映射更容易。如果身份映射是最佳的,那么将剩余的映射推为零,就比用一堆非线性层来适应身份映射更容易。

    公式F(x)+x可以通过“快捷连接”前馈神经网络实现(图2)。快捷连接是那些跳过中的一层或更多层。在我们的情景中,快捷连接简单的执行身份映射,并将它们的输出添加到叠加层的输出(图2)。身份快捷连接添加既不产生额外的参数,也会增加不计算的复杂度。通过反向传播的SGD,整个网络仍然可以被训练成终端到终端,并且可以在没有修改器的情况下很容易的使用公共图书馆(例如Caffe)。

    我们在ImageNet上进行了综合性的实验展示精准度下降问题,并评估我们的方法。我们发现:(1)特别深的残差网很容易优化,但深度增加时,对应的“平面”网(即简单的堆栈层)表现出更高的训练误差。(2)深度残差网络能够在大大增加深度中获得高精准度, 产生的结果本质上优于以前的网络。

    相似的现象同样出现在了CIFAR-10集中,这表明了优化的难度,以及我们方法影响的不仅仅是类似于一个特定的数据集。我们在这个超过100层数据集中提出了成功的训练模型,并探讨了超过1000层的模型。

    在ImageNet分层数据集中,通过极深的残差网络,我们得到了非常好的结果。152层的残差网络在ImageNet中是最深层的网络,同时相比于VGG网络仍然具有较低的复杂性。我们的集成在ImageNet测试集中有3.57%排前5的误差,并且在2015ILSVRC分类竞争中取得第一名。这种极深的陈述在其它识别任务方面也有出色的泛化性能,并带领我们进一步赢得了

    第一的位置:在ILSVRC和COCO2015竞赛中的,mageNet检测,ImageNet定位,COCO检测,和COCO分割方面。这有力的证据表明,剩余的学习的原则是通用的,我们期望它适用于其它的视觉和非视觉问题。

    2.相关工作

    残差表示。在图像识别中,VLAD是一个象征,通过关于字典的残差向量进行编程,而且费舍尔向量可以被制定成VLAD的概率版本。它们两个对于图像检索与分类都是有效的浅表示。

    对于矢量化,编码残差向量比编码原始向量更有效。

    在低层次视觉和计算机图形学中,为了解决偏微分方程(PDEs),它们广泛的使用多重网格法,将系统重构成多尺度的子问题,每个子问题负责更粗和更细规模之间的残差解答。多重网格的一种替代方法是分层的基础预处理,它依赖于表示两个尺度之间的残差向量的变量。

    这些收敛解法比不知道残差性质的标准解法快得多。这些方法表明,一个好的方法或预处理可以简化优化。

    快捷连接。导致快捷连接的方法和理论已经被研究了很长时间。训练多层感知器早期的实践是从网络的输入到输出添加一个线性层。在[44,24]中,一些中间层直接连接到辅助分类器,来解决梯度的爆发与消失问题。论文的[39,38,31,47]提出用于中心层响应,梯度和传播误差了的方法,该方法通过快捷连接实现。在[ 44 ],一个“开始”层是由一个快捷的分支和一些更深的分支组成。

    并发我们的工作,“highway network”提出了门控功能的快捷连接。这些门依赖于数据和已有的参数,而我们的身份快捷键无需参数。当一个门控的快捷方式“关闭”(接近零时),highway network中的层代表非残差函数。相反的是,我们的想法总是学习残差函数,我们的身份快捷方式从来没有关闭,而且所有的信息通过时,总是需要学习额外的残差函数。此外,highway network并没有表现出,精准度并没有随着深度的大大增加而提高(例如,超过100层)。

    3深度残差学习

    3.1残差学习

    将H(X)假设为由几个堆叠层匹配的(不一定是整个网)基础映射,用x表示这些第一层的输入。假设多元非线性层能逼近复杂的函数2,也就相当于假设它们可以逼近残差函数,例如H(x)-x(假设输入和输出在同一规模)。因此我们非常明确的让这些层近似于残差函数,而并非期待堆叠层近似于H(x)。所以原函数变成了:F(x)+x。尽管两种形式都能逼近期望函数,但它的学习难易度可能不同。

    新的构思源于反常的精准度下降问题。如我们在简介中讨论的一样,如果添加的层可以被构造为身份映射,那么一个更深度模型的训练误差,不应大于与其相应的更浅的模型训练误差。精准度下降问题表明,求解器在通过多个非线性层近似于身份映射方面有困难。随着残差学习重构,如果身份映射是最佳的方法,那么求解器可以简单地驱动多个非线性层的权重趋向于零,以便逼近身份映射。

    在现实情况中,身份映射不可能是最优的,但我们的方法可能有助于事先处理该问题。如果最优函数与趋近于零映射相比更趋近于身份函数,那么与学习一个新函数相比,求解器更容易找到关于身份映射的干扰。我们通过实验展示(图7)所学到的剩余函数一般有小的响应,这表明身份映射提供了合理的预处理。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图2残差学习:一个构建模块

    3.2。快捷方式的身份映射

    我们对每一个堆叠层都采用残差学习,一个构建模块如图2所示。正式地说,本文构建模块定义为:

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    其中x和y是考虑到的层的输入和输出向量。函数F(x,{Wi})代表学习的残差函数。如图2所示有两个层,F=W2ϭ(W1x)中ϭ表示ReLU,而且消除了简化符号的偏见。F+x的操作是是由快捷连接和增加的元素智能进行的。在增加之后我门采用了第二非线性特性(例如ϭ(y)如图2)。

    公式(1)中介绍的快捷连接,没有额外的参数和复杂的计算。这不仅在实践中有吸引力,它在对比平原和残差网络方面也同样重要。有着相同数量的参数,深度,宽度和计算成本时(除了可以忽略不计的元素智能的添加),我们可以对平原和残差网络进行简单的对比。

    在式1中x和F的大小必须相同。如果不同(例如改变输入和输出渠道)我们可以通过快捷连接线性投影Ws来匹配维度:

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    我们也可以在式1中使用一个正方形矩阵Ws。但我们会通过实验表明,身份映射足以

    用于解决精准度下降问题并且是非常合算的,因此只有在匹配维度时,才使用Ws。

    残差函数F的形式是灵活的,本文的实验涉及一个有两层或三层(图5)或者更多层的函数F。但如果F仅仅只有单层,式1就类似于线性层:y=W1x+x,因为们没有观察的优势。

    我们还注意到,虽然上面的符号为了简单起见是关于完全连接的层,但它们适用于卷积层。函数F(x,{Wi})可以代表多个卷积层。增加的元素智能在两个特征映射上通过通道对通道的方式进行。

    3.3网络构架

    我们测试了不同的平原和残差网络,并且观察到一致现象。为了给论述提供实例,我们描述了两个ImageNet模型如下。

    平原网络。我们的平原基线(图3,中间)主要受启于VGG网理论(图3,左)。卷积层主要有3X3的过滤器并遵循两个简单的设计规则:(i)对于相同的输出特征映射大小,图层有相同数量的滤波器;(ii)如果特征映射的大小被减半,过滤器的数量增加一倍,以保持每层的时间复杂度。我们通过有一个跨度为2的卷积层,直接进行下采样。网络以一个全局平均池层和有着最温和的1000路全连接层结尾。图3(中间)中加权层的总数是34。值得注意的是,我们的模型比VGG网(图3,左)有着更少的过滤器和更低的复杂度。我们的34层基线有3.6亿个触发器(乘加),这只有vgg-19的18%(19.6亿触发器)。

    残差网络。基于上面的平原网络,我们插入快捷连接(图3,右),将网络转变成其对应的残差版本。当输入,输出大小相同的时候(实心线的捷径在图3),身份快捷键(公式(1))可以直接使用。当尺寸增加时(虚线快捷键在图3)中,我们考虑了两个选项:(A)快捷方式仍然执行身份映射,用额外的零条目填充以便增加尺寸。该选项不会引入额外的参数;(B)公式(2)中的投影捷径,用于匹配尺寸(完成1×1卷积)。当快捷键以两个大小在功能地图上进行时,这两个选项进行了2跨度。

    3.4 实施

    我们遵循了[21,41]中的方法,实施ImageNet。图像的大小与随着[ 256,480 中]它的短边随机采样而调整,以便增大规模[ 41 ]。 224x224的结果,是从减去每个像素的平均值的图像或其水平翻转图像,中随机抽样得到的。在[ 21 ]中增强了标准色。在每一个卷积之后和激活之前,我们采用了BN算法。我们如[ 13]中一样初始化权重 ,并从零开始训练所有的平原和残差网。我们使用了一个迷你的SGD大小为256。学习率从0.1开始,并在误差稳定时分为10份,而且我们训练该模型高达60x104次迭代。我们使用重量衰减为0.0001和0.9。而不是使用传统[ 16 ]中的惯例。

    测试中,为了对比我们采用标准的10-crop试验。为达到最佳效果,我们采用完全卷积形式如[ 41,13 ]中所示,并且平均多尺度大小的成绩(短侧图像的大小调整在{ 224,256,384,480,640 }中)。

    4实验

    4.1 Imagenet分级

    我们在ImageNet 2012分类数据集中评估了我们的方法,该数据集由1000个级组成。我们在128万训练图像中训练模型,并且在50K验证图像进行了评估。测试中心报道,我们也在100k的测试图像中获得了最终结果。评估误差率在第1和第5之间。

    平面网络。我们首先评估了18层和34层平原网。34层的平原网在图3(中间)中。18层平面网形式是类似。详细架构见表1。

    表2显示的结果表明,较深的34层平面网具有比浅18层平面网更高的验证错误。为了揭示原因,在图4(左)中,我们比较了培训过程中的训练和验证错误。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图3。展示ImageNet网络架构。左:vgg-19模型(19.6亿触发器)作为参考。中间:一个有34个参数层的普通网络(3.6亿个触发器)。右:有34g1参数层的残差网络(3.6亿触发器)。快捷键点增加尺寸。表1显示更多细节和其他变型。

    我们观察到退化问题------在整个训练过程中34层的普通网络错误更多,尽管18层普通网络的解空间是34层的一个子空间。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表1. ImageNet的构架。

    我们会争论优化困难不太可能是由梯度消失问题导致的。所有的普通网络都是通过BN训练的,而这也保证了向前传播的信号有非零差异性。此外我们也要保证反向传播梯度在BN中表现出良好。所以无论是向前或是向后信号都消失了。事实上,34层的普通网络准确率更高(如表3),而这在一定的程度上也暗示了解算器。我们猜想深度普通网络可能会有指数较低的收敛率能降低训练错误。此类优化问题会在未来进行研究。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图4. ImageNet训练。细曲线指代训练错误,粗曲线指代核心部分的合理错误。左边:18层和34层的普通网络。右边:18层和34层的残差网络。在此处,残差网络和普通网络的参数一样。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表2.在ImageNet确认部分的最低错误率。

    残差网络。接下来我们会18层和34层的残差网络。残差网络的框架基准线与普通网络一样,处理每一对3×3过滤器有增加快捷连接(图3,右)。在第一个比较中(表2和图4,右),所有用于增加维度的捷径和补零都用使用身份定位。所以残差网络与普通网络的参数并无区别。

    我们从表2和图4中得出3个重大发现。第一,与残差学习模式相反的情况----34层网络比18层网络表现要好(2.8%左右)。更重要的是,34层残差网络比18层残差网络错误率更低,并且使用于验证数据。而这也表明退化问题能在此设置中解决且我们能从更深层次中获得准确率。

    第二,与普通网络相比,34层残差网络将最低错误率降低了3.5%(如图2所示)。这一比较证实了残差网络在深度学习系统中的优越性。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表3.在ImageNet确认部分的错误率。VGG-16是基于我们自己的测试。ResNet-50/101/152基于B方案的,且只使用规划增加维度。

     获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表4. ImageNet确认部分使用单一模式的错误率。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表5.整体错误率。

    最后,我们注意到18层的普通和残差网络是比较准确的(表2),但是18层的残差网络汇集更快(图4右VS左)。当网络没有“过度重合”(如18层),当前的SGD求解程序依然可以在普通网络找到好的解决方法。在此情况下,在早期阶段通过提供更快的聚合能缓解ResNet的优化问题。

    恒等式VS规划捷径。我们发现无参数和身份捷径能帮助训练。接下来我们会研究规划捷径(等式2)。在表3中我们比较了三种选择:(A)使用补零捷径增加维度,且所有的维度都无参数(如表2和图4,右);(B)使用规划捷径增加维度,而其他的捷径都是恒等式;(C)所有的捷径都是规划的。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图5.深层残差网络的在ImageNet的功能F。

    表3显示三个方案就普通网络比较而言都表现得更出色。B比A表现更好。我们会怀疑这是因为A中的零填充维度不具备残差学习模式。C比B表现稍好,我们将此归功于许多(13个)设计捷径引入许多其他因素。但只有在A/B/C之间的少量不同能证明规划捷径对于解决退化问题十分关键。所以为控制记忆、时间复杂性,及模式规模,在本文的剩余部分我们不使用方案C。恒等式捷径对于不增加瓶颈的结构复杂性至关重要。

    更深层次的瓶颈结构

    接下来我们会描述ImageNet更深层次网络。因为鉴于我们时间有限,要改善区块,将其变成瓶颈设计。对于每一个残留功能F,我们使用3层瓶颈模式,而不是2层。这3层分别是1×1,3×3和1×1的回旋。在此范围中,所有的1×1层都用于先减少后增加(存储)量度,让3×3层的输入或是输出量度更小。图5展示了一个例子,这两个设计的时间复杂性都一样。

    无参数恒等式捷径对于瓶颈的结构尤为重要。如果图5(右)中的恒等式捷径被规划所取代,我们能发现时间复杂性和模式大小都会翻倍,因为捷径是连接两个高量度端。

    50层的残留网络

    在34层网络结构中,我们用3层的瓶颈区块取代2层的区块,这时34层结构就会变成50层残差结构。我们使用B方案增加量度。而这一模式有38亿FLOPs。

    101层和152层的残差网络模式。我们通过使用3层的瓶颈区块构建101层和152层的残差模式。让人惊讶的是,尽管深度显著增加,152层的残差模式(113亿FLOPs)复杂性较VGG-16/19网络(153/196亿FLOPs)更小。

    34层的残差网络较50/101/152层更准确。我们不关注退化问题,因此更在意随着深度增加而增加的准确度。

    先进方法之间的比较。在表4中,我们比较了之前单一模式所取得的最好结果。而我们34层残差网络模式准确度最高。152层ResNet模式错误率最低4.49%。而这也超过之前所有模式。我们结合6种不同深度的模式形成一种合集模式,这一模在测试中的错误率只有3.57%,让我们在ILSVRC2015中拔得头筹。

    4.2 CIFAR-10和分析

    我们在CIFAR-10的数据集中进行多场研究,此数据集包括50k的训练图像和10K测试图像,且这些图像都被分成了10类。我们将会展示在训练集中训练和在测试集中评估的试验。我们关注的焦点在于深层网络的行为,不在于取得最好结果,所以我们有意使用简单结构。

    所有普通/残差结构都必须遵循图3 的流程。网络输入32×32的图像,但像素有所缩小。第一层是3×3回旋。接下来是6n层的3×3回旋,且特征神经网络有2n层左右大小。过滤层的数量分别是{16,32,64}。二次抽样是由跨度在2左右的回旋执行的。网络结构终止于10个完全连接层。这里总共有6n+2的权重层。下表总结了其结构构成。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    但当它们都运用捷径连接时,它们就是通过3×3层连接起来。在此数据集中,我们在所有的情况下都使用恒等式捷径,所以我们残留模式有与简单模式完全一样的深度,宽度和参数数量。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图6 基于CIFAR的错误分类

    我们使用了权重衰减为0.0001和冲量单位为0.9,并且在采用权值初始化和BN方法但并未输出。这些模式是在128有2个GPUs大小规模的数据中进行一次训练。我们开始的学习率是0.1,且由32k和48k迭代次数分为10类,且在64k迭代次数结束训练。我们根据简单的训练数据增大:每一边填充4像素,最后从填充的数据图像中随机选取32×32的结果。对于测试,我们只评价最初的32×32图像。

    我们比较当n={3,5,7}时,20层,32层,44层和56层的网络结构。图6(左)展示的是普通网络的表现。普通网络随着深度的增加错误率也会变高。这与ImageNet和MNIST是类似的,这说明优化问题是一个基础问题。

    图6向我们展示了ResNet的表现。与ImageNet类似,ResNet旨在随着深度的增加,克服优化问题和提高准确率。

    我们会更加深入探讨当n=18,110层的残差网络结构。在此情况下,我们发现初始学习率为0.1对于会聚还是太高了。所以我们使用0.01的初始学习率再进行训练,直到错误率低于80%时,才使用0.1的初始值进行训练。接下来的程序如之前所述进行。110层的网络汇聚的十分好(图6中)。它比其他深度或是简单模式的参数更少,如FitNet和Highway,但是结果却是十分的优秀。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图7.在CIFAR-10上的层对于标准背离的表现。其反应是3×3层的结构输出,在BN之后和非线性之前。上:是层的原始排列。下:是按照降序的方法排列其反应。

    反应分析。CIFAR-10上的层对于标准背离的表现。其反应是3×3层的结构输出,在BN之后和非线性之前。对于残差网络,这一分析反应的是残差函数的影响。图7显示ResNet通常比原始网络反应更小。这一结果证实了我们最初的想法残差函数比非残差函数更接近于零。更深层次的ResNet反应幅度更小,如图7ResNet-20,56,和100所示。随着层数的增加,ResNet的每一次会倾向于少改变信号。

    超过100层的探索。我们深入探索超过1000层的深度模式。我们假设n=200,此时的网络结果超过1202层。我们的模式没有任何优化问题,103的网络结构能实现训练错误率<0.1%(图6右)。其测试错误率也十分的低(7.93%,表6)。

    但是在深度模式中还是有问题。

    1202层的测试结果比110层的要差,尽管两者的训练错误率差不多。我们认为是过度拟合造成的。

    1202层网络系统对于如此小是数据集或许是太大了。为在此数据集中,取得较好结果有必要做重要调整如Maxout激发函数和Dropout激发函数。在本文中,我们并未使用Maxout激发函数和Dropout激发函数,仅仅只通过深度和简单结构做一些修改。但是联合更大的调整也许能取得更好的结果,这一课题我们会在未来进行研究。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图7使用更快的R-CNN标准,在PASCAL VOC 2007/2012测试集上进行目标检测。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    图8使用更快的R-CNN标准,在COCO验证集中进行目标测试。

    4.3 在PASCAL和MSCOCO上的目标检测

    我们的模式在其他的识别任务中有较好的表现。表7和表8显示了基于PASCAL VOC 2007,2012和COCO上,目标测试结果。我们采用更快的R-CNN作为测试方法。我们对于使用ResNet取代VGG-16方法所获得的进步很感兴趣。使用两种模式所得到的方法是一样的,所以所获结果的差别在于网络结构。更令人惊讶的是,在极具挑战性的COCO数据集中,我们取得了6.0%的进步,相较而言是有28%左右的进步。这全归功于所学得的表现。

    基于深度残差网络,我们赢得了ILSVRC&COCO2015比赛的冠军:图像识别,图像定位和公共图像识别和公共图像分类。详情请见附录。

    附录

    A.对象检测基准

    在这一节中来介绍我们基于Faster R-CNN系统的检测方法。这个模型最初是用来进行Imagenet分类,然后被用于对象检测数据领域。在ILSVRC&COCO 2015检测竞赛中我们实现了ResNet-50/101。

    与参考文献[32]中的VGG-16不一样,ResNet没有隐藏的fc层。我们将“卷曲特性图的神经网络”理念运用到这个问题中。将全图卷曲特性进行预算(如表1所示)。考虑到这些层级和VGG-16中的13卷曲层类似,这样ResNet和VGG-16就都有同样的跨步了(16像素)。最终的分类层被两个同级层取代。

    对于BN层的使用,在训练之前我们队ImageNet的每个训练层都进行了BN统计。然后BN层在转移到物体检测时被修正了。通过这些训练,BN层变得更加线性活跃了,而且BN统计数据不会随着转移而变化。这些对BN层的修正主要是为了减少在Faster-CNN训练过程中的内存消耗。

    PASCAL VOC

    针对PASCAL VOC 2007数据组,我们和参考文献[7,32]中使用的方法一样,用来训练Faster R-CNN的超参数和参考文献[32]中提到的一样,表7展示的是训练结果。ResNet-101在VGG-16的基础上提高了mAP超过3%的成绩。

    MS COCO

    MS COCO数据组包括80种对象类别。我们使用80k图像来针对训练组,40k图像来针对预判组。RPN步态和Fast R-CNN步态都使用0.001学习率的240k迭代来进行训练,然后在0.0001学习率的80k迭代下训练。

    表8展示的是MS COCO的训练结果,这些结果证明深度网络的确能提高识别和定位的效率。

    B. 对象检测提升

    为了追求完美,我们针对竞赛提升了性能。这些提升都是基于深度特性的,而且对残差学习有益。

    MS COCO

    边界改良

    我们的边界改良沿用的是参考文献[6]中的迭代定位。我们将300个新的预判与原始的300个预判结合起来。边界改良大概提升了mAP2个百分点的成绩(如表9所示)。


    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文

    表9

    多范围测试

    以上所有的结果都是在单一范围内进行训练、测试的,如参考文献[32]中提到的那样。在我们目前的训练中,我们沿用了参考文献[33]中的方法进行了多范围测试,但由于时间限制我们还没有进行多范围训练。另外,我们特别针对Fast R-CNN步态进行了多范围测试。通过模型训练,对图像椎体进行卷曲特性计算,它的短边长在s ∈ {200,400,600,800,1000}。

    我们选取了椎体的两个相邻范围,结果证明多范围测试能提升mAP2个百分点的成绩。

    使用确认数据

    接下来我们使用80k+40k trainval数据组来进行训练,20k测试数据组来进行评估,单模型结果如表9所示。

    组合

    在Faster R-CNN系统里这些是分别用来学习地区方案和对象分类,如果组合在一起能把两者加速。表9展示了我们基于3中网络组合的结果,在测试开发组中mAP取得了59.0%和37.4%的成绩。

    这是COCO 2015检测任务中的最佳成绩。

    PASCAL VOC

    基于上面的模型我们对PASCAL VOC数据组进行了测试,根据之前的经验我们进行了少许调整,结果是我们在PASCAL VOC 2007上取得了了85.6%mAP的成绩(表10),在PASCAL VOC 2012上取得了83.8%mAP成绩(表11)。在PASCAL VOC 2012上获得的成绩比目前最佳成绩还要好10%。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表10

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表11

    ImageNet检测

    ImageNet检测任务包括200个对象类别。我们使用的对象检测算法和在MS COCO中的相同(表9)。在DET测试组上,单一ResNet-101模型取得了58.9%的成绩,3个组合模型取得了62.1%的成绩(表12)。

    这是ILSVRC 2015中ImageNet检测任务的最佳成绩。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表12

    C.ImageNet定位

    Imagenet定位(LOC)任务要求对物体分类再进行定位。根据参考文献[40,41],假设首先用图像分类器来预测图像类别,然后根据预测类别使用定位算法开始进行定位。我们采用了“类回归策略”对每一类进行边界框定。我们提前对Imagnet分类进行训练然后将它们调整到定位功能。为了训练这个网络我们提供了1000类Imagnet训练组。

    我们的定位算法基于在RPN框架上进行小部分修改。和参考文献[32]的未知分类不一样,我们的RPN定位是每一类的形式。和参考文献[32]中的一样,我们的边界改良在每个位置都有平移不变的“锚定”边界。

    在3.4中的ImageNet分类训练中,我们随机使用224x224切割来进行数据扩增。为了测试,将整个网络都应用于完全卷积的图像中。

    表13对定位结果进行了对比,VGG的文章指出使用基础真实类别会找出33.1%的中心切割错误。当使用ResNet-101来预测类别,前5的定位错误结果是14.1%(表4)。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表13

    上述结果仅是基于Faster R-CNN(参考文献[32])的RPN上。人们可以使用检测网络(Fast R-CNN)在Faster R-CNN中来改善结果。但我们注意到,在此数据集,一个图像通常包含单个支配对象,并且该区域相互高度重叠。受此启发,在我们目前的实验中,我们使用原来的R-CNN 来取代现有的Fast R-CNN。

    我们的R-CNN实现情况如下。我们将每个训练好的的单类别RPN用于训练图像上,来预测边界限制情况和基准事实类别。为了测试,RPN在每个预测类别生成了最高到200层的方法,并且使用R-CNN网络来提高这些方法成绩和它的边界位置。

    这种方法将前5的定位错误降低到10.6%(表13)。这是我们的单模型在验证组上的成绩。将分类和定位组合起来使用,我们将前5的定位错误率降低到了9.0%。这个数字明显比ILSVRC14的结果要好(表14),还减少了64%的相关错误。

    获奖无数的深度残差学习,清华学霸的又一次No.1 | CVPR2016 最佳论文


    表14

    这个结果在ILSVRC2015中的ImageNet定位任务上取得第一名的成绩。

    via CVPR

    PS : 本文由雷锋网(公众号:雷锋网)独家编译,未经许可拒绝转载!

    雷锋网原创文章,未经授权禁止转载。详情见转载须知

    展开全文
  • 计算机图形之矩阵变换的深度理解

    万次阅读 多人点赞 2018-07-27 19:37:16
    对于图形来说,矩阵计算不可避免,既直观又方便。而如果线性代数学的不透彻的话,那么基本上是做不到应用的,这里推荐看一下3Blue1Brown的线性代数的视频,可以对矩阵计算有深刻的认识。 之后就是应用阶段,我们...

    对于图形学来说,矩阵计算不可避免,既直观又方便。而如果线性代数学的不透彻的话,那么基本上是做不到应用的,这里推荐看一下3Blue1Brown的线性代数的视频,可以对矩阵计算有深刻的认识。

    之后就是应用阶段,我们这个阶段就是使用我们的矩阵来完成空间中点或向量的各种变换。

    重点是理解矩阵的含义:矩阵其实是一种坐标系的转换

    1. 理解矩阵的几何功能:
      1. 矩阵是一种线性变换(线段变换后仍是线段,并且原点不会改变)
      2. 矩阵是一种映射,映射可以是一对一,也可以是一对多
      3. 矩阵是一种空间变换,每一种矩阵都是有本身的几何意义,而不是单纯的数字组合
    2. 理解矩阵的形式含义(矩阵在左,向量在右):
      1. 方阵满秩,是进行当前空间的坐标变换,不会进行维度的升高和降低
      2. M×N(M>N),这种矩阵是一种升维矩阵,几何意义如3×2,就如同把一个平面进行一个方向维度的延展(当秩为2时),变成一个三维的空间,当然之前空间中的点(x,y)在提升维度的过程中在三维空间中还是一个平面分布,只是多了一个值全相等的第三个维度的值而已
      3. M×N(M<N),这是一种降维矩阵,几何意义如2×3(当秩为2时),是把三维的向量进行一个维度的压缩,挤压成一个平面,这个时候当然会出现很多个点压缩到一个二维坐标的情况
      4. 方阵不满秩,其实和之前是一样的,不满秩就说明这个维度是虚的,没什么用,如果秩是一的话就是挤压成一条线。

    一.平移矩阵

    当我们理解了之前的种种逻辑之后,我们终于进入到了我们的就事论事环节,首先就是我们的平移矩阵。

    我们当然有理由先讨论二维的情况。

    1.1二维平移矩阵

    形式:平移矩阵的形式是什么样的呢。

    • 一定是一个满秩的矩阵,因为我们并不会进行维度的变换。
    • 坐标轴的方向和长度是不变的,因为矩阵运算的本质是改变的参考系的X,Y。

    寻找矩阵

    那么什么样的矩阵能够使得一个点进行平移操作呢,因为平移操作并不会改变参考系XY的方向和单位向量的长度,所以实际上,在当前的维度中,我们并不可能做到,为什么呢?因为我们之前说过,矩阵的线性变换并不会改变原点的位置,矩阵是默认原点就是(0,0),在矩阵中填充任何的值都没办法改变这个约定熟成的决定。那么怎么做到呢,我们利用更高一维的矩阵,也就是三维矩阵进行操作,其实就是在更高的维度当中,解决我们的原点平移问题。

    在这里,需要说明的是,如果我们使用的是左矩阵右向量的形式,那么构成我们全新的坐标系的向量应当是矩阵中的列。我们在增加了一个新的维度,(dx,dy,1),dx,dy就是我们要平移的数量,而增加一个1,是因为要保持秩是3,毕竟我们是从更高一层的维度来转移我们的坐标原点的。

    从几何意义来说,平移矩阵其实就是增加一个并不正交的Z轴(dx,dy,1)来进行一个坐标轴的重新定义,继而将坐标转换位置。

    这里需要注意的是,点V=(x,y,1),并不是再是我们二维坐标上的点,而是以P的列向量为基所构成的三维空间上的点,只是说我们把二维的点变为V=(x,y,1)然后经过对应矩阵P的线性变换,最终得到平移点的位置。

    这个过程可能有些难以理解,但是又是那么的巧妙和精确。我们所构造的矩阵P,是通过新空间中基的列向量来构成的,其中的列向量中每一个坐标值对应的数值,其实都是以我们原有的基x=(1,0),y=(0,1)来决定的。P这个矩阵,是一种线性的变换,是把用它坐标系中表示的点的位置坐标,来映射到原先我们确定它的时用的基的空间里。

    这其实就是一种投影运算,对V的每个坐标值进行投影。

    而p的逆矩阵,其实就是把我们的坐标映射到P的坐标中的运算。

    1.2三维平移矩阵

    当我们理解了二维的平移矩阵,那么,其实三维的矩阵也是一样的原理

    二.旋转矩阵

    旋转矩阵的基础含义就是绕原点的旋转。

    2.1二维旋转矩阵

    形式:旋转矩阵的形式是什么样的呢。

    • 一定是满秩的,因为没有进行维度变换
    • 坐标轴的长度应该是不变的,因为没有进行放缩操作,但是角度应该是要变的。

    寻找矩阵:

    假设我们有点v=(x,y),坐标轴是标准的坐标轴,角度是为是与x轴正方向的夹角。

    首先,我们已经知道了一些知识,那就是寻找新的坐标轴的基,且基的长度仍是1.那么

    形成的矩阵

    这就是我们所需要的旋转矩阵,这里其实我们并

    2.2三维旋转矩阵

    对于三维来说是差不多的,不过因为旋转轴的不同,旋转矩阵的形状也是不同的

    可以结构成这么一段话,对于标准坐标系而言,要将一个点V移动到V1(在这里V和V1的表示都是标准坐标系下的),那么用新基构成的矩阵P变换V,得到V1。这是因为,V在标准坐标系中的坐标和V1在P的坐标系下的坐标是一样的。


    所以,矩阵变换的并不是V,而是V1。因为P的作用是把本身的坐标里的点变成标准坐标系的点的坐标,所以变换的其实不是标准坐标系里的V,而是自身的V1,因为他们两者的值是相同的,会有很强的误导性,但是并不是说他们空间中的绝对位置是相同的。仅仅因为参考系的变换,导致了他们的值相同。

    三.放缩矩阵

    放缩矩阵其实就很简单,图形的放缩其实就是我们本身的坐标轴的基的放缩

    四.投影矩阵

    将点v,投影到一个平面A,投影方向是B的法向量n。

    形式:正交投影矩阵的形式是什么样的呢。

    • 应该是高维矩阵,因为其中包含平移操作
    • 应该是一个不满秩的矩阵,毕竟我们的坐标都放到了一个面上
    • 因为是正交投影,所以我们压缩的,其实就是沿着B法向量上的点

    寻找矩阵:我们设V=(vx,vy,vz),A有一点a=(ax,ay,az),面的法向量N=(Nx,Ny,Nz)

    1. 首先确立平面上的坐标系,通过点A,和法向量N建立坐标系(X*,Y*,Z*).
    2. 其中Z是法向量N的方向,转向是没有关系的。
    3. 如果这么就完了并不能完成平移操作,仅仅完成的是坐标系方向的转向,位置上还需要平移
    4. 所以需要原点到平面的距离矢量L
    5. 建立最后的投影矩阵

    注意:我们这里所需要的,是应该求逆矩阵,当我们所需要的是投影坐标表示而不是在原先坐标系表示的时候

    四.自由矩阵变换

    熟练的掌握以上技巧就完美进行坐标运算

     

    展开全文
  • 相比之,第一种更“宽”更“浅”,第二种更“窄”更“”。对于每个输入,两种方式都可能产生8种结果(第一种:8*1;第二种:2*4),但是第二种方式的神经元总数更少。 因此,在神经元数目相同的情况,“深度”...

    为什么是深度而不是宽度?

    先说说我个人的理解,如下图所示,圆圈表示神经元,有两种方式。第一种,只有一层神经层,有8个神经元;第二种方式,有两层神经层,分别有2个和4个神经元。相比之下,第一种更“宽”更“浅”,第二种更“窄”更“深”。对于每个输入,两种方式都可能产生8种结果(第一种:8*1;第二种:2*4),但是第二种方式的神经元总数更少。

    因此,在神经元数目相同的情况下,“深度”学习出现的情况会更多,效果也会更好、更全面。
    这里写图片描述

    多说一句,在操作系统课程中,内存管理中多级页表技术来节省空间也与“深度”这个问题原理相同。

    下面这张图是《深度学习入门教程》中看到的,是对上面这个问题的解释,这里与逻辑门类比,与上面的解释本质是一样的。

    这里写图片描述

    展开全文
  • Java学习之拷贝浅拷贝及对象拷贝的两种思路

    万次阅读 多人点赞 2017-12-18 22:22:01
    Java语言中的拷贝、浅拷贝以及对象拷贝

    I. Java之Clone

    0. 背景

    对象拷贝,是一个非常基础的内容了,为什么会单独的把这个领出来讲解,主要是先前遇到了一个非常有意思的场景

    有一个任务,需要解析类xml标记语言,然后生成document对象,之后将会有一系列针对document对象的操作

    通过实际的测试,发现生成Document对象是比较耗时的一个操作,再加上这个任务场景中,需要解析的xml文档是固定的几个,那么一个可以优化的思路就是能不能缓存住创建后的Document对象,在实际使用的时候clone一份出来

    1. 内容说明

    看到了上面的应用背景,自然而言的就会想到深拷贝了,本篇博文则主要内容如下

    • 介绍下两种拷贝方式的区别
    • 深拷贝的辅助工具类
    • 如何自定义实现对象拷贝

    II. 深拷贝和浅拷贝

    0. 定义说明

    深拷贝

    相当于创建了一个新的对象,只是这个对象的所有内容,都和被拷贝的对象一模一样而已,即两者的修改是隔离的,相互之间没有影响

    浅拷贝

    也是创建了一个对象,但是这个对象的某些内容(比如A)依然是被拷贝对象的,即通过这两个对象中任意一个修改A,两个对象的A都会受到影响

    看到上面两个简单的说明,那么问题来了

    • 浅拷贝中,是所有的内容公用呢?还是某些内容公用?
    • 从隔离来将,都不希望出现浅拷贝这种方式了,太容易出错了,那么两种拷贝方式的应用场景是怎样的?

    1. 浅拷贝

    一般来说,浅拷贝方式需要实现Cloneable接口,下面结合一个实例,来看下浅拷贝中哪些是独立的,哪些是公用的

    @Data
    public class ShallowClone implements Cloneable {
    
        private String name;
    
        private int age;
    
        private List<String> books;
    
    
        public ShallowClone clone() {
            ShallowClone clone = null;
            try {
                clone = (ShallowClone) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return clone;
        }
    
    
        public static void main(String[] args) {
            ShallowClone shallowClone = new ShallowClone();
            shallowClone.setName("SourceName");
            shallowClone.setAge(28);
            List<String> list = new ArrayList<>();
            list.add("java");
            list.add("c++");
            shallowClone.setBooks(list);
    
    
            ShallowClone cloneObj = shallowClone.clone();
    
    
            // 判断两个对象是否为同一个对象(即是否是新创建了一个实例)
            System.out.println(shallowClone == cloneObj);
    
            // 修改一个对象的内容是否会影响另一个对象
            shallowClone.setName("newName");
            shallowClone.setAge(20);
            shallowClone.getBooks().add("javascript");
            System.out.println("source: " + shallowClone.toString() + "\nclone:" + cloneObj.toString());
    
            shallowClone.setBooks(Arrays.asList("hello"));
            System.out.println("source: " + shallowClone.toString() + "\nclone:" + cloneObj.toString());
        }
    }

    输出结果:

    false
    source: ShallowClone(name=newName, age=20, books=[java, c++, javascript])
    clone:ShallowClone(name=SourceName, age=28, books=[java, c++, javascript])
    source: ShallowClone(name=newName, age=20, books=[hello])
    clone:ShallowClone(name=SourceName, age=28, books=[java, c++, javascript])

    结果分析:

    • 拷贝后获取的是一个独立的对象,和原对象拥有不同的内存地址
    • 基本元素类型,两者是隔离的(虽然上面只给出了int,String)
      • 基本元素类型包括:
      • int, Integer, long, Long, char, Charset, byte,Byte, boolean, Boolean, float,Float, double, Double, String
    • 非基本数据类型(如基本容器,其他对象等),只是拷贝了一份引用出去了,实际指向的依然是同一份

    其实,浅拷贝有个非常简单的理解方式:

    浅拷贝的整个过程就是,创建一个新的对象,然后新对象的每个值都是由原对象的值,通过 = 进行赋值

    这个怎么理解呢?

    上面的流程拆解就是:

    - Object clone = new Object();
    - clone.a = source.a
    - clone.b = source.b
    - ...

    那么=赋值有什么特点呢?

    基本数据类型是值赋值;非基本的就是引用赋值

    2. 深拷贝

    深拷贝,就是要创建一个全新的对象,新的对象内部所有的成员也都是全新的,只是初始化的值已经由被拷贝的对象确定了而已

    那么上面的实例改成深拷贝应该是怎样的呢?

    可以加上这么一个方法

    public ShallowClone deepClone() {
        ShallowClone clone = new ShallowClone();
        clone.name = this.name;
        clone.age = this.age;
        if (this.books != null) {
            clone.books = new ArrayList<>(this.books);
        }
        return clone;
    }
    
    
    // 简单改一下测试case
    public static void main(String[] args) {
        ShallowClone shallowClone = new ShallowClone();
        shallowClone.setName("SourceName");
        shallowClone.setAge(new Integer(1280));
        List<String> list = new ArrayList<>();
        list.add("java");
        list.add("c++");
        shallowClone.setBooks(list);
    
    
        ShallowClone cloneObj = shallowClone.deepClone();
    
    
        // 判断两个对象是否为同一个对象(即是否是新创建了一个实例)
        System.out.println(shallowClone == cloneObj);
    
        // 修改一个对象的内容是否会影响另一个对象
        shallowClone.setName("newName");
        shallowClone.setAge(2000);
        shallowClone.getBooks().add("javascript");
        System.out.println("source: " + shallowClone.toString() + "\nclone:" + cloneObj.toString());
    
    
        shallowClone.setBooks(Arrays.asList("hello"));
        System.out.println("source: " + shallowClone.toString() + "\nclone:" + cloneObj.toString());
    }

    输出结果为:

    false
    source: ShallowClone(name=newName, age=2000, books=[java, c++, javascript])
    clone:ShallowClone(name=SourceName, age=1280, books=[java, c++])
    source: ShallowClone(name=newName, age=2000, books=[hello])
    clone:ShallowClone(name=SourceName, age=1280, books=[java, c++])

    结果分析:

    • 深拷贝独立的对象
    • 拷贝后对象的内容,与原对象的内容完全没关系,都是独立的

    简单来说,深拷贝是需要自己来实现的,对于基本类型可以直接赋值,而对于对象、容器、数组来讲,需要创建一个新的出来,然后重新赋值

    3. 应用场景区分

    深拷贝的用途我们很容易可以想见,某个复杂对象创建比较消耗资源的时候,就可以缓存一个蓝本,后续的操作都是针对深clone后的对象,这样就不会出现混乱的情况了

    那么浅拷贝呢?感觉留着是一个坑,一个人修改了这个对象的值,结果发现对另一个人造成了影响,真不是坑爹么?

    假设又这么一个通知对象长下面这样

    private String notifyUser;
    
    // xxx
    
    private List<String> notifyRules;

    我们现在随机挑选了一千个人,同时发送通知消息,所以需要创建一千个上面的对象,这些对象中呢,除了notifyUser不同,其他的都一样

    在发送之前,突然发现要临时新增一条通知信息,如果是浅拷贝的话,只用在任意一个通知对象的notifyRules中添加一调消息,那么这一千个对象的通知消息都会变成最新的了;而如果你是用深拷贝,那么苦逼的得遍历这一千个对象,每个都加一条消息了


    III. 对象拷贝工具

    上面说到,浅拷贝,需要实现Clonebale接口,深拷贝一般需要自己来实现,那么我现在拿到一个对象A,它自己没有提供深拷贝接口,我们除了主动一条一条的帮它实现之外,有什么辅助工具可用么?

    对象拷贝区别与clone,它可以支持两个不同对象之间实现内容拷贝

    Apache的两个版本:(反射机制)

    org.apache.commons.beanutils.PropertyUtils.copyProperties(Object dest, Object orig)
    
    
    org.apache.commons.beanutils.BeanUtils#cloneBean

    Spring版本:(反射机制)

    org.springframework.beans.BeanUtils.copyProperties(Object source, Object target, Class editable, String[] ignoreProperties)

    cglib版本:(使用动态代理,效率高)

    net.sf.cglib.beans.BeanCopier.copy(Object paramObject1, Object paramObject2, Converter paramConverter)

    从上面的几个有名的工具类来看,提供了两种使用者姿势,一个是反射,一个是动态代理,下面分别来看两种思路

    1. 借助反射实现对象拷贝

    通过反射的方式实现对象拷贝的思路还是比较清晰的,先通过反射获取对象的所有属性,然后修改可访问级别,然后赋值;再获取继承的父类的属性,同样利用反射进行赋值

    上面的几个开源工具,内部实现封装得比较好,所以直接贴源码可能不太容易一眼就能看出反射方式的原理,所以简单的实现了一个, 仅提供思路

    public static void copy(Object source, Object dest) throws Exception {
        Class destClz = dest.getClass();
    
        // 获取目标的所有成员
        Field[] destFields = destClz.getDeclaredFields();
        Object value;
        for (Field field : destFields) { // 遍历所有的成员,并赋值
            // 获取value值
            value = getVal(field.getName(), source);
    
            field.setAccessible(true);
            field.set(dest, value);
        }
    }
    
    
    private static Object getVal(String name, Object obj) throws Exception {
        try {
            // 优先获取obj中同名的成员变量
            Field field = obj.getClass().getDeclaredField(name);
            field.setAccessible(true);
            return field.get(obj);
        } catch (NoSuchFieldException e) {
            // 表示没有同名的变量
        }
    
        // 获取对应的 getXxx() 或者 isXxx() 方法
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        String methodName = "get" + name;
        String methodName2 = "is" + name;
        Method[] methods = obj.getClass().getMethods();
        for (Method method : methods) {
            // 只获取无参的方法
            if (method.getParameterCount() > 0) {
                continue;
            }
    
            if (method.getName().equals(methodName)
                    || method.getName().equals(methodName2)) {
                return method.invoke(obj);
            }
        }
    
        return null;
    }

    上面的实现步骤还是非常清晰的,首先是找同名的属性,然后利用反射获取对应的值

    Field field = obj.getClass().getDeclaredField(name);
    field.setAccessible(true);
    return field.get(obj);

    如果找不到,则找getXXX, isXXX来获取

    2. 代理的方式实现对象拷贝

    Cglib的BeanCopier就是通过代理的方式实现拷贝,性能优于反射的方式,特别是在大量的数据拷贝时,比较明显

    代理,我们知道可以区分为静态代理和动态代理,简单来讲就是你要操作对象A,但是你不直接去操作A,而是找一个中转porxyA, 让它来帮你操作对象A

    那么这种技术是如何使用在对象拷贝的呢?

    我们知道,效率最高的对象拷贝方式就是Getter/Setter方法了,前面说的代理的含义指我们不直接操作,而是找个中间商来赚差价,那么方案就出来了

    将原SourceA拷贝到目标DestB

    • 创建一个代理 copyProxy
    • 在代理中,依次调用 SourceA的get方法获取属性值,然后调用DestB的set方法进行赋值

    实际上BeanCopier的思路大致如上,具体的方案当然就不太一样了, 简单看了一下实现逻辑,挺有意思的一块,先留个坑,后面单独开个博文补上

    说明

    从实现原理和通过简单的测试,发现BeanCopier是扫描原对象的getXXX方法,然后赋值给同名的 setXXX 方法,也就是说,如果这个对象中某个属性没有get/set方法,那么就无法赋值成功了


    IV. 小结

    1. 深拷贝和浅拷贝

    深拷贝

    相当于创建了一个新的对象,只是这个对象的所有内容,都和被拷贝的对象一模一样而已,即两者的修改是隔离的,相互之间没有影响
    - 完全独立

    浅拷贝

    也是创建了一个对象,但是这个对象的某些内容(比如A)依然是被拷贝对象的,即通过这两个对象中任意一个修改A,两个对象的A都会受到影响

    • 等同与新创建一个对象,然后使用=,将原对象的属性赋值给新对象的属性
    • 需要实现Cloneable接口

    2. 对象拷贝的两种方法

    通过反射方式实现对象拷贝

    主要原理就是通过反射获取所有的属性,然后反射更改属性的内容

    通过代理实现对象拷贝

    将原SourceA拷贝到目标DestB

    创建一个代理 copyProxy
    在代理中,依次调用 SourceA的get方法获取属性值,然后调用DestB的set方法进行赋值

    V. 其他

    声明

    尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见解不全,如有问题,欢迎批评指正

    扫描关注,java分享

    QrCode

    展开全文
  • 可能很多刚接触 Spring Boot 的小伙伴都会有这样的疑问,Spring Boot 要怎么,要不要先 SSM?今天结合自己的经验,和大家说一说我的看法,也顺便推荐几个 Spring Boot 学习资料。 1.Spring Boot 是什么 我们刚...
  • Unity Shader-后处理:景

    万次阅读 多人点赞 2016-10-21 00:33:21
    一直是我最喜欢的效果之一,最早接触CE3的时候,发现CE引擎默认就支持景的效果,当时感觉这个效果特别酷炫,如今投身于Unity的怀抱中,准备用Unity实现以下传说中的景效果。 所谓景,是摄影的一个专业术语...
  • 适合普通大学生的前端开发学习路线

    万次阅读 多人点赞 2021-04-12 15:22:41
    假如你没有明确的目标,或许可以按照我说的学习路线来学习一波,我写的每一份学习路线,不会很全面,因为我认为,东西列的太多,反而不利于新手的学习,所以我列举的,都是比较必要的知识,当你把这些知识了的时候...
  • 区块链中的密码

    千次阅读 2018-11-06 11:48:58
    而量子计算机的出现也变越来越近,2018 年 7 月初潘建伟教授的团队首次实现 18 个量子比特的纠缠,再次刷新量子纠缠世界记录。 本课程内容将讲解密码在区块链中的应用、量子力学基础知识以及量子计算是如...
  • 6.4 深度Q学习

    2020-03-18 15:43:47
    由于神经网络具有强大的表达能力,能够自动寻找特征,所以采用神经网络有潜力比传统人工特征强大多。最近基于深度Q网络的深度强化学习算法有了重大的进展,在目前学术界有非常大的影响力。 当同时出现异策、自益和...
  • 9.4 深度Q学习

    2020-03-18 15:43:47
    假设强化学习智能体与环境交互时,以元组(tuple)形式(s,a,r,s')将经验存储在一个回放缓冲器中,那么就可以对回放缓冲器进行小批量采样,来训练网络。一开始,回放缓冲器中的经验数据是随机生成的。
  • 8.3 深度增强学习

    2020-03-18 15:43:47
    8.3 深度增强学习 增强学习(Reinforcement Learning,...若要增强学习真正能够在自动驾驶的场景应用,那么还需要进行很多改进。 [1] 参考教程网址:http://mnemstudio.org/path-finding-q-learning-tutorial.htm。
  • 由李沐、Aston Zhang 等人合力打造的《动手深度学习》正式上线,免费供大家阅读。这是一本面向中文读者的能运行、可讨论的深度学习教科书 李沐的这本《动手深度学习》也是使用 MXNet 框架写成的。但是很多...
  • 密码的学习心得

    千次阅读 2020-11-07 13:57:51
    学习了网络安全的课程后感触很,尤其对密码。密码充满了神秘性,让我对它产生了浓厚的兴趣和好奇。这个学期以来,学习到了很多东西,到了各种密码体制,让我知道了密码体制在我们的生活中发挥着巨大的作用。...
  • 一年前入职格灵瞳时,陈乐就觉得有些不太对劲,“没有加班费,项目提成算进年终奖”,熬到年尾,上司的一句话比北京冬天的风还刺骨,“今年的提成,没了。” 让她寒心的事情还在后头。只要找 HR 问工资条和报销...
  • 每天一点图像处理--位深度

    千次阅读 2018-01-09 10:23:44
    当图像是灰度图时,像素点的范围是0-255 那么用8位表示就足够了,因此位深度是8;如果是彩色图就是24位(RGB) 但有时,我们看到Mat格式的图像,位深度是16等 因为Mat是按照矩阵来存储图像的,一个点的取值范围...
  • 2)热衷于 ACM 3)尝试 Unix、Linux 环境编程 4)醉心于网络编程和多线程编程,对 TCP/IP、HTTP 等网络协议有很的理解 好了,今天的分享就到这吧。吹个牛逼,二哥影响力已经扩散到两岸三地了,正在服兵役的小...
  • 本文介绍了图像深度、像素深度、位的概念,图像深度是指表示图像的像素中有多少位用于表示颜色,像素深度是指图像中一个像素占用的位数,位是指像素的通道占用的位数。像素深度大于等于图像深度,等于所有通道位...
  • 9.6 深度双Q学习

    2020-03-18 15:43:47
    从图中可以看到:网络A学习在给定状态预测实际Q值,网络B用于计算目标Q值。网络A通过最小化关于目标Q值和预测Q值的损失函数不断提升Q值的预测能力。本质上Q值是连续的,因此合理的损失函数有均方误差、平均绝对...
  • 他们当中有很多人都想把自己的专业跳到编程专业,原因是多方面的,其中包括了他们对自动化的畏惧,而这个问题在40岁以上的人群中变更具针对性。编程是一项很花脑力、精力的高压工作,在40岁后才开始学习编程,...
  • 我为什么放弃java学习Kotlin?

    万次阅读 多人点赞 2017-05-19 22:21:52
    妈妈在也不用担心我的NullPointerExpection了,因为一般情况,如果你项目有空指针,是在运行后报错,而用了kotlin,编译直接会不通过,不过缺少了NullPointerExpection,编码是不是少了很多乐趣呢?Lambda表达式不...
  • 由上图可见,随着模型越,错误率逐步下降。这个其实很好理解,可能有人会问,模型加深带来了模型参数量的提升,有助于bias降低。通过增加训练数据和各种正则,可以降低variance。从而降低整体的误差。 1.2 加深和...
  • 在有限的时间里能学习的东西也是有限的,那么在我们决定去学习之前,就经常会遇到深度和广度的选择问题,尤其是目前所掌握的技能已经足够应对现有工作的时候,纵向深入还是横向拓宽技术,现有工作似乎并不能直接体现...
  • GitChat 作者:MyStery 其实学习是一件很私人的事情,每个人都应该有一套自己的学习方式,而...所以,读这篇分享的时候,请以一种【这点是不是对我有帮助】的心态来阅读,而不是【原来我要这样才能的好】的心态。
  • 因为在很多场景,回馈 r 并不是每时每刻都能获取的(比如在 Labyrinth 中吃到苹果才能1分),所以让神经网络能够预测回馈值会使其具有更好的表达能力。在 UNREAL 算法中,使用历史连续多帧的图像输入来预测...
  • java学习之道 --- 如何学习java?

    万次阅读 多人点赞 2017-04-02 00:27:58
    当然,大部分人都是在问我怎么学习,自己不知道该怎么了,让我出个主意啥的,真正讨论问题的并且加了我好友的,大概只有两个吧。因为总有人陆陆续续的加我并且问我怎么学习啊啥的,一个一个回复
  • 今天小编就来安利一本中文资料——中文版本的《动手深度学习》。 内容简介  本书向希望了解深度学习,特别是对实际使用深度学习感兴趣的学习者。本书并不要求你有任何深度学习或者机器学习的背景知识,我们将...
  • 计算机专业 学习路线

    万次阅读 多人点赞 2019-07-07 20:22:46
    希望写一篇学习路线,让自己剩下的一年有点规划,同时也希望给一些对计算机有兴趣的却不知道从何入手的同志一些借鉴。 (本人只是学生,技术经验有限且已后端javaEE学习为主,此篇仅是本人...
  • 大学四年嵌入式学习心得体会

    万次阅读 多人点赞 2018-08-14 00:44:15
    我所在学校是普通的本科...在此写的这篇 Chat 主要是针对大学学习嵌入式的童鞋的,嵌入式不容易,牵扯的内容,知识点太多,你是否不知道如何下手呢?是否在迷茫为什么我的这么吃力呢?下面作为过来人的我为您解...
  • 目录 ...10. 将你所的知识与你已经知道的东西联系起来 11. 大声读出来 12. 定期休息 13.在学习结束时奖励自己 14. 专注于过程,而不是结果 15. 每天喝八杯水 16. 每周至少锻炼三次 17. 每...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 799,025
精华内容 319,610
关键字:

学下得那么深