Косинусное сходство против расстояния Хэмминга [закрыто]
чтобы вычислить сходство между двумя документами, я создаю вектор объектов, содержащий частоты терминов. Но затем, для следующего шага, я не могу решить между "Косинус сходство" и "Хэмминга".
мой вопрос: у вас есть опыт работы с этими алгоритмами? Какой из них дает лучшие результаты?
в дополнение к этому: не могли бы вы рассказать мне, как кодировать косинусное сходство в PHP? Для hamming distance у меня уже есть код:
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));
}
Я не хочу использовать любой другой алгоритм. Я только хотел бы, чтобы мне помогли выбрать между тем и другим.
и, возможно, кто-то может сказать что-то о том, как улучшить алгоритмы. Вы получите лучшие результаты, если отфильтровать стоп-слова или общие слова?
надеюсь, вы сможете мне помочь. Заранее спасибо!
4 ответов
расстояние Хэмминга должно быть сделано между двумя строками равной длины и с учетом порядка.
поскольку ваши документы, безусловно, разной длины, и если слова места не считаются, косинусное сходство лучше (обратите внимание, что в зависимости от ваших потребностей существуют лучшие решения). :)
вот косинусная функция подобия 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
Если я не ошибаюсь, Я думаю, что у вас есть алгоритм на полпути между двумя алгоритмами. Для расстояния Хэмминга используйте:
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));
}
(обратите внимание, что вы добавляете продукт подсчета токенов между двумя документами.)
основное различие между ними заключается в том, что Косинус сходство даст более сильный показатель, когда два документа имеют одно и то же слово несколько раз в документах, а расстояние Хэмминга не волнует, как часто появляются отдельные токены.
редактировать: просто заметил, что ваш запрос об удалении слов и т. д. Я советую это, если вы собираетесь использовать косинусное сходство - поскольку функциональные слова довольно часты (по крайней мере, на английском языке), вы можете исказить результат, не фильтруя их. Если вы используете Hamming расстояние, эффект будет не совсем так велик, но в некоторых случаях он все еще может быть заметен. Кроме того, если у вас есть доступ к лемматизатор, это уменьшит пропуски, когда один документ содержит "галактики", а другой содержит" галактику", например.
какой бы путь вы идете, удачи!
Я прошу прощения за то, что игнорирую тот факт, что вы сказали, что не хотите использовать какие-либо другие алгоритмы, но серьезно, расстояние Левенштейна и Дамерау-Левенштейна расстояние являются более чертовски полезными, чем расстояние Хэмминга. Вот реализация расстояния D-L в 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];
}
здесь мой исправленный код для функции косинусного расстояния, размещенной 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;
}