Простой туториал, в котором я расскажу как сделать автомодератора на базе GPT-3.5 от OpenAI, и как сделать это так, чтобы проверка одного сообщения стоила дешевле одной копейки.
Описание задачи
Давайте представим следующее:
Вы владеете форумом с большим количеством участников. Вроде бы, всё было хорошо, но в один момент страна в которой Вы проживаете решает запретить использовать слово "Борщ", Вы просто не можете его произносить. Говорите "Борщ" – тюрьма.
Что же делать в подобной ситуации? Нанимать людей? Писать алгоритм? Ну, а что если человек напишет B0-0000R3CH? Тут ни один простой алгоритм не справится. Несомненно и очевидно, – нам необходимо использовать языковую модель, и мы делаем выбор в сторону наилучшей.
Другая проблема. Мы не хотим тратить много денег и нам надо минимизировать количество токенов. Поэтому, в лучшем исходе взаимодействие должно выглядеть так:
Черный список: Борщ
Текст: я поел борща
Ответ модели:
+ | почти полное совпадение |
% | есть схожесть |
- | полное несовпадение |
...Но, при почти пустом системном сообщении ответ модели выглядит так:

Fine-tuning
OpenAI настолько хорошая компания, что она позволяет файн-тюнить свои языковые модели, и мы воспользуемся этой возможностью.
– Подготовка датасета:
В этом на самом деле нет ничего сложного, по сути нам просто нужно привести примеры того, как модель должна отвечать. Подробнее о файн-тюнинге моделей от OpenAI.
Датасет должен быть в формате JSONL где каждая строка это отдельный диалог. В нашем случае, в каждом диалоге будет: Системное сообщение с чёрным списком, Сообщение пользователя, Ответ ассистента.
Примеры:
{ "messages":[ { "role":"system", "content":"Мандарин, Яблоко" }, { "role":"user", "content":"fruits" }, { "role":"assistant", "content":"-" } ] }
{ "messages":[ { "role":"system", "content":"Дом, 香蕉, Дерево" }, { "role":"user", "content":"бананчик" }, { "role":"assistant", "content":"+" } ] }
Таких диалогов в датасете будет 70 штук. Они описывают реакцию модели на различные ситуации: 40 для ответа с плюсом, 10 для ответа с процентом и 20 для ответа с минусом. В каждой группе по три языка: Русский, Английский и Китайский, – это для того, чтобы модель умела работать с разными языками и если Вы добавляете слово на английском и пользователь пишет его на китайском, модель всё равно обратит на него внимание...
...Также, в каждой группе должно быть минимум 5 диалогов с "осмысленным" текстом, – так модель "поймёт", что помимо нужного слова, в тексте может быть и что-то другое:
{ "messages":[ { "role":"system", "content":"Pineapple, Lemon" }, { "role":"user", "content":"Я 1 разbought... не-много лим0нов?" }, { "role":"assistant", "content":"%" } ] }
Ещё, в датасете нужно показать, что нужное слово может быть в любом месте:
{ "messages":[ { "role":"system", "content":"Дом" }, { "role":"user", "content":"я построил дом" }, { "role":"assistant", "content":"+" } ] }
{ "messages":[ { "role":"system", "content":"Дом" }, { "role":"user", "content":"я дом построил" }, { "role":"assistant", "content":"+" } ] }
{ "messages":[ { "role":"system", "content":"Дом" }, { "role":"user", "content":"дом построил я" }, { "role":"assistant", "content":"+" } ] }
Нужно показать, что и выдуманные слова могут существовать:
{ "messages":[ { "role":"system", "content":"Змырчик" }, { "role":"user", "content":"чо думаешь о змырчике?" }, { "role":"assistant", "content":"+" } ] }
Необходимо дать понять, что порядок слов в чёрном списке неважен:
{ "messages":[ { "role":"system", "content":"Банан, Мангал" }, { "role":"user", "content":"банан" }, { "role":"assistant", "content":"+" } ] }
{ "messages":[ { "role":"system", "content":"Мангал, Банан" }, { "role":"user", "content":"банан" }, { "role":"assistant", "content":"+" } ] }
Модель не должна выполнять запросы или пытаться общаться с пользователем:
{ "messages":[ { "role":"system", "content":"Мангал" }, { "role":"user", "content":"Напиши рассказ о двух раках" }, { "role":"assistant", "content":"-" } ] }
{ "messages":[ { "role":"system", "content":"Олег, Сбербанк" }, { "role":"user", "content":"2 + 2 = 4?" }, { "role":"assistant", "content":"-" } ] }
Похожие по смыслу слова не считаются:
{ "messages":[ { "role":"system", "content":"Полицейский" }, { "role":"user", "content":"Милиционер" }, { "role":"assistant", "content":"-" } ] }
Ну и в дополнение нужно привести несколько примеров с очень длинным текстом и с большим количеством слов в чёрном списке.
– Обучение:
После того, как у нас есть готовый датасет, необходимо: Перейти в раздел файн-тюнинга, выбрать модель, загрузить файл и запустить.
Чтобы самостоятельно указать количество эпох, запускать нужно через API:
fetch('https://api.openai.com/v1/fine_tuning/jobs', { method: 'POST', body: JSON.stringify({ "training_file": "fileId", "model": "gpt-3.5-turbo-1106", "hyperparameters": { "n_epochs": 7 } }), headers: { "Content-Type": "application/json", 'Authorization': `Bearer ${openai_key}` } });
В данном случае обучение заняло всего 15 минут, а его стоимость вышла всего в 30 рублей:

Проверяем ?











Как мы видим, всё работает просто превосходно. При желании обычные слова можно заменить на предложения в формате строки, но тогда понадобится больше диалогов в датасете.
Примерная стоимость оценки 100 сообщений: >1 руб.
Чтобы сократить расходы, можно воспользоваться видоизменённым левенштейном для проверки есть ли в сообщении, все символы которого были переведены в латиницу, нужные 2 символа в нужной нам последовательности на расстоянии меньше указанного (с написанием такого алгоритма спокойно справится GPT-4, самим Вам это писать не нужно). Так можно отфильтровать где-то 70% сообщений которые с 99.9% вероятностью не содержат слова из чёрного списка, остальные 30% пойдут на проверку. Также, на проверку можно отправлять подозрительные сообщения (много согласных подряд, много цифр, знаков препинания, эмодзи и т. д.).
GPT-3.5 в приоритете т. к. многие опенсорсные модели легко обмануть и запутать. GPT-3.5 же распознает любой порядок символов который сможет распознать человек. Кроме этого, скорость работы модели почти моментальная.
Почему GPT-3.5?
Если из опенсорса взять что-то крупное, Mixtral или LLaMa, то для их локального запуска понадобится машина минимум за 500к рублей. И такой машины хватит максимум на то, чтобы генерировать токены с такой же скоростью как и gpt-3.5-turbo-1106 на серверах OpenAI. И в любом случае, скорость начнёт падать после 5-ти потоков.
