Подтвердить что ты не робот

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

Я следил за текстом здесь и код здесь (он реализуется с использованием симметричной 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.

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

Update:

Чтобы сделать как 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
4b9b3361

Ответ 1

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

  • sum (ps) == 1, поскольку for-loop суммирует все слова в _s.
  • sum (pt) <= 1, где равенство будет выполняться, если носитель t является подмножеством носителя s (т.е. если все слова в _t появляются в _s). Кроме того, sum (pt) может быть близким к 0, если совпадение слов в _s и _t невелико. В частности, если пересечение _s и _t является пустым множеством, тогда sum (pt) == epsilon * len (_s).

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

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

Ответ 2

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