лемматизатор питона, который лемматизирует «политический» и «политический» в одно и то же слово - PullRequest
2 голосов
/ 07 мая 2019

Я тестировал разные лемматизаторы Python для решения, которое я строю. Одна сложная проблема, с которой я столкнулся, заключается в том, что стеммеры производят неанглийские слова, которые не подойдут для моего варианта использования. Хотя парадигматисты правильно переносят слова «политика» и «политический» в один и тот же корень, я хотел бы сделать это с помощью лемматизатора, но spacy и nltk производят разные слова для «политический» и «политический». Кто-нибудь знает более мощный лемматизатор? Мое идеальное решение выглядело бы так:

from nltk.stem import WordNetLemmatizer

lemmatizer = WordNetLemmatizer()

print("political = ", lemmatizer.lemmatize("political"))
print("politics = ", lemmatizer.lemmatize("politics"))  

возвращение:

political =  political
politics =  politics  

Куда я хочу вернуться:

political =  politics
politics =  politics  

1 Ответ

1 голос
/ 07 мая 2019

Во-первых, лемма - это не "корневое" слово, как вы думали. Это просто форма, которая существует в словаре, а для английского языка в NLTK WordNetLemmatizer словарь - это WordNet, и если запись словаря находится в WordNet, то это лемма, здесь есть записи для «политических» и «политических», поэтому они Действительна лемма:

from itertools import chain
print(set(chain(*[ss.lemma_names() for ss in wn.synsets('political')])))
print(set(chain(*[ss.lemma_names() for ss in wn.synsets('politics')])))

[вне]:

{'political'}
{'political_sympathies', 'political_relation', 'government', 'politics', 'political_science'}

Может быть, есть другие инструменты, которые могут это сделать, но я попробую это как первый.

Сначала обведите все названия лемм и сгруппируйте леммы с одинаковым основанием:

from collections import defaultdict

from wn import WordNet
from nltk.stem import PorterStemmer

porter = PorterStemmer()
wn = WordNet()

x = defaultdict(set)
i = 0
for lemma_name in wn.all_lemma_names():
    if lemma_name:
        x[porter.stem(lemma_name)].add(lemma_name)
        i += 1

Примечание: pip install -U wn

Затем в качестве проверки работоспособности мы проверяем, что нет. лемм> нет. групп:

print(len(x.keys()), i)

[выход]:

(128442, 147306)

Тогда мы можем взглянуть на группировки:

for k in sorted(x):
    if len(x[k]) > 1:
        print(k, x[k])

Кажется, мы делаем то, что нам нужно, чтобы сгруппировать слова вместе с их "корневым словом", например,

poke {'poke', 'poking'}
polar {'polarize', 'polarity', 'polarization', 'polar'}
polaris {'polarisation', 'polarise'}
pole_jump {'pole_jumping', 'pole_jumper', 'pole_jump'}
pole_vault {'pole_vaulter', 'pole_vault', 'pole_vaulting'}
poleax {'poleaxe', 'poleax'}
polem {'polemically', 'polemics', 'polemic', 'polemical', 'polemize'}
police_st {'police_state', 'police_station'}
polish {'polished', 'polisher', 'polish', 'polishing'}
polit {'politics', 'politic', 'politeness', 'polite', 'politically', 'politely', 'political'}
poll {'poll', 'polls'}

Но если мы посмотрим ближе, то возникнет путаница:

polit {'politics', 'politic', 'politeness', 'polite', 'politically', 'politely', 'political'}

Так что я бы предложил следующий шаг

чтобы снова пройтись по группировкам, запустить некоторую семантику и проверить «родство» слов и разбить слова, которые могут быть не связаны , возможно, попробовать что-то вроде Universal Sentence Encoder, например. https://colab.research.google.com/drive/1BM-eKdFb2G2zXqNt3dHgVm4gH8PaPJOq (может быть, нетривиальная задача)

Или выполните ручную работу и измените порядок групп . (Тяжелую работу уже проделал носитель портера в группировке, теперь пришло время сделать какую-то человеческую работу)

Тогда вам нужно будет как-то найти корень среди каждой группы слов (то есть прототип / метка для кластера).

Наконец, используя ресурс групп слов, которые вы создали, вы не можете «найти корневое слово».

...