基于policy的强化学习
一、actor的作用和设计
和以往的机器学习手段类似,强化学习的目的是为了学习一个“function”,这个“function”描述了agent对环境的观测(observation)和他采取的action之间的关系。即:action=f(observation),具体寻找这个“function”的步骤主要分为三部:
1.定义这个抽象的actor
比如我们可以使用神经网络来对这个抽象的“function”进行描述,在这种情况下,actor就指的是neural network。
我们以深度强化学习为例,在深度强化学习中,我们使用神经网络作为actor,那么我们就需要定义neural network的输入输出。具体而言,该神经网络的输入应该定义为对当前environment的observation,二网络的输出可以定义为具体的action,一个简单的例子如下:
2.定义actor的好坏
在讲述这个问题之前,我们首先要明确一下强化学习的训练过程,这里要引入一个“episode”的概念,它指的是进行了若干次循环的“get observation”->“take action”->"obtain reward"过程。对于一个actor而言,我们衡量其优劣是站在整个“episode”的角度出发的,而不是从每一次得到“reward”的角度进行优化的。一般,我们为了衡量某个actor性能的优劣,可以将整个“episode”内的reward进行累加,从而通过优化手段,使得模型能够在一段我们期望的操作时间内得到最优的reward。接下来我们推导整个“episode”中的reward函数。
我们设置整个episode内的操作记录为:
其中,
该episode内的reward记作:
对这个actor进行多次的训练之后,每一个episode都对应着一个reward,在总共的N个episode中,变量
的分布服从于概率,为了依据这个概率分布求得所有N次训练中的reward,我们求reward对该概率分布的期望,即:
3.选择最好的actor(对actor参数进行优化)
为了选择最好的actor,我们使用梯度上升的方法,对整体的reward进行最优化。具体而言,我们迭代的对网络参数进行更新:
其中,网络参数的梯度具体为:
正如上式所描述的,gradient ascent的原理十分简单,但是在程序中,为了求出这个梯度,我们还需进行一些处理,以便使用程序快速有效的进行运算,同时使我们对参数更新的过程有一个新的认识。接下来我们就对一个episode内的total reward进行化简。
从以上表达式中可以看到,整个梯度只和条件概率有关,我们接下来对这个条件概率的梯度进行推导。首先注意到操作记录的定义为:
接下来根据之前梯度表达式,我们对上式取对数,得到:
在原有的梯度表达式中,我们需要求出上式的梯度,在上式中只有第二项与网络参数有关,所以求了梯度之后,只剩下第二项,我们把上式代入之前的梯度表达式。
至此,我们就得到了一个episode中的reward的梯度。
在得到了梯度之后,我们将思路回到整个gradient ascent算法,与gradient ascent算法类似,整个网络参数的更新会使得目标函数沿着梯度最大的方向增加,然而目标函数的梯度变化至于其表达式中的“梯度目标项”有关,所以,随着网络参数的更新,“梯度目标项”也会不断增加。那么基于此,我们可以对整个梯度上升的过程产生一个定性的解释:
(1)当某一个episode内,reward函数为正时,网络参数的更新会使得增加,这一项增加就意味着更新后的网络参数会使得actor增加之后当出现状态时的,采取动作的概率。
(2)当某一个episode内,reward函数为负时,网络参数的更新会使得减小,这一项减小就意味着更新后的网络参数会使得actor减小之后当出现状态时的,采取动作的概率。
(1)question1:为什么使用微分关系对梯度表达式进行化简?
以上的解释无疑是十分符合一般逻辑的,因此,我们也从侧面印证了这种算法的正确性。
接下来,我们回到之前的式子,讨论为什么要使用那样一组微分关系对表达式进行化简,以及这么做能够带来什么好处。首先,我们已经知道,随着参数更新的过程,神经网络其实在调整所有的条件概率,对于那些使得reward为正的episode,神经网络会增加本次episode内所有操作的条件概率,反之当reward为负的情况,神经网络会将本次episode内所有操作的条件概率减少。从这个过程我们发现,其实可能会存在这样一种情况,对于某一特定的环境状态,在整个训练过程中出现了若干次,在这若干次对应的action中,存在着一个概率分布,有些action被采取的次数比较多,而有些action被采取的次数比较少,但是这些采取次数多的action所引起的“immediate reward”可能比那些采取次数少的action所引起的“immediate reward”少,对于这种情况,根据整个网络参数更新的规则,随着网络参数的不断更新,这两种action对应的条件概率可能都在增加,而且采取次数多但是“immediate reward”较小的那种action所对应的条件概率增加的会更多(因为梯度的增减其实就是依据“total reward”的正负,修改每个系统状态所对应的条件概率),然而这可能并不是一个最优的结果,为了解决这个问题,使用微分关系:,相当于对条件概率的梯度进行了归一化,从而避免了total reward因为这种情况从而没有达到全局最优:
(2)question2:当所有的total reward都为正会出现什么,怎么办?
在有些问题场景中,所有的action都对应着一个非负的reward,那么对于梯度更新算法来说,很明显,效果可能会受到一些影响。先观察理想情况:
在理想情况中,一个重要的假设就是**“对于每一个系统状态s,其对应的所有可能的action都会被采样到”**,那么经过若干次的参数更新之后,即便每一种action的reward都为正,最后采取每一种action的概率还是会按照每一种action对应的reward的相对关系进行变化。貌似这并不会造成什么影响。
但是在实际情况中,对于每一个系统状态s,其对应的所有action不一定都会被采样到。
比如,当状态s对应的action中,只有b和c被采样到,那么,根据参数更新的所造成的影响,b和c这两种action所对应的条件概率和会增加,那么显然,a对应的条件概率自然就会减少。然而这种情况是我们不想看到的。
为了解决这一问题,我们引入baseline:
通过这一措施,我们使得所有的reward有正有负,这样就不会盲目的增加所有的action对应的条件概率,从而导致的有些action由于没有被采样的从而其条件概率减少这种情况。
二、关于critic的作用和设计
在众多的强化学习模型中,“Actor-Critic”模型得到了许多研究者的青睐,它也作为一种常用的强化学习模型,在许多问题上取得了不错的效果。在“Actor-Critic”模型中,除了上文中所讲到的“Actor”,“Critic”也扮演者比较重要的角色。
(1)首先,critic没有办法决定所采取的action
(2)critic的作用是为了衡量一个给定的actor的优劣程度
1.基于neural network的reward estimator
一个基于neural network的reward estimator的critic可以表示为,这个意思就是,在给定的actor条件下,根据当前的observation,计算出当前episode的total reward的期望值。值得注意的是,针对于同样的observation,如果actor不同,那么使用critic得出的值也可能是不一样的。
那么现在就出现了一个问题,如何计算上述的这种critic:,常见的解决方法有两种:1.基于Monte-Carlo的方法;2.Temporal-difference的方法
(1)基于Monte-Carlo的方法
这种方法是建立在大量的实验基础上的,我们首先要明确,我们所建立的实际上是从observation到一个reward的期望之间的映射,所以我们可以采取以下的策略:
记录每次actor与环境进行交互的过程,每一次的observation和该批次的total reward作为用于训练critic的样本点,对神经网络进行训练,从而使得critic能够对之后的observation做出有价值的判断。
(2)Temporal-difference的方法
与上一个方法比较类似的是,这个方法同样是基于观察,但是,这个方法用于训练的样本点是每一次的observation和每一次的immediate reward,将这个immediate reward作为两个神经网络的差值进行训练,这两个神经网络只有输入不同,分别输入的是得到这个immediate reward 之前和之后的两次observation。
使用这个方法的优势在于,能够立即开始critic的训练,而不必等待一次episode结束,对于一些episode比较长的情况比较有优势。
2.基于Q-function的critic
这种critic可以写作,它能够根据当前时刻的observation和当前时刻actor所采取的action,得到一个对于累积的reward的估计。与之前的critic比较类似,只不过它的输入参数多了当前时刻的action
一个简单的Q-function实现方式如上图所示,当actor所采取的action是可以穷举的时候,我们可以使用Q-function对所有可能的action计算得到相应的accumulated reward,如下图所示.
使用Q-function我们可以实现一种新的强化学习方法——Q-learning,一个简单的Q-learning的结构如下图所示,其基本思路是,在一个actor与环境发生互动之后,Q-function通过观察整个互动,根据observation和相应的action得到一个结果,该结果可以用于衡量该actor的优劣程度,之后,我们在对actor进行进一步的优化调整。
在以上的这个过程中,一个十分重要的环节就是,如何根据现有的actor 和Q-function,得到一个更优的actor 呢?其实思路还是比较简单,我们首先定义这个“更优的”actor:
我们可以通过以下的表达式得到一个更优的actor的输出:
这个表达式的意义在于,一个更优的actor的输出是所有action中使得Q-function最大的那一个,这也就意味着,要想得到最优的actor,就必须要遍历所有可能的action,然而这只有在action为离散的情况下才能实现。其实“找到更优的actor”是通过直接给出“更优的actor”对应的输出实现的。
具体得到这样一个Q-function的方法,与上述的critic类似,可以使用MC或TD的方法得到。
那么常用的实现Q-learning的具体网络就是DQN,具体的网络架构以及基本概念可以参考文献:
《Rainbow: Combining Improvements in Deep Reinforcement Learning》,文献连接:https://www.aaai.org/ocs/index.php/AAAI/AAAI18/paper/viewPaper/17204
三、如何实现 policy gradient算法
实现policy gradient算法的核心是得到actor的参数。
首先,我们先回顾一下整个算法的流程:
对于一个给定的actor参数,我们训练若干个episode,得到若干个对应的操作记录,然后再利用这些操作记录,依据梯度更新公式对网络参数进行更新。这两个步骤是一个相互迭代进行的过程。
接下来,我们来具体分析如何在程序中实现上述的操作,为了清晰的理解这一点,我们先考虑一个最基本的分类问题。
在这样一个基本的分类问题中,我们需要给网络送入打好标签的数据,在神经网络接受了训练样本点之后,得到相应的网络输出,在经过onehot编码,得到了最终的action,我们将该样本点的标签记作,对于这样的网络,我们进行训练时,选取的loss function为交叉熵:
训练的过程实际就是减少交叉熵的过程,观察交叉熵的表达式,我们不难发现,减小交叉熵的同时,其实就是将对数项最大化,实际上,在用于分类的神经网络中,网络的输出一般是经过了softmax层,被归一化为了概率的结果。所以我们可以将理解作在给定当前输入(observation)条件下,采取各个action的概率,即:
我们要最大化上式,就可以采用对上式进行gradient ascent,来达到它的最大值,即:
至此,我们来分析之前的梯度表达式:
我们发现,当我们去掉中间的reward,得到了以下的表达式:
这个表达式中的梯度项就是之前的$logP(a_i|s)=logy_i $,也就是说,当我们将之前的梯度表达式中的reward项去掉之后,问题就退化成了一个基本的分类问题,而且该分类问题的每一个训练样本点就是一个操作记录中的每一个action与其对应的reward。
问题回到最初的如何实现policy gradient,我们可以得出一下的结论:只需要将每一次操作记录中的所有“observation-action”样本点都乘以这次episode中的reward,也就是一个权重。其实,在这个框架下,我们训练reinforcement learning的过程就比较清晰了:
(1)初始化网络参数
(2)使用当前网络参数在environment中进行一次交互,记作一个episode,得到一组“observation-action”,和当前episode对应的total reward
(3)进行此次训练,训练的过程就是将每一个“observation-action”作为一个样本点,对神经网络进行训练,只不过在训练的过程中,要在交叉熵的前面乘以当前episode的total reward
(4)反复进行步骤(2)、(3),完成训练# 基于policy的强化学习
一、actor的作用和设计
和以往的机器学习手段类似,强化学习的目的是为了学习一个“function”,这个“function”描述了agent对环境的观测(observation)和他采取的action之间的关系。即:action=f(observation),具体寻找这个“function”的步骤主要分为三部:
1.定义这个抽象的actor
比如我们可以使用神经网络来对这个抽象的“function”进行描述,在这种情况下,actor就指的是neural network。
我们以深度强化学习为例,在深度强化学习中,我们使用神经网络作为actor,那么我们就需要定义neural network的输入输出。具体而言,该神经网络的输入应该定义为对当前environment的observation,二网络的输出可以定义为具体的action,一个简单的例子如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wa1wrwCe-1596428175435)(F:\youdao_note\[email protected]\8437305fac71400ebaa75c598692049c\clipboard.png)]
2.定义actor的好坏
在讲述这个问题之前,我们首先要明确一下强化学习的训练过程,这里要引入一个“episode”的概念,它指的是进行了若干次循环的“get observation”->“take action”->"obtain reward"过程。对于一个actor而言,我们衡量其优劣是站在整个“episode”的角度出发的,而不是从每一次得到“reward”的角度进行优化的。一般,我们为了衡量某个actor性能的优劣,可以将整个“episode”内的reward进行累加,从而通过优化手段,使得模型能够在一段我们期望的操作时间内得到最优的reward。接下来我们推导整个“episode”中的reward函数。
我们设置整个episode内的操作记录为:
其中,
该episode内的reward记作:
对这个actor进行多次的训练之后,每一个episode都对应着一个reward,在总共的N个episode中,变量
的分布服从于概率,为了依据这个概率分布求得所有N次训练中的reward,我们求reward对该概率分布的期望,即:
3.选择最好的actor(对actor参数进行优化)
为了选择最好的actor,我们使用梯度上升的方法,对整体的reward进行最优化。具体而言,我们迭代的对网络参数进行更新:
其中,网络参数的梯度具体为:
正如上式所描述的,gradient ascent的原理十分简单,但是在程序中,为了求出这个梯度,我们还需进行一些处理,以便使用程序快速有效的进行运算,同时使我们对参数更新的过程有一个新的认识。接下来我们就对一个episode内的total reward进行化简。
从以上表达式中可以看到,整个梯度只和条件概率有关,我们接下来对这个条件概率的梯度进行推导。首先注意到操作记录的定义为:
接下来根据之前梯度表达式,我们对上式取对数,得到:
在原有的梯度表达式中,我们需要求出上式的梯度,在上式中只有第二项与网络参数有关,所以求了梯度之后,只剩下第二项,我们把上式代入之前的梯度表达式。
至此,我们就得到了一个episode中的reward的梯度。
在得到了梯度之后,我们将思路回到整个gradient ascent算法,与gradient ascent算法类似,整个网络参数的更新会使得目标函数沿着梯度最大的方向增加,然而目标函数的梯度变化至于其表达式中的“梯度目标项”有关,所以,随着网络参数的更新,“梯度目标项”也会不断增加。那么基于此,我们可以对整个梯度上升的过程产生一个定性的解释:
(1)当某一个episode内,reward函数为正时,网络参数的更新会使得增加,这一项增加就意味着更新后的网络参数会使得actor增加之后当出现状态时的,采取动作的概率。
(2)当某一个episode内,reward函数为负时,网络参数的更新会使得减小,这一项减小就意味着更新后的网络参数会使得actor减小之后当出现状态时的,采取动作的概率。
(1)question1:为什么使用微分关系对梯度表达式进行化简?
以上的解释无疑是十分符合一般逻辑的,因此,我们也从侧面印证了这种算法的正确性。
接下来,我们回到之前的式子,讨论为什么要使用那样一组微分关系对表达式进行化简,以及这么做能够带来什么好处。首先,我们已经知道,随着参数更新的过程,神经网络其实在调整所有的条件概率,对于那些使得reward为正的episode,神经网络会增加本次episode内所有操作的条件概率,反之当reward为负的情况,神经网络会将本次episode内所有操作的条件概率减少。从这个过程我们发现,其实可能会存在这样一种情况,对于某一特定的环境状态,在整个训练过程中出现了若干次,在这若干次对应的action中,存在着一个概率分布,有些action被采取的次数比较多,而有些action被采取的次数比较少,但是这些采取次数多的action所引起的“immediate reward”可能比那些采取次数少的action所引起的“immediate reward”少,对于这种情况,根据整个网络参数更新的规则,随着网络参数的不断更新,这两种action对应的条件概率可能都在增加,而且采取次数多但是“immediate reward”较小的那种action所对应的条件概率增加的会更多(因为梯度的增减其实就是依据“total reward”的正负,修改每个系统状态所对应的条件概率),然而这可能并不是一个最优的结果,为了解决这个问题,使用微分关系:,相当于对条件概率的梯度进行了归一化,从而避免了total reward因为这种情况从而没有达到全局最优:
(2)question2:当所有的total reward都为正会出现什么,怎么办?
在有些问题场景中,所有的action都对应着一个非负的reward,那么对于梯度更新算法来说,很明显,效果可能会受到一些影响。先观察理想情况:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UM3JkVv9-1596428175436)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200730211141954.png)]
在理想情况中,一个重要的假设就是**“对于每一个系统状态s,其对应的所有可能的action都会被采样到”**,那么经过若干次的参数更新之后,即便每一种action的reward都为正,最后采取每一种action的概率还是会按照每一种action对应的reward的相对关系进行变化。貌似这并不会造成什么影响。
但是在实际情况中,对于每一个系统状态s,其对应的所有action不一定都会被采样到。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1YmNrSBA-1596428175436)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200730211831670.png)]
比如,当状态s对应的action中,只有b和c被采样到,那么,根据参数更新的所造成的影响,b和c这两种action所对应的条件概率和会增加,那么显然,a对应的条件概率自然就会减少。然而这种情况是我们不想看到的。
为了解决这一问题,我们引入baseline:
通过这一措施,我们使得所有的reward有正有负,这样就不会盲目的增加所有的action对应的条件概率,从而导致的有些action由于没有被采样的从而其条件概率减少这种情况。
二、关于critic的作用和设计
在众多的强化学习模型中,“Actor-Critic”模型得到了许多研究者的青睐,它也作为一种常用的强化学习模型,在许多问题上取得了不错的效果。在“Actor-Critic”模型中,除了上文中所讲到的“Actor”,“Critic”也扮演者比较重要的角色。
(1)首先,critic没有办法决定所采取的action
(2)critic的作用是为了衡量一个给定的actor的优劣程度
1.基于neural network的reward estimator
一个基于neural network的reward estimator的critic可以表示为,这个意思就是,在给定的actor条件下,根据当前的observation,计算出当前episode的total reward的期望值。值得注意的是,针对于同样的observation,如果actor不同,那么使用critic得出的值也可能是不一样的。
那么现在就出现了一个问题,如何计算上述的这种critic:,常见的解决方法有两种:1.基于Monte-Carlo的方法;2.Temporal-difference的方法
(1)基于Monte-Carlo的方法
这种方法是建立在大量的实验基础上的,我们首先要明确,我们所建立的实际上是从observation到一个reward的期望之间的映射,所以我们可以采取以下的策略:
记录每次actor与环境进行交互的过程,每一次的observation和该批次的total reward作为用于训练critic的样本点,对神经网络进行训练,从而使得critic能够对之后的observation做出有价值的判断。
(2)Temporal-difference的方法
与上一个方法比较类似的是,这个方法同样是基于观察,但是,这个方法用于训练的样本点是每一次的observation和每一次的immediate reward,将这个immediate reward作为两个神经网络的差值进行训练,这两个神经网络只有输入不同,分别输入的是得到这个immediate reward 之前和之后的两次observation。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n9FMLcEZ-1596428175437)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200731160032155.png)]
使用这个方法的优势在于,能够立即开始critic的训练,而不必等待一次episode结束,对于一些episode比较长的情况比较有优势。
2.基于Q-function的critic
这种critic可以写作,它能够根据当前时刻的observation和当前时刻actor所采取的action,得到一个对于累积的reward的估计。与之前的critic比较类似,只不过它的输入参数多了当前时刻的action
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ynjIYgx-1596428175438)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200731161631227.png)]
一个简单的Q-function实现方式如上图所示,当actor所采取的action是可以穷举的时候,我们可以使用Q-function对所有可能的action计算得到相应的accumulated reward,如下图所示.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sn5XQsIc-1596428175439)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200731161914038.png)]
使用Q-function我们可以实现一种新的强化学习方法——Q-learning,一个简单的Q-learning的结构如下图所示,其基本思路是,在一个actor与环境发生互动之后,Q-function通过观察整个互动,根据observation和相应的action得到一个结果,该结果可以用于衡量该actor的优劣程度,之后,我们在对actor进行进一步的优化调整。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-INMcLHN5-1596428175440)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200731162713528.png)]
在以上的这个过程中,一个十分重要的环节就是,如何根据现有的actor 和Q-function,得到一个更优的actor 呢?其实思路还是比较简单,我们首先定义这个“更优的”actor:
我们可以通过以下的表达式得到一个更优的actor的输出:
这个表达式的意义在于,一个更优的actor的输出是所有action中使得Q-function最大的那一个,这也就意味着,要想得到最优的actor,就必须要遍历所有可能的action,然而这只有在action为离散的情况下才能实现。其实“找到更优的actor”是通过直接给出“更优的actor”对应的输出实现的。
具体得到这样一个Q-function的方法,与上述的critic类似,可以使用MC或TD的方法得到。
那么常用的实现Q-learning的具体网络就是DQN,具体的网络架构以及基本概念可以参考文献:
《Rainbow: Combining Improvements in Deep Reinforcement Learning》,文献连接:https://www.aaai.org/ocs/index.php/AAAI/AAAI18/paper/viewPaper/17204
三、如何实现 policy gradient算法
实现policy gradient算法的核心是得到actor的参数。
首先,我们先回顾一下整个算法的流程:
对于一个给定的actor参数,我们训练若干个episode,得到若干个对应的操作记录,然后再利用这些操作记录,依据梯度更新公式对网络参数进行更新。这两个步骤是一个相互迭代进行的过程。
接下来,我们来具体分析如何在程序中实现上述的操作,为了清晰的理解这一点,我们先考虑一个最基本的分类问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9eVKvXS3-1596428175441)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200731105257385.png)]
在这样一个基本的分类问题中,我们需要给网络送入打好标签的数据,在神经网络接受了训练样本点之后,得到相应的网络输出,在经过onehot编码,得到了最终的action,我们将该样本点的标签记作,对于这样的网络,我们进行训练时,选取的loss function为交叉熵:
训练的过程实际就是减少交叉熵的过程,观察交叉熵的表达式,我们不难发现,减小交叉熵的同时,其实就是将对数项最大化,实际上,在用于分类的神经网络中,网络的输出一般是经过了softmax层,被归一化为了概率的结果。所以我们可以将理解作在给定当前输入(observation)条件下,采取各个action的概率,即:
我们要最大化上式,就可以采用对上式进行gradient ascent,来达到它的最大值,即:
至此,我们来分析之前的梯度表达式:
我们发现,当我们去掉中间的reward,得到了以下的表达式:
这个表达式中的梯度项就是之前的$logP(a_i|s)=logy_i $,也就是说,当我们将之前的梯度表达式中的reward项去掉之后,问题就退化成了一个基本的分类问题,而且该分类问题的每一个训练样本点就是一个操作记录中的每一个action与其对应的reward。
问题回到最初的如何实现policy gradient,我们可以得出一下的结论:只需要将每一次操作记录中的所有“observation-action”样本点都乘以这次episode中的reward,也就是一个权重。其实,在这个框架下,我们训练reinforcement learning的过程就比较清晰了:
(1)初始化网络参数
(2)使用当前网络参数在environment中进行一次交互,记作一个episode,得到一组“observation-action”,和当前episode对应的total reward
(3)进行此次训练,训练的过程就是将每一个“observation-action”作为一个样本点,对神经网络进行训练,只不过在训练的过程中,要在交叉熵的前面乘以当前episode的total reward
(4)反复进行步骤(2)、(3),完成训练