Всем привет! Давно хотел написать публикацию в которой были бы перечислены самые распространённые варианты датасетов для обучения языковых моделей.
Про то как правильно собирать датасеты для языковых моделей меня спрашивают не то чтобы часто, но каждый раз приходится достаточно подробно расписывать что да как. Поэтому я решил собрать все свои мысли на этот счёт в одну небольшую публикацию и в дальнейшем просто ссылаться на неё.
Кстати, также рекомендую ознакомиться с моей публикацией про систему Label Studio, там я рассказывал про то как упростить процедуру создания и разметки датасетов.
Введение
В наш век информационных технологий и искусственного интеллекта, обучение больших языковых моделей (Large Language Model, LLM) стоит на переднем крае инноваций. Эти модели играют ключевую роль во многих современных приложениях, таких как чат-боты, системы перевода и аналитические инструменты. Однако, несмотря на их потенциал, создание эффективной языковой модели - это не простая задача. Одним из наиболее важных и сложных этапов является сборка хорошего датасета. Ведь датасет - это та "пища", которую модель "кушает" во время обучения, и его качество напрямую влияет на результаты работы модели.
В зависимости от специфики задачи, которую решает языковая модель, а также от целей обучения, требуются специализированные датасеты, составленные в различных форматах. Без качественных данных даже самая продвинутая модель может показать неудовлетворительные результаты.
В этой статье я предлагаю рассмотреть основные виды датасетов, которые используются в этой области, чтобы дать вам лучшее понимание того, какие данные и в каком формате могут потребоваться для ваших проектов.
Но прежде чем мы продолжим тему датасетов поговорим немного про токенизацию текста.
Токенизация в языковых моделях
Токенизация является одним из основных этапов предварительной обработки текста в области обработки естественного языка (NLP). Прежде чем погружаться в детали и методы токенизации, давайте разберемся, что это такое и для чего она нужна.
Токенизация - это процесс разбиения текста на более мелкие единицы, называемые токенами, представляют из себя они условно идентификаторы каких-то последовательностей символов в словаре. Представьте себе таблицу в реляционной базе данных, в этой таблице у вас имеются строки, у каждой строки есть какой-то уникальный ID, который позволяет легко выполнять поиск строки. Так вот токены это условно и есть эти самые идентификаторы.
В большинстве случаев токенами являются слова, но они также могут представлять собой предложения, абзацы или даже символы, в зависимости от контекста и задачи.
Зачем нужна токенизация?
- Упрощение текста
Разбив текст на токены, мы преобразуем сложные текстовые данные в структурированный и упорядоченный формат, который машине будет легче анализировать и обрабатывать. - Подготовка к анализу
Многие методы и алгоритмы в NLP работают на уровне токенов, а не на уровне полного текста. Токенизированный текст позволяет моделям эффективно определять и анализировать отношения между словами и другими элементами текста. - Устранение шума
Токенизация может помочь отфильтровать ненужную информацию или "шум" из текста, такой как знаки препинания, числа или специальные символы. - Построение словаря
После токенизации можно создать словарь уникальных слов или токенов, который может быть использован для представления или кодирования текста в числовом формате, пригодном для анализа моделями.
По сути своей вся работа большинства генеративных языковых моделей сводится к вычислению коэффициента вероятности появления того или иного токена (идентификатора) после предыдущего.
Про специальные токены
Помимо токенов, несущих непосредственную семантическую или лексическую информацию (например, слова или символы), существуют и специальные системные токены. Эти токены играют важную роль в предобработке, обучении и функционировании языковых моделей, обеспечивая дополнительный уровень структурирования и контекстуализации данных.
Beginning Of Sentence (bos)
Токен, который обозначает начало предложения или последовательности. Особенно полезен в задачах, где необходимо знать начало вводимого текста, например, в машинном переводе или при создании кодогенератора и так далее.
Для обучения моделей семейства ruGPT или LLaMA используется <s>.
End Of Sentence (eos)
Токен, который обозначает конец предложения или последовательности. Помогает модели определить, где завершается вводимый текст.
Для обучения моделей семейства ruGPT или LLaMA используется </s>.
Padding (pad)
Токен заполнения используется для унификации длины последовательностей при обучении модели. Если последовательности в батче имеют разную длину, их можно дополнить токенами pad до одинаковой длины.
Не используется для обучения моделей семейства ruGPT или LLaMA.
Unknown (unk)
Токен, который обозначает неизвестное или редко встречающееся слово, которое не входит в словарь токенизатора модели.
Для обучения моделей семейства ruGPT или LLaMA используется <unk>.
Separator (sep)
Токен-разделитель, который часто используется для разделения различных частей текста либо же разделяет несколько вариантов ответа на один и тот же запрос пользователя, обычно используется в моделях, которые обрабатывают несколько предложений одновременно.
Не используется для обучения моделей семейства ruGPT или LLaMA.
Какие бывают датасеты?
Качество данных и формат в котором составлен датасет напрямую влияет на то какую языковую модель мы получим после обучения, а также на то как хорошо модель будет отвечать на запросы пользователя. Разберём самые распространённые форматы датасетов.
Тестовые корпусы (Text Corpora)
Сырые данные, или как их ещё называют текстовые корпусы, представляют из себя большую коллекцию каких-то разношерстных текстовых данных, например это могут быть тексты статей из Википедии, тексты книг, посты на Reddit, публикации сообществ VK, какие-то скравленные блоги, рецепты блюд и так далее слитые воедино.
Пример такого датасета представляет из себя просто набор строк текста:
Вечером на улице зажглись фонари, освещая дорогу для прохожих.
Книги, разложенные на столе, были полны историй про далекие страны и приключения.
Музыка из кафе напротив звучала мягко, унося в дали воспоминания о прошлом лете.
Дети смеялись, играя в парке, и их радостные крики наполняли воздух.
Старый город сохранял в себе тайны веков, каждый уголок рассказывал свою историю.
Обычно подобные сырые датасеты используют на первом этапе обучения модели, это необходимо для того чтобы научить генеративную модель грамотно продолжать текст или выполнять генерацию в свободном стиле.
Для того, чтобы подобный датасет стал чуть более пригоден для обучения языковых моделей по типу ruGPT или LLaMA в него желательно добавить bos и eos токены, обычно используют <s> и </s> для обозначения границ текстового блока, поэтому обновим пример датасета используя указанный формат данных:
<s>Вечером на улице зажглись фонари, освещая дорогу для прохожих.</s>
<s>Книги, разложенные на столе, были полны историй про далекие страны и приключения.</s>
<s>Музыка из кафе напротив звучала мягко, унося в дали воспоминания о прошлом лете.</s>
<s>Дети смеялись, играя в парке, и их радостные крики наполняли воздух.</s>
<s>Старый город сохранял в себе тайны веков, каждый уголок рассказывал свою историю.</s>
Хочу также заметить, что применение специальных токенов является необязательным, но подобную методику желательно применять для того чтобы сократить длину ответа модели, если же специальных токенов не будет то модель может например продолжать текст бесконечно (обычно такие модели называю storywriter), что потребует дополнительных манипуляций в дальнейшем.
Ещё одна любопытная особенность это дообучение (pretrain) на текстовом корпусе, чтобы она генерировала текст в определённом стиле. Например если дообучить модель rugpt3large на корпусе из текстов книг Фёдора Михайловича Достоевского, то она научится генерировать текст в стиле книг упомянутого автора. Об это я рассказывал в публикации Обучение ИИ модели ruGPT-3 в домашних условиях ранее. То есть мы можем взять оригинальную модель rugpt3large (или любую другую генеративную модель) и заставить её генерировать не просто текст, а текст в нужном стиле.
Такой подход возможно будет любопытен исследователям занимающимся разработкой чат-ботов мимикрирующих под героев из произведений художественной литературы.
Датасет инструкций (Instructional Dataset)
Датасеты, содержащие команды или инструкции, следуя которым модель должна выполнять определенные действия или давать ответы. Часто подобные датасеты называют датасетами для обучения с учителем (Supervised Learning Datasets). Сбор таких датасетов требует чёткой формулировки задачи и понимания того что именно вы хотите получить на выходе.
Подобные датасеты обычно используются на втором этапе дообучения модели, иными словами сначала мы обучаем модель на большом текстовом корпусе, а далее при помощи inscruct датасета выполняем тонкую настройку (fine-tuning) для того чтобы модель не просто генерировала какой-то текст, а давала осмысленные ответы на запросы пользователя.
Модели обученные на instruct датасете обычно называют instruct-моделями и в названии они обычно содержат слово instruct.
Классический instruct датасет обычно представляет из себя множество пар запрос-ответ, например:
<s>[INST]
Сколько дней в году?
[/INST]
В обычном году 365 дней. Однако каждые четыре года происходит високосный год, в котором 366 дней. В високосном году февраль имеет 29 дней вместо обычных 28.</s>
Или вот так:
<s>[INST]
Покажи уравнение расчёта последовательности Фибоначчи.
[/INST]
Последовательность Фибоначчи определяется следующим образом:
```latext
F(n) =
\begin{cases}
0 & \text{если } n = 0 \
1 & \text{если } n = 1 \
F(n-1) + F(n-2) & \text{если } n > 1
\end{cases}
```
Это рекурсивное определение, где каждый следующий элемент последовательности равен сумме двух предыдущих. Начальные значения определены как `F(0) = 0` и `F(1) = 1`.</s>
Вот ещё пример:
<s>[INST]
Напиши пример функции выводящей Hello, world! в консоль на языке C++.
[/INST]
Вот простой пример функции на C++, которая выводит "Hello, world!" в консоль:
```cpp
#include <iostream>
int main() {
std::cout << "Hello, world!" << std::endl;
return 0;
}
```
Вы можете скомпилировать и выполнить этот код, используя компилятор C++, чтобы увидеть сообщение "Hello, world!" в консоли.</s>
Крайне рекомендую при составлении инструкционных датасетов выполнять преобразование математических формул в формат LaTeX, а все примеры исходного кода представлять в формате блока кода Markdown, да и вообще считать что все ответы модели должны быть в формате Markdown, тем самым вы сохраните себе много времени и нервов в дальнейшем.
Лично мне удобнее всего под каждую инструкцию создавать отдельный Markdown файл в нем описывать запрос пользователя и желаемый ответ языковой модели, однако, многие предпочитают собирать такие датасеты например в формат JSONL (это многострочный JSON) или например в CSV. В прочем формат того как данные хранятся на файловой системе или базе данных не имеет особого значения, важна не форма, а содержание.
Запрос к модели обученной на подобном датасете может выглядеть следующим образом:
[INST]
{prompt}
[/INST]
Где prompt это переменная в которую поменяется запрос пользователя, ещё нужно оставлять две пустые строки после запроса пользователя - это место где система должна будет писать свой ответ. Модель вернёт текст начиная с этого блока и этот ответ уже передаётся пользователю.
Диалоговый датасет (Dialogue Dataset)
Датасеты, содержащие диалоги между двумя или более участниками. Они обычно используются для обучения чат-ботов или систем ответов на вопросы. Как правило на таких датасетах дообучают модель предварительно обученную на текстовых корпусах, а затем и на датасетах инструкций, то есть будущая чат-бот модель уже умеет адекватно реагировать на запросы пользователя и выполнять необходимые задачи.
Модели обученные на chat датасете обычно называют chat-моделями и в названии они обычно содержат слово chat.
Давайте рассмотрим простой пример диалога между языковой моделью (assistant) и пользователем (user).
System: Ты - мультимодальная нейросетевая модель от Сбера с 13 миллиардами параметров, умеешь отвечать на фактологические вопросы, поддерживать диалог, писать тексты разных форматов и стилей, создавать изображения по запросу. Твоя задача быть внимательным к пользователи и точно отвечать на его вопросы. Дата твоего последнего обучения была в конце февраля 2023 года.
User: Привет! Как тебя зовут?
Assistant: Приветствую! Я мультимодальная нейросетевая модель от Сбера с 13 миллиардами параметров.
User: Расскажи когда в последний раз проводилось твоё обучение?
Assistant: Обучение проводилось в конце февраля 2023 года.
User: Какие задачи ты умеешь решать?
Assistant: Я умею отвечать на фактологические вопросы, поддерживать диалог, писать тексты разных форматов и стилей, создавать изображения по запросу.
Для удобства датасеты в типа чаты обычно хранят в JSON формате следующего вида:
{
"messages":
{
"role": <enum:(system|user|assistant)>,
"content": <string>
}
...
}
Например такой формат применяется для дообучения модели ChatGPT-3.5, о чём я рассказывал в моей публикации Обучение ChatGPT-3.5 или сказ про то как научить старого бота новым шуткам, но его можно использовать и для обучения любой другой модели. Давайте попробуем преобразовать чат из примера выше в указанный формат:
{
"messages": [
{
"role": "system",
"content": "Ты - мультимодальная нейросетевая модель от Сбера с 13 миллиардами параметров, умеешь отвечать на фактологические вопросы, поддерживать диалог, писать тексты разных форматов и стилей, создавать изображения по запросу. Твоя задача быть внимательным к пользователи и точно отвечать на его вопросы. Дата твоего последнего обучения была в конце февраля 2023 года.",
{
"role": "user",
"content": "Привет! Как тебя зовут?"
},
{
"role": "assistant",
"content": "Приветствую! Я мультимодальная нейросетевая модель от Сбера с 13 миллиардами параметров."
},
{
"role": "user",
"content": "Расскажи когда в последний раз проводилось твоё обучение?"
},
{
"role": "assistant",
"content": "Обучение проводилось в конце февраля 2023 года."
},
{
"role": "user",
"content": "Какие задачи ты умеешь решать?"
},
{
"role": "assistant",
"content": "Я умею отвечать на фактологические вопросы, поддерживать диалог, писать тексты разных форматов и стилей, создавать изображения по запросу."
}
]
}
Однако, если планируется дообучать скажем модель ruGPT-3.5 или LLaMA-2 датасет всё равно придётся предварительно преобразовывать добавляя системные токены, например вот так:
<s>System: Ты - мультимодальная нейросетевая модель от Сбера с 13 миллиардами параметров, умеешь отвечать на фактологические вопросы, поддерживать диалог, писать тексты разных форматов и стилей, создавать изображения по запросу. Твоя задача быть внимательным к пользователи и точно отвечать на его вопросы. Дата твоего последнего обучения была в конце февраля 2023 года.
User: Привет! Как тебя зовут?
Assistant: Приветствую! Я мультимодальная нейросетевая модель от Сбера с 13 миллиардами параметров.
User: Расскажи когда в последний раз проводилось твоё обучение?
Assistant: Обучение проводилось в конце февраля 2023 года.
User: Какие задачи ты умеешь решать?
Assistant: Я умею отвечать на фактологические вопросы, поддерживать диалог, писать тексты разных форматов и стилей, создавать изображения по запросу.</s>
Но в дальнейшем в коде процессинга потребуется выполнять обрезку сообщений специальным образом, например можно выполнять обрезку сгенерированного ответа представив, что токен bos это "\nAssistant: ", а eos это "\nUser: ", но обрезку результата выполнять придётся при помощи регулярных выражений.
Запрос к модели обученной на подобном датасете может выглядеть следующим образом:
System: {system}
User: {prompt}
Assistant:
Где:
- system - это прогревочная инструкция для модели, обычно в ней описывается ожидаемое поведение модели и какие-то технические детали. типа сегодняшней даты, когда проводилось обучение и так далее.
- prompt - это запрос пользователя
А после "Assistant: " система должна генерировать свой ответ.
Параллельный корпус (Parallel Corpora)
Датасеты, содержащие пары предложений на разных языках, обычно используются для обучения моделей машинного перевода. Пример датасета выполняющего перевод текста с английского на русский:
Source: Hello, world!
Target: Привет, мир!
Помимо перевода с одного языка на другой можно таким образом обучить специализированную модель которая бы выполняла преобразование исходного кода с одного языка программирования на другой и насколько мне известно о подобных моделях западные светочи высоких технологий ещё не додумались, можете стать первым (если у вас это получится. не забудьте упомянуть и меня :)
Для удобства пары параллельного корпуса обычно хранят в формате JSON:
Существует большое количество готовых к использованию датасетов OPUS которые представляют из себя коллекцию технической документации к различным OpenSource проектам на разных языках, возможно вам это пригодится, ну и конечно же шерстите датасеты на HuggingFace с фильтром по Translations.
Ещё один способ создания таких корпусов данных это экспорт словарей и переведённых проектов например из системы Crowdin или SmartCat (но тут вам нужен профессиональный переводчик с профилем).
Запрос к модели обученной на подобном датасете может выглядеть следующим образом:
Source: {prompt}
Target:
Где prompt это запрос пользователя, а после "Target: " система должна генерировать свой ответ.
Аннотированный датасет (Annotated Dataset)
Текстовые данные с разметкой, например, для задач извлечения информации, распознавания именованных сущностей или определения части речи. Обычно используется для обучения моделей типа классификаторы, извлечение данных из текста, анализ предложений и так далее.
В аннотированном датасете каждый элемент данных (например, слово или фраза) сопровождается меткой, указывающей на определенную характеристику этого элемента. Эти метки позволяют машине "понимать", как интерпретировать данный элемент в контексте задачи, которую требуется решить.
- Классификация текста (Text Classification)
При обучении на подобном датасете модель обучается относить текст к одному или нескольким предопределенным категориям или классам на основе его содержания. Часто используется для обучения моделей для фильтрации спама (spam/ham), моделей анализа тональности текста (positive/negative/neutral), категоризации новостей (tech/social/hype...), определения языка у текста (fr/ru/de/en) и так далее.
Кстати, ранее я написал публикацию про классификацию токсичных сообщений, рекомендую ознакомиться.
- Распознавание/извлечение именованных сущностей (NER, Named Entity Recognition)
Определение и классификация имен, мест, организаций и других сущностей в тексте.
Например: "Барак Обама был 44-м президентом США." -> "Барак Обама" (Персона), "США" (Географическое местоположение). - Определение части речи (POS-tagging, Part of Speech tagging)
Каждому слову в предложении присваивается метка, указывающая на его часть речи.
Например: "Яблоко упало." -> "Яблоко" (существительное), "упало" (глагол).
Для создания аннотированного датасета требуется значительное количество времени и человеческих ресурсов, так как необходимо точно и корректно разметить большое количество данных. Однако, после создания такого датасета, он может быть использован для обучения множества моделей, что делает этот процесс высокоценным для исследований и разработок в области машинного обучения.
Среды для обучения с подкреплением (Reinforcement Learning Environments)
Обучение с подкреплением (Reinforcement Learning, RL) - это область машинного обучения, где агент обучается взаимодействовать с окружением для достижения определенной цели, на основе обратной связи в виде награды или штрафа.
Подобные системы обычно состоят из следующих сущностей:
- Агент (Agent)
Сущность, которая принимает решения на основе данных, полученных от окружения. В контексте NLP это может быть языковая модель, которая обучается генерировать текст или отвечать на вопросы. - Окружение (Environment)
Мир, с которым взаимодействует агент. Окружение дает агенту информацию о текущем состоянии и изменяется в ответ на действия агента. - Действие (Action)
Шаг или решение, принимаемое агентом на основе текущего состояния окружения. - Награда (Reward)
Обратная связь, которую агент получает после выполнения действия. Награда может быть положительной (если действие было правильным) или отрицательной (если действие было неправильным).
Работают подобные решения следующими образом:
- Агент наблюдает текущее состояние окружения;
- На основе наблюдений и своей стратегии агент выбирает действие;
- Окружение реагирует на действие, изменяя свое состояние;
- Окружение предоставляет агенту обратную связь в виде награды или штрафа;
- Агент обновляет свою стратегию на основе полученной обратной связи.
Хотя обучение с подкреплением традиционно ассоциируется с задачами, такими как управление роботами или игры, оно также может быть применено в NLP. Например для оптимизации диалоговых систем или генерации более корректного и точного текста.
Пример элемента датасета, который получает модель в процессе RL обучения:
Action: "Translate the sentence ti Russian:\nHello, World!"
Response: "Привет, Мир!"
Reward: +1
Награды или штрафы направляют по шине в агент модели, после чего агент вносит соответствующие корректировки в свои веса.
В реальном мире чаще всего используется парадигма обучение с подкреплением на основе отзывов людей (Reinforcement learning from human feedback, RLHF), она предполагает что обратная связь приходит не от какой-то другой нейросети, а от реального человека.
Кстати, я тут подумал, возможно это и есть самая главная причина по которой ChatGPT-4 так отупела за последние пол года год? Просто люди чаще давали +1 за плохие ответы или -1 за хорошие, и агент RLHF принимает решение, что веса надо портить соответствующим образом. Это что получается, кто-то изобрёл новый вид атаки на нейросети, RLHF attack?
Датасеты кода (Code Datasets)
Датасеты, содержащие примеры кода и комментарии к ним. Они могут быть использованы для обучения моделей, которые помогают в написании кода. По сути своей они практически ничем не отличаются от датасетов текстовых корпусов, только в отличии от обычных текстов датасеты кода содержат исходный код на разных языках программирования.
Модели обученные на instruct датасете обычно называют code моделями и в названии они обычно содержат слово code.
Выглядить подобный датасет может следующим образом:
<s>Language: python
User: Нафипиши функцию рассчёта последовательности Фибоначчи.
Assistant: def fibonacci_recursive(n):
if n == 0:
return 0 elif n == 1:
return 1 else:
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)</s>
Запрос к модели обученной на подобном датасете может выглядеть следующим образом:
Language: {language}
User: {prompt}
Assistant:
Где:
- language - это язык программирования о котором пользователь попросил информацию (контекст)
- prompt - это запрос пользователя
А после "Assistant: " система должна генерировать свой ответ.
Заключение
От правильного подхода к сбору, аннотации и обработке данных во многом зависит качество и эффективность последующих моделей. В данной статье мы рассмотрели различные типы датасетов, их особенности и применение, а также коснулись важных аспектов обработки текста, таких как токенизация и использование системных токенов. Эти знания помогут вам создать качественную основу для разработки и обучения ваших собственных языковых моделей.
Помимо теоретических аспектов, практический опыт играет ключевую роль. Экспериментируйте, адаптируйте методы под свои задачи и постоянно совершенствуйтесь. В мире искусственного интеллекта постоянное обучение - это не только прерогатива машин, но и наша с вами.
Не забудьте поставить лайк, подписаться на канал, а также приглашаю ко мне на Telegram-канал на котором я публикую свои размышления, рассказываю о разных интересных новостях, а также делюсь своими наработками и опытом.
К тому же, если вы хотите поддержать мои усилия и вклад в развитие общества знаний, вы можете сделать пожертвование на CloudTips. Ваша поддержка поможет мне продолжать свою работу и делиться новыми открытиями с вами.
До встречи в следующей публикации!