一篇文章看懂物体检测的发展脉络 转

转 https://zhuanlan.zhihu.com/p/28399320

  • 第一,什么是物体检测,如何去评价一个物体里系统的好坏。
  • 第二,物体检测整个的框架是怎么样的?它一般包含了图像的分类和物体检测的定位。
  • 第三,介绍物体检测的历史发展,从传统的人工设计的一些图像特征加上分类器到现在的深度学习。

  • What’s Computer Vision

    介绍物体检测之前,我们首先要知道什么是计算机视觉。计算机视觉是计算机科学的一个分支领域,旨在构造智能算法和程序,来“观察”这个世界。比如说,一个机器人手里拿着一个东西,它可以知道这个手上拿着的是一个魔方。这个问题对人而言十分简单,但是对于计算机却不是那么容易的。因为计算机拿到的信息,是一系列的二进制数据,它描述了每个像素点的颜色强度,而计算机本身是比较困难将这些信息抽象成为一种比较高层语意的表达,去对应现实生活中的名词概念。

    所以,我们通常所说的“观察”(see),实际上是已经包含了对视觉信息的加工,以及和真实世界的关系映射。那么在计算机视觉领域,我们怎样去定义“观察”(see)这个概念,和我们人类的理解一致呢?这里我们把它分成三个层次:

    第一个层次,也是最经典的解释,1982年David Marr(著名的生物神经学家,计算机视觉理论的鼻祖),他总结的“To know what is where by looking”。中文意思就是知道有什么东西,在哪里,这就是一个最基本的“see”这个概念。它对应计算机视觉里面几类基本问题:一个是做图像识别,即解决是什么(what),另一个是物体定位,即在哪里(where)。这里还需要提一下“图像语意分割”这个概念,它是一个像素级别的物体识别,即每个像素点都要判断它的类别。它和检测的区别是,物体检测是一个物体级别的,他只需要一个框,去框住物体的位置,而通常分割是比检测要更难的问题。

    再进一个层次,除了要知道图像像素/二维平面内每一个像素代表的信息以外,我们可能还需要知道这个物体在这个真实世界当中的3D信息。举个例子,给定一张卧室的照片,以及一些相机参数和环境的一些假设,我们需要知道床在这个三维空间的真实的位置,包括它的长宽高。在这个层次中,一个重要的课题就是我们经常提到的SLAM,即同时定位和建图,这个方向在机器人领域有十分重要的作用。

    第三个层次则是更高一层的视觉问题,比如说给定图片或者视频,我们需要去知晓这个场景正在发生什么,如果是一个更高级的系统,我们甚至希望计算机能够根据图像或者视频,我们回答一些问题。这就是前两年非常火的视觉问答(Visual question answering)方向。

    现在我们回归物体检测。 物体检测是视觉感知的第一步,也是计算机视觉的一个重要分支。物体检测的目标,就是用框去标出物体的位置,并给出物体的类别。物体检测和图像分类不一样,检测侧重于物体的搜索,而且物体检测的目标必须要有固定的形状和轮廓。图像分类可以是任意的目标,这个目标可能是物体,也可能是一些属性或者场景。

    Object Detection Evaluation

    如果给定一个物体检测系统,我们要怎么样去评价它的好坏?物体检测的输出,就是一系列的框,加上其属于某一类别的置信度得分。测评的时候我们需要把他们和标注的框(ground truth)进行比配。好的检测框,应该和标注框有非常大的重叠率,同时又不能对同一个物体重复检测。我们把那些和标注框重叠率很高的定义为true positive, 把那些重叠率低(甚至没有重叠)和重复检测的框,定义为false positive。

    我们还需要定义两个描述指标:precision 和recall。Precision是true positive 数量除以 true positive和false positive的比值,即所有的检测结果中,正确的比例。 Recall 则是所有truepositive的个数和标注框个数的比值,即所有的目标中,被系统检测出来的比例。

    接下来,我们可以把检测的结果根据置信度进行排序, 设一个阈值,然后去计算这个情况下的precision和recall。我们设置不同的阈值,可以得到很多组precision 和recall。 如果我们把所有的precision 和recall 都画到一张图上,x轴代表recall, y轴代表precision,那么我们得到的图,就叫做PR(precision-recall) 曲线。我们可以用这条曲线在x轴上的积分,去描述物体检测的好坏,这个指标叫做Average precision (AP)。 AP值高,就说明系统在比较高recall的情况下,还能保持比较高的的precision. 现在一些成熟的人脸检测系统, AP都是在90%以上。如果我们直接拿标注去算AP, 那么肯定就是100%了。

    How to Detect an Object

    我们再讲讲如何去做物体检测?其实很简单,典型的一个思路就是,我去搜索所有可能的位置。然后再去对这些所有可能的位置进行分类,看看它是不是包含这个物体。所以,我们把这个问题分解成两个步骤,第一步是去找目标的位置,第二步就是去做一个置信度分类。

    搜索目标位置的方法,总体来讲可以分成两类两种:

    第一种是我把所有可能的位置和大小都列举一遍:我可以通过一个扫描窗口,从图像左上角开始,从左到右,从上到下,一直扫到到右下角,然后我们改变图像的大小,保持扫描窗口大小不变,继续扫。每一个位置和大小,我们都可以后续通过处理得到一个置信度得分,这就是最典型的图像模板匹配的例子。在经典的模板匹配的例子中,扫描窗口所用的模板就是一个图像块,置信度得分的计算方法是correlation。如果当前位置和模板越匹配,那么得分就越大。因此我们能够通过这种方式得到物体的位置。

    遍历所有可能的位置太耗时,我们能不能比较高效地通过一些启发式的方法,快速地得到一些可能会有物体的位置(region proposal)?这样的方法通常都叫region proposal method。这类方法可以是有监督的方法,也可以是无监督的方法。 Selective Search就是一个常用的无监督的region proposal method。它的原理是根据像素点的相似度,逐层合并,当他们合并之后,就可以得到区域的边界,再把这些区域转化成region proposal 的框。

    History of Recognition(Detection)

    有了目标的候选区域,我们怎样得到物体的类别分数呢?这就是一个分类问题。其实图像识别这个领域,已经有大半个世纪的历史了。图像识别,或者说模式识别,最早是在二十世纪60年代被提出来的。当时MIT的计算机教授,组织了一个面向本科生的两个月的Summer project。这个project 的目的是设计一个系统,能够智能识别场景里头的物体,并区分出类别。显然当时他们低估了这个问题的难度,结果可想而知。

    实际上,从1966年之后到现在,这个问题还并不能算完全解决。但在一定程度上,在深度学习出来之后,这个问题得到了很大程度上的解决。其实识别问题本身就不是一个容易的事情。为什么呢?首先,我们看到的这个物体的样子,只是它在某种背景下某一种光线条件下特定角度的投影的,换一个角度可能就是完全不同的样子。即使是同一个物体,例如人,它具有多种不同的姿态,所以外观也会不一样。例如他可能是躺着的,或者是站着的,形态都是不一样的。

    假设,我们事先知道了物体的三维形状,那么物体识别的难度就会小一些。这时候识别问题变成了一个匹配问题。我们可以事先构造物体的形状,然后去搜索可能的视角投影,跟待识别的图像进行匹配。如果找到最合适的匹配,就认为是识别成功了。在20世纪六十年代初到九十年代,大家都是尝试用这种方法去做。例如我们可以定义一些基本的几何形状,然后把物体表示为基本几何形状的组合,然后去匹配图像。

    但是这么做并不是很有效,为什么呢?首先很多物体很难用所谓的基本几何形状去描述它,特别是一些非刚体,比如动物。其次对于一类物体,它可能会有丰富的类内差异性,即使是同一个物体在不同的姿态下也不一样,难道我们要为每一种姿态都预先创建一个三维模型模板?最后,即使解决了之前的问题,如何才能准确地从图像中提取出这些几何形状呢?因此这个方法在当时虽然理论挺优美,但实施起来非常困难。

    到了九十年代之后,主流的方法是只从图像本身考虑,而不去管物体原来的三维形状。这类方法统一叫做appearance based techniques. 所谓appearance, 从模式识别的角度去描述的话,就是图像特征(feature),即对图像的一种抽象描述。有了图像特征,我们就可以在这个特征空间内做匹配,或者分类。一个最经典的例子,就是”Eigen Faces”方法,这也是90年代做人脸识别最重要的方法之一。它的主要思想是用PCA(主成分分析)去分解人脸数据集,得到特征向量,然后把每一张人脸图像表达为特征向量的组合。这些组合系数,就构成了对人脸图像的抽象描述,即特征。最后我们就可以用特征空间内样本的距离,来判断样本是否是属于同一个人脸:同一个人脸在样本空间内距离很小,不同脸之间距离比较大。

    然而这个方法还是存在很多问题,首先它需要我们对所有的图片进行对齐,像人脸图像,就要求每一幅图中五官基本在固定的位置。但是很多应用场景下,目标并不是像人脸那么规整,很难去做统一对齐,而且这种基于全局特征和简单欧式距离的检索方法,对复杂背景,遮挡,和几何变化等并不适用。

    到2000年之后,识别领域有了较大的发展。首先图像特征层面,人们设计了各种各样的图像特征,像SIFT,HOG,LBP等等,比起图像边缘和角点等简单的特征更加鲁棒。与此同时,机器学习方法的发展也为模式识别提供了各种强大的分类器,例如SVM,boosting等的方法。在此期间,出现了第一个真正具有实际应用价值的人脸检测: Viola and Jones提出的实时人脸检测。在它之前的一些方法要么效果不怎么好,速度比较快,或者是速度很慢,效果相当或者更好。所以作者当时在CVPR上拿着摄像头展示算法的实时demo,惊艳了全场。这个方法能够做到又快又好的关键,在于使用了简单的基于积分图像(integral-image)的图像特征,和级联分类器(cascaded classifier)。前者可以在常数的时间复杂度内计算任意区域的特征,使得特征提取变得十分快速;后者可以在一些简单的背景样本上做到提前终止(earlyrejection),极大降低了全图中所有图像块分类的计算量。

    另外一类方法,通常是人工精心设计的图像特征,配上很强的分类器,典型例子就是Dalaland Triggs 在2005年做的行人检测方面的工作。他们使用了HOG(一种基于图像梯度直方图的局部统计特征)作为图像特征,使用支持向量机(SVM)作为分类器,通过扫描窗口的形式去遍历图像所有的位置。

    后来人们还在对物体建模方面做了一些工作,旨在用更灵活的模型,而不是单一的模板去定义物体。其实这个思想也不是当时提出来的,早在1973年的时候,Fischler& Elschlager 就提出了一种基于部件的模型,名叫Pictorial Structure。它的核心思想有两点:

    1. 物体是由在特定相对位置的不同部件所组成。

    2.不同的物体实例中,部件的位置可以允许一定程度上的不同。我们通常又把这个模型叫做弹簧模型,因为部件之间的连接可以看作是用弹簧相连的,虽然大概位置是固定的,但是还是能够允许一定的形变自由度。

    如果我们把Pictorial Structure和上文提到的用作行人检测的HOG detector结合起来,是不是会更好?其实这个工作就是深度学习还没火起来之前,在物体检测领域鼎鼎大名的Deformable Part-based Model(DPM)。这个方法使用了整体模板加上多个部件的模板去描述一个物体,部件之间的位置可以发生变化。因此它对于复杂的物体都能够有比较好的表现。这个方法在PASCAL VOC数据集上统治了数年时间,通过引入更多更复杂的图像特征,把检测平均准确率从2007年的17%提升到2012年的41%。正是由于这个方法对这一数据集的贡献,他的作者之一Ross Girshick被PASCAL VOC数据集的组织授予了终生成就奖。

    Object Detection via Deep Learning

    我们开始第二部分,这一部分主要讲的是深度学习给物体检测领域带来的变化。

    首先,关于深度学习的概念,大家只需要记住,深度学习其实就是神经网络,是一种特征学习的方法。它能在图像识别,语音识别,自然语言处理等领域有非常好的效果,是因为这些领域内很多问题都可以拆解为两个步骤:特征提取加上模式分类。那么为什么在很多领域,深度学习能够大幅度超过传统的方法呢?本质还是“特征”二字。

    我们以图像分类为例,传统方法需要人为地根据场景和目标去设计合适的图像特征。例如当你需要去对物体的外观进行建模,你可能需要基于梯度的特征去描述轮廓。你还需要想办法对梯度信息进行筛选,量化,得到相对稳定的表达。而这些所有的工作,都需要有一定的领域知识去设计和调优。从PASCAL VOC数据集物体检测方法的发展来看,更好的特征对最终的效果起到决定性作用。

    然而特征学习正是深度学习所擅长的部分。它把相关场景和目标的特征学习,转变为网络结构的定义和参数的学习,从而免去领域专家去设计特征这一环节。我们不需要绞尽脑汁去为你的目标去设计合适的特征,你只需要把原始图片和标注提供给网络,定义好网络结构,他就可以从头到尾自动学习出多层次的特征表达和分类器。深度学习还具有非常好的可扩展性。

    你可以去设计很大的模型,利用服务器GPU在1000类的图像分类数据集上达到80%多的准确率,亦或针对一类数据例如人脸做检测,用很小的模型在手机上达到还不错的效果。得益于深度学习的可扩展性,当数据规模足够大的时候,我们通过增加模型容量和复杂度,可以比较容易地达到更好的效果,而很多传统的方法在大数据下的提升比较困难。

    深度学习这么厉害,它是一个全新的概念么?它跟二三十年前的神经网络是一回事么?这个问题我们需要辩证地区看待。首先,现在我们的深度学习实际上就是以前传统的神经网络的一个发展,它的一些基本元素并没有变。例如现在深度神经网络的学习方法仍然是反向传播(back-propagation),它是在1986年被第一次提出来。像现在非常流行的卷积神经网络(ConvNet),其实1998年的时候就已经被应用到字符识别系统了。

    现在得益于更快的计算能力,更多的数据,神经网络以从前无法想象的模型规模的形态,又重新火了起来。在二三十年前,神经网络一般就只有两三层,每一层最多几十到上百个神经元。而现在比较主流的网络结构,像GoogleNet,ResNet等,参数量在上百万个。能够训练这么大的除了计算和数据之外,近些年提出的一些神经网络的优化技巧也起到了决定性的作用。例如ReLU激活函数的应用,使得神经网络比之前用tanh, sigmoid 等激活函数的网络更容易训练,也更快收敛。另外,像一些更好的神经网络权重的初始化方法,以及一些其他的奇淫巧技(trick), 例如drop out等,和新的网络结构(ResNet等)也让神经网络的实用性和之前相比大大提高。

    有关神经网络的基础内容,我们就不在这里再细讲了。除了经典的denseconnected network, 大家还需要了解Convolution,和Pooling两个操作,这两个是现在的主流的神经网络中最常用也是做基本的单元。我们继续回到物体检测这个问题。大家都知道,如果是用神经网络做图像分类的话,我们就直接把图像送进去,就可以直接输出分类的结果了。那么物体检测要怎么做呢?回顾之前提到的物体检测的流程,物体检测无非就是搜索加分类。那么,我们能不能: 1. 用神经网络去替代传统的分类器?2. 能否把区域搜索也交给神经网络去做?3. 甚至直接利用神经网络通过输入图像去输出物体检测的结果?答案是肯定的。

    Detection as Region Proposal + Classification

    其实这三个问题就代表了现在用神经网络去做物体检测的三个基本的思路。假设我们直接把传统的基于人工设计的特种加上分类器的步骤,用卷积神经网络(CNN)去替代,其实就是大名鼎鼎的R-CNN。这个方法思路很简单,首先需要产生物体可能的位置的候选区域,然后把这个区域送给CNN去做分类和检测框回归。候选区域的产生可以使用任意方法,并不是这个方法所讨论的重点。R-CNN这个工作的贡献是用CNN去取代原来的特征提取和分类器。虽然R-CNN的方法很简单,而且它也不是2013年深度学习火了之后第一个尝试用CNN去解决物体检测的方法,但是它在当时是性能最好的方法:在PASCAL VOC检测数据集上,比传统最好的方法要好一大截。

    这个方法简单有效,原因在于:1. 把检测问题分解为候选区域搜索和分类两个子任务,可以分别去优化求解,简化了检测任务的学习难度。2.后面的分类可以使用在ImageNet等更大数据集上预训练的模型,提供非常丰富的图像特征和强大的分类器。之前也讨论过,图像特征对物体识别和检测起非常重要的作用。一般来说,特征越强大,那么分类的效果就越好。所以ImageNet训练的高精确度的分类模型,在R-CNN这个方法中,就是“巨人的肩膀”。

    R-CNN虽然很简单,但是速度太慢的问题严重制约了它的实用性。在论文的实验中,检测一张图像需要40多秒。这个时间还是使用当时最好的GPU的速度,如果使用CPU的话,速度还会慢上几十倍。这个时间开销主要还是消耗在用CNN作图像分类上。为了达到一个好的效果,一张图像所产生的候选区域可能有上千个。分类器则需要把这近千个候选区域都裁剪出来,统一缩放到224*224的大小再分别过一遍CNN。所以在这个方法中,每个区域的分类器的计算都是独立的。

    而实际上,同一张图片里头的检测候选区域可能是高度相关的,特别是当候选区域很多的时候,它们重叠的概率会非常大,所以很多计算应该是可以共享的。之前在给定检测候选区域之后,我们在原始图像上裁剪并缩放得到固定大小的输入图像送到分类器,我们能否直接在特征图上这么做呢?这样的话,特征计算可以被不同的候选区域共享,从而避免了对相同区域的特征重复提取。这个改进方法就叫做Fast R-CNN。

    我们可以通过简单的计算去分析改进之后的理想的加速比:假设两种方法使用的是同一个网络结构,原始图像的大小是600*1000,如果我们用R-CNN 的话, 2000个候选区域累计起来,输入像素个数是224*224*2000;如果改用Fast R-CNN,输入像素个数只有600*1000,相比之前大约能加速100倍!

    现在我们再看看实际运行中, Fast R-CNN的速度。同样是一张PASCAL VOC数据集的图片,检测网络只需要0.2秒,而之前R-CNN 要达到类似的准确率,需要40多秒!而现在候选区域提取反而成了瓶颈,通常要几百毫秒到数秒的时间,而且这一步的计算也是独立的。后来进一步改进的FasterR-CNN方法引入了一个叫Region Proposal Network(RPN)的结构,解决了这个问题。所谓RPN就是一个CNN,用来预测哪些地方可能有物体,并回归出物体的位置。而且RPN和分类网络共享了图像特征,进一步避免了重复计算。所以FasterR-CNN其实就是RPN加上Fast R-CNN,一整套系统可以端到端进行训练。FasterR-CNN相比于之前的FastR-CNN,检测时间由原来的2秒缩短到0.2秒,使得实时的检测系统成为可能。

    One-Shot Detection

    刚刚所说的这一系列方法,都是把检测问题分成了两步:先产生regionproposal,然后再去分类和回归。另一类方法则是希望神经网络能够一步到位,直接去定位物体。在Faster R-CNN中,RPN是一个弱分类器,它不需要定位和分类十分准确。如果我们要求RPN得到非常精确的结果,就可以去掉后续做分类和定位这一步骤了,整体流程变得更加简单。其实这类方法相比R-CNN系列的方法,更早的时候就有了。例如2013年的时候有一个叫OverFeat的方法就是这么做的,只不过因为性能不够好的原因没有火起来。更早之前,在二十世纪90年代的时候用CNN去做人脸识别的一些工作,其实也是类似的流程。这类方法虽然流程上简单,但是模型的学习难度相比于两个阶段的方法要更难一些,要取得足够好的效果,需要在网络设计和学习目标上下一些功夫。

    一步到位的方法现在的一些代表工作有YOLO, SSD。现在公司里用的DenseBox也是和SSD类似的一个方法,是我在2014年末还在百度实习的时候做的工作。时间上应该比SSD和YOLO都要早。这里我们就主要讲一下SSD,它在PASCAL VOC数据集上能够达到实时的检测速度,效果也和Faster R-CNN差不多。SSD可以看作是一个强化版的RPN,输出层的每一个像素都代表了一个检测框。和RPN一个输出层不同, SSD会有好几个输出层:比较浅的输出层的分辨率比较高,用来检测小一些的物体,比较深的层检测大的物体。最后再把所有层的检测结果合在一起作为最终的输出。

    这么做的也是有它的考虑:小物体在过深的层当中可能会因为上下文信息过多而丢失掉有效的表达,同时高层的特征的分辨率也比较低,不好处理一块区域内多个物体的情况。物体检测想要做得好的话,本质上还是需要去解决物体和模板的配准对齐(registration,alignment),包括位置上的和尺度上的对齐。R-CNN系列把配准对齐问题交给了regionproposal,而像SSD这种一步到位的检测器,就只能指望模型本身的感受野去做配准对齐。因此从方法上讨论的话,前者更容易学习,效果也会更好。

    Summary

    用CNN去解决检测问题的思路,基本就是这几个套路。其实还有一个方向我们并没有讨论,就是用神经网络去学习怎么做检测的后处理,例如非极大抑制(Non-Maximum Suppression)等。这类工作关注的人相对比较少,所以相关的工作也不多。不过在上面介绍的几个基本框架下,可以研究的细节还有很多,例如怎样去处理好物体的尺度问题,物体的形变问题,怎样用多任务学习来提高物体检测的性能,怎样去最好context和detail之前的平衡等等,我们就不在此做详细讨论了。

    总结一下,今天主要介绍了物体检测的领域,包括物体检测的概念,方法的评估。然后回顾了下图像识别和检测领域五十年来的发展。对于模式识别领域来说,深度学习是最好的提取图像特征的方式,除非硬件受限只能用传统的方法,不然就可以直接用现在最好的深度学习模型去做实验。这里面我们并没有讨论太多具体实现的细节,其实在物体检测领域,对细节的处理直接影响到模型最终的结果。如果你对有兴趣的话,还是建议大家在具体的数据集上去尝试,调参,慢慢地你就会有自己的理解。

    其实物体检测的方法框架,近二十年来并没有太大的变化。以前的方法为什么在当时没有成功呢?因为当时人们并没有那么多的数据和计算资源,所以像深度学习这种计算密集型的数据驱动的方法,在当时完全没有用武之地。而且当数据很小的时候,很多根据数据集的特点专门调优的方法,比起数据驱动的方法更有效。另外,当时的机器学习的工具并不像现在这么丰富和方便,即使有的话,很多做计算机视觉的人也还没学会去用。

    历史总是会交替发展的,作为一个好的研究人员,你需要去发现从前的一些好的工作。解决问题的具体办法不一定相同,但是人们解决问题的大致思路总是相似的。我们需要去弄明白为什么某些方法当时并没有起作用,哪些方法在现在还有应用的潜力,需要用现在的方法去重新审视它们。

    -End-

时间: 2024-10-13 12:26:03

一篇文章看懂物体检测的发展脉络 转的相关文章

(好文推荐)一篇文章看懂JavaScript作用域链

闭包和作用域链是JavaScript中比较重要的概念,首先,看看几段简单的代码. 代码1: 1 var name = "stephenchan"; 2 var age = 23; 3 function myFunc() { 4 alert(name); 5 var name = "endlesscode"; 6 alert(name); 7 alert(age); 8 alert(weight); 9 } 10 myFunc(); 11 myFunc(); 上述代码

一篇文章看懂Android学习最佳路线

为什么中高级Android程序员不多呢?这是一个问题,我不好回答,但是我想写一篇文章来描述下Android的学习路线,期望可以帮助更多的Android程序员提升自己. 作者:来源:Android开发中文站|2015-11-12 10:40 收藏 分享 前言 看到一篇文章中提到"最近几年国内的初级Android程序员已经很多了,但是中高级的Android技术人才仍然稀缺",这的确不假,从我在百度所进行的一些面试来看,找一个适合的高级Android工程师的确不容易,一般需要进行大量的面试才

angularjs 一篇文章看懂自定义指令directive

 壹 ? 引 在angularjs开发中,指令的使用是无处无在的,我们习惯使用指令来拓展HTML:那么如何理解指令呢,你可以把它理解成在DOM元素上运行的函数,它可以帮助我们拓展DOM元素的功能.比如最常用ng-click可以让一个元素能监听click事件,这里你可能就有疑问了,同样都是监听为什么不直接使用click事件呢,angular提供的事件指令与传统指令有什么区别?我们来看一个例子: <body ng-controller="myCtrl as vm"> <d

一篇文章看懂spark 1.3+各版本特性

Spark 1.6.x的新特性Spark-1.6是Spark-2.0之前的最后一个版本.主要是三个大方面的改进:性能提升,新的 Dataset API 和数据科学功能的扩展.这是社区开发非常重要的一个里程碑.1. 性能提升根据 Apache Spark 官方 2015 年 Spark Survey,有 91% 的用户想要提升 Spark 的性能.Parquet 性能自动化内存管理流状态管理速度提升 10X 2. Dataset APISpark 团队引入了 DataFrames,新型Datase

小程序社交立减金正式开放!一篇文章看懂它

2018微信公开课PRO现场,微信小程序团队宣布,小程序立减金能力正式上线. 什么是社交立减金? 这是一款能够帮助商家快速获取社交.裂变传播属性的小程序经营工具--商家应用了这一能力后,用户通过支付.扫码等场景就能参与社交立减金的活动,将社交立减金礼包分享出去,就能获得一份立减金. 如何领取社交立减金? 1.用户在门店用微信支付消费后,可通过收到的模板消息领取立减金: 2.点击"邀请好友一起领取",让更多好友参与进来: 3.下次使用微信支付后,可以直接抵扣立减金. 社交立减金营销效果如

一篇文章看懂iOS代码块Block

iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中,常用于GCD.动画.排序及各类回调 注: Block的声明与赋值只是保存了一段代码段,必须调用才能执行内部代码 Block变量的声明.赋值与调用 Block变量的声明 Block变量的声明格式为: 返回值类型(^Bl

一篇文章看懂Java并发和线程安全

一.前言 长久以来,一直想剖析一下Java线程安全的本质,但是苦于有些微观的点想不明白,便搁置了下来,前段时间慢慢想明白了,便把所有的点串联起来,趁着思路清晰,整理成这样一篇文章. 二.导读 1.为什么有多线程? 2.线程安全描述的本质问题是什么? 3.Java内存模型(JMM)数据可见性问题.指令重排序.内存屏障 三.揭晓答案 1.为什么有多线程 谈到多线程,我们很容易与高性能画上等号,但是并非如此,举个简单的例子,从1加到100,用四个线程计算不一定比一个线程来得快.因为线程的创建和上下文切

微信小程序社交立减金正式开放!一篇文章看懂它(开通微信免充值代金券)

2018微信公开课PRO现场,微信小程序团队宣布,小程序立减金能力正式上线. 什么是社交立减金? 这是一款能够帮助商家快速获取社交.裂变传播属性的小程序经营工具--商家应用了这一能力后,用户通过支付.扫码等场景就能参与社交立减金的活动,将社交立减金礼包分享出去,就能获得一份立减金. 如何领取社交立减金? 1.用户在门店用微信支付消费后,可通过收到的模板消息领取立减金: 2.点击"邀请好友一起领取",让更多好友参与进来: 3.下次使用微信支付后,可以直接抵扣立减金. 社交立减金营销效果如

一篇文章看懂JS执行上下文

 壹 ? 引 我们都知道,JS代码的执行顺序总是与代码先后顺序有所差异,当先抛开异步问题你会发现就算是同步代码,它的执行也与你的预期不一致,比如: function f1() { console.log('听风是风'); }; f1(); //echo function f1() { console.log('echo'); }; f1(); //echo 按照代码书写顺序,应该先输出 听风是风,再输出 echo才对,很遗憾,两次输出均为 echo:如果我们将上述代码中的函数声明改为函数表达式,