使用Word2Vec/Doc2Vec对IMDB情感分析

标签: 数据 NLP | 发表时间:2019-11-05 21:42 | 作者:标点符
出处:https://www.biaodianfu.com

情感分析是一种常见的自然语言处理(NLP)方法的应用,特别是在以提取文本的情感内容为目标的分类方法中。通过这种方式,情感分析可以被视为利用一些情感得分指标来量化定性数据的方法。尽管情绪在很大程度上是主观的,但是情感量化分析已经有很多有用的实践,比如企业分析消费者对产品的反馈信息,或者检测在线评论中的差评信息。

最简单的情感分析方法是利用词语的正负属性来判定。句子中的每个单词都有一个得分,乐观的单词得分为 +1,悲观的单词则为 -1。然后我们对句子中所有单词得分进行加总求和得到一个最终的情感总分。很明显,这种方法有许多局限之处,最重要的一点在于它忽略了上下文的信息。例如,在这个简易模型中,因为“not”的得分为 -1,而“good”的得分为 +1,所以词组“not good”将被归类到中性词组中。尽管词组“not good”中包含单词“good”,但是人们仍倾向于将其归类到悲观词组中。

另外一个常见的方法是将文本视为一个“词袋”。我们将每个文本看出一个1*N的向量,其中N表示文本词汇的数量。该向量中每一列都是一个单词, 其对应的值为该单词出现的频数。这些数据可以被应用到机器学习分类算法中(比如Logistic回归或者支持向量机),从而预测未知数据的情感状况。需要注意的是,这种有监督学习的方法要求利用已知情感状况的数据作为训练集。虽然这个方法改进了之前的模型,但是它仍然忽略了上下文的信息和数据集的规模情况。例如,句子“这部电影糟糕透了”和“一个乏味,空洞,没有内涵的作品”在情感分析中具有很高的语义相似度,但是它们的BOW表示的相似度为0。又如,句子“一个空洞,没有内涵的作品”和“一个不空洞而且有内涵的作品”的BOW相似度很高,但实际上它们的意思很不一样。

Word2Vec 和 Doc2Vec

Google开发的 Word2Vec 的方法,该方法可以在捕捉语境信息的同时压缩数据规模。Word2Vec实际上是两种不同的方法:Continuous Bag of Words (CBOW) 和 Skip-gram。CBOW的目标是根据上下文来预测当前词语的概率。Skip-gram刚好相反:根据当前词语来预测上下文的概率。这两种方法都利用人工神经网络作为它们的分类算法。起初,每个单词都是一个随机 N 维向量。经过训练之后,该算法利用 CBOW 或者 Skip-gram 的方法获得了每个单词的最优向量。

现在这些词向量已经捕捉到上下文的信息。我们可以利用基本代数公式来发现单词之间的关系(比如,“king” – “man” + “woman” = “queen”)。这些词向量可以代替词袋用来预测未知数据的情感状况。该模型的优点在于不仅考虑了语境信息还压缩了数据规模(通常情况下,词汇量规模大约在300个单词左右而不是之前模型的100000个单词)。因为神经网络可以替我们提取出这些特征的信息,所以我们仅需要做很少的手动工作。但是由于文本的长度各异,我们可能需要利用所有词向量的平均值作为分类算法的输入值,从而对整个文本文档进行分类处理。

然而,即使上述模型对词向量进行平均处理,我们仍然忽略了单词之间的排列顺序对情感分析的影响。作为一个处理可变长度文本的总结性方法,Quoc Le 和 Tomas Mikolov 提出了 Doc2Vec方法。除了增加一个段落向量以外,这个方法几乎等同于 Word2Vec。和 Word2Vec 一样,该模型也存在两种方法:Distributed Memory(DM) 和 Distributed Bag of Words(DBOW)。DM 试图在给定上下文和段落向量的情况下预测单词的概率。在一个句子或者文档的训练过程中,段落 ID 保持不变,共享着同一个段落向量。DBOW 则在仅给定段落向量的情况下预测段落中一组随机单词的概率。

一旦开始被训练,这些段落向量可以被纳入情感分类器中而不必对单词进行加总处理。这个方法是当前最先进的方法,当它被用于对 IMDB 电影评论数据进行情感分类时,该模型的错分率仅为 7.42%。当然如果我们无法真正实施的话,一切都是浮云。幸运的是,genism(Python 软件库)中 Word2Vec 和 Doc2Vec 的优化版本是可用的。

使用Word2Vec进行情感分析

训练数据: http://ai.stanford.edu/~amaas/data/sentiment/

import numpy as np
import matplotlib.pyplot as plt
import gensim
from gensim.utils import open
from gensim.models import Word2Vec
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import scale
from sklearn.metrics import roc_curve, auc
import random


# 读取影评内容
with open('./imdb_data/pos.txt', 'r', encoding='utf-8') as infile:
    pos_reviews = []
    line = infile.readline()
    while line:
        pos_reviews.append(line)
        line = infile.readline()

with open('./imdb_data/neg.txt', 'r', encoding='utf-8') as infile:
    neg_reviews = []
    line = infile.readline()
    while line:
        neg_reviews.append(line)
        line = infile.readline()

with open('./imdb_data/unsup.txt', 'r', encoding='utf-8') as infile:
    unsup_reviews = []
    line = infile.readline()
    while line:
        unsup_reviews.append(line)
        line = infile.readline()

# 数据划分, 1代表积极情绪,0代表消极情绪
y = np.concatenate((np.ones(len(pos_reviews)), np.zeros(len(neg_reviews))))
x_train, x_test, y_train, y_test = train_test_split(np.concatenate((pos_reviews, neg_reviews)), y, test_size=0.2)


def labelize_reviews(reviews):
    for i, v in enumerate(reviews):
        yield gensim.utils.simple_preprocess(v, max_len=100)
x_train_tag = list(labelize_reviews(x_train))
x_test_tag = list(labelize_reviews(x_test))
unsup_reviews_tag = list(labelize_reviews(unsup_reviews))


model = Word2Vec(size=200, window=10, min_count=1)
# 对所有评论创建词汇表
all_data = x_train_tag
all_data.extend(x_test_tag)
all_data.extend(unsup_reviews_tag)
model.build_vocab(all_data)


def sentences_perm(sentences):
    shuffled = list(sentences)
    random.shuffle(shuffled)
    return (shuffled)


for epoch in range(10):
    print('EPOCH: {}'.format(epoch))
    model.train(sentences_perm(all_data), total_examples=model.corpus_count, epochs=1)


def build_word_vector(text, size=200):
    vec = np.zeros(size).reshape((1, size))
    count = 0.
    for word in text:
        try:
            vec += model[word]
            count += 1.
        except KeyError:
            continue
    if count != 0:
        vec /= count
    return vec


train_vecs = np.concatenate([build_word_vector(gensim.utils.simple_preprocess(z, max_len=200)) for z in x_train])
test_vecs = np.concatenate([build_word_vector(gensim.utils.simple_preprocess(z, max_len=200)) for z in x_test])
train_vecs = scale(train_vecs)
test_vecs = scale(test_vecs)
classifier = LogisticRegression()
classifier.fit(train_vecs, y_train)

print(classifier.score(test_vecs, y_test))
y_prob = classifier.predict_proba(test_vecs)[:, 1]

fpr, tpr, _ = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, label='area = %.2f' % roc_auc)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.legend(loc='lower right')
plt.show()

最终的得分为0.8859

使用Doc2Vec进行情感分析

from gensim.utils import open, simple_preprocess
from gensim.models.doc2vec import TaggedDocument
from gensim.models import Doc2Vec
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, auc
import matplotlib.pyplot as plt
import random

# 读取影评内容
with open('./imdb_data/pos.txt', 'r', encoding='utf-8') as infile:
    pos_reviews = []
    line = infile.readline()
    while line:
        pos_reviews.append(line)
        line = infile.readline()

with open('./imdb_data/neg.txt', 'r', encoding='utf-8') as infile:
    neg_reviews = []
    line = infile.readline()
    while line:
        neg_reviews.append(line)
        line = infile.readline()

with open('./imdb_data/unsup.txt', 'r', encoding='utf-8') as infile:
    unsup_reviews = []
    line = infile.readline()
    while line:
        unsup_reviews.append(line)
        line = infile.readline()

# 数据划分
# 1 代表积极情绪,0 代表消极情绪
y = np.concatenate((np.ones(len(pos_reviews)), np.zeros(len(neg_reviews))))
x_train, x_test, y_train, y_test = train_test_split(np.concatenate((pos_reviews, neg_reviews)), y, test_size=0.2)


# 创建TaggedDocument对象
# Gensim 的 Doc2Vec 工具要求每个文档/段落包含一个与之关联的标签。我们利用 TaggedDocument进行处理。
# 格式形如 “TRAIN_i” 或者 “TEST_i”,其中 “i” 是索引
def labelize_reviews(reviews, label_type):
    for i, v in enumerate(reviews):
        label = '%s_%s' % (label_type, i)
        yield TaggedDocument(simple_preprocess(v, max_len=100), [label])


x_train_tag = list(labelize_reviews(x_train, 'train'))
x_test_tag = list(labelize_reviews(x_test, 'test'))
unsup_reviews_tag = list(labelize_reviews(unsup_reviews, 'unsup'))

# 实例化Doc2vec模型
# 下面我们实例化两个 Doc2Vec 模型,DM 和 DBOW。
# gensim 文档建议多次训练数据,并且在每一步(pass)调节学习率(learning rate)或者用随机顺序输入文本。
# 接着我们收集了通过模型训练后的电影评论向量。
# DM 和 DBOW会进行向量叠加,这是因为两个向量叠加后可以获得更好的结果
size = 100
model_dm = Doc2Vec(min_count=1, window=10, vector_size=size, sample=1e-3, negative=5, workers=3,
                   epochs=10)
model_dbow = Doc2Vec(min_count=1, window=10, vector_size=size, sample=1e-3, negative=5, dm=0, workers=3,
                     epochs=10)

# 对所有评论创建词汇表
all_data = x_train_tag
all_data.extend(x_test_tag)
all_data.extend(unsup_reviews_tag)
model_dm.build_vocab(all_data)
model_dbow.build_vocab(all_data)


def sentences_perm(sentences):
    shuffled = list(sentences)
    random.shuffle(shuffled)
    return (shuffled)


for epoch in range(10):
    print('EPOCH: {}'.format(epoch))
    model_dm.train(sentences_perm(all_data), total_examples=model_dm.corpus_count, epochs=1)
    model_dbow.train(sentences_perm(all_data), total_examples=model_dbow.corpus_count, epochs=1)

# 获取生成的向量
# 获取向量有两种方式,一种是根据上面我们定义的标签来获取,另一种通过输入一篇文章的内容来获取这篇文章的向量。
# 更推荐使用第一种方式来获取向量。

# 第一种方法
train_arrays_dm = np.zeros((len(x_train), 100))
train_arrays_dbow = np.zeros((len(x_train), 100))
for i in range(len(x_train)):
    tag = 'train_' + str(i)
    train_arrays_dm[i] = model_dm.docvecs[tag]
    train_arrays_dbow[i] = model_dbow.docvecs[tag]
train_arrays = np.hstack((train_arrays_dm, train_arrays_dbow))
test_arrays_dm = np.zeros((len(x_test), 100))
test_arrays_dbow = np.zeros((len(x_test), 100))
for i in range(len(x_test)):
    tag = 'test_' + str(i)
    test_arrays_dm[i] = model_dm.docvecs[tag]
    test_arrays_dbow[i] = model_dbow.docvecs[tag]
test_arrays = np.hstack((test_arrays_dm, test_arrays_dbow))


# 第二种
def get_vecs(model, corpus):
    vecs = []
    for i in corpus:
        vec = model.infer_vector(simple_preprocess(i, max_len=300))
        vecs.append(vec)
    return vecs


train_vecs_dm = get_vecs(model_dm, x_train)
train_vecs_dbow = get_vecs(model_dbow, x_train)
train_vecs = np.hstack((train_vecs_dm, train_vecs_dbow))

# 预测
classifier = LogisticRegression()
classifier.fit(train_arrays, y_train)
print(classifier.score(test_arrays, y_test))
y_prob = classifier.predict_proba(test_arrays)[:, 1]
fpr, tpr, _ = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, label='area = %.2f' % roc_auc)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.legend(loc='lower right')
plt.show()

最终得分为:0.8911

参考链接: https://cloud.tencent.com/developer/article/1111575

相关 [word2vec doc2vec imdb] 推荐:

使用Word2Vec/Doc2Vec对IMDB情感分析

- - 标点符
情感分析是一种常见的自然语言处理(NLP)方法的应用,特别是在以提取文本的情感内容为目标的分类方法中. 通过这种方式,情感分析可以被视为利用一些情感得分指标来量化定性数据的方法. 尽管情绪在很大程度上是主观的,但是情感量化分析已经有很多有用的实践,比如企业分析消费者对产品的反馈信息,或者检测在线评论中的差评信息.

Word2Vec

- - Yonglong.Zhou
Word2Vec核心包括四个组组件,Context Builder、Input Vectors、Output Vectors和Parameter Learner. Context Builder决定是了是采用CBOW模型还是Skip-Gram模型,一般采用Input Vectors作为最终的词向量输出结果,参数学习可以按照常规的反向传播算法,以及为了提升计算速度提出来的霍夫曼树softmax和负采样方法.

为什么IMDB会被封…

- 刘星炜 - FeedzShare
来自: www.isweetriver.com - FeedzShare  . 发布时间:2011年07月05日,  已有 4 人推荐. 其实IMDB早就被封了,哥在去年就有一篇附送IMDB访问方式了,今天更新了一下,又可以国内直接通过IP上了,只不过有些图看不到. 为什么IMDB会被封… – 《建党伟业》精彩IMDB评论.

词向量工具word2vec的学习

- - 标点符
word2vec是Google在2013年开源的一款将词表征为实数值向量(word vector)的高效工具,采用的模型有CBOW(Continuous Bag-Of-Words,即连续的词袋模型)和Skip-Gram两种. word2vec通过训练,可以把对文本内容的处理简化为K维向量空间中的向量运算,而向量空间上的相似度可以用来表示文本语义上的相似度.

女演员起诉IMDb公开她的年龄

- applelen - Solidot
一位匿名的女演员因年龄被公布在互联网数据库IMDb网站上而起诉亚马逊公司,索赔超过百万美元. IMDb通过付费服务IMDbPro,向客户提供业内内部信息. 这位女艺人在2008年注册了付费服务,她在起诉书中指控亚马逊滥用她注册时使用的法定出生日期,称公开年龄损害了她的演艺机会,指控亚马逊及IMDb违反合同、欺诈、侵犯隐私和违反消费者保护法.

深度学习word2vec笔记之应用篇

- - 我爱机器学习
1)该博文是Google专家以及多位博主所无私奉献的论文资料整理的. 2)本文仅供学术交流,非商用. 所以每一部分具体的参考资料并没有详细对应,更有些部分本来就是直接从其他博客复制过来的. 如果某部分不小心侵犯了大家的利益,还望海涵,并联系老衲删除或修改,直到相关人士满意为止. 3)本人才疏学浅,整理总结的时候难免出错,还望各位前辈不吝指正,谢谢.

基于word2vec和Elasticsearch实现个性化搜索

- - GinoBeFunny
在 word2vec学习小记一文中我们曾经学习了word2vec这个工具,它基于神经网络语言模型并在其基础上进行优化,最终能获取词向量和语言模型. 在我们的商品搜索系统里,采用了word2vec的方式来计算用户向量和商品向量,并通过Elasticsearch的function_score评分机制和自定义的脚本插件来实现个性化搜索.

word2vec词向量训练及中文文本相似度计算 - CSDN博客

- -
本文是讲述如何使用word2vec的基础教程,文章比较基础,希望对你有所帮助. 参考:《Word2vec的核心架构及其应用 · 熊富林,邓怡豪,唐晓晟 · 北邮2015年》.           《Word2vec的工作原理及应用探究 · 周练 · 西安电子科技大学2014年》.           《Word2vec对中文词进行聚类的研究 · 郑文超,徐鹏 · 北京邮电大学2013年》.

Word2vec之情感语义分析实战(part3)--利用分布式词向量完成监督学习任务

- - CSDN博客推荐文章
这篇博客将基于前面一篇博客 Part2做进一步的探索与实战. demo代码与数据: 传送门. 前面我们训练了单词的语义理解模型. 如果我们深入研究就会发现,Part2中训练好的模型是由词汇表中单词的特征向量所组成的. 这些特征向量存储在叫做syn0的numpy数组中:. 很明显这个numpy数组大小为(16490,300)分别代表词汇表单词数目及每个单词对应的特征数.