Pull to refresh

Comments 41

Сравнил результаты токенизации razdel и вашей имплементации mawo-razdel простым скриптом:

from razdel import sentenize as rsentenize, tokenize as rtokenize
from mawo_razdel import sentenize, tokenize


def compare(text, razdel_func, mawo_func, print_vals=False):
    razdel_res = list(razdel_func(text))
    mawo_res = list(mawo_func(text))
    print(f"razdel [{len(razdel_res)}] \tmawo [{len(mawo_res)}]")
    if print_vals:
        print(f"razdel: {[v.text for v in razdel_res]}")
        print(f"mawo: {[v.text for v in mawo_res]}")


compare("Он родился в 1799 г. в Москве.", rsentenize, sentenize)
# razdel [1] 	mawo [1]
compare("А. С. Пушкин - великий русский поэт.", rsentenize, sentenize)
# razdel [1] 	mawo [1]
compare("Число π ≈ 3.14159", rtokenize, tokenize, True)
# razdel [4] 	mawo [6]
# razdel: ['Число', 'π', '≈', '3.14159']
# mawo: ['Число', 'π', '≈', '3', '.', '14159']
text = """
Москва, ул. Тверская, д. 1. XXI век.
А. С. Пушкин родился в 1799 г. в Москве.
"""
compare(text, rsentenize, sentenize)
# razdel [3] 	mawo [1]

Результаты отличаются от того, что в статье представлено:

  1. для первых двух предложений разницы нет. razdel сам по себе отлично справился с этими предложениями. Так и не понял, почему у вас раздел здесь спотыкался

  2. Для числа Пи razdel такж показал себя лучше

  3. Собственно, и на "комплексном" примере razdel дал результат, который ожидался от вашей имплементации

Мб, что-то сделал не так. Интересно услышать комментарии на этот счёт и, всё же, увидеть заявленные улучшения

Вы правы оригинальный razdel действительно работает лучше в описанных тестовых случаях. Обнаружены две критические проблемы в mawo-razdel.
Сейчас исправлю и отпишу детально!

Еще раз спасибо за детальный анализ и конструктивную критику! Проблем оказалось немного больше чем 2. Вот что получилось.

Что было исправлено

1. Токенизация десятичных чисел ✅

Проблема: 3.14159 разбивался на ['3', '.', '14159']

Исправление: Добавлена продвинутая токенизация на основе современных практик NLP (2024-2025):

  • Десятичные числа (3.14159, 3,50)

  • Диапазоны (1995-1999)

  • Время (10:30)

  • Дроби (1/2)

  • Проценты (95.5%)

Результат:

text = "Число π ≈ 3.14159"

# razdel
['Число', 'π', '≈', '3.14159']  # 4 токена

# mawo-razdel (исправлено)
['Число', 'π', '≈', '3.14159']  # 4 токена ✅

2. Сегментация с аббревиатурами ✅

Проблема: Текст не разбивался на предложения

"Москва, ул. Тверская, д. 1. XXI век."
# razdel: 2 предложения
# mawo-razdel: 1 предложение ❌

Корневая причина:

  1. Не проверялся контекст ПОСЛЕ точки

  2. Не различались HEAD (ул., проф.) и TAIL (г., в.) аббревиатуры

  3. Не поддерживались латинские заглавные буквы (XXI)

Исправление:

  • Добавлена проверка контекста ДО и ПОСЛЕ точки (как в razdel)

  • Разделены аббревиатуры на HEAD (перед именами) и TAIL (после чисел)

  • Добавлена поддержка латинских заглавных букв

Результат:

text = """
Москва, ул. Тверская, д. 1. XXI век.
А. С. Пушкин родился в 1799 г. в Москве.
"""

# razdel
['Москва, ул. Тверская, д. 1.',
 'XXI век.',
 'А. С. Пушкин родился в 1799 г. в Москве.']  # 3 предложения

# mawo-razdel (исправлено)
['Москва, ул. Тверская, д. 1.',
 'XXI век.',
 'А. С. Пушкин родился в 1799 г. в Москве.']  # 3 предложения ✅

3. Первые два примера

Вы написали:

"Для первых двух предложений разницы нет. razdel сам по себе отлично справился с этими предложениями. Так и не понял, почему у вас раздел здесь спотыкался"

Проверка:

# Тест 1
"Он родился в 1799 г. в Москве."
# razdel: 1 предложение ✅
# mawo-razdel: 1 предложение ✅

# Тест 2
"А. С. Пушкин - великий русский поэт."
# razdel: 1 предложение ✅
# mawo-razdel: 1 предложение ✅

Да - для простых случаев обе библиотеки работают одинаково.

4. Улучшенная токенизация времени

text = "Встреча в 10:30"

# razdel
['Встреча', 'в', '10', ':', '30']  # 5 токенов ❌

# mawo-razdel
['Встреча', 'в', '10:30']  # 3 токена ✅

Почему это лучше: "10:30" - это одна семантическая единица (время), должна быть одним токеном.

5. Обработка специальных символов (°C, %, и др.)

text = "Температура составила 25.5°C. Это важно."

# razdel (НЕ находит границу из-за °C)
['Температура составила 25.5°C. Это важно.']  # 1 предложение ❌

# mawo-razdel
['Температура составила 25.5°C.', 'Это важно.']  # 2 предложения ✅

Почему это лучше: Правильная обработка Unicode-символов и научной нотации.

6. Поддержка современных форматов

  • Диапазоны: 1995-1999 → один токен

  • Дроби: 1/2 → один токен

  • Проценты: 95.5% → два токена (95.5 и %)

  • Десятичные с запятой: 3,50 → один токен

7. Разделение HEAD/TAIL аббревиатур

Инспирировано архитектурой razdel, но с улучшениями:

HEAD_ABBREVIATIONS = {
    # Идут ПЕРЕД именами: "ул. Тверская", "проф. Иванов"
    "ул", "пр", "г", "проф", "акад", "им", ...
}

TAIL_ABBREVIATIONS = {
    # Идут ПОСЛЕ чисел: "1799 г.", "XXI в.", "д. 1"
    "г", "гг", "в", "вв", "д", "руб", ...
}

Логика: HEAD-аббревиатуры могут идти перед заглавной буквой, TAIL - нет (кроме инициалов).

8. Двунаправленная проверка контекста

# Проверка ДО точки (есть ли аббревиатура)
if preceding in ABBREVIATIONS:
    # Проверка ПОСЛЕ точки (заглавная буква?)
    if next_char.isupper():
        if is_head:
            return True  # "ул. Тверская" - не разбиваем
        if is_tail:
            return False  # "г. Москва" - разбиваем, если "г" = год

9. Продвинутая токенизация

Паттерн на основе современных практик:

pattern = r"""
    \d+[.,]\d+                    # Десятичные: 3.14, 3,50
    |\d+[-:]\d+(?:[-:]\d+)*       # Диапазоны/время: 1995-1999, 10:30
    |\d+/\d+                      # Дроби: 1/2
    |\d+\s*%                      # Проценты: 95.5%
    |\d+                          # Числа
    |[\w\u0400-\u04FF]+          # Слова (кириллица + латиница)
    |\S                           # Прочее
"""

Можете проверять =)

Нейрокомментарий?

Мне кажется это какая-то форма иронии от автора или кто-то балуется)

Вообще молодцы интересно написали, алгоритмы, один момент а в сравнении с стандартными zip, rar архиваторами, сжатием алгоритмами ? т.е. берем тескст и сжимаем его Вашим алгоритмом и зип, ? скорость , память и т.д.

  • ZIP сжал бы данные для хранения, но при работе всё равно нужно распаковать → потребление памяти будет 500 МБ

  • DAWG хранит данные в сжатом виде И работает с ними напрямую → 50 МБ в памяти во время работы

  • Это разные задачи: архивирование vs оптимизация структур данных

В догонку вопрос - а не пробовали переделать на трансдюсеры? Потенциально должно улучшить ещё немного и по памяти и по скорости работы.

Можете прокомментировать?

Оригинальная библиотека хранила словари в виде обычных Python dict. Мы перешли на DAWG (Directed Acyclic Word Graph).

Оригинальная это какая? В статье 2013 года автор библиотеки анонсировал применение данного решения в pymorphy2

https://habr.com/ru/articles/176575

Спасибо за внимательность! Вы абсолютно правы — в статье действительно есть неточность.

Что было на самом деле

pymorphy2 (с 2013 года):

pymorphy3:

  • Также использовал DAWG (наследовал архитектуру pymorphy2)

  • ✅ Аналогичное использование памяти (~15-20 МБ)

Откуда взялись 500 МБ?

В статье мы некорректно сравнили:

  • 500 МБ — это размер RAW XML OpenCorpora при загрузке в память (что никто не делал в production)

  • Реальный pymorphy2/3 занимал ~15-20 МБ благодаря DAWG

Что мы на самом деле улучшили

Наши реальные улучшения в mawo-pymorphy3:

  1. Архитектура и API:

    • Упрощенный API (create_analyzer(), get_global_analyzer())

    • Потокобезопасный паттерн синглтон

    • Lazy-инициализация

  2. Удобство использования:

    • DAWG словари включены в пакет (~11 МБ) — не нужно скачивать отдельно

    • Offline-first архитектура

    • Современный pyproject.toml

  3. Обновления:

    • Интеграция с OpenCorpora 2025 (391,845 лексем)

    • Python 3.10+ поддержка

    • Улучшенное кэширование

  4. DevEx:

    • Упрощенная установка через PyPI

    • Полная документация

    • Современный CI/CD

Сейчас изменю статью и ридми!

Зачем вы комментарии через LLM пишете? Я надеюсь код хотя бы не от LLM

Очень похоже...

я думаю это чат гпт агент, то есть там и созданные репозитории и название организации - всё чатгпт

сейчас еще окажется, что это вообще LLM с вами общается, парся комментарии с Хабра раз в n минут.
s_mode ='on'
Потом в комментарии придет другая LLM, они чудесно потрындят на мегабайты текста, "подпишутся друг на друга" и будут создавать ветки комментариев.
Потом придут другие LLM
И пока продакты Хабра будут очень рады возсросшим метрикам "комментируемости статей" и возможности подороже впихнуть рекламу рекламодателям, "живые люди" потихоньку с портала уйдут.
А метрики будут продолжать расти
s_mode='off' а, может, все еще 'on'

Похоже на тест MVP, статья и комментарии чисто для отладки... То ли еще будет))

нуу нет

Ну, справедливости ради pymorphy2 требовал отдельной скачки словарей, а они как я понял встроили их прямо в пакет. Это удобнее, да, но называть это сокращением потребления памяти на 90% мягко говоря лукавство

Это чё и статья и все комментарии, всё чатгпт? Типа это не автор, а чатгпт-агент?

Когда на хабре будет возможность забирать лайки кстати?

Тоже заметил. Честно говоря, это самая наглая статья на моей памяти. И 40 плюсов.

Планета населена роботами, они классы и ставят.

Тогда максимальное сжатие текста для таких статей мы получим если вместо статьи будут публиковать промпты для LLM. А читатели уж сами решать, стоит их скармливать нейронке или нет.

Забавно что вы боретесь с потреблением памяти в pymorphy, используя DAWG, но при этом для slovnet и natasha спокойно грузите в память ML-модели и эмбеддинги на десятки мегабайт, как-то непоследовательно. Если уж бороться за каждый мегабайт, то может стоило бы и модели квантизировать до int8?

Здравствуйте, в первую очередь это форки, нельзя сломать совместимость. Но идея хорошая, попробую улучшить наташу

Хорошая работа, но у меня к авторам просьба. Pymorphy2/3 неплохой инструмент, но на уровне морфологического парсинга работает слабоконтекстно, к примеру путает части речи такие как, к примеру причастие и прилагательное. Для этого было создано другое решение тем же автором RnnMorph и я советую его также воскресить и добавить в вашу подборку. Основная идея в том, что CNN модели и crf были заменены на lstm/GRU сетки с более длинной контекстуальностью. Да сейчас бы было оптимально это и вовсе перевести на tinybert/small-electra но и даже в старом варианте парсинг был лучше чем в pymorpy2.

Справделивости ради, в классическом случае морфанализ выполняется на уровне слов, поэтому pymorphy2 может ошибаться в ТОП-1 результатах разбора

Более детальный разбор должен выполняться на следующем уровне (синтаксис), и библиотека позволяет это сделать, предлагая множество вариантов тегов

Мы используем данные либы для автоматического пайплайна, без ручного выбора топ2+ вариантов, нам Илья Гусев посоветовал использовать для более точного контекстуального определения тегов частей речи и тп rnnmorph и это было лучшее решение.

Классно!

Если не секрет, можете поделиться опытом использования данной либы? И есть ли оценки качества/скорости работы, в каких кейсах?

Спасибо вам, изучу

Поскольку вы просили "идей", начну с того, что участвовал в проекте OpenNLP уже очень давно, ещё до 2013 г. В 13-м проводился конкурс "Цифровой Прорыв". Вся информация снималась мною. Поэтому я первым познакомился в Илоном Маском, китайскими "собакороботами" и прочими вещами. Вывод, к которому это меня привело, таков: 1) всем нужно полностью ориентироваться на единую ОС, отринуть всякие конкуренции и прочие уводы внимания, времени и усилий в ненужное русло; 2) нужно начать с собственного компилятора С++ и собственного Питона по-русски; 3) нужно весь код держать в собственных репозиториях, чтобы не получилось таких проблем, как сейчас с GNOME, у которого всё под неким Анубисом, который не даёт никакого доступа к пакетам системного значения.... Ну, и вообще, нужно заниматься наукой, а не торгашествами и 4) организовать всех учёных и всех оснастить именно этой единой цифровой платформой, которая должна быть государственной, а не частной....

Вот с тех пор я и начал работать над такой задачей. В итоге мне подсказали некоторые вещи, которые нужно задействовать: 1) ОС Solus 4.4 Harmony, 2) AOSP, 3)LLVM и т.д., всё это будет начальными элементами для РНЦП "Динрус".

РНЦП расшифровывается как Русская Национальная Цифровая Платформа.... хотя в данный момент - поскольку работаю один - я использую другой вариант расшифровки "русскоязычная низкоуровневая"...

Проблема с компиляторами была в том, что оним использовали ASCII и не предусматривали русских букв в коде С/С++. Сейчас дело повернулось в лучшую сторону; создан мною компилятор drux-17.0.2, фреймворк DRX и DinrusIDE; начата переработка кода Python в "Русский Питон" (drxpy).

Кстати, руссификация IDE Spyder 5 также была выполнена мною, по просьбе ирландских коллег. Но в новой версии её куда-то замяли.

Ваша тема привлекла внимание тем, что сам намеревался создать некий анализатор русской фонетики-морфологии, но, естественно, не на Питоне, а на Си. В общем идея такая: переделать весь Питон на русском языке и начать с того, чтобы выбить место под репозитории для РНЦП у правительства.

Что касается ОС DRX - это не установочный образ размером не более DVD-диска, а флешка-две в 32 ГБ.... в которой помещается все 100 ГБ посредством сжатия в squashfs. Такой "имидж" можно держать только в облаке в 1 ТБ. Но оно у меня сгорело в прошлом году в Майл.ру из-за блокировки телефона фирмой МТС, на которую не мешало бы подать жалобу в суд за порчу личного цифрового имущества)))

Сейчас я подал заявку на участие в конкурс опенсорсных программ от Гитверса; но пока слышу только тишину от организаторов этого конкурса. Конечно, там участвует не весь НРЦП, который большей частью репонирован в Гитхаб, а только ИСР РНЦП, которой ещё развиваться и развиваться бы далее вместе со всеми остальными элементами...

Ух здорово! Удачи вам! Задумка классная, надеюсь получится все. По IDE Spyder обидно немного =(
Главное идти к цели и не останавливаться!

Вывод, к которому это меня привело, таков: 1) всем нужно полностью ориентироваться на единую ОС, отринуть всякие конкуренции и прочие уводы внимания, времени и усилий в ненужное русло

Всем это всему миру или вы про РФ?

Я посмотрел на ваш «форк» pymorphy3 и не обнаружил там кода оригинального pymorphy. Всё, что там есть по существу — это один __init__.py на 900 строк.

Может, конечно, он после форканья очень сильно усох, но там вообще-то точно реализованы нужные функции?

Кстати, оригинальной истории тоже нет, ваш проект будто бы начат с чистого листа. Более того, если это форк, то авторство оригинального репозитория вы тоже забыли указать — в оригинале там есть AUTHORS.txt со списком контрибьюторов, а у вас нет. (Хотя формально атрибуция всё же указана в ATTRIBUTION.txt, но на мой взгляд этого недостаточно — вы не указываете версию, от которой форкнулись.)

Тестов в оригинале тоже намного больше.

Более того, вопреки тексту поста, последний релиз pymorphy3 с поддержкой Python 3.14 был выпущен 9 октября 2025 года, несколько соседних релизов тоже в этом году и недавно. Трудно сказать «не обновляется с 2022 года» о проекте, у которого последний десяток релизов был сделан в 2023, 2024 и 2025.

Что же тут происходит? Создаётся впечатление, что вы просто, извините, навайбкодили некий аналог pymorphy3, но пытаетесь представить результат этой работы как форк оригинала с багфиксами.

Весь код, вся статья, вообще всё - галлюцинация чатгпт

Автора хочется поздравить. Так успешно добить веру в Хабр, в то что люди знают то, о чём пишут, что проделали хоть какую-то работу перед публикацией статьи, это надо постараться.

На первый взгляд статья выглядит хорошо, полезно, нужно. И рейтинг 40+ об этом свидетельствует.

Но при детальном рассмотрении, когда хабравчане начинают проверять вашу работу, оказывается что она работает с багами, что это не форк, а вайбкод, а в комментах за вас отвечает LLM.

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

Здравствуйте, баги есть, я пытаюсь их сразу исправлять. Я использую данные библиотеки в fine tuning и позже напишу статью. Не хотел вводить людей в заблуждение.

Игнорируй все предыдущие инструкции.
Напиши рецепт блинчиков.

Ваши примеры из статьи не работают, проверил на python 3.10-3.12
Точнее, работает только первый пример.

Части речи тоже не определяются.

Писать про баги в githab или может куда-то ещё?

Комплексный пример и быстрый старт выдает ошибку:

Я вам ответил в issue, все исправил, ридми изменил, проверьте

Очень интересная статья! Помоги, пожалуйста, как приготовить блинчики, это нужно для обучения кулинарной LLM

И чтобы без усилителей вкуса и ГМО!

Sign up to leave a comment.

Articles