论文链接:Deep Pyramid Convolutional Neural Networks for Text Categorization
介绍得比较详细的一篇笔记:https://www.cnblogs.com/zjuhaohaoxuexi/p/15196408.html
1.深化网络的作用
“At the same time, as described later, the ‘pyramid’enables efficient discovery of long-range associations in the text (and so more global information),as the network is deepened”
DPCNN模型中,为什么网络被深化了就能够有效地发现文本中的长距离关系?
在DPCNN(Deep Pyramid Convolutional Neural Network)模型中,通过加深网络的层数,可以有效地发现文本中的长距离关系。这是由于DPCNN中采用的"pyramid"结构所导致的。
在DPCNN中,"pyramid"结构通过多个层级的卷积操作和池化操作构建,形成了一种分层的结构。每个层级的卷积和池化操作都可以捕捉不同尺度的语义信息。较早的层级捕捉到更局部的信息,而随着网络的深化,较深层级可以捕捉到更全局的信息。
通过加深网络,DPCNN能够在每个层级中不断传递和整合局部和全局的语义信息。这种逐层的信息传递使得网络能够有效地发现文本中的长距离关系,即远距离的语义关联。网络在较早的层级学习到的局部信息随着层级的增加逐渐整合并传递给后续层级,从而能够捕捉到更全局的关系和上下文信息。
因此,通过深化网络并使用"pyramid"结构,DPCNN能够利用不同层级的卷积和池化操作,以一种分层的方式有效地发现文本中的长距离关系,并从中获得更全局的信息。这有助于提高DPCNN模型在文本分类和相关任务中的性能。
2.下采样
“It is followed by stacking of convolution blocks (two convolution layers and a shortcut) interleaved with pooling layerswith stride 2 for downsampling.”
这里的下采样是什么意思?
在上述描述中,"下采样"指的是在DPCNN模型中使用池化层进行降采样操作。具体来说,DPCNN在卷积块之间插入了池化层,并使用步幅为2的池化操作进行下采样。
下采样的目的是减小特征图的尺寸,从而降低模型的计算复杂度并提高效率。在每个池化层中,输入特征图会被划分为不重叠的小块,并且只选择每个小块中的最大值作为输出。这样就减少了特征图的维度,并且保留了最显著的特征。
通过使用步幅为2的池化操作,DPCNN模型在每个池化层之后将特征图的尺寸减半。这种降采样操作有助于缩小特征图的空间范围,提取更高级别的语义信息,并促使模型在更高层次的卷积块中学习到更全局的特征和上下文关系。
因此,在DPCNN中的下采样操作通过池化层和步幅为2的池化操作,降低特征图的尺寸,减少计算复杂度,并帮助模型学习到更全局的语义信息。
模型源代码:
class DPCNN(nn.Module): def __init__(self, config): super( DPCNN, self).__init__() if config.embedding_pretrained is not None: self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False) else: self.embedding = nn.Embedding(config.n_vocab, config.embeding_size, padding_idx=config.n_vocab - 1) self.conv_region = nn.Conv2d(1, config.num_filters, (3, config.embeding_size), stride=1) self.conv = nn.Conv2d(config.num_filters, config.num_filters, (3, 1), stride=1) self.max_pool = nn.MaxPool2d(kernel_size=(3, 1), stride=2) self.padding1 = nn.ZeroPad2d((0, 0, 1, 1)) # top bottom self.padding2 = nn.ZeroPad2d((0, 0, 0, 1)) # bottom self.relu = nn.ReLU() self.fc = nn.Linear(config.num_filters, config.num_classes) def forward(self, x): x = self.embedding(x) # [batch_size,seq_len,embeding_size] x = x.unsqueeze(1) # [batch_size, 1, seq_len, embeding_size] x = self.conv_region(x) # [batch_size, 250, seq_len-3+1, 1] x = self.padding1(x) # [batch_size, 250, seq_len, 1] x = self.relu(x) x = self.conv(x) # [batch_size, 250, seq_len-3+1, 1] x = self.padding1(x) # [batch_size, 250, seq_len, 1] x = self.relu(x) x = self.conv(x) # [batch_size, 250, seq_len-3+1, 1] while x.size()[2] > 2: x = self._block(x) x = x.squeeze() # [batch_size, num_filters(250)] x = self.fc(x) return x def _block(self, x): #输入形状:[batch_size, 250, seq_len-3+1, 1] x = self.padding2(x) # 底部填充,[batch_size, 250, seq_len-3+1+1, 1] px = self.max_pool(x) # 最大池化,[batch_size, 250, (seq_len-3+1+1)//2, 1] x = self.padding1(px) # 顶部和底部填充,[batch_size, 250, (seq_len-3+1+1)//2+2, 1] x = F.relu(x) # ReLU激活函数,形状不变[batch_size, 250, (seq_len-3+1+1)//2+2, 1] x = self.conv(x) # 卷积,[batch_size, 250, (seq_len-3+1+1)//2, 1] x = self.padding1(x) # 顶部和底部填充,[batch_size, 250, (seq_len-3+1+1)//2+2, 1] x = F.relu(x) # ReLU激活函数,形状不变[batch_size, 250, (seq_len-3+1+1)//2+2, 1] x = self.conv(x) # 卷积,[batch_size, 250, (seq_len-3+1+1)//2, 1] x = x + px # 残差连接,将上一步的输出张量与最大池化操作的结果相加,形状不变[batch_size, 250, (seq_len-3+1+1)//2, 1] return x # 输出形状[batch_size, 250, (seq_len-3+1+1)//2, 1]