6.2 Flutter 与人工智能/机器学习 (AI/ML)

基础知识

人工智能 (AI) 和机器学习 (ML) 正在改变我们与技术互动的方式。将 AI/ML 能力集成到移动和桌面应用程序中,可以提供更智能、更个性化的用户体验。Flutter 作为跨平台框架,能够很好地与 AI/ML 模型集成,无论是通过云服务还是在设备端运行。

1. AI/ML 在移动应用中的应用场景

  • 图像识别:物体检测、人脸识别、场景分类。
  • 自然语言处理 (NLP):文本分类、情感分析、语音识别、机器翻译。
  • 推荐系统:根据用户行为推荐内容或商品。
  • 智能助手:语音交互、任务自动化。
  • 个性化体验:根据用户偏好调整应用内容和功能。

2. Flutter 集成 AI/ML 的方式

  • 云端 AI/ML 服务

    • Firebase ML:Google 提供的机器学习服务,包括预训练模型(如文本识别、人脸检测、图像标签)和自定义模型部署。Flutter 可以通过 Firebase 插件轻松集成。
    • Google Cloud AI Platform:更高级的云端 ML 服务,用于训练、部署和管理自定义模型。Flutter 应用可以通过 REST API 或 gRPC 与其交互。
    • AWS AI Services:亚马逊提供的各种 AI 服务,如 Rekognition (图像和视频分析)、Polly (文本转语音)、Translate (翻译) 等。Flutter 应用可以通过 AWS Amplify 或直接调用 REST API 集成。
  • 设备端 AI/ML (On-device ML)

    • TensorFlow Lite:Google 专门为移动和嵌入式设备设计的轻量级机器学习框架。它允许在设备上运行预训练的 TensorFlow 模型,无需网络连接,响应速度快,保护用户隐私。
    • Core ML (iOS) / ML Kit (Android):原生平台提供的机器学习框架。Flutter 可以通过平台通道与它们交互,或者直接使用 Flutter 插件。

3. TensorFlow Lite 在 Flutter 中的应用

TensorFlow Lite 是在设备端运行 ML 模型的首选方案。其基本流程如下:

  1. 准备 TensorFlow Lite 模型 (.tflite):通常由数据科学家或 ML 工程师训练并导出。
  2. 将模型添加到 Flutter 项目:将 .tflite 文件作为 assets 资源添加到 pubspec.yaml 中。
  3. 使用 Flutter 插件加载和运行模型:例如 tflite_fluttertflite_flutter_helper 等社区插件。
  4. 处理输入数据:将图像、文本等输入数据转换为模型所需的格式(通常是 Tensor)。
  5. 解析输出结果:将模型的输出结果解析为可理解的格式。

官方文档链接

Flutter 开发中的应用案例

将 AI/ML 能力集成到 Flutter 应用中,可以极大地提升用户体验和应用价值。以下案例将演示如何使用 tflite_flutter 插件在 Flutter 应用中进行简单的图像分类。

案例:使用 TensorFlow Lite 进行图像分类

我们将构建一个简单的应用,允许用户从相册选择图片,然后使用预训练的 TensorFlow Lite 模型对图片进行分类。

前提条件

  • 一个预训练的 TensorFlow Lite 图像分类模型(例如 MobileNetV2)。你可以从 TensorFlow Lite 官方网站下载示例模型。
  • 一个标签文件(通常是 .txt),包含模型能够识别的类别名称。
  • image_picker 插件用于选择图片。

步骤 1: 创建项目并添加依赖

bash
复制代码
flutter create image_classifier
cd image_classifier

pubspec.yaml 中添加 tflite_flutterimage_picker 依赖:

yaml
复制代码
dependencies:
  flutter:
    sdk: flutter
  tflite_flutter: ^0.10.0 # 最新版本可能不同
  image_picker: ^1.0.4 # 最新版本可能不同

运行 flutter pub get

步骤 2: 添加模型和标签文件

在项目根目录下创建 assets/ml/ 目录,并将你的 .tflite 模型文件(例如 mobilenet_v2_1.0_224.tflite)和标签文件(例如 labels.txt)放入其中。

修改 pubspec.yaml,声明这些 assets:

yaml
复制代码
flutter:
  uses-material-design: true
  assets:
    - assets/ml/mobilenet_v2_1.0_224.tflite
    - assets/ml/labels.txt

步骤 3: 构建 UI 和逻辑 (lib/main.dart)

dart
复制代码
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:tflite_flutter/tflite_flutter.dart';
import 'package:tflite_flutter/tflite_flutter_helper.dart'; // 用于图像预处理

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '图像分类器',
      theme: ThemeData(
        primarySwatch: Colors.teal,
      ),
      home: const ImageClassifierScreen(),
    );
  }
}

class ImageClassifierScreen extends StatefulWidget {
  const ImageClassifierScreen({super.key});

  @override
  State<ImageClassifierScreen> createState() => _ImageClassifierScreenState();
}

class _ImageClassifierScreenState extends State<ImageClassifierScreen> {
  Interpreter? _interpreter;
  List<String>? _labels;
  File? _image;
  String _result = '请选择一张图片进行分类';
  bool _isLoading = false;

  final ImagePicker _picker = ImagePicker();

  @override
  void initState() {
    super.initState();
    _loadModel();
  }

  @override
  void dispose() {
    _interpreter?.close();
    super.dispose();
  }

  Future<void> _loadModel() async {
    setState(() {
      _isLoading = true;
    });
    try {
      _interpreter = await Interpreter.fromAsset(
        'assets/ml/mobilenet_v2_1.0_224.tflite',
        options: InterpreterOptions()..threads = 4, // 可以配置线程数
      );
      _labels = await FileUtil.loadLabels('assets/ml/labels.txt');
      _result = '模型加载成功,请选择图片。';
    } catch (e) {
      _result = '模型加载失败: $e';
      print('Failed to load model: $e');
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  Future<void> _pickImage() async {
    final XFile? pickedFile = await _picker.pickImage(source: ImageSource.gallery);
    if (pickedFile != null) {
      setState(() {
        _image = File(pickedFile.path);
        _result = '正在分类...';
        _isLoading = true;
      });
      await _classifyImage(_image!);
    }
  }

  Future<void> _classifyImage(File image) async {
    if (_interpreter == null || _labels == null) {
      setState(() {
        _result = '模型未加载或标签缺失。';
        _isLoading = false;
      });
      return;
    }

    try {
      // 图像预处理
      // 根据你的模型输入要求调整参数
      // MobileNetV2 通常需要 224x224 像素的 RGB 图像,像素值归一化到 [-1, 1] 或 [0, 1]
      ImageProcessor imageProcessor = ImageProcessorBuilder()
          .add(ResizeOp(224, 224, ResizeMethod.BILINEAR))
          .add(NormalizeOp(127.5, 127.5)) // 归一化到 [-1, 1]
          .build();

      TensorImage tensorImage = TensorImage.fromFile(image);
      tensorImage = imageProcessor.process(tensorImage);

      // 获取输入输出张量信息
      Tensor inputTensor = _interpreter!.getInputTensor(0);
      Tensor outputTensor = _interpreter!.getOutputTensor(0);

      // 运行模型
      _interpreter!.run(tensorImage.buffer, outputTensor.buffer);

      // 解析输出结果
      // 假设输出是 Float32List,表示每个类别的概率
      List<double> probabilities = outputTensor.buffer.asFloat32List();

      // 找到概率最高的类别
      int maxProbIndex = 0;
      for (int i = 0; i < probabilities.length; i++) {
        if (probabilities[i] > probabilities[maxProbIndex]) {
          maxProbIndex = i;
        }
      }

      String predictedLabel = _labels![maxProbIndex];
      double confidence = probabilities[maxProbIndex] * 100;

      setState(() {
        _result = '分类结果: $predictedLabel (${confidence.toStringAsFixed(2)}%)';
      });
    } catch (e) {
      setState(() {
        _result = '分类失败: $e';
        print('Failed to classify image: $e');
      });
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('图像分类器'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _image == null
                ? const Text('没有选择图片')
                : Image.file(
                    _image!,
                    height: 250,
                    width: 250,
                    fit: BoxFit.contain,
                  ),
            const SizedBox(height: 20),
            Text(
              _result,
              textAlign: TextAlign.center,
              style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 20),
            _isLoading
                ? const CircularProgressIndicator()
                : ElevatedButton(
                    onPressed: _pickImage,
                    child: const Text('从相册选择图片'),
                  ),
          ],
        ),
      ),
    );
  }
}

案例分析:

  • tflite_flutter 插件:这是在 Flutter 中加载和运行 TensorFlow Lite 模型的核心插件。
  • Interpreter.fromAsset():用于从 Flutter assets 中加载 .tflite 模型文件。确保模型文件已在 pubspec.yaml 中声明。
  • FileUtil.loadLabels()tflite_flutter_helper 提供的辅助函数,用于加载标签文件。
  • 图像预处理
    • ImageProcessorBuilder 用于构建图像处理管道。模型通常对输入图像的尺寸和像素值范围有严格要求。
    • ResizeOp:将图像调整到模型所需的尺寸(例如 224x224)。
    • NormalizeOp:将像素值归一化到模型所需的范围(例如 [-1, 1] 或 [0, 1])。
    • TensorImage.fromFile():将 File 对象转换为 TensorImage,这是 tflite_flutter 处理图像的内部表示。
  • _interpreter!.run(tensorImage.buffer, outputTensor.buffer):执行模型推理。tensorImage.buffer 是输入数据,outputTensor.buffer 是模型输出结果的缓冲区。
  • 解析输出:模型的输出通常是表示每个类别概率的浮点数列表。你需要找到概率最高的索引,并将其映射到对应的标签。
  • 用户体验:通过 _isLoading 状态显示加载指示器,并通过 _result 文本显示分类结果和错误信息。

如何运行和测试:

  1. 下载模型和标签:从 TensorFlow Lite 官方网站下载一个预训练的图像分类模型(例如 MobileNetV2)及其对应的标签文件。将它们放置在 assets/ml/ 目录下。
  2. 运行应用:在模拟器或真实设备上运行 flutter run
  3. 选择图片:点击“从相册选择图片”按钮,选择一张图片。
  4. 查看结果:应用将显示分类结果和置信度。

这个案例展示了如何在 Flutter 应用中集成设备端机器学习能力。通过 TensorFlow Lite,你可以构建智能应用,在用户设备上直接进行图像识别、文本处理等任务,提供快速、私密且无需网络连接的 AI 体验。