本文在win10下,基于最新版的anaconda、Keras以及其它模块,在ABAE作者开源代码的基础上进行了移植(原程序基于Python2、Keras1等老的模块)。本文讲解了程序的大致结构,并附录了一些参考资源,最后给出进行了移植和注释的程序的GitHub链接。ABAE是一种aspect extraction算法(提取意见实体的方面),其原理解读可以参看文后链接的文章。
算法介绍
在情感分析中,aspect extraction(方面提取)是最关键的任务之一,目的是提取出opinions(意见)针对的对象,例如“The beef was tender and melted in my mouth”中,aspect term(方面关键词)就是beef(细粒度)。ABAE是一种aspect extraction算法。
aspect是opinion mining中意见实体的方面,例如:我们可以把上面找到的beef作为entity,把其归为aspect(food)(粗粒度),当然把beef本身当作aspect也是可以的,但是这个粒度就太细了。为了更好地理解aspect,可以参考文献[1]和[2]。下图给出一些抽取出的aspect的例子,以建立更直观的理解。
目前在aspect extraction方面state-of-the-art的工作都是基于LDA的,而LDA本身有一定的缺点,例如:
(1)不编码word co-occurrences(词同现)统计特性,往往导致提取出的aspect中词语关联性不强(指的是top N的词语)。
(2)LDA给每个训练文档都估计一个topics分布,而review corpus往往很短,使得这个估计很困难。
文献[3]提出了一种Attention-based Aspect Extraction,ABAE算法,很好地弥补了LDA-based方法的缺点,取得了不错的效果。作者Ruidan He给出了算法的开源实现,本文主要目的是分析源码并复现其结果。
ABAE算法的原理可以参考上一篇博客。
运行环境
根据作者GitHub上的介绍,对本地环境进行配置。
作者的代码基于Python 2,而我本机上的版本是Python 3,因此实际运行中需要对一些地方进行修改(主要是print语句)。
复现过程在Win10系统下进行,Python则直接采用最新版本的anaconda。
调试的全部过程都是在命令行下进行的,代码在notepadd++和PyCharm上编辑。
程序调试过程的依赖安装会在下面的具体步骤中介绍,实际上直接将各个.py文件中import进的模块全部安装即可,如果安装过程遇到一些问题,一般可以比较容易地查到解决方法。
这里需要特别说明,各种依赖模块我在安装时都没有指定版本,也就是说基本都是安装的最新版本,因此在调试程序过程中遇到了很多问题,需要逐一解决,实际上可以安装作者使用的Theano、Keras版本,并配合Python2,这样可以避免很多麻烦。
源码与数据下载
直接将GitHub上的源码打包下载即可。下载后解压。
根据作者GitHub上的介绍下载原始数据集datasets和作者处理好的preprocessed_data,然后解压到程序主目录下,此时程序的目录如下图:
注意到解压后的语料库还是很大的,有700多Mb。
数据预处理
首先要进行数据预处理的工作,主要是两步:
(1)去掉stop words,提取名词的主干。
(2)利用word2vec算法将原始数据转换为词向量。
我们从code中找到preprocess.py和word2vec.py两个文件。
preprocess
preprocess需要调用四个文件,如下所示:
from sklearn.feature_extraction.text import CountVectorizer
from nltk.corpus import stopwords
from nltk.stem.wordnet import WordNetLemmatizer
import codecs
因此,我们需要保证已经安装了sklearn、nltk、codec模块。可以直接用pip指令安装,在命令行中输入:
pip install sklearn
pip install nltk
pip install codecs
其中,nltk模块仅仅通过pip安装还是不够的,为了程序能够顺利运行,还需要先执行以下语句加载stop words和wordnet(用来提取名词的主干),在命令行中:
python
>>>import nltk
>>>nltk.download('stopwords')
>>>nltk.download('wordnet') # 这一句可能会等待较长时间
运行preprocess之前,要注意把print语句加上括号。
preprocess首先把两个语料的训练数据按行读取,逐行处理数据;然后preprocess会逐行读取测试数据,并根据论文中的设置,对于restaurant语料只保留[‘Food’, ‘Staff’, ‘Ambience’]三个aspect的数据,并逐行处理。
下面我们把逐行处理的函数从程序中单独拿出来,稍微进行修改,保存到preprocesstest.py文件中,我们运行这个文件来查看预处理的效果。
from sklearn.feature_extraction.text import CountVectorizer
from nltk.corpus import stopwords
from nltk.stem.wordnet import WordNetLemmatizer
import codecs
# lmtzr.lemmatize(w)只修改名词,如需修改动词时态,需要lmtzr.lemmatize(w, 'v')
# remove stop words and lemmatize words of one sentence.(提取单词主干,例如:'loving'->'love')
if __name__ == '__main__':
line = 'Bar was a little bit crowded , but these five girls know how to have fun ! ! it was a little hard to understand the waitress and she seemed to have little patience with our questions .'
lmtzr = WordNetLemmatizer()
stop = stopwords.words('english')
print('[original ]', line)
text_token = CountVectorizer().build_tokenizer()(line.lower())
print('[tokenize ]', text_token)
text_rmstop = [i for i in text_token if i not in stop]
print('[rmvstops ]', text_rmstop)
text_stem = [lmtzr.lemmatize(w) for w in text_rmstop]
print('[lemmatize]', text_stem)
在命令行中运行这个文件,效果如下:
程序分别输出原始的一个review, tokenize的结果,去掉停止词的结果,提取名词主干的结果。图片明确展示了每一步操作的效果。
最后,运行preprocess.py文件,发现需要较长时间才能处理完毕,尤其是对beer数据集的预处理。作者已经给出了预处理的结果,我们可以直接使用其结果。
其余代码比较简单,不详细分析。
word2vec
word2vec利用神经网络将原始数据转换为词向量。其需要调用两个模块:gensim和codecs。我们仿照上一步直接安装即可。
利用gensim可以方便地进行word2vec操作,因此代码非常简短。需要注意的是,之所以构造一个generator传入gensim.models.Word2Vec函数,是为了避免加载数据过程中消耗很多时间,可以提高效率。
作者已经提供了训练好的模型,但是在windows下这个模型直接读取的话会出错,我们需要在命令行运行一次word2vec来更新模型,这个操作需要较长时间。更新完成后,我们可以利用这个模型来查看一些单词的向量形式,检验word2vec的效果。
import gensim
import codecs
import numpy as np
if __name__ == '__main__':
model_file = '../preprocessed_data/restaurant/w2v_embedding'
model = gensim.models.Word2Vec.load(model_file)
print(model.wv['like', 'hello'])
model_file = '../preprocessed_data/beer/w2v_embedding'
model = gensim.models.Word2Vec.load(model_file)
print(model.wv['like', 'hello'])
输出为:
这样就成功转换了。
其余代码比较简单,不详细分析。
训练
训练过程主要依赖train.py文件,首先给出运行的方法(以下各个阶段的测试都如此运行),在命令行中输入:
python train.py --emb ../preprocessed_d