专栏名称: 飞猪前端团队
公众号
目录
相关文章推荐
ZaomeDesign  ·  每日灵感丨二月十三日 ·  18 小时前  
创伙伴  ·  欢迎你也加入创伙伴知识星球 ·  昨天  
庞门正道  ·  我为什么拜神? ·  4 天前  
产业互联网大视野  ·  产业互联网大视野2025年线下活动安排表 ·  2 天前  
51好读  ›  专栏  ›  飞猪前端团队

飞猪找一找,端侧 AI 的实践和展望

飞猪前端团队  · 掘金  ·  · 2020-05-09 17:48

正文

阅读 24

飞猪找一找,端侧 AI 的实践和展望

本文介绍飞猪互动双十一彩蛋“飞猪找一找”, 一个基于 AI 图像识别的纯客户端游戏

以这个游戏只是抛砖引玉,试图讨论:

  1. 前端同学如何使用 tensorflow 迅速地训练一个符合个性化需求的图像识别模型
  2. 如何使用 tensorflow.js 在前端载入和使用模型,以及如何整合所有体验到 React.js 框架下
  3. 踩过的一些需要注意的坑
  4. 前端同学使用 tensorflow.js 可以走多远?落地业务的可能性有多大?
  5. 和哪些技术可以结合?有哪些可以展望的未来

可以通过支付宝或者飞猪扫码此二维码试玩:

机器学习 101

机器学习说到底就是一种算法,并没有什么神秘性。业界甚至避免使用人工智能(AI)的称呼,因为人工智能暗示了智能,但现在的机器学习还称不上智能。婴儿能在没有规则和目标的情况下学习,而且要识别什么是“猫”大概看到 5-10 只猫就够了。相比之下机器学习非常笨拙,识别猫必须有上百万张图片数据灌入,且结果对数据源的取材也高敏感。

AI 是如何识别的呢

  • 一个简化的模型是中学常解的三元一次联立方程,只不过他要解的是几百万元方程。
  • 神经网络:每个元就是“参数”,而联立方程式称为一层“神经网络”,常用的简单模型可能有 3-6 层,复杂模型甚至有 50+层。
  • 训练:即解这些方程式的过程。计算机通过趋近的算法逐步逼近正确的 “参数”,经过几千次训练,这些参数足够逼近正确答案,一个 AI 模型就训练完成了。此时再喂一张图片的像素数据,它会回答答案是 0.99,即 99%是猫。

端侧机器学习(client side machine learning) 的意义

1. 端侧机器学习有两类

  • 在端侧使用训练完成的模型
  • 在端侧进行模型训练

后者现在还不成熟,训练对机型要求高,比如需要通过 webgl 调用 gpu 加速训练。

2. 端侧机器学习的利弊

  • 优点

    • 开发成本,从训练、开发到发布一切前端搞定,迭代效率有大幅提高
    • 无需网络本地可玩,这也确保了某些涉及隐私的应用
    • 一次加载资源后,不需反复请求服务端传递数据量以及获取 AI 分析结果,非常适合图像识别等高频 AI 分析场景
  • 缺点 :模型大小受限,精细的非定制 mobile 的模型体积常有上百 M,并不适合直接投放 h5

如何使用 tensorflow.js 简单的跑起一个已有的模型

tensorflow.js 整合了 4 个 api:

  • Core 最底层 api,用于创建模型,就是之前的
    deeplearn.js
    .
  • Layers 一个类似于 Keras 的高层 api
  • Data 用于加载和加工数据的 api,类似于 tf.data .
  • Converter 用于在 js 中引入 TensorFlow 模型的 loader

从用途上来说,无外乎 a. 使用已有的模型。包括如何加载以及每个模型提供的高层的 api b. 重新训练已有模型 c. 完全定制和创建崭新的模型

本文会涉及到 a 和 b,重点放在 a,关于 b 也只会在原理层分析。

1.通过摄像获取图片

摄像头调用(webar)(新的 webar 模板,使用新版 webar) 如下的普通的 h5 页面调用摄像头受到安全限制无法使用

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  const stream = await navigator.mediaDevices.getUserMedia({
    'audio': false,
    'video': {facingMode: 'environment'}
  });
复制代码

我们使用支付宝的 webar.js 来强行打开摄像头。webar 提供了 getWebCameraAsync 方法获取摄像头

...
<canvas ref={node => (this.glCanvas = node)} />
...
// 开启相机
initCamera = async () => {
  // 获取 webgl 上下文
  const cvs = this.glCanvas;
  const gl = cvs.getContext('webgl');
  // 获取摄像头
  this.camera = await getWebCameraAsync({ facing: CAMERA_FACING_BACK });
  // 打开摄像头
  await this.camera.openAsync();
  // 在canvas上绘制相机投影
  this.displayTarget = this.camera.createDisplayTarget('ar-container', { autoResize: 0, gl });
  this.displayTarget.loop();
};
复制代码

webar 的相机提供了 pause、resume、snapshot、close 等一系列方法用于开始/暂停/截图和销毁。详见 文档

2. AI 处理和预测图片内容:

  • 前端载入模型:

    使用 tfjs 的 converter。0.x 和 1.x 版本从模型保存格式到调用 api 都不相同,新版参考文档 Save and load models , 旧版如下:

import { loadFrozenModel } from '@tensorflow/tfjs-converter';

const WEIGHT_MANIFEST_FILE_URL =
  'https://gw.alipayobjects.com/os/fliggy-play/181301-3/weights_manifest.json';
const MODEL_FILE_URL =
  'https://gw.alipayobjects.com/os/fliggy-play/181301-3/tensorflowjs_model.pb';
this.model = await loadFrozenModel(MODEL_FILE_URL, WEIGHT_MANIFEST_FILE_URL);
复制代码

而预测非常简单:

// 运行模型获得预测结果
this.model.execute(data, 'final_result');
// 丢弃模型释放内存,在didMount中使用
this.model.dispose();
复制代码

旧版 tfjs 需加载的模型文件三个:

  1. tensorflowjs_model.pb (模型文件)
  2. weights_manifest.json (配重 manifest)
  3. group1-shard1of1 (二进制配重表)

其中 group1-shard1of1 不被直接引用,但必须放在同一路径上,否则会报错!

  • 使用模型处理和预测 版本 0.x 和 1.x 之间的 api 也有少许区别,例如从图片上获取像素信息,在 0.x 中是 fromPixels 在 1.x 中是 browser.fromPixels

从截图上获取和加工像素数据:

import * as tfc from '@tensorflow/tfjs-core';
...
// tidy方法用于在tensorflow调用结束后清理内存
const result = tfc.tidy(() => {
  // 使用webar相机截图
  const img = await this.displayTarget.snapshotImageDataURLAsync({type: 'imageData'});
  // 获取图片信息
  const pixels = tfc.browser.fromPixels(img);
  // “截图”,确保模型接收的所有素材数据格式统一到224*224
  const centerHeight = pixels.shape[0] / 2;
  const beginHeight = centerHeight - 112;
  const centerWidth = pixels.shape[1] / 2;
  const beginWidth = centerWidth - 112;
  const pixelsCropped = pixels.slice(
    [beginHeight, beginWidth, 0],
    [224, 224, 3]
  );
  // 将规范化的224*224像素信息传入模型,获取模型预测结果
  return predict(pixelsCropped);
});
复制代码

预测的 predict 方法具体详见 gitlab,这里不展开。至此,你可以成功载入已有模型,从摄像机获取截图,并通过分析返回预测结果(是一个数组,包含预测结果和预测确定性)。

3. 优化注意

  • 使用 tfc.tidy 包裹模型预测,用于清理内存
  • 模型先跑一组空数据预热 tfc.zeros([VIDEO_PIXELS, VIDEO_PIXELS, 3])
  • 注意在 willUnmount 里销毁模型和相机,清理内存
  • 加载优化

利用 React 的 Suspense 和 lazy 语法,我们 code splitting 主体 Game 组件(乃至所有 AI 模型加载预热和相机调用),并确保 Game 组件在 Menu 加载完成后立刻 preload,达到满意的游戏加载体验

const Menu = lazy(() => import('./Menu'));
const Playground = lazy(() => import('./Playground'));

function Game() {
  const [isMenu, set] = useState(true);

  useEffect(() => {
    import('./Playground');
  }, []);
  ...
  return (
    <div>
      {isMenu ? (
        <Suspense fallback={<Loading show />}>
          <Menu toGame={toGame} />
        </Suspense>
      ) : (
        <Suspense fallback={<Loading show />}>
          <div className="game-page">
            <Playground backToMenu={backToMenu} />
          </div>
        </Suspense>
      )}
    </div>
  );
};
复制代码

如何 retrain 一个模型

1. 什么是 retrain

图像识别模型有上百万个参数,从头开始训练一个图像识别模型需要海量的数据和上百个 gpu 训练小时。作为希望能低成本的使用的前端同学,从头训练模型不可取。但现有模型很可能无法满足你需要识别 “飞猪” 的定制要求。这时候最佳途径是 retrain,说具体点是 transfer learning,即利用已有的训练模型的结果,再添加一层神经网络,将其转换为我们定制的训练结果。

结果肯定不如从头开始,但这个简单手段的精确度已经足够高,是个投入产出比优异的方案。“飞猪找一找”的 retrain 在我的 mac pro 上,无 gpu 加速,一次训练 3000 轮,只需 30 分钟左右(首次训练会比较耗时)。

2. 如何 retrain

我们使用了 hub 上现成的 图像 retrain 模型 , 具体训练细节可以参考 google 的 emoji-scavenger-hunt 仓库 README, 将收集的图片按照归类如下方式放入训练文件夹 train/data, 安装 python 等环境和依赖,然后运行 hub 的 retrain 模型。

data
└── images
    ├── cat
    │   ├── cat1.jpg
    │   ├── cat2.jpg
    │   └── ...
    └── dog
        ├── dog1.jpg
        ├── dog2.jpg
        └── ...
复制代码






请到「今天看啥」查看全文


推荐文章
ZaomeDesign  ·  每日灵感丨二月十三日
18 小时前
创伙伴  ·  欢迎你也加入创伙伴知识星球
昨天
庞门正道  ·  我为什么拜神?
4 天前
产业互联网大视野  ·  产业互联网大视野2025年线下活动安排表
2 天前
悦读文摘  ·  2017年度笑到肾抽筋的段子
7 年前
行业研究报告  ·  2017全球人工智能发展报告
7 年前