为了计算两个文档之间的相似性,我创建了一个包含术语频率的特征向量。但是,对于下一步,我无法在“Cosine similarity”和“Hamming distance”之间做出决定。
我的问题:你有使用这些算法的经验吗?哪一个给你更好的结果?
除此之外:你能告诉我如何在PHP中编码余弦相似度吗?对于汉明距离,我已经得到了代码:
function check ($terms1, $terms2) {
$counts1 = array_count_values($terms1);
$totalScore = 0;
foreach ($terms2 as $term) {
if (isset($counts1[$term])) $totalScore += $counts1[$term];
}
return $totalScore * 500 / (count($terms1) * count($terms2));
}
我不想使用任何其他算法。我只想在两者之间做出决定。
也许有人可以说一些如何改进算法。如果你过滤掉停用词或常用词,你会得到更好的结果吗?
我希望你能帮助我。提前谢谢!
答案 0 :(得分:16)
应该在两个相等长度的琴弦之间进行汉明距离并考虑顺序。
由于您的文件长度不一,如果不计算字词,余弦相似性更好(请注意,根据您的需要,存在更好的解决方案)。 :)
这是2个数组的余弦相似度函数:
function cosineSimilarity($tokensA, $tokensB)
{
$a = $b = $c = 0;
$uniqueTokensA = $uniqueTokensB = array();
$uniqueMergedTokens = array_unique(array_merge($tokensA, $tokensB));
foreach ($tokensA as $token) $uniqueTokensA[$token] = 0;
foreach ($tokensB as $token) $uniqueTokensB[$token] = 0;
foreach ($uniqueMergedTokens as $token) {
$x = isset($uniqueTokensA[$token]) ? 1 : 0;
$y = isset($uniqueTokensB[$token]) ? 1 : 0;
$a += $x * $y;
$b += $x;
$c += $y;
}
return $b * $c != 0 ? $a / sqrt($b * $c) : 0;
}
速度很快(isset()
而不是in_array()
是大型数组的杀手锏。
正如您所看到的,结果没有考虑每个单词的“幅度”。
我用它来检测“差不多”复制粘贴文本的多张贴信息。它运作良好。 :)
有关字符串相似性指标的最佳链接: http://www.dcs.shef.ac.uk/~sam/stringmetrics.html
进一步有趣的读物:
http://www.miislita.com/information-retrieval-tutorial/cosine-similarity-tutorial.html http://bioinformatics.oxfordjournals.org/cgi/content/full/22/18/2298
答案 1 :(得分:9)
除非我弄错了,否则我认为你在两个算法之间有一个算法。对于汉明距离,请使用:
function check ($terms1, $terms2) {
$counts1 = array_count_values($terms1);
$totalScore = 0;
foreach ($terms2 as $term) {
if (isset($counts1[$term])) $totalScore += 1;
}
return $totalScore * 500 / (count($terms1) * count($terms2));
}
(请注意,您只为令牌向量中的每个匹配元素添加1。)
对于余弦相似性,请使用:
function check ($terms1, $terms2) {
$counts1 = array_count_values($terms1);
$counts2 = array_count_values($terms2);
$totalScore = 0;
foreach ($terms2 as $term) {
if (isset($counts1[$term])) $totalScore += $counts1[$term] * $counts2[$term];
}
return $totalScore / (count($terms1) * count($terms2));
}
(请注意,您要在两个文档之间添加令牌计数的产品。)
两者之间的主要区别在于,当两个文档在文档中多次具有相同的单词时,余弦相似性将产生更强的指示,而汉明距离并不关心多久个人代币出现。
编辑:刚刚注意到你有关删除功能词等的问题。如果你要使用余弦相似性,我建议你这样做 - 因为功能词非常频繁(至少在英语中),你可能会因为没有过滤它们而扭曲结果。如果你使用汉明距离,效果将不会那么大,但在某些情况下它仍然可以理解。此外,如果您有权访问lemmatizer,那么当一个文档包含“galaxies”而另一个文档包含“galaxy”时,它将减少未命中。
无论你走哪条路,祝你好运!
答案 2 :(得分:5)
我为忽略你说你不想使用任何其他算法这一事实而道歉,但严肃地说,Levenshtein distance和Damerau-Levenshtein distance比汉明距离更有用。这是一个D-L distance implementation in PHP,如果你不喜欢PHP的原生levenshtein()
函数,我认为你不会因为它有长度限制,这里是一个非长度限制的版本:
function levenshtein_distance($text1, $text2) {
$len1 = strlen($text1);
$len2 = strlen($text2);
for($i = 0; $i <= $len1; $i++)
$distance[$i][0] = $i;
for($j = 0; $j <= $len2; $j++)
$distance[0][$j] = $j;
for($i = 1; $i <= $len1; $i++)
for($j = 1; $j <= $len2; $j++)
$distance[$i][$j] = min($distance[$i - 1][$j] + 1, $distance[$i][$j - 1] + 1, $distance[$i - 1][$j - 1] + ($text1[$i - 1] != $text2[$j - 1]));
return $distance[$len1][$len2];
}
答案 3 :(得分:2)
这里是由Toto发布的余弦距离函数的更正代码
function cosineSimilarity($tokensA, $tokensB)
{
$a = $b = $c = 0;
$uniqueTokensA = $uniqueTokensB = array();
$uniqueMergedTokens = array_unique(array_merge($tokensA, $tokensB));
foreach ($tokensA as $token) $uniqueTokensA[$token] = 0;
foreach ($tokensB as $token) $uniqueTokensB[$token] = 0;
foreach ($uniqueMergedTokens as $token) {
$x = isset($uniqueTokensA[$token]) ? 1 : 0;
$y = isset($uniqueTokensB[$token]) ? 1 : 0;
$a += $x * $y;
$b += pow($x,2);
$c += pow($y,2);
}
return $b * $c != 0 ? $a / sqrt($b * $c) : 0;
}