Я должен сравнить документы, хранившие в DB и придумывать счет подобия между 0 и 1.
Метод, который я должен использовать, должен быть очень простым. Реализация ванильной версии n-грамм (где это возможный определить, сколько граммов для использования), наряду с простой реализацией tf-idf и подобия Косинуса.
Есть ли какая-либо программа, которая может сделать это? Или я должен начать писать это с нуля?
Проверьте пакет NLTK: http://www.nltk.org в нем есть все, что вам нужно
Для cosine_similarity:
def cosine_distance(u, v):
"""
Returns the cosine of the angle between vectors v and u. This is equal to
u.v / |u||v|.
"""
return numpy.dot(u, v) / (math.sqrt(numpy.dot(u, u)) * math.sqrt(numpy.dot(v, v)))
Для ngrams:
def ngrams(sequence, n, pad_left=False, pad_right=False, pad_symbol=None):
"""
A utility that produces a sequence of ngrams from a sequence of items.
For example:
>>> ngrams([1,2,3,4,5], 3)
[(1, 2, 3), (2, 3, 4), (3, 4, 5)]
Use ingram for an iterator version of this function. Set pad_left
or pad_right to true in order to get additional ngrams:
>>> ngrams([1,2,3,4,5], 2, pad_right=True)
[(1, 2), (2, 3), (3, 4), (4, 5), (5, None)]
@param sequence: the source data to be converted into ngrams
@type sequence: C{sequence} or C{iterator}
@param n: the degree of the ngrams
@type n: C{int}
@param pad_left: whether the ngrams should be left-padded
@type pad_left: C{boolean}
@param pad_right: whether the ngrams should be right-padded
@type pad_right: C{boolean}
@param pad_symbol: the symbol to use for padding (default is None)
@type pad_symbol: C{any}
@return: The ngrams
@rtype: C{list} of C{tuple}s
"""
if pad_left:
sequence = chain((pad_symbol,) * (n-1), sequence)
if pad_right:
sequence = chain(sequence, (pad_symbol,) * (n-1))
sequence = list(sequence)
count = max(0, len(sequence) - n + 1)
return [tuple(sequence[i:i+n]) for i in range(count)]
для tf-idf вам придется сначала вычислить распределение, я использую Lucene для этого, но вы вполне можете сделать что-то подобное с NLTK, используйте FreqDist:
http://nltk. googlecode.com/svn/trunk/doc/book/ch01.html#frequency_distribution_index_term
если вам нравится pylucene, это расскажет вам, как вычислить tf.idf
# reader = lucene.IndexReader(FSDirectory.open(index_loc))
docs = reader.numDocs()
for i in xrange(docs):
tfv = reader.getTermFreqVector(i, fieldname)
if tfv:
rec = {}
terms = tfv.getTerms()
frequencies = tfv.getTermFrequencies()
for (t,f,x) in zip(terms,frequencies,xrange(maxtokensperdoc)):
df= searcher.docFreq(Term(fieldname, t)) # number of docs with the given term
tmap.setdefault(t, len(tmap))
rec[t] = sim.tf(f) * sim.idf(df, max_doc) #compute TF.IDF
# and normalize the values using cosine normalization
if cosine_normalization:
denom = sum([x**2 for x in rec.values()])**0.5
for k,v in rec.items():
rec[k] = v / denom
В нашем курсе поиска информации мы используем код, написанный нашим профессором на языке Java. Извините, нет порта python. «Он выпущен для образовательных и исследовательских целей только под Стандартной общественной лицензией GNU».
Вы можете проверить документацию http://userweb.cs.utexas.edu/~mooney/ir-course/doc/
Но более конкретно: http://userweb.cs.utexas.edu/users/mooney/ir-course/doc/ir/vsr/HashMapVector.html
Вы можете скачать его http://userweb.cs.utexas.edu / users / mooney / ir-course /
Если вас все еще интересует эта проблема, я сделал нечто очень похожее, используя Lucene Java и Jython. Вот несколько фрагментов из моего кода.
Lucene предварительно обрабатывает документы и запросы с помощью так называемых анализаторов. Этот использует встроенный в Lucene фильтр n-грамм:
class NGramAnalyzer(Analyzer):
'''Analyzer that yields n-grams for minlength <= n <= maxlength'''
def __init__(self, minlength, maxlength):
self.minlength = minlength
self.maxlength = maxlength
def tokenStream(self, field, reader):
lower = ASCIIFoldingFilter(LowerCaseTokenizer(reader))
return NGramTokenFilter(lower, self.minlength, self.maxlength)
Для превращения списка ngrams
в документ
:
doc = Document()
doc.add(Field('n-grams', ' '.join(ngrams),
Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.YES))
Для хранения документа в индексе:
wr = IndexWriter(index_dir, NGramAnalyzer(), True,
IndexWriter.MaxFieldLength.LIMITED)
wr.addDocument(doc)
Создание запросов немного сложнее, поскольку QueryParser
Lucene ожидает язык запроса со специальными операторами, кавычками и т.д., но это можно обойти (как частично объяснено здесь).