0


【强化学习】策略梯度算法中的损失函数

策略梯度算法推导

适合有一定强化学习算法基础的人看。

首先我们定义一条轨迹的奖励,有两种定义方法:
无折扣有限步奖励

  1. R
  2. (
  3. τ
  4. )
  5. =
  6. t
  7. =
  8. 0
  9. T
  10. r
  11. t
  12. .
  13. R(\tau) = \sum_{t=0}^T r_t.
  14. R(τ)=t=0Trt​.

折扣无限步奖励

  1. R
  2. (
  3. τ
  4. )
  5. =
  6. t
  7. =
  8. 0
  9. γ
  10. t
  11. r
  12. t
  13. .
  14. R(\tau) = \sum_{t=0}^{\infty} \gamma^t r_t.
  15. R(τ)=t=0∑∞​γtrt​.

以上两种轨迹奖励的求和方法第一种比较简单,第二种比较合适。而且一般对于第二种也是有限的,比如游戏必然会终结的。

智能体和环境交互的过程中,产生的轨迹有很多条,强化学习的目标是使得奖励的期望值最大:
假设在环境中智能体以策略

  1. π
  2. \pi
  3. π和环境进行交互产生一条轨迹
  4. τ
  5. \tau
  6. τ的概率:
  7. P
  8. (
  9. τ
  10. π
  11. )
  12. =
  13. ρ
  14. 0
  15. (
  16. s
  17. 0
  18. )
  19. t
  20. =
  21. 0
  22. T
  23. 1
  24. P
  25. (
  26. s
  27. t
  28. +
  29. 1
  30. s
  31. t
  32. ,
  33. a
  34. t
  35. )
  36. π
  37. (
  38. a
  39. t
  40. s
  41. t
  42. )
  43. .
  44. P(\tau|\pi) = \rho_0 (s_0) \prod_{t=0}^{T-1} P(s_{t+1} | s_t, a_t) \pi(a_t | s_t).
  45. P(τ∣π)=ρ0​(s0​)t=0T1P(st+1​∣st​,at​)π(at​∣st​).

其中

  1. ρ
  2. 0
  3. (
  4. s
  5. 0
  6. )
  7. \rho_0(s_0)
  8. ρ0​(s0​)是初始状态出现的概率,
  9. P
  10. (
  11. s
  12. t
  13. +
  14. 1
  15. s
  16. t
  17. ,
  18. a
  19. t
  20. )
  21. P(s_{t+1}|s_t,a_t)
  22. P(st+1​∣st​,at​)是状态转移的概率,
  23. π
  24. (
  25. a
  26. t
  27. s
  28. t
  29. )
  30. \pi(a_t|s_t)
  31. π(at​∣st​)是策略,也就是在状态
  32. s
  33. t
  34. s_t
  35. st​下采取动作
  36. a
  37. t
  38. a_t
  39. at​的概率。

那么目标就是最大化期望奖励:

  1. J
  2. (
  3. π
  4. )
  5. =
  6. τ
  7. P
  8. (
  9. τ
  10. π
  11. )
  12. R
  13. (
  14. τ
  15. )
  16. =
  17. E
  18. τ
  19. π
  20. [
  21. R
  22. (
  23. τ
  24. )
  25. ]
  26. .
  27. J(\pi) = \int_{\tau} P(\tau|\pi) R(\tau) = \mathop{E}\limits_{\tau\sim\pi}[{R(\tau)}].
  28. J(π)=∫τ​P(τ∣π)R(τ)=τ∼πE​[R(τ)].

强化学习的核心问题是找到一个策略,使得目标函数值最大:

  1. π
  2. =
  3. arg
  4. max
  5. π
  6. J
  7. (
  8. π
  9. )
  10. \pi^* = \arg \max_{\pi} J(\pi)
  11. π∗=argπmaxJ(π)

目标函数的自变量是

  1. π
  2. \pi
  3. π,如果我们用某种方法来表示策略,参数是
  4. θ
  5. \theta
  6. θ,那么上面的式子就可以写成:
  7. J
  8. (
  9. π
  10. θ
  11. )
  12. =
  13. τ
  14. P
  15. (
  16. τ
  17. π
  18. θ
  19. )
  20. R
  21. (
  22. τ
  23. )
  24. =
  25. E
  26. τ
  27. π
  28. θ
  29. [
  30. R
  31. (
  32. τ
  33. )
  34. ]
  35. .
  36. J(\pi_\theta) = \int_{\tau} P(\tau|\pi_\theta) R(\tau) = \mathop{E}\limits_{\tau\sim\pi_\theta}[{R(\tau)}].
  37. J(πθ​)=∫τ​P(τ∣πθ​)R(τ)=τ∼πθ​E​[R(τ)].

最终目标函数是和参数

  1. θ
  2. \theta
  3. θ有关的,我们只需要更新参数
  4. θ
  5. \theta
  6. θ就能影响策略
  7. π
  8. (
  9. a
  10. s
  11. )
  12. \pi(a|s)
  13. π(as)的分布,进而影响
  14. J
  15. (
  16. π
  17. θ
  18. )
  19. J(\pi_\theta)
  20. J(πθ​).

接下来我们求目标函数的梯度
1.轨迹

  1. τ
  2. \tau
  3. τ出现的概率
  4. P
  5. (
  6. τ
  7. θ
  8. )
  9. =
  10. ρ
  11. 0
  12. (
  13. s
  14. 0
  15. )
  16. t
  17. =
  18. 0
  19. T
  20. P
  21. (
  22. s
  23. t
  24. +
  25. 1
  26. s
  27. t
  28. ,
  29. a
  30. t
  31. )
  32. π
  33. θ
  34. (
  35. a
  36. t
  37. s
  38. t
  39. )
  40. .
  41. P(\tau|\theta) = \rho_0 (s_0) \prod_{t=0}^{T} P(s_{t+1}|s_t, a_t) \pi_{\theta}(a_t |s_t).
  42. P(τ∣θ)=ρ0​(s0​)t=0TP(st+1​∣st​,at​)πθ​(at​∣st​).

2.对轨迹的概率

  1. P
  2. (
  3. τ
  4. θ
  5. )
  6. P(\tau|\theta)
  7. P(τ∣θ)求关于
  8. θ
  9. \theta
  10. θ的导数

这里用到了一个技巧后面会用到

  1. θ
  2. P
  3. (
  4. τ
  5. θ
  6. )
  7. =
  8. P
  9. (
  10. τ
  11. θ
  12. )
  13. θ
  14. log
  15. P
  16. (
  17. τ
  18. θ
  19. )
  20. .
  21. \nabla_{\theta} P(\tau | \theta) = P(\tau | \theta) \nabla_{\theta} \log P(\tau | \theta).
  22. ∇θ​P(τ∣θ)=P(τ∣θ)∇θ​logP(τ∣θ).
  23. log
  24. P
  25. (
  26. τ
  27. θ
  28. )
  29. \log P(\tau|\theta)
  30. logP(τ∣θ)可以展开为
  31. log
  32. P
  33. (
  34. τ
  35. θ
  36. )
  37. =
  38. log
  39. ρ
  40. 0
  41. (
  42. s
  43. 0
  44. )
  45. +
  46. t
  47. =
  48. 0
  49. T
  50. (
  51. log
  52. P
  53. (
  54. s
  55. t
  56. +
  57. 1
  58. s
  59. t
  60. ,
  61. a
  62. t
  63. )
  64. +
  65. log
  66. π
  67. θ
  68. (
  69. a
  70. t
  71. s
  72. t
  73. )
  74. )
  75. .
  76. \log P(\tau|\theta) = \log \rho_0 (s_0) + \sum_{t=0}^{T} \bigg( \log P(s_{t+1}|s_t, a_t) + \log \pi_{\theta}(a_t |s_t)\bigg).
  77. logP(τ∣θ)=logρ0​(s0​)+t=0T​(logP(st+1​∣st​,at​)+logπθ​(at​∣st​)).

第一项和第二项都和参数无关,只有最后一项和参数有关:

  1. θ
  2. l
  3. o
  4. g
  5. P
  6. (
  7. τ
  8. θ
  9. )
  10. =
  11. t
  12. =
  13. 0
  14. T
  15. θ
  16. l
  17. o
  18. g
  19. π
  20. θ
  21. (
  22. a
  23. t
  24. s
  25. t
  26. )
  27. \nabla_{\theta}logP(\tau|\theta)=\sum_{t=0}^{T}{\nabla_{\theta}log{\pi_\theta(a_t|s_t)}}
  28. ∇θ​logP(τ∣θ)=t=0T​∇θ​logπθ​(at​∣st​)

所以

  1. θ
  2. J
  3. (
  4. π
  5. θ
  6. )
  7. =
  8. θ
  9. E
  10. τ
  11. π
  12. θ
  13. [
  14. R
  15. (
  16. τ
  17. )
  18. ]
  19. =
  20. θ
  21. τ
  22. P
  23. (
  24. τ
  25. θ
  26. )
  27. R
  28. (
  29. τ
  30. )
  31. 展开期望
  32. =
  33. τ
  34. θ
  35. P
  36. (
  37. τ
  38. θ
  39. )
  40. R
  41. (
  42. τ
  43. )
  44. 把梯度放到里面
  45. =
  46. τ
  47. P
  48. (
  49. τ
  50. θ
  51. )
  52. θ
  53. log
  54. P
  55. (
  56. τ
  57. θ
  58. )
  59. R
  60. (
  61. τ
  62. )
  63. log技巧
  64. =
  65. E
  66. τ
  67. π
  68. θ
  69. θ
  70. log
  71. P
  72. (
  73. τ
  74. θ
  75. )
  76. R
  77. (
  78. τ
  79. )
  80. 又重新变成期望的格式
  81. θ
  82. J
  83. (
  84. π
  85. θ
  86. )
  87. =
  88. E
  89. τ
  90. π
  91. θ
  92. t
  93. =
  94. 0
  95. T
  96. θ
  97. log
  98. π
  99. θ
  100. (
  101. a
  102. t
  103. s
  104. t
  105. )
  106. R
  107. (
  108. τ
  109. )
  110. 目标函数的梯度
  111. \begin{align*} \nabla_{\theta} J(\pi_{\theta}) &= \nabla_{\theta} \mathop{E}\limits_{\tau \sim \pi_{\theta}}[{R(\tau)}] & \\ &= \nabla_{\theta} \int_{\tau} P(\tau|\theta) R(\tau) & \text{展开期望} \\ &= \int_{\tau} \nabla_{\theta} P(\tau|\theta) R(\tau) & \text{把梯度放到里面} \\ &= \int_{\tau} P(\tau|\theta) \nabla_{\theta} \log P(\tau|\theta) R(\tau) & \text{log技巧} \\ &= \mathop{E}\limits_{\tau \sim \pi_{\theta}}{\nabla_{\theta} \log P(\tau|\theta) R(\tau)} & \text{又重新变成期望的格式} \\ \therefore \nabla_{\theta} J(\pi_{\theta}) &= \mathop{E}\limits_{\tau \sim \pi_{\theta}}{\sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t |s_t) R(\tau)} & \text{目标函数的梯度} \end{align*}
  112. ∇θ​J(πθ​)∴∇θ​J(πθ​)​=∇θ​τ∼πθ​E​[R(τ)]=∇θ​∫τ​P(τ∣θ)R(τ)=∫τ​∇θ​P(τ∣θ)R(τ)=∫τ​P(τ∣θ)∇θ​logP(τ∣θ)R(τ)=τ∼πθ​E​∇θ​logP(τ∣θ)R(τ)=τ∼πθ​Et=0T​∇θ​logπθ​(at​∣st​)R(τ)​展开期望把梯度放到里面log技巧又重新变成期望的格式目标函数的梯度​

最后的表达式就是我们所需要的目标函数的梯度,后面的

  1. R
  2. (
  3. τ
  4. )
  5. R(\tau)
  6. R(τ)部分还可以写成其他的形式,这里不做讨论,例如写成Rewart-to-go的形式:
  7. θ
  8. J
  9. (
  10. π
  11. θ
  12. )
  13. =
  14. E
  15. τ
  16. π
  17. θ
  18. t
  19. =
  20. 0
  21. T
  22. θ
  23. log
  24. π
  25. θ
  26. (
  27. a
  28. t
  29. s
  30. t
  31. )
  32. t
  33. =
  34. t
  35. T
  36. R
  37. (
  38. s
  39. t
  40. ,
  41. a
  42. t
  43. ,
  44. s
  45. t
  46. +
  47. 1
  48. )
  49. .
  50. \nabla_{\theta} J(\pi_{\theta}) = \mathop{E}\limits_{\tau \sim \pi_{\theta}}{\sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t |s_t) \sum_{t'=t}^T R(s_{t'}, a_{t'}, s_{t'+1})}.
  51. ∇θ​J(πθ​)=τ∼πθ​Et=0T​∇θ​logπθ​(at​∣st​)t′=tTR(st′​,at′​,st′+1​).

还可以减去一个baseline来减小方差

  1. θ
  2. J
  3. (
  4. π
  5. θ
  6. )
  7. =
  8. E
  9. τ
  10. π
  11. θ
  12. t
  13. =
  14. 0
  15. T
  16. θ
  17. log
  18. π
  19. θ
  20. (
  21. a
  22. t
  23. s
  24. t
  25. )
  26. (
  27. t
  28. =
  29. t
  30. T
  31. R
  32. (
  33. s
  34. t
  35. ,
  36. a
  37. t
  38. ,
  39. s
  40. t
  41. +
  42. 1
  43. )
  44. b
  45. (
  46. s
  47. t
  48. )
  49. )
  50. .
  51. \nabla_{\theta} J(\pi_{\theta}) = \mathop{E}\limits_{\tau \sim \pi_{\theta}}{\sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t |s_t) \left(\sum_{t'=t}^T R(s_{t'}, a_{t'}, s_{t'+1}) - b(s_t)\right)}.
  52. ∇θ​J(πθ​)=τ∼πθ​Et=0T​∇θ​logπθ​(at​∣st​)(t′=tTR(st′​,at′​,st′+1​)−b(st​)).

这个式子是正确的,因为后面的baseline不会影响梯度的值,可以证明第二项的期望为0.

回到话题,我们已经求出了目标函数的梯度:

  1. θ
  2. J
  3. (
  4. π
  5. θ
  6. )
  7. =
  8. E
  9. τ
  10. π
  11. θ
  12. t
  13. =
  14. 0
  15. T
  16. θ
  17. log
  18. π
  19. θ
  20. (
  21. a
  22. t
  23. s
  24. t
  25. )
  26. R
  27. (
  28. τ
  29. )
  30. \nabla_{\theta} J(\pi_{\theta}) = \mathop{E}\limits_{\tau \sim \pi_{\theta}}{\sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t |s_t) R(\tau)}
  31. ∇θ​J(πθ​)=τ∼πθ​Et=0T​∇θ​logπθ​(at​∣st​)R(τ)

最外层的期望可以通过蒙特卡洛采样的方法来实现,即采样多条轨迹然后求平均值:

  1. g
  2. ^
  3. =
  4. 1
  5. D
  6. τ
  7. D
  8. t
  9. =
  10. 0
  11. T
  12. θ
  13. log
  14. π
  15. θ
  16. (
  17. a
  18. t
  19. s
  20. t
  21. )
  22. R
  23. (
  24. τ
  25. )
  26. ,
  27. \hat{g} = \frac{1}{|\mathcal{D}|} \sum_{\tau \in \mathcal{D}} \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t |s_t) R(\tau),
  28. g^​=∣D1​τ∈D∑​t=0T​∇θ​logπθ​(at​∣st​)R(τ),
  1. D
  2. \mathcal{D}
  3. D是采集的轨迹的集合:
  4. D
  5. =
  6. {
  7. τ
  8. 1
  9. ,
  10. τ
  11. 2
  12. ,
  13. .
  14. .
  15. .
  16. ,
  17. τ
  18. D
  19. }
  20. \mathcal{D} =\{\tau_1,\tau_2,...,\tau_{|\mathcal{D}|}\}
  21. D={τ1​,τ2​,...,τ∣D∣​}

但是问题是,放到神经网络中,参数该如何更新呢?

策略梯度算法的损失函数

神经网络的结构

如果我们用神经网络来表示策略

  1. π
  2. θ
  3. \pi_\theta
  4. πθ​,神经网络的输入就是状态
  5. s
  6. t
  7. s_t
  8. st​,输出是每一个动作的概率
  9. π
  10. θ
  11. (
  12. a
  13. t
  14. s
  15. t
  16. )
  17. \pi_\theta(a_t|s_t)
  18. πθ​(at​∣st​),
  19. θ
  20. \theta
  21. θ是神经网络中的参数.

这里考虑的是离散动作的情况,如果是连续动作,输出是高斯分布的参数。如果是多维连续动作,那么输出是多维高斯分布的参数。

要想输出是概率,那么最后一层之后我们需要接一个

  1. s
  2. o
  3. f
  4. t
  5. m
  6. a
  7. x
  8. softmax
  9. softmax函数,这样才能保证每一个输出值大于0,且和为1.
  10. s
  11. o
  12. f
  13. t
  14. m
  15. a
  16. x
  17. (
  18. x
  19. i
  20. )
  21. =
  22. e
  23. x
  24. i
  25. j
  26. =
  27. 1
  28. N
  29. e
  30. x
  31. j
  32. softmax(x_i) = \frac{e^{x_i}}{\sum_{j=1}^{N}{e^{x_j}}}
  33. softmax(xi​)=∑j=1Nexjexi​​

Note:神经网络的参数

  1. θ
  2. \theta
  3. θ影响的是策略
  4. π
  5. θ
  6. \pi_\theta
  7. πθ​的分布,策略的分布,影响的是智能体在环境中的交互产生的轨迹的分布,轨迹的分布影响的是最终智能体得到的累计回报。

我们希望产生回报大的轨迹出现的概率高,产生回报小的轨迹出现的概率低。如果能够构造一个好的动作评判指标,来判断一个动作的好和不好,那么就可以通过改变动作出现的概率来优化策略。
我们构造损失函数如下:

  1. L
  2. (
  3. θ
  4. )
  5. =
  6. τ
  7. π
  8. θ
  9. l
  10. o
  11. g
  12. π
  13. θ
  14. (
  15. a
  16. s
  17. )
  18. R
  19. (
  20. τ
  21. )
  22. L(\theta) = -\sum_{\tau \sim \pi_\theta}{log\pi_\theta(a|s)R(\tau)}
  23. L(θ)=−τ∼πθ​∑​logπθ​(as)R(τ)

那么在深度学习框架中实现参数更新只需要如下的伪代码:

  1. loss = L(theta)
  2. loss.backward()#梯度反向传播
  3. loss.step()#参数theta更新

上述代码会对loss求梯度,然后通过梯度下降来更新参数。

回顾一下交叉熵:

  1. H
  2. (
  3. p
  4. ,
  5. q
  6. )
  7. =
  8. x
  9. p
  10. (
  11. x
  12. )
  13. l
  14. o
  15. g
  16. q
  17. (
  18. x
  19. )
  20. H(p,q) = -\sum_{x}{\mathcal{p}(x)log \mathcal{q}(x)}
  21. H(p,q)=−x∑​p(x)logq(x)

其中p和q是两个概率分布,交叉熵刻画的是两个概率分布之间的距离,两个概率的分布越接近,交叉熵越小
回到我们的话题,我们希望当轨迹累计回报

  1. R
  2. (
  3. τ
  4. )
  5. R(\tau)
  6. R(τ) 越大的时候,对应的动作出现的概率也越大
  7. l
  8. o
  9. g
  10. π
  11. θ
  12. (
  13. a
  14. t
  15. s
  16. t
  17. )
  18. log\pi_\theta(a_t|s_t)
  19. logπθ​(at​∣st​). 反之亦然。 换句话说,我们希望
  20. R
  21. (
  22. τ
  23. )
  24. R(\tau)
  25. R(τ)和
  26. l
  27. o
  28. g
  29. π
  30. θ
  31. (
  32. a
  33. t
  34. s
  35. t
  36. )
  37. log\pi_\theta(a_t|s_t)
  38. logπθ​(at​∣st​)的分布越接近越好,用交叉熵损失函数是再合适不过了!

这里还可以继续深入理解: 使用交叉熵损失函数将两者绑定在了一起,其中

  1. R
  2. (
  3. τ
  4. )
  5. R(\tau)
  6. R(τ)在确定环境中给定策略下是确定的,无法改变的。智能体在和环境交互的过程中,发现轨迹
  7. τ
  8. i
  9. \tau_i
  10. τi​的奖励大一些,于是就通过交叉熵损失函数将对应的动作出现的概率也增大了一些(通过更新参数实现)。于是,智能体再次探索环境的时候,走那些奖励大一些的轨迹的概率更大一些(这就是利用已经学会的知识)。

策略梯度算法的简单实现

声明:以下代码为大概思路,不是完整的代码。完整代码可以参考 【强化学习】spinningup最简单的策略梯度(VPG)代码详细注释——基于pytorch实现
或者这篇:动手强化学习(九):策略梯度算法 代码通俗易懂,适合新手。

定义神经网络
  1. classPolicyNet(torch.nn.Module):def__init__(self, state_dim, hidden_dim, action_dim):super(PolicyNet, self).__init__()
  2. self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
  3. self.fc2 = torch.nn.Linear(hidden_dim, action_dim)defforward(self, x):
  4. x = F.relu(self.fc1(x))return F.softmax(self.fc2(x), dim=1)

神经网络就两层,第一层的输出接一个激活函数,第二层的输出接一个softmax函数,目的是得到输入状态为

  1. s
  2. t
  3. s_t
  4. st​时,输出动作的概率。
选取动作

神经网络的输出是动作的概率分布,根据这个分布选取当前智能体执行的动作是什么:

  1. deftake_action(self, state):# 根据动作概率分布随机采样
  2. state = torch.tensor([state], dtype=torch.float).to(self.device)# 输入状态得到动作的概率分布
  3. probs = self.policy_net(state)# 离散型的动作分布
  4. action_dist = torch.distributions.Categorical(probs)# 随机采样(概率大的动作采样出现的次数更多)
  5. action = action_dist.sample()# 返回动作(这里的动作是index,从0开始)return action.item()
采样

接下来就是智能体和环境进行交互采样,得到一个批次的数据。

损失函数

定义损失函数

  1. defcompute_loss(obs, act, weights):# 注意:这里传入的是批次数据(多条轨迹),所以最后求平均值,为平均loss,或者
  2. logp = get_policy(obs).log_prob(act)# 计算 logp(a|s)return-(logp * weights).mean()# 交叉熵损失函数
参数更新

有了损失函数之后,定义优化器,更新参数:

  1. optimizer = Adam(logits_net.parameters(), lr=lr)# 采样之后,计算梯度,更新网络参数
  2. optimizer.zero_grad()# 这里是计算梯度
  3. batch_loss = compute_loss(obs=torch.as_tensor(batch_obs, dtype=torch.float32),
  4. act=torch.as_tensor(batch_acts, dtype=torch.int32),
  5. weights=torch.as_tensor(batch_weights, dtype=torch.float32)# 梯度反向传播
  6. batch_loss.backward()
  7. optimizer.step()

本文转载自: https://blog.csdn.net/Cser_zhu/article/details/126780417
版权归原作者 sumilkk 所有, 如有侵权,请联系我们删除。

“【强化学习】策略梯度算法中的损失函数”的评论:

还没有评论