2015-11-06 5 views
15

Я преподал несколько вводных классов для интеллектуального анализа текста с помощью Python, и класс попробовал аналогичный метод с предоставленными практическими текстами. Некоторые студенты получили разные результаты для text1.similar(), чем другие.Аналогичный метод из модуля nltk дает разные результаты на разных машинах. Зачем?

Все версии и т. Д. Были одинаковыми.

Кто-нибудь знает, почему произошли эти различия? Благодарю.

Код, используемый в командной строке.

python 
>>> import nltk 
>>> nltk.download() #here you use the pop-up window to download texts 
>>> from nltk.book import * 
*** Introductory Examples for the NLTK Book *** 
Loading text1, ..., text9 and sent1, ..., sent9 
Type the name of the text or sentence to view it. 
Type: 'texts()' or 'sents()' to list the materials. 
text1: Moby Dick by Herman Melville 1851 
text2: Sense and Sensibility by Jane Austen 1811 
text3: The Book of Genesis 
text4: Inaugural Address Corpus 
text5: Chat Corpus 
text6: Monty Python and the Holy Grail 
text7: Wall Street Journal 
text8: Personals Corpus 
text9: The Man Who Was Thursday by G . K . Chesterton 1908 
>>>>>> text1.similar("monstrous") 
mean part maddens doleful gamesome subtly uncommon careful untoward 
exasperate loving passing mouldy christian few true mystifying 
imperial modifies contemptible 
>>> text2.similar("monstrous") 
very heartily so exceedingly remarkably as vast a great amazingly 
extremely good sweet 

Эти списки терминов, возвращаемых подобным способом отличаются от пользователя к пользователю, у них есть много слов, в общем, но они не являются идентичными списками. Все пользователи использовали одну и ту же ОС и те же версии python и nltk.

Надеюсь, что вопрос станет понятным. Благодарю.

+0

Можете ли вы предоставить текст ввода и фрагмент кода, который вы использовали? Затем мы можем попытаться пропустить код и посмотреть, как объяснить разницу. – alvas

+0

Я просто выполнил инструкции, которые являются частью этой страницы книги NLTK. http://www.nltk.org/book/ch01.html –

+0

Что такое битрейт машины, которую вы используете для фрагмента кода в вопросе? http://stackoverflow.com/questions/9964396/python-check-if-a-system-is-32-or-64-bit-to-determine-whether-to-run-the-funct, каков ваш результат для 'python -c" import struct; print struct.calcsize ('P') * 8 "' – alvas

ответ

16

В вашем примере есть 40 других слов, которые имеют ровно один контекст общего со словом 'monstrous'. В функции similar объект Counter используется для подсчета слов с похожими контекстами, а затем печатаются наиболее распространенные (по умолчанию 20). Поскольку все 40 имеют одинаковую частоту, порядок может отличаться.

Из doc из Counter.most_common:

элементов с одинаковыми подсчетов упорядочены произвольно


Я проверил частоту подобных слов с этим кодом (который, по существу, копия соответствующая часть функционального кода):

from nltk.book import * 
from nltk.util import tokenwrap 
from nltk.compat import Counter 

word = 'monstrous' 
num = 20 

text1.similar(word) 

wci = text1._word_context_index._word_to_contexts 

if word in wci.conditions(): 
      contexts = set(wci[word]) 
      fd = Counter(w for w in wci.conditions() for c in wci[w] 
          if c in contexts and not w == word) 
      words = [w for w, _ in fd.most_common(num)] 
      # print(tokenwrap(words)) 

print(fd) 
print(len(fd)) 
print(fd.most_common(num)) 

Выход: (различные пробеги дают различные результаты для меня)

Counter({'doleful': 1, 'curious': 1, 'delightfully': 1, 'careful': 1, 'uncommon': 1, 'mean': 1, 'perilous': 1, 'fearless': 1, 'imperial': 1, 'christian': 1, 'trustworthy': 1, 'untoward': 1, 'maddens': 1, 'true': 1, 'contemptible': 1, 'subtly': 1, 'wise': 1, 'lamentable': 1, 'tyrannical': 1, 'puzzled': 1, 'vexatious': 1, 'part': 1, 'gamesome': 1, 'determined': 1, 'reliable': 1, 'lazy': 1, 'passing': 1, 'modifies': 1, 'few': 1, 'horrible': 1, 'candid': 1, 'exasperate': 1, 'pitiable': 1, 'abundant': 1, 'mystifying': 1, 'mouldy': 1, 'loving': 1, 'domineering': 1, 'impalpable': 1, 'singular': 1}) 
6

Короче:

Это что-то делать с тем, как python3 хэши клавиши, когда функция similar() использует словарь счетчика. См http://pastebin.com/ysAF6p6h

См How and why is the dictionary hashes different in python2 and python3?


В длинной:

Давайте начнем с:

from nltk.book import * 

Импорт здесь происходит из https://github.com/nltk/nltk/blob/develop/nltk/book.py, который импортировать nltk.text.Text объект и прочитать несколько корпусов в Text objec т.

E.g. Это как переменная text1 была прочитана из nltk.book:

>>> import nltk.corpus 
>>> from nltk.text import Text 
>>> moby = Text(nltk.corpus.gutenberg.words('melville-moby_dick.txt')) 

Теперь, если мы спускаемся на код функции similar() в https://github.com/nltk/nltk/blob/develop/nltk/text.py#L377, мы видим эту инициализацию, если это первый случай доступа self._word_context_index:

def similar(self, word, num=20): 
    """ 
    Distributional similarity: find other words which appear in the 
    same contexts as the specified word; list most similar words first. 
    :param word: The word used to seed the similarity search 
    :type word: str 
    :param num: The number of words to generate (default=20) 
    :type num: int 
    :seealso: ContextIndex.similar_words() 
    """ 
    if '_word_context_index' not in self.__dict__: 
     #print('Building word-context index...') 
     self._word_context_index = ContextIndex(self.tokens, 
               filter=lambda x:x.isalpha(), 
               key=lambda s:s.lower()) 


    word = word.lower() 
    wci = self._word_context_index._word_to_contexts 
    if word in wci.conditions(): 
     contexts = set(wci[word]) 
     fd = Counter(w for w in wci.conditions() for c in wci[w] 
         if c in contexts and not w == word) 
     words = [w for w, _ in fd.most_common(num)] 
     print(tokenwrap(words)) 
    else: 
     print("No matches") 

Так что мы указываем на объект nltk.text.ContextIndex, то есть собираем все слова с похожим контекстным окном и храним их. В докстерии говорится:

Двунаправленный индекс между словами и их «контекстами» в тексте. Контекст слова обычно определяется как слова, которые встречаются в фиксированном окне вокруг слова; но другие определения могут также использоваться , предоставляя настраиваемую функцию контекста.

По умолчанию, если вы звоните функцию similar(), он будет инициализировать _word_context_index с настройками по умолчанию контекста т.е.левый и правый маркер окна, см https://github.com/nltk/nltk/blob/develop/nltk/text.py#L40

@staticmethod 
def _default_context(tokens, i): 
    """One left token and one right token, normalized to lowercase""" 
    left = (tokens[i-1].lower() if i != 0 else '*START*') 
    right = (tokens[i+1].lower() if i != len(tokens) - 1 else '*END*') 
    return (left, right) 

Из функции similar(), мы видим, что она перебирает слова в контексте, хранящихся в word_context_index, т.е. wci = self._word_context_index._word_to_contexts.

По существу, _word_to_contexts представляет собой словарь, где ключи слова в корпусе и значения левые и правые слова из https://github.com/nltk/nltk/blob/develop/nltk/text.py#L55:

self._word_to_contexts = CFD((self._key(w), self._context_func(tokens, i)) 
           for i, w in enumerate(tokens)) 

И здесь мы видим, что это CFD, который является nltk.probability.ConditionalFreqDist объект, который не включает сглаживание вероятности токена, см. Полный код на https://github.com/nltk/nltk/blob/develop/nltk/probability.py#L1646.


только возможно, и получение другого результата когда функция similar() перебирает most_common слов на https://github.com/nltk/nltk/blob/develop/nltk/text.py#L402

Учитывая, что два ключа в Counter объекта имеют те же счетчики, слово с ниже отсортированный хэш напечатает первый и хэш-ключа зависит от битового размера процессора, см http://www.laurentluce.com/posts/python-dictionary-implementation/


Весь процесс Попав подобные слова детерминировано, так как:

  • желтый/вход фиксировано Text(gutenberg.words('melville-moby_dick.txt'))
  • контекста по умолчанию для каждого слова также фиксируются, т.е. self._word_context_index
  • вычисления условное распределение частот для _word_context_index._word_to_contexts дискретна

исключения случаев, когда функция выводит most_common список, который когда есть галстук в значениях Counter, он выведет список ключей с учетом их хэшей.

В python2, нет никаких причин, чтобы получить другой выход из различных экземпляров той же машине со следующим кодом:

$ python 
>>> from nltk.book import * 
>>> text1.similar('monstrous') 
>>> exit() 
$ python 
>>> from nltk.book import * 
>>> text1.similar('monstrous') 
>>> exit() 
$ python 
>>> from nltk.book import * 
>>> text1.similar('monstrous') 
>>> exit() 

Но в Python3, он дает другой результат, каждый раз, когда вы запускаете text1.similar('monstrous') см http://pastebin.com/ysAF6p6h


Вот простой эксперимент, чтобы доказать, что изворотливые различия между хэширования python2 и python3:

[email protected]:~$ python -c "from collections import Counter; x = Counter({'foo': 1, 'bar': 1, 'foobar': 1, 'barfoo': 1}); print(x.most_common())" 
[('foobar', 1), ('foo', 1), ('bar', 1), ('barfoo', 1)] 
[email protected]:~$ python -c "from collections import Counter; x = Counter({'foo': 1, 'bar': 1, 'foobar': 1, 'barfoo': 1}); print(x.most_common())" 
[('foobar', 1), ('foo', 1), ('bar', 1), ('barfoo', 1)] 
[email protected]:~$ python -c "from collections import Counter; x = Counter({'foo': 1, 'bar': 1, 'foobar': 1, 'barfoo': 1}); print(x.most_common())" 
[('foobar', 1), ('foo', 1), ('bar', 1), ('barfoo', 1)] 


[email protected]:~$ python3 -c "from collections import Counter; x = Counter({'foo': 1, 'bar': 1, 'foobar': 1, 'barfoo': 1}); print(x.most_common())" 
[('barfoo', 1), ('foobar', 1), ('bar', 1), ('foo', 1)] 
[email protected]:~$ python3 -c "from collections import Counter; x = Counter({'foo': 1, 'bar': 1, 'foobar': 1, 'barfoo': 1}); print(x.most_common())" 
[('foo', 1), ('barfoo', 1), ('bar', 1), ('foobar', 1)] 
[email protected]:~$ python3 -c "from collections import Counter; x = Counter({'foo': 1, 'bar': 1, 'foobar': 1, 'barfoo': 1}); print(x.most_common())" 
[('bar', 1), ('barfoo', 1), ('foobar', 1), ('foo', 1)] 
+0

Я побежал ваш последний фрагмент кода (3 раза загрузка и вызов «похоже») на Windows (64-разрядная версия), и я получаю разный вывод для каждого прогона. – b3000

+0

@ b3000 Прохладный. Можете ли вы поделиться чем-то выходом? Даже разные ОС имеют разные вещи. – alvas

+1

О, UBER cool, это происходит только в python3! см. http://pastebin.com/ysAF6p6h – alvas

Смежные вопросы