Анализ Python - Sentiment с использованием Pointwise Mutual Information

from __future__ import division
import urllib
import json
from math import log


def hits(word1,word2=""):
    query = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=%s"
    if word2 == "":
        results = urllib.urlopen(query % word1)
    else:
        results = urllib.urlopen(query % word1+" "+"AROUND(10)"+" "+word2)
    json_res = json.loads(results.read())
    google_hits=int(json_res['responseData']['cursor']['estimatedResultCount'])
    return google_hits


def so(phrase):
    num = hits(phrase,"excellent")
    #print num
    den = hits(phrase,"poor")
    #print den
    ratio = num / den
    #print ratio
    sop = log(ratio)
    return sop

print so("ugly product")

Мне нужен этот код для вычисления Point Wise Mutual Information, который может использоваться для классификации отзывов как положительных или отрицательных. В основном я использую технику, указанную Turney (2002): http://acl.ldc.upenn.edu/P/P02/P02-1053.pdf в качестве примера для неконтролируемого метода классификации для анализа настроений.

Как объясняется в статье, семантическая ориентация фразы отрицательна, если фраза более тесно связана со словом "бедный" и положительным, если она более сильно связана со словом "отлично".

В приведенном выше коде вычисляется SO фразы. Я использую Google для вычисления количества обращений и вычисления SO. (Поскольку AltaVista теперь не существует)

Вычисленные значения очень неустойчивы. Они не придерживаются определенного шаблона. Например, SO ( "уродливый продукт" ) получается 2,85462098541, тогда как SO ( "прекрасный продукт" ) - 1,71395061117. В то время как первый, как ожидается, будет отрицательным, а другой положительным.

Что-то не так с кодом? Есть ли более простой способ вычисления SO фразы (с использованием PMI) с любой библиотекой Python, скажем, NLTK? Я попробовал NLTK, но не смог найти какой-либо явный метод, который вычисляет PMI.

Ответ 1

Как правило, вычисление PMI является сложным, так как формула будет меняться в зависимости от размера nграммы, которую вы хотите принять во внимание:

Математически для битрамов вы можете просто рассмотреть:

log(p(a,b) / ( p(a) * p(b) ))

Программно, скажем, вы рассчитали все частоты униграмм и биграмм в вашем корпусе, вы делаете это:

def pmi(word1, word2, unigram_freq, bigram_freq):
  prob_word1 = unigram_freq[word1] / float(sum(unigram_freq.values()))
  prob_word2 = unigram_freq[word2] / float(sum(unigram_freq.values()))
  prob_word1_word2 = bigram_freq[" ".join([word1, word2])] / float(sum(bigram_freq.values()))
  return math.log(prob_word1_word2/float(prob_word1*prob_word2),2) 

Это фрагмент кода из библиотеки MWE, но он находится на стадии предварительной разработки (https://github.com/alvations/Terminator/blob/master/mwe.py). Но обратите внимание, что это для параллельного извлечения MWE, так вот как вы можете "взломать" его для извлечения одноязычного MWE:

$ wget https://dl.dropboxusercontent.com/u/45771499/mwe.py
$ printf "This is a foo bar sentence .\nI need multi-word expression from this text file.\nThe text file is messed up , I know you foo bar multi-word expression thingy .\n More foo bar is needed , so that the text file is populated with some sort of foo bar bigrams to extract the multi-word expression ." > src.txt
$ printf "" > trg.txt
$ python
>>> import codecs
>>> from mwe import load_ngramfreq, extract_mwe

>>> # Calculates the unigrams and bigrams counts.
>>> # More superfluously, "Training a bigram 'language model'."
>>> unigram, bigram, _ , _ = load_ngramfreq('src.txt','trg.txt')

>>> sent = "This is another foo bar sentence not in the training corpus ."

>>> for threshold in range(-2, 4):
...     print threshold, [mwe for mwe in extract_mwe(sent.strip().lower(), unigram, bigram, threshold)]

[выход]:

-2 ['this is', 'is another', 'another foo', 'foo bar', 'bar sentence', 'sentence not', 'not in', 'in the', 'the training', 'training corpus', 'corpus .']
-1 ['this is', 'is another', 'another foo', 'foo bar', 'bar sentence', 'sentence not', 'not in', 'in the', 'the training', 'training corpus', 'corpus .']
0 ['this is', 'foo bar', 'bar sentence']
1 ['this is', 'foo bar', 'bar sentence']
2 ['this is', 'foo bar', 'bar sentence']
3 ['foo bar', 'bar sentence']
4 []

Для получения дополнительной информации я нахожу этот тезис быстрым и легким введением в извлечение MWE: "Расширение меридианности журнала для улучшения идентификации коллаборации", см. http://goo.gl/5ebTJJ

Ответ 2

Библиотека Python DISSECT содержит несколько методов для вычисления Pointwise Взаимная информация о матрицах совпадения.

Пример:

#ex03.py
#-------
from composes.utils import io_utils
from composes.transformation.scaling.ppmi_weighting import PpmiWeighting

#create a space from co-occurrence counts in sparse format
my_space = io_utils.load("./data/out/ex01.pkl")

#print the co-occurrence matrix of the space
print my_space.cooccurrence_matrix

#apply ppmi weighting
my_space = my_space.apply(PpmiWeighting())

#print the co-occurrence matrix of the transformed space
print my_space.cooccurrence_matrix

Код в GitHub для методов PMI.

Справка: Джорджиана Дину, Нгия Фам и Марко Барони. 2013. DISSECT: DIStributional SEmantics Composition Инструментарий. В материалах системных демонстраций от ACL 2013, София, Болгария

Связанный: Вычисление потоковой взаимной информации между двумя строками

Ответ 3

Чтобы ответить, почему ваши результаты ошибочны, важно знать, что поиск Google не является надежным источником частот слов. Частоты, возвращаемые механизмом, являются простыми оценками, которые являются особенно неточными и, возможно, противоречивыми при запросе нескольких слов. Это не является ударом по Google, но это не утилита для подсчета частоты. Следовательно, ваша реализация может быть в порядке, но результаты на этом основании могут быть бессмысленными.

Адама Килгаррифа читайте в статье " Гуглология - плохая наука ".