深度学习面试宝典&卷积层(一)

1. 什么是卷积

1.1 卷积定义

在高等数学中,卷积是一个泛函数,具体来说就是通过两个函数f和g生成第三个函数的一种数学运算,其本质是一种特殊的积分变换,表征函数f与g经过翻转和平移的重叠部分函数值乘积对重叠长度的积分。卷积有如下的定义公式:

设 f 和 g 是两个定义在实数域上的函数,它们的卷积 fg 定义为:

其中 t 为实数,τ 是积分变量。

在离散形式下,如果 f 和 g 是两个长度为 n 的向量,它们的卷积 fg 定义为:

其中 k 是整数,[i] 表示向量 f 的第 i 个元素。

例子,f是吃进去的食物,而且还在一直消化,比如十点吃进去,十二点还剩多少,但看f函数是不够的,g函数代表消化了多少。

到一般情况,在x时刻吃进去,t时刻还剩多少

可以设f(t)曲线为我们一天的时间与进食量的关系曲线。g(t)为时间与剩余食物的多少。从图像可以看出,假设要求t时刻胃里的食物残余量。在x时刻吃了一份米饭,那么在t时刻胃里米饭的剩余量为g(t-x),这仅仅是x时刻大米饭的剩余量。对以上进行扩展的话,t时刻之前所有进食的东西到t时刻剩余量我们可以以积分的形式进行表达。也就是∫ 0 t f ( x ) g ( t − x )   d x \int_0^t {f(x)g(t-x)} ,{\rm d}x∫0t​f(x)g(t−x)dx,对以上的解释进行一个总结的话我们就可以得出如下结论:t时刻的食物剩余量与t时刻之前所有时刻的进食量有关,也就是t时刻之前的进食量越多,其剩余量越多。

图像中的卷积运算

有了上面的基础后,我们再来讲讲图像中的卷积运算,例子一个有噪点的原图,可以把它转为一个矩阵:

因为图像有噪点,我想平滑一下图像,图像中的噪点属于高频信号,高频信号,就好像平地耸立的山峰,看起来很显眼。平滑这座山峰的办法之一就是,把山峰刨掉一些土,填到山峰周围去。用数学的话来说,就是把山峰周围的高度平均一下

然后用下面这个平均矩阵来平滑图像。

记得刚才说过的算法,把高频信号与周围的数值平均一下就可以平滑山峰。比如我要对点 a1,1a1,1​ 进行卷积,那么我们从矩阵中取出 a1,1a1,1​ 附近的点组成矩阵 ff,和 gg 进行卷积运算后,再填回去。

要注意一点,ff,虽然和 gg 同维度,但下标有点不一样:

用一个动图来说明下计算过程:

写成卷积公式就是:

(f∗g)(1,1)=∑k=02∑h=02f(h,k)g(1−h,1−k)(.)(f∗g)(1,1)=k=0∑2​h=0∑2​f(h,k)g(1−h,1−k)(.)

从这个例子可以很好的看出来,卷积就是一个运算规则

这样相当于实现了 gg 这个矩阵在原图上的滑动(准确来说,下面这幅图把 gg 这个矩阵旋转了 180°)

注意:卷积核是一个nxn的矩阵,且n是一个奇数,这样卷积核才有一个中心点,卷积核中的每个数字都是一个权重。

1.2 卷积的作用

卷积的作用卷积的作用就是为了将Input的数据做一次特征提取,例如下图有一组衣服的图片:

我们神经网络的目的是为了判断输入目标是上衣还是裤子,或者裙子,那么我们就不需要考虑颜色分量,因为如果只是判断衣服的类型的话可以通过外形判断,那么在输入到全连接层之前可以利用卷积来提取我们想要的数据,过滤掉不需要的数据,这样可以大幅度提升神经网络的准确性以及网络大小,如下图是经过卷积和池化后的一组图片:

可以看到经过卷积和池化后已经将图像的颜色过滤掉了,只保留的轮廓特征,这样的话就过滤掉我们不需要的颜色特征,这样的特征输入到全连接层里会更加符合我们的业务需求。

如果需要判断衣服的品牌那么就需要颜色特征,所以我们就需要对卷积做一次优化,让卷积操作尽可能的去提取到图片里的颜色分量,例如修改卷积核的数量。

卷积的目的就是为了从输入数据中提取我们需要的特征,一般情况下卷积是配合池化层一起使用的,池化层的目的是将卷积提取的特征进行一次筛选,保留最优质的特征,例如如下图片:

从左到右分别是:原图、卷积、池化

卷积的参数卷积核大小(kernel_size)

卷积核的大小是用来控制卷积在提取时的维度,例如下面动图表示当卷积核大小为3x3:

通过上图可以看到卷积在每次提取数据时都以3*3的维度进行提取,一般卷积核的尺寸是根据padding(填充)方式来决定是奇数还是偶数。

填充(padding)

通常情况下当我们在做卷积操作时最常见的卷积核尺寸是:3 x 3、5 * 5、7 * 7,其实这些数是和padding有关,那什么是padding?

padding是卷积在进行操作时对边缘进行填充的一种方式,例如下图:

上图的蓝色部分代表输入数据,维度是4 x 4,淡绿色是卷积后的数据,卷积核大小是3 x 3,可以明显看到卷积后的维度变成了2 x 2导致了卷积维度与原图像维度不一致了,所以我们需要进行一次填充,padding有两种常用的填充方式:

same

same是尺寸不变的一种卷积方式,它会保证输出维度与输入维度一致,但不是百分百保证,它会在输入数据边缘填充数据,这些数据通常为0,即填充空白数据,以便让卷积输出尺寸与输入尺寸一致,它是根据你的参数来决定的,下图会更直观的表示same的原理:

可以看到输入数据的周围出现了白色的方块,这些方块就是same填充的数据,目的是为了保证卷积时输出尺寸与输入尺寸一致,这也意味着边缘特征会被提取多次,保证了特征的准确性。

同时也可以看到same卷积时锚点在中心位置,只有中心位置与数据重叠交汇时才会进行卷积:

2. 深度学习中的卷积层

上面我们讲的卷积核都是手动设计的,手工设计特征面对复杂物体的时候就力不从心了。传统的机器学习都是手工设计特征,甚至发展为特征工程。而深度学习则不一样,卷积核不需要手工设计,而是在网络训练过程中自动更新参数,自动提取特征,这就是深度学习大大超越传统机器学习的重要原因。

为什么需要卷积层,深度学习中的卷积是什么?

在介绍卷积之前,先引入一个场景

假设您在草地上漫步,手里拿着一个尺子,想要测量草地上某些物体的大小,比如一片叶子。但是叶子的形状各异,并且草地非常大,您要用一款尺子轻易地测量出草地上所有叶子的大小。

显然,这很难做到,那么引入卷积的概念

于是,您想到了一种方法,即将叶子放在一个网格纸上,并把网格纸放在尺子上。您现在只需要记录纸张上每个网格的大小,然后用它们来计算叶子的大小。

在深度学习中,直接输入原始数据是可行的,但是如果数据的尺寸很大,比如图像、音频或视频等,那么会遇到以下问题:

  • 参数数量过多:如果直接将大型图像输入神经网络,由于图像的尺寸可能很大,因此网络的参数数量也会相应增加。这将导致更多的计算量和更多的内存需求。而卷积操作可以共享参数,通过卷积核对输入数据进行特征提取,从而减少了需要训练的参数数量,降低了计算量和内存需求。
  • 局部相关性:对于大型图像,其中的信息通常是局部相关的。也就是说,相邻的像素通常具有相似的特征。卷积操作通过滑动窗口对输入数据进行卷积操作,可以有效地捕捉局部相关性,从而提高模型的精度。
  • 平移不变性:图像的很多特征(如边缘、纹理等)在不同位置都是具有相似性质的。卷积操作具有平移不变性,也就是说,无论特征出现在图像的哪个位置,卷积操作都能够将其检测出来。这种平移不变性对于图像识别、物体检测等任务非常重要。

我们以灰度图像为例进行讲解:让一个权重矩阵即卷积核(kernel)逐步在二维输入数据上“扫描”。卷积核“滑动”的同时,计算权重矩阵和扫描所得的数据矩阵的乘积,然后把结果汇总成一个输出像素。使用3x3卷积核,在未使用padding的情况下,卷积之后的图像尺寸为(width-2)*(height-2)。

以下动图展示了in_channels=3、out_channels =2,kernel_size=3、stride=2、padding=1的卷积层运算过程。

【卷积的计算公式】

输入图片的尺寸:一般用nn表示输入的image大小。

卷积核的大小:一般用ff表示卷积核的大小。

填充(Padding):一般用p来表示填充大小。

步长(Stride):一般用s来表示步长大小。输出图片的尺寸:一般用o来表示。

如果已知 n,f,p,s,可以求得o计算公式如下:

其中"⌊ ⌋ \lfloor \rfloor⌊⌋"是向下取整符号,用于结果不是整数时进行向下取整。

输出图片大小为:o×o步长为2,卷积核为3*3,p=0的卷积情况如下:

3.什么是空洞卷积

空洞卷积也叫扩张卷积或者膨胀卷积,简单来说就是在卷积核元素之间加入一些空格(零)来扩大卷积核的过程。

空洞卷积的简单原理。下图是常规卷积和空洞卷积的动图对比:

常规卷积:

空洞卷积:

假设以一个变量a来衡量空洞卷积的扩张系数,则加入空洞之后的实际卷积核尺寸与原始卷积核尺寸之间的关系:K = K + (k-1)(a-1)

其中k为原始卷积核大小,a为卷积扩张率(dilation rate),K为经过扩展后实际卷积核大小。

除此之外,空洞卷积的卷积方式跟常规卷积一样。我们用一个扩展率a来表示卷积核扩张的程度。比如说a=1,2,4的时候卷积核核感受野如下图所示:

在这张图像中,3×3 的红点表示经过卷积后,输出图像是 3×3 像素。尽管所有这三个扩张卷积的输出都是同一尺寸,但模型观察到的感受野有很大的不同。

当a=1,原始卷积核size为3 * 3,就是常规卷积。

a=2时,加入空洞之后的卷积核:size=3+(3-1) * (2-1)=5,对应的感受野可计算为:(2 ^(a+2))-1=7。

a=3时,卷积核size可以变化到3+(3-1)(4-1)=9,感受野则增长到 (2 ^(a+2))-1=15。

4.可分离卷积和深度可分离卷积的计算量有什么区别?

可分离卷积

可分离卷积包括空间可分离卷积(Spatially Separable Convolutions)和深度可分离卷积(depthwise separable convolution)。

假设feature的size为[channel, height , width]

  • 空间也就是指:[height, width]这两维度组成的。
  • 深度也就是指:channel这一维度。

空间可分离卷积

通俗的说,空间可分离卷积就是将nxn的卷积分成1xn和nx1两步计算。

普通的3x3卷积在一个5x5的feature map上的计算方式如下图,每个位置需要9此惩罚,一共9个位置,整个操作要81次做乘法:

同样的状况在空间可分离卷积中的计算方式如下图,第一步先使用3x1的filter,所需计算量为:15x3=45;第二步使用1x3的filter,所需计算量为:9x3=27;总共需要72次乘法就可以得到最终结果,要小于普通卷积的81次乘法。

深度可分离卷积:

它的核心思想是将一个完整的卷积运算分解为两步进行,分别为Depthwise Convolution(逐深度卷积)与Pointwise Convolution(逐点1*1卷积)。

高效的神经网络主要通过:

  • 1. 减少参数数量;
  • 2. 量化参数,减少每个参数占用内存目

首先,我们比较下全卷积和深度可分离卷积:

常规卷积:假设输入层为一个大小为64×64像素、三通道彩色图片。经过一个包含4个Filter的卷积层,最终输出4个Feature Map,且尺寸与输入层相同。我们可以计算出卷积层的参数数量是 4x3x3x3=108,参考下图:

逐深度卷积(滤波):将单个滤波器应用到每一个输入通道。还用上面那个例子,这里的Filter的数量与上一层的Depth相同。所以一个三通道的图像经过运算后生成了3个Feature map,参数数量是 3x3x3=27,参考下图:

逐点卷积(组合):用1*1的卷积组合不同深度卷积的输出,得到一组新的输出。卷积核的尺寸为 1×1×M,M为上一层的depth。这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个Filter就有几个Feature map,计算参数量为 1x1x3x4=12,参考下图:

5.为什么 Depthwise 卷积后还要进行 Pointwise 卷积?

1. Depthwise 卷积

  • 定义:Depthwise 卷积对每个输入通道分别应用一个卷积核,意味着每个通道使用独立的卷积操作。假设输入有 CC 个通道,每个通道的卷积核大小为 K×KK×K,那么 Depthwise 卷积的参数数量为 C×K2C×K2。
  • 优点:减少参数数量:相比于标准卷积(每个输出通道与所有输入通道相连),Depthwise 卷积显著减少了参数数量和计算量。提高计算效率:由于只对每个通道进行卷积,计算速度更快,适合在移动设备等资源受限的环境中使用。

2. Pointwise 卷积

  • 定义:Pointwise 卷积是一个 1×11×1 的卷积,主要用于将 Depthwise 卷积的输出进行线性组合。它对每个像素的所有通道进行加权和,从而生成新的输出通道。
  • 优点:通道混合:Pointwise 卷积允许模型在不同通道之间学习相互关系和特征组合。它可以有效地捕捉通道间的交互信息。增加输出通道:通过 Pointwise 卷积,模型可以生成更多的输出通道,从而增加网络的表达能力。

3. Depthwise 和 Pointwise 的结合

  • 整体效果:结合 Depthwise 和 Pointwise 卷积形成深度可分卷积,能够在保持较低计算复杂度的同时,增加模型的表达能力。Depthwise 卷积负责提取每个通道的空间特征,而 Pointwise 卷积则负责通道间的特征融合。

4. 总结

在 Depthwise 卷积后进行 Pointwise 卷积的主要原因是为了:

  • 减少参数和计算量:Depthwise 卷积显著降低了参数数量。
  • 增强特征表达能力:Pointwise 卷积可以有效地组合不同通道的特征,从而提升模型的性能。

6.什么是分组卷积

1.1 分组卷积与普通卷积的区别

        Group convolution是将输入层的不同特征图进行分组,然后采用不同的卷积核再对各个组进行卷积,这样会降低卷积的计算量。因为一般的卷积都是在所有的输入特征图上做卷积,可以说是全通道卷积,这是一种通道密集连接方式(channel dense connection)。而group convolution相比则是一种通道稀疏连接方式(channel sparse connection)。

普通卷积(左)和分组卷积(右)

1)常规卷积(Convolution)

        如果输入feature map尺寸为𝐶∗𝐻∗𝑊,卷积核有𝑁个,输出feature map与卷积核的数量相同也是𝑁,每个卷积核的尺寸为𝐶∗𝐾∗𝐾,𝑁个卷积核的总参数量为𝑁∗𝐶∗𝐾∗𝐾,输入map与输出map的连接方式如上图左所示。

2)分组卷积(Group Convolution)

        Group Convolution是对输入feature map进行分组,然后每组分别卷积。假设输入feature map的尺寸仍为𝐶∗𝐻∗𝑊,输出feature map的数量为𝑁个,如果设定要分成𝐺个groups,则每组的输入feature map数量为𝐶 / 𝐺,每组的输出feature map数量为𝑁 / 𝐺,每个卷积核的尺寸为𝐶/𝐺∗𝐾∗𝐾,卷积核的总数仍为𝑁个,每组的卷积核数量为𝑁 / 𝐺,卷积核只与其同组的输入map进行卷积,卷积核的总参数量为𝑁∗𝐶 / 𝐺∗𝐾∗𝐾,可见,总参数量减少为原来的 1 / 𝐺,其连接方式如上图右所示,group1输出map数为2,有2个卷积核,每个卷积核的channel数为4,与group1的输入map的channel数相同,卷积核只与同组的输入map卷积,而不与其他组的输入map卷积。

1.2 分组卷积的用途

  • 减少参数量,分成𝐺组,则该层的参数量减少为原来的1 / 𝐺
  • Group Convolution可以看成是structured sparse,每个卷积核的尺寸由𝐶∗𝐾∗𝐾变为𝐶/𝐺∗𝐾∗𝐾,可以将其余(𝐶−𝐶 / 𝐺)∗𝐾∗𝐾的参数视为0,有时甚至可以在减少参数量的同时获得更好的效果(相当于正则)。
  • 当分组数量等于输入map数量,输出map数量也等于输入map数量,即𝐺=𝑁=𝐶,𝑁个卷积核每个尺寸为1∗𝐾∗𝐾时,Group Convolution就成了Depthwise Convolution,参见MobileNetXception等,参数量进一步缩减

1.3 分组卷积的注意点

分组卷积使用后,最好将各组卷积的结果做一个shuffle打乱,让不同组卷积的特征能够通信。

7. 反卷积是怎么实现的?

反卷积是一种特殊的正向卷积,先按照一定的比例通过补 0 来扩大输入图像的尺寸,接着旋转卷积核,再进行正向卷积。

图1 反卷积原理图(stride=1)

2.数学推导假设输入图像 input 尺寸为 4×4 ,元素矩阵为:

卷积核 kernel 尺寸为 3×3 ,元素矩阵为:

步长 strides=1,填充 padding=0,即 i=4,k=3,s=1,p=0,按照卷积公式, ,输出图像 output 的尺寸为 2×2。

把 input 的元素矩阵展开成一个列向量 X:

把输出图像 output 的元素矩阵展开成一个列向量 Y:

对于输入的元素矩阵 X 和 输出的元素矩阵 Y ,用矩阵运算描述这个过程:Y=CX

通过推导,我们可以得到稀疏矩阵 C :

用C的第一行和input相乘,得到y1; 第二行和input相乘,得到y2......

反卷积的操作就是要对这个矩阵运算过程进行逆运算,即 X=CTY

下面这个图直观地体现出整个过程:

将输入input 平展成 16∗1 矩阵,将卷积核转换为一个 4x16 的稀疏矩阵。然后,进行矩阵乘法。将所得到的 4x1 矩阵reshape为 2x2 输出。

此时,若用卷积核对应稀疏矩阵的转置CT𝐶𝑇(16∗4)乘以输出的平展(4∗1),得到的结果(16∗1)的形状和输入的形状(16∗1)相同。

注:上述两次操作并不是可逆关系,对于同一个卷积核,转置卷积的操作并不能恢复到原始的数值,而仅仅保留原始的形状。

如上图所示,我们用一个3×3的卷积核卷积一个4×4的输入,得到一个2×2的输出后,再进行反卷积,发现并不是原来的输入。其实很简单,第一个数相当于9个数相加等于4.5,用一个方程怎么可能解出9个未知数。所以反卷积不能还原原来的输入,只能保证shape相同。

1.卷积的输出图像大小

以上图为例,设input=6,kernel=3,stride=2,padding=1,则output=[(6+2-3)/2+1]=[3.5]=3

2.反卷积的输出图像大小

注:此公式中的padding是外层填充0的层数。

反卷积中,stride就是在相邻元素之间添加stride-1个0元素。 变换后,实际的input大小

8. 卷积的底层实现/加速技巧是什么?

卷积操作的底层实现主要涉及到两个方面,一是使用高效的卷积算法,二是利用硬件加速技术。

在卷积算法方面,常用的优化技术包括:

  • 基于FFT的卷积算法:将卷积转化为点乘操作,利用FFT算法高效计算,适用于大尺寸卷积核和大尺寸输入。
  • 基于im2col的卷积算法:将输入图像转换为一个矩阵,再通过矩阵乘法实现卷积,适用于小尺寸卷积核和小尺寸输入。
  • Winograd算法:将卷积转换为矩阵乘法的形式,并利用特殊的变换矩阵进行计算,适用于小尺寸卷积核和小尺寸输入。

在硬件加速方面,常用的技术包括:

  • GPU加速:利用图形处理器的并行计算能力,可以大大提升卷积计算的速度。
  • ASIC加速:专门设计的应用特定集成电路(ASIC)可以高效地执行卷积操作,性能和功耗都优于通用处理器。
  • FPGA加速:可编程逻辑门阵列(FPGA)可以通过编写硬件描述语言来定制化实现卷积计算,具有灵活性和高性能的优点

9. 1x1卷积有什么作用?

一、通道维度的降维/升维

这是1x1卷积最显著的作用之一。通过应用具有较少输出通道的1x1卷积层,可以有效减少网络中的参数数量,从而降低模型的复杂度并帮助防止过拟合。相反,如果使用具有更多输出通道的1x1卷积,则可以增加特征图的深度,为网络引入更多的非线性,增强其表达能力。

二、计算通道间的组合权重

1x1卷积,虽然没有空间维度上的滑动(因为核大小为1x1),但它依然在通道维度上操作。想象一下,如果你的输入特征图有C个通道,那么一个1x1卷积层中的每个卷积核也将有C个权重,对应于输入的每一个通道。这些权重可以被视为对输入通道的一种加权组合方式。有助于捕捉更高级别的特征交互。

10.什么是卷积神经网络中的局部感受野?

3. 阐述一下感受野的概念。

一、感受野定义

在卷积神经网络中,感受野(Receptive Field)的定义是卷积神经网络每一层输出的特征图(feature map)上每个像素点在原始图像上映射的区域大小,这里的原始图像是指网络的输入图像,是经过预处理后的图像。

 从上图可看到输出中的每个元素都由(3,3)的卷积核对应输入中(3,3)的局部区域“加权求和”得到,所以该输出的特征元素对应到输入中的区域大小就是3×3,既其感受野大小为3×3。

 神经元感受野的值越大表示其能接触到的原始图像范围就越大,也意味着它可能蕴含更为全局,语义层次更高的特征;相反,值越小则表示其所包含的特征越趋向局部和细节。因此感受野的值可以用来大致判断每一层的抽象层次。

二、感受野大小的计算

很明显,深层卷积层的感受野大小和它之前所有层的滤波器大小和步长有关系,而涉及到这两个参数的有卷积层和pooling层。所以是个迭代公式。

其中 为第 层的感受野大小为第 层的的感受野大小为第L层的卷积核大小(也可以是Pooling), Si为第 i 层的卷积步长。一般来说

三、局部感受野

在卷积神经网络(CNN)中,局部感受野(Local Receptive Field)是指每个神经元(或特征图中的某个位置)在输入数据中所连接的一个小区域。具体来说,它描述了某个神经元在输入图像中所“看到”的部分。

主要特点

  1. 连接范围:每个神经元只与输入图像的一个小块相连接,这个小块的大小由卷积核(滤波器)的尺寸决定。例如,使用 3×33×3 的卷积核时,局部感受野的大小就是 3×33×3。
  2. 局部特征提取:局部感受野使得网络能够专注于局部特征,如边缘、纹理和其他细节。这种局部连接的方式有助于捕捉图像中的重要信息。
  3. 参数共享:在卷积层中,局部感受野的概念与参数共享结合使用。多个神经元共享相同的卷积核,这样可以减少模型的参数数量,并提高学习效率。

#面经##深度学习##24届软开秋招面试经验大赏##机械制造笔面经##晒一晒我的offer#
全部评论

相关推荐

头像
09-24 11:30
已编辑
山东大学 FPGA工程师
#通信硬件人笔面经互助#2024-09-08 (一面,周日!周日!周日)2024-09-09  (二面,周一,晚八点半左右)🤪 背景本科双非/硕士985硕本人的经历是(研电赛技术赛国二+企业赛第二名、研数模赛国三(本科国一),均团队主战+队长,两项发明专利+一软著),无实习经历,4段项目经历偏FPGA数字信号处理+高速接口。🤔 面试感受二面主管(叫主任更贴切一些)HR微信视频面30+分钟(沃日,这是我见过最快的流程了,前一天晚上一面,第二天就二面,我收回我上一条的吐槽)。总体而言,面试感觉还可以,没有问技术相关的,问的家庭情况,来上海的意愿,找工作的安排,介绍他们的福利待遇等等。首先,自我介绍(大概三四分钟吧),介绍完了以后,他先问我对工作地和工作性质的看法,我说我找工作对地点没有特别要求,工作找到哪里就去哪里,然后他跟我分享了一下他对此的看法,感觉他说的很有道理,回去仔细的思考一下!!!然后他问我对上海买房有什么看法(我心想,上海!买房!这是我这种土狗能想的吗,我能有什么看法,我想都不敢想好吧,但哥们不能这么说啊)我说,我还没毕业,没有想过买房的事情,他和我说他们上海买房没我想的拿吗恐怖,两个人一起努力父母再帮衬一下还是可以的,而且他们单位的薪资福利绝对能支持我在上海安家落户的(有点画大饼PUA的味道了哈,没全信),然后有陆陆续续谈了很多,比如薪资福利待遇、解决户口、给编制之类的,最后我说跟父母回去商量一下,然后就结束了。
点赞 评论 收藏
分享
3 1 评论
分享
牛客网
牛客企业服务