购买笔记本拥有Intel集成显卡和NVIDIA显卡,如何在Debian Linux下启用NVIDIA显卡是一个难题。幸运的是,通过bumblebee软件可在Linux下支持NVIDIA Optimus技术,实现Intel集成显卡和NVIDIA显卡之间的切换使用。

背景知识

NVIDIA Optimus

Optimus是NVIDIA针对笔记本电脑的显示切换技术。它可以实现笔记本电脑中独立显卡与主板内置显卡之间无缝、自动、实时的切换(用户无需重启或手动调整),从而实现能源的节约。

利用Optimus技术后,计算机的显示输出永远都只会通过集成式芯片组的显示核心。独立显卡的显示输出将会被废掉,有关的输出亦会发送到前者。由于显示输出的设备是固定的,所以黑屏现象将会被解决。额外的切换线路亦变得不需要,因为在不需要独立显示核心的时候,系统会索性断绝其电源供应。当系统的显示负载轻的时候,就会完全利用内置的显示核心。当负载增加后,系统就会打开独立显示核心作计算之用。协议分工方面由驱动程序负责,NVIDIA声称可以兼容所有的API。

Bumblebee

Bumblebee 致力于使 NVIDIA Optimus 在 GNU/Linux 系统上可用,实现两块不同的供电配置的显卡同时插入使用,共享同一个 framebuffer。

安装配置过程

禁止加载开源驱动nouveau

1
2
3
4
5
6
7
8
$ vi /etc/modprobe.d/nvidia-installer-disable-nouveau.conf

#上述文件不存在则新建,在文件中添加下面两行文字
blacklist nouveau
options nouveau modeset = 0

#系统重启后使用以下命令查看nouveau驱动是否被禁止
lsmod |grep -i nouveau

安装内核头文件

1
$ sudo apt-get install -y linux-headers-$(uname -r)

安装NVIDIA驱动

1
2
# 关于是否生存xorg.conf文件,选no
$ sudo apt-get install nvidia-driver nvidia-xconfig nvidia-kernel-dkms

安装双显卡切换解决方案包

1
$ apt-get install bumblebee-nvidia primus

添加当前用户到bumblebee用户组

1
$ adduser $USER bumblebee

修改bumblebee配置

1
2
3
4
5
6
7
8
9
10
11
12
$ vi /etc/bumeblebee/bumblebee.conf
: Driver=nvidia

#将/etc/bumeblebee/bumblebee.conf文件中的Driver=* 改成 =nvidia

$ vi /etc/bumblebee/xorg.conf.nvidia
:BusID "PCI:01:00:0"

#在/etc/bumblebee/xorg.conf.nvidia文件中,将Section“Device”部分添加PCI ID

# 可使用如下命令查看NVIDIA显卡的BUS ID
nvidia-xconfig --query-gpu-info | grep 'BusID : ' | cut -d ' ' -f6

重启进入桌面

如果无法进入桌面环境,则安装失败,进入高级修复使用如下命令进行清理。

1
$ sudo apt-get purge nvidia* bumblebee* prime*

bumblebee服务开机不会启动,设置开机启动即可。

1
$ update-rc.d -f bumblebee defaults

使用独显运行程序

bumblebee不能做到集显和独显之间自动切换,系统运行时默认使用集显,当你需要使用独显运行某个程序或者游戏的时候,终端执行如下命令即可:

1
2
$ optirum command #使用独显运行command程序
$ optirum -b primus command #使用独显运行command程序,提升性能

参考链接

  1. kali 在支持双显卡切换机器宏碁(Acer)V3下安装NVIDIA驱动, by 不梦君.
  2. Installing-Nvidia-drivers-on-Kali-Linux, by europa502.
  3. NVIDIA Optimus, by wikipedia.
  4. 在Ubuntu上安装和配置Nvidia Optimus驱动, by Linux中国.
  5. Bumblebee,by archlinux.

Linux初学者经常分不清楚linux和X之间,X和Xfree86之间,X和KDE,GNOME等之间是什么关系。常常混淆概念,下面以易于理解的方式解释X,X11,XFREE,WM,KDE,GNOME等之间的关系。

Linux内核没有图形用户界面

与Windows操作系统内核集成图形用户界面不同,Linux内核没有图形用户界面,是一个基于命令行的操作系统。

linux和Xfree的关系就相当于当年的DOS和 WINDOWS3.0一样,windows3.0不是独立的操作系统,它只是DOS的扩充,是DOS下的应用程序级别的系统,不是独立的操作系统,同样 XFree只是linux下的一个应用程序而已。不是系统的一部分,但是X的存在可以方便用户使用电脑。

WINDOWS95及以后的版本就不一样了,他们 的图形界面是操作系统的一部分,图形界面在系统内核中就实现了,没有了图形界面windows就不成为windows了,但linux却不一样,没有图形 界面linux还是linux,很多装linux的WEB服务器就根本不装X服务器.这也WINDOWS和linux的重要区别之一。

X是协议,不是具体的某个软件

X 是协议,就像HTTP协议,IP协议一样。一个基于X的应用程序需要运行并显示内容时他就联 接到X服务器,开始用X协议和服务器交谈。

比如一个X应用程序要在屏幕上输出一个圆那么他就用X协议对X服务器说:喂!我需要在屏幕上画一个圆.X应用程 序只负责告诉X服务器在屏幕的什么地方用什么颜色画一个多大的圆,而具体的”画”的动作,比如这个圆如何生成,用什么显卡的驱动程序去指挥显卡完成等等工作是由X服务器来完成的。

X服务器还负责捕捉键盘和鼠标的动作,假设X服务器捕捉到鼠标的左键被按下了,他就告诉X应用程序:亲爱的应用程序先生,我发现 鼠标被按下了,您有什么指示吗?如果X应用程序被设计成当按下鼠标左健后再在屏幕上画一个正方形的话,X应用程序就对X服务器说:请再画一个正方形,当然 他会告诉服务器在什么地方用什么颜色画多大的正方形,但不关心具体怎么画–那是服务器的事情。

XFree86和Xorg是实现X协议的服务器程序

Xfree86就是这样一个去根据法规实现协议的 “交警”。他按照X协议的规定来完成X应用程序提交的在屏幕上显示的任务。当然不仅仅是某个特定的交警才可以去维护和实现这个法规,比如还可以由交通协管 员来实现交通法规,必要的时候警察也可以介入,当然前提是他们都要懂得交通法规,也就是要懂得协议。

所以实现X协议的软件也并不只有 XFree86,XFree86只是实现X协议的一个免费X服务器软件.商业上常用MOTIF,现在还有XORG,还有很多很小的由爱好者写的小的X服务器软件。

X和X11R6的关系

X11R6 实际上是 X Protocol version 11 Release 6
(X协议第11版第六次发行)的意思,就是说目前用的X协议是第11版的,然后经过了6次小的修正。

不同版本的X协议是不能通信的。就象我们现在IPV4和IPV6不能通信一样,不过不用担心,现在的X服务器软件和X应用程序都遵循X11R6。

X服务器和WM(window manager 窗口管理器)的关系

窗口管理器是一种X应用程序,它负责窗口最大化,最小化,移动,关闭窗口等。这些不是X服务器负责完成的。

KDE和GNOME

KDE 和GNOME是LINUX里最常用的图形界面操作环境,他们不仅仅是一个窗口管理器那么简单, KDE是K Desktop Environment 的缩写。他不仅是一个窗口管理器,还有很多配套的应用软件和方便使用的桌面环境,比如任务栏,开始菜单,桌面图标等等。

GNOME是GNU Network Object Model Environment 的缩写。和KDE一样,也是一个功能强大的综合环境。

另外在其它UNIX系统中,常常使用CDE作为这样一个环境。

其它的小型窗口管理器有: window maker,after step,blackbox,fvwm,fvwm2,等等都是常用的优秀窗口管理器。

linux图形界面层次关系总结

linux本身–>X服务器<-[通过X协议交谈]->窗口管理器(综合桌面环境)–>X应用程序。

Xfree86服务器的实现包括两个部分,一部分是和显卡直接打交道的低层,一部分是和X应用程序打交道的上层。

上层负责接收应用程序的请求和鼠标 键盘的动作。而和显卡直接打交道的底层负责指挥显卡生成图形,其实就是显卡驱动。上层接收到应用程序的请求后,将请求内容做适当处理,然后交给显卡驱动来 指挥 显卡完成画图的动作。

另外,上层的捕捉键盘和鼠标动作的部分会向应用程序提供鼠标和键盘的状态信息,应用程序接收到这些信息后决定是否再有相应的动作。

参考链接

  1. linux图形界面基本知识(X、X11、Xfree86、Xorg、GNOME、KDE), by huang_xw.

家用电器都有一定的安全使用年限,超过安全使用年限,家用电器因产品老化将产生如下问题,造成安全隐患:

  • 绝缘体老化,产生漏电,导致电磁污染
  • 元器件技术指标严重下降,导致有害物质泄漏,耗电量增加

那么家用电器的安全使用年限是多少呢?购买二手家用电器时如何折旧呢?

家用电器安全使用年限

有关家电安全使用年限的标准通则早已出台,但是针对不同产品的具体年限细则却迟迟未发布。原因主要在于不同产品的使用年限评估难度较大,不同地区的使用差异性也较大,因此就目前而言国内尚无明确的家电使用年限方面的规定,国际通行年限可作参考:

  • 电吹风:4年
  • 电热毯:6年
  • 电脑:6年
  • 吸尘器:8年
  • 煤气灶:8年
  • 电热水器:8年
  • 洗衣机:8年
  • 电视:8-10年
  • 空调:8-10年
  • 微波炉:10年
  • 电饭煲:10年
  • 电风扇:10年
  • 冰箱:12-16年

家用电器安全使用年限只是最高限定,如果在使用过程中不注意保养维护,也有可能缩减家用电器的使用寿命,这就要求用户在日常生活中需要注重维护保养,使家电处于良好的工作环境,确保家电安全“服役”。

家用电器老化特征

家用电器也有各自的老化特征,当出现以下老化特征时,建议及时保养或更换:

  1. 空调:如果使用多年的空调开始出现制冷或制热变慢,噪音过大,在开机时会出现霉味且出风伴有灰尘,流出黑水,那么用户就应该考虑更换一台新空调。老旧空调此时不仅耗费更多电量,出风还会带有细菌污垢,影响身体健康。

  2. 冰箱:平时运行良好的冰箱开始出现制冷效果差,噪音过大,甚至发生轻微颤动时,说明已经老化。若继续使用,耗电量将会加大,食物的储鲜功能也会退化,有时还会出现冰箱制冷剂泄露,污染食材环境的问题。

  3. 洗衣机:洗衣机使用超龄的现象在用户中最为常见,判断它已经超龄的方式也最为简单。通常来说,洗衣机出现渗水、漏水、漏电,噪音过大,衣物清洗不净等问题都是达到使用年限的表现,严重时甚至会发生爆炸事件。

  4. 电视机:虽然各种类型的平板、投影仪的市场占有率越来越高,但电视机仍然是每个家庭不可或缺的电器之一。日常使用电视时,屏幕色彩暗淡,画面模糊抖动,除了要考虑电视自身质量问题,还要查看电视使用时长是否已经超出使用年限。超龄服役的电视机除了耗电量增加外,自燃或爆炸事故也时有发生。

  5. 热水器:热水器超出使用年限后,在运行时会出现异常声音,安全装置也会发生功能异常问题,再加上内胆中日积月累的杂质污垢,不仅会污染水质,还可能因漏电对人体造成伤害。除此之外,超龄热水器还易出现漏水、保温性差以及因内部电器元件老化导致的短路、冒烟等故障。

一旦发现家中电器存在以上类似的老化特征时,最好提前更换产品,以免带来严重后果。据媒体公开报道,每年我国因家电触电伤亡人数超过1000人,因家电引起的火灾损失更加惊人,而这其中最典型的原因就是家电超龄“使用”。

家用电器折旧

家用电器折旧可简单按固定资产折旧方法中的直线法,根据家用电器安全使用年限逐年折旧。

固定资产折旧方法

固定资产折旧方法(Fixed-assets Depreciation Method),指将应提折旧总额在固定资产各使用期间进行分配时所采用的具体计算方法。折旧是指固定资产由于使用而逐渐磨损所减少的那部分价值。

固定资产的损耗有两种:

  • 有形损耗,也称作物质磨损,是由于使用而发生的机械磨损,以及由于自然力的作用所引起的自然损耗。
  • 无形损耗,也称精神磨损,是指科学进步以及劳动生产率提高等原因而引起的固定资产价值的损失。

一般情况下,当计算固定资产折旧时,要同时考虑这两种损耗。

固定资产折旧方法可分为三类:

直线法

直线法(straight-line method)根据固定资产在整个使用寿命中的磨损状态而确定的成本分配结构。直线法依据是,固定资产的使用强度比较平均,而且各期所取得的收入差距不大。在直线法下,折旧金额是时间的线性函数。

直线法折旧公式如下:

年折旧率 = (1 - 预计净残值率)÷ 预计使用寿命(年)× 100%

月折旧额 = 固定资产原价× 年折旧率 ÷ 12

其中,净残值是指预计固定资产清理报废时可收回的残值扣除清理费用后的数额。企业应根据固定资产的性质和使用方式,合理估计固定资产的净残值。

加速折旧法

加速折旧法也称为快速折旧法或递减折旧法。其特点是:在固定资产有效使用年限的前期多提折旧,后期则少提折旧。从而相对加快折旧的速度,以使固定资产成本在有效使用年限中加快得到补偿。在具体实务中,加速折旧方法又包括年数总和法和双倍余额递减法两种。

年数总和法计算公式如下:

年折旧率 = 尚可使用寿命/ 预计使用寿命的年数总和 × 100 %

月折旧额 = (固定资产原价 - 预计净残值 )× 年折旧率 ÷ 12

某设备预计使用5年,则预计使用寿命的年数总和为15(5+4+3+2+1)。第2年时尚可使用寿命为4,此年的年折旧率为4/15。

双倍余额递减法计算公式如下:

年折旧率 =2÷ 预计使用寿命(年)× 100%

月折旧额 = 固定资产净值 × 年折旧率 ÷ 12

产量法

产量法(production method)根据固定资产的产出量分配其成本的方法。产量法的依据是固定资产的使用寿命主要是受其使用量影响,其合理性取决于三个条件:

(1)固定资产的每期用量波动较大;

(2)固定资产使用期内的产出的每期用量波动较大;

(3)固定资产服务潜力的下降与其使用程序密切相关。

如果符合这三个条件,产量法是最符合配比原则的。反对产量法的人认为,估计固定资产的产出量比估计固定资产的使用年限更加困难,而且很难考虑功能损耗因素。

产量法计算公式如下:

单位工作量折旧额 = 固定资产原价 × ( 1 - 预计净残值率 )/ 预计总工作量

某项固定资产月折旧额 = 该项固定资产当月工作量 × 单位工作量折旧额

参考链接

  1. 家用电器的使用年限是多久?如何尽可能延长其寿命?,by 海尔智慧生活.
  2. 固定资产折旧方法,by 百度百科.

空战类游戏仿真涉及飞机、导弹、诱饵弹三种关键元素。为了使游戏仿真接近真实,一方面要使飞机、导弹、诱饵弹的模型及其特效(如尾焰)尽量逼真,另一方面要使飞机、导弹、诱饵弹的运动控制模型尽量接近真实。飞机、导弹、诱饵弹的模型可以用3d建模软件构建,尾焰也可以用粒子系统进行模拟,但飞机、导弹、诱饵弹的运动控制模型比较复杂,是实现空战类游戏仿真的关键。下面分别介绍飞机、导弹、诱饵弹的建模仿真。

飞机的建模仿真

飞机的运动模型基于牛顿第二运动定律,即物体所受到的外力等于此物体的质量与加速度的乘积,而加速度与外力同方向。根据外部受力和力矩计算出各种惯性坐标系下的加速度和角加速度。将加速度和角加速度分别进行积分,则可以得出飞机在某个时间段内的速度变化量和角速度变化量,进一步积分便可得到位置变化量和角度的变化量,再通过积分过程中的坐标变换以及初始时刻的状态参数,便可获得任意时刻飞机的姿态、位置、速度等参数。

飞行员通过油门杆和操纵杆控制飞机的运动。油门杆决定飞机的推力,操纵杆则控制飞机的副翼、襟翼、尾翼,改变飞机所受合力矩,从而改变飞机姿态。飞行仿真采用同样原理,油门杆和操作杆的位移经过非线性函数变换改变飞机所受合力和合力矩,经飞机运动模型获得加速度和角加速度,再经积分获得任意时刻飞机的姿态、位置、速度等参数。

当使用键盘输入控制飞机时,应将键盘输入先虚拟成油门杆和操作杆。另外需要注意的是,油门杆不回中,操作杆则自动回中。

导弹的建模仿真

根据导弹的工作原理,导弹的建模仿真应分成三个方面:

  • 探测模型
  • 目标识别算法
  • 运动控制模型

诱饵弹的建模仿真

诱饵弹的运动模型比较简单,可运用牛顿第二运动定律分析得出。比较有难度的是诱饵弹的视觉特效建模。诱饵弹是一个变质量、不规则的发光体,如何3D建模有难度,不能像太阳一样直接用图片代替。

参考链接

  1. 无人机飞控通俗演义之 纷繁复杂的飞行力学, by 量子黑洞.
  2. 一起写飞行仿真之 六自由度动力学方程, by 量子黑洞.
  3. 飞行仿真–1.三维建模篇, by WFYX.
  4. 无人机飞控通俗演义之 无人机导航制导与控制, by WFYX.
  5. FLIGHTGEAR FLIGHT SIMULATOR, by flightgear.

计算机视觉是一个跨领域的交叉学科,包括计算机科学(图形、算法、理论、系统、体系结构),数学(信息检索、机器学习),工程学(机器人、语音、自然语言处理、图像处理),物理学(光学 ),生物学(神经科学)和心理学(认知科学)等等。那么什么是计算机视觉呢?这里给出了几个比较严谨的定义:

  1. “对图像中的客观对象构建明确而有意义的描述”(Ballard&Brown,1982)
  2. “从一个或多个数字图像中计算三维世界的特性”(Trucco&Verri,1998)
  3. “基于感知图像做出对客观对象和场景有用的决策”(Sockman&Shapiro,2001)

研究范畴

视觉识别是计算机视觉的关键组成部分,如图像分类、定位和检测。神经网络和深度学习的最新进展极大地推动了这些最先进的视觉识别系统的发展。下面介绍 5 种主要的计算机视觉技术:

  • 图像分类

给定一组各自被标记为单一类别的图像,我们对一组新的测试图像的类别进行预测,并测量预测的准确性结果,这就是图像分类问题。图像分类问题需要面临以下几个挑战:

视点变化,尺度变化,类内变化,图像变形,图像遮挡,照明条件和背景杂斑

  • 对象检测

识别图像中的对象这一任务,通常会涉及到为各个对象输出边界框和标签。这不同于分类/定位任务——对很多对象进行分类和定位,而不仅仅是对个主体对象进行分类和定位。在对象检测中,你只有 2 个对象分类类别,即对象边界框和非对象边界框。例如,在汽车检测中,你必须使用边界框检测所给定图像中的所有汽车。

  • 目标跟踪

目标跟踪,是指在特定场景跟踪某一个或多个特定感兴趣对象的过程。传统的应用就是视频和真实世界的交互,在检测到初始对象之后进行观察。现在,目标跟踪在无人驾驶领域也很重要,例如 Uber 和特斯拉等公司的无人驾驶。

  • 语义分割

计算机视觉的核心是分割,它将整个图像分成一个个像素组,然后对其进行标记和分类。特别地,语义分割试图在语义上理解图像中每个像素的角色(比如,识别它是汽车、摩托车还是其他的类别)。

  • 实例分割

除了语义分割之外,实例分割将不同类型的实例进行分类,比如用 5 种不同颜色来标记 5 辆汽车。分类任务通常来说就是识别出包含单个对象的图像是什么,但在分割实例时,我们需要执行更复杂的任务。我们会看到多个重叠物体和不同背景的复杂景象,我们不仅需要将这些不同的对象进行分类,而且还要确定对象的边界、差异和彼此之间的关系!

典型应用

计算机视觉领域已经衍生出了一大批快速成长的、有实际作用的应用,例如:

  • 人脸识别: Snapchat 和 Facebook 使用人脸检测算法来识别人脸。
  • 图像检索:Google Images 使用基于内容的查询来搜索相关图片,算法分析查询图像中的内容并根据最佳匹配内容返回结果。
  • 游戏和控制:使用立体视觉较为成功的游戏应用产品是:微软 Kinect。
  • 监测:用于监测可疑行为的监视摄像头遍布于各大公共场所中。
  • 生物识别技术:指纹、虹膜和人脸匹配仍然是生物识别领域的一些常用方法。
  • 智能汽车:计算机视觉仍然是检测交通标志、灯光和其他视觉特征的主要信息来源。

前沿研究

  • 超分辨率重建 (Image Super-Resolution)
  • 图像着色 (Image Colorization)
  • “看图说话” (Image Caption)
  • 人像复原 (Sketch Inversion)
  • 图像自动生成

参考文献

  1. 详解计算机视觉五大技术:图像分类、对象检测、目标跟踪、语义分割和实例分割…, by AI科技大本营
  2. 「无中生有」计算机视觉探奇, by 魏秀参

三维建模是一种常用技术,奈何心向往之,却没有时间去系统学习,也没有做出过一个作品。下面将我看到的一些好的有关三维建模的资料记录一下,以做备忘。

三维建模方法

选择软件

当前三维软件层出不穷,既有商业级的3ds MAX、MAYA、Creator,也有免费使用的Blender、MilkShape。

下载蓝图

飞行仿真的三维模型主要有飞行器、机场建筑等。和大多数虚拟现实中使用的模型一样,并不需要CAD级别的精度,一般是下载一些图片或飞机整体蓝图作为蓝本,采用多边形建模的方法分部建模。蓝图下载网址首推:

the‑blueprints.com

这里免费提供各种汽车、国内外飞机、船舶的三视图图纸下载。

多边形建模

采用下载的蓝图,作为贴图放置在相应的投影平面上,然后采用多边形建模的方法画出各部分细节。多边形建模的思想就是采用一个诸如一个最接近的多边形,进行点线面的操作就可以绘制出复杂的飞机外形。

多边形建模从最基本的外形开始变形,可以从点线面及整体四个级别的操作,在越需要突出外形细节处用越多的点,通过建立网格模型,然后使用网格平滑功能生成平滑的曲面模型。

建模时不能有重复的点线面等几何体,否则即使表面上已经贴好了材质,但载入程序后,往往会因为Z向深度冲突的原因产生闪烁效果。其细节取决于您的需求,需要花费大量的时间。

贴图

真实世界的模型带有各种彩色并具有光泽度。完成了几何体的建模对于一个模型来说只完成了一小半工作,还需要给模型添加材质。

与一般的模型不同,飞行器出于气动设计的考虑,其表面模型一般成流线型的曲面,且部件组成复杂,界线较难区分,即使是UVW贴图也难以胜任。因此,对飞行器的贴图主要采用3ds MAX的UVW展开贴图技术贴图。

一个模型最好只有一个贴图,因为贴图数目过多零散会影响计算机载入三维模型时间和效率。因此,一般将整个模型各个部件组合为一个多边形,各部件为元素,待完成UVW贴图后再分离成单独的部件。UVW展开贴图有多重形式,如长方体展开(即按六个视图投影)、平面展开等,按需选择。

在一张贴图上展开整个模型的UV坐标显然是有限的,需要根据现实精度调整各个部分的贴图大小。保存这些展开的贴图坐标并渲染输出UV模板图像文件。UV模板图像其实就是整个模型的贴图的界线图像,通过Photoshop等图像处理软件处理,采用尽可能真实的图像放置在对应的区域。最后将材质赋给对象即完成贴图渲染。

数据导出

直接采用3ds MAX绘制的三维模型当然是*.max格式,它不能为外界程序读取。这时就需要根据自己的开发环境选择导出的数据格式,比如用开源的OSG做仿真开发,就需要OSGExp插件的支持,导成*.ive、.osg、.osgb等格式即可。但无论导出何种格式,切记保证模型质心与建模的原点不能相差不远,还要注意光照、贴图格式以及单位问题。

参考链接

  1. 飞行仿真–1.三维建模篇,by WFYX.
  2. 调整边缘画笔工具怎么用?,by zhihu.

深度强化学习Deep Reinforcement Learning是将深度学习与强化学习结合起来,从而实现从Perception感知到Action动作的端对端学习End-to-End Learning的一种全新的算法。深度强化学习具备使机器人实现真正完全自主地学习一种甚至多种技能的潜力。

深度强化学习的起源

深度强化学习是深度学习与强化学习相结合的产物。

深度学习

深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

深度学习是机器学习中一种基于对数据进行表征学习的算法。观测值(例如一幅图像)可以使用多种方式来表示,如每个像素强度值的向量,或者更抽象地表示成一系列边、特定形状的区域等。而使用某些特定的表示方法更容易从实例中学习任务(例如,人脸识别或面部表情识别)。深度学习的好处是用非监督式或半监督式的特征学习和分层特征提取高效算法来替代手工获取特征。

表征学习的目标是寻求更好的表示方法并创建更好的模型来从大规模未标记数据中学习这些表示方法。表示方法来自神经科学,并松散地创建在类似神经系统中的信息处理和对通信模式的理解上,如神经编码,试图定义拉动神经元的反应之间的关系以及大脑中的神经元的电活动之间的关系。

至今已有数种深度学习框架,如深度神经网络、卷积神经网络和深度置信网络和递归神经网络已被应用在计算机视觉、语音识别、自然语言处理、音频识别与生物信息学等领域并获取了极好的效果。

另外,“深度学习”已成为类似术语,或者说是神经网络的品牌重塑。

深度神经网络

深度神经网络是一种具备至少一个隐层的神经网络。与浅层神经网络类似,深度神经网络也能够为复杂非线性系统提供建模,但多出的层次为模型提供了更高的抽象层次,因而提高了模型的能力。

深度神经网络(Deep Neural Networks, DNN)是一种判别模型,可以使用反向传播算法进行训练。权重更新可以使用下式进行随机梯度下降法求解:

$$\Delta w_{ij}(t+1)=\Delta w_{ij}(t) + \eta\frac{\partial C}{\partial w_{ij}}$$

其中,$\eta$为学习率,$C$为代价函数。这一函数的选择与学习的类型(例如监督学习、无监督学习、增强学习)以及激活函数相关。例如,为了在一个多分类问题上进行监督学习,通常的选择是使用ReLU作为激活函数,而使用交叉熵作为代价函数。Softmax函数定义为${\displaystyle p_{j}={\frac {\exp(x_{j})}{\sum {k}\exp(x{k})}}}$,其中 ${\displaystyle p_{j}}$代表类别 ${\displaystyle j}$的概率,而 ${\displaystyle x_{j}}$和 ${\displaystyle x_{k}}$分别代表对单元 ${\displaystyle j}$ 和 ${\displaystyle k}$的输入。交叉熵定义为 $C = -\sum_j d_j \log(p_j)$ ,其中 ${\displaystyle d_{j}}$代表输出单元${\displaystyle j}$的目标概率, ${\displaystyle p_{j}}$代表应用了激活函数后对单元 ${\displaystyle j}$的概率输出。

强化学习

在人工智能领域,一般用智能体Agent表示一个具备行为能力的物体,比如机器人,无人车等等。而强化学习则研究智能体Agent和环境Environment之间交互过程如何取得任务的成功。

强化学习与环境的交互过程如图1所示。在某个时间点,智能体Agent会获得观察值(Observation)和反馈值(Reward),然后根据这些选择下一步的动作(Action)。

强化学习示意图

图1 强化学习示意图

在整个过程中,任务的目标是获取尽可能多的Reward,这是任务的目标。而在每个时间片,Agent都是根据当前的观察来确定下一步的动作。观察Observation的集合就作为Agent的所处的状态State,因此,状态State和动作Action存在映射关系,也就是一个state可以对应一个action,或者对应不同动作的概率(常常用概率来表示,概率最高的就是最值得执行的动作)。状态与动作的关系其实就是输入与输出的关系,而状态State到动作Action的过程就称之为一个策略Policy,一般用 $\pi$ 表示,也就是需要找到以下关系:
$$a=\pi(s)$$
或者
$$\pi(a|s)$$

其中a是action,s是state。第一种是一一对应的表示,第二种是概率的表示。

1
强化学习的任务就是找到一个最优的策略Policy从而使Reward最多。

强化学习的训练一开始从采用随机策略进行试验开始,可获得一系列的状态,动作和反馈:
$${s_1,a_1,r_1,s_2,a_2,r_2,…,s_t,a_t,r_t}$$
根据这一系列样本,强化学习从中改进策略,使得任务反馈Reward越来越多。

强化学习分类

在Model-free 算法里面,最为常见的分类方案是根据模型的表示方式来对算法进行分类。在这种分类方法中,RL算法可以划分为3个类别:

  • Critic-only,在Critic-only 类算法中,模型只有值评估函数。
  • Actor-only,在Actor-only类算法中,模型是通过一个策略函数 $\pi(a_t|s_t)$ 来表示
  • Actor-critic, 在Actor-critic 算法中,模型通过同时学习值评估函数以及策略函数一起强化算法。

强化学习分类

图2 强化学习分类

第二种分类方式是根据模型的学习特性来区分的,一般可以分为:

  • On-policy
  • Off-policy

强化学习分类

图3 强化学习分类

强化学习模型假设

强化学习的研究建立在经典物理学基础上,基于以下两种假设:

  1. 时间是可以分割成一个一个时间片的,并且有完全的先后顺序。
  2. 上帝不掷筛子!如果输入是确定的,那么输出也一定是确定的。

强化学习与马尔科夫决策过程

MDP基于这样一种假设:未来只取决于当前。

一个状态$S_t$是Markov当且仅当
$$P(s_{t+1}|s_t)=P(s_{t+1}|s_t,s_{t-1},…s_1,s_0)$$

P为概率。简单的说就是下一个状态仅取决于当前的状态和当前的动作。注意这里的状态是完全可观察的全部的环境状态(也就是上帝视角)。

增强学习的问题都可以模型化为MDP的问题。

一个基本的MDP可以用(S,A,P)来表示,S表示状态,A表示动作,P表示状态转移概率,也就是根据当前的状态$s_t$和$a_t$转移到$s_{t+1}$的概率。如果我们知道了转移概率P,也就是称为我们获得了模型Model,有了模型,未来就可以求解,那么获取最优的动作也就有可能,这种通过模型来获取最优动作的方法也就称为Model-based的方法。但是现实情况下,很多问题是很难得到准确的模型的,因此就有Model-free的方法来寻找最优的动作。

强化学习的回报Result

既然一个状态对应一个动作,或者动作的概率,而有了动作,下一个状态也就确定了。这就意味着每个状态可以用一个确定的值来进行描述。可以由此判断一个状态是好的状态还是不好的状态。

状态的好坏其实等价于对未来回报的期望。因此,引入回报Return来表示某个时刻t的状态将具备的回报:

$$G_t = R_{t+1} + \lambda R_{t+2} + … = \sum_{k=0}^\infty\lambda^kR_{t+k+1}$$

上面R是Reward反馈,λ是discount factor折扣因子,一般小于1,就是说一般当下的反馈是比较重要的,时间越久,影响越小。

那么实际上除非整个过程结束,否则显然我们无法获取所有的reward来计算出每个状态的Return,因此,再引入一个概念价值函数Value Function,用value function $v(s)$来表示一个状态未来的潜在价值。

从定义上看,value function就是回报的期望:
$$v(s) = \mathbb E[G_t|S_t = s]
$$

引出价值函数,对于获取最优的策略Policy这个目标,我们就会有两种方法:

  • 直接优化策略$\pi(a|s)$或者$a = \pi(s)$使得回报更高
  • 通过估计value function来间接获得优化的策略。道理很简单,既然我知道每一种状态的优劣,那么我就知道我应该怎么选择了,而这种选择就是我们想要的策略。

把值函数展开:

$$\begin{aligned}
v(s) & = \mathbb E[G_t|S_t = s] \\
& = \mathbb E[R_{t+1}+\lambda R_{t+2} + \lambda ^2R_{t+3} + …|S_t = s] \\
& = \mathbb E[R_{t+1}+\lambda (R_{t+2} + \lambda R_{t+3} + …)|S_t = s] \\
& = \mathbb E[R_{t+1} + \lambda G_{t+1}|S_t = s] \\
& = \mathbb E[R_{t+1} + \lambda v(S_{t+1})|S_t = s]
\end{aligned}
$$

因此:

$$v(s) = \mathbb E[R_{t+1} + \lambda v(S_{t+1})|S_t = s]$$

上面这个公式就是Bellman方程的基本形态。从公式上看,当前状态的价值和下一步的价值以及当前的反馈Reward有关。它表明Value Function是可以通过迭代来进行计算的!!!

Action-Value function 动作价值函数

前面我们引出了价值函数,考虑到每个状态之后都有多种动作可以选择,每个动作之下的状态又多不一样,我们更关心在某个状态下的不同动作的价值。显然。如果知道了每个动作的价值,那么就可以选择价值最大的一个动作去执行了。这就是Action-Value function $Q^\pi(s,a)$。那么同样的道理,也是使用reward来表示,只是这里的reward和之前的reward不一样,这里是执行完动作action之后得到的reward,之前state对应的reward则是多种动作对应的reward的期望值。显然,动作之后的reward更容易理解。

动作价值函数就为如下表示:
$$\begin{aligned}
Q^\pi(s,a) & = \mathbb E[r_{t+1} + \lambda r_{t+2} + \lambda^2r_{t+3} + … |s,a] \\
& = \mathbb E_{s^\prime}[r+\lambda Q^\pi(s^\prime,a^\prime)|s,a]
\end{aligned}$$

这里要说明的是动作价值函数的定义,加了$\pi$,也就是说是在策略下的动作价值。因为对于每一个动作而已,都需要由策略根据当前的状态生成,因此必须有策略的支撑。而前面的价值函数则不一定依赖于策略。当然,如果定义$v^\pi(s)$则表示在策略$\pi$下的价值。

Optimal value function 最优价值函数

能计算动作价值函数是不够的,因为我们需要的是最优策略,现在求解最优策略等价于求解最优的value function,找到了最优的value function,自然而然策略也就是找到。(当然,这只是求解最优策略的一种方法,也就是value-based approach,由于DQN就是value-based,因此这里只讲这部分,以后我们会看到还有policy-based和model-based方法。一个就是直接计算策略函数,一个是估计模型,也就是计算出状态转移函数,从而整个MDP过程得解)

这里以动作价值函数来分析。

首先是最优动作价值函数和一般的动作价值函数的关系:

$$\begin{aligned}
Q^(s,a) &= \max_\pi Q^\pi(s,a)\\
& = \mathbb E_{s^\prime}[r+\lambda \max _{a^\prime}Q^
(s^\prime,a^\prime)|s,a]
\end{aligned}
$$

也就是最优的动作价值函数就是所有策略下的动作价值函数的最大值。通过这样的定义就可以使最优的动作价值的唯一性,从而可以求解整个MDP。

下面介绍基于Bellman方程的两个最基本的算法,策略迭代和值迭代。

策略迭代Policy Iteration

Policy Iteration的目的是通过迭代计算value function 价值函数的方式来使policy收敛到最优。

Policy Iteration本质上就是直接使用Bellman方程而得到的:

$$
\begin{aligned}
v_{k+1}(s) &\overset{.}{=} \mathbb E_{\pi}[R_{t+1}+\gamma v {k}(S{t+1})|S_t=s]\\
&=\sum_{a}{\pi(a|s)}\sum_{s^\prime,r}{p(s^\prime,r|s,a)[r+\gamma v_k(s^\prime)]}
\end{aligned}
$$

那么Policy Iteration一般分成两步:

  1. Policy Evaluation 策略评估。目的是更新Value Function,或者说更好的估计基于当前策略的价值
  2. Policy Improvement 策略改进。 使用 greedy policy 产生新的样本用于第一步的策略评估。

策略迭代算法示意图

图4 策略迭代算法示意图

本质上就是使用当前策略产生新的样本,然后使用新的样本更好的估计策略的价值,然后利用策略的价值更新策略,然后不断反复。理论可以证明最终策略将收敛到最优。

具体算法:

策略迭代算法

图5 策略迭代算法

那么这里要注意的是policy evaluation部分。这里的迭代很重要的一点是需要知道state状态转移概率p。也就是说依赖于model模型。而且按照算法要反复迭代直到收敛为止。所以一般需要做限制。比如到某一个比率或者次数就停止迭代。那么需要特别说明的是不管是策略迭代还是值迭代都是在理想化的情况下(上帝视角)推导出来的算法,本质上并不能直接应用,因为依赖Model。

Value Iteration 价值迭代

Value Iteration则是使用Bellman 最优方程得到:
$$
\begin{aligned}
v_{}(s) & = \max_{a} \mathbb E_{\pi}[R_{t+1}+\gamma v _{}(S_{t+1})|S_t=s,A_t=a]\\
& = \max_{a} \sum_{s^\prime,r}p(s^\prime,r|s,a)[r+\gamma v_*(s^\prime)]
\end{aligned}
$$

然后改变成迭代形式:

$$
\begin{aligned}
v_{k+1}(s) &\overset{.}{=} \max_{a} \mathbb E_{\pi}[R_{t+1}+\gamma v {k}(S{t+1})|S_t=s,A_t=a]\\
& = \max_{a} \sum_{s^\prime,r}p(s^\prime,r|s,a)[r+\gamma v_k(s^\prime)]
\end{aligned}
$$

value iteration的算法如下:

值迭代算法

图6 值迭代算法

Policy Iteration和Value Iteration有什么本质区别?

policy iteration使用bellman方程来更新value,最后收敛的value 即$v_\pi$是当前policy下的value值(所以叫做对policy进行评估),目的是为了后面的policy improvement得到新的policy。

而value iteration是使用bellman 最优方程来更新value,最后收敛得到的value即$v_*$就是当前state状态下的最优的value值。因此,只要最后收敛,那么最优的policy也就得到的。因此这个方法是基于更新value的,所以叫value iteration。

从上面的分析看,value iteration较之policy iteration更直接。不过问题也都是一样,需要知道状态转移函数p才能计算。本质上依赖于模型,而且理想条件下需要遍历所有的状态,这在稍微复杂一点的问题上就基本不可能了。

Q-Learning

Q Learning的思想完全根据value iteration得到。但要明确一点是value iteration每次都对所有的Q值更新一遍,也就是所有的状态和动作。但事实上在实际情况下我们没办法遍历所有的状态,还有所有的动作,我们只能得到有限的系列样本。因此,只能使用有限的样本进行操作。那么,怎么处理?Q Learning提出了一种更新Q值的办法:

$$Q(S_{t},A_{t}) \leftarrow Q(S_{t},A_{t})+\alpha({R_{t+1}+\lambda \max aQ(S{t+1},a)} - Q(S_t,A_t))$$

虽然根据value iteration计算出target Q值,但是这里并没有直接将这个Q值(是估计值)直接赋予新的Q,而是采用渐进的方式类似梯度下降,朝target迈近一小步,取决于α,这就能够减少估计误差造成的影响。类似随机梯度下降,最后可以收敛到最优的Q值。

具体的算法如下:

Q学习算法

图7 Q学习算法

Exploration and Exploitation 探索与利用

回到policy的问题,那么要选择怎样的policy来生成action呢?有两种做法:

  • 随机的生成一个动作

  • 根据当前的Q值计算出一个最优的动作,这个policy\pi称之为greedy policy贪婪策略。也就是

$$\pi(S_{t+1}) = arg\max aQ(S{t+1},a)$$

使用随机的动作就是exploration,也就是探索未知的动作会产生的效果,有利于更新Q值,获得更好的policy。而使用greedy policy也就是target policy则是exploitation,利用policy,这个相对来说就不好更新出更好的Q值,但可以得到更好的测试效果用于判断算法是否有效。

将两者结合起来就是所谓的$\epsilon-greedy$策略,$\epsilon$一般是一个很小的值,作为选取随机动作的概率值。可以更改$\epsilon$的值从而得到不同的exploration和exploitation的比例。

这里需要说明的一点是使用$\epsilon-greedy$策略是一种极其简单粗暴的方法,对于一些复杂的任务采用这种方法来探索未知空间是不可取的。因此,最近有越来越多的方法来改进这种探索机制。

深度强化学习

深度强化学习始于DeepMind在NIPS 2013上发表的Playing Atari with Deep Reinforcement Learning一文,在该文中第一次提出Deep Reinforcement Learning 这个名称,并且提出DQN(Deep Q-Network)算法,实现从纯图像输入完全通过学习来玩Atari游戏的成果。

之后DeepMind在Nature上发表了改进版的DQN文章Human-level Control through Deep Reinforcement Learning,引起了广泛的关注,Deep Reinfocement Learning 从此成为深度学习领域的前沿研究方向。

维度灾难

对简单问题可使用表格来表示Q(s,a),但是这个在现实的很多问题上是几乎不可行的,因为状态实在是太多。使用表格的方式根本存不下。

以计算机玩Atari游戏为例,计算机玩Atari游戏的要求是输入原始图像数据,也就是210x160像素的图片,然后输出几个按键动作。总之就是和人类的要求一样,纯视觉输入,然后让计算机自己玩游戏。那么这种情况下,到底有多少种状态呢?有可能每一秒钟的状态都不一样。因为,从理论上看,如果每一个像素都有256种选择,那么就有:
$$
256^{210\times 160}
$$

这简直是天文数字。所以,我们是不可能通过表格来存储状态的。我们有必要对状态的维度进行压缩,解决办法就是 价值函数近似Value Function Approximation

价值函数近似Value Function Approximation

什么是价值函数近似呢?说起来很简单,就是用一个函数来表示Q(s,a)。即
$$Q(s,a) = f(s,a)$$

$f$可以是任意类型的函数,比如线性函数:

$$Q(s,a) = w_1s + w_2a + b$$

其中$w_1$,$w_2$,$b$是函数$f$的参数。

通过函数表示,我们就可以无所谓s到底是多大的维度,反正最后都通过矩阵运算降维输出为单值的Q。

这就是价值函数近似的基本思路。

如果我们就用$w$来统一表示函数$f$的参数,那么就有

$$Q(s,a) = f(s,a,w)$$

为什么叫近似,因为我们并不知道Q值的实际分布情况,本质上就是用一个函数来近似Q值的分布,所以,也可以说是

$$Q(s,a)\approx f(s,a,w)$$

Q值神经网络化——DQN算法

意思很清楚,就是我们用一个深度神经网络来表示这个函数$f$。

以DQN为例,输入是经过处理的4个连续的84x84图像,然后经过两个卷积层,两个全连接层,最后输出包含每一个动作Q值的向量。

神经网络的训练是一个最优化问题,最优化一个损失函数loss function,也就是标签和网络输出的偏差,目标是让损失函数最小化。为此,我们需要有样本,巨量的有标签数据,然后通过反向传播使用梯度下降的方法来更新神经网络的参数。

如何为Q网络提供有标签的样本?答案就是利用Q-Learning算法。目标Q值作为标签,使Q值趋近于目标Q值。于是Q网络训练的损失函数就是:

DQN损失函数

图8 DQN损失函数

上面公式是$s^\prime$, $a^\prime$即下一个状态和动作。这里用了David Silver的表示方式,看起来比较清晰。
既然确定了损失函数,也就是cost,确定了获取样本的方式。那么DQN的整个算法也就成型了!

这里分析第一个版本的DQN,也就是NIPS 2013提出的DQN。

DQN算法

图9 DQN算法

具体的算法主要涉及到Experience Replay,也就是经验池的技巧,就是如何存储样本及采样问题。

由于玩Atari采集的样本是一个时间序列,样本之间具有连续性,如果每次得到样本就更新Q值,受样本分布影响,效果会不好。因此,一个很直接的想法就是把样本先存起来,然后随机采样如何?这就是Experience Replay的意思。按照脑科学的观点,人的大脑也具有这样的机制,就是在回忆中学习。

那么上面的算法看起来那么长,其实就是反复试验,然后存储数据。接下来数据存到一定程度,就每次随机采用数据,进行梯度下降!

DQN的三大改进方法

大幅度提升DQN玩Atari性能的主要就是Double DQN,Prioritised Replay还有Dueling Network三大方法。

  • Double DQN:目的是减少因为max Q值计算带来的计算偏差,或者称为过度估计(over estimation)问题,用当前的Q网络来选择动作,用目标Q网络来计算目标Q。
  • Prioritised replay:也就是优先经验的意思。优先级采用目标Q值与当前Q值的差值来表示。优先级高,那么采样的概率就高。
  • Dueling Network:将Q网络分成两个通道,一个输出V,一个输出A,最后再合起来得到Q。如下图所示(引用自Dueling Network论文)。这个方法主要是idea很简单但是很难想到,然后效果一级棒,因此也成为了ICML的best paper。

策略梯度

Policy Gradient的方法的基本思想是通过评价动作action的好坏,来调整该action的出现概率。最基本的Policy Gradient的损失函数Loss就是:

$$loss = -log(\pi)*Q$$

这里先以Q值来指代对动作的评价。

Robot Learning的发展路径

Robot Learning从目前来看,经过了以下研究思路的发展:

(1)利用传统的控制算法结合深度学习来实现机器人端到端的控制。这个方法主要是以Guided Policy Search(GPS)为首。这个方法是Sergey Levine提出的,通过与传统方法结合,确实可以让机器人学习出一些有意思的技能,但是有个根本问题摆在面前,就是传统方法通常需要知道整个系统的模型,而这在实际的机器人中非常难以适用。就比如四轴飞行器的控制,我们可以通过外部的Vicon设备来精确的定位四轴飞行器的位置,从而实现对其精确控制,但是在户外,我们根本就做不到这点,也就无法精确建模。因此,还依赖传统方法是没有出路的,我们使用深度学习就是要抛弃传统方法的弊端。

(2)深度增强学习DRL。由于DeepMind在DRL取得了巨大成功,而DRL就是面向决策与控制问题,特别适用于机器人,因此想在机器人上使用DRL是一种必然的想法。Google Brain团队(依然以Sergey Levine为首)做出了一些进展,在我们之前的专栏文章中也有分析最前沿 之 谷歌的协作机械臂 - 知乎专栏 。但是在使用DRL之后,DRL的弊端也就显现出来了,那就是需要大量的尝试来获取数据。对于这个问题,在机器人仿真环境还好,但是在真实的机器人上就根本没办法这么做了。为了解决这个问题,也就引出来下面两个研究思路。

(3)迁移学习Transfer Learning。既然在真实环境不行,而仿真环境可以,那么是不是可以先在仿真环境中训练好,再把知识迁移到真实机器人上。Google Deepmind在这一块做了一些不错的工作,提出了Progressive Neural Net和PathNet,验证了迁移的可能性。而且很显然的,仿真环境越真实,迁移效果会越好。那么,搞一个非常仿真的环境就非常有意义了。这不,Nvidia 刚刚推出Isaac机器人模拟系统,确实是对Robot Learning的研究注入了一剂强心剂。

(4)Imitation Learning 模仿学习/Few Shot Learning 少样本学习/ Meta Learning 学会学习。这是另一条思路,那就是尽量减少数据的使用量。我们如果能够教机器人几次机器人就能学会技能那么问题也能解决。而这一块也就是OpenAI (依然是Sergey Levine)那帮人在如火如荼的研究的方向。而且特别是Meta Learning,直指通用人工智能的核心。如果能够在Meta Learning上取得突破,那么本身会是革命性的。

因此,Robot Learning发展到这里,把研究的方向就聚焦到第三和第四点上了,并且也取得了一定的成果,但是显然还有非常多的工作可以去做。

参考链接

  1. 深度学习, by wikipedia.
  2. DQN 从入门到放弃1 DQN与增强学习, by Flood Sung.
  3. DQN 从入门到放弃2 增强学习与MDP, by Flood Sung.
  4. DQN 从入门到放弃3 价值函数与Bellman方程, by Flood Sung.
  5. DQN 从入门到放弃4 动态规划与Q-Learning,by Flood Sung.
  6. DQN从入门到放弃5 深度解读DQN算法, by Flood Sung.
  7. DQN从入门到放弃6 DQN的各种改进, by Flood Sung.
  8. DQN从入门到放弃7 连续控制DQN算法-NAF,by Flood Sung.
  9. 最前沿:机器人学习Robot Learning的发展,by Flood Sung.
  10. 简单的交叉熵损失函数,你真的懂了吗?,by 红色石头.
  11. Markov decision process,by wikipedia.
  12. openai spinningup,by LJ147.
  13. 深度强化学习发展史,by JQWang2048.
  14. 强化学习简介 (Introduction of Reinforcement Learning),by 范叶亮.
  15. 【写在复现之前】强化学习常用算法分类,by Firework.
  16. 强化学习中无处不在的贝尔曼最优性方程,背后的数学原理知多少?,by 亚希伯恩•菲.
  17. DQN从入门到放弃6 DQN的各种改进,by Flood Sung.
  18. Dueling DQN,by 小小何先生.
  19. 强化学习笔记(六)- 策略梯度,by Hazza Cheng.
  20. 目前,人工智能各个流派发展现状如何?,by zhihu.

wiringPi 库是由 Gordon Henderson 所编写并维护的一个用 C 语言写成的类库。起初,主要是作为 BCM2835 芯片的 GPIO 库。而现在,已经非常丰富,除了 GPIO 库,还包括了I2C 库、SPI 库、UART 库和软件 PWM 库等。

由于其与 Arduino 的“wiring”系统较为类似,故以此命名。它是采用 GNU LGPLv3许可证的,可以在 C 或 C++上使用,而且在其他编程语言上也有对应的扩展。

wiringPi 库包含了一个命令行工具 gpio,它可以用来设置 GPIO 管脚,可以用来读写GPIO 管脚,甚至可以在 Shell 脚本中使用来达到控制 GPIO 管脚的目的。

下载、编译和测试wiringPi

  1. 下载并编译wiringPi
1
2
3
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build
  1. 测试wiringPi是否安装成功
    打开命令终端,可以通过 gpio 命令来检查 wiringPi 是否安装成功,运行下面的命令:
1
2
gpio –v
gpio readall

运行上述第二条命令将得到树莓派的 GPIO 接口之间的对应关系。

gpio管脚定义

使用wiringPi

在使用wiringPi之前,应首先对wiringPi进行设置。

wiringPi设置函数

wiringPi设置函数如下:

1
2
3
4
int wiringPiSetup (void) ;
int wiringPiSetupGpio (void) ;
int wiringPiSetupPhys (void) ;
int wiringPiSetupSys (void) ;

wiringPiSetup (void) 函数

该函数初始化wiringPi,假定程序将使用wiringPi的管脚定义图。具体管脚映射,可以通过gpio readall命令来查看。该函数需要root权限。

wiringPiSetupGpio (void) 函数

该函数与wiringPiSetup函数类似,区别在于假定程序使用的是Broadcom的GPIO管脚定义,而没有重新映射。该函数需要root权限,需要注意v1和v2版本的树莓派是不同的。

wiringPiSetupPhys (void) 函数

该函数与wiringPiSetup函数类似,区别在于允许程序使用物理管脚定义,但仅支持P1接口。该函数需要root权限。

wiringPiSetupSys (void) 函数

该函数初始化wiringPi,使用/sys/class/gpio接口,而不是直接通过操作硬件来实现。该函数可以使用非root权限用户,在此种模式下的管脚号是Broadcom的GPIO管脚号,不wiringPiSetupGpio函数类似,需要注意v1和v2板子的不同。

在此种模式下,在运行程序前,您需要通过/sys/class/gpio接口导出要使用的管脚。你可以在一个独立的shell脚本中来导出将要使用的管脚,或者使用系统的system()函数来调用GPIO命令。

软件 PWM 库

wiringPi 中包含了一个软件驱动的 PWM (Pulse Width Modulation, 脉冲宽度调节)处理库,可以在任意的树莓派 GPIO 上输出 PWM 信号。

但是也有一些限制。为了维护较低的 CPU 使用率,最小的脉冲宽度是 100 微秒,结合默认的建议值为 100,那么最小的 PWM 频率是 100Hz。如果需要更高的频率,可以使用更低的数值。如果看脉冲宽度的驱动代码,你会发现低于 100 微秒,wiringPi 是在软件循环中实现的,这就意味着 CPU 使用率将会动态增加,从而使得控制其他管脚成为不可能。

需要注意的是,当其他程序运行在更高的实时的优先级,Linux 可能会影响产生信号的精度。尽管有这些限制,控制 LED 或电机还是可以的。

使用前,需要包含相应的文件:

1
2
#include <wiringPi.h>
#include <softPwm.h>

当编译程序时,必须加上 pthread 库,如下:

1
gcc –o myprog myprog.c –lwiringPi –lpthread

必须使用 wiringPiSetup()、 wiringPiSetupGpio()或者 wiringPiSetupPhys()函数来初始化 wiringPi。

wiringPiSetupSys()是不够快的,因此,必须使用 sudo 命令来运行程序。

softPwmCreate 函数

该函数的原型为:int softPwmCreate(int pin, int initialValue, int pwmRange);

该函数将会创建一个软件控制的 PWM 管脚。可以使用任何一个 GPIO 管脚 , pwmRange 参数可以为 0(关)~100(全开)。

返回值为 0,代表成功,其他值,代表失败。

softPwmWrite 函数

该函数的原型为: void softPwmWrite(int pin, int value);

该函数将会更新指定管脚的 PWM 值。value 参数的范围将会被检查,如果指定的管脚之前没有通过 softPwmCreate 初始化,将会被忽略。

wiringPi核心函数

pinMode函数

该函数的原型为: void pinMode(int pin, int mode);

使用该函数可以将某个管脚讴置为 INPUT(输入)、 OUTPUT(输出)、 PWM_OUTPUT(脉冲输出)或者 GPIO_CLOCK(GPIO 时钟)。

需要注意的是仅有管脚 1(BCM_GPIO 18)支持 PWM_OUTPUT 模式,仅有管脚 7(BCM_GPIO 4)支持 CLOCK 输出模式。

在 Sys 模式下,返个函数没有影响。你可以通过调用 gpio 命令在 shell 脚本中来设置管脚的模式。

digitalWrite 函数

该函数的原型为: void digitalWrite(int pin, int value);

使用该函数可以向指定的管脚写入 HIGH(高)或者 LOW(低),写入前,需要将管脚设置为输出模式。

wiringPi 将任何的非 0 值作为 HIGH (高)来对待,因此, 0 是唯一能够代表 LOW (低)的数值。

digitalRead 函数

该函数原型: digitalRead(int pin);

使用该函数可以读取指定管脚的值,读取到的值为HIGH(1)或者LOW(0),该值取决于该管脚的逻辑电平的高低。

时间函数

delay 函数

该函数的原型为:void delay(unsigned int howLong);

该函数将会中断程序执行至少 howLong 毫秒。因为 Linux 是多任务的原因,中断时间可能会更长。需要注意的是,最长的延迟值是一个无符号 32 位整数,其大约为 49 天。

delayMicroseconds 函数

该函数的原型为:void delayMicroseconds(unsigned int howLong);

该函数将会中断程序执行至少 howLong 微秒。因为 Linux 是一个多任务的系统,因此中断时间可能会更长。需要注意的是,最长的延迟值是一个无符号 32 位整数,其大约为 71分钟。

延迟低于100 微秒,将会使用硬件循环来实现;超过 100 微秒,将会使用系统的nanosleep()函数来实现。

优先级/时间/线程

wiringPiISR 函数

该函数的原型为:int wiringPiISR(int pin, int edgeType, void (*function)(void));

该函数会在指定管脚注册一个中断事件的函数,当指定管脚发生中断事件时,会自动调用该函数。

edgeType 参数可以为 INT_EDGE_FALLING(下降沿)、INT_EDGE_RISING(上升沿)、INT_EDGE_BOTH(上升沿或者下降沿)或者 INT_EDGE_SETUP。如果是INT_EDGE_SETUP,将不会初始化该管脚,因为它假定已经在别处设置过该管脚(比如使用 gpio 命令),但是,如果指定另外的类型,指定管脚将会被导出并初始化。完成此操作使用的是 gpio 命令,所以,必须保证 gpio 命令是可用的。

注册函数在中断触发时,将会被调用。在调用注册函数前,中断事件将会从分配器中清除,所以,即使有后续的触发发生,在处理完成前,也不会错过此次触发。(当然,如果在正在处理触发时,有不止一个的中断发生,已经发生的中断将会被忽略)。

I2C库

wiringPi 包含了一个 I2C 库,来让您能够更轻松的使用树莓派的板上 I2C 接口。在使用 I2C 接口之前,您可能需要使用 gpio 命令来加载 I2C 驱劢到内核中:

1
gpio load i2c

如果你需要的波特率是 100Kbps,那么您可以使用如下命令设置波特率为1000Kbps:

1
gpio load i2c 1000

使用 I2C 库,需要包含 wiringPiI2C.h 文件。并且编译时,同样需要使用-lwiringPi 来连接到 wiringPi 库。

您仍然可以使用标准的系统命令来检测 I2C 设备,如 i2cdetect 命令,需要注意的是,在 v1 版本的树莓派上是 0,v2 版本上是 1,如下:

1
2
3
ls /dev/i2c-1
# 注意i2c-1后面的编码1
i2cdetect –y 1

当然,您也可以使用 gpio 命令来调用 i2cdetect 命令,从而检测 I2C 讴备,返样就不用在乎您的树莓派版本了,如下:

1
gpio i2cdetect

wiringPiI2CSetup 函数

该函数的原型为:int wiringPiI2CSetup(int devId);

该函数使用指定设备标示号来初始化 I2C 系统。参数 devId 是 I2C 设备的地址,可以通过 i2cdetect 命令可以查到该地址。该函数会获取树莓派的版本并依据此打开/dev 目录下对应的讴备。

返回值是标准的 Linux 文件句柄,如果有错误,则返回-1。

比如,流行的 MCP23017 GPIO 扩展器的设备 ID 是 0x20,所以,你需要将这个数值传递给 wiringPiI2CSetup()。

wiringPiI2CWrite 函数

该函数的原型为:int wiringPiI2CWrite(int fd, int data);

简单的设备写操作。一些设备可以接受数据,而不需要发送任何内部寄存器地址。

wiringPiI2CRead 函数

该函数的原型为:int wiringPiI2CRead(int fd);

简单的设备读操作。一些设备可以直接读取,而不需要发送任何寄存器地址。

wiringPiI2CWriteReg8 和 wiringPiI2CWriteReg16 函数

该函数的原型为:

int wiringPiI2CWriteReg8(int fd, int reg, int data); int wiringPiI2CWriteReg16(int fd, int reg, int data);

使用返两个函数,可以写一个 8 位或 16 位数值到指定的设备寄存器。

wiringPiI2CReadReg8 和 wiringPiI2CReadReg16 函数

该函数的原型为:

int wiringPiI2CReadReg8(int fd, int reg); int wiringPiI2CReadReg16(int fd, int reg);

使用返两个函数,可以从指定的设备寄存器读取一个 8 位或 16 位的数值。

参考链接

  1. 树莓派 wiringPi 用户手册 , by 科技爱好者博客
  2. Wiring Pi
  3. Pthreads 入门教程,by hanbingyan.

人工智能的研究历史有着一条从以“推理”为重点,到以“知识”为重点,再到以“学习”为重点的自然、清晰的脉络。机器学习则是实现人工智能的一个途径,即以机器学习为手段解决人工智能中的问题。

问题的提出

问题的分析

解决方案

总结

参考文献

  1. 机器学习, by wikipedia.

人工神经网络(英语:Artificial Neural Network,ANN),简称神经网络(Neural Network,NN)或类神经网络,在机器学习和认知科学领域,是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。

神经网络由大量的人工神经元联结进行计算。大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自适应系统,通俗的讲就是具备学习功能。现代神经网络是一种非线性统计性数据建模工具。

神经元

神经元示意图:

神经元示意图

图1 神经元示意图
  • a1~an为输入向量的各个分量
  • w1~wn为神经元各个突触的权值
  • b为偏置
  • f为传递函数,通常为非线性函数。一般有Sigmoid(), ReLU(), Softmax()。
  • t为神经元输出

神经元的数学表示是:$t=f(\vec{W^{‘}}\vec{A}+b)$

  • $\vec{W}$为权向量,$\vec{W^{‘}}$为$\vec{W}$的转置
  • $\vec{A}$为输入向量
  • $b$为偏置
  • $f$为传递函数

可见,一个神经元的功能是求得输入向量与权向量的内积后,经一个非线性传递函数得到一个标量结果。

激活函数

  • Sigmoid函数

Sigmoid函数也称S型激活函数,其将加权和转换为介于 0 和 1 之间的值。
$$F(x)=\frac{1} {1+e^{-x}}$$

曲线图如下:

S 型激活函数

图2 S 型激活函数
  • ReLU函数

相较于 S 型函数等平滑函数,以下修正线性单元激活函数(简称为 ReLU)的效果通常要好一点,同时还非常易于计算。

$$F(x)=max(0,x)$$

ReLU 的优势在于它基于实证发现(可能由 ReLU 驱动),拥有更实用的响应范围。S 型函数的响应性在两端相对较快地减少。

ReLU 激活函数

图3 ReLU 激活函数
  • Softmax函数

Softmax函数用于多类别神经网络。在多类别问题中,Softmax 会为每个类别分配一个用小数表示的概率。这些用小数表示的概率相加之和必须是 1.0。

Softmax 方程式如下所示:
$$p(y = j|\textbf{x}) = \frac{e^{(\textbf{w}j^{T}\textbf{x} + b_j)}}{\sum{k\in K} {e^{(\textbf{w}_k^{T}\textbf{x} + b_k)}} }$$

请注意,此公式本质上是将逻辑回归公式延伸到了多类别。

Softmax 层是紧挨着输出层之前的神经网络层。Softmax 层必须和输出层拥有一样的节点数。

神经网络中的 Softmax 层

图4 神经网络中的 Softmax 层

神经元网络

神经元网络可分为单层神经元网络和多层神经元网络。而常用的是多层神经元网络。

多层神经元网络

一种常见的多层结构的前馈网络(Multilayer Feedforward Network)由三部分组成,如图2所示:

包含激活函数的三层模型的图表

图5 包含激活函数的三层模型的图表
  • 输入层(Input layer),众多神经元(Neuron)接受大量非线形输入消息。输入的消息称为输入向量。
  • 输出层(Output layer),消息在神经元链接中传输、分析、权衡,形成输出结果。输出的消息称为输出向量。
  • 隐藏层(Hidden layer),简称“隐层”,是输入层和输出层之间众多神经元和链接组成的各个层面。隐层可以有一层或多层。隐层的节点(神经元)数目不定,但数目越多神经网络的非线性越显著,从而神经网络的强健性(robustness)(控制系统在一定结构、大小等的参数摄动下,维持某些性能的特性)更显著。习惯上会选输入节点1.2至1.5倍的节点。

这种网络一般称为感知器(对单隐藏层)或多层感知器(对多隐藏层),神经网络的类型已经演变出很多种,这种分层的结构也并不是对所有的神经网络都适用。

训练神经网络

通常使用反向传播算法训练神经网络[3],但一些常见情况都会导致反向传播算法出错。

  • 梯度消失

较低层(更接近输入)的梯度可能会变得非常小。在深度网络中,计算这些梯度时,可能涉及许多小项的乘积。

当较低层的梯度逐渐消失到 0 时,这些层的训练速度会非常缓慢,甚至不再训练。

ReLU 激活函数有助于防止梯度消失。

  • 梯度爆炸

如果网络中的权重过大,则较低层的梯度会涉及许多大项的乘积。在这种情况下,梯度就会爆炸:梯度过大导致难以收敛。

批标准化可以降低学习速率,因而有助于防止梯度爆炸。

  • ReLU 单元消失

一旦 ReLU 单元的加权和低于 0,ReLU 单元就可能会停滞。它会输出对网络输出没有任何贡献的 0 激活,而梯度在反向传播算法期间将无法再从中流过。由于梯度的来源被切断,ReLU 的输入可能无法作出足够的改变来使加权和恢复到 0 以上。

降低学习速率有助于防止 ReLU 单元消失。

  • 丢弃正则化

这是称为丢弃的另一种形式的正则化,可用于神经网络。其工作原理是,在梯度下降法的每一步中随机丢弃一些网络单元。丢弃得越多,正则化效果就越强。

卷积神经网络

卷积神经⽹络(convolutional neural network)是含有卷积层(convolutional layer)的神经⽹
络。下面将按时间顺序介绍各类经典的卷积神经网络。

卷积神经⽹络(LeNet)

LeNet是⼀个早期⽤来识别⼿写数字图像的卷积神经⽹络,于80 年代末期提出。这个名字来源于LeNet论⽂的第⼀作者Yann LeCun。LeNet展⽰了通过梯度下降训练卷积神经⽹络可以达到⼿写数字识别在当时最先进的结果。这个奠基性的⼯作第⼀次将卷积神经⽹络推上舞台,为世⼈所知。

LeNet结构

LeNet一共有7层(不包括输入层),可分为卷积层块和全连接层块两个部分,如图6所示。

LeNet结构

图6 LeNet结构
  • 输入层:输入图像的大小为32*32,这要比mnist数据库中的最大字母(28*28)还大。作用: 图像较大,这样做的目的是希望潜在的明显特征,比如笔画断续,角点等能够出现在最高层特征监测子感受野的中心。
  • 卷积层:C1,C3,C5为卷积层,S2,S4为降采样层。
  • 全连接层:F6为全连接层,还有一个输出层。

深度卷积神经⽹络(AlexNet)

2012年,AlexNet横空出世。这个模型的名字来源于论⽂第⼀作者的姓名Alex Krizhevsky。AlexNet使⽤了8层卷积神经⽹络,并以很⼤的优势赢得了ImageNet 2012图像识别挑战赛。它⾸次证明了学习到的特征可以超越⼿⼯设计的特征,从而⼀举打破计算机视觉研究的前状。

AlexNet与LeNet区别

AlexNet与LeNet的设计理念⾮常相似,但也有显著的区别。

第⼀,与相对较小的LeNet相⽐,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。

第⼆,AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数。

第三,AlexNet通过丢弃法来控制全连接层的模型复杂度。

第四,AlexNet引⼊了⼤量的图像增⼴,如翻转、裁剪和颜⾊变化,从而进⼀步扩⼤数据集来缓解过拟合。

AlexNet结构

AlexNet的一些参数和结构图:

  • 卷积层:5层
  • 全连接层:3层
  • 深度:8层
  • 参数个数:60M
  • 神经元个数:650k
  • 分类数目:1000类

AlexNet结构

图7 AlexNet结构

由于当时的显卡容量问题,AlexNet 的60M个参数无法全部放在一张显卡上操作,所以采用了两张显卡分开操作的形式,其中在C3,R1,R2,R3层上出现交互,所谓的交互就是通道的合并,是一种串接操作。

使⽤重复元素的⽹络(VGG网络)

VGG的名字来源于论⽂作者所在的实验室Visual Geometry Group。2014年VGG提出了可以通过重复使⽤简单的基础块来构建深度模型的思路。

VGG块的组成规律是:连续使⽤数个相同的填充为1、窗口形状为3*3的卷积层后接上⼀个步幅为2、窗口形状为2*2的最⼤池化层。卷积层保持输⼊的⾼和宽不变,而池化层则对其减半。

VGG相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)。对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。

与AlexNet和LeNet⼀样,VGG⽹络由卷积层模块后接全连接层模块构成。卷积层模块串联数个vgg_block,其超参数由变量conv_arch定义。该变量指定了每个VGG块⾥卷积层个数和输出通道数。全连接模块则跟AlexNet中的⼀样。

构造⼀个最简单的VGG⽹络VGG-11。它有5个卷积块,前2块使⽤单卷积层,而后3块使⽤双卷积层。第⼀块的输出通道是64,之后每次对输出通道数翻倍,直到变为512。

VGG结构

图8 VGG结构

⽹络中的⽹络(NiN)

在AlexNet问世不久,⽹络中的⽹络(NiN)提出即串联多个由卷积层和“全连接”层构成的小⽹络来构建⼀个深层⽹络。

卷积层的输⼊和输出通常是四维数组(样本,通道,⾼,宽),而全连接层的输⼊和输出则通常是⼆维数组(样本,特征)。如果想在全连接层后再接上卷积层,则需要将全连接层的输出变换为四维。1*1卷积层可以看成全连接层中空间维度(⾼和宽)上的每个元素相当于样本,通道相当于特征。因此, NiN使⽤1*1卷积层来替代全连接层,从而使空间信息能够⾃然传递到后⾯的层中去。

NiN结构(右边)与AlexNet、VGG(左边)的区别:

NiN与VGG区别

图9 NiN与VGG区别

NiN块是NiN中的基础块。它由⼀个卷积层加两个充当全连接层的1 * 1卷积层串联而成。其中第⼀个卷积层的超参数可以⾃⾏设置,而第⼆和第三个卷积层的超参数⼀般是固定的。

NiN重复使⽤由卷积层和代替全连接层的1 * 1卷积层构成的NiN块来构建深层⽹络。NiN去除了容易造成过拟合的全连接输出层,而是将其替换成输出通道数等于标签类别数的NiN块和全局平均池化层。

含并⾏连结的⽹络(GoogLeNet)

在2014年的ImageNet图像识别挑战赛中,⼀个名叫GoogLeNet的⽹络结构⼤放异彩。它虽然在名字上向LeNet致敬,但在⽹络结构上已经很难看到LeNet的影⼦。GoogLeNet吸收了NiN中⽹络串联⽹络的思想,并在此基础上做了很⼤改进。

GoogLeNet中的基础卷积块叫作Inception块,得名于同名电影《盗梦空间》(Inception)。与NiN块相⽐,这个基础块在结构上更加复杂,如图所⽰。

Inception块结构

图10 Inception块结构

Inception块⾥有4条并⾏的线路。前3条线路使⽤窗口⼤小分别是1 * 1、3 * 3和5 * 5的卷积层来抽取不同空间尺⼨下的信息,其中中间2个线路会对输⼊先做1 * 1卷积来减少输⼊通道数,以降低模型复杂度。第四条线路则使⽤3*3最⼤池化层,后接1*1卷积层来改变通道数。4条线路都使⽤了合适的填充来使输⼊与输出的⾼和宽⼀致。最后我们将每条线路的输出在通道维上连结,并输⼊接下来的层中去。

Inception块中可以⾃定义的超参数是每个层的输出通道数,以此来控制模型复杂度。

GoogLeNet跟VGG⼀样,在主体卷积部分中使⽤5个模块(block),每个模块之间使⽤步幅为2的3*3最⼤池化层来减小输出⾼宽。

残差网络(ResNet)

让我们先思考一个问题:对神经网络模型添加新的层,充分训练后的模型是否只可能更有效地降低训练误差?理论上,原模型解的空间只是新模型解的空间的子空间。也就是说,如果我们能将新添加的层训练成恒等映射 f(x)=x ,新模型和原模型将同样有效。由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。然而在实践中,添加过多的层后训练误差往往不降反升。即使利用批量归一化带来的数值稳定性使训练深层模型更加容易,该问题仍然存在。针对这一问题,何恺明等人提出了残差网络(ResNet)。它在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经网络的设计。

让我们聚焦于神经网络局部。如图11所示,设输入为 x 。假设我们希望学出的理想映射为 f(x) ,从而作为图11上方激活函数的输入。左图虚线框中的部分需要直接拟合出该映射 f(x) ,而右图虚线框中的部分则需要拟合出有关恒等映射的残差映射 f(x)−x 。残差映射在实际中往往更容易优化。以本节开头提到的恒等映射作为我们希望学出的理想映射 f(x) 。我们只需将图11中右图虚线框内上方的加权运算(如仿射)的权重和偏差参数学成0,那么 f(x) 即为恒等映射。实际中,当理想映射 f(x) 极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。图11右图也是ResNet的基础块,即残差块(residual block)。在残差块中,输入可通过跨层的数据线路更快地向前传播。

ResNet残差块结构

图11 ResNet残差块结构

ResNet沿用了VGG全 3×3 卷积层的设计。残差块里首先有2个有相同输出通道数的 3×3 卷积层。每个卷积层后接一个批量归一化层和ReLU激活函数。然后我们将输入跳过这两个卷积运算后直接加在最后的ReLU激活函数前。这样的设计要求两个卷积层的输出与输入形状一样,从而可以相加。如果想改变通道数,就需要引入一个额外的 1×1 卷积层来将输入变换成需要的形状后再做相加运算。

稠密连接网络(DenseNet)

稠密连接网络(DenseNet)与ResNet的主要区别如图12所示。

ResNet(左)与DenseNet(右)在跨层连接上的主要区别:使用相加和使用连结

图12 ResNet(左)与DenseNet(右)在跨层连接上的主要区别:使用相加和使用连结

图12中将部分前后相邻的运算抽象为模块A和模块B。与ResNet的主要区别在于,DenseNet里模块B的输出不是像ResNet那样和模块A的输出相加,而是在通道维上连结。这样模块A的输出可以直接传入模块B后面的层。在这个设计里,模块A直接跟模块B后面的所有层连接在了一起。这也是它被称为“稠密连接”的原因。

DenseNet的主要构建模块是稠密块(dense block)和过渡层(transition layer)。前者定义了输入和输出是如何连结的,后者则用来控制通道数,使之不过大。

MobileNets:同样的卷积层,更少的参数

MobileNet,正如其名,这是一个非常简单快速并且准确率也不错的CNN网络结构,它大大减少了网络层的参数数量,使得网络的前向传播和后向传播的运算量大幅减少,最终成为了一个效率极高的CNN网络。

ShuffleNets:Group convolution+Channel Shuffle

ShuffleNet是Face++提出的一种轻量化网络结构,主要思路是使用Group convolution和Channel shuffle改进ResNet,可以看作是ResNet的压缩版本。

循环神经网络

循环神经网络是为更好地处理时序信息而设计的。它引入状态变量来存储过去的信息,并用其与当前的输入共同决定当前的输出。

循环神经网络常用于处理序列数据,如一段文字或声音、购物或观影的顺序,甚至是图像中的一行或一列像素。因此,循环神经网络有着极为广泛的实际应用,如语言模型、文本分类、机器翻译、语音识别、图像分析、手写识别和推荐系统。

现在我们考虑输入数据存在时间相关性的情况。假设 $X_t∈R^{n×d}$ 是序列中时间步 $t$ 的小批量输入,$H_t∈R^{n×h}$ 是该时间步的隐藏变量。与多层感知机不同的是,这里我们保存上一时间步的隐藏变量 $H_{t−1}$ ,并引入一个新的权重参数 $W_{hh}∈R^{h×h}$ ,该参数用来描述在当前时间步如何使用上一时间步的隐藏变量。具体来说,时间步 $t$ 的隐藏变量的计算由当前时间步的输入和上一时间步的隐藏变量共同决定:

$$H_t=ϕ(X_tW_{xh}+H_{t−1}W_{hh}+b_h)$$

与多层感知机相比,我们在这里添加了 $H_{t−1}W_{hh}$一项。由上式中相邻时间步的隐藏变量 $H_t$ 和 $H_{t−1}$ 之间的关系可知,这里的隐藏变量能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。因此,该隐藏变量也称为隐藏状态。由于隐藏状态在当前时间步的定义使用了上一时间步的隐藏状态,上式的计算是循环的。使用循环计算的网络即循环神经网络(recurrent neural network)。

循环神经网络有很多种不同的构造方法。含上式所定义的隐藏状态的循环神经网络是极为常见的一种。若无特别说明,本章中的循环神经网络均基于上式中隐藏状态的循环计算。在时间步 $t$ ,输出层的输出和多层感知机中的计算类似:

$$O_t=H_tW_{hq}+b_q$$

循环神经网络的参数包括隐藏层的权重 $W_{xh}∈R^{d×h}$ 、 $W_{hh}∈R^{h×h}$ 和偏差 $b_h∈R^{1×h}$ ,以及输出层的权重 $W_{hq}∈R^{h×q}$ 和偏差 $b_q∈R^{1×q}$ 。值得一提的是,即便在不同时间步,循环神经网络也始终使用这些模型参数。因此,循环神经网络模型参数的数量不随时间步的增加而增长。

图13展示了循环神经网络在3个相邻时间步的计算逻辑。在时间步 $t$ ,隐藏状态的计算可以看成是将输入 $X_t$ 和前一时间步隐藏状态 $H_{t−1}$ 连结后输入一个激活函数为 $ϕ$ 的全连接层。该全连接层的输出就是当前时间步的隐藏状态 $H_t$ ,且模型参数为 $W_{xh}$ 与 $W_{hh}$ 的连结,偏差为 $b_h$ 。当前时间步 $t$ 的隐藏状态 $H_t$ 将参与下一个时间步 $t+1$ 的隐藏状态 $H_{t+1}$ 的计算,并输入到当前时间步的全连接输出层。

含隐藏状态的循环神经网络

图13 含隐藏状态的循环神经网络

门控循环单元(GRU)

当时间步数较大或者时间步较小时,循环神经网络的梯度较容易出现衰减或爆炸。虽然裁剪梯度可以应对梯度爆炸,但无法解决梯度衰减的问题。通常由于这个原因,循环神经网络在实际中较难捕捉时间序列中时间步距离较大的依赖关系。

门控循环神经网络(gated recurrent neural network)的提出,正是为了更好地捕捉时间序列中时间步距离较大的依赖关系。它通过可以学习的门来控制信息的流动。其中,门控循环单元(gated recurrent unit,GRU)是一种常用的门控循环神经网络。

门控循环单元引入了重置门(reset gate)和更新门(update gate)的概念,从而修改了循环神经网络中隐藏状态的计算方式。门控循环单元中的重置门和更新门的输入均为当前时间步输入 $X_t$ 与上一时间步隐藏状态 $H_{t−1}$ ,输出由激活函数为sigmoid函数的全连接层计算得到。

门控循环单元中候选隐藏状态的计算

图14 门控循环单元中候选隐藏状态的计算

具体来说,时间步 $t$ 的候选隐藏状态 $\tilde{\boldsymbol{H}}_t∈R^{n×h}$ 的计算为

$$\tilde{\boldsymbol{H}}t=tanh(X_tW{xh}+(R_t⊙H_{t−1})W_{hh}+b_h)$$

其中 $W_{xh}∈R^{d×h}$ 和 $W_{hh}∈R^{h×h}$ 是权重参数, $b_h∈R^{1×h}$ 是偏差参数。从上面这个公式可以看出,重置门控制了上一时间步的隐藏状态如何流入当前时间步的候选隐藏状态。而上一时间步的隐藏状态可能包含了时间序列截至上一时间步的全部历史信息。因此,重置门可以用来丢弃与预测无关的历史信息。

最后,时间步 $t$ 的隐藏状态 $H_t∈R^{n×h}$ 的计算使用当前时间步的更新门 $Z_t$ 来对上一时间步的隐藏状态 $H_{t−1}$ 和当前时间步的候选隐藏状态 $\tilde{\boldsymbol{H}}_t$ 做组合:

$$Ht=Zt⊙Ht−1+(1−Zt)⊙\tilde{\boldsymbol{H}}_t$$

门控循环单元中隐藏状态的计算

图15 门控循环单元中隐藏状态的计算

值得注意的是,更新门可以控制隐藏状态应该如何被包含当前时间步信息的候选隐藏状态所更新。

我们对门控循环单元的设计稍作总结:

  • 重置门有助于捕捉时间序列里短期的依赖关系;
  • 更新门有助于捕捉时间序列里长期的依赖关系。

长短期记忆(LSTM)

LSTM 中引入了3个门,即输入门(input gate)、遗忘门(forget gate)和输出门(output gate),以及与隐藏状态形状相同的记忆细胞(某些文献把记忆细胞当成一种特殊的隐藏状态),从而记录额外的信息。

输入门、遗忘门和输出门

与门控循环单元中的重置门和更新门一样,如图16所示,长短期记忆的门的输入均为当前时间步输入 $X_t$ 与上一时间步隐藏状态 $H_{t−1}$ ,输出由激活函数为sigmoid函数的全连接层计算得到。如此一来,这3个门元素的值域均为 [0,1] 。

长短期记忆中输入门、遗忘门和输出门的计算

图16 长短期记忆中输入门、遗忘门和输出门的计算

具体来说,假设隐藏单元个数为$h$,给定时间步$t$的小批量输入$\boldsymbol{X}t \in \mathbb{R}^{n \times d}$(样本数为$n$,输入个数为$d$)和上一时间步隐藏状态$\boldsymbol{H}_{t-1} \in \mathbb{R}^{n \times h}$。 时间步$t$的输入门$\boldsymbol{I}_t \in \mathbb{R}^{n \times h}$、遗忘门$\boldsymbol{F}_t \in \mathbb{R}^{n \times h}$和输出门$\boldsymbol{O}_t \in \mathbb{R}^{n \times h}$分别计算如下:

$$ \begin{aligned} \boldsymbol{I}t &= \sigma(\boldsymbol{X}t \boldsymbol{W}{xi} + \boldsymbol{H}{t-1} \boldsymbol{W}_{hi} + \boldsymbol{b}_i)\end{aligned}$$

$$\begin{aligned} \boldsymbol{F}t &= \sigma(\boldsymbol{X}t \boldsymbol{W}{xf} + \boldsymbol{H}{t-1} \boldsymbol{W}_{hf} + \boldsymbol{b}_f)\end{aligned}$$

$$\begin{aligned} \boldsymbol{O}t &= \sigma(\boldsymbol{X}t \boldsymbol{W}{xo} + \boldsymbol{H}{t-1} \boldsymbol{W}_{ho} + \boldsymbol{b}_o)\end{aligned} $$

其中的$\boldsymbol{W}{xi}, \boldsymbol{W}{xf}, \boldsymbol{W}{xo} \in \mathbb{R}^{d \times h}$和$\boldsymbol{W}{hi}, \boldsymbol{W}{hf}, \boldsymbol{W}{ho} \in \mathbb{R}^{h \times h}$是权重参数,$\boldsymbol{b}_i, \boldsymbol{b}_f, \boldsymbol{b}_o \in \mathbb{R}^{1 \times h}$是偏差参数。

候选记忆细胞

接下来,长短期记忆需要计算候选记忆细胞$\tilde{\boldsymbol{C}}_t$。它的计算与上面介绍的3个门类似,但使用了值域在$[-1, 1]$的tanh函数作为激活函数,如图17所示。

长短期记忆中候选记忆细胞的计算

图17 长短期记忆中候选记忆细胞的计算

具体来说,时间步$t$的候选记忆细胞$\tilde{\boldsymbol{C}}_t \in \mathbb{R}^{n \times h}$的计算为

$$\tilde{\boldsymbol{C}}t = \text{tanh}(\boldsymbol{X}t \boldsymbol{W}{xc} + \boldsymbol{H}{t-1} \boldsymbol{W}_{hc} + \boldsymbol{b}_c),$$

其中$\boldsymbol{W}{xc} \in \mathbb{R}^{d \times h}$和$\boldsymbol{W}{hc} \in \mathbb{R}^{h \times h}$是权重参数,$\boldsymbol{b}_c \in \mathbb{R}^{1 \times h}$是偏差参数。

记忆细胞

我们可以通过元素值域在$[0, 1]$的输入门、遗忘门和输出门来控制隐藏状态中信息的流动,这一般也是通过使用按元素乘法(符号为$\odot$)来实现的。当前时间步记忆细胞$\boldsymbol{C}_t \in \mathbb{R}^{n \times h}$的计算组合了上一时间步记忆细胞和当前时间步候选记忆细胞的信息,并通过遗忘门和输入门来控制信息的流动:

$$\boldsymbol{C}_t = \boldsymbol{F}t \odot \boldsymbol{C}{t-1} + \boldsymbol{I}_t \odot \tilde{\boldsymbol{C}}_t.$$

如图6.9所示,遗忘门控制上一时间步的记忆细胞$\boldsymbol{C}_{t-1}$中的信息是否传递到当前时间步,而输入门则控制当前时间步的输入$\boldsymbol{X}_t$通过候选记忆细胞$\tilde{\boldsymbol{C}}_t$如何流入当前时间步的记忆细胞。如果遗忘门一直近似1且输入门一直近似0,过去的记忆细胞将一直通过时间保存并传递至当前时间步。这个设计可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。

长短期记忆中候选记忆细胞的计算

图18 长短期记忆中候选记忆细胞的计算

隐藏状态

有了记忆细胞以后,接下来我们还可以通过输出门来控制从记忆细胞到隐藏状态$\boldsymbol{H}_t \in \mathbb{R}^{n \times h}$的信息的流动:

$$\boldsymbol{H}_t = \boldsymbol{O}_t \odot \text{tanh}(\boldsymbol{C}_t).$$

这里的tanh函数确保隐藏状态元素值在-1到1之间。需要注意的是,当输出门近似1时,记忆细胞信息将传递到隐藏状态供输出层使用;当输出门近似0时,记忆细胞信息只自己保留。图6.10展示了长短期记忆中隐藏状态的计算。

长短期记忆中隐藏状态的计算

图19 长短期记忆中隐藏状态的计算

深度循环神经网络

在深度学习应用里,我们通常会用到含有多个隐藏层的循环神经网络,也称作深度循环神经网络。图20演示了一个有 L 个隐藏层的深度循环神经网络,每个隐藏状态不断传递至当前层的下一时间步和当前时间步的下一层。

深度循环神经网络的架构

图20 深度循环神经网络的架构

具体来说,在时间步$t$里,设小批量输入$\boldsymbol{X}_t \in \mathbb{R}^{n \times d}$(样本数为$n$,输入个数为$d$),第$\ell$隐藏层($\ell=1,\ldots,L$)的隐藏状态为$\boldsymbol{H}_t^{(\ell)} \in \mathbb{R}^{n \times h}$(隐藏单元个数为$h$),输出层变量为$\boldsymbol{O}_t \in \mathbb{R}^{n \times q}$(输出个数为$q$),且隐藏层的激活函数为$\phi$。第1隐藏层的隐藏状态和之前的计算一样:

$$\boldsymbol{H}t^{(1)} = \phi(\boldsymbol{X}t \boldsymbol{W}{xh}^{(1)} + \boldsymbol{H}{t-1}^{(1)} \boldsymbol{W}_{hh}^{(1)} + \boldsymbol{b}_h^{(1)}),$$

其中权重$\boldsymbol{W}{xh}^{(1)} \in \mathbb{R}^{d \times h}$、$\boldsymbol{W}{hh}^{(1)} \in \mathbb{R}^{h \times h}$和偏差 $\boldsymbol{b}_h^{(1)} \in \mathbb{R}^{1 \times h}$分别为第1隐藏层的模型参数。

当$1 < \ell \leq L$时,第$\ell$隐藏层的隐藏状态的表达式为

$$\boldsymbol{H}t^{(\ell)} = \phi(\boldsymbol{H}t^{(\ell-1)} \boldsymbol{W}{xh}^{(\ell)} + \boldsymbol{H}{t-1}^{(\ell)} \boldsymbol{W}_{hh}^{(\ell)} + \boldsymbol{b}_h^{(\ell)}),$$

其中权重$\boldsymbol{W}{xh}^{(\ell)} \in \mathbb{R}^{h \times h}$、$\boldsymbol{W}{hh}^{(\ell)} \in \mathbb{R}^{h \times h}$和偏差 $\boldsymbol{b}_h^{(\ell)} \in \mathbb{R}^{1 \times h}$分别为第$\ell$隐藏层的模型参数。

最终,输出层的输出只需基于第$L$隐藏层的隐藏状态:

$$\boldsymbol{O}_t = \boldsymbol{H}t^{(L)} \boldsymbol{W}{hq} + \boldsymbol{b}_q,$$

其中权重$\boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q}$和偏差$\boldsymbol{b}_q \in \mathbb{R}^{1 \times q}$为输出层的模型参数。

同多层感知机一样,隐藏层个数$L$和隐藏单元个数$h$都是超参数。此外,如果将隐藏状态的计算换成门控循环单元或者长短期记忆的计算,我们可以得到深度门控循环神经网络。

双向循环神经网络

之前介绍的循环神经网络模型都是假设当前时间步是由前面的较早时间步的序列决定的,因此它们都将信息通过隐藏状态从前往后传递。有时候,当前时间步也可能由后面时间步决定。例如,当我们写下一个句子时,可能会根据句子后面的词来修改句子前面的用词。双向循环神经网络通过增加从后往前传递信息的隐藏层来更灵活地处理这类信息。图21演示了一个含单隐藏层的双向循环神经网络的架构。

双向循环神经网络的架构

图21 双向循环神经网络的架构

#生成模型

在概率统计理论中, 生成模型是指能够随机生成观测数据的模型,尤其是在给定某些隐含参数的条件下。它给观测值和标注数据序列指定一个联合概率分布。在机器学习中,生成模型可以用来直接对数据建模(例如根据某个变量的概率密度函数进行数据采样),也可以用来建立变量间的条件概率分布。条件概率分布可以由生成模型根据贝叶斯定理形成。

香农 (1948) 给出了有一个英语双词频率表生成句子的例子。可以生成如“representing and speedily is an good”这种句子。一开始并不能生成正确的英文句子,但随着词频表由双词扩大为三词甚至多词,生成的句子也就慢慢的成型了。

生成模型的定义与判别模型相对应:生成模型是所有变量的全概率模型,而判别模型是在给定观测变量值前提下目标变量条件概率模型。因此生成模型能够用于模拟(即生成)模型中任意变量的分布情况,而判别模型只能根据观测变量得到目标变量的采样。判别模型不对观测变量的分布建模,因此它不能够表达观测变量与目标变量之间更复杂的关系。因此,生成模型更适用于无监督的任务,如分类和聚类。

生成对抗网络

生成对抗网络(英语:Generative Adversarial Network,简称GAN)是非监督式学习的一种方法,通过让两个神经网络相互博弈的方式进行学习。该方法由伊恩·古德费洛等人于2014年提出。[1]

生成对抗网络由一个生成网络与一个判别网络组成。生成网络从潜在空间(latent space)中随机采样作为输入,其输出结果需要尽量模仿训练集中的真实样本。判别网络的输入则为真实样本或生成网络的输出,其目的是将生成网络的输出从真实样本中尽可能分辨出来。而生成网络则要尽可能地欺骗判别网络。两个网络相互对抗、不断调整参数,最终目的是使判别网络无法判断生成网络的输出结果是否真实。[2][1][3]

生成对抗网络常用于生成以假乱真的图片。[4]此外,该方法还被用于生成视频[5]、三维物体模型[6]等。

强化学习

强化学习(英语:Reinforcement learning,简称RL)是机器学习中的一个领域,强调如何基于环境而行动,以取得最大化的预期利益。其灵感来源于心理学中的行为主义理论,即有机体如何在环境给予的奖励或惩罚的刺激下,逐步形成对刺激的预期,产生能获得最大利益的习惯性行为。这个方法具有普适性,因此在其他许多领域都有研究,例如博弈论、控制论、运筹学、信息论、仿真优化、多主体系统学习、群体智能、统计学以及遗传算法。在运筹学和控制理论研究的语境下,强化学习被称作“近似动态规划”(approximate dynamic programming,ADP)。在最优控制理论中也有研究这个问题,虽然大部分的研究是关于最优解的存在和特性,并非是学习或者近似方面。在经济学和博弈论中,强化学习被用来解释在有限理性的条件下如何出现平衡。

在机器学习问题中,环境通常被规范为马可夫决策过程(MDP),所以许多强化学习算法在这种情况下使用动态规划技巧。传统的技术和强化学习算法的主要区别是,后者不需要关于MDP的知识,而且针对无法找到确切方法的大规模MDP。

强化学习和标准的监督式学习之间的区别在于,它并不需要出现正确的输入/输出对,也不需要精确校正次优化的行为。强化学习更加专注于在线规划,需要在探索(在未知的领域)和遵从(现有知识)之间找到平衡。强化学习中的“探索-遵从”的交换,在多臂老虎机(英语:multi-armed bandit)问题和有限MDP中研究得最多。

理解强化学习

抛开强化学习探索反馈过程,从回合的最终结果看,强化学习也是一种有监督学习。回合最终结果的输赢就是标签,如果最终结果是好的,说明之前的一系列状态动作的决策过程是有效的,反之是无效的。通过不断地学习,最终可得到较优的状态到动作地策略分布Q函数或者状态和动作的值函数。

记忆网络

传统的深度学习模型(RNN、LSTM、GRU等)使用hidden states或者Attention机制作为他们的记忆功能,但是这种方法产生的记忆太小了,无法精确记录一段话中所表达的全部内容,也就是在将输入编码成dense vectors的时候丢失了很多信息。记忆网络采用一种可读写的外部记忆模块,并将其和inference组件联合训练,最终得到一个可以被灵活操作的记忆模块。

参考链接

  1. 人工神经网络,by wikipedia.
  2. 神经网络简介,by google.
  3. 一文详解神经网络 BP 算法原理及 Python 实现,by AI研习社.
  4. 反向传播算法动态演示, by google.
  5. 深度学习架构家谱,by hunkim.
  6. 动手学深度学习,by d2l-zh.
  7. 神经网络之LeNet结构分析及参数详解,by Genius_zz.
  8. 经典CNN结构简析:AlexNet、VGG、NIN、GoogLeNet、ResNet etc. ,by Uno Whoiam.
  9. 一文读懂VGG网络,by Amusi.
  10. 生成模型,by wikipedia.
  11. 生成对抗网络,by wikipedia.
  12. GAN动态演示,by poloclub.
  13. 强化学习,by wikipedia.
  14. 25张图让你读懂神经网络架构, by Nicholas_Liu2017.
  15. 常见的损失函数(loss function)总结,by yyHaker.
0%