【深度学习】常用运算符

卷积(Convolution)

卷积是卷积神经网络中最常用的操作,卷积到底是如何运算的呢?

我们先从一维来看,如上图所示,有一个滑动的窗口从左到右,从上到下滑动,这个滑动的 3 ∗ 3 3*3 33的矩阵我们就称作卷积核,卷积核里面的数和输入矩阵的数字一一相乘后相加,就得到了一个新的数字,这些新的数字按从左到右、从上到下的顺序排列,就得到了输出矩阵,整个这个过程,我们就叫做卷积

请添加图片描述

通过一维的卷积我们可以看到,输入的矩阵内的数字是固定的,输出矩阵的数字其实完全取决于卷积核内的数字,如果我想要输出正确的结果,只能通过修正卷积核。换句话来说,卷积核就是我们需要修正的参数,这一点希望大家能够明确。
请添加图片描述

对于图像来讲,我们看到的都是三通道的彩色图(RGB格式),也就是说最初的输入是一个3通道的矩阵,比如(3,640,640),对于多通道的矩阵,卷积操作也不难理解。

请添加图片描述请添加图片描述

其实就是将一维的卷积重复了三次,有3个卷积窗口分别滑动得到了三个矩阵,最后将这三个矩阵内的数相加,得到了最后的输出。可以看到上图的输入shape为(3,5,5),卷积核的shape为(3,3,3),输出的shape为(1,3,3)

然而,输入图像往往需要经过多个卷积层才能输出,也就是说,输入的通道数是不固定的,可能是3、16、64等等,对于这些情况如何卷积呢?

请添加图片描述

卷积操作

如上图所示,有一个通道数很大的输入矩阵,类比我们三通道的卷积方式,其实也不难看出,卷积核的通道数应该等于输入的通道数,这样每一层对应起来,其实就变成了一维的形式,最后把每层的结果相加,就得到了一个一维的输出矩阵

到这边其实还没有结束,因为除了输入通道以外,我们还希望可以控制输出通道数。具体如何控制输出通道数呢?
请添加图片描述

就是用Dout个卷积核重复我们上一步的操作,这样的话,输入通道和输出通道都可以是任意的数

我们可以作出以下的总结,对于一个(Din,Hin,Win)的输入矩阵,经过Dout个(Din,h,w)的卷积核之后,输出为(Dout,Hout,Wout)的矩阵。换句话来说卷积核的通道数等于输入通道数,卷积核的个数等于输出通道数。

实现卷积操作

import paddle
import paddle.nn as nn
#                         N   C   H   w
input = paddle.rand(shape=[1, 3, 32, 32])
# N:批尺寸
# C:通道数
# H:特征高度
# W:特征宽度
Conv2D_func = nn.Conv2D(in_channels=3, out_channels=16, kernel_size=2, stride=2, data_format="NCHW")
print("输入矩阵的形状:", input.shape)
kernel_weights = Conv2D_func.weight.numpy()
print("卷积核形状:", kernel_weights.shape)
output = Conv2D_func(input)
print("Conv2D的输出矩阵的形状:", output.shape)

输出

输入矩阵的形状: [1, 3, 32, 32]
卷积核形状: (16, 3, 2, 2)
Conv2D的输出矩阵的形状: [1, 16, 16, 16]

池化(Pooling)

除了卷积以外,池化也是一个比较常用的运算操作,池化主要分为两种最大池化和平均池化。
![[7.jpg]]
![[8.jpg]]
上图分别为最大池化和平均池化,池化窗口的大小为(2,2),步长为2,池化就是将这个窗口内的数变成一位数,对于最大池化来说,就是变成窗口内的最大数,平均池化就是变成窗口内所有数的平均值。对于多通道的输入来说,池化和卷积不同,池化就是对每一个通道上都进行这样的操作,最后不需要加在一起,也就是说池化前后的输入输出通道不变,仅仅改变了特征图的大小。

更加直观得来说,池化其实相当于对特征图进行了resize的操作。图像的特征特征不会明显改变,池化可以有效地减少参数量,而且可以有效地防止过拟合。

实现两种池化操作

# 池化
# 1. 最大池化
print("---最大池化---")
MaxPool2D_func = nn.MaxPool2D(kernel_size = 2, stride = 2, padding = 0) # 定义一个池化函数实例
print("输入矩阵的形状:", input.shape)
output = MaxPool2D_func(input)
print("输出矩阵:", output.shape)

# 2. 平均池化
print("---平均池化---")
MaxPool2D_func = nn.AvgPool2D(kernel_size = 2, stride = 2, padding = 0) # 定义一个池化函数实例
print("输入矩阵的形状:", input.shape)
output = MaxPool2D_func(input)
print("输出矩阵:", output.shape)

输出

---最大池化---
输入矩阵的形状: [1, 3, 32, 32]
输出矩阵: [1, 3, 16, 16]
---平均池化---
输入矩阵的形状: [1, 3, 32, 32]
输出矩阵: [1, 3, 16, 16]

归一化(Normalization)

归一化的含义也非常简单,就是把矩阵内所有的数不失真地映射到0-1之间。这样做可以防止某一维或某几维对数据影响过大,一般我们在卷积运算之后紧跟着都需要进行归一化操作。
请添加图片描述

左图是未归一化的梯度等高图,右图是归一化之后的图,明显左图受到某些维度的数据影响过大,右图梯度下降就平缓很多,相对更容易达到期望的最优解

实现softmax归一化

# 归一化
print("---归一化---")
batch_normalize_func = nn.Softmax()
output = batch_normalize_func(input)
print("归一化结果:", output)

输出

---归一化---
归一化结果: Tensor(shape=[1, 3, 32, 32], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[[[0.02945272, 0.02023173, 0.03672620, ..., 0.02727095,
           0.04800135, 0.03767298],
          [0.02483018, 0.03907410, 0.04241405, ..., 0.02664823,
           0.03834482, 0.02681958],
          [0.02151689, 0.04063185, 0.02607030, ..., 0.02340001,
           0.03528566, 0.03000160],
          ...,
          [0.03189616, 0.03406674, 0.03284206, ..., 0.01868608,
           0.04039628, 0.03823650],
          [0.02570179, 0.04229375, 0.03318217, ..., 0.03910165,
           0.03136663, 0.03478132],
          [0.04273928, 0.04915112, 0.02639926, ..., 0.02896717,
           0.03779081, 0.02076009]],

         [[0.02125637, 0.01911546, 0.03468457, ..., 0.03193741,
           0.02249386, 0.03008775],
          [0.02613716, 0.03536418, 0.04924056, ..., 0.02179346,
           0.03020715, 0.02932096],
          [0.02268929, 0.02485034, 0.04595596, ..., 0.05259003,
           0.03095243, 0.02420499],
          ...,
          [0.03150102, 0.02546015, 0.02193094, ..., 0.03013777,
           0.03983514, 0.04770198],
          [0.03008535, 0.03357872, 0.02689368, ..., 0.02260291,
           0.01929821, 0.01890717],
          [0.04432007, 0.02071708, 0.01900296, ..., 0.04294547,
           0.02441577, 0.03218404]],

         [[0.05063424, 0.03174394, 0.03208403, ..., 0.04883498,
           0.01918886, 0.02432425],
          [0.02613127, 0.04466325, 0.01913738, ..., 0.03364428,
           0.02955570, 0.02185302],
          [0.02892951, 0.01994222, 0.02151663, ..., 0.03105490,
           0.02314292, 0.02446281],
          ...,
          [0.02056237, 0.02511812, 0.03405361, ..., 0.03050380,
           0.04212487, 0.02005277],
          [0.04123520, 0.02985365, 0.03573005, ..., 0.01828455,
           0.03955228, 0.01976910],
          [0.01794492, 0.03676109, 0.02784664, ..., 0.02514183,
           0.01729956, 0.03750264]]]])

激活函数(Activation)

通过以上的讨论,不难发现整个卷积网络都是对矩阵的一些线性化操作,对于一些非线性的情况,是无法精确拟合的,这就需要增加一些函数略微扭曲输出的结果,这些函数也被称为激活函数

常见的激活函数有很多,例如relu,swish,mish,softmax等。

请添加图片描述
请添加图片描述

这些激活函数看似区别不大,对最终的模型表现却有极大的影响。例如有些激活函数是无上下限的,有些函数时间成本很高,这些都会影响到最后模型的速度和精度

激活函数

# 激活函数relu
print("---激活函数---")
x = paddle.to_tensor([-2.0, -1.0, 0, 1.0, 2.0])
output = F.relu(x)
print("relu", output)

输出

---激活函数---
relu Tensor(shape=[5], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [0., 0., 0., 1., 2.])
  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_宁清

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

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

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

打赏作者

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

抵扣说明:

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

余额充值