r算法包
㈠ 目标检测算法(R-CNN,fast R-CNN,faster R-CNN,yolo,SSD,yoloV2,yoloV3)
深度学习目前已经应用到了各个领域,应用场景大体分为三类:物体识别,目标检测,自然语言处理。 目标检测可以理解为是物体识别和物体定位的综合 ,不仅仅要识别出物体属于哪个分类,更重要的是得到物体在图片中的具体位置。
2014年R-CNN算法被提出,基本奠定了two-stage方式在目标检测领域的应用。它的算法结构如下图
算法步骤如下:
R-CNN较传统的目标检测算法获得了50%的性能提升,在使用VGG-16模型作为物体识别模型情况下,在voc2007数据集上可以取得66%的准确率,已经算还不错的一个成绩了。其最大的问题是速度很慢,内存占用量很大,主要原因有两个
针对R-CNN的部分问题,2015年微软提出了Fast R-CNN算法,它主要优化了两个问题。
R-CNN和fast R-CNN均存在一个问题,那就是 由选择性搜索来生成候选框,这个算法很慢 。而且R-CNN中生成的2000个左右的候选框全部需要经过一次卷积神经网络,也就是需要经过2000次左右的CNN网络,这个是十分耗时的(fast R-CNN已经做了改进,只需要对整图经过一次CNN网络)。这也是导致这两个算法检测速度较慢的最主要原因。
faster R-CNN 针对这个问题, 提出了RPN网络来进行候选框的获取,从而摆脱了选择性搜索算法,也只需要一次卷积层操作,从而大大提高了识别速度 。这个算法十分复杂,我们会详细分析。它的基本结构如下图
主要分为四个步骤:
使用VGG-16卷积模型的网络结构:
卷积层采用的VGG-16模型,先将PxQ的原始图片,缩放裁剪为MxN的图片,然后经过13个conv-relu层,其中会穿插4个max-pooling层。所有的卷积的kernel都是3x3的,padding为1,stride为1。pooling层kernel为2x2, padding为0,stride为2。
MxN的图片,经过卷积层后,变为了(M/16) x (N/16)的feature map了。
faster R-CNN抛弃了R-CNN中的选择性搜索(selective search)方法,使用RPN层来生成候选框,能极大的提升候选框的生成速度。RPN层先经过3x3的卷积运算,然后分为两路。一路用来判断候选框是前景还是背景,它先reshape成一维向量,然后softmax来判断是前景还是背景,然后reshape恢复为二维feature map。另一路用来确定候选框的位置,通过bounding box regression实现,后面再详细讲。两路计算结束后,挑选出前景候选框(因为物体在前景中),并利用计算得到的候选框位置,得到我们感兴趣的特征子图proposal。
卷积层提取原始图像信息,得到了256个feature map,经过RPN层的3x3卷积后,仍然为256个feature map。但是每个点融合了周围3x3的空间信息。对每个feature map上的一个点,生成k个anchor(k默认为9)。anchor分为前景和背景两类(我们先不去管它具体是飞机还是汽车,只用区分它是前景还是背景即可)。anchor有[x,y,w,h]四个坐标偏移量,x,y表示中心点坐标,w和h表示宽度和高度。这样,对于feature map上的每个点,就得到了k个大小形状各不相同的选区region。
对于生成的anchors,我们首先要判断它是前景还是背景。由于感兴趣的物体位于前景中,故经过这一步之后,我们就可以舍弃背景anchors了。大部分的anchors都是属于背景,故这一步可以筛选掉很多无用的anchor,从而减少全连接层的计算量。
对于经过了3x3的卷积后得到的256个feature map,先经过1x1的卷积,变换为18个feature map。然后reshape为一维向量,经过softmax判断是前景还是背景。此处reshape的唯一作用就是让数据可以进行softmax计算。然后输出识别得到的前景anchors。
另一路用来确定候选框的位置,也就是anchors的[x,y,w,h]坐标值。如下图所示,红色代表我们当前的选区,绿色代表真实的选区。虽然我们当前的选取能够大概框选出飞机,但离绿色的真实位置和形状还是有很大差别,故需要对生成的anchors进行调整。这个过程我们称为bounding box regression。
假设红色框的坐标为[x,y,w,h], 绿色框,也就是目标框的坐标为[Gx, Gy,Gw,Gh], 我们要建立一个变换,使得[x,y,w,h]能够变为[Gx, Gy,Gw,Gh]。最简单的思路是,先做平移,使得中心点接近,然后进行缩放,使得w和h接近。如下:
我们要学习的就是dx dy dw dh这四个变换。由于是线性变换,我们可以用线性回归来建模。设定loss和优化方法后,就可以利用深度学习进行训练,并得到模型了。对于空间位置loss,我们一般采用均方差算法,而不是交叉熵(交叉熵使用在分类预测中)。优化方法可以采用自适应梯度下降算法Adam。
得到了前景anchors,并确定了他们的位置和形状后,我们就可以输出前景的特征子图proposal了。步骤如下:
1,得到前景anchors和他们的[x y w h]坐标。
2,按照anchors为前景的不同概率,从大到小排序,选取前pre_nms_topN个anchors,比如前6000个
3,剔除非常小的anchors。
4,通过NMS非极大值抑制,从anchors中找出置信度较高的。这个主要是为了解决选取交叠问题。首先计算每一个选区面积,然后根据他们在softmax中的score(也就是是否为前景的概率)进行排序,将score最大的选区放入队列中。接下来,计算其余选区与当前最大score选区的IOU(IOU为两box交集面积除以两box并集面积,它衡量了两个box之间重叠程度)。去除IOU大于设定阈值的选区。这样就解决了选区重叠问题。
5,选取前post_nms_topN个结果作为最终选区proposal进行输出,比如300个。
经过这一步之后,物体定位应该就基本结束了,剩下的就是物体识别了。
和fast R-CNN中类似,这一层主要解决之前得到的proposal大小形状各不相同,导致没法做全连接。全连接计算只能对确定的shape进行运算,故必须使proposal大小形状变为相同。通过裁剪和缩放的手段,可以解决这个问题,但会带来信息丢失和图片形变问题。我们使用ROI pooling可以有效的解决这个问题。
ROI pooling中,如果目标输出为MxN,则在水平和竖直方向上,将输入proposal划分为MxN份,每一份取最大值,从而得到MxN的输出特征图。
ROI Pooling层后的特征图,通过全连接层与softmax,就可以计算属于哪个具体类别,比如人,狗,飞机,并可以得到cls_prob概率向量。同时再次利用bounding box regression精细调整proposal位置,得到bbox_pred,用于回归更加精确的目标检测框。
这样就完成了faster R-CNN的整个过程了。算法还是相当复杂的,对于每个细节需要反复理解。faster R-CNN使用resNet101模型作为卷积层,在voc2012数据集上可以达到83.8%的准确率,超过yolo ssd和yoloV2。其最大的问题是速度偏慢,每秒只能处理5帧,达不到实时性要求。
针对于two-stage目标检测算法普遍存在的运算速度慢的缺点, yolo创造性的提出了one-stage。也就是将物体分类和物体定位在一个步骤中完成。 yolo直接在输出层回归bounding box的位置和bounding box所属类别,从而实现one-stage。通过这种方式, yolo可实现45帧每秒的运算速度,完全能满足实时性要求 (达到24帧每秒,人眼就认为是连续的)。它的网络结构如下图:
主要分为三个部分:卷积层,目标检测层,NMS筛选层。
采用Google inceptionV1网络,对应到上图中的第一个阶段,共20层。这一层主要是进行特征提取,从而提高模型泛化能力。但作者对inceptionV1进行了改造,他没有使用inception mole结构,而是用一个1x1的卷积,并联一个3x3的卷积来替代。(可以认为只使用了inception mole中的一个分支,应该是为了简化网络结构)
先经过4个卷积层和2个全连接层,最后生成7x7x30的输出。先经过4个卷积层的目的是为了提高模型泛化能力。yolo将一副448x448的原图分割成了7x7个网格,每个网格要预测两个bounding box的坐标(x,y,w,h)和box内包含物体的置信度confidence,以及物体属于20类别中每一类的概率(yolo的训练数据为voc2012,它是一个20分类的数据集)。所以一个网格对应的参数为(4x2+2+20) = 30。如下图
其中前一项表示有无人工标记的物体落入了网格内,如果有则为1,否则为0。第二项代表bounding box和真实标记的box之间的重合度。它等于两个box面积交集,除以面积并集。值越大则box越接近真实位置。
分类信息: yolo的目标训练集为voc2012,它是一个20分类的目标检测数据集 。常用目标检测数据集如下表:
| Name | # Images (trainval) | # Classes | Last updated |
| --------------- | ------------------- | --------- | ------------ |
| ImageNet | 450k | 200 | 2015 |
| COCO | 120K | 90 | 2014 |
| Pascal VOC | 12k | 20 | 2012 |
| Oxford-IIIT Pet | 7K | 37 | 2012 |
| KITTI Vision | 7K | 3 | |
每个网格还需要预测它属于20分类中每一个类别的概率。分类信息是针对每个网格的,而不是bounding box。故只需要20个,而不是40个。而confidence则是针对bounding box的,它只表示box内是否有物体,而不需要预测物体是20分类中的哪一个,故只需要2个参数。虽然分类信息和confidence都是概率,但表达含义完全不同。
筛选层是为了在多个结果中(多个bounding box)筛选出最合适的几个,这个方法和faster R-CNN 中基本相同。都是先过滤掉score低于阈值的box,对剩下的box进行NMS非极大值抑制,去除掉重叠度比较高的box(NMS具体算法可以回顾上面faster R-CNN小节)。这样就得到了最终的最合适的几个box和他们的类别。
yolo的损失函数包含三部分,位置误差,confidence误差,分类误差。具体公式如下:
误差均采用了均方差算法,其实我认为,位置误差应该采用均方差算法,而分类误差应该采用交叉熵。由于物体位置只有4个参数,而类别有20个参数,他们的累加和不同。如果赋予相同的权重,显然不合理。故yolo中位置误差权重为5,类别误差权重为1。由于我们不是特别关心不包含物体的bounding box,故赋予不包含物体的box的置信度confidence误差的权重为0.5,包含物体的权重则为1。
Faster R-CNN准确率mAP较高,漏检率recall较低,但速度较慢。而yolo则相反,速度快,但准确率和漏检率不尽人意。SSD综合了他们的优缺点,对输入300x300的图像,在voc2007数据集上test,能够达到58 帧每秒( Titan X 的 GPU ),72.1%的mAP。
SSD网络结构如下图:
和yolo一样,也分为三部分:卷积层,目标检测层和NMS筛选层
SSD论文采用了VGG16的基础网络,其实这也是几乎所有目标检测神经网络的惯用方法。先用一个CNN网络来提取特征,然后再进行后续的目标定位和目标分类识别。
这一层由5个卷积层和一个平均池化层组成。去掉了最后的全连接层。SSD认为目标检测中的物体,只与周围信息相关,它的感受野不是全局的,故没必要也不应该做全连接。SSD的特点如下。
每一个卷积层,都会输出不同大小感受野的feature map。在这些不同尺度的feature map上,进行目标位置和类别的训练和预测,从而达到 多尺度检测 的目的,可以克服yolo对于宽高比不常见的物体,识别准确率较低的问题。而yolo中,只在最后一个卷积层上做目标位置和类别的训练和预测。这是SSD相对于yolo能提高准确率的一个关键所在。
如上所示,在每个卷积层上都会进行目标检测和分类,最后由NMS进行筛选,输出最终的结果。多尺度feature map上做目标检测,就相当于多了很多宽高比例的bounding box,可以大大提高泛化能力。
和faster R-CNN相似,SSD也提出了anchor的概念。卷积输出的feature map,每个点对应为原图的一个区域的中心点。以这个点为中心,构造出6个宽高比例不同,大小不同的anchor(SSD中称为default box)。每个anchor对应4个位置参数(x,y,w,h)和21个类别概率(voc训练集为20分类问题,在加上anchor是否为背景,共21分类)。如下图所示:
另外,在训练阶段,SSD将正负样本比例定位1:3。训练集给定了输入图像以及每个物体的真实区域(ground true box),将default box和真实box最接近的选为正样本。然后在剩下的default box中选择任意一个与真实box IOU大于0.5的,作为正样本。而其他的则作为负样本。由于绝大部分的box为负样本,会导致正负失衡,故根据每个box类别概率排序,使正负比例保持在1:3。SSD认为这个策略提高了4%的准确率
另外,SSD采用了数据增强。生成与目标物体真实box间IOU为0.1 0.3 0.5 0.7 0.9的patch,随机选取这些patch参与训练,并对他们进行随机水平翻转等操作。SSD认为这个策略提高了8.8%的准确率。
和yolo的筛选层基本一致,同样先过滤掉类别概率低于阈值的default box,再采用NMS非极大值抑制,筛掉重叠度较高的。只不过SSD综合了各个不同feature map上的目标检测输出的default box。
SSD基本已经可以满足我们手机端上实时物体检测需求了,TensorFlow在Android上的目标检测官方模型ssd_mobilenet_v1_android_export.pb,就是通过SSD算法实现的。它的基础卷积网络采用的是mobileNet,适合在终端上部署和运行。
针对yolo准确率不高,容易漏检,对长宽比不常见物体效果差等问题,结合SSD的特点,提出了yoloV2。它主要还是采用了yolo的网络结构,在其基础上做了一些优化和改进,如下
网络采用DarkNet-19:19层,里面包含了大量3x3卷积,同时借鉴inceptionV1,加入1x1卷积核全局平均池化层。结构如下
yolo和yoloV2只能识别20类物体,为了优化这个问题,提出了yolo9000,可以识别9000类物体。它在yoloV2基础上,进行了imageNet和coco的联合训练。这种方式充分利用imageNet可以识别1000类物体和coco可以进行目标位置检测的优点。当使用imageNet训练时,只更新物体分类相关的参数。而使用coco时,则更新全部所有参数。
YOLOv3可以说出来直接吊打一切图像检测算法。比同期的DSSD(反卷积SSD), FPN(feature pyramid networks)准确率更高或相仿,速度是其1/3.。
YOLOv3的改动主要有如下几点:
不过如果要求更精准的预测边框,采用COCO AP做评估标准的话,YOLO3在精确率上的表现就弱了一些。如下图所示。
当前目标检测模型算法也是层出不穷。在two-stage领域, 2017年Facebook提出了mask R-CNN 。CMU也提出了A-Fast-RCNN 算法,将对抗学习引入到目标检测领域。Face++也提出了Light-Head R-CNN,主要探讨了 R-CNN 如何在物体检测中平衡精确度和速度。
one-stage领域也是百花齐放,2017年首尔大学提出 R-SSD 算法,主要解决小尺寸物体检测效果差的问题。清华大学提出了 RON 算法,结合 two stage 名的方法和 one stage 方法的优势,更加关注多尺度对象定位和负空间样本挖掘问题。
目标检测领域的深度学习算法,需要进行目标定位和物体识别,算法相对来说还是很复杂的。当前各种新算法也是层不出穷,但模型之间有很强的延续性,大部分模型算法都是借鉴了前人的思想,站在巨人的肩膀上。我们需要知道经典模型的特点,这些tricks是为了解决什么问题,以及为什么解决了这些问题。这样才能举一反三,万变不离其宗。综合下来,目标检测领域主要的难点如下:
一文读懂目标检测AI算法:R-CNN,faster R-CNN,yolo,SSD,yoloV2
从YOLOv1到v3的进化之路
SSD-Tensorflow超详细解析【一】:加载模型对图片进行测试 https://blog.csdn.net/k87974/article/details/80606407
YOLO https://pjreddie.com/darknet/yolo/ https://github.com/pjreddie/darknet
C#项目参考:https://github.com/AlturosDestinations/Alturos.Yolo
项目实践贴个图。
㈡ R-CNN 系列 object detection 算法
在 object detection 领域,近 5 年的突破性进展似乎都与一个名字有关系:Ross Girshick。梳理从 R-CNN,Fast R-CNN, Faster R-CNN 到 Mask R-CNN 等各种经典模型,Ross Girshick 都是作者之一,甚至连 YOLO 的作者中也出现了 Ross Girshick 的名字。
这位大神简历如下:
从算法到实现框架再到数据集,这位大神实现了一条龙的突破~
本文的目的是整理总结 R-CNN 系列算法的发展历程和模型本身的核心思想,不涉及太多技术细节(例如训练数据预处理,超参数设置等)。
参考文献主要是上述各算法的原文以及下列资源:
R-CNN,一般认为全称是 Region-based CNN 或者作者原文中提到的 Regions with CNN features。
概括地说,R-CNN 的步骤如下图所示:
下面详细介绍 R-CNN 中的关键环节。
对于输入的图片,首先利用 selective search 算法 生成约 2000 个 region。关于 selective search 算法可以参考 原文 ,也可以参考 我们之前的博客文章 。原文中提到 R-CNN 对各种 region proposal 算法没有偏好,之所以选择 selective search 算法仅仅是为了方便与前人工作做对比。
这一部分的目的是对于每一个 region,通过 CNN (原文选用 AlexNet) 进行特征提取,得到统一长度的 feature vector,以便后续的分类。
由于每个 region 的大小差别比较大,而 AlexNet 默认接收 227×227 pixel 的图片,这里就需要对 region 做一些预处理,主要是 region 大小的转化。
要把一个任意大小的图片转化成 227×227 像素的图片方法有很多,原文中介绍了 4 种方式:
分别是:
最终作者选择了 warp + padding 的方式,一方面 warp 相对来说是最简单的,直接把任意大小的图片缩放成 227×227 即可,另外 padding 是在原 region 周围稍微添加了一些像素点,从实际效果看提高了检测正确率。
将统一大小的 region 送入 CNN 中,进行特征提取。 如何得到这个 CNN 也是一个问题。
针对目标检测的数据集 ILSVRC detection dataset 包含了 200 类物体,PASCAL VOC (Visual Object Classes) 包含了 20 类物体。相对来说带有标签的训练数据比较少,不足以训练一个大型的 CNN,因此采用了 transfer learning 的技术。原文中并没有提到 transfer learning 这个名词,只是说 fine-tuning 。
首先借用在 ImageNet 上已经训练好的 CNN 模型(最初的文章中用了 AlexNet,后来 arXiv 上新版文章中用了 VGG,效果提升很明显),然后在 PASCAL 数据集上进行 fine-tuning。这里对 AlexNet 网络结构的改变只是将原本对应 ImageNet 1000 类输出的 classification layer 替换成了对应 N+1 类输出的 classification layer,该层权重随机初始化。对于 PASCAL 数据集 N=20,ILSVRC 数据集 N=200,另外 +1 对应 background 类型。
经过 fine-tuning 之后,CNN softmax layer 之前的 4096 维向量即为该 region 的 feature vector.
得到 region 的 feature vector 之后,送入 SVM 进行最后的分类。
这里 SVM 的训练是针对不同类型的物体分开进行的,每一类训练一个 SVM,它只给出针对这一类物体的分类结果。之所以最后用 SVM 分类,而不是直接用 CNN 的 softmax 进行分类,原文作者的解释是尝试过 softmax 之后发现效果比 SVM 差一些,但是同时指出如果调整一些训练策略,softmax 和 SVM 之间的差距有可能缩小。这也为后来基于 R-CNN 的改进埋下了伏笔。
得到所有 region 对应的检测结果(即包含某种类型物体的概率 score)之后,还有一步操作: Non-Maximum Suppression (NMS) 。如果两个 region 检测到同一类物体,比如都检测到了行人,一个 region score 较高,而另一个 score 较低,当这两个 region 的 IoU (intersection-over-union) 超过某个阈值时,即它们重合较多时,只保留那个 score 较高的 region.
object detection 的任务除了检测图中的物体,还要给出定位,即用 bounding box 尽量准确的圈出该物体。前边基于 region 的分类过程可能能够正确辨识出 region 中的物体,但是初始的 region 并不一定是一个合适的 bbox。在 R-CNN 最后又添加了一个线性回归模型,基于 feature vector 来预测正确的 bbox 相对于 region 的位置变换,即预测 bbox 应该如何调整。这个训练过程也是 class-specific 的。
在最终使用时,R-CNN 输出包含两部分:
理论上来说,更新 bbox 的位置之后,应该在新的 bbox 中重新进行分类,这样准确度可能更高一些,但是原文作者发现实际上并没有明显改进。因此,实际使用中并没有对新的 bbox 重新分类。
总的来说,上述 R-CNN 的训练是分多步走的:先是 fine-tuning 一个 CNN 得到 feature vector,然后训练 SVM 进行分类,最后还要再训练一个线性回归环节预测 bounding box 的调整。
Fast R-CNN 的改进是不再使用独立的 SVM 和线性回归,而是统一用 CNN 将这三个环节整合起来。Fast R-CNN 在训练时间和检测时间方面比当时已有的其他算法快若干数量级。
Fast R-CNN 整体框架如下:
基本步骤:
在上述各环节中,我认为比较关键的有两个:一是 RoI projection,即将 image 上的 RoI 映射到 feature map 上的 RoI。二是通过 RoI pooling layer 将 feature map 上不同大小的 RoI 转化成统一大小的 sub feature map。而这两个环节都借鉴了 SPPnets ,其中 RoI pooling layer 是 SPPnets 中 Spatial Pyramid Pooling layer 的特例。
原本 R-CNN 是在原图上选取若干RoI,然后经过 CNN 处理,最后提取出 feature vector。对于每个图片上不同的 RoI 来说,从输入到输出没有任何共享的东西。
RoI projection 的作用是将 R-CNN 中对 image RoI 的处理推迟到了 feature map 上,这样可以让一个 image 的所有 RoI 共享从 image 到 feature map 的卷积处理过程。这很显然会加速训练和测试过程。至于如何将 image RoI 映射到 feature map RoI,已经有了 非常细致的讨论 ,这里不再赘述。
如何将 feature map 上不同大小的 RoI 转化成统一大小的 sub feature map? 这里 有非常直观的动画演示。
概括如下:
假设我们已经得到下面的 feature map (只考虑 2D)
其中 RoI 为黑框部分,大小为 。
我们希望将 RoI 转化成 2×2 大小,可以选择一个 2×2 的窗口如下
对每一个格子进行 max pooling 操作,得到如下的 2×2 的 feature map
总的来说,如果 RoI 大小为 ,希望得到的 feature map 大小为 ,则窗口中格子数目为 。可以根据具体情况向上或向下取整。
结合实际应用,如果 CNN 网络选用 VGG16,结构如下:
将最后一个 max pooling layer 替换为 RoI pooling layer。前部的卷积层对输入图片的大小没有严格限制,这一限制主要是在 fully connected layer,所以为了配合 VGG16 网络结构,要确保每个 RoI 输出的 feature map 依然为 。
对于 VGG16 网络结构的修改还包括:
在 Fast R-CNN 中,region proposal 是由 CNN 网络之外的算法提供的,例如 selective search。相对于后续的 region recognition 过程,region proposal 这一步实际上是整个算法的速度瓶颈。
Faster R-CNN 之所以 "Faster",就是因为提出了 Region Proposal Network (RPN) ,加速了 region proposal 过程。Faster R-CNN 本质上就是 RPN + Fast R-CNN.
整个 Faster R-CNN 结构如下:
或者更加详细的结构如下:
RPN 和 Fast R-CNN 共享从 image 到最后一层 CNN 输出的 feature map 这一段网络结构。 有些文章 也将 Faster R-CNN 看做三个模块:用于生成 feature map 的 Feature network,用于生成 region proposal 的 RPN,以及用于最终的 object detection 的 Detection network。我们这里还是采用 RPN + Fast R-CNN 的形式。
RPN 的输入是原始 image,输出是 region proposals。在具体实现中,RPN 是 fully convolutional network (FCN),只包含 convolutional layer,原本在分类/回归中常用的全连通层也由卷积操作替代。
有了 region proposals,后边的操作与 Fast R-CNN 是相同的。
原文中采用 alternating training 的方式:
㈢ 求计算机求解关系R的传递闭包 C语言算法
传递闭包,最简单的技术是采用 【弗洛伊德算法】
Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。
Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
Floyd-Warshall算法的原理是动态规划。
设Di,j,k为从i到j的只以(1..k)集合中的节点为中间节点的最短路径的长度。
1.若最短路径经过点k,则Di,j,k = Di,k,k − 1 + Dk,j,k − 1;
2.若最短路径不经过点k,则Di,j,k = Di,j,k − 1。
因此,Di,j,k = min(Di,k,k − 1 + Dk,j,k − 1,Di,j,k − 1)。
在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。
代码请见: