基于MLP的文本情绪分类(深度学习课设)

 需要源码或者有任何问题都可以私信或者评论,博主会定期查看的,希望本篇文章可以帮到你们!

源码以及数据集下载链接(有问题可以直接私信):https://download.csdn.net/download/m0_63975371/87971178

这个模型是一个基于MLP的简单文本情绪分类模型,使用了线性层、激活函数和Softmax函数构建网络结构。通过交叉熵损失函数进行训练,并使用Adam优化算法自动调节学习率。训练过程中记录了损失值,并在每个3000步后对校验集进行验证。该模型可以用于对文本情绪进行分类,并评估模型的准确率和损失值。其中包含数据收集、数据预处理、构建模型、训练模型、测试模型、观察模型表现、保存模型

1.题目概述

编写一个爬虫程序,并使用它从在线商城的大量商品评论中抓取评论文本以及分类标签(评论得分)。
将根据文本的词袋(Bag of Word)模型来对文本进行建模,然后利用一个神经网络来对这段文本进行分类。识别一段文字中的情绪,从而判断出这句话是称赞还是抱怨。

2.数据集介绍

数据集原本应该从在线商城抓取评论数据,我们设定只获得5星的商品评价,即 score=5 表示好的评分。

由于现在的许多网页设置了反爬虫,故直接采用了网络下载的数据集进行训练和检测。

将数据集分为了good和bad两个数据集,分别存放五星好评以及一星的差评。

3.数据预处理

编写的函数,以句子为单位,将所有的积极情感的评论文本,全部转化为句子向量,并保存到数据集 dataset 中。同样对于消极情绪的评论如法炮制,全部转化为句子向量并保存到数据集 dataset 中。

定义函数:

filter_punc:该函数使用正则表达式从句子中删除标点符号和无意义字符。

prepare_data:该函数通过扫描包含正向和负向评论的文本文件来准备数据。它对句子进行分词处理,如果指定了参数,则过滤掉标点符号,并建立单词及其频率的字典。

word2index:该函数根据字典中的单词返回其编码(索引)。如果单词不存在,则返回-1。

index2word:该函数根据给定的编码查询字典中对应的单词。如果找到,则返回相应的单词;否则返回None。

sentence2vec:该函数将句子基于单词频率转换为向量表示。

加载和处理数据:

代码指定两个文本文件的路径:good_file和bad_file,分别包含正向和负向评论。

调用prepare_data函数处理数据,并获取分词后的句子、单词字典以及它们的频率。

使用sentence2vec将句子转换为句子向量,并将其与相应的标签(正向为0,负向为1)添加到数据集中。

数据打乱和划分:

使用np.random.permutation对数据集的索引进行打乱。

根据打乱后的索引重新排序数据集、标签和句子。

将数据集划分为训练集、验证集和测试集。其中,训练集占总数据的80%,而验证集和测试集各占总数据的10%。

总体而言,该代码执行了预处理步骤,包括分词、单词过滤、向量化、数据打乱和数据集划分,以便为情感分析任务准备数据。

#句子向量的尺寸为词典中词汇的个数,句子向量在第i位置上面的数值为第i个单词在 sentence 中出现的频率。

def sentence2vec(sentence, dictionary):

    vector = np.zeros(len(dictionary))

    for l in sentence:

        vector[l] += 1

    return(1.0 * vector / len(sentence))

# 遍历所有句子,将每一个词映射成编码

dataset = [] #数据集

labels = [] #标签

sentences = [] #原始句子,调试用

# 处理正向评论

for sentence in pos_sentences:

    new_sentence = []

    for l in sentence:

        if l in diction:

            new_sentence.append(word2index(l, diction))

    dataset.append(sentence2vec(new_sentence, diction))

    labels.append(0) #正标签为0

    sentences.append(sentence)

# 处理负向评论

for sentence in neg_sentences:

    new_sentence = []

    for l in sentence:

        if l in diction:

            new_sentence.append(word2index(l, diction))

    dataset.append(sentence2vec(new_sentence, diction))

    labels.append(1) #负标签为1

sentences.append(sentence)

在这里对句子进行切分,分为:训练集、测试集、校验集(比例大概10:1:1)
校验集:主要是为了校验模型是否会产生过拟合的现象。
过程:模型训练好后,利用校验集数据检测模型表现,如果误差和训练数据差不多,则说明模型泛化能力很强,否则就是模型出现了过拟合的现象。

#打乱所有的数据顺序,形成数据集

# indices为所有数据下标的一个全排列

indices = np.random.permutation(len(dataset))

#重新根据打乱的下标生成数据集dataset,标签集labels,以及对应的原始句子sentences

dataset = [dataset[i] for i in indices]

labels = [labels[i] for i in indices]

sentences = [sentences[i] for i in indices]

#对整个数据集进行划分,分为:训练集、校准集和测试集,其中校准和测试集合的长度都是整个数据集的10分之一

test_size = len(dataset) // 10

train_data = dataset[2 * test_size :]

train_label = labels[2 * test_size :]

valid_data = dataset[: test_size]

valid_label = labels[: test_size]

test_data = dataset[test_size : 2 * test_size]

test_label = labels[test_size : 2 * test_size]

                    

1——数据清洗后的结果

4.模型设计与搭建

使用 PyTorch 自带的 Sequential 命令来建立多层前馈网络。其中,Sequential 中的每一个部件都是 PyTorch 的 nn.Module 模块继承而来的对象。

定义模型结构:

    • 模型采用nn.Sequential顺序容器定义。
    • 第一层是线性层(nn.Linear),输入维度为词典大小,输出维度为10,即包含10个隐含层神经元。
    • 在第一层后面添加了一个非线性激活函数ReLU(nn.ReLU)。
    • 第二层是线性层,输入维度为10,输出维度为2,用于分类预测。
    • 最后一层使用了LogSoftmax函数(nn.LogSoftmax(dim=1))进行多类别分类。

定义计算预测错误率的函数:

    • rightness函数接收模型的预测结果和标签,并计算预测正确的数量和总样本数量。

定义损失函数和优化算法:

    • 使用负对数似然损失(NLLLoss,torch.nn.NLLLoss())作为损失函数。
    • 选择Adam优化算法(torch.optim.Adam)来自动调节学习率,优化模型参数。

图2——多层神经网络

# 一个简单的前馈神经网络,三层,第一层线性层,加一个非线性ReLU,第二层线性层,中间有10个隐含层神经元

# 输入维度为词典的大小:每一段评论的词袋模型

model = nn.Sequential(

    nn.Linear(len(diction), 10),    #线性       包含10个隐含层神经元

    nn.ReLU(),                      #非线性

    nn.Linear(10, 2),               #线性

    nn.LogSoftmax(dim=1),

)

#计算预测错误率的函数

def rightness(predictions, labels):

    """其中predictions是模型给出的一组预测结果,batch_size行num_classes列的矩阵,labels是数据之中的正确答案"""

    pred = torch.max(predictions.data, 1)[1] # 对于任意一行(一个样本)的输出值的第1个维度,求最大,得到每一行的最大元素的下标

    rights = pred.eq(labels.data.view_as(pred)).sum() #将下标与labels中包含的类别进行比较,并累计得到比较正确的数量

    return rights, len(labels) #返回正确的数量和这一次一共比较了多少元素

5.模型训练

训练循环过程:

    • 使用trainModel函数进行训练,传入数据和标签。
    • 清空梯度,进行模型预测,计算损失函数,进行反向传播和参数优化。
    • 记录每次训练的损失值。

模型评估过程:

    • 使用evaluateModel函数对校验数据进行评估,传入数据和标签。
    • 进行模型预测,计算准确率和损失值。

训练循环迭代:

    • 外层循环是Epoch的数量,这里设置为10。
    • 内层循环遍历训练数据集,调用trainModel进行训练,并在每隔3000步时对校验数据集进行评估并输出临时结果。
    • 在校验集上计算平均准确率、训练损失和校验损失,并将这些结果记录下来。

# 损失函数为交叉熵

cost = torch.nn.NLLLoss()

# 优化算法为Adam,可以自动调节学习率

optimizer = torch.optim.Adam(model.parameters(), lr = 0.01)

# 记录列表,记录训练时的各种数据,以用于绘图

records = []

# loss 列表,用于记录训练中的 loss

losses = []

#开始训练循环

def trainModel(data, label):

    # 需要将输入的数据进行适当的变形,主要是要多出一个batch_size的维度,也即第一个为1的维度

    # 这样做是为了适应 PyTorch 函数的特殊用法,具体可以参考 PyTorch 官方文档

    x = Variable(torch.FloatTensor(data).view(1,-1))

    # x的尺寸:batch_size=1, len_dictionary

    # 标签也要加一层外衣以变成1*1的张量

    y = Variable(torch.LongTensor(np.array([label])))

    # y的尺寸:batch_size=1, 1

    # 清空梯度

    optimizer.zero_grad()

    # 模型预测

    predict = model(x)

    # 计算损失函数

    loss = cost(predict, y)

    # 将损失函数数值加入到列表中

    losses.append(loss.data.numpy())

    # 开始进行梯度反传

    loss.backward()

    # 开始对参数进行一步优化

    optimizer.step()

#函数会返回模型预测的结果、正确率、损失值

def evaluateModel(data, label):

    x = Variable(torch.FloatTensor(data).view(1, -1))

    y = Variable(torch.LongTensor(np.array([label])))

    # 模型预测

    predict = model(x)

    # 调用rightness函数计算准确度

    right = rightness(predict, y)

    # 计算loss

    loss = cost(predict, y)

    return predict, right, loss

#训练循环部分  循环10个Epoch

for epoch in range(10):

    for i, data in enumerate(zip(train_data, train_label)):

        x, y = data

        # 调用上面编写的训练函数

        # x 即句子向量,y 即标签(0 or 1)

        trainModel(x, y)

        # 每隔3000步,跑一下校验数据集的数据,输出临时结果

        if i % 3000 == 0:

            val_losses = []

            rights = []

            # 在所有校验数据集上实验

            for j, val in enumerate(zip(valid_data, valid_label)):

                x, y = val

                # 调用模型测试函数

                predict, right, loss = evaluateModel(x, y)

                rights.append(right)

                val_losses.append(loss.data.numpy())

            # 将校验集合上面的平均准确度计算出来

            right_ratio = 1.0 * np.sum([i[0] for i in rights]) / np.sum([i[1] for i in rights])

            print('第{}轮,训练损失:{:.2f}, 校验损失:{:.2f}, 校验准确率: {:.2f}'.format(epoch, np.mean(losses),

                                                                        np.mean(val_losses), right_ratio))

            records.append([np.mean(losses), np.mean(val_losses), right_ratio])

# 绘制误差曲线

# 从记录中提取第一列数据作为列表a

a = [i[0] for i in records]

# 从记录中提取第二列数据作为列表b

b = [i[1] for i in records]

# 从记录中提取第三列数据作为列表c

c = [i[2] for i in records]

# 绘制训练损失曲线,将a作为横坐标,label参数设置为'Train Loss'

plt.plot(a, label='Train Loss')

# 绘制验证损失曲线,将b作为横坐标,label参数设置为'Valid Loss'

plt.plot(b, label='Valid Loss')

# 绘制验证准确率曲线,将c作为横坐标,label参数设置为'Valid Accuracy'

plt.plot(c, label='Valid Accuracy')

# 设置横坐标的标签为'Steps'

plt.xlabel('Steps')

# 设置纵坐标的标签为'Loss & Accuracy'

plt.ylabel('Loss & Accuracy')

# 显示图例


plt.legend()

                                                                图3——训练过程 

                

                                                                图4——损失函数

                ​​​​​​​       

                                                                 图5——计算准确率

        ​​​​​​​        ​​​​​​​        

                                                                   图6——结果预测

6.设计中遇到的问题及解决办法

训练时间长:大规模模型的训练可能需要较长时间。解决方法包括优化算法、减少模型复杂度进行微调。

从网页爬取数据获取数据集的过程比较困难,从在线商城爬到的数据残次不齐,达不到理想的效果,经过多次尝试后,在处理数据集的问题花费了较长的时间,解决方法是直接从网络上下载现有的数据集。

数据集的预处理,数据集中的数据无法直接使用,解决方法,使用jieba分词,基于词袋模型对数据进行处理。

7.结论

   通过本次的课程设计,完成了一个基于PyTorch构建的RNN文本情绪分类器,它可以有效地处理文本情感分类任务。通过利用RNN的循环结构,该模型能够捕捉文本序列中的依赖关系和语义信息。同时,PyTorch提供了灵活且高效的神经网络设计和训练接口,使得构建和训练RNN模型变得相对简单。同时也学到了在构建模型的时候要选择合适的架构:在构建RNN模型时,可以选择不同的架构,如LSTM、GRU等。不同的架构具有不同的记忆和更新机制,因此本次我只是选择了最简单的架构完成任务。在本次一周的学习中,极大的提高了我的代码能力,学习基于RNN的文本情绪分类器是一个循序渐进的过程。需要对RNN原理有深入理解,熟悉数据处理和模型调优技巧,并通过实际应用来提升技能。持续学习和实践是掌握它的关键。总而言之,在一周的学习中,极大的提高了我的个人能力,同时加深了我对深度学习这门课程的认识。

猜你喜欢

转载自blog.csdn.net/m0_63975371/article/details/131472685