Приветствую! Существует устоявшееся мнение о том, что нейросеть - это прежде всего датасет, на котором происходит её обучение. Следовательно, чтобы обучить нейросеть способную имитировать некоего определённого персонажа необходимо иметь датасет, представляющий из себя чат с этим персонажем.
Данная публикация является первой частью описания проекта над которым я работал, и в ней мы поговорим о том как при помощи больших языковых моделей (Large Language Models, LLM) можно создавать синтетические датасеты типа чат для обучения моделей чат-ботов имитаторов виртуальных персонажей.
Героем текущей работы стал человек по имени Иван Яковлевич Яковлев, в своё время он был выдающимся историческим деятелем, педагогом и автором современного чувашского алфавита, за подробностями перенаправляю вас в Яндекс поиск, ну а мы продолжим.
Публикация построена следующим образом: в первой половине мы с вами поговорим о мотивации, затем проанализируем некоторые существующие реализации и смежные решения, а под занавес рассмотрим ту реализацию, что сделал я.
Кстати, ранее в публикации "Про датасеты для обучения языковых ИИ моделей" я рассказывал про основные виды различных датасетов, возможно вам будут интересны мои размышления на эту тему.
Ну а тем кому не терпится изучить исходники вот ссылка на проект: character-ai
Мотивация
Некоторое время назад мне предложили поучаствовать в проекте по разработке чат-бота способного отвечать на вопросы по определённой тематике имитируя при этом общение с Иваном Яковлевичем Яковлевым.
Один из вариантов реализации должен быть работать поверх решения на базе дообученой ruGPT-3.5 о которой я некоторое время назад рассказывал на Хабре и чуть менее подробно на Дзене.
Если в двух словах то план был следующий:
- Взять модель ruGPT-3.5-13B дообученную мною на датасетах Saiga (rulm) (так как она неплохо отвечает на вопросы на грамотном русском языке)
- Собрать датасет на основе публикаций и статей об Иване Яковлевиче
- Дообучить модель на этом датасете ещё немного
- ...
- Profit
С процедурой дообучения и моделью всё более-менее понятно, но где взять такой специализированный датасет, да ещё и в формате чата с Иваном Яковлевичем? Никто ранее не делал ничего подобного, так что пришлось пораскинуть мозгами и собрать его самостоятельно.
Опыта в решении подобных задач у меня до этого не было, были лишь поверхностное представления о том как это происходит, поэтому перед тем как начинать работу над таким датасетом мне потребовалось изучить аналогичные проекты, а также решения на их основе.
Аналитический обзор
В данной главе я вскользь пройду по проектам, которые натолкнули меня на идею создания проектов имитационных нейросетей.
Character AI
Проприетарный проект на базе ChatGPT, в нём можно настроить системный промпт в котором предложить несколько примеров текста иммитируемого персонажа.
Это могут быть твиты или какие-то стенограммы докладов, короче любой текст отражающий характер. В результате получаются эдакие плюшевые упрощённые персонажи-иммитаторы с которыми можно общаться при помощи текстовых сообщений, ограниченны они лишь цензурой OpenAI.
Проект интересен по большей части только тем, как выглядит фронтенд, на низах там судя по всему используется API клиент для работы с ChatGPT и возможно LangChain. Система передаёт запросы на серверы OpenAI и возвращает стилизованные под персонажа ответы, короче любопытная игрушка.
TavernAI
Self-hosted проект с открытыми исходными кодами, который позволяет подключить любой бэкенд, настроить характер персонажа для общения, и общаться с ним в формате новеллы... Звучит странно, выглядит ещё страннее.
Из любопытных моментов, существует проприетарное решение на базе данного проекта, основное его назначение это генерация NFSW контента, но я, как человек приличный, это осуждаю ;)
AI Character Editor
Любопытный OpenSource проект, основная его задача в генерации конфигураций для таких проектов как Pygmalion, Text Generation Web UI, TavernAI.
После загрузки конфигурации можно будет выполнить настройку, чуть точнее настроить параметры персонажа, добавить примеров чатов и так далее.
rulm
Данный проект был создан коллективом русскоговорящих разработчиков и является коллекцией скриптов, а также конфигурационных файлов для обучения русскоязычных моделей семейства Saiga.
Примечателен он в первую очередь тем, что в нем уже реализованы скрипты и примеры промтов для генерации диалогов с виртуальными персонажами. Осуществляется генерация при помощи ChatGPT, но само-собой вы можете использовать любую другую языковую модель запустив её скажем при помощи LocalAI.
Подготовка виртуального окружения
Для работы нам потребуется создать пустую директорию, назовём её скажем character-ai, затем перейдём в неё и выполним инициализацию виртуального окружения Python.
mkdir character-ai
python3.11 -m venv venv
source venv/bin/activate
Затем нам потребуется установить ряд библиотек:
pip install openai==1.3.8 ruamel.yaml==0.18.5 jinja2==3.1.2
Далее создадим папку characters, в ней мы будем складывать файлы персонажей, сгенерированные темы для обсуждения с персонажем, а также сгенерированные тематические чаты, а так же папку instructs, в ней будут храниться шаблоны промтов для работы с моделью.
mkdir {characters,instructs}
На этом шаге у нас всё готово для последующей работы.
Подготавливаем персонажей
Ну чтож давайте приступим собственно к реализации. Первым делом нам нужно описать всех персонажей для которых мы будем генерировать диалоги. Вот список минимального набора информации о каждом персонаже:
- name - имя персонажа
- greeting (приветствие) - пример того как персонаж будет здороваться с пользователем
- context (контекст) - публичная информация о персонаже, скажем статья на википедии или небольшой отрывок из автобиографии. Тут один очень важный момент, chatgpt-3.5-turbo-16k ограничена размером контекста в 16 тысяч токенов, поэтому постарайтесь писать в этом поле не слишком много информации о персонаже
- example_dialogue - пример диалога с персонажем, который представляет из себя массив из объектов, содержащих в себе название роли (name) и текстовое сообщение (user)
Лично мне на этапе подготовки подобных отдельных персонажей удобнее всего использовать формат YAML, так как данный формат поддерживает множество удобных функции таких как: встраиваемые шаблоны, текст на несколько строк, комментарии в тексте, ссылки и простейшие логические операции. Подробнее о спецификациях YAML можно почитать тут.
Все YAML файлы мы будем хранить в ранее созданной папке characters.
Генерация тем для обсуждения
Прежде чем генерировать чаты нам понадобится на основании описания персонажа сгенерировать список тем (topics) для чатов. Идея следующая:
- Некая языковая модель, скажем ChatGPT или GigaChat, или YandexGPT и т.д., изучает всю доступную информацию о персонаже (из поля context)
- На основании полученной информации она пытается придумать список тем, которые пользователь будущей модели потенциально может обсуждать с этой моделью
- Система должна вернуть извлечённые потенциальные темы в специальном формате, таким образом, чтобы мы могли автоматизировать процедуру их извлечения
В качестве основы для решения подобной задачи я решил использовать скрипт generate_char_topics.py из проекта rulm, однако, внёс в него ряд корректировок. В частности мне не нравилось, что он может работать только с JSON и JSONL, а мне кажется, что YAML гораздо более удобный формат для хранения подобного рода данных.
Помимо этого оригинальный скрипт не имел возможности генерировать темы диалога исключая те, что уже были сгенерированы ранее при предыдущем запуске, он лишь заменял их.
Поэтому я сделал свою версию generate_char_topics.py лишённую перечисленных недостатков. Для его запуска вам потребуется токен для работы с ChatGPT 3.5 через OpenAI API, а также мой скрипт и несколько библиотек.
Сначала создадим шаблон промта, который будет заставлять модель генерироватить список тем из описания персонажа, для этого в папке instructs создадим файл ru_char_topics.txt.
touch ru_char_topics.txt
Добавим в него следующее содержание:
{{char_json}}
Предложи 10 тем для обсуждения с этим персонажем, которые затрагивают различные аспекты описанные в поле context, касающиеся биографии, места и времени в которое этот персонаж жил. Исключай темы, которые уже имеются в поле topics.
Выведи их под номерами на отдельных строках.
Теперь из корня запустим скрипт генерации тем:
OPENAI_TOKEN="<TOKEN>" python generate_char_topics.py
Вместо <TOKEN> нужно будет подставить ваш OpenAI API токен.
В результате выполнения указанного скрипта в YML файлы персонажей будет добавлено поле topics с перечислением всех тем, которые система сгенерировала на основании контекста.
По умолчанию генерируется всего 10 тем, это сказано в шаблоне промта ru_char_topics.txt, но можно при желании увеличить это количество.
Помимо этого, если в YML файле персонажа поле topics уже существует, то повторный запуск скрипта прочтёт предыдущие темы и сгенерирует дополнительные темы, которые не будут совпадают с теми что уже есть. Это позволяет генерировать темы небольшими партиями и чуть более аккуратно управлять процессом генерации.
Само-собой после генерации вы можете руками подкорректировать получившийся список, добавить свои темы или удалить лишние.
Генерация чатов
Итак, у нас готова базовая информация о персонаже, есть контекст в котором генератор будет обсуждать с пользователем темы, а также имеется список тем для обсуждения, поэтому мы можем перейти к этапу генерации чатов по созданным темам.
В качестве основы для моего генератора чатов я взял из проекта rulm скрипт generate_char_chats.py, он также как и генератор тем не умеет работать YAML, помимо этого мне оригинальный генератор не нравился своей переусложнённостью (фатальные недостатки #meme ага:), в общем поэтому я создал свою упрощённую версию generate_char_chats.py, с поддержкой YAML.
Помимо скрипта понадобится создать в папке instructs файл ru_char_chat.txt следующего вида:
Представь, что ты искусственный интеллект имитирующий персонажа, описанного вот таким JSON:
{{char_json}}
Сгенерируй диалог между пользователем и этим искусственным интеллектом, старайся чтобы бот отвечал коротко и по делу, пусть диалог выглядит так будто искусственный интеллект считает себя персонажем, которого он имитирует и ответы даёт от первого лица.
Тема диалога: {{topic}}
Верни список реплик в виде списка в формате JSON, пригодном для загрузки через json.loads. Обязательно используй те же поля и роли, что и в example_dialogue это очень важно.
Теперь из корня запустим скрипт генерации чатов по темам:
OPENAI_TOKEN="<TOKEN>" python generate_char_chats.py
В результате работы скрипта в YAML файлы персонажей будет добавлено поле dialogues, в нём будет содержаться массив из объектов описывающих чаты.
Количество сгенерированных чатов будет равно количеству тем из поля topics, но если запустить скрипт генерации чатов повторно, то новые чаты будут добавленны в массив dialogues. Соответственно можно будет несколько раз запустить указанный скрипт и получить несколько разных вариантов чата на одни и те же темы.
Ну и конечно же можно будет добавить чаты, скорректировать результат или удалить лишее вручную.
Генерация датасета совместимого с форматом чатов rulm
Далее мы преобразуем данный YAML файл в формат JSONL (многострочный JSON).
Для этого я реализовал простой метод, под названием convert_yaml_to_jsonl, полный код скрипта можно посмотреть тут.
Предполагается, что скрипт convert_yml_to_jsonl.py лежит на одном уровне с директорией characters.
На этапе вызова метода (последняя строка), можно обратить внимание на то что мы передаём в функцию путь до папки в которой хранятся файлы с расширением .yml, а вторым параметром путь до .jsonl в котором преобразованные данные будут сохранены.
В результате работы скрипта в корне проекта получится файл characters.jsonl, данный файл будет содержать все сгенерированные ранее чаты, темы и информацию обо всех персонажах из папки characters.
Данный файл будет полностью совместим по своему формату с датасетом IlyaGusev/gpt_roleplay_realm, поэтому его можно будет без особых проблем встроить в цепочку тренировки вашей модели.
Вот небольшой пример кода, демонстрирующий то как встроить датасет со сгенерированными чатами в скрипт create_chat_set.py или create_short_chat_set.py
Полный пример вы можете найти в файле create_custom_chat_set.py, в нём нет ничего кроме генератора датасета из файла characters.jsonl, совместимого со скриптами тренировки Saiga
Запустим этот скрипт вот так:
python create_custom_chat_set.py test.jsonl val.jsonl
По завершению работы скрипта у нас получатся два файла: test.jsonl и val.jsonl. Заглянем внутрь test.jsonl:
И в нём мы увидем несколько строк, обозначающих отдельные чаты, первым сообщением будет идти сообщение роли system, это так называемое прогревочное сообщение, которое будет содержать контекст последующего чата. Далее уже будут чередоваться сообщения от бота и от пользователя.
Завершение
Вот в принципе и всё, что я хотел рассказать про то как создать датасет для обучения модели иммитатора персонажей. В следующей публикации покажу как встроить указанный датасет в цепочку тренировки модели, а после чего используя обученную модель создать полноценного чат-бота с базой знаний поиск ответов по которой происходит при помощи миниатюного RAG (без СУБД) и крохотной модели извлекающей эмбеддинги.
Не забудьте поставить лайк, подписаться на канал, а также приглашаю ко мне на Telegram-канал на котором я публикую свои размышления, рассказываю о разных интересных новостях, а также делюсь своими наработками и опытом.
К тому же, если вы хотите поддержать мои усилия то можете сделать пожертвование на CloudTips. Ваша поддержка поможет мне продолжать свою работу и делиться новыми открытиями с вами.
До встречи в следующей публикации!