两分钟通过代码理解深度神经网络中的卷积层


本文声明:作者才疏学浅,写文章记录和大家一起学习。
前情提要:
不懂张量的小伙伴可以看一看我的这篇博文: 两分钟了解神经网络中的张量
想了解神经网络三部曲的小伙伴可以看一看我的这篇博文: 两分钟了解神经网络中的梯度求解

卷积

1. 什么是卷积

最早了解的就是信号与系统中的一维卷积,在时域中求解LTI系统的零状态响应就是要求输入激励和系统冲激响应的卷积积分。
口诀:反转平移相乘,然后再积分。
定义这样的运算为卷积积分。
卷积积分公式
就这么简单,没什么好怕的。

2. 神经网络中的卷积

一般是CV中的卷积神经网络,用卷积操作来提取图片中的特征,而图片是一个数字矩阵。所以我们下面的卷积操作就为二维卷积了,因为是二维,所以就是Conv2d[dog]。

  • 普通卷积【普通卷积的操作分成3个维度,在空间维度(H和W维度)是共享卷积核权重滑窗相乘求和(融合空间信息),在输入通道维度是每一个通道使用不同的卷积核参数并对输入通道维度求和(融合通道信息),在输出通道维度操作方式是并行堆叠(多种),有多少个卷积核就有多少个输出通道】

2.1 二维卷积

照旧代码加深理解并验证:
torch.nn.functional中的conv
输入:

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

# 卷积输出尺寸计算公式 o = (i + 2*p -k')//s  + 1 
# 对空洞卷积 k' = d(k-1) + 1
# o是输出尺寸,i 是输入尺寸,p是 padding大小, k 是卷积核尺寸, s是stride步长, d是dilation空洞参数

inputs = torch.arange(0,25).reshape(1,1,5,5).float() # i= 5
filters = torch.tensor([[[[1.0,1],[1,1]]]]) # k = 2

# torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1)
outputs = F.conv2d(inputs, filters) # o = (5+2*0-2)//1+1 = 4
outputs_s2 = F.conv2d(inputs, filters, stride=2)  #o = (5+2*0-2)//2+1 = 2
outputs_p1 = F.conv2d(inputs, filters, padding=1) #o = (5+2*1-2)//1+1 = 6
outputs_d2 = F.conv2d(inputs,filters, dilation=2) #o = (5+2*0-(2(2-1)+1))//1+1 = 3

print("--inputs--")
print(inputs)
print("--filters--")
print(filters)

print("--outputs--")
print(outputs,"\n")

print("--outputs(stride=2)--")
print(outputs_s2,"\n")

print("--outputs(padding=1)--")
print(outputs_p1,"\n")

print("--outputs(dilation=2)--")
print(outputs_d2,"\n")

输出:

--inputs--
tensor([[[[ 0.,  1.,  2.,  3.,  4.],
          [ 5.,  6.,  7.,  8.,  9.],
          [10., 11., 12., 13., 14.],
          [15., 16., 17., 18., 19.],
          [20., 21., 22., 23., 24.]]]])
--filters--
tensor([[[[1., 1.],
          [1., 1.]]]])
--outputs--
tensor([[[[12., 16., 20., 24.],
          [32., 36., 40., 44.],
          [52., 56., 60., 64.],
          [72., 76., 80., 84.]]]]) 

--outputs(stride=2)--
tensor([[[[12., 20.],
          [52., 60.]]]]) 

--outputs(padding=1)--
tensor([[[[ 0.,  1.,  3.,  5.,  7.,  4.],
          [ 5., 12., 16., 20., 24., 13.],
          [15., 32., 36., 40., 44., 23.],
          [25., 52., 56., 60., 64., 33.],
          [35., 72., 76., 80., 84., 43.],
          [20., 41., 43., 45., 47., 24.]]]]) 

--outputs(dilation=2)--
tensor([[[[24., 28., 32.],
          [44., 48., 52.],
          [64., 68., 72.]]]]) 

上面是很简单的单个图像用卷积核提取特征。计算的过程就如上面动图所示。

2.2 多维卷积

torch.nn中的conv
nn中的类直接继承了nn.functional
输入:

import torch 
from torch import nn 

features = torch.randn(8,64,128,128)  #四维 可以理解成8维 64输入通道 128*128
print("features.shape:",features.shape)
print("\n")

#普通卷积
# torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
print("--conv--")
conv = nn.Conv2d(in_channels=64,out_channels=32,kernel_size=3) #多卷积核运算
conv_out = conv(features)
print("Conv2d(in_channels=64,out_channels=32,kernel_size=3)")
print("conv_out.shape:",conv_out.shape) 
print("conv.weight.shape:",conv.weight.shape)
print("\n")

#分组卷积
print("--group conv--")
conv_group = nn.Conv2d(in_channels=64,out_channels=32,kernel_size=3,groups=8)
group_out = conv_group(features)
print("group_out.shape:",group_out.shape) 
print("conv_group.weight.shape:",conv_group.weight.shape)
print("\n")

#深度可分离卷积
print("--separable conv--")
depth_conv = nn.Conv2d(in_channels=64,out_channels=64,kernel_size=3,groups=64)
oneone_conv = nn.Conv2d(in_channels=64,out_channels=32,kernel_size=1)
separable_conv = nn.Sequential(depth_conv,oneone_conv)
separable_out = separable_conv(features)
print("separable_out.shape:",separable_out.shape) 
print("depth_conv.weight.shape:",depth_conv.weight.shape)
print("oneone_conv.weight.shape:",oneone_conv.weight.shape)
print("\n")

#转置卷积
print("--conv transpose--")
conv_t = nn.ConvTranspose2d(in_channels=32,out_channels=64,kernel_size=3)
features_like = conv_t(conv_out)
print("features_like.shape:",features_like.shape)
print("conv_t.weight.shape:",conv_t.weight.shape)

输出:

eatures.shape: torch.Size([8, 64, 128, 128])


--conv--
Conv2d(in_channels=64,out_channels=32,kernel_size=3)
conv_out.shape: torch.Size([8, 32, 126, 126])
conv.weight.shape: torch.Size([32, 64, 3, 3])


--group conv--
group_out.shape: torch.Size([8, 32, 126, 126])
conv_group.weight.shape: torch.Size([32, 8, 3, 3])


--separable conv--
separable_out.shape: torch.Size([8, 32, 126, 126])
depth_conv.weight.shape: torch.Size([64, 1, 3, 3])
oneone_conv.weight.shape: torch.Size([32, 64, 1, 1])


--conv transpose--
features_like.shape: torch.Size([8, 64, 128, 128])
conv_t.weight.shape: torch.Size([32, 64, 3, 3])

而上面这一段代码是多通道卷积,也就是用多个卷积核提取特征。
图:
多维卷积核移动过程
这样移动一遍之后,输出的是一个矩阵。
在这里插入图片描述
重点在这里!要考的!
6 * 6 * 3 经过2个卷积核提取特征之后变成了4 * 4 * 2。
和input=64而output=32有异曲同工之妙。
其关键就在于使用的卷积核数目或输出通道数(检测的特征数目)

总结

本文从一维卷积层开始层层刨析,揭开了卷积神经网络普通卷积的神秘面纱。

点个赞,鼓励一下博主呗,么么哒~

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦想当极客的小芦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值