CV深度学习面试问题记录

Posted by kevin on June 30, 2020

preface

这是在牛客网上根据大家的面经收集过来的题目,并以自己的理解来作出回答,也查阅了很多博客和资料。水平有限,不一定是正确的,欢迎指正,铁子们要找工作的时候可以看看

图像细粒度分类是什么

不同于普通的分类任务,图像细粒度分类是指的对同一个大类别进行更加细致的分类,例如哈士奇与柯基。

RPN是怎么做的

过拟合欠拟合是啥,怎么解决?

过拟合

过拟合是指训练误差和测试误差之间的差距太大。模型在训练集上表现很好,但在测试集上却表现很差,也就是泛化能力差。引起的原因有 ①训练数据集样本单一,样本不足 ②训练数据中噪声干扰过大 ③模型过于复杂。

防止过拟合的几种方法:

  1. 增加更多的训练集或者数据增强(根本上解决问题)
  2. 采用更简单点的模型(奥卡姆剃刀准则,简单的就是正确的)
  3. 正则化(L1,L2,Dropout)
  4. 可以减少不必要的特征或使用较少的特征组合

欠拟合

欠拟合是指模型不能在训练集上获得足够低的误差。换句换说,就是模型复杂度低,模型在训练集上就表现很差,没法学习到数据背后的规律。

欠拟合基本上都会发生在训练刚开始的时候,经过不断训练之后欠拟合应该不怎么考虑了。欠拟合可以通过使用非线性模型来改善,也就是使用一个更强的模型,并且可以增加训练特征

ResNet为啥能够解决梯度消失?怎么做的,能推导吗?

深度学习里面PCA的作用

PCA 用于降低维度,消除数据的冗余,减少一些不必要的特征。常用步骤如下(设有 m 条 n 维数据):

  1. 将原始数据按照列组成 m 行 n 列矩阵 X
  2. 将 X 的每一列(代表一个属性字段)进行零均值化(去中心化)减去这一列的均值得到特征中心化后的矩阵 $B = X_i - \mu_i$
  3. 求出协方差矩阵 $C = (X_i - \mu_i)*(X_i - \mu_i)^T$
  4. 求出矩阵 C 的特征值($det A-\lambda I =0$)和特征向量,按照特征值的大小将对应的特征向量从左往右排列,取前 k 个最大的特征值对应的特征向量组成矩阵 P
  5. Y = PX 即为降维到 k 维后的数据,即从 n 维降到了 k 维,保留了原本的大部分信息

reference:

王兴政老师的PPT

https://blog.csdn.net/Virtual_Func/article/details/51273985

YOLOv3的框是怎么聚出来的?

YOLOv3 的框是作者通过对自己的训练集的 bbox 聚类聚出来的,本质上就是一个 kmeans 算法,原理如下:

  1. 随机选取 k 个类别的中心点,代表各个聚类
  2. 计算所有样本到这几个中心点的距离,将每个样本归为距离最小的类别
  3. 更新每个类别的中心点(计算均值)
  4. 重新进行步骤 2 ,直到类的中心点不再改变

kmeans.png

YOLOv3 的 kmeans 想法不能直接用 w 和 h 来作为聚类计算距离的标准,因为 bbox 有大有小,不应该让 bbox 的尺寸影响聚类结果,因此,YOLOv3 的聚类使用了 IOU 思想来作为距离的度量 \(d(bbox, centroid) = 1 - IOU(bbox, centroid)\) 也就是说 IOU 越大的话,表示 bbox 和 box_cluster 就越类似,于是将 bbox 划归为 box_cluster

在 AlexeyAB 大佬改进的 darknet 中实现了这个过程,具体就是加载所有训练集中的 bbox 的 w 和 h,随机选择 k 个作为 centroids,然后用所有样本去和这 k 个 centroids 做 IOU 得出距离,并将样本归为距离最小的 cluster,然后更新 centroids 重复上面的步骤,直到 centroids 不再更新

reference:

https://github.com/AlexeyAB/darknet/blob/master/scripts/gen_anchors.py

https://www.cnblogs.com/sdu20112013/p/10937717.html

YOLO和SSD的本质区别?

R-CNN系列和SSD本质有啥不一样吗?

SSD的致命缺点?如何改进

需要人工设置prior box的min_size,max_size和aspect_ratio值。网络中prior box的基础大小和形状不能直接通过学习获得,而是需要手工设置。而网络中每一层feature使用的prior box大小和形状恰好都不一样,导致调试过程非常依赖经验。 虽然采用了pyramdial feature hierarchy的思路,但是对小目标的recall依然一般,并没有达到碾压Faster RCNN的级别。作者认为,这是由于SSD使用conv4_3低级feature去检测小目标,而低级特征卷积层数少,存在特征提取不充分的问题。 ———————————————— 版权声明:本文为CSDN博主「一个新新的小白」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_31511955/article/details/80597211

怎么解决梯度消失和梯度弥散

YOLOv1到YOLOv3的发展历程以及解决的问题

常见的Loss,回归的,分类的,Focal Loss

Focal Loss

reference: https://www.cnblogs.com/king-lps/p/9497836.html

L1、L2范数是什么,区别呢?

范数是具有 “长度” 概念的函数。在向量空间内,为所有的向量的赋予非零的增长度或者大小。不同的范数,所求的向量的长度或者大小是不同的。举个例子,2 维空间中,向量 (3,4) 的长度是 5,那么 5 就是这个向量的一个范数的值,更确切的说,是欧式范数或者L2范数的值。

更一般的情况下,对于一个 p- 范数,如果 $x = [x_1, x_2, x_3, …, x_n]^T$, 那么向量 x 的 p- 范数就是 $$ \begin{equation}

  x   _p = ( x_1 ^p+ x_2 ^p+…+ x_n ^p)^{1/p}

\end{equation} \(那么 L1 范数就是 p 为 1 的时候,也就是向量内所有元素的绝对值之和:\) \begin{equation}

  x   _p = ( x_1 + x_2 +…+ x_n )

\end{equation} \(L2 范数就是向量内所有元素的平方相加然后再开方:\) \begin{equation}

  x   _p = ( x_1 ^2+ x_2 ^2+…+ x_n ^2)^{1/2}

\end{equation} $$ 特别的,L0 范数是指向量中非零元素的个数!


在深度学习中,常常会在最后的 loss 函数中加一个正则项,将模型的权重 W 作为参数传入范数中,为的就是防止模型参数过大,这样可以防止过拟合发生。

reference:

https://zhuanlan.zhihu.com/p/28023308

https://www.cnblogs.com/LXP-Never/p/10918704.html(解释得挺好的,借鉴了下面这篇文章)

http://www.chioka.in/differences-between-l1-and-l2-as-loss-function-and-regularization/#

https://wangjie-users.github.io/2018/11/28/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3L1%E3%80%81L2%E8%8C%83%E6%95%B0/

pooling layer怎么反向传播

池化层没有可以训练的参数,因此在卷积神经网络的训练中,池化层只需要将误差传递到上一层,并不需要做梯度的计算。要追求一个原则,那就是梯度之和不变。

  • average pooling

直接将梯度平均分到对应的多个像素中,可以保证梯度之和是不变的

average

  • max pooling

max pooling要记录下最大值像素的index,然后在反向传播时将梯度传递给值最大的一个像素,其他像素不接受梯度,也就是为0

max

reference: https://blog.csdn.net/qq_21190081/article/details/72871704

手撸IOU计算公式

就是一个点,要记得加上 1 ,不然是错的!

def bb_intersection_over_union(boxA, boxB):
    boxA = [int(x) for x in boxA]
    boxB = [int(x) for x in boxB]

    xA = max(boxA[0], boxB[0])
    yA = max(boxA[1], boxB[1])
    xB = min(boxA[2], boxB[2])
    yB = min(boxA[3], boxB[3])
	
    # 要加上 1 ,因为像素是一个点,并不是一个矩形
    interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)

    boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)
    boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)
    
    iou = interArea / float(boxAArea + boxBArea - interArea)

    return iou

MobileNet在特征提取上有什么不同

MobileNet 用的是深度可分离卷积(Depthwise separable convolution),将传统的卷积分成了 Depthwise convolution 和 Pointwise convolution,也就是先对每个通道进行单独的卷积,不把这些特征组合在一起,然后用 Pointwise convolution 也就是一个 1* 1 卷积核将通道特征组合在一起,这样可以大大减少模型的参数量以及运算量,适合运用于嵌入式设备等对延时和性能要求较高的场景中。

下面就是深度可分离卷积的两个步骤,假设输入的 input 是 $D_F$ * $D_F$ * $M$,我们要的输出为 $D_F$ * $D_F$ * $N$,$D_K$ 为卷积核的大小

Depthwise separable convolution

用了深度可分离卷积所需要的运算量和普通卷积的计算量如下,在 MobileNet 中,卷积核的尺寸 $D_K$ 为 3 * 3,因此这样的计算量相当于减少了 9 倍

computation

NMS 怎样实现的

在目标检测中,常会利用非极大值抑制算法 (NMS) 对生成的大量候选框进行后处理 (post processing) ,去除冗余的候选框,得到最具代表性的结果,以加快目标检测的效率。

NMS 算法的主要流程如下所示:

根据候选框的类别分类概率做排序:A<B<C<D<E<F

  1. 先标记最大概率矩形框 F 是我们要保留下来的;
  2. 从最大概率矩形框 F 开始,分别判断 A~E 与 F 的重叠度 IOU(两框的交并比)是否大于某个设定的阈值,假设 B、D 与 F 的重叠度超过阈值,那么就扔掉 B、D;
  3. 从剩下的矩形框 A、C、E 中,选择概率最大的 E,标记为要保留下来的,然后判读 E 与 A、C 的重叠度,扔掉重叠度超过设定阈值的矩形框

就这样一直重复下去,直到剩下的矩形框没有了,标记完所有要保留下来的矩形框

import numpy as np

def py_nms(dets, thresh):
    """Pure Python NMS baseline."""
    #x1、y1、x2、y2、以及score赋值
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    scores = dets[:, 4]

    #每一个候选框的面积
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    #order是按照score降序排序的
    order = scores.argsort()[::-1]

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        #计算当前概率最大矩形框与其他矩形框的相交框的坐标,会用到numpy的broadcast机制,得到的是向量
        xx1 = np.maximum(x1[i], x1[order[1:]])
        yy1 = np.maximum(y1[i], y1[order[1:]])
        xx2 = np.minimum(x2[i], x2[order[1:]])
        yy2 = np.minimum(y2[i], y2[order[1:]])

        #计算相交框的面积,注意矩形框不相交时w或h算出来会是负数,用0代替
        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h
        #计算重叠度IOU:重叠面积/(面积1+面积2-重叠面积)
        ovr = inter / (areas[i] + areas[order[1:]] - inter)

        #找到重叠度不高于阈值的矩形框索引
        inds = np.where(ovr <= thresh)[0]
        #将order序列更新,由于前面得到的矩形框索引要比矩形框在原order序列中的索引小1,所以要把这个1加回来
        order = order[inds + 1]
    return keep

# test
if __name__ == "__main__":
    dets = np.array([[30, 20, 230, 200, 1], 
                     [50, 50, 260, 220, 0.9],
                     [210, 30, 420, 5, 0.8],
                     [430, 280, 460, 360, 0.7]])
    thresh = 0.35
    keep_dets = py_nms(dets, thresh)
    print(keep_dets)
    print(dets[keep_dets])

reference:

https://github.com/kuaikuaikim/DFace/

https://blog.csdn.net/Blateyang/article/details/79113030

为什么用smooth L1 loss,和L1 loss、L2 loss有啥区别?