Часто в прикладных задачах NLU при создании эмбеддингов приходится использовать уже обученные модели. Но что если вы работаете со специфичными или нестандартными текстами? Какие модели для создания эмбеддингов выбрать и где их взять, а может быть лучше обучить их на своих данных? О том, какую модель из растущего семейства больших языковых моделей выбрать, как их обучать, а также немного о кейсах применения нашумевшей ChatGPT разбираемся в блоге ЛАНИТ под катом.
Наша цель – собрать и структурировать опыт использования языковых эмбеддингов из различных моделей, а также поделиться практическими советами и лайфхаками, которые помогут сократить количество перезапусков обучения и файнтюнинга моделей. И в конце также затронуть горячую тему с ChatGPT.
Структурно статья содержит следующие темы:
простые предобученные модели,
модели с Hugging Face,
дообучение моделей,
обучение с нуля на собственных данных,
использование ChatGPT и подобных моделей для создания датасетов.
Итак, опустим вступление про языковые модели а также будем предполагать, что читатель знаком с такими понятиями, как эмбеддинги.
Как правило, эмбеддинги приходят к нам на помощь в решении таких задач, как:
классификация текстов (бинарная, мультиклассовая, с разным количеством примеров обучения – few shot, zero shot и т.д.);
кластеризация отзывов/реплик/любых текстов;
семантический поиск;
ответы на вопросы (Question Answering);
поиск схожих текстов/предложений.
Это далеко не полный список. Для нас эти задачи объединяет то, что для их решения мы будем использовать эмбеддинги, и потому мы будем рассматривать их одновременно. Независимо от того, какая из задач выше стоит перед нами, хорошие эмбеддинги должны обладать следующими качествами:
отображение семантической близости: эмбеддинги текстов, имеющих схожий смысл или семантику, должны быть близкими друг к другу в векторном пространстве, и соответственно, наоборот: векторные представления разных по смыслу текстов должны располагаться далеко друг от друга;
передача контекстной информации: эмбеддинги должны обладать способностью улавливать контекстуальные взаимосвязи в тексте, например, в предложении: "Я хотел пойти в кино, но пошел на концерт" слова "кино" и "концерт" взаимосвязаны в контексте отказа от похода в пользу концерта, и в хороших эмбеддингах эта взаимосвязь будет учтена.
Если обобщить опыт работы с некоторыми NLP-проектами (Natural language processing, обработка естественного языка), то прежде, чем начать использовать определенные эмбеддинги, стоит выполнить несколько шагов:
сформулировать бизнес-задачу в терминах Machine Learning;
проработать пайплайн системы (на этом же шаге можно верхнеуровнево оценить узкие места, потенциальные точки роста);
разработать систему оценки качества работы.
Эти шаги кажутся тривиальными, но далеко не всегда им уделяется должное внимание. Особенное значение они приобретают, когда пайплайн становится большим и комплексным, и важно помнить, что эмбеддинги являются лишь частью большой системы (например, если речь идёт о поисковой системе, рекомендациях, чат-ботах), качество работы которой зависит ещё от многих факторов.
Допустим, мы прошли все эти шаги и уже хотим наконец-то начать что-то кодить.
Простые предобученные модели
Простыми они названы условно, потому что понятны и запускаются несколькими строчками кода.
Простыми они названы условно, потому что являются предобученными и запускаются несколькими строчками кода, что позволяет не тратя много времени и ресурсов на дообучение, с минимальными усилиями сделать базовое решение (далее бейзлайн), взглянуть на метрики, прикинуть качество, на которое можно рассчитывать. Для таких целей можно воспользоваться следующими энкодерами:
LaBSE (также есть версия на pytorch от Sentence-Transformers);
Laser (к сожалению, в силу понятных причин не нашёл там поддержки русского языка, только белорусский), в некоторых туториалах также используется реализация;
FastText (версия gensim).
Эти модели хочется выделить отдельно по следующим причинам.
При помощи них можно с минимальными трудозатратами создать бейзлайн, прикинуть метрики, сделать MVP.
По многим отзывам эти модели хороши при кодировании небольших текстов (твитов, отзывов, комментариев). Бывали случаи, когда на таких данных они показывали лучшее качество по сравнению с более тяжёлыми моделями.
В отличие от большинства моделей на основе архитектуры Bert (с которыми можно работать при помощи библиотеки transformers), у моделей выше отдельный интерфейс использования (MUSE, LaBSE доступны при помощи Tensorflow; FastText есть в gensim и Laser можно загрузить из github).
После того, как вы определились с базовой моделью и обучили ее на своих данных, следующим шагом может быть файн-тюнинг модели. Файн-тюнинг позволяет модели лучше адаптироваться к конкретной задаче, улучшая её производительность.
Важно помнить, что файн-тюнинг - это лишь одна из частей процесса. Важно также учесть другие аспекты системы, включая предобработку данных и архитектуру модели. Например, вы можете улучшить качество своей модели, улучшив предобработку текста перед подачей его на вход модели.
Лайфхак 1. При разработке своих функций старайтесь делать их максимально универсальными, чтобы можно было легко переключаться между разными моделями.
Лайфхак 2. При поиске способов улучшения модели, старайтесь рассматривать всю систему в целом, а не только саму модель. Узкие места могут быть не только в модели, но и в других частях вашего пайплайна. Часто улучшить качество можно, например, за счёт лучшей обработки текста, который затем подается на вход модели.
Модели с Hugging Face
Первые шаги пройдены и теперь мы хотим улучшить качество работы. за счёт усложнения моделей. Тут важно помнить, что не всегда более сложная модель будет показывать лучшее качество, поэтому лучше идти итерационно от простого к сложному.
Теперь можно более пристально рассмотреть семейство моделей Bert и портал Hugging Face (далее HF). По сути HF является хабом, с которого можно скачивать и использовать предобученные модели, выложенные в открытый доступ, при помощи библиотеки Transformers, а также выкладывать туда свои модели.
В этом посте приведено отличное Введение в библиотеку Transformers и платформу Hugging Face. Помимо обзора там также есть хорошие описания таких структур, как пайплайны (Pipelines), автоматические классы (Autoclasses) и класс Trainer, необходимый нам для дальнейшего обучения.
Если вы работаете с английскими текстами, то вам повезло: большинство предобученных моделей, которые есть в открытом доступе, работают с этим языком (практически для всех видов NLP-задач). Отсюда следствие: с большей вероятностью вам подойдут пайплайны – готовые конвейеры этапов обработки входных данных и применения к ним соответствующей обученной модели в зависимости от поставленной задачи (Pipelines doc tutorial). Например, для классификации есть пайплайны TextClassificationPipeline, ZeroShotClassificationPipeline, для вопросно-ответной системы QuestionAnsweringPipeline и т.д.
Пайплайны помогут вам не углублятся в тонкости вызовов различных классов и функций, а сделают это за вас, спрятав всё под капотом. Для каждой задачи есть свой пайплайн.
С одной стороны пайплайны дают меньше гибкости и тонкой настройки, с другой – возможность быстро провести эксперимент и проверить базовое качество решаемой задачи. Пайплайны сами по умолчанию подбирают модель, но есть возможность указать конкретную модель параметром “model=” (в том числе прошедшую дообучение (далее файнтюнинг).
Одним из основных вопросов, которые теперь встают перед нами, является выбор модели. Как разобраться в этом множестве и понять, что лучше?
Системно решить этот вопрос помогут бенчмарки – сводные показателей качества работы ML моделей при решении разных задач на различных наборах данных. Они помогают сравнить модели между собой и выбрать лучшую.
Для русских текстов – Рейтинг русскоязычных энкодеров предложений.
MTEB – английский бенчмарк, но по нему можно выбирать мультиязычные модели.
Также на Хабре недавно вышла замечательная обзорная статья с обзорным списком моделей и их архитектур Зоопарк трансформеров: большой обзор моделей от BERT до Alpaca. К ней мы ещё вернёмся позже.
Большинство из приведённых в бенчмарках моделей для английского языка. Если нужно работать с русским, то можно обратить внимание следующие репозитории с моделями:
cointegrated (rubert-tiny2 и обзорная статья про него, LaBSE-en-ru, rut5-base-multitask);
bert-base-multilingual-cased по-прежнему популярная мультиязычная модель;
DeepPavlov (rubert-base-cased, rubert-base-cased-sentence и др.);
ai-forever (ruRoberta-large, sbert_large_mt_nlu_ru, sbert_large_nlu_ru);
При выборе модели можно ориентироваться на количество скачиваний модели, этот показатель косвенно указывает на эффективность модели. Но в первую очередь важно учитывать, на какой задаче она была обучена и на каких данных, а также вес модели, поскольку в зависимости от веса модели будут отъедать разное количество памяти при инференсе и файнтюнинге. Если хватает ресурсов, в качестве ещё одного лайфхака можно посоветовать всё равно начинать с более легких моделей. Отладив на них процесс использования в продакшене или файнтюнинга, уже можно переходить к более тяжеловесным, рассчитывая повысить качество. По опыту, это борьба за проценты, крайне редко – за десятки процентов.
При использовании выбранной модели могут пригодиться автоматические классы (Autoclasses). Они как раз нужны, если выбрана модель, но вы не знаете точных настроек и параметров. По аналогии с пайплайнами для каждой задачи свой класс: для классификации – AutoModelForSequenceClassification, AutoModelForMultipleChoice, для вопросно-ответной системы – AutoModelForQuestionAnswering и т.д.
Дообучение моделей
Когда уже поигрались с предобученными моделями и выбрали лучшие, пришло время их дообучить на своих данных.
В Hugging Face предоставляет API, который позволяет проводить обучение моделей практически для всех распространённых задач.
При дообучении (файнтюнинге) процедура обучения будет различаться в зависимости от задачи. Мы более подробно остановимся на задаче классификации (как одном из самых частых и понятных кейсов). Но для остальных задач также будут приведены ссылки на соответствующие классы/методы.
Классификация
Рассмотрим подробнее на примере классификации, как одной из самых популярных задач. Для остальных алгоритм действий будет схожим. Основным классом из Hugging Face, который нам понадобится, является BertForSequenceClassification. По той же ссылке ниже есть классы для других потенциально интересных задач:
Все они наследуются от базового класса Bert (там же приведена ссылка на научную статью и собраны ноутбуки с примерами решения разных NLP-задач).
Поскольку работа с BertForSequenceClassification и подобными ему классами является высокоуровневой, сложно понять, что происходит внутри, какие тензоры откуда берутся и куда передаются и т.д. Помочь в этом могут следующие ресурсы.
Видеотуториал от Юры Кашницкого, ссылка на первую часть (туториал из четырех частей, в котором Юра пишет аналогичный класс с нуля сам, подробно объясняя свой код), объяснение на английском, но язык очень простой.
Статья на Хабре: Ваш первый BERT: иллюстрированное руководство.
Также по ссылке список ноутбуков с примерами по решению задач Text Classification, Token Classification, Question Answering а также примерами по обучению и деплою модели.
И коли мы говорим про классификацию, нельзя обойти стороной интересный фреймворк SetFit (создан для эффективного обучения на небольшом количестве размеченных данных).
Классы для других задач и другие модели
Если вам помимо Bert интересно экспериментировать с другими архитектурами, например, дообучать на собственных данных, то список всех архитектур Hugging Face можно найти в левой боковой панели сайта по адресу: API -> Models -> Text Models.
На мой взгляд тут стоит обратить внимание на Roberta и ELECTRA, подробнее о них чуть ниже.
Про дообучение (файнтюнинг) уже обученных моделей подробно расписано в документации.
Так как довольно много практических задач сводятся к классификации, немного подробнее затронем эту тему. Дело в том, что кроме экспериментов с дообучением модели в парадигме классификации, можно ещё пробовать дообучать модели в парадигме обучения сиамских сетей.
Это принципиально другой подход, но тем не менее, при помощи него можно решать схожую задачу. Отличие состоит в том, что если при классификации мы оптимизируем веса таким образом, чтобы эмбеддинги, созданные моделью для разных классов, были разделимы (в идеале чтобы все объекты каждого класса выделялись в определённый кластер) в пространстве эмбеддингов, то при обучении сиамских сетей мы стараемся добиться того, чтобы эмбеддинги одинаковых/схожих объектов находились близко, а эмбеддинги разных объектов - максимально далеко друг от друга.
В каких же случаях это может быть эффективно?
Постановка задачи. Сиамские сети традиционно чаще используются для поиска, если у нас есть бесконечно много таких сущностей, как запрос и список кандидатов, который необходимо проранжировать в зависимости от близости к запросу. Поэтому логично учить модель приближать закодированный запрос к кандидатам, которые удовлетворяют этому запросу, и наоборот отдалять от всех остальных.
Подобный подход также можно использовать для классификации. В каких именно случаях?
Большое количество классов (по абсолютной величине по соотношению числа примеров на класс).
Небольшое количество примеров на класс. Задача приближается к few-shot learning / zero-shot learning.
Количество динамически изменяется (допустим, исчезают старые и появляются новые). В момент появления нового класса у него всего несколько примеров. Снова пахнет few-shot learning обучением.
Таким образом, выше мы видим факторы, которые в определённом роде делают нашу задачу классификации более близкой к задаче текстового поиска. При этом важно держать в голове, что каждый объект в батче будет состоять из пары документов (например вопрос - ответ), каждый из которых надо будет прогонять через модель, что будет увеличивать требования по объему памяти. Также обучение сиамских сетей будет требовать уже другой разметки, учитывающей близость между парами вопрос-ответ.
Итак, если в задаче классификации после традиционного файнтюнинга у вас остались время и ресурсы на дополнительные эксперименты, то можно взглянуть в сторону сиамских сетей. Если же вы изначально делаете эмбеддинги для задачи поиска, то этот подход имеет смысл попробовать одним из первых. Одним из популярнейших фреймворков, позволяющих использовать и обучать трансформеры для задач, связанных с семантическим поиском, является библиотека Sbert.
Sentence Transformers, SBert
Библиотека SBert построена на базе библиотеки Transformers и ориентирована на создание эмбеддингов предложений и текстов. Она позволяет работать с моделями с Hugging Face, но помимо этого также предоставляет дополнительный функционал, нацеленный на обработку векторных представлений текста:
создание текстовых эмбеддингов (Computing Sentence Embeddings),
семантический поиск (Semantic Search),
расчёт семантической близости текстов (Semantic Textual Similarity),
кластеризация (Clustering).
По ссылке – полный список решаемых задач, а также примеры использования для каждой задачи.
Но нам скорее всего интереснее дообучать модель на собственных данных. В документации этот раздел довольно подробно расписан. Единственный момент, который хочется дополнительно прояснить, – использование разных Loss-функций.
Помимо стандартной CosineSimilarityLoss для экспериментов также могут быть интересны следующие Los-функции:
TripletLoss (классический TripletLoss),
Более подробно про различия функций потерь
С классическим TripletLoss более менее все понятно (статья с хорошим объяснением, чтобы вспомнить). А вот с его разновидностями, возможно потребуется пояснение.
Чтобы разобраться с остальными разновидностями TripletLoss, введём формальное описание (оригинал отсюда).
Имея функцию расстояния d в пространстве эмбеддингов для якоря (anchor), позитивный объект (positive) того же класса и негативные объекты (negative) других классов:
где a, p, n – соответственно якорь, объект позитивного и объект негативного классов.
margin является гиперпараметром.
в случае, если
и, следовательно,
расстояние от якоря до позитивного объекта плюс margin больше расстояния от якоря до негативного. В этом случае функция потерь больше нуля, и мы оптимизируем веса пропорционально его величине.
В случае, если
то общий лосс становится равным нулю, и на таких объектах обучения не происходит.
Если простыми словами: в ходе обучения мы оптимизируем те ситуации, в которых негативные объекты ближе к якорю, чем позитивные объекты.
Поэтому при наличии якоря (a), позитивного объекта (p) и заданной константы margin всё пространство можно разделить на три полосы в зависимости от близости негативных объектов к якорю (a).
Негативные объекты в зелёной области лежат дальше позитивного, поэтому на них обучения нет. В оранжевой полосе уже лежат объекты, которые сравнимы по расстоянию до якоря с позитивным объектом (ширина полосы регулируется гиперпараметром margin). Их назвали Semi-hard negatives. В красной области – негативные объекты, которые ближе к якорю, чем позитивный. Это Hard Negatieves. Различия между описываемыми TripletLoss заключаются в том, на каких триплетах идёт обучение.
BatchAllTripletLoss для каждой пары (целевая метка – terget, текст) составляет все возможные триплеты, где якорем является оригинальный текст, положительными объектами – тексты с той же целевой меткой а отрицательными объектами – тексты с любой другой меткой и обучается с TripletLoss на всех получившихся триплетах, гиперпараметр margin задается вручную, по умолчанию равен 5.
BatchHardSoftMarginTripletLoss также обучается на всех триплетах, но при этом margin выводится автоматически.
BatchHardTripletLoss обучается только триплетах с Hard Negatieves объектами, margin выводится автоматически, но можно также задать параметр вручную.
BatchSemiHardTripletLoss обучается только триплетах с SemiHard Negatieves объектами, margin выводится автоматически, но можно также задать параметр вручную.
По умолчанию я бы рекомендовал использовать BatchHardSoftMarginTripletLoss, не грузить голову самостоятельным подбором параметра margin, а использовать автоматический расчёт этого параметра.
ContrastiveLoss часто используется при обработке изображений. Этот тип лосса является парным, то есть в нём сравниваются два объекта, и если они одинаковы, то в ходе обучения веса оптимизируются так, что расстояние между ними уменьшается, а если разные, то увеличивается.
Обучение с нуля на собственных данных
Такое обучение, которое ещё называется pre-training, решает задачу моделирования языка или, говоря проще, позволяет нашей модели выучить (насколько это возможно) язык, с которым она работает.
Для этого обучение проходит в парадигме Masked Language Modeling, MLM (проходим по тексту, маскируем по очереди каждый токен и пытаемся по окружающим его токенам – контексту предсказать этот токен) и Next Sentence Prediction, NSP (предсказание по паре текстовых фрагментов, следуют они друг за другом или нет). Есть ещё также Casual Language Modeling, но MLM обычно используется чаще.
На практике прибегнуть к процедуре pre-training может быть полезно, когда тексты, с которыми нужно работать, отличны от тех, на которых обучались общедоступные известные модели. Это может происходить в следующих случаях:
специфичный стиль текста, его структура (яркий пример – юридические тексты, договора, сметы),
наличие специфичных терминов (пример – медицинские тексты).
На что стоит обратить внимание перед процедурой pre-training:
данных, с которыми вы работаете, может быть недостаточно для эффективного обучения модели, поэтому даже обладая хорошим потенциалом, они могут не обучиться и показывать такое же либо даже худшее качество по сравнению с бейзлайном;
хорошие эмбеддинги – это лишь один из этапов во всём пайплайне и вовсе не единственная точка роста. Поэтому если вы стоите перед выбором, стоит ли тратить ресурсы на pre-training, аренду видеокарт и т.д., возможно, стоит сначала сделать упор на другие вещи, например, на более качественный сбор/обработку/обогащение данных.
Если решили идти по этому пути, то рабочим вариантом может быть следующий: взять веса общедоступной модели, обученной на таких текстах, как Википедия, затем обучить её на собственных текстах в парадигме MLM, дав возможность изучить тонкости именно ваших текстов. А затем снова дообучить в парадигме fine-tuning для решения целевой задачи.
Выбор модели и процесс обучения
Какую архитектуру выбрать для pre-training обучения? Можно взять классический Bert. Но c другой стороны, с его создания уже утекло много воды и появились более продвинутые архитектуры. Особенно заслуживают внимания следующие архитектуры.
RoBERTa (A Robustly Optimized BERT Pre-training Approach) – это архитектура, основанная на BERT, но в которой оптимизировали параметры обучения, отказались от обучения в парадигме NSP и показали, что так качество будет выше.
DeBERTa (Decoding-enhanced BERT with disentangled attention) – дословно BERT улучшенного декодирования с рассеянным вниманием. Это основанная на RoBERTa архитектура, в которой авторы дополнительно применили механизм кодирования слова двумя векторами (кодирующими его содержание и положение в тексте) и заменили выходной softmax-слоя усовершенствованным декодером. Вот пример успешного применения этой архитектуры.
ELECTRA – архитектура (оригинальная статья), в которой отказались от обучения в парадигмах MLM и NSP и тренируют генератор и дискриминатор. Первая модель учится выборочно заменять токены в тексте, а вторая (которую и будем использовать после обучения) учится определять токены, которые были заменены, и таким образом осваивает структуру языка. Архитектура модели и её преимущества довольно подробно расписаны в статье Более эффективное предварительное обучение NLP моделей с ELECTRA.
Сам процесс обучения неплохо описан в документации, но немного скупо. Поэтому приведу ещё пару ссылок:
How to train a new language model from scratch using Transformers and Tokenizers, статья, colab;
5 видеоуроков на youtube от James Briggs: Training Bert.
Лайфхаки и частые ошибки при обучении
Место хранения моделей. По умолчанию вся загрузка происходит в домашнюю директорию, в папку ~/cache. Если много разных моделей, то в какой-то момент место может закончиться с ошибкой: no space left on device. Если вы вышли за рамки установленной квоты также может возникнуть Disk quota exceeded. Чтобы такого не произошло, можно изначально складывать их в другое место, аргумент cache_dir в методе загрузки моделей from_pretrained (неплохой практикой является иметь в файловой системе большой объём примонтированной памяти, куда имеют доступ разные члены команды и куда можно складывать разные данные).
-
Обучение с checkpoint. При продолжительном обучении полезно сохранять чекпоинты на случай, если всё упадёт. Это следующие аргументы в классе TrainingArguments:
output_dir, путь сохранения чекпоинтов, по которому будут создаваться поддиректории checkpoint-XXX с номерами чекпоинтов;
save_steps число шагов, предшествующих сохранению чекпоинта;
save_total_limit, ограничение на число сохраненных чекпоинтов (чтобы бесконтрольно не захламлять память), при его превышении чекпоинты будут перезаписываться.
-
Запуск обучения с чекпоинта:
trainer = Trainer(...)
trainer.train(resume_from_checkpoint=True)
При корректно заданном output_dir он автоматически найдет чекпоинты и загрузит последний.
Загрузка модели с чекпоинта: from_pretrained(checkpoint). Где checkpoint - это путь до директории вида checkpoint-XXX
-
CUDA out of memory. Вышли за пределы памяти на GPU. Чтобы избежать ошибки, нужно очистить GPU и уменьшить величину батчей.
Чтобы посмотреть потребление памяти, используем команду nvidia-smi. Она отобразит потребление памяти и вызвавший его процесс.
Та же команда, но с красивым выводом в формате csv: $nvidia-smi --query-compute-apps=pid, process_name, used_memory --format=csv.
В ситуации, когда выделите видеокарты внутри команды и необходимо использовать какую-то одну, можно ограничить использование соответствующей переменной окружения CUDA_VISIBLE_DEVICES = "0,1" python3 training_script.py
-
Несоответствия классов. В случаях, когда нужно обучать модель разными способами (например, взята предобученная модель, затем MLM, а потом ещё классификация) на разных этапах будут использоваться разные классы моделей и токенизаторов. Могут возникать ошибки следующего вида:
Some weights of the model checkpoint at /home/XXX/MODEL_DIR/checkpoint-XXX were not used when initializing XXX_Model
XXXTokenizerFast: Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained
Пугаться не стоит. Это вызвано тем, что для разных задач к базовым слоям модели разные классы добавляют поверх какие-то слои с весами и инициализируют их случайным образом. Токенайзеры добавляют специальные токены. Если такое возникло перед обучением модели, ничего страшного, поскольку в процессе обучения соответствующие веса и эмбеддинги токенов будут обучены. Если уже используете модели в бою, значит, что-то упустили. Можно либо использовать автоклассы, чтобы автоматически подтянуть необходимое, либо пересматривать процедуру обучения.
Количество слов и токенов. Модели имеют ограничение на длину входной последовательности и контекста. Всё, что выходит за рамки, обрезается. Поэтому важно понимать характеристику max_len вашей модели, а также полезно строить гистограммы длины токенизированных входных текстов (потому что max_len измеряется не в количестве слов, а в количестве токенов и после токенизации длина последовательности меняется). В Bert-подобных моделях используется BPE-токенизация, поэтому тексты с редкими сочетаниями букв на выходе будут иметь много токенов. Одним из интересных следствий этого является то, что токенизированные тексты в UPPER CASE (полностью из заглавных букв) будут очень большой длины, поэтому, возможно, стоит переводить их в LOWER CASE, даже если модель умеет работать с верхним регистром.
А теперь несколько слов о нашумевшей ChatGPT и том, что со всем этим делать.
Использование ChatGPT и подобных моделей для создания датасетов
Сценариев работы с ChatGPT-like моделями много, но давайте подумаем, как они могут помочь нам в Data Science (если с ChatGPT знакомы плохо, вот пара статей, чтобы обновить знания: GPT для чайников, Как работает ChatGPT).
В чём особенность всех моделей, которые мы обсуждали выше? Говоря простыми словами, они являются энкодерами, то есть входные последовательности на естественном языке преобразуют вектора, что открывает нам богатый арсенал различных математических методов для последующей их обработки. А модели семейства GPT являются декодер онли архитектурой. Они начинают генерацию с промта, который мы подаём им на вход и затем продолжают генерацию авторегрессионно. По сути мы задаём направление мысли генерации, а дальше модель её продолжает. Поэтому методы взаимодействия с моделью и её использование будут отличаться, и фактически, это будет взаимодействие по следующей схеме: при наличии определённых “псевдознаний” о мире возьми то–то (в простейшем случае текст) и сделай с ним то-то, и вот тебе ещё список примеров, чтобы ты лучше понимала, чего я от тебя хочу. Таким образом можно решать задачи генерации и перефразирования текста, суммаризации, придания стилистики, генерации ответов на вопросы по определённым данным и т. д. (список только растёт). В качестве глобальных направлений можно выделить использование в поисковых системах, диалоговых системах, системах генерации кода и, конечно же, обработки и разметки данных. (более подробно эта тема обсуждается здесь).
Рассмотрим последнее направление более подробно. Одной из самых распространённых проблем в работе с данными является отсутствие этих самых данных, и с этого ракурса ChatGPT можно использовать для генерации и аугментации датасетов.
Преимущества такого подхода по сравнению с асессорами очевидны: это меньшая цена, большая скорость получения данных и возможность более гибкой и тонкой настройки генерации при помощи подбора необходимых промтов.
В качестве недостатков можно выделить проблему галлюцинирования, которую необходимо дополнительно контролировать (одним из вариантов является валидирование вывода ChatGPT другой моделью) и зависимость от железа, на котором вы всё это будете запускать. Есть также компании, которые предоставляют API для пользования GPT-like моделями, но тут нужно понимать, можете ли вы отдавать им свои данные в виде запросов, промтов и прочих данных, особенно если вы работаете с sensitive информацией.
Для более простого и эффективного использования на практике хочется осветить несколько моментов.
Создание аккаунта. Статья: как получить доступ к ChatGPT в России. И ещё один интересный ресурс Theb.ai. Этот сервис даёт возможность поговорить с разными версиями GPT, в том числе c GPT-4. Когда закончатся бесплатные попытки, попросят пополнить кошелёк. Это можно сделать зарубежной банковской картой либо криптовалютой. Но в первую очередь сервис удобен тем, что можно бесплатно попробовать всё в пару кликов.
Поиск промта. В интернетах много примеров и сервисов по генерации промтов под конкретные задачи. Они быстро обновляются. Поэтому вряд ли имеет смысл советовать какие-то конкретные, нужно просто гуглить. Но хочется поделиться определёнными рецептами, которые могут сделать ваши промты лучше или же подкинут новые идеи.
Конкретизировать и разжевывать: сколько предложений хотите получить, какого объёма, какой стилистики, кто, что, где, когда, почему, насколько и как.
Указывать, что должно присутствовать в выдаче и в каком виде, если у вас есть чёткое представление, лучше подробно его донести до GPT – суммы, сроки, объемы, названия и т.д. Но при этом оставить пространство для творчества и вариативности при генерации.
Явно указывать то, чего не должно быть: тексты не должны содержать вводных фраз типа «Вы не могли бы ответить на вопрос…», грубостей либо же просторечий,
структурировать подачу материала в промте: если это тезисы, ограничения или пункты, которым вы хотите, чтобы следовала модель при генерации, в промт их можно подать тоже пронумерованным списком. Модели демонстрируют понимание таких вещей.Не бояться использовать различные символы кроме букв и цифр. Допустим, вам нужно, чтобы при генерации модель опиралась на какой-то фрагмент текста как на пример или использовала его, и хочется в промте его выделить относительно инструкций генерации. Это можно сделать при помощи скобок [] {}. Пример: «Сгенерируй мне вопросы к следующему тексту [Начало текста] // несколько строк текста [Конец текста]».
Использовать ChatGPT-like модели для преобразования текстов на входе в данные определённой структуры на выходе. Например, вы подаёте предложения на вход модели и просите для каждого сгенерировать json файл с определёнными полями (которые должна заполнить модель по каждому предложению). Только нужно не забывать всегда подавать после инструкций парочку примеров, как всё должно выглядеть на выходе. Полученный json можно уже парсить при помощи python. Кстати, потенциально можно пробовать делать так NER, но я пока что не проверял этот метод.
Краткий обзор аналогов. Какие ещё GPT-like можно использовать? Этот вопрос может стать актуальным, если как сказано выше, вы работаете с информацией, которую нельзя передавать ChatGPT или же GPT-like модель запустить в пайплайне, работающем в изолированном контуре. Какие модели ещё существуют? После успеха ChatGPT на сцену вышла LLaMA (статья, версия .cpp). Это построенная по тому же принципу, но меньшая по размеру и потребляемым ресурсам модель, что делает её более доступной для использования простыми смертными. Недавно была выпущена LLaMA 2 (теперь и на Hugging Face). Также наравне со старыми возникли новые механизмы обучения.
Классическое обучение, которым была получена модель, только на новых данных.
Обучение на инструкциях, когда модели предоставляется задание, контекст и правильный ответ.
Reinforcement Learning from Human Feedback, хорошее обяъснение.
Механизм обучения адаптеров (LoRA: Low-Rank Adaptation of Large Language Models, оригинальная статья). По сути это заморозка веса основной модели (которых огромное множество, и поэтому файнтюнить эту кучу затратно) и дообучение слоёв адаптера, которые будут применяться поверх исходных слоёв. После этой процедуры веса основной модели размораживаются и используются совместно с весами адаптеров.
Если хочется более глубоко погрузиться и понять, как всё это работает, то вот несколько ресурсов:
лекция: Татьяна Гайнцева “Instruction tuning: что, почему и как”,
воркшоп: Александр Голубев “Воркшоп по LLM + RLHF”,
статья: StackLLaMA: практическое руководство по обучению LLaMA с помощью RLHF (оригинал),
статья: Как воспитать GPT модель в домашних условиях [LLaMA Update].
Эти механизмы обусловили бурный рост числа GPT-like моделей. Вот некоторые, на которые стоит обратить внимание:
alpaca (репозиторий, описание, гайд по запуску),
vicuna (описание, веса, гайд по запуску),
gpt4all (репозиторий, описание); небольшое сравнение gpt4all и alpaca,
saiga (репозиторий, видеопрезентация),
ruGPT от Сбера и аналог ChatGPT Giaga-Chat,
open assistant (возможно, не самая хорошая на данный момент модель, но интересна как открытый проект, поддерживемый краудсорсингом).
Наиболее подробный список с кратким описанием каждой модели приведён в статье Зоопарк трансформеров: большой обзор моделей от BERT до Alpaca.
Также в этой области есть свои бенчмарки, которые помогут в выборе модели.
Мы же с командой в качестве закрытой альтернативы ChatGPT решили остановиться на saiga в силу хорошего качества и отсутствия проблем с лицензированием.
И что же дальше? Дальше только интереснее: мы приходим к тому, что у нас есть инструмент, имитирующий работу интеллекта и способный выполнять инструкции. Что необходимо еще для эффективной работы и возможности извлечения экономической выгоды? Правильно, необходимо обеспечить наши LLM доступом к данным разной природы. Стали появляться различные библиотеки, обеспечивающие такое взаимодействие. Среди известных – LangChain и LlamaIndex.
LangChain включает:
компоненты, которые являются абстракциями, предоставляющими разные пути взаимодействия с окружающей средой, с различными типами и структурами данных и т.д.;
агенты, которые способны выполнять определённые действия по такому взаимодействию;
цепочки действий, включающие совместное использование различных компонентов и агентов для выполнения более высокоуровневых задач.
По библиотеке есть видеокурс: LangChain for Gen AI and LLMs.
LlamaIndex предоставляет:
коннекторы данных для подключения к существующим источникам данных и форматам данных (API, PDF, документы, SQL и т.д.);
различные способы структурирования данных (индексы, графики), позволяющие легко использовать эти данные в LLM;
расширенный интерфейс поиска/запросов по вашим данным, позволяющий вводить запросы в LLM и получать в ответ контекст и расширенный знаниями вывод;
возможности интеграции с разными внешними фреймворками приложений, в частности есть интеграция с LangChain.
На этом примерно всё. Здорово, если вы дочитали до конца. Тема быстро развивается. Если что-то упустил, добавляйте в комментариях.
Хочу также поблагодарить своих коллег – Сашу Замиралова и нашего тимлида @vladbalv, вместе с которыми мне довелось участвовать в проектах, по итогам которых родилась эта статья.