Работа с spaCy в Python: от установки до продвинутых техник

Раздел: NLP -> Обработка естественного языка

Библиотека 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 легче для простой токенизации, но не предоставляет готового пайплайна.

Библиотека spaCy в Python - comments

En
библиотека spacy python (python)