1、2D卷积
2、3D卷积
3D卷积相比2D卷积来说,多了一个(时间)维度。卷积核和map都是3维的。三维卷积常用于医学领域(CT影响),视频处理领域(检测动作及人物行为)
1、2D卷积
2、3D卷积
3D卷积相比2D卷积来说,多了一个(时间)维度。卷积核和map都是3维的。三维卷积常用于医学领域(CT影响),视频处理领域(检测动作及人物行为)
去年 1 月 6 日,OpenAI 发布了新模型 DALL·E,不用跨界也能从文本生成图像,打破了自然语言与视觉次元壁,引起了 AI 圈的一阵欢呼。时隔一年多后,DALL·E 迎来了升级版本——DALL·E 2。
DALL·E 2 是一个新的人工智能系统,可以根据自然语言的描述创建逼真的图像和艺术。
链接:
1、试玩 https://openai.com/dall-e-2/
2、论文地址:https://cdn.openai.com/papers/dall-e-2.pdf
3、github : https://github.com/lucidrains/DALLE2-pytorch
示例:
TEXT DESCRIPTION 文本描述:
An astronautTeddy bearsA bowl of soup
输出:
网络:(具体细节还没仔细看论文)
生成模型的迭代
DALL·E 2 建立在 CLIP 之上,OpenAI 研究科学家 Prafulla Dhariwal 说:「DALL·E 1 只是从语言中提取了 GPT-3 的方法并将其应用于生成图像:将图像压缩成一系列单词,并且学会了预测接下来会发生什么。」这是许多文本 AI 应用程序使用的 GPT 模型。但单词匹配并不一定能符合人们的预期,而且预测过程限制了图像的真实性。CLIP 旨在以人类的方式查看图像并总结其内容,OpenAI 迭代创建了一个 CLIP 的倒置版本——「unCLIP」,它能从描述生成图像,而 DALL·E 2 使用称为扩散(diffusion)的过程生成图像。
训练数据集由图像 x 及其对应的字幕 y 对 (x, y) 组成。给定图像 x, z_i 和 z_t 分别表示 CLIP 图像和文本嵌入。OpenAI 生成堆栈以使用两个组件从字幕生成图像:
解码器允许研究者在给定 CLIP 图像嵌入的情况下反演图像(invert images),而先验允许学习图像嵌入本身的生成模型。堆叠这两个组件产生一个图像 x 、给定字幕 y 的生成模型 P(x|y) :
DALL·E 的完整模型从未公开发布,但其他开发人员在过去一年中已经构建了一些模仿 DALL·E 功能的工具。最受欢迎的主流应用程序之一是 Wombo 的 Dream 移动应用程序,它能够根据用户描述的各种内容生成图片。
OpenAI 已经采取了一些内置的保护措施。该模型是在已剔除不良数据的数据集上进行训练的,理想情况下会限制其产生令人反感的内容的能力。为避免生成的图片被滥用,DALL·E 2 在生成的图片上都标有水印,以表明该作品是 AI 生成的。此外,该模型也无法根据名称生成任何可识别的面孔。DALL·E 2 将由经过审查的合作伙伴进行测试,但有一些要求:禁止用户上传或生成「可能造成伤害」的图像。他们还必须说明用 AI 生成图像的作用,并且不能通过应用程序或网站将生成的图像提供给其他人。
物体检测和分割应该算是计算机视觉中常用的而且也比较酷的任务。但相比图像分类,物体检测和分割任务难度更大,另外一点是就是代码实现也更复杂。对于物体检测和分割,目前有以下几个通用的开源项目:
1、detectron2
https://detectron2.readthedocs.io/en/latest/tutorials/index.html
2、coco
https://cocodataset.org/
3、MMDetection
MMDetection 是一个基于 PyTorch 的目标检测开源工具箱。它是 OpenMMLab 项目的一部分。
主分支代码目前支持 PyTorch 1.5 以上的版本。主要特性
除了 MMDetection 之外,我们还开源了计算机视觉基础库 MMCV,MMCV 是 MMDetection 的主要依赖。
https://github.com/open-mmlab/mmdetection
https://github.com/open-mmlab/mmdetection/blob/master/docs/zh_cn/article.md
Self Attention原理
self attention有什么优点呢,这里引用谷歌论文《Attention Is All You Need》里面说的,第一是计算复杂度小,第二是可以大量的并行计算,第三是可以更好的学习远距离依赖。Attention的计算公式如下:
下面一步步分解self attention的计算过程(图来自https://jalammar.github.io/illustrated-transformer/):
Self Attention代码实现
使用Keras自定义self attention层,代码如下:
from keras import initializersfrom keras import activationsfrom keras import backend as Kfrom keras.engine.topology import Layer class MySelfAttention(Layer): def __init__(self,output_dim,kernel_initializer='glorot_uniform',**kwargs): self.output_dim=output_dim self.kernel_initializer = initializers.get(kernel_initializer) super(MySelfAttention,self).__init__(**kwargs) def build(self,input_shape): self.W=self.add_weight(name='W', shape=(3,input_shape[2],self.output_dim), initializer=self.kernel_initializer, trainable=True) self.built = True def call(self,x): q=K.dot(x,self.W[0]) k=K.dot(x,self.W[1]) v=K.dot(x,self.W[2]) #print('q_shape:'+str(q.shape)) e=K.batch_dot(q,K.permute_dimensions(k,[0,2,1]))#把k转置,并与q点乘 e=e/(self.output_dim**0.5) e=K.softmax(e) o=K.batch_dot(e,v) return o def compute_output_shape(self,input_shape): return (input_shape[0],input_shape[1],self.output_dim)
Multi-Head Attention原理
不同的随机初始化映射矩阵Wq,Wk,Wv可以将输入向量映射到不同的子空间,这可以让模型从不同角度理解输入的序列。因此同时几个Attention的组合效果可能会优于单个Attenion,这种同时计算多个Attention的方法被称为Multi-Head Attention,或者多头注意力。
每个“Head”都会产生一个输出向量z,但是我们一般只需要一个,因此还需要一个矩阵把多个合并的注意力向量映射为单个向量。图示如下:
Multi-Head Attention代码实现
还是使用Keras实现multi-head attention,代码如下:
from keras import initializersfrom keras import activationsfrom keras import backend as Kfrom keras.engine.topology import Layer class MyMultiHeadAttention(Layer): def __init__(self,output_dim,num_head,kernel_initializer='glorot_uniform',**kwargs): self.output_dim=output_dim self.num_head=num_head self.kernel_initializer = initializers.get(kernel_initializer) super(MyMultiHeadAttention,self).__init__(**kwargs) def build(self,input_shape): self.W=self.add_weight(name='W', shape=(self.num_head,3,input_shape[2],self.output_dim), initializer=self.kernel_initializer, trainable=True) self.Wo=self.add_weight(name='Wo', shape=(self.num_head*self.output_dim,self.output_dim), initializer=self.kernel_initializer, trainable=True) self.built = True def call(self,x): q=K.dot(x,self.W[0,0]) k=K.dot(x,self.W[0,1]) v=K.dot(x,self.W[0,2]) e=K.batch_dot(q,K.permute_dimensions(k,[0,2,1]))#把k转置,并与q点乘 e=e/(self.output_dim**0.5) e=K.softmax(e) outputs=K.batch_dot(e,v) for i in range(1,self.W.shape[0]): q=K.dot(x,self.W[i,0]) k=K.dot(x,self.W[i,1]) v=K.dot(x,self.W[i,2]) #print('q_shape:'+str(q.shape)) e=K.batch_dot(q,K.permute_dimensions(k,[0,2,1]))#把k转置,并与q点乘 e=e/(self.output_dim**0.5) e=K.softmax(e) #print('e_shape:'+str(e.shape)) o=K.batch_dot(e,v) outputs=K.concatenate([outputs,o]) z=K.dot(outputs,self.Wo) return z def compute_output_shape(self,input_shape): return (input_shape[0],input_shape[1],self.output_dim)
Depthwise(DW)卷积与Pointwise(PW)卷积,合起来被称作Depthwise Separable Convolution(参见Google的Xception),该结构和常规卷积操作类似,可用来提取特征,但相比于常规卷积操作,其参数量和运算成本较低。所以在一些轻量级网络中会碰到这种结构如MobileNet。
摘自:https://zhuanlan.zhihu.com/p/80041030
对于一张5×5像素、三通道彩色输入图片(shape为5×5×3)。经过3×3卷积核的卷积层(假设输出通道数为4,则卷积核shape为3×3×3×4),最终输出4个Feature Map,如果有same padding则尺寸与输入层相同(5×5),如果没有则为尺寸变为3×3。
此时,卷积层共4个Filter,每个Filter包含了3个Kernel,每个Kernel的大小为3×3。因此卷积层的参数数量可以用如下公式来计算:
N_std = 4 × 3 × 3 × 3 = 108
Depthwise Separable Convolution是将一个完整的卷积运算分解为两步进行,即Depthwise Convolution与Pointwise Convolution。
Depthwise Convolution is a type of convolution where we apply a single convolutional filter for each input channel. In the regular 2D convolution performed over multiple input channels, the filter is as deep as the input and lets us freely mix channels to generate each element in the output. In contrast, depthwise convolutions keep each channel separate. To summarize the steps, we:
同于常规卷积操作,Depthwise Convolution的一个卷积核负责一个通道,一个通道只被一个卷积核卷积。上面所提到的常规卷积每个卷积核是同时操作输入图片的每个通道。
同样是对于一张5×5像素、三通道彩色输入图片(shape为5×5×3),Depthwise Convolution首先经过第一次卷积运算,不同于上面的常规卷积,DW完全是在二维平面内进行。卷积核的数量与上一层的通道数相同(通道和卷积核一一对应)。所以一个三通道的图像经过运算后生成了3个Feature map(如果有same padding则尺寸与输入层相同为5×5),如下图所示。
其中一个Filter只包含一个大小为3×3的Kernel,卷积部分的参数个数计算如下:
N_depthwise = 3 × 3 × 3 = 27
Depthwise Convolution完成后的Feature map数量与输入层的通道数相同,无法扩展Feature map。而且这种运算对输入层的每个通道独立进行卷积运算,没有有效的利用不同通道在相同空间位置上的feature信息。因此需要Pointwise Convolution来将这些Feature map进行组合生成新的Feature map。
Pointwise Convolution的运算与常规卷积运算非常相似,它的卷积核的尺寸为 1×1×M,M为上一层的通道数。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个卷积核就有几个输出Feature map。如下图所示。
由于采用的是1×1卷积的方式,此步中卷积涉及到的参数个数可以计算为:
N_pointwise = 1 × 1 × 3 × 4 = 12
经过Pointwise Convolution之后,同样输出了4张Feature map,与常规卷积的输出维度相同。
回顾一下,常规卷积的参数个数为:
N_std = 4 × 3 × 3 × 3 = 108
Separable Convolution的参数由两部分相加得到:
N_depthwise = 3 × 3 × 3 = 27
N_pointwise = 1 × 1 × 3 × 4 = 12
N_separable = N_depthwise + N_pointwise = 39
相同的输入,同样是得到4张Feature map,Separable Convolution的参数个数是常规卷积的约1/3。因此,在参数量相同的前提下,采用Separable Convolution的神经网络层数可以做的更深。
数据集下载地址:http://m6z.cn/5MjlYkAI-TOD 在 28,036 张航拍图像中包含 8 个类别的 700,621 个对象实例。与现有航拍图像中的目标检测数据集相比,AI-TOD 中目标的平均大小约为 12.8 像素,远小于其他数据集。
数据集下载地址:http://m6z.cn/6nUrYe现有的 Earth Vision 数据集要么适用于语义分割,要么适用于对象检测。iSAID 是第一个用于航空图像实例分割的基准数据集。这个大规模和密集注释的数据集包含 2,806 张高分辨率图像的 15 个类别的 655,451 个对象实例。iSAID 的显着特征如下:(a) 大量具有高空间分辨率的图像,(b) 十五个重要且常见的类别,(c) 每个类别的大量实例,(d) 每个类别的大量标记实例图像,这可能有助于学习上下文信息,(e) 巨大的对象尺度变化,通常在同一图像内包含小、中和大对象,(f) 图像内具有不同方向的对象的不平衡和不均匀分布,描绘真实-生活空中条件,(g)几个小尺寸物体,外观模糊,只能通过上下文推理来解决,(h)由专业注释者执行的精确实例级注释,由符合良好规范的专家注释者交叉检查和验证定义的指导方针。
数据集下载地址:http://m6z.cn/6vqF3T在 TinyPerson 中有 1610 个标记图像和 759 个未标记图像(两者主要来自同一视频集),总共有 72651 个注释。
数据集下载地址:http://m6z.cn/5xgYdYDeepScores 数据集的目标是推进小物体识别的最新技术,并将物体识别问题置于场景理解的背景下。DeepScores 包含高质量的乐谱图像,分为 300 0 000 张书面音乐,其中包含不同形状和大小的符号。拥有近一亿个小对象,这使得我们的数据集不仅独一无二,而且是最大的公共数据集。DeepScores 带有用于对象分类、检测和语义分割的基本事实。因此,DeepScores 总体上对计算机视觉提出了相关挑战,超出了光学音乐识别 (OMR) 研究的范围。
数据集下载地址:http://m6z.cn/6nUs1CWiderPerson 数据集是野外行人检测基准数据集,其图像选自广泛的场景,不再局限于交通场景。我们选择了 13,382 张图像并标记了大约 400K 带有各种遮挡的注释。我们随机选择 8000/1000/4382 图像作为训练、验证和测试子集。与 CityPersons 和 WIDER FACE 数据集类似,我们不发布测试图像的边界框基本事实。用户需要提交最终的预测文件,我们将进行评估。
数据集下载地址:http://m6z.cn/5N3Yk7加州理工学院行人数据集由大约 10 小时的 640×480 30Hz 视频组成,该视频取自在城市环境中通过常规交通行驶的车辆。注释了大约 250,000 帧(在 137 个大约分钟长的片段中),总共 350,000 个边界框和 2300 个独特的行人。注释包括边界框和详细的遮挡标签之间的时间对应关系。
数据集下载地址:http://m6z.cn/5UAbEWNWPU VHR-10 Dataset 是一个用于空间物体检测的 10 级地理遥感数据集,其拥有 650 张包含目标的图像和 150 张背景图像,共计 800 张,目标种类包括飞机、舰船、油罐、棒球场、网球场、篮球场、田径场、港口、桥梁和汽车共计 10 个类别。该数据集由西北工业大学于 2014 年发布,相关论文有《Multi-class geospatial object detection and geographic imageclassification based on collection of part detectors》、《A survey on objectdetection in optical remote sensing images》和《Learningrotation-invariant convolutional neural networks for object detection in VHRoptical remote sensing images》。
数据集下载地址:http://m6z.cn/6nUs6sInria 航空影像标注解决了遥感中的一个核心主题:航空影像的自动像素级标注(论文链接)。数据集特点:
数据集下载地址:http://m6z.cn/5EN96H它是一个开放的遥感图像目标检测数据集。数据集包括飞机、油箱、游乐场和立交桥。此数据集的格式为PASCAL VOC。数据集包括4个文件,每个文件用于一种对象。
数据集下载地址:http://m6z.cn/616t6R从Internet(例如YouTube或Google)上的图像/视频收集的四个小物体数据集,包括4种类型的图像,可用于小物体目标检测的研究。数据集包含四类:
COCO2017是2017年发布的COCO数据集的一个版本,主要用于COCO在2017年后持有的物体检测任务、关键点检测任务和全景分割任务。
数据集链接:http://m6z.cn/6fzn0f该数据集由早期火灾和烟雾的图像数据集组成。数据集由在真实场景中使用手机拍摄的早期火灾和烟雾图像组成。大约有7000张图像数据。图像是在各种照明条件(室内和室外场景)、天气等条件下拍摄的。该数据集非常适合早期火灾和烟雾探测。数据集可用于火灾和烟雾识别、检测、早期火灾和烟雾、异常检测等。数据集还包括典型的家庭场景,如垃圾焚烧、纸塑焚烧、田间作物焚烧、家庭烹饪等。本文仅含100张左右。
数据集链接:http://m6z.cn/6vIKlJDOTA是用于航空图像中目标检测的大型数据集。它可以用于开发和评估航空图像中的目标探测器。这些图像是从不同的传感器和平台收集的。每个图像的大小在800×800到20000×20000像素之间,包含显示各种比例、方向和形状的对象。DOTA图像中的实例由航空图像解释专家通过任意(8 d.o.f.)四边形进行注释。
数据集链接:http://m6z.cn/5DdJL1该数据库由七个不同织物结构的245张4096 x 256像素图像组成。数据库中有140个无缺陷图像,每种类型的织物20个,除此之外,有105幅纺织行业中常见的不同类型的织物缺陷(12种缺陷)图像。图像的大尺寸允许用户使用不同的窗口尺寸,从而增加了样本数量。
数据集链接:http://m6z.cn/5wnucm该数据集采集的目标为工业应用、纹理很少的目标,同时缺乏区别性的颜色,且目标具有对称性和互相关性,数据集由三个同步的传感器获得,一个结构光传感器,一个RGBD sensor,一个高分辨率RGBsensor,从每个传感器分别获得了3.9w训练集和1w测试集,此外为每个目标创建了2个3D model,一个是CAD手工制作的另一个是半自动重建的。训练集图片的背景大多是黑色的,而测试集的图片背景很多变,会包含不同光照、遮挡等等变换(之所以这么做作者说是为了使任务更具有挑战性)。同时作者解释了本数据集的优势在于:1.大量跟工业相关的目标;2.训练集都是在可控的环境下抓取的;3.测试集有大量变换的视角;4.图片是由同步和校准的sensor抓取的;5.准确的6D pose标签;6.每个目标有两种3D模型;
数据集链接:http://m6z.cn/6fzmQfH²O由V-COCO数据集中的10301张图像组成,其中添加了3635张图像,这些图像主要包含人与人之间的互动。所有的H²O图像都用一种新的动词分类法进行了注释,包括人与物和人与人之间的互动。该分类法由51个动词组成,分为5类:
数据集链接:http://m6z.cn/5ZMmRG图像中的垃圾(GINI)数据集是SpotGarbage引入的一个数据集,包含2561张图像,956张图像包含垃圾,其余的是在各种视觉属性方面与垃圾非常相似的非垃圾图像。
数据集链接:http://m6z.cn/5KJWJANAO包含7934张图像和9943个对象,这些图像未经修改,代表了真实世界的场景,但会导致最先进的检测模型以高置信度错误分类。与标准MSCOCO验证集相比,在NAO上评估时,EfficientDet-D7的平均精度(mAP)下降了74.5%。
数据集链接:http://m6z.cn/5Sg9NXLabelme Dataset 是用于目标识别的图像数据集,涵盖 1000 多个完全注释和 2000 个部分注释的图像,其中部分注释图像可以被用于训练标记算法 ,测试集拥有来自于世界不同地方拍摄的图像,这可以保证图片在续联和测试之间会有较大的差异。该数据集由麻省理工学院 –计算机科学和人工智能实验室于 2007 年发布,相关论文有《LabelMe: a database and web-based tool for image annotation》。
数据集链接:http://m6z.cn/6uxAIx该数据集包括小众印度车辆的图像,如Autorikshaw、Tempo、卡车等。该数据集由用于分类和目标检测的小众印度车辆图像组成。据观察,这些小众车辆(如autorickshaw、tempo、trucks等)上几乎没有可用的数据集。这些图像是在白天、晚上和晚上的不同天气条件下拍摄的。该数据集具有各种各样的照明、距离、视点等变化。该数据集代表了一组非常具有挑战性的利基类车辆图像。该数据集可用于驾驶员辅助系统、自动驾驶等的图像识别和目标检测。
数据集链接:http://m6z.cn/5DdK0v椅子数据集包含大约1000个不同三维椅子模型的渲染图像。
数据集链接:http://m6z.cn/60wX8rSUN09数据集包含12000个带注释的图像,其中包含200多个对象类别。它由自然、室内和室外图像组成。每个图像平均包含7个不同的注释对象,每个对象的平均占用率为图像大小的5%。对象类别的频率遵循幂律分布。发布者使用 397 个采样良好的类别进行场景识别,并以此搭配最先进的算法建立新的性能界限。该数据集由普林斯顿视觉与机器人实验室于 2014 年发布,相关论文有《SUN Database: Large-scale Scene Recognition from Abbey to Zoo》、《SUN Database: Exploring a Large Collection of Scene Categories》。
数据集链接:http://m6z.cn/5wnuoM使用迄今为止公开共享的全球最大的开放检索信息数据集。Unsplash数据集由250000多名贡献摄影师创建,并包含了数十亿次照片搜索的信息和对应的照片信息。由于Unsplash数据集中包含广泛的意图和语义,它为研究和学习提供了新的机会。
数据集链接:http://m6z.cn/5DdK6DHICO-DET是一个用于检测图像中人-物交互(HOI)的数据集。它包含47776幅图像(列车组38118幅,测试组9658幅),600个HOI类别,由80个宾语类别和117个动词类别构成。HICO-DET提供了超过150k个带注释的人类对象对。V-COCO提供了10346张图像(2533张用于培训,2867张用于验证,4946张用于测试)和16199人的实例。
数据集链接:http://m6z.cn/5Sgafn上海科技数据集是一个大规模的人群统计数据集。它由1198张带注释的群组图像组成。数据集分为两部分,A部分包含482张图像,B部分包含716张图像。A部分分为训练和测试子集,分别由300和182张图像组成。B部分分为400和316张图像组成的序列和测试子集。群组图像中的每个人都有一个靠近头部中心的点进行注释。总的来说,该数据集由33065名带注释的人组成。A部分的图像是从互联网上收集的,而B部分的图像是在上海繁忙的街道上收集的。
数据集链接:http://m6z.cn/6n5Adu大约9000多张独特的图片。该数据集由印度国内常见垃圾对象的图像组成。图像是在各种照明条件、天气、室内和室外条件下拍摄的。该数据集可用于制作垃圾/垃圾检测模型、环保替代建议、碳足迹生成等。
数据集下载地址:http://m6z.cn/61z9Fv当前大多数高级人脸识别方法都是基于深度学习而设计的,深度学习取决于大量人脸样本。但是,目前尚没有公开可用的口罩遮挡人脸识别数据集。为此,这项工作提出了三种类型的口罩遮挡人脸数据集,包括口罩遮挡人脸检测数据集(MFDD),真实口罩遮挡人脸识别数据集(RMFRD)和模拟口罩遮挡人脸识别数据集(SMFRD)。基于这些数据集,可以开发口罩遮挡人脸的各种应用。本项目开发的多粒度口罩遮挡人脸识别模型可达到95%的准确性,超过了行业报告的结果。
数据集下载地址:http://m6z.cn/5wJJLA德国交通标志基准测试是在 2011 年国际神经网络联合会议 (IJCNN) 上举办的多类单图像分类挑战赛。我们诚邀相关领域的研究人员参与:该比赛旨在参与者无需特殊领域知识。我们的基准测试具有以下属性:
数据集下载地址:http://m6z.cn/5U2X4u该数据集中含有自行车、摩托车、汽车、货车的图像数据,可用于CNN模型以实现车辆识别和车辆分类,其中自行车、摩托车、汽车数据来自2005 PASCAL视觉类挑战赛(VOC2005)所使用的数据的筛选处理结果,货车图片来自网络收集,后期通过筛选处理得到。在本数据中,训练数据集与测试数据集占比约为5:1。
数据集下载地址:http://m6z.cn/5TikF9WGISD(Wine Grape Instance Segmentation Dataset)是为了提供图像和注释来研究对象检测和实例分割,用于葡萄栽培中基于图像的监测和现场机器人技术。它提供了来自五种不同葡萄品种的实地实例。这些实例显示了葡萄姿势、光照和焦点的变化,包括遗传和物候变化,如形状、颜色和紧实度。可能的用途包括放宽实例分割问题:分类(图像中是否有葡萄?)、语义分割(图像中的“葡萄像素”是什么?)、对象检测(图像中的葡萄在哪里?)、和计数(每个簇有多少浆果?)。
数据集下载地址:http://m6z.cn/5wJK64检测小麦穗是一项重要任务,可以估计相关性状,包括穗种群密度和穗特征,如卫生状况、大小、成熟阶段和芒的存在。本数据集包含 4,700 张高分辨率 RGB 图像和 190,000 个标记的小麦头,这些小麦头采集自世界各地不同生长阶段的不同基因型的多个国家。
数据集下载地址:http://m6z.cn/68ldS0通过记录超过 350 公里的瑞典高速公路和城市道路的序列,创建了一个数据集。一个 1.3 兆像素的彩色摄像机,一个点灰色变色龙,被放置在一辆汽车的仪表板上,从前窗向外看。摄像头略微指向右侧,以便尽可能多地覆盖相关标志。该镜头的焦距为 6.5 毫米,视野约为 41 度。高速公路上的典型速度标志大约为 90 cm 宽,如果要在大约 30 m 的距离处检测到它们,则对应于大约 50 像素的大小。总共记录了超过 20 000 帧,其中每五帧被手动标记。每个标志的标签包含标志类型(人行横道、指定车道右侧、禁止站立或停车、优先道路、让路、50 公里/小时或 30 公里/小时)、能见度状态(遮挡、模糊或可见)和道路状态(是否标志是在正在行驶的道路上或在小路上)。
数据集下载地址:http://m6z.cn/61zarT包含 774 个众包图像和 698 个网络挖掘图像。众包和网络挖掘的图像分别包含 2,496 和 2,230 个工人实例。
数据集链接:https://beerys.github.io/CaltechCameraTraps/该数据集包含来自美国西南部 140 个摄像头位置的 243,100 张图像,带有 21 个动物类别的标签(加上空白),主要是在物种级别(例如,最常见的标签是负鼠、浣熊和土狼),以及 大约 66,000 个边界框注释。大约 70% 的图像被标记为空。
数据集下载地址:http://m6z.cn/6nnDQK该数据来自 J-EDI 海洋垃圾数据集。构成该数据集的视频在质量、深度、场景中的对象和使用的相机方面差异很大。它们包含许多不同类型的海洋垃圾的图像,这些图像是从现实世界环境中捕获的,提供了处于不同衰减、遮挡和过度生长状态的各种物体。此外,水的清晰度和光的质量因视频而异。这些视频经过处理以提取 5,700 张图像,这些图像构成了该数据集,所有图像都在垃圾实例、植物和动物等生物对象以及 ROV 上标有边界框。
数据集下载地址:http://m6z.cn/5wJJTa本数据集汇总了700个在坑洼处带有3K +注释的图像,用于从道路图像中检测坑洼,检测道路地形和坑洼。
数据集下载地址:http://m6z.cn/61EksR大赛数据集里有1万份来自实际生产中有瑕疵的铝型材监测影像数据,每个影像包含一个或多种瑕疵。供机器学习的样图会明确标识影像中所包含的瑕疵类型。
数据集下载地址:http://m6z.cn/61Ekw5在布匹的实际生产过程中,由于各方面因素的影响,会产生污渍、破洞、毛粒等瑕疵,为保证产品质量,需要对布匹进行瑕疵检测。布匹疵点检验是纺织行业生产和质量管理的重要环节,目前人工检测易受主观因素影响,缺乏一致性;并且检测人员在强光下长时间工作对视力影响极大。由于布匹疵点种类繁多、形态变化多样、观察识别难道大,导致布匹疵点智能检测是困扰行业多年的技术瓶颈。本数据涵盖了纺织业中布匹的各类重要瑕疵,每张图片含一个或多种瑕疵。数据包括包括素色布和花色布两类,其中,素色布数据约8000张;花色布数据约12000张。
数据集下载地址:http://m6z.cn/5U87us数据集收集了夹杂、划痕、压入氧化皮、裂纹、麻点和斑块6种缺陷,每种缺陷300张,图像尺寸为200×200。数据集包括分类和目标检测两部分,不过目标检测的标注中有少量错误,需要注意。
数据集下载地址:http://m6z.cn/61EkBp该数据集中提供了四种类型的带钢表面缺陷。训练集共有12568张,测试集5506张。图像尺寸为1600×256。
数据集下载地址:http://m6z.cn/61EkUh该数据集包含了7种带钢缺陷类型。这个数据集不是图像数据,而是带钢缺陷的28种特征数据,可用于机器学习项目。钢板故障的7种类型:装饰、Z_划痕、K_划痕、污渍、肮脏、颠簸、其他故障。
数据集下载地址:http://m6z.cn/5F5eQV该数据集主要针对纹理背景上的杂项缺陷,为较弱监督的训练数据。包含十个数据集,前六个为训练数据集,后四个为测试数据集。每个数据集均包含以灰度8位PNG格式保存的1000个“无缺陷”图像和150个“有缺陷”图像,每个数据集由不同的纹理模型和缺陷模型生成。“无缺陷”图像显示的背景纹理没有缺陷,“无缺陷”图像的背景纹理上恰好有一个标记的缺陷。所有数据集已随机分为大小相等的训练和测试子数据集。弱标签以椭圆形表示,大致表示缺陷区域。
数据集下载地址:http://m6z.cn/5F5eSd中国科学院自动所一个课题组收集的数据集,是“Saliency of magnetic tile surface defects”这篇论文的数据集。收集了6种常见磁瓦缺陷的图像,并做了语义分割的标注。
数据集下载地址:http://m6z.cn/61EkKLRSDDs数据集包含两种类型的数据集:第一种是从快车道捕获的I型RSDDs数据集,其中包含67个具有挑战性的图像。第二个是从普通/重型运输轨道捕获的II型RSDDs数据集,其中包含128个具有挑战性的图像。两个数据集的每幅图像至少包含一个缺陷,并且背景复杂且噪声很大。RSDDs数据集中的这些缺陷已由一些专业的人类观察员在轨道表面检查领域进行了标记。
数据集下载地址:http://m6z.cn/61EkMHKTH-TIPS 是一个纹理图像数据集,在不同的光照、角度和尺度下拍摄的不同材质表面纹理图片。类型包括砂纸、铝箔、发泡胶、海绵、灯芯绒、亚麻、棉、黑面包、橙皮和饼干共10类。
数据集下载地址:http://m6z.cn/5U87Ji这是一个公共的合成PCB数据集,由北京大学发布,其中包含1386张图像以及6种缺陷(缺失孔,鼠咬坏,开路,短路,杂散,伪铜),用于检测,分类和配准任务。
数据集下载地址:http://m6z.cn/6gGnTdIMDB-WIKI 500k+ 是一个包含名人人脸图像、年龄、性别的数据集,图像和年龄、性别信息从 IMDB 和 WiKi 网站抓取,总计 524230 张名人人脸图像及对应的年龄和性别。其中,获取自 IMDB 的 460723 张,获取自 WiKi 的 62328 张。
数据集下载地址:http://m6z.cn/5Nm7gpWIDER FACE数据集是人脸检测的一个benchmark数据集,包含32203图像,以及393,703个标注人脸,其中,158,989个标注人脸位于训练集,39,,496个位于验证集。每一个子集都包含3个级别的检测难度:Easy,Medium,Hard。这些人脸在尺度,姿态,光照、表情、遮挡方面都有很大的变化范围。WIDER FACE选择的图像主要来源于公开数据集WIDER。制作者来自于香港中文大学,他们选择了WIDER的61个事件类别,对于每个类别,随机选择40%10%50%作为训练、验证、测试集。
数据集下载地址:http://m6z.cn/61EnzL该数据集是用于研究无约束面部识别问题的面部照片数据库。数据集包含从网络收集的13000多张图像。每张脸都贴上了所画的人的名字,图片中的1680人在数据集中有两个或更多不同的照片。
数据集下载地址:http://m6z.cn/5F5hLpGENKI数据集是由加利福尼亚大学的机器概念实验室收集。该数据集包含GENKI-R2009a,GENKI-4K,GENKI-SZSL三个部分。GENKI-R2009a包含11159个图像,GENKI-4K包含4000个图像,分为“笑”和“不笑”两种,每个图片的人脸的尺度大小,姿势,光照变化,头的转动等都不一样,专门用于做笑脸识别。GENKI-SZSL包含3500个图像,这些图像包括广泛的背景,光照条件,地理位置,个人身份和种族等。
数据集链接:http://m6z.cn/5DlIR9PubFig Dataset 是一个大型人脸数据集,主要用于人脸识别和身份鉴定,其涵盖互联网上 200 人的 58,797 张图像,不同于大多数现有面部数据集,这些图像是在主体完全不受控制的情况下拍摄的,因此不同图像中姿势、光照、表情、场景、相机、成像条件和参数存在较大差异,该数据集类似于 UMass-Amherst 创建的 LFW 数据集。该数据集由哥伦比亚大学于 2009 年发布,相关论文有《Attribute and Simile Classifiers for Face Verification》。
数据集链接:http://m6z.cn/60EW0nCelebFaces Attributes Dataset (CelebA) 是一个大规模的人脸属性数据集,包含超过 20 万张名人图像,每张都有 40 个属性注释。该数据集中的图像涵盖了较大的姿势变化和杂乱的背景。CelebA 种类多、数量多、注释丰富,包括10,177 个身份,202,599 张人脸图像,以及5 个地标位置,每张图像 40 个二进制属性注释。该数据集可用作以下计算机视觉任务的训练和测试集:人脸属性识别、人脸识别、人脸检测、地标(或人脸部分)定位以及人脸编辑与合成。
数据集链接:http://m6z.cn/5So6DB为促进人脸识别算法的研究和实用化,美国国防部的Counterdrug Technology Transfer Program(CTTP)发起了一个人脸识别技术(Face Recognition Technology 简称FERET)工程,它包括了一个通用人脸库以及通用测试标准。到1997年,它已经包含了1000多人的10000多张照片,每个人包括了不同表情,光照,姿态和年龄的照片。
数据集链接:http://m6z.cn/6fHmaT该数据集包含 12,995 张人脸图像,这些图像用 (1) 五个面部标志,(2) 性别、微笑、戴眼镜和头部姿势的属性进行了注释。
数据集链接:http://m6z.cn/5ZUjyC这个数据集包含了1521幅分辨率为384×286像素的灰度图像。每一幅图像来自于23个不同的测试人员的正面角度的人脸。为了便于做比较,这个数据集也包含了对人脸图像对应的手工标注的人眼位置文件。图像以 “BioID_xxxx.pgm”的格式命名,其中xxxx代表当前图像的索引(从0开始)。类似的,形如”BioID_xxxx.eye”的文件包含了对应图像中眼睛的位置。
数据集链接:http://m6z.cn/5So6vR该数据集所选用的人脸照片均来自于两部比较知名的电视剧,《吸血鬼猎人巴菲》和《生活大爆炸》。
数据集链接:http://m6z.cn/5vPwfOCMU PIE人脸库建立于2000年11月,它包括来自68个人的40000张照片,其中包括了每个人的13种姿态条件,43种光照条件和4种表情下的照片,现有的多姿态人脸识别的文献基本上都是在CMU PIE人脸库上测试的。
数据集链接:http://m6z.cn/6u3P2V该数据集包含 1,595 个不同人的 3,425 个视频。所有视频都是从 YouTube 下载的。每个主题平均有 2.15 个视频可用。最短剪辑时长为 48 帧,最长剪辑为 6070 帧,视频剪辑的平均长度为 181.3 帧。在这个数据集下,算法需要判断两段视频里面是不是同一个人。有不少在照片上有效的方法,在视频上未必有效/高效。
数据集链接:http://m6z.cn/5vPwioCASIA 人脸图像数据库版本 5.0(或 CASIA-FaceV5)包含 500 个对象的 2,500 个彩色人脸图像。CASIA-FaceV5 的面部图像是使用罗技 USB 摄像头在一个会话中捕获的。CASIA-FaceV5的志愿者包括研究生、工人、服务员等。所有人脸图像均为16位彩色BMP文件,图像分辨率为640*480。典型的类内变化包括照明、姿势、表情、眼镜、成像距离等。
数据集链接:http://m6z.cn/5So6VP该数据集包含通过在谷歌图片搜索中输入常见的名字从网络上收集的人的图像。每个正面的眼睛、鼻子和嘴巴中心的坐标在地面实况文件中提供。此信息可用于对齐和裁剪人脸或作为人脸检测算法的基本事实。该数据集有 10,524 个不同分辨率和不同设置的人脸,例如 肖像图像、人群等。侧面或非常低分辨率的面孔未标记。
数据集链接:http://m6z.cn/69aaIeMPII Human Shape 人体模型数据是一系列人体轮廓和形状的3D模型及工具。模型是从平面扫描数据库 CAESAR 学习得到。
数据集链接:http://m6z.cn/6gGnPbMPII 人体姿态数据集是用于评估人体关节姿势估计的最先进基准。该数据集包括大约 25,000 张图像,其中包含超过 40,000 个带有注释身体关节的人。这些图像是使用已建立的人类日常活动分类法系统收集的。总的来说,数据集涵盖了 410 项人类活动,每个图像都提供了一个活动标签。每张图像都是从 YouTube 视频中提取的,并提供前后未注释的帧。此外,测试集有更丰富的注释,包括身体部位遮挡和 3D 躯干和头部方向。
数据集链接:http://m6z.cn/692agI作者收集了一个带有注释关节的足球运动员数据集,可用于多视图重建。数据集包括:
数据集链接:http://m6z.cn/692akKPenn Action Dataset(宾夕法尼亚大学)包含 15 个不同动作的 2326 个视频序列以及每个序列的人类联合注释。
数据集链接:http://m6z.cn/5xr6XqBBC Pose 包含 20 个视频(每个视频长度为 0.5 小时至 1.5 小时),由 BBC 录制,并配有手语翻译。这 20 个视频分为 10 个用于训练的视频、5 个用于验证的视频和 5 个用于测试的视频。
数据集链接:http://m6z.cn/6gynqzPoser 数据集是用于姿态估计的数据集,由 1927 个训练图像和 418 个测试图像组成。这些图像是综合生成的,并调整为单峰预测。这些图像是使用 Poser 软件包生成的。
数据集链接:http://m6z.cn/5xr6Z2“野外 3D 姿势数据集”是野外第一个具有准确 3D 姿势用于评估的数据集。虽然存在户外其他数据集,但它们都仅限于较小的记录量。3DPW 是第一个包含从移动电话摄像头拍摄的视频片段的技术。数据集包括:
数据集链接:http://m6z.cn/5UGaiiV-COCO是一个基于 COCO 的数据集,用于人机交互检测。V-COCO 提供 10,346 张图像(2,533 张用于训练,2,867 张用于验证,4,946 张用于测试)和 16,199 个人物实例。每个人都有 29 个动作类别的注释,并且没有包括对象在内的交互标签。
数据集链接:http://m6z.cn/692aos宜家 ASM 数据集是装配任务的多模式和多视图视频数据集,可对人类活动进行丰富的分析和理解。它包含 371 个家具组件样本及其真实注释。每个样本包括 3 个 RGB 视图、一个深度流、原子动作、人体姿势、对象片段、对象跟踪和外部相机校准。
数据集链接:http://m6z.cn/62cnp5这是一个立体图像对数据集,适用于上身人的立体人体姿态估计。SHPED 由 630 个立体图像对(即 1260 个图像)组成,分为 42 个视频片段,每个片段 15 帧。这些剪辑是从 26 个立体视频中提取的,这些视频是从 YouTube 获得的,标签为 yt3d:enable = true。此外,SHPED 包含 1470 条火柴人上身注释,对应于 49 个人根据这些条件:直立位置、所有上身部分几乎可见以及身体的非侧面视点。
数据集链接:http://m6z.cn/5xr6M8AIST++ 舞蹈动作数据集是从 AIST 舞蹈视频数据库构建的。对于多视图视频,设计了一个精心设计的管道来估计相机参数、3D 人体关键点和 3D 人体舞蹈动作序列:它为 1010 万张图像提供 3D 人体关键点注释和相机参数,涵盖 9 个视图中的 30 个不同主题。这些属性使其成为具有 3D 人体关键点注释的最大和最丰富的现有数据集。它还包含 1,408 个 3D 人类舞蹈动作序列,表示为关节旋转以及根轨迹。舞蹈动作平均分布在 10 种舞蹈流派中,有数百种编舞。运动持续时间从 7.4 秒不等。至 48.0 秒。所有的舞蹈动作都有相应的音乐。
数据集链接:http://m6z.cn/6o4AAg该数据集专注于在各种人群和复杂事件中进行非常具有挑战性和现实性的以人为中心的分析任务,包括地铁上下车、碰撞、战斗和地震逃生。并且具有大规模和密集注释的标签,涵盖了以人为中心的分析中的广泛任务。
数据集链接:http://m6z.cn/5xz4OW道路和车道估计基准包括289次培训和290幅测试图像。我们在鸟瞰空间中评估道路和车道的估计性能。它包含不同类别的道路场景:城市无标记、城市标记、 城市多条标记车道以及以上三者的结合。
数据集链接:http://m6z.cn/5xz4QoCrackForest数据集是一个带注释的道路裂缝图像数据库,可以大致反映城市路面状况。
数据集链接:http://m6z.cn/6gGlltstero 2015 基准测试包含 200 个训练场景和 200 个测试场景(每个场景 4 幅彩色图像,以无损 png 格式保存)。与stereo 2012 和flow 2012 基准测试相比,它包含动态场景,在半自动过程中为其建立了真值。该数据集是通过在卡尔斯鲁厄中等规模城市、农村地区和高速公路上行驶而捕获的。每张图像最多可以看到 15 辆汽车和 30 名行人。
数据集下载地址:http://www.cvlibs.net/datasets/kitti/eval_scene_flow.php?benchmark=flowFlow 2015 基准测试包含 200 个训练场景和 200 个测试场景(每个场景 4 幅彩色图像,以无损 png 格式保存)。与stereo 2012 和flow 2012 基准测试相比,它包含动态场景,在半自动过程中为其建立了真值。该数据集是通过在卡尔斯鲁厄中等规模城市、农村地区和高速公路上行驶而捕获的。每张图像最多可以看到 15 辆汽车和 30 名行人。
数据集下载地址:http://www.cvlibs.net/datasets/kitti/eval_scene_flow.phpSceneflow 2015 基准测试包含 200 个训练场景和 200 个测试场景(每个场景 4 幅彩色图像,以无损 png 格式保存)。与stereo 2012 和flow 2012 基准测试相比,它包含动态场景,在半自动过程中为其建立了真值。该数据集是通过在卡尔斯鲁厄中等规模城市、农村地区和高速公路上行驶而捕获的。每张图像最多可以看到 15 辆汽车和 30 名行人。
数据集下载地址:http://www.cvlibs.net/datasets/kitti/eval_depth_all.phpKITTI-depth 包含超过 93,000 个深度图以及相应的原始 LiDaR 扫描和 RGB 图像。鉴于大量的训练数据,该数据集应允许训练复杂的深度学习模型,以完成深度补全和单幅图像深度预测的任务。此外,该数据集提供了带有未发布深度图的手动选择图像,作为这两个具有挑战性的任务的基准。
数据集下载地址:http://m6z.cn/61EogvALOV++,Amsterdam Library of Ordinary Videos for tracking 是一个物体追踪视频数据,旨在对不同的光线、通透度、泛着条件、背景杂乱程度、焦距下的相似物体的追踪。
数据集下载地址:http://m6z.cn/6gGlzF由布朗大学发布的人类动作视频数据集,该数据集视频多数来源于电影,还有一部分来自公共数据库以及YouTube等网络视频库。数据库包含有6849段样本,分为51类,每类至少包含有101段样本。
数据集下载地址:http://m6z.cn/69a8xyUCF50 是一个由中佛罗里达大学发布的动作识别数据集,由来自 youtube 的真实视频组成,包含 50 个动作类别,如棒球投球、篮球投篮、卧推、骑自行车、骑自行车、台球、蛙泳、挺举、跳水、击鼓等。对于所有 50 个类别,视频分为 25 组,其中每组由超过 4 个动作剪辑。同一组中的视频片段可能具有一些共同的特征,例如同一个人、相似背景、相似视点等。
数据集下载地址:http://m6z.cn/6vILNpSBU Kinect Interaction是一个复杂的人类活动数据集,描述了两个人的交互,包括同步视频、深度和运动捕捉数据。
数据集下载地址:http://m6z.cn/5TAgdC一个包含 37 个类别的宠物数据集,每个类别大约有 200 张图像。这些图像在比例、姿势和照明方面有很大的变化。所有图像都有相关的品种、头部 ROI 和像素级三元图分割的地面实况注释。
数据集下载地址:http://m6z.cn/5TAgbwCAT 数据集包括超过 9,000 张猫图像。对于每张图像,猫的头部都有九个点的注释,眼睛两个,嘴巴一个,耳朵六个。
数据集下载地址:http://m6z.cn/6nF6kM斯坦福狗数据集包含来自世界各地的 120 种狗的图像。该数据集是使用 ImageNet 中的图像和注释构建的,用于细粒度图像分类任务。该数据集的内容:
数据集下载地址:http://m6z.cn/5TAgeAStreetScenes Challenge Framework 是用于对象检测的图像、注释、软件和性能测量的集合。每张图像都是从马萨诸塞州波士顿及其周边地区的 DSC-F717 相机拍摄的。然后用围绕 9 个对象类别的每个示例的多边形手动标记每个图像,包括 [汽车、行人、自行车、建筑物、树木、天空、道路、人行道和商店]。这些图像的标记是在仔细检查下完成的,以确保对象总是以相同的方式标记,关于遮挡和其他常见的图像变换。
数据集下载地址:http://m6z.cn/616wopCars 数据集包含 196 类汽车的 16,185 张图像。数据分为 8,144 个训练图像和 8,041 个测试图像,其中每个类别大致按 50-50 分割。课程通常在品牌、型号、年份级别,例如 2012 Tesla Model S 或 2012 BMW M3 coupe。
MNIST数据集是一个手写阿拉伯数字图像识别数据集,图片分辨率为 20×20 灰度图图片,包含‘0 – 9’ 十组手写手写阿拉伯数字的图片。其中,训练样本 60000 ,测试样本 10000,数据为图片的像素点值,作者已经对数据集进行了压缩。
该数据集是图片数据,分为训练集85%(Train)和测试集15%(Test)。其中O代表Organic(有机垃圾),R代表Recycle(可回收)
数据集下载地址:http://m6z.cn/5ExMWbSVHN 是一个真实世界的图像数据集,用于开发机器学习和对象识别算法,对数据预处理和格式化的要求最低。它可以被视为与MNIST风格相似(例如,图像是经过裁剪的小数字),但包含一个数量级的更多标记数据(超过 600,000 个数字图像),并且来自一个更难、未解决的现实世界问题(识别自然场景图像中的数字和数字)。SVHN 是从谷歌街景图像中的门牌号获得的。
数据集下载地址:http://m6z.cn/5SUfEd该数据集的目的是提供一种简单的方法来开始处理 3D 计算机视觉问题,例如 3D 形状识别。
LVIS是一个大规模细粒度词汇集标记数据集,该数据集针对超过 1000 类物体进行了约 200 万个高质量的实例分割标注,包含 164k 张图像。
Crowd Segmentation Dataset 是一个高密度人群和移动物体视频数据,视频来自BBC Motion Gallery 和 Getty Images 网站。
Densely Annotated Video Segmentation 是一个高清视频中的物体分割数据集,包括 50个 视频序列,3455个 帧标注,视频采集自高清 1080p 格式。、
数据集下载地址:http://m6z.cn/6nF67SMediaTeam Oulu Document 数据集是一个文档扫描图像和文档内容数据集,包含 500篇 1975年之前的文档信息。
Diganta Misra的一篇题为“Mish: A Self Regularized Non-Monotonic Neural Activation Function”的新论文介绍了一个新的深度学习激活函数,该函数在最终准确度上比Swish(+.494%)和ReLU(+ 1.671%)都有提高。
ReLU和Mish的对比,Mish的梯度更平滑
Mish=x * tanh(ln(1+e^x))
函数图像:
常见激活函数:
代码学习网站:
教程:https://stable-baselines.readthedocs.io/en/master/guide/examples.html
在做rl时候 ,如何利用gym将动画动起来,让每一步训练过程可视化:
例程:
import gym
from stable_baselines import DQN
from stable_baselines.common.evaluation import evaluate_policy
# Create environment
env = gym.make('LunarLander-v2')
# Instantiate the agent
model = DQN('MlpPolicy', env, learning_rate=1e-3, prioritized_replay=True, verbose=1)
# Train the agent
model.learn(total_timesteps=int(2e5))
# Save the agent
model.save("dqn_lunar")
del model # delete trained model to demonstrate loading
# Load the trained agent
model = DQN.load("dqn_lunar")
# Evaluate the agent
mean_reward, std_reward = evaluate_policy(model, model.get_env(), n_eval_episodes=10)
# Enjoy trained agent
obs = env.reset()
for i in range(1000):
action, _states = model.predict(obs)
obs, rewards, dones, info = env.step(action)
env.render()
from stable_baselines.common.cmd_util import make_atari_env from stable_baselines.common.vec_env import VecFrameStack from stable_baselines import ACER # There already exists an environment generator # that will make and wrap atari environments correctly. # Here we are also multiprocessing training (num_env=4 => 4 processes) env = make_atari_env('PongNoFrameskip-v4', num_env=4, seed=0) # Frame-stacking with 4 frames env = VecFrameStack(env, n_stack=4) model = ACER('CnnPolicy', env, verbose=1) model.learn(total_timesteps=25000) obs = env.reset() while True: action, _states = model.predict(obs) obs, rewards, dones, info = env.step(action) env.render()
在执行时:
import gym
env = gym.make('ALE/Pong-v5')
env.reset()
for i in range(1000):
env.step(env.action_space.sample())
env.render()
env.close()
输出,无法对fream进行渲染。
ImportError: cannot import name ‘rendering’ from ‘gym.envs.classic_control’.
解决办法:
打开 包gym.envs.classic_control,发现没有 rendering .py文件,去github,发现,main分支确实已经没有这个文件了,应该是版本的问题,最新版本已经去掉了该文件,然而其他分支是有的,所以将该文件下载并放在包对应位置。
此外,还需要 在代码中加入
from gym.envs.classic_control import rendering
导入rendering.py
摘自:https://blog.csdn.net/alangaixiaoxiao/article/details/105533746
网上的资源很多,也有很多博主的原理讲解。在这里推荐几个资源:
darknet官网:https://pjreddie.com/darknet/yolo/ linux系统
github上基于VS的工程:https://github.com/AlexeyAB/darknet
github上带有注释的工程:https://github.com/hgpvision/darknet
Yolo算法采用一个单独的CNN模型实现end-to-end的目标检测,首先将输入图片resize到448×448,然后送入CNN网络,最后处理网络预测结果得到检测的目标。
YOLO 的核心思想就是利用整张图作为网络的输入,直接在输出层回归 bounding box(边界框) 的位置及其所属的类别。将一幅图像分成 SxS 个网格(grid cell),如果某个 object 的中心落在这个网格中,则这个网格就负责预测这个 object。
每个 bounding box 要预测 (x, y, w, h) 和 confidence 共5个值,每个网格还要预测一个类别信息,记为 C 类。则 SxS个 网格,每个网格要预测 B 个 bounding box, 每个box中都有 C 个 classes对应的概率值。输出就是 S x S x B x(5+C) 的一个 tensor。
注意:class 信息是针对每个网格的,confidence 信息是针对每个 bounding box 的。
yolov3-tiny中,共有两个输出层(yolo层),分别为13×13和26×26,每个网格可以预测3个bounding box,共有80个分类数。所以最后的yolo层的尺寸为:13x13x255和26x26x255。
yolov3-tiny网络层结构如下:
可以看出,yolov3-tiny共有23层网络,其中包含五种不同的网络层:卷积层convolutional(13个),池化层maxpool(6个),路由层route(2个),上采样层upsample(1个),输出层yolo(2个)。Yolov3-tiny中,除了Yolo层之前的那个卷积层,每个卷积层之后都有BN层,且每个卷积层之后都有激活函数LEAKY(yolo层之前是linear)。
yolov3-tiny前向传播主要在detector.c中的test_detector函数中完成:
/** 本函数是检测模型的一个前向推理测试函数.
* @param datacfg 数据集信息文件路径(也即cfg/*.data文件),文件中包含有关数据集的信息,比如cfg/coco.data
* @param cfgfile 网络配置文件路径(也即cfg/*.cfg文件),包含一个网络所有的结构参数,比如cfg/yolo.cfg
* @param weightfile 已经训练好的网络权重文件路径,比如darknet网站上下载的yolo.weights文件
* @param filename 待进行检测的图片路径(单张图片)
* @param thresh 阈值,类别检测概率大于该阈值才认为其检测结果有效
* @param hier_thresh
* @param outfile
* @param fullscreen
* @details 该函数为一个前向推理测试函数,不包括训练过程,因此如果要使用该函数,必须提前训练好网络,并加载训练好的网络参数文件,
* 这些文件可以在作者网站上根据作者的提示下载到。本函数由darknet.c中的主函数调用,严格来说,本文件不应纳入darknet网络结构文件夹中,
* 其只是一个测试文件,或者说是一个example,应该放入到example文件夹中(新版的darknet已经这样做了,可以在github上查看)。
* 本函数的流程为:.
*/
void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh,
float hier_thresh, int dont_show, int ext_output, int save_labels, char *outfile, int letter_box, int benchmark_layers)
{
// 从指定数据文件datacfg(.data文件)中读入数据信息(测试、训练数据信息)到options中
// options是list类型数据,其中的node包含的void指针具体是kvp数据类型,具有键值和值(类似C++中的Map)
list *options = read_data_cfg(datacfg);
// 获取数据集的名称(包括路径),第二个参数"names"表明要从options中获取所用数据集的名称信息(如names = data/coco.names)
char *name_list = option_find_str(options, "names", "data/names.list");
int names_size = 0;
// 从data/**.names中读取物体名称/标签信息
char **names = get_labels_custom(name_list, &names_size); //get_labels(name_list);
// 加载data/labels/文件夹中所有的字符标签图片
image **alphabet = load_alphabet();
network net = parse_network_cfg_custom(cfgfile, 1, 1); // set batch=1 配置各网络层参数,重要
在parser.c中的parse_network_cfg_custom函数中,根据yolov3-tiny.cfg文件对网络结构进行配置,明确各层网络的类型、输入输出通道数、图像尺寸、卷积核大小等。
//配置各网络层参数
network parse_network_cfg_custom(char *filename, int batch, int time_steps)
{
// 从神经网络结构参数文件中读入所有神经网络层的结构参数,存储到sections中,
// sections的每个node包含一层神经网络的所有结构参数
list *sections = read_cfg(filename);
// 获取sections的第一个节点,可以查看一下cfg/***.cfg文件,其实第一块参数(以[net]开头)不是某层神经网络的参数,
// 而是关于整个网络的一些通用参数,比如学习率,衰减率,输入图像宽高,batch大小等,
// 具体的关于某个网络层的参数是从第二块开始的,如[convolutional],[maxpool]...,
// 这些层并没有编号,只说明了层的属性,但层的参数都是按顺序在文件中排好的,读入时,
// sections链表上的顺序就是文件中的排列顺序。
node *n = sections->front;
if(!n) error("Config file has no sections");
// 创建网络结构并动态分配内存:输入网络层数为sections->size - 1,sections的第一段不是网络层,而是通用网络参数
network net = make_network(sections->size - 1);
// 所用显卡的卡号(gpu_index在cuda.c中用extern关键字声明)
// 在调用parse_network_cfg()之前,使用了cuda_set_device()设置了gpu_index的值号为当前活跃GPU卡号
net.gpu_index = gpu_index;
// size_params结构体元素不含指针变量
size_params params;
if (batch > 0) params.train = 0; // allocates memory for Detection only
else params.train = 1; // allocates memory for Detection & Training
section *s = (section *)n->val;
list *options = s->options;
if(!is_network(s)) error("First section must be [net] or [network]");
parse_net_options(options, &net);
#ifdef GPU
printf("net.optimized_memory = %d \n", net.optimized_memory);
if (net.optimized_memory >= 2 && params.train) {
pre_allocate_pinned_memory((size_t)1024 * 1024 * 1024 * 8); // pre-allocate 8 GB CPU-RAM for pinned memory
}
#endif // GPU
params.h = net.h;
params.w = net.w;
params.c = net.c;
params.inputs = net.inputs;
if (batch > 0) net.batch = batch;
if (time_steps > 0) net.time_steps = time_steps;
if (net.batch < 1) net.batch = 1;
if (net.time_steps < 1) net.time_steps = 1;
if (net.batch < net.time_steps) net.batch = net.time_steps;
params.batch = net.batch;
params.time_steps = net.time_steps;
params.net = net;
printf("mini_batch = %d, batch = %d, time_steps = %d, train = %d \n", net.batch, net.batch * net.subdivisions, net.time_steps, params.train);
int avg_outputs = 0;
float bflops = 0;
size_t workspace_size = 0;
size_t max_inputs = 0;
size_t max_outputs = 0;
n = n->next;
int count = 0;
free_section(s);
// 此处stderr不是错误提示,而是输出结果提示,提示网络结构
fprintf(stderr, " layer filters size/strd(dil) input output\n");
while(n){
params.index = count;
fprintf(stderr, "%4d ", count);
s = (section *)n->val;
options = s->options;
// 定义网络层
layer l = { (LAYER_TYPE)0 };
// 获取网络层的类别
LAYER_TYPE lt = string_to_layer_type(s->type);
//通过读取网络类型,从而配置各网络层的参数
if(lt == CONVOLUTIONAL){//yolov3-tiny 卷积层 13层
l = parse_convolutional(options, params);
}else if(lt == LOCAL){
l = parse_local(options, params);
}else if(lt == ACTIVE){
l = parse_activation(options, params);
}else if(lt == RNN){
l = parse_rnn(options, params);
}else if(lt == GRU){
l = parse_gru(options, params);
}else if(lt == LSTM){
l = parse_lstm(options, params);
}else if (lt == CONV_LSTM) {
l = parse_conv_lstm(options, params);
}else if(lt == CRNN){
l = parse_crnn(options, params);
}else if(lt == CONNECTED){
l = parse_connected(options, params);
}else if(lt == CROP){
l = parse_crop(options, params);
}else if(lt == COST){
l = parse_cost(options, params);
l.keep_delta_gpu = 1;
}else if(lt == REGION){
l = parse_region(options, params);
l.keep_delta_gpu = 1;
}else if (lt == YOLO) {//yolov3-tiny YOLO层 两层
l = parse_yolo(options, params);
l.keep_delta_gpu = 1;
}else if (lt == GAUSSIAN_YOLO) {
l = parse_gaussian_yolo(options, params);
l.keep_delta_gpu = 1;
}else if(lt == DETECTION){
l = parse_detection(options, params);
}else if(lt == SOFTMAX){
l = parse_softmax(options, params);
net.hierarchy = l.softmax_tree;
l.keep_delta_gpu = 1;
}else if(lt == NORMALIZATION){
l = parse_normalization(options, params);
}else if(lt == BATCHNORM){
l = parse_batchnorm(options, params);
}else if(lt == MAXPOOL){//yolov3-tiny 池化层 maxpool 6层
l = parse_maxpool(options, params);
}else if (lt == LOCAL_AVGPOOL) {
l = parse_local_avgpool(options, params);
}else if(lt == REORG){
l = parse_reorg(options, params); }
else if (lt == REORG_OLD) {
l = parse_reorg_old(options, params);
}else if(lt == AVGPOOL){
l = parse_avgpool(options, params);
}else if(lt == ROUTE){//yolov3-tiny 路由层 2层
l = parse_route(options, params);
int k;
for (k = 0; k < l.n; ++k) {
net.layers[l.input_layers[k]].use_bin_output = 0;
net.layers[l.input_layers[k]].keep_delta_gpu = 1;
}
}else if (lt == UPSAMPLE) {//yolov3-tiny 上采样层 1层
l = parse_upsample(options, params, net);
}else if(lt == SHORTCUT){
l = parse_shortcut(options, params, net);
net.layers[count - 1].use_bin_output = 0;
net.layers[l.index].use_bin_output = 0;
net.layers[l.index].keep_delta_gpu = 1;
}else if (lt == SCALE_CHANNELS) {
l = parse_scale_channels(options, params, net);
net.layers[count - 1].use_bin_output = 0;
net.layers[l.index].use_bin_output = 0;
net.layers[l.index].keep_delta_gpu = 1;
}
else if (lt == SAM) {
l = parse_sam(options, params, net);
net.layers[count - 1].use_bin_output = 0;
net.layers[l.index].use_bin_output = 0;
net.layers[l.index].keep_delta_gpu = 1;
}else if(lt == DROPOUT){
l = parse_dropout(options, params);
l.output = net.layers[count-1].output;
l.delta = net.layers[count-1].delta;
.........
在parser.c的load_weights_upto中,根据卷积层的网络配置,开始下载读取各层的权重文件。
//读取权重文件函数
void load_weights_upto(network *net, char *filename, int cutoff)//cutoff = net->n
{
#ifdef GPU
if(net->gpu_index >= 0){
cuda_set_device(net->gpu_index);
}
#endif
fprintf(stderr, "Loading weights from %s...\n", filename);
fflush(stdout);
FILE *fp = fopen(filename, "rb");
if(!fp) file_error(filename);
int major;
int minor;
int revision;
fread(&major, sizeof(int), 1, fp);//读取一个4字节的数据
fread(&minor, sizeof(int), 1, fp);//读取一个4字节的数据
fread(&revision, sizeof(int), 1, fp);//读取一个4字节的数据
printf("the size of int in x64 is %d bytes,attention!!!\n", sizeof(int));//x86 x64: 4
printf("major ,minor,revision of weight is %d, %d ,%d\n", major, minor, revision);//0.2.0
if ((major * 10 + minor) >= 2) {//运行这一部分
printf("\n seen 64");
uint64_t iseen = 0;
fread(&iseen, sizeof(uint64_t), 1, fp);//读取一个8字节的数据
printf("the size of uint64_t is %d\n", sizeof(uint64_t));
*net->seen = iseen;
}
else {
printf("\n seen 32");
uint32_t iseen = 0;
fread(&iseen, sizeof(uint32_t), 1, fp);
*net->seen = iseen;
}
*net->cur_iteration = get_current_batch(*net);
printf(", trained: %.0f K-images (%.0f Kilo-batches_64) \n", (float)(*net->seen / 1000), (float)(*net->seen / 64000));
int transpose = (major > 1000) || (minor > 1000);
int i;
for(i = 0; i < net->n && i < cutoff; ++i){//cutoff = net->n
layer l = net->layers[i];
if (l.dontload) continue;//always 0 跳过之后的循环体,直接运行++i
if(l.type == CONVOLUTIONAL && l.share_layer == NULL){ //只运行这一个分支的代码
load_convolutional_weights(l, fp);
//printf("network layer [%d] is CONVOLUTIONAL \n",i);
}
.......
在读取yolov3-tiny各层权重文件前,先读取4个和训练有关的参数:major,minor, revision和iseen。在前向传播的工程当中,并没有实际的应用。
parser.c中的load_convolutional_weights函数,具体执行对yolov3-tiny权重文件的下载,包括节点参数weight,偏置参数bias和批量归一化参数BN。
void load_convolutional_weights(layer l, FILE *fp)
{
static int flipped_num;
if(l.binary){
//load_convolutional_weights_binary(l, fp);
//return;
}
int num = l.nweights;
//int num = l.n*l.c*l.size*l.size;//l.n 输出的层数 l.c输入的层数
int read_bytes;
read_bytes = fread(l.biases, sizeof(float), l.n, fp);//读取偏置参数 l.n个float数据
if (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.biases - l.index = %d \n", l.index);
//fread(l.weights, sizeof(float), num, fp); // as in connected layer
if (l.batch_normalize && (!l.dontloadscales)){
read_bytes = fread(l.scales, sizeof(float), l.n, fp);//读取batch normalize 参数 l.n个float数据
if (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.scales - l.index = %d \n", l.index);
read_bytes = fread(l.rolling_mean, sizeof(float), l.n, fp);//读取batch normalize 参数 l.n个float数据
if (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.rolling_mean - l.index = %d \n", l.index);
read_bytes = fread(l.rolling_variance, sizeof(float), l.n, fp);//读取batch normalize 参数 l.n个float数据
if (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.rolling_variance - l.index = %d \n", l.index);
yolov3-tiny每个卷积层之后,激活函数之前,都要对结果进行Batch Normalization:
由于BN层和卷积操作都是线性的,将权重文件进行批量归一化,可以代替卷积层之后的BN层:
在network.c的fuse_conv_batchnorm函数中实现权重文件和BN层的合并。
void fuse_conv_batchnorm(network net)
{
int j;
for (j = 0; j < net.n; ++j) {
layer *l = &net.layers[j];
// printf("the %d layer batch_normalize is %d, groups is %d \n", j, l->batch_normalize, l->groups);
if (l->type == CONVOLUTIONAL) { //只运行这一分支 合并卷积层和batch_normal
//printf(" Merges Convolutional-%d and batch_norm \n", j);
if (l->share_layer != NULL) {//l->share_layer always is 0,不运行这个分支
l->batch_normalize = 0;
}
if (l->batch_normalize) {//#15,22层卷积,卷积之后没有batch normalize,其他都要运行这一分支
int f;
for (f = 0; f < l->n; ++f)//该层神经网络 1->n 个输出层权重
{
l->biases[f] = l->biases[f] - (double)l->scales[f] * l->rolling_mean[f] / (sqrt((double)l->rolling_variance[f] + .00001));
const size_t filter_size = l->size*l->size*l->c / l->groups;//kernel_size * kernel_size * c/分组 l->groups存在于卷积层always is 1
int i;
for (i = 0; i < filter_size; ++i) {
int w_index = f*filter_size + i;
l->weights[w_index] = (double)l->weights[w_index] * l->scales[f] / (sqrt((double)l->rolling_variance[f] + .00001));
}
}
free_convolutional_batchnorm(l);//no use
l->batch_normalize = 0;
......
yolov3-tiny输入神经网络的图像尺寸为416×416,对不符合该尺寸的图像,要进行裁剪。在image.c的resize_image函数中完成。这个可以说是整个yolo算法对输入图像唯一进行预处理的地方了。这也是yolo算法在工程应用中极好的地方,没有那么多类似于降噪、滤波之类的预处理,直接送到网络里就完事了。
//im:输入图片 w:416 h:416
//函数作用:将输入图片热size到416x416的尺寸,基本按照缩放/扩大的策略
image resize_image(image im, int w, int h)
{
if (im.w == w && im.h == h) return copy_image(im);
image resized = make_image(w, h, im.c);//416 x 416 x 3空的地址空间
image part = make_image(w, im.h, im.c);//416 x im.h x im.c空的地址空间
int r, c, k;
float w_scale = (float)(im.w - 1) / (w - 1);//宽度缩放因子
float h_scale = (float)(im.h - 1) / (h - 1);//高度缩放因子
for(k = 0; k < im.c; ++k){
for(r = 0; r < im.h; ++r){
for(c = 0; c < w; ++c){//416
float val = 0;
if(c == w-1 || im.w == 1){//c =415 最后一列
val = get_pixel(im, im.w-1, r, k);//取原图片最后一列的像素
} else {
float sx = c*w_scale;
int ix = (int) sx;
float dx = sx - ix;
val = (1 - dx) * get_pixel(im, ix, r, k) + dx * get_pixel(im, ix+1, r, k);
}
set_pixel(part, c, r, k, val);
}
}
}
for(k = 0; k < im.c; ++k){
for(r = 0; r < h; ++r){
float sy = r*h_scale;
int iy = (int) sy;
float dy = sy - iy;
for(c = 0; c < w; ++c){
float val = (1-dy) * get_pixel(part, c, iy, k);
set_pixel(resized, c, r, k, val);
}
if(r == h-1 || im.h == 1) continue;
for(c = 0; c < w; ++c){
float val = dy * get_pixel(part, c, iy+1, k);
add_pixel(resized, c, r, k, val);
}
}
}
free_image(part);
return resized;
}
network.c中的forward_network函数是整个神经网络的核心部分,各层的网络都在函数指针l.forward(l, state)中完成。
void forward_network(network net, network_state state)
{
state.workspace = net.workspace;
int i;
/// 遍历所有层,从第一层到最后一层,逐层进行前向传播(网络总共有net.n层)
for(i = 0; i < net.n; ++i){
state.index = i;/// 置网络当前活跃层为当前层,即第i层
layer l = net.layers[i];/// 获取当前层
if(l.delta && state.train){//不执行此分支的代码
/// 如果当前层的l.delta已经动态分配了内存,则调用fill_cpu()函数,将其所有元素的值初始化为0
scal_cpu(l.outputs * l.batch, 0, l.delta, 1);/// 第一个参数为l.delta的元素个数,第二个参数为初始化值,为0
printf("forward_network scal_cpu of %d layer done!\n ", i);
}
//double time = get_time_point();
l.forward(l, state);//进行卷积运算,激活函数,池化运算/
//if layer_type = convolutional ; l.forward = forward_convolutional_layer;
//if layer_type = maxpool l.forward = forward_maxpool_layer;
//if layer_type = yolo l.forward = forward_yolo_layer;
//if layer_type = ROUTE l.forward = forward_route_layer;其实就是数据的复制和搬移
//if layer_type = upsample l.forward = forward_upsample_layer;;
//printf("%d - Predicted in %lf milli-seconds.\n", i, ((double)get_time_point() - time) / 1000);
/// 完成某一层的推理时,置网络的输入为当前层的输出(这将成为下一层网络的输入),要注意的是,此处是直接更改指针变量net.input本身的值,
/// 也就是此处是通过改变指针net.input所指的地址来改变其中所存内容的值,并不是直接改变其所指的内容而指针所指的地址没变,
/// 所以在退出forward_network()函数后,其对net.input的改变都将失效,net.input将回到进入forward_network()之前时的值。
......
卷积层在convolutional_layer.c中的forward_convolutional_layer函数实现。
void forward_convolutional_layer(convolutional_layer l, network_state state)
{
int out_h = convolutional_out_height(l);//获得本层卷积层输出特征图的高、宽
int out_w = convolutional_out_width(l);
int i, j;
// l.outputs = l.out_h * l.out_w * l.out_c在make各网络层函数中赋值(比如make_convolutional_layer()),
// 对应每张输入图片的所有输出特征图的总元素个数(每张输入图片会得到n也即l.out_c张特征图)
// 初始化输出l.output全为0.0;输入l.outputs*l.batch为输出的总元素个数,其中l.outputs为batch
// 中一个输入对应的输出的所有元素的个数,l.batch为一个batch输入包含的图片张数;0表示初始化所有输出为0;
fill_cpu(l.outputs*l.batch, 0, l.output, 1);//将地址l.output,l.outputs*l.batch个float地址空间的数据初始化0
.......
作者在进行卷积运算前,将输入特征图进行重新排序:
```c
void im2col_cpu(float* data_im,
int channels, int height, int width,
int ksize, int stride, int pad, float* data_col)
{
int c,h,w;
// 计算该层神经网络的输出图像尺寸(其实没有必要再次计算的,因为在构建卷积层时,make_convolutional_layer()函数
// 已经调用convolutional_out_width(),convolutional_out_height()函数求取了这两个参数,
// 此处直接使用l.out_h,l.out_w即可,函数参数只要传入该层网络指针就可了,没必要弄这么多参数)
int height_col = (height + 2*pad - ksize) / stride + 1;
int width_col = (width + 2*pad - ksize) / stride + 1;
/// 卷积核大小:ksize*ksize是一个卷积核的大小,之所以乘以通道数channels,是因为输入图像有多通道,每个卷积核在做卷积时,
/// 是同时对同一位置处多通道的图像进行卷积运算,这里为了实现这一目的,将三通道上的卷积核并在一起以便进行计算,因此卷积核
/// 实际上并不是二维的,而是三维的,比如对于3通道图像,卷积核尺寸为3*3,该卷积核将同时作用于三通道图像上,这样并起来就得
/// 到含有27个元素的卷积核,且这27个元素都是独立的需要训练的参数。所以在计算训练参数个数时,一定要注意每一个卷积核的实际
/// 训练参数需要乘以输入通道数。
int channels_col = channels * ksize * ksize;//输入通道
// 外循环次数为一个卷积核的尺寸数,循环次数即为最终得到的data_col的总行数
for (c = 0; c < channels_col; ++c) {
//行,列偏置都是对应着本次循环要操作的输出位置的像素而言的,通道偏置,是该位置像素所在的输出通道的绝对位置(通道数)
// 列偏移,卷积核是一个二维矩阵,并按行存储在一维数组中,利用求余运算获取对应在卷积核中的列数,比如对于
// 3*3的卷积核(3通道),当c=0时,显然在第一列,当c=5时,显然在第2列,当c=9时,在第二通道上的卷积核的第一列,
// 当c=26时,在第三列(第三输入通道上)
int w_offset = c % ksize;//0,1,2
// 行偏移,卷积核是一个二维的矩阵,且是按行(卷积核所有行并成一行)存储在一维数组中的,
// 比如对于3*3的卷积核,处理3通道的图像,那么一个卷积核具有27个元素,每9个元素对应一个通道上的卷积核(互为一样),
// 每当c为3的倍数,就意味着卷积核换了一行,h_offset取值为0,1,2,对应3*3卷积核中的第1, 2, 3行
int h_offset = (c / ksize) % ksize;//0,1,2
// 通道偏移,channels_col是多通道的卷积核并在一起的,比如对于3通道,3*3卷积核,每过9个元素就要换一通道数,
// 当c=0~8时,c_im=0;c=9~17时,c_im=1;c=18~26时,c_im=2,操作对象是排序后的像素位置
int c_im = c / ksize / ksize;
// 中循环次数等于该层输出图像行数height_col,说明data_col中的每一行存储了一张特征图,这张特征图又是按行存储在data_col中的某行中
for (h = 0; h < height_col; ++h) {
// 内循环等于该层输出图像列数width_col,说明最终得到的data_col总有channels_col行,height_col*width_col列
for (w = 0; w < width_col; ++w) {
// 由上面可知,对于3*3的卷积核,行偏置h_offset取值为0,1,2,当h_offset=0时,会提取出所有与卷积核第一行元素进行运算的像素,
// 依次类推;加上h*stride是对卷积核进行行移位操作,比如卷积核从图像(0,0)位置开始做卷积,那么最先开始涉及(0,0)~(3,3)
// 之间的像素值,若stride=2,那么卷积核进行一次行移位时,下一行的卷积操作是从元素(2,0)(2为图像行号,0为列号)开始
int im_row = h_offset + h * stride;//yolov3-tiny stride = 1
// 对于3*3的卷积核,w_offset取值也为0,1,2,当w_offset取1时,会提取出所有与卷积核中第2列元素进行运算的像素,
// 实际在做卷积操作时,卷积核对图像逐行扫描做卷积,加上w*stride就是为了做列移位,
// 比如前一次卷积其实像素元素为(0,0),若stride=2,那么下次卷积元素起始像素位置为(0,2)(0为行号,2为列号)
int im_col = w_offset + w * stride;
// col_index为重排后图像中的像素索引,等于c * height_col * width_col + h * width_col +w(还是按行存储,所有通道再并成一行),
// 对应第c通道,h行,w列的元素
int col_index = (c * height_col + h) * width_col + w;//将重排后的图片像素,按照左上->右下的顺序,计算一维索引
//im_col + width*im_row + width*height*channel 重排前的特征图在内存中的位置索引
// im2col_get_pixel函数获取输入图像data_im中第c_im通道,im_row,im_col的像素值并赋值给重排后的图像,
// height和width为输入图像data_im的真实高、宽,pad为四周补0的长度(注意im_row,im_col是补0之后的行列号,
// 不是真实输入图像中的行列号,因此需要减去pad获取真实的行列号)
data_col[col_index] = im2col_get_pixel(data_im, height, width, channels,
im_row, im_col, c_im, pad);
// return data_im[im_col + width*im_row + width*height*channel)];
}
}
}
}
通过gemm进行卷积乘加操作,通过add_bias添加偏置。
//进行卷积的乘加运算,没有bias偏置参数参与运算;
gemm(0, 0, m, n, k, 1, a, k, b, n, 1, c, n);
add_bias(l.output, l.biases, l.batch, l.n, out_h*out_w);//每个输出特征图的元素都加上对应通道的偏置参数
maxpool_layer.c中的forward_maxpool_layer函数完成池化操作。yolov3-tiny保留了池化层,并使用最大值池化,将尺寸为2×2的核中最大值保留下来。
void forward_maxpool_layer_avx(float *src, float *dst, int *indexes, int size, int w, int h, int out_w, int out_h, int c,
int pad, int stride, int batch)
{
const int w_offset = -pad / 2;
const int h_offset = -pad / 2;
int b, k;
for (b = 0; b < batch; ++b) {
// 对于每张输入图片,将得到通道数一样的输出图,以输出图为基准,按输出图通道,行,列依次遍历
// (这对应图像在l.output的存储方式,每张图片按行铺排成一大行,然后图片与图片之间再并成一行)。
// 以输出图为基准进行遍历,最终循环的总次数刚好覆盖池化核在输入图片不同位置进行池化操作。
#pragma omp parallel for
for (k = 0; k < c; ++k) {
int i, j, m, n;
for (i = 0; i < out_h; ++i) {
//for (j = 0; j < out_w; ++j) {
j = 0;
for (; j < out_w; ++j) {
// out_index为输出图中的索引
int out_index = j + out_w*(i + out_h*(k + c*b));//j + out_w * i + out_w * iout_h * k
float max = -FLT_MAX;// FLT_MAX为c语言中float.h定义的对大浮点数,此处初始化最大元素值为最小浮点数
int max_i = -1;// 最大元素值的索引初始化为-1
// 下面两个循环回到了输入图片,计算得到的cur_h以及cur_w都是在当前层所有输入元素的索引,内外循环的目的是找寻输入图像中,
// 以(h_offset + i*l.stride, w_offset + j*l.stride)为左上起点,尺寸为l.size池化区域中的最大元素值max及其在所有输入元素中的索引max_i
for (n = 0; n < size; ++n) {//2
for (m = 0; m < size; ++m) {//2
// cur_h,cur_w是在所有输入图像中第k通道中的cur_h行与cur_w列,index是在所有输入图像元素中的总索引。
// 为什么这里少一层对输入通道数的遍历循环呢?因为对于最大池化层来说输入与输出通道数是一样的,并在上面的通道数循环了!
int cur_h = h_offset + i*stride + n;
int cur_w = w_offset + j*stride + m;
int index = cur_w + w*(cur_h + h*(k + b*c));
// 边界检查:正常情况下,是不会越界的,但是如果有补0操作,就会越界了,这里的处理方式是直接让这些元素值为-FLT_MAX
// (注意虽然称之为补0操作,但实际不是补0),总之,这些补的元素永远不会充当最大元素值。
int valid = (cur_h >= 0 && cur_h < h &&
cur_w >= 0 && cur_w < w);
float val = (valid != 0) ? src[index] : -FLT_MAX;
// 记录这个池化区域中的最大的元素值及其在所有输入元素中的总索引
max_i = (val > max) ? index : max_i;
max = (val > max) ? val : max;
}
}
// 由此得到最大池化层每一个输出元素值及其在所有输入元素中的总索引。
// 为什么需要记录每个输出元素值对应在输入元素中的总索引呢?因为在下面的反向过程中需要用到,在计算当前最大池化层上一层网络的敏感度时,
// 需要该索引明确当前层的每个元素究竟是取上一层输出(也即上前层输入)的哪一个元素的值,具体见下面backward_maxpool_layer()函数的注释。
dst[out_index] = max;
if (indexes) indexes[out_index] = max_i;
}
}
}
}
}
yolov3-tiny中共有两层路由层。第17层路由层(从0层开始),其实直接将第13层网络的输出结果输入。第20层路由层,将第19层和第8层网络结果合并在一起,19层在前,8层在后。在route_layer.c中的forward_route_layer函数中实现。
void forward_route_layer(const route_layer l, network_state state)
{
int i, j;
int offset = 0;
for(i = 0; i < l.n; ++i){//l.n: 卷积层:输出特征图通道数 路由层:有几层网络层输入本层 17层:1(路由第13层) 20:2(路由第19、8层)
int index = l.input_layers[i];//输入本网络层的网络层的索引:如13,19,8
float *input = state.net.layers[index].output;//输入等于 之前网络层索引值得输出(.output)
int input_size = l.input_sizes[i];//输入的网络层的数据量
int part_input_size = input_size / l.groups;//未分组
for(j = 0; j < l.batch; ++j){
//copy_cpu(input_size, input + j*input_size, 1, l.output + offset + j*l.outputs, 1);
//从首地址input处复制input_size 个数据到 l.output中
copy_cpu(part_input_size, input + j*input_size + part_input_size*l.group_id, 1, l.output + offset + j*l.outputs, 1);//l.group_id = 0
//其实就是copy_cpu(part_input_size, input, 1, l.output + offset, 1);
}
//offset += input_size;
offset += part_input_size;
}
}
yolov3-tiny中第19层是上采样层,将18层13x13x128的输入特征图转变为26x26x128的输出特征图。在upsample_layer.c中的forward_upsample_layer函数中完成。
void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out)
{
int i, j, k, b;
for (b = 0; b < batch; ++b) {
for (k = 0; k < c; ++k) {
for (j = 0; j < h*stride; ++j) {
for (i = 0; i < w*stride; ++i) {
int in_index = b*w*h*c + k*w*h + (j / stride)*w + i / stride;
int out_index = b*w*h*c*stride*stride + k*w*h*stride*stride + j*w*stride + i;
if (forward) out[out_index] = scale*in[in_index];
else in[in_index] += scale*out[out_index];
}
}
}
}
}
上采样效果:
yolo层完成了对13x13x255和26x26x255输入特诊图的logistic逻辑回归计算。每个box的预测宽度和高度不参与逻辑回归,在yolo_layer.c中的forward_yolo_layer函数中完成。
//两个yolo层 只对数据进行了logistic处理,并没有预测box的位置
//将0-1通道(x,y) 4-84(confidence+class)计算logistic,三个prior(预测框都是这样)
void forward_yolo_layer(const layer l, network_state state)
{
int i, j, b, t, n;
//从state.input复制数据到l.output
memcpy(l.output, state.input, l.outputs*l.batch * sizeof(float));
#ifndef GPU
printf("yolo v3 tiny l.n and l.batch of yolo layer is %d and %d \n ",l.n,l.batch);
for (b = 0; b < l.batch; ++b) {//l.batch = 1
for (n = 0; n < l.n; ++n) {//l.n:3(yolo层)mask 0,1,2 表示每个网络单元预测三个box?
//printf("l.coords is %d in yolov3 tiny yolo layer ,l.scale_x_y is %f \n", l.coords, l.scale_x_y);
// l.coords 坐标:0 l.classes分类数量:80 l.scale_x_y:1
//l.w:输入特征图宽度 l.h输出特征图高度
int index = entry_index(l, b, n*l.w*l.h, 0);//index = n*l.w*l.h*(4 + l.classes + 1)
//起始地址为:l.output + index 个数为:2 * l.w*l.h 计算逻辑回归值,并保存
activate_array(l.output + index, 2 * l.w*l.h, LOGISTIC); // x,y,
//起始地址为:l.output + index 个数为:2 * l.w*l.h 计算方式为:x = x*l.scale_x_y + -0.5*(l.scale_x_y - 1) 简化后:x = x
//yolov3-tiny l.scale_x_y = 1 实际上该函数没有参与任何的运算 scal_add_cpu
scal_add_cpu(2 * l.w*l.h, l.scale_x_y, -0.5*(l.scale_x_y - 1), l.output + index, 1); // scale x,y
//
index = entry_index(l, b, n*l.w*l.h, 4);//index = n*l.w*l.h*(4 + l.classes + 1)+ 4*l.w*l.h
//起始地址为:l.output + index,个数为:(1+80)*l.w*l.h 计算器其逻辑回归值
activate_array(l.output + index, (1 + l.classes)*l.w*l.h, LOGISTIC);
}
}
//w:输入图像宽度640,不一定是416 h:输入图像高度424,不一定是416 thresh:图像置信度阈值0.25 hier:0.5
//map:0 relative:1 num:0 letter:0
//函数作用:统计两个yolo层中 置信度大于阈值的box个数,并对这个box初始化一段地址空间 dets
//根据网络来填充该地址空间dets:
//根据yolo层 计算满足置信度阈值要求的box相对的预测坐标、宽度和高度,并将结果保存在dets[count].bbox结构体中
//每个box有80个类别,有一个置信度,该类别对应的可能性prob:class概率*置信度
///舍弃prob小于阈值0.25的box
//将满足阈值的box个数保存到num中
detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num, int letter)
{
//printf("w、h、thresh、hier and letter is %d 、%d 、%f 、%f and %d\n", w, h, thresh, hier, letter);
//函数作用:统计两个yolo层中 置信度大于阈值的box个数,并对这个box初始化一段地址空间 dets
//将满足阈值的box个数保存到num中
detection *dets = make_network_boxes(net, thresh, num);
//根据网络来填充该地址空间dets:
//根据yolo层 计算满足置信度阈值要求的box相对的预测坐标、宽度和高度,并将结果保存在dets[count].bbox结构体中
//每个box有80个类别,有一个置信度,该类别对应的可能性prob:class概率*置信度
///舍弃prob小于阈值0.25的box
fill_network_boxes(net, w, h, thresh, hier, map, relative, dets, letter);
return dets;
}
使用make_network_boxes来创建预测信息的指针变量:
// thresh: 置信度阈值
//num: 0
//函数作用:统计置信度大于阈值的box个数,并对这个box初始化一段地址空间
detection *make_network_boxes(network *net, float thresh, int *num)
{
layer l = net->layers[net->n - 1];//应该是神经网络最后一层 net->n:24 最后一层yolo层
//printf(" net->n of network is %d\n " ,(net->n));
int i;
// -thresh 0.25
//yolo层:yolov3-tiny中共有两层
//三个prior预测框,对每个预测框中,置信度大于thresh 0.25,记为一次,将次数进行累加,并输出
//nboxes:即为要保留的box的个数 两个yolo层中的置信度个数一起累加
int nboxes = num_detections(net, thresh);//-thresh 0.25
if (num) {
printf("nbox = %d \n", num);
*num = nboxes;//不执行该语句
}
//申请内存,个数为nboxes,每个内存大小为:sizeof(detection)
detection* dets = (detection*)xcalloc(nboxes, sizeof(detection));
//遍历每个box,每个dets.prob申请80个float类型的内存:
//dets.uc,申请4个float类型的空间:位置信息
for (i = 0; i < nboxes; ++i) {
dets[i].prob = (float*)xcalloc(l.classes, sizeof(float));
// tx,ty,tw,th uncertainty
dets[i].uc = (float*)xcalloc(4, sizeof(float)); // Gaussian_YOLOv3
if (l.coords > 4) {//不执行这个分支 l.coords:0
dets[i].mask = (float*)xcalloc(l.coords - 4, sizeof(float));
}
}
return dets;
}
使用get_yolo_detections来统计两层yolo层的预测信息:
//w,h:640,424 netw, neth:416,416 thresh:图像置信度阈值0.25 hier:0.5
//map:0 relative:1 letter:0
//根据yolo层 计算满足置信度阈值要求的box相对的预测坐标、宽度和高度,并将结果保存在dets[count].bbox结构体中
//每个box有80个类别,有一个置信度,该类别对应的可能性prob:class概率*置信度
///舍弃prob小于阈值0.25的box
int get_yolo_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, int relative, detection *dets, int letter)
{
printf("\n l.batch = %d, l.w = %d, l.h = %d, l.n = %d ,netw = %d, neth = %d \n", l.batch, l.w, l.h, l.n, netw, neth);
int i,j,n;
float *predictions = l.output;//yolo层的输出
// This snippet below is not necessary
// Need to comment it in order to batch processing >= 2 images
//if (l.batch == 2) avg_flipped_yolo(l);
int count = 0;
//printf("yolo layer l.mask[0] is %d, l.mask[1] is %d, l.mask[2] is %d\n", l.mask[0], l.mask[1], l.mask[2]);
//printf("yolo layer l.biases[l.mask[0]*2] is %f, l.biases[l.mask[1]*2] is %f, l.biases[l.mask[2]*2] is %f\n", l.biases[l.mask[0] * 2], l.biases[l.mask[1] * 2], l.biases[l.mask[2] * 2]);
//遍历yolo层
for (i = 0; i < l.w*l.h; ++i){//该yolo层输出特征图的宽度、高度:13x13 26x26
int row = i / l.w;
int col = i % l.w;
for(n = 0; n < l.n; ++n){//yolo层,l.n = 3
//obj_index:置信度层索引
int obj_index = entry_index(l, 0, n*l.w*l.h + i, 4);//obj_index = n*l.w*l.h*(4+l.classes+1) + 4*l.w*l.h + i;
float objectness = predictions[obj_index];//获得对应的置信度
//if(objectness <= thresh) continue; // incorrect behavior for Nan values
if (objectness > thresh) {//只有置信度大于阈值才开始执行该分支
//printf("\n objectness = %f, thresh = %f, i = %d, n = %d \n", objectness, thresh, i, n);
//box_index:yolo层每个像素点有三个box,表示每个box的索引值
int box_index = entry_index(l, 0, n*l.w*l.h + i, 0);//box_index = n*l.w*l.h*(4+l.classes+1)+ i;
//l.biases->偏置参数起始地址 l.mask[n]:分别为3,4,5,0,1,2,biases偏置参数偏移量
//根据yolo层 计算满足置信度阈值要求的box相对的预测坐标、宽度和高度,并将结果保存在dets[count].bbox结构体中
dets[count].bbox = get_yolo_box(predictions, l.biases, l.mask[n], box_index, col, row, l.w, l.h, netw, neth, l.w*l.h);
//获取对应的置信度,该置信度经过了logistic
dets[count].objectness = objectness;
//获得分类数:80(int类型)
dets[count].classes = l.classes;
for (j = 0; j < l.classes; ++j) {
//80个类别,每个类别对应的概率,class_index为其所在层的索引
int class_index = entry_index(l, 0, n*l.w*l.h + i, 4 + 1 + j);//class_index = n*l.w*l.h*(4+l.classes+1) + (4+1+j)*l.w*l.h + i;
//每个box有80个类别,有一个置信度,该类别对应的可能性prob:class概率*置信度
float prob = objectness*predictions[class_index];
//舍弃prob小于阈值0.25的box
dets[count].prob[j] = (prob > thresh) ? prob : 0;
}
++count;
}
}
}
correct_yolo_boxes(dets, count, w, h, netw, neth, relative, letter);
return count;
}
//dets:box结构体 nboxes:满足阈值的box个数 l.classe:80 thresh=0.45f
//两个box,同一类别进行非极大值抑制,遍历
void do_nms_sort(detection *dets, int total, int classes, float thresh)
{
int i, j, k;
k = total - 1;
for (i = 0; i <= k; ++i) {//box个数
if (dets[i].objectness == 0) {//置信度==0 不执行该分支,理论上没有objectness = 0
printf("there is no objectness == 0 !!! \n");
detection swap = dets[i];
dets[i] = dets[k];
dets[k] = swap;
--k;
--i;
}
}
total = k + 1;
//同一类别进行比较
for (k = 0; k < classes; ++k) {//80个
//box预测的类别
for (i = 0; i < total; ++i) {//box个数
dets[i].sort_class = k;
}
//函数作用:将prob较大的box排列到前面
qsort(dets, total, sizeof(detection), nms_comparator_v3);
for (i = 0; i < total; ++i) {//两个box,同一类别进行非极大值抑制
//printf(" k = %d, \t i = %d \n", k, i);
if (dets[i].prob[k] == 0) continue;
box a = dets[i].bbox;
for (j = i + 1; j < total;++j){
box b = dets[j].bbox;
if( box_iou(a, b) > thresh) dets[j].prob[k] = 0;
}
}
}
}