那个男孩不想玩人工智能呢?在玄学修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的功能:返回现有的类,如果没有就新建一个。
下面是样例中调用摄像头的代码:
|
|
尝试从camera.py里调用原始api。得到报错:Each object must be HasTraits, not <class 'NoneType'>
,是说必须为对象指定类型。那么HasTraits这个类型是啥?文档说:任何具有trait属性的类都必须从 HasTraits
继承。
再次梳理调用摄像头的流程:
- 引入模型:
model.load_state_dict(torch.load('best_model.pth'))
- 连接摄像头:见上文
- 模型执行:
|
|
研究一下observe用法:当对象发生变化时调用函数。
https://traitlets.readthedocs.io/en/stable/using_traitlets.html#validation
执行如下代码:
|
|
输出一大堆数组,说明camera.value是这一大堆像素。而且observe正常运行,数据一直冒出。
|
|
然而就是不实时更新数据,卒。
👴佛了。