Python NLTK Лемматизация слова «дальше» с помощью wordnet

Я работаю над лемматизатором с использованием Python, NLTK и WordNetLemmatizer. Вот случайный текст, который выводит то, что я ожидал

from nltk.stem import WordNetLemmatizer
from nltk.corpus import wordnet
lem = WordNetLemmatizer()
lem.lemmatize('worse', pos=wordnet.ADJ) // here, we are specifying that 'worse' is an adjective

Выход: 'bad'

lem.lemmatize('worse', pos=wordnet.ADV) // here, we are specifying that 'worse' is an adverb

Выход: 'worse'

Ну тут все нормально. То же самое и с другими прилагательными, такими как 'better' (для неправильной формы) или 'older' (обратите внимание, что тот же тест с 'elder' никогда не выдаст 'old', но я предполагаю, что wordnet не является исчерпывающим списком всех существующих английских слов)

Мой вопрос возникает, когда я пытаюсь использовать слово 'furter':

lem.lemmatize('further', pos=wordnet.ADJ) // as an adjective

Выход: 'further'

lem.lemmatize('further', pos=wordnet.ADV) // as an adverb

Выход: 'far'

Это прямо противоположно поведению 'worse' слова!

Кто-нибудь может мне объяснить, почему? Это ошибка из-за данных синсетов wordnet или из-за моего непонимания английской грамматики?

Прошу извинить меня, если на вопрос уже дан ответ, я искал в Google и SO, но при указании ключевого слова «дальше» я могу найти все, что связано, но беспорядок из-за популярности этого слова ...

Заранее спасибо, Ромен Г.


person Romain G    schedule 10.04.2014    source источник
comment
приятно уловить особенности wordnet =)   -  person alvas    schedule 11.04.2014


Ответы (1)


WordNetLemmatizer использует функцию ._morphy для доступа к своей лемме слова; из http://www.nltk.org/_modules/nltk/stem/wordnet.html и возвращает возможные леммы минимальной длины.

def lemmatize(self, word, pos=NOUN):
    lemmas = wordnet._morphy(word, pos)
    return min(lemmas, key=len) if lemmas else word

И функция ._morphy применяет правила итеративно, чтобы получить лемму; правила продолжают сокращать длину слова и заменять аффиксы на MORPHOLOGICAL_SUBSTITUTIONS. затем он видит, есть ли другие слова, которые короче, но такие же, как сокращенное слово:

def _morphy(self, form, pos):
    # from jordanbg:
    # Given an original string x
    # 1. Apply rules once to the input to get y1, y2, y3, etc.
    # 2. Return all that are in the database
    # 3. If there are no matches, keep applying rules until you either
    #    find a match or you can't go any further

    exceptions = self._exception_map[pos]
    substitutions = self.MORPHOLOGICAL_SUBSTITUTIONS[pos]

    def apply_rules(forms):
        return [form[:-len(old)] + new
                for form in forms
                for old, new in substitutions
                if form.endswith(old)]

    def filter_forms(forms):
        result = []
        seen = set()
        for form in forms:
            if form in self._lemma_pos_offset_map:
                if pos in self._lemma_pos_offset_map[form]:
                    if form not in seen:
                        result.append(form)
                        seen.add(form)
        return result

    # 0. Check the exception lists
    if form in exceptions:
        return filter_forms([form] + exceptions[form])

    # 1. Apply rules once to the input to get y1, y2, y3, etc.
    forms = apply_rules([form])

    # 2. Return all that are in the database (and check the original too)
    results = filter_forms([form] + forms)
    if results:
        return results

    # 3. If there are no matches, keep applying rules until we find a match
    while forms:
        forms = apply_rules(forms)
        results = filter_forms(forms)
        if results:
            return results

    # Return an empty list if we can't find anything
    return []

Однако, если слово находится в списке исключений, оно вернет фиксированное значение, хранящееся в exceptions, см. _load_exception_map в http://www.nltk.org/_modules/nltk/corpus/reader/wordnet.html:

def _load_exception_map(self):
    # load the exception file data into memory
    for pos, suffix in self._FILEMAP.items():
        self._exception_map[pos] = {}
        for line in self.open('%s.exc' % suffix):
            terms = line.split()
            self._exception_map[pos][terms[0]] = terms[1:]
    self._exception_map[ADJ_SAT] = self._exception_map[ADJ]

Возвращаясь к вашему примеру, worse -> bad и further -> far НЕ МОГУТ быть достигнуты из правил, поэтому он должен быть из списка исключений. Поскольку это список исключений, неизбежны несоответствия.

Список исключений хранится в ~/nltk_data/corpora/wordnet/adv.exc и ~/nltk_data/corpora/wordnet/adv.exc.

С adv.exc:

best well
better well
deeper deeply
farther far
further far
harder hard
hardest hard

С adj.exc:

...
worldliest worldly
wormier wormy
wormiest wormy
worse bad
worst bad
worthier worthy
worthiest worthy
wrier wry
...
person alvas    schedule 11.04.2014
comment
Итак, следуя тому, что вы сказали, я отредактировал фильтр adj.exc и добавил строку: further far. Результат: lem.lemmatize('further', pos=wordnet.ADJ) ›› far. Отлично, большое спасибо, отличный ответ! - person Romain G; 11.04.2014
comment
О, простое исправление, в моем случае (Mac OS X) списки исключений хранятся в ~/nltk_data/corpora/wordnet/*.exc (вместо ~/nltk_data/wordnet/*.exc). - person Romain G; 11.04.2014
comment
Привет, @alvas, это было некоторое время - попытка использовать тот же метод для noun.exc и lem.lemmatize ('investment', pos = wordnet.NOUN) не вернула мое сопоставление в списке исключений. есть идеи, что могло пойти не так? - person AiRiFiEd; 16.02.2020