文本分类任务(weibo_senti_100k为数据集)

weibo_senti_100k为数据集:ChineseNlpCorpus/datasets/weibo_senti_100k/intro.ipynb at master · SophonPlus/ChineseNlpCorpus · GitHub

hit_stopwords.txt:

hit_stopwords.txt · master · mirrors / goto456 / stopwords · GitCode

# ---------------------------数据准备------------------------------------------#
# ---------------------data_processing.py------------------------------------#

import jieba
# ---------------------------------------------------------------------#
# data_path:微博情感分析数据集的文件路径。
# data_stop_path:停用词文件的路径。
# data_list:从数据集文件中读取的数据列表,每个元素代表一条微博数据。
# stops_word:停用词列表,包含在该列表中的词语会被过滤掉。
# voc_dict:词汇表字典,包含词语及其对应的频次信息。
# min_seq:词语最小出现频次阈值,只有高于这个阈值的词语才会被保留在新的词汇表中。
# top_n:需要保留的高频词语数量。
# UNK:未登录词(Unknown)的标记字符串,用于表示不在词汇表中的词语。
# PAD:填充序列的标记字符串,用于标记长度不足的序列。
# ---------------------------------------------------------------------#
data_path = "weibo_senti_100k.csv"
data_stop_path = "hit_stopwords.txt"
data_list = open(data_path,encoding='utf-8').readlines()[1:]
stops_word = open(data_stop_path,encoding='utf-8').readlines()
stops_word = [line.strip() for line in stops_word]
stops_word.append(" ")
stops_word.append("\n")
voc_dict = {}
min_seq = 1
top_n = 1000
UNK="<UNK>"
PAD = "<PAD>"
# ------------------------------------------------------------------#
# data_list:数据列表,包含要处理的文本数据。
# label:当前项的标签,即第一个元素。
# content:当前项的内容,即第三个元素去除前后空格后的字符串。
# seg_list:使用结巴分词库将 content 进行分词后的结果,以生成一个分词列表。
# seg_res:用于存储去除停用词后的分词结果列表。
# stops_word:停用词列表,用于过滤掉常见的无意义词语。
# voc_dict:词汇表字典,用于统计每个词语在数据集中出现的频次。
# ------------------------------------------------------------------#
for item in data_list[:1000]:
    label = item[0]
    content = item[2:].strip()
    seg_list = jieba.cut(content, cut_all=False)
    seg_res = []

    for seg_item in seg_list:
        print(seg_item)
        if seg_item in stops_word:
            continue
        seg_res.append(seg_item)
        if seg_item in voc_dict.keys():
            voc_dict[seg_item] = voc_dict[seg_item] + 1
        else:
            voc_dict[seg_item] = 1

    print(content)
    print(seg_res)
# ----------------------------------------------------------------------------#
# voc_dict:词汇表字典,包含词语及其对应的频次信息。
# min_seq:最小出现频次阈值,只有高于这个阈值的词语才会被保留在新的词汇表中。
# top_n:需要保留的高频词语数量。
# voc_list:通过筛选出频次高于 min_seq 的词语生成的词汇列表。
# lambda x:x[1]:用于指定排序方式的匿名函数,按照词频进行降序排列。
# voc_dict:经过筛选和排序后的新词汇表字典。将高频词语作为键,以它们在排序后的列表中的索引作为值。
# UNK 和 PAD:特殊标记的词语,通常用于处理未登录词(UNK)和填充序列(PAD)。
# ff:打开用于写入词汇表的文件。
# item:循环遍历词汇表字典中的每一项。
# ff.writelines:将词汇表字典中的键值对以 <词语>,<索引> 的格式写入文件中。
# ff.close():关闭文件。
# ----------------------------------------------------------------------------#
voc_list = sorted([_ for _ in voc_dict.items() if _[1] > min_seq],
                  key=lambda x:x[1], reverse=True)[:top_n]
voc_dict = {word_count[0]: idx for idx,word_count in enumerate(voc_list)}
voc_dict.update({UNK:len(voc_dict),PAD:len(voc_dict) + 1})

print(voc_dict)

ff = open("dict","w")
for item in voc_dict.keys():
    ff.writelines("{},{}\n".format(item,voc_dict[item]))
ff.close()
代码的主要流程如下:
    读取微博情感分析数据集文件,并将数据存储在 data_list 列表中。
    读取停用词文件,并将停用词存储在 stops_word 列表中。
    将特殊的空格和换行符添加到停用词列表中。
    初始化词汇表字典 voc_dict,用于存储词语及其频次信息。
    遍历每个微博数据项:
    提取标签(情感分类)和内容,并对内容进行分词。
    将分词结果保存在 seg_res 列表中,并统计每个词语的频次。
    将词语及其频次更新到词汇表字典 voc_dict 中。
    根据词频信息筛选出频次高于 min_seq 的词语,并按照词频降序排列,保留前 top_n 个词语。
    根据筛选后的词语列表生成新的词汇表字典 voc_dict,键为词语,值为对应的索引。
    添加未登录词(UNK)和填充序列标记(PAD)到词汇表字典 voc_dict 中,分别赋予它们索引值。
    打印生成的新词汇表字典 voc_dict。
    打开文件 "dict",将词汇表字典中的每个键值对写入文件中,每行格式为 <词语>,<索引>。
    关闭文件。
这段代码的作用是构建一个词汇表字典,该词汇表包含微博情感分析数据集中出现频次较高的词语,并将该词汇表保存到文件中供后续使用。

 运行结果:

 

# ---------------------------加载数据集---------------------------------------#
# ---------------------------datasets.py------------------------------------#

from torch.utils.data import Dataset,DataLoader
import jieba
import numpy as np
from configs import Config

# -----------------------------------------------#
# voc_dict_path:词汇表文件的路径
# -----------------------------------------------#
def read_dict(voc_dict_path):
    voc_dict = {}
    dict_list = open(voc_dict_path,encoding='gbk').readlines()
    for item in dict_list:
        item = item.split(",")
        voc_dict[item[0]] = int(item[1].strip())
    return voc_dict

# -----------------------------------------------#
# data_path:数据集文件的路径。
# data_stop_path:停用词文件的路径
# data:用于存储处理后的数据的列表。
# max_len_seq:用于记录最长句子的长度
# -----------------------------------------------#
def load_data(data_path,data_stop_path):
    data_list = open(data_path,encoding='utf-8').readlines()[1:]
    stops_word = open(data_stop_path,encoding='utf-8').readlines()
    stops_word = [line.strip() for line in stops_word]
    stops_word.append(" ")
    stops_word.append("\n")
    voc_dict = {}
    data = []
    max_len_seq = 0
    np.random.shuffle(data_list)
    for item in data_list[:1000]:
        label = item[0]
        content = item[2:].strip()
        seg_list = jieba.cut(content, cut_all=False)
        seg_res = []
        for seg_item in seg_list:
            if seg_item in stops_word:
                continue
            seg_res.append(seg_item)
            if seg_item in voc_dict.keys():
                voc_dict[seg_item] = voc_dict[seg_item] + 1
            else:
                voc_dict[seg_item] = 1
        if len(seg_res) > max_len_seq:
            max_len_seq = len(seg_res)
        data.append([label, seg_res])
    return data, max_len_seq

# -------------------------------------------------------------------------#
# self.data_path:用于保存数据文件路径的实例变量。
# self.data_stop_path:用于保存停用词文件路径的实例变量。
# self.voc_dict:用于保存词汇表字典的实例变量,通过调用 read_dict() 方法从 voc_dict_path 文件中读取。
# self.data:用于保存处理后的数据的实例变量,通过调用 load_data() 方法从 data_path 和 data_stop_path 加载。
# self.max_len_seq:用于保存最长句子的长度的实例变量。
# np.random.shuffle():随机打乱 self.data 列表中的数据顺序,以增加数据的随机性。
# __len__(self):返回数据集中样本的数量。
# __getitem__(self, item):根据索引 item 获取数据集中的一个样本。
# -------------------------------------------------------------------------#
class text_ClS(Dataset):
    def __init__(self, voc_dict_path,data_path,data_stop_path):
        self.data_path = data_path
        self.data_stop_path = data_stop_path
        self.voc_dict = read_dict(voc_dict_path)
        self.data, self.max_len_seq = \
            load_data(self.data_path,self.data_stop_path)

        np.random.shuffle(self.data)

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

    # ----------------------------------------------------------#
    # self:代表类的实例自身。
    # item:样本的索引,通过该索引可以获取数据集中的一个样本。
    # data:根据索引 item 从 self.data 列表中获取到的数据项。
    # label:将 data[0] 转换为整数类型后的标签。
    # word_list:data[1],即样本中的单词列表。
    # input_idx:用于保存将单词转换为对应索引后的结果的列表。
    # ----------------------------------------------------------#
    def __getitem__(self, item):
        data = self.data[item]
        label = int(data[0])
        word_list = data[1]
        input_idx = []
        for word in word_list:
            if word in self.voc_dict.keys():
                input_idx.append(self.voc_dict[word])
            else:
                input_idx.append(self.voc_dict["<UNK>"])
        if len(input_idx) < self.max_len_seq:
            input_idx += [self.voc_dict["<PAD>"]
                          for _ in range(self.max_len_seq - len(input_idx))]
        data = np.array(input_idx)
        return label, data

# ----------------------------------------------------------#
# config.is_shuffle:是否对数据进行随机打乱。
# ----------------------------------------------------------#
# def data_loader(data_path,data_stop_path,dict_path):
#     dataset = text_ClS(dict_path,data_path,data_stop_path)
#     return DataLoader(dataset, batch_size=10, shuffle=True)
def data_loader(dataset, config):
    return DataLoader(dataset, batch_size=config.batch_size, shuffle=config.is_shuffle)

if __name__ == '__main__':
    data_path = "weibo_senti_100k.csv"
    data_stop_path = "hit_stopwords.txt"
    dict_path = "dict"
    train_dataloader = data_loader(data_path,data_stop_path,dict_path)
    for i, batch in enumerate(train_dataloader):
        print(batch)
这段代码的主要流程如下:
    导入必要的库和模块。
    定义函数read_dict(voc_dict_path),用于从词汇表文件中读取词汇表字典。
    定义函数load_data(data_path, data_stop_path),用于加载数据集文件和停用词文件,并进行数据处理。
    定义类text_ClS(Dataset),继承自torch.utils.data.Dataset,用于创建自定义的数据集。
        初始化方法__init__(self, voc_dict_path, data_path, data_stop_path),用于初始化实例变量,并调用函数read_dict()和load_data()加载数据集和词汇表。
        方法__len__(self),用于返回数据集中样本的数量。
        方法__getitem__(self, item),根据索引item获取数据集中的一个样本。
    定义函数data_loader(dataset, config),用于创建数据加载器。
    在主程序中,设置数据集和配置信息。然后通过调用data_loader()函数创建数据加载器,并使用for循环迭代打印每个batch的数据。
总体来说,这段代码实现了对文本数据集的加载、预处理和批量加载,并提供了方便的接口用于训练和测试模型。

运行结果:

 

 

# ---------------------------搭建模型结构---------------------------------------#
# ---------------------------models.py---------------------------------------#

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


# --------------------------------------------------------------------------------#
# self.embedding: 嵌入层,用于将词汇索引映射为词向量。参数解释:
#         config.n_vocab: 词汇表大小,用于指定嵌入层的输入维度。
#         config.embed_size: 词向量的维度。
#         padding_idx=config.n_vocab -1: 用于指定填充索引的值,当输入序列中有填充时,对应位置的词向量为0。
# self.lstm: LSTM层,用于对序列进行建模和特征提取。参数解释:
#         config.embed_size: 输入序列的维度,即词向量的维度。
#         config.hidden_size: LSTM隐藏状态的维度,控制网络的容量和记忆能力。
#         config.num_layers: LSTM层的层数。
#         bidirectional=True: 是否为双向LSTM,如果为True,则会返回前向和后向两个方向的隐藏状态。
#         batch_first=True: 输入数据的维度顺序,默认为(batch_size, seq_length, input_size)。
#         dropout=config.dropout: Dropout层的丢弃概率,用于防止过拟合。
# self.maxpooling: 池化层,用于对序列进行池化操作,提取序列的关键信息。参数解释:
#         config.pad_size: 输入序列的填充长度,用于指定池化窗口大小。
# self.fc: 全连接层,用于将池化后的特征映射为分类结果。参数解释:
#         config.hidden_size * 2 + config.embed_size: 输入特征维度,池化层输出的特征与词向量拼接后的维度。
#         config.num_classes: 输出的类别数量。
# self.softmax: Softmax层,用于对模型输出的结果进行归一化,生成概率分布。
# --------------------------------------------------------------------------------#
class Model(nn.Module):
    def __init__(self,config):
        super(Model,self).__init__()
        self.embeding = nn.Embedding(config.n_vocab,config.embed_size,
                                     padding_idx=config.n_vocab -1)
        self.lstm = nn.LSTM(config.embed_size, config.hidden_size,
                            config.num_layers, bidirectional=True,
                            batch_first=True, dropout=config.dropout)
        self.maxpooling = nn.MaxPool1d(config.pad_size)
        self.fc = nn.Linear(config.hidden_size * 2 + config.embed_size
                            , config.num_classes)
        self.softmax = nn.Softmax(dim=1)

    def forward(self,x):
        embed = self.embeding(x) #输出为[batchsize, seqlen, embed_size] 标准RNN网络的输入
        out, _ = self.lstm(embed)
        out = torch.cat((embed,out),2)
        out = F.relu(out)
        out = out.permute(0,2,1)#交换维度
        out = self.maxpooling(out).reshape(out.size()[0],-1)#转化为2维tensor
        print(out.size())
        out = self.fc(out)
        out = self.softmax(out)
        return out

if __name__ == '__main__':
    from configs import Config
    cfg = Config()
    cfg.pad_size = 640
    model_textcls = Model(config=cfg)
    input_tensor = torch.tensor([i for i in range(640)]).reshape([1, 640])
    out_tensor = model_textcls.forward(input_tensor)
    print(out_tensor.size())
    print(out_tensor)

 

这段代码的主要流程如下:
    导入必要的库和模块。
    定义类Model(nn.Module),继承自torch.nn.Module,用于构建文本分类模型。
        初始化方法__init__(self, config),用于初始化模型的各个组件,包括Embedding层、LSTM层、MaxPooling层、全连接层等。
        方法forward(self, x),定义了模型的前向传播过程。首先通过Embedding层将输入x转化为词嵌入表示。然后将词嵌入表示输入到LSTM层中进行序列建模。接着将词嵌入表示与LSTM的输出拼接在一起,并通过ReLU激活函数进行非线性变换。将拼接后的张量进行维度的变换和池化操作,最终将其转换为二维张量。然后将二维张量输入到全连接层中,并使用Softmax函数进行分类。
    在主程序中,导入配置信息Config。创建模型对象model_textcls,并给定一个输入张量input_tensor。调用模型的forward()方法对输入张量进行前向传播得到输出张量out_tensor,然后打印其大小和内容。
总体来说,这段代码定义了一个文本分类模型,包括嵌入层、LSTM层、池化层和全连接层,并实现了模型的前向传播过程。在主程序中,可以通过给定输入张量来获取模型的输出张量。

 运行结果:

 

 

# ---------------------------搭建模型结构---------------------------------------#
# ---------------------------configs.py---------------------------------------#

import torch

# -------------------------------------------------------------------------------#
# self.n_vocab:词汇表的长度,即词汇表中不同词汇的数量。
# self.embed_size:嵌入层的维度,用于将输入的离散词汇转换为连续的词嵌入表示。
# self.hidden_size:LSTM隐藏状态的维度,用于捕获输入序列的上下文信息。
# self.num_layers:LSTM层数,表示LSTM中LSTM单元的数量。
# self.dropout:在LSTM中应用的丢弃率,用于减少过拟合。
# self.num_classes:输出的类别数量,用于分类任务的类别预测。在这里是二分类问题,所以该值为2。
# self.pad_size:填充后的序列长度,保证所有输入序列具有相同的长度。
# self.batch_size:每个训练批次中的样本数量。
# self.is_shuffle:是否在每个训练批次之前对样本进行洗牌(随机排序)。
# self.learn_rate:学习率,用于控制模型参数更新的步长。
# self.num_epochs:训练的迭代次数,即遍历整个训练数据集的次数。
# self.devices:设备选择,通过判断是否有GPU可用来确定使用CPU还是GPU进行训练。
# -------------------------------------------------------------------------------#
class Config():
    def __init__(self):
        self.n_vocab = 1002 #字典长度
        self.embed_size = 128
        self.hidden_size = 128
        self.num_layers = 3
        self.dropout = 0.8
        self.num_classes = 2 #二分类问题
        self.pad_size = 32
        self.batch_size = 256
        self.is_shuffle = True
        self.learn_rate = 0.001
        self.num_epochs = 100
        self.devices = torch.device('cuda' if torch.cuda.is_available() else 'cpu')


# ---------------------------训练模型--------------------------------------#
# -------------------------run_train.py----------------------------------#

import torch
import torch.nn as nn
from torch import optim
from models import Model
from datasets import data_loader, text_ClS
from configs import Config

cfg = Config()
data_path = "weibo_senti_100k.csv"
data_stop_path = "hit_stopwords.txt"
dict_path = "dict"

dataset = text_ClS(dict_path, data_path, data_stop_path)
train_dataloader = data_loader(dataset, cfg)
cfg.pad_size = dataset.max_len_seq
model_text_cls = Model(cfg)
model_text_cls.to(cfg.devices)
loss_func = nn.CrossEntropyLoss()
optimizer = optim.Adam(model_text_cls.parameters(),lr=cfg.learn_rate)

for epoch in range(cfg.num_epochs):
    for i, batch in enumerate(train_dataloader):
        label, data = batch
        data = torch.tensor(data).to(cfg.devices)
        label = torch.tensor(label,dtype=torch.int64).to(cfg.devices)

        optimizer.zero_grad()
        pred = model_text_cls.forward(data)
        loss_val = loss_func(pred, label)

        # print(pred)
        # print(label)

        print("epoch is {}, ite is {}, val is {}".format(epoch,i,loss_val))
        loss_val.backward()
        optimizer.step()

    if epoch % 100 == 0:#每100次迭代存储一次模型
        torch.save(model_text_cls.state_dict(),"models_100.pth")


这段代码的主要流程如下:
    导入必要的库和模块。
    导入自定义的模型(Model)、数据集类(text_ClS)、数据加载器(data_loader)和配置信息(Config)。
    根据配置信息创建一个数据集对象dataset,并指定数据集的相关路径。
    使用数据集对象创建训练数据加载器train_dataloader,用于批量加载训练数据。
    根据配置信息创建文本分类模型对象model_text_cls,并将其移动到指定的设备上。
    定义损失函数loss_func,采用交叉熵损失函数。
    定义优化器optimizer,采用Adam优化算法,并将模型的参数传递给优化器。
    进行训练的循环,按照指定的迭代次数(cfg.num_epochs)进行循环。
        在每个epoch中,遍历训练数据加载器中的每个batch。
        从batch中获取标签(label)和数据(data)。
        将数据转换为张量类型,并移动到指定的设备上;将标签转换为torch.int64类型,并移动到指定的设备上。
        清空优化器的梯度。
        使用模型对数据进行前向传播得到预测结果pred。
        计算预测结果和标签之间的损失值loss_val。
        输出当前epoch、iteration和损失值。
        对损失值进行反向传播,并更新模型的参数。
        如果当前epoch是10的倍数,则保存模型的权重至文件中。
总体来说,这段代码实现了基于给定数据集进行文本分类任务的训练过程。通过加载数据集、创建模型、定义损失函数和优化器,循环遍历训练数据并进行前向传播、计算损失、反向传播和参数更新。在每个epoch结束时,将模型的权重保存到文件中。

 

运行结果:

 

 

# ---------------------------测试模型----------------------------------#
# ---------------------------test.py----------------------------------#

import torch
import torch.nn as nn
from torch import optim
from models import Model
from datasets import data_loader, text_ClS
from configs import Config

cfg = Config()

data_path = "weibo_senti_100k.csv"
data_stop_path = "hit_stopwords.txt"
dict_path = "dict"

dataset = text_ClS(dict_path, data_path, data_stop_path)
train_dataloader = data_loader(dataset, cfg)
cfg.pad_size = dataset.max_len_seq
model_text_cls = Model(cfg)
model_text_cls.to(cfg.devices)
model_text_cls.load_state_dict(torch.load("models_100.pth"))

for i, batch in enumerate(train_dataloader):
    label, data = batch
    data = torch.tensor(data).to(cfg.devices)
    label = torch.tensor(label,dtype=torch.int64).to(cfg.devices)
    pred_softmax = model_text_cls.forward(data)

    print(pred_softmax)
    print(label)
    pred = torch.argmax(pred_softmax, dim=1)
    print(pred)

    #统计准确率
    out = torch.eq(pred,label)
    print(out.sum() * 1.0 / pred.size()[0])


这段代码的主要流程如下:
    导入必要的库和模块。
    导入自定义的模型(Model)、数据集类(text_ClS)、数据加载器(data_loader)和配置信息(Config)。
    根据配置信息创建一个数据集对象dataset,并指定数据集的相关路径。
    使用数据集对象创建训练数据加载器train_dataloader,用于批量加载训练数据。
    根据配置信息创建文本分类模型对象model_text_cls,并将其移动到指定的设备上。
    使用torch.load()方法加载之前保存的模型权重文件"models/10.pth"。
    进行验证的循环,遍历训练数据加载器中的每个batch。
        从batch中获取标签(label)和数据(data)。
        将数据转换为张量类型,并移动到指定的设备上;将标签转换为torch.int64类型,并移动到指定的设备上。
        使用模型对数据进行前向传播得到预测结果pred_softmax。
        输出预测结果、标签和通过argmax函数计算的预测类别。
        统计准确率,通过torch.eq()函数判断预测类别和标签是否相等,并计算正确预测的数量,再除以总样本数来得到准确率。
总体来说,这段代码加载之前保存的模型权重文件,并使用模型对验证数据进行预测,输出预测结果、标签和准确率。这段代码可以用于在训练过程中进行模型的验证和性能评估。

 运行结果:

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭毓众

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值