📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Customize your AI with model fine-tuning on NVIDIA DGX Spark

NVIDIA Developer59:11

Transcription

Можете ли вы меня слышать? >> Здравствуйте. >> Извините за это, все. Теперь вы должны меня слышать. Хорошо. Э-э, так что я начну снова. Добро пожаловать всем на очередной код-семинар разработчиков ARM. Мы проводим эти код-семинары раз в месяц на разные темы, в зависимости от того, что актуально для нашей аудитории разработчиков в данный момент. В этом месяце я покажу, как выполнять тонкую настройку моделей на одном из этих новых боксов Nvidia DGX Spark. И поэтому сегодня с нами гости из Nvidia, которые будут готовы ответить на ваши вопросы, специфичные для Nvidia. Э-э, так что сегодня у нас Лакшми и Зак из команды Nvidia, и я собираюсь предоставить им возможность кратко представиться. >> Отлично. Спасибо, Майк. Э-э, меня зовут Зак. Я в команде сообщества разработчиков NVIDIA. Так что, вы знаете, мы тоже проводим прямые трансляции каждую неделю. Возможно, вы видели меня или слышали меня где-то в нашей трансляции. Так что спасибо Майку за то, что пригласил нас, и ARM тоже. Так что я в восторге от этого эпизода. >> Привет всем, э-э, я Лакшми. Э-э, я работаю системным инженером-программистом в Nvidia, и да, я участвовала в паре прямых трансляций от Nvidia раньше для Spark. Так что, э-э, мы очень рады услышать выступление Майка. Майк, я думаю, ваш звук э-э Привет, Майк, ваш звук, я думаю, он может идти через другое устройство. Извините, ребята. Можете поднять большой палец вверх, если слышите Майка? Спасибо, что терпите нас. Майк на самом деле использует обширную установку вместе со своим Spark и ПК. >> Да. Вы меня слышите сейчас? >> Да. >> Хорошо. Итак, я в основном запускаю это с DGX, чтобы я мог поделиться своим экраном. Проблема в том, что у DGX нет никакого аудиовыхода. Э-э, и поэтому моя обычная установка не сработала бы. Так что я сбалансировал это между Spark и моим ноутбуком, и с StreamYard это просто вызывает некоторую путаницу со звуком. Так что да, еще раз спасибо всем, что терпите нас. Э-э, живем и учимся, верно? Мы сделаем лучше в следующий раз. Хорошо, я собираюсь убрать Зака и Лакшми снова. Они будут в чате, так что вы можете свободно задавать вопросы, если хотите. А пока мы собираемся перейти прямо к нашему учебному плану. Хорошо. Все это видят? Надеюсь, да. Хорошо. Итак, это учебный план, который был частично основан на том, что Nvidia написала для своего DTX Mark. Если вы зайдете на build.nvidia.com, nvidia.com/spark. У них есть всевозможные руководства по различным вещам, которые вы можете делать на этом боксе. И это была моя отправная точка. Я хотел научиться тонкой настройке моделей, используя это новое оборудование. Так что я начал с одного из их учебных пособий в качестве отправной точки. И на самом деле, часть этого контента скопирована, по крайней мере частично, из контента Nvidia. Но если вы зайдете на learn.arm.com, arm.com сейчас, и найдете DGX Spark. Вы найдете этот учебный план там. Вы можете следовать ему, если хотите. Э-э, вы не сможете следовать ему напрямую со мной сегодня, потому что мы будем делать вещи, которые требуют загрузки некоторых больших моделей, некоторых больших образов Docker, и я уже их загрузил. Так что вам не придется ждать, пока я это сделаю. Но если вы попытаетесь следовать, вам придется ждать их, и это приведет к тому, что вы отстанете. Но если у вас есть Spark, и вы хотите попробовать это самостоятельно дома, перейдите к этому учебному плану. Все, что я собираюсь сделать сегодня, точно такое же, как и там, так что вы можете делать это шаг за шагом и получать те же результаты. А затем в конце этого, цель этого - научить вас, как это делать. Не с какой-либо конкретной моделью, не с каким-либо конкретным набором данных, а просто в общем смысле, чтобы вы могли затем пойти и выбрать свою собственную базовую модель в зависимости от ваших потребностей. Вы можете найти свой собственный набор данных или создать свой собственный набор данных для вашего конкретного контента. Э-э, а затем вы можете сделать все это самостоятельно таким образом, чтобы это лучше служило вам. Итак, мы собираемся перейти прямо к этому. Э-э, это учебный план. Как только вы начнете, э-э, если вы совершенно новичок в использовании DGX, вам, возможно, придется выполнить некоторую начальную настройку. Я знаю, что мне пришлось, когда я впервые начал это делать. Э-э, поскольку мы будем использовать Docker для тонкой настройки, а также для тестирования наших моделей впоследствии. Э-э, вам нужно убедиться, что у вас установлен Docker и что демон Docker запущен. Так что вы можете вызвать Docker images, чтобы убедиться, что это происходит. Если у вас возникнут какие-либо ошибки о невозможности найти сокет, это означает, что ваш демон Docker не запущен, и вам нужно его запустить. Но если вы просто увидите стандартный вывод, даже если там нет образов, это означает, что ваша настройка Docker хороша. Еще одна вещь, которую мы собираемся сделать, потому что в Ubuntu по умолчанию команда Docker требует от вас наличия прав root, и довольно раздражает запускать pseudo перед всеми вашими командами Docker. Так что этот шаг здесь добавит вашего пользователя в группу Docker в рамках разрешений Unix, и это позволит вам запускать Docker без вызова команды pseudo. Так что pseudo user mod- a docker, а затем ваш пользователь добавляет вас в эту группу. Затем вы запускаете эту новую команду группы docker. Это просто гарантирует, что ваша текущая активная сессия терминала имеет вас в этой группе. Э-э, если бы вы создали новую сессию терминала, вам не пришлось бы этого делать. Она была бы там по умолчанию. Но также, если у вас открыто другое окно терминала, оно не узнает автоматически, что вы находитесь в этой новой группе, без того, чтобы вы вызвали эту команду в этом окне терминала. Так что это просто небольшая организационная работа, чтобы убедиться, что ваш Docker настроен правильно. Следующий шаг - загрузить образ Docker. Это один из образов, который Nvidia уже создала для вас и который отлично подходит для PyTorch. Э-э, он использует относительно недавний PyTorch. Я думаю, когда я начинал, это был самый последний PyTorch. Возможно, сейчас есть более новый, но этот от ноября прошлого года. Он не очень старый. Итак, первое, что вам нужно будет сделать, это выполнить команду docker pull, и это загрузит образ, который мы будем использовать для выполнения процесса тонкой настройки. Позвольте мне просто убедиться, что нет сообщений. Хорошо, я думаю, мы готовы. Хорошо, так что теперь, когда у нас есть этот образ, мы собираемся его запустить. Так что мы скопируем эту команду прямо здесь. И это просто создаст новый запущенный контейнер с этим образом. Он смонтирует каталог кэша hugging face, так что когда мы будем загружать модели локально на нашу машину, они будут доступны в среде этого контейнера. Также он смонтирует наше рабочее пространство, текущий каталог, в /workspace внутри этого образа Docker, чтобы у нас был доступ ко всему, что есть в этом локальном каталоге. Так что мы скопируем это и собираемся запустить. И теперь мы находимся в нашем контейнере PyTorch. Теперь в стандартном контейнере PyTorch есть некоторые дополнительные вещи, которые нам нужно добавить, потому что мы будем использовать некоторые библиотеки hugging face для тонкой настройки. Нам нужно будет установить некоторые из них. Так что мы установим их здесь. И пока это работает в фоновом режиме, я просто быстро расскажу, что это такое. Библиотека transformers - это просто стандартный набор библиотек Python для трансформеров. TRL - это на самом деле главное, что мы будем использовать. Это обучение с подкреплением на основе трансформеров, что-то вроде того. Я не помню точного названия, но в нем есть класс trainer, класс SFT trainer, который мы будем использовать для полной программной тонкой настройки этой модели. А затем PFT и bits and bytes - это просто другие вспомогательные библиотеки, которые им нужны. Вы можете узнать о них больше. Э-э, есть краткие описания здесь в учебных планах, или вы можете узнать о них больше на hugging face. Все, что вам действительно нужно знать, это то, что скрипт, который мы будем использовать для тонкой настройки, использует эти библиотеки для этого. Еще одна вещь, которую вам нужно будет сделать, это аутентифицироваться в HuggingFace. Когда вы входите в HuggingFace, вы можете перейти в свою учетную запись пользователя и выбрать токены доступа, и там вы можете получить небольшую строку токена, которую вы вставляете в свою среду. Вы можете использовать команду HF off login или установить ее как переменную среды. Это HF token, все заглавными буквами. Это позволяет вам аутентифицироваться в hugging face при выполнении таких действий, как загрузка моделей. Я пропущу это, потому что я уже сделал это. Опять же, если вы следуете, вам нужно будет это сделать, потому что вам придется загружать модели. Я уже их загрузил, так что нам не придется делать этот шаг здесь, что отлично, потому что мой интернет печально известен своей медлительностью. И если бы вам пришлось ждать, пока я загружу эти модели, это был бы код-семинар на целый день, а не на час. Но убедитесь, что вы это сделали, чтобы когда вы дойдете до шагов, где вы будете загружать эти модели, он мог аутентифицироваться и получить их. Не все модели на hugging face нуждаются в этом, и для некоторых моделей этого недостаточно, но для моделей llama, которые мы будем использовать, этого достаточно. Хорошо, следующий шаг - мы клонируем этот репозиторий в наш запущенный контейнер. Э-э, это копия одного из скриптов Nvidia, которые они предоставляют. Скрипты, которые они предоставляют, очень хороши, но в них жестко закодирован набор данных. И для этого учебного плана и этого код-семинара я хотел использовать пользовательский. Так что с их разрешения я форкнул один из их скриптов и внес в него некоторые изменения, чтобы мы могли передавать этот набор данных в качестве параметра командной строки. Так что давайте вернемся к нашему терминалу и клонируем этот репозиторий. О, у меня он уже есть. Так что вот он. Э-э, так что это скрипт, который мы будем использовать, Llama 3B full fine-tuning. И опять же, это основано почти полностью на скрипте Nvidia. Так что вы можете использовать их, если хотите выполнить стандартную тонкую настройку, которую они предоставляют. Но поскольку я пытаюсь научить вас, как делать свою собственную и использовать свои собственные наборы данных, э-э, для этого предназначен этот скрипт. [хмыкает и откашливается] Хорошо. Итак, почему мы хотим тонко настраивать модель? Почему бы нам просто не использовать одну из этих моделей, которую кто-то другой уже построил, и просто поместить ее в сервер вывода, поставить перед ней чат-бота и считать, что дело сделано? Э-э, потому что эти модели обучены на общих знаниях, а не на специфических. Э-э, так что если бы вы загрузили модель Llama 3.2, которую мы будем использовать в качестве основы, и спросили бы ее что-то конкретное о Raspberry Pi. Скажем, вы спросили бы ее, какая тактовая частота у RP 2350, который является новым микроконтроллерным процессором Raspberry Pi. Она не даст вам очень хорошего ответа. Э-э, я получал всевозможные ответы на этот запрос на общей модели. Все они были неверными. И это потому, что когда эта модель обучалась, каждая крупица информации, на которой она обучалась, имела одинаковое значение. И поэтому, даже если она обучалась на каких-то данных, специфичных для Raspberry Pi, это такая малая доля ее обучения, что она на самом деле не знает, что правильно, а что нет. Так что мы хотим иметь модель, которая знает конкретно об этих устройствах Raspberry Pi, чтобы когда вы задаете ей вопросы, вы получали точные данные, а не галлюцинации. И вот тут-то и появляется тонкая настройка. С тонкой настройкой, вместо того чтобы начинать с нуля, вы начинаете с базовой модели. Вы начинаете с чего-то, что уже было обучено тому, как общаться с человеком, как генерировать текст на основе большого количества входных данных, и вы просто настраиваете это, чтобы оно было более специфичным для набора данных, который вас интересует. И способ, которым это работает, заключается в том, что набор данных будет итерировать через модель. Он будет тестировать и проверять, насколько хорошо модель предсказывает эти конкретные данные. А затем он пройдет через все параметры в этой модели, настраивая их до тех пор, пока она не будет делать это правильно. Это тот же процесс, который вы проходите при первоначальном обучении, но вместо того, чтобы обучать всю модель на огромном наборе данных, вы получаете преимущество от того, что кто-то другой уже сделал это, а теперь вы просто настраиваете эти параметры так, как вам нужно. Итак, вот пример некоторых данных, которые могут быть в наборе данных. Э-э, у нас есть инструкция. Мы будем использовать уже обученную на инструкциях модель. Э-э, так что она уже знает, как отвечать на вопросы и выполнять задачи, которые вы ей даете. И мы просто научим ее, как делать это конкретно для данных Raspberry Pi. Так что наш набор данных будет иметь три параметра. Он содержит инструкцию, которая является тем, что пользователь может использовать в качестве запроса. Он имеет необязательный ввод. Иногда ваш запрос имеет дополнительный ввод, иногда нет. Э-э, а затем он имеет ожидаемый вывод. И процесс обучения, процесс тонкой настройки будет использовать это для прогона через модель и проверки, производит ли она правильный вывод, учитывая инструкцию и ввод. И снова, пройдитесь по всем параметрам в этой модели, настраивая их до тех пор, пока она не сделает это правильно. Мы немного преобразуем этот набор данных, чтобы использовать этот формат. Если вы когда-либо писали код для использования LLM, вы знаете, что у всех них разные модели имеют немного разные форматы того, как они ожидают, что эти токены будут поступать. Для этого мы будем обучать ее с очень простым форматированием. Мы будем использовать три хэша перед инструкцией, перед вводом и перед ответом. И поскольку мы будем обучать ее в этом формате, мы также должны видеть вывод в похожем формате. Теперь существует несколько различных методов тонкой настройки. Э-э, самый базовый - это полная тонкая настройка. Это когда вы берете свою базовую модель, пропускаете набор данных через процесс тонкой настройки, и вы возвращаетесь и корректируете все параметры в этой модели. Так что мы начнем с трехмиллиардной модели. Это означает, что наша тонкая настройка возвращается и потенциально изменяет все три миллиарда этих параметров каждый раз, когда она проходит шаг в процессе обучения. Это может быть очень ресурсоемким. Мы используем небольшую модель, так что для нас это не будет большой проблемой. Э-э, но если бы вы тонко настраивали большую модель, например, стомиллиардную модель, все это должно было бы быть в памяти. И поэтому это может стать очень ресурсоемким для вас. Опять же, это самый базовый. Это не обязательно самый простой, но это самый базовый. Он следует тому же процессу, что и первоначальное обучение. Но пара других вариантов - это Лора, что означает Low Rank Adaptation. И как это работает, вместо того чтобы проходить и корректировать все параметры в вашей модели, она создает слой из двух меньших матриц. Э-э, так что когда вы выполняете умножение матриц, вы получаете новую матрицу. И это то, что представляют собой все эти слои параметров в моделях. С Лорой с адаптацией низкого ранга вы начинаете с двух меньших матриц, которые затем можно умножить друг на друга, чтобы получить одну размером с один из ваших слоев в модели. И это означает, что когда вы проходите и выполняете тонкую настройку, вы просто корректируете параметры в этом наборе матриц адаптера. Так что у вас гораздо, гораздо меньше параметров, которые вам приходится корректировать каждый раз. Это очень хорошо, если вы используете большую модель или у вас есть ограничения по ресурсам, которые не позволяют выполнять полную тонкую настройку. Это намного, намного быстрее, потому что вы снова корректируете лишь малую часть общего числа параметров. Это также открывает некоторые интересные варианты использования, когда вы развертываете ее, вы можете иметь разные наборы адаптеров, которые тонко настроены с разными данными, все работающие от одной базовой модели. Так что вы можете иметь меньший размер развертывания, повторно используя базовую модель, даже если у вас есть разные пользовательские слои адаптации, которые вы будете использовать для разных сценариев использования. Qura - это то же самое, только вместо использования полного размера параметров, например, 16-битного числа с плавающей запятой, параметры в ваших двух матрицах, которые вы используете, будут квантованы. Так что они могут быть, например, 4-битными целыми числами вместо 16-битных чисел с плавающей запятой, что снова уменьшает размеры параметров, делает процесс обучения намного быстрее. Это действительно зависит от вас, какой из этих вариантов вы хотите выбрать. Для этого код-семинара мы будем использовать полную тонкую настройку, просто потому, что это более простой сценарий использования. Э-э, и это работает очень хорошо. Это хороший пример для этого, а учитывая размер модели, которую мы используем, плюс большой объем памяти и вычислительной мощности на DGX Spark, это делает его действительно хорошим примером. Хорошо, теперь мы посмотрим на сам скрипт. Вы можете посмотреть полный скрипт в репозитории GitHub, если хотите. Э-э, мы просто посмотрим на некоторые важные части, чтобы объяснить, что он делает. Опять же, вы можете изменить скрипт как угодно, но это действительно хорошая отправная точка для вас. Итак, для начала у нас есть стандартные импорты Python. Мы используем PyTorch, так что у вас есть это там. Э-э, вы можете увидеть некоторые из библиотек, которые мы установили в начале, такие как TRL и Transformer. [хмыкает] Эти два здесь будут самыми интересными. SFT config и SFT trainer. Это будет класс обучения, который мы будем использовать для выполнения самой тонкой настройки. Мы также определили этот шаблон запроса. Если вы помните, на предыдущем экране я показывал, как мы будем использовать очень простой шаблон для наших входных данных. Так что, когда мы пройдем это обучение, он узнает, что хэш инструкции - это начало фазы инструкции, и он должен ответить хэш ответом, потому что это следующий токен, который он ожидает от вывода. У нас есть эта функция, которая загружает набор данных, которая возьмет информацию, которую вы ей передаете из командной строки. Она откроет этот файл или загрузит этот файл, если вы используете удаленный набор данных из hugging face, и она пройдет через него, преобразуя все строки в этом файле в тот формат запроса, который мы определили. Так что вы можете увидеть здесь, мы форматируем его на основе инструкции, ввода и вывода, которые мы получаем из этого набора данных. Э-э, так что мы сделаем это. И затем, если вы загружаете очень большой набор данных, вы можете не захотеть использовать его весь для вашего обучения. Так что вы можете передать определенный размер набора данных в ваших параметрах, и тогда он загрузит только это количество элементов из набора данных, когда вы вызовете эту функцию. И это подготовит ваш набор данных, чтобы мы могли начать передавать его тренеру модели. Следующий шаг - загрузка модели. Так что мы загрузим эту базовую модель, которая у нас есть. Э-э, опять же, вы можете передать ее в командной строке. Есть некоторые значения по умолчанию, но мы передадим ее, потому что мы хотим использовать очень специфическую предварительно обученную на инструкциях модель от Meta. Э-э, так что это загрузит ее. Она может проанализировать, какой тип модели это. Э-э, так что вам не нужно предоставлять всю информацию там. Э-э, мы также загрузим токенизатор. Э-э, он также идет с моделью, так что он знает, где его найти. Э-э, вам не нужно знать, что все это делает. Это все просто настройка, чтобы сообщить PyTorch и библиотеке hugging face, где находится модель, что это за модель и как с ней взаимодействовать. Следующий шаг - вызвать функцию get data set, которую мы рассмотрели ранее. Она передаст все наши аргументы из командной строки, включая, где найти набор данных, какой тип набора данных, и сколько элементов из него мы хотим загрузить для обучения. Конфигурация немного длинная, поэтому я не буду рассматривать все здесь. Э-э, но пара вещей, которые имеют значение, это, давайте посмотрим, где это, э-э, так что количество эпох, мы начнем с небольшого числа, мы сделаем прогрев модели, так что я считаю, что мы используем, да, мы используем компиляцию PyTorch, так что мы будем компилировать структуру модели, чтобы она работала быстрее. Э-э, и чтобы сделать это, ей нужно пройти через немного данных, чтобы увидеть, какие части модели активируются, чтобы она знала, как правильно компилировать и оптимизировать ее. Так что мы начнем всего с нескольких эпох. Так что это будет означать, что мы сделаем немного обучения, чтобы PyTorch мог ее изучить. Э-э, а затем мы увеличим это до полного набора после того, как мы выполним шаг компиляции. Так что следующий шаг - сделать это. Мы скомпилируем модель. мы заставим ее пройти через это начальное количество эпох, чтобы она могла прогреться и подготовиться для нас, а затем мы изменим это здесь. Э-э, теперь мы передадим ей количество эпох из командной строки или значение по умолчанию, которое является полным набором. Э-э, а затем мы запустим ее снова. Так что у нас есть эти два шага обучения. Первый здесь, который предназначен только для прогрева PyTorch. Затем мы проведем полное обучение, и вы можете видеть, что мы передаем модель, токенизатор, набор данных, и эта конфигурация - это наши параметры, а затем мы просто запускаем trainer.train, и он берет все оттуда. Этот процесс занимает, в зависимости от размера вашего набора данных, в зависимости от размера вашей модели, для тех, которые я использую для трехмиллиардной модели и небольшого набора данных, потому что это всего лишь информация о Raspberry Pi. Это заняло несколько минут, менее 10 минут, чтобы пройти полностью. Так что опять же, это очень мощный бокс. У него есть, я даже не помню, куча ядер CUDA. Я хочу сказать, вроде 144 ядра CUDA или что-то в этом роде. Плюс многоядерный процессор ARM. Так что это действительно очень мощный бокс, и он может быстро обрабатывать эти наборы данных. Вот что делает его отличным для тонкой настройки моделей. Вам не нужно платить за облачный экземпляр с подключенным GPU и платить почасовую ставку за это. Вы можете просто иметь этот бокс, стоящий на вашем столе или в вашей лаборатории, и вы можете продолжать итеративно обучать и тонко настраивать свои модели, пока они не станут именно такими, какими вы хотите их видеть. [откашливается] Хорошо, это основная часть скрипта. Опять же, я оставил кое-что, некоторые вещи, которые не очень важны. Это стандартный Python, но вы можете просмотреть весь скрипт, если хотите. Хорошо, наш следующий шаг - мы собираемся получить этот набор данных Raspberry Pi, который мы создали. Мы просто загрузим его с серверов ARM. И чтобы дать вам пример того, как это выглядит, мы напечатаем немного этого. Надеюсь, это достаточно крупно, чтобы увидеть на видео. Э-э, но вы можете видеть, что у нас есть просто этот простой JSON. Он содержит три элемента на строку. Инструкция, ввод и вывод. И это то, что мы будем использовать, чтобы поместить в нашу строку шаблона, а затем обучить нашу модель. В нем, я хочу сказать, около 300 элементов. Так что это не очень большой набор данных, но он очень, очень специфичен для Raspberry Pi, что нам и нужно для нашей конечной модели. Это команда, которую мы собираемся запустить в контейнере, который мы начали ранее. Она запустит скрипт тонкой настройки. Мы передадим ей имя модели, которое мы хотим, это llama 3 3.2, последние модели llama, 3-миллиардная модель с инструкциями. Вы можете начать с обычной модели 3.23B. Э-э, без обучения инструкциям, однако, она просто пытается выполнить завершение текста. Она не понимает концепцию вопроса и ответа или инструкции и ответа. Модель, обученная на наборе инструкций, лучше справляется с этим, что делает ее хорошей отправной точкой для нас. Но если бы вы хотели сделать свою собственную, вы могли бы начать с этой базовой модели, а затем выполнить тонкую настройку со стандартным набором инструкций, таким как Alpaca, который Nvidia использует по умолчанию. чтобы получить модель инструкций, а затем выполнить тонкую настройку, специфичную для Raspberry Pi. Мы просто вырезаем этот промежуточный шаг, используя модель, обученную на инструкциях, которую предоставляет Meta. Поскольку мы передаем локальный набор данных вместо использования одного из hugging face, нам нужно указать, какого он типа. Так что нам нужно сказать, что это JSON-файл. Нам нужно указать, где найти этот файл. И затем здесь мы говорим использовать 300 элементов из этого. И я думаю, что это количество параметров или количество записей в этом наборе данных в любом случае. Так что здесь вы бы установили меньшее количество, если бы использовали что-то действительно большое. А затем мы указываем выходной каталог. Так что мы отправим это в наш новый выходной каталог, который находится здесь локально и содержит нашу тонко настроенную модель в качестве вывода. Так что мы скопируем это. И я собираюсь запустить это. И это займет несколько минут. Так что я сначала отключу свой телефон. [откашливается] После этого я собираюсь посмотреть в чат и посмотреть, есть ли какие-либо вопросы, на которые я могу ответить, пока это работает. И вы можете наблюдать, как это работает. Она загрузит модель. А затем она запустит шаги тонкой настройки для каждого из этих элементов из набора данных. И вы можете просто наблюдать, как вывод прокручивается. Хорошо. Я собираюсь прокрутить обратно к началу. Хорошо, Генри спросил: "Какое программное обеспечение вы используете?" Итак, я использую стандартную операционную систему, которая поставляется с DGX Spark. Это система на базе Ubuntu. Э-э, мы будем использовать, мы используем Docker, и образ Docker уже содержит все библиотеки PyTorch. Дополнительно мы устанавливаем некоторые библиотеки hugging face. Э-э, а затем сам скрипт был в том репозитории, который вы видели, как я клонировал в начале этого. Посмотрим, что еще. Зависит ли это от архитектуры модели? Э-э, так что это устройство в облако. Могу ли я использовать тот же скрипт для других весовых моделей? Так что он специфичен для моделей в формате PyTorch. Э-э, вы не сможете использовать это напрямую на, скажем, gguf, просто потому, что скрипт был настроен для использования моделей в формате PyTorch, но в рамках этого вы должны иметь возможность использовать любую из них и любые наборы данных. Э-э, он предполагает, что ваш набор данных имеет формат инструкции, ввода и вывода. Если он отличается, вам нужно будет изменить скрипт, чтобы он соответствовал этому другому формату набора данных, но концепция та же, и библиотеки, которые вы используете, должны быть теми же для любой модели PyTorch, которая у вас есть. Так что да, вы должны иметь возможность использовать это. Я уже использовал это на паре разных моделей и паре разных наборов данных. Э-э, так что я знаю, что это гибко, по крайней мере, в пределах моих попыток. Если вы попробуете что-то, и это не сработает, не стесняйтесь связаться со мной. Э-э, у нас есть сообщество Discord, и я опубликую ссылку на него после того, как закончу. Э-э, но я в нашем Discord. Вы можете связаться со мной там в любое время. Э-э, есть также Discord Nvidia, если вы являетесь его частью. Я тоже там, и вы можете спросить меня там. [хмыкает] Хорошо. Device to cloud также спрашивает, применим ли процесс только к LLM, или он применим к диффузионным и другим моделям. Э-э, концепции применимы к диффузионным моделям. Скрипт и настройка действительно специфичны для LLM и текстовых LLM. Э-э, так что вы можете использовать диффузионные модели, вы можете использовать, скажем, визуальные модели. Данные, которые вам придется предоставить и как вы будете предоставлять их для тонкой настройки, будут немного отличаться от этого. Э-э, отличные вопросы, и, возможно, это хорошая тема для меня, чтобы осветить в будущем код-семинаре - показать, как использовать этот процесс, но для разных форматов, а не только для текстовых LLM. Хорошо. Э-э, кто-то спросил: "Вы используете DJI Spark?" Я предполагаю, вы имеете в виду DGX Spark. И да, у меня есть один из золотых боксов Nvidia. Э-э, я не могу его поднять, потому что он сейчас весь подключен, и я не хочу рисковать отключением чего-либо. Э-э, но да, у меня есть один из их красивых золотых боксов, и я играюсь с ним уже несколько месяцев и действительно получаю удовольствие от того, что могу с ним делать. Хорошо, наша тонкая настройка завершена. Так что я оставлю остальные вопросы и вернусь к код-семинару. Хорошо, мы закончили. Мы сохранили его в нашем выходном каталоге здесь. Теперь пришло время посмотреть, чего мы смогли достичь с этим. Так что мы вернемся к нашему учебному плану, перейдем к следующему шагу. Теперь, чтобы запустить его, я просто выбрал использовать VLM. Вы можете использовать любой сервер вывода, который хотите. Э-э, я знал, что VLM будет работать. Nvidia предоставила действительно хороший готовый образ Docker для него. Так что, э-э, именно поэтому я выбрал его, но вы можете использовать что-то другое, если хотите. Э-э, это действительно просто этап тестирования. Так что на данном этапе не так уж важно, что вы используете. Когда вы перейдете в продакшн, будут соображения о том, что вы хотите использовать в качестве сервера вывода, но для тестирования VLM работает отлично. Хорошо. Так что мы собираемся запустить этот контейнер. И на самом деле, я собираюсь выйти из этого, чтобы мы не запускали два одновременно. Э-э, потому что я предупрежу вас, если вы запускаете большую модель на сервере вывода в одном контейнере, а затем пытаетесь запустить другую задачу тонкой настройки в другом контейнере, вы очень быстро столкнетесь с нехваткой памяти, и Ubuntu зависнет. Так что постарайтесь этого не делать. Э-э, я делал это пару раз, прежде чем понял, где я ошибаюсь. Э-э, так что закройте свой контейнер PyTorch, а затем запустите контейнер VLM. Хорошо, так что очень похожий процесс. Мы монтируем каталог конфигурации/кэша hugging face. Так что снова у нас есть эти модели, которые мы загрузили из hugging face. Мы монтируем наш текущий каталог в workspace, чтобы у нас также была доступна наша новая модель. Ключевое отличие здесь - это перенаправление портов. Так что мы будем перенаправлять порты 8000 в контейнер, чтобы мы могли напрямую взаимодействовать с этим сервером вывода, потому что по умолчанию Docker будет держать его закрытым, и вы не сможете общаться с ним извне контейнера Docker. Хорошо, чтобы получить представление о том, с чего мы начали, мы запустим VLM с оригинальным llama 3.2 из hugging face. Так что это команда для этого. Мы передаем ей модель. Э-э, здесь мы скажем использовать только 80% памяти, потому что у этого бокса унифицированная память, разделяемая между GPU и CPU. Если вы скажете ему использовать 100% доступной памяти GPU, он будет использовать 100% памяти на боксе. И снова, вы столкнетесь с нехваткой памяти. Ubuntu зависнет, и у вас будут проблемы. Так что убедитесь, что вы устанавливаете ограничение. Ubuntu по умолчанию не использует так много. Так что если это только он и VLLM, вы можете использовать до 80% этого и быть в порядке. Так что вот что мы собираемся сделать. И это займет минуту, пока VLM загружает модель, читает все параметры и запускает сервер вывода и готовит его к ответам на запросы. [хмыкает] И для этого я открою новую вкладку. И э-э, это мы можем запустить вне контейнера, потому что мы сделали перенаправление портов. Это наш тестовый запрос, который мы собираемся использовать. Мы просто будем использовать базовый конечный узел API OpenAI. Э-э, и мы дадим ему запрос. Немного настройки в запросе, которая говорит, знаете ли, это инструкции для описания задачи. Напишите соответствующий ответ. Здесь вы можете видеть, что мы начинаем с формата, на котором мы ее обучили. Так что эта хэш-инструкция, мы просто спросим ее, сколько памяти имеет RP 2350. Так что это будет наш начальный запрос. И посмотрим. Хорошо, мы все еще загружаем сервер вывода. Это обычно занимает пару минут. Э-э, это займет больше времени, если ваша модель больше. Э-э, но все равно не должно занять много времени, чтобы добраться до момента, когда она будет готова начать отвечать. Хорошо, вот оно. Запуск приложения завершен. Это то, что мы хотим видеть. Теперь мы можем отправить эту команду curl и посмотреть, что мы получим в ответ. [хмыкает] Я должен был прогнать это через jq. Посмотрим, сможем ли мы получить хороший формат. Вот оно. Так вот ответ. мы получаем довольно длинный ответ, который дает нам некоторую совершенно неверную информацию о том, что процессор имеет емкость 4 мегабайта. Это не так. Э-э, так что вы можете видеть, что результат, который вы получаете от общей модели, может ответить на вопрос, но он не обязательно может ответить на него правильно, и это действительно сводит на нет смысл использования модели для этой цели. Так что именно поэтому нам нужно тонко настраивать модель, чтобы она могла отвечать правильно. Так что нашей следующей задачей будет попробовать нашу новую модель. Так что мы закроем VLM. Мы снова загрузим ее. Та же команда, что и раньше, только на этот раз мы говорим ей использовать нашу локальную тонко настроенную модель вместо той, которую мы загрузили из hugging face. Так что мы сделаем это, и вы, вероятно, столкнетесь с ошибкой, и я покажу вам, что это за ошибка и что с ней делать. Э-э, так что если вы получите эту ошибку Tokenizer class value error, это потому, что когда мы сохранили модель, конфигурация токенизатора, которую она сохранила, просто использовала общий бэкенд токенизатора, который не распознается. Так что вместо этого нам нужно изменить конфигурацию, которую она сгенерировала, чтобы указать на правильный бэкенд, тот же, который использовался в нашей оригинальной модели. И для этого нам нужно изменить только одну строку в этой конфигурации. Мы изменим tokenizer class с tokenizer backend на этот pre-trained tokenizer fast. И эта команда сделает это для вас. Вы можете изменить ее вручную, если хотите, потому что это всего лишь простая однострочная вещь. Но мы внесем это изменение. И теперь мы можем снова запустить VLLM, и он запустится. Я не совсем уверен, почему он не сохранял ее с исходным значением для этого класса токенизатора. Э-э, если кто-нибудь знает ответ, пожалуйста, дайте мне знать, и я могу изменить скрипт, чтобы, возможно, исправить это. Так что снова, нам нужно подождать, пока VLM загрузит это. Э-э, ему нужно загрузить все параметры и снова запустить сервер, и это займет несколько минут, что означает, что это хорошее время для меня, чтобы вернуться и посмотреть в чат. Хорошо, T. Sizzle говорит: "Как вы оцениваете, что тонко настроенная модель действительно лучше для вашего сценария использования и что она не переобучена?" Так что именно этим мы сейчас и занимаемся. Мы тестируем новую модель, чтобы увидеть, дает ли она нам тот вывод, который мы ожидаем. Я просто тестирую ее с парой запросов. Вам, вероятно, понадобится более надежная система тестирования, чтобы убедиться, что она правильно отвечает на более широкий спектр вопросов. А также убедиться, что она не делает других вещей, которые вы не хотите, чтобы она делала. Это все выходит за рамки этого учебного плана. Это может быть хорошей темой для будущего учебного плана или код-семинара. А затем, вторая часть этого вопроса: есть ли какие-либо преимущества для целей тонкой настройки путем кластеризации двух DGX Spark? Я имею в виду, у вас вдвое больше оборудования, если вы кластеризуете два из них вместе. Это одна из приятных особенностей этих боксов, что вы можете соединить их через высокоскоростное соединение и распределить этот процесс обучения между ними. Так что если вы используете очень большую модель, что-то вроде более 100 миллиардов параметров, или вы выполняете тонкую настройку на огромном, огромном наборе данных, тогда вы, безусловно, получите от этого выгоду. Но, как вы видели, с размером модели и набора данных, которые я использую для этого, он работает достаточно быстро. Я мог бы даже использовать гораздо большую модель и больший набор данных и просто дать ему работать час или два, и это совершенно нормально. Тонкая настройка - это не то, что вам обязательно нужно делать мгновенно. Так что это действительно вопрос, знаете ли, насколько вы терпеливы, ожидая, пока он пройдет через прогон. Я думаю, что итерация с меньшей моделью и набором данных, пока вы не поймете, что вы на правильном пути, а затем запуск на большей модели - это действительно хороший способ. И возможность итерировать это на этом боксе делает его очень приятным опытом. Хорошо, VLLM снова работает. Так что мы посмотрим, получим ли мы лучший ответ на этот раз. Мы собираемся использовать тот же запрос, те же параметры, тот же вопрос, и посмотрим, что он нам даст на этот раз, и я забыл прогнать его через jq снова. Хорошо, теперь вы видите, что мы получаем гораздо меньший, гораздо более лаконичный ответ. Э-э, я считаю, что это все еще неправильный ответ. Так что еще есть над чем поработать, но это гораздо ближе. Я думаю, 520K согласно документации - это правильный ответ. Да. Так что мы ближе, но мы не обязательно точно там. Еще есть над чем работать, но вы видите, что у нас есть очень разный ответ, и он гораздо ближе к правильному ответу для этого. Э-э, и мы можем попробовать другие запросы. Э-э, здесь мы спросим его, какие есть варианты памяти для модуля Raspberry Pi Compute. Так что это не просто сам процессор, а вся плата Raspberry Pi, которую вы купите. Э-э, и там вы можете видеть, что это более точно, что вы можете получить ее в моделях объемом 1, 2, 4 и 8 гигабайт для самой платы. Э-э, так что опять же, это лучший ответ, чем вы получили бы от простого стандартного общего LLM. Хорошо, я надеюсь, что это вдохновило некоторых из вас попробовать это самим. Э-э, с тонко настроенными моделями можно сделать гораздо больше, чем с общими, и вы получите гораздо меньше того, о чем люди обычно жалуются: галлюцинаций, неточных данных. Э-э, так что если вы создаете что-то специфичное для вашего продукта, бота поддержки или чат-бота, э-э, или даже если вы используете это для каких-то вещей, вам действительно нужно, чтобы это было более точным, чем общая модель. Существует ряд наборов данных на hugging face, которые вы можете использовать. Э-э, вы также можете создать свой собственный, как вы видели, они очень простые. Э-э, вы можете сделать это. Это просто простой JSON-файл с вводом и выводом, и использовать его в качестве обучающих данных. Так что я призываю всех вас попробовать это, чтобы увидеть, что вы можете сделать со своими собственными моделями и своими собственными данными, и, пожалуйста, дайте нам знать. Э-э, я действительно сказал, что у нас есть Discord, так что я найду ссылку на него и вставлю ее в чат для вас. Э-э, на самом деле, позвольте мне сделать это сейчас. Э-э, а пока я верну Лакшми, и мы сможем пройтись и посмотреть, какие были вопросы. Э-э, прежде всего, Лакшми, я должен спросить, есть ли у вас какие-либо вопросы ко мне, основанные на этом, потому что вы впервые видите, как я это делаю, не так ли? Да, я думаю, это было очень, очень хорошо. Э-э, охватило все концепции: от настройки данных, затем прохождения через все конфигурации SFT, а затем настройки для обучения и затем выполнения вывода через VLM. Это как сквозной рабочий процесс того, как любой пользователь, который захочет попробовать тонкую настройку, будет использовать его. Так что я думаю, это действительно, действительно хороший показ. Да. >> Хорошо. Я собираюсь просмотреть остальные комментарии здесь. Э-э, Сто спрашивает: "Как я могу использовать 4 узла GB10 для тонкой настройки?" Э-э, если вы говорите о стекировании четырех из них вместе, я не думаю, что вы можете. Я думаю, вы можете сделать только два вместе одновременно. Э-э, так что если вам нужно больше, вам придется использовать другую форму кластеризации, или в этом случае, если вам нужно столько вычислительного оборудования, вероятно, в ваших интересах просто арендовать облачный узел, который имеет, скажем, полный GB200 или что-то в этом роде. Э-э, и просто платить почасовую стоимость за это. Посмотрим. О. О, Лакс, у тебя был О, нет. Ты отвечаешь на вопросы там. Неважно. Я пропущу это. Посмотрим. Можем ли мы использовать модель llama и обучить ее, чтобы получить из нее больше? Но скажем, модель llama имеет интеллект 10, и мы обучаем на большом количестве данных. Улучшится ли интеллект до 20 или 30? Так что это не так, сложно измерить интеллект модели таким образом. Что вы делаете, это вы делаете модель более точной для конкретных данных, на которых вы ее обучаете. Да. >> Э-э, так что, например, в этом примере мы сделали модель гораздо более интеллектуальной, когда дело доходит до Raspberry Pi, но это не обязательно делает ее в целом гораздо более интеллектуальной, чем базовая модель. Так что базовая модель - это как стандартный общий набор всей информации, которую мог найти первоначальный тренер. И то, что вы делаете сейчас, это вы даете ей специализацию. Э-э, по сути, это как если бы вы проходили специальные курсы в университете, чтобы сосредоточиться на своей степени, а не на общем образовании, что вы делаете здесь. Э-э, посмотрим. Сто спрашивает о сервере Discord, и да, я дам вам его. Э-э, Стеф задает один вопрос: есть ли также стабильный образ SG lang, специально для Spark? Э-э, я предполагаю, вы имеете в виду образ Docker. Лакшми. Вы случайно не знаете это навскидку, есть ли у Nvidia приватный образ для SG Lang? >> Э-э, я не могу вспомнить навскидку об образе, но я публикую playbook, который размещен на нашем веб-сайте. Так что, вероятно, это может дать им некоторое направление о том, как начать работу с SG lang. Хорошо. >> Коди спрашивает: можем ли мы затем поместить модель в NVFP4, чтобы увидеть повышенную производительность? Э-э, так что да, если люди не знакомы с этим, это тип квантования для уменьшения размера, который занимает отдельный параметр на диске и в памяти. Э-э, так что да, квантование будет шагом, который вы сделаете после этого. Как только вы тонко настроите свою модель по своему вкусу, вы можете начать уменьшать размеры этих параметров, чтобы получить гораздо меньшую, гораздо более быструю модель. И квантование, как правило, не теряет столько точности в выводе. Так что это отличный способ повысить производительность, не теряя много в процессе. У нас есть учебные планы о квантовании и о том, как это делать. Э-э, так что да, опять же, как только вы дойдете до этого момента, вы перейдете к одному из них. Вы можете квантовать его до Nvidia FP4, или вы можете сделать int4, или что угодно еще, что вы хотите. Я слышал, даже Microsoft теперь имеет 2-битный формат квантования, который они могут использовать. Так что да, множество возможностей для вас.

после того, как вы выполнили этап тонкой настройки. Хорошо, Мелвин говорит: «Когда вы выполняете тонкую настройку LLM? Для каких вариантов использования это имеет смысл?» Итак, вы должны выполнить тонкую настройку, когда будете готовы к тому, чтобы она знала конкретную информацию. Часто это будет информация о продукте, бизнесе, услуге, что-то, где вы создаете продукт, и ему нужно знать больше об этом продукте. Чтобы сделать это, вы создаете набор данных с информацией о вашем продукте и пропускаете его через тонкую настройку, а затем у вас есть модель, которая специфична для вашего варианта использования и ваших потребностей. Итак, вы сделаете это после того, как скажете: «Начните с одной из этих базовых моделей». Вы не хотите создавать LLM с нуля, что требует огромного количества данных и огромного количества ресурсов, которых у вас нет, если вы не являетесь гигантской компанией, такой как Meta. Но поскольку такие компании, как Meta и Google, открывают исходный код или предоставляют открытые модели с весами, которые вы можете скачать и использовать в качестве отправной точки, это означает, что 99% работы уже сделано за вас, и теперь вы просто настраиваете это под свои нужды. И это большая экономия средств для вас, верно? Имеет ли тонкая настройка смысл только для небольших моделей, потому что большие уже очень умны? Опять же, большие модели, как правило, умны, но не специфически умны. И на самом деле, меньшие модели, такие как эта трехмиллиардная модель Pariah Vendor, начинаются как более крупные. И существует процесс обрезки параметров, чтобы попытаться сохранить тот же уровень вывода, используя при этом меньше параметров для достижения этой цели. Но данные, на которых они обучаются, одни и те же. Так что, независимо от того, начинаете ли вы с большой модели или с маленькой, вы все равно начинаете с общих данных, и вы хотите превратить их в специфические данные. Вот что такое процесс тонкой настройки. Вы видели репозиторий Auto Search от Карпати? Карпати. Может быть, он мог бы работать на Spark. Я не знаком с этим. Так что я не могу ответить. Я уверен, что он может работать на Spark. Он может работать или не работать со скриптами, которые я предоставил, потому что опять же, они специфичны для моделей в стиле PyTorch. Но да, Spark — это графический процессор Nvidia и процессор ARM. Практически все, что связано с ИИ, будет работать на нем. Верно. Сколько у нас времени? Приближаемся к концу. О, есть отличный вопрос. Итак, Мелвин спрашивает: «Значит, это лучше, чем RAG?» Я бы сказал, что это отличается от RAG, и в зависимости от ваших потребностей одно может быть лучше другого. Одним из недостатков RAG является то, что вам приходится иметь отдельную базу данных, и объем информации, которую вы можете извлечь из этой базы данных и передать модели, ограничен размером контекста, с которым вы работаете. Таким образом, вместо того, чтобы иметь возможность использовать все данные из вашего обучающего набора, вы сможете передать только небольшую часть из них. И поэтому процесс RAG использует что-то вроде векторной базы данных, чтобы определить, какие части этого набора данных будут наиболее релевантными, и передать их в качестве части подсказки для общей модели. Это очень легкий способ сделать это. И если у вас есть данные, которые часто меняются, это действительно хороший вариант, так что вам не придется постоянно переобучать и донастраивать ваши модели. Но если у вас есть статический набор данных, такой как эти спецификации Raspberry Pi, имеет смысл просто донастроить вашу модель для этого, чтобы у вас был доступ ко всему этому, независимо от подсказки. >> [хмыкает] >> Так что опять же, зависит от вашего варианта использования, ваших потребностей и ваших ресурсных ограничений, лучше ли RAG или тонкая настройка. И нет ничего, что говорило бы о том, что вы не можете сделать и то, и другое. Вы можете иметь донастроенную модель, а также RAG. Таким образом, вы можете иметь свою статическую информацию, запеченную в модель, и вы можете иметь свою более динамичную информацию, поступающую через RAG, и получить лучшее из обоих миров. Хорошо. Парень, занимающийся логистикой ИИ, спрашивает: «Как часто вы возвращаетесь и донастраиваете?» Это действительно полностью зависит от ваших данных для тонкой настройки и ваших потребностей. Если он меняется, и вы хотите использовать новые данные, вам придется вернуться и переобучиться. Как часто вы это делаете, зависит от того, как часто эти данные меняются, и это действительно будет сильно варьироваться от одного варианта использования к другому. Хорошо. Похоже, последний вопрос для вас, Лакшми. Что такое тонкая настройка модели в контексте ИИ и отличается ли она от обучения модели с нуля? Это очень общий вопрос. >> Да. Я бы сказал, что на первую часть вы ответили несколько раз. По сути, вам понадобится тонкая настройка, когда вы хотите сделать что-то специфичное для домена. Например, пример, который Майкл показал здесь, был связан с информацией, специфичной для Raspberry Pi. Для более легкого понимания мы могли бы подумать, например, о чем-то, связанном с медициной, и модели, которые обычно имеют открытый исходный код, обучаются на всех данных в Интернете. Так что это, по сути, обучение модели с нуля. Вы даете ей всю информацию, которая есть в Интернете. Так что она становится своего рода мастером на все руки. Она знает немного обо всем. Но когда вы хотите, чтобы она стала мастером в одной конкретной области, вы выполняете тонкую настройку. Что-нибудь еще, что вы хотите добавить, Майкл? >> Нет, я думаю, это был отличный ответ. >> Итак, я сейчас поделюсь парой ссылок. >> Первый — это наш Discord. Это приглашение на конкретный канал. У нас есть общий канал NVIDIA в Discord разработчиков ARM. Мы недавно запустили совместную инициативу программы разработчиков между ARM и NVIDIA, и в рамках этого мы размещаем каналы в нашем Discord, специфичные для такого рода взаимодействия. Я разместил ссылку в чате. Но затем я также дам просто общее приглашение на сервер, если я смогу его быстро найти. О, они изменили его. Раньше его было очень легко найти. Знаете что? Я просто скажу: найдите ARM Developer Program или зайдите на developer.arm.com. Там вы найдете ссылки на сервер Discord. Зарегистрируйтесь в нашей программе разработчиков, и мы отправим вам ссылки на нее и на всевозможные другие вещи, такие как предстоящие кодовые сессии. Это будет самый простой способ для вас получить все это. А затем у Nvidia также есть страница программы разработчиков. Лакшми, вы хотите рассказать всем, где это найти? >> Да. Я думаю, Зак, вероятно, разместит ссылки в чате, но для DJX Spark вы можете зайти на сайт playbook, чтобы ознакомиться со всеми playbook, которые вы можете запускать на Spark, а также вы можете зайти на форум разработчиков. В случае каких-либо вопросов вы можете напрямую связаться с командой, и люди на форуме разработчиков свяжут нас, и мы сможем ответить на больше вопросов. >> Хорошо. И это build.nvidia.com/spark, если я правильно помню. >> Да. >> Хорошо. Так что все, заходите, посмотрите. Присоединяйтесь к каналам Discord, которые я разместил. Посмотрите нашу совместную программу сообщества, и дайте нам знать. Я хочу услышать от кого-нибудь, кто попробует запустить это самостоятельно, и посмотреть, какие результаты вы получите и для каких интересных вариантов использования вы создаете модели. И снова, это ежемесячная серия кодовых сессий, которые мы проводим. Мы вернемся в следующем месяце с другими. Я не помню точные темы, которые мы обсуждаем в следующем месяце, но если вы зарегистрируетесь в программе разработчиков ARM, мы отправим вам напоминания о том, что это такое и когда это будет, и вы сможете посмотреть. Хорошо, я надеюсь, все нашли это информативным, и спасибо всем за то, что присоединились и смотрели нас, и спасибо Лакшми и Заку за то, что были здесь и помогали с этим. Мы с нетерпением ждем возможности поговорить с вами позже. >> До свидания всем. >> [музыка] [музыка]