参考链接:https://www.jianshu.com/p/362b637e2242

参考链接:https://blog.csdn.net/dcrmg/article/details/81255498/

参考链接:https://zhuanlan.zhihu.com/p/59917842

参考链接:https://www.jb51.net/article/171016.htm

一、卷积与特征图

一个简单的深度网络模型,主要是:

input -> conv -> feature_map -> maxpooling -> flatten -> fully connected -> output

CNN:卷积层、池化层、连接层

1. 卷积层

       卷积层由一组卷积单元(又称"卷积核")组成,可以把这些卷积单元理解为过滤器,每个过滤器都会提取一种特定的特征。           输入图像是32*32*3,3是它的深度(即R、G、B三色通道),卷积层是一个5*5*3的filter,这里注意:filter的深度必须和输入图像的深度相同。通过一个filter与输入图像的卷积可以得到一个28*28*1的特征图,如果用了两个filter得到了两个特征图。

举个例子,input、filter、output依次是

输入是一个4*4 的image,经过两个2*2的卷积核进行卷积运算后,变成两个3*3的feature_map。 

最简单的方式计算就是:

(4-2+1)×(4-2+1)=3×3

接下来,我们了解一下中间转换的过程,以上图卷积核filter1为例,filter1在图片滑过,步长为1(stride = 1):

input、filter、output如下图所示

计算第一个卷积层神经元o11的输入: 

neto11=conv(input,filter)=i11×h11+i12×h12+i21×h21+i22×h22=1×1+0×(−1)+1×1+1×(−1)=1

神经元o11的输出:(此处使用Relu激活函数)

outo11=activators(neto11)=max(0, neto11)=1

而对于这个问题,最终转换后的特征图为:

以filter1为例

这样表示不太准确,但大体就是这样计算的。

了解整个过程:

参考这个动图:来源于https://blog.csdn.net/silence1214/article/details/11809947

2.  池化层(下采样层、Down-pooling)

参考链接:https://blog.csdn.net/weixin_38208741/article/details/78818882

参考链接:https://blog.csdn.net/weixin_39568744/article/details/82250663?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

参考链接:https://blog.csdn.net/weixin_40396948/article/details/79353869?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

参考链接:https://blog.csdn.net/u013289254/article/details/99080916?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

参考链接:https://www.zhihu.com/question/36686900

(1)池化层的主要作用

1. invariance(不变性),这种不变性包括translation(平移),rotation(旋转),scale(尺度)

A. translation invariance

B.  rotation invariance

C. scale invariance


2. 保留主要的特征同时减少参数(降维,效果类似PCA)和计算量,防止过拟合,提高模型泛化能力

为什么池化层可以降维?

图像具有静态属性,在一个图像区域有用的特征极有可能在另一个区域同样适用。因此,为了描述大的图像,一个很自然的想法就是对不同位置的特征进行聚合统计。

(2)都有哪些池化?

A. 一般池化(General pooling)

常用的池化包括:mean-pooling(平均池化),max-pooling(最大池化)、Stochastic-pooling(随机池化)和global average pooling(全局平均池化)

池化作用于图像中不重合的区域(这与卷积操作不同),过程如下图。

定义池化窗口的大小为sizeX,即下图中红色正方形的边长,定义两个相邻池化窗口的水平位移/竖直位移为stride。一般池化由于每一池化窗口都是不重复的,所以sizeX=stride。

mean-pooling平均池化:即对邻域内特征点只求平均

优缺点:能很好的保留背景,但容易使得图片变模糊

图片来源于:https://blog.csdn.net/u013289254/article/details/99080916?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

正向传播:邻域内取平均

反向传播:特征值根据领域大小被平均,然后传给每个索引位置

max-pooling(最大池化):即对邻域内特征点取最大

优缺点:能很好的保留纹理特征,一般现在都用max-pooling而很少用mean-pooling

正向传播:取邻域内最大,并记住最大值的索引位置,以方便反向传播

反向传播:将特征值填充到正向传播中,值最大的索引位置,其他位置补0

Stochastic-pooling(随机池化)

只需对feature map中的元素按照其概率值大小随机选择,即元素值大的被选中的概率也大。而不像max-pooling那样,永远只取那个最大值元素。

在区域内,将左图的数值进行归一化处理,即 1/(1+2+3+4)=0.1;2/10=0.2;3/10=0.3;4/10=0.4

global average pooling(全局平均池化)

        全局平均池化一般是用来替换全连接层。在分类网络中,全连接层几乎成了标配,在最后几层,feature maps会被reshape成向量,接着对这个向量做乘法,最终降低其维度,然后输入到softmax层中得到对应的每个类别的得分,过多的全连接层,不仅会使得网络参数变多,也会产生过拟合现象,针对过拟合现象,全连接层一般会搭配dropout操作。

        而全局平均池化则直接把整幅feature maps(它的个数等于类别个数)进行平均池化,然后输入到softmax层中得到对应的每个类别的得分。在反向传播求导时,它的参数更新和mean-pooling(平均池化)很类似,可以参考上面的内容。

        优点:大幅度减少网络参数(对于分类网络,全连接的参数占了很大比列),同时理所当然的减少了过拟合现象。赋予了输出feature maps的每个通道类别意义,剔除了全连接黑箱操作。
 

B. 重叠池化(Overlapping pooling)

重叠池化正如其名字所说的,相邻池化窗口之间会有重叠区域,此时sizeX>stride。

C. 空金字塔池化(Spatial Pyramid Pooling)

        空间金字塔池化可以把任何尺度的图像的卷积特征转化成相同维度,这不仅可以让CNN处理任意尺度的图像,还能避免cropping和warping操作,导致一些信息的丢失,具有非常重要的意义。
       一般的CNN都需要输入图像的大小是固定的,这是因为全连接层的输入需要固定输入维度,但在卷积操作是没有对图像尺度有限制,所有作者提出了空间金字塔池化,先让图像进行卷积操作,然后转化成维度相同的特征输入到全连接层,这个可以把CNN扩展到任意大小的图像。

图片来源于:https://blog.csdn.net/weixin_38208741/article/details/78818882

        空间金字塔池化的思想来自于Spatial Pyramid Model,它一个pooling变成了多个scale的pooling。用不同大小池化窗口作用于卷积特征,我们可以得到1X1,2X2,4X4的池化结果,由于conv5中共有256个过滤器,所以得到1个256维的特征,4个256个特征,以及16个256维的特征,然后把这21个256维特征链接起来输入全连接层,通过这种方式把不同大小的图像转化成相同维度的特征。

        SPP首先把图片看成1块,对这1块进行最大值池化,得到1个值,分成4块,对这4块分别进行最大值池化,得到4个值;分成16块,对这16块分别进行最大值池化,得到16个值,以此类推。这样就可以保证对于不同尺寸的图片而言,最终得到的值的个数是一样的。因为是最大值池化,超出范围的用不用0填充不会影响结果。

3. 卷积网络的设计公式

(1)卷积层的填充(padding)公式

        直接对原始图像做卷积,会存在两个问题。一是每次卷积后图像(特征图)都会缩小,这样卷不了几次就没了; 二是相比于图片中间的点,图片边缘的点在卷积中被计算的次数很少,导致边缘的信息易于丢失。为了解决这个问题,我们可以采用填充的方法。我们每次卷积前,先给图片周围都补一圈空白,让卷积之后图片跟原来一样大,同时,原来的边缘也被计算了更多次。

比如,我们把(8,8)的图片给补成(10,10),那么经过(3,3)的filter之后,就是(8,8),没有变。
能够保证输入的数据和输出的数据具有相同的空间尺寸,假设零填充个数为p,卷积核为f * f,卷积核滑动步长为s,则p应设置为

(2)计算卷积层输出大小的公式

假设原始输入图像为m * m,输出图像为n * n,零填充个数为p,卷积核为f * f,卷积核滑动步长为s,则输出尺寸为

(3)计算卷积后参数个数

假设输入图像为(m,m,d),其中d为图像深度(通道数),卷积核为f * f,卷积核个数为n,则weight个数为

bias个数:

(4)计算池化层输出大小的公式

池化层很少使用零填充。假设原始输入图像为m * m,输出图像为n * n,卷积核为f * f,卷积核滑动步长为s,则输出尺寸为

二、自定义网络特征可视化

1. 代码

python3.6.2

keras2.1.1

from keras.models import Model
import cv2
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers.convolutional import Convolution2D, MaxPooling2D
from keras.layers import Activation
from pylab import *
import keras
 
 
def get_row_col(num_pic):
    squr = num_pic ** 0.5
    row = round(squr)
    col = row + 1 if squr - row > 0 else row
    return row, col
 
 
def visualize_feature_map(img_batch):
    feature_map = np.squeeze(img_batch, axis=0)
    print(feature_map.shape)
 
    feature_map_combination = []
    plt.figure()
 
    num_pic = feature_map.shape[2]
    row, col = get_row_col(num_pic)
 
    for i in range(0, num_pic):
        feature_map_split = feature_map[:, :, i]
        feature_map_combination.append(feature_map_split)
        plt.subplot(row, col, i + 1)
        plt.imshow(feature_map_split)
        axis('off')
        title('feature_map_{}'.format(i))
 
    plt.savefig('feature_map.png')
    plt.show()
 
    # 各个特征图按1:1 叠加
    feature_map_sum = sum(ele for ele in feature_map_combination)
    plt.imshow(feature_map_sum)
    plt.savefig("feature_map_sum.png")
 
 
def create_model():
    model = Sequential()
 
    # 第一层CNN
    # 第一个参数是卷积核的数量,第二三个参数是卷积核的大小
    model.add(Convolution2D(9, 5, 5, input_shape=img.shape))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(4, 4)))
 
    # 第二层CNN
    model.add(Convolution2D(9, 5, 5, input_shape=img.shape))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(3, 3)))
 
    # 第三层CNN
    model.add(Convolution2D(9, 5, 5, input_shape=img.shape))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
 
    # 第四层CNN
    model.add(Convolution2D(9, 3, 3, input_shape=img.shape))
    model.add(Activation('relu'))
    # model.add(MaxPooling2D(pool_size=(2, 2)))
 
    return model
 
 
if __name__ == "__main__":
    img = cv2.imread('C:/Users/Administrator/Desktop/test/a.jpg')
 
    model = create_model()
 
    img_batch = np.expand_dims(img, axis=0)
    conv_img = model.predict(img_batch)  # conv_img 卷积结果
 
    visualize_feature_map(conv_img)

2. 原图

a.jpg

3. 特征图可视化

feature_map.png

feature_map_sum.png

三、附加一点其他图

from pyheatmap.heatmap import HeatMap
import numpy as np
N = 10000
X = np.random.rand(N) * 255  # [0, 255]
Y = np.random.rand(N) * 255
data = []
for i in range(N):
  tmp = [int(X[i]), int(Y[i]), 1]
  data.append(tmp)
heat = HeatMap(data)
heat.clickmap(save_as="1.png") #点击图
heat.heatmap(save_as="2.png") #热图

        

import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import LogNorm
import numpy as np
x, y = np.random.rand(10), np.random.rand(10)
z = (np.random.rand(9000000)+np.linspace(0,1, 9000000)).reshape(3000, 3000)
plt.imshow(z+10, extent=(np.amin(x), np.amax(x), np.amin(y), np.amax(y)),
    cmap=cm.hot, norm=LogNorm())
plt.colorbar()
plt.show()

 
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐