之前这篇文章因为敏感关键词被下架了,现在将一些词语换成星号重发,希望大家能够理解。
一般情况下,文本都是由句子组成的,句子都是由词组成的。在介绍文本表示方式之前,有必要了解一下词的常用表示方式。
词表示方法
1 one-hot编码
one-hot编码也叫独热编码,假设我们有一个词典["male", "female"],里面有两个词。one-hot编码根据词典大小(这里是2),将文本中的没个词表示成一个独立的向量,比如上面这个词典中的每个词经过one-hot编码后可能是[01,10]。更生动详细的解释清参考https://www.jianshu.com/p/471d9bfbd72f。
优点:一是解决了分类器不好处理离散数据的问题,二是在一定程度上也起到了扩充特征的作用。
缺点:在文本特征表示上有些缺点就非常突出了。首先,它是一个词袋模型,不考虑词与词之间的顺序(文本中词的顺序信息也是很重要的);其次,它假设词与词相互独立(在大多数情况下,词与词是相互影响的);最后,它得到的特征是离散稀疏的。
2 Brown聚类
针对one-hot编码的后两个缺点,我们可以考虑布朗聚类。布朗聚类是一种自底向上的层次聚类算法,基于n-gram模型和马尔科夫链模型。布朗聚类是一种硬聚类,每一个词都在且只在唯一的一个类中。
w是词,c是词所属的类。
布朗聚类的输入是一个语料库,这个语料库是一个词序列,输出是一个二叉树,树的叶子节点是一个个词,树的中间节点是我们想要的类(中间结点作为根节点的子树上的所有叶子为类中的词)。
初始的时候,将每一个词独立的分成一类,然后,将两个类合并,使得合并之后评价函数最大,然后不断重复上述过程,达到想要的类的数量的时候停止合并。该算法是一种层次化的聚类算法。在得到层次化结构的词类簇之后,我们可以用根节点到词之间的路径来表示该词。
3 词嵌入
**2013年提出的Word2vec就是词嵌入模型之一,词嵌入时将词向量化的模型的通称,其核心思想是将每个词映射成低维-K维空间(通常K=50~300)的一个稠密向量(Dense Vector)。更生动详细的解释请参考https://www.jianshu.com/p/471d9bfbd72f。
一段文本的表示方法
文本本身不能够被机器学习算法直接读取处理,在训练模型时,需要提前将一段文本表示成一个只含有数字的向量或矩阵,这一过程也叫特征抽取。文本表示方法大致分为三类,即基于向量空间模型、基于主题模型和基于神经网络的方法,本文会针对每类给出一些具有代表性的模型。
1 基于向量空间模型的方法
向量空间模型是将文本表示成实数值分量所构成的向量,一般而言,每个分量对应一个词项,相当于将文本表示成空间中的一个点。向量不仅可以用来训练分类器,而且计算向量之间的相似度可以度量文本之间的相似度。
最常用的是TF-IDF计算方式,即向量的维度对应词表的大小,对应维度使用TF-IDF计算。向量空间模型的优点是简单明了,向量维度意义明确,效果不错,但也存在明显的缺点,其一,维度随着词表增大而增大,且向量高度稀疏;其二,无法处理“一义多词”和“一词多义”问题。
TF-IDF实践步骤,也即是一般的文本处理和模型训练步骤:
1.获取原始文本内容信息。
2.转换成纯小写,按空格把文章分成独立的词组成的list。
3.去除噪音符号: [""","=","\","/",":","-","(",")",",",".","\n"]等。
4.去除停用词。
5.提取词干,把相近的词转换为标准形式,比如把文章中的go,going,went,goes统一成go 。
6.wordcount,统计每个词出现的次数,去掉出现次数较少的词,比如在一百篇文档中,只出现了1~2次的词,显然是没有意义的。
7.训练idf模型。
8.对输入的每篇测试文章计算其tfidf向量,然后可以利用tfidf向量求文章之间的相似度(比如用欧拉距离,余弦相似度,Jaccard系数等方法)。
中文的处理可适当省略上述部分步骤。用TF-IDF表示文档的简略代码如下:
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
if __name__ == "__main__":
corpus = []
#读取预料 一行预料为一个文档
for line in open('test.txt', 'r').readlines():
#print line
corpus.append(line.strip())
#print (corpus)#将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer()
#该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer()
#print(transformer)
#第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))
#print(tfidf)
#获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
print(word)
weight = tfidf.toarray()
print(weight)
在向量空间模型中,文档集合相当于表示成高维稀疏矩阵,如图1中所示,文档集合矩阵的维度是N*V,其中N是文档数目,V是词表的大小。为了更好的提升文本的语义表示能力,有人提出通过矩阵分解的方法,对高维稀疏矩阵进行分解,最为著名的便是潜在语义分析(Latent semantic analysis, LSA),具体而言,LSA会构建一个文档与词项的共现矩阵,矩阵的元素一般通过TFIDF计算得到(也可以用词频等其他方式),最终通过奇异值分解的方法对原始矩阵降维,可以得到文档向量和词项向量。如图1所示,分解后,每个文档可以用k维向量表示
图1 LSA
LSA(latent semantic analysis)潜在语义分析,也被称为LSI(latent semantic index),是Scott Deerwester, Susan T. Dumais等人在1990年提出来的一种新的索引和检索方法。该方法和传统向量空间模型(vector space model)一样使用向量来表示词(terms)和文档(documents),并通过向量间的关系(如夹角)来判断词及文档间的关系;而不同的是,LSA将词和文档映射到潜在语义空间,从而去除了原始向量空间中的一些“噪音”,提高了信息检索的精确度,还可以解决一词多义和一义多词的问题。LSA的基本思想就是把高维的文档降到低维空间,那个空间被称为潜在语义空间。这个映射必须是严格线性的而且是基于共现表(就是那个矩阵啦)的奇异值分解。
LSA使用代码举例:
from gensim import corpora,models,similarities
from collections import defaultdict
import codecs
import jieba
f2 = codecs.open(r"D:/data/files/r.txt",'r','utf-8')
documents = f2.readlines()
f1 = codecs.open('D:/data/files/topic.txt', 'r')
topicnames = f1.readlines()
f1.close()
# 去掉停用词
#f = codecs.open(r"D:\tomcat\word2vector\hlt_stop_words.txt",'r')
#stopwords = f.readlines()
#stoplist = set(stopwords)
stoplist = {}.fromkeys([ line.rstrip() for line in codecs.open(r"D:\tomcat\word2vector\hlt_stop_words.txt",'r','utf-8') ])
#stoplist = {}.fromkeys(u'的')
texts = [[word for word in document.split() if word not in stoplist]
for document in documents]
#print "".join(texts[51])
frequency = defaultdict(int)
for text in texts:
for token in text:
frequency[token] += 1
texts = [[token for token in text if frequency[token] > 1]
for text in texts]
dictionary = corpora.Dictionary(texts) # 生成词典
dictionary.save('D:/tomcat/word2vector/tmp/deerwester.dict')
corpus = [dictionary.doc2bow(text) for text in texts]
corpora.MmCorpus.serialize('D:/tomcat/word2vector/tmp/deerwester.mm', corpus)
corpus = corpora.MmCorpus('D:/tomcat/word2vector/tmp/deerwester.mm')
lsi = models.LsiModel(corpus, id2word=dictionary, num_topics=1000)
doc = ["2016高校校园媒体发展报告",
"消费金融 创新新升升级2016首届金融科技**论坛隆重隆重召开召开 ",
"2015年***文文艺文艺片票房增幅达 68",
"*** 喜剧 20年开启我不是******笑里有料 ",
"******数数据金融发展交流暨**金融金融城高铁新城招商推介 推介会 隆重 隆重举行 举行 ",
"云栖大会**峰会今召开 ",
"深谋远虑的***也没想到它的崛起!",
"015年我*文艺片票房增幅达68% **社会 ** 天下 新闻中心 **网",
"开公司为3万会员***提供非转基因商品",
"网上曝光 淘宝兼职是真的吗 揭露刷单陷阱 亲身经历",
"四川新闻战线践行***讲话 讲述四川好故事 传递**正能量",
"新闻界热议***讲话 唯有创新奋斗方能不辱使命",
"以******系列重要讲话精神为指引忠诚担当 攻坚克难 为全面建成小康社会而奋斗",
" 她曾主持**因做**离开** 当演员却再次做**",
"在职研究生提供住宿吗 "
]
for j in range(len(doc)):
words = jieba.cut(doc[j], cut_all=False)
x = " ".join(words)
texts = [word for word in x.split() if word not in stoplist]
texts = " ".join(texts)
#print texts
vec_bow = dictionary.doc2bow(texts.split())
vec_lsi = lsi[vec_bow]
#print(vec_lsi)
index = similarities.MatrixSimilarity(lsi[corpus])
index.save('D:/tomcat/word2vector/tmp/deerwester.index')
index = similarities.MatrixSimilarity.load('D:/tomcat/word2vector/tmp/deerwester.index')
sims = index[vec_lsi]
#print(list(enumerate(sims)))
sims = sorted(enumerate(sims), key=lambda item: -item[1])
print(doc[j])
print("与它相似的话题及相似度:")
for i in range(10):
print(topicnames[sims[i][0]])
print(sims[i][1])
print "======================================="
---------------------
作者:6丁一的猫
来源:CSDN
原文:https://blog.csdn.net/weixin_36541072/article/details/53213226
版权声明:本文为博主原创文章,转载请附上博文链接!
2 基于主题模型的方法
第1节中提到LSA算法通过线性代数中奇异值分解实现文档映射到低维语义空间里的向量,但是空间中每一个维度是没有明确物理意义的,主题模型尝试从概率生成模型的角度实现文本的表示,每一个维度是一个“主题(topic)”,这个主题通常是一组词的聚类,因此可以通过主题大概猜测每个维度所代表的语义,具有一定的解释性。在主题模型中,最为著名的当属LDA,有关LDA的解释,请参考我的另一篇文章Latent Dirichlet Allocation(隐狄利克雷分配模型)。利用LDA进行文本处理的例子请参考:
python下进行lda主题挖掘(一)——预处理(英文)
python下进行lda主题挖掘(二)——利用gensim训练LDA模型
python下进行lda主题挖掘(三)——计算困惑度perplexity
3 基于神经网络的方法
现今,基于神经网络的方法受到广泛关注,各种各样的模型被相继提出,本节总结其中最具有代表性的模型,将其分为三类:
第一类,基于词向量合成的模型,该类方法仅是在词向量基础上简单合成;
第二类,基于RNN/CNN的模型,该类方法利用更复杂的深度学习模型对文本进行建模;
第三类,基于注意力机制的模型,在已有神经网络模型基础上,引入注意力机制,提升文本建模效果。
基于词向量合成的模型
2003年Bengio等人开始使用神经网络来做语言模型的工作,尝试得到词的低维、稠密的向量表示,2013年Mikolov等人提出简化的模型,即著名的Word2Vec,包含两个模型CBOW和Skip-gram,前者通过窗口语境预测目标词出现的概率,后者使用目标词预测窗口中的每个语境词出现的概率。语义上相似或相关的词,得到的表示向量也相近,这样的特性使得Word2Vec获得巨大成功。详细学习Word2Vec强参考我的另一篇文章通俗理解word2vec。
后来,Mikolov等人又延续Word2Vec的思想,提出Doc2Vec,核心思想是将文档向量当作“语境”,用来预测文档中的词。Doc2Vec算法可以得到词向量和文档向量。如图4所示,两个算法的思想基本一致。
Doc2vec是基于Word2vec基础上构建的,相比于Word2vec,Doc2vec不仅能训练处词向量还能训练处句子向量并预测新的句子向量。Doc2vec模型结构相对于Word2vec,不同点在于在输入层上多增加了一个Paragraph vector句子向量,该向量在同一句下的不同的训练中是权值共享的,这样训练出来的Paragraph vector就会逐渐在每句子中的几次训练中不断稳定下来,形成该句子的主旨。这样就训练出来了我们需要的句子向量。在预测新的句子向量时,是需要重新训练的,此时该模型的词向量和投影层到输出层的soft weights参数固定,只剩下Paragraph vector用梯度下降法求得,所以预测新句子时虽然也要放入模型中不断迭代求出,相比于训练时,速度会快得多。
使用Doc2Vec进行分类任务,我们使用 IMDB电影评论数据集作为分类例子,测试gensim的Doc2Vec的有效性。数据集中包含25000条正向评价,25000条负面评价以及50000条未标注评价。
#!/usr/bin/python
import sys
import numpy as np
import gensim
from gensim.models.doc2vec import Doc2Vec,LabeledSentence
from sklearn.cross_validation import train_test_split
LabeledSentence = gensim.models.doc2vec.LabeledSentence
##读取并预处理数据
def get_dataset():
#读取数据
with open(pos_file,'r') as infile:
pos_reviews = infile.readlines()
with open(neg_file,'r') as infile:
neg_reviews = infile.readlines()
with open(unsup_file,'r') as infile:
unsup_reviews = infile.readlines()
#使用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 cleanText(corpus):
punctuation = """.,?!:;(){}[]"""
corpus = [z.lower().replace('\n','') for z in corpus]
corpus = [z.replace('<br />', ' ') for z in corpus]
#treat punctuation as individual words
for c in punctuation:
corpus = [z.replace(c, ' %s '%c) for z in corpus]
corpus = [z.split() for z in corpus]
return corpus
x_train = cleanText(x_train)
x_test = cleanText(x_test)
unsup_reviews = cleanText(unsup_reviews)
#Gensim的Doc2Vec应用于训练要求每一篇文章/句子有一个唯一标识的label.
#我们使用Gensim自带的LabeledSentence方法. 标识的格式为"TRAIN_i"和"TEST_i",其中i为序号
def labelizeReviews(reviews, label_type):
labelized = []
for i,v in enumerate(reviews):
label = '%s_%s'%(label_type,i)
labelized.append(LabeledSentence(v, [label]))
return labelized
x_train = labelizeReviews(x_train, 'TRAIN')
x_test = labelizeReviews(x_test, 'TEST')
unsup_reviews = labelizeReviews(unsup_reviews, 'UNSUP')
return x_train,x_test,unsup_reviews,y_train, y_test
##读取向量
def getVecs(model, corpus, size):
vecs = [np.array(model.docvecs[z.tags[0]]).reshape((1, size)) for z in corpus]
return np.concatenate(vecs)
##对数据进行训练
def train(x_train,x_test,unsup_reviews,size = 400,epoch_num=10):
#实例DM和DBOW模型
model_dm = gensim.models.Doc2Vec(min_count=1, window=10, size=size, sample=1e-3, negative=5, workers=3)
model_dbow = gensim.models.Doc2Vec(min_count=1, window=10, size=size, sample=1e-3, negative=5, dm=0, workers=3)
#使用所有的数据建立词典
model_dm.build_vocab(np.concatenate((x_train, x_test, unsup_reviews)))
model_dbow.build_vocab(np.concatenate((x_train, x_test, unsup_reviews)))
#进行多次重复训练,每一次都需要对训练数据重新打乱,以提高精度
all_train_reviews = np.concatenate((x_train, unsup_reviews))
for epoch in range(epoch_num):
perm = np.random.permutation(all_train_reviews.shape[0])
model_dm.train(all_train_reviews[perm])
model_dbow.train(all_train_reviews[perm])
#训练测试数据集
x_test = np.array(x_test)
for epoch in range(epoch_num):
perm = np.random.permutation(x_test.shape[0])
model_dm.train(x_test[perm])
model_dbow.train(x_test[perm])
return model_dm,model_dbow
##将训练完成的数据转换为vectors
def get_vectors(model_dm,model_dbow):
#获取训练数据集的文档向量
train_vecs_dm = getVecs(model_dm, x_train, size)
train_vecs_dbow = getVecs(model_dbow, x_train, size)
train_vecs = np.hstack((train_vecs_dm, train_vecs_dbow))
#获取测试数据集的文档向量
test_vecs_dm = getVecs(model_dm, x_test, size)
test_vecs_dbow = getVecs(model_dbow, x_test, size)
test_vecs = np.hstack((test_vecs_dm, test_vecs_dbow))
return train_vecs,test_vecs
##使用分类器对文本向量进行分类训练
def Classifier(train_vecs,y_train,test_vecs, y_test):
#使用sklearn的SGD分类器
from sklearn.linear_model import SGDClassifier
lr = SGDClassifier(loss='log', penalty='l1')
lr.fit(train_vecs, y_train)
print 'Test Accuracy: %.2f'%lr.score(test_vecs, y_test)
return lr
##绘出ROC曲线,并计算AUC
def ROC_curve(lr,y_test):
from sklearn.metrics import roc_curve, auc
import matplotlib.pyplot as plt
pred_probas = lr.predict_proba(test_vecs)[:,1]
fpr,tpr,_ = roc_curve(y_test, pred_probas)
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.show()
##运行模块
if __name__ == "__main__":
#设置向量维度和训练次数
size,epoch_num = 400,10
#获取训练与测试数据及其类别标注
x_train,x_test,unsup_reviews,y_train, y_test = get_dataset()
#对数据进行训练,获得模型
model_dm,model_dbow = train(x_train,x_test,unsup_reviews,size,epoch_num)
#从模型中抽取文档相应的向量
train_vecs,test_vecs = get_vectors(model_dm,model_dbow)
#使用文章所转换的向量进行情感正负分类训练
lr=Classifier(train_vecs,y_train,test_vecs, y_test)
#画出ROC曲线
ROC_curve(lr,y_test)
其实,也可以通过最简单的合成方式实现从词向量到句子向量的表示,fastText就是这样简单有效的模型,输入层是词向量,然后通过把句子里的词向量平均就得到句子的表示,最后送到分类器中。不过,输入端会另外补充一些n-gram信息来捕捉局部序列信息。fastText是线性分类模型,实验表明在诸多“简单”文本分类任务中表现出色,且具备训练速度非常快的优点,所以可以成为很好的Baseline。
基于RNN/CNN的模型
自然语言中,词构成句子,句子构成文档,有很多工作尝试合理表示词向量同时,也有很多模型被提出来建模句子和文档,其中最常见的网络结构便是LSTM和CNN。
2014年Kim提出基于卷积神经网络的文本分类,如图2所示,输入是句子对应的词向量矩阵,经过一层卷积层和一层Max Pooling层,得到句子的表示,送入到全连接层,最后softmax输出。卷积神经网络擅长提取重要的局部特征,在文本分类中,可以理解为不同大小的卷积核在提取不同n-gram特征。一般认为,卷积神经网络无法考虑长距离的依赖信息,且没有考虑词序信息,在有限的窗口下提取句子特征,会损失一些语义信息。
图2 卷积神经网络
针对CNN的不足之处,LSTM和GRU等循环神经网络因为擅长捕捉长距离信息,所以也被大家尝试用来文本表示。如图3所示,图中利用双向LSTM来建模输入句子,输入是句子的词向量,输入至BiLSTM中进行序列建模。最后句子表示,可以尝试两种方法,其一,选择最后的hidden state用来表示句子;其二,所有hidden state的平均用于表示句子。
图3 BiLSTM用于文本表示
刚才分析到,CNN擅长提取局部特征,而LSTM擅长捕捉长距离信息,不难想到,有人尝试结合两种网络的优点,提出RCNN用于文本建模。如图4所示,整个网络结构主要有两部分组成,循环结构和Max Pooling。循环结构,可以理解为,在已有词向量为输入的基础上,通过双向RNN网络学习每一个词的左、右上下文信息,接着将三部分(left context, word embedding, right context)表示向量拼接起来,作为句子中每一个词的表示,然后使用变换得到中间语义表示;Max Pooling层,采用element-wise的max pooling方式,可以从变长的输入中得到固定的句子表示。
图4 RCNN用于文本表示
基于注意力机制的模型
注意力被认为是一种有效选择信息的方式,可以过滤掉大量与任务无关的信息,最先在机器翻译任务中被提出,解决seq2seq中encoder过程把源序列映射成固定大小的向量存在“损失”信息的情况。紧接着,Attention被推广到各种NLP任务中,文本表示任务当然不例外。这里,主要介绍两种Attention的形式,Hierarchical Attention 和 Self-Attention。
Hierarchical Attention网络结构,如图5所示,该模型基于两个基本假设,其一,文档是分层结构的,词构成句子,句子构成文档;其二,文档中不同词或句子提供的信息量不一样的,该模型适合用来表示包含多个句子的文档的表示问题。模型主要由word encoder和sentence encoder,以及相应的attention组成,word encoder部分用于得到句子的表示,该层的输入为句子的词向量,经过双向GRU后得到中间表示,word attention部分对中间表示按attention值进行加权得到此句最终的句子表示;sentence encoder部分用于得到文档的表示,使用word encoder部分得到文档中所有句子的表示后,将此作为sentence encoder部分的输入,类比word encoder的计算,可以最终得到文档的表示。简言之,利用Hierarchical Attention结构,一层词输入得到句子表示,一层句子输入得到文档表示。即使文档长度较长,但是在注意力的作用下,依然可以较好的捕捉到有效的特征信息,忽略无意义的输入。
图5
Self-Attention网络结构,如图6所示,大多数神经网络模型将文本表示成一维的向量,但是此模型通过二维矩阵来表示句子,包括两部分,一部分是双向的LSTM,另一部分是自注意力机制,自注意力机制实现对双向LSTM中所有的隐藏状态以不同权重的方式线形组合,每一次组合获得句子的一部分表示,多次组合便得到矩阵表示(图中矩阵M)。
图6
参考资料:https://blog.csdn.net/u014516670/article/details/50574147
https://blog.csdn.net/ch1209498273/article/details/78449554
https://cloud.tencent.com/developer/news/302139
https://blog.csdn.net/ximibbb/article/details/79264574
https://blog.csdn.net/fkyyly/article/details/84665361
https://blog.csdn.net/fendouaini/article/details/80327250
网友评论