深度学习之卷积神经网络(11)卷积层变种

  • 1. 空洞卷积
  • 2. 转置卷积
    • 矩阵角度
    • 转置卷积实现
  • 3. 分离卷积

卷积神经网络的研究产生了各种各样优秀的网络模型,还提出了各种卷积层的变种,本节将重点介绍书中典型的卷积层变种。

1. 空洞卷积

 普通的卷积层为了减少为了的参数量,卷积核的设计通常选择较小的1×11×11×1和3×33×33×3感受野大小。小卷积核使得网络提取特征时的感受野区域有限,但是增大感受野区域又会增加网络的参数量和计算代价,因此需要权衡设计。

空洞卷积Dilated/Atrous Convolution)的提出较好地解决了这个问题,空洞卷积在普通卷积的感受野上增加一个Dilation Rate参数,用于控制感受野区域的采样步长,如下图所示:

感受野采样步长示意图

当感受野的采样步长Dilation Rate为1时,每个感受野采样点之间的距离为1,此时的空洞卷积退化为普通的卷积; 当Dilation Rate为2时,感受野每2个单元采样一个点,如上图中间绿色方框中绿色格子所示,每个采样格子之间的距离为2; 同样的方法,最右边图的Dilation Rate为3,采样步长为3,尽管Dilation Rate的增大会使得感受野区域增大,但是实际参与运算的点数仍然保持不变。

 以输入为单通道的7×77×77×7张量,单个3×33×33×3卷积核为例,如下图所示。在初始位置,感受野从最上、最右位置开始采样,每隔一个点采样一次,共采集9个数据点,如下图中绿色方框所示。这9个数据点与卷积核相乘运算,写入输出张量的对应位置。

空洞卷积计算示意图-1

 卷积核窗口按着步长为s=1s=1s=1向右移动一个单位,如下图所示,同样进行个点采样,共采样9个数据点,与卷积核完成相乘累加运算,写入输出张量对应为,直至卷积核移动至最下方、最右边位置。需要注意区分的是,卷积核窗口的移动步长s和感受野区域的采样步长Dilation Rate是不同的概念。

空洞卷积计算示意图-2

 空洞卷积在不增加网络参数的条件下,提供了更大的感受野窗口。但是在使用空洞卷积设置网络模型时,需要精心设计Dilation Rate参数来避免出现网格效应,同样较大的Dilation Rate参数并不利于小物体的检测、语义分割等任务。

 在TensorFlow中,可以通过设置layers.Conv2D()类的dilation_rate参数来选择使用普通卷积还是空洞卷积。例如:

import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequentialx = tf.random.normal([1,7,7,1])  # 模拟输入
# 空洞卷积,1个3×3的卷积核
layer = layers.Conv2D(1, kernel_size=3, strides=1, dilation_rate=2)
out = layer(x)  # 向前计算
print(out.shape)

运行结果如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/6e3ecf2d1eff4b71acef8870dfcb42c5.png#pic_center)

当dilation_rate参数设置为默认值1时,使用普通卷积方式进行计算; 当dilation_rate参数大于1时,采用空洞卷积方式进行计算。

2. 转置卷积

 转置卷积(Transposed Convolution,或Fractionally Strided Convolution,部分资料也称之为反卷积/Deconvolution,实际上反卷积在数学上定义为卷积的逆过程,单转置卷积并不能恢复出原卷积的输入,因此称为反卷积并不妥当)通过在输入之间填充大量的padding来实现输出高宽大于输入高宽的效果,从而实现向上采样的目的,如下图所示。我们先介绍转置卷积的计算过程,再介绍转置卷积与普通卷积的联系。

 为了简化讨论,我们此处只讨论输入h=wh=wh=w,即输入高宽相等的情况。

转置卷积实现向上采样

o+2p−k\boldsymbol {o+2p-k}o+2p−ks\boldsymbol ss倍数

 考虑输入为2×22×22×2的单通道特征图,转置卷积核为3×33×33×3大小,步长为s=2s=2s=2,填充p=0p=0p=0的例子。首先再输入数据点之间均匀插入s−1s-1s−1个空白数据点,得到3×33×33×3的矩阵,如下图第2个矩阵所示,根据填充量3×33×33×3矩阵周围填充相应k−p−1=3−0−1=2k-p-1=3-0-1=2k−p−1=3−0−1=2行/列,此时输入张量的高宽为7×77×77×7,如下图中第3个矩阵所示。

输入填充步骤

 在7×77×77×7的输入张量上,进行3×33×33×3卷积核,步长s′=1s'=1s′=1,填充p=0p=0p=0的普通卷积运算(注意,此阶段的普通卷积的步长s′s's′始终为1,与转置卷积的步长sss不同),根据普通卷积的输出计算公式,得到输出大小为:
o=[i+2∗p−ks′]+1=[7+2∗0−31]+1=5o=[\frac{i+2*p-k}{s'} ]+1=[\frac{7+2*0-3}{1}]+1=5o=[s′i+2∗p−k​]+1=[17+2∗0−3​]+1=5
5×55×55×5大小的输出。我们直接按照此计算流程给出最终转置卷积输出与输入关系。在o+2p−ko+2p-ko+2p−k为sss倍数时,满足关系:
o=(i−1)s+k−2po=(i-1)s+k-2po=(i−1)s+k−2p
 转置卷积并不是普通的逆过程,但是二者之间有一定的联系,同时转置卷积也是基于普通卷积实现的。在相同的设定下,输入x\boldsymbol xx经过普通卷积运算得到o=Conv(x)\boldsymbol o=\text{Conv}(\boldsymbol x)o=Conv(x),我们将o送入转置卷积运算后,得到x′=ConvTranspose(o)\boldsymbol x'=\text{ConvTranspose}(\boldsymbol o)x′=ConvTranspose(o),其中x′≠x\boldsymbol x'≠\boldsymbol xx′​=x,但是x′\boldsymbol x'x′与x\boldsymbol xx形状相同。我们可以用输入为5×55×55×5,步长s=2s=2s=2,填充p=0p=0p=0,3×33×33×3卷积核的普通卷积运算进行验证演示,如下图所示:

利用普通卷积恢复等大小输入

 可以看到,将转置卷积的输出5×55×55×5在同设定条件下送入普通卷积,可以得到2×22×22×2的输出,此大小恰好就是转置卷积的输入大小,同时我们也观察到,输出2×22×22×2矩阵并不是转置卷积输入的2×22×22×2矩阵。转置卷积与普通卷积并不是互为逆过程,不能恢复出对方的输入内容,仅能恢复出等大小的张量。因此称之为反卷积并不切贴。

&emspl;基于TensorFlow实现上述例子的转置卷积运算,代码如下:

import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential# 创建X矩阵,高宽为5×5
x = tf.range(25)+1
# Reshape为合法维度的张量
x = tf.reshape(x, [1, 5, 5, 1])
x = tf.cast(x, tf.float32)
# 创建固定内容的卷积核矩阵
w = tf.constant([[-1, 2, -3.], [4, -5, 6], [-7, 8, -9]])
# 调整为合法维度的张量
w = tf.expand_dims(w, axis=2)
w = tf.expand_dims(w, axis=3)
# 进行普通卷积运算
out = tf.nn.conv2d(x, w, strides=2, padding='VALID')
print(out)

运行结果如下图所示:

 现在我们将普通卷积的输出作为转置卷积的输入,验证转置卷积的输出是否为5×55×55×5,代码如下:

# 普通卷积的输出作为转置卷积的输入,进行转置卷积运算
xx = tf.nn.conv2d_transpose(out, w, strides=2,padding='VALID',output_shape=[1, 5, 5, 1])
# 输出的高宽为5×5
print(xx)

运行结果如下:

tf.Tensor(
[[[[   67.][ -134.][  278.][ -154.][  231.]][[ -268.][  335.][ -710.][  385.][ -462.]][[  586.][ -770.][ 1620.][ -870.][ 1074.]][[ -468.][  585.][-1210.][  635.][ -762.]][[  819.][ -936.][ 1942.][-1016.][ 1143.]]]], shape=(1, 5, 5, 1), dtype=float32)

o+2p−k\boldsymbol{o+2p-k}o+2p−k不为s\boldsymbol ss倍数

 让我们更加深入地分析卷积运算中输入与输出大小关系的一个细节。考虑卷积运算的输出表达式:
o=[i+2∗p−ks′]+1o=[\frac{i+2*p-k}{s'} ]+1o=[s′i+2∗p−k​]+1
当步长s>1s>1s>1时,[i+2∗p−ks′][\frac{i+2*p-k}{s'} ][s′i+2∗p−k​]向下取整运算使得出现多种不同输入尺寸iii对应到相同的输出尺寸ooo上。举个例子,考虑输入大小为6×66×66×6,卷积核大小为3×33×33×3,步长为1的卷积运算,代码如下:

import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential# 创建X矩阵,高宽为5×5
x = tf.random.normal([1, 6, 6, 1])
x = tf.cast(x, tf.float32)
# 创建固定内容的卷积核矩阵
w = tf.constant([[-1, 2, -3.], [4, -5, 6], [-7, 8, -9]])
# 调整为合法维度的张量
w = tf.expand_dims(w, axis=2)
w = tf.expand_dims(w, axis=3)
# 进行普通卷积运算
out = tf.nn.conv2d(x, w, strides=2, padding='VALID')
print(out)
print(out.shape)

运行结果如下图所示:

此种情况也能获得2×22×22×2大小的卷积输出,与利用普通卷积可以获得相同大小的输出。因此,不同输入大小的卷积运算可能获得相同大小的输出。考虑到卷积与专制卷积输入输出大小关系互换,从转置卷积的角度来说,输入尺寸iii经过转置卷积运算后,可能获得不同的输出ooo大小。因此通过填充aaa行、aaa列来实现不同大小的输出ooo,从而恢复普通卷积不同大小的输入的情况,其中aaa关系为:
a=(o+2p−k)a=(o+2p-k)%sa=(o+2p−k)
此时转置卷积的输出变为:
o=(i−1)s+k−2p+ao=(i-1)s+k-2p+ao=(i−1)s+k−2p+a
 在TensorFlow中间,不需要手动指定aaa参数,只需要指定输出尺寸即可,TensorFlow会自动推导需要填充的行列数aaa,前提是输出尺寸合法。例如:

# 恢复出6×6大小
out = tf.nn.conv2d(x, w, strides=2, padding='VALID')
print(out)
print(out.shape)# 普通卷积的输出作为转置卷积的输入,进行转置卷积运算
xx = tf.nn.conv2d_transpose(out, w, strides=2,padding='VALID',output_shape=[1, 6, 6, 1])
# 输出的高宽为5×5
print(xx)

运行结果如下所示:

tf.Tensor(
[[[[  -8.0665455][  16.133091 ][  -4.7349663][ -38.92934  ][  58.394012 ][   0.       ]][[  32.266182 ][ -40.332726 ][ -29.459408 ][  97.32335  ][-116.788025 ][   0.       ]][[ -59.992893 ][  71.58651  ][  38.390343 ][-126.35293  ][ 131.13538  ][   0.       ]][[  14.108292 ][ -17.635365 ][  79.89131  ][ -73.41109  ][  88.09331  ][   0.       ]][[ -24.68951  ][  28.216583 ][-134.51918  ][ 117.45774  ][-132.13995  ][   0.       ]][[   0.       ][   0.       ][   0.       ][   0.       ][   0.       ][   0.       ]]]], shape=(1, 6, 6, 1), dtype=float32)

通过改变参数output_shape=[1, 5, 5, 1]也可以获得高宽为5×5的张量。

矩阵角度

 转置卷积的转置是指卷积核W\boldsymbol WW产生的稀疏矩阵W′\boldsymbol W'W′在计算过程中需要先转置W′T\boldsymbol W'^TW′T,再进行矩阵相乘运算,而普通卷积并没有转置W′\boldsymbol W'W′的步骤。这也是它被称为转置卷积的名字的由来。

 考虑普通Conv2d运算: X\boldsymbol XX和W\boldsymbol WW,需要根据strides将卷积核在行、列方向循环移动获取参与运算的感受野的数据,串行计算每个窗口的“相乘累加”值,计算效率极地。为了加速运算,在数学上可以将卷积核W\boldsymbol WW根据strides重排成稀疏矩阵W′\boldsymbol W'W′,再通过W′@X′\boldsymbol W'@\boldsymbol X'W′@X′一次完成运算(实际上,W′\boldsymbol W'W′矩阵过于稀疏,导致很多无用的0乘运算,很多深度学习框架也不是通过这种方式实现的)。

 以4行4列的输入X\boldsymbol XX,高宽为3×3,步长为1,无padding的卷积核W\boldsymbol WW的卷积运算为例,首先将X\boldsymbol XX打平成X′\boldsymbol X'X′,如下图所示:

转置卷积X'

然后将卷积核W\boldsymbol WW转换成稀疏矩阵W′\boldsymbol W'W′,如下图所示:

转置卷积W'

 此时通过一次矩阵相乘即可实现普通卷积运算:
O′=W′@X′\boldsymbol O'=\boldsymbol W'@\boldsymbol X'O′=W′@X′
如果给定O\boldsymbol OO,希望能够生成与X\boldsymbol XX相同形状大小的张量,怎么实现呢?将W′\boldsymbol W'W′转置后与上图方法重排后的O′\boldsymbol O'O′完成矩阵相乘即可:
X′=W′T@O′\boldsymbol X'=\boldsymbol W'^T@\boldsymbol O'X′=W′T@O′
得到的X′\boldsymbol X'X′通过reshape操作变为与原来的输入X\boldsymbol XX尺寸一致,但是内容不同。例如O′\boldsymbol O'O′的shape为[4,1][4,1][4,1],W′T\boldsymbol W'^TW′T的shape为[16,4][16,4][16,4],Reshape后即可产生[4,4][4,4][4,4]形状的张量。由于转置卷积在矩阵运算时,需要将W′\boldsymbol W'W′转置后才能与转置卷积的输入O′\boldsymbol O'O′矩阵相乘,故称为转置卷积。

 转置卷积具有“放大特征图”的功能,在生成对抗网络、语义分割等中得到了广泛应用,如DCGAN[1]中的生成器通过堆叠转置卷积层实现逐层“放大”特征图,最后获得十分逼真的生成图片。

DCGAN生成器网络结构

[1] A. Radford, L. Metz 和 S. Chintala, Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks, 2015.

转置卷积实现

 在TensorFlow中,可以通过nn.conv2d_transpose实现转置卷积运算。我们先通过nn.conv2d完成普通卷积运算。注意转置卷积的卷积核的定义格式为[k,k,cout,cin][k,k,c_{out},c_{in}][k,k,cout​,cin​]。例如:

import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential# 创建4×4大小的输入
x = tf.range(16)+1
x = tf.reshape(x, [1, 4, 4, 1])
x = tf.cast(x, tf.float32)
# 创建3×3卷积核
w = tf.constant([[-1, 2, -3.], [4, -5, 6], [-7, 8, -9]])
w = tf.expand_dims(w, axis=2)
w = tf.expand_dims(w, axis=3)
# 普通卷积运算
out = tf.nn.conv2d(x, w, strides=1, padding='VALID')
print(out)

运行结果如下图所示:

 保持strides=1,padding=‘VALID’,卷积核不变的情况下,我们通过卷积核www与输出outoutout的转置卷积运算尝试恢复与输入xxx相同大小的高宽张量,代码如下:

# 恢复4×4大小的输入
xx = tf.nn.conv2d_transpose(out, w, strides=1,padding='VALID',output_shape=[1, 4, 4, 1])
tf.squeeze(xx)
print(xx)

运行结果如下所示:

tf.Tensor(
[[[[  56.][ -51.][  46.][ 183.]][[-148.][ -35.][  35.][-123.]][[  88.][  35.][ -35.][  63.]][[ 532.][ -41.][  36.][ 729.]]]], shape=(1, 4, 4, 1), dtype=float32)

可以看到,转置卷积生成了4×44×44×4的特征图,单特征图的数据与输入xxx并不相同。

 在使用tf.nn.conv2d_transpose进行转置卷积运算时,需要额外手动设置输出的高宽。tf.nn.conv2d_transpose并不支持自定义padding设置,只能设置为VALID或者SAME。

 当设置padding=‘VALID’时,输出大小表达为:
o=(i−1)s+ko=(i-1)s+ko=(i−1)s+k

 当设置padding=‘SAME’时,输出大小表达为:
o=i⋅so=i\cdot so=i⋅s
 如果我们还是对转置卷积原理细节暂时无法理解,可以先牢记上述两个表达式即可。例如:

2×22×22×2的转置卷积输入与3×33×33×3的卷积核运算,strides=1,padding=‘VALID’时,输出大小为:
h′=w′=(2−1)⋅1+3=4h'=w'=(2-1)\cdot1+3=4h′=w′=(2−1)⋅1+3=4

2×22×22×2的转置卷积输入与3×33×33×3的卷积核运算,strides=1,padding=‘VALID’时,输出大小为:
h′=w′=2⋅3=6h'=w'=2\cdot3=6h′=w′=2⋅3=6
 转置卷积也可以和其他层一样,通过layers.Conv2DTranspose类创建一个转置卷积层,然后调用实例即可完成向前计算。代码如下:

# 创建转置卷积类
layer = layers.Conv2DTranspose(1, kernel_size=3, strides=1, padding='VALID')
xx2 = layer(out)
print(xx2)

运行结果如下:

tf.Tensor(
[[[[  6.942313 ][ 33.887856 ][ 24.800087 ][ -4.222195 ]][[ 21.720724 ][ 68.23484  ][ 73.74913  ][ 28.219326 ]][[ 15.284215 ][ 10.42746  ][ 12.951116 ][ 20.34887  ]][[ -1.9099097][-26.6492   ][-56.841545 ][-32.62231  ]]]], shape=(1, 4, 4, 1), dtype=float32)

3. 分离卷积

 这里以深度可分离卷积Depth-wise Separable Convolution)为例。普通卷积在对多通道输入进行运算时,卷积核的每个通道与输入的每个通道分别进行卷积运算,得到多通道的特征图,再对应元素相加产生单个卷积核的最终输出,如下图所示:

普通卷积计算示意图

 分离卷积的计算流程则不同,卷积核的每个通道与输入的每个通道进行卷积预算,得到多个通道的中间特征,如下图所示。这个多通道的中间特征张量接下来进行多个1×11×11×1卷积核的普通卷积运算,得到多个高宽不变的输出,这些输出在通道轴上面进行拼接,从而产生最终的分离卷积层的输出。可以看到,分离卷积层包含了两步卷积运算,第一步卷积运算是单个卷积核,第二个卷积运算包含了多个卷积核。

深度可分离卷积计算示意图

 那么采用分离卷积有什么优势呢?一个很明显的优势在于,同样的输入和输出,采用Separable Convolution的参数量约是普通卷积的13\frac{1}{3}31​。考虑上图中的普通卷积和分离卷积的例子。普通卷积的参数量是
3⋅3⋅3⋅4=1083\cdot3\cdot3\cdot4=1083⋅3⋅3⋅4=108
分离卷积的第一部分参数量是
3⋅3⋅3⋅1=273\cdot3\cdot3\cdot1=273⋅3⋅3⋅1=27
第二部分参数量是
1⋅1⋅3⋅4=121\cdot1\cdot3\cdot4=121⋅1⋅3⋅4=12
分离卷积的总参数量只有39,但是却能实现普通卷积同样的输入输出尺寸变换。分离卷积在Xception和MobileNets等对计算代价敏感的领域中得到了大量应用。

深度学习之卷积神经网络(11)卷积层变种相关推荐

  1. 神经网络的输出层有哪些_深度学习的数学-神经网络、输入层、隐藏层、输出层...

    前言 前文中了解到,神经网络由多个神经单元组成,而本篇博客将会了解到深度学习由多个神经网络组成,并且分为 输入层.隐藏层和输出层,隐藏层涉及到的知识点最多,是本文的重点 正文 阶层型的神经网络主要结构 ...

  2. 【深度学习】深入浅出神经网络框架的模型元件(池化、正则化和反卷积层)

    [深度学习]深入浅出神经网络框架的模型元件(池化.正则化和反卷积层) 文章目录 1 池化层 1.1 MaxPooling2D 1.2 MaxPooling1D: 1.3 AveragePooling2 ...

  3. 【深度学习】深入浅出神经网络框架的模型元件(常用层和卷积层)

    [深度学习]深入浅出神经网络框架的模型元件(常用层和卷积层) 文章目录 1 常用层1.1 Dense1.2 Activation层1.3 Dropout1.4 Flatten 2 卷积层2.1 Cov ...

  4. 花书+吴恩达深度学习(十二)卷积神经网络 CNN 之全连接层

    目录 0. 前言 1. 全连接层(fully connected layer) 如果这篇文章对你有一点小小的帮助,请给个关注,点个赞喔~我会非常开心的~ 花书+吴恩达深度学习(十)卷积神经网络 CNN ...

  5. 卷积神经网络之卷积计算、作用与思想 深度学习

    博客:blog.shinelee.me | 博客园 | CSDN 卷积运算与相关运算 在计算机视觉领域,卷积核.滤波器通常为较小尺寸的矩阵,比如3×33×3.从这个角度看,多层卷积是在进行逐层映射,整 ...

  6. 深度学习笔记 第四门课 卷积神经网络 第三周 目标检测

    本文是吴恩达老师的深度学习课程[1]笔记部分. 作者:黄海广[2] 主要编写人员:黄海广.林兴木(第四所有底稿,第五课第一二周,第三周前三节).祝彦森:(第三课所有底稿).贺志尧(第五课第三周底稿). ...

  7. 深度学习笔记 第四门课 卷积神经网络 第四周 特殊应用:人脸识别和神经风格转换...

    本文是吴恩达老师的深度学习课程[1]笔记部分. 作者:黄海广[2] 主要编写人员:黄海广.林兴木(第四所有底稿,第五课第一二周,第三周前三节).祝彦森:(第三课所有底稿).贺志尧(第五课第三周底稿). ...

  8. 卷积神经网络精确率不增反降_深度学习 第四门课:卷积神经网络(Convolutional Neural Networks)...

    "本文大约有 4864 字. 01|引言 在这两周时间里,我主要的学习内容如下: 重新复习了一遍前三门课: 学完第四门课卷积神经网络(ConvolutionalNeural Networks ...

  9. 深度学习中常见卷积(普通卷积、1×1卷积、转置卷积、可分离卷积、膨胀(空洞)卷积、3D卷积)

      总是在网络上看到各种名词的卷积,但是有搞不懂是什么含义,于是结合网上查阅的资料,总结一下.目前比较常用的卷积主要有常规的卷积.1×1卷积.转置卷积.可分离卷积.膨胀卷积.3D卷积.   以下是一些 ...

  10. 【动手学深度学习PyTorch版】12 卷积层

    上一篇移步[动手学深度学习PyTorch版]11 使用GPU_水w的博客-CSDN博客 目录 一.卷积层 1.1从全连接到卷积 ◼ 回顾单隐藏层MLP ◼ Waldo在哪里? ◼ 原则1-平移不变性 ...

最新文章

  1. 什么时候用转发什么时候用重定向_验孕棒什么时候用最准确
  2. python简单入门
  3. java 单元测试技巧_其他一些单元测试技巧
  4. python3.8文档_python 3.8的新功能
  5. 7-26 单词长度 (15 分)
  6. [转]Kaldi语音识别
  7. spring相关—IOC容器—使用注解配置bean
  8. 小孩桌面便签隐藏了怎么恢复?
  9. OpenSees二次开发实例01
  10. 深夜给这个世界添加一点佐料
  11. 黑客黑手伸向微博微信手机成网络钓鱼主要渠道
  12. 第26课时,实践4,定期存款利息计算器
  13. python读书心得体会范文_个人读书心得体会范文五篇
  14. 【JAVA长虹键法】第八式 代理模式(23种设计模式)
  15. Field userDao in com.sd.sbmb.service.impl.UserServiceImpl required a bean of type ‘com.sd.sbmb.dao.U
  16. Java 高并发编程详解 17.0 Active Object 模式
  17. 保研夏令营-南大、哈工、天大、中山、北理
  18. Android11 WMS 之 AppTransition
  19. Mansory makeConstraints 、remakeConstraints 、updateConstraints 注意事项
  20. 拓嘉辰丰:拼多多网店被投诉后影响有多大

热门文章

  1. Android开发之Android WIFI ADB(ADB WIFI)无线调试的技巧
  2. Android开发之6.0动态权限工具类(高德地图官方扣出来的)附源码
  3. html hover 效果,CSS八种让人眼前一亮的HOVER效果的示例代码
  4. 组件化开发实战_一篇文章搞懂什么是前端“组件化”开发
  5. android毛玻璃遮罩效果_css3毛玻璃效果[模糊图片]
  6. koa2入门(3)mongoose 增删改查
  7. Web Worker的最好文章
  8. Python为什么这么厉害? 不想成为专业码农? 来学习Python吧!
  9. c language compile process.
  10. 网段:192.168.6.0的机器A要访问网段:10.1.56.0的机器B