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:
- discourse_ai_enabled = true
- ai_sentiment_enabled = true
- 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