图像识别相关算法题解

1. CNN参数计算

(1)卷积层输出张量大小

图片说明

其中O为输出图像的尺寸,I为输入图像的尺寸,K为卷积层的核尺寸,N为核的数量,S为移动步长,P为填充。以AlexNet网络为例,其中输入图像的尺寸为227x227x3,第一个卷积层有96个尺寸
为11x11x3的卷积核,步长为4,填充为0,

图片说明

输出图像尺寸为55x55x96.

(2)池化层的输出张量大小

图片说明

其中O为输出图像的尺寸,I为输入图像的尺寸,S为移动步长,Ps为池化层尺寸(不同于卷积层,池化层的输出通道数不变化)。例:每一层卷积层后的池化层尺寸为3x3,步长为2,基于前面的输出维度55x55x96,池化的输出

图片说明

即维度为27x27x96。

(3)全连接层的输出向量长度等于神经单元的数量

(4)卷积层参数数量
卷积层中,核的深度等于输入图像的通道数,于是每个核有K*K个参数,当核的个数为N时,有:
图片说明

其中Wc为卷积层的权重weights数量,Bc为卷积层中的偏移biases数量,Pc为所有参数数量,K为核尺寸,N为核数量,C为输入图像通道数。
(5)全连接层的参数数量
a. 当全连接层接在卷积相邻时,有
图片说明
其中,Wcf为权重weights数量,Bcf为偏移biases数量,O为前接卷积层的输出图像的维度,N为前接卷积层的核数量,F为全连接层的神经单元数量。
b. 当全连接层相邻全连接层时,有
图片说明
其中F为当前全连接层的神经单元数量,$$为前接全连接层的神经单元数量。

2. 说说CNN的优点、缺点

对于此类基本概念,一般不会问太细节(面试官也记不住),按自己理解来答即可
优点:

  1. 共享卷积核,可以方便处理高维数据
  2. 自动进行特征提取,无需手动设计特征

缺点:

  1. 池化层会丢失大量信息
  2. 自动进行特征提取,也就造成了模型可解释性差,更类似一个黑盒
  3. 需要大批量数据进行训练

3. Faster RCNN的损失函数,由哪几部分构成,anchor如何生成

faster rcnn是很经典的目标检测方法,需要熟练掌握,并且很多部分需要熟悉代码,如最基本的两个bbox的iou计算、nms等

损失函数由四部分组成:

  1. RPN分类损失:anchor是否为gt,二类交叉熵损失
  2. RPN位置回归损失:anchor位置微调,bbox的第一次修正
  3. ROI分类损失:ROI所属类别,分类损失
  4. ROI位置回归损失:继续对ROI位置微调,第二次对bbox的修正

最终的损失是四个损失相加

anchor生成:

首先,要先熟悉anchor概念:Anchor是以待检测位置为中心,以指定的大小和高宽比构成一组锚框。假设Feature Map的宽度为W,宽度为H,在每个待检测的位置生成的锚框数目为K,根据滑动窗口的方法,生成总的锚框的数量是W * H * K
每个锚点有三种尺寸(128,256,512)和三种长宽比(1:1,1:2,2:1),相互组合,就是每个锚点有9个锚框

扩展:实现生成anchor代码

4. 生成对抗网络比起常用的数据增强为什么有效,有效在哪里

常用的数据增强包括:

  • 几何变换(Geometric Transformations)
  • 颜色变换(Color Space)
  • 旋转 | 反射变换(Rotation/Reflection)
  • 噪声注入(Noise Injection)
  • 混合图像(Mix)
  • 随机擦除(Random Erasing)
  • 缩放变换(Zoom)
  • 翻转变换(Flipping)
  • 裁剪(Cropping)

但这些常用的数据增广无法解决特定场景的复杂的问题,比如对于行人检测而言,我们白天采集数据,然后用白天采集的数据训练模型,把这个模型用到晚上,就会发现效果很差。于是,你不得不晚上再去采集数据。当然,AugGAN的存在让你不再需要晚上再去采集数据。这里有个原因是,白天数据和晚上数据其实冗余成分很大,只是数据域(domain)的不同。如果能把一个数据域的数据搬移到另一个数据域,是不是就增强了数据呢?我们可以通过白天的数据来生成晚上的数据;我们可以通过夏天的数据生成冬天的数据;我们也可以通过晴天的数据生成雾霾的数据……
也就是说,利用生成对抗网络,我们可以很方便地生成海量的复杂数据,有的放矢,有针对性地补足数据,提升模型性能。
这里也给出了一个数据增广的 demo,以及对应的可视化结果。

import numpy as np
import imgaug as ia
import imgaug.augmenters as iaa


images = np.array(
    [ia.quokka(size=(64, 64)) for _ in range(32)],
    dtype=np.uint8
)

seq = iaa.Sequential([

    # 随机水平翻转
    iaa.Fliplr(0.5), 
    # 随机裁剪
    iaa.Crop(percent=(0, 0.1)), 
    # 以 50%的概率进行高斯模糊
    iaa.Sometimes(
        0.5,
        iaa.GaussianBlur(sigma=(0, 0.5))
    ),
    # 调整对比度
    iaa.LinearContrast((0.75, 1.5)),
    #添加高斯噪声
    iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.05*255), per_channel=0.5),
    # 调整亮度
    iaa.Multiply((0.8, 1.2), per_channel=0.2),
    # 仿射变换
    iaa.Affine(
        scale={"x": (0.8, 1.2), "y": (0.8, 1.2)},
        translate_percent={"x": (-0.2, 0.2), "y": (-0.2, 0.2)},
        rotate=(-25, 25),
        shear=(-8, 8)
    )
], random_order=True) # 乱序

images_aug = seq(images=images)


# 画图
import matplotlib.pyplot as plt
%matplotlib inline
for i in range(len(images_aug)):
    plt.subplot(4, 8, i+1)
    plt.axis('off')
    plt.imshow(images_aug[i])
plt.show()

图片说明

数据增广的可视化结果,一张图瞬间变成了 32 份,有效提高模型的泛化能力

延伸考点

你知道哪些用 gan 做数据增广的方法?
提示:

  • 基于CycleGAN这类风格迁移的方法、
  • DAGAN (Data augmented GAN),它学习如何使用真实图像的低维表示来生成合成图像、
  • AutoAugment,使用增强学习从数据本身寻找最佳图像变换策略,对于不同的任务学习不同的增强方法、
  • 等等

5. vgg16同期还有哪些网络,inception网络有什么特点

VGG 网络是很早的文章了,那时候学术界主要关注的任务还是图像分类,参加ILSVRC-ImageNet比赛,提高 ImageNet 数据集的结果,同期还有 vgg19、alexNet、inception v1-3,以及非常经典的 resnet 网络。

  • AlexNet 因为受限于显存大小,作为副产品,提出了分组卷积;网络结构更深,使用了relu 激活函数、使用max pooling 替代 mean pooling,使用了 dropout
  • VGGNet 使用 3x3、1x1 的卷积核,取代之前网络的大卷积核(如 7x7); 网络深度达到 19 层
  • Inception and GoogLeNet 谷歌提出的模型,致敬 LeNet; 之前模型都是往深度考虑,inception 网络则是往宽度考虑
  • ResNet 何凯明大佬的经典论文,残差思想很棒;残差可以缓解梯度消失和梯度爆炸,网络深度达到 152 层。

早期网络的改进都是往模型深度上考虑的,认为模型越深,把卷积层堆叠得越来越多,效果就越好。
但Inception网络是设计一种能够捕获不同尺度特征的网络,即对输入图像并行地执行多个卷积运算或池化操作,并将所有输出结果拼接为一个非常深的特征图。如下图所示:
图片说明
网络结构图出自 inception 论文

因为 1x1、3x3 或 5x5 等不同的卷积运算与池化操作可以获得输入图像的不同信息,并行处理这些运算并结合所有结果将获得更好的图像表征。

此后,inception 还有几个变体,整体思想没有变化,做了几点改进:

Inception V2

  1. 学习VGGNet的特点,用两个33卷积代替55卷积,可以降低参数量。
  2. 提出BN算法。就是对输入层信息分布标准化处理,使得规范化为N(0,1)的高斯分布,收敛速度大大提高。

Inception V3

学习Factorization into small convolutions的思想,将一个二维卷积拆分成两个较小卷积,例如将7x7卷积拆成1x7卷积和7x1卷积。这样做的好处是降低参数量。paper中指出,通过这种非对称的卷积拆分,比对称的拆分为几个相同的卷积效果更好,可以处理更多,更丰富的空间特征。

Inception V4

借鉴了微软的ResNet网络结构思想。
Inception block 代码示例

import torch
from torch import nn
import torch.nn.functional as F

class BasicConv2d(nn.Module):

    def __init__(self, in_channels, out_channels, **kwargs):
        super(BasicConv2d, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs)
        self.bn = nn.BatchNorm2d(out_channels, eps=0.001)

    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        return F.relu(x, inplace=True)


# inception block 
class InceptionA(nn.Module):

    def __init__(self, in_channels, pool_features):
        super(InceptionA, self).__init__()
        # 定义 1*1 卷积的分支
        self.branch1x1 = BasicConv2d(in_channels, 64, kernel_size=1)
        # 定义 5*5 卷积的分支
        self.branch5x5_1 = BasicConv2d(in_channels, 48, kernel_size=1)
        self.branch5x5_2 = BasicConv2d(48, 64, kernel_size=5, padding=2)
        # 定义 3*3 卷积的分支
        self.branch3x3dbl_1 = BasicConv2d(in_channels, 64, kernel_size=1)
        self.branch3x3dbl_2 = BasicConv2d(64, 96, kernel_size=3, padding=1)
        self.branch3x3dbl_3 = BasicConv2d(96, 96, kernel_size=3, padding=1)

        self.branch_pool = BasicConv2d(in_channels, pool_features, kernel_size=1)

    def forward(self, x):
        branch1x1 = self.branch1x1(x)

        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)

        branch3x3dbl = self.branch3x3dbl_1(x)
        branch3x3dbl = self.branch3x3dbl_2(branch3x3dbl)
        branch3x3dbl = self.branch3x3dbl_3(branch3x3dbl)

        branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
        branch_pool = self.branch_pool(branch_pool)
        # 三个分支concat
        outputs = [branch1x1, branch5x5, branch3x3dbl, branch_pool]
        return torch.cat(outputs, 1)

6. vgg16当时提出有哪几个改进点,3×3卷积和1×1卷积的作用

VGG 的改进点
VGG的核心思想还是探索网络的深度,VGG16、VGG19分别为16层和19层。主要创新点包括:

  • 由之前的 7x7, 5x5 大卷积核,采用了固定3x3较小的卷积核,层数增加带了更强的非线性,使模型的判别能力更强;
  • 虽然层数增加,因为使用了较小的卷积核,所以反而在卷积层减小了参数数量,与大卷积核相比相当于增加了正则化。
  • 将最后三层全连接层改为卷积层,可以应对不同尺寸的输入,并在最后输出直接求平均。
  • 训练和预测时的技巧:训练时先训练级别A的简单网络,再复用A网络的权重来初始化后面的几个复杂模型,这样训练收敛的速度更快。预测时采用Multi-Scale的方法,同时还再训练时VGGNet也使用了Multi-Scale的方法做数据增强

3x3 卷积核的作用

前面提到,之前的 7x7, 5x5 大卷积核,为了保证感受野,提取全局的特征。而 vgg网络就提出,多个较小卷积核叠加感受野等于一个大的卷积核
如下图,2个3x3卷积核叠加,可代替一个5x5的卷积核;
图片说明
2个33卷积核叠加,可代替一个55的卷积核

同样的,3个3x3的卷积核叠加,则可代替一个7x7的卷积核。

1x1 卷积核的作用

1x1卷积核的作用

  1. 采用非线性激活函数可以提高模型的非线性能力;
  2. 专注于跨通道的特征组合;
  3. 对feature map在channel层级进行降维或升维。

Vgg net 代码示例(包含 vgg16 和 vgg19)

import torch
import torch.nn as nn

# vgg16, 可以看到, 带有参数的刚好为16个
net_arch16 = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M5', "FC1", "FC2", "FC"]

# vgg19, 基本和 vgg16 相同, 只不过在后3个卷积段中, 每个都多了一个卷积层
net_arch19 = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M5', "FC1", "FC2", "FC"]

class VGGNet(nn.Module):
    def __init__(self, net_arch, num_classes):
        # net_arch 即为上面定义的列表: net_arch16 或 net_arch19
        super(VGGNet, self).__init__()
        self.num_classes = num_classes
        layers = []
        in_channels = 3 # 初始化通道数
        for arch in net_arch:
            if arch == 'M':
                layers.append(nn.MaxPool2d(kernel_size=2, stride=2))
            elif arch == 'M5':
                layers.append(nn.MaxPool2d(kernel_size=3, stride=1, padding=1))
            elif arch == "FC1":
                layers.append(nn.Conv2d(in_channels=512, out_channels=1024, kernel_size=3, padding=6, dilation=6))
                layers.append(nn.ReLU(inplace=True))
            elif arch == "FC2":
                layers.append(nn.Conv2d(1024,1024, kernel_size=1))
                layers.append(nn.ReLU(inplace=True))
            elif arch == "FC":
                layers.append(nn.Conv2d(1024,self.num_classes, kernel_size=1))
            else:
                layers.append(nn.Conv2d(in_channels=in_channels, out_channels=arch, kernel_size=3, padding=1)
                layers.append(nn.ReLU(inplace=True))
                in_channels=arch
        self.vgg = nn.ModuleList(layers)
    def forward(self, input_data):
        x = input_data
        for layer in self.vgg:
            x = layer(x)
        out = x
        return out

7. 手写MIOU

概念:IoU(Intersection over Union)是交并比,计算式为:
MioU(Mean Intersection over Union)是均交并比,属于语义分割的标准度量,计算两个集合的交并比,在语义分割的问题中,这两个集合为真实值和预测值,计算式如下:
图片说明

等价于:
图片说明
图片说明
MIoU计算两圆交集(橙色部分)与两圆并集(红色+橙色+黄色)之间的比例,理想情况下两圆重合,比例为1。
流程:
(1)求出混淆矩阵,假设有150个样本数据,预测为1, 2, 3类各为50个,分类结束后得到的混淆矩阵为:

预测
-- --
-- --
-- --
预测
类1 类2 类3
实际 类1 43 2 0
类2 5 45 1
类3 2 3 49

每一行之和表示该类别的真实样本数量,每一列之和表示被预测为该类别的样本数量。

(2)求解miou,混淆矩阵的每一行再加上每一列,最后减去对角线上的值。

核心代码:

图片说明

8. 异常点检测你有了解吗?

概念:异常点是偏离大部分数据的数据,使人们怀疑这些数据是由不同的机制产生的,而非随机误差。
产生原因:
(1)数据测量收集产生的错误,包括录入错误、仪器故障等。如某次竞赛的计分过程中,出现了某个球队的积分为负分,这就是录入错误;而如某家超市营业额为0,可能为仪器故障。通常这类异常点没有任何价值,一般要在检测出来后进行剔除,或者找到原始数据替换,以免影响到模型的建立和诊断。
(2)数据接收到新的机制或者数据内在特性的骤然改变。这类异常点并不是“错误的”数据,而是由一些特殊的事件或者特殊的操作引发的数据。
(3)数据度量单位的不一致,也会产生异常点。
检测方法:

(1)基于统计的方法

  1. 基于假设检验的方法。首先假设数据集符合某种分布,利用数据集的小概率事件原理,结合假设检验的性质得出异常点。这种方式一般需要数据满足某种分布,有一定局限性。
  2. 基于模型的方法。假设数据满足某种模型,对原始的数据进行建模,利用AIC或者BIC准则。例如数据集满足ARIMA(p, d, q)模型,φ(B)(1-B)^dX_t = λ(B)a_t,其中Ф(B) = 1-φ_1B-…-φ_pB^p,λ(B) = 1-θ_1B-…-θ_qB^q,B为反向推移算子,对数据的残差进行分析,从拟合效果上来看,那些拟合效果较差的点就是异常点。

(2)基于距离的方法

如果数据集合T中至少有p部分与对象O的距离大于D,则对象O为DB(p, D)异常点。该方法主要用于观测值的分布不满足任何已知的标准分布,另外对高维的数据具有好的检测效果,不像基于深度的方法,维数k必须取较小的值。另外基于距离的算法运算量较低,算法简单。

(3)基于密度的方法

基本思想来源于密度聚类。在判断一个对象t是否为异常点时,首先计算对象t的k距离,然后计算对象t的k距离领域,最后给出对象t相对于其领域内的所有对象的可达距离,推导出可达密度LRD,最后算出对象t的局部异常因子LOF。

(4)基于聚类的方法

基于聚类的方法就是将数据集划分为若干组,使得在同一组的数据具有高度的相似性,那么这样得来的不属于任何一类的点就是异常点。基于聚类的算法主要有DBSCAN等算法,主要是利用已知的聚类算法,如划分法、层次法等。这类算法能同时发现类和异常点,但效率一般。

全部评论

相关推荐

点赞 评论 收藏
分享
MScoding:你这个实习有一个是当辅导老师,这个和找技术岗没有关系吧?
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客企业服务