1. 实验目的
通过动手进行模型构建深入理解RNN模型的原理和结构,学习RNN用于NLP任务,以及模型优化方法。
2. 实验平3. 实验内容
1. 实验目的
通过动手进行模型构建深入理解RNN模型的原理和结构,学习RNN用于NLP任务,以及模型优化方法。
2. 实验平台
操作系统:Windows 2000/ XP/7/8/10/11 或者 Linux
深度学习框架:pytorch,tensorflow, keras等
3. 实验内容
任务:属性级的文本情感分类任务。如句子为“这家餐厅很喜欢,味道很好,就是卫生不太行”,给定属性“味道”,情感类别为positive,给定属性“卫生”,情感类别为negative(负类,或类别标签为-1)。若无明显情感倾向,则为中性类别neutral(0)。
方法:使用一个GRU(或LSTM)模型完成情感分类。模型定义为一个继承nn.Module的类,可以直接引用torch.nn中的GRU模块。如使用双向循环,需要设置bidirectional=True,注意此时隐藏状态维度应该是单向的2倍。属性的利用方式可以参考课件,也可以自行设计。
- 输入层是文本的预训练词向量的序列。一个batch内的文本要进行padding,maxlen可以选择当前batch内的最大文本长度,或者自行设置一个合适的值。输出维度=hidden_size
- 隐藏层由GRU单元构成,输入维度和输出维度=hidden_size。
- 可以使用torch.nn.Dropout添加dropout,缓解模型过拟合
- 输出层输入维度= hidden_size,使用softmax输出单元,输出维度=类别数。
损失函数使用交叉熵,即criterion = nn.CrossEntropyLoss()
【数据集】SemEval2014 Restaurants数据集修改得到。句子文本:text,属性只需要读取aspectTerms中的属性项。例子中id=457的句子考虑属性dinner,id=1306的句子无属性,则该条样本忽略。Id=3086的句子考虑属性drinks和place,形成两条样本。
读取代码可参考:
import xml.etree.ElementTree as ET
tree = ET.parse('train.xml')
root = tree.getroot()
for sent in root.findall('sentence'):
title = sent.find('text').text
for child in sent:
if child.tag == 'aspectTerms':
for term in child:
aspect, polarity = term.attrib['term'], term.attrib['polarity']
【词向量】实验2给出的某个100维预训练词向量
(1)请说明具体参数设置
(2)统计准确率(accuracy)、精确率(precision)、召回率(recall)、宏F1值。
实验 | accuracy | precision | recall | F1 |
属性级分类 |
感兴趣的同学也可以尝试对比单层、多层、单向、多向网络的结果。
(3)对实验结果进行解释,为什么某种模型更好,分析可能的原因。
类似项目参考:
代码实践:基于LSTM网络的DEAP情感数据集情感分类_deapdataset-CSDN博客
https://blog.csdn.net/m0_63642362/article/details/124335408
一、实验环境
操作系统:Windows 10
深度学习框架:pytorch
二、实验内容及详细的完成情况
(1)读取数据
读取aspectTerms中的属性项,若无属性,忽略该样本。若有多个属性,则建立多个样本,将情感分级分别使用0(中立),1(positive),2(negative)表示。
- def load_data(file_path):
- dataset = []
- tree = ET.parse(file_path)
- root = tree.getroot()
- for sent in root.findall('sentence'):
- text = sent.find('text').text
- for child in sent:
- if child.tag == 'aspectTerms':
- for term in child:
- aspect, polarity = term.attrib['term'], term.attrib['polarity']
- if polarity == 'positive':
- sentiment = 1
- elif polarity == 'negative':
- sentiment = 2
- else:
- sentiment = 0 # Neutral
- dataset.append((text, aspect, sentiment))
- return dataset
(2)数据处理
1.将情感取向转变为tensor格式,以便后续模型训练处理。
2.将文本(test)按空格分成一个个token,再转为词向量(Glove)(每个token 100维),并将每条文本长度固定位100个token,多切少补,以便size统一,方便处理。最后将text转成的嵌入embeddings转成tensor。
- def preprocess_text(text, maxlen=100):
- tokens = text.split()
- embeddings = [torch.from_numpy(word_embeddings[token]) if token in word_embeddings else torch.zeros_like(torch.from_numpy((word_embeddings['the']))) for token in tokens]
- if len(embeddings) > maxlen:
- embeddings = embeddings[:maxlen]
- elif len(embeddings) < maxlen:
- embeddings.extend([torch.zeros_like(torch.from_numpy((word_embeddings['the'])))] * (maxlen - len(embeddings)))
- return torch.stack(embeddings)
(3)参数设置
input_size=100
hidden_size=128(hidden_size * 2 if bidirectional)双向循环,隐藏层数乘二
output_size=3 (总共三个输出类别:积极,中立,消极)
batch_size=32
optimizer = optim.Adam(model.parameters(), lr=0.001) #优化器
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
num_epochs = 10
(4)GRU模型
使用GRU模型完成情感分类。使用单向循环和双向循环(设置bidirectional=True)
测试结果↓
单向循环:
双向循环:
通过对比可以看出,GRU模型是否设置双向循环,训练出来的结果其实差异不大,甚至单项的效果还稍微好一些。
通过查询资料,我得知,GRU模型的双向循环和单向循环在精度上有差异可能是由于几个原因造成的:
1.信息流动方向不同:双向循环通过两个方向的信息流动,理论上可以更全面地捕捉序列数据中的信息,但这也可能导致模型更复杂,训练难度增加。
2.参数数量增加:双向模型需要更多的参数来处理正向和反向的信息,如果训练数据不足或者模型复杂度过高,可能会导致过拟合或者训练困难。
3.训练过程不稳定:双向模型可能在训练过程中更容易出现梯度消失或者梯度爆炸的问题,需要更谨慎的参数初始化和调整学习率等超参数。
4.数据特性:某些数据集可能更适合单向模型,因为信息的重要性更多集中在正向或者反向流动中。
因此我猜测,可能是因为我只训练了10轮,可能出现了过拟合或者没有很好的训练模型导致的。
三、实验总结
(可以总结实验中出现的问题以及解决的思路,也可以列出没有解决的问题)
- 报错:RuntimeError: each element in list of batch should be of equal size。
这个错误通常发生在数据加载过程中,提示批次中的元素大小不一致。这可能是由于在构建数据集对象时,某些句子的长度超过了指定的最大长度,而某些句子没有达到maxlen,导致在构建批次时出现不一致的情况。
于是采用统一为100个token的方式来解决该问题。
2.模型训练过程中,需要所有数字,数组,矩阵等都是tensor格式,需要在导入数据时就对其(词向量,情感倾向)进行处理。