您的位置:首页 > 编程语言 > C#

基于word2vec与Word Mover Distance的文档相似度计算

2017-06-07 21:03 453 查看
一、通过词向量进行文档相似度判定的背景与意义 

随着人类对自身秘密的探索与研究的不断深入,以生物技术为基石的人工智能技术也在快速的发展。通过对自身的神经以及思维的研究,人们创造出人工智能,来使在各个领域中创造出能替自己完成工作的“机器”成为可能,从而减轻人们因繁重的体力或脑力劳动而产生的压力。就目前来讲,人工智能的热门领域集中在自然语言生成,语音识别,机器学习,决策管理,和文本分析NLP等等。本文将就文本分析进行展开。文本分析主要应用于问答系统的开发,如基于知识的问答系统(Knowledge-based
QA),基于文档的问答系统(Documen-based QA),以及基于FAQ的问答系统(Community-QA)等。无论哪一种问答系统的开发,都离不开自然语言的理解,而文档相似度的判断对这个方面有着重要影响。对于问题的内容,需要进行相似度匹配,从而选择出与问题最接近,同时最合理的答案。其中,基于Google开源软件word2vec训练生成词向量后进行文档相似度匹配是较重要的方向之一。

二、文档相似度判定的研究内容

2.1利用Google开源工具word2vec训练词向量

word2vec是Google在2013推出的一款用于获取词向量的工具包,它的简单与高效引起了大量从事相关工作的开发者的关注。它的特点如下:

   (1)word2vec的模型通过一种神经网络语言模型(Neural Network Language Model)对语料中的所有词汇进行训练并生成相应的词向量(Word Embedding),通过对词向量的距离(如余弦值或者欧氏距离)的计算即可得出两个词的相似度。             

   (2)与CBOW(连续词袋模型)利用词语的上下文来预测词语相反,word2vec利用的Skip-Gram模型利用词语来预测其上下文。通过把一个个的词语当做特征,将特征映射到K维向量空间中去,来获得文本数据更精确的特征显示。

   (3)根据词频用Huffman编码,使得出现频率越高的词语,他们激活的隐藏层数目越少,这样有效的降低了计算的复杂度

   (4)综上,word2vec高效的实现了词向量的训练,甚至在优化好的单机版本中一天就可训练上亿个词汇,这是我选择word2vec来生成词向量的原因。

2.2利用WMD(Word Mover Distance)模型计算文档相似度

2.2.1WMD模型原理介绍

WMD的模型基于EMD(Earth Mover Distance)模型。EMD和欧式距离一样,它们都是一种距离度量的定义、可以用来测量某两个分布之间的距离。其主要应用在图像处理和语音信号处理领域,WMD的模型正是基于EMD,将该模型的适用范围延伸到了自然语言处理领域。对EMD的详细原理不再此赘述。

Matt等人(见参考文献)将词嵌入与EMD相联系,用来度量文档距离。提出了WMD(word mover’s distance)算法,以及WCD(word centroid distance)、RWMD(relaxed word mover’s distance)两种牺牲精度降低复杂度的算法。
   (1)WMD算法利用NBOW(normalized bag-of-words即归一化的词袋模型)表示分布P。其中P1表示词语本身,用来计算该词在当前文档中的权重,其中表示词语i在所属文档中出现了几次,P1的特征量用该词语的词向量表示。
   (2)WMD算法利用求一个Word travel cost来计算词i到词j的相似度,即是求词i与词j的词向量的欧式距离,距离值为C(i,j)=|(vecI-vecJ)|。在这里将C(i,j)看做将词i转化为词j所付出的代价。
  (3)WMD算法利用求Document distance来表示文档间的距离。
   (如上图所示,箭头表示各个文档(D)转化到其他文档时,各个词在转化代价中的贡献值)
在这里定义矩阵T ,。其中TijTij>=0)表示d文档中的词i有多少转化为了d’文档中的词j
。为了保证文档d能转化为文档d’,需要保证词i转化为d’中所有词语的量之和为di,即,同理,也应该满足d文档中转化到d’文档中的词j的总量为dj,即。我们即可定义文档dd’的距离为,将d中所有词转化为d’中词的最小代价,即求
 
2.2.2WMD的更快计算方法

WMD算法的复杂度是O(P^3*logP),其中P表示nBOW模型的长度,即数据集中不同词语的数目(去处停用词)。
为了降低模型的负责度,Matt等人还提出了WCD和RWMD两个算法,通过降低精度来降低算法的复杂度。这两个算法都是求得WMD的其中一种下限。实现时采用后者。

 

 

(1)WCD(Word centroid distance)
WCD方法是指让文档的加权平均词向量来代表各个文档。通过少量的矩阵运算就能得出结果,算法复杂度仅O(dp),可以求得WMD问题的其中一个下限,是最快的一种计算方法。将作为WCD,其中X是一个

的矩阵,d表示词向量的维度,p表示NBOW模型长度。

 
(2)RWMD(Relaxed word moving distance)

尽管WCD计算很快,但是下限并不是非常紧。我们可以通过放宽WMD的一些条件限制来降低WMD问题的复杂度,即去除掉2个限制条件中的后者。修改后的问题如下:

拿仓库问题来类比,通过去掉条件2,其实是去掉了仓库容量的限制,我们可以将货物全部运到离其最近的仓库,而不需要考虑仓库的容量。我们在运某个货物时,往离该货物最近的仓库运送,即在对d文档中的词i进行转换时,我们只需要将它转换到d’文档中离它最近的词j即可,而不需要考虑j所能容纳的转换的限制。

在用RWMD计算文档相似度时,可以先求出d文档中所有i
ad6b
d’文档中任意j的最近的C(i,j)(此处为欧式距离)即可,算法复杂度为O(p^2)。之后再用各个Tij乘以相应的最小C(i,j)即可得出结果,算法复杂度为O(p)。真个RWMD算法的总复杂度为O(p^2)

 
2.2.3RWMD算法的C#实现

word2vec的训练语料来自Wiki百科,训练后的词向量数据大小约1G。文档分词采用中科院的NIPIR。

此处用对问题答案对的相似度计算来演示文档相似度的计算。
public class WordEmbeddingPredictor : IPredictor
{
Dictionary<string, List<double>> word2vector; //词向量映射

public WordEmbeddingPredictor(string word2vectorFile) //初始化Predictor
{

word2vector = new Dictionary<string, List<double>>();

Console.WriteLine("Loading word2vec model...");
LoadWord2Vec(word2vectorFile);
Console.WriteLine("Load word2vec model done.");
}
public double GetSimilarity(string question, string answer) //获取问题与答案的相似度
{
var qWords = Tokenizer.Tokenize(question); //分词
var aWords = Tokenizer.Tokenize(answer);

return ComputeEmbeddingDistance(qWords, aWords); //计算偏移距离
}
void LoadWord2Vec(string word2vectorFile)//加载训练好的词向量文件
{

using (StreamReader sr = new StreamReader(word2vectorFile))
{
//read vocab size + dim size
string[] fileInfo = sr.ReadLine().Split(' ');
var vecLength = int.Parse(fileInfo[1]);

while (!sr.EndOfStream)
{
string[] parts = sr.ReadLine().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

if (parts.Length != 1 + vecLength) continue;

var word = parts[0];

if (!word2vector.ContainsKey(word))
{
List<double> vector = new List<double>();
for (int i = 1; i <= vecLength; i++)
{
vector.Add(double.Parse(parts[i]));
}
word2vector.Add(word, vector);
}

}
}

}

double ComputeEmbeddingDistance(IList<string> qWords, IList<string> aWords)
{
//载入每个在词向量中存在的词
var word2Frecs = GetWord2Frec(qWords, aWords);
//Console.WriteLine("Ok");
var qWord2Frec = word2Frecs[0];
var aWord2Frec = word2Frecs[1];
if (qWord2Frec.Count == 0 || aWord2Frec.Count == 0)
{
return -10000000; //如果问题或者答案中的词都没有出现在词向量中,返回一个较小的负值,避免对其他测试数据的干扰
}
List<double> minMetrics = new List<double>(); //计算每个i对应的最短欧式距离
List<double> frec = new List<double>();

for (int i = 0; i < qWords.Count; i++) //计算过程
{
if (!qWord2Frec.ContainsKey(qWords[i]))
{
continue;
}
double minMetric = double.MaxValue;
for (int j = 0; j < aWords.Count; j++)
{
if (!aWord2Frec.ContainsKey(aWords[j]))
{
continue;
}
IList<double> iVec = word2vector[qWords[i]];
IList<double> jVec = word2vector[aWords[j]];
double curMetric = GetEuclideanMetric(iVec, jVec);
if (curMetric < minMetric)
{
minMetric = curMetric;
}
}
minMetrics.Add(minMetric);
// Console.WriteLine("go");
frec.Add(qWord2Frec[qWords[i]]);
}
double sum = 0;
for (int i = 0; i < minMetrics.Count; i++)
{
sum += minMetrics[i] * frec[i];
}
return -sum; //因为值越小越不相似,所以取负后越相似值越大
}

IList<Dictionary<string, double>> GetWord2Frec(IList<string> qWords, IList<string> aWords)//获取所有qa中且词库中存在的词以及频率并返回
{
Dictionary<string, double> qWord2Frec = new Dictionary<string, double>(); //记录词语的出现次数
Dictionary<string, double> aWord2Frec = new Dictionary<string, double>();
foreach (var str in qWords)
{
if (!word2vector.ContainsKey(str))
{
continue;
}
if (!qWord2Frec.ContainsKey(str))
{
qWord2Frec.Add(str, 1.0);
}
else
{
qWord2Frec[str] += 1.0;
}
//Console.WriteLine("Ok");
}
foreach (var str in aWords)
{
if (!word2vector.ContainsKey(str))
{
continue;
}
if (!aWord2Frec.ContainsKey(str))
{
aWord2Frec.Add(str, 1.0);
}
else
{
aWord2Frec[str] += 1.0;
}
//Console.WriteLine("Ok2");
}
int qDicLen = qWord2Frec.Count;
int aDicLen = aWord2Frec.Count;
var qKeys = qWord2Frec.Keys;
var aKeys = aWord2Frec.Keys;
foreach (var str in qKeys.ToArray()) //不能在遍历时修改
{
qWord2Frec[str] /= (double)qDicLen;
}
//Console.WriteLine("Ok3");
foreach (var str in aKeys.ToArray())
{
aWord2Frec[str] /= (double)aDicLen;
}
//Console.WriteLine("Ok4");
List<Dictionary<string, double>> dics = new List<Dictionary<string, double>>();
dics.Add(qWord2Frec);
dics.Add(aWord2Frec);
return dics;
}

double GetEuclideanMetric(IList<double> qVec, IList<double> aVec)
{
double sum = 0;
for (int i = 0; i < qVec.Count; i++)
{
sum += Math.Pow((qVec[i] - aVec[i]), 2);
}
return Math.Sqrt(sum);
}

}
三、总结

WMD算法是对基于词向量进行文档相似度判断的一个不错的想法,模型本身简单明了,构思巧妙,但是解决实际问题时的准确度仍有限,且算法复杂度较高。在今后的学习中,希望了解更多的自然语言处理的相关知识,如利用各种神经网络(CNN,RNN等)来进行处理。

四、参考文献

[1] MattJ.Kusner YuSun等人.From Word Embeddings To Document Distances[C].

Washington University in St. Louis, 1 Brookings Dr., St. Louis, MO 63130

[2] 刘龙飞.http://ir.dlut.edu.cn/news/detail/362[Z].大连:大连理工大学信息检索研究室

[3] 梁俊毅. 人工智能的发展及其认知意义[C].广西:广西工业职业技术学院

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息