我们曾在之前的博文中介绍过 Shadow Art。这是一项 AI 实验,旨在颂扬中国古老的皮影戏艺术。实验利用 TensorFlow.js,在互动游戏中将用户的手影转换成数字动物。
在本文中,我们将探讨我们如何利用 TensorFlow.js 构建 Shadow Art。实验使用的所有代码皆为开放源代码,且可在 Github 上获得(https://github.com/thebitstudio/shadowart_ai_experiment)。
Shadow Art 简介
在 Shadow Art 中,您可通过在笔记本电脑或手机摄像头前摆弄手(字面意思),形成十二生肖动物的手影。如果手影正确匹配,系统便会将手影转换成相应动物的动画影象。
去年 9 月,我们曾构建一款互动式现实世界装置。该装置利用 TensorFlow 帮助人们探索皮影戏艺术。在中国农历新年,我们决定在线提供这项产品,以供所有人体验。为实现该目标,我们转而采用 TensorFlow.js。
借助 TensorFlow.js 在线推出 Shadow Art
要在网络上推出这项产品就需要改变原始的离线 Shadow Art。
首先,在离线版本中,我们需要捕捉用户的手部数据并在服务器上加以处理,发送要处理的图像并将其存储在服务器端。但是在网络版本中,我们可通过使用 TensorFlow.js,将所有内容一次性加载到浏览器,并在浏览器中完成整个 Shadow Art 流程:捕捉手部数据、处理数据、执行推理、显示结果,更不用说应用所需的其他依赖项。最困难的部分是用于执行手部数据分类的机器学习;毕竟这是核心的一环,而借助 TensorFlow.js,我们可以完成这项任务。
模型
模型将输入图像(用户的手部图像)与给定的一组类模板进行对比,以此判断最相似的图像。借助此方法,我们可以自由地为每个类添加或移除图像模板,甚至可以在不重新训练模型的情况下引入新的类。
机器学习模型能够有效学习如何使用残差网络高效对比两张图像,从而将固定长度的轮廓转换为固定维度的特征向量。
特征抽取网络
我们使用以下指标将从用户手部图像中抽取的特征与示例类中的特征进行对比:损失 = -exp(-(x-y)*(x-y)),其中 x 和 y 是从网络中获取的特征向量。为此,我们在训练期间抽取图像数据中的阴影轮廓、执行归一化,并随机旋转每个轮廓,然后再将轮廓送入训练管道。
在对比指标方面,我们采用负高斯,因为负高斯具有有界边缘,可防止出现梯度爆炸。对比其实就是计算距离。我们首先想到的是采用平方差之和。该函数没有极限值,有可能会导致梯度爆炸,不过其指数具有极限值。因此,我们采用平方差之和的负指数,并乘以另一个负数,以构成一个最小化问题。
与类模板的特征对比
数据集
初始数据集中包含很多从我们的团队成员处收集的二进制阴影投射图像,整个过程非常有趣。我们将这些图像用作训练数据,以供机器学习模型学习如何比较图像。此外,我们还将这些图像用作匹配模板。
捕捉的用户手部轮廓
数据集中包含的图像的分辨率各不相同。由于这些图像的性质,我们需要使用 RNN 或数据预处理等动态模型,将图像转换为固定维度的特征向量,以便进行直接对比。不过,像 RNN 这类高方差模型需要更多的数据,否则可能会出现过度拟合的情况。
对我们而言,数据预处理就是轮廓抽取,用于将图像转化为固定维度的特征向量,换句话说,就是准备数据并将数据送入残差网络。
执行
Shadow Art 在线体验
我们使用支持 TPU 的 TensorFlow 训练模型,然后进行转换,从而借助 TensorFlow.js 在网络上使用模型。
最初,我们的应用是在服务器端执行分类,而且用户量很大,因此我们预期会出现繁重的服务器负载。我们开展多个实验,以期克服该问题:
我们借助 TensorFlow.js,使用客户端 JavaScript 部署模型,以将机器学习处理任务移至客户端
该模型可供直接使用,且无需进行修改
移植的模型大小为 10.7MB,可以接受
每当我们检测到用户的手静止不动时,便会执行分类,整个过程大约用时一秒,而且分类时间几乎无法察觉
为了对用户的手部数据进行分类,我们采用修改版残差网络执行一次分类(每个类只使用少量示例)。该网络会获取固定长度的手部轮廓,并从中推断出动物类。
虽然可以使用 TensorFlow.js 在浏览器上训练模型,但我们使用 TPU 上的专用后端训练模型。然后使用 TensorFlow.js 将预训练的模型部署到浏览器,并在网络应用加载训练后存储权重。无需再在浏览器中进行训练。默认情况下,TensorFlow.js 采用动态编程范式。如此一来,即可在浏览器上轻松执行和测试想法。
为实现最大程度的控制,我们一次性自定义自己的权重转移协议(包括如何编码和压缩权重及模板以学习执行对比操作),以及用于网络应用的额外数据。
这样做的好处是我们可以在任何现有的张量库(而非只限于 TensorFlow)上构建训练管道。只要我们能够以相同的格式保存权重,并将其下载到我们的网页应用(借助 Tensorflow.js)即可。
TensorFlow.js 对比服务器端方法的优势
响应能力:在客户端分类阴影,并为用户提供即时反馈。若使用基于服务器的方法,则需要将图像发送到云,如此一来,分类结果就会出现延迟
减少带宽使用量及依赖项:不将用户的手部图像发送到服务器可显著减少用户的带宽使用量。此外,当页面加载完毕后,应用即可独立于互联网带宽顺畅地运行
减少服务器负载:迅速执行手影分类,并为用户提供即时反馈,告知其手影与阴影模板的匹配度。将此任务移至用户端可大幅减少服务器负载
网络托管要求更简单:无需设置基于 GPU 的云服务以提供模型,只需简单的网络托管服务即可
扩展更为容易:网络托管服务除了简单以外,还更容易设置和扩展,而且困难的部分也已进行处理
浏览器上的数据处理
为了在浏览器上获取手部轮廓,我们使用 OpenCV.js 通过网络摄像头从 HTML5
执行减除操作后,我们会处理手部图像以清除噪音(包括轮廓归一化和重新采样),然后再将图像作为阴影重新绘制到应用中。
捕捉并处理手部图像以实时创建阴影效果
根据初步试验,在 Android 手机上完成整个推理管道(包括预处理及分类)所用的时间不到一秒。考虑到所用的资源有限,这个时间相当惊人。
将结果集成到 AI 实验
鉴于分类结果,我们利用从模型返回的置信度值的阈值来判断手势是否与动物阴影匹配。与直接挑选置信度值最高的手势相比,这样做的结果更为直观。您可以这样想:人类判断手影是否像兔子的方式是测量手影有多像兔子,而不是比较手影看起来是否更像兔子而非其他动物。如此一来,我们即可轻松微调应用的难度,使其成为最适合全球用户的应用。
知道每位用户预期的动物之后,接下来就是将用户输入的手影与结果关联,并将手影转换为动物形象,然后再播放预先录制的阴影转化为活体动物的动画,如此即完成一次试验。
用户的手影(左)。变形后的动物阴影(右)
从用户的手影变形为目标动物是 AI 实验中精彩的一环。为确保捕捉的形状顺利变形为动物,我们从以下两种阴影中抽取轮廓:输入的手影和目标动物阴影。
阴影轮廓(为简化起见,限定为 100 点)。首先索引的地方以红色绘制
然后我们进行优化,以在手影轮廓(源轮廓)和动物阴影轮廓(目标轮廓)中找到正确匹配的每个点,接着执行步插值,以将源轮廓转换为目标轮廓。
我们执行动态时间弯曲,以便匹配不同的特征,如耳朵及更小的特征。
针对网站进行优化
精细控制动画
在网站上播放的视频通常为 .mp4 格式。但是,在网页上播放 .mp4 文件不允许我们对动画进行精细控制,而我们需要在阴影变形后使用这项功能来播放动画,从而获得顺畅的体验。
我们将动画转化成 PNG 序列。我们选择每一帧中特定的部分,并将其绘制在画布上。将这种方法与手影变形相结合,我们可以精确地知道何时绘制综合变形及预创建的视频:用户将看到手影变形为动物阴影,接着转换为彩色的动物形象。
预加载数据
预加载所有动画可以减少下载数据时的卡顿现象,从而提升用户体验。
下载大小
下载大小是确保我们网页应用更便于访问的重要方面之一。初始版本的应用需要用户下载大约 200MB 的数据。因此,我们不得不执行各种优化措施,以缩减下载大小。
PNG 大小优化我们首先查找应用中最消耗带宽的部分,结果发现 PNG 序列组合之后的大小超过 180MB。将 RGB PNG 转换为基于调色板的 PNG(仅允许从定义的集合中采样图像颜色),可将文件大小减少 70% 以上
模型模板优化我们的算法要求每个动物的手影模板与用户的手影匹配。在特征抽取过程中,每个手影将转换为轮廓,然后再被抽取到向量中。因此,我们并未存储手部模板作为图像,而是针对每个模板直接存储抽取的特征向量。这样做还可节省大量空间
结语与致谢
虽然当前模型仅限用于轮廓等对象分类,但其应用范围远不止如此。学习对比的核心概念是能够在不重新训练模型的情况下改变分类目标。
现在通过使用 Tensorflow.js,热表格人都能制作实现这种可移植性的网页应用,使监督式学习变得更加个性化。我们只需提供几个示例类,所有人便可使用或教授这种现成的对象分类工具,这令人很兴奋。所有人都可以轻松自定义该工具,以解决任何给定的特定任务。
非常感谢 Kiattiyot Panichprecha、Isarun Chamveha、Phatchara Pongsakorntorn、Chatavut Viriyasuthee 和 Pittayathorn Nomrak 在构建此实验时提供的所有帮助,而且我们期待更多使用模型和 Tensorflow.js 的有用、创新及有趣用例!
全部0条评论
快来发表一下你的评论吧 !