TextCNN文本分类实现(主要是CNN模型的使用)

数据预处理:

加载中文文本数据并进行分词,以将文本转化为单词序列。可以使用分词库,如jieba。然后,将文本序列转换为词嵌入表示,如Word2Vec或GloVe。

填充序列:

对文本序列进行填充,确保它们具有相同的长度。这是因为卷积神经网络需要输入相同长度的数据。你可以选择一个固定的序列长度或根据最长文本来决定。

构建TextCNN模型:

TextCNN模型通常由以下几个组件组成:

  1. 卷积层:

使用一维卷积层来捕捉文本中的局部特征。您可以定义多个卷积核,每个卷积核用于检测不同的特征。卷积核的大小通常是一个重要的超参数,您可以尝试不同的大小。
一维卷积操作将创建新的特征表示。在PyTorch或TensorFlow中,您可以使用nn.Conv1d(PyTorch)或Conv1D层(TensorFlow)来定义卷积层。
激活函数:

通常在卷积层后应用激活函数,如ReLU(Rectified Linear Unit),以引入非线性性。这有助于模型学习更复杂的特征。在PyTorch中,您可以使用nn.ReLU,而在TensorFlow中,您可以使用tf.nn.relu。

  1. 池化层:

池化层用于减小特征映射的尺寸,同时保留最重要的信息。通常使用最大池化层(Max-Pooling)来选择每个特征映射的最大值。您可以在卷积层之后添加一维最大池化层,以减小特征映射的大小。

  1. 全连接层和输出层:

将卷积和池化层的输出展平,然后连接一个或多个全连接层,以学习文本的全局特征。最后,添加一个输出层,其神经元数等于分类类别的数量,使用softmax激活函数进行多类别分类。

训练模型:

使用已标记的文本数据集来训练TextCNN模型。定义损失函数(如交叉熵损失)和选择优化器(如SGD、Adam等)。训练模型以最小化损失函数。

评估模型:

使用测试数据集评估模型性能。常见的性能指标包括准确性、精确度、召回率和F1分数。这些指标将帮助您了解模型在分类任务中的表现。

预测:

使用经过训练的模型对新文本进行分类预测。将新文本输入模型,获取其分类标签。

模型优化:

可以尝试不同的超参数,如卷积核大小、卷积层数、池化层类型、学习率等,以优化模型性能。您还可以考虑使用预训练的词嵌入来改善性能。

代码实现

import os
import numpy as np
import torch
import torch.nn as nn
from  torch.utils.data import Dataset,DataLoader


def read_data(train_or_test, num=None):
    with open(os.path.join(train_or_test + ".txt"), encoding="utf-8") as f:
        all_data = f.read().split("\n")

    texts = []
    labels = []
    for data in all_data:
        if data:
            t, l = data.split("\t")
            texts.append(t)
            labels.append(l)
    if num == None:
        return texts, labels
    else:
        return texts[:num], labels[:num]

def built_curpus(train_texts,embedding_num): # 传入参数:语料
    word_2_index = {"<PAD>":0,"<UNK>":1}# 填充为零,未知为一
    for text in train_texts:
        for word in text:
            word_2_index[word] = word_2_index.get(word,len(word_2_index))
        # get方法get(key) 方法在 key(键)不在字典中时,可以返回默认值 None 或者设置的默认值。
    return word_2_index,nn.Embedding(len(word_2_index),embedding_num)
# 返回键值以及一个tensor类型的张量,这个张量代表了生成字典元素个数个嵌入向量,每个嵌入向量的维度是embedding_num

class TextDataset(Dataset):
    def __init__(self, all_text, all_label, word_2_index, max_len):
        self.all_text = all_text
        self.all_label = all_label
        self.word_2_index = word_2_index
        self.max_len = max_len

    def __getitem__(self, index):
        text = self.all_text[index][:self.max_len]
        label = int(self.all_label[index])

        text_idx = [self.word_2_index.get(i, 1) for i in text]
        text_idx = text_idx + [0] * (self.max_len - len(text_idx))

        text_idx = torch.tensor(text_idx).unsqueeze(dim=0)

        return text_idx, label

    def __len__(self):
        return len(self.all_text)


class Block(nn.Module):
    def __init__(self, kernel_s, embeddin_num, max_len, hidden_num):
        super().__init__()  # 标准写法
        # 卷积层
        self.cnn = nn.Conv2d(in_channels=1, out_channels=hidden_num, kernel_size=(
        kernel_s, embeddin_num))  # 1 * 1 * 7 * 5 (batch *  in_channel * len * emb_num )
        # 激活层(采用Relu)
        self.act = nn.ReLU()
        # 池化层(取最大值)
        self.mxp = nn.MaxPool1d(kernel_size=(max_len - kernel_s + 1))

    # 进行三步自动化运算
    def forward(self, batch_emb):  # 1 * 1 * 20 * 50 (batch *  in_channel * len * emb_num )
        c = self.cnn.forward(batch_emb)
        a = self.act.forward(c)
        a = a.squeeze(dim=-1)
        m = self.mxp.forward(a)
        m = m.squeeze(dim=-1)
        return m


class TextCNNModel(nn.Module):
    def __init__(self, emb_matrix, max_len, class_num, hidden_num):
        super().__init__()
        self.emb_num = emb_matrix.weight.shape[1]
        # 第二个值为文字维度,一直对应到语料库第二个返回值的第二个元素,承接上文提到的nn.embedding函数的使用与返回结果

        self.block1 = Block(2, self.emb_num, max_len, hidden_num)
        self.block2 = Block(3, self.emb_num, max_len, hidden_num)
        self.block3 = Block(4, self.emb_num, max_len, hidden_num)
        self.block4 = Block(5, self.emb_num, max_len, hidden_num)  # 每次取词,词的维度
        # 构建每一个block

        self.emb_matrix = emb_matrix

        self.classifier = nn.Linear(hidden_num * 4, class_num)  # 2 * 3
        self.loss_fun = nn.CrossEntropyLoss()

    def forward(self, batch_idx, batch_label=None):  # 可以选择不传label值,就是训练的情况
        batch_emb = self.emb_matrix(batch_idx)  # 根据id号找到相应生成的embedding
        b1_result = self.block1.forward(batch_emb)
        b2_result = self.block2.forward(batch_emb)
        b3_result = self.block3.forward(batch_emb)
        b4_result = self.block4.forward(batch_emb)

        feature = torch.cat([b1_result, b2_result, b3_result, b4_result], dim=1)  # 1* 6 : [ batch * (3 * 2)]
        pre = self.classifier(feature)

        if batch_label is not None:  # 有标签代表是训练
            loss = self.loss_fun(pre, batch_label)
            return loss
        else:  # 没有标签代表是预测
            return torch.argmax(pre, dim=-1)  # 返回概率最大的那个


if __name__ == "__main__":
    train_text, train_label = read_data(r"E:\技能学习\AI\TextCNN文本分类实现\train")
    dev_text, dev_label = read_data(r"E:\技能学习\AI\TextCNN文本分类实现\dev")

    embedding = 50
    max_len = 20
    batch_size = 200
    epoch = 200
    lr = 0.001
    hidden_num = 2  # 输出通道
    class_num = len(set(train_label))
    device = "cuda:0" if torch.cuda.is_available() else "cpu"

    word_2_index, words_embedding = built_curpus(train_text, embedding)  # 返回的字的编号和每个字所对应的维度向量

    train_dataset = TextDataset(train_text, train_label, word_2_index, max_len)
    train_loader = DataLoader(train_dataset, batch_size, shuffle=False)

    dev_dataset = TextDataset(dev_text, dev_label, word_2_index, max_len)
    dev_loader = DataLoader(dev_dataset, batch_size, shuffle=False)

    model = TextCNNModel(words_embedding, max_len, class_num, hidden_num).to(device)
    # 进入模型进行forward
    opt = torch.optim.AdamW(model.parameters(), lr=lr)  # 进行优化

    for e in range(epoch):
        for batch_idx, batch_label in train_loader:
            batch_idx = batch_idx.to(device)
            batch_label = batch_label.to(device)
            loss = model.forward(batch_idx, batch_label)
            loss.backward()  # 反向传播找到合适的参数
            opt.step()
            opt.zero_grad()

        print(f"loss:{loss:.3f}")  # 损失值

        right_num = 0  # 统计测试集正确的数量
        for batch_idx, batch_label in dev_loader:
            batch_idx = batch_idx.to(device)
            batch_label = batch_label.to(device)
            pre = model.forward(batch_idx)
            right_num += int(torch.sum(pre == batch_label))  # 统计测试集正确的数量
        MODEL_DIR = r'E:/技能学习/AI/TextCNN文本分类实现/output/提问训练/'
        torch.save(model, MODEL_DIR + f'model_{e}.pth')  # 将模型存储到pth文件中
        print(f"acc = {right_num / len(dev_text) * 100:.2f}%")  # 输出正确率

本文参考

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值