Вычисления симметрических Кульбака-Лейблера расхождение между двумя документами

Я следил за бумагой здесь и код здесь (реализовано с использованием симметричной kld и обратной модели, предложенной в статье в 1-й ссылке) для вычисления KLD между двумя наборами текстовых данных. Я изменил for-loop в конце, чтобы вернуть распределение вероятностей двух наборов данных для проверки, если обе суммы равны 1:

import re, math, collections

def tokenize(_str):
    stopwords = ['and', 'for', 'if', 'the', 'then', 'be', 'is', 
                 'are', 'will', 'in', 'it', 'to', 'that']
    tokens = collections.defaultdict(lambda: 0.)
    for m in re.finditer(r"(w+)", _str, re.UNICODE):
        m = m.group(1).lower()
        if len(m) < 2: continue
        if m in stopwords: continue
        tokens[m] += 1

    return tokens
#end of tokenize

def kldiv(_s, _t):
    if (len(_s) == 0):
        return 1e33

    if (len(_t) == 0):
        return 1e33

    ssum = 0. + sum(_s.values())
    slen = len(_s)

    tsum = 0. + sum(_t.values())
    tlen = len(_t)

    vocabdiff = set(_s.keys()).difference(set(_t.keys()))
    lenvocabdiff = len(vocabdiff)

    """ epsilon """
    epsilon = min(min(_s.values())/ssum, min(_t.values())/tsum) * 0.001

    """ gamma """
    gamma = 1 - lenvocabdiff * epsilon

    """ Check if distribution probabilities sum to 1"""
    sc = sum([v/ssum for v in _s.itervalues()])
    st = sum([v/tsum for v in _t.itervalues()])

    ps=[] 
    pt = [] 
    for t, v in _s.iteritems(): 
        pts = v / ssum 
        ptt = epsilon 
        if t in _t: 
            ptt = gamma * (_t[t] / tsum) 
        ps.append(pts) 
        pt.append(ptt)
    return ps, pt

я протестировал с

d1 = """Many research publications want you to use BibTeX, which better organizes the whole process. Suppose for concreteness your source file is x.tex. Basically, you create a file x.bib containing the bibliography, and run bibtex on that file.""" d2 = """In this case you must supply both a left and a right because the delimiter height are made to match whatever is contained between the two commands. But, the left doesn't have to be an actual 'left delimiter', that is you can use 'left)' if there were some reason to do it."""

sum(ps) = 1 но sum(pt) намного меньше чем 1 когда:

This should be the case.

есть ли что-то, что неправильно в коде или иначе? Спасибо!

обновление:

чтобы сделать сумму pt и ps равной 1, мне пришлось изменить код на:

    vocab = Counter(_s)+Counter(_t)
    ps=[] 
    pt = [] 
    for t, v in vocab.iteritems(): 
        if t in _s:
            pts = gamma * (_s[t] / ssum) 
        else: 
            pts = epsilon

        if t in _t: 
            ptt = gamma * (_t[t] / tsum) 
        else:
            ptt = epsilon

        ps.append(pts) 
        pt.append(ptt)

    return ps, pt

2 ответов


как сумма (ps), так и сумма(pt) являются общей массой вероятности _s и _t над поддержкой s (под" поддержкой s " я подразумеваю все слова, которые появляются в _s, независимо от слов, которые появляются в _t). Это значит, что

  1. sum (ps)==1, так как for-loop суммирует все слова в _s.
  2. sum (pt)

Так, я не думаю, что есть проблема с кодом.

кроме того, вопреки названию вопроса, kldiv () вычисляет не симметричную KL-дивергенцию, а KL-дивергенцию между _s и сглаженной версией _t.


сумма ваших вероятностных распределений для каждого документа хранится в переменных sc и st, они близки к 1.