Fork me on GitHub

gensim文本相似度分析

背景

海量数据处理课程作业,对于给定的1998-1-人民日报语料,对于每篇文章,给出其余所有文章对它的相似度排序。为了完成这个作业,主要参考了这篇博客http://www.cnblogs.com/pinard/p/7160330.html。学习了一下gensim的基本用法,对NLP中的文本挖掘预处理有了简单的初步认识,接下来对实验过程进行总结。

word2vec原理

词向量基础

分为简单的1-of-N representation和Dristributed representation。

1-of-N representation就是有n个词,则用一个只有一个分量为1的n维向量表示,如图:

图源:cnblogs.com/pinard

Queen对应的词向量则为(0, 1, 0, 0, 0),King则为(1, 0, 0, 0, 0),Women、Man、Child同理。这种表示方法十分简单明了,然而其显著缺点便是,当词汇表规模较大时,特别是在百万级别的文本挖掘中,则需要耗费大量的空间来存储每一个词的百万维的词向量,其总的规模则为1M^2=1T,这显然是不实际的。

Dristributed representation是利用机器学习,通过学习得到每个词在相对纬度小得多的词向量。这样便可以很好地解决1-of-N representation的存储问题。举个具体的例子如图所示:

图源:cnblogs.com/pinard

这样便可以用较短的词向量来表示海量的词汇,这些词向量的具体分量均可以用神经网络来学习得到。

CBOW与Skip-Gram模型

在训练神经网络模型得到词向量的时候,主要用了CBOW和Skip-Gram两种模型。简单地来说,基于三层前馈神经网络,对词向量进行学习,神经网络的输入是目标词汇的上下文词汇的词向量,可以定义上下文的窗口大小为4,那么就输入上下文的八个词汇的词向量,比如:

图源:cnblogs.com/pinard

则输入为an、efficient、method、for、high、quality、distributed、vector的词向量。不考虑每个词到目标词汇的距离,每个词都是平等的,使用BP算法进行训练,输出函数使用softmax函数。神经网络输出为则为learning的词向量。

相反地,Skip-Gram模型则是以learning的词向量为输入,输出上下文八个词汇的词向量。

霍夫曼树

基于对深度神经网络的学习我们知道,对神经网络的训练是十分耗时的,所以利用霍夫曼树来简化词向量生成的过程。 生成霍夫曼树的过程应该不用多讲了,就是将现有的权重最小的两个节点生成一棵树,根节点为叶子节点权重之和,然后继续寻找权重最小的两棵子树,直至所有节点生成一棵树。

图源:cnblogs.com/pinard

一般得到霍夫曼树后我们会对叶子节点进行霍夫曼编码,由于权重高的叶子节点越靠近根节点,而权重低的叶子节点会远离根节点,这样我们的高权重节点编码值较短,而低权重值编码值较长。这保证的树的带权路径最短,也符合我们的信息论,即我们希望越常用的词拥有更短的编码。

基于Hierarchical Softmax的模型概述

word2vec对神经网络进行了两次优化,首先不采取加权和的方式,而直接将输入向量取平均值作为隐含层神经元的激活函数。其次隐含层到输出层的softmax函数采取Huffman树来计算到输出层的映射,能够大大减少计算量。这种一层一层地计算训练模型就叫做Hierarchical Softmax。其结果为对一个词向量输入,它被划为左子树的概率记为P(-),右子树的概率为P(+)=1-P(-)。回到基于Hierarchical Softmax的word2vec本身,我们的目标就是找到合适的所有节点的词向量和所有内部节点θ, 使训练样本达到最大似然。

使用gensim进行文本相似度分析

中文文本挖掘预处理

预处理一次为数据收集、除去数据非文本部分、中文分词、引入停用词、特征处理、建立分析模型。

数据收集主要有网上现有的语料,或者自己写爬虫爬数据两种途径。爬来的数据通常是HTML文件,包含大量非文本部分,比如HTML中的标签,需要对其进行清除。对于英文语料,通常只需要按空格、标点等特殊字符进行分词即可,而中文词汇之间没有像空格这样的明显标记,所以需要进行特殊的方法来分词,可以直接使用现有的Python分词包jieba来进行中文文本分词。分词之后,对于文本中的一些“着”、“和”、标点符号等无用词,进行清除,称为引入停用词。对于特征处理,则可以使用scikit-learning包来进行。有了每段文本的TF-IDF的特征向量,我们就可以利用这些数据建立分类模型,或者聚类模型了,或者进行主题模型的分析。

作业代码

gensim是一个很好用的Python NLP的包,不光可以用于使用word2vec,还有很多其他的API可以用。用它来实现求本文相似度十分简单,跑了4s,直接贴代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# python3.6
# -*- coding:utf-8 -*-
from gensim import corpora, models, similarities
from collections import defaultdict
from pandas import DataFrame
import numpy as np
import time
import pandas as pd

start = time.clock()

# coding
dateDic = defaultdict(list) # 文章日期作为键,文章内容['',''...]作为值
dateIndexs = [] # 保存文章索引
tempIndex = "" # 防止添加重复的索引至dateIndexs中
with open("199801_clear.txt", "r", encoding='utf-8', errors='ignore') as f:
for line in f.readlines():
if line.strip():
words = line.split()
dateId = words[0].rsplit("-", 1)[0] # 提取文章日期作为编号,19980101-01-001-001
dateDic[dateId] += words[1::] # 去除文章日期字符串
if tempIndex != dateId: # 去除重复的日期索引
tempIndex = dateId
dateIndexs.append(tempIndex)
train_documents = [] # 训练语料集
for d in dateDic:
train_documents.append(dateDic[d]) # [[],[],[]]...

# 删除无用词,停用词
texts = [[word for word in document if '/w' not in word and '/y' not in word and '/u' not in word and '/c' not in word]
for document in train_documents]
# 删除只出现一词的词
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] # texts为预处理后的最终语料集[[],[],[]...]

# 建立词袋doc2bow模型
dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]
# 依据词袋模型,计算单词的tf-idf值
tfidf = models.TfidfModel(corpus)
corpus_tfidf = tfidf[corpus]

similarity = similarities.Similarity('Similarity-tfidf-index', corpus_tfidf, num_features=99999)
similarity.num_best = 30 # 最相近的30篇文档

# 前100篇文档
corpus_pre100 = [dictionary.doc2bow(text) for text in texts[0:100]]
corpus_tfidf = tfidf[corpus_pre100]
result_sim = similarity[corpus_tfidf]
elapsed = (time.clock() - start)
print("Time used:", int(elapsed), "s")

# 保存结果
result_csv = pd.DataFrame(result_sim)
result_csv.to_csv("result.csv")
BJTU-HXS wechat
海内存知己,天涯若比邻。