TextCNN文本分类实现

该博客介绍了一个使用CNN模型进行文本分类的项目。首先,通过`read_data()`函数读取数据,然后利用`built_curpus()`创建语料库。接着,定义`TextDataset`类处理数据集,并构建卷积核`Block`。`TextCNNModel`类继承自`nn.Module`,包含了多个卷积层和全连接层。在主函数中,模型在训练集和验证集上进行训练,并保存模型。整个流程展示了如何使用PyTorch构建和训练一个文本分类模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:项目基于CNN模型,对输入问题进行训练,让机器可以识别出问题的类别从而通过相应类别查询所要寻找的数据

有关于数据部分的链接:https://pan.baidu.com/s/16ZR6LVVLP-_4mXLJG_aD4g?pwd=1111

你需要把它放在所建立的py文件通文件夹下,

原因如是

 注:有关浅谈和一些题外话仅仅作为学习过程中的测试用,代码中不加入无关紧要

0.导入包

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

1.read_data()#读取数据文件

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("")

    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]

2.built_curpus()#建立语料库

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)

2.1浅谈一下get的作用以及用法:

2.1.1一个例子

tinydict = {'Name': 'Runoob', 'Age': 27}

print ("Age : %s" %  tinydict.get('Age'))

# 没有设置 Sex,也没有设置默认的值,输出 None
print ("Sex : %s" %  tinydict.get('Sex'))  

# 没有设置 Salary,输出默认的值  0.0
print ('Salary: %s' % tinydict.get('Salary', 0.0))

2.1.2get() 方法 Vs dict[key] 访问元素区别

get(key) 方法在 key(键)不在字典中时,可以返回默认值 None 或者设置的默认值。

dict[key] 在 key(键)不在字典中时,会触发 KeyError 异常。

2.1.3嵌套字典使用

get() 方法对嵌套字典的使用方法如下:

tinydict = {'RUNOOB' : {'url' : 'www.runoob.com'}}

res = tinydict.get('RUNOOB', {}).get('url')
# 输出结果
print("RUNOOB url 为 :  %s" % str(res))

以上实例输出结果为:
RUNOOB url 为 :  www.runoob.com

3.class TextDataset()#建立数据集处理数据

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)

4.class Block()#一个卷积核(包含卷积层、激励层、池化层)

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

4.1浅谈conv1d和conv2d的区别

这篇博客既详细又好懂PyTorch中的nn.Conv1d与nn.Conv2d - 简书

conv1d简单来说一个句子没有高度,只有宽度,宽度指的是每一个字的维度(在本项目中,将高度设置为句子里的字数,宽度设置为字的维度,当然由于这时候channel没了,默认设置为1)

conv2d指的是一张图片有高度也有宽度,当然通道数还是可以类比RGB

另外插一句,本项目虽是文本的处理,但是然输入了四维数据(原作者不想改了,等我想改的时候再改吧)conv2d也而不是不能用。

nn.Conv2d(in_channels=1,out_channels=hidden_num,kernel_size=(kernel_s,embeddin_num))     传入的参数为输入channel,输出channel,卷积核大小(每次取字数,字的维度),见下图就明白了

5.class TextCNNModule(nn.Module)#继承自nn.module的CNN模型

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)#返回概率最大的那个

6.主函数

if __name__ == "__main__":
    train_text,train_label = read_data("book")
    dev_text,dev_label = read_data("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 = '../output/提问训练/'
        torch.save(model, MODEL_DIR+f'model_{e}.pth')#将模型存储到pth文件中
        print(f"acc = {right_num/len(dev_text)*100:.2f}%")#输出正确率

### TextCNN 文本分类 Python 实现 #### 数据准备与预处理 为了构建TextCNN模型,首先需要准备好用于训练的数据集并对其进行必要的预处理操作。这通常涉及到读取原始文本文件、清理噪声以及分词等工作。对于中文语料来说,还需要考虑去除停用词等问题。 ```python import jieba from sklearn.model_selection import train_test_split from tensorflow.keras.preprocessing.sequence import pad_sequences from gensim.models.word2vec import Word2Vec # 假设已经有一个列表形式的文档集合docs和对应的标签labels def preprocess_data(docs, labels): # 使用结巴分词器对每篇文档进行切分 docs_cut = [' '.join(jieba.cut(doc)) for doc in docs] # 将数据划分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split( docs_cut, labels, test_size=0.2) sentences = [doc.split() for doc in X_train] # 训练word2vec模型 wv_model = Word2Vec(sentences=sentences, size=100, window=5, min_count=1, workers=4) word_index = {t[0]: i+1 for i,t in enumerate(wv_model.wv.vocab.items())} sequences = [[word_index.get(word, 0) for word in sentence] for sentence in sentences] padded_sequences = pad_sequences(sequences, maxlen=maxlen) return (padded_sequences, y_train), \ ([pad_sequences([[word_index.get(word, 0) for word in d.split()]]) for d in X_test], y_test) ``` 此部分代码完成了从原始文本到适合输入给后续神经网络层的形式转换过程[^1]。 #### 构建TextCNN模型架构 接下来就是按照经典的TextCNN框架搭建相应的深度学习模型了。这里采用了Keras库来简化这一流程: ```python from tensorflow.keras.layers import Input, Embedding, Conv1D, GlobalMaxPooling1D, Dense, Concatenate from tensorflow.keras.models import Model embedding_dim = 100 max_features = len(word_index)+1 filter_sizes = [3, 4, 5] num_filters = 100 drop_rate = 0.5 dense_units = 4 def create_text_cnn(): inputs = Input(shape=(None,)) embedding_layer = Embedding(input_dim=max_features, output_dim=embedding_dim)(inputs) conv_blocks = [] for sz in filter_sizes: conv = Conv1D(filters=num_filters, kernel_size=sz, padding="valid", activation='relu', strides=1)(embedding_layer) max_pooling = GlobalMaxPooling1D()(conv) conv_blocks.append(max_pooling) z = Concatenate()(conv_blocks) if len(conv_blocks)>1 else conv_blocks[0] z = Dropout(drop_rate)(z) outputs = Dense(dense_units, activation='softmax')(z) model = Model(inputs, outputs) model.compile(loss='sparse_categorical_crossentropy', optimizer='adam') return model ``` 上述代码片段展示了如何创建一个多通道的一维卷积核来进行局部特征抽取,并通过最大池化汇聚全局信息;最后经过Dropout防止过拟合并通过全连接层完成类别预测任务[^2]。 #### 模型训练与评估 有了前面两步打下的基础之后就可以正式开始调用fit函数启动迭代优化程序啦! ```python model = create_text_cnn() history = model.fit(padded_sequences, np.array(y_train), batch_size=batch_size, epochs=epochs, validation_split=0.1) loss, accuracy = model.evaluate(test_padded_sequences, np.array(y_test)) print(f'Test Accuracy: {accuracy:.4f}') ``` 以上便是完整的TextCNN文本分类Python实现教程及示例代码[^5]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值