不懂word2vec,还敢说自己是做NLP?

如今,深度学习炙手可热,deep learning在图像处理领域已经取得了长足的进展。随着Google发布word2vec,深度学习在自然语言处理领域也掀起了一阵狂潮。由于最近正在做一个相关的NLP项目,所以抽时间总结一下word2vec的相关知识点。

 

文章结构:

1、词的独热表示

2、词的分布式表示

3、词嵌入

4、两种训练模式

5、两种加速方法

6、word2vec和word embedding的区别 

7、小结

 

1、词的独热表示

到目前为止最常用的词表示方法是 One-hot Representation,这种方法把每个词表示为一个很长的向量。这个向量的维度是词表大小,其中绝大多数元素为 0,只有一个维度的值为 1,这个向量就代表了当前的词。

 

“可爱”表示为 [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ...] 

“面包”表示为 [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ...]

 

每个词都是茫茫 0 海中的一个 1。这种 One-hot Representation 如果采用稀疏方式存储,会是非常的简洁:也就是给每个词分配一个数字 ID。比如刚才的例子中,可爱记为 3,面包记为 8(假设从 0 开始记)。

 

缺点分析: 

1、向量的维度会随着句子的词的数量类型增大而增大; 

2、任意两个词之间都是孤立的,无法表示语义层面上词汇之间的相关信息,而这一点是致命的。

 

2、词的分布式表示

传统的独热表示仅仅将词符号化,不包含任何语义信息。如何将语义融入到词表示中?Harris 在 1954 年提出的“分布假说”为这一设想提供了理论基础:上下文相似的词,其语义也相似。Firth 在 1957年对分布假说进行了进一步阐述和明确:词的语义由其上下文决定

 

以(CBOW)为例,如果有一个句子“the cat sits one the mat”,在训练的时候,将“the cat sits one the”作为输入,预测出最后一个词是“mat”。 

 

图1

 

分布式表示的较大优点在于它具有非常强大的表征能力,比如n维向量每维k个值,可以表征k的n次方个概念。下图的神经网络语言模型(NNLM)采用的就是文本分布式表示。而词向量(word embedding)是训练该语言模型的一个附加产物,即图中的Matrix C。

 

图2

 

所以我们可以将我们的任务拆解成两部分: 

一、选择一种方式描述上下文; 

二、选择一种“模型”刻画某个词(即“目标词”)与其上下文之间的关系。

 

3、词嵌入

基于神经网络的分布表示一般称为词向量、词嵌入( word embedding)或分布式表示( distributed representation)。核心依然是上下文的表示以及上下文与目标词之间的关系的建模。

 

上面了解到独热编码的维度过大的缺点。对此进行如下改进:

 

1、将vector每一个元素由整形改为浮点型,变为整个实数范围的表示; 

2、将原来稀疏的巨大维度压缩嵌入到一个更小维度的空间。如图所示: 

 

图3

 

“词嵌入”也由此得名。

 

本质词向量是训练神经网络时候的隐藏层参数或者说矩阵。

 

图4

 

4、两种训练模式

CBOW (Continuous Bag-of-Words Model)

Skip-gram (Continuous Skip-gram Model)

 

CBOW:上下文来预测当前词

图5

 

Skip-gram:当前词预测上下文

 

图6

 

5、两种加速方法

Negative Sample

Hierarchical Softmax

大家都知道哈夫曼树是带权路径最短的树,一般神经网络语言模型在预测的时候,输出的是预测目标词的概率(每一次预测都要基于全部的数据集进行计算,很大的时间开销)。

 

Hierarchical Softmax是一种对输出层进行优化的策略,输出层从原始模型的利用softmax计算概率值改为了利用Huffman树计算概率值。一开始我们可以用以词表中的全部词作为叶子节点,词频作为节点的权,构建Huffman树,作为输出。从根节点出发,到达指定叶子节点的路径是的。Hierarchical Softmax正是利用这条路径来计算指定词的概率,而非用softmax来计算。 

即Hierarchical Softmax:把 N 分类问题变成 log(N)次二分类

 

Negative Sampling(简写NEG,负采样),这是Noise-Contrastive Estimation(简写NCE,噪声对比估计)的简化版本:把语料中的一个词串的中心词替换为别的词,构造语料 D 中不存在的词串作为负样本。在这种策略下,优化目标变为了:较大化正样本的概率,同时最小化负样本的概率。这样说大家可能已经糊涂了,我们省去繁琐的数学推倒,这里举一个例子:

 

假设有一个句子为:“The quick brown fox jumps over the lazy dog.“ 

如果我们用skip-gram model ,貌似这是一个很大的网络。 

图7

 

注意:比如像“the”这种常见的词,我们会遇到两个问题: 

1. 比如(fox,the)其实没有传递我们关于 fox的信息。‘the‘出现得太多了。 

2. 我们有太多 (‘the’,…)的样本,多于我们实际需要的。 

所以word2vec采用了降采样的策略。对于每个我们在训练样本中遇到的词,我们有一个概率去删除它。这个概率与单词出现的频率相关。

 

如果我们采用window size = 10,同时我们删除‘the’: 

1. 当我们再去训练剩下的词,我们就不会再遇到‘the’了; 

2. 我们减少了10个包含‘the’的样本 

这其实就是‘NEG’的核心思想。

 

6、word2vec和word embedding的区别 

简言之,word embedding 是一个将词向量化的概念,中文译名为"词嵌入"。 

word2vec是谷歌提出的一种word embedding的具体手段,采用了两种模型(CBOW与skip-gram模型)与两种方法(负采样与层次softmax方法)的组合,比较常见的组合为 skip-gram+负采样方法。

 

Word embedding的训练方法大致可以分为两类:

(1)无监督或弱监督的预训练 

优点:不需要大量的人工标记样本就可以得到质量还不错的embedding向量 

缺点:准确率有待提高 

解决办法:得到预训练的embedding向量后,用少量人工标注的样本再去优化整个模型 

典型代表:word2vec和auto-encoder 

(这里解释一下AutoEncoder,AutoEncoder也可以用于训练词向量,先将one hot映射成一个hidden state,再映射回原来的维度,令输入等于输出,取中间的hidden vector作为词向量,在不损耗原表达能力的前提下压缩向量维度,得到一个压缩的向量表达形式。) 

 

(2)端对端(end to end)的有监督训练。 

优点:学习到的embedding向量也往往更加准确 

缺点:模型在结构上往往更加复杂 

应用:通过一个embedding层和若干个卷积层连接而成的深度神经网络以实现对句子的情感分类,可以学习到语义更丰富的词向量表达。

 

word2vec不关心后续的应用场景,其学习到的是就是根据共现信息得到的单词的表达,用n-gram信息来监督,在不同的子task间都会有一定效果。而end2end训练的embedding其和具体子task的学习目标紧密相关,直接迁移到另一个子task的能力非常弱

 

常用的word embedding分为dense和sparse两种形式: 

常见的sparse就比如 co-occurence 或者one-hot的形式;对sparse embedding进行一些降维运算比如SVD、PCA就可以得到dense。

 

小结

通过阅读这篇文章,大家应该对word2ec有了一个基本的了解,如果想更加深入透彻的了解此工具,可以从数学原理入手并且阅读word2vec的源码,这里附上我参考的带有完整注释的源码地址,感兴趣的可以参考一下。 

带完整注释的word2vec源码:

https://github.com/peklixuedong/RepresentationLearning

 

Word Embedding模型: 词、短语及它们的组合的分布式表示

摘要

  最近引入的连续Skip-gram模型是学习可以高质量分布式向量表示的有效方法,而这种分布式向量表示可以刻画大量精确的句法和语义关系。本文我们介绍了Skip-gram模型的多种扩展,它们可以提升向量的质量和训练速度。通过对频繁词进行重复采样我们可以极大地提升学习速度,学习到更有规律的单词表示。我们还描述了一种分层softmax的简单代替方案称作负抽样。
  单词表示的内在局限是它们对单词顺序的无差异化以及不能表示惯用短语。例如,在单词表示中,难以简单地将“Canada”和“Air”结合得到“Air Canada”。受此例所激发,我们给出了一种找到文本中的短语的简单方法,并且表明为百万级的短语学习到好的向量表示是可能的。

引言

  在自然语言处理任务中,向量空间中单词的分布式表示通过对相似的词分类可以帮助学习算法取得更好的效果。单词表示的最早使用可以追溯到1986年[Rumelhart, Hinton and Williams]。此后该思想便被应用于统计语言模型中,获得了极大的成功。后续的工作包括将其应用到自动语音辨识,机器翻译以及大量的自然语言处理任务中。
  最近,Mikolov等人引入了Skip-gram模型[8],这是一种从大量分结构化的文本数据中学习高质量的向量表示的有效方法。与之前大量使用的用神经网络结构学习单词向量的架构不同,训练Skip-gram模型(如图1)不涉及密集的矩阵乘法。这就使得训练非常有效:经过优化的单机训练可以在一天训练1000亿个单词。
  使用神经网络计算得到的单词表示非常有趣,因为学习到的向量可以显式编码很多语言规律和模式。有点惊人的是,很多这些模式可以表示成linear translations(线性变换)。例如,vec(‘‘Madrid")−vec(‘‘Spain")+vec(‘‘France")

的向量计算比任何词向量都更接近于‘‘vec("Paris")。
  本文我们给出了原始Skip-gram模型的若干扩展。我们的研究表明在模型训练时对频繁词进行子抽样可以极大提升训练速度(2-10倍),并且提升了欠频繁词的表示精度。此外,我们介绍了用Noise Contrastive Estimation(噪音对比估计)的简单变体来训练Skip-gram模型,实验表明我们的方法相比于之前工作中使用的更加复杂的分层softmax方法,可以得到更好的频繁词的向量表示,训练速度更快。
  单词表示受限于它们不能表示惯用语,惯用语不是单个词的简单组合。例如波士顿环球报是一家报纸,但它不是波士顿和环球两个单词组合在一起的意思。因此,使用向量来表示整个短语使得Skip-gram模型更具有表达力。其它的通过构成词向量来表示句子意思的技术也可以受益于短语向量而不是词向量。
  将基于词的模型扩展到基于短语的模型相当简单。首先我们使用数据驱动的方法识别大量的短语,然后在训练阶段我们将短语视作单个符号。为了评价短语向量的质量,我们生成了包含单词和短语的类比推理任务的测试集。我们测试集中一个典型的类比对是蒙特利尔:蒙特利尔加拿大人队,多伦多:多伦多枫叶队(二者都是北美冰球联盟的俱乐部)。因此如果vec(Montreal Canadiens)-vec(Montreal)+vec(Toronto)的最近表示为vec(Toronto Maple Leafs),那么我们就认为是一个正确的短语表示。

Skip-gram模型

  Skip-gram模型的训练目标是发现可以用于预测句子或者文档中附近的词(surrounding words)的单词表示。更正式地讲,给定一个训练词w1,w2,w3,⋯,wT

序列,Skip-gram模型的目标是最大化平均对数概率
  

1T∑t=1T∑−c≤j≤c,j≠0logp(wt+j|wt)


其中c是训练环境的规模(可以是中心词center wordwt的函数)。大c会得到更多的训练样本从而能得到更高的精度,当然也会有着更多的训练时间。基本的Skip-gram模型使用下面的softmax函数定义p(wt+j|wt):

p(wO|wI)=exp(v′⊤wOvwI)∑Ww=1exp(v′⊤wvwI)


其中vw和v′w是w的输入和输出的向量表示,W是词典中的单词个数。这种方式不太实际,因为计算∇logp(wO|wI)正比于W,而W通常非常大(105−107)。

 

分层Softmax

  完全softmax的有效近似计算是分层Softmax。在神经网络语言模型环境中,首先由Morin和Bengio引入。主要优势在于在神经网络中不需要评估W个输出节点而是log2(W)

个节点来得到概率分布。
  分层Softmax使用一种输出层的二元树表示(有W个词)作为它的叶子节点,而且对于每个节点,显式地表示它的子节点的相关概率。这些定义了一个将概率分配到单词的随机游走模型。
  更精确的是,我们可以通过一个从树根的合适路径到达每个单词w。令n(w,j)为从根到w的路径上的第j个节点,令L(w)为这条路径的长度,所以n(w,1)=root且n(w,L(w))=w。此外,对于任何内部节点n,令ch(n)为n的一个任意固定的子代,若x为真令[x]为1,否则为-1。那么用分层softmax定义的p(wO|wI)

如下:

p(w|wI)=∏j=1L(w)−1σ([n(w,j+1)=ch(n(w,j))]⋅v′⊤n(w,j)vwI)


其中σ(x)=1/(1+exp(−x))。可以证明∑Ww=1p(w|wI)=1。这表明计算logp(wO|wI)和∇logp(wO|wI)的成本正比于L(wO),一般来说不大于logW。此外,不像标准的softmax形式化的Skip-gram对每个单词w分配两个表示vw和v′w,分层softmax对每个单词w有一个表示vw,对二元树上的每个内节点n有一个表示v′n。
  分层softmax使用的树结构对结果又很大影响。Mnih和Hinton探究了很多方法来构建树结构及其对训练时间和模型精度的影响。在我们的工作中,我们用了一个二元Huffman树,由于它对频繁词分配了短代码,因而可以得到更快的训练速度。之前有观测发现,按照频率将单词分组可以加速基于语言模型的神经网络的训练速度。

 

负抽样

  一种对分层Softmax的替代方案是Noise Contrastive Estimation(NCE),这种方法由Gutmann和Hyvarimen引入,并被Mnih和Teh应用于语言模型中。NCE指出一个好的模型应该可以通过逻辑回归从噪音中区分数据。这类似于Collobert和Weston使用的Hinge loss,他们在训练模型时将数据排在噪音前面。
   尽管NCE可以近似最大化softmax的对数概率,但是Skip-gram模型只关心学习到高质量的向量表示,所以我们可以任意地简化NCE只要向量表示可以保持它们的质量。我们用下面的目标函数来定义负抽样(NEG):

logσ(v′⊤wOvwI)+∑i=1kEwi∼Pn(w)[logσ(−v′⊤wivwI)]


该目标函数用来替换Skip-gram目标函数中的每个logP(wO|wI)。因此任务就是利用逻辑回归从噪音分布Pn(w)抽取的分布中区分目标词wO,对每个数据样本有k个负样本。我们的实验表明对于小的训练数据集k的取值范围大概在5-20之间,而对于大型数据集k约为2-5。负抽样和NCE之间的主要区别在于NCE既需要样本还需要噪音分布的数值概率,而负抽样仅仅使用样本即可。此外尽管NCE近似最大化softmax的对数概率,但是这个性质对于我们的应用不重要。
  NCE和NEG都将噪音分布Pn(w)作为一个自由参数。对于NCE和NEG在每个任务上的表现,我们研究了Pn(w)的选择,发现一元分布U(w)的3/4次幂(也就是U(w)3/4/Z)显著优于一元分布和均匀分布。

 

频繁词的子抽样

  在非常大的语料库中,最频繁的词可能会出现上亿次(如in,the,a等)。这类词相比于那些较少出现的词通常提供较少的信息。例如,尽管Skip-gram模型可以从法国和巴黎的共现中发现模式,但是从同样频繁出现的法国和the中得到较少的信息,因为几乎每个词都会与the共现。这一思想也可反向应用:即频繁词(in, the, a)的向量表示在经过几百万个样本的训练后也不会有明显变化。
  为了考虑稀有词和频繁词的不平衡性,我们用一种简单的子抽样方法:训练集中的每个单词被丢弃概率的计算公式如下:
  

P(wi)=1−tf(wi)−−−−−√


其中f(wi)是单词wi的频率,t是一个选择的阈值,通常为10−5。我们选择这个子抽样公式因为它既可以抽取那些频率大于t的单词,同时又保证了频率的顺序。虽然这个子抽样公式是一个试探性的方法,但是我们发现在实际中很有效。它可以加速学习,甚至极大地提升了学习到的稀有词的向量的精度。

 

实验结果

  本节我们验证了分层Softmax(HS),NCE,负抽样和训练词的子抽样方法。我们使用Mikolov等人[8]引入的类比推理任务。该任务由下面的类比如:“德国”:“柏林”::“法国”:?组成,通过求一个向量x

使得vec(x)接近于vec(Berlin)−vec(Germany)+vec(France),如果x是巴黎那么就认为是正确的。该任务有两大类:句法类比如“quick”: “quickly”::”slow”:”slowly”以及语义类比如国家和首都城市关系。
  为了训练Skip-gram模型,我们使用了由多个新闻文章组成的大型数据集(有10亿单词的谷歌内部数据)。我们去除了训练数据中出现次数不超过5次的单词,得到的词典的规模为692K(也就是词典中有接近70万个单词)。多个Skip-gram模型在单词类比测试集上的效果如表1所示。该表显示:在类比推理任务中负抽样优于分层Softmax方法,甚至略微胜于NCE方法。对频繁词的子抽样提升了训练速度,使得单词表示更加精确。

 

 

背景简介

本人是深度学习入门的菜菜菜鸟一枚…
利用LSTM + word2vec词向量进行文本情感分类/情感分析实验,吸收了网上的资源和代码并尝试转化为自己的东西~

实验环境

  • win7 64位系统
  • Anaconda 4.3.0 , Python 2.7 version
  • Pycharm开发环境
  • python包:keras,gensim,numpy等

实验数据

本文的实验数据是来自网上的中文标注语料,涉及书籍、酒店、计算机、牛奶、手机、热水器六个方面的购物评论数据,具体介绍参见该文:购物评论情感分析

数据处理

上面提到的数据在网上见到的次数比较多,原始格式是两个excel文件,如图:
两个excel

对,就是这两个…估计来到本文的小伙伴也见过。一些代码就是直接从这两个excel里读取数据、分词、处理…不过我表示自己习惯从txt文本里获取数据,因此本人将数据合并、去重(原数据里有不少重复的评论)、分词(用的是哈工大LTP分词)之后存为一份txt文本,保留的数据情况如下:

正面评价个数:8680个
负面评价个数:8000个

文本如图所示:
这里写图片描述

然后人工生成一份【语料类别】文本,用1表示正面评价,用0表示负面评价,与评论数据一一对应。

生成词语的索引字典、词向量字典

利用上述文本语料生成词语的索引字典和词向量字典。
注意:当Word2vec词频阈值设置为5时,词频小于5的词语将不会生成索引,也不会生成词向量数据。

工具:gensim里的Word2vec,Dictionary

代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
功能:利用大语料生成词语的索引字典、词向量,然后保存为pkl文件
时间:2017年3月8日 13:19:40
"""

import pickle
import logging
import tkFileDialog

import numpy as np
np.random.seed(1337)  # For Reproducibility

from Functions.TextSta import TextSta
from gensim.models.word2vec import Word2Vec
from gensim.corpora.dictionary import Dictionary

# 创建词语字典,并返回word2vec模型中词语的索引,词向量
def create_dictionaries(p_model):
    gensim_dict = Dictionary()
    gensim_dict.doc2bow(p_model.vocab.keys(), allow_update=True)
    w2indx = {v: k + 1 for k, v in gensim_dict.items()}  # 词语的索引,从1开始编号
    w2vec = {word: model[word] for word in w2indx.keys()}  # 词语的词向量
    return w2indx, w2vec

# 主程序
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

print u"请选择大语料的分词文本..."
T = TextSta(tkFileDialog.askopenfilename(title=u"选择文件"))
sentences = T.sen()    # 获取句子列表,每个句子又是词汇的列表

print u'训练Word2vec模型(可尝试修改参数)...'
model = Word2Vec(sentences,
                 size=100,  # 词向量维度
                 min_count=5,  # 词频阈值
                 window=5)  # 窗口大小

model_name = raw_input(u"请输入保存的模型文件名...\n").decode("utf-8")
model.save(model_name + u'.model')  # 保存模型

# 索引字典、词向量字典
index_dict, word_vectors= create_dictionaries(model)

# 存储为pkl文件
pkl_name = raw_input(u"请输入保存的pkl文件名...\n").decode("utf-8")
output = open(pkl_name + u".pkl", 'wb')
pickle.dump(index_dict, output)  # 索引字典
pickle.dump(word_vectors, output)  # 词向量字典
output.close()

if __name__ == "__main__":
    pass

其中,

T = TextSta(tkFileDialog.askopenfilename(title=u"选择文件"))
sentences = T.sen()    # 获取句子列表,每个句子又是词汇的列表

TextSta是我自己写的一个类,读取语料文本后,sentences = T.sen()将文本里的每一行生成一个列表,每个列表又是词汇的列表。(这个类原来是用作句子分类的,每行是一个句子;这里每行其实是一个评论若干个句子…我就不改代码变量名了…)

TextSta类部分代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
功能:一个类,执行文本转换
输入:分词文本
输出:句子列表,全文的词汇列表,TF,DF
时间:2016年5月17日 19:08:34
"""

import codecs
import re
import tkFileDialog


class TextSta:
    # 定义基本属性,分词文本的全路径
    filename = ""

    # 定义构造方法
    def __init__(self, path):    # 参数path,赋给filename
        self.filename = path

    def sen(self):    # 获取句子列表
        f1 = codecs.open(self.filename, "r", encoding="utf-8")
        print u"已经打开文本:", self.filename

        # 获得句子列表,其中每个句子又是词汇的列表
        sentences_list = []
        for line in f1:
            single_sen_list = line.strip().split(" ")
            while "" in single_sen_list:
                single_sen_list.remove("")
            sentences_list.append(single_sen_list)
        print u"句子总数:", len(sentences_list)

        f1.close()
        return sentences_list

if __name__ == "__main__": 
    pass

总之,sentences的格式如下:

[[我, 是, 2月, …], [#, 蒙牛, 百, …], …]

所有的评论文本存为一个列表,每个评论文本又是词汇的列表。
sentences列表的长度就是文本的行数:len(sentences) = 16680

利用Keras + LSTM进行文本分类

工具:Keras深度学习库

代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
功能:利用词向量+LSTM进行文本分类
时间:2017年3月10日 21:18:34
"""

import numpy as np

np.random.seed(1337)  # For Reproducibility

import pickle
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers.embeddings import Embedding
from keras.layers.recurrent import LSTM
from keras.layers.core import Dense, Dropout, Activation

from sklearn.cross_validation import train_test_split

from Functions import GetLineList
from Functions.TextSta import TextSta

# 参数设置
vocab_dim = 100  # 向量维度
maxlen = 140  # 文本保留的最大长度
batch_size = 32
n_epoch = 5
input_length = 140


def text_to_index_array(p_new_dic, p_sen):  # 文本转为索引数字模式
    new_sentences = []
    for sen in p_sen:
        new_sen = []
        for word in sen:
            try:
                new_sen.append(p_new_dic[word])  # 单词转索引数字
            except:
                new_sen.append(0)  # 索引字典里没有的词转为数字0
        new_sentences.append(new_sen)

    return np.array(new_sentences)


# 定义网络结构
def train_lstm(p_n_symbols, p_embedding_weights, p_X_train, p_y_train, p_X_test, p_y_test):
    print u'创建模型...'
    model = Sequential()
    model.add(Embedding(output_dim=vocab_dim,
                        input_dim=p_n_symbols,
                        mask_zero=True,
                        weights=[p_embedding_weights],
                        input_length=input_length))

    model.add(LSTM(output_dim=50,
                   activation='sigmoid',
                   inner_activation='hard_sigmoid'))
    model.add(Dropout(0.5))
    model.add(Dense(1))
    model.add(Activation('sigmoid'))

    print u'编译模型...'
    model.compile(loss='binary_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])

    print u"训练..."
    model.fit(p_X_train, p_y_train, batch_size=batch_size, nb_epoch=n_epoch,
              validation_data=(p_X_test, p_y_test))

    print u"评估..."
    score, acc = model.evaluate(p_X_test, p_y_test, batch_size=batch_size)
    print 'Test score:', score
    print 'Test accuracy:', acc


# 读取大语料文本
f = open(u"评价语料索引及词向量.pkl", 'rb')  # 预先训练好的
index_dict = pickle.load(f)  # 索引字典,{单词: 索引数字}
word_vectors = pickle.load(f)  # 词向量, {单词: 词向量(100维长的数组)}
new_dic = index_dict

print u"Setting up Arrays for Keras Embedding Layer..."
n_symbols = len(index_dict) + 1  # 索引数字的个数,因为有的词语索引为0,所以+1
embedding_weights = np.zeros((n_symbols, 100))  # 创建一个n_symbols * 100的0矩阵
for w, index in index_dict.items():  # 从索引为1的词语开始,用词向量填充矩阵
    embedding_weights[index, :] = word_vectors[w]  # 词向量矩阵,第一行是0向量(没有索引为0的词语,未被填充)

# 读取语料分词文本,转为句子列表(句子为词汇的列表)
print u"请选择语料的分词文本..."
T1 = TextSta(u"评价语料_分词后.txt")
allsentences = T1.sen()

# 读取语料类别标签
print u"请选择语料的类别文本...(用0,1分别表示消极、积极情感)"
labels = GetLineList.main()

# 划分训练集和测试集,此时都是list列表
X_train_l, X_test_l, y_train_l, y_test_l = train_test_split(allsentences, labels, test_size=0.2)

# 转为数字索引形式
X_train = text_to_index_array(new_dic, X_train_l)
X_test = text_to_index_array(new_dic, X_test_l)
print u"训练集shape: ", X_train.shape
print u"测试集shape: ", X_test.shape

y_train = np.array(y_train_l)  # 转numpy数组
y_test = np.array(y_test_l)

# 将句子截取相同的长度maxlen,不够的补0
print('Pad sequences (samples x time)')
X_train = sequence.pad_sequences(X_train, maxlen=maxlen)
X_test = sequence.pad_sequences(X_test, maxlen=maxlen)
print('X_train shape:', X_train.shape)
print('X_test shape:', X_test.shape)

train_lstm(n_symbols, embedding_weights, X_train, y_train, X_test, y_test)

if __name__ == "__main__":
    pass

其中,

from Functions import GetLineList

GetLineList是自定义模块,用于获取文本的类别(存为列表),代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
功能:文本转列表,常用于读取词典(停用词,特征词等)
使用:给定一个文本,将文本按行转换为列表,每行对应列表里的一个元素
时间:2016年5月15日 22:45:23
"""

import codecs
import tkFileDialog


def main():
    # 打开文件
    file_path = tkFileDialog.askopenfilename(title=u"选择文件")
    f1 = codecs.open(file_path, "r", encoding="utf-8")
    print u"已经打开文本:", file_path

    # 转为列表
    line_list = []
    for line in f1:
        line_list.append(line.strip())
    print u"列表里的元素个数:", len(line_list)

    f1.close()
    return line_list

if __name__ == "__main__":
    pass

实验结果

这里写图片描述

参考文献:

http://buptldy.github.io/2016/07/20/2016-07-20-sentiment%20analysis/
https://github.com/BUPTLdy/Sentiment-Analysis

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值