Работа с spaCy в Python: от установки до продвинутых техник
Библиотека spaCy в Python: обзор и практические примеры
spaCy - это библиотека для обработки естественного языка, ориентированная на производительность и простоту использования. Она предоставляет готовые пайплайны для токенизации, лемматизации, распознавания именованных сущностей (NER), частеречной разметки (POS) и синтаксического анализа зависимостей.
Как начать работу с spaCy и выполнить базовый анализ текста?
Установить библиотеку можно через pip:
pip install spacyбиблиотека nlp python (библиотека nlp в python)
Далее необходимо загрузить языковую модель. Для русского языка доступна модель ru_core_news_sm (маленькая) или ru_core_news_lg (большая с word vectors). Пример установки:
python -m spacy download ru_core_news_smбиблиотека spacy python (библиотека spacy в python)
После загрузки модели создаётся объект nlp, который обрабатывает текст и возвращает объект Doc:
import spacy
nlp = spacy.load("ru_core_news_sm")
text = "Москва - столица Российской Федерации."
doc = nlp(text)
# Вывод токенов с леммами и POS-тегами
for token in doc:
print(f"{token.text}: лемма {token.lemma_}, частеречная метка {token.pos_}")Python текст перевод (перевод текста с помощью python)
Результат показывает каждый токен, его лемму и часть речи.
Типичные ошибки:
- При импорте возникает
ModuleNotFoundError- не установлена библиотека. - При
nlp()выдаётсяValueError: [E050] Can't find model...- не загружена модель. Решение: выполнитьpython -m spacy download ru_core_news_smи указать точное имя. - Для текста на русском используется модель русского языка. Если попытаться загрузить английскую (
en_core_web_sm), результаты для русского текста будут некорректны.
Цели и случаи использования:
- Предобработка текстов для NLP-задач.
- Извлечение ключевых слов и сущностей.
- Подготовка данных для машинного обучения.
Как выполнить токенизацию и разбиение на предложения?
spaCy автоматически выполняет токенизацию при обработке. Для доступа к предложениям используется атрибут doc.sents:
text = "Привет! Как дела? Я изучаю spaCy."
doc = nlp(text)
for sent in doc.sents:
print(sent.text)определить язык слова python (определение языка слова в python)
Результат: три предложения. Если требуется кастомная токенизация, можно модифицировать пайплайн или использовать Tokenizer напрямую.
Проблема: точки в сокращениях (например, “т.е.”) могут разделять предложение неверно. Решение: использовать модель, учитывающую правила русского языка, или добавить исключения через nlp.tokenizer.add_special_case.
Как извлечь именованные сущности (NER)?
Атрибут doc.ents содержит распознанные сущности с метками (LOC, PER, ORG и др.). Пример:
doc = nlp("Президент Владимир Путин посетил Казань.")
for ent in doc.ents:
print(ent.text, ent.label_)
# Вывод: Владимир Путин PER, Казань LOCстоп слова python (стоп-слова в python)
Метки можно отобразить в понятном виде через spacy.explain('PER').
Проблема: модель может не распознавать редкие сущности или ошибочно выделять числа. Решение: дообучить модель на своих данных или использовать правила EntityRuler.
Как построить дерево синтаксических зависимостей?
Каждый токен имеет атрибуты dep_ (тип зависимости) и head (родительский токен). Пример вывода:
doc = nlp("Автомобиль едет по дороге.")
for token in doc:
print(token.text, token.dep_, token.head.text)
# Результат: Автомобиль nsubj едет; едет ROOT едет; по prep дороге; дороге pobj поДля визуализации используется displacy.
Проблема: сложные предложения могут иметь неочевидные зависимости. Рекомендуется проверять на тестовых данных.
Как кастомизировать пайплайн (добавить свой компонент)?
Можно добавить функцию или класс в nlp.pipeline.
@spacy.Language.component("my_component")
def my_component(doc):
print("Произвольная обработка")
return doc
nlp.add_pipe("my_component", last=True)
doc = nlp("Тест")Пайплайн становится последовательностью шагов. Компонент может модифицировать Doc (добавлять сущности, менять атрибуты).
Проблема: добавление несовместимых компонентов может нарушить работу. Необходимо соблюдать порядок (например, NER после токенизации).
Как использовать правила EntityRuler для точного выделения сущностей?
EntityRuler позволяет задавать шаблоны для извлечения сущностей:
from spacy.pipeline import EntityRuler
ruler = nlp.add_pipe("entity_ruler", before="ner")
patterns = [{"label": "ORG", "pattern": "Газпром"}]
ruler.add_patterns(patterns)
doc = nlp("Газпром объявил о новых проектах.")
for ent in doc.ents:
print(ent.text, ent.label_) # Газпром ORGПравила могут включать последовательности токенов, регулярные выражения, атрибуты.
Проблема: конфликт с моделью NER. Если правило добавляет сущность, уже выделенную моделью, приоритет определяется порядком в пайплайне. Рекомендуется размещать entity_ruler до или после ner с перезаписью через overwrite_ents=True.
Как работать с векторами слов (word vectors)?
Модели с суффиксом _lg или _md содержат векторы. При обращении к token.vector получается массив чисел. Пример нахождения семантической близости:
nlp = spacy.load("ru_core_news_lg")
doc1 = nlp("король")
doc2 = nlp("королева")
print(doc1.similarity(doc2)) # число от 0 до 1Проблема: маленькая модель (sm) не имеет векторных представлений. Используйте _md или _lg. Также similarity основана на усреднении векторов токенов, что не всегда адекватно для длинных текстов.
Расширенные примеры работы с spaCy
Пример 1. Полный пайплайн с кастомным компонентом для поиска адресов
Создаётся компонент, который ищет шаблон "ул. Название, дом" с помощью регулярного выражения и добавляет сущность ADDR.
import spacy
import re
from spacy.tokens import Span
nlp = spacy.load("ru_core_news_sm")
@spacy.Language.component("addr_finder")
def addr_finder(doc):
pattern = r"ул\.\s+[А-Яа-яA-Za-z]+,\s+д\.\s+\d+"
matches = re.finditer(pattern, doc.text)
new_ents = []
for match in matches:
start = doc.char_span(match.start(), match.end())
if start is not None:
new_ents.append(Span(doc, start.start, start.end, label="ADDR"))
doc.ents = list(doc.ents) + new_ents
return doc
nlp.add_pipe("addr_finder", after="ner")
doc = nlp("Офис находится по адресу ул. Ленина, д. 10.")
for ent in doc.ents:
if ent.label_ == "ADDR":
print(f"Найден адрес: {ent.text}")Найден адрес: ул. Ленина, д. 10
Пояснение:
Регулярное выражение ищет подстроки, соответствующие шаблону. doc.char_span преобразует позиции символов в спаны токенов. Сущности добавляются к уже существующим, чтобы не потерять результаты стандартного NER.
Пример 2. Визуализация зависимостей с displacy (сервер или HTML)
from spacy import displacy
doc = nlp("Кошка поймала мышь.")
html = displacy.render(doc, style="dep", page=True)
with open("dep_visual.html", "w", encoding="utf-8") as f:
f.write(html)Открыв файл в браузере, пользователь видит граф зависимостей. Для серверного варианта используется displacy.serve(doc, style="dep").
Проблема: displacy требует установки библиотеки Flask для сервера. Если HTML-вывод не отображается корректно, проверить кодировку и наличие всех шрифтов.
Пример 3. Извлечение отношений между сущностями (Relation Extraction) с помощью шаблонов зависимостей
Используется синтаксическое дерево для нахождения триплетов (субъект, глагол, объект).
def extract_relations(doc):
relations = []
for token in doc:
if token.dep_ == "nsubj" and token.head.pos_ == "VERB":
subj = token.text
verb = token.head.text
# ищем объект при том же глаголе
for child in token.head.children:
if child.dep_ == "obj":
obj = child.text
relations.append((subj, verb, obj))
return relations
doc = nlp("Мальчик купил книгу.")
print(extract_relations(doc))[('Мальчик', 'купил', 'книгу')]Пояснение:
Функция перебирает токены, ищет подлежащие (nsubj) с глаголом-родителем, затем среди детей глагола - прямое дополнение (obj). Этот простой метод работает для базовых предложений, но не для пассивных конструкций.
Пример 4. Обработка большого текста (streaming) и экономия памяти
Для текстов, не помещающихся в память, используется nlp.pipe с генератором:
texts = ["Первый документ.", "Второй документ, более длинный.", "Третий."]
for doc in nlp.pipe(texts, batch_size=2):
print(len(doc)) # количество токеновnlp.pipe обрабатывает тексты последовательно, не загружая все в память одновременно. Параметр batch_size регулирует размер пакета.
Проблема: если тексты очень большие, может возникнуть переполнение памяти из-за хранения всех объектов Doc в списке. Решение: обрабатывать и сразу выводить результат, не сохраняя все doc.
Пример 5. Сравнение spaCy с NLTK - токенизация
# NLTK
import nltk
nltk.download('punkt')
from nltk.tokenize import word_tokenize
print(word_tokenize("Привет, мир!"))
# spaCy
doc = nlp("Привет, мир!")
tokens = [token.text for token in doc]
print(tokens)NLTK: ['Привет', ',', 'мир', '!'] spaCy: ['Привет', ',', 'мир', '!']
Оба результата идентичны, но spaCy дополнительно даёт леммы, POS, зависимости. NLTK легче для простой токенизации, но не предоставляет готового пайплайна.