Найти в Дзене
Pavel Zloi

Обучение ChatGPT-3.5 или сказ про то как научить старого бота новым шуткам

Всем привет! Вчера очень любопытные новости появились в сети, компания OpenAI (принадлежащая Microsoft) предоставила всем своим платным подписчикам эксклюзивную возможность выполнять дообучение модели gpt-3.5-turbo под свои нужды. Как следствие стартанула волна хайпа, на которую я тоже решил аккуратненько запрыгнуть. В данной публикации разберём оригинальную новость, проанализируем cURL запросы представленные в новости, а также напишем свой небольшой Juputer Notebook решающий задачи претрейна, ну и под конец затронем тему того сколько всё это будет стоить рядовому пользователю. Кому лень читать, вот ссылка на репозиторий. Кстати, хочу выразить огромную благодарность Денису Ширяеву, автору Telegram-канала Denis Sexy IT, за вдохновение и пример описывающий дообучение ChatGPT-3.5 используя датасет RecipeNLG. Без его примера я бы вряд ли собрался с мыслями с целью написания такой большой и развёрнутой статьи. И так, поехали! Разбор новости Тем кому лень изучать подробности описанные в нов
Оглавление

Всем привет! Вчера очень любопытные новости появились в сети, компания OpenAI (принадлежащая Microsoft) предоставила всем своим платным подписчикам эксклюзивную возможность выполнять дообучение модели gpt-3.5-turbo под свои нужды. Как следствие стартанула волна хайпа, на которую я тоже решил аккуратненько запрыгнуть.

Сгенерировано при помощи Kandinsky 2.2
Сгенерировано при помощи Kandinsky 2.2

В данной публикации разберём оригинальную новость, проанализируем cURL запросы представленные в новости, а также напишем свой небольшой Juputer Notebook решающий задачи претрейна, ну и под конец затронем тему того сколько всё это будет стоить рядовому пользователю.

Кому лень читать, вот ссылка на репозиторий.

Кстати, хочу выразить огромную благодарность Денису Ширяеву, автору Telegram-канала Denis Sexy IT, за вдохновение и пример описывающий дообучение ChatGPT-3.5 используя датасет RecipeNLG.
Без его примера я бы вряд ли собрался с мыслями с целью написания такой большой и развёрнутой статьи.

И так, поехали!

Разбор новости

Тем кому лень изучать подробности описанные в новости вот краткая выжимка из неё:

  • Теперь доступно дообучение для GPT-3.5 Turbo, дообучение для GPT-4 будет доступно этой осенью.
  • Дообучение позволит разработчикам настраивать модели таким образом, чтобы они лучше работали для решения конкретных, узкоспециализированных задач.
  • Данные, отправляемые в API дообучения, принадлежат заказчику и не используются OpenAI или другими организациями для обучения других моделей (менеджер из Microsoft, утвердивший эту новость, скорее всего лукаво улыбался в тот момент).
  • Дообучение улучшает управляемость модели, надежность форматирования вывода и кастомизацию тона ответов модели.
  • Благодаря дообучению можно сократить размеры запросов до 90%.
  • OpenAI акцентирует внимание на сохранении безопасности в процессе дообучения (предвижу большое количество слитых приватных датасетов в ближайшем будущем).
  • Все данные для дообучения проходят через систему модерации.

Запуск обучения с помощью cURL

Технически дообучение состоит из пяти простых шагов, все, кроме первого, можно легко реализовать при помощи cURL, разберёмся поподробнее.

1. Подготовка специального JSONL датасета

Разработчики из OpenAI рекомендуют выполнить подготовку датасета таким образом, чтобы он представлял из себя эдакий диалог между пользователем и нейросетью (включая при этом инструкцию для системы):

Пример датасета
Пример датасета

(посмотреть тут)

Полагаю это сделано специально для удобства и для того чтобы закрепить в нейросети системный промпт, описывающий что нейросети нужно сделать и в каком формате отвечать.

Кстати, большой датасет обычно разделяют на две части, скажем 70% - тренировочный набор (train) и 30% - валидационный (val).

2. Загрузка датасета в облако OpenAI

После того, как датасеты были подготовлены их можно выгрузить на серверы OpenAI, для этого необходимо выполнить POST запрос на /v1/files эндпоинт.

Загрузка файлов на сервер
Загрузка файлов на сервер

(посмотреть тут)

В ответе придёт JSON из которого нужно будет выбрать поле id, оно будет указывать на соответствующий файл, выглядеть он будет примерно вот так:

file-someRaNdOmhash

Если у вас два файла (train и val), то надо их оба загрузить и где-то сохранить ID-шники, они пригодятся в дальнейшем.

3. Создание задания для дообучения

Настал самый интересный этап - запуск дообучения, для выполнения этой задачи OpenAI выделит вам небольшой персональный GPU инстанс, всё дообучение будет происходит внутри него. Выполним POST запрос, который запросит у OpenAI ресурсы и начнёт дообучение.

Создание задачи fine-tuning
Создание задачи fine-tuning

Вместо TRAINING_FILE_ID надо будет подставить тот id который вы получили на предыдущем шаге. Если же вы хотите выполнять дообучение используя ещё и валидационный файл, то запрос нужно будет изменить следующим образом:

Создание джоба с тренировочным и валидационным наборами данных
Создание джоба с тренировочным и валидационным наборами данных

(посмотреть тут)

В любом случае, если всё выполнено правильно, API ответит JSON массивом, из которого понадобятся только поля id и status. Первое, как не сложно догадаться это идентификатор джоба, выполняющего дообучение, вот примерно так он будет выглядеть:

ftjob-someRaNdOmhash

А второе - статус этого джоба (скорее всего он будет created).

4. Статус и прогресс дообучения

Процедура дообучения происходит асинхронно и может занимать несколько минут, поэтому понадобится какое-то решение, которое бы помогло нам посмотреть на статус данной задачи.

Специально для этого в OpenAI API предусмотрен эндпоинт GET /v1/fine-tunes/{fine_tune_id}. Вот простенький запрос, который проверяет статус джобы, само собой вместо YOUR_JOB_ID надо подставить айдишник, полученный на предыдущем шаге.

Команда проверки статуса дообучение
Команда проверки статуса дообучение

(посмотреть тут)

В ответе будет JSON описывающий статус и прогресс дообучения. В нём нас прежде всего интересуют такие поля как status, events и fine_tuned_model. Первое, вполне ожидаемо будет отображать статус джоба, второе - события которые произошли, третье - уникальный идентификатор дообученной модели (он появится в случае если обучение завершилось без ошибок).

С полем events чуть интереснее, в нём отображается информация о процессе дообучения (к сожалению отладочных сообщений не видно, только прогресс работы, количество итераций и параметр loss).

Сначала поле status будет иметь значение creating, но после того как окружение для дообучения будет создано статус поменяется на running и в events начнут появляться логи, в случае успешного завершения статус изменится на succeeded.

После того как статус принял значение succeeded можно извлекать значение поля fine_tuned_model и использовать его в качестве названия модели для генерации текста, выглядеть оно будет примерно так:

ft:gpt-3.5-turbo:personal:HaSh

5. Использование дообученной модели

Финальный этап, попытаемся использовать дообученную модель, для этого нам понадобится сформировать POST запрос который будет иметь стандартный формат чата, использованный нами ранее для создания датасета:

Пример запроса для выполнения задач предикшена
Пример запроса для выполнения задач предикшена

В ответе придёт JSON содержащий в себе поле choises с массивом предикшенов из которого уже можно будет извлечь необходимые данные.

Ну вот в принципе и всё.

Пишем Jupyter Notebook

Теперь перейдём на более высокий уровень абстракции и попытаемся при помощи Python и библиотеки openai реализовать тоже самое, что мы делали ранее, но в рамках небольшого Juputer Notebook (кстати, про настройку JupyterLab на GPU сервере у меня имеется соответствующая публикация).

В качестве датасета будем использовать RecipeNLG, это такой особый датасет который содержит в себе большое количество рецептов (2 231 150 если точно), их названия, инструкцию по приготовлению и ингредиенты.

На данном датасете попробуем дообучить модель ChatGPT-3.5 Turbo извлекать ингредиенты из описания рецепта и возвращать нам их виде JSON массива содержащего строки, по итогу у нас получится модель суммаризации данных.

Создадим пустую директорию, назовём её скажем chatgpt-notebooks, в ней создадим пустой notebook под названием gpt-35-finetune.ipynb

mkdir chatgpt-notebooks
cd chatgpt-notebooks
touch gpt-35-finetune.ipynb

Далее, перейдём на сайт RecipeNLG, согласимся с правилами использования датасета, после чего начнётся его загрузка, должен скачаться файл dataset.zip весом примерно 592Мб.

Положим файл dataset.zip на одном уровне с файлом gpt-35-finetune.ipynb и откроем Jupyter Notebook.

Для начала определим всё что нам потребуется для дальнейшей работы, в частности будут необходимы такие библиотеки как pandas (для работы с CSV-файлами, openai для выполнения запросов через OpenAI API).

!pip install openai==0.27.9 pandas==2.0.3

Теперь импортируем эти библиотеки и распакуем ZIP-архив с датасетом (в распакованном виде он весит около 2.1Гб):

Импорт библиотек и распаковка архива датасета
Импорт библиотек и распаковка архива датасета

Дабы не расходовать слишком много ресурсов на сервере OpenAI выберем из полного датасета небольшой кусочек, тысячу, если быть точным. Помните, что чем больше исходных данных отправляется на обучение модели, тем больше времени займёт само обучение и тем дороже (в плане денег) это мероприятие обойдётся.

Подготовка 1к батча
Подготовка 1к батча

Теперь давайте опишем код функции prepare_example_conversation которая будет подготавливать сырые данные из датасета в формат пригодный для обучения модели.

Код функции и пример её использования
Код функции и пример её использования

После чего прогоним через неё весь датасет и по завершению посмотрим, что получилось в первых пяти элементах.

Видно, что преобразование прошло успешно
Видно, что преобразование прошло успешно

Далее разделим наш тысячный датасет на два набора: тренировочный (70%) и валидационный (30%), после чего посчитаем количество элементов и убедимся в том что разделение произошло корректно.

Тут видно что тренировочный датасет имеет размер 700 элементов, а валидационный - 300 (гусары, молчать!)
Тут видно что тренировочный датасет имеет размер 700 элементов, а валидационный - 300 (гусары, молчать!)

Теперь сохраним эти два файла, она понадобятся нам в дальнейшем, когда мы будем выгружать их на файловый сервер OpenAI.

Сохраняем две части тысячного датасета
Сохраняем две части тысячного датасета

Наконец мы добрались до этапа работы с OpenAI API, на самом деле до получения готовой и обученной модели осталось совсем немного. Для начала инициализируем клиент и на простой модели типа davinci проверим, что наше подключение исправно работает.

Инициализация клиента и тестовый запрос
Инициализация клиента и тестовый запрос

Отлично, запросы выполняются без ошибок, поэтому мы можем приступить к следующему этапу, а именно загрузке наборов на сервер.

Загрузка тестового и валидационного наборов
Загрузка тестового и валидационного наборов

Как видно файлы были загружены без ошибок, система вернула нам их идентификаторы и мы можем двигаться дальше.

На следующем шаге потребуется создать джоб дообучения модели gpt-3.5-turbo, для этого сформируем специальный запрос, передадим на вход идентификаторы тренировочного и валидационного датасетов, а также названием модели, которую мы хотим дообучить.

Скорее всего с первого раза у вас не получится создать джоб, всё дело в том самом пункте про модерацию, о котором шла речь в самом начале публикации.

Пока ещё не пущают
Пока ещё не пущают

До того как датасет получит добро от OpenAI может пройти несколько минут, поэтому не переживайте, просто попробуйте повторить этот шаг спустя некоторое время.

Джоб создан
Джоб создан

После этого сохраним в переменную job_id идентификатор данного джоба, он нам понадобится далее.

Проверим статус джоба сразу после создания:

Статус мы получаем передав job_id на вход метода retrieve
Статус мы получаем передав job_id на вход метода retrieve

На данном этапе видно статус у джоба поменялся на running, это означает, что OpenAI начал готовить для нас инстанс.

Проверим список событий:

Статус только с одной записью
Статус только с одной записью

Пока что пусто, возможно инстанс OpenAI ещё не готов и надо подождать ещё некоторое время.

-20

Обучение запустилось спустя несколько минут, можно обратить внимание на то что произошло это не с первого раза, вероятно в тот момент все учебные ноды на стороне OpenAI были заняты. В общей сложности процесс дообучения для меня занял где-то около 40 минут.

После того как последним эвентом станет строка Fine-tuning job successfully completed можно считать что обучение завершилось, а это означает, что можно узнать уникальный идентификатор модели и и использовать его для генерации ответов.

Переменная fine_tuned_model_id более не пустая
Переменная fine_tuned_model_id более не пустая

Обратите внимание на поле trained_tokens, запишите его куда-нибудь, оно понадобится нам в финальной главе, где мы будем говорить про деньги.

Ну и наконец, мы вплотную подобрались до момента, когда модель можно пощупать, для начала составим небольшой тестовый пример, на котором мы посмотрим как вся эта канитель работает.

Вот небольшой тестовый пример, который мы будет отправлять через Inference API:

Тестовый пример того что будет отправлено на сервер
Тестовый пример того что будет отправлено на сервер

Тут видно, что поле NER (содержащее в себе список ингредиентов) - закомментировано, и никак не участвует в процессе генерации, но оно нам пригодится на следующем шаге, для сравнения ответа дообученной ChatGPT и того что написано NER.

Ну и наконец выполним запрос:

Ответ дообученной ChatGPT
Ответ дообученной ChatGPT

В ответе видно, что модель немного ошиблась, она добавила в вывод onion и Ranch dressing, хотя должна была вместо них вернуть Bermuda onion и dressing соответственно.

Полагаю такая небольшая неточность связана с маленьким датасетом, уверен если передать модели на этапе обучения на вход не 1 тысячу рецептов, а скажем 100 тысяч или даже все 2 миллиона, то результат будет ещё точнее.

Но это уже влетит в копеечку, а теперь...

Про деньги

Теперь самое интересное, сколько же всё это распрекрасное добро обойдётся рядовому пользователю и насколько эффективно использовать дообученную ChatGPT для своих хобби проектов?

Для того чтобы максимально точно узнать сколько денег с меня списали OpenAI за обучение ChatGPT на датасете с 1000 элементов, которое к слову продлилось что-то около 40 минут я взять одну пустую учётную запись, которую я использую для разных экспериментов, потому что если мне забанят (по какой-нибудь надуманной причине) основную учётку, то некоторые мои проекты просто остановятся.

Короче, ближе к телу:

Кажется, что не очень много
Кажется, что не очень много

За обучение с меня списали 2 доллара и 77 центов, в принципе не много, но учитывая тот факт, что нейросеть даже на простом тестов запросе выдаёт слегка некорректные данные с большой долей вероятности модель придётся дообучать ещё несколько раз.

Теперь давайте рассчитаем откуда взялась указанная сумма, ранее я просил запомнить количество trained_tokens, так вот там у нас было сказано что 346584 токенов участвовало в дообучении.

Теперь взглянем на цены (указанные в новости):

Цены на дообучение и использование дообученных моделей
Цены на дообучение и использование дообученных моделей
(346584/1000) * 0.008 = 2.772672

Что в целом бьётся с тем, что я вижу на странице Usage, иными словами OpenAI выбрали очень оригинальную модель оплаты, не по времени, не по ресурсам, а по количеству токенов, что очень и очень необычно.

Теперь посмотрим сколько будет стоить обслуживание дообученной ChatGPT-3.5, конекст которой кстати всего 4096 токенов:

Расчёты стоимости обслуживания дообученной ChatGPT-3.5 Turbo с контекстом 4096 токенов
Расчёты стоимости обслуживания дообученной ChatGPT-3.5 Turbo с контекстом 4096 токенов

Сравним с ценами на оригинальную 3.5 (без дообучения) с контекстом 4096 токенов:

Рассчёт цен на оригинальную ChatGPT-3.5 Turbo
Рассчёт цен на оригинальную ChatGPT-3.5 Turbo

Ну и для чистоты сравнения с оригинальная ChatGPT-3.5 Turbo с наибольшим контекстом, равным 16384 токенов:

ChatGPT-3.5 Turbo на 16384 токенов
ChatGPT-3.5 Turbo на 16384 токенов

Как-то не очень выгодно получается, особенно если уже имеется налаженный процесс работы с моделями без дообучения.

Ещё немного про деньги

Процитирую новость:

Early testers have reduced prompt size by up to 90% by fine-tuning instructions into the model itself, speeding up each API call and cutting costs.

То есть каким-то тестерам (наверняка не всем) удалось сократить количество входных данных на 90%, но вот про выходные (output) данные почему-то ничего не сказано, предположу что количество выходных токенов при этом у тестеров не изменилось.

Проведём мысленный эксперимент, допустим у нас есть проект, испльзующий ChatGPT-3.5 Turbo без обучения, который генерирует в час 1M входных токенов и в ответ получает, скажем 10M выходных токенов (обратимся к табличке с ценами gpt-3.5 4k).

1.5 + 20 = 21.5 (далларов в час)

Теперь представим, что мы решили дообучить свою нейросеть, сделаем допущение, что в результате использования дообученной модели удалось сократить количество входных токенов на 90%, то есть их стало не 1M, а 100k, но количество выходных токенов при этом как было 10M так и осталось (обратимся к табличке с ценами дообученной gpt-3.5).

1.2 + 160 = 161.2 (долларов в час)

Стало примерно в 6 раз дороже, выгодно ;)

Кстати, давайте под занавес посчитаем сколько будет стоить обучение модели на полном датасете RecipeNLG, нам известно количество токенов для 1к рецептов (346 584) и известно что всего в датасет более 2х миллинов рецептов (2 231 150) (напомню, что в 1м миллионе 1000 тысяч), предположим что количество токенов будет расти линейно, сделаем расчёт:

346584 * 2231 = 773 228 904 (токенов)

Получилось, что в полном датасете RecipeNLG после подготовки для дообучения получится где-то около 773х миллионов токенов.

Теперь разделим на тысячу и умножим на стоимость обучения на тысяче токенов:

(773228904/1000) * 0.008 = 6185.83 (долларов)

Ну то есть для обучения на полном датасете RecipeNLG понадобится примерно 6 тысяч долларов и неизвестно сколько дней/недель это займёт.

Завершение

Подводя итог скажу, что с моей скромной точки зрения получается, что расходы на обслуживание инфраструктуры на базе дообученной модели в среднем вырастают примерно в 6 раз.

Для компании Microsoft это безусловно радостное событые, а вот для рядовых пользователей и для тех для кого нейросети это хобби, наверно будет не очень весело :) но что поделать, реальность стримительно уносит нас технологическое будущее, наполененное нейросетями.

Ну вот и всё на сегодня, большая статья получилась, даже сам не ожидал, что смогу так много написать и сказать. Надеюсь она вам понравилась, пишите, жмите, подписывайтесь на мой Telegram-канал и до встречи в следующей публикации!

К тому же, если вы хотите поддержать мои усилия и вклад в развитие общества знаний, вы можете сделать пожертвование на CloudTips. Ваша поддержка поможет мне продолжать свою работу и делиться новыми открытиями с вами.