概述
有时候我们想用CNN来做文本分类。文本分类大多是RNN模型来做,但是对于分类任务来说CNN是更加擅长的。所以我们可以用CNN来进行文本的分类。
环境
pytorch=1.8 python=3.8 rtx2080ti
模型
对于一个语料库我们要先用 wordv2vec 预训练一个词向量模型,在早期使用 one-hot 来做,实际上,对于大的语料库来说需要很大的内存开销。所以就可以用word2vec预训练一个词向量模型。然后用他来当cnn的第一层,它不冻结参数,参与模型的训练,进行反向传播。当然pytorch有现成的embedding库,他是随机的权重,与语料库几乎无关的。所以训练起来慢,精度也不高。对于分类任务来说,多阶段的分类精度会更高。所以我们可以先训练一个词向量权重。
接着就用一个3层的cnn来搭建模型。为什么只有3层?因为cnn太容易过拟合了,模型加深会导致模型的误差升高,包括训练误差和验证误差。这就不是过拟合了,因为训练误差也升高了。所以用小一些的模型来做分类不失为一个好选择。当然可以用resnet把模型加深,模型越深,分类的精确度也越高,可以考虑用resnet50 或者 resnet100 来做。其实对于一个二分类来说,三层很够用了。
class TextCNN(nn.Module):
def __init__(self, vocab, embed_size, kernel_sizes, num_channels, weight):
super(TextCNN, self).__init__()
# 将预训练好的word2vec放进来
self.embedding = nn.Embedding.from_pretrained(torch.from_numpy(weight))
# 不参与训练的嵌入层
self.constant_embedding = nn.Embedding(len(vocab), embed_size)
self.dropout = nn.Dropout(0.5)
self.decoder = nn.Linear(sum(num_channels), 2)
# 时序最大池化层没有权重,所以可以共用一个实例
self.pool = GlobalMaxPool1d()
self.convs = nn.ModuleList() # 创建多个一维卷积层
for c, k in zip(num_channels, kernel_sizes):
self.convs.append(nn.Conv1d(in_channels=2 * embed_size,
out_channels=c,
kernel_size=k))
def forward(self, inputs):
# 将两个形状是(批量大小, 词数, 词向量维度)的嵌入层的输出按词向量连结
embeddings = self.embedding(inputs)
embeddings.to(device)
embeddings_2 = self.constant_embedding(inputs)
embeddings = torch.cat((
embeddings,
embeddings_2), dim=2) # (batch, seq_len, 2*embed_size)
# 根据Conv1D要求的输入格式,将词向量维,即一维卷积层的通道维(即词向量那一维),变换到前一维
embeddings.to(device)
embeddings = embeddings.permute(0, 2, 1)
# 对于每个一维卷积层,在时序最大池化后会得到一个形状为(批量大小, 通道大小, 1)的
# Tensor。使用flatten函数去掉最后一维,然后在通道维上连结
# embeddings.to(device)
encoding = torch.cat([self.pool(torch.relu(conv(embeddings))).squeeze(-1) for conv in self.convs], dim=1)
# 应用丢弃法后使用全连接层得到输出
outputs = self.decoder(self.dropout(encoding))
return outputs
主要介绍cnn的搭建。word2vec怎么使用网络上很多。解析中文的分词可以用 jieba库。还有记得做分词的时候把标点符号啥的去掉 可以用正则表达式。