人类的视觉原理( 二 )


池化层通常会分别作用域每个输入的特征并减小其大小 。当前最常用形式的池化层是每隔2个元素从图像划分出 2 × 2 2 \times 2 2×2的区块,然后对每个区块中的4个数取最大值 。这将会减少75%的数据量 。
池化的作用
池化操作后的结果相比其输入缩小了 。池化层的引入是仿照人的视觉系统对视觉输入对象进行降维和抽象 。在卷积神经网络过去的工作中,研究者普遍认为池化层有如下三个功效:
特征不变性:池化操作是模型更加关注是否存在某些特征而不是特征具体的位置特征降维:池化相当于在空间范围内做了维度约减,从而使模型可以抽取更加广阔的特征 。同时减少了下一层的输入大小,进而减少计算量和参数个数在一定程度上防止过拟合:更方便优化
用例
import numpy as npimport torchd = np.array([[1.0,0.0,1.0,2.0,3.0,4.0]])input = torch.from_numpy(d)print(input)"""ouputtensor([[1., 0., 1., 2., 3., 4.]], dtype=torch.float64)"""import torch.nn.functional as Fouput = F.max_pool1d(input, kernel_size = 2, stride = 1)print(ouput)ouput = F.avg_pool1d(input, kernel_size = 2, stride = 1)print(ouput)"""ouputtensor([[1., 1., 2., 3., 4.]], dtype=torch.float64)tensor([[0.5000, 0.5000, 1.5000, 2.5000, 3.5000]], dtype=torch.float64)"""
上述的用例中展示了,最大池化()和平均池化()的用例 。因为池化也涉及到这个子区域的选择嘛,所以里面的一些参数就和前面的torch.nn.中的参数很类似 。
激活层()
激活层里面其实就是激活函数,就是在人工神经网络的神经元上运行的函数,负责将神经元的输入映射到输出端 。
激活函数对于人工神经网络模型去学习、理解非常复杂和非线性的函数来说具有非常重要的作用 。它们将给线性特性引入到我们的网络中 。引入激活函数是为了增加神经网络模型的非线性 。没有激活函数的每层都相当于矩阵相乘 。就算你叠加了若干层之后,无非还是个矩阵相乘罢了 。
下面是一张关于常见激活函数的表:
全连接层()
全连接层在整个CNN网络中起到“分类器”的作用 。如果说前面的卷积层、池化层和激活函数等操作是将原始数据映射到隐层特征空间,全连接层起到的将学到的特征表示映射到样本的标记空间的作用 。
全连接层实现原理
在卷积神经网络的最后,往往会出现一两层全连接层,全连接一般会把卷积输出的二维特征图转化为一维的向量 。如上图中最后两列表示的就是两个全连接层,在最后一层卷积结束后,进行了最后一次池化,输出了20个 12 × 12 12 \times 12 12×12的图像,然后通过了一个全连接层变成了 1 × 100 1 \times 100 1×100的向量 。这么一看前面的工作我么做的就是一个降维的操作,然后我们将得到的向量输入全连接层进行输出 。
用例
input = torch.Tensor(1, 10)linear = nn.Linear(10, 2)print(input)print(linear(input))"""outputtensor([[5.1429e-39, 4.5000e-39, 4.9592e-39, 4.2246e-39, 1.0286e-38, 1.0653e-38,1.0194e-38, 8.4490e-39, 1.0469e-38, 9.3674e-39]])tensor([[-0.1961,0.2197]], grad_fn=)"""
比如用例中我们可能在前面得到了一个一百维的向量,然后这个任务的是一个二分类的任务 。我们就可以设置通过全连接层输出的向量维度为(1,2) 。其中的2与二分类任务对应 。上面输出的([[-0.1961, 0.2197]],那么一般来说我们认为这个得到的分类结果是第2类 。
实现
class TextCNN(nn.Module):def __init__(self, vocab_size, embedding_dim, kernel_sizes, num_channels, dropout, vectors=None):super(TextCNN, self).__init__()self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)# embedding之后的shape: torch.Size([200, 8, 300])if vectors is not None:self.word_embeddings = self.word_embeddings.from_pretrained(vectors, freeze=False)# dropout 代表每个神经元不被激活的概率self.dropout = nn.Dropout(dropout)self.decoder = nn.Linear(sum(num_channels), 2)# 时序最大池化层没有权重,所以可以共用一个实例self.pool = F.GlobalMaxPool1d()self.convs = nn.ModuleList([nn.Sequential(nn.Conv1d(in_channels = embedding_dim, out_channels = c, kernel_size = k),nn.ReLU(),nn.MaxPool1d(2, padding=1),)for c, k in zip(num_channels, kernel_sizes)])# 创建多个一维卷积层def forward(self, sentence):embeds = self.word_embeddings(sentence)embeds = embeds.permute(0, 2, 1)# 对于每个一维卷积层,在时序最大池化后会得到一个形状为(批量大小, 通道大小, 1)的# Tensor 。使用flatten函数去掉最后一维,然后在通道维上连结encoding = torch.cat([self.pool(conv(embeds)).squeeze(-1) for conv in self.convs], dim=1)# 应用丢弃法后使用全连接层得到输出outputs = self.decoder(self.dropout(encoding))return outputs