深度学习入门——jetbot智能小车尝鲜(二)

那个男孩不想玩人工智能呢?在玄学修bug之后,我终于跑通了jetbot自带的深度学习demo。

怎样才能让ai程序发挥好的效果呢?众所周知,所谓人工智能,有多少人工就有多智能。

AI的发展离不开三个要素:算力算法算材。根据摩尔定律,算力的发展是不会停滞的(虽然定律快失效了);进几年来的AI热正是算法的突破,即深度学习相关算法的突飞猛进;而算材就是用来训练模型的数据,未来几年AI应用的进一步落地离不开算材的进一步开发(中国在AI方面的最大优势正在于此)。数据集的丰富程度和有效程度直接影响了AI应用的效果,我将在下文详细说明。

在jetbot项目中,我们也能体验到用“人工”换“智能”的快乐。作为视觉识别类的AI应用,我们要在预设环境里创建数据集,并为其标注。有了数据集,jetbot搭载的NVIDIA牌GPU在方寸之间就能完成海量计算,仅用一颗摄像头就能实现自动避障,目标追踪,自动巡线等等炫酷功能!不要1999,也不要999,只要99!99刀NVIDIA计算卡带回家!(妮维雅打钱)

给萌新理清几个概念:

人工智能,机器学习,深度学习的关系:

  • 深度学习:一种实现机器学习的技术;机器学习:一种实现人工智能的方法
  • 【包含关系图】

AI的发展路径:

  • 弱AI:单独领域工作效率超过人类→
  • 通用AI:可以广泛应用于大部分领域→
  • 强AI:有自主意识,即将灭绝人类(不是)→

现在AI发展到什么地步了:弱AI,有生之年可能见到通用AI

推荐一波汉化的很好的wiki,也有自己原创的内容:http://www.waveshare.net/wiki/JetBot_AI_Kit

本篇详细介绍两个demo的代码和可能遇到的问题,最后附上神经网络的入门笔记。同样是初次接触,大佬请绕道。

demo1:自动避障

小车如何实现自动避障的呢?用通俗的不能再通俗的说法,AI程序通过学习你给他的数据集,知道了什么样的图像是死路,什么样的图像是通路。得到新图像时就能判断是死路的概率有多少,在程序里可以很简单的看出,当这个概率大于0.5的时候就触发小车转向。

具体而言,你要在你的环境里拍至少200张照片,100张标记为通路(free),100张标记为死路(blocked)。这便是你的数据集(dataset)。构建数据集的时候尽量分散在环境的各个位置和各个方向,可以沿边界环绕一圈,走一段距离停下,转一圈,收集8-10张图片。反正你的数据越多,标记的越准确,模型效果越好。

下一步就开始训练模型了,从代码里看出,这个demo使用AlexNet模型,用pytorch实现(废话)。第一次运行你会下载一个244M左右的大文件,在/home/jetbot/.torch/models目录下会看到这个.pth文件。这便是AlexNet了。

继续运行程序,完整的输出结果有三十行,每行后面的小数代表当前模型的准确度(?),程序最后会从这30个模型中选取准确度最高的作为最终模型,也是一个pth文件:best_model.pth

下载文件和训练模型都需要花挺长时间,看到kernel busy,也就是右上角的大黑点不要轻易打断。

什么是模型呢?稍微解释一下机器学习的概念。

模型就是函数,其要素为输入,输出,和变换关系。举例说明:

模型 输入 输出
细菌向养分移动 外界环境的化学信号 催动鞭毛的电信号
学生参加高考 试卷反射的光信号 试卷上问题的答案
小车自动避障 摄像头传输图像信号 前方被堵塞的概率

实际上,知识的本质也是函数,生命延续的关键就在于该生命的模型是否适应环境。这里不深入解释了,觉得惊奇请参阅Yjango的频道https://space.bilibili.com/344849038他用机器学习的角度解释生物进化,非常颠覆三观。

总之训练出来的模型就是这样一个函数。其输入为经过处理的摄像头的图形信号,输出一个0-1的数,越接近1越意味着模型认为小车要撞墙了。但是当他大于0.5的时候就会触发转向,也就实现了自动避障。

AlexNet是2012年提出的一种卷积神经网络(即CNN)算法。首次实现gpu加速。

主流深度学习框架:TensorFlow;PyTorch;Keras

还挺好玩的😀

demo2:目标追踪

基于上一个demo,我们还要下载一个模型,coco数据集神经网络,可以检测90种不同的物体。按教程把.engine文件下载到指定位置,顺着跑就完事了。(引入模型也要花挺长时间)

如果有数据集里的物品,从输出里能看到蓝框标出,小车会自动转向物体,同时还保留了自动避障的程序。

遇到bug:程序仅能读取一张图像进行识别,摄像头更新的功能无法执行。

修bug:摄像头问题

描述:摄像头只要调用了一次,后面就无法在其他地方调用。直接在jupyter上关闭输出并没有作用。而且只要在一个notebook里就能重复调用,换一个就不行。而且并没有报错信息,程序一直处在busy状态。

找到源码,在jetbot/jetbot/camera.py,但是所有样例里面调用摄像头都是用的Camera.instance()方法,而这个instance是在traitlets库里的,于是找到trailets官方文档

Traitlets是一个纯 python 库,支持:

  • 对 python 对象属性的强类型实施( 类型属性称为 “特征” ) ;
  • 动态计算的默认值;
  • 当尝试改变时,自动验证和强制特征属性;
  • 当特征值改变时注册接收通知;
  • 从文件或者 命令行 参数中读取值- 在traitlets上不同层,因这里可以在没有配置机器的情况下使用 traitlets。

Traitlets支持IPython和Jupyter的配置系统,以及IPython交互小部件的声明性 API。

ipython是一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数。其中就包括traitlets库。

https://traitlets.readthedocs.io/en/stable/config.html 在这里找到instance的功能:返回现有的类,如果没有就新建一个。

下面是样例中调用摄像头的代码:

1
2
3
4
5
6
7
8
import ipywidgets.widgets as widgets  #图像模块
from IPython.display import display   #ipy的显示模块
import traitlets                      
from jetbot import Camera, bgr8_to_jpeg #摄像头驱动,图像格式转换
camera = Camera.instance(width=500, height=500)#初始化摄像头对象
image = widgets.Image(format='jpeg', width=400, height=400)#创建图像
camera_link = traitlets.dlink((camera, 'value'), (image, 'value'), transform=bgr8_to_jpeg)     #连接摄像头到图像
display(image)  #显示图像

尝试从camera.py里调用原始api。得到报错:Each object must be HasTraits, not <class 'NoneType'>,是说必须为对象指定类型。那么HasTraits这个类型是啥?文档说:任何具有trait属性的类都必须从 HasTraits 继承。

再次梳理调用摄像头的流程:

  • 引入模型:model.load_state_dict(torch.load('best_model.pth'))
  • 连接摄像头:见上文
  • 模型执行:
1
2
3
4
def update():
	...#此处为模型执行函数,将输入图像预处理后,执行模型
update({'new': camera.value})  #初始化该函数
camera.observe(update, names='value')  #将update函数设为camera.value的observer

研究一下observe用法:当对象发生变化时调用函数。

https://traitlets.readthedocs.io/en/stable/using_traitlets.html#validation

执行如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import ipywidgets.widgets as widgets  #图像模块
from IPython.display import display   #ipy的显示模块
import traitlets                      
from jetbot import Camera, bgr8_to_jpeg #摄像头驱动,图像格式转换
camera = Camera.instance(width=500, height=500)#初始化摄像头对象
def update(change):
	x = change['new'] 
	display(x)  #显示图像
update({'new': camera.value})  
camera.observe(update, names='value') 

输出一大堆数组,说明camera.value是这一大堆像素。而且observe正常运行,数据一直冒出。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
array([[[122, 116, 130],
        [126, 113, 127],
        [125, 117, 129],
        ...,
        [ 84,  96, 107],
        [ 82,  96, 113],
        [ 93,  93, 113]],

       [[120, 119, 130],
        [122, 120, 119],
        [118, 123, 130],
        ...,

然而就是不实时更新数据,卒。

👴佛了。

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy