TextCNN 文本分类

        TextCNN 模型主要由一维卷积层和时序最大池化层构成,一维卷积层是高为 1 的二维卷积层,在每个通道上,卷积核与输入做互相关运算,并将通道之间的结果相加得到输出结果。

        时序最大池化层对应一维全局最大池化层,特点是卷积窗口和输入数组的宽高对应相同,每个通道只输出一个元素。例如,输入的文本序列由 n 个词组成,词向量为 d 维,输入样本的宽为 n,高为1,输入通道数为 d,卷积核的高度为 d,卷积核的个数即为输出通道数。

TextCNN步骤:

        定义多个一维卷积核,对输入分别做卷积计算,宽度不同的卷积核会捕捉到不同个数的相邻词的相关性。对输出的所有通道分别做时序最大池化,将通道的池化输出值连结为向量。通过全连接层将连结后的向量变换为有关各类别的输出。

        如上面的流程图,输入文本 shape 为 7 * 5,设置了三种卷积,高为(3,4,5),宽都为 5,每种卷积数量为 2,每个卷积输出 (7 - (3/4/5)) + 1 个结果,再用最大池化,将池化结果时输入全连接。

PyTorch 实例

filter_num = 2,embed_dim=128,seq_len=17,filter_size=[3,4,5]

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


class TextCNNModule(nn.Module):

    def __init__(self, seq_len, filter_num, filter_size, vocab_size, embed_dim, class_num):
        super(TextCNNModule, self).__init__()
        self.seq_len = seq_len
        self.filter_size = filter_size
        self.total_filter_num = filter_num * len(filter_size)
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.weight = nn.Linear(self.total_filter_num, class_num, bias=False)
        self.Bias = nn.Parameter(torch.ones([class_num]))
        self.filter_list = nn.ModuleList([nn.Conv2d(1, filter_num, (i, embed_dim)) for i in filter_size])

    def forward(self, X):
        embed_char = self.embedding(X)
        embed_char = embed_char.unsqueeze(1)  # 增加一个维度

        pooled_output = []
        for i, conv in enumerate(self.filter_list):
            conv2d = conv(embed_char)
            h = F.relu(conv2d)
            p = nn.MaxPool2d((self.seq_len - self.filter_size[i] + 1, 1))
            pooled = p(h).permute(0, 3, 2, 1)
            pooled_output.append(pooled)

        h_pool = torch.cat(pooled_output, len(self.filter_size))
        h_pool_flat = torch.reshape(h_pool, [-1, self.total_filter_num])
        m = self.weight(h_pool_flat) + self.Bias

        return m

seq_len=17,conv_num=2,conv_size_list=[3,4,5]

class TextCNN(nn.Module):

    def __init__(self, vocab_size, embedding_dim, seq_len, conv_num, conv_size_list, dropout, class_num):
        super(TextCNN, self).__init__()
        self.seq_len = seq_len
        self.conv_size_list = conv_size_list
        self.embed = nn.Embedding(vocab_size, embedding_dim)
        
        # 1 代表输入 channel, conv_num 代表输出 channel, 输入是图片时两者应相同;
        # 处理文本时输入是一条文本样本, 其channel为1, 如10个token的样本, shape=(10, embed_dim),
        # conv_num 代表用conv_num个核的卷积对样本做卷积计算,每个核输出一个值
        self.conv_list = nn.ModuleList([nn.Conv2d(1, conv_num, (i, embedding_dim)) for i in conv_size_list])
        self.dropout = nn.Dropout(dropout)
        total_conv_num = conv_num * len(conv_size_list)
        self.fc = nn.Linear(total_conv_num, class_num, bias=True)

    def forward(self, x):
        x_embed = self.embed(x).unsqueeze(1)  # 在第二个位置添加一维,shape=(4,1,17,128)
        pools = []
        for index, conv in enumerate(self.conv_list):
            x_conv = conv(x_embed)
            # xr = nn.ReLU(x_conv)  # nn.ReLU()返回的是ReLU类
            x_conv_sq = x_conv.squeeze(3)
            xr = F.relu(x_conv_sq)  # F.relu()返回的是tensor
            xr_sq = xr.squeeze(2)
            p = nn.MaxPool1d(self.seq_len - self.conv_size_list[index] + 1, 1)
            # p = F.max_pool1d(self.seq_len - self.conv_size_list[index] + 1, 1)  # 输入需要是 tensor
            x_pool = p(xr_sq)  # shape(4,2,1), 2代表的是conv_num, 每个 conv 输出一个结果
            pools.append(x_pool)

        h_pool = torch.cat(pools, 1)  # 按照pools中元素(x_pool)第二个维度合并, shape=(4,6,1)
        h_pool = h_pool.squeeze(2)  # 去除最后一维(1), shape=(4,6)
        # x_drop = self.dropout(h_pool)  # 数据量小时,可能导致欠拟合
        output = self.fc(h_pool)

        return output

使用Conv1d遇到的问题

使用CNN中的conv1d做特征提取,输入数据特征为50,随机生成了1000条数据,目标是用每条数据的50个特征做回归预测。

定义模型时开始使用如下输入shape,报错:ValueError: One of the dimensions in the output is <= 0 due to downsampling in conv1d. Consider increasing the input size. Received input shape [None, 1, 50] which would produce output shape with a zero or negative value in a dimension.

model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(1, 50)))

解决办法:调整输入shape,Conv1d是为时间序列设计的,我的目标是用一条50个特征做预测,划分数据时一条数据就是一个批次,训练时只是使用batch_size参数,网上有解释此处不能为1。

model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(50, 1)))

Pytorch中的conv1d

torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')

in_channels (int):输入图片的通道数量,在文本分类中为词向量的维度
out_channels (int) :卷积产生的通道,代表1维卷积数量;
kernel_size (int or tuple) :卷积核的尺寸,第二个维度由in_channels决定,卷积核大小为kernel_size*in_channels
stride (int or tuple, optional):卷积步长,Default=1;
padding (int or tuple, optional) :输入的每一条边补充0的层数,Default=0
padding_mode (string, optional) :‘zeros’, ‘reflect’, ‘replicate’ or ‘circular’,Default=‘zeros’
dilation (int or tuple, optional) :Spacing between kernel elements,Default=1
groups (int, optional) :Number of blocked connections from input channels to output channels, Default=1,该参数控制输入和输出的连接。当groups = 2时,相当于有两个conv层并排,每个conv层看到一半的输入通道,产生一半的输出通道,然后两者concate起来。
当groups = in_channels 且out_channels = K * in_channels, K为正整数,得到 “depthwise convolution”。

一维卷积计算示意图

有C_out个卷积核,取一个卷积核(红色部分),让其在输入数据中“滑动”,“滑动”的次数代表输出的序列长度。
输入矩阵的第 k行和卷积核的第k行做互相关的计算,即卷积核不断移动,与输入对应位置的元素做对应元素逐个相乘并求和。
纵向看,输入的每一个通道与卷积核相应行卷积出的向量(序列)加在一起。

参考:

pytorch 实现 textCNN_pytorch textcnn_明日何其多_的博客-CSDN博客

pytorch搭建TextCNN与使用案例_textcnn pytorch_呆萌的代Ma的博客-CSDN博客

CNN - nn.Conv1d使用-CSDN博客

nn.Conv2d参数详解及输入特征图尺寸的计算-CSDN博客

根据PyTorch学习CONV1D_pytorch conv1d-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值