Классифицируем отзывы сотрудников о работодателе с помощью R, rollama и Ollama
Анализ отзывов сотрудников помогает HR-менеджерам выявлять ключевые проблемы в компании, оценивать уровень удовлетворенности персонала и находить точки роста. Использование локальных моделей LLM (Large Language Models) позволяет анализировать данные без отправки конфиденциальной информации во внешние сервисы, что особенно важно при работе с чувствительными данными сотрудников.
📌Почему стоит использовать локальные LLM для анализа отзывов?
- Безопасность данных — отзывы остаются на вашем компьютере, исключая риск утечки информации.
- Быстродействие — модели работают локально, без задержек, связанных с интернет-соединением.
- Гибкость — можно использовать различные модели и настраивать их под свои задачи.
- Экономия — не требуется платить за API-запросы, как при использовании облачных сервисов.
🔧 1. Установка Ollama и rollama
Прежде чем начать, нужно установить два компонента:
1. Ollama — это сервер, на котором запускаются модели LLM.
2. rollama — R-пакет для взаимодействия с Ollama.
Шаг 1: Установите Ollama с официального сайта:
➡ Скачать Ollama
После установки запустите Ollama, и он будет работать в фоновом режиме.
❗️Простая и понятная инструкция для установки.
Шаг 2: Установите пакет rollama в R:
install.packages("remotes") remotes::install_github("JBGruber/rollama")
Шаг 3: Проверьте, работает ли Ollama:
rollama::ping_ollama()
Если всё установлено правильно, вы увидите сообщение:
# ▶ Ollama (v0.5.7) is running at <http://localhost:11434>!
📥 2. Загрузка модели LLM
Перед анализом отзывов нужно загрузить языковую модель. Для этого выполните:
rollama::pull_model("gemma2:2b")
Модель скачивается один раз, после чего готова к работе. Для этого укажем, что именно ее следует использовать:
#так указываем, какую модель использовать options(rollama_model = "gemma2:2b")
Список доступных локально LLM (уже загруженных на ПК):
rollama::list_models() # A tibble: 3 × 11 # name model modified_at size digest parent_model format family families # <chr> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <list> # 1 mistral:lat… mist… 2025-01-29… 4.11e9 f974a… "" gguf llama <chr> # 2 deepseek-r1… deep… 2025-01-29… 4.92e9 28f8f… "" gguf llama <chr> # 3 gemma2:2b gemm… 2025-01-29… 1.63e9 8ccf1… "" gguf gemma2 <chr> # # ℹ 2 more variables: parameter_size <chr>, quantization_level <chr>
📊 3. Классификация отзывов
Мы будем использовать модель для автоматического определения тональности отзывов (позитивный, нейтральный, негативный), ориентируясь на:
Простой пример (zero-shot)
"Компания предоставляет хорошие условия, но с карьерным ростом тут сложно."
Мы можем запросить у модели классификацию:
library(tibble) library(purrr) q <- tribble( ~role, ~content, "system", "Ты определяешь категорию текста. Отвечай только одним словом.", "user", "text: Компания предоставляет хорошие условия, но с карьерным ростом тут сложно.\ncategories: положительный, нейтральный, негативный" )
result <- rollama::query(q, output = "text")
Ожидаемый результат: "нейтральный"
Ответ модели:
> result <- rollama::query(q, output = "text") ── Answer from gemma2:2b ───────────────────────────────────────────────────────────────────── негативный
Zero-shot не всегда дает необходимый результат.
q <- tribble( ~role, ~content, "system", "Ты определяешь категорию текста. Отвечай только одним словом.", "user", "text: Зарплата высокая, коллектив дружный.\ncategories: положительный, нейтральный, негативный", "assistant", "сategory: положительный", "user", "text: Компания предоставляет хорошие условия, но с карьерным ростом тут сложно.\ncategories: положительный, нейтральный, негативный" )
Ожидаемый результат: "нейтральный"
Ответ модели:
> result <- rollama::query(q, output = "text") ── Answer from gemma2:2b ───────────────────────────────────────────────────────────────────── нейтральный
🔍 4. Улучшение точности (few-shot и Chain-of-thought)
Чтобы повысить точность классификации, можно показать модели несколько примеров:
q <- tribble( ~role, ~content, "system", "Ты определяешь категорию текста. Отвечай только одним словом.", "user", "text: Зарплата высокая, коллектив дружный.\ncategories: положительный, нейтральный, негативный", "assistant", "положительный", "user", "text: Работаю тут давно, но перспективы роста нет.\ncategories: положительный, нейтральный, негативный", "assistant", "нейтральный", "user", "text: Постоянные переработки и низкая оплата.\ncategories: положительный, нейтральный, негативный", "assistant", "негативный", "user", "text: Компания предоставляет хорошие условия, но с карьерным ростом тут сложно.\ncategories: положительный, нейтральный, негативный" )
result <- rollama::query(q, output = "text")
Ожидаемый результат: "нейтральный"
Ответ модели:
> result <- rollama::query(q, output = "text") ── Answer from gemma2:2b ───────────────────────────────────────────────────────────────────── нейтральный
Здесь модель учится на примерах, что делает классификацию уже точнее.
Chain-of-thought (CoT), или подсказка по цепочке рассуждений
Этот подход включает в себя как минимум один шаг рассуждения. Структура здесь начинается с подсказки системы, затем появляется подсказка пользователя с текстом для классификации и вопросом для рассуждения.
q_thought <- tribble( ~role, ~content, "system", "Ты определяешь категорию текста. Размышляй перед тем, как дать ответ.", "user", "text: Зарплата высокая, коллектив дружный.\nКакую категорию (положительный, нейтральный или негативный) ты бы назначил? Объясни свой выбор.", "assistant", "Этот отзыв содержит положительные элементы: высокая зарплата и дружный коллектив — оба фактора указывают на удовлетворенность сотрудника. Категория: положительный.", "user", "text: Работаю тут давно, но перспективы роста нет.\nКакую категорию (положительный, нейтральный или негативный) ты бы назначил? Объясни свой выбор.", "assistant", "Этот отзыв содержит как нейтральные, так и негативные аспекты. Хотя работа стабильная ('давно'), отсутствие карьерного роста — минус. Однако в целом эмоциональная окраска отзыва не резкая. Категория: нейтральный.", "user", "text: Постоянные переработки и низкая оплата.\nКакую категорию (положительный, нейтральный или негативный) ты бы назначил? Объясни свой выбор.", "assistant", "Этот отзыв выражает явное недовольство: переработки и низкая оплата — два сильных негативных фактора. Категория: негативный.", "user", "text: Рабочая атмосфера замечательная, руководство всегда поддерживает.\nКакую категорию (положительный, нейтральный или негативный) ты бы назначил? Объясни свой выбор." )
output_thought <- rollama::query(q_thought, output = "text")
Ожидаемый результат: "положительный"
Ответ модели:
> output_thought <- rollama::query(q_thought, output = "text") ── Answer from gemma2:2b ───────────────────────────────────────────────────────────────────── Положительный. **Объяснение:** * **Рабочая атмосфера замечательная**: Это позитивная характеристика, которая указывает на благоприятные условия для работы. * **Руководство всегда поддерживает**: Поддержка руководства – важный фактор для удовлетворенности работника. В целом, отзыв свидетельствует о положительном опыте работы в компании.
🔄 5. Массовая обработка отзывов
Если у вас есть список отзывов, можно обработать их векторизированым способом:
reviews_df <- tibble::tibble( review_id = 1:3, review = c( "Отличная команда и интересные задачи.", "Руководство не ценит сотрудников.", "Работа стабильная, но скучная." ) )
annotated_reviews <- reviews_df %>% mutate( sentiment = rollama::make_query( text = review, prompt = "Категории: положительный, нейтральный, негативный", template = "{prefix}{text}\n{prompt}", system = "Определи категорию текста. Отвечай только одним словом из указанных категорий.", prefix = "Текст отзыва: " ) %>% rollama::query(screen = FALSE, output = "text") )
> annotated_reviews # A tibble: 3 × 3 review_id review sentiment <int> <chr> <chr> 1 1 Отличная команда и интересные задачи. "положительный" 2 2 Руководство не ценит сотрудников. "негативный" 3 3 Работа стабильная, но скучная. "нейтральный"
⚙ 6. Настройка модели
Если модель отвечает слишком длинно или слишком кратко, или вам не нравятся ответы конкретной LLM, можно настроить её поведение или выбрать другую:
options(rollama_config = "Делай ответы краткими, но точными.") #так указываем, какую модель использовать options(rollama_model = "gemma2:2b") #список локальных LLM rollama::list_models() # A tibble: 3 × 11 # name model modified_at size digest parent_model format family families # <chr> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <list> # 1 mistral:lat… mist… 2025-01-29… 4.11e9 f974a… "" gguf llama <chr> # 2 deepseek-r1… deep… 2025-01-29… 4.92e9 28f8f… "" gguf llama <chr> # 3 gemma2:2b gemm… 2025-01-29… 1.63e9 8ccf1… "" gguf gemma2 <chr> # # ℹ 2 more variables: parameter_size <chr>, quantization_level <chr>
✅ Итоги
Использование локальной LLM через Ollama и пакет rollama в R даёт HR-менеджерам мощный инструмент для анализа отзывов сотрудников, обеспечивая безопасность данных и высокую скорость обработки.
Преимущества метода:
✔ Полная конфиденциальность (данные не покидают ваш компьютер).
✔ Высокая скорость обработки без задержек.
✔ Возможность адаптировать модель под задачи HR.
Теперь вы можете быстро анализировать с помощью локальной LLM отзывы сотрудников, находить проблемные зоны и принимать решения на основе данных, сохраня конфиденциальность и соблюдая политику информационной безопасноти компании 🚀
Дополнительная информация
1. Документация к пакету rollama
2. 2024 LLMs/genAI + R roundup
3. https://ercbk.github.io/Data-Science-Notebook/qmd/llms-general.html
4. Ollama + tidychatmodels: Categorizing Text Data in R
Подписывайтесь на канал People Analytics.