文 / 腾讯IEG 王高垒 (Allan)
众所周知,前端相关的业务相当于整个业务的用户体验前哨站。
除了在性能优化、动画等传统的体验上做努力,我们又如何在:目标(手势、肢体)识别、语音识别分析、无障碍、语音分析、增强现实、情绪识别、画像细分等等,基于人工智能的交互体验上,尽自己的一份力呢?
本文分为:第三方接口 、重头训练一个模型、使用成熟模型、再次训练 4个部分来展开前端应用人工智能的方法。
希望可以抛砖引玉,给大家带来一些思考。如果能对实际工作带来帮助,那就更好不过了。
下面开始正文:
使用第三方应用接口
使用第三方接口(比如腾讯云,谷歌云)就跟普通的业务没太大区别,都是 【用户输入数据 -> 请求接口 -> 解析接口内容】,难点主要在于把解析后的数据玩出花样。
比如之前刷爆朋友圈的你画我猜,就是先让用户在 canvas 上画出图形后,把图形上传到自己的服务器,然后做出相应的预测,你画我猜类似版本体验。
版本体验
https://quickdraw.withgoogle.com/
如果你调查市面上的 API 接口没有发现你需要的,那么就跟我一起来看下第二种解决方案:重头训练一个新的模型。
重头训练一个新的模型
重头训练一个模型需要大量的专业知识,但是这并不影响我开始这部分的介绍 :p,我会以一个简单的例子开始,你可以测试一下你是否有兴趣深入下去。
大家请看这个例子:
x = [-1,0,1,2,3,4]
y = [-3,-1.0,1,3,5,7],
请用一点时间推导一下 X 和 Y 的关系公式:
3…
2…
1…
相信大家经过试错和思考已经找到答案了:
【y=2x-1】
这个就是神经网络的原理 (我所理解的大白话):设计好神经网络之后,输入的数据根据正确答案,经过某些次数的训练 (试错) 之后,拟合到一个最终的值 (参数)。把值 (参数) 保存下来,以方便其他类似场景使用。
那么落地到 TensorFlow 代码中又是怎样的呢?我们又如何设计这个神经网络呢?
请看代码(代码基于 TensorFlow 1.15.x):
#加载模块依赖
import tensorflow as tf
import numpy as np
from tensorflow import keras
#添加一层神经网络,输入的参数是一维向量
model = keras.Sequential([keras.layers.Dense(units=1,input_shape=[1])])
#为模型添加sgd优化器 损失函数为均方误差
model.compile(optimizer='sgd',loss='mse')
#输入的数据及正确的数据
xs = np.array([-1.0,0.0,1.0,2.0,3.0,4.0],dtype=float)
ys = np.array([-3.0,-1.0,1.0,3.0,5.0,7.0],dtype=float)
#训练模型50轮
model.fit(xs,ys,epochs=500)
#输出模型详细信息
model.summary()
#保存模型
model.save('model1.h5')
#预测某个数字的结果
print(model.predict([654615]))
#[[1307159.4]]
接下来,在模型调试成功之后,把 keras 模型转为 tfjs 可以使用的模型就可以在网页中预测了 (在第 4 部分有讲到具体操作方法)。
如果你是一个没有接触过 TensorFlow 的同学,看完这段代码应该大概率懵逼了,没错,我也是懵逼的。
而这只是一个非常简单的只有一个全连接层的入门例子 (人都可以短时间推导出来),但是人工智能往往需要做特别复杂的预测,比如票房预测、无人机飞行控制,人类手动计算各项输入几乎没办法完成。那网络的设计将比这个复杂的多(手动狗头)。
除了完美的网络设计以外,如果你还希望预测的准确率高一些,很完善的数据集也是必不可少的。比如之前提到的快速涂鸦的例子,仅 "飞机" 一个分类的数据集就有高达 13 万张图片数据。
数据集
https://quickdraw.withgoogle.com/data/airplane
所以重头训练一个新的模型,是一个学习周期很长、成本相当高的方案。如果希望短时间内用 AI 赋能你的业务,最好先看下其他三种方案有没有适合你的需求,或者问下 AI 部门的同事,他们有没有兴趣和你一起干。
如果你对此很有兴趣想要深入学习下去,推荐你看吴恩达的最新课程 (开始的例子也是出自这个课程,旁听免费)。
吴恩达最新课程
https://www.coursera.org/specializations/tensorflow-in-practice
使用成熟模型
除了很多限制的使用第三方接口和门槛很高的重头训练以外,我们还可以选择使用成熟的模型,TensorFlow 已经有 js 版本了,并且已经有成熟的开源模型和类库(前端开源的相关应用几乎全是基于 tfjs 的)。
但是几乎每个例子的 demo 为了展示能力,都预置了参数调整和复杂的交互逻辑,可读性方面尚待提高。
我把社区中开源的前端 AI 能力,又重新用直白的语言写了一遍,每个 DEMO 下有原项目地址链接,有兴趣应用的可以研究更精确的参数。
为了照顾没有摄像头的用户,我为每个例子都写了上传图像的版本。
也为了保持demo的单独性和便于理解,没有抽离公用模块,基本开箱即用:
风格迁移
抽取图像上的特征应用到其他图片
https://allan5.com/FE-AI/style-transfer.html
看图识花
可识别的分类:雏菊、蒲公英、玫瑰、向日葵、郁金香
https://allan5.com/FE-AI/flower.html
人脸检测 支持摄像头
识别人脸、情绪、年龄
https://allan5.com/FE-AI/flower.html
手部追踪
识别人体手部
https://allan5.com/FE-AI/handtrack.html
图像分类 支持摄像头
可识别的分类:https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/imagenet_classes.ts
https://allan5.com/FE-AI/mobilenet.html
目标识别 支持摄像头
可识别的目标:https://github.com/tensorflow/tfjs-models/blob/master/coco-ssd/src/classes.ts
https://allan5.com/FE-AI/object_detection.html
人物分割 1.0 支持摄像头
可识别人体轮廓
https://allan5.com/FE-AI/bodypix.html
人物分割 2.0 支持摄像头
可同时识别多个人体轮廓
https://allan5.com/FE-AI/bodypix2.html
姿态检测 支持摄像头
识别人体关键点
https://allan5.com/FE-AI/posenet.html
除了在浏览器中支持以外,TensorFlow.js 官方也推出了支持小程序的插件。
插件
https://mp.weixin.qq.com/wxopen/pluginbasicprofile?action=intro&appid=wx6afed118d9e81df9&token=612578398&lang=zh_CN
有兴趣的同学可以学习我同事 @xiaoxiwang 出品在腾讯课堂上发布的 TensorFlow.js 小程序视频教程。
视频教程
https://ke.qq.com/course/428263?taid=3607622205212903&dialog=1
成熟模型带来了免费、视频实时预测等等优点,同时也带来了无法定制识别自己的分类等缺点。
接下来是最后一种方法:再次训练。
再次训练
有的时候你的需求,需要的 AI 能力仅仅只需要识别一个标志物体或者目标的坐标,比如某个 LOGO、某幅画。那么可以在前人的训练好的模型基础上再次训练。
在没有强大数据集和 AI 团队支持的情况下,使用前人训练好的参数和模型设计基础上,再次训练不失为一种不错的选择(这种方法也称为迁移学习)。
下面以高度封装的 tensorflow-for-poets-2 为例,介绍训练图片分类的详细步骤(只需 7 步):
1. clone 预训练源代码
git clone https://github.com/googlecodelabs/tensorflow-for-poets-2
cd tensorflow-for-poets-2
2. 下载测试数据集 (解压到 tensorflow-for-poets-2/tf_files)
测试数据集
http://download.tensorflow.org/example_images/flower_photos.tgz
3. 开始训练 (基于 tensorflow 1.15.0 环境)
python -m scripts.retrain ^
--bottleneck_dir=tf_files/bottlenecks ^
--how_many_training_steps=2000 ^
--model_dir=tf_files/models/ ^
--summaries_dir=tf_files/training_summaries/mobilenet_0.50_224 ^
--output_graph=tf_files/retrained_graph.pb ^
--output_labels=tf_files/retrained_labels.txt ^
--architecture=mobilenet_0.50_224 ^
--image_dir=tf_files/flower_photos
(mac下命令连接符是 \,win下是 ^)
4. 测试输出
python -m scripts.label_image --graph=tf_files/retrained_graph.pb --image=xxx
#正常情况下会有类似输出:
daisy (score = 0.xxxx)
sunflowers (score = 0.xxxx)
5. 优化 PB 文件
python scripts/quantize_graph.py ^
--input=tf_files/retrained_graph.pb ^
--output=tf_files/quantized_graph.pb ^
--output_node_names=final_result ^
--mode=weights_rounded
6.安装 tensorflowjs
pip install tensorflowjs==1.3.2
7. 转换 PB 文件为 web 可用的 PB 文件
tensorflowjs_converter ^
--input_format=tf_frozen_model ^
--output_node_names=final_result ^
--output_format=tfjs_graph_model ^
tf_files/quantized_graph.pb ^
tf_files/web
到这里,全部步骤已经完成了。我也使用刚才的 tfjs-converter 转换之后的 model.json 写了一个示例 (https://allan5.com/FE-AI/flower.html)。
和使用成熟模型最大的不同是,需要自己处理传递给模型的图像参数:
//初始输入信息
const image = tf.browser.fromPixels(pic);
//把初始化为224x224的大小
const resized_image =tf.image.resizeBilinear(image, [224,224]).toFloat();
//把向量转换为0~1的数值
const offset = tf.scalar(255.0);
const normalized = tf.scalar(1.0).sub(resized_image.div(offset));
//把输入的数据转换为[0,224,224,3]的标准格式
const batchedImage = normalized.expandDims(0);
const result = await model.predict(batchedImage).data();
//根据评分排序结果
let text = label.map((label, index) => ({ label, score: result[index] })).sort((a,b)=>b.score - a.score)
因为篇幅的关系,就不仔细拆解目标识别 (带物体具体的坐标信息的预测) 的训练步骤了,如果有兴趣,可以试试这个项目。
项目
https://github.com/EdjeElectronics/TensorFlow-Object-Detection-API-Tutorial-Train-Multiple-Objects-Windows-10
文中第二部分和第四部分选用了 python 语言作为演示语言,作为前端的你现在 tfjs 同样支持在 nodejs 和浏览器中训练模型。
笔者认为现阶段如果要深入机器学习,python 有自己得天独厚的优势,比如社区丰富、有大规模训练的解决方案、图像处理和数学计算等 nodejs 或 js 不能替代的模块。所以本文中的一些示例用了 python 语言。
现在 tf.js 现在也在飞速发展,除了开源社区,TensorFlow 官方也投入了数个全职人力开发。相信不久的将来,tf.js 除了适合前端应用以外,对训练、部署这些周边支撑也会有高效的解决方案!
结语
模型的加载是一个问题,像目标识别模型分类多一些的有超过 20M 大小。现阶段如果应用比较复杂的预测,还是比较适合在资源预载的场景(线下、打包到移动 APP、桌面端软件)使用。但是未来 5G 的发展,可能会逐渐弱化这个劣势。
但是这些都是客观原因,如何用现在的技术和自己本身的领域结合,做出更多花样,才是前端同胞们需要一直思考的地方。
最后,特别感谢 tfjs 官方团队 Ping Yu 和 Wei Wei,对此文的指导和校对。