Self-hosted Discourse AI Sentiment: GPU и CPU варианты

Self-hosted Discourse AI Sentiment: GPU и CPU варианты

Настройка анализа тональности и эмоций постов в Discourse AI через self-hosted HuggingFace Text Embeddings Inference (TEI).

Что это

Sentiment в Discourse AI - это не chat/completions LLM. Под капотом две маленькие классификационные RoBERTa (~125M параметров каждая), которые запускаются через HuggingFace TEI. Имена моделей захардкожены в SQL-запросах дашбордов Discourse - менять нельзя.

Источник: Self-Hosting Sentiment and Emotion for DiscourseAI (Falco, Discourse team).

Модель model_name (точно как в коде) Назначение
Sentiment cardiffnlp/twitter-roberta-base-sentiment-latest positive / negative / neutral
Emotion SamLowe/roberta-base-go_emotions 28 эмоций (joy, anger, surprise…)

API-формат: POST {"inputs": "text", "truncate": true} → массив [{"label": "...", "score": 0.95}, ...]

Особенность: модель cardiffnlp не имеет tokenizer.json

TEI требует tokenizer.json, а у cardiffnlp/twitter-roberta-base-sentiment-latest его нет (старый формат: vocab.json + merges.txt). Решение: скачать файлы модели локально и добавить tokenizer.json из SamLowe/roberta-base-go_emotions (тот же RoBERTa-base токенизатор).

Подготовка (одноразово)

sudo mkdir -p /opt/tei-sentiment-cache/model
cd /opt/tei-sentiment-cache/model

for f in config.json vocab.json merges.txt special_tokens_map.json pytorch_model.bin; do
  sudo curl -sL -o "$f"     "https://huggingface.co/cardiffnlp/twitter-roberta-base-sentiment-latest/resolve/main/$f"
done

sudo curl -sL -o tokenizer.json   "https://huggingface.co/SamLowe/roberta-base-go_emotions/resolve/main/tokenizer.json"
sudo curl -sL -o tokenizer_config.json   "https://huggingface.co/SamLowe/roberta-base-go_emotions/resolve/main/tokenizer_config.json"

Вариант A: GPU

Образ: ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3

Стандартный тег :latest:1.9) скомпилирован для compute cap 80 (Ampere) и не работает на Blackwell (RTX 50x0, compute cap 120). Используйте именно cuda-1.9.3

docker pull ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3
sudo mkdir -p /opt/tei-emotion-cache

docker run -d --name tei-sentiment   --gpus all --shm-size 1g   -p 8081:80   -v /opt/tei-sentiment-cache/model:/data/model   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3   --model-id /data/model

docker run -d --name tei-emotion   --gpus all --shm-size 1g   -p 8082:80   -v /opt/tei-emotion-cache:/data   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3   --model-id SamLowe/roberta-base-go_emotions

Первый запуск на Blackwell: JIT-компиляция CUDA-ядер занимает ~5 минут на контейнер. Это одноразово

Производительность GPU (RTX 5060 Ti)

Метрика Значение
Sentiment inference ~14ms
Emotion inference ~60ms
VRAM на контейнер ~428 MB
VRAM на оба ~856 MB

Вариант B: CPU (fallback)

Образ: ghcr.io/huggingface/text-embeddings-inference:cpu-1.9

Подходит если GPU недоступен или VRAM не хватает. Не требует NVIDIA драйверов.

docker pull ghcr.io/huggingface/text-embeddings-inference:cpu-1.9
sudo mkdir -p /opt/tei-emotion-cache

docker run -d --name tei-sentiment   --shm-size 1g   -p 8081:80   -v /opt/tei-sentiment-cache/model:/data/model   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cpu-1.9   --model-id /data/model --dtype float32

docker run -d --name tei-emotion   --shm-size 1g   -p 8082:80   -v /opt/tei-emotion-cache:/data   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cpu-1.9   --model-id SamLowe/roberta-base-go_emotions --dtype float32

При высоком LA можно ограничить CPU:

docker update --cpus=0.1 tei-sentiment tei-emotion

Производительность CPU

Метрика Значение
Sentiment inference ~270ms
Emotion inference ~205ms
RAM на контейнер ~500 MB
С --cpus=0.1 ~2-3s на пост

Переключение GPU ↔ CPU

docker stop tei-sentiment tei-emotion
docker rm tei-sentiment tei-emotion

Затем запустить контейнеры по нужному варианту. Настройки Discourse менять не нужно - endpoint-ы те же.

Проверка

curl -s http://localhost:8081/   -X POST -H 'Content-Type: application/json'   -d '{"inputs": "I am happy"}'

curl -s http://localhost:8082/   -X POST -H 'Content-Type: application/json'   -d '{"inputs": "I am happy"}'

Ожидаемый ответ sentiment: [{"label":"positive","score":0.96},...]

Настройка Discourse

В /admin/plugins/discourse-ai/settings?filter=sentiment:

  1. discourse_ai_enabled = true
  2. ai_sentiment_enabled = true
  3. ai_sentiment_model_configs - два объекта:
Поле model 1 model 2
model_name cardiffnlp/twitter-roberta-base-sentiment-latest SamLowe/roberta-base-go_emotions
endpoint http://<your-host>:8081 http://<your-host>:8082
api_key (пусто) (пусто)

Дашборды

  • /admin/reports/overall_sentiment - общее настроение (positive - negative)
  • /admin/reports/emotion_joy (и другие 27 эмоций)
  • Бэкфилл: ~2500 постов/час, посты не старше 60 дней

Условия и риски

  • Модели обучены на английском. Для русского текста результат approximate, но базовый sentiment работает
  • Endpoint открыт без api_key - для прода закрыть за reverse proxy
  • VRAM мониторинг: nvidia-smi --query-compute-apps=pid,name,used_memory --format=csv,noheader