Hace un par de semanas asistí al Gemma Developer Day de Google. Un día dedicado a que Google presente las capacidades, portabilidad y apertura de sus modelos LLM más nuevos llamados Gemma.
Estos modelos presentan una nueva y emocionante fase para la IA generativa. Modelos lo suficientemente pequeños como para implementarse en dispositivos locales y aún así pueden brindar una experiencia atractiva y útil.
Ha pasado un tiempo desde que miré Mobile AI y terminé el día sintiéndome inspirado. Decidí ver qué pasaba cuando probé estos modelos en una aplicación de Android.
Quería darle a Gemma un desafío fácil pero novedoso. Jugando un juego de Simón Dice.
Las reglas son simples. Un jugador llega a ser Simon. Su función es dar tareas a realizar a los demás jugadores. El jugador que juega como Simón tiene que decir "Simón dice" antes de realizar la tarea.
Creé una aplicación con tres pantallas. Una pantalla de entrada, una pantalla de instrucciones y una pantalla de chat donde Gemma puede comunicarse y dar tareas.
Para acelerar la creación de la pantalla de chat, encontré esta publicación de blog de Meyta Taliti extremadamente útil.
Con las pantallas creadas mi siguiente tarea fue integrar a Gemma. Para ello me basé en un conjunto de herramientas que aprendí llamado MediaPipe.
MediaPipe es una colección de herramientas cuyo objetivo es simplificar la integración de modelos de IA en aplicaciones de Android, iOS y la web. Con MediaPipe tienes muchas opciones según tus necesidades.
Si desea comenzar rápidamente, MediaPipe proporciona una API llamada Tareas para que pueda llamar a los modelos de IA. Estas API se dividen en diferentes áreas, como visión, texto y audio.
MediaPipe también proporciona una colección de modelos previamente entrenados para integrarlos en sus aplicaciones. Nuevamente, útil para comenzar rápidamente.
Si necesita algo más personalizado y no desea crear un modelo desde cero, MediaPipe proporciona una herramienta llamada Model Maker . Model Maker utiliza un proceso llamado Transfer Learning para volver a entrenar un modelo de aprendizaje automático existente y proporcionarle nuevos datos. El beneficio de este enfoque es que ahorra tiempo y requiere menos datos de entrenamiento para crear un nuevo modelo.
Model Maker también puede reducir el tamaño del modelo creado mediante este proceso. Tenga en cuenta que este proceso hace que el modelo "olvide" parte de su conocimiento existente.
La última herramienta de MediaPipe es MediaPipe Studio , una aplicación web para evaluar y modificar sus modelos personalizados. Útil si desea comparar sus modelos y comprender qué tan bien funcionan antes de la implementación.
Para nuestras necesidades, aprovecharemos la API LLM Interfence, una nueva API para MediaPipe. Esto nos permite comunicarnos con Gemma y recibir una respuesta.
Para usar MediaPipe, primero debes agregarlo como una dependencia de Gradle a la aplicación:
implementation ("com.google.mediapipe:tasks-genai:0.10.11")
A continuación, crea una instancia de LlmInference
. Este es el objeto que utilizas para comunicarte con Gemma:
val llmInference = LlmInference.createFromOptions( context, LlmInference.LlmInferenceOptions.builder() .setModelPath("/data/local/tmp/llm/gemma-2b-it-cpu-int8.bin") .build() )
Es importante tener en cuenta la ruta establecida mediante .setModelPath
. Aquí es donde reside el modelo Gemma en el dispositivo. También es importante que el modelo Gemma utilizado sean las versiones gemma-2b
. Las versiones 7b
aún no son compatibles con MediaPipe; hablaremos más sobre lo que esto significa más adelante. Por ahora descarguemos el modelo.
Puedes descargar Gemma desde Kaggle . Un sitio web dedicado a los científicos de datos y el aprendizaje automático. Debe crear una cuenta y aceptar los Términos y condiciones de uso antes de poder descargar los modelos. Puedes encontrar la página de Gemma aquí .
Si sigues esta publicación, recuerda descargar solo las versiones gemma-2b-it-cpu
del modelo, en la pestaña TensorFlow Lite
. Estás solo si pruebas las versiones gemma-2b-it-gpu
.
Una vez descargado el modelo. Utilice el Explorador de dispositivos en Android Studio para importar el modelo a la ruta establecida en .setModelPath
. Si ha cambiado la ruta o el nombre del modelo, asegúrese de actualizar el nombre de la ruta.
Una vez importado el modelo, puede comenzar a pasar indicaciones a Gemma utilizando el método .generateResponse
. Aquí hay un ejemplo del mensaje que le paso a Gemma para jugar Simon Says:
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)
Si ha utilizado LLM antes y tiene conocimientos básicos de Prompt Engineering, esto debería resultarle familiar. Para pecar de cauteloso, he incluido instrucciones de precaución en el mensaje. ¡No queremos que Simon le pida al usuario que haga nada cuestionable!
Si intenta ejecutar esto en un dispositivo, pueden suceder un par de cosas:
La aplicación puede tardar un poco en responder y eventualmente brindar una respuesta.
La aplicación puede fallar. Al buscar en Logcat, verá mensajes indicando que MediaPipe no puede encontrar el modelo. ¿Estableciste la ruta del modelo correcta?
La aplicación puede fallar. Si observa Logcat, puede ver una gran cantidad de registros de código nativo e información sobre el reciclaje de la memoria.
Mi experiencia cayó en la segunda y tercera categoría. Tus propias experiencias pueden variar si has configurado todo correctamente y utilizas un dispositivo físico de alta especificación.
Si no tienes éter de estas cosas. Hay otra opción, aumentar la cantidad de RAM disponible a través del emulador.
Aumentar la cantidad de RAM disponible generalmente ayuda en un entorno con uso intensivo de memoria, entonces, ¿por qué un LLM con mucha memoria sería diferente? Para hacer esto, personalicé la cantidad de RAM que usaba mi emulador de Android.
Si tiene un emulador existente, puede notar que el campo RAM está deshabilitado. Aún puedes actualizar la cantidad de RAM que tiene disponible haciendo clic en los tres puntos a la derecha en el Administrador de dispositivos.
Haga clic en Mostrar en disco y luego abra los archivos config.ini y hardware-qemu.ini en un editor de texto. Cambie los valores de hw.ramSize
en cada archivo. Gracias a esta pregunta de Stack Overflow por darme la respuesta sobre cómo hacerlo.
Alternativamente, puede crear un emulador personalizado yendo al Administrador de dispositivos en Android Studio, haciendo clic en Crear dispositivo virtual y luego en Nuevo perfil de hardware . Como parte de las opciones para personalizar puedes seleccionar la cantidad de RAM.
Encontré que 8 GB de RAM funcionan relativamente bien. También probé suerte con 22 GB de RAM. Funciona ligeramente mejor en términos de velocidad, aunque no tanto como esperaba.
Sospecho que hay un cuello de botella en algún lugar cuando Gemma se carga en la memoria, ya que el resto del emulador se ejecuta con fluidez. Quizás se pueda hacer una mejora en algún lugar.
Los modelos Gemma compatibles con MediaPipe son las versiones gemma-2b
. El 2b
representa 2 mil millones de parámetros. La cantidad de parámetros que trabajan juntos para hacer que el modelo funcione.
Estos son los valores establecidos dentro del modelo durante el entrenamiento para proporcionar conexiones e inferencias entre sí cuando le haces una pregunta a Gemma.
También hay una colección gemma-7b
, que utiliza 7 mil millones de parámetros. Sin embargo, MediaPipe no los admite. ¡Tal vez algun dia!
Si está interesado en comprender más sobre los parámetros cuando se trata de LLM, le recomiendo esta página .
Tener un modelo de 2 mil millones de parámetros cargado y ejecutándose en un dispositivo móvil es un logro impresionante. ¿Qué tan bien funciona? Vamos a averiguar.
gemma-2b-it-cpu-int4
es un LLM de 4 bits. Esto significa que cada parámetro utilizado por el modelo tiene un tamaño de memoria de 4 bits. El beneficio aquí es que el tamaño total del modelo es menor; sin embargo, el tamaño reducido de la memoria para cada parámetro significa que la precisión y la calidad del modelo también se ven afectadas.
Entonces, ¿cómo funciona gemma-2b-it-cpu-int4
? No es tan bueno para ser honesto. Aquí hay algunas capturas de pantalla de mis intentos de jugar Simon Says usando el mensaje anterior y haciéndole preguntas generales.
Las respuestas fueron inesperadas y fue frustrante lograr que el modelo hiciera algo parecido a un juego de Simon Says. Se desviaría hacia un tema diferente y alucinaría con información inexacta.
Las alucinaciones son un fenómeno en el que los LLM dicen falsedades y cosas mentirosas como si fueran hechos. Tomemos el ejemplo anterior: no es cierto que puedas conducir a Marte en 60 minutos a 60 mph. No todavía, de todos modos. 😃
También hubo una falta de conciencia del contexto. Lo que significa que no podía recordar algo que mencioné anteriormente en una conversación. Probablemente esto se deba al tamaño limitado del modelo.
Después de un tiempo, abandoné este modelo y decidí probar el modelo más grande de 8 bits.
gemma-2b-it-cpu-int8
es un LLM de 8 bits. Es de mayor tamaño que su hermano de 4 bits. Lo que significa que puede ser más preciso y proporcionar respuestas de mejor calidad. Entonces, ¿cuál fue el resultado aquí?
Este modelo supo captar la idea de Simón Dice, asumiendo inmediatamente el papel de Simón. Desafortunadamente, también adolece de una falta de conciencia del contexto.
Para contrarrestar esto, necesitaba volver a incitar al modelo cada vez con las reglas de Simon Says y combinarlo con otro mensaje para pedirle que realizara una tarea.
Las indicaciones de las tareas se seleccionan aleatoriamente de una lista para pasarlas a Gemma, lo que brinda cierta variedad en las tareas que se solicitan.
A continuación se muestra un ejemplo de lo que está sucediendo a continuación:
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)
Ocasionalmente arroja una respuesta de bola curva que parece fuera de lugar. Lo atribuyo al tamaño del modelo. También vale la pena considerar que esto es solo la v1.
Una vez que las indicaciones quedaron grabadas en piedra, descubrí que era útil confiar únicamente en las indicaciones y no tener en cuenta las aportaciones del usuario. Debido a que el modelo carece de conocimiento del contexto, la entrada del usuario hace que deje de reproducir Simon Says y en su lugar responda a la entrada.
Agregar este pequeño truco no fue un resultado satisfactorio, pero era necesario mantener a Gemma jugando a Simon Says.
Entonces, ¿Gemma puede jugar a Simon Says en un dispositivo Android? Voy a decir "más o menos, con ayuda".
Me gustaría que la versión de 4 bits de Gemma 2b respondiera de forma más intuitiva. También sería útil hacer que Gemma 2b sea consciente del contexto para evitar la necesidad de volver a solicitarlo para cada solicitud y tener cuidado con la entrada del usuario.
Para solicitudes simples que necesitan solo un mensaje. Puedo ver que Gemma 2b puede realizar cómodamente estas tareas.
También vale la pena tener en cuenta que se trata de la versión 1 de los modelos. ¡El hecho de que se ejecuten y funcionen en un sistema operativo móvil es un logro impresionante!
¿Qué pasa con el futuro de los LLM en dispositivos móviles? Hay dos barreras que veo. Limitaciones de hardware y casos de uso práctico.
Creo que estamos en un punto en el que sólo los dispositivos de alta gama pueden ejecutar estos modelos de forma eficaz. Los dispositivos que me vienen a la mente son las series de teléfonos Pixel 7 o Pixel 8 con sus chips Tensor G y el iPhone de Apple con su chip Neural Engine.
Necesitamos ver este tipo de especificaciones filtrándose a los teléfonos de gama media.
Podrían surgir ideas interesantes a partir de LLM en dispositivos que aprovechen la generación aumentada de recuperación . Una técnica para que los LLM se comuniquen con fuentes de datos externas para recuperar contexto adicional al brindar respuestas. Esta podría ser una forma eficaz de aumentar el rendimiento.
La segunda barrera es encontrar casos de uso prácticos. Creo que son limitados, mientras que los dispositivos pueden comunicarse con LLM más potentes a través de Internet. ¡Se rumorea que GPT-4 de OpenAI, por ejemplo, admite más de un billón de parámetros!
Sin embargo, podría llegar un momento en el que el costo de implementar estos modelos en dispositivos móviles sea más barato que alojarlos en la nube. Dado que la reducción de costos está de moda en estos días, puedo ver que este es un caso de uso viable.
También existen los beneficios de privacidad de tener su propio LLM personal, sin que ninguna información salga de los límites de su dispositivo. Un beneficio útil que atraerá a los usuarios de aplicaciones preocupados por la privacidad.
Mi apuesta es que todavía faltan algunos años para que los LLM se implementen regularmente en el dispositivo.
Si desea probar Gemma usted mismo en un dispositivo móvil, aquí tiene algunos recursos que le ayudarán:
Gemma : el sitio web oficial de Gemma contiene una gran cantidad de información que incluye puntos de referencia, guías de inicio rápido e información sobre el enfoque de Google para el desarrollo responsable de la IA generativa.
MediaPipe : MediaPipe tiene su propia sección de Desarrolladores de Google donde puedes aprender más sobre él y cómo usarlo. Lectura muy recomendable.
Discord del grupo de desarrolladores de Google : El grupo de desarrolladores de Google Discord tiene canales dedicados a la IA generativa. Consulte los canales #gemma, #gemini y #ml para chatear con personas con ideas afines.
Aplicación Simons Says: clona y ejecuta el código de muestra de esta publicación de blog para verla en acción. También incluye el uso de la tarea de clasificación de imágenes de MediaPipe. Las instrucciones de configuración se encuentran en el archivo LÉAME.
Actualizado el 23/03/24 para mencionar llamar a la inferencia LLM desde un hilo IO
Después de escribir esta publicación, se me ocurrió que llamar a Gemma es una operación de lectura/escritura en un archivo. Mover el método .generateResponse()
a un subproceso IO evitará el inmenso bloqueo cuando gemma se carga en la memoria:
suspend fun sendMessage(): String { return withContext(Dispatchers.IO) { val prompt = prompts.random() llmInference.generateResponse(prompt) } }
También aparece aquí .