本节内容:

1、POMDP;

2、Policy search算法:reinforced和Pegasus;


马尔科夫决策过程(Partially Observable Markov Decision Process,缩写:POMDP)

简介:

马尔科夫过程的预测:



以下转自:http://www.cnblogs.com/jinxulin/p/3517377.html?utm_source=tuicool

1. 马尔可夫模型的几类子模型

大家应该还记得马尔科夫链(Markov Chain),了解机器学习的也都知道隐马尔可夫模型(Hidden Markov Model,HMM)。它们具有的一个共同性质就是马尔可夫性(无后效性),也就是指系统的下个状态只与当前状态信息有关,而与更早之前的状态无关。

马尔可夫决策过程(Markov Decision Process, MDP)也具有马尔可夫性,与上面不同的是MDP考虑了动作,即系统下个状态不仅和当前的状态有关,也和当前采取的动作有关。还是举下棋的例子,当我们在某个局面(状态s)走了一步(动作a),这时对手的选择(导致下个状态s’)我们是不能确定的,但是他的选择只和s和a有关,而不用考虑更早之前的状态和动作,即s’是根据s和a随机生成的。

我们用一个二维表格表示一下,各种马尔可夫子模型的关系就很清楚了:

不考虑动作
考虑动作

状态完全可见
马尔科夫链(MC)
马尔可夫决策过程(MDP)

状态不完全可见
隐马尔可夫模型(HMM)
不完全可观察马尔可夫决策过程(POMDP)

2. 马尔可夫决策过程

一个马尔可夫决策过程由一个四元组构成M = (S, A, Psa, ) [1]

· S: 表示状态集(states),有s∈S,si表示第i步的状态。

· A:表示一组动作(actions),有a∈A,ai表示第i步的动作。

· sa: 表示状态转移概率。s 表示的是在当前s ∈ S状态下,经过a ∈ A作用后,会转移到的其他状态的概率分布情况。比如,在状态s下执行动作a,转移到s'的概率可以表示为p(s'|s,a)。

· R: S×A⟼ℝ ,R是回报函数(reward function)。有些回报函数状态S的函数,可以简化为R: S⟼ℝ。如果一组(s,a)转移到了下个状态s',那么回报函数可记为r(s'|s, a)。如果(s,a)对应的下个状态s'是唯一的,那么回报函数也可以记为r(s,a)。

MDP 的动态过程如下:某个智能体(agent)的初始状态为s0,然后从 A 中挑选一个动作a0执行,执行后,agent 按Psa概率随机转移到了下一个s1状态,s1∈ Ps0a0。然后再执行一个动作a1,就转移到了s2,接下来再执行a2…,我们可以用下面的图表示状态转移的过程。

如果回报r是根据状态s和动作a得到的,则MDP还可以表示成下图:

3. 值函数(value function)

上篇我们提到增强学习学到的是一个从环境状态到动作的映射(即行为策略),记为策略π: S→A。而增强学习往往又具有延迟回报的特点: 如果在第n步输掉了棋,那么只有状态sn和动作an获得了立即回报r(sn,an)=-1,前面的所有状态立即回报均为0。所以对于之前的任意状态s和动作a,立即回报函数r(s,a)无法说明策略的好坏。因而需要定义值函数(value function,又叫效用函数)来表明当前状态下策略π的长期影响。

用Vπ(s)表示策略π下,状态s的值函数。ri表示未来第i步的立即回报,常见的值函数有以下三种:

a)

b)

c)

其中:

a)是采用策略π的情况下未来有限h步的期望立即回报总和;

b)是采用策略π的情况下期望的平均回报;

c)是值函数最常见的形式,式中γ∈[0,1]称为折合因子,表明了未来的回报相对于当前回报的重要程度。特别的,γ=0时,相当于只考虑立即不考虑长期回报,γ=1时,将长期回报和立即回报看得同等重要。接下来我们只讨论第三种形式,

现在将值函数的第三种形式展开,其中ri表示未来第i步回报,s'表示下一步状态,则有:

给定策略π和初始状态s,则动作a=π(s),下个时刻将以概率p(s'|s,a)转向下个状态s',那么上式的期望可以拆开,可以重写为:

上面提到的值函数称为状态值函数(state value function),需要注意的是,在Vπ(s)中,π和初始状态s是我们给定的,而初始动作a是由策略π和状态s决定的,即a=π(s)。

定义动作值函数(action value functionQ函数)如下:

给定当前状态s和当前动作a,在未来遵循策略π,那么系统将以概率p(s'|s,a)转向下个状态s',上式可以重写为:

在Qπ(s,a)中,不仅策略π和初始状态s是我们给定的,当前的动作a也是我们给定的,这是Qπ(s,a)和Vπ(a)的主要区别。

知道值函数的概念后,一个MDP的最优策略可以由下式表示:

即我们寻找的是在任意初始条件s下,能够最大化值函数的策略π*。

4. 值函数与Q函数计算的例子

上面的概念可能描述得不够清晰,接下来我们实际计算一下,如图所示是一个格子世界,我们假设agent从左下角的start点出发,右上角为目标位置,称为吸收状态(Absorbing state),对于进入吸收态的动作,我们给予立即回报100,对其他动作则给予0回报,折合因子γ的值我们选择0.9。

为了方便描述,记第i行,第j列的状态为sij, 在每个状态,有四种上下左右四种可选的动作,分别记为au,ad,al,ar。(up,down,left,right首字母),并认为状态按动作a选择的方向转移的概率为1。

1.由于状态转移概率是1,每组(s,a)对应了唯一的s'。回报函数r(s'|s,a)可以简记为r(s,a)

如下所示,每个格子代表一个状态s,箭头则代表动作a,旁边的数字代表立即回报,可以看到只有进入目标位置的动作获得了回报100,其他动作都获得了0回报。 即r(s12,ar) = r(s23,au) =100。

2. 一个策略π如图所示:

3. 值函数Vπ(s)如下所示

根据Vπ的表达式,立即回报,和策略π,有

Vπ(s12) = r(s12,ar) = r(s13|s12,ar) = 100

Vπ(s11)= r(s11,ar)+γ*Vπ(s12) = 0+0.9*100 = 90

Vπ(s23) = r(s23,au) = 100

Vπ(s22)= r(s22,ar)+γ*Vπ(s23) = 90

Vπ(s21)= r(s21,ar)+γ*Vπ(s22) = 81

4. Q(s,a)值如下所示

有了策略π和立即回报函数r(s,a), Qπ(s,a)如何得到的呢?

对s11计算Q函数(用到了上面Vπ的结果)如下:

Qπ(s11,ar)=r(s11,ar)+ γ *Vπ(s12) =0+0.9*100 = 90

Qπ(s11,ad)=r(s11,ad)+ γ *Vπ(s21) = 72

至此我们了解了马尔可夫决策过程的基本概念,知道了增强学习的目标(获得任意初始条件下,使Vπ值最大的策略π*),下一篇开始介绍求解最优策略的方法。

PS:发现写东西还是蛮辛苦的,希望对大家有帮助。另外自己也比较菜,没写对的地方欢迎指出~~

[注]采用折合因子作为值函数的MDP也可以定义为五元组M=(S, A, P, γ, R)。也有的书上把值函数作为一个因子定义五元组。还有定义为三元组的,不过MDP的基本组成元素是不变的。

参考资料:

[1] R.Sutton et al. Reinforcement learning: An introduction , 1998

[2] T.Mitchell. 《机器学习》,2003

[3] 金卓军,逆向增强学习和示教学习算法研究及其在智能机器人中的应用[D],2011

[4] Oliver Sigaud et al,Markov Decision Process in Artificial Intelligence[M], 2010


上一篇我们已经说到了,增强学习的目的就是求解马尔可夫决策过程(MDP)的最优策略,使其在任意初始状态下,都能获得最大的Vπ值。(本文不考虑非马尔可夫环境和不完全可观测马尔可夫决策过程(POMDP)中的增强学习)。

那么如何求解最优策略呢?基本的解法有三种:

动态规划法(dynamic programming methods)

蒙特卡罗方法(Monte Carlo methods)

时间差分法(temporal difference)。

动态规划法是其中最基本的算法,也是理解后续算法的基础,因此本文先介绍动态规划法求解MDP。本文假设拥有MDP模型M=(S, A, Psa, R)的完整知识。

1. 贝尔曼方程(Bellman Equation)

上一篇我们得到了Vπ和Qπ的表达式,并且写成了如下的形式

在动态规划中,上面两个式子称为贝尔曼方程,它表明了当前状态的值函数与下个状态的值函数的关系。

优化目标π*可以表示为:

分别记最优策略π*对应的状态值函数和行为值函数为V*(s)和Q*(s, a),由它们的定义容易知道,V*(s)和Q*(s, a)存在如下关系:

状态值函数和行为值函数分别满足如下贝尔曼最优性方程(Bellman optimality equation):

有了贝尔曼方程和贝尔曼最优性方程后,我们就可以用动态规划来求解MDP了。

2. 策略估计(Policy Evaluation)

首先,对于任意的策略π,我们如何计算其状态值函数Vπ(s)?这个问题被称作策略估计,

前面讲到对于确定性策略,值函数

现在扩展到更一般的情况,如果在某策略π下,π(s)对应的动作a有多种可能,每种可能记为π(a|s),则状态值函数定义如下:

一般采用迭代的方法更新状态值函数,首先将所有Vπ(s)的初值赋为0(其他状态也可以赋为任意值,不过吸收态必须赋0值),然后采用如下式子更新所有状态s的值函数(第k+1次迭代):

对于Vπ(s),有两种更新方法,

第一种:将第k次迭代的各状态值函数[Vk(s1),Vk(s2),Vk(s3)..]保存在一个数组中,第k+1次的Vπ(s)采用第k次的Vπ(s')来计算,并将结果保存在第二个数组中。

第二种:即仅用一个数组保存各状态值函数,每当得到一个新值,就将旧的值覆盖,形如[Vk+1(s1),Vk+1(s2),Vk(s3)..],第k+1次迭代的Vπ(s)可能用到第k+1次迭代得到的Vπ(s')。

通常情况下,我们采用第二种方法更新数据,因为它及时利用了新值,能更快的收敛。整个策略估计算法如下图所示:

3. 策略改进(Policy Improvement)

上一节中进行策略估计的目的,是为了寻找更好的策略,这个过程叫做策略改进(Policy Improvement)。

假设我们有一个策略π,并且确定了它的所有状态的值函数Vπ(s)。对于某状态s,有动作a0=π(s)。 那么如果我们在状态s下不采用动作a0,而采用其他动作a≠π(s)是否会更好呢?要判断好坏就需要我们计算行为值函数Qπ(s,a),公式我们前面已经说过:

评判标准是:Qπ(s,a)是否大于Vπ(s)。如果Qπ(s,a)> Vπ(s),那么至少说明新策略【仅在状态s下采用动作a,其他状态下遵循策略π】比旧策略【所有状态下都遵循策略π】整体上要更好。

策略改进定理(policy improvement theorem):π和π'是两个确定的策略,如果对所有状态s∈S有Qπ(s,π'(s))≥Vπ(s),那么策略π'必然比策略π更好,或者至少一样好。其中的不等式等价于Vπ'(s)≥Vπ(s)。

有了在某状态s上改进策略的方法和策略改进定理,我们可以遍历所有状态和所有可能的动作a,并采用贪心策略来获得新策略π'。即对所有的s∈S, 采用下式更新策略:

这种采用关于值函数的贪心策略获得新策略,改进旧策略的过程,称为策略改进(Policy Improvement)

最后大家可能会疑惑,贪心策略能否收敛到最优策略,这里我们假设策略改进过程已经收敛,即对所有的s,Vπ'(s)等于Vπ(s)。那么根据上面的策略更新的式子,可以知道对于所有的s∈S下式成立:

可是这个式子正好就是我们在1中所说的Bellman optimality equation,所以π和π'都必然是最优策略!神奇吧!

4. 策略迭代(Policy Iteration)

策略迭代算法就是上面两节内容的组合。假设我们有一个策略π,那么我们可以用policy evaluation获得它的值函数Vπ(s),然后根据policy improvement得到更好的策略π',接着再计算Vπ'(s),再获得更好的策略π'',整个过程顺序进行如下图所示:

完整的算法如下图所示:

5. 值迭代(Value Iteration)

从上面我们可以看到,策略迭代算法包含了一个策略估计的过程,而策略估计则需要扫描(sweep)所有的状态若干次,其中巨大的计算量直接影响了策略迭代算法的效率。我们必须要获得精确的Vπ值吗?事实上不必,有几种方法可以在保证算法收敛的情况下,缩短策略估计的过程。

值迭代(Value Iteration)就是其中非常重要的一种。它的每次迭代只扫描(sweep)了每个状态一次。值迭代的每次迭代对所有的s∈S按照下列公式更新:

即在值迭代的第k+1次迭代时,直接将能获得的最大的Vπ(s)值赋给Vk+1。值迭代算法直接用可能转到的下一步s'的V(s')来更新当前的V(s),算法甚至都不需要存储策略π。而实际上这种更新方式同时却改变了策略πk和V(s)的估值Vk(s)。 直到算法结束后,我们再通过V值来获得最优的π。

此外,值迭代还可以理解成是采用迭代的方式逼近1中所示的贝尔曼最优方程。

值迭代完整的算法如图所示:

由上面的算法可知,值迭代的最后一步,我们才根据V*(s),获得最优策略π*。

一般来说值迭代和策略迭代都需要经过无数轮迭代才能精确的收敛到V*和π*, 而实践中,我们往往设定一个阈值来作为中止条件,即当Vπ(s)值改变很小时,我们就近似的认为获得了最优策略。在折扣回报的有限MDP(discounted finite MDPs)中,进过有限次迭代,两种算法都能收敛到最优策略π*。

至此我们了解了马尔可夫决策过程的动态规划解法,动态规划的优点在于它有很好的数学上的解释,但是动态要求一个完全已知的环境模型,这在现实中是很难做到的。另外,当状态数量较大的时候,动态规划法的效率也将是一个问题。下一篇介绍蒙特卡罗方法,它的优点在于不需要完整的环境模型。


1. 蒙特卡罗方法的基本思想

蒙特卡罗方法又叫统计模拟方法,它使用随机数(或伪随机数)来解决计算的问题,是一类重要的数值计算方法。该方法的名字来源于世界著名的赌城蒙特卡罗,而蒙特卡罗方法正是以概率为基础的方法。

一个简单的例子可以解释蒙特卡罗方法,假设我们需要计算一个不规则图形的面积,那么图形的不规则程度和分析性计算(比如积分)的复杂程度是成正比的。而采用蒙特卡罗方法是怎么计算的呢?首先你把图形放到一个已知面积的方框内,然后假想你有一些豆子,把豆子均匀地朝这个方框内撒,散好后数这个图形之中有多少颗豆子,再根据图形内外豆子的比例来计算面积。当你的豆子越小,撒的越多的时候,结果就越精确。

2. 增强学习中的蒙特卡罗方法

现在我们开始讲解增强学习中的蒙特卡罗方法,与上篇的DP不同的是,这里不需要对环境的完整知识。蒙特卡罗方法仅仅需要经验就可以求解最优策略,这些经验可以在线获得或者根据某种模拟机制获得。

要注意的是,我们仅将蒙特卡罗方法定义在episode task上,所谓的episode task就是指不管采取哪种策略π,都会在有限时间内到达终止状态并获得回报的任务。比如玩棋类游戏,在有限步数以后总能达到输赢或者平局的结果并获得相应回报。

那么什么是经验呢?经验其实就是训练样本。比如在初始状态s,遵循策略π,最终获得了总回报R,这就是一个样本。如果我们有许多这样的样本,就可以估计在状态s下,遵循策略π的期望回报,也就是状态值函数Vπ(s)了。蒙特卡罗方法就是依靠样本的平均回报来解决增强学习问题的。

尽管蒙特卡罗方法和动态规划方法存在诸多不同,但是蒙特卡罗方法借鉴了很多动态规划中的思想。在动态规划中我们首先进行策略估计,计算特定策略π对应的Vπ和Qπ,然后进行策略改进,最终形成策略迭代。这些想法同样在蒙特卡罗方法中应用。

3. 蒙特卡罗策略估计(Monte Carlo Policy evalution)

首先考虑用蒙特卡罗方法来学习状态值函数Vπ(s)。如上所述,估计Vπ(s)的一个明显的方法是对于所有到达过该状态的回报取平均值。这里又分为first-visit MC methods和every-visit MC methods。这里,我们只考虑first MC methods,即在一个episode内,我们只记录s的第一次访问,并对它取平均回报。

现在我们假设有如下一些样本,取折扣因子γ=1,即直接计算累积回报,则有

根据first MC methods,对出现过状态s的episode的累积回报取均值,有Vπ(s)≈ (2 + 1 – 5 + 4)/4 = 0.5

容易知道,当我们经过无穷多的episode后,Vπ(s)的估计值将收敛于其真实值。

4. 动作值函数的MC估计(Mote Carlo Estimation of Action Values)

在状态转移概率p(s'|a,s)已知的情况下,策略估计后有了新的值函数,我们就可以进行策略改进了,只需要看哪个动作能获得最大的期望累积回报就可以。然而在没有准确的状态转移概率的情况下这是不可行的。为此,我们需要估计动作值函数Qπ(s,a)。Qπ(s,a)的估计方法前面类似,即在状态s下采用动作a,后续遵循策略π获得的期望累积回报即为Qπ(s,a),依然用平均回报来估计它。有了Q值,就可以进行策略改进了

5. 持续探索(Maintaining Exploration)

下面我们来探讨一下Maintaining Exploration的问题。前面我们讲到,我们通过一些样本来估计Q和V,并且在未来执行估值最大的动作。这里就存在一个问题,假设在某个确定状态s0下,能执行a0, a1, a2这三个动作,如果智能体已经估计了两个Q函数值,如Q(s0,a0), Q(s0,a1),且Q(s0,a0)>Q(s0,a1),那么它在未来将只会执行一个确定的动作a0。这样我们就无法更新Q(s0,a1)的估值和获得Q(s0,a2)的估值了。这样的后果是,我们无法保证Q(s0,a0)就是s0下最大的Q函数。

Maintaining Exploration的思想很简单,就是用soft policies来替换确定性策略,使所有的动作都有可能被执行。比如其中的一种方法是ε-greedy policy,即在所有的状态下,用1-ε的概率来执行当前的最优动作a0,ε的概率来执行其他动作a1, a2。这样我们就可以获得所有动作的估计值,然后通过慢慢减少ε值,最终使算法收敛,并得到最优策略。简单起见,在下面MC控制中,我们使用exploring start,即仅在第一步令所有的a都有一个非零的概率被选中。

6. 蒙特卡罗控制(Mote Carlo Control)

我们看下MC版本的策略迭代过程:

根据前面的说法,值函数Qπ(s,a)的估计值需要在无穷多episode后才能收敛到其真实值。这样的话策略迭代必然是低效的。在上一篇DP中,我们了值迭代算法,即每次都不用完整的策略估计,而仅仅使用值函数的近似值进行迭代,这里也用到了类似的思想。每次策略的近似值,然后用这个近似值来更新得到一个近似的策略,并最终收敛到最优策略。这个思想称为广义策略迭代。

具体到MC control,就是在每个episode后都重新估计下动作值函数(尽管不是真实值),然后根据近似的动作值函数,进行策略更新。这是一个episode by episode的过程。

一个采用exploring starts的Monte Carlo control算法,如下图所示,称为Monte Carlo ES。而对于所有状态都采用soft policy的版本,这里不再讨论。

7. 小结

Monte Carlo方法的一个显而易见的好处就是我们不需要环境模型了,可以从经验中直接学到策略。它的另一个好处是,它对所有状态s的估计都是独立的,而不依赖与其他状态的值函数。在很多时候,我们不需要对所有状态值进行估计,这种情况下蒙特卡罗方法就十分适用。

不过,现在增强学习中,直接使用MC方法的情况比较少,而较多的采用TD算法族。但是如同DP一样,MC方法也是增强学习的基础之一,因此依然有学习的必要。

【cs229-Lecture20】策略搜索的更多相关文章

  1. hibernate框架学习第六天:QBC、分页查询、投影、数据加载策略、二级缓存

    QBC查询 1.简单查询 Criteria c = s.createCriteria(TeacherModel.class); 2.获取查询结果 多条:list 单挑:uniqueResult 3.分 ...

  2. 回溯算法 DFS深度优先搜索 (递归与非递归实现)

    回溯法是一种选优搜索法(试探法),被称为通用的解题方法,这种方法适用于解一些组合数相当大的问题.通过剪枝(约束+限界)可以大幅减少解决问题的计算量(搜索量). 基本思想 将n元问题P的状态空间E表示成 ...

  3. 深度|OpenAI 首批研究成果聚焦无监督学习,生成模型如何高效的理解世界(附论文)

    本文经机器之心(微信公众号:almosthuman2014)授权转载,禁止二次转载,原文. 选自 Open AI 作者:ANDREJ KARPATHY, PIETER ABBEEL, GREG BRO ...

  4. (转)【重磅】无监督学习生成式对抗网络突破,OpenAI 5大项目落地

    [重磅]无监督学习生成式对抗网络突破,OpenAI 5大项目落地 [新智元导读]"生成对抗网络是切片面包发明以来最令人激动的事情!"LeCun前不久在Quroa答问时毫不加掩饰对生 ...

  5. LeetCode OJ 78. Subsets

    Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subset must ...

  6. JavaScript中国象棋程序(8) - 进一步优化

    在这最后一节,我们的主要工作是使用开局库.对根节点的搜索分离出来.以及引入PVS(Principal Variation Search,)主要变例搜索. 8.1.开局库 这一节我们引入book.js文 ...

  7. 静态频繁子图挖掘算法用于动态网络——gSpan算法研究

    摘要 随着信息技术的不断发展,人类可以很容易地收集和储存大量的数据,然而,如何在海量的数据中提取对用户有用的信息逐渐地成为巨大挑战.为了应对这种挑战,数据挖掘技术应运而生,成为了最近一段时期数据科学的 ...

  8. CS:APP3e 深入理解计算机系统_3e MallocLab实验

    详细的题目要求和资源可以到 http://csapp.cs.cmu.edu/3e/labs.html 或者 http://www.cs.cmu.edu/~./213/schedule.html 获取. ...

  9. ICML 2018 | 从强化学习到生成模型:40篇值得一读的论文

    https://blog.csdn.net/y80gDg1/article/details/81463731 感谢阅读腾讯AI Lab微信号第34篇文章.当地时间 7 月 10-15 日,第 35 届 ...

随机推荐

  1. (转)雅虎WEB前端网站优化 -- 34条军规

    雅虎给出了优化网站加载速度的34条法则(包括Yslow规则22条) 详细说明,下载转发 ponytail 的译文(来自帕兰映像). 1.Minimize HTTP Requests 减少HTTP请求 ...

  2. atitit.web ui 结构建模工具总结

    atitit.web ui 结构建模工具总结 1. 王者.dreamweaver 1 2. Frontpage/SharePoint Designer(FrontPage) 2010... 1 3.  ...

  3. paip.提升效率--调试--日志系统日志参数含义---python

    paip.提升效率--调试--日志系统日志参数含义---python #同时向控制台和文件输出日志 #日志参数含义 import logging log_format = '%(filename)s ...

  4. SQL Server 之 GROUP BY、GROUPING SETS、ROLLUP、CUBE

    1.创建表 Staff CREATE TABLE [dbo].[Staff]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Money] [int] NULL, [Cr ...

  5. Leetcode 219 Contains Duplicate II STL

    找出是否存在nums[i]==nums[j],使得 j - i <=k 这是map的一个应用 class Solution { public: bool containsNearbyDuplic ...

  6. 安卓开发, 遇到WebView不能加载静态网页, WebView显示 "net::ERR_PROXY_CONNECTON_FAILED"

    http://blog.csdn.net/zhouchangshi/article/details/44454695 Android开发中遇到网络连接问题, 要找WebView中显示一个静态的网页, ...

  7. eclipse web项目转maven项目

    ps:好久没写博客了,工作了人就懒了,加油加油,up,up 1 eclipse web项目目录 /web app src com.xx.xx *.properties *.xml WebRoot ​W ...

  8. [转]如果我有jQuery背景,我应该如何切换到AngularJS的思维模式?

    导言 stackoverflow上有一个人问了一个问题:如果我有jQuery背景,我应该如何切换到AngularJS的思维模式? 有一个回复非常经典,获得了两千多票. 为了让国内开发者也能领略到其中的 ...

  9. TStringList中AddObject使用

    结构体定义 PYpType=^TYpType; TYpType=record    yfcode:string;    ypcode:string;    YpUnitPrice:Currency;  ...

  10. 【ASP.NET MVC 5】第27章 Web API与单页应用程序

    注:<精通ASP.NET MVC 3框架>受到了出版社和广大读者的充分肯定,这让本人深感欣慰.目前该书的第4版不日即将出版,现在又已开始第5版的翻译,这里先贴出该书的最后一章译稿,仅供大家 ...