-
一文读懂目标检测:R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD
2018-05-02 18:41:26之前我所在的公司七月在线开设的深度学习等一系列课程经常会讲目标检测,包括R-CNN、Fast R-CNN、Faster R-CNN,但一直没有比较好的机会深入(但当你对目标检测有个基本的了解之后,再看这些课程你会收益很大)。...一文读懂目标检测:R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD
前言
之前我所在的公司七月在线开设的深度学习等一系列课程经常会讲目标检测,包括R-CNN、Fast R-CNN、Faster R-CNN,但一直没有比较好的机会深入(但当你对目标检测有个基本的了解之后,再看这些课程你会收益很大)。但目标检测这个领域实在是太火了,经常会看到一些写的不错的通俗易懂的资料,加之之前在京东上掏了一本书看了看,就这样耳濡目染中,还是开始研究了。
今年五一,从保定回京,怕高速路上堵 没坐大巴,高铁又没抢上,只好选择哐当哐当好几年没坐过的绿皮车,关键还不断晚点。在车站,用手机做个热点,修改题库,顺便终于搞清R-CNN、fast R-CNN、faster R-CNN的核心区别。有心中热爱 何惧任何啥。
为纪念这心中热爱,故成此文。
一、目标检测常见算法
object detection,就是在给定的图片中精确找到物体所在位置,并标注出物体的类别。所以,object detection要解决的问题就是物体在哪里以及是什么的整个流程问题。
然而,这个问题可不是那么容易解决的,物体的尺寸变化范围很大,摆放物体的角度,姿态不定,而且可以出现在图片的任何地方,更何况物体还可以是多个类别。
目前学术和工业界出现的目标检测算法分成3类:
1. 传统的目标检测算法:Cascade + HOG/DPM + Haar/SVM以及上述方法的诸多改进、优化;
2. 候选区域/窗 + 深度学习分类:通过提取候选区域,并对相应区域进行以深度学习方法为主的分类的方案,如:
R-CNN(Selective Search + CNN + SVM)
SPP-net(ROI Pooling)
Fast R-CNN(Selective Search + CNN + ROI)
Faster R-CNN(RPN + CNN + ROI)
R-FCN等系列方法;
3. 基于深度学习的回归方法:YOLO/SSD/DenseBox 等方法;以及最近出现的结合RNN算法的RRC detection;结合DPM的Deformable CNN等
传统目标检测流程:
1)区域选择(穷举策略:采用滑动窗口,且设置不同的大小,不同的长宽比对图像进行遍历,时间复杂度高)
2)特征提取(SIFT、HOG等;形态多样性、光照变化多样性、背景多样性使得特征鲁棒性差)
3)分类器分类(主要有SVM、Adaboost等)二、传统的目标检测算法
2.1 从图像识别的任务说起
这里有一个图像任务:既要把图中的物体识别出来,又要用方框框出它的位置。
这个任务本质上就是这两个问题:一:图像识别,二:定位。
图像识别(classification):
输入:图片
输出:物体的类别
评估方法:准确率
定位(localization):
输入:图片
输出:方框在图片中的位置(x,y,w,h)
评估方法:检测评价函数 intersection-over-union(关于什么是IOU,请参看七月在线APP题库大题查看深度学习分类下第55题:https://www.julyedu.com/question/big/kp_id/26/ques_id/2138)
卷积神经网络CNN已经帮我们完成了图像识别(判定是猫还是狗)的任务了,我们只需要添加一些额外的功能来完成定位任务即可。
定位的问题的解决思路有哪些?
思路一:看做回归问题
看做回归问题,我们需要预测出(x,y,w,h)四个参数的值,从而得出方框的位置。
步骤1:
• 先解决简单问题, 搭一个识别图像的神经网络
• 在AlexNet VGG GoogleLenet上fine-tuning一下(关于什么是微调fine-tuning,请参看:https://www.julyedu.com/question/big/kp_id/26/ques_id/2137)
步骤2:
• 在上述神经网络的尾部展开(也就说CNN前面保持不变,我们对CNN的结尾处作出改进:加了两个头:“分类头”和“回归头”)
• 成为classification + regression模式
步骤3:
• Regression那个部分用欧氏距离损失
• 使用SGD训练
步骤4:
• 预测阶段把2个头部拼上
• 完成不同的功能
这里需要进行两次fine-tuning
第一次在ALexNet上做,第二次将头部改成regression head,前面不变,做一次fine-tuning
Regression的部分加在哪?
有两种处理方法:
• 加在最后一个卷积层后面(如VGG)
• 加在最后一个全连接层后面(如R-CNN)
regression太难做了,应想方设法转换为classification问题。
regression的训练参数收敛的时间要长得多,所以上面的网络采取了用classification的网络来计算出网络共同部分的连接权值。
思路二:取图像窗口
• 还是刚才的classification + regression思路
• 咱们取不同的大小的“框”
• 让框出现在不同的位置,得出这个框的判定得分
• 取得分最高的那个框
左上角的黑框:得分0.5
右上角的黑框:得分0.75
左下角的黑框:得分0.6
右下角的黑框:得分0.8
根据得分的高低,我们选择了右下角的黑框作为目标位置的预测。
注:有的时候也会选择得分最高的两个框,然后取两框的交集作为最终的位置预测。
疑惑:框要取多大?
取不同的框,依次从左上角扫到右下角。非常粗暴啊。
总结一下思路:
对一张图片,用各种大小的框(遍历整张图片)将图片截取出来,输入到CNN,然后CNN会输出这个框的得分(classification)以及这个框图片对应的x,y,h,w(regression)。
这方法实在太耗时间了,做个优化。
原来网络是这样的:
优化成这样:把全连接层改为卷积层,这样可以提提速。
2.2 物体检测(Object Detection)
当图像有很多物体怎么办的?难度可是一下暴增啊。
那任务就变成了:多物体识别+定位多个物体
那把这个任务看做分类问题?
看成分类问题有何不妥?
• 你需要找很多位置, 给很多个不同大小的框
• 你还需要对框内的图像分类
• 当然, 如果你的GPU很强大, 恩, 那加油做吧…
所以,传统目标检测的主要问题是:
1)基于滑动窗口的区域选择策略没有针对性,时间复杂度高,窗口冗余
2)手工设计的特征对于多样性的变化没有很好的鲁棒性
看做classification, 有没有办法优化下?我可不想试那么多框那么多位置啊!三、候选区域/窗 + 深度学习分类
3.1 R-CNN横空出世
有人想到一个好方法:预先找出图中目标可能出现的位置,即候选区域(Region Proposal)。利用图像中的纹理、边缘、颜色等信息,可以保证在选取较少窗口(几千甚至几百)的情况下保持较高的召回率(Recall)。
所以,问题就转变成找出可能含有物体的区域/框(也就是候选区域/框,比如选2000个候选框),这些框之间是可以互相重叠互相包含的,这样我们就可以避免暴力枚举的所有框了。
大牛们发明好多选定候选框Region Proposal的方法,比如Selective Search和EdgeBoxes。那提取候选框用到的算法“选择性搜索”到底怎么选出这些候选框的呢?具体可以看一下PAMI2015的“What makes for effective detection proposals?”
以下是各种选定候选框的方法的性能对比。
有了候选区域,剩下的工作实际就是对候选区域进行图像分类的工作(特征提取+分类)。对于图像分类,不得不提的是2012年ImageNet大规模视觉识别挑战赛(ILSVRC)上,机器学习泰斗Geoffrey Hinton教授带领学生Krizhevsky使用卷积神经网络将ILSVRC分类任务的Top-5 error降低到了15.3%,而使用传统方法的第二名top-5 error高达 26.2%。此后,卷积神经网络CNN占据了图像分类任务的绝对统治地位。
2014年,RBG(Ross B. Girshick)使用Region Proposal + CNN代替传统目标检测使用的滑动窗口+手工设计特征,设计了R-CNN框架,使得目标检测取得巨大突破,并开启了基于深度学习目标检测的热潮。
R-CNN的简要步骤如下
(1) 输入测试图像
(2) 利用选择性搜索Selective Search算法在图像中从下到上提取2000个左右的可能包含物体的候选区域Region Proposal
(3) 因为取出的区域大小各自不同,所以需要将每个Region Proposal缩放(warp)成统一的227x227的大小并输入到CNN,将CNN的fc7层的输出作为特征
(4) 将每个Region Proposal提取到的CNN特征输入到SVM进行分类
具体步骤则如下
步骤一:训练(或者下载)一个分类模型(比如AlexNet)
步骤二:对该模型做fine-tuning
• 将分类数从1000改为20,比如20个物体类别 + 1个背景
• 去掉最后一个全连接层
步骤三:特征提取
• 提取图像的所有候选框(选择性搜索Selective Search)
• 对于每一个区域:修正区域大小以适合CNN的输入,做一次前向运算,将第五个池化层的输出(就是对候选框提取到的特征)存到硬盘
步骤四:训练一个SVM分类器(二分类)来判断这个候选框里物体的类别
每个类别对应一个SVM,判断是不是属于这个类别,是就是positive,反之nagative。
比如下图,就是狗分类的SVM
步骤五:使用回归器精细修正候选框位置:对于每一个类,训练一个线性回归模型去判定这个框是否框得完美。
细心的同学可能看出来了问题,R-CNN虽然不再像传统方法那样穷举,但R-CNN流程的第一步中对原始图片通过Selective Search提取的候选框region proposal多达2000个左右,而这2000个候选框每个框都需要进行CNN提特征+SVM分类,计算量很大,导致R-CNN检测速度很慢,一张图都需要47s。
有没有方法提速呢?答案是有的,这2000个region proposal不都是图像的一部分吗,那么我们完全可以对图像提一次卷积层特征,然后只需要将region proposal在原图的位置映射到卷积层特征图上,这样对于一张图像我们只需要提一次卷积层特征,然后将每个region proposal的卷积层特征输入到全连接层做后续操作。
但现在的问题是每个region proposal的尺度不一样,而全连接层输入必须是固定的长度,所以直接这样输入全连接层肯定是不行的。SPP Net恰好可以解决这个问题。
3.2 SPP Net
SPP:Spatial Pyramid Pooling(空间金字塔池化)
SPP-Net是出自2015年发表在IEEE上的论文-《Spatial Pyramid Pooling in Deep ConvolutionalNetworks for Visual Recognition》。
众所周知,CNN一般都含有卷积部分和全连接部分,其中,卷积层不需要固定尺寸的图像,而全连接层是需要固定大小的输入。
所以当全连接层面对各种尺寸的输入数据时,就需要对输入数据进行crop(crop就是从一个大图扣出网络输入大小的patch,比如227×227),或warp(把一个边界框bounding box的内容resize成227×227)等一系列操作以统一图片的尺寸大小,比如224*224(ImageNet)、32*32(LenNet)、96*96等。
所以才如你在上文中看到的,在R-CNN中,“因为取出的区域大小各自不同,所以需要将每个Region Proposal缩放(warp)成统一的227x227的大小并输入到CNN”。
但warp/crop这种预处理,导致的问题要么被拉伸变形、要么物体不全,限制了识别精确度。没太明白?说句人话就是,一张16:9比例的图片你硬是要Resize成1:1的图片,你说图片失真不?
SPP Net的作者Kaiming He等人逆向思考,既然由于全连接FC层的存在,普通的CNN需要通过固定输入图片的大小来使得全连接层的输入固定。那借鉴卷积层可以适应任何尺寸,为何不能在卷积层的最后加入某种结构,使得后面全连接层得到的输入变成固定的呢?
这个“化腐朽为神奇”的结构就是spatial pyramid pooling layer。下图便是R-CNN和SPP Net检测流程的比较:
它的特点有两个:
1.结合空间金字塔方法实现CNNs的多尺度输入。
SPP Net的第一个贡献就是在最后一个卷积层后,接入了金字塔池化层,保证传到下一层全连接层的输入固定。
换句话说,在普通的CNN机构中,输入图像的尺寸往往是固定的(比如224*224像素),输出则是一个固定维数的向量。SPP Net在普通的CNN结构中加入了ROI池化层(ROI Pooling),使得网络的输入图像可以是任意尺寸的,输出则不变,同样是一个固定维数的向量。
简言之,CNN原本只能固定输入、固定输出,CNN加上SSP之后,便能任意输入、固定输出。神奇吧?
ROI池化层一般跟在卷积层后面,此时网络的输入可以是任意尺度的,在SPP layer中每一个pooling的filter会根据输入调整大小,而SPP的输出则是固定维数的向量,然后给到全连接FC层。
2.只对原图提取一次卷积特征
在R-CNN中,每个候选框先resize到统一大小,然后分别作为CNN的输入,这样是很低效的。
而SPP Net根据这个缺点做了优化:只对原图进行一次卷积计算,便得到整张图的卷积特征feature map,然后找到每个候选框在feature map上的映射patch,将此patch作为每个候选框的卷积特征输入到SPP layer和之后的层,完成特征提取工作。
如此这般,R-CNN要对每个区域计算卷积,而SPPNet只需要计算一次卷积,从而节省了大量的计算时间,比R-CNN有一百倍左右的提速。3.3 Fast R-CNN
SPP Net真是个好方法,R-CNN的进阶版Fast R-CNN就是在R-CNN的基础上采纳了SPP Net方法,对R-CNN作了改进,使得性能进一步提高。
R-CNN与Fast R-CNN的区别有哪些呢?
先说R-CNN的缺点:即使使用了Selective Search等预处理步骤来提取潜在的bounding box作为输入,但是R-CNN仍会有严重的速度瓶颈,原因也很明显,就是计算机对所有region进行特征提取时会有重复计算,Fast-RCNN正是为了解决这个问题诞生的。
与R-CNN框架图对比,可以发现主要有两处不同:一是最后一个卷积层后加了一个ROI pooling layer,二是损失函数使用了多任务损失函数(multi-task loss),将边框回归Bounding Box Regression直接加入到CNN网络中训练(关于什么是边框回归,请参看七月在线APP题库大题查看深度学习分类下第56题:https://www.julyedu.com/question/big/kp_id/26/ques_id/2139)。
(1) ROI pooling layer实际上是SPP-NET的一个精简版,SPP-NET对每个proposal使用了不同大小的金字塔映射,而ROI pooling layer只需要下采样到一个7x7的特征图。对于VGG16网络conv5_3有512个特征图,这样所有region proposal对应了一个7*7*512维度的特征向量作为全连接层的输入。
换言之,这个网络层可以把不同大小的输入映射到一个固定尺度的特征向量,而我们知道,conv、pooling、relu等操作都不需要固定size的输入,因此,在原始图片上执行这些操作后,虽然输入图片size不同导致得到的feature map尺寸也不同,不能直接接到一个全连接层进行分类,但是可以加入这个神奇的ROI Pooling层,对每个region都提取一个固定维度的特征表示,再通过正常的softmax进行类型识别。
(2) R-CNN训练过程分为了三个阶段,而Fast R-CNN直接使用softmax替代SVM分类,同时利用多任务损失函数边框回归也加入到了网络中,这样整个的训练过程是端到端的(除去Region Proposal提取阶段)。
也就是说,之前R-CNN的处理流程是先提proposal,然后CNN提取特征,之后用SVM分类器,最后再做bbox regression,而在Fast R-CNN中,作者巧妙的把bbox regression放进了神经网络内部,与region分类和并成为了一个multi-task模型,实际实验也证明,这两个任务能够共享卷积特征,并相互促进。
所以,Fast-RCNN很重要的一个贡献是成功的让人们看到了Region Proposal + CNN这一框架实时检测的希望,原来多类检测真的可以在保证准确率的同时提升处理速度,也为后来的Faster R-CNN做下了铺垫。
画一画重点:
R-CNN有一些相当大的缺点(把这些缺点都改掉了,就成了Fast R-CNN)。
大缺点:由于每一个候选框都要独自经过CNN,这使得花费的时间非常多。
解决:共享卷积层,现在不是每一个候选框都当做输入进入CNN了,而是输入一张完整的图片,在第五个卷积层再得到每个候选框的特征
原来的方法:许多候选框(比如两千个)-->CNN-->得到每个候选框的特征-->分类+回归
现在的方法:一张完整图片-->CNN-->得到每张候选框的特征-->分类+回归
所以容易看见,Fast R-CNN相对于R-CNN的提速原因就在于:不过不像R-CNN把每个候选区域给深度网络提特征,而是整张图提一次特征,再把候选框映射到conv5上,而SPP只需要计算一次特征,剩下的只需要在conv5层上操作就可以了。
在性能上提升也是相当明显的:3.4 Faster R-CNN
Fast R-CNN存在的问题:存在瓶颈:选择性搜索,找出所有的候选框,这个也非常耗时。那我们能不能找出一个更加高效的方法来求出这些候选框呢?
解决:加入一个提取边缘的神经网络,也就说找到候选框的工作也交给神经网络来做了。
所以,rgbd在Fast R-CNN中引入Region Proposal Network(RPN)替代Selective Search,同时引入anchor box应对目标形状的变化问题(anchor就是位置和大小固定的box,可以理解成事先设置好的固定的proposal)。
具体做法:
• 将RPN放在最后一个卷积层的后面
• RPN直接训练得到候选区域
RPN简介:
• 在feature map上滑动窗口
• 建一个神经网络用于物体分类+框位置的回归
• 滑动窗口的位置提供了物体的大体位置信息
• 框的回归提供了框更精确的位置
一种网络,四个损失函数;
• RPN calssification(anchor good.bad)
• RPN regression(anchor->propoasal)
• Fast R-CNN classification(over classes)
• Fast R-CNN regression(proposal ->box)
速度对比
Faster R-CNN的主要贡献就是设计了提取候选区域的网络RPN,代替了费时的选择性搜索selective search,使得检测速度大幅提高。
最后总结一下各大算法的步骤:
RCNN
1.在图像中确定约1000-2000个候选框 (使用选择性搜索Selective Search)
2.每个候选框内图像块缩放至相同大小,并输入到CNN内进行特征提取
3.对候选框中提取出的特征,使用分类器判别是否属于一个特定类
4.对于属于某一类别的候选框,用回归器进一步调整其位置
Fast R-CNN
1.在图像中确定约1000-2000个候选框 (使用选择性搜索Selective Search)
2.对整张图片输进CNN,得到feature map
3.找到每个候选框在feature map上的映射patch,将此patch作为每个候选框的卷积特征输入到SPP layer和之后的层
4.对候选框中提取出的特征,使用分类器判别是否属于一个特定类
5.对于属于某一类别的候选框,用回归器进一步调整其位置
Faster R-CNN
1.对整张图片输进CNN,得到feature map
2.卷积特征输入到RPN,得到候选框的特征信息
3.对候选框中提取出的特征,使用分类器判别是否属于一个特定类
4.对于属于某一类别的候选框,用回归器进一步调整其位置简言之,即如本文开头所列
R-CNN(Selective Search + CNN + SVM)
SPP-net(ROI Pooling)
Fast R-CNN(Selective Search + CNN + ROI)
Faster R-CNN(RPN + CNN + ROI)
总的来说,从R-CNN, SPP-NET, Fast R-CNN, Faster R-CNN一路走来,基于深度学习目标检测的流程变得越来越精简,精度越来越高,速度也越来越快。可以说基于Region Proposal的R-CNN系列目标检测方法是当前目标检测技术领域最主要的一个分支。四、基于深度学习的回归方法
4.1 YOLO (CVPR2016, oral)
(You Only Look Once: Unified, Real-Time Object Detection)
Faster R-CNN的方法目前是主流的目标检测方法,但是速度上并不能满足实时的要求。YOLO一类的方法慢慢显现出其重要性,这类方法使用了回归的思想,利用整张图作为网络的输入,直接在图像的多个位置上回归出这个位置的目标边框,以及目标所属的类别。
我们直接看上面YOLO的目标检测的流程图:
(1) 给个一个输入图像,首先将图像划分成7*7的网格
(2) 对于每个网格,我们都预测2个边框(包括每个边框是目标的置信度以及每个边框区域在多个类别上的概率)
(3) 根据上一步可以预测出7*7*2个目标窗口,然后根据阈值去除可能性比较低的目标窗口,最后NMS去除冗余窗口即可(关于什么是非极大值抑制NMS,请参看七月在线APP题库大题查看深度学习分类下第58题:https://www.julyedu.com/question/big/kp_id/26/ques_id/2141)。
可以看到整个过程非常简单,不再需要中间的Region Proposal找目标,直接回归便完成了位置和类别的判定。
小结:YOLO将目标检测任务转换成一个回归问题,大大加快了检测的速度,使得YOLO可以每秒处理45张图像。而且由于每个网络预测目标窗口时使用的是全图信息,使得false positive比例大幅降低(充分的上下文信息)。
但是YOLO也存在问题:没有了Region Proposal机制,只使用7*7的网格回归会使得目标不能非常精准的定位,这也导致了YOLO的检测精度并不是很高。
4.2 SSD
(SSD: Single Shot MultiBox Detector)
上面分析了YOLO存在的问题,使用整图特征在7*7的粗糙网格内回归对目标的定位并不是很精准。那是不是可以结合Region Proposal的思想实现精准一些的定位?SSD结合YOLO的回归思想以及Faster R-CNN的anchor机制做到了这点。
上图是SSD的一个框架图,首先SSD获取目标位置和类别的方法跟YOLO一样,都是使用回归,但是YOLO预测某个位置使用的是全图的特征,SSD预测某个位置使用的是这个位置周围的特征(感觉更合理一些)。
那么如何建立某个位置和其特征的对应关系呢?可能你已经想到了,使用Faster R-CNN的anchor机制。如SSD的框架图所示,假如某一层特征图(图b)大小是8*8,那么就使用3*3的滑窗提取每个位置的特征,然后这个特征回归得到目标的坐标信息和类别信息(图c)。
不同于Faster R-CNN,这个anchor是在多个feature map上,这样可以利用多层的特征并且自然的达到多尺度(不同层的feature map 3*3滑窗感受野不同)。
小结:SSD结合了YOLO中的回归思想和Faster R-CNN中的anchor机制,使用全图各个位置的多尺度区域特征进行回归,既保持了YOLO速度快的特性,也保证了窗口预测的跟Faster R-CNN一样比较精准。SSD在VOC2007上mAP可以达到72.1%,速度在GPU上达到58帧每秒。
主要参考及扩展阅读
1 https://www.cnblogs.com/skyfsm/p/6806246.html,by @Madcola
2 https://mp.weixin.qq.com/s?__biz=MzI1NTE4NTUwOQ==&mid=502841131&idx=1&sn=bb3e8e6aeee2ee1f4d3f22459062b814#rd
3 https://zhuanlan.zhihu.com/p/27546796
4 https://blog.csdn.net/v1_vivian/article/details/73275259
5 https://blog.csdn.net/tinyzhao/article/details/53717136
6 Spatial Pyramid Pooling in Deep Convolutional
Networks for Visual Recognition,by Kaiming He等人
7 https://zhuanlan.zhihu.com/p/24774302
8 知乎专栏作者何之源新书《21个项目玩转深度学习——基于TensorFlow的实践详解》
9 YOLO:https://blog.csdn.net/tangwei2014/article/details/50915317,https://zhuanlan.zhihu.com/p/24916786 -
CNN
2020-10-09 20:39:44卷积会损失很多东西,交给池化。。池化相当于降维,压缩数据。 CNN在图像处理的搭建过程: def CNN1(): # 卷积神经网络 # Hyper Parameters EPOCH = 1 # train the training data n times, to save time,...卷积会损失很多东西,交给池化。。池化相当于降维,压缩数据。
CNN在图像处理的搭建过程:
def CNN1(): # 卷积神经网络 # Hyper Parameters EPOCH = 1 # train the training data n times, to save time, we just train 1 epoch BATCH_SIZE = 50 LR = 0.0001 # learning rate DOWNLOAD_MNIST = False # 刚开始没有数据图片的时候这里要用True,有的话就改为F train_data = torchvision.datasets.MNIST( root='./mnist/', train=True, # this is training data transform=torchvision.transforms.ToTensor(), # 将0-255转换成0-1 Converts a PIL.Image or numpy.ndarray to # torch.FloatTensor of shape (C x H x W) and normalize in the range [0.0, 1.0] download=DOWNLOAD_MNIST, ) # plot one example print(train_data.train_data.size()) # (60000, 28, 28) print(train_data.train_labels.size()) # (60000) plt.imshow(train_data.train_data[0].numpy(), cmap='gray') # train_data里面的第一章数据 plt.title('%i' % train_data.train_labels[0]) plt.show() # Data Loader for easy mini-batch return in training, the image batch shape will be (50, 1, 28, 28) train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True) # pick 2000 samples to speed up testing test_data = torchvision.datasets.MNIST(root='./mnist/', train=False) test_x = torch.unsqueeze(test_data.test_data, dim=1).type(torch.FloatTensor)[ :2000] / 255. # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1) test_y = test_data.test_labels[:2000] #只取前2000个 # 构建卷积神经网络 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Sequential( # input shape (1, 28, 28) # 构建卷积层 1是图片的高度,28是长和宽 nn.Conv2d( # 卷积核 2维 in_channels=1, # input height #输入的图片的高度 out_channels=16, # n_filters 有16个硬币来卷积,所以有16个特征,后面的图片高度就是16了, kernel_size=5, # filter size 硬币是5*5的 stride=1, # filter movement/step 每次硬币跳的步数 padding=2, # if want same width and length of this image after Conv2d, padding=(kernel_size-1)/2 if stride=1 ), # output shape (16, 28, 28) nn.ReLU(), # activation nn.MaxPool2d(kernel_size=2), # choose max value in 2x2 area, output shape (16, 14, 14) #池化层 ) self.conv2 = nn.Sequential( # input shape (16, 14, 14) nn.Conv2d(16, 32, 5, 1, 2), # output shape (32, 14, 14) nn.ReLU(), # activation nn.MaxPool2d(2), # output shape (32, 7, 7) ) self.out = nn.Linear(32 * 7 * 7, 10) # 把三维的展开成2维的。 fully connected layer, output 10 classes def forward(self, x): x = self.conv1(x) x = self.conv2(x) # (32, 7, 7) x = x.view(x.size(0), -1) # flatten the output of conv2 to (batch_size, 32 * 7 * 7) output = self.out(x) return output, x # return x for visualization cnn = CNN() print(cnn) # net architecture optimizer = torch.optim.Adam(cnn.parameters(), lr=LR) # optimize all cnn parameters loss_func = nn.CrossEntropyLoss() # the target label is not one-hotted # following function (plot_with_labels) is for visualization, can be ignored if not interested from matplotlib import cm try: from sklearn.manifold import TSNE; HAS_SK = True except: HAS_SK = False; print('Please install sklearn for layer visualization') def plot_with_labels(lowDWeights, labels): plt.cla() X, Y = lowDWeights[:, 0], lowDWeights[:, 1] for x, y, s in zip(X, Y, labels): c = cm.rainbow(int(255 * s / 9)); plt.text(x, y, s, backgroundcolor=c, fontsize=9) plt.xlim(X.min(), X.max()); plt.ylim(Y.min(), Y.max()); plt.title('Visualize last layer'); plt.show(); plt.pause(0.01) plt.ion() # training and testing for epoch in range(EPOCH): for step, (b_x, b_y) in enumerate(train_loader): # gives batch data, normalize x when iterate train_loader output = cnn(b_x)[0] # cnn output loss = loss_func(output, b_y) # cross entropy loss optimizer.zero_grad() # clear gradients for this training step loss.backward() # backpropagation, compute gradients optimizer.step() # apply gradients if step % 50 == 0: test_output, last_layer = cnn(test_x) pred_y = torch.max(test_output, 1)[1].data.numpy() accuracy = float((pred_y == test_y.data.numpy()).astype(int).sum()) / float(test_y.size(0)) print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.numpy(), '| test accuracy: %.2f' % accuracy) if HAS_SK: # Visualization of trained flatten layer (T-SNE) tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000) plot_only = 500 low_dim_embs = tsne.fit_transform(last_layer.data.numpy()[:plot_only, :]) labels = test_y.numpy()[:plot_only] plot_with_labels(low_dim_embs, labels) plt.ioff() # print 10 predictions from test data test_output, _ = cnn(test_x[:10]) pred_y = torch.max(test_output, 1)[1].data.numpy() print(pred_y, 'prediction number') print(test_y[:10].numpy(), 'real number')
-
cnn
2017-05-04 17:51:12deepLearnToolbox-master是一个深度学习matlab包,里面含有很多机器学习算法,如卷积神经网络CNN,深度信念网络DBN,自动编码AutoEncoder(堆栈SAE,卷积CAE)的作者是 RasmusBerg Palm。 今天给介绍deep...deepLearnToolbox-master是一个深度学习matlab包,里面含有很多机器学习算法,如卷积神经网络CNN,深度信念网络DBN,自动编码AutoEncoder(堆栈SAE,卷积CAE)的作者是 RasmusBerg Palm。
今天给介绍deepLearnToolbox-master中的CNN部分。
DeepLearnToolbox-master中CNN内的函数:
调用关系为:
该模型使用了mnist的数字mnist_uint8.mat作为训练样本,作为cnn的一个使用样例,每个样本特征为一个28*28=的向量。
网络结构为:
让我们来分析各个函数:
一、Test_example_CNN
三、cnntrain.m.
四、cnnff.m.
五、cnnbp.m.
五、cnnapplygrads.m.
六、cnntest.m.
一、Test_example_CNN:
1设置CNN的基本参数规格,如卷积、降采样层的数量,卷积核的大小、降采样的降幅
2 cnnsetup函数 初始化卷积核、偏置等
3 cnntrain函数 训练cnn,把训练数据分成batch,然后调用
3.1 cnnff 完成训练的前向过程,
3.2 cnnbp计算并传递神经网络的error,并计算梯度(权重的修改量)
3.3 cnnapplygrads 把计算出来的梯度加到原始模型上去
4 cnntest函数,测试当前模型的准确率
该模型采用的数据为mnist_uint8.mat,
含有70000个手写数字样本其中60000作为训练样本,10000作为测试样本。
把数据转成相应的格式,并归一化。
二、Cnnsetup.m
该函数你用于初始化CNN的参数。
设置各层的mapsize大小,
初始化卷积层的卷积核、bias
尾部单层感知机的参数设置
bias统一设置为0
权重设置为:-1~1之间的随机数/sqrt(6/(输入神经元数量+输出神经元数量))
对于卷积核权重,输入输出为fan_in, fan_out
fan_out= net.layers{l}.outputmaps * net.layers{l}.kernelsize ^ 2;
%卷积核初始化,1层卷积为1*6个卷积核,2层卷积一共6*12=72个卷积核。对于每个卷积输出featuremap,
%fan_in= 表示该层的一个输出map,所对应的所有卷积核,包含的神经元的总数。1*25,6*25
fan_in =numInputmaps * net.layers{l}.kernelsize ^ 2;
fin=1*25 or 6*25
fout=1*6*25 or 6*12*25
net.layers{l}.k{i}{j} =(rand(net.layers{l}.kernelsize) - 0.5) * 2 * sqrt(6 / (fan_in + fan_out));
1、卷积降采样的参数初始化
2、尾部单层感知机的参数(权重和偏量)设置:
三、cnntrain.m
该函数用于训练CNN。
生成随机序列,每次选取一个batch(50)个样本进行训练。
批训练:计算50个随机样本的梯度,求和之后一次性更新到模型权重中。
在批训练过程中调用:
Cnnff.m 完成前向过程
Cnnbp.m 完成误差传导和梯度计算过程
Cnnapplygrads.m把计算出来的梯度加到原始模型上去
四、cnnff.m
3、尾部单层感知机的数据处理,需要把subFeatureMap2连接成为一个(4*4)*12=192的向量,但是由于采用了50样本批训练的方法,subFeatureMap2被拼合成为一个192*50的特征向量fv;
Fv作为单层感知机的输入,全连接的方式得到输出层
五、cnnbp.m
该函数实现2部分功能,计算并传递误差,计算梯度
3、把单层感知机的输入层featureVector的误差矩阵,恢复为subFeatureMap2的4*4二维矩阵形式
插播一张图片:
4、误差在特征提取网络【卷积降采样层】的传播
如果本层是卷积层,它的误差是从后一层(降采样层)传过来,误差传播实际上是用降采样的反向过程,也就是降采样层的误差复制为2*2=4份。卷积层的输入是经过sigmoid处理的,所以,从降采样层扩充来的误差要经过sigmoid求导处理。
如果本层是降采样层,他的误差是从后一层(卷积层)传过来,误差传播实际是用卷积的反向过程,也就是卷积层的误差,反卷积(卷积核转180度)卷积层的误差,原理参看插图。
5、计算特征抽取层和尾部单层感知机的梯度
五、cnnapplygrads.m
该函数完成权重修改,更新模型的功能
1更新特征抽取层的权重 weight+bias
2 更新末尾单层感知机的权重 weight+bias
六、cnntest.m
验证测试样本的准确率
-
R-CNN家族梳理:从R-CNN到Mask R-CNN
2020-02-21 23:50:24R-CNN家族梳理:从R-CNN到Mask R-CNN 本文打算对目标检测很火的算法R-CNN这一家族进行一个梳理。R-CNN家族梳理:从R-CNN到Mask R-CNN
文章目录
本文打算对目标检测很火的算法R-CNN这一家族进行一个梳理。
一、R-CNN(2014)
目标检测领域的开篇之作,第一个能成功应用到工业上的目标检测算法,详见论文:Rich feature hierarchies for accurate oject detection and semantic segmentation
之前的目标检测领域,常见的传统算法采用SIFT 和 HOG等方法取得了一定的进步,但是在2012年左右进展缓慢,直到AlexNet横空出世,才有了后面各种将深度学习应用到各个领域的算法的出现。
对于R-CNN,其含义为: Regions with CNN features,是two stage检测方法的代表,R-CNN这一家族的核心都是依赖region proposals进行检测定位,最后再去进行分类。
R-CNN主要由三个部分:
- 利用Selective Search生成region proposals,即前景的定位方框。
- 将各个proposal对应的图片区域抠出来,resize成固定大小,再送入CNN网络中提取特征。
- 通过全连接拉成一纬向量后,再利用SVM进行分类。
从现在(2020)回过头来看,R-CNN确实存在许多的缺陷,但不扣否认的是,他提出的two stage方法一直是目标检测算法的常青藤,以及首次将深度学习的办法应用到目标检测上的意义。
1.1 Selective Search:Region Proposals 最早的生成方法:
详见论文:Selective search for object recognition
要想生成捕捉不同尺度(Capture All Scales)、多样化(Diversification)、快速计算(Fast to Compute)
建议参考这篇知乎文章
1.2 NMS非极大值抑制(重要)
未完待续
1.3 Bounding Box Regression (重要)
Bounding Box (后面简称bbox) 可以理解为目标检测的那个定位框。但是为什么要进行一个Regression呢?这是由于直接通过Selective Search (在Faster R-CNN里面使用的是RPN生成),通过NMS筛选出的候选框,其实并不是完全地“框”住了目标,可能会存在长宽比例,以及中心位置的一个微小偏差,这样就需要对原有的候选框,通过回归进行一个修正。
比如这张图里面,红色框是我们得到的RegionProposal的一个框,但是实际的Ground Truth是绿色的,这个时候就需要对得到的红框进行一个修正。而原来的候选框,他的数据格式长啥样呢?其实也就是一个四维的向量,即中心点的坐标以及方框的长和宽,总共四个参数。Bounding Box Regression其实也就是对这四个参数进行回归修正。
是第个检测框,是相应的Ground Truth。且,。bbox回归就是希望找到一个映射关系,使得:
但实际上,这个可以分为如下两种:
平移变换:尺度变换:
在这关于的四个公式里面,输入是已知的,需要对这几个函数进行一个学习,也就是回归。
对一个来说,输入并不是的四个值,而是对应那个区域,经过卷积提取到的特征,比如在AlexNet中,就是pool5的值(记为,指经过CNN第五个pooling提取到的特征)。它的输出就是一个单值(记为)。也就是说,可以把等价为,其中,可以通过多种办法得到,梯度下降,或者是最小二乘法。
因此就有了一个关于得到的损失函数:这里面新出现的符号表示:
1.4 R-CNN优缺点讨论
缺点:
- Selective Search 效率其实十分低下。
尽管在R-CNN的论文中提到这是一种“很快”的方法,但是 selective search当中并没有任何可学习的参数,它需要提取的特征也是事先给定的。 - Proposals送入网络的过程很耗时。
这个主要体现在,当根据不同的proposal,从图片上提取出送入CNN时,会出现重复计算的情况。整张图的特征并没有共享,这也就造成了计算上的开销。 - 最后采用SVM分类其实并不比全连接+softmax好。
SVM的训练也需要很多的空间,而分类准确率并不会有很明显的提升。 - 训练很麻烦,多个地方都是分开训练的。
二、Fast R-CNN(2015)
详情参考这篇论文:Fast R-CNN
相比较R-CNN,Fast R-CNN在性能上有了一些提升,但是它最大的缺点,还是没有用CNN的方法来生成proposal。而最大的贡献,是用softmax替换掉svm,从而实现了大部分pipeline的end to end训练。简述一下整个流程:
- Selective Search得到候选框的坐标信息
- 将对应候选框的图像送入CNN
- 之后进行ROI Pooling
- 全连接层拉成一维向量
- 最后得到softmax分类和Bounding Box Regression
可以看到,其中所有的步骤,除了在开始要依赖Selective Search得到Proposals以外,其余的部分都由CNN完成,也就是更加方便了训练的步骤。
由于Bounding Box Regression前面叙述过了,这里不再重复,主要说一下ROI Pooling。
2.1 原始图片中的proposal如何映射到到feature map(重要)
这个主要源自SPP-net:Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
SPP-net主要是在R-CNN的基础上进行改进的,但是它仍旧继承了R-CNN的多阶段的处理过程。但是这里讨论的主要是,如何将原始图片中的proposal如何映射到到feature map。
对于一个region proposal来说,它只包含四个信息中心点的坐标和矩形的长宽,要想知道这个原图到特征图的映射关系,也就是需要知道边的映射和点的映射。- 点的映射(这里没有给出无规律网络的推到)
对于有规律的网络,如VGG16,统一使用3*3卷积和2*2池化,feature map之间的映射关系就是简单的倍数关系。 - 线的映射
的输出是,对于kernel size为,padding为 ,stride为的下一层卷积层而言,其输出为:因此,两个卷积层之间边长的映射关系就是:
2.2 ROI Pooling: Region of Interest Pooling
一句话就是:对非均匀尺寸的输入执行最大池化以获得固定尺寸的特征图。比如输入是5*7的尺寸,可以池化成4*4。
少了几张图,未完待续那么为什么要这样做呢?这是由于得到的Bounding Box大小不一,然而送到后续的全连接层又需要有固定的大小,因此就采用这样的办法来进行pooling。
它的优点是,这样的操作计算很快,但在精细度上还是太粗糙了,尤其是在分割任务里面是不够精确的,这点直到在Mask R-CNN中才得到改善,升级成了ROI Align。
三、Faster R-CNN(2015)
详见论文:Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
Faster R-CNN,见名知义,基于fast R-CNN在速度上有了更好的提升。前面提到了,Fast R-CNN还不够end to end,并且Selective Search的效率很低,这点是严重限制他性能的瓶颈。而到了Faster这里,通过RPN网络结构,用CNN的办法来生成region proposals,并且整个网络共享卷积层提取到的特征,这又在计算上带来了节省。在当年的目标检测比赛中,拿到了最高的mAP,几乎做到了实时目标检测,仅在FPS上面略输给YOLO一点。
3.1 RPN结构(重要)
RPN特指这一部分
他是接在前面的CNN后面,用于产生Region Proposals。可以看到RPN网络实际分为2条线,上面一条通过softmax分类anchors获得positive和negative分类,下面一条用于计算对于anchors的bounding box regression偏移量,以获得精确的proposal。而最后的Proposal层则负责综合positive anchors和对应bounding box regression偏移量获取proposals,同时剔除太小和超出边界的proposals。3.1.1 Anchor
我理解的Anchor,就是要在特征图上,先生成一堆候选框,再通过各种方法进行筛选,将筛选出来的送入网络进行训练。
论文中选取了三种长宽比和三种尺度,排列组合一下就是9种anchor。对于一个长宽为的特征图,总共会生成个anchor。四、Mask R-CNN(2016)
详见论文:Mask R-CNN
为了能够做到更加精细的分割,这里成功的把全卷积网络FCN与Faster R-CNN结合在一起,实现了实例分割。由于引入了检测定位框的“先验”,Mask R-CNN的分割结果相比较FCN会更加精细。
4.1 从ROI到Mask
在FCN中,输入图片先通过CNN得到特征图,之后特征图又经过transposed convolution 恢复成原图尺寸大小的分割Mask。
同样的,这里是将经过ROI Pooling后的特征图,又再恢复成原region proposal对应大小的尺寸。最后的输出是,其中代表的是分类的类别数。因为这里,最后的Mask采用了分别对每一种类别都预测一个前景和背景,能够有效避免在一张图片上同时预测多个类别时可能出现的Competition。4.2 ROI Align
在之前的ROI Pooling 中,存在这么一个问题:对特征图进行ROI Pooling下采样时的取整,会带来边框的一个误差,这也就是misalignments.
最后的解决办法是,通过ROI Align,用这么一种带有双线性插值的池化方法得到更为精细的池化。
-
R-CNN(Region-CNN)、Fast R-CNN、Faster R-CNN学习笔记
2020-05-16 21:22:46R-CNN 是伴随着目标检测技术提出的一种模型; 目标检测要解决的问题是:找出物体的位置、判断是什么物体;... 手工设计的特征对于多样性的变化没有很好的鲁棒性。 为了优化上述问题,提出了 R-CNN:预先找到目标 -
CNN概述
2020-04-20 15:29:18CNN 卷积神经网络简介 特点 将大数据量的图片降维成小数据量 有效保留图片特征 应用领域 人脸识别、自动驾驶、无人安防 CNN解决的问题 图像的数据量太大,导致成本很高,效率很低 图像在数字化的过程中容易... -
CNN卷积神经网络
2018-05-26 09:49:50目录 一、BP神经网络回顾 二、CNN卷积神经网络 1、CNN的主要概述 ...(2)当输入层的特征维度变得很高时,这时全连接网络需要训练的参数就会增大很多,计算速度就会变得很慢 传统神经网络存在的... -
很好的CNN学习资料
2016-12-22 14:18:15http://xiahouzuoxin.github.io/notes/html/%E6%B7%B1%E5%BA%A6%E5%8D%B7%E7%A7%AF%E7%BD%91%E7%BB%9CCNN%E4%B8%8E%E5%9B%BE%E5%83%8F%E8%AF%AD%E4%B9%89%E5%88%86%E5%89%B2.html#级别3读paper网络train起来 -
CNN新手
2018-01-17 15:21:50https://www.leiphone.com/news/201607/KjXQ1dFpOQfhTEdK.html 很通俗的介绍了CNN -
CNN学习
2016-11-22 20:24:48之前学习CNN时,知道CNN是做特征升维的,随着设置不同的步长以及子采样来提升维度。比如一个图片28*28像素,设置步长为1并使用max_pool_2x2可以升维到14*14。但不知道具体卷积的计算过程,网上的解释也很少,所以... -
cnn开篇
2018-04-19 20:25:56很早就打算写深度学习相关博客了,但是由于各种原因被搁置了。 这段时间刚好有空,就把以前的笔记整理总结了一下,温故而知新,以前有些不是特别清楚的概念,通过这次的复习豁然开朗了,也希望自己的分享能够帮助... -
CNN架构
2018-05-07 16:20:51如果没有这些层,模型便很难与复杂的模式匹配,因为网络将被填充过多的信息。一个设计良好的CNN架构会突出那些重要的信息,而将噪声忽略。1.get_shapeTensorFlow的输入流水线(用于读取和解码文件)拥有一种为使用一... -
寻找CNN的弱点
2019-06-21 08:32:11CNN是现在十分火热的模型,在很多图像检索问题上,CNN模型的效果在以往的基础上有了很大的提高,但是CNN毕竟没有把这些问题完全解决,CNN还是有它自己的弱点的。这个弱点也不能算作是它独有的问题,但是由于它的效果... -
目标检测R-CNN、Fast R-CNN、Faster R-CNN
2019-05-14 15:14:27R-CNN、Fast R-CNN、Faster R-CNN object detection我的理解,就是在给定的图片中精确找到物体所在位置,并标注出物体的类别。object detection要解决的问题就是物体在哪里,是什么这整个流程的问题。然而,这个问题... -
CNN练习
2019-04-03 19:34:42(3)运行extractCNN提取图片特征; (4)运行quaryInDatabase,更改查询ID更改查询图片进行检索; 小结: 1.database是才裁剪过得图片,可以查询返回所有相似图片; 如果是图片没有裁剪,比如谢霆锋那张图片查询不... -
CNN中的feature map (写的很好理解)
2020-07-13 10:44:50CNN中的feature map -
CNN训练
2017-04-28 11:20:27CNN训练技巧实现完成神经网络,通常需要验证网络的参数是否正确,通常有以下步骤: 1、检验前向传播的实现是否正确 采用很小的数据去验证 关闭正则项,若是softmax损失则loss值应当为log(C),其中C为类别数,若是... -
cnn 示意图_高效的 CNN 模型 MobileNets
2020-12-20 21:44:33CNN 已被广泛用在计算机视觉领域,但是近年来各种 CNN 模型复杂度越来越高,计算量越来越大,导致很多模型无法应用在移动端。本文介绍一种高效的 CNN 模型 MobileNets,MobileNets 使用 Depthwise Convolution 和 ... -
CNN可视化
2020-05-09 16:37:46目前没有完善的理论可以描述CNN的本质,人类也很难理解网络学到的表征以及作出的决策; 研究CNN可视化,有助于我们进一步理解CNN的本质,明白CNN的作用机理,帮助模型优化和debugging,促进CNN在实际任务中的应用... -
CNN基础
2017-04-26 09:58:021、CNN的引入传统的神经网络一般属于全连接神经网络,也就是说相邻的两层间中每一层的神经元都与另一层的神经元间有链接。这样的网络显然可以完整的保持输入数据的信息。但假设输入数据的维度较高(假设输入时图片,... -
图解何为CNN
2017-05-01 11:57:14参考CNN - Convolutional Neural Networks是近些年在机器视觉领域很火的模型,最先由 Yan Lecun 提出。 如果想学细节可以看 Andrej Karpathy 的 cs231n 。How does it work?给一张图片,每个圆负责处理图片的一... -
cnn池化层输入通道数_CNN网络基础
2021-01-01 14:21:43卷积神经网络(CNN),是深度学习中常见的一种网络结构,相比于传统的神经网络大量的神经节点带来的内存消耗过大和参数爆炸,CNN的出现很好解决了这一问题。一.CNN基本概念1.filter filter(过滤器),是用来在卷积过程... -
对R-CNN、Fast R-CNN与Faster R-CNN的浅显理解
2020-03-29 17:16:31一、物体识别与定位: ...如果一个图片中具有多个物体如何进行物体识别呢,你需要找很多位置,给出不同大小的框,对框内大小进行分类等等…… 所以这时候提出了R-CNN。我们不先给定框,而是先给定候选框(reg... -
CNN的理解
2019-06-27 11:44:00CNN最成功的应用是在CV,那为什么NLP和Speech的很多问题也可以用CNN解出来?为什么AlphaGo里也用了CNN?这几个不相关的问题的相似性在哪里?CNN通过什么手段抓住了这个共性? Deep Learning -Yann LeCun, Yoshua ... -
Mask R-CNN
2021-01-06 21:56:02Mask R-CNN训练简单,相对于Faster R-CNN,只需增加一个较小的开销,运行速度可达5 FPS,且很容易推广到其他任务中 二、模型与方法 2.1 设计思路 由于Fast/Faster R-CNN和FCN的出现,使得目标检测和语义分割的效果在... -
CNN做图片分类——李宏毅CNN作业记录
2020-08-18 17:58:02虽然这个模型最后的准确率只有53%,但我觉得还是很有必要记录下整个模型的流程的。 一方面是为了以后再复习CNN时可以快速想起来如何构建模型,另一方面是等以后等哪天突然知道我这个模型存在什么缺陷时,可以回过头...
-
TL1771 COG Application Guide_V0.3_20041202.pdf
-
用微服务spring cloud架构打造物联网云平台
-
MySQL 多实例安装 及配置主从复制实验环境
-
java正确的代码_java代码段正确与否
-
java百万级tcp ip_就这一次:TCP、IP、操作系统、Netty、算法一次性讲透
-
jdk8win64.zip
-
C++代码规范和Doxygen根据注释自动生成手册
-
投标方法论
-
用Go语言来写区块链(一)
-
新想法!Geoffrey Hinton独自署名44页论文,如何在神经网络中表示部分-整体层次结构,结构化表示获取可解释性
-
TL1771 & Hannstar FPC Application Guide_Ver2.0_20050615.pdf
-
零基础极简以太坊智能合约开发环境搭建并开发部署
-
IDEA Pushfailed:Unabletoacc...OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
-
java 字节数组作用_这段java代码中字节数组b起到了什么作用?
-
TL1771_V1.1_20050301.pdf
-
phpstudy 升级mysql版本后无法启动mysql问题
-
《计算机网络 自顶向下方法》第三章3.6 TCP连接管理
-
项目管理工具与方法
-
java比php_java比php难吗?
-
TL1771 Qualification Report_20050110.pdf