圣诞将至,你可能已经在商场、公园或自家的烟囱里发现了圣诞老人。随着机器识别技术的发展,用人工智能来识别路过或来送礼物的圣诞老人似乎是个不错的选择。近日,Adrian Rosebrock 在 PyImageSearch 上发表了一篇教程,介绍了在树莓派上使用 Keras 实现深度学习圣诞老人识别器的过程。你可通过文末链接访问原文——也可在原文末尾留下电子邮箱地址向原作者索取本项目的完整代码。另外,本教程中还提及了很多其它的关联教程和项目,为了更方便阅读,机器之心对这些链接进行了缩短处理。
在我写过的 PyImageSearch 教程中,这一篇是最有意思的!其中涉及到的内容包括:
- 深度学习
- 树莓派开发板
- 3D 圣诞树
- 参考 HBO 电视剧《硅谷》中的「Not Hotdog(不是热狗)」检测器
- 我打扮成圣诞老人
为了不辜负这个圣诞假期,我将在这里介绍如何将一个使用 Keras 训练的深度学习模型部署到树莓派上。
但这可不是随便什么机器学习模型……
这个图像分类器是专门为检测我们的视频流中是否存在圣诞老人而设计的。
如果我们确实检测到了圣诞老人……
好吧,我现在还不想剧透(但确实和 3D 圣诞树以及一首欢快的曲子有关)。
请享受这个教程,下载代码,然后自己动手实现吧!
最重要的是:要玩得开心哟!
在树莓派上用 Keras 做深度学习
这篇文章将完整地介绍使用 Keras 在树莓派上运行深度神经网络的过程。本项目的目标是实现一个 Not Santa(不是圣诞老人)检测器,这样可以具体地进行演示,也能让我们玩得开心。
在本文正文的第一部分,我们将谈论 Not Santa 检测器是什么(因为你可能并不了解 HBO 的《硅谷》电视剧中的 Not Hotdog 检测器,这东西已经圈粉了很多人)。
然后我们会配置我们的树莓派使其能够执行深度学习任务——我们要安装 TensorFlow、Keras 和其它一些必备软件包和库。
在我们的树莓派为深度学习做好了准备之后,我们将创建一个 Python 脚本,该脚本能够:
1. 从磁盘加载我们的 Keras 模型
2. 访问我们的树莓派相机模块/USB 网络摄像头
3. 应用深度学习来检测视频帧中是否存在圣诞老人
4. 如果存在圣诞老人,就访问我们的 GPIO 引脚并播放音乐
我在 PyImageSearch 上最喜欢写这一类文章了,因为这种教程能将多种不同的技术聚集到一起,这篇文章包含:
- 在树莓派上部署深度学习;可参阅:https://goo.gl/3MPJUp
- 访问树莓派相机模块/USB 网络摄像头;可参阅:https://goo.gl/NEZrGK
- 操作树莓派上的 GPIO 和计算机;可参阅:https://goo.gl/nwoS38
那我们就开始吧!
Not Santa 检测器是什么?
图 1:来自《硅谷》电视剧的 Not Hotdog 检测器应用
Not Santa 检测器的灵感来自 HBO 的电视剧《硅谷》,其中有角色创造了一个可以检测输入的图像是「热狗」或「不是热狗」的智能手机应用。
这个节目显然是在用美国硅谷的创业公司文化制造笑料,其中包括:
1. 炒作机器学习/深度学习概念
2. 讽刺他们重做了大量用途很小的智能应用(但其创造者却相信他们的应用将会「改变世界」)。
我决定自己也来找点乐子。
今天我们要创建一个 Not Santa 检测器,可以检测图像或视频帧中是否存在圣诞老人。
如果你不知道圣诞老人是啥,简单介绍一下。他是一个欢乐的、胖乎乎的、长着白胡子的虚构的西方文化形象,他会在平安夜孩子们睡觉时给他们带来礼物。
但是,我们这个应用不只是为了好玩或讽刺!
我们可以在这个过程中学习到很多实用的技能,包括:
1. 如何为深度学习任务配置你的树莓派
2. 在你的树莓派上安装 Keras 和 TensorFlow
3. 在你的树莓派上部署一个之前训练的卷积神经网络(使用 Keras)
4. 在检测到正例时执行给定的动作
但在深入代码之前,我们先看看我们需要的硬件。
需要什么硬件?
图 2:Not Santa 检测器的硬件包含树莓派 3、扬声器、3D 圣诞树和网络摄像头(图中没有)。这个树莓派中有用 Keras 实现的 LeNet 的 Python 脚本,可以检测圣诞老人。
如果你要按照这个教程进行操作(不加更改),你需要:
- 一块树莓派 3 开发板(或树莓派 3 入门套件,强烈推荐)
- 一个树莓派相机模块或一个 USB 摄像头。在本教程中,我使用了 Logitech C920,因为它的性价比不错(而且还有一条 USB 线能为你提供一点额外的操作空间,而不是树莓派相机那种很短的带线
- 树莓派可用的 3D 圣诞树(由 Rachel Rayns 设计):https://goo.gl/GSqxjs
- 一组扬声器——我推荐 Pi Hut 的这个:https://goo.gl/A7MmR4;来自 Adafruit 的这个:https://www.adafruit.com/product/1363。或者你喜欢小而有力的,可以选择这个:http://amzn.to/2AF920r
当然,你并不需要所有这些组件。
实际上只要有树莓派和相机模块/USB 摄像头就够了(但这样你就需要修改代码,使其不会试图访问 GPIO 引脚或通过扬声器播放音乐)。
你的配置应该和我的类似,如上面的图 2 所示,上面已经连接了扬声器、3D 圣诞树和网络摄像头(但图中看不到,因为它是 off camera)。
我还推荐在上面连接 HDMI 显示器和键盘,以便测试和调试你的脚本。
图 3:我的深度学习配置包含树莓派及其组件,另外还有键盘、鼠标和一个小型 HDMI 显示器。使用这个配置,我们肯定能抓住在我的圣诞树前送礼物的圣诞老人。
你可以在上图中看到我的树莓派、HDMI 显示器、键盘以及圣诞小动物朋友,它在我完成这个教程的过程中一直陪伴着我。
如何在树莓派上安装 TensorFlow 和 Keras?
图 4:我们将在树莓派上使用后端为 TensorFlow 的 Keras 来实现深度学习 Not Santa 检测器
我们之前介绍过如何使用 Keras 训练一个能够识别输入图像中是否有圣诞老人的卷积神经网络,参阅:https://goo.gl/imxkrY
这里我们将要之前训练的模型部署到树莓派上。
我之前也曾提到过,树莓派并不适用于训练神经网络(除了简单的试玩案例)。但在神经网络训练好了之后,我们可以使用树莓派来部署(当然,这个模型必须足够小,要能放进树莓派的内存中)。
我假设你已经在你的树莓派上安装了 OpenCV。如果你还没有在树莓派上安装 OpenCV,可以参考这个教程:https://goo.gl/ARPdYa。我在其中演示了为了提升速度而优化树莓派+OpenCV 安装的方法(可以实现 30% 的性能提升)。
注意:本教程不适用于 Python 3——你应该使用 Python 2.7。后面我会解释原因。现在你就使用 Python 2.7 和 OpenCV 配置你的树莓派吧。在树莓派+OpenCV 安装指南的第 4 步,一定要换成 -p python2 来创建一个虚拟环境。
现在,我建议你增加树莓派的 swap 空间。增大 swap 让你可以使用树莓派 SD 卡来增加内存(当你想在内存有限的树莓派上编译和安装大型库时,这个步骤会很关键)。
要增大你的 swap 空间,先打开 /etc/dphys-swapfile,然后编辑 CONF_SWAPSIZE 变量:
# set size to absolute value, leaving empty (default) then uses computed value
# you most likely don't want this, unless you have a special disk situation
# CONF_SWAPSIZE=100
CONF_SWAPSIZE=1024
注意这里我将 swap 从 100MB 增大到了 1024MB。
现在,重启 swap 服务:
$ sudo /etc/init.d/dphys-swapfile stop
$ sudo /etc/init.d/dphys-swapfile start
注:增大 swap 的大小会损伤你的内存卡,所以在你完事之后一定要撤销这个更改并重启 swap 服务。关于大 swap 损伤内存卡的信息可参阅:https://goo.gl/ZpIuaI
现在你的 swap 空间已经增大了,该配置开发环境了。
首先,使用 Python 2.7 创建一个名为 not_santa 的虚拟环境(我会在安装 TensorFlow 时解释使用 Python 2.7 的原因):
$ mkvirtualenv not_santa -p python2
注意这里的 -p 指向了 python2,表示这个虚拟环境将使用 Python 2.7。
如果你对 Python 虚拟环境不熟悉,不知道它们的工作方式和我们使用它的原因,请参阅这两个链接:https://goo.gl/nwoS38 和 https://goo.gl/1vPU6b
你还需要确保你已经将你的 cv2.so 捆绑包符号连接(sym-link)到了你的 not_santa 虚拟环境(如果你还没这样做):
$ cd ~/.virtualenvs/not_santa/lib/python2.7/site-packages
$ ln -s /usr/local/lib/python2.7/site-packages/cv2.so cv2.so
同样,要确保你已经使用 Python 2.7 捆绑包编译了 OpenCV。你还需要仔细检查你的 cv2.so 文件的路径,以防你的安装路径和我的路径有所不同。
如果你已经编译了 Python 3 + OpenCV 并且创建了 sym-link,那么就尝试 import cv2 到你的 Python shell 中,你会收到一个让人困惑的 traceback 说这个导入失败了。
重要说明:对于接下来的几个 pip 命令,要确保你在 not_santa 环境(或你选择的 Python 环境)中,否则你就要把这些包安装到你的树莓派的系统 Python 上。
要进入该环境,只需在 bash 提示符使用 workon 命令:
$ workon not_santa
然后,你会看到「(not_santa)」出现在你的 bash 提示符的开始处。
确保你使用以下命令在 not_santa 环境中安装了 NumPy:
$ pip install numpy
因为这个项目需要访问 GPIO 引脚,所以我们需要安装 RPi.GPIO 和 gpiozero:
$ sudo pip install RPi.GPIO gpiozero
现在在你的树莓派上安装 TensorFlow
这里有一个问题:没有合适的官方(谷歌发布的)TensorFlow 版本。
我们可以从头开始编译适用于树莓派的 TensorFlow,但这个过程非常漫长、麻烦和痛苦,可以参考:https://goo.gl/a2hnzK
或者我们也可以使用 Sam Abrahams 创建的预编译后的二进制文件,GitHub 地址:https://goo.gl/WJzbL6
但问题是只有两种预编译的 TensorFlow 二进制文件:
1. 用于 Python 2.7 的
2. 用于 Python 3.4 的
而 Raspbian Stretch 发行版(在本文写作时的最新版 Raspbian 操作系统)采用的是 Python 3.5——所以这里有个版本不匹配的问题。
为了避免 Python 3.4 和 Python 3.5 之间的麻烦,我决定还是使用 Python 2.7 好了。
尽管我很想在本教程中使用 Python 3,但这样的话安装过程就会变得更加复杂(而且因为本教程关注的重点不是安装,所以我决定让这个任务更简单一点)。
让我们使用以下命令安装使用 Python 2.7 的 TensorFlow:
$ wget https://github.com/samjabrahams/tensorflow-on-raspberry-pi/releases/download/v1.1.0/tensorflow-1.1.0-cp27-none-linux_armv7l.whl
$ pip install tensorflow-1.1.0-cp27-none-linux_armv7l.whl
注:上面代码中的 URL 较长,注意识别。
编译和安装了 TensorFlow 之后(在我的树莓派上用了大概一个小时),你需要安装 HDF5 和 h5py。这些库让我们可以从磁盘加载我们之前训练的模型:
$ sudo apt-get install libhdf5-serial-dev
$ pip install h5py
我安装 HDF5 和 h5py 的时候没有运行 time 命令,所以我不记得安装它们用去了多少时间,我估计大概是 30-45 分钟。
最后,让我们安装 Keras 和本项目所需的其它包:
$ pip install pillow imutils
$ pip install scipy --no-cache-dir
$ pip install keras
SciPy 的安装需要几个小时,所以你可以在工作时或晚上安装它。
要测试你的配置,可在 not_santa 环境中打开 Python shell,然后执行以下命令:
$ workon not_santa
$ python
>>> import h5py
>>> from gpiozero import LEDBoard
>>> from gpiozero.tools import random_values
>>> import cv2
>>> import imutils
>>> import keras
Using TesnsorFlow backend.
>>> print("OpenCV version: {}".format(cv2.__version__))
'3.3.1'
>>> print("Keras version: {}".format(keras.__version__))
'2.0.8'
>>> exit()
如果一切进展顺利,你应该会看到使用 TensorFlow 后端导入的 Keras。
正如上面的输出演示的那样,你应该再次检查是否可以导入你的 OpenCV 捆绑包(cv2)。
最后,不要忘记将你的 swap 大小从 1024MB 改回 100MB,步骤是:
1. 打开 /etc/dphys-swapfile
2. 将 CONF_SWAPSIZE 重置为 100MB
3. 重启 swap 服务(就像我们前面说的那样)。
就像前面说的那样,将 swap 大小改回 100MB 有利于内存卡的使用寿命。如果你跳过这个步骤,你可能会遇到内存受损问题,而且卡的使用寿命也会变短。
在树莓派上运行 Keras 深度学习模型
图 5:使用 Keras 和 Python 在树莓派上运行深度学习模型
现在我们可以开始使用 Keras、TensorFlow 和树莓派来编写 Not Santa 检测器的代码了。
再次重申,我会假设你的硬件配置和我的一样(即 3D 圣诞树和扬声器),所以如果你的配置不一样,你需要自己写或修改下面的代码。
首先,创建一个新文件,将其命名为 not_santa_detector.py,然后贴入以下代码:
其中第 2-12 行代码处理我们的导入,需要说明的是:
- keras 用于预处理用于分类的输入帧,以及用于从磁盘中加载训练好的模型。
- gpiozero 用于访问 3D 圣诞树。
- imutils 用于访问视频流(不管是树莓派相机模块还是 USB)。
- threading 用于非阻塞(non-blocking)操作,尤其是当我们需要在点亮圣诞树或播放音乐的同时不阻塞主线程的执行时。
为此,我们要定义一个用于点亮 3D 圣诞树的函数:
我们的 light_tree 函数接受一个 tree 参数(这应该是一个 LEDBoard 对象)。
首先,我们在 tree 中循环所有的 LED 并随机点亮每个 LED 以创造闪烁效果(第 17-19 行)。
我们让灯保持亮一段时间(第 23 行),然后再次循环这些 LED,这一次是将它们关闭(第 26-28 行)。
3D 圣诞树亮灯的效果如下:
图 6:树莓派控制的 3D 圣诞树
我们的下一个函数会在检测到圣诞老人时播放音乐:
在 play_christmas_music 函数中,我们做了一次对 aplay 命令的系统调用,这让我们可以用命令行播放一个音乐文件。
使用 os.system 调用需要花一点功夫,但通过纯 Python 来播放音频文件(使用 Pygame 这样的库)对这个项目而言就有些大材小用了。
因此,让我们硬编码我们将用到的配置:
第 38 和 39 行硬编码了我们训练好的 Keras 模型与音频文件的路径。
我们也初始化了用于检测的参数,其中包括 TOTAL_CONSEC 和 TOTAL_THRESH。这两个值分别表示包含圣诞老人的帧的数量以及我们播放音乐和开启圣诞树灯光的阈值。(第 43 和 44 行)
最后的初始化是 SANTA = False,这是一个布尔值(第 47 行)。我们后面会在脚本使用 SANTA 变量作为状态标志来辅助我们的逻辑。
接下来,我们加载我们之前训练的 Keras 模型并初始化我们的圣诞树:
Keras 让我们可以将模型保存下来以备后来使用。在 https://goo.gl/imxkrY 这个教程中,我们将 Not Santa 模型保存到了磁盘,现在我们要将其载入到我们的树莓派上。我们在第 51 行使用 Keras 的 load_model 函数载入了该模型。
我们的 tree 对象在第 54 行进行了实例化。可以看到,tree 是来自 gpiozero 包的 LEDBoard 对象。
现在我们初始化我们的视频流:
为了访问相机,我们要使用来自 imutils 包的 VideoStream(第 58 或 59 行),参阅文档:https://goo.gl/NEZrGK
重要说明:如果你用的是 PiCamera 模块(而不是 USB 摄像头),那就直接注释掉第 58 行并去掉第 59 行的注释。
我们 sleep 了 2 秒钟,这样我们可以在开始循环帧之间预热我们的相机(第 60 行):
在第 63 行,我们开始循环视频帧,直到满足停止条件(在脚本后面给出)。
首先,我们通过调用 vs.read 抓取帧 frame(第 66 行)。
然后我们将 frame 的大小调整为 width=400,并保持原来的纵横比(第 67 行)。我们会在预处理 frame 之后才将其发送给我们的神经网络模型。之后我们会将该帧与一个文本标签一起展示在屏幕上。
因此,让我们预处理图像并将其传递给我们的 Keras 深度学习模型进行预测:
第 70-73 行是对 image 进行预处理,准备用于分类。更多有关深度学习预处理的信息可参考我最新的书《Deep Learning for Computer Vision with Python》:https://goo.gl/jTYng4
然后,我们使用 image 作为参数来查询 model.predict。这会将 image 发送给神经网络,然后返回一个包含类概率的元组(第 77 行)。
我们将 label 初始化为 "Not Santa"(后面我们还会访问 label),并将其概率 proba 初始化为 notSanta 的值(第 78 和 79 行)。
让我们看看图像中是否有圣诞老人:
第 83 行是检查 santa 的概率是否大于 notSanta。如果确实大于,我们继续并更新 label 和 proba,之后 TOTAL_CONSEC 递增(第 85-90 行)。
当有足够多连续的有圣诞老人的帧通过之后,我们需要触发圣诞老人警报:
如果 SANTA 是 False 且如果 TOTAL_CONSEC 达到了 TOTAL_CONSEC,会执行两个动作:
1. 创建并启动一个 treeThread 来闪烁圣诞树的灯(第 98-100 行);
2. 创建并启动一个 musicThread 来播放背景音乐(第 103-106 行)。
这些线程会独立运行,而不会阻止该脚本的前向执行(即:非阻塞操作)。
你也可以看到我们在第 95 行将我们的 SANTA 状态标志设置成了 True,表明我们在该输入帧中找到了圣诞老人。在该循环的下一次通过中,我们将检查这个值,如第 93 行所示。
否则(SANTA 是 True 或还未达到 TOTAL_THRESH),那么我们就将 TOTAL_CONSEC 重置为零,将 SANTA 重置为 False:
最后,我们将带有生成的文本标签的帧展示在我们的屏幕上:
概率值也附加到了带有「Santa」或「Not Santa」的 label 上(第 115 行)。
然后我们可以使用 OpenCV 的 cv2.putText 在帧上面写上标签(用圣诞主题的绿色),然后我们将该帧展示在屏幕上(第 116-120 行)。
我们的无限 while 循环的退出条件是按下键盘上的「q」键(第 121-125 行)。
如果该循环的退出条件得到满足,我们就 break,并且在脚本本身退出之间执行一些清理(第 129-130 行)。
这就是所有全部了。你应该整体回顾一下这 130 行代码——这个框架/模板也可以轻松用于树莓派上的其它深度学习项目。
现在就让我们找到那个胖乎乎、大胡子的欢乐圣诞老人吧!
深度学习+Keras+树莓派得到的结果
在之前开发 Not Santa 深度学习模型时,我们使用了从网上收集到的图像。
但那很没意思——也不符合我们这篇文章的追求。
我一直都想打扮成圣诞老人的样子,所以我上周订购了一套便宜的圣诞老人套装:
图 7:我 Adrian Rosebrock 的圣诞老人扮相。我将亲自上阵测试我们用深度学习、Keras、Python 和 OpenCV 创造的 Not Santa 检测器。我的长相与真正的圣诞老人差距很大,但这个道具服应该就够了。
然后我将树莓派上连接的相机安装到了我公寓里的圣诞树上:
图 8:我自己的圣诞树将作为我们的树莓派 Not Santa 检测器深度学习模型的测试背景。
如果圣诞老人来到这颗圣诞树下给好孩子送礼物,我们要通过闪烁 3D 圣诞树灯和播放圣诞曲子来欢迎他。
然后我使用以下命令启动了 Not Santa 深度学习+Keras 检测器:
$ python not_santa_detector.py
如果你想按照这个教程走,你可以在原文最后的下载环节下载源代码、预训练模型和音频文件。
Not Santa 启动并运行之后,我开始行动了:
图 9:使用深度学习、Python、Keras 和树莓派成功检测到视频流中的圣诞老人
当检测到圣诞老人时,3D 圣诞树灯点亮,音乐开始播放。
这是是带声音的演示视频地址::https://www.youtube.com/watch?v=RdK-8pSQIP0
每当圣诞老人进入视野时,你都可以看到 3D 圣诞树灯开启,同时伴随着树莓派扬声器发出的欢乐笑声。
我们的这个深度学习模型是一个很小的网络架构,但准确度和稳健性方面的表现很让人惊喜。
我这一年来一直都乖乖的,所以我相信圣诞老人会到我的公寓来。
我也比以往更相信我的 Not Santa 检测器会看到圣诞老人送礼物给我。
在圣诞节之前,我可能还会修改一下这个脚本(调用一下 cv2.imwrite 或更好是保存视频),以确保我将圣诞老人的影像保存到磁盘上留作证据。要是有其他人在我的圣诞树下放了礼物,我一定会知道的。
亲爱的圣诞老人:要是你读到了这篇文章,你就知道我用树莓派逮到你了!
总结
在这篇文章中,你学习到了如何在树莓派上运行 Keras 深度学习模型。
要实现这一目标,我们首先在笔记本电脑或桌面计算机上训练了一个可以检测图像中是否包含「Santa」或「Not Santa」的 Keras 深度学习模型。
然后我们将 TensorFlow 和 Keras 安装到了我们的树莓派上,这让我们可以将我们之前训练的深度学习图像分类器部署到这个树莓派上。尽管树莓派并不适合训练深度神经网络,但可以用于部署这些网络——只要网络架构足够简单,我们甚至可以实时运行我们的模型。
为了演示这一点,我们在树莓派上创建了一个 Not Santa 检测器,可以分类视频流中的每一个输入帧。
如果检测到了圣诞老人,我们就访问 GPIO 引脚来点亮 3D 圣诞树和播放节日乐曲。
原文链接:https://www.pyimagesearch.com/2017/12/18/keras-deep-learning-raspberry-pi/