王小新 编译自 Google Cloud Blog
量子位 出品 | 公众号 QbitAI
你们程序员啊,连带娃都这么技术流……
今年夏天,谷歌云负责维护开发者关系的Kaz Sato带着他的儿子,用一些传感器和一个简单的机器学习线性模型,开发了一个“猜拳机器”,能检测石头剪刀布的手势。
最近他还还根据这个过程写了一份教程,详细介绍了怎样构建这个机器,以及怎样用机器学习算法解决日常问题。
量子位搬运编译整理如下,适合有一定编程基础的同学,需要大约200美元的硬件设备。
我们先来看一下这个机器:
上面视频中,我们搭建的系统正在通过手套上的传感器,借助一个用Tensorflow编写的简单机器学习算法来检测我儿子的手势,然后选择相应的选项:石头、剪刀、布。
项目源代码在此:
https://github.com/kazunori279/ml-misc/tree/master/glove-sensor
具体是怎样实现的呢?接下来我会一步一步地讲。
第1步:
制作手套传感器
我们使用littleBits来构建硬件系统。这套设备对儿童很友好,包含各种各样的组件,如LED灯、电机、开关、传感器和控制器等,这些组件可以靠磁性链接,无需焊接。在这个实验中,我们使用了三个
弯曲传感器
,将它们附在塑料手套上。
△
littleBits弯曲传感器
当你戴着手套、弯曲手指时,传感器会输出一个从0V到5V变化的电压信号。为传感器输出加一个指示器,比如LED光柱,就能实时看到每个传感器受到的压力。
△
弯曲传感器输出0V-5V信号
第2步:
安装Arduino和伺服模块
要读取弯曲传感器的输出信号并控制机器的转动幅度,我们使用了Arduino模块和伺服模块。Arduino模块内部有一个微控制器芯片,且具有多个输入和输出端口。你可以在笔记本电脑上用Processing语言(和C语言比较像)编写一个程序并编译,然后通过USB线传输到该模块中。
△
littleBits Arduino模块
△
伺服模块
△
我儿子在画转盘指示图
现在,用于构建猜拳机的所有硬件已经准确齐全,接下来,就该写代码了。
△
猜拳机硬件部分
第3步:
写程序从弯曲传感器读取数据
在配置好硬件后,我们开始在Arduino模块上编写代码,实现从弯曲传感器读取数据的功能。在Arduino的IDE中,设定为每隔0.1秒读取传感器数据,然后将其记录在串行控制台上,代码如下。
△
在Arduino IDE中编写程序
运行这段代码时,你会在控制台上看到这样的数字:
其中,每行的三个数字表示弯曲传感器输出的三个数据。Arduino模块将输入信号电压(0V - 5V)转换成从0到1023变化的数字。
上图是“石头”手势的数据,所有传感器都是弯曲的。如果换成“布”的收拾,所有传感器都不弯曲,则上图的数据都会趋近于0。
第4步:
使用Cloud Datalab可视化数据
该如何确定这三个数字的组合是代表着“石头”、“布”还是“剪刀”?
最简单的方法是编写能判断阈值和条件的IF语句。比如:
-
当三个输出数值都低于100时,则输出“布”;
-
当三个输出数值都高于400时,则输出“石头”;
-
若不满足以上两个条件,则输出“剪刀”。
这个程序可能满足当前任务的要求,但是很不灵活也不稳定。
如果我儿子要求我在手套上添加更多传感器,来捕获10个不同手势,那该怎么办?或者,如何向紧身衣添加多个传感器,来识别不同身体姿势?显然,上述程序无法处理这么复杂的任务。
当然,主要是因为我比较懒,想编写出更强大和更灵活的代码,能在不改变基本设计的前提下,灵活处理善变的甲方(我儿子)可能提出的各种请求。
为了找到更好的数据处理方法,我对手套传感器数据做了一些快速的分析。我使用的工具是Cloud Datalab,这是一个很受欢迎的Jupyter Notebook版本,并已集成到Google Cloud平台,可提供基于云数据分析的一站式服务。你可以在Web UI中编写Python代码,使用如NumPy、Scikit-learning和TensorFlow等函数库,并将其与Google Cloud服务(如BigQuery、Cloud Dataflow和Cloud ML Engine)相结合。
根据不同手势,我把手套传感器数据分开保存成三个CSV文件,每个文件包含800行数据。你可以在Cloud Datalab上编写Python代码,将它们读取并转换为NumPy数组,示例代码如下:
△
使用Cloud Datalab读取CSV文件转为NumPy数组
完整代码:https://github.com/kazunori279/ml-misc/blob/master/glove-sensor/Rock-paper-scissors.ipynb
你也可以使用Matplotlib库来可视化NumPy数组。下面代码画出了一个3D图,其中每个轴对应着一个不同传感器。
△
用3D图绘制传感器数据,已缩放原始多维数据
通过观察上面的3D图,你可以更清楚地看到数据的空间分布。
第5步:
创建一个线性模型
接下来,我们要将这些原始传感器数据划分到三种不同手势类别中。这里会用到线性代数,你们在高中或者大学应该都学过了。
线性代数是一种可以将某个空间映射到另一空间的数学方法。例如,下面公式表示了一种从某个一维空间到另一个一维空间的线性映射。
△
一元公式
其中,x和y分别为两个一维空间中的变量,w为权重,b为偏差。利用这个公式,可以将一维空间“纽约市出租车的行驶距离”映射到另一个一维空间“出租车费用”,其中把2.5美元(每英里费用)作为权重,把3.3美元(初始费用)设置为偏差。
△
“行驶距离”和“出租车费用”的映射函数
从图中看出,权重和偏差(也叫参数)分别定义该直线的斜率和初始位置。你可以通过调整这些参数,来创建从一个一维空间到另一个一维空间的任何线性映射。
线性代数的优点在于,在从任意m维空间到任意n维空间进行线性映射时,可使用相同公式。例如,在将三维空间(x1,x2,x3)中的某个点映射到另一个三维空间(y1,y2,y3)中,均可使用以下公式。
△
三维空间之间的映射函数
数学家认为上面公式写得太冗长了,所以设计了一种更容易的表示方法:矩阵乘法。
三维映射关系也可以这么表示:
或者,更简单写成:
其中,x和y为3维列向量,W为3×3的权重矩阵,b是3维偏置列向量。是的,它与一维空间的映射函数完全一样。而且,该公式可应用于m维空间和n维空间之间的任何线性映射,这就是所谓的“线性模型”。
那么,线性模型在本项目能起到什么作用呢?我们可以利用它,将“手套传感器数据”的3维空间转换为“石头剪刀布”的3维空间,如下所示:
△
3维空间的动态转换
在完成手套传感器数据与“石头剪刀布”3维空间的配对后,很容易写出用于分类的IF语句,如下: