textcnn-文本分类


参考资料:github: textcnn.

数据处理

参考另一条博客的利用torchtext处理文本分类数据
torchtext: 数据处理.
定义分词函数,这里用jieba分词工具自定义分词函数

def word_cut(text):
    text = regex.sub(' ', text)
    return [word for word in jieba.cut(text) if word.strip()]

根据文件路径和预定义的文本预处理,生成文本数据集

def get_dataset(args, text_field, label_field):
    text_field.tokenize = word_cut
    train, val = data.TabularDataset.splits(
        path=args.path, format='tsv', skip_header=True,
        train='train.tsv', validation='valid.tsv',
        fields=[
            ('label', label_field),
            ('text', text_field)
        ]
    )
    return train, val

定义迭代器生成迭代数据集来给模型输送批量数据
这里记得生成词表,然后统计词表数量为了后面embedding层做准备

def get_iter(args):
    LABEL = data.Field(sequential=False, unk_token=None)
    TEXT = data.Field(sequential=True, tokenize=word_cut)
    train_iter, val_iter = load_dataset(TEXT, LABEL, args)
    args.vocabulary_size = len(TEXT.vocab)
    args.class_num = len(LABEL.vocab)
    args.vocab = TEXT.vocab.stoi
    args.label_vocab = LABEL.vocab.stoi
    return train_iter, val_iter

模型搭建

基于cnn搭建一个文本分类的模型,理论知识可以看这篇论文
Convolutional Neural Networks for Sentence Classification.
模型的架构定义,首先设置embedding层的维度,行为词表数量,词嵌入的维度自己定义。然后定义cnn卷积层,分别定义三个卷积,分别是1,2,3,最后定义dropout层。最后通过线性层来将数据转化为为类别数量的向量维度。

class TextCNN(nn.Module):
    def __init__(self, args):
        super(TextCNN, self).__init__()
        class_num = args.class_num
        filter_num = args.filter_num
        filter_sizes = args.filter_sizes
        vocabulary_size = args.vocabulary_size
        embedding_dimension = args.embedding_dim
        self.embedding = nn.Embedding(vocabulary_size, embedding_dimension)
        self.convs = nn.ModuleList(
            [nn.Conv2d(1, filter_num, (size, embedding_dimension)) for size in filter_sizes])
        self.dropout = nn.Dropout(args.dropout)
        self.fc = nn.Linear(len(filter_sizes) * filter_num, class_num)

接着定义forward方法,首先,接受的数据定义是(batch_size, stence_len)维度的,经过embedding层,成为(batch_size, stence_len, embedding_dim)。这里提一下这个unsqueeze和sqeeze这两个函数,是为了调整张量数据维度的函数,unsqueeze就是增添多一个维度为1,而sqeeze就是将某个值为1的维度直接消除。这里为什么在第二个维度加1?因为这里设置了同一结构的卷积核是100个,那卷积之后的结果就有一百种不同的结果,需要多一个维度存储。然后卷积之后为什么要取消最后一个维度?因为文本的cnn卷积的dim设置就是embedding_dim,那卷积之后最后一维就为1,所以直接消除。最后再将三种不同结构的卷积核的结果连接起来。

    def forward(self, x):
        x = self.embedding(x)
        x = x.unsqueeze(1)
        x = [F.relu(conv(x)).squeeze(3) for conv in self.convs]
        x = [F.max_pool1d(item, item.size(2)).squeeze(2) for item in x]
        x = torch.cat(x, 1)
        x = self.dropout(x)
        output = self.fc(x)
        return output

训练模型

模型和数据都定义好了,我们需要的是训练了。
首先要定义一个优化器,它能设置梯度更新的方式,然后让它来帮助我们更行模型的参数。然后将数据喂给模型得到最后的结果再输入到损失函数中去,这里定义的是交叉熵损失函数。注意这里优化器的一开始梯度归0和最后的梯度更新。

def train(train_iter, model, args):
    optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)
    steps = 0
    model.to(torch.device("cpu"))
    model.train()
    for epoch in tqdm(range(1, args.epochs + 1), desc='epoch:', colour='BLUE'):
        for batch in train_iter:
            feature, target = batch.text, batch.label
            feature.data.t_()
            optimizer.zero_grad()
            output = model(feature)
            loss = F.cross_entropy(output, target)
            loss.backward()
            optimizer.step()
            steps += 1
    save(model, "save_model", 'best2', steps)

接着定义模型的存储方法,主要还是torch.save方法。

def save(model, save_dir, save_prefix, steps):
    if not os.path.isdir(save_dir):
        os.makedirs(save_dir)
    save_prefix = os.path.join(save_dir, save_prefix)
    save_path = '{}_steps_{}.pt'.format(save_prefix, steps)
    torch.save(model.state_dict(), save_path)

未完待续。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值