Skip to content

NikPlayTik/Moodix-NLP

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Moodix v1.1.1

Moodix — локальный модуль анализа русскоязычного текста. Актуальная версия определяет:

  • основное настроение: negative, neutrally, positive;
  • 15 независимых суб-настроений;
  • 6 независимых деструктивных классов;
  • фрагменты исходного текста, связанные с найденными настроениями;
  • результаты одного текста или пакета текстов.

Данные обрабатываются локально. Режим api в Moodix означает локальный Python/JSON batch API и не запускает HTTP-сервер.

Содержание

  1. Состав актуальной сборки
  2. Установка
  3. Быстрый запуск
  4. Как модель обрабатывает текст
  5. Как интерпретировать проценты
  6. Argmax и threshold
  7. Режим standard
  8. Режим fragments
  9. Что означают start и end
  10. Что означает вклад фрагмента
  11. Локальный API и пакетная обработка
  12. Структура JSON-ответа
  13. Конфигурация
  14. Ограничения текущей реализации
  15. Benchmark
  16. Тестирование

Состав актуальной сборки

Актуальная сборка находится в каталоге:

model/Moodix v1.1.1_28.06.2026/
Файл Назначение
moodix.py CLI, fragment-анализ, Python API и JSON batch-режим
model.keras обученная Keras-модель
tokenizer.pickle токенизатор и словарь модели
label_config.json порядок классов и настройки входной последовательности
thresholds.json индивидуальные пороги классов
requirements.txt минимальные зависимости актуального анализатора

Сборки v0.65 и v0.66 оставлены в репозитории для сравнения и обратной совместимости. Их поведение отличается от v1.1.1.

Установка

Рекомендуется Python 3.10 или совместимая версия, поддерживаемая выбранной сборкой TensorFlow.

python -m venv .venv
source .venv/bin/activate
pip install -r "model/Moodix v1.1.1_28.06.2026/requirements.txt"

Для Windows:

python -m venv .venv
.venv\Scripts\activate
pip install -r "model/Moodix v1.1.1_28.06.2026/requirements.txt"

Модель работает на CPU. GPU не является обязательным, но может ускорить большие пакетные прогоны.

Быстрый запуск

Общая оценка текста

python "model/Moodix v1.1.1_28.06.2026/moodix.py" \
  --mode standard \
  --text "Спасибо за помощь, сервис отличный."

Оценка с выделением фрагментов

python "model/Moodix v1.1.1_28.06.2026/moodix.py" \
  --mode fragments \
  --text "Сервис отличный, но приложение зависает."

Пакетная обработка JSON

python "model/Moodix v1.1.1_28.06.2026/moodix.py" \
  --mode api \
  --input-file batch.json \
  --output-file result.json

Интерактивный режим

Если --text не указан для standard или fragments, анализатор последовательно запрашивает строки до команды exit:

python "model/Moodix v1.1.1_28.06.2026/moodix.py" --mode standard

Как модель обрабатывает текст

1. Нормализация

Функция clean_text:

  • переводит текст в нижний регистр;
  • заменяет URL на urltoken;
  • заменяет упоминания вида @user на mentiontoken;
  • заменяет хештег на hashtagtoken и сохраняет его слово;
  • преобразует группы эмодзи в эмоциональные токены;
  • отдельно кодирует !, ?, повторные !! и ??;
  • удаляет остальные неподдерживаемые символы;
  • нормализует повторяющиеся пробелы.

Лемматизация в актуальном moodix.py не выполняется. Модель использует токены, полученные после описанной нормализации.

2. Токенизация

Keras-токенизатор преобразует слова в целочисленные идентификаторы. Неизвестные слова переходят в специальный OOV-токен.

Максимальная длина одного входа задаётся параметром:

{
  "max_seq_length": 50
}

То есть один непосредственный вход модели содержит не более 50 токенов.

3. Архитектура

Упрощённый путь данных:

50 token IDs
  → Embedding: 30 000 × 128
  → SpatialDropout1D
  → Bidirectional LSTM: 128 нейронов в каждом направлении
  → GlobalAveragePooling1D
  → общий Dense-слой
  → три отдельные выходные головы

Выходные головы:

Голова Размер Активация Назначение
main_output 3 softmax один основной класс
sub_output 15 sigmoid независимые суб-настроения
destr_output 6 sigmoid независимые деструктивные классы

Модель классифицирует последовательность целиком. Точные позиции фрагментов не являются отдельным обученным выходом модели: fragment-режим получает их через сегментацию исходного текста и отдельный анализ сегментов.

Как интерпретировать проценты

Основное настроение

В threshold-режиме основной результат содержит два набора процентов:

  • raw_probs — исходные softmax-score модели, которые обычно суммируются до 100%;
  • probs — итоговые threshold-adjusted проценты после учёта чувствительности классов; они также нормируются до суммы 100%.

В argmax-режиме пороговая коррекция не применяется, поэтому probs совпадает с raw_probs.

Негативное: 10%
Нейтральное: 10%
Позитивное: 80%

Если показаны исходные значения raw_probs, правильная интерпретация будет такой:

Модель оценила весь переданный вход как позитивный; исходный score позитивного класса равен 80%.

Неправильная интерпретация:

80% слов позитивные, 10% слов негативные и 10% слов нейтральные.

Модель не подсчитывает долю позитивных или негативных слов. raw_probs являются исходными оценками для всей входной последовательности, а probs в threshold-режиме являются прозрачным представлением итогового решения после порогов.

Итоговые threshold-adjusted проценты не являются новыми вероятностями нейросети. Это нормированные decision score, используемые для выбора класса.

Суб-настроения

Каждое суб-настроение рассчитывается независимо через sigmoid:

радость: 75%
восхищение: 68%
оптимизм: 66%

Их сумма может быть больше 100%. Это нормально: один текст одновременно может выражать несколько эмоций.

Деструктивные классы

Деструктивные классы также независимы:

деструктивность: 92% — Да
угроза: 81% — Да
оскорбление: 14% — Нет

Да означает, что score класса достиг индивидуального порога. Нет означает, что score ниже порога.

Что означает confidence

confidence — итоговый процент выбранного класса:

  • для threshold это значение из threshold-adjusted probs;
  • для argmax это исходное значение из raw_probs.

raw_confidence всегда содержит исходный softmax-score выбранного класса.

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

Argmax и threshold

Параметр --main-mode определяет, как из трёх основных классов выбирается один итоговый.

Argmax

--main-mode argmax

Выбирается класс с максимальным исходным процентом:

Негативное: 40%
Нейтральное: 35%
Позитивное: 25%

Итог:

Негативное — 40%

Threshold

--main-mode threshold

Сначала для каждого класса вычисляется decision score:

decision_score = raw_probability / threshold

Затем три decision score нормируются до 100%:

adjusted_probability = decision_score / sum(decision_scores) × 100

Пример с текущими условными значениями:

Класс Raw score Порог Decision score Итоговый процент
Негативное 40% 50% 0.40 / 0.50 = 0.80 33.96%
Нейтральное 35% 35% 0.35 / 0.35 = 1.00 42.45%
Позитивное 25% 45% 0.25 / 0.45 = 0.56 23.58%

Максимальный decision score у нейтрального класса, поэтому итог будет таким:

Нейтральное
Итоговая confidence: 42.45%
Исходная raw_confidence: 35%

Здесь 1.00 — не вероятность и не 100% уверенности. Это отношение исходного score к порогу. Пользовательский итоговый процент 42.45% уже нормирован вместе с коэффициентами остальных классов.

Проверка примера 30% / 20% / 50%

Если исходные значения равны positive=30%, negative=20%, neutrally=50%, текущие пороги дадут:

Класс Raw score Decision score Итоговый процент
Негативное 20% 0.40 16.03%
Нейтральное 50% 1.4286 57.25%
Позитивное 30% 0.6667 26.72%

В этом конкретном примере выигрывает нейтральный класс, а не позитивный.

Пример, где более чувствительный позитивный класс обгоняет немного больший raw-score негативного:

Класс Raw score Decision score Итоговый процент
Негативное 42% 0.84 37.24%
Нейтральное 20% 0.5714 25.33%
Позитивное 38% 0.8444 37.43%

Raw-score негативного выше: 42% > 38%. Но после учёта порогов итоговый positive равен 37.43%, а negative — 37.24%, поэтому прозрачно выбирается позитивный класс.

Для основного настроения всегда выбирается ровно один класс, даже если все три значения находятся ниже своих порогов.

По умолчанию v1.1.1 использует threshold. Режим можно переопределить:

python "model/Moodix v1.1.1_28.06.2026/moodix.py" \
  --mode standard \
  --main-mode argmax \
  --text "Текст для анализа"

Режим standard

standard предназначен для общей оценки одного документа без вывода границ смысловых фрагментов.

Алгоритм

  1. Текст разделяется по пустым строкам на абзацы.
  2. Абзац до 300 символов передаётся как одна часть.
  3. Абзац длиннее 300 символов группируется по предложениям в части приблизительно до 300 символов.
  4. Все части анализируются пакетно.
  5. Вероятности частей усредняются с весом по количеству использованных токенов.
  6. Итоговый main-класс выбирается единым правилом threshold или argmax.

Вес по токенам означает, что часть из 40 использованных токенов влияет на итог сильнее, чем часть из 5 токенов.

Условный CLI-вывод

Основное настроение: Позитивное
Итоговая оценка: 92.89% (threshold_adjusted, режим: threshold)
Исходный score выбранного класса: 93.5%

Итоговые threshold-adjusted проценты:
  Негативное: 2.77%
  Нейтральное: 4.34%
  Позитивное: 92.89%

Исходные softmax-score модели:
  Негативное: 3.1%
  Нейтральное: 3.4%
  Позитивное: 93.5%

Пороговые коэффициенты probability / threshold:
  Негативное: 0.062 (порог 50%)
  Нейтральное: 0.0971 (порог 35%)
  Позитивное: 2.0778 (порог 45%)

Вероятности всех суб-настроений:
  восхищение: 67.7% — Да
  волнение: 18.2% — Нет
  вдохновение: 12.4% — Нет
  радость: 75.6% — Да
  любовь: 20.1% — Нет
  оптимизм: 66.2% — Да
  любопытство: 3.1% — Нет
  информативность: 8.4% — Нет
  осознание: 5.2% — Нет
  гнев: 1.2% — Нет
  раздражение: 4.3% — Нет
  разочарование: 6.8% — Нет
  отвращение: 1.1% — Нет
  страх: 0.8% — Нет
  грусть: 2.4% — Нет

Топ-3 суб-настроения: радость, восхищение, оптимизм

Вероятности всех деструктивных классов:
  деструктивность: 0.8% — Нет
  экстремизм: 0.0% — Нет
  угроза: 0.1% — Нет
  ненависть: 0.0% — Нет
  непристойность: 0.3% — Нет
  оскорбление: 0.2% — Нет

Значения в этом примере условные. Реальный результат зависит от текста и весов модели.

Топ-3 является дополнительной сводкой. Он не скрывает остальные классы и не определяет статус Да/Нет.

Режим fragments

fragments предназначен для ответа на два разных вопроса:

  1. Какое настроение имеет документ в целом?
  2. Какие части исходного текста получили отдельные оценки?

Правила выделения частей

Исходный текст разделяется:

  • по ., !, ?, ;
  • по переводу строки;
  • по ;;
  • после запятой перед но, однако, зато, хотя;
  • по тире, окружённому пробелами;
  • дополнительно по словам, если часть превышает 50 токенов.

Границы рассчитываются до нормализации. Поэтому start, end и поле text относятся к исходной строке пользователя, а не к очищенному тексту с urltoken и другими служебными токенами.

Каждая часть классифицируется самостоятельно. Затем результаты частей агрегируются по токенам так же, как в standard.

Условный пример

Вход:

Сервис отличный, но приложение зависает.

Условный вывод:

Основное настроение: Негативное
Итоговая оценка: 58.7% (threshold_adjusted, режим: threshold)
Исходный score выбранного класса: 55.0%

Выделенные фрагменты:

  [0:16] Позитивное (итог 93.02%; raw 93.1%; -34.2 п.п.)
  Сервис отличный,

  Итоговые threshold-adjusted проценты:
    Негативное: 4.41%
    Нейтральное: 2.57%
    Позитивное: 93.02%

  Исходные softmax-score модели:
    Негативное: 4.9%
    Нейтральное: 2.0%
    Позитивное: 93.1%

* [17:40] Негативное (итог 77.42%; raw 82.4%; +51.4 п.п.)
  но приложение зависает.

  Итоговые threshold-adjusted проценты:
    Негативное: 77.42%
    Нейтральное: 18.93%
    Позитивное: 3.65%

  Исходные softmax-score модели:
    Негативное: 82.4%
    Нейтральное: 14.1%
    Позитивное: 3.5%

* — фрагмент поддерживает итоговое основное настроение

После каждого фрагмента реальный CLI также выводит все 15 суб-настроений и все 6 деструктивных классов с процентами и статусами порогов.

Что означают start и end

Запись:

[0:16]

содержит два индекса:

  • 0start, индекс первого символа фрагмента;
  • 16end, индекс позиции сразу после последнего символа фрагмента.

Индексация начинается с нуля. Первый символ строки имеет индекс 0, второй — 1 и так далее.

Рассмотрим исходную строку:

text = "Сервис отличный, но приложение зависает."

Разметка символов:

Часть строки Индексы символов
Сервис 0–5
пробел после Сервис 6
отличный 7–14
запятая 15
пробел после запятой 16
но 17–18
пробел после но 19
приложение 20–29
пробел 30
зависает 31–38
точка 39

Первый фрагмент имеет границы:

start = 0
end = 16

В Python правая граница среза не включается. Поэтому:

text[0:16]

берёт символы с индексами от 0 до 15 включительно и возвращает:

Сервис отличный,

Символ с индексом 16 — пробел после запятой. Он не входит ни в текст первого фрагмента, ни в начало второго фрагмента:

text[16]       # " "
text[17:40]    # "но приложение зависает."

Для второго фрагмента:

start = 17
end = 40

Это означает:

  • первый включённый символ находится на позиции 17 — буква н в слове но;
  • последний включённый символ находится на позиции 39 — точка;
  • позиция 40 является правой невключаемой границей;
  • длина фрагмента равна end - start, то есть 40 - 17 = 23 символа.

Поле text в JSON дублирует найденную подстроку:

{
  "start": 17,
  "end": 40,
  "text": "но приложение зависает."
}

Практическая проверка корректности границ:

assert original_text[start:end] == fragment["text"]

Важная деталь Unicode

Offsets рассчитываются как индексы Python str, а не как номера байтов UTF-8.

Для обычного русского текста один символ обычно соответствует одной позиции. Составные эмодзи могут содержать несколько Unicode code points. JavaScript использует UTF-16 code units, поэтому при подсветке текста с некоторыми эмодзи его индексы могут отличаться от Python. В такой интеграции безопасно дополнительно сверять поле text или преобразовывать индексы на стороне клиента.

Что означает вклад фрагмента

Поле:

{
  "impact_on_overall_pp": 51.4
}

показывает изменение итогового probs основного класса при исключении фрагмента из агрегирования. В threshold-режиме используются именно нормированные threshold-adjusted проценты, а не raw softmax-score.

Упрощённая формула:

impact = score итогового класса со всеми частями
       − score итогового класса без текущей части

п.п. означает процентные пункты, а не проценты относительного изменения.

Пример:

Итоговый adjusted score со всеми фрагментами: 58.7%
Итоговый adjusted score без фрагмента:         7.3%
Impact:                                        +51.4 п.п.

Положительный impact означает, что фрагмент усиливает итоговый класс. Отрицательный impact означает, что фрагмент ему противодействует.

Флаг:

{
  "supports_overall": true
}

устанавливается, когда основной класс фрагмента совпадает с итоговым классом документа и вклад положительный. Для единственного фрагмента вклад не вычисляется, но совпадающий фрагмент считается поддерживающим итог.

Impact — это объяснение на уровне сегментов текущего агрегатора. Это не token-level attribution и не доказательство того, что конкретное слово было причинной основой решения BiLSTM.

Локальный API и пакетная обработка

Режим api не открывает порт, не создаёт сервер и не требует FastAPI. Он предназначен для автоматической обработки JSON и для прямого вызова из Python.

Формат входного файла

batch.json:

{
  "texts": [
    "Спасибо за помощь, сервис отличный.",
    "Приложение зависает и ужасно раздражает.",
    "Опубликована новая версия документа."
  ],
  "analysis_mode": "standard",
  "batch_size": 128
}
Поле Обязательно Описание
texts да массив исходных строк
analysis_mode нет standard или fragments
batch_size нет внутренний размер шага Keras

Допускается сокращённый вход в виде массива:

[
  "Первый текст",
  "Второй текст"
]

В этом случае analysis_mode и batch_size берутся из CLI-параметров.

Файл → файл

python "model/Moodix v1.1.1_28.06.2026/moodix.py" \
  --mode api \
  --input-file batch.json \
  --output-file result.json \
  --batch-analysis-mode standard \
  --batch-size 256

stdin → stdout

python "model/Moodix v1.1.1_28.06.2026/moodix.py" \
  --mode api \
  --batch-analysis-mode fragments \
  --batch-size 128 \
  < batch.json > result.json

В режиме api служебные сообщения не печатаются в stdout, поэтому stdout можно напрямую сохранять как JSON.

Приоритет настроек

Если analysis_mode или batch_size указаны внутри JSON, они имеют приоритет над CLI-параметрами.

Пример:

python moodix.py --mode api --batch-analysis-mode fragments --batch-size 256

но вход содержит:

{
  "texts": ["Текст"],
  "analysis_mode": "standard",
  "batch_size": 32
}

Фактически будут использованы standard и batch_size=32.

Что именно означает batch_size

batch_size — не максимальное количество документов в запросе.

Это количество подготовленных последовательностей, которые Keras обрабатывает за один внутренний шаг. Один документ может создать несколько последовательностей:

  • в standard — несколько частей длинного документа;
  • в fragments — несколько предложений или оборотов.

Пример:

10 документов
по 3 фрагмента в каждом
= 30 последовательностей для модели

При batch_size=8 Keras обработает их внутренними шагами 8 + 8 + 8 + 6, сохранив один общий вызов model.predict.

Ограничения пакета

CLI-параметр По умолчанию Назначение
--batch-size 256 внутренний размер шага Keras
--max-batch-items 256 максимальное число исходных документов
--max-text-length 100000 максимальное число Python-символов в одном документе
--max-batch-characters 1000000 максимальная сумма длин всех документов

Порядок результатов

Порядок сохраняется:

texts[0] → results[0]
texts[1] → results[1]
texts[2] → results[2]

Если текст невозможно токенизировать, соответствующий элемент results равен null, а его индекс добавляется в failed_indices.

Python API

Если пользовательский скрипт находится вне каталога модели, каталог можно добавить в sys.path:

from pathlib import Path
import sys

model_dir = Path("model/Moodix v1.1.1_28.06.2026").resolve()
sys.path.insert(0, str(model_dir))

from moodix import MoodixBatchAPI, build_analyzer

analyzer = build_analyzer(model_dir)
analyzer.load_model()

api = MoodixBatchAPI(
    analyzer,
    default_batch_size=128,
    max_batch_items=256,
)

response = api.analyze(
    [
        "Спасибо за помощь.",
        "Приложение постоянно зависает.",
    ],
    analysis_mode="fragments",
)

MoodixBatchAPI загруженную модель не копирует. Следует создать один SentimentAnalyzer, один раз вызвать load_model() и повторно использовать объект API.

Структура JSON-ответа

Верхний уровень batch-ответа

Поле Тип Значение
analysis_mode string standard или fragments
main_mode string threshold или argmax
count integer количество исходных документов
failed_indices array индексы документов с результатом null
batch_size integer фактически использованный внутренний batch size
elapsed_ms number полное время обработки пакета в миллисекундах
texts_per_second number/null приблизительная пропускная способность пакета
inference_units integer число частей или фрагментов, переданных модели
results array результаты в порядке входного массива

elapsed_ms включает нормализацию, токенизацию, сегментацию, инференс и сбор результата. Это не только чистое время нейросети.

Результат standard

Основные поля:

{
  "main": {
    "label": "positive",
    "label_ru": "Позитивное",
    "confidence": 92.89,
    "raw_confidence": 93.5,
    "probs": {
      "negative": 2.77,
      "neutrally": 4.34,
      "positive": 92.89
    },
    "raw_probs": {
      "negative": 3.1,
      "neutrally": 3.4,
      "positive": 93.5
    },
    "decision": {
      "mode": "threshold",
      "score_type": "threshold_adjusted",
      "thresholds": {
        "negative": 50.0,
        "neutrally": 35.0,
        "positive": 45.0
      },
      "scores": {
        "negative": 0.062,
        "neutrally": 0.0971,
        "positive": 2.0778
      }
    }
  },
  "sub": {
    "admiration": 67.7,
    "excitement": 18.2,
    "inspiration": 12.4,
    "joy": 75.6,
    "love": 20.1,
    "optimism": 66.2,
    "curiosity": 3.1,
    "informative": 8.4,
    "realization": 5.2,
    "anger": 1.2,
    "annoyance": 4.3,
    "disappointment": 6.8,
    "disgust": 1.1,
    "fear": 0.8,
    "sadness": 2.4
  },
  "sub_flags": {
    "admiration": true,
    "joy": true,
    "optimism": true
  },
  "top3_sub": ["joy", "admiration", "optimism"],
  "destructive": {
    "probs": {
      "destructive": 0.8,
      "extremist": 0.0,
      "threat": 0.1,
      "hate": 0.0,
      "obscene": 0.3,
      "insult": 0.2
    },
    "flags": {
      "destructive": false,
      "extremist": false,
      "threat": false,
      "hate": false,
      "obscene": false,
      "insult": false
    }
  },
  "meta": {
    "fragment_count": 1,
    "used_token_count": 6
  }
}

Значения полей main:

Поле Смысл
label выбранный основной класс
confidence итоговый процент выбранного класса из probs
raw_confidence исходный softmax-score выбранного класса
probs итоговые threshold-adjusted проценты; в argmax совпадают с raw
raw_probs исходный softmax-выход модели
decision.thresholds применённые пороги в процентах; null в argmax
decision.scores ненормированные коэффициенты raw_probability / threshold

Обратная совместимость: раньше main.probs и main.confidence содержали raw-score. Теперь для исходных значений клиенты должны использовать main.raw_probs и main.raw_confidence.

В реальном sub_flags присутствуют все 15 ключей. В примере выше часть неактивных ключей опущена только ради компактности описания JSON.

Результат fragments

{
  "overall": {
    "main": {
      "label": "negative"
    }
  },
  "fragments": [
    {
      "index": 0,
      "start": 0,
      "end": 16,
      "text": "Сервис отличный,",
      "main": {
        "label": "positive",
        "confidence": 93.02,
        "raw_confidence": 93.1,
        "probs": {
          "negative": 4.41,
          "neutrally": 2.57,
          "positive": 93.02
        },
        "raw_probs": {
          "negative": 4.9,
          "neutrally": 2.0,
          "positive": 93.1
        }
      },
      "impact_on_overall_pp": -34.2,
      "supports_overall": false,
      "sub": {
        "probs": {},
        "flags": {},
        "active": []
      },
      "destructive": {
        "probs": {},
        "flags": {},
        "active": []
      }
    }
  ],
  "evidence": {
    "main": {
      "label": "negative",
      "fragments": []
    },
    "sub": {}
  }
}

Пустые объекты в сокращённом примере обозначают места, где реальный ответ содержит все вероятности и флаги.

Конфигурация

label_config.json

{
  "main": ["negative", "neutrally", "positive"],
  "sub": ["admiration", "excitement", "..."],
  "destructive": ["destructive", "extremist", "..."],
  "max_seq_length": 50,
  "preprocessing_version": 2,
  "main_decision_mode": "threshold",
  "truncating": "post"
}
Поле Значение
main, sub, destructive порядок выходных классов модели
max_seq_length максимальная длина входа в токенах
preprocessing_version версия нормализации текста
main_decision_mode режим выбора основного класса по умолчанию
truncating=post при обрезании сохраняются первые 50 токенов

Порядок классов нельзя менять без согласования с порядком выходов обученной модели.

thresholds.json

Актуальные пороги:

Группа Класс Порог
main negative 50%
main neutrally 35%
main positive 45%
sub admiration 65%
sub excitement 55%
sub inspiration 45%
sub joy 60%
sub love 60%
sub optimism 55%
sub curiosity 80%
sub informative 40%
sub realization 55%
sub anger 55%
sub annoyance 50%
sub disappointment 55%
sub disgust 55%
sub fear 55%
sub sadness 60%
destructive destructive 40%
destructive extremist 65%
destructive threat 75%
destructive hate 30%
destructive obscene 50%
destructive insult 50%

Для sub/destructive флаг активен при условии:

probability >= threshold

Например, для joy:

joy = 61%, threshold = 60% → Да
joy = 59%, threshold = 60% → Нет

Ограничения текущей реализации

  1. raw_probs являются softmax-score модели, а не долями слов и не гарантированно откалиброванными вероятностями реального мира.
  2. Threshold-adjusted probs являются нормированными коэффициентами принятия решения, а не новым выходом нейросети и не калиброванной вероятностью реального мира.
  3. standard может обрезать конец части, если она содержит больше 50 токенов, но не была предварительно разделена. При truncating=post сохраняются первые 50 токенов.
  4. fragments предотвращает такое обрезание, дополнительно разделяя длинные части до лимита модели.
  5. Fragment-анализ оценивает сегменты отдельно. Настроение сегмента вне контекста иногда отличается от его смысла внутри полного предложения.
  6. Impact рассчитывается из агрегированных итоговых probs и не является градиентным объяснением отдельных слов.
  7. Offsets являются индексами Python Unicode-строки, а не UTF-8 байтами.
  8. Пустая строка отклоняется. Непустой текст без известных токенов возвращает null в batch-результате и его индекс в failed_indices.
  9. top3_sub всегда является рейтингом трёх наибольших score. Класс из top-3 не обязательно пересёк свой порог.
  10. Основная и суб-головы независимы, поэтому между ними теоретически возможны семантически противоречивые сочетания.

Benchmark

В репозитории находится стенд RuSocialSentiment Benchmark для сравнения основного настроения по macro-F1 и accuracy.

Модель Тип
Moodix v0.66 локальный Keras, argmax, preprocessing v1
Moodix v1.1.1 локальный Keras, threshold, preprocessing v2
Qwen Flash 3.5 внешний API через OpenRouter

Команда запуска:

pip install -r benchmarks/requirements-benchmark.txt
python -m benchmarks.rusocialsent \
  --suites manual rusentitweet \
  --models moodix_0_66 moodix_1_1_1_threshold

Подробное описание корпусов и метрик: benchmarks/rusocialsent/README.md.

Сводная иллюстрация:

RuSocialSentiment Benchmark

Тестирование

Запуск актуальных тестов:

pytest -q

Тесты проверяют:

  • сохранение порядка документов;
  • один общий batch-конвейер для standard;
  • объединение фрагментов разных документов в fragments;
  • ограничения локального API;
  • корректную JSON-сериализацию;
  • чтение JSON-файла и чистый JSON в stdout.

Все CLI-параметры

python "model/Moodix v1.1.1_28.06.2026/moodix.py" --help

Основные параметры:

Параметр Назначение
--mode standard общая оценка текста
--mode fragments оценка с фрагментами и offsets
--mode api локальная пакетная обработка JSON
--main-mode threshold выбор main-класса с учётом порогов
--main-mode argmax выбор максимального исходного score
--text одноразовый текст вместо интерактивного ввода
--show-model-summary вывести архитектуру Keras для CLI-режимов
--batch-analysis-mode обработка batch как standard или fragments
--batch-size внутренний размер шага Keras
--input-file входной JSON-файл
--output-file выходной JSON-файл

About

Moodix — локальный модуль анализа русскоязычного текста, определяющий основное настроение (позитивное, нейтральное, негативное), 15 суб-настроений и 6 деструктивных признаков (угроза, ненависть, экстремизм и др.). Основан на BiLSTM-модели и работает без доступа к интернету. Подходит для интеграции в CRM, e-commerce, модерации и аналитики.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages