Moodix — локальный модуль анализа русскоязычного текста. Актуальная версия определяет:
- основное настроение:
negative,neutrally,positive; - 15 независимых суб-настроений;
- 6 независимых деструктивных классов;
- фрагменты исходного текста, связанные с найденными настроениями;
- результаты одного текста или пакета текстов.
Данные обрабатываются локально. Режим api в Moodix означает локальный Python/JSON batch API и не запускает HTTP-сервер.
- Состав актуальной сборки
- Установка
- Быстрый запуск
- Как модель обрабатывает текст
- Как интерпретировать проценты
- Argmax и threshold
- Режим standard
- Режим fragments
- Что означают start и end
- Что означает вклад фрагмента
- Локальный API и пакетная обработка
- Структура JSON-ответа
- Конфигурация
- Ограничения текущей реализации
- Benchmark
- Тестирование
Актуальная сборка находится в каталоге:
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 "Сервис отличный, но приложение зависает."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Функция clean_text:
- переводит текст в нижний регистр;
- заменяет URL на
urltoken; - заменяет упоминания вида
@userнаmentiontoken; - заменяет хештег на
hashtagtokenи сохраняет его слово; - преобразует группы эмодзи в эмоциональные токены;
- отдельно кодирует
!,?, повторные!!и??; - удаляет остальные неподдерживаемые символы;
- нормализует повторяющиеся пробелы.
Лемматизация в актуальном moodix.py не выполняется. Модель использует токены, полученные после описанной нормализации.
Keras-токенизатор преобразует слова в целочисленные идентификаторы. Неизвестные слова переходят в специальный OOV-токен.
Максимальная длина одного входа задаётся параметром:
{
"max_seq_length": 50
}То есть один непосредственный вход модели содержит не более 50 токенов.
Упрощённый путь данных:
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 — итоговый процент выбранного класса:
- для
thresholdэто значение из threshold-adjustedprobs; - для
argmaxэто исходное значение изraw_probs.
raw_confidence всегда содержит исходный softmax-score выбранного класса.
Таким образом, клиент может одновременно показать понятный итог и исходные данные модели.
Параметр --main-mode определяет, как из трёх основных классов выбирается один итоговый.
--main-mode argmaxВыбирается класс с максимальным исходным процентом:
Негативное: 40%
Нейтральное: 35%
Позитивное: 25%
Итог:
Негативное — 40%
--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% уже нормирован вместе с коэффициентами остальных классов.
Если исходные значения равны 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 предназначен для общей оценки одного документа без вывода границ смысловых фрагментов.
- Текст разделяется по пустым строкам на абзацы.
- Абзац до 300 символов передаётся как одна часть.
- Абзац длиннее 300 символов группируется по предложениям в части приблизительно до 300 символов.
- Все части анализируются пакетно.
- Вероятности частей усредняются с весом по количеству использованных токенов.
- Итоговый main-класс выбирается единым правилом
thresholdилиargmax.
Вес по токенам означает, что часть из 40 использованных токенов влияет на итог сильнее, чем часть из 5 токенов.
Основное настроение: Позитивное
Итоговая оценка: 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 предназначен для ответа на два разных вопроса:
- Какое настроение имеет документ в целом?
- Какие части исходного текста получили отдельные оценки?
Исходный текст разделяется:
- по
.,!,?,…; - по переводу строки;
- по
;; - после запятой перед
но,однако,зато,хотя; - по тире, окружённому пробелами;
- дополнительно по словам, если часть превышает 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 деструктивных классов с процентами и статусами порогов.
Запись:
[0:16]
содержит два индекса:
0—start, индекс первого символа фрагмента;16—end, индекс позиции сразу после последнего символа фрагмента.
Индексация начинается с нуля. Первый символ строки имеет индекс 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"]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 не открывает порт, не создаёт сервер и не требует 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 256python "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 — не максимальное количество документов в запросе.
Это количество подготовленных последовательностей, которые 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.
Если пользовательский скрипт находится вне каталога модели, каталог можно добавить в 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.
| Поле | Тип | Значение |
|---|---|---|
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 включает нормализацию, токенизацию, сегментацию, инференс и сбор результата. Это не только чистое время нейросети.
Основные поля:
{
"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.
{
"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": {}
}
}Пустые объекты в сокращённом примере обозначают места, где реальный ответ содержит все вероятности и флаги.
{
"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 токенов |
Порядок классов нельзя менять без согласования с порядком выходов обученной модели.
Актуальные пороги:
| Группа | Класс | Порог |
|---|---|---|
| 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% → Нет
raw_probsявляются softmax-score модели, а не долями слов и не гарантированно откалиброванными вероятностями реального мира.- Threshold-adjusted
probsявляются нормированными коэффициентами принятия решения, а не новым выходом нейросети и не калиброванной вероятностью реального мира. standardможет обрезать конец части, если она содержит больше 50 токенов, но не была предварительно разделена. Приtruncating=postсохраняются первые 50 токенов.fragmentsпредотвращает такое обрезание, дополнительно разделяя длинные части до лимита модели.- Fragment-анализ оценивает сегменты отдельно. Настроение сегмента вне контекста иногда отличается от его смысла внутри полного предложения.
- Impact рассчитывается из агрегированных итоговых
probsи не является градиентным объяснением отдельных слов. - Offsets являются индексами Python Unicode-строки, а не UTF-8 байтами.
- Пустая строка отклоняется. Непустой текст без известных токенов возвращает
nullв batch-результате и его индекс вfailed_indices. top3_subвсегда является рейтингом трёх наибольших score. Класс из top-3 не обязательно пересёк свой порог.- Основная и суб-головы независимы, поэтому между ними теоретически возможны семантически противоречивые сочетания.
В репозитории находится стенд 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.
Сводная иллюстрация:
Запуск актуальных тестов:
pytest -qТесты проверяют:
- сохранение порядка документов;
- один общий batch-конвейер для
standard; - объединение фрагментов разных документов в
fragments; - ограничения локального API;
- корректную JSON-сериализацию;
- чтение JSON-файла и чистый JSON в stdout.
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-файл |
