我 相 信 这 么 优秀 的 你 已 经 置 顶 了 我
以下内容来源于一次部门内部的分享,主要针对AI初学者,介绍包括CNN、Deep Q Network以及TensorFlow平台等内容。由于笔者并非深度学习算法研究者,因此以下更多从应用的角度对整个系统进行介绍,而不会进行详细的公式推导。
本文主要介绍如何通过AI(人工智能)的方式玩Flappy Bird游戏,分为以下四个部分内容:
一、Flappy Bird 游戏展示在介绍模型、算法前先来直接看下效果,上图是刚开始训练的时候,画面中的小鸟就像无头苍蝇一样乱飞,下图展示的是在本机(后面会给出配置)训练超过10小时后(训练步数超过2000000)的情况,其最好成绩已经超过200分,人类玩家已基本不可能超越。 训练数小于10000步(刚开始训练) 训练步数大于2000000步(10小时后) 由于本机配置了CUDA以及cuDNN,采用了NVIDIA的显卡进行并行计算,所以这里提前贴一下运行时的日志输出。
加载CUDA运算库
TensorFlow运行设备/gpu:0 /gpu:0 这是TensorFlow平台默认的配置方法,表示使用系统中的第一块显卡。
二、模型:卷积神经网络神经网络算法是由众多的神经元可调的连接权值连接而成,具有大规模并行处理、分布式信息存储、良好的自组织自学习能力等特点。人工神经元与生物神经元结构类似,其结构对比如下图所示。 生物神经元 人工神经元 人工神经元的输入(x1,x2...xm)类似于生物神经元的树突,输入经过不同的权值(wk1, wk2, ....wkn),加上偏置,经过激活函数得到输出,最后将输出传输到下一层神经元进行处理。 单神经元输出函数 激活函数为整个网络引入了非线性特性,这也是神经网络相比于回归等算法拟合能力更强的原因。常用的激活函数包括sigmoid、tanh等,它们的函数表达式如下: sigmoid函数 tanh双曲正切函数 这里可以看出,sigmoid函数的值域是(0,1),tanh函数的值域是(-1,1)。 卷积神经网络起源于动物的视觉系统,主要包含的技术是:
全连接网络的问题在于:
人类的视觉系统决定了人在观察外界的时候,总是从局部到全局。
因此,卷积神经网络与人类的视觉类似,采用局部感知,低层的神经元只负责感知局部的信息,在向后传输的过程中,高层的神经元将局部信息综合起来得到全局信息。 全连接与局部连接的对比(图片来自互联网) 从上图中可以看出,采用局部连接之后,可以大大的降低训练参数的量级。
虽然通过局部感知降低了训练参数的量级,但整个网络需要训练的参数依然很多。 参数共享就是将多个具有相同统计特征的参数设置为相同,其依据是图像中一部分的统计特征与其它部分是一样的。其实现是通过对图像进行卷积(卷积神经网络命名的来源)。 可以理解为,比如从一张图像中的某个局部(卷积核大小)提取了某种特征,然后以这种特征为探测器,应用到整个图像中,对整个图像顺序进行卷积,得到不同的特征。 卷积过程(图片来自互联网) 每个卷积都是一种特征提取方式,就像一个筛子,将图像中符合条件(激活值越大越符合条件)的部分筛选出来,通过这种卷积就进一步降低训练参数的量级。
如上,每个卷积都是一种特征提取方式,那么对于整幅图像来讲,单个卷积核提取的特征肯定是不够的,那么对同一幅图像使用多种卷积核进行特征提取,就能得到多幅特征图(feature map)。 不同的卷积核提取不同的特征(图片来自互联网) 多幅特征图可以看成是同一张图像的不同通道,这个概念在后面代码实现的时候用得上。
得到特征图之后,可以使用提取到的特征去训练分类器,但依然会面临特征维度过多,难以计算,并且可能过拟合的问题。从图像识别的角度来讲,图像可能存在偏移、旋转等,但图像的主体却相同的情况。也就是不同的特征向量可能对应着相同的结果,那么池化就是解决这个问题的。 池化过程(图片来自互联网) 池化就是将池化核范围内(比如2*2范围)的训练参数采用平均值(平均值池化)或最大值(最大值池化)来进行替代。 终于到了展示模型的时候,下面这幅图是笔者手画的(用电脑画太费时,将就看吧),这幅图展示了本文中用于训练游戏所用的卷积神经网络模型。 卷积神经网络模型 图像的处理过程
可以看出,该模型实现了端到端的学习,输入的是游戏屏幕的截图信息(代码中经过opencv处理),输出的是游戏的动作,即是否点击屏幕。深度学习的强大在于其数据拟合能力,不需要传统机器学习中复杂的特征提取过程,而是依靠模型发现数据内部的关系。
三、算法:Deep Q Network有了卷积神经网络模型,那么怎样训练模型?使得模型收敛,从而能够指导游戏动作呢?机器学习分为监督学习、非监督学习和强化学习,这里要介绍的Q Network属于强化学习(Reinforcement Learning)的范畴。在正式介绍Q Network之前,先简单说下它的光荣历史。 2014年Google 4亿美金收购DeepMind的桥段,大家可能听说过。那么,DeepMind是如何被Google给盯上的呢?最终原因可以归咎为这篇论文: DeepMind团队通过强化学习,完成了20多种游戏,实现了端到端的学习。其用到的算法就是Q Network。2015年,DeepMind团队在《Nature》上发表了一篇升级版: 自此,在这类游戏领域,人已经无法超过机器了。后来又有了AlphaGo,以及Master,当然,这都是后话了。其实本文也属于上述论文的范畴,只不过基于TensorFlow平台进行了实现,加入了一些笔者自己的理解而已。 回到正题,Q Network属于强化学习,那么先介绍下强化学习。 强化学习模型
强化学习过程有两个组成部分:
如图所示,在每步迭代过程中,首先智能代理(学习系统)接收环境的状态
MDP:马尔科夫决策过程 马尔科夫决策过程与著名的HMM(隐马尔科夫模型)相同的是,它们都具有马尔科夫特性。那么什么是马尔科夫特性呢?简单来说,就是未来的状态只取决于当前的状态,与过去的状态无关。
上图可以用一个很形象的例子来说明。比如你毕业进入了一个公司,你的初始职级是T1(对应图中的 这里注意下,我们当然希望获取最多的升职,那么问题转换为:如何根据当前状态s(s属于状态集S),从A中选取动作a执行于环境,从而获取最多的r,即r1 + r2 ……+rn的和最大 ?这里必须要引入一个数学公式:状态值函数。 状态值函数模型 公式中有个折合因子γ,其取值范围为[0,1],当其为0时,表示只考虑当前动作对当前的影响,不考虑对后续步骤的影响,当其为1时,表示当前动作对后续每步都有均等的影响。当然,实际情况通常是当前动作对后续得分有一定的影响,但随着步数增加,其影响减小。 从公式中可以看出,状态值函数可以通过迭代的方式来求解。增强学习的目的就是求解马尔可夫决策过程(MDP)的最优策略。
求解上述状态函数需要采用动态规划的方法,而具体到公式,不得不提:
贝尔曼方程 其中,π代表上述提到的策略,Q π (s, a)相比于V π (s),引入了动作,被称作动作值函数。对贝尔曼方程求最优解,就得到了贝尔曼最优性方程。 状态值函数最优解 动作值函数最优解 求解该方程有两种方法:策略迭代和值迭代。
策略迭代分为两个步骤:策略评估和策略改进,即首先评估策略,得到状态值函数,其次,改进策略,如果新的策略比之前好,就替代老的策略。 策略迭代
从上面我们可以看到,策略迭代算法包含了一个策略估计的过程,而策略估计则需要扫描(sweep)所有的状态若干次,其中巨大的计算量直接影响了策略迭代算法的效率。而值迭代每次只扫描一次,更新过程如下: 值迭代 即在值迭代的第k+1次迭代时,直接将能获得的最大的Vπ(s)值赋给Vk+1。
Q-Learning是根据值迭代的思路来进行学习的。该算法中,Q值更新的方法如下: Q值更新方法 虽然根据值迭代计算出目标Q值,但是这里并没有直接将这个Q值(是估计值)直接赋予新的Q,而是采用渐进的方式类似梯度下降,朝目标迈近一小步,取决于α,这就能够减少估计误差造成的影响。类似随机梯度下降,最后可以收敛到最优的Q值。具体算法如下: Q-Learning算法 如果没有接触过动态规划的童鞋看上述公式可能有点头大,下面通过表格来演示下Q值更新的过程,大家就明白了。
Q-Learning算法的过程就是存储Q值的过程。上表中,横列为状态s,纵列为Action a,s和a决定了表中的Q值。
Q值更新公式 来更新Q值,这里我们假设α是1,λ也等于1,也就是每一次都把目标Q值赋给Q。那么这里公式变成: Q值更新公式 所以在这里,就是 本次Q值更新 那么对应的s3状态,最大值是0,所以 Q值 Q表格就变成:
然后置位当前状态s为s3。
Q值更新 所以Q的表格就变成:
上述表格演示了具有4种状态/4种行为的系统,然而在实际应用中,以本文讲到的Flappy Bird游戏为例,界面为80*80个像素点,每个像素点的色值有256种可能。那么实际的状态总数为256的80*80次方,这是一个很大的数字,直接导致无法通过表格的思路进行计算。 因此,为了实现降维,这里引入了一个价值函数近似的方法,通过一个函数表近似表达价值函数: 价值函数近似 其中,ω 与 b 分别为参数。看到这里,终于可以联系到前面提到的神经网络了,上面的表达式不就是神经元的函数吗?
下面这张图来自论文《Human-level Control through Deep Reinforcement Learning》,其中详细介绍了上述将Q值神经网络化的过程。(感兴趣的可以点之前的链接了解原文~) Q-network 以本文为例,输入是经过处理的4个连续的80x80图像,然后经过三个卷积层,一个池化层,两个全连接层,最后输出包含每一个动作Q值的向量。 现在已经将Q-learning神经网络化为Q-network了,接下来的问题是如何训练这个神经网络。神经网络训练的过程其实就是一个最优化方程求解的过程,定义系统的损失函数,然后让损失函数最小化的过程。 训练过程依赖于上述提到的DQN算法,以目标Q值作为标签,因此,损失函数可以定义为: DQN损失函数(来源于论文) 上面公式是 DQN算法(来源于论文) 值得注意的是这里的 由于玩Flappy Bird游戏,采集的样本是一个时间序列,样本之间具有连续性,如果每次得到样本就更新Q值,受样本分布影响,效果会不好。因此,一个很直接的想法就是把样本先存起来,然后随机采样如何?这就是Experience Replay的思想。 算法实现上,先反复实验,并且将实验数据存储在 四、代码:TensorFlow实现终于到了看代码的时候。首先申明下,当笔者从Deep Mind的论文入手,试图用TensorFlow实现对Flappy Bird游戏进行实现时,发现github已有大神完成demo。思路相同,所以直接以公开代码为例进行分析说明了。 如有源码需要,请移步github:Using Deep Q-Network to Learn How To Play Flappy Bird。 代码从结构上来讲,主要分为以下几部分:
1. GameState游戏类及frame_step方法通过Python实现游戏必然要用pygame库,其包含时钟、基本的显示控制、各种游戏控件、触发事件等,对此有兴趣的,可以详细了解pygame。frame_step方法的入参为shape为 (2,) 的ndarray,值域: [1,0]:什么都不做; [0,1]:提升Bird。来看下代码实现: 后续操作包括检查得分、设置界面、检查是否碰撞等,这里不再详细展开。 分别表示界面图像数据,得分以及是否结束游戏。对应前面强化学习模型,界面图像数据表示环境状态 s,得分表示环境给予学习系统的反馈 r。 2. CNN模型构建该Demo中包含三个卷积层,一个池化层,两个全连接层,最后输出包含每一个动作Q值的向量。因此,首先定义权重、偏置、卷积和池化函数: 然后,通过上述函数构建卷积神经网络模型(对代码中参数不解的,可直接往前翻,看上面那张手画的图)。 3. OpenCV-Python图像预处理方法
这部分主要对frame_step方法返回的数据进行了灰度化和二值化,也就是最基本的图像预处理方法。 4. DQN训练过程这是代码部分要讲的重点,也是上述Q-learning算法的代码化。 i. 在进入训练之前,首先创建一些变量:在TensorFlow中,通常有三种读取数据的方式:Feeding、Reading from files和Preloaded data。Feeding是最常用也最有效的方法。即在模型(Graph)构建之前,先使用placeholder进行占位,但此时并没有训练数据,训练是通过feed_dict传入数据。 这里的 赋值的过程为: ii. 创建游戏及经验池 D经验池 D采用了队列的数据结构,是TensorFlow中最基础的数据结构,可以通过 变量创建完成之后,需要调用TensorFlow系统方法tf.global_variables_initializer()添加一个操作实现变量初始化。运行时机是在模型构建完成,Session建立之初。比如: iii. 参数保存及加载采用TensorFlow训练模型,需要将训练得到的参数进行保存,不然一关机,就一夜回到解放前了。TensorFlow采用Saver来保存。一般在Session()建立之前,通过 变量的恢复使用 在该Demo训练时,也采用了Saver进行参数保存。 首先加载CheckPointState文件,然后采用 iv. 实验及样本存储首先,根据ε 概率选择一个Action。 这里, 其次,执行选择的动作,并保存返回的状态、得分。 经验池 在下一训练过程中,更新当前状态及步数: 重复上述过程,实现反复实验及样本存储。 v. 通过梯度下降进行模型训练在实验一段时间后,经验池
以上。 |
|