Depthwise卷积与Pointwise卷积

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 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:

  1. Split the input and filter into channels.
  2. We convolve each input with the respective filter.
  3. We stack the convolved outputs together.

同于常规卷积操作,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(目的: 利用不同通道在相同空间位置上的feature信息 )

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的神经网络层数可以做的更深。

开源数据集整理

小目标检测

1.AI-TOD航空图像数据集

数据集下载地址:http://m6z.cn/5MjlYkAI-TOD 在 28,036 张航拍图像中包含 8 个类别的 700,621 个对象实例。与现有航拍图像中的目标检测数据集相比,AI-TOD 中目标的平均大小约为 12.8 像素,远小于其他数据集。

此图片的alt属性为空;文件名为image-4.jpeg

2.iSAID航空图像大规模数据集

数据集下载地址:http://m6z.cn/6nUrYe现有的 Earth Vision 数据集要么适用于语义分割,要么适用于对象检测。iSAID 是第一个用于航空图像实例分割的基准数据集。这个大规模和密集注释的数据集包含 2,806 张高分辨率图像的 15 个类别的 655,451 个对象实例。iSAID 的显着特征如下:(a) 大量具有高空间分辨率的图像,(b) 十五个重要且常见的类别,(c) 每个类别的大量实例,(d) 每个类别的大量标记实例图像,这可能有助于学习上下文信息,(e) 巨大的对象尺度变化,通常在同一图像内包含小、中和大对象,(f) 图像内具有不同方向的对象的不平衡和不均匀分布,描绘真实-生活空中条件,(g)几个小尺寸物体,外观模糊,只能通过上下文推理来解决,(h)由专业注释者执行的精确实例级注释,由符合良好规范的专家注释者交叉检查和验证定义的指导方针。

3.TinyPerson数据集

数据集下载地址:http://m6z.cn/6vqF3T在 TinyPerson 中有 1610 个标记图像和 759 个未标记图像(两者主要来自同一视频集),总共有 72651 个注释。

4.Deepscores 数据集

数据集下载地址:http://m6z.cn/5xgYdYDeepScores 数据集的目标是推进小物体识别的最新技术,并将物体识别问题置于场景理解的背景下。DeepScores 包含高质量的乐谱图像,分为 300 0 000 张书面音乐,其中包含不同形状和大小的符号。拥有近一亿个小对象,这使得我们的数据集不仅独一无二,而且是最大的公共数据集。DeepScores 带有用于对象分类、检测和语义分割的基本事实。因此,DeepScores 总体上对计算机视觉提出了相关挑战,超出了光学音乐识别 (OMR) 研究的范围。

5.密集行人检测数据集

数据集下载地址:http://m6z.cn/6nUs1CWiderPerson 数据集是野外行人检测基准数据集,其图像选自广泛的场景,不再局限于交通场景。我们选择了 13,382 张图像并标记了大约 400K 带有各种遮挡的注释。我们随机选择 8000/1000/4382 图像作为训练、验证和测试子集。与 CityPersons 和 WIDER FACE 数据集类似,我们不发布测试图像的边界框基本事实。用户需要提交最终的预测文件,我们将进行评估。

6.加州理工学院行人检测数据集

数据集下载地址:http://m6z.cn/5N3Yk7加州理工学院行人数据集由大约 10 小时的 640×480 30Hz 视频组成,该视频取自在城市环境中通过常规交通行驶的车辆。注释了大约 250,000 帧(在 137 个大约分钟长的片段中),总共 350,000 个边界框和 2300 个独特的行人。注释包括边界框和详细的遮挡标签之间的时间对应关系。

7.NWPU VHR-10卫星图像数据集

数据集下载地址: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》。

8.Inria 航空影像数据集

数据集下载地址:http://m6z.cn/6nUs6sInria 航空影像标注解决了遥感中的一个核心主题:航空影像的自动像素级标注(论文链接)。数据集特点:

  • 覆盖面积 810 平方公里(405 平方公里用于训练,405 平方公里用于测试)
  • 空间分辨率为 0.3 m 的航空正射校正彩色图像
  • 两个语义类的地面实况数据:构建和非构建(仅针对训练子集公开披露)
  • 这些图像涵盖了不同的城市住区,从人口稠密的地区(例如,旧金山的金融区)到高山城镇(例如,奥地利蒂罗尔的 Lienz)。

9.RSOD遥感图像数据集

数据集下载地址:http://m6z.cn/5EN96H它是一个开放的遥感图像目标检测数据集。数据集包括飞机、油箱、游乐场和立交桥。此数据集的格式为PASCAL VOC。数据集包括4个文件,每个文件用于一种对象。

  • 飞机数据集,446张图片中有4993架飞机。
  • 操场,189张图片中的191个操场。
  • 天桥,176张图片中的180座天桥。
  • 油箱,165张图片中的1586个油箱。

10.小目标检测数据集

数据集下载地址:http://m6z.cn/616t6R从Internet(例如YouTube或Google)上的图像/视频收集的四个小物体数据集,包括4种类型的图像,可用于小物体目标检测的研究。数据集包含四类:

  • fly:飞行数据集,包含600个视频帧,平均每帧86±39个物体(648×72 @ 30 fps)。32张图像用于训练(1:6:187),50张图像用于测试(301:6:600)。
  • honeybee:蜜蜂数据集,包含118张图像,每张图像平均有28±6个蜜蜂(640×480)。数据集被平均分配用于训练和测试集。仅前32张图像用于训练。
  • seagull:海鸥数据集,包含三个高分辨率图像(624×964),每个图像平均有866±107个海鸥。第一张图片用于训练,其余图片用于测试。
  • fish:鱼数据集,包含387帧视频数据,平均每帧56±9条鱼(300×410 @ 30 fps)。32张图像进行训练(1:3:94),65张图像进行测试(193:3:387)。

目标检测开源数据集

11.COCO2017数据集

COCO2017是2017年发布的COCO数据集的一个版本,主要用于COCO在2017年后持有的物体检测任务、关键点检测任务和全景分割任务。

12.火焰和烟雾图像数据集

数据集链接:http://m6z.cn/6fzn0f该数据集由早期火灾和烟雾的图像数据集组成。数据集由在真实场景中使用手机拍摄的早期火灾和烟雾图像组成。大约有7000张图像数据。图像是在各种照明条件(室内和室外场景)、天气等条件下拍摄的。该数据集非常适合早期火灾和烟雾探测。数据集可用于火灾和烟雾识别、检测、早期火灾和烟雾、异常检测等。数据集还包括典型的家庭场景,如垃圾焚烧、纸塑焚烧、田间作物焚烧、家庭烹饪等。本文仅含100张左右。

13.DOTA航拍图像数据集

数据集链接:http://m6z.cn/6vIKlJDOTA是用于航空图像中目标检测的大型数据集。它可以用于开发和评估航空图像中的目标探测器。这些图像是从不同的传感器和平台收集的。每个图像的大小在800×800到20000×20000像素之间,包含显示各种比例、方向和形状的对象。DOTA图像中的实例由航空图像解释专家通过任意(8 d.o.f.)四边形进行注释。

14. AITEX数据集

数据集链接:http://m6z.cn/5DdJL1该数据库由七个不同织物结构的245张4096 x 256像素图像组成。数据库中有140个无缺陷图像,每种类型的织物20个,除此之外,有105幅纺织行业中常见的不同类型的织物缺陷(12种缺陷)图像。图像的大尺寸允许用户使用不同的窗口尺寸,从而增加了样本数量。

15. T-LESS数据集

数据集链接:http://m6z.cn/5wnucm该数据集采集的目标为工业应用、纹理很少的目标,同时缺乏区别性的颜色,且目标具有对称性和互相关性,数据集由三个同步的传感器获得,一个结构光传感器,一个RGBD sensor,一个高分辨率RGBsensor,从每个传感器分别获得了3.9w训练集和1w测试集,此外为每个目标创建了2个3D model,一个是CAD手工制作的另一个是半自动重建的。训练集图片的背景大多是黑色的,而测试集的图片背景很多变,会包含不同光照、遮挡等等变换(之所以这么做作者说是为了使任务更具有挑战性)。同时作者解释了本数据集的优势在于:1.大量跟工业相关的目标;2.训练集都是在可控的环境下抓取的;3.测试集有大量变换的视角;4.图片是由同步和校准的sensor抓取的;5.准确的6D pose标签;6.每个目标有两种3D模型;

16.H²O 行人交互检测数据集

数据集链接:http://m6z.cn/6fzmQfH²O由V-COCO数据集中的10301张图像组成,其中添加了3635张图像,这些图像主要包含人与人之间的互动。所有的H²O图像都用一种新的动词分类法进行了注释,包括人与物和人与人之间的互动。该分类法由51个动词组成,分为5类:

  • 描述主语一般姿势的动词
  • 与主语移动方式有关的动词
  • 与宾语互动的动词
  • 描述人与人之间互动的动词
  • 涉及力量或暴力的互动动词

17.SpotGarbage垃圾识别数据集

数据集链接:http://m6z.cn/5ZMmRG图像中的垃圾(GINI)数据集是SpotGarbage引入的一个数据集,包含2561张图像,956张图像包含垃圾,其余的是在各种视觉属性方面与垃圾非常相似的非垃圾图像。

18.NAO自然界对抗样本数据集

数据集链接:http://m6z.cn/5KJWJANAO包含7934张图像和9943个对象,这些图像未经修改,代表了真实世界的场景,但会导致最先进的检测模型以高置信度错误分类。与标准MSCOCO验证集相比,在NAO上评估时,EfficientDet-D7的平均精度(mAP)下降了74.5%。

19.Labelme 图像数据集

数据集链接:http://m6z.cn/5Sg9NXLabelme Dataset 是用于目标识别的图像数据集,涵盖 1000 多个完全注释和 2000 个部分注释的图像,其中部分注释图像可以被用于训练标记算法 ,测试集拥有来自于世界不同地方拍摄的图像,这可以保证图片在续联和测试之间会有较大的差异。该数据集由麻省理工学院 –计算机科学和人工智能实验室于 2007 年发布,相关论文有《LabelMe: a database and web-based tool for image annotation》。

20.印度车辆数据集

数据集链接:http://m6z.cn/6uxAIx该数据集包括小众印度车辆的图像,如Autorikshaw、Tempo、卡车等。该数据集由用于分类和目标检测的小众印度车辆图像组成。据观察,这些小众车辆(如autorickshaw、tempo、trucks等)上几乎没有可用的数据集。这些图像是在白天、晚上和晚上的不同天气条件下拍摄的。该数据集具有各种各样的照明、距离、视点等变化。该数据集代表了一组非常具有挑战性的利基类车辆图像。该数据集可用于驾驶员辅助系统、自动驾驶等的图像识别和目标检测。

21.Seeing 3D chairs椅子检测模型

数据集链接:http://m6z.cn/5DdK0v椅子数据集包含大约1000个不同三维椅子模型的渲染图像。

22.SUN09场景理解数据集

数据集链接: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》。

23.Unsplash图片检索数据集

数据集链接:http://m6z.cn/5wnuoM使用迄今为止公开共享的全球最大的开放检索信息数据集。Unsplash数据集由250000多名贡献摄影师创建,并包含了数十亿次照片搜索的信息和对应的照片信息。由于Unsplash数据集中包含广泛的意图和语义,它为研究和学习提供了新的机会。

24.HICO-DET人物交互检测数据集

数据集链接:http://m6z.cn/5DdK6DHICO-DET是一个用于检测图像中人-物交互(HOI)的数据集。它包含47776幅图像(列车组38118幅,测试组9658幅),600个HOI类别,由80个宾语类别和117个动词类别构成。HICO-DET提供了超过150k个带注释的人类对象对。V-COCO提供了10346张图像(2533张用于培训,2867张用于验证,4946张用于测试)和16199人的实例。

25.上海科技大学人群统计数据集

数据集链接:http://m6z.cn/5Sgafn上海科技数据集是一个大规模的人群统计数据集。它由1198张带注释的群组图像组成。数据集分为两部分,A部分包含482张图像,B部分包含716张图像。A部分分为训练和测试子集,分别由300和182张图像组成。B部分分为400和316张图像组成的序列和测试子集。群组图像中的每个人都有一个靠近头部中心的点进行注释。总的来说,该数据集由33065名带注释的人组成。A部分的图像是从互联网上收集的,而B部分的图像是在上海繁忙的街道上收集的。

26.生活垃圾数据集

数据集链接:http://m6z.cn/6n5Adu大约9000多张独特的图片。该数据集由印度国内常见垃圾对象的图像组成。图像是在各种照明条件、天气、室内和室外条件下拍摄的。该数据集可用于制作垃圾/垃圾检测模型、环保替代建议、碳足迹生成等。

27.RMFD口罩遮挡人脸数据集

数据集下载地址:http://m6z.cn/61z9Fv当前大多数高级人脸识别方法都是基于深度学习而设计的,深度学习取决于大量人脸样本。但是,目前尚没有公开可用的口罩遮挡人脸识别数据集。为此,这项工作提出了三种类型的口罩遮挡人脸数据集,包括口罩遮挡人脸检测数据集(MFDD),真实口罩遮挡人脸识别数据集(RMFRD)和模拟口罩遮挡人脸识别数据集(SMFRD)。基于这些数据集,可以开发口罩遮挡人脸的各种应用。本项目开发的多粒度口罩遮挡人脸识别模型可达到95%的准确性,超过了行业报告的结果。

28.GTSRB德国交通标志数据集

数据集下载地址:http://m6z.cn/5wJJLA德国交通标志基准测试是在 2011 年国际神经网络联合会议 (IJCNN) 上举办的多类单图像分类挑战赛。我们诚邀相关领域的研究人员参与:该比赛旨在参与者无需特殊领域知识。我们的基准测试具有以下属性:

  • 单图像、多类分类问题
  • 40多个分类
  • 总共超过 50,000 张图片
  • 逼真的大型数据库

29.VOC2005车辆数据集

数据集下载地址:http://m6z.cn/5U2X4u该数据集中含有自行车、摩托车、汽车、货车的图像数据,可用于CNN模型以实现车辆识别和车辆分类,其中自行车、摩托车、汽车数据来自2005 PASCAL视觉类挑战赛(VOC2005)所使用的数据的筛选处理结果,货车图片来自网络收集,后期通过筛选处理得到。在本数据中,训练数据集与测试数据集占比约为5:1。

30.Winegrape检测数据集

数据集下载地址:http://m6z.cn/5TikF9WGISD(Wine Grape Instance Segmentation Dataset)是为了提供图像和注释来研究对象检测和实例分割,用于葡萄栽培中基于图像的监测和现场机器人技术。它提供了来自五种不同葡萄品种的实地实例。这些实例显示了葡萄姿势、光照和焦点的变化,包括遗传和物候变化,如形状、颜色和紧实度。可能的用途包括放宽实例分割问题:分类(图像中是否有葡萄?)、语义分割(图像中的“葡萄像素”是什么?)、对象检测(图像中的葡萄在哪里?)、和计数(每个簇有多少浆果?)。

31.全球小麦检测数据集

数据集下载地址:http://m6z.cn/5wJK64检测小麦穗是一项重要任务,可以估计相关性状,包括穗种群密度和穗特征,如卫生状况、大小、成熟阶段和芒的存在。本数据集包含 4,700 张高分辨率 RGB 图像和 190,000 个标记的小麦头,这些小麦头采集自世界各地不同生长阶段的不同基因型的多个国家。

32.Linkopings交通标志数据集

数据集下载地址:http://m6z.cn/68ldS0通过记录超过 350 公里的瑞典高速公路和城市道路的序列,创建了一个数据集。一个 1.3 兆像素的彩色摄像机,一个点灰色变色龙,被放置在一辆汽车的仪表板上,从前窗向外看。摄像头略微指向右侧,以便尽可能多地覆盖相关标志。该镜头的焦距为 6.5 毫米,视野约为 41 度。高速公路上的典型速度标志大约为 90 cm 宽,如果要在大约 30 m 的距离处检测到它们,则对应于大约 50 像素的大小。总共记录了超过 20 000 帧,其中每五帧被手动标记。每个标志的标签包含标志类型(人行横道、指定车道右侧、禁止站立或停车、优先道路、让路、50 公里/小时或 30 公里/小时)、能见度状态(遮挡、模糊或可见)和道路状态(是否标志是在正在行驶的道路上或在小路上)。

33.防护装备-头盔和背心检测

数据集下载地址:http://m6z.cn/61zarT包含 774 个众包图像和 698 个网络挖掘图像。众包和网络挖掘的图像分别包含 2,496 和 2,230 个工人实例。

34.加州理工学院相机陷阱数据集

数据集链接:https://beerys.github.io/CaltechCameraTraps/该数据集包含来自美国西南部 140 个摄像头位置的 243,100 张图像,带有 21 个动物类别的标签(加上空白),主要是在物种级别(例如,最常见的标签是负鼠、浣熊和土狼),以及 大约 66,000 个边界框注释。大约 70% 的图像被标记为空。

35.水下垃圾检测数据集

数据集下载地址:http://m6z.cn/6nnDQK该数据来自 J-EDI 海洋垃圾数据集。构成该数据集的视频在质量、深度、场景中的对象和使用的相机方面差异很大。它们包含许多不同类型的海洋垃圾的图像,这些图像是从现实世界环境中捕获的,提供了处于不同衰减、遮挡和过度生长状态的各种物体。此外,水的清晰度和光的质量因视频而异。这些视频经过处理以提取 5,700 张图像,这些图像构成了该数据集,所有图像都在垃圾实例、植物和动物等生物对象以及 ROV 上标有边界框。

工业检测数据集

36.坑洼检测数据集

数据集下载地址:http://m6z.cn/5wJJTa本数据集汇总了700个在坑洼处带有3K +注释的图像,用于从道路图像中检测坑洼,检测道路地形和坑洼。

37.天池铝型材表面缺陷数据集

数据集下载地址:http://m6z.cn/61EksR大赛数据集里有1万份来自实际生产中有瑕疵的铝型材监测影像数据,每个影像包含一个或多种瑕疵。供机器学习的样图会明确标识影像中所包含的瑕疵类型。

38.Kylberg 纹理数据集

数据集下载地址:http://m6z.cn/61Ekw5在布匹的实际生产过程中,由于各方面因素的影响,会产生污渍、破洞、毛粒等瑕疵,为保证产品质量,需要对布匹进行瑕疵检测。布匹疵点检验是纺织行业生产和质量管理的重要环节,目前人工检测易受主观因素影响,缺乏一致性;并且检测人员在强光下长时间工作对视力影响极大。由于布匹疵点种类繁多、形态变化多样、观察识别难道大,导致布匹疵点智能检测是困扰行业多年的技术瓶颈。本数据涵盖了纺织业中布匹的各类重要瑕疵,每张图片含一个或多种瑕疵。数据包括包括素色布和花色布两类,其中,素色布数据约8000张;花色布数据约12000张。

39.东北大学带钢表面缺陷数据集

数据集下载地址:http://m6z.cn/5U87us数据集收集了夹杂、划痕、压入氧化皮、裂纹、麻点和斑块6种缺陷,每种缺陷300张,图像尺寸为200×200。数据集包括分类和目标检测两部分,不过目标检测的标注中有少量错误,需要注意。

40.Severstal 带钢缺陷数据集

数据集下载地址:http://m6z.cn/61EkBp该数据集中提供了四种类型的带钢表面缺陷。训练集共有12568张,测试集5506张。图像尺寸为1600×256。

41.UCI 带钢缺陷数据集

数据集下载地址:http://m6z.cn/61EkUh该数据集包含了7种带钢缺陷类型。这个数据集不是图像数据,而是带钢缺陷的28种特征数据,可用于机器学习项目。钢板故障的7种类型:装饰、Z_划痕、K_划痕、污渍、肮脏、颠簸、其他故障。

42.DAGM 2007数据集

数据集下载地址:http://m6z.cn/5F5eQV该数据集主要针对纹理背景上的杂项缺陷,为较弱监督的训练数据。包含十个数据集,前六个为训练数据集,后四个为测试数据集。每个数据集均包含以灰度8位PNG格式保存的1000个“无缺陷”图像和150个“有缺陷”图像,每个数据集由不同的纹理模型和缺陷模型生成。“无缺陷”图像显示的背景纹理没有缺陷,“无缺陷”图像的背景纹理上恰好有一个标记的缺陷。所有数据集已随机分为大小相等的训练和测试子数据集。弱标签以椭圆形表示,大致表示缺陷区域。

43.磁瓦缺陷数据集

数据集下载地址:http://m6z.cn/5F5eSd中国科学院自动所一个课题组收集的数据集,是“Saliency of magnetic tile surface defects”这篇论文的数据集。收集了6种常见磁瓦缺陷的图像,并做了语义分割的标注。

44.RSDDs铁轨表面缺陷数据集

数据集下载地址:http://m6z.cn/61EkKLRSDDs数据集包含两种类型的数据集:第一种是从快车道捕获的I型RSDDs数据集,其中包含67个具有挑战性的图像。第二个是从普通/重型运输轨道捕获的II型RSDDs数据集,其中包含128个具有挑战性的图像。两个数据集的每幅图像至少包含一个缺陷,并且背景复杂且噪声很大。RSDDs数据集中的这些缺陷已由一些专业的人类观察员在轨道表面检查领域进行了标记。

45.KTH-TIPS 纹理图像数据集

数据集下载地址:http://m6z.cn/61EkMHKTH-TIPS 是一个纹理图像数据集,在不同的光照、角度和尺度下拍摄的不同材质表面纹理图片。类型包括砂纸、铝箔、发泡胶、海绵、灯芯绒、亚麻、棉、黑面包、橙皮和饼干共10类。

46.印刷电路板(PCB)瑕疵数据集

数据集下载地址:http://m6z.cn/5U87Ji这是一个公共的合成PCB数据集,由北京大学发布,其中包含1386张图像以及6种缺陷(缺失孔,鼠咬坏,开路,短路,杂散,伪铜),用于检测,分类和配准任务。

人脸识别常用开源数据集

47.IMDB-WIKI人脸数据集

数据集下载地址:http://m6z.cn/6gGnTdIMDB-WIKI 500k+ 是一个包含名人人脸图像、年龄、性别的数据集,图像和年龄、性别信息从 IMDB 和 WiKi 网站抓取,总计 524230 张名人人脸图像及对应的年龄和性别。其中,获取自 IMDB 的 460723 张,获取自 WiKi 的 62328 张。

48.WiderFace人脸检测数据集

数据集下载地址: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%作为训练、验证、测试集。

49.LFW 人像图像数据集

数据集下载地址:http://m6z.cn/61EnzL该数据集是用于研究无约束面部识别问题的面部照片数据库。数据集包含从网络收集的13000多张图像。每张脸都贴上了所画的人的名字,图片中的1680人在数据集中有两个或更多不同的照片。

50.GENKI 人脸图像数据集

数据集下载地址:http://m6z.cn/5F5hLpGENKI数据集是由加利福尼亚大学的机器概念实验室收集。该数据集包含GENKI-R2009a,GENKI-4K,GENKI-SZSL三个部分。GENKI-R2009a包含11159个图像,GENKI-4K包含4000个图像,分为“笑”和“不笑”两种,每个图片的人脸的尺度大小,姿势,光照变化,头的转动等都不一样,专门用于做笑脸识别。GENKI-SZSL包含3500个图像,这些图像包括广泛的背景,光照条件,地理位置,个人身份和种族等。

51.哥伦比亚大学公众人物脸部数据库

数据集链接:http://m6z.cn/5DlIR9PubFig Dataset 是一个大型人脸数据集,主要用于人脸识别和身份鉴定,其涵盖互联网上 200 人的 58,797 张图像,不同于大多数现有面部数据集,这些图像是在主体完全不受控制的情况下拍摄的,因此不同图像中姿势、光照、表情、场景、相机、成像条件和参数存在较大差异,该数据集类似于 UMass-Amherst 创建的 LFW 数据集。该数据集由哥伦比亚大学于 2009 年发布,相关论文有《Attribute and Simile Classifiers for Face Verification》。

52.CelebA人脸数据集

数据集链接:http://m6z.cn/60EW0nCelebFaces Attributes Dataset (CelebA) 是一个大规模的人脸属性数据集,包含超过 20 万张名人图像,每张都有 40 个属性注释。该数据集中的图像涵盖了较大的姿势变化和杂乱的背景。CelebA 种类多、数量多、注释丰富,包括10,177 个身份,202,599 张人脸图像,以及5 个地标位置,每张图像 40 个二进制属性注释。该数据集可用作以下计算机视觉任务的训练和测试集:人脸属性识别、人脸识别、人脸检测、地标(或人脸部分)定位以及人脸编辑与合成。

53.美国国防部人脸库

数据集链接:http://m6z.cn/5So6DB为促进人脸识别算法的研究和实用化,美国国防部的Counterdrug Technology Transfer Program(CTTP)发起了一个人脸识别技术(Face Recognition Technology 简称FERET)工程,它包括了一个通用人脸库以及通用测试标准。到1997年,它已经包含了1000多人的10000多张照片,每个人包括了不同表情,光照,姿态和年龄的照片。

54.MTFL人脸识别数据集

数据集链接:http://m6z.cn/6fHmaT该数据集包含 12,995 张人脸图像,这些图像用 (1) 五个面部标志,(2) 性别、微笑、戴眼镜和头部姿势的属性进行了注释。

55.BioID人脸数据集

数据集链接:http://m6z.cn/5ZUjyC这个数据集包含了1521幅分辨率为384×286像素的灰度图像。每一幅图像来自于23个不同的测试人员的正面角度的人脸。为了便于做比较,这个数据集也包含了对人脸图像对应的手工标注的人眼位置文件。图像以 “BioID_xxxx.pgm”的格式命名,其中xxxx代表当前图像的索引(从0开始)。类似的,形如”BioID_xxxx.eye”的文件包含了对应图像中眼睛的位置。

56.PersonID人脸识别数据集

数据集链接:http://m6z.cn/5So6vR该数据集所选用的人脸照片均来自于两部比较知名的电视剧,《吸血鬼猎人巴菲》和《生活大爆炸》。

57.CMU PIE人脸库

数据集链接:http://m6z.cn/5vPwfOCMU PIE人脸库建立于2000年11月,它包括来自68个人的40000张照片,其中包括了每个人的13种姿态条件,43种光照条件和4种表情下的照片,现有的多姿态人脸识别的文献基本上都是在CMU PIE人脸库上测试的。

58.Youtube视频人脸数据集

数据集链接:http://m6z.cn/6u3P2V该数据集包含 1,595 个不同人的 3,425 个视频。所有视频都是从 YouTube 下载的。每个主题平均有 2.15 个视频可用。最短剪辑时长为 48 帧,最长剪辑为 6070 帧,视频剪辑的平均长度为 181.3 帧。在这个数据集下,算法需要判断两段视频里面是不是同一个人。有不少在照片上有效的方法,在视频上未必有效/高效。

59.CASIA 人脸图像数据集

数据集链接:http://m6z.cn/5vPwioCASIA 人脸图像数据库版本 5.0(或 CASIA-FaceV5)包含 500 个对象的 2,500 个彩色人脸图像。CASIA-FaceV5 的面部图像是使用罗技 USB 摄像头在一个会话中捕获的。CASIA-FaceV5的志愿者包括研究生、工人、服务员等。所有人脸图像均为16位彩色BMP文件,图像分辨率为640*480。典型的类内变化包括照明、姿势、表情、眼镜、成像距离等。

60.Caltech人脸数据库

数据集链接:http://m6z.cn/5So6VP该数据集包含通过在谷歌图片搜索中输入常见的名字从网络上收集的人的图像。每个正面的眼睛、鼻子和嘴巴中心的坐标在地面实况文件中提供。此信息可用于对齐和裁剪人脸或作为人脸检测算法的基本事实。该数据集有 10,524 个不同分辨率和不同设置的人脸,例如 肖像图像、人群等。侧面或非常低分辨率的面孔未标记。

人体姿态估计

61.MPII人体模型数据集

数据集链接:http://m6z.cn/69aaIeMPII Human Shape 人体模型数据是一系列人体轮廓和形状的3D模型及工具。模型是从平面扫描数据库 CAESAR 学习得到。

62.MPII人类姿态数据集

数据集链接:http://m6z.cn/6gGnPbMPII 人体姿态数据集是用于评估人体关节姿势估计的最先进基准。该数据集包括大约 25,000 张图像,其中包含超过 40,000 个带有注释身体关节的人。这些图像是使用已建立的人类日常活动分类法系统收集的。总的来说,数据集涵盖了 410 项人类活动,每个图像都提供了一个活动标签。每张图像都是从 YouTube 视频中提取的,并提供前后未注释的帧。此外,测试集有更丰富的注释,包括身体部位遮挡和 3D 躯干和头部方向。

63.KTH 多视图足球数据集

数据集链接:http://m6z.cn/692agI作者收集了一个带有注释关节的足球运动员数据集,可用于多视图重建。数据集包括:

  • 771张足球运动员的照片
  • 在 257 个时间实例中从 3 个视图中获取的图像
  • 14 个带注释的身体关节

64.宾夕法尼亚动作数据集

数据集链接:http://m6z.cn/692akKPenn Action Dataset(宾夕法尼亚大学)包含 15 个不同动作的 2326 个视频序列以及每个序列的人类联合注释。

65.BBC姿态数据集

数据集链接:http://m6z.cn/5xr6XqBBC Pose 包含 20 个视频(每个视频长度为 0.5 小时至 1.5 小时),由 BBC 录制,并配有手语翻译。这 20 个视频分为 10 个用于训练的视频、5 个用于验证的视频和 5 个用于测试的视频。

66.Poser 数据集

数据集链接:http://m6z.cn/6gynqzPoser 数据集是用于姿态估计的数据集,由 1927 个训练图像和 418 个测试图像组成。这些图像是综合生成的,并调整为单峰预测。这些图像是使用 Poser 软件包生成的。

67.野外 3D 姿势数据集

数据集链接:http://m6z.cn/5xr6Z2“野外 3D 姿势数据集”是野外第一个具有准确 3D 姿势用于评估的数据集。虽然存在户外其他数据集,但它们都仅限于较小的记录量。3DPW 是第一个包含从移动电话摄像头拍摄的视频片段的技术。数据集包括:

  • 60 个视频序列。
  • 2D 姿势注释。
  • 使用我们的方法获得的 3D 姿势。我们的方法利用了视频和 IMU,尽管场景很复杂,但姿势非常准确。
  • 序列中每一帧的相机姿势。
  • 3D 身体扫描和 3D 人物模型(可重新调整和重新塑造)。每个序列都包含其对应的模型。
  • 18 个不同服装款式的 3D 模型。

68.V-COCO数据集

数据集链接:http://m6z.cn/5UGaiiV-COCO是一个基于 COCO 的数据集,用于人机交互检测。V-COCO 提供 10,346 张图像(2,533 张用于训练,2,867 张用于验证,4,946 张用于测试)和 16,199 个人物实例。每个人都有 29 个动作类别的注释,并且没有包括对象在内的交互标签。

69.宜家 ASM 数据集

数据集链接:http://m6z.cn/692aos宜家 ASM 数据集是装配任务的多模式和多视图视频数据集,可对人类活动进行丰富的分析和理解。它包含 371 个家具组件样本及其真实注释。每个样本包括 3 个 RGB 视图、一个深度流、原子动作、人体姿势、对象片段、对象跟踪和外部相机校准。

70.立体人体姿势估计数据集

数据集链接:http://m6z.cn/62cnp5这是一个立体图像对数据集,适用于上身人的立体人体姿态估计。SHPED 由 630 个立体图像对(即 1260 个图像)组成,分为 42 个视频片段,每个片段 15 帧。这些剪辑是从 26 个立体视频中提取的,这些视频是从 YouTube 获得的,标签为 yt3d:enable = true。此外,SHPED 包含 1470 条火柴人上身注释,对应于 49 个人根据这些条件:直立位置、所有上身部分几乎可见以及身体的非侧面视点。

71.AIST++ 舞蹈动作数据集

数据集链接:http://m6z.cn/5xr6M8AIST++ 舞蹈动作数据集是从 AIST 舞蹈视频数据库构建的。对于多视图视频,设计了一个精心设计的管道来估计相机参数、3D 人体关键点和 3D 人体舞蹈动作序列:它为 1010 万张图像提供 3D 人体关键点注释和相机参数,涵盖 9 个视图中的 30 个不同主题。这些属性使其成为具有 3D 人体关键点注释的最大和最丰富的现有数据集。它还包含 1,408 个 3D 人类舞蹈动作序列,表示为关节旋转以及根轨迹。舞蹈动作平均分布在 10 种舞蹈流派中,有数百种编舞。运动持续时间从 7.4 秒不等。至 48.0 秒。所有的舞蹈动作都有相应的音乐。

72.HiEve数据集

数据集链接:http://m6z.cn/6o4AAg该数据集专注于在各种人群和复杂事件中进行非常具有挑战性和现实性的以人为中心的分析任务,包括地铁上下车、碰撞、战斗和地震逃生。并且具有大规模和密集注释的标签,涵盖了以人为中心的分析中的广泛任务。

六、自动驾驶

73.KITTI 道路数据集

数据集链接:http://m6z.cn/5xz4OW道路和车道估计基准包括289次培训和290幅测试图像。我们在鸟瞰空间中评估道路和车道的估计性能。它包含不同类别的道路场景:城市无标记、城市标记、 城市多条标记车道以及以上三者的结合。

74.CrackForest数据集

数据集链接:http://m6z.cn/5xz4QoCrackForest数据集是一个带注释的道路裂缝图像数据库,可以大致反映城市路面状况。

75.KITTI-2015立体声数据集

数据集链接:http://m6z.cn/6gGlltstero 2015 基准测试包含 200 个训练场景和 200 个测试场景(每个场景 4 幅彩色图像,以无损 png 格式保存)。与stereo 2012 和flow 2012 基准测试相比,它包含动态场景,在半自动过程中为其建立了真值。该数据集是通过在卡尔斯鲁厄中等规模城市、农村地区和高速公路上行驶而捕获的。每张图像最多可以看到 15 辆汽车和 30 名行人。

76.KITTI-2015光流数据集

数据集下载地址:http://www.cvlibs.net/datasets/kitti/eval_scene_flow.php?benchmark=flowFlow 2015 基准测试包含 200 个训练场景和 200 个测试场景(每个场景 4 幅彩色图像,以无损 png 格式保存)。与stereo 2012 和flow 2012 基准测试相比,它包含动态场景,在半自动过程中为其建立了真值。该数据集是通过在卡尔斯鲁厄中等规模城市、农村地区和高速公路上行驶而捕获的。每张图像最多可以看到 15 辆汽车和 30 名行人。

77.KITTI-2015场景流数据集

数据集下载地址:http://www.cvlibs.net/datasets/kitti/eval_scene_flow.phpSceneflow 2015 基准测试包含 200 个训练场景和 200 个测试场景(每个场景 4 幅彩色图像,以无损 png 格式保存)。与stereo 2012 和flow 2012 基准测试相比,它包含动态场景,在半自动过程中为其建立了真值。该数据集是通过在卡尔斯鲁厄中等规模城市、农村地区和高速公路上行驶而捕获的。每张图像最多可以看到 15 辆汽车和 30 名行人。

78.KITTI深度数据集

数据集下载地址:http://www.cvlibs.net/datasets/kitti/eval_depth_all.phpKITTI-depth 包含超过 93,000 个深度图以及相应的原始 LiDaR 扫描和 RGB 图像。鉴于大量的训练数据,该数据集应允许训练复杂的深度学习模型,以完成深度补全和单幅图像深度预测的任务。此外,该数据集提供了带有未发布深度图的手动选择图像,作为这两个具有挑战性的任务的基准。

七、目标跟踪

79.ALOV300++跟踪数据集

数据集下载地址:http://m6z.cn/61EogvALOV++,Amsterdam Library of Ordinary Videos for tracking 是一个物体追踪视频数据,旨在对不同的光线、通透度、泛着条件、背景杂乱程度、焦距下的相似物体的追踪。

八、动作识别

80.HMDB人类动作视频数据集

数据集下载地址:http://m6z.cn/6gGlzF由布朗大学发布的人类动作视频数据集,该数据集视频多数来源于电影,还有一部分来自公共数据库以及YouTube等网络视频库。数据库包含有6849段样本,分为51类,每类至少包含有101段样本。

81.UCF50动作识别数据集

数据集下载地址:http://m6z.cn/69a8xyUCF50 是一个由中佛罗里达大学发布的动作识别数据集,由来自 youtube 的真实视频组成,包含 50 个动作类别,如棒球投球、篮球投篮、卧推、骑自行车、骑自行车、台球、蛙泳、挺举、跳水、击鼓等。对于所有 50 个类别,视频分为 25 组,其中每组由超过 4 个动作剪辑。同一组中的视频片段可能具有一些共同的特征,例如同一个人、相似背景、相似视点等。

82.SBU Kinect 交互数据集

数据集下载地址:http://m6z.cn/6vILNpSBU Kinect Interaction是一个复杂的人类活动数据集,描述了两个人的交互,包括同步视频、深度和运动捕捉数据。

图像分类数据集

83.宠物图像数据集

数据集下载地址:http://m6z.cn/5TAgdC一个包含 37 个类别的宠物数据集,每个类别大约有 200 张图像。这些图像在比例、姿势和照明方面有很大的变化。所有图像都有相关的品种、头部 ROI 和像素级三元图分割的地面实况注释。

84.猫咪数据集

数据集下载地址:http://m6z.cn/5TAgbwCAT 数据集包括超过 9,000 张猫图像。对于每张图像,猫的头部都有九个点的注释,眼睛两个,嘴巴一个,耳朵六个。

85.斯坦福狗狗数据集

数据集下载地址:http://m6z.cn/6nF6kM斯坦福狗数据集包含来自世界各地的 120 种狗的图像。该数据集是使用 ImageNet 中的图像和注释构建的,用于细粒度图像分类任务。该数据集的内容:

  • 类别数:120
  • 图片数量:20,580
  • 注释:类标签、边界框

86.CBCL 街道场景数据

数据集下载地址:http://m6z.cn/5TAgeAStreetScenes Challenge Framework 是用于对象检测的图像、注释、软件和性能测量的集合。每张图像都是从马萨诸塞州波士顿及其周边地区的 DSC-F717 相机拍摄的。然后用围绕 9 个对象类别的每个示例的多边形手动标记每个图像,包括 [汽车、行人、自行车、建筑物、树木、天空、道路、人行道和商店]。这些图像的标记是在仔细检查下完成的,以确保对象总是以相同的方式标记,关于遮挡和其他常见的图像变换。

87.Stanford 汽车图片数据

数据集下载地址:http://m6z.cn/616wopCars 数据集包含 196 类汽车的 16,185 张图像。数据分为 8,144 个训练图像和 8,041 个测试图像,其中每个类别大致按 50-50 分割。课程通常在品牌、型号、年份级别,例如 2012 Tesla Model S 或 2012 BMW M3 coupe。

88.MNIST 手写数字图像数据集

MNIST数据集是一个手写阿拉伯数字图像识别数据集,图片分辨率为 20×20 灰度图图片,包含‘0 – 9’ 十组手写手写阿拉伯数字的图片。其中,训练样本 60000 ,测试样本 10000,数据为图片的像素点值,作者已经对数据集进行了压缩。图片图片

89.Kaggle 垃圾分类图片数据集

该数据集是图片数据,分为训练集85%(Train)和测试集15%(Test)。其中O代表Organic(有机垃圾),R代表Recycle(可回收)图片图片

图像识别数据集

90.街景门牌号 (SVHN) 数据集

数据集下载地址:http://m6z.cn/5ExMWbSVHN 是一个真实世界的图像数据集,用于开发机器学习和对象识别算法,对数据预处理和格式化的要求最低。它可以被视为与MNIST风格相似(例如,图像是经过裁剪的小数字),但包含一个数量级的更多标记数据(超过 600,000 个数字图像),并且来自一个更难、未解决的现实世界问题(识别自然场景图像中的数字和数字)。SVHN 是从谷歌街景图像中的门牌号获得的。

91.3D MNIST 数字识别图像数据

数据集下载地址:http://m6z.cn/5SUfEd该数据集的目的是提供一种简单的方法来开始处理 3D 计算机视觉问题,例如 3D 形状识别。

图像分割数据集

92.LVIS数据集

LVIS是一个大规模细粒度词汇集标记数据集,该数据集针对超过 1000 类物体进行了约 200 万个高质量的实例分割标注,包含 164k 张图像。图片图片

93.高密度人群及移动物体视频数据集

Crowd Segmentation Dataset 是一个高密度人群和移动物体视频数据,视频来自BBC Motion Gallery 和 Getty Images 网站。图片图片

94.DAVIS 视频分割数据集

Densely Annotated Video Segmentation 是一个高清视频中的物体分割数据集,包括 50个 视频序列,3455个 帧标注,视频采集自高清 1080p 格式。、

NLP相关数据集

95.文档影印和内容数据

数据集下载地址:http://m6z.cn/6nF67SMediaTeam Oulu Document 数据集是一个文档扫描图像和文档内容数据集,包含 500篇 1975年之前的文档信息。

Mish:一个新的state-of-the-art激活函数,ReLU的继任者

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))

preview
Mish的PyTorch实现

函数图像:

preview

常见激活函数:

preview

强化学习:

代码学习网站:

教程:https://stable-baselines.readthedocs.io/en/master/guide/examples.html

gym使用

在做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()
https://cdn-images-1.medium.com/max/960/1*h4WTQNVIsvMXJTCpXm_TAw.gif
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()
https://cdn-images-1.medium.com/max/960/1*UHYJE7lF8IDZS_U5SsAFUQ.gif

bug解决:

在执行时:

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

yolov3 -tiny 网络实现和源码分析

摘自: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

yolov3-tiny 原理

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 源码分析

配置网络结构

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()之前时的值。	
		   ......

卷积层[convolution]

卷积层在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]

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;
                }
            }
        }
    }
}

路由层[route]

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;
    }
}

上采样层[upsample]

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]

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);
        }
    }

预测结果统计[detection ]


//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;
}

非极大值抑制[NMS]

//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;
            }
        }
    }
}

AlphaFold2论文

https://www.nature.com/articles/s41586-021-03819-2

https://github.com/deepmind/alphafold

沐神AlphaFold讲解

论文补充材料:https://www.biorxiv.org/content/10.1101/2021.10.04.463034v1

摘要

蛋白质对生命至关重要,了解它们的结构可以促进对其功能的系统理解。通过大量的实验,已经确定了大约 100,000 种独特的蛋白质结构,但这仅代表了数十亿已知蛋白质序列中的一小部分。

蛋白质结构是指蛋白质分子的空间结构。作为一类重要的生物大分子,蛋白质主要由化学元素组成。所有蛋白质都是由20种不同的L型α氨基酸连接形成的多聚体,在形成蛋白质后,这些氨基酸又被称为残基。
蛋白质一级结构:组成蛋白质多肽链的线性氨基酸序列。
蛋白质二级结构:依靠不同氨基酸之间的C=O和N-H基团间的氢键形成的稳定结构,主要为α螺旋和β折叠。
蛋白质三级结构:通过多个二级结构元素在三维空间的排列所形成的一个蛋白质分子的三维结构。
蛋白质四级结构:用于描述由不同多肽链(亚基)间相互作用形成具有功能的蛋白质复合物分子。

仅根据其氨基酸序列预测蛋白质的三级结构,这是“蛋白质折叠问题”, 50 多年来一直是一个重要的开放性研究问题。尽管最近取得了进展,但现有方法仍远未达到原子级准确度,尤其是当没有可用的同源结构时。

同源结构,那些不同物种因来自共同祖先而具有的相似性结构。 例如现代马经较长时间的修饰成为具有一个趾,鼹鼠及其他洞穴动物成为瘤状肢体,大象的肢体成为柱状,这样功能各异的前肢有一个共同来源,他们都来自原始陆生脊椎动物五趾型的肢体。

在这里,我们提供了第一种计算方法,即使在不知道相似结构的情况下,它也可以以原子精度定期预测蛋白质结构。我们在具有挑战性的第 14 次蛋白质结构预测关键评估 (CASP14) 中验证了我们基于神经网络模型的完全重新设计的AlphaFold,在大多数情况下表现出与实验相媲美的准确性,并且大大优于其他方法。支持最新版本的 AlphaFold 是一种新颖的机器学习方法,它将关于蛋白质结构的物理和生物学知识,利用多序列比对,融入深度学习算法的设计中。

序列比对指将两个或多个序列排列在一起,标明其相似之处。序列中可以插入间隔(通常用短横线“-”表示)。对应的相同或相似的符号(在核酸中是A, T(或U), C, G,在蛋白质中是氨基酸残基的单字母表示)排列在同一列上。
tcctctgcctctgccatcat—caaccccaaagt
|||| ||| ||||| ||||| ||||||||||||
tcctgtgcatctgcaatcatgggcaaccccaaagt
多序列比对是成对比对的延伸,是为了在一次比对里面处理多于两条的的序列。多序列比对方法试图比对一个指定序列集合里面的所有序列,这可以帮助确定这些序列的共同区段。进行多序列比对有几种方法,最常用的一种是Clustal程序集,它使用渐进多序列比对算法。Clustal在cladistics中被用来建立进化树,在PSI-BLAST和Hidden Markov model (HMM)中用来建立序列档案以在序列数据库中搜索更远的同源序列。

从蛋白质序列预测蛋白质3D结构的计算方法的发展沿着两条互补的路径前进,分别关注物理相互作用或进化历史。 物理相互作用方案将我们认知的分子驱动力(molecular driving forces)整合到物理热力学或动力学模拟或统计模型逼近中。 虽然理论上非常吸引人,但由于分子模拟的计算难度、蛋白质稳定性的上下文依赖性以及难以产生足够准确的蛋白质物理学模型,这种方法已被证明对即使是中等大小的蛋白质也极具挑战性。 近年来,进化方案提供了一种替代方案,其中蛋白质结构约束来自蛋白质进化历史的生物信息学分析、与已解决结构的同源性和成对进化相关性。

这种生物信息学方法极大地受益于蛋白质数据库 (PDB) 中存储的实验蛋白质结构的稳定增长、基因组测序的爆炸式增长以及用于解释这些相关性的深度学习技术的快速发展。 尽管取得了这些进展,基于物理和进化历史的方法产生的预测远低于实验准确性。

我们(DeepMind团队)开发了第一个能够在大多数情况下预测蛋白质结构接近实验准确性的计算方法。 我们开发的神经网络 AlphaFold 已进入 CASP14 评估(2020 年 5 月至 7 月)。 CASP 评估每两年进行一次,使用未在 PDB 中存放或公开披露的最近解决的结构,因此它是对参与方法的盲测,长期以来一直作为结构预测准确性的金标准评估。

蛋白质结构预测 (CASP) 实验的批判性评估旨在建立蛋白质结构预测的当前技术水平,确定已取得的进展,并突出未来可能最有成效的工作重点。

… 省略了部分内容

在图 2a 中证明,CASP14 中展示AlphaFold的高准确率扩展到最近大量PDB结构样本,其中所有结构在我们的训练数据截止后都存储在PDB中,并作为完整链进行分析。此外,当主链预测准确时,观察到高侧链准确性(图 2b),并且表明我们预测的局部距离差异测试(pLDDT)置信度可靠地预测了 Ca 局部距离差异测试(lDDT-Cα)准确度相应的预测(图 2c)。我们还发现可以准确估计全局叠加度量模板建模分数 (TM-score)(图 2d)。总体而言,这些验证了 AlphaFold 在 CASP14 蛋白上的高精度和可靠性也可以迁移到最近 PDB 提交的未经整理的数据集中。

Fig. 1 | AlphaFold produces highly accurate structures.

PDB蛋白质结构数据库(Protein Data Bank,简称PDB)是美国Brookhaven国家实验室于1971年创建的,由结构生物信息学研究合作组织(Research Collaboratory for Structural Bioinformatics,简称RCSB)维护。和核酸序列数据库一样,可以通过网络直接向PDB数据库提交数据。

AlphaFold网络

AlphaFold 通过结合基于蛋白质结构的进化、物理和几何约束的新型神经网络架构和训练程序,大大提高了结构预测的准确性。特别是,我们展示了一种联合嵌入多序列比对 (MSA) 和成对特征的新架构、一种新的输出表示和相关损失函数,可实现准确的端到端结构预测、新的等变注意架构、使用中间损失函数来实现预测的迭代改进,屏蔽 MSA 损失与结构联合训练,使用自蒸馏从未标记的蛋白质序列中学习,以及自我估计准确率。

蒸馏,就是知识蒸馏,将教师网络(teacher network)的知识迁移到学生网络(student network)上,使得学生网络的性能表现如教师网络一般;或者大型模型迁移到小型模型中,小型模型的参数规模小,运行速度快,但性能与大型模型参不多。

AlphaFold 网络使用一级氨基酸序列和同源物的比对序列作为输入,直接预测给定蛋白质的所有重原子的 3-D 坐标(图 1e,请参阅方法了解输入的详细信息,包括数据库、MSA 构建和使用的模板)。 最重要的方法和组件的描述如下在附件中提供了完整的网络架构和训练过程在附件的方法章节中。

该网络包括两个主要阶段。 首先,网络的主干通过我们称为 Evoformer 的新型神经网络块的重复层处理输入,以生成 Nseq × Nres 数组(Nseq:序列数,Nres:残基数),表示已处理的 MSA 和 Nres × Nres 数组,表示残基对。 MSA 表示是用原始 MSA 初始化的,但请参阅 附件-方法 1.2.7 了解处理非常深的 MSA 的详细信息。 Evoformer 块包含许多新颖的基于注意力和非基于注意力的组件。 我们在“可解释性”部分展示了证据,表明在 Evoformer 块中早期出现了具体的结构假设并不断完善。 Evoformer 模块的关键创新是在 MSA 内交换信息的新机制和允许直接推理空间和进化关系的配对表示。

网络的主干之后是结构模块,该模块以蛋白质的每个残基(全局刚体框架)的旋转和平移的形式引入了明确的 3-D 结构。这些表示在简单的状态下初始化,所有旋转设置为一致,所有位置设置为原点,但快速发展和完善,具有精确原子细节的高度准确的蛋白质结构。网络这一部分的关键创新包括打破链原子结构以允许同时对结构的所有部分进行局部细化,一种新颖的等变变换器允许网络隐式推理未表示的侧链原子,以及一个损失项代替残基的方向正确性的重要权重。在结构模块和整个网络中,我们通过反复将最终损失函数应用于输出,然后将输出递归地提供给相同的模块来强化迭代细化的概念。使用整个网络的迭代细化(我们称之为“循环”) 对准确性有显着贡献,而额外的训练时间很少(有关详细信息,请参阅附件-方法-1.8)。

Evoformer模块

名为 Evoformer(图 1e 和 3a)的网络构建块的关键原理是将蛋白质结构预测视为 3-D 空间中的图推理问题,其中图的边缘由邻近的残基定义。 配对表示的元素编码有关残基之间关系的信息(图 3b)。 MSA 表示的列编码输入序列的各个残基,而行表示这些残基出现的序列。 在这个框架内,我们定义了许多更新操作,这些更新操作应用于每个块中,其中不同的更新操作被串联应用。

Fig. 3 | Architectural details.

MSA 表示通过在 MSA 序列维度上求和的逐元素外积更新配对表示。 与之前的工作不同,此操作应用于每个块中,而不是在网络中应用一次,这使得从不断发展的 MSA 表示到配对表示的连续通信成为可能。

在配对表示中,有两种不同的更新模式。两者都受到配对表示一致性必要性的启发——为了将氨基酸的配对描述表示为单个 3-D 结构,必须满足许多约束,包括距离上的三角不等式。基于这种直觉,我们根据涉及三个不同节点的边三角形来安排对表示的更新操作(图 3c)。特别是,我们向轴向注意力添加了一个额外的 logit 偏差,以包括三角形的“缺失边”,并且我们定义了一个非注意力更新操作“三角形乘法更新”,它使用两条边来更新缺失的第三条边(参见 附件-方法-1.6.5 了解详情)。三角形乘法更新最初是作为注意力更对称且更便宜的替代品而开发的,仅使用注意力或乘法更新的网络都能够产生高精度结构。然而,两个更新的组合更准确。

我们还在 MSA 表示中使用了一种轴向注意力的变体。 在 MSA 中的 per-sequence attention 期间,我们从 pair stack 中投射额外的 logits 以偏置 MSA attention。 这通过提供从配对表示返回到 MSA 表示的信息流来关闭循环,确保整个 Evoformer 模块能够完全混合对和 MSA 表示之间的信息,并为结构模块中的结构生成做好准备。

端到端结构预测

结构模块(图 3d)使用对表示和来自主干的 MSA 表示的原始序列行(“single representation”,“单一表示”)在具体的 3-D 主干结构上运行。 3-D 主干结构表示为 Nres 独立的旋转和平移,每个旋转和平移相对于全局框架(residue gas,“残余气”?,图 3e)。这些旋转和平移,代表 N-Cα-C 原子的几何形状,优先考虑蛋白质骨架的方向,以便每个残基的侧链位置在该框架内受到高度限制。相反,肽键几何形状完全不受约束,并且在应用结构模块期间观察到网络经常违反链约束,因为打破此约束允许对链的所有部分进行局部细化,而无需解决复杂的闭环问题。在微调期间通过违规损失项鼓励满足肽键几何形状。只有在 Amber力场中的梯度下降结构的预测后松弛中才能实现肽键几何形状的精确执行。根据经验,这种最终松弛不会提高模型的准确性,如通过全局距离测试 (GDT) 或 IDDT-Cα34 测量的,但确实消除了分散注意力的立体化学违规而不会损失准确性。

AMBER力场是在生物大分子的模拟计算领域有着广泛应用的一个分子力场。AMBER力场的优势在于对生物大分子的计算,其对小分子体系的计算结果常常不能令人满意。

residue gas表示分两个阶段迭代更新(图 3d)。首先,我们称为不变点注意力 ( Point Attention) 的新型几何感知注意操作用于更新 Nres 神经激活集(single representation,“单一表示”)而不改变 3-D 位置,然后对residue gas使用更新的激活。不变点注意力通过在每个残基的局部框架中产生的 3-D 点来增强每个通常的注意力查询、键和值,这样最终值对全局旋转和平移是不变的(参见方法“不变点注意力(IPA)”了解详情)。 3-D 查询和键也对注意力施加了强烈的空间/局部性偏差,这非常适合蛋白质结构的迭代细化。在每个注意力操作和逐元素转换块之后,该模块计算每个主干帧的旋转和平移的更新。这些更新在每个残差的局部框架内的应用使得整体注意力和更新块成为对residue gas的等变操作。

侧链 chi 角的预测以及结构的最终每个残基精度 (pLDDT) 是在网络末端的最终激活上使用小的每个残基网络计算的。 TM 分数 (pTM) 的估计值是从成对错误预测中获得的,该预测被计算为最终对表示的线性投影。最后的损失(我们称之为帧对齐点误差(FAPE)(图 3f))将预测的原子位置与许多不同对齐下的真实位置进行比较。对于每个对齐,通过将预测帧 (Rk,tk) 对齐到相应的真实帧来定义,我们计算所有预测原子位置 xi 与真实原子位置的距离。由此产生的 Nframes × Natoms 距离受到限制的 L1 损失的惩罚。这对原子相对于每个残基的局部框架是正确的产生了强烈的偏见,因此在其侧链相互作用方面是正确的,并为 AlphaFold 提供了手性的主要来源(增刊-方法 1.9.3 和增刊-图 9)

使用标记和未标记数据进行训练

AlphaFold 架构能够仅使用对 PDB 数据的监督学习来训练到高精度,但我们能够使用类似于noisy-student自我蒸馏的方法来提高准确性(见图 4a)。 在这个过程中,我们使用一个训练有素的网络来预测来自 Uniclust30的约 350,000 个不同序列的结构,并将预测结构的新数据集过滤为高置信度子集。 然后,我们使用 PDB 和这个新的预测结构数据集的混合作为训练数据从头开始训练相同的架构,其中各种训练数据增强(例如裁剪和 MSA 子采样)使网络难以重现先前预测的结构。 这种自蒸馏过程有效地利用了未标记的序列数据,并显着提高了所得网络的准确性。

Self-training是最简单的半监督方法之一,其主要思想是找到一种方法,用未标记的数据集来扩充已标记的数据集。算法流程如下:
(1)首先,利用已标记的数据来训练一个好的模型,然后使用这个模型对未标记的数据进行标记。
(2)然后,进行伪标签的生成,因为我们知道,已训练好的模型对未标记数据的所有预测都不可能都是好的,因此对于经典的Self-training,通常是使用分数阈值过滤部分预测,以选择出未标记数据的预测标签的一个子集。
(3)其次,将生成的伪标签与原始的标记数据相结合,并在合并后数据上进行联合训练。
(4)整个过程可以重复n次,直到达到收敛。

此外,我们随机屏蔽或突变 MSA 中的单个残基,并具有来自 Transformers (BERT) 式目标的双向编码器表示来预测 MSA 序列的屏蔽元素。 这个目标鼓励网络学习解释系统发育和协变关系,而无需将特定的相关统计量硬编码到特征中。 与最近的独立工作相比,BERT 目标是在相同的训练示例上与正常的 PDB 结构损失联合训练的,并且没有进行预训练。

解释神经网络

为了了解 AlphaFold 如何预测蛋白质结构,我们为网络中的 48 个 Evoformer 块中的每一个训练了一个单独的结构模块,同时保持主网络的所有参数保持不变(补充方法 1.14)。包括我们的回收阶段,这提供了 192 个中间结构的轨迹,每个完整的 Evoformer 块一个,其中每个中间体代表网络对该块最可能结构的信念。在前几个块之后产生的轨迹出奇地平滑,表明 AlphaFold 对结构进行了不断的增量改进,直到它不能再改进为止(见图 4b 的准确度轨迹)。这些轨迹也说明了网络深度的作用。对于非常具有挑战性的蛋白质,如 SARS-CoV-2 Orf8 (T1064),网络搜索并重新排列多层的二级结构元素,然后再确定一个好的结构。对于 LmrP (T1024) 等其他蛋白质,网络会在前几层内找到最终结构。请参阅增刊-视频 1-4 的 CASP14 目标 T1024、T1044、T1064 和 T1091 的结构轨迹显示了一系列蛋白质大小和难度的清晰迭代构建过程。在补充-方法 1.16 和增刊-图12-13,我们解释了 AlphaFold 层产生的注意力图。

Fig. 4 | Interpreting the neural network

图 4a 包含 AlphaFold 组件的详细消融,表明各种不同的机制有助于 AlphaFold 的准确性。 请参阅增刊-方法 1.13 详细描述了每个消融模型、它们的训练细节、消融结果的扩展讨论以及 MSA 深度对每次消融的影响(补充-图 10)。

MSA 深度和跨链接触

虽然 AlphaFold 在绝大多数沉积的 PDB 结构中具有很高的准确性,但我们注意到仍然存在影响准确性或限制模型适用性的因素。当平均比对深度小于约 30 个序列时,该模型使用多个序列比对,准确度大幅下降(详见图 5a)。我们观察到阈值效应,其中 MSA 深度超过约 100 个序列的改进导致小增益。我们假设需要 MSA 信息在网络的早期阶段粗略地找到正确的结构,但是将该预测细化为高精度模型并不关键取决于 MSA 信息。我们观察到的另一个实质性限制是,与异型接触的数量相比,AlphaFold 对于链内或同型接触很少的蛋白质要弱得多。这通常发生在较大复合物中的桥接结构域,其中蛋白质的形状几乎完全由与复合物中其他链的相互作用产生。相反,AlphaFold 通常能够为同聚体提供高精度预测,即使链基本上交织在一起(例如图 5b)。我们希望 AlphaFold 的想法很容易适用于预测未来系统中的完整异质复合物,并且这将消除具有大量异质接触的蛋白质链的困难。

Fig. 5 | Effect of MSA depth and cross-chain contacts.

相关工作

蛋白质结构预测有一个漫长而多样的发展,在许多优秀的评论中得到了广泛的介绍。尽管将神经网络应用于结构预测的历史悠久,但它们最近才开始改进结构预测。这些方法通过将蛋白质结构预测问题处理为将进化耦合的“图像” 转换为蛋白质距离矩阵的“图像”,然后将距离预测集成到启发式系统中,从而有效地利用了计算机视觉系统的快速改进。产生最终的 3-D 坐标预测。最近开发了一些工作来直接预测 3-D 坐标 ,但这些方法的准确性与传统的手工结构预测管道不匹配 。同时,基于注意力的语言处理网络的成功 和最近的计算机视觉激发了对解释蛋白质序列的基于注意力的方法的探索。

讨论

我们在设计 AlphaFold 时采用的方法是生物信息学和物理方法的结合:我们使用物理和几何归纳偏差来构建组件,这些组件可以从 PDB 数据中学习,并最大限度地减少手工制作的特征(例如,AlphaFold 在没有氢的情况下有效地构建氢键债券评分函数)。这使得网络从 PDB 中的有限数据中更有效地学习,但能够应对结构数据的复杂性和多样性。特别是,AlphaFold 能够处理缺失的物理环境,并在具有挑战性的情况下生成准确的模型,例如交织的同源异构体或仅在未知血红素组存在时才会折叠的蛋白质。处理未指定结构条件的能力对于从 PDB 结构中学习至关重要,因为 PDB 代表了结构已被求解的所有条件。一般来说,AlphaFold 被训练产生最有可能作为 PDB 结构的一部分出现的蛋白质结构。在特定化学计量或配体/离子可单独从序列预测的情况下,AlphaFold 可能会产生一种隐式遵守这些约束的结构。

AlphaFold 已经向实验界展示了它的实用性,包括分子置换和解释低温电子显微镜 (cryo-EM) 图 。 此外,由于 AlphaFold 直接输出蛋白质坐标,因此 AlphaFold 会根据蛋白质序列的长度以图形处理单元 (GPU) 分钟到 GPU 小时生成预测(例如,对于 384 个残基,每个模型大约 1 个 GPU 分钟,请参阅方法了解详细信息 )。 这开辟了在蛋白质组规模及其他范围内预测结构的令人兴奋的可能性。

可用基因组测序技术和数据的爆炸式增长彻底改变了生物信息学,但实验结构测定的内在挑战阻止了我们结构知识的类似扩展。 通过开发准确的蛋白质结构预测算法,再加上由实验社区组装的现有大型且精心准备的结构和序列数据库,我们希望加速结构生物信息学的进步,以跟上基因组学革命的步伐。 我们希望 AlphaFold 以及将其技术应用于其他生物物理问题的计算方法将成为现代生物学的重要工具。

在线内容

任何方法、附加参考资料、自然研究报告摘要、源数据、扩展数据、补充信息、致谢、同行评审信息; 作者贡献和竞争利益的详细信息; 数据和代码可用性声明可在 Highly accurate protein structure prediction with AlphaFold – Nature 上获得。

 Highly accurate protein structure prediction with AlphaFold – Nature

AI制药 : 分子对接

任务:

1、安装学习yutobe上面的软件

https://www.youtube.com/watch?v=Sux91FJ3Xe8&t=629s

2、跑通论文代码

https://www.youtube.com/watch?v=Sux91FJ3Xe8&t=629s

Pymol简介

Pymol是一款操作简单,功能强大的分子以及蛋白的可视化软件,由薛定谔公司研发,科研人员可以从官网申请最新教育版本,同时pymo的开源版(https://github.com/schrodinger/pymol-open-source),可以直接从网站上下载,但是版本较老。所以,根据需求选择版本进行下载。 说明: https://cloud.tencent.com/developer/article/1785088

Pymol入门教程:

http://pymol.chenzhaoqiang.com/intro/startManual.html

分子对接教程

1、

https://cloud.tencent.com/developer/inventory/15332

2、https://www.bilibili.com/video/av466685164?from=search&seid=9870338638011316620&spm_id_from=333.337.0.0

vina只负责对接,mgltool负责提供蛋白质分子和配体分子。

先用MGL 生成vina需要的pdbqt文件

MGL tools的作用就是生成pdbqt文件

1、:打开MGL tools,打开受体蛋白的pdb :file-》read molecule

2、蛋白质的pdb(数据库):

关于蛋白质结构的PDB文件,做分子对接,估计大家都知道PDB这个蛋白质数据库啦。这里简单的介绍一下。

蛋白质的三级结构是指整条多肽链的三维空间结构,也就是包括碳骨架和侧链在内的所有原子的空间排列。第一个蛋白质的三维空间结构于 1958 年用 X-射线衍射法(X-ray Crystallography)测定。这种方法目前仍然是获取蛋白质三级结构的主要方法。PDB 数据库中绝大多数蛋白质结构都是用这种方法测定的。另一个测定蛋白质三维空间结构的方法是核磁共振法(Nuclear Magnetic Resonance, NMR)。无法结晶的蛋白质,可以利用核磁共振法在液体环境中进行结构测定。但是核磁共振法只能用于质量小于 70 千道尔顿的分子,大约对应 200 个氨基酸的长度。除此之外,还有一些不太常用的方法也可以测定分子的三维空间结构,比如冷冻电子显微镜技术(Cyro-Electron Microscopy)。无论用什么方法测定的空间结构,都要提交到 PDB 数据库。所以我们获取蛋白质三级结构最直接的办法就是去PDB 搜索(http://www.rcsb.org/)。 从PDB首页的搜索条里,可以通过搜索PDB ID、分子名称、作者姓名等关键词来查找蛋白质三级结构。此外,利用高级搜索工具,可以通过序列相似性搜索获得与输入序列在序列水平上相似的蛋白质的三级结构。搜索方法选 BLAST,输入序列,点击“Result Count”。这里不详细介绍,因为我们做分子对接,通常蛋白名称是已知的。我们重点介绍怎么选择合适的蛋白结构文件。 比如我们搜索PI3K这个蛋白,直接在搜索栏搜索,结果是有很多的。可以看到有393个结构信息。首先我们可以通过左边的栏进行筛选,比如物种信息,我们选择人。当然,结果的显示排序可通过结果上面的选项卡进行选择不同的排序方式。我们筛选合适的蛋白结构,常用Score这个选项.我们选择分辨率较好的在前。这里的0.9Å,Å是光波长度和分子直径的常用计量单位,值越小,分辨率越高,结构越准确。页面往下拉,可以看见这个值越来越大,我们优先选择值小的。我们可以从页面里面看见一下基本信息,比如方法,物种以及被解析的时间等。这里5GJI这个结构获取的方法就是X-RAY。我们点击这个蛋白,进入后可以看见详细的信息。然后我们还要看这个蛋白的描述是不是我们想要的蛋白,从这里面感觉看起来比较费劲。这里我们借助uniprot这个数据库来选择是比较方便的。这里简单介绍一下这个数据库,可能有的同学是第一次知道。翻了多年前的笔记,粘贴在下面。 UniProt 数据库有三个层次。

第一层叫 UniParc,收录了所有 UniProt 数据库子库中的蛋白质序列,量大,粗糙。

第二层是 UniRef,他归纳了 UniProt 几个主要数据库并且是将重复序列去除后的数据库。

第三层是 UniProtKB,他有详细注释并与其他数据库有链接,分为 UniProtKB 下的 Swiss-Prot和 UniProtKB 下的 TrEMBL 数据库。

关系稍有点复杂,但实际上我们最常用的就是 UniProtKB下的 Swiss-Prot 数据库。

从 UniProt 数据库查看一条蛋白质序列(http://www.uniprot.org/)。在UniProt数据库的首页上有一个关于 UniProtKB 数据库的统计表。可以看到,TrEMBL 数据库里存储的序列数量远远大于 Swiss-Prot 中的。统计表里清楚的写着:TrEMBL 是自动注释的,没有经过检查,而 Swiss-Prot 是人工注释的,并且经过检查。

然后点击下载文件就可以直接下载PDB格式的蛋白结构文件。下载的PDB文件可以用pymol或者VMD观察结构。能够实现蛋白质三维结构可视化的软件非常多。比专业级的PyMOL(https://pymol.org/2/)。这个软件已经被世界上著名的生物医药软件公司“薛定谔公司(Schrödinger)”收购。这种专业级的可视化软件不仅能够做出非常漂亮的图片,它还有强大的插件支持各种各样的蛋白质结构分析,这款软件需要购买,如果你发表的文章里提到某些内容是使用PyMOL制作的,而文章中所有作者和作者单位都没有PyMOL的购买记录的话,你可能会面临薛定谔公司的追责。 如果要对接的蛋白没有结构,我们又要对接,那就只能是自己通过软件预测了。蛋白质结构预测的方法有从头计算法,同源建模法,穿线法和综合法。常用的是同源建模法,SWISS-MODEL(www.swissmodel.expasy.org)就是一款用同源建模法预测蛋白质三级结构的全自动软件,这里不详细介绍了,预测的模型还要涉及模型好坏的评价,后续有时间,再介绍蛋白质三级结构的预测。

接下来我们打开AutoDockTools(ADT),打开我们前面保存的文件1E8Y_PYMOL.pdb

删除水分子和其他配体,常规操作不用解释

然后计算电荷和添加原子类型

Edit–Charges–Compute Gasteiger

Edit–Atoms–Assign AD4 type

就可以导出成pbdqt格式的文件了

然后右键吧蛋白删除掉,导入配体小分子,随便从ZINC下了一个

ZINC(http://zinc.docking.org/) 还有一个数据库能下载mol2格式的文件。ZINC),这里就不介绍了,你要是能从上面的数据库下载到你配体小分子的mol2格式文件,就直接用,如果不能,那就是去PubChem数据库(https://pubchem.ncbi.nlm.nih.gov/)下载sdf文件,然后进行转换,这也是我这里要介绍的。

Ligand-input-open

子对接教程

1 分子对接的工作环境
1.1 对接基本需求
受体的 pdb 文件、知道配体的结构
1.2 对接软件
使用 AutoDock 进行对接,该软件由 AutoDock 和 MGLTools 两
部分组成,AutoDock 为主程序,仅提供了命令行接口,而 MGLTools
中的 AutoDockTools(ADT)可以看作是 AutoDock 的图形用户界面。
配体使用 ChemDraw 和 Chem3D 绘制,该软件为收费软件,可
使用其它免费的结构式绘制工具进行结构绘制,使用 Open Babel 转
3D 结构和文件格式的转换。
使用 PyMol 开源版本对受体蛋白的 pdb 文件进行处理。

2 准备受体、配体的 pdbqt 文件


AutoDock 只接受 pdbqt 格 式 的 文 件 , 所 以 需 要 通 过
AutoDockTools 将 pdb 或者其他格式的文件转化为 pdbqt 文件。


2.1 准备受体的 pdbqt 文件
2.1.1 使用 PyMol 软件进行处理


pdb 下载的蛋白需要先使用 PyMol 软件删除多余的离子、水分
子,同源多聚体蛋白还可以只保留一条链。直接从 pdb 下载蛋白可以
使用 fetch [protein name]命令。导入后 pdb 文件后,点击右下角的 S
以显示结构信息、

2.1.2 关于 pdb 文件的格式

该格式省略了一切氢原子,包括游离的水分子都只保留了一个 O,所以后续需要加氢操作。该文件的格式较复杂,最好使用成熟的软件进行编辑,而不上自行编辑。该文件中的氨基酸残基的原子全部记录在 ATOM 行中,后面的每一项分别为原子序号、原子名称(第一个字母为原子的元素符号,第二个字母为远近标识符 A、B、G、D、E、Z、H 分别对应有机化合物命名系统中的 α、β、γ、δ、ε、ζ、η)、残基名称、链编号、残基序号、原子坐标等

而离子、水分子以及结合在蛋白中的配体(如抑制剂等)等非蛋白质的部分记录在 HETNAM(非标准残基的名称)中:

同样的依次是原子编号、原子名称、基团名称(如水这里起名为HOH)、链编号(但是它本身不依附于哪条链)、原子编号、坐标等。

2.1.3 使用 PyMol 命令进行选择和删除
选择也可以使用命令选择,select 命令可以用于选择链如:
select chain A
indicate 命令的格式则为 indicate [element_type] [name],如根据残基
名称 HOH 选择所有的水分子:
indicate resname HOH
需要注意的是,选择水分子不要使用:
indicate name O
否则其会选中所有氨基酸残基中的氧原子。根据残基名称选择配体
Mr-Greyfun
(示例中配体名称为 STI):
indicate resname STI
根据原子名称选择氯原子:
indicate name CL
可以使用 remove 命令进行删除操作,命令格式与之类似:
remove resname HOH
remove name CL
remove chain B

2.1.1 保存
在 PyMol 中完成上述处理后使用 Export Molecule-save 保存成pdb 格式的文件。
2.1.2 加氢操作
pdb 文件会省略氢原子,所以需要进行加氢,可以在 PyMol 中使用 h_add 命 令 加氢 (删除为 remove hydrogen ), 但最 好 使 用AutoDockTools(ADT)进行加氢。处理好后的文件用 ADT 打开后,点击 Edit-Hydrogens-Add-OK。加氢。
2.1.3 转化为 pdbqt 格式的文件
点击 Grid-Macromolecule-Choose,选择好后点 select molecule,
它会自动加电荷等,点 OK 即可,完成后,会弹出保存窗口,直接保
存 pdbqt 格式即可

2.1.1 保存
在 PyMol 中完成上述处理后使用 Export Molecule-save 保存成
pdb 格式的文件。
2.1.2 加氢操作
pdb 文件会省略氢原子,所以需要进行加氢,可以在 PyMol 中使
用 h_add 命 令 加氢 (删除为 remove hydrogen ), 但最 好 使 用
AutoDockTools(ADT)进行加氢。
处理好后的文件用 ADT 打开后,点击 Edit-Hydrogens-Add-OK。
加氢。
2.1.3 转化为 pdbqt 格式的文件
点击 Grid-Macromolecule-Choose,选择好后点 select molecule,
它会自动加电荷等,点 OK 即可,完成后,会弹出保存窗口,直接保
存 pdbqt 格式即可

设置好后点 Done,然后点击 Ligand-Output-Save as PDBQT 即可
保存。

2.2.1 附:用 PyMol 导出 pdb 文件中的配体的方法
在界面上选中小分子,点击(sele)的 A-copy to object-new。红框内
的区域可以点击后将其对应的对象隐藏起来:
然后点击 Export Molecule-save 保存成 pdb 格式的文件。注意,这个
内置的配体也是没有加氢的,需要用 ADT 加氢。

3 对接
3.1 对接操作步骤
3.1.1 导入受体和配体
在 ADT 里面,点击 Grid-Macromolecule-Open 打开受体蛋白
点击 Grid-Set-Map-Types-Open Ligand 打开受体的 pdbqt 文件
3.1.2 定义对接盒子
点击 Grid-Grid Box 定义对接盒子
3.1.3 生成 config.txt 文件
点击 Docking-Output-Vina config 生成 config.txt 文件
3.1.4 启动对接

视频中的步骤:

1、导入protein蛋白质

2、删除水分子 如果你的对接区域有水分子,会影响对接结果

pdq该格式省略了一切氢原子,包括游离的水分子都只保留了一个 O,所以后续需要加氢操作。

3、edit ->hydrogens->add->polar only 此时结构中发亮的就是氢键(加氢)

4、加电荷 edit -> charges->add kollman charges

5 报存 grid -> macromolecule ->choose->select

准备配体文件:

以sdf结尾的文件直接拖进 AUTO软件中会报错,需要转换成pdb文件

可以使用pymol可视化工具转换(注意:配体 英文 ligend)

1、将该文件拖动到pymol中打开,file ->molecule

配体文件:

1、打开auto dock,将配体文件导入:

2、 点击 Ligand(配体)->input->choose (这一步就是生成了配体文件)

3、点击 Ligand(配体)->output->save as pdbqt

接下来就可以进行分子对接:

1、将两个(受体和配体)pdpqt导入

2、重新选择蛋白质分子作为受体

点击NO

接下来设置对接盒子:

grid -> grid box

设置盒子位置(spacing设置为1)

然后 grid box 弹出设置中选择 file->output grid dimensions file(保存盒子设置)

保存:

新建config.txt:用于启动vina

receptor 蛋白质名(生成的蛋白质文件名)

ligand 配体名

center 和size在上一步的grid中有

receptor:指定受体分子的路径

ligand:配体分子的路径

center_x,center_y,center_z:搜索空间中心的坐标

size_x,size_y,size_z:指定搜索空间的大小。这里设置的大小基本就是把整个受体分子都包含了,属于blind docking。如何更准确确定结合口袋的位置,我们稍后再说。

energy_range:默认4,与最优结合模型相差的最大能量值,单位是kcal/mol。比方说,最优模型的能量计算出来是-8.5kcal/mol,那么vina也就最多计算到-4.5kcal/mol的模型就终止了,也就意味着这个值决定了生成模型的最大个数。

exhaustiveness:用来控制对接的细致程度,默认值是8. 大致与时间成正比。

num_modes:最多生成多少个模型。实际生成的模型数由num_modes和energy_range共同决定。

energy_range
 maximum energy difference between the best binding 
 mode and the worst one displayed (kcal/mol)

最后启动vina 进行分子对接:

在config目录中打开cmd->输入vina

其中:这条命令就是利用config.txt文件进行分子对接(cmd 必须在config文件目录下打开)

执行 命令:

D:\Autodock\pdbqt>”D:\Vina\vina.exe” –receptor protein.pdbqt –ligand ligend.pdbqt –config config.txt –log log.txt –out output.pdbqt

“D:\Vina\vina.exe” –receptor selected_prediction_ready.pdbqt –ligand Conformer3D_CID_65536.pdbqt –config config.txt –log log.txt –out output.pdbqt

如果上述命令报错,一般是生成的config文件有问题:

正确的config内容如下:

执行完毕可以看到生成一个log文件

vina 中的affinity是亲合力结果排名

最后:

查看生成的模型:

将protein和output输出导入pymol

点击 all -> s查看表面 结构

点击左右箭头查看不同的结构:

5.我们挑选第一个模型,看看结构方式是怎样的,见下图。很显然与真实的结合方式相差甚远,可以说是完全错误。

-1.jpg

为什么会出现这种情况,很大程度上是因为search space太大,可能需要设置更大的exhaustiveness。

如果我们大致知道binding pocket在什么位置,那准确性应该会高不少,如何大致确定binding pocket的位置呢?我们接着试验。

可以通过实验的方式,比如某个点突变对结合或者活性影响非常大,那么大概率这个残基是结合口袋的一部分。

可以通过软件预测,比如蛋白与配体(底物)结合位点预测:https://zhanglab.ccmb.med.umich.edu/COACH/

再比如Discovery studio软件(专业版的),可以很方便的根据受体分子的表面形状来预测结合口袋位置。

20190507175152.png

口袋的坐标为:

34.3356,14.9412,26.9615

我们修改下对接参数,新的参数如下:

receptor = r.pdbqt

ligand = nap.pdbqt

center_x = 34.3356

center_y = 14.9412

center_z = 26.9615

size_x = 30.0

size_y = 30.0

size_z = 30.0

energy_range = 4

exhaustiveness = 10

num_modes = 10

最优结果与真实模型的RMSD为1.795埃,可以说非常精准了

比对结果如下:

Souce: 纽普生物    2019-05-07

PyMOL 相关操作:

1、导入蛋白质

先在NCBI子数据库structure检索所需要的蛋白结构,https://www.ncbi.nlm.nih.gov/structure/?term=
记录下该蛋白的PDB ID,然后打开PyMOL,命令行输入:

fetch 5ocn#foxn1
fetch 3uf0#
fetch 1si4#血红蛋白

去除水分子

remove solvent

分离得到蛋白

remove organic

分离配体:

../_images/splitlig2020-03-12_202822.573956.png

pymol教程:

http://pymol.chenzhaoqiang.com/intro/startManual.html

分子对接进阶教程:选定对接位点区域:

如果我们想要将配体和蛋白质受体的某几个位点部位对接:

1、标记这些点 select->select from string ,选择对应的molecule和链,以及residue(寻找的位点),点击add

2、此时,会出现currenr selection ,选择该列中 c,点击绿点,会将对应的分子三D化。

3、接下来设置盒子,spacing 设置为1(相当于比例尺),xyz一般设置为20-22

这样就可以了。

一文归纳 AI 数据增强之法

作者 | 算法进阶
摘自: 算法进阶微信公众号

数据、算法、算力是人工智能发展的三要素。数据决定了Ai模型学习的上限,数据规模越大、质量越高,模型就能够拥有更好的泛化能力。然而在实际工程中,经常有数据量太少(相对模型而言)、样本不均衡、很难覆盖全部的场景等问题,解决这类问题的一个有效途径是通过数据增强(Data Augmentation),使模型学习获得较好的泛化性能。

1 数据增强介绍

数据增强(Data Augmentation)是在不实质性的增加数据的情况下,从原始数据加工出更多的表示,提高原数据的数量及质量,以接近于更多数据量产生的价值。其原理是,通过对原始数据融入先验知识,加工出更多数据的表示,有助于模型判别数据中统计噪声,加强本体特征的学习,减少模型过拟合,提升泛化能力。

如经典的机器学习例子–哈士奇误分类为狼:通过可解释性方法,可发现错误分类是由于图像上的雪造成的。通常狗对比狼的图像里面雪地背景比较少,分类器学会使用雪作为一个特征来将图像分类为狼还是狗,而忽略了动物本体的特征。此时,可以通过数据增强的方法,增加变换后的数据(如背景换色、加入噪声等方式)来训练模型,帮助模型学习到本体的特征,提高泛化能力。

需要关注的是,数据增强样本也有可能是引入片面噪声,导致过拟合。此时需要考虑的是调整数据增强方法,或者通过算法(可借鉴Pu-Learning思路)选择增强数据的最佳子集,以提高模型的泛化能力。

常用数据增强方法可分为:基于样本变换的数据增强及基于深度学习的数据增强。

2 基于样本变换的数据增强

样本变换数据增强即采用预设的数据变换规则进行已有数据的扩增,包含单样本数据增强和多样本数据增强。

2.1 单样本增强

单(图像)样本增强主要有几何操作、颜色变换、随机擦除、添加噪声等方法,可参见imgaug开源库。

2.2 多样本数据增强方法

多样本增强是通过先验知识组合及转换多个样本,主要有Smote、SamplePairing、Mixup等方法在特征空间内构造已知样本的邻域值。

  • Smote

Smote(Synthetic Minority Over-sampling Technique)方法较常用于样本均衡学习,核心思想是从训练集随机同类的两近邻样本合成一个新的样本,其方法可以分为三步:

1、 对于各样本X_i,计算与同类样本的欧式距离,确定其同类的K个(如图3个)近邻样本;

2、从该样本k近邻中随机选择一个样本如近邻X_ik,生成新的样本:

Xsmote_ik =  Xi  +  rand(0,1) ∗ ∣X_i − X_ik∣  

3、重复2步骤迭代N次,可以合成N个新的样本。

# SMOTE
from imblearn.over_sampling import SMOTE

print("Before OverSampling, counts of label\n{}".format(y_train.value_counts()))
smote = SMOTE()
x_train_res, y_train_res = smote.fit_resample(x_train, y_train)
print("After OverSampling, counts of label\n{}".format(y_train_res.value_counts())) 
  • SamplePairing

SamplePairing算法的核心思想是从训练集随机抽取的两幅图像叠加合成一个新的样本(像素取平均值),使用第一幅图像的label作为合成图像的正确label。

  • Mixup

Mixup算法的核心思想是按一定的比例随机混合两个训练样本及其标签,这种混合方式不仅能够增加样本的多样性,且能够使决策边界更加平滑,增强了难例样本的识别,模型的鲁棒性得到提升。其方法可以分为两步:

1、从原始训练数据中随机选取的两个样本(xi, yi) and (xj, yj)。其中y(原始label)用one-hot 编码。

2、对两个样本按比例组合,形成新的样本和带权重的标签

x˜ = λxi + (1 − λ)xj  
y˜ = λyi + (1 − λ)yj  

最终的loss为各标签上分别计算cross-entropy loss,加权求和。其中 λ ∈ [0, 1], λ是mixup的超参数,控制两个样本插值的强度。

# Mixup
def mixup_batch(x, y, step, batch_size, alpha=0.2):
    """
    get batch data
    :param x: training data
    :param y: one-hot label
    :param step: step
    :param batch_size: batch size
    :param alpha: hyper-parameter α, default as 0.2
    :return:  x y 
    """
    candidates_data, candidates_label = x, y
    offset = (step * batch_size) % (candidates_data.shape[0] - batch_size)

    # get batch data
    train_features_batch = candidates_data[offset:(offset + batch_size)]
    train_labels_batch = candidates_label[offset:(offset + batch_size)]

    if alpha == 0:
        return train_features_batch, train_labels_batch

    if alpha > 0:
        weight = np.random.beta(alpha, alpha, batch_size)
        x_weight = weight.reshape(batch_size, 1)
        y_weight = weight.reshape(batch_size, 1)
        index = np.random.permutation(batch_size)
        x1, x2 = train_features_batch, train_features_batch[index]
        x = x1 * x_weight + x2 * (1 - x_weight)
        y1, y2 = train_labels_batch, train_labels_batch[index]
        y = y1 * y_weight + y2 * (1 - y_weight)
        return x, y 

3 基于深度学习的数据增强

3.1 特征空间的数据增强

不同于传统在输入空间变换的数据增强方法,神经网络可将输入样本映射为网络层的低维向量(表征学习),从而直接在学习的特征空间进行组合变换等进行数据增强,如MoEx方法等。

3.2 基于生成模型的数据增强

生成模型如变分自编码网络(Variational Auto-Encoding network, VAE)和生成对抗网络(Generative Adversarial Network, GAN),其生成样本的方法也可以用于数据增强。这种基于网络合成的方法相比于传统的数据增强技术虽然过程更加复杂, 但是生成的样本更加多样。

  • 变分自编码器VAE变分自编码器(Variational Autoencoder,VAE)其基本思路是:将真实样本通过编码器网络变换成一个理想的数据分布,然后把数据分布再传递给解码器网络,构造出生成样本,模型训练学习的过程是使生成样本与真实样本足够接近。
# VAE模型
class VAE(keras.Model):
    ...
    def train_step(self, data):
        with tf.GradientTape() as tape:
            z_mean, z_log_var, z = self.encoder(data)
            reconstruction = self.decoder(z)
            reconstruction_loss = tf.reduce_mean(
                tf.reduce_sum(
                    keras.losses.binary_crossentropy(data, reconstruction), axis=(1, 2)
                )
            )
            kl_loss = -0.5 * (1 + z_log_var - tf.square(z_mean) - tf.exp(z_log_var))
            kl_loss = tf.reduce_mean(tf.reduce_sum(kl_loss, axis=1))
            total_loss = reconstruction_loss + kl_loss
        grads = tape.gradient(total_loss, self.trainable_weights)
        self.optimizer.apply_gradients(zip(grads, self.trainable_weights))
        self.total_loss_tracker.update_state(total_loss)
        self.reconstruction_loss_tracker.update_state(reconstruction_loss)
        self.kl_loss_tracker.update_state(kl_loss)
        return {
            "loss": self.total_loss_tracker.result(),
            "reconstruction_loss": self.reconstruction_loss_tracker.result(),
            "kl_loss": self.kl_loss_tracker.result(),
        } 
  • 生成对抗网络GAN生成对抗网络-GAN(Generative Adversarial Network) 由生成网络(Generator, G)和判别网络(Discriminator, D)两部分组成, 生成网络构成一个映射函数GZX(输入噪声z, 输出生成的图像数据x), 判别网络判别输入是来自真实数据还是生成网络生成的数据。
# DCGAN模型

class GAN(keras.Model):
    ...
    def train_step(self, real_images):
        batch_size = tf.shape(real_images)[0]
        random_latent_vectors = tf.random.normal(shape=(batch_size, self.latent_dim))
        # G: Z→X(输入噪声z, 输出生成的图像数据x)
        generated_images = self.generator(random_latent_vectors)
        # 合并生成及真实的样本并赋判定的标签
        combined_images = tf.concat([generated_images, real_images], axis=0)
        labels = tf.concat(
            [tf.ones((batch_size, 1)), tf.zeros((batch_size, 1))], axis=0
        )
        # 标签加入随机噪声
        labels += 0.05 * tf.random.uniform(tf.shape(labels))
        # 训练判定网络
        with tf.GradientTape() as tape:
            predictions = self.discriminator(combined_images)
            d_loss = self.loss_fn(labels, predictions)
        grads = tape.gradient(d_loss, self.discriminator.trainable_weights)
        self.d_optimizer.apply_gradients(
            zip(grads, self.discriminator.trainable_weights)
        )

        random_latent_vectors = tf.random.normal(shape=(batch_size, self.latent_dim))
        # 赋生成网络样本的标签(都赋为真实样本)
        misleading_labels = tf.zeros((batch_size, 1))
        # 训练生成网络
        with tf.GradientTape() as tape:
            predictions = self.discriminator(self.generator(random_latent_vectors))
            g_loss = self.loss_fn(misleading_labels, predictions)
        grads = tape.gradient(g_loss, self.generator.trainable_weights)
        self.g_optimizer.apply_gradients(zip(grads, self.generator.trainable_weights))
        # 更新损失
        self.d_loss_metric.update_state(d_loss)
        self.g_loss_metric.update_state(g_loss)
        return {
            "d_loss": self.d_loss_metric.result(),
            "g_loss": self.g_loss_metric.result(),
        }

3.3 基于神经风格迁移的数据增强

神经风格迁移(Neural Style Transfer)可以在保留原始内容的同时,将一个图像的样式转移到另一个图像上。除了实现类似色彩空间照明转换,还可以生成不同的纹理和艺术风格。

神经风格迁移是通过优化三类的损失来实现的:

style_loss:使生成的图像接近样式参考图像的局部纹理;

content_loss:使生成的图像的内容表示接近于基本图像的表示;

total_variation_loss:是一个正则化损失,它使生成的图像保持局部一致。

# 样式损失
def style_loss(style, combination):
    S = gram_matrix(style)
    C = gram_matrix(combination)
    channels = 3
    size = img_nrows * img_ncols
    return tf.reduce_sum(tf.square(S - C)) / (4.0 * (channels ** 2) * (size ** 2))

# 内容损失
def content_loss(base, combination):
    return tf.reduce_sum(tf.square(combination - base))

# 正则损失
def total_variation_loss(x):
    a = tf.square(
        x[:, : img_nrows - 1, : img_ncols - 1, :] - x[:, 1:, : img_ncols - 1, :]
    )
    b = tf.square(
        x[:, : img_nrows - 1, : img_ncols - 1, :] - x[:, : img_nrows - 1, 1:, :]
    )
    return tf.reduce_sum(tf.pow(a + b, 1.25))

3.4 基于元学习的数据增强

深度学习研究中的元学习(Meta learning)通常是指使用神经网络优化神经网络,元学习的数据增强有神经增强(Neural augmentation)等方法。

  • 神经增强

神经增强(Neural augmentation)是通过神经网络组的学习以获得较优的数据增强并改善分类效果的一种方法。其方法步骤如下:

1、获取与target图像同一类别的一对随机图像,前置的增强网络通过CNN将它们映射为合成图像,合成图像与target图像对比计算损失;

2、将合成图像与target图像神经风格转换后输入到分类网络中,并输出该图像分类损失;

3、将增强与分类的loss加权平均后,反向传播以更新分类网络及增强网络权重。使得其输出图像的同类内差距减小且分类准确。

MAE:Masked Autoencoders Are Scalable Vision Learners

摘自 Jack Cui

马赛克,克星,真来了!

何恺明大神的新作 论文:https://arxiv.org/abs/2111.06377

项目地址:https://github.com/facebookresearch/mae

简单讲:将图片随机遮挡然后复原。并且遮挡的比例,非常大超过整张图的 80% ,我们直接看效果:

第一列是遮挡图,第二列是修复结果,第三列是原图。图片太多,可能看不清,我们单看一个:

看这个遮挡的程度,表针、表盘几乎都看不见了。但是 MAE 依然能够修复出来:

这个效果真的很惊艳!甚至对于遮挡 95% 的面积的图片依然 work。

看左图,你能看出来被遮挡的是蘑菇吗??MAE 却能轻松修复出来。接下来,跟大家聊聊 MAE。

Vit

讲解 MAE 之前不得不先说下 Vit。红遍大江南北的 Vision Transformer,ViT。领域内的小伙伴,或多或少都应该听说过。它将 Transformer 应用到了 CV 上面,将整个图分为 16 * 16 的小方块,每个方块做成一个词,然后放进 Transformer 进行训练。视觉transformer 和自然语言处理 中的transformer可以进行类比,可以把一个图像块理解成一个单词。

MAE

MAE 结构设计的非常简单:

将一张图随机打 Mask,未 Mask 部分输入给 Encoder 进行编码学习,这个 Encoder 就是 Vit,然后得到每个块的特征。再将未 Mask 部分以及 Mask 部分全部输入给 Decoder 进行解码学习,最终目标是修复图片。而 Decoder 就是一个轻量化的 Transformer。它的损失函数就是普通的 MSE。所以说, MAE 的 Encoder 和 Decoder 结构不同,是非对称式的。Encoder 将输入编码为 latent representation,而 Decoder 将从 latent representation 重建原始信号。

项目提供了 Colab,如果你能登陆,那么可以直接体验:https://colab.research.google.com/github/facebookresearch/mae/blob/main/demo/mae_visualize.ipynb

如果不能登陆,可以直接本地部署,作者提供了预训练模型。

MAE 可以用来生成不存在的内容,就像 GAN 一样。

首先来看看神魔是 Transformer :

Transformer 最初主要应用于一些自然语言处理场景,比如翻译、文本分类、写小说、写歌等。随着技术的发展,Transformer 开始征战视觉领域,分类、检测等任务均不在话下,逐渐走上了多模态的道路。

Transformer 是 Google 在 2017 年提出的用于机器翻译的模型。

Transformer 的内部,在本质上是一个 Encoder-Decoder 的结构,即 编码器-解码器。

Transformer 中抛弃了传统的 CNN 和 RNN,整个网络结构完全由 Attention 机制组成,并且采用了 6 层 Encoder-Decoder 结构。

显然,Transformer 主要分为两大部分,分别是编码器解码器。整个 Transformer 是由 6 个这样的结构组成,为了方便理解,我们只看其中一个Encoder-Decoder 结构。

以一个简单的例子进行说明:

Why do we work?,我们为什么工作?左侧红框是编码器,右侧红框是解码器编码器负责把自然语言序列映射成为隐藏层(上图第2步),即含有自然语言序列的数学表达。解码器把隐藏层再映射为自然语言序列,从而使我们可以解决各种问题,如情感分析、机器翻译、摘要生成、语义关系抽取等。简单说下,上图每一步都做了什么:

  • 输入自然语言序列到编码器: Why do we work?(为什么要工作);
  • 编码器输出的隐藏层,再输入到解码器;
  • 输入 <𝑠𝑡𝑎𝑟𝑡> (起始)符号到解码器;
  • 解码器得到第一个字”为”;
  • 将得到的第一个字”为”落下来再输入到解码器;
  • 解码器得到第二个字”什”;
  • 将得到的第二字再落下来,直到解码器输出 <𝑒𝑛𝑑> (终止符),即序列生成完成。

解码器和编码器的结构类似,本文以编码器部分进行讲解。即把自然语言序列映射为隐藏层的数学表达的过程,因为理解了编码器中的结构,理解解码器就非常简单了。为了方便学习,我将编码器分为 4 个部分,依次讲解。

1、位置嵌入(𝑝𝑜𝑠𝑖𝑡𝑖𝑜𝑛𝑎𝑙 𝑒𝑛𝑐𝑜𝑑𝑖𝑛𝑔)

我们输入数据 X 维度为[batch size, sequence length]的数据,比如我们为什么工作。batch size 就是 batch 的大小,这里只有一句话,所以 batch size 为 1,sequence length 是句子的长度,一共 7 个字,所以输入的数据维度是 [1, 7]。我们不能直接将这句话输入到编码器中,因为 Tranformer 不认识,我们需要先进行字嵌入,即得到图中的 。简单点说,就是文字->字向量的转换,这种转换是将文字转换为计算机认识的数学表示,用到的方法就是 Word2Vec,Word2Vec 的具体细节,对于初学者暂且不用了解,这个是可以直接使用的。得到维度是 [batch size, sequence length, embedding dimension],embedding dimension 的大小由 Word2Vec 算法决定,Tranformer 采用 512 长度的字向量。所以 的维度是 [1, 7, 512]。至此,输入的我们为什么工作,可以用一个矩阵来简化表示。

我们知道,文字的先后顺序,很重要。比如吃饭没、没吃饭、没饭吃、饭吃没、饭没吃,同样三个字,顺序颠倒,所表达的含义就不同了。文字的位置信息很重要,Tranformer 没有类似 RNN 的循环结构,没有捕捉顺序序列的能力。为了保留这种位置信息交给 Tranformer 学习,我们需要用到位置嵌入。加入位置信息的方式非常多,最简单的可以是直接将绝对坐标 0,1,2 编码。Tranformer 采用的是 sin-cos 规则,使用了 sin 和 cos 函数的线性变换来提供给模型位置信息:

上式中 pos 指的是句中字的位置,取值范围是 [0, 𝑚𝑎𝑥 𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛𝑔𝑡ℎ),i 指的是字嵌入的维度, 取值范围是 [0, 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛)。 就是 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛 的大小。上面有 sin 和 cos 一组公式,也就是对应着 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛 维度的一组奇数和偶数的序号的维度,从而产生不同的周期性变化。

# 导入依赖库
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import math

def get_positional_encoding(max_seq_len, embed_dim):
    # 初始化一个positional encoding
    # embed_dim: 字嵌入的维度
    # max_seq_len: 最大的序列长度
    positional_encoding = np.array([
        [pos / np.power(10000, 2 * i / embed_dim) for i in range(embed_dim)]
        if pos != 0 else np.zeros(embed_dim) for pos in range(max_seq_len)])
    positional_encoding[1:, 0::2] = np.sin(positional_encoding[1:, 0::2])  # dim 2i 偶数
    positional_encoding[1:, 1::2] = np.cos(positional_encoding[1:, 1::2])  # dim 2i+1 奇数
    # 归一化, 用位置嵌入的每一行除以它的模长
    # denominator = np.sqrt(np.sum(position_enc**2, axis=1, keepdims=True))
    # position_enc = position_enc / (denominator + 1e-8)
    return positional_encoding
    
positional_encoding = get_positional_encoding(max_seq_len=100, embed_dim=16)
plt.figure(figsize=(10,10))
sns.heatmap(positional_encoding)
plt.title("Sinusoidal Function")
plt.xlabel("hidden dimension")
plt.ylabel("sequence length")
可以看到,位置嵌入在 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛 (也是hidden dimension )维度上随着维度序号增大,周期变化会越来越慢,而产生一种包含位置信息的纹理。

就这样,产生独一的纹理位置信息,模型从而学到位置之间的依赖关系和自然语言的时序特性。

最后,将Xembedding  和 位置嵌入 相加,送给下一层。

2、自注意力层(𝑠𝑒𝑙𝑓 𝑎𝑡𝑡𝑒𝑛𝑡𝑖𝑜𝑛 𝑚𝑒𝑐ℎ𝑎𝑛𝑖𝑠𝑚)

直接看下图笔记,讲解的非常详细。

多头的意义在于,\(QK^{T}\) 得到的矩阵就叫注意力矩阵,它可以表示每个字与其他字的相似程度。因为,向量的点积值越大,说明两个向量越接近。

我们的目的是,让每个字都含有当前这个句子中的所有字的信息,用注意力层,我们做到了。

需要注意的是,在上面 𝑠𝑒𝑙𝑓 𝑎𝑡𝑡𝑒𝑛𝑡𝑖𝑜𝑛 的计算过程中,我们通常使用 𝑚𝑖𝑛𝑖 𝑏𝑎𝑡𝑐ℎ,也就是一次计算多句话,上文举例只用了一个句子。

每个句子的长度是不一样的,需要按照最长的句子的长度统一处理。对于短的句子,进行 Padding 操作,一般我们用 0 来进行填充。

3、残差链接和层归一化

加入了残差设计和层归一化操作,目的是为了防止梯度消失,加快收敛。

1) 残差设计

我们在上一步得到了经过注意力矩阵加权之后的 𝑉, 也就是 𝐴𝑡𝑡𝑒𝑛𝑡𝑖𝑜𝑛(𝑄, 𝐾, 𝑉),我们对它进行一下转置,使其和 𝑋𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 的维度一致, 也就是 [𝑏𝑎𝑡𝑐ℎ 𝑠𝑖𝑧𝑒, 𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛𝑔𝑡ℎ, 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛] ,然后把他们加起来做残差连接,直接进行元素相加,因为他们的维度一致:

$$
X_{\text {embedding }}+\text { Attention }(Q, K, V)
$$

在之后的运算里,每经过一个模块的运算,都要把运算之前的值和运算之后的值相加,从而得到残差连接,训练的时候可以使梯度直接走捷径反传到最初始层:

$$
X+\text { SubLayer }(X)
$$

2) 层归一化

作用是把神经网络中隐藏层归一为标准正态分布,也就是 𝑖.𝑖.𝑑 独立同分布, 以起到加快训练速度, 加速收敛的作用。

$$
\mu_{i}=\frac{1}{m} \sum_{i=1}^{m} x_{i j}
$$

上式中以矩阵的行 (𝑟𝑜𝑤) 为单位求均值:

$$
\sigma_{j}^{2}=\frac{1}{m} \sum_{i=1}^{m}\left(x_{i j}-\mu_{j}\right)^{2}
$$

上式中以矩阵的行 (𝑟𝑜𝑤) 为单位求方差:

$$
\operatorname{LayerNorm}(x)=\alpha \odot \frac{x_{i j}-\mu_{i}}{\sqrt{\sigma_{i}^{2}+\epsilon}}+\beta
$$

然后用每一行的每一个元素减去这行的均值,再除以这行的标准差,从而得到归 一化后的数值, \(\epsilon\) 是为了防止除 0 ;之后引入两个可训练参数 \(\alpha, \beta\) 来弥补归一化的过程中损失掉的信息,注意 \(\odot\) 表示 元素相乘而不是点积,我们一般初始化 \(\alpha\) 为全 1 ,而\(\beta\) 为全 0 。

代码层面非常简单,单头 attention 操作如下:

class ScaledDotProductAttention(nn.Module):
    ''' Scaled Dot-Product Attention '''

    def __init__(self, temperature, attn_dropout=0.1):
        super().__init__()
        self.temperature = temperature
        self.dropout = nn.Dropout(attn_dropout)

    def forward(self, q, k, v, mask=None):
        # self.temperature是论文中的d_k ** 0.5,防止梯度过大
        # QxK/sqrt(dk)
        attn = torch.matmul(q / self.temperature, k.transpose(2, 3))

        if mask is not None:
            # 屏蔽不想要的输出
            attn = attn.masked_fill(mask == 0, -1e9)
        # softmax+dropout
        attn = self.dropout(F.softmax(attn, dim=-1))
        # 概率分布xV
        output = torch.matmul(attn, v)

        return output, attn

Multi-Head Attention 实现在 ScaledDotProductAttention 基础上构建:

class MultiHeadAttention(nn.Module):
    ''' Multi-Head Attention module '''

    # n_head头的个数,默认是8
    # d_model编码向量长度,例如本文说的512
    # d_k, d_v的值一般会设置为 n_head * d_k=d_model,
    # 此时concat后正好和原始输入一样,当然不相同也可以,因为后面有fc层
    # 相当于将可学习矩阵分成独立的n_head份
    def __init__(self, n_head, d_model, d_k, d_v, dropout=0.1):
        super().__init__()
        # 假设n_head=8,d_k=64
        self.n_head = n_head
        self.d_k = d_k
        self.d_v = d_v
        # d_model输入向量,n_head * d_k输出向量
        # 可学习W^Q,W^K,W^V矩阵参数初始化
        self.w_qs = nn.Linear(d_model, n_head * d_k, bias=False)
        self.w_ks = nn.Linear(d_model, n_head * d_k, bias=False)
        self.w_vs = nn.Linear(d_model, n_head * d_v, bias=False)
        # 最后的输出维度变换操作
        self.fc = nn.Linear(n_head * d_v, d_model, bias=False)
        # 单头自注意力
        self.attention = ScaledDotProductAttention(temperature=d_k ** 0.5)
        self.dropout = nn.Dropout(dropout)
        # 层归一化
        self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)

    def forward(self, q, k, v, mask=None):
        # 假设qkv输入是(b,100,512),100是训练每个样本最大单词个数
        # 一般qkv相等,即自注意力
        residual = q
        # 将输入x和可学习矩阵相乘,得到(b,100,512)输出
        # 其中512的含义其实是8x64,8个head,每个head的可学习矩阵为64维度
        # q的输出是(b,100,8,64),kv也是一样
        q = self.w_qs(q).view(sz_b, len_q, n_head, d_k)
        k = self.w_ks(k).view(sz_b, len_k, n_head, d_k)
        v = self.w_vs(v).view(sz_b, len_v, n_head, d_v)

        # 变成(b,8,100,64),方便后面计算,也就是8个头单独计算
        q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)

        if mask is not None:
            mask = mask.unsqueeze(1)   # For head axis broadcasting.
        # 输出q是(b,8,100,64),维持不变,内部计算流程是:
        # q*k转置,除以d_k ** 0.5,输出维度是b,8,100,100即单词和单词直接的相似性
        # 对最后一个维度进行softmax操作得到b,8,100,100
        # 最后乘上V,得到b,8,100,64输出
        q, attn = self.attention(q, k, v, mask=mask)

        # b,100,8,64-->b,100,512
        q = q.transpose(1, 2).contiguous().view(sz_b, len_q, -1)
        q = self.dropout(self.fc(q))
        # 残差计算
        q += residual
        # 层归一化,在512维度计算均值和方差,进行层归一化
        q = self.layer_norm(q)

        return q, attn

4、前馈网络

这个层就没啥说的了,非常简单,直接看代码吧:

class PositionwiseFeedForward(nn.Module):
    ''' A two-feed-forward-layer module '''

    def __init__(self, d_in, d_hid, dropout=0.1):
        super().__init__()
        # 两个fc层,对最后的512维度进行变换
        self.w_1 = nn.Linear(d_in, d_hid) # position-wise
        self.w_2 = nn.Linear(d_hid, d_in) # position-wise
        self.layer_norm = nn.LayerNorm(d_in, eps=1e-6)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        residual = x

        x = self.w_2(F.relu(self.w_1(x)))
        x = self.dropout(x)
        x += residual

        x = self.layer_norm(x)

        return x