Какие проверенные и действительные алгоритмы для предложения связанных статей существуют? - PullRequest
23 голосов
/ 10 августа 2009

Довольно распространенная ситуация, я бы поспорил. У вас есть блог или новостной сайт, и у вас есть множество статей или блогов, или как вы их называете, и вы хотите, в нижней части каждого, предложить другие, которые кажутся связанными.

Давайте предположим, что очень мало метаданных о каждом элементе. То есть без тегов, категорий. Рассматривайте как один большой фрагмент текста, включая заголовок и имя автора.

Как вы находите, возможно, связанные документы?

Я скорее заинтересован в реальном алгоритме, а не в готовых решениях, хотя я согласился бы взглянуть на что-то, реализованное в ruby ​​или python, или полагаться на mysql или pgsql.

edit: текущий ответ довольно хорош, но я бы хотел увидеть больше. Может быть, какой-то действительно чистый пример кода для вещи или двух.

Ответы [ 5 ]

38 голосов
/ 10 августа 2009

Это довольно большая тема - в дополнение к ответам, которые приходят здесь, я рекомендую отследить учебные планы для пары классов по поиску информации и проверить назначенные для них учебники и статьи. Тем не менее, вот краткий обзор моих дней выпускной школы:

Самый простой подход называется мешок слов . Каждый документ сводится к разреженному вектору из {word: wordcount} пар, и вы можете добавить классификатор NaiveBayes (или некоторый другой) в набор векторов, представляющий ваш набор документов, или вычислить оценки сходства между каждой сумкой и каждой другой сумкой ( это называется классификацией k-ближайших соседей). KNN быстр для поиска, но требует O (n ^ 2) памяти для матрицы оценок; однако для блога n не очень велико. Для чего-то размером с большую газету KNN быстро становится непрактичным, поэтому алгоритм классификации на лету иногда лучше. В этом случае вы можете рассмотреть механизм опорных векторов ранжирования . SVM аккуратны, потому что они не ограничивают вас линейными мерами сходства, и все еще довольно быстры.

Стебминг - это обычный шаг предварительной обработки для техник мешка слов; это включает сокращение морфологически родственных слов, таких как «кошка» и «кошка», «боб» и «бобс», или «похожий» и «подобный», до их корней перед вычислением пакета слов. Существует множество различных алгоритмов стемминга; на странице Википедии есть ссылки на несколько реализаций.

Если сходство с набором слов недостаточно, вы можете абстрагировать его до уровня сходства с пакетом из N-граммов, где вы создадите вектор, представляющий документ, основанный на парах или тройках слов. (Вы можете использовать 4-х или даже более крупные кортежи, но на практике это не очень помогает.) Это имеет недостаток, заключающийся в создании гораздо больших векторов, и классификация, соответственно, потребует больше работы, но совпадения, которые вы получите, будут намного ближе синтаксически. OTOH, вам, вероятно, не нужно это для семантического сходства; это лучше для таких вещей, как обнаружение плагиата. Разделение на части , или сокращение документа до облегченных деревьев разбора, также может быть использовано (есть алгоритмы классификации для деревьев), но это более полезно для таких вещей, как проблема авторства ("с учетом документа неизвестного происхождения") кто это написал? ").

Возможно, более полезным для вашего случая использования является интеллектуальный анализ понятий, который включает сопоставление слов с понятиями (с использованием тезауруса, такого как WordNet ), а затем классификацию документов на основе сходства между используемыми понятиями. Это часто оказывается более эффективным, чем классификация по сходству на основе слов, поскольку сопоставление слов с понятиями является редуцирующим, но этап предварительной обработки может быть довольно трудоемким.

Наконец, есть разбор дискурса , который включает в себя анализ документов на предмет их семантической структуры; вы можете запускать классификаторы подобия на деревьях рассуждений так же, как и на кусочных документах.

В значительной степени все они включают генерацию метаданных из неструктурированного текста; прямое сравнение между необработанными блоками текста трудно, поэтому люди сначала обрабатывают документы в метаданные.

4 голосов
/ 11 декабря 2009

Вам следует прочитать книгу «Программирование коллективного интеллекта: создание приложений Smart Web 2.0» (ISBN 0596529325)!

Для некоторого метода и кода: сначала спросите себя, хотите ли вы найти прямое сходство на основе совпадений слов или хотите показать похожие статьи, которые могут не иметь прямого отношения к текущей, но принадлежать к той же группе статьи.

См. Кластерный анализ / Секционированная кластеризация .

Очень простой (но теоретический и медленный) метод для нахождения прямого сходства:

Preprocess:

  1. Хранить плоский список слов для каждой статьи (не удаляйте повторяющиеся слова).
  2. «Перекрестное соединение» статей: подсчитайте количество слов в статье A, которые совпадают с теми же словами в статье B. Теперь у вас есть матрица int word_matches[narticles][narticles] (вы не должны хранить ее таким образом, сходство A-> B одинаково так как B-> A, то разреженная матрица экономит почти половину пространства).
  3. Нормализовать счетчик слов в диапазоне 0..1! (найдите максимальное количество, а затем разделите любое количество на это) - вы должны хранить числа с плавающей точкой, а не целые;)

Найти похожие статьи:

  1. выберите X статей с наибольшим соответствием из word_matches
4 голосов
/ 06 декабря 2009

Это типичный случай Классификация документов , который изучается в каждом классе машинного обучения. Если вы любите статистику, математику и информатику, я рекомендую взглянуть на неконтролируемые методы, такие как kmeans ++ , байесовские методы и LDA . В частности, байесовские методы довольно хороши в том, что вы ищете, их единственная проблема - медленный (но если вы не используете очень большой сайт, это не должно вас сильно беспокоить).

В более практичном и менее теоретическом подходе я рекомендую вам посмотреть это и это другие отличные примеры кода.

3 голосов
/ 06 декабря 2009

Небольшая поисковая система векторно-пространственных моделей в Ruby. Основная идея заключается в том, что два документа связаны между собой, если они содержат одинаковые слова. Таким образом, мы подсчитываем вхождение слов в каждом документе и затем вычисляем косинус между этими векторами (каждый член имеет фиксированный индекс, если кажется, что в этом индексе есть 1, если не ноль). Косинус будет равен 1,0, если в двух документах есть все общие термины, и 0,0, если у них нет общих терминов. Вы можете напрямую перевести это в% значения.

terms = Hash.new{|h,k|h[k]=h.size}
docs = DATA.collect { |line| 
  name = line.match(/^\d+/)
  words = line.downcase.scan(/[a-z]+/)
  vector = [] 
  words.each { |word| vector[terms[word]] = 1 }
  {:name=>name,:vector=>vector}
}
current = docs.first # or any other
docs.sort_by { |doc| 
  # assume we have defined cosine on arrays
  doc[:vector].cosine(current[:vector]) 
}
related = docs[1..5].collect{|doc|doc[:name]}

puts related

__END__
0 Human machine interface for Lab ABC computer applications
1 A survey of user opinion of computer system response time
2 The EPS user interface management system
3 System and human system engineering testing of EPS
4 Relation of user-perceived response time to error measurement
5 The generation of random, binary, unordered trees
6 The intersection graph of paths in trees
7 Graph minors IV: Widths of trees and well-quasi-ordering
8 Graph minors: A survey

определение Array#cosine оставлено читателю как упражнение (должно иметь дело с нулевыми значениями и различной длиной, но хорошо для этого мы получили Array#zip верно?)

Кстати, документы для примера взяты из бумаги SVD Deerwester etal:)

1 голос
/ 06 декабря 2009

Некоторое время назад я реализовал что-то похожее. Возможно, эта идея устарела, но я надеюсь, что она поможет.

Я запустил веб-сайт ASP 3.0 для программирования общих задач и исходил из этого принципа: у пользователя есть сомнения и он останется на сайте, пока он / она сможет найти интересный контент на эту тему.

Когда пользователь прибыл, я запустил объект ASP 3.0 Session и записал всю пользовательскую навигацию, как связанный список. На событии Session.OnEnd я беру первую ссылку, ищу следующую ссылку и увеличиваю столбец счетчика, например:

<Article Title="Cookie problem A">
    <NextPage Title="Cookie problem B" Count="5" />
    <NextPage Title="Cookie problem C" Count="2" />
</Article>

Итак, для проверки связанных статей мне просто нужно было перечислить топ n NextPage сущностей, упорядоченных по убыванию столбца счетчика.

...