Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

Linux图形界面基础知识

发表于 2018-12-09 | 更新于 2018-12-10

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.

家用电器安全使用年限与折旧

发表于 2018-12-08

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

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

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

家用电器安全使用年限

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

  • 电吹风: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 百度百科.

空战类游戏仿真的一些思考

发表于 2018-12-08 | 更新于 2018-12-18

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

飞机的建模仿真

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

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

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

导弹的建模仿真

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

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

诱饵弹的建模仿真

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

参考链接

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

计算机视觉研究范畴简介

发表于 2018-12-07

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

  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 魏秀参

三维建模方法简介

发表于 2018-12-05 | 更新于 2022-12-19

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

三维建模方法

选择软件

当前三维软件层出不穷,既有商业级的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.

深度强化学习研究笔记

发表于 2018-11-26 | 更新于 2022-04-16

深度强化学习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学习笔记

发表于 2018-11-24 | 更新于 2018-12-21

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
  2. 测试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.

人工智能——人类科技再次飞跃的门槛

发表于 2018-11-21 | 更新于 2019-01-17

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

问题的提出

问题的分析

解决方案

总结

参考文献

  1. 机器学习, by wikipedia.

机器学习之神经网络

发表于 2018-11-20 | 更新于 2020-02-10

人工神经网络(英语: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.

机器学习之相关概念

发表于 2018-11-19 | 更新于 2024-12-18

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

定义

机器学习有下面几种定义:

  • 机器学习是一门人工智能的科学,该领域的主要研究对象是人工智能,特别是如何在经验学习中改善具体算法的性能。
  • 机器学习是对能通过经验自动改进的计算机算法的研究。
  • 机器学习是用数据或以往的经验,以此优化计算机程序的性能标准。

一种经常引用的英文定义是:A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.

分类

机器学习可以分成下面几种类别:

  • 监督学习:从给定的训练数据集中学习出一个函数,当新的数据到来时,可以根据这个函数预测结果。监督学习的训练集要求是包括输入和输出,也可以说是特征和目标。训练集中的目标是由人标注的。常见的监督学习算法包括回归分析和统计分类。

监督学习和非监督学习的差别就是训练集目标是否人标注。他们都有训练集且都有输入和输出。

  • 无监督学习:与监督学习相比,训练集没有人为标注的结果。常见的无监督学习算法有生成对抗网络(GAN)、聚类。
  • 半监督学习:介于监督学习与无监督学习之间。
  • 强化学习:通过观察来学习做成如何的动作。每个动作都会对环境有所影响,学习对象根据观察到的周围环境的反馈来做出判断。

算法

具体的机器学习算法有:

  • 构造间隔理论分布:聚类分析和模式识别
    • 人工神经网络
    • 决策树
    • 感知器
    • 支持向量机
    • 集成学习AdaBoost
    • 降维与度量学习
    • 聚类
    • 贝叶斯分类器
  • 构造条件概率:回归分析和统计分类
    • 高斯过程回归
    • 线性判别分析
    • 最近邻居法
    • 径向基函数核
  • 通过再生模型构造概率密度函数:
    • 最大期望算法
    • 概率图模型:包括贝叶斯网和Markov随机场
    • Generative Topographic Mapping
  • 近似推断技术:
    • 马尔可夫链
    • 蒙特卡罗方法
    • 变分法
  • 最优化:大多数以上方法,直接或者间接使用最优化算法。

机器学习基础

标签

标签是我们要预测的事物,即简单线性回归中的 y 变量。标签可以是小麦未来的价格、图片中显示的动物品种、音频剪辑的含义或任何事物。

特征

特征是输入变量,即简单线性回归中的 x 变量。简单的机器学习项目可能会使用单个特征,而比较复杂的机器学习项目可能会使用数百万个特征,按如下方式指定:
$${ x_1,x_2,…x_N } $$

在垃圾邮件检测器示例中,特征可能包括:

  • 电子邮件文本中的字词
  • 发件人的地址
  • 发送电子邮件的时段
  • 电子邮件中包含“一种奇怪的把戏”这样的短语。

样本

样本是指数据的特定实例:x。(我们采用粗体 x 表示它是一个矢量。)我们将样本分为以下两类:

  • 有标签样本
  • 无标签样本

有标签样本同时包含特征和标签,常用于训练模型。。即:

1
labeled examples: {features, label}: (x, y)

无标签样本包含特征,但不包含标签,常用于模型预测。即:

1
unlabeled examples: {features, ?}: (x, ?)

模型

模型定义了特征与标签之间的关系。例如,垃圾邮件检测模型可能会将某些特征与“垃圾邮件”紧密联系起来。我们来重点介绍一下模型生命周期的两个阶段:

  • 训练表示创建或学习模型。向模型展示有标签样本,让模型逐渐学习特征与标签之间的关系。

  • 推断表示将训练后的模型应用于无标签样本。使用训练后的模型来做出有用的预测 (y’)。

回归与分类

回归模型可预测连续值。例如,回归模型做出的预测可回答如下问题:

  • 加利福尼亚州一栋房产的价值是多少?

  • 用户点击此广告的概率是多少?

分类模型可预测离散值。例如,分类模型做出的预测可回答如下问题:

  • 某个指定电子邮件是垃圾邮件还是非垃圾邮件?

  • 这是一张狗、猫还是仓鼠图片?

损失

训练模型表示通过有标签样本来学习(确定)所有权重和偏差的理想值。在监督式学习中,机器学习算法通过以下方式构建模型:检查多个样本并尝试找出可最大限度地减少损失的模型;这一过程称为经验风险最小化。

损失是对糟糕预测的惩罚。也就是说,损失是一个数值,表示对于单个样本而言模型预测的准确程度。如果模型的预测完全准确,则损失为零,否则损失会较大。训练模型的目标是从所有样本中找到一组平均损失“较小”的权重和偏差。

平方损失:又称为 $L_2$ 损失,一种常见的损失函数。例如单个样本的平方损失如下:

1
2
3
= the square of the difference between the label and the prediction
= (observation - prediction(x))^2
= (y - y')^2

均方误差 (MSE) 指的是每个样本的平均平方损失。要计算 MSE,请求出各个样本的所有平方损失之和,然后除以样本数量:
$$
MSE = \frac{1}{N} \sum_{(x,y)\in D} (y - prediction(x))^2
$$

其中:

  • (x,y)指的是样本,其中
    • x指的是模型进行预测时使用的特征集(例如,温度、年龄和交配成功率)。
    • y指的是样本的标签(例如,每分钟的鸣叫次数)。
  • prediction(x)指的是权重和偏差与特征集 x 结合的函数。
  • D指的是包含多个有标签样本(即 (x,y))的数据集。
  • N指的是D中的样本数量。

迭代方法

下图显示了机器学习算法用于训练模型的迭代试错过程:

用于训练模型的迭代方法

图1 用于训练模型的迭代方法

梯度下降法

计算参数更新的目标是在模型的迭代试错过程中,使损失越来越小。而常用的方法就是梯度下降法。

回归问题产生的损失与权重图为凸形

图2 回归问题产生的损失与权重图为凸形

对于图2所示的凸形问题,刚好存在一个斜率正好为 0 的位置,即是损失函数的收敛之处。梯度下降法的第一个阶段是为$w_1$ 选择一个起始值(起点)。

然后,梯度下降法算法会计算损失曲线在起点处的梯度。简而言之,梯度是偏导数的矢量;它可以让您了解哪个方向距离目标“更近”或“更远”。

请注意,梯度是一个矢量,因此具有以下两个特征:

  • 方向
  • 大小

梯度始终指向损失函数中增长最为迅猛的方向。梯度下降法算法会沿着负梯度的方向走一步,以便尽快降低损失。

为了确定损失函数曲线上的下一个点,梯度下降法算法会将梯度大小的一部分与起点相加,如图3所示:

一个梯度步长将我们移动到损失曲线上的下一个点

图3 一个梯度步长将我们移动到损失曲线上的下一个点

然后,梯度下降法会重复此过程,逐渐接近最低点。

学习速率

梯度下降法算法用梯度乘以一个称为学习速率(有时也称为步长)的标量,以确定下一个点的位置。例如,如果梯度大小为 2.5,学习速率为 0.01,则梯度下降法算法会选择距离前一个点 0.025 的位置作为下一个点。

超参数是编程人员在机器学习算法中用于调整的旋钮。大多数机器学习编程人员会花费相当多的时间来调整学习速率。如果您选择的学习速率过小,就会花费太长的学习时间。如果您指定的学习速率过大,下一个点将永远在 U 形曲线的底部随意弹跳。

随机梯度下降法

在梯度下降法中,批量指的是用于在单次迭代中计算梯度的样本总数。到目前为止,我们一直假定批量是指整个数据集。就 Google 的规模而言,数据集通常包含数十亿甚至数千亿个样本。此外,Google 数据集通常包含海量特征。因此,一个批量可能相当巨大。如果是超大批量,则单次迭代就可能要花费很长时间进行计算。

通过从我们的数据集中随机选择样本,我们可以通过小得多的数据集估算(尽管过程非常杂乱)出较大的平均值。 随机梯度下降法 (SGD) 将这种想法运用到极致,它每次迭代只使用一个样本(批量大小为 1)。如果进行足够的迭代,SGD 也可以发挥作用,但过程会非常杂乱。“随机”这一术语表示构成各个批量的一个样本都是随机选择的。

小批量随机梯度下降法(小批量 SGD)是介于全批量迭代与 SGD 之间的折衷方案。小批量通常包含 10-1000 个随机选择的样本。小批量 SGD 可以减少 SGD 中的杂乱样本数量,但仍然比全批量更高效。

泛化(Generalization)

泛化是指机器学习对从真实概率分布(已隐藏)中抽取的新数据做出良好预测的能力。要取得良好的泛化能力,机器学习必须满足以下基本假设,同时防止过拟合。

  • 机器学习的基本假设:

    • 从分布中随机抽取独立同分布 (i.i.d) 的样本。换言之,样本之间不会互相影响。(另一种解释:i.i.d. 是表示变量随机性的一种方式)。
    • 分布是平稳的;即分布在数据集内不会发生变化。
    • 从同一分布的数据划分中抽取样本。
  • 过拟合模型在训练过程中产生的损失很低,但在预测新数据方面的表现却非常糟糕。

训练集与测试集

机器学习模型旨在根据以前未见过的新数据做出良好预测。但是,如果您要根据数据集构建模型,如何获得以前未见过的数据呢?一种方法是将您的数据集分成两个子集:

  • 训练集 - 用于训练模型的子集。
  • 测试集 - 用于测试模型的子集。

测试集应满足以下两个条件:

  • 规模足够大,可产生具有统计意义的结果。
  • 能代表整个数据集。换言之,挑选的测试集的特征应该与训练集的特征相同。

验证集

将数据集划分为训练集和测试集两个子集是个不错的想法,但不是万能良方。通过将数据集划分为训练集、验证集、测试集三个子集,可以大幅降低过拟合的发生几率。

使用验证集评估训练集的效果。然后,在模型“通过”验证集之后,使用测试集再次检查评估结果。图4展示了这一新工作流程:

使用验证集的工作流程

图4 使用验证集的工作流程

特征表示

特征工程指的是将原始数据转换为特征矢量。进行特征工程预计需要大量时间。

  • 映射数值: 机器学习模型根据浮点值进行训练,因此整数和浮点原始数据不需要特殊编码。
  • 映射字符串值: 首先为要表示的所有特征的字符串值定义一个词汇表。然后使用该词汇表创建一个独热编码,用于将指定字符串值表示为二元矢量。
  • 映射分类(枚举)值

通过独热编码映射字符串值

图5 通过独热编码映射字符串值

良好特征的特点

  • 避免很少使用的离散特征值。

良好的特征值应该在数据集中出现大约 5 次以上。这样一来,模型就可以学习该特征值与标签是如何关联的。

  • 最好具有清晰明确的含义。

每个特征对于项目中的任何人来说都应该具有清晰明确的含义。例如,下面的房龄适合作为特征,可立即识别为年龄:

1
house_age: 27
  • 不要将“神奇”的值与实际数据混为一谈

良好的浮点特征不包含超出范围的异常断点或“神奇”的值。例如,假设一个特征具有 0 到 1 之间的浮点值。那么,如下值是可以接受的:

1
2
quality_rating: 0.82
quality_rating: 0.37

不过,如果用户没有输入 quality_rating,则数据集可能使用如下神奇值来表示不存在该值:

1
quality_rating: -1

为解决神奇值的问题,需将该特征转换为两个特征:

1
2
3
一个特征只存储质量评分,不含神奇值。

一个特征存储布尔值,表示是否提供了 quality_rating。为该布尔值特征指定一个名称,例如 is_quality_rating_defined。
  • 考虑上游不稳定性

特征的定义不应随时间发生变化。例如,下列值是有用的,因为城市名称一般不会改变。(注意,我们仍然需要将“br/sao_paulo”这样的字符串转换为独热矢量。)

1
city_id: "br/sao_paulo"

但收集由其他模型推理的值会产生额外成本。可能值“219”目前代表圣保罗,但这种表示在未来运行其他模型时可能轻易发生变化:

1
inferred_city_cluster: "219"

清理数据

即使是非常少量的坏样本会破坏掉一个大规模数据集,因此需花费大量的时间挑出坏样本并加工可以挽救的样本。

  1. 缩放特征值: 缩放是指将浮点特征值从自然范围(例如 100 到 900)转换为标准范围(例如 0 到 1 或 -1 到 +1)。如果特征集包含多个特征,则缩放特征可以带来以下优势:

    • 帮助梯度下降法更快速地收敛。
    • 帮助避免“NaN 陷阱”。
    • 帮助模型为每个特征确定合适的权重。
  2. 处理极端离群值

    • 对每个值取对数
    • 将最大值“限制”为某个任意值
  3. 分箱

  4. 清查

数据集中的很多样本是不可靠的,原因有以下一种或多种:

  • 遗漏值。 例如,有人忘记为某个房屋的年龄输入值。
  • 重复样本。 例如,服务器错误地将同一条记录上传了两次。
  • 不良标签。 例如,有人错误地将一颗橡树的图片标记为枫树。
  • 不良特征值。 例如,有人输入了多余的位数,或者温度计被遗落在太阳底下。

正则化

图6泛化曲线显示的是训练集和验证集相对于训练迭代次数的损失。

训练集和验证集损失

图6 训练集和验证集损失

图6显示的是某个模型的训练损失逐渐减少,但验证损失最终增加。换言之,该泛化曲线显示该模型与训练集中的数据过拟合。根据奥卡姆剃刀定律,或许我们可以通过降低复杂模型的复杂度来防止过拟合,这种原则称为正则化。

正则化以最小化损失和复杂度为目标,这称为结构风险最小化:
$$\text{minimize(Loss(Data|Model) + complexity(Model))}$$

现在,训练优化算法是一个由两项内容组成的函数:一个是损失项,用于衡量模型与数据的拟合度,另一个是正则化项,用于衡量模型复杂度。

有两种常用衡量模型复杂度的方法:

  • 将模型复杂度作为模型中所有特征的权重的函数。
  • 将模型复杂度作为具有非零权重的特征总数的函数。

如果模型复杂度是权重的函数,则特征权重的绝对值越高,对模型复杂度的贡献就越大。

L2正则化

可以使用 L2 正则化公式来量化复杂度,该公式将正则化项定义为所有特征权重的平方和:
$$L_2\text{ regularization term} = ||\boldsymbol w||_2^2 = {w_1^2 + w_2^2 + … + w_n^2}$$

在这个公式中,接近于 0 的权重对模型复杂度几乎没有影响,而离群值权重则可能会产生巨大的影响。

模型开发者通过以下方式来调整正则化项的整体影响:用正则化项的值乘以名为 lambda(又称为正则化率)的标量。也就是说,模型开发者会执行以下运算:

$$\text{minimize(Loss(Data|Model)} + \lambda \text{ complexity(Model))}$$

执行 L2 正则化对模型具有以下影响:

  • 使权重值接近于 0(但并非正好为 0)
  • 使权重的平均值接近于 0,且呈正态(钟形曲线或高斯曲线)分布。

在选择 lambda 值时,目标是在简单化和训练数据拟合之间达到适当的平衡:

  • 如果您的 lambda 值过高,则模型会非常简单,但是您将面临数据欠拟合的风险。您的模型将无法从训练数据中获得足够的信息来做出有用的预测。

  • 如果您的 lambda 值过低,则模型会比较复杂,并且您将面临数据过拟合的风险。您的模型将因获得过多训练数据特点方面的信息而无法泛化到新数据。

L1正则化

稀疏矢量通常包含许多维度。创建特征组合会导致包含更多维度。由于使用此类高维度特征矢量,因此模型可能会非常庞大,并且需要大量的 RAM。

在高维度稀疏矢量中,最好尽可能使权重正好降至 0。正好为 0 的权重基本上会使相应特征从模型中移除。 将特征设为 0 可节省 RAM 空间,且可以减少模型中的噪点。

L1 正则化使模型中很多信息缺乏的系数正好为 0,从而在推理时节省 RAM,同时具有凸优化的优势,可有效进行计算。

L2 和 L1 采用不同的方式降低权重:

  • L2 会降低权重2。
  • L1 会降低 |权重|。

因此,L2 和 L1 具有不同的导数:

  • L2 的导数为 2 * 权重。
  • L1 的导数为 k(一个常数,其值与权重无关)。

逻辑回归

许多问题需要将概率估算值作为输出。逻辑回归是一种极其高效的概率计算机制。实际上,您可以通过下两种方式之一使用返回的概率:

  • “按原样”
  • 转换成二元类别。

在很多情况下,您会将逻辑回归输出映射到二元分类问题的解决方案,该二元分类问题的目标是正确预测两个可能的标签(例如,“垃圾邮件”或“非垃圾邮件”)中的一个。

您可能想知道逻辑回归模型如何确保输出值始终落在 0 和 1 之间。巧合的是,S 型函数生成的输出值正好具有这些特性,其定义如下:

$$y = \frac{1}{1 + e^{-z}}$$

S 型函数会产生以下曲线图:

S 型函数

图7 S 型函数

如果 z 表示使用逻辑回归训练的模型的线性层的输出,则 S 型(z) 函数会生成一个介于 0 和 1 之间的值(概率)。用数学方法表示为:

$$y’ = \frac{1}{1 + e^{-(z)}}$$

其中:

  • y’ 是逻辑回归模型针对特定样本的输出。
  • z 是 b + w1x1 + w2x2 + … wNxN
    • “w”值是该模型学习的权重和偏差。
    • “x”值是特定样本的特征值。

请注意,z 也称为对数几率,因为 S 型函数的反函数表明,z 可定义为标签“1”(例如“狗叫”)的概率除以标签“0”(例如“狗不叫”)的概率得出的值的对数:
$$z = log(\frac{y}{1-y})$$

逻辑回归模型训练

线性回归的损失函数是平方损失。逻辑回归的损失函数是对数损失函数,定义如下:
$$Log Loss = \sum_{(x,y)\in D} -ylog(y’) - (1 - y)log(1 - y’)$$

其中:

  • (xy)ϵD 是包含很多有标签样本 (x,y) 的数据集。
  • “y”是有标签样本中的标签。由于这是逻辑回归,因此“y”的每个值必须是 0 或 1。
  • “y’”是对于特征集“x”的预测值(介于 0 和 1 之间)。

对数损失函数的方程式与 Shannon 信息论中的熵测量密切相关。它也是似然函数的负对数(假设“y”属于伯努利分布)。实际上,最大限度地降低损失函数的值会生成最大的似然估计值。

正则化在逻辑回归建模中极其重要。如果没有正则化,逻辑回归的渐近性会不断促使损失在高维度空间内达到 0。因此,大多数逻辑回归模型会使用以下两个策略之一来降低模型复杂性:

  • L2 正则化。
  • 早停法,即,限制训练步数或学习速率。

分类

指定阈值

为了将逻辑回归值映射到二元类别,您必须指定分类阈值(也称为判定阈值)。如果值高于该阈值,则表示“垃圾邮件”;如果值低于该阈值,则表示“非垃圾邮件”。人们往往会认为分类阈值应始终为 0.5,但阈值取决于具体问题,因此您必须对其进行调整。

真与假以及正类别与负类别

  • 真正例是指模型将正类别样本正确地预测为正类别。
  • 真负例是指模型将负类别样本正确地预测为负类别。
  • 假正例是指模型将负类别样本错误地预测为正类别
  • 假负例是指模型将正类别样本错误地预测为负类别。

准确率

准确率是一个用于评估分类模型的指标。通俗来说,准确率是指我们的模型预测正确的结果所占的比例。正式点说,准确率的定义如下:

$$\text{Accuracy} = \frac{\text{Number of correct predictions}}{\text{Total number of predictions}}$$

对于二元分类,也可以根据正类别和负类别按如下方式计算准确率:

$$\text{Accuracy} = \frac{TP+TN}{TP+TN+FP+FN}$$

其中,TP = 真正例,TN = 真负例,FP = 假正例,FN = 假负例。

精确率和召回率

当使用分类不平衡的数据集(比如正类别标签和负类别标签的数量之间存在明显差异)时,单单准确率一项并不能反映全面情况。这时需要能够更好地评估分类不平衡问题的指标:精确率和召回率。

精确率的定义如下:
$$\text{Precision} = \frac{TP}{TP+FP}$$

从数学上讲,召回率的定义如下:
$$\text{Recall} = \frac{TP}{TP+FN}$$

要全面评估模型的有效性,必须同时检查精确率和召回率。遗憾的是,精确率和召回率往往是此消彼长的情况。

ROC 和曲线下面积

ROC 曲线(接收者操作特征曲线)是一种显示分类模型在所有分类阈值下的效果的图表。该曲线绘制了以下两个参数:

  • 真正例率
  • 假正例率

真正例率 (TPR) 是召回率的同义词,因此定义如下:

$$TPR = \frac{TP} {TP + FN}$$

假正例率 (FPR) 的定义如下:

$$FPR = \frac{FP} {FP + TN}$$

ROC 曲线用于绘制采用不同分类阈值时的 TPR 与 FPR。降低分类阈值会导致将更多样本归为正类别,从而增加假正例和真正例的个数。下图显示了一个典型的 ROC 曲线。

不同分类阈值下的 TP 率与 FP 率

图8 不同分类阈值下的 TP 率与 FP 率

曲线下面积表示“ROC 曲线下面积”。也就是说,曲线下面积测量的是从 (0,0) 到 (1,1) 之间整个 ROC 曲线以下的整个二维面积(参考积分学)。

曲线下面积(ROC 曲线下面积)

图9 曲线下面积(ROC 曲线下面积)

曲线下面积对所有可能的分类阈值的效果进行综合衡量。曲线下面积的一种解读方式是看作模型将某个随机正类别样本排列在某个随机负类别样本之上的概率。

曲线下面积的取值范围为 0-1。预测结果 100% 错误的模型的曲线下面积为 0.0;而预测结果 100% 正确的模型的曲线下面积为 1.0。

曲线下面积因以下两个原因而比较实用:

  • 曲线下面积的尺度不变。它测量预测的排名情况,而不是测量其绝对值。
  • 曲线下面积的分类阈值不变。它测量模型预测的质量,而不考虑所选的分类阈值。

不过,这两个原因都有各自的局限性,这可能会导致曲线下面积在某些用例中不太实用:

  • 并非总是希望尺度不变。 例如,有时我们非常需要被良好校准的概率输出,而曲线下面积无法告诉我们这一结果。

  • 并非总是希望分类阈值不变。 在假负例与假正例的代价存在较大差异的情况下,尽量减少一种类型的分类错误可能至关重要。例如,在进行垃圾邮件检测时,您可能希望优先考虑尽量减少假正例(即使这会导致假负例大幅增加)。对于此类优化,曲线下面积并非一个实用的指标。

预测偏差

逻辑回归预测应当无偏差。即:
$$ \text{预测平均值}\approx\text{观察平均值}$$
预测偏差指的是这两个平均值之间的差值。即:
$$\text{预测偏差} = \text{预测平均值} - \text{数据集中相应标签的平均值}$$

造成预测偏差的可能原因包括:

  • 特征集不完整
  • 数据集混乱
  • 模型实现流水线中有错误?
  • 训练样本有偏差
  • 正则化过强

端到端(end to end)

传统机器学习算法在应用过程中需要经历特征工程这一步骤,从研究对象中提取特征信息,便于后续的训练和测试。在传统机器学习中,特征工程非常重要,它提取特征的好坏关系到机器学习的最终效果。

本质上,特征工程是将研究对象信息降维的过程。而深度学习则无需这一手工提取特征的过程。以深度学习在图像分类中的应用为例,它直接输入高维的原始图像,输出即是图像分类。这个过程即叫做端到端。

参考链接

  1. 机器学习,by wikipedia.
  2. 机器学习速成课程,by google.
  3. MathJax使用LaTeX语法编写数学公式教程
  4. 什么是 end-to-end 神经网络?,by zhihu.
  5. 数学基础——浅谈似然,by shidata.
  6. 【机器学习】解构概率,重构世界:贝叶斯定理与智能世界的暗语,by 半截诗​.
上一页1…464748…53下一页

Jack Huang

521 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse