Conv1d

Conv1d(in_channels, out_channels, kernel_size)

一般来说,一维卷积nn.Conv1d用于文本数据,只对宽度进行卷积,对高度不卷积。通常,输入大小为word_embedding_dim * max_length,其中,word_embedding_dim为词向量的维度,max_length为句子的最大长度。卷积核窗口在句子长度的方向上滑动,进行卷积操作。

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

主要参数说明:

  • in_channels:在文本应用中,即为词向量的维度
  • out_channels:卷积产生的通道数,有多少个out_channels,就需要多少个一维卷积(也就是卷积核的数量)
  • kernel_size:卷积核的尺寸;卷积核的第二个维度由in_channels决定,所以实际上卷积核的大小为kernel_size *
  • in_channels padding:对输入的每一条边,补充0的层数

Yoon Kim在2014年发表的论文Convolutional Neural Networks for Sentence Classification中,给出了一个非常形象的图,诠释了文本卷积模型的框架,如下所示
Conv1d_第1张图片

其构造函数至少需要提供三个参数

  • in_channels:输入通道的个数,在输入层对应词向量的维度
  • out_channels:输出通道的个数,对应卷积核的个数
  • kernel_size:每个卷积核的宽度

1、定义Conv

当调用该Conv1d对象时,
输入数据形状为 (batch, in_channels, seq_len)
输出数据形状为(batch, out_channels, seq_len)
其中在输入数据与输出数据中,seq_len分别表示输入的序列长度和输出的序列长度, 输出的seq_len长度为L-N+1

import torch
from torch.nn import Conv1d

# 定义一个一维卷积,输入通道大小为5,输出通道大小为2,卷积核宽度为4
conv1 = Conv1d(5, 2, 4)
# 再定义一个一维卷积,输入通道大小为5,输出通道大小为2,卷积核宽度为3
conv2 = Conv1d(5, 2, 3)

# 输入数据批次大小为2,即有两个序列,每个序列长度为6,每个输入的维度为5
inputs = torch.rand(2, 5, 6)

outputs1 = conv1(inputs)
outputs2 = conv2(inputs)

print(outputs1)  # 第一个输出为两个序列,每个序列长度为3,大小为2  (2,2,3)  6-4+1=3
tensor([[[ 0.5825,  0.6546,  0.8976],
         [ 0.2662, -0.0193,  0.3143]],

        [[ 0.4642,  0.5291,  0.6590],
         [ 0.0013, -0.0230,  0.1702]]], grad_fn=<SqueezeBackward1>)
         
print(outputs2)  # 第二个输出也为两个序列,每个序列长度为4,大小为2  (2,2,4)   6-3+1=4
tensor([[[-0.0573, -0.3312, -0.3224, -0.3645],
         [ 0.0404, -0.0330,  0.2703,  0.0099]],

        [[-0.4157, -0.5011, -0.5010, -0.4057],
         [ 0.5016,  0.4791,  0.5678,  0.4822]]], grad_fn=<SqueezeBackward1>)

2、池化

from torch.nn import MaxPool1d

pool1 = MaxPool1d(3)  # 第一个池化层核的大小为3,即卷积层的输出序列长度
pool2 = MaxPool1d(4)  # 第二个池化层核的大小为4,即卷积层的输出序列长度

# 执行一维最大池化操作,即取每行输入的最大值
outputs_pool1 = pool1(outputs1)
outputs_pool2 = pool2(outputs2)

print(outputs_pool1)
tensor([[[0.8976],
         [0.3143]],

        [[0.6590],
         [0.1702]]], grad_fn=<SqueezeBackward1>)
         
print(outputs_pool2)
tensor([[[-0.0573],
         [ 0.2703]],

        [[-0.4057],
         [ 0.5678]]], grad_fn=<SqueezeBackward1>)

也可以用Pytorch在torch.nn.functional 中实现的池化函数,如max_pool1d

import torch.nn.functional as F

outputs_pool1 = F.max_pool1d(outputs1, kernel_size=outputs1.shape[2])
outputs_pool2 = F.max_pool1d(outputs2, kernel_size=outputs2.shape[2])
# outputs的最后一维恰好是其序列的长度

print(outputs_pool1)
tensor([[[0.8976],
         [0.3143]],

        [[0.6590],
         [0.1702]]], grad_fn=<SqueezeBackward1>)

print(outputs_pool2)
tensor([[[-0.0573],
         [ 0.2703]],

        [[-0.4057],
         [ 0.5678]]], grad_fn=<SqueezeBackward1>)

3、cat

由于outputs_pool1和outputs_pool2是两个独立的张量,为了进行下一步操作,需要调用torch.cat函数将他们连接起来。在此之前,还需要调用squeeze函数将最后一个为1的维度删除,即两行一列的矩阵变为一个向量。

outputs_pool_squeeze1 = outputs_pool1.squeeze(dim=2)
print(outputs_pool_squeeze1)
tensor([[0.8976, 0.3143],
        [0.6590, 0.1702]], grad_fn=<SqueezeBackward1>)

outputs_pool_squeeze2 = outputs_pool2.squeeze(dim=2)
print(outputs_pool_squeeze2)
tensor([[-0.0573,  0.2703],
        [-0.4057,  0.5678]], grad_fn=<SqueezeBackward1>)

outputs_pool = torch.cat([outputs_pool_squeeze1, outputs_pool_squeeze2], dim=1)
print(outputs_pool)
tensor([[ 0.8976,  0.3143, -0.0573,  0.2703],
        [ 0.6590,  0.1702, -0.4057,  0.5678]], grad_fn=<CatBackward0>)

4、分类

池化后,再连接一个全连接层,实现分类功能。

from torch.nn import Linear
linear = Linear(4, 2)  # 全连接层,输入维度为4,即池化层输出的维度

outputs_linear = linear(outputs_pool)

print(outputs_linear)
tensor([[ 0.3148, -0.4818],
        [ 0.3412, -0.2712]], grad_fn=<AddmmBackward0>)

参考

[1] 自然语言处理 基于预训练模型的方法 车万翔 郭江等
[2] https://www.jianshu.com/p/45a26d278473
[3] https://arxiv.org/abs/1408.5882

你可能感兴趣的:(NLP,python,cnn,自然语言处理)