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中,给出了一个非常形象的图,诠释了文本卷积模型的框架,如下所示
其构造函数至少需要提供三个参数:
- 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