2010-03-04 9 views
45

में कोसाइन समानता की सरल कार्यान्वयन मैं एक DB में संग्रहीत दस्तावेज़ों की तुलना और 0 और 1.एन-ग्राम, tf-आईडीएफ और अजगर

विधि मैं उपयोग करने के लिए है की जरूरत है जो समानता स्कोर के साथ आने की जरूरत है बहुत आसान होना टी-आईडीएफ और कोसाइन समानता के सरल कार्यान्वयन के साथ, एन-ग्राम्स के एक वेनिला संस्करण को कार्यान्वित करना (जहां यह निर्धारित करना संभव है कि कितने ग्राम का उपयोग करना है)।

क्या कोई ऐसा प्रोग्राम है जो यह कर सकता है? या मुझे इसे स्क्रैच से लिखना शुरू करना चाहिए?

उत्तर

45

चेक आउट: 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-आईडीएफ के लिए आप पहली बार वितरण की गणना करने के, मैं Lucene का उपयोग कर रहा है कि क्या करना होगा, लेकिन आप बहुत अच्छी तरह से NLTK साथ कुछ इसी तरह कर सकते हैं, FreqDist का उपयोग करें:

http://nltk.googlecode.com/svn/trunk/doc/book/ch01.html#frequency_distribution_index_term

अगर आप pylucene पसंद है, यह आपको बता कैसे होगा tf.idf comute को

# 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 
+1

,) sqrt (प्रदर्शन करने के लिए दो बार sqrt के बाद से कोई ज़रूरत नहीं (क) * sqrt (बी) = sqrt (ए * बी)। –

3

हमारे सूचना पुनर्प्राप्ति कोर्स के लिए, हम कुछ कोड है कि जावा में हमारे प्रोफेसर ने लिखा है का उपयोग करें। क्षमा करें, कोई पायथन बंदरगाह नहीं है। "यह केवल जीएनयू जनरल पब्लिक लाइसेंस के तहत शैक्षणिक और शोध उद्देश्यों के लिए जारी किया जा रहा है।"

आप प्रलेखन http://userweb.cs.utexas.edu/~mooney/ir-course/doc/

की जांच कर सकते लेकिन अधिक विशेष रूप से बाहर की जाँच: http://userweb.cs.utexas.edu/users/mooney/ir-course/doc/ir/vsr/HashMapVector.html

आप NLTK पैकेज डाउनलोड कर सकते हैं यह http://userweb.cs.utexas.edu/users/mooney/ir-course/

3

यदि आप अभी भी इस समस्या में रुचि रखते हैं, तो मैंने Lucene Java और ज्योथन का उपयोग करके बहुत कुछ किया है। मेरे कोड से कुछ स्निपेट यहां दिए गए हैं।

Lucene दस्तावेजों और प्रश्नों विश्लेषक तथाकथित का उपयोग कर preprocesses। यह एक का उपयोग करता है Lucene के अंतर्निहित एन-ग्राम फिल्टर:

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) 

एक Document में ngrams की एक सूची बदलने के लिए:

wr = IndexWriter(index_dir, NGramAnalyzer(), True, 
       IndexWriter.MaxFieldLength.LIMITED) 
wr.addDocument(doc) 
:

doc = Document() 
doc.add(Field('n-grams', ' '.join(ngrams), 
     Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.YES)) 

एक सूचकांक में कोई दस्तावेज़ संग्रहीत करने के लिए

बिल्डिंग प्रश्न थोड़ा और मुश्किल है क्योंकि लुसेन के QueryParser विशेष ऑपरेटरों, उद्धरण इत्यादि के साथ एक क्वेरी भाषा की अपेक्षा करता है, लेकिन इसे घुमाया जा सकता है (आंशिक रूप से here समझाया गया)।

19

आप रुचि रखते हैं, मैं ट्यूटोरियल श्रृंखला (Part I और Part II) tf-आईडीएफ के बारे में बात और Scikits.learn (sklearn) पायथन मॉड्यूल का उपयोग किया गया है।

Part 3 में कोसाइन समानता है। एक

def ngrams(sentence, n): 
    return zip(*[sentence.split()[i:] for i in range(n)]) 

TF-आईडीएफ (यह है:

6

यहाँ python + numpy साथ एक जवाब है, संक्षेप में:

कोसाइन:

def cosine_sim(u,v): 
    return np.dot(u,v)/(sqrt(np.dot(u,u)) * sqrt(np.dot(v,v))) 

Ngrams थोड़ा अजीब लेकिन यह काम करता है):

def tfidf(corpus, vocab): 
    """ 
    INPUT: 

    corpus = [('this is a foo bar', [1, 1, 0, 1, 1, 0, 0, 1]), 
    ('foo bar bar black sheep', [0, 2, 1, 1, 0, 0, 1, 0]), 
    ('this is a sentence', [1, 0, 0, 0, 1, 1, 0, 1])] 

    vocab = ['a', 'bar', 'black', 'foo', 'is', 'sentence', 
    'sheep', 'this'] 

    OUTPUT: 

    [[0.300, 0.300, 0.0, 0.300, 0.300, 0.0, 0.0, 0.300], 
    [0.0, 0.600, 0.600, 0.300, 0.0, 0.0, 0.600, 0.0], 
    [0.375, 0.0, 0.0, 0.0, 0.375, 0.75, 0.0, 0.375]] 

    """ 
    def termfreq(matrix, doc, term): 
     try: return matrix[doc][term]/float(sum(matrix[doc].values())) 
     except ZeroDivisionError: return 0 
    def inversedocfreq(matrix, term): 
     try: 
      return float(len(matrix)) /sum([1 for i,_ in enumerate(matrix) if matrix[i][term] > 0]) 
     except ZeroDivisionError: return 0 

    matrix = [{k:v for k,v in zip(vocab, i[1])} for i in corpus] 
    tfidf = defaultdict(dict) 
    for doc,_ in enumerate(matrix): 
     for term in matrix[doc]: 
      tf = termfreq(matrix,doc,term) 
      idf = inversedocfreq(matrix, term) 
      tfidf[doc][term] = tf*idf 

    return [[tfidf[doc][term] for term in vocab] for doc,_ in enumerate(tfidf)] 

यहाँ परीक्षण के साथ लंबे समय जवाब है:

import numpy as np 
from math import sqrt, log 
from itertools import chain, product 
from collections import defaultdict 

def cosine_sim(u,v): 
    return np.dot(u,v)/(sqrt(np.dot(u,u)) * sqrt(np.dot(v,v))) 

def ngrams(sentence, n): 
    return zip(*[sentence.split()[i:] for i in range(n)]) 

def tfidf(corpus, vocab): 
    """ 
    INPUT: 

    corpus = [('this is a foo bar', [1, 1, 0, 1, 1, 0, 0, 1]), 
    ('foo bar bar black sheep', [0, 2, 1, 1, 0, 0, 1, 0]), 
    ('this is a sentence', [1, 0, 0, 0, 1, 1, 0, 1])] 

    vocab = ['a', 'bar', 'black', 'foo', 'is', 'sentence', 
    'sheep', 'this'] 

    OUTPUT: 

    [[0.300, 0.300, 0.0, 0.300, 0.300, 0.0, 0.0, 0.300], 
    [0.0, 0.600, 0.600, 0.300, 0.0, 0.0, 0.600, 0.0], 
    [0.375, 0.0, 0.0, 0.0, 0.375, 0.75, 0.0, 0.375]] 

    """ 
    def termfreq(matrix, doc, term): 
     try: return matrix[doc][term]/float(sum(matrix[doc].values())) 
     except ZeroDivisionError: return 0 
    def inversedocfreq(matrix, term): 
     try: 
      return float(len(matrix)) /sum([1 for i,_ in enumerate(matrix) if matrix[i][term] > 0]) 
     except ZeroDivisionError: return 0 

    matrix = [{k:v for k,v in zip(vocab, i[1])} for i in corpus] 
    tfidf = defaultdict(dict) 
    for doc,_ in enumerate(matrix): 
     for term in matrix[doc]: 
      tf = termfreq(matrix,doc,term) 
      idf = inversedocfreq(matrix, term) 
      tfidf[doc][term] = tf*idf 

    return [[tfidf[doc][term] for term in vocab] for doc,_ in enumerate(tfidf)] 


def corpus2vectors(corpus): 
    def vectorize(sentence, vocab): 
     return [sentence.split().count(i) for i in vocab] 
    vectorized_corpus = [] 
    vocab = sorted(set(chain(*[i.lower().split() for i in corpus]))) 
    for i in corpus: 
     vectorized_corpus.append((i, vectorize(i, vocab))) 
    return vectorized_corpus, vocab 

def create_test_corpus(): 
    sent1 = "this is a foo bar" 
    sent2 = "foo bar bar black sheep" 
    sent3 = "this is a sentence" 

    all_sents = [sent1,sent2,sent3] 
    corpus, vocab = corpus2vectors(all_sents) 
    return corpus, vocab 

def test_cosine(): 
    corpus, vocab = create_test_corpus() 

    for sentx, senty in product(corpus, corpus): 
     print sentx[0] 
     print senty[0] 
     print "cosine =", cosine_sim(sentx[1], senty[1]) 
     print 

def test_ngrams(): 
    corpus, vocab = create_test_corpus() 
    for sentx in corpus: 
     print sentx[0] 
     print ngrams(sentx[0],2) 
     print ngrams(sentx[0],3) 
     print 

def test_tfidf(): 
    corpus, vocab = create_test_corpus() 
    print corpus 
    print vocab 
    print tfidf(corpus, vocab) 

print "Testing cosine..." 
test_cosine() 
print 
print "Testing ngrams..." 
test_ngrams() 
print 
print "Testing tfidf..." 
test_tfidf() 
print 

[बाहर]:

Testing cosine... 
this is a foo bar 
this is a foo bar 
cosine = 1.0 

this is a foo bar 
foo bar bar black sheep 
cosine = 0.507092552837 

this is a foo bar 
this is a sentence 
cosine = 0.67082039325 

foo bar bar black sheep 
this is a foo bar 
cosine = 0.507092552837 

foo bar bar black sheep 
foo bar bar black sheep 
cosine = 1.0 

foo bar bar black sheep 
this is a sentence 
cosine = 0.0 

this is a sentence 
this is a foo bar 
cosine = 0.67082039325 

this is a sentence 
foo bar bar black sheep 
cosine = 0.0 

this is a sentence 
this is a sentence 
cosine = 1.0 


Testing ngrams... 
this is a foo bar 
[('this', 'is'), ('is', 'a'), ('a', 'foo'), ('foo', 'bar')] 
[('this', 'is', 'a'), ('is', 'a', 'foo'), ('a', 'foo', 'bar')] 

foo bar bar black sheep 
[('foo', 'bar'), ('bar', 'bar'), ('bar', 'black'), ('black', 'sheep')] 
[('foo', 'bar', 'bar'), ('bar', 'bar', 'black'), ('bar', 'black', 'sheep')] 

this is a sentence 
[('this', 'is'), ('is', 'a'), ('a', 'sentence')] 
[('this', 'is', 'a'), ('is', 'a', 'sentence')] 


Testing tfidf... 
[('this is a foo bar', [1, 1, 0, 1, 1, 0, 0, 1]), ('foo bar bar black sheep', [0, 2, 1, 1, 0, 0, 1, 0]), ('this is a sentence', [1, 0, 0, 0, 1, 1, 0, 1])] 
['a', 'bar', 'black', 'foo', 'is', 'sentence', 'sheep', 'this'] 
[[0.30000000000000004, 0.30000000000000004, 0.0, 0.30000000000000004, 0.30000000000000004, 0.0, 0.0, 0.30000000000000004], [0.0, 0.6000000000000001, 0.6000000000000001, 0.30000000000000004, 0.0, 0.0, 0.6000000000000001, 0.0], [0.375, 0.0, 0.0, 0.0, 0.375, 0.75, 0.0, 0.375]]