需要源码或者有任何问题都可以私信或者评论,博主会定期查看的,希望本篇文章可以帮到你们!
源码以及数据集下载链接(有问题可以直接私信):https://download.youkuaiyun.com/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原理有深入理解,熟悉数据处理和模型调优技巧,并通过实际应用来提升技能。持续学习和实践是掌握它的关键。总而言之,在一周的学习中,极大的提高了我的个人能力,同时加深了我对深度学习这门课程的认识。