LogoLogo
  • README
  • 前端编程
    • 01 Node JS
    • 02-ES6详解
    • 03-NPM详解
    • 04-Babel详解
    • 05-前端模块化开发
    • 06-WebPack详解
    • 07-Vue详解
    • 08-Git详解
    • 09-微信小程序
  • 人工智能
    • 机器学习
      • 二次分配问题
      • 非负矩阵
      • 概率潜在语义分析
      • 概率图模型
      • 集成学习
      • 降维
      • 距离度量
      • 决策树
      • 逻辑回归
      • 马尔可夫决策过程
      • 马尔可夫链蒙特卡洛法
      • 朴素贝叶斯法
      • 谱聚类
      • 奇异值分解
      • 潜在狄利克雷分配
      • 潜在语义分析
      • 强化学习
      • 社区算法
      • 时间序列模型
      • 特征工程
      • 条件随机场
      • 图论基础
      • 线性分类
      • 线性回归
      • 信息论中的熵
      • 隐马尔科夫模型
      • 支持向量机
      • 主成分分析
      • EM算法
      • Hermite 矩阵的特征值不等式
      • k-means聚类
      • k近邻法
      • PageRank算法
    • 深度学习
      • Pytorch篇
        • 01-线性模型
        • 02-梯度下降法
        • 03-反向传播
        • 04-pytorch入门
        • 05-用pytorch实现线性回归
        • 06-logistic回归
        • 07-处理多维特征的输入
        • 08-加载数据集
        • 09-多分类问题
        • 10-卷积神经网络
        • 11-循环神经网络
    • 图神经网络
      • 图神经网络笔记01
        • 01-图(Graphs)的结构
        • 02-网络的性质和随机图模型
        • 03-网络工具
        • 04-网络中的主题和结构角色
        • 05-网络中的社区结构
      • 图神经网络笔记02
        • 01-深度学习引言
        • 02-神经网络基础
        • 03-卷积神经网络
        • 04-图信号处理与图卷积神经网络
        • 05-GNN的变体与框架-
        • [06-Google PPRGo 两分钟分类千万节点的最快GNN](人工智能/图神经网络/图神经网络笔记02/06-Google%20PPRGo 两分钟分类千万节点的最快GNN.md)
        • 07-序列模型
        • 08-变分自编码器
        • 09-对抗生成网络
  • 日常记录
    • 健身日记
    • 面经记录
    • 自动生成Summary文件
  • 实战项目
    • 谷粒商城
      • 00-项目概述
      • 01-分布式基础-全栈开发篇
      • 02-分布式高级-微服务架构篇
      • 03-高可用集群-架构师提升篇
  • 数据库
    • MySQL笔记
      • 01-MySQL基础篇
      • 02-MySQL架构篇
      • 03-MySQL索引及调优篇
      • 04-MySQL事务篇
      • 05-MySQL日志与备份篇
    • Redis笔记
      • 01-Redis基础篇
      • 02-Redis高级篇
    • 02-Redis篇
  • 算法笔记
    • 01-算法基础篇
    • 02-算法刷题篇
  • 职能扩展
    • 产品运营篇
  • Go编程
    • 01-Go基础
      • 01-Go基础篇
  • Java编程
    • 01-Java基础
      • 01-Java基础篇
      • 02-多线程篇
      • 03-注射与反解篇
      • 04-JUC并发编程篇
      • 05-JUC并发编程与源码分析
      • 06-JVM原理篇
      • 07-Netty原理篇
      • 08-设计模式篇
    • 02 Java Web
      • 01-Mybatis篇
      • 01-Mybatis篇(新版)
      • 02-Spring篇
      • 02-Spring篇(新版)
      • 03-SpringMVC篇
      • 04-MybatisPlus篇
    • 03-Java微服务
      • 01-SpringBoot篇
      • 01-SpringBoot篇(新版)
      • 02-SpringSecurity篇
      • 03-Shiro篇
      • 04-Swagger篇
      • 05-Zookeeper篇
      • 06-Dubbo篇
      • 07-SpringCloud篇
      • 08-SpringAlibaba篇
      • 09-SpringCloud篇(新版)
    • 04-Java中间件
      • 数据库篇
        • 01-分库分表概述
        • 02-MyCat篇
        • 03-MyCat2篇
        • 04-Sharding-jdbc篇
        • 05-ElasticSearch篇
      • 消息中间件篇
        • 01-MQ概述
        • 02-RabbitMQ篇
        • 03-Kafka篇
        • 04-RocketMQ篇
        • 05-Pulsar篇
    • 05-扩展篇
      • Dubbo篇
      • SpringBoot篇
      • SpringCloud篇
    • 06-第三方技术
      • 01-CDN技术篇
      • 02-POI技术篇
      • 03-第三方支付技术篇
      • 04-第三方登录技术篇
      • 05-第三方短信接入篇
      • 06-视频点播技术篇
      • 07-视频直播技术篇
    • 07-云原生
      • 01-Docker篇
      • 02-Kubernetes篇
      • 03-Kubesphere篇
  • Linux运维
    • 01-Linux篇
    • 02-Nginx篇
  • Python编程
    • 01-Python基础
      • 01.配置环境
      • 02.流程控制
      • 03.数值
      • 04.操作符
      • 05.列表
      • 06.元祖
      • 07.集合
      • 08.字典
      • 09.复制
      • 10.字符串
      • 11.函数
      • 12.常见内置函数
      • 13.变量
      • 14.异常和语法错误
      • 15.时间和日期
      • 16.正则表达式
    • 02 Python Web
      • flask篇
        • 01.前言
        • 02.路由
        • 03.模板
        • 04.视图进阶
        • 05.flask-sqlalchemy
        • 06.表单WTForms
        • 07.session与cookie
        • 08.上下文
        • 09.钩子函数
        • 10.flask 信号
        • 11.RESTFUL
        • 13.flask-mail
        • 14.flask+celery
        • 15.部署
        • 16.flask-login
        • 17.flask-cache
        • 18.flask-babel
        • 19.flask-dashed
        • 20.flask-pjax
        • 21.flask上传文件到第三方
        • 22.flask-restless
        • 23.flask-redis
        • 24.flask-flash
        • 25.消息通知
        • 26.分页
    • 03-Python数据分析
      • Matplotlib
      • Numpy
      • Pandas
      • Seaborn
    • 04-Python爬虫
      • 1.准备工作
      • 2.请求模块的使用
      • 3.解析模块的使用
      • 4.数据存储
      • 5.识别验证码
      • 6.爬取APP
      • 7.爬虫框架
      • 8.分布式爬虫
由 GitBook 提供支持
在本页
  • 什么是卷积神经网络
  • 卷积神经网络
  • 信号处理中的卷积
  • 图像中的卷积
  • 二维卷积层
  • 卷积的意义
  • 卷积的特点
  • 深度学习中的卷积操作
  • 特征图
  • padding
  • 步长
  • 池化

这有帮助吗?

在GitHub上编辑
  1. 人工智能
  2. 图神经网络
  3. 图神经网络笔记02

03-卷积神经网络

上一页02-神经网络基础下一页04-图信号处理与图卷积神经网络

最后更新于3年前

这有帮助吗?

什么是卷积神经网络

卷积神经网络(Convolutional Neural Network,CNN or ConvNet)是一种具有局部连接、权值共享等特点的深层前馈神经网络,目前在图像和视频分析领域取得了显著的成功,是目前应用最为广泛的模型之一。

卷积神经网络

卷积与池化是卷积神经网络中的两个核心操作,大多数的神经网络结构都是将它们组合而得到的。

信号处理中的卷积

来源:卷积一词是来自于信号处理领域,是一项广泛应用于信号处理,图像处理以及其他工程科学领域的技术。

典型应用:针对某个线性时不变的系统,给定输入信号$f(\tau)$和系统响应$g(\tau)$,求系统的输出。

数学定义:

(f∗g)(t)=∫−∞∞f(ξ)g(t−τ)dτ(f*g)(t) = \int_{-\infty}^{\infty}f(\xi)g(t-\tau)d\tau(f∗g)(t)=∫−∞∞​f(ξ)g(t−τ)dτ

含义:函数$g(\tau)$经过翻转和平移$t$后,得到$g(t-\tau)$,再求与函数$f(\tau)$乘积的积分。

image-20210228153530879

图像中的卷积

接下来以图像为例来直观地理解卷积,计算机中的图像通常都是按照像素点以离散的形式存储的,可以用一个二维或者三维的矩阵来表示。

假设对于一个二维的图像$X \in R^{H\times W}$,卷积核为$G\in R^{k\times k}$,通常$k$为奇数,二维离散卷积的计算方式如下:

Ym,n=∑i=−∣k2∣k2∑j=−∣k2∣k2Xm−i,n−jGi,jY_{m,n} = \sum\limits_{i=-|\frac{k}{2}|}^{\frac{k}{2}} \sum\limits_{j=-|\frac{k}{2}|}^{\frac{k}{2}} X_{m-i,n-j} G_{i,j}Ym,n​=i=−∣2k​∣∑2k​​j=−∣2k​∣∑2k​​Xm−i,n−j​Gi,j​
import torch

def corr2d(X,K):
    h,w = K.shape
    m,n = X.shape
    
    Y = torch.zeros((m-h+1,n-w+1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            Y[i,j] = (X[i:i+h,j:j+w]*K).sum()
    return Y


X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
K = torch.tensor([[0, 1], [2, 3]])
corr2d(X,K)

直观理解上述卷积过程:先将卷积核选择$180^。$,然后在输入中的对应位置去除一个大小为$k \times k $的区域,得到对应位置的输出。

在传统的图像处理中,卷积核通常是人为设定的,不同的卷积核可以提取输入中的某种特征,得到不同的输出。如下展示了两种不同的卷积核Sobel和Laplacian,它们都可以用于提取图像的边缘,但Laplacian是一个二阶的算子,而Sobel是一个一阶的算子,因此应用它们得到的边缘检测效果有明显的差异。也就是说,不同的卷积核可以提取到不一样的特征。

二维卷积层

二维卷积层将输入和卷积核做互相关运算,并加上一个标量偏差来得到输出。卷积层的模型参数包括了卷积核和标量偏差。在训练模型的时候,通常我们先对卷积核随机初始化,然后不断迭代卷积核和偏差。

class Conv2D(torch.nn.Module):
    
    def __init__(self,kernel_size):
        super(Conv2D,self).__init__()
        self.weight = torch.nn.Parameter(torch.randn(kernel_size))
        self.bias = torch.nn.Parameter(torch.randn(1))
        
    def forward(self,x):
        return corr2d(x,self.weight) + self.bias

一个卷积层的简单应用(1):检测图像中物体的边缘,即找到像素变化的位置。首先我们构造一张6×8的图像(即高和宽分别为6像素和8像素的图像)。它中间4列为黑(0),其余为白(1)。

X = torch.ones(6, 8)
X[:, 2:6] = 0
X

然后构造一个卷积核,进行计算,可以看出,我们将从白到黑的边缘和从黑到白的边缘分别检测成了1和-1。其余部分的输出全是0。:

K = torch.tensor([
    [1,-1]
])
Y = corr2d(X,K)
Y

由此,可以看出,卷积层可通过重复使用卷积核有效地表征局部空间。

一个卷积层的简单应用(2):使用物体边缘检测中的输入数据X和输出数据Y来学习构造的核数组K。首先构造一个卷积层,其卷积核将被初始化成随机数组。接下来在每一次迭代中,使用平方误差来比较Y和卷积层的输出,然后计算梯度来更新权重。

conv2d = Conv2D(kernel_size=(1,2))

step = 20
lr = 0.01
for i in range(step):
    Y_hat = conv2d(X)
    l = ((Y_hat - Y)**2).sum()
    l.backward()
    
    # 梯度下降
    conv2d.weight.data -= lr*conv2d.weight.grad
    conv2d.bias.data -= lr*conv2d.bias.grad
    
    # 梯度清零
    conv2d.weight.grad.zero_()
    conv2d.bias.grad.zero_()
    
    if (i + 1) % 5 == 0:
        print('Step %d, loss %.3f' % (i + 1, l.item()))

卷积的意义

意义:在于可以将时域中复杂的卷积运算转换为频域中简单的相乘运算。

(f∗g)(t)⇔F(t)G(t)(f*g)(t) \Leftrightarrow F(t)G(t)(f∗g)(t)⇔F(t)G(t)

要理解卷积定理,还需要知道傅里叶变换。傅里叶变换是将时域中的数据转换到频域中的一种方法,它将函数分解为一系列不同频率的三角函数的叠加,可以将它理解为从另一个维度去观察数据。

卷积的特点

  • 局部连接:由于图像通常具有局部相关性,因此卷积计算每次只在与卷积核大小对应的区域进行,也就是说输入和输出是局部连接的。

  • 权值共享:在图像中,不同的区域使用相同的卷积核参数,这一方面减少了参数量,另一方面带来了平移不变性。平移不变性指的是不管输入如何平移,总能够得到相同的输出。

  • 层次化表演:卷积网络通过卷积层堆叠得到,每一层都是对前一层进行变换,提取的特征从低层次到高层次,逐渐变得抽象。

深度学习中的卷积操作

单通道卷积

  • 定义

Hm,n=∑i=−∣k2∣k2∑j=−∣k2∣k2Xm+i,n+jGi,jH_{m,n} = \sum\limits_{i=-|\frac{k}{2}|}^{\frac{k}{2}} \sum\limits_{j=-|\frac{k}{2}|}^{\frac{k}{2}} X_{m+i,n+j} G_{i,j}Hm,n​=i=−∣2k​∣∑2k​​j=−∣2k​∣∑2k​​Xm+i,n+j​Gi,j​
import torch

inputs = torch.Tensor([
    [3,4,6,5,7],
    [2,4,6,8,2],
    [1,6,7,8,4],
    [9,7,4,6,2],
    [3,7,5,4,1]
])

kernel = torch.Tensor([
    [1,2,3],
    [4,5,6],
    [7,8,9]
])

m,n = inputs.shape
mk,nk = kernel.shape
outputs = torch.zeros((m-mk+1,n-nk+1))
mo,no = outputs.shape

for i in range(mo):
    for j in range(no):
        outputs[i,j] = torch.mul(inputs[i:i+mk,j:j+nk],kernel).sum()
        

多通道卷积

设输入$X∈R^{H×W×C}$,C表示通道(channels)数,卷积核的长宽都为$k$。由于输入有多个通道,因此我们赋予每个通道一个$k×k$的卷积核,这些卷积核构成$G^{c'}∈R^{k×k×C}$,这样多通道卷积可以定义为:

Hm,n,c′=∑i=−∣k2∣k2∑j=−∣k2∣k2Xm+i,n+j⋅Gi,jc′H_{m,n,c^{'}} = \sum\limits_{i=-|\frac{k}{2}|}^{\frac{k}{2}} \sum\limits_{j=-|\frac{k}{2}|}^{\frac{k}{2}} X_{m+i,n+j}\cdot G^{c^{'}}_{i,j}Hm,n,c′​=i=−∣2k​∣∑2k​​j=−∣2k​∣∑2k​​Xm+i,n+j​⋅Gi,jc′​

注意输出维度$H'=\frac{H+2p-k}{s}+1$,其中p为padding,s为步长。

import torch

in_channels,out_channels = 5,10
width, height = 100,100
kernel_size = 3
batch_size = 1

inputs = torch.randn(batch_size,in_channels,width,height)

conv_layer = torch.nn.Conv2d(in_channels,out_channels,
									kernel_size=kernel_size)

outputs = conv_layer(inputs)

print("inputs.shape = {};
	\noutputs.shape = {};
	\nconv_layer.weight.shape = {}".format(inputs.shape,
                                        outputs.shape,
                                        conv_layer.weight.shape))
                                        

特征图

经过一系列卷积对应相乘,求均值运算后,把一张完整的feature map填满。

feature map是每一个feature从原始图像中提取出来的“特征”。其中的值,越接近为1表示对应位置和feature的匹配越完整,越是接近-1,表示对应位置和feature的反面匹配越完整,而值接近0的表示对应位置没有任何匹配或者说没有什么关联。

一个feature作用于图片产生一张feature map,对这张图来说,用的是3个feature,因此最终产生3个 feature map。

padding

padding,即边缘填充,能够避免边缘信息的丢失,可以分为四类:零填充,常数填充,镜像填充,重复填充。

import torch
import numpy as np

inputs = torch.Tensor([
    3,4,6,5,7,
    2,4,6,8,2,
    1,6,7,8,4,
    9,7,4,6,2,
    3,7,5,4,1])
inputs = inputs.view(1,1,5,5)

conv_layer = torch.nn.Conv2d(1,1,kernel_size=3,padding=1,bias=False)

kernel = torch.Tensor(list(range(1,10))).view(1,1,3,3)
conv_layer.weight.data = kernel.data

outputs = conv_layer(inputs)
print(outputs)

以低光照增强任务为例,最终对比效果如下图。零填充会产生边缘伪影,而镜像填充很好地缓解了这一效应。

步长

也就是窗口移动的步福,比如设置步长(stride)为2:

import torch
import numpy as np

inputs = torch.Tensor([
    3,4,6,5,7,
    2,4,6,8,2,
    1,6,7,8,4,
    9,7,4,6,2,
    3,7,5,4,1])
inputs = inputs.view(1,1,5,5)

conv_layer = torch.nn.Conv2d(1,1,kernel_size=3,
					padding=0,stride=2,bias=False)

kernel = torch.Tensor(list(range(1,10))).view(1,1,3,3)
conv_layer.weight.data = kernel.data

outputs = conv_layer(inputs)
print(outputs)

池化

两种池化:最大池化和平均池化。

池化(pooling)目的:为了缓解卷积层对位置的过度敏感性,或者降维,以降低计算量,并在训练初期提供一些平移不变性。

同卷积层一样,池化层每次对输入数据的一个固定形状窗口(又称池化窗口)中的元素计算输出。不同于卷积层里计算输入和核的互相关性,池化层直接计算池化窗口内元素的最大值或者平均值。该运算也分别叫做最大池化或平均池化。在二维最大池化中,池化窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。当池化窗口滑动到某一位置时,窗口中的输入子数组的最大值即输出数组中相应位置的元素。

池化窗口形状为22×2的最大池化,阴影部分为第一个输出元素及其计算所使用的输入元素。输出数组的高和宽分别为2,其中的4个元素由取最大值运算$\text{max}$得出:

max(0,1,3,4)=4,max(1,2,4,5)=5,max(3,4,6,7)=7,max(4,5,7,8)=8.max(0,1,3,4)=4,\\ max(1,2,4,5)=5,\\ max(3,4,6,7)=7,\\ max(4,5,7,8)=8.\\max(0,1,3,4)=4,max(1,2,4,5)=5,max(3,4,6,7)=7,max(4,5,7,8)=8.

二维平均池化的工作原理与二维最大池化类似,但将最大运算符替换成平均运算符。池化窗口形状为$p×q$的池化层称为$p×q$池化层,其中的池化运算叫作$p×q$池化。

def pool2d(X,pool_size,mode="max"):
    X = X.float()
    p_h,p_w = pool_size
    Y = torch.zeros(X.shape[0]-p_h+1,X.shape[1]-p_w+1)
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            if mode == "max":
                Y[i,j] = X[i:i+p_h,j:j+p_w].max()
            elif mode == "avg":
                Y[i,j] = X[i:i+p_h,j:j+p_w].mean()
    return Y

X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
pool2d(X, (2, 2))

或者

X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
X = X.view((1, 1, X.shape[0], -1)).float()
# padding,stride并分别指定高和宽上的填充和步幅
pool2d = torch.nn.MaxPool2d(2, padding=0, stride=1)
pool2d(X)
img

image-20210228154726025
image-20210228162711113
image-20210228162837600
image-20210228163829256
image-20210228155907399
img
img
preview
image-20210305164802953
img
image-20210305170448674
image-20210305170455973
image-20210305170545745
img
image-20210228165654724
img