Gemma 3n:移动设备全栈AI
运行复杂的 AI 工作流,例如将语音转换为文本(STT)、调用函数、使用视觉语言模型(VLM)生成视觉洞察力以及合成语音(TTS),完全在移动设备上进行不再是未来的想法;它正在成为现实。这一转变不仅仅是一项技术成就,它代表了一个新的 AI 可访问性、隐私性和响应性的时代。随着用户期望的变化和数据隐私问题的加深,在设备上处理 AI 避免了对云的依赖,减少了延迟,并将敏感数据保留在其来源地。这是朝着民主化 AI 的重要一步,使其更快、更安全、更个性化。对于开发人员和决策者来说,这不仅仅是优化,而是重塑我们与智能系统日常互动的方式。
在这篇博客文章中,我们将探讨如何在移动设备上完全运行 完整的 AI 栈,涵盖从语音到文本(STT)、函数调用、视觉语言模型(VLM)推理到文本到语音(TTS)的完整 Android 应用程序实现。随着 Gemma 3n 的发布,该模型现在支持图像输入,以及 MediaPipe 库的最新更新,使得本地函数调用成为可能,完全在设备上的 AI 工作流不再只是理想,它们已经到来。我们将逐步介绍每个组件,展示所有这些任务如何在几秒钟内在移动硬件上执行,其性能令人惊讶地接近传统的基于云的管道。
1、语音到文本(STT)
今天与移动设备交互比以往任何时候都更加直观,通常从简单的语音命令开始。处理语音输入最有效的开源模型之一是 OpenAI 的 Whisper。存在两年多的 Whisper 被证明既可靠又多功能,能够处理从转录到翻译的各种任务。它有多种模型大小以适应不同的性能需求,对于这个项目,我们选择了轻量级的 Tiny English 版本,非常适合在设备上执行,而不会牺牲日常使用的准确性。
在移动应用程序中,您会找到 Whisper 的完整实现,从加载 .bin 词汇文件到初始化 TensorFlow Lite 解释器以进行设备上推理。该模型由 Mel 频谱图输入提供动力,后者使用 C++ 计算以实现更快和更高效的执行。这种设置确保了速度和准确性,使得实时转录在移动硬件上成为可能:
JNIEXPORT jfloatArray JNICALL  
Java_com_example_jetsonapp_whisperengine_WhisperEngine_transcribeFileWithMel(JNIEnv *env,  
                                                                             jobject thiz,  
                                                                             jlong nativePtr,  
                                                                             jstring waveFile,  
                                                                             jfloatArray filtersJava) {  
    talkandexecute *engine = reinterpret_cast<talkandexecute *>(nativePtr);  
    const char *cWaveFile = env->GetStringUTFChars(waveFile, NULL);  
    // 第一步:从 jfloatArray 获取原生数组  
    jfloat *nativeFiltersArray = env->GetFloatArrayElements(filtersJava, NULL);  
    jsize filtersSize = env->GetArrayLength(filtersJava);  
    // 第二步:将原生数组转换为 std::vector<float>  
    std::vector<float> filtersVector(nativeFiltersArray, nativeFiltersArray + filtersSize);  
    // 释放原生数组  
    env->ReleaseFloatArrayElements(filtersJava, nativeFiltersArray, JNI_ABORT);  
    // 调用引擎方法以转录音频文件并获取结果作为 float 向量  
    std::vector<float> result = engine->transcribeFileWithMel(cWaveFile, filtersVector);  
    env->ReleaseStringUTFChars(waveFile, cWaveFile);  
    // 将结果向量转换为 jfloatArray  
    jfloatArray resultArray = env->NewFloatArray(result.size());  
    env->SetFloatArrayRegion(resultArray, 0, result.size(), result.data());  
    return resultArray;  
}
您可以在这里查看初始化过程并了解 Whisper 如何在 Android 应用程序中运行 这里。
2、函数调用(FC)
MediaPipe 最近发布了 指南,介绍了如何直接在移动设备上实现函数调用!除了文档外,他们还提供了 GitHub 示例,任何人都可以构建并在 Android 手机上部署。此快速入门演示利用了 LLM 推理 API 和 Hammer 2.1 (1.5B),提供了一种简便的方法来开始。以下是任务的基本初始化和使用示例:
private fun createGenerativeModel(): GenerativeModel {  
        val getCameraImage = FunctionDeclaration.newBuilder()  
            .setName("getCameraImage")  
            .setDescription("打开相机的功能")  
            .build()  
        val openPhoneGallery = FunctionDeclaration.newBuilder()  
            .setName("openPhoneGallery")  
            .setDescription("打开相册的功能")  
            .build()  
        val tool = Tool.newBuilder()  
            .addFunctionDeclarations(getCameraImage)  
            .addFunctionDeclarations(openPhoneGallery)  
            .build()  
        val formatter =  
            HammerFormatter(ModelFormatterOptions.builder().setAddPromptTemplate(true).build())  
        val llmInferenceOptions = LlmInferenceOptions.builder()  
            // hammer2.1_1.5b_q8_ekv4096.task  
            // gemma-3n-E2B-it-int4.task  
            .setModelPath("/data/local/tmp/Hammer2.1-1.5b_seq128_q8_ekv1280.task")  
            .setMaxTokens(512)  
            .apply { setPreferredBackend(Backend.GPU) }  
            .build()  
        val llmInference =  
            LlmInference.createFromOptions(context, llmInferenceOptions)  
        val llmInferenceBackend =  
            LlmInferenceBackend(llmInference, formatter)  
        val systemInstruction = Content.newBuilder()  
            .setRole("system")  
            .addParts(  
                Part.newBuilder()  
                    .setText("你是一个有用的助手,可以打开相机或手机相册。")  
            )  
            .build()  
        val model = GenerativeModel(  
            llmInferenceBackend,  
            systemInstruction,  
            listOf(tool).toMutableList()  
        )  
        return model  
    }  
....  
val chat = generativeModel?.startChat()  
        val response = chat?.sendMessage(userPrompt.value)  
        Log.v("function", "Model response: $response")  
        if (response != null && response.candidatesCount > 0 && response.getCandidates(0).content.partsList.size > 0) {  
            val message = response.getCandidates(0).content.getParts(0)  
            // 如果消息包含函数调用,则执行相应的函数。  
            if (message.hasFunctionCall()) {  
                val functionCall = message.functionCall  
                    // 调用适当的函数。  
                    when (functionCall.name) {  
                        "getCameraImage" -> {  
                            Log.v("function", "getCameraImage")  
                            _cameraFunctionTriggered.value = true  
                            updateJetsonIsWorking(false)  
                        }  
                        "openPhoneGallery" -> {  
                            Log.v("function", "openPhoneGallery")  
                            _phoneGalleryTriggered.value = true  
                            updateJetsonIsWorking(false)  
                        }  
                        else -> {  
                            Log.e("function", "没有要调用的函数")  
                            withContext(Dispatchers.Main) {  
                                Toast.makeText(  
                                    context,  
                                    "没有要调用的函数,请说类似“打开相机”的话",  
                                    Toast.LENGTH_LONG  
                                ).show()  
                            }  
                            updateJetsonIsWorking(false)  
                        }  
                    }  
....
一旦任务成功识别出一个函数,就会触发相应的操作,例如打开相机。更多内容请参阅应用程序中的 JetsonViewModel.kt 文件。用户然后可以通过相机 API 与之交互以捕获图像,该图像无缝传递到视觉语言模型(VLM)中以进一步处理和生成见解。
3、视觉语言模型(VLM)
备受期待的 Gemma 3n 模型终于发布了,提供两种变体,2B 和 4B,两者均具备视觉支持。虽然多家公司和开发者之前已经推出了自己的视觉语言模型,但 这篇文章 探讨了为什么 Gemma 作为一个生产级选择脱颖而出,重点在于性能、效率和安全性。
MediaPipe 是第一个从第一天起就支持执行 Gemma 3n 模型的库。凭借内置的选项可以在 CPU 或 GPU 上运行模型,以及其标志性的高级 API,将 Gemma 3n 集成到 Android 项目中非常流畅且简单,提供了可靠的性能,没有任何意外的障碍。
private fun createSession(context: Context): LlmInferenceSession {  
        // 配置推理选项并创建推理实例  
        val options = LlmInferenceOptions.builder()  
            .setModelPath("/data/local/tmp/gemma-3n-E2B-it-int4.task")  
            .setMaxTokens(1024)  
            .setPreferredBackend(Backend.GPU)  
            .setMaxNumImages(1)  
            .build()  
        val llmInference = LlmInference.createFromOptions(context, options)  
        // 配置会话选项并创建会话  
        val sessionOptions = LlmInferenceSession.LlmInferenceSessionOptions.builder()  
            .setTopK(40) // 默认值  
            .setTopP(0.9f)  
            .setTemperature(1.0f)  
            .setGraphOptions(GraphOptions.builder().setEnableVisionModality(true).build())  
            .build()  
        return LlmInferenceSession.createFromOptions(llmInference, sessionOptions)  
    }  
....  
val mpImage = BitmapImageBuilder(bitmap).build()  
        session?.addQueryChunk(userPrompt.value + " in 20 words") // 如果不想输出太多,可以限制。  
        session?.addImage(mpImage)  
        var stringBuilder = ""  
        session?.generateResponseAsync { chunk, done ->  
            updateJetsonIsWorking(false)  
            stringBuilder += chunk  
            // Log.v("image_partial", "$stringBuilder $done")  
            updateVlmResult(transcribedText.trim() + "\n\n" + stringBuilder)  
....
您可以在 此文件 中查看代码。
4、文本到语音(TTS)
虽然视觉语言模型(VLM)的输出是文本,但该项目更进一步,添加了 离线文本到语音(TTS) 功能,使整个语音响应无需互联网连接即可完成。一个关键的中间步骤涉及 检测 VLM 输出的语言。这确保系统可以动态加载适当的发音,特别是当输出的语言不是英语时。由于 Gemma 3 和 3n 提供了超过 140 种语言 的内置支持,此功能为未来的多语言语音交互奠定了基础。语言检测使用的是 ML Kit 的语言识别 API。
private val languageIdentifier = LanguageIdentification.getClient()  
private fun speakOut(text: String) {  
    val defaultLocale = Locale("en")  
    languageIdentifier.identifyLanguage(text)  
        .addOnSuccessListener { languageCode ->  
            val locale = if (languageCode == "und") defaultLocale else Locale(languageCode)  
            textToSpeech.setLanguage(locale)  
            // Log.v("available_languages", textToSpeech.availableLanguages.toString())  
            textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, null, "speech_utterance_id")  
        }  
        .addOnFailureListener {  
            textToSpeech.setLanguage(defaultLocale)  
            textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, "speech_utterance_id")  
        }  
}
您可以在这里了解更多关于 Android API 的 TextToSpeech。
观看一段在功能齐全的 三星 S24 上录制的短片,该设备拥有 12GB 内存,允许函数调用和 VLM 推理模型在 GPU 上平稳运行。
本项目中使用的模型总结:
- Whisper Tiny English 模型用于语音到文本(下载 vocab.bin,whisper.tflite 并将其放入 assets 文件夹)
 - Hammer 2.1 1.5B 用于函数调用 (下载)
 - Gemma 3n 2B 用于 VLM (下载)
 - ML KIT 语言识别 API
 
您可以直接从这个分支构建 项目。
5、结束语
这个项目展示了设备上 AI 走得多远,将语音到文本、函数调用、视觉语言建模和文本到语音整合到一个移动应用程序中。借助 Whisper、Hammer 和 Gemma 3n 等开源模型,以及 MediaPipe 和 ML Kit 等框架,现在可以完全离线交付智能、多模式的体验。除了展示技术可行性外,这个端到端的移动 AI 栈还强调了实际影响:减少延迟、增强隐私,并通过不依赖云的方式实现更丰富、以语音驱动的交互。随着 AI 更加个人化并嵌入我们的日常设备,构建本地执行的解决方案已不再是例外情况。
原文链接:Gemma 3n models made possible the full AI stack entirely on mobile!
汇智网翻译整理,转载请标明出处