MediaPipe Tasks 在 Android 中的生成式 AI 实践:基于 Gemma 3n 的移动端

目录

  1. 引言
  2. MediaPipe 简介
  3. Android 环境搭建与使用
  4. Gemma 3n 功能特性
  5. 多模态支持与高级功能
  6. 移动端 AI 的优势与前景
  7. 总结与展望
  8. 参考资源

引言

随着人工智能技术的快速发展,移动端 AI 应用需求日益增长。Google 的 MediaPipe 作为一个开源的机器学习框架,为开发者提供了强大的工具集来构建跨平台的 AI 应用。本文将深入探讨 MediaPipe Tasks 在 Android 平台上的应用,特别是基于最新的 Gemma 3n 模型实现生成式 AI 任务。

Gemma 3n 是 Google 最新发布的生成式 AI 模型,专门为手机、笔记本电脑和平板电脑等移动设备优化。该模型采用了参数高效处理创新技术,包括:

  • 每层嵌入 (PLE) 参数缓存:通过分层参数复用降低内存占用
  • MatFormer 模型架构:针对移动设备优化的轻量级 Transformer 结构

这些技术能够灵活地降低计算和内存要求,在移动端生成内容的速度和 AI 能力上都表现很优秀。

MediaPipe 简介

MediaPipe 是一套工具和库,它可以让我们在移动端快速部署人工智能 (AI) 和机器学习 (ML)模型。

核心组件

MediaPipe 包含以下核心组件:

  1. MediaPipe Tasks:提供跨平台 API 和库,用于部署各种 AI 解决方案
  2. MediaPipe 模型:预训练的即用型模型,支持多种 AI 任务
  3. MediaPipe Model Maker:自定义模型训练工具
  4. MediaPipe Studio:可视化评估和对比解决方案的浏览器工具

MediaPipe Model Maker

MediaPipe Model Maker 是一款用于自定义机器学习模型的工具,它通过新数据对已有模型进行重新训练来实现定制。
这款工具适用范围较广,能用于多种传统 AI 任务的模型定制,比如对象检测、手势识别,以及图片、文本、音频数据的分类器等。
需要注意的是,它与后面会介绍的 LoRA 模型定制有所不同:LoRA 主要面向生成式 AI 领域,尤其专注于大语言模型(LLM)的定制化。

MediaPipe Studio

MediaPipe Studio 是一款网页版工具,专门用来调试和定制能在设备端运行的机器学习模型及流程。
用它的时候,你可以直接在浏览器里上传自己的数据、用自己定制的机器学习模型,快速测试 MediaPipe 的各种解决方案(比如手势识别、图像分类等)。
简单说,它就像一个在线工作台,不用复杂配置,打开就能直观地评估模型效果,或者根据自己的需求调整模型和运行流程,很适合快速验证想法。

支持的任务类型

MediaPipe Tasks 支持的功能和平台如下表格所示:

解决方案 Android 网页 Python iOS 自定义模型
LLM Inference API
对象检测
图片分类
图片分割
交互式细分
手部地标检测
手势识别
图片嵌入
人脸检测
人脸特征点检测
人脸风格化
姿势特征点检测
图片生成
文本分类
文本嵌入
语言检测器
音频分类

第三方模型生态

LiteRT Community 平台是一个专为移动端优化的模型分发平台,提供预转换的.task 格式模型,这种格式可以使用 MediaPipe LLM Inference API 直接加载使用,避免开发者手动转换。模型库中主要包含以下热门的生成式 AI 模型:

  • Gemma 系列:Gemma-3 1B、Gemma-3 2B、Gemma 3n 等
  • Deepseek 系列:针对代码生成和推理优化的模型
  • Qwen 系列:阿里巴巴的多语言大模型,如 Qwen2.5-1.5B-Instruct
  • SmolVLM:轻量级多模态视觉语言模型
  • MedGemma:医疗领域专用模型

图片生成模型

MediaPipe 虽然支持文本到图像生成功能,但第三方的模型并不完善,需要转换。兼容的模型主要有以下几个,这些模型只能实现生成固定的风格,比如 hakurei/waifu-diffusion-v1-4 可以生成动漫风格图片。这些模型需要转换成 .task 格式才能在 MediaPipe 中使用。

推荐的兼容模型:

  • runwayml/stable-diffusion-v1-5:主推荐模型,稳定性和质量最佳
  • justinpinkney/miniSD:轻量级版本,适合移动端
  • hakurei/waifu-diffusion-v1-4:动漫风格图片生成
  • Fictiverse/Stable_Diffusion_PaperCut_Model:纸切艺术风格

设备端转换流程:

  1. 从 Hugging Face 下载基础模型后,使用 image_generator_converter 将模型转换为适用于图片生成器的设备端格式

  2. 安装必要的依赖项:

$ pip install torch typing_extensions numpy Pillow requests pytorch_lightning absl-py
  1. 运行转换脚本:
$ python3 convert.py --ckpt_path <ckpt_path> --output_path <output_path>

转换完成后,就可以在 MediaPipe 应用中使用图片生成功能,通过文本提示创建各种风格的图像。


Android 环境搭建与使用

项目依赖配置

首先,在 Android 项目中添加 MediaPipe Tasks 依赖:

// app/build.gradle
dependencies {
    // LLM 推理专用依赖
    implementation 'com.google.mediapipe:tasks-genai:0.10.24'

    // 其他 MediaPipe 任务依赖
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
    implementation 'com.google.mediapipe:tasks-text:latest.release'
    implementation 'com.google.mediapipe:tasks-audio:latest.release'
}

各包功能说明:

  • tasks-genai专门用于生成式 AI 任务(LLM 推理、多模态推理等)
  • tasks-vision:用于视觉任务(对象检测、图像分类、人脸识别等)
  • tasks-text:用于传统文本任务(文本分类、文本嵌入、语言检测等)
  • tasks-audio:用于音频任务(音频分类等)

模型下载与部署

从 Hugging Face 下载.task 格式的Gemma 3n 模型或从 LiteRT Community 选择其他适合的模型。

模型文件说明:

  • 典型的 LLM 模型大小在 3GB-5GB 左右
  • 由于文件过大,不建议直接打包到 APK 中

可以直接使用 ADB 推送模型到手机本地目录,或者部署到服务端供用户下载到手机本地目录。


Gemma 3n 功能特性

核心功能

基于 Gemma 3n 的 LLM Inference API 主要功能包括:

  • 完全设备端运行:无需网络连接,保护用户隐私
  • 智能文本生成:根据输入提示生成各类高质量文本内容
  • 多模态输入支持:同时支持文本和图像输入,实现图像理解与文本生成
  • 广泛语言支持:支持超过 140 种语言,可以翻译各种语言
  • 大上下文处理:32K token 上下文,处理复杂长文档

配置参数详解

LLM Inference API 提供了丰富的配置选项来优化模型性能:

选项名称 说明 值范围 默认值 建议用法
modelPath 模型在项目目录中的存储路径 路径字符串 绝对路径
maxTokens 模型处理的词元数量上限(输入+输出) 整数 512 根据设备性能调整,一般 256-1024
topK 每个步骤考虑的高概率词元数量 整数 40 较小值提高一致性,较大值增加多样性
temperature 生成过程中的随机性程度 0.0-2.0 0.8 0.7-0.9 适合大多数应用
maxNumImages 最大可以识别的图片数量 整数 1 一次识别一张
randomSeed 文本生成的随机种子 整数 0 固定种子可确保结果可重现

代码实现

LLM 管理器

class LLMManager(private val context: Context) {
    private var llmInference: LlmInference? = null

    suspend fun initializeModel(modelPath: String) {
        val options = LlmInference.LlmInferenceOptions.builder()
            .setModelPath(modelPath)        // 指定模型文件路径
            .setMaxTokens(1024)             // 设置最大处理token数(输入+输出)
            .setTopK(40)                    // 限制候选词数量,提高输出质量
            .setTemperature(0.8f)           // 控制输出随机性,0.8适合对话任务
            .setRandomSeed(101)             // 设置随机种子,确保结果可重现
            .build()

        llmInference = LlmInference.createFromOptions(context, options)
    }

    suspend fun generateText(prompt: String): String {
        return try {
            val response = llmInference?.generateResponse(prompt)
            response?.text() ?: "生成失败"
        } catch (e: Exception) {
            Log.e("LLMManager", "推理失败: ${e.message}")
            "推理出错: ${e.message}"
        }
    }

    suspend fun generateStreamingText(
        prompt: String,
        callback: (String) -> Unit
    ) {
        try {
            llmInference?.generateResponseAsync(prompt) { result ->
                result.text()?.let { callback(it) }
            }
        } catch (e: Exception) {
            Log.e("LLMManager", "流式推理失败: ${e.message}")
        }
    }

    fun release() {
        llmInference?.close()
        llmInference = null
    }
}

多模态支持与高级功能

图像预处理

图片需要转换为 MPImage 对象才能识别,下面是转换方法:

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

class ImageProcessor {

    fun convertBitmapToMPImage(bitmap: Bitmap): MPImage {
        return BitmapImageBuilder(bitmap).build()
    }

    fun loadImageFromAssets(context: Context, fileName: String): MPImage {
        val inputStream = context.assets.open(fileName)
        val bitmap = BitmapFactory.decodeStream(inputStream)
        return BitmapImageBuilder(bitmap).build()
    }

    fun loadImageFromUri(context: Context, uri: Uri): MPImage? {
        return try {
            val inputStream = context.contentResolver.openInputStream(uri)
            val bitmap = BitmapFactory.decodeStream(inputStream)
            BitmapImageBuilder(bitmap).build()
        } catch (e: Exception) {
            Log.e("ImageProcessor", "加载图像失败: ${e.message}")
            null
        }
    }
}

视觉功能配置

使用之前要为 LLM Inference API 启用视觉功能,下面是配置的方法:

import android.content.Context
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.google.mediapipe.tasks.genai.llminference.*
import com.google.mediapipe.framework.image.MPImage

class MultimodalLLMManager(private val context: Context) {
    private var llmInference: LlmInference? = null

    fun createMultimodalSession(llmInference: LlmInference): LlmInferenceSession {
        val sessionOptions = LlmInferenceSession.LlmInferenceSessionOptions.builder()
            .setTopK(10)
            .setTemperature(0.4f)
            .setGraphOptions(
                GraphOptions.builder()
                    .setEnableVisionModality(true)  // 启用视觉模态支持,允许模型接收图像输入
                    .build()
            )
            .build()

        return LlmInferenceSession.createFromOptions(llmInference, sessionOptions)
    }

    fun createMultimodalLLMOptions(modelPath: String): LlmInferenceOptions {
        return LlmInferenceOptions.builder()
            .setModelPath(modelPath)
            .setMaxNumImages(1)  // Gemma 3n 每次会话最多接受一张图片
            .setMaxTokens(2048)  // Gemma 3n 支持更大的上下文窗口(32K tokens)
            .setTopK(40)         // 控制输出多样性的参数
            .setTemperature(0.7f) // 控制生成随机性,0.7适合多模态任务
            .build()
    }
    suspend fun initializeModel(modelPath: String) = withContext(Dispatchers.IO) {
        try {
            val options = createMultimodalLLMOptions(modelPath)
            llmInference = LlmInference.createFromOptions(context, options)
        } catch (e: Exception) {
            throw e
        }
    }

    suspend fun analyzeImageWithText(
        image: MPImage,
        question: String
    ): String {
        return try {
            llmInference?.let { llm ->
                val session = createMultimodalSession(llm)
                session.use { s ->
                    s.addQueryChunk(question)
                    s.addImage(image)
                    s.generateResponse()
                }
            } ?: "模型未初始化"
        } catch (e: Exception) {
            Log.e("MultimodalLLM", "多模态推理失败: ${e.message}")
            "推理失败: ${e.message}"
        }
    }
}

使用方法

创建MultimodalLLMManager并调用initializeModel初始化模型,然后就可以调用MultimodalLLMManager的analyzeImageWithText识别图片了。


import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

class MultimodalDemoActivity : AppCompatActivity() {

    private lateinit var multimodalManager: MultimodalLLMManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        multimodalManager = MultimodalLLMManager(this)
        initializeModels()
    }

    private fun initializeModels() {
        lifecycleScope.launch {
            try {
                val modelPath = "${filesDir.absolutePath}/gemma_3n_model.task"
                multimodalManager.initializeModel(modelPath)
            } catch (e: Exception) {
                Log.e("MultimodalDemo", "模型初始化失败: ${e.message}")
            }
        }
    }

}

实际应用示例

下面是实际使用的几个例子,直接传入LlmInference和MPImage就可以解决不同的图片问题

class VisionQAHelper(private val multimodalManager: MultimodalLLMManager) {

    // 图像描述
    suspend fun describeImage(image: MPImage): String {
        return multimodalManager.analyzeImageWithText(
            image,
            "请详细描述这张图片中的内容。"
        )
    }

    // 对象识别
    suspend fun identifyObjects(image: MPImage): String {
        return multimodalManager.analyzeImageWithText(
            image,
            "识别图片中的所有对象,并列出它们的位置。"
        )
    }

    // 实用场景:菜单识别
    suspend fun analyzeMenu(menuImage: MPImage): String {
        return multimodalManager.analyzeImageWithText(
            menuImage,
            "分析这份菜单,提取菜品名称和价格,并推荐几道受欢迎的菜。"
        )
    }

    // 问题解答
    suspend fun answerImageQuestion(
        image: MPImage,
        question: String
    ): String {
        return multimodalManager.analyzeImageWithText(
            image,
            "根据图片回答问题:$question"
        )
    }
}

LoRA 模型定制

LoRA(Low-Rank Adaptation,低秩自适应)是一种参数高效的微调技术,让开发者能够使用相对较少的标注数据定制专用模型。

核心原理

LoRA 的核心价值就是"花小钱办大事"。传统的全量微调需要重新训练模型所有参数(数十亿个),而 LoRA 微调只训练模型中注意力层的少量参数(约万分之一),像给模型加个"插件",既保留原模型能力,又能学会新任务。

技术优势

  • 内存效率:只需要存储少量额外参数(通常 < 10MB)
  • 训练速度:比全量微调快 3-5 倍
  • 移动端友好:可以在手机 GPU 上运行
  • 可组合性:可以为不同任务训练多个 LoRA 适配器

在 MediaPipe 中使用 LoRA

// 加载带有 LoRA 适配器的模型
class LoRAEnabledLLM(private val context: Context) {

    fun createLoRAModel(baseModelPath: String, loraAdapterPath: String): LlmInference {
        val options = LlmInference.LlmInferenceOptions.builder()
            .setModelPath(baseModelPath)
            .setLoraPath(loraAdapterPath)  // 指定 LoRA 适配器路径
            .setMaxTokens(1024)
            .setTemperature(0.7f)
            .build()

        return LlmInference.createFromOptions(context, options)
    }

}

实际应用场景

医疗助手:基础 Gemma 3n + 医疗知识 LoRA

val medicalLLM = createLoRAModel(
    "gemma_3n_base.task",
    "medical_knowledge_lora.task"
)

代码助手:基础 Gemma 3n + 编程 LoRA

val codingLLM = createLoRAModel(
    "gemma_3n_base.task",
    "coding_assistant_lora.task"
)

感兴趣的同学可以参考 LoRA 官方文档 了解更多技术细节。


移动端 AI 的优势与前景

核心优势

移动端大模型的优势有三个方面:

数据不用上传,更安全
适合医疗图像分析等隐私敏感场景。

小内存也能高效运行
在手机上既能快速响应,又能保证准确性。

本地就能定制模型
可以定制模型,满足特定场景需求。

应用场景

离线刚需场景:地铁、偏远地区的语音导航和翻译;自然灾害中的图像处理和应急方案生成;出差途中的文档处理和设备故障诊断。

隐私保护场景:医疗影像分析、金融风险评估、法律合同审查、政府机密文件处理等,确保敏感数据不出设备。

实时交互场景:毫秒级语音助手响应、实时翻译、游戏剧情生成、短视频智能配音等,提供流畅的用户体验。


总结与展望

本文详细阐述了基于 MediaPipe Tasks 与最新 Gemma 3n 模型在 Android 平台实现生成式 AI 应用的完整方案,涵盖环境搭建、基础推理到多模态支持的全流程。
Gemma 3n 依托 PLE 缓存、MatFormer 架构等创新技术,为移动设备赋予了强大的 AI 能力,尤其在隐私保护、实时响应、云端与移动端协同处理等场景中具有显著应用价值。
希望本文能为相关开发者提供实用参考。


参考资源

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容