IMDB 数据集包含了 50000 条电影评论和它们对应的情感标签(积极或消极),在本实验中,我们的任务是训练出一个结合 Word2Vec 的循环神经网络模型,对电影评论进行文本分类。
算法的流程包括以下步骤:
运行环境:Cuda。神经网络代码基于 Pytorch 库。
项目文件说明:
/Image 文件夹,保存所有可视化结果/Model 文件夹,保存训练好的模型参数data_utils.py 数据预处理封装代码visual_utils.py 数据可视化封装代码model.py 神经网络模型代码val.py 验证代码test.py 测试代码output_val.txt 验证程序输出结果output_test.py 测试程序输出结果注:由于 IMDB 数据集比较大,所有中间处理结果文件(包括文本清洗后的文件、Word2Vec编码文件)并没有提交,但可以通过代码重新生成这些文件。
文本清洗的内容包括:
,Html 标签考虑:<...> 和 <\...> 两种格式;https:// 和 www. 开头的 URL;you're 保留 you,I'll 保留 I;文本清洗实现:data_utils.py 的 data_clean(s) 函数
文本清洗举例:
from data_utils import data_clean
s = "Hello, 你好 @China, Today is 2022.5.19, it's a nice day #HelloMorning !!!! "
print(data_clean(s))
文本预处理的内容包括:
correct 函数,耗时较长nltk.download('stopwords') 下载文本预处理实现:data_utils.py 的 data_preprocess(s, spell_check=True, lemmatization=True, del_stopwords=True) 函数
spell_check,lemmatization,del_stopwords 是可选参数,即拼写校对、词形还原、停用词删除都是可选选项。文本预处理举例:
from data_utils import data_clean, data_preprocess
s = "Hello, 你好 @China, Today is 2022.5.19, it's a nice day #HelloMorning !!!! I did nothing last day, and cats are so cutee!"
print(data_preprocess(data_clean(s)))
is、it、a、so 等,将cats变为了单数、cutee改成了cut。Word2Vec采用gensim.models库的Word2Vec跟据数据集文本生成词向量,并将生成的词向量按找每一个词的索引依次拼接在一起,组成一个矩阵保存在文件中,该矩阵可以用来直接替换循环神经网络中的 Embedding 层,也可以作为 Embedding 层参数的初始化,训练时进行梯度计算。
实现于data_utils.py的word2vec_imdb(src_file=None, tar_file=None, min_times=50, vec_size=100)函数,可以指定词向量的长度vec_size,和少用词剔除的参数min_times,生成结果保存在tar_file文件中。
生成结果举例:
源文本:
s = “one”
生成的词向量(vec_size=150, min_times=150):
[-1.921392560005188,2.9480791091918945,1.182746171951294,-1.9803478717803955,-1.2230794429779053,...,-0.795309841632843]
我们在预处理时对单词进行了少见词剔除,即单词在数据集中出现次数少于 Min_times 的单词被剔除,以除去错别字、西语等非英语单词。为了找到较为合适的 Min_times 值,我们选择 5, 10, 30, 50 进行测试,测试基于 Embedding Size = 100,Hidden Size = 200 的 RNN 网络。
测试结果如下图所示,实验表明在 Min_times = 10 时训练效果最好。输出结果储存在_output.py中。

网络结构如下:
Model(
(Embedding): 嵌入层,用词向量矩阵填充,大小为[max_word, emb_size]
(RNN/LSTM/GRU):循环神经层,大小为[emb_size, hid_size]
(avg_pool):平均池化层
(fc2): 全连接层,大小为[hid_size, 2]
)
梯度计算采用:Adam 方法;Batch Size:256;Epochs:20。
为了对比不同模型的效果,给出以下一共十个网络进行验证测试。其中,我们在 RNN 1、LSTM 1、GRU 1三个网络中不对 Embedding 层进行梯度计算,即固定该层的值为 Word2Vec 矩阵,其他网络则对 Embedding 曾进行梯度计算。验证网络分为两种规模:Emb_size,Hid_size 为 [150, 300] 和 [300, 500]。
| 模型 | Embedding Size | Hidden Size | Grad For Embedding |
|---|---|---|---|
| RNN 1 | 150 | 300 | False |
| RNN 2 | 150 | 300 | True |
| RNN 3 | 300 | 500 | True |
| LSTM 1 | 150 | 300 | False |
| LSTM 2 | 150 | 300 | True |
| LSTM 3 | 300 | 500 | True |
| BiLSTM | 300 | 300 | True |
| GRU 1 | 150 | 300 | False |
| GRU 2 | 300 | 300 | True |
| GRU 3 | 300 | 500 | True |
在实验时我们发现使用Word2Vec模型来初始化Embedding层能够明显加快收敛速度。
我们选用数据集前三万条作为训练集,再选择第三万到四万条作为验证集,得到的训练结果如下;输出结果储存在_output.py中。
注:下表 Train Acc 值 Val Acc 取得最高时的 Train Acc
| 模型 | Train Acc | Best Val Acc | Best Epoch | Train Time Per Epoch |
|---|---|---|---|---|
| RNN 1 | 0.8556 | 0.8494 | 6 | 12.47秒 |
| RNN 2 | 0.9140 | 0.8772 | 4 | 12.53秒 |
| RNN 3 | 0.8852 | 0.8618 | 2 | 22.45秒 |
| LSTM 1 | 0.9463 | 0.8848 | 4 | 40.82秒 |
| LSTM 2 | 0.9448 | 0.8888 | 4 | 42.16秒 |
| LSTM 3 | 0.9754 | 0.8963 | 5 | 91.30秒 |
| BiLSTM | 0.9884 | 0.8830 | 7 | 48.10秒 |
| GRU 1 | 0.8957 | 0.8861 | 6 | 20.85秒 |
| GRU 2 | 0.9247 | 0.8957 | 3 | 21.29秒 |
| GRU 3 | 0.9784 | 0.8975 | 4 | 50.23秒 |
| RNN 1 (20 Epochs) | RNN 2 (20 Epochs) | RNN 3 (20 Epochs) |
|---|

通过观察上述十个网络的损失函数和准确率变化,我们得到以下结论:
跟据验证集结果调整超参,最后在 RNN / LSTM / GRU 中选出三个性能较好的网络,将数据集的第 40000 - 50000 个样本作为测试集,在测试集上进行测试,结果如下表所示。输出结果储存在_output_test.py中。
| 模型 | Embedding Size | Hidden Size | Grad For Embedding | Test Acc |
|---|---|---|---|---|
| RNN 2 | 150 | 300 | True | 0.8817 |
| LSTM 3 | 300 | 500 | True | 0.9017 |
| GRU 3 | 300 | 500 | True | 0.8988 |