Зачем вообще нужна LLM классификация? Для примера – хрестоматийная задача из суровой реальности:
- На вход – история переписки пользователя с ботом
- На выход True, если пользователь делает в чате что-то нехорошее (например, пытается вытащить системный промпт или разговорить модель про ее полические взгляды). False, если все хорошо
Вот 4 типичных подхода и один нетипичный
Уровень 0 – просто промпт:
Блаблабла, напиши True, если пользователь делает что-то плохое. Вот критерии плохого: ... Напиши False, если все ок
Проблемы: Модель может ответить "false", "FALSE", "false, потому что блаблабла" или вообще выдать системный промпт, если инъекция хорошая
Уровень 1 – structured output:
class Response(BaseModel):
verdict: bool = Field(..., description="Блаблабла, напиши True, если пользователь делает что-то плохое. Вот критерии плохого: ... Напиши False, если все ок")
Уже лучше. Но что если хочется еще и знать что конкретно идет не так?
Уровень 2 – Literal (multiclass классификация):
Вместо бинарного поля – несколько возможных вариантов
class Response(BaseModel):
verdict: Literal["normal", "politics_bait", "system_prompt_robbery", ...]
А что если у нас одновременно и politics_bait и system_prompt_robbery???
Уровень 3 – Несколько полей (mutlilabel классификация)
Разделяем варианты по разным бинарным критериям (aka чеклист)
class Response(BaseModel):
is_politic_bait: bool
is_system_prompt_robbery: bool
🧐 Вместо бинарных полей можно тоже сделать Literal с несколькими значениями – будет multiclass multilabel. Пример – извлечение данных из вакансий – одно поле может быть Junior/Middle/Senior, второе удаленка/офис/гибрид, третье – основной рабочий язык
Коля, ну а тут то что не так?
Дело в том, что триггер True для
is_system_prompt_robbery сработает только, если вероятность будет больше чем False . Давайте представим себе такие вероятности:P(True) = 0.49
P(False) = 0.51
Система скажет False ("все ок"). А на самом деле? А на самом деле уверенность, что все ненормально - 49% 🙈
И большинство решений на рынке остаются именно на этом уровне!
Да, могут добавить thinking или даже Structured Guided Reasoning (например перед вердиктом попросив цитату пользователя, где мб есть проблема), но глобально это ситуацию не меняет.
Пора учиться управлять вероятностями (с) Коля
Уровень 4 – logprobs + пороги:
Мало кто знает, что LLM провайдер может отдавать сырые "вероятности" токенов. Не забываем softmax или попросить модельку, если не шарите.
По сути – это "уверенность" модели в каждом конкретном токене
Включается вот этими параметрами
client.chat.completions.create(
...,
logprobs=True,
top_logprobs=5
)
А потом выбираем пороги. Тут неплохо иметь небольшой датасет, чтобы правильно подобрать их.
Кстати, кто знает, как правильно доставать logprobs только важных для нас токенов (true/false) из всего json'а?
{
"is_politic_bait": true,
"is_system_prompt_robbery": false
}
———
Короче, полноценная классификация появляется тогда, когда появляется вероятность.
Ну и идеи дальнейших улучшений одной строкой:
- Разные пороги под разные характеристики
- Подбор порогов автоматически, если есть датасет
- Если multiclass (пример с вакансиями), то еще важно насколько у топовых классов близкая "уверенность" – если у обоих большая, но примерно одинаковая, то контринтуитивно, но значит, моделька на самом деле сомневается в своем топ-1