在前一篇文章中,我们使用了一维卷积核来处理文本数据,其输入格式为[batch, 通道数,文本单词数(长)]。而在二维卷积的应用中,输入格式变为[batch, 通道数,单词数(长),词向量维度(宽)]。本文将展示如何使用二维卷积来进行文本分类,尤其关注于单个数据的处理(即batch=1)。
现在假设有一个文本数据,其格式为[1, 通道数, 单词数, 词向量]。在这个情况下,通道数为1,因此文本格式简化为[1, 1, 单词数, 词向量]。
接下来,我们使用不同的卷积核进行卷积操作。卷积核的大小可以根据需要选择,例如3-gram、4-gram和5-gram。卷积核大小分别为[单词数, 词向量维度]。
接着,我们对这些卷积结果进行最大池化操作,池化窗口大小分别为(单词数-3)/1+1、(单词数-4)/1+1、(单词数-5)/1+1。这些池化窗口大小与向量大小一致,即在每个向量上找到最大值。
经过池化层后,输入格式变为[1, 卷积核通道数]。这表明第一个卷积层已经将词向量维度(第四个维度)处理掉,而第二个池化层则处理了单词数维度(第三个维度)。每个池化层的输入格式为[1, 卷积核通道数]。
最后,我们将这些卷积层的结果进行拼接,形成一个新的向量[1, 卷积通道数1 + 卷积通道数2 + 卷积通道数3]。这个向量随后被送入全连接层,以完成最终的分类任务。
本文的代码和数据集已上传至GitHub,供读者自行下载和运行。
```python import torch.nn as nn import torch.nn.functional as F
class CNNText(nn.Module): def init(self, embednum, embeddim, classnum, Ci, kernelnum, kernelsizes): super(CNNText, self).init() self.embed = nn.Embedding(embednum, embeddim) self.convs1 = nn.ModuleList([nn.Conv2d(Ci, kernelnum, (K, embeddim)) for K in kernelsizes]) self.dropout = nn.Dropout(0.5) self.fc1 = nn.Linear(len(kernelsizes) * kernelnum, class_num)
def forward(self, x):
# 对于batch=1的x,形状为[单词数]
x = self.embed(x) # 形状为[单词数, 词向量维度]
x = x.unsqueeze(0) # 形状为[1, 单词数, 词向量维度]
x = x.unsqueeze(1) # 形状为[1, 1, 单词数, 词向量维度]
x = [F.relu(conv(x)).squeeze(3) for conv in self.convs1] # 形状为[1, 卷积核通道数, (单词数-卷积核大小)/1+1]
x = [F.max_pool1d(i, i.size(2)).squeeze(2) for i in x] # 形状为[1, 卷积核通道数]
x = torch.cat(x, 1) # 形状为[1, 卷积核通道数1 + 卷积核通道数2 + 卷积核通道数3]
x = self.dropout(x)
logit = self.fc1(x)
return logit
```
embed_num 表示词汇表中的所有词的数量。embed_dim 表示每个词的编码维度。class_num 表示分类类别数量。Ci 表示输入通道数,在这里为1。kernel_num 表示卷积核的通道数。kernel_sizes 表示卷积核的大小,这里使用三种大小的卷积核,因此为一个列表[3, 4, 5]。通过以上步骤,我们能够利用二维卷积神经网络有效地完成文本分类任务。