目录
一、从EMD到WMD
二、词移距离(WMD)
举例说明
带监督的词移距离(Supervised Word Mover’s Distance)
三、word2vec实现词移距离
四、词移距离来衡量唐诗诗句的相关性
EMD算法简介,该部分引用自[1]。
Earth Mover’s Distance (EMD),和欧氏距离一样,他们都是一种距离度量的定义,可以用来测量某分布之间的距离。EMD主要应用在图像处理和语音信号处理领域。
EMD问题如下图所示
给定两个签名(或者叫分布、特征量集合)P和Q,P为m个特征量Pi和其权重wPi的集合,记作P={(P1,wP1),(P2,wP2),...(Pm,wPm)},如图左侧部分。同样的,还有一个分布Q,Q=(Q1,wQ1),(Q2,wQ2),...(Qn,wQn),即上图右侧部分。在计算这个这两个签名的Earth Mover's Distance(EMD)前,我们要先定义好P、Q中任意取一个特征量( Pi and Qj )之间的距离(这个距离叫ground distance,两个签名之间EMD依赖于签名中特征量之间的ground distance)。当这两个特征量是向量时得到的是欧式距离,当这两个特征量是概率分布时得到的是相对熵(KL距离/Kullback–Leibler divergence)。现在,给定两个签名(P和Q),只要计算好每两个特征量之间的距离,系统就能给出这两个签名之间的距离了。
EMD 实际上是线性规划中运输问题的最优解。首先,简要描述下运输问题。我们假设这个例子是从多个工厂运输货物到多个仓库。在上图左侧,P从在P1 到 Pm代表m座工厂,工厂Pi有重量为wPi的货物。在上图右侧,Q从Q1到Qn代表n个仓库,仓库Qj最大容量为wQj。货物之间没有什么区别,都是同一类东西。每个仓库都希望装尽可能多的货物。如何尽可能高效把所有货物(实际上不一定是所有货物,部分也OK)从P运送到Q,就是运输问题的优化目标。在本例中,P、Q都是离散的,那么EMD可以用运输问题的Hungarian算法来计算它们之间的距离。挖个坑而已,这里不具体讨论。
这里定义,货物从工厂Pi运到仓库Qj,距离是,运送货物的重量为。这样一次运输需要的工作量为∗。显然,距离越远、或货物越重,工作量就越大。(注:运输可能是多对多的,即一个工厂运输货物到多个仓库,或者一个仓库接收多个工厂的货物。)货物从工厂运到仓库需要很多次这样的运输,经过一些计算和优化,这时我们得到了工作量总和的最小值W。
距离是事先定义的,所以运输量fij是式中唯一的变量,对作如下4个约束:
(1) 运输过程从工厂P到仓库Q,不能反向。
≥ 0 (1≤i≤m,1≤j≤n)
(2) 从工厂Pi一次次运出去的所有货物重量的和不可能超过该工厂中所有货物的总重量wPi
(3) 仓库Qj接收的所有货物总重量不可能超过其总容量wQj。
(4) 总运输量的上限是工厂中货物总重、仓库总容量中的最小值。
当仓库的总容量和工厂货物总重不一样时,我们才需要上述第4个限制条件。仓库总容量比货物总量大的话就可以全部运输了,所以这时候呢,运输量的上限就是货物总量。但如果货物总量比仓库总容量大的话,就不能全部运输了,这时候,运输量的上限就是仓库的总容量啦。方便起见,本例中当仓库的总容量和工厂货物总重是一样的。
运输问题的具体解答此处省略不讨论,假设这个时候我们已经得到了最优解。为了使EMD不会随着总运输量的变化而变化,每一次的运输量还要除以总运输量,以达到归一化的目的。(in order to avoid favoring smaller signatures in the case of partial matching.)在后面的具体例子中会对它进行详细描述。
很自然可以想到,给定两个签名,把一个变成另一个所需要的最小工作量,就是EMD对距离的定义,这里的「工作量」要基于用户对ground distance的定义,即特征量之间的距离的定义。然而,当特征量非常多的时候,由于要做一一匹配,其计算量是非常大的。因此,有人提出了一种将多个特征量组合起来做向量量化编码(Vector Quantization)后再组成签名的方法。
EMD算法在自然语言处理领域的应用
通过词嵌入(Word Embedding),我们可以得到词语的分布式低维实数向量表示,我们可以计算词语之间的距离,即我们可以得到dij,因此可以将EMD引入自然语言处理领域。
Matt等人[2]将词嵌入与EMD相联系,用来度量文档距离。提出了WMD(word mover’s distance)算法,以及WCD(word centroid distance)、RWMD(relaxed word mover’s distance)两种牺牲精度降低复杂度的算法。
如何将一个文档变为分布P这种形式?
用归一化的词袋模型(nBOW, normalized bag-of-words)表示。其中P1表示词语,用计算该词的权重,其中表示词语i在文档中出现了次,P1的特征量用该词语的词向量表示。一个nBOW文档向量是比较稀疏的,因为大部分的词语不会出现在该文档中,且在生成文档向量时,去掉的停用词。用欧式距离计算词语与词语之间的距离。
假定数据集中只有两个文档,则这两个文档生成的归一化nBOW向量如下图所示。
转移量用矩阵T表示,生成的矩阵T如下图所示
综上,Matt等人提出了WMD算法,WMD是EMD的一个特殊形式。
由于文档向量是经过归一化的,与EMD算法相比,WMD没有
的约束。
WMD算法的复杂度是,其中p表示nBOW模型的长度,即数据集中不同词语的数目(去处停用词)。该算法已经有成熟的解决方法,详情参考[3]
算法的改进
为了降低模型的计算复杂度,Matt等人提出了WCD和RWMD两个算法,这两个算法是WMD的两个不同下限,通过降低精度来降低计算复杂度。
Word centroid distance(WCD)
将作为WCD。其中X是一个的矩阵,d表示词向量的维度,p表示nBOW模型的长度。d表示一个nBOW文档向量。如下图所示
Relaxed word moving distance(RWMD)
通过放松限制条件,得到WMD的下限。通过去掉条件2,保留条件1,我们得到
通过去掉条件2,其实是去掉了仓库容量的限制,我们可以将货物全部运到离其最近的仓库,而不需要考虑仓库的容量。我们在运某个货物时,往离该货物最近的仓库运送,即在转移词语i时,我们只向与词语i距离最近的词语j转移。
当我们去掉条件1,保留条件2时,我们得到,与基本相同。去掉条件1,其实是去掉了货物量的限制,我们可以将货物源源不断的运到仓库中,直到仓库满了为止。我们在为填满某个仓库选择运送的货物时,选择离该仓库距离最近的货物,即在词语j接收时,我们选择接收与词语j距离近词语i。
我们将作为RWMD。在计算文档距离时,我们可以事先计算该数据集中,每个词语之间的相似度,复杂度为,之后计算两个文档的时,只需要。
WCD vs. RWMD
RWMD相比WCD更紧,具体的验证可以参考论文[2]。
读完论文[2]后,有些问题:对比实验使用的是欧式距离,欧式距离是否适用于所有的文本表示方式?譬如LDA得到的应该是一个主题概率分布向量,对于概率分布KL距离是否更合适?
Reference:
[1] http://blog.mckelv.in/articles/1474.html
[2] From word embeddings to document distances, Matt J. kusner et al.
[3] Fast and robust earth mover’s distance, Pele et al.
假如现在有一个任务,是判断两段文本之间的相似性,那我们应该怎么做呢?一个很自然的想法是用word2vec对两段文本的词向量化,然后再利用欧氏距离或者余弦相似性进行求解。不过这种方法有着致命的缺陷,即无法从文档整体上来考虑相似性,仅仅是基于词,这就造成了很大的信息缺失问题,下面要介绍的这种方法可以从文档整体上来考虑两个文档之间的相似性,这种技术称为词移距离(WMD)。
究竟什么才是词移距离呢?这要从Word2Vec讲起。Word2Vec将词映射为一个词向量,在这个向量空间中,语义相似的词之间距离会比较小,而词移距离(WMD)正是基于word2vec的这一特性开发出来的。
正如上面所讲到的,Word2Vec得到的词向量可以反映词与词之间的语义差别,那么如果我们希望有一个距离能够反映文档和文档之间的相似度,应该怎么做呢?一个想法是将文档距离建模成两个文档中词的语义距离的一个组合,比如说对两个文档中的任意两个词所对应的词向量求欧氏距离然后再加权求和,大概是这样的形式:∑i,j=1nTijc(i,j)∑i,j=1nTijc(i,j),其中c(i,j)c(i,j)为i,ji,j两个词所对应的词向量的欧氏距离。
那我们怎样得到这个加权矩阵TT呢?又或者说这个加权矩阵TT代表什么含义呢?在我看来,这个加权矩阵TT有些类似于HMM中的状态转移矩阵,只不过其中的概率转换为权重了而已。我们再来看下面这个图:
这里有两个文档,去除停用词后,每篇文档仅剩下4个词,我们就是要用这四个词来比较两个文档之间的相似度。在这里,我们假设’Obama’这个词在文档1中的的权重为0.5(可以简单地用词频或者TFIDF进行计算),那么由于’Obama’和’president’的相似度很高,那么我们可以给由’Obama’移动到’president’很高的权重,这里假设为0.4,文档2中其他的词由于和’Obama’的距离比较远,所以会分到更小的权重。这里的约束是,由文档1中的某个词ii移动到文档2中的各个词的权重之和应该与文档1中的这个词ii的权重相等,即’Obama’要把自己的权重(0.5)分给文档2中的各个词。同样,文档2中的某个词jj所接受到由文档1中的各个词所流入的权重之和应该等于词jj在文档2中的权重。
为什么要有这样的操作呢?因为我们可以设想,∑i,j=1nTijc(i,j)∑i,j=1nTijc(i,j)代表的是文档1要转换为文档2所需要付出的总代价。将这种代价求得下界即最小化之后,即可求得所有文档a中单词转移到文档b中单词的最短总距离,代表两个文档之间的相似度。
形象化的考虑一下,我们有三个文档,文档1中每个词都跟“王者荣耀”紧密相关;文档2中只有一个词跟“王者荣耀”密切相关,其余词都跟“王者荣耀”完全无关;文档3中有一个词跟“王者荣耀”密切相关,其他词都跟“王者荣耀”有点关系但关联性不大。那么可以想象,WMD(d1,d2)>WMD(d1,d3)WMD(d1,d2)>WMD(d1,d3),因为文档1中的词和文档2中和“王者荣耀”完全无关的词之间的距离要比文档1中的词和文档3中和“王者荣耀”有点关系但关联性不大的词之间的距离要大。
我们理解了WMD距离,那么问题来了,学习这个权重矩阵用来聚类虽好(告诉我们哪些文档比较相近),但是, 用来分类却很差,这就要引入监督学习。
为什么? 因为一些文章虽然近义词很多, 但是表达的不是一个语义和主题. 比如: I love playing football 和 I like playing piano . 虽然看起来句式差不多, 可能会归为同类, 但是如果打标签时如果是”运动”和”艺术”两类, 显然就不能用WMD直接分类了. 因为, WMD没有加入 football和”运动”是强相关的信息.
所以, 论文Supervised Word Mover’s Distance给出的解决方案就是在WMD距离中加入可以训练类别权重的功能:
这里的dd加入了类别权重ww:
单词间距离也要进行调整(单词间距离也因为类别不同需要调整距离),加入训练参数矩阵AA。
词移距离(Word Mover's Distance)是在词向量的基础上发展而来的用来衡量文档相似性的度量。
本文主要介绍了词移距离的源码实现,并给出了使用实例计算文本相似度。
安装gensim 并且有pyemd,详情见下
def wmdistance(self, document1, document2):
"""
Compute the Word Mover's Distance between two documents. When using this
code, please consider citing the following papers:
.. Ofir Pele and Michael Werman, "A linear time histogram metric for improved SIFT matching".
.. Ofir Pele and Michael Werman, "Fast and robust earth mover's distances".
.. Matt Kusner et al. "From Word Embeddings To Document Distances".
Note that if one of the documents have no words that exist in the
Word2Vec vocab, `float('inf')` (i.e. infinity) will be returned.
This method only works if `pyemd` is installed (can be installed via pip, but requires a C compiler).
Example:
>>> # Train word2vec model.
>>> model = Word2Vec(sentences)
>>> # Some sentences to test.
>>> sentence_obama = 'Obama speaks to the media in Illinois'.lower().split()
>>> sentence_president = 'The president greets the press in Chicago'.lower().split()
>>> # Remove their stopwords.
>>> from nltk.corpus import stopwords
>>> stopwords = nltk.corpus.stopwords.words('english')
>>> sentence_obama = [w for w in sentence_obama if w not in stopwords]
>>> sentence_president = [w for w in sentence_president if w not in stopwords]
>>> # Compute WMD.
>>> distance = model.wmdistance(sentence_obama, sentence_president)
"""
if not PYEMD_EXT:
raise ImportError("Please install pyemd Python package to compute WMD.")
# Remove out-of-vocabulary words.
len_pre_oov1 = len(document1)
len_pre_oov2 = len(document2)
document1 = [token for token in document1 if token in self]
document2 = [token for token in document2 if token in self]
diff1 = len_pre_oov1 - len(document1)
diff2 = len_pre_oov2 - len(document2)
if diff1 > 0 or diff2 > 0:
logger.info('Removed %d and %d OOV words from document 1 and 2 (respectively).',
diff1, diff2)
if len(document1) == 0 or len(document2) == 0:
logger.info('At least one of the documents had no words that were'
'in the vocabulary. Aborting (returning inf).')
return float('inf')
dictionary = Dictionary(documents=[document1, document2])
vocab_len = len(dictionary)
if vocab_len == 1:
# Both documents are composed by a single unique token
return 0.0
# Sets for faster look-up.
docset1 = set(document1)
docset2 = set(document2)
# Compute distance matrix.
distance_matrix = zeros((vocab_len, vocab_len), dtype=double)
for i, t1 in dictionary.items():
for j, t2 in dictionary.items():
if not t1 in docset1 or not t2 in docset2:
continue
# Compute Euclidean distance between word vectors.
distance_matrix[i, j] = sqrt(np_sum((self[t1] - self[t2])**2))
if np_sum(distance_matrix) == 0.0:
# `emd` gets stuck if the distance matrix contains only zeros.
logger.info('The distance matrix is all zeros. Aborting (returning inf).')
return float('inf')
def nbow(document):
d = zeros(vocab_len, dtype=double)
nbow = dictionary.doc2bow(document) # Word frequencies.
doc_len = len(document)
for idx, freq in nbow:
d[idx] = freq / float(doc_len) # Normalized word frequencies.
return d
# Compute nBOW representation of documents.
d1 = nbow(document1)
d2 = nbow(document2)
# Compute WMD.
return emd(d1, d2, distance_matrix)
词移距离在gensim中有封装包,代码实现如上,本例子直接调用gensim中封装函数
全唐诗txt链接:https://files.cnblogs.com/files/combfish/%E5%85%A8%E5%94%90%E8%AF%97.zip。
步骤:
1. 预处理语料集: 唐诗的断句分词,断句基于标点符号,分词依靠结巴分词
2. gensim训练词向量模型与wmd相似性模型
3. 查询
import jieba
from nltk import word_tokenize
from nltk.corpus import stopwords
from time import time
start_nb = time()
import logging
print(20*'*','loading data',40*'*')
f=open('全唐诗.txt',encoding='utf-8')
lines=f.readlines()
corpus=[]
documents=[]
useless=[',','.','(',')','!','?','\'','\"',':','<','>',
',', '。', '(', ')', '!', '?', '’', '“',':','《','》','[',']','【','】']
for each in lines:
each=each.replace('\n','')
each.replace('-','')
each=each.strip()
each=each.replace(' ','')
if(len(each)>3):
if(each[0]!='卷'):
documents.append(each)
each=list(jieba.cut(each))
text=[w for w in each if not w in useless]
corpus.append(text)
print(len(corpus))
print(20*'*','trainning models',40*'*')
from gensim.models import Word2Vec
model = Word2Vec(corpus, workers=3, size=100)
# Initialize WmdSimilarity.
from gensim.similarities import WmdSimilarity
num_best = 10
instance = WmdSimilarity(corpus, model, num_best=10)
print(20*'*','testing',40*'*')
while True:
sent = input('输入查询语句: ')
sent_w = list(jieba.cut(sent))
query = [w for w in sent_w if not w in useless]
sims = instance[query] # A query is simply a "look-up" in the similarity class.
# Print the query and the retrieved documents, together with their similarities.
print('Query:')
print(sent)
for i in range(num_best):
print
print('sim = %.4f' % sims[i][1])
print(documents[sims[i][0]])
结果:从结果kan
相关参考:
https://www.cnblogs.com/combfish/p/8126857.html
词移距离的具体介绍http://blog.csdn.net/qrlhl/article/details/78512598
官方例子
https://github.com/RaReTechnologies/gensim/blob/c971411c09773488dbdd899754537c0d1a9fce50/docs/notebooks/WMD_tutorial.ipynb
如何通过词向量技术来计算2个文档的相似度?
Supervised Word Mover’s Distance (可监督的词移距离) – NIPS 2016论文精选#2
https://blog.csdn.net/qrlhl/article/details/78512598