Пару недель назад я посетил День разработчиков Gemma от Google. День, посвященный презентации Google возможностей, портативности и открытости новейшей модели LLM под названием Gemma.
Эти модели представляют собой новый захватывающий этап развития генеративного искусственного интеллекта. Модели достаточно малы, чтобы их можно было развернуть на локальных устройствах, но при этом могут предоставлять интересный и полезный опыт.
Прошло много времени с тех пор, как я смотрел Mobile AI и ушел с того дня, чувствуя себя вдохновленным. Я решил посмотреть, что произойдет, если я опробую эти модели в приложении для Android.
Я хотел дать Джемме простой, но новый вызов. Играем в игру «Саймон говорит».
Правила просты. Одним игроком станет Саймон. Их роль — давать другим игрокам задания для выполнения. Игрок, играющий за Саймона, должен сказать «Саймон говорит», прежде чем дать задание.
Я создал приложение с тремя экранами. Экран входа, экран инструкций и экран чата, где Джемма может общаться и давать задания.
Чтобы ускорить создание экрана чата, я нашел эту запись в блоге Мейты Талити чрезвычайно полезной.
После создания экранов моей следующей задачей стала интеграция Джеммы. Для этого я использовал набор инструментов, о которых узнал, под названием MediaPipe.
MediaPipe — это набор инструментов, цель которого — упростить интеграцию моделей искусственного интеллекта в приложения на Android, iOS и в Интернете. С MediaPipe у вас есть большой выбор в зависимости от ваших потребностей.
Если вы хотите быстро приступить к работе, MediaPipe предоставляет API под названием Tasks , позволяющий вызывать модели искусственного интеллекта. Эти API разделены на различные области, такие как видение, текст и аудио.
MediaPipe также предоставляет коллекцию предварительно обученных моделей для встраивания в ваши приложения. Опять же, полезно для быстрого начала работы.
Если вам нужно что-то более индивидуальное и вы не хотите создавать модель с нуля, MediaPipe предоставляет инструмент Model Maker . Model Maker использует процесс под названием Transfer Learning для переобучения существующей модели машинного обучения и предоставления ей новых данных. Преимущество этого подхода заключается в том, что он экономит время и требует меньше обучающих данных для создания новой модели.
Благодаря этому процессу Model Maker также может уменьшить размер созданной модели. Обратите внимание, что этот процесс заставляет модель «забывать» некоторые из имеющихся у нее знаний.
Последний инструмент MediaPipe — MediaPipe Studio , веб-приложение для оценки и настройки ваших пользовательских моделей. Полезно, если вы хотите протестировать свои модели и понять, насколько хорошо они работают перед развертыванием.
Для наших нужд мы собираемся использовать LLM Interfence API, новый API для MediaPipe. Это позволяет нам общаться с Джеммой и получать ответ.
Чтобы использовать MediaPipe, вам сначала нужно добавить его в качестве зависимости Gradle к приложению:
implementation ("com.google.mediapipe:tasks-genai:0.10.11")
Затем вы создаете экземпляр LlmInference
. Это объект, который вы используете для общения с Джеммой:
val llmInference = LlmInference.createFromOptions( context, LlmInference.LlmInferenceOptions.builder() .setModelPath("/data/local/tmp/llm/gemma-2b-it-cpu-int8.bin") .build() )
Важно отметить путь, установленный с помощью .setModelPath
. Здесь на устройстве находится модель Джеммы. Также важно, чтобы используемая модель Gemma была версией gemma-2b
. Версии 7b
еще не поддерживаются MediaPipe, подробнее о том, что все это означает, позже. А пока давайте скачаем модель.
Вы можете скачать Gemma с Kaggle . Веб-сайт, посвященный специалистам по данным и машинному обучению. Вам необходимо создать учетную запись и принять Условия использования, прежде чем вы сможете загрузить модели. Вы можете найти страницу Джеммы здесь .
Если вы следите за этим постом, не забудьте загружать только версии модели gemma-2b-it-cpu
на вкладке TensorFlow Lite
. Вы сами по себе, если попробуете версии gemma-2b-it-gpu
.
После загрузки модели. Используйте Обозреватель устройств в Android Studio, чтобы импортировать модель по пути, указанному в .setModelPath
. Если вы изменили путь или имя модели, обязательно обновите имя пути.
После импорта модели вы можете начать передавать запросы в Gemma, используя метод .generateResponse
. Вот пример подсказки, которую я передаю Джемме, чтобы она сыграла Саймона Сэйса:
private const val SimonSaysPrompt = """ You are a Simon in a game of Simon Says. Your objective is to ask the player to perform tasks. For every task you give, you must prefix it with the words "Simon says". You must not ask the player to do anything that is dangerous, unethical or unlawful. Do not try to communicate with the player. Only ask the player to perform tasks. """ val gemmaResponse = llmInference.generateResponse(SimonSaysPrompt)
Если вы раньше использовали LLM и имеете базовое понимание оперативного проектирования, это должно выглядеть знакомо. Чтобы проявить осторожность, я включил в подсказку инструкции по мерам предосторожности. Мы не хотим, чтобы Саймон просил пользователя сделать что-то сомнительное!
Если вы попытаетесь запустить это на устройстве, может произойти несколько вещей:
Приложению может потребоваться некоторое время, чтобы ответить и в конечном итоге предоставить ответ.
Приложение может выйти из строя. Заглянув в Logcat, вы увидите сообщения о том, что MediaPipe не может найти модель. Вы установили правильный путь модели?
Приложение может выйти из строя. Если вы посмотрите в Logcat, вы увидите множество журналов собственного кода и информацию о переработке памяти.
Мой опыт попал во вторую и третью категорию. Ваш собственный опыт может отличаться, если вы все настроили правильно и используете физическое устройство с высокими характеристиками.
Если у вас нет эфира этих вещей. Есть еще один вариант — увеличить объем оперативной памяти, доступной через эмулятор.
Увеличение объема доступной оперативной памяти обычно помогает в средах с интенсивным использованием памяти, так почему же LLM, требующий большого объема памяти, должен быть другим? Для этого я настроил объем оперативной памяти, используемый моим эмулятором Android.
Если у вас есть существующий эмулятор, вы можете заметить, что поле RAM отключено. Вы по-прежнему можете обновить объем доступной оперативной памяти, щелкнув три точки справа от нее в диспетчере устройств.
Нажмите «Показать на диске» , а затем откройте файлы config.ini и hardware-qemu.ini в текстовом редакторе. Измените значения hw.ramSize
в каждом файле. Спасибо этому вопросу о переполнении стека за то, что он дал мне ответ о том, как это сделать.
Альтернативно вы можете создать собственный эмулятор, перейдя в Диспетчер устройств в Android Studio, нажав «Создать виртуальное устройство» , а затем «Новый профиль оборудования» . В рамках опций настройки вы можете выбрать объем оперативной памяти.
Я обнаружил, что 8 ГБ ОЗУ работают относительно хорошо. Я также попытал счастья с 22 ГБ оперативной памяти. Он работает немного лучше с точки зрения скорости, хотя и не так сильно, как я ожидал.
Я подозреваю, что при загрузке Gemma в память где-то есть узкое место, поскольку остальная часть эмулятора работает плавно. Возможно, где-то можно сделать улучшение.
Модели Gemma, совместимые с MediaPipe, представляют собой версии gemma-2b
. 2b
означает 2 миллиарда параметров. Количество параметров, работающих вместе, чтобы модель работала.
Это значения, установленные в модели во время обучения, чтобы обеспечить связи и выводы между собой, когда вы задаете Джемме вопрос.
Существует также коллекция gemma-7b
, в которой используется 7 миллиардов параметров. Однако они не поддерживаются MediaPipe. Возможно, однажды!
Если вы хотите больше узнать о параметрах LLM, я рекомендую эту страницу .
Загрузка и запуск модели с 2 миллиардами параметров на мобильном устройстве — впечатляющее достижение. Насколько хорошо это работает? Давай выясним.
gemma-2b-it-cpu-int4
это 4-битный LLM. Это означает, что каждый параметр, используемый моделью, имеет размер памяти 4 бита. Преимущество здесь заключается в том, что общий размер модели меньше, однако уменьшенный размер памяти для каждого параметра означает, что это также влияет на точность и качество модели.
Так как же работает gemma-2b-it-cpu-int4
? Не так уж здорово, если честно. Вот несколько скриншотов моих попыток сыграть в Simon Says, используя приведенную выше подсказку и задавая общие вопросы.
Ответы были неожиданными, и было неприятно заставить модель делать что-то, напоминающее игру Саймона Сэйса. Это отклонялось бы на другую тему и галлюцинировало неточную информацию.
Галлюцинации — это явление, когда магистры права говорят неправду и неправдивые вещи, как если бы они были фактами. Возьмем приведенный выше пример: это неправда, что вы можете доехать до Марса за 60 минут со скоростью 60 миль в час. Во всяком случае, пока нет. 😃
Также наблюдалось отсутствие понимания контекста. Это означает, что он не мог вспомнить то, о чем я упоминал ранее в разговоре. Вероятно, это связано с ограниченным размером модели.
Через некоторое время я отказался от этой модели и решил попробовать более крупную 8-битную модель.
gemma-2b-it-cpu-int8
это 8-битный LLM. Он больше по размеру, чем его 4-битный брат. Это означает, что он может быть более точным и давать более качественные ответы. Так каков был здесь результат?
Эта модель смогла уловить идею Саймона Сэйса, сразу взяв на себя роль Саймона. К сожалению, он также страдал от недостатка понимания контекста.
Чтобы противостоять этому, мне нужно было каждый раз повторно запрашивать модель с помощью правил Саймона Сэйса и объединять ее с другим приглашением, чтобы попросить ее предоставить задачу.
Подсказки к задачам случайным образом выбираются из списка и передаются в Джемму, что придает некоторое разнообразие задаваемым задачам.
Вот пример того, что происходит ниже:
private const val SimonSaysPrompt = """ You are a Simon in a game of Simon Says. Your objective is to ask the player to perform tasks. For every task you give, you must prefix it with the words "Simon says". You must not ask the player to do anything that is dangerous, unethical or unlawful. Do not try to communicate with the player. Only ask the player to perform tasks. """ private const val MovePrompt = SimonSaysPrompt + """ Give the player a task related to moving to a different position. """ private const val SingASongPrompt = SimonSaysPrompt + """ Ask the player to sing a song of their choice. """ private const val TakePhotoPrompt = SimonSaysPrompt + """ Give the player a task to take a photo of an object. """ private val prompts = listOf( MovePrompt, SingASongPrompt, TakePhotoPrompt ) val prompt = prompts.random() val response = llmInference.generateResponse(prompt)
Иногда он дает кривую реакцию, которая кажется нетипичной. Я связываю это с размером модели. Также стоит учитывать, что это только v1.
Как только подсказки были высечены в камне, я обнаружил, что полезно полагаться только на подсказки и не принимать во внимание действия пользователя. Поскольку модели не хватает контекстной поддержки, пользовательский ввод приводит к тому, что она прекращает воспроизведение Simon Says и вместо этого реагирует на ввод.
Добавление этой хитрости не принесло удовлетворения, но нужно было продолжать играть Джемму в роли Саймона Сэйса.
Так может ли Джемма сыграть в Simon Says на устройстве Android? Я скажу «вроде как с помощью».
Мне бы хотелось, чтобы 4-битная версия Gemma 2b реагировала более интуитивно. Также будет полезно сделать Gemma 2b контекстно-зависимой, чтобы избежать необходимости повторно запрашивать ее для каждого запроса, и быть осторожным с пользовательским вводом.
Для простых запросов требуется только одно приглашение. Я вижу, что Джемма 2b сможет с легкостью справиться с этими задачами.
Также стоит иметь в виду, что это версия v1 моделей. Тот факт, что они работают на мобильной операционной системе, является впечатляющим достижением!
А как насчет будущего LLM на мобильных устройствах? Я вижу два препятствия. Аппаратные ограничения и варианты практического использования.
Я думаю, что мы находимся на этапе, когда только устройства высокого класса могут эффективно работать с этими моделями. На ум приходят устройства серии Pixel 7 или Pixel 8 с чипами Tensor G и iPhone от Apple с чипом Neural Engine.
Нам нужно, чтобы подобные характеристики распространялись на телефоны среднего класса.
Интересные идеи могут появиться на LLM-устройствах, использующих Retrival Augmented Generation . Методика LLM для связи с внешними источниками данных для получения дополнительного контекста при предоставлении ответов. Это может быть эффективным способом повысить производительность.
Второй барьер — поиск практических вариантов использования. Я думаю, что они ограничены, хотя устройства могут связываться с более мощными LLM через Интернет. Например, по слухам, GPT-4 от OpenAI поддерживает более триллиона параметров!
Однако может наступить момент, когда стоимость развертывания этих моделей на мобильных устройствах станет дешевле, чем их размещение в облаке. Поскольку сегодня в моде сокращение затрат, я вижу, что это жизнеспособный вариант использования.
Имея свой собственный LLM, вы также получаете преимущества конфиденциальности, при этом никакая информация не выходит за пределы вашего устройства. Полезное преимущество, которое понравится пользователям приложений, заботящимся о конфиденциальности.
Могу поспорить, что до того, как LLM будут регулярно внедряться на устройствах, осталось еще несколько лет.
Если вы хотите попробовать Gemma на мобильном устройстве, вот несколько ресурсов, которые могут вам помочь:
Джемма : Официальный веб-сайт Gemma содержит множество информации, включая тесты, краткие руководства и информацию о подходе Google к ответственной разработке генеративного ИИ.
MediaPipe : MediaPipe имеет собственный раздел Google Developer , где вы можете узнать больше о нем и о том, как его использовать. Настоятельно рекомендуется к прочтению.
Discord группы разработчиков Google : Discord группы разработчиков Google имеет специальные каналы, посвященные генеративному искусственному интеллекту. Посетите каналы #gemma, #gemini и #ml, чтобы пообщаться с единомышленниками.
Приложение Simons Says: клонируйте и запустите пример кода для этой записи в блоге, чтобы увидеть его в действии. Он также включает использование задачи классификации изображений из MediaPipe. Инструкции по установке находятся в README.
Обновлено 23.03.24, чтобы упомянуть вызов вывода LLM из потока ввода-вывода.
После написания этого поста мне пришло в голову, что вызов gemma — это операция чтения/записи файла. Перемещение метода .generateResponse()
в поток ввода-вывода позволит избежать огромных зависаний при загрузке драгоценного камня в память:
suspend fun sendMessage(): String { return withContext(Dispatchers.IO) { val prompt = prompts.random() llmInference.generateResponse(prompt) } }
Также появляется здесь .