Лемматизация — это процесс преобразования слова в его базовую форму. Разница между стемминг (stemming) и лемматизацией заключается в том, что лемматизация учитывает контекст и преобразует слово в его значимую базовую форму, тогда как стемминг просто удаляет последние несколько символов, что часто приводит к неверному значению и орфографическим ошибкам.
Например, лемматизация правильно определила бы базовую форму «caring» и «care», в то время как стемминг отрезал бы «ing» и преобразовал ее в car.
«Caring» -> Лемматизация -> «Care»
«Caring» -> Стемминг -> «Car»
Кроме того, иногда одно и то же слово может иметь несколько разных лемм. Основываясь на контексте, который вы используете, вы должны определить тег «part-of-speech» (POS) для слова в этом конкретном контексте и извлечь соответствующую лемму. Примеры реализации этого приведены в следующих разделах.
В этой статье мы рассмотри, реализацию лемматизации с помощью следующих пакетов Python.
Wordnet — это большая, свободно распространяемая и общедоступная лексическая база данных для английского языка с целью установления структурированных семантических отношений между словами. Библиотека также предлагает возможности лемматизации и является одним из самых ранних и наиболее часто используемых лемматизаторов.
Пример использования.
Вначале необходимо загрузить библиотеку
# How to install and import NLTK # In terminal or prompt: # pip install nltk # # Download Wordnet through NLTK in python console: import nltk nltk.download('wordnet')
Для того, чтобы лемматизировать, нужно создать экземпляр WordNetLemmatizer() и вызвать функцию lemmatize() для одного слова.
import nltk from nltk.stem import WordNetLemmatizer # Init the Wordnet Lemmatizer lemmatizer = WordNetLemmatizer() # Lemmatize Single Word print(lemmatizer.lemmatize("bats")) #> bat print(lemmatizer.lemmatize("are")) #> are print(lemmatizer.lemmatize("feet")) #> foot
Давайте лемматизируем простое предложение. Сначала мы разобъем предложение на слова с помощью nltk.word_tokenize, а затем вызываем lemmatizer.lemmatize() для каждого слова.
# Define the sentence to be lemmatized sentence = "The striped bats are hanging on their feet for best" # Tokenize: Split the sentence into words word_list = nltk.word_tokenize(sentence) print(word_list) #> ['The', 'striped', 'bats', 'are', 'hanging', 'on', 'their', 'feet', 'for', 'best'] # Lemmatize list of words and join lemmatized_output = ' '.join([lemmatizer.lemmatize(w) for w in word_list]) print(lemmatized_output) #> The striped bat are hanging on their foot for best
Приведенный выше код является простым примером того, как использовать лемматизатор wordnet для слов и предложений.
Обратите внимание, что есть ошибки. Потому что «are» не преобразовалось в «be», а «Hang» не преобразовалось в «hang», как ожидалось. Это можно исправить, если в качестве второго аргумента lemmatize() указать правильный тег «part-of-speech» (POS-тег).
Иногда одно и то же слово может иметь несколько лемм в зависимости от значения / контекста.
print(lemmatizer.lemmatize("stripes", 'v')) #> strip print(lemmatizer.lemmatize("stripes", 'n')) #> stripe
Достаточно сложно вручную проставить соответствующий POS-тег для каждого слова при обработке больших текстов. Поэтому вместо этого мы найдем правильный POS-тег для каждого слова, сопоставим его с правильным входным символом, который принимает WordnetLemmatizer, и передадим его в качестве второго аргумента в lemmatize().
Как получить POS-тег для выбранного слова?
В nltk для этого есть метод nltk.pos_tag(). Он принимает только список (список слов), даже если нужно передать только одно слово.
print(nltk.pos_tag(['feet'])) #> [('feet', 'NNS')] print(nltk.pos_tag(nltk.word_tokenize(sentence))) #> [('The', 'DT'), ('striped', 'JJ'), ('bats', 'NNS'), ('are', 'VBP'), ('hanging', 'VBG'), ('on', 'IN'), ('their', 'PRP$'), ('feet', 'NNS'), ('for', 'IN'), ('best', 'JJS')]
nltk.pos_tag() возвращает кортеж с тегом POS. Ключевым моментом здесь является сопоставление POS-тегов NLTK с форматом, принятым лемматизатором wordnet.
# Lemmatize with POS Tag from nltk.corpus import wordnet def get_wordnet_pos(word): """Map POS tag to first character lemmatize() accepts""" tag = nltk.pos_tag([word])[0][1][0].upper() tag_dict = {"J": wordnet.ADJ, "N": wordnet.NOUN, "V": wordnet.VERB, "R": wordnet.ADV} return tag_dict.get(tag, wordnet.NOUN) # 1. Init Lemmatizer lemmatizer = WordNetLemmatizer() # 2. Lemmatize Single Word with the appropriate POS tag word = 'feet' print(lemmatizer.lemmatize(word, get_wordnet_pos(word))) # 3. Lemmatize a Sentence with the appropriate POS tag sentence = "The striped bats are hanging on their feet for best" print([lemmatizer.lemmatize(w, get_wordnet_pos(w)) for w in nltk.word_tokenize(sentence)]) #> ['The', 'strip', 'bat', 'be', 'hang', 'on', 'their', 'foot', 'for', 'best']
spaCy является относительно новым пакетом и на данный момент считается стандартом в индустрии NLP. Он поставляется с предварительно созданными моделями, которые могут анализировать текст и выполнять различный функционал, связанный с NLP.
Прежде чем мы начнем, установим spaCy и загрузим модель «en».
# Install spaCy (run in terminal/prompt) import sys !{sys.executable} -m pip install spacy # Download spaCy's 'en' Model !{sys.executable} -m spacy download en
spaCy по умолчанию определяет часть речи и назначает соответствующую лемму.
import spacy # Initialize spacy 'en' model, keeping only tagger component needed for lemmatization nlp = spacy.load('en', disable=['parser', 'ner']) sentence = "The striped bats are hanging on their feet for best" # Parse the sentence using the loaded 'en' model object `nlp` doc = nlp(sentence) # Extract the lemma for each token and join " ".join([token.lemma_ for token in doc]) #> 'the strip bat be hang on -PRON- foot for good'
Он выполнил все лемматизации, которые так же выполнил лемматизатор Wordnet, с проставленным правильным тегом POS. Плюс к этому также лемматизируется такое слово как «best» превращаясь в «good».
Обратите внимание что символ -PRON- появляется, когда spacy обнаруживает местоимение.
TextBlob — это мощный, быстрый пакет NLP. Используя объекты Word и TextBlob, довольно просто анализировать и лемматизировать слова и предложения соответственно.
# pip install textblob from textblob import TextBlob, Word # Lemmatize a word word = 'stripes' w = Word(word) w.lemmatize() #> stripe
Чтобы лемматизировать предложение или абзац, мы анализируем его с помощью TextBlob а затем вызвать функцию lemmatize() для проанализированных слов.
# Lemmatize a sentence sentence = "The striped bats are hanging on their feet for best" sent = TextBlob(sentence) " ". join([w.lemmatize() for w in sent.words]) #> 'The striped bat are hanging on their foot for best'
Хотя видно что с самого начала он не справился с работой. Как и NLTK, TextBlob внутри использует Wordnet. Соотвественно, для корректной работы так же требует передачу соответствующего POS-тега методу lemmatize().
# Define function to lemmatize each word with its POS tag def lemmatize_with_postag(sentence): sent = TextBlob(sentence) tag_dict = {"J": 'a', "N": 'n', "V": 'v', "R": 'r'} words_and_tags = [(w, tag_dict.get(pos[0], 'n')) for w, pos in sent.tags] lemmatized_list = [wd.lemmatize(tag) for wd, tag in words_and_tags] return " ".join(lemmatized_list) # Lemmatize sentence = "The striped bats are hanging on their feet for best" lemmatize_with_postag(sentence) #> 'The striped bat be hang on their foot for best'
Pattern by CLiPs — это универсальный пакет со многими полезными возможностями NLP.
Его установка:
pip install pattern
Далее пример его использования
import pattern from pattern.en import lemma, lexeme sentence = "The striped bats were hanging on their feet and ate best fishes" " ".join([lemma(wd) for wd in sentence.split()]) #> 'the stripe bat be hang on their feet and eat best fishes'
Можно просмотреть возможные лексемы для каждого слова.
# Lexeme's for each word [lexeme(wd) for wd in sentence.split()] #> [['the', 'thes', 'thing', 'thed'], #> ['stripe', 'stripes', 'striping', 'striped'], #> ['bat', 'bats', 'batting', 'batted'], #> ['be', 'am', 'are', 'is', 'being', 'was', 'were', 'been', #> . 'am not', "aren't", "isn't", "wasn't", "weren't"], #> ['hang', 'hangs', 'hanging', 'hung'], #> ['on', 'ons', 'oning', 'oned'], #> ['their', 'theirs', 'theiring', 'theired'], #> ['feet', 'feets', 'feeting', 'feeted'], #> ['and', 'ands', 'anding', 'anded'], #> ['eat', 'eats', 'eating', 'ate', 'eaten'], #> ['best', 'bests', 'besting', 'bested'], #> ['fishes', 'fishing', 'fishesed']]
Вы также можно получить лемму, анализируя текст.
from pattern.en import parse print(parse('The striped bats were hanging on their feet and ate best fishes', lemmata=True, tags=False, chunks=False)) #> The/DT/the striped/JJ/striped bats/NNS/bat were/VBD/be hanging/VBG/hang on/IN/on their/PRP$/their #> feet/NNS/foot and/CC/and ate/VBD/eat best/JJ/best fishes/NNS/fish
Standford CoreNLP — так же популярный инструмент NLP, который изначально был реализован на Java. Так же был сделано несколько враперов на Python. Тот, который я использую ниже, достаточно удобен в использовании.
Но перед этим вам нужно скачать Java и Standford CoreNLP. Прежде чем перейти к коду лемматизации, убедитесь в следующем:
Шаг 1: установите Java 8
На Mac выполните следующее
brew update brew install jenv brew cask install java
Шаг 2: Загрузите Stanford CoreNLP и распакуйте его.
Шаг 3: Запустите Stanford CoreNLP с терминала. Для этого перейдите в папку, которую вы только что распаковали, и запустите следующую команду в терминале:
cd stanford-corenlp-full-2018-02-27 java -mx4g -cp "*" edu.stanford.nlp.pipeline.StanfordCoreNLPServer -annotators "tokenize,ssplit,pos,lemma,parse,sentiment" -port 9000 -timeout 30000
Запустите StanfordCoreNLPServer на порту 9000. Теперь можно извлечь леммы в python.
В пакете stanfordcorenlp лемма встроена в выходные данные метода annotate() объекта StanfordCoreNLP connection (см. Код ниже).
# Run `pip install stanfordcorenlp` to install stanfordcorenlp package from stanfordcorenlp import StanfordCoreNLP import json # Connect to the CoreNLP server we just started nlp = StanfordCoreNLP('http://localhost', port=9000, timeout=30000) # Define proporties needed to get lemma props = {'annotators': 'pos,lemma', 'pipelineLanguage': 'en', 'outputFormat': 'json'} sentence = "The striped bats were hanging on their feet and ate best fishes" parsed_str = nlp.annotate(sentence, properties=props) parsed_dict = json.loads(parsed_str) parsed_dict #> {'sentences': [{'index': 0, #> 'tokens': [{'after': ' ', #> 'before': '', #> 'characterOffsetBegin': 0, #> 'characterOffsetEnd': 3, #> 'index': 1, #> 'lemma': 'the', << ----------- LEMMA #> 'originalText': 'The', #> 'pos': 'DT', #> 'word': 'The'}, #> {'after': ' ', #> 'before': ' ', #> 'characterOffsetBegin': 4, #> 'characterOffsetEnd': 11, #> 'index': 2, #> 'lemma': 'striped', << ----------- LEMMA #> 'originalText': 'striped', #> 'pos': 'JJ', #> 'word': 'striped'}, #> {'after': ' ', #> 'before': ' ', #> 'characterOffsetBegin': 12, #> 'characterOffsetEnd': 16, #> 'index': 3, #> 'lemma': 'bat', << ----------- LEMMA #> 'originalText': 'bats', #> 'pos': 'NNS', #> 'word': 'bats'} #> ... #> ...
Вывод nlp.annotate() был преобразован в dict с использованием json.loads. Теперь нужная нам лемма встроена в пару слоев внутри parsed_dict. Здесь нам нужно только значение леммы из каждого dict.
lemma_list = [v for d in parsed_dict['sentences'][0]['tokens'] for k,v in d.items() if k == 'lemma'] " ".join(lemma_list) #> 'the striped bat be hang on they foot and eat best fish'
Обобщим эту замечательную функцию для обработки больших абзацев.
from stanfordcorenlp import StanfordCoreNLP import json, string def lemmatize_corenlp(conn_nlp, sentence): props = { 'annotators': 'pos,lemma', 'pipelineLanguage': 'en', 'outputFormat': 'json' } # tokenize into words sents = conn_nlp.word_tokenize(sentence) # remove punctuations from tokenised list sents_no_punct = [s for s in sents if s not in string.punctuation] # form sentence sentence2 = " ".join(sents_no_punct) # annotate to get lemma parsed_str = conn_nlp.annotate(sentence2, properties=props) parsed_dict = json.loads(parsed_str) # extract the lemma for each word lemma_list = [v for d in parsed_dict['sentences'][0]['tokens'] for k,v in d.items() if k == 'lemma'] # form sentence and return it return " ".join(lemma_list) # make the connection and call `lemmatize_corenlp` nlp = StanfordCoreNLP('http://localhost', port=9000, timeout=30000) lemmatize_corenlp(conn_nlp=nlp, sentence=sentence) #> 'the striped bat be hang on they foot and eat best fish'
Gensim предоставляет средства лемматизации на основе пакета pattern. Используем метод lemmatize() в модуле utils. По умолчанию lemmatize() допускает только теги «JJ», «VB», «NN» и «RB».
from gensim.utils import lemmatize sentence = "The striped bats were hanging on their feet and ate best fishes" lemmatized_out = [wd.decode('utf-8').split('/')[0] for wd in lemmatize(sentence)] #> ['striped', 'bat', 'be', 'hang', 'foot', 'eat', 'best', 'fish']
Treetagger является POS тегером для многих языков. И он также предоставляем возможности лемматизации.
Пример его использования
# pip install treetaggerwrapper import treetaggerwrapper as ttpw tagger = ttpw.TreeTagger(TAGLANG='en', TAGDIR='/Users/ecom-selva.p/Documents/MLPlus/11_Lemmatization/treetagger') tags = tagger.tag_text("The striped bats were hanging on their feet and ate best fishes") lemmas = [t.split('\t')[-1] for t in tags] #> ['the', 'striped', 'bat', 'be', 'hang', 'on', 'their', 'foot', 'and', 'eat', 'good', 'fish']
Treetagger хорошо справляется с преобразованием «best» в «good», а также в другие слова. Для большей информации о пакете обратитесь к документации TreeTaggerWrapper.
Давайте запустим лемматизацию, используя 5 пакетов для следующих предложений, и сравним вывод.
sentence = """Following mice attacks, caring farmers were marching to Delhi for better living conditions. Delhi police on Tuesday fired water cannons and teargas shells at protesting farmers as they tried to break barricades with their cars, automobiles and tractors.""" # NLTK from nltk.stem import WordNetLemmatizer lemmatizer = WordNetLemmatizer() pprint(" ".join([lemmatizer.lemmatize(w, get_wordnet_pos(w)) for w in nltk.word_tokenize(sentence) if w not in string.punctuation])) # ('Following mouse attack care farmer be march to Delhi for well living ' # 'condition Delhi police on Tuesday fire water cannon and teargas shell at ' # 'protest farmer a they try to break barricade with their car automobile and ' # 'tractor') # Spacy import spacy nlp = spacy.load('en', disable=['parser', 'ner']) doc = nlp(sentence) pprint(" ".join([token.lemma_ for token in doc])) # ('follow mice attack , care farmer be march to delhi for good living condition ' # '. delhi police on tuesday fire water cannon and teargas shell at protest ' # 'farmer as -PRON- try to break barricade with -PRON- car , automobile and ' # 'tractor .') # TextBlob pprint(lemmatize_with_postag(sentence)) # ('Following mouse attack care farmer be march to Delhi for good living ' # 'condition Delhi police on Tuesday fire water cannon and teargas shell at ' # 'protest farmer a they try to break barricade with their car automobile and ' # 'tractor') # Pattern from pattern.en import lemma pprint(" ".join([lemma(wd) for wd in sentence.split()])) # ('follow mice attacks, care farmer be march to delhi for better live ' # 'conditions. delhi police on tuesday fire water cannon and tearga shell at ' # 'protest farmer a they try to break barricade with their cars, automobile and ' # 'tractors.') # Stanford pprint(lemmatize_corenlp(conn_nlp=conn_nlp, sentence=sentence)) # ('follow mouse attack care farmer be march to Delhi for better living ' # 'condition Delhi police on Tuesday fire water cannon and tearga shell at ' # 'protest farmer as they try to break barricade with they car automobile and ' # 'tractor')
Оригинал:
https://www.machinelearningplus.com/nlp/lemmatization-examples-python/
Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…
Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…
Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…
Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…
Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…
Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…
View Comments
Отличная статья! Спасибо вам. Только вы не прикрепили инструмент по лемматицзации. Я пол часа искал нормальный. Вот https://c-wd.ru/tools/lemma/ может кому пригодится
А что по поводу обработки текстов на русском языке? Подскажите, пожалуйста, какие нюансы,кажется все это по умолчанию не работает на русском.
Алекс, добрый вечер
Для обработки текстов на русском языке используется либо отдельные библиотеки, либо в больших библиотеках есть способ настройки лемматизации, стемминга и тд для русского языка
Касаемо отдельных библиотек, вы можете присмотреться к проекту Natasha от наших СНГ разрабов