文档分类DPCNN简介(pytorch实现)

DPCNN简介

论文中提出了一种基于 word-level 级别的网络-DPCNN,由于 TextCNN 不能通过卷积获得文本的长距离依赖关系,而论文中 DPCNN 通过不断加深网络,可以抽取长距离的文本依赖关系。

实验证明在不增加太多计算成本的情况下,增加网络深度就可以获得最佳的准确率。‍

前面我们提到过TextRCNN就是将CNN中的池化加入到RNN中,来解决RNN偏倚性问题,现在DPCNN通过不断加深网络,来弥补自身短缺的长距离依赖问题,可见每一种模型都不是十全十美的。相比与RNN自带处理时间序列数据的能力,本文作者提出增加模型深度以及降采样(合并卷积核结果)来获取长距离文本特征,同时提取到更加抽象、深层的特征。

DPCNN有如下几个特点:

  1. 在不增加特征数量的情况下进行下采样。下采样能够有效表示文本中的长距离关联。 通过保持相同数量的特征,每 2-stride 下采样将每块计算减少一半,因此总计算时间是常数级;
  2. 残差链接支持训练深层次网络;
  3. region embedding增强了无监督学习得到的embedding信息;

模型结构

在这里插入图片描述

区域嵌入

这里是将TextCNN的包含多尺寸卷积滤波器的卷积层的卷积结果称之为区域嵌入,即对一个文本区域文本片段(比如3-gram)进行一组卷积操作后生成的embedding。这里不同于textCNN的二维卷积,DPCNN采用的是一维卷积。以3-gram为例子回顾textCNN,设置了一个大小为3xD的二维卷积核进行卷积操作(其中D是词嵌入的维度),其实这是一种保留词序的做法。那么不保留词序的做法是首先对3-grm中的3个词的词向量取均值得到一个大小为1xD的向量,然后设置一组大小为1*D的一维卷积核对该3-grm进行卷积操作。那么对于DPCNN,既可以采取保留词序的卷积操作,也可以采用不保留词序的卷积操作。

等长卷积

经过区域嵌入后,是两层卷积层,这里采用的是等长卷积,以此来提高词位embedding的表示的丰富性。首先先介绍一下三种卷积的概念:

假设输入的序列长度为 n,卷积核大小为 m,步长(stride)为 s,输入序列两端各填补 p 个零(zero padding),那么该卷积层的输出序列为 (n-m+2p)/s+1。

  1. 窄卷积:步长 s=1,两端不补零,即 p=0,卷积后输出长度为 n-m+1。
  2. 宽卷积:步长 s=1,两端补零 p=m-1,卷积后输出长度 n+m-1。
  3. 等长卷积:步长 s=1,两端补零 p=(m-1)/2,卷积后输出长度为 n。

输入输出序列的位置数一样多,即为等长卷积,该卷积的意义是:输出的词是由该位置输入的词以及其左右词的上下文信息提取得到的,也就是说,这个词包含被上下文信息修饰过的更高级别的语义。

1/2池化

本文使用一个 size=3,stride=2(大小为3,步长为2)的池化层进行最大池化,在此称为1/2池化层。每经过一个1/2池化层,序列的长度就被压缩成了原来的一半。因此,经过1/2池化后,同样一个size为3的卷积核,其能够感知到的文本片段就比之前长了一倍。在堆叠多层卷积池化层之后,就得到了加深的可以抽取长距离的文本依赖关系的网络。最后的池化层把每段文本聚合为一个向量。

主要区别在于输入层由无监督词嵌入层作为输入,把文档的每个词的词向量作出二维数组作为输入;卷积层有两个卷积层组成,卷积层输入通过跳跃连接,恒等映射和卷积层输出相加作为卷积层输出;采样层以尺度大小为2进行下采样,达到尺度缩放的目的。堆叠几层卷积层和采样层,形成尺度缩放金字塔,达到维度缩放的目的。最终将卷积层输出拼接成向量通过隐藏层和softmax层作为输出分类。

总结:

主要区别在于输入层由无监督词嵌入层作为输入,把文档的每个词的词向量作出二维数组作为输入;卷积层有两个卷积层组成,卷积层输入通过跳跃连接,恒等映射和卷积层输出相加作为卷积层输出;采样层以尺度大小为2进行下采样,达到尺度缩放的目的。堆叠几层卷积层和采样层,形成尺度缩放金字塔,达到维度缩放的目的。最终将卷积层输出拼接成向量通过隐藏层和softmax层作为输出分类。

DPCNN模型代码实现
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class Config(object):
    """配置参数"""
    def __init__(self):
        self.dropout = 0.5  # 随机失活
        self.require_improvement = 1000  # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes =10 # 类别数
        self.n_vocab = 10000  # 词表大小,在运行时赋值
        self.num_epochs = 20  # epoch数
        self.batch_size = 128  # mini-batch大小
        self.pad_size = 32  # 每句话处理成的长度(短填长切)
        self.learning_rate = 1e-3  # 学习率
        self.embed = 300  # 字向量维度
        self.num_filters = 250  # 卷积核数量(channels数)


'''Deep Pyramid Convolutional Neural Networks for Text Categorization'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
        self.conv_region = nn.Conv2d(1, config.num_filters, (3, config.embed), 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)
        # (pad_left, pad_right, pad_top, pad_bottom)填充
        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 = x[0]  # torch.Size([128, 32])
        x = self.embedding(x)  # torch.Size([128, 32,300])
        x = x.unsqueeze(1)  # torch.Size([128, 1, 32, 300])
        x = self.conv_region(x)  # torch.Size([128, 250, 30, 300])
        x = self.padding1(x)  # [128, 250, 32, 1]
        x = self.relu(x)  # [128, 250, 32, 1]
        x = self.conv(x)  # [125, 250, 30, 1]
        x = self.padding1(x)  # [128, 250, 32, 1]
        x = self.relu(x)  # [128, 250, 32, 1]
        x = self.conv(x)  # [128, 250, 30, 1]
        while x.size()[2] > 2:
            x = self._block(x)
        # print("x10", x)#torch.Size([128, 250, 1, 1])
        x = x.squeeze()  # [128, 250]
        x = self.fc(x)  # [128, 10]
        return x

    def _block(self, x):
        x = self.padding2(x)
        px = self.max_pool(x)
        x = self.padding1(px)
        x = F.relu(x)
        x = self.conv(x)
        x = self.padding1(x)
        x = F.relu(x)
        x = self.conv(x)
        x = x + px
        return x

config=Config()
model=Model(config)
print(model)

输出:

Model(
  (embedding): Embedding(10000, 300, padding_idx=9999)
  (conv_region): Conv2d(1, 250, kernel_size=(3, 300), stride=(1, 1))
  (conv): Conv2d(250, 250, kernel_size=(3, 1), stride=(1, 1))
  (max_pool): MaxPool2d(kernel_size=(3, 1), stride=2, padding=0, dilation=1, ceil_mode=False)
  (padding1): ZeroPad2d((0, 0, 1, 1))
  (padding2): ZeroPad2d((0, 0, 0, 1))
  (relu): ReLU()
  (fc): Linear(in_features=250, out_features=10, bias=True)
)
  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值